diff options
Diffstat (limited to 'simple/simple-http/src/test/java/org/simpleframework')
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 0000000..9047de5 --- /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 0000000..9fd4441 --- /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 0000000..b916494 --- /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 0000000..76bfb6e --- /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 0000000..be11f59 --- /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 0000000..2f22a0a --- /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 0000000..0ee0e7d --- /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 0000000..6bb8ef5 --- /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 0000000..9ca5fe7 --- /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 0000000..70db4a3 --- /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 0000000..9f7a6bb --- /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 0000000..8f5ef13 --- /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 0000000..13d2f30 --- /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 0000000..d002c86 --- /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 0000000..00855c2 --- /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 0000000..14f2768 --- /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 0000000..f0011ce --- /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 0000000..f7b8f33 --- /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 0000000..b972f03 --- /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 0000000..92a4f5d --- /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 0000000..b631803 --- /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 0000000..e0ec896 --- /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 0000000..cb4b41e --- /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 0000000..614a7aa --- /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 0000000..a7f12b6 --- /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 0000000..f382a32 --- /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 0000000..43c0b86 --- /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 0000000..eb20930 --- /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 0000000..5ac7a14 --- /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 0000000..6b23fab --- /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 0000000..d81370e --- /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 0000000..92c9d64 --- /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 0000000..9b0bdcd --- /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 0000000..b0aae80 --- /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 0000000..ae9672f --- /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 0000000..47cbf35 --- /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 0000000..c48b248 --- /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 0000000..67751b8 --- /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 0000000..d6f6a09 --- /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 0000000..85960ed --- /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 0000000..60fcb5c --- /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 0000000..4636cc7 --- /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 0000000..0d0d73d --- /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 0000000..ea6e313 --- /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 0000000..8f52100 --- /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 0000000..3b86020 --- /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 0000000..a6f4f62 --- /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 0000000..d668a4a --- /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 0000000..36c0309 --- /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 0000000..4a4ee75 --- /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 0000000..67ea091 --- /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 0000000..7f49acb --- /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 0000000..4f96405 --- /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 0000000..448dad5 --- /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 0000000..a7fbfe5 --- /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 0000000..1c6916e --- /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 0000000..de7461c --- /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 0000000..0b93a90 --- /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 0000000..e5b0266 --- /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 0000000..863440e --- /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 0000000..9c0c7b8 --- /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 0000000..f262d10 --- /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 0000000..2d382f0 --- /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 0000000..100bf27 --- /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 0000000..5c14d00 --- /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 0000000..4ae4d60 --- /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 0000000..5c03ebd --- /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 0000000..aae92ff --- /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 0000000..037627d --- /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 0000000..3e89652 --- /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 0000000..99018e3 --- /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 0000000..7d27b9f --- /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 0000000..4711494 --- /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 0000000..1443c69 --- /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 0000000..6ca855a --- /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 0000000..e976b78 --- /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 0000000..5e6f81e --- /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 0000000..21c4abc --- /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 0000000..95bc6ef --- /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 0000000..fbabeeb --- /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 0000000..d6cd401 --- /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 0000000..24dfbec --- /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 0000000..5a77537 --- /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 0000000..966d92f --- /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 0000000..f514295 --- /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 0000000..d099f27 --- /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 0000000..bb8a6db --- /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 0000000..0ef307e --- /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 0000000..bdc6755 --- /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 0000000..c8f4a68 --- /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 0000000..b8402eb --- /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 0000000..5f09efa --- /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 0000000..d7780e7 --- /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 0000000..0c4ec4d --- /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 0000000..586c82f --- /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 0000000..7afa93b --- /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 0000000..20bf961 --- /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 0000000..0e33fb1 --- /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 0000000..ee1178a --- /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 Binary files differnew file mode 100644 index 0000000..e560b08 --- /dev/null +++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/failure.png 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 0000000..417f2c9 --- /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 0000000..69b546a --- /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 0000000..5377060 --- /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 0000000..9ed2acc --- /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 0000000..4711494 --- /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 0000000..3b898f7 --- /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 Binary files differnew file mode 100644 index 0000000..da9ebb8 --- /dev/null +++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/pending.png 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 Binary files differnew file mode 100644 index 0000000..b46c7f2 --- /dev/null +++ b/simple/simple-http/src/test/java/org/simpleframework/http/socket/table/success.png 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 0000000..97fc528 --- /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 0000000..4fbb478 --- /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 0000000..afdc6a0 --- /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, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """); + 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, '&').replace(/</g, '>').replace(/>/g, '<'); + 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 +' ').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();" '+ + ' > </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">'+ + ' - <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> </div>'+ + '</td>'; + } + if (obj.show.selectColumn) { + html += '<td class="w2ui-head w2ui-col-select">'+ + ' <div> </div>'+ + '</td>'; + } + if (obj.show.expandColumn) { + html += '<td class="w2ui-head w2ui-col-expand">'+ + ' <div> </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 ? ' ' : 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 ? ' ' : 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> </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 ? ' ' : col.caption) + + ' </div>'+ + '</td>'; + } + } + html += '<td class="w2ui-head w2ui-head-last"><div> </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'] = ' '; + 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;"> </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> </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"> </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"> </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 +'"> </div>'); + } else { + ren = '<div class="w2ui-list-remove" title="'+ w2utils.lang('Remove') +'" index="'+ s +'"> </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('•'); + }) + .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"> '+ + ' <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] ? '•' : ' ')+ + ' </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"> </td>'; + ci += 6; + } else { + if (ci < weekDay || day > daysCount[month-1]) { + html += '<td class="w2ui-day-empty"> </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 0000000..c69eecd --- /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 0000000..d82e32e --- /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,"&").replace(/>/g,">").replace(/</g,"<").replace(/"/g,""");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,"&").replace(/</g,">").replace(/>/g,"<");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+" ").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();\" > </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"> - <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> </div></td>'),c.show.selectColumn&&(a+='<td class="w2ui-head w2ui-col-select"> <div> </div></td>'),c.show.expandColumn&&(a+='<td class="w2ui-head w2ui-col-expand"> <div> </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:" ")+" </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:" ")+" </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> </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:" ")+" </div></td>"}}}return b+='<td class="w2ui-head w2ui-head-last"><div> </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=" ",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;"> </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> </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"> </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"> </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+'"> </div>'):'<div class="w2ui-list-remove" title="'+w2utils.lang("Remove")+'" index="'+g+'"> </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("•")}).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"> <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]?"•":" ")+" </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"> </td>';n+=6}else if(g>n||m>e[a-1]){l+='<td class="w2ui-day-empty"> </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 |