summaryrefslogtreecommitdiffstats
path: root/simple/simple-http/src/test/java/org/simpleframework/http
diff options
context:
space:
mode:
Diffstat (limited to 'simple/simple-http/src/test/java/org/simpleframework/http')
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/ConnectionTest.java267
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/CookieTest.java63
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/Debug.java11
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/MockRenegotiationServer.java434
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/MockSocket.java45
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/MockTrace.java8
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/RenegotiationExample.java351
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/StatusTest.java20
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/StreamTransport.java67
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/AccumulatorTest.java99
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/ChunkedProducerTest.java43
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/Chunker.java52
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/Client.java264
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/Connector.java9
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/ConversationTest.java126
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/DribbleCursor.java62
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/FixedConsumerTest.java80
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/FixedProducerTest.java50
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MessageTest.java72
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MockChannel.java57
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MockController.java55
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MockEntity.java49
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MockObserver.java62
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MockPart.java49
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MockProxyRequest.java67
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MockRequest.java202
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MockResponse.java95
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MockSender.java75
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/MockSocket.java42
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/PayloadTest.java97
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/ProducerExceptionTest.java23
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/QueryBuilderTest.java35
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/ReactorProcessorTest.java247
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/ReactorTest.java178
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/RequestConsumerTest.java138
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/RequestTest.java144
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/Result.java37
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/StopTest.java176
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/StreamCursor.java74
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/ThreadDumper.java183
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/Ticket.java22
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/TicketProcessor.java28
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/TransferTest.java195
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/core/WebSocketUpgradeTest.java126
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/BoundaryConsumerTest.java77
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/ChunkedConsumerTest.java118
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/ContentConsumerTest.java99
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/FileUploadConsumerTest.java86
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/MessageHeaderTest.java48
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/MockBody.java47
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/MockHeader.java22
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/MockSegment.java83
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/PartConsumerTest.java33
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/PartSeriesConsumerTest.java157
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/ReplyConsumer.java141
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/SegmentConsumerTest.java103
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/message/TokenConsumerTest.java55
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/AddressParserTest.java92
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/ContentDispositionParserTest.java33
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/ContentTypeParserTest.java74
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/CookieParserTest.java22
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/DateParserTest.java55
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/LanguageParserTest.java26
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/ListParserTest.java97
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/ParameterTest.java69
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/PathParserTest.java97
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/PriorityQueueTest.java48
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/parse/QueryParserTest.java69
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/WebFrameTypeTest.java29
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketAnalyzer.java66
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketCertificate.java170
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatApplication.java170
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatLogin.html12
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoom.html29
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoom.java86
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoomListener.java106
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketKeyTest.java37
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketTestClient.java25
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/service/PathRouterTest.java62
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/service/WebSocketPerformanceTest.java439
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTable.java151
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableCell.java26
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableChanger.java58
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableColumn.java22
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableColumnStyle.java35
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableListener.java69
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRow.java84
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRowAnnotator.java89
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRowChanger.java73
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSchema.java40
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSubscription.java44
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSweeper.java33
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdateType.java17
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdater.java126
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdaterApplication.java154
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketValueEncoder.java53
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/bootstrap.css5774
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/bootstrap.min.js6
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/delta.js344
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/failure.pngbin0 -> 3113 bytes
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/font-awesome.min.css33
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/grid.html103
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/index.html40
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/jquery-2.1.1.min.js4
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/login.html12
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/main.html182
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/pending.pngbin0 -> 4897 bytes
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/success.pngbin0 -> 4514 bytes
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/table.html347
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.css2750
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.js13617
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.min.css2
-rw-r--r--simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.min.js11
113 files changed, 31860 insertions, 0 deletions
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/ConnectionTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/ConnectionTest.java
new file mode 100644
index 00000000..9047de55
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/ConnectionTest.java
@@ -0,0 +1,267 @@
+package org.simpleframework.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.URL;
+import java.util.List;
+import java.util.Vector;
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.FileAllocator;
+import org.simpleframework.common.thread.ConcurrentExecutor;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.core.Container;
+import org.simpleframework.http.core.ContainerTransportProcessor;
+import org.simpleframework.http.core.ThreadDumper;
+import org.simpleframework.transport.TransportProcessor;
+import org.simpleframework.transport.TransportSocketProcessor;
+import org.simpleframework.transport.SocketProcessor;
+import org.simpleframework.transport.Socket;
+import org.simpleframework.transport.connect.Connection;
+import org.simpleframework.transport.connect.SocketConnection;
+
+public class ConnectionTest extends TestCase {
+
+ private static final int PING_TEST_PORT = 12366;
+
+ public void testSocketPing() throws Exception {
+ // for(int i = 0; i < 10; i++) {
+ // System.err.printf("Ping [%s]%n", i);
+ // testPing(PING_TEST_PORT, "Hello World!", true, 2);
+ // }
+ }
+
+ public void testURLPing() throws Exception {
+ for(int i = 0; i < 20; i++) {
+ System.err.printf("Ping [%s]%n", i);
+ testPing(PING_TEST_PORT, "Hello World!", false, 10);
+ }
+ }
+
+ public void testMixPing() throws Exception {
+ //for(int i = 0; i < 50; i+=2) {
+ // System.err.printf("Ping [%s]%n", i);
+ // testPing(PING_TEST_PORT, "Hello World!", true, 2);
+ // System.err.printf("Ping [%s]%n", i+1);
+ // testPing(PING_TEST_PORT, "Hello World!", false, 10);
+ //}
+ }
+
+ private void testPing(int port, String message, boolean socket, int count) throws Exception {
+ PingServer server = new PingServer(PING_TEST_PORT, message);
+ Pinger pinger = new Pinger(PING_TEST_PORT, socket, count);
+
+ server.start();
+ List<String> list = pinger.execute();
+
+ for(int i = 0; i < count; i++) { // at least 20
+ String result = list.get(i);
+
+ assertNotNull(result);
+ assertEquals(result, message);
+ }
+ server.stop();
+ pinger.validate();
+ pinger.stop(); // wait for it all to finish
+ }
+
+ private static class DebugServer implements SocketProcessor {
+
+ private SocketProcessor server;
+
+ public DebugServer(SocketProcessor server) {
+ this.server = server;
+ }
+
+ public void process(Socket socket) throws IOException {
+ System.err.println("Connect...");
+ server.process(socket);
+ }
+
+ public void stop() throws IOException {
+ System.err.println("Stop...");
+ server.stop();
+ }
+ }
+
+ private static class PingServer implements Container {
+
+ private final Connection connection;
+ private final SocketAddress address;
+ private final String message;
+
+ public PingServer(int port, String message) throws Exception {
+ Allocator allocator = new FileAllocator();
+ TransportProcessor processor = new ContainerTransportProcessor(this, allocator, 5);
+ SocketProcessor server = new TransportSocketProcessor(processor);
+ DebugServer debug = new DebugServer(server);
+
+ this.connection = new SocketConnection(debug);
+ this.address = new InetSocketAddress(port);
+ this.message = message;
+ }
+
+ public void start() throws Exception {
+ try {
+ System.err.println("Starting...");
+ connection.connect(address);
+ }finally {
+ System.err.println("Started...");
+ }
+ }
+
+ public void stop() throws Exception {
+ connection.close();
+ }
+
+ public void handle(Request req, Response resp) {
+ try {
+ System.err.println(req);
+ PrintStream out = resp.getPrintStream(1024);
+
+ resp.setValue("Content-Type", "text/plain");
+ out.print(message);
+ out.close();
+ }catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static class Pinger implements Runnable {
+
+ private final int count;
+ private final int port;
+ private final boolean socket;
+ private final CountDownLatch latch;
+ private final CountDownLatch stop;
+ private final ConcurrentExecutor executor;
+ private final ThreadDumper dumper;
+ private final List<String> list;
+ private final List<java.net.Socket> sockets;
+
+ public Pinger(int port, boolean socket, int count) throws Exception {
+ this.executor = new ConcurrentExecutor(Pinger.class, count);
+ this.list = new Vector<String>(count);
+ this.sockets = new Vector<java.net.Socket>(count);
+ this.latch = new CountDownLatch(count);
+ this.stop = new CountDownLatch(count + count);
+ this.dumper = new ThreadDumper();
+ this.port = port;
+ this.socket = socket;
+ this.count = count;
+ }
+
+ public List<String> execute() throws Exception {
+ dumper.start();
+
+ for(int i = 0; i < count; i++) {
+ executor.execute(this);
+ }
+ latch.await();
+
+ // Overrun with pings to ensure they close
+ if(socket) {
+ for(int i = 0; i < count; i++) {
+ executor.execute(this);
+ }
+ }
+ return list;
+ }
+
+ public void validate() throws Exception {
+ if(socket) {
+ for(java.net.Socket socket : sockets) {
+ if(socket.getInputStream().read() != -1) {
+ throw new IOException("Connection not closed");
+ } else {
+ System.err.println("Socket is closed");
+ }
+ }
+ }
+ }
+
+ public void stop() throws Exception {
+ executor.stop();
+
+ if(socket) {
+ stop.await(); // wait for all excess pings to finish
+ }
+ dumper.kill();
+ }
+
+ private String ping() throws Exception {
+ if(socket) {
+ return pingWithSocket();
+ }
+ return pingWithURL();
+ }
+
+ public void run() {
+ try {
+ String result = ping();
+
+ list.add(result);
+ latch.countDown();
+ }catch(Throwable e){
+ System.err.println(e);
+ } finally {
+ stop.countDown(); // account for excess pings
+ }
+ }
+
+ /**
+ * This works as it opens a socket and sends the request.
+ * This will split using the CRLF and CRLF ending.
+ *
+ * @return the response body
+ *
+ * @throws Exception if the socket can not connect
+ */
+ private String pingWithSocket() throws Exception {
+ java.net.Socket socket = new java.net.Socket("localhost", port);
+ OutputStream out = socket.getOutputStream();
+ out.write(
+ ("GET / HTTP/1.1\r\n" +
+ "Host: localhost\r\n"+
+ "\r\n").getBytes());
+ out.flush();
+ InputStream in = socket.getInputStream();
+ byte[] block = new byte[1024];
+ int count = in.read(block);
+ String result = new String(block, 0, count);
+ String parts[] = result.split("\r\n\r\n");
+
+ if(!result.startsWith("HTTP")) {
+ throw new IOException("Header is not valid");
+ }
+ sockets.add(socket);
+ return parts[1];
+ }
+
+ /**
+ * Use the standard URL tool to get the content.
+ *
+ * @return the response body
+ *
+ * @throws Exception if a connection can not be made.
+ */
+ private String pingWithURL() throws Exception {
+ URL target = new URL("http://localhost:"+ port+"/");
+ InputStream in = target.openStream();
+ byte[] block = new byte[1024];
+ int count = in.read(block);
+ String result = new String(block, 0, count);
+
+ return result;
+ }
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/CookieTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/CookieTest.java
new file mode 100644
index 00000000..9fd44414
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/CookieTest.java
@@ -0,0 +1,63 @@
+package org.simpleframework.http;
+
+import junit.framework.TestCase;
+
+public class CookieTest extends TestCase {
+
+ public void testCookies() throws Exception {
+ Cookie cookie = new Cookie("JSESSIONID", "XXX");
+
+ cookie.setExpiry(10);
+ cookie.setPath("/path");
+
+ System.err.println(cookie);
+
+ assertTrue(cookie.toString().contains("max-age=10"));
+ assertTrue(cookie.toString().matches(".*expires=\\w\\w\\w, \\d\\d-\\w\\w\\w-\\d\\d\\d\\d \\d\\d:\\d\\d:\\d\\d GMT;.*"));
+ }
+
+ public void testCookieWithoutExpiry() throws Exception {
+ Cookie cookie = new Cookie("JSESSIONID", "XXX");
+
+ cookie.setPath("/path");
+
+ System.err.println(cookie);
+
+ assertFalse(cookie.toString().contains("max-age=10"));
+ assertFalse(cookie.toString().matches(".*expires=\\w\\w\\w, \\d\\d \\w\\w\\w \\d\\d\\d\\d \\d\\d:\\d\\d:\\d\\d GMT;.*"));
+ }
+
+ public void testSecureCookies() throws Exception {
+ Cookie cookie = new Cookie("JSESSIONID", "XXX");
+
+ cookie.setExpiry(10);
+ cookie.setPath("/path");
+ cookie.setSecure(true);
+
+ System.err.println(cookie);
+
+ assertTrue(cookie.toString().contains("max-age=10"));
+ assertTrue(cookie.toString().matches(".*expires=\\w\\w\\w, \\d\\d-\\w\\w\\w-\\d\\d\\d\\d \\d\\d:\\d\\d:\\d\\d GMT;.*"));
+
+ cookie.setExpiry(10);
+ cookie.setPath("/path");
+ cookie.setSecure(false);
+ cookie.setProtected(true);
+
+ System.err.println(cookie);
+
+ assertTrue(cookie.toString().contains("max-age=10"));
+ assertTrue(cookie.toString().matches(".*expires=\\w\\w\\w, \\d\\d-\\w\\w\\w-\\d\\d\\d\\d \\d\\d:\\d\\d:\\d\\d GMT;.*"));
+
+ cookie.setExpiry(10);
+ cookie.setPath("/path");
+ cookie.setSecure(true);
+ cookie.setProtected(true);
+
+ System.err.println(cookie);
+
+ assertTrue(cookie.toString().contains("max-age=10"));
+ assertTrue(cookie.toString().matches(".*expires=\\w\\w\\w, \\d\\d-\\w\\w\\w-\\d\\d\\d\\d \\d\\d:\\d\\d:\\d\\d GMT;.*"));
+
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/Debug.java b/simple/simple-http/src/test/java/org/simpleframework/http/Debug.java
new file mode 100644
index 00000000..b916494a
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/Debug.java
@@ -0,0 +1,11 @@
+package org.simpleframework.http;
+
+public class Debug {
+ public void log(String text, Object... list) {
+ System.out.printf(text, list);
+ }
+
+ public void logln(String text, Object... list) {
+ System.out.printf(text + "%n", list);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/MockRenegotiationServer.java b/simple/simple-http/src/test/java/org/simpleframework/http/MockRenegotiationServer.java
new file mode 100644
index 00000000..76bfb6ef
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/MockRenegotiationServer.java
@@ -0,0 +1,434 @@
+package org.simpleframework.http;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SocketChannel;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509TrustManager;
+import javax.security.cert.X509Certificate;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.FileAllocator;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.core.Client.AnonymousTrustManager;
+import org.simpleframework.http.core.Container;
+import org.simpleframework.http.core.ContainerTransportProcessor;
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.CertificateChallenge;
+import org.simpleframework.transport.TransportProcessor;
+import org.simpleframework.transport.TransportSocketProcessor;
+import org.simpleframework.transport.SocketProcessor;
+import org.simpleframework.transport.Socket;
+import org.simpleframework.transport.Transport;
+import org.simpleframework.transport.connect.Connection;
+import org.simpleframework.transport.connect.SocketConnection;
+import org.simpleframework.transport.trace.TraceAnalyzer;
+import org.simpleframework.transport.trace.Trace;
+
+public class MockRenegotiationServer implements Container {
+
+ private final ConfigurableCertificateServer server;
+ private final Connection connection;
+ private final SocketAddress address;
+ private final SSLContext context;
+ private final TraceAnalyzer agent;
+
+ public static void main(String[] list) throws Exception {
+ System.err.println("Starting renegotiation test.....");
+ //System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
+ //System.setProperty("sun.security.ssl.allowLegacyHelloMessages", "true");
+ //File file = new File("C:\\work\\development\\async_http\\yieldbroker-proxy-site\\etc\\www.yieldbroker.com.pfx");
+ File file = new File("/Users/niallg/Work/development/yieldbroker/proxy/yieldbroker-proxy-site/certificate/www.yieldbroker.com.pfx");
+ //File file = new File("C:\\work\\development\\async_http\\yieldbroker-proxy-trading\\etc\\uat.yieldbroker.com.pfx");
+ KeyStoreReader reader = new KeyStoreReader(KeyStoreType.PKCS12, file, "p", "p");
+ SecureSocketContext context = new SecureSocketContext(reader, SecureProtocol.TLS);
+ SSLContext sslContext = context.getContext();
+ MockRenegotiationServer server = new MockRenegotiationServer(sslContext, false, 10001);
+ server.start();
+ }
+
+ public MockRenegotiationServer(SSLContext context, boolean certRequired, int port) throws IOException {
+ Allocator allocator = new FileAllocator();
+ ContainerTransportProcessor processor = new ContainerTransportProcessor(this, allocator, 4);
+ TransportGrabber grabber = new TransportGrabber(processor);
+ TransportSocketProcessor processorServer = new TransportSocketProcessor(grabber);
+
+ this.server = new ConfigurableCertificateServer(processorServer, certRequired);
+ this.agent = new ConsoleAgent();
+ this.connection = new SocketConnection(server, agent);
+ this.address = new InetSocketAddress(port);
+ this.context = context;
+ }
+
+ public void handle(final Request req, final Response resp) {
+ boolean challengeForCertificate = false;
+
+ try {
+ final PrintStream out = resp.getPrintStream();
+ String normal = req.getPath().getPath();
+
+ if(normal.indexOf(".ico") == -1) {
+ SSLEngine engine = (SSLEngine)req.getAttribute(SSLEngine.class);
+ if(normal.startsWith("/niall/cert")) {
+ SocketChannel channel = ((Transport)req.getAttribute(Transport.class)).getChannel();
+ System.err.println("NEW REQUEST ("+System.identityHashCode(engine)+"): " + req);
+
+
+ try {
+ resp.setContentType("text/plain");
+ resp.setValue("Connection", "keep-alive");
+ String certificateInfo = null;
+
+
+ try {
+ X509Certificate[] list = req.getClientCertificate().getChain();
+ StringBuilder builder = new StringBuilder();
+ for(X509Certificate cert : list) {
+ X509Certificate x509 = (X509Certificate)cert;
+ builder.append(x509);
+ }
+ certificateInfo = builder.toString();
+ } catch(Exception e) {
+ e.printStackTrace();
+ certificateInfo = e.getMessage();
+ challengeForCertificate = true;
+
+ // http://stackoverflow.com/questions/14281628/ssl-renegotiation-with-client-certificate-causes-server-buffer-overflow
+ // Perhaps an expect 100 continue does something here?????
+ if(challengeForCertificate) {
+ Certificate certificate = req.getClientCertificate();
+ CertificateChallenge challenge = certificate.getChallenge();
+
+ Future<Certificate> future = challenge.challenge(new Runnable() {
+ public void run() {
+ System.err.println("FINISHED THE CHALLENGE!!!");
+ }
+ });
+ Certificate futureCert = future.get(10, TimeUnit.SECONDS);
+
+ if(futureCert == null) {
+ System.err.println("FAILED TO GET CERT!!!!");
+ } else {
+ System.err.println("**** GOT THE CERT");
+ }
+
+ String text= "Challenge finished without cert";
+ try {
+ X509Certificate[] list = req.getClientCertificate().getChain();
+ StringBuilder builder = new StringBuilder();
+ for(X509Certificate x509 : list) {
+ builder.append(x509);
+ }
+ text = builder.toString();
+ } catch(Exception ex) {
+ e.printStackTrace();
+ }
+ out.print(text);
+ out.flush();
+ try {
+ resp.close();
+ } catch(Exception ex){
+ e.printStackTrace();
+ }
+ }
+ }
+ // Thread.sleep(10000);
+ if(!challengeForCertificate) {
+ try {
+ X509Certificate[] list = req.getClientCertificate().getChain();
+ StringBuilder builder = new StringBuilder();
+ for(X509Certificate cert : list) {
+ X509Certificate x509 = (X509Certificate)cert;
+ builder.append(x509);
+ }
+ certificateInfo = builder.toString();
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ out.print(certificateInfo);
+ out.flush();
+ resp.close();
+ }
+
+
+ } finally {
+ if(!challengeForCertificate) {
+ System.err.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!("+System.identityHashCode(engine)+")!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WORKING");
+ }
+ }
+ } else {
+ resp.setStatus(org.simpleframework.http.Status.NOT_FOUND);
+ resp.setValue("Connection", "keep-alive");
+ resp.setValue("Content-Type", "text/plain");
+ out.println("Ok normal request with NO renegotiation " + req);
+ }
+ } else {
+ resp.setStatus(org.simpleframework.http.Status.NOT_FOUND);
+ resp.setValue("Connection", "keep-alive");
+ resp.setValue("Content-Type", "text/plain");
+ out.println("fuck off!!");
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if(!challengeForCertificate) {
+ resp.close();
+ } else {
+ System.err.println("NOT DOING ANYTHING WITH THE REQUEST, AS A CHALLENGE IS UNDERWAY challengeForCertificate="+challengeForCertificate+" path="+req);
+ }
+ } catch(Exception ex) {
+ ex.printStackTrace();
+ }
+
+ }
+ }
+
+ public void start() throws IOException {
+ connection.connect(address, context);
+ }
+
+ public void stop() throws IOException {
+ connection.close();
+ }
+
+ private static class ConsoleAgent implements TraceAnalyzer {
+
+ private final Map<SelectableChannel, Integer> map;
+ private final AtomicInteger count;
+
+ public ConsoleAgent() {
+ this.map = new ConcurrentHashMap<SelectableChannel, Integer>();
+ this.count = new AtomicInteger();
+ }
+
+ public Trace attach(SelectableChannel channel) {
+ if(map.containsKey(channel)) {
+ throw new IllegalStateException("Can't attach twice");
+ }
+ final int counter = count.getAndIncrement();
+ map.put(channel, counter);
+
+ return new Trace() {
+
+ public void trace(Object event) {
+ trace(event, "");
+ }
+
+ public void trace(Object event, Object value) {
+ if(value instanceof Throwable) {
+ StringWriter writer = new StringWriter();
+ PrintWriter out = new PrintWriter(writer);
+ ((Exception)value).printStackTrace(out);
+ out.flush();
+ value = writer.toString();
+ }
+ if(value != null && !String.valueOf(value).isEmpty()) {
+ System.err.printf("(%s) [%s] %s: %s%n", Thread.currentThread().getName(), counter, event, value);
+ } else {
+ System.err.printf("(%s) [%s] %s%n", Thread.currentThread().getName(), counter, event);
+ }
+ }
+ };
+ }
+
+ public void stop() {
+ System.err.println("Stop agent");
+ }
+ }
+
+ public static class TransportGrabber implements TransportProcessor {
+
+ private TransportProcessor processor;
+
+ public TransportGrabber(TransportProcessor processor) {
+ this.processor = processor;
+ }
+
+ public void process(Transport transport) throws IOException {
+ transport.getAttributes().put(Transport.class, transport);
+ processor.process(transport);
+
+ }
+
+ public void stop() throws IOException {
+ processor.stop();
+ }
+
+ }
+
+ public static class ConfigurableCertificateServer implements SocketProcessor {
+
+ private SocketProcessor server;
+ private boolean certRequired;
+
+ public ConfigurableCertificateServer(SocketProcessor server) {
+ this(server, false);
+ }
+
+ public ConfigurableCertificateServer(SocketProcessor server, boolean certRequired) {
+ this.certRequired = certRequired;
+ this.server = server;
+ }
+
+ public void setCertRequired(boolean certRequired) {
+ this.certRequired = certRequired;
+ }
+
+ public void process(Socket socket) throws IOException {
+ SSLEngine engine = socket.getEngine();
+ socket.getAttributes().put(SSLEngine.class, engine);
+ if(certRequired) {
+ socket.getEngine().setNeedClientAuth(true);
+ }
+ server.process(socket);
+ }
+
+ public void stop() throws IOException {
+ System.err.println("stop");
+ }
+ }
+
+
+ public enum KeyStoreType {
+ JKS("JKS", "SunX509"),
+ PKCS12("PKCS12", "SunX509");
+
+ private final String algorithm;
+ private final String type;
+
+ private KeyStoreType(String type, String algorithm) {
+ this.algorithm = algorithm;
+ this.type = type;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public KeyStore getKeyStore() throws KeyStoreException {
+ return KeyStore.getInstance(type);
+ }
+
+ public KeyManagerFactory getKeyManagerFactory() throws NoSuchAlgorithmException {
+ return KeyManagerFactory.getInstance(algorithm);
+ }
+ }
+
+ private static class KeyStoreManager {
+
+ private final KeyStoreType keyStoreType;
+
+ public KeyStoreManager(KeyStoreType keyStoreType) {
+ this.keyStoreType = keyStoreType;
+ }
+
+ public KeyManager[] getKeyManagers(InputStream keyStoreSource, String keyStorePassword, String keyManagerPassword) throws Exception {
+ KeyStore keyStore = keyStoreType.getKeyStore();
+ KeyManagerFactory keyManagerFactory = keyStoreType.getKeyManagerFactory();
+
+ keyStore.load(keyStoreSource, keyManagerPassword.toCharArray());
+ keyManagerFactory.init(keyStore, keyManagerPassword.toCharArray());
+
+ return keyManagerFactory.getKeyManagers();
+ }
+
+ }
+
+ private static class KeyStoreReader {
+
+ private final KeyStoreManager keyStoreManager;
+ private final String keyManagerPassword;
+ private final String keyStorePassword;
+ private final File keyStore;
+
+ public KeyStoreReader(KeyStoreType keyStoreType, File keyStore, String keyStorePassword, String keyManagerPassword) {
+ this.keyStoreManager = new KeyStoreManager(keyStoreType);
+ this.keyManagerPassword = keyManagerPassword;
+ this.keyStorePassword = keyStorePassword;
+ this.keyStore = keyStore;
+ }
+
+ public KeyManager[] getKeyManagers() throws Exception {
+ InputStream storeSource = new FileInputStream(keyStore);
+
+ try {
+ return keyStoreManager.getKeyManagers(storeSource, keyStorePassword, keyManagerPassword);
+ } finally {
+ storeSource.close();
+ }
+ }
+ }
+
+ public enum SecureProtocol {
+ DEFAULT("Default"),
+ SSL("SSL"),
+ TLS("TLS");
+
+ private final String protocol;
+
+ private SecureProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public SSLContext getContext() throws NoSuchAlgorithmException {
+ return SSLContext.getInstance(protocol);
+ }
+ }
+
+ private static class SecureSocketContext {
+
+ private final X509TrustManager trustManager;
+ private final X509TrustManager[] trustManagers;
+ private final KeyStoreReader keyStoreReader;
+ private final SecureProtocol secureProtocol;
+
+ public SecureSocketContext(KeyStoreReader keyStoreReader, SecureProtocol secureProtocol) {
+ this.trustManager = new AnonymousTrustManager();
+ this.trustManagers = new X509TrustManager[]{trustManager};
+ this.keyStoreReader = keyStoreReader;
+ this.secureProtocol = secureProtocol;
+ }
+
+ public SSLContext getContext() throws Exception {
+ KeyManager[] keyManagers = keyStoreReader.getKeyManagers();
+ SSLContext secureContext = secureProtocol.getContext();
+
+ secureContext.init(keyManagers, trustManagers, null);
+
+ return secureContext;
+ }
+
+ public SocketFactory getSocketFactory() throws Exception {
+ KeyManager[] keyManagers = keyStoreReader.getKeyManagers();
+ SSLContext secureContext = secureProtocol.getContext();
+
+ secureContext.init(keyManagers, trustManagers, null);
+
+ return secureContext.getSocketFactory();
+ }
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/MockSocket.java b/simple/simple-http/src/test/java/org/simpleframework/http/MockSocket.java
new file mode 100644
index 00000000..be11f594
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/MockSocket.java
@@ -0,0 +1,45 @@
+
+package org.simpleframework.http;
+
+import java.nio.channels.SocketChannel;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.net.ssl.SSLEngine;
+
+import org.simpleframework.transport.Socket;
+import org.simpleframework.transport.trace.Trace;
+
+public class MockSocket implements Socket {
+
+ private SocketChannel socket;
+ private SSLEngine engine;
+ private Map map;
+
+ public MockSocket(SocketChannel socket) {
+ this(socket, null);
+ }
+
+ public MockSocket(SocketChannel socket, SSLEngine engine) {
+ this.map = new HashMap();
+ this.engine = engine;
+ this.socket = socket;
+ }
+
+ public SSLEngine getEngine() {
+ return engine;
+ }
+
+ public SocketChannel getChannel() {
+ return socket;
+ }
+
+ public Map getAttributes() {
+ return map;
+ }
+
+ public Trace getTrace() {
+ return new MockTrace();
+ }
+}
+
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/MockTrace.java b/simple/simple-http/src/test/java/org/simpleframework/http/MockTrace.java
new file mode 100644
index 00000000..2f22a0a0
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/MockTrace.java
@@ -0,0 +1,8 @@
+package org.simpleframework.http;
+
+import org.simpleframework.transport.trace.Trace;
+
+public class MockTrace implements Trace{
+ public void trace(Object event) {}
+ public void trace(Object event, Object value) {}
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/RenegotiationExample.java b/simple/simple-http/src/test/java/org/simpleframework/http/RenegotiationExample.java
new file mode 100644
index 00000000..0ee0e7d4
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/RenegotiationExample.java
@@ -0,0 +1,351 @@
+package org.simpleframework.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.channels.SelectableChannel;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509TrustManager;
+
+import org.simpleframework.http.core.Client.AnonymousTrustManager;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.TransportProcessor;
+import org.simpleframework.transport.TransportSocketProcessor;
+import org.simpleframework.transport.SocketProcessor;
+import org.simpleframework.transport.Socket;
+import org.simpleframework.transport.Transport;
+import org.simpleframework.transport.TransportCursor;
+import org.simpleframework.transport.TransportWriter;
+import org.simpleframework.transport.connect.Connection;
+import org.simpleframework.transport.connect.SocketConnection;
+import org.simpleframework.transport.trace.TraceAnalyzer;
+import org.simpleframework.transport.trace.Trace;
+
+public class RenegotiationExample {
+
+ public static void main(String[] list) throws Exception {
+ Connection serverCon = createServer(false, 443);
+ /*SSLSocket socketCon = createClient();
+ OutputStream out = socketCon.getOutputStream();
+
+ for(int i = 0; i < 1000; i++) {
+ out.write("TEST".getBytes());
+ out.flush();
+ Thread.sleep(5000);
+ }*/
+ Thread.sleep(1000000);
+ serverCon.close();
+ }
+
+ public static Connection createServer(boolean certificateRequired, int listenPort) throws Exception {
+ System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
+ System.setProperty("sun.security.ssl.allowLegacyHelloMessages", "true");
+ File file = new File("C:\\work\\development\\async_http\\yieldbroker-proxy-trading\\etc\\uat.yieldbroker.com.pfx");
+ KeyStoreReader reader = new KeyStoreReader(KeyStoreType.PKCS12, file, "p", "p");
+ SecureSocketContext context = new SecureSocketContext(reader, SecureProtocol.TLS);
+ SSLContext sslContext = context.getContext();
+ TraceAnalyzer agent = new MockAgent();
+ TransportProcessor processor = new MockTransportProcessor();
+ TransportSocketProcessor server = new TransportSocketProcessor(processor);
+ ConfigurableCertificateServer certServer = new ConfigurableCertificateServer(server);
+ SocketConnection con = new SocketConnection(certServer, agent);
+ SocketAddress serverAddress = new InetSocketAddress(listenPort);
+
+ certServer.setCertRequired(certificateRequired);
+ con.connect(serverAddress, sslContext);
+
+ return con;
+ }
+
+
+ public static SSLSocket createClient() throws Exception {
+ System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
+ System.setProperty("sun.security.ssl.allowLegacyHelloMessages", "true");
+ File file = new File("C:\\work\\development\\async_http\\yieldbroker-proxy-benchmark\\etc\\niall.pfx");
+ KeyStoreReader reader = new KeyStoreReader(KeyStoreType.PKCS12, file, "1234", "1234");
+ SecureSocketContext context = new SecureSocketContext(reader, SecureProtocol.TLS);
+ SocketFactory factory = context.getSocketFactory();
+ SSLSocket socket = (SSLSocket)factory.createSocket("localhost", 9333);
+ socket.setEnabledProtocols(new String[] {"SSLv3", "TLSv1"});
+
+ return socket;
+ }
+
+ public static class ConfigurableCertificateServer implements SocketProcessor {
+
+ private SocketProcessor server;
+ private boolean certRequired;
+
+ public ConfigurableCertificateServer(SocketProcessor server) {
+ this.server = server;
+ }
+
+ public void setCertRequired(boolean certRequired) {
+ this.certRequired = certRequired;
+ }
+
+ public void process(Socket socket) throws IOException {
+ if(certRequired) {
+ socket.getEngine().setNeedClientAuth(true);
+ }
+ server.process(socket);
+ }
+
+ public void stop() throws IOException {
+ System.err.println("stop");
+ }
+ }
+
+ public static class TransportPoller extends Thread {
+
+ private final ByteCursor cursor;
+ private final Transport transport;
+
+
+ public TransportPoller(Transport transport) {
+ this.cursor = new TransportCursor(transport);
+ this.transport = transport;
+ }
+
+ public void run() {
+ try {
+ System.err.println("Poller started");
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] chunk = new byte[1024];
+ int count = 0;
+
+ while(cursor.isOpen()) {
+ while(cursor.isReady()) {
+ count = cursor.read(chunk);
+ if(count != 0) {
+ out.write(chunk, 0, count);
+ }
+ }
+ String message = out.toString();
+ out.reset();
+ if(message != null && !message.isEmpty()) {
+ SSLEngine engine = transport.getEngine();
+ String certificateInfo = null;
+
+ if(engine != null) {
+ try {
+ Certificate[] list = engine.getSession().getPeerCertificates();
+ StringBuilder builder = new StringBuilder();
+ for(Certificate cert : list) {
+ X509Certificate x509 = (X509Certificate)cert;
+ builder.append(x509);
+ }
+ certificateInfo = builder.toString();
+ } catch(Exception e) {
+
+ // Here we would have to ask the transport to renegotiate.....!!!
+ transport.getEngine().setWantClientAuth(true);
+ transport.getEngine().beginHandshake();
+ transport.getEngine().setWantClientAuth(true);
+ for(int i = 0; i < 100; i++) {
+ Runnable task = transport.getEngine().getDelegatedTask();
+ if(task != null){
+ task.run();
+ }
+ }
+ certificateInfo = e.getMessage();
+ }
+ }
+ TransportWriter sender = new TransportWriter(transport);
+ sender.write(
+ ("HTTP/1.1 200 OK\r\n" +
+ "Connection: keep-alive\r\n"+
+ "Content-Length: 5\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+
+ "hello").getBytes());
+
+
+ sender.flush();
+
+
+
+
+ System.err.println("["+message+"]: " + certificateInfo);
+ }
+ Thread.sleep(5000);
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+
+ public static class MockTransportProcessor implements TransportProcessor {
+
+ public void process(Transport transport) throws IOException {
+ System.err.println("New transport");
+ TransportPoller poller = new TransportPoller(transport);
+ poller.start();
+ }
+
+ public void stop() throws IOException {
+ System.err.println("Transport stopped");
+ }
+ }
+
+ private static class MockAgent implements TraceAnalyzer {
+
+ public Trace attach(SelectableChannel channel) {
+ return new Trace() {
+ public void trace(Object event) {
+ trace(event, "");
+ }
+ public void trace(Object event, Object value) {
+ if(value != null && !String.valueOf(value).isEmpty()) {
+ System.err.printf("%s: %s%n", event, value);
+ } else {
+ System.err.println(event);
+ }
+ }
+ };
+ }
+
+ public void stop() {
+ System.err.println("Stop agent");
+ }
+
+ }
+
+ public enum KeyStoreType {
+ JKS("JKS", "SunX509"),
+ PKCS12("PKCS12", "SunX509");
+
+ private final String algorithm;
+ private final String type;
+
+ private KeyStoreType(String type, String algorithm) {
+ this.algorithm = algorithm;
+ this.type = type;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public KeyStore getKeyStore() throws KeyStoreException {
+ return KeyStore.getInstance(type);
+ }
+
+ public KeyManagerFactory getKeyManagerFactory() throws NoSuchAlgorithmException {
+ return KeyManagerFactory.getInstance(algorithm);
+ }
+ }
+
+ private static class KeyStoreManager {
+
+ private final KeyStoreType keyStoreType;
+
+ public KeyStoreManager(KeyStoreType keyStoreType) {
+ this.keyStoreType = keyStoreType;
+ }
+
+ public KeyManager[] getKeyManagers(InputStream keyStoreSource, String keyStorePassword, String keyManagerPassword) throws Exception {
+ KeyStore keyStore = keyStoreType.getKeyStore();
+ KeyManagerFactory keyManagerFactory = keyStoreType.getKeyManagerFactory();
+
+ keyStore.load(keyStoreSource, keyManagerPassword.toCharArray());
+ keyManagerFactory.init(keyStore, keyManagerPassword.toCharArray());
+
+ return keyManagerFactory.getKeyManagers();
+ }
+
+ }
+
+ private static class KeyStoreReader {
+
+ private final KeyStoreManager keyStoreManager;
+ private final String keyManagerPassword;
+ private final String keyStorePassword;
+ private final File keyStore;
+
+ public KeyStoreReader(KeyStoreType keyStoreType, File keyStore, String keyStorePassword, String keyManagerPassword) {
+ this.keyStoreManager = new KeyStoreManager(keyStoreType);
+ this.keyManagerPassword = keyManagerPassword;
+ this.keyStorePassword = keyStorePassword;
+ this.keyStore = keyStore;
+ }
+
+ public KeyManager[] getKeyManagers() throws Exception {
+ InputStream storeSource = new FileInputStream(keyStore);
+
+ try {
+ return keyStoreManager.getKeyManagers(storeSource, keyStorePassword, keyManagerPassword);
+ } finally {
+ storeSource.close();
+ }
+ }
+ }
+
+ public enum SecureProtocol {
+ DEFAULT("Default"),
+ SSL("SSL"),
+ TLS("TLS");
+
+ private final String protocol;
+
+ private SecureProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public SSLContext getContext() throws NoSuchAlgorithmException {
+ return SSLContext.getInstance(protocol);
+ }
+ }
+
+ private static class SecureSocketContext {
+
+ private final X509TrustManager trustManager;
+ private final X509TrustManager[] trustManagers;
+ private final KeyStoreReader keyStoreReader;
+ private final SecureProtocol secureProtocol;
+
+ public SecureSocketContext(KeyStoreReader keyStoreReader, SecureProtocol secureProtocol) {
+ this.trustManager = new AnonymousTrustManager();
+ this.trustManagers = new X509TrustManager[]{trustManager};
+ this.keyStoreReader = keyStoreReader;
+ this.secureProtocol = secureProtocol;
+ }
+
+ public SSLContext getContext() throws Exception {
+ KeyManager[] keyManagers = keyStoreReader.getKeyManagers();
+ SSLContext secureContext = secureProtocol.getContext();
+
+ secureContext.init(keyManagers, trustManagers, null);
+
+ return secureContext;
+ }
+
+ public SocketFactory getSocketFactory() throws Exception {
+ KeyManager[] keyManagers = keyStoreReader.getKeyManagers();
+ SSLContext secureContext = secureProtocol.getContext();
+
+ secureContext.init(keyManagers, trustManagers, null);
+
+ return secureContext.getSocketFactory();
+ }
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/StatusTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/StatusTest.java
new file mode 100644
index 00000000..6bb8ef5f
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/StatusTest.java
@@ -0,0 +1,20 @@
+package org.simpleframework.http;
+
+import junit.framework.TestCase;
+
+public class StatusTest extends TestCase {
+
+ private static final int ITERATIONS = 100000;
+
+ public void testStatus() {
+ testStatus(200, "OK");
+ testStatus(404, "Not Found");
+ }
+
+ public void testStatus(int code, String expect) {
+ for(int i = 0; i < ITERATIONS; i++) {
+ assertEquals(expect, Status.getDescription(code));
+ }
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/StreamTransport.java b/simple/simple-http/src/test/java/org/simpleframework/http/StreamTransport.java
new file mode 100644
index 00000000..9ca5fe7c
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/StreamTransport.java
@@ -0,0 +1,67 @@
+package org.simpleframework.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SocketChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.Map;
+
+import javax.net.ssl.SSLEngine;
+
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.Transport;
+import org.simpleframework.transport.trace.Trace;
+
+public class StreamTransport implements Transport {
+
+ private final WritableByteChannel write;
+ private final ReadableByteChannel read;
+ private final OutputStream out;
+
+ public StreamTransport(InputStream in, OutputStream out) {
+ this.write = Channels.newChannel(out);
+ this.read = Channels.newChannel(in);
+ this.out = out;
+ }
+
+ public void close() throws IOException {
+ write.close();
+ read.close();
+ }
+
+ public void flush() throws IOException {
+ out.flush();
+ }
+
+ public int read(ByteBuffer buffer) throws IOException {
+ return read.read(buffer);
+ }
+
+ public void write(ByteBuffer buffer) throws IOException {
+ write.write(buffer);
+ }
+
+ public Map getAttributes() {
+ return null;
+ }
+
+ public SocketChannel getChannel() {
+ return null;
+ }
+
+ public SSLEngine getEngine() {
+ return null;
+ }
+
+ public Certificate getCertificate() {
+ return null;
+ }
+
+ public Trace getTrace() {
+ return new MockTrace();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/AccumulatorTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/AccumulatorTest.java
new file mode 100644
index 00000000..70db4a30
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/AccumulatorTest.java
@@ -0,0 +1,99 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+public class AccumulatorTest extends TestCase {
+
+ public void testAccumulator() throws IOException {
+ MockChannel channel = new MockChannel(null);
+ MockObserver monitor = new MockObserver();
+ MockRequest request = new MockRequest();
+ MockResponse response = new MockResponse();
+ Conversation support = new Conversation(request, response);
+ ResponseBuffer buffer = new ResponseBuffer(monitor, response, support, channel);
+
+ byte[] content = { 'T', 'E', 'S', 'T' };
+
+ // Start a HTTP/1.1 conversation
+ request.setMajor(1);
+ request.setMinor(1);
+
+ // Write to a zero capacity buffer
+ buffer.expand(0);
+ buffer.write(content, 0, content.length);
+
+ assertEquals(response.getValue("Connection"), "keep-alive");
+ assertEquals(response.getValue("Transfer-Encoding"), "chunked");
+ assertEquals(response.getValue("Content-Length"), null);
+ assertEquals(response.getContentLength(), -1);
+ assertTrue(response.isCommitted());
+
+ channel = new MockChannel(null);
+ monitor = new MockObserver();
+ request = new MockRequest();
+ response = new MockResponse();
+ support = new Conversation(request, response);
+ buffer = new ResponseBuffer(monitor, response, support, channel);
+
+ // Start a HTTP/1.0 conversation
+ request.setMajor(1);
+ request.setMinor(0);
+
+ // Write to a zero capacity buffer
+ buffer.expand(0);
+ buffer.write(content, 0, content.length);
+
+ assertEquals(response.getValue("Connection"), "close");
+ assertEquals(response.getValue("Transfer-Encoding"), null);
+ assertEquals(response.getValue("Content-Length"), null);
+ assertEquals(response.getContentLength(), -1);
+ assertTrue(response.isCommitted());
+
+ channel = new MockChannel(null);
+ monitor = new MockObserver();
+ request = new MockRequest();
+ response = new MockResponse();
+ support = new Conversation(request, response);
+ buffer = new ResponseBuffer(monitor, response, support, channel);
+
+ // Start a HTTP/1.1 conversation
+ request.setMajor(1);
+ request.setMinor(1);
+
+ // Write to a large capacity buffer
+ buffer.expand(1024);
+ buffer.write(content, 0, content.length);
+
+ assertEquals(response.getValue("Connection"), null);
+ assertEquals(response.getValue("Transfer-Encoding"), null);
+ assertEquals(response.getValue("Content-Length"), null);
+ assertEquals(response.getContentLength(), -1);
+ assertFalse(response.isCommitted());
+ assertFalse(monitor.isReady());
+ assertFalse(monitor.isClose());
+ assertFalse(monitor.isError());
+
+ // Flush the buffer
+ buffer.close();
+
+ assertEquals(response.getValue("Connection"), "keep-alive");
+ assertEquals(response.getValue("Transfer-Encoding"), null);
+ assertEquals(response.getValue("Content-Length"), "4");
+ assertEquals(response.getContentLength(), 4);
+ assertTrue(response.isCommitted());
+ assertTrue(monitor.isReady());
+ assertFalse(monitor.isClose());
+ assertFalse(monitor.isError());
+
+ boolean catchOverflow = false;
+
+ try {
+ buffer.write(content, 0, content.length);
+ } catch(Exception e) {
+ catchOverflow = true;
+ }
+ assertTrue(catchOverflow);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/ChunkedProducerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/ChunkedProducerTest.java
new file mode 100644
index 00000000..9f7a6bb5
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/ChunkedProducerTest.java
@@ -0,0 +1,43 @@
+package org.simpleframework.http.core;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.message.ChunkedConsumer;
+import org.simpleframework.transport.ByteCursor;
+
+public class ChunkedProducerTest extends TestCase {
+
+ public void testChunk() throws Exception {
+ testChunk(1024, 1);
+ testChunk(1024, 2);
+ testChunk(512, 20);
+ testChunk(64, 64);
+ }
+
+ public void testChunk(int chunkSize, int count) throws Exception {
+ MockSender sender = new MockSender((chunkSize * count) + 1024);
+ MockObserver monitor = new MockObserver();
+ ChunkedConsumer validator = new ChunkedConsumer(new ArrayAllocator());
+ ChunkedEncoder producer = new ChunkedEncoder(monitor, sender);
+ byte[] chunk = new byte[chunkSize];
+
+ for(int i = 0; i < chunk.length; i++) {
+ chunk[i] = (byte)String.valueOf(i).charAt(0);
+ }
+ for(int i = 0; i < count; i++) {
+ producer.encode(chunk, 0, chunkSize);
+ }
+ producer.close();
+
+ System.err.println(sender.getBuffer().encode("UTF-8"));
+
+ ByteCursor cursor = sender.getCursor();
+
+ while(!validator.isFinished()) {
+ validator.consume(cursor);
+ }
+ assertEquals(cursor.ready(), -1);
+ assertTrue(monitor.isReady());
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/Chunker.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/Chunker.java
new file mode 100644
index 00000000..8f5ef139
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/Chunker.java
@@ -0,0 +1,52 @@
+
+package org.simpleframework.http.core;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class Chunker extends FilterOutputStream {
+
+
+ private byte[] size = {'0', '0', '0', '0', '0',
+ '0', '0', '0', 13, 10};
+
+
+ private byte[] index = {'0', '1', '2', '3', '4', '5','6', '7',
+ '8', '9', 'a', 'b', 'c', 'd','e', 'f'};
+
+
+ private byte[] zero = {'0', 13, 10, 13, 10};
+
+
+ public Chunker(OutputStream out){
+ super(out);
+ }
+
+ public void write(int octet) throws IOException {
+ byte[] swap = new byte[1];
+ swap[0] = (byte)octet;
+ write(swap);
+ }
+
+
+ public void write(byte[] buf, int off, int len) throws IOException {
+ int pos = 7;
+
+ if(len > 0) {
+ for(int num = len; num > 0; num >>>= 4){
+ size[pos--] = index[num & 0xf];
+ }
+ String text = String.format("%s; %s\r\n", Integer.toHexString(len), len);
+
+ out.write(text.getBytes("ISO-8859-1"));
+ out.write(buf, off, len);
+ out.write(size, 8, 2);
+ }
+ }
+
+ public void close() throws IOException {
+ out.write(zero);
+ out.close();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/Client.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/Client.java
new file mode 100644
index 00000000..13d2f30f
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/Client.java
@@ -0,0 +1,264 @@
+package org.simpleframework.http.core;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.Socket;
+import java.security.KeyStore;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.X509TrustManager;
+
+
+public class Client {
+
+
+ private static final byte[] CERTIFICATE = {
+ (byte)254,(byte)237,(byte)254,(byte)237,(byte)0, (byte)0, (byte)0, (byte)2, (byte)0, (byte)0,
+ (byte)0, (byte)1, (byte)0, (byte)0, (byte)0, (byte)1, (byte)0, (byte)3, (byte)107,(byte)101,
+ (byte)121,(byte)0, (byte)0, (byte)1, (byte)26, (byte)105,(byte)38, (byte)187,(byte)170,(byte)0,
+ (byte)0, (byte)2, (byte)187,(byte)48, (byte)130,(byte)2, (byte)183,(byte)48, (byte)14, (byte)6,
+ (byte)10, (byte)43, (byte)6, (byte)1, (byte)4, (byte)1, (byte)42, (byte)2, (byte)17, (byte)1,
+ (byte)1, (byte)5, (byte)0, (byte)4, (byte)130,(byte)2, (byte)163,(byte)138,(byte)122,(byte)194,
+ (byte)218,(byte)31, (byte)101,(byte)210,(byte)131,(byte)160,(byte)37, (byte)111,(byte)187,(byte)43,
+ (byte)192,(byte)67, (byte)244,(byte)136,(byte)120,(byte)166,(byte)171,(byte)204,(byte)87, (byte)156,
+ (byte)50, (byte)58, (byte)153,(byte)37, (byte)180,(byte)248,(byte)60, (byte)73, (byte)16, (byte)110,
+ (byte)176,(byte)84, (byte)239,(byte)247,(byte)113,(byte)133,(byte)193,(byte)239,(byte)94, (byte)107,
+ (byte)126,(byte)141,(byte)199,(byte)243,(byte)243,(byte)25, (byte)179,(byte)181,(byte)201,(byte)100,
+ (byte)194,(byte)146,(byte)114,(byte)162,(byte)124,(byte)96, (byte)198,(byte)248,(byte)232,(byte)162,
+ (byte)143,(byte)170,(byte)120,(byte)106,(byte)171,(byte)128,(byte)32, (byte)18, (byte)134,(byte)69,
+ (byte)2, (byte)230,(byte)204,(byte)18, (byte)191,(byte)212,(byte)236,(byte)130,(byte)76, (byte)24,
+ (byte)24, (byte)131,(byte)210,(byte)150,(byte)209,(byte)205,(byte)174,(byte)25, (byte)175,(byte)45,
+ (byte)39, (byte)223,(byte)17, (byte)57, (byte)129,(byte)6, (byte)195,(byte)116,(byte)197,(byte)143,
+ (byte)14, (byte)160,(byte)120,(byte)249,(byte)220,(byte)48, (byte)71, (byte)109,(byte)122,(byte)64,
+ (byte)195,(byte)139,(byte)61, (byte)206,(byte)83, (byte)159,(byte)78, (byte)137,(byte)160,(byte)88,
+ (byte)252,(byte)120,(byte)217,(byte)251,(byte)254,(byte)151,(byte)94, (byte)242,(byte)170,(byte)0,
+ (byte)247,(byte)170,(byte)53, (byte)197,(byte)34, (byte)253,(byte)96, (byte)47, (byte)248,(byte)194,
+ (byte)230,(byte)62, (byte)121,(byte)117,(byte)163,(byte)35, (byte)80, (byte)15, (byte)113,(byte)203,
+ (byte)71, (byte)202,(byte)36, (byte)187,(byte)163,(byte)78, (byte)228,(byte)31, (byte)3, (byte)53,
+ (byte)214,(byte)149,(byte)170,(byte)214,(byte)161,(byte)180,(byte)53, (byte)207,(byte)158,(byte)150,
+ (byte)161,(byte)37, (byte)59, (byte)150,(byte)107,(byte)161,(byte)9, (byte)195,(byte)79, (byte)254,
+ (byte)62, (byte)231,(byte)13, (byte)195,(byte)173,(byte)139,(byte)15, (byte)153,(byte)62, (byte)20,
+ (byte)204,(byte)111,(byte)64, (byte)89, (byte)180,(byte)201,(byte)58, (byte)64, (byte)15, (byte)195,
+ (byte)18, (byte)29, (byte)29, (byte)44, (byte)5, (byte)101,(byte)132,(byte)113,(byte)204,(byte)251,
+ (byte)225,(byte)3, (byte)82, (byte)52, (byte)62, (byte)86, (byte)142,(byte)43, (byte)240,(byte)201,
+ (byte)26, (byte)226,(byte)143,(byte)162,(byte)9, (byte)97, (byte)96, (byte)185,(byte)59, (byte)85,
+ (byte)54, (byte)115,(byte)135,(byte)199,(byte)26, (byte)58, (byte)185,(byte)100,(byte)118,(byte)48,
+ (byte)119,(byte)110,(byte)203,(byte)115,(byte)74, (byte)152,(byte)144,(byte)137,(byte)13, (byte)18,
+ (byte)192,(byte)82, (byte)101,(byte)163,(byte)8, (byte)128,(byte)57, (byte)68, (byte)183,(byte)225,
+ (byte)79, (byte)6, (byte)143,(byte)94, (byte)203,(byte)203,(byte)121,(byte)52, (byte)128,(byte)94,
+ (byte)184,(byte)223,(byte)107,(byte)217,(byte)68, (byte)118,(byte)145,(byte)164,(byte)13, (byte)220,
+ (byte)135,(byte)11, (byte)74, (byte)193,(byte)48, (byte)7, (byte)95, (byte)190,(byte)17, (byte)0,
+ (byte)69, (byte)109,(byte)6, (byte)64, (byte)86, (byte)80, (byte)93, (byte)82, (byte)20, (byte)106,
+ (byte)191,(byte)201,(byte)13, (byte)91, (byte)132,(byte)102,(byte)47, (byte)188,(byte)123,(byte)79,
+ (byte)209,(byte)43, (byte)180,(byte)152,(byte)128,(byte)20, (byte)182,(byte)148,(byte)19, (byte)24,
+ (byte)230,(byte)249,(byte)42, (byte)51, (byte)197,(byte)176,(byte)113,(byte)44, (byte)100,(byte)95,
+ (byte)59, (byte)91, (byte)78, (byte)226,(byte)184,(byte)224,(byte)72, (byte)233,(byte)133,(byte)154,
+ (byte)42, (byte)221,(byte)32, (byte)165,(byte)41, (byte)156,(byte)165,(byte)247,(byte)86, (byte)115,
+ (byte)183,(byte)22, (byte)89, (byte)17, (byte)165,(byte)215,(byte)148,(byte)32, (byte)199,(byte)64,
+ (byte)139,(byte)171,(byte)236,(byte)43, (byte)5, (byte)36, (byte)35, (byte)223,(byte)35, (byte)247,
+ (byte)255,(byte)112,(byte)27, (byte)215,(byte)57, (byte)251,(byte)236,(byte)128,(byte)168,(byte)219,
+ (byte)146,(byte)235,(byte)241,(byte)68, (byte)213,(byte)127,(byte)63, (byte)231,(byte)236,(byte)176,
+ (byte)166,(byte)121,(byte)203,(byte)114,(byte)33, (byte)19, (byte)200,(byte)167,(byte)155,(byte)27,
+ (byte)38, (byte)109,(byte)133,(byte)1, (byte)184,(byte)173,(byte)253,(byte)198,(byte)122,(byte)98,
+ (byte)196,(byte)43, (byte)145,(byte)86, (byte)182,(byte)208,(byte)78, (byte)246,(byte)234,(byte)249,
+ (byte)229,(byte)202,(byte)75, (byte)66, (byte)108,(byte)134,(byte)81, (byte)134,(byte)90, (byte)251,
+ (byte)137,(byte)155,(byte)209,(byte)11, (byte)249,(byte)87, (byte)164,(byte)98, (byte)242,(byte)51,
+ (byte)184,(byte)162,(byte)35, (byte)20, (byte)248,(byte)14, (byte)224,(byte)76, (byte)31, (byte)132,
+ (byte)125,(byte)44, (byte)83, (byte)15, (byte)221,(byte)43, (byte)62, (byte)187,(byte)211,(byte)176,
+ (byte)41, (byte)70, (byte)187,(byte)3, (byte)48, (byte)150,(byte)206,(byte)54, (byte)38, (byte)33,
+ (byte)94, (byte)133,(byte)145,(byte)148,(byte)58, (byte)219,(byte)252,(byte)124,(byte)251,(byte)46,
+ (byte)72, (byte)35, (byte)244,(byte)33, (byte)97, (byte)50, (byte)21, (byte)207,(byte)163,(byte)3,
+ (byte)226,(byte)225,(byte)252,(byte)149,(byte)214,(byte)200,(byte)132,(byte)65, (byte)224,(byte)121,
+ (byte)205,(byte)241,(byte)107,(byte)155,(byte)252,(byte)158,(byte)64, (byte)40, (byte)252,(byte)143,
+ (byte)76, (byte)71, (byte)227,(byte)13, (byte)176,(byte)50, (byte)250,(byte)115,(byte)198,(byte)64,
+ (byte)174,(byte)146,(byte)108,(byte)106,(byte)66, (byte)98, (byte)78, (byte)196,(byte)126,(byte)118,
+ (byte)51, (byte)65, (byte)251,(byte)8, (byte)28, (byte)75, (byte)123,(byte)92, (byte)5, (byte)125,
+ (byte)16, (byte)127,(byte)250,(byte)65, (byte)178,(byte)54, (byte)169,(byte)109,(byte)94, (byte)171,
+ (byte)97, (byte)154,(byte)232,(byte)24, (byte)196,(byte)91, (byte)103,(byte)90, (byte)217,(byte)75,
+ (byte)126,(byte)76, (byte)129,(byte)240,(byte)67, (byte)131,(byte)147,(byte)178,(byte)29, (byte)234,
+ (byte)150,(byte)91, (byte)78, (byte)165,(byte)76, (byte)200,(byte)99, (byte)175,(byte)240,(byte)3,
+ (byte)76, (byte)151,(byte)111,(byte)167,(byte)220,(byte)162,(byte)7, (byte)249,(byte)12, (byte)201,
+ (byte)171,(byte)58, (byte)170,(byte)26, (byte)149,(byte)224,(byte)135,(byte)201,(byte)186,(byte)201,
+ (byte)253,(byte)153,(byte)248,(byte)148,(byte)171,(byte)197,(byte)70, (byte)179,(byte)127,(byte)210,
+ (byte)30, (byte)172,(byte)207,(byte)179,(byte)140,(byte)240,(byte)244,(byte)2, (byte)24, (byte)156,
+ (byte)116,(byte)6, (byte)237,(byte)42, (byte)221,(byte)201,(byte)244,(byte)207,(byte)123,(byte)19,
+ (byte)189,(byte)58, (byte)189,(byte)107,(byte)223,(byte)44, (byte)230,(byte)114,(byte)115,(byte)194,
+ (byte)189,(byte)163,(byte)189,(byte)224,(byte)161,(byte)221,(byte)40, (byte)29, (byte)73, (byte)244,
+ (byte)231,(byte)213,(byte)139,(byte)178,(byte)248,(byte)84, (byte)137,(byte)65, (byte)124,(byte)98,
+ (byte)248,(byte)62, (byte)229,(byte)86, (byte)128,(byte)57, (byte)106,(byte)38, (byte)193,(byte)185,
+ (byte)10, (byte)162,(byte)0, (byte)0, (byte)0, (byte)1, (byte)0, (byte)5, (byte)88, (byte)46,
+ (byte)53, (byte)48, (byte)57, (byte)0, (byte)0, (byte)2, (byte)72, (byte)48, (byte)130,(byte)2,
+ (byte)68, (byte)48, (byte)130,(byte)1, (byte)173,(byte)2, (byte)4, (byte)72, (byte)76, (byte)18,
+ (byte)25, (byte)48, (byte)13, (byte)6, (byte)9, (byte)42, (byte)134,(byte)72, (byte)134,(byte)247,
+ (byte)13, (byte)1, (byte)1, (byte)4, (byte)5, (byte)0, (byte)48, (byte)105,(byte)49, (byte)16,
+ (byte)48, (byte)14, (byte)6, (byte)3, (byte)85, (byte)4, (byte)6, (byte)19, (byte)7, (byte)67,
+ (byte)111,(byte)117,(byte)110,(byte)116,(byte)114,(byte)121,(byte)49, (byte)17, (byte)48, (byte)15,
+ (byte)6, (byte)3, (byte)85, (byte)4, (byte)7, (byte)19, (byte)8, (byte)76, (byte)111,(byte)99,
+ (byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)49, (byte)28, (byte)48, (byte)26, (byte)6,
+ (byte)3, (byte)85, (byte)4, (byte)11, (byte)19, (byte)19, (byte)79, (byte)114,(byte)103,(byte)97,
+ (byte)110,(byte)105,(byte)122,(byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)97, (byte)108,
+ (byte)32, (byte)85, (byte)110,(byte)105,(byte)116,(byte)49, (byte)21, (byte)48, (byte)19, (byte)6,
+ (byte)3, (byte)85, (byte)4, (byte)10, (byte)19, (byte)12, (byte)79, (byte)114,(byte)103,(byte)97,
+ (byte)110,(byte)105,(byte)122,(byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)49, (byte)13,
+ (byte)48, (byte)11, (byte)6, (byte)3, (byte)85, (byte)4, (byte)3, (byte)19, (byte)4, (byte)78,
+ (byte)97, (byte)109,(byte)101,(byte)48, (byte)30, (byte)23, (byte)13, (byte)48, (byte)56, (byte)48,
+ (byte)54, (byte)48, (byte)56, (byte)49, (byte)55, (byte)48, (byte)56, (byte)52, (byte)49, (byte)90,
+ (byte)23, (byte)13, (byte)48, (byte)57, (byte)48, (byte)54, (byte)48, (byte)56, (byte)49, (byte)55,
+ (byte)48, (byte)56, (byte)52, (byte)49, (byte)90, (byte)48, (byte)105,(byte)49, (byte)16, (byte)48,
+ (byte)14, (byte)6, (byte)3, (byte)85, (byte)4, (byte)6, (byte)19, (byte)7, (byte)67, (byte)111,
+ (byte)117,(byte)110,(byte)116,(byte)114,(byte)121,(byte)49, (byte)17, (byte)48, (byte)15, (byte)6,
+ (byte)3, (byte)85, (byte)4, (byte)7, (byte)19, (byte)8, (byte)76, (byte)111,(byte)99, (byte)97,
+ (byte)116,(byte)105,(byte)111,(byte)110,(byte)49, (byte)28, (byte)48, (byte)26, (byte)6, (byte)3,
+ (byte)85, (byte)4, (byte)11, (byte)19, (byte)19, (byte)79, (byte)114,(byte)103,(byte)97, (byte)110,
+ (byte)105,(byte)122,(byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)97, (byte)108,(byte)32,
+ (byte)85, (byte)110,(byte)105,(byte)116,(byte)49, (byte)21, (byte)48, (byte)19, (byte)6, (byte)3,
+ (byte)85, (byte)4, (byte)10, (byte)19, (byte)12, (byte)79, (byte)114,(byte)103,(byte)97, (byte)110,
+ (byte)105,(byte)122,(byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)49, (byte)13, (byte)48,
+ (byte)11, (byte)6, (byte)3, (byte)85, (byte)4, (byte)3, (byte)19, (byte)4, (byte)78, (byte)97,
+ (byte)109,(byte)101,(byte)48, (byte)129,(byte)159,(byte)48, (byte)13, (byte)6, (byte)9, (byte)42,
+ (byte)134,(byte)72, (byte)134,(byte)247,(byte)13, (byte)1, (byte)1, (byte)1, (byte)5, (byte)0,
+ (byte)3, (byte)129,(byte)141,(byte)0, (byte)48, (byte)129,(byte)137,(byte)2, (byte)129,(byte)129,
+ (byte)0, (byte)137,(byte)239,(byte)22, (byte)193,(byte)171,(byte)79, (byte)177,(byte)85, (byte)159,
+ (byte)210,(byte)81, (byte)174,(byte)63, (byte)210,(byte)57, (byte)43, (byte)172,(byte)130,(byte)205,
+ (byte)144,(byte)207,(byte)100,(byte)16, (byte)69, (byte)78, (byte)72, (byte)22, (byte)155,(byte)44,
+ (byte)146,(byte)252,(byte)202,(byte)119,(byte)199,(byte)69, (byte)38, (byte)48, (byte)38, (byte)39,
+ (byte)46, (byte)119,(byte)219,(byte)200,(byte)105,(byte)216,(byte)188,(byte)162,(byte)175,(byte)74,
+ (byte)43, (byte)175,(byte)6, (byte)148,(byte)131,(byte)125,(byte)226,(byte)198,(byte)239,(byte)115,
+ (byte)204,(byte)196,(byte)28, (byte)189,(byte)108,(byte)236,(byte)29, (byte)132,(byte)72, (byte)207,
+ (byte)238,(byte)3, (byte)97, (byte)223,(byte)227,(byte)82, (byte)115,(byte)202,(byte)134,(byte)43,
+ (byte)242,(byte)83, (byte)70, (byte)226,(byte)172,(byte)162,(byte)177,(byte)183,(byte)128,(byte)126,
+ (byte)164,(byte)233,(byte)250,(byte)230,(byte)18, (byte)177,(byte)126,(byte)40, (byte)36, (byte)30,
+ (byte)169,(byte)124,(byte)126,(byte)203,(byte)23, (byte)252,(byte)38, (byte)55, (byte)250,(byte)181,
+ (byte)232,(byte)168,(byte)84, (byte)232,(byte)140,(byte)85, (byte)119,(byte)163,(byte)255,(byte)117,
+ (byte)133,(byte)174,(byte)51, (byte)195,(byte)8, (byte)174,(byte)200,(byte)142,(byte)43, (byte)2,
+ (byte)3, (byte)1, (byte)0, (byte)1, (byte)48, (byte)13, (byte)6, (byte)9, (byte)42, (byte)134,
+ (byte)72, (byte)134,(byte)247,(byte)13, (byte)1, (byte)1, (byte)4, (byte)5, (byte)0, (byte)3,
+ (byte)129,(byte)129,(byte)0, (byte)9, (byte)240,(byte)8, (byte)65, (byte)178,(byte)238,(byte)119,
+ (byte)127,(byte)249,(byte)164,(byte)9, (byte)159,(byte)110,(byte)132,(byte)177,(byte)76, (byte)239,
+ (byte)164,(byte)27, (byte)130,(byte)174,(byte)97, (byte)100,(byte)2, (byte)154,(byte)231,(byte)44,
+ (byte)217,(byte)30, (byte)210,(byte)42, (byte)221,(byte)225,(byte)114,(byte)205,(byte)165,(byte)152,
+ (byte)188,(byte)232,(byte)1, (byte)128,(byte)143,(byte)116,(byte)113,(byte)128,(byte)50, (byte)199,
+ (byte)80, (byte)16, (byte)172,(byte)112,(byte)129,(byte)236,(byte)34, (byte)189,(byte)106,(byte)79,
+ (byte)152,(byte)67, (byte)233,(byte)61, (byte)114,(byte)137,(byte)40, (byte)157,(byte)233,(byte)83,
+ (byte)123,(byte)28, (byte)138,(byte)168,(byte)46, (byte)151,(byte)36, (byte)177,(byte)7, (byte)22,
+ (byte)148,(byte)253,(byte)80, (byte)144,(byte)122,(byte)52, (byte)104,(byte)196,(byte)15, (byte)225,
+ (byte)148,(byte)136,(byte)193,(byte)68, (byte)133,(byte)113,(byte)48, (byte)244,(byte)8, (byte)64,
+ (byte)117,(byte)110,(byte)115,(byte)80, (byte)110,(byte)105,(byte)56, (byte)20, (byte)170,(byte)125,
+ (byte)182,(byte)159,(byte)190,(byte)4, (byte)173,(byte)193,(byte)200,(byte)153,(byte)246,(byte)155,
+ (byte)249,(byte)33, (byte)180,(byte)233,(byte)48, (byte)109,(byte)55, (byte)208,(byte)209,(byte)196,
+ (byte)16, (byte)23, (byte)172,(byte)125,(byte)207,(byte)94, (byte)238,(byte)23, (byte)38, (byte)60,
+ (byte)58, (byte)92, (byte)244,(byte)100,(byte)145,(byte)44, (byte)204,(byte)92, (byte)21, (byte)136,
+ (byte)39, };
+
+
+ public static class AnonymousTrustManager implements X509TrustManager {
+
+ public boolean isClientTrusted(X509Certificate[] cert) {
+ return true;
+ }
+
+ public boolean isServerTrusted(X509Certificate[] cert) {
+ return true;
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+
+ public void checkClientTrusted(X509Certificate[] arg0, String arg1)
+ throws CertificateException {
+ }
+
+ public void checkServerTrusted(X509Certificate[] arg0, String arg1)
+ throws CertificateException {
+ }
+ }
+
+ private static SSLContext sslContext;
+ private static SocketFactory factory;
+
+ static {
+ try {
+ KeyStore store = KeyStore.getInstance("JKS");
+ KeyManagerFactory keyFactory = KeyManagerFactory.getInstance("SunX509");
+ sslContext = SSLContext.getInstance("TLS");//SSLv3");
+ InputStream stream = new ByteArrayInputStream(CERTIFICATE);
+ X509TrustManager trustManager = new AnonymousTrustManager();
+ X509TrustManager[] trustManagers = new X509TrustManager[]{trustManager};
+
+ store.load(stream, "password".toCharArray());
+ keyFactory.init(store, "password".toCharArray());
+ sslContext.init(keyFactory.getKeyManagers(), trustManagers, null);
+
+ factory = sslContext.getSocketFactory();
+ }catch(Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+
+ public SSLContext getServerSSLContext() {
+ return sslContext;
+ }
+
+ public SocketFactory getClientSocketFactory() {
+ return factory;
+ }
+
+ public static void main(String[] list) throws Exception {
+ FileOutputStream out = new FileOutputStream("c:\\client");
+ final PrintStream console = System.out;
+ OutputStream dup = new FilterOutputStream(out) {
+ public void write(int off) throws IOException {
+ console.write(off);
+ out.write(off);
+ }
+ public void write(byte[] b, int off, int len) throws IOException {
+ out.write(b, off, len);
+ console.write(b, off, len);
+ }
+ public void flush() throws IOException {
+ out.flush();
+ console.flush();
+ }
+ public void close() throws IOException {
+ out.close();
+ }
+ };
+ PrintStream p = new PrintStream(dup, true);
+
+ System.setOut(p);
+ System.setErr(p);
+ Socket socket = factory.createSocket("localhost", 9091);
+ OutputStream sockOut = socket.getOutputStream();
+ sockOut.write("GET /tmp/amazon.htm HTTP/1.1\r\nConnection: keep-alive\r\n\r\n".getBytes("ISO-8859-1"));
+ sockOut.flush();
+ InputStream in = socket.getInputStream();
+ byte[] buf = new byte[1024];
+ int all = 0;
+ int count = 0;
+ while((count = in.read(buf)) != -1) {
+ all += count;
+ if(all >= 564325) {
+ break;
+ }
+ System.out.write(buf, 0, count);
+ System.out.flush();
+ }
+ console.println(">>>>>>>>>>>>>> ALL=["+all+"]");
+ System.err.println("FINISHED READING");
+ Thread.sleep(10000);
+
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/Connector.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/Connector.java
new file mode 100644
index 00000000..d002c865
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/Connector.java
@@ -0,0 +1,9 @@
+package org.simpleframework.http.core;
+
+import java.net.Socket;
+
+public interface Connector {
+
+ public Socket getSocket() throws Exception;
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/ConversationTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/ConversationTest.java
new file mode 100644
index 00000000..00855c29
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/ConversationTest.java
@@ -0,0 +1,126 @@
+package org.simpleframework.http.core;
+
+import org.simpleframework.http.core.Conversation;
+
+import junit.framework.TestCase;
+
+public class ConversationTest extends TestCase {
+
+ private MockRequest request;
+ private MockResponse response;
+ private Conversation support;
+
+ public void setUp() {
+ request = new MockRequest();
+ response = new MockResponse();
+ support = new Conversation(request, response);
+ }
+
+ public void testWebSocket() {
+ request.setMajor(1);
+ request.setMinor(1);
+ response.setValue("Connection", "upgrade");
+
+ assertFalse(support.isWebSocket());
+ assertFalse(support.isTunnel());
+ assertTrue(support.isKeepAlive());
+
+ request.setValue("Upgrade", "WebSocket");
+
+ assertFalse(support.isWebSocket());
+ assertFalse(support.isTunnel());
+ assertTrue(support.isKeepAlive());
+
+ response.setCode(101);
+ response.setValue("Upgrade", "websocket");
+
+ assertTrue(support.isWebSocket());
+ assertTrue(support.isTunnel());
+ assertTrue(support.isKeepAlive());
+ }
+
+ public void testConnectTunnel() {
+ request.setMajor(1);
+ request.setMinor(1);
+ response.setCode(404);
+ request.setMethod("CONNECT");
+
+ assertFalse(support.isWebSocket());
+ assertFalse(support.isTunnel());
+ assertTrue(support.isKeepAlive());
+
+ response.setCode(200);
+
+ assertFalse(support.isWebSocket());
+ assertTrue(support.isTunnel());
+ assertTrue(support.isKeepAlive());
+ }
+
+ public void testResponse() {
+ request.setMajor(1);
+ request.setMinor(1);
+ response.setValue("Content-Length", "10");
+ response.setValue("Connection", "close");
+
+ assertFalse(support.isKeepAlive());
+ assertTrue(support.isPersistent());
+ assertEquals(support.getContentLength(), 10);
+ assertEquals(support.isChunkedEncoded(), false);
+
+ request.setMinor(0);
+
+ assertFalse(support.isKeepAlive());
+ assertFalse(support.isPersistent());
+
+ response.setValue("Connection", "keep-alive");
+
+ assertTrue(support.isKeepAlive());
+ assertFalse(support.isPersistent());
+
+ response.setValue("Transfer-Encoding", "chunked");
+
+ assertTrue(support.isChunkedEncoded());
+ assertTrue(support.isKeepAlive());
+ }
+
+ public void testConversation() {
+ request.setMajor(1);
+ request.setMinor(1);
+ support.setChunkedEncoded();
+
+ assertEquals(response.getValue("Transfer-Encoding"), "chunked");
+ assertEquals(response.getValue("Connection"), "keep-alive");
+ assertTrue(support.isKeepAlive());
+ assertTrue(support.isPersistent());
+
+ request.setMinor(0);
+ support.setChunkedEncoded();
+
+ assertEquals(response.getValue("Connection"), "close");
+ assertFalse(support.isKeepAlive());
+
+ request.setMajor(1);
+ request.setMinor(1);
+ response.setValue("Content-Length", "10");
+ response.setValue("Connection", "close");
+
+ assertFalse(support.isKeepAlive());
+ assertTrue(support.isPersistent());
+ assertEquals(support.getContentLength(), 10);
+
+ request.setMinor(0);
+
+ assertFalse(support.isKeepAlive());
+ assertFalse(support.isPersistent());
+
+ response.setValue("Connection", "keep-alive");
+
+ assertTrue(support.isKeepAlive());
+ assertFalse(support.isPersistent());
+
+ response.setValue("Transfer-Encoding", "chunked");
+
+ assertTrue(support.isChunkedEncoded());
+ assertTrue(support.isKeepAlive());
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/DribbleCursor.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/DribbleCursor.java
new file mode 100644
index 00000000..14f2768c
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/DribbleCursor.java
@@ -0,0 +1,62 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import org.simpleframework.transport.ByteCursor;
+
+public class DribbleCursor implements ByteCursor {
+
+ private ByteCursor cursor;
+ private byte[] swap;
+ private int dribble;
+
+ public DribbleCursor(ByteCursor cursor, int dribble) {
+ this.cursor = cursor;
+ this.dribble = dribble;
+ this.swap = new byte[1];
+ }
+
+ public boolean isOpen() throws IOException {
+ return true;
+ }
+
+ public boolean isReady() throws IOException {
+ return cursor.isReady();
+ }
+
+ public int ready() throws IOException {
+ int ready = cursor.ready();
+
+ return Math.min(ready, dribble);
+ }
+
+ public int read() throws IOException {
+ if(read(swap) > 0) {
+ return swap[0] & 0xff;
+ }
+ return 0;
+ }
+
+
+ public int read(byte[] data) throws IOException {
+ return read(data, 0, data.length);
+ }
+
+ public int read(byte[] data, int off, int len) throws IOException {
+ int size = Math.min(len, dribble);
+
+ return cursor.read(data, off, size);
+ }
+
+ public int reset(int len) throws IOException {
+ return cursor.reset(len);
+ }
+
+ public void push(byte[] data) throws IOException {
+ cursor.push(data);
+ }
+
+ public void push(byte[] data, int off, int len) throws IOException {
+ cursor.push(data, off, len);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/FixedConsumerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/FixedConsumerTest.java
new file mode 100644
index 00000000..f0011ce5
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/FixedConsumerTest.java
@@ -0,0 +1,80 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.common.buffer.Buffer;
+import org.simpleframework.http.message.FixedLengthConsumer;
+import org.simpleframework.transport.ByteCursor;
+
+public class FixedConsumerTest extends TestCase implements Allocator {
+
+ private Buffer buffer;
+
+ public Buffer allocate() {
+ return buffer;
+ }
+
+ public Buffer allocate(long size) {
+ return buffer;
+ }
+
+ public void testConsumer() throws Exception {
+ testConsumer(10, 10, 10);
+ testConsumer(1024, 10, 1024);
+ testConsumer(1024, 1024, 1024);
+ testConsumer(1024, 1024, 1023);
+ testConsumer(1024, 1, 1024);
+ testConsumer(1, 1, 1);
+ testConsumer(2, 2, 2);
+ testConsumer(3, 1, 2);
+ }
+
+ public void testConsumer(int entitySize, int dribble, int limitSize) throws Exception {
+ StringBuffer buf = new StringBuffer();
+
+ // Ensure that we dont try read forever
+ limitSize = Math.min(entitySize, limitSize);
+
+ for(int i = 0, line = 0; i < entitySize; i++) {
+ String text = "["+String.valueOf(i)+"]";
+
+ line += text.length();
+ buf.append(text);
+
+ if(line >= 48) {
+ buf.append("\n");
+ line = 0;
+ }
+
+ }
+ buffer = new ArrayAllocator().allocate();
+
+ String requestBody = buf.toString();
+ FixedLengthConsumer consumer = new FixedLengthConsumer(this, limitSize);
+ ByteCursor cursor = new DribbleCursor(new StreamCursor(requestBody), dribble);
+ byte[] requestBytes = requestBody.getBytes("UTF-8");
+
+ while(!consumer.isFinished()) {
+ consumer.consume(cursor);
+ }
+ byte[] consumedBytes = buffer.encode("UTF-8").getBytes("UTF-8");
+
+ assertEquals(buffer.encode("UTF-8").length(), limitSize);
+
+ for(int i = 0; i < limitSize; i++) {
+ if(consumedBytes[i] != requestBytes[i]) {
+ throw new IOException("Fixed consumer modified the request!");
+ }
+ }
+ }
+
+ public void close() throws IOException {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/FixedProducerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/FixedProducerTest.java
new file mode 100644
index 00000000..f7b8f335
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/FixedProducerTest.java
@@ -0,0 +1,50 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import org.simpleframework.http.core.FixedLengthEncoder;
+
+import junit.framework.TestCase;
+
+public class FixedProducerTest extends TestCase {
+
+ public void testContent() throws IOException {
+ testContent(1024, 1);
+ testContent(1024, 2);
+ testContent(512, 20);
+ testContent(64, 64);
+ }
+
+ public void testContent(int chunkSize, int count) throws IOException {
+ MockSender sender = new MockSender((chunkSize * count) + chunkSize);
+ MockObserver monitor = new MockObserver();
+ FixedLengthEncoder producer = new FixedLengthEncoder(monitor, sender, chunkSize * count);
+ byte[] chunk = new byte[chunkSize];
+
+ for(int i = 0; i < chunk.length; i++) {
+ chunk[i] = (byte)String.valueOf(i).charAt(0);
+ }
+ for(int i = 0; i < count; i++) {
+ producer.encode(chunk, 0, chunkSize);
+ }
+ producer.close();
+
+ System.err.println(sender.getBuffer().encode());
+
+ assertTrue(monitor.isReady());
+ assertFalse(monitor.isError());
+ assertFalse(monitor.isClose());
+
+ sender = new MockSender((chunkSize * count) + chunkSize);
+ monitor = new MockObserver();
+ producer = new FixedLengthEncoder(monitor, sender, chunkSize * count);
+
+ for(int i = 0; i < count; i++) {
+ producer.encode(chunk, 0, chunkSize);
+ }
+ producer.close();
+
+ assertFalse(monitor.isError());
+ assertTrue(monitor.isReady());
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MessageTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MessageTest.java
new file mode 100644
index 00000000..b972f039
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MessageTest.java
@@ -0,0 +1,72 @@
+package org.simpleframework.http.core;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.simpleframework.http.message.MessageHeader;
+
+import junit.framework.TestCase;
+
+public class MessageTest extends TestCase {
+
+ public void testMessage() {
+ MessageHeader message = new MessageHeader();
+
+ message.addValue("Content-Length", "10");
+ message.addValue("Connection", "keep-alive");
+ message.addValue("Accept", "image/gif, image/jpeg, */*");
+ message.addValue("Set-Cookie", "a=b");
+ message.addValue("Set-Cookie", "b=c");
+
+ assertEquals(message.getValue("CONTENT-LENGTH"), "10");
+ assertEquals(message.getValue("Content-Length"), "10");
+ assertEquals(message.getValue("CONTENT-length"), "10");
+ assertEquals(message.getValue("connection"), "keep-alive");
+ assertEquals(message.getValue("CONNECTION"), "keep-alive");
+
+ assertTrue(message.getValues("CONNECTION") != null);
+ assertEquals(message.getValues("connection").size(), 1);
+
+ assertTrue(message.getValues("set-cookie") != null);
+ assertEquals(message.getValues("set-cookie").size(), 2);
+ assertTrue(message.getValues("SET-COOKIE").contains("a=b"));
+ assertTrue(message.getValues("SET-COOKIE").contains("b=c"));
+
+ assertTrue(message.getNames().contains("Content-Length"));
+ assertFalse(message.getNames().contains("CONTENT-LENGTH"));
+ assertTrue(message.getNames().contains("Connection"));
+ assertFalse(message.getNames().contains("CONNECTION"));
+ assertTrue(message.getNames().contains("Set-Cookie"));
+ assertFalse(message.getNames().contains("SET-COOKIE"));
+
+ message.setValue("Set-Cookie", "d=e");
+
+ assertTrue(message.getValues("set-cookie") != null);
+ assertEquals(message.getValues("set-cookie").size(), 1);
+ assertFalse(message.getValues("SET-COOKIE").contains("a=b"));
+ assertFalse(message.getValues("SET-COOKIE").contains("b=c"));
+ assertTrue(message.getValues("SET-COOKIE").contains("d=e"));
+ }
+
+ public void testDates() {
+ MessageHeader message = new MessageHeader();
+ DateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
+ TimeZone zone = TimeZone.getTimeZone("GMT");
+ long time = System.currentTimeMillis();
+ Date date = new Date(time);
+
+ format.setTimeZone(zone);
+ message.setValue("Date", format.format(date));
+
+ assertEquals(format.format(date), message.getValue("date"));
+ assertEquals(new Date(message.getDate("DATE")).toString(), date.toString());
+
+ message.setDate("Date", time);
+
+ assertEquals(format.format(date), message.getValue("date"));
+ assertEquals(new Date(message.getDate("DATE")).toString(), date.toString());
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MockChannel.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockChannel.java
new file mode 100644
index 00000000..92a4f5d4
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockChannel.java
@@ -0,0 +1,57 @@
+package org.simpleframework.http.core;
+
+import java.nio.channels.SocketChannel;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.simpleframework.common.lease.Lease;
+import org.simpleframework.http.MockTrace;
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.ByteWriter;
+import org.simpleframework.transport.trace.Trace;
+
+
+public class MockChannel implements Channel {
+
+ private ByteCursor cursor;
+
+ public MockChannel(ByteCursor cursor) {
+ this.cursor = cursor;
+ }
+
+ public boolean isSecure() {
+ return false;
+ }
+
+ public Trace getTrace(){
+ return new MockTrace();
+ }
+
+ public Lease getLease() {
+ return null;
+ }
+
+ public Certificate getCertificate() {
+ return null;
+ }
+
+ public ByteCursor getCursor() {
+ return cursor;
+ }
+
+ public ByteWriter getWriter() {
+ return new MockSender();
+ }
+
+ public Map getAttributes() {
+ return new HashMap();
+ }
+
+ public void close() {}
+
+ public SocketChannel getSocket() {
+ return null;
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MockController.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockController.java
new file mode 100644
index 00000000..b6318036
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockController.java
@@ -0,0 +1,55 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import org.simpleframework.transport.Channel;
+
+public class MockController implements Controller {
+
+ private boolean ready;
+ private boolean sleep;
+ private boolean start;
+ private boolean initiated;
+ private boolean stop;
+
+ public void start(Channel channel) throws IOException {
+ initiated = true;
+ }
+
+ public void ready(Collector collector) throws IOException {
+ ready = true;
+ }
+
+ public void select(Collector collector) throws IOException {
+ sleep = true;
+ }
+
+ public void start(Collector collector) throws IOException {
+ start = true;
+ }
+
+ public void stop() throws IOException {
+ stop = true;
+ }
+
+ public boolean isStopped() {
+ return stop;
+ }
+
+ public boolean isInitiated() {
+ return initiated;
+ }
+
+ public boolean isReady() {
+ return ready;
+ }
+
+ public boolean isSleep() {
+ return sleep;
+ }
+
+ public boolean isStart() {
+ return start;
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MockEntity.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockEntity.java
new file mode 100644
index 00000000..e0ec8966
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockEntity.java
@@ -0,0 +1,49 @@
+
+package org.simpleframework.http.core;
+
+import org.simpleframework.http.message.Body;
+import org.simpleframework.http.message.Entity;
+import org.simpleframework.http.message.Header;
+import org.simpleframework.transport.Channel;
+
+
+public class MockEntity implements Entity {
+
+ private Body body;
+ private Header header;
+
+ public MockEntity() {
+ super();
+ }
+
+ public MockEntity(Body body) {
+ this.body = body;
+ }
+
+ public MockEntity(Body body, Header header) {
+ this.body = body;
+ this.header = header;
+ }
+
+ public long getTime() {
+ return 0;
+ }
+
+ public Body getBody() {
+ return body;
+ }
+
+ public Header getHeader() {
+ return header;
+ }
+
+ public Channel getChannel() {
+ return null;
+ }
+
+ public void close() {}
+
+ public long getStart() {
+ return 0;
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MockObserver.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockObserver.java
new file mode 100644
index 00000000..cb4b41ed
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockObserver.java
@@ -0,0 +1,62 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import org.simpleframework.transport.ByteWriter;
+
+
+public class MockObserver implements BodyObserver {
+
+ private boolean close;
+
+ private boolean error;
+
+ private boolean ready;
+
+ private boolean commit;
+
+ public MockObserver() {
+ super();
+ }
+
+ public void close(ByteWriter sender) {
+ close = true;
+ }
+
+ public boolean isClose() {
+ return close;
+ }
+
+ public boolean isError() {
+ return error;
+ }
+
+ public void ready(ByteWriter sender) {
+ ready = true;
+ }
+
+ public boolean isReady() {
+ return ready;
+ }
+
+ public void error(ByteWriter sender) {
+ error = true;
+ }
+
+ public boolean isClosed() {
+ return close || error;
+ }
+
+ public long getTime() {
+ return 0;
+ }
+
+ public void commit(ByteWriter sender) {
+ this.commit = commit;
+ }
+
+ public boolean isCommitted() {
+ return commit;
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MockPart.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockPart.java
new file mode 100644
index 00000000..614a7aa8
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockPart.java
@@ -0,0 +1,49 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import org.simpleframework.http.ContentDisposition;
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.Part;
+import org.simpleframework.http.message.MockBody;
+
+public class MockPart extends MockBody implements Part {
+
+ private String name;
+ private boolean file;
+
+ public MockPart(String name, String body, boolean file) {
+ super(body);
+ this.file = file;
+ this.name = name;
+ }
+
+ public String getContent() throws IOException {
+ return body;
+ }
+
+ public ContentType getContentType() {
+ return null;
+ }
+
+ public ContentDisposition getDisposition() {
+ return null;
+ }
+
+ public String getHeader(String name) {
+ return null;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isFile() {
+ return file;
+ }
+
+ public String getFileName() {
+ return null;
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MockProxyRequest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockProxyRequest.java
new file mode 100644
index 00000000..a7f12b64
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockProxyRequest.java
@@ -0,0 +1,67 @@
+package org.simpleframework.http.core;
+
+import java.util.List;
+
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.Path;
+import org.simpleframework.http.Query;
+import org.simpleframework.http.RequestHeader;
+
+public class MockProxyRequest extends MockRequest {
+
+ private RequestHeader header;
+
+ public MockProxyRequest(RequestHeader header) {
+ this.header = header;
+ }
+
+ public long getContentLength() {
+ return header.getContentLength();
+ }
+
+ public ContentType getContentType() {
+ return header.getContentType();
+ }
+
+ public String getValue(String name) {
+ return header.getValue(name);
+ }
+
+ public List<String> getValues(String name) {
+ return header.getValues(name);
+ }
+
+ public int getMajor() {
+ return header.getMajor();
+ }
+
+ public String getMethod() {
+ return header.getMethod();
+ }
+
+ public int getMinor() {
+ return header.getMajor();
+ }
+
+ public Path getPath() {
+ return header.getPath();
+ }
+
+ public Query getQuery() {
+ return header.getQuery();
+ }
+
+ public String getTarget() {
+ return header.getTarget();
+ }
+
+
+ public String getParameter(String name) {
+ return header.getQuery().get(name);
+ }
+
+ public Cookie getCookie(String name) {
+ return header.getCookie(name);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MockRequest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockRequest.java
new file mode 100644
index 00000000..f382a321
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockRequest.java
@@ -0,0 +1,202 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.simpleframework.http.ContentDisposition;
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.Part;
+import org.simpleframework.http.Path;
+import org.simpleframework.http.Query;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.message.MessageHeader;
+import org.simpleframework.http.message.RequestConsumer;
+import org.simpleframework.http.parse.AddressParser;
+import org.simpleframework.http.parse.ContentDispositionParser;
+import org.simpleframework.http.parse.ContentTypeParser;
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.Channel;
+
+public class MockRequest extends RequestMessage implements Request {
+
+ private MessageHeader message;
+ private Channel channel;
+ private String target;
+ private String method = "GET";
+ private String content;
+ private String type;
+ private int major = 1;
+ private int minor = 1;
+
+ public MockRequest() {
+ this.header = new RequestConsumer();
+ this.message = new MessageHeader();
+ this.channel = new MockChannel(null);
+ }
+
+ public void setValue(String name, String value) {
+ message.setValue(name, value);
+ }
+
+ public void add(String name, String value) {
+ message.addValue(name, value);
+ }
+
+ public boolean isSecure(){
+ return false;
+ }
+
+ public String getTarget() {
+ return target;
+ }
+
+ public void setContentType(String value) {
+ type = value;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ public Path getPath() {
+ return new AddressParser(target).getPath();
+ }
+
+ public Query getQuery() {
+ return new AddressParser(target).getQuery();
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public int getMajor() {
+ return major;
+ }
+
+ public void setMajor(int major) {
+ this.major = major;
+ }
+
+ public int getMinor() {
+ return minor;
+ }
+
+ public void setMinor(int minor) {
+ this.minor = minor;
+ }
+
+ public Certificate getClientCertificate() {
+ return null;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public InputStream getInputStream() {
+ return null;
+ }
+
+ public Part getPart(String name) {
+ return null;
+ }
+
+ public List<Part> getParts() {
+ return Collections.emptyList();
+ }
+
+ public int size() {
+ return 0;
+ }
+
+ public Cookie getCookie(String name) {
+ return null;
+ }
+
+ public String getParameter(String name) {
+ return null;
+ }
+
+ public Map getAttributes() {
+ return null;
+ }
+
+
+ public ContentType getContentType() {
+ return new ContentTypeParser(type);
+ }
+
+ public long getContentLength() {
+ String value = getValue("Content-Length");
+
+ if(value != null) {
+ return new Long(value);
+ }
+ return -1;
+ }
+
+ public String getTransferEncoding() {
+ List<String> list = getValues("Transfer-Encoding");
+
+ if(list.size() > 0) {
+ return list.get(0);
+ }
+ return null;
+ }
+
+ public ContentDisposition getDisposition() {
+ String value = getValue("Content-Disposition");
+
+ if(value == null) {
+ return null;
+ }
+ return new ContentDispositionParser(value);
+ }
+
+ public List<String> getValues(String name) {
+ return message.getValues(name);
+ }
+
+ public String getValue(String name) {
+ return message.getValue(name);
+ }
+
+ public Object getAttribute(Object key) {
+ return null;
+ }
+
+ public boolean isKeepAlive() {
+ return true;
+ }
+
+ public InetSocketAddress getClientAddress() {
+ return null;
+ }
+
+ public ReadableByteChannel getByteChannel() throws IOException {
+ return null;
+ }
+
+ public long getRequestTime() {
+ return 0;
+ }
+
+ public Channel getChannel() {
+ return channel;
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MockResponse.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockResponse.java
new file mode 100644
index 00000000..43c0b869
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockResponse.java
@@ -0,0 +1,95 @@
+package org.simpleframework.http.core;
+
+import static org.simpleframework.http.Protocol.CLOSE;
+import static org.simpleframework.http.Protocol.CONNECTION;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.channels.WritableByteChannel;
+import java.util.Map;
+
+import org.simpleframework.http.Response;
+import org.simpleframework.http.core.ResponseMessage;
+
+public class MockResponse extends ResponseMessage implements Response {
+
+ private boolean committed;
+
+ public MockResponse() {
+ super();
+ }
+
+ public OutputStream getOutputStream() {
+ return System.out;
+ }
+
+ public boolean isKeepAlive() {
+ String value = getValue(CONNECTION);
+
+ if(value != null) {
+ return value.equalsIgnoreCase(CLOSE);
+ }
+ return true;
+ }
+
+ public boolean isCommitted() {
+ return committed;
+ }
+
+ public void commit() {
+ committed = true;
+ }
+
+ public void reset() {
+ return;
+ }
+
+ public void close() {
+ return;
+ }
+
+ public Object getAttribute(String name) {
+ return null;
+ }
+
+ public Map getAttributes() {
+ return null;
+ }
+
+ public OutputStream getOutputStream(int size) throws IOException {
+ return null;
+ }
+
+ public PrintStream getPrintStream() throws IOException {
+ return null;
+ }
+
+ public PrintStream getPrintStream(int size) throws IOException {
+ return null;
+ }
+
+ public void setContentLength(long length) {
+ setValue("Content-Length", String.valueOf(length));
+ }
+
+ public WritableByteChannel getByteChannel() throws IOException {
+ return null;
+ }
+
+ public WritableByteChannel getByteChannel(int size) throws IOException {
+ return null;
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public long getResponseTime() {
+ return 0;
+ }
+
+ public void setContentType(String type) {
+ setValue("Content-Type", type);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MockSender.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockSender.java
new file mode 100644
index 00000000..eb209301
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockSender.java
@@ -0,0 +1,75 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.simpleframework.common.buffer.ArrayBuffer;
+import org.simpleframework.common.buffer.Buffer;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.ByteWriter;
+
+public class MockSender implements ByteWriter {
+
+ private Buffer buffer;
+
+ public MockSender() {
+ this(1024);
+ }
+
+ public MockSender(int size) {
+ this.buffer = new ArrayBuffer(size);
+ }
+
+ public Buffer getBuffer() {
+ return buffer;
+ }
+
+ public ByteCursor getCursor() throws IOException {
+ return new StreamCursor(buffer.encode("UTF-8"));
+ }
+
+ public void write(byte[] array) throws IOException {
+ buffer.append(array);
+ }
+
+ public void write(byte[] array, int off, int len) throws IOException {
+ buffer.append(array, off, len);
+ }
+
+ public void flush() throws IOException {
+ return;
+ }
+
+ public void close() throws IOException {
+ return;
+ }
+
+ public String toString() {
+ return buffer.toString();
+ }
+
+ public boolean isOpen() throws Exception {
+ return true;
+ }
+
+ public void write(ByteBuffer source) throws IOException {
+ int mark = source.position();
+ int limit = source.limit();
+
+ byte[] array = new byte[limit - mark];
+ source.get(array, 0, array.length);
+ buffer.append(array);
+ }
+
+ public void write(ByteBuffer source, int off, int len) throws IOException {
+ int mark = source.position();
+ int limit = source.limit();
+
+ if(limit - mark < len) {
+ len = limit - mark;
+ }
+ byte[] array = new byte[len];
+ source.get(array, 0, len);
+ buffer.append(array);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/MockSocket.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockSocket.java
new file mode 100644
index 00000000..5ac7a14f
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/MockSocket.java
@@ -0,0 +1,42 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.SocketException;
+
+public class MockSocket extends Socket {
+
+ private Socket socket;
+
+ private OutputStream out;
+
+ public MockSocket(Socket socket) {
+ this(socket, System.err);
+ }
+
+ public MockSocket(Socket socket, OutputStream out){
+ this.socket = socket;
+ this.out = out;
+ }
+
+ @Override
+ public void setSoTimeout(int delay) throws SocketException {
+ socket.setSoTimeout(delay);
+ }
+
+ @Override
+ public int getSoTimeout() throws SocketException {
+ return socket.getSoTimeout();
+ }
+
+
+ public InputStream getInputStream() throws IOException {
+ return socket.getInputStream();
+ }
+
+ public OutputStream getOutputStream() {
+ return out;
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/PayloadTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/PayloadTest.java
new file mode 100644
index 00000000..6b23fab6
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/PayloadTest.java
@@ -0,0 +1,97 @@
+package org.simpleframework.http.core;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.Part;
+import org.simpleframework.http.message.Header;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteCursor;
+
+public class PayloadTest extends TestCase {
+
+ private static final String PAYLOAD =
+ "POST /index.html HTTP/1.0\r\n"+
+ "Content-Type: multipart/form-data; boundary=AaB03x\r\n"+
+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+
+ " \t\t image/png;\t\r\n\t"+
+ " q=1.0,*;q=0.1\r\n"+
+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+
+ "Host: some.host.com \r\n"+
+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+
+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+
+ "\r\n" +
+ "--AaB03x\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file1.txt\r\n"+
+ "--AaB03x\r\n"+
+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file2.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file3.txt ...\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file3.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file4.txt ...\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file4.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file4.txt ...\r\n"+
+ "--BbC04y--\r\n"+
+ "--AaB03x--\r\n";
+
+
+ public void testPayload() throws Exception {
+ for(int i = 1; i < 4096; i++) {
+ testPayload(i);
+ }
+ }
+
+ public void testPayload(int dribble) throws Exception {
+ ByteCursor cursor = new DribbleCursor(new StreamCursor(PAYLOAD), 10);
+ Channel channel = new MockChannel(cursor);
+ MockController selector = new MockController();
+ Collector body = new RequestCollector(new ArrayAllocator(), channel);
+ long time = System.currentTimeMillis();
+
+ while(!selector.isReady()) {
+ body.collect(selector);
+ }
+ System.err.println("Time taken to parse payload "+(System.currentTimeMillis() - time)+" ms");
+
+ Header header = body.getHeader();
+ List<Part> list = body.getBody().getParts();
+
+ assertEquals(header.getTarget(), "/index.html");
+ assertEquals(header.getMethod(), "POST");
+ assertEquals(header.getMajor(), 1);
+ assertEquals(header.getMinor(), 0);
+ assertEquals(header.getContentType().getPrimary(), "multipart");
+ assertEquals(header.getContentType().getSecondary(), "form-data");
+ assertEquals(header.getValue("Host"), "some.host.com");
+ assertEquals(header.getValues("Accept").size(), 4);
+ assertEquals(header.getValues("Accept").get(0), "image/gif");
+ assertEquals(header.getValues("Accept").get(1), "image/png");
+ assertEquals(header.getValues("Accept").get(2), "image/jpeg");
+ assertEquals(header.getValues("Accept").get(3), "*");
+ assertEquals(list.size(), 4);
+ assertEquals(list.get(0).getContentType().getPrimary(), "text");
+ assertEquals(list.get(0).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'");
+ assertEquals(list.get(1).getContentType().getPrimary(), "text");
+ assertEquals(list.get(1).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(1).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file2.txt'");
+ assertEquals(list.get(2).getContentType().getPrimary(), "text");
+ assertEquals(list.get(2).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(2).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file3.txt'");
+ assertEquals(list.get(3).getContentType().getPrimary(), "text");
+ assertEquals(list.get(3).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(3).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file4.txt'");
+ assertEquals(cursor.ready(), -1);
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/ProducerExceptionTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/ProducerExceptionTest.java
new file mode 100644
index 00000000..d81370ed
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/ProducerExceptionTest.java
@@ -0,0 +1,23 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+public class ProducerExceptionTest extends TestCase {
+
+ public void testException() {
+ try {
+ throw new IOException("Error");
+ }catch(Exception main) {
+ try {
+ throw new BodyEncoderException("Wrapper", main);
+ }catch(Exception cause) {
+ cause.printStackTrace();
+
+ assertEquals(cause.getCause(), main);
+ }
+ }
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/QueryBuilderTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/QueryBuilderTest.java
new file mode 100644
index 00000000..92c9d64d
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/QueryBuilderTest.java
@@ -0,0 +1,35 @@
+package org.simpleframework.http.core;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.http.Query;
+import org.simpleframework.http.message.MockBody;
+import org.simpleframework.http.message.MockHeader;
+
+public class QueryBuilderTest extends TestCase{
+
+ public void testBuilder() throws Exception {
+ MockRequest request = new MockRequest();
+
+ request.setContentType("application/x-www-form-urlencoded");
+ request.setContent("a=post_A&c=post_C&e=post_E");
+
+ MockBody body = new MockBody();
+ MockHeader header = new MockHeader("/path?a=query_A&b=query_B&c=query_C&d=query_D");
+ MockEntity entity = new MockEntity(body, header);
+ QueryBuilder builder = new QueryBuilder(request, entity);
+
+ Query form = builder.build();
+
+ assertEquals(form.getAll("a").size(), 2);
+ assertEquals(form.getAll("b").size(), 1);
+ assertEquals(form.getAll("c").size(), 2);
+ assertEquals(form.getAll("e").size(), 1);
+
+ assertEquals(form.get("a"), "query_A");
+ assertEquals(form.get("b"), "query_B");
+ assertEquals(form.get("c"), "query_C");
+ assertEquals(form.get("e"), "post_E");
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/ReactorProcessorTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/ReactorProcessorTest.java
new file mode 100644
index 00000000..9b0bdcde
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/ReactorProcessorTest.java
@@ -0,0 +1,247 @@
+package org.simpleframework.http.core;
+
+import java.nio.channels.SocketChannel;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.MockTrace;
+import org.simpleframework.http.Part;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.core.ReactorTest.TestChannel;
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.ByteWriter;
+import org.simpleframework.transport.trace.Trace;
+
+public class ReactorProcessorTest extends TestCase implements Container {
+
+ private static final int ITERATIONS = 20000;
+
+ private static final String MINIMAL =
+ "HEAD /MINIMAL/%s HTTP/1.0\r\n" +
+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+
+ "Host: some.host.com \r\n"+
+ "\r\n";
+
+ private static final String SIMPLE =
+ "GET /SIMPLE/%s HTTP/1.0\r\n" +
+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+
+ " \t\t image/png;\t\r\n\t"+
+ " q=1.0,*;q=0.1\r\n"+
+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+
+ "Host: some.host.com \r\n"+
+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+
+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+
+ "\r\n";
+
+ private static final String UPLOAD =
+ "POST /UPLOAD/%s HTTP/1.0\r\n" +
+ "Content-Type: multipart/form-data; boundary=AaB03x\r\n"+
+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+
+ " \t\t image/png;\t\r\n\t"+
+ " q=1.0,*;q=0.1\r\n"+
+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+
+ "Host: some.host.com \r\n"+
+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+
+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+
+ "\r\n" +
+ "--AaB03x\r\n"+
+ "Content-Disposition: file; name=\"pics\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file1.txt\r\n"+
+ "--AaB03x\r\n"+
+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: file; name=\"pics\"; filename=\"file2.txt\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file3.txt ...\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: file; name=\"pics\"; filename=\"file3.txt\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file4.txt ...\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: file; name=\"pics\"; filename=\"file4.txt\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file4.txt ...\r\n"+
+ "--BbC04y--\r\n"+
+ "--AaB03x--\r\n";
+
+ private static class StopWatch {
+
+ private long duration;
+
+ private long start;
+
+ public StopWatch() {
+ this.start = System.currentTimeMillis();
+ }
+
+ public long time() {
+ return duration;
+ }
+
+ public void stop() {
+ duration = System.currentTimeMillis() - start;
+ }
+ }
+
+ public static class MockChannel implements Channel {
+
+ private ByteCursor cursor;
+
+ public MockChannel(StreamCursor cursor, int dribble) {
+ this.cursor = new DribbleCursor(cursor, dribble);
+ }
+ public boolean isSecure() {
+ return false;
+ }
+
+ public Trace getTrace() {
+ return new MockTrace();
+ }
+
+ public Certificate getCertificate() {
+ return null;
+ }
+
+ public ByteCursor getCursor() {
+ return cursor;
+ }
+
+ public ByteWriter getWriter() {
+ return null;
+ }
+
+ public Map getAttributes() {
+ return null;
+ }
+
+ public void close() {}
+
+ public SocketChannel getSocket() {
+ return null;
+ }
+ }
+
+ private ConcurrentHashMap<String, StopWatch> timers = new ConcurrentHashMap<String, StopWatch>();
+
+ private LinkedBlockingQueue<StopWatch> finished = new LinkedBlockingQueue<StopWatch>();
+
+ public void testMinimal() throws Exception {
+ Controller handler = new ContainerController(this, new ArrayAllocator(), 10, 2);
+
+ testRequest(handler, "/MINIMAL/%s", MINIMAL, "MINIMAL");
+ testRequest(handler, "/SIMPLE/%s", SIMPLE, "SIMPLE");
+ testRequest(handler, "/UPLOAD/%s", UPLOAD, "UPLOAD");
+ }
+
+ public void testRequest(Controller handler, String target, String payload, String name) throws Exception {
+ long start = System.currentTimeMillis();
+
+ for(int i = 0; i < ITERATIONS; i++) {
+ String request = String.format(payload, i);
+ StopWatch stopWatch = new StopWatch();
+
+ timers.put(String.format(target, i), stopWatch);
+ testHandler(handler, request, 2048);
+ }
+ double sum = 0;
+
+ for(int i = 0; i < ITERATIONS; i++) {
+ StopWatch stopWatch = finished.take();
+ sum += stopWatch.time();
+ }
+ double total = (System.currentTimeMillis() - start);
+ double count = ITERATIONS;
+
+ System.err.println(String.format("%s total=[%s] for=[%s] average=[%s] time-per-request=[%s] request-per-millisecond=[%s] request-per-second=[%s]",
+ name, total, count, sum / count, total / count, count / total + 1, count / (total / 1000)));
+ }
+
+ public void testHandler(Controller handler, String payload, int dribble) throws Exception {
+ StreamCursor cursor = new StreamCursor(payload);
+ Channel channel = new TestChannel(cursor, dribble);
+
+ handler.start(channel);
+ }
+
+
+ public void handle(Request request, Response response) {
+ try {
+ process(request, response);
+ }catch(Exception e) {
+ e.printStackTrace();
+ assertTrue(false);
+ }
+ }
+
+ public void process(Request request, Response response) throws Exception {
+ List<Part> list = request.getParts();
+ String method = request.getMethod();
+
+ if(method.equals("HEAD")) {
+ assertEquals(request.getMajor(), 1);
+ assertEquals(request.getMinor(), 0);
+ assertEquals(request.getValue("Host"), "some.host.com");
+ } else if(method.equals("GET")) {
+ assertEquals(request.getMajor(), 1);
+ assertEquals(request.getMinor(), 0);
+ assertEquals(request.getValue("Host"), "some.host.com");
+ assertEquals(request.getValues("Accept").size(), 4);
+ assertEquals(request.getValues("Accept").get(0), "image/gif");
+ assertEquals(request.getValues("Accept").get(1), "image/png");
+ assertEquals(request.getValues("Accept").get(2), "image/jpeg");
+ assertEquals(request.getValues("Accept").get(3), "*");
+ } else {
+ assertEquals(request.getMajor(), 1);
+ assertEquals(request.getMinor(), 0);
+ assertEquals(request.getContentType().getPrimary(), "multipart");
+ assertEquals(request.getContentType().getSecondary(), "form-data");
+ assertEquals(request.getValue("Host"), "some.host.com");
+ assertEquals(request.getValues("Accept").size(), 4);
+ assertEquals(request.getValues("Accept").get(0), "image/gif");
+ assertEquals(request.getValues("Accept").get(1), "image/png");
+ assertEquals(request.getValues("Accept").get(2), "image/jpeg");
+ assertEquals(request.getValues("Accept").get(3), "*");
+ assertEquals(list.size(), 4);
+ assertEquals(list.get(0).getContentType().getPrimary(), "text");
+ assertEquals(list.get(0).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(0).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"");
+ assertEquals(list.get(0).getName(), "pics");
+ assertEquals(list.get(0).getFileName(), "file1.txt");
+ assertEquals(list.get(0).isFile(), true);
+ assertEquals(list.get(1).getContentType().getPrimary(), "text");
+ assertEquals(list.get(1).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(1).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file2.txt\"");
+ assertEquals(list.get(1).getContentType().getPrimary(), "text");
+ assertEquals(list.get(1).getName(), "pics");
+ assertEquals(list.get(1).getFileName(), "file2.txt");
+ assertEquals(list.get(1).isFile(), true);
+ assertEquals(list.get(2).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(2).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file3.txt\"");
+ assertEquals(list.get(2).getName(), "pics");
+ assertEquals(list.get(2).getFileName(), "file3.txt");
+ assertEquals(list.get(2).isFile(), true);
+ assertEquals(list.get(3).getContentType().getPrimary(), "text");
+ assertEquals(list.get(3).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(3).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file4.txt\"");
+ assertEquals(list.get(3).getName(), "pics");
+ assertEquals(list.get(3).getFileName(), "file4.txt");
+ assertEquals(list.get(3).isFile(), true);
+ }
+ StopWatch stopWatch = timers.get(request.getTarget());
+ stopWatch.stop();
+ finished.offer(stopWatch);
+ }
+
+ public static void main(String[] list) throws Exception {
+ new ReactorProcessorTest().testMinimal();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/ReactorTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/ReactorTest.java
new file mode 100644
index 00000000..b0aae802
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/ReactorTest.java
@@ -0,0 +1,178 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.nio.channels.SocketChannel;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.common.lease.Lease;
+import org.simpleframework.http.MockTrace;
+import org.simpleframework.http.Part;
+import org.simpleframework.http.message.Body;
+import org.simpleframework.http.message.Entity;
+import org.simpleframework.http.message.Header;
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.ByteWriter;
+import org.simpleframework.transport.trace.Trace;
+
+public class ReactorTest extends TestCase implements Controller {
+
+ private static final String SOURCE =
+ "POST /index.html HTTP/1.0\r\n"+
+ "Content-Type: multipart/form-data; boundary=AaB03x\r\n"+
+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+
+ " \t\t image/png;\t\r\n\t"+
+ " q=1.0,*;q=0.1\r\n"+
+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+
+ "Host: some.host.com \r\n"+
+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+
+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+
+ "\r\n" +
+ "--AaB03x\r\n"+
+ "Content-Disposition: file; name=\"pics\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file1.txt\r\n"+
+ "--AaB03x\r\n"+
+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: file; name=\"pics\"; filename=\"file2.txt\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file3.txt ...\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: file; name=\"pics\"; filename=\"file3.txt\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file4.txt ...\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: file; name=\"pics\"; filename=\"file4.txt\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file4.txt ...\r\n"+
+ "--BbC04y--\r\n"+
+ "--AaB03x--\r\n";
+
+ public static class TestChannel implements Channel {
+
+ private ByteCursor cursor;
+
+ public TestChannel(StreamCursor cursor, int dribble) {
+ this.cursor = new DribbleCursor(cursor, dribble);
+ }
+
+ public boolean isSecure() {
+ return false;
+ }
+
+ public Trace getTrace() {
+ return new MockTrace();
+ }
+
+ public Certificate getCertificate() {
+ return null;
+ }
+
+ public Lease getLease() {
+ return null;
+ }
+
+ public ByteCursor getCursor() {
+ return cursor;
+ }
+
+ public ByteWriter getWriter() {
+ return null;
+ }
+
+ public Map getAttributes() {
+ return null;
+ }
+
+ public void close() {}
+
+ public SocketChannel getSocket() {
+ return null;
+ }
+ }
+
+ public void testHandler() throws Exception {
+ testHandler(1024);
+
+ for(int i = 10; i < 2048; i++) {
+ testHandler(i);
+ }
+ }
+
+ public void testHandler(int dribble) throws Exception {
+ StreamCursor cursor = new StreamCursor(SOURCE);
+ Channel channel = new TestChannel(cursor, dribble);
+
+ start(channel);
+
+ assertEquals(cursor.ready(), -1);
+ }
+
+ public void start(Channel channel) throws IOException {
+ start(new RequestCollector(new ArrayAllocator(), channel));
+ }
+
+ public void start(Collector collector) throws IOException {
+ collector.collect(this);
+ }
+
+ public void select(Collector collector) throws IOException {
+ collector.collect(this);
+ }
+
+ public void ready(Collector collector) throws IOException {
+ Entity entity = collector;
+ Channel channel = entity.getChannel();
+ ByteCursor cursor = channel.getCursor();
+ Header header = entity.getHeader();
+ Body body = entity.getBody();
+ List<Part> list = body.getParts();
+
+ assertEquals(header.getTarget(), "/index.html");
+ assertEquals(header.getMethod(), "POST");
+ assertEquals(header.getMajor(), 1);
+ assertEquals(header.getMinor(), 0);
+ assertEquals(header.getContentType().getPrimary(), "multipart");
+ assertEquals(header.getContentType().getSecondary(), "form-data");
+ assertEquals(header.getValue("Host"), "some.host.com");
+ assertEquals(header.getValues("Accept").size(), 4);
+ assertEquals(header.getValues("Accept").get(0), "image/gif");
+ assertEquals(header.getValues("Accept").get(1), "image/png");
+ assertEquals(header.getValues("Accept").get(2), "image/jpeg");
+ assertEquals(header.getValues("Accept").get(3), "*");
+ assertEquals(list.size(), 4);
+ assertEquals(list.get(0).getContentType().getPrimary(), "text");
+ assertEquals(list.get(0).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(0).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"");
+ assertEquals(list.get(0).getName(), "pics");
+ assertEquals(list.get(0).getFileName(), "file1.txt");
+ assertEquals(list.get(0).isFile(), true);
+ assertEquals(list.get(1).getContentType().getPrimary(), "text");
+ assertEquals(list.get(1).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(1).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file2.txt\"");
+ assertEquals(list.get(1).getContentType().getPrimary(), "text");
+ assertEquals(list.get(1).getName(), "pics");
+ assertEquals(list.get(1).getFileName(), "file2.txt");
+ assertEquals(list.get(1).isFile(), true);
+ assertEquals(list.get(2).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(2).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file3.txt\"");
+ assertEquals(list.get(2).getName(), "pics");
+ assertEquals(list.get(2).getFileName(), "file3.txt");
+ assertEquals(list.get(2).isFile(), true);
+ assertEquals(list.get(3).getContentType().getPrimary(), "text");
+ assertEquals(list.get(3).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(3).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file4.txt\"");
+ assertEquals(list.get(3).getName(), "pics");
+ assertEquals(list.get(3).getFileName(), "file4.txt");
+ assertEquals(list.get(3).isFile(), true);
+ assertEquals(cursor.ready(), -1);
+ }
+
+ public void stop() throws IOException {}
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/RequestConsumerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/RequestConsumerTest.java
new file mode 100644
index 00000000..ae9672f8
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/RequestConsumerTest.java
@@ -0,0 +1,138 @@
+package org.simpleframework.http.core;
+
+import org.simpleframework.http.message.RequestConsumer;
+import org.simpleframework.transport.ByteCursor;
+
+import junit.framework.TestCase;
+
+public class RequestConsumerTest extends TestCase {
+
+ private static final byte[] SOURCE_1 =
+ ("POST /index.html HTTP/1.0\r\n"+
+ "Content-Type: application/x-www-form-urlencoded\r\n"+
+ "Content-Length: 42\r\n"+
+ "Transfer-Encoding: chunked\r\n"+
+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+
+ " \t\t image/png;\t\r\n\t"+
+ " q=1.0,*;q=0.1\r\n"+
+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+
+ "Host: some.host.com \r\n"+
+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+
+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+
+ "\r\n").getBytes();
+
+ private static final byte[] SOURCE_2 =
+ ("GET /tmp/amazon_files/21lP7I1XB5L.jpg HTTP/1.1\r\n"+
+ "Accept-Encoding: gzip, deflate\r\n"+
+ "Connection: keep-alive\r\n"+
+ "Referer: http://localhost:9090/tmp/amazon.htm\r\n"+
+ "Cache-Control: max-age=0\r\n"+
+ "Host: localhost:9090\r\n"+
+ "Accept-Language: en-US\r\n"+
+ "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13\r\n"+
+ "Accept: */*\r\n" +
+ "\r\n").getBytes();
+
+ private static final byte[] SOURCE_3 =
+ ("GET /tmp/amazon_files/in-your-city-blue-large._V256095983_.gif HTTP/1.1Accept-Encoding: gzip, deflate\r\n"+
+ "Connection: keep-alive\r\n"+
+ "Referer: http://localhost:9090/tmp/amazon.htm\r\n"+
+ "Cache-Control: max-age=0\r\n"+
+ "Host: localhost:9090\r\n"+
+ "Accept-Language: en-US\r\n"+
+ "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13\r\n"+
+ "Accept: */*\r\n"+
+ "\r\n").getBytes();
+
+ private static final byte[] SOURCE_4 =
+ ("GET /tmp/amazon_files/narrowtimer_transparent._V47062518_.gif HTTP/1.1\r\n"+
+ "Accept-Encoding: gzip, deflate\r\n"+
+ "Connection: keep-alive\r\n"+
+ "Referer: http://localhost:9090/tmp/amazon.htm\r\n"+
+ "Cache-Control: max-age=0\r\n"+
+ "Host: localhost:9090\r\n"+
+ "Accept-Language: en-US\r\n"+
+ "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13\r\n"+
+ "Accept: */*\r\n"+
+ "\r\n").getBytes();
+
+ public void testPerformance() throws Exception {
+ testPerformance(SOURCE_1, "/index.html");
+ testPerformance(SOURCE_2, "/tmp/amazon_files/21lP7I1XB5L.jpg");
+ testPerformance(SOURCE_3, "/tmp/amazon_files/in-your-city-blue-large._V256095983_.gif");
+ testPerformance(SOURCE_4, "/tmp/amazon_files/narrowtimer_transparent._V47062518_.gif");
+ }
+
+ public void testPerformance(byte[] request, String path) throws Exception {
+ long start = System.currentTimeMillis();
+
+ for(int i = 0; i < 10000; i++) {
+ RequestConsumer header = new RequestConsumer();
+ ByteCursor cursor = new StreamCursor(request);
+
+ while(!header.isFinished()) {
+ header.consume(cursor);
+ }
+
+ assertEquals(cursor.ready(), -1);
+ assertEquals(header.getPath().getPath(), path);
+ }
+ System.err.printf("%s time=%s%n", path, (System.currentTimeMillis() - start));
+ }
+
+ public void testHeader() throws Exception {
+ long start = System.currentTimeMillis();
+
+ for(int i = 0; i < 10000; i++) {
+ RequestConsumer header = new RequestConsumer();
+ ByteCursor cursor = new StreamCursor(SOURCE_1);
+
+ while(!header.isFinished()) {
+ header.consume(cursor);
+ }
+
+ assertEquals(cursor.ready(), -1);
+ assertEquals(header.getTarget(), "/index.html");
+ assertEquals(header.getMethod(), "POST");
+ assertEquals(header.getMajor(), 1);
+ assertEquals(header.getMinor(), 0);
+ assertEquals(header.getValue("Content-Length"), "42");
+ assertEquals(header.getValue("Content-Type"), "application/x-www-form-urlencoded");
+ assertEquals(header.getValue("Host"), "some.host.com");
+ assertEquals(header.getValues("Accept").size(), 4);
+ assertEquals(header.getValues("Accept").get(0), "image/gif");
+ assertEquals(header.getValues("Accept").get(1), "image/png");
+ assertEquals(header.getValues("Accept").get(2), "image/jpeg");
+ assertEquals(header.getValues("Accept").get(3), "*");
+ assertEquals(header.getContentType().getPrimary(), "application");
+ assertEquals(header.getContentType().getSecondary(), "x-www-form-urlencoded");
+ assertEquals(header.getTransferEncoding(), "chunked");
+ }
+ System.err.printf("time=%s%n", (System.currentTimeMillis() - start));
+ }
+
+ public void testDribble() throws Exception {
+ RequestConsumer header = new RequestConsumer();
+ ByteCursor cursor = new DribbleCursor(new StreamCursor(SOURCE_1), 1);
+
+ while(!header.isFinished()) {
+ header.consume(cursor);
+ }
+ assertEquals(cursor.ready(), -1);
+ assertEquals(header.getTarget(), "/index.html");
+ assertEquals(header.getMethod(), "POST");
+ assertEquals(header.getMajor(), 1);
+ assertEquals(header.getMinor(), 0);
+ assertEquals(header.getValue("Content-Length"), "42");
+ assertEquals(header.getValue("Content-Type"), "application/x-www-form-urlencoded");
+ assertEquals(header.getValue("Host"), "some.host.com");
+ assertEquals(header.getValues("Accept").size(), 4);
+ assertEquals(header.getValues("Accept").get(0), "image/gif");
+ assertEquals(header.getValues("Accept").get(1), "image/png");
+ assertEquals(header.getValues("Accept").get(2), "image/jpeg");
+ assertEquals(header.getValues("Accept").get(3), "*");
+ assertEquals(header.getContentType().getPrimary(), "application");
+ assertEquals(header.getContentType().getSecondary(), "x-www-form-urlencoded");
+ assertEquals(header.getTransferEncoding(), "chunked");
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/RequestTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/RequestTest.java
new file mode 100644
index 00000000..47cbf35a
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/RequestTest.java
@@ -0,0 +1,144 @@
+package org.simpleframework.http.core;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.Part;
+import org.simpleframework.http.Request;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteCursor;
+
+public class RequestTest extends TestCase {
+
+ private static final String HEADER =
+ "POST /index.html?a=b&c=d&e=f&g=h&a=1 HTTP/1.0\r\n"+
+ "Content-Type: multipart/form-data; boundary=AaB03x\r\n"+
+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+
+ " \t\t image/png;\t\r\n\t"+
+ " q=1.0,*;q=0.1\r\n"+
+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+
+ "Host: some.host.com \r\n"+
+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+
+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+
+ "\r\n";
+
+ private static final String BODY =
+ "--AaB03x\r\n"+
+ "Content-Disposition: file; name=\"file1\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file1.txt\r\n"+
+ "--AaB03x\r\n"+
+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: file; name=\"file2\"; filename=\"file2.txt\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file2.txt ...\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: file; name=\"file3\"; filename=\"file3.txt\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file3.txt ...\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: file; name=\"file4\"; filename=\"file4.txt\"\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file4.txt ...\r\n"+
+ "--BbC04y--\r\n"+
+ "--AaB03x--\r\n";
+
+ private static final byte[] PAYLOAD = (HEADER + BODY).getBytes();
+
+ public void testPayload() throws Exception {
+ long start = System.currentTimeMillis();
+
+ for(int i = 1; i < 8192; i++) {
+ testPayload(i);
+ }
+ System.err.printf("time=%s%n",(System.currentTimeMillis() - start));
+ }
+
+ public void testPerformance() throws Exception {
+ long start = System.currentTimeMillis();
+
+ for(int i = 1; i < 10000; i++) {
+ testPayload(8192);
+ }
+ System.err.printf("time=%s%n",(System.currentTimeMillis() - start));
+ }
+
+ public void testPayload(int dribble) throws Exception {
+ System.out.println("Testing dribbling cursor of "+dribble+" ...");
+ ByteCursor cursor = new StreamCursor(PAYLOAD);
+
+ if(dribble < PAYLOAD.length) {
+ cursor = new DribbleCursor(cursor, dribble);
+ }
+ Channel channel = new MockChannel(cursor);
+ MockController selector = new MockController();
+ Collector body = new RequestCollector(new ArrayAllocator(), channel);
+
+ while(!selector.isReady()) {
+ body.collect(selector);
+ }
+ Request request = new RequestEntity(null, body);
+ List<Part> list = request.getParts();
+
+ assertEquals(request.getParameter("a"), "b");
+ assertEquals(request.getParameter("c"), "d");
+ assertEquals(request.getParameter("e"), "f");
+ assertEquals(request.getParameter("g"), "h");
+ assertEquals(request.getTarget(), "/index.html?a=b&c=d&e=f&g=h&a=1");
+ assertEquals(request.getMethod(), "POST");
+ assertEquals(request.getMajor(), 1);
+ assertEquals(request.getMinor(), 0);
+ assertEquals(request.getContentType().getPrimary(), "multipart");
+ assertEquals(request.getContentType().getSecondary(), "form-data");
+ assertEquals(request.getValue("Host"), "some.host.com");
+ assertEquals(request.getValues("Accept").size(), 4);
+ assertEquals(request.getValues("Accept").get(0), "image/gif");
+ assertEquals(request.getValues("Accept").get(1), "image/png");
+ assertEquals(request.getValues("Accept").get(2), "image/jpeg");
+ assertEquals(request.getValues("Accept").get(3), "*");
+ assertEquals(request.getCookie("UID").getValue(), "1234-5678");
+ assertEquals(request.getCookie("UID").getPath(), "/");
+ assertEquals(request.getCookie("UID").getDomain(), ".host.com");
+ assertEquals(request.getCookie("NAME").getValue(), "Niall Gallagher");
+ assertEquals(request.getCookie("NAME").getPath(), "/");
+ assertEquals(request.getCookie("NAME").getDomain(), null);
+ assertEquals(list.size(), 4);
+ assertEquals(list.get(0).getContentType().getPrimary(), "text");
+ assertEquals(list.get(0).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(0).getHeader("Content-Disposition"), "file; name=\"file1\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"");
+ assertEquals(list.get(0).getName(), "file1");
+ assertEquals(list.get(0).getFileName(), "file1.txt");
+ assertEquals(list.get(0).isFile(), true);
+ assertEquals(list.get(0).getContent(), "example contents of file1.txt");
+ assertEquals(request.getPart("file1").getContent(), "example contents of file1.txt");
+ assertEquals(list.get(1).getContentType().getPrimary(), "text");
+ assertEquals(list.get(1).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(1).getHeader("Content-Disposition"), "file; name=\"file2\"; filename=\"file2.txt\"");
+ assertEquals(list.get(1).getContentType().getPrimary(), "text");
+ assertEquals(list.get(1).getName(), "file2");
+ assertEquals(list.get(1).getFileName(), "file2.txt");
+ assertEquals(list.get(1).isFile(), true);
+ assertEquals(list.get(1).getContent(), "example contents of file2.txt ...");
+ assertEquals(request.getPart("file2").getContent(), "example contents of file2.txt ...");
+ assertEquals(list.get(2).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(2).getHeader("Content-Disposition"), "file; name=\"file3\"; filename=\"file3.txt\"");
+ assertEquals(list.get(2).getName(), "file3");
+ assertEquals(list.get(2).getFileName(), "file3.txt");
+ assertEquals(list.get(2).isFile(), true);
+ assertEquals(list.get(2).getContent(), "example contents of file3.txt ...");
+ assertEquals(request.getPart("file3").getContent(), "example contents of file3.txt ...");
+ assertEquals(list.get(3).getContentType().getPrimary(), "text");
+ assertEquals(list.get(3).getContentType().getSecondary(), "plain");
+ assertEquals(list.get(3).getHeader("Content-Disposition"), "file; name=\"file4\"; filename=\"file4.txt\"");
+ assertEquals(list.get(3).getName(), "file4");
+ assertEquals(list.get(3).getFileName(), "file4.txt");
+ assertEquals(list.get(3).isFile(), true);
+ assertEquals(list.get(3).getContent(), "example contents of file4.txt ...");
+ assertEquals(request.getPart("file4").getContent(), "example contents of file4.txt ...");
+ assertEquals(cursor.ready(), -1);
+ assertEquals(request.getContent(), BODY);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/Result.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/Result.java
new file mode 100644
index 00000000..c48b2489
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/Result.java
@@ -0,0 +1,37 @@
+package org.simpleframework.http.core;
+
+import java.util.List;
+import java.util.Map;
+
+import org.simpleframework.http.Cookie;
+
+class Result {
+
+ private List<Cookie> cookies;
+ private String response;
+ private byte[] body;
+ private Map map;
+
+ public Result(String response, byte[] body, Map map, List<Cookie> cookies) {
+ this.cookies = cookies;
+ this.response = response;
+ this.body = body;
+ this.map = map;
+ }
+
+ public List<Cookie> getCookies() {
+ return cookies;
+ }
+
+ public byte[] getBody() {
+ return body;
+ }
+
+ public String getResponse() throws Exception {
+ return response;
+ }
+
+ public Map getMap() {
+ return map;
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/StopTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/StopTest.java
new file mode 100644
index 00000000..67751b89
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/StopTest.java
@@ -0,0 +1,176 @@
+package org.simpleframework.http.core;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadMXBean;
+import java.net.InetSocketAddress;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.thread.ConcurrentExecutor;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.transport.connect.Connection;
+import org.simpleframework.transport.connect.SocketConnection;
+
+public class StopTest extends TestCase {
+
+ private static final int ITERATIONS = 20;
+
+ public void testStop() throws Exception {
+ ThreadDumper dumper = new ThreadDumper();
+
+ dumper.start();
+ dumper.waitUntilStarted();
+
+ ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
+ int initialThreads = threadBean.getThreadCount();
+
+ for(int i = 0; i < ITERATIONS; i++) {
+ try {
+ ServerCriteria criteria = createServer();
+ InetSocketAddress address = criteria.getAddress();
+ Connection connection = criteria.getConnection();
+ Client client = createClient(address, String.format("[%s of %s]", i, ITERATIONS));
+
+ Thread.sleep(2000); // allow some requests to execute
+ connection.close();
+ Thread.sleep(100); // ensure client keeps executing
+ client.close();
+ Thread.sleep(1000); // wait for threads to terminate
+ }catch(Exception e) {
+ e.printStackTrace();
+ }
+ //assertEquals(initialThreads, threadBean.getThreadCount());
+ }
+ dumper.kill();
+ }
+
+ public static Client createClient(InetSocketAddress address, String tag) throws Exception {
+ ConcurrentExecutor executor = new ConcurrentExecutor(Runnable.class, 20);
+ int port = address.getPort();
+ Client client = new Client(executor, port, tag);
+
+ client.start();
+ return client;
+ }
+
+ public static ServerCriteria createServer() throws Exception {
+ Container container = new Container() {
+ public void handle(Request request, Response response) {
+ try {
+ PrintStream out = response.getPrintStream();
+ response.setValue("Content-Type", "text/plain");
+ response.setValue("Connection", "close");
+
+ out.print("TEST " + new Date());
+ response.close();
+ }catch(Exception e) {
+ e.printStackTrace();
+ try {
+ response.close();
+ }catch(Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ };
+ ContainerSocketProcessor server = new ContainerSocketProcessor(container);
+ Connection connection = new SocketConnection(server);
+ InetSocketAddress address = (InetSocketAddress)connection.connect(null); // ephemeral port
+
+ return new ServerCriteria(connection, address);
+ }
+
+ private static class Client extends Thread implements Closeable {
+
+ private ConcurrentExecutor executor;
+ private RequestTask task;
+ private volatile boolean dead;
+
+ public Client(ConcurrentExecutor executor, int port, String tag) {
+ this.task = new RequestTask(this, port, tag);
+ this.executor = executor;
+ }
+
+ public boolean isDead() {
+ return dead;
+ }
+
+ public void run() {
+ try {
+ while(!dead) {
+ executor.execute(task);
+ Thread.sleep(100);
+ }
+ }catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void close() {
+ dead = true;
+ executor.stop();
+ }
+ }
+
+ private static class RequestTask implements Runnable {
+
+ private Client client;
+ private String tag;
+ private int port;
+
+ public RequestTask(Client client, int port, String tag) {
+ this.client = client;
+ this.port = port;
+ this.tag = tag;
+ }
+
+ public void run() {
+ try {
+ if(!client.isDead()) {
+ URL target = new URL("http://localhost:"+port+"/");
+ URLConnection connection = target.openConnection();
+
+ // set a timeout
+ connection.setConnectTimeout(10000);
+ connection.setReadTimeout(10000);
+
+ InputStream stream = connection.getInputStream();
+ StringBuilder builder = new StringBuilder();
+ int octet = 0;
+
+ while((octet = stream.read()) != -1) {
+ builder.append((char)octet);
+ }
+ stream.close();
+ System.out.println(tag + " " + Thread.currentThread() + ": " + builder);
+ }
+ }catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static class ServerCriteria {
+
+ private Connection connection;
+ private InetSocketAddress address;
+
+ public ServerCriteria(Connection connection, InetSocketAddress address){
+ this.connection = connection;
+ this.address = address;
+ }
+ public Connection getConnection() {
+ return connection;
+ }
+ public InetSocketAddress getAddress() {
+ return address;
+ }
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/StreamCursor.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/StreamCursor.java
new file mode 100644
index 00000000..d6f6a099
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/StreamCursor.java
@@ -0,0 +1,74 @@
+package org.simpleframework.http.core;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.simpleframework.http.StreamTransport;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.Transport;
+import org.simpleframework.transport.TransportCursor;
+
+public class StreamCursor implements ByteCursor {
+
+ private TransportCursor cursor;
+ private Transport transport;
+ private byte[] swap;
+
+ public StreamCursor(String source) throws IOException {
+ this(source.getBytes("UTF-8"));
+ }
+
+ public StreamCursor(byte[] data) throws IOException {
+ this(new ByteArrayInputStream(data));
+ }
+
+ public StreamCursor(InputStream source) throws IOException {
+ this.transport = new StreamTransport(source, new OutputStream() {
+ public void write(int octet){}
+ });
+ this.cursor = new TransportCursor(transport);
+ this.swap = new byte[1];
+ }
+
+ // TODO investigate this
+ public boolean isOpen() throws IOException {
+ return true;
+ }
+
+ public boolean isReady() throws IOException {
+ return cursor.isReady();
+ }
+
+ public int ready() throws IOException {
+ return cursor.ready();
+ }
+
+ public int read() throws IOException {
+ if(read(swap) > 0) {
+ return swap[0] & 0xff;
+ }
+ return 0;
+ }
+
+ public int read(byte[] data) throws IOException {
+ return read(data, 0, data.length);
+ }
+
+ public int read(byte[] data, int off, int len) throws IOException {
+ return cursor.read(data, off, len);
+ }
+
+ public int reset(int len) throws IOException {
+ return cursor.reset(len);
+ }
+
+ public void push(byte[] data) throws IOException {
+ push(data, 0, data.length);
+ }
+
+ public void push(byte[] data, int off, int len) throws IOException {
+ cursor.push(data, off, len);
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/ThreadDumper.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/ThreadDumper.java
new file mode 100644
index 00000000..85960eda
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/ThreadDumper.java
@@ -0,0 +1,183 @@
+package org.simpleframework.http.core;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+
+public class ThreadDumper extends Thread {
+
+ private static String INDENT = " ";
+ private CountDownLatch latch;
+ private volatile boolean dead;
+ private int wait;
+
+ public ThreadDumper() {
+ this(10000);
+ }
+
+ public ThreadDumper(int wait) {
+ this.latch = new CountDownLatch(1);
+ this.wait = wait;
+ }
+
+ public void waitUntilStarted() throws InterruptedException{
+ latch.await();
+ }
+
+ public void kill(){
+ try {
+ Thread.sleep(1000);
+ dead = true;
+ dumpThreadInfo();
+ }catch(Exception e){
+ e.printStackTrace();
+ }
+ }
+ public void run() {
+ while(!dead) {
+ try{
+ latch.countDown();
+ dumpThreadInfo();
+ findDeadlock();
+ Thread.sleep(wait);
+ }catch(Exception e){
+ e.printStackTrace();
+ }
+ }
+ }
+ public String dumpThreads() {
+ Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
+ return generateDump(stackTraces);
+ }
+
+ public static String dumpCurrentThread() {
+ Thread currentThread = Thread.currentThread();
+ StackTraceElement[] stackTrace = currentThread.getStackTrace();
+ Map<Thread, StackTraceElement[]> stackTraces = Collections.singletonMap(currentThread, stackTrace);
+ return generateDump(stackTraces);
+
+ }
+
+ private static String generateDump(Map<Thread, StackTraceElement[]> stackTraces) {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("<pre>");
+ builder.append("<b>Full Java thread dump</b>");
+ builder.append("\n");
+
+ Set<Thread> threads = stackTraces.keySet();
+
+ for (Thread thread : threads) {
+ StackTraceElement[] stackElements = stackTraces.get(thread);
+
+ generateDescription(thread, builder);
+ generateStackFrames(stackElements, builder);
+ }
+ builder.append("</pre>");
+ return builder.toString();
+ }
+
+ private static void generateStackFrames(StackTraceElement[] stackElements, StringBuilder builder) {
+ for (StackTraceElement stackTraceElement : stackElements) {
+ builder.append(" at ");
+ builder.append(stackTraceElement);
+ builder.append("\n");
+ }
+ }
+
+ private static void generateDescription(Thread thread, StringBuilder builder) {
+ Thread.State threadState = thread.getState();
+ String threadName = thread.getName();
+ long threadId = thread.getId();
+
+ builder.append("\n");
+ builder.append("<b>");
+ builder.append(threadName);
+ builder.append("</b> Id=");
+ builder.append(threadId);
+ builder.append(" in ");
+ builder.append(threadState);
+ builder.append("\n");
+ }
+
+ /**
+ * Prints the thread dump information to System.out.
+ */
+ public static void dumpThreadInfo(){
+ System.out.println(getThreadInfo());
+ }
+
+ public static String getThreadInfo() {
+ ThreadMXBean tmbean = ManagementFactory.getThreadMXBean();
+ long[] tids = tmbean.getAllThreadIds();
+ ThreadInfo[] tinfos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE);
+ StringWriter str = new StringWriter();
+ PrintWriter log = new PrintWriter(str);
+ log.println("Full Java thread dump");
+
+ for (ThreadInfo ti : tinfos) {
+ printThreadInfo(ti, log);
+ }
+ log.flush();
+ return str.toString();
+ }
+
+ private static void printThreadInfo(ThreadInfo ti, PrintWriter log) {
+ if(ti != null) {
+ StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" +
+ " Id=" + ti.getThreadId() +
+ " in " + ti.getThreadState());
+ if (ti.getLockName() != null) {
+ sb.append(" on lock=" + ti.getLockName());
+ }
+ if (ti.isSuspended()) {
+ sb.append(" (suspended)");
+ }
+ if (ti.isInNative()) {
+ sb.append(" (running in native)");
+ }
+ log.println(sb.toString());
+ if (ti.getLockOwnerName() != null) {
+ log.println(INDENT + " owned by " + ti.getLockOwnerName() +
+ " Id=" + ti.getLockOwnerId());
+ }
+ for (StackTraceElement ste : ti.getStackTrace()) {
+ log.println(INDENT + "at " + ste.toString());
+ }
+ log.println();
+ }
+ }
+
+ /**
+ * Checks if any threads are deadlocked. If any, print
+ * the thread dump information.
+ */
+ public static boolean findDeadlock() {
+ ThreadMXBean tmbean = ManagementFactory.getThreadMXBean();
+ long[] tids = tmbean.findMonitorDeadlockedThreads();
+ if (tids == null) {
+ return false;
+ } else {
+ StringWriter str = new StringWriter();
+ PrintWriter log = new PrintWriter(str);
+
+ tids = tmbean.getAllThreadIds();
+ System.out.println("Deadlock found :-");
+ ThreadInfo[] tinfos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE);
+ for (ThreadInfo ti : tinfos) {
+ printThreadInfo(ti, log);
+ }
+ log.flush();
+ System.out.println(str.toString());
+ return true;
+ }
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/Ticket.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/Ticket.java
new file mode 100644
index 00000000..60fcb5c1
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/Ticket.java
@@ -0,0 +1,22 @@
+package org.simpleframework.http.core;
+
+public class Ticket {
+
+ public static final Class KEY = Ticket.class;
+
+ private final String ticket;
+ private final int port;
+ public Ticket(int port) {
+ this.ticket = String.valueOf(port);
+ this.port = port;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getTicket() {
+ return ticket;
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/TicketProcessor.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/TicketProcessor.java
new file mode 100644
index 00000000..4636cc7f
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/TicketProcessor.java
@@ -0,0 +1,28 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.nio.channels.SocketChannel;
+
+import org.simpleframework.transport.SocketProcessor;
+import org.simpleframework.transport.Socket;
+
+class TicketProcessor implements SocketProcessor {
+
+ private SocketProcessor delegate;
+
+ public TicketProcessor(SocketProcessor delegate) {
+ this.delegate = delegate;
+ }
+
+ public void process(Socket pipe) throws IOException {
+ SocketChannel channel = pipe.getChannel();
+ int port = channel.socket().getPort();
+
+ pipe.getAttributes().put(Ticket.KEY,new Ticket(port));
+ delegate.process(pipe);
+ }
+
+ public void stop() throws IOException {
+ delegate.stop();
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/TransferTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/TransferTest.java
new file mode 100644
index 00000000..0d0d73dc
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/TransferTest.java
@@ -0,0 +1,195 @@
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import org.simpleframework.http.core.Conversation;
+import org.simpleframework.http.core.ResponseEncoder;
+
+import junit.framework.TestCase;
+
+public class TransferTest extends TestCase {
+
+ public void testTransferEncoding() throws IOException {
+ MockChannel channel = new MockChannel(null);
+ MockObserver monitor = new MockObserver();
+ MockRequest request = new MockRequest();
+ MockResponse response = new MockResponse();
+ Conversation support = new Conversation(request, response);
+ ResponseEncoder transfer = new ResponseEncoder(monitor, response, support, channel);
+
+ // Start a HTTP/1.1 conversation
+ request.setMajor(1);
+ request.setMinor(1);
+ transfer.start();
+
+ assertEquals(response.getValue("Connection"), "keep-alive");
+ assertEquals(response.getValue("Transfer-Encoding"), "chunked");
+ assertEquals(response.getValue("Content-Length"), null);
+ assertEquals(response.getContentLength(), -1);
+ assertTrue(response.isCommitted());
+
+ channel = new MockChannel(null);
+ monitor = new MockObserver();
+ request = new MockRequest();
+ response = new MockResponse();
+ support = new Conversation(request, response);
+ transfer = new ResponseEncoder(monitor, response, support, channel);
+
+ // Start a HTTP/1.0 conversation
+ request.setMajor(1);
+ request.setMinor(0);
+ transfer.start();
+
+ assertEquals(response.getValue("Connection"), "close");
+ assertEquals(response.getValue("Transfer-Encoding"), null);
+ assertEquals(response.getValue("Content-Length"), null);
+ assertEquals(response.getContentLength(), -1);
+ assertTrue(response.isCommitted());
+ }
+
+ public void testContentLength() throws IOException {
+ MockChannel channel = new MockChannel(null);
+ MockObserver monitor = new MockObserver();
+ MockRequest request = new MockRequest();
+ MockResponse response = new MockResponse();
+ Conversation support = new Conversation(request, response);
+ ResponseEncoder transfer = new ResponseEncoder(monitor, response, support, channel);
+
+ // Start a HTTP/1.1 conversation
+ request.setMajor(1);
+ request.setMinor(1);
+ transfer.start(1024);
+
+ assertEquals(response.getValue("Connection"), "keep-alive");
+ assertEquals(response.getValue("Content-Length"), "1024");
+ assertEquals(response.getValue("Transfer-Encoding"), null);
+ assertEquals(response.getContentLength(), 1024);
+ assertTrue(response.isCommitted());
+
+ channel = new MockChannel(null);
+ monitor = new MockObserver();
+ request = new MockRequest();
+ response = new MockResponse();
+ support = new Conversation(request, response);
+ transfer = new ResponseEncoder(monitor, response, support, channel);
+
+ // Start a HTTP/1.0 conversation
+ request.setMajor(1);
+ request.setMinor(0);
+ transfer.start(1024);
+
+ assertEquals(response.getValue("Connection"), "close");
+ assertEquals(response.getValue("Content-Length"), "1024");
+ assertEquals(response.getValue("Transfer-Encoding"), null);
+ assertEquals(response.getContentLength(), 1024);
+ assertTrue(response.isCommitted());
+
+ channel = new MockChannel(null);
+ monitor = new MockObserver();
+ request = new MockRequest();
+ response = new MockResponse();
+ support = new Conversation(request, response);
+ transfer = new ResponseEncoder(monitor, response, support, channel);
+
+ // Start a HTTP/1.0 conversation
+ request.setMajor(1);
+ request.setMinor(1);
+ response.setValue("Content-Length", "2048");
+ response.setValue("Connection", "close");
+ response.setValue("Transfer-Encoding", "chunked");
+ transfer.start(1024);
+
+ assertEquals(response.getValue("Connection"), "close");
+ assertEquals(response.getValue("Content-Length"), "1024"); // should be 1024
+ assertEquals(response.getValue("Transfer-Encoding"), null);
+ assertEquals(response.getContentLength(), 1024);
+ assertTrue(response.isCommitted());
+ }
+
+ public void testHeadMethodWithConnectionClose() throws IOException {
+ MockChannel channel = new MockChannel(null);
+ MockObserver monitor = new MockObserver();
+ MockRequest request = new MockRequest();
+ MockResponse response = new MockResponse();
+ Conversation support = new Conversation(request, response);
+ ResponseEncoder transfer = new ResponseEncoder(monitor, response, support, channel);
+
+ request.setMajor(1);
+ request.setMinor(0);
+ request.setMethod("HEAD");
+ request.setValue("Connection", "keep-alive");
+ response.setContentLength(1024);
+ response.setValue("Connection", "close");
+
+ transfer.start();
+
+ assertEquals(response.getValue("Connection"), "close");
+ assertEquals(response.getValue("Content-Length"), "1024"); // should be 1024
+ assertEquals(response.getValue("Transfer-Encoding"), null);
+ assertEquals(response.getContentLength(), 1024);
+ }
+
+ public void testHeadMethodWithSomethingWritten() throws IOException {
+ MockChannel channel = new MockChannel(null);
+ MockObserver monitor = new MockObserver();
+ MockRequest request = new MockRequest();
+ MockResponse response = new MockResponse();
+ Conversation support = new Conversation(request, response);
+ ResponseEncoder transfer = new ResponseEncoder(monitor, response, support, channel);
+
+ request.setMajor(1);
+ request.setMinor(1);
+ request.setMethod("HEAD");
+ request.setValue("Connection", "keep-alive");
+ response.setContentLength(1024);
+
+ transfer.start(512);
+
+ assertEquals(response.getValue("Connection"), "keep-alive");
+ assertEquals(response.getValue("Content-Length"), "512"); // should be 512
+ assertEquals(response.getValue("Transfer-Encoding"), null);
+ assertEquals(response.getContentLength(), 512);
+ }
+
+ public void testHeadMethodWithNoContentLength() throws IOException {
+ MockChannel channel = new MockChannel(null);
+ MockObserver monitor = new MockObserver();
+ MockRequest request = new MockRequest();
+ MockResponse response = new MockResponse();
+ Conversation support = new Conversation(request, response);
+ ResponseEncoder transfer = new ResponseEncoder(monitor, response, support, channel);
+
+ request.setMajor(1);
+ request.setMinor(1);
+ request.setMethod("HEAD");
+ request.setValue("Connection", "keep-alive");
+
+ transfer.start();
+
+ assertEquals(response.getValue("Connection"), "keep-alive");
+ assertEquals(response.getValue("Content-Length"), null);
+ assertEquals(response.getValue("Transfer-Encoding"), "chunked");
+ assertEquals(response.getContentLength(), -1);
+ }
+
+ public void testHeadMethodWithNoContentLengthAndSomethingWritten() throws IOException {
+ MockChannel channel = new MockChannel(null);
+ MockObserver monitor = new MockObserver();
+ MockRequest request = new MockRequest();
+ MockResponse response = new MockResponse();
+ Conversation support = new Conversation(request, response);
+ ResponseEncoder transfer = new ResponseEncoder(monitor, response, support, channel);
+
+ request.setMajor(1);
+ request.setMinor(1);
+ request.setMethod("HEAD");
+ request.setValue("Connection", "keep-alive");
+
+ transfer.start(32);
+
+ assertEquals(response.getValue("Connection"), "keep-alive");
+ assertEquals(response.getValue("Content-Length"), "32");
+ assertEquals(response.getValue("Transfer-Encoding"), null);
+ assertEquals(response.getContentLength(), 32);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/core/WebSocketUpgradeTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/core/WebSocketUpgradeTest.java
new file mode 100644
index 00000000..ea6e313c
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/core/WebSocketUpgradeTest.java
@@ -0,0 +1,126 @@
+package org.simpleframework.http.core;
+
+import java.io.OutputStream;
+import java.nio.channels.SocketChannel;
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.MockTrace;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.ByteWriter;
+import org.simpleframework.transport.trace.Trace;
+
+public class WebSocketUpgradeTest extends TestCase implements Container {
+
+ private static final String OPEN_HANDSHAKE =
+ "GET /chat HTTP/1.1\r\n"+
+ "Host: server.example.com\r\n"+
+ "Upgrade: websocket\r\n"+
+ "Connection: Upgrade\r\n"+
+ "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+
+ "Origin: http://example.com\r\n"+
+ "Sec-WebSocket-Protocol: chat, superchat\r\n"+
+ "Sec-WebSocket-Version: 14\r\n" +
+ "\r\n";
+
+ public static class MockChannel implements Channel {
+
+ private ByteCursor cursor;
+
+ public MockChannel(StreamCursor cursor, int dribble) {
+ this.cursor = new DribbleCursor(cursor, dribble);
+ }
+ public boolean isSecure() {
+ return false;
+ }
+
+ public Trace getTrace() {
+ return new MockTrace();
+ }
+
+ public Certificate getCertificate() {
+ return null;
+ }
+
+ public ByteCursor getCursor() {
+ return cursor;
+ }
+
+ public ByteWriter getWriter() {
+ return new MockSender();
+ }
+
+ public Map getAttributes() {
+ return null;
+ }
+
+ public void close() {}
+
+ public SocketChannel getSocket() {
+ return null;
+ }
+ }
+
+ private final BlockingQueue<Response> responses = new LinkedBlockingQueue<Response>();
+
+ public void testWebSocketUpgrade() throws Exception {
+ Allocator allocator = new ArrayAllocator();
+ Controller handler = new ContainerController(this, allocator, 10, 2);
+ StreamCursor cursor = new StreamCursor(OPEN_HANDSHAKE);
+ Channel channel = new MockChannel(cursor, 10);
+
+ handler.start(channel);
+
+ Response response = responses.poll(5000, TimeUnit.MILLISECONDS);
+
+ assertEquals(response.getValue("Connection"), "Upgrade");
+ assertEquals(response.getValue("Upgrade"), "websocket");
+ assertTrue(response.isCommitted());
+ assertTrue(response.isKeepAlive());
+ }
+
+ public void handle(Request request, Response response) {
+ try {
+ process(request, response);
+ responses.offer(response);
+ }catch(Exception e) {
+ e.printStackTrace();
+ assertTrue(false);
+ }
+ }
+
+ public void process(Request request, Response response) throws Exception {
+ String method = request.getMethod();
+
+ assertEquals(method, "GET");
+ assertEquals(request.getValue("Upgrade"), "websocket");
+ assertEquals(request.getValue("Connection"), "Upgrade");
+ assertEquals(request.getValue("Sec-WebSocket-Key"), "dGhlIHNhbXBsZSBub25jZQ==");
+
+ response.setCode(101);
+ response.setValue("Connection", "close");
+ response.setValue("Upgrade", "websocket");
+
+ OutputStream out = response.getOutputStream();
+
+ out.write(10); // force commit
+
+ assertTrue(response.isCommitted());
+ assertTrue(response.isKeepAlive());
+ }
+
+ public static void main(String[] list) throws Exception {
+ new ReactorProcessorTest().testMinimal();
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/BoundaryConsumerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/BoundaryConsumerTest.java
new file mode 100644
index 00000000..8f52100b
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/BoundaryConsumerTest.java
@@ -0,0 +1,77 @@
+package org.simpleframework.http.message;
+
+import java.io.ByteArrayInputStream;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.core.DribbleCursor;
+import org.simpleframework.http.core.StreamCursor;
+import org.simpleframework.http.message.BoundaryConsumer;
+
+public class BoundaryConsumerTest extends TestCase {
+
+ private static final byte[] TERMINAL = { '-', '-', 'A', 'a', 'B', '0', '3', 'x', '-', '-', '\r', '\n', 'X', 'Y' };
+
+ private static final byte[] NORMAL = { '-', '-', 'A', 'a', 'B', '0', '3', 'x', '\r', '\n', 'X', 'Y' };
+
+ private static final byte[] BOUNDARY = { 'A', 'a', 'B', '0', '3', 'x' };
+
+ private BoundaryConsumer boundary;
+
+ public void setUp() {
+ boundary = new BoundaryConsumer(new ArrayAllocator(), BOUNDARY);
+ }
+
+ public void testBoundary() throws Exception {
+ StreamCursor cursor = new StreamCursor(new ByteArrayInputStream(NORMAL));
+
+ while(!boundary.isFinished()) {
+ boundary.consume(cursor);
+ }
+ assertEquals(cursor.read(), 'X');
+ assertEquals(cursor.read(), 'Y');
+ assertTrue(boundary.isFinished());
+ assertFalse(boundary.isEnd());
+ assertFalse(cursor.isReady());
+ }
+
+ public void testTerminal() throws Exception {
+ StreamCursor cursor = new StreamCursor(new ByteArrayInputStream(TERMINAL));
+
+ while(!boundary.isFinished()) {
+ boundary.consume(cursor);
+ }
+ assertEquals(cursor.read(), 'X');
+ assertEquals(cursor.read(), 'Y');
+ assertTrue(boundary.isFinished());
+ assertTrue(boundary.isEnd());
+ assertFalse(cursor.isReady());
+ }
+
+ public void testDribble() throws Exception {
+ DribbleCursor cursor = new DribbleCursor(new StreamCursor(new ByteArrayInputStream(TERMINAL)), 3);
+
+ while(!boundary.isFinished()) {
+ boundary.consume(cursor);
+ }
+ assertEquals(cursor.read(), 'X');
+ assertEquals(cursor.read(), 'Y');
+ assertTrue(boundary.isFinished());
+ assertTrue(boundary.isEnd());
+ assertFalse(cursor.isReady());
+
+ boundary.clear();
+
+ cursor = new DribbleCursor(new StreamCursor(new ByteArrayInputStream(TERMINAL)), 1);
+
+ while(!boundary.isFinished()) {
+ boundary.consume(cursor);
+ }
+ assertEquals(cursor.read(), 'X');
+ assertEquals(cursor.read(), 'Y');
+ assertTrue(boundary.isFinished());
+ assertTrue(boundary.isEnd());
+ assertFalse(cursor.isReady());
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/ChunkedConsumerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/ChunkedConsumerTest.java
new file mode 100644
index 00000000..3b860202
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/ChunkedConsumerTest.java
@@ -0,0 +1,118 @@
+package org.simpleframework.http.message;
+
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.common.buffer.ArrayBuffer;
+import org.simpleframework.common.buffer.Buffer;
+import org.simpleframework.http.core.Chunker;
+import org.simpleframework.http.core.DribbleCursor;
+import org.simpleframework.http.core.StreamCursor;
+import org.simpleframework.http.message.ChunkedConsumer;
+
+public class ChunkedConsumerTest extends TestCase implements Allocator {
+
+ public Buffer buffer;
+
+ public void setUp() {
+ buffer = new ArrayBuffer();
+ }
+
+ public Buffer allocate() {
+ return buffer;
+ }
+
+ public Buffer allocate(long size) {
+ return buffer;
+ }
+
+ public void testChunks() throws Exception {
+ testChunks(64, 1024, 64);
+ testChunks(64, 11, 64);
+ testChunks(1024, 1024, 100000);
+ testChunks(1024, 10, 100000);
+ testChunks(1024, 11, 100000);
+ testChunks(1024, 113, 100000);
+ testChunks(1024, 1, 100000);
+ testChunks(1024, 2, 50000);
+ testChunks(1024, 3, 50000);
+ testChunks(10, 1024, 50000);
+ testChunks(1, 10, 71234);
+ testChunks(2, 11, 123456);
+ testChunks(15, 113, 25271);
+ testChunks(16, 1, 43265);
+ testChunks(64, 2, 63266);
+ testChunks(32, 3, 9203);
+ }
+
+ public void testChunks(int chunkSize, int dribble, int entitySize) throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayOutputStream plain = new ByteArrayOutputStream();
+ Chunker encode = new Chunker(out);
+ StringBuffer buf = new StringBuffer();
+ int fill = 0;
+
+ for(int i = 0, line = 0; i < entitySize; i++) {
+ String text = "["+String.valueOf(i)+"]";
+
+ if(fill >= chunkSize) {
+ encode.write(buf.toString().getBytes("UTF-8"));
+ plain.write(buf.toString().getBytes("UTF-8"));
+ buf.setLength(0);
+ fill = 0;
+ line = 0;
+ }
+ line += text.length();
+ fill += text.length();
+ buf.append(text);
+
+ if(line >= 48) {
+ buf.append("\n");
+ fill++;
+ line = 0;
+ }
+
+ }
+ if(buf.length() > 0) {
+ encode.write(buf.toString().getBytes("UTF-8"));
+ plain.write(buf.toString().getBytes("UTF-8"));
+ }
+ buffer = new ArrayAllocator().allocate(); // N.B clear previous buffer
+ encode.close();
+ byte[] data = out.toByteArray();
+ byte[] plainText = plain.toByteArray();
+ //System.out.println(">>"+new String(data, 0, data.length, "UTF-8")+"<<");
+ //System.out.println("}}"+new String(plainText, 0, plainText.length,"UTF-8")+"{{");
+ DribbleCursor cursor = new DribbleCursor(new StreamCursor(new ByteArrayInputStream(data)), dribble);
+ ChunkedConsumer test = new ChunkedConsumer(this);
+
+ while(!test.isFinished()) {
+ test.consume(cursor);
+ }
+ byte[] result = buffer.encode("UTF-8").getBytes("UTF-8");
+ //System.out.println("))"+new String(result, 0, result.length, "UTF-8")+"((");
+
+ if(result.length != plainText.length) {
+ throw new IOException(String.format("Bad encoding result=[%s] plainText=[%s]", result.length, plainText.length));
+ }
+ for(int i = 0; i < result.length; i++) {
+ if(result[i] != plainText[i]) {
+ throw new IOException(String.format("Values do not match for %s, %s, and %s", chunkSize, dribble, entitySize));
+ }
+ }
+ }
+
+ public void close() throws IOException {
+ // TODO Auto-generated method stub
+
+ }
+
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/ContentConsumerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/ContentConsumerTest.java
new file mode 100644
index 00000000..a6f4f628
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/ContentConsumerTest.java
@@ -0,0 +1,99 @@
+package org.simpleframework.http.message;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.common.buffer.Buffer;
+import org.simpleframework.http.core.DribbleCursor;
+import org.simpleframework.http.core.StreamCursor;
+import org.simpleframework.http.message.ContentConsumer;
+import org.simpleframework.http.message.PartData;
+
+public class ContentConsumerTest extends TestCase implements Allocator {
+
+ private static final byte[] BOUNDARY = { 'A', 'a', 'B', '0', '3', 'x' };
+
+ private Buffer buffer;
+
+ public Buffer allocate() {
+ return buffer;
+ }
+
+ public Buffer allocate(long size) {
+ return buffer;
+ }
+
+ public void testContent() throws Exception {
+ testContent(1, 1);
+
+ for(int i = 1; i < 1000; i++) {
+ testContent(i, i);
+ }
+ for(int i = 20; i < 1000; i++) {
+ for(int j = 1; j < 19; j++) {
+ testContent(i, j);
+ }
+ }
+ testContent(10, 10);
+ testContent(100, 2);
+ }
+
+ public void testContent(int entitySize, int dribble) throws Exception {
+ MockSegment segment = new MockSegment();
+ PartData list = new PartData();
+ ContentConsumer consumer = new ContentConsumer(this, segment, list, BOUNDARY);
+ StringBuffer buf = new StringBuffer();
+
+ segment.add("Content-Disposition", "form-data; name='photo'; filename='photo.jpg'");
+ segment.add("Content-Type", "text/plain");
+ segment.add("Content-ID", "<IDENTITY>");
+
+ for(int i = 0, line = 0; buf.length() < entitySize; i++) {
+ String text = String.valueOf(i);
+
+ line += text.length();
+ buf.append(text);
+
+ if(line >= 48) {
+ buf.append("\n");
+ line = 0;
+ }
+ }
+ // Get request body without boundary
+ String requestBody = buf.toString();
+
+ // Add the boundary to the request body
+ buf.append("\r\n--");
+ buf.append(new String(BOUNDARY, 0, BOUNDARY.length, "UTF-8"));
+ buffer = new ArrayAllocator().allocate();
+
+ DribbleCursor cursor = new DribbleCursor(new StreamCursor(buf.toString()), dribble);
+
+ while(!consumer.isFinished()) {
+ consumer.consume(cursor);
+ }
+ byte[] consumedBytes = buffer.encode("UTF-8").getBytes("UTF-8");
+ String consumedBody = new String(consumedBytes, 0, consumedBytes.length, "UTF-8");
+
+ assertEquals(String.format("Failed for entitySize=%s and dribble=%s", entitySize, dribble), consumedBody, requestBody);
+ assertEquals(cursor.read(), '\r');
+ assertEquals(cursor.read(), '\n');
+ assertEquals(cursor.read(), '-');
+ assertEquals(cursor.read(), '-');
+ assertEquals(cursor.read(), BOUNDARY[0]);
+ assertEquals(cursor.read(), BOUNDARY[1]);
+ assertEquals(consumer.getPart().getContentType().getPrimary(), "text");
+ assertEquals(consumer.getPart().getContentType().getSecondary(), "plain");
+ }
+
+ public void close() throws IOException {
+ // TODO Auto-generated method stub
+
+ }
+
+
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/FileUploadConsumerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/FileUploadConsumerTest.java
new file mode 100644
index 00000000..d668a4a9
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/FileUploadConsumerTest.java
@@ -0,0 +1,86 @@
+package org.simpleframework.http.message;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.core.DribbleCursor;
+import org.simpleframework.http.core.StreamCursor;
+import org.simpleframework.transport.ByteCursor;
+
+public class FileUploadConsumerTest extends TestCase {
+
+ private static final String SOURCE =
+ "--mxvercagiykxaqsdvrfabfhfpaseejrg\r\n"+
+ "Content-Disposition: form-data; name=\"fn\"\r\n"+
+ "\r\n"+
+ "blah_niall\r\n"+
+ "--mxvercagiykxaqsdvrfabfhfpaseejrg\r\n"+
+ "Content-Disposition: form-data; name=\"Filename\"\r\n"+
+ "\r\n"+
+ "content\r\n"+
+ "--mxvercagiykxaqsdvrfabfhfpaseejrg\r\n"+
+ "Content-Disposition: form-data; name=\"Filedata[]\"; filename=\"content\"\r\n"+
+ "Content-Type: application/octet-stream\r\n"+
+ "\r\n"+
+ "<stage version=\"2.0\" keygen_seq=\"1\"><pageObj print_grid=\"0\" border=\"0\" gr=\"1\" width=\"5000\" highResImage=\"1\" height=\"5000\" drawingHeight=\"379\" print_paper=\"LETTER\" istt=\"false\" guides=\"0\" print_layout=\"0\" print_scale=\"0\" drawingWidth=\"188\" fill=\"16777215\" pb=\"0\"><styles><shapeStyle lineColor=\"global:0x333333\" lineWidth=\"-1\" gradientOn=\"true\" dropShadowOn=\"true\" fillColor=\"global:0xd1d1d1\"/><lineStyle borderLine=\"false\" connType=\"right\" width=\"1\" roundCorners=\"true\" begin=\"0\" color=\"0x000000\" end=\"0\" pattern=\"0\"/><textStyle face=\"Arial\" size=\"12\" color=\"0\" style=\"\"/></styles><objects><object shp_id=\"0\" x=\"158\" order=\"0\" y=\"361.5\" linec=\"3355443\" dsy=\"4\" height=\"75\" symbol_id=\"\" gradon=\"true\" text-vertical-pos=\"middle\" width=\"100\" dshad=\"true\" class=\"rectangle\" dsx=\"4\" linew=\"2\" fill=\"0xd1d1d1\" fixed-aspect=\"false\" rot=\"0\" lock=\"false\" libraryid=\"com.gliffy.symbols.basic\" text-horizontal-pos=\"center\"><text/><connlines/></object></objects></pageObj></stage>\r\n"+
+ "--mxvercagiykxaqsdvrfabfhfpaseejrg\r\n"+
+ "Content-Disposition: form-data; name=\"Filename\"\r\n"+
+ "\r\n"+
+ "image\r\n"+
+ "--mxvercagiykxaqsdvrfabfhfpaseejrg\r\n"+
+ "Content-Disposition: form-data; name=\"Filedata[]\"; filename=\"image\"\r\n"+
+ "Content-Type: application/octet-stream\r\n"+
+ "\r\n"+
+ "PNG"+
+ "\r\n"+
+ "--mxvercagiykxaqsdvrfabfhfpaseejrg\r\n"+
+ "Content-Disposition: form-data; name=\"Upload\"\r\n"+
+ "\r\n"+
+ "Submit Query\r\n"+
+ "--mxvercagiykxaqsdvrfabfhfpaseejrg--";
+
+ public void testNoFinalCRLF() throws Exception {
+ byte[] data = SOURCE.getBytes("UTF-8");
+ byte[] boundary = "mxvercagiykxaqsdvrfabfhfpaseejrg".getBytes("UTF-8");
+ Allocator allocator = new ArrayAllocator();
+ FileUploadConsumer consumer = new FileUploadConsumer(allocator, boundary, data.length);
+ ByteCursor cursor = new StreamCursor(data);
+
+ while(!consumer.isFinished()) {
+ consumer.consume(cursor);
+ }
+ assertEquals(consumer.getBody().getContent(), SOURCE);
+ assertEquals(consumer.getBody().getParts().size(), 6);
+ }
+
+ public void testNoFinalCRLSWithDribble() throws Exception {
+ byte[] data = SOURCE.getBytes("UTF-8");
+ byte[] boundary = "mxvercagiykxaqsdvrfabfhfpaseejrg".getBytes("UTF-8");
+ Allocator allocator = new ArrayAllocator();
+ FileUploadConsumer consumer = new FileUploadConsumer(allocator, boundary, data.length);
+ ByteCursor cursor = new StreamCursor(data);
+ DribbleCursor dribble = new DribbleCursor(cursor, 1);
+
+ while(!consumer.isFinished()) {
+ consumer.consume(dribble);
+ }
+ assertEquals(consumer.getBody().getContent(), SOURCE);
+ assertEquals(consumer.getBody().getParts().size(), 6);
+ }
+
+ public void testNoFinalCRLSWithDribble3() throws Exception {
+ byte[] data = SOURCE.getBytes("UTF-8");
+ byte[] boundary = "mxvercagiykxaqsdvrfabfhfpaseejrg".getBytes("UTF-8");
+ Allocator allocator = new ArrayAllocator();
+ FileUploadConsumer consumer = new FileUploadConsumer(allocator, boundary, data.length);
+ ByteCursor cursor = new StreamCursor(data);
+ DribbleCursor dribble = new DribbleCursor(cursor, 3);
+
+ while(!consumer.isFinished()) {
+ consumer.consume(dribble);
+ }
+ assertEquals(consumer.getBody().getContent(), SOURCE);
+ assertEquals(consumer.getBody().getParts().size(), 6);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/MessageHeaderTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/MessageHeaderTest.java
new file mode 100644
index 00000000..36c03092
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/MessageHeaderTest.java
@@ -0,0 +1,48 @@
+package org.simpleframework.http.message;
+
+import junit.framework.TestCase;
+
+public class MessageHeaderTest extends TestCase {
+
+ public void testMessage() {
+ MessageHeader header = new MessageHeader();
+ header.addValue("A", "a");
+ header.addValue("A", "b");
+ header.addValue("A", "c");
+
+ assertEquals(header.getValue("A"), "a");
+ assertEquals(header.getValue("A", 0), "a");
+ assertEquals(header.getValue("A", 1), "b");
+ assertEquals(header.getValue("A", 2), "c");
+
+ header.setValue("A", null);
+
+ assertEquals(header.getValue("A"), null);
+ assertEquals(header.getValue("A", 0), null);
+ assertEquals(header.getValue("A", 1), null);
+ assertEquals(header.getValue("A", 2), null);
+ assertEquals(header.getValue("A", 3), null);
+ assertEquals(header.getValue("A", 4), null);
+ assertEquals(header.getValue("A", 5), null);
+
+ header.setValue("A", "X");
+
+ assertEquals(header.getValue("A"), "X");
+ assertEquals(header.getValue("A", 0), "X");
+ assertEquals(header.getValue("A", 1), null);
+
+ header.addInteger("A", 1);
+
+ assertEquals(header.getValue("A"), "X");
+ assertEquals(header.getValue("A", 0), "X");
+ assertEquals(header.getValue("A", 1), "1");
+ assertEquals(header.getValue("A", 2), null);
+
+ header.addValue("A", null);
+
+ assertEquals(header.getValue("A"), "X");
+ assertEquals(header.getValue("A", 0), "X");
+ assertEquals(header.getValue("A", 1), "1");
+ assertEquals(header.getValue("A", 2), null);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/MockBody.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/MockBody.java
new file mode 100644
index 00000000..4a4ee756
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/MockBody.java
@@ -0,0 +1,47 @@
+package org.simpleframework.http.message;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.simpleframework.http.Part;
+import org.simpleframework.http.message.PartData;
+
+
+public class MockBody implements Body {
+
+ protected PartData list;
+
+ protected String body;
+
+ public MockBody() {
+ this("");
+ }
+
+ public MockBody(String body) {
+ this.list = new PartData();
+ this.body = body;
+ }
+
+ public List<Part> getParts() {
+ return list.getParts();
+ }
+
+ public Part getPart(String name) {
+ return list.getPart(name);
+ }
+
+ public String getContent(String charset) {
+ return body;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new ByteArrayInputStream(body.getBytes("UTF-8"));
+ }
+
+ public String getContent() throws IOException {
+ return body;
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/MockHeader.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/MockHeader.java
new file mode 100644
index 00000000..67ea0910
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/MockHeader.java
@@ -0,0 +1,22 @@
+package org.simpleframework.http.message;
+
+import org.simpleframework.http.Address;
+import org.simpleframework.http.parse.AddressParser;
+
+public class MockHeader extends RequestConsumer {
+
+ private AddressParser parser;
+ private String address;
+
+ public MockHeader(String address) {
+ this.address = address;
+ }
+ public Address getAddress() {
+ if(parser == null) {
+ parser = new AddressParser(address);
+ }
+ return parser;
+ }
+
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/MockSegment.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/MockSegment.java
new file mode 100644
index 00000000..7f49acb1
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/MockSegment.java
@@ -0,0 +1,83 @@
+package org.simpleframework.http.message;
+
+import java.util.List;
+
+import org.simpleframework.http.ContentDisposition;
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.message.MessageHeader;
+import org.simpleframework.http.message.Segment;
+import org.simpleframework.http.parse.ContentDispositionParser;
+import org.simpleframework.http.parse.ContentTypeParser;
+
+public class MockSegment implements Segment {
+
+ private MessageHeader header;
+
+ public MockSegment() {
+ this.header = new MessageHeader();
+ }
+
+ public boolean isFile() {
+ return false;
+ }
+
+ public ContentType getContentType() {
+ String value = getValue("Content-Type");
+
+ if(value == null) {
+ return null;
+ }
+ return new ContentTypeParser(value);
+ }
+
+ public long getContentLength() {
+ String value = getValue("Content-Length");
+
+ if(value != null) {
+ return new Long(value);
+ }
+ return -1;
+ }
+
+ public String getTransferEncoding() {
+ List<String> list = getValues("Transfer-Encoding");
+
+ if(list.size() > 0) {
+ return list.get(0);
+ }
+ return null;
+ }
+
+ public ContentDisposition getDisposition() {
+ String value = getValue("Content-Disposition");
+
+ if(value == null) {
+ return null;
+ }
+ return new ContentDispositionParser(value);
+ }
+
+ public List<String> getValues(String name) {
+ return header.getValues(name);
+ }
+
+ public String getValue(String name) {
+ return header.getValue(name);
+ }
+
+ public String getValue(String name, int index) {
+ return header.getValue(name, index);
+ }
+
+ protected void add(String name, String value) {
+ header.addValue(name, value);
+ }
+
+ public String getName() {
+ return null;
+ }
+
+ public String getFileName() {
+ return null;
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/PartConsumerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/PartConsumerTest.java
new file mode 100644
index 00000000..4f96405b
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/PartConsumerTest.java
@@ -0,0 +1,33 @@
+package org.simpleframework.http.message;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.Part;
+import org.simpleframework.http.core.StreamCursor;
+import org.simpleframework.http.message.PartConsumer;
+import org.simpleframework.http.message.PartData;
+import org.simpleframework.transport.ByteCursor;
+
+public class PartConsumerTest extends TestCase {
+
+ private static final String SOURCE =
+ "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "... contents of file1.txt ...\r\n"+
+ "--AaB03x\r\n";
+
+ public void testHeader() throws Exception {
+ PartData list = new PartData();
+ PartConsumer consumer = new PartConsumer(new ArrayAllocator(), list, "AaB03x".getBytes("UTF-8"), 8192);
+ ByteCursor cursor = new StreamCursor(SOURCE);
+
+ while(!consumer.isFinished()) {
+ consumer.consume(cursor);
+ }
+ assertEquals(list.getParts().size(), 1);
+ assertEquals(list.getParts().get(0).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(0).getContentType().getSecondary(), "plain");
+ assertEquals(((Part)list.getParts().get(0)).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'");
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/PartSeriesConsumerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/PartSeriesConsumerTest.java
new file mode 100644
index 00000000..448dad50
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/PartSeriesConsumerTest.java
@@ -0,0 +1,157 @@
+package org.simpleframework.http.message;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.core.DribbleCursor;
+import org.simpleframework.http.core.StreamCursor;
+import org.simpleframework.http.message.PartData;
+import org.simpleframework.http.message.PartSeriesConsumer;
+import org.simpleframework.transport.ByteCursor;
+
+public class PartSeriesConsumerTest extends TestCase {
+
+ private static final String SIMPLE =
+ "--AaB03x\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file1.txt ...\r\n"+
+ "--AaB03x--\r\n";
+
+ private static final String NORMAL =
+ "--AaB03x\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file1.txt\r\n"+
+ "--AaB03x\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file2.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file2.txt\r\n"+
+ "--AaB03x\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file3.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file3.txt ...\r\n"+
+ "--AaB03x--\r\n";
+
+ private static final String MIXED =
+ "--AaB03x\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file1.txt\r\n"+
+ "--AaB03x\r\n"+
+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file2.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file2.txt ...\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file3.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file3.txt ...\r\n"+
+ "--BbC04y\r\n"+
+ "Content-Disposition: form-data; name='pics'; filename='file4.txt'\r\n"+
+ "Content-Type: text/plain\r\n\r\n"+
+ "example contents of file4.txt ...\r\n"+
+ "--BbC04y--\r\n"+
+ "--AaB03x--\r\n";
+
+ public void testSimple() throws Exception {
+ PartData list = new PartData();
+ PartSeriesConsumer consumer = new PartSeriesConsumer(new ArrayAllocator(), list, "AaB03x".getBytes("UTF-8"));
+ ByteCursor cursor = new StreamCursor(SIMPLE);
+
+ while(!consumer.isFinished()) {
+ consumer.consume(cursor);
+ }
+ assertEquals(list.getParts().size(), 1);
+ assertEquals(list.getParts().get(0).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(0).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'");
+ assertEquals(list.getParts().get(0).getContent(), "example contents of file1.txt ...");
+ assertEquals(cursor.ready(), -1);
+ assertEquals(consumer.getBody().getContent(), SIMPLE);
+ }
+
+ public void testNormal() throws Exception {
+ PartData list = new PartData();
+ PartSeriesConsumer consumer = new PartSeriesConsumer(new ArrayAllocator(), list, "AaB03x".getBytes("UTF-8"));
+ ByteCursor cursor = new StreamCursor(NORMAL);
+
+ while(!consumer.isFinished()) {
+ consumer.consume(cursor);
+ }
+ assertEquals(list.getParts().size(), 3);
+ assertEquals(list.getParts().get(0).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(0).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'");
+ assertEquals(list.getParts().get(0).getContent(), "example contents of file1.txt");
+ assertEquals(list.getParts().get(1).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(1).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(1).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file2.txt'");
+ assertEquals(list.getParts().get(1).getContent(), "example contents of file2.txt");
+ assertEquals(list.getParts().get(2).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(2).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(2).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file3.txt'");
+ assertEquals(list.getParts().get(2).getContent(), "example contents of file3.txt ...");
+ assertEquals(cursor.ready(), -1);
+ assertEquals(consumer.getBody().getContent(), NORMAL);
+ }
+
+ public void testMixed() throws Exception {
+ PartData list = new PartData();
+ PartSeriesConsumer consumer = new PartSeriesConsumer(new ArrayAllocator(), list, "AaB03x".getBytes("UTF-8"));
+ ByteCursor cursor = new StreamCursor(MIXED);
+
+ while(!consumer.isFinished()) {
+ consumer.consume(cursor);
+ }
+ assertEquals(list.getParts().size(), 4);
+ assertEquals(list.getParts().get(0).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(0).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'");
+ assertEquals(list.getParts().get(0).getContent(), "example contents of file1.txt");
+ assertEquals(list.getParts().get(1).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(1).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(1).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file2.txt'");
+ assertEquals(list.getParts().get(1).getContent(), "example contents of file2.txt ...");
+ assertEquals(list.getParts().get(2).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(2).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(2).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file3.txt'");
+ assertEquals(list.getParts().get(2).getContent(), "example contents of file3.txt ...");
+ assertEquals(list.getParts().get(3).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(3).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(3).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file4.txt'");
+ assertEquals(list.getParts().get(3).getContent(), "example contents of file4.txt ...");
+ assertEquals(cursor.ready(), -1);
+ assertEquals(consumer.getBody().getContent(), MIXED);
+ }
+
+ public void testDribble() throws Exception {
+ PartData list = new PartData();
+ PartSeriesConsumer consumer = new PartSeriesConsumer(new ArrayAllocator(), list, "AaB03x".getBytes("UTF-8"));
+ ByteCursor cursor = new DribbleCursor(new StreamCursor(NORMAL), 1);
+
+ while(!consumer.isFinished()) {
+ consumer.consume(cursor);
+ }
+ assertEquals(list.getParts().size(), 3);
+ assertEquals(list.getParts().get(0).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(0).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'");
+ assertEquals(list.getParts().get(0).getContent(), "example contents of file1.txt");
+ assertEquals(list.getParts().get(1).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(1).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(1).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file2.txt'");
+ assertEquals(list.getParts().get(1).getContent(), "example contents of file2.txt");
+ assertEquals(list.getParts().get(2).getContentType().getPrimary(), "text");
+ assertEquals(list.getParts().get(2).getContentType().getSecondary(), "plain");
+ assertEquals(list.getParts().get(2).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file3.txt'");
+ assertEquals(list.getParts().get(2).getContent(), "example contents of file3.txt ...");
+ assertEquals(cursor.ready(), -1);
+ assertEquals(consumer.getBody().getContent(), NORMAL);
+ }
+
+ public static void main(String[] list) throws Exception {
+ new PartSeriesConsumerTest().testMixed();
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/ReplyConsumer.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/ReplyConsumer.java
new file mode 100644
index 00000000..a7fbfe5f
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/ReplyConsumer.java
@@ -0,0 +1,141 @@
+package org.simpleframework.http.message;
+
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.ResponseHeader;
+import org.simpleframework.http.Status;
+import org.simpleframework.http.message.RequestConsumer;
+
+public class ReplyConsumer extends RequestConsumer implements ResponseHeader {
+
+ private String text;
+ private int code;
+
+ public ReplyConsumer() {
+ super();
+ }
+
+ private void status() {
+ while(pos < count) {
+ if(!digit(array[pos])) {
+ break;
+ }
+ code *= 10;
+ code += array[pos];
+ code -= '0';
+ pos++;
+ }
+ }
+
+ private void text() {
+ StringBuilder builder = new StringBuilder();
+
+ while(pos < count) {
+ if(terminal(array[pos])) {
+ pos += 2;
+ break;
+ }
+ builder.append((char) array[pos]);
+ pos++;
+ }
+ text = builder.toString();
+ }
+
+ public String getDescription() {
+ return text;
+ }
+
+ public void setDescription(String text) {
+ this.text = text;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int status) {
+ this.code = status;
+ }
+
+ public Status getStatus() {
+ return Status.getStatus(code);
+ }
+
+ public void setStatus(Status status) {
+ code = status.code;
+ text = status.description;
+ }
+
+ @Override
+ protected void add(String name, String value) {
+ if(equal("Set-Cookie", name)) { // A=b; version=1; path=/;
+ String[] list = value.split(";"); // "A=b", "version=1", "path=/"
+
+ if(list.length > 0) {
+ String[] pair = list[0].split("=");
+
+ if(pair.length > 1) {
+ header.setCookie(pair[0], pair[1]); // "A", "b"
+ }
+ }
+ }
+ super.add(name, value);
+ }
+
+ @Override
+ protected void process() {
+ version(); // HTTP/1.1
+ adjust();
+ status(); // 200
+ adjust();
+ text(); // OK
+ adjust();
+ headers();
+ }
+
+ public void setMajor(int major) {
+ this.major = major;
+
+ }
+
+ public void setMinor(int minor) {
+ this.minor = minor;
+
+ }
+
+ public void addValue(String name, String value) {
+ header.addValue(name, value);
+ }
+
+ public void addInteger(String name, int value) {
+ header.addInteger(name, value);
+
+ }
+
+ public void addDate(String name, long date) {
+ header.addDate(name, date);
+ }
+
+ public void setValue(String name, String value) {
+ header.setValue(name, value);
+ }
+
+ public void setInteger(String name, int value) {
+ header.setInteger(name, value);
+ }
+
+ public void setLong(String name, long value) {
+ header.setLong(name, value);
+ }
+
+ public void setDate(String name, long date) {
+ header.setDate(name, date);
+ }
+
+ public Cookie setCookie(Cookie cookie) {
+ return header.setCookie(cookie);
+ }
+
+ public Cookie setCookie(String name, String value) {
+ return header.setCookie(name, value);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/SegmentConsumerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/SegmentConsumerTest.java
new file mode 100644
index 00000000..1c6916e7
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/SegmentConsumerTest.java
@@ -0,0 +1,103 @@
+package org.simpleframework.http.message;
+
+import java.io.IOException;
+
+import org.simpleframework.http.core.DribbleCursor;
+import org.simpleframework.http.core.StreamCursor;
+import org.simpleframework.http.message.SegmentConsumer;
+import org.simpleframework.transport.ByteCursor;
+
+import junit.framework.TestCase;
+
+public class SegmentConsumerTest extends TestCase {
+
+ private static final String SOURCE =
+ "Content-Type: application/x-www-form-urlencoded\r\n"+
+ "User-Agent:\r\n" +
+ "Content-Length: 42\r\n"+
+ "Transfer-Encoding: chunked\r\n"+
+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+
+ " \t\t image/png;\t\r\n\t"+
+ " q=1.0,*;q=0.1\r\n"+
+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+
+ "Host: some.host.com \r\n"+
+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+
+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+
+ "\r\n";
+
+ private static final String EMPTY =
+ "Accept-Language:\r\n"+
+ "Content-Length:\r\n"+
+ "Content-Type:\r\n"+
+ "Content-Disposition:\r\n"+
+ "Transfer-Encoding:\r\n"+
+ "Expect:\r\n"+
+ "Cookie:\r\n"+
+ "\r\n";
+
+ protected SegmentConsumer header;
+
+ public void setUp() throws IOException {
+ header = new SegmentConsumer();
+ }
+
+ public void testHeader() throws Exception {
+ ByteCursor cursor = new StreamCursor(SOURCE);
+
+ while(!header.isFinished()) {
+ header.consume(cursor);
+ }
+ assertEquals(cursor.ready(), -1);
+ assertEquals(header.getValue("Pragma"), null);
+ assertEquals(header.getValue("User-Agent"), "");
+ assertEquals(header.getValue("Content-Length"), "42");
+ assertEquals(header.getValue("Content-Type"), "application/x-www-form-urlencoded");
+ assertEquals(header.getValue("Host"), "some.host.com");
+ assertEquals(header.getValues("Accept").size(), 4);
+ assertEquals(header.getValues("Accept").get(0), "image/gif");
+ assertEquals(header.getValues("Accept").get(1), "image/png");
+ assertEquals(header.getValues("Accept").get(2), "image/jpeg");
+ assertEquals(header.getValues("Accept").get(3), "*");
+ assertEquals(header.getContentType().getPrimary(), "application");
+ assertEquals(header.getContentType().getSecondary(), "x-www-form-urlencoded");
+ assertEquals(header.getTransferEncoding(), "chunked");
+ }
+
+ public void testEmptyHeader() throws Exception {
+ ByteCursor cursor = new StreamCursor(EMPTY);
+
+ while(!header.isFinished()) {
+ header.consume(cursor);
+ }
+ assertEquals(cursor.ready(), -1);
+ assertEquals(header.getValue("Accept-Language"), "");
+ assertEquals(header.getValue("Content-Length"), "");
+ assertEquals(header.getValue("Content-Type"), "");
+ assertEquals(header.getValue("Content-Disposition"), "");
+ assertEquals(header.getValue("Transfer-Encoding"), "");
+ assertEquals(header.getValue("Expect"), "");
+ assertEquals(header.getValue("Cookie"), "");
+ assertEquals(header.getContentType().getPrimary(), null);
+ assertEquals(header.getContentType().getSecondary(), null);
+ }
+
+ public void testDribble() throws Exception {
+ ByteCursor cursor = new DribbleCursor(new StreamCursor(SOURCE), 1);
+
+ while(!header.isFinished()) {
+ header.consume(cursor);
+ }
+ assertEquals(cursor.ready(), -1);
+ assertEquals(header.getValue("Content-Length"), "42");
+ assertEquals(header.getValue("Content-Type"), "application/x-www-form-urlencoded");
+ assertEquals(header.getValue("Host"), "some.host.com");
+ assertEquals(header.getValues("Accept").size(), 4);
+ assertEquals(header.getValues("Accept").get(0), "image/gif");
+ assertEquals(header.getValues("Accept").get(1), "image/png");
+ assertEquals(header.getValues("Accept").get(2), "image/jpeg");
+ assertEquals(header.getValues("Accept").get(3), "*");
+ assertEquals(header.getContentType().getPrimary(), "application");
+ assertEquals(header.getContentType().getSecondary(), "x-www-form-urlencoded");
+ assertEquals(header.getTransferEncoding(), "chunked");
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/message/TokenConsumerTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/message/TokenConsumerTest.java
new file mode 100644
index 00000000..de7461c2
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/message/TokenConsumerTest.java
@@ -0,0 +1,55 @@
+package org.simpleframework.http.message;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.core.DribbleCursor;
+import org.simpleframework.http.core.StreamCursor;
+import org.simpleframework.http.message.TokenConsumer;
+import org.simpleframework.transport.ByteCursor;
+
+public class TokenConsumerTest extends TestCase {
+
+ public void testTokenConsumer() throws IOException {
+ Allocator allocator = new ArrayAllocator();
+ TokenConsumer consumer = new TokenConsumer(allocator, "\r\n".getBytes());
+ ByteCursor cursor = new StreamCursor("\r\n");
+
+ consumer.consume(cursor);
+
+ assertEquals(cursor.ready(), -1);
+ assertTrue(consumer.isFinished());
+ }
+
+ public void testTokenConsumerException() throws IOException {
+ Allocator allocator = new ArrayAllocator();
+ TokenConsumer consumer = new TokenConsumer(allocator, "\r\n".getBytes());
+ ByteCursor cursor = new StreamCursor("--\r\n");
+ boolean exception = false;
+
+ try {
+ consumer.consume(cursor);
+ } catch(Exception e) {
+ exception = true;
+ }
+ assertTrue("Exception not thrown for invalid token", exception);
+ }
+
+ public void testTokenConsumerDribble() throws IOException {
+ Allocator allocator = new ArrayAllocator();
+ TokenConsumer consumer = new TokenConsumer(allocator, "This is a large token to be consumed\r\n".getBytes());
+ DribbleCursor cursor = new DribbleCursor(new StreamCursor("This is a large token to be consumed\r\n0123456789"), 1);
+
+ consumer.consume(cursor);
+
+ assertEquals(cursor.ready(), 1);
+ assertTrue(consumer.isFinished());
+ assertEquals(cursor.read(), '0');
+ assertEquals(cursor.read(), '1');
+ assertEquals(cursor.read(), '2');
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/AddressParserTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/AddressParserTest.java
new file mode 100644
index 00000000..0b93a900
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/AddressParserTest.java
@@ -0,0 +1,92 @@
+package org.simpleframework.http.parse;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.http.Query;
+
+public class AddressParserTest extends TestCase {
+
+ private AddressParser link;
+
+ protected void setUp() {
+ link = new AddressParser();
+ }
+
+ public void testEmptyPath() {
+ assertEquals("/", link.getPath().toString());
+ }
+
+ public void testEmptyQuery() {
+ Query query = link.getQuery();
+ assertEquals(0, query.size());
+ }
+
+ public void testPath() {
+ link.parse("/this/./is//some/relative/./hidden/../URI.txt");
+ assertEquals("/this/is//some/relative/URI.txt", link.getPath().toString());
+
+ link.parse("/this//is/a/simple/path.html?query");
+ assertEquals("/this//is/a/simple/path.html", link.getPath().toString());
+ }
+
+ public void testQuery() {
+ link.parse("/?name=value&attribute=string");
+
+ Query query = link.getQuery();
+
+ assertEquals(2, query.size());
+ assertEquals("value", query.get("name"));
+ assertTrue(query.containsKey("attribute"));
+
+ query.clear();
+ query.put("name", "change");
+
+ assertEquals("change", query.get("name"));
+ }
+
+ public void testPathParameters() {
+ link.parse("/index.html;jsessionid=1234567890?jsessionid=query");
+ assertEquals("1234567890", link.getParameters().get("jsessionid"));
+
+ link.parse("/path/index.jsp");
+ link.getParameters().put("jsessionid", "value");
+
+ assertEquals("/path/index.jsp;jsessionid=value", link.toString());
+
+ link.parse("/path");
+ link.getParameters().put("a", "1");
+ link.getParameters().put("b", "2");
+ link.getParameters().put("c", "3");
+
+ link.parse(link.toString());
+
+ assertEquals("1", link.getParameters().get("a"));
+ assertEquals("2", link.getParameters().get("b"));
+ assertEquals("3", link.getParameters().get("c"));
+
+
+ }
+
+ public void testAbsolute() {
+ link.parse("http://domain:9090/index.html?query=value");
+ assertEquals("domain", link.getDomain());
+
+ link.setDomain("some.domain");
+ assertEquals("some.domain", link.getDomain());
+ assertEquals("http://some.domain:9090/index.html?query=value", link.toString());
+ assertEquals(9090, link.getPort());
+
+ link.parse("domain.com:80/index.html?a=b&c=d");
+ assertEquals("domain.com", link.getDomain());
+ assertEquals(80, link.getPort());
+
+ link.parse("https://secure.com/index.html");
+ assertEquals("https", link.getScheme());
+ assertEquals("secure.com", link.getDomain());
+
+ link.setDomain("www.google.com:45");
+ assertEquals("www.google.com", link.getDomain());
+ assertEquals("https://www.google.com:45/index.html", link.toString());
+ assertEquals(45, link.getPort());
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/ContentDispositionParserTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/ContentDispositionParserTest.java
new file mode 100644
index 00000000..e5b02665
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/ContentDispositionParserTest.java
@@ -0,0 +1,33 @@
+package org.simpleframework.http.parse;
+
+import org.simpleframework.http.parse.ContentDispositionParser;
+
+import junit.framework.TestCase;
+
+public class ContentDispositionParserTest extends TestCase {
+
+ private ContentDispositionParser parser;
+
+ public void setUp() {
+ parser = new ContentDispositionParser();
+ }
+
+ public void testDisposition() {
+ parser.parse("form-data; name=\"input_check\"");
+
+ assertFalse(parser.isFile());
+ assertEquals(parser.getName(), "input_check");
+
+ parser.parse("form-data; name=\"input_password\"");
+
+ assertFalse(parser.isFile());
+ assertEquals(parser.getName(), "input_password");
+
+ parser.parse("form-data; name=\"FileItem\"; filename=\"C:\\Inetpub\\wwwroot\\Upload\\file1.txt\"");
+
+ assertTrue(parser.isFile());
+ assertEquals(parser.getName(), "FileItem");
+ assertEquals(parser.getFileName(), "C:\\Inetpub\\wwwroot\\Upload\\file1.txt");
+
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/ContentTypeParserTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/ContentTypeParserTest.java
new file mode 100644
index 00000000..863440ec
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/ContentTypeParserTest.java
@@ -0,0 +1,74 @@
+package org.simpleframework.http.parse;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.http.parse.ContentTypeParser;
+
+public class ContentTypeParserTest extends TestCase {
+
+ private ContentTypeParser type;
+
+ protected void setUp() {
+ type = new ContentTypeParser();
+ }
+
+ public void testEmpty() {
+ assertEquals(null, type.getPrimary());
+ assertEquals(null, type.getSecondary());
+ assertEquals(null, type.getCharset());
+ }
+
+ public void testPlain() {
+ type.parse("text/html");
+ assertEquals("text", type.getPrimary());
+ assertEquals("html", type.getSecondary());
+
+ type.setSecondary("plain");
+ assertEquals("text", type.getPrimary());
+ assertEquals("plain", type.getSecondary());
+ }
+
+ public void testCharset() {
+ type.parse("text/html; charset=UTF-8");
+ assertEquals("text", type.getPrimary());
+ assertEquals("UTF-8", type.getCharset());
+ assertEquals("text/html", type.getType());
+
+ type.setCharset("ISO-8859-1");
+ assertEquals("ISO-8859-1", type.getCharset());
+ }
+
+ public void testIgnore() {
+ type.parse("text/html; name=value; charset=UTF-8; property=value");
+ assertEquals("UTF-8", type.getCharset());
+ assertEquals("html", type.getSecondary());
+ }
+
+ public void testFlexibility() {
+ type.parse(" text/html ;charset= UTF-8 ; name = value" );
+ assertEquals("text", type.getPrimary());
+ assertEquals("html", type.getSecondary());
+ assertEquals("text/html", type.getType());
+ assertEquals("UTF-8", type.getCharset());
+ }
+
+ public void testString() {
+ type.parse(" image/gif; name=value");
+ assertEquals("image/gif; name=value", type.toString());
+
+ type.parse(" text/html; charset =ISO-8859-1");
+ assertEquals("text/html; charset=ISO-8859-1", type.toString());
+ assertEquals("text/html", type.getType());
+
+ type.setSecondary("css");
+ assertEquals("text", type.getPrimary());
+ assertEquals("css", type.getSecondary());
+ assertEquals("text/css", type.getType());
+ assertEquals("text/css; charset=ISO-8859-1", type.toString());
+
+ type.setPrimary("image");
+ assertEquals("image", type.getPrimary());
+ assertEquals("css", type.getSecondary());
+ assertEquals("image/css", type.getType());
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/CookieParserTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/CookieParserTest.java
new file mode 100644
index 00000000..9c0c7b89
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/CookieParserTest.java
@@ -0,0 +1,22 @@
+package org.simpleframework.http.parse;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.http.Cookie;
+
+public class CookieParserTest extends TestCase {
+
+ public void testParse() throws Exception {
+ CookieParser parser = new CookieParser("blackbird={\"pos\": 1, \"size\": 0, \"load\": null}; JSESSIONID=31865d30-e252-4729-ac6f-9abdd1fb9071");
+ List<Cookie> cookies = new ArrayList<Cookie>();
+
+ for(Cookie cookie : parser) {
+ System.out.println(cookie.toClientString());
+ cookies.add(cookie);
+ }
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/DateParserTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/DateParserTest.java
new file mode 100644
index 00000000..f262d10a
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/DateParserTest.java
@@ -0,0 +1,55 @@
+package org.simpleframework.http.parse;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import junit.framework.TestCase;
+
+public class DateParserTest extends TestCase {
+
+ /**
+ * Sun, 06 Nov 2009 08:49:37 GMT ; RFC 822, updated by RFC 1123 Sunday,
+ * 06-Nov-09 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 Sun Nov 6 08:49:37
+ * 2009 ; ANSI C's asctime() format
+ */
+ public void testDate() {
+ DateParser rfc822 = new DateParser("Sun, 06 Nov 2009 08:49:37 GMT");
+ DateParser rfc850 = new DateParser("Sunday, 06-Nov-09 08:49:37 GMT");
+ DateParser asctime = new DateParser("Sun Nov 6 08:49:37 2009");
+
+ assertEquals(rfc822.toLong() >> 10, rfc850.toLong() >> 10); // shift out
+ // seconds
+ assertEquals(rfc822.toLong() >> 10, asctime.toLong() >> 10); // shift out
+ // seconds
+ assertEquals(rfc822.toString(), rfc850.toString());
+ assertEquals(rfc822.toString(), asctime.toString());
+ assertEquals(rfc850.toString(), "Sun, 06 Nov 2009 08:49:37 GMT");
+ assertEquals(rfc850.toString().length(), 29);
+ assertEquals(rfc822.toString(), "Sun, 06 Nov 2009 08:49:37 GMT");
+ assertEquals(rfc822.toString().length(), 29);
+ assertEquals(asctime.toString(), "Sun, 06 Nov 2009 08:49:37 GMT");
+ assertEquals(asctime.toString().length(), 29);
+ }
+
+ public void testLong() throws Exception {
+ String date = "Thu, 20 Jan 2011 16:43:08 GMT";
+
+ DateParser dp1 = new DateParser(date);
+ System.out.println("value a: " + dp1.toLong());
+ Thread.sleep(50);
+
+ DateParser dp2 = new DateParser(date);
+ System.out.println("value b: " + dp2.toLong());
+ Thread.sleep(50);
+
+ DateParser dp3 = new DateParser(date);
+ System.out.println("value c: " + dp3.toLong());
+
+ assertEquals(dp1.toLong(), dp2.toLong());
+ assertEquals(dp2.toLong(), dp3.toLong());
+ assertEquals(dp1.toString(), dp2.toString());
+ assertEquals(dp2.toString(), dp3.toString());
+
+
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/LanguageParserTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/LanguageParserTest.java
new file mode 100644
index 00000000..2d382f04
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/LanguageParserTest.java
@@ -0,0 +1,26 @@
+package org.simpleframework.http.parse;
+
+import junit.framework.TestCase;
+
+public class LanguageParserTest extends TestCase {
+
+ public void testLanguages() throws Exception {
+ LanguageParser parser = new LanguageParser();
+
+ parser.parse("en-gb,en;q=0.5");
+
+ assertEquals(parser.list().get(0).getLanguage(), "en");
+ assertEquals(parser.list().get(0).getCountry(), "GB");
+ assertEquals(parser.list().get(1).getLanguage(), "en");
+ assertEquals(parser.list().get(1).getCountry(), "");
+
+ parser.parse("en-gb,en;q=0.5,*;q=0.9");
+
+ assertEquals(parser.list().get(0).getLanguage(), "en");
+ assertEquals(parser.list().get(0).getCountry(), "GB");
+ assertEquals(parser.list().get(1).getLanguage(), "*");
+ assertEquals(parser.list().get(2).getLanguage(), "en");
+ assertEquals(parser.list().get(2).getCountry(), "");
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/ListParserTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/ListParserTest.java
new file mode 100644
index 00000000..100bf275
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/ListParserTest.java
@@ -0,0 +1,97 @@
+package org.simpleframework.http.parse;
+
+import junit.framework.TestCase;
+
+public class ListParserTest extends TestCase {
+
+ private ValueParser list;
+
+ protected void setUp() {
+ list = new ValueParser();
+ }
+
+ public void testEmpty() {
+ assertEquals(0, list.list().size());
+ }
+
+ public void testQvalue() {
+ list.parse("ISO-8859-1,utf-8;q=0.7,*;q=0.7");
+ assertEquals(list.list().get(0), "ISO-8859-1");
+ assertEquals(list.list().get(1), "utf-8");
+ assertEquals(list.list().get(2), "*");
+ }
+
+ public void testPlain() {
+ list.parse("en-gb");
+ assertEquals("en-gb", list.list().get(0));
+
+ list.parse("en");
+ assertEquals("en", list.list().get(0));
+ }
+
+ public void testList() {
+ list.parse("en-gb, en-us");
+ assertEquals(2, list.list().size());
+ assertEquals("en-gb", list.list().get(0));
+ assertEquals("en-us", list.list().get(1));
+ }
+
+ public void testOrder() {
+ list.parse("en-gb, en-us");
+ assertEquals(2, list.list().size());
+ assertEquals("en-gb", list.list().get(0));
+ assertEquals("en-us", list.list().get(1));
+
+ list.parse("da, en-gb;q=0.8, en;q=0.7");
+ assertEquals("da", list.list().get(0));
+ assertEquals("en-gb", list.list().get(1));
+ assertEquals("en", list.list().get(2));
+
+ list.parse("fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7");
+ assertEquals("en-gb", list.list().get(0));
+ assertEquals("en", list.list().get(1));
+ assertEquals("en-us", list.list().get(2));
+ assertEquals("fr", list.list().get(3));
+
+ list.parse("en;q=0.2, en-us;q=1.0, en-gb");
+ assertEquals("en-gb", list.list().get(0));
+ assertEquals("en-us", list.list().get(1));
+ assertEquals("en", list.list().get(2));
+ }
+
+ public void testRange() {
+ list.parse("image/gif, image/jpeg, text/html");
+ assertEquals(3, list.list().size());
+ assertEquals("image/gif", list.list().get(0));
+ assertEquals("text/html", list.list().get(2));
+
+ list.parse("image/gif;q=1.0, image/jpeg;q=0.8, image/png; q=1.0,*;q=0.1");
+ assertEquals("image/gif", list.list().get(0));
+ assertEquals("image/png", list.list().get(1));
+ assertEquals("image/jpeg", list.list().get(2));
+
+ list.parse("gzip;q=1.0, identity; q=0.5, *;q=0");
+ assertEquals("gzip", list.list().get(0));
+ assertEquals("identity", list.list().get(1));
+ }
+
+ public void testFlexibility() {
+ list.parse("last; quantity=1;q=0.001, first; text=\"a, b, c, d\";q=0.4");
+ assertEquals(2, list.list().size());
+ assertEquals("first; text=\"a, b, c, d\"", list.list().get(0));
+ assertEquals("last; quantity=1", list.list().get(1));
+
+ list.parse("image/gif, , image/jpeg, image/png;q=0.8, *");
+ assertEquals(4, list.list().size());
+ assertEquals("image/gif", list.list().get(0));
+ assertEquals("image/jpeg", list.list().get(1));
+ assertEquals("*", list.list().get(2));
+ assertEquals("image/png", list.list().get(3));
+
+ list.parse("first=\"\\\"a, b, c, d\\\", a, b, c, d\", third=\"a\";q=0.9,,second=2");
+ assertEquals(3, list.list().size());
+ assertEquals("first=\"\\\"a, b, c, d\\\", a, b, c, d\"", list.list().get(0));
+ assertEquals("second=2", list.list().get(1));
+ assertEquals("third=\"a\"", list.list().get(2));
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/ParameterTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/ParameterTest.java
new file mode 100644
index 00000000..5c14d00c
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/ParameterTest.java
@@ -0,0 +1,69 @@
+package org.simpleframework.http.parse;
+
+import org.simpleframework.http.parse.QueryParser;
+
+import junit.framework.TestCase;
+
+public class ParameterTest extends TestCase {
+
+ private QueryParser data;
+
+ protected void setUp() {
+ data = new QueryParser();
+ }
+
+ public void testEmptyPath() {
+ assertEquals(0, data.size());
+ }
+
+ public void testValue() {
+ data.parse("a=");
+
+ assertEquals(1, data.size());
+ assertEquals("", data.get("a"));
+
+ data.parse("a=&b=c");
+
+ assertEquals(2, data.size());
+ assertEquals("", data.get("a"));
+ assertEquals("c", data.get("b"));
+
+ data.parse("a=b&c=d&e=f&");
+
+ assertEquals(3, data.size());
+ assertEquals("b", data.get("a"));
+ assertEquals("d", data.get("c"));
+ assertEquals("f", data.get("e"));
+
+ data.clear();
+ data.put("a", "A");
+ data.put("c", "C");
+ data.put("x", "y");
+
+ assertEquals(3, data.size());
+ assertEquals("A", data.get("a"));
+ assertEquals("C", data.get("c"));
+ assertEquals("y", data.get("x"));
+ }
+
+ public void testValueList() {
+ data.parse("a=1&a=2&a=3");
+
+ assertEquals(data.size(), 1);
+ assertEquals(data.getAll("a").size(), 3);
+ assertEquals(data.getAll("a").get(0), "1");
+ assertEquals(data.getAll("a").get(1), "2");
+ assertEquals(data.getAll("a").get(2), "3");
+
+ data.parse("a=b&c=d&c=d&a=1");
+
+ assertEquals(data.size(), 2);
+ assertEquals(data.getAll("a").size(), 2);
+ assertEquals(data.getAll("a").get(0), "b");
+ assertEquals(data.getAll("a").get(1), "1");
+ assertEquals(data.getAll("c").size(), 2);
+ assertEquals(data.getAll("c").get(0), "d");
+ assertEquals(data.getAll("c").get(1), "d");
+
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/PathParserTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/PathParserTest.java
new file mode 100644
index 00000000..4ae4d606
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/PathParserTest.java
@@ -0,0 +1,97 @@
+package org.simpleframework.http.parse;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.http.parse.PathParser;
+
+public class PathParserTest extends TestCase {
+
+ private PathParser path;
+
+ protected void setUp() {
+ path = new PathParser();
+ }
+
+ public void testEmpty() {
+ assertEquals(null, path.getPath());
+ assertEquals(null, path.getExtension());
+ assertEquals(null, path.getName());
+ }
+
+ public void testSegments() {
+ path.parse("/a/b/c/d");
+
+ String[] list = path.getSegments();
+
+ assertEquals("a", list[0]);
+ assertEquals("b", list[1]);
+ assertEquals("c", list[2]);
+ assertEquals("d", list[3]);
+ }
+
+ public void testSubPath() {
+ path.parse("/0/1/2/3/4/5/6/index.html");
+
+ testSubPath(1);
+ testSubPath(2);
+ testSubPath(3);
+ testSubPath(4);
+ testSubPath(5);
+ testSubPath(6);
+ testSubPath(7);
+
+ testSubPath(0,4);
+ testSubPath(1,2);
+ testSubPath(2,3);
+ testSubPath(3,4);
+ testSubPath(1,3);
+ testSubPath(1,4);
+ testSubPath(1,5);
+
+ path.parse("/a/b/c/d/e/index.html");
+
+ testSubPath(1,2);
+ testSubPath(2,3);
+ testSubPath(3,1);
+ testSubPath(1,3);
+ }
+
+ private void testSubPath(int from) {
+ System.err.printf("[%s] %s: %s%n", path, from, path.getPath(from));
+ }
+
+ private void testSubPath(int from, int to) {
+ System.err.printf("[%s] %s, %s: %s%n", path, from, to, path.getPath(from, to));
+ }
+
+ public void testDirectory() {
+ path.parse("/some/directory/path/index.html");
+ assertEquals("/some/directory/path/", path.getDirectory());
+
+ path.parse("/some/path/README");
+ assertEquals("/some/path/", path.getDirectory());
+ }
+
+ public void testNormalization() {
+ path.parse("/path/./../index.html");
+ assertEquals("/", path.getDirectory());
+
+ path.parse("/path/hidden/./index.html");
+ assertEquals("/path/hidden/", path.getDirectory());
+
+ path.parse("/path/README");
+ assertEquals("/path/", path.getDirectory());
+ }
+
+ public void testString() {
+ path.parse("/some/path/../path/./to//a/file.txt");
+ assertEquals("/some/path/to//a/file.txt", path.toString());
+ }
+
+ public void testAIOB(){
+ path.parse("/admin/ws");
+ String result = path.getRelative("/admin/ws/");
+ String expResult = null;
+ assertEquals(expResult, result);
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/PriorityQueueTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/PriorityQueueTest.java
new file mode 100644
index 00000000..5c03ebd7
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/PriorityQueueTest.java
@@ -0,0 +1,48 @@
+package org.simpleframework.http.parse;
+
+import java.util.PriorityQueue;
+
+import junit.framework.TestCase;
+
+public class PriorityQueueTest extends TestCase {
+
+
+ private static class Entry implements Comparable<Entry> {
+
+ private final String text;
+ private final int priority;
+ private final int start;
+
+ public Entry(String text, int priority, int start) {
+ this.priority = priority;
+ this.start = start;
+ this.text = text;
+ }
+
+ public int compareTo(Entry entry) {
+ int value = entry.priority - priority;
+
+ if(value == 0) {
+ return entry.start - start;
+ }
+ return value;
+ }
+ }
+ public void testPriorityQueue() {
+ PriorityQueue<Entry> queue = new PriorityQueue<Entry>();
+ int start = 10000;
+
+ queue.offer(new Entry("a", 10, start--));
+ queue.offer(new Entry("b", 10, start--));
+ queue.offer(new Entry("c", 10, start--));
+ queue.offer(new Entry("d", 10, start--));
+ queue.offer(new Entry("e", 20, start--));
+ queue.offer(new Entry("f", 30, start--));
+ queue.offer(new Entry("g", 20, start--));
+
+ while(!queue.isEmpty()) {
+ System.err.println(queue.remove().text);
+ }
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/parse/QueryParserTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/parse/QueryParserTest.java
new file mode 100644
index 00000000..aae92ff7
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/parse/QueryParserTest.java
@@ -0,0 +1,69 @@
+package org.simpleframework.http.parse;
+
+import org.simpleframework.http.parse.QueryParser;
+
+import junit.framework.TestCase;
+
+public class QueryParserTest extends TestCase {
+
+ private QueryParser data;
+
+ protected void setUp() {
+ data = new QueryParser();
+ }
+
+ public void testEmptyPath() {
+ assertEquals(0, data.size());
+ }
+
+ public void testValue() {
+ data.parse("a=");
+
+ assertEquals(1, data.size());
+ assertEquals("", data.get("a"));
+
+ data.parse("a=&b=c");
+
+ assertEquals(2, data.size());
+ assertEquals("", data.get("a"));
+ assertEquals("c", data.get("b"));
+
+ data.parse("a=b&c=d&e=f&");
+
+ assertEquals(3, data.size());
+ assertEquals("b", data.get("a"));
+ assertEquals("d", data.get("c"));
+ assertEquals("f", data.get("e"));
+
+ data.clear();
+ data.put("a", "A");
+ data.put("c", "C");
+ data.put("x", "y");
+
+ assertEquals(3, data.size());
+ assertEquals("A", data.get("a"));
+ assertEquals("C", data.get("c"));
+ assertEquals("y", data.get("x"));
+ }
+
+ public void testValueList() {
+ data.parse("a=1&a=2&a=3");
+
+ assertEquals(data.size(), 1);
+ assertEquals(data.getAll("a").size(), 3);
+ assertEquals(data.getAll("a").get(0), "1");
+ assertEquals(data.getAll("a").get(1), "2");
+ assertEquals(data.getAll("a").get(2), "3");
+
+ data.parse("a=b&c=d&c=d&a=1");
+
+ assertEquals(data.size(), 2);
+ assertEquals(data.getAll("a").size(), 2);
+ assertEquals(data.getAll("a").get(0), "b");
+ assertEquals(data.getAll("a").get(1), "1");
+ assertEquals(data.getAll("c").size(), 2);
+ assertEquals(data.getAll("c").get(0), "d");
+ assertEquals(data.getAll("c").get(1), "d");
+
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebFrameTypeTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebFrameTypeTest.java
new file mode 100644
index 00000000..037627d2
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebFrameTypeTest.java
@@ -0,0 +1,29 @@
+package org.simpleframework.http.socket;
+
+import junit.framework.TestCase;
+
+public class WebFrameTypeTest extends TestCase {
+
+ public void testFrameType() throws Exception {
+ System.err.println(Integer.toBinaryString(129)); // TEXT FRAME
+ System.err.println(Integer.toBinaryString(128));
+ System.err.println(Integer.toBinaryString(129 >>> 4));
+ System.err.println(Integer.toBinaryString(0x01)); // TEXT
+ System.err.println(Integer.toBinaryString(0x02)); // BINARY
+ System.err.println(Integer.toBinaryString(0x03));
+ System.err.println(Integer.toBinaryString(0x01 % 0x80)); // TEXT
+ System.err.println(Integer.toBinaryString(0x02 % 0x80)); // BINARY
+ System.err.println(Integer.toBinaryString(0x03 % 0x80));
+ System.err.println(Integer.toBinaryString(0x80)); // FIN
+
+ int b0 = 0;
+ if (true) {
+ b0 |= 1 << 7;
+ }
+ b0 |= 0x02 % 128;
+
+
+ System.err.println(Integer.toBinaryString(b0));
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketAnalyzer.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketAnalyzer.java
new file mode 100644
index 00000000..3e896525
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketAnalyzer.java
@@ -0,0 +1,66 @@
+package org.simpleframework.http.socket;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.channels.SelectableChannel;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.simpleframework.transport.trace.TraceAnalyzer;
+import org.simpleframework.transport.trace.Trace;
+
+public class WebSocketAnalyzer implements TraceAnalyzer {
+
+ private final Map<SelectableChannel, Integer> map;
+ private final AtomicInteger count;
+ private final boolean debug;
+
+ public WebSocketAnalyzer() {
+ this(true);
+ }
+
+ public WebSocketAnalyzer(boolean debug) {
+ this.map = new ConcurrentHashMap<SelectableChannel, Integer>();
+ this.count = new AtomicInteger();
+ this.debug = debug;
+ }
+
+ public Trace attach(SelectableChannel channel) {
+ if(map.containsKey(channel)) {
+ throw new IllegalStateException("Can't attach twice");
+ }
+ final int counter = count.getAndIncrement();
+ map.put(channel, counter);
+
+ return new Trace() {
+
+ public void trace(Object event) {
+ if(debug) {
+ trace(event, "");
+ }
+ }
+
+ public void trace(Object event, Object value) {
+ if(debug) {
+ if(value instanceof Throwable) {
+ StringWriter writer = new StringWriter();
+ PrintWriter out = new PrintWriter(writer);
+ ((Exception)value).printStackTrace(out);
+ out.flush();
+ value = writer.toString();
+ }
+ if(value != null && !String.valueOf(value).isEmpty()) {
+ System.err.printf("(%s) [%s] %s: %s%n", Thread.currentThread().getName(), counter, event, value);
+ } else {
+ System.err.printf("(%s) [%s] %s%n", Thread.currentThread().getName(), counter, event);
+ }
+ }
+ }
+ };
+ }
+
+ public void stop() {
+ System.err.println("Stop agent");
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketCertificate.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketCertificate.java
new file mode 100644
index 00000000..99018e39
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketCertificate.java
@@ -0,0 +1,170 @@
+package org.simpleframework.http.socket;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.X509TrustManager;
+
+public class WebSocketCertificate {
+
+ private final X509TrustManager trustManager;
+ private final X509TrustManager[] trustManagers;
+ private final KeyStoreReader keyStoreReader;
+ private final SecureProtocol secureProtocol;
+
+ public WebSocketCertificate(KeyStoreReader keyStoreReader, SecureProtocol secureProtocol) {
+ this.trustManager = new AnonymousTrustManager();
+ this.trustManagers = new X509TrustManager[] { trustManager };
+ this.keyStoreReader = keyStoreReader;
+ this.secureProtocol = secureProtocol;
+ }
+
+ public WebSocketCertificate(KeyStoreReader keyStoreReader, SecureProtocol secureProtocol, X509TrustManager trustManager) {
+ this.trustManagers = new X509TrustManager[] { trustManager };
+ this.keyStoreReader = keyStoreReader;
+ this.secureProtocol = secureProtocol;
+ this.trustManager = trustManager;
+ }
+
+ public SSLContext getContext() throws Exception {
+ KeyManager[] keyManagers = keyStoreReader.getKeyManagers();
+ SSLContext secureContext = secureProtocol.getContext();
+
+ secureContext.init(keyManagers, trustManagers, null);
+
+ return secureContext;
+ }
+
+ public SSLSocketFactory getSocketFactory() throws Exception {
+ KeyManager[] keyManagers = keyStoreReader.getKeyManagers();
+ SSLContext secureContext = secureProtocol.getContext();
+
+ secureContext.init(keyManagers, trustManagers, null);
+
+ return secureContext.getSocketFactory();
+ }
+
+ public SSLServerSocketFactory getServerSocketFactory() throws Exception {
+ KeyManager[] keyManagers = keyStoreReader.getKeyManagers();
+ SSLContext secureContext = secureProtocol.getContext();
+
+ secureContext.init(keyManagers, trustManagers, null);
+
+ return secureContext.getServerSocketFactory();
+ }
+
+ public static enum SecureProtocol {
+ DEFAULT("Default"),
+ SSL("SSL"),
+ TLS("TLS");
+
+ private final String protocol;
+
+ private SecureProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public SSLContext getContext() throws NoSuchAlgorithmException {
+ return SSLContext.getInstance(protocol);
+ }
+ }
+
+ public static enum KeyStoreType {
+ JKS("JKS", "SunX509"),
+ PKCS12("PKCS12", "SunX509");
+
+ private final String algorithm;
+ private final String type;
+
+ private KeyStoreType(String type, String algorithm) {
+ this.algorithm = algorithm;
+ this.type = type;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public KeyStore getKeyStore() throws KeyStoreException {
+ return KeyStore.getInstance(type);
+ }
+
+ public KeyManagerFactory getKeyManagerFactory() throws NoSuchAlgorithmException {
+ return KeyManagerFactory.getInstance(algorithm);
+ }
+ }
+
+ public static class KeyStoreReader {
+
+ private final KeyStoreManager keyStoreManager;
+ private final String keyManagerPassword;
+ private final String keyStorePassword;
+ private final File keyStore;
+
+ public KeyStoreReader(KeyStoreType keyStoreType, File keyStore, String keyStorePassword, String keyManagerPassword) {
+ this.keyStoreManager = new KeyStoreManager(keyStoreType);
+ this.keyManagerPassword = keyManagerPassword;
+ this.keyStorePassword = keyStorePassword;
+ this.keyStore = keyStore;
+ }
+
+ public KeyManager[] getKeyManagers() throws Exception {
+ InputStream storeSource = new FileInputStream(keyStore);
+
+ try {
+ return keyStoreManager.getKeyManagers(storeSource, keyStorePassword, keyManagerPassword);
+ } finally {
+ storeSource.close();
+ }
+ }
+ }
+
+ public static class KeyStoreManager {
+
+ private final KeyStoreType keyStoreType;
+
+ public KeyStoreManager(KeyStoreType keyStoreType) {
+ this.keyStoreType = keyStoreType;
+ }
+
+ public KeyManager[] getKeyManagers(InputStream keyStoreSource, String keyStorePassword, String keyManagerPassword) throws Exception {
+ KeyStore keyStore = keyStoreType.getKeyStore();
+ KeyManagerFactory keyManagerFactory = keyStoreType.getKeyManagerFactory();
+
+ keyStore.load(keyStoreSource, keyManagerPassword.toCharArray());
+ keyManagerFactory.init(keyStore, keyManagerPassword.toCharArray());
+
+ return keyManagerFactory.getKeyManagers();
+ }
+ }
+
+ public static class AnonymousTrustManager implements X509TrustManager {
+
+ public boolean isClientTrusted(X509Certificate[] cert) {
+ return true;
+ }
+
+ public boolean isServerTrusted(X509Certificate[] cert) {
+ return true;
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+
+ public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
+
+ public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatApplication.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatApplication.java
new file mode 100644
index 00000000..7d27b9f3
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatApplication.java
@@ -0,0 +1,170 @@
+package org.simpleframework.http.socket;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Map;
+
+import javax.net.ssl.SSLContext;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.Status;
+import org.simpleframework.http.core.Container;
+import org.simpleframework.http.core.ContainerTransportProcessor;
+import org.simpleframework.http.socket.service.Router;
+import org.simpleframework.http.socket.service.RouterContainer;
+import org.simpleframework.http.socket.service.DirectRouter;
+import org.simpleframework.transport.TransportProcessor;
+import org.simpleframework.transport.TransportSocketProcessor;
+import org.simpleframework.transport.SocketProcessor;
+import org.simpleframework.transport.Transport;
+import org.simpleframework.transport.connect.Connection;
+import org.simpleframework.transport.connect.SocketConnection;
+import org.simpleframework.transport.trace.TraceAnalyzer;
+
+public class WebSocketChatApplication implements Container, TransportProcessor {
+
+ private final WebSocketCertificate certificate;
+ private final Router negotiator;
+ private final RouterContainer container;
+ private final SocketAddress address;
+ private final Connection connection;
+ private final TransportProcessor processor;
+ private final Allocator allocator;
+ private final SocketProcessor server;
+
+ public WebSocketChatApplication(WebSocketChatRoom service, WebSocketCertificate certificate, TraceAnalyzer agent, int port) throws Exception {
+ this.negotiator = new DirectRouter(service);
+ this.container = new RouterContainer(this, negotiator, 10);
+ this.allocator = new ArrayAllocator();
+ this.processor = new ContainerTransportProcessor(container, allocator, 1);
+ this.server = new TransportSocketProcessor(this);
+ this.connection = new SocketConnection(server, agent);
+ this.address = new InetSocketAddress(port);
+ this.certificate = certificate;
+ }
+
+ public void connect() throws Exception {
+ // if(certificate != null) {
+ // SSLContext context = certificate.getContext();
+ //
+ // connection.connect(address, context);
+ // container.start();
+ // } else {
+ connection.connect(address);
+ // }
+ }
+
+ public void handle(Request req, Response resp) {
+ System.err.println(req);
+
+ if(req.getTarget().equals("/")) {
+ long time = System.currentTimeMillis();
+
+ try {
+ resp.setDate("Date", time);
+ resp.setValue("Server", "WebSocketChatApplication/1.0");
+ resp.setContentType("text/html");
+ String page = loadPage("WebSocketChatLogin.html");
+
+ resp.setDate("Date", time);
+ resp.setValue("Server", "WebSocketChatApplication/1.0");
+ resp.setContentType("text/html");
+
+ PrintStream out = resp.getPrintStream();
+ out.println(page);
+ out.close();
+ }catch(Exception e) {
+ e.printStackTrace();
+ }
+ } else if(req.getTarget().equals("/login")) {
+ String user = req.getParameter("user");
+ long time = System.currentTimeMillis();
+
+ try {
+ resp.setStatus(Status.FOUND);
+ resp.setValue("Location", "/chat");
+ resp.setCookie("user", user);
+ resp.setDate("Date", time);
+ resp.setValue("Server", "WebSocketChatApplication/1.0");
+ resp.setContentType("text/html");
+ resp.close();
+ }catch(Exception e) {
+ e.printStackTrace();
+ }
+ } else if(req.getTarget().equals("/chat")) {
+ long time = System.currentTimeMillis();
+
+ try {
+ Cookie user = req.getCookie("user");
+ String name = user.getValue();
+ String page = loadPage("WebSocketChatRoom.html");
+
+ resp.setDate("Date", time);
+ resp.setValue("Server", "WebSocketChatApplication/1.0");
+ resp.setContentType("text/html");
+
+ PrintStream out = resp.getPrintStream();
+ page = page.replaceAll("%1", name);
+ out.println(page);
+ out.close();
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ } else if(req.getTarget().equals("/talk")){
+ long time = System.currentTimeMillis();
+ try {
+ container.handle(req, resp);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ long time = System.currentTimeMillis();
+
+ try {
+ resp.setCode(404);
+ resp.setDescription("Not Found");
+ resp.setDate("Date", time);
+ resp.setValue("Server", "WebSocketChatApplication/1.0");
+ resp.setContentType("text/plain");
+
+ PrintStream out = resp.getPrintStream();
+
+ out.println("Not Found");
+ out.close();
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public String loadPage(String name) throws IOException {
+ InputStream loginPage = WebSocketChatApplication.class.getResourceAsStream(name);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] chunk = new byte[1024];
+ int count = 0;
+
+ while((count = loginPage.read(chunk)) != -1) {
+ out.write(chunk, 0, count);
+ }
+ out.close();
+ return out.toString();
+ }
+
+ public void process(Transport transport) throws IOException {
+ Map map = transport.getAttributes();
+ map.put(Transport.class, transport);
+ processor.process(transport);
+ }
+
+ public void stop() throws IOException {
+ processor.stop();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatLogin.html b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatLogin.html
new file mode 100644
index 00000000..47114949
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatLogin.html
@@ -0,0 +1,12 @@
+ <html>
+ <head>
+ <title>Login Page</title>
+ </head>
+ <body>
+ <h1>Please Login</h1>
+ <form action='/login' method='POST'>
+ <input type='text' name='user'/>
+ <input type='submit' value='Sign In'/>
+ </form>
+ </body>
+</html> \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoom.html b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoom.html
new file mode 100644
index 00000000..1443c691
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoom.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <title>WebSocket Echo Test</title>
+ <script>
+ function init() {
+ websocket = new WebSocket("ws://localhost:6060/talk");
+
+ websocket.onopen = function() { document.getElementById("output").innerHTML += "<p>> CONNECTED</p>"; };
+
+ websocket.onmessage = function(evt) { document.getElementById("output").innerHTML += "<p style='color: blue;'>> RECEIVE: " + evt.data + "</p>"; };
+
+ websocket.onerror = function(evt) { document.getElementById("output").innerHTML += "<p style='color: red;'>> ERROR: " + evt.data + "</p>"; };
+ }
+
+ function sendMessage(message) {
+ document.getElementById("output").innerHTML += "<p>> SEND: " + message + "</p>";
+
+ websocket.send(message);
+ }
+
+ window.addEventListener("load", init, false);
+ </script>
+ </head>
+ <body>
+ <h2>Welcome %1</h2><small>Refresh browser to clear page and resubscribe</small><br/><br/>
+ <input onkeypress="if(this.value) {if (window.event.keyCode == 13) { sendMessage(this.value); this.value = null; }}"/>
+ <div id="output"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoom.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoom.java
new file mode 100644
index 00000000..6ca855a5
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoom.java
@@ -0,0 +1,86 @@
+package org.simpleframework.http.socket;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.socket.WebSocketCertificate.KeyStoreReader;
+import org.simpleframework.http.socket.service.Service;
+import org.simpleframework.transport.trace.TraceAnalyzer;
+
+public class WebSocketChatRoom extends Thread implements Service {
+
+ private final WebSocketChatRoomListener listener;
+ private final Map<String, FrameChannel> sockets;
+ private final Set<String> users;
+
+ public WebSocketChatRoom() {
+ this.listener = new WebSocketChatRoomListener(this);
+ this.sockets = new ConcurrentHashMap<String, FrameChannel>();
+ this.users = new CopyOnWriteArraySet<String>();
+ }
+
+ public void connect(Session connection) {
+ FrameChannel socket = connection.getChannel();
+ Request req = connection.getRequest();
+ Cookie user = req.getCookie("user");
+
+ if(user == null) {
+ user = new Cookie("user", "anonymous");
+ }
+ String name = user.getValue();
+
+ try {
+ socket.register(listener);
+ join(name, socket);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ public void join(String user, FrameChannel operation) {
+ sockets.put(user, operation);
+ users.add(user);
+ }
+
+ public void leave(String user, FrameChannel operation){
+ sockets.put(user, operation);
+ users.add(user);
+ }
+
+ public void distribute(Frame frame) {
+ try {
+ for(String user : users) {
+ FrameChannel operation = sockets.get(user);
+
+ try {
+
+ operation.send(frame);
+ } catch(Exception e){
+ sockets.remove(user);
+ users.remove(user);
+ e.printStackTrace();
+ operation.close();
+ }
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void main(String[] list) throws Exception {
+ TraceAnalyzer agent = new WebSocketAnalyzer();
+ WebSocketChatRoom application = new WebSocketChatRoom();
+ File file = new File("C:\\work\\development\\async_http\\proxy\\yieldbroker-proxy-site\\certificate\\www.yieldbroker.com.pfx");
+ KeyStoreReader reader = new KeyStoreReader(WebSocketCertificate.KeyStoreType.PKCS12, file, "p", "p");
+ WebSocketCertificate certificate = new WebSocketCertificate(reader, WebSocketCertificate.SecureProtocol.TLS);
+ WebSocketChatApplication container = new WebSocketChatApplication(application, certificate, agent, 6060);
+ application.start();
+ container.connect();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoomListener.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoomListener.java
new file mode 100644
index 00000000..e976b780
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketChatRoomListener.java
@@ -0,0 +1,106 @@
+package org.simpleframework.http.socket;
+
+import java.security.Principal;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.security.cert.X509Certificate;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.transport.Certificate;
+
+
+public class WebSocketChatRoomListener implements FrameListener {
+
+ private final CertificateUserExtractor extractor;
+ private final WebSocketChatRoom room;
+
+ public WebSocketChatRoomListener(WebSocketChatRoom room) {
+ this.extractor = new CertificateUserExtractor(".*EMAILADDRESS=(.*?),.*");
+ this.room = room;
+ }
+
+ public void onFrame(Session socket, Frame frame) {
+ FrameType type = frame.getType();
+ String text = frame.getText();
+
+ if(type == FrameType.TEXT){
+ try {
+ Request request = socket.getRequest();
+ String user = extractor.extractUser(request);
+
+ text = text + " (SSL=" + request.isSecure() + ", EMAILADDRESS=" + user + ")";
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ Frame replay = new DataFrame(type, text);
+ room.distribute(replay);
+ }
+ }
+
+ public void onError(Session socket, Exception cause) {
+ System.err.println("onError(");
+ cause.printStackTrace();
+ System.err.println(")");
+ }
+
+ public void onOpen(Session socket) {
+ System.err.println("onOpen(" + socket +")");
+ }
+
+ public void onClose(Session session, Reason reason) {
+ System.err.println("onClose(" + reason +")");
+ }
+
+ public static class CertificateUserExtractor {
+
+ private final Map<String, String> cache;
+ private final Pattern pattern;
+
+ public CertificateUserExtractor(String pattern) {
+ this.cache = new ConcurrentHashMap<String, String>();
+ this.pattern = Pattern.compile(pattern);
+ }
+
+ public String extractUser(Request request) throws Exception {
+ try {
+ Certificate certificate = request.getClientCertificate();
+
+ if(certificate != null) {
+ X509Certificate[] certificates = certificate.getChain();
+
+ for(X509Certificate entry : certificates) {
+ String user = extractCertificateUser(entry);
+
+ if(user != null) {
+ return user;
+ }
+ }
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private String extractCertificateUser(X509Certificate certificate) throws Exception {
+ Principal principal = certificate.getSubjectDN();
+ String name = principal.getName();
+ String user = cache.get(name);
+
+ if(user == null) {
+ if(!cache.containsKey(name)) {
+ Matcher matcher = pattern.matcher(name);
+
+ if(matcher.matches()) {
+ user = matcher.group(1);
+ }
+ cache.put(name, user);
+ }
+ }
+ return user;
+ }
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketKeyTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketKeyTest.java
new file mode 100644
index 00000000..5e6f81e4
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketKeyTest.java
@@ -0,0 +1,37 @@
+package org.simpleframework.http.socket;
+
+import java.security.MessageDigest;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.common.encode.Base64Encoder;
+
+public class WebSocketKeyTest extends TestCase {
+
+ /*
+ From RFC 6455
+
+ Concretely, if as in the example above, the |Sec-WebSocket-Key|
+ header field had the value "dGhlIHNhbXBsZSBub25jZQ==", the server
+ would concatenate the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+ to form the string "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-
+ C5AB0DC85B11". The server would then take the SHA-1 hash of this,
+ giving the value 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6
+ 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea. This value is
+ then base64-encoded (see Section 4 of [RFC4648]), to give the value
+ "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=". This value would then be echoed in
+ the |Sec-WebSocket-Accept| header field.
+ */
+ public void testKey() throws Exception {
+ String key = "dGhlIHNhbXBsZSBub25jZQ==";
+ String result = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ byte[] data = result.getBytes("ISO-8859-1");
+ digest.update(data);
+ byte[] digested = digest.digest();
+ String value = new String(Base64Encoder.encode(digested));
+
+ assertEquals(value, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketTestClient.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketTestClient.java
new file mode 100644
index 00000000..21c4abcb
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/WebSocketTestClient.java
@@ -0,0 +1,25 @@
+package org.simpleframework.http.socket;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+public class WebSocketTestClient {
+
+ public static void main(String[] list) throws Exception {
+ Socket socket = new Socket("localhost", 80);
+ OutputStream out = socket.getOutputStream();
+ byte[] request = ("GET / HTTP/1.0\r\n\r\n").getBytes("ISO-8859-1");
+ out.write(request);
+ InputStream in = socket.getInputStream();
+ byte[] chunk = new byte[1024];
+ int count = 0;
+
+ while((count = in.read(chunk)) != -1) {
+ Thread.sleep(1000);
+ System.err.write(chunk, 0, count);
+ }
+
+
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/service/PathRouterTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/service/PathRouterTest.java
new file mode 100644
index 00000000..95bc6ef1
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/service/PathRouterTest.java
@@ -0,0 +1,62 @@
+package org.simpleframework.http.socket.service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.simpleframework.http.core.MockRequest;
+import org.simpleframework.http.core.MockResponse;
+import org.simpleframework.http.socket.Session;
+
+public class PathRouterTest extends TestCase {
+
+ public static class A implements Service {
+ public void connect(Session session) {}
+ }
+
+ public static class B implements Service {
+ public void connect(Session session) {}
+ }
+
+ public static class C implements Service {
+ public void connect(Session session) {}
+ }
+
+ public void testRouter() throws Exception{
+ Map<String, Service> services = new HashMap<String, Service>();
+
+ services.put("/a", new A());
+ services.put("/b", new B());
+
+ PathRouter router = new PathRouter(services, new C());
+ MockRequest request = new MockRequest();
+ MockResponse response = new MockResponse();
+
+ request.setTarget("/a");
+
+ Service service = router.route(request, response);
+
+ assertNull(service);
+
+ request.setValue("Sec-WebSocket-Version", "13");
+ request.setValue("connection", "upgrade");
+ request.setValue("upgrade", "WebSocket");
+
+ service = router.route(request, response);
+
+ assertNotNull(service);
+ assertEquals(service.getClass(), A.class);
+ assertEquals(response.getValue("Sec-WebSocket-Version"), "13");
+
+ request.setTarget("/c");
+
+ service = router.route(request, response);
+
+ assertNotNull(service);
+ assertEquals(service.getClass(), C.class);
+ assertEquals(response.getValue("Sec-WebSocket-Version"), "13");
+
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/service/WebSocketPerformanceTest.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/service/WebSocketPerformanceTest.java
new file mode 100644
index 00000000..fbabeeb8
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/service/WebSocketPerformanceTest.java
@@ -0,0 +1,439 @@
+package org.simpleframework.http.socket.service;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.channels.SelectableChannel;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.common.thread.Daemon;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.core.Container;
+import org.simpleframework.http.core.ContainerTransportProcessor;
+import org.simpleframework.http.core.StreamCursor;
+import org.simpleframework.http.core.ThreadDumper;
+import org.simpleframework.http.message.ReplyConsumer;
+import org.simpleframework.http.socket.DataFrame;
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameListener;
+import org.simpleframework.http.socket.FrameType;
+import org.simpleframework.http.socket.Reason;
+import org.simpleframework.http.socket.Session;
+import org.simpleframework.http.socket.FrameChannel;
+import org.simpleframework.http.socket.WebSocketAnalyzer;
+import org.simpleframework.transport.TransportProcessor;
+import org.simpleframework.transport.TransportSocketProcessor;
+import org.simpleframework.transport.SocketProcessor;
+import org.simpleframework.transport.connect.Connection;
+import org.simpleframework.transport.connect.SocketConnection;
+import org.simpleframework.transport.trace.TraceAnalyzer;
+import org.simpleframework.transport.trace.Trace;
+
+public class WebSocketPerformanceTest {
+
+ public static class MessageCounter implements FrameListener {
+
+ private final AtomicInteger counter;
+
+ public MessageCounter(AtomicInteger counter) {
+ this.counter = counter;
+ }
+
+ public void onFrame(Session socket, Frame frame) {
+ counter.getAndIncrement();
+ }
+
+ public void onError(Session socket, Exception cause) {
+ System.err.println("onError(");
+ cause.printStackTrace();
+ System.err.println(")");
+ }
+
+ public void onOpen(Session socket) {
+ System.err.println("onOpen(" + socket +")");
+ }
+
+ public void onClose(Session session, Reason reason) {
+ System.err.println("onClose(" + reason +")");
+ }
+ }
+
+ public static class MessageGeneratorService extends Thread implements Service {
+
+ private static final String MESSAGE =
+ "{'product': {\r\n"+
+ " 'id': '1234',\r\n"+
+ " 'name': 'AU3TB00001256',\r\n"+
+ " 'values': {\r\n"+
+ " 'best': [\r\n"+
+ " {'bid': '13.344'},\r\n"+
+ " {'offer': '12.1'},\r\n"+
+ " {'volume': '100000'}\r\n"+
+ " ]\r\n"+
+ " }\r\n"+
+ "}}";
+
+ private final Set<FrameChannel> sockets;
+ private final MessageCounter listener;
+ private final AtomicInteger counter;
+ private final AtomicBoolean begin;
+
+ public MessageGeneratorService() {
+ this.sockets = new CopyOnWriteArraySet<FrameChannel>();
+ this.counter = new AtomicInteger();
+ this.listener = new MessageCounter(counter);
+ this.begin = new AtomicBoolean();
+ }
+
+ public void begin() {
+ if(begin.compareAndSet(false, true)) {
+ start();
+ }
+ }
+
+ public void connect(Session connection) {
+ FrameChannel socket = connection.getChannel();
+
+ try {
+ sockets.add(socket);
+ socket.register(listener);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void distribute(Frame frame) {
+ try {
+ for(FrameChannel socket : sockets) {
+ try {
+ socket.send(frame);
+ } catch(Exception e){
+ e.printStackTrace();
+ sockets.remove(socket);
+ socket.close();
+ }
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void run() {
+ try {
+ for(int i = 0; i < 10000000; i++) {
+ distribute(new DataFrame(FrameType.TEXT, System.currentTimeMillis() + ":" + MESSAGE));
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static class MessageGeneratorContainer implements Container {
+
+ private final RouterContainer container;
+ private final SocketAddress address;
+ private final Connection connection;
+ private final Allocator allocator;
+ private final TransportProcessor processor;
+ private final Router negotiator;
+ private final SocketProcessor server;
+
+ public MessageGeneratorContainer(MessageGeneratorService service, TraceAnalyzer agent, int port) throws Exception {
+ this.negotiator = new DirectRouter(service);
+ this.container = new RouterContainer(this, negotiator, 10, 100000);
+ this.allocator = new ArrayAllocator();
+ this.processor = new ContainerTransportProcessor(container, allocator, 10);
+ this.server = new TransportSocketProcessor(processor, 10, 8192*10);
+ this.connection = new SocketConnection(server, agent);
+ this.address = new InetSocketAddress(port);
+ }
+
+ public void connect() throws Exception {
+ connection.connect(address);
+ }
+
+ public void handle(Request req, Response resp) {
+ long time = System.currentTimeMillis();
+
+ System.err.println(req);
+
+ try {
+ PrintStream out = resp.getPrintStream();
+
+ resp.setDate("Date", time);
+ resp.setValue("Server", "MessageGeneratorContainer/1.0");
+ resp.setContentType("text/plain");
+ resp.setDate("Date", time);
+ resp.setValue("Server", "MessageGeneratorContainer/1.0");
+ resp.setContentType("text/html");
+
+ out.println("Your request is invalid as this is a websocket test!!");
+ out.close();
+ }catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static class MessageGeneratorClient extends Thread {
+
+ private final MessageGeneratorService service;
+ private final AtomicInteger counter;
+ private final AtomicLong duration;
+ private final int port;
+ private final boolean debug;
+
+ public MessageGeneratorClient(MessageGeneratorService service, AtomicInteger counter, AtomicLong duration, int port, boolean debug) {
+ this.duration = duration;
+ this.counter = counter;
+ this.service = service;
+ this.debug = debug;
+ this.port = port;
+ }
+
+ public void run() {
+ try {
+ Socket socket = new Socket("localhost", port);
+ StreamCursor cursor = new StreamCursor(socket.getInputStream());
+ FrameConsumer consumer = new FrameConsumer();
+ ReplyConsumer response = new ReplyConsumer();
+
+ byte[] request = ("GET /chat HTTP/1.1\r\n"+
+ "Host: server.example.com\r\n"+
+ "Upgrade: websocket\r\n"+
+ "Connection: Upgrade\r\n"+
+ "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"+
+ "Sec-WebSocket-Protocol: chat, superchat\r\n"+
+ "Sec-WebSocket-Version: 13\r\n"+
+ "Origin: http://example.com\r\n" +
+ "\r\n").getBytes("ISO-8859-1");
+
+ socket.getOutputStream().write(request);
+
+ while(cursor.isOpen()) {
+ response.consume(cursor);
+
+ if(response.isFinished()) {
+ System.err.println(response);
+ break;
+ }
+ }
+ service.begin();
+
+ while(cursor.isOpen()) {
+ consumer.consume(cursor);
+
+ if(consumer.isFinished()) {
+ Frame frame = consumer.getFrame();
+
+ if(frame != null) {
+ validate(frame);
+ }
+ consumer.clear();
+ }
+
+ if(!cursor.isReady()) { // wait for it to fill
+ Thread.sleep(1);
+ }
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void validate(Frame frame) throws Exception {
+ FrameType type = frame.getType();
+
+ if(type == FrameType.TEXT) {
+ String text = frame.getText();
+ int index = text.indexOf(':');
+ String time = text.substring(0, index);
+ long sendTime = Long.parseLong(time);
+ long timeElapsed = System.currentTimeMillis() - sendTime;
+
+ duration.getAndAdd(timeElapsed);
+ counter.getAndIncrement();
+
+ if(debug) {
+ System.err.println("count=" + counter + " text="+text + " time="+duration);
+ }
+ }
+ }
+ }
+
+ public static class MessageTimer extends Thread {
+
+ private final AtomicLong duration;
+ private final AtomicInteger counter;
+
+ public MessageTimer(AtomicInteger counter, AtomicLong duration) {
+ this.duration = duration;
+ this.counter = counter;
+ }
+
+ public void run() {
+ while(true) {
+ try {
+ Thread.sleep(1000);
+ int count = counter.getAndSet(0);
+ long total = duration.getAndSet(0);
+ long average = (total > 0 ? total : 1) / (count > 0 ? count : 1);
+
+ System.err.println("framesPerSecond="+count+" millisPerFrame="+average);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public static class ConsoleAnalyzer extends Daemon implements TraceAnalyzer {
+
+ private final Queue<TraceRecord> queue;
+ private final AtomicLong count;
+ private final String filter;
+
+ public ConsoleAnalyzer() {
+ this(null);
+ }
+
+ public ConsoleAnalyzer(String filter) {
+ this.queue = new ConcurrentLinkedQueue<TraceRecord>();
+ this.count = new AtomicLong();
+ this.filter = filter;
+ }
+
+ public Trace attach(SelectableChannel channel) {
+ return new TraceFeeder(channel);
+ }
+
+ public void run() {
+ try {
+ while(isActive()) {
+ Thread.sleep(1000);
+
+ while(!queue.isEmpty()) {
+ TraceRecord record = queue.poll();
+
+ if(filter != null) {
+ Object event = record.event;
+ Class type = event.getClass();
+ String name = type.getName();
+
+ if(name.contains(filter)) {
+ System.err.println(record);
+ }
+ } else {
+ System.err.println(record);
+ }
+ }
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private class TraceFeeder implements Trace {
+
+ private final SelectableChannel channel;
+ private final long sequence;
+
+ public TraceFeeder(SelectableChannel channel) {
+ this.sequence = count.getAndIncrement();
+ this.channel = channel;
+ }
+
+ public void trace(Object event) {
+ trace(event, null);
+ }
+
+ public void trace(Object event, Object value) {
+ TraceRecord record = new TraceRecord(channel, event, value, sequence);
+
+ if(isActive()) {
+ queue.offer(record);
+ }
+ }
+
+ }
+
+ private class TraceRecord {
+
+ private final SelectableChannel channel;
+ private final String thread;
+ private final Object event;
+ private final Object value;
+ private final long sequence;
+
+ public TraceRecord(SelectableChannel channel, Object event, Object value, long sequence) {
+ this.thread = Thread.currentThread().getName();
+ this.sequence = sequence;
+ this.channel = channel;
+ this.event = event;
+ this.value = value;
+ }
+
+ public String toString() {
+ StringWriter builder = new StringWriter();
+ PrintWriter writer = new PrintWriter(builder);
+
+ writer.print(sequence);
+ writer.print(" [");
+ writer.print(channel);
+ writer.print("]");
+ writer.print(" ");
+ writer.print(thread);
+ writer.print(": ");
+ writer.print(event);
+
+ if(value != null) {
+ if(value instanceof Throwable) {
+ ((Throwable)value).printStackTrace(writer);
+ } else {
+ writer.print(" -> ");
+ writer.print(value);
+ }
+ }
+ writer.close();
+ return builder.toString();
+ }
+ }
+
+ }
+
+
+ public static void main(String[] list) throws Exception {
+ ThreadDumper dumper = new ThreadDumper();
+ ConsoleAnalyzer agent = new ConsoleAnalyzer();
+ AtomicLong duration = new AtomicLong();
+ AtomicInteger counter = new AtomicInteger();
+ MessageGeneratorService service = new MessageGeneratorService();
+ MessageGeneratorContainer container = new MessageGeneratorContainer(service, agent, 7070);
+ MessageTimer timer = new MessageTimer(counter, duration);
+
+ //agent.start();
+ dumper.start();
+ timer.start();
+ container.connect();
+
+ for(int i = 0; i < 100; i++) {
+ MessageGeneratorClient client = new MessageGeneratorClient(service, counter, duration, 7070, false);
+ client.start();
+ }
+ }
+}
+ \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTable.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTable.java
new file mode 100644
index 00000000..d6cd4011
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTable.java
@@ -0,0 +1,151 @@
+package org.simpleframework.http.socket.table;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class WebSocketTable {
+
+ private final List<WebSocketTableRow> rows;
+ private final WebSocketTableRowAnnotator annotator;
+ private final WebSocketTableSchema schema;
+ private final String key;
+
+ public WebSocketTable(String key, WebSocketTableSchema schema, WebSocketTableRowAnnotator annotator) {
+ this.rows = new LinkedList<WebSocketTableRow>();
+ this.annotator = annotator;
+ this.schema = schema;
+ this.key = key;
+ }
+
+ public String getKey(){
+ return key;
+ }
+
+ public WebSocketTableSchema getSchema(){
+ return schema;
+ }
+
+ public int getRows(){
+ return rows.size();
+ }
+
+ public WebSocketTableRow updateRow(int index, String value) {
+ Map<String, String> map = new HashMap<String, String>();
+ String[] cells = value.split(",");
+ for(String cell : cells){
+ String[] pair = cell.split("=");
+ map.put(pair[0], pair[1]);
+ }
+ return updateRow(index, map);
+
+ }
+
+ public WebSocketTableRow updateRow(int index, Map<String, String> data) {
+ WebSocketTableRow row = rows.get(index);
+ if(row != null) {
+ Set<String> columns = data.keySet();
+ for(String column : columns) {
+ if(!schema.validColumn(column)) {
+ throw new IllegalArgumentException("Schema does not match row " + data);
+ }
+
+ }
+ for(String column : columns){
+ String value = data.get(column);
+ WebSocketTableCell tableCell = row.getValue(column);
+
+ if(tableCell == null) {
+ row.setValue(column, value);
+ } else {
+ if(!tableCell.getValue().equals(value)) {
+ row.setValue(column, value);
+ }
+ }
+ }
+ }
+ return row;
+ }
+
+ public WebSocketTableRow addRow(String value) {
+ Map<String, String> map = new HashMap<String, String>();
+ String[] cells = value.split(",");
+ for(String cell : cells){
+ String[] pair = cell.split("=");
+ map.put(pair[0], pair[1]);
+ }
+ return addRow(map);
+
+ }
+
+ public WebSocketTableRow addRow(Map<String, String> data) {
+ Set<String> columns = data.keySet();
+ for(String column : columns) {
+ if(!schema.validColumn(column)) {
+ throw new IllegalArgumentException("Schema does not match row " + data);
+ }
+ }
+ int length = rows.size();
+ WebSocketTableRow row = new WebSocketTableRow(schema, length);
+ for(String column : columns){
+ String value = data.get(column);
+ row.setValue(column, value);
+ }
+ rows.add(row);
+ return row;
+ }
+
+ public WebSocketTableRow getRow(int row) {
+ int size = rows.size();
+
+ if(row < size) {
+ return rows.get(row);
+ }
+ return null;
+ }
+
+ public String calculateHighlight(long since) {
+ StringBuilder builder = new StringBuilder();
+ String delim = "";
+ int size = rows.size();
+
+ for(int i = 0; i < size; i++) {
+ WebSocketTableRow row = rows.get(i);
+ long time = since;
+ String text = annotator.calculateHighlight(row, time);
+
+ if(text != null && text.length() > 0) {
+ builder.append(delim);
+ builder.append(text);
+ delim = "|";
+ }
+ }
+ return builder.toString();
+ }
+
+ public String calculateChange(long since) {
+ StringBuilder builder = new StringBuilder();
+ String delim = "";
+ int size = rows.size();
+
+ for(int i = 0; i < size; i++) {
+ WebSocketTableRow row = rows.get(i);
+ long time = since;
+ String text = row.calculateChange(time);
+
+ if(text != null && text.length() > 0) {
+ builder.append(delim);
+ builder.append(text);
+ delim = "|";
+ }
+ }
+ return builder.toString();
+ }
+
+ public void clearTable() {
+ rows.clear();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableCell.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableCell.java
new file mode 100644
index 00000000..24dfbecd
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableCell.java
@@ -0,0 +1,26 @@
+package org.simpleframework.http.socket.table;
+
+public class WebSocketTableCell {
+
+ private final long timeStamp;
+ private final String column;
+ private final String value;
+
+ public WebSocketTableCell(String column, String value) {
+ this.timeStamp = System.currentTimeMillis();
+ this.value = value;
+ this.column = column;
+ }
+
+ public long getTimeStamp(){
+ return timeStamp;
+ }
+
+ public String getValue(){
+ return value;
+ }
+
+ public String getColumn(){
+ return column;
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableChanger.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableChanger.java
new file mode 100644
index 00000000..5a77537d
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableChanger.java
@@ -0,0 +1,58 @@
+package org.simpleframework.http.socket.table;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class WebSocketTableChanger {
+
+ private final Map<String, Integer> currentRows;
+ private final WebSocketValueEncoder encoder;
+ private final WebSocketTable table;
+
+ public WebSocketTableChanger(WebSocketTable table) {
+ this.currentRows = new ConcurrentHashMap<String, Integer>();
+ this.encoder = new WebSocketValueEncoder();
+ this.table = table;
+ }
+
+ public void onChange(Map<String, Object> values) {
+ Map<String, String> row = new HashMap<String, String>();
+ Map<String, String> header = new HashMap<String, String>();
+ Set<String> columns = values.keySet();
+
+ for (String column : columns) {
+ Object value = values.get(column);
+ String encoded = encoder.encode(value);
+
+ row.put(column, encoded);
+ header.put(column, column);
+ }
+ WebSocketTableRow headerRow = table.getRow(0);
+
+ if (headerRow == null) {
+ table.addRow(header);
+ } else {
+ for (String column : columns) {
+ String name = header.get(column);
+ headerRow.setValue(column, name);
+ }
+ }
+ String key = table.getKey();
+ Object keyAttribute = values.get(key);
+
+ if (keyAttribute != null) {
+ String tableKey = String.valueOf(keyAttribute);
+ Integer index = currentRows.get(tableKey);
+
+ if (index == null) {
+ WebSocketTableRow newRow = table.addRow(row);
+ index = newRow.getIndex();
+ currentRows.put(tableKey, index);
+ } else {
+ table.updateRow(index, row);
+ }
+ }
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableColumn.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableColumn.java
new file mode 100644
index 00000000..966d92ff
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableColumn.java
@@ -0,0 +1,22 @@
+package org.simpleframework.http.socket.table;
+
+public enum WebSocketTableColumn {
+ BID_OUTRIGHT_VOLUME("bov", "Bid Outright Volume"),
+ OFFER_OUTRIGHT_VOLUME("bov", "Offer Outright Volume"),
+ BID_OUTRIGHT("bo", "Bid Outright"),
+ OFFER_OUTRIGHT("bov", "Offer Outright"),
+ BID_EFP_VOLUME("bov", "Bid EFP Volume"),
+ OFFER_EFP_VOLUME("bov", "Offer EFP Volume"),
+ BID_EFP("bo", "Bid EFP"),
+ OFFER_EFP("bov", "Offer EFP"),
+ PRODUCT("p", "Product");
+
+ public final String name;
+ public final String title;
+
+ private WebSocketTableColumn(String name, String title) {
+ this.name = name;
+ this.title = title;
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableColumnStyle.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableColumnStyle.java
new file mode 100644
index 00000000..f514295f
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableColumnStyle.java
@@ -0,0 +1,35 @@
+package org.simpleframework.http.socket.table;
+
+public class WebSocketTableColumnStyle {
+
+ private final String template;
+ private final String caption;
+ private final String name;
+ private final boolean sortable;
+ private final boolean resizable;
+
+ public WebSocketTableColumnStyle(String name, String caption, String template, boolean resizable, boolean sortable) {
+ this.name = name;
+ this.caption = caption;
+ this.template = template;
+ this.resizable = resizable;
+ this.sortable = sortable;
+ }
+
+ public String createStyle() {
+ StringBuilder builder = new StringBuilder();
+ WebSocketValueEncoder encoder = new WebSocketValueEncoder();
+
+ builder.append(name);
+ builder.append(",");
+ builder.append(encoder.encode(caption));
+ builder.append(",");
+ builder.append(encoder.encode(template));
+ builder.append(",");
+ builder.append(resizable);
+ builder.append(",");
+ builder.append(sortable);
+
+ return builder.toString();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableListener.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableListener.java
new file mode 100644
index 00000000..d099f27a
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableListener.java
@@ -0,0 +1,69 @@
+package org.simpleframework.http.socket.table;
+
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameListener;
+import org.simpleframework.http.socket.FrameType;
+import org.simpleframework.http.socket.Reason;
+import org.simpleframework.http.socket.Session;
+
+public class WebSocketTableListener implements FrameListener {
+
+ private final WebSocketTableUpdater updater;
+
+ public WebSocketTableListener(WebSocketTableUpdater updater) {
+ this.updater = updater;
+ }
+
+ public void onFrame(Session socket, Frame frame) {
+ FrameType type = frame.getType();
+
+ if(type != FrameType.PONG && type != FrameType.PING) {
+
+ if(type == FrameType.TEXT) {
+ String text = frame.getText();
+ String[] command = text.split(":");
+ String operation = command[0];
+ String parameters = command[1];
+ String[] values = parameters.split(",");
+
+ if(values.length > 0) {
+ for(String value : values) {
+ String[] pair = value.split("=");
+
+ if(operation.equals("refresh")) {
+ updater.refresh(socket);
+ }else if(operation.equals("status")) {
+ System.err.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" + value);
+
+ if(pair[0].equals("sequence")) {
+ if(pair[1].indexOf("@") != -1) {
+ String time = pair[1].split("@")[1];
+ Long sent = Long.parseLong(time);
+
+ System.err.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TIME RTT: " + (System.currentTimeMillis() - sent));
+ }
+ }
+ }
+ }
+ }
+ }
+ System.err.println("onFrame(");
+ System.err.println(frame.getText());
+ System.err.println(")");
+ }
+ }
+
+ public void onError(Session socket, Exception cause) {
+ System.err.println("onError(");
+ cause.printStackTrace();
+ System.err.println(")");
+ }
+
+ public void onOpen(Session socket) {
+ System.err.println("onOpen(" + socket +")");
+ }
+
+ public void onClose(Session session, Reason reason) {
+ System.err.println("onClose(" + reason +" reason="+reason.getText()+" code="+reason.getCode()+")");
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRow.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRow.java
new file mode 100644
index 00000000..bb8a6db5
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRow.java
@@ -0,0 +1,84 @@
+package org.simpleframework.http.socket.table;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class WebSocketTableRow {
+
+ private final Map<String, WebSocketTableCell> cells;
+ private final WebSocketValueEncoder encoder;
+ private final WebSocketTableSchema schema;
+ private final int index;
+
+ public WebSocketTableRow(WebSocketTableSchema schema, int index) {
+ this.cells = new ConcurrentHashMap<String, WebSocketTableCell>();
+ this.encoder = new WebSocketValueEncoder();
+ this.index = index;
+ this.schema = schema;
+ }
+
+ public int getIndex(){
+ return index;
+ }
+
+ public void setValue(String column, String value){
+ WebSocketTableCell cell = getValue(column);
+
+ if(cell == null) {
+ WebSocketTableCell newCell = new WebSocketTableCell(column, value);
+ List<String> columns = schema.columnNames();
+ boolean match = false;
+ for(String name : columns) {
+ if(name.equals(column)) {
+ match = true;
+ }
+ }
+ if(!match) {
+ throw new IllegalStateException("Could not find " + column + " in schema");
+ }
+ cells.put(column, newCell);
+ } else {
+ String previous = cell.getValue();
+
+ if(previous != null && !previous.equals(value)) {
+ WebSocketTableCell replaceCell = new WebSocketTableCell(column, value);
+ cells.put(column, replaceCell);
+ }
+ }
+ }
+
+ public WebSocketTableCell getValue(String column) {
+ return cells.get(column);
+ }
+
+ public String calculateChange(long lastUpdateDone) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(index);
+ builder.append(":");
+ String delim = "";
+ int count = 0;
+ List<String> columns = schema.columnNames();
+ for(int i = 0; i < columns.size(); i++){
+ String column = columns.get(i);
+ WebSocketTableCell cell = cells.get(column);
+ if(cell != null) {
+ long cellChanged = cell.getTimeStamp();
+ long difference = cellChanged - lastUpdateDone;
+
+ if(difference > 0) { // positive means change happened later than update
+ builder.append(delim);
+ builder.append(i);
+ builder.append("=");
+ builder.append(cell.getValue());
+ count++;
+ delim = ",";
+ }
+ }
+ }
+ if(count <= 0) {
+ return "";
+ }
+ return builder.toString();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRowAnnotator.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRowAnnotator.java
new file mode 100644
index 00000000..0ef307ef
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRowAnnotator.java
@@ -0,0 +1,89 @@
+package org.simpleframework.http.socket.table;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class WebSocketTableRowAnnotator {
+
+ private final WebSocketTableSchema schema;
+ private final WebSocketValueEncoder encoder;
+
+ public WebSocketTableRowAnnotator(WebSocketTableSchema schema) {
+ this.encoder = new WebSocketValueEncoder();
+ this.schema = schema;
+ }
+
+ public String calculateHighlight(WebSocketTableRow row, long lastUpdateDone) {
+ Map<String, String> pairs = new LinkedHashMap<String, String>();
+ Map<String, String> values = new LinkedHashMap<String, String>();
+
+ pairs.put("bidEFP", "bidEFPVolume");
+ pairs.put("bidEFPVolume", "bidEFP");
+ pairs.put("offerEFP", "offerEFPVolume");
+ pairs.put("offerEFPVolume", "offerEFP");
+ pairs.put("bidOutright", "bidOutrightVolume");
+ pairs.put("bidOutrightVolume", "bidOutright");
+ pairs.put("offerOutright", "offerOutrightVolume");
+ pairs.put("offerOutrightVolume", "offerOutright");
+
+ StringBuilder builder = new StringBuilder();
+ int index = row.getIndex();
+ builder.append(index);
+ builder.append(":");
+ String delim = "";
+ int count = 0;
+ List<String> columns = schema.columnNames();
+ for(int i = 0; i < columns.size(); i++){
+ String column = columns.get(i);
+ WebSocketTableCell cell = row.getValue(column);
+
+ if(cell == null) {
+ throw new IllegalStateException("Could not find column " + column);
+ }
+ String value = cell.getValue();
+
+ if(cell != null) {
+ long cellChanged = cell.getTimeStamp();
+ long difference = cellChanged - lastUpdateDone;
+
+ if(difference > 0 || values.containsKey(column)) { // positive means change happened later than update
+ builder.append(delim);
+ builder.append(i);
+ builder.append("=");
+ String doneAlready = values.get(column);
+
+ if(doneAlready != null) {
+ builder.append(doneAlready);
+ } else {
+ if(String.valueOf(value).indexOf("20") != -1 && (column.indexOf("bid") != -1 || column.indexOf("offer") != -1)) {
+ String style = encoder.encode("background-color: #32cd32;");
+ String other = pairs.get(column);
+
+ if(other != null) {
+ values.put(other, style);
+ }
+ values.put(column, style);
+ builder.append(style);
+ } else {
+ String style = encoder.encode("");
+ String other = pairs.get(column);
+
+ if(other != null) {
+ values.put(other, style);
+ }
+ values.put(column, style);
+ builder.append(style);
+ }
+ }
+ count++;
+ delim = ",";
+ }
+ }
+ }
+ if(count <= 0) {
+ return "";
+ }
+ return builder.toString();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRowChanger.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRowChanger.java
new file mode 100644
index 00000000..bdc6755d
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableRowChanger.java
@@ -0,0 +1,73 @@
+package org.simpleframework.http.socket.table;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+public class WebSocketTableRowChanger extends Thread {
+
+ private final WebSocketTableChanger changer;
+
+ public WebSocketTableRowChanger(WebSocketTable table) {
+ this.changer = new WebSocketTableChanger(table);
+ }
+
+ public void run() {
+ Random random = new SecureRandom();
+ List<String> products = new ArrayList<String>();
+
+ products.add("QTC44");
+ products.add("NSW22");
+ products.add("NSW23");
+ products.add("NSW24");
+ products.add("WAGA19");
+ products.add("CGS19");
+ products.add("CGS21");
+ products.add("CGS22");
+ products.add("CGSJun14");
+ products.add("CGSOct14");
+ products.add("CGSDec12");
+ products.add("QTC33");
+
+ for(int i = 0; i < 100; i++) {
+ products.add("CGS" + i);
+ }
+
+ while(true) {
+ try {
+ int rows = products.size();
+ long randomWait = random.nextInt(50) + 40;
+ int randomRow = random.nextInt(rows);
+ int randomBid = random.nextInt(50) + 1;
+ int randomOffer = random.nextInt(50) + 1;
+ int randomVolume = (random.nextInt(5) + 1) * 10;
+
+ if(randomRow != 0) {
+ String product = products.get(randomRow);
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ map.put("id", randomRow);
+ map.put("product", product);
+ map.put("bidOutrightVolume", randomVolume);
+ map.put("bidOutright", randomBid);
+ map.put("offerOutright", randomOffer);
+ map.put("offerOutrightVolume", randomVolume);
+ map.put("bidEFPVolume", randomVolume);
+ map.put("bidEFP", randomBid);
+ map.put("offerEFP", randomOffer);
+ map.put("offerEFPVolume", randomVolume);
+ map.put("reference", "3ySep");
+
+ changer.onChange(map);
+ }
+ Thread.sleep(randomWait);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSchema.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSchema.java
new file mode 100644
index 00000000..c8f4a684
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSchema.java
@@ -0,0 +1,40 @@
+package org.simpleframework.http.socket.table;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class WebSocketTableSchema {
+
+ private final Map<String, WebSocketTableColumnStyle> columns;
+
+ public WebSocketTableSchema(Map<String, WebSocketTableColumnStyle> columns) {
+ this.columns = columns;
+ }
+
+ public List<String> columnNames(){
+ return new ArrayList<String>(columns.keySet());
+ }
+
+ public boolean validColumn(String name) {
+ return columns.containsKey(name);
+ }
+
+ public String createStyle() {
+ StringBuilder builder = new StringBuilder();
+ Set<String> keys = columns.keySet();
+ int count = 0;
+
+ for(String key : keys){
+ WebSocketTableColumnStyle style = columns.get(key);
+ String columnStyle = style.createStyle();
+
+ if(count++ > 0) {
+ builder.append("|");
+ }
+ builder.append(columnStyle);
+ }
+ return builder.toString();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSubscription.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSubscription.java
new file mode 100644
index 00000000..b8402eb6
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSubscription.java
@@ -0,0 +1,44 @@
+package org.simpleframework.http.socket.table;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.simpleframework.http.socket.FrameChannel;
+
+public class WebSocketTableSubscription {
+
+ private final Set<Integer> missedUpdates;
+ private final AtomicLong timeStamp;
+ private final FrameChannel socket;
+ private final AtomicLong send;
+ private final AtomicLong received;
+
+ public WebSocketTableSubscription(FrameChannel socket) {
+ this.timeStamp = new AtomicLong();
+ this.received = new AtomicLong();
+ this.send = new AtomicLong();
+ this.missedUpdates = new HashSet<Integer>();
+ this.socket = socket;
+ }
+
+ public Set<Integer> getMissedUpdates() {
+ return missedUpdates;
+ }
+
+ public AtomicLong getSendCount() {
+ return send;
+ }
+
+ public AtomicLong getReceiveCount() {
+ return received;
+ }
+
+ public AtomicLong getTimeStamp() {
+ return timeStamp;
+ }
+
+ public FrameChannel getSocket() {
+ return socket;
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSweeper.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSweeper.java
new file mode 100644
index 00000000..5f09efa2
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableSweeper.java
@@ -0,0 +1,33 @@
+package org.simpleframework.http.socket.table;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class WebSocketTableSweeper {
+
+ private final WebSocketTable table;
+
+ public WebSocketTableSweeper(WebSocketTable table) {
+ this.table = table;
+ }
+
+ public Map<WebSocketTableUpdateType, String> sweep(long time, long count) {
+ Map<WebSocketTableUpdateType, String> messages = new LinkedHashMap<WebSocketTableUpdateType, String>();
+
+ if(count <= 1) {
+ WebSocketTableSchema schema = table.getSchema();
+ String schemaUpdate = schema.createStyle();
+ messages.put(WebSocketTableUpdateType.SCHEMA, schemaUpdate);
+ }
+ String highlightUpdate = table.calculateHighlight(time);
+ String deltaUpdate = table.calculateChange(time);// really should only take small batches...
+
+ highlightUpdate = count + "@" + System.currentTimeMillis() + ":" + highlightUpdate;
+ deltaUpdate = count + "@" + System.currentTimeMillis() + ":" + deltaUpdate;
+
+ messages.put(WebSocketTableUpdateType.HIGHLIGHT, highlightUpdate);
+ messages.put(WebSocketTableUpdateType.DELTA, deltaUpdate);
+
+ return messages;
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdateType.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdateType.java
new file mode 100644
index 00000000..d7780e76
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdateType.java
@@ -0,0 +1,17 @@
+package org.simpleframework.http.socket.table;
+
+public enum WebSocketTableUpdateType {
+ SCHEMA('S'),
+ HIGHLIGHT('H'),
+ DELTA('D');
+
+ public final char code;
+
+ private WebSocketTableUpdateType(char code) {
+ this.code = code;
+ }
+
+ public char getCode() {
+ return code;
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdater.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdater.java
new file mode 100644
index 00000000..0c4ec4de
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdater.java
@@ -0,0 +1,126 @@
+package org.simpleframework.http.socket.table;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.simpleframework.http.socket.Session;
+import org.simpleframework.http.socket.FrameChannel;
+import org.simpleframework.http.socket.WebSocketAnalyzer;
+import org.simpleframework.http.socket.service.Service;
+import org.simpleframework.transport.trace.TraceAnalyzer;
+
+public class WebSocketTableUpdater extends Thread implements Service {
+
+ private final Set<WebSocketTableSubscription> subscriptions;
+ private final WebSocketTableListener listener;
+ private final WebSocketTableRowChanger changer;
+ private final WebSocketTableSweeper sweeper;
+ private final WebSocketTable table;
+ private final AtomicLong time;
+
+ public WebSocketTableUpdater(String key, WebSocketTableSchema schema, WebSocketTableRowAnnotator annotator) {
+ this.subscriptions = new CopyOnWriteArraySet<WebSocketTableSubscription>();
+ this.table = new WebSocketTable(key, schema, annotator);
+ this.sweeper = new WebSocketTableSweeper(table);
+ this.changer = new WebSocketTableRowChanger(table);
+ this.listener = new WebSocketTableListener(this);
+ this.time = new AtomicLong();
+ }
+
+ public void refresh(Session session) {
+ for(WebSocketTableSubscription subscription : subscriptions) {
+ FrameChannel socket = subscription.getSocket();
+ FrameChannel other = session.getChannel();
+
+ if(socket == other) {
+ AtomicLong timeStamp = subscription.getTimeStamp();
+ timeStamp.set(0);
+ }
+ }
+ }
+
+ public void run() {
+ changer.start();
+
+ while(true) {
+ try {
+ Thread.sleep(200);
+
+ for(WebSocketTableSubscription subscription : subscriptions) {
+ FrameChannel socket = subscription.getSocket();
+ AtomicLong timeStamp = subscription.getTimeStamp();
+ AtomicLong sendCount = subscription.getSendCount();
+ long before = System.currentTimeMillis();
+ long time = timeStamp.get();
+ long count = sendCount.get();
+
+ try {
+ Map<WebSocketTableUpdateType, String> messages = sweeper.sweep(time - 1000, count);
+ Set<WebSocketTableUpdateType> updates = messages.keySet();
+
+ for(WebSocketTableUpdateType update : updates) {
+ String message = messages.get(update);
+
+ if(message != null) {
+ socket.send(update.code + message);
+ }
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ subscriptions.remove(subscription);
+ socket.close();
+ } finally {
+ sendCount.getAndIncrement();
+ timeStamp.set(before);
+ }
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void connect(Session connection) {
+ FrameChannel socket = connection.getChannel();
+
+ try {
+ WebSocketTableSubscription subscription = new WebSocketTableSubscription(socket);
+
+ socket.register(listener);
+ subscriptions.add(subscription);
+ time.set(0);
+ Thread.sleep(1000); // crap
+ time.set(0);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ public static void main(String[] list) throws Exception {
+ TraceAnalyzer agent = new WebSocketAnalyzer();
+ Map<String, WebSocketTableColumnStyle> columns = new LinkedHashMap<String, WebSocketTableColumnStyle>();
+
+ WebSocketTableSchema schema = new WebSocketTableSchema(columns);
+ columns.put("id", new WebSocketTableColumnStyle("id", "Id", "{id}", true, true));
+ columns.put("bidOutrightVolume", new WebSocketTableColumnStyle("bidOutrightVolume", "$ B", "<div style='font-weight: bold; color: #0000ff; text-decoration: underline;'>{bidOutrightVolume}</a>", true, false));
+ columns.put("bidOutright", new WebSocketTableColumnStyle("bidOutright", "Bid", "<div style='font-weight: bold; color: #0000ff; text-decoration: underline;'>{bidOutright}</a>", true, false));
+ columns.put("offerOutright", new WebSocketTableColumnStyle("offerOutright", "Offer", "<div style='font-weight: bold; color: #ff0000; text-decoration: underline;'>{offerOutright}</a>", true, false));
+ columns.put("offerOutrightVolume", new WebSocketTableColumnStyle("offerOutrightVolume", "$ O", "<div style='font-weight: bold; color: #ff0000; text-decoration: underline;'>{offerOutrightVolume}</a>", true, false));
+ columns.put("product", new WebSocketTableColumnStyle("product", "Security", "<div style='font-weight: bold;'>{product}</div>", true, true));
+ columns.put("bidEFPVolume", new WebSocketTableColumnStyle("bidEFPVolume", "$ B", "<div style='font-weight: bold; color: #0000ff; text-decoration: underline;'>{bidEFPVolume}</a>", true, false));
+ columns.put("bidEFP", new WebSocketTableColumnStyle("bidEFP", "Bid", "<div style='font-weight: bold; color: #0000ff; text-decoration: underline;'>{bidEFP}</a>", true, false));
+ columns.put("offerEFP", new WebSocketTableColumnStyle("offerEFP", "Offer", "<div style='font-weight: bold; color: #ff0000; text-decoration: underline;'>{offerEFP}</a>", true, false));
+ columns.put("offerEFPVolume", new WebSocketTableColumnStyle("offerEFPVolume", "$ O", "<div style='font-weight: bold; color: #ff0000; text-decoration: underline;'>{offerEFPVolume}</a>", true, false));
+ columns.put("reference", new WebSocketTableColumnStyle("reference", "Ref", "{reference}", true, true));
+ WebSocketTableRowAnnotator annotator = new WebSocketTableRowAnnotator(schema);
+ WebSocketTableUpdater application = new WebSocketTableUpdater("product", schema, annotator);
+
+ WebSocketTableUpdaterApplication container = new WebSocketTableUpdaterApplication(application, agent, 6060);
+ application.start();
+ container.connect();
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdaterApplication.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdaterApplication.java
new file mode 100644
index 00000000..586c82f4
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketTableUpdaterApplication.java
@@ -0,0 +1,154 @@
+package org.simpleframework.http.socket.table;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Map;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.ArrayAllocator;
+import org.simpleframework.http.Path;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.Status;
+import org.simpleframework.http.core.Container;
+import org.simpleframework.http.core.ContainerTransportProcessor;
+import org.simpleframework.http.socket.service.Router;
+import org.simpleframework.http.socket.service.RouterContainer;
+import org.simpleframework.http.socket.service.DirectRouter;
+import org.simpleframework.transport.TransportProcessor;
+import org.simpleframework.transport.TransportSocketProcessor;
+import org.simpleframework.transport.SocketProcessor;
+import org.simpleframework.transport.Transport;
+import org.simpleframework.transport.connect.Connection;
+import org.simpleframework.transport.connect.SocketConnection;
+import org.simpleframework.transport.trace.TraceAnalyzer;
+
+public class WebSocketTableUpdaterApplication implements Container, TransportProcessor {
+
+ private final String ROOT_PATH = "C:\\Work\\development\\github\\simpleframework\\simple\\simple-http\\src\\test\\java\\org\\simpleframework\\http\\socket\\table";
+
+ private final Router negotiator;
+ private final RouterContainer container;
+ private final SocketAddress address;
+ private final Connection connection;
+ private final TransportProcessor processor;
+ private final Allocator allocator;
+ private final SocketProcessor server;
+
+ public WebSocketTableUpdaterApplication(WebSocketTableUpdater handler, TraceAnalyzer agent, int port) throws Exception {
+ this.negotiator = new DirectRouter(handler);
+ this.container = new RouterContainer(this, negotiator, 10);
+ this.allocator = new ArrayAllocator();
+ this.processor = new ContainerTransportProcessor(container, allocator, 1);
+ this.server = new TransportSocketProcessor(this);
+ this.connection = new SocketConnection(server, agent);
+ this.address = new InetSocketAddress(port);
+ }
+
+ public void connect() throws IOException {
+ connection.connect(address);
+ }
+
+ public void handle(Request req, Response resp) {
+ Path path = req.getPath();
+ String normal = path.getPath();
+
+ System.err.println(req);
+
+ if(req.getTarget().equals("/login")) {
+ String user = req.getParameter("user");
+ long time = System.currentTimeMillis();
+
+ try {
+ resp.setStatus(Status.FOUND);
+ resp.setValue("Location", "/table");
+ resp.setCookie("user", user);
+ resp.setDate("Date", time);
+ resp.setValue("Server", "WebSocketTableApplication/1.0");
+ resp.setContentType("text/html");
+ resp.close();
+ }catch(Exception e) {
+ e.printStackTrace();
+ }
+ } else if(req.getTarget().equals("/update")){
+ long time = System.currentTimeMillis();
+ try {
+ container.handle(req, resp);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ long time = System.currentTimeMillis();
+
+ try {
+ byte[] page = loadPage(normal);
+
+ resp.setDate("Date", time);
+ resp.setValue("Server", "WebSocketTableApplication/1.0");
+
+ if(normal.endsWith(".html")) {
+ resp.setContentType("text/html");
+ } else if(normal.endsWith(".css")) {
+ resp.setContentType("text/css");
+ } else if(normal.endsWith(".js")) {
+ resp.setContentType("text/javascript");
+ } else if(normal.endsWith(".png")) {
+ resp.setContentType("image/png");
+ } else {
+ resp.setContentType("text/plain");
+ }
+ OutputStream out = resp.getOutputStream();
+ out.write(page);
+ out.close();
+ }catch(Exception e) {
+ e.printStackTrace();
+
+ try {
+ resp.setCode(404);
+ resp.setDescription("Not Found");
+ resp.setDate("Date", time);
+ resp.setValue("Server", "WebSocketTableApplication/1.0");
+ resp.setContentType("text/plain");
+
+ PrintStream out = resp.getPrintStream();
+
+ e.printStackTrace(out);
+ out.close();
+ } catch(Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ }
+ }
+
+ public byte[] loadPage(String name) throws IOException {
+ InputStream loginPage = new FileInputStream(new File(ROOT_PATH, name));
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] chunk = new byte[1024];
+ int count = 0;
+
+ while((count = loginPage.read(chunk)) != -1) {
+ out.write(chunk, 0, count);
+ }
+ out.close();
+ return out.toByteArray();
+ }
+
+ public void process(Transport transport) throws IOException {
+ Map map = transport.getAttributes();
+ map.put(Transport.class, transport);
+ processor.process(transport);
+ }
+
+ public void stop() throws IOException {
+ processor.stop();
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketValueEncoder.java b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketValueEncoder.java
new file mode 100644
index 00000000..7afa93b3
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/WebSocketValueEncoder.java
@@ -0,0 +1,53 @@
+package org.simpleframework.http.socket.table;
+
+public class WebSocketValueEncoder {
+
+ public String encode(Object value) {
+ if(value instanceof String) {
+ String text = String.valueOf(value);
+
+ if(containsAnyOf(text, "<>|:=,")) {
+ StringBuffer buffer = new StringBuffer("<");
+
+ for(int i = 0; i < text.length(); i++){
+ char ch = text.charAt(i);
+ String hex = Integer.toHexString(ch);
+
+ buffer.append(hex);
+ }
+ return buffer.toString();
+ }
+ }
+ return ">" + value;
+ }
+
+ public String decode(String text) {
+ String value = text.substring(1);
+
+ if(text.startsWith("?")) {
+ StringBuilder buffer = new StringBuilder();
+
+ for(int i = 0; i < value.length() - 1; i += 2){
+ String output = value.substring(i, (i + 2));
+ int decimal = Integer.parseInt(output, 16);
+
+ buffer.append((char)decimal);
+ }
+ return buffer.toString();
+ }
+ return value;
+ }
+
+ public boolean containsAnyOf(String text, String chars) {
+ int length = chars.length();
+
+ for(int i = 0; i < length; i++) {
+ char value = chars.charAt(i);
+
+ if(text.indexOf(value) != -1) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/bootstrap.css b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/bootstrap.css
new file mode 100644
index 00000000..20bf9619
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/bootstrap.css
@@ -0,0 +1,5774 @@
+/*!
+ * Bootstrap v2.1.1
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section {
+ display: block;
+}
+
+audio,
+canvas,
+video {
+ display: inline-block;
+ *display: inline;
+ *zoom: 1;
+}
+
+audio:not([controls]) {
+ display: none;
+}
+
+html {
+ font-size: 100%;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+
+a:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+a:hover,
+a:active {
+ outline: 0;
+}
+
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+img {
+ width: auto\9;
+ height: auto;
+ max-width: 100%;
+ vertical-align: middle;
+ border: 0;
+ -ms-interpolation-mode: bicubic;
+}
+
+#map_canvas img {
+ max-width: none;
+}
+
+button,
+input,
+select,
+textarea {
+ margin: 0;
+ font-size: 100%;
+ vertical-align: middle;
+}
+
+button,
+input {
+ *overflow: visible;
+ line-height: normal;
+}
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ cursor: pointer;
+ -webkit-appearance: button;
+}
+
+input[type="search"] {
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ -webkit-appearance: textfield;
+}
+
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+ -webkit-appearance: none;
+}
+
+textarea {
+ overflow: auto;
+ vertical-align: top;
+}
+
+.clearfix {
+ *zoom: 1;
+}
+
+.clearfix:before,
+.clearfix:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.clearfix:after {
+ clear: both;
+}
+
+.hide-text {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.input-block-level {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 20px;
+ color: #333333;
+ background-color: #ffffff;
+}
+
+a {
+ color: #0088cc;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #005580;
+ text-decoration: underline;
+}
+
+.img-rounded {
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.img-polaroid {
+ padding: 4px;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.img-circle {
+ -webkit-border-radius: 500px;
+ -moz-border-radius: 500px;
+ border-radius: 500px;
+}
+
+.row {
+ margin-left: -20px;
+ *zoom: 1;
+}
+
+.row:before,
+.row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.row:after {
+ clear: both;
+}
+
+[class*="span"] {
+ float: left;
+ min-height: 1px;
+ margin-left: 20px;
+}
+
+.container,
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+ width: 940px;
+}
+
+.span12 {
+ width: 940px;
+}
+
+.span11 {
+ width: 860px;
+}
+
+.span10 {
+ width: 780px;
+}
+
+.span9 {
+ width: 700px;
+}
+
+.span8 {
+ width: 620px;
+}
+
+.span7 {
+ width: 540px;
+}
+
+.span6 {
+ width: 460px;
+}
+
+.span5 {
+ width: 380px;
+}
+
+.span4 {
+ width: 300px;
+}
+
+.span3 {
+ width: 220px;
+}
+
+.span2 {
+ width: 140px;
+}
+
+.span1 {
+ width: 60px;
+}
+
+.offset12 {
+ margin-left: 980px;
+}
+
+.offset11 {
+ margin-left: 900px;
+}
+
+.offset10 {
+ margin-left: 820px;
+}
+
+.offset9 {
+ margin-left: 740px;
+}
+
+.offset8 {
+ margin-left: 660px;
+}
+
+.offset7 {
+ margin-left: 580px;
+}
+
+.offset6 {
+ margin-left: 500px;
+}
+
+.offset5 {
+ margin-left: 420px;
+}
+
+.offset4 {
+ margin-left: 340px;
+}
+
+.offset3 {
+ margin-left: 260px;
+}
+
+.offset2 {
+ margin-left: 180px;
+}
+
+.offset1 {
+ margin-left: 100px;
+}
+
+.row-fluid {
+ width: 100%;
+ *zoom: 1;
+}
+
+.row-fluid:before,
+.row-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.row-fluid:after {
+ clear: both;
+}
+
+.row-fluid [class*="span"] {
+ display: block;
+ float: left;
+ width: 100%;
+ min-height: 30px;
+ margin-left: 2.127659574468085%;
+ *margin-left: 2.074468085106383%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+}
+
+.row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+}
+
+.row-fluid .span11 {
+ width: 91.48936170212765%;
+ *width: 91.43617021276594%;
+}
+
+.row-fluid .span10 {
+ width: 82.97872340425532%;
+ *width: 82.92553191489361%;
+}
+
+.row-fluid .span9 {
+ width: 74.46808510638297%;
+ *width: 74.41489361702126%;
+}
+
+.row-fluid .span8 {
+ width: 65.95744680851064%;
+ *width: 65.90425531914893%;
+}
+
+.row-fluid .span7 {
+ width: 57.44680851063829%;
+ *width: 57.39361702127659%;
+}
+
+.row-fluid .span6 {
+ width: 48.93617021276595%;
+ *width: 48.88297872340425%;
+}
+
+.row-fluid .span5 {
+ width: 40.42553191489362%;
+ *width: 40.37234042553192%;
+}
+
+.row-fluid .span4 {
+ width: 31.914893617021278%;
+ *width: 31.861702127659576%;
+}
+
+.row-fluid .span3 {
+ width: 23.404255319148934%;
+ *width: 23.351063829787233%;
+}
+
+.row-fluid .span2 {
+ width: 14.893617021276595%;
+ *width: 14.840425531914894%;
+}
+
+.row-fluid .span1 {
+ width: 6.382978723404255%;
+ *width: 6.329787234042553%;
+}
+
+.row-fluid .offset12 {
+ margin-left: 104.25531914893617%;
+ *margin-left: 104.14893617021275%;
+}
+
+.row-fluid .offset12:first-child {
+ margin-left: 102.12765957446808%;
+ *margin-left: 102.02127659574467%;
+}
+
+.row-fluid .offset11 {
+ margin-left: 95.74468085106382%;
+ *margin-left: 95.6382978723404%;
+}
+
+.row-fluid .offset11:first-child {
+ margin-left: 93.61702127659574%;
+ *margin-left: 93.51063829787232%;
+}
+
+.row-fluid .offset10 {
+ margin-left: 87.23404255319149%;
+ *margin-left: 87.12765957446807%;
+}
+
+.row-fluid .offset10:first-child {
+ margin-left: 85.1063829787234%;
+ *margin-left: 84.99999999999999%;
+}
+
+.row-fluid .offset9 {
+ margin-left: 78.72340425531914%;
+ *margin-left: 78.61702127659572%;
+}
+
+.row-fluid .offset9:first-child {
+ margin-left: 76.59574468085106%;
+ *margin-left: 76.48936170212764%;
+}
+
+.row-fluid .offset8 {
+ margin-left: 70.2127659574468%;
+ *margin-left: 70.10638297872339%;
+}
+
+.row-fluid .offset8:first-child {
+ margin-left: 68.08510638297872%;
+ *margin-left: 67.9787234042553%;
+}
+
+.row-fluid .offset7 {
+ margin-left: 61.70212765957446%;
+ *margin-left: 61.59574468085106%;
+}
+
+.row-fluid .offset7:first-child {
+ margin-left: 59.574468085106375%;
+ *margin-left: 59.46808510638297%;
+}
+
+.row-fluid .offset6 {
+ margin-left: 53.191489361702125%;
+ *margin-left: 53.085106382978715%;
+}
+
+.row-fluid .offset6:first-child {
+ margin-left: 51.063829787234035%;
+ *margin-left: 50.95744680851063%;
+}
+
+.row-fluid .offset5 {
+ margin-left: 44.68085106382979%;
+ *margin-left: 44.57446808510638%;
+}
+
+.row-fluid .offset5:first-child {
+ margin-left: 42.5531914893617%;
+ *margin-left: 42.4468085106383%;
+}
+
+.row-fluid .offset4 {
+ margin-left: 36.170212765957444%;
+ *margin-left: 36.06382978723405%;
+}
+
+.row-fluid .offset4:first-child {
+ margin-left: 34.04255319148936%;
+ *margin-left: 33.93617021276596%;
+}
+
+.row-fluid .offset3 {
+ margin-left: 27.659574468085104%;
+ *margin-left: 27.5531914893617%;
+}
+
+.row-fluid .offset3:first-child {
+ margin-left: 25.53191489361702%;
+ *margin-left: 25.425531914893618%;
+}
+
+.row-fluid .offset2 {
+ margin-left: 19.148936170212764%;
+ *margin-left: 19.04255319148936%;
+}
+
+.row-fluid .offset2:first-child {
+ margin-left: 17.02127659574468%;
+ *margin-left: 16.914893617021278%;
+}
+
+.row-fluid .offset1 {
+ margin-left: 10.638297872340425%;
+ *margin-left: 10.53191489361702%;
+}
+
+.row-fluid .offset1:first-child {
+ margin-left: 8.51063829787234%;
+ *margin-left: 8.404255319148938%;
+}
+
+[class*="span"].hide,
+.row-fluid [class*="span"].hide {
+ display: none;
+}
+
+[class*="span"].pull-right,
+.row-fluid [class*="span"].pull-right {
+ float: right;
+}
+
+.container {
+ margin-right: auto;
+ margin-left: auto;
+ *zoom: 1;
+}
+
+.container:before,
+.container:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.container:after {
+ clear: both;
+}
+
+.container-fluid {
+ padding-right: 20px;
+ padding-left: 20px;
+ *zoom: 1;
+}
+
+.container-fluid:before,
+.container-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.container-fluid:after {
+ clear: both;
+}
+
+p {
+ margin: 0 0 10px;
+}
+
+.lead {
+ margin-bottom: 20px;
+ font-size: 21px;
+ font-weight: 200;
+ line-height: 30px;
+}
+
+small {
+ font-size: 85%;
+}
+
+strong {
+ font-weight: bold;
+}
+
+em {
+ font-style: italic;
+}
+
+cite {
+ font-style: normal;
+}
+
+.muted {
+ color: #999999;
+}
+
+.text-warning {
+ color: #c09853;
+}
+
+.text-error {
+ color: #b94a48;
+}
+
+.text-info {
+ color: #3a87ad;
+}
+
+.text-success {
+ color: #468847;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ margin: 10px 0;
+ font-family: inherit;
+ font-weight: bold;
+ line-height: 1;
+ color: inherit;
+ text-rendering: optimizelegibility;
+}
+
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small {
+ font-weight: normal;
+ line-height: 1;
+ color: #999999;
+}
+
+h1 {
+ font-size: 36px;
+ line-height: 40px;
+}
+
+h2 {
+ font-size: 30px;
+ line-height: 40px;
+}
+
+h3 {
+ font-size: 24px;
+ line-height: 40px;
+}
+
+h4 {
+ font-size: 18px;
+ line-height: 20px;
+}
+
+h5 {
+ font-size: 14px;
+ line-height: 20px;
+}
+
+h6 {
+ font-size: 12px;
+ line-height: 20px;
+}
+
+h1 small {
+ font-size: 24px;
+}
+
+h2 small {
+ font-size: 18px;
+}
+
+h3 small {
+ font-size: 14px;
+}
+
+h4 small {
+ font-size: 14px;
+}
+
+.page-header {
+ padding-bottom: 9px;
+ margin: 20px 0 30px;
+ border-bottom: 1px solid #eeeeee;
+}
+
+ul,
+ol {
+ padding: 0;
+ margin: 0 0 10px 25px;
+}
+
+ul ul,
+ul ol,
+ol ol,
+ol ul {
+ margin-bottom: 0;
+}
+
+li {
+ line-height: 20px;
+}
+
+ul.unstyled,
+ol.unstyled {
+ margin-left: 0;
+ list-style: none;
+}
+
+dl {
+ margin-bottom: 20px;
+}
+
+dt,
+dd {
+ line-height: 20px;
+}
+
+dt {
+ font-weight: bold;
+}
+
+dd {
+ margin-left: 10px;
+}
+
+.dl-horizontal {
+ *zoom: 1;
+}
+
+.dl-horizontal:before,
+.dl-horizontal:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.dl-horizontal:after {
+ clear: both;
+}
+
+.dl-horizontal dt {
+ float: left;
+ width: 160px;
+ overflow: hidden;
+ clear: left;
+ text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.dl-horizontal dd {
+ margin-left: 180px;
+}
+
+hr {
+ margin: 20px 0;
+ border: 0;
+ border-top: 1px solid #eeeeee;
+ border-bottom: 1px solid #ffffff;
+}
+
+abbr[title] {
+ cursor: help;
+ border-bottom: 1px dotted #999999;
+}
+
+abbr.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+
+blockquote {
+ padding: 0 0 0 15px;
+ margin: 0 0 20px;
+ border-left: 5px solid #eeeeee;
+}
+
+blockquote p {
+ margin-bottom: 0;
+ font-size: 16px;
+ font-weight: 300;
+ line-height: 25px;
+}
+
+blockquote small {
+ display: block;
+ line-height: 20px;
+ color: #999999;
+}
+
+blockquote small:before {
+ content: '\2014 \00A0';
+}
+
+blockquote.pull-right {
+ float: right;
+ padding-right: 15px;
+ padding-left: 0;
+ border-right: 5px solid #eeeeee;
+ border-left: 0;
+}
+
+blockquote.pull-right p,
+blockquote.pull-right small {
+ text-align: right;
+}
+
+blockquote.pull-right small:before {
+ content: '';
+}
+
+blockquote.pull-right small:after {
+ content: '\00A0 \2014';
+}
+
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+ content: "";
+}
+
+address {
+ display: block;
+ margin-bottom: 20px;
+ font-style: normal;
+ line-height: 20px;
+}
+
+code,
+pre {
+ padding: 0 3px 2px;
+ font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
+ font-size: 12px;
+ color: #333333;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+code {
+ padding: 2px 4px;
+ color: #d14;
+ background-color: #f7f7f9;
+ border: 1px solid #e1e1e8;
+}
+
+pre {
+ display: block;
+ padding: 9.5px;
+ margin: 0 0 10px;
+ font-size: 13px;
+ line-height: 20px;
+ word-break: break-all;
+ word-wrap: break-word;
+ white-space: pre;
+ white-space: pre-wrap;
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+pre.prettyprint {
+ margin-bottom: 20px;
+}
+
+pre code {
+ padding: 0;
+ color: inherit;
+ background-color: transparent;
+ border: 0;
+}
+
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
+}
+
+form {
+ margin: 0 0 20px;
+}
+
+fieldset {
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: 40px;
+ color: #333333;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5;
+}
+
+legend small {
+ font-size: 15px;
+ color: #999999;
+}
+
+label,
+input,
+button,
+select,
+textarea {
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 20px;
+}
+
+input,
+button,
+select,
+textarea {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+label {
+ display: block;
+ margin-bottom: 5px;
+}
+
+select,
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+ display: inline-block;
+ height: 20px;
+ padding: 4px 6px;
+ margin-bottom: 9px;
+ font-size: 14px;
+ line-height: 20px;
+ color: #555555;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+input,
+textarea,
+.uneditable-input {
+ width: 206px;
+}
+
+textarea {
+ height: auto;
+}
+
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
+ -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
+ -o-transition: border linear 0.2s, box-shadow linear 0.2s;
+ transition: border linear 0.2s, box-shadow linear 0.2s;
+}
+
+textarea:focus,
+input[type="text"]:focus,
+input[type="password"]:focus,
+input[type="datetime"]:focus,
+input[type="datetime-local"]:focus,
+input[type="date"]:focus,
+input[type="month"]:focus,
+input[type="time"]:focus,
+input[type="week"]:focus,
+input[type="number"]:focus,
+input[type="email"]:focus,
+input[type="url"]:focus,
+input[type="search"]:focus,
+input[type="tel"]:focus,
+input[type="color"]:focus,
+.uneditable-input:focus {
+ border-color: rgba(82, 168, 236, 0.8);
+ outline: 0;
+ outline: thin dotted \9;
+ /* IE6-9 */
+
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+}
+
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 4px 0 0;
+ margin-top: 1px \9;
+ *margin-top: 0;
+ line-height: normal;
+ cursor: pointer;
+}
+
+input[type="file"],
+input[type="image"],
+input[type="submit"],
+input[type="reset"],
+input[type="button"],
+input[type="radio"],
+input[type="checkbox"] {
+ width: auto;
+}
+
+select,
+input[type="file"] {
+ height: 30px;
+ /* In IE7, the height of the select element cannot be changed by height, only font-size */
+
+ *margin-top: 4px;
+ /* For IE7, add top margin to align select with labels */
+
+ line-height: 30px;
+}
+
+select {
+ width: 220px;
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+}
+
+select[multiple],
+select[size] {
+ height: auto;
+}
+
+select:focus,
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+.uneditable-input,
+.uneditable-textarea {
+ color: #999999;
+ cursor: not-allowed;
+ background-color: #fcfcfc;
+ border-color: #cccccc;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+}
+
+.uneditable-input {
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.uneditable-textarea {
+ width: auto;
+ height: auto;
+}
+
+input:-moz-placeholder,
+textarea:-moz-placeholder {
+ color: #999999;
+}
+
+input:-ms-input-placeholder,
+textarea:-ms-input-placeholder {
+ color: #999999;
+}
+
+input::-webkit-input-placeholder,
+textarea::-webkit-input-placeholder {
+ color: #999999;
+}
+
+.radio,
+.checkbox {
+ min-height: 18px;
+ padding-left: 18px;
+}
+
+.radio input[type="radio"],
+.checkbox input[type="checkbox"] {
+ float: left;
+ margin-left: -18px;
+}
+
+.controls > .radio:first-child,
+.controls > .checkbox:first-child {
+ padding-top: 5px;
+}
+
+.radio.inline,
+.checkbox.inline {
+ display: inline-block;
+ padding-top: 5px;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+
+.radio.inline + .radio.inline,
+.checkbox.inline + .checkbox.inline {
+ margin-left: 10px;
+}
+
+.input-mini {
+ width: 60px;
+}
+
+.input-small {
+ width: 90px;
+}
+
+.input-medium {
+ width: 150px;
+}
+
+.input-large {
+ width: 210px;
+}
+
+.input-xlarge {
+ width: 270px;
+}
+
+.input-xxlarge {
+ width: 530px;
+}
+
+input[class*="span"],
+select[class*="span"],
+textarea[class*="span"],
+.uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"] {
+ float: none;
+ margin-left: 0;
+}
+
+.input-append input[class*="span"],
+.input-append .uneditable-input[class*="span"],
+.input-prepend input[class*="span"],
+.input-prepend .uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"],
+.row-fluid .input-prepend [class*="span"],
+.row-fluid .input-append [class*="span"] {
+ display: inline-block;
+}
+
+input,
+textarea,
+.uneditable-input {
+ margin-left: 0;
+}
+
+.controls-row [class*="span"] + [class*="span"] {
+ margin-left: 20px;
+}
+
+input.span12,
+textarea.span12,
+.uneditable-input.span12 {
+ width: 926px;
+}
+
+input.span11,
+textarea.span11,
+.uneditable-input.span11 {
+ width: 846px;
+}
+
+input.span10,
+textarea.span10,
+.uneditable-input.span10 {
+ width: 766px;
+}
+
+input.span9,
+textarea.span9,
+.uneditable-input.span9 {
+ width: 686px;
+}
+
+input.span8,
+textarea.span8,
+.uneditable-input.span8 {
+ width: 606px;
+}
+
+input.span7,
+textarea.span7,
+.uneditable-input.span7 {
+ width: 526px;
+}
+
+input.span6,
+textarea.span6,
+.uneditable-input.span6 {
+ width: 446px;
+}
+
+input.span5,
+textarea.span5,
+.uneditable-input.span5 {
+ width: 366px;
+}
+
+input.span4,
+textarea.span4,
+.uneditable-input.span4 {
+ width: 286px;
+}
+
+input.span3,
+textarea.span3,
+.uneditable-input.span3 {
+ width: 206px;
+}
+
+input.span2,
+textarea.span2,
+.uneditable-input.span2 {
+ width: 126px;
+}
+
+input.span1,
+textarea.span1,
+.uneditable-input.span1 {
+ width: 46px;
+}
+
+.controls-row {
+ *zoom: 1;
+}
+
+.controls-row:before,
+.controls-row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.controls-row:after {
+ clear: both;
+}
+
+.controls-row [class*="span"] {
+ float: left;
+}
+
+input[disabled],
+select[disabled],
+textarea[disabled],
+input[readonly],
+select[readonly],
+textarea[readonly] {
+ cursor: not-allowed;
+ background-color: #eeeeee;
+}
+
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"][readonly],
+input[type="checkbox"][readonly] {
+ background-color: transparent;
+}
+
+.control-group.warning > label,
+.control-group.warning .help-block,
+.control-group.warning .help-inline {
+ color: #c09853;
+}
+
+.control-group.warning .checkbox,
+.control-group.warning .radio,
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+ color: #c09853;
+}
+
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+ border-color: #c09853;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.warning input:focus,
+.control-group.warning select:focus,
+.control-group.warning textarea:focus {
+ border-color: #a47e3c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+}
+
+.control-group.warning .input-prepend .add-on,
+.control-group.warning .input-append .add-on {
+ color: #c09853;
+ background-color: #fcf8e3;
+ border-color: #c09853;
+}
+
+.control-group.error > label,
+.control-group.error .help-block,
+.control-group.error .help-inline {
+ color: #b94a48;
+}
+
+.control-group.error .checkbox,
+.control-group.error .radio,
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+ color: #b94a48;
+}
+
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+ border-color: #b94a48;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.error input:focus,
+.control-group.error select:focus,
+.control-group.error textarea:focus {
+ border-color: #953b39;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+}
+
+.control-group.error .input-prepend .add-on,
+.control-group.error .input-append .add-on {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #b94a48;
+}
+
+.control-group.success > label,
+.control-group.success .help-block,
+.control-group.success .help-inline {
+ color: #468847;
+}
+
+.control-group.success .checkbox,
+.control-group.success .radio,
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+ color: #468847;
+}
+
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+ border-color: #468847;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.success input:focus,
+.control-group.success select:focus,
+.control-group.success textarea:focus {
+ border-color: #356635;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+}
+
+.control-group.success .input-prepend .add-on,
+.control-group.success .input-append .add-on {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #468847;
+}
+
+.control-group.info > label,
+.control-group.info .help-block,
+.control-group.info .help-inline {
+ color: #3a87ad;
+}
+
+.control-group.info .checkbox,
+.control-group.info .radio,
+.control-group.info input,
+.control-group.info select,
+.control-group.info textarea {
+ color: #3a87ad;
+}
+
+.control-group.info input,
+.control-group.info select,
+.control-group.info textarea {
+ border-color: #3a87ad;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.info input:focus,
+.control-group.info select:focus,
+.control-group.info textarea:focus {
+ border-color: #2d6987;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
+}
+
+.control-group.info .input-prepend .add-on,
+.control-group.info .input-append .add-on {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #3a87ad;
+}
+
+input:focus:required:invalid,
+textarea:focus:required:invalid,
+select:focus:required:invalid {
+ color: #b94a48;
+ border-color: #ee5f5b;
+}
+
+input:focus:required:invalid:focus,
+textarea:focus:required:invalid:focus,
+select:focus:required:invalid:focus {
+ border-color: #e9322d;
+ -webkit-box-shadow: 0 0 6px #f8b9b7;
+ -moz-box-shadow: 0 0 6px #f8b9b7;
+ box-shadow: 0 0 6px #f8b9b7;
+}
+
+.form-actions {
+ padding: 19px 20px 20px;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #e5e5e5;
+ *zoom: 1;
+}
+
+.form-actions:before,
+.form-actions:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.form-actions:after {
+ clear: both;
+}
+
+.help-block,
+.help-inline {
+ color: #595959;
+}
+
+.help-block {
+ display: block;
+ margin-bottom: 10px;
+}
+
+.help-inline {
+ display: inline-block;
+ *display: inline;
+ padding-left: 5px;
+ vertical-align: middle;
+ *zoom: 1;
+}
+
+.input-append,
+.input-prepend {
+ margin-bottom: 5px;
+ font-size: 0;
+ white-space: nowrap;
+}
+
+.input-append input,
+.input-prepend input,
+.input-append select,
+.input-prepend select,
+.input-append .uneditable-input,
+.input-prepend .uneditable-input {
+ position: relative;
+ margin-bottom: 0;
+ *margin-left: 0;
+ font-size: 14px;
+ vertical-align: top;
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+.input-append input:focus,
+.input-prepend input:focus,
+.input-append select:focus,
+.input-prepend select:focus,
+.input-append .uneditable-input:focus,
+.input-prepend .uneditable-input:focus {
+ z-index: 2;
+}
+
+.input-append .add-on,
+.input-prepend .add-on {
+ display: inline-block;
+ width: auto;
+ height: 20px;
+ min-width: 16px;
+ padding: 4px 5px;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 20px;
+ text-align: center;
+ text-shadow: 0 1px 0 #ffffff;
+ background-color: #eeeeee;
+ border: 1px solid #ccc;
+}
+
+.input-append .add-on,
+.input-prepend .add-on,
+.input-append .btn,
+.input-prepend .btn {
+ vertical-align: top;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.input-append .active,
+.input-prepend .active {
+ background-color: #a9dba9;
+ border-color: #46a546;
+}
+
+.input-prepend .add-on,
+.input-prepend .btn {
+ margin-right: -1px;
+}
+
+.input-prepend .add-on:first-child,
+.input-prepend .btn:first-child {
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.input-append input,
+.input-append select,
+.input-append .uneditable-input {
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.input-append .add-on,
+.input-append .btn {
+ margin-left: -1px;
+}
+
+.input-append .add-on:last-child,
+.input-append .btn:last-child {
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+.input-prepend.input-append input,
+.input-prepend.input-append select,
+.input-prepend.input-append .uneditable-input {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.input-prepend.input-append .add-on:first-child,
+.input-prepend.input-append .btn:first-child {
+ margin-right: -1px;
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.input-prepend.input-append .add-on:last-child,
+.input-prepend.input-append .btn:last-child {
+ margin-left: -1px;
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+input.search-query {
+ padding-right: 14px;
+ padding-right: 4px \9;
+ padding-left: 14px;
+ padding-left: 4px \9;
+ /* IE7-8 doesn't have border-radius, so don't indent the padding */
+
+ margin-bottom: 0;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+
+/* Allow for input prepend/append in search forms */
+
+.form-search .input-append .search-query,
+.form-search .input-prepend .search-query {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.form-search .input-append .search-query {
+ -webkit-border-radius: 14px 0 0 14px;
+ -moz-border-radius: 14px 0 0 14px;
+ border-radius: 14px 0 0 14px;
+}
+
+.form-search .input-append .btn {
+ -webkit-border-radius: 0 14px 14px 0;
+ -moz-border-radius: 0 14px 14px 0;
+ border-radius: 0 14px 14px 0;
+}
+
+.form-search .input-prepend .search-query {
+ -webkit-border-radius: 0 14px 14px 0;
+ -moz-border-radius: 0 14px 14px 0;
+ border-radius: 0 14px 14px 0;
+}
+
+.form-search .input-prepend .btn {
+ -webkit-border-radius: 14px 0 0 14px;
+ -moz-border-radius: 14px 0 0 14px;
+ border-radius: 14px 0 0 14px;
+}
+
+.form-search input,
+.form-inline input,
+.form-horizontal input,
+.form-search textarea,
+.form-inline textarea,
+.form-horizontal textarea,
+.form-search select,
+.form-inline select,
+.form-horizontal select,
+.form-search .help-inline,
+.form-inline .help-inline,
+.form-horizontal .help-inline,
+.form-search .uneditable-input,
+.form-inline .uneditable-input,
+.form-horizontal .uneditable-input,
+.form-search .input-prepend,
+.form-inline .input-prepend,
+.form-horizontal .input-prepend,
+.form-search .input-append,
+.form-inline .input-append,
+.form-horizontal .input-append {
+ display: inline-block;
+ *display: inline;
+ margin-bottom: 0;
+ vertical-align: middle;
+ *zoom: 1;
+}
+
+.form-search .hide,
+.form-inline .hide,
+.form-horizontal .hide {
+ display: none;
+}
+
+.form-search label,
+.form-inline label,
+.form-search .btn-group,
+.form-inline .btn-group {
+ display: inline-block;
+}
+
+.form-search .input-append,
+.form-inline .input-append,
+.form-search .input-prepend,
+.form-inline .input-prepend {
+ margin-bottom: 0;
+}
+
+.form-search .radio,
+.form-search .checkbox,
+.form-inline .radio,
+.form-inline .checkbox {
+ padding-left: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+
+.form-search .radio input[type="radio"],
+.form-search .checkbox input[type="checkbox"],
+.form-inline .radio input[type="radio"],
+.form-inline .checkbox input[type="checkbox"] {
+ float: left;
+ margin-right: 3px;
+ margin-left: 0;
+}
+
+.control-group {
+ margin-bottom: 10px;
+}
+
+legend + .control-group {
+ margin-top: 20px;
+ -webkit-margin-top-collapse: separate;
+}
+
+.form-horizontal .control-group {
+ margin-bottom: 20px;
+ *zoom: 1;
+}
+
+.form-horizontal .control-group:before,
+.form-horizontal .control-group:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.form-horizontal .control-group:after {
+ clear: both;
+}
+
+.form-horizontal .control-label {
+ float: left;
+ width: 160px;
+ padding-top: 5px;
+ text-align: right;
+}
+
+.form-horizontal .controls {
+ *display: inline-block;
+ *padding-left: 20px;
+ margin-left: 180px;
+ *margin-left: 0;
+}
+
+.form-horizontal .controls:first-child {
+ *padding-left: 180px;
+}
+
+.form-horizontal .help-block {
+ margin-bottom: 0;
+}
+
+.form-horizontal input + .help-block,
+.form-horizontal select + .help-block,
+.form-horizontal textarea + .help-block {
+ margin-top: 10px;
+}
+
+.form-horizontal .form-actions {
+ padding-left: 180px;
+}
+
+table {
+ max-width: 100%;
+ background-color: transparent;
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+.table {
+ width: 100%;
+ margin-bottom: 20px;
+}
+
+.table th,
+.table td {
+ padding: 8px;
+ line-height: 20px;
+ text-align: left;
+ vertical-align: top;
+ border-top: 1px solid #dddddd;
+}
+
+.table th {
+ font-weight: bold;
+}
+
+.table thead th {
+ vertical-align: bottom;
+}
+
+.table caption + thead tr:first-child th,
+.table caption + thead tr:first-child td,
+.table colgroup + thead tr:first-child th,
+.table colgroup + thead tr:first-child td,
+.table thead:first-child tr:first-child th,
+.table thead:first-child tr:first-child td {
+ border-top: 0;
+}
+
+.table tbody + tbody {
+ border-top: 2px solid #dddddd;
+}
+
+.table-condensed th,
+.table-condensed td {
+ padding: 4px 5px;
+}
+
+.table-bordered {
+ border: 1px solid #dddddd;
+ border-collapse: separate;
+ *border-collapse: collapse;
+ border-left: 0;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.table-bordered th,
+.table-bordered td {
+ border-left: 1px solid #dddddd;
+}
+
+.table-bordered caption + thead tr:first-child th,
+.table-bordered caption + tbody tr:first-child th,
+.table-bordered caption + tbody tr:first-child td,
+.table-bordered colgroup + thead tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child td,
+.table-bordered thead:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child td {
+ border-top: 0;
+}
+
+.table-bordered thead:first-child tr:first-child th:first-child,
+.table-bordered tbody:first-child tr:first-child td:first-child {
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.table-bordered thead:first-child tr:first-child th:last-child,
+.table-bordered tbody:first-child tr:first-child td:last-child {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+}
+
+.table-bordered thead:last-child tr:last-child th:first-child,
+.table-bordered tbody:last-child tr:last-child td:first-child,
+.table-bordered tfoot:last-child tr:last-child td:first-child {
+ -webkit-border-radius: 0 0 0 4px;
+ -moz-border-radius: 0 0 0 4px;
+ border-radius: 0 0 0 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+}
+
+.table-bordered thead:last-child tr:last-child th:last-child,
+.table-bordered tbody:last-child tr:last-child td:last-child,
+.table-bordered tfoot:last-child tr:last-child td:last-child {
+ -webkit-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+}
+
+.table-bordered caption + thead tr:first-child th:first-child,
+.table-bordered caption + tbody tr:first-child td:first-child,
+.table-bordered colgroup + thead tr:first-child th:first-child,
+.table-bordered colgroup + tbody tr:first-child td:first-child {
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.table-bordered caption + thead tr:first-child th:last-child,
+.table-bordered caption + tbody tr:first-child td:last-child,
+.table-bordered colgroup + thead tr:first-child th:last-child,
+.table-bordered colgroup + tbody tr:first-child td:last-child {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.table-striped tbody tr:nth-child(odd) td,
+.table-striped tbody tr:nth-child(odd) th {
+ background-color: #f9f9f9;
+}
+
+.table-hover tbody tr:hover td,
+.table-hover tbody tr:hover th {
+ background-color: #f5f5f5;
+}
+
+table [class*=span],
+.row-fluid table [class*=span] {
+ display: table-cell;
+ float: none;
+ margin-left: 0;
+}
+
+.table .span1 {
+ float: none;
+ width: 44px;
+ margin-left: 0;
+}
+
+.table .span2 {
+ float: none;
+ width: 124px;
+ margin-left: 0;
+}
+
+.table .span3 {
+ float: none;
+ width: 204px;
+ margin-left: 0;
+}
+
+.table .span4 {
+ float: none;
+ width: 284px;
+ margin-left: 0;
+}
+
+.table .span5 {
+ float: none;
+ width: 364px;
+ margin-left: 0;
+}
+
+.table .span6 {
+ float: none;
+ width: 444px;
+ margin-left: 0;
+}
+
+.table .span7 {
+ float: none;
+ width: 524px;
+ margin-left: 0;
+}
+
+.table .span8 {
+ float: none;
+ width: 604px;
+ margin-left: 0;
+}
+
+.table .span9 {
+ float: none;
+ width: 684px;
+ margin-left: 0;
+}
+
+.table .span10 {
+ float: none;
+ width: 764px;
+ margin-left: 0;
+}
+
+.table .span11 {
+ float: none;
+ width: 844px;
+ margin-left: 0;
+}
+
+.table .span12 {
+ float: none;
+ width: 924px;
+ margin-left: 0;
+}
+
+.table .span13 {
+ float: none;
+ width: 1004px;
+ margin-left: 0;
+}
+
+.table .span14 {
+ float: none;
+ width: 1084px;
+ margin-left: 0;
+}
+
+.table .span15 {
+ float: none;
+ width: 1164px;
+ margin-left: 0;
+}
+
+.table .span16 {
+ float: none;
+ width: 1244px;
+ margin-left: 0;
+}
+
+.table .span17 {
+ float: none;
+ width: 1324px;
+ margin-left: 0;
+}
+
+.table .span18 {
+ float: none;
+ width: 1404px;
+ margin-left: 0;
+}
+
+.table .span19 {
+ float: none;
+ width: 1484px;
+ margin-left: 0;
+}
+
+.table .span20 {
+ float: none;
+ width: 1564px;
+ margin-left: 0;
+}
+
+.table .span21 {
+ float: none;
+ width: 1644px;
+ margin-left: 0;
+}
+
+.table .span22 {
+ float: none;
+ width: 1724px;
+ margin-left: 0;
+}
+
+.table .span23 {
+ float: none;
+ width: 1804px;
+ margin-left: 0;
+}
+
+.table .span24 {
+ float: none;
+ width: 1884px;
+ margin-left: 0;
+}
+
+.table tbody tr.success td {
+ background-color: #dff0d8;
+}
+
+.table tbody tr.error td {
+ background-color: #f2dede;
+}
+
+.table tbody tr.warning td {
+ background-color: #fcf8e3;
+}
+
+.table tbody tr.info td {
+ background-color: #d9edf7;
+}
+
+.table-hover tbody tr.success:hover td {
+ background-color: #d0e9c6;
+}
+
+.table-hover tbody tr.error:hover td {
+ background-color: #ebcccc;
+}
+
+.table-hover tbody tr.warning:hover td {
+ background-color: #faf2cc;
+}
+
+.table-hover tbody tr.info:hover td {
+ background-color: #c4e3f3;
+}
+
+[class^="icon-"],
+[class*=" icon-"] {
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ margin-top: 1px;
+ *margin-right: .3em;
+ line-height: 14px;
+ vertical-align: text-top;
+ background-image: url("../img/glyphicons-halflings.png");
+ background-position: 14px 14px;
+ background-repeat: no-repeat;
+}
+
+/* White icons with optional class, or on hover/active states of certain elements */
+
+.icon-white,
+.nav-tabs > .active > a > [class^="icon-"],
+.nav-tabs > .active > a > [class*=" icon-"],
+.nav-pills > .active > a > [class^="icon-"],
+.nav-pills > .active > a > [class*=" icon-"],
+.nav-list > .active > a > [class^="icon-"],
+.nav-list > .active > a > [class*=" icon-"],
+.navbar-inverse .nav > .active > a > [class^="icon-"],
+.navbar-inverse .nav > .active > a > [class*=" icon-"],
+.dropdown-menu > li > a:hover > [class^="icon-"],
+.dropdown-menu > li > a:hover > [class*=" icon-"],
+.dropdown-menu > .active > a > [class^="icon-"],
+.dropdown-menu > .active > a > [class*=" icon-"] {
+ background-image: url("../img/glyphicons-halflings-white.png");
+}
+
+.icon-glass {
+ background-position: 0 0;
+}
+
+.icon-music {
+ background-position: -24px 0;
+}
+
+.icon-search {
+ background-position: -48px 0;
+}
+
+.icon-envelope {
+ background-position: -72px 0;
+}
+
+.icon-heart {
+ background-position: -96px 0;
+}
+
+.icon-star {
+ background-position: -120px 0;
+}
+
+.icon-star-empty {
+ background-position: -144px 0;
+}
+
+.icon-user {
+ background-position: -168px 0;
+}
+
+.icon-film {
+ background-position: -192px 0;
+}
+
+.icon-th-large {
+ background-position: -216px 0;
+}
+
+.icon-th {
+ background-position: -240px 0;
+}
+
+.icon-th-list {
+ background-position: -264px 0;
+}
+
+.icon-ok {
+ background-position: -288px 0;
+}
+
+.icon-remove {
+ background-position: -312px 0;
+}
+
+.icon-zoom-in {
+ background-position: -336px 0;
+}
+
+.icon-zoom-out {
+ background-position: -360px 0;
+}
+
+.icon-off {
+ background-position: -384px 0;
+}
+
+.icon-signal {
+ background-position: -408px 0;
+}
+
+.icon-cog {
+ background-position: -432px 0;
+}
+
+.icon-trash {
+ background-position: -456px 0;
+}
+
+.icon-home {
+ background-position: 0 -24px;
+}
+
+.icon-file {
+ background-position: -24px -24px;
+}
+
+.icon-time {
+ background-position: -48px -24px;
+}
+
+.icon-road {
+ background-position: -72px -24px;
+}
+
+.icon-download-alt {
+ background-position: -96px -24px;
+}
+
+.icon-download {
+ background-position: -120px -24px;
+}
+
+.icon-upload {
+ background-position: -144px -24px;
+}
+
+.icon-inbox {
+ background-position: -168px -24px;
+}
+
+.icon-play-circle {
+ background-position: -192px -24px;
+}
+
+.icon-repeat {
+ background-position: -216px -24px;
+}
+
+.icon-refresh {
+ background-position: -240px -24px;
+}
+
+.icon-list-alt {
+ background-position: -264px -24px;
+}
+
+.icon-lock {
+ background-position: -287px -24px;
+}
+
+.icon-flag {
+ background-position: -312px -24px;
+}
+
+.icon-headphones {
+ background-position: -336px -24px;
+}
+
+.icon-volume-off {
+ background-position: -360px -24px;
+}
+
+.icon-volume-down {
+ background-position: -384px -24px;
+}
+
+.icon-volume-up {
+ background-position: -408px -24px;
+}
+
+.icon-qrcode {
+ background-position: -432px -24px;
+}
+
+.icon-barcode {
+ background-position: -456px -24px;
+}
+
+.icon-tag {
+ background-position: 0 -48px;
+}
+
+.icon-tags {
+ background-position: -25px -48px;
+}
+
+.icon-book {
+ background-position: -48px -48px;
+}
+
+.icon-bookmark {
+ background-position: -72px -48px;
+}
+
+.icon-print {
+ background-position: -96px -48px;
+}
+
+.icon-camera {
+ background-position: -120px -48px;
+}
+
+.icon-font {
+ background-position: -144px -48px;
+}
+
+.icon-bold {
+ background-position: -167px -48px;
+}
+
+.icon-italic {
+ background-position: -192px -48px;
+}
+
+.icon-text-height {
+ background-position: -216px -48px;
+}
+
+.icon-text-width {
+ background-position: -240px -48px;
+}
+
+.icon-align-left {
+ background-position: -264px -48px;
+}
+
+.icon-align-center {
+ background-position: -288px -48px;
+}
+
+.icon-align-right {
+ background-position: -312px -48px;
+}
+
+.icon-align-justify {
+ background-position: -336px -48px;
+}
+
+.icon-list {
+ background-position: -360px -48px;
+}
+
+.icon-indent-left {
+ background-position: -384px -48px;
+}
+
+.icon-indent-right {
+ background-position: -408px -48px;
+}
+
+.icon-facetime-video {
+ background-position: -432px -48px;
+}
+
+.icon-picture {
+ background-position: -456px -48px;
+}
+
+.icon-pencil {
+ background-position: 0 -72px;
+}
+
+.icon-map-marker {
+ background-position: -24px -72px;
+}
+
+.icon-adjust {
+ background-position: -48px -72px;
+}
+
+.icon-tint {
+ background-position: -72px -72px;
+}
+
+.icon-edit {
+ background-position: -96px -72px;
+}
+
+.icon-share {
+ background-position: -120px -72px;
+}
+
+.icon-check {
+ background-position: -144px -72px;
+}
+
+.icon-move {
+ background-position: -168px -72px;
+}
+
+.icon-step-backward {
+ background-position: -192px -72px;
+}
+
+.icon-fast-backward {
+ background-position: -216px -72px;
+}
+
+.icon-backward {
+ background-position: -240px -72px;
+}
+
+.icon-play {
+ background-position: -264px -72px;
+}
+
+.icon-pause {
+ background-position: -288px -72px;
+}
+
+.icon-stop {
+ background-position: -312px -72px;
+}
+
+.icon-forward {
+ background-position: -336px -72px;
+}
+
+.icon-fast-forward {
+ background-position: -360px -72px;
+}
+
+.icon-step-forward {
+ background-position: -384px -72px;
+}
+
+.icon-eject {
+ background-position: -408px -72px;
+}
+
+.icon-chevron-left {
+ background-position: -432px -72px;
+}
+
+.icon-chevron-right {
+ background-position: -456px -72px;
+}
+
+.icon-plus-sign {
+ background-position: 0 -96px;
+}
+
+.icon-minus-sign {
+ background-position: -24px -96px;
+}
+
+.icon-remove-sign {
+ background-position: -48px -96px;
+}
+
+.icon-ok-sign {
+ background-position: -72px -96px;
+}
+
+.icon-question-sign {
+ background-position: -96px -96px;
+}
+
+.icon-info-sign {
+ background-position: -120px -96px;
+}
+
+.icon-screenshot {
+ background-position: -144px -96px;
+}
+
+.icon-remove-circle {
+ background-position: -168px -96px;
+}
+
+.icon-ok-circle {
+ background-position: -192px -96px;
+}
+
+.icon-ban-circle {
+ background-position: -216px -96px;
+}
+
+.icon-arrow-left {
+ background-position: -240px -96px;
+}
+
+.icon-arrow-right {
+ background-position: -264px -96px;
+}
+
+.icon-arrow-up {
+ background-position: -289px -96px;
+}
+
+.icon-arrow-down {
+ background-position: -312px -96px;
+}
+
+.icon-share-alt {
+ background-position: -336px -96px;
+}
+
+.icon-resize-full {
+ background-position: -360px -96px;
+}
+
+.icon-resize-small {
+ background-position: -384px -96px;
+}
+
+.icon-plus {
+ background-position: -408px -96px;
+}
+
+.icon-minus {
+ background-position: -433px -96px;
+}
+
+.icon-asterisk {
+ background-position: -456px -96px;
+}
+
+.icon-exclamation-sign {
+ background-position: 0 -120px;
+}
+
+.icon-gift {
+ background-position: -24px -120px;
+}
+
+.icon-leaf {
+ background-position: -48px -120px;
+}
+
+.icon-fire {
+ background-position: -72px -120px;
+}
+
+.icon-eye-open {
+ background-position: -96px -120px;
+}
+
+.icon-eye-close {
+ background-position: -120px -120px;
+}
+
+.icon-warning-sign {
+ background-position: -144px -120px;
+}
+
+.icon-plane {
+ background-position: -168px -120px;
+}
+
+.icon-calendar {
+ background-position: -192px -120px;
+}
+
+.icon-random {
+ width: 16px;
+ background-position: -216px -120px;
+}
+
+.icon-comment {
+ background-position: -240px -120px;
+}
+
+.icon-magnet {
+ background-position: -264px -120px;
+}
+
+.icon-chevron-up {
+ background-position: -288px -120px;
+}
+
+.icon-chevron-down {
+ background-position: -313px -119px;
+}
+
+.icon-retweet {
+ background-position: -336px -120px;
+}
+
+.icon-shopping-cart {
+ background-position: -360px -120px;
+}
+
+.icon-folder-close {
+ background-position: -384px -120px;
+}
+
+.icon-folder-open {
+ width: 16px;
+ background-position: -408px -120px;
+}
+
+.icon-resize-vertical {
+ background-position: -432px -119px;
+}
+
+.icon-resize-horizontal {
+ background-position: -456px -118px;
+}
+
+.icon-hdd {
+ background-position: 0 -144px;
+}
+
+.icon-bullhorn {
+ background-position: -24px -144px;
+}
+
+.icon-bell {
+ background-position: -48px -144px;
+}
+
+.icon-certificate {
+ background-position: -72px -144px;
+}
+
+.icon-thumbs-up {
+ background-position: -96px -144px;
+}
+
+.icon-thumbs-down {
+ background-position: -120px -144px;
+}
+
+.icon-hand-right {
+ background-position: -144px -144px;
+}
+
+.icon-hand-left {
+ background-position: -168px -144px;
+}
+
+.icon-hand-up {
+ background-position: -192px -144px;
+}
+
+.icon-hand-down {
+ background-position: -216px -144px;
+}
+
+.icon-circle-arrow-right {
+ background-position: -240px -144px;
+}
+
+.icon-circle-arrow-left {
+ background-position: -264px -144px;
+}
+
+.icon-circle-arrow-up {
+ background-position: -288px -144px;
+}
+
+.icon-circle-arrow-down {
+ background-position: -312px -144px;
+}
+
+.icon-globe {
+ background-position: -336px -144px;
+}
+
+.icon-wrench {
+ background-position: -360px -144px;
+}
+
+.icon-tasks {
+ background-position: -384px -144px;
+}
+
+.icon-filter {
+ background-position: -408px -144px;
+}
+
+.icon-briefcase {
+ background-position: -432px -144px;
+}
+
+.icon-fullscreen {
+ background-position: -456px -144px;
+}
+
+.dropup,
+.dropdown {
+ position: relative;
+}
+
+.dropdown-toggle {
+ *margin-bottom: -3px;
+}
+
+.dropdown-toggle:active,
+.open .dropdown-toggle {
+ outline: 0;
+}
+
+.caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ vertical-align: top;
+ border-top: 4px solid #000000;
+ border-right: 4px solid transparent;
+ border-left: 4px solid transparent;
+ content: "";
+}
+
+.dropdown .caret {
+ margin-top: 8px;
+ margin-left: 2px;
+}
+
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ list-style: none;
+ background-color: #ffffff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ *border-right-width: 2px;
+ *border-bottom-width: 2px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+.dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+
+.dropdown-menu .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 9px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+
+.dropdown-menu a {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 20px;
+ color: #333333;
+ white-space: nowrap;
+}
+
+.dropdown-menu li > a:hover,
+.dropdown-menu li > a:focus,
+.dropdown-submenu:hover > a {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: #0088cc;
+ background-color: #0081c2;
+ background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -o-linear-gradient(top, #0088cc, #0077b3);
+ background-image: linear-gradient(to bottom, #0088cc, #0077b3);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+}
+
+.dropdown-menu .active > a,
+.dropdown-menu .active > a:hover {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: #0088cc;
+ background-color: #0081c2;
+ background-image: linear-gradient(to bottom, #0088cc, #0077b3);
+ background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -o-linear-gradient(top, #0088cc, #0077b3);
+ background-repeat: repeat-x;
+ outline: 0;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+}
+
+.dropdown-menu .disabled > a,
+.dropdown-menu .disabled > a:hover {
+ color: #999999;
+}
+
+.dropdown-menu .disabled > a:hover {
+ text-decoration: none;
+ cursor: default;
+ background-color: transparent;
+}
+
+.open {
+ *z-index: 1000;
+}
+
+.open > .dropdown-menu {
+ display: block;
+}
+
+.pull-right > .dropdown-menu {
+ right: 0;
+ left: auto;
+}
+
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+ border-top: 0;
+ border-bottom: 4px solid #000000;
+ content: "";
+}
+
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 1px;
+}
+
+.dropdown-submenu {
+ position: relative;
+}
+
+.dropdown-submenu > .dropdown-menu {
+ top: 0;
+ left: 100%;
+ margin-top: -6px;
+ margin-left: -1px;
+ -webkit-border-radius: 0 6px 6px 6px;
+ -moz-border-radius: 0 6px 6px 6px;
+ border-radius: 0 6px 6px 6px;
+}
+
+.dropdown-submenu:hover > .dropdown-menu {
+ display: block;
+}
+
+.dropdown-submenu > a:after {
+ display: block;
+ float: right;
+ width: 0;
+ height: 0;
+ margin-top: 5px;
+ margin-right: -10px;
+ border-color: transparent;
+ border-left-color: #cccccc;
+ border-style: solid;
+ border-width: 5px 0 5px 5px;
+ content: " ";
+}
+
+.dropdown-submenu:hover > a:after {
+ border-left-color: #ffffff;
+}
+
+.dropdown .dropdown-menu .nav-header {
+ padding-right: 20px;
+ padding-left: 20px;
+}
+
+.typeahead {
+ margin-top: 2px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border: 1px solid #e3e3e3;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+
+.well blockquote {
+ border-color: #ddd;
+ border-color: rgba(0, 0, 0, 0.15);
+}
+
+.well-large {
+ padding: 24px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.well-small {
+ padding: 9px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity 0.15s linear;
+ -moz-transition: opacity 0.15s linear;
+ -o-transition: opacity 0.15s linear;
+ transition: opacity 0.15s linear;
+}
+
+.fade.in {
+ opacity: 1;
+}
+
+.collapse {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition: height 0.35s ease;
+ -moz-transition: height 0.35s ease;
+ -o-transition: height 0.35s ease;
+ transition: height 0.35s ease;
+}
+
+.collapse.in {
+ height: auto;
+}
+
+.close {
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 20px;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+}
+
+.close:hover {
+ color: #000000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 0.4;
+ filter: alpha(opacity=40);
+}
+
+button.close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+}
+
+.btn {
+ display: inline-block;
+ *display: inline;
+ padding: 4px 14px;
+ margin-bottom: 0;
+ *margin-left: .3em;
+ font-size: 14px;
+ line-height: 20px;
+ *line-height: 20px;
+ color: #333333;
+ text-align: center;
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
+ vertical-align: middle;
+ cursor: pointer;
+ background-color: #f5f5f5;
+ *background-color: #e6e6e6;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
+ background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
+ background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
+ background-repeat: repeat-x;
+ border: 1px solid #bbbbbb;
+ *border: 0;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ border-color: #e6e6e6 #e6e6e6 #bfbfbf;
+ border-bottom-color: #a2a2a2;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ *zoom: 1;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn:hover,
+.btn:active,
+.btn.active,
+.btn.disabled,
+.btn[disabled] {
+ color: #333333;
+ background-color: #e6e6e6;
+ *background-color: #d9d9d9;
+}
+
+.btn:active,
+.btn.active {
+ background-color: #cccccc \9;
+}
+
+.btn:first-child {
+ *margin-left: 0;
+}
+
+.btn:hover {
+ color: #333333;
+ text-decoration: none;
+ background-color: #e6e6e6;
+ *background-color: #d9d9d9;
+ /* Buttons in IE7 don't get borders, so darken on hover */
+
+ background-position: 0 -15px;
+ -webkit-transition: background-position 0.1s linear;
+ -moz-transition: background-position 0.1s linear;
+ -o-transition: background-position 0.1s linear;
+ transition: background-position 0.1s linear;
+}
+
+.btn:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+.btn.active,
+.btn:active {
+ background-color: #e6e6e6;
+ background-color: #d9d9d9 \9;
+ background-image: none;
+ outline: 0;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn.disabled,
+.btn[disabled] {
+ cursor: default;
+ background-color: #e6e6e6;
+ background-image: none;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.btn-large {
+ padding: 9px 14px;
+ font-size: 16px;
+ line-height: normal;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.btn-large [class^="icon-"] {
+ margin-top: 2px;
+}
+
+.btn-small {
+ padding: 3px 9px;
+ font-size: 12px;
+ line-height: 18px;
+}
+
+.btn-small [class^="icon-"] {
+ margin-top: 0;
+}
+
+.btn-mini {
+ padding: 2px 6px;
+ font-size: 11px;
+ line-height: 17px;
+}
+
+.btn-block {
+ display: block;
+ width: 100%;
+ padding-right: 0;
+ padding-left: 0;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.btn-block + .btn-block {
+ margin-top: 5px;
+}
+
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
+}
+
+.btn-primary.active,
+.btn-warning.active,
+.btn-danger.active,
+.btn-success.active,
+.btn-info.active,
+.btn-inverse.active {
+ color: rgba(255, 255, 255, 0.75);
+}
+
+.btn {
+ border-color: #c5c5c5;
+ border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25);
+}
+
+.btn-primary {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #006dcc;
+ *background-color: #0044cc;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -o-linear-gradient(top, #0088cc, #0044cc);
+ background-image: linear-gradient(to bottom, #0088cc, #0044cc);
+ background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
+ background-repeat: repeat-x;
+ border-color: #0044cc #0044cc #002a80;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-primary:hover,
+.btn-primary:active,
+.btn-primary.active,
+.btn-primary.disabled,
+.btn-primary[disabled] {
+ color: #ffffff;
+ background-color: #0044cc;
+ *background-color: #003bb3;
+}
+
+.btn-primary:active,
+.btn-primary.active {
+ background-color: #003399 \9;
+}
+
+.btn-warning {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #faa732;
+ *background-color: #f89406;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
+ background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
+ background-image: -o-linear-gradient(top, #fbb450, #f89406);
+ background-image: linear-gradient(to bottom, #fbb450, #f89406);
+ background-image: -moz-linear-gradient(top, #fbb450, #f89406);
+ background-repeat: repeat-x;
+ border-color: #f89406 #f89406 #ad6704;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-warning:hover,
+.btn-warning:active,
+.btn-warning.active,
+.btn-warning.disabled,
+.btn-warning[disabled] {
+ color: #ffffff;
+ background-color: #f89406;
+ *background-color: #df8505;
+}
+
+.btn-warning:active,
+.btn-warning.active {
+ background-color: #c67605 \9;
+}
+
+.btn-danger {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #da4f49;
+ *background-color: #bd362f;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
+ background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: linear-gradient(to bottom, #ee5f5b, #bd362f);
+ background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
+ background-repeat: repeat-x;
+ border-color: #bd362f #bd362f #802420;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-danger:hover,
+.btn-danger:active,
+.btn-danger.active,
+.btn-danger.disabled,
+.btn-danger[disabled] {
+ color: #ffffff;
+ background-color: #bd362f;
+ *background-color: #a9302a;
+}
+
+.btn-danger:active,
+.btn-danger.active {
+ background-color: #942a25 \9;
+}
+
+.btn-success {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #5bb75b;
+ *background-color: #51a351;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
+ background-image: -webkit-linear-gradient(top, #62c462, #51a351);
+ background-image: -o-linear-gradient(top, #62c462, #51a351);
+ background-image: linear-gradient(to bottom, #62c462, #51a351);
+ background-image: -moz-linear-gradient(top, #62c462, #51a351);
+ background-repeat: repeat-x;
+ border-color: #51a351 #51a351 #387038;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-success:hover,
+.btn-success:active,
+.btn-success.active,
+.btn-success.disabled,
+.btn-success[disabled] {
+ color: #ffffff;
+ background-color: #51a351;
+ *background-color: #499249;
+}
+
+.btn-success:active,
+.btn-success.active {
+ background-color: #408140 \9;
+}
+
+.btn-info {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #49afcd;
+ *background-color: #2f96b4;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));
+ background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: linear-gradient(to bottom, #5bc0de, #2f96b4);
+ background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
+ background-repeat: repeat-x;
+ border-color: #2f96b4 #2f96b4 #1f6377;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-info:hover,
+.btn-info:active,
+.btn-info.active,
+.btn-info.disabled,
+.btn-info[disabled] {
+ color: #ffffff;
+ background-color: #2f96b4;
+ *background-color: #2a85a0;
+}
+
+.btn-info:active,
+.btn-info.active {
+ background-color: #24748c \9;
+}
+
+.btn-inverse {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #363636;
+ *background-color: #222222;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));
+ background-image: -webkit-linear-gradient(top, #444444, #222222);
+ background-image: -o-linear-gradient(top, #444444, #222222);
+ background-image: linear-gradient(to bottom, #444444, #222222);
+ background-image: -moz-linear-gradient(top, #444444, #222222);
+ background-repeat: repeat-x;
+ border-color: #222222 #222222 #000000;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-inverse:hover,
+.btn-inverse:active,
+.btn-inverse.active,
+.btn-inverse.disabled,
+.btn-inverse[disabled] {
+ color: #ffffff;
+ background-color: #222222;
+ *background-color: #151515;
+}
+
+.btn-inverse:active,
+.btn-inverse.active {
+ background-color: #080808 \9;
+}
+
+button.btn,
+input[type="submit"].btn {
+ *padding-top: 3px;
+ *padding-bottom: 3px;
+}
+
+button.btn::-moz-focus-inner,
+input[type="submit"].btn::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
+button.btn.btn-large,
+input[type="submit"].btn.btn-large {
+ *padding-top: 7px;
+ *padding-bottom: 7px;
+}
+
+button.btn.btn-small,
+input[type="submit"].btn.btn-small {
+ *padding-top: 3px;
+ *padding-bottom: 3px;
+}
+
+button.btn.btn-mini,
+input[type="submit"].btn.btn-mini {
+ *padding-top: 1px;
+ *padding-bottom: 1px;
+}
+
+.btn-link,
+.btn-link:active,
+.btn-link[disabled] {
+ background-color: transparent;
+ background-image: none;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.btn-link {
+ color: #0088cc;
+ cursor: pointer;
+ border-color: transparent;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-link:hover {
+ color: #005580;
+ text-decoration: underline;
+ background-color: transparent;
+}
+
+.btn-link[disabled]:hover {
+ color: #333333;
+ text-decoration: none;
+}
+
+.btn-group {
+ position: relative;
+ *margin-left: .3em;
+ font-size: 0;
+ white-space: nowrap;
+ vertical-align: middle;
+}
+
+.btn-group:first-child {
+ *margin-left: 0;
+}
+
+.btn-group + .btn-group {
+ margin-left: 5px;
+}
+
+.btn-toolbar {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ font-size: 0;
+}
+
+.btn-toolbar .btn-group {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+}
+
+.btn-toolbar .btn + .btn,
+.btn-toolbar .btn-group + .btn,
+.btn-toolbar .btn + .btn-group {
+ margin-left: 5px;
+}
+
+.btn-group > .btn {
+ position: relative;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-group > .btn + .btn {
+ margin-left: -1px;
+}
+
+.btn-group > .btn,
+.btn-group > .dropdown-menu {
+ font-size: 14px;
+}
+
+.btn-group > .btn-mini {
+ font-size: 11px;
+}
+
+.btn-group > .btn-small {
+ font-size: 12px;
+}
+
+.btn-group > .btn-large {
+ font-size: 16px;
+}
+
+.btn-group > .btn:first-child {
+ margin-left: 0;
+ -webkit-border-bottom-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.btn-group > .btn:last-child,
+.btn-group > .dropdown-toggle {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -moz-border-radius-bottomright: 4px;
+}
+
+.btn-group > .btn.large:first-child {
+ margin-left: 0;
+ -webkit-border-bottom-left-radius: 6px;
+ border-bottom-left-radius: 6px;
+ -webkit-border-top-left-radius: 6px;
+ border-top-left-radius: 6px;
+ -moz-border-radius-bottomleft: 6px;
+ -moz-border-radius-topleft: 6px;
+}
+
+.btn-group > .btn.large:last-child,
+.btn-group > .large.dropdown-toggle {
+ -webkit-border-top-right-radius: 6px;
+ border-top-right-radius: 6px;
+ -webkit-border-bottom-right-radius: 6px;
+ border-bottom-right-radius: 6px;
+ -moz-border-radius-topright: 6px;
+ -moz-border-radius-bottomright: 6px;
+}
+
+.btn-group > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group > .btn:active,
+.btn-group > .btn.active {
+ z-index: 2;
+}
+
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+
+.btn-group > .btn + .dropdown-toggle {
+ *padding-top: 5px;
+ padding-right: 8px;
+ *padding-bottom: 5px;
+ padding-left: 8px;
+ -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-group > .btn-mini + .dropdown-toggle {
+ *padding-top: 2px;
+ padding-right: 5px;
+ *padding-bottom: 2px;
+ padding-left: 5px;
+}
+
+.btn-group > .btn-small + .dropdown-toggle {
+ *padding-top: 5px;
+ *padding-bottom: 4px;
+}
+
+.btn-group > .btn-large + .dropdown-toggle {
+ *padding-top: 7px;
+ padding-right: 12px;
+ *padding-bottom: 7px;
+ padding-left: 12px;
+}
+
+.btn-group.open .dropdown-toggle {
+ background-image: none;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-group.open .btn.dropdown-toggle {
+ background-color: #e6e6e6;
+}
+
+.btn-group.open .btn-primary.dropdown-toggle {
+ background-color: #0044cc;
+}
+
+.btn-group.open .btn-warning.dropdown-toggle {
+ background-color: #f89406;
+}
+
+.btn-group.open .btn-danger.dropdown-toggle {
+ background-color: #bd362f;
+}
+
+.btn-group.open .btn-success.dropdown-toggle {
+ background-color: #51a351;
+}
+
+.btn-group.open .btn-info.dropdown-toggle {
+ background-color: #2f96b4;
+}
+
+.btn-group.open .btn-inverse.dropdown-toggle {
+ background-color: #222222;
+}
+
+.btn .caret {
+ margin-top: 8px;
+ margin-left: 0;
+}
+
+.btn-mini .caret,
+.btn-small .caret,
+.btn-large .caret {
+ margin-top: 6px;
+}
+
+.btn-large .caret {
+ border-top-width: 5px;
+ border-right-width: 5px;
+ border-left-width: 5px;
+}
+
+.dropup .btn-large .caret {
+ border-top: 0;
+ border-bottom: 5px solid #000000;
+}
+
+.btn-primary .caret,
+.btn-warning .caret,
+.btn-danger .caret,
+.btn-info .caret,
+.btn-success .caret,
+.btn-inverse .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.btn-group-vertical {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+}
+
+.btn-group-vertical .btn {
+ display: block;
+ float: none;
+ width: 100%;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-group-vertical .btn + .btn {
+ margin-top: -1px;
+ margin-left: 0;
+}
+
+.btn-group-vertical .btn:first-child {
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+
+.btn-group-vertical .btn:last-child {
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+
+.btn-group-vertical .btn-large:first-child {
+ -webkit-border-radius: 6px 6px 0 0;
+ -moz-border-radius: 6px 6px 0 0;
+ border-radius: 6px 6px 0 0;
+}
+
+.btn-group-vertical .btn-large:last-child {
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+}
+
+.alert {
+ padding: 8px 35px 8px 14px;
+ margin-bottom: 20px;
+ color: #c09853;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ background-color: #fcf8e3;
+ border: 1px solid #fbeed5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.alert h4 {
+ margin: 0;
+}
+
+.alert .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ line-height: 20px;
+}
+
+.alert-success {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+}
+
+.alert-danger,
+.alert-error {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #eed3d7;
+}
+
+.alert-info {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+}
+
+.alert-block {
+ padding-top: 14px;
+ padding-bottom: 14px;
+}
+
+.alert-block > p,
+.alert-block > ul {
+ margin-bottom: 0;
+}
+
+.alert-block p + p {
+ margin-top: 5px;
+}
+
+.nav {
+ margin-bottom: 20px;
+ margin-left: 0;
+ list-style: none;
+}
+
+.nav > li > a {
+ display: block;
+}
+
+.nav > li > a:hover {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+
+.nav > .pull-right {
+ float: right;
+}
+
+.nav-header {
+ display: block;
+ padding: 3px 15px;
+ font-size: 11px;
+ font-weight: bold;
+ line-height: 20px;
+ color: #999999;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ text-transform: uppercase;
+}
+
+.nav li + .nav-header {
+ margin-top: 9px;
+}
+
+.nav-list {
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-bottom: 0;
+}
+
+.nav-list > li > a,
+.nav-list .nav-header {
+ margin-right: -15px;
+ margin-left: -15px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+}
+
+.nav-list > li > a {
+ padding: 3px 15px;
+}
+
+.nav-list > .active > a,
+.nav-list > .active > a:hover {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
+ background-color: #0088cc;
+}
+
+.nav-list [class^="icon-"] {
+ margin-right: 2px;
+}
+
+.nav-list .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 9px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+
+.nav-tabs,
+.nav-pills {
+ *zoom: 1;
+}
+
+.nav-tabs:before,
+.nav-pills:before,
+.nav-tabs:after,
+.nav-pills:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.nav-tabs:after,
+.nav-pills:after {
+ clear: both;
+}
+
+.nav-tabs > li,
+.nav-pills > li {
+ float: left;
+}
+
+.nav-tabs > li > a,
+.nav-pills > li > a {
+ padding-right: 12px;
+ padding-left: 12px;
+ margin-right: 2px;
+ line-height: 14px;
+}
+
+.nav-tabs {
+ border-bottom: 1px solid #ddd;
+}
+
+.nav-tabs > li {
+ margin-bottom: -1px;
+}
+
+.nav-tabs > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ line-height: 20px;
+ border: 1px solid transparent;
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+
+.nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover {
+ color: #fff;
+ cursor: default;
+ background-color: #0093FF;
+ border: 1px solid #0093FF !important;
+ border-bottom-color: transparent;
+}
+
+.nav-pills > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ margin-top: 2px;
+ margin-bottom: 2px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.nav-pills > .active > a,
+.nav-pills > .active > a:hover {
+ color: #ffffff;
+ background-color: #0088cc;
+}
+
+.nav-stacked > li {
+ float: none;
+}
+
+.nav-stacked > li > a {
+ margin-right: 0;
+}
+
+.nav-tabs.nav-stacked {
+ border-bottom: 0;
+}
+
+.nav-tabs.nav-stacked > li > a {
+ border: 1px solid #ddd;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.nav-tabs.nav-stacked > li:first-child > a {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.nav-tabs.nav-stacked > li:last-child > a {
+ -webkit-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -moz-border-radius-bottomleft: 4px;
+}
+
+.nav-tabs.nav-stacked > li > a:hover {
+ z-index: 2;
+ border-color: #ddd;
+}
+
+.nav-pills.nav-stacked > li > a {
+ margin-bottom: 3px;
+}
+
+.nav-pills.nav-stacked > li:last-child > a {
+ margin-bottom: 1px;
+}
+
+.nav-tabs .dropdown-menu {
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+}
+
+.nav-pills .dropdown-menu {
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.nav .dropdown-toggle .caret {
+ margin-top: 6px;
+ border-top-color: #0088cc;
+ border-bottom-color: #0088cc;
+}
+
+.nav .dropdown-toggle:hover .caret {
+ border-top-color: #005580;
+ border-bottom-color: #005580;
+}
+
+/* move down carets for tabs */
+
+.nav-tabs .dropdown-toggle .caret {
+ margin-top: 8px;
+}
+
+.nav .active .dropdown-toggle .caret {
+ border-top-color: #fff;
+ border-bottom-color: #fff;
+}
+
+.nav-tabs .active .dropdown-toggle .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+
+.nav > .dropdown.active > a:hover {
+ cursor: pointer;
+}
+
+.nav-tabs .open .dropdown-toggle,
+.nav-pills .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover {
+ color: #ffffff;
+ background-color: #999999;
+ border-color: #999999;
+}
+
+.nav li.dropdown.open .caret,
+.nav li.dropdown.open.active .caret,
+.nav li.dropdown.open a:hover .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+ opacity: 1;
+ filter: alpha(opacity=100);
+}
+
+.tabs-stacked .open > a:hover {
+ border-color: #999999;
+}
+
+.tabbable {
+ *zoom: 1;
+}
+
+.tabbable:before,
+.tabbable:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.tabbable:after {
+ clear: both;
+}
+
+.tab-content {
+ overflow: auto;
+}
+
+.tabs-below > .nav-tabs,
+.tabs-right > .nav-tabs,
+.tabs-left > .nav-tabs {
+ border-bottom: 0;
+}
+
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+ display: none;
+}
+
+.tab-content > .active,
+.pill-content > .active {
+ display: block;
+}
+
+.tabs-below > .nav-tabs {
+ border-top: 1px solid #ddd;
+}
+
+.tabs-below > .nav-tabs > li {
+ margin-top: -1px;
+ margin-bottom: 0;
+}
+
+.tabs-below > .nav-tabs > li > a {
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+
+.tabs-below > .nav-tabs > li > a:hover {
+ border-top-color: #ddd;
+ border-bottom-color: transparent;
+}
+
+.tabs-below > .nav-tabs > .active > a,
+.tabs-below > .nav-tabs > .active > a:hover {
+ border-color: transparent #ddd #ddd #ddd;
+}
+
+.tabs-left > .nav-tabs > li,
+.tabs-right > .nav-tabs > li {
+ float: none;
+}
+
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+ min-width: 74px;
+ margin-right: 0;
+ margin-bottom: 3px;
+}
+
+.tabs-left > .nav-tabs {
+ float: left;
+ margin-right: 19px;
+ border-right: 1px solid #ddd;
+}
+
+.tabs-left > .nav-tabs > li > a {
+ margin-right: -1px;
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
+}
+
+.tabs-left > .nav-tabs > li > a:hover {
+ border-color: #eeeeee #dddddd #eeeeee #eeeeee;
+}
+
+.tabs-left > .nav-tabs .active > a,
+.tabs-left > .nav-tabs .active > a:hover {
+ border-color: #ddd transparent #ddd #ddd;
+ *border-right-color: #ffffff;
+}
+
+.tabs-right > .nav-tabs {
+ float: right;
+ margin-left: 19px;
+ border-left: 1px solid #ddd;
+}
+
+.tabs-right > .nav-tabs > li > a {
+ margin-left: -1px;
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+
+.tabs-right > .nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #eeeeee #dddddd;
+}
+
+.tabs-right > .nav-tabs .active > a,
+.tabs-right > .nav-tabs .active > a:hover {
+ border-color: #ddd #ddd #ddd transparent;
+ *border-left-color: #ffffff;
+}
+
+.nav > .disabled > a {
+ color: #999999;
+}
+
+.nav > .disabled > a:hover {
+ text-decoration: none;
+ cursor: default;
+ background-color: transparent;
+}
+
+.navbar {
+ *position: relative;
+ *z-index: 2;
+ margin-bottom: 20px;
+ overflow: visible;
+ color: #777777;
+}
+
+.navbar-inner {
+ min-height: 40px;
+ padding-right: 20px;
+ padding-left: 20px;
+ background-color: #fafafa;
+ background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));
+ background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: -o-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: linear-gradient(to bottom, #ffffff, #f2f2f2);
+ background-repeat: repeat-x;
+ border: 1px solid #d4d4d4;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
+ *zoom: 1;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+}
+
+.navbar-inner:before,
+.navbar-inner:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.navbar-inner:after {
+ clear: both;
+}
+
+.navbar .container {
+ width: auto;
+}
+
+.nav-collapse.collapse {
+ height: auto;
+}
+
+.navbar .brand {
+ display: block;
+ float: left;
+ padding: 10px 20px 10px;
+ margin-left: -20px;
+ font-size: 20px;
+ font-weight: 200;
+ color: #777777;
+ text-shadow: 0 1px 0 #ffffff;
+}
+
+.navbar .brand:hover {
+ text-decoration: none;
+}
+
+.navbar-text {
+ margin-bottom: 0;
+ line-height: 40px;
+}
+
+.navbar-link {
+ color: #777777;
+}
+
+.navbar-link:hover {
+ color: #333333;
+}
+
+.navbar .divider-vertical {
+ height: 40px;
+ margin: 0 9px;
+ border-right: 1px solid #ffffff;
+ border-left: 1px solid #f2f2f2;
+}
+
+.navbar .btn,
+.navbar .btn-group {
+ margin-top: 5px;
+}
+
+.navbar .btn-group .btn,
+.navbar .input-prepend .btn,
+.navbar .input-append .btn {
+ margin-top: 0;
+}
+
+.navbar-form {
+ margin-bottom: 0;
+ *zoom: 1;
+}
+
+.navbar-form:before,
+.navbar-form:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.navbar-form:after {
+ clear: both;
+}
+
+.navbar-form input,
+.navbar-form select,
+.navbar-form .radio,
+.navbar-form .checkbox {
+ margin-top: 5px;
+}
+
+.navbar-form input,
+.navbar-form select,
+.navbar-form .btn {
+ display: inline-block;
+ margin-bottom: 0;
+}
+
+.navbar-form input[type="image"],
+.navbar-form input[type="checkbox"],
+.navbar-form input[type="radio"] {
+ margin-top: 3px;
+}
+
+.navbar-form .input-append,
+.navbar-form .input-prepend {
+ margin-top: 6px;
+ white-space: nowrap;
+}
+
+.navbar-form .input-append input,
+.navbar-form .input-prepend input {
+ margin-top: 0;
+}
+
+.navbar-search {
+ position: relative;
+ float: left;
+ margin-top: 5px;
+ margin-bottom: 0;
+}
+
+.navbar-search .search-query {
+ padding: 4px 14px;
+ margin-bottom: 0;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+
+.navbar-static-top {
+ position: static;
+ width: 100%;
+ margin-bottom: 0;
+}
+
+.navbar-static-top .navbar-inner {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+ margin-bottom: 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+ border-width: 0 0 1px;
+}
+
+.navbar-fixed-bottom .navbar-inner {
+ border-width: 1px 0 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-fixed-bottom .navbar-inner {
+ padding-right: 0;
+ padding-left: 0;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+ width: 940px;
+}
+
+.navbar-fixed-top {
+ top: 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
+}
+
+.navbar-fixed-bottom {
+ bottom: 0;
+}
+
+.navbar-fixed-bottom .navbar-inner {
+ -webkit-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);
+}
+
+.navbar .nav {
+ position: relative;
+ left: 0;
+ display: block;
+ float: left;
+ margin: 0 10px 0 0;
+}
+
+.navbar .nav.pull-right {
+ float: right;
+ margin-right: 0;
+}
+
+.navbar .nav > li {
+ float: left;
+}
+
+.navbar .nav > li > a {
+ float: none;
+ padding: 10px 15px 10px;
+ color: #777777;
+ text-decoration: none;
+ text-shadow: 0 1px 0 #ffffff;
+}
+
+.navbar .nav .dropdown-toggle .caret {
+ margin-top: 8px;
+}
+
+.navbar .nav > li > a:focus,
+.navbar .nav > li > a:hover {
+ color: #333333;
+ text-decoration: none;
+ background-color: transparent;
+}
+
+.navbar .nav > .active > a,
+.navbar .nav > .active > a:hover,
+.navbar .nav > .active > a:focus {
+ color: #555555;
+ text-decoration: none;
+ background-color: #e5e5e5;
+ -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+ -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+}
+
+.navbar .btn-navbar {
+ display: none;
+ float: right;
+ padding: 7px 10px;
+ margin-right: 5px;
+ margin-left: 5px;
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #ededed;
+ *background-color: #e5e5e5;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));
+ background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5);
+ background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5);
+ background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5);
+ background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5);
+ background-repeat: repeat-x;
+ border-color: #e5e5e5 #e5e5e5 #bfbfbf;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+}
+
+.navbar .btn-navbar:hover,
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active,
+.navbar .btn-navbar.disabled,
+.navbar .btn-navbar[disabled] {
+ color: #ffffff;
+ background-color: #e5e5e5;
+ *background-color: #d9d9d9;
+}
+
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active {
+ background-color: #cccccc \9;
+}
+
+.navbar .btn-navbar .icon-bar {
+ display: block;
+ width: 18px;
+ height: 2px;
+ background-color: #f5f5f5;
+ -webkit-border-radius: 1px;
+ -moz-border-radius: 1px;
+ border-radius: 1px;
+ -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+ -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.btn-navbar .icon-bar + .icon-bar {
+ margin-top: 3px;
+}
+
+.navbar .nav > li > .dropdown-menu:before {
+ position: absolute;
+ top: -7px;
+ left: 9px;
+ display: inline-block;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid #ccc;
+ border-left: 7px solid transparent;
+ border-bottom-color: rgba(0, 0, 0, 0.2);
+ content: '';
+}
+
+.navbar .nav > li > .dropdown-menu:after {
+ position: absolute;
+ top: -6px;
+ left: 10px;
+ display: inline-block;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid #ffffff;
+ border-left: 6px solid transparent;
+ content: '';
+}
+
+.navbar-fixed-bottom .nav > li > .dropdown-menu:before {
+ top: auto;
+ bottom: -7px;
+ border-top: 7px solid #ccc;
+ border-bottom: 0;
+ border-top-color: rgba(0, 0, 0, 0.2);
+}
+
+.navbar-fixed-bottom .nav > li > .dropdown-menu:after {
+ top: auto;
+ bottom: -6px;
+ border-top: 6px solid #ffffff;
+ border-bottom: 0;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle,
+.navbar .nav li.dropdown.active > .dropdown-toggle,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle {
+ color: #555555;
+ background-color: #e5e5e5;
+}
+
+.navbar .nav li.dropdown > .dropdown-toggle .caret {
+ border-top-color: #777777;
+ border-bottom-color: #777777;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+
+.navbar .pull-right > li > .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu:before,
+.navbar .nav > li > .dropdown-menu.pull-right:before {
+ right: 12px;
+ left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu:after,
+.navbar .nav > li > .dropdown-menu.pull-right:after {
+ right: 13px;
+ left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu {
+ right: 100%;
+ left: auto;
+ margin-right: -1px;
+ margin-left: 0;
+ -webkit-border-radius: 6px 0 6px 6px;
+ -moz-border-radius: 6px 0 6px 6px;
+ border-radius: 6px 0 6px 6px;
+}
+
+.navbar-inverse {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-inner {
+ background-color: #1b1b1b;
+ background-image: -moz-linear-gradient(top, #333, #222);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333), to(#222));
+ background-image: -webkit-linear-gradient(top, #333, #222);
+ background-image: -o-linear-gradient(top, #333, #222);
+ background-image: linear-gradient(to bottom, #333, #222);
+ background-repeat: repeat-x;
+ border-color: #252525;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
+}
+
+.navbar-inverse .brand,
+.navbar-inverse .nav > li > a {
+ color: #999999;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.navbar-inverse .brand:hover,
+.navbar-inverse .nav > li > a:hover {
+ color: #ffffff;
+}
+
+.navbar-inverse .nav > li > a:focus,
+.navbar-inverse .nav > li > a:hover {
+ color: #ffffff;
+ background-color: transparent;
+}
+
+.navbar-inverse .nav .active > a,
+.navbar-inverse .nav .active > a:hover,
+.navbar-inverse .nav .active > a:focus {
+ color: #ffffff;
+ background-color: #111111;
+}
+
+.navbar-inverse .navbar-link {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-link:hover {
+ color: #ffffff;
+}
+
+.navbar-inverse .divider-vertical {
+ border-right-color: #222222;
+ border-left-color: #111111;
+}
+
+.navbar-inverse .nav li.dropdown.open > .dropdown-toggle,
+.navbar-inverse .nav li.dropdown.active > .dropdown-toggle,
+.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle {
+ color: #ffffff;
+ background-color: #111111;
+}
+
+.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret {
+ border-top-color: #999999;
+ border-bottom-color: #999999;
+}
+
+.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.navbar-inverse .navbar-search .search-query {
+ color: #ffffff;
+ background-color: #515151;
+ border-color: #111111;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ -webkit-transition: none;
+ -moz-transition: none;
+ -o-transition: none;
+ transition: none;
+}
+
+.navbar-inverse .navbar-search .search-query:-moz-placeholder {
+ color: #cccccc;
+}
+
+.navbar-inverse .navbar-search .search-query:-ms-input-placeholder {
+ color: #cccccc;
+}
+
+.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder {
+ color: #cccccc;
+}
+
+.navbar-inverse .navbar-search .search-query:focus,
+.navbar-inverse .navbar-search .search-query.focused {
+ padding: 5px 15px;
+ color: #333333;
+ text-shadow: 0 1px 0 #ffffff;
+ background-color: #ffffff;
+ border: 0;
+ outline: 0;
+ -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+}
+
+.navbar-inverse .btn-navbar {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #0e0e0e;
+ *background-color: #040404;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404));
+ background-image: -webkit-linear-gradient(top, #151515, #040404);
+ background-image: -o-linear-gradient(top, #151515, #040404);
+ background-image: linear-gradient(to bottom, #151515, #040404);
+ background-image: -moz-linear-gradient(top, #151515, #040404);
+ background-repeat: repeat-x;
+ border-color: #040404 #040404 #000000;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.navbar-inverse .btn-navbar:hover,
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active,
+.navbar-inverse .btn-navbar.disabled,
+.navbar-inverse .btn-navbar[disabled] {
+ color: #ffffff;
+ background-color: #040404;
+ *background-color: #000000;
+}
+
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active {
+ background-color: #000000 \9;
+}
+
+.breadcrumb {
+ padding: 8px 15px;
+ margin: 0 0 20px;
+ list-style: none;
+ background-color: #f5f5f5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.breadcrumb li {
+ display: inline-block;
+ *display: inline;
+ text-shadow: 0 1px 0 #ffffff;
+ *zoom: 1;
+}
+
+.breadcrumb .divider {
+ padding: 0 5px;
+ color: #ccc;
+}
+
+.breadcrumb .active {
+ color: #999999;
+}
+
+.pagination {
+ height: 40px;
+ margin: 20px 0;
+}
+
+.pagination ul {
+ display: inline-block;
+ *display: inline;
+ margin-bottom: 0;
+ margin-left: 0;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ *zoom: 1;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.pagination ul > li {
+ display: inline;
+}
+
+.pagination ul > li > a,
+.pagination ul > li > span {
+ float: left;
+ padding: 0 14px;
+ line-height: 38px;
+ text-decoration: none;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-left-width: 0;
+}
+
+.pagination ul > li > a:hover,
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+ background-color: #f5f5f5;
+}
+
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+ color: #999999;
+ cursor: default;
+}
+
+.pagination ul > .disabled > span,
+.pagination ul > .disabled > a,
+.pagination ul > .disabled > a:hover {
+ color: #999999;
+ cursor: default;
+ background-color: transparent;
+}
+
+.pagination ul > li:first-child > a,
+.pagination ul > li:first-child > span {
+ border-left-width: 1px;
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.pagination ul > li:last-child > a,
+.pagination ul > li:last-child > span {
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+.pagination-centered {
+ text-align: center;
+}
+
+.pagination-right {
+ text-align: right;
+}
+
+.pager {
+ margin: 20px 0;
+ text-align: center;
+ list-style: none;
+ *zoom: 1;
+}
+
+.pager:before,
+.pager:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.pager:after {
+ clear: both;
+}
+
+.pager li {
+ display: inline;
+}
+
+.pager a,
+.pager span {
+ display: inline-block;
+ padding: 5px 14px;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+
+.pager a:hover {
+ text-decoration: none;
+ background-color: #f5f5f5;
+}
+
+.pager .next a,
+.pager .next span {
+ float: right;
+}
+
+.pager .previous a {
+ float: left;
+}
+
+.pager .disabled a,
+.pager .disabled a:hover,
+.pager .disabled span {
+ color: #999999;
+ cursor: default;
+ background-color: #fff;
+}
+
+.modal-open .modal .dropdown-menu {
+ z-index: 2050;
+}
+
+.modal-open .modal .dropdown.open {
+ *z-index: 2050;
+}
+
+.modal-open .modal .popover {
+ z-index: 2060;
+}
+
+.modal-open .modal .tooltip {
+ z-index: 2080;
+}
+
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+ background-color: #000000;
+}
+
+.modal-backdrop.fade {
+ opacity: 0;
+}
+
+.modal-backdrop,
+.modal-backdrop.fade.in {
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+
+.modal {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ z-index: 1050;
+ width: 560px;
+ margin: -250px 0 0 -280px;
+ overflow: auto;
+ background-color: #ffffff;
+ border: 1px solid #999;
+ border: 1px solid rgba(0, 0, 0, 0.3);
+ *border: 1px solid #999;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding-box;
+ background-clip: padding-box;
+}
+
+.modal.fade {
+ top: -25%;
+ -webkit-transition: opacity 0.3s linear, top 0.3s ease-out;
+ -moz-transition: opacity 0.3s linear, top 0.3s ease-out;
+ -o-transition: opacity 0.3s linear, top 0.3s ease-out;
+ transition: opacity 0.3s linear, top 0.3s ease-out;
+}
+
+.modal.fade.in {
+ top: 50%;
+}
+
+.modal-header {
+ padding: 9px 15px;
+ border-bottom: 1px solid #eee;
+}
+
+.modal-header .close {
+ margin-top: 2px;
+}
+
+.modal-header h3 {
+ margin: 0;
+ line-height: 30px;
+}
+
+.modal-body {
+ max-height: 400px;
+ padding: 15px;
+ overflow-y: auto;
+}
+
+.modal-form {
+ margin-bottom: 0;
+}
+
+.modal-footer {
+ padding: 14px 15px 15px;
+ margin-bottom: 0;
+ text-align: right;
+ background-color: #f5f5f5;
+ border-top: 1px solid #ddd;
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+ *zoom: 1;
+ -webkit-box-shadow: inset 0 1px 0 #ffffff;
+ -moz-box-shadow: inset 0 1px 0 #ffffff;
+ box-shadow: inset 0 1px 0 #ffffff;
+}
+
+.modal-footer:before,
+.modal-footer:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.modal-footer:after {
+ clear: both;
+}
+
+.modal-footer .btn + .btn {
+ margin-bottom: 0;
+ margin-left: 5px;
+}
+
+.modal-footer .btn-group .btn + .btn {
+ margin-left: -1px;
+}
+
+.tooltip {
+ position: absolute;
+ z-index: 1030;
+ display: block;
+ padding: 5px;
+ font-size: 11px;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ visibility: visible;
+}
+
+.tooltip.in {
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+
+.tooltip.top {
+ margin-top: -3px;
+}
+
+.tooltip.right {
+ margin-left: 3px;
+}
+
+.tooltip.bottom {
+ margin-top: 3px;
+}
+
+.tooltip.left {
+ margin-left: -3px;
+}
+
+.tooltip-inner {
+ max-width: 200px;
+ padding: 3px 8px;
+ color: #ffffff;
+ text-align: center;
+ text-decoration: none;
+ background-color: #000000;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.tooltip-arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+
+.tooltip.top .tooltip-arrow {
+ bottom: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-top-color: #000000;
+ border-width: 5px 5px 0;
+}
+
+.tooltip.right .tooltip-arrow {
+ top: 50%;
+ left: 0;
+ margin-top: -5px;
+ border-right-color: #000000;
+ border-width: 5px 5px 5px 0;
+}
+
+.tooltip.left .tooltip-arrow {
+ top: 50%;
+ right: 0;
+ margin-top: -5px;
+ border-left-color: #000000;
+ border-width: 5px 0 5px 5px;
+}
+
+.tooltip.bottom .tooltip-arrow {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-bottom-color: #000000;
+ border-width: 0 5px 5px;
+}
+
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1010;
+ display: none;
+ width: 236px;
+ padding: 1px;
+ background-color: #ffffff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+.popover.top {
+ margin-bottom: 10px;
+}
+
+.popover.right {
+ margin-left: 10px;
+}
+
+.popover.bottom {
+ margin-top: 10px;
+}
+
+.popover.left {
+ margin-right: 10px;
+}
+
+.popover-title {
+ padding: 8px 14px;
+ margin: 0;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 18px;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ -webkit-border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ border-radius: 5px 5px 0 0;
+}
+
+.popover-content {
+ padding: 9px 14px;
+}
+
+.popover-content p,
+.popover-content ul,
+.popover-content ol {
+ margin-bottom: 0;
+}
+
+.popover .arrow,
+.popover .arrow:after {
+ position: absolute;
+ display: inline-block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+
+.popover .arrow:after {
+ z-index: -1;
+ content: "";
+}
+
+.popover.top .arrow {
+ bottom: -10px;
+ left: 50%;
+ margin-left: -10px;
+ border-top-color: #ffffff;
+ border-width: 10px 10px 0;
+}
+
+.popover.top .arrow:after {
+ bottom: -1px;
+ left: -11px;
+ border-top-color: rgba(0, 0, 0, 0.25);
+ border-width: 11px 11px 0;
+}
+
+.popover.right .arrow {
+ top: 50%;
+ left: -10px;
+ margin-top: -10px;
+ border-right-color: #ffffff;
+ border-width: 10px 10px 10px 0;
+}
+
+.popover.right .arrow:after {
+ bottom: -11px;
+ left: -1px;
+ border-right-color: rgba(0, 0, 0, 0.25);
+ border-width: 11px 11px 11px 0;
+}
+
+.popover.bottom .arrow {
+ top: -10px;
+ left: 50%;
+ margin-left: -10px;
+ border-bottom-color: #ffffff;
+ border-width: 0 10px 10px;
+}
+
+.popover.bottom .arrow:after {
+ top: -1px;
+ left: -11px;
+ border-bottom-color: rgba(0, 0, 0, 0.25);
+ border-width: 0 11px 11px;
+}
+
+.popover.left .arrow {
+ top: 50%;
+ right: -10px;
+ margin-top: -10px;
+ border-left-color: #ffffff;
+ border-width: 10px 0 10px 10px;
+}
+
+.popover.left .arrow:after {
+ right: -1px;
+ bottom: -11px;
+ border-left-color: rgba(0, 0, 0, 0.25);
+ border-width: 11px 0 11px 11px;
+}
+
+.thumbnails {
+ margin-left: -20px;
+ list-style: none;
+ *zoom: 1;
+}
+
+.thumbnails:before,
+.thumbnails:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.thumbnails:after {
+ clear: both;
+}
+
+.row-fluid .thumbnails {
+ margin-left: 0;
+}
+
+.thumbnails > li {
+ float: left;
+ margin-bottom: 20px;
+ margin-left: 20px;
+}
+
+.thumbnail {
+ display: block;
+ padding: 4px;
+ line-height: 20px;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ -webkit-transition: all 0.2s ease-in-out;
+ -moz-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+}
+
+a.thumbnail:hover {
+ border-color: #0088cc;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+ -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+ box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+}
+
+.thumbnail > img {
+ display: block;
+ max-width: 100%;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.thumbnail .caption {
+ padding: 9px;
+ color: #555555;
+}
+
+.label,
+.badge {
+ font-size: 11.844px;
+ font-weight: bold;
+ line-height: 14px;
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ white-space: nowrap;
+ vertical-align: baseline;
+ background-color: #999999;
+}
+
+.label {
+ padding: 1px 4px 2px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+.badge {
+ padding: 1px 9px 2px;
+ -webkit-border-radius: 9px;
+ -moz-border-radius: 9px;
+ border-radius: 9px;
+}
+
+a.label:hover,
+a.badge:hover {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.label-important,
+.badge-important {
+ background-color: #b94a48;
+}
+
+.label-important[href],
+.badge-important[href] {
+ background-color: #953b39;
+}
+
+.label-warning,
+.badge-warning {
+ background-color: #f89406;
+}
+
+.label-warning[href],
+.badge-warning[href] {
+ background-color: #c67605;
+}
+
+.label-success,
+.badge-success {
+ background-color: #468847;
+}
+
+.label-success[href],
+.badge-success[href] {
+ background-color: #356635;
+}
+
+.label-info,
+.badge-info {
+ background-color: #3a87ad;
+}
+
+.label-info[href],
+.badge-info[href] {
+ background-color: #2d6987;
+}
+
+.label-inverse,
+.badge-inverse {
+ background-color: #333333;
+}
+
+.label-inverse[href],
+.badge-inverse[href] {
+ background-color: #1a1a1a;
+}
+
+.btn .label,
+.btn .badge {
+ position: relative;
+ top: -1px;
+}
+
+.btn-mini .label,
+.btn-mini .badge {
+ top: 0;
+}
+
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-moz-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-ms-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-o-keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+ to {
+ background-position: 40px 0;
+ }
+}
+
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+.progress {
+ height: 20px;
+ margin-bottom: 20px;
+ overflow: hidden;
+ background-color: #f7f7f7;
+ background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
+ background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
+ background-repeat: repeat-x;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.progress .bar {
+ float: left;
+ width: 0;
+ height: 100%;
+ font-size: 12px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #0e90d2;
+ background-image: -moz-linear-gradient(top, #149bdf, #0480be);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
+ background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
+ background-image: -o-linear-gradient(top, #149bdf, #0480be);
+ background-image: linear-gradient(to bottom, #149bdf, #0480be);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ -webkit-transition: width 0.6s ease;
+ -moz-transition: width 0.6s ease;
+ -o-transition: width 0.6s ease;
+ transition: width 0.6s ease;
+}
+
+.progress .bar + .bar {
+ -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+}
+
+.progress-striped .bar {
+ background-color: #149bdf;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ -webkit-background-size: 40px 40px;
+ -moz-background-size: 40px 40px;
+ -o-background-size: 40px 40px;
+ background-size: 40px 40px;
+}
+
+.progress.active .bar {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -moz-animation: progress-bar-stripes 2s linear infinite;
+ -ms-animation: progress-bar-stripes 2s linear infinite;
+ -o-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
+}
+
+.progress-danger .bar,
+.progress .bar-danger {
+ background-color: #dd514c;
+ background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
+ background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
+}
+
+.progress-danger.progress-striped .bar,
+.progress-striped .bar-danger {
+ background-color: #ee5f5b;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-success .bar,
+.progress .bar-success {
+ background-color: #5eb95e;
+ background-image: -moz-linear-gradient(top, #62c462, #57a957);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
+ background-image: -webkit-linear-gradient(top, #62c462, #57a957);
+ background-image: -o-linear-gradient(top, #62c462, #57a957);
+ background-image: linear-gradient(to bottom, #62c462, #57a957);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
+}
+
+.progress-success.progress-striped .bar,
+.progress-striped .bar-success {
+ background-color: #62c462;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-info .bar,
+.progress .bar-info {
+ background-color: #4bb1cf;
+ background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
+ background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
+}
+
+.progress-info.progress-striped .bar,
+.progress-striped .bar-info {
+ background-color: #5bc0de;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-warning .bar,
+.progress .bar-warning {
+ background-color: #faa732;
+ background-image: -moz-linear-gradient(top, #fbb450, #f89406);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
+ background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
+ background-image: -o-linear-gradient(top, #fbb450, #f89406);
+ background-image: linear-gradient(to bottom, #fbb450, #f89406);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
+}
+
+.progress-warning.progress-striped .bar,
+.progress-striped .bar-warning {
+ background-color: #fbb450;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.accordion {
+ margin-bottom: 20px;
+}
+
+.accordion-group {
+ margin-bottom: 2px;
+ border: 1px solid #e5e5e5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.accordion-heading {
+ border-bottom: 0;
+}
+
+.accordion-heading .accordion-toggle {
+ display: block;
+ padding: 8px 15px;
+}
+
+.accordion-toggle {
+ cursor: pointer;
+}
+
+.accordion-inner {
+ padding: 9px 15px;
+ border-top: 1px solid #e5e5e5;
+}
+
+.carousel {
+ position: relative;
+ margin-bottom: 20px;
+ line-height: 1;
+}
+
+.carousel-inner {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+}
+
+.carousel .item {
+ position: relative;
+ display: none;
+ -webkit-transition: 0.6s ease-in-out left;
+ -moz-transition: 0.6s ease-in-out left;
+ -o-transition: 0.6s ease-in-out left;
+ transition: 0.6s ease-in-out left;
+}
+
+.carousel .item > img {
+ display: block;
+ line-height: 1;
+}
+
+.carousel .active,
+.carousel .next,
+.carousel .prev {
+ display: block;
+}
+
+.carousel .active {
+ left: 0;
+}
+
+.carousel .next,
+.carousel .prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
+}
+
+.carousel .next {
+ left: 100%;
+}
+
+.carousel .prev {
+ left: -100%;
+}
+
+.carousel .next.left,
+.carousel .prev.right {
+ left: 0;
+}
+
+.carousel .active.left {
+ left: -100%;
+}
+
+.carousel .active.right {
+ left: 100%;
+}
+
+.carousel-control {
+ position: absolute;
+ top: 40%;
+ left: 15px;
+ width: 40px;
+ height: 40px;
+ margin-top: -20px;
+ font-size: 60px;
+ font-weight: 100;
+ line-height: 30px;
+ color: #ffffff;
+ text-align: center;
+ background: #222222;
+ border: 3px solid #ffffff;
+ -webkit-border-radius: 23px;
+ -moz-border-radius: 23px;
+ border-radius: 23px;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+
+.carousel-control.right {
+ right: 15px;
+ left: auto;
+}
+
+.carousel-control:hover {
+ color: #ffffff;
+ text-decoration: none;
+ opacity: 0.9;
+ filter: alpha(opacity=90);
+}
+
+.carousel-caption {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 15px;
+ background: #333333;
+ background: rgba(0, 0, 0, 0.75);
+}
+
+.carousel-caption h4,
+.carousel-caption p {
+ line-height: 20px;
+ color: #ffffff;
+}
+
+.carousel-caption h4 {
+ margin: 0 0 5px;
+}
+
+.carousel-caption p {
+ margin-bottom: 0;
+}
+
+.hero-unit {
+ padding: 60px;
+ margin-bottom: 30px;
+ background-color: #eeeeee;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.hero-unit h1 {
+ margin-bottom: 0;
+ font-size: 60px;
+ line-height: 1;
+ letter-spacing: -1px;
+ color: inherit;
+}
+
+.hero-unit p {
+ font-size: 18px;
+ font-weight: 200;
+ line-height: 30px;
+ color: inherit;
+}
+
+.pull-right {
+ float: right;
+}
+
+.pull-left {
+ float: left;
+}
+
+.hide {
+ display: none;
+}
+
+.show {
+ display: block;
+}
+
+.invisible {
+ visibility: hidden;
+}
+
+.affix {
+ position: fixed;
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/bootstrap.min.js b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/bootstrap.min.js
new file mode 100644
index 00000000..0e33fb16
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/bootstrap.min.js
@@ -0,0 +1,6 @@
+/*!
+* Bootstrap.js by @fat & @mdo
+* Copyright 2012 Twitter, Inc.
+* http://www.apache.org/licenses/LICENSE-2.0.txt
+*/
+!function(e){e(function(){"use strict";e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()},e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e(function(){e("body").on("click.alert.data-api",t,n.prototype.close)})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")},e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e(function(){e("body").on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=n,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},to:function(t){var n=this.$element.find(".item.active"),r=n.parent().children(),i=r.index(n),s=this;if(t>r.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){s.to(t)}):i==t?this.pause().cycle():this.slide(t>i?"next":"prev",e(r[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f=e.Event("slide",{relatedTarget:i[0]});this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u]();if(i.hasClass("active"))return;if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}},e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e(function(){e("body").on("click.carousel.data-api","[data-slide]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=!i.data("modal")&&e.extend({},i.data(),n.data());i.carousel(s),t.preventDefault()})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning)return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning)return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=typeof n=="object"&&n;i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e(function(){e("body").on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})})}(window.jQuery),!function(e){"use strict";function r(){i(e(t)).removeClass("open")}function i(t){var n=t.attr("data-target"),r;return n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=e(n),r.length||(r=t.parent()),r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||(s.toggleClass("open"),n.focus()),!1},keydown:function(t){var n,r,s,o,u,a;if(!/(38|40|27)/.test(t.keyCode))return;n=e(this),t.preventDefault(),t.stopPropagation();if(n.is(".disabled, :disabled"))return;o=i(n),u=o.hasClass("open");if(!u||u&&t.keyCode==27)return n.click();r=e("[role=menu] li:not(.divider) a",o);if(!r.length)return;a=r.index(r.filter(":focus")),t.keyCode==38&&a>0&&a--,t.keyCode==40&&a<r.length-1&&a++,~a||(a=0),r.eq(a).focus()}},e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e(function(){e("html").on("click.dropdown.data-api touchstart.dropdown.data-api",r),e("body").on("click.dropdown touchstart.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown.data-api touchstart.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api touchstart.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;e("body").addClass("modal-open"),this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1).focus(),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.trigger("shown")}):t.$element.trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,e("body").removeClass("modal-open"),this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(e){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,e.proxy(this.removeBackdrop,this)):this.removeBackdrop()):t&&t()}},e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e(function(){e("body").on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,this.options.trigger=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):this.options.trigger!="manual"&&(i=this.options.trigger=="hover"?"mouseenter":"focus",s=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this))),this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,t,this.$element.data()),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);if(!n.options.delay||!n.options.delay.show)return n.show();clearTimeout(this.timeout),n.hoverState="in",this.timeout=setTimeout(function(){n.hoverState=="in"&&n.show()},n.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var e,t,n,r,i,s,o;if(this.hasContent()&&this.enabled){e=this.tip(),this.setContent(),this.options.animation&&e.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,e[0],this.$element[0]):this.options.placement,t=/in/.test(s),e.remove().css({top:0,left:0,display:"block"}).appendTo(t?this.$element:document.body),n=this.getPosition(t),r=e[0].offsetWidth,i=e[0].offsetHeight;switch(t?s.split(" ")[1]:s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}e.css(o).addClass(s).addClass("in")}},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function r(){var t=setTimeout(function(){n.off(e.support.transition.end).remove()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.remove()})}var t=this,n=this.tip();return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?r():n.remove(),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(t){return e.extend({},t?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}},e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover",title:"",delay:0,html:!0}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content > *")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-content")||(typeof n.content=="function"?n.content.call(t[0]):n.content),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}}),e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var t=e(this),n=t.data("target")||t.attr("href"),r=/^#\w/.test(n)&&e(n);return r&&r.length&&[[r.position().top,n]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}},e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active a").last()[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}},e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e(function(){e("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.$menu=e(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:t.top+t.height,left:t.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),(e.browser.chrome||e.browser.webkit||e.browser.msie)&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this))},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=!~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},blur:function(e){var t=this;setTimeout(function(){t.hide()},150)},click:function(e){e.stopPropagation(),e.preventDefault(),this.select()},mouseenter:function(t){this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")}},e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e(function(){e("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;t.preventDefault(),n.typeahead(n.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))},e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery); \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/delta.js b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/delta.js
new file mode 100644
index 00000000..ee1178ad
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/delta.js
@@ -0,0 +1,344 @@
+var templates = new Array();
+var records = new Array();
+var schema = new Array();
+var connections = 0;
+var attempts = 0;
+var total = 1;
+
+function connect() {
+ socket = new WebSocket("ws://localhost:6060/update");
+
+ socket.onopen = function() {
+ attempts = 1;
+ connections++;
+ reportStatus(this, "success.png", "0", "0", "0", "0", "0", "");
+ };
+
+ socket.onerror = function(message) {
+ reportStatus(this, "failure.png", "0", "0", "0", "0", "0", "");
+ };
+
+ socket.onclose = function(message) {
+ var exponent = Math.pow(2, attempts++);
+ var interval = (exponent - 1) * 1000;
+ var reference = connect();
+
+ if (interval > 30 * 1000) {
+ interval = 30 * 1000;
+ }
+ setTimeout(reference, interval);
+ reportStatus(this, "pending.png", "0", "0", "0", "0", "0", "");
+ };
+
+ socket.onmessage = function(message) {
+ var data = message.data.substring(1);
+ var table = w2ui['mainGrid'];
+
+ if (message.data.charAt(0) == 'T') {
+ deltaUpdate(this, table, data, updateTable);
+ } else if (message.data.charAt(0) == 'H') {
+ deltaUpdate(this, table, data, highlightTable);
+ } else if (message.data.charAt(0) == 'S') {
+ schemaUpdate(this, table, data);
+ }
+ };
+}
+
+function reportStatus(socket, status, height, delta, change, duration, sequence, method) {
+ var image = '<img src="';
+
+ image += status;
+ image += '"';
+ image += 'style="';
+ image += ' max-width: 100%;';
+ image += ' max-height: 100%;';
+ image += ' padding-top: 4px;';
+ image += ' padding-bottom: 4px;';
+ image += ' padding-left: 4px;';
+ image += ' padding-right: 8px;';
+ image += '"/>';
+
+ document.getElementById("connection").innerHTML = image;
+ document.getElementById("rows").innerHTML = height;
+ document.getElementById("changes").innerHTML = change;
+ document.getElementById("duration").innerHTML = duration;
+ socket.send("status:rows="+height+",change="+change+",duration="+duration+",sequence="+sequence+",method="+method);
+
+}
+
+function schemaUpdate(socket, table, message) {
+ var cells = message.split('|');
+ var minimum = cells.length;
+ var width = schema.length;
+
+ for ( var i = 0; i < cells.length; i++) {
+ var values = cells[i].split(',');
+ var name = values[0];
+ var caption = decodeValue(values[1]);
+ var template = decodeValue(values[2]);
+ var resizable = values[3];
+ var sortable = values[4];
+ var style = {};
+
+ style['name'] = name;
+ style['caption'] = caption;
+ style['template'] = template;
+ style['resizable'] = resizable;
+ style['sortable'] = sortable;
+
+ schema[i] = style;
+ }
+ if(width < minimum) {
+ expandWidth(table);
+ requestRefresh(socket, 'schemaUpdate');
+ }
+}
+
+function requestRefresh(socket, message) {
+ socket.send('refresh:everything=true,message='+message);
+}
+
+function deltaUpdate(socket, table, message, method) {
+ var header = message.indexOf(':');
+ var sequence = 0;
+
+ if(header > 0) {
+ sequence = message.substring(0, header);
+ message = message.substring(header + 1);
+ }
+ var rows = message.split('|');
+ var length = message.length;
+ var start = currentTime();
+
+ if(schema.length > 0) {
+ method(socket, table, rows);
+ }
+ var finish = currentTime();
+ var duration = finish - start;
+ var height = table.total;
+ var change = rows.length;
+ var operation = method.name;
+
+ reportStatus(socket, "success.png", height, length, change, duration, sequence, operation);
+}
+
+function currentTime() {
+ var date = new Date()
+ return date.getTime();
+}
+
+function findRow(table, row) {
+ var record = table.find({ recid: row });
+ var height = table.total;
+ var index = 0;
+
+ if(record.length > 0) {
+ index = record[0];
+ } else {
+ index = height + 1;
+ }
+ return index;
+}
+
+function highlightTable(socket, table, rows) {
+ for ( var i = 0; i < rows.length; i++) {
+ var row = rows[i];
+ var pair = row.split(':');
+ var index = pair[0];
+
+ if (index > 0) {
+ index = findRow(table, index);
+
+ if (pair != null && pair.length > 1) {
+ var cells = pair[1].split(',');
+
+ if (cells.length > 0) {
+ highlightRow(table, index, cells);
+ }
+ }
+ }
+ }
+}
+
+function updateTable(socket, table, rows) {
+ for ( var i = 0; i < rows.length; i++) {
+ var row = rows[i];
+ var pair = row.split(':');
+ var index = pair[0];
+
+ if (index > 0) {
+ index = findRow(table, index);
+
+ if (pair != null && pair.length > 1) {
+ var cells = pair[1].split(',');
+
+ if (cells.length > 0) {
+ updateRow(socket, table, index, cells);
+ }
+ }
+ }
+ }
+}
+
+function findCell(table, row, column) {
+ var height = table.total;
+ var width = schema.length;
+
+ if(row <= height && column <= width) {
+ var expression = "#mainGrid_";
+
+ expression += table.name;
+ expression += "_rec_";
+ expression += row;
+ expression += " td[col=";
+ expression += column;
+ expression += "]";
+
+ return $(expression)[0];
+ }
+ return null;
+}
+
+function highlightRow(table, row, cells) {
+ var height = table.total;
+
+ if (height <= row) {
+ expandHeight(table, row);
+ }
+ var record = records[row];
+
+ for ( var i = 0; i < cells.length; i++) {
+ var cell = cells[i].split('=');
+ var column = cell[0];
+ var value = cell[1];
+ var style = schema[column];
+ var decoded = decodeValue(value);
+
+ record.style[column] = decoded;
+ }
+}
+
+function updateRow(socket, table, row, cells) {
+ var height = table.total;
+
+ if (height <= row) {
+ expandHeight(table, row);
+ }
+ var record = records[row];
+ var template = templates[row];
+
+ for ( var i = 0; i < cells.length; i++) {
+ var cell = cells[i].split('=');
+ var column = cell[0];
+ var value = cell[1];
+ var style = schema[column];
+ var decoded = decodeValue(value);
+
+ record[style.name] = decoded;
+ }
+ interpolateRow(record, template);
+ table.set(record.recid, template, false);
+ reconcileRow(socket, table, row);
+}
+
+function interpolateRow(record, template) {
+ for ( var i = 0; i < schema.length; i++) {
+ var style = schema[i];
+ var name = style.name;
+ var text = style.template;
+
+ for( var j = 0; j < schema.length; j++) {
+ var index = text.indexOf('{');
+
+ if(index == -1) {
+ break;
+ }
+ var key = schema[j].name;
+ var token = "{" + key + "}";
+ var value = record[key];
+
+ text = text.replace(token, value);
+ }
+ template.style[i] = record.style[i];
+ template[name] = text;
+ }
+}
+
+function reconcileRow(socket, table, row) {
+ var template = templates[row];
+ var index = findRow(table, row);
+ var row = table.get(index);
+
+ for( var i = 0; i < schema.length; i++) {
+ var style = schema[i];
+ var name = style.name;
+ var actual = row[name];
+ var expect = template[name];
+
+ if(actual != expect) {
+ requestRefresh(socket, 'reconcileFailure');
+ }
+ }
+}
+
+function decodeValue(value) {
+ var text = value.substring(1);
+
+ if (value.charAt(0) == '<') {
+ var encoded = text.toString();
+ var decoded = '';
+
+ for ( var i = 0; i < encoded.length; i += 2) {
+ var char = encoded.substr(i, 2);
+ var decimal = parseInt(char, 16);
+
+ decoded += String.fromCharCode(decimal);
+ }
+ return decoded;
+ }
+ return text;
+}
+
+function expandWidth(table) {
+ var width = table.columns.length;
+ var height = table.total;
+
+ for ( var i = width; i < schema.length; i++) {
+ var style = schema[i];
+ var column = {};
+
+ column['field'] = style.name;
+ column['caption'] = style.caption;
+ column['resizable'] = style.resizable;
+ column['sortable'] = style.sortable;
+ column['size'] = '50px';
+
+ for( var j = 0; j < height; j++) {
+ templates[i][name] = '';
+ records[i][name] = '';
+ }
+ table.addColumn(column);
+ }
+}
+
+function expandHeight(table, row) {
+ var height = table.total;
+
+ for ( var i = height; i < row; i++) {
+ var index = i + 1;
+ var record = {recid : index, id: index, style: []};
+ var template = {recid : index, id: index, style: []};
+
+ for( var j = 0; j < schema.length; j++) {
+ var name = schema[j].name;
+
+ template[name] = '';
+ record[name] = '';
+ }
+ templates[row] = template;
+ records[row] = record;
+ table.add(template);
+ }
+}
+
+window.addEventListener("load", connect, false); \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/failure.png b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/failure.png
new file mode 100644
index 00000000..e560b088
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/failure.png
Binary files differ
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/font-awesome.min.css b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/font-awesome.min.css
new file mode 100644
index 00000000..417f2c9a
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/font-awesome.min.css
@@ -0,0 +1,33 @@
+/*!
+ * Font Awesome 3.0.2
+ * the iconic font designed for use with Twitter Bootstrap
+ * -------------------------------------------------------
+ * The full suite of pictographic icons, examples, and documentation
+ * can be found at: http://fortawesome.github.com/Font-Awesome/
+ *
+ * License
+ * -------------------------------------------------------
+ * - The Font Awesome font is licensed under the SIL Open Font License - http://scripts.sil.org/OFL
+ * - Font Awesome CSS, LESS, and SASS files are licensed under the MIT License -
+ * http://opensource.org/licenses/mit-license.html
+ * - The Font Awesome pictograms are licensed under the CC BY 3.0 License - http://creativecommons.org/licenses/by/3.0/
+ * - Attribution is no longer required in Font Awesome 3.0, but much appreciated:
+ * "Font Awesome by Dave Gandy - http://fortawesome.github.com/Font-Awesome"
+
+ * Contact
+ * -------------------------------------------------------
+ * Email: dave@davegandy.com
+ * Twitter: http://twitter.com/fortaweso_me
+ * Work: Lead Product Designer @ http://kyruus.com
+ */
+
+@font-face{
+ font-family:'FontAwesome';
+ src:url('font/fontawesome-webfont.eot?v=3.0.1');
+ src:url('font/fontawesome-webfont.eot?#iefix&v=3.0.1') format('embedded-opentype'),
+ url('font/fontawesome-webfont.woff?v=3.0.1') format('woff'),
+ url('font/fontawesome-webfont.ttf?v=3.0.1') format('truetype');
+ font-weight:normal;
+ font-style:normal }
+
+[class^="fa-"],[class*=" fa-"]{font-family:FontAwesome !important;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;display:inline;width:auto;height:auto;line-height:normal;vertical-align:baseline;background-image:none;background-position:0 0;background-repeat:repeat;margin-top:0}.fa-white,.nav-pills>.active>a>[class^="fa-"],.nav-pills>.active>a>[class*=" fa-"],.nav-list>.active>a>[class^="fa-"],.nav-list>.active>a>[class*=" fa-"],.navbar-inverse .nav>.active>a>[class^="fa-"],.navbar-inverse .nav>.active>a>[class*=" fa-"],.dropdown-menu>li>a:hover>[class^="fa-"],.dropdown-menu>li>a:hover>[class*=" fa-"],.dropdown-menu>.active>a>[class^="fa-"],.dropdown-menu>.active>a>[class*=" fa-"],.dropdown-submenu:hover>a>[class^="fa-"],.dropdown-submenu:hover>a>[class*=" fa-"]{background-image:none}[class^="fa-"]:before,[class*=" fa-"]:before{text-decoration:inherit;display:inline-block;speak:none}a [class^="fa-"],a [class*=" fa-"]{display:inline-block}.fa-large:before{vertical-align:-10%;font-size:1.3333333333333333em}.btn [class^="fa-"],.nav [class^="fa-"],.btn [class*=" fa-"],.nav [class*=" fa-"]{display:inline}.btn [class^="fa-"].fa-large,.nav [class^="fa-"].fa-large,.btn [class*=" fa-"].fa-large,.nav [class*=" fa-"].fa-large{line-height:.9em}.btn [class^="fa-"].fa-spin,.nav [class^="fa-"].fa-spin,.btn [class*=" fa-"].fa-spin,.nav [class*=" fa-"].fa-spin{display:inline-block}.nav-tabs [class^="fa-"],.nav-pills [class^="fa-"],.nav-tabs [class*=" fa-"],.nav-pills [class*=" fa-"],.nav-tabs [class^="fa-"].fa-large,.nav-pills [class^="fa-"].fa-large,.nav-tabs [class*=" fa-"].fa-large,.nav-pills [class*=" fa-"].fa-large{line-height:.9em}li [class^="fa-"],.nav li [class^="fa-"],li [class*=" fa-"],.nav li [class*=" fa-"]{display:inline-block;width:1.25em;text-align:center}li [class^="fa-"].fa-large,.nav li [class^="fa-"].fa-large,li [class*=" fa-"].fa-large,.nav li [class*=" fa-"].fa-large{width:1.5625em}ul.icons{list-style-type:none;text-indent:-0.75em}ul.icons li [class^="fa-"],ul.icons li [class*=" fa-"]{width:.75em}.fa-muted{color:#eee}.fa-border{border:solid 1px #eee;padding:.2em .25em .15em;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fa-2x{font-size:2em}.fa-2x.fa-border{border-width:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.fa-3x{font-size:3em}.fa-3x.fa-border{border-width:3px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.fa-4x{font-size:4em}.fa-4x.fa-border{border-width:4px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.pull-right{float:right}.pull-left{float:left}[class^="fa-"].pull-left,[class*=" fa-"].pull-left{margin-right:.3em}[class^="fa-"].pull-right,[class*=" fa-"].pull-right{margin-left:.3em}.btn [class^="fa-"].pull-left.fa-2x,.btn [class*=" fa-"].pull-left.fa-2x,.btn [class^="fa-"].pull-right.fa-2x,.btn [class*=" fa-"].pull-right.fa-2x{margin-top:.18em}.btn [class^="fa-"].fa-spin.fa-large,.btn [class*=" fa-"].fa-spin.fa-large{line-height:.8em}.btn.btn-small [class^="fa-"].pull-left.fa-2x,.btn.btn-small [class*=" fa-"].pull-left.fa-2x,.btn.btn-small [class^="fa-"].pull-right.fa-2x,.btn.btn-small [class*=" fa-"].pull-right.fa-2x{margin-top:.25em}.btn.btn-large [class^="fa-"],.btn.btn-large [class*=" fa-"]{margin-top:0}.btn.btn-large [class^="fa-"].pull-left.fa-2x,.btn.btn-large [class*=" fa-"].pull-left.fa-2x,.btn.btn-large [class^="fa-"].pull-right.fa-2x,.btn.btn-large [class*=" fa-"].pull-right.fa-2x{margin-top:.05em}.btn.btn-large [class^="fa-"].pull-left.fa-2x,.btn.btn-large [class*=" fa-"].pull-left.fa-2x{margin-right:.2em}.btn.btn-large [class^="fa-"].pull-right.fa-2x,.btn.btn-large [class*=" fa-"].pull-right.fa-2x{margin-left:.2em}.fa-spin{display:inline-block;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}@-moz-document url-prefix(){.fa-spin{height:.9em}.btn .fa-spin{height:auto}.fa-spin.fa-large{height:1.25em}.btn .fa-spin.fa-large{height:.75em}}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-empty: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-ok:before{content:"\f00c"}.fa-remove:before{content:"\f00d"}.fa-zoom-in:before{content:"\f00e"}.fa-zoom-out:before{content:"\f010"}.fa-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before{content:"\f013"}.fa-trash:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file:before{content:"\f016"}.fa-time:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download-alt:before{content:"\f019"}.fa-download:before{content:"\f01a"}.fa-upload:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle:before{content:"\f01d"}.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-indent-left:before{content:"\f03b"}.fa-indent-right:before{content:"\f03c"}.fa-facetime-video:before{content:"\f03d"}.fa-picture: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{content:"\f044"}.fa-share:before{content:"\f045"}.fa-check:before{content:"\f046"}.fa-move: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-sign:before{content:"\f055"}.fa-minus-sign:before{content:"\f056"}.fa-remove-sign:before{content:"\f057"}.fa-ok-sign:before{content:"\f058"}.fa-question-sign:before{content:"\f059"}.fa-info-sign:before{content:"\f05a"}.fa-screenshot:before{content:"\f05b"}.fa-remove-circle:before{content:"\f05c"}.fa-ok-circle:before{content:"\f05d"}.fa-ban-circle: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-share-alt:before{content:"\f064"}.fa-resize-full:before{content:"\f065"}.fa-resize-small:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-sign:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye-open:before{content:"\f06e"}.fa-eye-close:before{content:"\f070"}.fa-warning-sign: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-close:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-resize-vertical:before{content:"\f07d"}.fa-resize-horizontal:before{content:"\f07e"}.fa-bar-chart:before{content:"\f080"}.fa-twitter-sign:before{content:"\f081"}.fa-facebook-sign:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-up:before{content:"\f087"}.fa-thumbs-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-empty:before{content:"\f08a"}.fa-signout:before{content:"\f08b"}.fa-linkedin-sign:before{content:"\f08c"}.fa-pushpin:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-signin:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-sign:before{content:"\f092"}.fa-upload-alt:before{content:"\f093"}.fa-lemon:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-check-empty:before{content:"\f096"}.fa-bookmark-empty:before{content:"\f097"}.fa-phone-sign:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0a2"}.fa-certificate:before{content:"\f0a3"}.fa-hand-right:before{content:"\f0a4"}.fa-hand-left:before{content:"\f0a5"}.fa-hand-up:before{content:"\f0a6"}.fa-hand-down:before{content:"\f0a7"}.fa-circle-arrow-left:before{content:"\f0a8"}.fa-circle-arrow-right:before{content:"\f0a9"}.fa-circle-arrow-up:before{content:"\f0aa"}.fa-circle-arrow-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-fullscreen:before{content:"\f0b2"}.fa-group:before{content:"\f0c0"}.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-beaker:before{content:"\f0c3"}.fa-cut:before{content:"\f0c4"}.fa-copy:before{content:"\f0c5"}.fa-paper-clip:before{content:"\f0c6"}.fa-save:before{content:"\f0c7"}.fa-sign-blank:before{content:"\f0c8"}.fa-reorder: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-sign:before{content:"\f0d3"}.fa-google-plus-sign: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-sort:before{content:"\f0dc"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-up:before{content:"\f0de"}.fa-envelope-alt:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-undo:before{content:"\f0e2"}.fa-legal:before{content:"\f0e3"}.fa-dashboard:before{content:"\f0e4"}.fa-comment-alt:before{content:"\f0e5"}.fa-comments-alt:before{content:"\f0e6"}.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before{content:"\f0ea"}.fa-lightbulb: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-alt:before{content:"\f0f3"}.fa-coffee:before{content:"\f0f4"}.fa-food:before{content:"\f0f5"}.fa-file-alt:before{content:"\f0f6"}.fa-building:before{content:"\f0f7"}.fa-hospital: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-sign:before{content:"\f0fd"}.fa-plus-sign-alt:before{content:"\f0fe"}.fa-double-angle-left:before{content:"\f100"}.fa-double-angle-right:before{content:"\f101"}.fa-double-angle-up:before{content:"\f102"}.fa-double-angle-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{content:"\f10b"}.fa-circle-blank: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-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-close-alt:before{content:"\f114"}.fa-folder-open-alt:before{content:"\f115"} \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/grid.html b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/grid.html
new file mode 100644
index 00000000..69b546a0
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/grid.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Bond Semi Central</title>
+ <link href="font-awesome.min.css" rel="stylesheet">
+ <link href="bootstrap.css" rel="stylesheet">
+ <script src="jquery-2.1.1.min.js"></script>
+ <script src="bootstrap.min.js"></script>
+ <link rel="stylesheet" type="text/css" href="w2ui-1.4.min.css" />
+ <script type="text/javascript" src="w2ui-1.4.js"></script>
+ <script type="text/javascript" src="delta.js"></script>
+</head>
+<style>
+.update {
+ background-color: #5cacee;
+}
+.highlight {
+ background-color: #00ff00;
+}
+#grid_mainGrid_body > .w2ui-grid-records table tr.w2ui-even {
+ background-color: #ffddf1;
+}
+#grid_mainGrid_body > .w2ui-grid-records table tr.w2ui-odd {
+ background-color: #ffc0cb;
+}
+#grid_litEFPGrid_body > .w2ui-grid-records table tr.w2ui-even {
+ background-color: #0000ee;
+}
+#grid_litEFPGrid_body > .w2ui-grid-records table tr.w2ui-odd {
+ background-color: #0000cd;
+}
+#grid_litSwitchGrid_body > .w2ui-grid-records table tr.w2ui-even {
+ background-color: #0000ee;
+}
+#grid_litSwitchGrid_body > .w2ui-grid-records table tr.w2ui-odd {
+ background-color: #0000cd;
+}
+</style>
+<body style="height: 100%; margin: 0; background-color: #ff0000;">
+<div id="mainLayout" style="position: absolute; top: 0px; left: 0px; bottom: 0px; right: 0px;"></div>
+<script>
+$(function () {
+
+ // -- LAYOUT
+
+ var pstyle = 'background-color: #F5F6F7; overflow: hidden;';
+ $('#mainLayout').w2layout({
+ name: 'mainLayout',
+ padding: 0,
+ panels: [
+ { type: 'left', size: '40%', style: pstyle, resizable: true },
+ { type: 'right', size: '60%', style: pstyle, resizable: true },
+ { type: 'bottom', size: '25px', style: pstyle, resizable: true }
+ ]
+ });
+
+ var pstyle = 'background-color: #F5F6F7; overflow: hidden;';
+ $('#blueLayout').w2layout({
+ name: 'blueLayout',
+ padding: 0,
+ panels: [
+ { type: 'left', size: '50%', style: pstyle, resizable: true },
+ { type: 'right', size: '50%', style: pstyle, resizable: true },
+ { type: 'bottom', size: '30%', style: pstyle, resizable: true,
+ tabs: {
+ active: 'tab1',
+ tabs: [
+ { id: 'tab1', caption: 'Market Monitor' },
+ { id: 'tab2', caption: 'Blotter' }
+ ],
+ onClick: function (event) {
+ this.owner.content('main', event);
+ }
+ }
+ }
+ ]
+ });
+
+ $().w2grid({
+ name: 'mainGrid'
+ });
+
+ $().w2grid({
+ name: 'litEFPGrid'
+ });
+
+ $().w2grid({
+ name: 'litSwitchGrid'
+ });
+
+ w2ui['mainLayout'].content('left', w2ui['mainGrid']);
+ w2ui['mainLayout'].content('right', w2ui['blueLayout']);
+
+ w2ui['blueLayout'].content('left', w2ui['litEFPGrid']);
+ w2ui['blueLayout'].content('right', w2ui['litSwitchGrid']);
+
+ w2ui['mainLayout'].content('bottom', '<div style="background-color: #eee; padding: 2px 2px; font-family: Verdana,Arial,sans-serif; font-size: 11px;"> <span id="connection"><img style="max-width: 100%; max-height: 100%; padding-top: 4px; padding-bottom: 4px;" src="failure.png"/></span> Rows - <span id="rows"></span> Changes - <span id="changes"></span> Duration - <span id="duration"></span></div>');
+
+});
+</script>
+</body>
+</html>
+
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/index.html b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/index.html
new file mode 100644
index 00000000..53770604
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/index.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Bond Semi Central</title>
+ <link href="font-awesome.min.css" rel="stylesheet">
+ <link href="bootstrap.css" rel="stylesheet">
+ <script src="jquery-2.1.1.min.js"></script>
+ <script src="bootstrap.min.js"></script>
+ <link rel="stylesheet" type="text/css" href="w2ui-1.4.min.css" />
+ <script type="text/javascript" src="w2ui-1.4.min.js"></script>
+</head>
+<body>
+<div id="layout" style="width: 100%; height: 400px;"></div>
+<div id="grid" style="width: 100%; height: 350px;"></div>
+<script type="text/javascript">
+$(function () {
+ var pstyle = 'border: 1px solid #dfdfdf; padding: 5px;';
+ $('#layout').w2layout({
+ name: 'layout',
+ panels: [
+ { type: 'left', size: 200, resizable: true, style: pstyle, content: 'left' },
+ { type: 'main', style: pstyle, content: 'main' },
+ { type: 'right', size: 200, resizable: true, style: pstyle, content: 'right' }
+ ]
+ });
+});
+$(function () {
+ $('#grid').w2grid({
+ name: 'grid',
+ url: 'data/list2.json',
+ columns: [
+ { field: 'fname', caption: 'First Name', size: '30%' },
+ { field: 'lname', caption: 'Last Name', size: '70%' },
+ { field: 'sdate', caption: 'Dates', size: '120px', attr: "align=center" },
+ ]
+ });
+});
+</script>
+</body>
+</html>
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/jquery-2.1.1.min.js b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/jquery-2.1.1.min.js
new file mode 100644
index 00000000..9ed2acc6
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/jquery-2.1.1.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)
+},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button;return null==a.pageX&&null!=b.clientX&&(c=a.target.ownerDocument||l,d=c.documentElement,e=c.body,a.pageX=b.clientX+(d&&d.scrollLeft||e&&e.scrollLeft||0)-(d&&d.clientLeft||e&&e.clientLeft||0),a.pageY=b.clientY+(d&&d.scrollTop||e&&e.scrollTop||0)-(d&&d.clientTop||e&&e.clientTop||0)),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=W.test(e)?this.mouseHooks:V.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=l),3===a.target.nodeType&&(a.target=a.target.parentNode),g.filter?g.filter(a,f):a},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==_()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===_()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&n.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?Z:$):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:$,isPropagationStopped:$,isImmediatePropagationStopped:$,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=Z,a&&a.preventDefault&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=Z,a&&a.stopPropagation&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=Z,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=L.access(d,b);e||d.addEventListener(a,c,!0),L.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=L.access(d,b)-1;e?L.access(d,b,e):(d.removeEventListener(a,c,!0),L.remove(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(g in a)this.on(g,b,c,a[g],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=$;else if(!d)return this;return 1===e&&(f=d,d=function(a){return n().off(a),f.apply(this,arguments)},d.guid=f.guid||(f.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=$),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var ab=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ib={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1></$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=qb[0].contentDocument,b.write(),b.close(),c=sb(a,b),qb.detach()),rb[a]=c),c}var ub=/^margin/,vb=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wb=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)};function xb(a,b,c){var d,e,f,g,h=a.style;return c=c||wb(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),vb.test(g)&&ub.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function yb(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var zb=/^(none|table(?!-c[ea]).+)/,Ab=new RegExp("^("+Q+")(.*)$","i"),Bb=new RegExp("^([+-])=("+Q+")","i"),Cb={position:"absolute",visibility:"hidden",display:"block"},Db={letterSpacing:"0",fontWeight:"400"},Eb=["Webkit","O","Moz","ms"];function Fb(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Eb.length;while(e--)if(b=Eb[e]+c,b in a)return b;return d}function Gb(a,b,c){var d=Ab.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Hb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ib(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wb(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xb(a,b,f),(0>e||null==e)&&(e=a.style[b]),vb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Hb(a,b,c||(g?"border":"content"),d,f)+"px"}function Jb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",tb(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fb(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Bb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fb(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xb(a,b,d)),"normal"===e&&b in Db&&(e=Db[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?zb.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Cb,function(){return Ib(a,b,d)}):Ib(a,b,d):void 0},set:function(a,c,d){var e=d&&wb(a);return Gb(a,c,d?Hb(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=yb(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xb,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ub.test(a)||(n.cssHooks[a+b].set=Gb)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wb(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Jb(this,!0)},hide:function(){return Jb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Kb(a,b,c,d,e){return new Kb.prototype.init(a,b,c,d,e)}n.Tween=Kb,Kb.prototype={constructor:Kb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Kb.propHooks[this.prop];return a&&a.get?a.get(this):Kb.propHooks._default.get(this)},run:function(a){var b,c=Kb.propHooks[this.prop];return this.pos=b=this.options.duration?n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Kb.propHooks._default.set(this),this}},Kb.prototype.init.prototype=Kb.prototype,Kb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Kb.propHooks.scrollTop=Kb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Kb.prototype.init,n.fx.step={};var Lb,Mb,Nb=/^(?:toggle|show|hide)$/,Ob=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pb=/queueHooks$/,Qb=[Vb],Rb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Ob.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Ob.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sb(){return setTimeout(function(){Lb=void 0}),Lb=n.now()}function Tb(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ub(a,b,c){for(var d,e=(Rb[b]||[]).concat(Rb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Vb(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||tb(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Nb.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?tb(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ub(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wb(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xb(a,b,c){var d,e,f=0,g=Qb.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Lb||Sb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:Lb||Sb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wb(k,j.opts.specialEasing);g>f;f++)if(d=Qb[f].call(j,a,k,j.opts))return d;return n.map(k,Ub,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xb,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Rb[c]=Rb[c]||[],Rb[c].unshift(b)},prefilter:function(a,b){b?Qb.unshift(a):Qb.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xb(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Tb(b,!0),a,d,e)}}),n.each({slideDown:Tb("show"),slideUp:Tb("hide"),slideToggle:Tb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(Lb=n.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||n.fx.stop(),Lb=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){Mb||(Mb=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(Mb),Mb=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a=l.createElement("input"),b=l.createElement("select"),c=b.appendChild(l.createElement("option"));a.type="checkbox",k.checkOn=""!==a.value,k.optSelected=c.selected,b.disabled=!0,k.optDisabled=!c.disabled,a=l.createElement("input"),a.value="t",a.type="radio",k.radioValue="t"===a.value}();var Yb,Zb,$b=n.expr.attrHandle;n.fn.extend({attr:function(a,b){return J(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Zb:Yb)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))
+},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Zb={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$b[b]||n.find.attr;$b[b]=function(a,b,d){var e,f;return d||(f=$b[b],$b[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$b[b]=f),e}});var _b=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_b.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ac=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ac," ").indexOf(b)>=0)return!0;return!1}});var bc=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bc,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cc=n.now(),dc=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var ec,fc,gc=/#.*$/,hc=/([?&])_=[^&]*/,ic=/^(.*?):[ \t]*([^\r\n]*)$/gm,jc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,kc=/^(?:GET|HEAD)$/,lc=/^\/\//,mc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,nc={},oc={},pc="*/".concat("*");try{fc=location.href}catch(qc){fc=l.createElement("a"),fc.href="",fc=fc.href}ec=mc.exec(fc.toLowerCase())||[];function rc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function sc(a,b,c,d){var e={},f=a===oc;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function tc(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function uc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function vc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:fc,type:"GET",isLocal:jc.test(ec[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":pc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?tc(tc(a,n.ajaxSettings),b):tc(n.ajaxSettings,a)},ajaxPrefilter:rc(nc),ajaxTransport:rc(oc),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=ic.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||fc)+"").replace(gc,"").replace(lc,ec[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=mc.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===ec[1]&&h[2]===ec[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(ec[3]||("http:"===ec[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),sc(nc,k,b,v),2===t)return v;i=k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!kc.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(dc.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=hc.test(d)?d.replace(hc,"$1_="+cc++):d+(dc.test(d)?"&":"?")+"_="+cc++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+pc+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=sc(oc,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=uc(k,v,f)),u=vc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var wc=/%20/g,xc=/\[\]$/,yc=/\r?\n/g,zc=/^(?:submit|button|image|reset|file)$/i,Ac=/^(?:input|select|textarea|keygen)/i;function Bc(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||xc.test(a)?d(a,e):Bc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Bc(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Bc(c,a[c],b,e);return d.join("&").replace(wc,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&Ac.test(this.nodeName)&&!zc.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(yc,"\r\n")}}):{name:b.name,value:c.replace(yc,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Cc=0,Dc={},Ec={0:200,1223:204},Fc=n.ajaxSettings.xhr();a.ActiveXObject&&n(a).on("unload",function(){for(var a in Dc)Dc[a]()}),k.cors=!!Fc&&"withCredentials"in Fc,k.ajax=Fc=!!Fc,n.ajaxTransport(function(a){var b;return k.cors||Fc&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Cc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Dc[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Ec[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Dc[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("<script>").prop({async:!0,charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&e("error"===a.type?404:200,a.type)}),l.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Gc=[],Hc=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Gc.pop()||n.expando+"_"+cc++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Hc.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Hc.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Hc,"$1"+e):b.jsonp!==!1&&(b.url+=(dc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Gc.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||l;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var Ic=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&Ic)return Ic.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=n.trim(a.slice(h)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e,dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,f||[a.responseText,b,a])}),this},n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var Jc=a.document.documentElement;function Kc(a){return n.isWindow(a)?a:9===a.nodeType&&a.defaultView}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d=this[0],e={top:0,left:0},f=d&&d.ownerDocument;if(f)return b=f.documentElement,n.contains(b,d)?(typeof d.getBoundingClientRect!==U&&(e=d.getBoundingClientRect()),c=Kc(f),{top:e.top+c.pageYOffset-b.clientTop,left:e.left+c.pageXOffset-b.clientLeft}):e},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===n.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(d=a.offset()),d.top+=n.css(a[0],"borderTopWidth",!0),d.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-d.top-n.css(c,"marginTop",!0),left:b.left-d.left-n.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||Jc;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Jc})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b,c){var d="pageYOffset"===c;n.fn[b]=function(e){return J(this,function(b,e,f){var g=Kc(b);return void 0===f?g?g[c]:b[e]:void(g?g.scrollTo(d?a.pageXOffset:f,d?f:a.pageYOffset):b[e]=f)},b,e,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=yb(k.pixelPosition,function(a,c){return c?(c=xb(a,b),vb.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return J(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var Lc=a.jQuery,Mc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=Mc),b&&a.jQuery===n&&(a.jQuery=Lc),n},typeof b===U&&(a.jQuery=a.$=n),n}); \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/login.html b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/login.html
new file mode 100644
index 00000000..47114949
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/login.html
@@ -0,0 +1,12 @@
+ <html>
+ <head>
+ <title>Login Page</title>
+ </head>
+ <body>
+ <h1>Please Login</h1>
+ <form action='/login' method='POST'>
+ <input type='text' name='user'/>
+ <input type='submit' value='Sign In'/>
+ </form>
+ </body>
+</html> \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/main.html b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/main.html
new file mode 100644
index 00000000..3b898f7f
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/main.html
@@ -0,0 +1,182 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Bond Semi Central</title>
+ <link href="font-awesome.min.css" rel="stylesheet">
+ <link href="bootstrap.css" rel="stylesheet">
+ <script src="jquery-2.1.1.min.js"></script>
+ <script src="bootstrap.min.js"></script>
+ <link rel="stylesheet" type="text/css" href="w2ui-1.4.min.css" />
+ <script type="text/javascript" src="w2ui-1.4.min.js"></script>
+</head>
+<body>
+<div id="layout" style="width: 100%; height: 400px;"></div>
+<div id="grid" style="width: 100%; height: 350px;"></div>
+<script>
+$(function () {
+
+ // -- LAYOUT
+
+ var pstyle = 'background-color: #F5F6F7; overflow: hidden';
+ $('#layout').w2layout({
+ name: 'layout',
+ padding: 0,
+ panels: [
+ { type: 'left', size: '40%', style: pstyle, resizable: true },
+ { type: 'right', size: '60%', style: pstyle, resizable: true }
+ ]
+ });
+
+ var pstyle = 'background-color: #F5F6F7; overflow: hidden';
+ $('#layout2').w2layout({
+ name: 'layout2',
+ padding: 0,
+ panels: [
+ { type: 'left', size: '50%', style: pstyle, resizable: true },
+ { type: 'right', size: '50%', style: pstyle, resizable: true },
+ { type: 'bottom', size: '30%', style: pstyle, resizable: true }
+ ]
+ });
+
+
+ // -- GRID
+
+ $().w2grid({
+ name: 'pinkGrid',
+ sortData: [ { field: 'recid', direction: 'asc' } ],
+ columns: [
+ { field: 'recid', caption: 'ID', size: '50px', sortable: true, resizable: true },
+ { field: 'lname', caption: 'Last Name', size: '30%', sortable: true, resizable: true },
+ { field: 'fname', caption: 'First Name', size: '30%', sortable: true, resizable: true },
+ { field: 'email', caption: 'Email', size: '40%', resizable: true, sortable: true },
+ { field: 'sdate', caption: 'Start Date', size: '120px', resizable: true },
+ { field: 'sdate', caption: 'End Date', size: '120px', resizable: true },
+ { field: 'sdate', caption: 'Release Date', size: '120px', resizable: true },
+ ],
+ records: [
+ { recid: 1, fname: 'John', lname: 'doe', email: 'vitali@gmail.com', sdate: '1/3/2012' },
+ { recid: 2, fname: 'Stuart', lname: 'Motzart', email: 'jdoe@gmail.com', sdate: '2/4/2012' },
+ { recid: 3, fname: 'Jin', lname: 'Franson', email: 'jdoe@gmail.com', sdate: '4/23/2012' },
+ { recid: 4, fname: 'Susan', lname: 'Ottie', email: 'jdoe@gmail.com', sdate: '5/3/2012' },
+ { recid: 5, fname: 'Kelly', lname: 'Silver', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 6, fname: 'Francis', lname: 'Gatos', email: 'vitali@gmail.com', sdate: '2/5/2012' },
+ { recid: 7, fname: 'Mark', lname: 'Welldo', email: 'jdoe@gmail.com', sdate: '8/25/2012' },
+ { recid: 8, fname: 'Thomas', lname: 'Bahh', email: 'jdoe@gmail.com', sdate: '9/3/2012' },
+ { recid: 9, fname: 'Sergei', lname: 'Rachmaninov', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 20, fname: 'Jill', lname: 'Doe', email: 'jdoe@gmail.com', sdate: '10/3/2012' },
+ { recid: 21, fname: 'Frank', lname: 'Motzart', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 22, fname: 'Peter', lname: 'Franson', email: 'vitali@gmail.com', sdate: '4/3/2012' },
+ { recid: 23, fname: 'Andrew', lname: 'Ottie', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 24, fname: 'Manny', lname: 'Silver', email: 'jim@gmail.com', sdate: '12/3/2012' },
+ { recid: 25, fname: 'Ben', lname: 'Gatos', email: 'jim@gmail.com', sdate: '4/3/2012' },
+ { recid: 26, fname: 'Doer', lname: 'Welldo', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 27, fname: 'Shashi', lname: 'bahh', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 28, fname: 'Av', lname: 'Rachmaninov', email: 'jdoe@gmail.com', sdate: '4/3/2012' }
+ ],
+ onClick: function (event) {
+ var rec = this.get(event.recid);
+ w2ui['form'].record = rec;
+ w2ui['form'].refresh();
+ event.onComplete = function () {
+ if (this.getSelection().length == 0) $('#delete').attr('disabled', true); else $('#delete').removeAttr('disabled');
+ }
+ }
+ });
+
+ $().w2grid({
+ name: 'blueOutrightGrid',
+ sortData: [ { field: 'recid', direction: 'asc' } ],
+ columns: [
+ { field: 'recid', caption: 'ID', size: '50px', sortable: true, resizable: true },
+ { field: 'lname', caption: 'Last Name', size: '30%', sortable: true, resizable: true },
+ { field: 'fname', caption: 'First Name', size: '30%', sortable: true, resizable: true },
+ { field: 'email', caption: 'Email', size: '40%', resizable: true, sortable: true },
+ { field: 'sdate', caption: 'Start Date', size: '120px', resizable: true },
+ { field: 'sdate', caption: 'End Date', size: '120px', resizable: true },
+ { field: 'sdate', caption: 'Release Date', size: '120px', resizable: true },
+ ],
+ records: [
+ { recid: 1, fname: 'John', lname: 'doe', email: 'vitali@gmail.com', sdate: '1/3/2012' },
+ { recid: 2, fname: 'Stuart', lname: 'Motzart', email: 'jdoe@gmail.com', sdate: '2/4/2012' },
+ { recid: 3, fname: 'Jin', lname: 'Franson', email: 'jdoe@gmail.com', sdate: '4/23/2012' },
+ { recid: 4, fname: 'Susan', lname: 'Ottie', email: 'jdoe@gmail.com', sdate: '5/3/2012' },
+ { recid: 5, fname: 'Kelly', lname: 'Silver', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 6, fname: 'Francis', lname: 'Gatos', email: 'vitali@gmail.com', sdate: '2/5/2012' },
+ { recid: 7, fname: 'Mark', lname: 'Welldo', email: 'jdoe@gmail.com', sdate: '8/25/2012' },
+ { recid: 8, fname: 'Thomas', lname: 'Bahh', email: 'jdoe@gmail.com', sdate: '9/3/2012' },
+ { recid: 9, fname: 'Sergei', lname: 'Rachmaninov', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 20, fname: 'Jill', lname: 'Doe', email: 'jdoe@gmail.com', sdate: '10/3/2012' },
+ { recid: 21, fname: 'Frank', lname: 'Motzart', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 22, fname: 'Peter', lname: 'Franson', email: 'vitali@gmail.com', sdate: '4/3/2012' },
+ { recid: 23, fname: 'Andrew', lname: 'Ottie', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 24, fname: 'Manny', lname: 'Silver', email: 'jim@gmail.com', sdate: '12/3/2012' },
+ { recid: 25, fname: 'Ben', lname: 'Gatos', email: 'jim@gmail.com', sdate: '4/3/2012' },
+ { recid: 26, fname: 'Doer', lname: 'Welldo', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 27, fname: 'Shashi', lname: 'bahh', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 28, fname: 'Av', lname: 'Rachmaninov', email: 'jdoe@gmail.com', sdate: '4/3/2012' }
+ ],
+ onClick: function (event) {
+ var rec = this.get(event.recid);
+ w2ui['form'].record = rec;
+ w2ui['form'].refresh();
+ event.onComplete = function () {
+ if (this.getSelection().length == 0) $('#delete').attr('disabled', true); else $('#delete').removeAttr('disabled');
+ }
+ }
+ });
+
+ $().w2grid({
+ name: 'blueSwitchGrid',
+ sortData: [ { field: 'recid', direction: 'asc' } ],
+ columns: [
+ { field: 'recid', caption: 'ID', size: '50px', sortable: true, resizable: true },
+ { field: 'lname', caption: 'Last Name', size: '30%', sortable: true, resizable: true },
+ { field: 'fname', caption: 'First Name', size: '30%', sortable: true, resizable: true },
+ { field: 'email', caption: 'Email', size: '40%', resizable: true, sortable: true },
+ { field: 'sdate', caption: 'Start Date', size: '120px', resizable: true },
+ { field: 'sdate', caption: 'End Date', size: '120px', resizable: true },
+ { field: 'sdate', caption: 'Release Date', size: '120px', resizable: true },
+ ],
+ records: [
+ { recid: 1, fname: 'John', lname: 'doe', email: 'vitali@gmail.com', sdate: '1/3/2012' },
+ { recid: 2, fname: 'Stuart', lname: 'Motzart', email: 'jdoe@gmail.com', sdate: '2/4/2012' },
+ { recid: 3, fname: 'Jin', lname: 'Franson', email: 'jdoe@gmail.com', sdate: '4/23/2012' },
+ { recid: 4, fname: 'Susan', lname: 'Ottie', email: 'jdoe@gmail.com', sdate: '5/3/2012' },
+ { recid: 5, fname: 'Kelly', lname: 'Silver', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 6, fname: 'Francis', lname: 'Gatos', email: 'vitali@gmail.com', sdate: '2/5/2012' },
+ { recid: 7, fname: 'Mark', lname: 'Welldo', email: 'jdoe@gmail.com', sdate: '8/25/2012' },
+ { recid: 8, fname: 'Thomas', lname: 'Bahh', email: 'jdoe@gmail.com', sdate: '9/3/2012' },
+ { recid: 9, fname: 'Sergei', lname: 'Rachmaninov', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 20, fname: 'Jill', lname: 'Doe', email: 'jdoe@gmail.com', sdate: '10/3/2012' },
+ { recid: 21, fname: 'Frank', lname: 'Motzart', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 22, fname: 'Peter', lname: 'Franson', email: 'vitali@gmail.com', sdate: '4/3/2012' },
+ { recid: 23, fname: 'Andrew', lname: 'Ottie', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 24, fname: 'Manny', lname: 'Silver', email: 'jim@gmail.com', sdate: '12/3/2012' },
+ { recid: 25, fname: 'Ben', lname: 'Gatos', email: 'jim@gmail.com', sdate: '4/3/2012' },
+ { recid: 26, fname: 'Doer', lname: 'Welldo', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 27, fname: 'Shashi', lname: 'bahh', email: 'jdoe@gmail.com', sdate: '4/3/2012' },
+ { recid: 28, fname: 'Av', lname: 'Rachmaninov', email: 'jdoe@gmail.com', sdate: '4/3/2012' }
+ ],
+ onClick: function (event) {
+ var rec = this.get(event.recid);
+ w2ui['form'].record = rec;
+ w2ui['form'].refresh();
+ event.onComplete = function () {
+ if (this.getSelection().length == 0) $('#delete').attr('disabled', true); else $('#delete').removeAttr('disabled');
+ }
+ }
+ });
+
+ w2ui['layout'].content('left', w2ui['pinkGrid']);
+ w2ui['layout'].content('right', w2ui['layout2']);
+ w2ui['layout2'].content('left', w2ui['blueOutrightGrid']);
+ w2ui['layout2'].content('right', w2ui['blueSwitchGrid']);
+ w2ui['pinkGrid'].get(1).getCell(1).style = 'bgcolor: #ff0000';
+});
+</script>
+</head>
+<body>
+ <div id="layout" style="width: 100%; height: 500px;"></div>
+</body>
+</html>
+
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/pending.png b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/pending.png
new file mode 100644
index 00000000..da9ebb84
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/pending.png
Binary files differ
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/success.png b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/success.png
new file mode 100644
index 00000000..b46c7f24
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/success.png
Binary files differ
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/table.html b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/table.html
new file mode 100644
index 00000000..97fc5284
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/table.html
@@ -0,0 +1,347 @@
+<html>
+ <head>
+ <style type="text/css">
+ .page {
+ background-color: #ebebeb;
+ }
+ .grid {
+ table-layout:fixed;
+ font-family: verdana,arial,sans-serif;
+ font-size:11px;
+ color:#333333;
+ border-width: 1px;
+ border-color: #666666;
+ border-collapse: collapse;
+ }
+ .header {
+ border-width: 1px;
+ padding: 8px;
+ border-style: solid;
+ border-color: #666666;
+ background-color: #dedede;
+ white-space: nowrap;
+ word-break: keep-all;
+ font-weight: bold;
+ }
+ .normal {
+ border-width: 1px;
+ padding: 8px;
+ border-style: solid;
+ border-color: #666666;
+ background-color: #ffffff;
+ white-space: nowrap;
+ word-break: keep-all;
+ font-weight: normal;
+ }
+ .highlight {
+ border-width: 1px;
+ padding: 8px;
+ border-style: solid;
+ border-color: #666666;
+ background-color: #efefef;
+ white-space: nowrap;
+ word-break: keep-all;
+ font-weight: normal;
+ }
+ .update {
+ border-width: 1px;
+ padding: 8px;
+ border-style: solid;
+ border-color: #666666;
+ background-color: #5cacee;
+ white-space: nowrap;
+ word-break: keep-all;
+ font-weight: normal;
+ }
+ .input {
+ border: 1px solid #666666;
+ padding: 5px;
+ font-family: verdana,arial,sans-serif;
+ font-size:11px;
+ }
+ .label {
+ font-family: verdana,arial,sans-serif;
+ font-size:14px;
+ font-weight: normal;
+ }
+ .debug {
+ width: 800px;
+ height: 120px;
+ border: 1px solid #666666;
+ padding: 5px;
+ font-family: verdana,arial,sans-serif;
+ font-size:11px;
+ }
+ </style>
+ <title>Table Subscription</title>
+ <script>
+ var translate = new Array();
+ var dirty = new Array();
+ var connections = 0;
+ var attempts = 0;
+ var total = 1;
+
+ function connect() {
+ socket = new WebSocket("ws://localhost:9090/update");
+
+ socket.onopen = function () {
+ attempts = 1;
+ connections++;
+ reportStatus("CONNECTED", "0", "0", "0", "0");
+ };
+
+ socket.onerror = function (message) {
+ reportStatus("ERROR", "0", "0", "0", "0");
+ };
+
+ socket.onclose = function (message) {
+ var exponent = Math.pow(2, attempts++);
+ var interval = (exponent - 1) * 1000;
+ var reference = connect();
+
+ if (interval > 30 * 1000) {
+ interval = 30 * 1000;
+ }
+ setTimeout(reference, interval);
+ reportStatus("CLOSED", "0", "0", "0", "0");
+ };
+
+ socket.onmessage = function (message) {
+ var table = document.getElementById("grid");
+ var data = message.data.substring(1);
+
+ if (message.data.charAt(0) == 'D') {
+ deltaUpdate(table, data);
+ } else if (message.data.charAt(0) == 'S') {
+ schemaUpdate(table, data);
+ }
+ };
+ }
+
+ function reportDebug(tag, text) {
+ document.getElementById("debug").value = text;
+ }
+
+ function reportStatus(status, height, delta, change, duration) {
+ document.getElementById("connection").innerHTML = status;
+ document.getElementById("rows").innerHTML = height;
+ document.getElementById("delta").innerHTML = delta;
+ document.getElementById("changes").innerHTML = change;
+ document.getElementById("duration").innerHTML = duration;
+ }
+
+ function schemaUpdate(table, message) {
+ var cells = message.split(',');
+ var height = table.rows.length;
+
+ if (height < 1) {
+ expandHeight(table, 1);
+ }
+ var width = table.rows[0].cells.length;
+ var minimum = cells.length;
+
+ if (width <= minimum) {
+ expandWidth(table, minimum);
+ }
+ for (var i = 0; i < cells.length; i++) {
+ table.rows[0].cells[i].innerHTML = cells[i];
+ table.rows[0].cells[i].className = 'header'
+ }
+ }
+
+ function deltaUpdate(table, message) {
+ var rows = message.split('|');
+ var length = message.length;
+ var start = currentTime();
+
+ clearHighlight(table);
+ updateTable(table, rows);
+
+ var finish = currentTime();
+ var duration = finish - start;
+ var height = table.rows.length;
+ var change = rows.length;
+
+ reportDebug("Delta", message);
+ reportStatus("CONNECTED", height, length, change, duration);
+ }
+
+ function currentTime() {
+ var date = new Date()
+ return date.getTime();
+ }
+
+ function translateRow(row) {
+ if (translate[row] === undefined) {
+ translate[row] = total++;
+ }
+ return translate[row];
+ }
+
+ function updateTable(table, rows) {
+ for (var i = 0; i < rows.length; i++) {
+ var row = rows[i];
+ var pair = row.split(':');
+ var index = pair[0];
+
+ if (index > 0) {
+ index = translateRow(index);
+
+ if (pair != null && pair.length > 1) {
+ var cells = pair[1].split(',');
+
+ if (cells.length > 0) {
+ updateRow(table, index, cells);
+ }
+ }
+ }
+ }
+ }
+
+ function updateRow(table, row, cells) {
+ var height = table.rows.length;
+
+ if (height <= row) {
+ expandHeight(table, row);
+ }
+ var width = table.rows[row].cells.length;
+ var minimum = cells.length;
+
+ if (width <= minimum) {
+ expandWidth(table, minimum);
+ }
+ dirty[row] = 'dirty';
+
+ for (var i = 0; i < cells.length; i++) {
+ var cell = cells[i].split('=');
+ var column = cell[0];
+ var value = cell[1];
+ var decoded = decodeValue(value);
+
+ if (table.rows[row].cells.length < column) {
+ expandWidth(table, column);
+ }
+ table.rows[row].cells[column].innerHTML = decoded;
+
+ if (row > 0) {
+ table.rows[row].cells[column].className = 'update';
+ }
+ }
+ }
+
+ function decodeValue(value) {
+ var text = value.substring(1);
+
+ if(value.charAt(0) == '<') {
+ var encoded = text.toString();
+ var decoded = '';
+
+ for (var i = 0; i < encoded.length; i += 2) {
+ var char = encoded.substr(i, 2);
+ var decimal = parseInt(char, 16);
+
+ decoded += String.fromCharCode(decimal);
+ }
+ return decoded;
+ }
+ return text;
+ }
+
+ function clearHighlight(table) {
+ var height = table.rows.length;
+
+ for (var i = 0; i < height; i++) {
+ if(dirty[i] === undefined || dirty[i] == 'dirty') {
+ var width = table.rows[i].cells.length;
+
+ for (var j = 0; j < width; j++) {
+ if (i > 0) {
+ if (i % 2 == 0) {
+ table.rows[i].cells[j].className = 'highlight';
+ } else {
+ table.rows[i].cells[j].className = 'normal';
+ }
+ } else {
+ table.rows[i].cells[j].className = 'header';
+ }
+ }
+ dirty[i] = 'clean';
+ }
+ }
+ }
+
+ function expandHeight(table, row) {
+ var height = table.rows.length;
+
+ for (var i = height; i <= row; i++) {
+ table.insertRow(i);
+
+ if (i > 0) {
+ var width = table.rows[i - 1].cells.length;
+
+ for (var j = 0; j < width; j++) {
+ table.rows[i].insertCell(j);
+ }
+ }
+ }
+ }
+
+ function expandWidth(table, column) {
+ for (var i = 0; i < table.rows.length; i++) {
+ var width = table.rows[i].cells.length;
+
+ for (var j = width; j < column; j++) {
+ table.rows[i].insertCell(j);
+
+ if (i > 0) {
+ table.rows[i].cells[j].className = 'header';
+ }
+ }
+ }
+ }
+ window.addEventListener("load", connect, false);
+ </script>
+ </head>
+ <body class="page">
+ <form action='/table' method='POST'>
+ <table>
+ <tr>
+ <td class="label">Type</td>
+ </tr>
+ <tr>
+ <td><input class="input" size='100' type='text' name='type' value='${type}'/></td>
+ </tr>
+ <tr>
+ <td class="label">Predicate</td>
+ </tr>
+ <tr>
+ <td><input class="input" size='100' type='text' name='predicate' value='${predicate}'/><input type='submit' value='Change'/></td>
+ </tr>
+ <tr>
+ <td class="label">Delta</td>
+ </tr>
+ <tr>
+ <td><textarea id="debug" class="debug"></textarea></td>
+ </tr>
+ </table>
+ </form>
+ <table class="grid">
+ <tr>
+ <td class="header">connection</td>
+ <td class="header">rows</td>
+ <td class="header">delta</td>
+ <td class="header">changes</td>
+ <td class="header">duration</td>
+ </tr>
+ <tr>
+ <td class="normal" id="connection"></td>
+ <td class="normal" id="rows"></td>
+ <td class="normal" id="delta"></td>
+ <td class="normal" id="changes"></td>
+ <td class="normal" id="duration"></td>
+ </tr>
+ </table>
+ <br/>
+ <table id="grid" class="grid"/>
+ </body>
+</html>
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.css b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.css
new file mode 100644
index 00000000..4fbb4788
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.css
@@ -0,0 +1,2750 @@
+/* w2ui 1.4 (c) http://w2ui.com, vitmalina@gmail.com */
+@font-face {
+ font-family: "w2ui-font";
+ src: url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAWIAAoAAAAACAgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAAA9AAAAEMAAABWQLxMsmNtYXAAAAE4AAAAOgAAAUriGRC2Z2x5ZgAAAXQAAAH9AAACgLu4vTRoZWFkAAADdAAAADAAAAA2AOYXBGhoZWEAAAOkAAAAIAAAACQD8wHHaG10eAAAA8QAAAAWAAAAIA7dAABsb2NhAAAD3AAAABIAAAASAngBuG1heHAAAAPwAAAAHwAAACABFQA2bmFtZQAABBAAAAEtAAACIsTQ/zJwb3N0AAAFQAAAAEgAAABi4/7ZEHicY2BkvMM4gYGVgYPRhTGNgYHBHUp/ZZBkaGFgYGJgZWbACgLSXFMYHD4yfmRnPPD/AIMe4wEGR6AwI0gOANHZC/IAeJxjYGBgZoBgGQZGBhBwAfIYwXwWBg0gzQakGRmYGBg+sv//D1LwkRFE8zNA1QMBIxvDiAcAddwGvgAAeJxFkLFv01AQxu97LrHdRImjpnaS1gnEia3IoqAktkkiEhaEOiCsDkEo8dyBDkytKpYKVWwssKKKAYmBREKMLJSFoRJ/AGJhY0NZGFgSzrUinvR+d++7T+/ePQLRci4IJ3SFCLIjG8CH+ZPwHHdwkkSS2PMXP3DGmUxpoo123lrt5nj8fjyejsc4W0zwNtl8FfHCKV5QnaOhF3IJUrUbkGPYnSGcGH6risBvGQhdVY0iVXXVkhJN1JL6/6xOIqWk4tRlJiVFiSJFSUrsZ+tkoqpEgt/6Hb/wjtZog2gonBx24AyFJUuNwMvha+/CvLi3vq1f785GsxGuTqfWc5O1N/r2+lNrOl38ZHnWpdUMr/CaLKLGZiHl4hI1+zasGB2/Dy9GSzfRbul4qaWPtHSQ0Y7SWpxmgnSc/mblMKNpmcOVEhfj+5d/8BGfqMl9bKuWFYWKaLf8YIAq9IKc5TY7ojNgTTcC7iEjaN4yPdswbM+81t8UimRLojq66YbdWq0bus375t21bwgcw/H6nmNUyhIkR/DsTasXPgx7VtV8oDx6XIxHE8vl8rMAzqlEDZ7QseUBPP6tLOQKDH5HqooKfLvB2gABa1lgflzI60VxsLd3IJj1YRn5/Wx9Syy++LvArn/JzH4e5WE98TCLer5wnBNb9WcrB5PoH084dg8AAAB4nGNgZGBgAOKMsPib8fw2Xxm4mRhA4PzjbBcY/f////1MjIwHgFwOBrA0AFcuDPF4nGNgZGBgPPD/AIMeEwMDw/9/TEwMQBEUwAEAe34EvHicY2JgYGCCYsbJCJpxO4QNABdTAesAAAAAAAAAEgAsAGgAjgC+AP4BQAAAeJxjYGRgYOBg0GJgZgABJiDmAkIGhv9gPgMADYEBTAB4nG2PTW7CMBCFXyBQFaQKtVKl7qwuuqkIPwsWHAD2LNiH4ARQEkeOQeICPUHP0DP0BF32DD1KX8IoixZbHn/z5o1/AAzwBQ/V8HBbx2q0cMPswm3SQNgnPwl30MezcJf6ULiHV8yE+3hAyBM8vzrtHk64hTu8Cbepvwv75A/hDh7xKdyl/i3cwxo/wn28eLN9ZPJhbHK30skxDW2TN7DWttybXE2CcaMtda5t6PRWbc6qPCVT52IVW5OpBas6TY0qrDnoyAU754r5aBSLHkQmwx4RDHL+Oq53hxU0EhyR8sf2Sv2/smaHRclKlStMEGB8xbekL6+9ITONLb0bnBlLnHjnlKqjW3FZ9mSkhfRqviclKxR17UAloh5gV3cVmGPEGf/xB/Ursl9uDmByAAAAeJxtwUEOgCAMBMAu0sI3SdMEIwKh8n8PXp2hQB+mf5kIAQciGIKEzFpNr6Sj7bs76xruMq3r2eJs22XZtPKIW1laiV6rCBDA") format("woff");
+ font-weight: normal;
+ font-style: normal;
+}
+[class^="w2ui-icon-"]:before,
+[class*=" w2ui-icon-"]:before {
+ font-family: "w2ui-font";
+ display: inline-block;
+ vertical-align: middle;
+ line-height: 1;
+ font-weight: normal;
+ font-style: normal;
+ speak: none;
+ text-decoration: inherit;
+ text-transform: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+/* Icons */
+.w2ui-icon-check:before {
+ content: "\f101";
+}
+.w2ui-icon-columns:before {
+ content: "\f102";
+}
+.w2ui-icon-cross:before {
+ content: "\f103";
+}
+.w2ui-icon-pencil:before {
+ content: "\f104";
+}
+.w2ui-icon-plus:before {
+ content: "\f105";
+}
+.w2ui-icon-reload:before {
+ content: "\f106";
+}
+.w2ui-icon-search:before {
+ content: "\f107";
+}
+/*************************************************
+* --- Reset (used for all w2ui wdigetes)
+* --- The reset is needed to coexist with other CSS
+* --- on the same page (for example bootstrap)
+*/
+.w2ui-reset {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+ font-family: Verdana, Arial, sans-serif;
+ font-size: 11px;
+}
+.w2ui-reset * {
+ color: default;
+ line-height: 100%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+ margin: 0px;
+ padding: 0px;
+}
+.w2ui-reset table {
+ font-family: Verdana, Arial, sans-serif;
+ font-size: 11px;
+ max-width: none;
+ background-color: transparent;
+ border-collapse: separate;
+ border-spacing: 0;
+}
+.w2ui-reset input,
+.w2ui-reset textarea {
+ width: auto;
+ height: auto;
+ vertical-align: baseline;
+ padding: 4px;
+}
+.w2ui-reset select {
+ padding: 1px;
+ height: 23px;
+}
+.w2ui-centered {
+ position: absolute;
+ left: 0px;
+ right: 0px;
+ top: 50%;
+ -webkit-transform: translateY(-50%);
+ -moz-transform: translateY(-50%);
+ -ms-transform: translateY(-50%);
+ -o-transform: translateY(-50%);
+ transform: translateY(-50%);
+ max-height: 100%;
+ margin: 0px;
+ padding: 0px 10px;
+ text-align: center;
+}
+.w2ui-disabled,
+.w2ui-readonly {
+ background-color: #f1f1f1 !important;
+ color: #777 !important;
+}
+/*************************************************
+* ---- Input Controls ----
+*/
+input:not([type=button]),
+select,
+textarea {
+ padding: 4px;
+ border: 1px solid #bbbbbb;
+ border-radius: 3px;
+ color: #000000;
+ background-color: #ffffff;
+}
+input:not([type=button]):focus,
+select:focus,
+textarea:focus {
+ outline-color: #72b2ff;
+}
+input:not([type=button]):disabled,
+select:disabled,
+textarea:disabled,
+input:not([type=button])[readonly],
+select[readonly],
+textarea[readonly] {
+ background-color: #f1f1f1;
+ color: #777;
+}
+/* IE9-11 specific classes */
+/* needs doblue :: */
+input::-ms-clear {
+ display: none;
+}
+input:-ms-input-placeholder {
+ color: #aaa !important;
+}
+select {
+ padding: 2px;
+}
+/* On/Off switch */
+input[type="checkbox"].w2ui-toggle {
+ position: absolute;
+ opacity: 0;
+ width: 46px;
+ height: 22px;
+ padding: 0px;
+ margin: 0px;
+ margin-left: 2px;
+}
+/* Track */
+input[type="checkbox"].w2ui-toggle + div {
+ display: inline-block;
+ width: 46px;
+ height: 22px;
+ border: 1px solid #bbb;
+ border-radius: 30px;
+ background-color: #eee;
+ -webkit-transition-duration: .3s;
+ -webkit-transition-property: background-color, box-shadow;
+ -moz-transition-duration: .3s;
+ -moz-transition-property: background-color, box-shadow;
+ box-shadow: inset 0 0 0 0px rgba(0, 0, 0, 0.4);
+ margin-left: 2px;
+}
+input[type="checkbox"].w2ui-toggle:disabled + div {
+ opacity: 0.3;
+}
+/* Knob */
+input[type="checkbox"].w2ui-toggle + div > div {
+ float: left;
+ width: 22px;
+ height: 22px;
+ border-radius: inherit;
+ background: #f5f5f5;
+ -webkit-transition-duration: 0.3s;
+ -webkit-transition-property: transform, background-color, box-shadow;
+ -moz-transition-duration: 0.3s;
+ -moz-transition-property: transform, background-color;
+ box-shadow: 0px 0px 1px #323232, 0 0 0 1px rgba(200, 200, 200, 0.6);
+ pointer-events: none;
+ margin-top: -1px;
+ margin-left: -1px;
+}
+/* Default Green */
+input[type="checkbox"].w2ui-toggle:checked + div {
+ border: 1px solid #00a23f;
+ box-shadow: inset 0 0 0 12px #54B350;
+}
+input[type="checkbox"].w2ui-toggle:checked + div > div {
+ -webkit-transform: translate3d(24px, 0, 0);
+ -moz-transform: translate3d(24px, 0, 0);
+ background-color: #ffffff;
+ box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.3), 0 0 0 1px #00a23f;
+}
+/* Blue */
+input[type="checkbox"].w2ui-toggle.blue:checked + div {
+ border: 1px solid #206FAD;
+ box-shadow: inset 0 0 0 12px #35A6EB;
+}
+input[type="checkbox"].w2ui-toggle.blue:checked + div > div {
+ box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.3), 0px 0px 0 1px #206fad;
+}
+input[type=checkbox].w2ui-toggle:focus {
+ outline: none;
+}
+/*************************************************
+* ---- Overlay and Bubble ----
+*/
+.w2ui-overlay {
+ position: absolute;
+ margin-top: 6px;
+ margin-left: -17px;
+ display: none;
+ z-index: 1300;
+ color: inherit;
+ background-color: #fbfbfb;
+ border: 3px solid #777777;
+ box-shadow: 0px 2px 10px #999999;
+ border-radius: 4px;
+ text-align: left;
+}
+.w2ui-overlay table td {
+ color: inherit;
+}
+.w2ui-overlay:before {
+ content: "";
+ position: absolute;
+ -webkit-transform: rotate(-45deg);
+ -moz-transform: rotate(-45deg);
+ -ms-transform: rotate(-45deg);
+ -o-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+ width: 12px;
+ height: 12px;
+ border: 3px solid #777777;
+ border-color: inherit;
+ background-color: inherit;
+ border-left: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ border-bottom-left-radius: 50px;
+ margin: -9px 0 0 30px;
+}
+.w2ui-overlay:after {
+ display: none;
+ content: "";
+ position: absolute;
+ -webkit-transform: rotate(135deg);
+ -moz-transform: rotate(135deg);
+ -ms-transform: rotate(135deg);
+ -o-transform: rotate(135deg);
+ transform: rotate(135deg);
+ width: 12px;
+ height: 12px;
+ border: 3px solid #777777;
+ border-color: inherit;
+ background-color: inherit;
+ border-left: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ border-bottom-left-radius: 50px;
+ margin: -7px 0 0 30px;
+}
+.w2ui-overlay.w2ui-overlay-popup {
+ z-index: 1700;
+}
+.w2ui-tag {
+ position: absolute;
+ z-index: 1300;
+ opacity: 0;
+ -webkit-transition: opacity 0.3s;
+ -moz-transition: opacity 0.3s;
+ -ms-transition: opacity 0.3s;
+ -o-transition: opacity 0.3s;
+ transition: opacity 0.3s;
+}
+.w2ui-tag .w2ui-tag-body {
+ background-color: rgba(60, 60, 60, 0.82);
+ display: inline-block;
+ position: absolute;
+ border-radius: 4px;
+ padding: 4px 10px;
+ margin-left: 10px;
+ margin-top: 0px;
+ color: #ffffff !important;
+ box-shadow: 1px 1px 3px #000000;
+ line-height: 100%;
+ font-size: 11px;
+ font-family: Verdana, Arial, sans-serif;
+}
+.w2ui-tag .w2ui-tag-body:before {
+ content: "";
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-top: 5px solid transparent;
+ border-right: 5px solid rgba(60, 60, 60, 0.82);
+ border-bottom: 5px solid transparent;
+ margin: 2px 0 0 -15px;
+}
+.w2ui-tag.w2ui-tag-popup {
+ z-index: 1700;
+}
+/*
+* Drop down menu
+*/
+.w2ui-overlay table.w2ui-drop-menu {
+ width: 100%;
+ color: #000000;
+ background-color: #ffffff;
+ padding: 5px 0px;
+ cursor: default;
+}
+.w2ui-overlay table.w2ui-drop-menu td {
+ white-space: nowrap;
+}
+.w2ui-overlay table.w2ui-drop-menu .w2ui-item-even {
+ color: inherit;
+ background-color: #ffffff;
+}
+.w2ui-overlay table.w2ui-drop-menu .w2ui-item-odd {
+ color: inherit;
+ background-color: #f3f6fa;
+}
+.w2ui-overlay table.w2ui-drop-menu .w2ui-item-group {
+ color: #444;
+ font-weight: bold;
+ background-color: #ECEDF0;
+ border-bottom: 1px solid #D3D2D4;
+}
+.w2ui-overlay table.w2ui-drop-menu td.menu-icon {
+ padding: 3px 0px 4px 6px;
+ width: 20px;
+}
+.w2ui-overlay table.w2ui-drop-menu td.menu-text {
+ padding: 8px 10px 8px 5px;
+ width: auto;
+}
+.w2ui-overlay table.w2ui-drop-menu td.menu-count {
+ text-align: right;
+}
+.w2ui-overlay table.w2ui-drop-menu td.menu-count > span {
+ border: 1px solid #9da4af;
+ border-radius: 20px;
+ width: auto;
+ height: 18px;
+ padding: 2px 7px;
+ margin: 3px 5px 0px 5px;
+ background-color: #e7f0fc;
+ color: #667274;
+ box-shadow: 0 0 2px #ffffff;
+ text-shadow: 1px 1px 1px #e6e6e6;
+}
+.w2ui-overlay table.w2ui-drop-menu tr:hover {
+ color: inherit;
+ background-color: #e6f0ff;
+}
+.w2ui-overlay table.w2ui-drop-menu tr.w2ui-selected {
+ background-color: #b6d5fb;
+}
+.w2ui-overlay table.w2ui-drop-menu tr.w2ui-selected td {
+ color: inherit;
+}
+.w2ui-overlay table.w2ui-drop-menu tr.w2ui-disabled {
+ opacity: 0.4;
+ background-color: white !important;
+}
+.w2ui-overlay table.w2ui-drop-menu .w2ui-icon {
+ font-size: 14px;
+ color: #8d99a7;
+ display: inline-block;
+ padding-top: 4px;
+}
+/*************************************************
+* ---- Common Classes ----
+*/
+.w2ui-marker {
+ color: #444;
+ background-color: rgba(252, 244, 161, 0.48);
+}
+.w2ui-spinner {
+ display: inline-block;
+ background-size: 100%;
+ background-repeat: no-repeat;
+ background-image: url(data:image/gif;base64,R0lGODlhgACAAKIAAP///93d3bu7u5mZmQAA/wAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBQAEACwCAAIAfAB8AAAD/0i63P4wygYqmDjrzbtflvWNZGliYXiubKuloivPLlzReD7al+7/Eh5wSFQIi8hHYBkwHUmD6CD5YTJLz49USuVYraRsZ7vtar7XnQ1Kjpoz6LRHvGlz35O4nEPP2O94EnpNc2sef1OBGIOFMId/inB6jSmPdpGScR19EoiYmZobnBCIiZ95k6KGGp6ni4wvqxilrqBfqo6skLW2YBmjDa28r6Eosp27w8Rov8ekycqoqUHODrTRvXsQwArC2NLF29UM19/LtxO5yJd4Au4CK7DUNxPebG4e7+8n8iv2WmQ66BtoYpo/dvfacBjIkITBE9DGlMvAsOIIZjIUAixliv9ixYZVtLUos5GjwI8gzc3iCGghypQqrbFsme8lwZgLZtIcYfNmTJ34WPTUZw5oRxdD9w0z6iOpO15MgTh1BTTJUKos39jE+o/KS64IFVmsFfYT0aU7capdy7at27dw48qdS7eu3bt480I02vUbX2F/JxYNDImw4GiGE/P9qbhxVpWOI/eFKtlNZbWXuzlmG1mv58+gQ4seTbq06dOoU6vGQZJy0FNlMcV+czhQ7SQmYd8eMhPs5BxVdfcGEtV3buDBXQ+fURxx8oM6MT9P+Fh6dOrH2zavc13u9JXVJb520Vp8dvC76wXMuN5Sepm/1WtkEZHDefnzR9Qvsd9+/wi8+en3X0ntYVcSdAE+UN4zs7ln24CaLagghIxBaGF8kFGoIYV+Ybghh841GIyI5ICIFoklJsigihmimJOLEbLYIYwxSgigiZ+8l2KB+Ml4oo/w8dijjcrouCORKwIpnJIjMnkkksalNeR4fuBIm5UEYImhIlsGCeWNNJphpJdSTlkml1jWeOY6TnaRpppUctcmFW9mGSaZceYopH9zkjnjUe59iR5pdapWaGqHopboaYua1qije67GJ6CuJAAAIfkEBQUABAAsCgACAFcAMAAAA/9Iutz+ML5Ag7w46z0r5WAoSp43nihXVmnrdusrv+s332dt4Tyo9yOBUJD6oQBIQGs4RBlHySSKyczVTtHoidocPUNZaZAr9F5FYbGI3PWdQWn1mi36buLKFJvojsHjLnshdhl4L4IqbxqGh4gahBJ4eY1kiX6LgDN7fBmQEJI4jhieD4yhdJ2KkZk8oiSqEaatqBekDLKztBG2CqBACq4wJRi4PZu1sA2+v8C6EJexrBAD1AOBzsLE0g/V1UvYR9sN3eR6lTLi4+TlY1wz6Qzr8u1t6FkY8vNzZTxaGfn6mAkEGFDgL4LrDDJDyE4hEIbdHB6ESE1iD4oVLfLAqPETIsOODwmCDJlv5MSGJklaS6khAQAh+QQFBQAEACwfAAIAVwAwAAAD/0i63P5LSAGrvTjrNuf+YKh1nWieIumhbFupkivPBEzR+GnnfLj3ooFwwPqdAshAazhEGUXJJIrJ1MGOUamJ2jQ9QVltkCv0XqFh5IncBX01afGYnDqD40u2z76JK/N0bnxweC5sRB9vF34zh4gjg4uMjXobihWTlJUZlw9+fzSHlpGYhTminKSepqebF50NmTyor6qxrLO0L7YLn0ALuhCwCrJAjrUqkrjGrsIkGMW/BMEPJcphLgDaABjUKNEh29vdgTLLIOLpF80s5xrp8ORVONgi8PcZ8zlRJvf40tL8/QPYQ+BAgjgMxkPIQ6E6hgkdjoNIQ+JEijMsasNY0RQix4gKP+YIKXKkwJIFF6JMudFEAgAh+QQFBQAEACw8AAIAQgBCAAAD/kg0PPowykmrna3dzXvNmSeOFqiRaGoyaTuujitv8Gx/661HtSv8gt2jlwIChYtc0XjcEUnMpu4pikpv1I71astytkGh9wJGJk3QrXlcKa+VWjeSPZHP4Rtw+I2OW81DeBZ2fCB+UYCBfWRqiQp0CnqOj4J1jZOQkpOUIYx/m4oxg5cuAaYBO4Qop6c6pKusrDevIrG2rkwptrupXB67vKAbwMHCFcTFxhLIt8oUzLHOE9Cy0hHUrdbX2KjaENzey9Dh08jkz8Tnx83q66bt8PHy8/T19vf4+fr6AP3+/wADAjQmsKDBf6AOKjS4aaHDgZMeSgTQcKLDhBYPEswoA1BBAgAh+QQFBQAEACxOAAoAMABXAAAD7Ei6vPOjyUkrhdDqfXHm4OZ9YSmNpKmiqVqykbuysgvX5o2HcLxzup8oKLQQix0UcqhcVo5ORi+aHFEn02sDeuWqBGCBkbYLh5/NmnldxajX7LbPBK+PH7K6narfO/t+SIBwfINmUYaHf4lghYyOhlqJWgqDlAuAlwyBmpVnnaChoqOkpaanqKmqKgGtrq+wsbA1srW2ry63urasu764Jr/CAb3Du7nGt7TJsqvOz9DR0tPU1TIA2ACl2dyi3N/aneDf4uPklObj6OngWuzt7u/d8fLY9PXr9eFX+vv8+PnYlUsXiqC3c6PmUUgAACH5BAUFAAQALE4AHwAwAFcAAAPpSLrc/m7IAau9bU7MO9GgJ0ZgOI5leoqpumKt+1axPJO1dtO5vuM9yi8TlAyBvSMxqES2mo8cFFKb8kzWqzDL7Xq/4LB4TC6bz1yBes1uu9uzt3zOXtHv8xN+Dx/x/wJ6gHt2g3Rxhm9oi4yNjo+QkZKTCgGWAWaXmmOanZhgnp2goaJdpKGmp55cqqusrZuvsJays6mzn1m4uRAAvgAvuBW/v8GwvcTFxqfIycA3zA/OytCl0tPPO7HD2GLYvt7dYd/ZX99j5+Pi6tPh6+bvXuTuzujxXens9fr7YPn+7egRI9PPHrgpCQAAIfkEBQUABAAsPAA8AEIAQgAAA/lIutz+UI1Jq7026h2x/xUncmD5jehjrlnqSmz8vrE8u7V5z/m5/8CgcEgsGo/IpHLJbDqf0Kh0ShBYBdTXdZsdbb/Yrgb8FUfIYLMDTVYz2G13FV6Wz+lX+x0fdvPzdn9WeoJGAYcBN39EiIiKeEONjTt0kZKHQGyWl4mZdREAoQAcnJhBXBqioqSlT6qqG6WmTK+rsa1NtaGsuEu6o7yXubojsrTEIsa+yMm9SL8osp3PzM2cStDRykfZ2tfUtS/bRd3ewtzV5pLo4eLjQuUp70Hx8t9E9eqO5Oku5/ztdkxi90qPg3x2EMpR6IahGocPCxp8AGtigwQAIfkEBQUABAAsHwBOAFcAMAAAA/9Iutz+MMo36pg4682J/V0ojs1nXmSqSqe5vrDXunEdzq2ta3i+/5DeCUh0CGnF5BGULC4tTeUTFQVONYAs4CfoCkZPjFar83rBx8l4XDObSUL1Ott2d1U4yZwcs5/xSBB7dBMBhgEYfncrTBGDW4WHhomKUY+QEZKSE4qLRY8YmoeUfkmXoaKInJ2fgxmpqqulQKCvqRqsP7WooriVO7u8mhu5NacasMTFMMHCm8qzzM2RvdDRK9PUwxzLKdnaz9y/Kt8SyR3dIuXmtyHpHMcd5+jvWK4i8/TXHff47SLjQvQLkU+fG29rUhQ06IkEG4X/Rryp4mwUxSgLL/7IqFETB8eONT6ChCFy5ItqJomES6kgAQAh+QQFBQAEACwKAE4AVwAwAAAD/0i63A4QuEmrvTi3yLX/4MeNUmieITmibEuppCu3sDrfYG3jPKbHveDktxIaF8TOcZmMLI9NyBPanFKJp4A2IBx4B5lkdqvtfb8+HYpMxp3Pl1qLvXW/vWkli16/3dFxTi58ZRcChwIYf3hWBIRchoiHiotWj5AVkpIXi4xLjxiaiJR/T5ehoomcnZ+EGamqq6VGoK+pGqxCtaiiuJVBu7yaHrk4pxqwxMUzwcKbyrPMzZG90NGDrh/JH8t72dq3IN1jfCHb3L/e5ebh4ukmxyDn6O8g08jt7tf26ybz+m/W9GNXzUQ9fm1Q/APoSWAhhfkMAmpEbRhFKwsvCsmosRIHx444PoKcIXKkjIImjTzjkQAAIfkEBQUABAAsAgA8AEIAQgAAA/VIBNz+8KlJq72Yxs1d/uDVjVxogmQqnaylvkArT7A63/V47/m2/8CgcEgsGo/IpHLJbDqf0Kh0Sj0FroGqDMvVmrjgrDcTBo8v5fCZki6vCW33Oq4+0832O/at3+f7fICBdzsChgJGeoWHhkV0P4yMRG1BkYeOeECWl5hXQ5uNIAOjA1KgiKKko1CnqBmqqk+nIbCkTq20taVNs7m1vKAnurtLvb6wTMbHsUq4wrrFwSzDzcrLtknW16tI2tvERt6pv0fi48jh5h/U6Zs77EXSN/BE8jP09ZFA+PmhP/xvJgAMSGBgQINvEK5ReIZhQ3QEMTBLAAAh+QQFBQAEACwCAB8AMABXAAAD50i6DA4syklre87qTbHn4OaNYSmNqKmiqVqyrcvBsazRpH3jmC7yD98OCBF2iEXjBKmsAJsWHDQKmw571l8my+16v+CweEwum8+hgHrNbrvbtrd8znbR73MVfg838f8BeoB7doN0cYZvaIuMjY6PkJGSk2gClgJml5pjmp2YYJ6dX6GeXaShWaeoVqqlU62ir7CXqbOWrLafsrNctjIDwAMWvC7BwRWtNsbGFKc+y8fNsTrQ0dK3QtXAYtrCYd3eYN3c49/a5NVj5eLn5u3s6e7x8NDo9fbL+Mzy9/T5+tvUzdN3Zp+GBAAh+QQJBQAEACwCAAIAfAB8AAAD/0i63P4wykmrvTjrzbv/YCiOZGmeaKqubOu+cCzPdArcQK2TOL7/nl4PSMwIfcUk5YhUOh3M5nNKiOaoWCuWqt1Ou16l9RpOgsvEMdocXbOZ7nQ7DjzTaeq7zq6P5fszfIASAYUBIYKDDoaGIImKC4ySH3OQEJKYHZWWi5iZG0ecEZ6eHEOio6SfqCaqpaytrpOwJLKztCO2jLi1uoW8Ir6/wCHCxMG2x7muysukzb230M6H09bX2Nna29zd3t/g4cAC5OXm5+jn3Ons7eba7vHt2fL16tj2+QL0+vXw/e7WAUwnrqDBgwgTKlzIsKHDh2gGSBwAccHEixAvaqTYcFCjRoYeNyoM6REhyZIHT4o0qPIjy5YTTcKUmHImx5cwE85cmJPnSYckK66sSAAj0aNIkypdyrSp06dQo0qdSrWq1atYs2rdyrWr169gwxZJAAA7);
+}
+/* common icons */
+.w2ui-icon {
+ background-repeat: no-repeat;
+ height: 16px;
+ width: 16px;
+ overflow: hidden;
+ margin: 2px 2px;
+ display: inline-block;
+}
+.w2ui-icon.icon-search,
+.w2ui-icon.icon-search-down {
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAgCAYAAAB+ZAqzAAACuElEQVRYw9WXSWhTQRjHR0UKLqhFaV0OUih68GAOWjyJKypCpAoV8aIiioIICiKiB1GMtE3MYmry2moXDz1UDx7sUXHBhQpSaRVxrYpWcMO9avx/8AJh/CbznHkxdeB3Cd/8589kvuUJkWcdjCTHghUgAi6DJ+AVeAqugSQIggniXywcNBJsB70g44EHYBcYXUhTM8EFj4ZkboKqQpiqAv2GprK8o7/f75t6pjn0M3gNPmri3vtycxAZA64qDvkJ2kENqAQTQQWoBg74qth3B4y3NbZDIX4fzNfsnQtuK/YfsjFVCh4pMq3Co0Y5uMVoUGkpy8aFT5xaeSzVEo45bXdBt4LeaLq1k0RXMYJfdDfFmAuAD4zWlty4UNyZEkm19MUb2zMw8Sfp1u+IWSrcIimLnTG8/SijdU6OO5poDESdtgHZVBzUHm/amhW7zoitMTS2mNHqASPk2FDCCcLMYK6p+obmulyxfiYLA4bGKFvfSnrUvkq5+Lpk8z4yRH8r3l/X4WiqJFfspSQ0CGYZGpsMnkt6L+h31Z76hpMdeOwPQ7H0NFnssST0C8wxNDaDKb6kP06150gsHahNNlVzYheZd7HJ0BiX4VRGhpmIhRixKyZilM2M1mnTArtIUbU3/qVO0H0GvmQ4CY4C3YopYYlHjXlggNG4R33Ypi2tVtwaPeTdNMkq9pVQZQdvFPs32zbx4aAjzxhDRfIAWAeWg7VgrzsY5ht/zoNJtubKwA3LITGjSKRyW3NTwaUCmKOSMd3WHH0ZJRQZZkOP1zFKZ3CB++4+aQ6kEeksWAb2a2L7qDv49S1Q6T72MOgEXa6RGFhP3wpS/B6NOWpRs0UxFg7eqTFHjX1hscxtAz/ymEuIYi0cvgF8Y0w5Ro3dZ3M1boJkTaXEUFlug6fsdsRQWzTj0cey+N/Xb2sj5lTh2M6OAAAAAElFTkSuQmCC) no-repeat center !important;
+ background-size: 14px 12px !important;
+ opacity: 0.9;
+}
+.w2ui-icon.icon-folder {
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGrSURBVDjLxZO7ihRBFIa/6u0ZW7GHBUV0UQQTZzd3QdhMQxOfwMRXEANBMNQX0MzAzFAwEzHwARbNFDdwEd31Mj3X7a6uOr9BtzNjYjKBJ6nicP7v3KqcJFaxhBVtZUAK8OHlld2st7Xl3DJPVONP+zEUV4HqL5UDYHr5xvuQAjgl/Qs7TzvOOVAjxjlC+ePSwe6DfbVegLVuT4r14eTr6zvA8xSAoBLzx6pvj4l+DZIezuVkG9fY2H7YRQIMZIBwycmzH1/s3F8AapfIPNF3kQk7+kw9PWBy+IZOdg5Ug3mkAATy/t0usovzGeCUWTjCz0B+Sj0ekfdvkZ3abBv+U4GaCtJ1iEm6ANQJ6fEzrG/engcKw/wXQvEKxSEKQxRGKE7Izt+DSiwBJMUSm71rguMYhQKrBygOIRStf4TiFFRBvbRGKiQLWP29yRSHKBTtfdBmHs0BUpgvtgF4yRFR+NUKi0XZcYjCeCG2smkzLAHkbRBmP0/Uk26O5YnUActBp1GsAI+S5nRJJJal5K1aAMrq0d6Tm9uI6zjyf75dAe6tx/SsWeD//o2/Ab6IH3/h25pOAAAAAElFTkSuQmCC) no-repeat center !important;
+}
+.w2ui-icon.icon-page {
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAINSURBVBgZBcG/r55zGAfg6/4+z3va01NHlYgzEfE7MdCIGISFgS4Gk8ViYyM2Mdlsko4GSf8Do0FLRCIkghhYJA3aVBtEz3nP89wf11VJvPDepdd390+8Nso5nESBQoq0pfvXm9fzWf19453LF85vASqJlz748vInb517dIw6EyYBIIG49u+xi9/c9MdvR//99MPPZ7+4cP4IZhhTPbwzT2d+vGoaVRRp1rRliVvHq+cfvM3TD82+7mun0o/ceO7NT+/4/KOXjwZU1ekk0840bAZzMQ2mooqh0A72d5x/6sB9D5zYnff3PoYBoWBgFKPKqDKqjCpjKr//dcu9p489dra88cydps30KswACfNEKanSaxhlntjJ8Mv12Paie+vZ+0+oeSwwQ0Iw1xAR1CiFNJkGO4wu3ZMY1AAzBI0qSgmCNJsJUEOtJSMaCTBDLyQ0CknAGOgyTyFFiLI2awMzdEcSQgSAAKVUmAeNkxvWJWCGtVlDmgYQ0GFtgg4pNtOwbBcwQy/Rife/2yrRRVI0qYCEBly8Z+P4qMEMy7JaVw72N568e+iwhrXoECQkfH91kY7jwwXMsBx1L93ZruqrK6uuiAIdSnTIKKPLPFcvay8ww/Hh+ufeznTXu49v95IMoQG3784gYXdTqvRmqn/Wpa/ADFX58MW3L71SVU9ETgEIQQQIOOzub+fhIvwPRDgeVjWDahIAAAAASUVORK5CYII=) no-repeat center !important;
+}
+/*************************************************
+* ---- Locking portion of the screen (in grid, form, layout)
+*/
+.w2ui-lock {
+ display: none;
+ position: absolute;
+ z-index: 1400;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ height: 100%;
+ opacity: 0.15;
+ filter: alpha(opacity=15);
+ background-color: #333333;
+}
+.w2ui-lock-msg {
+ display: none;
+ position: absolute;
+ z-index: 1400;
+ top: 45%;
+ left: 50%;
+ -webkit-transform: translateX(-50%) translateY(-50%);
+ -moz-transform: translateX(-50%) translateY(-50%);
+ -ms-transform: translateX(-50%) translateY(-50%);
+ -o-transform: translateX(-50%) translateY(-50%);
+ transform: translateX(-50%) translateY(-50%);
+ width: 200px;
+ height: 80px;
+ padding: 30px 8px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ font-size: 13px;
+ font-family: Verdana, Arial, sans-serif;
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+ background-color: #555555;
+ color: #ffffff;
+ text-align: center;
+ border-radius: 5px;
+ border: 2px solid #444444;
+}
+.w2ui-lock-msg .w2ui-spinner {
+ display: inline-block;
+ width: 24px;
+ height: 24px;
+ margin: -3px 8px -7px -10px;
+}
+button.btn {
+ display: inline-block;
+ border-radius: 4px;
+ margin: 0px 5px;
+ padding: 7px 12px 6px 12px !important;
+ color: #666;
+ font-size: 12px !important;
+ border: 1px solid #B6B6B6;
+ background-image: -webkit-linear-gradient(#ffffff 0%, #e7e7e7 100%);
+ background-image: -moz-linear-gradient(#ffffff 0%, #e7e7e7 100%);
+ background-image: -ms-linear-gradient(#ffffff 0%, #e7e7e7 100%);
+ background-image: -o-linear-gradient(#ffffff 0%, #e7e7e7 100%);
+ background-image: linear-gradient(#ffffff 0%, #e7e7e7 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffe7e7e7', endColorstr='#ffffffff', GradientType=0);
+ outline: none;
+ box-shadow: 0px 1px 0px white;
+ cursor: default;
+ min-width: 75px;
+ line-height: 100% !important;
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+button.btn:hover {
+ text-decoration: none;
+ border: 1px solid #bbb;
+ background-image: -webkit-linear-gradient(#f7f7f7 0%, #dddddd 100%);
+ background-image: -moz-linear-gradient(#f7f7f7 0%, #dddddd 100%);
+ background-image: -ms-linear-gradient(#f7f7f7 0%, #dddddd 100%);
+ background-image: -o-linear-gradient(#f7f7f7 0%, #dddddd 100%);
+ background-image: linear-gradient(#f7f7f7 0%, #dddddd 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffdddddd', endColorstr='#fff7f7f7', GradientType=0);
+ color: #333;
+}
+button.btn:active,
+button.btn.clicked {
+ border: 1px solid #999;
+ background-image: -webkit-linear-gradient(#cccccc 0%, #cccccc 100%);
+ background-image: -moz-linear-gradient(#cccccc 0%, #cccccc 100%);
+ background-image: -ms-linear-gradient(#cccccc 0%, #cccccc 100%);
+ background-image: -o-linear-gradient(#cccccc 0%, #cccccc 100%);
+ background-image: linear-gradient(#cccccc 0%, #cccccc 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffcccccc', endColorstr='#ffcccccc', GradientType=0);
+ text-shadow: 1px 1px 1px #eee;
+}
+button.btn:disabled {
+ border: 1px solid #bbb !important;
+ background: #f7f7f7 !important;
+ color: #bdbcbc !important;
+ text-shadow: none !important;
+}
+button.btn-blue {
+ color: white;
+ background-image: -webkit-linear-gradient(#80c0f7 0%, #269df0 100%);
+ background-image: -moz-linear-gradient(#80c0f7 0%, #269df0 100%);
+ background-image: -ms-linear-gradient(#80c0f7 0%, #269df0 100%);
+ background-image: -o-linear-gradient(#80c0f7 0%, #269df0 100%);
+ background-image: linear-gradient(#80c0f7 0%, #269df0 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff269df0', endColorstr='#ff80c0f7', GradientType=0);
+ border: 1px solid #538AB7;
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-blue:hover {
+ color: white;
+ background-image: -webkit-linear-gradient(#73b6f0 0%, #2391dd 100%);
+ background-image: -moz-linear-gradient(#73b6f0 0%, #2391dd 100%);
+ background-image: -ms-linear-gradient(#73b6f0 0%, #2391dd 100%);
+ background-image: -o-linear-gradient(#73b6f0 0%, #2391dd 100%);
+ background-image: linear-gradient(#73b6f0 0%, #2391dd 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff2391dd', endColorstr='#ff73b6f0', GradientType=0);
+ border: 1px solid #497BA3;
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-blue:active,
+button.btn-blue.clicked {
+ color: white;
+ background-image: -webkit-linear-gradient(#1e83c9 0%, #1e83c9 100%);
+ background-image: -moz-linear-gradient(#1e83c9 0%, #1e83c9 100%);
+ background-image: -ms-linear-gradient(#1e83c9 0%, #1e83c9 100%);
+ background-image: -o-linear-gradient(#1e83c9 0%, #1e83c9 100%);
+ background-image: linear-gradient(#1e83c9 0%, #1e83c9 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff1e83c9', endColorstr='#ff1e83c9', GradientType=0);
+ border: 1px solid #1268A6;
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-green {
+ color: white;
+ background-image: -webkit-linear-gradient(#81cf81 0%, #52a452 100%);
+ background-image: -moz-linear-gradient(#81cf81 0%, #52a452 100%);
+ background-image: -ms-linear-gradient(#81cf81 0%, #52a452 100%);
+ background-image: -o-linear-gradient(#81cf81 0%, #52a452 100%);
+ background-image: linear-gradient(#81cf81 0%, #52a452 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff52a452', endColorstr='#ff81cf81', GradientType=0);
+ border: 1px solid #479247;
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-green:hover {
+ color: white;
+ background-image: -webkit-linear-gradient(#6abe68 0%, #3f8f3d 100%);
+ background-image: -moz-linear-gradient(#6abe68 0%, #3f8f3d 100%);
+ background-image: -ms-linear-gradient(#6abe68 0%, #3f8f3d 100%);
+ background-image: -o-linear-gradient(#6abe68 0%, #3f8f3d 100%);
+ background-image: linear-gradient(#6abe68 0%, #3f8f3d 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff3f8f3d', endColorstr='#ff6abe68', GradientType=0);
+ border: 1px solid #479247;
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-green:active,
+button.btn-green.clicked {
+ color: white;
+ background-image: -webkit-linear-gradient(#377d36 0%, #377d36 100%);
+ background-image: -moz-linear-gradient(#377d36 0%, #377d36 100%);
+ background-image: -ms-linear-gradient(#377d36 0%, #377d36 100%);
+ background-image: -o-linear-gradient(#377d36 0%, #377d36 100%);
+ background-image: linear-gradient(#377d36 0%, #377d36 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff377d36', endColorstr='#ff377d36', GradientType=0);
+ border: 1px solid #555 !important;
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-orange {
+ color: white;
+ background-image: -webkit-linear-gradient(#fcc272 0%, #fb8822 100%);
+ background-image: -moz-linear-gradient(#fcc272 0%, #fb8822 100%);
+ background-image: -ms-linear-gradient(#fcc272 0%, #fb8822 100%);
+ background-image: -o-linear-gradient(#fcc272 0%, #fb8822 100%);
+ background-image: linear-gradient(#fcc272 0%, #fb8822 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fffb8822', endColorstr='#fffcc272', GradientType=0);
+ border: 1px solid #B68B4C;
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-orange:hover {
+ color: white;
+ background-image: -webkit-linear-gradient(#f4ad59 0%, #f1731f 100%);
+ background-image: -moz-linear-gradient(#f4ad59 0%, #f1731f 100%);
+ background-image: -ms-linear-gradient(#f4ad59 0%, #f1731f 100%);
+ background-image: -o-linear-gradient(#f4ad59 0%, #f1731f 100%);
+ background-image: linear-gradient(#f4ad59 0%, #f1731f 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fff1731f', endColorstr='#fff4ad59', GradientType=0);
+ border: 1px solid #B68B4C;
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-orange:active,
+button.btn-orange.clicked {
+ color: white;
+ border: 1px solid #666;
+ background-image: -webkit-linear-gradient(#b98747 0%, #b98747 100%);
+ background-image: -moz-linear-gradient(#b98747 0%, #b98747 100%);
+ background-image: -ms-linear-gradient(#b98747 0%, #b98747 100%);
+ background-image: -o-linear-gradient(#b98747 0%, #b98747 100%);
+ background-image: linear-gradient(#b98747 0%, #b98747 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffb98747', endColorstr='#ffb98747', GradientType=0);
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-red {
+ color: white;
+ background-image: -webkit-linear-gradient(#ff6e70 0%, #c72d2d 100%);
+ background-image: -moz-linear-gradient(#ff6e70 0%, #c72d2d 100%);
+ background-image: -ms-linear-gradient(#ff6e70 0%, #c72d2d 100%);
+ background-image: -o-linear-gradient(#ff6e70 0%, #c72d2d 100%);
+ background-image: linear-gradient(#ff6e70 0%, #c72d2d 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffc72d2d', endColorstr='#ffff6e70', GradientType=0);
+ border: 1px solid #BB3C3E;
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-red:hover {
+ color: white;
+ background-image: -webkit-linear-gradient(#ee696c 0%, #ae2527 100%);
+ background-image: -moz-linear-gradient(#ee696c 0%, #ae2527 100%);
+ background-image: -ms-linear-gradient(#ee696c 0%, #ae2527 100%);
+ background-image: -o-linear-gradient(#ee696c 0%, #ae2527 100%);
+ background-image: linear-gradient(#ee696c 0%, #ae2527 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffae2527', endColorstr='#ffee696c', GradientType=0);
+ border: 1px solid #BB3C3E;
+ text-shadow: 1px 1px 1px #777777;
+}
+button.btn-red:active,
+button.btn-red.clicked {
+ color: white;
+ border: 1px solid #861C1E;
+ background-image: -webkit-linear-gradient(#9c2123 0%, #9c2123 100%);
+ background-image: -moz-linear-gradient(#9c2123 0%, #9c2123 100%);
+ background-image: -ms-linear-gradient(#9c2123 0%, #9c2123 100%);
+ background-image: -o-linear-gradient(#9c2123 0%, #9c2123 100%);
+ background-image: linear-gradient(#9c2123 0%, #9c2123 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff9c2123', endColorstr='#ff9c2123', GradientType=0);
+ text-shadow: 1px 1px 1px #777777;
+}
+/*************************************************
+* ---- Forms ----
+*/
+.w2ui-form {
+ position: relative;
+ color: #000000;
+ background-color: #f5f6f7;
+ border: 1px solid #c0c0c0;
+ border-radius: 3px;
+ padding: 0px;
+ overflow: hidden !important;
+}
+.w2ui-form > div {
+ position: absolute;
+ overflow: hidden;
+}
+.w2ui-form .w2ui-form-header {
+ position: absolute;
+ left: 0;
+ right: 0;
+ border-bottom: 1px solid #99bbe8 !important;
+ overflow: hidden;
+ color: #444444;
+ font-size: 13px;
+ text-align: center;
+ padding: 8px;
+ background-image: -webkit-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: -moz-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: -ms-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: -o-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: linear-gradient(#dae6f3, #c2d5ed);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffdae6f3', endColorstr='#ffc2d5ed', GradientType=0);
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+}
+.w2ui-form .w2ui-form-toolbar {
+ position: absolute;
+ left: 0px;
+ right: 0px;
+ margin: 0px;
+ padding: 6px 3px;
+ border-bottom: 1px solid #d5d8d8;
+}
+.w2ui-form .w2ui-form-tabs {
+ margin: 0px;
+ padding: 0px;
+}
+.w2ui-form .w2ui-tabs {
+ position: absolute;
+ left: 0;
+ right: 0;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+ padding-top: 5px !important;
+ background-color: #fafafa;
+}
+.w2ui-form .w2ui-tabs .w2ui-tab.active {
+ background-color: #f5f6f7;
+}
+.w2ui-form .w2ui-page {
+ position: absolute;
+ left: 0;
+ right: 0;
+ overflow: auto;
+ padding: 10px;
+ border-left: 1px solid inherit;
+ border-right: 1px solid inherit;
+ background-color: inherit;
+ border-radius: 3px;
+}
+.w2ui-form .w2ui-buttons {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ text-align: center;
+ border-top: 1px solid #d5d8d8;
+ border-bottom: 0px solid #d5d8d8;
+ background-color: #fafafa;
+ padding: 15px 0px !important;
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
+}
+.w2ui-form .w2ui-buttons input[type="button"],
+.w2ui-form .w2ui-buttons button {
+ min-width: 80px;
+ margin-right: 5px;
+}
+.w2ui-form input[type=checkbox],
+.w2ui-form input[type=radio] {
+ margin-top: 4px;
+ margin-bottom: 4px;
+}
+.w2ui-form input[type=checkbox].w2ui-toggle {
+ margin: 0px;
+}
+.w2ui-group-title {
+ padding: 5px 2px;
+ color: #8D96A2;
+ text-shadow: 1px 1px 2px #fdfdfd;
+ font-size: 120%;
+}
+.w2ui-group {
+ background-color: #ebecef;
+ margin: 5px 0px 10px 0px;
+ padding: 10px 5px;
+ border-top: 1px solid #cedcea;
+ border-bottom: 1px solid #cedcea;
+}
+.w2ui-field > label {
+ display: block;
+ float: left;
+ margin-top: 7px;
+ margin-bottom: 3px;
+ width: 120px;
+ padding: 0px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ text-align: right;
+ min-height: 20px;
+ color: #666;
+}
+.w2ui-field > div {
+ /* do not include width */
+ margin-bottom: 3px;
+ margin-left: 128px;
+ padding: 3px;
+ min-height: 28px;
+ float: none;
+}
+.w2ui-field.w2ui-required > div {
+ position: relative;
+}
+.w2ui-field.w2ui-required > div::before {
+ content: '*';
+ position: absolute;
+ margin-top: 5px;
+ margin-left: -9px;
+ color: #ff0000;
+}
+.w2ui-field.w2ui-span1 > label {
+ width: 20px;
+}
+.w2ui-field.w2ui-span1 > div {
+ margin-left: 28px;
+}
+.w2ui-field.w2ui-span2 > label {
+ width: 40px;
+}
+.w2ui-field.w2ui-span2 > div {
+ margin-left: 48px;
+}
+.w2ui-field.w2ui-span3 > label {
+ width: 60px;
+}
+.w2ui-field.w2ui-span3 > div {
+ margin-left: 68px;
+}
+.w2ui-field.w2ui-span4 > label {
+ width: 80px;
+}
+.w2ui-field.w2ui-span4 > div {
+ margin-left: 88px;
+}
+.w2ui-field.w2ui-span5 > label {
+ width: 100px;
+}
+.w2ui-field.w2ui-span5 > div {
+ margin-left: 108px;
+}
+.w2ui-field.w2ui-span6 > label {
+ width: 120px;
+}
+.w2ui-field.w2ui-span6 > div {
+ margin-left: 128px;
+}
+.w2ui-field.w2ui-span7 > label {
+ width: 140px;
+}
+.w2ui-field.w2ui-span7 > div {
+ margin-left: 148px;
+}
+.w2ui-field.w2ui-span8 > label {
+ width: 160px;
+}
+.w2ui-field.w2ui-span8 > div {
+ margin-left: 168px;
+}
+.w2ui-field.w2ui-span9 > label {
+ width: 180px;
+}
+.w2ui-field.w2ui-span9 > div {
+ margin-left: 188px;
+}
+.w2ui-field.w2ui-span10 > label {
+ width: 200px;
+}
+.w2ui-field.w2ui-span10 > div {
+ margin-left: 208px;
+}
+.w2ui-error {
+ border: 1px solid #ffa8a8 !important;
+ background-color: #fff4eb !important;
+}
+.w2field {
+ padding: 3px;
+ border-radius: 3px;
+ border: 1px solid silver;
+}
+.w2ui-field-helper {
+ position: absolute;
+ display: inline-block;
+ line-height: 100%;
+ /* pointer-events: none; - do not use as IE does not support it */
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+}
+.w2ui-field-helper .w2ui-field-up {
+ position: absolute;
+ top: 0px;
+ padding: 2px 3px;
+}
+.w2ui-field-helper .w2ui-field-down {
+ position: absolute;
+ bottom: 0px;
+ padding: 2px 3px;
+}
+.w2ui-field-helper .arrow-up:hover {
+ border-bottom-color: #81C6FF;
+}
+.w2ui-field-helper .arrow-down:hover {
+ border-top-color: #81C6FF;
+}
+/*
+* ARROWS
+*/
+.arrow-up {
+ background: none;
+ width: 0;
+ height: 0;
+ border-left: 4px solid transparent;
+ /* left arrow slant */
+ border-right: 4px solid transparent;
+ /* right arrow slant */
+ border-bottom: 5px solid #777;
+ /* bottom, add background color here */
+ font-size: 0;
+ line-height: 0;
+}
+.arrow-down {
+ background: none;
+ width: 0;
+ height: 0;
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ border-top: 5px solid #777;
+ font-size: 0;
+ line-height: 0;
+}
+.arrow-left {
+ background: none;
+ width: 0;
+ height: 0;
+ border-bottom: 4px solid transparent;
+ /* left arrow slant */
+ border-top: 4px solid transparent;
+ /* right arrow slant */
+ border-right: 5px solid #777;
+ /* bottom, add background color here */
+ font-size: 0;
+ line-height: 0;
+}
+.arrow-right {
+ background: none;
+ width: 0;
+ height: 0;
+ border-bottom: 4px solid transparent;
+ /* left arrow slant */
+ border-top: 4px solid transparent;
+ /* right arrow slant */
+ border-left: 5px solid #777;
+ /* bottom, add background color here */
+ font-size: 0;
+ line-height: 0;
+}
+/*
+* COLOR overlay
+*/
+.w2ui-color {
+ padding: 5px;
+ padding-top: 8px;
+ background-color: white;
+ border-radius: 3px;
+}
+.w2ui-color > table {
+ table-layout: fixed;
+ width: 160px;
+}
+.w2ui-color > table td {
+ width: 20px;
+ height: 20px;
+ text-align: center;
+}
+.w2ui-color > table td div {
+ cursor: pointer;
+ display: inline-block;
+ width: 16px;
+ height: 17px;
+ padding: 1px 4px;
+ border: 1px solid transparent;
+ color: white;
+ text-shadow: 0px 0px 2px #000;
+}
+.w2ui-color > table td div:hover {
+ outline: 1px solid #666;
+ border: 1px solid #fff;
+}
+/*
+* DATE overlay
+*/
+.w2ui-calendar {
+ margin: 0px;
+ padding: 1px;
+ line-height: 108%;
+}
+.w2ui-calendar .w2ui-calendar-title {
+ margin: 0px -1px;
+ padding: 7px 2px;
+ background-image: -webkit-linear-gradient(#f6f6f6, #d9d9d9);
+ background-image: -moz-linear-gradient(#f6f6f6, #d9d9d9);
+ background-image: -ms-linear-gradient(#f6f6f6, #d9d9d9);
+ background-image: -o-linear-gradient(#f6f6f6, #d9d9d9);
+ background-image: linear-gradient(#f6f6f6, #d9d9d9);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fff6f6f6', endColorstr='#ffd9d9d9', GradientType=0);
+ border-bottom: 1px solid #bbbbbb;
+ color: #555555;
+ text-align: center;
+ text-shadow: 1px 1px 1px #eeeeee;
+ cursor: pointer;
+}
+.w2ui-calendar .w2ui-calendar-jump {
+ position: absolute;
+ top: 27px;
+ left: 0px;
+ right: 0px;
+ bottom: 0px;
+ background-color: #FaFaFa;
+}
+.w2ui-calendar .w2ui-calendar-jump > :first-child {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ bottom: 0px;
+ width: 110px;
+ overflow: hidden;
+ padding-top: 5px;
+ border-right: 1px solid #c0c0c0;
+}
+.w2ui-calendar .w2ui-calendar-jump > :last-child {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ bottom: 0px;
+ width: 88px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ padding-top: 5px;
+ text-align: center;
+}
+.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-month,
+.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-year {
+ display: inline-block;
+ padding: 5px 0px;
+ text-align: center;
+ float: left;
+ margin: 2px;
+ width: 50px;
+ cursor: default;
+ border: 1px solid transparent;
+ border-radius: 2px;
+}
+.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-year {
+ float: none;
+ width: 95%;
+}
+.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-month:hover,
+.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-year:hover {
+ border: 1px solid #cccccc;
+ color: #000000;
+ background-color: #efefef;
+}
+.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-month.selected,
+.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-year.selected {
+ border: 1px solid #cccccc;
+ color: #000000;
+ background-color: #dadada;
+}
+.w2ui-calendar .w2ui-calendar-previous,
+.w2ui-calendar .w2ui-calendar-next {
+ width: 24px;
+ height: 20px;
+ color: #666666;
+ border: 1px solid transparent;
+ border-radius: 3px;
+ padding: 2px 3px 1px 2px;
+ margin: -4px 0px 0px 0px;
+ cursor: default;
+}
+.w2ui-calendar .w2ui-calendar-previous:hover,
+.w2ui-calendar .w2ui-calendar-next:hover {
+ border: 1px solid #c0c0c0;
+ background-color: #efefef;
+}
+.w2ui-calendar .w2ui-calendar-previous > div,
+.w2ui-calendar .w2ui-calendar-next > div {
+ position: absolute;
+ border-left: 4px solid #888;
+ border-top: 4px solid #888;
+ border-right: 4px solid transparent;
+ border-bottom: 4px solid transparent;
+ width: 0px;
+ height: 0px;
+ padding: 0px;
+ margin: 3px 0px 0px 0px;
+}
+.w2ui-calendar .w2ui-calendar-previous {
+ float: left;
+}
+.w2ui-calendar .w2ui-calendar-previous > div {
+ -webkit-transform: rotate(-45deg);
+ -moz-transform: rotate(-45deg);
+ -ms-transform: rotate(-45deg);
+ -o-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+ margin-left: 6px;
+}
+.w2ui-calendar .w2ui-calendar-next {
+ float: right;
+}
+.w2ui-calendar .w2ui-calendar-next > div {
+ -webkit-transform: rotate(135deg);
+ -moz-transform: rotate(135deg);
+ -ms-transform: rotate(135deg);
+ -o-transform: rotate(135deg);
+ transform: rotate(135deg);
+ margin-left: 2px;
+ margin-right: 2px;
+}
+.w2ui-calendar table.w2ui-calendar-days {
+ padding: 0px;
+}
+.w2ui-calendar table.w2ui-calendar-days td {
+ border: 1px solid #ffffff;
+ color: #000000;
+ background-color: #f9f9f9;
+ padding: 6px;
+ cursor: default;
+ text-align: right;
+}
+.w2ui-calendar table.w2ui-calendar-days td.w2ui-saturday,
+.w2ui-calendar table.w2ui-calendar-days td.w2ui-sunday {
+ border: 1px solid #ffffff;
+ color: #c8493b;
+ background-color: #f9f9f9;
+}
+.w2ui-calendar table.w2ui-calendar-days td.w2ui-saturday:hover,
+.w2ui-calendar table.w2ui-calendar-days td.w2ui-sunday:hover {
+ border: 1px solid #cccccc;
+ color: #000000;
+ background-color: #e9e9e9;
+}
+.w2ui-calendar table.w2ui-calendar-days td.w2ui-saturday.w2ui-blocked,
+.w2ui-calendar table.w2ui-calendar-days td.w2ui-sunday.w2ui-blocked {
+ text-decoration: line-through;
+ border: 1px solid #ffffff;
+ color: #cccccc;
+ background-color: #ffffff;
+}
+.w2ui-calendar table.w2ui-calendar-days td.w2ui-today {
+ border: 1px solid #8cb067;
+ color: #000000;
+ background-color: #e2f7cd;
+}
+.w2ui-calendar table.w2ui-calendar-days td:hover {
+ border: 1px solid #cccccc;
+ color: #000000;
+ background-color: #e9e9e9;
+}
+.w2ui-calendar table.w2ui-calendar-days td.w2ui-blocked {
+ text-decoration: line-through;
+ border: 1px solid #ffffff;
+ color: #cccccc;
+ background-color: #ffffff;
+}
+.w2ui-calendar table.w2ui-calendar-days td.w2ui-day-empty {
+ border: 1px solid #ffffff;
+ background-color: #fdfdfd;
+}
+.w2ui-calendar table.w2ui-calendar-days tr.w2ui-day-title td {
+ border: 1px solid #ffffff;
+ color: #808080;
+ background-color: #ffffff;
+ text-align: center;
+ padding: 6px;
+}
+/*
+* Time
+*/
+.w2ui-calendar-time {
+ padding: 5px;
+ cursor: default;
+}
+.w2ui-calendar-time td div {
+ padding: 7px 10px;
+ text-align: center;
+ border: 1px solid transparent;
+ white-space: nowrap;
+}
+.w2ui-calendar-time td:nth-child(even) {
+ background-color: #f6f6f6;
+}
+.w2ui-calendar-time td div:hover {
+ border: 1px solid #cccccc;
+ color: #000000;
+ background-color: #e9e9e9;
+}
+.w2ui-calendar-time td div.w2ui-blocked {
+ text-decoration: line-through;
+ border: 1px solid #ffffff;
+ color: #cccccc;
+ background-color: #ffffff;
+}
+.w2ui-select {
+ cursor: default;
+ color: black !important;
+ background-image: -webkit-linear-gradient(top,#ffffff 20%,#f6f6f6 50%,#eeeeee 52%,#f4f4f4 100%);
+ background-image: -moz-linear-gradient(top,#ffffff 20%,#f6f6f6 50%,#eeeeee 52%,#f4f4f4 100%);
+ background-image: -ms-linear-gradient(top,#ffffff 20%,#f6f6f6 50%,#eeeeee 52%,#f4f4f4 100%);
+ background-image: -o-linear-gradient(top,#ffffff 20%,#f6f6f6 50%,#eeeeee 52%,#f4f4f4 100%);
+ background-image: linear-gradient(top,#ffffff 20%,#f6f6f6 50%,#eeeeee 52%,#f4f4f4 100%);
+}
+/*
+* ENUM items
+*/
+.w2ui-list {
+ color: inherit;
+ position: absolute;
+ padding: 0px;
+ margin: 0px;
+ min-height: 25px;
+ overflow: auto;
+ border: 1px solid #c0c0c0;
+ border-radius: 3px;
+ font-size: 6px;
+ line-height: 100%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+ background-color: #ffffff;
+}
+.w2ui-list input[type=text] {
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ -ms-box-shadow: none;
+ -o-box-shadow: none;
+ box-shadow: none;
+}
+.w2ui-list ul {
+ list-style-type: none;
+ background-color: black;
+ margin: 0px;
+ padding: 0px;
+}
+.w2ui-list ul li {
+ float: left;
+ margin: 2px 1px 0px 2px;
+ border-radius: 3px;
+ width: auto;
+ padding: 3px 10px 1px 7px;
+ border: 1px solid #88b0d6;
+ background-color: #eff3f5;
+ white-space: nowrap;
+ cursor: default;
+ font-family: verdana;
+ font-size: 11px;
+ line-height: 100%;
+ height: 20px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.w2ui-list ul li:hover {
+ background-color: #d0dbe1;
+}
+.w2ui-list ul li:last-child {
+ border-radius: 0px;
+ border: 1px solid transparent;
+ background-color: transparent;
+}
+.w2ui-list ul li:last-child input {
+ padding: 1px;
+ padding-top: 0px;
+ margin: 0px;
+ border: 0px;
+ outline: none;
+ height: auto;
+ line-height: 100%;
+ font-size: inherit;
+ font-family: inherit;
+ background-color: transparent;
+}
+.w2ui-list ul li .w2ui-list-remove {
+ float: right;
+ width: 15px;
+ height: 14px;
+ margin: -1px -9px 0px 3px;
+ border-radius: 15px;
+}
+.w2ui-list ul li .w2ui-list-remove:hover {
+ background-color: #D77F7F;
+ color: white;
+}
+.w2ui-list ul li .w2ui-list-remove:before {
+ position: relative;
+ top: 0px;
+ padding: 0px;
+ margin: 0px;
+ left: 5px;
+ color: inherit;
+ opacity: 0.7;
+ text-shadow: inherit;
+ font-size: inherit;
+ font-variant: small-caps;
+ content: 'x';
+ line-height: 100%;
+}
+.w2ui-list ul li > span.file-size {
+ pointer-events: none;
+ color: #777;
+}
+.w2ui-list .w2ui-enum-placeholder {
+ display: inline;
+ position: absolute;
+ pointer-events: none;
+ color: #999;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.w2ui-list.w2ui-file-dragover {
+ background-color: #E4FFDA;
+ border: 1px solid #93E07D;
+}
+/*************************************************
+* ---- Layout ----
+*/
+.w2ui-layout {
+ overflow: hidden !important;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.w2ui-layout * {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.w2ui-layout > div {
+ position: absolute;
+ overflow: hidden;
+ border: 0px;
+ margin: 0px;
+ padding: 0px;
+ outline: 0px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.w2ui-layout > div .w2ui-panel {
+ display: none;
+ position: absolute;
+ z-index: 120;
+}
+.w2ui-layout > div .w2ui-panel .w2ui-panel-title {
+ padding: 5px;
+ background-image: -webkit-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: -moz-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: -ms-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: -o-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: linear-gradient(#dae6f3, #c2d5ed);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffdae6f3', endColorstr='#ffc2d5ed', GradientType=0);
+ border: 1px solid #b9cee9;
+ border-bottom: 1px solid #99bbe8;
+}
+.w2ui-layout > div .w2ui-panel .w2ui-panel-tabs {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ right: 0px;
+ z-index: 2;
+ display: none;
+ overflow: hidden;
+ background-color: #fafafa;
+ padding: 4px 0px;
+}
+.w2ui-layout > div .w2ui-panel .w2ui-panel-tabs > .w2ui-tab.active {
+ background-color: #f5f6f7;
+}
+.w2ui-layout > div .w2ui-panel .w2ui-panel-toolbar {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ right: 0px;
+ z-index: 2;
+ display: none;
+ overflow: hidden;
+ background-color: #fafafa;
+ border-bottom: 1px solid silver;
+ padding: 4px;
+}
+.w2ui-layout > div .w2ui-panel .w2ui-panel-content {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ right: 0px;
+ bottom: 0px;
+ z-index: 1;
+ color: inherit;
+ background-color: #f5f6f7;
+}
+.w2ui-layout > div .w2ui-resizer {
+ display: none;
+ position: absolute;
+ z-index: 121;
+ background-color: transparent;
+}
+.w2ui-layout > div .w2ui-resizer:hover,
+.w2ui-layout > div .w2ui-resizer.active {
+ background-color: #d7e4f2;
+}
+/*************************************************
+* ---- Grid ----
+*/
+.w2ui-grid {
+ position: relative;
+ border: 1px solid #c0c0c0;
+ border-radius: 2px;
+ overflow: hidden !important;
+}
+.w2ui-grid > div {
+ position: absolute;
+ overflow: hidden;
+}
+.w2ui-grid .w2ui-grid-header {
+ position: absolute;
+ border-bottom: 1px solid #99bbe8 !important;
+ height: 28px;
+ overflow: hidden;
+ color: #444444;
+ font-size: 13px;
+ text-align: center;
+ padding: 7px;
+ background-image: -webkit-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: -moz-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: -ms-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: -o-linear-gradient(#dae6f3, #c2d5ed);
+ background-image: linear-gradient(#dae6f3, #c2d5ed);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffdae6f3', endColorstr='#ffc2d5ed', GradientType=0);
+ border-top-left-radius: 2px;
+ border-top-right-radius: 2px;
+}
+.w2ui-grid .w2ui-grid-toolbar {
+ position: absolute;
+ border-bottom: 1px solid #c0c0c0;
+ background-color: #eaeaea;
+ height: 38px;
+ padding: 7px 3px 4px 3px;
+ margin: 0px;
+ box-shadow: 0px 1px 2px #dddddd;
+}
+.w2ui-grid .w2ui-toolbar-search {
+ width: 160px;
+ margin-right: 3px;
+}
+.w2ui-grid .w2ui-toolbar-search .w2ui-search-all {
+ outline: none !important;
+ width: 160px;
+ border-radius: 10px;
+ line-height: normal;
+ height: 22px;
+ border: 1px solid #b9b9b9;
+ color: #000000;
+ background-color: #ffffff;
+ padding: 3px 18px 3px 23px;
+ margin: 0px;
+}
+.w2ui-grid .w2ui-toolbar-search .w2ui-search-down {
+ position: absolute;
+ margin-top: -7px;
+ margin-left: 6px;
+}
+.w2ui-grid .w2ui-toolbar-search .w2ui-search-clear {
+ position: absolute;
+ width: 16px;
+ height: 16px;
+ margin-top: -8px;
+ margin-left: -20px;
+ border-radius: 15px;
+ cursor: default;
+}
+.w2ui-grid .w2ui-toolbar-search .w2ui-search-clear:hover {
+ background-color: #D77F7F;
+ color: white;
+}
+.w2ui-grid .w2ui-toolbar-search .w2ui-search-clear:before {
+ position: relative;
+ top: 1px;
+ left: 5px;
+ opacity: 0.6;
+ color: inherit;
+ text-shadow: inherit;
+ content: 'x';
+ cursor: default;
+}
+.w2ui-grid .w2ui-grid-body {
+ position: absolute;
+ overflow: hidden;
+ padding: 0px;
+ background-color: #ffffff;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+.w2ui-grid .w2ui-grid-body input,
+.w2ui-grid .w2ui-grid-body select,
+.w2ui-grid .w2ui-grid-body textarea {
+ user-select: text;
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ -ms-user-select: text;
+ -o-user-select: text;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-columns {
+ overflow: hidden;
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ right: 0px;
+ box-shadow: 0px 1px 4px #dddddd;
+ height: auto;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-columns table {
+ height: auto;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-columns .w2ui-resizer {
+ position: absolute;
+ z-index: 1000;
+ display: block;
+ background-image: none;
+ background-color: rgba(0, 0, 0, 0);
+ /* needed for IE */
+ padding: 0px;
+ margin: 0px;
+ width: 6px;
+ height: 12px;
+ cursor: col-resize;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records {
+ position: absolute;
+ left: 0px;
+ right: 0px;
+ top: 0px;
+ bottom: 0px;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-odd {
+ color: inherit;
+ background-color: #ffffff;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-odd:hover {
+ color: inherit;
+ background-color: #e6f0ff;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-odd.w2ui-empty-record:hover {
+ background-color: #ffffff;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-even {
+ color: inherit;
+ background-color: #f3f6fa;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-even:hover {
+ color: inherit;
+ background-color: #e6f0ff;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-even.w2ui-empty-record:hover {
+ background-color: #f3f6fa;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-selected,
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr td.w2ui-selected {
+ color: #000000 !important;
+ background-color: #b6d5ff !important;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-expanded {
+ background-color: #CCDCF0 !important;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-expanded1 {
+ height: 0px;
+ border-bottom: 1px solid #b2bac0;
+ background-color: #CCDCF0;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-expanded1 > div {
+ height: 100%;
+ margin: 0px;
+ padding: 0px;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-expanded2 {
+ height: 0px;
+ border-radius: 0px;
+ border-bottom: 1px solid #b2bac0;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-expanded2 > div {
+ height: 0px;
+ border: 0px;
+ transition: height .3s, opacity .3s;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-load-more {
+ border-top: 1px solid #d6d5d7;
+ cursor: pointer;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-load-more > div {
+ text-align: center;
+ color: #777777;
+ background-color: rgba(233, 237, 243, 0.5);
+ padding: 10px 0px 15px 0px;
+ border-top: 1px solid #ffffff;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-load-more > div:hover {
+ color: inherit;
+ background-color: #e6f0ff;
+}
+.w2ui-grid .w2ui-grid-body table {
+ border-spacing: 0px;
+ border-collapse: collapse;
+ table-layout: fixed;
+ width: 1px;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-head {
+ margin: 0px;
+ padding: 0px;
+ border-right: 1px solid #c5c5c5;
+ border-bottom: 1px solid #c5c5c5;
+ color: #000000;
+ background-image: -webkit-linear-gradient(#f9f9f9, #e4e4e4);
+ background-image: -moz-linear-gradient(#f9f9f9, #e4e4e4);
+ background-image: -ms-linear-gradient(#f9f9f9, #e4e4e4);
+ background-image: -o-linear-gradient(#f9f9f9, #e4e4e4);
+ background-image: linear-gradient(#f9f9f9, #e4e4e4);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#ffe4e4e4', GradientType=0);
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-head > div {
+ padding: 7px 3px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ position: relative;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-head.w2ui-col-intersection {
+ border-right-color: #72b2ff;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-head.w2ui-reorder-cols-head:hover {
+ cursor: move;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-head .col-intersection-marker {
+ padding: 0;
+ position: absolute;
+ height: 100%;
+ top: 0;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-head .col-intersection-marker.left {
+ left: 0;
+ margin-left: -5px;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-head .col-intersection-marker.right {
+ right: 0;
+ margin-right: -5px;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-head .col-intersection-marker .top-marker {
+ position: absolute;
+ top: 0;
+ height: 0;
+ width: 0;
+ border-top: 5px solid #72b2ff;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-head .col-intersection-marker .bottom-marker {
+ position: absolute;
+ bottom: 0;
+ height: 0;
+ width: 0;
+ border-bottom: 5px solid #72b2ff;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+}
+.w2ui-grid .w2ui-grid-body table td {
+ border-right: 1px solid #d6d5d7;
+ border-bottom: 0px solid #d6d5d7;
+ cursor: default;
+ overflow: hidden;
+}
+.w2ui-grid .w2ui-grid-body table td.w2ui-grid-data {
+ margin: 0px;
+ padding: 0px;
+}
+.w2ui-grid .w2ui-grid-body table td.w2ui-grid-data > div {
+ padding: 3px 3px 3px 3px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+.w2ui-grid .w2ui-grid-body table td.w2ui-grid-data > div.flexible-record {
+ height: auto;
+ overflow: visible;
+ white-space: normal;
+}
+.w2ui-grid .w2ui-grid-body table td:last-child {
+ border-right: 0px;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-col-number {
+ width: 34px;
+ color: #777777;
+ background-color: rgba(233, 237, 243, 0.5);
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-col-number div {
+ padding: 0px 7px 0px 3px;
+ text-align: right;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-col-select {
+ width: 26px;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-col-select div {
+ padding: 0px 0px;
+ text-align: center;
+ overflow: hidden;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-col-select div input[type=checkbox] {
+ margin-top: 2px;
+ position: relative;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-col-expand {
+ width: 26px;
+}
+.w2ui-grid .w2ui-grid-body table .w2ui-col-expand div {
+ padding: 0px 0px;
+ text-align: center;
+ font-weight: bold;
+}
+.w2ui-grid .w2ui-grid-body div.w2ui-col-header {
+ height: auto !important;
+ width: 100%;
+ overflow: hidden;
+ padding-right: 10px !important;
+}
+.w2ui-grid .w2ui-grid-body div.w2ui-col-header > div.w2ui-sort-up {
+ border: 4px solid transparent;
+ border-bottom: 5px solid #8D99A7;
+ margin-top: -2px;
+ margin-right: -7px;
+ float: right;
+}
+.w2ui-grid .w2ui-grid-body div.w2ui-col-header > div.w2ui-sort-down {
+ border: 4px solid transparent;
+ border-top: 5px solid #8D99A7;
+ margin-top: 2px;
+ margin-right: -7px;
+ float: right;
+}
+.w2ui-grid .w2ui-grid-body .w2ui-col-group {
+ text-align: center;
+}
+.w2ui-grid .w2ui-changed {
+ background: url(data:image/gif;base64,R0lGODlhCgAKAJEAALAABf///wAAAAAAACH5BAEAAAIALAAAAAAKAAoAAAIPlI8Hy8mbxIsSUnup3rQAADs=) no-repeat top right;
+}
+.w2ui-grid .w2ui-editable {
+ overflow: hidden;
+ height: 100% !important;
+ margin: 0px !important;
+ padding: 0px !important;
+}
+.w2ui-grid .w2ui-editable input {
+ border: 0px;
+ border-radius: 0px;
+ margin: 0px;
+ padding: 4px 3px;
+ width: 100%;
+ height: 100%;
+}
+.w2ui-grid .w2ui-editable input.w2ui-select {
+ outline: none !important;
+ background: #fff;
+}
+.w2ui-grid .w2ui-grid-summary {
+ position: absolute;
+ box-shadow: 0px -1px 4px #aaaaaa;
+}
+.w2ui-grid .w2ui-grid-summary table {
+ color: inherit;
+}
+.w2ui-grid .w2ui-grid-summary table .w2ui-odd {
+ background-color: #eef5eb;
+}
+.w2ui-grid .w2ui-grid-summary table .w2ui-even {
+ background-color: #f8fff5;
+}
+.w2ui-grid .w2ui-grid-footer {
+ position: absolute;
+ margin: 0px;
+ padding: 0px;
+ text-align: center;
+ height: 24px;
+ overflow: hidden;
+ user-select: text;
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ -ms-user-select: text;
+ -o-user-select: text;
+ box-shadow: 0px -1px 4px #eeeeee;
+ color: #444444;
+ background-color: #f8f8f8;
+ border-top: 1px solid #dddddd;
+ border-bottom-left-radius: 2px;
+ border-bottom-right-radius: 2px;
+}
+.w2ui-grid .w2ui-grid-footer .w2ui-footer-left {
+ float: left;
+ padding-top: 5px;
+ padding-left: 5px;
+}
+.w2ui-grid .w2ui-grid-footer .w2ui-footer-right {
+ float: right;
+ padding-top: 5px;
+ padding-right: 5px;
+}
+.w2ui-grid .w2ui-grid-footer .w2ui-footer-center {
+ padding: 2px;
+ text-align: center;
+}
+.w2ui-grid .w2ui-grid-footer .w2ui-footer-center .w2ui-footer-nav {
+ width: 110px;
+ margin: 0 auto;
+ padding: 0px;
+ text-align: center;
+}
+.w2ui-grid .w2ui-grid-footer .w2ui-footer-center .w2ui-footer-nav input[type=text] {
+ padding: 1px 2px 2px 2px;
+ border-radius: 3px;
+ width: 40px;
+ text-align: center;
+}
+.w2ui-grid .w2ui-grid-footer .w2ui-footer-center .w2ui-footer-nav a.w2ui-footer-btn {
+ display: inline-block;
+ border-radius: 3px;
+ cursor: pointer;
+ font-size: 11px;
+ line-height: 16px;
+ padding: 1px 5px;
+ width: 30px;
+ height: 18px;
+ margin-top: -1px;
+ color: #000000;
+ background-color: transparent;
+}
+.w2ui-grid .w2ui-grid-footer .w2ui-footer-center .w2ui-footer-nav a.w2ui-footer-btn:hover {
+ color: #000000;
+ background-color: #aec8ff;
+}
+/* SpeadSheet */
+.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr.w2ui-odd,
+.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr.w2ui-even,
+.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr.w2ui-odd:hover,
+.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr.w2ui-even:hover {
+ background-color: inherit;
+}
+.w2ui-ss .w2ui-grid-records table td {
+ border-right-width: 1px;
+ border-bottom: 1px solid #efefef;
+}
+.w2ui-ss .w2ui-grid-records table tr:first-child td {
+ border-bottom: 0px;
+}
+.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr.w2ui-selected,
+.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr td.w2ui-selected {
+ background-color: #EEF4FE !important;
+}
+.w2ui-ss .w2ui-changed {
+ background: inherit;
+}
+.w2ui-ss .w2ui-grid-body .w2ui-selection {
+ position: absolute;
+ border: 2px solid #6299DA;
+ /* #457FC2; */
+ pointer-events: none;
+}
+.w2ui-ss .w2ui-grid-body .w2ui-selection .w2ui-selection-resizer {
+ cursor: crosshair;
+ position: absolute;
+ bottom: 0px;
+ right: 0px;
+ width: 6px;
+ height: 6px;
+ margin-right: -3px;
+ margin-bottom: -3px;
+ background-color: #457FC2;
+ border: 0.5px solid #fff;
+ outline: 1px solid white;
+ pointer-events: auto;
+}
+.w2ui-overlay .w2ui-select-field {
+ padding: 8px 5px;
+ cursor: default;
+}
+.w2ui-overlay .w2ui-select-field table {
+ font-size: 11px;
+ font-family: Verdana, Arial, sans-serif;
+ border-spacing: 0px;
+ border-collapse: border-collapse;
+}
+.w2ui-overlay .w2ui-select-field table tr:hover {
+ background-color: #b6d5ff;
+}
+.w2ui-overlay .w2ui-select-field table td:nth-child(1) {
+ padding: 3px 3px 3px 6px;
+}
+.w2ui-overlay .w2ui-select-field table td:nth-child(1) input {
+ margin: 3px 2px 2px 2px;
+}
+.w2ui-overlay .w2ui-select-field table td:nth-child(2) {
+ padding: 3px 15px 3px 3px;
+}
+.w2ui-overlay .w2ui-col-on-off {
+ padding: 4px 0px;
+}
+.w2ui-overlay .w2ui-col-on-off table {
+ border-spacing: 0px;
+ border-collapse: border-collapse;
+}
+.w2ui-overlay .w2ui-col-on-off table tr:hover {
+ background-color: #b6d5ff;
+}
+.w2ui-overlay .w2ui-col-on-off table td input[type=checkbox] {
+ margin: 3px 2px 2px 2px;
+}
+.w2ui-overlay .w2ui-col-on-off table td label {
+ display: block;
+ padding: 3px 0px;
+ padding-right: 10px;
+}
+.w2ui-overlay .w2ui-col-on-off table td:first-child {
+ padding: 4px 0px 4px 6px;
+}
+.w2ui-overlay .w2ui-col-on-off table td:last-child {
+ padding: 4px 6px 4px 0px;
+}
+.w2ui-overlay .w2ui-grid-searches {
+ text-align: left;
+ padding: 0px;
+ border-top: 0px;
+ background-color: #f7f6f0;
+}
+.w2ui-overlay .w2ui-grid-searches table {
+ padding: 4px;
+ padding-top: 12px;
+ border-collapse: border-collapse;
+}
+.w2ui-overlay .w2ui-grid-searches table td {
+ padding: 4px;
+ /* for IE */
+}
+.w2ui-overlay .w2ui-grid-searches table td.close-btn {
+ width: 20px;
+ padding-right: 20px;
+}
+.w2ui-overlay .w2ui-grid-searches table td.close-btn button {
+ min-width: 24px;
+ height: 24px;
+ padding-top: 6px !important;
+}
+.w2ui-overlay .w2ui-grid-searches table td.caption {
+ text-align: right;
+ padding-right: 5px;
+ border-right: 1px solid #e8e8e3;
+}
+.w2ui-overlay .w2ui-grid-searches table td.operator {
+ text-align: left;
+ padding: 0px 10px;
+ padding-right: 5px;
+ border-right: 1px solid #e8e8e3;
+}
+.w2ui-overlay .w2ui-grid-searches table td.operator select {
+ width: 100%;
+ color: black;
+ padding: 0px 15px 0px 5px;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ -ms-appearance: none;
+ -o-appearance: none;
+ background-image: -webkit-linear-gradient(top,#ffffff 20%,#f6f6f6 50%,#eeeeee 52%,#f4f4f4 100%);
+ background-image: -moz-linear-gradient(top,#ffffff 20%,#f6f6f6 50%,#eeeeee 52%,#f4f4f4 100%);
+ background-image: -ms-linear-gradient(top,#ffffff 20%,#f6f6f6 50%,#eeeeee 52%,#f4f4f4 100%);
+ background-image: -o-linear-gradient(top,#ffffff 20%,#f6f6f6 50%,#eeeeee 52%,#f4f4f4 100%);
+ background-image: linear-gradient(top,#ffffff 20%,#f6f6f6 50%,#eeeeee 52%,#f4f4f4 100%);
+}
+.w2ui-overlay .w2ui-grid-searches table td.operator select::-ms-expand {
+ display: none;
+}
+.w2ui-overlay .w2ui-grid-searches table td.value {
+ padding-right: 5px;
+ padding-left: 5px;
+}
+.w2ui-overlay .w2ui-grid-searches table td.value input[type=text] {
+ border-radius: 3px;
+ padding: 3px;
+ margin-right: 3px;
+ height: 23px;
+}
+.w2ui-overlay .w2ui-grid-searches table td.value select {
+ padding: 3px;
+ margin-right: 3px;
+ height: 23px;
+}
+.w2ui-overlay .w2ui-grid-searches table td.actions {
+ border-right: 0px;
+}
+.w2ui-overlay .w2ui-grid-searches table td.actions > div {
+ margin: -7px;
+ margin-top: 15px;
+ padding: 13px 0px;
+ text-align: center;
+ background-color: #efefe9;
+ border-top: 1px solid #e8e8e3;
+}
+/*************************************************
+* ---- Popup ----
+*/
+.w2ui-popup {
+ position: fixed;
+ z-index: 1600;
+ overflow: hidden;
+ font-family: Verdana, Arial, sans-serif;
+ border-radius: 6px;
+ padding: 0px;
+ margin: 0px;
+ border: 1px solid #777777;
+ background-color: #eeeeee;
+ box-shadow: 0px 0px 25px #555555;
+}
+.w2ui-popup,
+.w2ui-popup * {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.w2ui-popup .w2ui-msg-title {
+ padding: 6px;
+ border-radius: 6px 6px 0px 0px;
+ background-image: -webkit-linear-gradient(#ececec, #dfdfdf);
+ background-image: -moz-linear-gradient(#ececec, #dfdfdf);
+ background-image: -ms-linear-gradient(#ececec, #dfdfdf);
+ background-image: -o-linear-gradient(#ececec, #dfdfdf);
+ background-image: linear-gradient(#ececec, #dfdfdf);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffececec', endColorstr='#ffdfdfdf', GradientType=0);
+ border-bottom: 2px solid #bfbfbf;
+ position: absolute;
+ overflow: hidden;
+ height: 32px;
+ left: 0px;
+ right: 0px;
+ top: 0px;
+ text-overflow: ellipsis;
+ text-align: center;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+ cursor: move;
+ font-size: 15px;
+ color: #555555;
+ z-index: 300;
+}
+.w2ui-popup .w2ui-msg-button {
+ float: right;
+ width: 18px;
+ height: 18px;
+ cursor: pointer;
+ overflow: hidden;
+ padding: 0px;
+ margin: 0px 3px 0px 0px;
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAQCAYAAABQrvyxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAj1JREFUeNrslr9rFFEQxz/zZi/qxSgW2lsqkiYoBku5Ro1o4UFKEYkgSaxSCLYqdv5IEVPYCMJJwERWrK0CKhoQ8hdobQTjXW7njcXlYnLunQQu0YDTLOy+Nzvfme98Z8Td2ckW2OGWdMvRvYfT/RGfBPoBBVpLK0AEPgVkdGL06vt/CoB5nBaRE8AXYKXNsQIwaB4fAwOtH+88mn4m7ifN4vUYebWBKkFKqjIV3N9NjI2Uuw5ARI45fBanH+F77iFnN8JHETmS68P9NHBQNTwHL8foaSN4SqoyA/SZyL4tqQAQBVYCLOFYlNxmq0WorVLpN9Oe5LKt1CsgRVWpAOfB66phBuhTkepSdfnKVjaxNJMSWn/iawmTtpeDp6pWBpaBoqrMqoYU6AOqIbFhxGa3R4V8nfNNKLUESzXJhoCvQC+wF/gW1C5IiC+2XUbD5jA3rd4C26NR3945IA2iRzqRJgdElJJlSQocAKrAD2A/6Ev3cLajjN59MDWHyKl2voOI1zKbv3Xj2lCHJFoz+LXuBoIAjnUklEvJrDDT5LwmdhG8blkyBxRjXSu4loE0X4VEznXKV3SnoOFMB7YUolBcbcKNdxuPXUBPu8pbLXsK0ghebVjEXgNoYmXLtGLuxd6ePU+AQ20AaIrb4DpFycmSv81/7YsiMgAstB1kQgE47O4LuQmCNwGOB7VxCb/URsRSTbhkmU4ifGiZHd1Z5m7fnxoIQSaBo39YJRZj9LGb4yPzXWm1/9voX7afAwAC5tacDTA2XgAAAABJRU5ErkJggg==) no-repeat center left;
+ background-position: 0px 0px;
+ color: transparent !important;
+ border-radius: 3px;
+ border: 1px solid transparent;
+}
+.w2ui-popup .w2ui-msg-close {
+ margin-top: 0px;
+ background-position: -32px 0px;
+}
+.w2ui-popup .w2ui-msg-close:hover {
+ background-color: #cccccc;
+ border: 1px solid #aaaaaa;
+}
+.w2ui-popup .w2ui-msg-max {
+ background-position: -16px 0px;
+}
+.w2ui-popup .w2ui-msg-max:hover {
+ background-color: #cccccc;
+ border: 1px solid #aaaaaa;
+}
+.w2ui-popup .w2ui-box1,
+.w2ui-popup .w2ui-box2 {
+ position: absolute;
+ left: 0px;
+ right: 0px;
+ top: 32px;
+ bottom: 55px;
+ z-index: 100;
+}
+.w2ui-popup .w2ui-msg-body {
+ font-size: 13px;
+ line-height: 130%;
+ padding: 0px 7px 7px 7px;
+ color: #000000;
+ background-color: #eeeeee;
+ position: absolute;
+ overflow: auto;
+ width: 100%;
+ height: 100%;
+}
+.w2ui-popup .w2ui-popup-message {
+ position: absolute;
+ z-index: 250;
+ background-color: #f9f9f9;
+ border: 1px solid #999999;
+ box-shadow: 0px 0px 15px #aaaaaa;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+ border-top: 0px;
+ border-radius: 0px 0px 6px 6px;
+ overflow: auto;
+}
+.w2ui-popup .w2ui-msg-buttons {
+ padding: 12px;
+ border-radius: 0px 0px 6px 6px;
+ border-top: 1px solid #d5d8d8;
+ background-color: #f1f1f1;
+ text-align: center;
+ position: absolute;
+ overflow: hidden;
+ height: 52px;
+ left: 0px;
+ right: 0px;
+ bottom: 0px;
+ z-index: 200;
+}
+.w2ui-popup .w2ui-msg-no-title {
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
+ top: 0px !important;
+}
+.w2ui-popup .w2ui-msg-no-buttons {
+ border-bottom-left-radius: 6px;
+ border-bottom-right-radius: 6px;
+ bottom: 0px !important;
+}
+/*************************************************
+* ---- Sidebar ----
+*/
+.w2ui-sidebar {
+ cursor: default;
+ overflow: hidden !important;
+ background-color: #edf1f6 !important;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.w2ui-sidebar * {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.w2ui-sidebar > div {
+ position: relative;
+ overflow: hidden;
+}
+.w2ui-sidebar .w2ui-sidebar-top {
+ position: absolute;
+ z-index: 2;
+ top: 0px;
+ left: 0px;
+ right: 0px;
+}
+.w2ui-sidebar .w2ui-sidebar-bottom {
+ position: absolute;
+ z-index: 2;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;
+}
+.w2ui-sidebar .w2ui-sidebar-div {
+ position: absolute;
+ z-index: 1;
+ overflow: auto;
+ top: 0px;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;
+ padding: 2px 0px;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+.w2ui-sidebar .w2ui-sidebar-div table {
+ width: 100%;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node {
+ background-color: #edf1f6;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ margin: 0px;
+ padding: 1px 0px;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node table {
+ pointer-events: none;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-caption,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image > span,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node td.w2ui-node-dots {
+ color: #000000;
+ text-shadow: 0px 0px 0px #ffffff;
+ pointer-events: none;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-caption:hover,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image:hover,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image > span:hover,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node td.w2ui-node-dots:hover {
+ color: inherit;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node:hover {
+ border-top: 1px solid #f9f9f9;
+ border-bottom: 1px solid #f9f9f9;
+ background-color: #d7e1ef;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image {
+ width: 22px;
+ text-align: center;
+ pointer-events: none;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image > span {
+ color: #516173 !important;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected:hover {
+ background-image: -webkit-linear-gradient(#69b1e0, #4a96d3);
+ background-image: -moz-linear-gradient(#69b1e0, #4a96d3);
+ background-image: -ms-linear-gradient(#69b1e0, #4a96d3);
+ background-image: -o-linear-gradient(#69b1e0, #4a96d3);
+ background-image: linear-gradient(#69b1e0, #4a96d3);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff69b1e0', endColorstr='#ff4a96d3', GradientType=0);
+ border-top: 1px solid #5295cd;
+ border-bottom: 1px solid #2661a6;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected .w2ui-node-caption,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected:hover .w2ui-node-caption,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected .w2ui-node-image,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected:hover .w2ui-node-image,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected .w2ui-node-image > span,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected:hover .w2ui-node-image > span,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected td.w2ui-node-dots,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected:hover td.w2ui-node-dots {
+ color: #ffffff !important;
+ text-shadow: 1px 1px 2px #666666 !important;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled:hover {
+ background: transparent !important;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled .w2ui-node-caption,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled:hover .w2ui-node-caption,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled .w2ui-node-image,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled:hover .w2ui-node-image,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled .w2ui-node-image > span,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled:hover .w2ui-node-image > span,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled td.w2ui-node-dots,
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled:hover td.w2ui-node-dots {
+ opacity: 0.4;
+ filter: alpha(opacity=40);
+ color: #000000 !important;
+ text-shadow: 0px 0px 0px #ffffff !important;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node-caption {
+ white-space: nowrap;
+ padding: 5px 0px 5px 3px;
+ margin: 1px 0px 1px 22px;
+ position: relative;
+ z-index: 1;
+ font-size: 12px;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node-group {
+ white-space: nowrap;
+ overflow: hidden;
+ padding: 10px 0px 10px 10px;
+ margin: 0px;
+ cursor: default;
+ color: #868b92;
+ background-color: transparent;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node-group :nth-child(1) {
+ /* show / hide link */
+ margin-right: 10px;
+ float: right;
+ color: transparent;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node-group :nth-child(2) {
+ /* title text */
+ font-weight: normal;
+ text-transform: uppercase;
+}
+.w2ui-sidebar .w2ui-sidebar-div .w2ui-node-sub {
+ overflow: hidden;
+}
+.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-dots {
+ width: 18px;
+ padding: 0px 0px 1px 7px;
+ text-align: center;
+}
+.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-dots .w2ui-expand {
+ width: 16px;
+ margin-top: -3px;
+ pointer-events: auto;
+}
+.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-data {
+ padding: 1px 1px 3px 1px;
+}
+.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-data .w2ui-node-image {
+ padding: 3px 0px 0px 0px;
+ float: left;
+}
+.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-data .w2ui-node-image > span {
+ font-size: 16px;
+ color: #000000;
+ text-shadow: 0px 0px 0px #ffffff;
+}
+.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-data .w2ui-node-image.w2ui-icon {
+ margin-top: 3px;
+}
+.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-data .w2ui-node-count {
+ float: right;
+ border: 1px solid #9da4af;
+ border-radius: 20px;
+ width: auto;
+ height: 18px;
+ padding: 2px 7px;
+ margin: 3px 4px -2px 0;
+ background-color: #e7f0fc;
+ color: #667274;
+ box-shadow: 0 0 2px #ffffff;
+ text-shadow: 1px 1px 1px #e6e6e6;
+ position: relative;
+ z-index: 2;
+}
+/*************************************************
+* ---- Tabs ----
+*/
+.w2ui-tabs {
+ cursor: default;
+ overflow: hidden !important;
+ background-color: #fafafa;
+ padding: 3px 0px;
+ padding-bottom: 0px !important;
+}
+.w2ui-tabs table {
+ border-bottom: 1px solid silver;
+ padding: 0px 7px;
+}
+.w2ui-tabs .w2ui-tab {
+ padding: 6px 20px;
+ text-align: center;
+ color: #000000;
+ background-color: transparent;
+ border: 1px solid #c0c0c0;
+ border-bottom: 1px solid silver;
+ white-space: nowrap;
+ margin: 1px 1px -1px 0px;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ cursor: default;
+}
+.w2ui-tabs .w2ui-tab.active {
+ color: #000000;
+ background-color: #ffffff;
+ border: 1px solid #c0c0c0;
+ border-bottom: 1px solid transparent;
+}
+.w2ui-tabs .w2ui-tab.closable {
+ padding: 6px 28px 6px 20px;
+}
+.w2ui-tabs .w2ui-tab-close {
+ color: #555;
+ text-shadow: 1px 1px 1px #bbb;
+ float: right;
+ margin: 6px 4px 0px 0px;
+ padding: 0px 0px 0px 5px;
+ width: 16px;
+ height: 16px;
+ opacity: 0.9;
+ border: 0px;
+ border-top: 3px solid transparent;
+ border-radius: 9px;
+}
+.w2ui-tabs .w2ui-tab-close:hover {
+ background-color: #D77F7F;
+ color: white;
+}
+.w2ui-tabs .w2ui-tab-close:before {
+ position: relative;
+ top: -2px;
+ left: 0px;
+ opacity: 0.6;
+ color: inherit;
+ text-shadow: inherit;
+ content: 'x';
+}
+/*************************************************
+* ---- Toolbar ----
+*/
+.w2ui-toolbar {
+ margin: 0px;
+ padding: 2px;
+ outline: 0px;
+ background-color: #efefef;
+ overflow: hidden !important;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+.w2ui-toolbar .disabled {
+ opacity: 0.3;
+ filter: alpha(opacity=30);
+}
+.w2ui-toolbar table {
+ table-layout: auto !important;
+}
+.w2ui-toolbar table td {
+ border: 0px !important;
+}
+.w2ui-toolbar table.w2ui-button {
+ margin: 0px 1px;
+ border-radius: 4px;
+ height: 24px;
+ border: 1px solid transparent;
+ background-color: transparent;
+}
+.w2ui-toolbar table.w2ui-button .w2ui-tb-image {
+ width: 16px;
+ height: 16px;
+ padding: 0px;
+ margin: 2px 4px 3px 3px !important;
+ border: 0px !important;
+ text-align: center;
+}
+.w2ui-toolbar table.w2ui-button .w2ui-tb-image > span {
+ font-size: 15px;
+ margin-top: 3px;
+ display: block;
+ color: #8d99a7;
+}
+.w2ui-toolbar table.w2ui-button .w2ui-tb-caption {
+ color: #000000;
+ padding: 0px 4px 0px 2px;
+}
+.w2ui-toolbar table.w2ui-button .w2ui-tb-count {
+ padding: 0px 4px 0px 0px;
+}
+.w2ui-toolbar table.w2ui-button .w2ui-tb-count > span {
+ border: 1px solid #9da4af;
+ border-radius: 20px;
+ width: auto;
+ height: 18px;
+ padding: 2px 7px;
+ background-color: #e7f0fc;
+ color: #667274;
+ box-shadow: 0 0 2px #ffffff;
+ text-shadow: 1px 1px 1px #e6e6e6;
+}
+.w2ui-toolbar table.w2ui-button .w2ui-tb-down {
+ padding: 3px;
+}
+.w2ui-toolbar table.w2ui-button .w2ui-tb-down > div {
+ border: 4px solid transparent;
+ border-top: 5px solid #8D99A7;
+ margin-top: 5px;
+}
+.w2ui-toolbar table.w2ui-button.over {
+ border: 1px solid #cccccc;
+ background-color: #eeeeee;
+}
+.w2ui-toolbar table.w2ui-button.over .w2ui-tb-caption {
+ color: #000000;
+}
+.w2ui-toolbar table.w2ui-button.down {
+ border: 1px solid #aaaaaa;
+ background-color: #dddddd;
+}
+.w2ui-toolbar table.w2ui-button.down .w2ui-tb-caption {
+ color: #666666;
+}
+.w2ui-toolbar table.w2ui-button.checked {
+ border: 1px solid #aaaaaa;
+ background-color: #ffffff;
+}
+.w2ui-toolbar table.w2ui-button.checked .w2ui-tb-caption {
+ color: #000000;
+}
+.w2ui-toolbar table.w2ui-button table {
+ height: 17px;
+ border-radius: 4px;
+ cursor: default;
+}
+.w2ui-toolbar .w2ui-break {
+ background-image: -webkit-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, #999999 40%, #999999 60%, rgba(153, 153, 153, 0.1) 100%);
+ background-image: -moz-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, #999999 40%, #999999 60%, rgba(153, 153, 153, 0.1) 100%);
+ background-image: -ms-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, #999999 40%, #999999 60%, rgba(153, 153, 153, 0.1) 100%);
+ background-image: -o-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, #999999 40%, #999999 60%, rgba(153, 153, 153, 0.1) 100%);
+ background-image: linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, #999999 40%, #999999 60%, rgba(153, 153, 153, 0.1) 100%);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff999999', endColorstr='#ff999999', GradientType=0);
+ width: 1px !important;
+ height: 22px;
+ padding: 0px;
+ margin: 0px 6px;
+}
+.w2ui-listview {
+ overflow: auto !important;
+ background-color: #ffffff !important;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.w2ui-listview * {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.w2ui-listview > ul {
+ list-style-type: none;
+ margin: 0;
+ cursor: default;
+}
+.w2ui-listview > ul > li {
+ display: inline-block;
+ vertical-align: top;
+ overflow: hidden;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+.w2ui-listview > ul > li.w2ui-focused {
+ border: 1px solid #2661a6;
+}
+.w2ui-listview > ul > li.w2ui-selected {
+ border: 1px solid #2661a6;
+}
+.w2ui-listview > ul > li.w2ui-selected,
+.w2ui-listview > ul > li.w2ui-selected.hover {
+ background-image: -webkit-linear-gradient(#69b1e0, #4a96d3);
+ background-image: -moz-linear-gradient(#69b1e0, #4a96d3);
+ background-image: -ms-linear-gradient(#69b1e0, #4a96d3);
+ background-image: -o-linear-gradient(#69b1e0, #4a96d3);
+ background-image: linear-gradient(#69b1e0, #4a96d3);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff69b1e0', endColorstr='#ff4a96d3', GradientType=0);
+}
+.w2ui-listview > ul > li.w2ui-selected > div > div.caption,
+.w2ui-listview > ul > li.w2ui-selected.hover > div > div.caption {
+ color: #ffffff;
+}
+.w2ui-listview > ul > li.w2ui-selected > div > div.description,
+.w2ui-listview > ul > li.w2ui-selected.hover > div > div.description {
+ color: #dddddd;
+}
+.w2ui-listview > ul > li.w2ui-selected > div > div.extra > div > div,
+.w2ui-listview > ul > li.w2ui-selected.hover > div > div.extra > div > div {
+ color: #dddddd;
+}
+.w2ui-listview > ul > li.hover {
+ background-color: #d7e1ef;
+ border: 1px solid #2661a6;
+}
+.w2ui-listview > ul > li div {
+ vertical-align: middle;
+}
+.w2ui-listview > ul > li > div > div.caption {
+ display: block;
+ text-align: center;
+ word-wrap: break-word;
+ max-height: 50px;
+ color: #000000;
+ font-size: 12px;
+}
+.w2ui-listview > ul > li > div > div.description {
+ display: none;
+ text-align: left;
+ color: #777777;
+ font-size: 12px;
+}
+.w2ui-listview > ul > li > div > div.extra {
+ display: none;
+}
+.w2ui-listview > ul > li > div > div.extra > div > div {
+ color: #777777;
+}
+.w2ui-icon-small > ul {
+ padding: 1px 0px 0px 1px;
+}
+.w2ui-icon-small > ul > li {
+ margin: 0px 1px 1px 0px;
+ padding: 2px;
+ width: 250px;
+ white-space: nowrap;
+}
+.w2ui-icon-small > ul > li > div > div.w2ui-listview-img {
+ display: inline-block;
+ width: 26px;
+ height: 22px;
+ font-size: 21px;
+ margin-right: 2px;
+}
+.w2ui-icon-small > ul > li > div > div.caption {
+ display: inline-block;
+}
+.w2ui-icon-medium > ul {
+ padding: 4px 0px 0px 4px;
+}
+.w2ui-icon-medium > ul > li {
+ margin: 0px 4px 4px 0px;
+ padding: 4px;
+ width: 100px;
+}
+.w2ui-icon-medium > ul > li > div > div.w2ui-listview-img {
+ display: block;
+ width: 92px;
+ height: 60px;
+ font-size: 57px;
+ margin-left: auto;
+ margin-right: auto;
+ background-position: center;
+}
+.w2ui-icon-large > ul {
+ padding: 4px 0px 0px 4px;
+}
+.w2ui-icon-large > ul > li {
+ margin: 0px 4px 4px 0px;
+ padding: 4px;
+ width: 160px;
+}
+.w2ui-icon-large > ul > li > div > div.w2ui-listview-img {
+ display: block;
+ width: 152px;
+ height: 120px;
+ font-size: 114px;
+ margin-left: auto;
+ margin-right: auto;
+ background-position: center;
+}
+.w2ui-icon-tile > ul {
+ padding: 1px 0px 0px 1px;
+}
+.w2ui-icon-tile > ul > li {
+ margin: 0px 1px 1px 0px;
+ padding: 4px;
+ width: 250px;
+ white-space: nowrap;
+}
+.w2ui-icon-tile > ul > li > div > div.w2ui-listview-img {
+ display: inline-block;
+ width: 72px;
+ height: 60px;
+ font-size: 57px;
+ float: left;
+ margin-right: 4px;
+}
+.w2ui-icon-tile > ul > li > div > div.caption {
+ text-align: left;
+}
+.w2ui-icon-tile > ul > li > div > div.description {
+ display: block;
+}
+.w2ui-table > ul {
+ padding: 0;
+}
+.w2ui-table > ul > li {
+ width: 100%;
+ padding: 2px;
+ border-radius: 0px;
+ border-bottom: 1px dotted lightgray;
+}
+.w2ui-table > ul > li > div {
+ display: inline-block;
+ position: relative;
+ width: 100%;
+ white-space: nowrap;
+ overflow: hidden;
+}
+.w2ui-table > ul > li > div > div.w2ui-listview-img {
+ display: inline-block;
+ width: 38px;
+ height: 32px;
+ font-size: 31px;
+ margin-right: 2px;
+}
+.w2ui-table > ul > li > div > div.caption {
+ display: inline-block;
+}
+.w2ui-table > ul > li > div > div.extra {
+ display: inline-block;
+ position: absolute;
+ right: 0;
+ height: 100%;
+ background-color: #ffffff;
+}
+.w2ui-table > ul > li > div > div.extra > div:before {
+ display: inline-block;
+ height: 100%;
+ width: 0;
+ content: '';
+ vertical-align: middle;
+}
+.w2ui-table > ul > li > div > div.extra > div {
+ display: inline;
+}
+.w2ui-table > ul > li > div > div.extra > div > div {
+ display: inline-block;
+ font-size: 12px;
+}
+.w2ui-table > ul > li.w2ui-selected div.extra,
+.w2ui-table > ul > li.w2ui-selected.hover div.extra {
+ background-image: -webkit-linear-gradient(#69b1e0, #4a96d3);
+ background-image: -moz-linear-gradient(#69b1e0, #4a96d3);
+ background-image: -ms-linear-gradient(#69b1e0, #4a96d3);
+ background-image: -o-linear-gradient(#69b1e0, #4a96d3);
+ background-image: linear-gradient(#69b1e0, #4a96d3);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff69b1e0', endColorstr='#ff4a96d3', GradientType=0);
+}
+.w2ui-table > ul > li.hover div.extra {
+ background-color: #d7e1ef;
+}
+.w2ui-listview > ul > li div.icon-none {
+ border: 1px solid rgba(102, 102, 102, 0.35);
+}
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.js b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.js
new file mode 100644
index 00000000..afdc6a00
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.js
@@ -0,0 +1,13617 @@
+/* w2ui 1.4 (c) http://w2ui.com, vitmalina@gmail.com */
+var w2ui = w2ui || {};
+var w2obj = w2obj || {}; // expose object to be able to overwrite default functions
+
+/************************************************
+* Library: Web 2.0 UI for jQuery
+* - Following objects are defines
+* - w2ui - object that will contain all widgets
+* - w2obj - object with widget prototypes
+* - w2utils - basic utilities
+* - $().w2render - common render
+* - $().w2destroy - common destroy
+* - $().w2marker - marker plugin
+* - $().w2tag - tag plugin
+* - $().w2overlay - overlay plugin
+* - $().w2menu - menu plugin
+* - w2utils.event - generic event object
+* - w2utils.keyboard - object for keyboard navigation
+* - Dependencies: jQuery
+*
+* == NICE TO HAVE ==
+* - date has problems in FF new Date('yyyy-mm-dd') breaks
+* - bug: w2utils.formatDate('2011-31-01', 'yyyy-dd-mm'); - wrong foratter
+* - overlay should be displayed where more space (on top or on bottom)
+* - write and article how to replace certain framework functions
+* - format date and time is buggy
+* - onComplete should pass widget as context (this)
+* - add maxHeight for the w2menu
+* - user localization from another lib (make it generic), https://github.com/jquery/globalize#readme
+* - hidden and disabled in menus
+* - isTime should support seconds
+* - TEST On IOS
+*
+************************************************/
+
+var w2utils = (function () {
+ var tmp = {}; // for some temp variables
+ var obj = {
+ version : '1.4.0',
+ settings : {
+ "locale" : "en-us",
+ "date_format" : "m/d/yyyy",
+ "date_display" : "Mon d, yyyy",
+ "time_format" : "h12",
+ "currencyPrefix" : "$",
+ "currencySuffix" : "",
+ "currencyPrecision" : 2,
+ "groupSymbol" : ",",
+ "shortmonths" : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ "fullmonths" : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ "shortdays" : ["M", "T", "W", "T", "F", "S", "S"],
+ "fulldays" : ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
+ "dataType" : 'HTTP', // can be HTTP, RESTFULL, JSON (case sensative)
+ "phrases" : {} // empty object for english phrases
+ },
+ isInt : isInt,
+ isFloat : isFloat,
+ isMoney : isMoney,
+ isHex : isHex,
+ isAlphaNumeric : isAlphaNumeric,
+ isEmail : isEmail,
+ isDate : isDate,
+ isTime : isTime,
+ age : age,
+ date : date,
+ size : size,
+ formatNumber : formatNumber,
+ formatDate : formatDate,
+ formatTime : formatTime,
+ formatDateTime : formatDateTime,
+ stripTags : stripTags,
+ encodeTags : encodeTags,
+ escapeId : escapeId,
+ base64encode : base64encode,
+ base64decode : base64decode,
+ transition : transition,
+ lock : lock,
+ unlock : unlock,
+ lang : lang,
+ locale : locale,
+ getSize : getSize,
+ scrollBarSize : scrollBarSize,
+ checkName : checkName,
+ checkUniqueId : checkUniqueId,
+ parseRoute : parseRoute,
+ // some internal variables
+ isIOS : ((navigator.userAgent.toLowerCase().indexOf('iphone') != -1 ||
+ navigator.userAgent.toLowerCase().indexOf('ipod') != -1 ||
+ navigator.userAgent.toLowerCase().indexOf('ipad') != -1)
+ ? true : false),
+ isIE : ((navigator.userAgent.toLowerCase().indexOf('msie') != -1 ||
+ navigator.userAgent.toLowerCase().indexOf('trident') != -1 )
+ ? true : false)
+ };
+ return obj;
+
+ function isInt (val) {
+ var re = /^[-+]?[0-9]+$/;
+ return re.test(val);
+ }
+
+ function isFloat (val) {
+ return (typeof val === 'number' || (typeof val === 'string' && val !== '')) && !isNaN(Number(val));
+ }
+
+ function isMoney (val) {
+ var se = w2utils.settings;
+ var re = new RegExp('^'+ (se.currencyPrefix ? '\\' + se.currencyPrefix + '?' : '') +'[-+]?[0-9]*[\.]?[0-9]+'+ (se.currencySuffix ? '\\' + se.currencySuffix + '?' : '') +'$', 'i');
+ if (typeof val === 'string') {
+ val = val.replace(new RegExp(se.groupSymbol, 'g'), '');
+ }
+ if (typeof val === 'object' || val === '') return false;
+ return re.test(val);
+ }
+
+ function isHex (val) {
+ var re = /^[a-fA-F0-9]+$/;
+ return re.test(val);
+ }
+
+ function isAlphaNumeric (val) {
+ var re = /^[a-zA-Z0-9_-]+$/;
+ return re.test(val);
+ }
+
+ function isEmail (val) {
+ var email = /^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
+ return email.test(val);
+ }
+
+ function isDate (val, format, retDate) {
+ if (!val) return false;
+
+ var dt = 'Invalid Date';
+ var month, day, year;
+
+ if (format == null) format = w2utils.settings.date_format;
+
+ if (typeof val.getUTCFullYear === 'function' && typeof val.getUTCMonth === 'function' && typeof val.getUTCDate === 'function') {
+ year = val.getUTCFullYear();
+ month = val.getUTCMonth();
+ day = val.getUTCDate();
+ } else if (typeof val.getFullYear === 'function' && typeof val.getMonth === 'function' && typeof val.getDate === 'function') {
+ year = val.getFullYear();
+ month = val.getMonth();
+ day = val.getDate();
+ } else {
+ val = String(val);
+ // convert month formats
+ if (RegExp('mon', 'ig').test(format)) {
+ format = format.replace(/month/ig, 'm').replace(/mon/ig, 'm').replace(/dd/ig, 'd').replace(/[, ]/ig, '/').replace(/\/\//g, '/').toLowerCase();
+ val = val.replace(/[, ]/ig, '/').replace(/\/\//g, '/').toLowerCase();
+ for (var m = 0, len = w2utils.settings.fullmonths.length; m < len; m++) {
+ var t = w2utils.settings.fullmonths[m];
+ val = val.replace(RegExp(t, 'ig'), (parseInt(m) + 1)).replace(RegExp(t.substr(0, 3), 'ig'), (parseInt(m) + 1));
+ }
+ }
+ // format date
+ var tmp = val.replace(/-/g, '/').replace(/\./g, '/').toLowerCase().split('/');
+ var tmp2 = format.replace(/-/g, '/').replace(/\./g, '/').toLowerCase();
+ if (tmp2 === 'mm/dd/yyyy') { month = tmp[0]; day = tmp[1]; year = tmp[2]; }
+ if (tmp2 === 'm/d/yyyy') { month = tmp[0]; day = tmp[1]; year = tmp[2]; }
+ if (tmp2 === 'dd/mm/yyyy') { month = tmp[1]; day = tmp[0]; year = tmp[2]; }
+ if (tmp2 === 'd/m/yyyy') { month = tmp[1]; day = tmp[0]; year = tmp[2]; }
+ if (tmp2 === 'yyyy/dd/mm') { month = tmp[2]; day = tmp[1]; year = tmp[0]; }
+ if (tmp2 === 'yyyy/d/m') { month = tmp[2]; day = tmp[1]; year = tmp[0]; }
+ if (tmp2 === 'yyyy/mm/dd') { month = tmp[1]; day = tmp[2]; year = tmp[0]; }
+ if (tmp2 === 'yyyy/m/d') { month = tmp[1]; day = tmp[2]; year = tmp[0]; }
+ if (tmp2 === 'mm/dd/yy') { month = tmp[0]; day = tmp[1]; year = tmp[2]; }
+ if (tmp2 === 'm/d/yy') { month = tmp[0]; day = tmp[1]; year = parseInt(tmp[2]) + 1900; }
+ if (tmp2 === 'dd/mm/yy') { month = tmp[1]; day = tmp[0]; year = parseInt(tmp[2]) + 1900; }
+ if (tmp2 === 'd/m/yy') { month = tmp[1]; day = tmp[0]; year = parseInt(tmp[2]) + 1900; }
+ if (tmp2 === 'yy/dd/mm') { month = tmp[2]; day = tmp[1]; year = parseInt(tmp[0]) + 1900; }
+ if (tmp2 === 'yy/d/m') { month = tmp[2]; day = tmp[1]; year = parseInt(tmp[0]) + 1900; }
+ if (tmp2 === 'yy/mm/dd') { month = tmp[1]; day = tmp[2]; year = parseInt(tmp[0]) + 1900; }
+ if (tmp2 === 'yy/m/d') { month = tmp[1]; day = tmp[2]; year = parseInt(tmp[0]) + 1900; }
+ }
+ if (!isInt(year)) return false;
+ if (!isInt(month)) return false;
+ if (!isInt(day)) return false;
+ year = +year;
+ month = +month;
+ day = +day;
+ dt = new Date(year, month - 1, day);
+ // do checks
+ if (month == null) return false;
+ if (dt === 'Invalid Date') return false;
+ if ((dt.getMonth() + 1 !== month) || (dt.getDate() !== day) || (dt.getFullYear() !== year)) return false;
+ if (retDate === true) return dt; else return true;
+ }
+
+ function isTime (val, retTime) {
+ // Both formats 10:20pm and 22:20
+ if (val == null) return false;
+ var max, pm;
+ // -- process american format
+ val = String(val);
+ val = val.toUpperCase();
+ pm = val.indexOf('PM') >= 0;
+ var ampm = (pm || val.indexOf('AM') >= 0);
+ if (ampm) max = 12; else max = 24;
+ val = val.replace('AM', '').replace('PM', '');
+ val = $.trim(val);
+ // ---
+ var tmp = val.split(':');
+ var h = parseInt(tmp[0] || 0), m = parseInt(tmp[1] || 0);
+ // accept edge case: 3PM is a good timestamp, but 3 (without AM or PM) is NOT:
+ if ((!ampm || tmp.length !== 1) && tmp.length !== 2) { return false; }
+ if (tmp[0] === '' || h < 0 || h > max || !this.isInt(tmp[0]) || tmp[0].length > 2) { return false; }
+ if (tmp.length === 2 && (tmp[1] === '' || m < 0 || m > 59 || !this.isInt(tmp[1]) || tmp[1].length !== 2)) { return false; }
+ // check the edge cases: 12:01AM is ok, as is 12:01PM, but 24:01 is NOT ok while 24:00 is (midnight; equivalent to 00:00).
+ // meanwhile, there is 00:00 which is ok, but 0AM nor 0PM are okay, while 0:01AM and 0:00AM are.
+ if (!ampm && max === h && m !== 0) { return false; }
+ if (ampm && tmp.length === 1 && h === 0) { return false; }
+
+ if (retTime === true) {
+ if (pm) h += 12;
+ return {
+ hours: h,
+ minutes: m
+ };
+ }
+ return true;
+ }
+
+ function age (dateStr) {
+ if (dateStr === '' || dateStr == null) return '';
+ var d1 = new Date(dateStr);
+ if (w2utils.isInt(dateStr)) d1 = new Date(Number(dateStr)); // for unix timestamps
+ if (d1 === 'Invalid Date') return '';
+
+ var d2 = new Date();
+ var sec = (d2.getTime() - d1.getTime()) / 1000;
+ var amount = '';
+ var type = '';
+ if (sec < 0) {
+ amount = '<span style="color: #aaa">future</span>';
+ type = '';
+ } else if (sec < 60) {
+ amount = Math.floor(sec);
+ type = 'sec';
+ if (sec < 0) { amount = 0; type = 'sec'; }
+ } else if (sec < 60*60) {
+ amount = Math.floor(sec/60);
+ type = 'min';
+ } else if (sec < 24*60*60) {
+ amount = Math.floor(sec/60/60);
+ type = 'hour';
+ } else if (sec < 30*24*60*60) {
+ amount = Math.floor(sec/24/60/60);
+ type = 'day';
+ } else if (sec < 365.25*24*60*60) {
+ amount = Math.floor(sec/365.25/24/60/60*10)/10;
+ type = 'month';
+ } else if (sec >= 365.25*24*60*60) {
+ amount = Math.floor(sec/365.25/24/60/60*10)/10;
+ type = 'year';
+ }
+ return amount + ' ' + type + (amount > 1 ? 's' : '');
+ }
+
+ function date (dateStr) {
+ if (dateStr === '' || dateStr == null) return '';
+ var d1 = new Date(dateStr);
+ if (w2utils.isInt(dateStr)) d1 = new Date(Number(dateStr)); // for unix timestamps
+ if (d1 === 'Invalid Date') return '';
+
+ var months = w2utils.settings.shortmonths;
+ var d2 = new Date(); // today
+ var d3 = new Date();
+ d3.setTime(d3.getTime() - 86400000); // yesterday
+
+ var dd1 = months[d1.getMonth()] + ' ' + d1.getDate() + ', ' + d1.getFullYear();
+ var dd2 = months[d2.getMonth()] + ' ' + d2.getDate() + ', ' + d2.getFullYear();
+ var dd3 = months[d3.getMonth()] + ' ' + d3.getDate() + ', ' + d3.getFullYear();
+
+ var time = (d1.getHours() - (d1.getHours() > 12 ? 12 :0)) + ':' + (d1.getMinutes() < 10 ? '0' : '') + d1.getMinutes() + ' ' + (d1.getHours() >= 12 ? 'pm' : 'am');
+ var time2= (d1.getHours() - (d1.getHours() > 12 ? 12 :0)) + ':' + (d1.getMinutes() < 10 ? '0' : '') + d1.getMinutes() + ':' + (d1.getSeconds() < 10 ? '0' : '') + d1.getSeconds() + ' ' + (d1.getHours() >= 12 ? 'pm' : 'am');
+ var dsp = dd1;
+ if (dd1 === dd2) dsp = time;
+ if (dd1 === dd3) dsp = w2utils.lang('Yesterday');
+
+ return '<span title="'+ dd1 +' ' + time2 +'">'+ dsp +'</span>';
+ }
+
+ function size (sizeStr) {
+ if (!w2utils.isFloat(sizeStr) || sizeStr === '') return '';
+ sizeStr = parseFloat(sizeStr);
+ if (sizeStr === 0) return 0;
+ var sizes = ['Bt', 'KB', 'MB', 'GB', 'TB'];
+ var i = parseInt( Math.floor( Math.log(sizeStr) / Math.log(1024) ) );
+ return (Math.floor(sizeStr / Math.pow(1024, i) * 10) / 10).toFixed(i === 0 ? 0 : 1) + ' ' + sizes[i];
+ }
+
+ function formatNumber (val, groupSymbol) {
+ var ret = '';
+ if (groupSymbol == null) groupSymbol = w2utils.settings.groupSymbol || ',';
+ // check if this is a number
+ if (w2utils.isFloat(val) || w2utils.isInt(val) || w2utils.isMoney(val)) {
+ tmp = String(val).split('.');
+ ret = String(tmp[0]).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1" + groupSymbol);
+ if (tmp[1] != null) ret += '.' + tmp[1];
+ }
+ return ret;
+ }
+
+ function formatDate (dateStr, format) { // IMPORTANT dateStr HAS TO BE valid JavaScript Date String
+ var months = w2utils.settings.shortmonths;
+ var fullMonths = w2utils.settings.fullmonths;
+ if (!format) format = this.settings.date_format;
+ if (dateStr === '' || dateStr == null) return '';
+
+ var dt = new Date(dateStr);
+ if (w2utils.isInt(dateStr)) dt = new Date(Number(dateStr)); // for unix timestamps
+ if (dt === 'Invalid Date') return '';
+
+ var year = dt.getFullYear();
+ var month = dt.getMonth();
+ var date = dt.getDate();
+ return format.toLowerCase()
+ .replace('month', w2utils.settings.fullmonths[month])
+ .replace('mon', w2utils.settings.shortmonths[month])
+ .replace(/yyyy/g, year)
+ .replace(/yyy/g, year)
+ .replace(/yy/g, year > 2000 ? 100 + parseInt(String(year).substr(2)) : String(year).substr(2))
+ .replace(/(^|[^a-z$])y/g, '$1' + year) // only y's that are not preceeded by a letter
+ .replace(/mm/g, (month + 1 < 10 ? '0' : '') + (month + 1))
+ .replace(/dd/g, (date < 10 ? '0' : '') + date)
+ .replace(/(^|[^a-z$])m/g, '$1' + (month + 1)) // only y's that are not preceeded by a letter
+ .replace(/(^|[^a-z$])d/g, '$1' + date); // only y's that are not preceeded by a letter
+ }
+
+ function formatTime (dateStr, format) { // IMPORTANT dateStr HAS TO BE valid JavaScript Date String
+ var months = w2utils.settings.shortmonths;
+ var fullMonths = w2utils.settings.fullmonths;
+ if (!format) format = (this.settings.time_format === 'h12' ? 'hh:mi pm' : 'h24:mi');
+ if (dateStr === '' || dateStr == null) return '';
+
+ var dt = new Date(dateStr);
+ if (w2utils.isInt(dateStr)) dt = new Date(Number(dateStr)); // for unix timestamps
+ if (w2utils.isTime(dateStr)) {
+ var tmp = w2utils.isTime(dateStr, true);
+ dt = new Date();
+ dt.setHours(tmp.hours);
+ dt.setMinutes(tmp.minutes);
+ }
+ if (dt === 'Invalid Date') return '';
+
+ var type = 'am';
+ var hour = dt.getHours();
+ var h24 = dt.getHours();
+ var min = dt.getMinutes();
+ var sec = dt.getSeconds();
+ if (min < 10) min = '0' + min;
+ if (sec < 10) sec = '0' + sec;
+ if (format.indexOf('am') !== -1 || format.indexOf('pm') !== -1) {
+ if (hour >= 12) type = 'pm';
+ if (hour > 12) hour = hour - 12;
+ }
+ return format.toLowerCase()
+ .replace('am', type)
+ .replace('pm', type)
+ .replace('hh', hour)
+ .replace('h24', h24)
+ .replace('mm', min)
+ .replace('mi', min)
+ .replace('ss', sec)
+ .replace(/(^|[^a-z$])h/g, '$1' + hour) // only y's that are not preceeded by a letter
+ .replace(/(^|[^a-z$])m/g, '$1' + min) // only y's that are not preceeded by a letter
+ .replace(/(^|[^a-z$])s/g, '$1' + sec); // only y's that are not preceeded by a letter
+ }
+
+ function formatDateTime(dateStr, format) {
+ var fmt;
+ if (typeof format !== 'string') {
+ fmt = [this.settings.date_format, this.settings.time_format];
+ } else {
+ fmt = format.split('|');
+ }
+ return this.formatDate(dateStr, fmt[0]) + ' ' + this.formatTime(dateStr, fmt[1]);
+ }
+
+ function stripTags (html) {
+ if (html === null) return html;
+ switch (typeof html) {
+ case 'number':
+ break;
+ case 'string':
+ html = $.trim(String(html).replace(/(<([^>]+)>)/ig, ""));
+ break;
+ case 'object':
+ for (var a in html) html[a] = this.stripTags(html[a]);
+ break;
+ }
+ return html;
+ }
+
+ function encodeTags (html) {
+ if (html === null) return html;
+ switch (typeof html) {
+ case 'number':
+ break;
+ case 'string':
+ html = String(html).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
+ break;
+ case 'object':
+ for (var a in html) html[a] = this.encodeTags(html[a]);
+ break;
+ }
+ return html;
+ }
+
+ function escapeId (id) {
+ if (id === '' || id == null) return '';
+ return String(id).replace(/([;&,\.\+\*\~'`:"\!\^#$%@\[\]\(\)=<>\|\/? {}\\])/g, '\\$1');
+ }
+
+ function base64encode (input) {
+ var output = "";
+ var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+ var i = 0;
+ var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ input = utf8_encode(input);
+
+ while (i < input.length) {
+ chr1 = input.charCodeAt(i++);
+ chr2 = input.charCodeAt(i++);
+ chr3 = input.charCodeAt(i++);
+ enc1 = chr1 >> 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 + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
+ }
+
+ function utf8_encode (string) {
+ var string = String(string).replace(/\r\n/g,"\n");
+ var utftext = "";
+
+ for (var n = 0; n < string.length; n++) {
+ var 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;
+ }
+
+ return output;
+ }
+
+ function base64decode (input) {
+ var output = "";
+ var chr1, chr2, chr3;
+ var enc1, enc2, enc3, enc4;
+ var i = 0;
+ var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+ while (i < input.length) {
+ enc1 = keyStr.indexOf(input.charAt(i++));
+ enc2 = keyStr.indexOf(input.charAt(i++));
+ enc3 = keyStr.indexOf(input.charAt(i++));
+ enc4 = 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 = utf8_decode(output);
+
+ function utf8_decode (utftext) {
+ var string = "";
+ var i = 0;
+ var c = 0, c2, c3;
+
+ 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;
+ }
+
+ return output;
+ }
+
+ function transition (div_old, div_new, type, callBack) {
+ var width = $(div_old).width();
+ var height = $(div_old).height();
+ var time = 0.5;
+
+ if (!div_old || !div_new) {
+ console.log('ERROR: Cannot do transition when one of the divs is null');
+ return;
+ }
+
+ div_old.parentNode.style.cssText += cross('perspective', '700px') +'; overflow: hidden;';
+ div_old.style.cssText += '; position: absolute; z-index: 1019; '+ cross('backface-visibility', 'hidden');
+ div_new.style.cssText += '; position: absolute; z-index: 1020; '+ cross('backface-visibility', 'hidden');
+
+ switch (type) {
+ case 'slide-left':
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ div_new.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d('+ width + 'px, 0, 0)', 'translate('+ width +'px, 0)');
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time+'s') +';'+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ div_old.style.cssText += cross('transition', time+'s') +';'+ cross('transform', 'translate3d(-'+ width +'px, 0, 0)', 'translate(-'+ width +'px, 0)');
+ }, 1);
+ break;
+
+ case 'slide-right':
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ div_new.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(-'+ width +'px, 0, 0)', 'translate(-'+ width +'px, 0)');
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'translate3d(0px, 0, 0)', 'translate(0px, 0)');
+ div_old.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'translate3d('+ width +'px, 0, 0)', 'translate('+ width +'px, 0)');
+ }, 1);
+ break;
+
+ case 'slide-down':
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; z-index: 1; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ div_new.style.cssText += 'overflow: hidden; z-index: 0; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ div_old.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'translate3d(0, '+ height +'px, 0)', 'translate(0, '+ height +'px)');
+ }, 1);
+ break;
+
+ case 'slide-up':
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ div_new.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(0, '+ height +'px, 0)', 'translate(0, '+ height +'px)');
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ div_old.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ }, 1);
+ break;
+
+ case 'flip-left':
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; '+ cross('transform', 'rotateY(0deg)');
+ div_new.style.cssText += 'overflow: hidden; '+ cross('transform', 'rotateY(-180deg)');
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'rotateY(0deg)');
+ div_old.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'rotateY(180deg)');
+ }, 1);
+ break;
+
+ case 'flip-right':
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; '+ cross('transform', 'rotateY(0deg)');
+ div_new.style.cssText += 'overflow: hidden; '+ cross('transform', 'rotateY(180deg)');
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'rotateY(0deg)');
+ div_old.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'rotateY(-180deg)');
+ }, 1);
+ break;
+
+ case 'flip-down':
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; '+ cross('transform', 'rotateX(0deg)');
+ div_new.style.cssText += 'overflow: hidden; '+ cross('transform', 'rotateX(180deg)');
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'rotateX(0deg)');
+ div_old.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'rotateX(-180deg)');
+ }, 1);
+ break;
+
+ case 'flip-up':
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; '+ cross('transform', 'rotateX(0deg)');
+ div_new.style.cssText += 'overflow: hidden; '+ cross('transform', 'rotateX(-180deg)');
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'rotateX(0deg)');
+ div_old.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'rotateX(180deg)');
+ }, 1);
+ break;
+
+ case 'pop-in':
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ div_new.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)') + '; '+ cross('transform', 'scale(.8)') + '; opacity: 0;';
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'scale(1)') +'; opacity: 1;';
+ div_old.style.cssText += cross('transition', time+'s') +';';
+ }, 1);
+ break;
+
+ case 'pop-out':
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)') +'; '+ cross('transform', 'scale(1)') +'; opacity: 1;';
+ div_new.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)') +'; opacity: 0;';
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time+'s') +'; opacity: 1;';
+ div_old.style.cssText += cross('transition', time+'s') +'; '+ cross('transform', 'scale(1.7)') +'; opacity: 0;';
+ }, 1);
+ break;
+
+ default:
+ // init divs
+ div_old.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)');
+ div_new.style.cssText += 'overflow: hidden; '+ cross('transform', 'translate3d(0, 0, 0)', 'translate(0, 0)') +'; opacity: 0;';
+ $(div_new).show();
+ // -- need a timing function because otherwise not working
+ window.setTimeout(function() {
+ div_new.style.cssText += cross('transition', time +'s') +'; opacity: 1;';
+ div_old.style.cssText += cross('transition', time +'s');
+ }, 1);
+ break;
+ }
+
+ setTimeout(function () {
+ if (type === 'slide-down') {
+ $(div_old).css('z-index', '1019');
+ $(div_new).css('z-index', '1020');
+ }
+ if (div_new) {
+ $(div_new).css({
+ 'opacity': '1',
+ '-webkit-transition': '',
+ '-moz-transition': '',
+ '-ms-transition': '',
+ '-o-transition': '',
+ '-webkit-transform': '',
+ '-moz-transform': '',
+ '-ms-transform': '',
+ '-o-transform': '',
+ '-webkit-backface-visibility': '',
+ '-moz-backface-visibility': '',
+ '-ms-backface-visibility': '',
+ '-o-backface-visibility': ''
+ });
+ }
+ if (div_old) {
+ $(div_old).css({
+ 'opacity': '1',
+ '-webkit-transition': '',
+ '-moz-transition': '',
+ '-ms-transition': '',
+ '-o-transition': '',
+ '-webkit-transform': '',
+ '-moz-transform': '',
+ '-ms-transform': '',
+ '-o-transform': '',
+ '-webkit-backface-visibility': '',
+ '-moz-backface-visibility': '',
+ '-ms-backface-visibility': '',
+ '-o-backface-visibility': ''
+ });
+ if (div_old.parentNode) $(div_old.parentNode).css({
+ '-webkit-perspective': '',
+ '-moz-perspective': '',
+ '-ms-perspective': '',
+ '-o-perspective': ''
+ });
+ }
+ if (typeof callBack === 'function') callBack();
+ }, time * 1000);
+
+ function cross(property, value, none_webkit_value) {
+ var isWebkit=!!window.webkitURL; // jQuery no longer supports $.browser - RR
+ if (!isWebkit && typeof none_webkit_value !== 'undefined') value = none_webkit_value;
+ return ';'+ property +': '+ value +'; -webkit-'+ property +': '+ value +'; -moz-'+ property +': '+ value +'; '+
+ '-ms-'+ property +': '+ value +'; -o-'+ property +': '+ value +';';
+ }
+ }
+
+ function lock (box, msg, spinner) {
+ var options = {};
+ if (typeof msg === 'object') {
+ options = msg;
+ } else {
+ options.msg = msg;
+ options.spinner = spinner;
+ }
+ if (!options.msg && options.msg !== 0) options.msg = '';
+ w2utils.unlock(box);
+ $(box).prepend(
+ '<div class="w2ui-lock"></div>'+
+ '<div class="w2ui-lock-msg"></div>'
+ );
+ var $lock = $(box).find('.w2ui-lock');
+ var mess = $(box).find('.w2ui-lock-msg');
+ if (!options.msg) mess.css({ 'background-color': 'transparent', 'border': '0px' });
+ if (options.spinner === true) options.msg = '<div class="w2ui-spinner" '+ (!options.msg ? 'style="width: 35px; height: 35px"' : '') +'></div>' + options.msg;
+ if (options.opacity != null) $lock.css('opacity', options.opacity);
+ if (typeof $lock.fadeIn == 'function') {
+ $lock.fadeIn(200);
+ mess.html(options.msg).fadeIn(200);
+ } else {
+ $lock.show();
+ mess.html(options.msg).show(0);
+ }
+ // hide all tags (do not hide overlays as the form can be in overlay)
+ $().w2tag();
+ }
+
+ function unlock (box) {
+ $(box).find('.w2ui-lock').remove();
+ $(box).find('.w2ui-lock-msg').remove();
+ }
+
+ function getSize (el, type) {
+ var $el = $(el);
+ var bwidth = {
+ left : parseInt($el.css('border-left-width')) || 0,
+ right : parseInt($el.css('border-right-width')) || 0,
+ top : parseInt($el.css('border-top-width')) || 0,
+ bottom : parseInt($el.css('border-bottom-width')) || 0
+ };
+ var mwidth = {
+ left : parseInt($el.css('margin-left')) || 0,
+ right : parseInt($el.css('margin-right')) || 0,
+ top : parseInt($el.css('margin-top')) || 0,
+ bottom : parseInt($el.css('margin-bottom')) || 0
+ };
+ var pwidth = {
+ left : parseInt($el.css('padding-left')) || 0,
+ right : parseInt($el.css('padding-right')) || 0,
+ top : parseInt($el.css('padding-top')) || 0,
+ bottom : parseInt($el.css('padding-bottom')) || 0
+ };
+ switch (type) {
+ case 'top' : return bwidth.top + mwidth.top + pwidth.top;
+ case 'bottom' : return bwidth.bottom + mwidth.bottom + pwidth.bottom;
+ case 'left' : return bwidth.left + mwidth.left + pwidth.left;
+ case 'right' : return bwidth.right + mwidth.right + pwidth.right;
+ case 'width' : return bwidth.left + bwidth.right + mwidth.left + mwidth.right + pwidth.left + pwidth.right + parseInt($el.width());
+ case 'height' : return bwidth.top + bwidth.bottom + mwidth.top + mwidth.bottom + pwidth.top + pwidth.bottom + parseInt($el.height());
+ case '+width' : return bwidth.left + bwidth.right + mwidth.left + mwidth.right + pwidth.left + pwidth.right;
+ case '+height' : return bwidth.top + bwidth.bottom + mwidth.top + mwidth.bottom + pwidth.top + pwidth.bottom;
+ }
+ return 0;
+ }
+
+ function lang (phrase) {
+ var translation = this.settings.phrases[phrase];
+ if (translation == null) return phrase; else return translation;
+ }
+
+ function locale (locale) {
+ if (!locale) locale = 'en-us';
+ if (locale.length === 5) locale = 'locale/'+ locale +'.json';
+ // load from the file
+ $.ajax({
+ url : locale,
+ type : "GET",
+ dataType : "JSON",
+ async : false,
+ cache : false,
+ success : function (data, status, xhr) {
+ w2utils.settings = $.extend(true, w2utils.settings, data);
+ // apply translation to some prototype functions
+ var p = w2obj.grid.prototype;
+ for (var b in p.buttons) {
+ p.buttons[b].caption = w2utils.lang(p.buttons[b].caption);
+ p.buttons[b].hint = w2utils.lang(p.buttons[b].hint);
+ }
+ p.msgDelete = w2utils.lang(p.msgDelete);
+ p.msgNotJSON = w2utils.lang(p.msgNotJSON);
+ p.msgRefresh = w2utils.lang(p.msgRefresh);
+ },
+ error : function (xhr, status, msg) {
+ console.log('ERROR: Cannot load locale '+ locale);
+ }
+ });
+ }
+
+ function scrollBarSize () {
+ if (tmp.scrollBarSize) return tmp.scrollBarSize;
+ var html =
+ '<div id="_scrollbar_width" style="position: absolute; top: -300px; width: 100px; height: 100px; overflow-y: scroll;">'+
+ ' <div style="height: 120px">1</div>'+
+ '</div>';
+ $('body').append(html);
+ tmp.scrollBarSize = 100 - $('#_scrollbar_width > div').width();
+ $('#_scrollbar_width').remove();
+ if (String(navigator.userAgent).indexOf('MSIE') >= 0) tmp.scrollBarSize = tmp.scrollBarSize / 2; // need this for IE9+
+ return tmp.scrollBarSize;
+ }
+
+
+ function checkName (params, component) { // was w2checkNameParam
+ if (!params || typeof params.name === 'undefined') {
+ console.log('ERROR: The parameter "name" is required but not supplied in $().'+ component +'().');
+ return false;
+ }
+ if (typeof w2ui[params.name] !== 'undefined') {
+ console.log('ERROR: The parameter "name" is not unique. There are other objects already created with the same name (obj: '+ params.name +').');
+ return false;
+ }
+ if (!w2utils.isAlphaNumeric(params.name)) {
+ console.log('ERROR: The parameter "name" has to be alpha-numeric (a-z, 0-9, dash and underscore). ');
+ return false;
+ }
+ return true;
+ }
+
+ function checkUniqueId (id, items, itemsDecription, objName) { // was w2checkUniqueId
+ if (!$.isArray(items)) items = [items];
+ for (var i = 0; i < items.length; i++) {
+ if (items[i].id === id) {
+ console.log('ERROR: The parameter "id='+ id +'" is not unique within the current '+ itemsDecription +'. (obj: '+ objName +')');
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function parseRoute(route) {
+ var keys = [];
+ var path = route
+ .replace(/\/\(/g, '(?:/')
+ .replace(/\+/g, '__plus__')
+ .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional) {
+ keys.push({ name: key, optional: !! optional });
+ slash = slash || '';
+ return '' + (optional ? '' : slash) + '(?:' + (optional ? slash : '') + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')' + (optional || '');
+ })
+ .replace(/([\/.])/g, '\\$1')
+ .replace(/__plus__/g, '(.+)')
+ .replace(/\*/g, '(.*)');
+ return {
+ path : new RegExp('^' + path + '$', 'i'),
+ keys : keys
+ };
+ }
+})();
+
+/***********************************************************
+* Generic Event Object
+* --- This object is reused across all other
+* --- widgets in w2ui.
+*
+*********************************************************/
+
+w2utils.event = {
+
+ on: function (eventData, handler) {
+ if (!$.isPlainObject(eventData)) eventData = { type: eventData };
+ eventData = $.extend({ type: null, execute: 'before', target: null, onComplete: null }, eventData);
+
+ if (!eventData.type) { console.log('ERROR: You must specify event type when calling .on() method of '+ this.name); return; }
+ if (!handler) { console.log('ERROR: You must specify event handler function when calling .on() method of '+ this.name); return; }
+ if (!$.isArray(this.handlers)) this.handlers = [];
+ this.handlers.push({ event: eventData, handler: handler });
+ },
+
+ off: function (eventData, handler) {
+ if (!$.isPlainObject(eventData)) eventData = { type: eventData };
+ eventData = $.extend({}, { type: null, execute: 'before', target: null, onComplete: null }, eventData);
+
+ if (!eventData.type) { console.log('ERROR: You must specify event type when calling .off() method of '+ this.name); return; }
+ if (!handler) { handler = null; }
+ // remove handlers
+ var newHandlers = [];
+ for (var h = 0, len = this.handlers.length; h < len; h++) {
+ var t = this.handlers[h];
+ if ((t.event.type === eventData.type || eventData.type === '*') &&
+ (t.event.target === eventData.target || eventData.target === null) &&
+ (t.handler === handler || handler === null))
+ {
+ // match
+ } else {
+ newHandlers.push(t);
+ }
+ }
+ this.handlers = newHandlers;
+ },
+
+ trigger: function (eventData) {
+ var eventData = $.extend({ type: null, phase: 'before', target: null }, eventData, {
+ isStopped: false, isCancelled: false,
+ preventDefault : function () { this.isCancelled = true; },
+ stopPropagation : function () { this.isStopped = true; }
+ });
+ if (eventData.phase === 'before') eventData.onComplete = null;
+ var args, fun, tmp;
+ if (eventData.target == null) eventData.target = null;
+ if (!$.isArray(this.handlers)) this.handlers = [];
+ // process events in REVERSE order
+ for (var h = this.handlers.length-1; h >= 0; h--) {
+ var item = this.handlers[h];
+ if ((item.event.type === eventData.type || item.event.type === '*') &&
+ (item.event.target === eventData.target || item.event.target === null) &&
+ (item.event.execute === eventData.phase || item.event.execute === '*' || item.event.phase === '*'))
+ {
+ eventData = $.extend({}, item.event, eventData);
+ // check handler arguments
+ args = [];
+ tmp = RegExp(/\((.*?)\)/).exec(item.handler);
+ if (tmp) args = tmp[1].split(/\s*,\s*/);
+ if (args.length === 2) {
+ item.handler.call(this, eventData.target, eventData); // old way for back compatibility
+ } else {
+ item.handler.call(this, eventData); // new way
+ }
+ if (eventData.isStopped === true || eventData.stop === true) return eventData; // back compatibility eventData.stop === true
+ }
+ }
+ // main object events
+ var funName = 'on' + eventData.type.substr(0,1).toUpperCase() + eventData.type.substr(1);
+ if (eventData.phase === 'before' && typeof this[funName] === 'function') {
+ fun = this[funName];
+ // check handler arguments
+ args = [];
+ tmp = RegExp(/\((.*?)\)/).exec(fun);
+ if (tmp) args = tmp[1].split(/\s*,\s*/);
+ if (args.length === 2) {
+ fun.call(this, eventData.target, eventData); // old way for back compatibility
+ } else {
+ fun.call(this, eventData); // new way
+ }
+ if (eventData.isStopped === true || eventData.stop === true) return eventData; // back compatibility eventData.stop === true
+ }
+ // item object events
+ if (eventData.object != null && eventData.phase === 'before' &&
+ typeof eventData.object[funName] === 'function')
+ {
+ fun = eventData.object[funName];
+ // check handler arguments
+ args = [];
+ tmp = RegExp(/\((.*?)\)/).exec(fun);
+ if (tmp) args = tmp[1].split(/\s*,\s*/);
+ if (args.length === 2) {
+ fun.call(this, eventData.target, eventData); // old way for back compatibility
+ } else {
+ fun.call(this, eventData); // new way
+ }
+ if (eventData.isStopped === true || eventData.stop === true) return eventData;
+ }
+ // execute onComplete
+ if (eventData.phase === 'after' && typeof eventData.onComplete === 'function') eventData.onComplete.call(this, eventData);
+
+ return eventData;
+ }
+};
+
+/***********************************************************
+* Common Keyboard Handler. Supported in
+* - grid
+* - sidebar
+* - popup
+*
+*********************************************************/
+
+w2utils.keyboard = (function (obj) {
+ // private scope
+ var w2ui_name = null;
+
+ obj.active = active;
+ obj.clear = clear;
+
+ init();
+ return obj;
+
+ function init() {
+ $(document).on('keydown', keydown);
+ $(document).on('mousedown', mousedown);
+ }
+
+ function keydown (event) {
+ var tag = event.target.tagName;
+ if ($.inArray(tag, ['INPUT', 'SELECT', 'TEXTAREA']) !== -1) return;
+ if ($(event.target).prop('contenteditable') === 'true') return;
+ if (!w2ui_name) return;
+ // pass to appropriate widget
+ if (w2ui[w2ui_name] && typeof w2ui[w2ui_name].keydown === 'function') {
+ w2ui[w2ui_name].keydown.call(w2ui[w2ui_name], event);
+ }
+ }
+
+ function mousedown (event) {
+ var tag = event.target.tagName;
+ var obj = $(event.target).parents('.w2ui-reset');
+ if (obj.length > 0) {
+ var name = obj.attr('name');
+ if (w2ui[name] && w2ui[name].keyboard) w2ui_name = name;
+ }
+ }
+
+ function active (new_w2ui_name) {
+ if (typeof new_w2ui_name !== 'undefined') w2ui_name = new_w2ui_name;
+ return w2ui_name;
+ }
+
+ function clear () {
+ w2ui_name = null;
+ }
+
+})({});
+
+/***********************************************************
+* Commonly used plugins
+* --- used primarily in grid and form
+*
+*********************************************************/
+
+(function () {
+
+ $.fn.w2render = function (name) {
+ if ($(this).length > 0) {
+ if (typeof name === 'string' && w2ui[name]) w2ui[name].render($(this)[0]);
+ if (typeof name === 'object') name.render($(this)[0]);
+ }
+ };
+
+ $.fn.w2destroy = function (name) {
+ if (!name && this.length > 0) name = this.attr('name');
+ if (typeof name === 'string' && w2ui[name]) w2ui[name].destroy();
+ if (typeof name === 'object') name.destroy();
+ };
+
+ $.fn.w2marker = function (str) {
+ if (str === '' || str == null) { // remove marker
+ return $(this).each(function (index, el) {
+ el.innerHTML = el.innerHTML.replace(/\<span class=\"w2ui\-marker\"\>(.*)\<\/span\>/ig, '$1'); // unmark
+ });
+ } else { // add marker
+ return $(this).each(function (index, el) {
+ if (typeof str === 'string') str = [str];
+ el.innerHTML = el.innerHTML.replace(/\<span class=\"w2ui\-marker\"\>(.*)\<\/span\>/ig, '$1'); // unmark
+ for (var s in str) {
+ var tmp = str[s];
+ if (typeof tmp !== 'string') tmp = String(tmp);
+ // escape regex special chars
+ tmp = tmp.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&").replace(/&/g, '&amp;').replace(/</g, '&gt;').replace(/>/g, '&lt;');
+ var regex = new RegExp(tmp + '(?!([^<]+)?>)', "gi"); // only outside tags
+ el.innerHTML = el.innerHTML.replace(regex, replaceValue);
+ }
+ function replaceValue(matched) { // mark new
+ return '<span class="w2ui-marker">' + matched + '</span>';
+ }
+ });
+ }
+ };
+
+ // -- w2tag - appears on the right side from element, there can be multiple on screen at a time
+
+ $.fn.w2tag = function (text, options) {
+ if (!$.isPlainObject(options)) options = {};
+ if (!$.isPlainObject(options.css)) options.css = {};
+ if (typeof options['class'] === 'undefined') options['class'] = '';
+ // remove all tags
+ if ($(this).length === 0) {
+ $('.w2ui-tag').each(function (index, elem) {
+ var opt = $(elem).data('options');
+ if (opt == null) opt = {};
+ $($(elem).data('taged-el')).removeClass( opt['class'] );
+ clearInterval($(elem).data('timer'));
+ $(elem).remove();
+ });
+ return;
+ }
+ return $(this).each(function (index, el) {
+ // show or hide tag
+ var tagOrigID = el.id;
+ var tagID = w2utils.escapeId(el.id);
+ if (text === '' || text == null) {
+ $('#w2ui-tag-'+tagID).css('opacity', 0);
+ setTimeout(function () {
+ // remmove element
+ clearInterval($('#w2ui-tag-'+tagID).data('timer'));
+ $('#w2ui-tag-'+tagID).remove();
+ }, 300);
+ } else {
+ // remove elements
+ clearInterval($('#w2ui-tag-'+tagID).data('timer'));
+ $('#w2ui-tag-'+tagID).remove();
+ // insert
+ $('body').append(
+ '<div id="w2ui-tag-'+ tagOrigID +'" class="w2ui-tag '+ ($(el).parents('.w2ui-popup').length > 0 ? 'w2ui-tag-popup' : '') +
+ '" style=""></div>');
+
+ var timer = setInterval(function () {
+ // monitor if destroyed
+ if ($(el).length === 0 || ($(el).offset().left === 0 && $(el).offset().top === 0)) {
+ clearInterval($('#w2ui-tag-'+tagID).data('timer'));
+ tmp_hide();
+ return;
+ }
+ // monitor if moved
+ if ($('#w2ui-tag-'+tagID).data('position') !== ($(el).offset().left + el.offsetWidth) + 'x' + $(el).offset().top) {
+ $('#w2ui-tag-'+tagID).css({
+ '-webkit-transition' : '.2s',
+ '-moz-transition' : '.2s',
+ '-ms-transition' : '.2s',
+ '-o-transition' : '.2s',
+ left: ($(el).offset().left + el.offsetWidth) + 'px',
+ top: $(el).offset().top + 'px'
+ }).data('position', ($(el).offset().left + el.offsetWidth) + 'x' + $(el).offset().top);
+ }
+ }, 100);
+ setTimeout(function () {
+ if (!$(el).offset()) return;
+ $('#w2ui-tag-'+tagID).css({
+ opacity: '1',
+ left: ($(el).offset().left + el.offsetWidth) + 'px',
+ top: $(el).offset().top + 'px'
+ }).html('<div style="margin-top: -2px 0px 0px -2px; white-space: nowrap;"> <div class="w2ui-tag-body">'+ text +'</div> </div>')
+ .data('text', text)
+ .data('taged-el', el)
+ .data('options', options)
+ .data('position', ($(el).offset().left + el.offsetWidth) + 'x' + $(el).offset().top)
+ .data('timer', timer);
+ $(el).off('keypress', tmp_hide).on('keypress', tmp_hide).off('change', tmp_hide).on('change', tmp_hide)
+ .css(options.css).addClass(options['class']);
+ if (typeof options.onShow === 'function') options.onShow();
+ }, 1);
+ var originalCSS = '';
+ if ($(el).length > 0) originalCSS = $(el)[0].style.cssText;
+ // bind event to hide it
+ function tmp_hide() {
+ $tag = $('#w2ui-tag-'+tagID);
+ if ($tag.length <= 0) return;
+ clearInterval($tag.data('timer'));
+ $tag.remove();
+ $(el).off('keypress', tmp_hide).removeClass(options['class']);
+ if ($(el).length > 0) $(el)[0].style.cssText = originalCSS;
+ if (typeof options.onHide === 'function') options.onHide();
+ }
+ }
+ });
+ };
+
+ // w2overlay - appears under the element, there can be only one at a time
+
+ $.fn.w2overlay = function (html, options) {
+ var obj = this;
+ var name = '';
+ var defaults = {
+ name : null, // it not null, then allows multiple concurent overlays
+ html : '', // html text to display
+ align : 'none', // can be none, left, right, both
+ left : 0, // offset left
+ top : 0, // offset top
+ tipLeft : 30, // tip offset left
+ width : 0, // fixed width
+ height : 0, // fixed height
+ maxWidth : null, // max width if any
+ maxHeight : null, // max height if any
+ style : '', // additional style for main div
+ 'class' : '', // additional class name for main div
+ onShow : null, // event on show
+ onHide : null, // event on hide
+ openAbove : false, // show abover control
+ tmp : {}
+ };
+ if (arguments.length == 1) {
+ if (typeof html == 'object') {
+ options = html;
+ } else {
+ options = { html: html };
+ }
+ }
+ if (arguments.length == 2) options.html = html;
+ if (!$.isPlainObject(options)) options = {};
+ options = $.extend({}, defaults, options);
+ if (options.name) name = '-' + options.name;
+ // if empty then hide
+ var tmp_hide;
+ if (this.length === 0 || options.html === '' || options.html == null) {
+ if ($('#w2ui-overlay'+ name).length > 0) {
+ tmp_hide = $('#w2ui-overlay'+ name)[0].hide;
+ if (typeof tmp_hide === 'function') tmp_hide();
+ } else {
+ $('#w2ui-overlay'+ name).remove();
+ }
+ return $(this);
+ }
+ if ($('#w2ui-overlay'+ name).length > 0) {
+ tmp_hide = $('#w2ui-overlay'+ name)[0].hide;
+ $(document).off('click', tmp_hide);
+ if (typeof tmp_hide === 'function') tmp_hide();
+ }
+ $('body').append(
+ '<div id="w2ui-overlay'+ name +'" style="display: none"'+
+ ' class="w2ui-reset w2ui-overlay '+ ($(this).parents('.w2ui-popup, .w2ui-overlay-popup').length > 0 ? 'w2ui-overlay-popup' : '') +'">'+
+ ' <style></style>'+
+ ' <div style="'+ options.style +'" class="'+ options['class'] +'"></div>'+
+ '</div>'
+ );
+ // init
+ var div1 = $('#w2ui-overlay'+ name);
+ var div2 = div1.find(' > div');
+ div2.html(options.html);
+ // pick bg color of first div
+ var bc = div2.css('background-color');
+ if (bc != null && bc !== 'rgba(0, 0, 0, 0)' && bc !== 'transparent') div1.css('background-color', bc);
+
+ div1.data('element', obj.length > 0 ? obj[0] : null)
+ .data('options', options)
+ .data('position', $(obj).offset().left + 'x' + $(obj).offset().top)
+ .fadeIn('fast').on('mousedown', function (event) {
+ $('#w2ui-overlay'+ name).data('keepOpen', true);
+ if (['INPUT', 'TEXTAREA', 'SELECT'].indexOf(event.target.tagName) === -1) event.preventDefault();
+ });
+ div1[0].hide = hide;
+ div1[0].resize = resize;
+
+ // need time to display
+ resize();
+ setTimeout(function () {
+ resize();
+ $(document).off('click', hide).on('click', hide);
+ if (typeof options.onShow === 'function') options.onShow();
+ }, 10);
+
+ monitor();
+ return $(this);
+
+ // monitor position
+ function monitor() {
+ var tmp = $('#w2ui-overlay'+ name);
+ if (tmp.data('element') !== obj[0]) return; // it if it different overlay
+ if (tmp.length === 0) return;
+ var pos = $(obj).offset().left + 'x' + $(obj).offset().top;
+ if (tmp.data('position') !== pos) {
+ hide();
+ } else {
+ setTimeout(monitor, 250);
+ }
+ }
+
+ // click anywhere else hides the drop down
+ function hide () {
+ var div1 = $('#w2ui-overlay'+ name);
+ if (div1.data('keepOpen') === true) {
+ div1.removeData('keepOpen');
+ return;
+ }
+ var result;
+ if (typeof options.onHide === 'function') result = options.onHide();
+ if (result === false) return;
+ div1.remove();
+ $(document).off('click', hide);
+ clearInterval(div1.data('timer'));
+ }
+
+ function resize () {
+ var div1 = $('#w2ui-overlay'+ name);
+ var div2 = div1.find(' > div');
+ // if goes over the screen, limit height and width
+ if (div1.length > 0) {
+ div2.height('auto').width('auto');
+ // width/height
+ var overflowX = false;
+ var overflowY = false;
+ var h = div2.height();
+ var w = div2.width();
+ if (options.width && options.width < w) w = options.width;
+ if (w < 30) w = 30;
+ // if content of specific height
+ if (options.tmp.contentHeight) {
+ h = options.tmp.contentHeight;
+ div2.height(h);
+ setTimeout(function () {
+ if (div2.height() > div2.find('div.menu > table').height()) {
+ div2.find('div.menu').css('overflow-y', 'hidden');
+ }
+ }, 1);
+ setTimeout(function () { div2.find('div.menu').css('overflow-y', 'auto'); }, 10);
+ }
+ if (options.tmp.contentWidth) {
+ w = options.tmp.contentWidth;
+ div2.width(w);
+ setTimeout(function () {
+ if (div2.width() > div2.find('div.menu > table').width()) {
+ div2.find('div.menu').css('overflow-x', 'hidden');
+ }
+ }, 1);
+ setTimeout(function () { div2.find('div.menu').css('overflow-y', 'auto'); }, 10);
+ }
+ // alignment
+ switch (options.align) {
+ case 'both':
+ options.left = 17;
+ if (options.width === 0) options.width = w2utils.getSize($(obj), 'width');
+ break;
+ case 'left':
+ options.left = 17;
+ break;
+ case 'right':
+ options.tipLeft = w - 45;
+ options.left = w2utils.getSize($(obj), 'width') - w + 10;
+ break;
+ }
+ // adjust position
+ var tmp = (w - 17) / 2;
+ var boxLeft = options.left;
+ var boxWidth = options.width;
+ var tipLeft = options.tipLeft;
+ if (w === 30 && !boxWidth) boxWidth = 30; else boxWidth = (options.width ? options.width : 'auto');
+ if (tmp < 25) {
+ boxLeft = 25 - tmp;
+ tipLeft = Math.floor(tmp);
+ }
+ // Y coord
+ div1.css({
+ top : (obj.offset().top + w2utils.getSize(obj, 'height') + options.top + 7) + 'px',
+ left : ((obj.offset().left > 25 ? obj.offset().left : 25) + boxLeft) + 'px',
+ 'min-width' : boxWidth,
+ 'min-height': (options.height ? options.height : 'auto')
+ });
+ // $(window).height() - has a problem in FF20
+ var maxHeight = window.innerHeight + $(document).scrollTop() - div2.offset().top - 7;
+ var maxWidth = window.innerWidth + $(document).scrollLeft() - div2.offset().left - 7;
+ if ((maxHeight > -50 && maxHeight < 210) || options.openAbove === true) {
+ // show on top
+ maxHeight = div2.offset().top - $(document).scrollTop() - 7;
+ if (options.maxHeight && maxHeight > options.maxHeight) maxHeight = options.maxHeight;
+ if (h > maxHeight) {
+ overflowY = true;
+ div2.height(maxHeight).width(w).css({ 'overflow-y': 'auto' });
+ h = maxHeight;
+ }
+ div1.css('top', ($(obj).offset().top - h - 24 + options.top) + 'px');
+ div1.find('>style').html(
+ '#w2ui-overlay'+ name +':before { display: none; margin-left: '+ parseInt(tipLeft) +'px; }'+
+ '#w2ui-overlay'+ name +':after { display: block; margin-left: '+ parseInt(tipLeft) +'px; }'
+ );
+ } else {
+ // show under
+ if (options.maxHeight && maxHeight > options.maxHeight) maxHeight = options.maxHeight;
+ if (h > maxHeight) {
+ overflowY = true;
+ div2.height(maxHeight).width(w).css({ 'overflow-y': 'auto' });
+ }
+ div1.find('>style').html(
+ '#w2ui-overlay'+ name +':before { display: block; margin-left: '+ parseInt(tipLeft) +'px; }'+
+ '#w2ui-overlay'+ name +':after { display: none; margin-left: '+ parseInt(tipLeft) +'px; }'
+ );
+ }
+ // check width
+ w = div2.width();
+ maxWidth = window.innerWidth + $(document).scrollLeft() - div2.offset().left - 7;
+ if (options.maxWidth && maxWidth > options.maxWidth) maxWidth = options.maxWidth;
+ if (w > maxWidth && options.align !== 'both') {
+ options.align = 'right';
+ setTimeout(function () { resize(); }, 1);
+ }
+ // check scroll bar
+ if (overflowY && overflowX) div2.width(w + w2utils.scrollBarSize() + 2);
+ }
+ }
+ };
+
+ $.fn.w2menu = function (menu, options) {
+ /*
+ ITEM STRUCTURE
+ item : {
+ id : null,
+ text : '',
+ style : '',
+ img : '',
+ icon : '',
+ count : '',
+ hidden : false,
+ disabled : false
+ ...
+ }
+ */
+ var defaults = {
+ index : null, // current selected
+ items : [],
+ render : null,
+ msgNoItems : 'No items',
+ onSelect : null,
+ tmp : {}
+ };
+ var obj = this;
+ var name = '';
+ if (menu === 'refresh') {
+ // if not show - call blur
+ if ($('#w2ui-overlay'+ name).length > 0) {
+ options = $.extend($.fn.w2menuOptions, options);
+ var scrTop = $('#w2ui-overlay'+ name +' div.menu').scrollTop();
+ $('#w2ui-overlay'+ name +' div.menu').html(getMenuHTML());
+ $('#w2ui-overlay'+ name +' div.menu').scrollTop(scrTop);
+ mresize();
+ } else {
+ $(this).w2menu(options);
+ }
+ } else {
+ if (arguments.length === 1) options = menu; else options.items = menu;
+ if (typeof options !== 'object') options = {};
+ options = $.extend({}, defaults, options);
+ $.fn.w2menuOptions = options;
+ if (options.name) name = '-' + options.name;
+ if (typeof options.select === 'function' && typeof options.onSelect !== 'function') options.onSelect = options.select;
+ if (typeof options.onRender === 'function' && typeof options.render !== 'function') options.render = options.onRender;
+ // since only one overlay can exist at a time
+ $.fn.w2menuHandler = function (event, index) {
+ if (typeof options.onSelect === 'function') {
+ // need time so that menu first hides
+ setTimeout(function () {
+ options.onSelect({
+ index : index,
+ item : options.items[index],
+ originalEvent: event
+ });
+ }, 10);
+ }
+ setTimeout(function () { $(document).click(); }, 50);
+ };
+ var html = '';
+ if (options.search) {
+ html +=
+ '<div style="position: absolute; top: 0px; height: 40px; left: 0px; right: 0px; border-bottom: 1px solid silver; background-color: #ECECEC; padding: 8px 5px;">'+
+ ' <div class="w2ui-icon icon-search" style="position: absolute; margin-top: 4px; margin-left: 6px; width: 11px; background-position: left !important;"></div>'+
+ ' <input id="menu-search" type="text" style="width: 100%; outline: none; padding-left: 20px;" onclick="event.stopPropagation();">'+
+ '</div>';
+ options.style += ';background-color: #ECECEC';
+ options.index = 0;
+ for (var i in options.items) options.items[i].hidden = false;
+ }
+ html += '<div class="menu" style="position: absolute; top: '+ (options.search ? 40 : 0) + 'px; bottom: 0px; width: 100%; overflow: auto;">' +
+ getMenuHTML() +
+ '</div>';
+ var ret = $(this).w2overlay(html, options);
+ setTimeout(function () {
+ $('#w2ui-overlay'+ name +' #menu-search')
+ .on('keyup', change)
+ .on('keydown', function (event) {
+ // cancel tab key
+ if (event.keyCode === 9) { event.stopPropagation(); event.preventDefault(); }
+ });
+ if (options.search) {
+ if (['text', 'password'].indexOf($(obj)[0].type) != -1 || $(obj)[0].tagName == 'texarea') return;
+ $('#w2ui-overlay'+ name +' #menu-search').focus();
+ }
+ }, 200);
+ mresize();
+ return ret;
+ }
+
+ function mresize() {
+ setTimeout(function () {
+ // show selected
+ $('#w2ui-overlay'+ name +' tr.w2ui-selected').removeClass('w2ui-selected');
+ var cur = $('#w2ui-overlay'+ name +' tr[index='+ options.index +']');
+ var scrTop = $('#w2ui-overlay'+ name +' div.menu').scrollTop();
+ cur.addClass('w2ui-selected');
+ if (options.tmp) options.tmp.contentHeight = $('#w2ui-overlay'+ name +' table').height() + (options.search ? 50 : 10);
+ if (options.tmp) options.tmp.contentWidth = $('#w2ui-overlay'+ name +' table').width();
+ if ($('#w2ui-overlay'+ name).length > 0) $('#w2ui-overlay'+ name)[0].resize();
+ // scroll into view
+ if (cur.length > 0) {
+ var top = cur[0].offsetTop - 5; // 5 is margin top
+ var el = $('#w2ui-overlay'+ name +' div.menu');
+ var height = el.height();
+ $('#w2ui-overlay'+ name +' div.menu').scrollTop(scrTop);
+ if (top < scrTop || top + cur.height() > scrTop + height) {
+ $('#w2ui-overlay'+ name +' div.menu').animate({ 'scrollTop': top - (height - cur.height() * 2) / 2 }, 200, 'linear');
+ }
+ }
+ }, 1);
+ }
+
+ function change(event) {
+ var search = this.value;
+ var key = event.keyCode;
+ var cancel = false;
+ switch (key) {
+ case 13: // enter
+ $('#w2ui-overlay'+ name).remove();
+ $.fn.w2menuHandler(event, options.index);
+ break;
+ case 9: // tab
+ case 27: // escape
+ $('#w2ui-overlay'+ name).remove();
+ $.fn.w2menuHandler(event, -1);
+ break;
+ case 38: // up
+ options.index = w2utils.isInt(options.index) ? parseInt(options.index) : 0;
+ options.index--;
+ while (options.index > 0 && options.items[options.index].hidden) options.index--;
+ if (options.index === 0 && options.items[options.index].hidden) {
+ while (options.items[options.index] && options.items[options.index].hidden) options.index++;
+ }
+ if (options.index < 0) options.index = 0;
+ cancel = true;
+ break;
+ case 40: // down
+ options.index = w2utils.isInt(options.index) ? parseInt(options.index) : 0;
+ options.index++;
+ while (options.index < options.items.length-1 && options.items[options.index].hidden) options.index++;
+ if (options.index === options.items.length-1 && options.items[options.index].hidden) {
+ while (options.items[options.index] && options.items[options.index].hidden) options.index--;
+ }
+ if (options.index >= options.items.length) options.index = options.items.length - 1;
+ cancel = true;
+ break;
+ }
+ // filter
+ if (!cancel) {
+ var shown = 0;
+ for (var i in options.items) {
+ var item = options.items[i];
+ var prefix = '';
+ var suffix = '';
+ if (['is', 'begins with'].indexOf(options.match) !== -1) prefix = '^';
+ if (['is', 'ends with'].indexOf(options.match) !== -1) suffix = '$';
+ try {
+ var re = new RegExp(prefix + search + suffix, 'i');
+ if (re.test(item.text) || item.text === '...') item.hidden = false; else item.hidden = true;
+ } catch (e) {}
+ // do not show selected items
+ if (obj.type === 'enum' && $.inArray(item.id, ids) !== -1) item.hidden = true;
+ if (item.hidden !== true) shown++;
+ }
+ options.index = 0;
+ while (options.index < options.items.length-1 && options.items[options.index].hidden) options.index++;
+ if (shown <= 0) options.index = -1;
+ }
+ $(obj).w2menu('refresh', options);
+ mresize();
+ }
+
+ function getMenuHTML () {
+ if (options.spinner) {
+ return '<table class="w2ui-drop-menu"><tr><td style="padding: 5px 10px 10px 10px; text-align: center">'+
+ ' <div class="w2ui-spinner" style="width: 18px; height: 18px; position: relative; top: 5px;"></div> '+
+ ' <div style="display: inline-block; padding: 3px; color: #999;"> Loading...</div>'+
+ '</td></tr></table>';
+ }
+ var count = 0;
+ var menu_html = '<table cellspacing="0" cellpadding="0" class="w2ui-drop-menu">';
+ var img = null, icon = null;
+ for (var f = 0; f < options.items.length; f++) {
+ var mitem = options.items[f];
+ if (typeof mitem === 'string') {
+ mitem = { id: mitem, text: mitem };
+ } else {
+ if (mitem.text != null && mitem.id == null) mitem.id = mitem.text;
+ if (mitem.text == null && mitem.id != null) mitem.text = mitem.id;
+ if (mitem.caption != null) mitem.text = mitem.caption;
+ img = mitem.img;
+ icon = mitem.icon;
+ if (img == null) img = null;
+ if (icon == null) icon = null;
+ }
+ if (mitem.hidden !== true) {
+ var imgd = '';
+ var txt = mitem.text;
+ if (typeof options.render === 'function') txt = options.render(mitem, options);
+ if (img) imgd = '<td class="menu-icon"><div class="w2ui-tb-image w2ui-icon '+ img +'"></div></td>';
+ if (icon) imgd = '<td class="menu-icon" align="center"><span class="w2ui-icon '+ icon +'"></span></td>';
+ // render only if non-empty
+ if (typeof txt !== 'undefined' && txt !== '' && !(/^-+$/.test(txt))) {
+ var bg = (count % 2 === 0 ? 'w2ui-item-even' : 'w2ui-item-odd');
+ if (options.altRows !== true) bg = '';
+ var colspan = 1;
+ if (imgd == '') colspan++;
+ if (mitem.count == null) colspan++;
+ menu_html +=
+ '<tr index="'+ f + '" style="'+ (mitem.style ? mitem.style : '') +'" '+
+ ' class="'+ bg +' '+ (options.index === f ? 'w2ui-selected' : '') + ' ' + (mitem.disabled === true ? 'w2ui-disabled' : '') +'"'+
+ ' onmousedown="$(this).parent().find(\'tr\').removeClass(\'w2ui-selected\'); $(this).addClass(\'w2ui-selected\');"'+
+ ' onclick="event.stopPropagation(); '+
+ ' if ('+ (mitem.disabled === true ? 'true' : 'false') + ') return;'+
+ ' $(\'#w2ui-overlay'+ name +'\').remove(); '+
+ ' $.fn.w2menuHandler(event, \''+ f +'\');">'+
+ imgd +
+ ' <td class="menu-text" colspan="'+ colspan +'">'+ txt +'</td>'+
+ ' <td class="menu-count">'+ (mitem.count != null ? '<span>' + mitem.count + '</span>' : '') + '</td>' +
+ '</tr>';
+ count++;
+ } else {
+ // horizontal line
+ menu_html += '<tr><td colspan="2" style="padding: 6px; pointer-events: none"><div style="border-top: 1px solid silver;"></div></td></tr>';
+ }
+ }
+ options.items[f] = mitem;
+ }
+ if (count === 0) {
+ menu_html += '<tr><td style="padding: 13px; color: #999; text-align: center">'+ options.msgNoItems +'</div></td></tr>';
+ }
+ menu_html += "</table>";
+ return menu_html;
+ }
+ };
+})();
+
+
+/************************************************************************
+* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
+* - Following objects defined
+* - w2grid - grid widget
+* - $().w2grid - jQuery wrapper
+* - Dependencies: jQuery, w2utils, w2toolbar, w2fields, w2alert, w2confirm
+*
+* == NICE TO HAVE ==
+* - frozen columns
+* - add colspans
+* - allow this.total to be unknown (-1)
+* - column autosize based on largest content
+* - easy bubbles in the grid
+* - More than 2 layers of header groups
+* - reorder columns/records
+* - hidden searches could not be clearned by the user
+* - problem with .set() and arrays, array get extended too, but should be replaced
+* - move events into prototype
+* - add grid.focus()
+* - add showExtra, KickIn Infinite scroll when so many records
+* - after edit stay on the same record option
+* - allow render: function to be filters
+*
+************************************************************************/
+
+(function () {
+ var w2grid = function(options) {
+
+ // public properties
+ this.name = null;
+ this.box = null; // HTML element that hold this element
+ this.header = '';
+ this.url = '';
+ this.routeData = {}; // data for dynamic routes
+ this.columns = []; // { field, caption, size, attr, render, hidden, gridMinWidth, editable }
+ this.columnGroups = []; // { span: int, caption: 'string', master: true/false }
+ this.records = []; // { recid: int(requied), field1: 'value1', ... fieldN: 'valueN', style: 'string', editable: true/false, summary: true/false, changes: object }
+ this.summary = []; // arry of summary records, same structure as records array
+ this.searches = []; // { type, caption, field, inTag, outTag, hidden }
+ this.searchData = [];
+ this.sortData = [];
+ this.postData = {};
+ this.toolbar = {}; // if not empty object; then it is toolbar object
+
+ this.show = {
+ header : false,
+ toolbar : false,
+ footer : false,
+ columnHeaders : true,
+ lineNumbers : false,
+ expandColumn : false,
+ selectColumn : false,
+ emptyRecords : true,
+ toolbarReload : true,
+ toolbarColumns : true,
+ toolbarSearch : true,
+ toolbarAdd : false,
+ toolbarEdit : false,
+ toolbarDelete : false,
+ toolbarSave : false,
+ selectionBorder : true,
+ recordTitles : true,
+ skipRecords : true
+ };
+
+ this.autoLoad = true; // for infinite scroll
+ this.fixedBody = true; // if false; then grid grows with data
+ this.recordHeight = 24;
+ this.keyboard = true;
+ this.selectType = 'row'; // can be row|cell
+ this.multiSearch = true;
+ this.multiSelect = true;
+ this.multiSort = true;
+ this.reorderColumns = false;
+ this.reorderRows = false;
+ this.markSearch = true;
+
+ this.total = 0; // server total
+ this.limit = 100;
+ this.offset = 0; // how many records to skip (for infinite scroll) when pulling from server
+ this.style = '';
+ this.ranges = [];
+ this.menu = [];
+ this.method = null; // if defined, then overwrited ajax method
+ this.recid = null;
+ this.parser = null;
+
+ // events
+ this.onAdd = null;
+ this.onEdit = null;
+ this.onRequest = null; // called on any server event
+ this.onLoad = null;
+ this.onDelete = null;
+ this.onDeleted = null;
+ this.onSubmit = null;
+ this.onSave = null;
+ this.onSelect = null;
+ this.onUnselect = null;
+ this.onClick = null;
+ this.onDblClick = null;
+ this.onContextMenu = null;
+ this.onMenuClick = null; // when context menu item selected
+ this.onColumnClick = null;
+ this.onColumnResize = null;
+ this.onSort = null;
+ this.onSearch = null;
+ this.onChange = null; // called when editable record is changed
+ this.onRestore = null; // called when editable record is restored
+ this.onExpand = null;
+ this.onCollapse = null;
+ this.onError = null;
+ this.onKeydown = null;
+ this.onToolbar = null; // all events from toolbar
+ this.onColumnOnOff = null;
+ this.onCopy = null;
+ this.onPaste = null;
+ this.onSelectionExtend = null;
+ this.onEditField = null;
+ this.onRender = null;
+ this.onRefresh = null;
+ this.onReload = null;
+ this.onResize = null;
+ this.onDestroy = null;
+ this.onStateSave = null;
+ this.onStateRestore = null;
+
+ // internal
+ this.last = {
+ field : 'all',
+ caption : w2utils.lang('All Fields'),
+ logic : 'OR',
+ search : '',
+ searchIds : [],
+ selection : {
+ indexes : [],
+ columns : {}
+ },
+ multi : false,
+ scrollTop : 0,
+ scrollLeft : 0,
+ sortData : null,
+ sortCount : 0,
+ xhr : null,
+ range_start : null,
+ range_end : null,
+ sel_ind : null,
+ sel_col : null,
+ sel_type : null,
+ edit_col : null
+ };
+
+ $.extend(true, this, w2obj.grid, options);
+ };
+
+ // ====================================================
+ // -- Registers as a jQuery plugin
+
+ $.fn.w2grid = function(method) {
+ if (typeof method === 'object' || !method ) {
+ // check name parameter
+ if (!w2utils.checkName(method, 'w2grid')) return;
+ // remember items
+ var columns = method.columns;
+ var columnGroups = method.columnGroups;
+ var records = method.records;
+ var searches = method.searches;
+ var searchData = method.searchData;
+ var sortData = method.sortData;
+ var postData = method.postData;
+ var toolbar = method.toolbar;
+ // extend items
+ var object = new w2grid(method);
+ $.extend(object, { postData: {}, records: [], columns: [], searches: [], toolbar: {}, sortData: [], searchData: [], handlers: [] });
+ if (object.onExpand != null) object.show.expandColumn = true;
+ $.extend(true, object.toolbar, toolbar);
+ // reassign variables
+ for (var p in columns) object.columns[p] = $.extend(true, {}, columns[p]);
+ for (var p in columnGroups) object.columnGroups[p] = $.extend(true, {}, columnGroups[p]);
+ for (var p in searches) object.searches[p] = $.extend(true, {}, searches[p]);
+ for (var p in searchData) object.searchData[p] = $.extend(true, {}, searchData[p]);
+ for (var p in sortData) object.sortData[p] = $.extend(true, {}, sortData[p]);
+ object.postData = $.extend(true, {}, postData);
+
+ // check if there are records without recid
+ for (var r in records) {
+ if (records[r].recid == null || typeof records[r].recid == 'undefined') {
+ console.log('ERROR: Cannot add records without recid. (obj: '+ object.name +')');
+ return;
+ }
+ object.records[r] = $.extend(true, {}, records[r]);
+ }
+ // add searches
+ for (var c in object.columns) {
+ var col = object.columns[c];
+ if (typeof col.searchable == 'undefined' || object.getSearch(col.field) != null) continue;
+ var stype = col.searchable;
+ var attr = '';
+ if (col.searchable === true) { stype = 'text'; attr = 'size="20"'; }
+ object.addSearch({ field: col.field, caption: col.caption, type: stype, attr: attr });
+ }
+ // init toolbar
+ object.initToolbar();
+ // render if necessary
+ if ($(this).length !== 0) {
+ object.render($(this)[0]);
+ }
+ // register new object
+ w2ui[object.name] = object;
+ return object;
+
+ } else if (w2ui[$(this).attr('name')]) {
+ var obj = w2ui[$(this).attr('name')];
+ obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
+ return this;
+ } else {
+ console.log('ERROR: Method ' + method + ' does not exist on jQuery.w2grid');
+ }
+ }
+
+ // ====================================================
+ // -- Implementation of core functionality
+
+ w2grid.prototype = {
+ // ----
+ // properties that need to be in prototype
+
+ msgDelete : 'Are you sure you want to delete selected records?',
+ msgNotJSON : 'Returned data is not in valid JSON format.',
+ msgAJAXerror : 'AJAX error. See console for more details.',
+ msgRefresh : 'Refreshing...',
+
+ // for easy button overwrite
+ buttons: {
+ 'reload' : { type: 'button', id: 'w2ui-reload', icon: 'w2ui-icon-reload', hint: 'Reload data in the list' },
+ 'columns' : { type: 'drop', id: 'w2ui-column-on-off', icon: 'w2ui-icon-columns', hint: 'Show/hide columns', arrow: false, html: '' },
+ 'search' : { type: 'html', id: 'w2ui-search',
+ html: '<div class="w2ui-icon icon-search-down w2ui-search-down" title="'+ 'Select Search Field' +'" '+
+ 'onclick="var obj = w2ui[$(this).parents(\'div.w2ui-grid\').attr(\'name\')]; obj.searchShowFields();"></div>'
+ },
+ 'search-go': { type: 'check', id: 'w2ui-search-advanced', caption: 'Search...', hint: 'Open Search Fields' },
+ 'add' : { type: 'button', id: 'w2ui-add', caption: 'Add New', hint: 'Add new record', icon: 'w2ui-icon-plus' },
+ 'edit' : { type: 'button', id: 'w2ui-edit', caption: 'Edit', hint: 'Edit selected record', icon: 'w2ui-icon-pencil', disabled: true },
+ 'delete' : { type: 'button', id: 'w2ui-delete', caption: 'Delete', hint: 'Delete selected records', icon: 'w2ui-icon-cross', disabled: true },
+ 'save' : { type: 'button', id: 'w2ui-save', caption: 'Save', hint: 'Save changed records', icon: 'w2ui-icon-check' }
+ },
+
+ add: function (record) {
+ if (!$.isArray(record)) record = [record];
+ var added = 0;
+ for (var o in record) {
+ if (!this.recid && typeof record[o].recid == 'undefined') record[o].recid = record[o][this.recid];
+ if (record[o].recid == null || typeof record[o].recid == 'undefined') {
+ console.log('ERROR: Cannot add record without recid. (obj: '+ this.name +')');
+ continue;
+ }
+ this.records.push(record[o]);
+ added++;
+ }
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (!url) {
+ this.total = this.records.length;
+ this.localSort();
+ this.localSearch();
+ }
+ this.refresh(); // ?? should it be reload?
+ return added;
+ },
+
+ find: function (obj, returnIndex) {
+ if (typeof obj == 'undefined' || obj == null) obj = {};
+ var recs = [];
+ var hasDots = false;
+ // check if property is nested - needed for speed
+ for (var o in obj) if (String(o).indexOf('.') != -1) hasDots = true;
+ // look for an item
+ for (var i = 0; i < this.records.length; i++) {
+ var match = true;
+ for (var o in obj) {
+ var val = this.records[i][o];
+ if (hasDots && String(o).indexOf('.') != -1) val = this.parseField(this.records[i], o);
+ if (obj[o] != val) match = false;
+ }
+ if (match && returnIndex !== true) recs.push(this.records[i].recid);
+ if (match && returnIndex === true) recs.push(i);
+ }
+ return recs;
+ },
+
+ set: function (recid, record, noRefresh) { // does not delete existing, but overrides on top of it
+ if (typeof recid == 'object') {
+ noRefresh = record;
+ record = recid;
+ recid = null;
+ }
+ // update all records
+ if (recid == null) {
+ for (var r in this.records) {
+ $.extend(true, this.records[r], record); // recid is the whole record
+ }
+ if (noRefresh !== true) this.refresh();
+ } else { // find record to update
+ var ind = this.get(recid, true);
+ if (ind == null) return false;
+ $.extend(true, this.records[ind], record);
+ if (noRefresh !== true) this.refreshRow(recid); // refresh only that record
+ }
+ return true;
+ },
+
+ get: function (recid, returnIndex) {
+ for (var i = 0; i < this.records.length; i++) {
+ if (this.records[i].recid == recid) {
+ if (returnIndex === true) return i; else return this.records[i];
+ }
+ }
+ return null;
+ },
+
+ remove: function () {
+ var removed = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ for (var r = this.records.length-1; r >= 0; r--) {
+ if (this.records[r].recid == arguments[a]) { this.records.splice(r, 1); removed++; }
+ }
+ }
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (!url) {
+ this.localSort();
+ this.localSearch();
+ }
+ this.refresh();
+ return removed;
+ },
+
+ addColumn: function (before, columns) {
+ var added = 0;
+ if (arguments.length == 1) {
+ columns = before;
+ before = this.columns.length;
+ } else {
+ if (typeof before == 'string') before = this.getColumn(before, true);
+ if (before === null) before = this.columns.length;
+ }
+ if (!$.isArray(columns)) columns = [columns];
+ for (var o in columns) {
+ this.columns.splice(before, 0, columns[o]);
+ before++;
+ added++;
+ }
+ this.refresh();
+ return added;
+ },
+
+ removeColumn: function () {
+ var removed = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ for (var r = this.columns.length-1; r >= 0; r--) {
+ if (this.columns[r].field == arguments[a]) { this.columns.splice(r, 1); removed++; }
+ }
+ }
+ this.refresh();
+ return removed;
+ },
+
+ getColumn: function (field, returnIndex) {
+ for (var i = 0; i < this.columns.length; i++) {
+ if (this.columns[i].field == field) {
+ if (returnIndex === true) return i; else return this.columns[i];
+ }
+ }
+ return null;
+ },
+
+ toggleColumn: function () {
+ var effected = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ for (var r = this.columns.length-1; r >= 0; r--) {
+ var col = this.columns[r];
+ if (col.field == arguments[a]) {
+ col.hidden = !col.hidden;
+ effected++;
+ }
+ }
+ }
+ this.refresh();
+ return effected;
+ },
+
+ showColumn: function () {
+ var shown = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ for (var r = this.columns.length-1; r >= 0; r--) {
+ var col = this.columns[r];
+ if (col.gridMinWidth) delete col.gridMinWidth;
+ if (col.field == arguments[a] && col.hidden !== false) {
+ col.hidden = false;
+ shown++;
+ }
+ }
+ }
+ this.refresh();
+ return shown;
+ },
+
+ hideColumn: function () {
+ var hidden = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ for (var r = this.columns.length-1; r >= 0; r--) {
+ var col = this.columns[r];
+ if (col.field == arguments[a] && col.hidden !== true) {
+ col.hidden = true;
+ hidden++;
+ }
+ }
+ }
+ this.refresh();
+ return hidden;
+ },
+
+ addSearch: function (before, search) {
+ var added = 0;
+ if (arguments.length == 1) {
+ search = before;
+ before = this.searches.length;
+ } else {
+ if (typeof before == 'string') before = this.getSearch(before, true);
+ if (before === null) before = this.searches.length;
+ }
+ if (!$.isArray(search)) search = [search];
+ for (var o in search) {
+ this.searches.splice(before, 0, search[o]);
+ before++;
+ added++;
+ }
+ this.searchClose();
+ return added;
+ },
+
+ removeSearch: function () {
+ var removed = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ for (var r = this.searches.length-1; r >= 0; r--) {
+ if (this.searches[r].field == arguments[a]) { this.searches.splice(r, 1); removed++; }
+ }
+ }
+ this.searchClose();
+ return removed;
+ },
+
+ getSearch: function (field, returnIndex) {
+ for (var i = 0; i < this.searches.length; i++) {
+ if (this.searches[i].field == field) {
+ if (returnIndex === true) return i; else return this.searches[i];
+ }
+ }
+ return null;
+ },
+
+ toggleSearch: function () {
+ var effected = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ for (var r = this.searches.length-1; r >= 0; r--) {
+ if (this.searches[r].field == arguments[a]) {
+ this.searches[r].hidden = !this.searches[r].hidden;
+ effected++;
+ }
+ }
+ }
+ this.searchClose();
+ return effected;
+ },
+
+ showSearch: function () {
+ var shown = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ for (var r = this.searches.length-1; r >= 0; r--) {
+ if (this.searches[r].field == arguments[a] && this.searches[r].hidden !== false) {
+ this.searches[r].hidden = false;
+ shown++;
+ }
+ }
+ }
+ this.searchClose();
+ return shown;
+ },
+
+ hideSearch: function () {
+ var hidden = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ for (var r = this.searches.length-1; r >= 0; r--) {
+ if (this.searches[r].field == arguments[a] && this.searches[r].hidden !== true) {
+ this.searches[r].hidden = true;
+ hidden++;
+ }
+ }
+ }
+ this.searchClose();
+ return hidden;
+ },
+
+ getSearchData: function (field) {
+ for (var s in this.searchData) {
+ if (this.searchData[s].field == field) return this.searchData[s];
+ }
+ return null;
+ },
+
+ localSort: function (silent) {
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (url) {
+ console.log('ERROR: grid.localSort can only be used on local data source, grid.url should be empty.');
+ return;
+ }
+ if ($.isEmptyObject(this.sortData)) return;
+ var time = (new Date()).getTime();
+ var obj = this;
+ // process date fields
+ obj.prepareData();
+ // process sortData
+ for (var s in this.sortData) {
+ var column = this.getColumn(this.sortData[s].field);
+ if (!column) return;
+ if (column.render && ['date', 'age'].indexOf(column.render.split(':')[0]) != -1) {
+ this.sortData[s]['field_'] = column.field + '_';
+ }
+ if (column.render && ['time'].indexOf(column.render.split(':')[0]) != -1) {
+ this.sortData[s]['field_'] = column.field + '_';
+ }
+ }
+ // process sort
+ this.records.sort(function (a, b) {
+ var ret = 0;
+ for (var s in obj.sortData) {
+ var fld = obj.sortData[s].field;
+ if (obj.sortData[s].field_) fld = obj.sortData[s].field_;
+ var aa = a[fld];
+ var bb = b[fld];
+ if (String(fld).indexOf('.') != -1) {
+ aa = obj.parseField(a, fld);
+ bb = obj.parseField(b, fld);
+ }
+ if (typeof aa == 'string') aa = $.trim(aa.toLowerCase());
+ if (typeof bb == 'string') bb = $.trim(bb.toLowerCase());
+ if (aa > bb) ret = (obj.sortData[s].direction == 'asc' ? 1 : -1);
+ if (aa < bb) ret = (obj.sortData[s].direction == 'asc' ? -1 : 1);
+ if (typeof aa != 'object' && typeof bb == 'object') ret = -1;
+ if (typeof bb != 'object' && typeof aa == 'object') ret = 1;
+ if (aa == null && bb != null) ret = 1; // all nuls and undefined on bottom
+ if (aa != null && bb == null) ret = -1;
+ if (ret != 0) break;
+ }
+ return ret;
+ });
+ time = (new Date()).getTime() - time;
+ if (silent !== true) setTimeout(function () { obj.status('Sorting took ' + time/1000 + ' sec'); }, 10);
+ return time;
+ },
+
+ localSearch: function (silent) {
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (url) {
+ console.log('ERROR: grid.localSearch can only be used on local data source, grid.url should be empty.');
+ return;
+ }
+ var time = (new Date()).getTime();
+ var obj = this;
+ this.total = this.records.length;
+ // mark all records as shown
+ this.last.searchIds = [];
+ // prepare date/time fields
+ this.prepareData();
+ // hide records that did not match
+ if (this.searchData.length > 0 && !url) {
+ this.total = 0;
+ for (var r in this.records) {
+ var rec = this.records[r];
+ var fl = 0;
+ for (var s in this.searchData) {
+ var sdata = this.searchData[s];
+ var search = this.getSearch(sdata.field);
+ if (sdata == null) continue;
+ if (search == null) search = { field: sdata.field, type: sdata.type };
+ var val1 = String(obj.parseField(rec, search.field)).toLowerCase();
+ if (typeof sdata.value != 'undefined') {
+ if (!$.isArray(sdata.value)) {
+ var val2 = String(sdata.value).toLowerCase();
+ } else {
+ var val2 = sdata.value[0];
+ var val3 = sdata.value[1];
+ }
+ }
+ switch (sdata.operator) {
+ case 'is':
+ if (rec[search.field] == sdata.value) fl++; // do not hide record
+ if (search.type == 'date') {
+ var val1 = w2utils.formatDate(rec[search.field + '_'], 'yyyy-mm-dd');
+ var val2 = w2utils.formatDate(val2, 'yyyy-mm-dd');
+ if (val1 == val2) fl++;
+ }
+ if (search.type == 'time') {
+ var val1 = w2utils.formatTime(rec[search.field + '_'], 'h24:mi');
+ var val2 = w2utils.formatTime(val2, 'h24:mi');
+ if (val1 == val2) fl++;
+ }
+ break;
+ case 'between':
+ if (['int', 'float', 'money', 'currency', 'percent'].indexOf(search.type) != -1) {
+ if (parseFloat(rec[search.field]) >= parseFloat(val2) && parseFloat(rec[search.field]) <= parseFloat(val3)) fl++;
+ }
+ if (search.type == 'date') {
+ var val1 = rec[search.field + '_'];
+ var val2 = w2utils.isDate(val2, w2utils.settings.date_format, true);
+ var val3 = w2utils.isDate(val3, w2utils.settings.date_format, true);
+ if (val3 != null) val3 = new Date(val3.getTime() + 86400000); // 1 day
+ if (val1 >= val2 && val1 < val3) fl++;
+ }
+ if (search.type == 'time') {
+ var val1 = rec[search.field + '_'];
+ var val2 = w2utils.isTime(val2, true);
+ var val3 = w2utils.isTime(val3, true);
+ val2 = (new Date()).setHours(val2.hours, val2.minutes, val2.seconds ? val2.seconds : 0, 0);
+ val3 = (new Date()).setHours(val3.hours, val3.minutes, val3.seconds ? val3.seconds : 0, 0);
+ if (val1 >= val2 && val1 < val3) fl++;
+ }
+ break;
+ case 'in':
+ var tmp = sdata.value;
+ if (sdata.svalue) tmp = sdata.svalue;
+ if (tmp.indexOf(val1) !== -1) fl++;
+ break;
+ case 'not in':
+ var tmp = sdata.value;
+ if (sdata.svalue) tmp = sdata.svalue;
+ if (tmp.indexOf(val1) == -1) fl++;
+ break;
+ case 'begins':
+ case 'begins with': // need for back compatib.
+ if (val1.indexOf(val2) == 0) fl++; // do not hide record
+ break;
+ case 'contains':
+ if (val1.indexOf(val2) >= 0) fl++; // do not hide record
+ break;
+ case 'ends':
+ case 'ends with': // need for back compatib.
+ if (val1.indexOf(val2) == val1.length - val2.length) fl++; // do not hide record
+ break;
+ }
+ }
+ if ((this.last.logic == 'OR' && fl != 0) || (this.last.logic == 'AND' && fl == this.searchData.length)) this.last.searchIds.push(parseInt(r));
+ }
+ this.total = this.last.searchIds.length;
+ }
+ time = (new Date()).getTime() - time;
+ if (silent !== true) setTimeout(function () { obj.status('Search took ' + time/1000 + ' sec'); }, 10);
+ return time;
+ },
+
+ getRangeData: function (range, extra) {
+ var rec1 = this.get(range[0].recid, true);
+ var rec2 = this.get(range[1].recid, true);
+ var col1 = range[0].column;
+ var col2 = range[1].column;
+
+ var res = [];
+ if (col1 == col2) { // one row
+ for (var r = rec1; r <= rec2; r++) {
+ var record = this.records[r];
+ var dt = record[this.columns[col1].field] || null;
+ if (extra !== true) {
+ res.push(dt);
+ } else {
+ res.push({ data: dt, column: col1, index: r, record: record });
+ }
+ }
+ } else if (rec1 == rec2) { // one line
+ var record = this.records[rec1];
+ for (var i = col1; i <= col2; i++) {
+ var dt = record[this.columns[i].field] || null;
+ if (extra !== true) {
+ res.push(dt);
+ } else {
+ res.push({ data: dt, column: i, index: rec1, record: record });
+ }
+ }
+ } else {
+ for (var r = rec1; r <= rec2; r++) {
+ var record = this.records[r];
+ res.push([]);
+ for (var i = col1; i <= col2; i++) {
+ var dt = record[this.columns[i].field];
+ if (extra !== true) {
+ res[res.length-1].push(dt);
+ } else {
+ res[res.length-1].push({ data: dt, column: i, index: r, record: record });
+ }
+ }
+ }
+ }
+ return res;
+ },
+
+ addRange: function (ranges) {
+ var added = 0;
+ if (this.selectType == 'row') return added;
+ if (!$.isArray(ranges)) ranges = [ranges];
+ // if it is selection
+ for (var r in ranges) {
+ if (typeof ranges[r] != 'object') ranges[r] = { name: 'selection' };
+ if (ranges[r].name == 'selection') {
+ if (this.show.selectionBorder === false) continue;
+ var sel = this.getSelection();
+ if (sel.length == 0) {
+ this.removeRange(ranges[r].name);
+ continue;
+ } else {
+ var first = sel[0];
+ var last = sel[sel.length-1];
+ var td1 = $('#grid_'+ this.name +'_rec_'+ first.recid + ' td[col='+ first.column +']');
+ var td2 = $('#grid_'+ this.name +'_rec_'+ last.recid + ' td[col='+ last.column +']');
+ }
+ } else { // other range
+ var first = ranges[r].range[0];
+ var last = ranges[r].range[1];
+ var td1 = $('#grid_'+ this.name +'_rec_'+ first.recid + ' td[col='+ first.column +']');
+ var td2 = $('#grid_'+ this.name +'_rec_'+ last.recid + ' td[col='+ last.column +']');
+ }
+ if (first) {
+ var rg = {
+ name: ranges[r].name,
+ range: [{ recid: first.recid, column: first.column }, { recid: last.recid, column: last.column }],
+ style: ranges[r].style || ''
+ };
+ // add range
+ var ind = false;
+ for (var t in this.ranges) if (this.ranges[t].name == ranges[r].name) { ind = r; break; }
+ if (ind !== false) {
+ this.ranges[ind] = rg;
+ } else {
+ this.ranges.push(rg);
+ }
+ added++
+ }
+ }
+ this.refreshRanges();
+ return added;
+ },
+
+ removeRange: function () {
+ var removed = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ var name = arguments[a];
+ $('#grid_'+ this.name +'_'+ name).remove();
+ for (var r = this.ranges.length-1; r >= 0; r--) {
+ if (this.ranges[r].name == name) {
+ this.ranges.splice(r, 1);
+ removed++;
+ }
+ }
+ }
+ return removed;
+ },
+
+ refreshRanges: function () {
+ var obj = this;
+ var time = (new Date()).getTime();
+ var rec = $('#grid_'+ this.name +'_records');
+ for (var r in this.ranges) {
+ var rg = this.ranges[r];
+ var first = rg.range[0];
+ var last = rg.range[1];
+ var td1 = $('#grid_'+ this.name +'_rec_'+ first.recid + ' td[col='+ first.column +']');
+ var td2 = $('#grid_'+ this.name +'_rec_'+ last.recid + ' td[col='+ last.column +']');
+ if ($('#grid_'+ this.name +'_'+ rg.name).length == 0) {
+ rec.append('<div id="grid_'+ this.name +'_' + rg.name +'" class="w2ui-selection" style="'+ rg.style +'">'+
+ (rg.name == 'selection' ? '<div id="grid_'+ this.name +'_resizer" class="w2ui-selection-resizer"></div>' : '')+
+ '</div>');
+ } else {
+ $('#grid_'+ this.name +'_'+ rg.name).attr('style', rg.style);
+ }
+ if (td1.length > 0 && td2.length > 0) {
+ $('#grid_'+ this.name +'_'+ rg.name).css({
+ left : (td1.position().left - 1 + rec.scrollLeft()) + 'px',
+ top : (td1.position().top - 1 + rec.scrollTop()) + 'px',
+ width : (td2.position().left - td1.position().left + td2.width() + 3) + 'px',
+ height : (td2.position().top - td1.position().top + td2.height() + 3) + 'px'
+ });
+ }
+ }
+
+ // add resizer events
+ $(this.box).find('#grid_'+ this.name +'_resizer').off('mousedown').on('mousedown', mouseStart);
+ //$(this.box).find('#grid_'+ this.name +'_resizer').off('selectstart').on('selectstart', function () { return false; }); // fixes chrome cursror bug
+
+ var eventData = { phase: 'before', type: 'selectionExtend', target: obj.name, originalRange: null, newRange: null };
+
+ function mouseStart (event) {
+ var sel = obj.getSelection();
+ obj.last.move = {
+ type : 'expand',
+ x : event.screenX,
+ y : event.screenY,
+ divX : 0,
+ divY : 0,
+ recid : sel[0].recid,
+ column : sel[0].column,
+ originalRange : [{ recid: sel[0].recid, column: sel[0].column }, { recid: sel[sel.length-1].recid, column: sel[sel.length-1].column }],
+ newRange : [{ recid: sel[0].recid, column: sel[0].column }, { recid: sel[sel.length-1].recid, column: sel[sel.length-1].column }]
+ };
+ $(document).off('mousemove', mouseMove).on('mousemove', mouseMove);
+ $(document).off('mouseup', mouseStop).on('mouseup', mouseStop);
+ }
+
+ function mouseMove (event) {
+ var mv = obj.last.move;
+ if (!mv || mv.type != 'expand') return;
+ mv.divX = (event.screenX - mv.x);
+ mv.divY = (event.screenY - mv.y);
+ // find new cell
+ var recid, column;
+ var tmp = event.originalEvent.target;
+ if (tmp.tagName != 'TD') tmp = $(tmp).parents('td')[0];
+ if (typeof $(tmp).attr('col') != 'undefined') column = parseInt($(tmp).attr('col'));
+ tmp = $(tmp).parents('tr')[0];
+ recid = $(tmp).attr('recid');
+ // new range
+ if (mv.newRange[1].recid == recid && mv.newRange[1].column == column) return;
+ var prevNewRange = $.extend({}, mv.newRange);
+ mv.newRange = [{ recid: mv.recid, column: mv.column }, { recid: recid, column: column }];
+ // event before
+ eventData = obj.trigger($.extend(eventData, { originalRange: mv.originalRange, newRange : mv.newRange }));
+ if (eventData.isCancelled === true) {
+ mv.newRange = prevNewRange;
+ eventData.newRange = prevNewRange;
+ return;
+ } else {
+ // default behavior
+ obj.removeRange('grid-selection-expand');
+ obj.addRange({
+ name : 'grid-selection-expand',
+ range : eventData.newRange,
+ style : 'background-color: rgba(100,100,100,0.1); border: 2px dotted rgba(100,100,100,0.5);'
+ });
+ }
+ }
+
+ function mouseStop (event) {
+ // default behavior
+ obj.removeRange('grid-selection-expand');
+ delete obj.last.move;
+ $(document).off('mousemove', mouseMove);
+ $(document).off('mouseup', mouseStop);
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }
+
+ return (new Date()).getTime() - time;
+ },
+
+ select: function () {
+ var selected = 0;
+ var sel = this.last.selection;
+ if (!this.multiSelect) this.selectNone();
+ for (var a = 0; a < arguments.length; a++) {
+ var recid = typeof arguments[a] == 'object' ? arguments[a].recid : arguments[a];
+ var record = this.get(recid);
+ if (record == null) continue;
+ var index = this.get(recid, true);
+ var recEl = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid));
+ if (this.selectType == 'row') {
+ if (sel.indexes.indexOf(index) >= 0) continue;
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'select', target: this.name, recid: recid, index: index });
+ if (eventData.isCancelled === true) continue;
+ // default action
+ sel.indexes.push(index);
+ sel.indexes.sort(function(a, b) { return a-b });
+ recEl.addClass('w2ui-selected').data('selected', 'yes');
+ recEl.find('.w2ui-grid-select-check').prop("checked", true);
+ selected++;
+ } else {
+ var col = arguments[a].column;
+ if (!w2utils.isInt(col)) { // select all columns
+ var cols = [];
+ for (var c in this.columns) { if (this.columns[c].hidden) continue; cols.push({ recid: recid, column: parseInt(c) }); }
+ if (!this.multiSelect) cols = cols.splice(0, 1);
+ return this.select.apply(this, cols);
+ }
+ var s = sel.columns[index] || [];
+ if ($.isArray(s) && s.indexOf(col) != -1) continue;
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'select', target: this.name, recid: recid, index: index, column: col });
+ if (eventData.isCancelled === true) continue;
+ // default action
+ if (sel.indexes.indexOf(index) == -1) {
+ sel.indexes.push(index);
+ sel.indexes.sort(function(a, b) { return a-b });
+ }
+ s.push(col);
+ s.sort(function(a, b) { return a-b }); // sort function must be for numerical sort
+ recEl.find(' > td[col='+ col +']').addClass('w2ui-selected');
+ selected++;
+ recEl.data('selected', 'yes');
+ recEl.find('.w2ui-grid-select-check').prop("checked", true);
+ // save back to selection object
+ sel.columns[index] = s;
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ // all selected?
+ if (sel.indexes.length == this.records.length || (this.searchData.length !== 0 && sel.indexes.length == this.last.searchIds.length)) {
+ $('#grid_'+ this.name +'_check_all').prop('checked', true);
+ } else {
+ $('#grid_'+ this.name +'_check_all').prop('checked', false);
+ }
+ this.status();
+ this.addRange('selection');
+ return selected;
+ },
+
+ unselect: function () {
+ var unselected = 0;
+ var sel = this.last.selection;
+ for (var a = 0; a < arguments.length; a++) {
+ var recid = typeof arguments[a] == 'object' ? arguments[a].recid : arguments[a];
+ var record = this.get(recid);
+ if (record == null) continue;
+ var index = this.get(record.recid, true);
+ var recEl = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid));
+ if (this.selectType == 'row') {
+ if (sel.indexes.indexOf(index) == -1) continue;
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'unselect', target: this.name, recid: recid, index: index });
+ if (eventData.isCancelled === true) continue;
+ // default action
+ sel.indexes.splice(sel.indexes.indexOf(index), 1);
+ recEl.removeClass('w2ui-selected').removeData('selected');
+ if (recEl.length != 0) recEl[0].style.cssText = 'height: '+ this.recordHeight +'px; ' + recEl.attr('custom_style');
+ recEl.find('.w2ui-grid-select-check').prop("checked", false);
+ unselected++;
+ } else {
+ var col = arguments[a].column;
+ if (!w2utils.isInt(col)) { // unselect all columns
+ var cols = [];
+ for (var c in this.columns) { if (this.columns[c].hidden) continue; cols.push({ recid: recid, column: parseInt(c) }); }
+ return this.unselect.apply(this, cols);
+ }
+ var s = sel.columns[index];
+ if (!$.isArray(s) || s.indexOf(col) == -1) continue;
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'unselect', target: this.name, recid: recid, column: col });
+ if (eventData.isCancelled === true) continue;
+ // default action
+ s.splice(s.indexOf(col), 1);
+ $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid) + ' > td[col='+ col +']').removeClass('w2ui-selected');
+ unselected++;
+ if (s.length == 0) {
+ delete sel.columns[index];
+ sel.indexes.splice(sel.indexes.indexOf(index), 1);
+ recEl.removeData('selected');
+ recEl.find('.w2ui-grid-select-check').prop("checked", false);
+ }
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ // all selected?
+ if (sel.indexes.length == this.records.length || (this.searchData.length !== 0 && sel.indexes.length == this.last.searchIds.length)) {
+ $('#grid_'+ this.name +'_check_all').prop('checked', true);
+ } else {
+ $('#grid_'+ this.name +'_check_all').prop('checked', false);
+ }
+ // show number of selected
+ this.status();
+ this.addRange('selection');
+ return unselected;
+ },
+
+ selectAll: function () {
+ if (this.multiSelect === false) return;
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'select', target: this.name, all: true });
+ if (eventData.isCancelled === true) return;
+ // default action
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ var sel = this.last.selection;
+ var cols = [];
+ for (var c in this.columns) cols.push(parseInt(c));
+ // if local data source and searched
+ sel.indexes = [];
+ if (!url && this.searchData.length !== 0) {
+ // local search applied
+ for (var i = 0; i < this.last.searchIds.length; i++) {
+ sel.indexes.push(this.last.searchIds[i]);
+ if (this.selectType != 'row') sel.columns[this.last.searchIds[i]] = cols.slice(); // .slice makes copy of the array
+ }
+ } else {
+ var buffered = this.records.length;
+ if (this.searchData.length != 0 && !this.url) buffered = this.last.searchIds.length;
+ for (var i = 0; i < buffered; i++) {
+ sel.indexes.push(i);
+ if (this.selectType != 'row') sel.columns[i] = cols.slice(); // .slice makes copy of the array
+ }
+ }
+ this.refresh();
+ // enable/disable toolbar buttons
+ var sel = this.getSelection();
+ if (sel.length == 1) this.toolbar.enable('w2ui-edit'); else this.toolbar.disable('w2ui-edit');
+ if (sel.length >= 1) this.toolbar.enable('w2ui-delete'); else this.toolbar.disable('w2ui-delete');
+ this.addRange('selection');
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ selectNone: function () {
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'unselect', target: this.name, all: true });
+ if (eventData.isCancelled === true) return;
+ // default action
+ var sel = this.last.selection;
+ for (var s in sel.indexes) {
+ var index = sel.indexes[s];
+ var rec = this.records[index];
+ var recid = rec ? rec.recid : null;
+ var recEl = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid));
+ recEl.removeClass('w2ui-selected').removeData('selected');
+ recEl.find('.w2ui-grid-select-check').prop("checked", false);
+ // for not rows
+ if (this.selectType != 'row') {
+ var cols = sel.columns[index];
+ for (var c in cols) recEl.find(' > td[col='+ cols[c] +']').removeClass('w2ui-selected');
+ }
+ }
+ sel.indexes = [];
+ sel.columns = {};
+ this.toolbar.disable('w2ui-edit', 'w2ui-delete');
+ this.removeRange('selection');
+ $('#grid_'+ this.name +'_check_all').prop('checked', false);
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ getSelection: function (returnIndex) {
+ var ret = [];
+ var sel = this.last.selection;
+ if (this.selectType == 'row') {
+ for (var s in sel.indexes) {
+ if (!this.records[sel.indexes[s]]) continue;
+ if (returnIndex === true) ret.push(sel.indexes[s]); else ret.push(this.records[sel.indexes[s]].recid);
+ }
+ return ret;
+ } else {
+ for (var s in sel.indexes) {
+ var cols = sel.columns[sel.indexes[s]];
+ if (!this.records[sel.indexes[s]]) continue;
+ for (var c in cols) {
+ ret.push({ recid: this.records[sel.indexes[s]].recid, index: parseInt(sel.indexes[s]), column: cols[c] });
+ }
+ }
+ return ret;
+ }
+ },
+
+ search: function (field, value) {
+ var obj = this;
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ var searchData = [];
+ var last_multi = this.last.multi;
+ var last_logic = this.last.logic;
+ var last_field = this.last.field;
+ var last_search = this.last.search;
+ // 1: search() - advanced search (reads from popup)
+ if (arguments.length == 0) {
+ last_search = '';
+ // advanced search
+ for (var s in this.searches) {
+ var search = this.searches[s];
+ var operator = $('#grid_'+ this.name + '_operator_'+s).val();
+ var field1 = $('#grid_'+ this.name + '_field_'+s);
+ var field2 = $('#grid_'+ this.name + '_field2_'+s);
+ var value1 = field1.val();
+ var value2 = field2.val();
+ var svalue = null;
+ if (['int', 'float', 'money', 'currency', 'percent'].indexOf(search.type) != -1) {
+ var fld1 = field1.data('w2field');
+ var fld2 = field2.data('w2field');
+ if (fld1) value1 = fld1.clean(value1);
+ if (fld2) value2 = fld2.clean(value2);
+ }
+ if (['list', 'enum'].indexOf(search.type) != -1) {
+ value1 = field1.data('selected') || {};
+ if ($.isArray(value1)) {
+ svalue = [];
+ for (var v in value1) {
+ svalue.push(w2utils.isFloat(value1[v].id) ? parseFloat(value1[v].id) : String(value1[v].id).toLowerCase());
+ delete value1[v].hidden;
+ }
+ } else {
+ value1 = value1.id || '';
+ }
+ }
+ if ((value1 != '' && value1 != null) || (typeof value2 != 'undefined' && value2 != '')) {
+ var tmp = {
+ field : search.field,
+ type : search.type,
+ operator : operator
+ }
+ if (operator == 'between') {
+ $.extend(tmp, { value: [value1, value2] });
+ } else if (operator == 'in' && typeof value1 == 'string') {
+ $.extend(tmp, { value: value1.split(',') });
+ } else if (operator == 'not in' && typeof value1 == 'string') {
+ $.extend(tmp, { value: value1.split(',') });
+ } else {
+ $.extend(tmp, { value: value1 });
+ }
+ if (svalue) $.extend(tmp, { svalue: svalue });
+ // conver date to unix time
+ try {
+ if (search.type == 'date' && operator == 'between') {
+ tmp.value[0] = value1; // w2utils.isDate(value1, w2utils.settings.date_format, true).getTime();
+ tmp.value[1] = value2; // w2utils.isDate(value2, w2utils.settings.date_format, true).getTime();
+ }
+ if (search.type == 'date' && operator == 'is') {
+ tmp.value = value1; // w2utils.isDate(value1, w2utils.settings.date_format, true).getTime();
+ }
+ } catch (e) {
+
+ }
+ searchData.push(tmp);
+ }
+ }
+ if (searchData.length > 0 && !url) {
+ last_multi = true;
+ last_logic = 'AND';
+ } else {
+ last_multi = true;
+ last_logic = 'AND';
+ }
+ }
+ // 2: search(field, value) - regular search
+ if (typeof field == 'string') {
+ last_field = field;
+ last_search = value;
+ last_multi = false;
+ last_logic = 'OR';
+ // loop through all searches and see if it applies
+ if (typeof value != 'undefined') {
+ if (field.toLowerCase() == 'all') {
+ // if there are search fields loop thru them
+ if (this.searches.length > 0) {
+ for (var s in this.searches) {
+ var search = this.searches[s];
+ if (search.type == 'text' || (search.type == 'alphanumeric' && w2utils.isAlphaNumeric(value))
+ || (search.type == 'int' && w2utils.isInt(value)) || (search.type == 'float' && w2utils.isFloat(value))
+ || (search.type == 'percent' && w2utils.isFloat(value)) || (search.type == 'hex' && w2utils.isHex(value))
+ || (search.type == 'currency' && w2utils.isMoney(value)) || (search.type == 'money' && w2utils.isMoney(value))
+ || (search.type == 'date' && w2utils.isDate(value)) ) {
+ var tmp = {
+ field : search.field,
+ type : search.type,
+ operator : (search.type == 'text' ? 'contains' : 'is'),
+ value : value
+ };
+ searchData.push(tmp);
+ }
+ // range in global search box
+ if (['int', 'float', 'money', 'currency', 'percent'].indexOf(search.type) != -1 && String(value).indexOf('-') != -1) {
+ var t = String(value).split('-');
+ var tmp = {
+ field : search.field,
+ type : search.type,
+ operator : 'between',
+ value : [t[0], t[1]]
+ };
+ searchData.push(tmp);
+ }
+ }
+ } else {
+ // no search fields, loop thru columns
+ for (var c in this.columns) {
+ var tmp = {
+ field : this.columns[c].field,
+ type : 'text',
+ operator : 'contains',
+ value : value
+ };
+ searchData.push(tmp);
+ }
+ }
+ } else {
+ var el = $('#grid_'+ this.name +'_search_all');
+ var search = this.getSearch(field);
+ if (search == null) search = { field: field, type: 'text' };
+ if (search.field == field) this.last.caption = search.caption;
+ if (search.type == 'list') {
+ var tmp = el.data('selected');
+ if (tmp && !$.isEmptyObject(tmp)) value = tmp.id;
+ }
+ if (value != '') {
+ var op = 'contains';
+ var val = value;
+ if (['date', 'time', 'list'].indexOf(search.type) != -1) op = 'is';
+ if (search.type == 'int' && value != '') {
+ op = 'is';
+ if (String(value).indexOf('-') != -1) {
+ var tmp = value.split('-');
+ if (tmp.length == 2) {
+ op = 'between';
+ val = [parseInt(tmp[0]), parseInt(tmp[1])];
+ }
+ }
+ if (String(value).indexOf(',') != -1) {
+ var tmp = value.split(',');
+ op = 'in';
+ val = [];
+ for (var t in tmp) val.push(tmp[t]);
+ }
+ }
+ var tmp = {
+ field : search.field,
+ type : search.type,
+ operator : op,
+ value : val
+ }
+ searchData.push(tmp);
+ }
+ }
+ }
+ }
+ // 3: search([ { field, value, [operator,] [type] }, { field, value, [operator,] [type] } ], logic) - submit whole structure
+ if ($.isArray(field)) {
+ var logic = 'AND';
+ if (typeof value == 'string') {
+ logic = value.toUpperCase();
+ if (logic != 'OR' && logic != 'AND') logic = 'AND';
+ }
+ last_search = '';
+ last_multi = true;
+ last_logic = logic;
+ for (var f in field) {
+ var data = field[f];
+ var search = this.getSearch(data.field);
+ if (search == null) search = { type: 'text', operator: 'contains' };
+ // merge current field and search if any
+ searchData.push($.extend(true, {}, search, data));
+ }
+ }
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'search', target: this.name, searchData: searchData,
+ searchField: (field ? field : 'multi'), searchValue: (value ? value : 'multi') });
+ if (eventData.isCancelled === true) return;
+ // default action
+ this.searchData = eventData.searchData;
+ this.last.field = last_field;
+ this.last.search = last_search;
+ this.last.multi = last_multi;
+ this.last.logic = last_logic;
+ this.last.scrollTop = 0;
+ this.last.scrollLeft = 0;
+ this.last.selection.indexes = [];
+ this.last.selection.columns = {};
+ // -- clear all search field
+ this.searchClose();
+ this.set({ expanded: false }, true);
+ // apply search
+ if (url) {
+ this.last.xhr_offset = 0;
+ this.reload();
+ } else {
+ // local search
+ this.localSearch();
+ this.refresh();
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ searchOpen: function () {
+ if (!this.box) return;
+ if (this.searches.length == 0) return;
+ var obj = this;
+ // show search
+ $('#tb_'+ this.name +'_toolbar_item_w2ui-search-advanced').w2overlay(
+ this.getSearchesHTML(), {
+ name : 'searches-'+ this.name,
+ left : -10,
+ 'class' : 'w2ui-grid-searches',
+ onShow : function () {
+ if (obj.last.logic == 'OR') obj.searchData = [];
+ obj.initSearches();
+ $('#w2ui-overlay-searches-'+ this.name +' .w2ui-grid-searches').data('grid-name', obj.name);
+ var sfields = $('#w2ui-overlay-searches-'+ this.name +' .w2ui-grid-searches *[rel=search]');
+ if (sfields.length > 0) sfields[0].focus();
+ }
+ }
+ );
+ },
+
+ searchClose: function () {
+ if (!this.box) return;
+ if (this.searches.length == 0) return;
+ if (this.toolbar) this.toolbar.uncheck('w2ui-search-advanced')
+ // hide search
+ if ($('#w2ui-overlay-searches-'+ this.name +' .w2ui-grid-searches').length > 0) {
+ $().w2overlay('', { name: 'searches-'+ this.name });
+ }
+ },
+
+ searchShowFields: function () {
+ var el = $('#grid_'+ this.name +'_search_all');
+ var html = '<div class="w2ui-select-field"><table>';
+ for (var s = -1; s < this.searches.length; s++) {
+ var search = this.searches[s];
+ if (s == -1) {
+ if (!this.multiSearch) continue;
+ search = { field: 'all', caption: w2utils.lang('All Fields') };
+ } else {
+ if (this.searches[s].hidden === true) continue;
+ }
+ html += '<tr '+ (w2utils.isIOS ? 'onTouchStart' : 'onClick') +'="w2ui[\''+ this.name +'\'].initAllField(\''+ search.field +'\')">'+
+ ' <td><input type="radio" tabIndex="-1" '+ (search.field == this.last.field ? 'checked' : '') +'></td>'+
+ ' <td>'+ search.caption +'</td>'+
+ '</tr>';
+ }
+ html += "</table></div>";
+ // need timer otherwise does nto show with list type
+ setTimeout(function () {
+ $(el).w2overlay(html, { left: -10 });
+ }, 1);
+ },
+
+ initAllField: function (field, value) {
+ var el = $('#grid_'+ this.name +'_search_all');
+ var search = this.getSearch(field);
+ if (field == 'all') {
+ search = { field: 'all', caption: w2utils.lang('All Fields') };
+ el.w2field('clear');
+ el.change().focus();
+ } else {
+ var st = search.type;
+ if (['enum', 'select'].indexOf(st) != -1) st = 'list';
+ el.w2field(st, $.extend({}, search.options, { suffix: '', autoFormat: false, selected: value }));
+ if (['list', 'enum'].indexOf(search.type) != -1) {
+ this.last.search = '';
+ this.last.item = '';
+ el.val('');
+ }
+ // set focus
+ setTimeout(function () {
+ el.focus(); /* do not do el.change() as it will refresh grid and pull from server */
+ }, 1);
+ }
+ // update field
+ if (this.last.search != '') {
+ this.search(search.field, this.last.search);
+ } else {
+ this.last.field = search.field;
+ this.last.caption = search.caption;
+ }
+ el.attr('placeholder', search.caption);
+ $().w2overlay();
+ },
+
+ searchReset: function (noRefresh) {
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'search', target: this.name, searchData: [] });
+ if (eventData.isCancelled === true) return;
+ // default action
+ this.searchData = [];
+ this.last.search = '';
+ this.last.logic = 'OR';
+ // --- do not reset to All Fields (I think)
+ // if (this.last.multi) {
+ // if (!this.multiSearch) {
+ // this.last.field = this.searches[0].field;
+ // this.last.caption = this.searches[0].caption;
+ // } else {
+ // this.last.field = 'all';
+ // this.last.caption = w2utils.lang('All Fields');
+ // }
+ // }
+ this.last.multi = false;
+ this.last.xhr_offset = 0;
+ // reset scrolling position
+ this.last.scrollTop = 0;
+ this.last.scrollLeft = 0;
+ this.last.selection.indexes = [];
+ this.last.selection.columns = {};
+ // -- clear all search field
+ this.searchClose();
+ $('#grid_'+ this.name +'_search_all').val('');
+ // apply search
+ if (!noRefresh) this.reload();
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ clear: function (noRefresh) {
+ // this.offset = 0; // clear should not reset offset
+ // this.total = 0; // clear should not reset total
+ this.records = [];
+ this.summary = [];
+ this.last.scrollTop = 0;
+ this.last.scrollLeft = 0;
+ this.last.range_start = null;
+ this.last.range_end = null;
+ // this.last.xhr_offset = 0; // clear should not reset offset
+ if (!noRefresh) this.refresh();
+ },
+
+ reset: function (noRefresh) {
+ // reset last remembered state
+ this.offset = 0;
+ this.total = 0;
+ this.last.scrollTop = 0;
+ this.last.scrollLeft = 0;
+ this.last.selection.indexes = [];
+ this.last.selection.columns = {};
+ this.last.range_start = null;
+ this.last.range_end = null;
+ this.last.xhr_offset = 0;
+ this.searchReset(noRefresh);
+ // initial sort
+ if (this.last.sortData != null ) this.sortData = this.last.sortData;
+ // select none without refresh
+ this.set({ expanded: false }, true);
+ // refresh
+ if (!noRefresh) this.refresh();
+ },
+
+ skip: function (offset) {
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (url) {
+ this.offset = parseInt(offset);
+ if (this.offset > this.total) this.offset = this.total - this.limit;
+ if (this.offset < 0 || !w2utils.isInt(this.offset)) this.offset = 0;
+ this.records = [];
+ this.last.xhr_offset = 0;
+ this.last.pull_more = true;
+ this.last.scrollTop = 0;
+ this.last.scrollLeft = 0;
+ $('#grid_'+ this.name +'_records').prop('scrollTop', 0);
+ this.reload();
+ } else {
+ console.log('ERROR: grid.skip() can only be called when you have remote data source.');
+ }
+ },
+
+ load: function (url, callBack) {
+ if (typeof url == 'undefined') {
+ console.log('ERROR: You need to provide url argument when calling .load() method of "'+ this.name +'" object.');
+ return;
+ }
+ // default action
+ this.request('get-records', {}, url, callBack);
+ },
+
+ reload: function (callBack) {
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (url) {
+ this.clear(true);
+ this.request('get-records', {}, null, callBack);
+ } else {
+ this.last.scrollTop = 0;
+ this.last.scrollLeft = 0;
+ this.last.range_start = null;
+ this.last.range_end = null;
+ this.localSearch();
+ this.refresh();
+ if (typeof callBack == 'function') callBack({ status: 'success' });
+ }
+ },
+
+ request: function (cmd, add_params, url, callBack) {
+ if (typeof add_params == 'undefined') add_params = {};
+ if (typeof url == 'undefined' || url == '' || url == null) url = this.url;
+ if (url == '' || url == null) return;
+ // build parameters list
+ var params = {};
+ if (!w2utils.isInt(this.offset)) this.offset = 0;
+ if (!w2utils.isInt(this.last.xhr_offset)) this.last.xhr_offset = 0;
+ // add list params
+ params['cmd'] = cmd;
+ params['selected'] = this.getSelection();
+ params['limit'] = this.limit;
+ params['offset'] = parseInt(this.offset) + this.last.xhr_offset;
+ params['search'] = this.searchData;
+ params['searchLogic'] = this.last.logic;
+ params['sort'] = this.sortData;
+ if (this.searchData.length == 0) {
+ delete params['search'];
+ delete params['searchLogic'];
+ }
+ if (this.sortData.length == 0) {
+ delete params['sort'];
+ }
+ // append other params
+ $.extend(params, this.postData);
+ $.extend(params, add_params);
+ // event before
+ if (cmd == 'get-records') {
+ var eventData = this.trigger({ phase: 'before', type: 'request', target: this.name, url: url, postData: params });
+ if (eventData.isCancelled === true) { if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' }); return; }
+ } else {
+ var eventData = { url: url, postData: params };
+ }
+ // call server to get data
+ var obj = this;
+ if (this.last.xhr_offset == 0) {
+ this.lock(this.msgRefresh, true);
+ } else {
+ var more = $('#grid_'+ this.name +'_rec_more');
+ if (this.autoLoad === true) {
+ more.show().find('td').html('<div><div style="width: 20px; height: 20px;" class="w2ui-spinner"></div></div>');
+ } else {
+ more.find('td').html('<div>'+ w2utils.lang('Load') + ' ' + obj.limit + ' ' + w2utils.lang('More') + '...</div>');
+ }
+ }
+ if (this.last.xhr) try { this.last.xhr.abort(); } catch (e) {};
+ // URL
+ var url = (typeof eventData.url != 'object' ? eventData.url : eventData.url.get);
+ if (params.cmd == 'save-records' && typeof eventData.url == 'object') url = eventData.url.save;
+ if (params.cmd == 'delete-records' && typeof eventData.url == 'object') url = eventData.url.remove;
+ // process url with routeData
+ if (!$.isEmptyObject(obj.routeData)) {
+ var info = w2utils.parseRoute(url);
+ if (info.keys.length > 0) {
+ for (var k = 0; k < info.keys.length; k++) {
+ if (obj.routeData[info.keys[k].name] == null) continue;
+ url = url.replace((new RegExp(':'+ info.keys[k].name, 'g')), obj.routeData[info.keys[k].name]);
+ }
+ }
+ }
+ // ajax ptions
+ var ajaxOptions = {
+ type : 'POST',
+ url : url,
+ data : eventData.postData,
+ dataType : 'text' // expected data type from server
+ };
+ if (w2utils.settings.dataType == 'HTTP') {
+ ajaxOptions.data = (typeof ajaxOptions.data == 'object' ? String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']') : ajaxOptions.data);
+ }
+ if (w2utils.settings.dataType == 'RESTFULL') {
+ ajaxOptions.type = 'GET';
+ if (params.cmd == 'save-records') ajaxOptions.type = 'PUT'; // so far it is always update
+ if (params.cmd == 'delete-records') ajaxOptions.type = 'DELETE';
+ ajaxOptions.data = (typeof ajaxOptions.data == 'object' ? String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']') : ajaxOptions.data);
+ }
+ if (w2utils.settings.dataType == 'JSON') {
+ ajaxOptions.type = 'POST';
+ ajaxOptions.data = JSON.stringify(ajaxOptions.data);
+ ajaxOptions.contentType = 'application/json';
+ }
+ if (this.method) ajaxOptions.type = this.method;
+
+ this.last.xhr_cmd = params.cmd;
+ this.last.xhr_start = (new Date()).getTime();
+ this.last.xhr = $.ajax(ajaxOptions)
+ .done(function (data, status, xhr) {
+ obj.requestComplete(status, cmd, callBack);
+ })
+ .fail(function (xhr, status, error) {
+ // trigger event
+ var errorObj = { status: status, error: error, rawResponseText: xhr.responseText };
+ var eventData2 = obj.trigger({ phase: 'before', type: 'error', error: errorObj, xhr: xhr });
+ if (eventData2.isCancelled === true) return;
+ // default behavior
+ if (status != 'abort') {
+ var data;
+ try { data = $.parseJSON(xhr.responseText) } catch (e) {}
+ console.log('ERROR: Server communication failed.',
+ '\n EXPECTED:', { status: 'success', total: 5, records: [{ recid: 1, field: 'value' }] },
+ '\n OR:', { status: 'error', message: 'error message' },
+ '\n RECEIVED:', typeof data == 'object' ? data : xhr.responseText);
+ }
+ obj.requestComplete('error', cmd, callBack);
+ // event after
+ obj.trigger($.extend(eventData2, { phase: 'after' }));
+ });
+ if (cmd == 'get-records') {
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ },
+
+ requestComplete: function(status, cmd, callBack) {
+ var obj = this;
+ this.unlock();
+ setTimeout(function () { obj.status(w2utils.lang('Server Response') + ' ' + ((new Date()).getTime() - obj.last.xhr_start)/1000 +' ' + w2utils.lang('sec')); }, 10);
+ this.last.pull_more = false;
+ this.last.pull_refresh = true;
+
+ // event before
+ var event_name = 'load';
+ if (this.last.xhr_cmd == 'save-records') event_name = 'save';
+ if (this.last.xhr_cmd == 'delete-records') event_name = 'deleted';
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: event_name, xhr: this.last.xhr, status: status });
+ if (eventData.isCancelled === true) {
+ if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' });
+ return;
+ }
+ // parse server response
+ var data;
+ var responseText = this.last.xhr.responseText;
+ if (status != 'error') {
+ // default action
+ if (typeof responseText != 'undefined' && responseText != '') {
+ // check if the onLoad handler has not already parsed the data
+ if (typeof responseText == "object") {
+ data = responseText;
+ } else {
+ if (typeof obj.parser == 'function') {
+ data = obj.parser(responseText);
+ if (typeof data != 'object') {
+ console.log('ERROR: Your parser did not return proper object');
+ }
+ } else {
+ // $.parseJSON or $.getJSON did not work because those expect perfect JSON data - where everything is in double quotes
+ //
+ // TODO: avoid (potentially malicious) code injection from the response.
+ try { eval('data = '+ responseText); } catch (e) { }
+ }
+ }
+ // convert recids
+ if (obj.recid) {
+ for (var r in data.records) {
+ data.records[r]['recid'] = data.records[r][obj.recid];
+ }
+ }
+ if (typeof data == 'undefined') {
+ data = {
+ status : 'error',
+ message : this.msgNotJSON,
+ responseText : responseText
+ };
+ }
+ if (data['status'] == 'error') {
+ obj.error(data['message']);
+ } else {
+ if (cmd == 'get-records') {
+ if (this.last.xhr_offset == 0) {
+ this.records = [];
+ this.summary = [];
+ //data.xhr_status=data.status;
+ delete data.status;
+ $.extend(true, this, data);
+ } else {
+ var records = data.records;
+ delete data.records;
+ //data.xhr_status=data.status;
+ delete data.status;
+ $.extend(true, this, data);
+ for (var r in records) {
+ this.records.push(records[r]);
+ }
+ }
+ }
+ if (cmd == 'delete-records') {
+ // reset() also triggers reload
+ this.reset(); // unselect old selections
+ return;
+ }
+ }
+ }
+ } else {
+ data = {
+ status : 'error',
+ message : this.msgAJAXerror,
+ responseText : responseText
+ };
+ obj.error(this.msgAJAXerror);
+ }
+ // event after
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (!url) {
+ this.localSort();
+ this.localSearch();
+ }
+ this.total = parseInt(this.total);
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ // do not refresh if loading on infinite scroll
+ if (this.last.xhr_offset == 0) this.refresh(); else this.scroll();
+ // call back
+ if (typeof callBack == 'function') callBack(data);
+ },
+
+ error: function (msg) {
+ var obj = this;
+ // let the management of the error outside of the grid
+ var eventData = this.trigger({ target: this.name, type: 'error', message: msg , xhr: this.last.xhr });
+ if (eventData.isCancelled === true) {
+ if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' });
+ return;
+ }
+ w2alert(msg, 'Error');
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ getChanges: function () {
+ var changes = [];
+ for (var r in this.records) {
+ var rec = this.records[r];
+ if (typeof rec['changes'] != 'undefined') {
+ changes.push($.extend(true, { recid: rec.recid }, rec.changes));
+ }
+ }
+ return changes;
+ },
+
+ mergeChanges: function () {
+ var changes = this.getChanges();
+ for (var c in changes) {
+ var record = this.get(changes[c].recid);
+ for (var s in changes[c]) {
+ if (s == 'recid') continue; // do not allow to change recid
+ try { eval('record.' + s + ' = changes[c][s]'); } catch (e) {}
+ delete record.changes;
+ }
+ }
+ this.refresh();
+ },
+
+ // ===================================================
+ // -- Action Handlers
+
+ save: function () {
+ var obj = this;
+ var changes = this.getChanges();
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'submit', changes: changes });
+ if (eventData.isCancelled === true) return;
+ var url = (typeof this.url != 'object' ? this.url : this.url.save);
+ if (url) {
+ this.request('save-records', { 'changes' : eventData.changes }, null,
+ function (data) {
+ if (data.status !== 'error') {
+ // only merge changes, if save was successful
+ obj.mergeChanges();
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ );
+ } else {
+ this.mergeChanges();
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ },
+
+ editField: function (recid, column, value, event) {
+ var obj = this;
+ var index = obj.get(recid, true);
+ var rec = obj.records[index];
+ var col = obj.columns[column];
+ var edit = col ? col.editable : null;
+ if (!rec || !col || !edit || rec.editable === false) return;
+ if (['enum', 'file'].indexOf(edit.type) != -1) {
+ console.log('ERROR: input types "enum" and "file" are not supported in inline editing.');
+ return;
+ }
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'editField', target: obj.name, recid: recid, column: column, value: value,
+ index: index, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ value = eventData.value;
+ // default behaviour
+ this.selectNone();
+ this.select({ recid: recid, column: column });
+ this.last.edit_col = column;
+ if (['checkbox', 'check'].indexOf(edit.type) != -1) return;
+ // create input element
+ var tr = $('#grid_'+ obj.name +'_rec_'+ w2utils.escapeId(recid));
+ var el = tr.find('[col='+ column +'] > div');
+ if (typeof edit.inTag == 'undefined') edit.inTag = '';
+ if (typeof edit.outTag == 'undefined') edit.outTag = '';
+ if (typeof edit.style == 'undefined') edit.style = '';
+ if (typeof edit.items == 'undefined') edit.items = [];
+ var val = (rec.changes && typeof rec.changes[col.field] != 'undefined' ? w2utils.stripTags(rec.changes[col.field]) : w2utils.stripTags(rec[col.field]));
+ if (val == null || typeof val == 'undefined') val = '';
+ if (typeof value != 'undefined' && value != null) val = value;
+ var addStyle = (typeof col.style != 'undefined' ? col.style + ';' : '');
+ if (typeof col.render == 'string' && ['number', 'int', 'float', 'money', 'percent'].indexOf(col.render.split(':')[0]) != -1) {
+ addStyle += 'text-align: right;';
+ }
+ if (edit.type == 'select') {
+ var html = '';
+ for (var i in edit.items) {
+ html += '<option value="'+ edit.items[i].id +'" '+ (edit.items[i].id == val ? 'selected' : '') +'>'+ edit.items[i].text +'</option>';
+ }
+ el.addClass('w2ui-editable')
+ .html('<select id="grid_'+ obj.name +'_edit_'+ recid +'_'+ column +'" column="'+ column +'" '+
+ ' style="width: 100%; '+ addStyle + edit.style +'" field="'+ col.field +'" recid="'+ recid +'" '+
+ ' '+ edit.inTag +
+ '>'+ html +'</select>' + edit.outTag);
+ el.find('select').focus()
+ .on('change', function (event) {
+ delete obj.last.move;
+ })
+ .on('blur', function (event) {
+ obj.editChange.call(obj, this, index, column, event);
+ });
+ } else {
+ el.addClass('w2ui-editable')
+ .html('<input id="grid_'+ obj.name +'_edit_'+ recid +'_'+ column +'" '+
+ ' type="text" style="outline: none; '+ addStyle + edit.style +'" field="'+ col.field +'" recid="'+ recid +'" '+
+ ' column="'+ column +'" '+ edit.inTag +
+ '>' + edit.outTag);
+ if (value == null) el.find('input').val(val != 'object' ? val : '');
+ // init w2field
+ var input = el.find('input').get(0);
+ $(input).w2field(edit.type, $.extend(edit, { selected: val }))
+ // add blur listener
+ setTimeout(function () {
+ var tmp = input;
+ if (edit.type == 'list') {
+ tmp = $($(input).data('w2field').helpers.focus).find('input');
+ if (val != 'object' && val != '') tmp.val(val).css({ opacity: 1 }).prev().css({ opacity: 1 });
+ }
+ $(tmp).on('blur', function (event) {
+ obj.editChange.call(obj, input, index, column, event);
+ });
+ }, 10);
+ if (value != null) $(input).val(val != 'object' ? val : '');
+ }
+ setTimeout(function () {
+ el.find('input, select')
+ .on('click', function (event) {
+ event.stopPropagation();
+ })
+ .on('keydown', function (event) {
+ var cancel = false;
+ switch (event.keyCode) {
+ case 9: // tab
+ cancel = true;
+ var next_rec = recid;
+ var next_col = event.shiftKey ? obj.prevCell(column, true) : obj.nextCell(column, true);
+ // next or prev row
+ if (next_col == null) {
+ var tmp = event.shiftKey ? obj.prevRow(index) : obj.nextRow(index);
+ if (tmp != null && tmp != index) {
+ next_rec = obj.records[tmp].recid;
+ // find first editable row
+ for (var c in obj.columns) {
+ var tmp = obj.columns[c].editable;
+ if (typeof tmp != 'undefined' && ['checkbox', 'check'].indexOf(tmp.type) == -1) {
+ next_col = parseInt(c);
+ if (!event.shiftKey) break;
+ }
+ }
+ }
+
+ }
+ if (next_rec === false) next_rec = recid;
+ if (next_col == null) next_col = column;
+ // init new or same record
+ this.blur();
+ setTimeout(function () {
+ if (obj.selectType != 'row') {
+ obj.selectNone();
+ obj.select({ recid: next_rec, column: next_col });
+ } else {
+ obj.editField(next_rec, next_col, null, event);
+ }
+ }, 1);
+ break;
+
+ case 13: // enter
+ this.blur();
+ var next = event.shiftKey ? obj.prevRow(index) : obj.nextRow(index);
+ if (next != null && next != index) {
+ setTimeout(function () {
+ if (obj.selectType != 'row') {
+ obj.selectNone();
+ obj.select({ recid: obj.records[next].recid, column: column });
+ } else {
+ obj.editField(obj.records[next].recid, column, null, event);
+ }
+ }, 100);
+ }
+ break;
+
+ case 38: // up arrow
+ if (!event.shiftKey) break;
+ cancel = true;
+ var next = obj.prevRow(index);
+ if (next != index) {
+ this.blur();
+ setTimeout(function () {
+ if (obj.selectType != 'row') {
+ obj.selectNone();
+ obj.select({ recid: obj.records[next].recid, column: column });
+ } else {
+ obj.editField(obj.records[next].recid, column, null, event);
+ }
+ }, 1);
+ }
+ break;
+
+ case 40: // down arrow
+ if (!event.shiftKey) break;
+ cancel = true;
+ var next = obj.nextRow(index);
+ if (next != null && next != index) {
+ this.blur();
+ setTimeout(function () {
+ if (obj.selectType != 'row') {
+ obj.selectNone();
+ obj.select({ recid: obj.records[next].recid, column: column });
+ } else {
+ obj.editField(obj.records[next].recid, column, null, event);
+ }
+ }, 1);
+ }
+ break;
+
+ case 27: // escape
+ var old = obj.parseField(rec, col.field);
+ if (rec.changes && typeof rec.changes[col.field] != 'undefined') old = rec.changes[col.field];
+ this.value = typeof old != 'undefined' ? old : '';
+ this.blur();
+ setTimeout(function () { obj.select({ recid: recid, column: column }) }, 1);
+ break;
+ }
+ if (cancel) if (event.preventDefault) event.preventDefault();
+ });
+ // focus and select
+ var tmp = el.find('input').focus();
+ if (value != null) {
+ // set cursor to the end
+ tmp[0].setSelectionRange(tmp.val().length, tmp.val().length);
+ } else {
+ tmp.select();
+ }
+
+ }, 1);
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ editChange: function (el, index, column, event) {
+ // all other fields
+ var summary = index < 0;
+ index = index < 0 ? -index - 1 : index;
+ var records = summary ? this.summary : this.records;
+ var rec = records[index];
+ var tr = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(rec.recid));
+ var col = this.columns[column];
+ var new_val = el.value;
+ var old_val = this.parseField(rec, col.field);
+ var tmp = $(el).data('w2field');
+ if (tmp) {
+ new_val = tmp.clean(new_val);
+ if (tmp.type == 'list' && new_val != '') new_val = $(el).data('selected');
+ }
+ if (el.type == 'checkbox') new_val = el.checked;
+ // change/restore event
+ var eventData = {
+ phase: 'before', type: 'change', target: this.name, input_id: el.id, recid: rec.recid, index: index, column: column,
+ value_new: new_val, value_previous: (rec.changes && rec.changes.hasOwnProperty(col.field) ? rec.changes[col.field]: old_val), value_original: old_val
+ };
+ while (true) {
+ new_val = eventData.value_new;
+ if (( typeof old_val == 'undefined' || old_val === null ? '' : String(old_val)) !== String(new_val)) {
+ // change event
+ eventData = this.trigger($.extend(eventData, { type: 'change', phase: 'before' }));
+ if (eventData.isCancelled !== true) {
+ if (new_val !== eventData.value_new) {
+ // re-evaluate the type of change to be made
+ continue;
+ }
+ // default action
+ rec.changes = rec.changes || {};
+ rec.changes[col.field] = eventData.value_new;
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ } else {
+ // restore event
+ eventData = this.trigger($.extend(eventData, { type: 'restore', phase: 'before' }));
+ if (eventData.isCancelled !== true) {
+ if (new_val !== eventData.value_new) {
+ // re-evaluate the type of change to be made
+ continue;
+ }
+ // default action
+ if (rec.changes) delete rec.changes[col.field];
+ if ($.isEmptyObject(rec.changes)) delete rec.changes;
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ }
+ break;
+ }
+ // refresh cell
+ var cell = this.getCellHTML(index, column, summary);
+ if (!summary) {
+ if (rec.changes && typeof rec.changes[col.field] != 'undefined') {
+ $(tr).find('[col='+ column +']').addClass('w2ui-changed').html(cell);
+ } else {
+ $(tr).find('[col='+ column +']').removeClass('w2ui-changed').html(cell);
+ }
+ }
+ },
+
+ "delete": function (force) {
+ var obj = this;
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'delete', force: force });
+ if (eventData.isCancelled === true) return;
+ force = eventData.force;
+ // default action
+ var recs = this.getSelection();
+ if (recs.length == 0) return;
+ if (this.msgDelete != '' && !force) {
+ w2confirm({
+ title : w2utils.lang('Delete Confirmation'),
+ msg : obj.msgDelete,
+ btn_yes : { "class": 'btn-red' },
+ callBack: function (result) {
+ if (result == 'Yes') w2ui[obj.name].delete(true);
+ }
+ });
+ return;
+ }
+ // call delete script
+ var url = (typeof this.url != 'object' ? this.url : this.url.remove);
+ if (url) {
+ this.request('delete-records');
+ } else {
+ this.selectNone();
+ if (typeof recs[0] != 'object') {
+ this.remove.apply(this, recs);
+ } else {
+ // clear cells
+ for (var r in recs) {
+ var fld = this.columns[recs[r].column].field;
+ var ind = this.get(recs[r].recid, true);
+ if (ind != null && fld != 'recid') {
+ this.records[ind][fld] = '';
+ if (this.records[ind].changes) delete this.records[ind].changes[fld];
+ }
+ }
+ this.refresh();
+ }
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ click: function (recid, event) {
+ var time = (new Date()).getTime();
+ var column = null;
+ if (this.last.cancelClick == true || (event && event.altKey)) return;
+ if (typeof recid == 'object') {
+ column = recid.column;
+ recid = recid.recid;
+ }
+ if (typeof event == 'undefined') event = {};
+ // check for double click
+ if (time - parseInt(this.last.click_time) < 350 && event.type == 'click') {
+ this.dblClick(recid, event);
+ return;
+ }
+ this.last.click_time = time;
+ // column user clicked on
+ if (column == null && event.target) {
+ var tmp = event.target;
+ if (tmp.tagName != 'TD') tmp = $(tmp).parents('td')[0];
+ if (typeof $(tmp).attr('col') != 'undefined') column = parseInt($(tmp).attr('col'));
+ }
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'click', recid: recid, column: column, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // if it is subgrid unselect top grid
+ var parent = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid)).parents('tr');
+ if (parent.length > 0 && String(parent.attr('id')).indexOf('expanded_row') != -1) {
+ var grid = parent.parents('.w2ui-grid').attr('name');
+ w2ui[grid].selectNone();
+ // all subgrids
+ parent.parents('.w2ui-grid').find('.w2ui-expanded-row .w2ui-grid').each(function (index, el) {
+ var grid = $(el).attr('name');
+ if (w2ui[grid]) w2ui[grid].selectNone();
+ });
+ }
+ // unselect all subgrids
+ $(this.box).find('.w2ui-expanded-row .w2ui-grid').each(function (index, el) {
+ var grid = $(el).attr('name');
+ if (w2ui[grid]) w2ui[grid].selectNone();
+ });
+ // default action
+ var obj = this;
+ var sel = this.getSelection();
+ $('#grid_'+ this.name +'_check_all').prop("checked", false);
+ var ind = this.get(recid, true);
+ var record = this.records[ind];
+ var selectColumns = [];
+ obj.last.sel_ind = ind;
+ obj.last.sel_col = column;
+ obj.last.sel_recid = recid;
+ obj.last.sel_type = 'click';
+ // multi select with shif key
+ if (event.shiftKey && sel.length > 0 && obj.multiSelect) {
+ if (sel[0].recid) {
+ var start = this.get(sel[0].recid, true);
+ var end = this.get(recid, true);
+ if (column > sel[0].column) {
+ var t1 = sel[0].column;
+ var t2 = column;
+ } else {
+ var t1 = column;
+ var t2 = sel[0].column;
+ }
+ for (var c = t1; c <= t2; c++) selectColumns.push(c);
+ } else {
+ var start = this.get(sel[0], true);
+ var end = this.get(recid, true);
+ }
+ var sel_add = []
+ if (start > end) { var tmp = start; start = end; end = tmp; }
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ for (var i = start; i <= end; i++) {
+ if (this.searchData.length > 0 && !url && $.inArray(i, this.last.searchIds) == -1) continue;
+ if (this.selectType == 'row') {
+ sel_add.push(this.records[i].recid);
+ } else {
+ for (var sc in selectColumns) sel_add.push({ recid: this.records[i].recid, column: selectColumns[sc] });
+ }
+ //sel.push(this.records[i].recid);
+ }
+ this.select.apply(this, sel_add);
+ } else {
+ var last = this.last.selection;
+ var flag = (last.indexes.indexOf(ind) != -1 ? true : false);
+ // clear other if necessary
+ if (((!event.ctrlKey && !event.shiftKey && !event.metaKey) || !this.multiSelect) && !this.showSelectColumn) {
+ if (this.selectType != 'row' && $.inArray(column, last.columns[ind]) == -1) flag = false;
+ if (sel.length > 300) this.selectNone(); else this.unselect.apply(this, sel);
+ if (flag === true) {
+ this.unselect({ recid: recid, column: column });
+ } else {
+ this.select({ recid: recid, column: column });
+ }
+ } else {
+ if (this.selectType != 'row' && $.inArray(column, last.columns[ind]) == -1) flag = false;
+ if (flag === true) {
+ this.unselect({ recid: recid, column: column });
+ } else {
+ this.select({ recid: recid, column: column });
+ }
+ }
+ }
+ this.status();
+ obj.initResize();
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ columnClick: function (field, event) {
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'columnClick', target: this.name, field: field, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default behaviour
+ var column = this.getColumn(field);
+ if (column.sortable) this.sort(field, null, (event && (event.ctrlKey || event.metaKey) ? true : false) );
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ keydown: function (event) {
+ // this method is called from w2utils
+ var obj = this;
+ if (obj.keyboard !== true) return;
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'keydown', target: obj.name, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default behavior
+ var empty = false;
+ var records = $('#grid_'+ obj.name +'_records');
+ var sel = obj.getSelection();
+ if (sel.length == 0) empty = true;
+ var recid = sel[0] || null;
+ var columns = [];
+ var recid2 = sel[sel.length-1];
+ if (typeof recid == 'object' && recid != null) {
+ recid = sel[0].recid;
+ columns = [];
+ var ii = 0;
+ while (true) {
+ if (!sel[ii] || sel[ii].recid != recid) break;
+ columns.push(sel[ii].column);
+ ii++;
+ }
+ recid2 = sel[sel.length-1].recid;
+ }
+ var ind = obj.get(recid, true);
+ var ind2 = obj.get(recid2, true);
+ var rec = obj.get(recid);
+ var recEL = $('#grid_'+ obj.name +'_rec_'+ (ind !== null ? w2utils.escapeId(obj.records[ind].recid) : 'none'));
+ var cancel = false;
+ var key = event.keyCode;
+ var shiftKey= event.shiftKey;
+ if (key == 9) { // tab key
+ if (event.shiftKey) key = 37; else key = 39; // replace with arrows
+ shiftKey = false;
+ cancel = true;
+ }
+ switch (key) {
+ case 8: // backspace
+ case 46: // delete
+ if (this.show.toolbarDelete) obj["delete"]();
+ cancel = true;
+ event.stopPropagation();
+ break;
+
+ case 27: // escape
+ obj.selectNone();
+ if (sel.length > 0 && typeof sel[0] == 'object') {
+ obj.select({ recid: sel[0].recid, column: sel[0].column });
+ }
+ cancel = true;
+ break;
+
+ case 65: // cmd + A
+ if (!event.metaKey && !event.ctrlKey) break;
+ obj.selectAll();
+ cancel = true;
+ break;
+
+ case 70: // cmd + F
+ if (!event.metaKey && !event.ctrlKey) break;
+ $('#grid_'+ obj.name + '_search_all').focus();
+ cancel = true;
+ break;
+
+ case 13: // enter
+ // if expandable columns - expand it
+ if (this.selectType == 'row' && obj.show.expandColumn === true) {
+ if (recEL.length <= 0) break;
+ obj.toggle(recid, event);
+ cancel = true;
+ } else { // or enter edit
+ for (var c in this.columns) {
+ if (this.columns[c].editable) {
+ columns.push(parseInt(c));
+ break;
+ }
+ }
+ // edit last column that was edited
+ if (this.selectType == 'row' && this.last.edit_col) columns = [this.last.edit_col];
+ if (columns.length > 0) {
+ obj.editField(recid, columns[0], null, event);
+ cancel = true;
+ }
+ }
+ break;
+
+ case 37: // left
+ if (empty) break;
+ // check if this is subgrid
+ var parent = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(obj.records[ind].recid)).parents('tr');
+ if (parent.length > 0 && String(parent.attr('id')).indexOf('expanded_row') != -1) {
+ var recid = parent.prev().attr('recid');
+ var grid = parent.parents('.w2ui-grid').attr('name');
+ obj.selectNone();
+ w2utils.keyboard.active(grid);
+ w2ui[grid].set(recid, { expanded: false });
+ w2ui[grid].collapse(recid);
+ w2ui[grid].click(recid);
+ cancel = true;
+ break;
+ }
+ if (this.selectType == 'row') {
+ if (recEL.length <= 0 || rec.expanded !== true ) break;
+ obj.set(recid, { expanded: false }, true);
+ obj.collapse(recid, event);
+ } else {
+ var prev = obj.prevCell(columns[0]);
+ if (prev != null) {
+ if (shiftKey && obj.multiSelect) {
+ if (tmpUnselect()) return;
+ var tmp = [];
+ var newSel = [];
+ var unSel = [];
+ if (columns.indexOf(this.last.sel_col) == 0 && columns.length > 1) {
+ for (var i in sel) {
+ if (tmp.indexOf(sel[i].recid) == -1) tmp.push(sel[i].recid);
+ unSel.push({ recid: sel[i].recid, column: columns[columns.length-1] });
+ }
+ } else {
+ for (var i in sel) {
+ if (tmp.indexOf(sel[i].recid) == -1) tmp.push(sel[i].recid);
+ newSel.push({ recid: sel[i].recid, column: prev });
+ }
+ }
+ obj.unselect.apply(obj, unSel);
+ obj.select.apply(obj, newSel);
+ } else {
+ event.shiftKey = false;
+ obj.click({ recid: recid, column: prev }, event);
+ }
+ } else {
+ // if selected more then one, then select first
+ if (!shiftKey) {
+ for (var s=1; s<sel.length; s++) obj.unselect(sel[s]);
+ }
+ }
+ }
+ cancel = true;
+ break;
+
+ case 39: // right
+ if (empty) break;
+ if (this.selectType == 'row') {
+ if (recEL.length <= 0 || rec.expanded === true || obj.show.expandColumn !== true) break;
+ obj.expand(recid, event);
+ } else {
+ var next = obj.nextCell(columns[columns.length-1]);
+ if (next !== null) {
+ if (shiftKey && key == 39 && obj.multiSelect) {
+ if (tmpUnselect()) return;
+ var tmp = [];
+ var newSel = [];
+ var unSel = [];
+ if (columns.indexOf(this.last.sel_col) == columns.length-1 && columns.length > 1) {
+ for (var i in sel) {
+ if (tmp.indexOf(sel[i].recid) == -1) tmp.push(sel[i].recid);
+ unSel.push({ recid: sel[i].recid, column: columns[0] });
+ }
+ } else {
+ for (var i in sel) {
+ if (tmp.indexOf(sel[i].recid) == -1) tmp.push(sel[i].recid);
+ newSel.push({ recid: sel[i].recid, column: next });
+ }
+ }
+ obj.unselect.apply(obj, unSel);
+ obj.select.apply(obj, newSel);
+ } else {
+ obj.click({ recid: recid, column: next }, event);
+ }
+ } else {
+ // if selected more then one, then select first
+ if (!shiftKey) {
+ for (var s=0; s<sel.length-1; s++) obj.unselect(sel[s]);
+ }
+ }
+ }
+ cancel = true;
+ break;
+
+ case 38: // up
+ if (empty) selectTopRecord();
+ if (recEL.length <= 0) break;
+ // move to the previous record
+ var prev = obj.prevRow(ind);
+ if (prev != null) {
+ // jump into subgrid
+ if (obj.records[prev].expanded) {
+ var subgrid = $('#grid_'+ obj.name +'_rec_'+ w2utils.escapeId(obj.records[prev].recid) +'_expanded_row').find('.w2ui-grid');
+ if (subgrid.length > 0 && w2ui[subgrid.attr('name')]) {
+ obj.selectNone();
+ var grid = subgrid.attr('name');
+ var recs = w2ui[grid].records;
+ w2utils.keyboard.active(grid);
+ w2ui[grid].click(recs[recs.length-1].recid);
+ cancel = true;
+ break;
+ }
+ }
+ if (shiftKey && obj.multiSelect) { // expand selection
+ if (tmpUnselect()) return;
+ if (obj.selectType == 'row') {
+ if (obj.last.sel_ind > prev && obj.last.sel_ind != ind2) {
+ obj.unselect(obj.records[ind2].recid);
+ } else {
+ obj.select(obj.records[prev].recid);
+ }
+ } else {
+ if (obj.last.sel_ind > prev && obj.last.sel_ind != ind2) {
+ prev = ind2;
+ var tmp = [];
+ for (var c in columns) tmp.push({ recid: obj.records[prev].recid, column: columns[c] });
+ obj.unselect.apply(obj, tmp);
+ } else {
+ var tmp = [];
+ for (var c in columns) tmp.push({ recid: obj.records[prev].recid, column: columns[c] });
+ obj.select.apply(obj, tmp);
+ }
+ }
+ } else { // move selected record
+ obj.selectNone();
+ obj.click({ recid: obj.records[prev].recid, column: columns[0] }, event);
+ }
+ obj.scrollIntoView(prev);
+ if (event.preventDefault) event.preventDefault();
+ } else {
+ // if selected more then one, then select first
+ if (!shiftKey) {
+ for (var s=1; s<sel.length; s++) obj.unselect(sel[s]);
+ }
+ // jump out of subgird (if first record)
+ var parent = $('#grid_'+ obj.name +'_rec_'+ w2utils.escapeId(obj.records[ind].recid)).parents('tr');
+ if (parent.length > 0 && String(parent.attr('id')).indexOf('expanded_row') != -1) {
+ var recid = parent.prev().attr('recid');
+ var grid = parent.parents('.w2ui-grid').attr('name');
+ obj.selectNone();
+ w2utils.keyboard.active(grid);
+ w2ui[grid].click(recid);
+ cancel = true;
+ break;
+ }
+ }
+ break;
+
+ case 40: // down
+ if (empty) selectTopRecord();
+ if (recEL.length <= 0) break;
+ // jump into subgrid
+ if (obj.records[ind2].expanded) {
+ var subgrid = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(obj.records[ind2].recid) +'_expanded_row').find('.w2ui-grid');
+ if (subgrid.length > 0 && w2ui[subgrid.attr('name')]) {
+ obj.selectNone();
+ var grid = subgrid.attr('name');
+ var recs = w2ui[grid].records;
+ w2utils.keyboard.active(grid);
+ w2ui[grid].click(recs[0].recid);
+ cancel = true;
+ break;
+ }
+ }
+ // move to the next record
+ var next = obj.nextRow(ind2);
+ if (next != null) {
+ if (shiftKey && obj.multiSelect) { // expand selection
+ if (tmpUnselect()) return;
+ if (obj.selectType == 'row') {
+ if (this.last.sel_ind < next && this.last.sel_ind != ind) {
+ obj.unselect(obj.records[ind].recid);
+ } else {
+ obj.select(obj.records[next].recid);
+ }
+ } else {
+ if (this.last.sel_ind < next && this.last.sel_ind != ind) {
+ next = ind;
+ var tmp = [];
+ for (var c in columns) tmp.push({ recid: obj.records[next].recid, column: columns[c] });
+ obj.unselect.apply(obj, tmp);
+ } else {
+ var tmp = [];
+ for (var c in columns) tmp.push({ recid: obj.records[next].recid, column: columns[c] });
+ obj.select.apply(obj, tmp);
+ }
+ }
+ } else { // move selected record
+ obj.selectNone();
+ obj.click({ recid: obj.records[next].recid, column: columns[0] }, event);
+ }
+ obj.scrollIntoView(next);
+ cancel = true;
+ } else {
+ // if selected more then one, then select first
+ if (!shiftKey) {
+ for (var s=0; s<sel.length-1; s++) obj.unselect(sel[s]);
+ }
+ // jump out of subgrid (if last record in subgrid)
+ var parent = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(obj.records[ind2].recid)).parents('tr');
+ if (parent.length > 0 && String(parent.attr('id')).indexOf('expanded_row') != -1) {
+ var recid = parent.next().attr('recid');
+ var grid = parent.parents('.w2ui-grid').attr('name');
+ obj.selectNone();
+ w2utils.keyboard.active(grid);
+ w2ui[grid].click(recid);
+ cancel = true;
+ break;
+ }
+ }
+ break;
+
+ // copy & paste
+
+ case 17: // ctrl key
+ case 91: // cmd key
+ if (empty) break;
+ var text = obj.copy();
+ $('body').append('<textarea id="_tmp_copy_data" '+
+ ' onpaste="var obj = this; setTimeout(function () { w2ui[\''+ obj.name + '\'].paste(obj.value); }, 1);" '+
+ ' onkeydown="w2ui[\''+ obj.name +'\'].keydown(event)"'+
+ ' style="position: absolute; top: -100px; height: 1px; width: 1px">'+ text +'</textarea>');
+ $('#_tmp_copy_data').focus().select();
+ // remove _tmp_copy_data textarea
+ $(document).on('keyup', tmp_key_down);
+ function tmp_key_down() {
+ $('#_tmp_copy_data').remove();
+ $(document).off('keyup', tmp_key_down);
+ }
+ break;
+
+ case 88: // x - cut
+ if (empty) break;
+ if (event.ctrlKey || event.metaKey) {
+ setTimeout(function () { obj["delete"](true); }, 100);
+ }
+ break;
+ }
+ var tmp = [187, 189, 32]; // =-spacebar
+ for (var i=48; i<=90; i++) tmp.push(i); // 0-9,a-z,A-Z
+ if (tmp.indexOf(key) != -1 && !event.ctrlKey && !event.metaKey && !cancel) {
+ if (columns.length == 0) columns.push(0);
+ var tmp = String.fromCharCode(key);
+ if (key == 187) tmp = '=';
+ if (key == 189) tmp = '-';
+ if (!shiftKey) tmp = tmp.toLowerCase();
+ obj.editField(recid, columns[0], tmp, event);
+ cancel = true;
+ }
+ if (cancel) { // cancel default behaviour
+ if (event.preventDefault) event.preventDefault();
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+
+ function selectTopRecord() {
+ var ind = Math.floor((records[0].scrollTop + (records.height() / 2.1)) / obj.recordHeight);
+ if (!obj.records[ind]) ind = 0;
+ obj.select({ recid: obj.records[ind].recid, column: 0});
+ }
+
+ function tmpUnselect () {
+ if (obj.last.sel_type != 'click') return false;
+ if (obj.selectType != 'row') {
+ obj.last.sel_type = 'key';
+ if (sel.length > 1) {
+ for (var s in sel) {
+ if (sel[s].recid == obj.last.sel_recid && sel[s].column == obj.last.sel_col) {
+ sel.splice(s, 1);
+ break;
+ }
+ }
+ obj.unselect.apply(obj, sel);
+ return true;
+ }
+ return false;
+ } else {
+ obj.last.sel_type = 'key';
+ if (sel.length > 1) {
+ sel.splice(sel.indexOf(obj.records[obj.last.sel_ind].recid), 1);
+ obj.unselect.apply(obj, sel);
+ return true;
+ }
+ return false;
+ }
+ }
+ },
+
+ scrollIntoView: function (ind) {
+ var buffered = this.records.length;
+ if (this.searchData.length != 0 && !this.url) buffered = this.last.searchIds.length;
+ if (typeof ind == 'undefined') {
+ var sel = this.getSelection();
+ if (sel.length == 0) return;
+ ind = this.get(sel[0], true);
+ }
+ var records = $('#grid_'+ this.name +'_records');
+ if (buffered == 0) return;
+ // if all records in view
+ var len = this.last.searchIds.length;
+ if (records.height() > this.recordHeight * (len > 0 ? len : buffered)) return;
+ if (len > 0) ind = this.last.searchIds.indexOf(ind); // if seach is applied
+ // scroll to correct one
+ var t1 = Math.floor(records[0].scrollTop / this.recordHeight);
+ var t2 = t1 + Math.floor(records.height() / this.recordHeight);
+ if (ind == t1) records.animate({ 'scrollTop': records.scrollTop() - records.height() / 1.3 }, 250, 'linear');
+ if (ind == t2) records.animate({ 'scrollTop': records.scrollTop() + records.height() / 1.3 }, 250, 'linear');
+ if (ind < t1 || ind > t2) records.animate({ 'scrollTop': (ind - 1) * this.recordHeight });
+ },
+
+ dblClick: function (recid, event) {
+ //if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ // find columns
+ var column = null;
+ if (typeof recid == 'object') {
+ column = recid.column;
+ recid = recid.recid;
+ }
+ if (typeof event == 'undefined') event = {};
+ // column user clicked on
+ if (column == null && event.target) {
+ var tmp = event.target;
+ if (tmp.tagName != 'TD') tmp = $(tmp).parents('td')[0];
+ column = parseInt($(tmp).attr('col'));
+ }
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'dblClick', recid: recid, column: column, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default action
+ this.selectNone();
+ var col = this.columns[column];
+ if (col && $.isPlainObject(col.editable)) {
+ this.editField(recid, column, null, event);
+ } else {
+ this.select({ recid: recid, column: column });
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ contextMenu: function (recid, event) {
+ var obj = this;
+ if (obj.last.userSelect == 'text') return;
+ if (typeof event == 'undefined') event = { offsetX: 0, offsetY: 0, target: $('#grid_'+ obj.name +'_rec_'+ recid)[0] };
+ if (typeof event.offsetX === 'undefined') {
+ event.offsetX = event.layerX - event.target.offsetLeft;
+ event.offsetY = event.layerY - event.target.offsetTop;
+ }
+ if (w2utils.isFloat(recid)) recid = parseFloat(recid);
+ if (this.getSelection().indexOf(recid) == -1) obj.click(recid);
+ // need timeout to allow click to finish first
+ setTimeout(function () {
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'contextMenu', target: obj.name, originalEvent: event, recid: recid });
+ if (eventData.isCancelled === true) return;
+ // default action
+ if (obj.menu.length > 0) {
+ $(obj.box).find(event.target)
+ .w2menu(obj.menu, {
+ left : event.offsetX,
+ onSelect: function (event) {
+ obj.menuClick(recid, parseInt(event.index), event.originalEvent);
+ }
+ }
+ );
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }, 150); // need timer 150 for FF
+ // cancel event
+ if (event.preventDefault) event.preventDefault();
+ },
+
+ menuClick: function (recid, index, event) {
+ var obj = this;
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'menuClick', target: obj.name, originalEvent: event,
+ recid: recid, menuIndex: index, menuItem: obj.menu[index] });
+ if (eventData.isCancelled === true) return;
+ // default action
+ // -- empty
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ toggle: function (recid) {
+ var rec = this.get(recid);
+ if (rec.expanded === true) return this.collapse(recid); else return this.expand(recid);
+ },
+
+ expand: function (recid) {
+ var rec = this.get(recid);
+ var obj = this;
+ var id = w2utils.escapeId(recid);
+ if ($('#grid_'+ this.name +'_rec_'+ id +'_expanded_row').length > 0) return false;
+ if (rec.expanded == 'none') return false;
+ // insert expand row
+ var tmp = 1 + (this.show.selectColumn ? 1 : 0);
+ var addClass = ''; // ($('#grid_'+this.name +'_rec_'+ w2utils.escapeId(recid)).hasClass('w2ui-odd') ? 'w2ui-odd' : 'w2ui-even');
+ $('#grid_'+ this.name +'_rec_'+ id).after(
+ '<tr id="grid_'+ this.name +'_rec_'+ id +'_expanded_row" class="w2ui-expanded-row '+ addClass +'">'+
+ (this.show.lineNumbers ? '<td class="w2ui-col-number"></td>' : '') +
+ ' <td class="w2ui-grid-data w2ui-expanded1" colspan="'+ tmp +'"><div style="display: none"></div></td>'+
+ ' <td colspan="100" class="w2ui-expanded2">'+
+ ' <div id="grid_'+ this.name +'_rec_'+ id +'_expanded" style="opacity: 0"></div>'+
+ ' </td>'+
+ '</tr>');
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'expand', target: this.name, recid: recid,
+ box_id: 'grid_'+ this.name +'_rec_'+ id +'_expanded', ready: ready });
+ if (eventData.isCancelled === true) {
+ $('#grid_'+ this.name +'_rec_'+ id +'_expanded_row').remove();
+ return;
+ }
+ // default action
+ $('#grid_'+ this.name +'_rec_'+ id).attr('expanded', 'yes').addClass('w2ui-expanded');
+ $('#grid_'+ this.name +'_rec_'+ id +'_expanded_row').show();
+ $('#grid_'+ this.name +'_cell_'+ this.get(recid, true) +'_expand div').html('<div class="w2ui-spinner" style="width: 16px; height: 16px; margin: -2px 2px;"></div>');
+ rec.expanded = true;
+ // check if height of expanded row > 5 then remove spinner
+ setTimeout(ready, 300);
+ function ready() {
+ var div1 = $('#grid_'+ obj.name +'_rec_'+ id +'_expanded');
+ var div2 = $('#grid_'+ obj.name +'_rec_'+ id +'_expanded_row .w2ui-expanded1 > div');
+ if (div1.height() < 5) return;
+ div1.css('opacity', 1);
+ div2.show().css('opacity', 1);
+ $('#grid_'+ obj.name +'_cell_'+ obj.get(recid, true) +'_expand div').html('-');
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ this.resizeRecords();
+ return true;
+ },
+
+ collapse: function (recid) {
+ var rec = this.get(recid);
+ var obj = this;
+ var id = w2utils.escapeId(recid);
+ if ($('#grid_'+ this.name +'_rec_'+ id +'_expanded_row').length == 0) return false;
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'collapse', target: this.name, recid: recid,
+ box_id: 'grid_'+ this.name +'_rec_'+ id +'_expanded' });
+ if (eventData.isCancelled === true) return;
+ // default action
+ $('#grid_'+ this.name +'_rec_'+ id).removeAttr('expanded').removeClass('w2ui-expanded');
+ $('#grid_'+ this.name +'_rec_'+ id +'_expanded').css('opacity', 0);
+ $('#grid_'+ this.name +'_cell_'+ this.get(recid, true) +'_expand div').html('+');
+ setTimeout(function () {
+ $('#grid_'+ obj.name +'_rec_'+ id +'_expanded').height('0px');
+ setTimeout(function () {
+ $('#grid_'+ obj.name +'_rec_'+ id +'_expanded_row').remove();
+ delete rec.expanded;
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ obj.resizeRecords();
+ }, 300);
+ }, 200);
+ return true;
+ },
+
+ sort: function (field, direction, multiField) { // if no params - clears sort
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'sort', target: this.name, field: field, direction: direction, multiField: multiField });
+ if (eventData.isCancelled === true) return;
+ // check if needed to quit
+ if (typeof field != 'undefined') {
+ // default action
+ var sortIndex = this.sortData.length;
+ for (var s in this.sortData) {
+ if (this.sortData[s].field == field) { sortIndex = s; break; }
+ }
+ if (typeof direction == 'undefined' || direction == null) {
+ if (typeof this.sortData[sortIndex] == 'undefined') {
+ direction = 'asc';
+ } else {
+ switch (String(this.sortData[sortIndex].direction)) {
+ case 'asc' : direction = 'desc'; break;
+ case 'desc' : direction = 'asc'; break;
+ default : direction = 'asc'; break;
+ }
+ }
+ }
+ if (this.multiSort === false) { this.sortData = []; sortIndex = 0; }
+ if (multiField != true) { this.sortData = []; sortIndex = 0; }
+ // set new sort
+ if (typeof this.sortData[sortIndex] == 'undefined') this.sortData[sortIndex] = {};
+ this.sortData[sortIndex].field = field;
+ this.sortData[sortIndex].direction = direction;
+ } else {
+ this.sortData = [];
+ }
+ this.selectNone();
+ // if local
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (!url) {
+ this.localSort();
+ if (this.searchData.length > 0) this.localSearch(true);
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ this.refresh();
+ } else {
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ this.last.xhr_offset = 0;
+ this.reload();
+ }
+ },
+
+ copy: function () {
+ var sel = this.getSelection();
+ if (sel.length == 0) return '';
+ var text = '';
+ if (typeof sel[0] == 'object') { // cell copy
+ // find min/max column
+ var minCol = sel[0].column;
+ var maxCol = sel[0].column;
+ var recs = [];
+ for (var s in sel) {
+ if (sel[s].column < minCol) minCol = sel[s].column;
+ if (sel[s].column > maxCol) maxCol = sel[s].column;
+ if (recs.indexOf(sel[s].index) == -1) recs.push(sel[s].index);
+ }
+ recs.sort();
+ for (var r in recs) {
+ var ind = recs[r];
+ for (var c = minCol; c <= maxCol; c++) {
+ var col = this.columns[c];
+ if (col.hidden === true) continue;
+ text += w2utils.stripTags(this.getCellHTML(ind, c)) + '\t';
+ }
+ text = text.substr(0, text.length-1); // remove last \t
+ text += '\n';
+ }
+ } else { // row copy
+ // copy headers
+ for (var c in this.columns) {
+ var col = this.columns[c];
+ if (col.hidden === true) continue;
+ text += '"' + w2utils.stripTags(col.caption ? col.caption : col.field) + '"\t';
+ }
+ text = text.substr(0, text.length-1); // remove last \t
+ text += '\n';
+ // copy selected text
+ for (var s in sel) {
+ var ind = this.get(sel[s], true);
+ for (var c in this.columns) {
+ var col = this.columns[c];
+ if (col.hidden === true) continue;
+ text += '"' + w2utils.stripTags(this.getCellHTML(ind, c)) + '"\t';
+ }
+ text = text.substr(0, text.length-1); // remove last \t
+ text += '\n';
+ }
+ }
+ text = text.substr(0, text.length - 1);
+ // before event
+ var eventData = this.trigger({ phase: 'before', type: 'copy', target: this.name, text: text });
+ if (eventData.isCancelled === true) return '';
+ text = eventData.text;
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return text;
+ },
+
+ paste: function (text) {
+ var sel = this.getSelection();
+ var ind = this.get(sel[0].recid, true);
+ var col = sel[0].column;
+ // before event
+ var eventData = this.trigger({ phase: 'before', type: 'paste', target: this.name, text: text, index: ind, column: col });
+ if (eventData.isCancelled === true) return;
+ text = eventData.text;
+ // default action
+ if (this.selectType == 'row' || sel.length == 0) {
+ console.log('ERROR: You can paste only if grid.selectType = \'cell\' and when at least one cell selected.');
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return;
+ }
+ var newSel = [];
+ var text = text.split('\n');
+ for (var t in text) {
+ var tmp = text[t].split('\t');
+ var cnt = 0;
+ var rec = this.records[ind];
+ var cols = [];
+ for (var dt in tmp) {
+ if (!this.columns[col + cnt]) continue;
+ var field = this.columns[col + cnt].field;
+ rec.changes = rec.changes || {};
+ rec.changes[field] = tmp[dt];
+ cols.push(col + cnt);
+ cnt++;
+ }
+ for (var c in cols) newSel.push({ recid: rec.recid, column: cols[c] });
+ ind++;
+ }
+ this.selectNone();
+ this.select.apply(this, newSel);
+ this.refresh();
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ // ==================================================
+ // --- Common functions
+
+ resize: function () {
+ var obj = this;
+ var time = (new Date()).getTime();
+ //if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ // make sure the box is right
+ if (!this.box || $(this.box).attr('name') != this.name) return;
+ // determine new width and height
+ $(this.box).find('> div')
+ .css('width', $(this.box).width())
+ .css('height', $(this.box).height());
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'resize', target: this.name });
+ if (eventData.isCancelled === true) return;
+ // resize
+ obj.resizeBoxes();
+ obj.resizeRecords();
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return (new Date()).getTime() - time;
+ },
+
+ refreshCell: function (recid, field) {
+ var index = this.get(recid, true);
+ var col_ind = this.getColumn(field, true);
+ var rec = this.records[index];
+ var col = this.columns[col_ind];
+ var cell = $('#grid_'+ this.name + '_rec_'+ recid +' [col='+ col_ind +']');
+ // set cell html and changed flag
+ cell.html(this.getCellHTML(index, col_ind));
+ if (rec.changes && typeof rec.changes[col.field] != 'undefined') {
+ cell.addClass('w2ui-changed');
+ } else {
+ cell.removeClass('w2ui-changed');
+ }
+ },
+
+ refreshRow: function (recid) {
+ var tr = $('#grid_'+ this.name +'_rec_'+ w2utils.escapeId(recid));
+ if (tr.length != 0) {
+ var ind = this.get(recid, true);
+ var line = tr.attr('line');
+ // if it is searched, find index in search array
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (this.searchData.length > 0 && !url) for (var s in this.last.searchIds) if (this.last.searchIds[s] == ind) ind = s;
+ $(tr).replaceWith(this.getRecordHTML(ind, line));
+ }
+
+ },
+
+ refresh: function () {
+ var obj = this;
+ var time = (new Date()).getTime();
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (this.total <= 0 && !url && this.searchData.length == 0) {
+ this.total = this.records.length;
+ }
+ //if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ this.toolbar.disable('w2ui-edit', 'w2ui-delete');
+ if (!this.box) return;
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'refresh' });
+ if (eventData.isCancelled === true) return;
+ // -- header
+ if (this.show.header) {
+ $('#grid_'+ this.name +'_header').html(this.header +'&nbsp;').show();
+ } else {
+ $('#grid_'+ this.name +'_header').hide();
+ }
+ // -- toolbar
+ if (this.show.toolbar) {
+ // if select-collumn is checked - no toolbar refresh
+ if (this.toolbar && this.toolbar.get('w2ui-column-on-off') && this.toolbar.get('w2ui-column-on-off').checked) {
+ // no action
+ } else {
+ $('#grid_'+ this.name +'_toolbar').show();
+ // refresh toolbar all but search field
+ if (typeof this.toolbar == 'object') {
+ var tmp = this.toolbar.items;
+ for (var t in tmp) {
+ if (tmp[t].id == 'w2ui-search' || tmp[t].type == 'break') continue;
+ this.toolbar.refresh(tmp[t].id);
+ }
+ }
+ }
+ } else {
+ $('#grid_'+ this.name +'_toolbar').hide();
+ }
+ // -- make sure search is closed
+ this.searchClose();
+ // search placeholder
+ var el = $('#grid_'+ obj.name +'_search_all');
+ if (!this.multiSearch && this.last.field == 'all' && this.searches.length > 0) {
+ this.last.field = this.searches[0].field;
+ this.last.caption = this.searches[0].caption;
+ }
+ for (var s in this.searches) {
+ if (this.searches[s].field == this.last.field) this.last.caption = this.searches[s].caption;
+ }
+ if (this.last.multi) {
+ el.attr('placeholder', '[' + w2utils.lang('Multiple Fields') + ']');
+ } else {
+ el.attr('placeholder', this.last.caption);
+ }
+ if (el.val() != this.last.search) {
+ var val = this.last.search;
+ var tmp = el.data('w2field');
+ if (tmp) val = tmp.format(val);
+ el.val(val);
+ }
+
+ // -- separate summary
+ var tmp = this.find({ summary: true }, true);
+ if (tmp.length > 0) {
+ for (var t in tmp) this.summary.push(this.records[tmp[t]]);
+ for (var t=tmp.length-1; t>=0; t--) this.records.splice(tmp[t], 1);
+ this.total = this.total - tmp.length;
+ }
+
+ // -- body
+ var bodyHTML = '';
+ bodyHTML += '<div id="grid_'+ this.name +'_records" class="w2ui-grid-records"'+
+ ' onscroll="var obj = w2ui[\''+ this.name + '\']; '+
+ ' obj.last.scrollTop = this.scrollTop; '+
+ ' obj.last.scrollLeft = this.scrollLeft; '+
+ ' $(\'#grid_'+ this.name +'_columns\')[0].scrollLeft = this.scrollLeft;'+
+ ' $(\'#grid_'+ this.name +'_summary\')[0].scrollLeft = this.scrollLeft;'+
+ ' obj.scroll(event);">'+
+ this.getRecordsHTML() +
+ '</div>'+
+ '<div id="grid_'+ this.name +'_columns" class="w2ui-grid-columns">'+
+ ' <table>'+ this.getColumnsHTML() +'</table>'+
+ '</div>'; // Columns need to be after to be able to overlap
+ $('#grid_'+ this.name +'_body').html(bodyHTML);
+ // show summary records
+ if (this.summary.length > 0) {
+ $('#grid_'+ this.name +'_summary').html(this.getSummaryHTML()).show();
+ } else {
+ $('#grid_'+ this.name +'_summary').hide();
+ }
+ // -- footer
+ if (this.show.footer) {
+ $('#grid_'+ this.name +'_footer').html(this.getFooterHTML()).show();
+ } else {
+ $('#grid_'+ this.name +'_footer').hide();
+ }
+ // show/hide clear search link
+ if (this.searchData.length > 0) {
+ $('#grid_'+ this.name +'_searchClear').show();
+ } else {
+ $('#grid_'+ this.name +'_searchClear').hide();
+ }
+ // all selected?
+ var sel = this.last.selection;
+ if (sel.indexes.length == this.records.length || (this.searchData.length !== 0 && sel.indexes.length == this.last.searchIds.length)) {
+ $('#grid_'+ this.name +'_check_all').prop('checked', true);
+ } else {
+ $('#grid_'+ this.name +'_check_all').prop('checked', false);
+ }
+ // show number of selected
+ this.status();
+ // collapse all records
+ var rows = obj.find({ expanded: true }, true);
+ for (var r in rows) obj.records[rows[r]].expanded = false;
+ // mark selection
+ setTimeout(function () {
+ var str = $.trim($('#grid_'+ obj.name +'_search_all').val());
+ if (str != '') $(obj.box).find('.w2ui-grid-data > div').w2marker(str);
+ }, 50);
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ obj.resize();
+ obj.addRange('selection');
+ setTimeout(function () { obj.resize(); obj.scroll(); }, 1); // allow to render first
+
+ if ( obj.reorderColumns && !obj.last.columnDrag ) {
+ obj.last.columnDrag = obj.initColumnDrag();
+ } else if ( !obj.reorderColumns && obj.last.columnDrag ) {
+ obj.last.columnDrag.remove();
+ }
+
+ return (new Date()).getTime() - time;
+ },
+
+ render: function (box) {
+ var obj = this;
+ var time = (new Date()).getTime();
+ //if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ if (typeof box != 'undefined' && box != null) {
+ if ($(this.box).find('#grid_'+ this.name +'_body').length > 0) {
+ $(this.box)
+ .removeAttr('name')
+ .removeClass('w2ui-reset w2ui-grid')
+ .html('');
+ }
+ this.box = box;
+ }
+ if (!this.box) return;
+ if (this.last.sortData == null) this.last.sortData = this.sortData;
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'render', box: box });
+ if (eventData.isCancelled === true) return;
+ // insert Elements
+ $(this.box)
+ .attr('name', this.name)
+ .addClass('w2ui-reset w2ui-grid')
+ .html('<div>'+
+ ' <div id="grid_'+ this.name +'_header" class="w2ui-grid-header"></div>'+
+ ' <div id="grid_'+ this.name +'_toolbar" class="w2ui-grid-toolbar"></div>'+
+ ' <div id="grid_'+ this.name +'_body" class="w2ui-grid-body"></div>'+
+ ' <div id="grid_'+ this.name +'_summary" class="w2ui-grid-body w2ui-grid-summary"></div>'+
+ ' <div id="grid_'+ this.name +'_footer" class="w2ui-grid-footer"></div>'+
+ '</div>');
+ if (this.selectType != 'row') $(this.box).addClass('w2ui-ss');
+ if ($(this.box).length > 0) $(this.box)[0].style.cssText += this.style;
+ // init toolbar
+ this.initToolbar();
+ if (this.toolbar != null) this.toolbar.render($('#grid_'+ this.name +'_toolbar')[0]);
+ // reinit search_all
+ if (this.last.field && this.last.field != 'all') {
+ var sd = this.searchData;
+ this.initAllField(this.last.field, (sd.length == 1 ? sd[0].value : null));
+ }
+ // init footer
+ $('#grid_'+ this.name +'_footer').html(this.getFooterHTML());
+ // refresh
+ if (!this.last.state) this.last.state = this.stateSave(true); // initial default state
+ this.stateRestore();
+ if (this.url) this.refresh(); // show empty grid (need it) - should it be only for remote data source
+ this.reload();
+
+ // init mouse events for mouse selection
+ $(this.box).on('mousedown', mouseStart);
+ $(this.box).on('selectstart', function () { return false; }); // fixes chrome cursor bug
+
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ // attach to resize event
+ if ($('.w2ui-layout').length == 0) { // if there is layout, it will send a resize event
+ this.tmp_resize = function (event) { w2ui[obj.name].resize(); }
+ $(window).off('resize', this.tmp_resize).on('resize', this.tmp_resize);
+ }
+ return (new Date()).getTime() - time;
+
+ function mouseStart (event) {
+ if (event.which != 1) return; // if not left mouse button
+ // restore css user-select
+ if (obj.last.userSelect == 'text') {
+ delete obj.last.userSelect;
+ $(obj.box).find('.w2ui-grid-body')
+ .css('user-select', 'none')
+ .css('-webkit-user-select', 'none')
+ .css('-moz-user-select', 'none')
+ .css('-ms-user-select', 'none');
+ $(this.box).on('selectstart', function () { return false; });
+ }
+ // regular record select
+ if ($(event.target).parents().hasClass('w2ui-head') || $(event.target).hasClass('w2ui-head')) return;
+ if (obj.last.move && obj.last.move.type == 'expand') return;
+ // if altKey - alow text selection
+ if (event.altKey) {
+ $(obj.box).off('selectstart');
+ $(obj.box).find('.w2ui-grid-body')
+ .css('user-select', 'text')
+ .css('-webkit-user-select', 'text')
+ .css('-moz-user-select', 'text')
+ .css('-ms-user-select', 'text');
+ obj.selectNone();
+ obj.last.move = { type: 'text-select' };
+ obj.last.userSelect = 'text';
+ } else {
+ if (!obj.multiSelect) return;
+ obj.last.move = {
+ x : event.screenX,
+ y : event.screenY,
+ divX : 0,
+ divY : 0,
+ recid : $(event.target).parents('tr').attr('recid'),
+ column : (event.target.tagName == 'TD' ? $(event.target).attr('col') : $(event.target).parents('td').attr('col')),
+ type : 'select',
+ ghost : false,
+ start : true
+ };
+ }
+ $(document).on('mousemove', mouseMove);
+ $(document).on('mouseup', mouseStop);
+ }
+
+ function mouseMove (event) {
+ var mv = obj.last.move;
+ if (!mv || mv.type != 'select') return;
+ mv.divX = (event.screenX - mv.x);
+ mv.divY = (event.screenY - mv.y);
+ if (Math.abs(mv.divX) <= 1 && Math.abs(mv.divY) <= 1) return; // only if moved more then 1px
+ obj.last.cancelClick = true;
+ if (obj.reorderRows == true) {
+ if (!mv.ghost) {
+ var row = $('#grid_'+ obj.name + '_rec_'+ mv.recid);
+ var tmp = row.parents('table').find('tr:first-child').clone();
+ mv.offsetY = event.offsetY;
+ mv.from = mv.recid;
+ mv.pos = row.position();
+ mv.ghost = $(row).clone(true);
+ mv.ghost.removeAttr('id');
+ row.find('td:first-child').replaceWith('<td colspan="1000" style="height: '+ obj.recordHeight +'px; background-color: #ddd"></td>');
+ var recs = $(obj.box).find('.w2ui-grid-records');
+ recs.append('<table id="grid_'+ obj.name + '_ghost" style="position: absolute; z-index: 999999; opacity: 0.8; border-bottom: 2px dashed #aaa; border-top: 2px dashed #aaa; pointer-events: none;"></table>');
+ $('#grid_'+ obj.name + '_ghost').append(tmp).append(mv.ghost);
+ }
+ var recid = $(event.target).parents('tr').attr('recid');
+ if (recid != mv.from) {
+ var row1 = $('#grid_'+ obj.name + '_rec_'+ mv.recid);
+ var row2 = $('#grid_'+ obj.name + '_rec_'+ recid);
+ if (event.screenY - mv.lastY < 0) row1.after(row2); else row2.after(row1);
+ mv.lastY = event.screenY;
+ mv.to = recid;
+ }
+ var ghost = $('#grid_'+ obj.name + '_ghost');
+ var recs = $(obj.box).find('.w2ui-grid-records');
+ ghost.css({
+ top : mv.pos.top + mv.divY + recs.scrollTop(), // + mv.offsetY - obj.recordHeight / 2,
+ left : mv.pos.left
+ });
+ return;
+ }
+ if (mv.start && mv.recid) {
+ obj.selectNone();
+ mv.start = false;
+ }
+ var newSel= [];
+ var recid = (event.target.tagName == 'TR' ? $(event.target).attr('recid') : $(event.target).parents('tr').attr('recid'));
+ if (typeof recid == 'undefined') return;
+ var ind1 = obj.get(mv.recid, true);
+ // |:wolfmanx:| this happens when selection is started on summary row
+ if (ind1 === null) return;
+ var ind2 = obj.get(recid, true);
+ // this happens when selection is extended into summary row (a good place to implement scrolling)
+ if (ind2 === null) return;
+ var col1 = parseInt(mv.column);
+ var col2 = parseInt(event.target.tagName == 'TD' ? $(event.target).attr('col') : $(event.target).parents('td').attr('col'));
+ if (ind1 > ind2) { var tmp = ind1; ind1 = ind2; ind2 = tmp; }
+ // check if need to refresh
+ var tmp = 'ind1:'+ ind1 +',ind2;'+ ind2 +',col1:'+ col1 +',col2:'+ col2;
+ if (mv.range == tmp) return;
+ mv.range = tmp;
+ for (var i = ind1; i <= ind2; i++) {
+ if (obj.last.searchIds.length > 0 && obj.last.searchIds.indexOf(i) == -1) continue;
+ if (obj.selectType != 'row') {
+ if (col1 > col2) { var tmp = col1; col1 = col2; col2 = tmp; }
+ var tmp = [];
+ for (var c = col1; c <= col2; c++) {
+ if (obj.columns[c].hidden) continue;
+ newSel.push({ recid: obj.records[i].recid, column: parseInt(c) });
+ }
+ } else {
+ newSel.push(obj.records[i].recid);
+ }
+ }
+ if (obj.selectType != 'row') {
+ var sel = obj.getSelection();
+ // add more items
+ var tmp = [];
+ for (var ns in newSel) {
+ var flag = false;
+ for (var s in sel) if (newSel[ns].recid == sel[s].recid && newSel[ns].column == sel[s].column) flag = true;
+ if (!flag) tmp.push({ recid: newSel[ns].recid, column: newSel[ns].column });
+ }
+ obj.select.apply(obj, tmp);
+ // remove items
+ var tmp = [];
+ for (var s in sel) {
+ var flag = false;
+ for (var ns in newSel) if (newSel[ns].recid == sel[s].recid && newSel[ns].column == sel[s].column) flag = true;
+ if (!flag) tmp.push({ recid: sel[s].recid, column: sel[s].column });
+ }
+ obj.unselect.apply(obj, tmp);
+ } else {
+ if (obj.multiSelect) {
+ var sel = obj.getSelection();
+ for (var ns in newSel) if (sel.indexOf(newSel[ns]) == -1) obj.select(newSel[ns]); // add more items
+ for (var s in sel) if (newSel.indexOf(sel[s]) == -1) obj.unselect(sel[s]); // remove items
+ }
+ }
+ }
+
+ function mouseStop (event) {
+ var mv = obj.last.move;
+ setTimeout(function () { delete obj.last.cancelClick; }, 1);
+ if ($(event.target).parents().hasClass('.w2ui-head') || $(event.target).hasClass('.w2ui-head')) return;
+ if (mv && mv.type == 'select') {
+ if (obj.reorderRows == true) {
+ var ind1 = obj.get(mv.from, true);
+ var tmp = obj.records[ind1];
+ obj.records.splice(ind1, 1);
+ var ind2 = obj.get(mv.to, true);
+ if (ind1 > ind2) obj.records.splice(ind2, 0, tmp); else obj.records.splice(ind2+1, 0, tmp);
+ $('#grid_'+ obj.name + '_ghost').remove();
+ obj.refresh();
+ }
+ }
+ delete obj.last.move;
+ $(document).off('mousemove', mouseMove);
+ $(document).off('mouseup', mouseStop);
+ }
+ },
+
+ destroy: function () {
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'destroy' });
+ if (eventData.isCancelled === true) return;
+ // remove events
+ $(window).off('resize', this.tmp_resize);
+ // clean up
+ if (typeof this.toolbar == 'object' && this.toolbar.destroy) this.toolbar.destroy();
+ if ($(this.box).find('#grid_'+ this.name +'_body').length > 0) {
+ $(this.box)
+ .removeAttr('name')
+ .removeClass('w2ui-reset w2ui-grid')
+ .html('');
+ }
+ delete w2ui[this.name];
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ // ===========================================
+ // --- Internal Functions
+
+ initColumnOnOff: function () {
+ if (!this.show.toolbarColumns) return;
+ var obj = this;
+ var col_html = '<div class="w2ui-col-on-off">'+
+ '<table><tr>'+
+ '<td style="width: 30px">'+
+ ' <input id="grid_'+ this.name +'_column_ln_check" type="checkbox" tabIndex="-1" '+ (obj.show.lineNumbers ? 'checked' : '') +
+ ' onclick="w2ui[\''+ obj.name +'\'].columnOnOff(this, event, \'line-numbers\');">'+
+ '</td>'+
+ '<td onclick="w2ui[\''+ obj.name +'\'].columnOnOff(this, event, \'line-numbers\'); $(\'#w2ui-overlay\')[0].hide();">'+
+ ' <label for="grid_'+ this.name +'_column_ln_check">'+ w2utils.lang('Line #') +'</label>'+
+ '</td></tr>';
+ for (var c in this.columns) {
+ var col = this.columns[c];
+ var tmp = this.columns[c].caption;
+ if (col.hideable === false) continue;
+ if (!tmp && this.columns[c].hint) tmp = this.columns[c].hint;
+ if (!tmp) tmp = '- column '+ (parseInt(c) + 1) +' -';
+ col_html += '<tr>'+
+ '<td style="width: 30px">'+
+ ' <input id="grid_'+ this.name +'_column_'+ c +'_check" type="checkbox" tabIndex="-1" '+ (col.hidden ? '' : 'checked') +
+ ' onclick="w2ui[\''+ obj.name +'\'].columnOnOff(this, event, \''+ col.field +'\');">'+
+ '</td>'+
+ '<td>'+
+ ' <label for="grid_'+ this.name +'_column_'+ c +'_check">'+ tmp + '</label>'+
+ '</td>'+
+ '</tr>';
+ }
+ col_html += '<tr><td colspan="2"><div style="border-top: 1px solid #ddd;"></div></td></tr>';
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (url && obj.show.skipRecords) {
+ col_html +=
+ '<tr><td colspan="2" style="padding: 0px">'+
+ ' <div style="cursor: pointer; padding: 2px 8px; cursor: default">'+ w2utils.lang('Skip') +
+ ' <input type="text" style="width: 45px" value="'+ this.offset +'" '+
+ ' onkeypress="if (event.keyCode == 13) { '+
+ ' w2ui[\''+ obj.name +'\'].skip(this.value); '+
+ ' $(\'#w2ui-overlay\')[0].hide(); '+
+ ' }"> '+ w2utils.lang('Records')+
+ ' </div>'+
+ '</td></tr>';
+ }
+ col_html += '<tr><td colspan="2" onclick="w2ui[\''+ obj.name +'\'].stateSave(); $(\'#w2ui-overlay\')[0].hide();">'+
+ ' <div style="cursor: pointer; padding: 4px 8px; cursor: default">'+ w2utils.lang('Save Grid State') + '</div>'+
+ '</td></tr>'+
+ '<tr><td colspan="2" onclick="w2ui[\''+ obj.name +'\'].stateReset(); $(\'#w2ui-overlay\')[0].hide();">'+
+ ' <div style="cursor: pointer; padding: 4px 8px; cursor: default">'+ w2utils.lang('Restore Default State') + '</div>'+
+ '</td></tr>';
+ col_html += "</table></div>";
+ this.toolbar.get('w2ui-column-on-off').html = col_html;
+ },
+
+ /**
+ *
+ * @param box, grid object
+ * @returns {{remove: Function}} contains a closure around all events to ensure they are removed from the dom
+ */
+ initColumnDrag: function ( box ) {
+ //throw error if using column groups
+ if ( this.columnGroups && this.columnGroups.length ) throw 'Draggable columns are not currently supported with column groups.';
+
+ var obj = this,
+ _dragData = {};
+ _dragData.lastInt = null;
+ _dragData.pressed = false;
+ _dragData.timeout = null;_dragData.columnHead = null;
+
+ //attach orginal event listener
+ $(obj.box).on('mousedown', dragColStart);
+ $(obj.box).on('mouseup', catchMouseup);
+
+ function catchMouseup(){
+ _dragData.pressed = false;
+ clearTimeout( _dragData.timeout );
+ }
+ /**
+ *
+ * @param event, mousedown
+ * @returns {boolean} false, preventsDefault
+ */
+ function dragColStart ( event ) {
+ if ( _dragData.timeout ) clearTimeout( _dragData.timeout );
+ var self = this;
+ _dragData.pressed = true;
+
+ _dragData.timeout = setTimeout(function(){
+ if ( !_dragData.pressed ) return;
+
+ var eventData,
+ columns,
+ selectedCol,
+ origColumn,
+ origColumnNumber,
+ invalidPreColumns = [ 'w2ui-col-number', 'w2ui-col-expand', 'w2ui-col-select' ],
+ invalidPostColumns = [ 'w2ui-head-last' ],
+ invalidColumns = invalidPreColumns.concat( invalidPostColumns ),
+ preColumnsSelector = '.w2ui-col-number, .w2ui-col-expand, .w2ui-col-select',
+ preColHeadersSelector = '.w2ui-head.w2ui-col-number, .w2ui-head.w2ui-col-expand, .w2ui-head.w2ui-col-select';
+
+ // do nothing if it is not a header
+ if ( !$( event.originalEvent.target ).parents().hasClass( 'w2ui-head' ) ) return;
+
+ // do nothing if it is an invalid column
+ for ( var i = 0, l = invalidColumns.length; i < l; i++ ){
+ if ( $( event.originalEvent.target ).parents().hasClass( invalidColumns[ i ] ) ) return;
+ }
+
+ _dragData.numberPreColumnsPresent = $( obj.box ).find( preColHeadersSelector ).length;
+
+ //start event for drag start
+ _dragData.columnHead = origColumn = $( event.originalEvent.target ).parents( '.w2ui-head' );
+ origColumnNumber = parseInt( origColumn.attr( 'col' ), 10);
+ eventData = obj.trigger({ type: 'columnDragStart', phase: 'before', originalEvent: event, origColumnNumber: origColumnNumber, target: origColumn[0] });
+ if ( eventData.isCancelled === true ) return false;
+
+ columns = _dragData.columns = $( obj.box ).find( '.w2ui-head:not(.w2ui-head-last)' );
+
+ //add events
+ $( document ).on( 'mouseup', dragColEnd );
+ $( document ).on( 'mousemove', dragColOver );
+
+ _dragData.originalPos = parseInt( $( event.originalEvent.target ).parent( '.w2ui-head' ).attr( 'col' ), 10 );
+ //_dragData.columns.css({ overflow: 'visible' }).children( 'div' ).css({ overflow: 'visible' });
+
+ //configure and style ghost image
+ _dragData.ghost = $( self ).clone( true );
+
+ //hide other elements on ghost except the grid body
+ $( _dragData.ghost ).find( '[col]:not([col="' + _dragData.originalPos + '"]), .w2ui-toolbar, .w2ui-grid-header' ).remove();
+ $( _dragData.ghost ).find( preColumnsSelector ).remove();
+ $( _dragData.ghost ).find( '.w2ui-grid-body' ).css({ top: 0 });
+
+ selectedCol = $( _dragData.ghost ).find( '[col="' + _dragData.originalPos + '"]' );
+ $( document.body ).append( _dragData.ghost );
+
+ $( _dragData.ghost ).css({
+ width: 0,
+ height: 0,
+ margin: 0,
+ position: 'fixed',
+ zIndex: 999999,
+ opacity: 0
+ }).addClass( '.w2ui-grid-ghost' ).animate({
+ width: selectedCol.width(),
+ height: $(obj.box).find('.w2ui-grid-body:first').height(),
+ left : event.pageX,
+ top : event.pageY,
+ opacity: .8
+ }, 0 );
+
+ //establish current offsets
+ _dragData.offsets = [];
+ for ( var i = 0, l = columns.length; i < l; i++ ) {
+ _dragData.offsets.push( $( columns[ i ] ).offset().left );
+ }
+
+ //conclude event
+ obj.trigger( $.extend( eventData, { phase: 'after' } ) );
+ }, 150 );//end timeout wrapper
+ }
+
+ function dragColOver ( event ) {
+ if ( !_dragData.pressed ) return;
+
+ var cursorX = event.originalEvent.pageX,
+ cursorY = event.originalEvent.pageY,
+ offsets = _dragData.offsets,
+ lastWidth = $( '.w2ui-head:not(.w2ui-head-last)' ).width();
+
+ _dragData.targetInt = Math.max(_dragData.numberPreColumnsPresent,targetIntersection( cursorX, offsets, lastWidth ));
+
+ markIntersection( _dragData.targetInt );
+ trackGhost( cursorX, cursorY );
+ }
+
+ function dragColEnd ( event ) {
+ _dragData.pressed = false;
+
+ var eventData,
+ target,
+ selected,
+ columnConfig,
+ targetColumn,
+ ghosts = $( '.w2ui-grid-ghost' );
+
+ //start event for drag start
+ eventData = obj.trigger({ type: 'columnDragEnd', phase: 'before', originalEvent: event, target: _dragData.columnHead[0] });
+ if ( eventData.isCancelled === true ) return false;
+
+ selected = obj.columns[ _dragData.originalPos ];
+ columnConfig = obj.columns;
+ targetColumn = $( _dragData.columns[ Math.min(_dragData.lastInt, _dragData.columns.length - 1) ] );
+ target = (_dragData.lastInt < _dragData.columns.length) ? parseInt(targetColumn.attr('col')) : columnConfig.length;
+
+ if ( target !== _dragData.originalPos + 1 && target !== _dragData.originalPos && targetColumn && targetColumn.length ) {
+ $( _dragData.ghost ).animate({
+ top: $( obj.box ).offset().top,
+ left: targetColumn.offset().left,
+ width: 0,
+ height: 0,
+ opacity:.2
+ }, 300, function(){
+ $( this ).remove();
+ ghosts.remove();
+ });
+
+ columnConfig.splice( target, 0, $.extend( {}, selected ) );
+ columnConfig.splice( columnConfig.indexOf( selected ), 1);
+
+ } else {
+ $( _dragData.ghost ).remove();
+ ghosts.remove();
+ }
+
+ //_dragData.columns.css({ overflow: '' }).children( 'div' ).css({ overflow: '' });
+
+ $( document ).off( 'mouseup', dragColEnd );
+ $( document ).off( 'mousemove', dragColOver );
+ if ( _dragData.marker ) _dragData.marker.remove();
+ _dragData = {};
+
+ obj.refresh();
+
+ //conclude event
+ obj.trigger( $.extend( eventData, { phase: 'after', targetColumnNumber: target - 1 } ) );
+ }
+
+ function markIntersection( intersection ){
+ if ( !_dragData.marker && !_dragData.markerLeft ) {
+ _dragData.marker = $('<div class="col-intersection-marker">' +
+ '<div class="top-marker"></div>' +
+ '<div class="bottom-marker"></div>' +
+ '</div>');
+ _dragData.markerLeft = $('<div class="col-intersection-marker">' +
+ '<div class="top-marker"></div>' +
+ '<div class="bottom-marker"></div>' +
+ '</div>');
+ }
+
+ if ( !_dragData.lastInt || _dragData.lastInt !== intersection ){
+ _dragData.lastInt = intersection;
+ _dragData.marker.remove();
+ _dragData.markerLeft.remove();
+ $('.w2ui-head').removeClass('w2ui-col-intersection');
+
+ //if the current intersection is greater than the number of columns add the marker to the end of the last column only
+ if ( intersection >= _dragData.columns.length ) {
+ $( _dragData.columns[ _dragData.columns.length - 1 ] ).children( 'div:last' ).append( _dragData.marker.addClass( 'right' ).removeClass( 'left' ) );
+ $( _dragData.columns[ _dragData.columns.length - 1 ] ).addClass('w2ui-col-intersection');
+ } else if ( intersection <= _dragData.numberPreColumnsPresent ) {
+ //if the current intersection is on the column numbers place marker on first available column only
+ $( _dragData.columns[ _dragData.numberPreColumnsPresent ] ).prepend( _dragData.marker.addClass( 'left' ).removeClass( 'right' ) ).css({ position: 'relative' });
+ $( _dragData.columns[ _dragData.numberPreColumnsPresent ] ).prev().addClass('w2ui-col-intersection');
+ } else {
+ //otherwise prepend the marker to the targeted column and append it to the previous column
+ $( _dragData.columns[intersection] ).children( 'div:last' ).prepend( _dragData.marker.addClass( 'left' ).removeClass( 'right' ) );
+ $( _dragData.columns[intersection] ).prev().children( 'div:last' ).append( _dragData.markerLeft.addClass( 'right' ).removeClass( 'left' ) ).css({ position: 'relative' });
+ $( _dragData.columns[intersection - 1] ).addClass('w2ui-col-intersection');
+ }
+ }
+ }
+
+ function targetIntersection( cursorX, offsets, lastWidth ){
+ if ( cursorX <= offsets[0] ) {
+ return 0;
+ } else if ( cursorX >= offsets[offsets.length - 1] + lastWidth ) {
+ return offsets.length;
+ } else {
+ for ( var i = 0, l = offsets.length; i < l; i++ ) {
+ var thisOffset = offsets[ i ];
+ var nextOffset = offsets[ i + 1 ] || offsets[ i ] + lastWidth;
+ var midpoint = ( nextOffset - offsets[ i ]) / 2 + offsets[ i ];
+
+ if ( cursorX > thisOffset && cursorX <= midpoint ) {
+ return i;
+ } else if ( cursorX > midpoint && cursorX <= nextOffset ) {
+ return i + 1;
+ }
+ }
+ return intersection;
+ }
+ }
+
+ function trackGhost( cursorX, cursorY ){
+ $( _dragData.ghost ).css({
+ left: cursorX - 10,
+ top: cursorY - 10
+ });
+ }
+
+ //return an object to remove drag if it has ever been enabled
+ return {
+ remove: function(){
+ $( obj.box ).off( 'mousedown', dragColStart );
+ $( obj.box ).off( 'mouseup', catchMouseup );
+ $( obj.box ).find( '.w2ui-head' ).removeAttr( 'draggable' );
+ obj.last.columnDrag = false;
+ }
+ }
+ },
+
+ columnOnOff: function (el, event, field) {
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'columnOnOff', checkbox: el, field: field, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // regular processing
+ var obj = this;
+ // collapse expanded rows
+ for (var r in this.records) {
+ if (this.records[r].expanded === true) this.records[r].expanded = false
+ }
+ // show/hide
+ var hide = true;
+ if (field == 'line-numbers') {
+ this.show.lineNumbers = !this.show.lineNumbers;
+ this.refresh();
+ } else {
+ var col = this.getColumn(field);
+ if (col.hidden) {
+ $(el).prop('checked', true);
+ this.showColumn(col.field);
+ } else {
+ $(el).prop('checked', false);
+ this.hideColumn(col.field);
+ }
+ hide = false;
+ }
+ if (hide) {
+ setTimeout(function () {
+ $().w2overlay('', { name: 'searches-'+ this.name });
+ obj.toolbar.uncheck('column-on-off');
+ }, 100);
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ initToolbar: function () {
+ // -- if toolbar is true
+ if (typeof this.toolbar['render'] == 'undefined') {
+ var tmp_items = this.toolbar.items;
+ this.toolbar.items = [];
+ this.toolbar = $().w2toolbar($.extend(true, {}, this.toolbar, { name: this.name +'_toolbar', owner: this }));
+
+ // =============================================
+ // ------ Toolbar Generic buttons
+
+ if (this.show.toolbarReload) {
+ this.toolbar.items.push($.extend(true, {}, this.buttons['reload']));
+ }
+ if (this.show.toolbarColumns) {
+ this.toolbar.items.push($.extend(true, {}, this.buttons['columns']));
+ }
+ if (this.show.toolbarReload || this.show.toolbarColumn) {
+ this.toolbar.items.push({ type: 'break', id: 'w2ui-break0' });
+ }
+ if (this.show.toolbarSearch) {
+ var html =
+ '<div class="w2ui-toolbar-search">'+
+ '<table cellpadding="0" cellspacing="0"><tr>'+
+ ' <td>'+ this.buttons['search'].html +'</td>'+
+ ' <td>'+
+ ' <input id="grid_'+ this.name +'_search_all" class="w2ui-search-all" '+
+ ' placeholder="'+ this.last.caption +'" value="'+ this.last.search +'"'+
+ ' onkeydown="if (event.keyCode == 13 && w2utils.isIE) this.onchange();"'+
+ ' onchange="'+
+ ' var val = this.value; '+
+ ' var fld = $(this).data(\'w2field\'); '+
+ ' if (fld) val = fld.clean(val);'+
+ ' w2ui[\''+ this.name +'\'].search(w2ui[\''+ this.name +'\'].last.field, val); '+
+ ' ">'+
+ ' </td>'+
+ ' <td>'+
+ ' <div title="'+ w2utils.lang('Clear Search') +'" class="w2ui-search-clear" id="grid_'+ this.name +'_searchClear" '+
+ ' onclick="var obj = w2ui[\''+ this.name +'\']; obj.searchReset();" '+
+ ' >&nbsp;&nbsp;</div>'+
+ ' </td>'+
+ '</tr></table>'+
+ '</div>';
+ this.toolbar.items.push({ type: 'html', id: 'w2ui-search', html: html });
+ if (this.multiSearch && this.searches.length > 0) {
+ this.toolbar.items.push($.extend(true, {}, this.buttons['search-go']));
+ }
+ }
+ if (this.show.toolbarSearch && (this.show.toolbarAdd || this.show.toolbarEdit || this.show.toolbarDelete || this.show.toolbarSave)) {
+ this.toolbar.items.push({ type: 'break', id: 'w2ui-break1' });
+ }
+ if (this.show.toolbarAdd) {
+ this.toolbar.items.push($.extend(true, {}, this.buttons['add']));
+ }
+ if (this.show.toolbarEdit) {
+ this.toolbar.items.push($.extend(true, {}, this.buttons['edit']));
+ }
+ if (this.show.toolbarDelete) {
+ this.toolbar.items.push($.extend(true, {}, this.buttons['delete']));
+ }
+ if (this.show.toolbarSave) {
+ if (this.show.toolbarAdd || this.show.toolbarDelete || this.show.toolbarEdit) {
+ this.toolbar.items.push({ type: 'break', id: 'w2ui-break2' });
+ }
+ this.toolbar.items.push($.extend(true, {}, this.buttons['save']));
+ }
+ // add original buttons
+ for (var i in tmp_items) this.toolbar.items.push(tmp_items[i]);
+
+ // =============================================
+ // ------ Toolbar onClick processing
+
+ var obj = this;
+ this.toolbar.on('click', function (event) {
+ var eventData = obj.trigger({ phase: 'before', type: 'toolbar', target: event.target, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ var id = event.target;
+ switch (id) {
+ case 'w2ui-reload':
+ var eventData2 = obj.trigger({ phase: 'before', type: 'reload', target: obj.name });
+ if (eventData2.isCancelled === true) return false;
+ obj.reload();
+ obj.trigger($.extend(eventData2, { phase: 'after' }));
+ break;
+ case 'w2ui-column-on-off':
+ obj.initColumnOnOff();
+ obj.initResize();
+ obj.resize();
+ break;
+ case 'w2ui-search-advanced':
+ var tb = this;
+ var it = this.get(id);
+ if (it.checked) {
+ obj.searchClose();
+ setTimeout(function () { tb.uncheck(id); }, 1);
+ } else {
+ obj.searchOpen();
+ event.originalEvent.stopPropagation();
+ function tmp_close() {
+ if ($('#w2ui-overlay-searches-'+ obj.name).data('keepOpen') === true) return;
+ tb.uncheck(id);
+ $(document).off('click', 'body', tmp_close);
+ }
+ $(document).on('click', 'body', tmp_close);
+ }
+ break;
+ case 'w2ui-add':
+ // events
+ var eventData = obj.trigger({ phase: 'before', target: obj.name, type: 'add', recid: null });
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ break;
+ case 'w2ui-edit':
+ var sel = obj.getSelection();
+ var recid = null;
+ if (sel.length == 1) recid = sel[0];
+ // events
+ var eventData = obj.trigger({ phase: 'before', target: obj.name, type: 'edit', recid: recid });
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ break;
+ case 'w2ui-delete':
+ obj["delete"]();
+ break;
+ case 'w2ui-save':
+ obj.save();
+ break;
+ }
+ // no default action
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ });
+ }
+ return;
+ },
+
+ initResize: function () {
+ var obj = this;
+ //if (obj.resizing === true) return;
+ $(this.box).find('.w2ui-resizer')
+ .off('click')
+ .on('click', function (event) {
+ if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
+ if (event.preventDefault) event.preventDefault();
+ })
+ .off('mousedown')
+ .on('mousedown', function (event) {
+ if (!event) event = window.event;
+ if (!window.addEventListener) { window.document.attachEvent('onselectstart', function() { return false; } ); }
+ obj.resizing = true;
+ obj.last.tmp = {
+ x : event.screenX,
+ y : event.screenY,
+ gx : event.screenX,
+ gy : event.screenY,
+ col : parseInt($(this).attr('name'))
+ };
+ if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
+ if (event.preventDefault) event.preventDefault();
+ // fix sizes
+ for (var c in obj.columns) {
+ if (typeof obj.columns[c].sizeOriginal == 'undefined') obj.columns[c].sizeOriginal = obj.columns[c].size;
+ obj.columns[c].size = obj.columns[c].sizeCalculated;
+ }
+ var eventData = { phase: 'before', type: 'columnResize', target: obj.name, column: obj.last.tmp.col, field: obj.columns[obj.last.tmp.col].field };
+ eventData = obj.trigger($.extend(eventData, { resizeBy: 0, originalEvent: event }));
+ // set move event
+ var mouseMove = function (event) {
+ if (obj.resizing != true) return;
+ if (!event) event = window.event;
+ // event before
+ eventData = obj.trigger($.extend(eventData, { resizeBy: (event.screenX - obj.last.tmp.gx), originalEvent: event }));
+ if (eventData.isCancelled === true) { eventData.isCancelled = false; return; }
+ // default action
+ obj.last.tmp.x = (event.screenX - obj.last.tmp.x);
+ obj.last.tmp.y = (event.screenY - obj.last.tmp.y);
+ obj.columns[obj.last.tmp.col].size = (parseInt(obj.columns[obj.last.tmp.col].size) + obj.last.tmp.x) + 'px';
+ obj.resizeRecords();
+ // reset
+ obj.last.tmp.x = event.screenX;
+ obj.last.tmp.y = event.screenY;
+ }
+ var mouseUp = function (event) {
+ delete obj.resizing;
+ $(document).off('mousemove', 'body');
+ $(document).off('mouseup', 'body');
+ obj.resizeRecords();
+ // event before
+ obj.trigger($.extend(eventData, { phase: 'after', originalEvent: event }));
+ }
+ $(document).on('mousemove', 'body', mouseMove);
+ $(document).on('mouseup', 'body', mouseUp);
+ })
+ .each(function (index, el) {
+ var td = $(el).parent();
+ $(el).css({
+ "height" : '25px',
+ "margin-left" : (td.width() - 3) + 'px'
+ })
+ });
+ },
+
+ resizeBoxes: function () {
+ // elements
+ var main = $(this.box).find('> div');
+ var header = $('#grid_'+ this.name +'_header');
+ var toolbar = $('#grid_'+ this.name +'_toolbar');
+ var summary = $('#grid_'+ this.name +'_summary');
+ var footer = $('#grid_'+ this.name +'_footer');
+ var body = $('#grid_'+ this.name +'_body');
+ var columns = $('#grid_'+ this.name +'_columns');
+ var records = $('#grid_'+ this.name +'_records');
+
+ if (this.show.header) {
+ header.css({
+ top: '0px',
+ left: '0px',
+ right: '0px'
+ });
+ }
+
+ if (this.show.toolbar) {
+ toolbar.css({
+ top: ( 0 + (this.show.header ? w2utils.getSize(header, 'height') : 0) ) + 'px',
+ left: '0px',
+ right: '0px'
+ });
+ }
+ if (this.show.footer) {
+ footer.css({
+ bottom: '0px',
+ left: '0px',
+ right: '0px'
+ });
+ }
+ if (this.summary.length > 0) {
+ summary.css({
+ bottom: ( 0 + (this.show.footer ? w2utils.getSize(footer, 'height') : 0) ) + 'px',
+ left: '0px',
+ right: '0px'
+ });
+ }
+ body.css({
+ top: ( 0 + (this.show.header ? w2utils.getSize(header, 'height') : 0) + (this.show.toolbar ? w2utils.getSize(toolbar, 'height') : 0) ) + 'px',
+ bottom: ( 0 + (this.show.footer ? w2utils.getSize(footer, 'height') : 0) + (this.summary.length > 0 ? w2utils.getSize(summary, 'height') : 0) ) + 'px',
+ left: '0px',
+ right: '0px'
+ });
+ },
+
+ resizeRecords: function () {
+ var obj = this;
+ // remove empty records
+ $(this.box).find('.w2ui-empty-record').remove();
+ // -- Calculate Column size in PX
+ var box = $(this.box);
+ var grid = $(this.box).find('> div');
+ var header = $('#grid_'+ this.name +'_header');
+ var toolbar = $('#grid_'+ this.name +'_toolbar');
+ var summary = $('#grid_'+ this.name +'_summary');
+ var footer = $('#grid_'+ this.name +'_footer');
+ var body = $('#grid_'+ this.name +'_body');
+ var columns = $('#grid_'+ this.name +'_columns');
+ var records = $('#grid_'+ this.name +'_records');
+
+ // body might be expanded by data
+ if (!this.fixedBody) {
+ // allow it to render records, then resize
+ var calculatedHeight = w2utils.getSize(columns, 'height')
+ + w2utils.getSize($('#grid_'+ obj.name +'_records table'), 'height');
+ obj.height = calculatedHeight
+ + w2utils.getSize(grid, '+height')
+ + (obj.show.header ? w2utils.getSize(header, 'height') : 0)
+ + (obj.show.toolbar ? w2utils.getSize(toolbar, 'height') : 0)
+ + (summary.css('display') != 'none' ? w2utils.getSize(summary, 'height') : 0)
+ + (obj.show.footer ? w2utils.getSize(footer, 'height') : 0);
+ grid.css('height', obj.height);
+ body.css('height', calculatedHeight);
+ box.css('height', w2utils.getSize(grid, 'height') + w2utils.getSize(box, '+height'));
+ } else {
+ // fixed body height
+ var calculatedHeight = grid.height()
+ - (this.show.header ? w2utils.getSize(header, 'height') : 0)
+ - (this.show.toolbar ? w2utils.getSize(toolbar, 'height') : 0)
+ - (summary.css('display') != 'none' ? w2utils.getSize(summary, 'height') : 0)
+ - (this.show.footer ? w2utils.getSize(footer, 'height') : 0);
+ body.css('height', calculatedHeight);
+ }
+
+ var buffered = this.records.length;
+ if (this.searchData.length != 0 && !this.url) buffered = this.last.searchIds.length;
+ // check overflow
+ var bodyOverflowX = false;
+ var bodyOverflowY = false;
+ if (body.width() < $(records).find('>table').width()) bodyOverflowX = true;
+ if (body.height() - columns.height() < $(records).find('>table').height() + (bodyOverflowX ? w2utils.scrollBarSize() : 0)) bodyOverflowY = true;
+ if (!this.fixedBody) { bodyOverflowY = false; bodyOverflowX = false; }
+ if (bodyOverflowX || bodyOverflowY) {
+ columns.find('> table > tbody > tr:nth-child(1) td.w2ui-head-last').css('width', w2utils.scrollBarSize()).show();
+ records.css({
+ top: ((this.columnGroups.length > 0 && this.show.columns ? 1 : 0) + w2utils.getSize(columns, 'height')) +'px',
+ "-webkit-overflow-scrolling": "touch",
+ "overflow-x": (bodyOverflowX ? 'auto' : 'hidden'),
+ "overflow-y": (bodyOverflowY ? 'auto' : 'hidden') });
+ } else {
+ columns.find('> table > tbody > tr:nth-child(1) td.w2ui-head-last').hide();
+ records.css({
+ top: ((this.columnGroups.length > 0 && this.show.columns ? 1 : 0) + w2utils.getSize(columns, 'height')) +'px',
+ overflow: 'hidden'
+ });
+ if (records.length > 0) { this.last.scrollTop = 0; this.last.scrollLeft = 0; } // if no scrollbars, always show top
+ }
+ if (this.show.emptyRecords && !bodyOverflowY) {
+ var max = Math.floor(records.height() / this.recordHeight) + 1;
+ if (this.fixedBody) {
+ for (var di = buffered; di <= max; di++) {
+ var html = '';
+ html += '<tr class="'+ (di % 2 ? 'w2ui-even' : 'w2ui-odd') + ' w2ui-empty-record" style="height: '+ this.recordHeight +'px">';
+ if (this.show.lineNumbers) html += '<td class="w2ui-col-number"></td>';
+ if (this.show.selectColumn) html += '<td class="w2ui-grid-data w2ui-col-select"></td>';
+ if (this.show.expandColumn) html += '<td class="w2ui-grid-data w2ui-col-expand"></td>';
+ var j = 0;
+ while (true && this.columns.length > 0) {
+ var col = this.columns[j];
+ if (col.hidden) { j++; if (typeof this.columns[j] == 'undefined') break; else continue; }
+ html += '<td class="w2ui-grid-data" '+ (typeof col.attr != 'undefined' ? col.attr : '') +' col="'+ j +'"></td>';
+ j++;
+ if (typeof this.columns[j] == 'undefined') break;
+ }
+ html += '<td class="w2ui-grid-data-last"></td>';
+ html += '</tr>';
+ $('#grid_'+ this.name +'_records > table').append(html);
+ }
+ }
+ }
+ if (body.length > 0) {
+ var width_max = parseInt(body.width())
+ - (bodyOverflowY ? w2utils.scrollBarSize() : 0)
+ - (this.show.lineNumbers ? 34 : 0)
+ - (this.show.selectColumn ? 26 : 0)
+ - (this.show.expandColumn ? 26 : 0);
+ var width_box = width_max;
+ var percent = 0;
+ // gridMinWidth processiong
+ var restart = false;
+ for (var i = 0; i < this.columns.length; i++) {
+ var col = this.columns[i];
+ if (col.gridMinWidth > 0) {
+ if (col.gridMinWidth > width_box && col.hidden !== true) {
+ col.hidden = true;
+ restart = true;
+ }
+ if (col.gridMinWidth < width_box && col.hidden === true) {
+ col.hidden = false;
+ restart = true;
+ }
+ }
+ }
+ if (restart === true) {
+ this.refresh();
+ return;
+ }
+ // assign PX column s
+ for (var i = 0; i < this.columns.length; i++) {
+ var col = this.columns[i];
+ if (col.hidden) continue;
+ if (String(col.size).substr(String(col.size).length-2).toLowerCase() == 'px') {
+ width_max -= parseFloat(col.size);
+ this.columns[i].sizeCalculated = col.size;
+ this.columns[i].sizeType = 'px';
+ } else {
+ percent += parseFloat(col.size);
+ this.columns[i].sizeType = '%';
+ delete col.sizeCorrected;
+ }
+ }
+ // if sum != 100% -- reassign proportionally
+ if (percent != 100 && percent > 0) {
+ for (var i = 0; i < this.columns.length; i++) {
+ var col = this.columns[i];
+ if (col.hidden) continue;
+ if (col.sizeType == '%') {
+ col.sizeCorrected = Math.round(parseFloat(col.size) * 100 * 100 / percent) / 100 + '%';
+ }
+ }
+ }
+ // calculate % columns
+ for (var i = 0; i < this.columns.length; i++) {
+ var col = this.columns[i];
+ if (col.hidden) continue;
+ if (col.sizeType == '%') {
+ if (typeof this.columns[i].sizeCorrected != 'undefined') {
+ // make it 1px smaller, so margin of error can be calculated correctly
+ this.columns[i].sizeCalculated = Math.floor(width_max * parseFloat(col.sizeCorrected) / 100) - 1 + 'px';
+ } else {
+ // make it 1px smaller, so margin of error can be calculated correctly
+ this.columns[i].sizeCalculated = Math.floor(width_max * parseFloat(col.size) / 100) - 1 + 'px';
+ }
+ }
+ }
+ }
+ // fix margin of error that is due percentage calculations
+ var width_cols = 0;
+ for (var i = 0; i < this.columns.length; i++) {
+ var col = this.columns[i];
+ if (col.hidden) continue;
+ if (typeof col.min == 'undefined') col.min = 20;
+ if (parseInt(col.sizeCalculated) < parseInt(col.min)) col.sizeCalculated = col.min + 'px';
+ if (parseInt(col.sizeCalculated) > parseInt(col.max)) col.sizeCalculated = col.max + 'px';
+ width_cols += parseInt(col.sizeCalculated);
+ }
+ var width_diff = parseInt(width_box) - parseInt(width_cols);
+ if (width_diff > 0 && percent > 0) {
+ var i = 0;
+ while (true) {
+ var col = this.columns[i];
+ if (typeof col == 'undefined') { i = 0; continue; }
+ if (col.hidden || col.sizeType == 'px') { i++; continue; }
+ col.sizeCalculated = (parseInt(col.sizeCalculated) + 1) + 'px';
+ width_diff--;
+ if (width_diff == 0) break;
+ i++;
+ }
+ } else if (width_diff > 0) {
+ columns.find('> table > tbody > tr:nth-child(1) td.w2ui-head-last').css('width', w2utils.scrollBarSize()).show();
+ }
+ // resize columns
+ columns.find('> table > tbody > tr:nth-child(1) td').each(function (index, el) {
+ var ind = $(el).attr('col');
+ if (typeof ind != 'undefined' && obj.columns[ind]) $(el).css('width', obj.columns[ind].sizeCalculated);
+ // last column
+ if ($(el).hasClass('w2ui-head-last')) {
+ $(el).css('width', w2utils.scrollBarSize() + (width_diff > 0 && percent == 0 ? width_diff : 0) + 'px');
+ }
+ });
+ // if there are column groups - hide first row (needed for sizing)
+ if (columns.find('> table > tbody > tr').length == 3) {
+ columns.find('> table > tbody > tr:nth-child(1) td').html('').css({
+ 'height' : '0px',
+ 'border' : '0px',
+ 'padding': '0px',
+ 'margin' : '0px'
+ });
+ }
+ // resize records
+ records.find('> table > tbody > tr:nth-child(1) td').each(function (index, el) {
+ var ind = $(el).attr('col');
+ if (typeof ind != 'undefined' && obj.columns[ind]) $(el).css('width', obj.columns[ind].sizeCalculated);
+ // last column
+ if ($(el).hasClass('w2ui-grid-data-last')) {
+ $(el).css('width', (width_diff > 0 && percent == 0 ? width_diff : 0) + 'px');
+ }
+ });
+ // resize summary
+ summary.find('> table > tbody > tr:nth-child(1) td').each(function (index, el) {
+ var ind = $(el).attr('col');
+ if (typeof ind != 'undefined' && obj.columns[ind]) $(el).css('width', obj.columns[ind].sizeCalculated);
+ // last column
+ if ($(el).hasClass('w2ui-grid-data-last')) {
+ $(el).css('width', w2utils.scrollBarSize() + (width_diff > 0 && percent == 0 ? width_diff : 0) + 'px');
+ }
+ });
+ this.initResize();
+ this.refreshRanges();
+ // apply last scroll if any
+ if (this.last.scrollTop != '' && records.length > 0) {
+ columns.prop('scrollLeft', this.last.scrollLeft);
+ records.prop('scrollTop', this.last.scrollTop);
+ records.prop('scrollLeft', this.last.scrollLeft);
+ }
+ },
+
+ getSearchesHTML: function () {
+ var html = '<table cellspacing="0">';
+ var showBtn = false;
+ for (var i = 0; i < this.searches.length; i++) {
+ var s = this.searches[i];
+ s.type = String(s.type).toLowerCase();
+ if (s.hidden) continue;
+ var btn = '';
+ if (showBtn == false) {
+ btn = '<button class="btn close-btn" onclick="obj = w2ui[\''+ this.name +'\']; if (obj) { obj.searchClose(); }">X</button';
+ showBtn = true;
+ }
+ if (typeof s.inTag == 'undefined') s.inTag = '';
+ if (typeof s.outTag == 'undefined') s.outTag = '';
+ if (typeof s.type == 'undefined') s.type = 'text';
+ if (['text', 'alphanumeric', 'combo'].indexOf(s.type) != -1) {
+ var operator = '<select id="grid_'+ this.name +'_operator_'+ i +'" onclick="event.stopPropagation();">'+
+ ' <option value="is">'+ w2utils.lang('is') +'</option>'+
+ ' <option value="begins">'+ w2utils.lang('begins') +'</option>'+
+ ' <option value="contains">'+ w2utils.lang('contains') +'</option>'+
+ ' <option value="ends">'+ w2utils.lang('ends') +'</option>'+
+ '</select>';
+ }
+ if (['int', 'float', 'money', 'currency', 'percent', 'date', 'time'].indexOf(s.type) != -1) {
+ var operator = '<select id="grid_'+ this.name +'_operator_'+ i +'" '+
+ ' onchange="w2ui[\''+ this.name + '\'].initOperator(this, '+ i +');" onclick="event.stopPropagation();">'+
+ ' <option value="is">'+ w2utils.lang('is') +'</option>'+
+ (['int'].indexOf(s.type) != -1 ? '<option value="in">'+ w2utils.lang('in') +'</option>' : '') +
+ (['int'].indexOf(s.type) != -1 ? '<option value="not in">'+ w2utils.lang('not in') +'</option>' : '') +
+ '<option value="between">'+ w2utils.lang('between') +'</option>'+
+ '</select>';
+ }
+ if (['select', 'list', 'hex'].indexOf(s.type) != -1) {
+ var operator = '<select id="grid_'+ this.name +'_operator_'+ i +'" onclick="event.stopPropagation();">'+
+ ' <option value="is">'+ w2utils.lang('is') +'</option>'+
+ '</select>';
+ }
+ if (['enum'].indexOf(s.type) != -1) {
+ var operator = '<select id="grid_'+ this.name +'_operator_'+ i +'" onclick="event.stopPropagation();">'+
+ ' <option value="in">'+ w2utils.lang('in') +'</option>'+
+ ' <option value="in">'+ w2utils.lang('not in') +'</option>'+
+ '</select>';
+ }
+ html += '<tr>'+
+ ' <td class="close-btn">'+ btn +'</td>' +
+ ' <td class="caption">'+ s.caption +'</td>' +
+ ' <td class="operator">'+ operator +'</td>'+
+ ' <td class="value">';
+
+ switch (s.type) {
+ case 'text':
+ case 'alphanumeric':
+ case 'hex':
+ case 'list':
+ case 'combo':
+ case 'enum':
+ html += '<input rel="search" type="text" style="width: 300px;" id="grid_'+ this.name +'_field_'+ i +'" name="'+ s.field +'" '+ s.inTag +'>';
+ break;
+
+ case 'int':
+ case 'float':
+ case 'money':
+ case 'currency':
+ case 'percent':
+ case 'date':
+ case 'time':
+ html += '<input rel="search" type="text" size="12" id="grid_'+ this.name +'_field_'+ i +'" name="'+ s.field +'" '+ s.inTag +'>'+
+ '<span id="grid_'+ this.name +'_range_'+ i +'" style="display: none">'+
+ '&nbsp;-&nbsp;&nbsp;<input rel="search" type="text" style="width: 90px" id="grid_'+ this.name +'_field2_'+i+'" name="'+ s.field +'" '+ s.inTag +'>'+
+ '</span>';
+ break;
+
+ case 'select':
+ html += '<select rel="search" id="grid_'+ this.name +'_field_'+ i +'" name="'+ s.field +'" '+ s.inTag +' onclick="event.stopPropagation();"></select>';
+ break;
+
+ }
+ html += s.outTag +
+ ' </td>' +
+ '</tr>';
+ }
+ html += '<tr>'+
+ ' <td colspan="4" class="actions">'+
+ ' <div>'+
+ ' <button class="btn" onclick="obj = w2ui[\''+ this.name +'\']; if (obj) { obj.searchReset(); }">'+ w2utils.lang('Reset') + '</button>'+
+ ' <button class="btn btn-blue" onclick="obj = w2ui[\''+ this.name +'\']; if (obj) { obj.search(); }">'+ w2utils.lang('Search') + '</button>'+
+ ' </div>'+
+ ' </td>'+
+ '</tr></table>';
+ return html;
+ },
+
+ initOperator: function (el, search_ind) {
+ var obj = this;
+ var search = obj.searches[search_ind];
+ var range = $('#grid_'+ obj.name + '_range_'+ search_ind);
+ var fld1 = $('#grid_'+ obj.name +'_field_'+ search_ind);
+ var fld2 = fld1.parent().find('span input');
+ if ($(el).val() == 'in' || $(el).val() == 'not in') { fld1.w2field('clear'); } else { fld1.w2field(search.type); }
+ if ($(el).val() == 'between') { range.show(); fld2.w2field(search.type); } else { range.hide(); }
+ },
+
+ initSearches: function () {
+ var obj = this;
+ // init searches
+ for (var s in this.searches) {
+ var search = this.searches[s];
+ var sdata = this.getSearchData(search.field);
+ search.type = String(search.type).toLowerCase();
+ if (typeof search.options != 'object') search.options = {};
+ // init types
+ switch (search.type) {
+ case 'text':
+ case 'alphanumeric':
+ $('#grid_'+ this.name +'_operator_'+s).val('begins');
+ if (['alphanumeric', 'hex'].indexOf(search.type) != -1) {
+ $('#grid_'+ this.name +'_field_' + s).w2field(search.type, search.options);
+ }
+ break;
+
+ case 'int':
+ case 'float':
+ case 'money':
+ case 'currency':
+ case 'percent':
+ case 'date':
+ case 'time':
+ if (sdata && sdata.type == 'int' && ['in', 'not in'].indexOf(sdata.operator) != -1) break;
+ $('#grid_'+ this.name +'_field_'+s).w2field(search.type, search.options);
+ $('#grid_'+ this.name +'_field2_'+s).w2field(search.type, search.options);
+ setTimeout(function () { // convert to date if it is number
+ $('#grid_'+ obj.name +'_field_'+s).keydown();
+ $('#grid_'+ obj.name +'_field2_'+s).keydown();
+ }, 1);
+ break;
+
+ case 'hex':
+ break;
+
+ case 'list':
+ case 'combo':
+ case 'enum':
+ var options = search.options;
+ if (search.type == 'list') options.selected = {};
+ if (search.type == 'enum') options.selected = [];
+ if (sdata) options.selected = sdata.value;
+ $('#grid_'+ this.name +'_field_'+s).w2field(search.type, options);
+ if (search.type == 'combo') {
+ $('#grid_'+ this.name +'_operator_'+s).val('begins');
+ }
+ break;
+
+ case 'select':
+ // build options
+ var options = '<option value="">--</option>';
+ for (var i in search.options.items) {
+ var si = search.options.items[i];
+ if ($.isPlainObject(search.options.items[i])) {
+ var val = si.id;
+ var txt = si.text;
+ if (typeof val == 'undefined' && typeof si.value != 'undefined') val = si.value;
+ if (typeof txt == 'undefined' && typeof si.caption != 'undefined') txt = si.caption;
+ if (val == null) val = '';
+ options += '<option value="'+ val +'">'+ txt +'</option>';
+ } else {
+ options += '<option value="'+ si +'">'+ si +'</option>';
+ }
+ }
+ $('#grid_'+ this.name +'_field_'+s).html(options);
+ break;
+ }
+ if (sdata != null) {
+ if (sdata.type == 'int' && ['in', 'not in'].indexOf(sdata.operator) != -1) {
+ $('#grid_'+ this.name +'_field_'+ s).w2field('clear').val(sdata.value);
+ }
+ $('#grid_'+ this.name +'_operator_'+ s).val(sdata.operator).trigger('change');
+ if (!$.isArray(sdata.value)) {
+ if (typeof sdata.value != 'udefined') $('#grid_'+ this.name +'_field_'+ s).val(sdata.value).trigger('change');
+ } else {
+ if (['in', 'not in'].indexOf(sdata.operator) != -1) {
+ $('#grid_'+ this.name +'_field_'+ s).val(sdata.value).trigger('change');
+ } else {
+ $('#grid_'+ this.name +'_field_'+ s).val(sdata.value[0]).trigger('change');
+ $('#grid_'+ this.name +'_field2_'+ s).val(sdata.value[1]).trigger('change');
+ }
+ }
+ }
+ }
+ // add on change event
+ $('#w2ui-overlay-searches-'+ this.name +' .w2ui-grid-searches *[rel=search]').on('keypress', function (evnt) {
+ if (evnt.keyCode == 13) {
+ obj.search();
+ $().w2overlay();
+ }
+ });
+ },
+
+ getColumnsHTML: function () {
+ var obj = this;
+ var html = '';
+ if (this.show.columnHeaders) {
+ if (this.columnGroups.length > 0) {
+ html = getColumns(true) + getGroups() + getColumns(false);
+ } else {
+ html = getColumns(true);
+ }
+ }
+ return html;
+
+ function getGroups () {
+ var html = '<tr>';
+ // add empty group at the end
+ if (obj.columnGroups[obj.columnGroups.length-1].caption != '') obj.columnGroups.push({ caption: '' });
+
+ if (obj.show.lineNumbers) {
+ html += '<td class="w2ui-head w2ui-col-number">'+
+ ' <div>&nbsp;</div>'+
+ '</td>';
+ }
+ if (obj.show.selectColumn) {
+ html += '<td class="w2ui-head w2ui-col-select">'+
+ ' <div>&nbsp;</div>'+
+ '</td>';
+ }
+ if (obj.show.expandColumn) {
+ html += '<td class="w2ui-head w2ui-col-expand">'+
+ ' <div>&nbsp;</div>'+
+ '</td>';
+ }
+ var ii = 0;
+ for (var i=0; i<obj.columnGroups.length; i++) {
+ var colg = obj.columnGroups[i];
+ var col = obj.columns[ii];
+ if (typeof colg.span == 'undefined' || colg.span != parseInt(colg.span)) colg.span = 1;
+ if (typeof colg.colspan != 'undefined') colg.span = colg.colspan;
+ if (colg.master === true) {
+ var sortStyle = '';
+ for (var si in obj.sortData) {
+ if (obj.sortData[si].field == col.field) {
+ if (RegExp('asc', 'i').test(obj.sortData[si].direction)) sortStyle = 'w2ui-sort-up';
+ if (RegExp('desc', 'i').test(obj.sortData[si].direction)) sortStyle = 'w2ui-sort-down';
+ }
+ }
+ var resizer = "";
+ if (col.resizable !== false) {
+ resizer = '<div class="w2ui-resizer" name="'+ ii +'"></div>';
+ }
+ html += '<td class="w2ui-head '+ sortStyle +'" col="'+ ii + '" rowspan="2" colspan="'+ (colg.span + (i == obj.columnGroups.length-1 ? 1 : 0) ) +'" '+
+ ' onclick="w2ui[\''+ obj.name +'\'].columnClick(\''+ col.field +'\', event);">'+
+ resizer +
+ ' <div class="w2ui-col-group w2ui-col-header '+ (sortStyle ? 'w2ui-col-sorted' : '') +'">'+
+ ' <div class="'+ sortStyle +'"></div>'+
+ (!col.caption ? '&nbsp;' : col.caption) +
+ ' </div>'+
+ '</td>';
+ } else {
+ html += '<td class="w2ui-head" col="'+ ii + '" '+
+ ' colspan="'+ (colg.span + (i == obj.columnGroups.length-1 ? 1 : 0) ) +'">'+
+ ' <div class="w2ui-col-group">'+
+ (!colg.caption ? '&nbsp;' : colg.caption) +
+ ' </div>'+
+ '</td>';
+ }
+ ii += colg.span;
+ }
+ html += '</tr>';
+ return html;
+ }
+
+ function getColumns (master) {
+ var html = '<tr>',
+ reorderCols = (obj.reorderColumns && (!obj.columnGroups || !obj.columnGroups.length)) ? ' w2ui-reorder-cols-head ' : '';
+ if (obj.show.lineNumbers) {
+ html += '<td class="w2ui-head w2ui-col-number" onclick="w2ui[\''+ obj.name +'\'].columnClick(\'line-number\', event);">'+
+ ' <div>#</div>'+
+ '</td>';
+ }
+ if (obj.show.selectColumn) {
+ html += '<td class="w2ui-head w2ui-col-select" '+
+ ' onclick="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;">'+
+ ' <div>'+
+ ' <input type="checkbox" id="grid_'+ obj.name +'_check_all" tabIndex="-1"'+
+ ' style="' + (obj.multiSelect == false ? 'display: none;' : '') + '"'+
+ ' onclick="if (this.checked) w2ui[\''+ obj.name +'\'].selectAll(); '+
+ ' else w2ui[\''+ obj.name +'\'].selectNone(); '+
+ ' if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;">'+
+ ' </div>'+
+ '</td>';
+ }
+ if (obj.show.expandColumn) {
+ html += '<td class="w2ui-head w2ui-col-expand">'+
+ ' <div>&nbsp;</div>'+
+ '</td>';
+ }
+ var ii = 0;
+ var id = 0;
+ for (var i=0; i<obj.columns.length; i++) {
+ var col = obj.columns[i];
+ var colg = {};
+ if (i == id) {
+ id = id + (typeof obj.columnGroups[ii] != 'undefined' ? parseInt(obj.columnGroups[ii].span) : 0);
+ ii++;
+ }
+ if (typeof obj.columnGroups[ii-1] != 'undefined') var colg = obj.columnGroups[ii-1];
+ if (col.hidden) continue;
+ var sortStyle = '';
+ for (var si in obj.sortData) {
+ if (obj.sortData[si].field == col.field) {
+ if (RegExp('asc', 'i').test(obj.sortData[si].direction)) sortStyle = 'w2ui-sort-up';
+ if (RegExp('desc', 'i').test(obj.sortData[si].direction)) sortStyle = 'w2ui-sort-down';
+ }
+ }
+ if (colg['master'] !== true || master) { // grouping of columns
+ var resizer = "";
+ if (col.resizable !== false) {
+ resizer = '<div class="w2ui-resizer" name="'+ i +'"></div>';
+ }
+ html += '<td col="'+ i +'" class="w2ui-head '+ sortStyle + reorderCols + '" ' +
+ ' onclick="w2ui[\''+ obj.name +'\'].columnClick(\''+ col.field +'\', event);">'+
+ resizer +
+ ' <div class="w2ui-col-header '+ (sortStyle ? 'w2ui-col-sorted' : '') +'">'+
+ ' <div class="'+ sortStyle +'"></div>'+
+ (!col.caption ? '&nbsp;' : col.caption) +
+ ' </div>'+
+ '</td>';
+ }
+ }
+ html += '<td class="w2ui-head w2ui-head-last"><div>&nbsp;</div></td>';
+ html += '</tr>';
+ return html;
+ }
+ },
+
+ getRecordsHTML: function () {
+ var buffered = this.records.length;
+ if (this.searchData.length != 0 && !this.url) buffered = this.last.searchIds.length;
+ // larget number works better with chrome, smaller with FF.
+ if (buffered > 300) this.show_extra = 30; else this.show_extra = 300;
+ var records = $('#grid_'+ this.name +'_records');
+ var limit = Math.floor(records.height() / this.recordHeight) + this.show_extra + 1;
+ if (!this.fixedBody || limit > buffered) limit = buffered;
+ // always need first record for resizing purposes
+ var html = '<table>' + this.getRecordHTML(-1, 0);
+ // first empty row with height
+ html += '<tr id="grid_'+ this.name + '_rec_top" line="top" style="height: '+ 0 +'px">'+
+ ' <td colspan="200"></td>'+
+ '</tr>';
+ for (var i = 0; i < limit; i++) {
+ html += this.getRecordHTML(i, i+1);
+ }
+ html += '<tr id="grid_'+ this.name + '_rec_bottom" line="bottom" style="height: '+ ((buffered - limit) * this.recordHeight) +'px">'+
+ ' <td colspan="200"></td>'+
+ '</tr>'+
+ '<tr id="grid_'+ this.name +'_rec_more" style="display: none">'+
+ ' <td colspan="200" class="w2ui-load-more"></td>'+
+ '</tr>'+
+ '</table>';
+ this.last.range_start = 0;
+ this.last.range_end = limit;
+ return html;
+ },
+
+ getSummaryHTML: function () {
+ if (this.summary.length == 0) return;
+ var html = '<table>';
+ for (var i = 0; i < this.summary.length; i++) {
+ html += this.getRecordHTML(i, i+1, true);
+ }
+ html += '</table>';
+ return html;
+ },
+
+ scroll: function (event) {
+ var time = (new Date()).getTime();
+ var obj = this;
+ var records = $('#grid_'+ this.name +'_records');
+ var buffered = this.records.length;
+ if (this.searchData.length != 0 && !this.url) buffered = this.last.searchIds.length;
+ if (buffered == 0 || records.length == 0 || records.height() == 0) return;
+ if (buffered > 300) this.show_extra = 30; else this.show_extra = 300;
+ // need this to enable scrolling when this.limit < then a screen can fit
+ if (records.height() < buffered * this.recordHeight && records.css('overflow-y') == 'hidden') {
+ if (this.total > 0) this.refresh();
+ return;
+ }
+ // update footer
+ var t1 = Math.round(records[0].scrollTop / this.recordHeight + 1);
+ var t2 = t1 + (Math.round(records.height() / this.recordHeight) - 1);
+ if (t1 > buffered) t1 = buffered;
+ if (t2 > buffered) t2 = buffered;
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ $('#grid_'+ this.name + '_footer .w2ui-footer-right').html(w2utils.formatNumber(this.offset + t1) + '-' + w2utils.formatNumber(this.offset + t2) + ' ' + w2utils.lang('of') + ' ' + w2utils.formatNumber(this.total) +
+ (url ? ' ('+ w2utils.lang('buffered') + ' '+ w2utils.formatNumber(buffered) + (this.offset > 0 ? ', skip ' + w2utils.formatNumber(this.offset) : '') + ')' : '')
+ );
+ // only for local data source, else no extra records loaded
+ if (!url && (!this.fixedBody || this.total <= 300)) return;
+ // regular processing
+ var start = Math.floor(records[0].scrollTop / this.recordHeight) - this.show_extra;
+ var end = start + Math.floor(records.height() / this.recordHeight) + this.show_extra * 2 + 1;
+ // var div = start - this.last.range_start;
+ if (start < 1) start = 1;
+ if (end > this.total) end = this.total;
+ var tr1 = records.find('#grid_'+ this.name +'_rec_top');
+ var tr2 = records.find('#grid_'+ this.name +'_rec_bottom');
+ // if row is expanded
+ if (String(tr1.next().prop('id')).indexOf('_expanded_row') != -1) tr1.next().remove();
+ if (this.total > end && String(tr2.prev().prop('id')).indexOf('_expanded_row') != -1) tr2.prev().remove();
+ var first = parseInt(tr1.next().attr('line'));
+ var last = parseInt(tr2.prev().attr('line'));
+ //$('#log').html('buffer: '+ this.buffered +' start-end: ' + start + '-'+ end + ' ===> first-last: ' + first + '-' + last);
+ if (first < start || first == 1 || this.last.pull_refresh) { // scroll down
+ // console.log('end', end, 'last', last, 'show_extre', this.show_extra, this.last.pull_refresh);
+ if (end <= last + this.show_extra - 2 && end != this.total) return;
+ this.last.pull_refresh = false;
+ // remove from top
+ while (true) {
+ var tmp = records.find('#grid_'+ this.name +'_rec_top').next();
+ if (tmp.attr('line') == 'bottom') break;
+ if (parseInt(tmp.attr('line')) < start) tmp.remove(); else break;
+ }
+ // add at bottom
+ var tmp = records.find('#grid_'+ this.name +'_rec_bottom').prev();
+ var rec_start = tmp.attr('line');
+ if (rec_start == 'top') rec_start = start;
+ for (var i = parseInt(rec_start) + 1; i <= end; i++) {
+ if (!this.records[i-1]) continue;
+ if (this.records[i-1].expanded === true) this.records[i-1].expanded = false;
+ tr2.before(this.getRecordHTML(i-1, i));
+ }
+ markSearch();
+ setTimeout(function() { obj.refreshRanges(); }, 0);
+ } else { // scroll up
+ if (start >= first - this.show_extra + 2 && start > 1) return;
+ // remove from bottom
+ while (true) {
+ var tmp = records.find('#grid_'+ this.name +'_rec_bottom').prev();
+ if (tmp.attr('line') == 'top') break;
+ if (parseInt(tmp.attr('line')) > end) tmp.remove(); else break;
+ }
+ // add at top
+ var tmp = records.find('#grid_'+ this.name +'_rec_top').next();
+ var rec_start = tmp.attr('line');
+ if (rec_start == 'bottom') rec_start = end;
+ for (var i = parseInt(rec_start) - 1; i >= start; i--) {
+ if (!this.records[i-1]) continue;
+ if (this.records[i-1].expanded === true) this.records[i-1].expanded = false;
+ tr1.after(this.getRecordHTML(i-1, i));
+ }
+ markSearch();
+ setTimeout(function() { obj.refreshRanges(); }, 0);
+ }
+ // first/last row size
+ var h1 = (start - 1) * obj.recordHeight;
+ var h2 = (buffered - end) * obj.recordHeight;
+ if (h2 < 0) h2 = 0;
+ tr1.css('height', h1 + 'px');
+ tr2.css('height', h2 + 'px');
+ obj.last.range_start = start;
+ obj.last.range_end = end;
+ // load more if needed
+ var s = Math.floor(records[0].scrollTop / this.recordHeight);
+ var e = s + Math.floor(records.height() / this.recordHeight);
+ if (e + 10 > buffered && this.last.pull_more !== true && buffered < this.total - this.offset) {
+ if (this.autoLoad === true) {
+ this.last.pull_more = true;
+ this.last.xhr_offset += this.limit;
+ this.request('get-records');
+ } else {
+ var more = $('#grid_'+ this.name +'_rec_more');
+ if (more.css('display') == 'none') {
+ more.show()
+ .on('click', function () {
+ obj.last.pull_more = true;
+ obj.last.xhr_offset += obj.limit;
+ obj.request('get-records');
+ // show spinner the last
+ $(this).find('td').html('<div><div style="width: 20px; height: 20px;" class="w2ui-spinner"></div></div>');
+ });
+ }
+ if (more.find('td').text().indexOf('Load') == -1) {
+ more.find('td').html('<div>'+ w2utils.lang('Load') + ' ' + obj.limit + ' ' + w2utils.lang('More') + '...</div>');
+ }
+ }
+ }
+ // check for grid end
+ if (buffered >= this.total - this.offset) $('#grid_'+ this.name +'_rec_more').hide();
+ return;
+
+ function markSearch() {
+ // mark search
+ if(obj.markSearch === false) return;
+ clearTimeout(obj.last.marker_timer);
+ obj.last.marker_timer = setTimeout(function () {
+ // mark all search strings
+ var str = [];
+ for (var s in obj.searchData) {
+ var tmp = obj.searchData[s];
+ if ($.inArray(tmp.value, str) == -1) str.push(tmp.value);
+ }
+ if (str.length > 0) $(obj.box).find('.w2ui-grid-data > div').w2marker(str);
+ }, 50);
+ }
+ },
+
+ getRecordHTML: function (ind, lineNum, summary) {
+ var rec_html = '';
+ var sel = this.last.selection;
+ var record;
+ // first record needs for resize purposes
+ if (ind == -1) {
+ rec_html += '<tr line="0">';
+ if (this.show.lineNumbers) rec_html += '<td class="w2ui-col-number" style="height: 0px;"></td>';
+ if (this.show.selectColumn) rec_html += '<td class="w2ui-col-select" style="height: 0px;"></td>';
+ if (this.show.expandColumn) rec_html += '<td class="w2ui-col-expand" style="height: 0px;"></td>';
+ for (var i in this.columns) {
+ if (this.columns[i].hidden) continue;
+ rec_html += '<td class="w2ui-grid-data" col="'+ i +'" style="height: 0px;"></td>';
+ }
+ rec_html += '<td class="w2ui-grid-data-last" style="height: 0px;"></td>';
+ rec_html += '</tr>';
+ return rec_html;
+ }
+ // regular record
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (summary !== true) {
+ if (this.searchData.length > 0 && !url) {
+ if (ind >= this.last.searchIds.length) return '';
+ ind = this.last.searchIds[ind];
+ record = this.records[ind];
+ } else {
+ if (ind >= this.records.length) return '';
+ record = this.records[ind];
+ }
+ } else {
+ if (ind >= this.summary.length) return '';
+ record = this.summary[ind];
+ }
+ if (!record) return '';
+ var id = w2utils.escapeId(record.recid);
+ var isRowSelected = false;
+ if (sel.indexes.indexOf(ind) != -1) isRowSelected = true;
+ // render TR
+ rec_html += '<tr id="grid_'+ this.name +'_rec_'+ record.recid +'" recid="'+ record.recid +'" line="'+ lineNum +'" '+
+ ' class="'+ (lineNum % 2 == 0 ? 'w2ui-even' : 'w2ui-odd') + (isRowSelected && this.selectType == 'row' ? ' w2ui-selected' : '') + (record.expanded === true ? ' w2ui-expanded' : '') + '" ' +
+ (summary !== true ?
+ (w2utils.isIOS ?
+ ' onclick = "w2ui[\''+ this.name +'\'].dblClick(\''+ record.recid +'\', event);"'
+ :
+ ' onclick = "w2ui[\''+ this.name +'\'].click(\''+ record.recid +'\', event);"'+
+ ' oncontextmenu = "w2ui[\''+ this.name +'\'].contextMenu(\''+ record.recid +'\', event);"'
+ )
+ : ''
+ ) +
+ ' style="height: '+ this.recordHeight +'px; '+ (!isRowSelected && typeof record['style'] == 'string' ? record['style'] : '') +'" '+
+ ( typeof record['style'] == 'string' ? 'custom_style="'+ record['style'] +'"' : '') +
+ '>';
+ if (this.show.lineNumbers) {
+ rec_html += '<td id="grid_'+ this.name +'_cell_'+ ind +'_number' + (summary ? '_s' : '') + '" class="w2ui-col-number">'+
+ (summary !== true ? '<div>'+ lineNum +'</div>' : '') +
+ '</td>';
+ }
+ if (this.show.selectColumn) {
+ rec_html +=
+ '<td id="grid_'+ this.name +'_cell_'+ ind +'_select' + (summary ? '_s' : '') + '" class="w2ui-grid-data w2ui-col-select" '+
+ ' onclick="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;">'+
+ (summary !== true ?
+ ' <div>'+
+ ' <input class="w2ui-grid-select-check" type="checkbox" tabIndex="-1"'+
+ ' '+ (isRowSelected ? 'checked="checked"' : '') +
+ ' onclick="var obj = w2ui[\''+ this.name +'\']; '+
+ ' if (!obj.multiSelect) { obj.selectNone(); }'+
+ ' if (this.checked) obj.select(\''+ record.recid + '\'); else obj.unselect(\''+ record.recid + '\'); '+
+ ' if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;">'+
+ ' </div>'
+ :
+ '' ) +
+ '</td>';
+ }
+ if (this.show.expandColumn) {
+ var tmp_img = '';
+ if (record.expanded === true) tmp_img = '-'; else tmp_img = '+';
+ if (record.expanded == 'none') tmp_img = '';
+ if (record.expanded == 'spinner') tmp_img = '<div class="w2ui-spinner" style="width: 16px; margin: -2px 2px;"></div>';
+ rec_html +=
+ '<td id="grid_'+ this.name +'_cell_'+ ind +'_expand' + (summary ? '_s' : '') + '" class="w2ui-grid-data w2ui-col-expand">'+
+ (summary !== true ?
+ ' <div ondblclick="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;" '+
+ ' onclick="w2ui[\''+ this.name +'\'].toggle(\''+ record.recid +'\', event); '+
+ ' if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;">'+
+ ' '+ tmp_img +' </div>'
+ :
+ '' ) +
+ '</td>';
+ }
+ var col_ind = 0;
+ while (true) {
+ var col = this.columns[col_ind];
+ if (col.hidden) { col_ind++; if (typeof this.columns[col_ind] == 'undefined') break; else continue; }
+ var isChanged = !summary && record.changes && typeof record.changes[col.field] != 'undefined';
+ var rec_cell = this.getCellHTML(ind, col_ind, summary);
+ var addStyle = '';
+ if (typeof col.render == 'string') {
+ var tmp = col.render.toLowerCase().split(':');
+ if (['number', 'int', 'float', 'money', 'currency', 'percent'].indexOf(tmp[0]) != -1) addStyle += 'text-align: right;';
+ }
+ if (typeof record.style == 'object' && typeof record.style[col_ind] == 'string') {
+ addStyle += record.style[col_ind] + ';';
+ }
+ var isCellSelected = false;
+ if (isRowSelected && $.inArray(col_ind, sel.columns[ind]) != -1) isCellSelected = true;
+ rec_html += '<td class="w2ui-grid-data'+ (isCellSelected ? ' w2ui-selected' : '') + (isChanged ? ' w2ui-changed' : '') +'" col="'+ col_ind +'" '+
+ ' style="'+ addStyle + (typeof col.style != 'undefined' ? col.style : '') +'" '+
+ (typeof col.attr != 'undefined' ? col.attr : '') +'>'+
+ rec_cell +
+ '</td>';
+ col_ind++;
+ if (typeof this.columns[col_ind] == 'undefined') break;
+ }
+ rec_html += '<td class="w2ui-grid-data-last"></td>';
+ rec_html += '</tr>';
+ return rec_html;
+ },
+
+ getCellHTML: function (ind, col_ind, summary) {
+ var col = this.columns[col_ind];
+ var record = (summary !== true ? this.records[ind] : this.summary[ind]);
+ var data = this.getCellValue(ind, col_ind, summary);
+ var edit = col.editable;
+ // various renderers
+ if (typeof col.render != 'undefined') {
+ if (typeof col.render == 'function') {
+ data = $.trim(col.render.call(this, record, ind, col_ind));
+ if (data.length < 4 || data.substr(0, 4).toLowerCase() != '<div') data = '<div>' + data + '</div>';
+ }
+ if (typeof col.render == 'object') data = '<div>' + (col.render[data] || '') + '</div>';
+ if (typeof col.render == 'string') {
+ var tmp = col.render.toLowerCase().split(':');
+ var prefix = '';
+ var suffix = '';
+ if (['number', 'int', 'float', 'money', 'currency', 'percent'].indexOf(tmp[0]) != -1) {
+ if (typeof tmp[1] == 'undefined' || !w2utils.isInt(tmp[1])) tmp[1] = 0;
+ if (tmp[1] > 20) tmp[1] = 20;
+ if (tmp[1] < 0) tmp[1] = 0;
+ if (['money', 'currency'].indexOf(tmp[0]) != -1) { tmp[1] = w2utils.settings.currencyPrecision; prefix = w2utils.settings.currencyPrefix; suffix = w2utils.settings.currencySuffix }
+ if (tmp[0] == 'percent') { suffix = '%'; if (tmp[1] !== '0') tmp[1] = 1; }
+ if (tmp[0] == 'int') { tmp[1] = 0; }
+ // format
+ data = '<div>' + (data !== '' ? prefix + w2utils.formatNumber(Number(data).toFixed(tmp[1])) + suffix : '') + '</div>';
+ }
+ if (tmp[0] == 'time') {
+ if (typeof tmp[1] == 'undefined' || tmp[1] == '') tmp[1] = w2utils.settings.time_format;
+ data = '<div>' + prefix + w2utils.formatTime(data, tmp[1] == 'h12' ? 'hh:mi pm': 'h24:min') + suffix + '</div>';
+ }
+ if (tmp[0] == 'date') {
+ if (typeof tmp[1] == 'undefined' || tmp[1] == '') tmp[1] = w2utils.settings.date_display;
+ data = '<div>' + prefix + w2utils.formatDate(data, tmp[1]) + suffix + '</div>';
+ }
+ if (tmp[0] == 'age') {
+ data = '<div>' + prefix + w2utils.age(data) + suffix + '</div>';
+ }
+ if (tmp[0] == 'toggle') {
+ data = '<div>' + prefix + (data ? 'Yes' : '') + suffix + '</div>';
+ }
+ }
+ } else {
+ // if editable checkbox
+ var addStyle = '';
+ if (edit && ['checkbox', 'check'].indexOf(edit.type) != -1) {
+ var changeInd = summary ? -(ind + 1) : ind;
+ addStyle = 'text-align: center';
+ data = '<input type="checkbox" '+ (data ? 'checked' : '') +' onclick="' +
+ ' var obj = w2ui[\''+ this.name + '\']; '+
+ ' obj.editChange.call(obj, this, '+ changeInd +', '+ col_ind +', event); ' +
+ '">';
+ }
+ if (!this.show.recordTitles) {
+ var data = '<div style="'+ addStyle +'">'+ data +'</div>';
+ } else {
+ // title overwrite
+ var title = String(data).replace(/"/g, "''");
+ if (typeof col.title != 'undefined') {
+ if (typeof col.title == 'function') title = col.title.call(this, record, ind, col_ind);
+ if (typeof col.title == 'string') title = col.title;
+ }
+ var data = '<div title="'+ w2utils.stripTags(title) +'" style="'+ addStyle +'">'+ data +'</div>';
+ }
+ }
+ if (data == null || typeof data == 'undefined') data = '';
+ return data;
+ },
+
+ getCellValue: function (ind, col_ind, summary) {
+ var col = this.columns[col_ind];
+ var record = (summary !== true ? this.records[ind] : this.summary[ind]);
+ var data = this.parseField(record, col.field);
+ if (record.changes && typeof record.changes[col.field] != 'undefined') data = record.changes[col.field];
+ if (data == null || typeof data == 'undefined') data = '';
+ return data;
+ },
+
+ getFooterHTML: function () {
+ return '<div>'+
+ ' <div class="w2ui-footer-left"></div>'+
+ ' <div class="w2ui-footer-right"></div>'+
+ ' <div class="w2ui-footer-center"></div>'+
+ '</div>';
+ },
+
+ status: function (msg) {
+ if (typeof msg != 'undefined') {
+ $('#grid_'+ this.name +'_footer').find('.w2ui-footer-left').html(msg);
+ } else {
+ // show number of selected
+ var msgLeft = '';
+ var sel = this.getSelection();
+ if (sel.length > 0) {
+ msgLeft = String(sel.length).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,") + ' ' + w2utils.lang('selected');
+ var tmp = sel[0];
+ if (typeof tmp == 'object') tmp = tmp.recid + ', '+ w2utils.lang('Column') +': '+ tmp.column;
+ if (sel.length == 1) msgLeft = w2utils.lang('Record ID') + ': '+ tmp + ' ';
+ }
+ $('#grid_'+ this.name +'_footer .w2ui-footer-left').html(msgLeft);
+ // toolbar
+ if (sel.length == 1) this.toolbar.enable('w2ui-edit'); else this.toolbar.disable('w2ui-edit');
+ if (sel.length >= 1) this.toolbar.enable('w2ui-delete'); else this.toolbar.disable('w2ui-delete');
+ }
+ },
+
+ lock: function (msg, showSpinner) {
+ var box = $(this.box).find('> div:first-child');
+ var args = Array.prototype.slice.call(arguments, 0);
+ args.unshift(box);
+ setTimeout(function () { w2utils.lock.apply(window, args); }, 10);
+ },
+
+ unlock: function () {
+ var box = this.box;
+ setTimeout(function () { w2utils.unlock(box); }, 25); // needed timer so if server fast, it will not flash
+ },
+
+ stateSave: function (returnOnly) {
+ if (!localStorage) return null;
+ var state = {
+ columns : [],
+ show : $.extend({}, this.show),
+ last : {
+ search : this.last.search,
+ multi : this.last.multi,
+ logic : this.last.logic,
+ caption : this.last.caption,
+ field : this.last.field,
+ scrollTop : this.last.scrollTop,
+ scrollLeft : this.last.scrollLeft
+ },
+ sortData : [],
+ searchData : []
+ };
+ for (var i in this.columns) {
+ var col = this.columns[i];
+ state.columns.push({
+ field : col.field,
+ hidden : col.hidden,
+ size : col.size,
+ sizeCalculated : col.sizeCalculated,
+ sizeOriginal : col.sizeOriginal,
+ sizeType : col.sizeType
+ });
+ }
+ for (var i in this.sortData) state.sortData.push($.extend({}, this.sortData[i]));
+ for (var i in this.searchData) state.searchData.push($.extend({}, this.searchData[i]));
+ // save into local storage
+ if (returnOnly !== true) {
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'stateSave', target: this.name, state: state });
+ if (eventData.isCancelled === true) { if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' }); return; }
+ try {
+ var savedState = $.parseJSON(localStorage.w2ui || '{}');
+ if (!savedState) savedState = {};
+ if (!savedState.states) savedState.states = {};
+ savedState.states[this.name] = state;
+ localStorage.w2ui = JSON.stringify(savedState);
+ } catch (e) {
+ delete localStorage.w2ui;
+ return null;
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ return state;
+ },
+
+ stateRestore: function (newState) {
+ var obj = this;
+ if (!newState) {
+ // read it from local storage
+ try {
+ if (!localStorage) return false;
+ var tmp = $.parseJSON(localStorage.w2ui || '{}');
+ if (!tmp) tmp = {};
+ if (!tmp.states) tmp.states = {};
+ newState = tmp.states[this.name];
+ } catch (e) {
+ delete localStorage.w2ui;
+ return null;
+ }
+ }
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'stateRestore', target: this.name, state: newState });
+ if (eventData.isCancelled === true) { if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' }); return; }
+ // default behavior
+ if ($.isPlainObject(newState)) {
+ $.extend(this.show, newState.show);
+ $.extend(this.last, newState.last);
+ var sTop = this.last.scrollTop;
+ var sLeft = this.last.scrollLeft;
+ for (var c in newState.columns) {
+ var tmp = newState.columns[c];
+ var col = this.getColumn(tmp.field);
+ if (col) $.extend(col, tmp);
+ }
+ this.sortData.splice(0, this.sortData.length);
+ for (var c in newState.sortData) this.sortData.push(newState.sortData[c]);
+ this.searchData.splice(0, this.searchData.length);
+ for (var c in newState.searchData) this.searchData.push(newState.searchData[c]);
+ // apply sort and search
+ setTimeout(function () {
+ // needs timeout as records need to be populated
+ if (obj.sortData.length > 0) obj.localSort();
+ if (obj.searchData.length > 0) obj.localSearch();
+ obj.last.scrollTop = sTop;
+ obj.last.scrollLeft = sLeft;
+ obj.refresh();
+ }, 1);
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return true;
+ },
+
+ stateReset: function () {
+ this.stateRestore(this.last.state);
+ // remove from local storage
+ if (localStorage) {
+ try {
+ var tmp = $.parseJSON(localStorage.w2ui || '{}');
+ if (tmp.states && tmp.states[this.name]) {
+ delete tmp.states[this.name];
+ }
+ localStorage.w2ui = JSON.stringify(tmp);
+ } catch (e) {
+ delete localStorage.w2ui;
+ return null;
+ }
+ }
+ },
+
+ parseField: function (obj, field) {
+ var val = '';
+ try { // need this to make sure no error in fields
+ val = obj;
+ var tmp = String(field).split('.');
+ for (var i in tmp) {
+ val = val[tmp[i]];
+ }
+ } catch (event) {
+ val = '';
+ }
+ return val;
+ },
+
+ prepareData: function () {
+ // loops thru records and prepares date and time objects
+ for (var r in this.records) {
+ var rec = this.records[r];
+ for (var c in this.columns) {
+ var column = this.columns[c];
+ if (rec[column.field] == null || typeof column.render != 'string') continue;
+ // number
+ if (['number', 'int', 'float', 'money', 'currency', 'percent'].indexOf(column.render.split(':')[0]) != -1) {
+ if (typeof rec[column.field] != 'number') rec[column.field] = parseFloat(rec[column.field]);
+ }
+ // date
+ if (['date', 'age'].indexOf(column.render.split(':')[0]) != -1) {
+ if (!rec[column.field + '_']) {
+ var dt = rec[column.field];
+ if (w2utils.isInt(dt)) dt = parseInt(dt);
+ rec[column.field + '_'] = new Date(dt);
+ }
+ }
+ // time
+ if (['time'].indexOf(column.render) != -1) {
+ if (w2utils.isTime(rec[column.field])) { // if string
+ var tmp = w2utils.isTime(rec[column.field], true);
+ var dt = new Date();
+ dt.setHours(tmp.hours, tmp.minutes, (tmp.seconds ? tmp.seconds : 0), 0); // sets hours, min, sec, mills
+ if (!rec[column.field + '_']) rec[column.field + '_'] = dt;
+ } else { // if date object
+ var tmp = rec[column.field];
+ if (w2utils.isInt(tmp)) tmp = parseInt(tmp);
+ var tmp = (tmp != null ? new Date(tmp) : new Date());
+ var dt = new Date();
+ dt.setHours(tmp.getHours(), tmp.getMinutes(), tmp.getSeconds(), 0); // sets hours, min, sec, mills
+ if (!rec[column.field + '_']) rec[column.field + '_'] = dt;
+ }
+ }
+ }
+ }
+ },
+
+ nextCell: function (col_ind, editable) {
+ var check = col_ind + 1;
+ if (this.columns.length == check) return null;
+ if (editable === true) {
+ var edit = this.columns[check].editable;
+ if (this.columns[check].hidden || typeof edit == 'undefined'
+ || (edit && ['checkbox', 'check'].indexOf(edit.type) != -1)) return this.nextCell(check, editable);
+ }
+ return check;
+ },
+
+ prevCell: function (col_ind, editable) {
+ var check = col_ind - 1;
+ if (check < 0) return null;
+ if (editable === true) {
+ var edit = this.columns[check].editable;
+ if (this.columns[check].hidden || typeof edit == 'undefined'
+ || (edit && ['checkbox', 'check'].indexOf(edit.type) != -1)) return this.prevCell(check, editable);
+ }
+ return check;
+ },
+
+ nextRow: function (ind) {
+ if ((ind + 1 < this.records.length && this.last.searchIds.length == 0) // if there are more records
+ || (this.last.searchIds.length > 0 && ind < this.last.searchIds[this.last.searchIds.length-1])) {
+ ind++;
+ if (this.last.searchIds.length > 0) {
+ while (true) {
+ if ($.inArray(ind, this.last.searchIds) != -1 || ind > this.records.length) break;
+ ind++;
+ }
+ }
+ return ind;
+ } else {
+ return null;
+ }
+ },
+
+ prevRow: function (ind) {
+ if ((ind > 0 && this.last.searchIds.length == 0) // if there are more records
+ || (this.last.searchIds.length > 0 && ind > this.last.searchIds[0])) {
+ ind--;
+ if (this.last.searchIds.length > 0) {
+ while (true) {
+ if ($.inArray(ind, this.last.searchIds) != -1 || ind < 0) break;
+ ind--;
+ }
+ }
+ return ind;
+ } else {
+ return null;
+ }
+ }
+ };
+
+ $.extend(w2grid.prototype, w2utils.event);
+ w2obj.grid = w2grid;
+})();
+
+/************************************************************************
+* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
+* - Following objects defined
+* - w2layout - layout widget
+* - $().w2layout - jQuery wrapper
+* - Dependencies: jQuery, w2utils, w2toolbar, w2tabs
+*
+* == NICE TO HAVE ==
+* - onResize for the panel
+* - add more panel title positions (left=rotated, right=rotated, bottom)
+* - bug: resizer is visible (and onHover) when panel is hidden.
+* - bug: when you assign content before previous transition completed.
+*
+************************************************************************/
+
+(function () {
+ var w2layout = function (options) {
+ this.box = null; // DOM Element that holds the element
+ this.name = null; // unique name for w2ui
+ this.panels = [];
+ this.tmp = {};
+
+ this.padding = 1; // panel padding
+ this.resizer = 4; // resizer width or height
+ this.style = '';
+
+ this.onShow = null;
+ this.onHide = null;
+ this.onResizing = null;
+ this.onResizerClick = null;
+ this.onRender = null;
+ this.onRefresh = null;
+ this.onResize = null;
+ this.onDestroy = null;
+
+ $.extend(true, this, w2obj.layout, options);
+ };
+
+ /* @const */ var w2layout_panels = ['top', 'left', 'main', 'preview', 'right', 'bottom'];
+
+ // ====================================================
+ // -- Registers as a jQuery plugin
+
+ $.fn.w2layout = function(method) {
+ if (typeof method === 'object' || !method ) {
+ // check name parameter
+ if (!w2utils.checkName(method, 'w2layout')) return;
+ var panels = method.panels || [];
+ var object = new w2layout(method);
+ $.extend(object, { handlers: [], panels: [] });
+ // add defined panels
+ for (var p = 0, len = panels.length; p < len; p++) {
+ object.panels[p] = $.extend(true, {}, w2layout.prototype.panel, panels[p]);
+ if ($.isPlainObject(object.panels[p].tabs) || $.isArray(object.panels[p].tabs)) initTabs(object, panels[p].type);
+ if ($.isPlainObject(object.panels[p].toolbar) || $.isArray(object.panels[p].toolbar)) initToolbar(object, panels[p].type);
+ }
+ // add all other panels
+ for (var p1 in w2layout_panels) {
+ p1 = w2layout_panels[p1];
+ if (object.get(p1) !== null) continue;
+ object.panels.push($.extend(true, {}, w2layout.prototype.panel, { type: p1, hidden: (p1 !== 'main'), size: 50 }));
+ }
+ if ($(this).length > 0) {
+ object.render($(this)[0]);
+ }
+ w2ui[object.name] = object;
+ return object;
+
+ } else if (w2ui[$(this).attr('name')]) {
+ var obj = w2ui[$(this).attr('name')];
+ obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
+ return this;
+ } else {
+ console.log('ERROR: Method ' + method + ' does not exist on jQuery.w2layout' );
+ }
+
+ function initTabs(object, panel, tabs) {
+ var pan = object.get(panel);
+ if (pan !== null && typeof tabs == 'undefined') tabs = pan.tabs;
+ if (pan === null || tabs === null) return false;
+ // instanciate tabs
+ if ($.isArray(tabs)) tabs = { tabs: tabs };
+ $().w2destroy(object.name + '_' + panel + '_tabs'); // destroy if existed
+ pan.tabs = $().w2tabs($.extend({}, tabs, { owner: object, name: object.name + '_' + panel + '_tabs' }));
+ pan.show.tabs = true;
+ return true;
+ }
+
+ function initToolbar(object, panel, toolbar) {
+ var pan = object.get(panel);
+ if (pan !== null && typeof toolbar == 'undefined') toolbar = pan.toolbar;
+ if (pan === null || toolbar === null) return false;
+ // instanciate toolbar
+ if ($.isArray(toolbar)) toolbar = { items: toolbar };
+ $().w2destroy(object.name + '_' + panel + '_toolbar'); // destroy if existed
+ pan.toolbar = $().w2toolbar($.extend({}, toolbar, { owner: object, name: object.name + '_' + panel + '_toolbar' }));
+ pan.show.toolbar = true;
+ return true;
+ }
+ };
+
+ // ====================================================
+ // -- Implementation of core functionality
+
+ w2layout.prototype = {
+ // default setting for a panel
+ panel: {
+ type : null, // left, right, top, bottom
+ title : '',
+ size : 100, // width or height depending on panel name
+ minSize : 20,
+ maxSize : false,
+ hidden : false,
+ resizable : false,
+ overflow : 'auto',
+ style : '',
+ content : '', // can be String or Object with .render(box) method
+ tabs : null,
+ toolbar : null,
+ width : null, // read only
+ height : null, // read only
+ show : {
+ toolbar : false,
+ tabs : false
+ },
+ onRefresh : null,
+ onShow : null,
+ onHide : null
+ },
+
+ // alias for content
+ html: function (panel, data, transition) {
+ return this.content(panel, data, transition);
+ },
+
+ content: function (panel, data, transition) {
+ var obj = this;
+ var p = this.get(panel);
+ // if it is CSS panel
+ if (panel == 'css') {
+ $('#layout_'+ obj.name +'_panel_css').html('<style>'+ data +'</style>');
+ return true;
+ }
+ if (p === null) return false;
+ if (typeof data == 'undefined' || data === null) {
+ return p.content;
+ } else {
+ if (data instanceof jQuery) {
+ console.log('ERROR: You can not pass jQuery object to w2layout.content() method');
+ return false;
+ }
+ var pname = '#layout_'+ this.name + '_panel_'+ p.type;
+ var current = $(pname + '> .w2ui-panel-content');
+ var panelTop = 0;
+ if (current.length > 0) {
+ $(pname).scrollTop(0);
+ panelTop = $(current).position().top;
+ }
+ if (p.content === '') {
+ p.content = data;
+ this.refresh(panel);
+ } else {
+ p.content = data;
+ if (!p.hidden) {
+ if (transition !== null && transition !== '' && typeof transition != 'undefined') {
+ // apply transition
+ var div1 = $(pname + '> .w2ui-panel-content');
+ div1.after('<div class="w2ui-panel-content new-panel" style="'+ div1[0].style.cssText +'"></div>');
+ var div2 = $(pname + '> .w2ui-panel-content.new-panel');
+ div1.css('top', panelTop);
+ div2.css('top', panelTop);
+ if (typeof data == 'object') {
+ data.box = div2[0]; // do not do .render(box);
+ data.render();
+ } else {
+ div2.html(data);
+ }
+ w2utils.transition(div1[0], div2[0], transition, function () {
+ div1.remove();
+ div2.removeClass('new-panel');
+ div2.css('overflow', p.overflow);
+ // IE Hack
+ obj.resize();
+ if (window.navigator.userAgent.indexOf('MSIE') != -1) setTimeout(function () { obj.resize(); }, 100);
+ });
+ }
+ }
+ this.refresh(panel);
+ }
+ }
+ // IE Hack
+ obj.resize();
+ if (window.navigator.userAgent.indexOf('MSIE') != -1) setTimeout(function () { obj.resize(); }, 100);
+ return true;
+ },
+
+ load: function (panel, url, transition, onLoad) {
+ var obj = this;
+ if (panel == 'css') {
+ $.get(url, function (data, status, xhr) { // should always be $.get as it is template
+ obj.content(panel, xhr.responseText);
+ if (onLoad) onLoad();
+ });
+ return true;
+ }
+ if (this.get(panel) !== null) {
+ $.get(url, function (data, status, xhr) { // should always be $.get as it is template
+ obj.content(panel, xhr.responseText, transition);
+ if (onLoad) onLoad();
+ // IE Hack
+ obj.resize();
+ if (window.navigator.userAgent.indexOf('MSIE') != -1) setTimeout(function () { obj.resize(); }, 100);
+ });
+ return true;
+ }
+ return false;
+ },
+
+ sizeTo: function (panel, size) {
+ var obj = this;
+ var pan = obj.get(panel);
+ if (pan === null) return false;
+ // resize
+ $(obj.box).find(' > div > .w2ui-panel').css({
+ '-webkit-transition' : '.2s',
+ '-moz-transition' : '.2s',
+ '-ms-transition' : '.2s',
+ '-o-transition' : '.2s'
+ });
+ setTimeout(function () {
+ obj.set(panel, { size: size });
+ }, 1);
+ // clean
+ setTimeout(function () {
+ $(obj.box).find(' > div > .w2ui-panel').css({
+ '-webkit-transition' : '0s',
+ '-moz-transition' : '0s',
+ '-ms-transition' : '0s',
+ '-o-transition' : '0s'
+ });
+ obj.resize();
+ }, 500);
+ return true;
+ },
+
+ show: function (panel, immediate) {
+ var obj = this;
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'show', target: panel, object: this.get(panel), immediate: immediate });
+ if (eventData.isCancelled === true) return;
+
+ var p = obj.get(panel);
+ if (p === null) return false;
+ p.hidden = false;
+ if (immediate === true) {
+ $('#layout_'+ obj.name +'_panel_'+panel).css({ 'opacity': '1' });
+ if (p.resizable) $('#layout_'+ obj.name +'_resizer_'+panel).show();
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ obj.resize();
+ } else {
+ if (p.resizable) $('#layout_'+ obj.name +'_resizer_'+panel).show();
+ // resize
+ $('#layout_'+ obj.name +'_panel_'+panel).css({ 'opacity': '0' });
+ $(obj.box).find(' > div > .w2ui-panel').css({
+ '-webkit-transition' : '.2s',
+ '-moz-transition' : '.2s',
+ '-ms-transition' : '.2s',
+ '-o-transition' : '.2s'
+ });
+ setTimeout(function () { obj.resize(); }, 1);
+ // show
+ setTimeout(function() {
+ $('#layout_'+ obj.name +'_panel_'+ panel).css({ 'opacity': '1' });
+ }, 250);
+ // clean
+ setTimeout(function () {
+ $(obj.box).find(' > div > .w2ui-panel').css({
+ '-webkit-transition' : '0s',
+ '-moz-transition' : '0s',
+ '-ms-transition' : '0s',
+ '-o-transition' : '0s'
+ });
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ obj.resize();
+ }, 500);
+ }
+ return true;
+ },
+
+ hide: function (panel, immediate) {
+ var obj = this;
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'hide', target: panel, object: this.get(panel), immediate: immediate });
+ if (eventData.isCancelled === true) return;
+
+ var p = obj.get(panel);
+ if (p === null) return false;
+ p.hidden = true;
+ if (immediate === true) {
+ $('#layout_'+ obj.name +'_panel_'+panel).css({ 'opacity': '0' });
+ $('#layout_'+ obj.name +'_resizer_'+panel).hide();
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ obj.resize();
+ } else {
+ $('#layout_'+ obj.name +'_resizer_'+panel).hide();
+ // hide
+ $(obj.box).find(' > div > .w2ui-panel').css({
+ '-webkit-transition' : '.2s',
+ '-moz-transition' : '.2s',
+ '-ms-transition' : '.2s',
+ '-o-transition' : '.2s'
+ });
+ $('#layout_'+ obj.name +'_panel_'+panel).css({ 'opacity': '0' });
+ setTimeout(function () { obj.resize(); }, 1);
+ // clean
+ setTimeout(function () {
+ $(obj.box).find(' > div > .w2ui-panel').css({
+ '-webkit-transition' : '0s',
+ '-moz-transition' : '0s',
+ '-ms-transition' : '0s',
+ '-o-transition' : '0s'
+ });
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ obj.resize();
+ }, 500);
+ }
+ return true;
+ },
+
+ toggle: function (panel, immediate) {
+ var p = this.get(panel);
+ if (p === null) return false;
+ if (p.hidden) return this.show(panel, immediate); else return this.hide(panel, immediate);
+ },
+
+ set: function (panel, options) {
+ var obj = this.get(panel, true);
+ if (obj === null) return false;
+ $.extend(this.panels[obj], options);
+ if (typeof options['content'] != 'undefined') this.refresh(panel); // refresh only when content changed
+ this.resize(); // resize is needed when panel size is changed
+ return true;
+ },
+
+ get: function (panel, returnIndex) {
+ for (var p in this.panels) {
+ if (this.panels[p].type == panel) {
+ if (returnIndex === true) return p; else return this.panels[p];
+ }
+ }
+ return null;
+ },
+
+ el: function (panel) {
+ var el = $('#layout_'+ this.name +'_panel_'+ panel +'> .w2ui-panel-content');
+ if (el.length != 1) return null;
+ return el[0];
+ },
+
+ hideToolbar: function (panel) {
+ var pan = this.get(panel);
+ if (!pan) return;
+ pan.show.toolbar = false;
+ $('#layout_'+ this.name +'_panel_'+ panel +'> .w2ui-panel-toolbar').hide();
+ this.resize();
+ },
+
+ showToolbar: function (panel) {
+ var pan = this.get(panel);
+ if (!pan) return;
+ pan.show.toolbar = true;
+ $('#layout_'+ this.name +'_panel_'+ panel +'> .w2ui-panel-toolbar').show();
+ this.resize();
+ },
+
+ toggleToolbar: function (panel) {
+ var pan = this.get(panel);
+ if (!pan) return;
+ if (pan.show.toolbar) this.hideToolbar(panel); else this.showToolbar(panel);
+ },
+
+ hideTabs: function (panel) {
+ var pan = this.get(panel);
+ if (!pan) return;
+ pan.show.tabs = false;
+ $('#layout_'+ this.name +'_panel_'+ panel +'> .w2ui-panel-tabs').hide();
+ this.resize();
+ },
+
+ showTabs: function (panel) {
+ var pan = this.get(panel);
+ if (!pan) return;
+ pan.show.tabs = true;
+ $('#layout_'+ this.name +'_panel_'+ panel +'> .w2ui-panel-tabs').show();
+ this.resize();
+ },
+
+ toggleTabs: function (panel) {
+ var pan = this.get(panel);
+ if (!pan) return;
+ if (pan.show.tabs) this.hideTabs(panel); else this.showTabs(panel);
+ },
+
+ render: function (box) {
+ var obj = this;
+ // if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ var time = (new Date()).getTime();
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'render', target: obj.name, box: box });
+ if (eventData.isCancelled === true) return;
+
+ if (typeof box != 'undefined' && box !== null) {
+ if ($(obj.box).find('#layout_'+ obj.name +'_panel_main').length > 0) {
+ $(obj.box)
+ .removeAttr('name')
+ .removeClass('w2ui-layout')
+ .html('');
+ }
+ obj.box = box;
+ }
+ if (!obj.box) return false;
+ $(obj.box)
+ .attr('name', obj.name)
+ .addClass('w2ui-layout')
+ .html('<div></div>');
+ if ($(obj.box).length > 0) $(obj.box)[0].style.cssText += obj.style;
+ // create all panels
+ for (var p1 in w2layout_panels) {
+ p1 = w2layout_panels[p1];
+ var pan = obj.get(p1);
+ var html = '<div id="layout_'+ obj.name + '_panel_'+ p1 +'" class="w2ui-panel">'+
+ ' <div class="w2ui-panel-title"></div>'+
+ ' <div class="w2ui-panel-tabs"></div>'+
+ ' <div class="w2ui-panel-toolbar"></div>'+
+ ' <div class="w2ui-panel-content"></div>'+
+ '</div>'+
+ '<div id="layout_'+ obj.name + '_resizer_'+ p1 +'" class="w2ui-resizer"></div>';
+ $(obj.box).find(' > div').append(html);
+ // tabs are rendered in refresh()
+ }
+ $(obj.box).find(' > div')
+ .append('<div id="layout_'+ obj.name + '_panel_css" style="position: absolute; top: 10000px;"></div');
+ obj.refresh(); // if refresh is not called here, the layout will not be available right after initialization
+ // process event
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ // reinit events
+ setTimeout(function () { // needed this timeout to allow browser to render first if there are tabs or toolbar
+ initEvents();
+ obj.resize();
+ }, 0);
+ return (new Date()).getTime() - time;
+
+ function initEvents() {
+ obj.tmp.events = {
+ resize : function (event) {
+ w2ui[obj.name].resize();
+ },
+ resizeStart : resizeStart,
+ mouseMove : resizeMove,
+ mouseUp : resizeStop
+ };
+ $(window).on('resize', obj.tmp.events.resize);
+ }
+
+ function resizeStart(type, evnt) {
+ if (!obj.box) return;
+ if (!evnt) evnt = window.event;
+ if (!window.addEventListener) { window.document.attachEvent('onselectstart', function() { return false; } ); }
+ $(document).off('mousemove', obj.tmp.events.mouseMove).on('mousemove', obj.tmp.events.mouseMove);
+ $(document).off('mouseup', obj.tmp.events.mouseUp).on('mouseup', obj.tmp.events.mouseUp);
+ obj.tmp.resize = {
+ type : type,
+ x : evnt.screenX,
+ y : evnt.screenY,
+ diff_x : 0,
+ diff_y : 0,
+ value : 0
+ };
+ // lock all panels
+ for (var p1 in w2layout_panels) {
+ p1 = w2layout_panels[p1];
+ obj.lock(p1, { opacity: 0 });
+ }
+ if (type == 'left' || type == 'right') {
+ obj.tmp.resize.value = parseInt($('#layout_'+ obj.name +'_resizer_'+ type)[0].style.left);
+ }
+ if (type == 'top' || type == 'preview' || type == 'bottom') {
+ obj.tmp.resize.value = parseInt($('#layout_'+ obj.name +'_resizer_'+ type)[0].style.top);
+ }
+ }
+
+ function resizeStop(evnt) {
+ if (!obj.box) return;
+ if (!evnt) evnt = window.event;
+ if (!window.addEventListener) { window.document.attachEvent('onselectstart', function() { return false; } ); }
+ $(document).off('mousemove', obj.tmp.events.mouseMove);
+ $(document).off('mouseup', obj.tmp.events.mouseUp);
+ if (typeof obj.tmp.resize == 'undefined') return;
+ // unlock all panels
+ for (var p1 in w2layout_panels) {
+ obj.unlock(w2layout_panels[p1]);
+ }
+ // set new size
+ if (obj.tmp.diff_x !== 0 || obj.tmp.resize.diff_y !== 0) { // only recalculate if changed
+ var ptop = obj.get('top');
+ var pbottom = obj.get('bottom');
+ var panel = obj.get(obj.tmp.resize.type);
+ var height = parseInt($(obj.box).height());
+ var width = parseInt($(obj.box).width());
+ var str = String(panel.size);
+ var ns, nd;
+ switch (obj.tmp.resize.type) {
+ case 'top':
+ ns = parseInt(panel.sizeCalculated) + obj.tmp.resize.diff_y;
+ nd = 0;
+ break;
+ case 'bottom':
+ ns = parseInt(panel.sizeCalculated) - obj.tmp.resize.diff_y;
+ nd = 0;
+ break;
+ case 'preview':
+ ns = parseInt(panel.sizeCalculated) - obj.tmp.resize.diff_y;
+ nd = (ptop && !ptop.hidden ? ptop.sizeCalculated : 0) +
+ (pbottom && !pbottom.hidden ? pbottom.sizeCalculated : 0);
+ break;
+ case 'left':
+ ns = parseInt(panel.sizeCalculated) + obj.tmp.resize.diff_x;
+ nd = 0;
+ break;
+ case 'right':
+ ns = parseInt(panel.sizeCalculated) - obj.tmp.resize.diff_x;
+ nd = 0;
+ break;
+ }
+ // set size
+ if (str.substr(str.length-1) == '%') {
+ panel.size = Math.floor(ns * 100 /
+ (panel.type == 'left' || panel.type == 'right' ? width : height - nd) * 100) / 100 + '%';
+ } else {
+ panel.size = ns;
+ }
+ obj.resize();
+ }
+ $('#layout_'+ obj.name + '_resizer_'+ obj.tmp.resize.type).removeClass('active');
+ delete obj.tmp.resize;
+ }
+
+ function resizeMove(evnt) {
+ if (!obj.box) return;
+ if (!evnt) evnt = window.event;
+ if (typeof obj.tmp.resize == 'undefined') return;
+ var panel = obj.get(obj.tmp.resize.type);
+ // event before
+ var tmp = obj.tmp.resize;
+ var eventData = obj.trigger({ phase: 'before', type: 'resizing', target: obj.name, object: panel, originalEvent: evnt,
+ panel: tmp ? tmp.type : 'all', diff_x: tmp ? tmp.diff_x : 0, diff_y: tmp ? tmp.diff_y : 0 });
+ if (eventData.isCancelled === true) return;
+
+ var p = $('#layout_'+ obj.name + '_resizer_'+ tmp.type);
+ var resize_x = (evnt.screenX - tmp.x);
+ var resize_y = (evnt.screenY - tmp.y);
+ var mainPanel = obj.get('main');
+
+ if (!p.hasClass('active')) p.addClass('active');
+
+ switch (tmp.type) {
+ case 'left':
+ if (panel.minSize - resize_x > panel.width) {
+ resize_x = panel.minSize - panel.width;
+ }
+ if (panel.maxSize && (panel.width + resize_x > panel.maxSize)) {
+ resize_x = panel.maxSize - panel.width;
+ }
+ if (mainPanel.minSize + resize_x > mainPanel.width) {
+ resize_x = mainPanel.width - mainPanel.minSize;
+ }
+ break;
+
+ case 'right':
+ if (panel.minSize + resize_x > panel.width) {
+ resize_x = panel.width - panel.minSize;
+ }
+ if (panel.maxSize && (panel.width - resize_x > panel.maxSize)) {
+ resize_x = panel.width - panel.maxSize;
+ }
+ if (mainPanel.minSize - resize_x > mainPanel.width) {
+ resize_x = mainPanel.minSize - mainPanel.width;
+ }
+ break;
+
+ case 'top':
+ if (panel.minSize - resize_y > panel.height) {
+ resize_y = panel.minSize - panel.height;
+ }
+ if (panel.maxSize && (panel.height + resize_y > panel.maxSize)) {
+ resize_y = panel.maxSize - panel.height;
+ }
+ if (mainPanel.minSize + resize_y > mainPanel.height) {
+ resize_y = mainPanel.height - mainPanel.minSize;
+ }
+ break;
+
+ case 'preview':
+ case 'bottom':
+ if (panel.minSize + resize_y > panel.height) {
+ resize_y = panel.height - panel.minSize;
+ }
+ if (panel.maxSize && (panel.height - resize_y > panel.maxSize)) {
+ resize_y = panel.height - panel.maxSize;
+ }
+ if (mainPanel.minSize - resize_y > mainPanel.height) {
+ resize_y = mainPanel.minSize - mainPanel.height;
+ }
+ break;
+ }
+ tmp.diff_x = resize_x;
+ tmp.diff_y = resize_y;
+
+ switch (tmp.type) {
+ case 'top':
+ case 'preview':
+ case 'bottom':
+ tmp.diff_x = 0;
+ if (p.length > 0) p[0].style.top = (tmp.value + tmp.diff_y) + 'px';
+ break;
+
+ case 'left':
+ case 'right':
+ tmp.diff_y = 0;
+ if (p.length > 0) p[0].style.left = (tmp.value + tmp.diff_x) + 'px';
+ break;
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ },
+
+ refresh: function (panel) {
+ var obj = this;
+ // if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ if (typeof panel == 'undefined') panel = null;
+ var time = (new Date()).getTime();
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'refresh', target: (typeof panel != 'undefined' ? panel : obj.name), object: obj.get(panel) });
+ if (eventData.isCancelled === true) return;
+ // obj.unlock(panel);
+ if (typeof panel == 'string') {
+ var p = obj.get(panel);
+ if (p === null) return;
+ var pname = '#layout_'+ obj.name + '_panel_'+ p.type;
+ var rname = '#layout_'+ obj.name +'_resizer_'+ p.type;
+ // apply properties to the panel
+ $(pname).css({ display: p.hidden ? 'none' : 'block' });
+ if (p.resizable) $(rname).show(); else $(rname).hide();
+ // insert content
+ if (typeof p.content == 'object' && typeof p.content.render === 'function') {
+ p.content.box = $(pname +'> .w2ui-panel-content')[0];
+ setTimeout(function () {
+ // need to remove unnecessary classes
+ if ($(pname +'> .w2ui-panel-content').length > 0) {
+ $(pname +'> .w2ui-panel-content')
+ .removeClass()
+ .addClass('w2ui-panel-content')
+ .css('overflow', p.overflow)[0].style.cssText += ';' + p.style;
+ }
+ p.content.render(); // do not do .render(box);
+ }, 1);
+ } else {
+ // need to remove unnecessary classes
+ if ($(pname +'> .w2ui-panel-content').length > 0) {
+ $(pname +'> .w2ui-panel-content')
+ .removeClass()
+ .addClass('w2ui-panel-content')
+ .html(p.content)
+ .css('overflow', p.overflow)[0].style.cssText += ';' + p.style;
+ }
+ }
+ // if there are tabs and/or toolbar - render it
+ var tmp = $(obj.box).find(pname +'> .w2ui-panel-tabs');
+ if (p.show.tabs) {
+ if (tmp.find('[name='+ p.tabs.name +']').length === 0 && p.tabs !== null) tmp.w2render(p.tabs); else p.tabs.refresh();
+ } else {
+ tmp.html('').removeClass('w2ui-tabs').hide();
+ }
+ tmp = $(obj.box).find(pname +'> .w2ui-panel-toolbar');
+ if (p.show.toolbar) {
+ if (tmp.find('[name='+ p.toolbar.name +']').length === 0 && p.toolbar !== null) tmp.w2render(p.toolbar); else p.toolbar.refresh();
+ } else {
+ tmp.html('').removeClass('w2ui-toolbar').hide();
+ }
+ // show title
+ tmp = $(obj.box).find(pname +'> .w2ui-panel-title');
+ if (p.title) {
+ tmp.html(p.title).show();
+ } else {
+ tmp.html('').hide();
+ }
+ } else {
+ if ($('#layout_'+ obj.name +'_panel_main').length == 0) {
+ obj.render();
+ return;
+ }
+ obj.resize();
+ // refresh all of them
+ for (var p1 in this.panels) { obj.refresh(this.panels[p1].type); }
+ }
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ return (new Date()).getTime() - time;
+ },
+
+ resize: function () {
+ // if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ if (!this.box) return false;
+ var time = (new Date()).getTime();
+ // event before
+ var tmp = this.tmp.resize;
+ var eventData = this.trigger({ phase: 'before', type: 'resize', target: this.name,
+ panel: tmp ? tmp.type : 'all', diff_x: tmp ? tmp.diff_x : 0, diff_y: tmp ? tmp.diff_y : 0 });
+ if (eventData.isCancelled === true) return;
+ if (this.padding < 0) this.padding = 0;
+
+ // layout itself
+ var width = parseInt($(this.box).width());
+ var height = parseInt($(this.box).height());
+ $(this.box).find(' > div').css({
+ width : width + 'px',
+ height : height + 'px'
+ });
+ var obj = this;
+ // panels
+ var pmain = this.get('main');
+ var pprev = this.get('preview');
+ var pleft = this.get('left');
+ var pright = this.get('right');
+ var ptop = this.get('top');
+ var pbottom = this.get('bottom');
+ var smain = true; // main always on
+ var sprev = (pprev !== null && pprev.hidden !== true ? true : false);
+ var sleft = (pleft !== null && pleft.hidden !== true ? true : false);
+ var sright = (pright !== null && pright.hidden !== true ? true : false);
+ var stop = (ptop !== null && ptop.hidden !== true ? true : false);
+ var sbottom = (pbottom !== null && pbottom.hidden !== true ? true : false);
+ var l, t, w, h, e;
+ // calculate %
+ for (var p in w2layout_panels) {
+ p = w2layout_panels[p];
+ if (p === 'main') continue;
+ var tmp = this.get(p);
+ if (!tmp) continue;
+ var str = String(tmp.size || 0);
+ if (str.substr(str.length-1) == '%') {
+ var tmph = height;
+ if (tmp.type == 'preview') {
+ tmph = tmph -
+ (ptop && !ptop.hidden ? ptop.sizeCalculated : 0) -
+ (pbottom && !pbottom.hidden ? pbottom.sizeCalculated : 0);
+ }
+ tmp.sizeCalculated = parseInt((tmp.type == 'left' || tmp.type == 'right' ? width : tmph) * parseFloat(tmp.size) / 100);
+ } else {
+ tmp.sizeCalculated = parseInt(tmp.size);
+ }
+ tmp.sizeCalculated = Math.max(tmp.sizeCalculated, parseInt(tmp.minSize));
+ }
+ // top if any
+ if (ptop !== null && ptop.hidden !== true) {
+ l = 0;
+ t = 0;
+ w = width;
+ h = ptop.sizeCalculated;
+ $('#layout_'+ this.name +'_panel_top').css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px'
+ }).show();
+ ptop.width = w;
+ ptop.height = h;
+ // resizer
+ if (ptop.resizable) {
+ t = ptop.sizeCalculated - (this.padding === 0 ? this.resizer : 0);
+ h = (this.resizer > this.padding ? this.resizer : this.padding);
+ $('#layout_'+ this.name +'_resizer_top').show().css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px',
+ 'cursor': 'ns-resize'
+ }).off('mousedown').on('mousedown', function (event) {
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'resizerClick', target: 'top', originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default action
+ w2ui[obj.name].tmp.events.resizeStart('top', event);
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ return false;
+ });
+ }
+ } else {
+ $('#layout_'+ this.name +'_panel_top').hide();
+ }
+ // left if any
+ if (pleft !== null && pleft.hidden !== true) {
+ l = 0;
+ t = 0 + (stop ? ptop.sizeCalculated + this.padding : 0);
+ w = pleft.sizeCalculated;
+ h = height - (stop ? ptop.sizeCalculated + this.padding : 0) -
+ (sbottom ? pbottom.sizeCalculated + this.padding : 0);
+ e = $('#layout_'+ this.name +'_panel_left');
+ if (window.navigator.userAgent.indexOf('MSIE') != -1 && e.length > 0 && e[0].clientHeight < e[0].scrollHeight) w += 17; // IE hack
+ e.css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px'
+ }).show();
+ pleft.width = w;
+ pleft.height = h;
+ // resizer
+ if (pleft.resizable) {
+ l = pleft.sizeCalculated - (this.padding === 0 ? this.resizer : 0);
+ w = (this.resizer > this.padding ? this.resizer : this.padding);
+ $('#layout_'+ this.name +'_resizer_left').show().css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px',
+ 'cursor': 'ew-resize'
+ }).off('mousedown').on('mousedown', function (event) {
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'resizerClick', target: 'left', originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default action
+ w2ui[obj.name].tmp.events.resizeStart('left', event);
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ return false;
+ });
+ }
+ } else {
+ $('#layout_'+ this.name +'_panel_left').hide();
+ $('#layout_'+ this.name +'_resizer_left').hide();
+ }
+ // right if any
+ if (pright !== null && pright.hidden !== true) {
+ l = width - pright.sizeCalculated;
+ t = 0 + (stop ? ptop.sizeCalculated + this.padding : 0);
+ w = pright.sizeCalculated;
+ h = height - (stop ? ptop.sizeCalculated + this.padding : 0) -
+ (sbottom ? pbottom.sizeCalculated + this.padding : 0);
+ $('#layout_'+ this.name +'_panel_right').css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px'
+ }).show();
+ pright.width = w;
+ pright.height = h;
+ // resizer
+ if (pright.resizable) {
+ l = l - this.padding;
+ w = (this.resizer > this.padding ? this.resizer : this.padding);
+ $('#layout_'+ this.name +'_resizer_right').show().css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px',
+ 'cursor': 'ew-resize'
+ }).off('mousedown').on('mousedown', function (event) {
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'resizerClick', target: 'right', originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default action
+ w2ui[obj.name].tmp.events.resizeStart('right', event);
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ return false;
+ });
+ }
+ } else {
+ $('#layout_'+ this.name +'_panel_right').hide();
+ }
+ // bottom if any
+ if (pbottom !== null && pbottom.hidden !== true) {
+ l = 0;
+ t = height - pbottom.sizeCalculated;
+ w = width;
+ h = pbottom.sizeCalculated;
+ $('#layout_'+ this.name +'_panel_bottom').css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px'
+ }).show();
+ pbottom.width = w;
+ pbottom.height = h;
+ // resizer
+ if (pbottom.resizable) {
+ t = t - (this.padding === 0 ? 0 : this.padding);
+ h = (this.resizer > this.padding ? this.resizer : this.padding);
+ $('#layout_'+ this.name +'_resizer_bottom').show().css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px',
+ 'cursor': 'ns-resize'
+ }).off('mousedown').on('mousedown', function (event) {
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'resizerClick', target: 'bottom', originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default action
+ w2ui[obj.name].tmp.events.resizeStart('bottom', event);
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ return false;
+ });
+ }
+ } else {
+ $('#layout_'+ this.name +'_panel_bottom').hide();
+ }
+ // main - always there
+ l = 0 + (sleft ? pleft.sizeCalculated + this.padding : 0);
+ t = 0 + (stop ? ptop.sizeCalculated + this.padding : 0);
+ w = width - (sleft ? pleft.sizeCalculated + this.padding : 0) -
+ (sright ? pright.sizeCalculated + this.padding: 0);
+ h = height - (stop ? ptop.sizeCalculated + this.padding : 0) -
+ (sbottom ? pbottom.sizeCalculated + this.padding : 0) -
+ (sprev ? pprev.sizeCalculated + this.padding : 0);
+ e = $('#layout_'+ this.name +'_panel_main');
+ if (window.navigator.userAgent.indexOf('MSIE') != -1 && e.length > 0 && e[0].clientHeight < e[0].scrollHeight) w += 17; // IE hack
+ e.css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px'
+ });
+ pmain.width = w;
+ pmain.height = h;
+
+ // preview if any
+ if (pprev !== null && pprev.hidden !== true) {
+ l = 0 + (sleft ? pleft.sizeCalculated + this.padding : 0);
+ t = height - (sbottom ? pbottom.sizeCalculated + this.padding : 0) - pprev.sizeCalculated;
+ w = width - (sleft ? pleft.sizeCalculated + this.padding : 0) -
+ (sright ? pright.sizeCalculated + this.padding : 0);
+ h = pprev.sizeCalculated;
+ e = $('#layout_'+ this.name +'_panel_preview');
+ if (window.navigator.userAgent.indexOf('MSIE') != -1 && e.length > 0 && e[0].clientHeight < e[0].scrollHeight) w += 17; // IE hack
+ e.css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px'
+ }).show();
+ pprev.width = w;
+ pprev.height = h;
+ // resizer
+ if (pprev.resizable) {
+ t = t - (this.padding === 0 ? 0 : this.padding);
+ h = (this.resizer > this.padding ? this.resizer : this.padding);
+ $('#layout_'+ this.name +'_resizer_preview').show().css({
+ 'display': 'block',
+ 'left': l + 'px',
+ 'top': t + 'px',
+ 'width': w + 'px',
+ 'height': h + 'px',
+ 'cursor': 'ns-resize'
+ }).off('mousedown').on('mousedown', function (event) {
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'resizerClick', target: 'preview', originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default action
+ w2ui[obj.name].tmp.events.resizeStart('preview', event);
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ return false;
+ });
+ }
+ } else {
+ $('#layout_'+ this.name +'_panel_preview').hide();
+ }
+
+ // display tabs and toolbar if needed
+ for (var p1 in w2layout_panels) {
+ p1 = w2layout_panels[p1];
+ var pan = this.get(p1);
+ var tmp2 = '#layout_'+ this.name +'_panel_'+ p1 +' > .w2ui-panel-';
+ var tabHeight = 0;
+ if (pan) {
+ if (pan.title) {
+ tabHeight += w2utils.getSize($(tmp2 + 'title').css({ top: tabHeight + 'px', display: 'block' }), 'height');
+ }
+ if (pan.show.tabs) {
+ if (pan.tabs !== null && w2ui[this.name +'_'+ p1 +'_tabs']) w2ui[this.name +'_'+ p1 +'_tabs'].resize();
+ tabHeight += w2utils.getSize($(tmp2 + 'tabs').css({ top: tabHeight + 'px', display: 'block' }), 'height');
+ }
+ if (pan.show.toolbar) {
+ if (pan.toolbar !== null && w2ui[this.name +'_'+ p1 +'_toolbar']) w2ui[this.name +'_'+ p1 +'_toolbar'].resize();
+ tabHeight += w2utils.getSize($(tmp2 + 'toolbar').css({ top: tabHeight + 'px', display: 'block' }), 'height');
+ }
+ }
+ $(tmp2 + 'content').css({ display: 'block' }).css({ top: tabHeight + 'px' });
+ }
+ // send resize to all objects
+ clearTimeout(this._resize_timer);
+ this._resize_timer = setTimeout(function () {
+ for (var e in w2ui) {
+ if (typeof w2ui[e].resize == 'function') {
+ // sent to all none-layouts
+ if (w2ui[e].panels == 'undefined') w2ui[e].resize();
+ // only send to nested layouts
+ var parent = $(w2ui[e].box).parents('.w2ui-layout');
+ if (parent.length > 0 && parent.attr('name') == obj.name) w2ui[e].resize();
+ }
+ }
+ }, 100);
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return (new Date()).getTime() - time;
+ },
+
+ destroy: function () {
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'destroy', target: this.name });
+ if (eventData.isCancelled === true) return;
+ if (typeof w2ui[this.name] == 'undefined') return false;
+ // clean up
+ if ($(this.box).find('#layout_'+ this.name +'_panel_main').length > 0) {
+ $(this.box)
+ .removeAttr('name')
+ .removeClass('w2ui-layout')
+ .html('');
+ }
+ delete w2ui[this.name];
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ if (this.tmp.events && this.tmp.events.resize) $(window).off('resize', this.tmp.events.resize);
+ return true;
+ },
+
+ lock: function (panel, msg, showSpinner) {
+ if (w2layout_panels.indexOf(panel) == -1) {
+ console.log('ERROR: First parameter needs to be the a valid panel name.');
+ return;
+ }
+ var args = Array.prototype.slice.call(arguments, 0);
+ args[0] = '#layout_'+ this.name + '_panel_' + panel;
+ w2utils.lock.apply(window, args);
+ },
+
+ unlock: function (panel) {
+ if (w2layout_panels.indexOf(panel) == -1) {
+ console.log('ERROR: First parameter needs to be the a valid panel name.');
+ return;
+ }
+ var nm = '#layout_'+ this.name + '_panel_' + panel;
+ w2utils.unlock(nm);
+ }
+ };
+
+ $.extend(w2layout.prototype, w2utils.event);
+ w2obj.layout = w2layout;
+})();
+
+/************************************************************************
+* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
+* - Following objects defined
+* - w2popup - popup widget
+* - $().w2popup - jQuery wrapper
+* - Dependencies: jQuery, w2utils
+*
+* == NICE TO HAVE ==
+* - transition should include title, body and buttons, not just body
+*
+************************************************************************/
+
+var w2popup = {};
+
+(function () {
+
+ // ====================================================
+ // -- Registers as a jQuery plugin
+
+ $.fn.w2popup = function(method, options) {
+ if (typeof method === 'undefined') {
+ options = {};
+ method = 'open';
+ }
+ if ($.isPlainObject(method)) {
+ options = method;
+ method = 'open';
+ }
+ method = method.toLowerCase();
+ if (method === 'load' && typeof options === 'string') {
+ options = $.extend({ url: options }, arguments.length > 2 ? arguments[2] : {});
+ }
+ if (method === 'open' && options.url != null) method = 'load';
+ options = options || {};
+ // load options from markup
+ var dlgOptions = {};
+ if ($(this).length > 0) {
+ if ($(this).find('div[rel=title], div[rel=body], div[rel=buttons]').length > 0) {
+ if ($(this).find('div[rel=title]').length > 0) {
+ dlgOptions['title'] = $(this).find('div[rel=title]').html();
+ }
+ if ($(this).find('div[rel=body]').length > 0) {
+ dlgOptions['body'] = $(this).find('div[rel=body]').html();
+ dlgOptions['style'] = $(this).find('div[rel=body]')[0].style.cssText;
+ }
+ if ($(this).find('div[rel=buttons]').length > 0) {
+ dlgOptions['buttons'] = $(this).find('div[rel=buttons]').html();
+ }
+ } else {
+ dlgOptions['title'] = '&nbsp;';
+ dlgOptions['body'] = $(this).html();
+ }
+ if (parseInt($(this).css('width')) != 0) dlgOptions['width'] = parseInt($(this).css('width'));
+ if (parseInt($(this).css('height')) != 0) dlgOptions['height'] = parseInt($(this).css('height'));
+ }
+ // show popup
+ return w2popup[method]($.extend({}, dlgOptions, options));
+ };
+
+ // ====================================================
+ // -- Implementation of core functionality (SINGELTON)
+
+ w2popup = {
+ defaults: {
+ title : '',
+ body : '',
+ buttons : '',
+ style : '',
+ color : '#000',
+ opacity : 0.4,
+ speed : 0.3,
+ modal : false,
+ maximized : false,
+ keyboard : true, // will close popup on esc if not modal
+ width : 500,
+ height : 300,
+ showClose : true,
+ showMax : false,
+ transition: null
+ },
+ status : 'closed', // string that describes current status
+ handlers : [],
+ onOpen : null,
+ onClose : null,
+ onMax : null,
+ onMin : null,
+ onToggle : null,
+ onKeydown : null,
+
+ open: function (options) {
+ var obj = this;
+ if (w2popup.status == 'closing') {
+ setTimeout(function () { obj.open.call(obj, options); }, 100);
+ return;
+ }
+ // get old options and merge them
+ var old_options = $('#w2ui-popup').data('options');
+ var options = $.extend({}, this.defaults, old_options, { title: '', body : '', buttons: '' }, options, { maximized: false });
+ // need timer because popup might not be open
+ setTimeout(function () { $('#w2ui-popup').data('options', options); }, 100);
+ // if new - reset event handlers
+ if ($('#w2ui-popup').length == 0) {
+ w2popup.handlers = [];
+ w2popup.onMax = null;
+ w2popup.onMin = null;
+ w2popup.onToggle = null;
+ w2popup.onOpen = null;
+ w2popup.onClose = null;
+ w2popup.onKeydown = null;
+ }
+ if (options.onOpen) w2popup.onOpen = options.onOpen;
+ if (options.onClose) w2popup.onClose = options.onClose;
+ if (options.onMax) w2popup.onMax = options.onMax;
+ if (options.onMin) w2popup.onMin = options.onMin;
+ if (options.onToggle) w2popup.onToggle = options.onToggle;
+ if (options.onKeydown) w2popup.onKeydown = options.onKeydown;
+
+ if (window.innerHeight == undefined) {
+ var width = document.documentElement.offsetWidth;
+ var height = document.documentElement.offsetHeight;
+ if (w2utils.engine === 'IE7') { width += 21; height += 4; }
+ } else {
+ var width = window.innerWidth;
+ var height = window.innerHeight;
+ }
+ if (parseInt(width) - 10 < parseInt(options.width)) options.width = parseInt(width) - 10;
+ if (parseInt(height) - 10 < parseInt(options.height)) options.height = parseInt(height) - 10;
+ var top = parseInt(((parseInt(height) - parseInt(options.height)) / 2) * 0.6);
+ var left = parseInt((parseInt(width) - parseInt(options.width)) / 2);
+ // check if message is already displayed
+ if ($('#w2ui-popup').length == 0) {
+ // trigger event
+ var eventData = this.trigger({ phase: 'before', type: 'open', target: 'popup', options: options, present: false });
+ if (eventData.isCancelled === true) return;
+ w2popup.status = 'opening';
+ // output message
+ w2popup.lockScreen(options);
+ var btn = '';
+ if (options.showClose) {
+ btn += '<div class="w2ui-msg-button w2ui-msg-close" onmousedown="event.stopPropagation()" onclick="w2popup.close()">Close</div>';
+ }
+ if (options.showMax) {
+ btn += '<div class="w2ui-msg-button w2ui-msg-max" onmousedown="event.stopPropagation()" onclick="w2popup.toggle()">Max</div>';
+ }
+ var msg='<div id="w2ui-popup" class="w2ui-popup" style="opacity: 0; left: '+ left +'px; top: '+ top +'px;'+
+ ' width: ' + parseInt(options.width) + 'px; height: ' + parseInt(options.height) + 'px; '+
+ ' -webkit-transform: scale(0.8); -moz-transform: scale(0.8); -ms-transform: scale(0.8); -o-transform: scale(0.8); "'+
+ '>'+
+ ' <div class="w2ui-msg-title" style="'+ (options.title == '' ? 'display: none' : '') +'">' + btn + options.title + '</div>'+
+ ' <div class="w2ui-box1" style="'+ (options.title == '' ? 'top: 0px !important;' : '') +
+ (options.buttons == '' ? 'bottom: 0px !important;' : '') + '">'+
+ ' <div class="w2ui-msg-body' + (!options.title != '' ? ' w2ui-msg-no-title' : '') +
+ (!options.buttons != '' ? ' w2ui-msg-no-buttons' : '') + '" style="' + options.style + '">' + options.body + '</div>'+
+ ' </div>'+
+ ' <div class="w2ui-box2" style="' + (options.title == '' ? 'top: 0px !important;' : '') +
+ (options.buttons == '' ? 'bottom: 0px !important;' : '') + '">'+
+ ' <div class="w2ui-msg-body' + (!options.title != '' ? ' w2ui-msg-no-title' : '') +
+ (!options.buttons != '' ? ' w2ui-msg-no-buttons' : '') + '" style="' + options.style + '"></div>'+
+ ' </div>'+
+ ' <div class="w2ui-msg-buttons" style="'+ (options.buttons == '' ? 'display: none' : '') +'">' + options.buttons + '</div>'+
+ '</div>';
+ $('body').append(msg);
+ // allow element to render
+ setTimeout(function () {
+ $('#w2ui-popup .w2ui-box2').hide();
+ $('#w2ui-popup').css({
+ '-webkit-transition': options.speed + 's opacity, ' + options.speed + 's -webkit-transform',
+ '-webkit-transform': 'scale(1)',
+ '-moz-transition': options.speed + 's opacity, ' + options.speed + 's -moz-transform',
+ '-moz-transform': 'scale(1)',
+ '-ms-transition': options.speed + 's opacity, ' + options.speed + 's -ms-transform',
+ '-ms-transform': 'scale(1)',
+ '-o-transition': options.speed + 's opacity, ' + options.speed + 's -o-transform',
+ '-o-transform': 'scale(1)',
+ 'opacity': '1'
+ });
+ }, 1);
+ // clean transform
+ setTimeout(function () {
+ $('#w2ui-popup').css({
+ '-webkit-transform': '',
+ '-moz-transform': '',
+ '-ms-transform': '',
+ '-o-transform': ''
+ });
+ // event after
+ w2popup.status = 'open';
+ setTimeout(function () {
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }, 100);
+ }, options.speed * 1000);
+ } else {
+ // trigger event
+ var eventData = this.trigger({ phase: 'before', type: 'open', target: 'popup', options: options, present: true });
+ if (eventData.isCancelled === true) return;
+ // check if size changed
+ w2popup.status = 'opening';
+ if (typeof old_options == 'undefined' || old_options['width'] != options['width'] || old_options['height'] != options['height']) {
+ w2popup.resize(options.width, options.height);
+ }
+ if (typeof old_options != 'undefined') {
+ options.prevSize = options.width + ':' + options.height;
+ options.maximized = old_options.maximized;
+ }
+ // show new items
+ var body = $('#w2ui-popup .w2ui-box2 > .w2ui-msg-body').html(options.body);
+ if (body.length > 0) body[0].style.cssText = options.style;
+ if (options.buttons != '') {
+ $('#w2ui-popup .w2ui-msg-buttons').show().html(options.buttons);
+ $('#w2ui-popup .w2ui-msg-body').removeClass('w2ui-msg-no-buttons');
+ $('#w2ui-popup .w2ui-box1, #w2ui-popup .w2ui-box2').css('bottom', '');
+ } else {
+ $('#w2ui-popup .w2ui-msg-buttons').hide().html('');
+ $('#w2ui-popup .w2ui-msg-body').addClass('w2ui-msg-no-buttons');
+ $('#w2ui-popup .w2ui-box1, #w2ui-popup .w2ui-box2').css('bottom', '0px');
+ }
+ if (options.title != '') {
+ $('#w2ui-popup .w2ui-msg-title').show().html(
+ (options.showClose ? '<div class="w2ui-msg-button w2ui-msg-close" onmousedown="event.stopPropagation()" onclick="w2popup.close()">Close</div>' : '') +
+ (options.showMax ? '<div class="w2ui-msg-button w2ui-msg-max" onmousedown="event.stopPropagation()" onclick="w2popup.toggle()">Max</div>' : '') +
+ options.title);
+ $('#w2ui-popup .w2ui-msg-body').removeClass('w2ui-msg-no-title');
+ $('#w2ui-popup .w2ui-box1, #w2ui-popup .w2ui-box2').css('top', '');
+ } else {
+ $('#w2ui-popup .w2ui-msg-title').hide().html('');
+ $('#w2ui-popup .w2ui-msg-body').addClass('w2ui-msg-no-title');
+ $('#w2ui-popup .w2ui-box1, #w2ui-popup .w2ui-box2').css('top', '0px');
+ }
+ // transition
+ var div_old = $('#w2ui-popup .w2ui-box1')[0];
+ var div_new = $('#w2ui-popup .w2ui-box2')[0];
+ w2utils.transition(div_old, div_new, options.transition);
+ div_new.className = 'w2ui-box1';
+ div_old.className = 'w2ui-box2';
+ $(div_new).addClass('w2ui-current-box');
+ // remove max state
+ $('#w2ui-popup').data('prev-size', null);
+ // call event onChange
+ setTimeout(function () {
+ w2popup.status = 'open';
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }, 100);
+ }
+ // save new options
+ options._last_w2ui_name = w2utils.keyboard.active();
+ w2utils.keyboard.active(null);
+ // keyboard events
+ if (options.keyboard) $(document).on('keydown', this.keydown);
+
+ // initialize move
+ var tmp = {
+ resizing : false,
+ mvMove : mvMove,
+ mvStop : mvStop
+ };
+ $('#w2ui-popup .w2ui-msg-title').on('mousedown', function (event) { mvStart(event); })
+
+ // handlers
+ function mvStart(evnt) {
+ if (!evnt) evnt = window.event;
+ if (!window.addEventListener) { window.document.attachEvent('onselectstart', function() { return false; } ); }
+ w2popup.status = 'moving';
+ tmp.resizing = true;
+ tmp.x = evnt.screenX;
+ tmp.y = evnt.screenY;
+ tmp.pos_x = $('#w2ui-popup').position().left;
+ tmp.pos_y = $('#w2ui-popup').position().top;
+ w2popup.lock({ opacity: 0 });
+ $(document).on('mousemove', tmp.mvMove);
+ $(document).on('mouseup', tmp.mvStop);
+ if (evnt.stopPropagation) evnt.stopPropagation(); else evnt.cancelBubble = true;
+ if (evnt.preventDefault) evnt.preventDefault(); else return false;
+ }
+
+ function mvMove(evnt) {
+ if (tmp.resizing != true) return;
+ if (!evnt) evnt = window.event;
+ tmp.div_x = evnt.screenX - tmp.x;
+ tmp.div_y = evnt.screenY - tmp.y;
+ $('#w2ui-popup').css({
+ '-webkit-transition': 'none',
+ '-webkit-transform': 'translate3d('+ tmp.div_x +'px, '+ tmp.div_y +'px, 0px)',
+ '-moz-transition': 'none',
+ '-moz-transform': 'translate('+ tmp.div_x +'px, '+ tmp.div_y +'px)',
+ '-ms-transition': 'none',
+ '-ms-transform': 'translate('+ tmp.div_x +'px, '+ tmp.div_y +'px)',
+ '-o-transition': 'none',
+ '-o-transform': 'translate('+ tmp.div_x +'px, '+ tmp.div_y +'px)'
+ });
+ }
+
+ function mvStop(evnt) {
+ if (tmp.resizing != true) return;
+ if (!evnt) evnt = window.event;
+ w2popup.status = 'open';
+ tmp.div_x = (evnt.screenX - tmp.x);
+ tmp.div_y = (evnt.screenY - tmp.y);
+ $('#w2ui-popup').css({
+ 'left': (tmp.pos_x + tmp.div_x) + 'px',
+ 'top': (tmp.pos_y + tmp.div_y) + 'px',
+ '-webkit-transition': 'none',
+ '-webkit-transform': 'translate3d(0px, 0px, 0px)',
+ '-moz-transition': 'none',
+ '-moz-transform': 'translate(0px, 0px)',
+ '-ms-transition': 'none',
+ '-ms-transform': 'translate(0px, 0px)',
+ '-o-transition': 'none',
+ '-o-transform': 'translate(0px, 0px)'
+ });
+ tmp.resizing = false;
+ $(document).off('mousemove', tmp.mvMove);
+ $(document).off('mouseup', tmp.mvStop);
+ w2popup.unlock();
+ }
+ return this;
+ },
+
+ keydown: function (event) {
+ var options = $('#w2ui-popup').data('options');
+ if (!options.keyboard) return;
+ // trigger event
+ var eventData = w2popup.trigger({ phase: 'before', type: 'keydown', target: 'popup', options: options, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default behavior
+ switch (event.keyCode) {
+ case 27:
+ event.preventDefault();
+ if ($('#w2ui-popup .w2ui-popup-message').length > 0) w2popup.message(); else w2popup.close();
+ break;
+ }
+ // event after
+ w2popup.trigger($.extend(eventData, { phase: 'after'}));
+ },
+
+ close: function (options) {
+ var obj = this;
+ var options = $.extend({}, $('#w2ui-popup').data('options'), options);
+ if ($('#w2ui-popup').length == 0) return;
+ // trigger event
+ var eventData = this.trigger({ phase: 'before', type: 'close', target: 'popup', options: options });
+ if (eventData.isCancelled === true) return;
+ // default behavior
+ w2popup.status = 'closing';
+ $('#w2ui-popup').css({
+ '-webkit-transition': options.speed + 's opacity, ' + options.speed + 's -webkit-transform',
+ '-webkit-transform': 'scale(0.9)',
+ '-moz-transition': options.speed + 's opacity, ' + options.speed + 's -moz-transform',
+ '-moz-transform': 'scale(0.9)',
+ '-ms-transition': options.speed + 's opacity, ' + options.speed + 's -ms-transform',
+ '-ms-transform': 'scale(0.9)',
+ '-o-transition': options.speed + 's opacity, ' + options.speed + 's -o-transform',
+ '-o-transform': 'scale(0.9)',
+ 'opacity': '0'
+ });
+ w2popup.unlockScreen(options);
+ setTimeout(function () {
+ $('#w2ui-popup').remove();
+ w2popup.status = 'closed';
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after'}));
+ }, options.speed * 1000);
+ // restore active
+ w2utils.keyboard.active(options._last_w2ui_name);
+ // remove keyboard events
+ if (options.keyboard) $(document).off('keydown', this.keydown);
+ },
+
+ toggle: function () {
+ var obj = this;
+ var options = $('#w2ui-popup').data('options');
+ // trigger event
+ var eventData = this.trigger({ phase: 'before', type: 'toggle', target: 'popup', options: options });
+ if (eventData.isCancelled === true) return;
+ // defatul action
+ if (options.maximized === true) w2popup.min(); else w2popup.max();
+ // event after
+ setTimeout(function () {
+ obj.trigger($.extend(eventData, { phase: 'after'}));
+ }, (options.speed * 1000) + 50);
+ },
+
+ max: function () {
+ var obj = this;
+ var options = $('#w2ui-popup').data('options');
+ if (options.maximized === true) return;
+ // trigger event
+ var eventData = this.trigger({ phase: 'before', type: 'max', target: 'popup', options: options });
+ if (eventData.isCancelled === true) return;
+ // default behavior
+ w2popup.status = 'resizing';
+ options.prevSize = $('#w2ui-popup').css('width') + ':' + $('#w2ui-popup').css('height');
+ // do resize
+ w2popup.resize(10000, 10000, function () {
+ w2popup.status = 'open';
+ options.maximized = true;
+ obj.trigger($.extend(eventData, { phase: 'after'}));
+ });
+ },
+
+ min: function () {
+ var obj = this;
+ var options = $('#w2ui-popup').data('options');
+ if (options.maximized !== true) return;
+ var size = options.prevSize.split(':');
+ // trigger event
+ var eventData = this.trigger({ phase: 'before', type: 'min', target: 'popup', options: options });
+ if (eventData.isCancelled === true) return;
+ // default behavior
+ w2popup.status = 'resizing';
+ // do resize
+ w2popup.resize(size[0], size[1], function () {
+ w2popup.status = 'open';
+ options.maximized = false;
+ options.prevSize = null;
+ obj.trigger($.extend(eventData, { phase: 'after'}));
+ });
+ },
+
+ get: function () {
+ return $('#w2ui-popup').data('options');
+ },
+
+ set: function (options) {
+ w2popup.open(options);
+ },
+
+ clear: function() {
+ $('#w2ui-popup .w2ui-msg-title').html('');
+ $('#w2ui-popup .w2ui-msg-body').html('');
+ $('#w2ui-popup .w2ui-msg-buttons').html('');
+ },
+
+ reset: function () {
+ w2popup.open(w2popup.defaults);
+ },
+
+ load: function (options) {
+ w2popup.status = 'loading';
+ if (String(options.url) == 'undefined') {
+ console.log('ERROR: The url parameter is empty.');
+ return;
+ }
+ var tmp = String(options.url).split('#');
+ var url = tmp[0];
+ var selector = tmp[1];
+ if (String(options) == 'undefined') options = {};
+ // load url
+ var html = $('#w2ui-popup').data(url);
+ if (typeof html != 'undefined' && html != null) {
+ popup(html, selector);
+ } else {
+ $.get(url, function (data, status, obj) { // should always be $.get as it is template
+ popup(obj.responseText, selector);
+ $('#w2ui-popup').data(url, obj.responseText); // remember for possible future purposes
+ });
+ }
+ function popup(html, selector) {
+ delete options.url;
+ $('body').append('<div id="w2ui-tmp" style="display: none">' + html + '</div>');
+ if (typeof selector != 'undefined' && $('#w2ui-tmp #'+selector).length > 0) {
+ $('#w2ui-tmp #' + selector).w2popup(options);
+ } else {
+ $('#w2ui-tmp > div').w2popup(options);
+ }
+ // link styles
+ if ($('#w2ui-tmp > style').length > 0) {
+ var style = $('<div>').append($('#w2ui-tmp > style').clone()).html();
+ if ($('#w2ui-popup #div-style').length == 0) {
+ $('#w2ui-popup').append('<div id="div-style" style="position: absolute; left: -100; width: 1px"></div>');
+ }
+ $('#w2ui-popup #div-style').html(style);
+ }
+ $('#w2ui-tmp').remove();
+ }
+ },
+
+ message: function (options) {
+ $().w2tag(); // hide all tags
+ if (!options) options = { width: 200, height: 100 };
+ if (parseInt(options.width) < 10) options.width = 10;
+ if (parseInt(options.height) < 10) options.height = 10;
+ if (typeof options.hideOnClick == 'undefined') options.hideOnClick = false;
+ var poptions = $('#w2ui-popup').data('options') || {};
+ if (typeof options.width == 'undefined' || options.width > poptions.width - 10) options.width = poptions.width - 10;
+ if (typeof options.height == 'undefined' || options.height > poptions.height - 40) options.height = poptions.height - 40; // title is 30px or so
+
+ var head = $('#w2ui-popup .w2ui-msg-title');
+ var pwidth = parseInt($('#w2ui-popup').width());
+ var msgCount = $('#w2ui-popup .w2ui-popup-message').length;
+ // remove message
+ if ($.trim(options.html) == '') {
+ $('#w2ui-popup #w2ui-message'+ (msgCount-1)).css('z-Index', 250);
+ var options = $('#w2ui-popup #w2ui-message'+ (msgCount-1)).data('options') || {};
+ $('#w2ui-popup #w2ui-message'+ (msgCount-1)).remove();
+ if (typeof options.onClose == 'function') options.onClose();
+ if (msgCount == 1) {
+ w2popup.unlock();
+ } else {
+ $('#w2ui-popup #w2ui-message'+ (msgCount-2)).show();
+ }
+ } else {
+ // hide previous messages
+ $('#w2ui-popup .w2ui-popup-message').hide();
+ // add message
+ $('#w2ui-popup .w2ui-box1')
+ .before('<div id="w2ui-message' + msgCount + '" class="w2ui-popup-message" style="display: none; ' +
+ (head.length == 0 ? 'top: 0px;' : 'top: ' + w2utils.getSize(head, 'height') + 'px;') +
+ (typeof options.width != 'undefined' ? 'width: ' + options.width + 'px; left: ' + ((pwidth - options.width) / 2) + 'px;' : 'left: 10px; right: 10px;') +
+ (typeof options.height != 'undefined' ? 'height: ' + options.height + 'px;' : 'bottom: 6px;') +
+ '-webkit-transition: .3s; -moz-transition: .3s; -ms-transition: .3s; -o-transition: .3s;"' +
+ (options.hideOnClick === true ? 'onclick="w2popup.message();"' : '') + '>' +
+ '</div>');
+ $('#w2ui-popup #w2ui-message'+ msgCount).data('options', options);
+ var display = $('#w2ui-popup #w2ui-message'+ msgCount).css('display');
+ $('#w2ui-popup #w2ui-message'+ msgCount).css({
+ '-webkit-transform': (display == 'none' ? 'translateY(-' + options.height + 'px)' : 'translateY(0px)'),
+ '-moz-transform': (display == 'none' ? 'translateY(-' + options.height + 'px)' : 'translateY(0px)'),
+ '-ms-transform': (display == 'none' ? 'translateY(-' + options.height + 'px)' : 'translateY(0px)'),
+ '-o-transform': (display == 'none' ? 'translateY(-' + options.height + 'px)' : 'translateY(0px)')
+ });
+ if (display == 'none') {
+ $('#w2ui-popup #w2ui-message'+ msgCount).show().html(options.html);
+ // timer needs to animation
+ setTimeout(function () {
+ $('#w2ui-popup #w2ui-message'+ msgCount).css({
+ '-webkit-transform': (display == 'none' ? 'translateY(0px)' : 'translateY(-' + options.height + 'px)'),
+ '-moz-transform': (display == 'none' ? 'translateY(0px)' : 'translateY(-' + options.height + 'px)'),
+ '-ms-transform': (display == 'none' ? 'translateY(0px)' : 'translateY(-' + options.height + 'px)'),
+ '-o-transform': (display == 'none' ? 'translateY(0px)' : 'translateY(-' + options.height + 'px)')
+ });
+ }, 1);
+ // timer for lock
+ setTimeout(function() {
+ $('#w2ui-popup #w2ui-message'+ msgCount).css({
+ '-webkit-transition': '0s', '-moz-transition': '0s', '-ms-transition': '0s', '-o-transition': '0s',
+ 'z-Index': 1500
+ }); // has to be on top of lock
+ if (msgCount == 0) w2popup.lock();
+ if (typeof options.onOpen == 'function') options.onOpen();
+ }, 300);
+ }
+ }
+ },
+
+ lock: function (msg, showSpinner) {
+ var args = Array.prototype.slice.call(arguments, 0);
+ args.unshift($('#w2ui-popup'));
+ w2utils.lock.apply(window, args);
+ },
+
+ unlock: function () {
+ w2utils.unlock($('#w2ui-popup'));
+ },
+
+ // --- INTERNAL FUNCTIONS
+
+ lockScreen: function (options) {
+ if ($('#w2ui-lock').length > 0) return false;
+ if (typeof options == 'undefined') options = $('#w2ui-popup').data('options');
+ if (typeof options == 'undefined') options = {};
+ options = $.extend({}, w2popup.defaults, options);
+ // show element
+ $('body').append('<div id="w2ui-lock" ' +
+ ' onmousewheel="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true; if (event.preventDefault) event.preventDefault(); else return false;"'+
+ ' style="position: ' + (w2utils.engine == 'IE5' ? 'absolute' : 'fixed') + '; z-Index: 1199; left: 0px; top: 0px; ' +
+ ' padding: 0px; margin: 0px; background-color: ' + options.color + '; width: 100%; height: 100%; opacity: 0;"></div>');
+ // lock screen
+ setTimeout(function () {
+ $('#w2ui-lock').css({
+ '-webkit-transition': options.speed + 's opacity',
+ '-moz-transition': options.speed + 's opacity',
+ '-ms-transition': options.speed + 's opacity',
+ '-o-transition': options.speed + 's opacity',
+ 'opacity': options.opacity
+ });
+ }, 1);
+ // add events
+ if (options.modal == true) {
+ $('#w2ui-lock').on('mousedown', function () {
+ $('#w2ui-lock').css({
+ '-webkit-transition': '.1s',
+ '-moz-transition': '.1s',
+ '-ms-transition': '.1s',
+ '-o-transition': '.1s',
+ 'opacity': '0.6'
+ });
+ // if (window.getSelection) window.getSelection().removeAllRanges();
+ });
+ $('#w2ui-lock').on('mouseup', function () {
+ setTimeout(function () {
+ $('#w2ui-lock').css({
+ '-webkit-transition': '.1s',
+ '-moz-transition': '.1s',
+ '-ms-transition': '.1s',
+ '-o-transition': '.1s',
+ 'opacity': options.opacity
+ });
+ }, 100);
+ // if (window.getSelection) window.getSelection().removeAllRanges();
+ });
+ } else {
+ $('#w2ui-lock').on('mouseup', function () { w2popup.close(); });
+ }
+ return true;
+ },
+
+ unlockScreen: function (options) {
+ if ($('#w2ui-lock').length == 0) return false;
+ if (typeof options == 'undefined') options = $('#w2ui-popup').data('options');
+ if (typeof options == 'undefined') options = {};
+ options = $.extend({}, w2popup.defaults, options);
+ $('#w2ui-lock').css({
+ '-webkit-transition': options.speed + 's opacity',
+ '-moz-transition': options.speed + 's opacity',
+ '-ms-transition': options.speed + 's opacity',
+ '-o-transition': options.speed + 's opacity',
+ 'opacity': 0
+ });
+ setTimeout(function () {
+ $('#w2ui-lock').remove();
+ }, options.speed * 1000);
+ return true;
+ },
+
+ resize: function (width, height, callBack) {
+ var options = $('#w2ui-popup').data('options');
+ // calculate new position
+ if (parseInt($(window).width()) - 10 < parseInt(width)) width = parseInt($(window).width()) - 10;
+ if (parseInt($(window).height()) - 10 < parseInt(height)) height = parseInt($(window).height()) - 10;
+ var top = ((parseInt($(window).height()) - parseInt(height)) / 2) * 0.8;
+ var left = (parseInt($(window).width()) - parseInt(width)) / 2;
+ // resize there
+ $('#w2ui-popup').css({
+ '-webkit-transition': options.speed + 's width, ' + options.speed + 's height, ' + options.speed + 's left, ' + options.speed + 's top',
+ '-moz-transition': options.speed + 's width, ' + options.speed + 's height, ' + options.speed + 's left, ' + options.speed + 's top',
+ '-ms-transition': options.speed + 's width, ' + options.speed + 's height, ' + options.speed + 's left, ' + options.speed + 's top',
+ '-o-transition': options.speed + 's width, ' + options.speed + 's height, ' + options.speed + 's left, ' + options.speed + 's top',
+ 'top': top,
+ 'left': left,
+ 'width': width,
+ 'height': height
+ });
+ setTimeout(function () {
+ options.width = width;
+ options.height = height;
+ if (typeof callBack == 'function') callBack();
+ }, (options.speed * 1000) + 50); // give extra 50 ms
+ }
+ }
+
+ // merge in event handling
+ $.extend(w2popup, w2utils.event);
+
+})();
+
+// ============================================
+// --- Common dialogs
+
+var w2alert = function (msg, title, callBack) {
+ if (title == null) title = w2utils.lang('Notification');
+ if ($('#w2ui-popup').length > 0 && w2popup.status != 'closing') {
+ w2popup.message({
+ width : 400,
+ height : 170,
+ html : '<div style="position: absolute; top: 0px; left: 0px; right: 0px; bottom: 45px; overflow: auto">' +
+ ' <div class="w2ui-centered" style="font-size: 13px;">' + msg + '</div>' +
+ '</div>' +
+ '<div style="position: absolute; bottom: 7px; left: 0px; right: 0px; text-align: center; padding: 5px">' +
+ ' <button onclick="w2popup.message();" class="w2ui-popup-btn btn">' + w2utils.lang('Ok') + '</button>' +
+ '</div>',
+ onClose : function () {
+ if (typeof callBack == 'function') callBack();
+ }
+ });
+ } else {
+ w2popup.open({
+ width : 450,
+ height : 220,
+ showMax : false,
+ showClose : false,
+ title : title,
+ body : '<div class="w2ui-centered" style="font-size: 13px;">' + msg + '</div>',
+ buttons : '<button onclick="w2popup.close();" class="w2ui-popup-btn btn">' + w2utils.lang('Ok') + '</button>',
+ onClose : function () {
+ if (typeof callBack == 'function') callBack();
+ }
+ });
+ }
+};
+
+var w2confirm = function (msg, title, callBack) {
+ var options = {};
+ var defaults = {
+ msg : '',
+ title : w2utils.lang('Confirmation'),
+ width : ($('#w2ui-popup').length > 0 ? 400 : 450),
+ height : ($('#w2ui-popup').length > 0 ? 170 : 220),
+ yes_text : 'Yes',
+ yes_class : '',
+ yes_style : '',
+ yes_callBack: null,
+ no_text : 'No',
+ no_class : '',
+ no_style : '',
+ no_callBack : null,
+ callBack : null
+ };
+ if (arguments.length == 1 && typeof msg == 'object') {
+ $.extend(options, defaults, msg);
+ } else {
+ if (typeof title == 'function') {
+ $.extend(options, defaults, {
+ msg : msg,
+ callBack: title
+ })
+ } else {
+ $.extend(options, defaults, {
+ msg : msg,
+ title : title,
+ callBack: callBack
+ })
+ }
+ }
+ if ($('#w2ui-popup').length > 0 && w2popup.status != 'closing') {
+ if (options.width > w2popup.get().width) options.width = w2popup.get().width;
+ if (options.height > (w2popup.get().height - 50)) options.height = w2popup.get().height - 50;
+ w2popup.message({
+ width : options.width,
+ height : options.height,
+ html : '<div style="position: absolute; top: 0px; left: 0px; right: 0px; bottom: 40px; overflow: auto">' +
+ ' <div class="w2ui-centered" style="font-size: 13px;">' + options.msg + '</div>' +
+ '</div>' +
+ '<div style="position: absolute; bottom: 7px; left: 0px; right: 0px; text-align: center; padding: 5px">' +
+ ' <button id="Yes" class="w2ui-popup-btn btn '+ options.yes_class +'" style="'+ options.yes_style +'">' + w2utils.lang(options.yes_text) + '</button>' +
+ ' <button id="No" class="w2ui-popup-btn btn '+ options.no_class +'" style="'+ options.no_style +'">' + w2utils.lang(options.no_text) + '</button>' +
+ '</div>',
+ onOpen: function () {
+ $('#w2ui-popup .w2ui-popup-message .btn').on('click', function (event) {
+ w2popup.message();
+ if (typeof options.callBack == 'function') options.callBack(event.target.id);
+ if (event.target.id == 'Yes' && typeof options.yes_callBack == 'function') options.yes_callBack();
+ if (event.target.id == 'No' && typeof options.no_callBack == 'function') options.no_callBack();
+ });
+ },
+ onKeydown: function (event) {
+ switch (event.originalEvent.keyCode) {
+ case 13: // enter
+ if (typeof options.callBack == 'function') options.callBack('Yes');
+ if (typeof options.yes_callBack == 'function') options.yes_callBack();
+ w2popup.message();
+ break
+ case 27: // esc
+ if (typeof options.callBack == 'function') options.callBack('No');
+ if (typeof options.no_callBack == 'function') options.no_callBack();
+ w2popup.message();
+ break
+ }
+ }
+ });
+
+ } else {
+
+ if (!w2utils.isInt(options.height)) options.height = options.height + 50;
+ w2popup.open({
+ width : options.width,
+ height : options.height,
+ title : options.title,
+ modal : true,
+ showClose : false,
+ body : '<div class="w2ui-centered" style="font-size: 13px;">' + options.msg + '</div>',
+ buttons : '<button id="Yes" class="w2ui-popup-btn btn '+ options.yes_class +'" style="'+ options.yes_style +'">'+ w2utils.lang(options.yes_text) +'</button>'+
+ '<button id="No" class="w2ui-popup-btn btn '+ options.no_class +'" style="'+ options.no_style +'">'+ w2utils.lang(options.no_text) +'</button>',
+ onOpen: function (event) {
+ event.onComplete = function () {
+ $('#w2ui-popup .w2ui-popup-btn').on('click', function (event) {
+ w2popup.close();
+ if (typeof options.callBack == 'function') options.callBack(event.target.id);
+ if (event.target.id == 'Yes' && typeof options.yes_callBack == 'function') options.yes_callBack();
+ if (event.target.id == 'No' && typeof options.no_callBack == 'function') options.no_callBack();
+ });
+ }
+ },
+ onKeydown: function (event) {
+ switch (event.originalEvent.keyCode) {
+ case 13: // enter
+ if (typeof options.callBack == 'function') options.callBack('Yes');
+ if (typeof options.yes_callBack == 'function') options.yes_callBack();
+ w2popup.close();
+ break
+ case 27: // esc
+ if (typeof options.callBack == 'function') options.callBack('No');
+ if (typeof options.no_callBack == 'function') options.no_callBack();
+ w2popup.close();
+ break
+ }
+ }
+ });
+ }
+
+ return {
+ yes: function (fun) {
+ options.yes_callBack = fun;
+ return this;
+ },
+ no: function (fun) {
+ options.no_callBack = fun;
+ return this;
+ }
+ };
+};
+/************************************************************************
+* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
+* - Following objects defined
+* - w2tabs - tabs widget
+* - $().w2tabs - jQuery wrapper
+* - Dependencies: jQuery, w2utils
+*
+* == NICE TO HAVE ==
+* - on overflow display << >>
+*
+************************************************************************/
+
+(function () {
+ var w2tabs = function (options) {
+ this.box = null; // DOM Element that holds the element
+ this.name = null; // unique name for w2ui
+ this.active = null;
+ this.tabs = [];
+ this.routeData = {}; // data for dynamic routes
+ this.right = '';
+ this.style = '';
+ this.onClick = null;
+ this.onClose = null;
+ this.onRender = null;
+ this.onRefresh = null;
+ this.onResize = null;
+ this.onDestroy = null;
+
+ $.extend(this, { handlers: [] });
+ $.extend(true, this, w2obj.tabs, options);
+ };
+
+ // ====================================================
+ // -- Registers as a jQuery plugin
+
+ $.fn.w2tabs = function(method) {
+ if (typeof method === 'object' || !method ) {
+ // check name parameter
+ if (!w2utils.checkName(method, 'w2tabs')) return;
+ // extend tabs
+ var tabs = method.tabs || [];
+ var object = new w2tabs(method);
+ for (var i = 0; i < tabs.length; i++) {
+ object.tabs[i] = $.extend({}, w2tabs.prototype.tab, tabs[i]);
+ }
+ if ($(this).length !== 0) {
+ object.render($(this)[0]);
+ }
+ // register new object
+ w2ui[object.name] = object;
+ return object;
+ } else if (w2ui[$(this).attr('name')]) {
+ var obj = w2ui[$(this).attr('name')];
+ obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
+ return this;
+ } else {
+ console.log('ERROR: Method ' + method + ' does not exist on jQuery.w2tabs' );
+ return undefined;
+ }
+ };
+
+ // ====================================================
+ // -- Implementation of core functionality
+
+ w2tabs.prototype = {
+ tab : {
+ id : null, // command to be sent to all event handlers
+ text : '',
+ route : null,
+ hidden : false,
+ disabled : false,
+ closable : false,
+ hint : '',
+ onClick : null,
+ onRefresh : null,
+ onClose : null
+ },
+
+ add: function (tab) {
+ return this.insert(null, tab);
+ },
+
+ insert: function (id, tab) {
+ if (!$.isArray(tab)) tab = [tab];
+ // assume it is array
+ for (var i = 0; i < tab.length; i++) {
+ // checks
+ if (typeof tab[i].id === 'undefined') {
+ console.log('ERROR: The parameter "id" is required but not supplied. (obj: '+ this.name +')');
+ return;
+ }
+ if (!w2utils.checkUniqueId(tab[i].id, this.tabs, 'tabs', this.name)) return;
+ // add tab
+ var newTab = $.extend({}, w2tabs.prototype.tab, tab[i]);
+ if (id === null || typeof id === 'undefined') {
+ this.tabs.push(newTab);
+ } else {
+ var middle = this.get(id, true);
+ this.tabs = this.tabs.slice(0, middle).concat([newTab], this.tabs.slice(middle));
+ }
+ this.refresh(tab[i].id);
+ }
+ },
+
+ remove: function () {
+ var removed = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ var tab = this.get(arguments[a]);
+ if (!tab) return false;
+ removed++;
+ // remove from array
+ this.tabs.splice(this.get(tab.id, true), 1);
+ // remove from screen
+ $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(tab.id)).remove();
+ }
+ return removed;
+ },
+
+ select: function (id) {
+ if (this.active == id || this.get(id) === null) return false;
+ this.active = id;
+ this.refresh();
+ return true;
+ },
+
+ set: function (id, tab) {
+ var index = this.get(id, true);
+ if (index === null) return false;
+ $.extend(this.tabs[index], tab);
+ this.refresh(id);
+ return true;
+ },
+
+ get: function (id, returnIndex) {
+ if (arguments.length === 0) {
+ var all = [];
+ for (var i1 = 0; i1 < this.tabs.length; i1++) {
+ if (this.tabs[i1].id != null) {
+ all.push(this.tabs[i1].id);
+ }
+ }
+ return all;
+ } else {
+ for (var i2 = 0; i2 < this.tabs.length; i2++) {
+ if (this.tabs[i2].id == id) { // need to be == since id can be numeric
+ return (returnIndex === true ? i2 : this.tabs[i2]);
+ }
+ }
+ }
+ return null;
+ },
+
+ show: function () {
+ var obj = this;
+ var shown = 0;
+ var tmp = [];
+ for (var a = 0; a < arguments.length; a++) {
+ var tab = this.get(arguments[a]);
+ if (!tab || tab.hidden === false) continue;
+ shown++;
+ tab.hidden = false;
+ tmp.push(tab.id);
+ }
+ setTimeout(function () { for (var t in tmp) obj.refresh(tmp[t]); }, 15); // needs timeout
+ return shown;
+ },
+
+ hide: function () {
+ var obj = this;
+ var hidden= 0;
+ var tmp = [];
+ for (var a = 0; a < arguments.length; a++) {
+ var tab = this.get(arguments[a]);
+ if (!tab || tab.hidden === true) continue;
+ hidden++;
+ tab.hidden = true;
+ tmp.push(tab.id);
+ }
+ setTimeout(function () { for (var t in tmp) obj.refresh(tmp[t]); }, 15); // needs timeout
+ return hidden;
+ },
+
+ enable: function () {
+ var obj = this;
+ var enabled = 0;
+ var tmp = [];
+ for (var a = 0; a < arguments.length; a++) {
+ var tab = this.get(arguments[a]);
+ if (!tab || tab.disabled === false) continue;
+ enabled++;
+ tab.disabled = false;
+ tmp.push(tab.id);
+ }
+ setTimeout(function () { for (var t in tmp) obj.refresh(tmp[t]); }, 15); // needs timeout
+ return enabled;
+ },
+
+ disable: function () {
+ var obj = this;
+ var disabled = 0;
+ var tmp = [];
+ for (var a = 0; a < arguments.length; a++) {
+ var tab = this.get(arguments[a]);
+ if (!tab || tab.disabled === true) continue;
+ disabled++;
+ tab.disabled = true;
+ tmp.push(tab.id);
+ }
+ setTimeout(function () { for (var t in tmp) obj.refresh(tmp[t]); }, 15); // needs timeout
+ return disabled;
+ },
+
+ refresh: function (id) {
+ var time = (new Date()).getTime();
+ // if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'refresh', target: (typeof id !== 'undefined' ? id : this.name), object: this.get(id) });
+ if (eventData.isCancelled === true) return;
+ if (typeof id === 'undefined') {
+ // refresh all
+ for (var i = 0; i < this.tabs.length; i++) this.refresh(this.tabs[i].id);
+ } else {
+ // create or refresh only one item
+ var tab = this.get(id);
+ if (tab === null) return false;
+ if (typeof tab.caption !== 'undefined') tab.text = tab.caption;
+
+ var jq_el = $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(tab.id));
+ var tabHTML = (tab.closable ? '<div class="w2ui-tab-close" onclick="w2ui[\''+ this.name +'\'].animateClose(\''+ tab.id +'\', event);"></div>' : '') +
+ ' <div class="w2ui-tab'+ (this.active === tab.id ? ' active' : '') + (tab.closable ? ' closable' : '') +'" '+
+ ' title="'+ (typeof tab.hint !== 'undefined' ? tab.hint : '') +'"'+
+ ' onclick="w2ui[\''+ this.name +'\'].click(\''+ tab.id +'\', event);">' + tab.text + '</div>';
+ if (jq_el.length === 0) {
+ // does not exist - create it
+ var addStyle = '';
+ if (tab.hidden) { addStyle += 'display: none;'; }
+ if (tab.disabled) { addStyle += 'opacity: 0.2; -moz-opacity: 0.2; -webkit-opacity: 0.2; -o-opacity: 0.2; filter:alpha(opacity=20);'; }
+ var html = '<td id="tabs_'+ this.name + '_tab_'+ tab.id +'" style="'+ addStyle +'" valign="middle">'+ tabHTML + '</td>';
+ if (this.get(id, true) !== this.tabs.length-1 && $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(this.tabs[parseInt(this.get(id, true))+1].id)).length > 0) {
+ $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(this.tabs[parseInt(this.get(id, true))+1].id)).before(html);
+ } else {
+ $(this.box).find('#tabs_'+ this.name +'_right').before(html);
+ }
+ } else {
+ // refresh
+ jq_el.html(tabHTML);
+ if (tab.hidden) { jq_el.css('display', 'none'); }
+ else { jq_el.css('display', ''); }
+ if (tab.disabled) { jq_el.css({ 'opacity': '0.2', '-moz-opacity': '0.2', '-webkit-opacity': '0.2', '-o-opacity': '0.2', 'filter': 'alpha(opacity=20)' }); }
+ else { jq_el.css({ 'opacity': '1', '-moz-opacity': '1', '-webkit-opacity': '1', '-o-opacity': '1', 'filter': 'alpha(opacity=100)' }); }
+ }
+ }
+ // right html
+ $('#tabs_'+ this.name +'_right').html(this.right);
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return (new Date()).getTime() - time;
+ },
+
+ render: function (box) {
+ var time = (new Date()).getTime();
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'render', target: this.name, box: box });
+ if (eventData.isCancelled === true) return;
+ // default action
+ // if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ if (typeof box !== 'undefined' && box !== null) {
+ if ($(this.box).find('> table #tabs_'+ this.name + '_right').length > 0) {
+ $(this.box)
+ .removeAttr('name')
+ .removeClass('w2ui-reset w2ui-tabs')
+ .html('');
+ }
+ this.box = box;
+ }
+ if (!this.box) return false;
+ // render all buttons
+ var html = '<table cellspacing="0" cellpadding="1" width="100%">'+
+ ' <tr><td width="100%" id="tabs_'+ this.name +'_right" align="right">'+ this.right +'</td></tr>'+
+ '</table>';
+ $(this.box)
+ .attr('name', this.name)
+ .addClass('w2ui-reset w2ui-tabs')
+ .html(html);
+ if ($(this.box).length > 0) $(this.box)[0].style.cssText += this.style;
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ this.refresh();
+ return (new Date()).getTime() - time;
+ },
+
+ resize: function () {
+ var time = (new Date()).getTime();
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'resize', target: this.name });
+ if (eventData.isCancelled === true) return;
+
+ // intentionaly blank
+
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return (new Date()).getTime() - time;
+ },
+
+ destroy: function () {
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'destroy', target: this.name });
+ if (eventData.isCancelled === true) return;
+ // clean up
+ if ($(this.box).find('> table #tabs_'+ this.name + '_right').length > 0) {
+ $(this.box)
+ .removeAttr('name')
+ .removeClass('w2ui-reset w2ui-tabs')
+ .html('');
+ }
+ delete w2ui[this.name];
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ // ===================================================
+ // -- Internal Event Handlers
+
+ click: function (id, event) {
+ var tab = this.get(id);
+ if (tab === null || tab.disabled) return false;
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'click', target: id, tab: tab, object: tab, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default action
+ $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(this.active) +' .w2ui-tab').removeClass('active');
+ this.active = tab.id;
+ // route processing
+ if (tab.route) {
+ var route = String('/'+ tab.route).replace(/\/{2,}/g, '/');
+ var info = w2utils.parseRoute(route);
+ if (info.keys.length > 0) {
+ for (var k = 0; k < info.keys.length; k++) {
+ if (this.routeData[info.keys[k].name] == null) continue;
+ route = route.replace((new RegExp(':'+ info.keys[k].name, 'g')), this.routeData[info.keys[k].name]);
+ }
+ }
+ setTimeout(function () { window.location.hash = route; }, 1);
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ this.refresh(id);
+ },
+
+ animateClose: function(id, event) {
+ var tab = this.get(id);
+ if (tab === null || tab.disabled) return false;
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'close', target: id, object: this.get(id), originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default action
+ var obj = this;
+ $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(tab.id)).css({
+ '-webkit-transition': '.2s',
+ '-moz-transition': '2s',
+ '-ms-transition': '.2s',
+ '-o-transition': '.2s',
+ opacity: '0' });
+ setTimeout(function () {
+ var width = $(obj.box).find('#tabs_'+ obj.name +'_tab_'+ w2utils.escapeId(tab.id)).width();
+ $(obj.box).find('#tabs_'+ obj.name +'_tab_'+ w2utils.escapeId(tab.id))
+ .html('<div style="width: '+ width +'px; -webkit-transition: .2s; -moz-transition: .2s; -ms-transition: .2s; -o-transition: .2s"></div>');
+ setTimeout(function () {
+ $(obj.box).find('#tabs_'+ obj.name +'_tab_'+ w2utils.escapeId(tab.id)).find(':first-child').css({ 'width': '0px' });
+ }, 50);
+ }, 200);
+ setTimeout(function () {
+ obj.remove(id);
+ }, 450);
+ // event before
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ this.refresh();
+ },
+
+ animateInsert: function(id, tab) {
+ if (this.get(id) === null) return;
+ if (!$.isPlainObject(tab)) return;
+ // check for unique
+ if (!w2utils.checkUniqueId(tab.id, this.tabs, 'tabs', this.name)) return;
+ // insert simple div
+ var jq_el = $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(tab.id));
+ if (jq_el.length !== 0) return; // already exists
+ // measure width
+ if (typeof tab.caption !== 'undefined') tab.text = tab.caption;
+ var tmp = '<div id="_tmp_tabs" class="w2ui-reset w2ui-tabs" style="position: absolute; top: -1000px;">'+
+ '<table cellspacing="0" cellpadding="1" width="100%"><tr>'+
+ '<td id="_tmp_simple_tab" style="" valign="middle">'+
+ (tab.closable ? '<div class="w2ui-tab-close"></div>' : '') +
+ ' <div class="w2ui-tab '+ (this.active === tab.id ? 'active' : '') +'">'+ tab.text +'</div>'+
+ '</td></tr></table>'+
+ '</div>';
+ $('body').append(tmp);
+ // create dummy element
+ var tabHTML = '<div style="width: 1px; -webkit-transition: 0.2s; -moz-transition: 0.2s; -ms-transition: 0.2s; -o-transition: 0.2s;">&nbsp;</div>';
+ var addStyle = '';
+ if (tab.hidden) { addStyle += 'display: none;'; }
+ if (tab.disabled) { addStyle += 'opacity: 0.2; -moz-opacity: 0.2; -webkit-opacity: 0.2; -o-opacity: 0.2; filter:alpha(opacity=20);'; }
+ var html = '<td id="tabs_'+ this.name +'_tab_'+ tab.id +'" style="'+ addStyle +'" valign="middle">'+ tabHTML +'</td>';
+ if (this.get(id, true) !== this.tabs.length && $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(this.tabs[parseInt(this.get(id, true))].id)).length > 0) {
+ $(this.box).find('#tabs_'+ this.name +'_tab_'+ w2utils.escapeId(this.tabs[parseInt(this.get(id, true))].id)).before(html);
+ } else {
+ $(this.box).find('#tabs_'+ this.name +'_right').before(html);
+ }
+ // -- move
+ var obj = this;
+ setTimeout(function () {
+ var width = $('#_tmp_simple_tab').width();
+ $('#_tmp_tabs').remove();
+ $('#tabs_'+ obj.name +'_tab_'+ w2utils.escapeId(tab.id) +' > div').css('width', width+'px');
+ }, 1);
+ setTimeout(function () {
+ // insert for real
+ obj.insert(id, tab);
+ }, 200);
+ }
+ };
+
+ $.extend(w2tabs.prototype, w2utils.event);
+ w2obj.tabs = w2tabs;
+})();
+
+
+/************************************************************************
+* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
+* - Following objects defined
+* - w2toolbar - toolbar widget
+* - $().w2toolbar - jQuery wrapper
+* - Dependencies: jQuery, w2utils
+*
+* == NICE TO HAVE ==
+* - on overflow display << >>
+* - verticle toolbar
+*
+************************************************************************/
+
+(function () {
+ var w2toolbar = function (options) {
+ this.box = null; // DOM Element that holds the element
+ this.name = null; // unique name for w2ui
+ this.routeData = {}; // data for dynamic routes
+ this.items = [];
+ this.right = ''; // HTML text on the right of toolbar
+ this.onClick = null;
+ this.onRender = null;
+ this.onRefresh = null;
+ this.onResize = null;
+ this.onDestroy = null;
+
+ $.extend(true, this, w2obj.toolbar, options);
+ };
+
+ // ====================================================
+ // -- Registers as a jQuery plugin
+
+ $.fn.w2toolbar = function(method) {
+ if (typeof method === 'object' || !method ) {
+ // check name parameter
+ if (!w2utils.checkName(method, 'w2toolbar')) return;
+ // extend items
+ var items = method.items || [];
+ var object = new w2toolbar(method);
+ $.extend(object, { items: [], handlers: [] });
+ for (var i = 0; i < items.length; i++) {
+ object.items[i] = $.extend({}, w2toolbar.prototype.item, items[i]);
+ }
+ if ($(this).length !== 0) {
+ object.render($(this)[0]);
+ }
+ // register new object
+ w2ui[object.name] = object;
+ return object;
+
+ } else if (w2ui[$(this).attr('name')]) {
+ var obj = w2ui[$(this).attr('name')];
+ obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
+ return this;
+ } else {
+ console.log('ERROR: Method ' + method + ' does not exist on jQuery.w2toolbar' );
+ }
+ };
+
+ // ====================================================
+ // -- Implementation of core functionality
+
+ w2toolbar.prototype = {
+ item: {
+ id : null, // command to be sent to all event handlers
+ type : 'button', // button, check, radio, drop, menu, break, html, spacer
+ text : '',
+ route : null, // if not null, it is route to go
+ html : '',
+ img : null,
+ icon : null,
+ count : null,
+ hidden : false,
+ disabled : false,
+ checked : false, // used for radio buttons
+ arrow : true, // arrow down for drop/menu types
+ hint : '',
+ group : null, // used for radio buttons
+ items : null, // for type menu it is an array of items in the menu
+ overlay : {},
+ onClick : null
+ },
+
+ add: function (items) {
+ this.insert(null, items);
+ },
+
+ insert: function (id, items) {
+ if (!$.isArray(items)) items = [items];
+ for (var o = 0; o < items.length; o++) {
+ // checks
+ if (typeof items[o].type === 'undefined') {
+ console.log('ERROR: The parameter "type" is required but not supplied in w2toolbar.add() method.');
+ return;
+ }
+ if ($.inArray(String(items[o].type), ['button', 'check', 'radio', 'drop', 'menu', 'break', 'html', 'spacer']) === -1) {
+ console.log('ERROR: The parameter "type" should be one of the following [button, check, radio, drop, menu, break, html, spacer] '+
+ 'in w2toolbar.add() method.');
+ return;
+ }
+ if (typeof items[o].id === 'undefined') {
+ console.log('ERROR: The parameter "id" is required but not supplied in w2toolbar.add() method.');
+ return;
+ }
+ if (!w2utils.checkUniqueId(items[o].id, this.items, 'toolbar items', this.name)) return;
+ // add item
+ var it = $.extend({}, w2toolbar.prototype.item, items[o]);
+ if (id == null) {
+ this.items.push(it);
+ } else {
+ var middle = this.get(id, true);
+ this.items = this.items.slice(0, middle).concat([it], this.items.slice(middle));
+ }
+ this.refresh(it.id);
+ }
+ },
+
+ remove: function () {
+ var removed = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ var it = this.get(arguments[a]);
+ if (!it) continue;
+ removed++;
+ // remove from screen
+ $(this.box).find('#tb_'+ this.name +'_item_'+ w2utils.escapeId(it.id)).remove();
+ // remove from array
+ var ind = this.get(it.id, true);
+ if (ind) this.items.splice(ind, 1);
+ }
+ return removed;
+ },
+
+ set: function (id, item) {
+ var index = this.get(id, true);
+ if (index === null) return false;
+ $.extend(this.items[index], item);
+ this.refresh(id);
+ return true;
+ },
+
+ get: function (id, returnIndex) {
+ if (arguments.length === 0) {
+ var all = [];
+ for (var i1 = 0; i1 < this.items.length; i1++) if (this.items[i1].id !== null) all.push(this.items[i1].id);
+ return all;
+ }
+ for (var i2 = 0; i2 < this.items.length; i2++) {
+ if (this.items[i2].id === id) {
+ if (returnIndex === true) return i2; else return this.items[i2];
+ }
+ }
+ return null;
+ },
+
+ show: function () {
+ var obj = this;
+ var items = 0;
+ var tmp = [];
+ for (var a = 0; a < arguments.length; a++) {
+ var it = this.get(arguments[a]);
+ if (!it) continue;
+ items++;
+ it.hidden = false;
+ tmp.push(it.id);
+ }
+ setTimeout(function () { for (var t in tmp) obj.refresh(tmp[t]); }, 15); // needs timeout
+ return items;
+ },
+
+ hide: function () {
+ var obj = this;
+ var items = 0;
+ var tmp = [];
+ for (var a = 0; a < arguments.length; a++) {
+ var it = this.get(arguments[a]);
+ if (!it) continue;
+ items++;
+ it.hidden = true;
+ tmp.push(it.id);
+ }
+ setTimeout(function () { for (var t in tmp) obj.refresh(tmp[t]); }, 15); // needs timeout
+ return items;
+ },
+
+ enable: function () {
+ var obj = this;
+ var items = 0;
+ var tmp = [];
+ for (var a = 0; a < arguments.length; a++) {
+ var it = this.get(arguments[a]);
+ if (!it) continue;
+ items++;
+ it.disabled = false;
+ tmp.push(it.id);
+ }
+ setTimeout(function () { for (var t in tmp) obj.refresh(tmp[t]); }, 15); // needs timeout
+ return items;
+ },
+
+ disable: function () {
+ var obj = this;
+ var items = 0;
+ var tmp = [];
+ for (var a = 0; a < arguments.length; a++) {
+ var it = this.get(arguments[a]);
+ if (!it) continue;
+ items++;
+ it.disabled = true;
+ tmp.push(it.id);
+ }
+ setTimeout(function () { for (var t in tmp) obj.refresh(tmp[t]); }, 15); // needs timeout
+ return items;
+ },
+
+ check: function () {
+ var obj = this;
+ var items = 0;
+ var tmp = [];
+ for (var a = 0; a < arguments.length; a++) {
+ var it = this.get(arguments[a]);
+ if (!it) continue;
+ items++;
+ it.checked = true;
+ tmp.push(it.id);
+ }
+ setTimeout(function () { for (var t in tmp) obj.refresh(tmp[t]); }, 15); // needs timeout
+ return items;
+ },
+
+ uncheck: function () {
+ var obj = this;
+ var items = 0;
+ var tmp = [];
+ for (var a = 0; a < arguments.length; a++) {
+ var it = this.get(arguments[a]);
+ if (!it) continue;
+ items++;
+ it.checked = false;
+ tmp.push(it.id);
+ }
+ setTimeout(function () { for (var t in tmp) obj.refresh(tmp[t]); }, 15); // needs timeout
+ return items;
+ },
+
+ render: function (box) {
+ var time = (new Date()).getTime();
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'render', target: this.name, box: box });
+ if (eventData.isCancelled === true) return;
+
+ if (box != null) {
+ if ($(this.box).find('> table #tb_'+ this.name + '_right').length > 0) {
+ $(this.box)
+ .removeAttr('name')
+ .removeClass('w2ui-reset w2ui-toolbar')
+ .html('');
+ }
+ this.box = box;
+ }
+ if (!this.box) return;
+ // render all buttons
+ var html = '<table cellspacing="0" cellpadding="0" width="100%">'+
+ '<tr>';
+ for (var i = 0; i < this.items.length; i++) {
+ var it = this.items[i];
+ if (it.id == null) it.id = "item_" + i;
+ if (it === null) continue;
+ if (it.type === 'spacer') {
+ html += '<td width="100%" id="tb_'+ this.name +'_item_'+ it.id +'" align="right"></td>';
+ } else {
+ html += '<td id="tb_'+ this.name + '_item_'+ it.id +'" style="'+ (it.hidden ? 'display: none' : '') +'" '+
+ ' class="'+ (it.disabled ? 'disabled' : '') +'" valign="middle">'+ this.getItemHTML(it) +
+ '</td>';
+ }
+ }
+ html += '<td width="100%" id="tb_'+ this.name +'_right" align="right">'+ this.right +'</td>';
+ html += '</tr>'+
+ '</table>';
+ $(this.box)
+ .attr('name', this.name)
+ .addClass('w2ui-reset w2ui-toolbar')
+ .html(html);
+ if ($(this.box).length > 0) $(this.box)[0].style.cssText += this.style;
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return (new Date()).getTime() - time;
+ },
+
+ refresh: function (id) {
+ var time = (new Date()).getTime();
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'refresh', target: (typeof id !== 'undefined' ? id : this.name), item: this.get(id) });
+ if (eventData.isCancelled === true) return;
+
+ if (id == null) {
+ // refresh all
+ for (var i = 0; i < this.items.length; i++) {
+ var it1 = this.items[i];
+ if (it1.id == null) it1.id = "item_" + i;
+ this.refresh(it1.id);
+ }
+ }
+ // create or refresh only one item
+ var it = this.get(id);
+ if (it === null) return false;
+
+ var el = $(this.box).find('#tb_'+ this.name +'_item_'+ w2utils.escapeId(it.id));
+ var html = this.getItemHTML(it);
+ if (el.length === 0) {
+ // does not exist - create it
+ if (it.type === 'spacer') {
+ html = '<td width="100%" id="tb_'+ this.name +'_item_'+ it.id +'" align="right"></td>';
+ } else {
+ html = '<td id="tb_'+ this.name + '_item_'+ it.id +'" style="'+ (it.hidden ? 'display: none' : '') +'" '+
+ ' class="'+ (it.disabled ? 'disabled' : '') +'" valign="middle">'+ html +
+ '</td>';
+ }
+ if (this.get(id, true) === this.items.length-1) {
+ $(this.box).find('#tb_'+ this.name +'_right').before(html);
+ } else {
+ $(this.box).find('#tb_'+ this.name +'_item_'+ w2utils.escapeId(this.items[parseInt(this.get(id, true))+1].id)).before(html);
+ }
+ } else {
+ // refresh
+ el.html(html);
+ if (it.hidden) { el.css('display', 'none'); } else { el.css('display', ''); }
+ if (it.disabled) { el.addClass('disabled'); } else { el.removeClass('disabled'); }
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return (new Date()).getTime() - time;
+ },
+
+ resize: function () {
+ var time = (new Date()).getTime();
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'resize', target: this.name });
+ if (eventData.isCancelled === true) return;
+
+ // intentionaly blank
+
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return (new Date()).getTime() - time;
+ },
+
+ destroy: function () {
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'destroy', target: this.name });
+ if (eventData.isCancelled === true) return;
+ // clean up
+ if ($(this.box).find('> table #tb_'+ this.name + '_right').length > 0) {
+ $(this.box)
+ .removeAttr('name')
+ .removeClass('w2ui-reset w2ui-toolbar')
+ .html('');
+ }
+ $(this.box).html('');
+ delete w2ui[this.name];
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ // ========================================
+ // --- Internal Functions
+
+ getItemHTML: function (item) {
+ var html = '';
+
+ if (typeof item.caption !== 'undefined') item.text = item.caption;
+ if (typeof item.hint === 'undefined') item.hint = '';
+ if (typeof item.text === 'undefined') item.text = '';
+
+ switch (item.type) {
+ case 'menu':
+ case 'button':
+ case 'check':
+ case 'radio':
+ case 'drop':
+ var img = '<td>&nbsp;</td>';
+ if (item.img) img = '<td><div class="w2ui-tb-image w2ui-icon '+ item.img +'"></div></td>';
+ if (item.icon) img = '<td><div class="w2ui-tb-image"><span class="'+ item.icon +'"></span></div></td>';
+ html += '<table cellpadding="0" cellspacing="0" title="'+ item.hint +'" class="w2ui-button '+ (item.checked ? 'checked' : '') +'" '+
+ ' onclick = "var el=w2ui[\''+ this.name + '\']; if (el) el.click(\''+ item.id +'\', event);" '+
+ ' onmouseover = "' + (!item.disabled ? "$(this).addClass('over');" : "") + '"'+
+ ' onmouseout = "' + (!item.disabled ? "$(this).removeClass('over').removeClass('down');" : "") + '"'+
+ ' onmousedown = "' + (!item.disabled ? "$(this).addClass('down');" : "") + '"'+
+ ' onmouseup = "' + (!item.disabled ? "$(this).removeClass('down');" : "") + '"'+
+ '>'+
+ '<tr><td>'+
+ ' <table cellpadding="1" cellspacing="0">'+
+ ' <tr>' +
+ img +
+ (item.text !== '' ? '<td class="w2ui-tb-caption" nowrap>'+ item.text +'</td>' : '') +
+ (item.count != null ? '<td class="w2ui-tb-count" nowrap><span>'+ item.count +'</span></td>' : '') +
+ (((item.type === 'drop' || item.type === 'menu') && item.arrow !== false) ?
+ '<td class="w2ui-tb-down" nowrap><div></div></td>' : '') +
+ ' </tr></table>'+
+ '</td></tr></table>';
+ break;
+
+ case 'break':
+ html += '<table cellpadding="0" cellspacing="0"><tr>'+
+ ' <td><div class="w2ui-break">&nbsp;</div></td>'+
+ '</tr></table>';
+ break;
+
+ case 'html':
+ html += '<table cellpadding="0" cellspacing="0"><tr>'+
+ ' <td nowrap>' + item.html + '</td>'+
+ '</tr></table>';
+ break;
+ }
+
+ var newHTML = '';
+ if (typeof item.onRender === 'function') newHTML = item.onRender.call(this, item.id, html);
+ if (typeof this.onRender === 'function') newHTML = this.onRender(item.id, html);
+ if (newHTML !== '' && newHTML != null) html = newHTML;
+ return html;
+ },
+
+ menuClick: function (event) {
+ var obj = this;
+ if (event.item && !event.item.disabled) {
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'click', target: event.item.id + ':' + event.subItem.id, item: event.item,
+ subItem: event.subItem, originalEvent: event.originalEvent });
+ if (eventData.isCancelled === true) return;
+
+ // route processing
+ var it = event.subItem;
+ if (it.route) {
+ var route = String('/'+ it.route).replace(/\/{2,}/g, '/');
+ var info = w2utils.parseRoute(route);
+ if (info.keys.length > 0) {
+ for (var k = 0; k < info.keys.length; k++) {
+ if (obj.routeData[info.keys[k].name] == null) continue;
+ route = route.replace((new RegExp(':'+ info.keys[k].name, 'g')), this.routeData[info.keys[k].name]);
+ }
+ }
+ setTimeout(function () { window.location.hash = route; }, 1);
+ }
+
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ },
+
+ click: function (id, event) {
+ var obj = this;
+ var it = this.get(id);
+ if (it && !it.disabled) {
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'click', target: (typeof id !== 'undefined' ? id : this.name),
+ item: it, object: it, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+
+ var btn = $('#tb_'+ this.name +'_item_'+ w2utils.escapeId(it.id) +' table.w2ui-button');
+ btn.removeClass('down');
+
+ if (it.type === 'radio') {
+ for (var i = 0; i < this.items.length; i++) {
+ var itt = this.items[i];
+ if (itt == null || itt.id === it.id || itt.type !== 'radio') continue;
+ if (itt.group === it.group && itt.checked) {
+ itt.checked = false;
+ this.refresh(itt.id);
+ }
+ }
+ it.checked = true;
+ btn.addClass('checked');
+ }
+
+ if (it.type === 'drop' || it.type === 'menu') {
+ if (it.checked) {
+ // if it was already checked, second click will hide it
+ it.checked = false;
+ } else {
+ // show overlay
+ setTimeout(function () {
+ var el = $('#tb_'+ obj.name +'_item_'+ w2utils.escapeId(it.id));
+ if (!$.isPlainObject(it.overlay)) it.overlay = {};
+ var left = (el.width() - 50) / 2;
+ if (left > 19) left = 19;
+ if (it.type === 'drop') {
+ el.w2overlay(it.html, $.extend({ left: left, top: 3 }, it.overlay));
+ }
+ if (it.type === 'menu') {
+ el.w2menu(it.items, $.extend({ left: left, top: 3 }, it.overlay, {
+ select: function (event) {
+ obj.menuClick({ item: it, subItem: event.item, originalEvent: event.originalEvent });
+ hideDrop();
+ }
+ }));
+ }
+ // window.click to hide it
+ $(document).on('click', hideDrop);
+ function hideDrop() {
+ $(document).off('click', hideDrop);
+ it.checked = false;
+ btn.removeClass('checked');
+ }
+ }, 1);
+ }
+ }
+
+ if (it.type === 'check' || it.type === 'drop' || it.type === 'menu') {
+ it.checked = !it.checked;
+ if (it.checked) {
+ btn.addClass('checked');
+ } else {
+ btn.removeClass('checked');
+ }
+ }
+ // route processing
+ if (it.route) {
+ var route = String('/'+ it.route).replace(/\/{2,}/g, '/');
+ var info = w2utils.parseRoute(route);
+ if (info.keys.length > 0) {
+ for (var k = 0; k < info.keys.length; k++) {
+ route = route.replace((new RegExp(':'+ info.keys[k].name, 'g')), this.routeData[info.keys[k].name]);
+ }
+ }
+ setTimeout(function () { window.location.hash = route; }, 1);
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ }
+ };
+
+ $.extend(w2toolbar.prototype, w2utils.event);
+ w2obj.toolbar = w2toolbar;
+})();
+
+
+/************************************************************************
+* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
+* - Following objects defined
+* - w2sidebar - sidebar widget
+* - $().w2sidebar - jQuery wrapper
+* - Dependencies: jQuery, w2utils
+*
+* == NICE TO HAVE ==
+* - return ids of all subitems
+* - add find() method to find nodes by a specific criteria (I want all nodes for exampe)
+* - dbl click should be like it is in grid (with timer not HTML dbl click event)
+* - reorder with grag and drop
+* - add route property that would navigate to a #route
+* - node.style is missleading - should be there to apply color for example
+*
+************************************************************************/
+
+(function () {
+ var w2sidebar = function (options) {
+ this.name = null;
+ this.box = null;
+ this.sidebar = null;
+ this.parent = null;
+ this.nodes = []; // Sidebar child nodes
+ this.menu = [];
+ this.routeData = {}; // data for dynamic routes
+ this.selected = null; // current selected node (readonly)
+ this.img = null;
+ this.icon = null;
+ this.style = '';
+ this.topHTML = '';
+ this.bottomHTML = '';
+ this.keyboard = true;
+ this.onClick = null; // Fire when user click on Node Text
+ this.onDblClick = null; // Fire when user dbl clicks
+ this.onContextMenu = null;
+ this.onMenuClick = null; // when context menu item selected
+ this.onExpand = null; // Fire when node Expands
+ this.onCollapse = null; // Fire when node Colapses
+ this.onKeydown = null;
+ this.onRender = null;
+ this.onRefresh = null;
+ this.onResize = null;
+ this.onDestroy = null;
+
+ $.extend(true, this, w2obj.sidebar, options);
+ };
+
+ // ====================================================
+ // -- Registers as a jQuery plugin
+
+ $.fn.w2sidebar = function(method) {
+ if (typeof method === 'object' || !method ) {
+ // check name parameter
+ if (!w2utils.checkName(method, 'w2sidebar')) return;
+ // extend items
+ var nodes = method.nodes;
+ var object = new w2sidebar(method);
+ $.extend(object, { handlers: [], nodes: [] });
+ if (typeof nodes != 'undefined') {
+ object.add(object, nodes);
+ }
+ if ($(this).length !== 0) {
+ object.render($(this)[0]);
+ }
+ object.sidebar = object;
+ // register new object
+ w2ui[object.name] = object;
+ return object;
+
+ } else if (w2ui[$(this).attr('name')]) {
+ var obj = w2ui[$(this).attr('name')];
+ obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
+ return this;
+ } else {
+ console.log('ERROR: Method ' + method + ' does not exist on jQuery.w2sidebar' );
+ }
+ };
+
+ // ====================================================
+ // -- Implementation of core functionality
+
+ w2sidebar.prototype = {
+
+ node: {
+ id : null,
+ text : '',
+ count : null,
+ img : null,
+ icon : null,
+ nodes : [],
+ style : '', // additional style for subitems
+ route : null,
+ selected : false,
+ expanded : false,
+ hidden : false,
+ disabled : false,
+ group : false, // if true, it will build as a group
+ groupShowHide : true,
+ plus : false, // if true, plus will be shown even if there is no sub nodes
+ // events
+ onClick : null,
+ onDblClick : null,
+ onContextMenu : null,
+ onExpand : null,
+ onCollapse : null,
+ // internal
+ parent : null, // node object
+ sidebar : null
+ },
+
+ add: function (parent, nodes) {
+ if (arguments.length == 1) {
+ // need to be in reverse order
+ nodes = arguments[0];
+ parent = this;
+ }
+ if (typeof parent == 'string') parent = this.get(parent);
+ return this.insert(parent, null, nodes);
+ },
+
+ insert: function (parent, before, nodes) {
+ var txt, ind, tmp, node, nd;
+ if (arguments.length == 2) {
+ // need to be in reverse order
+ nodes = arguments[1];
+ before = arguments[0];
+ ind = this.get(before);
+ if (ind === null) {
+ if (!$.isArray(nodes)) nodes = [nodes];
+ txt = (nodes[0].caption != null ? nodes[0].caption : nodes[0].text);
+ console.log('ERROR: Cannot insert node "'+ txt +'" because cannot find node "'+ before +'" to insert before.');
+ return null;
+ }
+ parent = this.get(before).parent;
+ }
+ if (typeof parent == 'string') parent = this.get(parent);
+ if (!$.isArray(nodes)) nodes = [nodes];
+ for (var o in nodes) {
+ node = nodes[o];
+ if (typeof node.id == null) {
+ txt = (node.caption != null ? node.caption : node.text);
+ console.log('ERROR: Cannot insert node "'+ txt +'" because it has no id.');
+ continue;
+ }
+ if (this.get(this, node.id) !== null) {
+ txt = (node.caption != null ? node.caption : node.text);
+ console.log('ERROR: Cannot insert node with id='+ node.id +' (text: '+ txt + ') because another node with the same id already exists.');
+ continue;
+ }
+ tmp = $.extend({}, w2sidebar.prototype.node, node);
+ tmp.sidebar = this;
+ tmp.parent = parent;
+ nd = tmp.nodes || [];
+ tmp.nodes = []; // very important to re-init empty nodes array
+ if (before === null) { // append to the end
+ parent.nodes.push(tmp);
+ } else {
+ ind = this.get(parent, before, true);
+ if (ind === null) {
+ txt = (node.caption != null ? node.caption : node.text);
+ console.log('ERROR: Cannot insert node "'+ txt +'" because cannot find node "'+ before +'" to insert before.');
+ return null;
+ }
+ parent.nodes.splice(ind, 0, tmp);
+ }
+ if (nd.length > 0) {
+ this.insert(tmp, null, nd);
+ }
+ }
+ this.refresh(parent.id);
+ return tmp;
+ },
+
+ remove: function () { // multiple arguments
+ var deleted = 0;
+ var tmp;
+ for (var a = 0; a < arguments.length; a++) {
+ tmp = this.get(arguments[a]);
+ if (tmp === null) continue;
+ if (this.selected !== null && this.selected === tmp.id) {
+ this.selected = null;
+ }
+ var ind = this.get(tmp.parent, arguments[a], true);
+ if (ind === null) continue;
+ if (tmp.parent.nodes[ind].selected) tmp.sidebar.unselect(tmp.id);
+ tmp.parent.nodes.splice(ind, 1);
+ deleted++;
+ }
+ if (deleted > 0 && arguments.length == 1) this.refresh(tmp.parent.id); else this.refresh();
+ return deleted;
+ },
+
+ set: function (parent, id, node) {
+ if (arguments.length == 2) {
+ // need to be in reverse order
+ node = id;
+ id = parent;
+ parent = this;
+ }
+ // searches all nested nodes
+ if (typeof parent == 'string') parent = this.get(parent);
+ if (parent.nodes == null) return null;
+ for (var i = 0; i < parent.nodes.length; i++) {
+ if (parent.nodes[i].id === id) {
+ // make sure nodes inserted correctly
+ var nodes = node.nodes;
+ $.extend(parent.nodes[i], node, { nodes: [] });
+ if (nodes != null) {
+ this.add(parent.nodes[i], nodes);
+ }
+ this.refresh(id);
+ return true;
+ } else {
+ var rv = this.set(parent.nodes[i], id, node);
+ if (rv) return true;
+ }
+ }
+ return false;
+ },
+
+ get: function (parent, id, returnIndex) { // can be just called get(id) or get(id, true)
+ if (arguments.length === 0) {
+ var all = [];
+ var tmp = this.find({});
+ for (var t = 0; t < tmp.length; t++) {
+ if (tmp[t].id != null) all.push(tmp[t].id);
+ }
+ return all;
+ } else {
+ if (arguments.length == 1 || (arguments.length == 2 && id === true) ) {
+ // need to be in reverse order
+ returnIndex = id;
+ id = parent;
+ parent = this;
+ }
+ // searches all nested nodes
+ if (typeof parent == 'string') parent = this.get(parent);
+ if (parent.nodes == null) return null;
+ for (var i = 0; i < parent.nodes.length; i++) {
+ if (parent.nodes[i].id == id) {
+ if (returnIndex === true) return i; else return parent.nodes[i];
+ } else {
+ var rv = this.get(parent.nodes[i], id, returnIndex);
+ if (rv || rv === 0) return rv;
+ }
+ }
+ return null;
+ }
+ },
+
+ find: function (parent, params, results) { // can be just called find({ selected: true })
+ if (arguments.length == 1) {
+ // need to be in reverse order
+ params = parent;
+ parent = this;
+ }
+ if (!results) results = [];
+ // searches all nested nodes
+ if (typeof parent == 'string') parent = this.get(parent);
+ if (parent.nodes == null) return results;
+ for (var i = 0; i < parent.nodes.length; i++) {
+ var match = true;
+ for (var prop in params) {
+ if (parent.nodes[i][prop] != params[prop]) match = false;
+ }
+ if (match) results.push(parent.nodes[i]);
+ if (parent.nodes[i].nodes.length > 0) results = this.find(parent.nodes[i], params, results);
+ }
+ return results;
+ },
+
+ hide: function () { // multiple arguments
+ var hidden = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ var tmp = this.get(arguments[a]);
+ if (tmp === null) continue;
+ tmp.hidden = true;
+ hidden++;
+ }
+ if (arguments.length == 1) this.refresh(arguments[0]); else this.refresh();
+ return hidden;
+ },
+
+ show: function () { // multiple arguments
+ var shown = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ var tmp = this.get(arguments[a]);
+ if (tmp === null) continue;
+ tmp.hidden = false;
+ shown++;
+ }
+ if (arguments.length == 1) this.refresh(arguments[0]); else this.refresh();
+ return shown;
+ },
+
+ disable: function () { // multiple arguments
+ var disabled = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ var tmp = this.get(arguments[a]);
+ if (tmp === null) continue;
+ tmp.disabled = true;
+ if (tmp.selected) this.unselect(tmp.id);
+ disabled++;
+ }
+ if (arguments.length == 1) this.refresh(arguments[0]); else this.refresh();
+ return disabled;
+ },
+
+ enable: function () { // multiple arguments
+ var enabled = 0;
+ for (var a = 0; a < arguments.length; a++) {
+ var tmp = this.get(arguments[a]);
+ if (tmp === null) continue;
+ tmp.disabled = false;
+ enabled++;
+ }
+ if (arguments.length == 1) this.refresh(arguments[0]); else this.refresh();
+ return enabled;
+ },
+
+ select: function (id) {
+ var new_node = this.get(id);
+ if (!new_node) return false;
+ if (this.selected == id && new_node.selected) return false;
+ this.unselect(this.selected);
+ $(this.box).find('#node_'+ w2utils.escapeId(id))
+ .addClass('w2ui-selected')
+ .find('.w2ui-icon').addClass('w2ui-icon-selected');
+ new_node.selected = true;
+ this.selected = id;
+ return true;
+ },
+
+ unselect: function (id) {
+ var current = this.get(id);
+ if (!current) return false;
+ current.selected = false;
+ $(this.box).find('#node_'+ w2utils.escapeId(id))
+ .removeClass('w2ui-selected')
+ .find('.w2ui-icon').removeClass('w2ui-icon-selected');
+ if (this.selected == id) this.selected = null;
+ return true;
+ },
+
+ toggle: function(id) {
+ var nd = this.get(id);
+ if (nd === null) return false;
+ if (nd.plus) {
+ this.set(id, { plus: false });
+ this.expand(id);
+ this.refresh(id);
+ return;
+ }
+ if (nd.nodes.length === 0) return false;
+ if (this.get(id).expanded) return this.collapse(id); else return this.expand(id);
+ },
+
+ collapse: function (id) {
+ var obj = this;
+ var nd = this.get(id);
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'collapse', target: id, object: nd });
+ if (eventData.isCancelled === true) return;
+ // default action
+ $(this.box).find('#node_'+ w2utils.escapeId(id) +'_sub').slideUp(200);
+ $(this.box).find('#node_'+ w2utils.escapeId(id) +' .w2ui-node-dots:first-child').html('<div class="w2ui-expand">+</div>');
+ nd.expanded = false;
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ setTimeout(function () { obj.refresh(id); }, 200);
+ return true;
+ },
+
+ collapseAll: function (parent) {
+ if (typeof parent == 'undefined') parent = this;
+ if (typeof parent == 'string') parent = this.get(parent);
+ if (parent.nodes == null) return false;
+ for (var i = 0; i < parent.nodes.length; i++) {
+ if (parent.nodes[i].expanded === true) parent.nodes[i].expanded = false;
+ if (parent.nodes[i].nodes && parent.nodes[i].nodes.length > 0) this.collapseAll(parent.nodes[i]);
+ }
+ this.refresh(parent.id);
+ return true;
+ },
+
+ expand: function (id) {
+ var obj = this;
+ var nd = this.get(id);
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'expand', target: id, object: nd });
+ if (eventData.isCancelled === true) return;
+ // default action
+ $(this.box).find('#node_'+ w2utils.escapeId(id) +'_sub').slideDown(200);
+ $(this.box).find('#node_'+ w2utils.escapeId(id) +' .w2ui-node-dots:first-child').html('<div class="w2ui-expand">-</div>');
+ nd.expanded = true;
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ setTimeout(function () { obj.refresh(id); }, 200);
+ return true;
+ },
+
+ expandAll: function (parent) {
+ if (typeof parent == 'undefined') parent = this;
+ if (typeof parent == 'string') parent = this.get(parent);
+ if (parent.nodes == null) return false;
+ for (var i = 0; i < parent.nodes.length; i++) {
+ if (parent.nodes[i].expanded === false) parent.nodes[i].expanded = true;
+ if (parent.nodes[i].nodes && parent.nodes[i].nodes.length > 0) this.collapseAll(parent.nodes[i]);
+ }
+ this.refresh(parent.id);
+ },
+
+ expandParents: function (id) {
+ var node = this.get(id);
+ if (node === null) return false;
+ if (node.parent) {
+ node.parent.expanded = true;
+ this.expandParents(node.parent.id);
+ }
+ this.refresh(id);
+ return true;
+ },
+
+ click: function (id, event) {
+ var obj = this;
+ var nd = this.get(id);
+ if (nd === null) return;
+ if (nd.disabled || nd.group) return; // should click event if already selected
+ // unselect all previsously
+ $(obj.box).find('.w2ui-node.w2ui-selected').each(function (index, el) {
+ var oldID = $(el).attr('id').replace('node_', '');
+ var oldNode = obj.get(oldID);
+ if (oldNode != null) oldNode.selected = false;
+ $(el).removeClass('w2ui-selected').find('.w2ui-icon').removeClass('w2ui-icon-selected');
+ });
+ // select new one
+ var newNode = $(obj.box).find('#node_'+ w2utils.escapeId(id));
+ var oldNode = $(obj.box).find('#node_'+ w2utils.escapeId(obj.selected));
+ newNode.addClass('w2ui-selected').find('.w2ui-icon').addClass('w2ui-icon-selected');
+ // need timeout to allow rendering
+ setTimeout(function () {
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'click', target: id, originalEvent: event, node: nd, object: nd });
+ if (eventData.isCancelled === true) {
+ // restore selection
+ newNode.removeClass('w2ui-selected').find('.w2ui-icon').removeClass('w2ui-icon-selected');
+ oldNode.addClass('w2ui-selected').find('.w2ui-icon').addClass('w2ui-icon-selected');
+ return;
+ }
+ // default action
+ if (oldNode !== null) oldNode.selected = false;
+ obj.get(id).selected = true;
+ obj.selected = id;
+ // route processing
+ if (nd.route) {
+ var route = String('/'+ nd.route).replace(/\/{2,}/g, '/');
+ var info = w2utils.parseRoute(route);
+ if (info.keys.length > 0) {
+ for (var k = 0; k < info.keys.length; k++) {
+ if (obj.routeData[info.keys[k].name] == null) continue;
+ route = route.replace((new RegExp(':'+ info.keys[k].name, 'g')), obj.routeData[info.keys[k].name]);
+ }
+ }
+ setTimeout(function () { window.location.hash = route; }, 1);
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }, 1);
+ },
+
+ keydown: function (event) {
+ var obj = this;
+ var nd = obj.get(obj.selected);
+ if (!nd || obj.keyboard !== true) return;
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'keydown', target: obj.name, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default behaviour
+ if (event.keyCode == 13 || event.keyCode == 32) { // enter or space
+ if (nd.nodes.length > 0) obj.toggle(obj.selected);
+ }
+ if (event.keyCode == 37) { // left
+ if (nd.nodes.length > 0 && nd.expanded) {
+ obj.collapse(obj.selected);
+ } else {
+ selectNode(nd.parent);
+ if (!nd.parent.group) obj.collapse(nd.parent.id);
+ }
+ }
+ if (event.keyCode == 39) { // right
+ if ((nd.nodes.length > 0 || nd.plus) && !nd.expanded) obj.expand(obj.selected);
+ }
+ if (event.keyCode == 38) { // up
+ selectNode(neighbor(nd, prev));
+ }
+ if (event.keyCode == 40) { // down
+ selectNode(neighbor(nd, next));
+ }
+ // cancel event if needed
+ if ($.inArray(event.keyCode, [13, 32, 37, 38, 39, 40]) != -1) {
+ if (event.preventDefault) event.preventDefault();
+ if (event.stopPropagation) event.stopPropagation();
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+
+ function selectNode (node, event) {
+ if (node !== null && !node.hidden && !node.disabled && !node.group) {
+ obj.click(node.id, event);
+ setTimeout(function () { obj.scrollIntoView(); }, 50);
+ }
+ }
+
+ function neighbor (node, neighborFunc) {
+ node = neighborFunc(node);
+ while (node !== null && (node.hidden || node.disabled)) {
+ if (node.group) break; else node = neighborFunc(node);
+ }
+ return node;
+ }
+
+ function next (node, noSubs) {
+ if (node === null) return null;
+ var parent = node.parent;
+ var ind = obj.get(node.id, true);
+ var nextNode = null;
+ // jump inside
+ if (node.expanded && node.nodes.length > 0 && noSubs !== true) {
+ var t = node.nodes[0];
+ if (t.hidden || t.disabled || t.group) nextNode = next(t); else nextNode = t;
+ } else {
+ if (parent && ind + 1 < parent.nodes.length) {
+ nextNode = parent.nodes[ind + 1];
+ } else {
+ nextNode = next(parent, true); // jump to the parent
+ }
+ }
+ if (nextNode !== null && (nextNode.hidden || nextNode.disabled || nextNode.group)) nextNode = next(nextNode);
+ return nextNode;
+ }
+
+ function prev (node) {
+ if (node === null) return null;
+ var parent = node.parent;
+ var ind = obj.get(node.id, true);
+ var prevNode = (ind > 0) ? lastChild(parent.nodes[ind - 1]) : parent;
+ if (prevNode !== null && (prevNode.hidden || prevNode.disabled || prevNode.group)) prevNode = prev(prevNode);
+ return prevNode;
+ }
+
+ function lastChild (node) {
+ if (node.expanded && node.nodes.length > 0) {
+ var t = node.nodes[node.nodes.length - 1];
+ if (t.hidden || t.disabled || t.group) return prev(t); else return lastChild(t);
+ }
+ return node;
+ }
+ },
+
+ scrollIntoView: function (id) {
+ if (typeof id == 'undefined') id = this.selected;
+ var nd = this.get(id);
+ if (nd === null) return;
+ var body = $(this.box).find('.w2ui-sidebar-div');
+ var item = $(this.box).find('#node_'+ w2utils.escapeId(id));
+ var offset = item.offset().top - body.offset().top;
+ if (offset + item.height() > body.height()) {
+ body.animate({ 'scrollTop': body.scrollTop() + body.height() / 1.3 }, 250, 'linear');
+ }
+ if (offset <= 0) {
+ body.animate({ 'scrollTop': body.scrollTop() - body.height() / 1.3 }, 250, 'linear');
+ }
+ },
+
+ dblClick: function (id, event) {
+ // if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ var nd = this.get(id);
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'dblClick', target: id, originalEvent: event, object: nd });
+ if (eventData.isCancelled === true) return;
+ // default action
+ this.toggle(id);
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ contextMenu: function (id, event) {
+ var obj = this;
+ var nd = obj.get(id);
+ if (id != obj.selected) obj.click(id);
+ // need timeout to allow click to finish first
+ setTimeout(function () {
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'contextMenu', target: id, originalEvent: event, object: nd });
+ if (eventData.isCancelled === true) return;
+ // default action
+ if (nd.group || nd.disabled) return;
+ if (obj.menu.length > 0) {
+ $(obj.box).find('#node_'+ w2utils.escapeId(id))
+ .w2menu(obj.menu, {
+ left : (event ? event.offsetX || event.pageX : 50) - 25,
+ onSelect: function (event) {
+ obj.menuClick(id, parseInt(event.index), event.originalEvent);
+ }
+ }
+ );
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }, 150); // need timer 150 for FF
+ },
+
+ menuClick: function (itemId, index, event) {
+ var obj = this;
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'menuClick', target: itemId, originalEvent: event, menuIndex: index, menuItem: obj.menu[index] });
+ if (eventData.isCancelled === true) return;
+ // default action
+ // -- empty
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ render: function (box) {
+ var time = (new Date()).getTime();
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'render', target: this.name, box: box });
+ if (eventData.isCancelled === true) return;
+ // default action
+ if (typeof box != 'undefined' && box !== null) {
+ if ($(this.box).find('> div > div.w2ui-sidebar-div').length > 0) {
+ $(this.box)
+ .removeAttr('name')
+ .removeClass('w2ui-reset w2ui-sidebar')
+ .html('');
+ }
+ this.box = box;
+ }
+ if (!this.box) return;
+ $(this.box)
+ .attr('name', this.name)
+ .addClass('w2ui-reset w2ui-sidebar')
+ .html('<div>'+
+ '<div class="w2ui-sidebar-top"></div>' +
+ '<div class="w2ui-sidebar-div"></div>'+
+ '<div class="w2ui-sidebar-bottom"></div>'+
+ '</div>'
+ );
+ $(this.box).find('> div').css({
+ width : $(this.box).width() + 'px',
+ height: $(this.box).height() + 'px'
+ });
+ if ($(this.box).length > 0) $(this.box)[0].style.cssText += this.style;
+ // adjust top and bottom
+ if (this.topHTML !== '') {
+ $(this.box).find('.w2ui-sidebar-top').html(this.topHTML);
+ $(this.box).find('.w2ui-sidebar-div')
+ .css('top', $(this.box).find('.w2ui-sidebar-top').height() + 'px');
+ }
+ if (this.bottomHTML !== '') {
+ $(this.box).find('.w2ui-sidebar-bottom').html(this.bottomHTML);
+ $(this.box).find('.w2ui-sidebar-div')
+ .css('bottom', $(this.box).find('.w2ui-sidebar-bottom').height() + 'px');
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ // ---
+ this.refresh();
+ return (new Date()).getTime() - time;
+ },
+
+ refresh: function (id) {
+ var time = (new Date()).getTime();
+ // if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'refresh', target: (typeof id != 'undefined' ? id : this.name) });
+ if (eventData.isCancelled === true) return;
+ // adjust top and bottom
+ if (this.topHTML !== '') {
+ $(this.box).find('.w2ui-sidebar-top').html(this.topHTML);
+ $(this.box).find('.w2ui-sidebar-div')
+ .css('top', $(this.box).find('.w2ui-sidebar-top').height() + 'px');
+ }
+ if (this.bottomHTML !== '') {
+ $(this.box).find('.w2ui-sidebar-bottom').html(this.bottomHTML);
+ $(this.box).find('.w2ui-sidebar-div')
+ .css('bottom', $(this.box).find('.w2ui-sidebar-bottom').height() + 'px');
+ }
+ // default action
+ $(this.box).find('> div').css({
+ width : $(this.box).width() + 'px',
+ height: $(this.box).height() + 'px'
+ });
+ var obj = this;
+ var node, nd;
+ var nm;
+ if (typeof id == 'undefined') {
+ node = this;
+ nm = '.w2ui-sidebar-div';
+ } else {
+ node = this.get(id);
+ if (node === null) return;
+ nm = '#node_'+ w2utils.escapeId(node.id) + '_sub';
+ }
+ var nodeHTML;
+ if (node !== this) {
+ var tmp = '#node_'+ w2utils.escapeId(node.id);
+ nodeHTML = getNodeHTML(node);
+ $(this.box).find(tmp).before('<div id="sidebar_'+ this.name + '_tmp"></div>');
+ $(this.box).find(tmp).remove();
+ $(this.box).find(nm).remove();
+ $('#sidebar_'+ this.name + '_tmp').before(nodeHTML);
+ $('#sidebar_'+ this.name + '_tmp').remove();
+ }
+ // refresh sub nodes
+ $(this.box).find(nm).html('');
+ for (var i = 0; i < node.nodes.length; i++) {
+ nd = node.nodes[i];
+ nodeHTML = getNodeHTML(nd);
+ $(this.box).find(nm).append(nodeHTML);
+ if (nd.nodes.length !== 0) { this.refresh(nd.id); }
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return (new Date()).getTime() - time;
+
+ function getNodeHTML(nd) {
+ var html = '';
+ var img = nd.img;
+ if (img === null) img = this.img;
+ var icon = nd.icon;
+ if (icon === null) icon = this.icon;
+ // -- find out level
+ var tmp = nd.parent;
+ var level = 0;
+ while (tmp && tmp.parent !== null) {
+ if (tmp.group) level--;
+ tmp = tmp.parent;
+ level++;
+ }
+ if (typeof nd.caption != 'undefined') nd.text = nd.caption;
+ if (nd.group) {
+ html =
+ '<div class="w2ui-node-group" id="node_'+ nd.id +'"'+
+ ' onclick="w2ui[\''+ obj.name +'\'].toggle(\''+ nd.id +'\')"'+
+ ' onmouseout="$(this).find(\'span:nth-child(1)\').css(\'color\', \'transparent\')" '+
+ ' onmouseover="$(this).find(\'span:nth-child(1)\').css(\'color\', \'inherit\')">'+
+ (nd.groupShowHide ? '<span>'+ (!nd.hidden && nd.expanded ? w2utils.lang('Hide') : w2utils.lang('Show')) +'</span>' : '<span></span>') +
+ ' <span>'+ nd.text +'</span>'+
+ '</div>'+
+ '<div class="w2ui-node-sub" id="node_'+ nd.id +'_sub" style="'+ nd.style +';'+ (!nd.hidden && nd.expanded ? '' : 'display: none;') +'"></div>';
+ } else {
+ if (nd.selected && !nd.disabled) obj.selected = nd.id;
+ tmp = '';
+ if (img) tmp = '<div class="w2ui-node-image w2ui-icon '+ img + (nd.selected && !nd.disabled ? " w2ui-icon-selected" : "") +'"></div>';
+ if (icon) tmp = '<div class="w2ui-node-image"><span class="'+ icon +'"></span></div>';
+ html =
+ '<div class="w2ui-node '+ (nd.selected ? 'w2ui-selected' : '') +' '+ (nd.disabled ? 'w2ui-disabled' : '') +'" id="node_'+ nd.id +'" style="'+ (nd.hidden ? 'display: none;' : '') +'"'+
+ ' ondblclick="w2ui[\''+ obj.name +'\'].dblClick(\''+ nd.id +'\', event);"'+
+ ' oncontextmenu="w2ui[\''+ obj.name +'\'].contextMenu(\''+ nd.id +'\', event); '+
+ ' if (event.preventDefault) event.preventDefault();"'+
+ ' onClick="w2ui[\''+ obj.name +'\'].click(\''+ nd.id +'\', event); ">'+
+ '<table cellpadding="0" cellspacing="0" style="margin-left:'+ (level*18) +'px; padding-right:'+ (level*18) +'px"><tr>'+
+ '<td class="w2ui-node-dots" nowrap onclick="w2ui[\''+ obj.name +'\'].toggle(\''+ nd.id +'\'); '+
+ ' if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;">'+
+ ' <div class="w2ui-expand">' + (nd.nodes.length > 0 ? (nd.expanded ? '-' : '+') : (nd.plus ? '+' : '')) + '</div>' +
+ '</td>'+
+ '<td class="w2ui-node-data" nowrap>'+
+ tmp +
+ (nd.count || nd.count === 0 ? '<div class="w2ui-node-count">'+ nd.count +'</div>' : '') +
+ '<div class="w2ui-node-caption">'+ nd.text +'</div>'+
+ '</td>'+
+ '</tr></table>'+
+ '</div>'+
+ '<div class="w2ui-node-sub" id="node_'+ nd.id +'_sub" style="'+ nd.style +';'+ (!nd.hidden && nd.expanded ? '' : 'display: none;') +'"></div>';
+ }
+ return html;
+ }
+ },
+
+ resize: function () {
+ var time = (new Date()).getTime();
+ // if (window.getSelection) window.getSelection().removeAllRanges(); // clear selection
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'resize', target: this.name });
+ if (eventData.isCancelled === true) return;
+ // default action
+ $(this.box).css('overflow', 'hidden'); // container should have no overflow
+ //$(this.box).find('.w2ui-sidebar-div').css('overflow', 'hidden');
+ $(this.box).find('> div').css({
+ width : $(this.box).width() + 'px',
+ height : $(this.box).height() + 'px'
+ });
+ //$(this.box).find('.w2ui-sidebar-div').css('overflow', 'auto');
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return (new Date()).getTime() - time;
+ },
+
+ destroy: function () {
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'destroy', target: this.name });
+ if (eventData.isCancelled === true) return;
+ // clean up
+ if ($(this.box).find('> div > div.w2ui-sidebar-div').length > 0) {
+ $(this.box)
+ .removeAttr('name')
+ .removeClass('w2ui-reset w2ui-sidebar')
+ .html('');
+ }
+ delete w2ui[this.name];
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ lock: function (msg, showSpinner) {
+ var box = $(this.box).find('> div:first-child');
+ var args = Array.prototype.slice.call(arguments, 0);
+ args.unshift(box);
+ w2utils.lock.apply(window, args);
+ },
+
+ unlock: function () {
+ w2utils.unlock(this.box);
+ }
+ };
+
+ $.extend(w2sidebar.prototype, w2utils.event);
+ w2obj.sidebar = w2sidebar;
+})();
+
+/************************************************************************
+* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
+* - Following objects defined
+* - w2field - various field controls
+* - $().w2field - jQuery wrapper
+* - Dependencies: jQuery, w2utils
+*
+* == NICE TO HAVE ==
+* - upload (regular files)
+* - BUG with prefix/postfix and arrows (test in different contexts)
+* - prefix and suffix are slow (100ms or so)
+* - multiple date selection
+* - month selection, year selections
+* - arrows no longer work (for int)
+* - form to support custom types
+* - bug: if input is hidden and then enum is applied, then when it becomes visible, it will be 110px
+*
+************************************************************************/
+
+(function ($) {
+
+ var w2field = function (options) {
+ // public properties
+ this.el = null
+ this.helpers = {}; // object or helper elements
+ this.type = options.type || 'text';
+ this.options = $.extend(true, {}, options);
+ this.onSearch = options.onSearch || null;
+ this.onRequest = options.onRequest || null;
+ this.onLoad = options.onLoad || null;
+ this.onError = options.onError || null;
+ this.onClick = options.onClick || null;
+ this.onAdd = options.onAdd || null;
+ this.onNew = options.onNew || null;
+ this.onRemove = options.onRemove || null;
+ this.onMouseOver = options.onMouseOver || null;
+ this.onMouseOut = options.onMouseOut || null;
+ this.onIconClick = options.onIconClick || null;
+ this.tmp = {}; // temp object
+ // clean up some options
+ delete this.options.type;
+ delete this.options.onSearch;
+ delete this.options.onRequest;
+ delete this.options.onLoad;
+ delete this.options.onError;
+ delete this.options.onClick;
+ delete this.options.onMouseOver;
+ delete this.options.onMouseOut;
+ delete this.options.onIconClick;
+ // extend with defaults
+ $.extend(true, this, w2obj.field);
+ };
+
+ // ====================================================
+ // -- Registers as a jQuery plugin
+
+ $.fn.w2field = function (method, options) {
+ // call direct
+ if (this.length == 0) {
+ var pr = w2field.prototype;
+ if (pr[method]) {
+ return pr[method].apply(pr, Array.prototype.slice.call(arguments, 1));
+ }
+ } else {
+ if (typeof method == 'string' && typeof options == 'object') {
+ method = $.extend(true, {}, options, { type: method });
+ }
+ if (typeof method == 'string' && typeof options == 'undefined') {
+ method = { type: method };
+ }
+ method.type = String(method.type).toLowerCase();
+ return this.each(function (index, el) {
+ var obj = $(el).data('w2field');
+ // if object is not defined, define it
+ if (typeof obj == 'undefined') {
+ var obj = new w2field(method);
+ $.extend(obj, { handlers: [] });
+ if (el) obj.el = $(el)[0];
+ obj.init();
+ $(el).data('w2field', obj);
+ return obj;
+ } else { // fully re-init
+ obj.clear();
+ if (method.type == 'clear') return;
+ var obj = new w2field(method);
+ $.extend(obj, { handlers: [] });
+ if (el) obj.el = $(el)[0];
+ obj.init();
+ $(el).data('w2field', obj);
+ return obj;
+ }
+ return null;
+ });
+ }
+ }
+
+ // ====================================================
+ // -- Implementation of core functionality
+
+ /* To add custom types
+ $().w2field('addType', 'myType', function (options) {
+ $(this.el).on('keypress', function (event) {
+ if (event.metaKey || event.ctrlKey || event.altKey
+ || (event.charCode != event.keyCode && event.keyCode > 0)) return;
+ var ch = String.fromCharCode(event.charCode);
+ if (ch != 'a' && ch != 'b' && ch != 'c') {
+ if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
+ return false;
+ }
+ });
+ $(this.el).on('blur', function (event) { // keyCode & charCode differ in FireFox
+ var ch = this.value;
+ if (ch != 'a' && ch != 'b' && ch != 'c') {
+ $(this).w2tag(w2utils.lang("Not a single charecter from the set of 'abc'"));
+ }
+ });
+ });
+ */
+
+ w2field.prototype = {
+
+ custom: {}, // map of custom types
+
+ pallete: [
+ ['000000', '444444', '666666', '999999', 'CCCCCC', 'EEEEEE', 'F3F3F3', 'FFFFFF'],
+ ['FF011B', 'FF9838', 'FFFD59', '01FD55', '00FFFE', '0424F3', '9B24F4', 'FF21F5'],
+ ['F4CCCC', 'FCE5CD', 'FFF2CC', 'D9EAD3', 'D0E0E3', 'CFE2F3', 'D9D1E9', 'EAD1DC'],
+ ['EA9899', 'F9CB9C', 'FEE599', 'B6D7A8', 'A2C4C9', '9FC5E8', 'B4A7D6', 'D5A6BD'],
+ ['E06666', 'F6B26B', 'FED966', '93C47D', '76A5AF', '6FA8DC', '8E7CC3', 'C27BA0'],
+ ['CC0814', 'E69138', 'F1C232', '6AA84F', '45818E', '3D85C6', '674EA7', 'A54D79'],
+ ['99050C', 'B45F17', 'BF901F', '37761D', '124F5C', '0A5394', '351C75', '741B47'],
+ ['660205', '783F0B', '7F6011', '274E12', '0C343D', '063762', '20124D', '4C1030']
+ ],
+
+ addType: function (type, handler) {
+ type = String(type).toLowerCase();
+ this.custom[type] = handler;
+ return true;
+ },
+
+ removeType: function (type) {
+ type = String(type).toLowerCase();
+ if (!this.custom[type]) return false;
+ delete this.custom[type];
+ return true
+ },
+
+ init: function () {
+ var obj = this;
+ var options = this.options;
+ var defaults;
+
+ // Custom Types
+ if (typeof this.custom[this.type] == 'function') {
+ this.custom[this.type].call(this, options);
+ return;
+ }
+ // only for INPUT or TEXTAREA
+ if (['INPUT', 'TEXTAREA'].indexOf(this.el.tagName) == -1) {
+ console.log('ERROR: w2field could only be applied to INPUT or TEXTAREA.', this.el);
+ return;
+ }
+
+ switch (this.type) {
+ case 'text':
+ case 'int':
+ case 'float':
+ case 'money':
+ case 'currency':
+ case 'percent':
+ case 'alphanumeric':
+ case 'hex':
+ defaults = {
+ min : null,
+ max : null,
+ step : 1,
+ placeholder : '',
+ autoFormat : true,
+ currencyPrefix : w2utils.settings.currencyPrefix,
+ currencySuffix : w2utils.settings.currencySuffix,
+ currencyPrecision : w2utils.settings.currencyPrecision,
+ groupSymbol : w2utils.settings.groupSymbol,
+ arrows : false,
+ keyboard : true,
+ precision : null,
+ silent : true,
+ prefix : '',
+ suffix : ''
+ };
+ this.options = $.extend(true, {}, defaults, options);
+ options = this.options; // since object is re-created, need to re-assign
+ options.numberRE = new RegExp('['+ options.groupSymbol + ']', 'g');
+ options.moneyRE = new RegExp('['+ options.currencyPrefix + options.currencySuffix + options.groupSymbol + ']', 'g');
+ options.percentRE = new RegExp('['+ options.groupSymbol + '%]', 'g');
+ // no keyboard support needed
+ if (['text', 'alphanumeric', 'hex'].indexOf(this.type) != -1) {
+ options.arrows = false;
+ options.keyboard = false;
+ }
+ this.addPrefix(); // only will add if needed
+ this.addSuffix();
+ $(this.el).attr('placeholder', options.placeholder);
+ break;
+
+ case 'color':
+ defaults = {
+ prefix : '#',
+ suffix : '<div style="width: '+ (parseInt($(this.el).css('font-size')) || 12) +'px">&nbsp;</div>',
+ placeholder : '',
+ arrows : false,
+ keyboard : false
+ };
+ $.extend(options, defaults);
+ this.addPrefix(); // only will add if needed
+ this.addSuffix(); // only will add if needed
+ // additional checks
+ $(this.el).attr('maxlength', 6);
+ if ($(this.el).val() != '') setTimeout(function () { $(obj.el).change(); }, 1);
+ $(this.el).attr('placeholder', options.placeholder);
+ break;
+
+ case 'date':
+ defaults = {
+ format : w2utils.settings.date_format, // date format
+ placeholder : '',
+ keyboard : true,
+ silent : true,
+ start : '', // string or jquery object
+ end : '', // string or jquery object
+ blocked : {}, // { '4/11/2011': 'yes' }
+ colored : {} // { '4/11/2011': 'red:white' }
+ };
+ this.options = $.extend(true, {}, defaults, options);
+ options = this.options; // since object is re-created, need to re-assign
+ $(this.el).attr('placeholder', options.placeholder ? options.placeholder : options.format);
+ break;
+
+ case 'time':
+ defaults = {
+ format : w2utils.settings.time_format,
+ placeholder : '',
+ keyboard : true,
+ silent : true,
+ start : '',
+ end : ''
+ };
+ this.options = $.extend(true, {}, defaults, options);
+ options = this.options; // since object is re-created, need to re-assign
+ $(this.el).attr('placeholder', options.placeholder ? options.placeholder : (options.format == 'h12' ? 'hh:mi pm' : 'hh:mi'));
+ break;
+
+ case 'datetime':
+ break;
+
+ case 'list':
+ case 'combo':
+ defaults = {
+ items : [],
+ selected : {},
+ placeholder : '',
+ url : null, // url to pull data from
+ postData : {},
+ minLength : 1,
+ cacheMax : 250,
+ maxDropHeight : 350, // max height for drop down menu
+ match : 'begins', // ['contains', 'is', 'begins', 'ends']
+ silent : true,
+ icon : null,
+ iconStyle : '',
+ onSearch : null, // when search needs to be performed
+ onRequest : null, // when request is submitted
+ onLoad : null, // when data is received
+ onError : null, // when data fails to load due to server error or other failure modes
+ onIconClick : null,
+ renderDrop : null, // render function for drop down item
+ prefix : '',
+ suffix : '',
+ openOnFocus : false, // if to show overlay onclick or when typing
+ markSearch : false
+ };
+ options.items = this.normMenu(options.items); // need to be first
+ if (this.type == 'list') {
+ // defaults.search = (options.items && options.items.length >= 10 ? true : false);
+ defaults.openOnFocus = true;
+ defaults.suffix = '<div class="arrow-down" style="margin-top: '+ ((parseInt($(this.el).height()) - 6) / 2) +'px;"></div>';
+ $(this.el).addClass('w2ui-select');
+ // if simple value - look it up
+ if (!$.isPlainObject(options.selected)) {
+ for (var i in options.items) {
+ var item = options.items[i];
+ if (item && item.id == options.selected) {
+ options.selected = $.extend(true, {}, item);
+ break;
+ }
+ }
+ }
+ }
+ options = $.extend({}, defaults, options, {
+ align : 'both', // same width as control
+ altRows : true // alternate row color
+ });
+ this.options = options;
+ if (!$.isPlainObject(options.selected)) options.selected = {};
+ $(this.el).data('selected', options.selected);
+ if (options.url) this.request(0);
+ if (this.type == 'list') this.addFocus();
+ this.addPrefix();
+ this.addSuffix();
+ setTimeout(function () { obj.refresh(); }, 10); // need this for icon refresh
+ $(this.el).attr('placeholder', options.placeholder).attr('autocomplete', 'off');
+ if (typeof options.selected.text != 'undefined') $(this.el).val(options.selected.text);
+ break;
+
+ case 'enum':
+ defaults = {
+ items : [],
+ selected : [],
+ placeholder : '',
+ max : 0, // max number of selected items, 0 - unlim
+ url : null, // not implemented
+ postData : {},
+ minLength : 1,
+ cacheMax : 250,
+ maxWidth : 250, // max width for a single item
+ maxHeight : 350, // max height for input control to grow
+ maxDropHeight : 350, // max height for drop down menu
+ match : 'contains', // ['contains', 'is', 'begins', 'ends']
+ silent : true,
+ openOnFocus : false, // if to show overlay onclick or when typing
+ markSearch : true,
+ renderDrop : null, // render function for drop down item
+ renderItem : null, // render selected item
+ style : '', // style for container div
+ onSearch : null, // when search needs to be performed
+ onRequest : null, // when request is submitted
+ onLoad : null, // when data is received
+ onError : null, // when data fails to load due to server error or other failure modes
+ onClick : null, // when an item is clicked
+ onAdd : null, // when an item is added
+ onNew : null, // when new item should be added
+ onRemove : null, // when an item is removed
+ onMouseOver : null, // when an item is mouse over
+ onMouseOut : null // when an item is mouse out
+ };
+ options = $.extend({}, defaults, options, {
+ align : 'both', // same width as control
+ suffix : '',
+ altRows : true // alternate row color
+ });
+ options.items = this.normMenu(options.items);
+ options.selected = this.normMenu(options.selected);
+ this.options = options;
+ if (!$.isArray(options.selected)) options.selected = [];
+ $(this.el).data('selected', options.selected);
+ if (options.url) this.request(0);
+ this.addSuffix();
+ this.addMulti();
+ break;
+
+ case 'file':
+ defaults = {
+ selected : [],
+ placeholder : w2utils.lang('Attach files by dragging and dropping or Click to Select'),
+ max : 0,
+ maxSize : 0, // max size of all files, 0 - unlim
+ maxFileSize : 0, // max size of a single file, 0 -unlim
+ maxWidth : 250, // max width for a single item
+ maxHeight : 350, // max height for input control to grow
+ maxDropHeight : 350, // max height for drop down menu
+ silent : true,
+ renderItem : null, // render selected item
+ style : '', // style for container div
+ onClick : null, // when an item is clicked
+ onAdd : null, // when an item is added
+ onRemove : null, // when an item is removed
+ onMouseOver : null, // when an item is mouse over
+ onMouseOut : null // when an item is mouse out
+ };
+ options = $.extend({}, defaults, options, {
+ align : 'both', // same width as control
+ altRows : true // alternate row color
+ });
+ this.options = options;
+ if (!$.isArray(options.selected)) options.selected = [];
+ $(this.el).data('selected', options.selected);
+ this.addMulti();
+ break;
+ }
+ // attach events
+ this.tmp = {
+ onChange : function (event) { obj.change.call(obj, event) },
+ onClick : function (event) { obj.click.call(obj, event) },
+ onFocus : function (event) { obj.focus.call(obj, event) },
+ onBlur : function (event) { obj.blur.call(obj, event) },
+ onKeydown : function (event) { obj.keyDown.call(obj, event) },
+ onKeyup : function (event) { obj.keyUp.call(obj, event) },
+ onKeypress : function (event) { obj.keyPress.call(obj, event) }
+ }
+ $(this.el)
+ .addClass('w2field')
+ .data('w2field', this)
+ .on('change', this.tmp.onChange)
+ .on('click', this.tmp.onClick) // ignore click because it messes overlays
+ .on('focus', this.tmp.onFocus)
+ .on('blur', this.tmp.onBlur)
+ .on('keydown', this.tmp.onKeydown)
+ .on('keyup', this.tmp.onKeyup)
+ .on('keypress', this.tmp.onKeypress)
+ .css({
+ 'box-sizing' : 'border-box',
+ '-webkit-box-sizing' : 'border-box',
+ '-moz-box-sizing' : 'border-box',
+ '-ms-box-sizing' : 'border-box',
+ '-o-box-sizing' : 'border-box'
+ });
+ // format initial value
+ this.change($.Event('change'));
+ },
+
+ clear: function () {
+ var obj = this;
+ var options = this.options;
+ // if money then clear value
+ if (['money', 'currency'].indexOf(this.type) != -1) {
+ $(this.el).val($(this.el).val().replace(options.moneyRE, ''));
+ }
+ if (this.type == 'percent') {
+ $(this.el).val($(this.el).val().replace(/%/g, ''));
+ }
+ if (this.type == 'color') {
+ $(this.el).removeAttr('maxlength');
+ }
+ if (this.type == 'list') {
+ $(this.el).removeClass('w2ui-select');
+ }
+ if (['date', 'time'].indexOf(this.type) != -1) {
+ if ($(this.el).attr('placeholder') == options.format) $(this.el).attr('placeholder', '');
+ }
+ this.type = 'clear';
+ var tmp = $(this.el).data('tmp');
+ if (!this.tmp) return;
+ // restore paddings
+ if (typeof tmp != 'undefined') {
+ if (tmp && tmp['old-padding-left']) $(this.el).css('padding-left', tmp['old-padding-left']);
+ if (tmp && tmp['old-padding-right']) $(this.el).css('padding-right', tmp['old-padding-right']);
+ }
+ // remove events and data
+ $(this.el)
+ .val(this.clean($(this.el).val()))
+ .removeClass('w2field')
+ .removeData() // removes all attached data
+ .off('change', this.tmp.onChange)
+ .off('click', this.tmp.onClick)
+ .off('focus', this.tmp.onFocus)
+ .off('blur', this.tmp.onBlur)
+ .off('keydown', this.tmp.onKeydown)
+ .off('keyup', this.tmp.onKeyup)
+ .off('keypress', this.tmp.onKeypress);
+ // remove helpers
+ for (var h in this.helpers) $(this.helpers[h]).remove();
+ this.helpers = {};
+ },
+
+ refresh: function () {
+ var obj = this;
+ var options = this.options;
+ var selected = $(this.el).data('selected');
+ var time = (new Date()).getTime();
+ // enum
+ if (['list'].indexOf(this.type) != -1) {
+ $(obj.el).parent().css('white-space', 'nowrap'); // needs this for arrow alway to appear on the right side
+ // hide focus and show text
+ if (obj.helpers.prefix) obj.helpers.prefix.hide();
+ setTimeout(function () {
+ if (!obj.helpers.focus) return;
+ // if empty show no icon
+ if (!$.isEmptyObject(selected) && options.icon) {
+ options.prefix = '<span class="w2ui-icon '+ options.icon +'"style="cursor: pointer; font-size: 14px;' +
+ ' display: inline-block; margin-top: -1px; color: #7F98AD;'+ options.iconStyle +'">'+
+ '</span>';
+ obj.addPrefix();
+ } else {
+ options.prefix = '';
+ obj.addPrefix();
+ }
+ // focus helpder
+ var focus = obj.helpers.focus.find('input');
+ if ($(focus).val() == '') {
+ $(focus).css('opacity', 0).prev().css('opacity', 0);
+ $(obj.el).val(selected && selected.text != null ? selected.text : '');
+ $(obj.el).attr('placeholder', $(obj.el).attr('_placeholder'));
+ } else {
+ $(focus).css('opacity', 1).prev().css('opacity', 1);
+ $(obj.el).val('');
+ $(obj.el).attr('_placeholder', $(obj.el).attr('placeholder')).removeAttr('placeholder');
+ setTimeout(function () {
+ if (obj.helpers.prefix) obj.helpers.prefix.hide();
+ var tmp = 'position: absolute; opacity: 0; margin: 4px 0px 0px 2px; background-position: left !important;';
+ if (options.icon) {
+ $(focus).css('margin-left', '17px');
+ $(obj.helpers.focus).find('.icon-search').attr('style', tmp + 'width: 11px !important; opacity: 1');
+ } else {
+ $(focus).css('margin-left', '0px');
+ $(obj.helpers.focus).find('.icon-search').attr('style', tmp + 'width: 0px !important; opacity: 0');
+ }
+ }, 1);
+ }
+ }, 1);
+ }
+ if (['enum', 'file'].indexOf(this.type) != -1) {
+ var html = '';
+ for (var s in selected) {
+ var it = selected[s];
+ var ren = '';
+ if (typeof options.renderItem == 'function') {
+ ren = options.renderItem(it, s, '<div class="w2ui-list-remove" title="'+ w2utils.lang('Remove') +'" index="'+ s +'">&nbsp;&nbsp;</div>');
+ } else {
+ ren = '<div class="w2ui-list-remove" title="'+ w2utils.lang('Remove') +'" index="'+ s +'">&nbsp;&nbsp;</div>'+
+ (obj.type == 'enum' ? it.text : it.name + '<span class="file-size"> - '+ w2utils.size(it.size) +'</span>');
+ }
+ html += '<li index="'+ s +'" style="max-width: '+ parseInt(options.maxWidth) + 'px; '+ (it.style ? it.style : '') +'">'+
+ ren +'</li>';
+ }
+ var div = obj.helpers.multi;
+ var ul = div.find('ul');
+ div.attr('style', div.attr('style') + ';' + options.style);
+ if ($(obj.el).attr('readonly')) div.addClass('w2ui-readonly'); else div.removeClass('w2ui-readonly');
+ // celan
+ div.find('.w2ui-enum-placeholder').remove();
+ ul.find('li').not('li.nomouse').remove();
+ // add new list
+ if (html != '') {
+ ul.prepend(html);
+ } else if (typeof options.placeholder != 'undefined') {
+ var style =
+ 'padding-top: ' + $(this.el).css('padding-top') + ';'+
+ 'padding-left: ' + $(this.el).css('padding-left') + '; ' +
+ 'box-sizing: ' + $(this.el).css('box-sizing') + '; ' +
+ 'line-height: ' + $(this.el).css('line-height') + '; ' +
+ 'font-size: ' + $(this.el).css('font-size') + '; ' +
+ 'font-family: ' + $(this.el).css('font-family') + '; ';
+ div.prepend('<div class="w2ui-enum-placeholder" style="'+ style +'">'+ options.placeholder + '</div>');
+ }
+ // ITEMS events
+ div.find('li')
+ .data('mouse', 'out')
+ .on('click', function (event) {
+ var item = selected[$(event.target).attr('index')];
+ if ($(event.target).hasClass('nomouse')) return;
+ event.stopPropagation();
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'click', target: obj.el, originalEvent: event.originalEvent, item: item });
+ if (eventData.isCancelled === true) return;
+ // default behavior
+ if ($(event.target).hasClass('w2ui-list-remove')) {
+ if ($(obj.el).attr('readonly')) return;
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'remove', target: obj.el, originalEvent: event.originalEvent, item: item });
+ if (eventData.isCancelled === true) return;
+ // default behavior
+ $().w2overlay();
+ selected.splice($(event.target).attr('index'), 1);
+ $(obj.el).trigger('change');
+ $(event.target).parent().fadeOut('fast');
+ setTimeout(function () {
+ obj.refresh();
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }, 300);
+ }
+ if (obj.type == 'file' && !$(event.target).hasClass('w2ui-list-remove')) {
+ var preview = '';
+ if ((/image/i).test(item.type)) { // image
+ preview = '<div style="padding: 3px;">'+
+ ' <img src="'+ (item.content ? 'data:'+ item.type +';base64,'+ item.content : '') +'" style="max-width: 300px;" '+
+ ' onload="var w = $(this).width(); var h = $(this).height(); '+
+ ' if (w < 300 & h < 300) return; '+
+ ' if (w >= h && w > 300) $(this).width(300);'+
+ ' if (w < h && h > 300) $(this).height(300);"'+
+ ' onerror="this.style.display = \'none\'"'+
+ ' >'+
+ '</div>';
+ }
+ var td1 = 'style="padding: 3px; text-align: right; color: #777;"';
+ var td2 = 'style="padding: 3px"';
+ preview += '<div style="padding: 8px;">'+
+ ' <table cellpadding="2">'+
+ ' <tr><td '+ td1 +'>Name:</td><td '+ td2 +'>'+ item.name +'</td></tr>'+
+ ' <tr><td '+ td1 +'>Size:</td><td '+ td2 +'>'+ w2utils.size(item.size) +'</td></tr>'+
+ ' <tr><td '+ td1 +'>Type:</td><td '+ td2 +'>' +
+ ' <span style="width: 200px; display: block-inline; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">'+ item.type +'</span>'+
+ ' </td></tr>'+
+ ' <tr><td '+ td1 +'>Modified:</td><td '+ td2 +'>'+ w2utils.date(item.modified) +'</td></tr>'+
+ ' </table>'+
+ '</div>';
+ $(event.target).w2overlay(preview);
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ })
+ .on('mouseover', function (event) {
+ var tmp = event.target;
+ if (tmp.tagName != 'LI') tmp = tmp.parentNode;
+ if ($(tmp).hasClass('nomouse')) return;
+ if ($(tmp).data('mouse') == 'out') {
+ var item = selected[$(tmp).attr('index')];
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'mouseOver', target: obj.el, originalEvent: event.originalEvent, item: item });
+ if (eventData.isCancelled === true) return;
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ $(tmp).data('mouse', 'over');
+ })
+ .on('mouseout', function (event) {
+ var tmp = event.target;
+ if (tmp.tagName != 'LI') tmp = tmp.parentNode;
+ if ($(tmp).hasClass('nomouse')) return;
+ $(tmp).data('mouse', 'leaving');
+ setTimeout(function () {
+ if ($(tmp).data('mouse') == 'leaving') {
+ $(tmp).data('mouse', 'out');
+ var item = selected[$(tmp).attr('index')];
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'f', target: obj.el, originalEvent: event.originalEvent, item: item });
+ if (eventData.isCancelled === true) return;
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ }, 0);
+ });
+ // adjust height
+ $(this.el).height('auto');
+ var cntHeight = $(div).find('> div').height() + w2utils.getSize(div, '+height') * 2;
+ if (cntHeight < 26) cntHeight = 26;
+ if (cntHeight > options.maxHeight) cntHeight = options.maxHeight;
+ if (div.length > 0) div[0].scrollTop = 1000;
+ var inpHeight = w2utils.getSize($(this.el), 'height') - 2;
+ if (inpHeight > cntHeight) cntHeight = inpHeight
+ $(div).css({ 'height': cntHeight + 'px', overflow: (cntHeight == options.maxHeight ? 'auto' : 'hidden') });
+ if (cntHeight < options.maxHeight) $(div).prop('scrollTop', 0);
+ $(this.el).css({ 'height' : (cntHeight + 2) + 'px' });
+ }
+ return (new Date()).getTime() - time;
+ },
+
+ reset: function () {
+ var obj = this;
+ var type = this.type;
+ this.clear();
+ this.type = type;
+ this.init();
+ },
+
+ clean: function (val) {
+ var options = this.options;
+ val = String(val).trim();
+ // clean
+ if (['int', 'float', 'money', 'currency', 'percent'].indexOf(this.type) != -1) {
+ if (options.autoFormat && ['money', 'currency'].indexOf(this.type) != -1) val = String(val).replace(options.moneyRE, '');
+ if (options.autoFormat && this.type == 'percent') val = String(val).replace(options.percentRE, '');
+ if (options.autoFormat && ['int', 'float'].indexOf(this.type) != -1) val = String(val).replace(options.numberRE, '');
+ if (parseFloat(val) == val) {
+ if (options.min !== null && val < options.min) { val = options.min; $(this.el).val(options.min); }
+ if (options.max !== null && val > options.max) { val = options.max; $(this.el).val(options.max); }
+ }
+ if (val !== '' && w2utils.isFloat(val)) val = Number(val); else val = '';
+ }
+ return val;
+ },
+
+ format: function (val) {
+ var options = this.options;
+ // autoformat numbers or money
+ if (options.autoFormat && val != '') {
+ switch (this.type) {
+ case 'money':
+ case 'currency':
+ val = w2utils.formatNumber(Number(val).toFixed(options.currencyPrecision), options.groupSymbol);
+ if (val != '') val = options.currencyPrefix + val + options.currencySuffix;
+ break;
+ case 'percent':
+ val = w2utils.formatNumber(options.precision ? Number(val).toFixed(options.precision) : val, options.groupSymbol);
+ if (val != '') val += '%';
+ break;
+ case 'float':
+ val = w2utils.formatNumber(options.precision ? Number(val).toFixed(options.precision) : val, options.groupSymbol);
+ break;
+ case 'int':
+ val = w2utils.formatNumber(val, options.groupSymbol);
+ break;
+ }
+ }
+ return val;
+ },
+
+ change: function (event) {
+ var obj = this;
+ var options = obj.options;
+ // numeric
+ if (['int', 'float', 'money', 'currency', 'percent'].indexOf(this.type) != -1) {
+ // check max/min
+ var val = $(this.el).val();
+ var new_val = this.format(this.clean($(this.el).val()));
+ // if was modified
+ if (val != '' && val != new_val) {
+ $(this.el).val(new_val).change();
+ // cancel event
+ event.stopPropagation();
+ event.preventDefault();
+ return false;
+ }
+ }
+ // color
+ if (this.type == 'color') {
+ var color = '#' + $(this.el).val();
+ if ($(this.el).val().length != 6 && $(this.el).val().length != 3) color = '';
+ $(this.el).next().find('div').css('background-color', color);
+ if ($(obj.el).is(':focus')) this.updateOverlay();
+ }
+ },
+
+ click: function (event) {
+ event.stopPropagation();
+ // lists
+ if (['list', 'combo', 'enum'].indexOf(this.type) != -1) {
+ if (!$(this.el).is(':focus')) this.focus(event);
+ }
+ // other fields with drops
+ if (['date', 'time', 'color'].indexOf(this.type) != -1) {
+ this.updateOverlay();
+ }
+ },
+
+ focus: function (event) {
+ var obj = this;
+ var options = this.options;
+ // color, date, time
+ if (['color', 'date', 'time'].indexOf(obj.type) !== -1) {
+ if ($(obj.el).attr('readonly')) return;
+ if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
+ setTimeout(function () { obj.updateOverlay(); }, 150);
+ }
+ // menu
+ if (['list', 'combo', 'enum'].indexOf(obj.type) != -1) {
+ if ($(obj.el).attr('readonly')) return;
+ if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
+ setTimeout(function () {
+ if (obj.type == 'list' && $(obj.el).is(':focus')) {
+ $(obj.helpers.focus).find('input').focus();
+ return;
+ }
+ obj.search();
+ setTimeout(function () { obj.updateOverlay(); }, 1);
+ }, 1);
+ }
+ // file
+ if (obj.type == 'file') {
+ $(obj.helpers.multi).css({ 'outline': 'auto 5px #7DB4F3', 'outline-offset': '-2px' });
+ }
+ },
+
+ blur: function (event) {
+ var obj = this;
+ var options = obj.options;
+ var val = $(obj.el).val().trim();
+ // hide overlay
+ if (['color', 'date', 'time', 'list', 'combo', 'enum'].indexOf(obj.type) != -1) {
+ if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
+ }
+ if (['int', 'float', 'money', 'currency', 'percent'].indexOf(obj.type) != -1) {
+ if (val !== '' && !obj.checkType(val)) {
+ $(obj.el).val('').change();
+ if (options.silent === false) {
+ $(obj.el).w2tag('Not a valid number');
+ setTimeout(function () { $(obj.el).w2tag(''); }, 3000);
+ }
+ }
+ }
+ // date or time
+ if (['date', 'time'].indexOf(obj.type) != -1) {
+ if (w2utils.isInt(obj.el.value)) {
+ $(obj.el).val(w2utils.formatDate(new Date(parseInt(obj.el.value)), options.format)).change();
+ }
+ // check if in range
+ if (val !== '' && !obj.inRange(obj.el.value)) {
+ $(obj.el).val('').removeData('selected').change();
+ if (options.silent === false) {
+ $(obj.el).w2tag('Not in range');
+ setTimeout(function () { $(obj.el).w2tag(''); }, 3000);
+ }
+ } else {
+ if (obj.type == 'date' && val !== '' && !w2utils.isDate(obj.el.value, options.format)) {
+ $(obj.el).val('').removeData('selected').change();
+ if (options.silent === false) {
+ $(obj.el).w2tag('Not a valid date');
+ setTimeout(function () { $(obj.el).w2tag(''); }, 3000);
+ }
+ }
+ if (obj.type == 'time' && val !== '' && !w2utils.isTime(obj.el.value)) {
+ $(obj.el).val('').removeData('selected').change();
+ if (options.silent === false) {
+ $(obj.el).w2tag('Not a valid time');
+ setTimeout(function () { $(obj.el).w2tag(''); }, 3000);
+ }
+ }
+ }
+ }
+ // clear search input
+ if (obj.type == 'enum') {
+ $(obj.helpers.multi).find('input').val('').width(20);
+ }
+ // file
+ if (obj.type == 'file') {
+ $(obj.helpers.multi).css({ 'outline': 'none' });
+ }
+ },
+
+ keyPress: function (event) {
+ var obj = this;
+ var options = obj.options;
+ // ignore wrong pressed key
+ if (['int', 'float', 'money', 'currency', 'percent', 'hex', 'color', 'alphanumeric'].indexOf(obj.type) != -1) {
+ // keyCode & charCode differ in FireFox
+ if (event.metaKey || event.ctrlKey || event.altKey || (event.charCode != event.keyCode && event.keyCode > 0)) return;
+ var ch = String.fromCharCode(event.charCode);
+ if (!obj.checkType(ch, true) && event.keyCode != 13) {
+ event.preventDefault();
+ if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
+ return false;
+ }
+ }
+ // update date popup
+ if (['date', 'time'].indexOf(obj.type) != -1) {
+ setTimeout(function () { obj.updateOverlay(); }, 1);
+ }
+ },
+
+ keyDown: function (event, extra) {
+ var obj = this;
+ var options = obj.options;
+ var key = event.keyCode || (extra && extra.keyCode);
+ // numeric
+ if (['int', 'float', 'money', 'currency', 'percent'].indexOf(obj.type) != -1) {
+ if (!options.keyboard || $(obj.el).attr('readonly')) return;
+ var cancel = false;
+ var val = parseFloat($(obj.el).val().replace(options.moneyRE, '')) || 0;
+ var inc = options.step;
+ if (event.ctrlKey || event.metaKey) inc = 10;
+ switch (key) {
+ case 38: // up
+ if (event.shiftKey) break; // no action if shift key is pressed
+ $(obj.el).val((val + inc <= options.max || options.max === null ? Number((val + inc).toFixed(12)) : options.max)).change();
+ cancel = true;
+ break;
+ case 40: // down
+ if (event.shiftKey) break; // no action if shift key is pressed
+ $(obj.el).val((val - inc >= options.min || options.min === null ? Number((val - inc).toFixed(12)) : options.min)).change();
+ cancel = true;
+ break;
+ }
+ if (cancel) {
+ event.preventDefault();
+ setTimeout(function () {
+ // set cursor to the end
+ obj.el.setSelectionRange(obj.el.value.length, obj.el.value.length);
+ }, 0);
+ }
+ }
+ // date
+ if (obj.type == 'date') {
+ if (!options.keyboard || $(obj.el).attr('readonly')) return;
+ var cancel = false;
+ var daymil = 24*60*60*1000;
+ var inc = 1;
+ if (event.ctrlKey || event.metaKey) inc = 10;
+ var dt = w2utils.isDate($(obj.el).val(), options.format, true);
+ if (!dt) { dt = new Date(); daymil = 0; }
+ switch (key) {
+ case 38: // up
+ if (event.shiftKey) break; // no action if shift key is pressed
+ var newDT = w2utils.formatDate(dt.getTime() + daymil, options.format);
+ if (inc == 10) newDT = w2utils.formatDate(new Date(dt.getFullYear(), dt.getMonth()+1, dt.getDate()), options.format);
+ $(obj.el).val(newDT).change();
+ cancel = true;
+ break;
+ case 40: // down
+ if (event.shiftKey) break; // no action if shift key is pressed
+ var newDT = w2utils.formatDate(dt.getTime() - daymil, options.format);
+ if (inc == 10) newDT = w2utils.formatDate(new Date(dt.getFullYear(), dt.getMonth()-1, dt.getDate()), options.format);
+ $(obj.el).val(newDT).change();
+ cancel = true;
+ break;
+ }
+ if (cancel) {
+ event.preventDefault();
+ setTimeout(function () {
+ // set cursor to the end
+ obj.el.setSelectionRange(obj.el.value.length, obj.el.value.length);
+ obj.updateOverlay();
+ }, 0);
+ }
+ }
+ // time
+ if (obj.type == 'time') {
+ if (!options.keyboard || $(obj.el).attr('readonly')) return;
+ var cancel = false;
+ var inc = 1;
+ if (event.ctrlKey || event.metaKey) inc = 60;
+ if (w2utils.isInt(obj.el.value)) {
+ $(obj.el).val(w2utils.formatTime(new Date(parseInt(obj.el.value)), options.format)).change();
+ }
+ var val = $(obj.el).val();
+ var time = obj.toMin(val) || obj.toMin((new Date()).getHours() + ':' + ((new Date()).getMinutes() - 1));
+ switch (key) {
+ case 38: // up
+ if (event.shiftKey) break; // no action if shift key is pressed
+ time += inc;
+ cancel = true;
+ break;
+ case 40: // down
+ if (event.shiftKey) break; // no action if shift key is pressed
+ time -= inc;
+ cancel = true;
+ break;
+ }
+ if (cancel) {
+ $(obj.el).val(obj.fromMin(time)).change();
+ event.preventDefault();
+ setTimeout(function () {
+ // set cursor to the end
+ obj.el.setSelectionRange(obj.el.value.length, obj.el.value.length);
+ }, 0);
+ }
+ }
+ // color
+ if (obj.type == 'color') {
+ if ($(obj.el).attr('readonly')) return;
+ // paste
+ if (event.keyCode == 86 && (event.ctrlKey || event.metaKey)) {
+ $(obj.el).prop('maxlength', 7);
+ setTimeout(function () {
+ var val = $(obj).val();
+ if (val.substr(0, 1) == '#') val = val.substr(1);
+ if (!w2utils.isHex(val)) val = '';
+ $(obj).val(val).prop('maxlength', 6).change();
+ }, 20);
+ }
+ if ((event.ctrlKey || event.metaKey) && !event.shiftKey) {
+ if (typeof obj.tmp.cind1 == 'undefined') {
+ obj.tmp.cind1 = -1;
+ obj.tmp.cind2 = -1;
+ } else {
+ switch (key) {
+ case 38: // up
+ obj.tmp.cind1--;
+ break;
+ case 40: // down
+ obj.tmp.cind1++;
+ break;
+ case 39: // right
+ obj.tmp.cind2++;
+ break;
+ case 37: // left
+ obj.tmp.cind2--;
+ break;
+ }
+ if (obj.tmp.cind1 < 0) obj.tmp.cind1 = 0;
+ if (obj.tmp.cind1 > this.pallete.length - 1) obj.tmp.cind1 = this.pallete.length - 1;
+ if (obj.tmp.cind2 < 0) obj.tmp.cind2 = 0;
+ if (obj.tmp.cind2 > this.pallete[0].length - 1) obj.tmp.cind2 = this.pallete[0].length - 1;
+ }
+ if ([37, 38, 39, 40].indexOf(key) != -1) {
+ $(obj.el).val(this.pallete[obj.tmp.cind1][obj.tmp.cind2]).change();
+ event.preventDefault();
+ }
+ }
+ }
+ // list/select/combo
+ if (['list', 'combo', 'enum'].indexOf(obj.type) != -1) {
+ if ($(obj.el).attr('readonly')) return;
+ var cancel = false;
+ var selected = $(obj.el).data('selected');
+ var focus = $(obj.helpers.focus).find('input');
+ if (obj.type == 'list') {
+ if ([37, 38, 39, 40].indexOf(key) == -1) obj.refresh(); // arrows
+ }
+ // apply arrows
+ switch (key) {
+ case 27: // escape
+ if (obj.type == 'list') {
+ if ($(focus).val() != '') $(focus).val('');
+ event.stopPropagation(); // escape in field should not close popup
+ }
+ break;
+ case 37: // left
+ case 39: // right
+ // cancel = true;
+ break;
+ case 13: // enter
+ if ($('#w2ui-overlay').length == 0) break; // no action if overlay not open
+ var item = options.items[options.index];
+ var multi = $(obj.helpers.multi).find('input');
+ if (obj.type == 'enum') {
+ if (item != null) {
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'add', target: obj.el, originalEvent: event.originalEvent, item: item });
+ if (eventData.isCancelled === true) return;
+ item = eventData.item; // need to reassign because it could be recreated by user
+ // default behavior
+ if (selected.length >= options.max && options.max > 0) selected.pop();
+ delete item.hidden;
+ delete obj.tmp.force_open;
+ selected.push(item);
+ $(obj.el).change();
+ multi.val('').width(20);
+ obj.refresh();
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ } else {
+ // trigger event
+ item = { id: multi.val(), text: multi.val() }
+ var eventData = obj.trigger({ phase: 'before', type: 'new', target: obj.el, originalEvent: event.originalEvent, item: item });
+ if (eventData.isCancelled === true) return;
+ item = eventData.item; // need to reassign because it could be recreated by user
+ // default behavior
+ if (typeof obj.onNew == 'function') {
+ if (selected.length >= options.max && options.max > 0) selected.pop();
+ delete obj.tmp.force_open;
+ selected.push(item);
+ $(obj.el).change();
+ multi.val('').width(20);
+ obj.refresh();
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ } else {
+ if (item) $(obj.el).data('selected', item).val(item.text).change();
+ if ($(obj.el).val() == '' && $(obj.el).data('selected')) $(obj.el).removeData('selected').val('').change();
+ if (obj.type == 'list') {
+ focus.val('');
+ obj.refresh();
+ }
+ // hide overlay
+ obj.tmp.force_hide = true;
+ }
+ break;
+ case 8: // backspace
+ case 46: // delete
+ if (obj.type == 'enum' && key == 8) {
+ if ($(obj.helpers.multi).find('input').val() == '' && selected.length > 0) {
+ var item = selected[selected.length - 1];
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'remove', target: obj.el, originalEvent: event.originalEvent, item: item });
+ if (eventData.isCancelled === true) return;
+ // default behavior
+ selected.pop();
+ $(obj.el).trigger('change');
+ obj.refresh();
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ }
+ if (obj.type == 'list' && $(focus).val() == '') {
+ $(obj.el).data('selected', {}).change();
+ obj.refresh();
+ }
+ break;
+ case 38: // up
+ options.index = w2utils.isInt(options.index) ? parseInt(options.index) : 0;
+ options.index--;
+ while (options.index > 0 && options.items[options.index].hidden) options.index--;
+ if (options.index == 0 && options.items[options.index].hidden) {
+ while (options.items[options.index] && options.items[options.index].hidden) options.index++;
+ }
+ cancel = true;
+ break;
+ case 40: // down
+ options.index = w2utils.isInt(options.index) ? parseInt(options.index) : -1;
+ options.index++;
+ while (options.index < options.items.length-1 && options.items[options.index].hidden) options.index++;
+ if (options.index == options.items.length-1 && options.items[options.index].hidden) {
+ while (options.items[options.index] && options.items[options.index].hidden) options.index--;
+ }
+ // show overlay if not shown
+ var input = obj.el;
+ if (['enum'].indexOf(obj.type) != -1) input = obj.helpers.multi.find('input');
+ if ($(input).val() == '' && $('#w2ui-overlay').length == 0) {
+ obj.tmp.force_open = true;
+ } else {
+ cancel = true;
+ }
+ break;
+ }
+ if (cancel) {
+ if (options.index < 0) options.index = 0;
+ if (options.index >= options.items.length) options.index = options.items.length -1;
+ obj.updateOverlay();
+ // cancel event
+ event.preventDefault();
+ setTimeout(function () {
+ // set cursor to the end
+ if (obj.type == 'enum') {
+ var tmp = obj.helpers.multi.find('input').get(0);
+ tmp.setSelectionRange(tmp.value.length, tmp.value.length);
+ } else if (obj.type == 'list') {
+ var tmp = obj.helpers.focus.find('input').get(0);
+ tmp.setSelectionRange(tmp.value.length, tmp.value.length);
+ } else {
+ obj.el.setSelectionRange(obj.el.value.length, obj.el.value.length);
+ }
+ }, 0);
+ return;
+ }
+ // expand input
+ if (obj.type == 'enum') {
+ var input = obj.helpers.multi.find('input');
+ var search = input.val();
+ input.width(((search.length + 2) * 8) + 'px');
+ }
+ // run search
+ if ([16, 17, 18, 20, 37, 39, 91].indexOf(key) == -1) { // no refreah on crtl, shift, left/right arrows, etc
+ setTimeout(function () {
+ if (!obj.tmp.force_hide) obj.request();
+ obj.search();
+ }, 1);
+ }
+ }
+ },
+
+ keyUp: function (event) {
+ if (this.type == 'color') {
+ if (event.keyCode == 86 && (event.ctrlKey || event.metaKey)) $(this).prop('maxlength', 6);
+ }
+ },
+
+ clearCache: function () {
+ var options = this.options;
+ options.items = [];
+ this.tmp.xhr_loading = false;
+ this.tmp.xhr_search = '';
+ this.tmp.xhr_total = -1;
+ this.search();
+ },
+
+ request: function (interval) {
+ var obj = this;
+ var options = this.options;
+ var search = $(obj.el).val() || '';
+ // if no url - do nothing
+ if (!options.url) return;
+ // --
+ if (obj.type == 'enum') {
+ var tmp = $(obj.helpers.multi).find('input');
+ if (tmp.length == 0) search = ''; else search = tmp.val();
+ }
+ if (obj.type == 'list') {
+ var tmp = $(obj.helpers.focus).find('input');
+ if (tmp.length == 0) search = ''; else search = tmp.val();
+ }
+ if (options.minLength != 0 && search.length < options.minLength) {
+ options.items = []; // need to empty the list
+ this.updateOverlay();
+ return;
+ }
+ if (typeof interval == 'undefined') interval = 350;
+ if (typeof obj.tmp.xhr_search == 'undefined') obj.tmp.xhr_search = '';
+ if (typeof obj.tmp.xhr_total == 'undefined') obj.tmp.xhr_total = -1;
+ // check if need to search
+ if (options.url && $(obj.el).prop('readonly') != true && (
+ (options.items.length === 0 && obj.tmp.xhr_total !== 0) ||
+ (obj.tmp.xhr_total == options.cacheMax && search.length > obj.tmp.xhr_search.length) ||
+ (search.length >= obj.tmp.xhr_search.length && search.substr(0, obj.tmp.xhr_search.length) != obj.tmp.xhr_search) ||
+ (search.length < obj.tmp.xhr_search.length)
+ )) {
+ // empty list
+ obj.tmp.xhr_loading = true;
+ obj.search();
+ // timeout
+ clearTimeout(obj.tmp.timeout);
+ obj.tmp.timeout = setTimeout(function () {
+ // trigger event
+ var url = options.url;
+ var postData = {
+ search : search,
+ max : options.cacheMax
+ };
+ $.extend(postData, options.postData);
+ var eventData = obj.trigger({ phase: 'before', type: 'request', target: obj.el, url: url, postData: postData });
+ if (eventData.isCancelled === true) return;
+ url = eventData.url;
+ postData = eventData.postData;
+ // console.log('REMOTE SEARCH:', search);
+ if (obj.tmp.xhr) obj.tmp.xhr.abort();
+ var ajaxOptions = {
+ type : 'GET',
+ url : url,
+ data : postData,
+ dataType : 'JSON' // expected from server
+ };
+ if (w2utils.settings.dataType == 'JSON') {
+ ajaxOptions.type = 'POST';
+ ajaxOptions.data = JSON.stringify(ajaxOptions.data);
+ ajaxOptions.contentType = 'application/json';
+ }
+ obj.tmp.xhr = $.ajax(ajaxOptions)
+ .done(function (data, status, xhr) {
+ // trigger event
+ var eventData2 = obj.trigger({ phase: 'before', type: 'load', target: obj.el, search: postData.search, data: data, xhr: xhr });
+ if (eventData2.isCancelled === true) return;
+ // default behavior
+ data = eventData2.data;
+ if (typeof data == 'string') data = JSON.parse(data);
+ if (data.status != 'success') {
+ console.log('ERROR: server did not return proper structure. It should return', { status: 'success', items: [{ id: 1, text: 'item' }] });
+ return;
+ }
+ // remove all extra items if more then needed for cache
+ if (data.items.length > options.cacheMax) data.items.splice(options.cacheMax, 100000);
+ // remember stats
+ obj.tmp.xhr_loading = false;
+ obj.tmp.xhr_search = search;
+ obj.tmp.xhr_total = data.items.length;
+ options.items = data.items;
+ if (search == '' && data.items.length == 0) obj.tmp.emptySet = true; else obj.tmp.emptySet = false;
+ obj.search();
+ // console.log('-->', 'retrieved:', obj.tmp.xhr_total);
+ // event after
+ obj.trigger($.extend(eventData2, { phase: 'after' }));
+ })
+ .fail(function (xhr, status, error) {
+ // trigger event
+ var errorObj = { status: status, error: error, rawResponseText: xhr.responseText };
+ var eventData2 = obj.trigger({ phase: 'before', type: 'error', target: obj.el, search: search, error: errorObj, xhr: xhr });
+ if (eventData2.isCancelled === true) return;
+ // default behavior
+ if (status != 'abort') {
+ var data;
+ try { data = $.parseJSON(xhr.responseText) } catch (e) {}
+ console.log('ERROR: Server communication failed.',
+ '\n EXPECTED:', { status: 'success', items: [{ id: 1, text: 'item' }] },
+ '\n OR:', { status: 'error', message: 'error message' },
+ '\n RECEIVED:', typeof data == 'object' ? data : xhr.responseText);
+ }
+ // reset stats
+ obj.clearCache();
+ // event after
+ obj.trigger($.extend(eventData2, { phase: 'after' }));
+ });
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }, interval);
+ }
+ },
+
+ search: function () {
+ var obj = this;
+ var options = this.options;
+ var search = $(obj.el).val();
+ var target = obj.el;
+ var ids = [];
+ var selected= $(obj.el).data('selected');
+ if (obj.type == 'enum') {
+ target = $(obj.helpers.multi).find('input');
+ search = target.val();
+ for (var s in selected) { if (selected[s]) ids.push(selected[s].id); }
+ }
+ if (obj.type == 'list') {
+ target = $(obj.helpers.focus).find('input');
+ search = target.val();
+ for (var s in selected) { if (selected[s]) ids.push(selected[s].id); }
+ }
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'search', target: target, search: search });
+ if (eventData.isCancelled === true) return;
+ if (obj.tmp.xhr_loading !== true) {
+ var shown = 0;
+ for (var i in options.items) {
+ var item = options.items[i];
+ var prefix = '';
+ var suffix = '';
+ if (['is', 'begins'].indexOf(options.match) != -1) prefix = '^';
+ if (['is', 'ends'].indexOf(options.match) != -1) suffix = '$';
+ try {
+ var re = new RegExp(prefix + search + suffix, 'i');
+ if (re.test(item.text) || item.text == '...') item.hidden = false; else item.hidden = true;
+ } catch (e) {}
+ // do not show selected items
+ if (obj.type == 'enum' && $.inArray(item.id, ids) != -1) item.hidden = true;
+ if (item.hidden !== true) shown++;
+ }
+ if (obj.type != 'combo') { // don't preselect first for combo
+ options.index = 0;
+ while (options.items[options.index] && options.items[options.index].hidden) options.index++;
+ } else {
+ options.index = -1;
+ }
+ if (shown <= 0) options.index = -1;
+ options.spinner = false;
+ obj.updateOverlay();
+ setTimeout(function () {
+ var html = $('#w2ui-overlay').html() || '';
+ if (options.markSearch && html.indexOf('$.fn.w2menuHandler') != -1) { // do not highlight when no items
+ $('#w2ui-overlay').w2marker(search);
+ }
+ }, 1);
+ } else {
+ options.items.splice(0, options.cacheMax);
+ options.spinner = true;
+ obj.updateOverlay();
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ updateOverlay: function () {
+ var obj = this;
+ var options = this.options;
+ // color
+ if (this.type == 'color') {
+ if ($(obj.el).attr('readonly')) return;
+ if ($('#w2ui-overlay').length == 0) {
+ $(obj.el).w2overlay(obj.getColorHTML());
+ } else {
+ $('#w2ui-overlay').html(obj.getColorHTML());
+ }
+ // bind events
+ $('#w2ui-overlay .color')
+ .on('mousedown', function (event) {
+ var color = $(event.originalEvent.target).attr('name');
+ var index = $(event.originalEvent.target).attr('index').split(':');
+ obj.tmp.cind1 = index[0];
+ obj.tmp.cind2 = index[1];
+ $(obj.el).val(color).change();
+ $(this).html('&#149;');
+ })
+ .on('mouseup', function () {
+ setTimeout(function () {
+ if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay').removeData('keepOpen')[0].hide();
+ }, 10);
+ });
+ }
+ // date
+ if (this.type == 'date') {
+ if ($(obj.el).attr('readonly')) return;
+ if ($('#w2ui-overlay').length == 0) {
+ $(obj.el).w2overlay('<div class="w2ui-reset w2ui-calendar" onclick="event.stopPropagation();"></div>', {
+ css: { "background-color": "#f5f5f5" }
+ });
+ }
+ var month, year;
+ var dt = w2utils.isDate($(obj.el).val(), obj.options.format, true);
+ if (dt) { month = dt.getMonth() + 1; year = dt.getFullYear(); }
+ (function refreshCalendar(month, year) {
+ $('#w2ui-overlay > div > div').html(obj.getMonthHTML(month, year));
+ $('#w2ui-overlay .w2ui-calendar-title')
+ .on('mousedown', function () {
+ if ($(this).next().hasClass('w2ui-calendar-jump')) {
+ $(this).next().remove();
+ } else {
+ var selYear, selMonth;
+ $(this).after('<div class="w2ui-calendar-jump" style=""></div>');
+ $(this).next().hide().html(obj.getYearHTML()).fadeIn(200);
+ setTimeout(function () {
+ $('#w2ui-overlay .w2ui-calendar-jump')
+ .find('.w2ui-jump-month, .w2ui-jump-year')
+ .on('click', function () {
+ if ($(this).hasClass('w2ui-jump-month')) {
+ $(this).parent().find('.w2ui-jump-month').removeClass('selected');
+ $(this).addClass('selected');
+ selMonth = $(this).attr('name');
+ }
+ if ($(this).hasClass('w2ui-jump-year')) {
+ $(this).parent().find('.w2ui-jump-year').removeClass('selected');
+ $(this).addClass('selected');
+ selYear = $(this).attr('name');
+ }
+ if (selYear != null && selMonth != null) {
+ $('#w2ui-overlay .w2ui-calendar-jump').fadeOut(100);
+ setTimeout(function () { refreshCalendar(parseInt(selMonth)+1, selYear); }, 100);
+ }
+ });
+ $('#w2ui-overlay .w2ui-calendar-jump >:last-child').prop('scrollTop', 2000);
+ }, 1);
+ }
+ });
+ $('#w2ui-overlay .w2ui-date')
+ .on('mousedown', function () {
+ var day = $(this).attr('date');
+ $(obj.el).val(day).change();
+ $(this).css({ 'background-color': '#B6D5FB', 'border-color': '#aaa' });
+ })
+ .on('mouseup', function () {
+ setTimeout(function () {
+ if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay').removeData('keepOpen')[0].hide();
+ }, 10);
+ });
+ $('#w2ui-overlay .previous').on('mousedown', function () {
+ var tmp = obj.options.current.split('/');
+ tmp[0] = parseInt(tmp[0]) - 1;
+ refreshCalendar(tmp[0], tmp[1]);
+ });
+ $('#w2ui-overlay .next').on('mousedown', function () {
+ var tmp = obj.options.current.split('/');
+ tmp[0] = parseInt(tmp[0]) + 1;
+ refreshCalendar(tmp[0], tmp[1]);
+ });
+ }) (month, year);
+ }
+ // date
+ if (this.type == 'time') {
+ if ($(obj.el).attr('readonly')) return;
+ if ($('#w2ui-overlay').length == 0) {
+ $(obj.el).w2overlay('<div class="w2ui-reset w2ui-calendar-time" onclick="event.stopPropagation();"></div>', {
+ css: { "background-color": "#fff" }
+ });
+ }
+ var h24 = (this.options.format == 'h24' ? true : false);
+ $('#w2ui-overlay > div').html(obj.getHourHTML());
+ $('#w2ui-overlay .w2ui-time')
+ .on('mousedown', function (event) {
+ $(this).css({ 'background-color': '#B6D5FB', 'border-color': '#aaa' });
+ var hour = $(this).attr('hour');
+ $(obj.el).val((hour > 12 && !h24 ? hour - 12 : hour) + ':00' + (!h24 ? (hour < 12 ? ' am' : ' pm') : '')).change();
+ })
+ .on('mouseup', function () {
+ var hour = $(this).attr('hour');
+ if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
+ $(obj.el).w2overlay('<div class="w2ui-reset w2ui-calendar-time"></div>', { css: { "background-color": "#fff" } });
+ $('#w2ui-overlay > div').html(obj.getMinHTML(hour));
+ $('#w2ui-overlay .w2ui-time')
+ .on('mousedown', function () {
+ $(this).css({ 'background-color': '#B6D5FB', 'border-color': '#aaa' });
+ var min = $(this).attr('min');
+ $(obj.el).val((hour > 12 && !h24 ? hour - 12 : hour) + ':' + (min < 10 ? 0 : '') + min + (!h24 ? (hour < 12 ? ' am' : ' pm') : '')).change();
+ })
+ .on('mouseup', function () {
+ setTimeout(function () { if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay').removeData('keepOpen')[0].hide(); }, 10);
+ });
+ });
+ }
+ // list
+ if (['list', 'combo', 'enum'].indexOf(this.type) != -1) {
+ var el = this.el;
+ var input = this.el;
+ if (this.type == 'enum') {
+ el = $(this.helpers.multi);
+ input = $(el).find('input');
+ }
+ if (this.type == 'list') {
+ input = $(this.helpers.focus).find('input');
+ }
+ if ($(input).is(':focus')) {
+ if (options.openOnFocus === false && $(input).val() == '' && obj.tmp.force_open !== true) {
+ $().w2overlay();
+ return;
+ }
+ if (obj.tmp.force_hide) {
+ $().w2overlay();
+ setTimeout(function () {
+ delete obj.tmp.force_hide;
+ }, 1);
+ return;
+ }
+ if ($(input).val() != '') delete obj.tmp.force_open;
+ if ($('#w2ui-overlay').length == 0) options.index = 0;
+ var msgNoItems = w2utils.lang('No matches');
+ if (options.url != null && $(input).val().length < options.minLength && obj.tmp.emptySet !== true) msgNoItems = options.minLength + ' ' + w2utils.lang('letters or more...');
+ if (options.url != null && $(input).val() == '' && obj.tmp.emptySet !== true) msgNoItems = w2utils.lang('Type to search....');
+ $(el).w2menu('refresh', $.extend(true, {}, options, {
+ search : false,
+ render : options.renderDrop,
+ maxHeight : options.maxDropHeight,
+ msgNoItems : msgNoItems,
+ // selected with mouse
+ onSelect: function (event) {
+ if (obj.type == 'enum') {
+ var selected = $(obj.el).data('selected');
+ if (event.item) {
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'add', target: obj.el, originalEvent: event.originalEvent, item: event.item });
+ if (eventData.isCancelled === true) return;
+ // default behavior
+ if (selected.length >= options.max && options.max > 0) selected.pop();
+ delete event.item.hidden;
+ selected.push(event.item);
+ $(obj.el).data('selected', selected).change();
+ $(obj.helpers.multi).find('input').val('').width(20);
+ obj.refresh();
+ if ($("#w2ui-overlay").length > 0) $('#w2ui-overlay')[0].hide();
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }
+ } else {
+ $(obj.el).data('selected', event.item).val(event.item.text).change();
+ if (obj.helpers.focus) {
+ obj.helpers.focus.find('input').val('');
+ obj.refresh();
+ }
+ }
+ }
+ }));
+ }
+ }
+ },
+
+ inRange: function (str) {
+ var inRange = false;
+ if (this.type == 'date') {
+ var dt = w2utils.isDate(str, this.options.format, true);
+ if (dt) {
+ // enable range
+ if (this.options.start || this.options.end) {
+ var st = (typeof this.options.start == 'string' ? this.options.start : $(this.options.start).val());
+ var en = (typeof this.options.end == 'string' ? this.options.end : $(this.options.end).val());
+ var start = w2utils.isDate(st, this.options.format, true);
+ var end = w2utils.isDate(en, this.options.format, true);
+ var current = new Date(dt);
+ if (!start) start = current;
+ if (!end) end = current;
+ if (current >= start && current <= end) inRange = true;
+ } else {
+ inRange = true;
+ }
+ // block predefined dates
+ if (this.options.blocked && $.inArray(str, this.options.blocked) != -1) inRange = false;
+ }
+ }
+ if (this.type == 'time') {
+ if (this.options.start || this.options.end) {
+ var tm = this.toMin(str);
+ var tm1 = this.toMin(this.options.start);
+ var tm2 = this.toMin(this.options.end);
+ if (!tm1) tm1 = tm;
+ if (!tm2) tm2 = tm;
+ if (tm >= tm1 && tm <= tm2) inRange = true;
+ } else {
+ inRange = true;
+ }
+ }
+ return inRange;
+ },
+
+ /*
+ * INTERNAL FUNCTIONS
+ */
+
+ checkType: function (ch, loose) {
+ var obj = this;
+ switch (obj.type) {
+ case 'int':
+ if (loose && ['-'].indexOf(ch) != -1) return true;
+ return w2utils.isInt(ch.replace(obj.options.numberRE, ''));
+ case 'percent':
+ ch = ch.replace(/%/g, '');
+ case 'float':
+ if (loose && ['-','.'].indexOf(ch) != -1) return true;
+ return w2utils.isFloat(ch.replace(obj.options.numberRE, ''));
+ case 'money':
+ case 'currency':
+ if (loose && ['-', '.', obj.options.groupSymbol, obj.options.currencyPrefix, obj.options.currencySuffix].indexOf(ch) != -1) return true;
+ return w2utils.isFloat(ch.replace(obj.options.moneyRE, ''));
+ case 'hex':
+ case 'color':
+ return w2utils.isHex(ch);
+ case 'alphanumeric':
+ return w2utils.isAlphaNumeric(ch);
+ }
+ return true;
+ },
+
+ addPrefix: function () {
+ var obj = this;
+ setTimeout(function () {
+ if (obj.type === 'clear') return;
+ var helper;
+ var tmp = $(obj.el).data('tmp') || {};
+ if (tmp['old-padding-left']) $(obj.el).css('padding-left', tmp['old-padding-left']);
+ tmp['old-padding-left'] = $(obj.el).css('padding-left');
+ $(obj.el).data('tmp', tmp);
+ // remove if already displaed
+ if (obj.helpers.prefix) $(obj.helpers.prefix).remove();
+ if (obj.options.prefix !== '') {
+ // add fresh
+ $(obj.el).before(
+ '<div class="w2ui-field-helper">'+
+ obj.options.prefix +
+ '</div>'
+ );
+ helper = $(obj.el).prev();
+ helper
+ .css({
+ 'color' : $(obj.el).css('color'),
+ 'font-family' : $(obj.el).css('font-family'),
+ 'font-size' : $(obj.el).css('font-size'),
+ 'padding-top' : $(obj.el).css('padding-top'),
+ 'padding-bottom' : $(obj.el).css('padding-bottom'),
+ 'padding-left' : $(obj.el).css('padding-left'),
+ 'padding-right' : 0,
+ 'margin-top' : (parseInt($(obj.el).css('margin-top'), 10) + 2) + 'px',
+ 'margin-bottom' : (parseInt($(obj.el).css('margin-bottom'), 10) + 1) + 'px',
+ 'margin-left' : $(obj.el).css('margin-left'),
+ 'margin-right' : 0
+ })
+ .on('click', function (event) {
+ if (obj.options.icon && typeof obj.onIconClick == 'function') {
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'iconClick', target: obj.el, el: $(this).find('span.w2ui-icon')[0] });
+ if (eventData.isCancelled === true) return;
+
+ // intentionally empty
+
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ } else {
+ if (obj.type == 'list') {
+ $(obj.helpers.focus).find('input').focus();
+ } else {
+ $(obj.el).focus();
+ }
+ }
+ });
+ $(obj.el).css('padding-left', (helper.width() + parseInt($(obj.el).css('padding-left'), 10)) + 'px');
+ // remember helper
+ obj.helpers.prefix = helper;
+ }
+ }, 1);
+ },
+
+ addSuffix: function () {
+ var obj = this;
+ var helper, pr;
+ setTimeout(function () {
+ if (obj.type === 'clear') return;
+ var tmp = $(obj.el).data('tmp') || {};
+ if (tmp['old-padding-right']) $(obj.el).css('padding-right', tmp['old-padding-right']);
+ tmp['old-padding-right'] = $(obj.el).css('padding-right');
+ $(obj.el).data('tmp', tmp);
+ pr = parseInt($(obj.el).css('padding-right'), 10);
+ if (obj.options.arrows) {
+ // remove if already displaed
+ if (obj.helpers.arrows) $(obj.helpers.arrows).remove();
+ // add fresh
+ $(obj.el).after(
+ '<div class="w2ui-field-helper" style="border: 1px solid transparent">&nbsp;'+
+ ' <div class="w2ui-field-up" type="up">'+
+ ' <div class="arrow-up" type="up"></div>'+
+ ' </div>'+
+ ' <div class="w2ui-field-down" type="down">'+
+ ' <div class="arrow-down" type="down"></div>'+
+ ' </div>'+
+ '</div>');
+ var height = w2utils.getSize(obj.el, 'height');
+ helper = $(obj.el).next();
+ helper.css({
+ 'color' : $(obj.el).css('color'),
+ 'font-family' : $(obj.el).css('font-family'),
+ 'font-size' : $(obj.el).css('font-size'),
+ 'height' : ($(obj.el).height() + parseInt($(obj.el).css('padding-top'), 10) + parseInt($(obj.el).css('padding-bottom'), 10) ) + 'px',
+ 'padding' : 0,
+ 'margin-top' : (parseInt($(obj.el).css('margin-top'), 10) + 1) + 'px',
+ 'margin-bottom' : 0,
+ 'border-left' : '1px solid silver'
+ })
+ .css('margin-left', '-'+ (helper.width() + parseInt($(obj.el).css('margin-right'), 10) + 12) + 'px')
+ .on('mousedown', function (event) {
+ $('body').on('mouseup', tmp);
+ $('body').data('_field_update_timer', setTimeout(update, 700));
+ update(false);
+ // timer function
+ function tmp() {
+ clearTimeout($('body').data('_field_update_timer'));
+ $('body').off('mouseup', tmp);
+ }
+ // update function
+ function update(notimer) {
+ $(obj.el).focus();
+ obj.keyDown($.Event("keydown"), {
+ keyCode : ($(event.target).attr('type') == 'up' ? 38 : 40)
+ });
+ if (notimer !== false) $('body').data('_field_update_timer', setTimeout(update, 60));
+ }
+ });
+ pr += helper.width() + 12;
+ $(obj.el).css('padding-right', pr + 'px');
+ // remember helper
+ obj.helpers.arrows = helper;
+ }
+ if (obj.options.suffix !== '') {
+ // remove if already displaed
+ if (obj.helpers.suffix) $(obj.helpers.suffix).remove();
+ // add fresh
+ $(obj.el).after(
+ '<div class="w2ui-field-helper">'+
+ obj.options.suffix +
+ '</div>');
+ helper = $(obj.el).next();
+ helper
+ .css({
+ 'color' : $(obj.el).css('color'),
+ 'font-family' : $(obj.el).css('font-family'),
+ 'font-size' : $(obj.el).css('font-size'),
+ 'padding-top' : $(obj.el).css('padding-top'),
+ 'padding-bottom' : $(obj.el).css('padding-bottom'),
+ 'padding-left' : '3px',
+ 'padding-right' : $(obj.el).css('padding-right'),
+ 'margin-top' : (parseInt($(obj.el).css('margin-top'), 10) + 2) + 'px',
+ 'margin-bottom' : (parseInt($(obj.el).css('margin-bottom'), 10) + 1) + 'px'
+ })
+ .on('click', function (event) {
+ if (obj.type == 'list') {
+ $(obj.helpers.focus).find('input').focus();
+ } else {
+ $(obj.el).focus();
+ }
+ });
+
+ helper.css('margin-left', '-'+ (w2utils.getSize(helper, 'width') + parseInt($(obj.el).css('margin-right'), 10) + 2) + 'px');
+ pr += helper.width() + 3;
+ $(obj.el).css('padding-right', pr + 'px');
+ // remember helper
+ obj.helpers.suffix = helper;
+ }
+ }, 1);
+ },
+
+ addFocus: function () {
+ var obj = this;
+ var options = this.options;
+ var width = 0; // 11 - show search icon, 0 do not show
+ // clean up & init
+ $(obj.helpers.focus).remove();
+ // build helper
+ var html =
+ '<div class="w2ui-field-helper">'+
+ ' <div class="w2ui-icon icon-search"></div>'+
+ ' <input type="text" autocomplete="off">'+
+ '<div>';
+ $(obj.el).attr('tabindex', -1).before(html);
+ var helper = $(obj.el).prev();
+ obj.helpers.focus = helper;
+ helper.css({
+ width : $(obj.el).width(),
+ "margin-top" : $(obj.el).css('margin-top'),
+ "margin-left" : (parseInt($(obj.el).css('margin-left')) + parseInt($(obj.el).css('padding-left'))) + 'px',
+ "margin-bottom" : $(obj.el).css('margin-bottom'),
+ "margin-right" : $(obj.el).css('margin-right')
+ })
+ .find('input')
+ .css({
+ cursor : 'default',
+ width : '100%',
+ outline : 'none',
+ opacity : 1,
+ margin : 0,
+ border : '1px solid transparent',
+ padding : $(obj.el).css('padding-top'),
+ "padding-left" : 0,
+ "margin-left" : (width > 0 ? width + 6 : 0),
+ "background-color" : 'transparent'
+ });
+ // INPUT events
+ helper.find('input')
+ .on('click', function (event) {
+ if ($('#w2ui-overlay').length == 0) obj.focus(event);
+ event.stopPropagation();
+ })
+ .on('focus', function (event) {
+ $(obj.el).css({ 'outline': 'auto 5px #7DB4F3', 'outline-offset': '-2px' });
+ $(this).val('');
+ $(obj.el).triggerHandler('focus');
+ if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
+ })
+ .on('blur', function (event) {
+ $(obj.el).css('outline', 'none');
+ $(this).val('');
+ obj.refresh();
+ $(obj.el).triggerHandler('blur');
+ if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
+ })
+ .on('keyup', function (event) { obj.keyUp(event) })
+ .on('keydown', function (event) { obj.keyDown(event) })
+ .on('keypress', function (event) { obj.keyPress(event); });
+ // MAIN div
+ helper.on('click', function (event) { $(this).find('input').focus(); });
+ obj.refresh();
+ },
+
+ addMulti: function () {
+ var obj = this;
+ var options = this.options;
+ // clean up & init
+ $(obj.helpers.multi).remove();
+ // build helper
+ var html = '';
+ var margin =
+ 'margin-top : 0px; ' +
+ 'margin-bottom : 0px; ' +
+ 'margin-left : ' + $(obj.el).css('margin-left') + '; ' +
+ 'margin-right : ' + $(obj.el).css('margin-right') + '; '+
+ 'width : ' + (w2utils.getSize(obj.el, 'width')
+ - parseInt($(obj.el).css('margin-left'), 10)
+ - parseInt($(obj.el).css('margin-right'), 10))
+ + 'px;';
+ if (obj.type == 'enum') {
+ html = '<div class="w2ui-field-helper w2ui-list" style="'+ margin + '; box-sizing: border-box">'+
+ ' <div style="padding: 0px; margin: 0px; margin-right: 20px; display: inline-block">'+
+ ' <ul>'+
+ ' <li style="padding-left: 0px; padding-right: 0px" class="nomouse">'+
+ ' <input type="text" style="width: 20px" autocomplete="off" '+ ($(obj.el).attr('readonly') ? 'readonly': '') + '>'+
+ ' </li>'
+ ' </ul>'+
+ ' </div>'+
+ '</div>';
+ }
+ if (obj.type == 'file') {
+ html = '<div class="w2ui-field-helper w2ui-list" style="'+ margin + '; box-sizing: border-box">'+
+ ' <div style="padding: 0px; margin: 0px; margin-right: 20px; display: inline-block">'+
+ ' <ul><li style="padding-left: 0px; padding-right: 0px" class="nomouse"></li></ul>'+
+ ' <input class="file-input" type="file" name="attachment" multiple style="display: none" tabindex="-1">'
+ ' </div>'+
+ '</div>';
+ }
+ $(obj.el)
+ .before(html)
+ .css({
+ 'background-color' : 'transparent',
+ 'border-color' : 'transparent'
+ });
+
+ var div = $(obj.el).prev();
+ obj.helpers.multi = div;
+ if (obj.type == 'enum') {
+ $(obj.el).attr('tabindex', -1);
+ // INPUT events
+ div.find('input')
+ .on('click', function (event) {
+ if ($('#w2ui-overlay').length == 0) obj.focus(event);
+ $(obj.el).triggerHandler('click');
+ })
+ .on('focus', function (event) {
+ $(div).css({ 'outline': 'auto 5px #7DB4F3', 'outline-offset': '-2px' });
+ $(obj.el).triggerHandler('focus');
+ if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
+ })
+ .on('blur', function (event) {
+ $(div).css('outline', 'none');
+ $(obj.el).triggerHandler('blur');
+ if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;
+ })
+ .on('keyup', function (event) { obj.keyUp(event) })
+ .on('keydown', function (event) { obj.keyDown(event) })
+ .on('keypress', function (event) { div.find('.w2ui-enum-placeholder').remove(); obj.keyPress(event); });
+ // MAIN div
+ div.on('click', function (event) { $(this).find('input').focus(); });
+ }
+ if (obj.type == 'file') {
+ $(obj.el).css('outline', 'none');
+ div.on('click', function (event) {
+ $(obj.el).focus();
+ if ($(obj.el).attr('readonly')) return;
+ obj.blur(event);
+ div.find('input').click();
+ })
+ .on('dragenter', function (event) {
+ if ($(obj.el).attr('readonly')) return;
+ $(div).addClass('w2ui-file-dragover');
+ })
+ .on('dragleave', function (event) {
+ if ($(obj.el).attr('readonly')) return;
+ var tmp = $(event.target).parents('.w2ui-field-helper');
+ if (tmp.length == 0) $(div).removeClass('w2ui-file-dragover');
+ })
+ .on('drop', function (event) {
+ if ($(obj.el).attr('readonly')) return;
+ $(div).removeClass('w2ui-file-dragover');
+ var files = event.originalEvent.dataTransfer.files;
+ for (var i=0, l=files.length; i<l; i++) obj.addFile.call(obj, files[i]);
+ // cancel to stop browser behaviour
+ event.preventDefault();
+ event.stopPropagation();
+ })
+ .on('dragover', function (event) {
+ // cancel to stop browser behaviour
+ event.preventDefault();
+ event.stopPropagation();
+ });
+ div.find('input')
+ .on('click', function (event) {
+ event.stopPropagation();
+ })
+ .on('change', function () {
+ if (typeof this.files !== "undefined") {
+ for (var i = 0, l = this.files.length; i < l; i++) {
+ obj.addFile.call(obj, this.files[i]);
+ }
+ }
+ });
+ }
+ obj.refresh();
+ },
+
+ addFile: function (file) {
+ var obj = this;
+ var options = this.options;
+ var selected = $(obj.el).data('selected');
+ var newItem = {
+ name : file.name,
+ type : file.type,
+ modified : file.lastModifiedDate,
+ size : file.size,
+ content : null
+ };
+ var size = 0;
+ var cnt = 0;
+ var err;
+ for (var s in selected) { size += selected[s].size; cnt++; }
+ // trigger event
+ var eventData = obj.trigger({ phase: 'before', type: 'add', target: obj.el, file: newItem, total: cnt, totalSize: size });
+ if (eventData.isCancelled === true) return;
+ // check params
+ if (options.maxFileSize !== 0 && newItem.size > options.maxFileSize) {
+ err = 'Maximum file size is '+ w2utils.size(options.maxFileSize);
+ if (options.silent === false) $(obj.el).w2tag(err);
+ console.log('ERROR: '+ err);
+ return;
+ }
+ if (options.maxSize !== 0 && size + newItem.size > options.maxSize) {
+ err = 'Maximum total size is '+ w2utils.size(options.maxSize);
+ if (options.silent === false) $(obj.el).w2tag(err);
+ console.log('ERROR: '+ err);
+ return;
+ }
+ if (options.max !== 0 && cnt >= options.max) {
+ err = 'Maximum number of files is '+ options.max;
+ if (options.silent === false) $(obj.el).w2tag(err);
+ console.log('ERROR: '+ err);
+ return;
+ }
+ selected.push(newItem);
+ // read file as base64
+ if (typeof FileReader !== "undefined") {
+ var reader = new FileReader();
+ // need a closure
+ reader.onload = (function () {
+ return function (event) {
+ var fl = event.target.result;
+ var ind = fl.indexOf(',');
+ newItem.content = fl.substr(ind+1);
+ obj.refresh();
+ $(obj.el).trigger('change');
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ };
+ })();
+ reader.readAsDataURL(file);
+ } else {
+ obj.refresh();
+ $(obj.el).trigger('change');
+ }
+ },
+
+ normMenu: function (menu) {
+ if ($.isArray(menu)) {
+ for (var m = 0; m < menu.length; m++) {
+ if (typeof menu[m] == 'string') {
+ menu[m] = { id: menu[m], text: menu[m] };
+ } else {
+ if (typeof menu[m].text != 'undefined' && typeof menu[m].id == 'undefined') menu[m].id = menu[m].text;
+ if (typeof menu[m].text == 'undefined' && typeof menu[m].id != 'undefined') menu[m].text = menu[m].id;
+ if (typeof menu[m].caption != 'undefined') menu[m].text = menu[m].caption;
+ }
+ }
+ return menu;
+ } else if (typeof menu == 'object') {
+ var tmp = []
+ for (var m in menu) tmp.push({ id: m, text: menu[m] });
+ return tmp;
+ }
+ },
+
+ getColorHTML: function () {
+ var html = '<div class="w2ui-color">'+
+ '<table cellspacing="5">';
+ for (var i = 0; i < 8; i++) {
+ html += '<tr>';
+ for (var j = 0; j < 8; j++) {
+ html += '<td>'+
+ ' <div class="color" style="background-color: #'+ this.pallete[i][j] +';" name="'+ this.pallete[i][j] +'" index="'+ i + ':' + j +'">'+
+ ' '+ ($(this.el).val() == this.pallete[i][j] ? '&#149;' : '&nbsp;')+
+ ' </div>'+
+ '</td>';
+ }
+ html += '</tr>';
+ if (i < 2) html += '<tr><td style="height: 8px" colspan="8"></td></tr>';
+ }
+ html += '</table></div>';
+ return html;
+ },
+
+ getMonthHTML: function (month, year) {
+ var td = new Date();
+ var months = w2utils.settings.fullmonths;
+ var days = w2utils.settings.fulldays;
+ var daysCount = ['31', '28', '31', '30', '31', '30', '31', '31', '30', '31', '30', '31'];
+ var today = td.getFullYear() + '/' + (Number(td.getMonth()) + 1) + '/' + td.getDate();
+ // normalize date
+ year = w2utils.isInt(year) ? parseInt(year) : td.getFullYear();
+ month = w2utils.isInt(month) ? parseInt(month) : td.getMonth() + 1;
+ if (month > 12) { month -= 12; year++; }
+ if (month < 1 || month === 0) { month += 12; year--; }
+ if (year/4 == Math.floor(year/4)) { daysCount[1] = '29'; } else { daysCount[1] = '28'; }
+ this.options.current = month + '/' + year;
+
+ // start with the required date
+ td = new Date(year, month-1, 1);
+ var weekDay = td.getDay();
+ var tabDays = w2utils.settings.shortdays;
+ var dayTitle = '';
+ for ( var i = 0, len = tabDays.length; i < len; i++) {
+ dayTitle += '<td>' + tabDays[i] + '</td>';
+ }
+ var html =
+ '<div class="w2ui-calendar-title title">'+
+ ' <div class="w2ui-calendar-previous previous"> <div></div> </div>'+
+ ' <div class="w2ui-calendar-next next"> <div></div> </div> '+
+ months[month-1] +', '+ year +
+ '</div>'+
+ '<table class="w2ui-calendar-days" cellspacing="0">'+
+ ' <tr class="w2ui-day-title">' + dayTitle + '</tr>'+
+ ' <tr>';
+
+ var day = 1;
+ for (var ci=1; ci<43; ci++) {
+ if (weekDay === 0 && ci == 1) {
+ for (var ti=0; ti<6; ti++) html += '<td class="w2ui-day-empty">&nbsp;</td>';
+ ci += 6;
+ } else {
+ if (ci < weekDay || day > daysCount[month-1]) {
+ html += '<td class="w2ui-day-empty">&nbsp;</td>';
+ if ((ci) % 7 === 0) html += '</tr><tr>';
+ continue;
+ }
+ }
+ var dt = year + '/' + month + '/' + day;
+
+ var className = '';
+ if (ci % 7 == 6) className = ' w2ui-saturday';
+ if (ci % 7 === 0) className = ' w2ui-sunday';
+ if (dt == today) className += ' w2ui-today';
+
+ var dspDay = day;
+ var col = '';
+ var bgcol = '';
+ var tmp_dt = w2utils.formatDate(dt, this.options.format);
+ if (this.options.colored && this.options.colored[tmp_dt] !== undefined) { // if there is predefined colors for dates
+ tmp = this.options.colored[tmp_dt].split(':');
+ bgcol = 'background-color: ' + tmp[0] + ';';
+ col = 'color: ' + tmp[1] + ';';
+ }
+ html += '<td class="'+ (this.inRange(tmp_dt) ? 'w2ui-date ' : 'w2ui-blocked') + className + '" style="'+ col + bgcol + '" date="'+ tmp_dt +'">'+
+ dspDay +
+ '</td>';
+ if (ci % 7 === 0 || (weekDay === 0 && ci == 1)) html += '</tr><tr>';
+ day++;
+ }
+ html += '</tr></table>';
+ return html;
+ },
+
+ getYearHTML: function () {
+ var months = w2utils.settings.shortmonths;
+ var mhtml = '';
+ var yhtml = '';
+ for (var m in months) {
+ mhtml += '<div class="w2ui-jump-month" name="'+ m +'">'+ months[m] + '</div>';
+ }
+ for (var y = 1950; y <= 2020; y++) {
+ yhtml += '<div class="w2ui-jump-year" name="'+ y +'">'+ y + '</div>'
+ }
+ return '<div>'+ mhtml +'</div><div>'+ yhtml +'</div>';
+ },
+
+ getHourHTML: function () {
+ var tmp = [];
+ var h24 = (this.options.format == 'h24' ? true : false);
+ for (var a=0; a<24; a++) {
+ var time = (a >= 12 && !h24 ? a - 12 : a) + ':00' + (!h24 ? (a < 12 ? ' am' : ' pm') : '');
+ if (a == 12 && !h24) time = '12:00 pm';
+ if (!tmp[Math.floor(a/8)]) tmp[Math.floor(a/8)] = '';
+ var tm1 = this.fromMin(this.toMin(time));
+ var tm2 = this.fromMin(this.toMin(time) + 59);
+ tmp[Math.floor(a/8)] += '<div class="'+ (this.inRange(tm1) || this.inRange(tm2) ? 'w2ui-time ' : 'w2ui-blocked') + '" hour="'+ a +'">'+ time +'</div>';
+ }
+ var html =
+ '<div class="w2ui-calendar-time"><table><tr>'+
+ ' <td>'+ tmp[0] +'</td>' +
+ ' <td>'+ tmp[1] +'</td>' +
+ ' <td>'+ tmp[2] +'</td>' +
+ '</tr></table></div>';
+ return html;
+ },
+
+ getMinHTML: function (hour) {
+ if (typeof hour == 'undefined') hour = 0;
+ var h24 = (this.options.format == 'h24' ? true : false);
+ var tmp = [];
+ for (var a=0; a<60; a+=5) {
+ var time = (hour > 12 && !h24 ? hour - 12 : hour) + ':' + (a < 10 ? 0 : '') + a + ' ' + (!h24 ? (hour < 12 ? 'am' : 'pm') : '');
+ var ind = a < 20 ? 0 : (a < 40 ? 1 : 2);
+ if (!tmp[ind]) tmp[ind] = '';
+ tmp[ind] += '<div class="'+ (this.inRange(time) ? 'w2ui-time ' : 'w2ui-blocked') + '" min="'+ a +'">'+ time +'</div>';
+ }
+ var html =
+ '<div class="w2ui-calendar-time"><table><tr>'+
+ ' <td>'+ tmp[0] +'</td>' +
+ ' <td>'+ tmp[1] +'</td>' +
+ ' <td>'+ tmp[2] +'</td>' +
+ '</tr></table></div>';
+ return html;
+ },
+
+ toMin: function (str) {
+ if (typeof str != 'string') return null;
+ var tmp = str.split(':');
+ if (tmp.length == 2) {
+ tmp[0] = parseInt(tmp[0]);
+ tmp[1] = parseInt(tmp[1]);
+ if (str.indexOf('pm') != -1 && tmp[0] != 12) tmp[0] += 12;
+ } else {
+ return null;
+ }
+ return tmp[0] * 60 + tmp[1];
+ },
+
+ fromMin: function (time) {
+ var ret = '';
+ if (time >= 24 * 60) time = time % (24 * 60);
+ if (time < 0) time = 24 * 60 + time;
+ var hour = Math.floor(time/60);
+ var min = ((time % 60) < 10 ? '0' : '') + (time % 60);
+ if (this.options.format.indexOf('h24') != -1) {
+ ret = hour + ':' + min;
+ } else {
+ ret = (hour <= 12 ? hour : hour - 12) + ':' + min + ' ' + (hour >= 12 ? 'pm' : 'am');
+ }
+ return ret;
+ }
+ }
+
+ $.extend(w2field.prototype, w2utils.event);
+ w2obj.field = w2field;
+
+}) (jQuery);
+
+/************************************************************************
+* Library: Web 2.0 UI for jQuery (using prototypical inheritance)
+* - Following objects defined
+* - w2form - form widget
+* - $().w2form - jQuery wrapper
+* - Dependencies: jQuery, w2utils, w2fields, w2tabs, w2toolbar, w2alert
+*
+* == NICE TO HAVE ==
+* - refresh(field) - would refresh only one field
+* - include delta on save
+* - create an example how to do cascadic dropdown
+* - form should read <select> <options> into items
+* - two way data bindings
+* - verify validation of fields
+* - when field is blank, set record.field = null
+* - show/hide a field
+* - added getChanges() - not complete
+*
+************************************************************************/
+
+
+(function () {
+ var w2form = function(options) {
+ // public properties
+ this.name = null;
+ this.header = '';
+ this.box = null; // HTML element that hold this element
+ this.url = '';
+ this.routeData = {}; // data for dynamic routes
+ this.formURL = ''; // url where to get form HTML
+ this.formHTML = ''; // form HTML (might be loaded from the url)
+ this.page = 0; // current page
+ this.recid = 0; // can be null or 0
+ this.fields = [];
+ this.actions = {};
+ this.record = {};
+ this.original = {};
+ this.postData = {};
+ this.toolbar = {}; // if not empty, then it is toolbar
+ this.tabs = {}; // if not empty, then it is tabs object
+
+ this.style = '';
+ this.focus = 0; // focus first or other element
+ this.msgNotJSON = w2utils.lang('Return data is not in JSON format.');
+ this.msgAJAXerror = w2utils.lang('AJAX error. See console for more details.');
+ this.msgRefresh = w2utils.lang('Refreshing...');
+ this.msgSaving = w2utils.lang('Saving...');
+
+ // events
+ this.onRequest = null;
+ this.onLoad = null;
+ this.onValidate = null;
+ this.onSubmit = null;
+ this.onSave = null;
+ this.onChange = null;
+ this.onRender = null;
+ this.onRefresh = null;
+ this.onResize = null;
+ this.onDestroy = null;
+ this.onAction = null;
+ this.onToolbar = null;
+ this.onError = null;
+
+ // internal
+ this.isGenerated = false;
+ this.last = {
+ xhr: null // jquery xhr requests
+ }
+
+ $.extend(true, this, w2obj.form, options);
+ };
+
+ // ====================================================
+ // -- Registers as a jQuery plugin
+
+ $.fn.w2form = function(method) {
+ if (typeof method === 'object' || !method ) {
+ var obj = this;
+ // check name parameter
+ if (!w2utils.checkName(method, 'w2form')) return;
+ // remember items
+ var record = method.record;
+ var original = method.original;
+ var fields = method.fields;
+ var toolbar = method.toolbar;
+ var tabs = method.tabs;
+ // extend items
+ var object = new w2form(method);
+ $.extend(object, { record: {}, original: {}, fields: [], tabs: {}, toolbar: {}, handlers: [] });
+ if ($.isArray(tabs)) {
+ $.extend(true, object.tabs, { tabs: [] });
+ for (var t in tabs) {
+ var tmp = tabs[t];
+ if (typeof tmp === 'object') object.tabs.tabs.push(tmp); else object.tabs.tabs.push({ id: tmp, caption: tmp });
+ }
+ } else {
+ $.extend(true, object.tabs, tabs);
+ }
+ $.extend(true, object.toolbar, toolbar);
+ // reassign variables
+ for (var p in fields) {
+ var field = $.extend(true, {}, fields[p]);
+ if (typeof field.name == 'undefined' && typeof field.field != 'undefined') field.name = field.field;
+ if (typeof field.field == 'undefined' && typeof field.name != 'undefined') field.field = field.name;
+ object.fields[p] = field;
+ }
+ for (var p in record) {
+ if ($.isPlainObject(record[p])) {
+ object.record[p] = $.extend(true, {}, record[p]);
+ } else {
+ object.record[p] = record[p];
+ }
+ }
+ for (var p in original) {
+ if ($.isPlainObject(original[p])) {
+ object.original[p] = $.extend(true, {}, original[p]);
+ } else {
+ object.original[p] = original[p];
+ }
+ }
+ if (obj.length > 0) object.box = obj[0];
+ // render if necessary
+ if (object.formURL != '') {
+ $.get(object.formURL, function (data) { // should always be $.get as it is template
+ object.formHTML = data;
+ object.isGenerated = true;
+ if ($(object.box).length != 0 || data.length != 0) {
+ $(object.box).html(data);
+ object.render(object.box);
+ }
+ });
+ } else if (object.formHTML != '') {
+ // it is already loaded into formHTML
+ } else if ($(this).length != 0 && $.trim($(this).html()) != '') {
+ object.formHTML = $(this).html();
+ } else { // try to generate it
+ object.formHTML = object.generateHTML();
+ }
+ // register new object
+ w2ui[object.name] = object;
+ // render if not loaded from url
+ if (object.formURL == '') {
+ if (String(object.formHTML).indexOf('w2ui-page') == -1) {
+ object.formHTML = '<div class="w2ui-page page-0">'+ object.formHTML +'</div>';
+ }
+ $(object.box).html(object.formHTML);
+ object.isGenerated = true;
+ object.render(object.box);
+ }
+ return object;
+
+ } else if (w2ui[$(this).attr('name')]) {
+ var obj = w2ui[$(this).attr('name')];
+ obj[method].apply(obj, Array.prototype.slice.call(arguments, 1));
+ return this;
+ } else {
+ console.log('ERROR: Method ' + method + ' does not exist on jQuery.w2form');
+ }
+ };
+
+ // ====================================================
+ // -- Implementation of core functionality
+
+ w2form.prototype = {
+
+ get: function (field, returnIndex) {
+ if (arguments.length === 0) {
+ var all = [];
+ for (var f1 in this.fields) {
+ if (this.fields[f1].name != null) all.push(this.fields[f1].name);
+ }
+ return all;
+ } else {
+ for (var f2 in this.fields) {
+ if (this.fields[f2].name == field) {
+ if (returnIndex === true) return f2; else return this.fields[f2];
+ }
+ }
+ return null;
+ }
+ },
+
+ set: function (field, obj) {
+ for (var f in this.fields) {
+ if (this.fields[f].name == field) {
+ $.extend(this.fields[f] , obj);
+ this.refresh();
+ return true;
+ }
+ }
+ return false;
+ },
+
+ reload: function (callBack) {
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (url && this.recid != 0) {
+ // this.clear();
+ this.request(callBack);
+ } else {
+ // this.refresh(); // no need to refresh
+ if (typeof callBack == 'function') callBack();
+ }
+ },
+
+ clear: function () {
+ this.recid = 0;
+ this.record = {};
+ $().w2tag();
+ this.refresh();
+ },
+
+ error: function (msg) {
+ var obj = this;
+ // let the management of the error outside of the grid
+ var eventData = this.trigger({ target: this.name, type: 'error', message: msg , xhr: this.last.xhr });
+ if (eventData.isCancelled === true) {
+ if (typeof callBack == 'function') callBack();
+ return;
+ }
+ // need a time out because message might be already up)
+ setTimeout(function () { w2alert(msg, 'Error'); }, 1);
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ validate: function (showErrors) {
+ if (typeof showErrors == 'undefined') showErrors = true;
+ $().w2tag(); // hide all tags before validating
+ // validate before saving
+ var errors = [];
+ for (var f in this.fields) {
+ var field = this.fields[f];
+ if (this.record[field.name] == null) this.record[field.name] = '';
+ switch (field.type) {
+ case 'int':
+ if (this.record[field.name] && !w2utils.isInt(this.record[field.name])) {
+ errors.push({ field: field, error: w2utils.lang('Not an integer') });
+ }
+ break;
+ case 'float':
+ if (this.record[field.name] && !w2utils.isFloat(this.record[field.name])) {
+ errors.push({ field: field, error: w2utils.lang('Not a float') });
+ }
+ break;
+ case 'money':
+ if (this.record[field.name] && !w2utils.isMoney(this.record[field.name])) {
+ errors.push({ field: field, error: w2utils.lang('Not in money format') });
+ }
+ break;
+ case 'color':
+ case 'hex':
+ if (this.record[field.name] && !w2utils.isHex(this.record[field.name])) {
+ errors.push({ field: field, error: w2utils.lang('Not a hex number') });
+ }
+ break;
+ case 'email':
+ if (this.record[field.name] && !w2utils.isEmail(this.record[field.name])) {
+ errors.push({ field: field, error: w2utils.lang('Not a valid email') });
+ }
+ break;
+ case 'checkbox':
+ // convert true/false
+ if (this.record[field.name] == true) this.record[field.name] = 1; else this.record[field.name] = 0;
+ break;
+ case 'date':
+ // format date before submit
+ if (!field.options.format) field.options.format = w2utils.settings.date_format;
+ if (this.record[field.name] && !w2utils.isDate(this.record[field.name], field.options.format)) {
+ errors.push({ field: field, error: w2utils.lang('Not a valid date') + ': ' + field.options.format });
+ } else {
+ }
+ break;
+ case 'list':
+ case 'combo':
+ break;
+ case 'enum':
+ break;
+ }
+ // === check required - if field is '0' it should be considered not empty
+ var val = this.record[field.name];
+ if (field.required && (val === '' || ($.isArray(val) && val.length == 0) || ($.isPlainObject(val) && $.isEmptyObject(val)))) {
+ errors.push({ field: field, error: w2utils.lang('Required field') });
+ }
+ if (field.equalto && this.record[field.name] != this.record[field.equalto]) {
+ errors.push({ field: field, error: w2utils.lang('Field should be equal to ') + field.equalto });
+ }
+ }
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'validate', errors: errors });
+ if (eventData.isCancelled === true) return;
+ // show error
+ if (showErrors) for (var e in eventData.errors) {
+ var err = eventData.errors[e];
+ if (err.field.type == 'radio') { // for radio and checkboxes
+ $($(err.field.el).parents('div')[0]).w2tag(err.error, { "class": 'w2ui-error' });
+ } else if (['enum', 'file'].indexOf(err.field.type) != -1) {
+ (function (err) {
+ setTimeout(function () {
+ var fld = $(err.field.el).data('w2field').helpers.multi;
+ $(err.field.el).w2tag(err.error);
+ $(fld).addClass('w2ui-error');
+ }, 1);
+ })(err);
+ } else {
+ $(err.field.el).w2tag(err.error, { "class": 'w2ui-error' });
+ }
+ this.goto(errors[0].field.page);
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ return errors;
+ },
+
+ getChanges: function () {
+ var differ = function(record, original, result) {
+ for (var i in record) {
+ if (typeof record[i] == "object") {
+ result[i] = differ(record[i], original[i] || {}, {});
+ if (!result[i] || $.isEmptyObject(result[i])) delete result[i];
+ } else if (record[i] != original[i]) {
+ result[i] = record[i];
+ }
+ }
+ return result;
+ }
+ return differ(this.record, this.original, {});
+ },
+
+ request: function (postData, callBack) { // if (1) param then it is call back if (2) then postData and callBack
+ var obj = this;
+ // check for multiple params
+ if (typeof postData == 'function') {
+ callBack = postData;
+ postData = null;
+ }
+ if (typeof postData == 'undefined' || postData == null) postData = {};
+ if (!this.url || (typeof this.url == 'object' && !this.url.get)) return;
+ if (this.recid == null || typeof this.recid == 'undefined') this.recid = 0;
+ // build parameters list
+ var params = {};
+ // add list params
+ params['cmd'] = 'get-record';
+ params['recid'] = this.recid;
+ // append other params
+ $.extend(params, this.postData);
+ $.extend(params, postData);
+ // event before
+ var eventData = this.trigger({ phase: 'before', type: 'request', target: this.name, url: this.url, postData: params });
+ if (eventData.isCancelled === true) { if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' }); return; }
+ // default action
+ this.record = {};
+ this.original = {};
+ // call server to get data
+ this.lock(this.msgRefresh);
+ var url = eventData.url;
+ if (typeof eventData.url == 'object' && eventData.url.get) url = eventData.url.get;
+ if (this.last.xhr) try { this.last.xhr.abort(); } catch (e) {};
+ // process url with routeData
+ if (!$.isEmptyObject(obj.routeData)) {
+ var info = w2utils.parseRoute(url);
+ if (info.keys.length > 0) {
+ for (var k = 0; k < info.keys.length; k++) {
+ if (obj.routeData[info.keys[k].name] == null) continue;
+ url = url.replace((new RegExp(':'+ info.keys[k].name, 'g')), obj.routeData[info.keys[k].name]);
+ }
+ }
+ }
+ var ajaxOptions = {
+ type : 'POST',
+ url : url,
+ data : eventData.postData,
+ dataType : 'text' // expected from server
+ };
+ if (w2utils.settings.dataType == 'HTTP') {
+ ajaxOptions.data = String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']');
+ }
+ if (w2utils.settings.dataType == 'RESTFULL') {
+ ajaxOptions.type = 'GET';
+ ajaxOptions.data = String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']');
+ }
+ if (w2utils.settings.dataType == 'JSON') {
+ ajaxOptions.type = 'POST';
+ ajaxOptions.data = JSON.stringify(ajaxOptions.data);
+ ajaxOptions.contentType = 'application/json';
+ }
+ this.last.xhr = $.ajax(ajaxOptions)
+ .done(function (data, status, xhr) {
+ obj.unlock();
+ // event before
+ var eventData = obj.trigger({ phase: 'before', target: obj.name, type: 'load', xhr: xhr });
+ if (eventData.isCancelled === true) {
+ if (typeof callBack == 'function') callBack({ status: 'error', message: 'Request aborted.' });
+ return;
+ }
+ // parse server response
+ var data;
+ var responseText = obj.last.xhr.responseText;
+ if (status != 'error') {
+ // default action
+ if (typeof responseText != 'undefined' && responseText != '') {
+ // check if the onLoad handler has not already parsed the data
+ if (typeof responseText == "object") {
+ data = responseText;
+ } else {
+ // $.parseJSON or $.getJSON did not work because those expect perfect JSON data - where everything is in double quotes
+ //
+ // TODO: avoid (potentially malicious) code injection from the response.
+ try { eval('data = '+ responseText); } catch (e) { }
+ }
+ if (typeof data == 'undefined') {
+ data = {
+ status : 'error',
+ message : obj.msgNotJSON,
+ responseText : responseText
+ }
+ }
+ if (data['status'] == 'error') {
+ obj.error(data['message']);
+ } else {
+ obj.record = $.extend({}, data.record);
+ obj.original = $.extend({}, data.record);
+ }
+ }
+ } else {
+ obj.error('AJAX Error ' + xhr.status + ': '+ xhr.statusText);
+ data = {
+ status : 'error',
+ message : obj.msgAJAXerror,
+ responseText : responseText
+ };
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ obj.refresh();
+ // call back
+ if (typeof callBack == 'function') callBack(data);
+ })
+ .fail(function (xhr, status, error) {
+ // trigger event
+ var errorObj = { status: status, error: error, rawResponseText: xhr.responseText };
+ var eventData2 = obj.trigger({ phase: 'before', type: 'error', error: errorObj, xhr: xhr });
+ if (eventData2.isCancelled === true) return;
+ // default behavior
+ if (status != 'abort') {
+ var data;
+ try { data = $.parseJSON(xhr.responseText) } catch (e) {}
+ console.log('ERROR: Server communication failed.',
+ '\n EXPECTED:', { status: 'success', items: [{ id: 1, text: 'item' }] },
+ '\n OR:', { status: 'error', message: 'error message' },
+ '\n RECEIVED:', typeof data == 'object' ? data : xhr.responseText);
+ }
+ // event after
+ obj.trigger($.extend(eventData2, { phase: 'after' }));
+ });
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ submit: function (postData, callBack) {
+ return this.save(postData, callBack);
+ },
+
+ save: function (postData, callBack) {
+ var obj = this;
+ $(this.box).find(':focus').change(); // trigger onchange
+ // check for multiple params
+ if (typeof postData == 'function') {
+ callBack = postData;
+ postData = null;
+ }
+ // validation
+ var errors = obj.validate(true);
+ if (errors.length !== 0) return;
+ // submit save
+ if (typeof postData == 'undefined' || postData == null) postData = {};
+ if (!obj.url || (typeof obj.url == 'object' && !obj.url.save)) {
+ console.log("ERROR: Form cannot be saved because no url is defined.");
+ return;
+ }
+ obj.lock(obj.msgSaving + ' <span id="'+ obj.name +'_progress"></span>');
+ // need timer to allow to lock
+ setTimeout(function () {
+ // build parameters list
+ var params = {};
+ // add list params
+ params['cmd'] = 'save-record';
+ params['recid'] = obj.recid;
+ // append other params
+ $.extend(params, obj.postData);
+ $.extend(params, postData);
+ params.record = $.extend(true, {}, obj.record);
+ // event before
+ var eventData = obj.trigger({ phase: 'before', type: 'submit', target: obj.name, url: obj.url, postData: params });
+ if (eventData.isCancelled === true) return;
+ // default action
+ var url = eventData.url;
+ if (typeof eventData.url == 'object' && eventData.url.save) url = eventData.url.save;
+ if (obj.last.xhr) try { obj.last.xhr.abort(); } catch (e) {};
+ // process url with routeData
+ if (!$.isEmptyObject(obj.routeData)) {
+ var info = w2utils.parseRoute(url);
+ if (info.keys.length > 0) {
+ for (var k = 0; k < info.keys.length; k++) {
+ if (obj.routeData[info.keys[k].name] == null) continue;
+ url = url.replace((new RegExp(':'+ info.keys[k].name, 'g')), obj.routeData[info.keys[k].name]);
+ }
+ }
+ }
+ var ajaxOptions = {
+ type : 'POST',
+ url : url,
+ data : eventData.postData,
+ dataType : 'text', // expected from server
+ xhr : function() {
+ var xhr = new window.XMLHttpRequest();
+ // upload
+ xhr.upload.addEventListener("progress", function(evt) {
+ if (evt.lengthComputable) {
+ var percent = Math.round(evt.loaded / evt.total * 100);
+ $('#'+ obj.name + '_progress').text(''+ percent + '%');
+ }
+ }, false);
+ return xhr;
+ }
+ };
+ if (w2utils.settings.dataType == 'HTTP') {
+ ajaxOptions.data = String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']');
+ }
+ if (w2utils.settings.dataType == 'RESTFULL') {
+ if (obj.recid != 0) ajaxOptions.type = 'PUT';
+ ajaxOptions.data = String($.param(ajaxOptions.data, false)).replace(/%5B/g, '[').replace(/%5D/g, ']');
+ }
+ if (w2utils.settings.dataType == 'JSON') {
+ ajaxOptions.type = 'POST';
+ ajaxOptions.data = JSON.stringify(ajaxOptions.data);
+ ajaxOptions.contentType = 'application/json';
+ }
+
+ obj.last.xhr = $.ajax(ajaxOptions)
+ .done(function (data, status, xhr) {
+ obj.unlock();
+ // event before
+ var eventData = obj.trigger({ phase: 'before', target: obj.name, type: 'save', xhr: xhr, status: status });
+ if (eventData.isCancelled === true) return;
+ // parse server response
+ var data;
+ var responseText = xhr.responseText;
+ if (status != 'error') {
+ // default action
+ if (typeof responseText != 'undefined' && responseText != '') {
+ // check if the onLoad handler has not already parsed the data
+ if (typeof responseText == "object") {
+ data = responseText;
+ } else {
+ // $.parseJSON or $.getJSON did not work because those expect perfect JSON data - where everything is in double quotes
+ //
+ // TODO: avoid (potentially malicious) code injection from the response.
+ try { eval('data = '+ responseText); } catch (e) { }
+ }
+ if (typeof data == 'undefined') {
+ data = {
+ status : 'error',
+ message : obj.msgNotJSON,
+ responseText : responseText
+ }
+ }
+ if (data['status'] == 'error') {
+ obj.error(data['message']);
+ } else {
+ obj.original = $.extend({}, obj.record);
+ }
+ }
+ } else {
+ obj.error('AJAX Error ' + xhr.status + ': '+ xhr.statusText);
+ data = {
+ status : 'error',
+ message : obj.msgAJAXerror,
+ responseText : responseText
+ };
+ }
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ obj.refresh();
+ // call back
+ if (data.status == 'success' && typeof callBack == 'function') callBack(data);
+ })
+ .fail(function (xhr, status, error) {
+ // trigger event
+ var errorObj = { status: status, error: error, rawResponseText: xhr.responseText };
+ var eventData2 = obj.trigger({ phase: 'before', type: 'error', error: errorObj, xhr: xhr });
+ if (eventData2.isCancelled === true) return;
+ // default behavior
+ console.log('ERROR: server communication failed. The server should return',
+ { status: 'success' }, 'OR', { status: 'error', message: 'error message' },
+ ', instead the AJAX request produced this: ', errorObj);
+ // event after
+ obj.trigger($.extend(eventData2, { phase: 'after' }));
+ });
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ }, 50);
+ },
+
+ lock: function (msg, showSpinner) {
+ var box = $(this.box).find('> div:first-child');
+ var args = Array.prototype.slice.call(arguments, 0);
+ args.unshift(box);
+ w2utils.lock.apply(window, args);
+ },
+
+ unlock: function () {
+ var obj = this;
+ setTimeout(function () { w2utils.unlock(obj.box); }, 25); // needed timer so if server fast, it will not flash
+ },
+
+ goto: function (page) {
+ if (typeof page != 'undefined') this.page = page;
+ // if it was auto size, resize it
+ if ($(this.box).data('auto-size') === true) $(this.box).height(0);
+ this.refresh();
+ },
+
+ generateHTML: function () {
+ var pages = []; // array for each page
+ var group = '';
+ var page;
+ for (var f in this.fields) {
+ var html = '';
+ var field = this.fields[f];
+ if (typeof field.html == 'undefined') field.html = {};
+ field.html = $.extend(true, { caption: '', span: 6, attr: '', text: '', page: 0 }, field.html);
+ if (typeof page == 'undefined') page = field.html.page;
+ if (field.html.caption == '') field.html.caption = field.name;
+ var input = '<input name="'+ field.name +'" type="text" '+ field.html.attr +'/>';
+ if ((field.type === 'pass') || (field.type === 'password')){
+ input = '<input name="' + field.name + '" type = "password" ' + field.html.attr + '/>';
+ }
+ if (field.type == 'checkbox') input = '<input name="'+ field.name +'" type="checkbox" '+ field.html.attr +'/>';
+ if (field.type == 'textarea') input = '<textarea name="'+ field.name +'" '+ field.html.attr +'></textarea>';
+ if (field.type == 'toggle') input = '<input name="'+ field.name +'" type="checkbox" '+ field.html.attr +' class="w2ui-toggle"/><div><div></div></div>';
+ if (field.html.group) {
+ if (group != '') html += '\n </div>';
+ html += '\n <div class="w2ui-group-title">'+ field.html.group + '</div>\n <div class="w2ui-group">';
+ group = field.html.group;
+ }
+ if (field.html.page != page && group != '') {
+ pages[pages.length-1] += '\n </div>';
+ group = '';
+ }
+ html += '\n <div class="w2ui-field '+ (typeof field.html.span != 'undefined' ? 'w2ui-span'+ field.html.span : '') +'">'+
+ '\n <label>' + w2utils.lang(field.html.caption) +'</label>'+
+ '\n <div>'+ input + w2utils.lang(field.html.text) + '</div>'+
+ '\n </div>';
+ if (typeof pages[field.html.page] == 'undefined') pages[field.html.page] = '';
+ pages[field.html.page] += html;
+ page = field.html.page;
+ }
+ if (group != '') pages[pages.length-1] += '\n </div>';
+ if (this.tabs.tabs) {
+ for (var i = 0; i < this.tabs.tabs.length; i++) if (typeof pages[i] == 'undefined') pages[i] = '';
+ }
+ for (var p in pages) pages[p] = '<div class="w2ui-page page-'+ p +'">' + pages[p] + '\n</div>';
+ // buttons if any
+ var buttons = '';
+ if (!$.isEmptyObject(this.actions)) {
+ var addClass = '';
+ buttons += '\n<div class="w2ui-buttons">';
+ for (var a in this.actions) {
+ if (['save', 'update', 'create'].indexOf(a.toLowerCase()) != -1) addClass = 'btn-green'; else addClass = '';
+ buttons += '\n <button name="'+ a +'" class="btn '+ addClass +'">'+ w2utils.lang(a) +'</button>';
+ }
+ buttons += '\n</div>';
+ }
+ return pages.join('') + buttons;
+ },
+
+ action: function (action, event) {
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: action, type: 'action', originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // default actions
+ if (typeof (this.actions[action]) == 'function') {
+ this.actions[action].call(this, event);
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ },
+
+ resize: function () {
+ var obj = this;
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'resize' });
+ if (eventData.isCancelled === true) return;
+ // default behaviour
+ var main = $(this.box).find('> div');
+ var header = $(this.box).find('> div .w2ui-form-header');
+ var toolbar = $(this.box).find('> div .w2ui-form-toolbar');
+ var tabs = $(this.box).find('> div .w2ui-form-tabs');
+ var page = $(this.box).find('> div .w2ui-page');
+ var cpage = $(this.box).find('> div .w2ui-page.page-'+ this.page);
+ var dpage = $(this.box).find('> div .w2ui-page.page-'+ this.page + ' > div');
+ var buttons = $(this.box).find('> div .w2ui-buttons');
+ // if no height, calculate it
+ resizeElements();
+ if (parseInt($(this.box).height()) == 0 || $(this.box).data('auto-size') === true) {
+ $(this.box).height(
+ (header.length > 0 ? w2utils.getSize(header, 'height') : 0) +
+ ((typeof this.tabs === 'object' && $.isArray(this.tabs.tabs) && this.tabs.tabs.length > 0) ? w2utils.getSize(tabs, 'height') : 0) +
+ ((typeof this.toolbar == 'object' && $.isArray(this.toolbar.items) && this.toolbar.items.length > 0) ? w2utils.getSize(toolbar, 'height') : 0) +
+ (page.length > 0 ? w2utils.getSize(dpage, 'height') + w2utils.getSize(cpage, '+height') + 12 : 0) + // why 12 ???
+ (buttons.length > 0 ? w2utils.getSize(buttons, 'height') : 0)
+ );
+ $(this.box).data('auto-size', true);
+ }
+ resizeElements();
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+
+ function resizeElements() {
+ // resize elements
+ main.width($(obj.box).width()).height($(obj.box).height());
+ toolbar.css('top', (obj.header != '' ? w2utils.getSize(header, 'height') : 0));
+ tabs.css('top', (obj.header != '' ? w2utils.getSize(header, 'height') : 0)
+ + ((typeof obj.toolbar == 'object' && $.isArray(obj.toolbar.items) && obj.toolbar.items.length > 0) ? w2utils.getSize(toolbar, 'height') : 0));
+ page.css('top', (obj.header != '' ? w2utils.getSize(header, 'height') : 0)
+ + ((typeof obj.toolbar == 'object' && $.isArray(obj.toolbar.items) && obj.toolbar.items.length > 0) ? w2utils.getSize(toolbar, 'height') + 5 : 0)
+ + ((typeof obj.tabs === 'object' && $.isArray(obj.tabs.tabs) && obj.tabs.tabs.length > 0) ? w2utils.getSize(tabs, 'height') + 5 : 0));
+ page.css('bottom', (buttons.length > 0 ? w2utils.getSize(buttons, 'height') : 0));
+ }
+ },
+
+ refresh: function () {
+ var time = (new Date()).getTime();
+ var obj = this;
+ if (!this.box) return;
+ if (!this.isGenerated || typeof $(this.box).html() == 'undefined') return;
+ // update what page field belongs
+ $(this.box).find('input, textarea, select').each(function (index, el) {
+ var name = (typeof $(el).attr('name') != 'undefined' ? $(el).attr('name') : $(el).attr('id'));
+ var field = obj.get(name);
+ if (field) {
+ // find page
+ var div = $(el).parents('.w2ui-page');
+ if (div.length > 0) {
+ for (var i = 0; i < 100; i++) {
+ if (div.hasClass('page-'+i)) { field.page = i; break; }
+ }
+ }
+ }
+ });
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'refresh', page: this.page })
+ if (eventData.isCancelled === true) return;
+ // default action
+ $(this.box).find('.w2ui-page').hide();
+ $(this.box).find('.w2ui-page.page-' + this.page).show();
+ $(this.box).find('.w2ui-form-header').html(this.header);
+ // refresh tabs if needed
+ if (typeof this.tabs === 'object' && $.isArray(this.tabs.tabs) && this.tabs.tabs.length > 0) {
+ $('#form_'+ this.name +'_tabs').show();
+ this.tabs.active = this.tabs.tabs[this.page].id;
+ this.tabs.refresh();
+ } else {
+ $('#form_'+ this.name +'_tabs').hide();
+ }
+ // refresh tabs if needed
+ if (typeof this.toolbar == 'object' && $.isArray(this.toolbar.items) && this.toolbar.items.length > 0) {
+ $('#form_'+ this.name +'_toolbar').show();
+ this.toolbar.refresh();
+ } else {
+ $('#form_'+ this.name +'_toolbar').hide();
+ }
+ // refresh values of all fields
+ for (var f in this.fields) {
+ var field = this.fields[f];
+ if (typeof field.name == 'undefined' && typeof field.field != 'undefined') field.name = field.field;
+ if (typeof field.field == 'undefined' && typeof field.name != 'undefined') field.field = field.name;
+ field.$el = $(this.box).find('[name="'+ String(field.name).replace(/\\/g, '\\\\') +'"]');
+ field.el = field.$el[0];
+ if (typeof field.el == 'undefined') {
+ console.log('ERROR: Cannot associate field "'+ field.name + '" with html control. Make sure html control exists with the same name.');
+ //return;
+ }
+ if (field.el) field.el.id = field.name;
+ var tmp = $(field).data('w2field');
+ if (tmp) tmp.clear();
+ $(field.$el).off('change').on('change', function () {
+ var value_new = this.value;
+ var value_previous = obj.record[this.name] ? obj.record[this.name] : '';
+ var field = obj.get(this.name);
+ if (['list', 'enum', 'file'].indexOf(field.type) != -1 && $(this).data('selected')) {
+ var nv = $(this).data('selected');
+ var cv = obj.record[this.name];
+ if ($.isArray(nv)) {
+ value_new = [];
+ for (var i in nv) value_new[i] = $.extend(true, {}, nv[i]); // clone array
+ }
+ if ($.isPlainObject(nv)) {
+ value_new = $.extend(true, {}, nv); // clone object
+ }
+ if ($.isArray(cv)) {
+ value_previous = [];
+ for (var i in cv) value_previous[i] = $.extend(true, {}, cv[i]); // clone array
+ }
+ if ($.isPlainObject(cv)) {
+ value_previous = $.extend(true, {}, cv); // clone object
+ }
+ }
+ if (field.type == 'toggle') value_new = ($(this).prop('checked') ? 1 : 0);
+ // clean extra chars
+ if (['int', 'float', 'percent', 'money', 'currency'].indexOf(field.type) != -1) {
+ value_new = $(this).data('w2field').clean(value_new);
+ }
+ if (value_new === value_previous) return;
+ // event before
+ var eventData = obj.trigger({ phase: 'before', target: this.name, type: 'change', value_new: value_new, value_previous: value_previous });
+ if (eventData.isCancelled === true) {
+ $(this).val(obj.record[this.name]); // return previous value
+ return;
+ }
+ // default action
+ var val = this.value;
+ if (this.type == 'select') val = this.value;
+ if (this.type == 'checkbox') val = this.checked ? true : false;
+ if (this.type == 'radio') {
+ field.$el.each(function (index, el) {
+ if (el.checked) val = el.value;
+ });
+ }
+ if (['int', 'float', 'percent', 'money', 'currency', 'list', 'combo', 'enum', 'file', 'toggle'].indexOf(field.type) != -1) {
+ val = value_new;
+ }
+ if (['enum', 'file'].indexOf(field.type) != -1) {
+ if (val.length > 0) {
+ var fld = $(field.el).data('w2field').helpers.multi;
+ $(fld).removeClass('w2ui-error');
+ }
+ }
+ obj.record[this.name] = val;
+ // event after
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ });
+ if (field.required) {
+ $(field.el).parent().parent().addClass('w2ui-required');
+ } else {
+ $(field.el).parent().parent().removeClass('w2ui-required');
+ }
+ }
+ // attach actions on buttons
+ $(this.box).find('button, input[type=button]').each(function (index, el) {
+ $(el).off('click').on('click', function (event) {
+ var action = this.value;
+ if (this.id) action = this.id;
+ if (this.name) action = this.name;
+ obj.action(action, event);
+ });
+ });
+ // init controls with record
+ for (var f in this.fields) {
+ var field = this.fields[f];
+ var value = (typeof this.record[field.name] != 'undefined' ? this.record[field.name] : '');
+ if (!field.el) continue;
+ field.type = String(field.type).toLowerCase();
+ if (!field.options) field.options = {};
+ switch (field.type) {
+ case 'text':
+ case 'textarea':
+ case 'email':
+ case 'pass':
+ case 'password':
+ field.el.value = value;
+ break;
+ case 'int':
+ case 'float':
+ case 'money':
+ case 'currency':
+ case 'percent':
+ case 'hex':
+ case 'alphanumeric':
+ case 'color':
+ case 'date':
+ case 'time':
+ field.el.value = value;
+ $(field.el).w2field($.extend({}, field.options, { type: field.type }));
+ break;
+ case 'toggle':
+ if (w2utils.isFloat(value)) value = parseFloat(value);
+ $(field.el).prop('checked', (value ? true : false));
+ this.record[field.name] = (value ? 1 : 0);
+ break;
+ // enums
+ case 'list':
+ case 'combo':
+ if (field.type == 'list' && !$.isPlainObject(value)) {
+ // find value from items
+ for (var i in field.options.items) {
+ var item = field.options.items[i];
+ if ($.isPlainObject(item) && item.id == value) {
+ value = $.extend(true, {}, item);
+ obj.record[field.name] = value;
+ break;
+ } else if (i == value) {
+ value = { id: i, text: item };
+ obj.record[field.name] = value;
+ break;
+ }
+ }
+ } else if (field.type == 'combo' && !$.isPlainObject(value)) {
+ field.el.value = value;
+ } else if ($.isPlainObject(value) && typeof value.text != 'undefined') {
+ field.el.value = value.text;
+ } else {
+ field.el.value = '';
+ }
+ if (!$.isPlainObject(value)) value = {};
+ $(field.el).w2field($.extend({}, field.options, { type: field.type, selected: value }));
+ break;
+ case 'enum':
+ case 'file':
+ if (!$.isArray(value)) value = [];
+ $(field.el).w2field($.extend({}, field.options, { type: field.type, selected: value }));
+ break;
+
+ // standard HTML
+ case 'select':
+ // generate options
+ var items = field.options.items;
+ if (typeof items != 'undefined' && items.length > 0) {
+ items = w2obj.field.prototype.normMenu(items);
+ $(field.el).html('');
+ for (var it in items) {
+ $(field.el).append('<option value="'+ items[it].id +'">' + items[it].text + '</option');
+ }
+ }
+ $(field.el).val(value);
+ break;
+ case 'radio':
+ $(field.$el).prop('checked', false).each(function (index, el) {
+ if ($(el).val() == value) $(el).prop('checked', true);
+ });
+ break;
+ case 'checkbox':
+ $(field.el).prop('checked', value ? true : false);
+ break;
+ default:
+ $(field.el).w2field($.extend({}, field.options, { type: field.type }));
+ break;
+ }
+ }
+ // wrap pages in div
+ var tmp = $(this.box).find('.w2ui-page');
+ for (var i = 0; i < tmp.length; i++) {
+ if ($(tmp[i]).find('> *').length > 1) $(tmp[i]).wrapInner('<div></div>');
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ this.resize();
+ return (new Date()).getTime() - time;
+ },
+
+ render: function (box) {
+ var time = (new Date()).getTime();
+ var obj = this;
+ if (typeof box == 'object') {
+ // remove from previous box
+ if ($(this.box).find('#form_'+ this.name +'_tabs').length > 0) {
+ $(this.box).removeAttr('name')
+ .removeClass('w2ui-reset w2ui-form')
+ .html('');
+ }
+ this.box = box;
+ }
+ if (!this.isGenerated) return;
+ if (!this.box) return;
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'render', box: (typeof box != 'undefined' ? box : this.box) });
+ if (eventData.isCancelled === true) return;
+ // default actions
+ if ($.isEmptyObject(this.original) && !$.isEmptyObject(this.record)) {
+ this.original = $.extend(true, {}, this.record);
+ }
+ var html = '<div>' +
+ (this.header != '' ? '<div class="w2ui-form-header">' + this.header + '</div>' : '') +
+ ' <div id="form_'+ this.name +'_toolbar" class="w2ui-form-toolbar"></div>' +
+ ' <div id="form_'+ this.name +'_tabs" class="w2ui-form-tabs"></div>' +
+ this.formHTML +
+ '</div>';
+ $(this.box).attr('name', this.name)
+ .addClass('w2ui-reset w2ui-form')
+ .html(html);
+ if ($(this.box).length > 0) $(this.box)[0].style.cssText += this.style;
+
+ // init toolbar regardless it is defined or not
+ if (typeof this.toolbar.render !== 'function') {
+ this.toolbar = $().w2toolbar($.extend({}, this.toolbar, { name: this.name +'_toolbar', owner: this }));
+ this.toolbar.on('click', function (event) {
+ var eventData = obj.trigger({ phase: 'before', type: 'toolbar', target: event.target, originalEvent: event });
+ if (eventData.isCancelled === true) return;
+ // no default action
+ obj.trigger($.extend(eventData, { phase: 'after' }));
+ });
+ }
+ if (typeof this.toolbar == 'object' && typeof this.toolbar.render == 'function') {
+ this.toolbar.render($('#form_'+ this.name +'_toolbar')[0]);
+ }
+ // init tabs regardless it is defined or not
+ if (typeof this.tabs.render !== 'function') {
+ this.tabs = $().w2tabs($.extend({}, this.tabs, { name: this.name +'_tabs', owner: this }));
+ this.tabs.on('click', function (event) {
+ obj.goto(this.get(event.target, true));
+ });
+ }
+ if (typeof this.tabs == 'object' && typeof this.tabs.render == 'function') {
+ this.tabs.render($('#form_'+ this.name +'_tabs')[0]);
+ }
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ // after render actions
+ this.resize();
+ var url = (typeof this.url != 'object' ? this.url : this.url.get);
+ if (url && this.recid != 0) {
+ this.request();
+ } else {
+ this.refresh();
+ }
+ // attach to resize event
+ if ($('.w2ui-layout').length == 0) { // if there is layout, it will send a resize event
+ this.tmp_resize = function (event) { w2ui[obj.name].resize(); }
+ $(window).off('resize', 'body').on('resize', 'body', this.tmp_resize);
+ }
+ setTimeout(function () { obj.resize(); obj.refresh(); }, 150); // need timer because resize is on timer
+ // focus on load
+ function focusEl() {
+ var inputs = $(obj.box).find('input, select, textarea');
+ if (inputs.length > obj.focus) inputs[obj.focus].focus();
+ }
+ if (this.focus >= 0) setTimeout(focusEl, 500); // need timeout to allow form to render
+ return (new Date()).getTime() - time;
+ },
+
+ destroy: function () {
+ // event before
+ var eventData = this.trigger({ phase: 'before', target: this.name, type: 'destroy' });
+ if (eventData.isCancelled === true) return;
+ // clean up
+ if (typeof this.toolbar == 'object' && this.toolbar.destroy) this.toolbar.destroy();
+ if (typeof this.tabs == 'object' && this.tabs.destroy) this.tabs.destroy();
+ if ($(this.box).find('#form_'+ this.name +'_tabs').length > 0) {
+ $(this.box)
+ .removeAttr('name')
+ .removeClass('w2ui-reset w2ui-form')
+ .html('');
+ }
+ delete w2ui[this.name];
+ // event after
+ this.trigger($.extend(eventData, { phase: 'after' }));
+ $(window).off('resize', 'body')
+ }
+ };
+
+ $.extend(w2form.prototype, w2utils.event);
+ w2obj.form = w2form;
+})();
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.min.css b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.min.css
new file mode 100644
index 00000000..c69eecd4
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.min.css
@@ -0,0 +1,2 @@
+/* w2ui 1.4 (c) http://w2ui.com, vitmalina@gmail.com */
+@font-face{font-family:w2ui-font;src:url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAWIAAoAAAAACAgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAAA9AAAAEMAAABWQLxMsmNtYXAAAAE4AAAAOgAAAUriGRC2Z2x5ZgAAAXQAAAH9AAACgLu4vTRoZWFkAAADdAAAADAAAAA2AOYXBGhoZWEAAAOkAAAAIAAAACQD8wHHaG10eAAAA8QAAAAWAAAAIA7dAABsb2NhAAAD3AAAABIAAAASAngBuG1heHAAAAPwAAAAHwAAACABFQA2bmFtZQAABBAAAAEtAAACIsTQ/zJwb3N0AAAFQAAAAEgAAABi4/7ZEHicY2BkvMM4gYGVgYPRhTGNgYHBHUp/ZZBkaGFgYGJgZWbACgLSXFMYHD4yfmRnPPD/AIMe4wEGR6AwI0gOANHZC/IAeJxjYGBgZoBgGQZGBhBwAfIYwXwWBg0gzQakGRmYGBg+sv//D1LwkRFE8zNA1QMBIxvDiAcAddwGvgAAeJxFkLFv01AQxu97LrHdRImjpnaS1gnEia3IoqAktkkiEhaEOiCsDkEo8dyBDkytKpYKVWwssKKKAYmBREKMLJSFoRJ/AGJhY0NZGFgSzrUinvR+d++7T+/ePQLRci4IJ3SFCLIjG8CH+ZPwHHdwkkSS2PMXP3DGmUxpoo123lrt5nj8fjyejsc4W0zwNtl8FfHCKV5QnaOhF3IJUrUbkGPYnSGcGH6risBvGQhdVY0iVXXVkhJN1JL6/6xOIqWk4tRlJiVFiSJFSUrsZ+tkoqpEgt/6Hb/wjtZog2gonBx24AyFJUuNwMvha+/CvLi3vq1f785GsxGuTqfWc5O1N/r2+lNrOl38ZHnWpdUMr/CaLKLGZiHl4hI1+zasGB2/Dy9GSzfRbul4qaWPtHSQ0Y7SWpxmgnSc/mblMKNpmcOVEhfj+5d/8BGfqMl9bKuWFYWKaLf8YIAq9IKc5TY7ojNgTTcC7iEjaN4yPdswbM+81t8UimRLojq66YbdWq0bus375t21bwgcw/H6nmNUyhIkR/DsTasXPgx7VtV8oDx6XIxHE8vl8rMAzqlEDZ7QseUBPP6tLOQKDH5HqooKfLvB2gABa1lgflzI60VxsLd3IJj1YRn5/Wx9Syy++LvArn/JzH4e5WE98TCLer5wnBNb9WcrB5PoH084dg8AAAB4nGNgZGBgAOKMsPib8fw2Xxm4mRhA4PzjbBcY/f////1MjIwHgFwOBrA0AFcuDPF4nGNgZGBgPPD/AIMeEwMDw/9/TEwMQBEUwAEAe34EvHicY2JgYGCCYsbJCJpxO4QNABdTAesAAAAAAAAAEgAsAGgAjgC+AP4BQAAAeJxjYGRgYOBg0GJgZgABJiDmAkIGhv9gPgMADYEBTAB4nG2PTW7CMBCFXyBQFaQKtVKl7qwuuqkIPwsWHAD2LNiH4ARQEkeOQeICPUHP0DP0BF32DD1KX8IoixZbHn/z5o1/AAzwBQ/V8HBbx2q0cMPswm3SQNgnPwl30MezcJf6ULiHV8yE+3hAyBM8vzrtHk64hTu8Cbepvwv75A/hDh7xKdyl/i3cwxo/wn28eLN9ZPJhbHK30skxDW2TN7DWttybXE2CcaMtda5t6PRWbc6qPCVT52IVW5OpBas6TY0qrDnoyAU754r5aBSLHkQmwx4RDHL+Oq53hxU0EhyR8sf2Sv2/smaHRclKlStMEGB8xbekL6+9ITONLb0bnBlLnHjnlKqjW3FZ9mSkhfRqviclKxR17UAloh5gV3cVmGPEGf/xB/Ursl9uDmByAAAAeJxtwUEOgCAMBMAu0sI3SdMEIwKh8n8PXp2hQB+mf5kIAQciGIKEzFpNr6Sj7bs76xruMq3r2eJs22XZtPKIW1laiV6rCBDA") format("woff");font-weight:400;font-style:normal}[class^=w2ui-icon-]:before,[class*=" w2ui-icon-"]:before{font-family:w2ui-font;display:inline-block;vertical-align:middle;line-height:1;font-weight:400;font-style:normal;speak:none;text-decoration:inherit;text-transform:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w2ui-icon-check:before{content:"\f101"}.w2ui-icon-columns:before{content:"\f102"}.w2ui-icon-cross:before{content:"\f103"}.w2ui-icon-pencil:before{content:"\f104"}.w2ui-icon-plus:before{content:"\f105"}.w2ui-icon-reload:before{content:"\f106"}.w2ui-icon-search:before{content:"\f107"}.w2ui-reset{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box;font-family:Verdana,Arial,sans-serif;font-size:11px}.w2ui-reset *{color:default;line-height:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box;margin:0;padding:0}.w2ui-reset table{font-family:Verdana,Arial,sans-serif;font-size:11px;max-width:none;background-color:transparent;border-collapse:separate;border-spacing:0}.w2ui-reset input,.w2ui-reset textarea{width:auto;height:auto;vertical-align:baseline;padding:4px}.w2ui-reset select{padding:1px;height:23px}.w2ui-centered{position:absolute;left:0;right:0;top:50%;-webkit-transform:translateY(-50%);-moz-transform:translateY(-50%);-ms-transform:translateY(-50%);-o-transform:translateY(-50%);transform:translateY(-50%);max-height:100%;margin:0;padding:0 10px;text-align:center}.w2ui-disabled,.w2ui-readonly{background-color:#f1f1f1!important;color:#777!important}input:not([type=button]),select,textarea{padding:4px;border:1px solid #bbb;border-radius:3px;color:#000;background-color:#fff}input:not([type=button]):focus,select:focus,textarea:focus{outline-color:#72b2ff}input:not([type=button]):disabled,select:disabled,textarea:disabled,input:not([type=button])[readonly],select[readonly],textarea[readonly]{background-color:#f1f1f1;color:#777}input::-ms-clear{display:none}input:-ms-input-placeholder{color:#aaa!important}select{padding:2px}input[type=checkbox].w2ui-toggle{position:absolute;opacity:0;width:46px;height:22px;padding:0;margin:0;margin-left:2px}input[type=checkbox].w2ui-toggle+div{display:inline-block;width:46px;height:22px;border:1px solid #bbb;border-radius:30px;background-color:#eee;-webkit-transition-duration:.3s;-webkit-transition-property:background-color,box-shadow;-moz-transition-duration:.3s;-moz-transition-property:background-color,box-shadow;box-shadow:inset 0 0 0 0 rgba(0,0,0,.4);margin-left:2px}input[type=checkbox].w2ui-toggle:disabled+div{opacity:.3}input[type=checkbox].w2ui-toggle+div>div{float:left;width:22px;height:22px;border-radius:inherit;background:#f5f5f5;-webkit-transition-duration:.3s;-webkit-transition-property:transform,background-color,box-shadow;-moz-transition-duration:.3s;-moz-transition-property:transform,background-color;box-shadow:0 0 1px #323232,0 0 0 1px rgba(200,200,200,.6);pointer-events:none;margin-top:-1px;margin-left:-1px}input[type=checkbox].w2ui-toggle:checked+div{border:1px solid #00a23f;box-shadow:inset 0 0 0 12px #54B350}input[type=checkbox].w2ui-toggle:checked+div>div{-webkit-transform:translate3d(24px,0,0);-moz-transform:translate3d(24px,0,0);background-color:#fff;box-shadow:0 2px 5px rgba(0,0,0,.3),0 0 0 1px #00a23f}input[type=checkbox].w2ui-toggle.blue:checked+div{border:1px solid #206FAD;box-shadow:inset 0 0 0 12px #35A6EB}input[type=checkbox].w2ui-toggle.blue:checked+div>div{box-shadow:0 2px 5px rgba(0,0,0,.3),0 0 0 1px #206fad}input[type=checkbox].w2ui-toggle:focus{outline:0}.w2ui-overlay{position:absolute;margin-top:6px;margin-left:-17px;display:none;z-index:1300;color:inherit;background-color:#fbfbfb;border:3px solid #777;box-shadow:0 2px 10px #999;border-radius:4px;text-align:left}.w2ui-overlay table td{color:inherit}.w2ui-overlay:before{content:"";position:absolute;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg);width:12px;height:12px;border:3px solid #777;border-color:inherit;background-color:inherit;border-left:1px solid transparent;border-bottom:1px solid transparent;border-bottom-left-radius:50px;margin:-9px 0 0 30px}.w2ui-overlay:after{display:none;content:"";position:absolute;-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg);transform:rotate(135deg);width:12px;height:12px;border:3px solid #777;border-color:inherit;background-color:inherit;border-left:1px solid transparent;border-bottom:1px solid transparent;border-bottom-left-radius:50px;margin:-7px 0 0 30px}.w2ui-overlay.w2ui-overlay-popup{z-index:1700}.w2ui-tag{position:absolute;z-index:1300;opacity:0;-webkit-transition:opacity .3s;-moz-transition:opacity .3s;-ms-transition:opacity .3s;-o-transition:opacity .3s;transition:opacity .3s}.w2ui-tag .w2ui-tag-body{background-color:rgba(60,60,60,.82);display:inline-block;position:absolute;border-radius:4px;padding:4px 10px;margin-left:10px;margin-top:0;color:#fff!important;box-shadow:1px 1px 3px #000;line-height:100%;font-size:11px;font-family:Verdana,Arial,sans-serif}.w2ui-tag .w2ui-tag-body:before{content:"";position:absolute;width:0;height:0;border-top:5px solid transparent;border-right:5px solid rgba(60,60,60,.82);border-bottom:5px solid transparent;margin:2px 0 0 -15px}.w2ui-tag.w2ui-tag-popup{z-index:1700}.w2ui-overlay table.w2ui-drop-menu{width:100%;color:#000;background-color:#fff;padding:5px 0;cursor:default}.w2ui-overlay table.w2ui-drop-menu td{white-space:nowrap}.w2ui-overlay table.w2ui-drop-menu .w2ui-item-even{color:inherit;background-color:#fff}.w2ui-overlay table.w2ui-drop-menu .w2ui-item-odd{color:inherit;background-color:#f3f6fa}.w2ui-overlay table.w2ui-drop-menu .w2ui-item-group{color:#444;font-weight:700;background-color:#ECEDF0;border-bottom:1px solid #D3D2D4}.w2ui-overlay table.w2ui-drop-menu td.menu-icon{padding:3px 0 4px 6px;width:20px}.w2ui-overlay table.w2ui-drop-menu td.menu-text{padding:8px 10px 8px 5px;width:auto}.w2ui-overlay table.w2ui-drop-menu td.menu-count{text-align:right}.w2ui-overlay table.w2ui-drop-menu td.menu-count>span{border:1px solid #9da4af;border-radius:20px;width:auto;height:18px;padding:2px 7px;margin:3px 5px 0;background-color:#e7f0fc;color:#667274;box-shadow:0 0 2px #fff;text-shadow:1px 1px 1px #e6e6e6}.w2ui-overlay table.w2ui-drop-menu tr:hover{color:inherit;background-color:#e6f0ff}.w2ui-overlay table.w2ui-drop-menu tr.w2ui-selected{background-color:#b6d5fb}.w2ui-overlay table.w2ui-drop-menu tr.w2ui-selected td{color:inherit}.w2ui-overlay table.w2ui-drop-menu tr.w2ui-disabled{opacity:.4;background-color:#fff!important}.w2ui-overlay table.w2ui-drop-menu .w2ui-icon{font-size:14px;color:#8d99a7;display:inline-block;padding-top:4px}.w2ui-marker{color:#444;background-color:rgba(252,244,161,.48)}.w2ui-spinner{display:inline-block;background-size:100%;background-repeat:no-repeat;background-image:url(data:image/gif;base64,R0lGODlhgACAAKIAAP///93d3bu7u5mZmQAA/wAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBQAEACwCAAIAfAB8AAAD/0i63P4wygYqmDjrzbtflvWNZGliYXiubKuloivPLlzReD7al+7/Eh5wSFQIi8hHYBkwHUmD6CD5YTJLz49USuVYraRsZ7vtar7XnQ1Kjpoz6LRHvGlz35O4nEPP2O94EnpNc2sef1OBGIOFMId/inB6jSmPdpGScR19EoiYmZobnBCIiZ95k6KGGp6ni4wvqxilrqBfqo6skLW2YBmjDa28r6Eosp27w8Rov8ekycqoqUHODrTRvXsQwArC2NLF29UM19/LtxO5yJd4Au4CK7DUNxPebG4e7+8n8iv2WmQ66BtoYpo/dvfacBjIkITBE9DGlMvAsOIIZjIUAixliv9ixYZVtLUos5GjwI8gzc3iCGghypQqrbFsme8lwZgLZtIcYfNmTJ34WPTUZw5oRxdD9w0z6iOpO15MgTh1BTTJUKos39jE+o/KS64IFVmsFfYT0aU7capdy7at27dw48qdS7eu3bt480I02vUbX2F/JxYNDImw4GiGE/P9qbhxVpWOI/eFKtlNZbWXuzlmG1mv58+gQ4seTbq06dOoU6vGQZJy0FNlMcV+czhQ7SQmYd8eMhPs5BxVdfcGEtV3buDBXQ+fURxx8oM6MT9P+Fh6dOrH2zavc13u9JXVJb520Vp8dvC76wXMuN5Sepm/1WtkEZHDefnzR9Qvsd9+/wi8+en3X0ntYVcSdAE+UN4zs7ln24CaLagghIxBaGF8kFGoIYV+Ybghh841GIyI5ICIFoklJsigihmimJOLEbLYIYwxSgigiZ+8l2KB+Ml4oo/w8dijjcrouCORKwIpnJIjMnkkksalNeR4fuBIm5UEYImhIlsGCeWNNJphpJdSTlkml1jWeOY6TnaRpppUctcmFW9mGSaZceYopH9zkjnjUe59iR5pdapWaGqHopboaYua1qije67GJ6CuJAAAIfkEBQUABAAsCgACAFcAMAAAA/9Iutz+ML5Ag7w46z0r5WAoSp43nihXVmnrdusrv+s332dt4Tyo9yOBUJD6oQBIQGs4RBlHySSKyczVTtHoidocPUNZaZAr9F5FYbGI3PWdQWn1mi36buLKFJvojsHjLnshdhl4L4IqbxqGh4gahBJ4eY1kiX6LgDN7fBmQEJI4jhieD4yhdJ2KkZk8oiSqEaatqBekDLKztBG2CqBACq4wJRi4PZu1sA2+v8C6EJexrBAD1AOBzsLE0g/V1UvYR9sN3eR6lTLi4+TlY1wz6Qzr8u1t6FkY8vNzZTxaGfn6mAkEGFDgL4LrDDJDyE4hEIbdHB6ESE1iD4oVLfLAqPETIsOODwmCDJlv5MSGJklaS6khAQAh+QQFBQAEACwfAAIAVwAwAAAD/0i63P5LSAGrvTjrNuf+YKh1nWieIumhbFupkivPBEzR+GnnfLj3ooFwwPqdAshAazhEGUXJJIrJ1MGOUamJ2jQ9QVltkCv0XqFh5IncBX01afGYnDqD40u2z76JK/N0bnxweC5sRB9vF34zh4gjg4uMjXobihWTlJUZlw9+fzSHlpGYhTminKSepqebF50NmTyor6qxrLO0L7YLn0ALuhCwCrJAjrUqkrjGrsIkGMW/BMEPJcphLgDaABjUKNEh29vdgTLLIOLpF80s5xrp8ORVONgi8PcZ8zlRJvf40tL8/QPYQ+BAgjgMxkPIQ6E6hgkdjoNIQ+JEijMsasNY0RQix4gKP+YIKXKkwJIFF6JMudFEAgAh+QQFBQAEACw8AAIAQgBCAAAD/kg0PPowykmrna3dzXvNmSeOFqiRaGoyaTuujitv8Gx/661HtSv8gt2jlwIChYtc0XjcEUnMpu4pikpv1I71astytkGh9wJGJk3QrXlcKa+VWjeSPZHP4Rtw+I2OW81DeBZ2fCB+UYCBfWRqiQp0CnqOj4J1jZOQkpOUIYx/m4oxg5cuAaYBO4Qop6c6pKusrDevIrG2rkwptrupXB67vKAbwMHCFcTFxhLIt8oUzLHOE9Cy0hHUrdbX2KjaENzey9Dh08jkz8Tnx83q66bt8PHy8/T19vf4+fr6AP3+/wADAjQmsKDBf6AOKjS4aaHDgZMeSgTQcKLDhBYPEswoA1BBAgAh+QQFBQAEACxOAAoAMABXAAAD7Ei6vPOjyUkrhdDqfXHm4OZ9YSmNpKmiqVqykbuysgvX5o2HcLxzup8oKLQQix0UcqhcVo5ORi+aHFEn02sDeuWqBGCBkbYLh5/NmnldxajX7LbPBK+PH7K6narfO/t+SIBwfINmUYaHf4lghYyOhlqJWgqDlAuAlwyBmpVnnaChoqOkpaanqKmqKgGtrq+wsbA1srW2ry63urasu764Jr/CAb3Du7nGt7TJsqvOz9DR0tPU1TIA2ACl2dyi3N/aneDf4uPklObj6OngWuzt7u/d8fLY9PXr9eFX+vv8+PnYlUsXiqC3c6PmUUgAACH5BAUFAAQALE4AHwAwAFcAAAPpSLrc/m7IAau9bU7MO9GgJ0ZgOI5leoqpumKt+1axPJO1dtO5vuM9yi8TlAyBvSMxqES2mo8cFFKb8kzWqzDL7Xq/4LB4TC6bz1yBes1uu9uzt3zOXtHv8xN+Dx/x/wJ6gHt2g3Rxhm9oi4yNjo+QkZKTCgGWAWaXmmOanZhgnp2goaJdpKGmp55cqqusrZuvsJays6mzn1m4uRAAvgAvuBW/v8GwvcTFxqfIycA3zA/OytCl0tPPO7HD2GLYvt7dYd/ZX99j5+Pi6tPh6+bvXuTuzujxXens9fr7YPn+7egRI9PPHrgpCQAAIfkEBQUABAAsPAA8AEIAQgAAA/lIutz+UI1Jq7026h2x/xUncmD5jehjrlnqSmz8vrE8u7V5z/m5/8CgcEgsGo/IpHLJbDqf0Kh0ShBYBdTXdZsdbb/Yrgb8FUfIYLMDTVYz2G13FV6Wz+lX+x0fdvPzdn9WeoJGAYcBN39EiIiKeEONjTt0kZKHQGyWl4mZdREAoQAcnJhBXBqioqSlT6qqG6WmTK+rsa1NtaGsuEu6o7yXubojsrTEIsa+yMm9SL8osp3PzM2cStDRykfZ2tfUtS/bRd3ewtzV5pLo4eLjQuUp70Hx8t9E9eqO5Oku5/ztdkxi90qPg3x2EMpR6IahGocPCxp8AGtigwQAIfkEBQUABAAsHwBOAFcAMAAAA/9Iutz+MMo36pg4682J/V0ojs1nXmSqSqe5vrDXunEdzq2ta3i+/5DeCUh0CGnF5BGULC4tTeUTFQVONYAs4CfoCkZPjFar83rBx8l4XDObSUL1Ott2d1U4yZwcs5/xSBB7dBMBhgEYfncrTBGDW4WHhomKUY+QEZKSE4qLRY8YmoeUfkmXoaKInJ2fgxmpqqulQKCvqRqsP7WooriVO7u8mhu5NacasMTFMMHCm8qzzM2RvdDRK9PUwxzLKdnaz9y/Kt8SyR3dIuXmtyHpHMcd5+jvWK4i8/TXHff47SLjQvQLkU+fG29rUhQ06IkEG4X/Rryp4mwUxSgLL/7IqFETB8eONT6ChCFy5ItqJomES6kgAQAh+QQFBQAEACwKAE4AVwAwAAAD/0i63A4QuEmrvTi3yLX/4MeNUmieITmibEuppCu3sDrfYG3jPKbHveDktxIaF8TOcZmMLI9NyBPanFKJp4A2IBx4B5lkdqvtfb8+HYpMxp3Pl1qLvXW/vWkli16/3dFxTi58ZRcChwIYf3hWBIRchoiHiotWj5AVkpIXi4xLjxiaiJR/T5ehoomcnZ+EGamqq6VGoK+pGqxCtaiiuJVBu7yaHrk4pxqwxMUzwcKbyrPMzZG90NGDrh/JH8t72dq3IN1jfCHb3L/e5ebh4ukmxyDn6O8g08jt7tf26ybz+m/W9GNXzUQ9fm1Q/APoSWAhhfkMAmpEbRhFKwsvCsmosRIHx444PoKcIXKkjIImjTzjkQAAIfkEBQUABAAsAgA8AEIAQgAAA/VIBNz+8KlJq72Yxs1d/uDVjVxogmQqnaylvkArT7A63/V47/m2/8CgcEgsGo/IpHLJbDqf0Kh0Sj0FroGqDMvVmrjgrDcTBo8v5fCZki6vCW33Oq4+0832O/at3+f7fICBdzsChgJGeoWHhkV0P4yMRG1BkYeOeECWl5hXQ5uNIAOjA1KgiKKko1CnqBmqqk+nIbCkTq20taVNs7m1vKAnurtLvb6wTMbHsUq4wrrFwSzDzcrLtknW16tI2tvERt6pv0fi48jh5h/U6Zs77EXSN/BE8jP09ZFA+PmhP/xvJgAMSGBgQINvEK5ReIZhQ3QEMTBLAAAh+QQFBQAEACwCAB8AMABXAAAD50i6DA4syklre87qTbHn4OaNYSmNqKmiqVqyrcvBsazRpH3jmC7yD98OCBF2iEXjBKmsAJsWHDQKmw571l8my+16v+CweEwum8+hgHrNbrvbtrd8znbR73MVfg838f8BeoB7doN0cYZvaIuMjY6PkJGSk2gClgJml5pjmp2YYJ6dX6GeXaShWaeoVqqlU62ir7CXqbOWrLafsrNctjIDwAMWvC7BwRWtNsbGFKc+y8fNsTrQ0dK3QtXAYtrCYd3eYN3c49/a5NVj5eLn5u3s6e7x8NDo9fbL+Mzy9/T5+tvUzdN3Zp+GBAAh+QQJBQAEACwCAAIAfAB8AAAD/0i63P4wykmrvTjrzbv/YCiOZGmeaKqubOu+cCzPdArcQK2TOL7/nl4PSMwIfcUk5YhUOh3M5nNKiOaoWCuWqt1Ou16l9RpOgsvEMdocXbOZ7nQ7DjzTaeq7zq6P5fszfIASAYUBIYKDDoaGIImKC4ySH3OQEJKYHZWWi5iZG0ecEZ6eHEOio6SfqCaqpaytrpOwJLKztCO2jLi1uoW8Ir6/wCHCxMG2x7muysukzb230M6H09bX2Nna29zd3t/g4cAC5OXm5+jn3Ons7eba7vHt2fL16tj2+QL0+vXw/e7WAUwnrqDBgwgTKlzIsKHDh2gGSBwAccHEixAvaqTYcFCjRoYeNyoM6REhyZIHT4o0qPIjy5YTTcKUmHImx5cwE85cmJPnSYckK66sSAAj0aNIkypdyrSp06dQo0qdSrWq1atYs2rdyrWr169gwxZJAAA7)}.w2ui-icon{background-repeat:no-repeat;height:16px;width:16px;overflow:hidden;margin:2px;display:inline-block}.w2ui-icon.icon-search,.w2ui-icon.icon-search-down{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAgCAYAAAB+ZAqzAAACuElEQVRYw9WXSWhTQRjHR0UKLqhFaV0OUih68GAOWjyJKypCpAoV8aIiioIICiKiB1GMtE3MYmry2moXDz1UDx7sUXHBhQpSaRVxrYpWcMO9avx/8AJh/CbznHkxdeB3Cd/8589kvuUJkWcdjCTHghUgAi6DJ+AVeAqugSQIggniXywcNBJsB70g44EHYBcYXUhTM8EFj4ZkboKqQpiqAv2GprK8o7/f75t6pjn0M3gNPmri3vtycxAZA64qDvkJ2kENqAQTQQWoBg74qth3B4y3NbZDIX4fzNfsnQtuK/YfsjFVCh4pMq3Co0Y5uMVoUGkpy8aFT5xaeSzVEo45bXdBt4LeaLq1k0RXMYJfdDfFmAuAD4zWlty4UNyZEkm19MUb2zMw8Sfp1u+IWSrcIimLnTG8/SijdU6OO5poDESdtgHZVBzUHm/amhW7zoitMTS2mNHqASPk2FDCCcLMYK6p+obmulyxfiYLA4bGKFvfSnrUvkq5+Lpk8z4yRH8r3l/X4WiqJFfspSQ0CGYZGpsMnkt6L+h31Z76hpMdeOwPQ7H0NFnssST0C8wxNDaDKb6kP06150gsHahNNlVzYheZd7HJ0BiX4VRGhpmIhRixKyZilM2M1mnTArtIUbU3/qVO0H0GvmQ4CY4C3YopYYlHjXlggNG4R33Ypi2tVtwaPeTdNMkq9pVQZQdvFPs32zbx4aAjzxhDRfIAWAeWg7VgrzsY5ht/zoNJtubKwA3LITGjSKRyW3NTwaUCmKOSMd3WHH0ZJRQZZkOP1zFKZ3CB++4+aQ6kEeksWAb2a2L7qDv49S1Q6T72MOgEXa6RGFhP3wpS/B6NOWpRs0UxFg7eqTFHjX1hscxtAz/ymEuIYi0cvgF8Y0w5Ro3dZ3M1boJkTaXEUFlug6fsdsRQWzTj0cey+N/Xb2sj5lTh2M6OAAAAAElFTkSuQmCC) no-repeat center!important;background-size:14px 12px!important;opacity:.9}.w2ui-icon.icon-folder{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGrSURBVDjLxZO7ihRBFIa/6u0ZW7GHBUV0UQQTZzd3QdhMQxOfwMRXEANBMNQX0MzAzFAwEzHwARbNFDdwEd31Mj3X7a6uOr9BtzNjYjKBJ6nicP7v3KqcJFaxhBVtZUAK8OHlld2st7Xl3DJPVONP+zEUV4HqL5UDYHr5xvuQAjgl/Qs7TzvOOVAjxjlC+ePSwe6DfbVegLVuT4r14eTr6zvA8xSAoBLzx6pvj4l+DZIezuVkG9fY2H7YRQIMZIBwycmzH1/s3F8AapfIPNF3kQk7+kw9PWBy+IZOdg5Ug3mkAATy/t0usovzGeCUWTjCz0B+Sj0ekfdvkZ3abBv+U4GaCtJ1iEm6ANQJ6fEzrG/engcKw/wXQvEKxSEKQxRGKE7Izt+DSiwBJMUSm71rguMYhQKrBygOIRStf4TiFFRBvbRGKiQLWP29yRSHKBTtfdBmHs0BUpgvtgF4yRFR+NUKi0XZcYjCeCG2smkzLAHkbRBmP0/Uk26O5YnUActBp1GsAI+S5nRJJJal5K1aAMrq0d6Tm9uI6zjyf75dAe6tx/SsWeD//o2/Ab6IH3/h25pOAAAAAElFTkSuQmCC) no-repeat center!important}.w2ui-icon.icon-page{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAINSURBVBgZBcG/r55zGAfg6/4+z3va01NHlYgzEfE7MdCIGISFgS4Gk8ViYyM2Mdlsko4GSf8Do0FLRCIkghhYJA3aVBtEz3nP89wf11VJvPDepdd390+8Nso5nESBQoq0pfvXm9fzWf19453LF85vASqJlz748vInb517dIw6EyYBIIG49u+xi9/c9MdvR//99MPPZ7+4cP4IZhhTPbwzT2d+vGoaVRRp1rRliVvHq+cfvM3TD82+7mun0o/ceO7NT+/4/KOXjwZU1ekk0840bAZzMQ2mooqh0A72d5x/6sB9D5zYnff3PoYBoWBgFKPKqDKqjCpjKr//dcu9p489dra88cydps30KswACfNEKanSaxhlntjJ8Mv12Paie+vZ+0+oeSwwQ0Iw1xAR1CiFNJkGO4wu3ZMY1AAzBI0qSgmCNJsJUEOtJSMaCTBDLyQ0CknAGOgyTyFFiLI2awMzdEcSQgSAAKVUmAeNkxvWJWCGtVlDmgYQ0GFtgg4pNtOwbBcwQy/Rife/2yrRRVI0qYCEBly8Z+P4qMEMy7JaVw72N568e+iwhrXoECQkfH91kY7jwwXMsBx1L93ZruqrK6uuiAIdSnTIKKPLPFcvay8ww/Hh+ufeznTXu49v95IMoQG3784gYXdTqvRmqn/Wpa/ADFX58MW3L71SVU9ETgEIQQQIOOzub+fhIvwPRDgeVjWDahIAAAAASUVORK5CYII=) no-repeat center!important}.w2ui-lock{display:none;position:absolute;z-index:1400;top:0;left:0;width:100%;height:100%;opacity:.15;filter:alpha(opacity=15);background-color:#333}.w2ui-lock-msg{display:none;position:absolute;z-index:1400;top:45%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%);-moz-transform:translateX(-50%) translateY(-50%);-ms-transform:translateX(-50%) translateY(-50%);-o-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);width:200px;height:80px;padding:30px 8px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;font-size:13px;font-family:Verdana,Arial,sans-serif;opacity:.8;filter:alpha(opacity=80);background-color:#555;color:#fff;text-align:center;border-radius:5px;border:2px solid #444}.w2ui-lock-msg .w2ui-spinner{display:inline-block;width:24px;height:24px;margin:-3px 8px -7px -10px}button.btn{display:inline-block;border-radius:4px;margin:0 5px;padding:7px 12px 6px!important;color:#666;font-size:12px!important;border:1px solid #B6B6B6;background-image:-webkit-linear-gradient(#fff 0,#e7e7e7 100%);background-image:-moz-linear-gradient(#fff 0,#e7e7e7 100%);background-image:-ms-linear-gradient(#fff 0,#e7e7e7 100%);background-image:-o-linear-gradient(#fff 0,#e7e7e7 100%);background-image:linear-gradient(#fff 0,#e7e7e7 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffe7e7e7', endColorstr='#ffffffff', GradientType=0);outline:0;box-shadow:0 1px 0 #fff;cursor:default;min-width:75px;line-height:100%!important;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}button.btn:hover{text-decoration:none;border:1px solid #bbb;background-image:-webkit-linear-gradient(#f7f7f7 0,#ddd 100%);background-image:-moz-linear-gradient(#f7f7f7 0,#ddd 100%);background-image:-ms-linear-gradient(#f7f7f7 0,#ddd 100%);background-image:-o-linear-gradient(#f7f7f7 0,#ddd 100%);background-image:linear-gradient(#f7f7f7 0,#ddd 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffdddddd', endColorstr='#fff7f7f7', GradientType=0);color:#333}button.btn:active,button.btn.clicked{border:1px solid #999;background-image:-webkit-linear-gradient(#ccc 0,#ccc 100%);background-image:-moz-linear-gradient(#ccc 0,#ccc 100%);background-image:-ms-linear-gradient(#ccc 0,#ccc 100%);background-image:-o-linear-gradient(#ccc 0,#ccc 100%);background-image:linear-gradient(#ccc 0,#ccc 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffcccccc', endColorstr='#ffcccccc', GradientType=0);text-shadow:1px 1px 1px #eee}button.btn:disabled{border:1px solid #bbb!important;background:#f7f7f7!important;color:#bdbcbc!important;text-shadow:none!important}button.btn-blue{color:#fff;background-image:-webkit-linear-gradient(#80c0f7 0,#269df0 100%);background-image:-moz-linear-gradient(#80c0f7 0,#269df0 100%);background-image:-ms-linear-gradient(#80c0f7 0,#269df0 100%);background-image:-o-linear-gradient(#80c0f7 0,#269df0 100%);background-image:linear-gradient(#80c0f7 0,#269df0 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff269df0', endColorstr='#ff80c0f7', GradientType=0);border:1px solid #538AB7;text-shadow:1px 1px 1px #777}button.btn-blue:hover{color:#fff;background-image:-webkit-linear-gradient(#73b6f0 0,#2391dd 100%);background-image:-moz-linear-gradient(#73b6f0 0,#2391dd 100%);background-image:-ms-linear-gradient(#73b6f0 0,#2391dd 100%);background-image:-o-linear-gradient(#73b6f0 0,#2391dd 100%);background-image:linear-gradient(#73b6f0 0,#2391dd 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff2391dd', endColorstr='#ff73b6f0', GradientType=0);border:1px solid #497BA3;text-shadow:1px 1px 1px #777}button.btn-blue:active,button.btn-blue.clicked{color:#fff;background-image:-webkit-linear-gradient(#1e83c9 0,#1e83c9 100%);background-image:-moz-linear-gradient(#1e83c9 0,#1e83c9 100%);background-image:-ms-linear-gradient(#1e83c9 0,#1e83c9 100%);background-image:-o-linear-gradient(#1e83c9 0,#1e83c9 100%);background-image:linear-gradient(#1e83c9 0,#1e83c9 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff1e83c9', endColorstr='#ff1e83c9', GradientType=0);border:1px solid #1268A6;text-shadow:1px 1px 1px #777}button.btn-green{color:#fff;background-image:-webkit-linear-gradient(#81cf81 0,#52a452 100%);background-image:-moz-linear-gradient(#81cf81 0,#52a452 100%);background-image:-ms-linear-gradient(#81cf81 0,#52a452 100%);background-image:-o-linear-gradient(#81cf81 0,#52a452 100%);background-image:linear-gradient(#81cf81 0,#52a452 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff52a452', endColorstr='#ff81cf81', GradientType=0);border:1px solid #479247;text-shadow:1px 1px 1px #777}button.btn-green:hover{color:#fff;background-image:-webkit-linear-gradient(#6abe68 0,#3f8f3d 100%);background-image:-moz-linear-gradient(#6abe68 0,#3f8f3d 100%);background-image:-ms-linear-gradient(#6abe68 0,#3f8f3d 100%);background-image:-o-linear-gradient(#6abe68 0,#3f8f3d 100%);background-image:linear-gradient(#6abe68 0,#3f8f3d 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff3f8f3d', endColorstr='#ff6abe68', GradientType=0);border:1px solid #479247;text-shadow:1px 1px 1px #777}button.btn-green:active,button.btn-green.clicked{color:#fff;background-image:-webkit-linear-gradient(#377d36 0,#377d36 100%);background-image:-moz-linear-gradient(#377d36 0,#377d36 100%);background-image:-ms-linear-gradient(#377d36 0,#377d36 100%);background-image:-o-linear-gradient(#377d36 0,#377d36 100%);background-image:linear-gradient(#377d36 0,#377d36 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff377d36', endColorstr='#ff377d36', GradientType=0);border:1px solid #555!important;text-shadow:1px 1px 1px #777}button.btn-orange{color:#fff;background-image:-webkit-linear-gradient(#fcc272 0,#fb8822 100%);background-image:-moz-linear-gradient(#fcc272 0,#fb8822 100%);background-image:-ms-linear-gradient(#fcc272 0,#fb8822 100%);background-image:-o-linear-gradient(#fcc272 0,#fb8822 100%);background-image:linear-gradient(#fcc272 0,#fb8822 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fffb8822', endColorstr='#fffcc272', GradientType=0);border:1px solid #B68B4C;text-shadow:1px 1px 1px #777}button.btn-orange:hover{color:#fff;background-image:-webkit-linear-gradient(#f4ad59 0,#f1731f 100%);background-image:-moz-linear-gradient(#f4ad59 0,#f1731f 100%);background-image:-ms-linear-gradient(#f4ad59 0,#f1731f 100%);background-image:-o-linear-gradient(#f4ad59 0,#f1731f 100%);background-image:linear-gradient(#f4ad59 0,#f1731f 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fff1731f', endColorstr='#fff4ad59', GradientType=0);border:1px solid #B68B4C;text-shadow:1px 1px 1px #777}button.btn-orange:active,button.btn-orange.clicked{color:#fff;border:1px solid #666;background-image:-webkit-linear-gradient(#b98747 0,#b98747 100%);background-image:-moz-linear-gradient(#b98747 0,#b98747 100%);background-image:-ms-linear-gradient(#b98747 0,#b98747 100%);background-image:-o-linear-gradient(#b98747 0,#b98747 100%);background-image:linear-gradient(#b98747 0,#b98747 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffb98747', endColorstr='#ffb98747', GradientType=0);text-shadow:1px 1px 1px #777}button.btn-red{color:#fff;background-image:-webkit-linear-gradient(#ff6e70 0,#c72d2d 100%);background-image:-moz-linear-gradient(#ff6e70 0,#c72d2d 100%);background-image:-ms-linear-gradient(#ff6e70 0,#c72d2d 100%);background-image:-o-linear-gradient(#ff6e70 0,#c72d2d 100%);background-image:linear-gradient(#ff6e70 0,#c72d2d 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffc72d2d', endColorstr='#ffff6e70', GradientType=0);border:1px solid #BB3C3E;text-shadow:1px 1px 1px #777}button.btn-red:hover{color:#fff;background-image:-webkit-linear-gradient(#ee696c 0,#ae2527 100%);background-image:-moz-linear-gradient(#ee696c 0,#ae2527 100%);background-image:-ms-linear-gradient(#ee696c 0,#ae2527 100%);background-image:-o-linear-gradient(#ee696c 0,#ae2527 100%);background-image:linear-gradient(#ee696c 0,#ae2527 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffae2527', endColorstr='#ffee696c', GradientType=0);border:1px solid #BB3C3E;text-shadow:1px 1px 1px #777}button.btn-red:active,button.btn-red.clicked{color:#fff;border:1px solid #861C1E;background-image:-webkit-linear-gradient(#9c2123 0,#9c2123 100%);background-image:-moz-linear-gradient(#9c2123 0,#9c2123 100%);background-image:-ms-linear-gradient(#9c2123 0,#9c2123 100%);background-image:-o-linear-gradient(#9c2123 0,#9c2123 100%);background-image:linear-gradient(#9c2123 0,#9c2123 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff9c2123', endColorstr='#ff9c2123', GradientType=0);text-shadow:1px 1px 1px #777}.w2ui-form{position:relative;color:#000;background-color:#f5f6f7;border:1px solid silver;border-radius:3px;padding:0;overflow:hidden!important}.w2ui-form>div{position:absolute;overflow:hidden}.w2ui-form .w2ui-form-header{position:absolute;left:0;right:0;border-bottom:1px solid #99bbe8!important;overflow:hidden;color:#444;font-size:13px;text-align:center;padding:8px;background-image:-webkit-linear-gradient(#dae6f3,#c2d5ed);background-image:-moz-linear-gradient(#dae6f3,#c2d5ed);background-image:-ms-linear-gradient(#dae6f3,#c2d5ed);background-image:-o-linear-gradient(#dae6f3,#c2d5ed);background-image:linear-gradient(#dae6f3,#c2d5ed);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffdae6f3', endColorstr='#ffc2d5ed', GradientType=0);border-top-left-radius:3px;border-top-right-radius:3px}.w2ui-form .w2ui-form-toolbar{position:absolute;left:0;right:0;margin:0;padding:6px 3px;border-bottom:1px solid #d5d8d8}.w2ui-form .w2ui-form-tabs{margin:0;padding:0}.w2ui-form .w2ui-tabs{position:absolute;left:0;right:0;border-top-left-radius:3px;border-top-right-radius:3px;padding-top:5px!important;background-color:#fafafa}.w2ui-form .w2ui-tabs .w2ui-tab.active{background-color:#f5f6f7}.w2ui-form .w2ui-page{position:absolute;left:0;right:0;overflow:auto;padding:10px;border-left:1px solid inherit;border-right:1px solid inherit;background-color:inherit;border-radius:3px}.w2ui-form .w2ui-buttons{position:absolute;left:0;right:0;bottom:0;text-align:center;border-top:1px solid #d5d8d8;border-bottom:0 solid #d5d8d8;background-color:#fafafa;padding:15px 0!important;border-bottom-left-radius:3px;border-bottom-right-radius:3px}.w2ui-form .w2ui-buttons input[type=button],.w2ui-form .w2ui-buttons button{min-width:80px;margin-right:5px}.w2ui-form input[type=checkbox],.w2ui-form input[type=radio]{margin-top:4px;margin-bottom:4px}.w2ui-form input[type=checkbox].w2ui-toggle{margin:0}.w2ui-group-title{padding:5px 2px;color:#8D96A2;text-shadow:1px 1px 2px #fdfdfd;font-size:120%}.w2ui-group{background-color:#ebecef;margin:5px 0 10px;padding:10px 5px;border-top:1px solid #cedcea;border-bottom:1px solid #cedcea}.w2ui-field>label{display:block;float:left;margin-top:7px;margin-bottom:3px;width:120px;padding:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:right;min-height:20px;color:#666}.w2ui-field>div{margin-bottom:3px;margin-left:128px;padding:3px;min-height:28px;float:none}.w2ui-field.w2ui-required>div{position:relative}.w2ui-field.w2ui-required>div::before{content:'*';position:absolute;margin-top:5px;margin-left:-9px;color:red}.w2ui-field.w2ui-span1>label{width:20px}.w2ui-field.w2ui-span1>div{margin-left:28px}.w2ui-field.w2ui-span2>label{width:40px}.w2ui-field.w2ui-span2>div{margin-left:48px}.w2ui-field.w2ui-span3>label{width:60px}.w2ui-field.w2ui-span3>div{margin-left:68px}.w2ui-field.w2ui-span4>label{width:80px}.w2ui-field.w2ui-span4>div{margin-left:88px}.w2ui-field.w2ui-span5>label{width:100px}.w2ui-field.w2ui-span5>div{margin-left:108px}.w2ui-field.w2ui-span6>label{width:120px}.w2ui-field.w2ui-span6>div{margin-left:128px}.w2ui-field.w2ui-span7>label{width:140px}.w2ui-field.w2ui-span7>div{margin-left:148px}.w2ui-field.w2ui-span8>label{width:160px}.w2ui-field.w2ui-span8>div{margin-left:168px}.w2ui-field.w2ui-span9>label{width:180px}.w2ui-field.w2ui-span9>div{margin-left:188px}.w2ui-field.w2ui-span10>label{width:200px}.w2ui-field.w2ui-span10>div{margin-left:208px}.w2ui-error{border:1px solid #ffa8a8!important;background-color:#fff4eb!important}.w2field{padding:3px;border-radius:3px;border:1px solid silver}.w2ui-field-helper{position:absolute;display:inline-block;line-height:100%;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none}.w2ui-field-helper .w2ui-field-up{position:absolute;top:0;padding:2px 3px}.w2ui-field-helper .w2ui-field-down{position:absolute;bottom:0;padding:2px 3px}.w2ui-field-helper .arrow-up:hover{border-bottom-color:#81C6FF}.w2ui-field-helper .arrow-down:hover{border-top-color:#81C6FF}.arrow-up{background:0 0;width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:5px solid #777;font-size:0;line-height:0}.arrow-down{background:0 0;width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid #777;font-size:0;line-height:0}.arrow-left{background:0 0;width:0;height:0;border-bottom:4px solid transparent;border-top:4px solid transparent;border-right:5px solid #777;font-size:0;line-height:0}.arrow-right{background:0 0;width:0;height:0;border-bottom:4px solid transparent;border-top:4px solid transparent;border-left:5px solid #777;font-size:0;line-height:0}.w2ui-color{padding:5px;padding-top:8px;background-color:#fff;border-radius:3px}.w2ui-color>table{table-layout:fixed;width:160px}.w2ui-color>table td{width:20px;height:20px;text-align:center}.w2ui-color>table td div{cursor:pointer;display:inline-block;width:16px;height:17px;padding:1px 4px;border:1px solid transparent;color:#fff;text-shadow:0 0 2px #000}.w2ui-color>table td div:hover{outline:1px solid #666;border:1px solid #fff}.w2ui-calendar{margin:0;padding:1px;line-height:108%}.w2ui-calendar .w2ui-calendar-title{margin:0 -1px;padding:7px 2px;background-image:-webkit-linear-gradient(#f6f6f6,#d9d9d9);background-image:-moz-linear-gradient(#f6f6f6,#d9d9d9);background-image:-ms-linear-gradient(#f6f6f6,#d9d9d9);background-image:-o-linear-gradient(#f6f6f6,#d9d9d9);background-image:linear-gradient(#f6f6f6,#d9d9d9);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fff6f6f6', endColorstr='#ffd9d9d9', GradientType=0);border-bottom:1px solid #bbb;color:#555;text-align:center;text-shadow:1px 1px 1px #eee;cursor:pointer}.w2ui-calendar .w2ui-calendar-jump{position:absolute;top:27px;left:0;right:0;bottom:0;background-color:#FaFaFa}.w2ui-calendar .w2ui-calendar-jump>:first-child{position:absolute;top:0;left:0;bottom:0;width:110px;overflow:hidden;padding-top:5px;border-right:1px solid silver}.w2ui-calendar .w2ui-calendar-jump>:last-child{position:absolute;top:0;right:0;bottom:0;width:88px;overflow-x:hidden;overflow-y:auto;padding-top:5px;text-align:center}.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-month,.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-year{display:inline-block;padding:5px 0;text-align:center;float:left;margin:2px;width:50px;cursor:default;border:1px solid transparent;border-radius:2px}.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-year{float:none;width:95%}.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-month:hover,.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-year:hover{border:1px solid #ccc;color:#000;background-color:#efefef}.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-month.selected,.w2ui-calendar .w2ui-calendar-jump .w2ui-jump-year.selected{border:1px solid #ccc;color:#000;background-color:#dadada}.w2ui-calendar .w2ui-calendar-previous,.w2ui-calendar .w2ui-calendar-next{width:24px;height:20px;color:#666;border:1px solid transparent;border-radius:3px;padding:2px 3px 1px 2px;margin:-4px 0 0 0;cursor:default}.w2ui-calendar .w2ui-calendar-previous:hover,.w2ui-calendar .w2ui-calendar-next:hover{border:1px solid silver;background-color:#efefef}.w2ui-calendar .w2ui-calendar-previous>div,.w2ui-calendar .w2ui-calendar-next>div{position:absolute;border-left:4px solid #888;border-top:4px solid #888;border-right:4px solid transparent;border-bottom:4px solid transparent;width:0;height:0;padding:0;margin:3px 0 0}.w2ui-calendar .w2ui-calendar-previous{float:left}.w2ui-calendar .w2ui-calendar-previous>div{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg);margin-left:6px}.w2ui-calendar .w2ui-calendar-next{float:right}.w2ui-calendar .w2ui-calendar-next>div{-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg);transform:rotate(135deg);margin-left:2px;margin-right:2px}.w2ui-calendar table.w2ui-calendar-days{padding:0}.w2ui-calendar table.w2ui-calendar-days td{border:1px solid #fff;color:#000;background-color:#f9f9f9;padding:6px;cursor:default;text-align:right}.w2ui-calendar table.w2ui-calendar-days td.w2ui-saturday,.w2ui-calendar table.w2ui-calendar-days td.w2ui-sunday{border:1px solid #fff;color:#c8493b;background-color:#f9f9f9}.w2ui-calendar table.w2ui-calendar-days td.w2ui-saturday:hover,.w2ui-calendar table.w2ui-calendar-days td.w2ui-sunday:hover{border:1px solid #ccc;color:#000;background-color:#e9e9e9}.w2ui-calendar table.w2ui-calendar-days td.w2ui-saturday.w2ui-blocked,.w2ui-calendar table.w2ui-calendar-days td.w2ui-sunday.w2ui-blocked{text-decoration:line-through;border:1px solid #fff;color:#ccc;background-color:#fff}.w2ui-calendar table.w2ui-calendar-days td.w2ui-today{border:1px solid #8cb067;color:#000;background-color:#e2f7cd}.w2ui-calendar table.w2ui-calendar-days td:hover{border:1px solid #ccc;color:#000;background-color:#e9e9e9}.w2ui-calendar table.w2ui-calendar-days td.w2ui-blocked{text-decoration:line-through;border:1px solid #fff;color:#ccc;background-color:#fff}.w2ui-calendar table.w2ui-calendar-days td.w2ui-day-empty{border:1px solid #fff;background-color:#fdfdfd}.w2ui-calendar table.w2ui-calendar-days tr.w2ui-day-title td{border:1px solid #fff;color:gray;background-color:#fff;text-align:center;padding:6px}.w2ui-calendar-time{padding:5px;cursor:default}.w2ui-calendar-time td div{padding:7px 10px;text-align:center;border:1px solid transparent;white-space:nowrap}.w2ui-calendar-time td:nth-child(even){background-color:#f6f6f6}.w2ui-calendar-time td div:hover{border:1px solid #ccc;color:#000;background-color:#e9e9e9}.w2ui-calendar-time td div.w2ui-blocked{text-decoration:line-through;border:1px solid #fff;color:#ccc;background-color:#fff}.w2ui-select{cursor:default;color:#000!important;background-image:-webkit-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:-moz-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:-ms-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:-o-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%)}.w2ui-list{color:inherit;position:absolute;padding:0;margin:0;min-height:25px;overflow:auto;border:1px solid silver;border-radius:3px;font-size:6px;line-height:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box;background-color:#fff}.w2ui-list input[type=text]{-webkit-box-shadow:none;-moz-box-shadow:none;-ms-box-shadow:none;-o-box-shadow:none;box-shadow:none}.w2ui-list ul{list-style-type:none;background-color:#000;margin:0;padding:0}.w2ui-list ul li{float:left;margin:2px 1px 0 2px;border-radius:3px;width:auto;padding:3px 10px 1px 7px;border:1px solid #88b0d6;background-color:#eff3f5;white-space:nowrap;cursor:default;font-family:verdana;font-size:11px;line-height:100%;height:20px;overflow:hidden;text-overflow:ellipsis;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.w2ui-list ul li:hover{background-color:#d0dbe1}.w2ui-list ul li:last-child{border-radius:0;border:1px solid transparent;background-color:transparent}.w2ui-list ul li:last-child input{padding:1px;padding-top:0;margin:0;border:0;outline:0;height:auto;line-height:100%;font-size:inherit;font-family:inherit;background-color:transparent}.w2ui-list ul li .w2ui-list-remove{float:right;width:15px;height:14px;margin:-1px -9px 0 3px;border-radius:15px}.w2ui-list ul li .w2ui-list-remove:hover{background-color:#D77F7F;color:#fff}.w2ui-list ul li .w2ui-list-remove:before{position:relative;top:0;padding:0;margin:0;left:5px;color:inherit;opacity:.7;text-shadow:inherit;font-size:inherit;font-variant:small-caps;content:'x';line-height:100%}.w2ui-list ul li>span.file-size{pointer-events:none;color:#777}.w2ui-list .w2ui-enum-placeholder{display:inline;position:absolute;pointer-events:none;color:#999;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.w2ui-list.w2ui-file-dragover{background-color:#E4FFDA;border:1px solid #93E07D}.w2ui-layout{overflow:hidden!important;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.w2ui-layout *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.w2ui-layout>div{position:absolute;overflow:hidden;border:0;margin:0;padding:0;outline:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.w2ui-layout>div .w2ui-panel{display:none;position:absolute;z-index:120}.w2ui-layout>div .w2ui-panel .w2ui-panel-title{padding:5px;background-image:-webkit-linear-gradient(#dae6f3,#c2d5ed);background-image:-moz-linear-gradient(#dae6f3,#c2d5ed);background-image:-ms-linear-gradient(#dae6f3,#c2d5ed);background-image:-o-linear-gradient(#dae6f3,#c2d5ed);background-image:linear-gradient(#dae6f3,#c2d5ed);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffdae6f3', endColorstr='#ffc2d5ed', GradientType=0);border:1px solid #b9cee9;border-bottom:1px solid #99bbe8}.w2ui-layout>div .w2ui-panel .w2ui-panel-tabs{position:absolute;left:0;top:0;right:0;z-index:2;display:none;overflow:hidden;background-color:#fafafa;padding:4px 0}.w2ui-layout>div .w2ui-panel .w2ui-panel-tabs>.w2ui-tab.active{background-color:#f5f6f7}.w2ui-layout>div .w2ui-panel .w2ui-panel-toolbar{position:absolute;left:0;top:0;right:0;z-index:2;display:none;overflow:hidden;background-color:#fafafa;border-bottom:1px solid silver;padding:4px}.w2ui-layout>div .w2ui-panel .w2ui-panel-content{position:absolute;left:0;top:0;right:0;bottom:0;z-index:1;color:inherit;background-color:#f5f6f7}.w2ui-layout>div .w2ui-resizer{display:none;position:absolute;z-index:121;background-color:transparent}.w2ui-layout>div .w2ui-resizer:hover,.w2ui-layout>div .w2ui-resizer.active{background-color:#d7e4f2}.w2ui-grid{position:relative;border:1px solid silver;border-radius:2px;overflow:hidden!important}.w2ui-grid>div{position:absolute;overflow:hidden}.w2ui-grid .w2ui-grid-header{position:absolute;border-bottom:1px solid #99bbe8!important;height:28px;overflow:hidden;color:#444;font-size:13px;text-align:center;padding:7px;background-image:-webkit-linear-gradient(#dae6f3,#c2d5ed);background-image:-moz-linear-gradient(#dae6f3,#c2d5ed);background-image:-ms-linear-gradient(#dae6f3,#c2d5ed);background-image:-o-linear-gradient(#dae6f3,#c2d5ed);background-image:linear-gradient(#dae6f3,#c2d5ed);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffdae6f3', endColorstr='#ffc2d5ed', GradientType=0);border-top-left-radius:2px;border-top-right-radius:2px}.w2ui-grid .w2ui-grid-toolbar{position:absolute;border-bottom:1px solid silver;background-color:#eaeaea;height:38px;padding:7px 3px 4px;margin:0;box-shadow:0 1px 2px #ddd}.w2ui-grid .w2ui-toolbar-search{width:160px;margin-right:3px}.w2ui-grid .w2ui-toolbar-search .w2ui-search-all{outline:0!important;width:160px;border-radius:10px;line-height:normal;height:22px;border:1px solid #b9b9b9;color:#000;background-color:#fff;padding:3px 18px 3px 23px;margin:0}.w2ui-grid .w2ui-toolbar-search .w2ui-search-down{position:absolute;margin-top:-7px;margin-left:6px}.w2ui-grid .w2ui-toolbar-search .w2ui-search-clear{position:absolute;width:16px;height:16px;margin-top:-8px;margin-left:-20px;border-radius:15px;cursor:default}.w2ui-grid .w2ui-toolbar-search .w2ui-search-clear:hover{background-color:#D77F7F;color:#fff}.w2ui-grid .w2ui-toolbar-search .w2ui-search-clear:before{position:relative;top:1px;left:5px;opacity:.6;color:inherit;text-shadow:inherit;content:'x';cursor:default}.w2ui-grid .w2ui-grid-body{position:absolute;overflow:hidden;padding:0;background-color:#fff;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.w2ui-grid .w2ui-grid-body input,.w2ui-grid .w2ui-grid-body select,.w2ui-grid .w2ui-grid-body textarea{user-select:text;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;-o-user-select:text}.w2ui-grid .w2ui-grid-body .w2ui-grid-columns{overflow:hidden;position:absolute;left:0;top:0;right:0;box-shadow:0 1px 4px #ddd;height:auto}.w2ui-grid .w2ui-grid-body .w2ui-grid-columns table{height:auto}.w2ui-grid .w2ui-grid-body .w2ui-grid-columns .w2ui-resizer{position:absolute;z-index:1000;display:block;background-image:none;background-color:rgba(0,0,0,0);padding:0;margin:0;width:6px;height:12px;cursor:col-resize}.w2ui-grid .w2ui-grid-body .w2ui-grid-records{position:absolute;left:0;right:0;top:0;bottom:0}.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-odd{color:inherit;background-color:#fff}.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-odd:hover{color:inherit;background-color:#e6f0ff}.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-odd.w2ui-empty-record:hover{background-color:#fff}.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-even{color:inherit;background-color:#f3f6fa}.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-even:hover{color:inherit;background-color:#e6f0ff}.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-even.w2ui-empty-record:hover{background-color:#f3f6fa}.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr.w2ui-selected,.w2ui-grid .w2ui-grid-body .w2ui-grid-records table tr td.w2ui-selected{color:#000!important;background-color:#b6d5ff!important}.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-expanded{background-color:#CCDCF0!important}.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-expanded1{height:0;border-bottom:1px solid #b2bac0;background-color:#CCDCF0}.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-expanded1>div{height:100%;margin:0;padding:0}.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-expanded2{height:0;border-radius:0;border-bottom:1px solid #b2bac0}.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-expanded2>div{height:0;border:0;transition:height .3s,opacity .3s}.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-load-more{border-top:1px solid #d6d5d7;cursor:pointer}.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-load-more>div{text-align:center;color:#777;background-color:rgba(233,237,243,.5);padding:10px 0 15px;border-top:1px solid #fff}.w2ui-grid .w2ui-grid-body .w2ui-grid-records .w2ui-load-more>div:hover{color:inherit;background-color:#e6f0ff}.w2ui-grid .w2ui-grid-body table{border-spacing:0;border-collapse:collapse;table-layout:fixed;width:1px}.w2ui-grid .w2ui-grid-body table .w2ui-head{margin:0;padding:0;border-right:1px solid #c5c5c5;border-bottom:1px solid #c5c5c5;color:#000;background-image:-webkit-linear-gradient(#f9f9f9,#e4e4e4);background-image:-moz-linear-gradient(#f9f9f9,#e4e4e4);background-image:-ms-linear-gradient(#f9f9f9,#e4e4e4);background-image:-o-linear-gradient(#f9f9f9,#e4e4e4);background-image:linear-gradient(#f9f9f9,#e4e4e4);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#ffe4e4e4', GradientType=0)}.w2ui-grid .w2ui-grid-body table .w2ui-head>div{padding:7px 3px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;position:relative}.w2ui-grid .w2ui-grid-body table .w2ui-head.w2ui-col-intersection{border-right-color:#72b2ff}.w2ui-grid .w2ui-grid-body table .w2ui-head.w2ui-reorder-cols-head:hover{cursor:move}.w2ui-grid .w2ui-grid-body table .w2ui-head .col-intersection-marker{padding:0;position:absolute;height:100%;top:0}.w2ui-grid .w2ui-grid-body table .w2ui-head .col-intersection-marker.left{left:0;margin-left:-5px}.w2ui-grid .w2ui-grid-body table .w2ui-head .col-intersection-marker.right{right:0;margin-right:-5px}.w2ui-grid .w2ui-grid-body table .w2ui-head .col-intersection-marker .top-marker{position:absolute;top:0;height:0;width:0;border-top:5px solid #72b2ff;border-left:5px solid transparent;border-right:5px solid transparent}.w2ui-grid .w2ui-grid-body table .w2ui-head .col-intersection-marker .bottom-marker{position:absolute;bottom:0;height:0;width:0;border-bottom:5px solid #72b2ff;border-left:5px solid transparent;border-right:5px solid transparent}.w2ui-grid .w2ui-grid-body table td{border-right:1px solid #d6d5d7;border-bottom:0 solid #d6d5d7;cursor:default;overflow:hidden}.w2ui-grid .w2ui-grid-body table td.w2ui-grid-data{margin:0;padding:0}.w2ui-grid .w2ui-grid-body table td.w2ui-grid-data>div{padding:3px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.w2ui-grid .w2ui-grid-body table td.w2ui-grid-data>div.flexible-record{height:auto;overflow:visible;white-space:normal}.w2ui-grid .w2ui-grid-body table td:last-child{border-right:0}.w2ui-grid .w2ui-grid-body table .w2ui-col-number{width:34px;color:#777;background-color:rgba(233,237,243,.5)}.w2ui-grid .w2ui-grid-body table .w2ui-col-number div{padding:0 7px 0 3px;text-align:right}.w2ui-grid .w2ui-grid-body table .w2ui-col-select{width:26px}.w2ui-grid .w2ui-grid-body table .w2ui-col-select div{padding:0;text-align:center;overflow:hidden}.w2ui-grid .w2ui-grid-body table .w2ui-col-select div input[type=checkbox]{margin-top:2px;position:relative}.w2ui-grid .w2ui-grid-body table .w2ui-col-expand{width:26px}.w2ui-grid .w2ui-grid-body table .w2ui-col-expand div{padding:0;text-align:center;font-weight:700}.w2ui-grid .w2ui-grid-body div.w2ui-col-header{height:auto!important;width:100%;overflow:hidden;padding-right:10px!important}.w2ui-grid .w2ui-grid-body div.w2ui-col-header>div.w2ui-sort-up{border:4px solid transparent;border-bottom:5px solid #8D99A7;margin-top:-2px;margin-right:-7px;float:right}.w2ui-grid .w2ui-grid-body div.w2ui-col-header>div.w2ui-sort-down{border:4px solid transparent;border-top:5px solid #8D99A7;margin-top:2px;margin-right:-7px;float:right}.w2ui-grid .w2ui-grid-body .w2ui-col-group{text-align:center}.w2ui-grid .w2ui-changed{background:url(data:image/gif;base64,R0lGODlhCgAKAJEAALAABf///wAAAAAAACH5BAEAAAIALAAAAAAKAAoAAAIPlI8Hy8mbxIsSUnup3rQAADs=) no-repeat top right}.w2ui-grid .w2ui-editable{overflow:hidden;height:100%!important;margin:0!important;padding:0!important}.w2ui-grid .w2ui-editable input{border:0;border-radius:0;margin:0;padding:4px 3px;width:100%;height:100%}.w2ui-grid .w2ui-editable input.w2ui-select{outline:0!important;background:#fff}.w2ui-grid .w2ui-grid-summary{position:absolute;box-shadow:0 -1px 4px #aaa}.w2ui-grid .w2ui-grid-summary table{color:inherit}.w2ui-grid .w2ui-grid-summary table .w2ui-odd{background-color:#eef5eb}.w2ui-grid .w2ui-grid-summary table .w2ui-even{background-color:#f8fff5}.w2ui-grid .w2ui-grid-footer{position:absolute;margin:0;padding:0;text-align:center;height:24px;overflow:hidden;user-select:text;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;-o-user-select:text;box-shadow:0 -1px 4px #eee;color:#444;background-color:#f8f8f8;border-top:1px solid #ddd;border-bottom-left-radius:2px;border-bottom-right-radius:2px}.w2ui-grid .w2ui-grid-footer .w2ui-footer-left{float:left;padding-top:5px;padding-left:5px}.w2ui-grid .w2ui-grid-footer .w2ui-footer-right{float:right;padding-top:5px;padding-right:5px}.w2ui-grid .w2ui-grid-footer .w2ui-footer-center{padding:2px;text-align:center}.w2ui-grid .w2ui-grid-footer .w2ui-footer-center .w2ui-footer-nav{width:110px;margin:0 auto;padding:0;text-align:center}.w2ui-grid .w2ui-grid-footer .w2ui-footer-center .w2ui-footer-nav input[type=text]{padding:1px 2px 2px;border-radius:3px;width:40px;text-align:center}.w2ui-grid .w2ui-grid-footer .w2ui-footer-center .w2ui-footer-nav a.w2ui-footer-btn{display:inline-block;border-radius:3px;cursor:pointer;font-size:11px;line-height:16px;padding:1px 5px;width:30px;height:18px;margin-top:-1px;color:#000;background-color:transparent}.w2ui-grid .w2ui-grid-footer .w2ui-footer-center .w2ui-footer-nav a.w2ui-footer-btn:hover{color:#000;background-color:#aec8ff}.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr.w2ui-odd,.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr.w2ui-even,.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr.w2ui-odd:hover,.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr.w2ui-even:hover{background-color:inherit}.w2ui-ss .w2ui-grid-records table td{border-right-width:1px;border-bottom:1px solid #efefef}.w2ui-ss .w2ui-grid-records table tr:first-child td{border-bottom:0}.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr.w2ui-selected,.w2ui-ss .w2ui-grid-body .w2ui-grid-records table tr td.w2ui-selected{background-color:#EEF4FE!important}.w2ui-ss .w2ui-changed{background:inherit}.w2ui-ss .w2ui-grid-body .w2ui-selection{position:absolute;border:2px solid #6299DA;pointer-events:none}.w2ui-ss .w2ui-grid-body .w2ui-selection .w2ui-selection-resizer{cursor:crosshair;position:absolute;bottom:0;right:0;width:6px;height:6px;margin-right:-3px;margin-bottom:-3px;background-color:#457FC2;border:.5px solid #fff;outline:1px solid #fff;pointer-events:auto}.w2ui-overlay .w2ui-select-field{padding:8px 5px;cursor:default}.w2ui-overlay .w2ui-select-field table{font-size:11px;font-family:Verdana,Arial,sans-serif;border-spacing:0;border-collapse:border-collapse}.w2ui-overlay .w2ui-select-field table tr:hover{background-color:#b6d5ff}.w2ui-overlay .w2ui-select-field table td:nth-child(1){padding:3px 3px 3px 6px}.w2ui-overlay .w2ui-select-field table td:nth-child(1) input{margin:3px 2px 2px}.w2ui-overlay .w2ui-select-field table td:nth-child(2){padding:3px 15px 3px 3px}.w2ui-overlay .w2ui-col-on-off{padding:4px 0}.w2ui-overlay .w2ui-col-on-off table{border-spacing:0;border-collapse:border-collapse}.w2ui-overlay .w2ui-col-on-off table tr:hover{background-color:#b6d5ff}.w2ui-overlay .w2ui-col-on-off table td input[type=checkbox]{margin:3px 2px 2px}.w2ui-overlay .w2ui-col-on-off table td label{display:block;padding:3px 0;padding-right:10px}.w2ui-overlay .w2ui-col-on-off table td:first-child{padding:4px 0 4px 6px}.w2ui-overlay .w2ui-col-on-off table td:last-child{padding:4px 6px 4px 0}.w2ui-overlay .w2ui-grid-searches{text-align:left;padding:0;border-top:0;background-color:#f7f6f0}.w2ui-overlay .w2ui-grid-searches table{padding:4px;padding-top:12px;border-collapse:border-collapse}.w2ui-overlay .w2ui-grid-searches table td{padding:4px}.w2ui-overlay .w2ui-grid-searches table td.close-btn{width:20px;padding-right:20px}.w2ui-overlay .w2ui-grid-searches table td.close-btn button{min-width:24px;height:24px;padding-top:6px!important}.w2ui-overlay .w2ui-grid-searches table td.caption{text-align:right;padding-right:5px;border-right:1px solid #e8e8e3}.w2ui-overlay .w2ui-grid-searches table td.operator{text-align:left;padding:0 10px;padding-right:5px;border-right:1px solid #e8e8e3}.w2ui-overlay .w2ui-grid-searches table td.operator select{width:100%;color:#000;padding:0 15px 0 5px;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;-o-appearance:none;background-image:-webkit-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:-moz-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:-ms-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:-o-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-image:linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%)}.w2ui-overlay .w2ui-grid-searches table td.operator select::-ms-expand{display:none}.w2ui-overlay .w2ui-grid-searches table td.value{padding-right:5px;padding-left:5px}.w2ui-overlay .w2ui-grid-searches table td.value input[type=text]{border-radius:3px;padding:3px;margin-right:3px;height:23px}.w2ui-overlay .w2ui-grid-searches table td.value select{padding:3px;margin-right:3px;height:23px}.w2ui-overlay .w2ui-grid-searches table td.actions{border-right:0}.w2ui-overlay .w2ui-grid-searches table td.actions>div{margin:-7px;margin-top:15px;padding:13px 0;text-align:center;background-color:#efefe9;border-top:1px solid #e8e8e3}.w2ui-popup{position:fixed;z-index:1600;overflow:hidden;font-family:Verdana,Arial,sans-serif;border-radius:6px;padding:0;margin:0;border:1px solid #777;background-color:#eee;box-shadow:0 0 25px #555}.w2ui-popup,.w2ui-popup *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.w2ui-popup .w2ui-msg-title{padding:6px;border-radius:6px 6px 0 0;background-image:-webkit-linear-gradient(#ececec,#dfdfdf);background-image:-moz-linear-gradient(#ececec,#dfdfdf);background-image:-ms-linear-gradient(#ececec,#dfdfdf);background-image:-o-linear-gradient(#ececec,#dfdfdf);background-image:linear-gradient(#ececec,#dfdfdf);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffececec', endColorstr='#ffdfdfdf', GradientType=0);border-bottom:2px solid #bfbfbf;position:absolute;overflow:hidden;height:32px;left:0;right:0;top:0;text-overflow:ellipsis;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;cursor:move;font-size:15px;color:#555;z-index:300}.w2ui-popup .w2ui-msg-button{float:right;width:18px;height:18px;cursor:pointer;overflow:hidden;padding:0;margin:0 3px 0 0;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAQCAYAAABQrvyxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAj1JREFUeNrslr9rFFEQxz/zZi/qxSgW2lsqkiYoBku5Ro1o4UFKEYkgSaxSCLYqdv5IEVPYCMJJwERWrK0CKhoQ8hdobQTjXW7njcXlYnLunQQu0YDTLOy+Nzvfme98Z8Td2ckW2OGWdMvRvYfT/RGfBPoBBVpLK0AEPgVkdGL06vt/CoB5nBaRE8AXYKXNsQIwaB4fAwOtH+88mn4m7ifN4vUYebWBKkFKqjIV3N9NjI2Uuw5ARI45fBanH+F77iFnN8JHETmS68P9NHBQNTwHL8foaSN4SqoyA/SZyL4tqQAQBVYCLOFYlNxmq0WorVLpN9Oe5LKt1CsgRVWpAOfB66phBuhTkepSdfnKVjaxNJMSWn/iawmTtpeDp6pWBpaBoqrMqoYU6AOqIbFhxGa3R4V8nfNNKLUESzXJhoCvQC+wF/gW1C5IiC+2XUbD5jA3rd4C26NR3945IA2iRzqRJgdElJJlSQocAKrAD2A/6Ev3cLajjN59MDWHyKl2voOI1zKbv3Xj2lCHJFoz+LXuBoIAjnUklEvJrDDT5LwmdhG8blkyBxRjXSu4loE0X4VEznXKV3SnoOFMB7YUolBcbcKNdxuPXUBPu8pbLXsK0ghebVjEXgNoYmXLtGLuxd6ePU+AQ20AaIrb4DpFycmSv81/7YsiMgAstB1kQgE47O4LuQmCNwGOB7VxCb/URsRSTbhkmU4ifGiZHd1Z5m7fnxoIQSaBo39YJRZj9LGb4yPzXWm1/9voX7afAwAC5tacDTA2XgAAAABJRU5ErkJggg==) no-repeat center left;background-position:0 0;color:transparent!important;border-radius:3px;border:1px solid transparent}.w2ui-popup .w2ui-msg-close{margin-top:0;background-position:-32px 0}.w2ui-popup .w2ui-msg-close:hover{background-color:#ccc;border:1px solid #aaa}.w2ui-popup .w2ui-msg-max{background-position:-16px 0}.w2ui-popup .w2ui-msg-max:hover{background-color:#ccc;border:1px solid #aaa}.w2ui-popup .w2ui-box1,.w2ui-popup .w2ui-box2{position:absolute;left:0;right:0;top:32px;bottom:55px;z-index:100}.w2ui-popup .w2ui-msg-body{font-size:13px;line-height:130%;padding:0 7px 7px;color:#000;background-color:#eee;position:absolute;overflow:auto;width:100%;height:100%}.w2ui-popup .w2ui-popup-message{position:absolute;z-index:250;background-color:#f9f9f9;border:1px solid #999;box-shadow:0 0 15px #aaa;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box;border-top:0;border-radius:0 0 6px 6px;overflow:auto}.w2ui-popup .w2ui-msg-buttons{padding:12px;border-radius:0 0 6px 6px;border-top:1px solid #d5d8d8;background-color:#f1f1f1;text-align:center;position:absolute;overflow:hidden;height:52px;left:0;right:0;bottom:0;z-index:200}.w2ui-popup .w2ui-msg-no-title{border-top-left-radius:6px;border-top-right-radius:6px;top:0!important}.w2ui-popup .w2ui-msg-no-buttons{border-bottom-left-radius:6px;border-bottom-right-radius:6px;bottom:0!important}.w2ui-sidebar{cursor:default;overflow:hidden!important;background-color:#edf1f6!important;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.w2ui-sidebar *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.w2ui-sidebar>div{position:relative;overflow:hidden}.w2ui-sidebar .w2ui-sidebar-top{position:absolute;z-index:2;top:0;left:0;right:0}.w2ui-sidebar .w2ui-sidebar-bottom{position:absolute;z-index:2;bottom:0;left:0;right:0}.w2ui-sidebar .w2ui-sidebar-div{position:absolute;z-index:1;overflow:auto;top:0;bottom:0;left:0;right:0;padding:2px 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.w2ui-sidebar .w2ui-sidebar-div table{width:100%}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node{background-color:#edf1f6;border-top:1px solid transparent;border-bottom:1px solid transparent;margin:0;padding:1px 0}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node table{pointer-events:none}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-caption,.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image,.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image>span,.w2ui-sidebar .w2ui-sidebar-div .w2ui-node td.w2ui-node-dots{color:#000;text-shadow:0 0 0 #fff;pointer-events:none}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-caption:hover,.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image:hover,.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image>span:hover,.w2ui-sidebar .w2ui-sidebar-div .w2ui-node td.w2ui-node-dots:hover{color:inherit}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node:hover{border-top:1px solid #f9f9f9;border-bottom:1px solid #f9f9f9;background-color:#d7e1ef}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image{width:22px;text-align:center;pointer-events:none}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node .w2ui-node-image>span{color:#516173!important}.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected,.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected:hover{background-image:-webkit-linear-gradient(#69b1e0,#4a96d3);background-image:-moz-linear-gradient(#69b1e0,#4a96d3);background-image:-ms-linear-gradient(#69b1e0,#4a96d3);background-image:-o-linear-gradient(#69b1e0,#4a96d3);background-image:linear-gradient(#69b1e0,#4a96d3);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff69b1e0', endColorstr='#ff4a96d3', GradientType=0);border-top:1px solid #5295cd;border-bottom:1px solid #2661a6}.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected .w2ui-node-caption,.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected:hover .w2ui-node-caption,.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected .w2ui-node-image,.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected:hover .w2ui-node-image,.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected .w2ui-node-image>span,.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected:hover .w2ui-node-image>span,.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected td.w2ui-node-dots,.w2ui-sidebar .w2ui-sidebar-div .w2ui-selected:hover td.w2ui-node-dots{color:#fff!important;text-shadow:1px 1px 2px #666!important}.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled,.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled:hover{background:transparent!important;border-top:1px solid transparent;border-bottom:1px solid transparent}.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled .w2ui-node-caption,.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled:hover .w2ui-node-caption,.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled .w2ui-node-image,.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled:hover .w2ui-node-image,.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled .w2ui-node-image>span,.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled:hover .w2ui-node-image>span,.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled td.w2ui-node-dots,.w2ui-sidebar .w2ui-sidebar-div .w2ui-disabled:hover td.w2ui-node-dots{opacity:.4;filter:alpha(opacity=40);color:#000!important;text-shadow:0 0 0 #fff!important}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node-caption{white-space:nowrap;padding:5px 0 5px 3px;margin:1px 0 1px 22px;position:relative;z-index:1;font-size:12px}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node-group{white-space:nowrap;overflow:hidden;padding:10px 0 10px 10px;margin:0;cursor:default;color:#868b92;background-color:transparent}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node-group :nth-child(1){margin-right:10px;float:right;color:transparent}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node-group :nth-child(2){font-weight:400;text-transform:uppercase}.w2ui-sidebar .w2ui-sidebar-div .w2ui-node-sub{overflow:hidden}.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-dots{width:18px;padding:0 0 1px 7px;text-align:center}.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-dots .w2ui-expand{width:16px;margin-top:-3px;pointer-events:auto}.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-data{padding:1px 1px 3px}.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-data .w2ui-node-image{padding:3px 0 0;float:left}.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-data .w2ui-node-image>span{font-size:16px;color:#000;text-shadow:0 0 0 #fff}.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-data .w2ui-node-image.w2ui-icon{margin-top:3px}.w2ui-sidebar .w2ui-sidebar-div td.w2ui-node-data .w2ui-node-count{float:right;border:1px solid #9da4af;border-radius:20px;width:auto;height:18px;padding:2px 7px;margin:3px 4px -2px 0;background-color:#e7f0fc;color:#667274;box-shadow:0 0 2px #fff;text-shadow:1px 1px 1px #e6e6e6;position:relative;z-index:2}.w2ui-tabs{cursor:default;overflow:hidden!important;background-color:#fafafa;padding:3px 0;padding-bottom:0!important}.w2ui-tabs table{border-bottom:1px solid silver;padding:0 7px}.w2ui-tabs .w2ui-tab{padding:6px 20px;text-align:center;color:#000;background-color:transparent;border:1px solid silver;border-bottom:1px solid silver;white-space:nowrap;margin:1px 1px -1px 0;border-top-left-radius:4px;border-top-right-radius:4px;cursor:default}.w2ui-tabs .w2ui-tab.active{color:#000;background-color:#fff;border:1px solid silver;border-bottom:1px solid transparent}.w2ui-tabs .w2ui-tab.closable{padding:6px 28px 6px 20px}.w2ui-tabs .w2ui-tab-close{color:#555;text-shadow:1px 1px 1px #bbb;float:right;margin:6px 4px 0 0;padding:0 0 0 5px;width:16px;height:16px;opacity:.9;border:0;border-top:3px solid transparent;border-radius:9px}.w2ui-tabs .w2ui-tab-close:hover{background-color:#D77F7F;color:#fff}.w2ui-tabs .w2ui-tab-close:before{position:relative;top:-2px;left:0;opacity:.6;color:inherit;text-shadow:inherit;content:'x'}.w2ui-toolbar{margin:0;padding:2px;outline:0;background-color:#efefef;overflow:hidden!important;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.w2ui-toolbar .disabled{opacity:.3;filter:alpha(opacity=30)}.w2ui-toolbar table{table-layout:auto!important}.w2ui-toolbar table td{border:0!important}.w2ui-toolbar table.w2ui-button{margin:0 1px;border-radius:4px;height:24px;border:1px solid transparent;background-color:transparent}.w2ui-toolbar table.w2ui-button .w2ui-tb-image{width:16px;height:16px;padding:0;margin:2px 4px 3px 3px!important;border:0!important;text-align:center}.w2ui-toolbar table.w2ui-button .w2ui-tb-image>span{font-size:15px;margin-top:3px;display:block;color:#8d99a7}.w2ui-toolbar table.w2ui-button .w2ui-tb-caption{color:#000;padding:0 4px 0 2px}.w2ui-toolbar table.w2ui-button .w2ui-tb-count{padding:0 4px 0 0}.w2ui-toolbar table.w2ui-button .w2ui-tb-count>span{border:1px solid #9da4af;border-radius:20px;width:auto;height:18px;padding:2px 7px;background-color:#e7f0fc;color:#667274;box-shadow:0 0 2px #fff;text-shadow:1px 1px 1px #e6e6e6}.w2ui-toolbar table.w2ui-button .w2ui-tb-down{padding:3px}.w2ui-toolbar table.w2ui-button .w2ui-tb-down>div{border:4px solid transparent;border-top:5px solid #8D99A7;margin-top:5px}.w2ui-toolbar table.w2ui-button.over{border:1px solid #ccc;background-color:#eee}.w2ui-toolbar table.w2ui-button.over .w2ui-tb-caption{color:#000}.w2ui-toolbar table.w2ui-button.down{border:1px solid #aaa;background-color:#ddd}.w2ui-toolbar table.w2ui-button.down .w2ui-tb-caption{color:#666}.w2ui-toolbar table.w2ui-button.checked{border:1px solid #aaa;background-color:#fff}.w2ui-toolbar table.w2ui-button.checked .w2ui-tb-caption{color:#000}.w2ui-toolbar table.w2ui-button table{height:17px;border-radius:4px;cursor:default}.w2ui-toolbar .w2ui-break{background-image:-webkit-linear-gradient(top,rgba(153,153,153,.1) 0,#999 40%,#999 60%,rgba(153,153,153,.1) 100%);background-image:-moz-linear-gradient(top,rgba(153,153,153,.1) 0,#999 40%,#999 60%,rgba(153,153,153,.1) 100%);background-image:-ms-linear-gradient(top,rgba(153,153,153,.1) 0,#999 40%,#999 60%,rgba(153,153,153,.1) 100%);background-image:-o-linear-gradient(top,rgba(153,153,153,.1) 0,#999 40%,#999 60%,rgba(153,153,153,.1) 100%);background-image:linear-gradient(top,rgba(153,153,153,.1) 0,#999 40%,#999 60%,rgba(153,153,153,.1) 100%);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff999999', endColorstr='#ff999999', GradientType=0);width:1px!important;height:22px;padding:0;margin:0 6px}.w2ui-listview{overflow:auto!important;background-color:#fff!important;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.w2ui-listview *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;-o-box-sizing:border-box;box-sizing:border-box}.w2ui-listview>ul{list-style-type:none;margin:0;cursor:default}.w2ui-listview>ul>li{display:inline-block;vertical-align:top;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;border:1px solid transparent;border-radius:4px}.w2ui-listview>ul>li.w2ui-focused{border:1px solid #2661a6}.w2ui-listview>ul>li.w2ui-selected{border:1px solid #2661a6}.w2ui-listview>ul>li.w2ui-selected,.w2ui-listview>ul>li.w2ui-selected.hover{background-image:-webkit-linear-gradient(#69b1e0,#4a96d3);background-image:-moz-linear-gradient(#69b1e0,#4a96d3);background-image:-ms-linear-gradient(#69b1e0,#4a96d3);background-image:-o-linear-gradient(#69b1e0,#4a96d3);background-image:linear-gradient(#69b1e0,#4a96d3);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff69b1e0', endColorstr='#ff4a96d3', GradientType=0)}.w2ui-listview>ul>li.w2ui-selected>div>div.caption,.w2ui-listview>ul>li.w2ui-selected.hover>div>div.caption{color:#fff}.w2ui-listview>ul>li.w2ui-selected>div>div.description,.w2ui-listview>ul>li.w2ui-selected.hover>div>div.description{color:#ddd}.w2ui-listview>ul>li.w2ui-selected>div>div.extra>div>div,.w2ui-listview>ul>li.w2ui-selected.hover>div>div.extra>div>div{color:#ddd}.w2ui-listview>ul>li.hover{background-color:#d7e1ef;border:1px solid #2661a6}.w2ui-listview>ul>li div{vertical-align:middle}.w2ui-listview>ul>li>div>div.caption{display:block;text-align:center;word-wrap:break-word;max-height:50px;color:#000;font-size:12px}.w2ui-listview>ul>li>div>div.description{display:none;text-align:left;color:#777;font-size:12px}.w2ui-listview>ul>li>div>div.extra{display:none}.w2ui-listview>ul>li>div>div.extra>div>div{color:#777}.w2ui-icon-small>ul{padding:1px 0 0 1px}.w2ui-icon-small>ul>li{margin:0 1px 1px 0;padding:2px;width:250px;white-space:nowrap}.w2ui-icon-small>ul>li>div>div.w2ui-listview-img{display:inline-block;width:26px;height:22px;font-size:21px;margin-right:2px}.w2ui-icon-small>ul>li>div>div.caption{display:inline-block}.w2ui-icon-medium>ul{padding:4px 0 0 4px}.w2ui-icon-medium>ul>li{margin:0 4px 4px 0;padding:4px;width:100px}.w2ui-icon-medium>ul>li>div>div.w2ui-listview-img{display:block;width:92px;height:60px;font-size:57px;margin-left:auto;margin-right:auto;background-position:center}.w2ui-icon-large>ul{padding:4px 0 0 4px}.w2ui-icon-large>ul>li{margin:0 4px 4px 0;padding:4px;width:160px}.w2ui-icon-large>ul>li>div>div.w2ui-listview-img{display:block;width:152px;height:120px;font-size:114px;margin-left:auto;margin-right:auto;background-position:center}.w2ui-icon-tile>ul{padding:1px 0 0 1px}.w2ui-icon-tile>ul>li{margin:0 1px 1px 0;padding:4px;width:250px;white-space:nowrap}.w2ui-icon-tile>ul>li>div>div.w2ui-listview-img{display:inline-block;width:72px;height:60px;font-size:57px;float:left;margin-right:4px}.w2ui-icon-tile>ul>li>div>div.caption{text-align:left}.w2ui-icon-tile>ul>li>div>div.description{display:block}.w2ui-table>ul{padding:0}.w2ui-table>ul>li{width:100%;padding:2px;border-radius:0;border-bottom:1px dotted #d3d3d3}.w2ui-table>ul>li>div{display:inline-block;position:relative;width:100%;white-space:nowrap;overflow:hidden}.w2ui-table>ul>li>div>div.w2ui-listview-img{display:inline-block;width:38px;height:32px;font-size:31px;margin-right:2px}.w2ui-table>ul>li>div>div.caption{display:inline-block}.w2ui-table>ul>li>div>div.extra{display:inline-block;position:absolute;right:0;height:100%;background-color:#fff}.w2ui-table>ul>li>div>div.extra>div:before{display:inline-block;height:100%;width:0;content:'';vertical-align:middle}.w2ui-table>ul>li>div>div.extra>div{display:inline}.w2ui-table>ul>li>div>div.extra>div>div{display:inline-block;font-size:12px}.w2ui-table>ul>li.w2ui-selected div.extra,.w2ui-table>ul>li.w2ui-selected.hover div.extra{background-image:-webkit-linear-gradient(#69b1e0,#4a96d3);background-image:-moz-linear-gradient(#69b1e0,#4a96d3);background-image:-ms-linear-gradient(#69b1e0,#4a96d3);background-image:-o-linear-gradient(#69b1e0,#4a96d3);background-image:linear-gradient(#69b1e0,#4a96d3);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ff69b1e0', endColorstr='#ff4a96d3', GradientType=0)}.w2ui-table>ul>li.hover div.extra{background-color:#d7e1ef}.w2ui-listview>ul>li div.icon-none{border:1px solid rgba(102,102,102,.35)} \ No newline at end of file
diff --git a/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.min.js b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.min.js
new file mode 100644
index 00000000..d82e32ed
--- /dev/null
+++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/w2ui-1.4.min.js
@@ -0,0 +1,11 @@
+/* w2ui 1.4 (c) http://w2ui.com, vitmalina@gmail.com */
+var w2ui=w2ui||{},w2obj=w2obj||{},w2utils=function(){function a(a){var b=/^[-+]?[0-9]+$/;return b.test(a)}function b(a){return("number"==typeof a||"string"==typeof a&&""!==a)&&!isNaN(Number(a))}function c(a){var b=w2utils.settings,c=new RegExp("^"+(b.currencyPrefix?"\\"+b.currencyPrefix+"?":"")+"[-+]?[0-9]*[.]?[0-9]+"+(b.currencySuffix?"\\"+b.currencySuffix+"?":"")+"$","i");return"string"==typeof a&&(a=a.replace(new RegExp(b.groupSymbol,"g"),"")),"object"==typeof a||""===a?!1:c.test(a)}function d(a){var b=/^[a-fA-F0-9]+$/;return b.test(a)}function e(a){var b=/^[a-zA-Z0-9_-]+$/;return b.test(a)}function f(a){var b=/^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;return b.test(a)}function g(b,c,d){if(!b)return!1;var e,f,g,h="Invalid Date";if(null==c&&(c=w2utils.settings.date_format),"function"==typeof b.getUTCFullYear&&"function"==typeof b.getUTCMonth&&"function"==typeof b.getUTCDate)g=b.getUTCFullYear(),e=b.getUTCMonth(),f=b.getUTCDate();else if("function"==typeof b.getFullYear&&"function"==typeof b.getMonth&&"function"==typeof b.getDate)g=b.getFullYear(),e=b.getMonth(),f=b.getDate();else{if(b=String(b),RegExp("mon","ig").test(c)){c=c.replace(/month/gi,"m").replace(/mon/gi,"m").replace(/dd/gi,"d").replace(/[, ]/gi,"/").replace(/\/\//g,"/").toLowerCase(),b=b.replace(/[, ]/gi,"/").replace(/\/\//g,"/").toLowerCase();for(var i=0,j=w2utils.settings.fullmonths.length;j>i;i++){var k=w2utils.settings.fullmonths[i];b=b.replace(RegExp(k,"ig"),parseInt(i)+1).replace(RegExp(k.substr(0,3),"ig"),parseInt(i)+1)}}var l=b.replace(/-/g,"/").replace(/\./g,"/").toLowerCase().split("/"),m=c.replace(/-/g,"/").replace(/\./g,"/").toLowerCase();"mm/dd/yyyy"===m&&(e=l[0],f=l[1],g=l[2]),"m/d/yyyy"===m&&(e=l[0],f=l[1],g=l[2]),"dd/mm/yyyy"===m&&(e=l[1],f=l[0],g=l[2]),"d/m/yyyy"===m&&(e=l[1],f=l[0],g=l[2]),"yyyy/dd/mm"===m&&(e=l[2],f=l[1],g=l[0]),"yyyy/d/m"===m&&(e=l[2],f=l[1],g=l[0]),"yyyy/mm/dd"===m&&(e=l[1],f=l[2],g=l[0]),"yyyy/m/d"===m&&(e=l[1],f=l[2],g=l[0]),"mm/dd/yy"===m&&(e=l[0],f=l[1],g=l[2]),"m/d/yy"===m&&(e=l[0],f=l[1],g=parseInt(l[2])+1900),"dd/mm/yy"===m&&(e=l[1],f=l[0],g=parseInt(l[2])+1900),"d/m/yy"===m&&(e=l[1],f=l[0],g=parseInt(l[2])+1900),"yy/dd/mm"===m&&(e=l[2],f=l[1],g=parseInt(l[0])+1900),"yy/d/m"===m&&(e=l[2],f=l[1],g=parseInt(l[0])+1900),"yy/mm/dd"===m&&(e=l[1],f=l[2],g=parseInt(l[0])+1900),"yy/m/d"===m&&(e=l[1],f=l[2],g=parseInt(l[0])+1900)}return a(g)&&a(e)&&a(f)?(g=+g,e=+e,f=+f,h=new Date(g,e-1,f),null==e?!1:"Invalid Date"===h?!1:h.getMonth()+1!==e||h.getDate()!==f||h.getFullYear()!==g?!1:d===!0?h:!0):!1}function h(a,b){if(null==a)return!1;var c,d;a=String(a),a=a.toUpperCase(),d=a.indexOf("PM")>=0;var e=d||a.indexOf("AM")>=0;c=e?12:24,a=a.replace("AM","").replace("PM",""),a=$.trim(a);var f=a.split(":"),g=parseInt(f[0]||0),h=parseInt(f[1]||0);return e&&1===f.length||2===f.length?""===f[0]||0>g||g>c||!this.isInt(f[0])||f[0].length>2?!1:2===f.length&&(""===f[1]||0>h||h>59||!this.isInt(f[1])||2!==f[1].length)?!1:e||c!==g||0===h?e&&1===f.length&&0===g?!1:b===!0?(d&&(g+=12),{hours:g,minutes:h}):!0:!1:!1}function i(a){if(""===a||null==a)return"";var b=new Date(a);if(w2utils.isInt(a)&&(b=new Date(Number(a))),"Invalid Date"===b)return"";var c=new Date,d=(c.getTime()-b.getTime())/1e3,e="",f="";return 0>d?(e='<span style="color: #aaa">future</span>',f=""):60>d?(e=Math.floor(d),f="sec",0>d&&(e=0,f="sec")):3600>d?(e=Math.floor(d/60),f="min"):86400>d?(e=Math.floor(d/60/60),f="hour"):2592e3>d?(e=Math.floor(d/24/60/60),f="day"):31557600>d?(e=Math.floor(d/365.25/24/60/60*10)/10,f="month"):d>=31557600&&(e=Math.floor(d/365.25/24/60/60*10)/10,f="year"),e+" "+f+(e>1?"s":"")}function j(a){if(""===a||null==a)return"";var b=new Date(a);if(w2utils.isInt(a)&&(b=new Date(Number(a))),"Invalid Date"===b)return"";var c=w2utils.settings.shortmonths,d=new Date,e=new Date;e.setTime(e.getTime()-864e5);var f=c[b.getMonth()]+" "+b.getDate()+", "+b.getFullYear(),g=c[d.getMonth()]+" "+d.getDate()+", "+d.getFullYear(),h=c[e.getMonth()]+" "+e.getDate()+", "+e.getFullYear(),i=b.getHours()-(b.getHours()>12?12:0)+":"+(b.getMinutes()<10?"0":"")+b.getMinutes()+" "+(b.getHours()>=12?"pm":"am"),j=b.getHours()-(b.getHours()>12?12:0)+":"+(b.getMinutes()<10?"0":"")+b.getMinutes()+":"+(b.getSeconds()<10?"0":"")+b.getSeconds()+" "+(b.getHours()>=12?"pm":"am"),k=f;return f===g&&(k=i),f===h&&(k=w2utils.lang("Yesterday")),'<span title="'+f+" "+j+'">'+k+"</span>"}function k(a){if(!w2utils.isFloat(a)||""===a)return"";if(a=parseFloat(a),0===a)return 0;var b=["Bt","KB","MB","GB","TB"],c=parseInt(Math.floor(Math.log(a)/Math.log(1024)));return(Math.floor(a/Math.pow(1024,c)*10)/10).toFixed(0===c?0:1)+" "+b[c]}function l(a,b){var c="";return null==b&&(b=w2utils.settings.groupSymbol||","),(w2utils.isFloat(a)||w2utils.isInt(a)||w2utils.isMoney(a))&&(E=String(a).split("."),c=String(E[0]).replace(/(\d)(?=(\d\d\d)+(?!\d))/g,"$1"+b),null!=E[1]&&(c+="."+E[1])),c}function m(a,b){w2utils.settings.shortmonths,w2utils.settings.fullmonths;if(b||(b=this.settings.date_format),""===a||null==a)return"";var c=new Date(a);if(w2utils.isInt(a)&&(c=new Date(Number(a))),"Invalid Date"===c)return"";var d=c.getFullYear(),e=c.getMonth(),f=c.getDate();return b.toLowerCase().replace("month",w2utils.settings.fullmonths[e]).replace("mon",w2utils.settings.shortmonths[e]).replace(/yyyy/g,d).replace(/yyy/g,d).replace(/yy/g,d>2e3?100+parseInt(String(d).substr(2)):String(d).substr(2)).replace(/(^|[^a-z$])y/g,"$1"+d).replace(/mm/g,(10>e+1?"0":"")+(e+1)).replace(/dd/g,(10>f?"0":"")+f).replace(/(^|[^a-z$])m/g,"$1"+(e+1)).replace(/(^|[^a-z$])d/g,"$1"+f)}function n(a,b){w2utils.settings.shortmonths,w2utils.settings.fullmonths;if(b||(b="h12"===this.settings.time_format?"hh:mi pm":"h24:mi"),""===a||null==a)return"";var c=new Date(a);if(w2utils.isInt(a)&&(c=new Date(Number(a))),w2utils.isTime(a)){var d=w2utils.isTime(a,!0);c=new Date,c.setHours(d.hours),c.setMinutes(d.minutes)}if("Invalid Date"===c)return"";var e="am",f=c.getHours(),g=c.getHours(),h=c.getMinutes(),i=c.getSeconds();return 10>h&&(h="0"+h),10>i&&(i="0"+i),(-1!==b.indexOf("am")||-1!==b.indexOf("pm"))&&(f>=12&&(e="pm"),f>12&&(f-=12)),b.toLowerCase().replace("am",e).replace("pm",e).replace("hh",f).replace("h24",g).replace("mm",h).replace("mi",h).replace("ss",i).replace(/(^|[^a-z$])h/g,"$1"+f).replace(/(^|[^a-z$])m/g,"$1"+h).replace(/(^|[^a-z$])s/g,"$1"+i)}function o(a,b){var c;return c="string"!=typeof b?[this.settings.date_format,this.settings.time_format]:b.split("|"),this.formatDate(a,c[0])+" "+this.formatTime(a,c[1])}function p(a){if(null===a)return a;switch(typeof a){case"number":break;case"string":a=$.trim(String(a).replace(/(<([^>]+)>)/gi,""));break;case"object":for(var b in a)a[b]=this.stripTags(a[b])}return a}function q(a){if(null===a)return a;switch(typeof a){case"number":break;case"string":a=String(a).replace(/&/g,"&amp;").replace(/>/g,"&gt;").replace(/</g,"&lt;").replace(/"/g,"&quot;");break;case"object":for(var b in a)a[b]=this.encodeTags(a[b])}return a}function r(a){return""===a||null==a?"":String(a).replace(/([;&,\.\+\*\~'`:"\!\^#$%@\[\]\(\)=<>\|\/? {}\\])/g,"\\$1")}function s(a){function b(a){for(var a=String(a).replace(/\r\n/g,"\n"),b="",c=0;c<a.length;c++){var d=a.charCodeAt(c);128>d?b+=String.fromCharCode(d):d>127&&2048>d?(b+=String.fromCharCode(d>>6|192),b+=String.fromCharCode(63&d|128)):(b+=String.fromCharCode(d>>12|224),b+=String.fromCharCode(d>>6&63|128),b+=String.fromCharCode(63&d|128))}return b}var c,d,e,f,g,h,i,j="",k=0,l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";for(a=b(a);k<a.length;)c=a.charCodeAt(k++),d=a.charCodeAt(k++),e=a.charCodeAt(k++),f=c>>2,g=(3&c)<<4|d>>4,h=(15&d)<<2|e>>6,i=63&e,isNaN(d)?h=i=64:isNaN(e)&&(i=64),j=j+l.charAt(f)+l.charAt(g)+l.charAt(h)+l.charAt(i);return j}function t(a){function b(a){for(var b,c,d="",e=0,f=0;e<a.length;)f=a.charCodeAt(e),128>f?(d+=String.fromCharCode(f),e++):f>191&&224>f?(b=a.charCodeAt(e+1),d+=String.fromCharCode((31&f)<<6|63&b),e+=2):(b=a.charCodeAt(e+1),c=a.charCodeAt(e+2),d+=String.fromCharCode((15&f)<<12|(63&b)<<6|63&c),e+=3);return d}var c,d,e,f,g,h,i,j="",k=0,l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");k<a.length;)f=l.indexOf(a.charAt(k++)),g=l.indexOf(a.charAt(k++)),h=l.indexOf(a.charAt(k++)),i=l.indexOf(a.charAt(k++)),c=f<<2|g>>4,d=(15&g)<<4|h>>2,e=(3&h)<<6|i,j+=String.fromCharCode(c),64!==h&&(j+=String.fromCharCode(d)),64!==i&&(j+=String.fromCharCode(e));return j=b(j)}function u(a,b,c,d){function e(a,b,c){var d=!!window.webkitURL;return d||"undefined"==typeof c||(b=c),";"+a+": "+b+"; -webkit-"+a+": "+b+"; -moz-"+a+": "+b+"; -ms-"+a+": "+b+"; -o-"+a+": "+b+";"}var f=$(a).width(),g=$(a).height(),h=.5;if(!a||!b)return void console.log("ERROR: Cannot do transition when one of the divs is null");switch(a.parentNode.style.cssText+=e("perspective","700px")+"; overflow: hidden;",a.style.cssText+="; position: absolute; z-index: 1019; "+e("backface-visibility","hidden"),b.style.cssText+="; position: absolute; z-index: 1020; "+e("backface-visibility","hidden"),c){case"slide-left":a.style.cssText+="overflow: hidden; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)"),b.style.cssText+="overflow: hidden; "+e("transform","translate3d("+f+"px, 0, 0)","translate("+f+"px, 0)"),$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+";"+e("transform","translate3d(0, 0, 0)","translate(0, 0)"),a.style.cssText+=e("transition",h+"s")+";"+e("transform","translate3d(-"+f+"px, 0, 0)","translate(-"+f+"px, 0)")},1);break;case"slide-right":a.style.cssText+="overflow: hidden; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)"),b.style.cssText+="overflow: hidden; "+e("transform","translate3d(-"+f+"px, 0, 0)","translate(-"+f+"px, 0)"),$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+"; "+e("transform","translate3d(0px, 0, 0)","translate(0px, 0)"),a.style.cssText+=e("transition",h+"s")+"; "+e("transform","translate3d("+f+"px, 0, 0)","translate("+f+"px, 0)")},1);break;case"slide-down":a.style.cssText+="overflow: hidden; z-index: 1; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)"),b.style.cssText+="overflow: hidden; z-index: 0; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)"),$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+"; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)"),a.style.cssText+=e("transition",h+"s")+"; "+e("transform","translate3d(0, "+g+"px, 0)","translate(0, "+g+"px)")},1);break;case"slide-up":a.style.cssText+="overflow: hidden; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)"),b.style.cssText+="overflow: hidden; "+e("transform","translate3d(0, "+g+"px, 0)","translate(0, "+g+"px)"),$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+"; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)"),a.style.cssText+=e("transition",h+"s")+"; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)")},1);break;case"flip-left":a.style.cssText+="overflow: hidden; "+e("transform","rotateY(0deg)"),b.style.cssText+="overflow: hidden; "+e("transform","rotateY(-180deg)"),$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+"; "+e("transform","rotateY(0deg)"),a.style.cssText+=e("transition",h+"s")+"; "+e("transform","rotateY(180deg)")},1);break;case"flip-right":a.style.cssText+="overflow: hidden; "+e("transform","rotateY(0deg)"),b.style.cssText+="overflow: hidden; "+e("transform","rotateY(180deg)"),$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+"; "+e("transform","rotateY(0deg)"),a.style.cssText+=e("transition",h+"s")+"; "+e("transform","rotateY(-180deg)")},1);break;case"flip-down":a.style.cssText+="overflow: hidden; "+e("transform","rotateX(0deg)"),b.style.cssText+="overflow: hidden; "+e("transform","rotateX(180deg)"),$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+"; "+e("transform","rotateX(0deg)"),a.style.cssText+=e("transition",h+"s")+"; "+e("transform","rotateX(-180deg)")},1);break;case"flip-up":a.style.cssText+="overflow: hidden; "+e("transform","rotateX(0deg)"),b.style.cssText+="overflow: hidden; "+e("transform","rotateX(-180deg)"),$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+"; "+e("transform","rotateX(0deg)"),a.style.cssText+=e("transition",h+"s")+"; "+e("transform","rotateX(180deg)")},1);break;case"pop-in":a.style.cssText+="overflow: hidden; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)"),b.style.cssText+="overflow: hidden; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)")+"; "+e("transform","scale(.8)")+"; opacity: 0;",$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+"; "+e("transform","scale(1)")+"; opacity: 1;",a.style.cssText+=e("transition",h+"s")+";"},1);break;case"pop-out":a.style.cssText+="overflow: hidden; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)")+"; "+e("transform","scale(1)")+"; opacity: 1;",b.style.cssText+="overflow: hidden; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)")+"; opacity: 0;",$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+"; opacity: 1;",a.style.cssText+=e("transition",h+"s")+"; "+e("transform","scale(1.7)")+"; opacity: 0;"},1);break;default:a.style.cssText+="overflow: hidden; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)"),b.style.cssText+="overflow: hidden; "+e("transform","translate3d(0, 0, 0)","translate(0, 0)")+"; opacity: 0;",$(b).show(),window.setTimeout(function(){b.style.cssText+=e("transition",h+"s")+"; opacity: 1;",a.style.cssText+=e("transition",h+"s")},1)}setTimeout(function(){"slide-down"===c&&($(a).css("z-index","1019"),$(b).css("z-index","1020")),b&&$(b).css({opacity:"1","-webkit-transition":"","-moz-transition":"","-ms-transition":"","-o-transition":"","-webkit-transform":"","-moz-transform":"","-ms-transform":"","-o-transform":"","-webkit-backface-visibility":"","-moz-backface-visibility":"","-ms-backface-visibility":"","-o-backface-visibility":""}),a&&($(a).css({opacity:"1","-webkit-transition":"","-moz-transition":"","-ms-transition":"","-o-transition":"","-webkit-transform":"","-moz-transform":"","-ms-transform":"","-o-transform":"","-webkit-backface-visibility":"","-moz-backface-visibility":"","-ms-backface-visibility":"","-o-backface-visibility":""}),a.parentNode&&$(a.parentNode).css({"-webkit-perspective":"","-moz-perspective":"","-ms-perspective":"","-o-perspective":""})),"function"==typeof d&&d()},1e3*h)}function v(a,b,c){var d={};"object"==typeof b?d=b:(d.msg=b,d.spinner=c),d.msg||0===d.msg||(d.msg=""),w2utils.unlock(a),$(a).prepend('<div class="w2ui-lock"></div><div class="w2ui-lock-msg"></div>');var e=$(a).find(".w2ui-lock"),f=$(a).find(".w2ui-lock-msg");d.msg||f.css({"background-color":"transparent",border:"0px"}),d.spinner===!0&&(d.msg='<div class="w2ui-spinner" '+(d.msg?"":'style="width: 35px; height: 35px"')+"></div>"+d.msg),null!=d.opacity&&e.css("opacity",d.opacity),"function"==typeof e.fadeIn?(e.fadeIn(200),f.html(d.msg).fadeIn(200)):(e.show(),f.html(d.msg).show(0)),$().w2tag()}function w(a){$(a).find(".w2ui-lock").remove(),$(a).find(".w2ui-lock-msg").remove()}function x(a,b){var c=$(a),d={left:parseInt(c.css("border-left-width"))||0,right:parseInt(c.css("border-right-width"))||0,top:parseInt(c.css("border-top-width"))||0,bottom:parseInt(c.css("border-bottom-width"))||0},e={left:parseInt(c.css("margin-left"))||0,right:parseInt(c.css("margin-right"))||0,top:parseInt(c.css("margin-top"))||0,bottom:parseInt(c.css("margin-bottom"))||0},f={left:parseInt(c.css("padding-left"))||0,right:parseInt(c.css("padding-right"))||0,top:parseInt(c.css("padding-top"))||0,bottom:parseInt(c.css("padding-bottom"))||0};switch(b){case"top":return d.top+e.top+f.top;case"bottom":return d.bottom+e.bottom+f.bottom;case"left":return d.left+e.left+f.left;case"right":return d.right+e.right+f.right;case"width":return d.left+d.right+e.left+e.right+f.left+f.right+parseInt(c.width());case"height":return d.top+d.bottom+e.top+e.bottom+f.top+f.bottom+parseInt(c.height());case"+width":return d.left+d.right+e.left+e.right+f.left+f.right;case"+height":return d.top+d.bottom+e.top+e.bottom+f.top+f.bottom}return 0}function y(a){var b=this.settings.phrases[a];return null==b?a:b}function z(a){a||(a="en-us"),5===a.length&&(a="locale/"+a+".json"),$.ajax({url:a,type:"GET",dataType:"JSON",async:!1,cache:!1,success:function(a){w2utils.settings=$.extend(!0,w2utils.settings,a);var b=w2obj.grid.prototype;for(var c in b.buttons)b.buttons[c].caption=w2utils.lang(b.buttons[c].caption),b.buttons[c].hint=w2utils.lang(b.buttons[c].hint);b.msgDelete=w2utils.lang(b.msgDelete),b.msgNotJSON=w2utils.lang(b.msgNotJSON),b.msgRefresh=w2utils.lang(b.msgRefresh)},error:function(){console.log("ERROR: Cannot load locale "+a)}})}function A(){if(E.scrollBarSize)return E.scrollBarSize;var a='<div id="_scrollbar_width" style="position: absolute; top: -300px; width: 100px; height: 100px; overflow-y: scroll;"> <div style="height: 120px">1</div></div>';return $("body").append(a),E.scrollBarSize=100-$("#_scrollbar_width > div").width(),$("#_scrollbar_width").remove(),String(navigator.userAgent).indexOf("MSIE")>=0&&(E.scrollBarSize=E.scrollBarSize/2),E.scrollBarSize}function B(a,b){return a&&"undefined"!=typeof a.name?"undefined"!=typeof w2ui[a.name]?(console.log('ERROR: The parameter "name" is not unique. There are other objects already created with the same name (obj: '+a.name+")."),!1):w2utils.isAlphaNumeric(a.name)?!0:(console.log('ERROR: The parameter "name" has to be alpha-numeric (a-z, 0-9, dash and underscore). '),!1):(console.log('ERROR: The parameter "name" is required but not supplied in $().'+b+"()."),!1)}function C(a,b,c,d){$.isArray(b)||(b=[b]);for(var e=0;e<b.length;e++)if(b[e].id===a)return console.log('ERROR: The parameter "id='+a+'" is not unique within the current '+c+". (obj: "+d+")"),!1;return!0}function D(a){var b=[],c=a.replace(/\/\(/g,"(?:/").replace(/\+/g,"__plus__").replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g,function(a,c,d,e,f,g){return b.push({name:e,optional:!!g}),c=c||"",""+(g?"":c)+"(?:"+(g?c:"")+(d||"")+(f||d&&"([^/.]+?)"||"([^/]+?)")+")"+(g||"")}).replace(/([\/.])/g,"\\$1").replace(/__plus__/g,"(.+)").replace(/\*/g,"(.*)");return{path:new RegExp("^"+c+"$","i"),keys:b}}var E={},F={version:"1.4.0",settings:{locale:"en-us",date_format:"m/d/yyyy",date_display:"Mon d, yyyy",time_format:"h12",currencyPrefix:"$",currencySuffix:"",currencyPrecision:2,groupSymbol:",",shortmonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],fullmonths:["January","February","March","April","May","June","July","August","September","October","November","December"],shortdays:["M","T","W","T","F","S","S"],fulldays:["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],dataType:"HTTP",phrases:{}},isInt:a,isFloat:b,isMoney:c,isHex:d,isAlphaNumeric:e,isEmail:f,isDate:g,isTime:h,age:i,date:j,size:k,formatNumber:l,formatDate:m,formatTime:n,formatDateTime:o,stripTags:p,encodeTags:q,escapeId:r,base64encode:s,base64decode:t,transition:u,lock:v,unlock:w,lang:y,locale:z,getSize:x,scrollBarSize:A,checkName:B,checkUniqueId:C,parseRoute:D,isIOS:-1!=navigator.userAgent.toLowerCase().indexOf("iphone")||-1!=navigator.userAgent.toLowerCase().indexOf("ipod")||-1!=navigator.userAgent.toLowerCase().indexOf("ipad")?!0:!1,isIE:-1!=navigator.userAgent.toLowerCase().indexOf("msie")||-1!=navigator.userAgent.toLowerCase().indexOf("trident")?!0:!1};return F}();w2utils.event={on:function(a,b){return $.isPlainObject(a)||(a={type:a}),a=$.extend({type:null,execute:"before",target:null,onComplete:null},a),a.type?b?($.isArray(this.handlers)||(this.handlers=[]),void this.handlers.push({event:a,handler:b})):void console.log("ERROR: You must specify event handler function when calling .on() method of "+this.name):void console.log("ERROR: You must specify event type when calling .on() method of "+this.name)},off:function(a,b){if($.isPlainObject(a)||(a={type:a}),a=$.extend({},{type:null,execute:"before",target:null,onComplete:null},a),!a.type)return void console.log("ERROR: You must specify event type when calling .off() method of "+this.name);b||(b=null);for(var c=[],d=0,e=this.handlers.length;e>d;d++){var f=this.handlers[d];(f.event.type!==a.type&&"*"!==a.type||f.event.target!==a.target&&null!==a.target||f.handler!==b&&null!==b)&&c.push(f)}this.handlers=c},trigger:function(a){var a=$.extend({type:null,phase:"before",target:null},a,{isStopped:!1,isCancelled:!1,preventDefault:function(){this.isCancelled=!0},stopPropagation:function(){this.isStopped=!0}});"before"===a.phase&&(a.onComplete=null);var b,c,d;null==a.target&&(a.target=null),$.isArray(this.handlers)||(this.handlers=[]);for(var e=this.handlers.length-1;e>=0;e--){var f=this.handlers[e];if(!(f.event.type!==a.type&&"*"!==f.event.type||f.event.target!==a.target&&null!==f.event.target||f.event.execute!==a.phase&&"*"!==f.event.execute&&"*"!==f.event.phase)&&(a=$.extend({},f.event,a),b=[],d=RegExp(/\((.*?)\)/).exec(f.handler),d&&(b=d[1].split(/\s*,\s*/)),2===b.length?f.handler.call(this,a.target,a):f.handler.call(this,a),a.isStopped===!0||a.stop===!0))return a}var g="on"+a.type.substr(0,1).toUpperCase()+a.type.substr(1);return"before"===a.phase&&"function"==typeof this[g]&&(c=this[g],b=[],d=RegExp(/\((.*?)\)/).exec(c),d&&(b=d[1].split(/\s*,\s*/)),2===b.length?c.call(this,a.target,a):c.call(this,a),a.isStopped===!0||a.stop===!0)?a:null!=a.object&&"before"===a.phase&&"function"==typeof a.object[g]&&(c=a.object[g],b=[],d=RegExp(/\((.*?)\)/).exec(c),d&&(b=d[1].split(/\s*,\s*/)),2===b.length?c.call(this,a.target,a):c.call(this,a),a.isStopped===!0||a.stop===!0)?a:("after"===a.phase&&"function"==typeof a.onComplete&&a.onComplete.call(this,a),a)}},w2utils.keyboard=function(a){function b(){$(document).on("keydown",c),$(document).on("mousedown",d)}function c(a){var b=a.target.tagName;-1===$.inArray(b,["INPUT","SELECT","TEXTAREA"])&&"true"!==$(a.target).prop("contenteditable")&&g&&w2ui[g]&&"function"==typeof w2ui[g].keydown&&w2ui[g].keydown.call(w2ui[g],a)}function d(a){var b=(a.target.tagName,$(a.target).parents(".w2ui-reset"));if(b.length>0){var c=b.attr("name");w2ui[c]&&w2ui[c].keyboard&&(g=c)}}function e(a){return"undefined"!=typeof a&&(g=a),g}function f(){g=null}var g=null;return a.active=e,a.clear=f,b(),a}({}),function(){$.fn.w2render=function(a){$(this).length>0&&("string"==typeof a&&w2ui[a]&&w2ui[a].render($(this)[0]),"object"==typeof a&&a.render($(this)[0]))},$.fn.w2destroy=function(a){!a&&this.length>0&&(a=this.attr("name")),"string"==typeof a&&w2ui[a]&&w2ui[a].destroy(),"object"==typeof a&&a.destroy()},$.fn.w2marker=function(a){return $(this).each(""===a||null==a?function(a,b){b.innerHTML=b.innerHTML.replace(/\<span class=\"w2ui\-marker\"\>(.*)\<\/span\>/gi,"$1")}:function(b,c){function d(a){return'<span class="w2ui-marker">'+a+"</span>"}"string"==typeof a&&(a=[a]),c.innerHTML=c.innerHTML.replace(/\<span class=\"w2ui\-marker\"\>(.*)\<\/span\>/gi,"$1");for(var e in a){var f=a[e];"string"!=typeof f&&(f=String(f)),f=f.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&").replace(/&/g,"&amp;").replace(/</g,"&gt;").replace(/>/g,"&lt;");var g=new RegExp(f+"(?!([^<]+)?>)","gi");c.innerHTML=c.innerHTML.replace(g,d)}})},$.fn.w2tag=function(a,b){return $.isPlainObject(b)||(b={}),$.isPlainObject(b.css)||(b.css={}),"undefined"==typeof b["class"]&&(b["class"]=""),0===$(this).length?void $(".w2ui-tag").each(function(a,b){var c=$(b).data("options");null==c&&(c={}),$($(b).data("taged-el")).removeClass(c["class"]),clearInterval($(b).data("timer")),$(b).remove()}):$(this).each(function(c,d){function e(){$tag=$("#w2ui-tag-"+g),$tag.length<=0||(clearInterval($tag.data("timer")),$tag.remove(),$(d).off("keypress",e).removeClass(b["class"]),$(d).length>0&&($(d)[0].style.cssText=i),"function"==typeof b.onHide&&b.onHide())}var f=d.id,g=w2utils.escapeId(d.id);if(""===a||null==a)$("#w2ui-tag-"+g).css("opacity",0),setTimeout(function(){clearInterval($("#w2ui-tag-"+g).data("timer")),$("#w2ui-tag-"+g).remove()},300);else{clearInterval($("#w2ui-tag-"+g).data("timer")),$("#w2ui-tag-"+g).remove(),$("body").append('<div id="w2ui-tag-'+f+'" class="w2ui-tag '+($(d).parents(".w2ui-popup").length>0?"w2ui-tag-popup":"")+'" style=""></div>');var h=setInterval(function(){return 0===$(d).length||0===$(d).offset().left&&0===$(d).offset().top?(clearInterval($("#w2ui-tag-"+g).data("timer")),void e()):void($("#w2ui-tag-"+g).data("position")!==$(d).offset().left+d.offsetWidth+"x"+$(d).offset().top&&$("#w2ui-tag-"+g).css({"-webkit-transition":".2s","-moz-transition":".2s","-ms-transition":".2s","-o-transition":".2s",left:$(d).offset().left+d.offsetWidth+"px",top:$(d).offset().top+"px"}).data("position",$(d).offset().left+d.offsetWidth+"x"+$(d).offset().top))},100);setTimeout(function(){$(d).offset()&&($("#w2ui-tag-"+g).css({opacity:"1",left:$(d).offset().left+d.offsetWidth+"px",top:$(d).offset().top+"px"}).html('<div style="margin-top: -2px 0px 0px -2px; white-space: nowrap;"> <div class="w2ui-tag-body">'+a+"</div> </div>").data("text",a).data("taged-el",d).data("options",b).data("position",$(d).offset().left+d.offsetWidth+"x"+$(d).offset().top).data("timer",h),$(d).off("keypress",e).on("keypress",e).off("change",e).on("change",e).css(b.css).addClass(b["class"]),"function"==typeof b.onShow&&b.onShow())},1);var i="";$(d).length>0&&(i=$(d)[0].style.cssText)}})},$.fn.w2overlay=function(a,b){function c(){var a=$("#w2ui-overlay"+g);if(a.data("element")===f[0]&&0!==a.length){var b=$(f).offset().left+"x"+$(f).offset().top;a.data("position")!==b?d():setTimeout(c,250)}}function d(){var a=$("#w2ui-overlay"+g);if(a.data("keepOpen")===!0)return void a.removeData("keepOpen");var c;"function"==typeof b.onHide&&(c=b.onHide()),c!==!1&&(a.remove(),$(document).off("click",d),clearInterval(a.data("timer")))}function e(){var a=$("#w2ui-overlay"+g),c=a.find(" > div");if(a.length>0){c.height("auto").width("auto");var d=!1,h=!1,i=c.height(),j=c.width();switch(b.width&&b.width<j&&(j=b.width),30>j&&(j=30),b.tmp.contentHeight&&(i=b.tmp.contentHeight,c.height(i),setTimeout(function(){c.height()>c.find("div.menu > table").height()&&c.find("div.menu").css("overflow-y","hidden")},1),setTimeout(function(){c.find("div.menu").css("overflow-y","auto")},10)),b.tmp.contentWidth&&(j=b.tmp.contentWidth,c.width(j),setTimeout(function(){c.width()>c.find("div.menu > table").width()&&c.find("div.menu").css("overflow-x","hidden")},1),setTimeout(function(){c.find("div.menu").css("overflow-y","auto")},10)),b.align){case"both":b.left=17,0===b.width&&(b.width=w2utils.getSize($(f),"width"));break;case"left":b.left=17;break;case"right":b.tipLeft=j-45,b.left=w2utils.getSize($(f),"width")-j+10}var k=(j-17)/2,l=b.left,m=b.width,n=b.tipLeft;m=30!==j||m?b.width?b.width:"auto":30,25>k&&(l=25-k,n=Math.floor(k)),a.css({top:f.offset().top+w2utils.getSize(f,"height")+b.top+7+"px",left:(f.offset().left>25?f.offset().left:25)+l+"px","min-width":m,"min-height":b.height?b.height:"auto"});var o=window.innerHeight+$(document).scrollTop()-c.offset().top-7,p=window.innerWidth+$(document).scrollLeft()-c.offset().left-7;o>-50&&210>o||b.openAbove===!0?(o=c.offset().top-$(document).scrollTop()-7,b.maxHeight&&o>b.maxHeight&&(o=b.maxHeight),i>o&&(h=!0,c.height(o).width(j).css({"overflow-y":"auto"}),i=o),a.css("top",$(f).offset().top-i-24+b.top+"px"),a.find(">style").html("#w2ui-overlay"+g+":before { display: none; margin-left: "+parseInt(n)+"px; }#w2ui-overlay"+g+":after { display: block; margin-left: "+parseInt(n)+"px; }")):(b.maxHeight&&o>b.maxHeight&&(o=b.maxHeight),i>o&&(h=!0,c.height(o).width(j).css({"overflow-y":"auto"})),a.find(">style").html("#w2ui-overlay"+g+":before { display: block; margin-left: "+parseInt(n)+"px; }#w2ui-overlay"+g+":after { display: none; margin-left: "+parseInt(n)+"px; }")),j=c.width(),p=window.innerWidth+$(document).scrollLeft()-c.offset().left-7,b.maxWidth&&p>b.maxWidth&&(p=b.maxWidth),j>p&&"both"!==b.align&&(b.align="right",setTimeout(function(){e()},1)),h&&d&&c.width(j+w2utils.scrollBarSize()+2)}}var f=this,g="",h={name:null,html:"",align:"none",left:0,top:0,tipLeft:30,width:0,height:0,maxWidth:null,maxHeight:null,style:"","class":"",onShow:null,onHide:null,openAbove:!1,tmp:{}};1==arguments.length&&(b="object"==typeof a?a:{html:a}),2==arguments.length&&(b.html=a),$.isPlainObject(b)||(b={}),b=$.extend({},h,b),b.name&&(g="-"+b.name);var i;if(0===this.length||""===b.html||null==b.html)return $("#w2ui-overlay"+g).length>0?(i=$("#w2ui-overlay"+g)[0].hide,"function"==typeof i&&i()):$("#w2ui-overlay"+g).remove(),$(this);$("#w2ui-overlay"+g).length>0&&(i=$("#w2ui-overlay"+g)[0].hide,$(document).off("click",i),"function"==typeof i&&i()),$("body").append('<div id="w2ui-overlay'+g+'" style="display: none" class="w2ui-reset w2ui-overlay '+($(this).parents(".w2ui-popup, .w2ui-overlay-popup").length>0?"w2ui-overlay-popup":"")+'"> <style></style> <div style="'+b.style+'" class="'+b["class"]+'"></div></div>');var j=$("#w2ui-overlay"+g),k=j.find(" > div");k.html(b.html);var l=k.css("background-color");return null!=l&&"rgba(0, 0, 0, 0)"!==l&&"transparent"!==l&&j.css("background-color",l),j.data("element",f.length>0?f[0]:null).data("options",b).data("position",$(f).offset().left+"x"+$(f).offset().top).fadeIn("fast").on("mousedown",function(a){$("#w2ui-overlay"+g).data("keepOpen",!0),-1===["INPUT","TEXTAREA","SELECT"].indexOf(a.target.tagName)&&a.preventDefault()}),j[0].hide=d,j[0].resize=e,e(),setTimeout(function(){e(),$(document).off("click",d).on("click",d),"function"==typeof b.onShow&&b.onShow()},10),c(),$(this)},$.fn.w2menu=function(a,b){function c(){setTimeout(function(){$("#w2ui-overlay"+h+" tr.w2ui-selected").removeClass("w2ui-selected");var a=$("#w2ui-overlay"+h+" tr[index="+b.index+"]"),c=$("#w2ui-overlay"+h+" div.menu").scrollTop();if(a.addClass("w2ui-selected"),b.tmp&&(b.tmp.contentHeight=$("#w2ui-overlay"+h+" table").height()+(b.search?50:10)),b.tmp&&(b.tmp.contentWidth=$("#w2ui-overlay"+h+" table").width()),$("#w2ui-overlay"+h).length>0&&$("#w2ui-overlay"+h)[0].resize(),a.length>0){var d=a[0].offsetTop-5,e=$("#w2ui-overlay"+h+" div.menu"),f=e.height();$("#w2ui-overlay"+h+" div.menu").scrollTop(c),(c>d||d+a.height()>c+f)&&$("#w2ui-overlay"+h+" div.menu").animate({scrollTop:d-(f-2*a.height())/2},200,"linear")}},1)}function d(a){var d=this.value,e=a.keyCode,f=!1;switch(e){case 13:$("#w2ui-overlay"+h).remove(),$.fn.w2menuHandler(a,b.index);break;case 9:case 27:$("#w2ui-overlay"+h).remove(),$.fn.w2menuHandler(a,-1);break;case 38:for(b.index=w2utils.isInt(b.index)?parseInt(b.index):0,b.index--;b.index>0&&b.items[b.index].hidden;)b.index--;if(0===b.index&&b.items[b.index].hidden)for(;b.items[b.index]&&b.items[b.index].hidden;)b.index++;b.index<0&&(b.index=0),f=!0;break;case 40:for(b.index=w2utils.isInt(b.index)?parseInt(b.index):0,b.index++;b.index<b.items.length-1&&b.items[b.index].hidden;)b.index++;if(b.index===b.items.length-1&&b.items[b.index].hidden)for(;b.items[b.index]&&b.items[b.index].hidden;)b.index--;b.index>=b.items.length&&(b.index=b.items.length-1),f=!0}if(!f){var i=0;for(var j in b.items){var k=b.items[j],l="",m="";-1!==["is","begins with"].indexOf(b.match)&&(l="^"),-1!==["is","ends with"].indexOf(b.match)&&(m="$");try{var n=new RegExp(l+d+m,"i");k.hidden=n.test(k.text)||"..."===k.text?!1:!0}catch(o){}"enum"===g.type&&-1!==$.inArray(k.id,ids)&&(k.hidden=!0),k.hidden!==!0&&i++}for(b.index=0;b.index<b.items.length-1&&b.items[b.index].hidden;)b.index++;0>=i&&(b.index=-1)}$(g).w2menu("refresh",b),c()}function e(){if(b.spinner)return'<table class="w2ui-drop-menu"><tr><td style="padding: 5px 10px 10px 10px; text-align: center"> <div class="w2ui-spinner" style="width: 18px; height: 18px; position: relative; top: 5px;"></div> <div style="display: inline-block; padding: 3px; color: #999;"> Loading...</div></td></tr></table>';for(var a=0,c='<table cellspacing="0" cellpadding="0" class="w2ui-drop-menu">',d=null,e=null,f=0;f<b.items.length;f++){var g=b.items[f];
+if("string"==typeof g?g={id:g,text:g}:(null!=g.text&&null==g.id&&(g.id=g.text),null==g.text&&null!=g.id&&(g.text=g.id),null!=g.caption&&(g.text=g.caption),d=g.img,e=g.icon,null==d&&(d=null),null==e&&(e=null)),g.hidden!==!0){var i="",j=g.text;if("function"==typeof b.render&&(j=b.render(g,b)),d&&(i='<td class="menu-icon"><div class="w2ui-tb-image w2ui-icon '+d+'"></div></td>'),e&&(i='<td class="menu-icon" align="center"><span class="w2ui-icon '+e+'"></span></td>'),"undefined"==typeof j||""===j||/^-+$/.test(j))c+='<tr><td colspan="2" style="padding: 6px; pointer-events: none"><div style="border-top: 1px solid silver;"></div></td></tr>';else{var k=a%2===0?"w2ui-item-even":"w2ui-item-odd";b.altRows!==!0&&(k="");var l=1;""==i&&l++,null==g.count&&l++,c+='<tr index="'+f+'" style="'+(g.style?g.style:"")+'" class="'+k+" "+(b.index===f?"w2ui-selected":"")+" "+(g.disabled===!0?"w2ui-disabled":"")+"\" onmousedown=\"$(this).parent().find('tr').removeClass('w2ui-selected'); $(this).addClass('w2ui-selected');\" onclick=\"event.stopPropagation(); if ("+(g.disabled===!0?"true":"false")+") return; $('#w2ui-overlay"+h+"').remove(); $.fn.w2menuHandler(event, '"+f+"');\">"+i+' <td class="menu-text" colspan="'+l+'">'+j+'</td> <td class="menu-count">'+(null!=g.count?"<span>"+g.count+"</span>":"")+"</td></tr>",a++}}b.items[f]=g}return 0===a&&(c+='<tr><td style="padding: 13px; color: #999; text-align: center">'+b.msgNoItems+"</div></td></tr>"),c+="</table>"}var f={index:null,items:[],render:null,msgNoItems:"No items",onSelect:null,tmp:{}},g=this,h="";if("refresh"!==a){1===arguments.length?b=a:b.items=a,"object"!=typeof b&&(b={}),b=$.extend({},f,b),$.fn.w2menuOptions=b,b.name&&(h="-"+b.name),"function"==typeof b.select&&"function"!=typeof b.onSelect&&(b.onSelect=b.select),"function"==typeof b.onRender&&"function"!=typeof b.render&&(b.render=b.onRender),$.fn.w2menuHandler=function(a,c){"function"==typeof b.onSelect&&setTimeout(function(){b.onSelect({index:c,item:b.items[c],originalEvent:a})},10),setTimeout(function(){$(document).click()},50)};var i="";if(b.search){i+='<div style="position: absolute; top: 0px; height: 40px; left: 0px; right: 0px; border-bottom: 1px solid silver; background-color: #ECECEC; padding: 8px 5px;"> <div class="w2ui-icon icon-search" style="position: absolute; margin-top: 4px; margin-left: 6px; width: 11px; background-position: left !important;"></div> <input id="menu-search" type="text" style="width: 100%; outline: none; padding-left: 20px;" onclick="event.stopPropagation();"></div>',b.style+=";background-color: #ECECEC",b.index=0;for(var j in b.items)b.items[j].hidden=!1}i+='<div class="menu" style="position: absolute; top: '+(b.search?40:0)+'px; bottom: 0px; width: 100%; overflow: auto;">'+e()+"</div>";var k=$(this).w2overlay(i,b);return setTimeout(function(){if($("#w2ui-overlay"+h+" #menu-search").on("keyup",d).on("keydown",function(a){9===a.keyCode&&(a.stopPropagation(),a.preventDefault())}),b.search){if(-1!=["text","password"].indexOf($(g)[0].type)||"texarea"==$(g)[0].tagName)return;$("#w2ui-overlay"+h+" #menu-search").focus()}},200),c(),k}if($("#w2ui-overlay"+h).length>0){b=$.extend($.fn.w2menuOptions,b);var l=$("#w2ui-overlay"+h+" div.menu").scrollTop();$("#w2ui-overlay"+h+" div.menu").html(e()),$("#w2ui-overlay"+h+" div.menu").scrollTop(l),c()}else $(this).w2menu(b)}}(),function(){var w2grid=function(a){this.name=null,this.box=null,this.header="",this.url="",this.routeData={},this.columns=[],this.columnGroups=[],this.records=[],this.summary=[],this.searches=[],this.searchData=[],this.sortData=[],this.postData={},this.toolbar={},this.show={header:!1,toolbar:!1,footer:!1,columnHeaders:!0,lineNumbers:!1,expandColumn:!1,selectColumn:!1,emptyRecords:!0,toolbarReload:!0,toolbarColumns:!0,toolbarSearch:!0,toolbarAdd:!1,toolbarEdit:!1,toolbarDelete:!1,toolbarSave:!1,selectionBorder:!0,recordTitles:!0,skipRecords:!0},this.autoLoad=!0,this.fixedBody=!0,this.recordHeight=24,this.keyboard=!0,this.selectType="row",this.multiSearch=!0,this.multiSelect=!0,this.multiSort=!0,this.reorderColumns=!1,this.reorderRows=!1,this.markSearch=!0,this.total=0,this.limit=100,this.offset=0,this.style="",this.ranges=[],this.menu=[],this.method=null,this.recid=null,this.parser=null,this.onAdd=null,this.onEdit=null,this.onRequest=null,this.onLoad=null,this.onDelete=null,this.onDeleted=null,this.onSubmit=null,this.onSave=null,this.onSelect=null,this.onUnselect=null,this.onClick=null,this.onDblClick=null,this.onContextMenu=null,this.onMenuClick=null,this.onColumnClick=null,this.onColumnResize=null,this.onSort=null,this.onSearch=null,this.onChange=null,this.onRestore=null,this.onExpand=null,this.onCollapse=null,this.onError=null,this.onKeydown=null,this.onToolbar=null,this.onColumnOnOff=null,this.onCopy=null,this.onPaste=null,this.onSelectionExtend=null,this.onEditField=null,this.onRender=null,this.onRefresh=null,this.onReload=null,this.onResize=null,this.onDestroy=null,this.onStateSave=null,this.onStateRestore=null,this.last={field:"all",caption:w2utils.lang("All Fields"),logic:"OR",search:"",searchIds:[],selection:{indexes:[],columns:{}},multi:!1,scrollTop:0,scrollLeft:0,sortData:null,sortCount:0,xhr:null,range_start:null,range_end:null,sel_ind:null,sel_col:null,sel_type:null,edit_col:null},$.extend(!0,this,w2obj.grid,a)};$.fn.w2grid=function(a){if("object"==typeof a||!a){if(!w2utils.checkName(a,"w2grid"))return;var b=a.columns,c=a.columnGroups,d=a.records,e=a.searches,f=a.searchData,g=a.sortData,h=a.postData,i=a.toolbar,j=new w2grid(a);$.extend(j,{postData:{},records:[],columns:[],searches:[],toolbar:{},sortData:[],searchData:[],handlers:[]}),null!=j.onExpand&&(j.show.expandColumn=!0),$.extend(!0,j.toolbar,i);for(var k in b)j.columns[k]=$.extend(!0,{},b[k]);for(var k in c)j.columnGroups[k]=$.extend(!0,{},c[k]);for(var k in e)j.searches[k]=$.extend(!0,{},e[k]);for(var k in f)j.searchData[k]=$.extend(!0,{},f[k]);for(var k in g)j.sortData[k]=$.extend(!0,{},g[k]);j.postData=$.extend(!0,{},h);for(var l in d){if(null==d[l].recid||"undefined"==typeof d[l].recid)return void console.log("ERROR: Cannot add records without recid. (obj: "+j.name+")");j.records[l]=$.extend(!0,{},d[l])}for(var m in j.columns){var n=j.columns[m];if("undefined"!=typeof n.searchable&&null==j.getSearch(n.field)){var o=n.searchable,p="";n.searchable===!0&&(o="text",p='size="20"'),j.addSearch({field:n.field,caption:n.caption,type:o,attr:p})}}return j.initToolbar(),0!==$(this).length&&j.render($(this)[0]),w2ui[j.name]=j,j}if(w2ui[$(this).attr("name")]){var q=w2ui[$(this).attr("name")];return q[a].apply(q,Array.prototype.slice.call(arguments,1)),this}console.log("ERROR: Method "+a+" does not exist on jQuery.w2grid")},w2grid.prototype={msgDelete:"Are you sure you want to delete selected records?",msgNotJSON:"Returned data is not in valid JSON format.",msgAJAXerror:"AJAX error. See console for more details.",msgRefresh:"Refreshing...",buttons:{reload:{type:"button",id:"w2ui-reload",icon:"w2ui-icon-reload",hint:"Reload data in the list"},columns:{type:"drop",id:"w2ui-column-on-off",icon:"w2ui-icon-columns",hint:"Show/hide columns",arrow:!1,html:""},search:{type:"html",id:"w2ui-search",html:'<div class="w2ui-icon icon-search-down w2ui-search-down" title="Select Search Field" onclick="var obj = w2ui[$(this).parents(\'div.w2ui-grid\').attr(\'name\')]; obj.searchShowFields();"></div>'},"search-go":{type:"check",id:"w2ui-search-advanced",caption:"Search...",hint:"Open Search Fields"},add:{type:"button",id:"w2ui-add",caption:"Add New",hint:"Add new record",icon:"w2ui-icon-plus"},edit:{type:"button",id:"w2ui-edit",caption:"Edit",hint:"Edit selected record",icon:"w2ui-icon-pencil",disabled:!0},"delete":{type:"button",id:"w2ui-delete",caption:"Delete",hint:"Delete selected records",icon:"w2ui-icon-cross",disabled:!0},save:{type:"button",id:"w2ui-save",caption:"Save",hint:"Save changed records",icon:"w2ui-icon-check"}},add:function(a){$.isArray(a)||(a=[a]);var b=0;for(var c in a)this.recid||"undefined"!=typeof a[c].recid||(a[c].recid=a[c][this.recid]),null!=a[c].recid&&"undefined"!=typeof a[c].recid?(this.records.push(a[c]),b++):console.log("ERROR: Cannot add record without recid. (obj: "+this.name+")");var d="object"!=typeof this.url?this.url:this.url.get;return d||(this.total=this.records.length,this.localSort(),this.localSearch()),this.refresh(),b},find:function(a,b){("undefined"==typeof a||null==a)&&(a={});var c=[],d=!1;for(var e in a)-1!=String(e).indexOf(".")&&(d=!0);for(var f=0;f<this.records.length;f++){var g=!0;for(var e in a){var h=this.records[f][e];d&&-1!=String(e).indexOf(".")&&(h=this.parseField(this.records[f],e)),a[e]!=h&&(g=!1)}g&&b!==!0&&c.push(this.records[f].recid),g&&b===!0&&c.push(f)}return c},set:function(a,b,c){if("object"==typeof a&&(c=b,b=a,a=null),null==a){for(var d in this.records)$.extend(!0,this.records[d],b);c!==!0&&this.refresh()}else{var e=this.get(a,!0);if(null==e)return!1;$.extend(!0,this.records[e],b),c!==!0&&this.refreshRow(a)}return!0},get:function(a,b){for(var c=0;c<this.records.length;c++)if(this.records[c].recid==a)return b===!0?c:this.records[c];return null},remove:function(){for(var a=0,b=0;b<arguments.length;b++)for(var c=this.records.length-1;c>=0;c--)this.records[c].recid==arguments[b]&&(this.records.splice(c,1),a++);var d="object"!=typeof this.url?this.url:this.url.get;return d||(this.localSort(),this.localSearch()),this.refresh(),a},addColumn:function(a,b){var c=0;1==arguments.length?(b=a,a=this.columns.length):("string"==typeof a&&(a=this.getColumn(a,!0)),null===a&&(a=this.columns.length)),$.isArray(b)||(b=[b]);for(var d in b)this.columns.splice(a,0,b[d]),a++,c++;return this.refresh(),c},removeColumn:function(){for(var a=0,b=0;b<arguments.length;b++)for(var c=this.columns.length-1;c>=0;c--)this.columns[c].field==arguments[b]&&(this.columns.splice(c,1),a++);return this.refresh(),a},getColumn:function(a,b){for(var c=0;c<this.columns.length;c++)if(this.columns[c].field==a)return b===!0?c:this.columns[c];return null},toggleColumn:function(){for(var a=0,b=0;b<arguments.length;b++)for(var c=this.columns.length-1;c>=0;c--){var d=this.columns[c];d.field==arguments[b]&&(d.hidden=!d.hidden,a++)}return this.refresh(),a},showColumn:function(){for(var a=0,b=0;b<arguments.length;b++)for(var c=this.columns.length-1;c>=0;c--){var d=this.columns[c];d.gridMinWidth&&delete d.gridMinWidth,d.field==arguments[b]&&d.hidden!==!1&&(d.hidden=!1,a++)}return this.refresh(),a},hideColumn:function(){for(var a=0,b=0;b<arguments.length;b++)for(var c=this.columns.length-1;c>=0;c--){var d=this.columns[c];d.field==arguments[b]&&d.hidden!==!0&&(d.hidden=!0,a++)}return this.refresh(),a},addSearch:function(a,b){var c=0;1==arguments.length?(b=a,a=this.searches.length):("string"==typeof a&&(a=this.getSearch(a,!0)),null===a&&(a=this.searches.length)),$.isArray(b)||(b=[b]);for(var d in b)this.searches.splice(a,0,b[d]),a++,c++;return this.searchClose(),c},removeSearch:function(){for(var a=0,b=0;b<arguments.length;b++)for(var c=this.searches.length-1;c>=0;c--)this.searches[c].field==arguments[b]&&(this.searches.splice(c,1),a++);return this.searchClose(),a},getSearch:function(a,b){for(var c=0;c<this.searches.length;c++)if(this.searches[c].field==a)return b===!0?c:this.searches[c];return null},toggleSearch:function(){for(var a=0,b=0;b<arguments.length;b++)for(var c=this.searches.length-1;c>=0;c--)this.searches[c].field==arguments[b]&&(this.searches[c].hidden=!this.searches[c].hidden,a++);return this.searchClose(),a},showSearch:function(){for(var a=0,b=0;b<arguments.length;b++)for(var c=this.searches.length-1;c>=0;c--)this.searches[c].field==arguments[b]&&this.searches[c].hidden!==!1&&(this.searches[c].hidden=!1,a++);return this.searchClose(),a},hideSearch:function(){for(var a=0,b=0;b<arguments.length;b++)for(var c=this.searches.length-1;c>=0;c--)this.searches[c].field==arguments[b]&&this.searches[c].hidden!==!0&&(this.searches[c].hidden=!0,a++);return this.searchClose(),a},getSearchData:function(a){for(var b in this.searchData)if(this.searchData[b].field==a)return this.searchData[b];return null},localSort:function(a){var b="object"!=typeof this.url?this.url:this.url.get;if(b)return void console.log("ERROR: grid.localSort can only be used on local data source, grid.url should be empty.");if(!$.isEmptyObject(this.sortData)){var c=(new Date).getTime(),d=this;d.prepareData();for(var e in this.sortData){var f=this.getColumn(this.sortData[e].field);if(!f)return;f.render&&-1!=["date","age"].indexOf(f.render.split(":")[0])&&(this.sortData[e].field_=f.field+"_"),f.render&&-1!=["time"].indexOf(f.render.split(":")[0])&&(this.sortData[e].field_=f.field+"_")}return this.records.sort(function(a,b){var c=0;for(var e in d.sortData){var f=d.sortData[e].field;d.sortData[e].field_&&(f=d.sortData[e].field_);var g=a[f],h=b[f];if(-1!=String(f).indexOf(".")&&(g=d.parseField(a,f),h=d.parseField(b,f)),"string"==typeof g&&(g=$.trim(g.toLowerCase())),"string"==typeof h&&(h=$.trim(h.toLowerCase())),g>h&&(c="asc"==d.sortData[e].direction?1:-1),h>g&&(c="asc"==d.sortData[e].direction?-1:1),"object"!=typeof g&&"object"==typeof h&&(c=-1),"object"!=typeof h&&"object"==typeof g&&(c=1),null==g&&null!=h&&(c=1),null!=g&&null==h&&(c=-1),0!=c)break}return c}),c=(new Date).getTime()-c,a!==!0&&setTimeout(function(){d.status("Sorting took "+c/1e3+" sec")},10),c}},localSearch:function(a){var b="object"!=typeof this.url?this.url:this.url.get;if(b)return void console.log("ERROR: grid.localSearch can only be used on local data source, grid.url should be empty.");var c=(new Date).getTime(),d=this;if(this.total=this.records.length,this.last.searchIds=[],this.prepareData(),this.searchData.length>0&&!b){this.total=0;for(var e in this.records){var f=this.records[e],g=0;for(var h in this.searchData){var i=this.searchData[h],j=this.getSearch(i.field);if(null!=i){null==j&&(j={field:i.field,type:i.type});var k=String(d.parseField(f,j.field)).toLowerCase();if("undefined"!=typeof i.value)if($.isArray(i.value))var l=i.value[0],m=i.value[1];else var l=String(i.value).toLowerCase();switch(i.operator){case"is":if(f[j.field]==i.value&&g++,"date"==j.type){var k=w2utils.formatDate(f[j.field+"_"],"yyyy-mm-dd"),l=w2utils.formatDate(l,"yyyy-mm-dd");k==l&&g++}if("time"==j.type){var k=w2utils.formatTime(f[j.field+"_"],"h24:mi"),l=w2utils.formatTime(l,"h24:mi");k==l&&g++}break;case"between":if(-1!=["int","float","money","currency","percent"].indexOf(j.type)&&parseFloat(f[j.field])>=parseFloat(l)&&parseFloat(f[j.field])<=parseFloat(m)&&g++,"date"==j.type){var k=f[j.field+"_"],l=w2utils.isDate(l,w2utils.settings.date_format,!0),m=w2utils.isDate(m,w2utils.settings.date_format,!0);null!=m&&(m=new Date(m.getTime()+864e5)),k>=l&&m>k&&g++}if("time"==j.type){var k=f[j.field+"_"],l=w2utils.isTime(l,!0),m=w2utils.isTime(m,!0);l=(new Date).setHours(l.hours,l.minutes,l.seconds?l.seconds:0,0),m=(new Date).setHours(m.hours,m.minutes,m.seconds?m.seconds:0,0),k>=l&&m>k&&g++}break;case"in":var n=i.value;i.svalue&&(n=i.svalue),-1!==n.indexOf(k)&&g++;break;case"not in":var n=i.value;i.svalue&&(n=i.svalue),-1==n.indexOf(k)&&g++;break;case"begins":case"begins with":0==k.indexOf(l)&&g++;break;case"contains":k.indexOf(l)>=0&&g++;break;case"ends":case"ends with":k.indexOf(l)==k.length-l.length&&g++}}}("OR"==this.last.logic&&0!=g||"AND"==this.last.logic&&g==this.searchData.length)&&this.last.searchIds.push(parseInt(e))}this.total=this.last.searchIds.length}return c=(new Date).getTime()-c,a!==!0&&setTimeout(function(){d.status("Search took "+c/1e3+" sec")},10),c},getRangeData:function(a,b){var c=this.get(a[0].recid,!0),d=this.get(a[1].recid,!0),e=a[0].column,f=a[1].column,g=[];if(e==f)for(var h=c;d>=h;h++){var i=this.records[h],j=i[this.columns[e].field]||null;g.push(b!==!0?j:{data:j,column:e,index:h,record:i})}else if(c==d)for(var i=this.records[c],k=e;f>=k;k++){var j=i[this.columns[k].field]||null;g.push(b!==!0?j:{data:j,column:k,index:c,record:i})}else for(var h=c;d>=h;h++){var i=this.records[h];g.push([]);for(var k=e;f>=k;k++){var j=i[this.columns[k].field];g[g.length-1].push(b!==!0?j:{data:j,column:k,index:h,record:i})}}return g},addRange:function(a){var b=0;if("row"==this.selectType)return b;$.isArray(a)||(a=[a]);for(var c in a){if("object"!=typeof a[c]&&(a[c]={name:"selection"}),"selection"==a[c].name){if(this.show.selectionBorder===!1)continue;var d=this.getSelection();if(0==d.length){this.removeRange(a[c].name);continue}{var e=d[0],f=d[d.length-1];$("#grid_"+this.name+"_rec_"+e.recid+" td[col="+e.column+"]"),$("#grid_"+this.name+"_rec_"+f.recid+" td[col="+f.column+"]")}}else{var e=a[c].range[0],f=a[c].range[1];$("#grid_"+this.name+"_rec_"+e.recid+" td[col="+e.column+"]"),$("#grid_"+this.name+"_rec_"+f.recid+" td[col="+f.column+"]")}if(e){var g={name:a[c].name,range:[{recid:e.recid,column:e.column},{recid:f.recid,column:f.column}],style:a[c].style||""},h=!1;for(var i in this.ranges)if(this.ranges[i].name==a[c].name){h=c;break}h!==!1?this.ranges[h]=g:this.ranges.push(g),b++}}return this.refreshRanges(),b},removeRange:function(){for(var a=0,b=0;b<arguments.length;b++){var c=arguments[b];$("#grid_"+this.name+"_"+c).remove();for(var d=this.ranges.length-1;d>=0;d--)this.ranges[d].name==c&&(this.ranges.splice(d,1),a++)}return a},refreshRanges:function(){function a(a){var e=d.getSelection();d.last.move={type:"expand",x:a.screenX,y:a.screenY,divX:0,divY:0,recid:e[0].recid,column:e[0].column,originalRange:[{recid:e[0].recid,column:e[0].column},{recid:e[e.length-1].recid,column:e[e.length-1].column}],newRange:[{recid:e[0].recid,column:e[0].column},{recid:e[e.length-1].recid,column:e[e.length-1].column}]},$(document).off("mousemove",b).on("mousemove",b),$(document).off("mouseup",c).on("mouseup",c)}function b(a){var b=d.last.move;if(b&&"expand"==b.type){b.divX=a.screenX-b.x,b.divY=a.screenY-b.y;var c,e,f=a.originalEvent.target;if("TD"!=f.tagName&&(f=$(f).parents("td")[0]),"undefined"!=typeof $(f).attr("col")&&(e=parseInt($(f).attr("col"))),f=$(f).parents("tr")[0],c=$(f).attr("recid"),b.newRange[1].recid!=c||b.newRange[1].column!=e){var g=$.extend({},b.newRange);return b.newRange=[{recid:b.recid,column:b.column},{recid:c,column:e}],m=d.trigger($.extend(m,{originalRange:b.originalRange,newRange:b.newRange})),m.isCancelled===!0?(b.newRange=g,void(m.newRange=g)):(d.removeRange("grid-selection-expand"),void d.addRange({name:"grid-selection-expand",range:m.newRange,style:"background-color: rgba(100,100,100,0.1); border: 2px dotted rgba(100,100,100,0.5);"}))}}}function c(){d.removeRange("grid-selection-expand"),delete d.last.move,$(document).off("mousemove",b),$(document).off("mouseup",c),d.trigger($.extend(m,{phase:"after"}))}var d=this,e=(new Date).getTime(),f=$("#grid_"+this.name+"_records");for(var g in this.ranges){var h=this.ranges[g],i=h.range[0],j=h.range[1],k=$("#grid_"+this.name+"_rec_"+i.recid+" td[col="+i.column+"]"),l=$("#grid_"+this.name+"_rec_"+j.recid+" td[col="+j.column+"]");0==$("#grid_"+this.name+"_"+h.name).length?f.append('<div id="grid_'+this.name+"_"+h.name+'" class="w2ui-selection" style="'+h.style+'">'+("selection"==h.name?'<div id="grid_'+this.name+'_resizer" class="w2ui-selection-resizer"></div>':"")+"</div>"):$("#grid_"+this.name+"_"+h.name).attr("style",h.style),k.length>0&&l.length>0&&$("#grid_"+this.name+"_"+h.name).css({left:k.position().left-1+f.scrollLeft()+"px",top:k.position().top-1+f.scrollTop()+"px",width:l.position().left-k.position().left+l.width()+3+"px",height:l.position().top-k.position().top+l.height()+3+"px"})}$(this.box).find("#grid_"+this.name+"_resizer").off("mousedown").on("mousedown",a);var m={phase:"before",type:"selectionExtend",target:d.name,originalRange:null,newRange:null};return(new Date).getTime()-e},select:function(){var a=0,b=this.last.selection;this.multiSelect||this.selectNone();for(var c=0;c<arguments.length;c++){var d="object"==typeof arguments[c]?arguments[c].recid:arguments[c],e=this.get(d);if(null!=e){var f=this.get(d,!0),g=$("#grid_"+this.name+"_rec_"+w2utils.escapeId(d));if("row"==this.selectType){if(b.indexes.indexOf(f)>=0)continue;var h=this.trigger({phase:"before",type:"select",target:this.name,recid:d,index:f});if(h.isCancelled===!0)continue;b.indexes.push(f),b.indexes.sort(function(a,b){return a-b}),g.addClass("w2ui-selected").data("selected","yes"),g.find(".w2ui-grid-select-check").prop("checked",!0),a++}else{var i=arguments[c].column;if(!w2utils.isInt(i)){var j=[];for(var k in this.columns)this.columns[k].hidden||j.push({recid:d,column:parseInt(k)});return this.multiSelect||(j=j.splice(0,1)),this.select.apply(this,j)}var l=b.columns[f]||[];if($.isArray(l)&&-1!=l.indexOf(i))continue;var h=this.trigger({phase:"before",type:"select",target:this.name,recid:d,index:f,column:i});if(h.isCancelled===!0)continue;-1==b.indexes.indexOf(f)&&(b.indexes.push(f),b.indexes.sort(function(a,b){return a-b})),l.push(i),l.sort(function(a,b){return a-b}),g.find(" > td[col="+i+"]").addClass("w2ui-selected"),a++,g.data("selected","yes"),g.find(".w2ui-grid-select-check").prop("checked",!0),b.columns[f]=l}this.trigger($.extend(h,{phase:"after"}))}}return b.indexes.length==this.records.length||0!==this.searchData.length&&b.indexes.length==this.last.searchIds.length?$("#grid_"+this.name+"_check_all").prop("checked",!0):$("#grid_"+this.name+"_check_all").prop("checked",!1),this.status(),this.addRange("selection"),a},unselect:function(){for(var a=0,b=this.last.selection,c=0;c<arguments.length;c++){var d="object"==typeof arguments[c]?arguments[c].recid:arguments[c],e=this.get(d);if(null!=e){var f=this.get(e.recid,!0),g=$("#grid_"+this.name+"_rec_"+w2utils.escapeId(d));if("row"==this.selectType){if(-1==b.indexes.indexOf(f))continue;var h=this.trigger({phase:"before",type:"unselect",target:this.name,recid:d,index:f});if(h.isCancelled===!0)continue;b.indexes.splice(b.indexes.indexOf(f),1),g.removeClass("w2ui-selected").removeData("selected"),0!=g.length&&(g[0].style.cssText="height: "+this.recordHeight+"px; "+g.attr("custom_style")),g.find(".w2ui-grid-select-check").prop("checked",!1),a++}else{var i=arguments[c].column;if(!w2utils.isInt(i)){var j=[];for(var k in this.columns)this.columns[k].hidden||j.push({recid:d,column:parseInt(k)});return this.unselect.apply(this,j)}var l=b.columns[f];if(!$.isArray(l)||-1==l.indexOf(i))continue;var h=this.trigger({phase:"before",type:"unselect",target:this.name,recid:d,column:i});if(h.isCancelled===!0)continue;l.splice(l.indexOf(i),1),$("#grid_"+this.name+"_rec_"+w2utils.escapeId(d)+" > td[col="+i+"]").removeClass("w2ui-selected"),a++,0==l.length&&(delete b.columns[f],b.indexes.splice(b.indexes.indexOf(f),1),g.removeData("selected"),g.find(".w2ui-grid-select-check").prop("checked",!1))}this.trigger($.extend(h,{phase:"after"}))}}return b.indexes.length==this.records.length||0!==this.searchData.length&&b.indexes.length==this.last.searchIds.length?$("#grid_"+this.name+"_check_all").prop("checked",!0):$("#grid_"+this.name+"_check_all").prop("checked",!1),this.status(),this.addRange("selection"),a},selectAll:function(){if(this.multiSelect!==!1){var a=this.trigger({phase:"before",type:"select",target:this.name,all:!0});if(a.isCancelled!==!0){var b="object"!=typeof this.url?this.url:this.url.get,c=this.last.selection,d=[];for(var e in this.columns)d.push(parseInt(e));if(c.indexes=[],b||0===this.searchData.length){var f=this.records.length;0==this.searchData.length||this.url||(f=this.last.searchIds.length);for(var g=0;f>g;g++)c.indexes.push(g),"row"!=this.selectType&&(c.columns[g]=d.slice())}else for(var g=0;g<this.last.searchIds.length;g++)c.indexes.push(this.last.searchIds[g]),"row"!=this.selectType&&(c.columns[this.last.searchIds[g]]=d.slice());this.refresh();var c=this.getSelection();1==c.length?this.toolbar.enable("w2ui-edit"):this.toolbar.disable("w2ui-edit"),c.length>=1?this.toolbar.enable("w2ui-delete"):this.toolbar.disable("w2ui-delete"),this.addRange("selection"),this.trigger($.extend(a,{phase:"after"}))}}},selectNone:function(){var a=this.trigger({phase:"before",type:"unselect",target:this.name,all:!0});if(a.isCancelled!==!0){var b=this.last.selection;for(var c in b.indexes){var d=b.indexes[c],e=this.records[d],f=e?e.recid:null,g=$("#grid_"+this.name+"_rec_"+w2utils.escapeId(f));if(g.removeClass("w2ui-selected").removeData("selected"),g.find(".w2ui-grid-select-check").prop("checked",!1),"row"!=this.selectType){var h=b.columns[d];for(var i in h)g.find(" > td[col="+h[i]+"]").removeClass("w2ui-selected")}}b.indexes=[],b.columns={},this.toolbar.disable("w2ui-edit","w2ui-delete"),this.removeRange("selection"),$("#grid_"+this.name+"_check_all").prop("checked",!1),this.trigger($.extend(a,{phase:"after"}))}},getSelection:function(a){var b=[],c=this.last.selection;if("row"==this.selectType){for(var d in c.indexes)this.records[c.indexes[d]]&&b.push(a===!0?c.indexes[d]:this.records[c.indexes[d]].recid);return b}for(var d in c.indexes){var e=c.columns[c.indexes[d]];if(this.records[c.indexes[d]])for(var f in e)b.push({recid:this.records[c.indexes[d]].recid,index:parseInt(c.indexes[d]),column:e[f]})}return b},search:function(a,b){var c="object"!=typeof this.url?this.url:this.url.get,d=[],e=this.last.multi,f=this.last.logic,g=this.last.field,h=this.last.search;if(0==arguments.length){h="";for(var i in this.searches){var j=this.searches[i],k=$("#grid_"+this.name+"_operator_"+i).val(),l=$("#grid_"+this.name+"_field_"+i),m=$("#grid_"+this.name+"_field2_"+i),n=l.val(),o=m.val(),p=null;if(-1!=["int","float","money","currency","percent"].indexOf(j.type)){var q=l.data("w2field"),r=m.data("w2field");q&&(n=q.clean(n)),r&&(o=r.clean(o))}if(-1!=["list","enum"].indexOf(j.type))if(n=l.data("selected")||{},$.isArray(n)){p=[];for(var s in n)p.push(w2utils.isFloat(n[s].id)?parseFloat(n[s].id):String(n[s].id).toLowerCase()),delete n[s].hidden}else n=n.id||"";if(""!=n&&null!=n||"undefined"!=typeof o&&""!=o){var t={field:j.field,type:j.type,operator:k};"between"==k?$.extend(t,{value:[n,o]}):"in"==k&&"string"==typeof n?$.extend(t,{value:n.split(",")}):"not in"==k&&"string"==typeof n?$.extend(t,{value:n.split(",")}):$.extend(t,{value:n}),p&&$.extend(t,{svalue:p});try{"date"==j.type&&"between"==k&&(t.value[0]=n,t.value[1]=o),"date"==j.type&&"is"==k&&(t.value=n)}catch(u){}d.push(t)}}d.length>0&&!c?(e=!0,f="AND"):(e=!0,f="AND")}if("string"==typeof a&&(g=a,h=b,e=!1,f="OR","undefined"!=typeof b))if("all"==a.toLowerCase())if(this.searches.length>0)for(var i in this.searches){var j=this.searches[i];if("text"==j.type||"alphanumeric"==j.type&&w2utils.isAlphaNumeric(b)||"int"==j.type&&w2utils.isInt(b)||"float"==j.type&&w2utils.isFloat(b)||"percent"==j.type&&w2utils.isFloat(b)||"hex"==j.type&&w2utils.isHex(b)||"currency"==j.type&&w2utils.isMoney(b)||"money"==j.type&&w2utils.isMoney(b)||"date"==j.type&&w2utils.isDate(b)){var t={field:j.field,type:j.type,operator:"text"==j.type?"contains":"is",value:b};d.push(t)}if(-1!=["int","float","money","currency","percent"].indexOf(j.type)&&-1!=String(b).indexOf("-")){var v=String(b).split("-"),t={field:j.field,type:j.type,operator:"between",value:[v[0],v[1]]};d.push(t)}}else for(var w in this.columns){var t={field:this.columns[w].field,type:"text",operator:"contains",value:b};d.push(t)}else{var x=$("#grid_"+this.name+"_search_all"),j=this.getSearch(a);if(null==j&&(j={field:a,type:"text"}),j.field==a&&(this.last.caption=j.caption),"list"==j.type){var t=x.data("selected");t&&!$.isEmptyObject(t)&&(b=t.id)}if(""!=b){var y="contains",z=b;if(-1!=["date","time","list"].indexOf(j.type)&&(y="is"),"int"==j.type&&""!=b){if(y="is",-1!=String(b).indexOf("-")){var t=b.split("-");2==t.length&&(y="between",z=[parseInt(t[0]),parseInt(t[1])])}if(-1!=String(b).indexOf(",")){var t=b.split(",");y="in",z=[];for(var v in t)z.push(t[v])}}var t={field:j.field,type:j.type,operator:y,value:z};d.push(t)}}if($.isArray(a)){var A="AND";"string"==typeof b&&(A=b.toUpperCase(),"OR"!=A&&"AND"!=A&&(A="AND")),h="",e=!0,f=A;for(var B in a){var C=a[B],j=this.getSearch(C.field);null==j&&(j={type:"text",operator:"contains"}),d.push($.extend(!0,{},j,C))}}var D=this.trigger({phase:"before",type:"search",target:this.name,searchData:d,searchField:a?a:"multi",searchValue:b?b:"multi"});D.isCancelled!==!0&&(this.searchData=D.searchData,this.last.field=g,this.last.search=h,this.last.multi=e,this.last.logic=f,this.last.scrollTop=0,this.last.scrollLeft=0,this.last.selection.indexes=[],this.last.selection.columns={},this.searchClose(),this.set({expanded:!1},!0),c?(this.last.xhr_offset=0,this.reload()):(this.localSearch(),this.refresh()),this.trigger($.extend(D,{phase:"after"})))},searchOpen:function(){if(this.box&&0!=this.searches.length){var a=this;$("#tb_"+this.name+"_toolbar_item_w2ui-search-advanced").w2overlay(this.getSearchesHTML(),{name:"searches-"+this.name,left:-10,"class":"w2ui-grid-searches",onShow:function(){"OR"==a.last.logic&&(a.searchData=[]),a.initSearches(),$("#w2ui-overlay-searches-"+this.name+" .w2ui-grid-searches").data("grid-name",a.name);var b=$("#w2ui-overlay-searches-"+this.name+" .w2ui-grid-searches *[rel=search]");b.length>0&&b[0].focus()}})}},searchClose:function(){this.box&&0!=this.searches.length&&(this.toolbar&&this.toolbar.uncheck("w2ui-search-advanced"),$("#w2ui-overlay-searches-"+this.name+" .w2ui-grid-searches").length>0&&$().w2overlay("",{name:"searches-"+this.name}))},searchShowFields:function(){for(var a=$("#grid_"+this.name+"_search_all"),b='<div class="w2ui-select-field"><table>',c=-1;c<this.searches.length;c++){var d=this.searches[c];if(-1==c){if(!this.multiSearch)continue;d={field:"all",caption:w2utils.lang("All Fields")}}else if(this.searches[c].hidden===!0)continue;b+="<tr "+(w2utils.isIOS?"onTouchStart":"onClick")+"=\"w2ui['"+this.name+"'].initAllField('"+d.field+'\')"> <td><input type="radio" tabIndex="-1" '+(d.field==this.last.field?"checked":"")+"></td> <td>"+d.caption+"</td></tr>"}b+="</table></div>",setTimeout(function(){$(a).w2overlay(b,{left:-10})},1)},initAllField:function(a,b){var c=$("#grid_"+this.name+"_search_all"),d=this.getSearch(a);if("all"==a)d={field:"all",caption:w2utils.lang("All Fields")},c.w2field("clear"),c.change().focus();else{var e=d.type;-1!=["enum","select"].indexOf(e)&&(e="list"),c.w2field(e,$.extend({},d.options,{suffix:"",autoFormat:!1,selected:b})),-1!=["list","enum"].indexOf(d.type)&&(this.last.search="",this.last.item="",c.val("")),setTimeout(function(){c.focus()},1)}""!=this.last.search?this.search(d.field,this.last.search):(this.last.field=d.field,this.last.caption=d.caption),c.attr("placeholder",d.caption),$().w2overlay()},searchReset:function(a){var b=this.trigger({phase:"before",type:"search",target:this.name,searchData:[]});b.isCancelled!==!0&&(this.searchData=[],this.last.search="",this.last.logic="OR",this.last.multi=!1,this.last.xhr_offset=0,this.last.scrollTop=0,this.last.scrollLeft=0,this.last.selection.indexes=[],this.last.selection.columns={},this.searchClose(),$("#grid_"+this.name+"_search_all").val(""),a||this.reload(),this.trigger($.extend(b,{phase:"after"})))},clear:function(a){this.records=[],this.summary=[],this.last.scrollTop=0,this.last.scrollLeft=0,this.last.range_start=null,this.last.range_end=null,a||this.refresh()},reset:function(a){this.offset=0,this.total=0,this.last.scrollTop=0,this.last.scrollLeft=0,this.last.selection.indexes=[],this.last.selection.columns={},this.last.range_start=null,this.last.range_end=null,this.last.xhr_offset=0,this.searchReset(a),null!=this.last.sortData&&(this.sortData=this.last.sortData),this.set({expanded:!1},!0),a||this.refresh()},skip:function(a){var b="object"!=typeof this.url?this.url:this.url.get;b?(this.offset=parseInt(a),this.offset>this.total&&(this.offset=this.total-this.limit),(this.offset<0||!w2utils.isInt(this.offset))&&(this.offset=0),this.records=[],this.last.xhr_offset=0,this.last.pull_more=!0,this.last.scrollTop=0,this.last.scrollLeft=0,$("#grid_"+this.name+"_records").prop("scrollTop",0),this.reload()):console.log("ERROR: grid.skip() can only be called when you have remote data source.")},load:function(a,b){return"undefined"==typeof a?void console.log('ERROR: You need to provide url argument when calling .load() method of "'+this.name+'" object.'):void this.request("get-records",{},a,b)
+},reload:function(a){var b="object"!=typeof this.url?this.url:this.url.get;b?(this.clear(!0),this.request("get-records",{},null,a)):(this.last.scrollTop=0,this.last.scrollLeft=0,this.last.range_start=null,this.last.range_end=null,this.localSearch(),this.refresh(),"function"==typeof a&&a({status:"success"}))},request:function(a,b,c,d){if("undefined"==typeof b&&(b={}),("undefined"==typeof c||""==c||null==c)&&(c=this.url),""!=c&&null!=c){var e={};if(w2utils.isInt(this.offset)||(this.offset=0),w2utils.isInt(this.last.xhr_offset)||(this.last.xhr_offset=0),e.cmd=a,e.selected=this.getSelection(),e.limit=this.limit,e.offset=parseInt(this.offset)+this.last.xhr_offset,e.search=this.searchData,e.searchLogic=this.last.logic,e.sort=this.sortData,0==this.searchData.length&&(delete e.search,delete e.searchLogic),0==this.sortData.length&&delete e.sort,$.extend(e,this.postData),$.extend(e,b),"get-records"==a){var f=this.trigger({phase:"before",type:"request",target:this.name,url:c,postData:e});if(f.isCancelled===!0)return void("function"==typeof d&&d({status:"error",message:"Request aborted."}))}else var f={url:c,postData:e};var g=this;if(0==this.last.xhr_offset)this.lock(this.msgRefresh,!0);else{var h=$("#grid_"+this.name+"_rec_more");this.autoLoad===!0?h.show().find("td").html('<div><div style="width: 20px; height: 20px;" class="w2ui-spinner"></div></div>'):h.find("td").html("<div>"+w2utils.lang("Load")+" "+g.limit+" "+w2utils.lang("More")+"...</div>")}if(this.last.xhr)try{this.last.xhr.abort()}catch(i){}var c="object"!=typeof f.url?f.url:f.url.get;if("save-records"==e.cmd&&"object"==typeof f.url&&(c=f.url.save),"delete-records"==e.cmd&&"object"==typeof f.url&&(c=f.url.remove),!$.isEmptyObject(g.routeData)){var j=w2utils.parseRoute(c);if(j.keys.length>0)for(var k=0;k<j.keys.length;k++)null!=g.routeData[j.keys[k].name]&&(c=c.replace(new RegExp(":"+j.keys[k].name,"g"),g.routeData[j.keys[k].name]))}var l={type:"POST",url:c,data:f.postData,dataType:"text"};"HTTP"==w2utils.settings.dataType&&(l.data="object"==typeof l.data?String($.param(l.data,!1)).replace(/%5B/g,"[").replace(/%5D/g,"]"):l.data),"RESTFULL"==w2utils.settings.dataType&&(l.type="GET","save-records"==e.cmd&&(l.type="PUT"),"delete-records"==e.cmd&&(l.type="DELETE"),l.data="object"==typeof l.data?String($.param(l.data,!1)).replace(/%5B/g,"[").replace(/%5D/g,"]"):l.data),"JSON"==w2utils.settings.dataType&&(l.type="POST",l.data=JSON.stringify(l.data),l.contentType="application/json"),this.method&&(l.type=this.method),this.last.xhr_cmd=e.cmd,this.last.xhr_start=(new Date).getTime(),this.last.xhr=$.ajax(l).done(function(b,c){g.requestComplete(c,a,d)}).fail(function(b,c,e){var f={status:c,error:e,rawResponseText:b.responseText},h=g.trigger({phase:"before",type:"error",error:f,xhr:b});if(h.isCancelled!==!0){if("abort"!=c){var i;try{i=$.parseJSON(b.responseText)}catch(j){}console.log("ERROR: Server communication failed.","\n EXPECTED:",{status:"success",total:5,records:[{recid:1,field:"value"}]},"\n OR:",{status:"error",message:"error message"},"\n RECEIVED:","object"==typeof i?i:b.responseText)}g.requestComplete("error",a,d),g.trigger($.extend(h,{phase:"after"}))}}),"get-records"==a&&this.trigger($.extend(f,{phase:"after"}))}},requestComplete:function(status,cmd,callBack){var obj=this;this.unlock(),setTimeout(function(){obj.status(w2utils.lang("Server Response")+" "+((new Date).getTime()-obj.last.xhr_start)/1e3+" "+w2utils.lang("sec"))},10),this.last.pull_more=!1,this.last.pull_refresh=!0;var event_name="load";"save-records"==this.last.xhr_cmd&&(event_name="save"),"delete-records"==this.last.xhr_cmd&&(event_name="deleted");var eventData=this.trigger({phase:"before",target:this.name,type:event_name,xhr:this.last.xhr,status:status});if(eventData.isCancelled===!0)return void("function"==typeof callBack&&callBack({status:"error",message:"Request aborted."}));var data,responseText=this.last.xhr.responseText;if("error"!=status){if("undefined"!=typeof responseText&&""!=responseText){if("object"==typeof responseText)data=responseText;else if("function"==typeof obj.parser)data=obj.parser(responseText),"object"!=typeof data&&console.log("ERROR: Your parser did not return proper object");else try{eval("data = "+responseText)}catch(e){}if(obj.recid)for(var r in data.records)data.records[r].recid=data.records[r][obj.recid];if("undefined"==typeof data&&(data={status:"error",message:this.msgNotJSON,responseText:responseText}),"error"==data.status)obj.error(data.message);else{if("get-records"==cmd)if(0==this.last.xhr_offset)this.records=[],this.summary=[],delete data.status,$.extend(!0,this,data);else{var records=data.records;delete data.records,delete data.status,$.extend(!0,this,data);for(var r in records)this.records.push(records[r])}if("delete-records"==cmd)return void this.reset()}}}else data={status:"error",message:this.msgAJAXerror,responseText:responseText},obj.error(this.msgAJAXerror);var url="object"!=typeof this.url?this.url:this.url.get;url||(this.localSort(),this.localSearch()),this.total=parseInt(this.total),this.trigger($.extend(eventData,{phase:"after"})),0==this.last.xhr_offset?this.refresh():this.scroll(),"function"==typeof callBack&&callBack(data)},error:function(a){var b=this.trigger({target:this.name,type:"error",message:a,xhr:this.last.xhr});return b.isCancelled===!0?void("function"==typeof callBack&&callBack({status:"error",message:"Request aborted."})):(w2alert(a,"Error"),void this.trigger($.extend(b,{phase:"after"})))},getChanges:function(){var a=[];for(var b in this.records){var c=this.records[b];"undefined"!=typeof c.changes&&a.push($.extend(!0,{recid:c.recid},c.changes))}return a},mergeChanges:function(){var changes=this.getChanges();for(var c in changes){var record=this.get(changes[c].recid);for(var s in changes[c])if("recid"!=s){try{eval("record."+s+" = changes[c][s]")}catch(e){}delete record.changes}}this.refresh()},save:function(){var a=this,b=this.getChanges(),c=this.trigger({phase:"before",target:this.name,type:"submit",changes:b});if(c.isCancelled!==!0){var d="object"!=typeof this.url?this.url:this.url.save;d?this.request("save-records",{changes:c.changes},null,function(b){"error"!==b.status&&a.mergeChanges(),a.trigger($.extend(c,{phase:"after"}))}):(this.mergeChanges(),this.trigger($.extend(c,{phase:"after"})))}},editField:function(a,b,c,d){var e=this,f=e.get(a,!0),g=e.records[f],h=e.columns[b],i=h?h.editable:null;if(g&&h&&i&&g.editable!==!1){if(-1!=["enum","file"].indexOf(i.type))return void console.log('ERROR: input types "enum" and "file" are not supported in inline editing.');var j=e.trigger({phase:"before",type:"editField",target:e.name,recid:a,column:b,value:c,index:f,originalEvent:d});if(j.isCancelled!==!0&&(c=j.value,this.selectNone(),this.select({recid:a,column:b}),this.last.edit_col=b,-1==["checkbox","check"].indexOf(i.type))){var k=$("#grid_"+e.name+"_rec_"+w2utils.escapeId(a)),l=k.find("[col="+b+"] > div");"undefined"==typeof i.inTag&&(i.inTag=""),"undefined"==typeof i.outTag&&(i.outTag=""),"undefined"==typeof i.style&&(i.style=""),"undefined"==typeof i.items&&(i.items=[]);var m=w2utils.stripTags(g.changes&&"undefined"!=typeof g.changes[h.field]?g.changes[h.field]:g[h.field]);(null==m||"undefined"==typeof m)&&(m=""),"undefined"!=typeof c&&null!=c&&(m=c);var n="undefined"!=typeof h.style?h.style+";":"";if("string"==typeof h.render&&-1!=["number","int","float","money","percent"].indexOf(h.render.split(":")[0])&&(n+="text-align: right;"),"select"==i.type){var o="";for(var p in i.items)o+='<option value="'+i.items[p].id+'" '+(i.items[p].id==m?"selected":"")+">"+i.items[p].text+"</option>";l.addClass("w2ui-editable").html('<select id="grid_'+e.name+"_edit_"+a+"_"+b+'" column="'+b+'" style="width: 100%; '+n+i.style+'" field="'+h.field+'" recid="'+a+'" '+i.inTag+">"+o+"</select>"+i.outTag),l.find("select").focus().on("change",function(){delete e.last.move}).on("blur",function(a){e.editChange.call(e,this,f,b,a)})}else{l.addClass("w2ui-editable").html('<input id="grid_'+e.name+"_edit_"+a+"_"+b+'" type="text" style="outline: none; '+n+i.style+'" field="'+h.field+'" recid="'+a+'" column="'+b+'" '+i.inTag+">"+i.outTag),null==c&&l.find("input").val("object"!=m?m:"");var q=l.find("input").get(0);$(q).w2field(i.type,$.extend(i,{selected:m})),setTimeout(function(){var a=q;"list"==i.type&&(a=$($(q).data("w2field").helpers.focus).find("input"),"object"!=m&&""!=m&&a.val(m).css({opacity:1}).prev().css({opacity:1})),$(a).on("blur",function(a){e.editChange.call(e,q,f,b,a)})},10),null!=c&&$(q).val("object"!=m?m:"")}setTimeout(function(){l.find("input, select").on("click",function(a){a.stopPropagation()}).on("keydown",function(c){var d=!1;switch(c.keyCode){case 9:d=!0;var i=a,j=c.shiftKey?e.prevCell(b,!0):e.nextCell(b,!0);if(null==j){var k=c.shiftKey?e.prevRow(f):e.nextRow(f);if(null!=k&&k!=f){i=e.records[k].recid;for(var l in e.columns){var k=e.columns[l].editable;if("undefined"!=typeof k&&-1==["checkbox","check"].indexOf(k.type)&&(j=parseInt(l),!c.shiftKey))break}}}i===!1&&(i=a),null==j&&(j=b),this.blur(),setTimeout(function(){"row"!=e.selectType?(e.selectNone(),e.select({recid:i,column:j})):e.editField(i,j,null,c)},1);break;case 13:this.blur();var m=c.shiftKey?e.prevRow(f):e.nextRow(f);null!=m&&m!=f&&setTimeout(function(){"row"!=e.selectType?(e.selectNone(),e.select({recid:e.records[m].recid,column:b})):e.editField(e.records[m].recid,b,null,c)},100);break;case 38:if(!c.shiftKey)break;d=!0;var m=e.prevRow(f);m!=f&&(this.blur(),setTimeout(function(){"row"!=e.selectType?(e.selectNone(),e.select({recid:e.records[m].recid,column:b})):e.editField(e.records[m].recid,b,null,c)},1));break;case 40:if(!c.shiftKey)break;d=!0;var m=e.nextRow(f);null!=m&&m!=f&&(this.blur(),setTimeout(function(){"row"!=e.selectType?(e.selectNone(),e.select({recid:e.records[m].recid,column:b})):e.editField(e.records[m].recid,b,null,c)},1));break;case 27:var n=e.parseField(g,h.field);g.changes&&"undefined"!=typeof g.changes[h.field]&&(n=g.changes[h.field]),this.value="undefined"!=typeof n?n:"",this.blur(),setTimeout(function(){e.select({recid:a,column:b})},1)}d&&c.preventDefault&&c.preventDefault()});var d=l.find("input").focus();null!=c?d[0].setSelectionRange(d.val().length,d.val().length):d.select()},1),e.trigger($.extend(j,{phase:"after"}))}}},editChange:function(a,b,c){var d=0>b;b=0>b?-b-1:b;var e=d?this.summary:this.records,f=e[b],g=$("#grid_"+this.name+"_rec_"+w2utils.escapeId(f.recid)),h=this.columns[c],i=a.value,j=this.parseField(f,h.field),k=$(a).data("w2field");k&&(i=k.clean(i),"list"==k.type&&""!=i&&(i=$(a).data("selected"))),"checkbox"==a.type&&(i=a.checked);for(var l={phase:"before",type:"change",target:this.name,input_id:a.id,recid:f.recid,index:b,column:c,value_new:i,value_previous:f.changes&&f.changes.hasOwnProperty(h.field)?f.changes[h.field]:j,value_original:j};;){if(i=l.value_new,("undefined"==typeof j||null===j?"":String(j))!==String(i)){if(l=this.trigger($.extend(l,{type:"change",phase:"before"})),l.isCancelled!==!0){if(i!==l.value_new)continue;f.changes=f.changes||{},f.changes[h.field]=l.value_new,this.trigger($.extend(l,{phase:"after"}))}}else if(l=this.trigger($.extend(l,{type:"restore",phase:"before"})),l.isCancelled!==!0){if(i!==l.value_new)continue;f.changes&&delete f.changes[h.field],$.isEmptyObject(f.changes)&&delete f.changes,this.trigger($.extend(l,{phase:"after"}))}break}var m=this.getCellHTML(b,c,d);d||(f.changes&&"undefined"!=typeof f.changes[h.field]?$(g).find("[col="+c+"]").addClass("w2ui-changed").html(m):$(g).find("[col="+c+"]").removeClass("w2ui-changed").html(m))},"delete":function(a){var b=this,c=this.trigger({phase:"before",target:this.name,type:"delete",force:a});if(c.isCancelled!==!0){a=c.force;var d=this.getSelection();if(0!=d.length){if(""!=this.msgDelete&&!a)return void w2confirm({title:w2utils.lang("Delete Confirmation"),msg:b.msgDelete,btn_yes:{"class":"btn-red"},callBack:function(a){"Yes"==a&&w2ui[b.name].delete(!0)}});var e="object"!=typeof this.url?this.url:this.url.remove;if(e)this.request("delete-records");else if(this.selectNone(),"object"!=typeof d[0])this.remove.apply(this,d);else{for(var f in d){var g=this.columns[d[f].column].field,h=this.get(d[f].recid,!0);null!=h&&"recid"!=g&&(this.records[h][g]="",this.records[h].changes&&delete this.records[h].changes[g])}this.refresh()}this.trigger($.extend(c,{phase:"after"}))}}},click:function(a,b){var c=(new Date).getTime(),d=null;if(!(1==this.last.cancelClick||b&&b.altKey)){if("object"==typeof a&&(d=a.column,a=a.recid),"undefined"==typeof b&&(b={}),c-parseInt(this.last.click_time)<350&&"click"==b.type)return void this.dblClick(a,b);if(this.last.click_time=c,null==d&&b.target){var e=b.target;"TD"!=e.tagName&&(e=$(e).parents("td")[0]),"undefined"!=typeof $(e).attr("col")&&(d=parseInt($(e).attr("col")))}var f=this.trigger({phase:"before",target:this.name,type:"click",recid:a,column:d,originalEvent:b});if(f.isCancelled!==!0){var g=$("#grid_"+this.name+"_rec_"+w2utils.escapeId(a)).parents("tr");if(g.length>0&&-1!=String(g.attr("id")).indexOf("expanded_row")){var h=g.parents(".w2ui-grid").attr("name");w2ui[h].selectNone(),g.parents(".w2ui-grid").find(".w2ui-expanded-row .w2ui-grid").each(function(a,b){var c=$(b).attr("name");w2ui[c]&&w2ui[c].selectNone()})}$(this.box).find(".w2ui-expanded-row .w2ui-grid").each(function(a,b){var c=$(b).attr("name");w2ui[c]&&w2ui[c].selectNone()});var i=this,j=this.getSelection();$("#grid_"+this.name+"_check_all").prop("checked",!1);var k=this.get(a,!0),l=(this.records[k],[]);if(i.last.sel_ind=k,i.last.sel_col=d,i.last.sel_recid=a,i.last.sel_type="click",b.shiftKey&&j.length>0&&i.multiSelect){if(j[0].recid){var m=this.get(j[0].recid,!0),n=this.get(a,!0);if(d>j[0].column)var o=j[0].column,p=d;else var o=d,p=j[0].column;for(var q=o;p>=q;q++)l.push(q)}else var m=this.get(j[0],!0),n=this.get(a,!0);var r=[];if(m>n){var e=m;m=n,n=e}for(var s="object"!=typeof this.url?this.url:this.url.get,t=m;n>=t;t++)if(!(this.searchData.length>0)||s||-1!=$.inArray(t,this.last.searchIds))if("row"==this.selectType)r.push(this.records[t].recid);else for(var u in l)r.push({recid:this.records[t].recid,column:l[u]});this.select.apply(this,r)}else{var v=this.last.selection,w=-1!=v.indexes.indexOf(k)?!0:!1;(b.ctrlKey||b.shiftKey||b.metaKey)&&this.multiSelect||this.showSelectColumn?("row"!=this.selectType&&-1==$.inArray(d,v.columns[k])&&(w=!1),w===!0?this.unselect({recid:a,column:d}):this.select({recid:a,column:d})):("row"!=this.selectType&&-1==$.inArray(d,v.columns[k])&&(w=!1),j.length>300?this.selectNone():this.unselect.apply(this,j),w===!0?this.unselect({recid:a,column:d}):this.select({recid:a,column:d}))}this.status(),i.initResize(),this.trigger($.extend(f,{phase:"after"}))}}},columnClick:function(a,b){var c=this.trigger({phase:"before",type:"columnClick",target:this.name,field:a,originalEvent:b});if(c.isCancelled!==!0){var d=this.getColumn(a);d.sortable&&this.sort(a,null,b&&(b.ctrlKey||b.metaKey)?!0:!1),this.trigger($.extend(c,{phase:"after"}))}},keydown:function(a){function b(){$("#_tmp_copy_data").remove(),$(document).off("keyup",b)}function c(){var a=Math.floor((h[0].scrollTop+h.height()/2.1)/e.recordHeight);e.records[a]||(a=0),e.select({recid:e.records[a].recid,column:0})}function d(){if("click"!=e.last.sel_type)return!1;if("row"!=e.selectType){if(e.last.sel_type="key",i.length>1){for(var a in i)if(i[a].recid==e.last.sel_recid&&i[a].column==e.last.sel_col){i.splice(a,1);break}return e.unselect.apply(e,i),!0}return!1}return e.last.sel_type="key",i.length>1?(i.splice(i.indexOf(e.records[e.last.sel_ind].recid),1),e.unselect.apply(e,i),!0):!1}var e=this;if(e.keyboard===!0){var f=e.trigger({phase:"before",type:"keydown",target:e.name,originalEvent:a});if(f.isCancelled!==!0){var g=!1,h=$("#grid_"+e.name+"_records"),i=e.getSelection();0==i.length&&(g=!0);var j=i[0]||null,k=[],l=i[i.length-1];if("object"==typeof j&&null!=j){j=i[0].recid,k=[];for(var m=0;;){if(!i[m]||i[m].recid!=j)break;k.push(i[m].column),m++}l=i[i.length-1].recid}var n=e.get(j,!0),o=e.get(l,!0),p=e.get(j),q=$("#grid_"+e.name+"_rec_"+(null!==n?w2utils.escapeId(e.records[n].recid):"none")),r=!1,s=a.keyCode,t=a.shiftKey;switch(9==s&&(s=a.shiftKey?37:39,t=!1,r=!0),s){case 8:case 46:this.show.toolbarDelete&&e["delete"](),r=!0,a.stopPropagation();break;case 27:e.selectNone(),i.length>0&&"object"==typeof i[0]&&e.select({recid:i[0].recid,column:i[0].column}),r=!0;break;case 65:if(!a.metaKey&&!a.ctrlKey)break;e.selectAll(),r=!0;break;case 70:if(!a.metaKey&&!a.ctrlKey)break;$("#grid_"+e.name+"_search_all").focus(),r=!0;break;case 13:if("row"==this.selectType&&e.show.expandColumn===!0){if(q.length<=0)break;e.toggle(j,a),r=!0}else{for(var u in this.columns)if(this.columns[u].editable){k.push(parseInt(u));break}"row"==this.selectType&&this.last.edit_col&&(k=[this.last.edit_col]),k.length>0&&(e.editField(j,k[0],null,a),r=!0)}break;case 37:if(g)break;var v=$("#grid_"+this.name+"_rec_"+w2utils.escapeId(e.records[n].recid)).parents("tr");if(v.length>0&&-1!=String(v.attr("id")).indexOf("expanded_row")){var j=v.prev().attr("recid"),w=v.parents(".w2ui-grid").attr("name");e.selectNone(),w2utils.keyboard.active(w),w2ui[w].set(j,{expanded:!1}),w2ui[w].collapse(j),w2ui[w].click(j),r=!0;break}if("row"==this.selectType){if(q.length<=0||p.expanded!==!0)break;e.set(j,{expanded:!1},!0),e.collapse(j,a)}else{var x=e.prevCell(k[0]);if(null!=x)if(t&&e.multiSelect){if(d())return;var y=[],z=[],A=[];if(0==k.indexOf(this.last.sel_col)&&k.length>1)for(var B in i)-1==y.indexOf(i[B].recid)&&y.push(i[B].recid),A.push({recid:i[B].recid,column:k[k.length-1]});else for(var B in i)-1==y.indexOf(i[B].recid)&&y.push(i[B].recid),z.push({recid:i[B].recid,column:x});e.unselect.apply(e,A),e.select.apply(e,z)}else a.shiftKey=!1,e.click({recid:j,column:x},a);else if(!t)for(var C=1;C<i.length;C++)e.unselect(i[C])}r=!0;break;case 39:if(g)break;if("row"==this.selectType){if(q.length<=0||p.expanded===!0||e.show.expandColumn!==!0)break;e.expand(j,a)}else{var D=e.nextCell(k[k.length-1]);if(null!==D)if(t&&39==s&&e.multiSelect){if(d())return;var y=[],z=[],A=[];if(k.indexOf(this.last.sel_col)==k.length-1&&k.length>1)for(var B in i)-1==y.indexOf(i[B].recid)&&y.push(i[B].recid),A.push({recid:i[B].recid,column:k[0]});else for(var B in i)-1==y.indexOf(i[B].recid)&&y.push(i[B].recid),z.push({recid:i[B].recid,column:D});e.unselect.apply(e,A),e.select.apply(e,z)}else e.click({recid:j,column:D},a);else if(!t)for(var C=0;C<i.length-1;C++)e.unselect(i[C])}r=!0;break;case 38:if(g&&c(),q.length<=0)break;var x=e.prevRow(n);if(null!=x){if(e.records[x].expanded){var E=$("#grid_"+e.name+"_rec_"+w2utils.escapeId(e.records[x].recid)+"_expanded_row").find(".w2ui-grid");if(E.length>0&&w2ui[E.attr("name")]){e.selectNone();var w=E.attr("name"),F=w2ui[w].records;w2utils.keyboard.active(w),w2ui[w].click(F[F.length-1].recid),r=!0;break}}if(t&&e.multiSelect){if(d())return;if("row"==e.selectType)e.last.sel_ind>x&&e.last.sel_ind!=o?e.unselect(e.records[o].recid):e.select(e.records[x].recid);else if(e.last.sel_ind>x&&e.last.sel_ind!=o){x=o;var y=[];for(var u in k)y.push({recid:e.records[x].recid,column:k[u]});e.unselect.apply(e,y)}else{var y=[];for(var u in k)y.push({recid:e.records[x].recid,column:k[u]});e.select.apply(e,y)}}else e.selectNone(),e.click({recid:e.records[x].recid,column:k[0]},a);e.scrollIntoView(x),a.preventDefault&&a.preventDefault()}else{if(!t)for(var C=1;C<i.length;C++)e.unselect(i[C]);var v=$("#grid_"+e.name+"_rec_"+w2utils.escapeId(e.records[n].recid)).parents("tr");if(v.length>0&&-1!=String(v.attr("id")).indexOf("expanded_row")){var j=v.prev().attr("recid"),w=v.parents(".w2ui-grid").attr("name");e.selectNone(),w2utils.keyboard.active(w),w2ui[w].click(j),r=!0;break}}break;case 40:if(g&&c(),q.length<=0)break;if(e.records[o].expanded){var E=$("#grid_"+this.name+"_rec_"+w2utils.escapeId(e.records[o].recid)+"_expanded_row").find(".w2ui-grid");if(E.length>0&&w2ui[E.attr("name")]){e.selectNone();var w=E.attr("name"),F=w2ui[w].records;w2utils.keyboard.active(w),w2ui[w].click(F[0].recid),r=!0;break}}var D=e.nextRow(o);if(null!=D){if(t&&e.multiSelect){if(d())return;if("row"==e.selectType)this.last.sel_ind<D&&this.last.sel_ind!=n?e.unselect(e.records[n].recid):e.select(e.records[D].recid);else if(this.last.sel_ind<D&&this.last.sel_ind!=n){D=n;var y=[];for(var u in k)y.push({recid:e.records[D].recid,column:k[u]});e.unselect.apply(e,y)}else{var y=[];for(var u in k)y.push({recid:e.records[D].recid,column:k[u]});e.select.apply(e,y)}}else e.selectNone(),e.click({recid:e.records[D].recid,column:k[0]},a);e.scrollIntoView(D),r=!0}else{if(!t)for(var C=0;C<i.length-1;C++)e.unselect(i[C]);var v=$("#grid_"+this.name+"_rec_"+w2utils.escapeId(e.records[o].recid)).parents("tr");if(v.length>0&&-1!=String(v.attr("id")).indexOf("expanded_row")){var j=v.next().attr("recid"),w=v.parents(".w2ui-grid").attr("name");e.selectNone(),w2utils.keyboard.active(w),w2ui[w].click(j),r=!0;break}}break;case 17:case 91:if(g)break;var G=e.copy();$("body").append('<textarea id="_tmp_copy_data" onpaste="var obj = this; setTimeout(function () { w2ui[\''+e.name+"'].paste(obj.value); }, 1);\" onkeydown=\"w2ui['"+e.name+'\'].keydown(event)" style="position: absolute; top: -100px; height: 1px; width: 1px">'+G+"</textarea>"),$("#_tmp_copy_data").focus().select(),$(document).on("keyup",b);break;case 88:if(g)break;(a.ctrlKey||a.metaKey)&&setTimeout(function(){e["delete"](!0)},100)}for(var y=[187,189,32],B=48;90>=B;B++)y.push(B);if(-1!=y.indexOf(s)&&!a.ctrlKey&&!a.metaKey&&!r){0==k.length&&k.push(0);var y=String.fromCharCode(s);187==s&&(y="="),189==s&&(y="-"),t||(y=y.toLowerCase()),e.editField(j,k[0],y,a),r=!0}r&&a.preventDefault&&a.preventDefault(),e.trigger($.extend(f,{phase:"after"}))}}},scrollIntoView:function(a){var b=this.records.length;if(0==this.searchData.length||this.url||(b=this.last.searchIds.length),"undefined"==typeof a){var c=this.getSelection();if(0==c.length)return;a=this.get(c[0],!0)}var d=$("#grid_"+this.name+"_records");if(0!=b){var e=this.last.searchIds.length;if(!(d.height()>this.recordHeight*(e>0?e:b))){e>0&&(a=this.last.searchIds.indexOf(a));var f=Math.floor(d[0].scrollTop/this.recordHeight),g=f+Math.floor(d.height()/this.recordHeight);a==f&&d.animate({scrollTop:d.scrollTop()-d.height()/1.3},250,"linear"),a==g&&d.animate({scrollTop:d.scrollTop()+d.height()/1.3},250,"linear"),(f>a||a>g)&&d.animate({scrollTop:(a-1)*this.recordHeight})}}},dblClick:function(a,b){var c=null;if("object"==typeof a&&(c=a.column,a=a.recid),"undefined"==typeof b&&(b={}),null==c&&b.target){var d=b.target;"TD"!=d.tagName&&(d=$(d).parents("td")[0]),c=parseInt($(d).attr("col"))}var e=this.trigger({phase:"before",target:this.name,type:"dblClick",recid:a,column:c,originalEvent:b});if(e.isCancelled!==!0){this.selectNone();var f=this.columns[c];f&&$.isPlainObject(f.editable)?this.editField(a,c,null,b):this.select({recid:a,column:c}),this.trigger($.extend(e,{phase:"after"}))}},contextMenu:function(a,b){var c=this;"text"!=c.last.userSelect&&("undefined"==typeof b&&(b={offsetX:0,offsetY:0,target:$("#grid_"+c.name+"_rec_"+a)[0]}),"undefined"==typeof b.offsetX&&(b.offsetX=b.layerX-b.target.offsetLeft,b.offsetY=b.layerY-b.target.offsetTop),w2utils.isFloat(a)&&(a=parseFloat(a)),-1==this.getSelection().indexOf(a)&&c.click(a),setTimeout(function(){var d=c.trigger({phase:"before",type:"contextMenu",target:c.name,originalEvent:b,recid:a});d.isCancelled!==!0&&(c.menu.length>0&&$(c.box).find(b.target).w2menu(c.menu,{left:b.offsetX,onSelect:function(b){c.menuClick(a,parseInt(b.index),b.originalEvent)}}),c.trigger($.extend(d,{phase:"after"})))},150),b.preventDefault&&b.preventDefault())},menuClick:function(a,b,c){var d=this,e=d.trigger({phase:"before",type:"menuClick",target:d.name,originalEvent:c,recid:a,menuIndex:b,menuItem:d.menu[b]});e.isCancelled!==!0&&d.trigger($.extend(e,{phase:"after"}))},toggle:function(a){var b=this.get(a);return b.expanded===!0?this.collapse(a):this.expand(a)},expand:function(a){function b(){var b=$("#grid_"+d.name+"_rec_"+e+"_expanded"),c=$("#grid_"+d.name+"_rec_"+e+"_expanded_row .w2ui-expanded1 > div");b.height()<5||(b.css("opacity",1),c.show().css("opacity",1),$("#grid_"+d.name+"_cell_"+d.get(a,!0)+"_expand div").html("-"))}var c=this.get(a),d=this,e=w2utils.escapeId(a);if($("#grid_"+this.name+"_rec_"+e+"_expanded_row").length>0)return!1;if("none"==c.expanded)return!1;var f=1+(this.show.selectColumn?1:0),g="";$("#grid_"+this.name+"_rec_"+e).after('<tr id="grid_'+this.name+"_rec_"+e+'_expanded_row" class="w2ui-expanded-row '+g+'">'+(this.show.lineNumbers?'<td class="w2ui-col-number"></td>':"")+' <td class="w2ui-grid-data w2ui-expanded1" colspan="'+f+'"><div style="display: none"></div></td> <td colspan="100" class="w2ui-expanded2"> <div id="grid_'+this.name+"_rec_"+e+'_expanded" style="opacity: 0"></div> </td></tr>');var h=this.trigger({phase:"before",type:"expand",target:this.name,recid:a,box_id:"grid_"+this.name+"_rec_"+e+"_expanded",ready:b});return h.isCancelled===!0?void $("#grid_"+this.name+"_rec_"+e+"_expanded_row").remove():($("#grid_"+this.name+"_rec_"+e).attr("expanded","yes").addClass("w2ui-expanded"),$("#grid_"+this.name+"_rec_"+e+"_expanded_row").show(),$("#grid_"+this.name+"_cell_"+this.get(a,!0)+"_expand div").html('<div class="w2ui-spinner" style="width: 16px; height: 16px; margin: -2px 2px;"></div>'),c.expanded=!0,setTimeout(b,300),this.trigger($.extend(h,{phase:"after"})),this.resizeRecords(),!0)},collapse:function(a){var b=this.get(a),c=this,d=w2utils.escapeId(a);if(0==$("#grid_"+this.name+"_rec_"+d+"_expanded_row").length)return!1;var e=this.trigger({phase:"before",type:"collapse",target:this.name,recid:a,box_id:"grid_"+this.name+"_rec_"+d+"_expanded"});return e.isCancelled!==!0?($("#grid_"+this.name+"_rec_"+d).removeAttr("expanded").removeClass("w2ui-expanded"),$("#grid_"+this.name+"_rec_"+d+"_expanded").css("opacity",0),$("#grid_"+this.name+"_cell_"+this.get(a,!0)+"_expand div").html("+"),setTimeout(function(){$("#grid_"+c.name+"_rec_"+d+"_expanded").height("0px"),setTimeout(function(){$("#grid_"+c.name+"_rec_"+d+"_expanded_row").remove(),delete b.expanded,c.trigger($.extend(e,{phase:"after"})),c.resizeRecords()},300)},200),!0):void 0},sort:function(a,b,c){var d=this.trigger({phase:"before",type:"sort",target:this.name,field:a,direction:b,multiField:c});if(d.isCancelled!==!0){if("undefined"!=typeof a){var e=this.sortData.length;for(var f in this.sortData)if(this.sortData[f].field==a){e=f;break}if("undefined"==typeof b||null==b)if("undefined"==typeof this.sortData[e])b="asc";else switch(String(this.sortData[e].direction)){case"asc":b="desc";break;case"desc":b="asc";break;default:b="asc"}this.multiSort===!1&&(this.sortData=[],e=0),1!=c&&(this.sortData=[],e=0),"undefined"==typeof this.sortData[e]&&(this.sortData[e]={}),this.sortData[e].field=a,this.sortData[e].direction=b}else this.sortData=[];this.selectNone();var g="object"!=typeof this.url?this.url:this.url.get;g?(this.trigger($.extend(d,{phase:"after"})),this.last.xhr_offset=0,this.reload()):(this.localSort(),this.searchData.length>0&&this.localSearch(!0),this.trigger($.extend(d,{phase:"after"})),this.refresh())}},copy:function(){var a=this.getSelection();if(0==a.length)return"";var b="";if("object"==typeof a[0]){var c=a[0].column,d=a[0].column,e=[];for(var f in a)a[f].column<c&&(c=a[f].column),a[f].column>d&&(d=a[f].column),-1==e.indexOf(a[f].index)&&e.push(a[f].index);e.sort();for(var g in e){for(var h=e[g],i=c;d>=i;i++){var j=this.columns[i];j.hidden!==!0&&(b+=w2utils.stripTags(this.getCellHTML(h,i))+" ")}b=b.substr(0,b.length-1),b+="\n"}}else{for(var i in this.columns){var j=this.columns[i];j.hidden!==!0&&(b+='"'+w2utils.stripTags(j.caption?j.caption:j.field)+'" ')}b=b.substr(0,b.length-1),b+="\n";for(var f in a){var h=this.get(a[f],!0);for(var i in this.columns){var j=this.columns[i];j.hidden!==!0&&(b+='"'+w2utils.stripTags(this.getCellHTML(h,i))+'" ')}b=b.substr(0,b.length-1),b+="\n"}}b=b.substr(0,b.length-1);var k=this.trigger({phase:"before",type:"copy",target:this.name,text:b});return k.isCancelled===!0?"":(b=k.text,this.trigger($.extend(k,{phase:"after"})),b)},paste:function(a){var b=this.getSelection(),c=this.get(b[0].recid,!0),d=b[0].column,e=this.trigger({phase:"before",type:"paste",target:this.name,text:a,index:c,column:d});if(e.isCancelled!==!0){if(a=e.text,"row"==this.selectType||0==b.length)return console.log("ERROR: You can paste only if grid.selectType = 'cell' and when at least one cell selected."),void this.trigger($.extend(e,{phase:"after"}));var f=[],a=a.split("\n");for(var g in a){var h=a[g].split(" "),i=0,j=this.records[c],k=[];for(var l in h)if(this.columns[d+i]){var m=this.columns[d+i].field;j.changes=j.changes||{},j.changes[m]=h[l],k.push(d+i),i++}for(var n in k)f.push({recid:j.recid,column:k[n]});c++}this.selectNone(),this.select.apply(this,f),this.refresh(),this.trigger($.extend(e,{phase:"after"}))}},resize:function(){var a=this,b=(new Date).getTime();if(this.box&&$(this.box).attr("name")==this.name){$(this.box).find("> div").css("width",$(this.box).width()).css("height",$(this.box).height());var c=this.trigger({phase:"before",type:"resize",target:this.name});if(c.isCancelled!==!0)return a.resizeBoxes(),a.resizeRecords(),this.trigger($.extend(c,{phase:"after"})),(new Date).getTime()-b}},refreshCell:function(a,b){var c=this.get(a,!0),d=this.getColumn(b,!0),e=this.records[c],f=this.columns[d],g=$("#grid_"+this.name+"_rec_"+a+" [col="+d+"]");g.html(this.getCellHTML(c,d)),e.changes&&"undefined"!=typeof e.changes[f.field]?g.addClass("w2ui-changed"):g.removeClass("w2ui-changed")},refreshRow:function(a){var b=$("#grid_"+this.name+"_rec_"+w2utils.escapeId(a));if(0!=b.length){var c=this.get(a,!0),d=b.attr("line"),e="object"!=typeof this.url?this.url:this.url.get;if(this.searchData.length>0&&!e)for(var f in this.last.searchIds)this.last.searchIds[f]==c&&(c=f);$(b).replaceWith(this.getRecordHTML(c,d))}},refresh:function(){var a=this,b=(new Date).getTime(),c="object"!=typeof this.url?this.url:this.url.get;if(this.total<=0&&!c&&0==this.searchData.length&&(this.total=this.records.length),this.toolbar.disable("w2ui-edit","w2ui-delete"),this.box){var d=this.trigger({phase:"before",target:this.name,type:"refresh"});if(d.isCancelled!==!0){if(this.show.header?$("#grid_"+this.name+"_header").html(this.header+"&nbsp;").show():$("#grid_"+this.name+"_header").hide(),this.show.toolbar){if(this.toolbar&&this.toolbar.get("w2ui-column-on-off")&&this.toolbar.get("w2ui-column-on-off").checked);else if($("#grid_"+this.name+"_toolbar").show(),"object"==typeof this.toolbar){var e=this.toolbar.items;for(var f in e)"w2ui-search"!=e[f].id&&"break"!=e[f].type&&this.toolbar.refresh(e[f].id)}}else $("#grid_"+this.name+"_toolbar").hide();this.searchClose();var g=$("#grid_"+a.name+"_search_all");!this.multiSearch&&"all"==this.last.field&&this.searches.length>0&&(this.last.field=this.searches[0].field,this.last.caption=this.searches[0].caption);for(var h in this.searches)this.searches[h].field==this.last.field&&(this.last.caption=this.searches[h].caption);if(this.last.multi?g.attr("placeholder","["+w2utils.lang("Multiple Fields")+"]"):g.attr("placeholder",this.last.caption),g.val()!=this.last.search){var i=this.last.search,e=g.data("w2field");e&&(i=e.format(i)),g.val(i)}var e=this.find({summary:!0},!0);if(e.length>0){for(var f in e)this.summary.push(this.records[e[f]]);for(var f=e.length-1;f>=0;f--)this.records.splice(e[f],1);this.total=this.total-e.length}var j="";j+='<div id="grid_'+this.name+'_records" class="w2ui-grid-records" onscroll="var obj = w2ui[\''+this.name+"']; obj.last.scrollTop = this.scrollTop; obj.last.scrollLeft = this.scrollLeft; $('#grid_"+this.name+"_columns')[0].scrollLeft = this.scrollLeft; $('#grid_"+this.name+"_summary')[0].scrollLeft = this.scrollLeft; obj.scroll(event);\">"+this.getRecordsHTML()+'</div><div id="grid_'+this.name+'_columns" class="w2ui-grid-columns"> <table>'+this.getColumnsHTML()+"</table></div>",$("#grid_"+this.name+"_body").html(j),this.summary.length>0?$("#grid_"+this.name+"_summary").html(this.getSummaryHTML()).show():$("#grid_"+this.name+"_summary").hide(),this.show.footer?$("#grid_"+this.name+"_footer").html(this.getFooterHTML()).show():$("#grid_"+this.name+"_footer").hide(),this.searchData.length>0?$("#grid_"+this.name+"_searchClear").show():$("#grid_"+this.name+"_searchClear").hide();
+var k=this.last.selection;k.indexes.length==this.records.length||0!==this.searchData.length&&k.indexes.length==this.last.searchIds.length?$("#grid_"+this.name+"_check_all").prop("checked",!0):$("#grid_"+this.name+"_check_all").prop("checked",!1),this.status();var l=a.find({expanded:!0},!0);for(var m in l)a.records[l[m]].expanded=!1;return setTimeout(function(){var b=$.trim($("#grid_"+a.name+"_search_all").val());""!=b&&$(a.box).find(".w2ui-grid-data > div").w2marker(b)},50),this.trigger($.extend(d,{phase:"after"})),a.resize(),a.addRange("selection"),setTimeout(function(){a.resize(),a.scroll()},1),a.reorderColumns&&!a.last.columnDrag?a.last.columnDrag=a.initColumnDrag():!a.reorderColumns&&a.last.columnDrag&&a.last.columnDrag.remove(),(new Date).getTime()-b}}},render:function(a){function b(a){if(1==a.which&&("text"==e.last.userSelect&&(delete e.last.userSelect,$(e.box).find(".w2ui-grid-body").css("user-select","none").css("-webkit-user-select","none").css("-moz-user-select","none").css("-ms-user-select","none"),$(this.box).on("selectstart",function(){return!1})),!($(a.target).parents().hasClass("w2ui-head")||$(a.target).hasClass("w2ui-head")||e.last.move&&"expand"==e.last.move.type))){if(a.altKey)$(e.box).off("selectstart"),$(e.box).find(".w2ui-grid-body").css("user-select","text").css("-webkit-user-select","text").css("-moz-user-select","text").css("-ms-user-select","text"),e.selectNone(),e.last.move={type:"text-select"},e.last.userSelect="text";else{if(!e.multiSelect)return;e.last.move={x:a.screenX,y:a.screenY,divX:0,divY:0,recid:$(a.target).parents("tr").attr("recid"),column:"TD"==a.target.tagName?$(a.target).attr("col"):$(a.target).parents("td").attr("col"),type:"select",ghost:!1,start:!0}}$(document).on("mousemove",c),$(document).on("mouseup",d)}}function c(a){var b=e.last.move;if(b&&"select"==b.type&&(b.divX=a.screenX-b.x,b.divY=a.screenY-b.y,!(Math.abs(b.divX)<=1&&Math.abs(b.divY)<=1))){if(e.last.cancelClick=!0,1==e.reorderRows){if(!b.ghost){var c=$("#grid_"+e.name+"_rec_"+b.recid),d=c.parents("table").find("tr:first-child").clone();b.offsetY=a.offsetY,b.from=b.recid,b.pos=c.position(),b.ghost=$(c).clone(!0),b.ghost.removeAttr("id"),c.find("td:first-child").replaceWith('<td colspan="1000" style="height: '+e.recordHeight+'px; background-color: #ddd"></td>');var f=$(e.box).find(".w2ui-grid-records");f.append('<table id="grid_'+e.name+'_ghost" style="position: absolute; z-index: 999999; opacity: 0.8; border-bottom: 2px dashed #aaa; border-top: 2px dashed #aaa; pointer-events: none;"></table>'),$("#grid_"+e.name+"_ghost").append(d).append(b.ghost)}var g=$(a.target).parents("tr").attr("recid");if(g!=b.from){var h=$("#grid_"+e.name+"_rec_"+b.recid),i=$("#grid_"+e.name+"_rec_"+g);a.screenY-b.lastY<0?h.after(i):i.after(h),b.lastY=a.screenY,b.to=g}var j=$("#grid_"+e.name+"_ghost"),f=$(e.box).find(".w2ui-grid-records");return void j.css({top:b.pos.top+b.divY+f.scrollTop(),left:b.pos.left})}b.start&&b.recid&&(e.selectNone(),b.start=!1);var k=[],g="TR"==a.target.tagName?$(a.target).attr("recid"):$(a.target).parents("tr").attr("recid");if("undefined"!=typeof g){var l=e.get(b.recid,!0);if(null!==l){var m=e.get(g,!0);if(null!==m){var n=parseInt(b.column),o=parseInt("TD"==a.target.tagName?$(a.target).attr("col"):$(a.target).parents("td").attr("col"));if(l>m){var d=l;l=m,m=d}var d="ind1:"+l+",ind2;"+m+",col1:"+n+",col2:"+o;if(b.range!=d){b.range=d;for(var p=l;m>=p;p++)if(!(e.last.searchIds.length>0&&-1==e.last.searchIds.indexOf(p)))if("row"!=e.selectType){if(n>o){var d=n;n=o,o=d}for(var d=[],q=n;o>=q;q++)e.columns[q].hidden||k.push({recid:e.records[p].recid,column:parseInt(q)})}else k.push(e.records[p].recid);if("row"!=e.selectType){var r=e.getSelection(),d=[];for(var s in k){var t=!1;for(var u in r)k[s].recid==r[u].recid&&k[s].column==r[u].column&&(t=!0);t||d.push({recid:k[s].recid,column:k[s].column})}e.select.apply(e,d);var d=[];for(var u in r){var t=!1;for(var s in k)k[s].recid==r[u].recid&&k[s].column==r[u].column&&(t=!0);t||d.push({recid:r[u].recid,column:r[u].column})}e.unselect.apply(e,d)}else if(e.multiSelect){var r=e.getSelection();for(var s in k)-1==r.indexOf(k[s])&&e.select(k[s]);for(var u in r)-1==k.indexOf(r[u])&&e.unselect(r[u])}}}}}}}function d(a){var b=e.last.move;if(setTimeout(function(){delete e.last.cancelClick},1),!$(a.target).parents().hasClass(".w2ui-head")&&!$(a.target).hasClass(".w2ui-head")){if(b&&"select"==b.type&&1==e.reorderRows){var f=e.get(b.from,!0),g=e.records[f];e.records.splice(f,1);var h=e.get(b.to,!0);f>h?e.records.splice(h,0,g):e.records.splice(h+1,0,g),$("#grid_"+e.name+"_ghost").remove(),e.refresh()}delete e.last.move,$(document).off("mousemove",c),$(document).off("mouseup",d)}}var e=this,f=(new Date).getTime();if("undefined"!=typeof a&&null!=a&&($(this.box).find("#grid_"+this.name+"_body").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-reset w2ui-grid").html(""),this.box=a),this.box){null==this.last.sortData&&(this.last.sortData=this.sortData);var g=this.trigger({phase:"before",target:this.name,type:"render",box:a});if(g.isCancelled!==!0){if($(this.box).attr("name",this.name).addClass("w2ui-reset w2ui-grid").html('<div> <div id="grid_'+this.name+'_header" class="w2ui-grid-header"></div> <div id="grid_'+this.name+'_toolbar" class="w2ui-grid-toolbar"></div> <div id="grid_'+this.name+'_body" class="w2ui-grid-body"></div> <div id="grid_'+this.name+'_summary" class="w2ui-grid-body w2ui-grid-summary"></div> <div id="grid_'+this.name+'_footer" class="w2ui-grid-footer"></div></div>'),"row"!=this.selectType&&$(this.box).addClass("w2ui-ss"),$(this.box).length>0&&($(this.box)[0].style.cssText+=this.style),this.initToolbar(),null!=this.toolbar&&this.toolbar.render($("#grid_"+this.name+"_toolbar")[0]),this.last.field&&"all"!=this.last.field){var h=this.searchData;this.initAllField(this.last.field,1==h.length?h[0].value:null)}return $("#grid_"+this.name+"_footer").html(this.getFooterHTML()),this.last.state||(this.last.state=this.stateSave(!0)),this.stateRestore(),this.url&&this.refresh(),this.reload(),$(this.box).on("mousedown",b),$(this.box).on("selectstart",function(){return!1}),this.trigger($.extend(g,{phase:"after"})),0==$(".w2ui-layout").length&&(this.tmp_resize=function(){w2ui[e.name].resize()},$(window).off("resize",this.tmp_resize).on("resize",this.tmp_resize)),(new Date).getTime()-f}}},destroy:function(){var a=this.trigger({phase:"before",target:this.name,type:"destroy"});a.isCancelled!==!0&&($(window).off("resize",this.tmp_resize),"object"==typeof this.toolbar&&this.toolbar.destroy&&this.toolbar.destroy(),$(this.box).find("#grid_"+this.name+"_body").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-reset w2ui-grid").html(""),delete w2ui[this.name],this.trigger($.extend(a,{phase:"after"})))},initColumnOnOff:function(){if(this.show.toolbarColumns){var a=this,b='<div class="w2ui-col-on-off"><table><tr><td style="width: 30px"> <input id="grid_'+this.name+'_column_ln_check" type="checkbox" tabIndex="-1" '+(a.show.lineNumbers?"checked":"")+" onclick=\"w2ui['"+a.name+"'].columnOnOff(this, event, 'line-numbers');\"></td><td onclick=\"w2ui['"+a.name+"'].columnOnOff(this, event, 'line-numbers'); $('#w2ui-overlay')[0].hide();\"> <label for=\"grid_"+this.name+'_column_ln_check">'+w2utils.lang("Line #")+"</label></td></tr>";for(var c in this.columns){var d=this.columns[c],e=this.columns[c].caption;d.hideable!==!1&&(!e&&this.columns[c].hint&&(e=this.columns[c].hint),e||(e="- column "+(parseInt(c)+1)+" -"),b+='<tr><td style="width: 30px"> <input id="grid_'+this.name+"_column_"+c+'_check" type="checkbox" tabIndex="-1" '+(d.hidden?"":"checked")+" onclick=\"w2ui['"+a.name+"'].columnOnOff(this, event, '"+d.field+'\');"></td><td> <label for="grid_'+this.name+"_column_"+c+'_check">'+e+"</label></td></tr>")}b+='<tr><td colspan="2"><div style="border-top: 1px solid #ddd;"></div></td></tr>';var f="object"!=typeof this.url?this.url:this.url.get;f&&a.show.skipRecords&&(b+='<tr><td colspan="2" style="padding: 0px"> <div style="cursor: pointer; padding: 2px 8px; cursor: default">'+w2utils.lang("Skip")+' <input type="text" style="width: 45px" value="'+this.offset+'" onkeypress="if (event.keyCode == 13) { w2ui[\''+a.name+"'].skip(this.value); $('#w2ui-overlay')[0].hide(); }\"> "+w2utils.lang("Records")+" </div></td></tr>"),b+='<tr><td colspan="2" onclick="w2ui[\''+a.name+"'].stateSave(); $('#w2ui-overlay')[0].hide();\"> <div style=\"cursor: pointer; padding: 4px 8px; cursor: default\">"+w2utils.lang("Save Grid State")+'</div></td></tr><tr><td colspan="2" onclick="w2ui[\''+a.name+"'].stateReset(); $('#w2ui-overlay')[0].hide();\"> <div style=\"cursor: pointer; padding: 4px 8px; cursor: default\">"+w2utils.lang("Restore Default State")+"</div></td></tr>",b+="</table></div>",this.toolbar.get("w2ui-column-on-off").html=b}},initColumnDrag:function(){function a(){i.pressed=!1,clearTimeout(i.timeout)}function b(a){i.timeout&&clearTimeout(i.timeout);var b=this;i.pressed=!0,i.timeout=setTimeout(function(){if(i.pressed){var e,f,g,j,k,l=["w2ui-col-number","w2ui-col-expand","w2ui-col-select"],m=["w2ui-head-last"],n=l.concat(m),o=".w2ui-col-number, .w2ui-col-expand, .w2ui-col-select",p=".w2ui-head.w2ui-col-number, .w2ui-head.w2ui-col-expand, .w2ui-head.w2ui-col-select";if($(a.originalEvent.target).parents().hasClass("w2ui-head")){for(var q=0,r=n.length;r>q;q++)if($(a.originalEvent.target).parents().hasClass(n[q]))return;if(i.numberPreColumnsPresent=$(h.box).find(p).length,i.columnHead=j=$(a.originalEvent.target).parents(".w2ui-head"),k=parseInt(j.attr("col"),10),e=h.trigger({type:"columnDragStart",phase:"before",originalEvent:a,origColumnNumber:k,target:j[0]}),e.isCancelled===!0)return!1;f=i.columns=$(h.box).find(".w2ui-head:not(.w2ui-head-last)"),$(document).on("mouseup",d),$(document).on("mousemove",c),i.originalPos=parseInt($(a.originalEvent.target).parent(".w2ui-head").attr("col"),10),i.ghost=$(b).clone(!0),$(i.ghost).find('[col]:not([col="'+i.originalPos+'"]), .w2ui-toolbar, .w2ui-grid-header').remove(),$(i.ghost).find(o).remove(),$(i.ghost).find(".w2ui-grid-body").css({top:0}),g=$(i.ghost).find('[col="'+i.originalPos+'"]'),$(document.body).append(i.ghost),$(i.ghost).css({width:0,height:0,margin:0,position:"fixed",zIndex:999999,opacity:0}).addClass(".w2ui-grid-ghost").animate({width:g.width(),height:$(h.box).find(".w2ui-grid-body:first").height(),left:a.pageX,top:a.pageY,opacity:.8},0),i.offsets=[];for(var q=0,r=f.length;r>q;q++)i.offsets.push($(f[q]).offset().left);h.trigger($.extend(e,{phase:"after"}))}}},150)}function c(a){if(i.pressed){var b=a.originalEvent.pageX,c=a.originalEvent.pageY,d=i.offsets,h=$(".w2ui-head:not(.w2ui-head-last)").width();i.targetInt=Math.max(i.numberPreColumnsPresent,f(b,d,h)),e(i.targetInt),g(b,c)}}function d(a){i.pressed=!1;var b,e,f,g,j,k=$(".w2ui-grid-ghost");return b=h.trigger({type:"columnDragEnd",phase:"before",originalEvent:a,target:i.columnHead[0]}),b.isCancelled===!0?!1:(f=h.columns[i.originalPos],g=h.columns,j=$(i.columns[Math.min(i.lastInt,i.columns.length-1)]),e=i.lastInt<i.columns.length?parseInt(j.attr("col")):g.length,e!==i.originalPos+1&&e!==i.originalPos&&j&&j.length?($(i.ghost).animate({top:$(h.box).offset().top,left:j.offset().left,width:0,height:0,opacity:.2},300,function(){$(this).remove(),k.remove()}),g.splice(e,0,$.extend({},f)),g.splice(g.indexOf(f),1)):($(i.ghost).remove(),k.remove()),$(document).off("mouseup",d),$(document).off("mousemove",c),i.marker&&i.marker.remove(),i={},h.refresh(),void h.trigger($.extend(b,{phase:"after",targetColumnNumber:e-1})))}function e(a){i.marker||i.markerLeft||(i.marker=$('<div class="col-intersection-marker"><div class="top-marker"></div><div class="bottom-marker"></div></div>'),i.markerLeft=$('<div class="col-intersection-marker"><div class="top-marker"></div><div class="bottom-marker"></div></div>')),i.lastInt&&i.lastInt===a||(i.lastInt=a,i.marker.remove(),i.markerLeft.remove(),$(".w2ui-head").removeClass("w2ui-col-intersection"),a>=i.columns.length?($(i.columns[i.columns.length-1]).children("div:last").append(i.marker.addClass("right").removeClass("left")),$(i.columns[i.columns.length-1]).addClass("w2ui-col-intersection")):a<=i.numberPreColumnsPresent?($(i.columns[i.numberPreColumnsPresent]).prepend(i.marker.addClass("left").removeClass("right")).css({position:"relative"}),$(i.columns[i.numberPreColumnsPresent]).prev().addClass("w2ui-col-intersection")):($(i.columns[a]).children("div:last").prepend(i.marker.addClass("left").removeClass("right")),$(i.columns[a]).prev().children("div:last").append(i.markerLeft.addClass("right").removeClass("left")).css({position:"relative"}),$(i.columns[a-1]).addClass("w2ui-col-intersection")))}function f(a,b,c){if(a<=b[0])return 0;if(a>=b[b.length-1]+c)return b.length;for(var d=0,e=b.length;e>d;d++){var f=b[d],g=b[d+1]||b[d]+c,h=(g-b[d])/2+b[d];if(a>f&&h>=a)return d;if(a>h&&g>=a)return d+1}return intersection}function g(a,b){$(i.ghost).css({left:a-10,top:b-10})}if(this.columnGroups&&this.columnGroups.length)throw"Draggable columns are not currently supported with column groups.";var h=this,i={};return i.lastInt=null,i.pressed=!1,i.timeout=null,i.columnHead=null,$(h.box).on("mousedown",b),$(h.box).on("mouseup",a),{remove:function(){$(h.box).off("mousedown",b),$(h.box).off("mouseup",a),$(h.box).find(".w2ui-head").removeAttr("draggable"),h.last.columnDrag=!1}}},columnOnOff:function(a,b,c){var d=this.trigger({phase:"before",target:this.name,type:"columnOnOff",checkbox:a,field:c,originalEvent:b});if(d.isCancelled!==!0){var e=this;for(var f in this.records)this.records[f].expanded===!0&&(this.records[f].expanded=!1);var g=!0;if("line-numbers"==c)this.show.lineNumbers=!this.show.lineNumbers,this.refresh();else{var h=this.getColumn(c);h.hidden?($(a).prop("checked",!0),this.showColumn(h.field)):($(a).prop("checked",!1),this.hideColumn(h.field)),g=!1}g&&setTimeout(function(){$().w2overlay("",{name:"searches-"+this.name}),e.toolbar.uncheck("column-on-off")},100),this.trigger($.extend(d,{phase:"after"}))}},initToolbar:function(){if("undefined"==typeof this.toolbar.render){var a=this.toolbar.items;if(this.toolbar.items=[],this.toolbar=$().w2toolbar($.extend(!0,{},this.toolbar,{name:this.name+"_toolbar",owner:this})),this.show.toolbarReload&&this.toolbar.items.push($.extend(!0,{},this.buttons.reload)),this.show.toolbarColumns&&this.toolbar.items.push($.extend(!0,{},this.buttons.columns)),(this.show.toolbarReload||this.show.toolbarColumn)&&this.toolbar.items.push({type:"break",id:"w2ui-break0"}),this.show.toolbarSearch){var b='<div class="w2ui-toolbar-search"><table cellpadding="0" cellspacing="0"><tr> <td>'+this.buttons.search.html+'</td> <td> <input id="grid_'+this.name+'_search_all" class="w2ui-search-all" placeholder="'+this.last.caption+'" value="'+this.last.search+'" onkeydown="if (event.keyCode == 13 && w2utils.isIE) this.onchange();" onchange=" var val = this.value; var fld = $(this).data(\'w2field\'); if (fld) val = fld.clean(val); w2ui[\''+this.name+"'].search(w2ui['"+this.name+'\'].last.field, val); "> </td> <td> <div title="'+w2utils.lang("Clear Search")+'" class="w2ui-search-clear" id="grid_'+this.name+'_searchClear" onclick="var obj = w2ui[\''+this.name+"']; obj.searchReset();\" >&nbsp;&nbsp;</div> </td></tr></table></div>";this.toolbar.items.push({type:"html",id:"w2ui-search",html:b}),this.multiSearch&&this.searches.length>0&&this.toolbar.items.push($.extend(!0,{},this.buttons["search-go"]))}this.show.toolbarSearch&&(this.show.toolbarAdd||this.show.toolbarEdit||this.show.toolbarDelete||this.show.toolbarSave)&&this.toolbar.items.push({type:"break",id:"w2ui-break1"}),this.show.toolbarAdd&&this.toolbar.items.push($.extend(!0,{},this.buttons.add)),this.show.toolbarEdit&&this.toolbar.items.push($.extend(!0,{},this.buttons.edit)),this.show.toolbarDelete&&this.toolbar.items.push($.extend(!0,{},this.buttons["delete"])),this.show.toolbarSave&&((this.show.toolbarAdd||this.show.toolbarDelete||this.show.toolbarEdit)&&this.toolbar.items.push({type:"break",id:"w2ui-break2"}),this.toolbar.items.push($.extend(!0,{},this.buttons.save)));for(var c in a)this.toolbar.items.push(a[c]);var d=this;this.toolbar.on("click",function(a){function b(){$("#w2ui-overlay-searches-"+d.name).data("keepOpen")!==!0&&(g.uncheck(e),$(document).off("click","body",b))}var c=d.trigger({phase:"before",type:"toolbar",target:a.target,originalEvent:a});if(c.isCancelled!==!0){var e=a.target;switch(e){case"w2ui-reload":var f=d.trigger({phase:"before",type:"reload",target:d.name});if(f.isCancelled===!0)return!1;d.reload(),d.trigger($.extend(f,{phase:"after"}));break;case"w2ui-column-on-off":d.initColumnOnOff(),d.initResize(),d.resize();break;case"w2ui-search-advanced":var g=this,h=this.get(e);h.checked?(d.searchClose(),setTimeout(function(){g.uncheck(e)},1)):(d.searchOpen(),a.originalEvent.stopPropagation(),$(document).on("click","body",b));break;case"w2ui-add":var c=d.trigger({phase:"before",target:d.name,type:"add",recid:null});d.trigger($.extend(c,{phase:"after"}));break;case"w2ui-edit":var i=d.getSelection(),j=null;1==i.length&&(j=i[0]);var c=d.trigger({phase:"before",target:d.name,type:"edit",recid:j});d.trigger($.extend(c,{phase:"after"}));break;case"w2ui-delete":d["delete"]();break;case"w2ui-save":d.save()}d.trigger($.extend(c,{phase:"after"}))}})}},initResize:function(){var a=this;$(this.box).find(".w2ui-resizer").off("click").on("click",function(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0,a.preventDefault&&a.preventDefault()}).off("mousedown").on("mousedown",function(b){b||(b=window.event),window.addEventListener||window.document.attachEvent("onselectstart",function(){return!1}),a.resizing=!0,a.last.tmp={x:b.screenX,y:b.screenY,gx:b.screenX,gy:b.screenY,col:parseInt($(this).attr("name"))},b.stopPropagation?b.stopPropagation():b.cancelBubble=!0,b.preventDefault&&b.preventDefault();for(var c in a.columns)"undefined"==typeof a.columns[c].sizeOriginal&&(a.columns[c].sizeOriginal=a.columns[c].size),a.columns[c].size=a.columns[c].sizeCalculated;var d={phase:"before",type:"columnResize",target:a.name,column:a.last.tmp.col,field:a.columns[a.last.tmp.col].field};d=a.trigger($.extend(d,{resizeBy:0,originalEvent:b}));var e=function(b){if(1==a.resizing){if(b||(b=window.event),d=a.trigger($.extend(d,{resizeBy:b.screenX-a.last.tmp.gx,originalEvent:b})),d.isCancelled===!0)return void(d.isCancelled=!1);a.last.tmp.x=b.screenX-a.last.tmp.x,a.last.tmp.y=b.screenY-a.last.tmp.y,a.columns[a.last.tmp.col].size=parseInt(a.columns[a.last.tmp.col].size)+a.last.tmp.x+"px",a.resizeRecords(),a.last.tmp.x=b.screenX,a.last.tmp.y=b.screenY}},f=function(b){delete a.resizing,$(document).off("mousemove","body"),$(document).off("mouseup","body"),a.resizeRecords(),a.trigger($.extend(d,{phase:"after",originalEvent:b}))};$(document).on("mousemove","body",e),$(document).on("mouseup","body",f)}).each(function(a,b){var c=$(b).parent();$(b).css({height:"25px","margin-left":c.width()-3+"px"})})},resizeBoxes:function(){{var a=($(this.box).find("> div"),$("#grid_"+this.name+"_header")),b=$("#grid_"+this.name+"_toolbar"),c=$("#grid_"+this.name+"_summary"),d=$("#grid_"+this.name+"_footer"),e=$("#grid_"+this.name+"_body");$("#grid_"+this.name+"_columns"),$("#grid_"+this.name+"_records")}this.show.header&&a.css({top:"0px",left:"0px",right:"0px"}),this.show.toolbar&&b.css({top:0+(this.show.header?w2utils.getSize(a,"height"):0)+"px",left:"0px",right:"0px"}),this.show.footer&&d.css({bottom:"0px",left:"0px",right:"0px"}),this.summary.length>0&&c.css({bottom:0+(this.show.footer?w2utils.getSize(d,"height"):0)+"px",left:"0px",right:"0px"}),e.css({top:0+(this.show.header?w2utils.getSize(a,"height"):0)+(this.show.toolbar?w2utils.getSize(b,"height"):0)+"px",bottom:0+(this.show.footer?w2utils.getSize(d,"height"):0)+(this.summary.length>0?w2utils.getSize(c,"height"):0)+"px",left:"0px",right:"0px"})},resizeRecords:function(){var a=this;$(this.box).find(".w2ui-empty-record").remove();var b=$(this.box),c=$(this.box).find("> div"),d=$("#grid_"+this.name+"_header"),e=$("#grid_"+this.name+"_toolbar"),f=$("#grid_"+this.name+"_summary"),g=$("#grid_"+this.name+"_footer"),h=$("#grid_"+this.name+"_body"),i=$("#grid_"+this.name+"_columns"),j=$("#grid_"+this.name+"_records");if(this.fixedBody){var k=c.height()-(this.show.header?w2utils.getSize(d,"height"):0)-(this.show.toolbar?w2utils.getSize(e,"height"):0)-("none"!=f.css("display")?w2utils.getSize(f,"height"):0)-(this.show.footer?w2utils.getSize(g,"height"):0);h.css("height",k)}else{var k=w2utils.getSize(i,"height")+w2utils.getSize($("#grid_"+a.name+"_records table"),"height");a.height=k+w2utils.getSize(c,"+height")+(a.show.header?w2utils.getSize(d,"height"):0)+(a.show.toolbar?w2utils.getSize(e,"height"):0)+("none"!=f.css("display")?w2utils.getSize(f,"height"):0)+(a.show.footer?w2utils.getSize(g,"height"):0),c.css("height",a.height),h.css("height",k),b.css("height",w2utils.getSize(c,"height")+w2utils.getSize(b,"+height"))}var l=this.records.length;0==this.searchData.length||this.url||(l=this.last.searchIds.length);var m=!1,n=!1;if(h.width()<$(j).find(">table").width()&&(m=!0),h.height()-i.height()<$(j).find(">table").height()+(m?w2utils.scrollBarSize():0)&&(n=!0),this.fixedBody||(n=!1,m=!1),m||n?(i.find("> table > tbody > tr:nth-child(1) td.w2ui-head-last").css("width",w2utils.scrollBarSize()).show(),j.css({top:(this.columnGroups.length>0&&this.show.columns?1:0)+w2utils.getSize(i,"height")+"px","-webkit-overflow-scrolling":"touch","overflow-x":m?"auto":"hidden","overflow-y":n?"auto":"hidden"})):(i.find("> table > tbody > tr:nth-child(1) td.w2ui-head-last").hide(),j.css({top:(this.columnGroups.length>0&&this.show.columns?1:0)+w2utils.getSize(i,"height")+"px",overflow:"hidden"}),j.length>0&&(this.last.scrollTop=0,this.last.scrollLeft=0)),this.show.emptyRecords&&!n){var o=Math.floor(j.height()/this.recordHeight)+1;if(this.fixedBody)for(var p=l;o>=p;p++){var q="";q+='<tr class="'+(p%2?"w2ui-even":"w2ui-odd")+' w2ui-empty-record" style="height: '+this.recordHeight+'px">',this.show.lineNumbers&&(q+='<td class="w2ui-col-number"></td>'),this.show.selectColumn&&(q+='<td class="w2ui-grid-data w2ui-col-select"></td>'),this.show.expandColumn&&(q+='<td class="w2ui-grid-data w2ui-col-expand"></td>');for(var r=0;this.columns.length>0;){var s=this.columns[r];if(s.hidden){if(r++,"undefined"==typeof this.columns[r])break}else if(q+='<td class="w2ui-grid-data" '+("undefined"!=typeof s.attr?s.attr:"")+' col="'+r+'"></td>',r++,"undefined"==typeof this.columns[r])break}q+='<td class="w2ui-grid-data-last"></td>',q+="</tr>",$("#grid_"+this.name+"_records > table").append(q)}}if(h.length>0){for(var t=parseInt(h.width())-(n?w2utils.scrollBarSize():0)-(this.show.lineNumbers?34:0)-(this.show.selectColumn?26:0)-(this.show.expandColumn?26:0),u=t,v=0,w=!1,x=0;x<this.columns.length;x++){var s=this.columns[x];s.gridMinWidth>0&&(s.gridMinWidth>u&&s.hidden!==!0&&(s.hidden=!0,w=!0),s.gridMinWidth<u&&s.hidden===!0&&(s.hidden=!1,w=!0))}if(w===!0)return void this.refresh();for(var x=0;x<this.columns.length;x++){var s=this.columns[x];s.hidden||("px"==String(s.size).substr(String(s.size).length-2).toLowerCase()?(t-=parseFloat(s.size),this.columns[x].sizeCalculated=s.size,this.columns[x].sizeType="px"):(v+=parseFloat(s.size),this.columns[x].sizeType="%",delete s.sizeCorrected))}if(100!=v&&v>0)for(var x=0;x<this.columns.length;x++){var s=this.columns[x];s.hidden||"%"==s.sizeType&&(s.sizeCorrected=Math.round(100*parseFloat(s.size)*100/v)/100+"%")}for(var x=0;x<this.columns.length;x++){var s=this.columns[x];s.hidden||"%"==s.sizeType&&(this.columns[x].sizeCalculated="undefined"!=typeof this.columns[x].sizeCorrected?Math.floor(t*parseFloat(s.sizeCorrected)/100)-1+"px":Math.floor(t*parseFloat(s.size)/100)-1+"px")}}for(var y=0,x=0;x<this.columns.length;x++){var s=this.columns[x];s.hidden||("undefined"==typeof s.min&&(s.min=20),parseInt(s.sizeCalculated)<parseInt(s.min)&&(s.sizeCalculated=s.min+"px"),parseInt(s.sizeCalculated)>parseInt(s.max)&&(s.sizeCalculated=s.max+"px"),y+=parseInt(s.sizeCalculated))}var z=parseInt(u)-parseInt(y);if(z>0&&v>0)for(var x=0;;){var s=this.columns[x];if("undefined"!=typeof s)if(s.hidden||"px"==s.sizeType)x++;else{if(s.sizeCalculated=parseInt(s.sizeCalculated)+1+"px",z--,0==z)break;x++}else x=0}else z>0&&i.find("> table > tbody > tr:nth-child(1) td.w2ui-head-last").css("width",w2utils.scrollBarSize()).show();i.find("> table > tbody > tr:nth-child(1) td").each(function(b,c){var d=$(c).attr("col");"undefined"!=typeof d&&a.columns[d]&&$(c).css("width",a.columns[d].sizeCalculated),$(c).hasClass("w2ui-head-last")&&$(c).css("width",w2utils.scrollBarSize()+(z>0&&0==v?z:0)+"px")}),3==i.find("> table > tbody > tr").length&&i.find("> table > tbody > tr:nth-child(1) td").html("").css({height:"0px",border:"0px",padding:"0px",margin:"0px"}),j.find("> table > tbody > tr:nth-child(1) td").each(function(b,c){var d=$(c).attr("col");"undefined"!=typeof d&&a.columns[d]&&$(c).css("width",a.columns[d].sizeCalculated),$(c).hasClass("w2ui-grid-data-last")&&$(c).css("width",(z>0&&0==v?z:0)+"px")}),f.find("> table > tbody > tr:nth-child(1) td").each(function(b,c){var d=$(c).attr("col");"undefined"!=typeof d&&a.columns[d]&&$(c).css("width",a.columns[d].sizeCalculated),$(c).hasClass("w2ui-grid-data-last")&&$(c).css("width",w2utils.scrollBarSize()+(z>0&&0==v?z:0)+"px")}),this.initResize(),this.refreshRanges(),""!=this.last.scrollTop&&j.length>0&&(i.prop("scrollLeft",this.last.scrollLeft),j.prop("scrollTop",this.last.scrollTop),j.prop("scrollLeft",this.last.scrollLeft))},getSearchesHTML:function(){for(var a='<table cellspacing="0">',b=!1,c=0;c<this.searches.length;c++){var d=this.searches[c];if(d.type=String(d.type).toLowerCase(),!d.hidden){var e="";if(0==b&&(e='<button class="btn close-btn" onclick="obj = w2ui[\''+this.name+"']; if (obj) { obj.searchClose(); }\">X</button",b=!0),"undefined"==typeof d.inTag&&(d.inTag=""),"undefined"==typeof d.outTag&&(d.outTag=""),"undefined"==typeof d.type&&(d.type="text"),-1!=["text","alphanumeric","combo"].indexOf(d.type))var f='<select id="grid_'+this.name+"_operator_"+c+'" onclick="event.stopPropagation();"> <option value="is">'+w2utils.lang("is")+'</option> <option value="begins">'+w2utils.lang("begins")+'</option> <option value="contains">'+w2utils.lang("contains")+'</option> <option value="ends">'+w2utils.lang("ends")+"</option></select>";if(-1!=["int","float","money","currency","percent","date","time"].indexOf(d.type))var f='<select id="grid_'+this.name+"_operator_"+c+'" onchange="w2ui[\''+this.name+"'].initOperator(this, "+c+');" onclick="event.stopPropagation();"> <option value="is">'+w2utils.lang("is")+"</option>"+(-1!=["int"].indexOf(d.type)?'<option value="in">'+w2utils.lang("in")+"</option>":"")+(-1!=["int"].indexOf(d.type)?'<option value="not in">'+w2utils.lang("not in")+"</option>":"")+'<option value="between">'+w2utils.lang("between")+"</option></select>";if(-1!=["select","list","hex"].indexOf(d.type))var f='<select id="grid_'+this.name+"_operator_"+c+'" onclick="event.stopPropagation();"> <option value="is">'+w2utils.lang("is")+"</option></select>";if(-1!=["enum"].indexOf(d.type))var f='<select id="grid_'+this.name+"_operator_"+c+'" onclick="event.stopPropagation();"> <option value="in">'+w2utils.lang("in")+'</option> <option value="in">'+w2utils.lang("not in")+"</option></select>";switch(a+='<tr> <td class="close-btn">'+e+'</td> <td class="caption">'+d.caption+'</td> <td class="operator">'+f+'</td> <td class="value">',d.type){case"text":case"alphanumeric":case"hex":case"list":case"combo":case"enum":a+='<input rel="search" type="text" style="width: 300px;" id="grid_'+this.name+"_field_"+c+'" name="'+d.field+'" '+d.inTag+">";break;case"int":case"float":case"money":case"currency":case"percent":case"date":case"time":a+='<input rel="search" type="text" size="12" id="grid_'+this.name+"_field_"+c+'" name="'+d.field+'" '+d.inTag+'><span id="grid_'+this.name+"_range_"+c+'" style="display: none">&nbsp;-&nbsp;&nbsp;<input rel="search" type="text" style="width: 90px" id="grid_'+this.name+"_field2_"+c+'" name="'+d.field+'" '+d.inTag+"></span>";break;case"select":a+='<select rel="search" id="grid_'+this.name+"_field_"+c+'" name="'+d.field+'" '+d.inTag+' onclick="event.stopPropagation();"></select>'}a+=d.outTag+" </td></tr>"}}return a+='<tr> <td colspan="4" class="actions"> <div> <button class="btn" onclick="obj = w2ui[\''+this.name+"']; if (obj) { obj.searchReset(); }\">"+w2utils.lang("Reset")+'</button> <button class="btn btn-blue" onclick="obj = w2ui[\''+this.name+"']; if (obj) { obj.search(); }\">"+w2utils.lang("Search")+"</button> </div> </td></tr></table>"},initOperator:function(a,b){var c=this,d=c.searches[b],e=$("#grid_"+c.name+"_range_"+b),f=$("#grid_"+c.name+"_field_"+b),g=f.parent().find("span input");f.w2field("in"==$(a).val()||"not in"==$(a).val()?"clear":d.type),"between"==$(a).val()?(e.show(),g.w2field(d.type)):e.hide()},initSearches:function(){var a=this;for(var b in this.searches){var c=this.searches[b],d=this.getSearchData(c.field);switch(c.type=String(c.type).toLowerCase(),"object"!=typeof c.options&&(c.options={}),c.type){case"text":case"alphanumeric":$("#grid_"+this.name+"_operator_"+b).val("begins"),-1!=["alphanumeric","hex"].indexOf(c.type)&&$("#grid_"+this.name+"_field_"+b).w2field(c.type,c.options);break;case"int":case"float":case"money":case"currency":case"percent":case"date":case"time":if(d&&"int"==d.type&&-1!=["in","not in"].indexOf(d.operator))break;$("#grid_"+this.name+"_field_"+b).w2field(c.type,c.options),$("#grid_"+this.name+"_field2_"+b).w2field(c.type,c.options),setTimeout(function(){$("#grid_"+a.name+"_field_"+b).keydown(),$("#grid_"+a.name+"_field2_"+b).keydown()},1);break;case"hex":break;case"list":case"combo":case"enum":var e=c.options;"list"==c.type&&(e.selected={}),"enum"==c.type&&(e.selected=[]),d&&(e.selected=d.value),$("#grid_"+this.name+"_field_"+b).w2field(c.type,e),"combo"==c.type&&$("#grid_"+this.name+"_operator_"+b).val("begins");break;case"select":var e='<option value="">--</option>';for(var f in c.options.items){var g=c.options.items[f];if($.isPlainObject(c.options.items[f])){var h=g.id,i=g.text;"undefined"==typeof h&&"undefined"!=typeof g.value&&(h=g.value),"undefined"==typeof i&&"undefined"!=typeof g.caption&&(i=g.caption),null==h&&(h=""),e+='<option value="'+h+'">'+i+"</option>"}else e+='<option value="'+g+'">'+g+"</option>"}$("#grid_"+this.name+"_field_"+b).html(e)}null!=d&&("int"==d.type&&-1!=["in","not in"].indexOf(d.operator)&&$("#grid_"+this.name+"_field_"+b).w2field("clear").val(d.value),$("#grid_"+this.name+"_operator_"+b).val(d.operator).trigger("change"),$.isArray(d.value)?-1!=["in","not in"].indexOf(d.operator)?$("#grid_"+this.name+"_field_"+b).val(d.value).trigger("change"):($("#grid_"+this.name+"_field_"+b).val(d.value[0]).trigger("change"),$("#grid_"+this.name+"_field2_"+b).val(d.value[1]).trigger("change")):"udefined"!=typeof d.value&&$("#grid_"+this.name+"_field_"+b).val(d.value).trigger("change"))}$("#w2ui-overlay-searches-"+this.name+" .w2ui-grid-searches *[rel=search]").on("keypress",function(b){13==b.keyCode&&(a.search(),$().w2overlay())})},getColumnsHTML:function(){function a(){var a="<tr>";""!=c.columnGroups[c.columnGroups.length-1].caption&&c.columnGroups.push({caption:""}),c.show.lineNumbers&&(a+='<td class="w2ui-head w2ui-col-number"> <div>&nbsp;</div></td>'),c.show.selectColumn&&(a+='<td class="w2ui-head w2ui-col-select"> <div>&nbsp;</div></td>'),c.show.expandColumn&&(a+='<td class="w2ui-head w2ui-col-expand"> <div>&nbsp;</div></td>');for(var b=0,d=0;d<c.columnGroups.length;d++){var e=c.columnGroups[d],f=c.columns[b];if(("undefined"==typeof e.span||e.span!=parseInt(e.span))&&(e.span=1),"undefined"!=typeof e.colspan&&(e.span=e.colspan),e.master===!0){var g="";
+for(var h in c.sortData)c.sortData[h].field==f.field&&(RegExp("asc","i").test(c.sortData[h].direction)&&(g="w2ui-sort-up"),RegExp("desc","i").test(c.sortData[h].direction)&&(g="w2ui-sort-down"));var i="";f.resizable!==!1&&(i='<div class="w2ui-resizer" name="'+b+'"></div>'),a+='<td class="w2ui-head '+g+'" col="'+b+'" rowspan="2" colspan="'+(e.span+(d==c.columnGroups.length-1?1:0))+'" onclick="w2ui[\''+c.name+"'].columnClick('"+f.field+"', event);\">"+i+' <div class="w2ui-col-group w2ui-col-header '+(g?"w2ui-col-sorted":"")+'"> <div class="'+g+'"></div>'+(f.caption?f.caption:"&nbsp;")+" </div></td>"}else a+='<td class="w2ui-head" col="'+b+'" colspan="'+(e.span+(d==c.columnGroups.length-1?1:0))+'"> <div class="w2ui-col-group">'+(e.caption?e.caption:"&nbsp;")+" </div></td>";b+=e.span}return a+="</tr>"}function b(a){var b="<tr>",d=!c.reorderColumns||c.columnGroups&&c.columnGroups.length?"":" w2ui-reorder-cols-head ";c.show.lineNumbers&&(b+='<td class="w2ui-head w2ui-col-number" onclick="w2ui[\''+c.name+"'].columnClick('line-number', event);\"> <div>#</div></td>"),c.show.selectColumn&&(b+='<td class="w2ui-head w2ui-col-select" onclick="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;"> <div> <input type="checkbox" id="grid_'+c.name+'_check_all" tabIndex="-1" style="'+(0==c.multiSelect?"display: none;":"")+'" onclick="if (this.checked) w2ui[\''+c.name+"'].selectAll(); else w2ui['"+c.name+"'].selectNone(); if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;\"> </div></td>"),c.show.expandColumn&&(b+='<td class="w2ui-head w2ui-col-expand"> <div>&nbsp;</div></td>');for(var e=0,f=0,g=0;g<c.columns.length;g++){var h=c.columns[g],i={};if(g==f&&(f+="undefined"!=typeof c.columnGroups[e]?parseInt(c.columnGroups[e].span):0,e++),"undefined"!=typeof c.columnGroups[e-1])var i=c.columnGroups[e-1];if(!h.hidden){var j="";for(var k in c.sortData)c.sortData[k].field==h.field&&(RegExp("asc","i").test(c.sortData[k].direction)&&(j="w2ui-sort-up"),RegExp("desc","i").test(c.sortData[k].direction)&&(j="w2ui-sort-down"));if(i.master!==!0||a){var l="";h.resizable!==!1&&(l='<div class="w2ui-resizer" name="'+g+'"></div>'),b+='<td col="'+g+'" class="w2ui-head '+j+d+'" onclick="w2ui[\''+c.name+"'].columnClick('"+h.field+"', event);\">"+l+' <div class="w2ui-col-header '+(j?"w2ui-col-sorted":"")+'"> <div class="'+j+'"></div>'+(h.caption?h.caption:"&nbsp;")+" </div></td>"}}}return b+='<td class="w2ui-head w2ui-head-last"><div>&nbsp;</div></td>',b+="</tr>"}var c=this,d="";return this.show.columnHeaders&&(d=this.columnGroups.length>0?b(!0)+a()+b(!1):b(!0)),d},getRecordsHTML:function(){var a=this.records.length;0==this.searchData.length||this.url||(a=this.last.searchIds.length),this.show_extra=a>300?30:300;var b=$("#grid_"+this.name+"_records"),c=Math.floor(b.height()/this.recordHeight)+this.show_extra+1;(!this.fixedBody||c>a)&&(c=a);var d="<table>"+this.getRecordHTML(-1,0);d+='<tr id="grid_'+this.name+'_rec_top" line="top" style="height: 0px"> <td colspan="200"></td></tr>';for(var e=0;c>e;e++)d+=this.getRecordHTML(e,e+1);return d+='<tr id="grid_'+this.name+'_rec_bottom" line="bottom" style="height: '+(a-c)*this.recordHeight+'px"> <td colspan="200"></td></tr><tr id="grid_'+this.name+'_rec_more" style="display: none"> <td colspan="200" class="w2ui-load-more"></td></tr></table>',this.last.range_start=0,this.last.range_end=c,d},getSummaryHTML:function(){if(0!=this.summary.length){for(var a="<table>",b=0;b<this.summary.length;b++)a+=this.getRecordHTML(b,b+1,!0);return a+="</table>"}},scroll:function(){function a(){b.markSearch!==!1&&(clearTimeout(b.last.marker_timer),b.last.marker_timer=setTimeout(function(){var a=[];for(var c in b.searchData){var d=b.searchData[c];-1==$.inArray(d.value,a)&&a.push(d.value)}a.length>0&&$(b.box).find(".w2ui-grid-data > div").w2marker(a)},50))}var b=((new Date).getTime(),this),c=$("#grid_"+this.name+"_records"),d=this.records.length;if(0==this.searchData.length||this.url||(d=this.last.searchIds.length),0!=d&&0!=c.length&&0!=c.height()){if(this.show_extra=d>300?30:300,c.height()<d*this.recordHeight&&"hidden"==c.css("overflow-y"))return void(this.total>0&&this.refresh());var e=Math.round(c[0].scrollTop/this.recordHeight+1),f=e+(Math.round(c.height()/this.recordHeight)-1);e>d&&(e=d),f>d&&(f=d);var g="object"!=typeof this.url?this.url:this.url.get;if($("#grid_"+this.name+"_footer .w2ui-footer-right").html(w2utils.formatNumber(this.offset+e)+"-"+w2utils.formatNumber(this.offset+f)+" "+w2utils.lang("of")+" "+w2utils.formatNumber(this.total)+(g?" ("+w2utils.lang("buffered")+" "+w2utils.formatNumber(d)+(this.offset>0?", skip "+w2utils.formatNumber(this.offset):"")+")":"")),g||this.fixedBody&&!(this.total<=300)){var h=Math.floor(c[0].scrollTop/this.recordHeight)-this.show_extra,i=h+Math.floor(c.height()/this.recordHeight)+2*this.show_extra+1;1>h&&(h=1),i>this.total&&(i=this.total);var j=c.find("#grid_"+this.name+"_rec_top"),k=c.find("#grid_"+this.name+"_rec_bottom");-1!=String(j.next().prop("id")).indexOf("_expanded_row")&&j.next().remove(),this.total>i&&-1!=String(k.prev().prop("id")).indexOf("_expanded_row")&&k.prev().remove();var l=parseInt(j.next().attr("line")),m=parseInt(k.prev().attr("line"));if(h>l||1==l||this.last.pull_refresh){if(i<=m+this.show_extra-2&&i!=this.total)return;for(this.last.pull_refresh=!1;;){var n=c.find("#grid_"+this.name+"_rec_top").next();if("bottom"==n.attr("line"))break;if(!(parseInt(n.attr("line"))<h))break;n.remove()}var n=c.find("#grid_"+this.name+"_rec_bottom").prev(),o=n.attr("line");"top"==o&&(o=h);for(var p=parseInt(o)+1;i>=p;p++)this.records[p-1]&&(this.records[p-1].expanded===!0&&(this.records[p-1].expanded=!1),k.before(this.getRecordHTML(p-1,p)));a(),setTimeout(function(){b.refreshRanges()},0)}else{if(h>=l-this.show_extra+2&&h>1)return;for(;;){var n=c.find("#grid_"+this.name+"_rec_bottom").prev();if("top"==n.attr("line"))break;if(!(parseInt(n.attr("line"))>i))break;n.remove()}var n=c.find("#grid_"+this.name+"_rec_top").next(),o=n.attr("line");"bottom"==o&&(o=i);for(var p=parseInt(o)-1;p>=h;p--)this.records[p-1]&&(this.records[p-1].expanded===!0&&(this.records[p-1].expanded=!1),j.after(this.getRecordHTML(p-1,p)));a(),setTimeout(function(){b.refreshRanges()},0)}var q=(h-1)*b.recordHeight,r=(d-i)*b.recordHeight;0>r&&(r=0),j.css("height",q+"px"),k.css("height",r+"px"),b.last.range_start=h,b.last.range_end=i;var s=Math.floor(c[0].scrollTop/this.recordHeight),t=s+Math.floor(c.height()/this.recordHeight);if(t+10>d&&this.last.pull_more!==!0&&d<this.total-this.offset)if(this.autoLoad===!0)this.last.pull_more=!0,this.last.xhr_offset+=this.limit,this.request("get-records");else{var u=$("#grid_"+this.name+"_rec_more");"none"==u.css("display")&&u.show().on("click",function(){b.last.pull_more=!0,b.last.xhr_offset+=b.limit,b.request("get-records"),$(this).find("td").html('<div><div style="width: 20px; height: 20px;" class="w2ui-spinner"></div></div>')}),-1==u.find("td").text().indexOf("Load")&&u.find("td").html("<div>"+w2utils.lang("Load")+" "+b.limit+" "+w2utils.lang("More")+"...</div>")}d>=this.total-this.offset&&$("#grid_"+this.name+"_rec_more").hide()}}},getRecordHTML:function(a,b,c){var d,e="",f=this.last.selection;if(-1==a){e+='<tr line="0">',this.show.lineNumbers&&(e+='<td class="w2ui-col-number" style="height: 0px;"></td>'),this.show.selectColumn&&(e+='<td class="w2ui-col-select" style="height: 0px;"></td>'),this.show.expandColumn&&(e+='<td class="w2ui-col-expand" style="height: 0px;"></td>');for(var g in this.columns)this.columns[g].hidden||(e+='<td class="w2ui-grid-data" col="'+g+'" style="height: 0px;"></td>');return e+='<td class="w2ui-grid-data-last" style="height: 0px;"></td>',e+="</tr>"}var h="object"!=typeof this.url?this.url:this.url.get;if(c!==!0)if(this.searchData.length>0&&!h){if(a>=this.last.searchIds.length)return"";a=this.last.searchIds[a],d=this.records[a]}else{if(a>=this.records.length)return"";d=this.records[a]}else{if(a>=this.summary.length)return"";d=this.summary[a]}if(!d)return"";var i=(w2utils.escapeId(d.recid),!1);if(-1!=f.indexes.indexOf(a)&&(i=!0),e+='<tr id="grid_'+this.name+"_rec_"+d.recid+'" recid="'+d.recid+'" line="'+b+'" class="'+(b%2==0?"w2ui-even":"w2ui-odd")+(i&&"row"==this.selectType?" w2ui-selected":"")+(d.expanded===!0?" w2ui-expanded":"")+'" '+(c!==!0?w2utils.isIOS?" onclick = \"w2ui['"+this.name+"'].dblClick('"+d.recid+"', event);\"":" onclick = \"w2ui['"+this.name+"'].click('"+d.recid+"', event);\" oncontextmenu = \"w2ui['"+this.name+"'].contextMenu('"+d.recid+"', event);\"":"")+' style="height: '+this.recordHeight+"px; "+(i||"string"!=typeof d.style?"":d.style)+'" '+("string"==typeof d.style?'custom_style="'+d.style+'"':"")+">",this.show.lineNumbers&&(e+='<td id="grid_'+this.name+"_cell_"+a+"_number"+(c?"_s":"")+'" class="w2ui-col-number">'+(c!==!0?"<div>"+b+"</div>":"")+"</td>"),this.show.selectColumn&&(e+='<td id="grid_'+this.name+"_cell_"+a+"_select"+(c?"_s":"")+'" class="w2ui-grid-data w2ui-col-select" onclick="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;">'+(c!==!0?' <div> <input class="w2ui-grid-select-check" type="checkbox" tabIndex="-1" '+(i?'checked="checked"':"")+" onclick=\"var obj = w2ui['"+this.name+"']; if (!obj.multiSelect) { obj.selectNone(); } if (this.checked) obj.select('"+d.recid+"'); else obj.unselect('"+d.recid+"'); if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;\"> </div>":"")+"</td>"),this.show.expandColumn){var j="";j=d.expanded===!0?"-":"+","none"==d.expanded&&(j=""),"spinner"==d.expanded&&(j='<div class="w2ui-spinner" style="width: 16px; margin: -2px 2px;"></div>'),e+='<td id="grid_'+this.name+"_cell_"+a+"_expand"+(c?"_s":"")+'" class="w2ui-grid-data w2ui-col-expand">'+(c!==!0?' <div ondblclick="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;" onclick="w2ui[\''+this.name+"'].toggle('"+d.recid+"', event); if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;\"> "+j+" </div>":"")+"</td>"}for(var k=0;;){var l=this.columns[k];if(l.hidden){if(k++,"undefined"==typeof this.columns[k])break}else{var m=!c&&d.changes&&"undefined"!=typeof d.changes[l.field],n=this.getCellHTML(a,k,c),o="";if("string"==typeof l.render){var p=l.render.toLowerCase().split(":");-1!=["number","int","float","money","currency","percent"].indexOf(p[0])&&(o+="text-align: right;")}"object"==typeof d.style&&"string"==typeof d.style[k]&&(o+=d.style[k]+";");var q=!1;if(i&&-1!=$.inArray(k,f.columns[a])&&(q=!0),e+='<td class="w2ui-grid-data'+(q?" w2ui-selected":"")+(m?" w2ui-changed":"")+'" col="'+k+'" style="'+o+("undefined"!=typeof l.style?l.style:"")+'" '+("undefined"!=typeof l.attr?l.attr:"")+">"+n+"</td>",k++,"undefined"==typeof this.columns[k])break}}return e+='<td class="w2ui-grid-data-last"></td>',e+="</tr>"},getCellHTML:function(a,b,c){var d=this.columns[b],e=c!==!0?this.records[a]:this.summary[a],f=this.getCellValue(a,b,c),g=d.editable;if("undefined"!=typeof d.render){if("function"==typeof d.render&&(f=$.trim(d.render.call(this,e,a,b)),(f.length<4||"<div"!=f.substr(0,4).toLowerCase())&&(f="<div>"+f+"</div>")),"object"==typeof d.render&&(f="<div>"+(d.render[f]||"")+"</div>"),"string"==typeof d.render){var h=d.render.toLowerCase().split(":"),i="",j="";-1!=["number","int","float","money","currency","percent"].indexOf(h[0])&&("undefined"!=typeof h[1]&&w2utils.isInt(h[1])||(h[1]=0),h[1]>20&&(h[1]=20),h[1]<0&&(h[1]=0),-1!=["money","currency"].indexOf(h[0])&&(h[1]=w2utils.settings.currencyPrecision,i=w2utils.settings.currencyPrefix,j=w2utils.settings.currencySuffix),"percent"==h[0]&&(j="%","0"!==h[1]&&(h[1]=1)),"int"==h[0]&&(h[1]=0),f="<div>"+(""!==f?i+w2utils.formatNumber(Number(f).toFixed(h[1]))+j:"")+"</div>"),"time"==h[0]&&(("undefined"==typeof h[1]||""==h[1])&&(h[1]=w2utils.settings.time_format),f="<div>"+i+w2utils.formatTime(f,"h12"==h[1]?"hh:mi pm":"h24:min")+j+"</div>"),"date"==h[0]&&(("undefined"==typeof h[1]||""==h[1])&&(h[1]=w2utils.settings.date_display),f="<div>"+i+w2utils.formatDate(f,h[1])+j+"</div>"),"age"==h[0]&&(f="<div>"+i+w2utils.age(f)+j+"</div>"),"toggle"==h[0]&&(f="<div>"+i+(f?"Yes":"")+j+"</div>")}}else{var k="";if(g&&-1!=["checkbox","check"].indexOf(g.type)){var l=c?-(a+1):a;k="text-align: center",f='<input type="checkbox" '+(f?"checked":"")+" onclick=\" var obj = w2ui['"+this.name+"']; obj.editChange.call(obj, this, "+l+", "+b+', event); ">'}if(this.show.recordTitles){var m=String(f).replace(/"/g,"''");"undefined"!=typeof d.title&&("function"==typeof d.title&&(m=d.title.call(this,e,a,b)),"string"==typeof d.title&&(m=d.title));var f='<div title="'+w2utils.stripTags(m)+'" style="'+k+'">'+f+"</div>"}else var f='<div style="'+k+'">'+f+"</div>"}return(null==f||"undefined"==typeof f)&&(f=""),f},getCellValue:function(a,b,c){var d=this.columns[b],e=c!==!0?this.records[a]:this.summary[a],f=this.parseField(e,d.field);return e.changes&&"undefined"!=typeof e.changes[d.field]&&(f=e.changes[d.field]),(null==f||"undefined"==typeof f)&&(f=""),f},getFooterHTML:function(){return'<div> <div class="w2ui-footer-left"></div> <div class="w2ui-footer-right"></div> <div class="w2ui-footer-center"></div></div>'},status:function(a){if("undefined"!=typeof a)$("#grid_"+this.name+"_footer").find(".w2ui-footer-left").html(a);else{var b="",c=this.getSelection();if(c.length>0){b=String(c.length).replace(/(\d)(?=(\d\d\d)+(?!\d))/g,"$1,")+" "+w2utils.lang("selected");var d=c[0];"object"==typeof d&&(d=d.recid+", "+w2utils.lang("Column")+": "+d.column),1==c.length&&(b=w2utils.lang("Record ID")+": "+d+" ")}$("#grid_"+this.name+"_footer .w2ui-footer-left").html(b),1==c.length?this.toolbar.enable("w2ui-edit"):this.toolbar.disable("w2ui-edit"),c.length>=1?this.toolbar.enable("w2ui-delete"):this.toolbar.disable("w2ui-delete")}},lock:function(){var a=$(this.box).find("> div:first-child"),b=Array.prototype.slice.call(arguments,0);b.unshift(a),setTimeout(function(){w2utils.lock.apply(window,b)},10)},unlock:function(){var a=this.box;setTimeout(function(){w2utils.unlock(a)},25)},stateSave:function(a){if(!localStorage)return null;var b={columns:[],show:$.extend({},this.show),last:{search:this.last.search,multi:this.last.multi,logic:this.last.logic,caption:this.last.caption,field:this.last.field,scrollTop:this.last.scrollTop,scrollLeft:this.last.scrollLeft},sortData:[],searchData:[]};for(var c in this.columns){var d=this.columns[c];b.columns.push({field:d.field,hidden:d.hidden,size:d.size,sizeCalculated:d.sizeCalculated,sizeOriginal:d.sizeOriginal,sizeType:d.sizeType})}for(var c in this.sortData)b.sortData.push($.extend({},this.sortData[c]));for(var c in this.searchData)b.searchData.push($.extend({},this.searchData[c]));if(a!==!0){var e=this.trigger({phase:"before",type:"stateSave",target:this.name,state:b});if(e.isCancelled===!0)return void("function"==typeof callBack&&callBack({status:"error",message:"Request aborted."}));try{var f=$.parseJSON(localStorage.w2ui||"{}");f||(f={}),f.states||(f.states={}),f.states[this.name]=b,localStorage.w2ui=JSON.stringify(f)}catch(g){return delete localStorage.w2ui,null}this.trigger($.extend(e,{phase:"after"}))}return b},stateRestore:function(a){var b=this;if(!a)try{if(!localStorage)return!1;var c=$.parseJSON(localStorage.w2ui||"{}");c||(c={}),c.states||(c.states={}),a=c.states[this.name]}catch(d){return delete localStorage.w2ui,null}var e=this.trigger({phase:"before",type:"stateRestore",target:this.name,state:a});if(e.isCancelled===!0)return void("function"==typeof callBack&&callBack({status:"error",message:"Request aborted."}));if($.isPlainObject(a)){$.extend(this.show,a.show),$.extend(this.last,a.last);var f=this.last.scrollTop,g=this.last.scrollLeft;for(var h in a.columns){var c=a.columns[h],i=this.getColumn(c.field);i&&$.extend(i,c)}this.sortData.splice(0,this.sortData.length);for(var h in a.sortData)this.sortData.push(a.sortData[h]);this.searchData.splice(0,this.searchData.length);for(var h in a.searchData)this.searchData.push(a.searchData[h]);setTimeout(function(){b.sortData.length>0&&b.localSort(),b.searchData.length>0&&b.localSearch(),b.last.scrollTop=f,b.last.scrollLeft=g,b.refresh()},1)}return this.trigger($.extend(e,{phase:"after"})),!0},stateReset:function(){if(this.stateRestore(this.last.state),localStorage)try{var a=$.parseJSON(localStorage.w2ui||"{}");a.states&&a.states[this.name]&&delete a.states[this.name],localStorage.w2ui=JSON.stringify(a)}catch(b){return delete localStorage.w2ui,null}},parseField:function(a,b){var c="";try{c=a;var d=String(b).split(".");for(var e in d)c=c[d[e]]}catch(f){c=""}return c},prepareData:function(){for(var a in this.records){var b=this.records[a];for(var c in this.columns){var d=this.columns[c];if(null!=b[d.field]&&"string"==typeof d.render){if(-1!=["number","int","float","money","currency","percent"].indexOf(d.render.split(":")[0])&&"number"!=typeof b[d.field]&&(b[d.field]=parseFloat(b[d.field])),-1!=["date","age"].indexOf(d.render.split(":")[0])&&!b[d.field+"_"]){var e=b[d.field];w2utils.isInt(e)&&(e=parseInt(e)),b[d.field+"_"]=new Date(e)}if(-1!=["time"].indexOf(d.render))if(w2utils.isTime(b[d.field])){var f=w2utils.isTime(b[d.field],!0),e=new Date;e.setHours(f.hours,f.minutes,f.seconds?f.seconds:0,0),b[d.field+"_"]||(b[d.field+"_"]=e)}else{var f=b[d.field];w2utils.isInt(f)&&(f=parseInt(f));var f=null!=f?new Date(f):new Date,e=new Date;e.setHours(f.getHours(),f.getMinutes(),f.getSeconds(),0),b[d.field+"_"]||(b[d.field+"_"]=e)}}}}},nextCell:function(a,b){var c=a+1;if(this.columns.length==c)return null;if(b===!0){var d=this.columns[c].editable;if(this.columns[c].hidden||"undefined"==typeof d||d&&-1!=["checkbox","check"].indexOf(d.type))return this.nextCell(c,b)}return c},prevCell:function(a,b){var c=a-1;if(0>c)return null;if(b===!0){var d=this.columns[c].editable;if(this.columns[c].hidden||"undefined"==typeof d||d&&-1!=["checkbox","check"].indexOf(d.type))return this.prevCell(c,b)}return c},nextRow:function(a){if(a+1<this.records.length&&0==this.last.searchIds.length||this.last.searchIds.length>0&&a<this.last.searchIds[this.last.searchIds.length-1]){if(a++,this.last.searchIds.length>0)for(;;){if(-1!=$.inArray(a,this.last.searchIds)||a>this.records.length)break;a++}return a}return null},prevRow:function(a){if(a>0&&0==this.last.searchIds.length||this.last.searchIds.length>0&&a>this.last.searchIds[0]){if(a--,this.last.searchIds.length>0)for(;;){if(-1!=$.inArray(a,this.last.searchIds)||0>a)break;a--}return a}return null}},$.extend(w2grid.prototype,w2utils.event),w2obj.grid=w2grid}(),function(){var a=function(a){this.box=null,this.name=null,this.panels=[],this.tmp={},this.padding=1,this.resizer=4,this.style="",this.onShow=null,this.onHide=null,this.onResizing=null,this.onResizerClick=null,this.onRender=null,this.onRefresh=null,this.onResize=null,this.onDestroy=null,$.extend(!0,this,w2obj.layout,a)},b=["top","left","main","preview","right","bottom"];$.fn.w2layout=function(c){function d(a,b,c){var d=a.get(b);return null!==d&&"undefined"==typeof c&&(c=d.tabs),null===d||null===c?!1:($.isArray(c)&&(c={tabs:c}),$().w2destroy(a.name+"_"+b+"_tabs"),d.tabs=$().w2tabs($.extend({},c,{owner:a,name:a.name+"_"+b+"_tabs"})),d.show.tabs=!0,!0)}function e(a,b,c){var d=a.get(b);return null!==d&&"undefined"==typeof c&&(c=d.toolbar),null===d||null===c?!1:($.isArray(c)&&(c={items:c}),$().w2destroy(a.name+"_"+b+"_toolbar"),d.toolbar=$().w2toolbar($.extend({},c,{owner:a,name:a.name+"_"+b+"_toolbar"})),d.show.toolbar=!0,!0)}if("object"==typeof c||!c){if(!w2utils.checkName(c,"w2layout"))return;var f=c.panels||[],g=new a(c);$.extend(g,{handlers:[],panels:[]});for(var h=0,i=f.length;i>h;h++)g.panels[h]=$.extend(!0,{},a.prototype.panel,f[h]),($.isPlainObject(g.panels[h].tabs)||$.isArray(g.panels[h].tabs))&&d(g,f[h].type),($.isPlainObject(g.panels[h].toolbar)||$.isArray(g.panels[h].toolbar))&&e(g,f[h].type);for(var j in b)j=b[j],null===g.get(j)&&g.panels.push($.extend(!0,{},a.prototype.panel,{type:j,hidden:"main"!==j,size:50}));return $(this).length>0&&g.render($(this)[0]),w2ui[g.name]=g,g}if(w2ui[$(this).attr("name")]){var k=w2ui[$(this).attr("name")];return k[c].apply(k,Array.prototype.slice.call(arguments,1)),this}console.log("ERROR: Method "+c+" does not exist on jQuery.w2layout")},a.prototype={panel:{type:null,title:"",size:100,minSize:20,maxSize:!1,hidden:!1,resizable:!1,overflow:"auto",style:"",content:"",tabs:null,toolbar:null,width:null,height:null,show:{toolbar:!1,tabs:!1},onRefresh:null,onShow:null,onHide:null},html:function(a,b,c){return this.content(a,b,c)},content:function(a,b,c){var d=this,e=this.get(a);if("css"==a)return $("#layout_"+d.name+"_panel_css").html("<style>"+b+"</style>"),!0;if(null===e)return!1;if("undefined"==typeof b||null===b)return e.content;if(b instanceof jQuery)return console.log("ERROR: You can not pass jQuery object to w2layout.content() method"),!1;var f="#layout_"+this.name+"_panel_"+e.type,g=$(f+"> .w2ui-panel-content"),h=0;if(g.length>0&&($(f).scrollTop(0),h=$(g).position().top),""===e.content)e.content=b,this.refresh(a);else{if(e.content=b,!e.hidden&&null!==c&&""!==c&&"undefined"!=typeof c){var i=$(f+"> .w2ui-panel-content");i.after('<div class="w2ui-panel-content new-panel" style="'+i[0].style.cssText+'"></div>');var j=$(f+"> .w2ui-panel-content.new-panel");i.css("top",h),j.css("top",h),"object"==typeof b?(b.box=j[0],b.render()):j.html(b),w2utils.transition(i[0],j[0],c,function(){i.remove(),j.removeClass("new-panel"),j.css("overflow",e.overflow),d.resize(),-1!=window.navigator.userAgent.indexOf("MSIE")&&setTimeout(function(){d.resize()},100)})}this.refresh(a)}return d.resize(),-1!=window.navigator.userAgent.indexOf("MSIE")&&setTimeout(function(){d.resize()},100),!0},load:function(a,b,c,d){var e=this;return"css"==a?($.get(b,function(b,c,f){e.content(a,f.responseText),d&&d()}),!0):null!==this.get(a)?($.get(b,function(b,f,g){e.content(a,g.responseText,c),d&&d(),e.resize(),-1!=window.navigator.userAgent.indexOf("MSIE")&&setTimeout(function(){e.resize()},100)}),!0):!1},sizeTo:function(a,b){var c=this,d=c.get(a);return null===d?!1:($(c.box).find(" > div > .w2ui-panel").css({"-webkit-transition":".2s","-moz-transition":".2s","-ms-transition":".2s","-o-transition":".2s"}),setTimeout(function(){c.set(a,{size:b})},1),setTimeout(function(){$(c.box).find(" > div > .w2ui-panel").css({"-webkit-transition":"0s","-moz-transition":"0s","-ms-transition":"0s","-o-transition":"0s"}),c.resize()},500),!0)},show:function(a,b){var c=this,d=this.trigger({phase:"before",type:"show",target:a,object:this.get(a),immediate:b});if(d.isCancelled!==!0){var e=c.get(a);return null===e?!1:(e.hidden=!1,b===!0?($("#layout_"+c.name+"_panel_"+a).css({opacity:"1"}),e.resizable&&$("#layout_"+c.name+"_resizer_"+a).show(),c.trigger($.extend(d,{phase:"after"})),c.resize()):(e.resizable&&$("#layout_"+c.name+"_resizer_"+a).show(),$("#layout_"+c.name+"_panel_"+a).css({opacity:"0"}),$(c.box).find(" > div > .w2ui-panel").css({"-webkit-transition":".2s","-moz-transition":".2s","-ms-transition":".2s","-o-transition":".2s"}),setTimeout(function(){c.resize()},1),setTimeout(function(){$("#layout_"+c.name+"_panel_"+a).css({opacity:"1"})},250),setTimeout(function(){$(c.box).find(" > div > .w2ui-panel").css({"-webkit-transition":"0s","-moz-transition":"0s","-ms-transition":"0s","-o-transition":"0s"}),c.trigger($.extend(d,{phase:"after"})),c.resize()},500)),!0)}},hide:function(a,b){var c=this,d=this.trigger({phase:"before",type:"hide",target:a,object:this.get(a),immediate:b});if(d.isCancelled!==!0){var e=c.get(a);return null===e?!1:(e.hidden=!0,b===!0?($("#layout_"+c.name+"_panel_"+a).css({opacity:"0"}),$("#layout_"+c.name+"_resizer_"+a).hide(),c.trigger($.extend(d,{phase:"after"})),c.resize()):($("#layout_"+c.name+"_resizer_"+a).hide(),$(c.box).find(" > div > .w2ui-panel").css({"-webkit-transition":".2s","-moz-transition":".2s","-ms-transition":".2s","-o-transition":".2s"}),$("#layout_"+c.name+"_panel_"+a).css({opacity:"0"}),setTimeout(function(){c.resize()},1),setTimeout(function(){$(c.box).find(" > div > .w2ui-panel").css({"-webkit-transition":"0s","-moz-transition":"0s","-ms-transition":"0s","-o-transition":"0s"}),c.trigger($.extend(d,{phase:"after"})),c.resize()},500)),!0)}},toggle:function(a,b){var c=this.get(a);return null===c?!1:c.hidden?this.show(a,b):this.hide(a,b)},set:function(a,b){var c=this.get(a,!0);return null===c?!1:($.extend(this.panels[c],b),"undefined"!=typeof b.content&&this.refresh(a),this.resize(),!0)},get:function(a,b){for(var c in this.panels)if(this.panels[c].type==a)return b===!0?c:this.panels[c];return null},el:function(a){var b=$("#layout_"+this.name+"_panel_"+a+"> .w2ui-panel-content");return 1!=b.length?null:b[0]},hideToolbar:function(a){var b=this.get(a);b&&(b.show.toolbar=!1,$("#layout_"+this.name+"_panel_"+a+"> .w2ui-panel-toolbar").hide(),this.resize())},showToolbar:function(a){var b=this.get(a);b&&(b.show.toolbar=!0,$("#layout_"+this.name+"_panel_"+a+"> .w2ui-panel-toolbar").show(),this.resize())},toggleToolbar:function(a){var b=this.get(a);b&&(b.show.toolbar?this.hideToolbar(a):this.showToolbar(a))},hideTabs:function(a){var b=this.get(a);b&&(b.show.tabs=!1,$("#layout_"+this.name+"_panel_"+a+"> .w2ui-panel-tabs").hide(),this.resize())},showTabs:function(a){var b=this.get(a);b&&(b.show.tabs=!0,$("#layout_"+this.name+"_panel_"+a+"> .w2ui-panel-tabs").show(),this.resize())},toggleTabs:function(a){var b=this.get(a);b&&(b.show.tabs?this.hideTabs(a):this.showTabs(a))},render:function(a){function c(){g.tmp.events={resize:function(){w2ui[g.name].resize()},resizeStart:d,mouseMove:f,mouseUp:e},$(window).on("resize",g.tmp.events.resize)}function d(a,c){if(g.box){c||(c=window.event),window.addEventListener||window.document.attachEvent("onselectstart",function(){return!1}),$(document).off("mousemove",g.tmp.events.mouseMove).on("mousemove",g.tmp.events.mouseMove),$(document).off("mouseup",g.tmp.events.mouseUp).on("mouseup",g.tmp.events.mouseUp),g.tmp.resize={type:a,x:c.screenX,y:c.screenY,diff_x:0,diff_y:0,value:0};for(var d in b)d=b[d],g.lock(d,{opacity:0});("left"==a||"right"==a)&&(g.tmp.resize.value=parseInt($("#layout_"+g.name+"_resizer_"+a)[0].style.left)),("top"==a||"preview"==a||"bottom"==a)&&(g.tmp.resize.value=parseInt($("#layout_"+g.name+"_resizer_"+a)[0].style.top))}}function e(a){if(g.box&&(a||(a=window.event),window.addEventListener||window.document.attachEvent("onselectstart",function(){return!1}),$(document).off("mousemove",g.tmp.events.mouseMove),$(document).off("mouseup",g.tmp.events.mouseUp),"undefined"!=typeof g.tmp.resize)){for(var c in b)g.unlock(b[c]);if(0!==g.tmp.diff_x||0!==g.tmp.resize.diff_y){var d,e,f=g.get("top"),h=g.get("bottom"),i=g.get(g.tmp.resize.type),j=parseInt($(g.box).height()),k=parseInt($(g.box).width()),l=String(i.size);switch(g.tmp.resize.type){case"top":d=parseInt(i.sizeCalculated)+g.tmp.resize.diff_y,e=0;break;case"bottom":d=parseInt(i.sizeCalculated)-g.tmp.resize.diff_y,e=0;break;case"preview":d=parseInt(i.sizeCalculated)-g.tmp.resize.diff_y,e=(f&&!f.hidden?f.sizeCalculated:0)+(h&&!h.hidden?h.sizeCalculated:0);break;case"left":d=parseInt(i.sizeCalculated)+g.tmp.resize.diff_x,e=0;break;case"right":d=parseInt(i.sizeCalculated)-g.tmp.resize.diff_x,e=0}i.size="%"==l.substr(l.length-1)?Math.floor(100*d/("left"==i.type||"right"==i.type?k:j-e)*100)/100+"%":d,g.resize()}$("#layout_"+g.name+"_resizer_"+g.tmp.resize.type).removeClass("active"),delete g.tmp.resize}}function f(a){if(g.box&&(a||(a=window.event),"undefined"!=typeof g.tmp.resize)){var b=g.get(g.tmp.resize.type),c=g.tmp.resize,d=g.trigger({phase:"before",type:"resizing",target:g.name,object:b,originalEvent:a,panel:c?c.type:"all",diff_x:c?c.diff_x:0,diff_y:c?c.diff_y:0});if(d.isCancelled!==!0){var e=$("#layout_"+g.name+"_resizer_"+c.type),f=a.screenX-c.x,h=a.screenY-c.y,i=g.get("main");switch(e.hasClass("active")||e.addClass("active"),c.type){case"left":b.minSize-f>b.width&&(f=b.minSize-b.width),b.maxSize&&b.width+f>b.maxSize&&(f=b.maxSize-b.width),i.minSize+f>i.width&&(f=i.width-i.minSize);break;case"right":b.minSize+f>b.width&&(f=b.width-b.minSize),b.maxSize&&b.width-f>b.maxSize&&(f=b.width-b.maxSize),i.minSize-f>i.width&&(f=i.minSize-i.width);break;case"top":b.minSize-h>b.height&&(h=b.minSize-b.height),b.maxSize&&b.height+h>b.maxSize&&(h=b.maxSize-b.height),i.minSize+h>i.height&&(h=i.height-i.minSize);break;case"preview":case"bottom":b.minSize+h>b.height&&(h=b.height-b.minSize),b.maxSize&&b.height-h>b.maxSize&&(h=b.height-b.maxSize),i.minSize-h>i.height&&(h=i.minSize-i.height)}switch(c.diff_x=f,c.diff_y=h,c.type){case"top":case"preview":case"bottom":c.diff_x=0,e.length>0&&(e[0].style.top=c.value+c.diff_y+"px");break;case"left":case"right":c.diff_y=0,e.length>0&&(e[0].style.left=c.value+c.diff_x+"px")}g.trigger($.extend(d,{phase:"after"}))}}}var g=this,h=(new Date).getTime(),i=g.trigger({phase:"before",type:"render",target:g.name,box:a});if(i.isCancelled!==!0){if("undefined"!=typeof a&&null!==a&&($(g.box).find("#layout_"+g.name+"_panel_main").length>0&&$(g.box).removeAttr("name").removeClass("w2ui-layout").html(""),g.box=a),!g.box)return!1;$(g.box).attr("name",g.name).addClass("w2ui-layout").html("<div></div>"),$(g.box).length>0&&($(g.box)[0].style.cssText+=g.style);for(var j in b){j=b[j];var k=(g.get(j),'<div id="layout_'+g.name+"_panel_"+j+'" class="w2ui-panel"> <div class="w2ui-panel-title"></div> <div class="w2ui-panel-tabs"></div> <div class="w2ui-panel-toolbar"></div> <div class="w2ui-panel-content"></div></div><div id="layout_'+g.name+"_resizer_"+j+'" class="w2ui-resizer"></div>');$(g.box).find(" > div").append(k)}return $(g.box).find(" > div").append('<div id="layout_'+g.name+'_panel_css" style="position: absolute; top: 10000px;"></div'),g.refresh(),g.trigger($.extend(i,{phase:"after"})),setTimeout(function(){c(),g.resize()},0),(new Date).getTime()-h}},refresh:function(a){var b=this;"undefined"==typeof a&&(a=null);var c=(new Date).getTime(),d=b.trigger({phase:"before",type:"refresh",target:"undefined"!=typeof a?a:b.name,object:b.get(a)});if(d.isCancelled!==!0){if("string"==typeof a){var e=b.get(a);if(null===e)return;var f="#layout_"+b.name+"_panel_"+e.type,g="#layout_"+b.name+"_resizer_"+e.type;$(f).css({display:e.hidden?"none":"block"}),e.resizable?$(g).show():$(g).hide(),"object"==typeof e.content&&"function"==typeof e.content.render?(e.content.box=$(f+"> .w2ui-panel-content")[0],setTimeout(function(){$(f+"> .w2ui-panel-content").length>0&&($(f+"> .w2ui-panel-content").removeClass().addClass("w2ui-panel-content").css("overflow",e.overflow)[0].style.cssText+=";"+e.style),e.content.render()},1)):$(f+"> .w2ui-panel-content").length>0&&($(f+"> .w2ui-panel-content").removeClass().addClass("w2ui-panel-content").html(e.content).css("overflow",e.overflow)[0].style.cssText+=";"+e.style);var h=$(b.box).find(f+"> .w2ui-panel-tabs");e.show.tabs?0===h.find("[name="+e.tabs.name+"]").length&&null!==e.tabs?h.w2render(e.tabs):e.tabs.refresh():h.html("").removeClass("w2ui-tabs").hide(),h=$(b.box).find(f+"> .w2ui-panel-toolbar"),e.show.toolbar?0===h.find("[name="+e.toolbar.name+"]").length&&null!==e.toolbar?h.w2render(e.toolbar):e.toolbar.refresh():h.html("").removeClass("w2ui-toolbar").hide(),h=$(b.box).find(f+"> .w2ui-panel-title"),e.title?h.html(e.title).show():h.html("").hide()}else{if(0==$("#layout_"+b.name+"_panel_main").length)return void b.render();b.resize();for(var i in this.panels)b.refresh(this.panels[i].type)}return b.trigger($.extend(d,{phase:"after"})),(new Date).getTime()-c}},resize:function(){if(!this.box)return!1;var a=(new Date).getTime(),c=this.tmp.resize,d=this.trigger({phase:"before",type:"resize",target:this.name,panel:c?c.type:"all",diff_x:c?c.diff_x:0,diff_y:c?c.diff_y:0});if(d.isCancelled!==!0){this.padding<0&&(this.padding=0);
+var e=parseInt($(this.box).width()),f=parseInt($(this.box).height());$(this.box).find(" > div").css({width:e+"px",height:f+"px"});var g,h,i,j,k,l=this,m=this.get("main"),n=this.get("preview"),o=this.get("left"),p=this.get("right"),q=this.get("top"),r=this.get("bottom"),s=null!==n&&n.hidden!==!0?!0:!1,t=null!==o&&o.hidden!==!0?!0:!1,u=null!==p&&p.hidden!==!0?!0:!1,v=null!==q&&q.hidden!==!0?!0:!1,w=null!==r&&r.hidden!==!0?!0:!1;for(var x in b)if(x=b[x],"main"!==x){var c=this.get(x);if(c){var y=String(c.size||0);if("%"==y.substr(y.length-1)){var z=f;"preview"==c.type&&(z=z-(q&&!q.hidden?q.sizeCalculated:0)-(r&&!r.hidden?r.sizeCalculated:0)),c.sizeCalculated=parseInt(("left"==c.type||"right"==c.type?e:z)*parseFloat(c.size)/100)}else c.sizeCalculated=parseInt(c.size);c.sizeCalculated=Math.max(c.sizeCalculated,parseInt(c.minSize))}}null!==q&&q.hidden!==!0?(g=0,h=0,i=e,j=q.sizeCalculated,$("#layout_"+this.name+"_panel_top").css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px"}).show(),q.width=i,q.height=j,q.resizable&&(h=q.sizeCalculated-(0===this.padding?this.resizer:0),j=this.resizer>this.padding?this.resizer:this.padding,$("#layout_"+this.name+"_resizer_top").show().css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px",cursor:"ns-resize"}).off("mousedown").on("mousedown",function(a){var b=l.trigger({phase:"before",type:"resizerClick",target:"top",originalEvent:a});if(b.isCancelled!==!0)return w2ui[l.name].tmp.events.resizeStart("top",a),l.trigger($.extend(b,{phase:"after"})),!1}))):$("#layout_"+this.name+"_panel_top").hide(),null!==o&&o.hidden!==!0?(g=0,h=0+(v?q.sizeCalculated+this.padding:0),i=o.sizeCalculated,j=f-(v?q.sizeCalculated+this.padding:0)-(w?r.sizeCalculated+this.padding:0),k=$("#layout_"+this.name+"_panel_left"),-1!=window.navigator.userAgent.indexOf("MSIE")&&k.length>0&&k[0].clientHeight<k[0].scrollHeight&&(i+=17),k.css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px"}).show(),o.width=i,o.height=j,o.resizable&&(g=o.sizeCalculated-(0===this.padding?this.resizer:0),i=this.resizer>this.padding?this.resizer:this.padding,$("#layout_"+this.name+"_resizer_left").show().css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px",cursor:"ew-resize"}).off("mousedown").on("mousedown",function(a){var b=l.trigger({phase:"before",type:"resizerClick",target:"left",originalEvent:a});if(b.isCancelled!==!0)return w2ui[l.name].tmp.events.resizeStart("left",a),l.trigger($.extend(b,{phase:"after"})),!1}))):($("#layout_"+this.name+"_panel_left").hide(),$("#layout_"+this.name+"_resizer_left").hide()),null!==p&&p.hidden!==!0?(g=e-p.sizeCalculated,h=0+(v?q.sizeCalculated+this.padding:0),i=p.sizeCalculated,j=f-(v?q.sizeCalculated+this.padding:0)-(w?r.sizeCalculated+this.padding:0),$("#layout_"+this.name+"_panel_right").css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px"}).show(),p.width=i,p.height=j,p.resizable&&(g-=this.padding,i=this.resizer>this.padding?this.resizer:this.padding,$("#layout_"+this.name+"_resizer_right").show().css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px",cursor:"ew-resize"}).off("mousedown").on("mousedown",function(a){var b=l.trigger({phase:"before",type:"resizerClick",target:"right",originalEvent:a});if(b.isCancelled!==!0)return w2ui[l.name].tmp.events.resizeStart("right",a),l.trigger($.extend(b,{phase:"after"})),!1}))):$("#layout_"+this.name+"_panel_right").hide(),null!==r&&r.hidden!==!0?(g=0,h=f-r.sizeCalculated,i=e,j=r.sizeCalculated,$("#layout_"+this.name+"_panel_bottom").css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px"}).show(),r.width=i,r.height=j,r.resizable&&(h-=0===this.padding?0:this.padding,j=this.resizer>this.padding?this.resizer:this.padding,$("#layout_"+this.name+"_resizer_bottom").show().css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px",cursor:"ns-resize"}).off("mousedown").on("mousedown",function(a){var b=l.trigger({phase:"before",type:"resizerClick",target:"bottom",originalEvent:a});if(b.isCancelled!==!0)return w2ui[l.name].tmp.events.resizeStart("bottom",a),l.trigger($.extend(b,{phase:"after"})),!1}))):$("#layout_"+this.name+"_panel_bottom").hide(),g=0+(t?o.sizeCalculated+this.padding:0),h=0+(v?q.sizeCalculated+this.padding:0),i=e-(t?o.sizeCalculated+this.padding:0)-(u?p.sizeCalculated+this.padding:0),j=f-(v?q.sizeCalculated+this.padding:0)-(w?r.sizeCalculated+this.padding:0)-(s?n.sizeCalculated+this.padding:0),k=$("#layout_"+this.name+"_panel_main"),-1!=window.navigator.userAgent.indexOf("MSIE")&&k.length>0&&k[0].clientHeight<k[0].scrollHeight&&(i+=17),k.css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px"}),m.width=i,m.height=j,null!==n&&n.hidden!==!0?(g=0+(t?o.sizeCalculated+this.padding:0),h=f-(w?r.sizeCalculated+this.padding:0)-n.sizeCalculated,i=e-(t?o.sizeCalculated+this.padding:0)-(u?p.sizeCalculated+this.padding:0),j=n.sizeCalculated,k=$("#layout_"+this.name+"_panel_preview"),-1!=window.navigator.userAgent.indexOf("MSIE")&&k.length>0&&k[0].clientHeight<k[0].scrollHeight&&(i+=17),k.css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px"}).show(),n.width=i,n.height=j,n.resizable&&(h-=0===this.padding?0:this.padding,j=this.resizer>this.padding?this.resizer:this.padding,$("#layout_"+this.name+"_resizer_preview").show().css({display:"block",left:g+"px",top:h+"px",width:i+"px",height:j+"px",cursor:"ns-resize"}).off("mousedown").on("mousedown",function(a){var b=l.trigger({phase:"before",type:"resizerClick",target:"preview",originalEvent:a});if(b.isCancelled!==!0)return w2ui[l.name].tmp.events.resizeStart("preview",a),l.trigger($.extend(b,{phase:"after"})),!1}))):$("#layout_"+this.name+"_panel_preview").hide();for(var A in b){A=b[A];var B=this.get(A),C="#layout_"+this.name+"_panel_"+A+" > .w2ui-panel-",D=0;B&&(B.title&&(D+=w2utils.getSize($(C+"title").css({top:D+"px",display:"block"}),"height")),B.show.tabs&&(null!==B.tabs&&w2ui[this.name+"_"+A+"_tabs"]&&w2ui[this.name+"_"+A+"_tabs"].resize(),D+=w2utils.getSize($(C+"tabs").css({top:D+"px",display:"block"}),"height")),B.show.toolbar&&(null!==B.toolbar&&w2ui[this.name+"_"+A+"_toolbar"]&&w2ui[this.name+"_"+A+"_toolbar"].resize(),D+=w2utils.getSize($(C+"toolbar").css({top:D+"px",display:"block"}),"height"))),$(C+"content").css({display:"block"}).css({top:D+"px"})}return clearTimeout(this._resize_timer),this._resize_timer=setTimeout(function(){for(var a in w2ui)if("function"==typeof w2ui[a].resize){"undefined"==w2ui[a].panels&&w2ui[a].resize();var b=$(w2ui[a].box).parents(".w2ui-layout");b.length>0&&b.attr("name")==l.name&&w2ui[a].resize()}},100),this.trigger($.extend(d,{phase:"after"})),(new Date).getTime()-a}},destroy:function(){var a=this.trigger({phase:"before",type:"destroy",target:this.name});if(a.isCancelled!==!0)return"undefined"==typeof w2ui[this.name]?!1:($(this.box).find("#layout_"+this.name+"_panel_main").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-layout").html(""),delete w2ui[this.name],this.trigger($.extend(a,{phase:"after"})),this.tmp.events&&this.tmp.events.resize&&$(window).off("resize",this.tmp.events.resize),!0)},lock:function(a){if(-1==b.indexOf(a))return void console.log("ERROR: First parameter needs to be the a valid panel name.");var c=Array.prototype.slice.call(arguments,0);c[0]="#layout_"+this.name+"_panel_"+a,w2utils.lock.apply(window,c)},unlock:function(a){if(-1==b.indexOf(a))return void console.log("ERROR: First parameter needs to be the a valid panel name.");var c="#layout_"+this.name+"_panel_"+a;w2utils.unlock(c)}},$.extend(a.prototype,w2utils.event),w2obj.layout=a}();var w2popup={};!function(){$.fn.w2popup=function(a,b){"undefined"==typeof a&&(b={},a="open"),$.isPlainObject(a)&&(b=a,a="open"),a=a.toLowerCase(),"load"===a&&"string"==typeof b&&(b=$.extend({url:b},arguments.length>2?arguments[2]:{})),"open"===a&&null!=b.url&&(a="load"),b=b||{};var c={};return $(this).length>0&&($(this).find("div[rel=title], div[rel=body], div[rel=buttons]").length>0?($(this).find("div[rel=title]").length>0&&(c.title=$(this).find("div[rel=title]").html()),$(this).find("div[rel=body]").length>0&&(c.body=$(this).find("div[rel=body]").html(),c.style=$(this).find("div[rel=body]")[0].style.cssText),$(this).find("div[rel=buttons]").length>0&&(c.buttons=$(this).find("div[rel=buttons]").html())):(c.title="&nbsp;",c.body=$(this).html()),0!=parseInt($(this).css("width"))&&(c.width=parseInt($(this).css("width"))),0!=parseInt($(this).css("height"))&&(c.height=parseInt($(this).css("height")))),w2popup[a]($.extend({},c,b))},w2popup={defaults:{title:"",body:"",buttons:"",style:"",color:"#000",opacity:.4,speed:.3,modal:!1,maximized:!1,keyboard:!0,width:500,height:300,showClose:!0,showMax:!1,transition:null},status:"closed",handlers:[],onOpen:null,onClose:null,onMax:null,onMin:null,onToggle:null,onKeydown:null,open:function(a){function b(a){return a||(a=window.event),window.addEventListener||window.document.attachEvent("onselectstart",function(){return!1}),w2popup.status="moving",q.resizing=!0,q.x=a.screenX,q.y=a.screenY,q.pos_x=$("#w2ui-popup").position().left,q.pos_y=$("#w2ui-popup").position().top,w2popup.lock({opacity:0}),$(document).on("mousemove",q.mvMove),$(document).on("mouseup",q.mvStop),a.stopPropagation?a.stopPropagation():a.cancelBubble=!0,a.preventDefault?void a.preventDefault():!1}function c(a){1==q.resizing&&(a||(a=window.event),q.div_x=a.screenX-q.x,q.div_y=a.screenY-q.y,$("#w2ui-popup").css({"-webkit-transition":"none","-webkit-transform":"translate3d("+q.div_x+"px, "+q.div_y+"px, 0px)","-moz-transition":"none","-moz-transform":"translate("+q.div_x+"px, "+q.div_y+"px)","-ms-transition":"none","-ms-transform":"translate("+q.div_x+"px, "+q.div_y+"px)","-o-transition":"none","-o-transform":"translate("+q.div_x+"px, "+q.div_y+"px)"}))}function d(a){1==q.resizing&&(a||(a=window.event),w2popup.status="open",q.div_x=a.screenX-q.x,q.div_y=a.screenY-q.y,$("#w2ui-popup").css({left:q.pos_x+q.div_x+"px",top:q.pos_y+q.div_y+"px","-webkit-transition":"none","-webkit-transform":"translate3d(0px, 0px, 0px)","-moz-transition":"none","-moz-transform":"translate(0px, 0px)","-ms-transition":"none","-ms-transform":"translate(0px, 0px)","-o-transition":"none","-o-transform":"translate(0px, 0px)"}),q.resizing=!1,$(document).off("mousemove",q.mvMove),$(document).off("mouseup",q.mvStop),w2popup.unlock())}var e=this;if("closing"==w2popup.status)return void setTimeout(function(){e.open.call(e,a)},100);var f=$("#w2ui-popup").data("options"),a=$.extend({},this.defaults,f,{title:"",body:"",buttons:""},a,{maximized:!1});if(setTimeout(function(){$("#w2ui-popup").data("options",a)},100),0==$("#w2ui-popup").length&&(w2popup.handlers=[],w2popup.onMax=null,w2popup.onMin=null,w2popup.onToggle=null,w2popup.onOpen=null,w2popup.onClose=null,w2popup.onKeydown=null),a.onOpen&&(w2popup.onOpen=a.onOpen),a.onClose&&(w2popup.onClose=a.onClose),a.onMax&&(w2popup.onMax=a.onMax),a.onMin&&(w2popup.onMin=a.onMin),a.onToggle&&(w2popup.onToggle=a.onToggle),a.onKeydown&&(w2popup.onKeydown=a.onKeydown),void 0==window.innerHeight){var g=document.documentElement.offsetWidth,h=document.documentElement.offsetHeight;"IE7"===w2utils.engine&&(g+=21,h+=4)}else var g=window.innerWidth,h=window.innerHeight;parseInt(g)-10<parseInt(a.width)&&(a.width=parseInt(g)-10),parseInt(h)-10<parseInt(a.height)&&(a.height=parseInt(h)-10);var i=parseInt((parseInt(h)-parseInt(a.height))/2*.6),j=parseInt((parseInt(g)-parseInt(a.width))/2);if(0==$("#w2ui-popup").length){var k=this.trigger({phase:"before",type:"open",target:"popup",options:a,present:!1});if(k.isCancelled===!0)return;w2popup.status="opening",w2popup.lockScreen(a);var l="";a.showClose&&(l+='<div class="w2ui-msg-button w2ui-msg-close" onmousedown="event.stopPropagation()" onclick="w2popup.close()">Close</div>'),a.showMax&&(l+='<div class="w2ui-msg-button w2ui-msg-max" onmousedown="event.stopPropagation()" onclick="w2popup.toggle()">Max</div>');var m='<div id="w2ui-popup" class="w2ui-popup" style="opacity: 0; left: '+j+"px; top: "+i+"px; width: "+parseInt(a.width)+"px; height: "+parseInt(a.height)+'px; -webkit-transform: scale(0.8); -moz-transform: scale(0.8); -ms-transform: scale(0.8); -o-transform: scale(0.8); "> <div class="w2ui-msg-title" style="'+(""==a.title?"display: none":"")+'">'+l+a.title+'</div> <div class="w2ui-box1" style="'+(""==a.title?"top: 0px !important;":"")+(""==a.buttons?"bottom: 0px !important;":"")+'"> <div class="w2ui-msg-body'+(""!=!a.title?" w2ui-msg-no-title":"")+(""!=!a.buttons?" w2ui-msg-no-buttons":"")+'" style="'+a.style+'">'+a.body+'</div> </div> <div class="w2ui-box2" style="'+(""==a.title?"top: 0px !important;":"")+(""==a.buttons?"bottom: 0px !important;":"")+'"> <div class="w2ui-msg-body'+(""!=!a.title?" w2ui-msg-no-title":"")+(""!=!a.buttons?" w2ui-msg-no-buttons":"")+'" style="'+a.style+'"></div> </div> <div class="w2ui-msg-buttons" style="'+(""==a.buttons?"display: none":"")+'">'+a.buttons+"</div></div>";$("body").append(m),setTimeout(function(){$("#w2ui-popup .w2ui-box2").hide(),$("#w2ui-popup").css({"-webkit-transition":a.speed+"s opacity, "+a.speed+"s -webkit-transform","-webkit-transform":"scale(1)","-moz-transition":a.speed+"s opacity, "+a.speed+"s -moz-transform","-moz-transform":"scale(1)","-ms-transition":a.speed+"s opacity, "+a.speed+"s -ms-transform","-ms-transform":"scale(1)","-o-transition":a.speed+"s opacity, "+a.speed+"s -o-transform","-o-transform":"scale(1)",opacity:"1"})},1),setTimeout(function(){$("#w2ui-popup").css({"-webkit-transform":"","-moz-transform":"","-ms-transform":"","-o-transform":""}),w2popup.status="open",setTimeout(function(){e.trigger($.extend(k,{phase:"after"}))},100)},1e3*a.speed)}else{var k=this.trigger({phase:"before",type:"open",target:"popup",options:a,present:!0});if(k.isCancelled===!0)return;w2popup.status="opening",("undefined"==typeof f||f.width!=a.width||f.height!=a.height)&&w2popup.resize(a.width,a.height),"undefined"!=typeof f&&(a.prevSize=a.width+":"+a.height,a.maximized=f.maximized);var n=$("#w2ui-popup .w2ui-box2 > .w2ui-msg-body").html(a.body);n.length>0&&(n[0].style.cssText=a.style),""!=a.buttons?($("#w2ui-popup .w2ui-msg-buttons").show().html(a.buttons),$("#w2ui-popup .w2ui-msg-body").removeClass("w2ui-msg-no-buttons"),$("#w2ui-popup .w2ui-box1, #w2ui-popup .w2ui-box2").css("bottom","")):($("#w2ui-popup .w2ui-msg-buttons").hide().html(""),$("#w2ui-popup .w2ui-msg-body").addClass("w2ui-msg-no-buttons"),$("#w2ui-popup .w2ui-box1, #w2ui-popup .w2ui-box2").css("bottom","0px")),""!=a.title?($("#w2ui-popup .w2ui-msg-title").show().html((a.showClose?'<div class="w2ui-msg-button w2ui-msg-close" onmousedown="event.stopPropagation()" onclick="w2popup.close()">Close</div>':"")+(a.showMax?'<div class="w2ui-msg-button w2ui-msg-max" onmousedown="event.stopPropagation()" onclick="w2popup.toggle()">Max</div>':"")+a.title),$("#w2ui-popup .w2ui-msg-body").removeClass("w2ui-msg-no-title"),$("#w2ui-popup .w2ui-box1, #w2ui-popup .w2ui-box2").css("top","")):($("#w2ui-popup .w2ui-msg-title").hide().html(""),$("#w2ui-popup .w2ui-msg-body").addClass("w2ui-msg-no-title"),$("#w2ui-popup .w2ui-box1, #w2ui-popup .w2ui-box2").css("top","0px"));var o=$("#w2ui-popup .w2ui-box1")[0],p=$("#w2ui-popup .w2ui-box2")[0];w2utils.transition(o,p,a.transition),p.className="w2ui-box1",o.className="w2ui-box2",$(p).addClass("w2ui-current-box"),$("#w2ui-popup").data("prev-size",null),setTimeout(function(){w2popup.status="open",e.trigger($.extend(k,{phase:"after"}))},100)}a._last_w2ui_name=w2utils.keyboard.active(),w2utils.keyboard.active(null),a.keyboard&&$(document).on("keydown",this.keydown);var q={resizing:!1,mvMove:c,mvStop:d};return $("#w2ui-popup .w2ui-msg-title").on("mousedown",function(a){b(a)}),this},keydown:function(a){var b=$("#w2ui-popup").data("options");if(b.keyboard){var c=w2popup.trigger({phase:"before",type:"keydown",target:"popup",options:b,originalEvent:a});if(c.isCancelled!==!0){switch(a.keyCode){case 27:a.preventDefault(),$("#w2ui-popup .w2ui-popup-message").length>0?w2popup.message():w2popup.close()}w2popup.trigger($.extend(c,{phase:"after"}))}}},close:function(a){var b=this,a=$.extend({},$("#w2ui-popup").data("options"),a);if(0!=$("#w2ui-popup").length){var c=this.trigger({phase:"before",type:"close",target:"popup",options:a});c.isCancelled!==!0&&(w2popup.status="closing",$("#w2ui-popup").css({"-webkit-transition":a.speed+"s opacity, "+a.speed+"s -webkit-transform","-webkit-transform":"scale(0.9)","-moz-transition":a.speed+"s opacity, "+a.speed+"s -moz-transform","-moz-transform":"scale(0.9)","-ms-transition":a.speed+"s opacity, "+a.speed+"s -ms-transform","-ms-transform":"scale(0.9)","-o-transition":a.speed+"s opacity, "+a.speed+"s -o-transform","-o-transform":"scale(0.9)",opacity:"0"}),w2popup.unlockScreen(a),setTimeout(function(){$("#w2ui-popup").remove(),w2popup.status="closed",b.trigger($.extend(c,{phase:"after"}))},1e3*a.speed),w2utils.keyboard.active(a._last_w2ui_name),a.keyboard&&$(document).off("keydown",this.keydown))}},toggle:function(){var a=this,b=$("#w2ui-popup").data("options"),c=this.trigger({phase:"before",type:"toggle",target:"popup",options:b});c.isCancelled!==!0&&(b.maximized===!0?w2popup.min():w2popup.max(),setTimeout(function(){a.trigger($.extend(c,{phase:"after"}))},1e3*b.speed+50))},max:function(){var a=this,b=$("#w2ui-popup").data("options");if(b.maximized!==!0){var c=this.trigger({phase:"before",type:"max",target:"popup",options:b});c.isCancelled!==!0&&(w2popup.status="resizing",b.prevSize=$("#w2ui-popup").css("width")+":"+$("#w2ui-popup").css("height"),w2popup.resize(1e4,1e4,function(){w2popup.status="open",b.maximized=!0,a.trigger($.extend(c,{phase:"after"}))}))}},min:function(){var a=this,b=$("#w2ui-popup").data("options");if(b.maximized===!0){var c=b.prevSize.split(":"),d=this.trigger({phase:"before",type:"min",target:"popup",options:b});d.isCancelled!==!0&&(w2popup.status="resizing",w2popup.resize(c[0],c[1],function(){w2popup.status="open",b.maximized=!1,b.prevSize=null,a.trigger($.extend(d,{phase:"after"}))}))}},get:function(){return $("#w2ui-popup").data("options")},set:function(a){w2popup.open(a)},clear:function(){$("#w2ui-popup .w2ui-msg-title").html(""),$("#w2ui-popup .w2ui-msg-body").html(""),$("#w2ui-popup .w2ui-msg-buttons").html("")},reset:function(){w2popup.open(w2popup.defaults)},load:function(a){function b(b,c){if(delete a.url,$("body").append('<div id="w2ui-tmp" style="display: none">'+b+"</div>"),"undefined"!=typeof c&&$("#w2ui-tmp #"+c).length>0?$("#w2ui-tmp #"+c).w2popup(a):$("#w2ui-tmp > div").w2popup(a),$("#w2ui-tmp > style").length>0){var d=$("<div>").append($("#w2ui-tmp > style").clone()).html();0==$("#w2ui-popup #div-style").length&&$("#w2ui-popup").append('<div id="div-style" style="position: absolute; left: -100; width: 1px"></div>'),$("#w2ui-popup #div-style").html(d)}$("#w2ui-tmp").remove()}if(w2popup.status="loading","undefined"==String(a.url))return void console.log("ERROR: The url parameter is empty.");var c=String(a.url).split("#"),d=c[0],e=c[1];"undefined"==String(a)&&(a={});var f=$("#w2ui-popup").data(d);"undefined"!=typeof f&&null!=f?b(f,e):$.get(d,function(a,c,f){b(f.responseText,e),$("#w2ui-popup").data(d,f.responseText)})},message:function(a){$().w2tag(),a||(a={width:200,height:100}),parseInt(a.width)<10&&(a.width=10),parseInt(a.height)<10&&(a.height=10),"undefined"==typeof a.hideOnClick&&(a.hideOnClick=!1);var b=$("#w2ui-popup").data("options")||{};("undefined"==typeof a.width||a.width>b.width-10)&&(a.width=b.width-10),("undefined"==typeof a.height||a.height>b.height-40)&&(a.height=b.height-40);var c=$("#w2ui-popup .w2ui-msg-title"),d=parseInt($("#w2ui-popup").width()),e=$("#w2ui-popup .w2ui-popup-message").length;if(""==$.trim(a.html)){$("#w2ui-popup #w2ui-message"+(e-1)).css("z-Index",250);var a=$("#w2ui-popup #w2ui-message"+(e-1)).data("options")||{};$("#w2ui-popup #w2ui-message"+(e-1)).remove(),"function"==typeof a.onClose&&a.onClose(),1==e?w2popup.unlock():$("#w2ui-popup #w2ui-message"+(e-2)).show()}else{$("#w2ui-popup .w2ui-popup-message").hide(),$("#w2ui-popup .w2ui-box1").before('<div id="w2ui-message'+e+'" class="w2ui-popup-message" style="display: none; '+(0==c.length?"top: 0px;":"top: "+w2utils.getSize(c,"height")+"px;")+("undefined"!=typeof a.width?"width: "+a.width+"px; left: "+(d-a.width)/2+"px;":"left: 10px; right: 10px;")+("undefined"!=typeof a.height?"height: "+a.height+"px;":"bottom: 6px;")+'-webkit-transition: .3s; -moz-transition: .3s; -ms-transition: .3s; -o-transition: .3s;"'+(a.hideOnClick===!0?'onclick="w2popup.message();"':"")+"></div>"),$("#w2ui-popup #w2ui-message"+e).data("options",a);var f=$("#w2ui-popup #w2ui-message"+e).css("display");$("#w2ui-popup #w2ui-message"+e).css({"-webkit-transform":"none"==f?"translateY(-"+a.height+"px)":"translateY(0px)","-moz-transform":"none"==f?"translateY(-"+a.height+"px)":"translateY(0px)","-ms-transform":"none"==f?"translateY(-"+a.height+"px)":"translateY(0px)","-o-transform":"none"==f?"translateY(-"+a.height+"px)":"translateY(0px)"}),"none"==f&&($("#w2ui-popup #w2ui-message"+e).show().html(a.html),setTimeout(function(){$("#w2ui-popup #w2ui-message"+e).css({"-webkit-transform":"none"==f?"translateY(0px)":"translateY(-"+a.height+"px)","-moz-transform":"none"==f?"translateY(0px)":"translateY(-"+a.height+"px)","-ms-transform":"none"==f?"translateY(0px)":"translateY(-"+a.height+"px)","-o-transform":"none"==f?"translateY(0px)":"translateY(-"+a.height+"px)"})},1),setTimeout(function(){$("#w2ui-popup #w2ui-message"+e).css({"-webkit-transition":"0s","-moz-transition":"0s","-ms-transition":"0s","-o-transition":"0s","z-Index":1500}),0==e&&w2popup.lock(),"function"==typeof a.onOpen&&a.onOpen()},300))}},lock:function(){var a=Array.prototype.slice.call(arguments,0);a.unshift($("#w2ui-popup")),w2utils.lock.apply(window,a)},unlock:function(){w2utils.unlock($("#w2ui-popup"))},lockScreen:function(a){return $("#w2ui-lock").length>0?!1:("undefined"==typeof a&&(a=$("#w2ui-popup").data("options")),"undefined"==typeof a&&(a={}),a=$.extend({},w2popup.defaults,a),$("body").append('<div id="w2ui-lock" onmousewheel="if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true; if (event.preventDefault) event.preventDefault(); else return false;" style="position: '+("IE5"==w2utils.engine?"absolute":"fixed")+"; z-Index: 1199; left: 0px; top: 0px; padding: 0px; margin: 0px; background-color: "+a.color+'; width: 100%; height: 100%; opacity: 0;"></div>'),setTimeout(function(){$("#w2ui-lock").css({"-webkit-transition":a.speed+"s opacity","-moz-transition":a.speed+"s opacity","-ms-transition":a.speed+"s opacity","-o-transition":a.speed+"s opacity",opacity:a.opacity})},1),1==a.modal?($("#w2ui-lock").on("mousedown",function(){$("#w2ui-lock").css({"-webkit-transition":".1s","-moz-transition":".1s","-ms-transition":".1s","-o-transition":".1s",opacity:"0.6"})}),$("#w2ui-lock").on("mouseup",function(){setTimeout(function(){$("#w2ui-lock").css({"-webkit-transition":".1s","-moz-transition":".1s","-ms-transition":".1s","-o-transition":".1s",opacity:a.opacity})},100)})):$("#w2ui-lock").on("mouseup",function(){w2popup.close()}),!0)},unlockScreen:function(a){return 0==$("#w2ui-lock").length?!1:("undefined"==typeof a&&(a=$("#w2ui-popup").data("options")),"undefined"==typeof a&&(a={}),a=$.extend({},w2popup.defaults,a),$("#w2ui-lock").css({"-webkit-transition":a.speed+"s opacity","-moz-transition":a.speed+"s opacity","-ms-transition":a.speed+"s opacity","-o-transition":a.speed+"s opacity",opacity:0}),setTimeout(function(){$("#w2ui-lock").remove()},1e3*a.speed),!0)},resize:function(a,b,c){var d=$("#w2ui-popup").data("options");parseInt($(window).width())-10<parseInt(a)&&(a=parseInt($(window).width())-10),parseInt($(window).height())-10<parseInt(b)&&(b=parseInt($(window).height())-10);var e=(parseInt($(window).height())-parseInt(b))/2*.8,f=(parseInt($(window).width())-parseInt(a))/2;$("#w2ui-popup").css({"-webkit-transition":d.speed+"s width, "+d.speed+"s height, "+d.speed+"s left, "+d.speed+"s top","-moz-transition":d.speed+"s width, "+d.speed+"s height, "+d.speed+"s left, "+d.speed+"s top","-ms-transition":d.speed+"s width, "+d.speed+"s height, "+d.speed+"s left, "+d.speed+"s top","-o-transition":d.speed+"s width, "+d.speed+"s height, "+d.speed+"s left, "+d.speed+"s top",top:e,left:f,width:a,height:b}),setTimeout(function(){d.width=a,d.height=b,"function"==typeof c&&c()},1e3*d.speed+50)}},$.extend(w2popup,w2utils.event)}();var w2alert=function(a,b,c){null==b&&(b=w2utils.lang("Notification")),$("#w2ui-popup").length>0&&"closing"!=w2popup.status?w2popup.message({width:400,height:170,html:'<div style="position: absolute; top: 0px; left: 0px; right: 0px; bottom: 45px; overflow: auto"> <div class="w2ui-centered" style="font-size: 13px;">'+a+'</div></div><div style="position: absolute; bottom: 7px; left: 0px; right: 0px; text-align: center; padding: 5px"> <button onclick="w2popup.message();" class="w2ui-popup-btn btn">'+w2utils.lang("Ok")+"</button></div>",onClose:function(){"function"==typeof c&&c()}}):w2popup.open({width:450,height:220,showMax:!1,showClose:!1,title:b,body:'<div class="w2ui-centered" style="font-size: 13px;">'+a+"</div>",buttons:'<button onclick="w2popup.close();" class="w2ui-popup-btn btn">'+w2utils.lang("Ok")+"</button>",onClose:function(){"function"==typeof c&&c()}})},w2confirm=function(a,b,c){var d={},e={msg:"",title:w2utils.lang("Confirmation"),width:$("#w2ui-popup").length>0?400:450,height:$("#w2ui-popup").length>0?170:220,yes_text:"Yes",yes_class:"",yes_style:"",yes_callBack:null,no_text:"No",no_class:"",no_style:"",no_callBack:null,callBack:null};return 1==arguments.length&&"object"==typeof a?$.extend(d,e,a):"function"==typeof b?$.extend(d,e,{msg:a,callBack:b}):$.extend(d,e,{msg:a,title:b,callBack:c}),$("#w2ui-popup").length>0&&"closing"!=w2popup.status?(d.width>w2popup.get().width&&(d.width=w2popup.get().width),d.height>w2popup.get().height-50&&(d.height=w2popup.get().height-50),w2popup.message({width:d.width,height:d.height,html:'<div style="position: absolute; top: 0px; left: 0px; right: 0px; bottom: 40px; overflow: auto"> <div class="w2ui-centered" style="font-size: 13px;">'+d.msg+'</div></div><div style="position: absolute; bottom: 7px; left: 0px; right: 0px; text-align: center; padding: 5px"> <button id="Yes" class="w2ui-popup-btn btn '+d.yes_class+'" style="'+d.yes_style+'">'+w2utils.lang(d.yes_text)+'</button> <button id="No" class="w2ui-popup-btn btn '+d.no_class+'" style="'+d.no_style+'">'+w2utils.lang(d.no_text)+"</button></div>",onOpen:function(){$("#w2ui-popup .w2ui-popup-message .btn").on("click",function(a){w2popup.message(),"function"==typeof d.callBack&&d.callBack(a.target.id),"Yes"==a.target.id&&"function"==typeof d.yes_callBack&&d.yes_callBack(),"No"==a.target.id&&"function"==typeof d.no_callBack&&d.no_callBack()})},onKeydown:function(a){switch(a.originalEvent.keyCode){case 13:"function"==typeof d.callBack&&d.callBack("Yes"),"function"==typeof d.yes_callBack&&d.yes_callBack(),w2popup.message();break;case 27:"function"==typeof d.callBack&&d.callBack("No"),"function"==typeof d.no_callBack&&d.no_callBack(),w2popup.message()}}})):(w2utils.isInt(d.height)||(d.height=d.height+50),w2popup.open({width:d.width,height:d.height,title:d.title,modal:!0,showClose:!1,body:'<div class="w2ui-centered" style="font-size: 13px;">'+d.msg+"</div>",buttons:'<button id="Yes" class="w2ui-popup-btn btn '+d.yes_class+'" style="'+d.yes_style+'">'+w2utils.lang(d.yes_text)+'</button><button id="No" class="w2ui-popup-btn btn '+d.no_class+'" style="'+d.no_style+'">'+w2utils.lang(d.no_text)+"</button>",onOpen:function(a){a.onComplete=function(){$("#w2ui-popup .w2ui-popup-btn").on("click",function(a){w2popup.close(),"function"==typeof d.callBack&&d.callBack(a.target.id),"Yes"==a.target.id&&"function"==typeof d.yes_callBack&&d.yes_callBack(),"No"==a.target.id&&"function"==typeof d.no_callBack&&d.no_callBack()})}},onKeydown:function(a){switch(a.originalEvent.keyCode){case 13:"function"==typeof d.callBack&&d.callBack("Yes"),"function"==typeof d.yes_callBack&&d.yes_callBack(),w2popup.close();break;case 27:"function"==typeof d.callBack&&d.callBack("No"),"function"==typeof d.no_callBack&&d.no_callBack(),w2popup.close()}}})),{yes:function(a){return d.yes_callBack=a,this},no:function(a){return d.no_callBack=a,this}}};!function(){var a=function(a){this.box=null,this.name=null,this.active=null,this.tabs=[],this.routeData={},this.right="",this.style="",this.onClick=null,this.onClose=null,this.onRender=null,this.onRefresh=null,this.onResize=null,this.onDestroy=null,$.extend(this,{handlers:[]}),$.extend(!0,this,w2obj.tabs,a)};$.fn.w2tabs=function(b){if("object"!=typeof b&&b){if(w2ui[$(this).attr("name")]){var c=w2ui[$(this).attr("name")];return c[b].apply(c,Array.prototype.slice.call(arguments,1)),this}return void console.log("ERROR: Method "+b+" does not exist on jQuery.w2tabs")}if(w2utils.checkName(b,"w2tabs")){for(var d=b.tabs||[],e=new a(b),f=0;f<d.length;f++)e.tabs[f]=$.extend({},a.prototype.tab,d[f]);return 0!==$(this).length&&e.render($(this)[0]),w2ui[e.name]=e,e}},a.prototype={tab:{id:null,text:"",route:null,hidden:!1,disabled:!1,closable:!1,hint:"",onClick:null,onRefresh:null,onClose:null},add:function(a){return this.insert(null,a)},insert:function(b,c){$.isArray(c)||(c=[c]);for(var d=0;d<c.length;d++){if("undefined"==typeof c[d].id)return void console.log('ERROR: The parameter "id" is required but not supplied. (obj: '+this.name+")");if(!w2utils.checkUniqueId(c[d].id,this.tabs,"tabs",this.name))return;var e=$.extend({},a.prototype.tab,c[d]);if(null===b||"undefined"==typeof b)this.tabs.push(e);else{var f=this.get(b,!0);this.tabs=this.tabs.slice(0,f).concat([e],this.tabs.slice(f))}this.refresh(c[d].id)}},remove:function(){for(var a=0,b=0;b<arguments.length;b++){var c=this.get(arguments[b]);if(!c)return!1;a++,this.tabs.splice(this.get(c.id,!0),1),$(this.box).find("#tabs_"+this.name+"_tab_"+w2utils.escapeId(c.id)).remove()}return a},select:function(a){return this.active==a||null===this.get(a)?!1:(this.active=a,this.refresh(),!0)},set:function(a,b){var c=this.get(a,!0);return null===c?!1:($.extend(this.tabs[c],b),this.refresh(a),!0)},get:function(a,b){if(0===arguments.length){for(var c=[],d=0;d<this.tabs.length;d++)null!=this.tabs[d].id&&c.push(this.tabs[d].id);return c}for(var e=0;e<this.tabs.length;e++)if(this.tabs[e].id==a)return b===!0?e:this.tabs[e];return null},show:function(){for(var a=this,b=0,c=[],d=0;d<arguments.length;d++){var e=this.get(arguments[d]);e&&e.hidden!==!1&&(b++,e.hidden=!1,c.push(e.id))}return setTimeout(function(){for(var b in c)a.refresh(c[b])},15),b},hide:function(){for(var a=this,b=0,c=[],d=0;d<arguments.length;d++){var e=this.get(arguments[d]);e&&e.hidden!==!0&&(b++,e.hidden=!0,c.push(e.id))}return setTimeout(function(){for(var b in c)a.refresh(c[b])},15),b},enable:function(){for(var a=this,b=0,c=[],d=0;d<arguments.length;d++){var e=this.get(arguments[d]);e&&e.disabled!==!1&&(b++,e.disabled=!1,c.push(e.id))}return setTimeout(function(){for(var b in c)a.refresh(c[b])},15),b},disable:function(){for(var a=this,b=0,c=[],d=0;d<arguments.length;d++){var e=this.get(arguments[d]);e&&e.disabled!==!0&&(b++,e.disabled=!0,c.push(e.id))}return setTimeout(function(){for(var b in c)a.refresh(c[b])},15),b},refresh:function(a){var b=(new Date).getTime(),c=this.trigger({phase:"before",type:"refresh",target:"undefined"!=typeof a?a:this.name,object:this.get(a)});if(c.isCancelled!==!0){if("undefined"==typeof a)for(var d=0;d<this.tabs.length;d++)this.refresh(this.tabs[d].id);else{var e=this.get(a);if(null===e)return!1;"undefined"!=typeof e.caption&&(e.text=e.caption);var f=$(this.box).find("#tabs_"+this.name+"_tab_"+w2utils.escapeId(e.id)),g=(e.closable?'<div class="w2ui-tab-close" onclick="w2ui[\''+this.name+"'].animateClose('"+e.id+"', event);\"></div>":"")+' <div class="w2ui-tab'+(this.active===e.id?" active":"")+(e.closable?" closable":"")+'" title="'+("undefined"!=typeof e.hint?e.hint:"")+'" onclick="w2ui[\''+this.name+"'].click('"+e.id+"', event);\">"+e.text+"</div>";
+if(0===f.length){var h="";e.hidden&&(h+="display: none;"),e.disabled&&(h+="opacity: 0.2; -moz-opacity: 0.2; -webkit-opacity: 0.2; -o-opacity: 0.2; filter:alpha(opacity=20);");var i='<td id="tabs_'+this.name+"_tab_"+e.id+'" style="'+h+'" valign="middle">'+g+"</td>";this.get(a,!0)!==this.tabs.length-1&&$(this.box).find("#tabs_"+this.name+"_tab_"+w2utils.escapeId(this.tabs[parseInt(this.get(a,!0))+1].id)).length>0?$(this.box).find("#tabs_"+this.name+"_tab_"+w2utils.escapeId(this.tabs[parseInt(this.get(a,!0))+1].id)).before(i):$(this.box).find("#tabs_"+this.name+"_right").before(i)}else f.html(g),e.hidden?f.css("display","none"):f.css("display",""),f.css(e.disabled?{opacity:"0.2","-moz-opacity":"0.2","-webkit-opacity":"0.2","-o-opacity":"0.2",filter:"alpha(opacity=20)"}:{opacity:"1","-moz-opacity":"1","-webkit-opacity":"1","-o-opacity":"1",filter:"alpha(opacity=100)"})}return $("#tabs_"+this.name+"_right").html(this.right),this.trigger($.extend(c,{phase:"after"})),(new Date).getTime()-b}},render:function(a){var b=(new Date).getTime(),c=this.trigger({phase:"before",type:"render",target:this.name,box:a});if(c.isCancelled!==!0){if("undefined"!=typeof a&&null!==a&&($(this.box).find("> table #tabs_"+this.name+"_right").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-reset w2ui-tabs").html(""),this.box=a),!this.box)return!1;var d='<table cellspacing="0" cellpadding="1" width="100%"> <tr><td width="100%" id="tabs_'+this.name+'_right" align="right">'+this.right+"</td></tr></table>";return $(this.box).attr("name",this.name).addClass("w2ui-reset w2ui-tabs").html(d),$(this.box).length>0&&($(this.box)[0].style.cssText+=this.style),this.trigger($.extend(c,{phase:"after"})),this.refresh(),(new Date).getTime()-b}},resize:function(){var a=(new Date).getTime(),b=this.trigger({phase:"before",type:"resize",target:this.name});return b.isCancelled!==!0?(this.trigger($.extend(b,{phase:"after"})),(new Date).getTime()-a):void 0},destroy:function(){var a=this.trigger({phase:"before",type:"destroy",target:this.name});a.isCancelled!==!0&&($(this.box).find("> table #tabs_"+this.name+"_right").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-reset w2ui-tabs").html(""),delete w2ui[this.name],this.trigger($.extend(a,{phase:"after"})))},click:function(a,b){var c=this.get(a);if(null===c||c.disabled)return!1;var d=this.trigger({phase:"before",type:"click",target:a,tab:c,object:c,originalEvent:b});if(d.isCancelled!==!0){if($(this.box).find("#tabs_"+this.name+"_tab_"+w2utils.escapeId(this.active)+" .w2ui-tab").removeClass("active"),this.active=c.id,c.route){var e=String("/"+c.route).replace(/\/{2,}/g,"/"),f=w2utils.parseRoute(e);if(f.keys.length>0)for(var g=0;g<f.keys.length;g++)null!=this.routeData[f.keys[g].name]&&(e=e.replace(new RegExp(":"+f.keys[g].name,"g"),this.routeData[f.keys[g].name]));setTimeout(function(){window.location.hash=e},1)}this.trigger($.extend(d,{phase:"after"})),this.refresh(a)}},animateClose:function(a,b){var c=this.get(a);if(null===c||c.disabled)return!1;var d=this.trigger({phase:"before",type:"close",target:a,object:this.get(a),originalEvent:b});if(d.isCancelled!==!0){var e=this;$(this.box).find("#tabs_"+this.name+"_tab_"+w2utils.escapeId(c.id)).css({"-webkit-transition":".2s","-moz-transition":"2s","-ms-transition":".2s","-o-transition":".2s",opacity:"0"}),setTimeout(function(){var a=$(e.box).find("#tabs_"+e.name+"_tab_"+w2utils.escapeId(c.id)).width();$(e.box).find("#tabs_"+e.name+"_tab_"+w2utils.escapeId(c.id)).html('<div style="width: '+a+'px; -webkit-transition: .2s; -moz-transition: .2s; -ms-transition: .2s; -o-transition: .2s"></div>'),setTimeout(function(){$(e.box).find("#tabs_"+e.name+"_tab_"+w2utils.escapeId(c.id)).find(":first-child").css({width:"0px"})},50)},200),setTimeout(function(){e.remove(a)},450),this.trigger($.extend(d,{phase:"after"})),this.refresh()}},animateInsert:function(a,b){if(null!==this.get(a)&&$.isPlainObject(b)&&w2utils.checkUniqueId(b.id,this.tabs,"tabs",this.name)){var c=$(this.box).find("#tabs_"+this.name+"_tab_"+w2utils.escapeId(b.id));if(0===c.length){"undefined"!=typeof b.caption&&(b.text=b.caption);var d='<div id="_tmp_tabs" class="w2ui-reset w2ui-tabs" style="position: absolute; top: -1000px;"><table cellspacing="0" cellpadding="1" width="100%"><tr><td id="_tmp_simple_tab" style="" valign="middle">'+(b.closable?'<div class="w2ui-tab-close"></div>':"")+' <div class="w2ui-tab '+(this.active===b.id?"active":"")+'">'+b.text+"</div></td></tr></table></div>";$("body").append(d);var e='<div style="width: 1px; -webkit-transition: 0.2s; -moz-transition: 0.2s; -ms-transition: 0.2s; -o-transition: 0.2s;">&nbsp;</div>',f="";b.hidden&&(f+="display: none;"),b.disabled&&(f+="opacity: 0.2; -moz-opacity: 0.2; -webkit-opacity: 0.2; -o-opacity: 0.2; filter:alpha(opacity=20);");var g='<td id="tabs_'+this.name+"_tab_"+b.id+'" style="'+f+'" valign="middle">'+e+"</td>";this.get(a,!0)!==this.tabs.length&&$(this.box).find("#tabs_"+this.name+"_tab_"+w2utils.escapeId(this.tabs[parseInt(this.get(a,!0))].id)).length>0?$(this.box).find("#tabs_"+this.name+"_tab_"+w2utils.escapeId(this.tabs[parseInt(this.get(a,!0))].id)).before(g):$(this.box).find("#tabs_"+this.name+"_right").before(g);var h=this;setTimeout(function(){var a=$("#_tmp_simple_tab").width();$("#_tmp_tabs").remove(),$("#tabs_"+h.name+"_tab_"+w2utils.escapeId(b.id)+" > div").css("width",a+"px")},1),setTimeout(function(){h.insert(a,b)},200)}}}},$.extend(a.prototype,w2utils.event),w2obj.tabs=a}(),function(){var a=function(a){this.box=null,this.name=null,this.routeData={},this.items=[],this.right="",this.onClick=null,this.onRender=null,this.onRefresh=null,this.onResize=null,this.onDestroy=null,$.extend(!0,this,w2obj.toolbar,a)};$.fn.w2toolbar=function(b){if("object"==typeof b||!b){if(!w2utils.checkName(b,"w2toolbar"))return;var c=b.items||[],d=new a(b);$.extend(d,{items:[],handlers:[]});for(var e=0;e<c.length;e++)d.items[e]=$.extend({},a.prototype.item,c[e]);return 0!==$(this).length&&d.render($(this)[0]),w2ui[d.name]=d,d}if(w2ui[$(this).attr("name")]){var f=w2ui[$(this).attr("name")];return f[b].apply(f,Array.prototype.slice.call(arguments,1)),this}console.log("ERROR: Method "+b+" does not exist on jQuery.w2toolbar")},a.prototype={item:{id:null,type:"button",text:"",route:null,html:"",img:null,icon:null,count:null,hidden:!1,disabled:!1,checked:!1,arrow:!0,hint:"",group:null,items:null,overlay:{},onClick:null},add:function(a){this.insert(null,a)},insert:function(b,c){$.isArray(c)||(c=[c]);for(var d=0;d<c.length;d++){if("undefined"==typeof c[d].type)return void console.log('ERROR: The parameter "type" is required but not supplied in w2toolbar.add() method.');if(-1===$.inArray(String(c[d].type),["button","check","radio","drop","menu","break","html","spacer"]))return void console.log('ERROR: The parameter "type" should be one of the following [button, check, radio, drop, menu, break, html, spacer] in w2toolbar.add() method.');if("undefined"==typeof c[d].id)return void console.log('ERROR: The parameter "id" is required but not supplied in w2toolbar.add() method.');if(!w2utils.checkUniqueId(c[d].id,this.items,"toolbar items",this.name))return;var e=$.extend({},a.prototype.item,c[d]);if(null==b)this.items.push(e);else{var f=this.get(b,!0);this.items=this.items.slice(0,f).concat([e],this.items.slice(f))}this.refresh(e.id)}},remove:function(){for(var a=0,b=0;b<arguments.length;b++){var c=this.get(arguments[b]);if(c){a++,$(this.box).find("#tb_"+this.name+"_item_"+w2utils.escapeId(c.id)).remove();var d=this.get(c.id,!0);d&&this.items.splice(d,1)}}return a},set:function(a,b){var c=this.get(a,!0);return null===c?!1:($.extend(this.items[c],b),this.refresh(a),!0)},get:function(a,b){if(0===arguments.length){for(var c=[],d=0;d<this.items.length;d++)null!==this.items[d].id&&c.push(this.items[d].id);return c}for(var e=0;e<this.items.length;e++)if(this.items[e].id===a)return b===!0?e:this.items[e];return null},show:function(){for(var a=this,b=0,c=[],d=0;d<arguments.length;d++){var e=this.get(arguments[d]);e&&(b++,e.hidden=!1,c.push(e.id))}return setTimeout(function(){for(var b in c)a.refresh(c[b])},15),b},hide:function(){for(var a=this,b=0,c=[],d=0;d<arguments.length;d++){var e=this.get(arguments[d]);e&&(b++,e.hidden=!0,c.push(e.id))}return setTimeout(function(){for(var b in c)a.refresh(c[b])},15),b},enable:function(){for(var a=this,b=0,c=[],d=0;d<arguments.length;d++){var e=this.get(arguments[d]);e&&(b++,e.disabled=!1,c.push(e.id))}return setTimeout(function(){for(var b in c)a.refresh(c[b])},15),b},disable:function(){for(var a=this,b=0,c=[],d=0;d<arguments.length;d++){var e=this.get(arguments[d]);e&&(b++,e.disabled=!0,c.push(e.id))}return setTimeout(function(){for(var b in c)a.refresh(c[b])},15),b},check:function(){for(var a=this,b=0,c=[],d=0;d<arguments.length;d++){var e=this.get(arguments[d]);e&&(b++,e.checked=!0,c.push(e.id))}return setTimeout(function(){for(var b in c)a.refresh(c[b])},15),b},uncheck:function(){for(var a=this,b=0,c=[],d=0;d<arguments.length;d++){var e=this.get(arguments[d]);e&&(b++,e.checked=!1,c.push(e.id))}return setTimeout(function(){for(var b in c)a.refresh(c[b])},15),b},render:function(a){var b=(new Date).getTime(),c=this.trigger({phase:"before",type:"render",target:this.name,box:a});if(c.isCancelled!==!0&&(null!=a&&($(this.box).find("> table #tb_"+this.name+"_right").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-reset w2ui-toolbar").html(""),this.box=a),this.box)){for(var d='<table cellspacing="0" cellpadding="0" width="100%"><tr>',e=0;e<this.items.length;e++){var f=this.items[e];null==f.id&&(f.id="item_"+e),null!==f&&(d+="spacer"===f.type?'<td width="100%" id="tb_'+this.name+"_item_"+f.id+'" align="right"></td>':'<td id="tb_'+this.name+"_item_"+f.id+'" style="'+(f.hidden?"display: none":"")+'" class="'+(f.disabled?"disabled":"")+'" valign="middle">'+this.getItemHTML(f)+"</td>")}return d+='<td width="100%" id="tb_'+this.name+'_right" align="right">'+this.right+"</td>",d+="</tr></table>",$(this.box).attr("name",this.name).addClass("w2ui-reset w2ui-toolbar").html(d),$(this.box).length>0&&($(this.box)[0].style.cssText+=this.style),this.trigger($.extend(c,{phase:"after"})),(new Date).getTime()-b}},refresh:function(a){var b=(new Date).getTime(),c=this.trigger({phase:"before",type:"refresh",target:"undefined"!=typeof a?a:this.name,item:this.get(a)});if(c.isCancelled!==!0){if(null==a)for(var d=0;d<this.items.length;d++){var e=this.items[d];null==e.id&&(e.id="item_"+d),this.refresh(e.id)}var f=this.get(a);if(null===f)return!1;var g=$(this.box).find("#tb_"+this.name+"_item_"+w2utils.escapeId(f.id)),h=this.getItemHTML(f);return 0===g.length?(h="spacer"===f.type?'<td width="100%" id="tb_'+this.name+"_item_"+f.id+'" align="right"></td>':'<td id="tb_'+this.name+"_item_"+f.id+'" style="'+(f.hidden?"display: none":"")+'" class="'+(f.disabled?"disabled":"")+'" valign="middle">'+h+"</td>",this.get(a,!0)===this.items.length-1?$(this.box).find("#tb_"+this.name+"_right").before(h):$(this.box).find("#tb_"+this.name+"_item_"+w2utils.escapeId(this.items[parseInt(this.get(a,!0))+1].id)).before(h)):(g.html(h),f.hidden?g.css("display","none"):g.css("display",""),f.disabled?g.addClass("disabled"):g.removeClass("disabled")),this.trigger($.extend(c,{phase:"after"})),(new Date).getTime()-b}},resize:function(){var a=(new Date).getTime(),b=this.trigger({phase:"before",type:"resize",target:this.name});return b.isCancelled!==!0?(this.trigger($.extend(b,{phase:"after"})),(new Date).getTime()-a):void 0},destroy:function(){var a=this.trigger({phase:"before",type:"destroy",target:this.name});a.isCancelled!==!0&&($(this.box).find("> table #tb_"+this.name+"_right").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-reset w2ui-toolbar").html(""),$(this.box).html(""),delete w2ui[this.name],this.trigger($.extend(a,{phase:"after"})))},getItemHTML:function(a){var b="";switch("undefined"!=typeof a.caption&&(a.text=a.caption),"undefined"==typeof a.hint&&(a.hint=""),"undefined"==typeof a.text&&(a.text=""),a.type){case"menu":case"button":case"check":case"radio":case"drop":var c="<td>&nbsp;</td>";a.img&&(c='<td><div class="w2ui-tb-image w2ui-icon '+a.img+'"></div></td>'),a.icon&&(c='<td><div class="w2ui-tb-image"><span class="'+a.icon+'"></span></div></td>'),b+='<table cellpadding="0" cellspacing="0" title="'+a.hint+'" class="w2ui-button '+(a.checked?"checked":"")+'" onclick = "var el=w2ui[\''+this.name+"']; if (el) el.click('"+a.id+'\', event);" onmouseover = "'+(a.disabled?"":"$(this).addClass('over');")+'" onmouseout = "'+(a.disabled?"":"$(this).removeClass('over').removeClass('down');")+'" onmousedown = "'+(a.disabled?"":"$(this).addClass('down');")+'" onmouseup = "'+(a.disabled?"":"$(this).removeClass('down');")+'"><tr><td> <table cellpadding="1" cellspacing="0"> <tr>'+c+(""!==a.text?'<td class="w2ui-tb-caption" nowrap>'+a.text+"</td>":"")+(null!=a.count?'<td class="w2ui-tb-count" nowrap><span>'+a.count+"</span></td>":"")+("drop"!==a.type&&"menu"!==a.type||a.arrow===!1?"":'<td class="w2ui-tb-down" nowrap><div></div></td>')+" </tr></table></td></tr></table>";break;case"break":b+='<table cellpadding="0" cellspacing="0"><tr> <td><div class="w2ui-break">&nbsp;</div></td></tr></table>';break;case"html":b+='<table cellpadding="0" cellspacing="0"><tr> <td nowrap>'+a.html+"</td></tr></table>"}var d="";return"function"==typeof a.onRender&&(d=a.onRender.call(this,a.id,b)),"function"==typeof this.onRender&&(d=this.onRender(a.id,b)),""!==d&&null!=d&&(b=d),b},menuClick:function(a){var b=this;if(a.item&&!a.item.disabled){var c=this.trigger({phase:"before",type:"click",target:a.item.id+":"+a.subItem.id,item:a.item,subItem:a.subItem,originalEvent:a.originalEvent});if(c.isCancelled===!0)return;var d=a.subItem;if(d.route){var e=String("/"+d.route).replace(/\/{2,}/g,"/"),f=w2utils.parseRoute(e);if(f.keys.length>0)for(var g=0;g<f.keys.length;g++)null!=b.routeData[f.keys[g].name]&&(e=e.replace(new RegExp(":"+f.keys[g].name,"g"),this.routeData[f.keys[g].name]));setTimeout(function(){window.location.hash=e},1)}this.trigger($.extend(c,{phase:"after"}))}},click:function(a,b){var c=this,d=this.get(a);if(d&&!d.disabled){var e=this.trigger({phase:"before",type:"click",target:"undefined"!=typeof a?a:this.name,item:d,object:d,originalEvent:b});if(e.isCancelled===!0)return;var f=$("#tb_"+this.name+"_item_"+w2utils.escapeId(d.id)+" table.w2ui-button");if(f.removeClass("down"),"radio"===d.type){for(var g=0;g<this.items.length;g++){var h=this.items[g];null!=h&&h.id!==d.id&&"radio"===h.type&&h.group===d.group&&h.checked&&(h.checked=!1,this.refresh(h.id))}d.checked=!0,f.addClass("checked")}if(("drop"===d.type||"menu"===d.type)&&(d.checked?d.checked=!1:setTimeout(function(){function a(){$(document).off("click",a),d.checked=!1,f.removeClass("checked")}var b=$("#tb_"+c.name+"_item_"+w2utils.escapeId(d.id));$.isPlainObject(d.overlay)||(d.overlay={});var e=(b.width()-50)/2;e>19&&(e=19),"drop"===d.type&&b.w2overlay(d.html,$.extend({left:e,top:3},d.overlay)),"menu"===d.type&&b.w2menu(d.items,$.extend({left:e,top:3},d.overlay,{select:function(b){c.menuClick({item:d,subItem:b.item,originalEvent:b.originalEvent}),a()}})),$(document).on("click",a)},1)),("check"===d.type||"drop"===d.type||"menu"===d.type)&&(d.checked=!d.checked,d.checked?f.addClass("checked"):f.removeClass("checked")),d.route){var i=String("/"+d.route).replace(/\/{2,}/g,"/"),j=w2utils.parseRoute(i);if(j.keys.length>0)for(var k=0;k<j.keys.length;k++)i=i.replace(new RegExp(":"+j.keys[k].name,"g"),this.routeData[j.keys[k].name]);setTimeout(function(){window.location.hash=i},1)}this.trigger($.extend(e,{phase:"after"}))}}},$.extend(a.prototype,w2utils.event),w2obj.toolbar=a}(),function(){var a=function(a){this.name=null,this.box=null,this.sidebar=null,this.parent=null,this.nodes=[],this.menu=[],this.routeData={},this.selected=null,this.img=null,this.icon=null,this.style="",this.topHTML="",this.bottomHTML="",this.keyboard=!0,this.onClick=null,this.onDblClick=null,this.onContextMenu=null,this.onMenuClick=null,this.onExpand=null,this.onCollapse=null,this.onKeydown=null,this.onRender=null,this.onRefresh=null,this.onResize=null,this.onDestroy=null,$.extend(!0,this,w2obj.sidebar,a)};$.fn.w2sidebar=function(b){if("object"==typeof b||!b){if(!w2utils.checkName(b,"w2sidebar"))return;var c=b.nodes,d=new a(b);return $.extend(d,{handlers:[],nodes:[]}),"undefined"!=typeof c&&d.add(d,c),0!==$(this).length&&d.render($(this)[0]),d.sidebar=d,w2ui[d.name]=d,d}if(w2ui[$(this).attr("name")]){var e=w2ui[$(this).attr("name")];return e[b].apply(e,Array.prototype.slice.call(arguments,1)),this}console.log("ERROR: Method "+b+" does not exist on jQuery.w2sidebar")},a.prototype={node:{id:null,text:"",count:null,img:null,icon:null,nodes:[],style:"",route:null,selected:!1,expanded:!1,hidden:!1,disabled:!1,group:!1,groupShowHide:!0,plus:!1,onClick:null,onDblClick:null,onContextMenu:null,onExpand:null,onCollapse:null,parent:null,sidebar:null},add:function(a,b){return 1==arguments.length&&(b=arguments[0],a=this),"string"==typeof a&&(a=this.get(a)),this.insert(a,null,b)},insert:function(b,c,d){var e,f,g,h,i;if(2==arguments.length){if(d=arguments[1],c=arguments[0],f=this.get(c),null===f)return $.isArray(d)||(d=[d]),e=null!=d[0].caption?d[0].caption:d[0].text,console.log('ERROR: Cannot insert node "'+e+'" because cannot find node "'+c+'" to insert before.'),null;b=this.get(c).parent}"string"==typeof b&&(b=this.get(b)),$.isArray(d)||(d=[d]);for(var j in d)if(h=d[j],null!=typeof h.id)if(null===this.get(this,h.id)){if(g=$.extend({},a.prototype.node,h),g.sidebar=this,g.parent=b,i=g.nodes||[],g.nodes=[],null===c)b.nodes.push(g);else{if(f=this.get(b,c,!0),null===f)return e=null!=h.caption?h.caption:h.text,console.log('ERROR: Cannot insert node "'+e+'" because cannot find node "'+c+'" to insert before.'),null;b.nodes.splice(f,0,g)}i.length>0&&this.insert(g,null,i)}else e=null!=h.caption?h.caption:h.text,console.log("ERROR: Cannot insert node with id="+h.id+" (text: "+e+") because another node with the same id already exists.");else e=null!=h.caption?h.caption:h.text,console.log('ERROR: Cannot insert node "'+e+'" because it has no id.');return this.refresh(b.id),g},remove:function(){for(var a,b=0,c=0;c<arguments.length;c++)if(a=this.get(arguments[c]),null!==a){null!==this.selected&&this.selected===a.id&&(this.selected=null);var d=this.get(a.parent,arguments[c],!0);null!==d&&(a.parent.nodes[d].selected&&a.sidebar.unselect(a.id),a.parent.nodes.splice(d,1),b++)}return b>0&&1==arguments.length?this.refresh(a.parent.id):this.refresh(),b},set:function(a,b,c){if(2==arguments.length&&(c=b,b=a,a=this),"string"==typeof a&&(a=this.get(a)),null==a.nodes)return null;for(var d=0;d<a.nodes.length;d++){if(a.nodes[d].id===b){var e=c.nodes;return $.extend(a.nodes[d],c,{nodes:[]}),null!=e&&this.add(a.nodes[d],e),this.refresh(b),!0}var f=this.set(a.nodes[d],b,c);if(f)return!0}return!1},get:function(a,b,c){if(0===arguments.length){for(var d=[],e=this.find({}),f=0;f<e.length;f++)null!=e[f].id&&d.push(e[f].id);return d}if((1==arguments.length||2==arguments.length&&b===!0)&&(c=b,b=a,a=this),"string"==typeof a&&(a=this.get(a)),null==a.nodes)return null;for(var g=0;g<a.nodes.length;g++){if(a.nodes[g].id==b)return c===!0?g:a.nodes[g];var h=this.get(a.nodes[g],b,c);if(h||0===h)return h}return null},find:function(a,b,c){if(1==arguments.length&&(b=a,a=this),c||(c=[]),"string"==typeof a&&(a=this.get(a)),null==a.nodes)return c;for(var d=0;d<a.nodes.length;d++){var e=!0;for(var f in b)a.nodes[d][f]!=b[f]&&(e=!1);e&&c.push(a.nodes[d]),a.nodes[d].nodes.length>0&&(c=this.find(a.nodes[d],b,c))}return c},hide:function(){for(var a=0,b=0;b<arguments.length;b++){var c=this.get(arguments[b]);null!==c&&(c.hidden=!0,a++)}return 1==arguments.length?this.refresh(arguments[0]):this.refresh(),a},show:function(){for(var a=0,b=0;b<arguments.length;b++){var c=this.get(arguments[b]);null!==c&&(c.hidden=!1,a++)}return 1==arguments.length?this.refresh(arguments[0]):this.refresh(),a},disable:function(){for(var a=0,b=0;b<arguments.length;b++){var c=this.get(arguments[b]);null!==c&&(c.disabled=!0,c.selected&&this.unselect(c.id),a++)}return 1==arguments.length?this.refresh(arguments[0]):this.refresh(),a},enable:function(){for(var a=0,b=0;b<arguments.length;b++){var c=this.get(arguments[b]);null!==c&&(c.disabled=!1,a++)}return 1==arguments.length?this.refresh(arguments[0]):this.refresh(),a},select:function(a){var b=this.get(a);return b?this.selected==a&&b.selected?!1:(this.unselect(this.selected),$(this.box).find("#node_"+w2utils.escapeId(a)).addClass("w2ui-selected").find(".w2ui-icon").addClass("w2ui-icon-selected"),b.selected=!0,this.selected=a,!0):!1},unselect:function(a){var b=this.get(a);return b?(b.selected=!1,$(this.box).find("#node_"+w2utils.escapeId(a)).removeClass("w2ui-selected").find(".w2ui-icon").removeClass("w2ui-icon-selected"),this.selected==a&&(this.selected=null),!0):!1},toggle:function(a){var b=this.get(a);return null===b?!1:b.plus?(this.set(a,{plus:!1}),this.expand(a),void this.refresh(a)):0===b.nodes.length?!1:this.get(a).expanded?this.collapse(a):this.expand(a)},collapse:function(a){var b=this,c=this.get(a),d=this.trigger({phase:"before",type:"collapse",target:a,object:c});return d.isCancelled!==!0?($(this.box).find("#node_"+w2utils.escapeId(a)+"_sub").slideUp(200),$(this.box).find("#node_"+w2utils.escapeId(a)+" .w2ui-node-dots:first-child").html('<div class="w2ui-expand">+</div>'),c.expanded=!1,this.trigger($.extend(d,{phase:"after"})),setTimeout(function(){b.refresh(a)},200),!0):void 0},collapseAll:function(a){if("undefined"==typeof a&&(a=this),"string"==typeof a&&(a=this.get(a)),null==a.nodes)return!1;for(var b=0;b<a.nodes.length;b++)a.nodes[b].expanded===!0&&(a.nodes[b].expanded=!1),a.nodes[b].nodes&&a.nodes[b].nodes.length>0&&this.collapseAll(a.nodes[b]);return this.refresh(a.id),!0},expand:function(a){var b=this,c=this.get(a),d=this.trigger({phase:"before",type:"expand",target:a,object:c});return d.isCancelled!==!0?($(this.box).find("#node_"+w2utils.escapeId(a)+"_sub").slideDown(200),$(this.box).find("#node_"+w2utils.escapeId(a)+" .w2ui-node-dots:first-child").html('<div class="w2ui-expand">-</div>'),c.expanded=!0,this.trigger($.extend(d,{phase:"after"})),setTimeout(function(){b.refresh(a)},200),!0):void 0},expandAll:function(a){if("undefined"==typeof a&&(a=this),"string"==typeof a&&(a=this.get(a)),null==a.nodes)return!1;for(var b=0;b<a.nodes.length;b++)a.nodes[b].expanded===!1&&(a.nodes[b].expanded=!0),a.nodes[b].nodes&&a.nodes[b].nodes.length>0&&this.collapseAll(a.nodes[b]);this.refresh(a.id)},expandParents:function(a){var b=this.get(a);return null===b?!1:(b.parent&&(b.parent.expanded=!0,this.expandParents(b.parent.id)),this.refresh(a),!0)},click:function(a,b){var c=this,d=this.get(a);if(null!==d&&!d.disabled&&!d.group){$(c.box).find(".w2ui-node.w2ui-selected").each(function(a,b){var d=$(b).attr("id").replace("node_",""),e=c.get(d);null!=e&&(e.selected=!1),$(b).removeClass("w2ui-selected").find(".w2ui-icon").removeClass("w2ui-icon-selected")});var e=$(c.box).find("#node_"+w2utils.escapeId(a)),f=$(c.box).find("#node_"+w2utils.escapeId(c.selected));e.addClass("w2ui-selected").find(".w2ui-icon").addClass("w2ui-icon-selected"),setTimeout(function(){var g=c.trigger({phase:"before",type:"click",target:a,originalEvent:b,node:d,object:d});if(g.isCancelled===!0)return e.removeClass("w2ui-selected").find(".w2ui-icon").removeClass("w2ui-icon-selected"),void f.addClass("w2ui-selected").find(".w2ui-icon").addClass("w2ui-icon-selected");if(null!==f&&(f.selected=!1),c.get(a).selected=!0,c.selected=a,d.route){var h=String("/"+d.route).replace(/\/{2,}/g,"/"),i=w2utils.parseRoute(h);if(i.keys.length>0)for(var j=0;j<i.keys.length;j++)null!=c.routeData[i.keys[j].name]&&(h=h.replace(new RegExp(":"+i.keys[j].name,"g"),c.routeData[i.keys[j].name]));setTimeout(function(){window.location.hash=h},1)}c.trigger($.extend(g,{phase:"after"}))},1)}},keydown:function(a){function b(a,b){null===a||a.hidden||a.disabled||a.group||(g.click(a.id,b),setTimeout(function(){g.scrollIntoView()},50))}function c(a,b){for(a=b(a);null!==a&&(a.hidden||a.disabled)&&!a.group;)a=b(a);return a}function d(a,b){if(null===a)return null;var c=a.parent,e=g.get(a.id,!0),f=null;if(a.expanded&&a.nodes.length>0&&b!==!0){var h=a.nodes[0];f=h.hidden||h.disabled||h.group?d(h):h}else f=c&&e+1<c.nodes.length?c.nodes[e+1]:d(c,!0);return null!==f&&(f.hidden||f.disabled||f.group)&&(f=d(f)),f}function e(a){if(null===a)return null;var b=a.parent,c=g.get(a.id,!0),d=c>0?f(b.nodes[c-1]):b;return null!==d&&(d.hidden||d.disabled||d.group)&&(d=e(d)),d}function f(a){if(a.expanded&&a.nodes.length>0){var b=a.nodes[a.nodes.length-1];return b.hidden||b.disabled||b.group?e(b):f(b)}return a}var g=this,h=g.get(g.selected);if(h&&g.keyboard===!0){var i=g.trigger({phase:"before",type:"keydown",target:g.name,originalEvent:a});i.isCancelled!==!0&&((13==a.keyCode||32==a.keyCode)&&h.nodes.length>0&&g.toggle(g.selected),37==a.keyCode&&(h.nodes.length>0&&h.expanded?g.collapse(g.selected):(b(h.parent),h.parent.group||g.collapse(h.parent.id))),39==a.keyCode&&(h.nodes.length>0||h.plus)&&!h.expanded&&g.expand(g.selected),38==a.keyCode&&b(c(h,e)),40==a.keyCode&&b(c(h,d)),-1!=$.inArray(a.keyCode,[13,32,37,38,39,40])&&(a.preventDefault&&a.preventDefault(),a.stopPropagation&&a.stopPropagation()),g.trigger($.extend(i,{phase:"after"})))}},scrollIntoView:function(a){"undefined"==typeof a&&(a=this.selected);var b=this.get(a);if(null!==b){var c=$(this.box).find(".w2ui-sidebar-div"),d=$(this.box).find("#node_"+w2utils.escapeId(a)),e=d.offset().top-c.offset().top;e+d.height()>c.height()&&c.animate({scrollTop:c.scrollTop()+c.height()/1.3},250,"linear"),0>=e&&c.animate({scrollTop:c.scrollTop()-c.height()/1.3},250,"linear")}},dblClick:function(a,b){var c=this.get(a),d=this.trigger({phase:"before",type:"dblClick",target:a,originalEvent:b,object:c});d.isCancelled!==!0&&(this.toggle(a),this.trigger($.extend(d,{phase:"after"})))},contextMenu:function(a,b){var c=this,d=c.get(a);a!=c.selected&&c.click(a),setTimeout(function(){var e=c.trigger({phase:"before",type:"contextMenu",target:a,originalEvent:b,object:d});e.isCancelled!==!0&&(d.group||d.disabled||(c.menu.length>0&&$(c.box).find("#node_"+w2utils.escapeId(a)).w2menu(c.menu,{left:(b?b.offsetX||b.pageX:50)-25,onSelect:function(b){c.menuClick(a,parseInt(b.index),b.originalEvent)}}),c.trigger($.extend(e,{phase:"after"}))))},150)},menuClick:function(a,b,c){var d=this,e=d.trigger({phase:"before",type:"menuClick",target:a,originalEvent:c,menuIndex:b,menuItem:d.menu[b]});e.isCancelled!==!0&&d.trigger($.extend(e,{phase:"after"}))},render:function(a){var b=(new Date).getTime(),c=this.trigger({phase:"before",type:"render",target:this.name,box:a});return c.isCancelled!==!0&&("undefined"!=typeof a&&null!==a&&($(this.box).find("> div > div.w2ui-sidebar-div").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-reset w2ui-sidebar").html(""),this.box=a),this.box)?($(this.box).attr("name",this.name).addClass("w2ui-reset w2ui-sidebar").html('<div><div class="w2ui-sidebar-top"></div><div class="w2ui-sidebar-div"></div><div class="w2ui-sidebar-bottom"></div></div>'),$(this.box).find("> div").css({width:$(this.box).width()+"px",height:$(this.box).height()+"px"}),$(this.box).length>0&&($(this.box)[0].style.cssText+=this.style),""!==this.topHTML&&($(this.box).find(".w2ui-sidebar-top").html(this.topHTML),$(this.box).find(".w2ui-sidebar-div").css("top",$(this.box).find(".w2ui-sidebar-top").height()+"px")),""!==this.bottomHTML&&($(this.box).find(".w2ui-sidebar-bottom").html(this.bottomHTML),$(this.box).find(".w2ui-sidebar-div").css("bottom",$(this.box).find(".w2ui-sidebar-bottom").height()+"px")),this.trigger($.extend(c,{phase:"after"})),this.refresh(),(new Date).getTime()-b):void 0},refresh:function(a){function b(a){var b="",c=a.img;null===c&&(c=this.img);var d=a.icon;null===d&&(d=this.icon);for(var e=a.parent,f=0;e&&null!==e.parent;)e.group&&f--,e=e.parent,f++;return"undefined"!=typeof a.caption&&(a.text=a.caption),a.group?b='<div class="w2ui-node-group" id="node_'+a.id+'" onclick="w2ui[\''+h.name+"'].toggle('"+a.id+"')\" onmouseout=\"$(this).find('span:nth-child(1)').css('color', 'transparent')\" onmouseover=\"$(this).find('span:nth-child(1)').css('color', 'inherit')\">"+(a.groupShowHide?"<span>"+w2utils.lang(!a.hidden&&a.expanded?"Hide":"Show")+"</span>":"<span></span>")+" <span>"+a.text+'</span></div><div class="w2ui-node-sub" id="node_'+a.id+'_sub" style="'+a.style+";"+(!a.hidden&&a.expanded?"":"display: none;")+'"></div>':(a.selected&&!a.disabled&&(h.selected=a.id),e="",c&&(e='<div class="w2ui-node-image w2ui-icon '+c+(a.selected&&!a.disabled?" w2ui-icon-selected":"")+'"></div>'),d&&(e='<div class="w2ui-node-image"><span class="'+d+'"></span></div>'),b='<div class="w2ui-node '+(a.selected?"w2ui-selected":"")+" "+(a.disabled?"w2ui-disabled":"")+'" id="node_'+a.id+'" style="'+(a.hidden?"display: none;":"")+'" ondblclick="w2ui[\''+h.name+"'].dblClick('"+a.id+"', event);\" oncontextmenu=\"w2ui['"+h.name+"'].contextMenu('"+a.id+"', event); if (event.preventDefault) event.preventDefault();\" onClick=\"w2ui['"+h.name+"'].click('"+a.id+'\', event); "><table cellpadding="0" cellspacing="0" style="margin-left:'+18*f+"px; padding-right:"+18*f+'px"><tr><td class="w2ui-node-dots" nowrap onclick="w2ui[\''+h.name+"'].toggle('"+a.id+'\'); if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true;"> <div class="w2ui-expand">'+(a.nodes.length>0?a.expanded?"-":"+":a.plus?"+":"")+'</div></td><td class="w2ui-node-data" nowrap>'+e+(a.count||0===a.count?'<div class="w2ui-node-count">'+a.count+"</div>":"")+'<div class="w2ui-node-caption">'+a.text+'</div></td></tr></table></div><div class="w2ui-node-sub" id="node_'+a.id+'_sub" style="'+a.style+";"+(!a.hidden&&a.expanded?"":"display: none;")+'"></div>'),b}var c=(new Date).getTime(),d=this.trigger({phase:"before",type:"refresh",target:"undefined"!=typeof a?a:this.name});if(d.isCancelled!==!0){""!==this.topHTML&&($(this.box).find(".w2ui-sidebar-top").html(this.topHTML),$(this.box).find(".w2ui-sidebar-div").css("top",$(this.box).find(".w2ui-sidebar-top").height()+"px")),""!==this.bottomHTML&&($(this.box).find(".w2ui-sidebar-bottom").html(this.bottomHTML),$(this.box).find(".w2ui-sidebar-div").css("bottom",$(this.box).find(".w2ui-sidebar-bottom").height()+"px")),$(this.box).find("> div").css({width:$(this.box).width()+"px",height:$(this.box).height()+"px"});var e,f,g,h=this;if("undefined"==typeof a)e=this,g=".w2ui-sidebar-div";else{if(e=this.get(a),null===e)return;g="#node_"+w2utils.escapeId(e.id)+"_sub"}var i;if(e!==this){var j="#node_"+w2utils.escapeId(e.id);i=b(e),$(this.box).find(j).before('<div id="sidebar_'+this.name+'_tmp"></div>'),$(this.box).find(j).remove(),$(this.box).find(g).remove(),$("#sidebar_"+this.name+"_tmp").before(i),$("#sidebar_"+this.name+"_tmp").remove()}$(this.box).find(g).html("");for(var k=0;k<e.nodes.length;k++)f=e.nodes[k],i=b(f),$(this.box).find(g).append(i),0!==f.nodes.length&&this.refresh(f.id);return this.trigger($.extend(d,{phase:"after"})),(new Date).getTime()-c}},resize:function(){var a=(new Date).getTime(),b=this.trigger({phase:"before",type:"resize",target:this.name});return b.isCancelled!==!0?($(this.box).css("overflow","hidden"),$(this.box).find("> div").css({width:$(this.box).width()+"px",height:$(this.box).height()+"px"}),this.trigger($.extend(b,{phase:"after"})),(new Date).getTime()-a):void 0},destroy:function(){var a=this.trigger({phase:"before",type:"destroy",target:this.name});a.isCancelled!==!0&&($(this.box).find("> div > div.w2ui-sidebar-div").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-reset w2ui-sidebar").html(""),delete w2ui[this.name],this.trigger($.extend(a,{phase:"after"})))},lock:function(){var a=$(this.box).find("> div:first-child"),b=Array.prototype.slice.call(arguments,0);b.unshift(a),w2utils.lock.apply(window,b)},unlock:function(){w2utils.unlock(this.box)
+}},$.extend(a.prototype,w2utils.event),w2obj.sidebar=a}(),function(a){var b=function(b){this.el=null,this.helpers={},this.type=b.type||"text",this.options=a.extend(!0,{},b),this.onSearch=b.onSearch||null,this.onRequest=b.onRequest||null,this.onLoad=b.onLoad||null,this.onError=b.onError||null,this.onClick=b.onClick||null,this.onAdd=b.onAdd||null,this.onNew=b.onNew||null,this.onRemove=b.onRemove||null,this.onMouseOver=b.onMouseOver||null,this.onMouseOut=b.onMouseOut||null,this.onIconClick=b.onIconClick||null,this.tmp={},delete this.options.type,delete this.options.onSearch,delete this.options.onRequest,delete this.options.onLoad,delete this.options.onError,delete this.options.onClick,delete this.options.onMouseOver,delete this.options.onMouseOut,delete this.options.onIconClick,a.extend(!0,this,w2obj.field)};a.fn.w2field=function(c,d){if(0!=this.length)return"string"==typeof c&&"object"==typeof d&&(c=a.extend(!0,{},d,{type:c})),"string"==typeof c&&"undefined"==typeof d&&(c={type:c}),c.type=String(c.type).toLowerCase(),this.each(function(d,e){var f=a(e).data("w2field");if("undefined"==typeof f){var f=new b(c);return a.extend(f,{handlers:[]}),e&&(f.el=a(e)[0]),f.init(),a(e).data("w2field",f),f}if(f.clear(),"clear"!=c.type){var f=new b(c);return a.extend(f,{handlers:[]}),e&&(f.el=a(e)[0]),f.init(),a(e).data("w2field",f),f}});var e=b.prototype;return e[c]?e[c].apply(e,Array.prototype.slice.call(arguments,1)):void 0},b.prototype={custom:{},pallete:[["000000","444444","666666","999999","CCCCCC","EEEEEE","F3F3F3","FFFFFF"],["FF011B","FF9838","FFFD59","01FD55","00FFFE","0424F3","9B24F4","FF21F5"],["F4CCCC","FCE5CD","FFF2CC","D9EAD3","D0E0E3","CFE2F3","D9D1E9","EAD1DC"],["EA9899","F9CB9C","FEE599","B6D7A8","A2C4C9","9FC5E8","B4A7D6","D5A6BD"],["E06666","F6B26B","FED966","93C47D","76A5AF","6FA8DC","8E7CC3","C27BA0"],["CC0814","E69138","F1C232","6AA84F","45818E","3D85C6","674EA7","A54D79"],["99050C","B45F17","BF901F","37761D","124F5C","0A5394","351C75","741B47"],["660205","783F0B","7F6011","274E12","0C343D","063762","20124D","4C1030"]],addType:function(a,b){return a=String(a).toLowerCase(),this.custom[a]=b,!0},removeType:function(a){return a=String(a).toLowerCase(),this.custom[a]?(delete this.custom[a],!0):!1},init:function(){var b,c=this,d=this.options;if("function"==typeof this.custom[this.type])return void this.custom[this.type].call(this,d);if(-1==["INPUT","TEXTAREA"].indexOf(this.el.tagName))return void console.log("ERROR: w2field could only be applied to INPUT or TEXTAREA.",this.el);switch(this.type){case"text":case"int":case"float":case"money":case"currency":case"percent":case"alphanumeric":case"hex":b={min:null,max:null,step:1,placeholder:"",autoFormat:!0,currencyPrefix:w2utils.settings.currencyPrefix,currencySuffix:w2utils.settings.currencySuffix,currencyPrecision:w2utils.settings.currencyPrecision,groupSymbol:w2utils.settings.groupSymbol,arrows:!1,keyboard:!0,precision:null,silent:!0,prefix:"",suffix:""},this.options=a.extend(!0,{},b,d),d=this.options,d.numberRE=new RegExp("["+d.groupSymbol+"]","g"),d.moneyRE=new RegExp("["+d.currencyPrefix+d.currencySuffix+d.groupSymbol+"]","g"),d.percentRE=new RegExp("["+d.groupSymbol+"%]","g"),-1!=["text","alphanumeric","hex"].indexOf(this.type)&&(d.arrows=!1,d.keyboard=!1),this.addPrefix(),this.addSuffix(),a(this.el).attr("placeholder",d.placeholder);break;case"color":b={prefix:"#",suffix:'<div style="width: '+(parseInt(a(this.el).css("font-size"))||12)+'px">&nbsp;</div>',placeholder:"",arrows:!1,keyboard:!1},a.extend(d,b),this.addPrefix(),this.addSuffix(),a(this.el).attr("maxlength",6),""!=a(this.el).val()&&setTimeout(function(){a(c.el).change()},1),a(this.el).attr("placeholder",d.placeholder);break;case"date":b={format:w2utils.settings.date_format,placeholder:"",keyboard:!0,silent:!0,start:"",end:"",blocked:{},colored:{}},this.options=a.extend(!0,{},b,d),d=this.options,a(this.el).attr("placeholder",d.placeholder?d.placeholder:d.format);break;case"time":b={format:w2utils.settings.time_format,placeholder:"",keyboard:!0,silent:!0,start:"",end:""},this.options=a.extend(!0,{},b,d),d=this.options,a(this.el).attr("placeholder",d.placeholder?d.placeholder:"h12"==d.format?"hh:mi pm":"hh:mi");break;case"datetime":break;case"list":case"combo":if(b={items:[],selected:{},placeholder:"",url:null,postData:{},minLength:1,cacheMax:250,maxDropHeight:350,match:"begins",silent:!0,icon:null,iconStyle:"",onSearch:null,onRequest:null,onLoad:null,onError:null,onIconClick:null,renderDrop:null,prefix:"",suffix:"",openOnFocus:!1,markSearch:!1},d.items=this.normMenu(d.items),"list"==this.type&&(b.openOnFocus=!0,b.suffix='<div class="arrow-down" style="margin-top: '+(parseInt(a(this.el).height())-6)/2+'px;"></div>',a(this.el).addClass("w2ui-select"),!a.isPlainObject(d.selected)))for(var e in d.items){var f=d.items[e];if(f&&f.id==d.selected){d.selected=a.extend(!0,{},f);break}}d=a.extend({},b,d,{align:"both",altRows:!0}),this.options=d,a.isPlainObject(d.selected)||(d.selected={}),a(this.el).data("selected",d.selected),d.url&&this.request(0),"list"==this.type&&this.addFocus(),this.addPrefix(),this.addSuffix(),setTimeout(function(){c.refresh()},10),a(this.el).attr("placeholder",d.placeholder).attr("autocomplete","off"),"undefined"!=typeof d.selected.text&&a(this.el).val(d.selected.text);break;case"enum":b={items:[],selected:[],placeholder:"",max:0,url:null,postData:{},minLength:1,cacheMax:250,maxWidth:250,maxHeight:350,maxDropHeight:350,match:"contains",silent:!0,openOnFocus:!1,markSearch:!0,renderDrop:null,renderItem:null,style:"",onSearch:null,onRequest:null,onLoad:null,onError:null,onClick:null,onAdd:null,onNew:null,onRemove:null,onMouseOver:null,onMouseOut:null},d=a.extend({},b,d,{align:"both",suffix:"",altRows:!0}),d.items=this.normMenu(d.items),d.selected=this.normMenu(d.selected),this.options=d,a.isArray(d.selected)||(d.selected=[]),a(this.el).data("selected",d.selected),d.url&&this.request(0),this.addSuffix(),this.addMulti();break;case"file":b={selected:[],placeholder:w2utils.lang("Attach files by dragging and dropping or Click to Select"),max:0,maxSize:0,maxFileSize:0,maxWidth:250,maxHeight:350,maxDropHeight:350,silent:!0,renderItem:null,style:"",onClick:null,onAdd:null,onRemove:null,onMouseOver:null,onMouseOut:null},d=a.extend({},b,d,{align:"both",altRows:!0}),this.options=d,a.isArray(d.selected)||(d.selected=[]),a(this.el).data("selected",d.selected),this.addMulti()}this.tmp={onChange:function(a){c.change.call(c,a)},onClick:function(a){c.click.call(c,a)},onFocus:function(a){c.focus.call(c,a)},onBlur:function(a){c.blur.call(c,a)},onKeydown:function(a){c.keyDown.call(c,a)},onKeyup:function(a){c.keyUp.call(c,a)},onKeypress:function(a){c.keyPress.call(c,a)}},a(this.el).addClass("w2field").data("w2field",this).on("change",this.tmp.onChange).on("click",this.tmp.onClick).on("focus",this.tmp.onFocus).on("blur",this.tmp.onBlur).on("keydown",this.tmp.onKeydown).on("keyup",this.tmp.onKeyup).on("keypress",this.tmp.onKeypress).css({"box-sizing":"border-box","-webkit-box-sizing":"border-box","-moz-box-sizing":"border-box","-ms-box-sizing":"border-box","-o-box-sizing":"border-box"}),this.change(a.Event("change"))},clear:function(){var b=this.options;-1!=["money","currency"].indexOf(this.type)&&a(this.el).val(a(this.el).val().replace(b.moneyRE,"")),"percent"==this.type&&a(this.el).val(a(this.el).val().replace(/%/g,"")),"color"==this.type&&a(this.el).removeAttr("maxlength"),"list"==this.type&&a(this.el).removeClass("w2ui-select"),-1!=["date","time"].indexOf(this.type)&&a(this.el).attr("placeholder")==b.format&&a(this.el).attr("placeholder",""),this.type="clear";var c=a(this.el).data("tmp");if(this.tmp){"undefined"!=typeof c&&(c&&c["old-padding-left"]&&a(this.el).css("padding-left",c["old-padding-left"]),c&&c["old-padding-right"]&&a(this.el).css("padding-right",c["old-padding-right"])),a(this.el).val(this.clean(a(this.el).val())).removeClass("w2field").removeData().off("change",this.tmp.onChange).off("click",this.tmp.onClick).off("focus",this.tmp.onFocus).off("blur",this.tmp.onBlur).off("keydown",this.tmp.onKeydown).off("keyup",this.tmp.onKeyup).off("keypress",this.tmp.onKeypress);for(var d in this.helpers)a(this.helpers[d]).remove();this.helpers={}}},refresh:function(){var b=this,c=this.options,d=a(this.el).data("selected"),e=(new Date).getTime();if(-1!=["list"].indexOf(this.type)&&(a(b.el).parent().css("white-space","nowrap"),b.helpers.prefix&&b.helpers.prefix.hide(),setTimeout(function(){if(b.helpers.focus){!a.isEmptyObject(d)&&c.icon?(c.prefix='<span class="w2ui-icon '+c.icon+'"style="cursor: pointer; font-size: 14px; display: inline-block; margin-top: -1px; color: #7F98AD;'+c.iconStyle+'"></span>',b.addPrefix()):(c.prefix="",b.addPrefix());var e=b.helpers.focus.find("input");""==a(e).val()?(a(e).css("opacity",0).prev().css("opacity",0),a(b.el).val(d&&null!=d.text?d.text:""),a(b.el).attr("placeholder",a(b.el).attr("_placeholder"))):(a(e).css("opacity",1).prev().css("opacity",1),a(b.el).val(""),a(b.el).attr("_placeholder",a(b.el).attr("placeholder")).removeAttr("placeholder"),setTimeout(function(){b.helpers.prefix&&b.helpers.prefix.hide();var d="position: absolute; opacity: 0; margin: 4px 0px 0px 2px; background-position: left !important;";c.icon?(a(e).css("margin-left","17px"),a(b.helpers.focus).find(".icon-search").attr("style",d+"width: 11px !important; opacity: 1")):(a(e).css("margin-left","0px"),a(b.helpers.focus).find(".icon-search").attr("style",d+"width: 0px !important; opacity: 0"))},1))}},1)),-1!=["enum","file"].indexOf(this.type)){var f="";for(var g in d){var h=d[g],i="";i="function"==typeof c.renderItem?c.renderItem(h,g,'<div class="w2ui-list-remove" title="'+w2utils.lang("Remove")+'" index="'+g+'">&nbsp;&nbsp;</div>'):'<div class="w2ui-list-remove" title="'+w2utils.lang("Remove")+'" index="'+g+'">&nbsp;&nbsp;</div>'+("enum"==b.type?h.text:h.name+'<span class="file-size"> - '+w2utils.size(h.size)+"</span>"),f+='<li index="'+g+'" style="max-width: '+parseInt(c.maxWidth)+"px; "+(h.style?h.style:"")+'">'+i+"</li>"}var j=b.helpers.multi,k=j.find("ul");if(j.attr("style",j.attr("style")+";"+c.style),a(b.el).attr("readonly")?j.addClass("w2ui-readonly"):j.removeClass("w2ui-readonly"),j.find(".w2ui-enum-placeholder").remove(),k.find("li").not("li.nomouse").remove(),""!=f)k.prepend(f);else if("undefined"!=typeof c.placeholder){var l="padding-top: "+a(this.el).css("padding-top")+";padding-left: "+a(this.el).css("padding-left")+"; box-sizing: "+a(this.el).css("box-sizing")+"; line-height: "+a(this.el).css("line-height")+"; font-size: "+a(this.el).css("font-size")+"; font-family: "+a(this.el).css("font-family")+"; ";j.prepend('<div class="w2ui-enum-placeholder" style="'+l+'">'+c.placeholder+"</div>")}j.find("li").data("mouse","out").on("click",function(c){var e=d[a(c.target).attr("index")];if(!a(c.target).hasClass("nomouse")){c.stopPropagation();var f=b.trigger({phase:"before",type:"click",target:b.el,originalEvent:c.originalEvent,item:e});if(f.isCancelled!==!0){if(a(c.target).hasClass("w2ui-list-remove")){if(a(b.el).attr("readonly"))return;var f=b.trigger({phase:"before",type:"remove",target:b.el,originalEvent:c.originalEvent,item:e});if(f.isCancelled===!0)return;a().w2overlay(),d.splice(a(c.target).attr("index"),1),a(b.el).trigger("change"),a(c.target).parent().fadeOut("fast"),setTimeout(function(){b.refresh(),b.trigger(a.extend(f,{phase:"after"}))},300)}if("file"==b.type&&!a(c.target).hasClass("w2ui-list-remove")){var g="";/image/i.test(e.type)&&(g='<div style="padding: 3px;"> <img src="'+(e.content?"data:"+e.type+";base64,"+e.content:"")+'" style="max-width: 300px;" onload="var w = $(this).width(); var h = $(this).height(); if (w < 300 & h < 300) return; if (w >= h && w > 300) $(this).width(300); if (w < h && h > 300) $(this).height(300);" onerror="this.style.display = \'none\'" ></div>');var h='style="padding: 3px; text-align: right; color: #777;"',i='style="padding: 3px"';g+='<div style="padding: 8px;"> <table cellpadding="2"> <tr><td '+h+">Name:</td><td "+i+">"+e.name+"</td></tr> <tr><td "+h+">Size:</td><td "+i+">"+w2utils.size(e.size)+"</td></tr> <tr><td "+h+">Type:</td><td "+i+'> <span style="width: 200px; display: block-inline; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">'+e.type+"</span> </td></tr> <tr><td "+h+">Modified:</td><td "+i+">"+w2utils.date(e.modified)+"</td></tr> </table></div>",a(c.target).w2overlay(g)}b.trigger(a.extend(f,{phase:"after"}))}}}).on("mouseover",function(c){var e=c.target;if("LI"!=e.tagName&&(e=e.parentNode),!a(e).hasClass("nomouse")){if("out"==a(e).data("mouse")){var f=d[a(e).attr("index")],g=b.trigger({phase:"before",type:"mouseOver",target:b.el,originalEvent:c.originalEvent,item:f});if(g.isCancelled===!0)return;b.trigger(a.extend(g,{phase:"after"}))}a(e).data("mouse","over")}}).on("mouseout",function(c){var e=c.target;"LI"!=e.tagName&&(e=e.parentNode),a(e).hasClass("nomouse")||(a(e).data("mouse","leaving"),setTimeout(function(){if("leaving"==a(e).data("mouse")){a(e).data("mouse","out");var f=d[a(e).attr("index")],g=b.trigger({phase:"before",type:"f",target:b.el,originalEvent:c.originalEvent,item:f});if(g.isCancelled===!0)return;b.trigger(a.extend(g,{phase:"after"}))}},0))}),a(this.el).height("auto");var m=a(j).find("> div").height()+2*w2utils.getSize(j,"+height");26>m&&(m=26),m>c.maxHeight&&(m=c.maxHeight),j.length>0&&(j[0].scrollTop=1e3);var n=w2utils.getSize(a(this.el),"height")-2;n>m&&(m=n),a(j).css({height:m+"px",overflow:m==c.maxHeight?"auto":"hidden"}),m<c.maxHeight&&a(j).prop("scrollTop",0),a(this.el).css({height:m+2+"px"})}return(new Date).getTime()-e},reset:function(){var a=this.type;this.clear(),this.type=a,this.init()},clean:function(b){var c=this.options;return b=String(b).trim(),-1!=["int","float","money","currency","percent"].indexOf(this.type)&&(c.autoFormat&&-1!=["money","currency"].indexOf(this.type)&&(b=String(b).replace(c.moneyRE,"")),c.autoFormat&&"percent"==this.type&&(b=String(b).replace(c.percentRE,"")),c.autoFormat&&-1!=["int","float"].indexOf(this.type)&&(b=String(b).replace(c.numberRE,"")),parseFloat(b)==b&&(null!==c.min&&b<c.min&&(b=c.min,a(this.el).val(c.min)),null!==c.max&&b>c.max&&(b=c.max,a(this.el).val(c.max))),b=""!==b&&w2utils.isFloat(b)?Number(b):""),b},format:function(a){var b=this.options;if(b.autoFormat&&""!=a)switch(this.type){case"money":case"currency":a=w2utils.formatNumber(Number(a).toFixed(b.currencyPrecision),b.groupSymbol),""!=a&&(a=b.currencyPrefix+a+b.currencySuffix);break;case"percent":a=w2utils.formatNumber(b.precision?Number(a).toFixed(b.precision):a,b.groupSymbol),""!=a&&(a+="%");break;case"float":a=w2utils.formatNumber(b.precision?Number(a).toFixed(b.precision):a,b.groupSymbol);break;case"int":a=w2utils.formatNumber(a,b.groupSymbol)}return a},change:function(b){{var c=this;c.options}if(-1!=["int","float","money","currency","percent"].indexOf(this.type)){var d=a(this.el).val(),e=this.format(this.clean(a(this.el).val()));if(""!=d&&d!=e)return a(this.el).val(e).change(),b.stopPropagation(),b.preventDefault(),!1}if("color"==this.type){var f="#"+a(this.el).val();6!=a(this.el).val().length&&3!=a(this.el).val().length&&(f=""),a(this.el).next().find("div").css("background-color",f),a(c.el).is(":focus")&&this.updateOverlay()}},click:function(b){b.stopPropagation(),-1!=["list","combo","enum"].indexOf(this.type)&&(a(this.el).is(":focus")||this.focus(b)),-1!=["date","time","color"].indexOf(this.type)&&this.updateOverlay()},focus:function(){{var b=this;this.options}if(-1!==["color","date","time"].indexOf(b.type)){if(a(b.el).attr("readonly"))return;a("#w2ui-overlay").length>0&&a("#w2ui-overlay")[0].hide(),setTimeout(function(){b.updateOverlay()},150)}if(-1!=["list","combo","enum"].indexOf(b.type)){if(a(b.el).attr("readonly"))return;a("#w2ui-overlay").length>0&&a("#w2ui-overlay")[0].hide(),setTimeout(function(){return"list"==b.type&&a(b.el).is(":focus")?void a(b.helpers.focus).find("input").focus():(b.search(),void setTimeout(function(){b.updateOverlay()},1))},1)}"file"==b.type&&a(b.helpers.multi).css({outline:"auto 5px #7DB4F3","outline-offset":"-2px"})},blur:function(){var b=this,c=b.options,d=a(b.el).val().trim();-1!=["color","date","time","list","combo","enum"].indexOf(b.type)&&a("#w2ui-overlay").length>0&&a("#w2ui-overlay")[0].hide(),-1!=["int","float","money","currency","percent"].indexOf(b.type)&&(""===d||b.checkType(d)||(a(b.el).val("").change(),c.silent===!1&&(a(b.el).w2tag("Not a valid number"),setTimeout(function(){a(b.el).w2tag("")},3e3)))),-1!=["date","time"].indexOf(b.type)&&(w2utils.isInt(b.el.value)&&a(b.el).val(w2utils.formatDate(new Date(parseInt(b.el.value)),c.format)).change(),""===d||b.inRange(b.el.value)?("date"!=b.type||""===d||w2utils.isDate(b.el.value,c.format)||(a(b.el).val("").removeData("selected").change(),c.silent===!1&&(a(b.el).w2tag("Not a valid date"),setTimeout(function(){a(b.el).w2tag("")},3e3))),"time"!=b.type||""===d||w2utils.isTime(b.el.value)||(a(b.el).val("").removeData("selected").change(),c.silent===!1&&(a(b.el).w2tag("Not a valid time"),setTimeout(function(){a(b.el).w2tag("")},3e3)))):(a(b.el).val("").removeData("selected").change(),c.silent===!1&&(a(b.el).w2tag("Not in range"),setTimeout(function(){a(b.el).w2tag("")},3e3)))),"enum"==b.type&&a(b.helpers.multi).find("input").val("").width(20),"file"==b.type&&a(b.helpers.multi).css({outline:"none"})},keyPress:function(a){{var b=this;b.options}if(-1!=["int","float","money","currency","percent","hex","color","alphanumeric"].indexOf(b.type)){if(a.metaKey||a.ctrlKey||a.altKey||a.charCode!=a.keyCode&&a.keyCode>0)return;var c=String.fromCharCode(a.charCode);if(!b.checkType(c,!0)&&13!=a.keyCode)return a.preventDefault(),a.stopPropagation?a.stopPropagation():a.cancelBubble=!0,!1}-1!=["date","time"].indexOf(b.type)&&setTimeout(function(){b.updateOverlay()},1)},keyDown:function(b,c){var d=this,e=d.options,f=b.keyCode||c&&c.keyCode;if(-1!=["int","float","money","currency","percent"].indexOf(d.type)){if(!e.keyboard||a(d.el).attr("readonly"))return;var g=!1,h=parseFloat(a(d.el).val().replace(e.moneyRE,""))||0,i=e.step;switch((b.ctrlKey||b.metaKey)&&(i=10),f){case 38:if(b.shiftKey)break;a(d.el).val(h+i<=e.max||null===e.max?Number((h+i).toFixed(12)):e.max).change(),g=!0;break;case 40:if(b.shiftKey)break;a(d.el).val(h-i>=e.min||null===e.min?Number((h-i).toFixed(12)):e.min).change(),g=!0}g&&(b.preventDefault(),setTimeout(function(){d.el.setSelectionRange(d.el.value.length,d.el.value.length)},0))}if("date"==d.type){if(!e.keyboard||a(d.el).attr("readonly"))return;var g=!1,j=864e5,i=1;(b.ctrlKey||b.metaKey)&&(i=10);var k=w2utils.isDate(a(d.el).val(),e.format,!0);switch(k||(k=new Date,j=0),f){case 38:if(b.shiftKey)break;var l=w2utils.formatDate(k.getTime()+j,e.format);10==i&&(l=w2utils.formatDate(new Date(k.getFullYear(),k.getMonth()+1,k.getDate()),e.format)),a(d.el).val(l).change(),g=!0;break;case 40:if(b.shiftKey)break;var l=w2utils.formatDate(k.getTime()-j,e.format);10==i&&(l=w2utils.formatDate(new Date(k.getFullYear(),k.getMonth()-1,k.getDate()),e.format)),a(d.el).val(l).change(),g=!0}g&&(b.preventDefault(),setTimeout(function(){d.el.setSelectionRange(d.el.value.length,d.el.value.length),d.updateOverlay()},0))}if("time"==d.type){if(!e.keyboard||a(d.el).attr("readonly"))return;var g=!1,i=1;(b.ctrlKey||b.metaKey)&&(i=60),w2utils.isInt(d.el.value)&&a(d.el).val(w2utils.formatTime(new Date(parseInt(d.el.value)),e.format)).change();var h=a(d.el).val(),m=d.toMin(h)||d.toMin((new Date).getHours()+":"+((new Date).getMinutes()-1));switch(f){case 38:if(b.shiftKey)break;m+=i,g=!0;break;case 40:if(b.shiftKey)break;m-=i,g=!0}g&&(a(d.el).val(d.fromMin(m)).change(),b.preventDefault(),setTimeout(function(){d.el.setSelectionRange(d.el.value.length,d.el.value.length)},0))}if("color"==d.type){if(a(d.el).attr("readonly"))return;if(86==b.keyCode&&(b.ctrlKey||b.metaKey)&&(a(d.el).prop("maxlength",7),setTimeout(function(){var b=a(d).val();"#"==b.substr(0,1)&&(b=b.substr(1)),w2utils.isHex(b)||(b=""),a(d).val(b).prop("maxlength",6).change()},20)),(b.ctrlKey||b.metaKey)&&!b.shiftKey){if("undefined"==typeof d.tmp.cind1)d.tmp.cind1=-1,d.tmp.cind2=-1;else{switch(f){case 38:d.tmp.cind1--;break;case 40:d.tmp.cind1++;break;case 39:d.tmp.cind2++;break;case 37:d.tmp.cind2--}d.tmp.cind1<0&&(d.tmp.cind1=0),d.tmp.cind1>this.pallete.length-1&&(d.tmp.cind1=this.pallete.length-1),d.tmp.cind2<0&&(d.tmp.cind2=0),d.tmp.cind2>this.pallete[0].length-1&&(d.tmp.cind2=this.pallete[0].length-1)}-1!=[37,38,39,40].indexOf(f)&&(a(d.el).val(this.pallete[d.tmp.cind1][d.tmp.cind2]).change(),b.preventDefault())}}if(-1!=["list","combo","enum"].indexOf(d.type)){if(a(d.el).attr("readonly"))return;var g=!1,n=a(d.el).data("selected"),o=a(d.helpers.focus).find("input");switch("list"==d.type&&-1==[37,38,39,40].indexOf(f)&&d.refresh(),f){case 27:"list"==d.type&&(""!=a(o).val()&&a(o).val(""),b.stopPropagation());break;case 37:case 39:break;case 13:if(0==a("#w2ui-overlay").length)break;var p=e.items[e.index],q=a(d.helpers.multi).find("input");if("enum"==d.type)if(null!=p){var r=d.trigger({phase:"before",type:"add",target:d.el,originalEvent:b.originalEvent,item:p});if(r.isCancelled===!0)return;p=r.item,n.length>=e.max&&e.max>0&&n.pop(),delete p.hidden,delete d.tmp.force_open,n.push(p),a(d.el).change(),q.val("").width(20),d.refresh(),d.trigger(a.extend(r,{phase:"after"}))}else{p={id:q.val(),text:q.val()};var r=d.trigger({phase:"before",type:"new",target:d.el,originalEvent:b.originalEvent,item:p});if(r.isCancelled===!0)return;p=r.item,"function"==typeof d.onNew&&(n.length>=e.max&&e.max>0&&n.pop(),delete d.tmp.force_open,n.push(p),a(d.el).change(),q.val("").width(20),d.refresh()),d.trigger(a.extend(r,{phase:"after"}))}else p&&a(d.el).data("selected",p).val(p.text).change(),""==a(d.el).val()&&a(d.el).data("selected")&&a(d.el).removeData("selected").val("").change(),"list"==d.type&&(o.val(""),d.refresh()),d.tmp.force_hide=!0;break;case 8:case 46:if("enum"==d.type&&8==f&&""==a(d.helpers.multi).find("input").val()&&n.length>0){var p=n[n.length-1],r=d.trigger({phase:"before",type:"remove",target:d.el,originalEvent:b.originalEvent,item:p});if(r.isCancelled===!0)return;n.pop(),a(d.el).trigger("change"),d.refresh(),d.trigger(a.extend(r,{phase:"after"}))}"list"==d.type&&""==a(o).val()&&(a(d.el).data("selected",{}).change(),d.refresh());break;case 38:for(e.index=w2utils.isInt(e.index)?parseInt(e.index):0,e.index--;e.index>0&&e.items[e.index].hidden;)e.index--;if(0==e.index&&e.items[e.index].hidden)for(;e.items[e.index]&&e.items[e.index].hidden;)e.index++;g=!0;break;case 40:for(e.index=w2utils.isInt(e.index)?parseInt(e.index):-1,e.index++;e.index<e.items.length-1&&e.items[e.index].hidden;)e.index++;if(e.index==e.items.length-1&&e.items[e.index].hidden)for(;e.items[e.index]&&e.items[e.index].hidden;)e.index--;var s=d.el;-1!=["enum"].indexOf(d.type)&&(s=d.helpers.multi.find("input")),""==a(s).val()&&0==a("#w2ui-overlay").length?d.tmp.force_open=!0:g=!0}if(g)return e.index<0&&(e.index=0),e.index>=e.items.length&&(e.index=e.items.length-1),d.updateOverlay(),b.preventDefault(),void setTimeout(function(){if("enum"==d.type){var a=d.helpers.multi.find("input").get(0);a.setSelectionRange(a.value.length,a.value.length)}else if("list"==d.type){var a=d.helpers.focus.find("input").get(0);a.setSelectionRange(a.value.length,a.value.length)}else d.el.setSelectionRange(d.el.value.length,d.el.value.length)},0);if("enum"==d.type){var s=d.helpers.multi.find("input"),t=s.val();s.width(8*(t.length+2)+"px")}-1==[16,17,18,20,37,39,91].indexOf(f)&&setTimeout(function(){d.tmp.force_hide||d.request(),d.search()},1)}},keyUp:function(b){"color"==this.type&&86==b.keyCode&&(b.ctrlKey||b.metaKey)&&a(this).prop("maxlength",6)},clearCache:function(){var a=this.options;a.items=[],this.tmp.xhr_loading=!1,this.tmp.xhr_search="",this.tmp.xhr_total=-1,this.search()},request:function(b){var c=this,d=this.options,e=a(c.el).val()||"";if(d.url){if("enum"==c.type){var f=a(c.helpers.multi).find("input");e=0==f.length?"":f.val()}if("list"==c.type){var f=a(c.helpers.focus).find("input");e=0==f.length?"":f.val()}if(0!=d.minLength&&e.length<d.minLength)return d.items=[],void this.updateOverlay();"undefined"==typeof b&&(b=350),"undefined"==typeof c.tmp.xhr_search&&(c.tmp.xhr_search=""),"undefined"==typeof c.tmp.xhr_total&&(c.tmp.xhr_total=-1),d.url&&1!=a(c.el).prop("readonly")&&(0===d.items.length&&0!==c.tmp.xhr_total||c.tmp.xhr_total==d.cacheMax&&e.length>c.tmp.xhr_search.length||e.length>=c.tmp.xhr_search.length&&e.substr(0,c.tmp.xhr_search.length)!=c.tmp.xhr_search||e.length<c.tmp.xhr_search.length)&&(c.tmp.xhr_loading=!0,c.search(),clearTimeout(c.tmp.timeout),c.tmp.timeout=setTimeout(function(){var b=d.url,f={search:e,max:d.cacheMax};a.extend(f,d.postData);var g=c.trigger({phase:"before",type:"request",target:c.el,url:b,postData:f});if(g.isCancelled!==!0){b=g.url,f=g.postData,c.tmp.xhr&&c.tmp.xhr.abort();var h={type:"GET",url:b,data:f,dataType:"JSON"};"JSON"==w2utils.settings.dataType&&(h.type="POST",h.data=JSON.stringify(h.data),h.contentType="application/json"),c.tmp.xhr=a.ajax(h).done(function(b,g,h){var i=c.trigger({phase:"before",type:"load",target:c.el,search:f.search,data:b,xhr:h});if(i.isCancelled!==!0){if(b=i.data,"string"==typeof b&&(b=JSON.parse(b)),"success"!=b.status)return void console.log("ERROR: server did not return proper structure. It should return",{status:"success",items:[{id:1,text:"item"}]});b.items.length>d.cacheMax&&b.items.splice(d.cacheMax,1e5),c.tmp.xhr_loading=!1,c.tmp.xhr_search=e,c.tmp.xhr_total=b.items.length,d.items=b.items,c.tmp.emptySet=""==e&&0==b.items.length?!0:!1,c.search(),c.trigger(a.extend(i,{phase:"after"}))}}).fail(function(b,d,f){var g={status:d,error:f,rawResponseText:b.responseText},h=c.trigger({phase:"before",type:"error",target:c.el,search:e,error:g,xhr:b});if(h.isCancelled!==!0){if("abort"!=d){var i;try{i=a.parseJSON(b.responseText)}catch(j){}console.log("ERROR: Server communication failed.","\n EXPECTED:",{status:"success",items:[{id:1,text:"item"}]},"\n OR:",{status:"error",message:"error message"},"\n RECEIVED:","object"==typeof i?i:b.responseText)}c.clearCache(),c.trigger(a.extend(h,{phase:"after"}))}}),c.trigger(a.extend(g,{phase:"after"}))}},b))}},search:function(){var b=this,c=this.options,d=a(b.el).val(),e=b.el,f=[],g=a(b.el).data("selected");if("enum"==b.type){e=a(b.helpers.multi).find("input"),d=e.val();for(var h in g)g[h]&&f.push(g[h].id)}if("list"==b.type){e=a(b.helpers.focus).find("input"),d=e.val();for(var h in g)g[h]&&f.push(g[h].id)}var i=b.trigger({phase:"before",type:"search",target:e,search:d});if(i.isCancelled!==!0){if(b.tmp.xhr_loading!==!0){var j=0;for(var k in c.items){var l=c.items[k],m="",n="";-1!=["is","begins"].indexOf(c.match)&&(m="^"),-1!=["is","ends"].indexOf(c.match)&&(n="$");try{var o=new RegExp(m+d+n,"i");l.hidden=o.test(l.text)||"..."==l.text?!1:!0}catch(p){}"enum"==b.type&&-1!=a.inArray(l.id,f)&&(l.hidden=!0),l.hidden!==!0&&j++}if("combo"!=b.type)for(c.index=0;c.items[c.index]&&c.items[c.index].hidden;)c.index++;else c.index=-1;0>=j&&(c.index=-1),c.spinner=!1,b.updateOverlay(),setTimeout(function(){var b=a("#w2ui-overlay").html()||"";c.markSearch&&-1!=b.indexOf("$.fn.w2menuHandler")&&a("#w2ui-overlay").w2marker(d)},1)}else c.items.splice(0,c.cacheMax),c.spinner=!0,b.updateOverlay();b.trigger(a.extend(i,{phase:"after"}))}},updateOverlay:function(){var b=this,c=this.options;if("color"==this.type){if(a(b.el).attr("readonly"))return;0==a("#w2ui-overlay").length?a(b.el).w2overlay(b.getColorHTML()):a("#w2ui-overlay").html(b.getColorHTML()),a("#w2ui-overlay .color").on("mousedown",function(c){var d=a(c.originalEvent.target).attr("name"),e=a(c.originalEvent.target).attr("index").split(":");b.tmp.cind1=e[0],b.tmp.cind2=e[1],a(b.el).val(d).change(),a(this).html("&#149;")}).on("mouseup",function(){setTimeout(function(){a("#w2ui-overlay").length>0&&a("#w2ui-overlay").removeData("keepOpen")[0].hide()},10)})}if("date"==this.type){if(a(b.el).attr("readonly"))return;0==a("#w2ui-overlay").length&&a(b.el).w2overlay('<div class="w2ui-reset w2ui-calendar" onclick="event.stopPropagation();"></div>',{css:{"background-color":"#f5f5f5"}});var d,e,f=w2utils.isDate(a(b.el).val(),b.options.format,!0);f&&(d=f.getMonth()+1,e=f.getFullYear()),function k(c,d){a("#w2ui-overlay > div > div").html(b.getMonthHTML(c,d)),a("#w2ui-overlay .w2ui-calendar-title").on("mousedown",function(){if(a(this).next().hasClass("w2ui-calendar-jump"))a(this).next().remove();else{var c,d;a(this).after('<div class="w2ui-calendar-jump" style=""></div>'),a(this).next().hide().html(b.getYearHTML()).fadeIn(200),setTimeout(function(){a("#w2ui-overlay .w2ui-calendar-jump").find(".w2ui-jump-month, .w2ui-jump-year").on("click",function(){a(this).hasClass("w2ui-jump-month")&&(a(this).parent().find(".w2ui-jump-month").removeClass("selected"),a(this).addClass("selected"),d=a(this).attr("name")),a(this).hasClass("w2ui-jump-year")&&(a(this).parent().find(".w2ui-jump-year").removeClass("selected"),a(this).addClass("selected"),c=a(this).attr("name")),null!=c&&null!=d&&(a("#w2ui-overlay .w2ui-calendar-jump").fadeOut(100),setTimeout(function(){k(parseInt(d)+1,c)},100))}),a("#w2ui-overlay .w2ui-calendar-jump >:last-child").prop("scrollTop",2e3)},1)}}),a("#w2ui-overlay .w2ui-date").on("mousedown",function(){var c=a(this).attr("date");a(b.el).val(c).change(),a(this).css({"background-color":"#B6D5FB","border-color":"#aaa"})}).on("mouseup",function(){setTimeout(function(){a("#w2ui-overlay").length>0&&a("#w2ui-overlay").removeData("keepOpen")[0].hide()},10)}),a("#w2ui-overlay .previous").on("mousedown",function(){var a=b.options.current.split("/");a[0]=parseInt(a[0])-1,k(a[0],a[1])}),a("#w2ui-overlay .next").on("mousedown",function(){var a=b.options.current.split("/");a[0]=parseInt(a[0])+1,k(a[0],a[1])})}(d,e)}if("time"==this.type){if(a(b.el).attr("readonly"))return;0==a("#w2ui-overlay").length&&a(b.el).w2overlay('<div class="w2ui-reset w2ui-calendar-time" onclick="event.stopPropagation();"></div>',{css:{"background-color":"#fff"}});var g="h24"==this.options.format?!0:!1;a("#w2ui-overlay > div").html(b.getHourHTML()),a("#w2ui-overlay .w2ui-time").on("mousedown",function(){a(this).css({"background-color":"#B6D5FB","border-color":"#aaa"});var c=a(this).attr("hour");a(b.el).val((c>12&&!g?c-12:c)+":00"+(g?"":12>c?" am":" pm")).change()}).on("mouseup",function(){var c=a(this).attr("hour");a("#w2ui-overlay").length>0&&a("#w2ui-overlay")[0].hide(),a(b.el).w2overlay('<div class="w2ui-reset w2ui-calendar-time"></div>',{css:{"background-color":"#fff"}}),a("#w2ui-overlay > div").html(b.getMinHTML(c)),a("#w2ui-overlay .w2ui-time").on("mousedown",function(){a(this).css({"background-color":"#B6D5FB","border-color":"#aaa"});var d=a(this).attr("min");a(b.el).val((c>12&&!g?c-12:c)+":"+(10>d?0:"")+d+(g?"":12>c?" am":" pm")).change()}).on("mouseup",function(){setTimeout(function(){a("#w2ui-overlay").length>0&&a("#w2ui-overlay").removeData("keepOpen")[0].hide()},10)})})}if(-1!=["list","combo","enum"].indexOf(this.type)){var h=this.el,i=this.el;if("enum"==this.type&&(h=a(this.helpers.multi),i=a(h).find("input")),"list"==this.type&&(i=a(this.helpers.focus).find("input")),a(i).is(":focus")){if(c.openOnFocus===!1&&""==a(i).val()&&b.tmp.force_open!==!0)return void a().w2overlay();if(b.tmp.force_hide)return a().w2overlay(),void setTimeout(function(){delete b.tmp.force_hide},1);""!=a(i).val()&&delete b.tmp.force_open,0==a("#w2ui-overlay").length&&(c.index=0);var j=w2utils.lang("No matches");null!=c.url&&a(i).val().length<c.minLength&&b.tmp.emptySet!==!0&&(j=c.minLength+" "+w2utils.lang("letters or more...")),null!=c.url&&""==a(i).val()&&b.tmp.emptySet!==!0&&(j=w2utils.lang("Type to search....")),a(h).w2menu("refresh",a.extend(!0,{},c,{search:!1,render:c.renderDrop,maxHeight:c.maxDropHeight,msgNoItems:j,onSelect:function(d){if("enum"==b.type){var e=a(b.el).data("selected");if(d.item){var f=b.trigger({phase:"before",type:"add",target:b.el,originalEvent:d.originalEvent,item:d.item});
+if(f.isCancelled===!0)return;e.length>=c.max&&c.max>0&&e.pop(),delete d.item.hidden,e.push(d.item),a(b.el).data("selected",e).change(),a(b.helpers.multi).find("input").val("").width(20),b.refresh(),a("#w2ui-overlay").length>0&&a("#w2ui-overlay")[0].hide(),b.trigger(a.extend(f,{phase:"after"}))}}else a(b.el).data("selected",d.item).val(d.item.text).change(),b.helpers.focus&&(b.helpers.focus.find("input").val(""),b.refresh())}}))}}},inRange:function(b){var c=!1;if("date"==this.type){var d=w2utils.isDate(b,this.options.format,!0);if(d){if(this.options.start||this.options.end){var e="string"==typeof this.options.start?this.options.start:a(this.options.start).val(),f="string"==typeof this.options.end?this.options.end:a(this.options.end).val(),g=w2utils.isDate(e,this.options.format,!0),h=w2utils.isDate(f,this.options.format,!0),i=new Date(d);g||(g=i),h||(h=i),i>=g&&h>=i&&(c=!0)}else c=!0;this.options.blocked&&-1!=a.inArray(b,this.options.blocked)&&(c=!1)}}if("time"==this.type)if(this.options.start||this.options.end){var j=this.toMin(b),k=this.toMin(this.options.start),l=this.toMin(this.options.end);k||(k=j),l||(l=j),j>=k&&l>=j&&(c=!0)}else c=!0;return c},checkType:function(a,b){var c=this;switch(c.type){case"int":return b&&-1!=["-"].indexOf(a)?!0:w2utils.isInt(a.replace(c.options.numberRE,""));case"percent":a=a.replace(/%/g,"");case"float":return b&&-1!=["-","."].indexOf(a)?!0:w2utils.isFloat(a.replace(c.options.numberRE,""));case"money":case"currency":return b&&-1!=["-",".",c.options.groupSymbol,c.options.currencyPrefix,c.options.currencySuffix].indexOf(a)?!0:w2utils.isFloat(a.replace(c.options.moneyRE,""));case"hex":case"color":return w2utils.isHex(a);case"alphanumeric":return w2utils.isAlphaNumeric(a)}return!0},addPrefix:function(){var b=this;setTimeout(function(){if("clear"!==b.type){var c,d=a(b.el).data("tmp")||{};d["old-padding-left"]&&a(b.el).css("padding-left",d["old-padding-left"]),d["old-padding-left"]=a(b.el).css("padding-left"),a(b.el).data("tmp",d),b.helpers.prefix&&a(b.helpers.prefix).remove(),""!==b.options.prefix&&(a(b.el).before('<div class="w2ui-field-helper">'+b.options.prefix+"</div>"),c=a(b.el).prev(),c.css({color:a(b.el).css("color"),"font-family":a(b.el).css("font-family"),"font-size":a(b.el).css("font-size"),"padding-top":a(b.el).css("padding-top"),"padding-bottom":a(b.el).css("padding-bottom"),"padding-left":a(b.el).css("padding-left"),"padding-right":0,"margin-top":parseInt(a(b.el).css("margin-top"),10)+2+"px","margin-bottom":parseInt(a(b.el).css("margin-bottom"),10)+1+"px","margin-left":a(b.el).css("margin-left"),"margin-right":0}).on("click",function(){if(b.options.icon&&"function"==typeof b.onIconClick){var c=b.trigger({phase:"before",type:"iconClick",target:b.el,el:a(this).find("span.w2ui-icon")[0]});if(c.isCancelled===!0)return;b.trigger(a.extend(c,{phase:"after"}))}else"list"==b.type?a(b.helpers.focus).find("input").focus():a(b.el).focus()}),a(b.el).css("padding-left",c.width()+parseInt(a(b.el).css("padding-left"),10)+"px"),b.helpers.prefix=c)}},1)},addSuffix:function(){var b,c,d=this;setTimeout(function(){if("clear"!==d.type){var e=a(d.el).data("tmp")||{};if(e["old-padding-right"]&&a(d.el).css("padding-right",e["old-padding-right"]),e["old-padding-right"]=a(d.el).css("padding-right"),a(d.el).data("tmp",e),c=parseInt(a(d.el).css("padding-right"),10),d.options.arrows){d.helpers.arrows&&a(d.helpers.arrows).remove(),a(d.el).after('<div class="w2ui-field-helper" style="border: 1px solid transparent">&nbsp; <div class="w2ui-field-up" type="up"> <div class="arrow-up" type="up"></div> </div> <div class="w2ui-field-down" type="down"> <div class="arrow-down" type="down"></div> </div></div>');{w2utils.getSize(d.el,"height")}b=a(d.el).next(),b.css({color:a(d.el).css("color"),"font-family":a(d.el).css("font-family"),"font-size":a(d.el).css("font-size"),height:a(d.el).height()+parseInt(a(d.el).css("padding-top"),10)+parseInt(a(d.el).css("padding-bottom"),10)+"px",padding:0,"margin-top":parseInt(a(d.el).css("margin-top"),10)+1+"px","margin-bottom":0,"border-left":"1px solid silver"}).css("margin-left","-"+(b.width()+parseInt(a(d.el).css("margin-right"),10)+12)+"px").on("mousedown",function(b){function c(){clearTimeout(a("body").data("_field_update_timer")),a("body").off("mouseup",c)}function e(c){a(d.el).focus(),d.keyDown(a.Event("keydown"),{keyCode:"up"==a(b.target).attr("type")?38:40}),c!==!1&&a("body").data("_field_update_timer",setTimeout(e,60))}a("body").on("mouseup",c),a("body").data("_field_update_timer",setTimeout(e,700)),e(!1)}),c+=b.width()+12,a(d.el).css("padding-right",c+"px"),d.helpers.arrows=b}""!==d.options.suffix&&(d.helpers.suffix&&a(d.helpers.suffix).remove(),a(d.el).after('<div class="w2ui-field-helper">'+d.options.suffix+"</div>"),b=a(d.el).next(),b.css({color:a(d.el).css("color"),"font-family":a(d.el).css("font-family"),"font-size":a(d.el).css("font-size"),"padding-top":a(d.el).css("padding-top"),"padding-bottom":a(d.el).css("padding-bottom"),"padding-left":"3px","padding-right":a(d.el).css("padding-right"),"margin-top":parseInt(a(d.el).css("margin-top"),10)+2+"px","margin-bottom":parseInt(a(d.el).css("margin-bottom"),10)+1+"px"}).on("click",function(){"list"==d.type?a(d.helpers.focus).find("input").focus():a(d.el).focus()}),b.css("margin-left","-"+(w2utils.getSize(b,"width")+parseInt(a(d.el).css("margin-right"),10)+2)+"px"),c+=b.width()+3,a(d.el).css("padding-right",c+"px"),d.helpers.suffix=b)}},1)},addFocus:function(){var b=this,c=(this.options,0);a(b.helpers.focus).remove();var d='<div class="w2ui-field-helper"> <div class="w2ui-icon icon-search"></div> <input type="text" autocomplete="off"><div>';a(b.el).attr("tabindex",-1).before(d);var e=a(b.el).prev();b.helpers.focus=e,e.css({width:a(b.el).width(),"margin-top":a(b.el).css("margin-top"),"margin-left":parseInt(a(b.el).css("margin-left"))+parseInt(a(b.el).css("padding-left"))+"px","margin-bottom":a(b.el).css("margin-bottom"),"margin-right":a(b.el).css("margin-right")}).find("input").css({cursor:"default",width:"100%",outline:"none",opacity:1,margin:0,border:"1px solid transparent",padding:a(b.el).css("padding-top"),"padding-left":0,"margin-left":c>0?c+6:0,"background-color":"transparent"}),e.find("input").on("click",function(c){0==a("#w2ui-overlay").length&&b.focus(c),c.stopPropagation()}).on("focus",function(c){a(b.el).css({outline:"auto 5px #7DB4F3","outline-offset":"-2px"}),a(this).val(""),a(b.el).triggerHandler("focus"),c.stopPropagation?c.stopPropagation():c.cancelBubble=!0}).on("blur",function(c){a(b.el).css("outline","none"),a(this).val(""),b.refresh(),a(b.el).triggerHandler("blur"),c.stopPropagation?c.stopPropagation():c.cancelBubble=!0}).on("keyup",function(a){b.keyUp(a)}).on("keydown",function(a){b.keyDown(a)}).on("keypress",function(a){b.keyPress(a)}),e.on("click",function(){a(this).find("input").focus()}),b.refresh()},addMulti:function(){{var b=this;this.options}a(b.helpers.multi).remove();var c="",d="margin-top : 0px; margin-bottom : 0px; margin-left : "+a(b.el).css("margin-left")+"; margin-right : "+a(b.el).css("margin-right")+"; width : "+(w2utils.getSize(b.el,"width")-parseInt(a(b.el).css("margin-left"),10)-parseInt(a(b.el).css("margin-right"),10))+"px;";"enum"==b.type&&(c='<div class="w2ui-field-helper w2ui-list" style="'+d+'; box-sizing: border-box"> <div style="padding: 0px; margin: 0px; margin-right: 20px; display: inline-block"> <ul> <li style="padding-left: 0px; padding-right: 0px" class="nomouse"> <input type="text" style="width: 20px" autocomplete="off" '+(a(b.el).attr("readonly")?"readonly":"")+"> </li>"),"file"==b.type&&(c='<div class="w2ui-field-helper w2ui-list" style="'+d+'; box-sizing: border-box"> <div style="padding: 0px; margin: 0px; margin-right: 20px; display: inline-block"> <ul><li style="padding-left: 0px; padding-right: 0px" class="nomouse"></li></ul> <input class="file-input" type="file" name="attachment" multiple style="display: none" tabindex="-1">'),a(b.el).before(c).css({"background-color":"transparent","border-color":"transparent"});var e=a(b.el).prev();b.helpers.multi=e,"enum"==b.type&&(a(b.el).attr("tabindex",-1),e.find("input").on("click",function(c){0==a("#w2ui-overlay").length&&b.focus(c),a(b.el).triggerHandler("click")}).on("focus",function(c){a(e).css({outline:"auto 5px #7DB4F3","outline-offset":"-2px"}),a(b.el).triggerHandler("focus"),c.stopPropagation?c.stopPropagation():c.cancelBubble=!0}).on("blur",function(c){a(e).css("outline","none"),a(b.el).triggerHandler("blur"),c.stopPropagation?c.stopPropagation():c.cancelBubble=!0}).on("keyup",function(a){b.keyUp(a)}).on("keydown",function(a){b.keyDown(a)}).on("keypress",function(a){e.find(".w2ui-enum-placeholder").remove(),b.keyPress(a)}),e.on("click",function(){a(this).find("input").focus()})),"file"==b.type&&(a(b.el).css("outline","none"),e.on("click",function(c){a(b.el).focus(),a(b.el).attr("readonly")||(b.blur(c),e.find("input").click())}).on("dragenter",function(){a(b.el).attr("readonly")||a(e).addClass("w2ui-file-dragover")}).on("dragleave",function(c){if(!a(b.el).attr("readonly")){var d=a(c.target).parents(".w2ui-field-helper");0==d.length&&a(e).removeClass("w2ui-file-dragover")}}).on("drop",function(c){if(!a(b.el).attr("readonly")){a(e).removeClass("w2ui-file-dragover");for(var d=c.originalEvent.dataTransfer.files,f=0,g=d.length;g>f;f++)b.addFile.call(b,d[f]);c.preventDefault(),c.stopPropagation()}}).on("dragover",function(a){a.preventDefault(),a.stopPropagation()}),e.find("input").on("click",function(a){a.stopPropagation()}).on("change",function(){if("undefined"!=typeof this.files)for(var a=0,c=this.files.length;c>a;a++)b.addFile.call(b,this.files[a])})),b.refresh()},addFile:function(b){var c,d=this,e=this.options,f=a(d.el).data("selected"),g={name:b.name,type:b.type,modified:b.lastModifiedDate,size:b.size,content:null},h=0,i=0;for(var j in f)h+=f[j].size,i++;var k=d.trigger({phase:"before",type:"add",target:d.el,file:g,total:i,totalSize:h});if(k.isCancelled!==!0){if(0!==e.maxFileSize&&g.size>e.maxFileSize)return c="Maximum file size is "+w2utils.size(e.maxFileSize),e.silent===!1&&a(d.el).w2tag(c),void console.log("ERROR: "+c);if(0!==e.maxSize&&h+g.size>e.maxSize)return c="Maximum total size is "+w2utils.size(e.maxSize),e.silent===!1&&a(d.el).w2tag(c),void console.log("ERROR: "+c);if(0!==e.max&&i>=e.max)return c="Maximum number of files is "+e.max,e.silent===!1&&a(d.el).w2tag(c),void console.log("ERROR: "+c);if(f.push(g),"undefined"!=typeof FileReader){var l=new FileReader;l.onload=function(){return function(b){var c=b.target.result,e=c.indexOf(",");g.content=c.substr(e+1),d.refresh(),a(d.el).trigger("change"),d.trigger(a.extend(k,{phase:"after"}))}}(),l.readAsDataURL(b)}else d.refresh(),a(d.el).trigger("change")}},normMenu:function(b){if(a.isArray(b)){for(var c=0;c<b.length;c++)"string"==typeof b[c]?b[c]={id:b[c],text:b[c]}:("undefined"!=typeof b[c].text&&"undefined"==typeof b[c].id&&(b[c].id=b[c].text),"undefined"==typeof b[c].text&&"undefined"!=typeof b[c].id&&(b[c].text=b[c].id),"undefined"!=typeof b[c].caption&&(b[c].text=b[c].caption));return b}if("object"==typeof b){var d=[];for(var c in b)d.push({id:c,text:b[c]});return d}},getColorHTML:function(){for(var b='<div class="w2ui-color"><table cellspacing="5">',c=0;8>c;c++){b+="<tr>";for(var d=0;8>d;d++)b+='<td> <div class="color" style="background-color: #'+this.pallete[c][d]+';" name="'+this.pallete[c][d]+'" index="'+c+":"+d+'"> '+(a(this.el).val()==this.pallete[c][d]?"&#149;":"&nbsp;")+" </div></td>";b+="</tr>",2>c&&(b+='<tr><td style="height: 8px" colspan="8"></td></tr>')}return b+="</table></div>"},getMonthHTML:function(a,b){var c=new Date,d=w2utils.settings.fullmonths,e=(w2utils.settings.fulldays,["31","28","31","30","31","30","31","31","30","31","30","31"]),f=c.getFullYear()+"/"+(Number(c.getMonth())+1)+"/"+c.getDate();b=w2utils.isInt(b)?parseInt(b):c.getFullYear(),a=w2utils.isInt(a)?parseInt(a):c.getMonth()+1,a>12&&(a-=12,b++),(1>a||0===a)&&(a+=12,b--),e[1]=b/4==Math.floor(b/4)?"29":"28",this.options.current=a+"/"+b,c=new Date(b,a-1,1);for(var g=c.getDay(),h=w2utils.settings.shortdays,i="",j=0,k=h.length;k>j;j++)i+="<td>"+h[j]+"</td>";for(var l='<div class="w2ui-calendar-title title"> <div class="w2ui-calendar-previous previous"> <div></div> </div> <div class="w2ui-calendar-next next"> <div></div> </div> '+d[a-1]+", "+b+'</div><table class="w2ui-calendar-days" cellspacing="0"> <tr class="w2ui-day-title">'+i+"</tr> <tr>",m=1,n=1;43>n;n++){if(0===g&&1==n){for(var o=0;6>o;o++)l+='<td class="w2ui-day-empty">&nbsp;</td>';n+=6}else if(g>n||m>e[a-1]){l+='<td class="w2ui-day-empty">&nbsp;</td>',n%7===0&&(l+="</tr><tr>");continue}var p=b+"/"+a+"/"+m,q="";n%7==6&&(q=" w2ui-saturday"),n%7===0&&(q=" w2ui-sunday"),p==f&&(q+=" w2ui-today");var r=m,s="",t="",u=w2utils.formatDate(p,this.options.format);this.options.colored&&void 0!==this.options.colored[u]&&(tmp=this.options.colored[u].split(":"),t="background-color: "+tmp[0]+";",s="color: "+tmp[1]+";"),l+='<td class="'+(this.inRange(u)?"w2ui-date ":"w2ui-blocked")+q+'" style="'+s+t+'" date="'+u+'">'+r+"</td>",(n%7===0||0===g&&1==n)&&(l+="</tr><tr>"),m++}return l+="</tr></table>"},getYearHTML:function(){var a=w2utils.settings.shortmonths,b="",c="";for(var d in a)b+='<div class="w2ui-jump-month" name="'+d+'">'+a[d]+"</div>";for(var e=1950;2020>=e;e++)c+='<div class="w2ui-jump-year" name="'+e+'">'+e+"</div>";return"<div>"+b+"</div><div>"+c+"</div>"},getHourHTML:function(){for(var a=[],b="h24"==this.options.format?!0:!1,c=0;24>c;c++){var d=(c>=12&&!b?c-12:c)+":00"+(b?"":12>c?" am":" pm");12!=c||b||(d="12:00 pm"),a[Math.floor(c/8)]||(a[Math.floor(c/8)]="");var e=this.fromMin(this.toMin(d)),f=this.fromMin(this.toMin(d)+59);a[Math.floor(c/8)]+='<div class="'+(this.inRange(e)||this.inRange(f)?"w2ui-time ":"w2ui-blocked")+'" hour="'+c+'">'+d+"</div>"}var g='<div class="w2ui-calendar-time"><table><tr> <td>'+a[0]+"</td> <td>"+a[1]+"</td> <td>"+a[2]+"</td></tr></table></div>";return g},getMinHTML:function(a){"undefined"==typeof a&&(a=0);for(var b="h24"==this.options.format?!0:!1,c=[],d=0;60>d;d+=5){var e=(a>12&&!b?a-12:a)+":"+(10>d?0:"")+d+" "+(b?"":12>a?"am":"pm"),f=20>d?0:40>d?1:2;c[f]||(c[f]=""),c[f]+='<div class="'+(this.inRange(e)?"w2ui-time ":"w2ui-blocked")+'" min="'+d+'">'+e+"</div>"}var g='<div class="w2ui-calendar-time"><table><tr> <td>'+c[0]+"</td> <td>"+c[1]+"</td> <td>"+c[2]+"</td></tr></table></div>";return g},toMin:function(a){if("string"!=typeof a)return null;var b=a.split(":");return 2!=b.length?null:(b[0]=parseInt(b[0]),b[1]=parseInt(b[1]),-1!=a.indexOf("pm")&&12!=b[0]&&(b[0]+=12),60*b[0]+b[1])},fromMin:function(a){var b="";a>=1440&&(a%=1440),0>a&&(a=1440+a);var c=Math.floor(a/60),d=(10>a%60?"0":"")+a%60;return b=-1!=this.options.format.indexOf("h24")?c+":"+d:(12>=c?c:c-12)+":"+d+" "+(c>=12?"pm":"am")}},a.extend(b.prototype,w2utils.event),w2obj.field=b}(jQuery),function(){var w2form=function(a){this.name=null,this.header="",this.box=null,this.url="",this.routeData={},this.formURL="",this.formHTML="",this.page=0,this.recid=0,this.fields=[],this.actions={},this.record={},this.original={},this.postData={},this.toolbar={},this.tabs={},this.style="",this.focus=0,this.msgNotJSON=w2utils.lang("Return data is not in JSON format."),this.msgAJAXerror=w2utils.lang("AJAX error. See console for more details."),this.msgRefresh=w2utils.lang("Refreshing..."),this.msgSaving=w2utils.lang("Saving..."),this.onRequest=null,this.onLoad=null,this.onValidate=null,this.onSubmit=null,this.onSave=null,this.onChange=null,this.onRender=null,this.onRefresh=null,this.onResize=null,this.onDestroy=null,this.onAction=null,this.onToolbar=null,this.onError=null,this.isGenerated=!1,this.last={xhr:null},$.extend(!0,this,w2obj.form,a)};$.fn.w2form=function(a){if("object"==typeof a||!a){var b=this;if(!w2utils.checkName(a,"w2form"))return;var c=a.record,d=a.original,e=a.fields,f=a.toolbar,g=a.tabs,h=new w2form(a);if($.extend(h,{record:{},original:{},fields:[],tabs:{},toolbar:{},handlers:[]}),$.isArray(g)){$.extend(!0,h.tabs,{tabs:[]});for(var i in g){var j=g[i];h.tabs.tabs.push("object"==typeof j?j:{id:j,caption:j})}}else $.extend(!0,h.tabs,g);$.extend(!0,h.toolbar,f);for(var k in e){var l=$.extend(!0,{},e[k]);"undefined"==typeof l.name&&"undefined"!=typeof l.field&&(l.name=l.field),"undefined"==typeof l.field&&"undefined"!=typeof l.name&&(l.field=l.name),h.fields[k]=l}for(var k in c)h.record[k]=$.isPlainObject(c[k])?$.extend(!0,{},c[k]):c[k];for(var k in d)h.original[k]=$.isPlainObject(d[k])?$.extend(!0,{},d[k]):d[k];return b.length>0&&(h.box=b[0]),""!=h.formURL?$.get(h.formURL,function(a){h.formHTML=a,h.isGenerated=!0,(0!=$(h.box).length||0!=a.length)&&($(h.box).html(a),h.render(h.box))}):""!=h.formHTML||(h.formHTML=0!=$(this).length&&""!=$.trim($(this).html())?$(this).html():h.generateHTML()),w2ui[h.name]=h,""==h.formURL&&(-1==String(h.formHTML).indexOf("w2ui-page")&&(h.formHTML='<div class="w2ui-page page-0">'+h.formHTML+"</div>"),$(h.box).html(h.formHTML),h.isGenerated=!0,h.render(h.box)),h}if(w2ui[$(this).attr("name")]){var b=w2ui[$(this).attr("name")];return b[a].apply(b,Array.prototype.slice.call(arguments,1)),this}console.log("ERROR: Method "+a+" does not exist on jQuery.w2form")},w2form.prototype={get:function(a,b){if(0===arguments.length){var c=[];for(var d in this.fields)null!=this.fields[d].name&&c.push(this.fields[d].name);return c}for(var e in this.fields)if(this.fields[e].name==a)return b===!0?e:this.fields[e];return null},set:function(a,b){for(var c in this.fields)if(this.fields[c].name==a)return $.extend(this.fields[c],b),this.refresh(),!0;return!1},reload:function(a){var b="object"!=typeof this.url?this.url:this.url.get;b&&0!=this.recid?this.request(a):"function"==typeof a&&a()},clear:function(){this.recid=0,this.record={},$().w2tag(),this.refresh()},error:function(a){var b=this.trigger({target:this.name,type:"error",message:a,xhr:this.last.xhr});return b.isCancelled===!0?void("function"==typeof callBack&&callBack()):(setTimeout(function(){w2alert(a,"Error")},1),void this.trigger($.extend(b,{phase:"after"})))},validate:function(a){"undefined"==typeof a&&(a=!0),$().w2tag();var b=[];for(var c in this.fields){var d=this.fields[c];switch(null==this.record[d.name]&&(this.record[d.name]=""),d.type){case"int":this.record[d.name]&&!w2utils.isInt(this.record[d.name])&&b.push({field:d,error:w2utils.lang("Not an integer")});break;case"float":this.record[d.name]&&!w2utils.isFloat(this.record[d.name])&&b.push({field:d,error:w2utils.lang("Not a float")});break;case"money":this.record[d.name]&&!w2utils.isMoney(this.record[d.name])&&b.push({field:d,error:w2utils.lang("Not in money format")});break;case"color":case"hex":this.record[d.name]&&!w2utils.isHex(this.record[d.name])&&b.push({field:d,error:w2utils.lang("Not a hex number")});break;case"email":this.record[d.name]&&!w2utils.isEmail(this.record[d.name])&&b.push({field:d,error:w2utils.lang("Not a valid email")});break;case"checkbox":this.record[d.name]=1==this.record[d.name]?1:0;break;case"date":d.options.format||(d.options.format=w2utils.settings.date_format),this.record[d.name]&&!w2utils.isDate(this.record[d.name],d.options.format)&&b.push({field:d,error:w2utils.lang("Not a valid date")+": "+d.options.format});break;case"list":case"combo":break;case"enum":}var e=this.record[d.name];d.required&&(""===e||$.isArray(e)&&0==e.length||$.isPlainObject(e)&&$.isEmptyObject(e))&&b.push({field:d,error:w2utils.lang("Required field")}),d.equalto&&this.record[d.name]!=this.record[d.equalto]&&b.push({field:d,error:w2utils.lang("Field should be equal to ")+d.equalto})}var f=this.trigger({phase:"before",target:this.name,type:"validate",errors:b});if(f.isCancelled!==!0){if(a)for(var g in f.errors){var h=f.errors[g];"radio"==h.field.type?$($(h.field.el).parents("div")[0]).w2tag(h.error,{"class":"w2ui-error"}):-1!=["enum","file"].indexOf(h.field.type)?!function(a){setTimeout(function(){var b=$(a.field.el).data("w2field").helpers.multi;$(a.field.el).w2tag(a.error),$(b).addClass("w2ui-error")},1)}(h):$(h.field.el).w2tag(h.error,{"class":"w2ui-error"}),this.goto(b[0].field.page)}return this.trigger($.extend(f,{phase:"after"})),b}},getChanges:function(){var a=function(b,c,d){for(var e in b)"object"==typeof b[e]?(d[e]=a(b[e],c[e]||{},{}),(!d[e]||$.isEmptyObject(d[e]))&&delete d[e]):b[e]!=c[e]&&(d[e]=b[e]);return d};return a(this.record,this.original,{})},request:function(postData,callBack){var obj=this;if("function"==typeof postData&&(callBack=postData,postData=null),("undefined"==typeof postData||null==postData)&&(postData={}),this.url&&("object"!=typeof this.url||this.url.get)){(null==this.recid||"undefined"==typeof this.recid)&&(this.recid=0);var params={};params.cmd="get-record",params.recid=this.recid,$.extend(params,this.postData),$.extend(params,postData);var eventData=this.trigger({phase:"before",type:"request",target:this.name,url:this.url,postData:params});if(eventData.isCancelled===!0)return void("function"==typeof callBack&&callBack({status:"error",message:"Request aborted."}));this.record={},this.original={},this.lock(this.msgRefresh);var url=eventData.url;if("object"==typeof eventData.url&&eventData.url.get&&(url=eventData.url.get),this.last.xhr)try{this.last.xhr.abort()}catch(e){}if(!$.isEmptyObject(obj.routeData)){var info=w2utils.parseRoute(url);if(info.keys.length>0)for(var k=0;k<info.keys.length;k++)null!=obj.routeData[info.keys[k].name]&&(url=url.replace(new RegExp(":"+info.keys[k].name,"g"),obj.routeData[info.keys[k].name]))}var ajaxOptions={type:"POST",url:url,data:eventData.postData,dataType:"text"};"HTTP"==w2utils.settings.dataType&&(ajaxOptions.data=String($.param(ajaxOptions.data,!1)).replace(/%5B/g,"[").replace(/%5D/g,"]")),"RESTFULL"==w2utils.settings.dataType&&(ajaxOptions.type="GET",ajaxOptions.data=String($.param(ajaxOptions.data,!1)).replace(/%5B/g,"[").replace(/%5D/g,"]")),"JSON"==w2utils.settings.dataType&&(ajaxOptions.type="POST",ajaxOptions.data=JSON.stringify(ajaxOptions.data),ajaxOptions.contentType="application/json"),this.last.xhr=$.ajax(ajaxOptions).done(function(data,status,xhr){obj.unlock();var eventData=obj.trigger({phase:"before",target:obj.name,type:"load",xhr:xhr});if(eventData.isCancelled===!0)return void("function"==typeof callBack&&callBack({status:"error",message:"Request aborted."}));var data,responseText=obj.last.xhr.responseText;if("error"!=status){if("undefined"!=typeof responseText&&""!=responseText){if("object"==typeof responseText)data=responseText;else try{eval("data = "+responseText)}catch(e){}"undefined"==typeof data&&(data={status:"error",message:obj.msgNotJSON,responseText:responseText}),"error"==data.status?obj.error(data.message):(obj.record=$.extend({},data.record),obj.original=$.extend({},data.record))}}else obj.error("AJAX Error "+xhr.status+": "+xhr.statusText),data={status:"error",message:obj.msgAJAXerror,responseText:responseText};obj.trigger($.extend(eventData,{phase:"after"})),obj.refresh(),"function"==typeof callBack&&callBack(data)}).fail(function(a,b,c){var d={status:b,error:c,rawResponseText:a.responseText},e=obj.trigger({phase:"before",type:"error",error:d,xhr:a});if(e.isCancelled!==!0){if("abort"!=b){var f;try{f=$.parseJSON(a.responseText)}catch(g){}console.log("ERROR: Server communication failed.","\n EXPECTED:",{status:"success",items:[{id:1,text:"item"}]},"\n OR:",{status:"error",message:"error message"},"\n RECEIVED:","object"==typeof f?f:a.responseText)}obj.trigger($.extend(e,{phase:"after"}))}}),this.trigger($.extend(eventData,{phase:"after"}))}},submit:function(a,b){return this.save(a,b)},save:function(postData,callBack){var obj=this;$(this.box).find(":focus").change(),"function"==typeof postData&&(callBack=postData,postData=null);var errors=obj.validate(!0);if(0===errors.length){if(("undefined"==typeof postData||null==postData)&&(postData={}),!obj.url||"object"==typeof obj.url&&!obj.url.save)return void console.log("ERROR: Form cannot be saved because no url is defined.");obj.lock(obj.msgSaving+' <span id="'+obj.name+'_progress"></span>'),setTimeout(function(){var params={};params.cmd="save-record",params.recid=obj.recid,$.extend(params,obj.postData),$.extend(params,postData),params.record=$.extend(!0,{},obj.record);var eventData=obj.trigger({phase:"before",type:"submit",target:obj.name,url:obj.url,postData:params});if(eventData.isCancelled!==!0){var url=eventData.url;if("object"==typeof eventData.url&&eventData.url.save&&(url=eventData.url.save),obj.last.xhr)try{obj.last.xhr.abort()}catch(e){}if(!$.isEmptyObject(obj.routeData)){var info=w2utils.parseRoute(url);if(info.keys.length>0)for(var k=0;k<info.keys.length;k++)null!=obj.routeData[info.keys[k].name]&&(url=url.replace(new RegExp(":"+info.keys[k].name,"g"),obj.routeData[info.keys[k].name]))}var ajaxOptions={type:"POST",url:url,data:eventData.postData,dataType:"text",xhr:function(){var a=new window.XMLHttpRequest;return a.upload.addEventListener("progress",function(a){if(a.lengthComputable){var b=Math.round(a.loaded/a.total*100);$("#"+obj.name+"_progress").text(""+b+"%")}},!1),a}};"HTTP"==w2utils.settings.dataType&&(ajaxOptions.data=String($.param(ajaxOptions.data,!1)).replace(/%5B/g,"[").replace(/%5D/g,"]")),"RESTFULL"==w2utils.settings.dataType&&(0!=obj.recid&&(ajaxOptions.type="PUT"),ajaxOptions.data=String($.param(ajaxOptions.data,!1)).replace(/%5B/g,"[").replace(/%5D/g,"]")),"JSON"==w2utils.settings.dataType&&(ajaxOptions.type="POST",ajaxOptions.data=JSON.stringify(ajaxOptions.data),ajaxOptions.contentType="application/json"),obj.last.xhr=$.ajax(ajaxOptions).done(function(data,status,xhr){obj.unlock();var eventData=obj.trigger({phase:"before",target:obj.name,type:"save",xhr:xhr,status:status});if(eventData.isCancelled!==!0){var data,responseText=xhr.responseText;if("error"!=status){if("undefined"!=typeof responseText&&""!=responseText){if("object"==typeof responseText)data=responseText;else try{eval("data = "+responseText)}catch(e){}"undefined"==typeof data&&(data={status:"error",message:obj.msgNotJSON,responseText:responseText}),"error"==data.status?obj.error(data.message):obj.original=$.extend({},obj.record)}}else obj.error("AJAX Error "+xhr.status+": "+xhr.statusText),data={status:"error",message:obj.msgAJAXerror,responseText:responseText};obj.trigger($.extend(eventData,{phase:"after"})),obj.refresh(),"success"==data.status&&"function"==typeof callBack&&callBack(data)}}).fail(function(a,b,c){var d={status:b,error:c,rawResponseText:a.responseText},e=obj.trigger({phase:"before",type:"error",error:d,xhr:a});e.isCancelled!==!0&&(console.log("ERROR: server communication failed. The server should return",{status:"success"},"OR",{status:"error",message:"error message"},", instead the AJAX request produced this: ",d),obj.trigger($.extend(e,{phase:"after"})))}),obj.trigger($.extend(eventData,{phase:"after"}))}},50)}},lock:function(){var a=$(this.box).find("> div:first-child"),b=Array.prototype.slice.call(arguments,0);b.unshift(a),w2utils.lock.apply(window,b)},unlock:function(){var a=this;setTimeout(function(){w2utils.unlock(a.box)},25)},"goto":function(a){"undefined"!=typeof a&&(this.page=a),$(this.box).data("auto-size")===!0&&$(this.box).height(0),this.refresh()},generateHTML:function(){var a,b=[],c="";for(var d in this.fields){var e="",f=this.fields[d];"undefined"==typeof f.html&&(f.html={}),f.html=$.extend(!0,{caption:"",span:6,attr:"",text:"",page:0},f.html),"undefined"==typeof a&&(a=f.html.page),""==f.html.caption&&(f.html.caption=f.name);var g='<input name="'+f.name+'" type="text" '+f.html.attr+"/>";("pass"===f.type||"password"===f.type)&&(g='<input name="'+f.name+'" type = "password" '+f.html.attr+"/>"),"checkbox"==f.type&&(g='<input name="'+f.name+'" type="checkbox" '+f.html.attr+"/>"),"textarea"==f.type&&(g='<textarea name="'+f.name+'" '+f.html.attr+"></textarea>"),"toggle"==f.type&&(g='<input name="'+f.name+'" type="checkbox" '+f.html.attr+' class="w2ui-toggle"/><div><div></div></div>'),f.html.group&&(""!=c&&(e+="\n </div>"),e+='\n <div class="w2ui-group-title">'+f.html.group+'</div>\n <div class="w2ui-group">',c=f.html.group),f.html.page!=a&&""!=c&&(b[b.length-1]+="\n </div>",c=""),e+='\n <div class="w2ui-field '+("undefined"!=typeof f.html.span?"w2ui-span"+f.html.span:"")+'">\n <label>'+w2utils.lang(f.html.caption)+"</label>\n <div>"+g+w2utils.lang(f.html.text)+"</div>\n </div>","undefined"==typeof b[f.html.page]&&(b[f.html.page]=""),b[f.html.page]+=e,a=f.html.page}if(""!=c&&(b[b.length-1]+="\n </div>"),this.tabs.tabs)for(var h=0;h<this.tabs.tabs.length;h++)"undefined"==typeof b[h]&&(b[h]="");for(var i in b)b[i]='<div class="w2ui-page page-'+i+'">'+b[i]+"\n</div>";var j="";if(!$.isEmptyObject(this.actions)){var k="";j+='\n<div class="w2ui-buttons">';for(var l in this.actions)k=-1!=["save","update","create"].indexOf(l.toLowerCase())?"btn-green":"",j+='\n <button name="'+l+'" class="btn '+k+'">'+w2utils.lang(l)+"</button>";j+="\n</div>"}return b.join("")+j},action:function(a,b){var c=this.trigger({phase:"before",target:a,type:"action",originalEvent:b});c.isCancelled!==!0&&("function"==typeof this.actions[a]&&this.actions[a].call(this,b),this.trigger($.extend(c,{phase:"after"})))},resize:function(){function a(){d.width($(b.box).width()).height($(b.box).height()),f.css("top",""!=b.header?w2utils.getSize(e,"height"):0),g.css("top",(""!=b.header?w2utils.getSize(e,"height"):0)+("object"==typeof b.toolbar&&$.isArray(b.toolbar.items)&&b.toolbar.items.length>0?w2utils.getSize(f,"height"):0)),h.css("top",(""!=b.header?w2utils.getSize(e,"height"):0)+("object"==typeof b.toolbar&&$.isArray(b.toolbar.items)&&b.toolbar.items.length>0?w2utils.getSize(f,"height")+5:0)+("object"==typeof b.tabs&&$.isArray(b.tabs.tabs)&&b.tabs.tabs.length>0?w2utils.getSize(g,"height")+5:0)),h.css("bottom",k.length>0?w2utils.getSize(k,"height"):0)}var b=this,c=this.trigger({phase:"before",target:this.name,type:"resize"});if(c.isCancelled!==!0){var d=$(this.box).find("> div"),e=$(this.box).find("> div .w2ui-form-header"),f=$(this.box).find("> div .w2ui-form-toolbar"),g=$(this.box).find("> div .w2ui-form-tabs"),h=$(this.box).find("> div .w2ui-page"),i=$(this.box).find("> div .w2ui-page.page-"+this.page),j=$(this.box).find("> div .w2ui-page.page-"+this.page+" > div"),k=$(this.box).find("> div .w2ui-buttons");a(),(0==parseInt($(this.box).height())||$(this.box).data("auto-size")===!0)&&($(this.box).height((e.length>0?w2utils.getSize(e,"height"):0)+("object"==typeof this.tabs&&$.isArray(this.tabs.tabs)&&this.tabs.tabs.length>0?w2utils.getSize(g,"height"):0)+("object"==typeof this.toolbar&&$.isArray(this.toolbar.items)&&this.toolbar.items.length>0?w2utils.getSize(f,"height"):0)+(h.length>0?w2utils.getSize(j,"height")+w2utils.getSize(i,"+height")+12:0)+(k.length>0?w2utils.getSize(k,"height"):0)),$(this.box).data("auto-size",!0)),a(),b.trigger($.extend(c,{phase:"after"}))}},refresh:function(){var a=(new Date).getTime(),b=this;if(this.box&&this.isGenerated&&"undefined"!=typeof $(this.box).html()){$(this.box).find("input, textarea, select").each(function(a,c){var d=$(c).attr("undefined"!=typeof $(c).attr("name")?"name":"id"),e=b.get(d);if(e){var f=$(c).parents(".w2ui-page");if(f.length>0)for(var g=0;100>g;g++)if(f.hasClass("page-"+g)){e.page=g;break}}});var c=this.trigger({phase:"before",target:this.name,type:"refresh",page:this.page});if(c.isCancelled!==!0){$(this.box).find(".w2ui-page").hide(),$(this.box).find(".w2ui-page.page-"+this.page).show(),$(this.box).find(".w2ui-form-header").html(this.header),"object"==typeof this.tabs&&$.isArray(this.tabs.tabs)&&this.tabs.tabs.length>0?($("#form_"+this.name+"_tabs").show(),this.tabs.active=this.tabs.tabs[this.page].id,this.tabs.refresh()):$("#form_"+this.name+"_tabs").hide(),"object"==typeof this.toolbar&&$.isArray(this.toolbar.items)&&this.toolbar.items.length>0?($("#form_"+this.name+"_toolbar").show(),this.toolbar.refresh()):$("#form_"+this.name+"_toolbar").hide();
+for(var d in this.fields){var e=this.fields[d];"undefined"==typeof e.name&&"undefined"!=typeof e.field&&(e.name=e.field),"undefined"==typeof e.field&&"undefined"!=typeof e.name&&(e.field=e.name),e.$el=$(this.box).find('[name="'+String(e.name).replace(/\\/g,"\\\\")+'"]'),e.el=e.$el[0],"undefined"==typeof e.el&&console.log('ERROR: Cannot associate field "'+e.name+'" with html control. Make sure html control exists with the same name.'),e.el&&(e.el.id=e.name);var f=$(e).data("w2field");f&&f.clear(),$(e.$el).off("change").on("change",function(){var a=this.value,c=b.record[this.name]?b.record[this.name]:"",d=b.get(this.name);if(-1!=["list","enum","file"].indexOf(d.type)&&$(this).data("selected")){var e=$(this).data("selected"),f=b.record[this.name];if($.isArray(e)){a=[];for(var g in e)a[g]=$.extend(!0,{},e[g])}if($.isPlainObject(e)&&(a=$.extend(!0,{},e)),$.isArray(f)){c=[];for(var g in f)c[g]=$.extend(!0,{},f[g])}$.isPlainObject(f)&&(c=$.extend(!0,{},f))}if("toggle"==d.type&&(a=$(this).prop("checked")?1:0),-1!=["int","float","percent","money","currency"].indexOf(d.type)&&(a=$(this).data("w2field").clean(a)),a!==c){var h=b.trigger({phase:"before",target:this.name,type:"change",value_new:a,value_previous:c});if(h.isCancelled===!0)return void $(this).val(b.record[this.name]);var i=this.value;if("select"==this.type&&(i=this.value),"checkbox"==this.type&&(i=this.checked?!0:!1),"radio"==this.type&&d.$el.each(function(a,b){b.checked&&(i=b.value)}),-1!=["int","float","percent","money","currency","list","combo","enum","file","toggle"].indexOf(d.type)&&(i=a),-1!=["enum","file"].indexOf(d.type)&&i.length>0){var j=$(d.el).data("w2field").helpers.multi;$(j).removeClass("w2ui-error")}b.record[this.name]=i,b.trigger($.extend(h,{phase:"after"}))}}),e.required?$(e.el).parent().parent().addClass("w2ui-required"):$(e.el).parent().parent().removeClass("w2ui-required")}$(this.box).find("button, input[type=button]").each(function(a,c){$(c).off("click").on("click",function(a){var c=this.value;this.id&&(c=this.id),this.name&&(c=this.name),b.action(c,a)})});for(var d in this.fields){var e=this.fields[d],g="undefined"!=typeof this.record[e.name]?this.record[e.name]:"";if(e.el)switch(e.type=String(e.type).toLowerCase(),e.options||(e.options={}),e.type){case"text":case"textarea":case"email":case"pass":case"password":e.el.value=g;break;case"int":case"float":case"money":case"currency":case"percent":case"hex":case"alphanumeric":case"color":case"date":case"time":e.el.value=g,$(e.el).w2field($.extend({},e.options,{type:e.type}));break;case"toggle":w2utils.isFloat(g)&&(g=parseFloat(g)),$(e.el).prop("checked",g?!0:!1),this.record[e.name]=g?1:0;break;case"list":case"combo":if("list"!=e.type||$.isPlainObject(g))e.el.value="combo"!=e.type||$.isPlainObject(g)?$.isPlainObject(g)&&"undefined"!=typeof g.text?g.text:"":g;else for(var h in e.options.items){var i=e.options.items[h];if($.isPlainObject(i)&&i.id==g){g=$.extend(!0,{},i),b.record[e.name]=g;break}if(h==g){g={id:h,text:i},b.record[e.name]=g;break}}$.isPlainObject(g)||(g={}),$(e.el).w2field($.extend({},e.options,{type:e.type,selected:g}));break;case"enum":case"file":$.isArray(g)||(g=[]),$(e.el).w2field($.extend({},e.options,{type:e.type,selected:g}));break;case"select":var j=e.options.items;if("undefined"!=typeof j&&j.length>0){j=w2obj.field.prototype.normMenu(j),$(e.el).html("");for(var k in j)$(e.el).append('<option value="'+j[k].id+'">'+j[k].text+"</option")}$(e.el).val(g);break;case"radio":$(e.$el).prop("checked",!1).each(function(a,b){$(b).val()==g&&$(b).prop("checked",!0)});break;case"checkbox":$(e.el).prop("checked",g?!0:!1);break;default:$(e.el).w2field($.extend({},e.options,{type:e.type}))}}for(var f=$(this.box).find(".w2ui-page"),h=0;h<f.length;h++)$(f[h]).find("> *").length>1&&$(f[h]).wrapInner("<div></div>");return this.trigger($.extend(c,{phase:"after"})),this.resize(),(new Date).getTime()-a}}},render:function(a){function b(){var a=$(d.box).find("input, select, textarea");a.length>d.focus&&a[d.focus].focus()}var c=(new Date).getTime(),d=this;if("object"==typeof a&&($(this.box).find("#form_"+this.name+"_tabs").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-reset w2ui-form").html(""),this.box=a),this.isGenerated&&this.box){var e=this.trigger({phase:"before",target:this.name,type:"render",box:"undefined"!=typeof a?a:this.box});if(e.isCancelled!==!0){$.isEmptyObject(this.original)&&!$.isEmptyObject(this.record)&&(this.original=$.extend(!0,{},this.record));var f="<div>"+(""!=this.header?'<div class="w2ui-form-header">'+this.header+"</div>":"")+' <div id="form_'+this.name+'_toolbar" class="w2ui-form-toolbar"></div> <div id="form_'+this.name+'_tabs" class="w2ui-form-tabs"></div>'+this.formHTML+"</div>";$(this.box).attr("name",this.name).addClass("w2ui-reset w2ui-form").html(f),$(this.box).length>0&&($(this.box)[0].style.cssText+=this.style),"function"!=typeof this.toolbar.render&&(this.toolbar=$().w2toolbar($.extend({},this.toolbar,{name:this.name+"_toolbar",owner:this})),this.toolbar.on("click",function(a){var b=d.trigger({phase:"before",type:"toolbar",target:a.target,originalEvent:a});b.isCancelled!==!0&&d.trigger($.extend(b,{phase:"after"}))})),"object"==typeof this.toolbar&&"function"==typeof this.toolbar.render&&this.toolbar.render($("#form_"+this.name+"_toolbar")[0]),"function"!=typeof this.tabs.render&&(this.tabs=$().w2tabs($.extend({},this.tabs,{name:this.name+"_tabs",owner:this})),this.tabs.on("click",function(a){d.goto(this.get(a.target,!0))})),"object"==typeof this.tabs&&"function"==typeof this.tabs.render&&this.tabs.render($("#form_"+this.name+"_tabs")[0]),this.trigger($.extend(e,{phase:"after"})),this.resize();var g="object"!=typeof this.url?this.url:this.url.get;return g&&0!=this.recid?this.request():this.refresh(),0==$(".w2ui-layout").length&&(this.tmp_resize=function(){w2ui[d.name].resize()},$(window).off("resize","body").on("resize","body",this.tmp_resize)),setTimeout(function(){d.resize(),d.refresh()},150),this.focus>=0&&setTimeout(b,500),(new Date).getTime()-c}}},destroy:function(){var a=this.trigger({phase:"before",target:this.name,type:"destroy"});a.isCancelled!==!0&&("object"==typeof this.toolbar&&this.toolbar.destroy&&this.toolbar.destroy(),"object"==typeof this.tabs&&this.tabs.destroy&&this.tabs.destroy(),$(this.box).find("#form_"+this.name+"_tabs").length>0&&$(this.box).removeAttr("name").removeClass("w2ui-reset w2ui-form").html(""),delete w2ui[this.name],this.trigger($.extend(a,{phase:"after"})),$(window).off("resize","body"))}},$.extend(w2form.prototype,w2utils.event),w2obj.form=w2form}(); \ No newline at end of file