diff --git a/.classpath b/.classpath index 41aa8b0..35b0b7d 100644 --- a/.classpath +++ b/.classpath @@ -1,75 +1,93 @@ - + + - + + + + + + + + + + + + + - + - - + + - - + + - + - + - - - - - - - - + + + + + + + - - - - + + + + - - - - - - + + + + + + + + + + - - + + - - + + - + - - + + - + - - - - - - - - - - - + + + + + + + + + + + + + @@ -78,171 +96,270 @@ - - - - + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + - - + + + + + + - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + - + - - - + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - + + + + + - - - - + + + + - - - + + + - - - - + + + + + + - - - - + + + + - - - - - - + + + + + - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + diff --git a/.externalToolBuilders/Ant Superx Version.launch b/.externalToolBuilders/Ant Superx Version.launch new file mode 100644 index 0000000..a5803e9 --- /dev/null +++ b/.externalToolBuilders/Ant Superx Version.launch @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore index 4ae600a..993ef11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,46 +1,270 @@ -# ignore all log files -*.log -*.err -*.log.* +# agent and copilot context - local/temporary files +.github/ +# Gradle build output and wrapper cache +build/ +.gradle/ -# ignore all CVS + sub dirs -*CVS/ +# ignore /dist/* +*dist/ -# ignore java classes -*classes/ +# ignore swap files +superx/WEB-INF/reports/swap_* -# ignore files in directory rohdaten -superx/WEB-INF/conf/edustore/db/module/*/rohdaten/unl/*.unl -superx/WEB-INF/conf/edustore/db/install/rohdaten/unl/*.unl +# ignore eclipse settings +.settings/org.eclipse.ltk.core.refactoring.prefs -# ignore temporary files -*.tmp -*.tmp.sql -temp.xml -tmp.fo +# vim .swp files +*.swp -# ignore auto generated files -db.properties -dbforms-config.xml -superx/WEB-INF/dbforms-config.old -superx.datum +# ignore gradle bin +scripts/gradle/bin/ +scripts/gradle/.gradle/ -# ignore iReport Designer -*.jasper +# cobertura +superx-build/cobertura* -# ignore /dist/* -*dist/ +# classes +superx/WEB-INF/classes/ -# ignore swap files -superx/WEB-INF/reports/swap_* +# tag files +**/tags -src/edustore/edustore_*.xml +# .orig files +**/*.orig -# ignore eclipse settings -.settings/org.eclipse.ltk.core.refactoring.prefs -#rpta files: -superx/WEB-INF/reports/simple_table.jrtx -superx/WEB-INF/reports/tabelle_jrxml_a4_quer.xsl.jrxml -superx/xml/jr_templates.xsl -superx/xml/rpta_assistent.xsl +# datum.alt files +**/superx.datum.alt + +# script artefacts +scripts/**/build/ +scripts/**/log/ + +# build artefacts +superx-build/declared-jars.txt +superx-build/cobertura_build_tests/ +superx-build/cobertura_build_classes/ +superx-build/coveragereport/ +superx-build/results/ +superx-build/tmp_build/ + +# Eclipse launch configurations are causing conflicts on 'pull' if you've got local ones; so they should not be under SCM +scripts/eclipseExternalTools/ + +# extracted h1-modules. These should only be links to folders in cs.bia.his_components +src-modules/module/prom/ +src-modules/module/res/ +src-modules/module/astat/ +src-modules/module/tt/ +src-modules/module/star/ +src-modules/module/kern_s/ +src-modules/module/res_s/ +# also ignore symlinks on linux +src-modules/module/prom +src-modules/module/res +src-modules/module/astat +src-modules/module/tt +src-modules/module/star +src-modules/module/kern_s +src-modules/module/res_s +src-modules/module/biad + +# artifact folders for reduced/cleaned modules +superx/WEB-INF/conf/edustore/db/module/prom/ +superx/WEB-INF/conf/edustore/db/module/res/ +superx/WEB-INF/conf/edustore/db/module/zul/ +superx/WEB-INF/conf/edustore/db/module/sos/ +superx/WEB-INF/conf/edustore/db/module/sva/ +superx/WEB-INF/conf/edustore/db/module/ivs/ +superx/WEB-INF/conf/edustore/db/module/astat/ +superx/WEB-INF/conf/edustore/db/module/tt/ +superx/WEB-INF/conf/edustore/db/module/lm/ +superx/WEB-INF/conf/edustore/db/module/etl/ +superx/WEB-INF/conf/edustore/db/module/star/ +superx/WEB-INF/conf/edustore/db/module/cob/ +superx/WEB-INF/conf/edustore/db/module/kern_s/ +superx/WEB-INF/conf/edustore/db/module/res_s/ +superx/WEB-INF/conf/edustore/db/module/bau/ +superx/WEB-INF/conf/edustore/db/module/fin/ +superx/WEB-INF/conf/edustore/db/module/biad/ +superx/WEB-INF/conf/edustore/db/module/gang/ +superx/WEB-INF/conf/edustore/db/module/erfolg/ +superx/WEB-INF/conf/edustore/db/module/man/ +superx/WEB-INF/conf/edustore/db/module/kenn/ +superx/WEB-INF/conf/edustore/db/module/kennx/ + +# module generation artifacts for module fin +src/edustore/edustore_fin.xml +superx/WEB-INF/fin_dbforms-config_ids.xml +superx/WEB-INF/fin_dbforms-config_pg.xml + +# module generation artifacts for module bau +src/edustore/edustore_bau.xml +superx/WEB-INF/bau_dbforms-config_ids.xml +superx/WEB-INF/bau_dbforms-config_pg.xml + +# module generation artifacts for module prom +src/edustore/edustore_prom.xml +superx/WEB-INF/prom_dbforms-config_ids.xml +superx/WEB-INF/prom_dbforms-config_pg.xml +superx/edit/prom/ + +# module generation artifacts for module res +src/edustore/edustore_res.xml +superx/WEB-INF/res_dbforms-config_ids.xml +superx/WEB-INF/res_dbforms-config_pg.xml + +# module generation artifacts for module gang +src/edustore/edustore_gang.xml +superx/WEB-INF/gang_dbforms-config_ids.xml +superx/WEB-INF/gang_dbforms-config_pg.xml +superx/edit/gang/*edit.jsp +superx/edit/gang/*list.jsp + + +# module generation artifacts for module kenn +src/edustore/edustore_kenn.xml +superx/WEB-INF/kenn_dbforms-config_ids.xml +superx/WEB-INF/kenn_dbforms-config_pg.xml +superx/edit/kenn/*edit.jsp +superx/edit/kenn/*list.jsp + +# module generation artifacts for module kennx +src/edustore/edustore_kennx.xml +superx/WEB-INF/kennx_dbforms-config_ids.xml +superx/WEB-INF/kennx_dbforms-config_pg.xml +superx/edit/kennx/*edit.jsp +superx/edit/kennx/*list.jsp + + +# module generation artifacts for module man +src/edustore/edustore_man.xml +superx/WEB-INF/man_dbforms-config_ids.xml +superx/WEB-INF/man_dbforms-config_pg.xml +superx/edit/man/catdesc.jsp +superx/edit/man/cat.jsp +superx/edit/man/grouping.jsp +superx/edit/man/man_reports.jsp +superx/edit/man/organigramm_dialog.jsp +superx/edit/man/rpt.jsp +superx/edit/man/*edit.jsp +superx/edit/man/*list.jsp + +# module generation artifacts for module erfolg +src/edustore/edustore_erfolg.xml +superx/WEB-INF/erfolg_dbforms-config_ids.xml +superx/WEB-INF/erfolg_dbforms-config_pg.xml + +# module generation artifacts for module sos +src/edustore/edustore_sos.xml +superx/WEB-INF/sos_dbforms-config_ids.xml +superx/WEB-INF/sos_dbforms-config_pg.xml +superx/edit/sos/hoererstatus_edit.jsp +superx/edit/sos/koepfe_oder_faelle_edit.jsp +superx/edit/sos/lehr_stg_ab_edit.jsp +superx/edit/sos/semester_edit.jsp +superx/edit/sos/semester_list.jsp +superx/edit/sos/sos_status_edit.jsp +superx/edit/sos/sos_stichtag_edit.jsp +superx/edit/sos/sos_stichtag_list.jsp +superx/edit/sos/statistik_edit.jsp +superx/edit/sos/studierendenuebertragung_edit.jsp + +# module generation artifacts for module sva +src/edustore/edustore_sva.xml +superx/WEB-INF/sva_dbforms-config_ids.xml +superx/WEB-INF/sva_dbforms-config_pg.xml +superx/edit/sva/konstanten_sva_edit.jsp +superx/edit/sva/sichten_sva_dienstart_edit.jsp +superx/edit/sva/sichten_sva_klr_geldgeber_kurz_edit.jsp +superx/edit/sva/sva_stichtag_list.jsp +superx/edit/sva/sx_repository_sva_edit.jsp +superx/edit/sva/user_sva.jsp +superx/edit/sva/user_sva_manuell.jsp + +# module generation artifacts for module ivs +src/edustore/edustore_ivs.xml +superx/edit/ivs/user_inventar_edit.jsp +superx/WEB-INF/ivs_dbforms-config_ids.xml +superx/WEB-INF/ivs_dbforms-config_pg.xml + +# module generation artifacts for module zul +src/edustore/edustore_zul.xml +superx/WEB-INF/zul_dbforms-config_ids.xml +superx/WEB-INF/zul_dbforms-config_pg.xml + +# module generation artifacts for module astat +src/edustore/edustore_astat.xml +superx/WEB-INF/astat_dbforms-config_ids.xml +superx/WEB-INF/astat_dbforms-config_pg.xml + +# module generation artifacts for module tt +src/edustore/edustore_tt.xml +superx/WEB-INF/tt_dbforms-config_ids.xml +superx/WEB-INF/tt_dbforms-config_pg.xml +superx/edit/tt/ + +# module generation artifacts for module lm +src/edustore/edustore_lm.xml +superx/WEB-INF/lm_dbforms-config_ids.xml +superx/WEB-INF/lm_dbforms-config_pg.xml +superx/edit/lm/ + +# module generation artifacts for module etl +src/edustore/edustore_etl.xml +superx/WEB-INF/etl_dbforms-config_ids.xml +superx/WEB-INF/etl_dbforms-config_pg.xml +superx/edit/etl/etl_job_edit.jsp +superx/edit/etl/etl_step_edit.jsp +superx/edit/etl/etl_step_type_list.jsp + +# module generation artifacts for module star +src/edustore/edustore_star.xml +superx/WEB-INF/star_dbforms-config_ids.xml +superx/WEB-INF/star_dbforms-config_pg.xml + +# module generation artifacts for module cob +src/edustore/edustore_cob.xml +superx/WEB-INF/cob_dbforms-config_ids.xml +superx/WEB-INF/cob_dbforms-config_pg.xml +superx/edit/cob/sx_repository_cob_edit.jsp + +# module generation artifacts for module kern_s +src/edustore/edustore_kern_s.xml +superx/WEB-INF/kern_s_dbforms-config_ids.xml +superx/WEB-INF/kern_s_dbforms-config_pg.xml + +# module generation artifacts for module res_s +src/edustore/edustore_res_s.xml +superx/WEB-INF/res_s_dbforms-config_ids.xml +superx/WEB-INF/res_s_dbforms-config_pg.xml + +# module generation artifacts for module biad +src/edustore/edustore_biad.xml +superx/WEB-INF/biad_dbforms-config_ids.xml +superx/WEB-INF/biad_dbforms-config_pg.xml + +# dbt project generation backups +superx/dbt/projects/*_backup/ + +# Saiku his icons +superx/saiku/css/saiku/src/saiku_his.css + +# Dateifunktionalität im kenn und kennx Modul +superx\WEB-INF\downloads + +# vscode personal settings +.vscode/ +.metadata/ + +# edustore.xml soll nicht mehr ausgeliefert werden siehe #313658 +src/edustore/edustore.xml + +# package folder und config file für statisch generierte JSPs +src/de/superx/jsp/ +superx/WEB-INF/jsp_servlet_config.xml + +# jaspers werden nicht mehr commitet, sondern im build gebaut +*.jasper +# updateUnmodifiedJars backup +superx/WEB-INF/lib/backup/ diff --git a/.project b/.project index c89aa55..ee681a0 100644 --- a/.project +++ b/.project @@ -1,36 +1,58 @@ - - - eduStore_superX - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.babel.editor.rbeBuilder - - - - - - org.eclipse.jdt.groovy.core.groovyNature - org.eclipse.jdt.core.javanature - com.sysdeo.eclipse.tomcat.tomcatnature - com.jaspersoft.studio.babel.editor.rbeNature - - - - 1459344926910 - - 10 - - org.eclipse.ui.ide.multiFilter - 1.0-name-matches-false-false-dist - - - - + + + eduStore_superX + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.ui.externaltools.ExternalToolBuilder + full,incremental, + + + LaunchConfigHandle + <project>/.externalToolBuilders/Ant Superx Version.launch + + + + + + org.eclipse.jdt.core.javanature + com.sysdeo.eclipse.tomcat.tomcatnature + com.jaspersoft.studio.babel.editor.rbeNature + + + + 1617792163171 + + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-dist + + + + 1617792163186 + + 6 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-true-.+\.class + + + + 1687335947806 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/src-patches/com/sun/jersey/spi/container/servlet/WebComponent.java b/src-patches/com/sun/jersey/spi/container/servlet/WebComponent.java new file mode 100644 index 0000000..1678fc2 --- /dev/null +++ b/src-patches/com/sun/jersey/spi/container/servlet/WebComponent.java @@ -0,0 +1,901 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +/* gepatchtes class-File: + * im Rahmen von #331613 muss mit dem Update auf Tomcat 10 auch die Anwendung von Serlvet API 4.0 auf 6.0 umgestellt werden. + * Dies leistet ein Migrationstool, welches in erster Linie den Packagenamen von 'javax' auf 'jakarta' umstellt. + * Allerdings bestand danach immer noch in der 'jersey-bunde-1.19.4.jar' ein Zugriff auf die entfernte Methode + * 'HttpServletResponse.setStatus(int, String)', welcher beim Laden von Saiku zu einer Exception geführt hat, + * wodurch das Frontend in der Ladeanzeige geblieben ist. + * Der Patch ändert die Klasse derart, dass sie die noch vorhandene Methode 'HttpServletResponse.setStatus(int)' aufruft, + * was zu funktionieren scheint. + * Ursprung entspricht https://github.com/javaee/jersey-1.x/blob/864a01d7be490ab93d2424da3e446ad8eb84b1e8/jersey-servlet/src/main/java/com/sun/jersey/spi/container/servlet/WebComponent.java +*/ + +package com.sun.jersey.spi.container.servlet; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.net.URI; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.SecurityContext; + +import javax.naming.NamingException; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import com.sun.jersey.api.container.ContainerException; +import com.sun.jersey.api.container.MappableContainerException; +import com.sun.jersey.api.core.ApplicationAdapter; +import com.sun.jersey.api.core.ClasspathResourceConfig; +import com.sun.jersey.api.core.PackagesResourceConfig; +import com.sun.jersey.api.core.ResourceConfig; +import com.sun.jersey.api.core.TraceInformation; +import com.sun.jersey.api.core.servlet.WebAppResourceConfig; +import com.sun.jersey.api.representation.Form; +import com.sun.jersey.core.header.InBoundHeaders; +import com.sun.jersey.core.header.MediaTypes; +import com.sun.jersey.core.reflection.ReflectionHelper; +import com.sun.jersey.core.util.ReaderWriter; +import com.sun.jersey.server.impl.InitialContextHelper; +import com.sun.jersey.server.impl.ThreadLocalInvoker; +import com.sun.jersey.server.impl.application.DeferredResourceConfig; +import com.sun.jersey.server.impl.cdi.CDIComponentProviderFactoryInitializer; +import com.sun.jersey.server.impl.container.servlet.JSPTemplateProcessor; +import com.sun.jersey.server.impl.ejb.EJBComponentProviderFactoryInitilizer; +import com.sun.jersey.server.impl.managedbeans.ManagedBeanComponentProviderFactoryInitilizer; +import com.sun.jersey.server.impl.model.method.dispatch.FormDispatchProvider; +import com.sun.jersey.server.impl.monitoring.GlassFishMonitoringInitializer; +import com.sun.jersey.server.probes.UriRuleProbeProvider; +import com.sun.jersey.spi.container.ContainerListener; +import com.sun.jersey.spi.container.ContainerNotifier; +import com.sun.jersey.spi.container.ContainerRequest; +import com.sun.jersey.spi.container.ContainerResponse; +import com.sun.jersey.spi.container.ContainerResponseWriter; +import com.sun.jersey.spi.container.ReloadListener; +import com.sun.jersey.spi.container.WebApplication; +import com.sun.jersey.spi.container.WebApplicationFactory; +import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider; +import java.security.AccessController; +import java.security.PrivilegedActionException; + +/** + * An abstract Web component that may be extended a Servlet and/or + * Filter implementation, or encapsulated by a Servlet or Filter implementation. + * + * @author Paul Sandoz (paul.sandoz at oracle.com) + */ +public class WebComponent implements ContainerListener { + /** + * The servlet initialization property whose value is a fully qualified + * class name of a class that implements {@link ResourceConfig} or + * {@link Application}. + */ + public static final String APPLICATION_CONFIG_CLASS = + "javax.ws.rs.Application"; + + /** + * The servlet initialization property whose value is a fully qualified + * class name of a class that implements {@link ResourceConfig} or + * {@link Application}. + */ + public static final String RESOURCE_CONFIG_CLASS = + "com.sun.jersey.config.property.resourceConfigClass"; + + /** + * The base path in the Web Pages where JSP templates, associated with + * viewables of resource classes, are located. + *

+ * If this property is not set then the base path will be the root path + * of the Web Pages. + */ + public static final String JSP_TEMPLATES_BASE_PATH = + "com.sun.jersey.config.property.JSPTemplatesBasePath"; + + + private static final Logger LOGGER = + Logger.getLogger(WebComponent.class.getName()); + + private final ThreadLocalInvoker requestInvoker = + new ThreadLocalInvoker(); + + private final ThreadLocalInvoker responseInvoker = + new ThreadLocalInvoker(); + + + private WebConfig config; + + private ResourceConfig resourceConfig; + + private WebApplication application; + + public WebComponent() { + } + + public WebComponent(Application app) { + if (app == null) + throw new IllegalArgumentException(); + + if (app instanceof ResourceConfig) { + resourceConfig = (ResourceConfig) app; + } else { + resourceConfig = new ApplicationAdapter(app); + } + } + + /** + * Get the Web configuration. + * + * @return the Web configuration. + */ + public WebConfig getWebConfig() { + return config; + } + + /** + * Get the resource configuration. + * + * @return the resource configuration. + */ + public ResourceConfig getResourceConfig() { + return resourceConfig; + } + + /** + * Initiate the Web component. + * + * @param webConfig the Web configuration. + * @throws javax.servlet.ServletException in case of any initialization error + */ + public void init(WebConfig webConfig) throws ServletException { + config = webConfig; + + if (resourceConfig == null) + resourceConfig = createResourceConfig(config); + + load(); + + Object o = resourceConfig.getProperties().get( + ResourceConfig.PROPERTY_CONTAINER_NOTIFIER); + if (o instanceof List) { + List list = (List) o; + for (Object elem : list) { + if (elem instanceof ContainerNotifier) { + ContainerNotifier crf = (ContainerNotifier) elem; + crf.addListener(this); + } + } + } else if (o instanceof ContainerNotifier) { + ContainerNotifier crf = (ContainerNotifier) o; + crf.addListener(this); + } + } + + /** + * Destroy this Web component. + *

+ * This will destroy the Web application created by this this Web component. + */ + public void destroy() { + if (application != null) + application.destroy(); + } + + private final static class Writer extends OutputStream implements ContainerResponseWriter { + final HttpServletResponse response; + + ContainerResponse cResponse; + + long contentLength; + + OutputStream out; + + boolean statusAndHeadersWritten = false; + + Writer(HttpServletResponse response) { + this.response = response; + } + + public OutputStream writeStatusAndHeaders(long contentLength, + ContainerResponse cResponse) throws IOException { + this.contentLength = contentLength; + this.cResponse = cResponse; + this.statusAndHeadersWritten = false; + return this; + } + + public void finish() throws IOException { + if (statusAndHeadersWritten) + return; + + // Note that the writing of headers MUST be performed before + // the invocation of sendError as on some Servlet implementations + // modification of the response headers will have no effect + // after the invocation of sendError. + writeHeaders(); + + + writeStatus(); + } + + private void writeStatus() { + Response.StatusType statusType = cResponse.getStatusType(); + final String reasonPhrase = statusType.getReasonPhrase(); + +// if (reasonPhrase != null) { +// response.setStatus(statusType.getStatusCode(), reasonPhrase); +// } else { + response.setStatus(statusType.getStatusCode()); +// } + } + + public void write(int b) throws IOException { + initiate(); + out.write(b); + } + + @Override + public void write(byte b[]) throws IOException { + if (b.length > 0) { + initiate(); + out.write(b); + } + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + if (len > 0) { + initiate(); + out.write(b, off, len); + } + } + + @Override + public void flush() throws IOException { + writeStatusAndHeaders(); + if (out != null) + out.flush(); + } + + @Override + public void close() throws IOException { + initiate(); + out.close(); + } + + void initiate() throws IOException { + if (out == null) { + writeStatusAndHeaders(); + out = response.getOutputStream(); + } + } + + void writeStatusAndHeaders() { + if (statusAndHeadersWritten) + return; + + writeHeaders(); + writeStatus(); + statusAndHeadersWritten = true; + } + + void writeHeaders() { + if (contentLength != -1 && contentLength < Integer.MAX_VALUE) + response.setContentLength((int) contentLength); + + MultivaluedMap headers = cResponse.getHttpHeaders(); + for (Map.Entry> e : headers.entrySet()) { + for (Object v : e.getValue()) { + response.addHeader(e.getKey(), ContainerResponse.getHeaderValue(v)); + } + } + } + } + + /** + * Dispatch client requests to a resource class. + * + * @param baseUri the base URI of the request. + * @param requestUri the URI of the request. + * @param request the {@link HttpServletRequest} object that + * contains the request the client made to + * the Web component. + * @param response the {@link HttpServletResponse} object that + * contains the response the Web component returns + * to the client. + * @return the status code of the response. + * @throws IOException if an input or output error occurs + * while the Web component is handling the + * HTTP request. + * @throws ServletException if the HTTP request cannot + * be handled. + */ + public int service(URI baseUri, URI requestUri, + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + // Copy the application field to local instance to ensure that the + // currently loaded web application is used to process + // request + final WebApplication _application = application; + + final ContainerRequest cRequest = createRequest( + _application, + request, + baseUri, + requestUri); + + cRequest.setSecurityContext(new SecurityContext() { + public Principal getUserPrincipal() { + return request.getUserPrincipal(); + } + + public boolean isUserInRole(String role) { + return request.isUserInRole(role); + } + + public boolean isSecure() { + return request.isSecure(); + } + + public String getAuthenticationScheme() { + return request.getAuthType(); + } + }); + + try { + // Check if any servlet filters have consumed a request entity + // of the media type application/x-www-form-urlencoded + // This can happen if a filter calls request.getParameter(...) + filterFormParameters(request, cRequest); + + UriRuleProbeProvider.requestStart(requestUri); + + requestInvoker.set(request); + responseInvoker.set(response); + + final Writer w = new Writer(response); + _application.handleRequest(cRequest, w); + return w.cResponse.getStatus(); + } catch (WebApplicationException ex) { + final Response exResponse = ex.getResponse(); + final String entity = exResponse.getEntity() != null ? exResponse.getEntity().toString() : null; + + response.sendError(exResponse.getStatus(), entity); + + return exResponse.getStatus(); + } catch (MappableContainerException ex) { + traceOnException(cRequest, response); + throw new ServletException(ex.getCause()); + } catch (ContainerException ex) { + traceOnException(cRequest, response); + throw new ServletException(ex); + } catch (RuntimeException ex) { + traceOnException(cRequest, response); + throw ex; + } finally { + UriRuleProbeProvider.requestEnd(); + requestInvoker.set(null); + responseInvoker.set(null); + } + } + + /** + * Extension point for creating your custom container request. + * + * @param app the web app + * @param request the current servlet api request + * @param baseUri the base uri + * @param requestUri the request uri + * @return the request container + * @throws IOException if any error occurs when getting the input stream + */ + protected ContainerRequest createRequest(WebApplication app, + HttpServletRequest request, URI baseUri, URI requestUri) throws IOException { + return new ContainerRequest( + app, + request.getMethod(), + baseUri, + requestUri, + getHeaders(request), + request.getInputStream()); + } + + private void traceOnException(final ContainerRequest cRequest, final HttpServletResponse response) { + if (cRequest.isTracingEnabled()) { + final TraceInformation ti = (TraceInformation) cRequest.getProperties(). + get(TraceInformation.class.getName()); + + ti.addTraceHeaders(new TraceInformation.TraceHeaderListener() { + public void onHeader(String name, String value) { + response.addHeader(name, value); + } + }); + } + } + + /** + * Create a new instance of a {@link WebApplication}. + * + * @return the {@link WebApplication} instance. + */ + protected WebApplication create() { + return WebApplicationFactory.createWebApplication(); + } + + /** + * A helper class for creating an injectable provider that supports + * {@link Context} with a type and constant value. + * + * @param the type of the constant value. + */ + protected static class ContextInjectableProvider extends + SingletonTypeInjectableProvider { + + /** + * Create a new instance. + * + * @param type the type of the constant value. + * @param instance the constant value. + */ + protected ContextInjectableProvider(Type type, T instance) { + super(type, instance); + } + } + + /** + * Configure the {@link ResourceConfig}. + *

+ * The {@link ResourceConfig} is configured such that the following classes + * may be injected onto the field of a root resource class or a parameter + * of a method of root resource class that is annotated with + * {@link javax.ws.rs.core.Context}: {@link HttpServletRequest}, {@link HttpServletResponse} + * , {@link ServletContext} and {@link WebConfig}. + *

+ * Any root resource class in registered in the resource configuration + * that is an interface is processed as follows. + * If the class is an interface and there exists a JNDI named object + * with the fully qualified class name as the JNDI name then that named + * object is added as a singleton root resource and the class is removed + * from the set of root resource classes. + *

+ * An inheriting class may override this method to configure the + * {@link ResourceConfig} to provide alternative or additional instances + * that are resource or provider classes or instances, and may modify the + * features and properties of the {@link ResourceConfig}. For an inheriting + * class to extend configuration behaviour the overriding method MUST call + * super.configure(servletConfig, rc, wa) as the first statement + * of that method. + *

+ * This method will be called only once at initiation. Subsequent + * reloads of the Web application will not result in subsequence calls to + * this method. + * + * @param wc the Web configuration + * @param rc the Resource configuration + * @param wa the Web application + */ + protected void configure(WebConfig wc, ResourceConfig rc, WebApplication wa) { + configureJndiResources(rc); + + rc.getSingletons().add(new ContextInjectableProvider( + HttpServletRequest.class, + (HttpServletRequest) Proxy.newProxyInstance( + this.getClass().getClassLoader(), + new Class[]{HttpServletRequest.class}, + requestInvoker))); + + rc.getSingletons().add(new ContextInjectableProvider( + HttpServletResponse.class, + (HttpServletResponse) Proxy.newProxyInstance( + this.getClass().getClassLoader(), + new Class[]{HttpServletResponse.class}, + responseInvoker))); + + GenericEntity> requestThreadLocal = + new GenericEntity>(requestInvoker.getImmutableThreadLocal()) { + }; + rc.getSingletons().add(new ContextInjectableProvider>( + requestThreadLocal.getType(), requestThreadLocal.getEntity())); + + GenericEntity> responseThreadLocal = + new GenericEntity>(responseInvoker.getImmutableThreadLocal()) { + }; + rc.getSingletons().add(new ContextInjectableProvider>( + responseThreadLocal.getType(), responseThreadLocal.getEntity())); + + rc.getSingletons().add(new ContextInjectableProvider( + ServletContext.class, + wc.getServletContext())); + + rc.getSingletons().add(new ContextInjectableProvider( + WebConfig.class, + wc)); + + rc.getClasses().add(JSPTemplateProcessor.class); + + // TODO + // If CDI is enabled should EJB injection be disabled? + EJBComponentProviderFactoryInitilizer.initialize(rc); + + CDIComponentProviderFactoryInitializer.initialize(wc, rc, wa); + + // TODO + // If CDI is enabled then no need to initialize managed beans + ManagedBeanComponentProviderFactoryInitilizer.initialize(rc); + + GlassFishMonitoringInitializer.initialize(); + } + + /** + * Initiate the {@link WebApplication}. + *

+ * This method will be called once at initiation and for + * each reload of the Web application. + *

+ * An inheriting class may override this method to initiate the + * Web application with different parameters. + * + * @param rc the Resource configuration + * @param wa the Web application + */ + protected void initiate(ResourceConfig rc, WebApplication wa) { + wa.initiate(rc); + } + + + /** + * Load the Web application. This will create, configure and initiate + * the web application. + */ + public void load() { + WebApplication _application = create(); + configure(config, resourceConfig, _application); + initiate(resourceConfig, _application); + application = _application; + } + + /** + * Get the default resource configuration if one is not declared in the + * web.xml. + *

+ * This implementation returns an instance of {@link WebAppResourceConfig} + * that scans in files and directories as declared by the + * {@link ClasspathResourceConfig#PROPERTY_CLASSPATH} if present, otherwise + * in the "WEB-INF/lib" and "WEB-INF/classes" directories. + *

+ * An inheriting class may override this method to supply a different + * default resource configuration implementation. + * + * @param props the properties to pass to the resource configuration. + * @param wc the web configuration. + * @return the default resource configuration. + * @throws javax.servlet.ServletException in case of any issues with providing \ + * the default resource configuration + */ + protected ResourceConfig getDefaultResourceConfig(Map props, + WebConfig wc) throws ServletException { + return getWebAppResourceConfig(props, wc); + } + + + // ContainerListener + + /** + * Reload the Web application. This will create and initiate the web + * application using the same {@link ResourceConfig} implementation + * that was used to load the Web application. + *

+ * This method may be called at runtime, more than once, to reload the + * Web application. For example, if a {@link ResourceConfig} implementation + * is capable of detecting changes to resource classes (addition or removal) + * or providers then this method may be invoked to reload the web + * application for such changes to take effect. + *

+ * If this method is called when there are pending requests then such + * requests will be processed using the previously loaded web application. + */ + public void onReload() { + WebApplication oldApplication = application; + WebApplication newApplication = create(); + initiate(resourceConfig, newApplication); + application = newApplication; + + if (resourceConfig instanceof ReloadListener) + ((ReloadListener) resourceConfig).onReload(); + + oldApplication.destroy(); + + } + + + // + + /* package */ ResourceConfig getWebAppResourceConfig(Map props, + WebConfig webConfig) throws ServletException { + // Default to using Web app resource config + return new WebAppResourceConfig(props, webConfig.getServletContext()); + } + + private ResourceConfig createResourceConfig(WebConfig webConfig) + throws ServletException { + final Map props = getInitParams(webConfig); + final ResourceConfig rc = createResourceConfig(webConfig, props); + rc.setPropertiesAndFeatures(props); + return rc; + } + + private ResourceConfig createResourceConfig(WebConfig webConfig, Map props) + throws ServletException { + // Check if the resource config class property is present + String resourceConfigClassName = webConfig.getInitParameter(RESOURCE_CONFIG_CLASS); + // Otherwise check if the JAX-RS application config class property is + // present + if (resourceConfigClassName == null) + resourceConfigClassName = webConfig.getInitParameter(APPLICATION_CONFIG_CLASS); + + // If no resource config class property is present + if (resourceConfigClassName == null) { + // If the packages property is present then + // use the packages resource config + String packages = webConfig.getInitParameter( + PackagesResourceConfig.PROPERTY_PACKAGES); + if (packages != null) { + props.put(PackagesResourceConfig.PROPERTY_PACKAGES, packages); + return new PackagesResourceConfig(props); + } + + ResourceConfig defaultConfig = webConfig.getDefaultResourceConfig(props); + if (defaultConfig != null) + return defaultConfig; + + return getDefaultResourceConfig(props, webConfig); + } + + try { + Class resourceConfigClass = AccessController.doPrivileged(ReflectionHelper. + classForNameWithExceptionPEA(resourceConfigClassName)); + + // TODO add support for WebAppResourceConfig + if (resourceConfigClass == ClasspathResourceConfig.class) { + String[] paths = getPaths(webConfig.getInitParameter( + ClasspathResourceConfig.PROPERTY_CLASSPATH)); + props.put(ClasspathResourceConfig.PROPERTY_CLASSPATH, paths); + return new ClasspathResourceConfig(props); + } else if (ResourceConfig.class.isAssignableFrom(resourceConfigClass)) { + try { + Constructor constructor = resourceConfigClass.getConstructor(Map.class); + if (ClasspathResourceConfig.class.isAssignableFrom(resourceConfigClass)) { + String[] paths = getPaths(webConfig.getInitParameter( + ClasspathResourceConfig.PROPERTY_CLASSPATH)); + props.put(ClasspathResourceConfig.PROPERTY_CLASSPATH, paths); + } + return (ResourceConfig) constructor.newInstance(props); + } catch (NoSuchMethodException ex) { + // Pass through and try the default constructor + } catch (Exception e) { + throw new ServletException(e); + } + + return new DeferredResourceConfig(resourceConfigClass.asSubclass(ResourceConfig.class)); + } else if (Application.class.isAssignableFrom(resourceConfigClass)) { + return new DeferredResourceConfig(resourceConfigClass.asSubclass(Application.class)); + } else { + String message = "Resource configuration class, " + resourceConfigClassName + + ", is not a super class of " + Application.class; + throw new ServletException(message); + } + } catch (ClassNotFoundException e) { + String message = "Resource configuration class, " + resourceConfigClassName + + ", could not be loaded"; + throw new ServletException(message, e); + } catch (PrivilegedActionException e) { + String message = "Resource configuration class, " + resourceConfigClassName + + ", could not be loaded"; + throw new ServletException(message, e.getCause()); + } + } + + private Map getInitParams(WebConfig webConfig) { + Map props = new HashMap(); + Enumeration names = webConfig.getInitParameterNames(); + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + props.put(name, webConfig.getInitParameter(name)); + } + return props; + } + + private String[] getPaths(String classpath) throws ServletException { + final ServletContext context = config.getServletContext(); + if (classpath == null) { + String[] paths = { + context.getRealPath("/WEB-INF/lib"), + context.getRealPath("/WEB-INF/classes") + }; + if (paths[0] == null && paths[1] == null) { + String message = "The default deployment configuration that scans for " + + "classes in /WEB-INF/lib and /WEB-INF/classes is not supported " + + "for the application server." + + "Try using the package scanning configuration, see the JavaDoc for " + + PackagesResourceConfig.class.getName() + " and the property " + + PackagesResourceConfig.PROPERTY_PACKAGES + "."; + throw new ServletException(message); + } + return paths; + } else { + String[] virtualPaths = classpath.split(";"); + List resourcePaths = new ArrayList(); + for (String virtualPath : virtualPaths) { + virtualPath = virtualPath.trim(); + if (virtualPath.length() == 0) continue; + String path = context.getRealPath(virtualPath); + if (path != null) resourcePaths.add(path); + } + if (resourcePaths.isEmpty()) { + String message = "None of the declared classpath locations, " + + classpath + + ", could be resolved. " + + "This could be because the default deployment configuration that scans for " + + "classes in classpath locations is not supported. " + + "Try using the package scanning configuration, see the JavaDoc for " + + PackagesResourceConfig.class.getName() + " and the property " + + PackagesResourceConfig.PROPERTY_PACKAGES + "."; + throw new ServletException(message); + } + return resourcePaths.toArray(new String[resourcePaths.size()]); + } + } + + private void configureJndiResources(ResourceConfig rc) { + // Obtain any instances that are registered in JNDI + // Assumes such instances are singletons + // Registered classes have to be interfaces + javax.naming.Context x = InitialContextHelper.getInitialContext(); + if (x != null) { + Iterator> i = rc.getClasses().iterator(); + while (i.hasNext()) { + Class c = i.next(); + if (!c.isInterface()) continue; + + try { + Object o = x.lookup(c.getName()); + if (o != null) { + i.remove(); + rc.getSingletons().add(o); + LOGGER.log(Level.INFO, + "An instance of the class " + c.getName() + + " is found by JNDI look up using the class name as the JNDI name. " + + "The instance will be registered as a singleton."); + } + } catch (NamingException ex) { + LOGGER.log(Level.CONFIG, + "JNDI lookup failed for Jersey application resource " + c.getName(), + ex); + } + } + } + } + + private void filterFormParameters(HttpServletRequest servletRequest, ContainerRequest containerRequet) throws IOException { + if (MediaTypes.typeEquals(MediaType.APPLICATION_FORM_URLENCODED_TYPE, containerRequet.getMediaType()) + && !isEntityPresent(containerRequet)) { + Form f = new Form(); + + Enumeration e = servletRequest.getParameterNames(); + while (e.hasMoreElements()) { + String name = (String) e.nextElement(); + String[] values = servletRequest.getParameterValues(name); + + f.put(name, Arrays.asList(values)); + } + + if (!f.isEmpty()) { + containerRequet.getProperties().put(FormDispatchProvider.FORM_PROPERTY, f); + if (LOGGER.isLoggable(Level.WARNING)) { + LOGGER.log(Level.WARNING, + "A servlet request, to the URI " + containerRequet.getRequestUri() + ", " + + "contains form parameters in " + + "the request body but the request body has been consumed " + + "by the servlet or a servlet filter accessing the request " + + "parameters. Only resource methods using @FormParam " + + "will work as expected. Resource methods consuming the " + + "request body by other means will not work as expected."); + } + } + } + } + + private boolean isEntityPresent(ContainerRequest cr) throws IOException { + InputStream in = cr.getEntityInputStream(); + if (!in.markSupported()) { + in = new BufferedInputStream(in, ReaderWriter.BUFFER_SIZE); + cr.setEntityInputStream(in); + } + + in.mark(1); + if (in.read() == -1) + return false; + else { + in.reset(); + return true; + } + } + + private InBoundHeaders getHeaders(HttpServletRequest request) { + InBoundHeaders rh = new InBoundHeaders(); + + for (Enumeration names = request.getHeaderNames(); names.hasMoreElements();) { + String name = names.nextElement(); + List valueList = new LinkedList(); + for (Enumeration values = request.getHeaders(name); values.hasMoreElements();) { + valueList.add(values.nextElement()); + } + rh.put(name, valueList); + } + + return rh; + } +} diff --git a/src-patches/org/dbforms/util/MultipartRequest.java b/src-patches/org/dbforms/util/MultipartRequest.java new file mode 100644 index 0000000..3776261 --- /dev/null +++ b/src-patches/org/dbforms/util/MultipartRequest.java @@ -0,0 +1,366 @@ +/* + * $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/util/MultipartRequest.java,v 1.19 2005/02/19 21:26:32 hkollmann Exp $ + * $Revision: 1.19 $ + * $Date: 2005/02/19 21:26:32 $ + * + * DbForms - a Rapid Application Development Framework + * Copyright (C) 2001 Joachim Peer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.dbforms.util; + +import org.apache.commons.fileupload.DiskFileUpload; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletRequestWrapper; +import jakarta.servlet.http.HttpServletRequest; +import org.dbforms.util.external.FileUtil; + +import de.superx.util.multipart.support.DefaultMultipartHttpServletRequest; + + +/** + * A utility class to handle multipart/form-data requests, the + * kind of requests that support file uploads. This class emulates the interface + * of HttpServletRequest, making it familiar to use. It uses a + * "push" model where any incoming files are read and saved directly to disk in + * the constructor. If you wish to have more flexibility, e.g. write the files + * to a database, use the "pull" model MultipartParser instead. + *

+ * This class can receive arbitrarily large files (up to an artificial limit you + * can set), and fairly efficiently too. It cannot handle nested data (multipart + * content within multipart content) or internationalized content (such as non + * Latin-1 filenames). + *

+ *

+ * See the included upload.war for an example of how + * to use this class. + *

+ * The full file upload specification is contained in experimental RFC 1867, + * available at . + *

+ *

+ * PLEASE NOTE: this class uses Multipart Support Classes by Jason Hunter. + * Copyright (C) 1998 by Jason Hunter. All rights reserved. + * Use of this class is limited. Please see the LICENSE for more information. + * Make sure that you project meets the requierements defined by Jason Hunter + * respective O'reilly! + *

+ * + * @see MultipartParser + * + * @author Jason Hunter + * @author Geoff Soutter + * @author Joe Peer - _changed_ it for use in DbForms (i apologize) + */ +/* Witt: am 28.05.2026 als Basis für gepatchte Datei nach 'src-patches' übernommen, Basis waren meine + * lokalen 'dbforms_2.5_20050226' Sourcen */ +public class MultipartRequest { + private static Log logCat = LogFactory.getLog(MultipartRequest.class); // logging category + private Hashtable files = new Hashtable(); // name - UploadedFile + + // for this class + //private File dir; + private Hashtable parameters = new Hashtable(); // name - Vector of values + + /** + * Constructs a new MultipartRequest to handle the specified request, + * {saving any uploaded files to the given directory}, and limiting the + * upload size to the specified length. If the content is too large, an + * IOException is thrown. This constructor actually parses the + * multipart/form-data and throws an IOException if there's any + * problem reading or parsing the request. + * + * @param request + * the servlet request. + * @param maxPostSize + * the maximum size of the POST content. + * @exception IOException + * if the uploaded content is larger than + * maxPostSize or there's a problem reading or + * parsing the request. + */ + public MultipartRequest(HttpServletRequest request, + int maxPostSize) + throws IOException { + // Sanity check values + if (request == null) { + throw new IllegalArgumentException("request cannot be null"); + } + + // Create a new file upload handler + DiskFileUpload upload = new DiskFileUpload(); + + // Set upload parameters + upload.setSizeMax(maxPostSize); + + String tmpDir = System.getProperty("java.io.tmpdir"); + upload.setRepositoryPath(tmpDir); + + // Parse the request + try { + // we initilize this list with 'null', as the following unwrapping code should do the work - contrary to the 'org.dbforms.util.MultipartRequest code, from which this is copied + List items = null; //upload.parseRequest(request); + + if (items == null || items.size() == 0) { + ServletRequest originalRequest = request; + DefaultMultipartHttpServletRequest dmhsr = null; + while (originalRequest instanceof ServletRequestWrapper) { + if (originalRequest instanceof DefaultMultipartHttpServletRequest) { + dmhsr = (DefaultMultipartHttpServletRequest) originalRequest; + items = dmhsr.getFileItems(); + break; + } + originalRequest = ((ServletRequestWrapper) originalRequest).getRequest(); + } + } + + Iterator iter = items.iterator(); + + while (iter.hasNext()) { + FileItem item = (FileItem) iter.next(); + String name = item.getFieldName(); + + if (item.isFormField()) { + String value = item.getString(); + + // plain parameters get allways into parameters structure + Vector existingValues = (Vector) parameters.get(name); + + if (existingValues == null) { + existingValues = new Vector(); + parameters.put(name, existingValues); + } + + existingValues.addElement(value); + } else { + String fileName = item.getName(); + + if (!Util.isNull(fileName)) { + File f = new File(fileName); + // 2004-08-05-HKK/Dziugas Baltrunas: + // FileItem always returns the full pathname! + fileName = FileUtil.filename(f.getName()); + + // The part actually contained a file + // #changes by joe peer: + // we must delay storing the file-inputstream (into + // database, filesystem or whatever) + // until we know exactly what should happen with it + FileHolder fileHolder = new FileHolder(fileName, item); + files.put(name, fileHolder); + logCat.info("buffered and now added as " + name + + " the following fileHolder:" + + fileHolder.getFileName()); + + // #changes by joe peer: + // if a file parameter is not null it gets into the + // parameters structures as well + // this is important to provide a simple access to + // request (no distinction between files and plain + // params) + Vector existingValues = (Vector) parameters.get(name); + + if (existingValues == null) { + existingValues = new Vector(); + parameters.put(name, existingValues); + } + + existingValues.addElement(fileName); + } + } + } + } catch (Exception e) { + logCat.error("MultipartRequest", e); + } + } + + /** + * Returns the content type of the specified file (as supplied by the client + * browser), or null if the file was not included in the upload. + * + * @param name + * the file name. + * @return the content type of the file. + */ + public String getContentType(String name) { + try { + //#changed by joe peer + //UploadedFile file = (UploadedFile)files.get(name); + FileHolder filePart = (FileHolder) files.get(name); + + return filePart.getContentType(); // may be null + } catch (Exception e) { + return null; + } + } + + + /** + * Returns a FilePart object for the specified file this method was added by + * joe peer + * + * @param name + * the file name. + * @return a FilePart object for the named file. + */ + public FileHolder getFileHolder(String name) { + try { + return (FileHolder) files.get(name); + } catch (Exception e) { + return null; + } + } + + + /** + * Returns a InputStream object for the specified file saved on the server's + * filesystem, or null if the file was not included in the upload. + * + * @param name + * the file name. + * @return a InputStream object for the named file. + */ + public InputStream getFileInputStream(String name) { + try { + //#changed by joe peer + //UploadedFile file = (UploadedFile)files.get(name); + FileHolder filePart = (FileHolder) files.get(name); + + return filePart.getInputStreamFromBuffer(); // may be null + } catch (Exception e) { + return null; + } + } + + + /** + * Returns the names of all the uploaded files as an Enumeration of Strings. + * It returns an empty Enumeration if there are no uploaded files. Each file + * name is the name specified by the form, not by the user. + * + * @return the names of all the uploaded files as an Enumeration of Strings. + */ + public Enumeration getFileNames() { + return files.keys(); + } + + + /** + * Returns the filesystem name of the specified file, or null if the file + * was not included in the upload. A filesystem name is the name specified + * by the user. It is also the name under which the file is actually saved. + * + * @param name + * the file name. + * @return the filesystem name of the file. + */ + public String getFilesystemName(String name) { + try { + //#changed by joe peer + //UploadedFile file = (UploadedFile)files.get(name); + FileHolder filePart = (FileHolder) files.get(name); + + return filePart.getFileName(); // may be null + } catch (Exception e) { + return null; + } + } + + + /** + * Returns the value of the named parameter as a String, or null if the + * parameter was not sent or was sent without a value. The value is + * guaranteed to be in its normal, decoded form. If the parameter has + * multiple values, only the last one is returned (for backward + * compatibility). For parameters with multiple values, it's possible the + * last "value" may be null. + * + * @param name + * the parameter name. + * @return the parameter value. + */ + public String getParameter(String name) { + try { + Vector values = (Vector) parameters.get(name); + + if ((values == null) || (values.size() == 0)) { + return null; + } + + String value = (String) values.elementAt(0); + + return value; + } catch (Exception e) { + return null; + } + } + + + /** + * Returns the names of all the parameters as an Enumeration of Strings. It + * returns an empty Enumeration if there are no parameters. + * + * @return the names of all the parameters as an Enumeration of Strings. + */ + public Enumeration getParameterNames() { + return parameters.keys(); + } + + + /** + * Returns the values of the named parameter as a String array, or null if + * the parameter was not sent. The array has one entry for each parameter + * field sent. If any field was sent without a value that entry is stored in + * the array as a null. The values are guaranteed to be in their normal, + * decoded form. A single value is returned as a one-element array. + * + * @param name + * the parameter name. + * @return the parameter values. + */ + public String[] getParameterValues(String name) { + try { + Vector values = (Vector) parameters.get(name); + + if ((values == null) || (values.size() == 0)) { + return null; + } + + String[] valuesArray = new String[values.size()]; + values.copyInto(valuesArray); + + return valuesArray; + } catch (Exception e) { + return null; + } + } +} diff --git a/src-patches/org/springframework/security/web/util/OnCommittedResponseWrapper.java b/src-patches/org/springframework/security/web/util/OnCommittedResponseWrapper.java new file mode 100644 index 0000000..2418994 --- /dev/null +++ b/src-patches/org/springframework/security/web/util/OnCommittedResponseWrapper.java @@ -0,0 +1,699 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* taken/copied from 'spring-security-web-6.2.8.jar' as a base for patching */ + +package org.springframework.security.web.util; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Locale; + +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; + +/** + * Base class for response wrappers which encapsulate the logic for handling an event when + * the {@link jakarta.servlet.http.HttpServletResponse} is committed. + * + * @author Rob Winch + * @since 4.0.2 + */ +public abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper { + + private boolean disableOnCommitted; + + /** + * The Content-Length response header. If this is greater than 0, then once + * {@link #contentWritten} is larger than or equal the response is considered + * committed. + */ + private long contentLength; + + /** + * The size of data written to the response body. The field will only be updated when + * {@link #disableOnCommitted} is false. + */ + private long contentWritten; + + /** + * @param response the response to be wrapped + */ + public OnCommittedResponseWrapper(HttpServletResponse response) { + super(response); + } + + @Override + public void addHeader(String name, String value) { + checkContentLengthHeader(name, value); + super.addHeader(name, value); + } + + @Override + public void addIntHeader(String name, int value) { + checkContentLengthHeader(name, value); + super.addIntHeader(name, value); + } + + @Override + public void setHeader(String name, String value) { + checkContentLengthHeader(name, value); + super.setHeader(name, value); + } + + @Override + public void setIntHeader(String name, int value) { + checkContentLengthHeader(name, value); + super.setIntHeader(name, value); + } + + private void checkContentLengthHeader(String name, int value) { + if ("Content-Length".equalsIgnoreCase(name)) { + setContentLength(value); + } + } + + private void checkContentLengthHeader(String name, String value) { + if ("Content-Length".equalsIgnoreCase(name)) { + setContentLength(Long.parseLong(value)); + } + } + + @Override + public void setContentLength(int len) { + setContentLength((long) len); + super.setContentLength(len); + } + + @Override + public void setContentLengthLong(long len) { + setContentLength(len); + super.setContentLengthLong(len); + } + + private void setContentLength(long len) { + this.contentLength = len; + checkContentLength(0); + } + + /** + * Invoke this method to disable invoking + * {@link OnCommittedResponseWrapper#onResponseCommitted()} when the + * {@link jakarta.servlet.http.HttpServletResponse} is committed. This can be useful + * in the event that Async Web Requests are made. + */ + protected void disableOnResponseCommitted() { + this.disableOnCommitted = true; + } + + /** + * Returns true if {@link #onResponseCommitted()} will be invoked when the response is + * committed, else false. + * @return if {@link #onResponseCommitted()} is enabled + */ + protected boolean isDisableOnResponseCommitted() { + return this.disableOnCommitted; + } + + /** + * Implement the logic for handling the + * {@link jakarta.servlet.http.HttpServletResponse} being committed + */ + protected abstract void onResponseCommitted(); + + /** + * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked + * before calling the superclass sendError() + */ + @Override + public final void sendError(int sc) throws IOException { + doOnResponseCommitted(); + super.sendError(sc); + } + + /** + * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked + * before calling the superclass sendError() + */ + @Override + public final void sendError(int sc, String msg) throws IOException { + doOnResponseCommitted(); + super.sendError(sc, msg); + } + + /** + * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked + * before calling the superclass sendRedirect() + */ + @Override + public final void sendRedirect(String location) throws IOException { + doOnResponseCommitted(); + super.sendRedirect(location); + } + + /** + * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked + * before calling the calling getOutputStream().close() or + * getOutputStream().flush() + */ + @Override + public ServletOutputStream getOutputStream() throws IOException { + return new SaveContextServletOutputStream(super.getOutputStream()); + } + + /** + * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked + * before calling the getWriter().close() or + * getWriter().flush() + */ + @Override + public PrintWriter getWriter() throws IOException { + return new SaveContextPrintWriter(super.getWriter()); + } + + /** + * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked + * before calling the superclass flushBuffer() + */ + @Override + public void flushBuffer() throws IOException { + doOnResponseCommitted(); + super.flushBuffer(); + } + + private void trackContentLength(boolean content) { + if (!this.disableOnCommitted) { + checkContentLength(content ? 4 : 5); // TODO Localization + } + } + + private void trackContentLength(char content) { + if (!this.disableOnCommitted) { + checkContentLength(1); + } + } + + private void trackContentLength(Object content) { + if (!this.disableOnCommitted) { + trackContentLength(String.valueOf(content)); + } + } + + private void trackContentLength(byte[] content) { + if (!this.disableOnCommitted) { + checkContentLength((content != null) ? content.length : 0); + } + } + + private void trackContentLength(char[] content) { + if (!this.disableOnCommitted) { + checkContentLength((content != null) ? content.length : 0); + } + } + + private void trackContentLength(int content) { + if (!this.disableOnCommitted) { + trackContentLength(String.valueOf(content)); + } + } + + private void trackContentLength(float content) { + if (!this.disableOnCommitted) { + trackContentLength(String.valueOf(content)); + } + } + + private void trackContentLength(double content) { + if (!this.disableOnCommitted) { + trackContentLength(String.valueOf(content)); + } + } + + private void trackContentLengthLn() { + if (!this.disableOnCommitted) { + trackContentLength("\r\n"); + } + } + + private void trackContentLength(String content) { + if (!this.disableOnCommitted) { + int contentLength = (content != null) ? content.length() : 4; + checkContentLength(contentLength); + } + } + + /** + * Adds the contentLengthToWrite to the total contentWritten size and checks to see if + * the response should be written. + * @param contentLengthToWrite the size of the content that is about to be written. + */ + private void checkContentLength(long contentLengthToWrite) { + this.contentWritten += contentLengthToWrite; + boolean isBodyFullyWritten = this.contentLength > 0 && this.contentWritten >= this.contentLength; + int bufferSize = getBufferSize(); + boolean requiresFlush = bufferSize > 0 && this.contentWritten >= bufferSize; + if (isBodyFullyWritten || requiresFlush) { + doOnResponseCommitted(); + } + } + + /** + * Calls onResponseCommmitted() with the current contents as long as + * {@link #disableOnResponseCommitted()} was not invoked. + */ + private void doOnResponseCommitted() { + if (!this.disableOnCommitted) { + onResponseCommitted(); + disableOnResponseCommitted(); + } + } + + /** + * Ensures {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before + * calling the prior to methods that commit the response. We delegate all methods to + * the original {@link java.io.PrintWriter} to ensure that the behavior is as close to + * the original {@link java.io.PrintWriter} as possible. See SEC-2039 + * + * @author Rob Winch + */ + private class SaveContextPrintWriter extends PrintWriter { + + private final PrintWriter delegate; + + SaveContextPrintWriter(PrintWriter delegate) { + super(delegate); + this.delegate = delegate; + } + + @Override + public void flush() { + doOnResponseCommitted(); + this.delegate.flush(); + } + + @Override + public void close() { + doOnResponseCommitted(); + this.delegate.close(); + } + + @Override + public boolean equals(Object obj) { + return this.delegate.equals(obj); + } + + @Override + public int hashCode() { + return this.delegate.hashCode(); + } + + @Override + public String toString() { + return getClass().getName() + "[delegate=" + this.delegate.toString() + "]"; + } + + @Override + public boolean checkError() { + return this.delegate.checkError(); + } + + @Override + public void write(int c) { + trackContentLength(c); + this.delegate.write(c); + } + + @Override + public void write(char[] buf, int off, int len) { + checkContentLength(len); + this.delegate.write(buf, off, len); + } + + @Override + public void write(char[] buf) { + trackContentLength(buf); + this.delegate.write(buf); + } + + @Override + public void write(String s, int off, int len) { + checkContentLength(len); + this.delegate.write(s, off, len); + } + + @Override + public void write(String s) { + trackContentLength(s); + this.delegate.write(s); + } + + @Override + public void print(boolean b) { + trackContentLength(b); + this.delegate.print(b); + } + + @Override + public void print(char c) { + trackContentLength(c); + this.delegate.print(c); + } + + @Override + public void print(int i) { + trackContentLength(i); + this.delegate.print(i); + } + + @Override + public void print(long l) { + trackContentLength(l); + this.delegate.print(l); + } + + @Override + public void print(float f) { + trackContentLength(f); + this.delegate.print(f); + } + + @Override + public void print(double d) { + trackContentLength(d); + this.delegate.print(d); + } + + @Override + public void print(char[] s) { + trackContentLength(s); + this.delegate.print(s); + } + + @Override + public void print(String s) { + trackContentLength(s); + this.delegate.print(s); + } + + @Override + public void print(Object obj) { + trackContentLength(obj); + this.delegate.print(obj); + } + + @Override + public void println() { + trackContentLengthLn(); + this.delegate.println(); + } + + @Override + public void println(boolean x) { + trackContentLength(x); + trackContentLengthLn(); + this.delegate.println(x); + } + + @Override + public void println(char x) { + trackContentLength(x); + trackContentLengthLn(); + this.delegate.println(x); + } + + @Override + public void println(int x) { + trackContentLength(x); + trackContentLengthLn(); + this.delegate.println(x); + } + + @Override + public void println(long x) { + trackContentLength(x); + trackContentLengthLn(); + this.delegate.println(x); + } + + @Override + public void println(float x) { + trackContentLength(x); + trackContentLengthLn(); + this.delegate.println(x); + } + + @Override + public void println(double x) { + trackContentLength(x); + trackContentLengthLn(); + this.delegate.println(x); + } + + @Override + public void println(char[] x) { + trackContentLength(x); + trackContentLengthLn(); + this.delegate.println(x); + } + + @Override + public void println(String x) { + trackContentLength(x); + trackContentLengthLn(); + this.delegate.println(x); + } + + @Override + public void println(Object x) { + trackContentLength(x); + trackContentLengthLn(); + this.delegate.println(x); + } + + @Override + public PrintWriter printf(String format, Object... args) { + return this.delegate.printf(format, args); + } + + @Override + public PrintWriter printf(Locale l, String format, Object... args) { + return this.delegate.printf(l, format, args); + } + + @Override + public PrintWriter format(String format, Object... args) { + return this.delegate.format(format, args); + } + + @Override + public PrintWriter format(Locale l, String format, Object... args) { + return this.delegate.format(l, format, args); + } + + @Override + public PrintWriter append(CharSequence csq) { + checkContentLength(csq.length()); + return this.delegate.append(csq); + } + + @Override + public PrintWriter append(CharSequence csq, int start, int end) { + checkContentLength(end - start); + return this.delegate.append(csq, start, end); + } + + @Override + public PrintWriter append(char c) { + trackContentLength(c); + return this.delegate.append(c); + } + + } + + /** + * Ensures{@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before + * calling methods that commit the response. We delegate all methods to the original + * {@link jakarta.servlet.ServletOutputStream} to ensure that the behavior is as close + * to the original {@link jakarta.servlet.ServletOutputStream} as possible. See + * SEC-2039 + * + * @author Rob Winch + */ + private class SaveContextServletOutputStream extends ServletOutputStream { + + private final ServletOutputStream delegate; + + SaveContextServletOutputStream(ServletOutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + trackContentLength(b); + this.delegate.write(b); + } + + @Override + public void flush() throws IOException { + doOnResponseCommitted(); + this.delegate.flush(); + } + + @Override + public void close() throws IOException { + doOnResponseCommitted(); + this.delegate.close(); + } + + @Override + public void print(boolean b) throws IOException { + trackContentLength(b); + this.delegate.print(b); + } + + @Override + public void print(char c) throws IOException { + trackContentLength(c); + this.delegate.print(c); + } + + @Override + public void print(double d) throws IOException { + trackContentLength(d); + this.delegate.print(d); + } + + @Override + public void print(float f) throws IOException { + trackContentLength(f); + this.delegate.print(f); + } + + @Override + public void print(int i) throws IOException { + trackContentLength(i); + this.delegate.print(i); + } + + @Override + public void print(long l) throws IOException { + trackContentLength(l); + this.delegate.print(l); + } + + @Override + public void print(String s) throws IOException { + trackContentLength(s); + this.delegate.print(s); + } + + @Override + public void println() throws IOException { + trackContentLengthLn(); + this.delegate.println(); + } + + @Override + public void println(boolean b) throws IOException { + trackContentLength(b); + trackContentLengthLn(); + this.delegate.println(b); + } + + @Override + public void println(char c) throws IOException { + trackContentLength(c); + trackContentLengthLn(); + this.delegate.println(c); + } + + @Override + public void println(double d) throws IOException { + trackContentLength(d); + trackContentLengthLn(); + this.delegate.println(d); + } + + @Override + public void println(float f) throws IOException { + trackContentLength(f); + trackContentLengthLn(); + this.delegate.println(f); + } + + @Override + public void println(int i) throws IOException { + trackContentLength(i); + trackContentLengthLn(); + this.delegate.println(i); + } + + @Override + public void println(long l) throws IOException { + trackContentLength(l); + trackContentLengthLn(); + this.delegate.println(l); + } + + @Override + public void println(String s) throws IOException { + trackContentLength(s); + trackContentLengthLn(); + this.delegate.println(s); + } + + @Override + public void write(byte[] b) throws IOException { + trackContentLength(b); + this.delegate.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + checkContentLength(len); + this.delegate.write(b, off, len); + } + + @Override + public boolean isReady() { + return this.delegate.isReady(); + } + + @Override + public void setWriteListener(WriteListener writeListener) { + this.delegate.setWriteListener(writeListener); + } + + @Override + public boolean equals(Object obj) { + return this.delegate.equals(obj); + } + + @Override + public int hashCode() { + return this.delegate.hashCode(); + } + + @Override + public String toString() { + return getClass().getName() + "[delegate=" + this.delegate.toString() + "]"; + } + + } + +} diff --git a/src/de/statspez/plausi/astat/csv/Studienfach_Land.csv b/src/de/statspez/plausi/astat/csv/Studienfach_Land.csv index de2fe7a..4ca38d4 100644 --- a/src/de/statspez/plausi/astat/csv/Studienfach_Land.csv +++ b/src/de/statspez/plausi/astat/csv/Studienfach_Land.csv @@ -5825,9 +5825,9 @@ M05;20252;12;0;1332108;99999;Angewandte Psychologie;311;132 254;20062;04;0;0116790;99999;Sachunterricht (einschl.Schulgarten);142;254 255;20072;04;0;0329200;99999;Archiv- und Bibliothekswesen;322;255 256;20072;04;1;0329650;20251;Rechtspflege;380;256 -256;20252;04;1;0329650;20251;Rechtspflege;380;-1 +256;20252;04;1;0329650;99999;Rechtspflege;380;-1 258;20072;04;1;0329150;20251;Arbeitsverwaltung;345;258 -258;20252;04;0;0329150;20251;Arbeitsverwaltung;345; +258;20252;04;0;0329150;99999;Arbeitsverwaltung;345; 261;20062;04;0;0329190;99999;Innere Verwaltung;345;261 262;20072;04;1;0329350;20251;Bibliothekswesen;322;262 262;20252;04;0;0329350;99999;Bibliothekswesen;322;022 @@ -5835,7 +5835,7 @@ M05;20252;12;0;1332108;99999;Angewandte Psychologie;311;132 263;20072;04;0;0329600;99999;Bundeswehr, Justiz und Polizei;861;263 264;20171;04;0;5070359;99999;Arbeits- und Sozialverwaltung, Berufsberatung;343;264 265;20112;04;1;0329300;20251;Bankwesen;343;265 -265;20252;04;0;0329300;20251;Bankwesen;343; +265;20252;04;0;0329300;99999;Bankwesen;343; 266;20062;04;1;0329180;20251;Finanzverwaltung;344;266 266;20252;04;0;0329180;99999;Finanzverwaltung;344;269 268;20072;04;0;0329750;99999;Verkehrswesen;840;268 @@ -6391,7 +6391,7 @@ L40;20201;04;0;0330150;99999;Logistikmanangement (duales Studium);340;021 167;20072;06;0;0330200;20251;Europäische Wirtschaft;340;167 167;20252;06;1;1230200;99999;Europäische Wirtschaft;340;167 168;20072;06;1;0329550;20251;Justizvollzug;861;168 -168;20252;06;1;1129550;20251;Justizvollzug;861;168 +168;20252;06;1;1129550;99999;Justizvollzug;861;168 169;20072;06;0;0104100;99999;Ethik;226;169 170;20072;06;1;0117900;20151;Verhaltensgestörtenpädagogik;141;170 171;20072;06;0;0869200;99999;Vermessungswesen (Geodäsie);581;171 @@ -6483,27 +6483,27 @@ L40;20201;04;0;0330150;99999;Logistikmanangement (duales Studium);340;021 255;20072;06;0;0329200;20251;Archivwesen;322;255 255;20252;06;1;1129150;99999;Archiv- und Bibliothekswesen;322;255 256;20072;06;1;0329650;20251;Rechtspflege;380;256 -256;20252;06;1;1129650;20251;Rechtspflege;380;256 +256;20252;06;1;1129650;99999;Rechtspflege;380;256 257;20072;06;1;0329100;20251;Arbeits- und Berufsberatung;762;257 -257;20252;06;1;1129100;20251;Arbeits- und Berufsberatung;762;257 +257;20252;06;1;1129100;99999;Arbeits- und Berufsberatung;762;257 258;20072;06;1;0329150;20251;Arbeitsverwaltung;345;258 -258;20252;06;1;1129150;20251;Arbeitsverwaltung;345;258 +258;20252;06;1;1129150;99999;Arbeitsverwaltung;345;258 259;20072;06;0;0329250;20251;Auswärtige Angelegenheiten;345;259 259;20252;06;1;1129250;99999;Auswärtige Angelegenheiten;345;259 260;20072;06;1;0329400;20251;Bundeswehrverwaltung;345;260 -260;20252;06;1;1129400;20251;Bundeswehrverwaltung;345;260 +260;20252;06;1;1129400;99999;Bundeswehrverwaltung;345;260 261;20072;06;0;0329500;20251;Innere Verwaltung;345;261 261;20252;06;1;1129500;99999;Innere Verwaltung;345;261 262;20072;06;1;0329350;20251;Bibliothekswesen;322;262 -262;20252;06;1;1129350;20251;Bibliothekswesen;322;262 +262;20252;06;1;1129350;99999;Bibliothekswesen;322;262 263;20072;06;0;0329600;20251;Polizei/Verfassungsschutz;861;263 263;20252;06;1;1129300;99999;Bundeswehr, Justiz und Polizei;861;263 264;20072;06;0;0329700;20251;Sozialversicherung;343;264 264;20252;06;1;1129100;99999;Arbeits- und Sozialverwaltung, Berufsberatung;343;264 265;20072;06;1;0329300;20251;Bankwesen;343;265 -265;20252;06;1;1129300;20251;Bankwesen;343;265 +265;20252;06;1;1129300;99999;Bankwesen;343;265 266;20072;06;1;0329450;20251;Finanzverwaltung;344;266 -266;20252;06;1;1129450;20251;Finanzverwaltung;344;266 +266;20252;06;1;1129450;99999;Finanzverwaltung;344;266 268;20072;06;0;0329750;20251;Verkehrswesen;840;268 268;20252;06;1;1129750;99999;Verkehrswesen;840;268 269;20072;06;0;0329850;20251;Zoll- und Steuerverwaltung;344;269 @@ -7148,29 +7148,29 @@ a51;20252;06;1;1333409;99999;Bildungswissenschaften: Bildung im globalen Technis a52;20112;06;0;0330186;20251;Quant. Economics, Quant. Finance, Quant. Management, Quant. Marketing and Law a. Quant. Economics;340;021 a52;20252;06;1;1230186;99999;Quant. Economics, Quant. Finance, Quant. Management, Quant. Marketing and Law a. Quant. Economics;340;021 a53;20112;06;1;0330187;20122;Quantitative Economics;340;021 -a53;20252;06;1;1230187;20122;Quantitative Economics;340;021 +a53;20252;06;1;1230187;99999;Quantitative Economics;340;021 a54;20112;06;1;0330188;20122;Quantitative Finance;340;021 -a54;20252;06;1;1230188;20122;Quantitative Finance;340;021 +a54;20252;06;1;1230188;99999;Quantitative Finance;340;021 a55;20112;06;1;0330189;20122;Quantitative Management;340;021 -a55;20252;06;1;1230189;20122;Quantitative Management;340;021 +a55;20252;06;1;1230189;99999;Quantitative Management;340;021 a56;20112;06;1;0330190;20122;Quantitative Marketing;340;021 -a56;20252;06;1;1230190;20122;Quantitative Marketing;340;021 +a56;20252;06;1;1230190;99999;Quantitative Marketing;340;021 a57;20112;06;1;0330191;20122;Law and Quantitative Economics;340;021 -a57;20252;06;1;1230191;20122;Law and Quantitative Economics;340;021 +a57;20252;06;1;1230191;99999;Law and Quantitative Economics;340;021 a58;20112;06;0;0868113;99999;Bauingenieurwesen B07M03;582;017 a59;20112;06;0;0438704;20151;Wirtschaftsinformatik B08M02;481;277 a59;20152;06;0;0871704;99999;Wirtschaftsinformatik B08M02;481;277 a60;20112;06;0;0101107;99999;Empirische Sprachwissenschaften B08M02;220;004 a61;20112;06;1;0330192;20122;Ph.D. in Economics;340;021 -a61;20252;06;1;1230192;20122;Ph.D. in Economics;340;021 +a61;20252;06;1;1230192;99999;Ph.D. in Economics;340;021 a62;20112;06;1;0330193;20122;Ph.D. in Finance;340;021 -a62;20252;06;1;1230193;20122;Ph.D. in Finance;340;021 +a62;20252;06;1;1230193;99999;Ph.D. in Finance;340;021 a63;20112;06;1;0330194;20251;Ph.D. in Management;340;021 a63;20252;06;1;1230194;99999;Ph.D. in Management;340;021 a64;20112;06;1;0330195;20122;Ph.D. in Marketing;340;021 -a64;20252;06;1;1230195;20122;Ph.D. in Marketing;340;021 +a64;20252;06;1;1230195;99999;Ph.D. in Marketing;340;021 a65;20112;06;1;0330196;20122;Ph.D. in Law and Economics;340;021 -a65;20252;06;1;1230196;20122;Ph.D. in Law and Economics;340;021 +a65;20252;06;1;1230196;99999;Ph.D. in Law and Economics;340;021 a66;20111;06;0;0107401;99999;Gebärdensprachdolmetschen;220;018 a67;20111;06;0;0116408;20151;Frühpädagogik – Leitung und Management von Kindertageseinrichtungen;142;052 a67;20152;06;0;0333408;20251;Frühpädagogik – Leitung und Management von Kindertageseinrichtungen;142;052 @@ -7392,8 +7392,8 @@ c10;20252;06;1;1128104;99999;Wirtschaftsrecht – Nachhaltigkeit und Ethik;380;1 c11;20142;06;0;0330293;20251;Accounting, Finance, Controlling;340;021 c11;20252;06;1;1230293;99999;Accounting, Finance, Controlling;340;021 c12;20142;06;2;0331210;20151;LifeCycle Catering;621;125 -c12;20152;06;2;0758210;99999;LifeCycle Catering;621;125 -c12;20252;06;1;1231210;20151;LifeCycle Catering;621;125 +c12;20152;06;2;0758210;20251;LifeCycle Catering;621;125 +c12;20252;06;1;1231210;99999;LifeCycle Catering;621;125 c13;20142;06;0;0327308;20251;Systementwicklung Inklusion;760;253 c13;20252;06;1;1327308;99999;Systementwicklung Inklusion;762;253 c14;20142;06;0;0758552;99999;International Wine Business;621;227 @@ -8718,13 +8718,13 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 081;20062;03;0;0113400;20251;Iranistik;222;081 081;20252;03;0;0113400;99999;Iranistik;310;081 082;20062;03;0;0863460;99999;Holz-/Fasertechnik;543;082 -083;20062;03;0;0113450;99999;Islamwissenschaft;222;083 +083;20062;03;0;0113450;20251;Islamwissenschaft;222;083 083;20252;03;0;0113450;99999;Islamwissenschaft;310;083 084;20062;03;0;0111200;99999;Italienisch;222;084 085;20062;03;0;0113500;99999;Japanologie;222;085 086;20062;03;0;0103300;99999;Kath. Theologie, -Religionslehre;221;086 087;20062;03;1;0117500;20151;Körperbehindertenpädagogik;141;087 -088;20062;03;0;0864600;99999;Optoelektronik;524;088 +088;20062;03;0;0864600;20251;Optoelektronik;524;088 088;20252;03;0;0864600;99999;Optoelektronik;523;088 089;20062;03;0;0865500;99999;Verkehrsingenieurwesen;525;089 090;20062;03;1;0101200;20201;Lernbereich Sprach- und Kulturwissenschaften;142;090 @@ -8762,7 +8762,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 115;20252;03;0;1333500;99999;Grundschul-/Primarstufenpädagogik;144;115 116;20062;03;0;0976500;99999;Textilgestaltung;214;116 117;20062;03;1;0116100;20151;Ausländerpädagogik;142;117 -117;20152;03;0;0333100;99999;Ausländerpädagogik;142;117 +117;20152;03;0;0333100;20251;Ausländerpädagogik;142;117 117;20252;03;0;1333100;99999;Ausländerpädagogik;142;117 118;20062;03;0;0437300;99999;Technomathematik;461;118 119;20062;03;0;0109600;99999;Niederländisch;223;119 @@ -8787,7 +8787,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 132;20252;03;0;1332100;99999;Psychologie;311;132 133;20062;03;3;0106400;20081;Medienkunde/Kommunikations-/Informationswissenschaft;321;133 134;20062;03;0;0867100;99999;Raumplanung;581;134 -135;20062;03;0;0328100;99999;Rechtswissenschaft;380;135 +135;20062;03;0;0328100;20251;Rechtswissenschaft;380;135 135;20252;03;0;1128100;99999;Rechtswissenschaft;380;135 136;20062;03;0;0104300;99999;Religionswissenschaft;221;136 137;20062;03;0;0111400;99999;Romanistik (Romanische Philologie, Einzelsprachen a.n.g.);222;137 @@ -8842,11 +8842,11 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 169;20062;03;0;0104100;99999;Ethik;226;169 170;20062;03;1;0117900;20151;Verhaltensgestörtenpädagogik;141;170 171;20062;03;0;0869200;99999;Vermessungswesen (Geodäsie);581;171 -172;20062;03;0;0329800;99999;Verwaltungswissenschaft/-wesen;345;172 +172;20062;03;0;0329800;20251;Verwaltungswissenschaft/-wesen;345;172 172;20252;03;0;1129800;99999;Verwaltungswissenschaft/-wesen;345;172 173;20062;03;0;0114200;99999;Ethnologie;312;173 174;20062;03;0;0114300;99999;Volkskunde;312;174 -175;20062;03;0;0330450;99999;Volkswirtschaftslehre;314;175 +175;20062;03;0;0330450;20251;Volkswirtschaftslehre;314;175 175;20252;03;0;1230450;99999;Volkswirtschaftslehre;314;175 176;20062;03;0;0976600;20251;Werkerziehung;142;176 176;20252;03;0;0976600;99999;Werkerziehung;145;176 @@ -8857,12 +8857,12 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 179;20092;03;0;0331200;20251;Wirtschaftsingenieurwesen mit wirtschaftswissenschaftlichem Schwerpunkt;340;179 179;20252;03;0;1231200;99999;Wirtschaftsingenieurwesen mit wirtschaftswissenschaftlichem Schwerpunkt;340;179 180;20062;03;0;0113550;99999;Kaukasistik;222;180 -181;20062;03;0;0330500;99999;Wirtschaftspädagogik;142;181 +181;20062;03;0;0330500;20251;Wirtschaftspädagogik;142;181 181;20252;03;0;1230500;99999;Wirtschaftspädagogik;145;181 182;20062;03;0;0330250;20251;Intern. Betriebswirtschaft/Management;340;182 182;20252;03;0;1230250;99999;Intern. Betriebswirtschaft/Management;340;182 183;20062;03;0;0105600;99999;Wirtschafts-/Sozialgeschichte;225;183 -184;20062;03;0;0330550;99999;Wirtschaftswissenschaften;340;184 +184;20062;03;0;0330550;20251;Wirtschaftswissenschaften;340;184 184;20252;03;0;1230550;99999;Wirtschaftswissenschaften;340;184 185;20062;03;0;0550100;99999;Zahnmedizin;724;185 186;20062;03;0;0436300;20251;Lernbereich Naturwissenschaft/Sachunterricht;142;186 @@ -8944,7 +8944,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 256;20062;03;1;0329650;20251;Rechtspflege;380;256 257;20062;03;1;0329100;20251;Arbeits- und Berufsberatung;762;257 258;20062;03;1;0329150;20251;Arbeitsverwaltung;345;258 -259;20062;03;0;0329250;99999;Auswärtige Angelegenheiten;345;259 +259;20062;03;0;0329250;20251;Auswärtige Angelegenheiten;345;259 259;20252;03;0;1129250;99999;Auswärtige Angelegenheiten;345;259 260;20062;03;1;0329400;20251;Bundeswehrverwaltung;345;260 261;20062;03;0;0329500;20251;Innere Verwaltung;345;261 @@ -8968,7 +8968,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 271;20252;03;0;0109200;99999;Deutsch als Fremdsprache oder als Zweitsprache;222;271 272;20062;03;0;0105100;99999;Alte Geschichte;225;272 273;20062;03;0;0105400;99999;Mittlere und neuere Geschichte;225;273 -274;20062;03;0;0330350;99999;Tourismuswirtschaft;812;274 +274;20062;03;0;0330350;20251;Tourismuswirtschaft;812;274 274;20252;03;0;1230350;99999;Tourismuswirtschaft;812;274 275;20072;03;1;0436102;20201;Geschichte der Mathematik und Naturwissenschaften;142;275 275;20202;03;2;0105700;99999;Wissenschaftsgeschichte/Technikgeschichte;225;275 @@ -9018,7 +9018,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 361;20152;03;0;0333700;20251;Schulpädagogik;142;361 361;20252;03;0;1333700;99999;Schulpädagogik;142;361 365;20122;03;2;0116450;20151;Pädagogik der frühen Kindheit;142;365 -365;20152;03;1;0333450;99999;Pädagogik der frühen Kindheit;142;365 +365;20152;03;1;0333450;20251;Pädagogik der frühen Kindheit;142;365 365;20252;03;0;1333550;99999;Kindheitspädagogik;142;365 370;20092;03;0;0870100;99999;Wirtschaftsingenieurwesen mit ingenieurwissenschaftlichem Schwerpunkt;520;370 371;20062;03;0;0758500;99999;Tierproduktion;621;371 @@ -9159,19 +9159,19 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 681;20062;03;1;0439201;20121;Regen.Energiequellen(Erg.);441;128 681;20222;03;2;0439500;99999;Quantum Engineering;441;128 682;20062;03;0;0862302;99999;Steine,Erden;521;076 -683;20062;03;0;0323101;99999;Umweltrecht/-ökonomie(WB);142;030 +683;20062;03;0;0323101;20251;Umweltrecht/-ökonomie(WB);142;030 683;20252;03;0;1128102;99999;Nachhaltigkeitsrecht - Energie, Ressourcen, Umwelt;380;135 684;20112;03;0;0101104;20251;Angewandte Kulturwissenschaften-Kulturinformatik(NF);142;004 684;20252;03;0;0101104;99999;Kulturwiss., Angew., ~, Kritische, Kulturanalysen: Repräsentation, Performativität u. Gender, Interk;220;004 -685;20062;03;0;0326201;99999;Sozial-und Kulturgeschichte;312;148 +685;20062;03;0;0326201;20251;Sozial-und Kulturgeschichte;312;148 685;20252;03;0;1326201;99999;Kulturwiss., Ang. -Sozial- u. -Kulturgeschichte-, Sozialwissenschaftliche Diversitätsforschung;312;148 686;20062;03;0;0222201;20251;Spiel-u.Bewegungskultur;813;029 686;20252;03;0;1335201;99999;Kulturwiss., Ang. -Spiel- und -Bewegungskultur;813;029 -687;20062;03;0;0107301;99999;Sprache u. Kommunikation;220;284 +687;20062;03;0;0107301;20251;Sprache u. Kommunikation;220;284 687;20252;03;0;0107301;99999;Kulturwiss., Ang. -Sprache und Kommunikation-, Medientext und Medienübersetzung;223;284 688;20112;03;1;0438302;20151;Umweltinformatik(NF);481;079 688;20152;03;2;0871302;20231;Umweltinformatik(NF);481;079 -688;20232;03;1;0328201;99999;Tax Law - Steuerrecht;380;042 +688;20232;03;1;0328201;20251;Tax Law - Steuerrecht;380;042 688;20252;03;0;1128201;99999;Tax Law - Steuerrecht, Taxation;380;042 689;20062;03;0;0326202;20251;Europäische Studien;312;148 689;20252;03;0;1326202;99999;Europäische Studien, Eur. Integration/Eur. Studies, Euroculture, Europäisches Regieren;312;148 @@ -9179,7 +9179,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 690;20232;03;0;0758301;99999;International horticulture;622;060 691;20062;03;0;0548202;99999;Kosmetologie(Körperpflege);345;232 692;20062;03;1;0760201;99999;Lebensmittelwiss., Agrar- und Lebensmittelwirtschaft, Food Research and Development;814;071 -693;20062;03;0;0548203;99999;Gesundheit;345;232 +693;20062;03;0;0548203;20251;Gesundheit;345;232 693;20252;03;0;0548251;99999;Midwifery;723;550 694;20062;03;1;0866101;99999;Bautechnik, Wirtschaftsinformatik-Bau;581;013 695;20062;03;0;0861301;20251;Farbtechnik und Raumgestaltung (Gestaltungstechnik);142;199 @@ -9194,7 +9194,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 701;20101;03;3;0334200;20201;Internationales Informationsmanagement;321;303 701;20202;03;2;0334200;20251;Internationales Informationsmanagement;321;303 701;20252;03;0;1334200;99999;Informationsmanagement (int.), Informations- und Wissensmanagement, ,Int Informationsmgt-Information;321;303 -702;20062;03;0;0330154;99999;Krankenversicherungsmanagement;340;021 +702;20062;03;0;0330154;20251;Krankenversicherungsmanagement;340;021 702;20252;03;0;1230154;99999;Krankenversicherung/-management;340;021 703;20062;03;1;0757101;99999;Landesentwicklung/Landschaftsentwicklung, European Master in Territorial Development;581;093 706;20062;03;0;0331202;20251;Industrial Business System (IBS );340;179 @@ -9216,24 +9216,24 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 719;20252;03;0;0861201;99999;Hörtechnik u. Audiologie;520;072 721;20062;03;1;0865504;99999;Logistik und Informationsmanagement im Praxisverbund;525;089 722;20072;03;1;0869201;99999;Geoinformatik/-informationswesen, Navigation und Umweltrobotik;581;171 -723;20072;03;0;0330155;99999;Betr.Wirtsch.-Automobilwirtschaft;340;021 +723;20072;03;0;0330155;20251;Betr.Wirtsch.-Automobilwirtschaft;340;021 723;20252;03;0;1230155;99999;Automobilwirtschaft;; -724;20062;03;0;0330552;99999;Organization Studies;340;184 +724;20062;03;0;0330552;20251;Organization Studies;340;184 724;20252;03;0;1230552;99999;Organisation Studies, Performance Leadership, Führung und Organisation, WiPäd u. Personalentwicklung;340;184 725;20062;03;1;0330553;20072;Ergotherapie/Logopädie/Physiotherapie/Medizinalfachberufe;340;184 725;20081;03;1;0548301;20251;Ergotherapie/Logopädie/Physiotherapie/Medizinalfachberufe;722;233 725;20252;03;0;0548301;99999;Ergotherapie, Logopädie, Physiotherapie, Medizinalfachberufe, Berufspäd. u. Mgt. im Rettungsdienst;345;232 -726;20062;03;0;0330156;99999;Reedereilogistik;340;021 +726;20062;03;0;0330156;20251;Reedereilogistik;340;021 726;20252;03;0;1230156;99999;Schiffs- und Reedereimanagement, Maritime Technology and Shipping Management;340;021 727;20062;03;1;0330157;20111;Umwelt-u.Qualitätsmanagement;340;021 -727;20112;03;0;0330557;99999;Umwelt-u.Qualitätsmanagement;340;184 +727;20112;03;0;0330557;20251;Umwelt-u.Qualitätsmanagement;340;184 727;20252;03;0;1230557;99999;Umwelt-u.Qualitätsmanagement;340;184 -728;20062;03;0;0863823;99999;Environmental Engineering (Internat.Umwelt-Verfahrenstechnik),Bio-u.Enviromental Engineering;850;457 +728;20062;03;0;0863823;20251;Environmental Engineering (Internat.Umwelt-Verfahrenstechnik),Bio-u.Enviromental Engineering;850;457 728;20252;03;0;0863823;99999;Bio- und Umwelttechnik, Geoenvironmental Engineering;851;457 729;20062;03;0;0101101;20251;Kognitive Science;142;004 729;20252;03;0;0101101;99999;Cognitive Science;220;004 731;20062;03;1;0109601;99999;Niederlande-Studien;223;119 -732;20062;03;0;0326203;99999;Frauen-u.Geschlechterstudien,Geschlechterforschung;312;148 +732;20062;03;0;0326203;20251;Frauen-u.Geschlechterstudien,Geschlechterforschung;312;148 732;20252;03;0;1326203;99999;Frauen- u. Geschlechterstudien, Geschlechterforschung, Gender Studies;312;148 733;20062;03;0;0101102;20251;Kulturwissenschaftliche Geschlechterstudien;142;004 733;20252;03;0;0101102;99999;Kulturwiss. Geschlechterstudien, Combines Studies, Kultureller Wandel, KuWi: Kritik der Gegenwart - ;220;004 @@ -9243,7 +9243,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 736;20062;03;1;0115102;20151;Kinder-und Jugendlichenpsychotherapie;311;132 736;20152;03;0;0332102;20251;Kinder-und Jugendlichenpsychotherapie;311;132 736;20252;03;0;1332102;99999;Kinder- u. Jugendpsychiatrie/Jugendlichenpsychotherapie;311;132 -737;20072;03;0;0331203;99999;Kreislaufwirtschaft;340;179 +737;20072;03;0;0331203;20251;Kreislaufwirtschaft;340;179 737;20252;03;0;1231203;99999;Kreislaufwirtschaft;340;179 738;20072;03;0;0327304;20252;Supervision u.Organisationsberatung;760;253 738;20252;03;0;1327304;99999;Supervision u.Organisationsberatung;762;253 @@ -9342,7 +9342,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 798;20072;03;0;0863597;99999;Industrieinformatik im Praxisverbund;520;104 799;20062;03;0;0437105;99999;Mathematik und ihre Vermittlung/Elementarmathematik;461;105 800;20062;03;1;0442203;99999;Biologische Diversität u. Ökologie, Biodiversity: Ecology, Evolution, and Conservation;421;026 -801;20062;03;0;0330158;99999;Bildungsmanagement und Schulentwicklung;340;021 +801;20062;03;0;0330158;20251;Bildungsmanagement und Schulentwicklung;340;021 801;20252;03;0;1230158;99999;Bildungsmanagement u. Schulentwicklg, Business Admin. in Educational Management, Bildungs- u. Wissen;340;021 802;20062;03;0;0331212;20251;Wirtschaftsingenieur / Techn. Vertrieb;340;179 802;20252;03;0;1231212;99999;Technischer Vertrieb im Praxisverbund, Management betrieblicher Systeme im Praxisverbund;340;179 diff --git a/src/de/superx/access/DbSqlRestrictionProvider.java b/src/de/superx/access/DbSqlRestrictionProvider.java index 912b7fe..eadc3a8 100644 --- a/src/de/superx/access/DbSqlRestrictionProvider.java +++ b/src/de/superx/access/DbSqlRestrictionProvider.java @@ -1,5 +1,8 @@ package de.superx.access; +import java.util.ArrayList; +import java.util.List; + import javax.sql.DataSource; import org.springframework.beans.factory.InitializingBean; @@ -24,27 +27,37 @@ public class DbSqlRestrictionProvider implements SqlRestrictionProvider, Initial @Override public Select getRestriction(String username, String userrole, String schemaName, String tableName, int orgunitLid) { try { - - String lookupSql = String.format( - "SELECT restriction FROM view_rechte" - + " WHERE user_name = '%s' AND role_name = '%s'" - + " AND schema_name = '%s' AND table_name = '%s'" - + " AND orgunit_lid = '%d';", - username, userrole, schemaName, tableName, Integer.valueOf(orgunitLid)); - - SqlRowSet result = jt.queryForRowSet(lookupSql); - result.beforeFirst(); - String restriction = ""; + String lookupSql = + "SELECT granted_ids, join_column FROM user_dimension_grants" + + " WHERE user_name = ? AND role_name = ?" + + " AND schema_name = ? AND table_name = ?" + + " AND orgunit_lid = ?"; + + SqlRowSet result = jt.queryForRowSet(lookupSql, username, userrole, schemaName, tableName, Integer.valueOf(orgunitLid)); + + List restrictions = new ArrayList<>(); while (result.next()) { - restriction = result.getString("restriction"); + String grantedIds = result.getString("granted_ids"); + String joinColumn = result.getString("join_column"); + + if (grantedIds == null || grantedIds.isBlank()) { + restrictions.add("1 = 0"); + } else { + restrictions.add(String.format("%s IN (%s)", joinColumn, grantedIds)); + } } - + + String whereClause = ""; + if (!restrictions.isEmpty()) { + whereClause = " WHERE " + String.join(" AND ", restrictions); + } + String resultSql = String.format( - "(SELECT * FROM %s.%s %s) AS %s", - schemaName, tableName, restriction, tableName); + "(SELECT * FROM %s.%s%s) AS %s", + schemaName, tableName, whereClause, tableName); - Select select = (Select)CCJSqlParserUtil.parse(resultSql); - return select; + return (Select) CCJSqlParserUtil.parse(resultSql); + } catch (DataAccessException e) { throw new IllegalStateException("Restriction lookup failed", e); } catch (JSQLParserException e) { diff --git a/src/de/superx/access/InlineSelectVisitor.java b/src/de/superx/access/InlineSelectVisitor.java index bccaf70..db0a958 100644 --- a/src/de/superx/access/InlineSelectVisitor.java +++ b/src/de/superx/access/InlineSelectVisitor.java @@ -47,6 +47,11 @@ public class InlineSelectVisitor implements net.sf.jsqlparser.statement.select.S Set cteNames = new HashSet<>(); if (plainSelect.getWithItemsList() != null) { for (WithItem withItem : plainSelect.getWithItemsList()) { + PlainSelect cteSelect = withItem.getSelect().getPlainSelect(); // inner SELECT of the CTE + FromItem innerFrom = cteSelect.getFromItem(); // FROM inside the CTE + if(innerFrom instanceof Table) { + cteSelect.setFromItem(editFromItem((Table)innerFrom)); + } cteNames.add(withItem.getAlias() != null ? withItem.getAlias().getName() : withItem.getAliasName()); diff --git a/src/de/superx/bianalysis/ExcelSheetBuilder.java b/src/de/superx/bianalysis/ExcelSheetBuilder.java index 63a4882..b1a4804 100644 --- a/src/de/superx/bianalysis/ExcelSheetBuilder.java +++ b/src/de/superx/bianalysis/ExcelSheetBuilder.java @@ -369,11 +369,22 @@ public class ExcelSheetBuilder { Column column= this.visibleColumns.get(i); String[] columnHeader = column.header.split("\\|\\|"); boolean isLeftDimensionAttributeColumn = (columnHeader.length == 1 && !columnHeader[0].contains("|"))? true : false; + // sort headers according to headerOrder for top dimension columns + String[] columnHeaderSorted = new String[columnHeader.length]; + for (int ii = 0; !isLeftDimensionAttributeColumn && ii < result.headerOrder.size(); ii++) { + String header = result.headerOrder.get(ii); + for (int iii = 0; iii < result.headerOrder.size(); iii++) { + if (columnHeader[iii].trim().startsWith(header)) { + columnHeaderSorted[ii] = columnHeader[iii]; + continue; + } + } + } for(int j = 0; j < rowSize; j++) { if(isLeftDimensionAttributeColumn) { grid[j][i] = columnHeader[0]; }else { - String header = columnHeader[j]; + String header = columnHeaderSorted[j]; String[] headerValues = header.split("\\|"); grid[j][i] = headerValues[1]; } diff --git a/src/de/superx/bianalysis/ReportDefinition.java b/src/de/superx/bianalysis/ReportDefinition.java index 6bc76a2..35c43f1 100644 --- a/src/de/superx/bianalysis/ReportDefinition.java +++ b/src/de/superx/bianalysis/ReportDefinition.java @@ -9,7 +9,7 @@ import de.superx.bianalysis.service.DbMetaAdapter; public class ReportDefinition { - public List factTableIds; + public Identifier factTableId; public List leftDimensionAttributeIds; public List topDimensionAttributeIds; public List measureIds; @@ -18,7 +18,6 @@ public class ReportDefinition { public boolean hideEmptyColumns; public ReportDefinition() { - this.factTableIds = new ArrayList<>(); this.leftDimensionAttributeIds = new ArrayList<>(); this.topDimensionAttributeIds = new ArrayList<>(); this.measureIds = new ArrayList<>(); @@ -28,7 +27,7 @@ public class ReportDefinition { public ReportDefinition(ReportDefinition definition) { super(); - this.factTableIds = definition.factTableIds; + this.factTableId = definition.factTableId; this.topDimensionAttributeIds = definition.topDimensionAttributeIds; this.measureIds = definition.measureIds; this.filters = definition.filters; diff --git a/src/de/superx/bianalysis/ReportMetadata.java b/src/de/superx/bianalysis/ReportMetadata.java index 26a2e7e..131cc2d 100644 --- a/src/de/superx/bianalysis/ReportMetadata.java +++ b/src/de/superx/bianalysis/ReportMetadata.java @@ -135,9 +135,9 @@ public class ReportMetadata { List leftAttributes = new ArrayList<>(); List rightAttributes = new ArrayList<>(); List filterAttributes = new ArrayList<>(); - List measures2 = dbMetaAdapter.getMeasureMetadata(allMeasures); + List reportMeasures = dbMetaAdapter.getMeasureMetadata(allMeasures); - for (Measure measure : measures2) { + for (Measure measure : reportMeasures) { if(!measure.getFacttableId().equals(secondaryFacttable.factId)) { measure.isNullMeasure = true; } @@ -190,16 +190,29 @@ public class ReportMetadata { } } - ReportMetadata secondaryFacttableMetadata = new ReportMetadata(secondaryFacttable.factTable, leftAttributes, rightAttributes, measures2, filterAttributes, dbMetaAdapter); + ReportMetadata secondaryFacttableMetadata = new ReportMetadata(secondaryFacttable.factTable, leftAttributes, rightAttributes, reportMeasures, filterAttributes, dbMetaAdapter); + secondaryFacttableMetadata.hideEmptyColumns = primaryReportMetadata.hideEmptyColumns; // TODO all values from all facttables secondaryFacttableMetadata.setTopDimensionAttributeValues(dbMetaAdapter, secondaryFacttableMetadata.factTable.getId()); secondaryFacttableMetadata.isOuter = isOuter; factMetadatas.put(secondaryFacttable.factId.composedId, secondaryFacttableMetadata); + + boolean onlyNullAttributes = true; + for (DimensionAttribute attribute : leftAttributes) { + if(!attribute.isNullAttribute()) { + onlyNullAttributes = false; + break; + } + } + if(onlyNullAttributes) { + for (Measure measure : reportMeasures) { + measure.isNullMeasure = true; + } + } } - // set top dimension attribute filter values for (DimensionAttribute attribute : topDimensionAttributes) { DimensionAttribute confAttr = @@ -217,11 +230,22 @@ public class ReportMetadata { } } } + + // TODO workaround to have the correct topDimensionAttributes values in the first + // report metadata + for (DimensionAttribute a : this.topDimensionAttributes) { + for (DimensionAttribute att : primaryReportMetadata.topDimensionAttributes) { + if(a.getId().equals(att.getId())) { + a.setDimensionAttributeValues(att.getDimensionAttributeValues()); + } + } + } return factMetadatas; } public ReportMetadata(ReportMetadata metadata, List secondary, List measureIds) { + this.hideEmptyColumns = metadata.hideEmptyColumns; this.dbMetaAdapter = metadata.dbMetaAdapter; this.factTable = metadata.factTable; this.sachgebiet = metadata.sachgebiet; @@ -255,7 +279,6 @@ public class ReportMetadata { for (DimensionAttribute attribute : this.leftDimensionAttributes) { attribute.setGrouping(true); } - this.hideEmptyColumns = metadata.hideEmptyColumns; this.secondaryFacttables = secondaryFacttables; } @@ -474,21 +497,25 @@ public class ReportMetadata { for (SecondaryFacttable secondaryFacttable: secondaryFacttables) { String label = ""; if(secondaryFacttable.join.equals("left")) { - label+= "Inklusion "; + label+= "Inklusion ⊆ "; } else if(secondaryFacttable.join.equals("inner")) { - label+= "Schnittmenge "; + label+= "Schnittmenge ∩ "; } else if(secondaryFacttable.join.equals("union")) { - label+= "Vereinigung "; + label+= "Vereinigung ∪ "; } - label+= "mit "; label+=(secondaryFacttable.factTable.getCaption()); - label+= " über "; + label+= " über ("; for( int i = 0; i < secondaryFacttable.joinCriterias.size(); i++) { + label+=(secondaryFacttable.joinCriterias.get(i).leadingDimension.getCaption()); + label+= " = "; label+=(secondaryFacttable.joinCriterias.get(i).secondaryDimension.getCaption()); if(i < secondaryFacttable.joinCriterias.size() -1) { label+= " und "; } } + + label+= "); "; + secondaryFacttableList.add(label); } return secondaryFacttableList; diff --git a/src/de/superx/bianalysis/bin/BiAnalysisCLI.java b/src/de/superx/bianalysis/bin/BiAnalysisCLI.java index a85a423..1de4bc6 100644 --- a/src/de/superx/bianalysis/bin/BiAnalysisCLI.java +++ b/src/de/superx/bianalysis/bin/BiAnalysisCLI.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -267,7 +268,7 @@ public class BiAnalysisCLI { private static void validateMetadataAgainstSchema() { SuperXManager.setWEB_INFPfad(PathAndFileUtils.getWebinfPath()); - Path metadataFolder = DbtUtils.getBiAnalysisMetadataFolder(); + Path metadataFolder = DbtUtils.getBiAnalysisMetadataFolder("hisinone"); File directory = metadataFolder.toFile(); File[] files = directory.listFiles(); @@ -335,7 +336,7 @@ public class BiAnalysisCLI { throw new RuntimeException("Error processing validation output", e); } } - + private static void generateWikiMarkdown(CommandLine parsedArgs) throws IOException { SuperXManager.setWEB_INFPfad(PathAndFileUtils.getWebinfPath()); String facttable = parsedArgs.getOptionValue("d"); @@ -348,7 +349,42 @@ public class BiAnalysisCLI { String docDirectory = PathAndFileUtils.getModulePath("biad"); docDirectory = String.join(File.separator, docDirectory, "conf", "his1", "edustore_doc"); + String filepathAttributes = docDirectory + File.separator + "biad_attributes_mediawiki.txt"; + try (PrintWriter writer = new PrintWriter(filepathAttributes, StandardCharsets.UTF_8)) { + writer.println("{| class=\"wikitable sortable\""); + writer.println("! Attributsbezeichnung !! A. Beschreibung !! A. Spalte !! Dimension !! D. Tabelle !! Thema !! Faktentabelle"); + + for (MetaObject obj : importer.getAllMetaObjects()) { + if (!(obj instanceof MetaDimensionAttribute a)) { + continue; + } + + MetaDimension d = a.getDimension(); + MetaFact f = d == null ? null : (MetaFact) d.getFact(); + + MetaDimensionAttribute attributeRef = a.getConfDimAttrRef(); + MetaDimension conformedDimension = d == null ? null : d.getConformedDimension(); + + String attributeLine = """ + |- + | %s || %s || %s || %s || %s || %s || %s + """.formatted( + empty(firstNonNull(a.getCaption(), attributeRef == null ? null : attributeRef.getCaption())), + empty(firstNonNull(a.getDescription(), attributeRef == null ? null : attributeRef.getDescription())), + empty(firstNonNull(a.getDimColumn(), attributeRef == null ? null : attributeRef.getDimColumn())), + empty(firstNonNull(d == null ? null : d.getCaption(), conformedDimension == null ? null : conformedDimension.getCaption())), + empty(firstNonNull(d == null ? null : d.getDimension(), conformedDimension == null ? null : conformedDimension.getDimension())), + empty(f == null ? null : f.getCaption()), + empty(f == null ? null : f.getFacttable()) + ); + + writer.println(attributeLine); + } + + writer.println("|}"); + System.out.println("Written to file " + filepathAttributes); + } for(MetaFact fact : importer.getAllFactTables()) { if("all".equals(facttable) || fact.getFacttable().equals(facttable)) { @@ -408,7 +444,8 @@ public class BiAnalysisCLI { for(MetaDimensionAttribute a : attributes) { String caption = a.getCaption() == null ? a.getConfDimAttrRef().getCaption() : a.getCaption(); - writer.println("*"+caption); + String isHidden = a.isHidden() ? " (Standardmäßig ausgeblendet)" : ""; + writer.println("*"+caption + isHidden); try { String desc = a.getDescription() == null ? a.getConfDimAttrRef().getDescription() : a.getDescription(); if(!desc.equals("null")) { @@ -427,6 +464,10 @@ public class BiAnalysisCLI { } + private static String getEmptyStringIfNull(String value) { + return value == null ? "" : value; + } + private static void generateYmlForJsonFile(CommandLine parsedArgs) { String[] dirs = parsedArgs.getOptionValues("c"); String filename = parsedArgs.getOptionValue("y"); @@ -764,5 +805,14 @@ public class BiAnalysisCLI { public static boolean isNullOrEmpty(List list) { return list == null || list.isEmpty(); } + + private static String firstNonNull(String value, String fallback) { + return value != null ? value : fallback; + } + + private static String empty(String value) { + return value == null ? "" : value; + } + } diff --git a/src/de/superx/bianalysis/jasper/JasperReportJson.java b/src/de/superx/bianalysis/jasper/JasperReportJson.java index 3115add..0273ed4 100644 --- a/src/de/superx/bianalysis/jasper/JasperReportJson.java +++ b/src/de/superx/bianalysis/jasper/JasperReportJson.java @@ -105,6 +105,9 @@ public class JasperReportJson { for (Map cell : cells) { Map newRow = new HashMap<>(); for (Map.Entry entry : cell.entrySet()) { + if(entry.getKey().endsWith("_sorting")) { + break; + } String fieldCaptionWithoutBrackets = fieldCaptionMap.get(entry.getKey()) .replace("[", "_") .replace("]", "_") diff --git a/src/de/superx/bianalysis/metadata/MetadataImporter.java b/src/de/superx/bianalysis/metadata/MetadataImporter.java index 117363b..0432f6f 100644 --- a/src/de/superx/bianalysis/metadata/MetadataImporter.java +++ b/src/de/superx/bianalysis/metadata/MetadataImporter.java @@ -51,7 +51,7 @@ import de.superx.util.PathAndFileUtils; * dimension. * * To learn more about the metadata concept for the BIAnalysis Tool see: - * doc\bi_analysis\report_wizard\metadaten.adoc + * doc\bi_analysis\metadaten.adoc * */ public final class MetadataImporter { @@ -405,7 +405,7 @@ public final class MetadataImporter { } public void updateMetadataForH2Database(DataSource dataSource) throws Exception { - String metaFilesDir = String.join(File.separator, new String[] {"test", "resources", "db", "fixtures", "reportwizard", "metadata"}); + String metaFilesDir = String.join(File.separator, new String[] {"test", "resources", "db", "fixtures", "bianalysis", "metadata"}); deserializeMetadataFromJsonFiles(metaFilesDir); JdbcTemplate jt = new JdbcTemplate(dataSource); String upserts = String.join("\n", getAllUpsertStrings(false).toString()); @@ -471,6 +471,7 @@ public final class MetadataImporter { attribute.setCaption(rs.getString("caption")); attribute.setDimColumn(rs.getString("dim_column")); attribute.setDefaultRelease(DbMetaAdapter.getCustomDefaultReleaseForCurrentDate()); + attribute.setPosition(Integer.valueOf(numAttributes)); // create a new 'on the fly' identifier for the new metadata // attribute diff --git a/src/de/superx/bianalysis/metadata/models/json/MetaDimension.java b/src/de/superx/bianalysis/metadata/models/json/MetaDimension.java index 80a652f..1e9b498 100644 --- a/src/de/superx/bianalysis/metadata/models/json/MetaDimension.java +++ b/src/de/superx/bianalysis/metadata/models/json/MetaDimension.java @@ -140,11 +140,10 @@ public class MetaDimension extends MetaObject { builder = builder.withStringCol("is_historical", String.valueOf(conformedDimension.isHistorical)); } - if(this.isRestricted) { - builder = builder.withStringCol("is_restricted", String.valueOf(isRestricted)); - } else { - builder = builder.withStringCol("is_restricted", String.valueOf(conformedDimension.isRestricted)); - } + //if(this.isRestricted) { + //} else { + // builder = builder.withStringCol("is_restricted", String.valueOf(conformedDimension.isRestricted)); + //} if(this.dimension != null && !this.dimension.isBlank()) { builder = builder.withStringCol("tablename", this.dimension); @@ -153,6 +152,7 @@ public class MetaDimension extends MetaObject { } + builder = builder.withStringCol("is_restricted", String.valueOf(isRestricted)); builder = builder.withIdCol("conformed", this.conformedDimension.id); } diff --git a/src/de/superx/bianalysis/metadata/models/json/MetaDimensionAttribute.java b/src/de/superx/bianalysis/metadata/models/json/MetaDimensionAttribute.java index 109aebe..0d6c48e 100644 --- a/src/de/superx/bianalysis/metadata/models/json/MetaDimensionAttribute.java +++ b/src/de/superx/bianalysis/metadata/models/json/MetaDimensionAttribute.java @@ -140,7 +140,7 @@ public class MetaDimensionAttribute extends MetaObject { String newColumnName = this.caption.toLowerCase() .replaceAll("\\s+", "_") .replaceAll("[^a-z0-9-]", "_"); - builder = builder.withStringCol("columnname", "ind_" + newColumnName); + builder = builder.withStringCol("columnname", "custom_" + newColumnName); } if(confDimAttrRef != null && confDimAttrRef.hierarchicalFilter) { diff --git a/src/de/superx/bianalysis/models/RightParam.java b/src/de/superx/bianalysis/models/RightParam.java index 8922f35..e161c85 100644 --- a/src/de/superx/bianalysis/models/RightParam.java +++ b/src/de/superx/bianalysis/models/RightParam.java @@ -4,7 +4,8 @@ public enum RightParam { TOPIC_AREA("bianalysis.topic_area"), TOPIC("bianalysis.topic"), - ORGUNIT("bianalysis.orgunit"); + ORGUNIT("bianalysis.orgunit"), + PROJECT("bianalysis.project"); private String string; diff --git a/src/de/superx/bianalysis/repository/dto/AttributeDto.java b/src/de/superx/bianalysis/repository/dto/AttributeDto.java index 49ecb8c..9426a7b 100644 --- a/src/de/superx/bianalysis/repository/dto/AttributeDto.java +++ b/src/de/superx/bianalysis/repository/dto/AttributeDto.java @@ -52,7 +52,7 @@ public class AttributeDto extends EntityBase { @Column(value = "sort_order_column") public String sortOrderColumn; - @DynamicFieldType(label="Filter-Auswahl", readOnly = true, visibleInSimplifiedForm = false) + @DynamicFieldType(label="Filter-Auswahl", readOnly = false, visibleInSimplifiedForm = false) @Column(value = "filter_selection") public String filterSelection; @@ -147,7 +147,7 @@ public class AttributeDto extends EntityBase { String newColumnName = caption.toLowerCase() .replaceAll("\\s+", "_") .replaceAll("[^a-z0-9]", "_"); - return "ind_" + newColumnName; + return "custom_" + newColumnName; } diff --git a/src/de/superx/bianalysis/rest/BiAnalysisAdminApi.java b/src/de/superx/bianalysis/rest/BiAnalysisAdminApi.java index cd66222..d490cbb 100644 --- a/src/de/superx/bianalysis/rest/BiAnalysisAdminApi.java +++ b/src/de/superx/bianalysis/rest/BiAnalysisAdminApi.java @@ -16,8 +16,10 @@ import de.superx.bianalysis.models.Dimension; import de.superx.bianalysis.models.FactTable; import de.superx.bianalysis.models.Measure; import de.superx.bianalysis.repository.dto.AttributeDto; +import de.superx.bianalysis.repository.dto.DimensionDto; import de.superx.bianalysis.service.BiAnalysisRightService; import de.superx.bianalysis.service.DbMetaAdapter; +import de.superx.bianalysis.service.IllegalMetaDataOperation; import de.superx.common.NotYetImplementedException; import de.superx.rest.RestControllerBase; @@ -55,7 +57,7 @@ public class BiAnalysisAdminApi extends RestControllerBase { } @RequestMapping(method = RequestMethod.PUT, path = "/attribute-group") - public Identifier saveAttributeGroup(@RequestBody final AttributeDto attributeGroup, @RequestParam(value = "dimension_id") Identifier dimensionId) { + public Identifier saveAttributeGroup(@RequestBody final AttributeDto attributeGroup, @RequestParam(value = "dimension_id") Identifier dimensionId) throws IllegalMetaDataOperation { if(attributeGroup.id != null && attributeGroup.id.composedId != null && !attributeGroup.id.composedId.isBlank()) { dbAdapter.deleteColumnForAttributeGroup(attributeGroup.id, dimensionId); } @@ -75,6 +77,11 @@ public class BiAnalysisAdminApi extends RestControllerBase { return savedAttribute.id; } + @RequestMapping(method = RequestMethod.PUT, path = "/dimension") + public DimensionDto saveDimension(@RequestBody final DimensionDto dimension) { + return dbAdapter.saveExistingDimensionAndOverrideFields(dimension); + } + @RequestMapping(method = RequestMethod.PUT, path = "/attribute") public AttributeDto saveAttribute(@RequestBody final AttributeDto attribute, @RequestParam(value = "dimension_id") Identifier dimensionId) { attribute.isCustom = Boolean.TRUE; @@ -87,7 +94,7 @@ public class BiAnalysisAdminApi extends RestControllerBase { } @RequestMapping(method = RequestMethod.DELETE, path = "/attribute-group") - public void deleteAttributeGroup(@RequestParam(value = "attribute_group_id") Identifier attributeGroupId) { + public void deleteAttributeGroup(@RequestParam(value = "attribute_group_id") Identifier attributeGroupId) throws IllegalMetaDataOperation { dbAdapter.deleteAttributeGroupById(attributeGroupId); } @@ -107,20 +114,22 @@ public class BiAnalysisAdminApi extends RestControllerBase { @RequestMapping(method = RequestMethod.GET, path = "/filterDimensionAttributeValues") public List listAttributeValues (@RequestParam(value = "attribute_id") Identifier id, @RequestParam(value = "q") String q, - @RequestParam(value = "l") int length) { - return dbAdapter.getFilteredValuesForAttribute(id, q, length) + // l is not used because its fix to 100 values in sys component + @RequestParam(value = "l") int limit, + @RequestParam(value = "count") int count) { + return dbAdapter.getFilteredValuesForAttribute(id, q, count) .stream() .map(DimensionAttributeLabel::new) .toList(); } @RequestMapping(method = RequestMethod.PUT, path = "/hideAttribute") - public void hideAttribute (@RequestBody final Boolean hide, @RequestParam(value = "attribute_id") Identifier id) { + public void hideAttribute (@RequestBody final Boolean hide, @RequestParam(value = "attribute_id") Identifier id) throws IllegalMetaDataOperation { dbAdapter.hideAttributeById(id, hide); } @RequestMapping(method = RequestMethod.PUT, path = "/hideDimension") - public void hideDimension (@RequestBody final Boolean hide, @RequestParam(value = "dimension_id") Identifier id) { + public void hideDimension (@RequestBody final Boolean hide, @RequestParam(value = "dimension_id") Identifier id) throws IllegalMetaDataOperation { dbAdapter.hideDimensionById(id, hide); } diff --git a/src/de/superx/bianalysis/rest/BiAnalysisApi.java b/src/de/superx/bianalysis/rest/BiAnalysisApi.java index 8a78eac..bd13046 100644 --- a/src/de/superx/bianalysis/rest/BiAnalysisApi.java +++ b/src/de/superx/bianalysis/rest/BiAnalysisApi.java @@ -40,7 +40,7 @@ import de.superx.rest.model.ResultType; import de.superx.rest.model.Row; @RestController -@RequestMapping("/api/reportwizard") +@RequestMapping("/api/bianalysis") public class BiAnalysisApi extends RestControllerBase { /* Autor: Robin Wübbeling @@ -110,7 +110,7 @@ public class BiAnalysisApi extends RestControllerBase { @RequestParam(value = "title") Optional title, @RequestParam(value = "sach") Optional sach, @RequestParam(value = "facts") Optional> facts) { - rightsService.checkCreateRights(); + rightsService.checkCreateOrViewRights(); return dbAdapter.findStoredReports(title, sach, facts); } @@ -119,7 +119,7 @@ public class BiAnalysisApi extends RestControllerBase { Optional storedReportOpt = dbAdapter.findById(id); if(storedReportOpt.isPresent()) { StoredReport storedReport = storedReportOpt.get(); - checkCreateOrViewRightForFactTables(storedReport.reportDefinition.factTableIds); + checkCreateOrViewRightForLeadingFactTable(storedReport.reportDefinition.factTableId); try { storedReport.exportedResult = biAnalysisManager.createResult(storedReport.reportDefinition, dbAdapter); } catch (Exception e) { @@ -134,22 +134,22 @@ public class BiAnalysisApi extends RestControllerBase { @RequestMapping(method = RequestMethod.POST, path = "/report") public Result getReport(@RequestBody final ReportDefinition reportDefinition) throws Exception { - List factTableIds = reportDefinition.factTableIds; - checkCreateRightForFactTables(factTableIds); + Identifier factTableId = reportDefinition.factTableId; + checkCreateRightForLeadingFactTable(factTableId); return biAnalysisManager.createResult(reportDefinition, dbAdapter); } @RequestMapping(method = RequestMethod.POST, path = "/persistReportDefinition") public int persistReportDefinition(@RequestBody final StoredReport storedReport) throws Exception { StoredReport.setReportDefinitionJson(storedReport); - List factTableIds = storedReport.reportDefinition.factTableIds; - checkCreateRightForFactTables(factTableIds); + Identifier factTableId = (Identifier) storedReport.reportDefinition.factTableId; + checkCreateRightForLeadingFactTable(factTableId); return dbAdapter.saveReportDefinition(storedReport); } @RequestMapping(value = "/report/download", method = RequestMethod.POST) public Download getFile(@RequestBody final StoredReport storedReport) throws Exception { - checkCreateOrViewRightForFactTables(storedReport.reportDefinition.factTableIds); + checkCreateOrViewRightForLeadingFactTable(storedReport.reportDefinition.factTableId); Date date = new Date(); String fileName = "BI-Analyse_"; if(storedReport.id != 0) { @@ -185,7 +185,7 @@ public class BiAnalysisApi extends RestControllerBase { if(reportOpt.isEmpty()) { throw new Exception("FEHLER: Berichtskonfiguration konnte nicht gefunden werden."); } - checkCreateRightForFactTables(reportOpt.get().reportDefinition.factTableIds); + checkCreateRightForLeadingFactTable(reportOpt.get().reportDefinition.factTableId); rightsService.checkDeleteRights(); dbAdapter.deleteById(id); return true; @@ -211,20 +211,16 @@ public class BiAnalysisApi extends RestControllerBase { return storedReports; } - private void checkCreateOrViewRightForFactTables(List factTableIds) { - for (Identifier factId : factTableIds) { - int sachgebiet = dbAdapter.getSachgebietByFactTableId(factId.composedId); - rightsService.checkSachgebiet(sachgebiet, Right.CREATE_ANALYSIS, Right.VIEW_REPORT); - rightsService.checkFactTable(factId, Right.CREATE_ANALYSIS, Right.VIEW_REPORT); - } + private void checkCreateOrViewRightForLeadingFactTable(Identifier factTableId) { + int sachgebiet = dbAdapter.getSachgebietByFactTableId(factTableId.composedId); + rightsService.checkSachgebiet(sachgebiet, Right.CREATE_ANALYSIS, Right.VIEW_REPORT); + rightsService.checkFactTable(factTableId, Right.CREATE_ANALYSIS, Right.VIEW_REPORT); } - private void checkCreateRightForFactTables(List factTableIds) { - for (Identifier factId : factTableIds) { - int sachgebiet = dbAdapter.getSachgebietByFactTableId(factId.composedId); - rightsService.checkSachgebiet(sachgebiet, Right.CREATE_ANALYSIS); - rightsService.checkFactTable(factId, Right.CREATE_ANALYSIS); - } + private void checkCreateRightForLeadingFactTable(Identifier factTableId) { + int sachgebiet = dbAdapter.getSachgebietByFactTableId(factTableId.composedId); + rightsService.checkSachgebiet(sachgebiet, Right.CREATE_ANALYSIS); + rightsService.checkFactTable(factTableId, Right.CREATE_ANALYSIS); } diff --git a/src/de/superx/bianalysis/service/BiAnalysisManager.java b/src/de/superx/bianalysis/service/BiAnalysisManager.java index 4a42661..2bcbac7 100644 --- a/src/de/superx/bianalysis/service/BiAnalysisManager.java +++ b/src/de/superx/bianalysis/service/BiAnalysisManager.java @@ -52,13 +52,13 @@ public class BiAnalysisManager { public Result createResult(ReportDefinition reportDefinition, DbMetaAdapter dbAdapter) throws Exception { - if(reportDefinition.factTableIds.size() > 1) { - throw new IllegalArgumentException("Only one facttable per report definition allowed."); + if(reportDefinition.leftDimensionAttributeIds.isEmpty()) { + throw new IllegalMetaDataOperation("Bei einer BI-Analyse muss mindestens ein Zeilenattribut angegeben werden."); } try { biAnalysisRightService.checkCreateOrViewRights(); - ReportMetadata metadata = new ReportMetadata(reportDefinition, reportDefinition.factTableIds.get(0), dbAdapter); + ReportMetadata metadata = new ReportMetadata(reportDefinition, reportDefinition.factTableId, dbAdapter); checkColLimit(reportDefinition, dbAdapter, metadata); Result result = getReportData(metadata, dbAdapter, reportDefinition); return result; @@ -80,8 +80,8 @@ public class BiAnalysisManager { } } - private Result getReportData(ReportMetadata metadata, DbMetaAdapter dbAdapter, ReportDefinition definition) throws Exception { - String reportSql = getSqlStatement(definition, dbAdapter); + public Result getReportData(ReportMetadata metadata, DbMetaAdapter dbAdapter, ReportDefinition definition) throws Exception { + String reportSql = getSqlStatement(metadata); String totalsColumnSql = getTotalsColumnSqlStatement(definition, dbAdapter); List sqlStatements = new ArrayList<>(); @@ -138,11 +138,11 @@ public class BiAnalysisManager { return ""; } String sql = ""; - ReportMetadata metadata = new ReportMetadata(definition, definition.factTableIds.get(0), dbAdapter); + ReportMetadata metadata = new ReportMetadata(definition, definition.factTableId, dbAdapter); if(metadata.hasOuter()) { ReportDefinition definitionTotals = definition.toTotalsDefinition(); - metadata = new ReportMetadata(definitionTotals, definitionTotals.factTableIds.get(0), dbAdapter); + metadata = new ReportMetadata(definitionTotals, definitionTotals.factTableId, dbAdapter); HashMap metadatas = metadata.buildReportMetadatasForDrillAcross(); SQLGeneratorDrillAcross sqlGeneratorDrillAcross @@ -155,15 +155,14 @@ public class BiAnalysisManager { return sql; } - public static String getSqlStatement(ReportDefinition definition, DbMetaAdapter dbAdapter) { + public static String getSqlStatement(ReportMetadata metadata) { String sql = ""; - ReportMetadata metadata = definition.getReportMetadata(dbAdapter, definition.factTableIds.get(0)); if(metadata.hasOuter()) { HashMap metadatas = metadata.buildReportMetadatasForDrillAcross(); SQLGeneratorDrillAcross sqlGeneratorDrillAcross - = new SQLGeneratorDrillAcross(definition.factTableIds.get(0), metadatas); + = new SQLGeneratorDrillAcross(metadata.factTable.getId(), metadatas); sql = sqlGeneratorDrillAcross.buildFormattedSqlStatement(); } else { diff --git a/src/de/superx/bianalysis/service/DbMetaAdapter.java b/src/de/superx/bianalysis/service/DbMetaAdapter.java index 27ae963..c874b2c 100644 --- a/src/de/superx/bianalysis/service/DbMetaAdapter.java +++ b/src/de/superx/bianalysis/service/DbMetaAdapter.java @@ -1,1470 +1,1564 @@ -package de.superx.bianalysis.service; - -import java.sql.Array; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Date; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.StringJoiner; -import java.util.stream.Collectors; - -import javax.sql.DataSource; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.dao.IncorrectResultSizeDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.PreparedStatementCreator; -import org.springframework.jdbc.core.PreparedStatementSetter; -import org.springframework.jdbc.support.rowset.SqlRowSet; -import org.springframework.stereotype.Service; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; - -import de.superx.access.DbSqlRestrictionProvider; -import de.superx.access.SqlAccessRestrictor; -import de.superx.bianalysis.FaultyMetadataException; -import de.superx.bianalysis.JoinCriteria; -import de.superx.bianalysis.ReportDefinition; -import de.superx.bianalysis.SecondaryFacttable; -import de.superx.bianalysis.StoredReport; -import de.superx.bianalysis.metadata.Identifier; -import de.superx.bianalysis.models.Condition; -import de.superx.bianalysis.models.CriteriaGroup; -import de.superx.bianalysis.models.Dimension; -import de.superx.bianalysis.models.DimensionAttribute; -import de.superx.bianalysis.models.FactTable; -import de.superx.bianalysis.models.Filter; -import de.superx.bianalysis.models.Measure; -import de.superx.bianalysis.repository.DimensionAttributeRepository; -import de.superx.bianalysis.repository.DimensionRepository; -import de.superx.bianalysis.repository.MeasureFilterRepository; -import de.superx.bianalysis.repository.MeasureRepository; -import de.superx.bianalysis.repository.StoredReportRepository; -import de.superx.bianalysis.repository.dto.AttributeDto; -import de.superx.bianalysis.repository.dto.DimensionDto; -import de.superx.bianalysis.repository.dto.FactDto; -import de.superx.bianalysis.repository.dto.MeasureDto; -import de.superx.bianalysis.repository.dto.MeasureFilterDto; -import de.superx.bianalysis.sqlgeneration.AttributeGroupQueryBuilder; -import de.superx.bianalysis.sqlgeneration.SqlQuery; -import de.superx.common.NotYetImplementedException; -import de.superx.common.SxUser; -import de.superx.jdbc.entity.Sachgebiet; -import de.superx.jdbc.entity.Systeminfo; -import de.superx.jdbc.repository.SachgebieteRepository; -import de.superx.jdbc.repository.SysteminfoRepository; -import de.superx.rest.model.Item; -import de.superx.spring.service.UserService; - -@Service() -public class DbMetaAdapter implements InitializingBean { - -// @Autowired -// FactRepository factRepository; - - @Autowired - MeasureRepository measureRepository; - - @Autowired - DimensionRepository dimensionRepository; - - @Autowired - DimensionAttributeRepository dimensionAttrRepo; - - @Autowired - MeasureFilterRepository measureFilterRepo; - - @Autowired - StoredReportRepository storedReportRepository; - - @Autowired - SachgebieteRepository sachgebieterepository; - - @Autowired - SysteminfoRepository systeminfoRepository; - - @Autowired - UserService userService; - - @Autowired - DataSource dataSource; - - @Autowired - DbSqlRestrictionProvider sqlRestrictionProvider; - - private static final ObjectMapper MAPPER = new ObjectMapper(); - - private JdbcTemplate jt; - - @Override - public void afterPropertiesSet() throws Exception { - this.jt = new JdbcTemplate(dataSource); - } - - private List getFactDtos(String sql) { - List factDtos = new ArrayList(); - SqlRowSet result = jt.queryForRowSet(sql); - result.beforeFirst(); - while (result.next()) { - FactDto fd = new FactDto(); - fd.id = new Identifier(result.getString("id")); - fd.caption = result.getString("caption"); - fd.tablename = result.getString("tablename"); - fd.defaultRelease = result.getString("default_release"); - fd.isCustom = Boolean.valueOf(result.getBoolean("is_custom")); - fd.sachgebiettid = Integer.valueOf(result.getInt("sachgebiettid")); - fd.description = result.getString("description"); - factDtos.add(fd); - } - return factDtos; - } - - public List getFactTables() throws NotYetImplementedException { - try { - String sql = "SELECT * FROM metadata.facttable;"; - sql = applyAccessRestrictions(sql); - List fdtos = getFactDtos(sql); - List factTables = fdtos - .stream() - .map(f -> { - FactTable fact = new FactTable(f); - fact.setSachgebiet(getSachgebietForFacttable(f.sachgebiettid.intValue())); - return fact; - }) - .collect(Collectors.toList()); - return factTables; - } catch (Exception e) { - - e.printStackTrace(); - if (e.getCause().getMessage().contains("FEHLER: Relation »metadata.facttable« existiert nicht")) { - throw new NotYetImplementedException("Bitte installieren Sie zuerst die Komponente 'BI-Analyse-Daten' und führen Sie anschließend den Konnektor aus."); - } - throw e; - } - } - - public String applyAccessRestrictions(String sql) { - if ( SqlAccessRestrictor.rechteModus == SqlAccessRestrictor.Rechtekonzept.INLINE_SQL) { - try { - SxUser user = (SxUser)userService.currentUserDetails(); - String userName = user.getName(); - String roleName = user.getRole(); - int orgunitLid = user.getHisInOneOrgUnitLidOfRole().intValue(); SqlAccessRestrictor accessRestrictor = new SqlAccessRestrictor(this.sqlRestrictionProvider, userName, roleName, orgunitLid); - sql = accessRestrictor.restrictSql(sql); - } catch (Exception e) { - e.printStackTrace(); - } - } - return sql; - } - - private boolean hasSachgebietTopicRestrictions(List facts, int tid) { - for (Identifier factId : facts) { - if(getFactTable(factId).getSachgebiettid() == tid) { - return true; - } - } - return false; - } - - - - public DimensionAttribute getDimensionAttributeMetadata(Identifier id) { - Optional optAttribute = dimensionAttrRepo.findById(id); - if(optAttribute.isEmpty()) { - throw new FaultyMetadataException(id, "Attribute"); - } - - DimensionAttribute attr = new DimensionAttribute(optAttribute.get()); - Dimension dim = getDimension(attr.getDimensionId()); - - attr.setDimension(dim); - - if(attr.getAttrConformedId() != null) { - Optional optAttributeConf = dimensionAttrRepo.findById(new Identifier(attr.getAttrConformedId())); - if(optAttributeConf.isPresent()) { - Dimension dimConf = getDimension(optAttributeConf.get().dimensionId); - attr.setDimConformedId(dimConf.getId().composedId); - } - } - return attr; - } - - public List getDimensionAttributeMetadata(List attributeIds, Identifier factId) { - - if (attributeIds == null || attributeIds.size() <= 0 ) { - return null; - } - - List result = new ArrayList(); - for (Identifier id : attributeIds) { - Optional optAttribute = dimensionAttrRepo.findById(id); - if(optAttribute.isEmpty()) { - throw new FaultyMetadataException(id, "Attribute"); - } - - DimensionAttribute attr = new DimensionAttribute(optAttribute.get()); - Dimension dim = getDimension(attr.getDimensionId()); - - attr.setDimension(dim); - - if(attr.getAttrConformedId() != null) { - Optional optAttributeConf = dimensionAttrRepo.findById(new Identifier(attr.getAttrConformedId())); - if(optAttributeConf.isPresent()) { - Dimension dimConf = getDimension(optAttributeConf.get().dimensionId); - attr.setDimConformedId(dimConf.getId().composedId); - } - } - result.add(attr); - } - return result; - } - - public List getMeasureMetadata(List measureIds) { - if (measureIds != null && measureIds.size() > 0 ) { - List result = new ArrayList(); - for (Identifier id : measureIds) { - Measure measure = getMeasure(id); - FactTable factTable = getFactTable(measure.getFacttableId()); - measure.factTableName = factTable.getTablename(); - if(measure.getMeasureFilterId() != null) { - MeasureFilterDto filter = measureFilterRepo.findById(measure.getMeasureFilterId()).get(); - if(filter.dimensionAttributeId != null) { - DimensionAttribute attribute = getDimensionAttributeById(filter.dimensionAttributeId); - Dimension dimension = getDimension(attribute.getDimensionId()); - measure.setMeasureFilterAttributes(filter, attribute, dimension); - } else if(filter.factColumnFilter != null) { - measure.setFactColumnFilter(filter); - } - } - result.add(measure); - } - return result; - } - return null; - } - - - public List getDimensionsWithoutHidden(Identifier factTableId){ - - List dimensions = new ArrayList<>(); - for (Dimension dimension : getDimensions(factTableId)) { - if(dimension.isHidden()) { - continue; - } - dimensions.add(dimension); - - List attributes = new ArrayList<>(); - for (DimensionAttribute attribute : dimension.getDimensionAttributes()) { - if(!attribute.isHidden()) { - attributes.add(attribute); - } - } - dimension.setDimensionAttributes(attributes); - } - - return dimensions; - } - - - - public List getDimensions(Identifier factTableId) { - List dimensions = new ArrayList<>(); - - for (Dimension dimension : findByFactTableId(factTableId)) { - dimensions.add(dimension); - - List attr = dimensionAttrRepo.findByDimensionId(dimension.getId()); - List attributes = attr.stream() - .map(a -> new DimensionAttribute(a)) - .collect(Collectors.toList()); - - attributes.sort(Comparator.comparing( - DimensionAttribute::getPosition, - Comparator.nullsLast(Integer::compareTo) - )); - - dimension.setDimensionAttributes(attributes); - for(DimensionAttribute a : attributes) { - a.setDimension(dimension); - } - } - - return dimensions; - } - - private Optional getFactDtoById(Identifier id) { - String sql = String.format("SELECT * FROM metadata.facttable WHERE id = '%s';", - id.toString()); - sql = applyAccessRestrictions(sql); - List fdtos = getFactDtos(sql); - Optional factOpt = Optional.ofNullable(null); - if (fdtos.size() > 0) { - factOpt = Optional.ofNullable(fdtos.get(0)); - } - return factOpt; - } - - public List getDimensionAttributeValues(List attributes, List factTables) { - List result = new ArrayList(); - List tables = new ArrayList(); - for (DimensionAttribute attr : attributes) { - if(tables.contains(attr.getTablename())) { - continue; - } - tables.add(attr.getTablename()); - for(Identifier factId : factTables) { - result.addAll(getValuesOfAttribute(attr, factId, null, null)); - } - } - return result; - } - - public List getValuesOfAttribute(DimensionAttribute attr, Identifier factId, List filtersInDimension, Boolean hideEmptyColumns) { - FactTable fact = getFactTable(factId); - Dimension dim = getDimension(attr.getDimensionId()); - List values = getDimensionAttributeValues(attr, dim, fact, filtersInDimension, hideEmptyColumns); - return values; - } - - public List getFilterMetadata(List filters) { - for (Filter filter : filters) { - DimensionAttribute attr = getDimensionAttributeById(filter.dimensionAttributeId); - Dimension dim = getDimension(attr.getDimensionId()); - attr.setDimension(dim); - filter.setDimension(dim); - filter.setDimensionAttribute(attr); - } - return filters; - } - - public Filter setFilterMetadata(Filter filter) { - DimensionAttribute attr = getDimensionAttributeById(filter.dimensionAttributeId); - Dimension dim = getDimension(attr.getDimensionId()); - attr.setDimension(dim); - filter.setDimension(dim); - filter.setDimensionAttribute(attr); - return filter; - } - - public List getDimensionAttributeValues( - DimensionAttribute attr, Dimension dim, FactTable factTable, - List filtersInDimension, Boolean hideEmptyColumns) { - String sortOrderColumnName = attr.getSortOrderColumn() != null ? attr.getSortOrderColumn(): attr.getColumnname(); - - String templateSql = - "SELECT DISTINCT d.%s AS value, d.%s, " + - "array_position(array[%s], d.%s::text) " + - "FROM presentation.%s d"; - - /// default - if((factTable != null && attr.getFilterSelection() == null - || (dim != null && attr.getFilterSelection().equals("show_existing_only"))) && hideEmptyColumns == null - || hideEmptyColumns != null && hideEmptyColumns.booleanValue() == true) { - - String idColumn = "id"; - if(attr.getDimIdJoinColumn() != null && !attr.getDimIdJoinColumn().isBlank()) { - idColumn = attr.getDimIdJoinColumn(); - } - - templateSql += " INNER JOIN presentation." + factTable.getTablename() - + " f ON d." + idColumn + " = f." + dim.getJoincolumn(); - } - - templateSql += " WHERE d.%s IS NOT NULL "; - - if(dim != null - && attr.getFilterSelection() != null - && attr.getFilterSelection().equals("show_range")) { - - String idColumn = "id"; - if(attr.getDimIdJoinColumn() != null && !attr.getDimIdJoinColumn().isBlank()) { - idColumn = attr.getDimIdJoinColumn(); - } - - templateSql += " AND d." + idColumn + " BETWEEN (" - + "SELECT MIN(" + dim.getJoincolumn() + ")" - + " FROM presentation." + factTable.getTablename() - + ") AND (" - + "SELECT MAX(" + dim.getJoincolumn() - + " FROM presentation." + factTable.getTablename() + ")"; - } - - if(filtersInDimension != null) { - boolean first = true; - for (Filter filter : filtersInDimension) { - templateSql += " AND "; - first = true; - for( String filterValue : filter.filterValues) { - if(first) { - first = false; - } else { - templateSql += " OR "; - } - templateSql += " d." + filter.columnname + " = " + "'" + filterValue + "'"; - } - } - } - - // TODO: sometimes we need DESC - templateSql += " ORDER BY 3, 2 ASC;"; - String query = String.format( - templateSql, - attr.getColumnname(), - sortOrderColumnName, - DimensionAttribute.specialValueListForSql(), - attr.getColumnname(), - attr.getTablename(), - attr.getColumnname() - ); - - List values = jt.query(query, (rs, rowNum) -> rs.getString("value")) - .stream().distinct().collect(Collectors.toList()); - - return values; - } - - public DimensionAttribute getDimensionAttributeMetadataById(Identifier id) { - DimensionAttribute attr = getDimensionAttributeById(id); - Dimension dim = getDimension(attr.getDimensionId()); - attr.setDimension(dim); - if(dim.getConformed() != null && !dim.getConformed().isBlank()) { - Dimension confDim = getDimension(new Identifier(dim.getConformed())); - attr.setDimConformedId(confDim.getId().composedId); - } - return attr; - } - - public Sachgebiet getSachgebietById(int sachgebietId) { - return this.sachgebieterepository.findById(Integer.valueOf(sachgebietId)).get(); - } - - public int saveReportDefinition(StoredReport report) { - StoredReport savedStoredReport = this.storedReportRepository.save(report); - return savedStoredReport.id; - } - - public int getColNumbers(List topDimensionAttributes, List filters) { - JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); - int num = 1; - for (DimensionAttribute dimensionAttribute : topDimensionAttributes) { - if(filters != null && filters.size() > 0) { - Filter filter = Filter.findFilterById(filters, dimensionAttribute.getId()); - if(filter != null && filter.filterValues.size() > 0) { - num *= filter.filterValues.size(); - continue; - } - } - String sql = String.format("SELECT count(DISTINCT %s) FROM presentation.%s WHERE %s IS NOT NULL", dimensionAttribute.getColumnname(), dimensionAttribute.getTablename(), dimensionAttribute.getColumnname()); - int value = jdbcTemplate.queryForObject(sql, new Object[] {}, Integer.class).intValue(); - num *= value; - } - return num; - } - - public String getLastUpdate(int tid) { - Optional systeminfoOpt = systeminfoRepository.findById(Integer.valueOf(tid)); - if(systeminfoOpt.isPresent()) { - Date lastUpdate = systeminfoOpt.get().datum; - return new SimpleDateFormat("dd.MM.yyyy hh:mm").format(lastUpdate); - } - return "Unknown"; - } - - public DataSource getDataSource() { - return this.dataSource; - } - - public int getSachgebietByFactTableId(String factTableId) { - Optional fact = getFactDtoById(new Identifier(factTableId)); - if(fact.isPresent()) { - return fact.get().sachgebiettid.intValue(); - } - return -1; - } - - public List getSachgebieteForReport(ReportDefinition reportDefinition) { - List result = new ArrayList<>(); - for (Identifier factTableId : reportDefinition.factTableIds) { - int sachgebiet = getSachgebietByFactTableId(factTableId.composedId); - result.add(Integer.valueOf(sachgebiet)); - } - return result; - } - - public List findAllStoredReports(){ - List reports = new ArrayList<>(); - for (StoredReport report : this.storedReportRepository.findAll()) { - StoredReport.setReportDefinitionFromJson(report); - reports.add(report); - } - return reports; - } - - public Optional findById(int id) { - Optional report = storedReportRepository.findById(id); - if(report.isPresent()) { - StoredReport.setReportDefinitionFromJson(report.get()); - } - return report; - } - - public void deleteById(int id) { - storedReportRepository.deleteById(id); - } - - private Sachgebiet getSachgebietForFacttable(int sachgebiettid) { - Optional sachgebiet = sachgebieterepository - .findById(Integer.valueOf(sachgebiettid)); - if(sachgebiet.isPresent()) { - return sachgebiet.get(); - } - return new Sachgebiet(Integer.valueOf(-1), "Unknown", null); - } - - public List getSachgebieteForFactTables(List rightParamValues) { - List result = new ArrayList<>(); - for (String id : rightParamValues) { - int sachgebiet = getSachgebietByFactTableId(id); - if(sachgebiet != -1) { - result.add(String.valueOf(sachgebiet)); - } - } - return result; - } - - public List getAttributesOfDimension(Identifier dimId){ - List attr = dimensionAttrRepo.findByDimensionId(dimId); - List attributes = attr.stream() - .map(a -> new DimensionAttribute(a)) - .collect(Collectors.toList()); - - attributes.sort(Comparator.comparing( - DimensionAttribute::getPosition, - Comparator.nullsLast(Integer::compareTo) - )); - - return attributes; - } - - public Dimension getDimension(Identifier id) { - Optional dimOpt = dimensionRepository.findById(id); - if(dimOpt.isEmpty()) { - throw new FaultyMetadataException(id, "Dimension"); - } - Dimension dimension = new Dimension(dimOpt.get()); - return dimension; - } - - public Measure getMeasure(Identifier id) { - Optional measureOpt = measureRepository.findById(id); - if(measureOpt.isEmpty()) { - throw new FaultyMetadataException(id, "Measure"); - } - Measure measure = new Measure(measureOpt.get()); - return measure; - } - - - public List findByFactTableId(Identifier factTableId) { - ObjectMapper mapper = new ObjectMapper(); - String sql = """ - WITH join_partners AS ( - SELECT - conformed AS conf, - json_agg(json_build_object('id', id, 'caption', caption, 'factTableId', facttable_id)) AS role_playing_dimension - FROM metadata.dimension d WHERE conformed IS NOT NULL - AND facttable_id != ? - GROUP BY conformed - ) - SELECT d.id, d.caption, d.description, d.facttable_id, d.tablename, d.joincolumn, d.alias, - d.is_historical, d.conformed, d.id_column, d.default_release, - d.is_hidden, d.position, d.is_custom, join_partners.role_playing_dimension - FROM metadata.dimension d - LEFT JOIN join_partners ON join_partners.conf = d.conformed - WHERE d.facttable_id = ? - ORDER BY position - """; - - sql = applyAccessRestrictions(sql); - - List dimensions = new ArrayList<>(); - - SqlRowSet result = jt.queryForRowSet(sql, factTableId.composedId, factTableId.composedId); - result.beforeFirst(); - while (result.next()) { - Dimension dimension = new Dimension(); - DimensionDto dto = new DimensionDto(); - dto.id = new Identifier(result.getString("id")); - dto.caption = result.getString("caption"); - dto.description = result.getString("description"); - dto.factTableId = new Identifier(result.getString("facttable_id")); - dto.tablename = result.getString("tablename"); - dto.joincolumn = result.getString("joincolumn"); - dto.alias = result.getString("alias"); - dto.isHistorical = Boolean.valueOf(result.getBoolean("is_historical")); - dto.conformed = result.getString("conformed"); - dto.idColumn = result.getString("id_column"); - dto.defaultRelease = result.getString("default_release"); - dto.isCustom = Boolean.valueOf(result.getBoolean("is_custom")); - dto.isHidden = Boolean.valueOf(result.getBoolean("is_hidden")); - dto.position = Integer.valueOf(result.getInt("position")); - - // Handle JSON array for rolePlayingDimensions - String jsonArray = result.getString("role_playing_dimension"); - if (jsonArray != null && !jsonArray.isEmpty() && !"null".equals(jsonArray)) { - try { - List rolePlayingDimensions = - mapper.readValue(jsonArray, new TypeReference>() {}); - dimension.setRolePlayingDimensions(rolePlayingDimensions); - } catch (Exception e) { - // Log error or handle gracefully - System.err.println("Failed to parse role_playing_dimension JSON: " + e.getMessage()); - dimension.setRolePlayingDimensions(new ArrayList<>()); - } - } else { - dimension.setRolePlayingDimensions(new ArrayList<>()); - } - - dimension.setDimensionDto(dto); - dimensions.add(dimension); - } - return dimensions; - } - - - public List findMeasuresByFactTableId(Identifier factTableId) { - String sql = """ - SELECT id, caption, description, facttable_id, columnname, measure_filter_id, - aggregation_type, measure_type, default_release, is_hidden, is_custom - FROM metadata.measure - WHERE facttable_id = ? - """; - - sql = applyAccessRestrictions(sql); - - - List measureDtos = new ArrayList<>(); - SqlRowSet result = jt.queryForRowSet(sql, factTableId.composedId); - result.beforeFirst(); - while (result.next()) { - MeasureDto dto = new MeasureDto(); - dto.id = new Identifier(result.getString("id")); - dto.caption = result.getString("caption"); - dto.description = result.getString("description"); - dto.factTableId = new Identifier(result.getString("facttable_id")); - dto.columnname = result.getString("columnname"); - dto.measureFilterId = new Identifier(result.getString("measure_filter_id")); - dto.aggregationType = result.getString("aggregation_type"); - dto.measureType = result.getString("measure_type"); - dto.defaultRelease = result.getString("default_release"); - dto.isCustom = Boolean.valueOf(result.getBoolean("is_custom")); - dto.isHidden = Boolean.valueOf(result.getBoolean("is_hidden")); - measureDtos.add(dto); - } - - List measures = new ArrayList<>(); - for (MeasureDto measureDto : measureDtos) { - if(measureDto.isHidden == null || !(measureDto.isHidden.booleanValue())) { - measures.add(measureDto); - } - } - return measures; - } - - - public List getDimensionAttributeByIds(List ids, List factIds) { - String idsQuoted = ids.stream() - .map(s -> "'" + s.composedId + "'") - .collect(Collectors.joining(",")); - String sql = - String.format( - """ - SELECT id, caption, description, dimension_id, columnname, sort_order_column, - filter_selection, hierarchical_filter, is_hidden, conformed, - default_release, position, criteria_groups, is_custom - FROM metadata.dimension_attribute - WHERE id IN (%s) - """, idsQuoted); - - sql = applyAccessRestrictions(sql); - - List attributes = new ArrayList<>(); - - SqlRowSet result = jt.queryForRowSet(sql); - result.beforeFirst(); - while (result.next()) { - AttributeDto dto = new AttributeDto(); - dto.id = new Identifier(result.getString("id")); - dto.caption = result.getString("caption"); - dto.description = result.getString("description"); - dto.dimensionId = new Identifier(result.getString("dimension_id")); - dto.columnname = result.getString("columnname"); - dto.sortOrderColumn = result.getString("sort_order_column"); - dto.filterSelection = result.getString("filter_selection"); - dto.hierarchicalFilter = Boolean.valueOf(result.getBoolean("hierarchical_filter")); - dto.isHidden = Boolean.valueOf(result.getBoolean("is_hidden")); - dto.attrConformedId = result.getString("conformed"); - dto.defaultRelease = result.getString("default_release"); - dto.isCustom = Boolean.valueOf(result.getBoolean("is_custom")); - dto.position = Integer.valueOf(result.getInt("position")); - dto.criteriaGroups = (ArrayNode) result.getObject("criteria_groups"); - String jsonString = result.getString("criteria_groups"); - ArrayNode arrayNode = null; - if (jsonString != null && !jsonString.trim().isEmpty()) { - try { - arrayNode = (ArrayNode) MAPPER.readTree(jsonString); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - } - dto.criteriaGroups = arrayNode; - attributes.add(dto); - } - - - List finalAttributes = new ArrayList<>(); - for (AttributeDto attribute : attributes) { - DimensionAttribute attr = new DimensionAttribute(attribute); - Dimension dim = getDimension(attr.getDimensionId()); - attr.setDimension(dim); - if(factIds.contains(dim.getFactTableId())) { - finalAttributes.add(attr); - } - } - return finalAttributes; - } - - public DimensionAttribute getDimensionAttributeById(Identifier dimAttrId) { - Optional optAttr = this.dimensionAttrRepo.findById(dimAttrId); - if(optAttr.isEmpty()) { - throw new FaultyMetadataException(dimAttrId, "Attribute"); - } - return new DimensionAttribute(optAttr.get()); - } - - public List getMeasures(Identifier factTableId) { - List measures = new ArrayList<>(); - for (MeasureDto measureDto : measureRepository.findByFactTableId(factTableId)) { - if(!(measureDto.isHidden.booleanValue())) { - measures.add(new Measure(measureDto)); - } - } - return measures; - } - - public FactTable getFactTable(Identifier factTableId) { - Optional optFact = getFactDtoById(factTableId); - if(optFact.isEmpty()) { - throw new FaultyMetadataException(factTableId, "Fact"); - } - return new FactTable(optFact.get()); - } - - public static List getDefaultReleaseValues(DataSource dataSource) { - JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); - String sql = - "SELECT default_release FROM metadata.facttable UNION " + - "SELECT default_release FROM metadata.dimension UNION " + - "SELECT default_release FROM metadata.dimension_attribute UNION " + - "SELECT default_release FROM metadata.measure UNION " + - "SELECT default_release FROM metadata.measure_filter"; - - List results = jdbcTemplate.queryForList(sql, String.class); - List items = new ArrayList(); - for (String value : results) { - if (value != null && !value.isEmpty()) { - items.add(new Item(value, value)); - } - } - return items; - } - - - public void saveAllAttributeGroups() { - List groups = this.dimensionAttrRepo.findByNonEmptyCriteriaGroup(); - for (AttributeDto attribute : groups) { - saveAttributeGroupColumn(attribute, false); - saveAttributeGroupColumn(attribute, true); - } - } - - public void saveAttributeGroupColumn(AttributeDto attribute, boolean isSortOrder) { - - // Attribute and Dimension for which the attribute group was created - DimensionDto dimension = this.dimensionRepository.findByIdOrThrow(attribute.dimensionId); - String columnname = attribute.columnname; - if(isSortOrder) { - if(attribute.sortOrderColumn != null && !attribute.sortOrderColumn.isBlank()) { - columnname = attribute.sortOrderColumn; - } else { - columnname += "_sort_order"; - } - } - - // Dimensions can define a custom 'view' in JSON metadata (*_metadata.json), - // overriding the conformed dimension table used e.g. in fact table joins. - // Collect all such tables here to ensure the new column is added universally. - List allTableNames = this.dimensionRepository.getAllTableNamesById(attribute.dimensionId.composedId); - - for (String table : allTableNames) { - String tableName = "presentation." + table; - String columnSql = String.format(""" - ALTER TABLE %s DROP COLUMN IF EXISTS %s; - ALTER TABLE %s ADD COLUMN %s text; - """, tableName, columnname, tableName, columnname); - - List groups = attribute.readCriteriaGroups(); - SqlQuery query = AttributeGroupQueryBuilder.buildColumnQuery(groups, tableName, isSortOrder, columnname); - - jt.execute(columnSql); - jt.update(query.getSql(), query.getParams(), query.getTypes()); - } - - } - - private static List getAllColumns(List groups) { - Set columns = new LinkedHashSet<>(); - if (groups != null) { - for (CriteriaGroup group : groups) { - if (!group.isElse) { - collectColumns(group.conditions, columns); - } - } - } - return new ArrayList<>(columns); - } - - private static void collectColumns(List conditions, Set columns) { - for (Object obj : conditions) { - if (Condition.isCondition(obj)) { - Condition condition = Condition.fromMap(obj); - columns.add(condition.column); - } else if (CriteriaGroup.isGroup(obj)) { - CriteriaGroup subgroup = CriteriaGroup.fromMap(obj); - // Recursively collect from nested groups - collectColumns(subgroup.conditions, columns); - } - } - } - - - public List validateAttibuteGroup(AttributeDto attribute, Identifier dimensionId) { - DimensionDto dimension = this.dimensionRepository.findByIdOrThrow(dimensionId); - String tableName = "presentation." + dimension.tablename; - List groups = attribute.readCriteriaGroups(); - - List allColumns = getAllColumns(groups); - String colList = String.join(", ", allColumns); - List queries = AttributeGroupQueryBuilder.buildValidationQuery(tableName, colList, groups); - List results = new ArrayList<>(); - - for (SqlQuery query : queries) { - results = jt.query(query.getSql(), query.getParams(), query.getTypes(), - (rs, rowNum) -> { - StringJoiner row = new StringJoiner(", "); - for (String col : allColumns) { - row.add(col +"=" + "\"" + rs.getString(col) + "\""); - } - return row.toString(); - }); - if(!results.isEmpty()) { - break; - } - } - - return results; - } - - public AttributeDto saveAttributeGroup(AttributeDto attributeGroup, Identifier dimensionId) { - - // if an id is present we edit this attribute and if not we create a new one - boolean editAttribute = attributeGroup.id != null && - attributeGroup.id.composedId != null && - !attributeGroup.id.composedId.isEmpty(); - - // validate that caption is there - if(attributeGroup.caption == null || attributeGroup.caption.isBlank()) { - throw new FaultyMetadataException("Die Bezeichnung des Attributes darf nicht leer sein."); - } - - // validate that caption is unique - Optional attributeOpt = this.dimensionAttrRepo.findByDimensionId(dimensionId) - .stream().filter(a -> a.caption.equals(attributeGroup.caption)).findFirst(); - if(!editAttribute && attributeOpt.isPresent()) { - throw new FaultyMetadataException("Attribut mit der Bezeichnung '"+ attributeGroup.caption + "' existiert bereits."); - } - - // create or update attribute - Identifier targetAttributeId = attributeGroup.id; // null for new attributes - Identifier targetDimensionId = dimensionId; - AttributeDto updatedOrCreatedAttribute; - if(editAttribute) { - // If we edit an attribute we need to set its the conformed id (if it is a - // role playing dimension) since we can not be sure that it is included - String conformedDimensionId = this.dimensionAttrRepo.findByIdOrThrow(attributeGroup.id).attrConformedId; - updatedOrCreatedAttribute = - saveAttribute(attributeGroup, targetAttributeId, targetDimensionId, false, new Identifier(conformedDimensionId)); - } else { - updatedOrCreatedAttribute = - saveAttribute(attributeGroup, null, targetDimensionId, false, null); - } - - // If a dimension is a role playing dimension we want to add the attribute - // to its conformed dimension. But since attributes can belong to a role playing - // dimension without belonging to its conformed dimension we need to first check - // if all columns in the criteria groups occur in the conformed dimension. If - // thats not the case we add the new attribute to only the role playing dimension. - DimensionDto dimension = this.dimensionRepository.findByIdOrThrow(dimensionId); - if(dimension.conformed != null && !dimension.conformed.isBlank()) { - List attributeColumns = this.dimensionAttrRepo - .findByDimensionId(dimensionId) - .stream() - .map(d -> d.columnname) - .collect(Collectors.toList()); - - List groups = attributeGroup.readCriteriaGroups(); - List allColumns = getAllColumns(groups); - if(attributeColumns.containsAll(allColumns)) { - targetDimensionId = new Identifier(dimension.conformed); - if(editAttribute) { - Identifier confId = new Identifier(updatedOrCreatedAttribute.attrConformedId); - saveAttribute(attributeGroup, confId, targetDimensionId, true, null); - } else { - AttributeDto confAttr = saveAttribute(attributeGroup, null, targetDimensionId, true, null); - updatedOrCreatedAttribute = - saveAttribute(attributeGroup, updatedOrCreatedAttribute.id, dimensionId, false, confAttr.id); - } - } - } - - // This block is executed in one of two cases: - // 1. The passed dimension 'dimensionId' is a conformed dimension - // -> we need to create all the role playing attributes for the before - // created conformed attribute - // 2. The passed dimension 'dimensionId' is a role playing dimension for which - // we created a role playing attribute AND a conformed attribute. The - // 'targetDimensionId' was then set to its conformed dimension. - // -> we need to create all the role playing attributes for the before - // created conformed attribute - if(targetDimensionId.namespace.startsWith("conf")) { - List rolePlayingDimensions = new ArrayList(); - for (DimensionDto d : this.dimensionRepository.findAll()) { - if(d.conformed != null && d.conformed.equals(targetDimensionId.composedId)) { - if(d.id.equals(dimensionId)) { - // We passed a role playing dimension to this method and already created - // the attribute for it, therefore we do not need to create it again - continue; - } - rolePlayingDimensions.add(d); - } - } - - for (DimensionDto dimensionDto : rolePlayingDimensions) { - Identifier targetAttribute = null; - if(editAttribute) { - // We edited the attribute therefore we also need to edit the role - // playing attributes by providing their id - String conformedId = updatedOrCreatedAttribute.attrConformedId; - if(conformedId == null || conformedId.isBlank()) { - conformedId = updatedOrCreatedAttribute.id.composedId; - } - Optional attr = dimensionAttrRepo.findByDimensionIdAndAttrConformedId(dimensionDto.id, conformedId); - if(attr.isPresent()) { - targetAttribute = attr.get().id; - } - } - saveAttribute(attributeGroup, targetAttribute, dimensionDto.id, true, new Identifier(updatedOrCreatedAttribute.attrConformedId)); - } - } else { - // a non-conformed non-role-playing dimension - // ignore for now - } - - return updatedOrCreatedAttribute; - } - - public AttributeDto saveAttribute(AttributeDto attribute) { - return this.dimensionAttrRepo.save(attribute); - } - - private AttributeDto saveAttribute(AttributeDto attributeGroup, Identifier targetAttributeId, Identifier targetDimensionId, boolean isHidden, Identifier conformedAttribute) { - AttributeDto newAttrDto = new AttributeDto(); - newAttrDto.id = targetAttributeId; - newAttrDto.dimensionId = targetDimensionId; - newAttrDto.caption = attributeGroup.caption; - newAttrDto.description = attributeGroup.description; - newAttrDto.columnname = attributeGroup.getColumnNameFromCaption(); - newAttrDto.criteriaGroups = attributeGroup.criteriaGroups; - newAttrDto.isHidden = Boolean.valueOf(isHidden); - newAttrDto.defaultRelease = getCustomDefaultReleaseForCurrentDate(); - newAttrDto.isCustom = Boolean.valueOf(true);; - newAttrDto.sortOrderColumn = newAttrDto.columnname + "_sort_order"; - if(conformedAttribute != null) { - newAttrDto.attrConformedId = conformedAttribute.composedId; - } - return this.dimensionAttrRepo.save(newAttrDto); - } - - public void deleteAttributeGroupById(Identifier attributeId) { - AttributeDto attribute = this.dimensionAttrRepo.findByIdOrThrow(attributeId); - deleteColumnForAttributeGroup(attributeId, attribute.dimensionId); - - if(attribute.attrConformedId != null) { - for (AttributeDto rolePlayingAttribute : this.dimensionAttrRepo.findAll()) { - if(rolePlayingAttribute.attrConformedId != null && - !rolePlayingAttribute.attrConformedId.isBlank() && - rolePlayingAttribute.attrConformedId.equals(attribute.attrConformedId)) { - this.dimensionAttrRepo.deleteById(rolePlayingAttribute.id); - } - } - this.dimensionAttrRepo.deleteById(attribute.attrConformedId); - } else { - this.dimensionAttrRepo.deleteById(attribute.id); - } - - this.dimensionAttrRepo.deleteById(attributeId); - } - - public void deleteColumnForAttributeGroup(Identifier attrId, Identifier dimId) { - AttributeDto attribute = this.dimensionAttrRepo.findByIdOrThrow(attrId); - String columnname = attribute.columnname; - - List allTableNames = this.dimensionRepository.getAllTableNamesById(attribute.dimensionId.composedId); - - for (String table : allTableNames) { - String sql = """ - ALTER TABLE presentation.%s - DROP COLUMN IF EXISTS %s; - - ALTER TABLE presentation.%s - DROP COLUMN IF EXISTS %s_sort_order; - """.formatted(table, columnname, table, columnname); - - jt.update(sql); - } - } - - public List getAttributeGroups() { - return this.dimensionAttrRepo.findByNonEmptyCriteriaGroup(); - } - - public List getFilteredValuesForAttribute(Identifier id, String searchString, int limit){ - List result = new ArrayList(); - - DimensionAttribute attr = getDimensionAttributeById(id); - Dimension dim = getDimension(attr.getDimensionId()); - attr.setDimension(dim); - - String sortOrderColumnName = attr.getSortOrderColumn() != null ? attr.getSortOrderColumn(): attr.getColumnname(); - - String templateSql = - "SELECT DISTINCT d.%s AS value, d.%s, " + - "array_position(array[%s], d.%s::text) " + - "FROM presentation.%s d " + - "WHERE d.%s IS NOT NULL " + - "AND d.%s LIKE ? " + - // TODO: sometimes we need DESC - "ORDER BY 3, 2 ASC "; - - if(limit >= 0 ) { - templateSql += "LIMIT ?;"; - }else { - templateSql += ";"; - } - - String query = String.format( - templateSql, - attr.getColumnname(), - sortOrderColumnName, - DimensionAttribute.specialValueListForSql(), - attr.getColumnname(), - attr.getTablename(), - attr.getColumnname(), - attr.getColumnname() - ); - - - List values = jt.query(new PreparedStatementCreator() { - @Override - public PreparedStatement createPreparedStatement(Connection con) throws SQLException { - PreparedStatement pstmt = con.prepareStatement(query); - - pstmt.setString(1, "%" + searchString + "%"); - if(limit >= 0 ) { - pstmt.setInt(2, limit); - } - - return pstmt; - } - }, (rs, rowNum) -> rs.getString("value")) - .stream().distinct().collect(Collectors.toList()); - - result.addAll(values); - - return result; - } - - public void hideAttributeById(Identifier id, Boolean hide) { - AttributeDto attributeDto = this.dimensionAttrRepo.findByIdOrThrow(id); - if(!attributeDto.isCustom.booleanValue()) { - //attributeDto.id = getNewCustomIdentifier(dataSource); - attributeDto.isCustom = Boolean.valueOf(true); - } - attributeDto.isHidden = hide; - this.dimensionAttrRepo.save(attributeDto); - - } - - public void hideDimensionById(Identifier id, Boolean hide) { - DimensionDto dimensionDto = this.dimensionRepository.findByIdOrThrow(id); - if(!dimensionDto.isCustom.booleanValue()) { - //dimensionDto.id = getNewCustomIdentifier(dataSource); - dimensionDto.isCustom = Boolean.valueOf(true); - } - dimensionDto.isHidden = hide; - this.dimensionRepository.save(dimensionDto); - - } - - - public List findStoredReports(Optional title, Optional sach, Optional> facts) { - String sql = """ - WITH reports_with_facts AS ( - SELECT - rd.* - ,factid #>> '{}' AS fact_id - FROM - metadata.rw_report_definitions rd, - jsonb_array_elements(rd.definition::jsonb -> 'factTableIds') AS factid - ) - SELECT rf.*, ft.sachgebiettid FROM reports_with_facts rf - INNER JOIN metadata.facttable ft ON ft.id = fact_id - WHERE 1=1 - """; - - sql = applyAccessRestrictions(sql); - - if (facts.isPresent() && !facts.get().isEmpty()) { - sql += " AND rf.fact_id = ANY(?) "; - } - - if (sach.isPresent()) { - sql +=" AND ft.sachgebiettid = ? "; - } - - if (title.isPresent()) { - sql += " AND rf.name LIKE ? "; - } - - return jt.query( - sql, - new PreparedStatementSetter() { - @Override - public void setValues(PreparedStatement ps) throws SQLException { - int paramIndex = 1; - if (facts.isPresent() && !facts.get().isEmpty()) { - Array array = ps.getConnection().createArrayOf("TEXT", facts.get().toArray()); - ps.setArray(paramIndex++, array); - } - if (sach.isPresent()) { - ps.setInt(paramIndex++, sach.get().intValue()); - } - if (title.isPresent()) { - ps.setString(paramIndex++, "%" + title.get() + "%"); - } - } - }, - (rs, rowNum) -> { - StoredReport report = new StoredReport(); - report.id = rs.getInt(1); - report.name = rs.getString(2); - report.definition = rs.getString(3); - report.description = rs.getString(4); - report.showTotalColumn = rs.getInt(5); - StoredReport.setReportDefinitionFromJson(report); - return report; - } - ); - } - - - public List findRolePlayingDimensionsByFacttables(Identifier primaryFactId, Identifier secondaryFactId) { - String sql = """ - WITH first_dims AS ( - SELECT id, caption, conformed, facttable_id - FROM metadata.dimension - WHERE conformed IS NOT NULL AND facttable_id = ? - ), - second_dims AS ( - SELECT id, caption, conformed, facttable_id - FROM metadata.dimension - WHERE conformed IS NOT NULL AND facttable_id = ? - ) - SELECT distinct - s.id, - s.caption - FROM first_dims f - JOIN second_dims s ON f.conformed = s.conformed - """; - - List dims = new ArrayList<>(); - SqlRowSet result = jt.queryForRowSet(sql, primaryFactId.composedId, secondaryFactId.composedId); - result.beforeFirst(); - while (result.next()) { - DimensionDto fd = new DimensionDto(); - fd.id = new Identifier(result.getString("id")); - fd.caption = result.getString("caption"); - Dimension dim = new Dimension(); - dim.setDimensionDto(fd); - dims.add(dim); - } - return dims; - } - - public static Identifier getNewCustomIdentifier(DataSource dataSource) { - String query = "WITH all_ids AS (" - + " SELECT split_part(id, ':', 2) AS id_part FROM metadata.facttable WHERE is_custom = true UNION ALL" - + " SELECT split_part(id, ':', 2) FROM metadata.dimension WHERE is_custom = true UNION ALL" - + " SELECT split_part(id, ':', 2) FROM metadata.dimension_attribute WHERE is_custom = true UNION ALL" - + " SELECT split_part(id, ':', 2) FROM metadata.measure WHERE is_custom = true UNION ALL" - + " SELECT split_part(id, ':', 2) FROM metadata.attribute_group UNION ALL" - + " SELECT split_part(id, ':', 2) FROM metadata.measure_filter WHERE is_custom = true" - + " )" - + " SELECT MAX(CAST(id_part AS INTEGER)) AS max_int FROM all_ids;"; - - JdbcTemplate jt = new JdbcTemplate(dataSource); - int maxValue = 1; - try { - Integer maxCustomId = jt.queryForObject(query, Integer.class); - if(maxCustomId != null) { - maxValue += maxCustomId.intValue(); - } - } catch (EmptyResultDataAccessException e) { - // do nothing here - } - return new Identifier("custom:" + maxValue); - } - - public static String getCustomDefaultReleaseForCurrentDate() { - LocalDate now = LocalDate.now(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd"); - String result = "custom_" + now.format(formatter); - return result; - } - - public List findConformedDimensionsWithAtLeastTwoOccurences(Identifier factId) { - String sql = """ - WITH cd_occurrence AS ( - SELECT count(cd.id) AS num_cd, - cd.id - FROM metadata.dimension d - JOIN metadata.dimension cd ON d.conformed = cd.id - GROUP BY cd.id - ORDER BY 1 DESC - ) - SELECT * - FROM metadata.dimension d - LEFT JOIN cd_occurrence cdo ON cdo.id = d.conformed - WHERE d.conformed IS NOT NULL AND num_cd >= 2 - and d.facttable_id = ? - """; - List dims = new ArrayList<>(); - SqlRowSet result = jt.queryForRowSet(sql, factId.composedId); - result.beforeFirst(); - while (result.next()) { - DimensionDto fd = new DimensionDto(); - fd.id = new Identifier(result.getString("id")); - fd.caption = result.getString("caption"); - Dimension dim = new Dimension(); - dim.setDimensionDto(fd); - dims.add(dim); - } - return dims; - } - - - public void switchAttributePositions(Identifier id1, Identifier id2) { - AttributeDto attributeDto1 = this.dimensionAttrRepo.findByIdOrThrow(id1); - AttributeDto attributeDto2 = this.dimensionAttrRepo.findByIdOrThrow(id2); - Integer originPosition = attributeDto1.position; - attributeDto1.position = attributeDto2.position; - attributeDto2.position = originPosition; - this.dimensionAttrRepo.save(attributeDto1); - this.dimensionAttrRepo.save(attributeDto2); - ; - - } - - public List getTopDimensionAttributeValuesForFactTables(DimensionAttribute confAttr, DimensionAttribute rolePlayingPrimaryFact, FactTable primaryFactTable, List secondaryFactTables) { - String sortOrderColumnName - = confAttr.getSortOrderColumn() != null ? confAttr.getSortOrderColumn(): confAttr.getColumnname(); - - String sql = - "SELECT DISTINCT d.%s AS value, d.%s, " + - "array_position(array[%s], d.%s::text) " + - "FROM presentation.%s d " + - "WHERE d.id IN( "; - - StringJoiner unionJoiner = new StringJoiner(" UNION "); - unionJoiner.add("SELECT " + rolePlayingPrimaryFact.getJoincolumn() - + " FROM presentation." + primaryFactTable.getTablename()); - - for (SecondaryFacttable factTable : secondaryFactTables) { - for (JoinCriteria joinCriteria : factTable.joinCriterias) { - if(joinCriteria.secondaryDimension.getConformed().equals(confAttr.getDimId())) { - unionJoiner.add("SELECT " + joinCriteria.secondaryDimension.getJoincolumn() - + " FROM presentation." + factTable.factTable.getTablename()); - } - } - } - sql += unionJoiner.toString() + ")"; - sql += " ORDER BY 3, 2 ASC"; - - String query = String.format( - sql, - confAttr.getColumnname(), - sortOrderColumnName, - DimensionAttribute.specialValueListForSql(), - confAttr.getColumnname(), - confAttr.getTablename() - ); - - List values = jt.query(query, (rs, rowNum) -> rs.getString("value")) - .stream().distinct().collect(Collectors.toList()); - - return values; - } - - - public Optional findRolePlayingAttributeId( - Identifier conformedAttributeId, - Identifier rolePlayingDimensionId) { - - String sql = "select id from metadata.dimension_attribute" - + " where conformed = ?" - + " and dimension_id = ?"; - - JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); - - try { - String id = jdbcTemplate.queryForObject( - sql, - (rs, rowNum) -> rs.getString("id"), - conformedAttributeId.composedId, - rolePlayingDimensionId.composedId - ); - - if (id == null || id.isBlank()) { - return Optional.empty(); - } - - return Optional.of(new Identifier(id)); - - } catch (EmptyResultDataAccessException e) { - return Optional.empty(); - - } catch (IncorrectResultSizeDataAccessException e) { - throw new IllegalStateException( - "Expected exactly one role-playing attribute for conformedAttributeId=" - + conformedAttributeId - + " and rolePlayingDimensionId=" - + rolePlayingDimensionId - + " but found multiple", - e); - - } catch (DataAccessException e) { - throw new IllegalStateException( - "Failed to load role-playing attribute for conformedAttributeId=" - + conformedAttributeId - + " and rolePlayingDimensionId=" - + rolePlayingDimensionId, - e); - } - } - - public boolean factTableHasConformedDimension(Identifier factTableId, String conformedDimensionId) { - - if (conformedDimensionId == null || conformedDimensionId.isBlank()) { - return false; - } - - String sql = "SELECT COUNT(*) " + - "FROM metadata.dimension " + - "WHERE facttable_id = ? " + - "AND conformed = ?"; - - Integer count = new JdbcTemplate(dataSource).queryForObject( - sql, - Integer.class, - factTableId.composedId, - conformedDimensionId - ); - - return count != null && count.intValue() > 0; - } - - public List getSecondaryFactTableMetadata( - List secondaryFacttables, - List leftDimensionAttributeIds) { - - if(secondaryFacttables == null || secondaryFacttables.isEmpty()) { - return new ArrayList<>(); - } - - List result = new ArrayList<>(); - for (SecondaryFacttable secondaryFacttable : secondaryFacttables) { - FactTable fact = getFactTable(secondaryFacttable.factId); - secondaryFacttable.factTable = fact; - secondaryFacttable.measures = getMeasureMetadata(secondaryFacttable.measureIds); - if(secondaryFacttable.measures == null || secondaryFacttable.measures.isEmpty()) { - continue; - } - for (Measure measure : secondaryFacttable.measures) { - measure.factTableName = fact.getTablename(); - - for (Identifier attributeId: leftDimensionAttributeIds) { - DimensionAttribute attr = getDimensionAttributeMetadataById(attributeId); - boolean isInGrain = factTableHasConformedDimension(secondaryFacttable.factId, attr.getDimConformedId()); - - if(!isInGrain) { - if(!secondaryFacttable.join.equals("union")) { - measure.attributesNotInGrain.add(attr.getDimensionTableAlias() + "." + attr.getColumnname()); - secondaryFacttable.attributesNotInGrain.add(attr.getId().composedId); - } else { - measure.attributesNotInGrain.add(attr.getDimensionColumnAlias()); - secondaryFacttable.attributesNotInGrain.add(attr.getId().composedId); - } - } - } - } - - for (JoinCriteria rolePlayingDimension : secondaryFacttable.joinCriterias) { - rolePlayingDimension.leadingDimension = - getDimension(rolePlayingDimension.leadingRolePlayingDimensionId); - rolePlayingDimension.secondaryDimension = - getDimension(rolePlayingDimension.secondaryRolePlayingDimensionId); - } - result.add(secondaryFacttable); - } - - return result; - } - - -} +package de.superx.bianalysis.service; + +import java.sql.Array; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.StringJoiner; +import java.util.stream.Collectors; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.PreparedStatementCreator; +import org.springframework.jdbc.core.PreparedStatementSetter; +import org.springframework.jdbc.support.rowset.SqlRowSet; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; + +import de.superx.access.DbSqlRestrictionProvider; +import de.superx.access.SqlAccessRestrictor; +import de.superx.bianalysis.FaultyMetadataException; +import de.superx.bianalysis.JoinCriteria; +import de.superx.bianalysis.ReportDefinition; +import de.superx.bianalysis.SecondaryFacttable; +import de.superx.bianalysis.StoredReport; +import de.superx.bianalysis.metadata.Identifier; +import de.superx.bianalysis.models.Condition; +import de.superx.bianalysis.models.CriteriaGroup; +import de.superx.bianalysis.models.Dimension; +import de.superx.bianalysis.models.DimensionAttribute; +import de.superx.bianalysis.models.FactTable; +import de.superx.bianalysis.models.Filter; +import de.superx.bianalysis.models.Measure; +import de.superx.bianalysis.repository.DimensionAttributeRepository; +import de.superx.bianalysis.repository.DimensionRepository; +import de.superx.bianalysis.repository.MeasureFilterRepository; +import de.superx.bianalysis.repository.MeasureRepository; +import de.superx.bianalysis.repository.StoredReportRepository; +import de.superx.bianalysis.repository.dto.AttributeDto; +import de.superx.bianalysis.repository.dto.DimensionDto; +import de.superx.bianalysis.repository.dto.FactDto; +import de.superx.bianalysis.repository.dto.MeasureDto; +import de.superx.bianalysis.repository.dto.MeasureFilterDto; +import de.superx.bianalysis.sqlgeneration.AttributeGroupQueryBuilder; +import de.superx.bianalysis.sqlgeneration.SqlQuery; +import de.superx.common.NotYetImplementedException; +import de.superx.common.SxUser; +import de.superx.jdbc.entity.Sachgebiet; +import de.superx.jdbc.entity.Systeminfo; +import de.superx.jdbc.repository.SachgebieteRepository; +import de.superx.jdbc.repository.SysteminfoRepository; +import de.superx.rest.model.Item; +import de.superx.spring.service.UserService; + +@Service() +public class DbMetaAdapter implements InitializingBean { + +// @Autowired +// FactRepository factRepository; + + @Autowired + MeasureRepository measureRepository; + + @Autowired + DimensionRepository dimensionRepository; + + @Autowired + DimensionAttributeRepository dimensionAttrRepo; + + @Autowired + MeasureFilterRepository measureFilterRepo; + + @Autowired + StoredReportRepository storedReportRepository; + + @Autowired + SachgebieteRepository sachgebieterepository; + + @Autowired + SysteminfoRepository systeminfoRepository; + + @Autowired + UserService userService; + + @Autowired + DataSource dataSource; + + @Autowired + DbSqlRestrictionProvider sqlRestrictionProvider; + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + private JdbcTemplate jt; + + @Override + public void afterPropertiesSet() throws Exception { + this.jt = new JdbcTemplate(dataSource); + } + + private List getFactDtos(String sql) { + List factDtos = new ArrayList(); + SqlRowSet result = jt.queryForRowSet(sql); + result.beforeFirst(); + while (result.next()) { + FactDto fd = new FactDto(); + fd.id = new Identifier(result.getString("id")); + fd.caption = result.getString("caption"); + fd.tablename = result.getString("tablename"); + fd.defaultRelease = result.getString("default_release"); + fd.isCustom = Boolean.valueOf(result.getBoolean("is_custom")); + fd.sachgebiettid = Integer.valueOf(result.getInt("sachgebiettid")); + fd.description = result.getString("description"); + factDtos.add(fd); + } + return factDtos; + } + + public List getFactTables() throws NotYetImplementedException { + try { + String sql = "SELECT * FROM metadata.facttable;"; + sql = applyAccessRestrictions(sql); + List fdtos = getFactDtos(sql); + List factTables = fdtos + .stream() + .map(f -> { + FactTable fact = new FactTable(f); + fact.setSachgebiet(getSachgebietForFacttable(f.sachgebiettid.intValue())); + return fact; + }) + .filter(f -> getSachgebietForFacttable(f.getSachgebiettid()).tid.intValue() != -1) + .collect(Collectors.toList()); + return factTables; + } catch (Exception e) { + + e.printStackTrace(); + if (e.getCause().getMessage().contains("FEHLER: Relation »metadata.facttable« existiert nicht")) { + throw new NotYetImplementedException("Bitte installieren Sie zuerst die Komponente 'BI-Analyse-Daten' und führen Sie anschließend den Konnektor aus."); + } + throw e; + } + } + + public String applyAccessRestrictions(String sql) { + if ( SqlAccessRestrictor.rechteModus == SqlAccessRestrictor.Rechtekonzept.INLINE_SQL) { + try { + SxUser user = (SxUser)userService.currentUserDetails(); + String userName = user.getName(); + String roleName = user.getRole(); + int orgunitLid = user.getHisInOneOrgUnitLidOfRole().intValue(); + SqlAccessRestrictor accessRestrictor = new SqlAccessRestrictor(this.sqlRestrictionProvider, userName, roleName, orgunitLid); + sql = accessRestrictor.restrictSql(sql); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return sql; + } + + private boolean hasSachgebietTopicRestrictions(List facts, int tid) { + for (Identifier factId : facts) { + if(getFactTable(factId).getSachgebiettid() == tid) { + return true; + } + } + return false; + } + + + + public DimensionAttribute getDimensionAttributeMetadata(Identifier id) { + Optional optAttribute = dimensionAttrRepo.findById(id); + if(optAttribute.isEmpty()) { + throw new FaultyMetadataException(id, "Attribute"); + } + + DimensionAttribute attr = new DimensionAttribute(optAttribute.get()); + Dimension dim = getDimension(attr.getDimensionId()); + + attr.setDimension(dim); + + if(attr.getAttrConformedId() != null) { + Optional optAttributeConf = dimensionAttrRepo.findById(new Identifier(attr.getAttrConformedId())); + if(optAttributeConf.isPresent()) { + Dimension dimConf = getDimension(optAttributeConf.get().dimensionId); + attr.setDimConformedId(dimConf.getId().composedId); + } + } + return attr; + } + + public List getDimensionAttributeMetadata(List attributeIds, Identifier factId) { + + if (attributeIds == null || attributeIds.size() <= 0 ) { + return null; + } + + List result = new ArrayList(); + for (Identifier id : attributeIds) { + Optional optAttribute = dimensionAttrRepo.findById(id); + if(optAttribute.isEmpty()) { + throw new FaultyMetadataException(id, "Attribute"); + } + + DimensionAttribute attr = new DimensionAttribute(optAttribute.get()); + Dimension dim = getDimension(attr.getDimensionId()); + + attr.setDimension(dim); + + if(attr.getAttrConformedId() != null) { + Optional optAttributeConf = dimensionAttrRepo.findById(new Identifier(attr.getAttrConformedId())); + if(optAttributeConf.isPresent()) { + Dimension dimConf = getDimension(optAttributeConf.get().dimensionId); + attr.setDimConformedId(dimConf.getId().composedId); + } + } + result.add(attr); + } + return result; + } + + public List getMeasureMetadata(List measureIds) { + if (measureIds != null && measureIds.size() > 0 ) { + List result = new ArrayList(); + for (Identifier id : measureIds) { + Measure measure = getMeasure(id); + FactTable factTable = getFactTable(measure.getFacttableId()); + measure.factTableName = factTable.getTablename(); + if(measure.getMeasureFilterId() != null) { + MeasureFilterDto filter = measureFilterRepo.findById(measure.getMeasureFilterId()).get(); + if(filter.dimensionAttributeId != null) { + DimensionAttribute attribute = getDimensionAttributeById(filter.dimensionAttributeId); + Dimension dimension = getDimension(attribute.getDimensionId()); + measure.setMeasureFilterAttributes(filter, attribute, dimension); + } else if(filter.factColumnFilter != null) { + measure.setFactColumnFilter(filter); + } + } + result.add(measure); + } + return result; + } + return null; + } + + + public List getDimensionsWithoutHidden(Identifier factTableId){ + + List dimensions = new ArrayList<>(); + for (Dimension dimension : getDimensions(factTableId)) { + if(dimension.isHidden()) { + continue; + } + dimensions.add(dimension); + + List attributes = new ArrayList<>(); + for (DimensionAttribute attribute : dimension.getDimensionAttributes()) { + if(!attribute.isHidden()) { + attributes.add(attribute); + } + } + dimension.setDimensionAttributes(attributes); + } + + return dimensions; + } + + + + public List getDimensions(Identifier factTableId) { + List dimensions = new ArrayList<>(); + + for (Dimension dimension : findByFactTableId(factTableId)) { + dimensions.add(dimension); + + List attr = dimensionAttrRepo.findByDimensionId(dimension.getId()); + List attributes = attr.stream() + .map(a -> new DimensionAttribute(a)) + .collect(Collectors.toList()); + + attributes.sort(Comparator.comparing( + DimensionAttribute::getPosition, + Comparator.nullsLast(Integer::compareTo) + )); + + dimension.setDimensionAttributes(attributes); + for(DimensionAttribute a : attributes) { + a.setDimension(dimension); + } + } + + return dimensions; + } + + private Optional getFactDtoById(Identifier id) { + String sql = String.format("SELECT * FROM metadata.facttable WHERE id = '%s';", + id.toString()); + sql = applyAccessRestrictions(sql); + List fdtos = getFactDtos(sql); + Optional factOpt = Optional.ofNullable(null); + if (fdtos.size() > 0) { + factOpt = Optional.ofNullable(fdtos.get(0)); + } + return factOpt; + } + + public List getDimensionAttributeValues(List attributes, List factTables) { + List result = new ArrayList(); + List tables = new ArrayList(); + for (DimensionAttribute attr : attributes) { + if(tables.contains(attr.getTablename())) { + continue; + } + tables.add(attr.getTablename()); + for(Identifier factId : factTables) { + result.addAll(getValuesOfAttribute(attr, factId, null, null)); + } + } + return result; + } + + public List getValuesOfAttribute(DimensionAttribute attr, Identifier factId, List filtersInDimension, Boolean hideEmptyColumns) { + FactTable fact = getFactTable(factId); + Dimension dim = getDimension(attr.getDimensionId()); + List values = getDimensionAttributeValues(attr, dim, fact, filtersInDimension, hideEmptyColumns); + return values; + } + + public List getFilterMetadata(List filters) { + for (Filter filter : filters) { + DimensionAttribute attr = getDimensionAttributeById(filter.dimensionAttributeId); + Dimension dim = getDimension(attr.getDimensionId()); + attr.setDimension(dim); + filter.setDimension(dim); + filter.setDimensionAttribute(attr); + } + return filters; + } + + public Filter setFilterMetadata(Filter filter) { + DimensionAttribute attr = getDimensionAttributeById(filter.dimensionAttributeId); + Dimension dim = getDimension(attr.getDimensionId()); + attr.setDimension(dim); + filter.setDimension(dim); + filter.setDimensionAttribute(attr); + return filter; + } + + public List getDimensionAttributeValues( + DimensionAttribute attr, Dimension dim, FactTable factTable, + List filtersInDimension, Boolean hideEmptyColumns) { + String sortOrderColumnName = attr.getSortOrderColumn() != null ? attr.getSortOrderColumn(): attr.getColumnname(); + + String templateSql = + "SELECT DISTINCT d.%s AS value, d.%s, " + + "array_position(array[%s], d.%s::text) " + + "FROM presentation.%s d"; + + /// default + if((factTable != null && attr.getFilterSelection() == null + || (dim != null && attr.getFilterSelection().equals("show_existing_only"))) && hideEmptyColumns == null + || hideEmptyColumns != null && hideEmptyColumns.booleanValue() == true) { + + String idColumn = "id"; + if(attr.getDimIdJoinColumn() != null && !attr.getDimIdJoinColumn().isBlank()) { + idColumn = attr.getDimIdJoinColumn(); + } + + templateSql += " INNER JOIN presentation." + factTable.getTablename() + + " f ON d." + idColumn + " = f." + dim.getJoincolumn(); + } + + templateSql += " WHERE d.%s IS NOT NULL "; + + if(dim != null + && attr.getFilterSelection() != null + && attr.getFilterSelection().equals("show_range")) { + + String idColumn = "id"; + if(attr.getDimIdJoinColumn() != null && !attr.getDimIdJoinColumn().isBlank()) { + idColumn = attr.getDimIdJoinColumn(); + } + + templateSql += " AND d." + idColumn + " BETWEEN (" + + "SELECT MIN(" + dim.getJoincolumn() + ")" + + " FROM presentation." + factTable.getTablename() + + ") AND (" + + "SELECT MAX(" + dim.getJoincolumn() + + " FROM presentation." + factTable.getTablename() + ")"; + } + + if(filtersInDimension != null) { + boolean first = true; + for (Filter filter : filtersInDimension) { + templateSql += " AND "; + first = true; + for( String filterValue : filter.filterValues) { + if(first) { + first = false; + } else { + templateSql += " OR "; + } + templateSql += " d." + filter.columnname + " = " + "'" + filterValue + "'"; + } + } + } + + // TODO: sometimes we need DESC + templateSql += " ORDER BY 3, 2 ASC;"; + String query = String.format( + templateSql, + attr.getColumnname(), + sortOrderColumnName, + DimensionAttribute.specialValueListForSql(), + attr.getColumnname(), + attr.getTablename(), + attr.getColumnname() + ); + + List values = jt.query(query, (rs, rowNum) -> rs.getString("value")) + .stream().distinct().collect(Collectors.toList()); + + return values; + } + + public DimensionAttribute getDimensionAttributeMetadataById(Identifier id) { + DimensionAttribute attr = getDimensionAttributeById(id); + Dimension dim = getDimension(attr.getDimensionId()); + attr.setDimension(dim); + if(dim.getConformed() != null && !dim.getConformed().isBlank()) { + Dimension confDim = getDimension(new Identifier(dim.getConformed())); + attr.setDimConformedId(confDim.getId().composedId); + } + return attr; + } + + public Sachgebiet getSachgebietById(int sachgebietId) { + return sachgebieterepository + .findById(Integer.valueOf(sachgebietId)) + .orElseThrow(() -> + new IllegalArgumentException("Sachgebiet with ID " + sachgebietId + " was not found.")); + } + + public int saveReportDefinition(StoredReport report) { + StoredReport savedStoredReport = this.storedReportRepository.save(report); + return savedStoredReport.id; + } + + public int getColNumbers(List topDimensionAttributes, List filters) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + int num = 1; + for (DimensionAttribute dimensionAttribute : topDimensionAttributes) { + if(filters != null && filters.size() > 0) { + Filter filter = Filter.findFilterById(filters, dimensionAttribute.getId()); + if(filter != null && filter.filterValues.size() > 0) { + num *= filter.filterValues.size(); + continue; + } + } + String sql = String.format("SELECT count(DISTINCT %s) FROM presentation.%s WHERE %s IS NOT NULL", dimensionAttribute.getColumnname(), dimensionAttribute.getTablename(), dimensionAttribute.getColumnname()); + int value = jdbcTemplate.queryForObject(sql, new Object[] {}, Integer.class).intValue(); + num *= value; + } + return num; + } + + public String getLastUpdate(int tid) { + Optional systeminfoOpt = systeminfoRepository.findById(Integer.valueOf(tid)); + if(systeminfoOpt.isPresent()) { + Date lastUpdate = systeminfoOpt.get().datum; + return new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(lastUpdate); + } + return "Unknown"; + } + + public DataSource getDataSource() { + return this.dataSource; + } + + public int getSachgebietByFactTableId(String factTableId) { + Optional fact = getFactDtoById(new Identifier(factTableId)); + if(fact.isPresent()) { + return fact.get().sachgebiettid.intValue(); + } + return -1; + } + + public Integer getSachgebietForReport(ReportDefinition reportDefinition) { + int sachgebiet = getSachgebietByFactTableId(reportDefinition.factTableId.composedId); + Integer result = Integer.valueOf(sachgebiet); + return result; + } + + public List findAllStoredReports(){ + List reports = new ArrayList<>(); + for (StoredReport report : this.storedReportRepository.findAll()) { + StoredReport.setReportDefinitionFromJson(report); + reports.add(report); + } + return reports; + } + + public Optional findById(int id) { + Optional report = storedReportRepository.findById(id); + if(report.isPresent()) { + StoredReport.setReportDefinitionFromJson(report.get()); + } + return report; + } + + public void deleteById(int id) { + storedReportRepository.deleteById(id); + } + + private Sachgebiet getSachgebietForFacttable(int sachgebiettid) { + Optional sachgebiet = sachgebieterepository + .findById(Integer.valueOf(sachgebiettid)); + if(sachgebiet.isPresent()) { + return sachgebiet.get(); + } + return new Sachgebiet(Integer.valueOf(-1), "Unknown", null); + } + + public List getSachgebieteForFactTables(List rightParamValues) { + List result = new ArrayList<>(); + for (String id : rightParamValues) { + int sachgebiet = getSachgebietByFactTableId(id); + if(sachgebiet != -1) { + result.add(String.valueOf(sachgebiet)); + } + } + return result; + } + + public List getAttributesOfDimension(Identifier dimId){ + List attr = dimensionAttrRepo.findByDimensionId(dimId); + List attributes = attr.stream() + .map(a -> new DimensionAttribute(a)) + .collect(Collectors.toList()); + + attributes.sort(Comparator.comparing( + DimensionAttribute::getPosition, + Comparator.nullsLast(Integer::compareTo) + )); + + return attributes; + } + + public Dimension getDimension(Identifier id) { + Optional dimOpt = dimensionRepository.findById(id); + if(dimOpt.isEmpty()) { + throw new FaultyMetadataException(id, "Dimension"); + } + Dimension dimension = new Dimension(dimOpt.get()); + return dimension; + } + + public Measure getMeasure(Identifier id) { + Optional measureOpt = measureRepository.findById(id); + if(measureOpt.isEmpty()) { + throw new FaultyMetadataException(id, "Measure"); + } + Measure measure = new Measure(measureOpt.get()); + return measure; + } + + + public List findByFactTableId(Identifier factTableId) { + ObjectMapper mapper = new ObjectMapper(); + String sql = """ + WITH join_partners AS ( + SELECT + conformed AS conf, + json_agg(json_build_object('id', id, 'caption', caption, 'factTableId', facttable_id)) AS role_playing_dimension + FROM metadata.dimension d WHERE conformed IS NOT NULL + AND facttable_id != ? + GROUP BY conformed + ) + SELECT d.id, d.caption, d.description, d.facttable_id, d.tablename, d.joincolumn, d.alias, + d.is_historical, d.conformed, d.id_column, d.default_release, + d.is_hidden, d.position, d.is_custom, join_partners.role_playing_dimension + FROM metadata.dimension d + LEFT JOIN join_partners ON join_partners.conf = d.conformed + WHERE d.facttable_id = ? + ORDER BY position + """; + + sql = applyAccessRestrictions(sql); + + List dimensions = new ArrayList<>(); + + SqlRowSet result = jt.queryForRowSet(sql, factTableId.composedId, factTableId.composedId); + result.beforeFirst(); + while (result.next()) { + Dimension dimension = new Dimension(); + DimensionDto dto = new DimensionDto(); + dto.id = new Identifier(result.getString("id")); + dto.caption = result.getString("caption"); + dto.description = result.getString("description"); + dto.factTableId = new Identifier(result.getString("facttable_id")); + dto.tablename = result.getString("tablename"); + dto.joincolumn = result.getString("joincolumn"); + dto.alias = result.getString("alias"); + dto.isHistorical = Boolean.valueOf(result.getBoolean("is_historical")); + dto.conformed = result.getString("conformed"); + dto.idColumn = result.getString("id_column"); + dto.defaultRelease = result.getString("default_release"); + dto.isCustom = Boolean.valueOf(result.getBoolean("is_custom")); + dto.isHidden = Boolean.valueOf(result.getBoolean("is_hidden")); + dto.position = Integer.valueOf(result.getInt("position")); + + // Handle JSON array for rolePlayingDimensions + String jsonArray = result.getString("role_playing_dimension"); + if (jsonArray != null && !jsonArray.isEmpty() && !"null".equals(jsonArray)) { + try { + List rolePlayingDimensions = + mapper.readValue(jsonArray, new TypeReference>() {}); + dimension.setRolePlayingDimensions(rolePlayingDimensions); + } catch (Exception e) { + // Log error or handle gracefully + System.err.println("Failed to parse role_playing_dimension JSON: " + e.getMessage()); + dimension.setRolePlayingDimensions(new ArrayList<>()); + } + } else { + dimension.setRolePlayingDimensions(new ArrayList<>()); + } + + dimension.setDimensionDto(dto); + dimensions.add(dimension); + } + return dimensions; + } + + + public List findMeasuresByFactTableId(Identifier factTableId) { + String sql = """ + SELECT id, caption, description, facttable_id, columnname, measure_filter_id, + aggregation_type, measure_type, default_release, is_hidden, is_custom + FROM metadata.measure + WHERE facttable_id = ? + """; + + sql = applyAccessRestrictions(sql); + + + List measureDtos = new ArrayList<>(); + SqlRowSet result = jt.queryForRowSet(sql, factTableId.composedId); + result.beforeFirst(); + while (result.next()) { + MeasureDto dto = new MeasureDto(); + dto.id = new Identifier(result.getString("id")); + dto.caption = result.getString("caption"); + dto.description = result.getString("description"); + dto.factTableId = new Identifier(result.getString("facttable_id")); + dto.columnname = result.getString("columnname"); + dto.measureFilterId = new Identifier(result.getString("measure_filter_id")); + dto.aggregationType = result.getString("aggregation_type"); + dto.measureType = result.getString("measure_type"); + dto.defaultRelease = result.getString("default_release"); + dto.isCustom = Boolean.valueOf(result.getBoolean("is_custom")); + dto.isHidden = Boolean.valueOf(result.getBoolean("is_hidden")); + measureDtos.add(dto); + } + + List measures = new ArrayList<>(); + for (MeasureDto measureDto : measureDtos) { + if(measureDto.isHidden == null || !(measureDto.isHidden.booleanValue())) { + measures.add(measureDto); + } + } + return measures; + } + + + public List getDimensionAttributeByIds(List ids, List factIds) { + String idsQuoted = ids.stream() + .map(s -> "'" + s.composedId + "'") + .collect(Collectors.joining(",")); + String sql = + String.format( + """ + SELECT id, caption, description, dimension_id, columnname, sort_order_column, + filter_selection, hierarchical_filter, is_hidden, conformed, + default_release, position, criteria_groups, is_custom + FROM metadata.dimension_attribute + WHERE id IN (%s) + """, idsQuoted); + + sql = applyAccessRestrictions(sql); + + List attributes = new ArrayList<>(); + + SqlRowSet result = jt.queryForRowSet(sql); + result.beforeFirst(); + while (result.next()) { + AttributeDto dto = new AttributeDto(); + dto.id = new Identifier(result.getString("id")); + dto.caption = result.getString("caption"); + dto.description = result.getString("description"); + dto.dimensionId = new Identifier(result.getString("dimension_id")); + dto.columnname = result.getString("columnname"); + dto.sortOrderColumn = result.getString("sort_order_column"); + dto.filterSelection = result.getString("filter_selection"); + dto.hierarchicalFilter = Boolean.valueOf(result.getBoolean("hierarchical_filter")); + dto.isHidden = Boolean.valueOf(result.getBoolean("is_hidden")); + dto.attrConformedId = result.getString("conformed"); + dto.defaultRelease = result.getString("default_release"); + dto.isCustom = Boolean.valueOf(result.getBoolean("is_custom")); + dto.position = Integer.valueOf(result.getInt("position")); + String jsonString = result.getString("criteria_groups"); + ArrayNode arrayNode = null; + if (jsonString != null && !jsonString.trim().isEmpty()) { + try { + arrayNode = (ArrayNode) MAPPER.readTree(jsonString); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + dto.criteriaGroups = arrayNode; + attributes.add(dto); + } + + + List finalAttributes = new ArrayList<>(); + for (AttributeDto attribute : attributes) { + DimensionAttribute attr = new DimensionAttribute(attribute); + Dimension dim = getDimension(attr.getDimensionId()); + attr.setDimension(dim); + if(factIds.contains(dim.getFactTableId())) { + finalAttributes.add(attr); + } + } + return finalAttributes; + } + + public DimensionAttribute getDimensionAttributeById(Identifier dimAttrId) { + Optional optAttr = this.dimensionAttrRepo.findById(dimAttrId); + if(optAttr.isEmpty()) { + throw new FaultyMetadataException(dimAttrId, "Attribute"); + } + return new DimensionAttribute(optAttr.get()); + } + + public List getMeasures(Identifier factTableId) { + List measures = new ArrayList<>(); + for (MeasureDto measureDto : measureRepository.findByFactTableId(factTableId)) { + if(!(measureDto.isHidden.booleanValue())) { + measures.add(new Measure(measureDto)); + } + } + return measures; + } + + public FactTable getFactTable(Identifier factTableId) { + Optional optFact = getFactDtoById(factTableId); + if(optFact.isEmpty()) { + throw new FaultyMetadataException(factTableId, "Fact"); + } + return new FactTable(optFact.get()); + } + + public static List getDefaultReleaseValues(DataSource dataSource) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + String sql = + "SELECT default_release FROM metadata.facttable UNION " + + "SELECT default_release FROM metadata.dimension UNION " + + "SELECT default_release FROM metadata.dimension_attribute UNION " + + "SELECT default_release FROM metadata.measure UNION " + + "SELECT default_release FROM metadata.measure_filter"; + + List results = jdbcTemplate.queryForList(sql, String.class); + List items = new ArrayList(); + for (String value : results) { + if (value != null && !value.isEmpty()) { + items.add(new Item(value, value)); + } + } + return items; + } + + + public void saveAllAttributeGroups() { + List groups = this.dimensionAttrRepo.findByNonEmptyCriteriaGroup(); + for (AttributeDto attribute : groups) { + saveAttributeGroupColumn(attribute, false); + saveAttributeGroupColumn(attribute, true); + } + } + + public void saveAttributeGroupColumn(AttributeDto attribute, boolean isSortOrder) { + + // Attribute and Dimension for which the attribute group was created + DimensionDto dimension = this.dimensionRepository.findByIdOrThrow(attribute.dimensionId); + String columnname = attribute.columnname; + if(isSortOrder) { + if(attribute.sortOrderColumn != null && !attribute.sortOrderColumn.isBlank()) { + columnname = attribute.sortOrderColumn; + } else { + columnname += "_sort_order"; + } + } + + // Dimensions can define a custom 'view' in JSON metadata (*_metadata.json), + // overriding the conformed dimension table used e.g. in fact table joins. + // Collect all such tables here to ensure the new column is added universally. + List allTableNames = this.dimensionRepository.getAllTableNamesById(attribute.dimensionId.composedId); + + for (String table : allTableNames) { + String tableName = "presentation." + table; + String columnType = (isSortOrder) ? "integer" : "text"; + String columnSql = String.format(""" + ALTER TABLE %s DROP COLUMN IF EXISTS %s; + ALTER TABLE %s ADD COLUMN %s %s; + """, tableName, columnname, tableName, columnname, columnType); + + List groups = attribute.readCriteriaGroups(); + SqlQuery query = AttributeGroupQueryBuilder.buildColumnQuery(groups, tableName, isSortOrder, columnname); + + jt.execute(columnSql); + jt.update(query.getSql(), query.getParams(), query.getTypes()); + } + + } + + private static List getAllColumns(List groups) { + Set columns = new LinkedHashSet<>(); + if (groups != null) { + for (CriteriaGroup group : groups) { + if (!group.isElse) { + collectColumns(group.conditions, columns); + } + } + } + return new ArrayList<>(columns); + } + + private static void collectColumns(List conditions, Set columns) { + for (Object obj : conditions) { + if (Condition.isCondition(obj)) { + Condition condition = Condition.fromMap(obj); + columns.add(condition.column); + } else if (CriteriaGroup.isGroup(obj)) { + CriteriaGroup subgroup = CriteriaGroup.fromMap(obj); + // Recursively collect from nested groups + collectColumns(subgroup.conditions, columns); + } + } + } + + + public List validateAttibuteGroup(AttributeDto attribute, Identifier dimensionId) { + DimensionDto dimension = this.dimensionRepository.findByIdOrThrow(dimensionId); + String tableName = "presentation." + dimension.tablename; + List groups = attribute.readCriteriaGroups(); + + List allColumns = getAllColumns(groups); + String colList = String.join(", ", allColumns); + List queries = AttributeGroupQueryBuilder.buildValidationQuery(tableName, colList, groups); + List results = new ArrayList<>(); + + for (SqlQuery query : queries) { + results = jt.query(query.getSql(), query.getParams(), query.getTypes(), + (rs, rowNum) -> { + StringJoiner row = new StringJoiner(", "); + for (String col : allColumns) { + row.add(col +"=" + "\"" + rs.getString(col) + "\""); + } + return row.toString(); + }); + if(!results.isEmpty()) { + break; + } + } + + return results; + } + + public AttributeDto saveAttributeGroup(AttributeDto attributeGroup, Identifier dimensionId) { + + // if an id is present we edit this attribute and if not we create a new one + boolean editAttribute = attributeGroup.id != null && + attributeGroup.id.composedId != null && + !attributeGroup.id.composedId.isEmpty(); + + // validate that caption is there + if(attributeGroup.caption == null || attributeGroup.caption.isBlank()) { + throw new FaultyMetadataException("Die Bezeichnung des Attributes darf nicht leer sein."); + } + + // validate that caption is unique + Optional attributeOpt = this.dimensionAttrRepo.findByDimensionId(dimensionId) + .stream().filter(a -> a.caption.equals(attributeGroup.caption)).findFirst(); + if(!editAttribute && attributeOpt.isPresent()) { + throw new FaultyMetadataException("Attribut mit der Bezeichnung '"+ attributeGroup.caption + "' existiert bereits."); + } + + // create or update attribute + Identifier targetAttributeId = attributeGroup.id; // null for new attributes + Identifier targetDimensionId = dimensionId; + AttributeDto updatedOrCreatedAttribute; + if(editAttribute) { + // If we edit an attribute we need to set its the conformed id (if it is a + // role playing dimension) since we can not be sure that it is included + String conformedDimensionId = this.dimensionAttrRepo.findByIdOrThrow(attributeGroup.id).attrConformedId; + updatedOrCreatedAttribute = + saveAttribute(attributeGroup, targetAttributeId, targetDimensionId, false, new Identifier(conformedDimensionId)); + } else { + updatedOrCreatedAttribute = + saveAttribute(attributeGroup, null, targetDimensionId, false, null); + } + + // If a dimension is a role playing dimension we want to add the attribute + // to its conformed dimension. But since attributes can belong to a role playing + // dimension without belonging to its conformed dimension we need to first check + // if all columns in the criteria groups occur in the conformed dimension. If + // thats not the case we add the new attribute to only the role playing dimension. + DimensionDto dimension = this.dimensionRepository.findByIdOrThrow(dimensionId); + if(dimension.conformed != null && !dimension.conformed.isBlank()) { + List attributeColumns = this.dimensionAttrRepo + .findByDimensionId(dimensionId) + .stream() + .map(d -> d.columnname) + .collect(Collectors.toList()); + + List groups = attributeGroup.readCriteriaGroups(); + List allColumns = getAllColumns(groups); + if(attributeColumns.containsAll(allColumns)) { + targetDimensionId = new Identifier(dimension.conformed); + if(editAttribute) { + Identifier confId = new Identifier(updatedOrCreatedAttribute.attrConformedId); + saveAttribute(attributeGroup, confId, targetDimensionId, true, null); + } else { + AttributeDto confAttr = saveAttribute(attributeGroup, null, targetDimensionId, true, null); + updatedOrCreatedAttribute = + saveAttribute(attributeGroup, updatedOrCreatedAttribute.id, dimensionId, false, confAttr.id); + } + } + } + + // This block is executed in one of two cases: + // 1. The passed dimension 'dimensionId' is a conformed dimension + // -> we need to create all the role playing attributes for the before + // created conformed attribute + // 2. The passed dimension 'dimensionId' is a role playing dimension for which + // we created a role playing attribute AND a conformed attribute. The + // 'targetDimensionId' was then set to its conformed dimension. + // -> we need to create all the role playing attributes for the before + // created conformed attribute + if(targetDimensionId.namespace.startsWith("conf")) { + List rolePlayingDimensions = new ArrayList(); + for (DimensionDto d : this.dimensionRepository.findAll()) { + if(d.conformed != null && d.conformed.equals(targetDimensionId.composedId)) { + if(d.id.equals(dimensionId)) { + // We passed a role playing dimension to this method and already created + // the attribute for it, therefore we do not need to create it again + continue; + } + rolePlayingDimensions.add(d); + } + } + + for (DimensionDto dimensionDto : rolePlayingDimensions) { + Identifier targetAttribute = null; + if(editAttribute) { + // We edited the attribute therefore we also need to edit the role + // playing attributes by providing their id + String conformedId = updatedOrCreatedAttribute.attrConformedId; + if(conformedId == null || conformedId.isBlank()) { + conformedId = updatedOrCreatedAttribute.id.composedId; + } + Optional attr = dimensionAttrRepo.findByDimensionIdAndAttrConformedId(dimensionDto.id, conformedId); + if(attr.isPresent()) { + targetAttribute = attr.get().id; + } + } + saveAttribute(attributeGroup, targetAttribute, dimensionDto.id, true, new Identifier(updatedOrCreatedAttribute.attrConformedId)); + } + } else { + // a non-conformed non-role-playing dimension + // ignore for now + } + + return updatedOrCreatedAttribute; + } + + public DimensionDto saveExistingDimensionAndOverrideFields(DimensionDto dimension) { + DimensionDto dimensionDto = this.dimensionRepository.findByIdOrThrow(dimension.id); + dimensionDto.caption = dimension.caption; + dimensionDto.description = dimension.description; + dimensionDto.defaultRelease = dimension.defaultRelease; + dimensionDto.isCustom = Boolean.valueOf(true); + return this.dimensionRepository.save(dimensionDto); + } + + public DimensionDto saveDimension(DimensionDto dimension) { + return this.dimensionRepository.save(dimension); + } + + public AttributeDto saveAttribute(AttributeDto attribute) { + return this.dimensionAttrRepo.save(attribute); + } + + private AttributeDto saveAttribute(AttributeDto attributeGroup, Identifier targetAttributeId, Identifier targetDimensionId, boolean isHidden, Identifier conformedAttribute) { + AttributeDto newAttrDto = new AttributeDto(); + newAttrDto.id = targetAttributeId; + newAttrDto.dimensionId = targetDimensionId; + newAttrDto.caption = attributeGroup.caption; + newAttrDto.description = attributeGroup.description; + newAttrDto.columnname = attributeGroup.getColumnNameFromCaption(); + newAttrDto.criteriaGroups = attributeGroup.criteriaGroups; + newAttrDto.isHidden = Boolean.valueOf(isHidden); + newAttrDto.defaultRelease = getCustomDefaultReleaseForCurrentDate(); + newAttrDto.isCustom = Boolean.valueOf(true);; + newAttrDto.sortOrderColumn = newAttrDto.columnname + "_sort_order"; + if(conformedAttribute != null) { + newAttrDto.attrConformedId = conformedAttribute.composedId; + } + return this.dimensionAttrRepo.save(newAttrDto); + } + + public void deleteAttributeGroupById(Identifier attributeId) throws IllegalMetaDataOperation { + AttributeDto attribute = this.dimensionAttrRepo.findByIdOrThrow(attributeId); + + failIfIdIsUsedInReport(attributeId, attribute.caption); + + deleteColumnForAttributeGroup(attributeId, attribute.dimensionId); + + if(attribute.attrConformedId != null) { + for (AttributeDto rolePlayingAttribute : this.dimensionAttrRepo.findAll()) { + if(rolePlayingAttribute.attrConformedId != null && + !rolePlayingAttribute.attrConformedId.isBlank() && + rolePlayingAttribute.attrConformedId.equals(attribute.attrConformedId)) { + this.dimensionAttrRepo.deleteById(rolePlayingAttribute.id); + } + } + this.dimensionAttrRepo.deleteById(attribute.attrConformedId); + } else { + this.dimensionAttrRepo.deleteById(attribute.id); + } + + this.dimensionAttrRepo.deleteById(attributeId); + } + + public void deleteColumnForAttributeGroup(Identifier attrId, Identifier dimId) { + AttributeDto attribute = this.dimensionAttrRepo.findByIdOrThrow(attrId); + String columnname = attribute.columnname; + + List allTableNames = this.dimensionRepository.getAllTableNamesById(attribute.dimensionId.composedId); + + for (String table : allTableNames) { + String sql = """ + ALTER TABLE %s.%s + DROP COLUMN IF EXISTS %s; + + ALTER TABLE %s.%s + DROP COLUMN IF EXISTS %s_sort_order; + """.formatted("presentation", table, columnname, "presentation", table, columnname); + + jt.update(sql); + } + } + + public List getAttributeGroups() { + return this.dimensionAttrRepo.findByNonEmptyCriteriaGroup(); + } + + public List getFilteredValuesForAttribute(Identifier id, String searchString, int limit){ + List result = new ArrayList(); + + DimensionAttribute attr = getDimensionAttributeById(id); + Dimension dim = getDimension(attr.getDimensionId()); + attr.setDimension(dim); + + String sortOrderColumnName = attr.getSortOrderColumn() != null ? attr.getSortOrderColumn(): attr.getColumnname(); + + String templateSql = + "SELECT DISTINCT d.%s AS value, d.%s, " + + "array_position(array[%s], d.%s::text) " + + "FROM presentation.%s d " + + "WHERE d.%s IS NOT NULL " + + "AND d.%s ILIKE ? " + + // TODO: sometimes we need DESC + "ORDER BY 3, 2 ASC "; + + if(limit >= 0 ) { + templateSql += "LIMIT ?;"; + }else { + templateSql += ";"; + } + + String query = String.format( + templateSql, + attr.getColumnname(), + sortOrderColumnName, + DimensionAttribute.specialValueListForSql(), + attr.getColumnname(), + attr.getTablename(), + attr.getColumnname(), + attr.getColumnname() + ); + + + List values = jt.query(new PreparedStatementCreator() { + @Override + public PreparedStatement createPreparedStatement(Connection con) throws SQLException { + PreparedStatement pstmt = con.prepareStatement(query); + + pstmt.setString(1, "%" + searchString + "%"); + if(limit >= 0 ) { + pstmt.setInt(2, limit); + } + + return pstmt; + } + }, (rs, rowNum) -> rs.getString("value")) + .stream().distinct().collect(Collectors.toList()); + + result.addAll(values); + + return result; + } + + public void hideAttributeById(Identifier id, Boolean hide) throws IllegalMetaDataOperation { + AttributeDto attributeDto = this.dimensionAttrRepo.findByIdOrThrow(id); + + if(hide.booleanValue()) { + failIfIdIsUsedInReport(id, attributeDto.caption); + } + + if(!attributeDto.isCustom.booleanValue()) { + //attributeDto.id = getNewCustomIdentifier(dataSource); + attributeDto.isCustom = Boolean.valueOf(true); + } + attributeDto.isHidden = hide; + this.dimensionAttrRepo.save(attributeDto); + + } + + + public void failIfIdIsUsedInReport(Identifier id, String caption) throws IllegalMetaDataOperation { + String sql = """ + WITH report_definitions AS ( + SELECT + rd.name AS report_name, + rd.definition::jsonb AS definition + FROM metadata.rw_report_definitions rd + ), + direct_ids AS ( + SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'factTableIds', '[]'::jsonb)) AS used_id FROM report_definitions r UNION + SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'leftDimensionAttributeIds', '[]'::jsonb)) AS used_id FROM report_definitions r UNION + SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'topDimensionAttributeIds', '[]'::jsonb)) AS used_id FROM report_definitions r UNION + SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'measureIds', '[]'::jsonb)) AS used_id FROM report_definitions r UNION + SELECT r.report_name, filter_obj->>'dimensionAttributeId' AS used_id + FROM report_definitions r + CROSS JOIN LATERAL jsonb_array_elements(COALESCE(r.definition->'filters', '[]'::jsonb)) AS filter_obj + ), + used_attribute_ids AS ( + SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'leftDimensionAttributeIds', '[]'::jsonb)) AS attribute_id FROM report_definitions r UNION + SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'topDimensionAttributeIds', '[]'::jsonb)) AS attribute_id FROM report_definitions r UNION + SELECT r.report_name, filter_obj->>'dimensionAttributeId' AS attribute_id FROM report_definitions r CROSS JOIN LATERAL jsonb_array_elements(COALESCE(r.definition->'filters', '[]'::jsonb)) AS filter_obj + ), + indirect_dimension_ids AS ( + SELECT DISTINCT + u.report_name, + da.dimension_id::text AS used_id + FROM used_attribute_ids u + JOIN metadata.dimension_attribute da + ON da.id::text = u.attribute_id + ), + all_used_ids AS ( + SELECT report_name, used_id + FROM direct_ids + UNION + SELECT report_name, used_id + FROM indirect_dimension_ids + ) + SELECT report_name + FROM all_used_ids + WHERE used_id = ? + LIMIT 1 + """; + + String reportName = new JdbcTemplate(dataSource).query( + sql, + rs -> rs.next() ? rs.getString("report_name") : null, + id.toString() + ); + + if (reportName != null) { + throw new IllegalMetaDataOperation( + "Das Objekt '" + caption + "' (" + id + ") kann nicht gelöscht werden, " + + "weil es noch im Bericht '" + reportName + "' verwendet wird." + ); + } + } + + public void hideDimensionById(Identifier id, Boolean hide) throws IllegalMetaDataOperation { + DimensionDto dimensionDto = this.dimensionRepository.findByIdOrThrow(id); + + if(hide.booleanValue()) { + failIfIdIsUsedInReport(id, dimensionDto.caption); + } + + if(!dimensionDto.isCustom.booleanValue()) { + //dimensionDto.id = getNewCustomIdentifier(dataSource); + dimensionDto.isCustom = Boolean.valueOf(true); + } + dimensionDto.isHidden = hide; + this.dimensionRepository.save(dimensionDto); + + } + + + public List findStoredReports(Optional title, Optional sach, Optional> facts) { + String sql = """ + WITH reports_with_facts AS ( + SELECT + rd.* + ,(rd.definition::jsonb ->> 'factTableId') AS fact_id + FROM + metadata.rw_report_definitions rd + ) + SELECT rf.*, ft.sachgebiettid FROM reports_with_facts rf + INNER JOIN metadata.facttable ft ON ft.id = fact_id + WHERE 1=1 + """; + + sql = applyAccessRestrictions(sql); + + if (facts.isPresent() && !facts.get().isEmpty()) { + sql += " AND rf.fact_id = ANY(?) "; + } + + if (sach.isPresent()) { + sql +=" AND ft.sachgebiettid = ? "; + } + + if (title.isPresent()) { + sql += " AND rf.name LIKE ? "; + } + + return jt.query( + sql, + new PreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps) throws SQLException { + int paramIndex = 1; + if (facts.isPresent() && !facts.get().isEmpty()) { + Array array = ps.getConnection().createArrayOf("TEXT", facts.get().toArray()); + ps.setArray(paramIndex++, array); + } + if (sach.isPresent()) { + ps.setInt(paramIndex++, sach.get().intValue()); + } + if (title.isPresent()) { + ps.setString(paramIndex++, "%" + title.get() + "%"); + } + } + }, + (rs, rowNum) -> { + StoredReport report = new StoredReport(); + report.id = rs.getInt(1); + report.name = rs.getString(2); + report.definition = rs.getString(3); + report.description = rs.getString(4); + report.showTotalColumn = rs.getInt(5); + StoredReport.setReportDefinitionFromJson(report); + return report; + } + ); + } + + + public List findRolePlayingDimensionsByFacttables(Identifier primaryFactId, Identifier secondaryFactId) { + String sql = """ + WITH first_dims AS ( + SELECT id, caption, conformed, facttable_id + FROM metadata.dimension + WHERE conformed IS NOT NULL AND facttable_id = ? + ), + second_dims AS ( + SELECT id, caption, conformed, facttable_id + FROM metadata.dimension + WHERE conformed IS NOT NULL AND facttable_id = ? + ) + SELECT distinct + s.id, + s.caption + FROM first_dims f + JOIN second_dims s ON f.conformed = s.conformed + """; + + List dims = new ArrayList<>(); + SqlRowSet result = jt.queryForRowSet(sql, primaryFactId.composedId, secondaryFactId.composedId); + result.beforeFirst(); + while (result.next()) { + DimensionDto fd = new DimensionDto(); + fd.id = new Identifier(result.getString("id")); + fd.caption = result.getString("caption"); + Dimension dim = new Dimension(); + dim.setDimensionDto(fd); + dims.add(dim); + } + return dims; + } + + public static Identifier getNewCustomIdentifier(DataSource dataSource) { + String query = "WITH all_ids AS (" + + " SELECT split_part(id, ':', 2) AS id_part FROM metadata.facttable WHERE is_custom = true UNION ALL" + + " SELECT split_part(id, ':', 2) FROM metadata.dimension WHERE is_custom = true UNION ALL" + + " SELECT split_part(id, ':', 2) FROM metadata.dimension_attribute WHERE is_custom = true UNION ALL" + + " SELECT split_part(id, ':', 2) FROM metadata.measure WHERE is_custom = true UNION ALL" + + " SELECT split_part(id, ':', 2) FROM metadata.attribute_group UNION ALL" + + " SELECT split_part(id, ':', 2) FROM metadata.measure_filter WHERE is_custom = true" + + " )" + + " SELECT MAX(CAST(id_part AS INTEGER)) AS max_int FROM all_ids;"; + + JdbcTemplate jt = new JdbcTemplate(dataSource); + int maxValue = 1; + try { + Integer maxCustomId = jt.queryForObject(query, Integer.class); + if(maxCustomId != null) { + maxValue += maxCustomId.intValue(); + } + } catch (EmptyResultDataAccessException e) { + // do nothing here + } + return new Identifier("custom:" + maxValue); + } + + public static String getCustomDefaultReleaseForCurrentDate() { + LocalDate now = LocalDate.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd"); + String result = "custom_" + now.format(formatter); + return result; + } + + public List findConformedDimensionsWithAtLeastTwoOccurences(Identifier factId) { + String sql = """ + WITH cd_occurrence AS ( + SELECT count(cd.id) AS num_cd, + cd.id + FROM metadata.dimension d + JOIN metadata.dimension cd ON d.conformed = cd.id + GROUP BY cd.id + ORDER BY 1 DESC + ) + SELECT * + FROM metadata.dimension d + LEFT JOIN cd_occurrence cdo ON cdo.id = d.conformed + WHERE d.conformed IS NOT NULL AND num_cd >= 2 + and d.facttable_id = ? + """; + List dims = new ArrayList<>(); + SqlRowSet result = jt.queryForRowSet(sql, factId.composedId); + result.beforeFirst(); + while (result.next()) { + DimensionDto fd = new DimensionDto(); + fd.id = new Identifier(result.getString("id")); + fd.caption = result.getString("caption"); + Dimension dim = new Dimension(); + dim.setDimensionDto(fd); + dims.add(dim); + } + return dims; + } + + + public void switchAttributePositions(Identifier id1, Identifier id2) { + AttributeDto attributeDto1 = this.dimensionAttrRepo.findByIdOrThrow(id1); + AttributeDto attributeDto2 = this.dimensionAttrRepo.findByIdOrThrow(id2); + Integer originPosition = attributeDto1.position; + attributeDto1.position = attributeDto2.position; + attributeDto2.position = originPosition; + this.dimensionAttrRepo.save(attributeDto1); + this.dimensionAttrRepo.save(attributeDto2); + ; + + } + + public List getTopDimensionAttributeValuesForFactTables(DimensionAttribute confAttr, DimensionAttribute rolePlayingPrimaryFact, FactTable primaryFactTable, List secondaryFactTables) { + String sortOrderColumnName + = confAttr.getSortOrderColumn() != null ? confAttr.getSortOrderColumn(): confAttr.getColumnname(); + + String sql = + "SELECT DISTINCT d.%s AS value, d.%s, " + + "array_position(array[%s], d.%s::text) " + + "FROM presentation.%s d " + + "WHERE d.id IN( "; + + StringJoiner unionJoiner = new StringJoiner(" UNION "); + unionJoiner.add("SELECT " + rolePlayingPrimaryFact.getJoincolumn() + + " FROM presentation." + primaryFactTable.getTablename()); + + for (SecondaryFacttable factTable : secondaryFactTables) { + for (JoinCriteria joinCriteria : factTable.joinCriterias) { + if(joinCriteria.secondaryDimension.getConformed().equals(confAttr.getDimId())) { + unionJoiner.add("SELECT " + joinCriteria.secondaryDimension.getJoincolumn() + + " FROM presentation." + factTable.factTable.getTablename()); + } + } + } + sql += unionJoiner.toString() + ")"; + sql += " ORDER BY 3, 2 ASC"; + + String query = String.format( + sql, + confAttr.getColumnname(), + sortOrderColumnName, + DimensionAttribute.specialValueListForSql(), + confAttr.getColumnname(), + confAttr.getTablename() + ); + + List values = jt.query(query, (rs, rowNum) -> rs.getString("value")) + .stream().distinct().collect(Collectors.toList()); + + return values; + } + + + public Optional findRolePlayingAttributeId( + Identifier conformedAttributeId, + Identifier rolePlayingDimensionId) { + + String sql = "select id from metadata.dimension_attribute" + + " where conformed = ?" + + " and dimension_id = ?"; + + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + + try { + String id = jdbcTemplate.queryForObject( + sql, + (rs, rowNum) -> rs.getString("id"), + conformedAttributeId.composedId, + rolePlayingDimensionId.composedId + ); + + if (id == null || id.isBlank()) { + return Optional.empty(); + } + + return Optional.of(new Identifier(id)); + + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + + } catch (IncorrectResultSizeDataAccessException e) { + throw new IllegalStateException( + "Expected exactly one role-playing attribute for conformedAttributeId=" + + conformedAttributeId + + " and rolePlayingDimensionId=" + + rolePlayingDimensionId + + " but found multiple", + e); + + } catch (DataAccessException e) { + throw new IllegalStateException( + "Failed to load role-playing attribute for conformedAttributeId=" + + conformedAttributeId + + " and rolePlayingDimensionId=" + + rolePlayingDimensionId, + e); + } + } + + public boolean factTableHasConformedDimension(Identifier factTableId, String conformedDimensionId) { + + if (conformedDimensionId == null || conformedDimensionId.isBlank()) { + return false; + } + + String sql = "SELECT COUNT(*) " + + "FROM metadata.dimension " + + "WHERE facttable_id = ? " + + "AND conformed = ?"; + + Integer count = new JdbcTemplate(dataSource).queryForObject( + sql, + Integer.class, + factTableId.composedId, + conformedDimensionId + ); + + return count != null && count.intValue() > 0; + } + + public List getSecondaryFactTableMetadata( + List secondaryFacttables, + List leftDimensionAttributeIds) { + + if(secondaryFacttables == null || secondaryFacttables.isEmpty()) { + return new ArrayList<>(); + } + + List result = new ArrayList<>(); + for (SecondaryFacttable secondaryFacttable : secondaryFacttables) { + FactTable fact = getFactTable(secondaryFacttable.factId); + secondaryFacttable.factTable = fact; + secondaryFacttable.measures = getMeasureMetadata(secondaryFacttable.measureIds); + if(secondaryFacttable.measures == null || secondaryFacttable.measures.isEmpty()) { + continue; + } + for (Measure measure : secondaryFacttable.measures) { + measure.factTableName = fact.getTablename(); + + for (Identifier attributeId: leftDimensionAttributeIds) { + DimensionAttribute attr = getDimensionAttributeMetadataById(attributeId); + boolean isInGrain = factTableHasConformedDimension(secondaryFacttable.factId, attr.getDimConformedId()); + + if(!isInGrain) { + if(!secondaryFacttable.join.equals("union")) { + measure.attributesNotInGrain.add(attr.getDimensionTableAlias() + "." + attr.getColumnname()); + secondaryFacttable.attributesNotInGrain.add(attr.getId().composedId); + } else { + measure.attributesNotInGrain.add(attr.getDimensionColumnAlias()); + secondaryFacttable.attributesNotInGrain.add(attr.getId().composedId); + } + } + } + } + + for (JoinCriteria rolePlayingDimension : secondaryFacttable.joinCriterias) { + rolePlayingDimension.leadingDimension = + getDimension(rolePlayingDimension.leadingRolePlayingDimensionId); + rolePlayingDimension.secondaryDimension = + getDimension(rolePlayingDimension.secondaryRolePlayingDimensionId); + } + result.add(secondaryFacttable); + } + + return result; + } + + public void deleteAttributeDto(AttributeDto attribute) throws IllegalMetaDataOperation { + failIfIdIsUsedInReport(attribute.id, attribute.caption); + this.dimensionAttrRepo.deleteById(attribute.id); + } + + public void deleteDimensionDto(DimensionDto dimension) throws IllegalMetaDataOperation { + failIfIdIsUsedInReport(dimension.id, dimension.caption); + this.dimensionAttrRepo.deleteById(dimension.id); + } + +} \ No newline at end of file diff --git a/src/de/superx/bianalysis/service/IllegalMetaDataOperation.java b/src/de/superx/bianalysis/service/IllegalMetaDataOperation.java new file mode 100644 index 0000000..b309e30 --- /dev/null +++ b/src/de/superx/bianalysis/service/IllegalMetaDataOperation.java @@ -0,0 +1,11 @@ +package de.superx.bianalysis.service; + +public class IllegalMetaDataOperation extends Exception { + + private static final long serialVersionUID = 1L; + + public IllegalMetaDataOperation(String message) { + super(message); + } + +} diff --git a/src/de/superx/bianalysis/service/JasperReportExport.java b/src/de/superx/bianalysis/service/JasperReportExport.java index b1c3bc4..56ca878 100644 --- a/src/de/superx/bianalysis/service/JasperReportExport.java +++ b/src/de/superx/bianalysis/service/JasperReportExport.java @@ -39,7 +39,7 @@ public class JasperReportExport { @Autowired SxCaptionsRepository sxCaptionsRepository; - public static String DATASOURCE_URL = "/api/reportwizard/template/datasource/storedReport?"; + public static String DATASOURCE_URL = "/api/bianalysis/template/datasource/storedReport?"; private static String DATA_ADAPTER_JSON_FILE = "JsonDataAdapter.jrdax"; public static String DATA_ADAPTER_REST = "JsonDataAdapterRest.jrdax"; public static String MAIN_REPORT = "MainReport.jrxml"; diff --git a/src/de/superx/bianalysis/service/UserRestrictionService.java b/src/de/superx/bianalysis/service/UserRestrictionService.java index 34968a9..bb8a256 100644 --- a/src/de/superx/bianalysis/service/UserRestrictionService.java +++ b/src/de/superx/bianalysis/service/UserRestrictionService.java @@ -55,6 +55,37 @@ public class UserRestrictionService { this.user = user; this.dataSource = dataSource; this.jt = new JdbcTemplate(dataSource); + ensureAuthDbStructures(); + } + + private void ensureAuthDbStructures() { + /* 'view_rechte' wird angepasst: + * Einschränkungen werden als Listen von IDs von Dimensionseinträgen gespeichert. + * Einschränkungen von Faktentabellen ergeben sich daraus anhand der Metadaten. */ + jt.execute("DROP TABLE IF EXISTS view_rechte;"); + String[] sqlCols = new String[] { + "user_id integer NOT NULL", + "user_name character varying(255)", + "role_name character varying(255)", + "orgunit_lid integer", + "schema_name character varying(255)", + "table_name character varying(255)", + "join_column character varying(255)", + "granted_ids text", + "external_entry smallint DEFAULT 0", + "is_restricted BOOLEAN DEFAULT FALSE", + "is_custom BOOLEAN DEFAULT FALSE" + + }; + String sqlCreate = "CREATE TABLE IF NOT EXISTS public.user_dimension_grants (\n" + String.join(",\n", sqlCols) + "\n);"; + jt.execute(sqlCreate); + for (String colDef : sqlCols) { + String sql = "ALTER TABLE public.user_dimension_grants ADD COLUMN IF NOT EXISTS " + colDef + ";"; + jt.execute(sql); + } + + + } public void applyMetadataRestrictions() { @@ -66,18 +97,46 @@ public class UserRestrictionService { for (String tableName : tables) { String resultSql = buildMetadataTableRestriction(tableName); - insertRightsViewRestriction(dataSource, "metadata", tableName , resultSql, - user.getHisInOneOrgUnitLidOfRole().intValue()); + if(!resultSql.isBlank()) { + insertRightsViewRestriction(dataSource, "metadata", tableName , "id", resultSql, user.getHisInOneOrgUnitLidOfRole().intValue()); + } + } + } + + + public String buildOrgunitGrantedIds(Integer userOrgunitLid) { + List allowedOrgunitLids = new ArrayList<>(); + allowedOrgunitLids.add(userOrgunitLid); + allowedOrgunitLids.add(Integer.valueOf(-1)); + + List params = getRightParamValues + (RightParam.ORGUNIT, Right.CREATE_ANALYSIS, Right.VIEW_REPORT); + + if(!params.isEmpty()) { + for (String lid : params) { + allowedOrgunitLids.add(Integer.valueOf(lid)); + } + } + + Set allowedOrgunitsWithChildren = new HashSet<>(); + for (Integer lid : allowedOrgunitLids) { + allowedOrgunitsWithChildren.add(lid); + allowedOrgunitsWithChildren.addAll(fetchOrgunitChildren(lid)); } + + String lidsAsString = allowedOrgunitsWithChildren.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); + + return lidsAsString; } - public void applyOrgunitRestrictions(Integer userOrgunitLid) { + public void applyOrgunitRestrictions() { String tableJoinColSql = """ SELECT DISTINCT ft.tablename, d.joincolumn FROM metadata.dimension d LEFT JOIN metadata.facttable ft ON ft.id = d.facttable_id - WHERE d.tablename = 'dim_orgunit' - OR d.tablename = 'dim_orgunit_flat' + WHERE (d.tablename = 'dim_orgunit' OR d.tablename = 'dim_orgunit_flat') and d.is_restricted = true """; @@ -94,10 +153,10 @@ public class UserRestrictionService { continue; } String joinColumn = tableJoinColMap.get(tableName); - String restriction = buildOrgunitRestriction(userOrgunitLid, joinColumn); - if(!restriction.isEmpty()) { - insertRightsViewRestriction(dataSource, "presentation", tableName, restriction, userOrgunitLid.intValue()); - } + String restriction = buildOrgunitRestriction(user.getHisInOneOrgUnitLidOfRole(), joinColumn); + //if(!restriction.isEmpty()) { + insertRightsViewRestriction(dataSource, "presentation", tableName, joinColumn, restriction, user.getHisInOneOrgUnitLidOfRole().intValue()); + //} } } @@ -112,9 +171,16 @@ public class UserRestrictionService { String baseSql = BASE_QUERIES.get(tableName); String conditions = ""; conditions = buildConditionsForFacttable(topicAreaIds, topicIds); - resultSql = "WHERE id IN " + baseSql + resultSql = baseSql + (!conditions.isEmpty() ? " WHERE " + conditions : "") + ")"; - return resultSql; + + List> rows = jt.queryForList(resultSql); + String result = rows.stream() + .map(row -> row.get("id") != null ? row.get("id").toString() : "") + .filter(s -> !s.isEmpty()) + .map( id -> "'" + id + "'") + .collect(Collectors.joining(",")); + return result; } return ""; } @@ -145,7 +211,8 @@ public class UserRestrictionService { String restriction = ""; if(!allowedOrgunitsWithChildren.isEmpty()) { - restriction = "WHERE " + joinColumn + " IN (" + lidsAsString + ")"; + //restriction = "WHERE " + joinColumn + " IN (" + lidsAsString + ")"; + restriction = lidsAsString; } return restriction; } @@ -222,28 +289,29 @@ public class UserRestrictionService { return whereClause.toString(); } - private void insertRightsViewRestriction(DataSource ds, String schema, String table, String restriction, int orgunitLid) { + public void insertRightsViewRestriction(DataSource ds, String schema, String tableName, String joinColumn, String grantedIds, int orgunitLid) { String userName = user.getName(); String roleName = user.getRole(); int userId = user.getUserIdAsInt(); try (Connection conn = ds.getConnection(); Statement stmt = conn.createStatement()) { - String sql = "INSERT INTO public.view_rechte(user_id, user_name, " - + "role_name, orgunit_lid, schema_name, table_name, restriction, external_entry) " + - "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + String sql = "INSERT INTO public.user_dimension_grants(user_id, user_name, " + + "role_name, orgunit_lid, schema_name, table_name, join_column, granted_ids, external_entry) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, userId); pstmt.setString(2, userName); pstmt.setString(3, roleName); pstmt.setInt(4, orgunitLid); pstmt.setString(5, schema); - pstmt.setString(6, table); - pstmt.setString(7, restriction); - pstmt.setInt(8, 1); + pstmt.setString(6, tableName); + pstmt.setString(7, joinColumn); + pstmt.setString(8, grantedIds); + pstmt.setInt(9, 1); pstmt.execute(); } } catch (SQLException e) { - e.printStackTrace(); + throw new RuntimeException(e); } } @@ -257,6 +325,7 @@ public class UserRestrictionService { public List getRightParamValues(RightParam param, Right... rights) { if (user == null) return List.of(); + // Recht Rechteparameter, Wert des Rechteparameters Map> rightsMap = user.getRightsMap(); Map rightParams = findFirstValidRight(rightsMap, rights); @@ -269,12 +338,24 @@ public class UserRestrictionService { } private static Map findFirstValidRight(Map> rightsMap, Right... rights) { - return Arrays.stream(rights) - .map(Right::getString) - .map(rightsMap::get) - .filter(Objects::nonNull) - .findFirst() - .orElse(null); + List setRights = Arrays.stream(rights) + .filter(right -> rightsMap.get(right.getString()) != null) + .toList(); + + if (setRights.size() > 1) { + String setRightsString = setRights.stream() + .map(Right::getString) + .collect(Collectors.joining(", ")); + String exceptionMessage = "More than one of the supplied rights is set: " + setRightsString; + + throw new IllegalStateException(exceptionMessage); + } + + if (setRights.isEmpty()) { + return null; + } + + return rightsMap.get(setRights.get(0).getString()); } public static String quoteAndJoin(List strings) { @@ -286,11 +367,88 @@ public class UserRestrictionService { public void clearRestrictions() throws SQLException { try(Connection dbConnection = dataSource.getConnection()){ Statement stmt = dbConnection.createStatement(); - String sql = "DELETE FROM public.view_rechte WHERE user_name = '" + String sql = "DELETE FROM public.user_dimension_grants WHERE user_name = '" + user.getUsername() + "' AND role_name = '" + user.getRole() + "' AND external_entry = 1 AND orgunit_lid = " + user.getHisInOneOrgUnitLidOfRole().intValue(); stmt.execute(sql); } } + /* + * Die BI-Dimensionstabellen werden Nutzerbezogen zugriffsbeschränkt. + * Die Beschränkungen ergeben sich idR aus der Rollen-/Rechtekonfiguration in HISinOne. + * Diese Methode "übersetzt" dazwischen: + * Die aus HISinOne ausgelesenen Rechte werden in Listen von Dimensions-IDs pro + * Nutzer/Rolle/Tabelle überführt; das passiert idR beim Login des jeweiligen Nutzers + * (resp. Rollenwechsel) - evtl. kann es später noch weitere Auslöser geben. + * Durchgesetzt werden sie aktuell durch einsetzen der Listen in WHERE-Klauseln, + * die ins generierte SQL eingefügt werden umf die referenzierten DimensionIds in der + * Faktentabelle zu filtern. + * siehe Klasse DbSqlRestrictionProvider */ + public void calculateDimensionRestrictions(Integer userPersonId) { + if(userPersonId == null) { + // user is not a h1 user, e.g 'gast' + return; + } + // default ist: meine projekte + // + List params = getRightParamValues + (RightParam.PROJECT, Right.CREATE_ANALYSIS, Right.VIEW_REPORT); + + if(params.size() > 1) { + // TODO check if that is even possible using HI1 technology + throw new IllegalArgumentException("Es sollte nur einen Wert für den Rechteparameter 'bianalysis.project' geben."); + } + // 1 - für meine Projekte + // 2 - für alle Projekte + + if(!params.isEmpty() && params.get(0).equals("2")) { + // keine restriction für Projekte + return; + } + + String tableJoinColSql = """ + SELECT DISTINCT ft.tablename, d.joincolumn + FROM metadata.dimension d + LEFT JOIN metadata.facttable ft ON ft.id = d.facttable_id + WHERE d.tablename = 'dim_project' + and d.is_restricted = true + """; + + List> rows = jt.queryForList(tableJoinColSql); + + Map tableJoinColMap = rows.stream() + .collect(Collectors.toMap( + row -> (String) row.get("tablename"), + row -> (String) row.get("joincolumn"), + (existing, replacement) -> existing)); + + for (String tableName : tableJoinColMap.keySet()) { + if(tableName == null) { + continue; + } + String joinColumn = tableJoinColMap.get(tableName); + String sql = """ + SELECT pp.project_id + FROM staging.project_person AS pp + JOIN staging.project_person_position AS ppp + ON pp.id = ppp.project_person_id + JOIN staging.k_project_person_position AS kppp + ON ppp.k_project_person_position_id = kppp.id + WHERE kppp.hiskey_id = 1 + AND pp.person_id = ? + """; + + List grantedProjectIds = jt.queryForList(sql, Integer.class, userPersonId); + + String restriction = grantedProjectIds.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); + + insertRightsViewRestriction(dataSource, "presentation", tableName, joinColumn, restriction, user.getHisInOneOrgUnitLidOfRole().intValue()); + + } + + } + } diff --git a/src/de/superx/bianalysis/sqlgeneration/SQLGenerator.java b/src/de/superx/bianalysis/sqlgeneration/SQLGenerator.java index 3c3d108..a9575fc 100644 --- a/src/de/superx/bianalysis/sqlgeneration/SQLGenerator.java +++ b/src/de/superx/bianalysis/sqlgeneration/SQLGenerator.java @@ -394,20 +394,21 @@ public class SQLGenerator { if(reportMetadata.leftDimensionAttributes == null || reportMetadata.leftDimensionAttributes.size() <= 0) { return ""; } + + List validAttributes = new ArrayList<>(); + for (DimensionAttribute attr : reportMetadata.leftDimensionAttributes) { + if (!attr.isNullAttribute()) { + validAttributes.add(attr); + } + } + StringBuilder statement = new StringBuilder("GROUP BY GROUPING SETS ("); StringJoiner groups = new StringJoiner(", "); - for (int i = 0; i < reportMetadata.leftDimensionAttributes.size(); i++) { - DimensionAttribute a = reportMetadata.leftDimensionAttributes.get(i); - if(a.isNullAttribute()) { - continue; - } + for (int i = 0; i < validAttributes.size(); i++) { StringJoiner group = new StringJoiner(", "); - for (int j = 0; j < reportMetadata.leftDimensionAttributes.size() - i; j++) { - DimensionAttribute attr = reportMetadata.leftDimensionAttributes.get(j); - if(attr.isNullAttribute()) { - continue; - } + for (int j = 0; j < validAttributes.size() - i; j++) { + DimensionAttribute attr = validAttributes.get(j); group.add(attr.getDimensionTableAlias() + "." + attr.getColumnname()); if(attr.getSortOrderColumn() != null) { group.add(attr.getDimensionTableAlias() + "." + attr.getSortOrderColumn()); diff --git a/src/de/superx/bin/ComponentAdminCLI.java b/src/de/superx/bin/ComponentAdminCLI.java index fe4182e..88a532b 100644 --- a/src/de/superx/bin/ComponentAdminCLI.java +++ b/src/de/superx/bin/ComponentAdminCLI.java @@ -32,11 +32,11 @@ import de.superx.rest.model.job.Component; import de.superx.rest.model.job.JobExecutionStatus; import de.superx.servlet.SuperXManager; import de.superx.servlet.SxPools; +import de.superx.spring.AppConfig; import de.superx.spring.HisInOneConfiguration; import de.superx.spring.batch.His1DataSources; import de.superx.spring.batch.tasklet.ExecuteSqlScriptTasklet; import de.superx.spring.cli.config.CLIConfig; -import de.superx.spring.config.BatchConfig; import de.superx.spring.config.DataJdbcConfiguration; import de.superx.spring.config.ServiceConfig; import de.superx.spring.service.BatchJobDescriptionAdapter; @@ -56,7 +56,7 @@ import de.superx.util.PathAndFileUtils; * The class is just meant to be a frontend, so it uses the same implementation as the web application * (which happens to be the "EtlJobApi" bean) * However, there are to be some things to be considered: - * - The "jobLauncher" bean from the "BatchConfig" class works asynchronously, which is fine for the + * - The "jobLauncher" bean from the "AppConfig" class works asynchronously, which is fine for the * web application context, but in the CLI context, it is overridden by a synchronous implementation * (in fact the tweaks to get the configurations ready for the CLI context, are placed in the * "CLIConfig" class). @@ -409,7 +409,7 @@ public class ComponentAdminCLI { * - so it's alright to override some beans via "CLIConfig" */ if (APPLICATION_CONTEXT == null) { - APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class); + APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class); HisInOneConfiguration.configSuperXDbformsXML("pg", SuperXManager.getWEB_INFPfad() + File.separator + ".."); } // Override the JobDescriptionSource Bean if the -f flag is passed. diff --git a/src/de/superx/bin/DataProfiler.java b/src/de/superx/bin/DataProfiler.java index c37c429..04e1baf 100644 --- a/src/de/superx/bin/DataProfiler.java +++ b/src/de/superx/bin/DataProfiler.java @@ -45,9 +45,9 @@ import org.springframework.jdbc.core.RowMapper; import de.superx.servlet.SuperXManager; import de.superx.servlet.SxPools; +import de.superx.spring.AppConfig; import de.superx.spring.batch.His1DataSources; import de.superx.spring.cli.config.CLIConfig; -import de.superx.spring.config.BatchConfig; import de.superx.spring.config.DataJdbcConfiguration; import de.superx.spring.config.ServiceConfig; @@ -591,7 +591,7 @@ public class DataProfiler { * - so it's alright to override some beans via "CLIConfig" */ if (APPLICATION_CONTEXT == null) { - APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class); + APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class); } return APPLICATION_CONTEXT; } diff --git a/src/de/superx/bin/EtlFuzzer.java b/src/de/superx/bin/EtlFuzzer.java index b9db409..6d59b8e 100644 --- a/src/de/superx/bin/EtlFuzzer.java +++ b/src/de/superx/bin/EtlFuzzer.java @@ -43,9 +43,9 @@ import de.superx.rest.model.job.JobExecutionStatus; import de.superx.rest.model.job.StepExecutionStatus; import de.superx.servlet.SuperXManager; import de.superx.servlet.SxPools; +import de.superx.spring.AppConfig; import de.superx.spring.batch.His1DataSources; import de.superx.spring.cli.config.CLIConfig; -import de.superx.spring.config.BatchConfig; import de.superx.spring.config.DataJdbcConfiguration; @@ -270,7 +270,7 @@ public class EtlFuzzer { * - so it's alright to override some beans via "CLIConfig" */ if (APPLICATION_CONTEXT == null) { - APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class); + APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class); } return APPLICATION_CONTEXT; } diff --git a/src/de/superx/bin/ExecuteMask.java b/src/de/superx/bin/ExecuteMask.java index 685c503..59d926c 100644 --- a/src/de/superx/bin/ExecuteMask.java +++ b/src/de/superx/bin/ExecuteMask.java @@ -4,8 +4,6 @@ import static de.superx.servlet.ServletBasics.getParamChecked; import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.net.URISyntaxException; @@ -19,11 +17,9 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.StringTokenizer; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; @@ -49,10 +45,12 @@ import de.superx.servlet.SuperXManager; import de.superx.servlet.SxPools; import de.superx.servlet.UserInitializer; import de.superx.servlet.XmlTransformer; +import de.superx.spring.AppConfig; import de.superx.spring.cli.config.CLIConfig; -import de.superx.spring.config.BatchConfig; import de.superx.spring.config.DataJdbcConfiguration; import de.superx.spring.config.ServiceConfig; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import net.sf.jasperreports.engine.JRException; public class ExecuteMask { @@ -536,7 +534,7 @@ public class ExecuteMask { * - so it's alright to override some beans via "CLIConfig" */ if (APPLICATION_CONTEXT == null) { - APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class); + APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class); } return APPLICATION_CONTEXT; } diff --git a/src/de/superx/bin/FMParser.java b/src/de/superx/bin/FMParser.java index 201e942..cbe8580 100644 --- a/src/de/superx/bin/FMParser.java +++ b/src/de/superx/bin/FMParser.java @@ -1,233 +1,225 @@ -package de.superx.bin; - -import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.Properties; - -import org.apache.commons.io.FileUtils; -import org.apache.log4j.Level; - -import de.memtext.db.ConnectionCreator; -import de.memtext.util.DateUtils; -import de.memtext.util.GetOpts; -import de.memtext.util.GetOpts.Options; -import de.memtext.util.StringUtils; -import de.superx.common.FieldContainer; -import de.superx.common.Sichten; -import de.superx.common.StandaloneSicht; -import de.superx.common.SuperX_el; -import de.superx.common.SxResultRow; -import de.superx.common.SxResultSet; -import de.superx.common.SxSqlHelper; -import de.superx.common.SxUser; -import de.superx.common.TableFieldExists; -import de.superx.common.TemplateProcessor; -import de.superx.servlet.SxPools; -import de.superx.util.PropsReader; -import de.superx.util.SqlStringUtils; - -public class FMParser extends TemplateProcessor { - private String dbprop, infile, outfile; - - private Properties props; - - private Statement stm; - - private String isSimpleParser; - private String myParams=""; - private static String sqlDialect; - - public FMParser(String mandantenId, Connection con) throws SQLException { - super(mandantenId, con); - if (con.getMetaData().getDatabaseProductName().toLowerCase().contains("postgres")) { - sqlDialect = "Postgres"; - } - } - - public FMParser(String mandantenId) { - super(mandantenId); - } - - private void run() throws FileNotFoundException, IOException, Exception { - dbprop = GetOpts.getValue(Options.opt_dbprops); - props = PropsReader.prepareProps(new File(dbprop)); - infile = GetOpts.getValue(Options.opt_in); - outfile = GetOpts.getValue(Options.opt_out); - if (GetOpts.isPresent(Options.opt_simpPars)) - isSimpleParser = GetOpts.getValue(Options.opt_simpPars); - if (GetOpts.isPresent(Options.opt_params)) - myParams = GetOpts.getValue(Options.opt_params); - String input = StringUtils.readFile(new File(infile)); - if(myParams !="") - { - String paramName = "<<" +myParams.substring(0, myParams.indexOf("="))+">>"; - String paramVal = "'"+myParams.substring(myParams.indexOf("=") + 1)+"'"; - input=de.memtext.util.StringUtils.replace(input, paramName, paramVal); - } - String output = ""; - if (isSimpleParser != null && isSimpleParser.equalsIgnoreCase("true")) { - output = simpleParser(dbprop, input); - } else { - SxPools.initTesting(props); - initConAndParser(dbprop); - sqlDialect = de.superx.util.SqlStringUtils.getSqlDialect(props.getProperty("driverName")); - HashMap map = new HashMap(); - map.put("TableFieldExists", new TableFieldExists(con)); - output = "--automatically created by SuperX/Freemarker for " + sqlDialect + " (" - + DateUtils.getTodayString() + " " + DateUtils.getNowString() + ")\n" - + process("FM-Parsing " + infile, input, map, sqlDialect); - stm.close(); - con.close(); - } - FileUtils.writeStringToFile(new File(outfile), output); - } - - private void initConAndParser(String dbprop) throws IOException, SQLException, ClassNotFoundException { - super.con = ConnectionCreator.getConnectionCryptPassword(dbprop, "driverName", "connectionURL", - "connectionName", "connectionPassword"); - stm = con.createStatement(); - if (SqlStringUtils.tableExists(con, "fm_templates", mandantenID)) { - setTemplates(readFromDb("select trim(both from id),content from fm_templates")); - repositoryToMap(readFromDb(REPOSITORY_SELECT), repositoryMap); - } - if (SqlStringUtils.tableExists(con, "konstanten", mandantenID)) { - SxResultSet rs = readFromDb("select trim(both from beschreibung),apnr from konstanten"); - for (Iterator it = rs.iterator(); it.hasNext();) { - SxResultRow row = (SxResultRow) it.next(); - String beschreibung = (String) row.get(0); - repositoryMap.put("K_" + beschreibung.trim(), row.get(1)); - } - } - } - - @Override - protected SxResultSet readFromDb(String sql) throws SQLException { - SuperX_el el = new SuperX_el(); - SxSqlHelper sh = new SxSqlHelper(); - sh.execute(sql, this.con, el); - if (el.getError_String() != null && !el.getError_String().trim().equals("")) { - throw new SQLException("\nProblem bei:" + "\n\n Meldung:" + el.getError_String() + "\n sql:" + sql); - } - return el.getResultSet(); - } - - /** - * @deprecated - * @param sql - * @return - * @throws SQLException - */ - @Deprecated - protected SxResultSet readFromDbAlt(String sql) throws SQLException { - SxResultSet result = new SxResultSet(); - ResultSet rs = stm.executeQuery(sql); - - result.setColumnNames(rs.getMetaData()); - int naturalOrder = 0; - while (rs.next()) { - SxResultRow row = new SxResultRow(rs.getMetaData().getColumnCount(), naturalOrder++); - for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { - if (rs.getMetaData().getColumnType(i) == java.sql.Types.LONGVARCHAR) - row.add(rs.getString(i)); - else - row.add(rs.getObject(i)); - } - result.add(row); - } - rs.close(); - return result; - } - - public static void main(String[] args) { - GetOpts.setOpts(args); - String isdrin = GetOpts.isAllRequiredOptionsPresent(new Options[]{Options.opt_dbprops, Options.opt_in, Options.opt_out}); - if (isdrin != null) { - System.err.println("-dbproperties: -in: -out:"); - System.exit(1); - } - try { - new FMParser(DEFAULT_MANDANTEN_ID).run(); - } catch (Exception e) { - System.err.println("Fehler beim FM-Parsen "); - e.printStackTrace(); - System.exit(1); - } - } - - /* - * Einfacher Parser für Fremddatenbanken, nur sqlDialect wird übergeben wird von - * DOSQL direkt aufgerufen - */ - public static String simpleParser(String dbprop, String input) - throws FileNotFoundException, IOException, Exception { - Properties props = PropsReader.prepareProps(new File(dbprop)); - FMParser myParser=new FMParser(DEFAULT_MANDANTEN_ID); - myParser.initConAndParser(dbprop); - String output = ""; - sqlDialect = de.superx.util.SqlStringUtils.getSqlDialect(props.getProperty("driverName")); - HashMap map = new HashMap(); - try { - output = "--automatically created by SuperX/Freemarker for " + sqlDialect + " (" - + DateUtils.getTodayString() + " " + DateUtils.getNowString() + ")\n" - + myParser.process("FM-Parsing ", input, map, sqlDialect); - } catch (Exception e) { - System.err.println("Fehler beim FM-Parsen "); - e.printStackTrace(); - System.exit(1); - } - return output; - } - - /** - * Methode war ursprünglich für direktes Einspielen einer einzelnen konkret mit - * name_intern benannten Sicht. Jetzt erweitert, dass aufgebaute Sicht auch - * zurückgeliefert wird, damit eine sichtsequence aufgebaut werden kann - */ - @Override - protected StandaloneSicht addSqlVarSicht(HashMap hmap, String varname, String name_intern, String gewuenschterStand) - throws SQLException { - String sql = "select tid,name,label,'',beschreibung,quelle,standbutton,art \n" - + " ,type,alt_hier_id,treecfgtable,treecfgid,gueltig_seit,gueltig_bis,xmlmaxentries,name_intern,attribut1,attribut2,attribut3,cacheapplet,cachexml \n" - + "from sichten where name_intern='" - + name_intern - + "' and aktiv=1 order by type"; - SxResultSet roh = new SxResultSet(); - StandaloneSicht sicht = null; - roh = readFromDb(sql); - Sichten sichten = new Sichten(); - SxUser user = new SxUser(); - user.setAdmin(true); // Adminuser ok weil nur von DOSQL gebraucht - sichten.setUser(user); - for (Iterator iter = roh.iterator(); iter.hasNext();) { - SxResultRow row = (SxResultRow) iter.next(); - Integer id = Integer.valueOf(1); - try { - sichten.initNonInternalSicht(DEFAULT_MANDANTEN_ID, StandaloneSicht.class, row, id); - sicht = (StandaloneSicht) sichten.getById(id); - sicht.setConnection(con); - sicht.setSqlDialect(sqlDialect); - if (gewuenschterStand.trim().startsWith("today")) { - gewuenschterStand = DateUtils.getTodayString(); - } - sicht.setStand(new Hashtable(), hmap, new FieldContainer(), gewuenschterStand); - hmap.put(varname, sicht); - } catch (Exception e) { - e.printStackTrace(); - throw new SQLException("Fehler beim Aufbau von Sicht " + name_intern + " " + e); - } - } - return sicht; - } -} - -// Created on 08.12.2006 at 18:03:46 +package de.superx.bin; + +import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Properties; + +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Level; + +import de.memtext.db.ConnectionCreator; +import de.memtext.util.DateUtils; +import de.memtext.util.GetOpts; +import de.memtext.util.GetOpts.Options; +import de.memtext.util.StringUtils; +import de.superx.common.FieldContainer; +import de.superx.common.Sichten; +import de.superx.common.StandaloneSicht; +import de.superx.common.SuperX_el; +import de.superx.common.SxResultRow; +import de.superx.common.SxResultSet; +import de.superx.common.SxSqlHelper; +import de.superx.common.SxUser; +import de.superx.common.TableFieldExists; +import de.superx.common.TemplateProcessor; +import de.superx.servlet.SxPools; +import de.superx.util.PropsReader; +import de.superx.util.SqlStringUtils; + +public class FMParser extends TemplateProcessor { + private String dbprop, infile, outfile; + + private Properties props; + + private Statement stm; + + private String isSimpleParser; + + private static String sqlDialect; + + public FMParser(String mandantenId, Connection con) throws SQLException { + super(mandantenId, con); + if (con.getMetaData().getDatabaseProductName().toLowerCase().contains("postgres")) { + sqlDialect = "Postgres"; + } + } + + public FMParser(String mandantenId) { + super(mandantenId); + } + + private void run() throws FileNotFoundException, IOException, Exception { + dbprop = GetOpts.getValue(Options.opt_dbprops); + props = PropsReader.prepareProps(new File(dbprop)); + infile = GetOpts.getValue(Options.opt_in); + outfile = GetOpts.getValue(Options.opt_out); + if (GetOpts.isPresent(Options.opt_simpPars)) + isSimpleParser = GetOpts.getValue(Options.opt_simpPars); + String input = StringUtils.readFile(new File(infile)); + String output = ""; + if (isSimpleParser != null && isSimpleParser.equalsIgnoreCase("true")) { + output = simpleParser(dbprop, input); + } else { + SxPools.initTesting(props); + initConAndParser(dbprop); + sqlDialect = de.superx.util.SqlStringUtils.getSqlDialect(props.getProperty("driverName")); + HashMap map = new HashMap(); + map.put("TableFieldExists", new TableFieldExists(con)); + output = "--automatically created by SuperX/Freemarker for " + sqlDialect + " (" + + DateUtils.getTodayString() + " " + DateUtils.getNowString() + ")\n" + + process("FM-Parsing " + infile, input, map, sqlDialect); + stm.close(); + con.close(); + } + FileUtils.writeStringToFile(new File(outfile), output); + } + + private void initConAndParser(String dbprop) throws IOException, SQLException, ClassNotFoundException { + super.con = ConnectionCreator.getConnectionCryptPassword(dbprop, "driverName", "connectionURL", + "connectionName", "connectionPassword"); + stm = con.createStatement(); + if (SqlStringUtils.tableExists(con, "fm_templates", mandantenID)) { + setTemplates(readFromDb("select trim(both from id),content from fm_templates")); + repositoryToMap(readFromDb(REPOSITORY_SELECT), repositoryMap); + } + if (SqlStringUtils.tableExists(con, "konstanten", mandantenID)) { + SxResultSet rs = readFromDb("select trim(both from beschreibung),apnr from konstanten"); + for (Iterator it = rs.iterator(); it.hasNext();) { + SxResultRow row = (SxResultRow) it.next(); + String beschreibung = (String) row.get(0); + repositoryMap.put("K_" + beschreibung.trim(), row.get(1)); + } + } + } + + @Override + protected SxResultSet readFromDb(String sql) throws SQLException { + SuperX_el el = new SuperX_el(); + SxSqlHelper sh = new SxSqlHelper(); + sh.execute(sql, this.con, el); + if (el.getError_String() != null && !el.getError_String().trim().equals("")) { + throw new SQLException("\nProblem bei:" + "\n\n Meldung:" + el.getError_String() + "\n sql:" + sql); + } + return el.getResultSet(); + } + + /** + * @deprecated + * @param sql + * @return + * @throws SQLException + */ + @Deprecated + protected SxResultSet readFromDbAlt(String sql) throws SQLException { + SxResultSet result = new SxResultSet(); + ResultSet rs = stm.executeQuery(sql); + + result.setColumnNames(rs.getMetaData()); + int naturalOrder = 0; + while (rs.next()) { + SxResultRow row = new SxResultRow(rs.getMetaData().getColumnCount(), naturalOrder++); + for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { + if (rs.getMetaData().getColumnType(i) == java.sql.Types.LONGVARCHAR) + row.add(rs.getString(i)); + else + row.add(rs.getObject(i)); + } + result.add(row); + } + rs.close(); + return result; + } + + public static void main(String[] args) { + GetOpts.setOpts(args); + String isdrin = GetOpts.isAllRequiredOptionsPresent(new Options[]{Options.opt_dbprops, Options.opt_in, Options.opt_out}); + if (isdrin != null) { + System.err.println("-dbproperties: -in: -out:"); + System.exit(1); + } + try { + new FMParser(DEFAULT_MANDANTEN_ID).run(); + } catch (Exception e) { + System.err.println("Fehler beim FM-Parsen "); + e.printStackTrace(); + System.exit(1); + } + } + + /* + * Einfacher Parser für Fremddatenbanken, nur sqlDialect wird übergeben wird von + * DOSQL direkt aufgerufen + */ + public static String simpleParser(String dbprop, String input) + throws FileNotFoundException, IOException, Exception { + Properties props = PropsReader.prepareProps(new File(dbprop)); + FMParser myParser=new FMParser(DEFAULT_MANDANTEN_ID); + myParser.initConAndParser(dbprop); + String output = ""; + sqlDialect = de.superx.util.SqlStringUtils.getSqlDialect(props.getProperty("driverName")); + HashMap map = new HashMap(); + try { + output = "--automatically created by SuperX/Freemarker for " + sqlDialect + " (" + + DateUtils.getTodayString() + " " + DateUtils.getNowString() + ")\n" + + myParser.process("FM-Parsing ", input, map, sqlDialect); + } catch (Exception e) { + System.err.println("Fehler beim FM-Parsen "); + e.printStackTrace(); + System.exit(1); + } + return output; + } + + /** + * Methode war ursprünglich für direktes Einspielen einer einzelnen konkret mit + * name_intern benannten Sicht. Jetzt erweitert, dass aufgebaute Sicht auch + * zurückgeliefert wird, damit eine sichtsequence aufgebaut werden kann + */ + @Override + protected StandaloneSicht addSqlVarSicht(HashMap hmap, String varname, String name_intern, String gewuenschterStand) + throws SQLException { + String sql = "select tid,name,label,'',beschreibung,quelle,standbutton,art \n" + + " ,type,alt_hier_id,treecfgtable,treecfgid,gueltig_seit,gueltig_bis,xmlmaxentries,name_intern,attribut1,attribut2,attribut3,cacheapplet,cachexml \n" + + "from sichten where name_intern='" + + name_intern + + "' and aktiv=1 order by type"; + SxResultSet roh = new SxResultSet(); + StandaloneSicht sicht = null; + roh = readFromDb(sql); + Sichten sichten = new Sichten(); + SxUser user = new SxUser(); + user.setAdmin(true); // Adminuser ok weil nur von DOSQL gebraucht + sichten.setUser(user); + for (Iterator iter = roh.iterator(); iter.hasNext();) { + SxResultRow row = (SxResultRow) iter.next(); + Integer id = Integer.valueOf(1); + try { + sichten.initNonInternalSicht(DEFAULT_MANDANTEN_ID, StandaloneSicht.class, row, id); + sicht = (StandaloneSicht) sichten.getById(id); + sicht.setConnection(con); + sicht.setSqlDialect(sqlDialect); + if (gewuenschterStand.trim().startsWith("today")) { + gewuenschterStand = DateUtils.getTodayString(); + } + sicht.setStand(new Hashtable(), hmap, new FieldContainer(), gewuenschterStand); + hmap.put(varname, sicht); + } catch (Exception e) { + e.printStackTrace(); + throw new SQLException("Fehler beim Aufbau von Sicht " + name_intern + " " + e); + } + } + return sicht; + } +} + +// Created on 08.12.2006 at 18:03:46 diff --git a/src/de/superx/bin/fm/EtlStarter.java b/src/de/superx/bin/fm/EtlStarter.java index 00a903b..1772801 100644 --- a/src/de/superx/bin/fm/EtlStarter.java +++ b/src/de/superx/bin/fm/EtlStarter.java @@ -39,6 +39,7 @@ import org.pentaho.metastore.stores.memory.MemoryMetaStore; import de.superx.servlet.SuperXManager; import de.superx.servlet.SxPools; import de.superx.spring.batch.His1DataSources; +import de.superx.spring.service.ApplicationContextHolder; import freemarker.template.SimpleScalar; import freemarker.template.TemplateModelException; import freemarker.template.TemplateTransformModel; @@ -195,7 +196,7 @@ public class EtlStarter implements TemplateTransformModel { * get all db connections configured in databases.xml as * DataSource's by name */ - His1DataSources dataSources = SuperXManager.getBean("dataSources", His1DataSources.class); + His1DataSources dataSources = ApplicationContextHolder.getBean("dataSources", His1DataSources.class); // if no dataSources defined, at least try eduetl Set dbNames = dataSources != null ? dataSources.getKeys() : Set.of("eduetl"); for (String dbName : dbNames) { diff --git a/src/de/superx/bin/fm/SqlVarsDirective.java b/src/de/superx/bin/fm/SqlVarsDirective.java index 82df35c..cd3f38e 100644 --- a/src/de/superx/bin/fm/SqlVarsDirective.java +++ b/src/de/superx/bin/fm/SqlVarsDirective.java @@ -18,7 +18,7 @@ import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.support.rowset.SqlRowSet; import de.superx.common.TemplateProcessor.SqlVarType; -import de.superx.servlet.SuperXManager; +import de.superx.spring.service.ApplicationContextHolder; import freemarker.core.Environment; import freemarker.template.ObjectWrapper; import freemarker.template.Template; @@ -74,7 +74,7 @@ public class SqlVarsDirective implements TemplateDirectiveModel { } JdbcTemplate jt = new JdbcTemplate(); - DataSource dataSource = SuperXManager.getBean("dataSource", DataSource.class); + DataSource dataSource = ApplicationContextHolder.getBean("dataSource", DataSource.class); jt.setDataSource(dataSource); Object result = null; diff --git a/src/de/superx/common/FieldXmlEncoder.java b/src/de/superx/common/FieldXmlEncoder.java index 89ba109..2c358a3 100644 --- a/src/de/superx/common/FieldXmlEncoder.java +++ b/src/de/superx/common/FieldXmlEncoder.java @@ -1,6 +1,7 @@ package de.superx.common; import java.util.Enumeration; +import java.util.List; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; @@ -16,11 +17,20 @@ public class FieldXmlEncoder { * * @return */ - public static String toXml(Field f, boolean isFullXMLRequired, int rownr) { + public static String toXml(Field f, boolean isFullXMLRequired, int rownr, + List dependentOnFields) { StringBuffer result = new StringBuffer("\n"); result.append(getBasics(f)); appendSichtgruppe(f, result); appendRelation(f, isFullXMLRequired, result); + if(dependentOnFields!=null && dependentOnFields.size()>0) + { + result.append("\n"); + for (int i = 0; i < dependentOnFields.size(); ++i) { + result.append(""+dependentOnFields.get(i)+""); + } + result.append(""); + } // result.append(" Nutzungsprotokolle (intern) result.append("" + rownr + "\n"); result.append("\n"); @@ -44,7 +54,7 @@ public class FieldXmlEncoder { if (param == null) throw new IllegalArgumentException("Param null"); StringBuffer result = new StringBuffer(); if (param.equalsIgnoreCase("!all")) - result.append(toXml(f, true, 0));// bei Ajax-nachladen wird rownr + result.append(toXml(f, true, 0,null));// bei Ajax-nachladen wird rownr // nicht benötigt else { result.append("\n"); diff --git a/src/de/superx/common/Maske.java b/src/de/superx/common/Maske.java index c0a2b72..c7b1646 100644 --- a/src/de/superx/common/Maske.java +++ b/src/de/superx/common/Maske.java @@ -981,11 +981,13 @@ public class Maske extends NamedIdObject { } result.append(""); + for (Iterator it = individualFields.iterator(); it.hasNext();) { Field feld = (Field) it.next(); - + List dependentOnFields=getDependentOnFields(feld); + if (feld.getY() == 0) rownr++; - result.append(FieldXmlEncoder.toXml(feld, isFullXMLRequired, rownr)); + result.append(FieldXmlEncoder.toXml(feld, isFullXMLRequired, rownr,dependentOnFields)); } result.append("" + rownr + "\n"); // derzeit wird nur das erste Stylesheet zur Liste hinzugefügt @@ -1001,6 +1003,25 @@ public class Maske extends NamedIdObject { return result; } + /* Feldabhängigkeiten pro Maske abfragen, also ob ein Maskenfeld in der Spalte "relation" + * auf ein anderes Maskenfeld verweist. + * Wird nachher im XML-Strom im Element abgelegt */ + private List getDependentOnFields(Field fld) + { + List dependentOnFields = new ArrayList(); + String relation=fld.getRelation(); + if(relation !=null) { + for (Iterator it = individualFields.iterator(); it.hasNext();) { + Field feld = (Field) it.next(); + + if(relation.indexOf("<<"+feld.getName()+">>")>-1) + { + dependentOnFields.add(feld.getName()); + } + } + } + return dependentOnFields; + } /** * Bereitet aus der Maske in XML-Format einen HTML-String vor, der * zurückgeliefert werden kann. Oder aktualisiert diesen. diff --git a/src/de/superx/common/SxUser.java b/src/de/superx/common/SxUser.java index d918df5..eda88ec 100644 --- a/src/de/superx/common/SxUser.java +++ b/src/de/superx/common/SxUser.java @@ -32,18 +32,21 @@ import de.memtext.rights.Rights; import de.memtext.util.CryptUtils; import de.memtext.util.DateUtils; import de.superx.servlet.ServletUtils; +import de.superx.servlet.SuperXManager; +import de.superx.servlet.SxSQL_Server; import de.superx.servlet.Themenbaum; +import de.superx.util.PathAndFileUtils; import freemarker.template.TemplateBooleanModel; public class SxUser extends User implements Serializable, UserDetails, GrantedAuthoritiesContainer { - public static final String USER_ROLE_PLACEHOLDER = "UserRole"; + public static final String USER_ROLE_PLACEHOLDER = "UserRole"; public static final String USER_ROLE_ID_PLACEHOLDER = "UserRoleId"; public static final String USER_HISINONE_ORGUNIT_LID_PLACEHOLDER = "UserHISinOneOrgunitLid"; - private final UUID uuid = UUID.randomUUID(); - + private final UUID uuid = UUID.randomUUID(); + private Themenbaum themenbaum = new Themenbaum(); private String mandantenID = DEFAULT_MANDANTEN_ID; private Sichten sichten = new Sichten(); @@ -52,16 +55,16 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu private Collection groupnames = new LinkedList(); private Collection sachgebiete = new LinkedList(); private String[] oldShas = new String[7]; - private String currentSha=""; + private String currentSha = ""; private SxFinRechte finRechte = new SxFinRechte(); private HashMap basicMap = new HashMap(); private SelectionBuffer sb = new SelectionBuffer(); -// private Set maskenEditRights = new HashSet<>(); private boolean hasMaskenEditRights = false; private Collection hiddenFields = new LinkedList(); private Map> rightsMap = new HashMap>(); - private String role=""; + private String role = ""; private Integer roleId; + private Integer userPersonId; private Integer hisInOneOrgUnitLidOfRole; private Set allowedOrgUnits = new TreeSet(); private Set implicitlyAllowedOrgUnits = new TreeSet(); @@ -85,15 +88,15 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu public SxUser(String username, Integer userid) throws SQLException, DBServletException, TransformerException { super(username, userid); init(); - } - public SxUser(String username, Integer userid, String mandantenId) throws SQLException, DBServletException, TransformerException { + public SxUser(String username, Integer userid, String mandantenId) + throws SQLException, DBServletException, TransformerException { super(username, userid); this.mandantenID = mandantenId; init(); } - + /** * Nur für Testfälle - bei Echtbetrieb wird ID benötigt!! * @@ -107,7 +110,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu sichten.setUser(this); basicMap.put("SxFinRechte", finRechte); basicMap.put("SxKontierungsrechte", sxKontierungsrechte); - basicMap.put("Username", getName()); basicMap.put("today", DateUtils.getTodayString()); basicMap.put("yeartoday", DateUtils.getYear(new java.sql.Date(new java.util.Date().getTime()))); @@ -131,8 +133,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu return ((Integer) getId()).intValue(); } - - public SelectionBuffer getSelectionBuffer() { return sb; } @@ -154,8 +154,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu return basicMap; } - public Themenbaum getThemenbaum() - { + public Themenbaum getThemenbaum() { return themenbaum; } @@ -166,12 +165,20 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu public void setUserid(Integer id) { super.setId(id); } - + public void setRoleId(Integer id) { this.roleId = id; basicMap.put(USER_ROLE_ID_PLACEHOLDER, roleId); } + public void setUserPersonId(Integer id) { + this.userPersonId = id; + } + + public Integer getUserPersonId() { + return userPersonId; + } + public String getRole() { return role != null ? role : ""; } @@ -187,7 +194,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu public void clearCache() { sichten.clear(); } - + public void initErlaubteMasken(List maskenTids) { erlaubteMaskenTids.addAll(maskenTids); basicMap.put("UserMaskRights", getMaskRights()); @@ -208,21 +215,24 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu * geworfen */ public void checkAuthentificationForMask(String tidStr) { - if (tidStr == null) throw new IllegalStateException("Kann Maske nicht öffnen DETAILS:tid parameter war null"); - if (!isMaskAllowed(tidStr)) throw new IllegalArgumentException("Sie haben keine Berechtigung für Maske mit ID-Nummer " + tidStr); - + if (tidStr == null) { + throw new IllegalStateException("Kann Maske nicht öffnen DETAILS:tid parameter war null"); + } + if (!isMaskAllowed(tidStr)) { + throw new IllegalArgumentException("Sie haben keine Berechtigung für Maske mit ID-Nummer " + tidStr); + } } boolean isMaskAllowed(String tidStr) { boolean result = false; - if (isAdmin()) + if (isAdmin()) { result = true; - else + } else { try { result = erlaubteMaskenTids.contains(Integer.valueOf(tidStr)); } catch (Exception e) { } - + } return result; } @@ -235,7 +245,9 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu String sha1 = CryptUtils.encodeSHA(check); String sha2 = CryptUtils.encodeSHA(check + CryptUtils.geheimnis1); for (int i = 0; i < 7; i++) { - if (oldShas[i] != null && (sha1.equals(oldShas[i]) || sha2.equals(oldShas[i]))) result = true; + if (oldShas[i] != null && (sha1.equals(oldShas[i]) || sha2.equals(oldShas[i]))) { + result = true; + } } return result; } @@ -243,7 +255,9 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu public boolean isPWSHAUsedBefore(String sha) { boolean result = false; for (int i = 0; i < 7; i++) { - if (oldShas[i] != null && sha.equals(oldShas[i])) result = true; + if (oldShas[i] != null && sha.equals(oldShas[i])) { + result = true; + } } return result; } @@ -275,17 +289,20 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu public String getUser_PW_Command(int gueltigkeit) { String gueltig = "'1.1.3000'"; if (gueltigkeit > 0) { - gueltig = "'" + DateUtils.formatGerman(DateUtils.makeFutureDate(new java.sql.Date(new java.util.Date().getTime()), gueltigkeit)) + "'"; - + gueltig = "'" + + DateUtils.formatGerman( + DateUtils.makeFutureDate(new java.sql.Date(new java.util.Date().getTime()), gueltigkeit)) + + "'"; } String result = "delete from user_pw where userinfo_id='" + this.getId() - + "'; insert into user_pw (userinfo_id,pw_gueltig_bis,old_sha1,old_sha2,old_sha3,old_sha4,old_sha5,old_sha6,old_sha7) values ('" + getId() + "',date_val(" - + gueltig + ")"; + + "'; insert into user_pw (userinfo_id,pw_gueltig_bis,old_sha1,old_sha2,old_sha3,old_sha4,old_sha5,old_sha6,old_sha7) values ('" + + getId() + "',date_val(" + gueltig + ")"; for (int i = 0; i < 7; i++) - if (oldShas[i] == null) + if (oldShas[i] == null) { result += ",null"; - else + } else { result += ",'" + oldShas[i] + "'"; + } result += ")"; return result; } @@ -298,26 +315,24 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu this.currentSha = sha; } - public void initFinRechte(SxResultSet rs, int finRightVariant, SxResultSet rsProjToInst, boolean is0FINJoker, HashMap repository) { + public void initFinRechte(SxResultSet rs, int finRightVariant, SxResultSet rsProjToInst, boolean is0FINJoker, + HashMap repository) { finRechte.setVariant(finRightVariant); finRechte.setRepository(repository); finRechte.set0FINJoker(is0FINJoker); - if (isAdmin()) finRechte.setHasAllRights(true); + if (isAdmin()) { + finRechte.setHasAllRights(true); + } for (Iterator it = rs.iterator(); it.hasNext();) { SxResultRow row = (SxResultRow) it.next(); finRechte.createAndAddEntry(row); - } for (Iterator it = rsProjToInst.iterator(); it.hasNext();) { SxResultRow row = (SxResultRow) it.next(); finRechte.addProj((String) row.get(0)); - } - } - - public void addGruppe(String groupid, String groupname) { groups.add(groupid); groupnames.add(groupname); @@ -332,7 +347,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu basicMap.put("IsUserInGroup", new IsUserInGroup(this)); basicMap.put("IsUserInGroupWithName", new IsUserInGroupWithName(this)); basicMap.put("IsUserInAtLeastOneGroup", new IsUserInAtLeastOneGroup(this)); - } public String getSachgebiete() { @@ -342,7 +356,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu } return result.toString(); } - + public String getSachgebieteIn() { StringBuffer result = new StringBuffer(); for (Iterator it = sachgebiete.iterator(); it.hasNext();) { @@ -371,7 +385,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu } public boolean isInGroup(String group) { - return groups.contains(group); } @@ -390,22 +403,17 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu hiddenFields.add(row.get(0)); } } - + public void setMaskenEditRights(SxResultSet rs) { -// for (Iterator it = rs.iterator(); it.hasNext();) { -// SxResultRow row = (SxResultRow) it.next(); -// maskenEditRights.add((String)row.get(0)); -// } Iterator it = rs.iterator(); if (it.hasNext()) { SxResultRow row = (SxResultRow) it.next(); Object o = row.get(0); - this.hasMaskenEditRights = ((Number)o).intValue() > 0; + this.hasMaskenEditRights = ((Number) o).intValue() > 0; } } public boolean isFieldHidingWanted(Object id) { - // System.out.println(id+":"+hiddenFields.contains(id)); return hiddenFields.contains(id); } @@ -486,7 +494,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu return SxUser.this.role; } }); - + // add rights for (String right : rightsMap.keySet()) { authorities.add(new GrantedAuthority() { @@ -496,7 +504,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu } }); } - return authorities; } @@ -510,29 +517,28 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu }); } - private String getRoleString() - { - String rolle="standard"; //ohne HisInOne - if (getRoles().size()>0) - rolle=StringUtils.join(this.getRoles(), ", "); + private String getRoleString() { + String rolle = "standard"; // ohne HisInOne + if (getRoles().size() > 0) { + rolle = StringUtils.join(this.getRoles(), ", "); + } return rolle; } - public void initKontierungsrechte() throws SQLException, DBServletException { - if (isAdmin()) + public void initKontierungsrechte() throws SQLException, DBServletException { + if (isAdmin()) { sxKontierungsrechte.setHasAllRights(true); - else { + } else { sxKontierungsrechte.clearAll(); SxResultSet rs = ServletUtils - .executeALL_el(null, null, "Einlesen von Kontierungsrechten für Benutzer " + getId(), SxKontierungsrechte.readingSQL + getId(), getMandantenID(), true) - .getResultSet(); + .executeALL_el(null, null, "Einlesen von Kontierungsrechten für Benutzer " + getId(), + SxKontierungsrechte.readingSQL + getId(), getMandantenID(), true) + .getResultSet(); for (Iterator it = rs.iterator(); it.hasNext();) { SxResultRow row = (SxResultRow) it.next(); sxKontierungsrechte.createAndAddEntry(row); - } } - } public Integer getHisInOneOrgUnitLidOfRole() { @@ -541,7 +547,8 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu public void setHisInOneOrgUnitLidOfRole(Integer lid) { this.hisInOneOrgUnitLidOfRole = lid; - // remove if 'null': behaves as if 'unset' in Freemarker an homebrew variable replacement + // remove if 'null': behaves as if 'unset' in Freemarker an homebrew variable + // replacement basicMap.remove(USER_HISINONE_ORGUNIT_LID_PLACEHOLDER); if (lid != null) { basicMap.put(USER_HISINONE_ORGUNIT_LID_PLACEHOLDER, lid.toString()); @@ -553,7 +560,9 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu } /** - * Saiku braucht alle Schlüssel einzeln sowie explizit Unbekannt und Leer falls Berechtigung da + * Saiku braucht alle Schlüssel einzeln sowie explizit Unbekannt und Leer falls + * Berechtigung da + * * @return */ public Set getAllAllowedOrgUnitsForSaiku() { @@ -564,25 +573,25 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu orgUnitRights.addLiveMappingRights(optionalLogMessages, mandantenID); if (hasAllOrgUnitRights()) { // Berechtigung auf alle orgunits -> Leer/Unbekannt hinzufügen siehe #121106 orgUnitRights.add("Unbekannt"); - orgUnitRights.add("-999990"); //"Leer" + orgUnitRights.add("-999990"); // "Leer" } return orgUnitRights.getAllRightKeys(); } - public String getUUID() - { - return uuid.toString(); + public String getUUID() { + return uuid.toString(); } + @Override public int hashCode() { - return uuid.hashCode(); - } + return uuid.hashCode(); + } + @Override - public boolean equals(Object o) - { - return hashCode()==o.hashCode(); + public boolean equals(Object o) { + return hashCode() == o.hashCode(); } - + @Override public String toString() { return super.toString() + " (" + getRoleString() + ")"; @@ -610,27 +619,108 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu public boolean hasAllOrgUnitRights() { boolean result = false; - if (isAdmin()) + if (isAdmin()) { result = true; - else { + } else { for (OrgUnit ou : allowedOrgUnits) { - if (ou.getApnr().contentEquals("0")) result = true; + if (ou.getApnr().contentEquals("0")) { + result = true; + } } } return result; } - + + /* + * Es kann wohl passieren, dass im Betrieb die Komponentenupgrades nicht gemacht werden. + * Diese sind aber notwendig für die korrekte Zuordnung von dbforms zu Sachgebieten und + * somit auch für die korrekte Autorisierung. + * Mit dieser Methode kann geprüft werden, ob die Komponente, zu der ein dbForms gehört, + * aktuell ist. + */ + private boolean isModuleUpdated(String dbFormName) { + String lookUpSql = """ + SELECT + dv.his_system, + trim(df.name) AS dbforms_name, + trim(df.form_path) AS dbforms_path, + trim(st.name) AS table_name, + dv.version_hash + FROM + db_forms AS df + JOIN sx_tables st ON df.tablename = st.name + JOIN db_version dv ON st.systeminfo_id = dv.systeminfo_id + WHERE trim(df.name) = '""" + dbFormName + "';"; + try { + SxResultSet result = ServletUtils.execute("Berechtigungsprüfung", lookUpSql, + SxSQL_Server.DEFAULT_MANDANTEN_ID); + for (Iterator it = result.iterator(); it.hasNext();) { + SxResultRow row = (SxResultRow) it.next(); + String hisSystem = (String)row.get(0); + String dbVersionHash = (String)row.get(4); + String fileVersionHash = PathAndFileUtils.getModuleHash(hisSystem); + if (dbVersionHash.equals(fileVersionHash)) { + return true; + } + } + return false; + } catch (Exception e) { + return false; + } + + } + /* + * Prüft die Autorisierung für die jsp-Seiten unter "superx/edit"; diese wird durch die Zuordnung in + * der Datenbank ermittelt; das SQL, welches diese schreibt, wird aus der Modul-XML generiert + * - aus den Einträge unter "/module/dbforms/form". + * Aus denselben Einträgen werden idR auch die zugehörigen dbForms-jep-Seiten generiert, + * sodass es normal eine Zuordnung in der DB für eine jsp-Seite gibt. + * Es gibt aber einen Sonderfall: Es gibt auch jsp-Seiten, die nicht generiert werden (meist welche, + * die aus der Kopie einer generierten erzeugt wurden). + * Für diese gibt es demzufolge auch keine explizite Zuordnung in der DB. + * Hier gilt: + * Sie gehören per 'dbFormName' zu einer _Gruppe_ aus dbForms-jsps, von denen mindestens eine + * generiert sein muss und somit eine Zuordnung in der DB hat. + */ public boolean hasDbFormRight(String dbFormName) { - if (!isAdmin()) { -// return maskenEditRights.contains(dbFormName); - return hasMaskenEditRights; + if (isAdmin()) { + return true; + } + // ist die Komponente nicht aktuell, wird der Zugriff für nicht-Admins verweigert, weil evtl. + // Zurodnungen veraltet und somit keine korrekte Autorisierung gewährleistet ist + if (!isModuleUpdated(dbFormName)) { + return false; + } + String checkSql = """ +SELECT sg.* +FROM + sachgebiete sg + JOIN sachgeb_dbform_bez sdb ON sg.tid = sdb.sachgebiete_id + JOIN db_forms df ON df.tid = dbform_id + JOIN user_sachgeb_bez usb ON sg.tid = usb.sachgebiete_id +WHERE df.name = '""" + dbFormName + "' AND usb.userinfo_id = " + this.getId() + "\n" + + """ + union SELECT sg.* + FROM sachgebiete sg + JOIN sachgeb_dbform_bez sdb ON sg.tid = sdb.sachgebiete_id + JOIN db_forms df ON df.tid = dbform_id + JOIN group_sachgeb_bez gsb ON sg.tid = gsb.sachgebiete_id + JOIN user_group_bez ugb on ugb.groupinfo_id=gsb.groupinfo_id + WHERE df.name = '""" + dbFormName + "' AND ugb.userinfo_id = " + this.getId() + ";"; + try { + SxResultSet result = ServletUtils.execute("Berechtigungsprüfung", checkSql, + SxSQL_Server.DEFAULT_MANDANTEN_ID); + return result.iterator().hasNext(); + } catch (Exception e) { + return false; } - return true; } - public void initThemenbaum() throws SQLException, DBServletException, TransformerException, NichtAngemeldetException { + public void initThemenbaum() + throws SQLException, DBServletException, TransformerException, NichtAngemeldetException { themenbaum.init((Integer) getId(), getName(), this.isAdmin(), getMandantenID()); } + } // Created on 06.12.2004 at 14:48:11 diff --git a/src/de/superx/db/SpringBeanConnectionProvider.java b/src/de/superx/db/SpringBeanConnectionProvider.java index 7d55696..005d9a0 100644 --- a/src/de/superx/db/SpringBeanConnectionProvider.java +++ b/src/de/superx/db/SpringBeanConnectionProvider.java @@ -7,7 +7,7 @@ import javax.sql.DataSource; import org.dbforms.conprovider.ConnectionProvider; -import de.superx.servlet.SuperXManager; +import de.superx.spring.service.ApplicationContextHolder; public class SpringBeanConnectionProvider extends ConnectionProvider { @@ -24,7 +24,7 @@ public class SpringBeanConnectionProvider extends ConnectionProvider { @Override protected void init() throws Exception { - this.dataSource = SuperXManager.getBean("dataSource", DataSource.class); + this.dataSource = ApplicationContextHolder.getBean("dataSource", DataSource.class); } } diff --git a/src/de/superx/db/export/ExportConfigReader.java b/src/de/superx/db/export/ExportConfigReader.java index 98c57a3..759d3d1 100644 --- a/src/de/superx/db/export/ExportConfigReader.java +++ b/src/de/superx/db/export/ExportConfigReader.java @@ -77,13 +77,30 @@ public class ExportConfigReader { } } - // Parse tables with optional per-table column override + // Parse tables with optional per-table column, source, columns, and orderBy overrides List tableConfigs = new ArrayList<>(); for (Element tableEl : groupEl.getChildren("table")) { String tableName = tableEl.getTextTrim(); if (!tableName.isEmpty()) { String colOverride = tableEl.getAttributeValue("column"); - tableConfigs.add(new ExportTableConfig(tableName, colOverride)); + String sourceOverride = tableEl.getAttributeValue("source"); + String columnsAttr = tableEl.getAttributeValue("columns"); + String orderByAttr = tableEl.getAttributeValue("orderBy"); + + List columnsList = null; + if (columnsAttr != null && !columnsAttr.isBlank()) { + columnsList = new ArrayList<>(); + for (String col : columnsAttr.split(",")) { + String trimmed = col.trim(); + if (!trimmed.isEmpty()) { + columnsList.add(trimmed); + } + } + } + + tableConfigs.add(new ExportTableConfig( + tableName, colOverride, sourceOverride, + columnsList, orderByAttr)); } } diff --git a/src/de/superx/db/export/ExportConfigWriter.java b/src/de/superx/db/export/ExportConfigWriter.java new file mode 100644 index 0000000..54547d7 --- /dev/null +++ b/src/de/superx/db/export/ExportConfigWriter.java @@ -0,0 +1,110 @@ +package de.superx.db.export; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.springframework.stereotype.Component; + +/** + * Writes an {@link ExportConfig} to XML, producing the same format + * that {@link ExportConfigReader} can parse. + * + *

Example output: + *

{@code
+ * 
+ *   
+ *     
+ *     my_table
+ *
+ *
+ * }
+ */ +@Component +public class ExportConfigWriter { + + /** + * Write the config to a file. + */ + public void write(ExportConfig config, File outputFile) throws IOException { + try (FileOutputStream fos = new FileOutputStream(outputFile)) { + write(config, fos); + } + } + + /** + * Write the config to an output stream. + */ + public void write(ExportConfig config, OutputStream out) throws IOException { + Document doc = buildDocument(config); + XMLOutputter outputter = new XMLOutputter( + Format.getPrettyFormat().setEncoding("UTF-8")); + outputter.output(doc, out); + } + + /** + * Build the JDOM2 Document representing the config. + */ + public Document buildDocument(ExportConfig config) { + Document doc = new Document(); + Element root = new Element("export-config"); + doc.setRootElement(root); + + for (ExportGroup group : config.getGroups()) { + root.addContent(buildGroupElement(group)); + } + + return doc; + } + + private Element buildGroupElement(ExportGroup group) { + Element groupEl = new Element("group"); + groupEl.setAttribute("name", group.getName()); + + if (group.getSchema() != null) { + groupEl.setAttribute("schema", group.getSchema()); + } + + if (group.hasFilter()) { + Element filterEl = new Element("filter"); + filterEl.setAttribute("column", group.getFilterColumn()); + filterEl.setAttribute("source", group.getFilterSource()); + groupEl.addContent(filterEl); + } + + for (ExportTableConfig tableConfig : group.getTableConfigs()) { + groupEl.addContent(buildTableElement(tableConfig)); + } + + return groupEl; + } + + private Element buildTableElement(ExportTableConfig tableConfig) { + Element tableEl = new Element("table"); + tableEl.setText(tableConfig.getTableName()); + + if (tableConfig.getFilterColumn() != null) { + tableEl.setAttribute("column", tableConfig.getFilterColumn()); + } + + if (tableConfig.hasFilterSource()) { + tableEl.setAttribute("source", tableConfig.getFilterSource()); + } + + if (tableConfig.hasColumns()) { + tableEl.setAttribute("columns", String.join(", ", tableConfig.getColumns())); + } + + if (tableConfig.hasOrderBy()) { + tableEl.setAttribute("orderBy", tableConfig.getOrderBy()); + } + + return tableEl; + } +} diff --git a/src/de/superx/db/export/ExportTableConfig.java b/src/de/superx/db/export/ExportTableConfig.java index 77419aa..473f782 100644 --- a/src/de/superx/db/export/ExportTableConfig.java +++ b/src/de/superx/db/export/ExportTableConfig.java @@ -1,26 +1,57 @@ package de.superx.db.export; +import java.util.Collections; +import java.util.List; + /** * Configuration for a single table within an export group. - * Holds the table name and an optional override for the filter column. + * Holds the table name and optional overrides for the filter column, + * filter source, column selection, and ordering. */ public class ExportTableConfig { private final String tableName; private final String filterColumn; + private final String filterSource; + private final List columns; + private final String orderBy; /** * @param tableName the database table name * @param filterColumn optional override for the group-level filter column; * null means use the group default + * @param filterSource optional override for the group-level filter source; + * may contain {@code {filter}} as a placeholder that will be + * replaced with the group-level filter expression + * (i.e. "groupColumn IN (groupSource)"). Null means use + * the group-level filter directly. + * @param columns optional list of column names to export; null or empty + * means export all columns (SELECT *) + * @param orderBy optional ORDER BY expression (without the "ORDER BY" keyword); + * null means no ordering */ - public ExportTableConfig(String tableName, String filterColumn) { + public ExportTableConfig(String tableName, String filterColumn, + String filterSource, List columns, + String orderBy) { this.tableName = tableName; this.filterColumn = filterColumn; + this.filterSource = filterSource; + this.columns = columns != null && !columns.isEmpty() + ? Collections.unmodifiableList(columns) : null; + this.orderBy = orderBy; + } + + public ExportTableConfig(String tableName, String filterColumn, + String filterSource) { + this(tableName, filterColumn, filterSource, null, null); + } + + public ExportTableConfig(String tableName, String filterColumn) { + this(tableName, filterColumn, null, null, null); } public ExportTableConfig(String tableName) { - this(tableName, null); + this(tableName, null, null, null, null); } public String getTableName() { @@ -35,6 +66,14 @@ public class ExportTableConfig { return filterColumn; } + /** + * @return the per-table filter source SQL, or null to use the group default. + * May contain {@code {filter}} placeholder. + */ + public String getFilterSource() { + return filterSource; + } + /** * Returns the effective filter column: this table's override if set, * otherwise the provided group default. @@ -43,11 +82,71 @@ public class ExportTableConfig { return filterColumn != null ? filterColumn : groupDefault; } + /** + * Whether this table has its own filter source override. + */ + public boolean hasFilterSource() { + return filterSource != null; + } + + /** + * @return the list of columns to export, or null if all columns + * should be exported + */ + public List getColumns() { + return columns; + } + + /** + * Whether this table has a column selection configured. + */ + public boolean hasColumns() { + return columns != null; + } + + /** + * @return the ORDER BY expression (without the keyword), or null + * if no ordering is specified + */ + public String getOrderBy() { + return orderBy; + } + + /** + * Whether this table has an ORDER BY expression configured. + */ + public boolean hasOrderBy() { + return orderBy != null; + } + @Override public String toString() { - if (filterColumn != null) { - return tableName + " (column=" + filterColumn + ")"; + StringBuilder sb = new StringBuilder(tableName); + boolean hasExtra = filterColumn != null || filterSource != null + || columns != null || orderBy != null; + if (hasExtra) { + sb.append(" ("); + boolean first = true; + if (filterColumn != null) { + sb.append("column=").append(filterColumn); + first = false; + } + if (filterSource != null) { + if (!first) sb.append(", "); + sb.append("source=").append(filterSource); + first = false; + } + if (columns != null) { + if (!first) sb.append(", "); + sb.append("columns=").append(columns); + first = false; + } + if (orderBy != null) { + if (!first) sb.append(", "); + sb.append("orderBy=").append(orderBy); + } + sb.append(")"); } - return tableName; + return sb.toString(); } } diff --git a/src/de/superx/db/export/ImportMode.java b/src/de/superx/db/export/ImportMode.java index cf39b48..87ea8db 100644 --- a/src/de/superx/db/export/ImportMode.java +++ b/src/de/superx/db/export/ImportMode.java @@ -15,5 +15,11 @@ public enum ImportMode { CREATE_INSERT, /** Drop and recreate the table from the XML structure, then insert. */ - RECREATE + RECREATE, + + /** Insert new rows or update existing ones based on primary key. Table must already exist. */ + UPSERT, + + /** Only update existing rows based on primary key. Rows without a match are skipped. Table must already exist. */ + UPDATE } diff --git a/src/de/superx/dbt/DbtAction.java b/src/de/superx/dbt/DbtAction.java new file mode 100644 index 0000000..ddcb597 --- /dev/null +++ b/src/de/superx/dbt/DbtAction.java @@ -0,0 +1,11 @@ +package de.superx.dbt; + +public enum DbtAction { + BUILD, + TEST, + GENERATE_DOCS, + STAGING_TABLES, + SQLFLUFF_LINT, + NODE_COUNT_BUILD, + NODE_COUNT_TEST +} diff --git a/src/de/superx/dbt/DbtExecutionState.java b/src/de/superx/dbt/DbtExecutionState.java new file mode 100644 index 0000000..a59284b --- /dev/null +++ b/src/de/superx/dbt/DbtExecutionState.java @@ -0,0 +1,8 @@ +package de.superx.dbt; + +public enum DbtExecutionState { + ACTIVE, // dbt-Lauf ist noch nicht beendet - evtl. braucht es mehr als nur "active"!? + SUCCESS, // dbt-Lauf wurde erfolgreich abgeschlossen + FAILURE, // dbt-Lauf endete in einem Fehler + TIMEOUT +} \ No newline at end of file diff --git a/src/de/superx/dbt/DbtMetrics.java b/src/de/superx/dbt/DbtMetrics.java deleted file mode 100644 index ab742cb..0000000 --- a/src/de/superx/dbt/DbtMetrics.java +++ /dev/null @@ -1,59 +0,0 @@ -package de.superx.dbt; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class DbtMetrics { - - private static final String METRICS_LINE = "Found (\\d+) models, (\\d+) snapshots, (\\d+) data tests, (\\d+) seeds," - + " (\\d+) sources, (\\d+) macros"; - private static final Pattern METRICS_PATTERN = Pattern.compile(METRICS_LINE); - - private int models; - private int snapshots; - private int tests; - private int seeds; - private int sources; - private int macros; - - public DbtMetrics(String metricLine) { - Matcher metricsMatcher = METRICS_PATTERN.matcher(metricLine); - if (metricsMatcher.find()) { - models = Integer.parseInt(metricsMatcher.group(1)); - snapshots = Integer.parseInt(metricsMatcher.group(2)); - tests = Integer.parseInt(metricsMatcher.group(3)); - seeds = Integer.parseInt(metricsMatcher.group(4)); - sources = Integer.parseInt(metricsMatcher.group(5)); - macros = Integer.parseInt(metricsMatcher.group(6)); - } - } - - public int getExecCount(boolean isTests) { - return isTests ? tests : models + snapshots + seeds; - } - - public int getModels() { - return models; - } - - public int getSnapshots() { - return snapshots; - } - - public int getTests() { - return tests; - } - - public int getSeeds() { - return seeds; - } - - public int getSources() { - return sources; - } - - public int getMacros() { - return macros; - } - -} diff --git a/src/de/superx/dbt/DbtPermissionException.java b/src/de/superx/dbt/DbtPermissionException.java new file mode 100644 index 0000000..77ae142 --- /dev/null +++ b/src/de/superx/dbt/DbtPermissionException.java @@ -0,0 +1,7 @@ +package de.superx.dbt; + +public class DbtPermissionException extends Exception { + DbtPermissionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/de/superx/dbt/DbtRun.java b/src/de/superx/dbt/DbtRun.java new file mode 100644 index 0000000..e990681 --- /dev/null +++ b/src/de/superx/dbt/DbtRun.java @@ -0,0 +1,443 @@ +package de.superx.dbt; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.apache.commons.lang.SystemUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +public final class DbtRun implements Callable { + + static Logger logger = LoggerFactory.getLogger(DbtRun.class); + private static final boolean isLinux = SystemUtils.IS_OS_LINUX; + + private final DbtRunRequest request; + private final Map environment; + private final Path projectPath; + + public Process process; + + DbtRun(DbtRunRequest request, Map environment, Path projectPath) { + this.request = request; + this.environment = environment; + this.projectPath = projectPath; + } + + @Override + public DbtRunResult call() throws Exception { + return execute(); + } + + private DbtRunResult execute() { + return switch (request.action()) { + case BUILD -> runDbtCommand(createBuildCommand()); + case TEST -> runDbtCommand(createTestCommand()); + case NODE_COUNT_BUILD -> nodeCount(true); + case NODE_COUNT_TEST -> nodeCount(false); + case GENERATE_DOCS -> generateDocs(); + case STAGING_TABLES -> findDbtStagingTablesForSelection(); + case SQLFLUFF_LINT -> lintDbtModelsUsingSQLFluff(); + }; + } + + private List createTestCommand() { + List baseCommand = new ArrayList<>(DbtUtils.getDbtCommandBase()); + baseCommand.addAll(Arrays.asList( "--log-format", "json", "--no-use-colors", "test")); + if(request.select() != null && !request.select().isBlank()) { + baseCommand.add("--select"); + baseCommand.add(request.select()); + } + return baseCommand; + } + + private List createBuildCommand() { + List baseCommand = new ArrayList<>(DbtUtils.getDbtCommandBase()); + baseCommand.addAll(Arrays.asList( + "--log-format", "json", + "--no-use-colors", + "build", + "--resource-types", + "snapshot", + "seed", + "model" + )); + + if(request.select() != null && !request.select().isBlank()) { + baseCommand.add("--select"); + baseCommand.add(request.select()); + } + return baseCommand; + } + + public DbtRunResult runDbtCommand(List pbCommand) { + DbtRunResult result = new DbtRunResult(); + logger.info(Arrays.toString(pbCommand.toArray())); + ProcessBuilder p = new ProcessBuilder(pbCommand).directory(projectPath.toFile()); + p.environment().putAll(this.environment); + p.redirectErrorStream(false); + List dbtLog = new ArrayList<>(); + JsonNode stats = null; + try { + int progress = 0; + + process = p.start(); + logger.info("ProcessBuilder start"); + logger.info("PYTHONUSERBASE: " + p.environment().get("PYTHONUSERBASE")); + logger.info("PATH: " + p.environment().get((isLinux) ? "PATH" : "Path")); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + CompletableFuture errFuture = readOutStream(process.getErrorStream()); + + var line = ""; + ObjectMapper objectMapper = new ObjectMapper(); + while ((line = reader.readLine()) != null) { + JsonNode node = objectMapper.readTree(line.getBytes()); + logger.info(node.get("info").get("msg").asText()); + JsonNode data = node.get("data"); + if (data.get("stats") != null) { + stats = node; + } else { + if (data.has("index")) { + progress = Math.max(progress, data.get("index").asInt()); + if(request.progressCallback() != null) { + request.progressCallback().accept(Integer.valueOf(progress)); + } + } + dbtLog.add(node); + } + } + + int exitCode = process.waitFor(); + String error = errFuture.join(); + + result.setErrorMessages(getErrorMessages(dbtLog, error)); + result.setState(getExecutionState(stats)); + result.setExitCode(Integer.valueOf(exitCode)); + } catch (Exception e) { + logger.error("Couldn't start dbt run!", e); + result.setErrorMessages(List.of(e.getMessage())); + result.setState(DbtExecutionState.FAILURE); + } + return result; + } + + public DbtRunResult generateDocs() { + DbtRunResult result = new DbtRunResult(); + try { + List pbCommand = new ArrayList<>(DbtUtils.getDbtCommandBase()); + pbCommand.addAll(Arrays.asList("--no-use-colors", "docs", "generate")); + if(request.select() != null && !request.select().isBlank()) { + pbCommand.add("--select"); + pbCommand.add(request.select()); + } + ProcessBuilder pb = new ProcessBuilder(pbCommand).directory(projectPath.toFile()); + pb.environment().putAll(environment); + pb.redirectErrorStream(true); + Process p = pb.start(); + final InputStream stdoutInputStream = p.getInputStream(); + final BufferedReader stdoutReader = + new BufferedReader(new InputStreamReader(stdoutInputStream)); + String line = null; + while ((line = stdoutReader.readLine()) != null) { + logger.info(line); + } + result.setState(DbtExecutionState.SUCCESS); + } catch (Exception iox) { + String message = "Couldn't run dbt generate docs "; + logger.error(message, iox); + result.setState(DbtExecutionState.FAILURE); + result.setErrorMessages(List.of(message)); + } + return result; + } + + public DbtRunResult nodeCount(boolean test) { + DbtRunResult result = new DbtRunResult(); + int lineNr = 0; + try { + List pbCommand = new ArrayList<>(DbtUtils.getDbtCommandBase()); + pbCommand.addAll(Arrays.asList("ls", "--resource-types")); + if(test) { + pbCommand.add("test"); + } else { + pbCommand.addAll(Arrays.asList( + "snapshot", + "seed", + "model" + )); + } + if(request.select() != null && !request.select().isBlank()) { + pbCommand.add("--select"); + pbCommand.add(request.select()); + } + ProcessBuilder pb = new ProcessBuilder(pbCommand).directory(projectPath.toFile()); + pb.environment().putAll(environment); + pb.redirectErrorStream(true); + Process p = pb.start(); + final InputStream stdoutInputStream = p.getInputStream(); + final BufferedReader stdoutReader = + new BufferedReader(new InputStreamReader(stdoutInputStream)); + String line = ""; + while ((line = stdoutReader.readLine()) != null) { + if (!line.matches("(\\w+\\.)+\\w+$")) { + continue; + } + lineNr += 1; + logger.info(line); + } + result.setNodeCount(lineNr); + result.setState(DbtExecutionState.SUCCESS); + } catch (Exception iox) { + String message = "Couldn't run dbt ls "; + logger.error(message, iox); + result.setErrorMessages(List.of(message)); + result.setState(DbtExecutionState.FAILURE); + } + return result; + } + + private static CompletableFuture readOutStream(InputStream is) { + return CompletableFuture.supplyAsync(() -> { + try ( + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + ) { + StringBuilder res = new StringBuilder(); + String inputLine; + while ((inputLine = br.readLine()) != null) { + res.append(inputLine).append(System.lineSeparator()); + } + return res.toString(); + } catch (Throwable e) { + throw new RuntimeException("problem with executing program", e); + } + }); + } + + /** + * Finds staging tables for a dbt selection by running {@code dbt ls}. + *

+ * Matches models from staging schemas such as {@code staging}, + * {@code staging_xyz}, or {@code staging_vmpostgres}, and returns only the + * table name without the schema prefix. + * + * @param selectOption the dbt selector to inspect + * @return + * @return staging table names without schema prefix + */ + public DbtRunResult findDbtStagingTablesForSelection() { + DbtRunResult result = new DbtRunResult(); + logger.info("Finding dbt staging tables for selection: {}", request.select()); + try { + List command = new ArrayList<>(DbtUtils.getDbtCommandBase()); + command.addAll(Arrays.asList( + "ls", + "--select", request.select(), + "--output", "name" + )); + + logger.info("Dbt transform Dir: {}", projectPath); + logger.info("Command: {}", String.join(" ", command)); + + ProcessBuilder builder = new ProcessBuilder(command) + .directory(projectPath.toFile()); + + builder.environment().putAll(environment); + + this.process = builder.start(); + + CompletableFuture errFuture = readOutStream(process.getErrorStream()); + + List results; + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()) + )) { + results = reader.lines().collect(Collectors.toList()); + } + + int exitCode = process.waitFor(); + String error = errFuture.join(); + + String output = String.join(System.lineSeparator(), results); + + if (exitCode != 0) { + String message = "dbt ls failed for selection '" + request.select() + "' with exit code " + exitCode + + "\n\nstdout:\n" + output + + "\n\nstderr:\n" + error; + result.setState(DbtExecutionState.FAILURE); + result.setErrorMessages(List.of(message)); + return result; + } + + if (!error.isBlank()) { + logger.debug("dbt ls stderr for selection '{}': {}", request.select(), error); + } + + List tables = new ArrayList<>(); + + for (String model : results) { + int dotIndex = model.indexOf("."); + if (dotIndex > 0 && model.substring(0, dotIndex).startsWith("staging")) { + tables.add(model.substring(dotIndex + 1)); + } + } + + logger.info( + "Found {} dbt staging table(s) for selection '{}': {}", + new Object[] { Integer.valueOf(tables.size()), request.select(), tables } + ); + + result.setOutputMessages(tables); + result.setExitCode(Integer.valueOf(exitCode)); + if(!error.isBlank()) { + result.setErrorMessages(List.of(error)); + result.setState(DbtExecutionState.FAILURE); + } else { + result.setState(DbtExecutionState.SUCCESS); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + String message = "Interrupted while running dbt ls for selection: " + request.select(); + logger.error(message, e); + result.setState(DbtExecutionState.FAILURE); + result.setErrorMessages(List.of(message)); + } catch (IOException e) { + String message = "Could not start dbt ls for selection: " + request.select(); + logger.error(message , e); + result.setState(DbtExecutionState.FAILURE); + result.setErrorMessages(List.of(message)); + } + return result; + } + + public DbtRunResult lintDbtModelsUsingSQLFluff() { + DbtRunResult result = new DbtRunResult(); + List command = new ArrayList<>(DbtUtils.getSqlFluffCommandBase()); + command.addAll(Arrays.asList( + "lint", + "--verbose", + "--nocolor", + "--disable-progress-bar" + )); + logger.info("sqlfluff command: " + command); + + ProcessBuilder builder = new ProcessBuilder(command) + .directory(projectPath.toFile()); + + builder.environment().putAll(environment); + + try { + this.process = builder.start(); + + // read stdout + List lintingErrors = new ArrayList(); + int progress = 0; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))){ + String line; + while((line = reader.readLine()) != null) { + logger.info(line); + if(line.contains(".sql")) { + if (line.contains("FAIL")) { + lintingErrors.add(line); + } + if(request.progressCallback() != null) { + request.progressCallback().accept(Integer.valueOf(progress++)); + } + } + } + } + + List errorsFromErrorStream = new ArrayList(); + // read stderr + try (InputStream es = process.getErrorStream(); + InputStreamReader esr = new InputStreamReader(es); + BufferedReader errReader = new BufferedReader(esr)) { + String line; + while ((line = errReader.readLine()) != null) { + logger.error(line); + errorsFromErrorStream.add(line); + } + } + + // SQLFluff exit codes: 0 = clean, 1 = linting issues found, 2 = execution failed. + int exitCode = process.waitFor(); + result.setExitCode(Integer.valueOf(exitCode)); + + if(exitCode == 0) { + result.setState(DbtExecutionState.SUCCESS); + } else if(exitCode == 1) { + result.setErrorMessages(lintingErrors); + result.setState(DbtExecutionState.FAILURE); + } else { + result.setErrorMessages(errorsFromErrorStream); + result.setState(DbtExecutionState.FAILURE); + } + + return result; + } catch (Exception e) { + logger.error("Failed running sqlfluff.", e); + result.setErrorMessages(List.of(e.getMessage())); + result.setState(DbtExecutionState.FAILURE); + return result; + } + } + + public DbtExecutionState getExecutionState(JsonNode stats) { + if (stats == null) { + return DbtExecutionState.ACTIVE; + } + if (stats.get("data").get("stats").get("error").asInt() == 0) { + return DbtExecutionState.SUCCESS; + } + return DbtExecutionState.FAILURE; + } + + private List getErrorMessages(List dbtLog, String errors) { + var result = new ArrayList(); + if(errors == null) { + return result; + } + if (!errors.isEmpty()) { + // stderr was not empty! + result.add(errors); + } else { + // "regular" errors during dbt execution + for (var node : dbtLog) { + var level = findFirstOccurence("level", node); + if (level.isPresent() && "error".equals(level.get().asText())) { + var msg = findFirstOccurence("msg", node); + if (msg.isPresent()) { + result.add(msg.get().asText()); + } else { + logger.warn("No msg property found in dbt log " + node); + } + } else { + logger.warn("No level property found in dbt log " + node); + } + } + } + return result; + } + + private static Optional findFirstOccurence(String name, JsonNode searchNode) { + JsonNode parent = searchNode.findParent(name); + JsonNode result = parent != null ? parent.get(name) : null; + return Optional.of(result); + } + +} diff --git a/src/de/superx/dbt/DbtRunRequest.java b/src/de/superx/dbt/DbtRunRequest.java new file mode 100644 index 0000000..379d9d1 --- /dev/null +++ b/src/de/superx/dbt/DbtRunRequest.java @@ -0,0 +1,24 @@ +package de.superx.dbt; + +import java.util.function.Consumer; + +public record DbtRunRequest( + DbtAction action, + String project, + String select, + long timeoutMillis, + Consumer progressCallback +) { + + public DbtRunRequest(DbtAction action, String project, String select, long timeoutMillis) { + this(action, project, select, timeoutMillis, null); + } + + public DbtRunRequest(DbtAction action, String project, Consumer progressCallback) { + this(action, project, null, 0, progressCallback); + } + + public DbtRunRequest(DbtAction action, String project, String select) { + this(action, project, select, 0, null); + } +} diff --git a/src/de/superx/dbt/DbtRunResult.java b/src/de/superx/dbt/DbtRunResult.java new file mode 100644 index 0000000..c7487ef --- /dev/null +++ b/src/de/superx/dbt/DbtRunResult.java @@ -0,0 +1,62 @@ +package de.superx.dbt; + +import java.util.List; + +public class DbtRunResult { + private Integer exitCode; + private DbtExecutionState state; + private List outputMessages; + private List errorMessages; + + private int nodeCount; // used for DbtAction.NODE_COUNT* +// private List stagingTables;// used for DbtAction.STAGING_TABLES + + public Integer getExitCode() { + return exitCode; + } + + public void setExitCode(Integer exitCode) { + this.exitCode = exitCode; + } + + public DbtExecutionState getState() { + return state; + } + + public void setState(DbtExecutionState state) { + this.state = state; + } + + public List getErrorMessages() { + return errorMessages; + } + + public void setErrorMessages(List errorMessages) { + this.errorMessages = errorMessages; + } + + public int getNodeCount() { + return nodeCount; + } + + public void setNodeCount(int nodeCount) { + this.nodeCount = nodeCount; + } + + //public List getStagingTables() { + // return stagingTables; + //} + + //public void setStagingTables(List stagingTables) { + // this.stagingTables = stagingTables; + //} + + public List getOutputMessages() { + return outputMessages; + } + + public void setOutputMessages(List outputMessages) { + this.outputMessages = outputMessages; + } + +} diff --git a/src/de/superx/dbt/DbtUtils.java b/src/de/superx/dbt/DbtUtils.java index 9661159..1036b8c 100644 --- a/src/de/superx/dbt/DbtUtils.java +++ b/src/de/superx/dbt/DbtUtils.java @@ -1,15 +1,21 @@ package de.superx.dbt; import java.io.BufferedReader; +import java.io.File; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.StringJoiner; import java.util.stream.Collectors; +import javax.sql.DataSource; + import org.apache.commons.lang3.SystemUtils; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; import de.superx.servlet.SuperXManager; import de.superx.util.PathAndFileUtils; @@ -17,6 +23,8 @@ import de.superx.util.PathAndFileUtils; public class DbtUtils { private final static boolean isLinux = SystemUtils.IS_OS_LINUX; + + public static boolean isTestRun = false; public static String getPythonVersion() { return getPythonVersionForExecutable(getPython3ExecutableName()); @@ -36,7 +44,7 @@ public class DbtUtils { } throw new RuntimeException("No Python 3 installation found in PATH!"); } - + public static String getPythonVersionForExecutable(String executable) { try { ProcessBuilder p = new ProcessBuilder(executable, "--version"); @@ -53,27 +61,59 @@ public class DbtUtils { } } + private static Path getDbtFolderBinary() { + return Path.of(SuperXManager.getWEB_INFPfad(), "..", "dbt"); + } + private static Path getDbtFolder() { + if(isTestRun) { + return Path.of(String.join(File.separator, "test", "resources", "dbt" )); + } return Path.of(SuperXManager.getWEB_INFPfad(), "..", "dbt"); } private static Path getDbtInstallLib() { - return Path.of(getDbtFolder().toString(), "dbt_linux", "lib"); + return Path.of(getDbtFolderBinary().toString(), "dbt_linux", "lib"); + } + + public static Path getDbtModelsDirectory(String project) { + Path modelsPath = getDbtProjectDirectory(project).resolve("models"); + + if (!modelsPath.toFile().isDirectory()) { + throw new RuntimeException(modelsPath.toAbsolutePath() + " is no directory!"); + } + + return modelsPath.toAbsolutePath().normalize(); } - public static Path getDbtProjectDirectory() { - return Path.of(getDbtFolder().toString(), "projects", "hisinone", "transform"); + public static Path getDbtProjectDirectory(String project) { + Path projectPath = Path.of(getDbtFolder().toString(), "projects", project, "transform"); + + if (!projectPath.toFile().isDirectory()) { + throw new RuntimeException(projectPath.toAbsolutePath() + " is no directory!"); + } + + File projectFile = Path.of(projectPath.toString(), "dbt_project.yml").toFile(); + if (!projectFile.exists()) { + throw new RuntimeException(projectPath + " is no dbt project directory (no dbt_project.yml found)!"); + } + + File profilesFile = Path.of(projectPath.toString(), "profiles.yml").toFile(); + if (!profilesFile.exists()) { + throw new RuntimeException(projectPath + " is no dbt project directory (no profiles.yml found)!"); + } + return projectPath.toAbsolutePath().normalize(); } - public static Path getBiAnalysisMetadataFolder() { - return Path.of(getDbtFolder().toString(), "projects", "hisinone", "bianalysis_metadata"); + public static Path getBiAnalysisMetadataFolder(String project) { + return Path.of(getDbtFolder().toString(), "projects", project, "bianalysis_metadata"); } public static Path getVirtualEnvPath() { if(isLinux) { - return Path.of(getDbtFolder().toString(), "dbt_env_linux"); + return Path.of(getDbtFolderBinary().toString(), "dbt_env_linux"); } - return Path.of(getDbtFolder().toString(), "dbt_env_windows"); + return Path.of(getDbtFolderBinary().toString(), "dbt_env_windows"); } public static Path getDbtInstallDir() { @@ -84,7 +124,7 @@ public class DbtUtils { if(isLinux) { folder = "dbt_linux"; } - return Path.of(getDbtFolder().toString(), folder); + return Path.of(getDbtFolderBinary().toString(), folder); } public static boolean isVirtualEnvInstalled() { @@ -112,11 +152,42 @@ public class DbtUtils { return Path.of(getDbtBinaryFolder().toString(), bin); } - public static Path getDbtBinaryPath() { + private static Path getDbtBinaryPath(String bin) { + if(!(bin.equals("dbt") || bin.equals("sqlfluff"))) { + throw new IllegalArgumentException("binary not allowed: " + bin); + } + if(isLinux) { - return Path.of(getDbtBinaryFolder().toString(), "dbt"); + return Path.of(getDbtBinaryFolder().toString(), bin); + } + return Path.of(getDbtBinaryFolder().toString(), bin + ".exe"); + } + + public static List getDbtCommandBase() { + return getCommandBase("dbt"); + } + + public static List getSqlFluffCommandBase() { + return getCommandBase("sqlfluff"); + } + + public static List getCommandBase(String bin) { + List command = new ArrayList<>(); + + String pythonExecutable = getPython3ExecutableName(); + + if (!isLinux && DbtUtils.isVirtualEnvInstalled()) { + // On Windows, when using a virtual environment, we must run the dbt + // executable directly without invoking it through the python interpreter + pythonExecutable = ""; } - return Path.of(getDbtBinaryFolder().toString(), "dbt.exe"); + + if (pythonExecutable != null && !pythonExecutable.isBlank()) { + command.add(pythonExecutable); + } + + command.add(getDbtBinaryPath(bin).toString()); + return command; } @@ -157,20 +228,46 @@ public class DbtUtils { } + public static void checkDbtSchemaPermissions(DataSource dataSource) throws DbtPermissionException { + final List DBT_SCHEMAS = List.of( + "staging", + "core", + "presentation", + "metadata" + ); + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + for (String schema : DBT_SCHEMAS) { + String fullTableName = schema + ".__dbt_permission_check"; + try { + jdbcTemplate.execute("CREATE TABLE " + fullTableName + " (permission_check integer)"); + jdbcTemplate.execute("DROP TABLE " + fullTableName); + } catch (DataAccessException e) { + String errorMessage = """ + dbt permission check failed for schema '%s'. + + Could not create/drop table '%s'. + Grant likely needed: + GRANT USAGE, CREATE ON SCHEMA %s TO ; + """.formatted( schema, fullTableName, schema); + throw new DbtPermissionException(errorMessage, e); + } + } + } + public static void main(String[] args) { - - SuperXManager.setWEB_INFPfad(PathAndFileUtils.getWebinfPath()); - DbtWrapper.getStagingTables("+fact_application"); - System.out.println(getPythonVersion()); System.out.println(getPython3ExecutableName()); - System.out.println(getDbtFolder()); - System.out.println(getDbtProjectDirectory()); + System.out.println(getDbtProjectDirectory("hisinone")); System.out.println(getDbtBinaryFolder()); - System.out.println(getDbtBinaryPath()); + System.out.println(getDbtCommandBase()); + + //BasicConfigurator.configure(); + //DbtWrapper dbtWrapper = new DbtWrapper(); + //dbtWrapper.init("hisinone", "+fact_application"); + //System.out.println(dbtWrapper.findDbtStagingTablesForSelection()); } } diff --git a/src/de/superx/dbt/DbtWrapper.java b/src/de/superx/dbt/DbtWrapper.java index c47a2b4..5896ffb 100644 --- a/src/de/superx/dbt/DbtWrapper.java +++ b/src/de/superx/dbt/DbtWrapper.java @@ -1,28 +1,20 @@ package de.superx.dbt; -import static java.util.Map.entry; - -import java.io.BufferedReader; -import java.io.File; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URI; -import java.nio.file.Path; -import java.sql.Connection; import java.sql.ResultSet; +import static java.util.Map.entry; import java.sql.SQLException; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import javax.sql.DataSource; -import org.apache.commons.lang3.SystemUtils; import org.jfree.util.Log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,172 +24,72 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.stereotype.Service; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.zaxxer.hikari.HikariDataSource; - import de.superx.access.SqlAccessRestrictor; import de.superx.access.SqlAccessRestrictor.Rechtekonzept; import de.superx.rest.EtlJobApi; import de.superx.rest.model.job.Component; -import de.superx.servlet.SuperXManager; +import de.superx.spring.service.ApplicationContextHolder; @Service -public class DbtWrapper implements Runnable { - - public enum DbParamsSource { - FROM_PRESETS, - FROM_FILE - } - - public enum ExecutionState { - ACTIVE, // dbt-Lauf ist noch nicht beendet - evtl. braucht es mehr als nur "active"!? - SUCCESS, // dbt-Lauf wurde erfolgreich abgeschlossen - FAILURE // dbt-Lauf endete in einem Fehler - } +public class DbtWrapper { @Autowired EtlJobApi etlJobApi; @Autowired DataSource dataSource; - - static Logger logger = LoggerFactory.getLogger(DbtWrapper.class); - - private final boolean isLinux = SystemUtils.IS_OS_LINUX; - private Path dbtInstallDir; - private String pythonExecutable = "python"; - private String user = "postgres"; - private String password = "postgres"; - private String host = "localhost"; - private String port = "5432"; - private String db = "eduetl"; - private Process process; - private int modelCount = 0; - private String runtimeErrors = ""; - private String select; - private boolean test; - - private Path projectPath; - private List dbtLog = new ArrayList<>(); - private JsonNode stats; - - private boolean shouldGenerateDoc = true; + @Autowired + Map dbtProfile; - public void init(final Path projectPath, String select) { - init(projectPath, select, DbParamsSource.FROM_FILE, false); - } + static Logger logger = LoggerFactory.getLogger(DbtWrapper.class); - public void init(final Path projectPath, String select, boolean test) { - init(projectPath, select, DbParamsSource.FROM_FILE, test); - } + public static final String DBT_THREAD_NAME_PREFIX = "DBT-"; - public void init(final Path projectPath, String select, DbParamsSource dbParamsSource, boolean test) { - if (!projectPath.toFile().isDirectory()) { - throw new RuntimeException(projectPath.toAbsolutePath() + " is no directory!"); - } - File projectFile = Path.of(projectPath.toString(), "dbt_project.yml").toFile(); - if (!projectFile.exists()) { - throw new RuntimeException(projectPath + " is no dbt project directory (no dbt_project.yml found)!"); - } - File profilesFile = Path.of(projectPath.toString(), "profiles.yml").toFile(); - if (!profilesFile.exists()) { - throw new RuntimeException(projectPath + " is no dbt project directory (no profiles.yml found)!"); - } - this.projectPath = projectPath.toAbsolutePath().normalize(); - this.dbtInstallDir = DbtUtils.getDbtInstallDir(); - this.pythonExecutable = DbtUtils.getPython3ExecutableName(); - this.select = select; - this.test = test; - - // reset state members - this.process = null; - this.dbtLog = new ArrayList<>(); - this.runtimeErrors = ""; - this.modelCount = 0; - this.stats = null; - - if(this.test) { - this.shouldGenerateDoc = false; - } + public DbtRunResult execute(DbtRunRequest request) { + DbtRun run = new DbtRun( + request, + getEnvironment(), // TODO: costly, reuse for multiple calls ? + DbtUtils.getDbtProjectDirectory(request.project()) + ); - // TODO: there has to be a better way - // -> Ja, wie wäre es, wenn die ganzen Parameter für die Datenbankverbindung "injiziert" würden? - if (dbParamsSource == DbParamsSource.FROM_FILE) { - try { + ExecutorService executor = Executors.newSingleThreadExecutor(r -> { + Thread t = new Thread(r); + // name the thread so that the logging to file and frontend works + // see class: BiJobLoggingListener + t.setName(DBT_THREAD_NAME_PREFIX + System.currentTimeMillis()); + return t; + }); + Future future = executor.submit(run); - HikariDataSource eduetl = dataSource.unwrap(HikariDataSource.class); - String jdbcUrl = eduetl.getJdbcUrl(); + try { + return request.timeoutMillis() <= 0 + ? future.get() : future.get(request.timeoutMillis(), TimeUnit.MILLISECONDS); - String cleanUrl = jdbcUrl.substring(5); // remove "jdbc:" - URI uri = URI.create(cleanUrl); - String eduetlHost = uri.getHost(); - int eduetlPort = uri.getPort(); // returns -1 if no port specified - String eduetlFinalPort = String.valueOf(eduetlPort != -1 ? Integer.valueOf(port) : "5432"); + } catch (TimeoutException e) { + logger.error("dbt command timed out after {} milliseconds", Long.valueOf(request.timeoutMillis())); - String eduetlUsername = ""; - String eduetlDbName = ""; - try(Connection con = dataSource.getConnection()){ - eduetlUsername = con.getMetaData().getUserName(); - eduetlDbName = con.getCatalog(); - } - - this.user = eduetlUsername; - this.password = eduetl.getPassword(); - this.host = eduetlHost; - this.port = eduetlFinalPort; - this.db = eduetlDbName; - } catch (Exception e) { - throw new RuntimeException("Couldn't get connection info for eduetl", e); + if (run.process != null && run.process.isAlive()) { + run.process.destroyForcibly(); } - } - } - - - private List buildDbtCommand() { - String dbt = DbtUtils.getDbtBinaryPath().toString(); - logger.info("Project path: "+ projectPath.toString()); - logger.info("dbt_bin: "+ dbt.toString()); - List baseCommand = new ArrayList(Arrays.asList( - pythonExecutable, - dbt, - "--log-format", - "json", - "--no-use-colors" - )); + future.cancel(true); - if(!isLinux && DbtUtils.isVirtualEnvInstalled()) { - // On Windows, when using a virtual environment, we must run the dbt - // executable directly without invoking it through the python interpreter - baseCommand.remove(0); - } + DbtRunResult result = new DbtRunResult(); + result.setState(DbtExecutionState.TIMEOUT); + result.setErrorMessages(Arrays.asList("DBT run timeout!")); + return result; - if(this.test) { - baseCommand.add("test"); - } else { - baseCommand.addAll(Arrays.asList( - "build", - "--resource-types", - "snapshot", - "seed", - "model" - )); + } catch (Exception e) { + logger.error("Failed with error: ", e); + DbtRunResult result = new DbtRunResult(); + result.setState(DbtExecutionState.FAILURE); + result.setErrorMessages(Arrays.asList(e.getMessage())); + return result; } + } - if(select != null && !select.isBlank()) { - baseCommand.add("--select"); - baseCommand.add(select); - } - return baseCommand; - } - - @Override - public void run() { - List pbCommand = buildDbtCommand(); - logger.info(Arrays.toString(pbCommand.toArray())); - ProcessBuilder p = new ProcessBuilder(pbCommand).directory(projectPath.toFile()); + private Map getEnvironment(){ Map env = getComponentStatus(); env.putAll(getDbtEnvironment()); env.putAll(getUnloadParams()); @@ -205,223 +97,9 @@ public class DbtWrapper implements Runnable { Map rls_env_var = Map.of("ROW_LEVEL_SECURITY", "true"); env.putAll(rls_env_var); } - p.environment().putAll(env); - p.redirectErrorStream(false); - dbtLog = new ArrayList<>(); - try { - modelCount = 0; - process = p.start(); - logger.info("ProcessBuilder start"); - logger.info("PYTHONUSERBASE: " + p.environment().get("PYTHONUSERBASE")); - String pathVar = "PATH"; - if(!this.isLinux) { - pathVar = "Path"; - } - logger.info("PATH: " + p.environment().get(pathVar)); - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - CompletableFuture errFuture = readOutStream(process.getErrorStream()); - errFuture.thenAccept( (error) ->{ - checkStdError(error); - }); - var line = ""; - ObjectMapper objectMapper = new ObjectMapper(); - while((line = reader.readLine()) != null) { - JsonNode node = objectMapper.readTree(line.getBytes()); - logger.info(node.get("info").get("msg").asText()); - JsonNode data = node.get("data"); - if (data.get("stats") != null) { - stats = node; - } else { - if (data.has("index")) { - modelCount = Math.max(modelCount, data.get("index").asInt()); - } - dbtLog.add(node); - } - } - } catch (Exception e) { - logger.error("Couldn't start dbt run!", e); - this.runtimeErrors = ("Couldn't start dbt run!\n" + e.getMessage()); - } finally { - if(this.shouldGenerateDoc) { - generateDocs(env); - } - } - } - - private Map getComponentStatus() { - Map result = new HashMap<>(); - EtlJobApi api = etlJobApi == null ? SuperXManager.getBean(null, EtlJobApi.class) : etlJobApi; - List components = api.getInstallJobs(); - for (Component comp : components) { - result.put("COMP_" + comp.getAbbreviation().toUpperCase() + "_INSTALLED", comp.isInstalled() ? "TRUE" : "FALSE"); - } - return result; - } - - public int getModelCount() { - return modelCount; - } - - public List getErrorMessages() { - var result = new ArrayList(); - if (!this.runtimeErrors.isEmpty()) { - // stderr was not empty! - result.add(runtimeErrors); - } else { - // "regular" errors during dbt execution - for (var node : dbtLog) { - var level = findFirstOccurence("level", node); - if (level.isPresent() && "error".equals(level.get().asText())) { - var msg = findFirstOccurence("msg", node); - if (msg.isPresent()) { - result.add(msg.get().asText()); - } else { - logger.warn("No msg property found in dbt log " + node); - } - } else { - logger.warn("No level property found in dbt log " + node); - } - } - } - return result; - } - - public Process getProcess() { - return this.process; - } - - public ExecutionState getExecutionState() { - if (this.stats == null) { - return ExecutionState.ACTIVE; - } - if (this.stats.get("data").get("stats").get("error").asInt() == 0) { - return ExecutionState.SUCCESS; - } - return ExecutionState.FAILURE; - } - - public void generateDocs(Map env) { - try { - Path dbt = DbtUtils.getDbtBinaryPath(); - List pbCommand = new ArrayList(Arrays.asList(pythonExecutable, dbt.toString(), "--no-use-colors", "docs", "generate")); - if(select != null && !select.isBlank()) { - pbCommand.add("--select"); - pbCommand.add(select); - } - ProcessBuilder pb = new ProcessBuilder(pbCommand).directory(projectPath.toFile()); - pb.environment().putAll(env); - pb.redirectErrorStream(true); - Process p = pb.start(); - final InputStream stdoutInputStream = p.getInputStream(); - final BufferedReader stdoutReader = - new BufferedReader(new InputStreamReader(stdoutInputStream)); - String line = null; - while ((line = stdoutReader.readLine()) != null) { - logger.info(line); - } - } catch (Exception iox) { - throw new RuntimeException("Couldn't run dbt generate docs ", iox); - } - } - - public int getNodeCount() { - int lineNr = 0; - try { - Path dbt = DbtUtils.getDbtBinaryPath(); - List pbCommand = new ArrayList(Arrays.asList( - pythonExecutable, dbt.toString(), - "ls", - "--resource-types" - )); - - if(this.test) { - pbCommand.add("test"); - } else { - pbCommand.addAll(Arrays.asList( - "snapshot", - "seed", - "model" - )); - } - if(select != null && !select.isBlank()) { - pbCommand.add("--select"); - pbCommand.add(select); - } - ProcessBuilder pb = new ProcessBuilder(pbCommand).directory(projectPath.toFile()); - Map env = getComponentStatus(); - env.putAll(getDbtEnvironment()); - pb.environment().putAll(env); - pb.redirectErrorStream(true); - Process p = pb.start(); - final InputStream stdoutInputStream = p.getInputStream(); - final BufferedReader stdoutReader = - new BufferedReader(new InputStreamReader(stdoutInputStream)); - String line = ""; - while ((line = stdoutReader.readLine()) != null) { - if (!line.matches("(\\w+\\.)+\\w+$")) { - continue; - } - lineNr += 1; - logger.info(line); - } - } catch (Exception iox) { - throw new RuntimeException("Couldn't run dbt ls ", iox); - } - return lineNr; - } - - - public Map getDbtEnvironment() { - Map env = Map.ofEntries( - entry("PYTHONUSERBASE", dbtInstallDir.toString()), -// entry("DBT_TARGET_PATH", SuperXManager.getWEB_INFPfad() + File.separator + "dbt_target"), - entry("DBT_HOST", host), - entry("DBT_PORT", port.toString()), - entry("DBT_USER", user), - entry("DBT_ENV_SECRET_PW", password), - entry("DBT_DB", db) - ); return env; } - public List getDbtLog() { - return List.copyOf(this.dbtLog); - } - - private static Optional findFirstOccurence(String name, JsonNode searchNode) { - JsonNode parent = searchNode.findParent(name); - JsonNode result = parent != null ? parent.get(name) : null; - return Optional.of(result); - } - - private boolean checkStdError(String errorString) { - boolean error = false; - if (errorString.length() > 0) { - this.runtimeErrors = "Error starting dbt: \n" + errorString; - logger.error(runtimeErrors); - error = true; - } - return error; - } - - private static CompletableFuture readOutStream(InputStream is) { - return CompletableFuture.supplyAsync(() -> { - try ( - InputStreamReader isr = new InputStreamReader(is); - BufferedReader br = new BufferedReader(isr); - ) { - StringBuilder res = new StringBuilder(); - String inputLine; - while ((inputLine = br.readLine()) != null) { - res.append(inputLine).append(System.lineSeparator()); - } - return res.toString(); - } catch (Throwable e) { - throw new RuntimeException("problem with executing program", e); - } - }); - } - private Map getUnloadParams() { Map result = new HashMap<>(); if (dataSource != null) { @@ -442,46 +120,32 @@ public class DbtWrapper implements Runnable { } return params; } - }); } return result; } - public static List getStagingTables(String select) { - try { - Path dbtProjectDir = Path.of(SuperXManager.getWEB_INFPfad(), "..", "dbt", "projects", "hisinone", "transform"); - ProcessBuilder builder = new ProcessBuilder(DbtUtils.getPython3ExecutableName(), DbtUtils.getDbtBinaryPath().toString(), "ls", "--select", select, "--output", "name").directory(dbtProjectDir.toFile()); - Map env = builder.environment(); - env.put("COMP_RES_INSTALLED", "true"); - env.put("DBT_DB", "eduetl"); - env.put("PYTHONUSERBASE", DbtUtils.getDbtInstallDir().toString()); - Process process = builder.start(); - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - CompletableFuture errFuture = readOutStream(process.getErrorStream()); - errFuture.thenAccept( (error) ->{ - if (error.length() > 0) { - logger.warn("WARNING: could not get staging tables for select: " + select); - logger.warn(error); - } - }); - List results = reader.lines().collect(Collectors.toList()); - List tables = new ArrayList(); - for (String model : results) { - if(model.startsWith("staging.")) { - tables.add(model.split("staging.")[1]); - } - } - return tables; - } catch (Exception e) { - e.printStackTrace(); + private Map getComponentStatus() { + Map result = new HashMap<>(); + EtlJobApi api = etlJobApi == null ? ApplicationContextHolder.getBean(null, EtlJobApi.class) : etlJobApi; + List components = api.getInstallables(); + for (Component comp : components) { + result.put("COMP_" + comp.getAbbreviation().toUpperCase() + "_INSTALLED", comp.isInstalled() ? "TRUE" : "FALSE"); } - return null; + return result; } - public void setShouldGenerateDoc(boolean shouldGenerateDoc) { - this.shouldGenerateDoc = shouldGenerateDoc; + private Map getDbtEnvironment() { + Map env = Map.ofEntries( + entry("PYTHONUSERBASE", DbtUtils.getDbtInstallDir().toString()), + entry("DBT_HOST", dbtProfile.get("DBT_HOST")), + entry("DBT_PORT", dbtProfile.get("DBT_PORT")), + entry("DBT_USER", dbtProfile.get("DBT_USER")), + entry("DBT_ENV_SECRET_PW", dbtProfile.get("DBT_ENV_SECRET_PW")), + entry("DBT_DB", dbtProfile.get("DBT_DB")) + ); + return env; } - + } diff --git a/src/de/superx/etl/biad/DBSource.java b/src/de/superx/etl/biad/DBSource.java index 9ae86d6..33bbef6 100644 --- a/src/de/superx/etl/biad/DBSource.java +++ b/src/de/superx/etl/biad/DBSource.java @@ -2,15 +2,12 @@ package de.superx.etl.biad; import java.util.List; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import de.superx.job.ExtractAction; - public class DBSource { private String name; private String schema; private String database; + private List tables; public DBSource(String name, String schema, String database, List tables) { @@ -49,10 +46,18 @@ public class DBSource { public String getDatabase() { return database; } - + public void setDatabase(String database) { this.database = database; } + + public String getSrcDatabase() { + int indexUnderscore = schema.indexOf('_'); + if (indexUnderscore != -1) { + return schema.substring(indexUnderscore + 1); + } + return "hisinone"; + } public List getTables() { return tables; diff --git a/src/de/superx/jdbc/entity/Cif.java b/src/de/superx/jdbc/entity/Cif.java index 6d3b899..237afd7 100644 --- a/src/de/superx/jdbc/entity/Cif.java +++ b/src/de/superx/jdbc/entity/Cif.java @@ -33,7 +33,7 @@ public class Cif extends EntityBase { @DynamicFieldType(label = "Gültig bis", editControlType = FieldType.DatePicker, columnType = ColumnType.DateColumn, visibleInSimplifiedForm = false) public Date d_akt_bis; - @DynamicFieldType(label = "Kurzbeschreibung") + @DynamicFieldType(label = "Kurztext") public String kurz; @DynamicFieldType(label = "Drucktext") @@ -48,7 +48,7 @@ public class Cif extends EntityBase { @DynamicFieldType(label = "Langbesch. 3", visibleInSimplifiedForm = false) public String lang_3; - @DynamicFieldType(label = "Eindeutiger Name") + @DynamicFieldType(label = "Schlüssel (uniquename)") public String uniquename; @DynamicFieldType(label = "sort1", visibleInSimplifiedForm = false) diff --git a/src/de/superx/jdbc/entity/Cifx.java b/src/de/superx/jdbc/entity/Cifx.java index 1f0b005..dd86a5a 100644 --- a/src/de/superx/jdbc/entity/Cifx.java +++ b/src/de/superx/jdbc/entity/Cifx.java @@ -34,7 +34,7 @@ public class Cifx extends EntityBase { @DynamicFieldType(label = "Gültig bis", editControlType = FieldType.DatePicker, columnType = ColumnType.DateColumn, visibleInSimplifiedForm = false) public Date d_akt_bis; - @DynamicFieldType(label = "Kurzbeschreibung") + @DynamicFieldType(label = "Kurztext") public String kurz; @DynamicFieldType(label = "Drucktext") @@ -73,7 +73,7 @@ public class Cifx extends EntityBase { @DynamicFieldType(label = "HIS-KEY") public String hiskey_id; - @DynamicFieldType(label = "Eindeutiger Name") + @DynamicFieldType(label = "Schlüssel (uniquename)") public String uniquename; // wird nicht verwendet diff --git a/src/de/superx/jdbc/entity/CifxCustomize.java b/src/de/superx/jdbc/entity/CifxCustomize.java new file mode 100644 index 0000000..65a4cd9 --- /dev/null +++ b/src/de/superx/jdbc/entity/CifxCustomize.java @@ -0,0 +1,43 @@ +package de.superx.jdbc.entity; + +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Table; + +import de.superx.jdbc.model.DynamicFieldType; +import de.superx.jdbc.model.EntityDescriptor; + +@Table("cifx_customize") +public class CifxCustomize extends EntityBase { + + @Id + @DynamicFieldType(label = "ID", visibleInSimplifiedForm = false, readOnly = true) + public Integer tid; + + @DynamicFieldType(label = "CIFX Tabelle") + public String cifx_table; + + @DynamicFieldType(label = "Schlüsselgruppe (key)") + public Integer key; + + @EntityDescriptor + @DynamicFieldType(label = "Schlüssel (apnr)") + public String apnr; + + @DynamicFieldType(label = "Kurztext") + public String kurz; + + @DynamicFieldType(label = "Drucktext") + public String druck; + + @DynamicFieldType(label = "Langbesch. 1", visibleInSimplifiedForm = false) + public String lang_1; + + @DynamicFieldType(label = "Schlüssel (uniquename)") + public String uniquename; + + @DynamicFieldType(label = "struktur_c", visibleInSimplifiedForm = true) + public String struktur_c; + + + +} diff --git a/src/de/superx/jdbc/repository/CifxCustomizeRepository.java b/src/de/superx/jdbc/repository/CifxCustomizeRepository.java new file mode 100644 index 0000000..6082515 --- /dev/null +++ b/src/de/superx/jdbc/repository/CifxCustomizeRepository.java @@ -0,0 +1,9 @@ +package de.superx.jdbc.repository; + +import org.springframework.data.repository.RepositoryDefinition; + +import de.superx.jdbc.entity.CifxCustomize; + +@RepositoryDefinition(domainClass = CifxCustomize.class, idClass = Integer.class) +public interface CifxCustomizeRepository extends BiaAdminCrudRepository { +} diff --git a/src/de/superx/job/ContainerNode.java b/src/de/superx/job/ContainerNode.java index acbdbad..da2ad82 100644 --- a/src/de/superx/job/ContainerNode.java +++ b/src/de/superx/job/ContainerNode.java @@ -82,10 +82,12 @@ public class ContainerNode extends ActionNode { private Map parameters = new HashMap<>(); private boolean ignoreErrors = false; private JobSource source = JobSource.FILE; + private String description; public ContainerNode(String name, String id, Integer systemInfoId) { super(name, id, systemInfoId, true, ActionType.CONTAINER); this.jobType = JobType.getJobTypeForName(id); + this.description = name; } public void add(ActionNode node) { @@ -168,6 +170,14 @@ public class ContainerNode extends ActionNode { return source; } + public void setDescription(String description) { + this.description = description; + } + + public String getDescription() { + return this.description; + } + public void setCustom(int custom) { this.custom = Integer.valueOf(custom); List children = this.getActions(); diff --git a/src/de/superx/job/ExecuteSQLFluffAction.java b/src/de/superx/job/ExecuteSQLFluffAction.java index fcb427a..a91cc5a 100644 --- a/src/de/superx/job/ExecuteSQLFluffAction.java +++ b/src/de/superx/job/ExecuteSQLFluffAction.java @@ -1,8 +1,12 @@ package de.superx.job; public class ExecuteSQLFluffAction extends ActionNode { - public ExecuteSQLFluffAction (String name, String id, Integer systemInfoId, boolean active, Integer custom) { + + public String dbtProject; + + public ExecuteSQLFluffAction (String dbtProject, String name, String id, Integer systemInfoId, boolean active, Integer custom) { super(name, id, systemInfoId, active, custom, ActionType.EXECUTE_SQLFLUFF); + this.dbtProject = dbtProject; } public ExecuteSQLFluffAction () {} diff --git a/src/de/superx/job/ExtractAction.java b/src/de/superx/job/ExtractAction.java index ea5821f..48b447d 100644 --- a/src/de/superx/job/ExtractAction.java +++ b/src/de/superx/job/ExtractAction.java @@ -35,10 +35,10 @@ public class ExtractAction extends ActionNode { super(); } - public static ExtractAction createFromSourceTable(SourceTable table, String unlFile, String schema) { + public static ExtractAction createFromSourceTable(SourceTable table, String unlFile, String schema, String sourceSystem) { return new ExtractAction(table.getName(), table.getName(), table.getExtractionScript(), table.getExtractionInclude(), - schema, unlFile, "hisinone", Integer.valueOf(440), null); + schema, unlFile, sourceSystem, Integer.valueOf(440), null); } private static String makeName(String id, String unlFile, String dataSource) { diff --git a/src/de/superx/rest/Cache.java b/src/de/superx/rest/Cache.java index a919a61..8c1f57c 100644 --- a/src/de/superx/rest/Cache.java +++ b/src/de/superx/rest/Cache.java @@ -42,7 +42,7 @@ public class Cache { public static CacheInfo status() { return new CacheInfo(TransletCache.getLastClear(), TransletCache.getTransletPoolSize(), TransletCache.getMerkmaleSize()); } - @RequestMapping(path = "/clearpool", method = RequestMethod.GET) + @RequestMapping(path = "/clearpool", method = RequestMethod.POST) public static String clearpool(@Context HttpServletRequest httpRequest) { String returntext="Problem: Cannot clear server cache."; try { @@ -58,7 +58,7 @@ public class Cache { } return returntext; } - @RequestMapping(path = "/cleartranslet", method = RequestMethod.GET) + @RequestMapping(path = "/cleartranslet", method = RequestMethod.POST) public static String cleartranslet(@Context HttpServletRequest httpRequest) { String returntext="Problem: Cannot clear translet cache."; try { diff --git a/src/de/superx/rest/EtlJobApi.java b/src/de/superx/rest/EtlJobApi.java index ec251bb..87385e9 100644 --- a/src/de/superx/rest/EtlJobApi.java +++ b/src/de/superx/rest/EtlJobApi.java @@ -170,7 +170,7 @@ public class EtlJobApi extends RestControllerBase { * Stop all running JobExecutions. * @return */ - @RequestMapping(path="/job/stopAll", method = RequestMethod.GET) + @RequestMapping(path="/job/stopAll", method = RequestMethod.POST) public boolean stopAllJobs() { userService.checkAdminRights(); JobUtils.terminateAllJobExecutions(jobExplorer, jobOperator); @@ -189,7 +189,7 @@ public class EtlJobApi extends RestControllerBase { return jobDescriptionSource.getJobsForComponent(abbreviation); } - @RequestMapping(path="/job/writeJobsToDb", method = RequestMethod.GET) + @RequestMapping(path="/job/writeJobsToDb", method = RequestMethod.POST) public boolean writeJobsToDb() { try { List jobs = entityJobDescriptionSource.readJobs(); @@ -216,6 +216,7 @@ public class EtlJobApi extends RestControllerBase { @RequestMapping(path="/job/save", method = RequestMethod.POST) public Long saveJob(@RequestBody final Map jobJson) throws Exception { + userService.checkAdminRights(); ContainerNode container = (ContainerNode) jsonJobDescriptionAdapter.convert(jobJson); // TODO beim Speichern in der DB wird custom=true gesetzt, weil sonst (im Moment noch?) // Beim nächsten Tomcatstart der Job von SuperXManager.initEtl() wieder gelöscht wird! @@ -233,10 +234,11 @@ public class EtlJobApi extends RestControllerBase { return executeJob(sourceDatabase, container); } - @RequestMapping(path="/job/execute/{sourceDatabase}/{id}", method=RequestMethod.GET) + @RequestMapping(path="/job/execute/{sourceDatabase}/{id}", method=RequestMethod.POST) public Long executeJob(@PathVariable(value = "sourceDatabase") String sourceDatabase, @PathVariable(value="id") String id) throws DuplicateJobException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException, UnsupportedPythonVersionException { + userService.checkEtlJobRights(); // TODO There is no official convention, // that job names start with component name // but for now it holds. @@ -320,7 +322,7 @@ public class EtlJobApi extends RestControllerBase { return startJob(job, stepCounter.getCount(), additionalParameters); } - @RequestMapping(path="/job/install/{component}", method=RequestMethod.GET) + @RequestMapping(path="/job/install/{component}", method=RequestMethod.POST) public Long executeInstall(@PathVariable(value="component") String component) throws Exception { userService.checkAdminRights(); if (isAnyJobInstanceRunning()) { @@ -383,7 +385,7 @@ public class EtlJobApi extends RestControllerBase { return startJob(job, stepCounter.getCount(), parameterBuilder.toJobParameters()); } - @RequestMapping(path="/job/upgrade/{component}", method=RequestMethod.GET) + @RequestMapping(path="/job/upgrade/{component}", method=RequestMethod.POST) public Long executeUpgrade(@PathVariable(value="component") String component) throws Exception { userService.checkAdminRights(); if (isAnyJobInstanceRunning()) { @@ -402,7 +404,7 @@ public class EtlJobApi extends RestControllerBase { return startJob(job, stepCounter.getCount(), parameterBuilder.toJobParameters()); } - @RequestMapping(path="/job/upgrade_all", method=RequestMethod.GET) + @RequestMapping(path="/job/upgrade_all", method=RequestMethod.POST) public Long executeUpgradeAll() throws Exception { userService.checkAdminRights(); if (isAnyJobInstanceRunning()) { @@ -427,7 +429,7 @@ public class EtlJobApi extends RestControllerBase { return startJob(job, stepCounter.getCount(), null); } - @RequestMapping(path="/job/uninstall/{component}", method=RequestMethod.GET) + @RequestMapping(path="/job/uninstall/{component}", method=RequestMethod.POST) public Long executeUninstall(@PathVariable(value="component") String component) throws Exception { userService.checkAdminRights(); if (isAnyJobInstanceRunning()) { @@ -439,7 +441,7 @@ public class EtlJobApi extends RestControllerBase { return startJob(job, stepCounter.getCount(), null); } - @RequestMapping(path="/unload/{component}", method = RequestMethod.GET) + @RequestMapping(path="/unload/{component}", method = RequestMethod.POST) public Long unload(@PathVariable(value = "component") String component) throws Exception { userService.checkEtlJobRights(); if (isAnyJobInstanceRunning()) { @@ -452,7 +454,7 @@ public class EtlJobApi extends RestControllerBase { return startJob(job, stepCounter.getCount(), null); } - @RequestMapping(path="/transform/{component}", method = RequestMethod.GET) + @RequestMapping(path="/transform/{component}", method = RequestMethod.POST) public Long load(@PathVariable(value = "component") String component) throws Exception { userService.checkEtlJobRights(); if (isAnyJobInstanceRunning()) { @@ -465,7 +467,7 @@ public class EtlJobApi extends RestControllerBase { return startJob(job, stepCounter.getCount(), null); } - @RequestMapping(path="/complete/{component}", method = RequestMethod.GET) + @RequestMapping(path="/complete/{component}", method = RequestMethod.POST) public Long complete(@PathVariable(value = "component") String component) throws Exception { userService.checkEtlJobRights(); if (isAnyJobInstanceRunning()) { @@ -622,13 +624,13 @@ public class EtlJobApi extends RestControllerBase { } - @RequestMapping(path="/stop/{id}", method = RequestMethod.GET) + @RequestMapping(path="/stop/{id}", method = RequestMethod.POST) public boolean stop(@PathVariable(value = "id") Long id) throws Exception { userService.checkEtlJobRights(); return JobUtils.terminateJobExecution(jobExplorer, jobOperator, id); } - @RequestMapping(path="/start/{id}", method = RequestMethod.GET) + @RequestMapping(path="/start/{id}", method = RequestMethod.POST) public Long start(@PathVariable(value = "id") Long id) throws Exception { userService.checkEtlJobRights(); Long started = jobOperator.restart(id.longValue()); @@ -636,7 +638,7 @@ public class EtlJobApi extends RestControllerBase { } - @RequestMapping(path="/installFunctions/{component}", method = RequestMethod.GET) + @RequestMapping(path="/installFunctions/{component}", method = RequestMethod.POST) public boolean installModuleFunctions(@PathVariable(value = "component") String component) { File moduleFile = PathAndFileUtils.getXmlFile(component); SAXReader sr = new SAXReader(); @@ -913,6 +915,7 @@ public class EtlJobApi extends RestControllerBase { job.getJobType(), job.tid == null ? null : Long.valueOf(job.tid.longValue()), job.id); + connector.setDescription(job.getDescription()); component.addChild(connector); } } @@ -956,7 +959,7 @@ public class EtlJobApi extends RestControllerBase { } } - private List getInstallables(){ + public List getInstallables(){ List components = new ArrayList<>(); try { //List nodes = ((DbJobDescriptionAdapter) jobDescriptionSource).readInstallJobs(null); diff --git a/src/de/superx/rest/LogsProvider.java b/src/de/superx/rest/LogsProvider.java index cda60a1..95d90eb 100644 --- a/src/de/superx/rest/LogsProvider.java +++ b/src/de/superx/rest/LogsProvider.java @@ -120,7 +120,7 @@ public class LogsProvider { } - @RequestMapping(path = "/reload_log4j", method = RequestMethod.GET) + @RequestMapping(path = "/reload_log4j", method = RequestMethod.POST) public static String reloadLog4J(@Context HttpServletRequest httpRequest) { logger.info("*** log4j.properties neu einlesen ***"); String webInfPath = PathAndFileUtils.getWebinfPath(); diff --git a/src/de/superx/rest/Report.java b/src/de/superx/rest/Report.java index 0152925..628c737 100644 --- a/src/de/superx/rest/Report.java +++ b/src/de/superx/rest/Report.java @@ -306,6 +306,8 @@ public class Report extends ReportApiBase { writeOutputToRespons(params, request_wrapper, response_interceptor, maske, currentXml); response_interceptor.setContentType(MediaType.APPLICATION_JSON_VALUE); Download dl = createDownloadObject(response_interceptor, maske, user); + // set content-type again for 'safety' - see https://hiszilla.his.de/hiszilla/show_bug.cgi?id=375530 + response_interceptor.setContentType(MediaType.APPLICATION_JSON_VALUE); return dl; } } diff --git a/src/de/superx/rest/RestControllerBase.java b/src/de/superx/rest/RestControllerBase.java index dfffca8..e99eaa8 100644 --- a/src/de/superx/rest/RestControllerBase.java +++ b/src/de/superx/rest/RestControllerBase.java @@ -22,6 +22,7 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcep import de.memtext.tree.KeyParentEqualException; import de.memtext.tree.NoMainEntryException; import de.superx.bianalysis.FaultyMetadataException; +import de.superx.bianalysis.service.IllegalMetaDataOperation; import de.superx.common.AccessDeniedException; import de.superx.common.ConfigurationException; import de.superx.common.DBServletException; @@ -127,6 +128,8 @@ abstract public class RestControllerBase { return handleFaultyMetadataException((FaultyMetadataException)ex); } else if (ex instanceof RestMessageWrapperException) { return handleRestMessageWrapperException((RestMessageWrapperException)ex); + } else if (ex instanceof IllegalMetaDataOperation) { + return handleIllegalMetaDataOperation((IllegalMetaDataOperation)ex); } else if (ex instanceof RuntimeException) { // Log stacktraces of runtime exceptions, otherwise the information is lost! @@ -249,6 +252,11 @@ abstract public class RestControllerBase { return handleServerError(ex.getMessage()); } + public ResponseEntity handleIllegalMetaDataOperation(IllegalMetaDataOperation ex) { + getLogger().error("IllegalMetaDataOperation occured", ex); + return handleServerError(ex.getMessage()); + } + public ResponseEntity handleFaultyMetadataException(FaultyMetadataException ex) { getLogger().error("FaultyMetadataException occured", ex); return handleServerError(ex.getMessage()); diff --git a/src/de/superx/rest/model/Column.java b/src/de/superx/rest/model/Column.java index 1620c7f..195030b 100644 --- a/src/de/superx/rest/model/Column.java +++ b/src/de/superx/rest/model/Column.java @@ -21,6 +21,11 @@ public class Column { this.type = type; } + public Column(String field, String header) { + this.field = field; + this.header = header; + } + public Column(String field, String header, ColumnType type, boolean groupable) { this.field = field; this.header = header; diff --git a/src/de/superx/rest/model/ColumnType.java b/src/de/superx/rest/model/ColumnType.java index 03cd397..7a3fd59 100644 --- a/src/de/superx/rest/model/ColumnType.java +++ b/src/de/superx/rest/model/ColumnType.java @@ -20,6 +20,7 @@ public enum ColumnType { TimestampColumn(8, new CustomDateFormat("dd.MM.yyyy HH:mm:ss.SSSSSX")), // 8=timestamp TimeColumn(9, new CustomDateFormat("HH:mm:ss")), // 9=time StringColumn(1), // Default ist 1=string^ + MinuteAndSecondColumn(16), // Spezielle, abgeleitete Spaltentypen (links, etc.) DetailColumn(10, new DetailFormat()), // Information about a Detail-Report for this Master-Report @@ -86,7 +87,14 @@ public enum ColumnType { try { result = format.format(val); } catch (IllegalArgumentException e) { - logger.error("Couldn't format value " + val + " as " + format, e); + /* log-Level runter gesetzt: beim Aufruf von Berichten kommt + * es z.T. zu vielen Parsingexceptions, der Bericht wird dennoch + * angezeigt, aber das Logfile explodiert - das sollte nicht sein, + * daher bekommt die Meldung geringere Relevanz + * (man kann überlegen, evtl. sogar "DEBUG" zu setzen!?) + * https://hiszilla.his.de/hiszilla/show_bug.cgi?id=375194 + */ + logger.info("Couldn't format value " + val + " as " + format, e); } } return result; diff --git a/src/de/superx/rest/model/Result.java b/src/de/superx/rest/model/Result.java index 2e07ede..39b0d29 100644 --- a/src/de/superx/rest/model/Result.java +++ b/src/de/superx/rest/model/Result.java @@ -22,6 +22,7 @@ public class Result { public Legend legend = new Legend(); public List columns = new ArrayList(); public List rows = new ArrayList(); + public List headerOrder = new ArrayList<>(); public List subResults = new ArrayList(); public Result() { } diff --git a/src/de/superx/rest/model/job/Connector.java b/src/de/superx/rest/model/job/Connector.java index 3e6240f..a3da381 100644 --- a/src/de/superx/rest/model/job/Connector.java +++ b/src/de/superx/rest/model/job/Connector.java @@ -4,6 +4,7 @@ import de.superx.job.ContainerNode.JobType; public class Connector { private String name; + private String description; private String sourceDatabase; private JobType jobType; private Long jobId; @@ -22,6 +23,14 @@ public class Connector { return name; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public JobType getJobType() { return jobType; } diff --git a/src/de/superx/saiku/SuperxMondrianDataSourceResolver.java b/src/de/superx/saiku/SuperxMondrianDataSourceResolver.java index 6d7bb03..5fc0810 100644 --- a/src/de/superx/saiku/SuperxMondrianDataSourceResolver.java +++ b/src/de/superx/saiku/SuperxMondrianDataSourceResolver.java @@ -4,8 +4,8 @@ import javax.sql.DataSource; import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import de.superx.servlet.SuperXManager; import de.superx.spring.batch.His1DataSources; +import de.superx.spring.service.ApplicationContextHolder; import mondrian.spi.DataSourceResolver; public class SuperxMondrianDataSourceResolver implements DataSourceResolver { @@ -14,7 +14,7 @@ public class SuperxMondrianDataSourceResolver implements DataSourceResolver { public DataSource lookup(String dataSourceName) throws Exception { DataSource result = null; try { - His1DataSources h1ds = SuperXManager.getBean("dataSources", His1DataSources.class); + His1DataSources h1ds = ApplicationContextHolder.getBean("dataSources", His1DataSources.class); result = h1ds.get(dataSourceName); } catch (NoSuchBeanDefinitionException nsbde) { return null; diff --git a/src/de/superx/servlet/AbstractSuperXServlet.java b/src/de/superx/servlet/AbstractSuperXServlet.java index c6fc02b..2e969bb 100644 --- a/src/de/superx/servlet/AbstractSuperXServlet.java +++ b/src/de/superx/servlet/AbstractSuperXServlet.java @@ -144,15 +144,8 @@ public abstract class AbstractSuperXServlet extends HttpServlet { public static String insertCsrfToken(HttpServletRequest request, String html) throws IOException { CsrfToken token = SuperXServletHelper.getCsRfToken(request); // -> introduce more testable signature - if(token==null) - { - return html; - } - else - { String result = HtmlUtils.insertCsrfToken(token, html); return result; - } } /** diff --git a/src/de/superx/servlet/DbFormsController.java b/src/de/superx/servlet/DbFormsController.java deleted file mode 100644 index 7c42f6f..0000000 --- a/src/de/superx/servlet/DbFormsController.java +++ /dev/null @@ -1,446 +0,0 @@ -package de.superx.servlet; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.dbforms.config.DbFormsConfig; -import org.dbforms.config.DbFormsConfigRegistry; -import org.dbforms.config.MultipleValidationException; -import org.dbforms.config.Table; - -import org.dbforms.event.DatabaseEvent; -import org.dbforms.event.EventEngine; -import org.dbforms.event.WebEvent; - -import org.dbforms.util.MessageResources; -import org.dbforms.util.MultipartRequest; -import org.dbforms.util.ParseUtil; -import org.dbforms.util.SqlUtil; -import org.dbforms.util.Util; - -import org.dbforms.validation.ValidatorConstants; - -import de.superx.util.DbFormsMultipartRequest; - -import java.io.IOException; -import java.io.PrintWriter; - -import java.sql.Connection; -import java.sql.SQLException; - -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Locale; -import java.util.Vector; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; -import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID; - -/** - * - * @author Witt - * This class inherits from the DbForms Controller: - * As CSRF protection broke the file upload, we need to adjust some classes...and this is the way we "inject" them to DbForms... - */ -public class DbFormsController extends HttpServlet { - /** logging category for this class */ - private static Log logCat = LogFactory.getLog(DbFormsController.class.getName()); - - /** 100KB default upload size */ - private int maxUploadSize = 102400; - - /** - * Process an HTTP "GET" request. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet exception occurs - */ - @Override - public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - process(request, response); - } - - - /** - * Process an HTTP "POST" request. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet exception occurs - */ - @Override - public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - process(request, response); - } - - - /** - * Initialize this servlet. - * - * @exception ServletException if the initialization fails - */ - @Override - public void init() throws ServletException { - // if existing and valid, override default maxUploadSize, which determinates how - // big the http/multipart uploads may get - String maxUploadSizeStr = this.getServletConfig().getInitParameter("maxUploadSize"); - - if (maxUploadSizeStr != null) { - try { - this.maxUploadSize = Integer.parseInt(maxUploadSizeStr); - } catch (NumberFormatException nfe) { - logCat.error("maxUploadSize not a valid number => using default."); - } - } - } - - - /** - * Gets the connection object. Holds a list of all used connections. So they - * stay open between calls an can be reused - * - * @param request the request object - * @param tableId the table identifier - * @param connectionsTable the connections hash table - * - * @return The connection object - */ - private Connection getConnection(DbFormsConfig config, HttpServletRequest request, int tableId, Hashtable connectionsTable) throws SQLException { - String connectionName = null; - Connection con = null; - - // get the connection name from the request; - if (tableId != -1) { - connectionName = ParseUtil.getParameter(request, "invname_" + tableId); - } - - connectionName = Util.isNull(connectionName) ? DEFAULT_MANDANTEN_ID : connectionName; - - if ((con = (Connection) connectionsTable.get(connectionName)) == null) { - con = config.getConnection(connectionName); - connectionsTable.put(connectionName, con); - } - - return con; - } - - - /** - * In our development, we sometimes set the connection object to autoCommit - * = false in the interceptor (Pre... methods). This allows us to have - * dbForms do part of the required transaction (other parts are done via - * jdbc calls). If the database throws an exception, then we need to make - * sure that the connection is reinitialized (rollbacked) before it is sent - * back into the connection pool. Grunikiewicz.philip&at;hydro.qc.ca - * 2001-10-29 - * - * @param con the connection object - */ - private void cleanUpConnectionAfterException(Connection con) { - try { - // Do only if autoCommit is disabled - if ((con != null) && (!con.getAutoCommit())) { - con.rollback(); - con.setAutoCommit(true); - } - } catch (java.sql.SQLException e) { - SqlUtil.logSqlException(e); - } - } - - - /** - * Close all the connections stored into the the connections HashTable. - * - * @param connectionsTable the connections HashTable - */ - private void closeConnections(Hashtable connectionsTable) { - Enumeration cons = connectionsTable.keys(); - - while (cons.hasMoreElements()) { - String dbConnectionName = (String) cons.nextElement(); - Connection con = (Connection) connectionsTable.get(dbConnectionName); - - try { - // Do only if autoCommit is disabled - if ((con != null) && (!con.getAutoCommit())) { - con.commit(); - con.setAutoCommit(true); - } - } catch (java.sql.SQLException e) { - SqlUtil.logSqlException(e); - } - - SqlUtil.closeConnection(con); - } - } - - - /** - * Process the incoming requests. - * - * @param request the request object - * @param response the response object - * - * @throws IOException - * @throws ServletException - */ - private void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - HttpSession session = request.getSession(true); - logCat.debug("session timeout: " + session.getMaxInactiveInterval()); - - // take Config-Object from application context - this object should have been - // initalized by Config-Servlet on Webapp/server-startup! - DbFormsConfig config = null; - - try { - config = DbFormsConfigRegistry.instance().lookup(); - } catch (Exception e) { - logCat.error(e); - throw new ServletException(e); - } - - // create RFC-1867 data wrapper for the case the form was sent in multipart mode - // this is needed because common HttpServletRequestImpl - classes do not support it - // the whole application has to access Request via the "ParseUtil" class - // this is also true for jsp files written by the user - // #fixme taglib needed for convenient access to ParseUtil wrapper methods - Hashtable connections = new Hashtable(); - String contentType = request.getContentType(); - - // Verify if Locale have been setted in session with "LOCALE_KEY" - // if not, take the request.getLocale() as default and put it in session - // Verify if Locale have been setted in session with "LOCALE_KEY" - // if not, take the request.getLocale() as default and put it in session - processLocale(request); - - if ((contentType != null) && contentType.startsWith("multipart")) { - try { - MultipartRequest multipartRequest = new DbFormsMultipartRequest(request, maxUploadSize); - request.setAttribute("multipartRequest", multipartRequest); - } catch (IOException ioe) { - logCat.error("::process - check if uploaded file(s) exceeded allowed size", ioe); - sendErrorMessage("Check if uploaded file(s) exceeded allowed size.", response); - - return; - } - } - - Connection con = null; - WebEvent e = null; - Vector errors = new Vector(); - - try { - request.setAttribute("errors", errors); - - EventEngine engine = new EventEngine(request, config); - e = engine.generatePrimaryEvent(); - - // send as info to dbForms (=> Taglib) - if (e != null) { - request.setAttribute("webEvent", e); - } - - try { - con = getConnection(config, request, ((e == null) || (e.getTable() == null)) ? (-1) : e.getTable().getId(), connections); - - // primary event can be any kind of event (database, navigation...) - if (e instanceof DatabaseEvent) { - try { - // if hidden formValidatorName exist and it's an Update or Insert event, - // doValidation with Commons-Validator - String formValidatorName = request.getParameter(ValidatorConstants.FORM_VALIDATOR_NAME + "_" + e.getTable().getId()); - - if (formValidatorName != null) { - ((DatabaseEvent) e).doValidation(formValidatorName, getServletContext()); - } - - ((DatabaseEvent) e).processEvent(con); - } catch (SQLException sqle) { - logCat.error("::process - SQLException:", sqle); - errors.addElement(sqle); - cleanUpConnectionAfterException(con); - } catch (MultipleValidationException mve) { - processMultipleValidationException(con, errors, mve); - } - } - - //else - //{ - // currently, we support db events ONLY - // but in future there may be events with processEvent() method which do not need a jdbc con! - // (you may think: "what about navigation events?" - well they are created by the - // controller but they get executed in the referncing "DbFormTag" at the jsp -- that's why we - // do not any further operations on them right here...we just put them into the request) - //} - // secondary Events are always database events - // (in fact, they all are SQL UPDATEs) - if (engine.getInvolvedTables() != null) { - processInvolvedTables(config, request, connections, e, errors, engine); - } - } catch (SQLException exc) { - throw new ServletException(exc); - } - } finally { - // close all the connections stored into the connections hash table; - closeConnections(connections); - - if (e != null) { - // PG - if form contained errors, use followupOnError (if available!) - String followUp = null; - - if (errors.size() != 0) { - followUp = e.getFollowUpOnError(); - } - - if (Util.isNull(followUp)) { - followUp = e.getFollowUp(); - } - - logCat.info("*** e = " + e + "*** e.getFollowUp() = " + e.getFollowUp()); - - if (!Util.isNull(followUp)) { - request.getRequestDispatcher(followUp).forward(request, response); - } - } - } - } - - - /** - * PRIVATE METHODS here - * - * @param request DOCUMENT ME! - * @param connections DOCUMENT ME! - * @param e DOCUMENT ME! - * @param errors DOCUMENT ME! - * @param engine DOCUMENT ME! - */ - /** - * Process tables related to the main event table.
- * For every table related to the parent one, generate secundary (update) - * events for that table and execute them. - * - * @param request the request object - * @param connections the connections hashTable - * @param e the main webEvent object - * @param errors the errors vector - * @param engine the eventEngine reference - */ - private void processInvolvedTables(DbFormsConfig config, HttpServletRequest request, Hashtable connections, WebEvent e, Vector errors, EventEngine engine) throws SQLException { - Connection con; - - // may be null if empty form! - Enumeration tableEnum = engine.getInvolvedTables().elements(); - - while (tableEnum.hasMoreElements()) { - Table t = (Table) tableEnum.nextElement(); - Enumeration eventEnum = engine.generateSecundaryEvents(t, e); - - // scan all the secundary events for the current secundary table; - while (eventEnum.hasMoreElements()) { - DatabaseEvent dbE = (DatabaseEvent) eventEnum.nextElement(); - - // 2003-02-03 HKK: do not do the work twice - without this every event - // would be generated for each table and event - if (t.getId() == dbE.getTable().getId()) { - con = getConnection(config, request, dbE.getTable().getId(), connections); - - // 2003-02-03 HKK: do not do the work twice!!! - String formValidatorName = request.getParameter(ValidatorConstants.FORM_VALIDATOR_NAME + "_" + dbE.getTable().getId()); - - try { - // if hidden formValidatorName exist and it's an Update or Insert event, - // doValidation with Commons-Validator - if (formValidatorName != null) { - dbE.doValidation(formValidatorName, getServletContext()); - } - - dbE.processEvent(con); - } catch (SQLException sqle2) { - SqlUtil.logSqlException(sqle2, "::process - exception while process secundary events"); - errors.addElement(sqle2); - cleanUpConnectionAfterException(con); - } catch (MultipleValidationException mve) { - processMultipleValidationException(con, errors, mve); - } - } - } - } - } - - - /** - * Set the default locale - * - * @param request the request object - */ - private void processLocale(HttpServletRequest request) { - String lang = ParseUtil.getParameter(request, "lang"); - String country = ParseUtil.getParameter(request, "country", ""); - Locale locale = null; - - if (!Util.isNull(lang)) { - locale = new Locale(lang, country); - } else if (MessageResources.getLocale(request) == null) { - MessageResources.setLocale(request, request.getLocale()); - } - - if (locale != null) { - MessageResources.setLocale(request, locale); - } - } - - - /** - * Process the input MultipleValidationException object. - * - * @param con the connection object to close - * @param errors the errors vector to fill - * @param mve the MultipleValidationException to process - */ - private void processMultipleValidationException(Connection con, Vector errors, MultipleValidationException mve) { - java.util.Vector v = null; - - logCat.error("::processMultipleValidationException - exception", mve); - - if ((v = mve.getMessages()) != null) { - Enumeration e = v.elements(); - - while (e.hasMoreElements()) { - errors.addElement(e.nextElement()); - } - } - - cleanUpConnectionAfterException(con); - } - - - /** - * Send error messages to the servlet's output stream - * - * @param message the message to display - * @param response the response object - */ - private void sendErrorMessage(String message, HttpServletResponse response) { - try { - PrintWriter out = response.getWriter(); - - response.setContentType("text/html"); - out.println("

ERROR:

"); - out.println(message); - out.println("

"); - } catch (IOException ioe) { - logCat.error("::sendErrorMessage - senderror message crashed", ioe); - } - } -} diff --git a/src/de/superx/servlet/SuperXManager.java b/src/de/superx/servlet/SuperXManager.java index 6eab92e..72c6969 100644 --- a/src/de/superx/servlet/SuperXManager.java +++ b/src/de/superx/servlet/SuperXManager.java @@ -37,20 +37,12 @@ import org.pentaho.di.core.database.DataSourceProviderFactory; import org.pentaho.di.core.database.DataSourceProviderInterface; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.logging.KettleLogStore; -import org.quartz.JobDataMap; -import org.quartz.JobKey; -import org.quartz.ScheduleBuilder; -import org.quartz.Scheduler; -import org.quartz.SchedulerException; -import org.springframework.batch.core.configuration.JobLocator; -import org.springframework.batch.core.launch.JobLauncher; import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.security.web.csrf.CsrfToken; -import org.springframework.stereotype.Component; +import org.springframework.web.context.support.SpringBeanAutowiringSupport; import org.xml.sax.SAXException; import de.memtext.db.NichtAngemeldetException; @@ -71,7 +63,7 @@ import de.superx.saiku.SuperxRepositoryDataSourceManager; import de.superx.sec.HttpGetAccessGuard; import de.superx.sec.HttpGetAccessListParser; import de.superx.sec.InputCheckRegistry; -import de.superx.spring.batch.job.BiQuartzBatchJob; +import de.superx.spring.service.ApplicationContextHolder; import de.superx.spring.service.BeanUtil; import de.superx.spring.service.DbJobDescriptionAdapter; import de.superx.spring.service.EntityJobDescriptionSource; @@ -89,8 +81,7 @@ import jakarta.servlet.http.HttpServletResponse; /** * A servlet which can be advised to reload static resources */ -@Component -public class SuperXManager extends HttpServlet implements ApplicationContextAware { +public class SuperXManager extends HttpServlet { private static final String INSTALLED_COMPONENTS_LOOKUP_SQL = "SELECT trim(his_system) FROM db_version"; @@ -98,509 +89,496 @@ public class SuperXManager extends HttpServlet implements ApplicationContextAwar private static Logger logger = Logger.getLogger(SuperXManager.class); - // Spring ApplicationContext - private static ApplicationContext applicationContext; + public static final String QIS_CONTEXT = "QIS_CONTEXT"; - public static final String QIS_CONTEXT = "QIS_CONTEXT"; + public static final String PARAM_SUPERX_STANDALONE = "superxStandalone"; - public static final String PARAM_SUPERX_STANDALONE = "superxStandalone"; + private static final String PROP_DB_SET = "SuperX-DB-PROPERTIES-SET"; + private static final String PROP_DB_SET_NEW_SPELLING = "superxDbPropertiesSet"; - private static final String PROP_DB_SET = "SuperX-DB-PROPERTIES-SET"; - private static final String PROP_DB_SET_NEW_SPELLING = "superxDbPropertiesSet"; - - private static final long serialVersionUID = 2L; + private static final long serialVersionUID = 2L; - public static final long maxMillisWaitingTime = 60000 * 30; // 30 minutes should be sufficient + public static final long maxMillisWaitingTime = 60000 * 30; // 30 minutes should be sufficient - public static String his1_refapp = ""; - - private SuperXInitThread initThread; + public static String his1_refapp = ""; public static HttpGetAccessGuard httpGetAccessGuard; public static final String SUPER_X_HISINONE_VERSION = "SuperX-HISinOne-VERSION"; - public static boolean isZukuenftigerStandErlaubt = true; + public static boolean isZukuenftigerStandErlaubt = true; + + public static int maxRows = 20000; - public static int maxRows = 20000; + public static String defaultMaskXsl = "maske_html.xsl"; - public static String defaultMaskXsl = "maske_html.xsl"; + public static String defaultTableHtmlXsl = "tabelle_html.xsl"; - public static String defaultTableHtmlXsl = "tabelle_html.xsl"; + public static String defaultTableXlsXsl = "tabelle_xls.xsl"; - public static String defaultTableXlsXsl = "tabelle_xls.xsl"; + public static String defaultTablePdfXsl = "tabelle_fo_pdf.xsl"; - public static String defaultTablePdfXsl = "tabelle_fo_pdf.xsl"; + public static String sharedSecret = ""; - public static String sharedSecret = ""; + private static String xmenu, xmask, xtablehtml, xtablexsl, xtablepdf; - private static String xmenu, xmask, xtablehtml, xtablexsl, xtablepdf; + public static boolean isSichtenCachingWanted = true; - public static boolean isSichtenCachingWanted = true; - - public static boolean canWriteUserHomeDir = false; - - public static boolean canWriteUserKettleDir = false; + public static boolean canWriteUserHomeDir = false; - private static final String ADMIN_ONLY = de.superx.servlet.SuperXManager.htmlPageHead("Admin-Bereich") - + "

Hier ist ein Login nur für Administratoren möglich.

(Cookies müssen aktiviert sein)" - + "

Kennung:

Passwort:
" - + "

MandantenID


"; + public static boolean canWriteUserKettleDir = false; + + private static final String ADMIN_ONLY = de.superx.servlet.SuperXManager.htmlPageHead("Admin-Bereich") + + "

Hier ist ein Login nur für Administratoren möglich.

(Cookies müssen aktiviert sein)" + + "

Kennung:

Passwort:
" + + "

MandantenID


"; private static final int PLATTFORM_ID_HISINONE = 1; - private static String pageBeginning = htmlPageHead("Webanwendung Manager") - + "

SuperX Manager

" + "SuperX @version@ (build:@sxtimestamp@)" + "
"; + private static String pageBeginning = htmlPageHead("Webanwendung Manager") + + "

SuperX Manager

" + "SuperX @version@ (build:@sxtimestamp@)" + "
"; - private static String pageEnd = ""; + private static String pageEnd = ""; - public static StringBuffer activityLog = new StringBuffer(); + public static StringBuffer activityLog = new StringBuffer(); - // letzter maskensql bevor FreeMarker loslegte - private static String lastFMMaskenSql; + // letzter maskensql bevor FreeMarker loslegte + private static String lastFMMaskenSql; - private static String lastMaskenSql = "--", lastXml = "--"; - private static String lastXmlMandantid = "--"; - private static String lastMaskenSqlMandantid = ""; + private static String lastMaskenSql = "--", lastXml = "--"; + private static String lastXmlMandantid = "--"; + private static String lastMaskenSqlMandantid = ""; - // siehe SxSql_server.executeAll - public static boolean isDevelopmentMode = true; + // siehe SxSql_server.executeAll + public static boolean isDevelopmentMode = true; - private static boolean isLastSqlExtraPageWanted = true; + private static boolean isLastSqlExtraPageWanted = true; - static public boolean isXMLReturnLinkWanted = true; + static public boolean isXMLReturnLinkWanted = true; - static String refererstart = null; + static String refererstart = null; - public static String field1Cache; + public static String field1Cache; - private static CacheUpdater cashUpdater = new CacheUpdater(); + private static CacheUpdater cashUpdater = new CacheUpdater(); - public static boolean isHis1 = false; + public static boolean isHis1 = false; - private static String webinfPfad = "."; + private static String webinfPfad = "."; - private static String moduleDir = ""; // + private static String moduleDir = ""; // - public static boolean isResponseCompressionWanted = true; + public static boolean isResponseCompressionWanted = true; - private static String noSessionFieldDefaults; + private static String noSessionFieldDefaults; - private static Date start = null; + private static Date start = null; - private static boolean isStandaloneLdapActive = false; + private static boolean isStandaloneLdapActive = false; - public static String sxversion="@version@"; - - public static String builddatum="@sxtimestamp@"; + public static String sxversion="@version@"; - - public static String getMaxRowsHinweis() { - return " Die Datenbank lieferte eine große Menge an Datensätzen. Es werden generell nur die ersten " - + SuperXManager.maxRows - + " dargestellt, in Datenblatt-Abfragen ggf. sogar weniger. Schränken Sie Ihre Abfrage ggfs. weiter ein oder nutzen den Excelexport"; - } + public static String builddatum="@sxtimestamp@"; - public static boolean isSessionFieldDefaultsWanted(String mandantenID) { - if (noSessionFieldDefaults == null) - noSessionFieldDefaults = ""; - return noSessionFieldDefaults.indexOf("," + mandantenID + ",") == -1; - } + public static String getMaxRowsHinweis() { + return " Die Datenbank lieferte eine große Menge an Datensätzen. Es werden generell nur die ersten " + + SuperXManager.maxRows + + " dargestellt, in Datenblatt-Abfragen ggf. sogar weniger. Schränken Sie Ihre Abfrage ggfs. weiter ein oder nutzen den Excelexport"; + } - @Override + public static boolean isSessionFieldDefaultsWanted(String mandantenID) { + if (noSessionFieldDefaults == null) + noSessionFieldDefaults = ""; + return noSessionFieldDefaults.indexOf("," + mandantenID + ",") == -1; + } + + @Override public void init(ServletConfig config) throws ServletException { - super.init(config); - logger.debug("SuperXManager start."); - start = new Date(); - // tomcat8 verlangt einen virtual Path separator / vor WEB-INF - // vergl. - // http://stackoverflow.com/questions/25555541/getservletcontext-getrealpath-in-tomcat-8-returns-wrong-path - webinfPfad = getServletConfig().getServletContext().getRealPath("/WEB-INF"); - canWriteUserHomeDir = checkCanWriteUserHomeDir(); - canWriteUserKettleDir = checkCanWriteUserKettleDir(); - checkHis1OrStandalone(); - if (!isHis1()) - initLdapPasswordCheckingIfConfigured(); - - // are we running in a HISinOne env? - // init Prozess wird in eigenem Thread gestaretet, damit HIs1-init - // weiterläuft während superx wartet - try { - initThread = new SuperXInitThread(config); - initThread.start(); - } catch (Exception e) { - throw new ServletException(e.getMessage()); - } - } - - private void initLdapPasswordCheckingIfConfigured() { - File superxStandaloneLdapConfigFile = new File( - SuperXManager.webinfPfad + File.separator + "superx_standalone_ldap.properties"); - if (superxStandaloneLdapConfigFile.exists()) { - System.out.println(" Passwortkontrolle via LDAP wird aktiviert"); - isStandaloneLdapActive = true; - try { - LdapPasswordChecker.setup(superxStandaloneLdapConfigFile); - } catch (Exception e) { - System.out.println(" Konfiguration Passwordkontrolle via LDAP fehlgeschlagen " + e); - e.printStackTrace(); - } - - } - - } - - private void checkHis1OrStandalone() { - String param = this.getServletContext().getInitParameter(PARAM_SUPERX_STANDALONE); - if (param == null) { - logger.info("Kein Init-Parameter " + PARAM_SUPERX_STANDALONE - + " (true/false) in web.xml für SuperXManager gefunden. Setze auf HisInOne-Umgebung"); - isHis1 = true; - } else { - if (param.toLowerCase().equals("false")) { - isHis1 = true; - logger.info("Setze Umgebung HisInOne" + "aufgrund von Init-Parameter " - + PARAM_SUPERX_STANDALONE + " in web.xml für SuperXManager."); - } else { - isHis1 = false; - String msg="Setze Umgebung SuperX (Standalone)" + "aufgrund von Init-Parameter " - + PARAM_SUPERX_STANDALONE + " in web.xml für SuperXManager."; - logger.info(msg); - //bei Standalone auch Hinweis in catalina.out, da dort meist geschaut wird - System.out.println(msg); - } - } - - } - - public static String getWEB_INFPfad() { - return webinfPfad; - } - - public static void setWEB_INFPfad(String p) { - webinfPfad = p; - } - - private void initXSLFilenames() { - // tomcat8 verlangt einen virtual Path separator / vor xml - // vergl. - // http://stackoverflow.com/questions/25555541/getservletcontext-getrealpath-in-tomcat-8-returns-wrong-path - xmenu = "file:///" + getServletConfig().getServletContext().getRealPath("/xml/" + "menue_html.xsl"); - xmask = "file:///" + getServletConfig().getServletContext().getRealPath("/xml/" + SuperXManager.defaultMaskXsl); - xtablehtml = "file:///" - + getServletConfig().getServletContext().getRealPath("/xml/" + SuperXManager.defaultTableHtmlXsl); - xtablexsl = "file:///" - + getServletConfig().getServletContext().getRealPath("/xml/" + SuperXManager.defaultTableXlsXsl); - xtablepdf = "file:///" - + getServletConfig().getServletContext().getRealPath("/xml/" + SuperXManager.defaultTablePdfXsl); - } - - public static void initStandardXsl() throws TransformerConfigurationException, DBServletException { - TransletCache.getTransformer(null, xmenu); - TransletCache.getTransformer(null, xmask); - TransletCache.getTransformer(null, xtablehtml); - TransletCache.getTransformer(null, xtablexsl); - } - - @Override + super.init(config); + logger.debug("SuperXManager start."); + start = new Date(); + // tomcat8 verlangt einen virtual Path separator / vor WEB-INF + // vergl. + // http://stackoverflow.com/questions/25555541/getservletcontext-getrealpath-in-tomcat-8-returns-wrong-path + webinfPfad = getServletConfig().getServletContext().getRealPath("/WEB-INF"); + canWriteUserHomeDir = checkCanWriteUserHomeDir(); + canWriteUserKettleDir = checkCanWriteUserKettleDir(); + checkHis1OrStandalone(); + if (!isHis1()) + initLdapPasswordCheckingIfConfigured(); + // vormals im InitThread ausgeführt + doInit(config); + // "Trick" um SuperXManager-Instanz als Bean mit Autowire-Support in Spring zu registrieren + // (notwendig, weil SuperXManager bereits vom Servlet-Container instanziiert wird) + SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); + } + + private void initLdapPasswordCheckingIfConfigured() { + File superxStandaloneLdapConfigFile = new File( + SuperXManager.webinfPfad + File.separator + "superx_standalone_ldap.properties"); + if (superxStandaloneLdapConfigFile.exists()) { + System.out.println(" Passwortkontrolle via LDAP wird aktiviert"); + isStandaloneLdapActive = true; + try { + LdapPasswordChecker.setup(superxStandaloneLdapConfigFile); + } catch (Exception e) { + System.out.println(" Konfiguration Passwordkontrolle via LDAP fehlgeschlagen " + e); + e.printStackTrace(); + } + + } + + } + + private void checkHis1OrStandalone() { + String param = this.getServletContext().getInitParameter(PARAM_SUPERX_STANDALONE); + if (param == null) { + logger.info("Kein Init-Parameter " + PARAM_SUPERX_STANDALONE + + " (true/false) in web.xml für SuperXManager gefunden. Setze auf HisInOne-Umgebung"); + isHis1 = true; + } else { + if (param.toLowerCase().equals("false")) { + isHis1 = true; + logger.info("Setze Umgebung HisInOne" + "aufgrund von Init-Parameter " + + PARAM_SUPERX_STANDALONE + " in web.xml für SuperXManager."); + } else { + isHis1 = false; + String msg="Setze Umgebung SuperX (Standalone)" + "aufgrund von Init-Parameter " + + PARAM_SUPERX_STANDALONE + " in web.xml für SuperXManager."; + logger.info(msg); + //bei Standalone auch Hinweis in catalina.out, da dort meist geschaut wird + System.out.println(msg); + } + } + + } + + public static String getWEB_INFPfad() { + return webinfPfad; + } + + public static void setWEB_INFPfad(String p) { + webinfPfad = p; + } + + private void initXSLFilenames() { + // tomcat8 verlangt einen virtual Path separator / vor xml + // vergl. + // http://stackoverflow.com/questions/25555541/getservletcontext-getrealpath-in-tomcat-8-returns-wrong-path + xmenu = "file:///" + getServletConfig().getServletContext().getRealPath("/xml/" + "menue_html.xsl"); + xmask = "file:///" + getServletConfig().getServletContext().getRealPath("/xml/" + SuperXManager.defaultMaskXsl); + xtablehtml = "file:///" + + getServletConfig().getServletContext().getRealPath("/xml/" + SuperXManager.defaultTableHtmlXsl); + xtablexsl = "file:///" + + getServletConfig().getServletContext().getRealPath("/xml/" + SuperXManager.defaultTableXlsXsl); + xtablepdf = "file:///" + + getServletConfig().getServletContext().getRealPath("/xml/" + SuperXManager.defaultTablePdfXsl); + } + + public static void initStandardXsl() throws TransformerConfigurationException, DBServletException { + TransletCache.getTransformer(null, xmenu); + TransletCache.getTransformer(null, xmask); + TransletCache.getTransformer(null, xtablehtml); + TransletCache.getTransformer(null, xtablexsl); + } + + @Override public void destroy() { - super.destroy(); - try { - if (initThread != null) - initThread.destroy(); - SxPools.closeAll(); - cashUpdater.setActive(false); - cashUpdater.interrupt(); - } catch (Exception e) { - e.printStackTrace(); - } - try { - for (Enumeration en = DriverManager.getDrivers(); en.hasMoreElements();) { - Driver d = (Driver) en.nextElement(); - try { - DriverManager.deregisterDriver(d); - logger.info(new java.util.Date() + "{} deregister {}" + d); - } catch (Exception e) { - logger.error("Couldn't deregister driver", e); - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (KettleEnvironment.isInitialized()) { - KettleEnvironment.shutdown(); - } - } - } - - @Override - public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - AbstractSuperXServlet.setEncoding(request); - response.setContentType("text/html; charset=" + SqlStringUtils.getEncoding()); - if (ServletHelper.getParameter(request, RequestParameter.kennung) != null - && ServletHelper.getParameter(request, RequestParameter.passwort) != null) { - UserInitializer ui = new UserInitializer(request); - try { - ui.perform(null,null); - } catch (Exception e) { - throw new ServletException( - "Authentifizierung für " + ServletHelper.getParameter(request, RequestParameter.kennung) + " fehlgeschlagen"); - } - } - if (request.getParameter(RequestParameter.xupdater.toString()) != null) { - XUpdaterServlet xu = new XUpdaterServlet(); - xu.doPost(request, response); - } else { - SxUser user = (SxUser) request.getSession().getAttribute(SessionAttribute.user.toString()); - if (user == null || !user.isAdmin()) { - PrintWriter output = response.getWriter(); - output.write(ADMIN_ONLY); - output.flush(); - } else { - SxManager m = new SxManager(request, response); - m.run(false); - } - } - } - - @Override - public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.setContentType("text/html; charset=" + SqlStringUtils.getEncoding()); - SxUser user = (SxUser) request.getSession().getAttribute(SessionAttribute.user.toString()); - if (user == null || !user.isAdmin()) { - PrintWriter output = response.getWriter(); - output.write(ADMIN_ONLY); - output.flush(); - output.close(); - } else { - if (request.getParameter(RequestParameter.xupdater.toString()) != null) { - XUpdaterServlet xu = new XUpdaterServlet(); - xu.doGet(request, response); - } else { - PrintWriter output = response.getWriter(); - String showLastSqlXml = ServletHelper.getParameter(request, RequestParameter.showlastsqlxml); - boolean isLastSqlXmlWantend = showLastSqlXml != null && showLastSqlXml.equals("true"); - if (isLastSqlXmlWantend) { - output.write(pageBeginning + getLastSqlXml() + pageEnd); - } else { - CsrfToken token = SuperXServletHelper.getCsRfToken(request); - output.write(getStandardPage(request, "", token.getParameterName(), token.getToken()) + pageEnd); - } - output.flush(); - output.close(); - } - } - } - - private String getLastSqlXml() { - StringBuffer result = new StringBuffer("
"); - result.append("
"); - result.append( - ""); - result.append( - "" - + "

letzter Masken sql (vom XML-Frontend)
vor FreeMarker-Transformation

letzter Masken sql (vom XML-Frontend)

letztes XML

"); - return result.toString(); - } - - public String getStandardPage(HttpServletRequest request, String comment, String tokenParamName, String token) { - String m = ServletUtils.getMandantenID(request); - StringBuffer result = new StringBuffer(pageBeginning - + (m.equals(DEFAULT_MANDANTEN_ID) ? "" : "
(Mandant: " + m + ")
\n") + "\n
" - + "
" + comment - + "\n

 
" - + "Es werden Erläuterungen und Übersetzungen (captions) gecacht.
Fürs XML-Frontend auch: User,Userrechte und Sichten,
und auch Abfragen wenn in der db.properties eingetragen


" - + "Entwicklungsmodus ist " + (isDevelopmentMode ? "AN" : "AUS") - + " an\naus\n" - + "\n
" - + "(Im Entwicklungsmodus werden alle SQL-Befehle von Abfragen einzeln an die Datenbank geschickt.
Das dauert länger, ermöglicht aber bessere Fehlermeldungen.)

" - + "Achtung: bei Informix sollten Sie den Entwicklungsmodus nicht ausschalten, der JDBC-Treiber arbeitet dann instabil.

" - + "" - + "\n


\n"); - result.append("
"); - result.append("
"); - result.append(htmlSubsystemsInitStatus(m)); - - if (isLastSqlExtraPageWanted) { - result.append(getLastSqlXml()); - } else { - result.append( - "letzter SQL / XML


"); - } - result.append("

LOG aktualisieren


" + // "); + result.append( + "

letzter Masken sql (vom XML-Frontend)

" + + "

letztes XML

"); + return result.toString(); + } + + public String getStandardPage(HttpServletRequest request, String comment, String tokenParamName, String token) { + String m = ServletUtils.getMandantenID(request); + StringBuffer result = new StringBuffer(pageBeginning + + (m.equals(DEFAULT_MANDANTEN_ID) ? "" : "
(Mandant: " + m + ")
\n") + "\n
" + + "
" + comment + + "\n

 
" + + "Es werden Erläuterungen und Übersetzungen (captions) gecacht.
Fürs XML-Frontend auch: User,Userrechte und Sichten,
und auch Abfragen wenn in der db.properties eingetragen


" + + "Entwicklungsmodus ist " + (isDevelopmentMode ? "AN" : "AUS") + + " an\naus\n" + + "\n
" + + "(Im Entwicklungsmodus werden alle SQL-Befehle von Abfragen einzeln an die Datenbank geschickt.
Das dauert länger, ermöglicht aber bessere Fehlermeldungen.)

" + + "Achtung: bei Informix sollten Sie den Entwicklungsmodus nicht ausschalten, der JDBC-Treiber arbeitet dann instabil.

" + + "" + + "\n


\n"); + result.append("
"); + result.append("
"); + result.append(htmlSubsystemsInitStatus(m)); + + if (isLastSqlExtraPageWanted) { + result.append(getLastSqlXml()); + } else { + result.append( + "
letzter SQL / XML


"); + } + result.append("

LOG aktualisieren


" + // + + + + + + + + + <% if (request.getAttribute("result") != null) { %> + + Results: + <% out.println(request.getAttribute("result")); %> + + + <% } %> + + + + +
back to index

+ + + diff --git a/superx/applet.htm b/superx/applet.htm new file mode 100644 index 0000000..a3496a6 --- /dev/null +++ b/superx/applet.htm @@ -0,0 +1,38 @@ + +SuperX - Startseite + + + + +


+ + + + + + + +

+
SuperX-Start
+

(Wenn + Sie Netscape 6 benutzen, klicken Sie hier.)

+
+

+ +


+

    Link zur SuperX-Homepage +| Stand dieser Seite: 17.2.2002 +
+Kontakt +

+ +

  +


+   Copyright © 1999 Apache Software Foundation +
+ All Rights Reserved
+  

+ + diff --git a/superx/dbformslib/jscal/calendar.gif b/superx/dbformslib/jscal/calendar.gif index 2a9fb25..71c9593 100644 Binary files a/superx/dbformslib/jscal/calendar.gif and b/superx/dbformslib/jscal/calendar.gif differ diff --git a/superx/dbformslib/jscal/calendar.js b/superx/dbformslib/jscal/calendar.js index 838421b..5d0fc92 100644 --- a/superx/dbformslib/jscal/calendar.js +++ b/superx/dbformslib/jscal/calendar.js @@ -1,1832 +1,1832 @@ -// - -// JavaScript Calendar Component - -// Author: Robert W. Husted (robert.husted@iname.com) - -// Date: 8/22/1999 - -// Modified Date: 11/30/1999 - -// Modified By: Robert W. Husted - -// Notes: Added frameset support (changed reference for "newWin" to "top.newWin") - -// Also changed Spanish "March" from "Marcha" to "Marzo" - -// Fixed JavaScript Date Anomaly affecting days > 28 - -// - -// - -// - -// Usage: Add the following lines of code to your page to enable the Calendar - -// component. - -// - -// - -// // THIS LINE LOADS THE JS LIBRARY FOR THE CALENDAR COMPONENT - -// - -// - -// - -// - -// - -// // THIS LINE IS USED IN CONJUNCTION WITH A FORM FIELD (myDateField) IN A FORM (myForm). - -// // Replace "myForm" and "myDateField" WITH THE NAME OF YOUR FORM AND INPUT FIELD RESPECTIVELY - -// // WINDOW OPTIONS SET THE WIDTH, HEIGHT, AND X/Y POSITION OF THE CALENDAR WINDOW - -// // WITH TITLEBAR ON, ALL OTHER OPTIONS (TOOLBARS, ETC) ARE DISABLED BY DEFAULT - -// - -// - -// Popup Calendar - -// - -// - -// - -// Required Files: - -// - -// calendar.js - contains all JavaScript functions to make the calendar work - -// - -// calendar.html - frameset document (not required if you call the showCalendar() - -// function. However, calling showCalendar() directly causes - -// the Java Virtual Machine (JVM) to start which slows down the - -// loading of the calendar.) - -// - -// - -// Files Generally Included: - -// - -// calendar.gif - image that looks like a little calendar - -// - -// yourPage.html - page that contains a form and a date field which implements - -// the calendar component - -// - - - - - - - -// BEGIN USER-EDITABLE SECTION ----------------------------------------------------- - - - - - - - -// SPECIFY DATE FORMAT RETURNED BY THIS CALENDAR - -// (THIS IS ALSO THE DATE FORMAT RECOGNIZED BY THIS CALENDAR) - - - -// DATE FORMAT OPTIONS: - -// - -// dd = 1 or 2-digit Day - -// DD = 2-digit Day - -// mm = 1 or 2-digit Month - -// MM = 2-digit Month - -// yy = 2-digit Year - -// YY = 4-digit Year - -// yyyy = 4-digit Year - -// month = Month name in lowercase letters - -// Month = Month name in initial caps - -// MONTH = Month name in captital letters - -// mon = 3-letter month abbreviation in lowercase letters - -// Mon = 3-letter month abbreviation in initial caps - -// MON = 3-letter month abbreviation in uppercase letters - -// weekday = name of week in lowercase letters - -// Weekday = name of week in initial caps - -// WEEKDAY = name of week in uppercase letters - -// wkdy = 3-letter weekday abbreviation in lowercase letters - -// Wkdy = 3-letter weekday abbreviation in initial caps - -// WKDY = 3-letter weekday abbreviation in uppercase letters - -// - -// Examples: - -// - -// calDateFormat = "mm/dd/yy"; - -// calDateFormat = "Weekday, Month dd, yyyy"; - -// calDateFormat = "wkdy, mon dd, yyyy"; - -// calDateFormat = "DD.MM.YY"; // FORMAT UNSUPPORTED BY JAVASCRIPT -- REQUIRES CUSTOM PARSING - -// - - - -calDateFormat = "mm/dd/yyyy"; - - - - - -// CALENDAR COLORS - -topBackground = "white"; // BG COLOR OF THE TOP FRAME - -bottomBackground = "white"; // BG COLOR OF THE BOTTOM FRAME - -tableBGColor = "black"; // BG COLOR OF THE BOTTOM FRAME'S TABLE - -cellColor = "lightgrey"; // TABLE CELL BG COLOR OF THE DATE CELLS IN THE BOTTOM FRAME - -headingCellColor = "white"; // TABLE CELL BG COLOR OF THE WEEKDAY ABBREVIATIONS - -headingTextColor = "black"; // TEXT COLOR OF THE WEEKDAY ABBREVIATIONS - -dateColor = "blue"; // TEXT COLOR OF THE LISTED DATES (1-28+) - -focusColor = "#ff0000"; // TEXT COLOR OF THE SELECTED DATE (OR CURRENT DATE) - -hoverColor = "darkred"; // TEXT COLOR OF A LINK WHEN YOU HOVER OVER IT - -fontStyle = "12pt arial, helvetica"; // TEXT STYLE FOR DATES - -headingFontStyle = "bold 12pt arial, helvetica"; // TEXT STYLE FOR WEEKDAY ABBREVIATIONS - - - -// FORMATTING PREFERENCES - -bottomBorder = false; // TRUE/FALSE (WHETHER TO DISPLAY BOTTOM CALENDAR BORDER) - -tableBorder = 0; // SIZE OF CALENDAR TABLE BORDER (BOTTOM FRAME) 0=none - - - - - - - -// END USER-EDITABLE SECTION ------------------------------------------------------- - - - - - - - -// DETERMINE BROWSER BRAND - -var isNav = false; - -var isIE = false; - - - -// ASSUME IT'S EITHER NETSCAPE OR MSIE - -if (navigator.appName == "Netscape") { - - isNav = true; - -} - -else { - - isIE = true; - -} - - - -// GET CURRENTLY SELECTED LANGUAGE - -selectedLanguage = navigator.language; - - - -// PRE-BUILD PORTIONS OF THE CALENDAR WHEN THIS JS LIBRARY LOADS INTO THE BROWSER - -buildCalParts(); - - - - - - - -// CALENDAR FUNCTIONS BEGIN HERE --------------------------------------------------- - - - - - - - -// SET THE INITIAL VALUE OF THE GLOBAL DATE FIELD - -function setDateField(dateField) { - - - - // ASSIGN THE INCOMING FIELD OBJECT TO A GLOBAL VARIABLE - - calDateField = dateField; - - - - // GET THE VALUE OF THE INCOMING FIELD - - inDate = dateField.value; - - - - // SET calDate TO THE DATE IN THE INCOMING FIELD OR DEFAULT TO TODAY'S DATE - - setInitialDate(); - - - - // THE CALENDAR FRAMESET DOCUMENTS ARE CREATED BY JAVASCRIPT FUNCTIONS - - calDocTop = buildTopCalFrame(); - - calDocBottom = buildBottomCalFrame(); - -} - - - - - -// SET THE INITIAL CALENDAR DATE TO TODAY OR TO THE EXISTING VALUE IN dateField - -function setInitialDate() { - - - - // CREATE A NEW DATE OBJECT (WILL GENERALLY PARSE CORRECT DATE EXCEPT WHEN "." IS USED AS A DELIMITER) - - // (THIS ROUTINE DOES *NOT* CATCH ALL DATE FORMATS, IF YOU NEED TO PARSE A CUSTOM DATE FORMAT, DO IT HERE) - - - - // --------------------------------------------------------- - - // custom parsing of date format added by dikr, - - // added to support easy integration into dbforms tag library 2002-5-28: - - // ignore case when parsing - - var inputCalFormat = calDateFormat.toLowerCase(); - - - - // if pattern is made just of letters 'y','m','d' and - - // some delimiters '-','_','.' or '/' , e.g. 'dd.mm.yyyy', - - // 'yyyy-mm-dd', 'mm/dd/yyyy', we will try to automatically - - // parse it: - - if (inputCalFormat.match(/[ymd]+[\.\-_\/][ymd]+[\.\-_\/][ymd]+/)) { - - // transform pattern to use standard delimiter '-', - - // e.g. 'dd.mm.yyyy' => 'dd-mm-yyyy' - - // neccessary, because split function does not seem to - - // support regular expr. (or am I wrong?) - - inputCalFormat = inputCalFormat.replace(/[\.\-_\/]+/g,'-'); - - // split input format into components - - var formatComps = inputCalFormat.split('-'); - - - - // now the same for input date: transform to use - - // standard delimiter.... - - inDate2 = inDate.replace(/[\.\-_\/]+/g,'-'); - - // and split it into components: - - datComps = inDate2.split('-'); - - // now examine all (hopefully) three parts: - - for (var ii = 0 ; ii<3;ii++) { - - // ii-th part of pattern, hopefully 'dd', 'mm', 'yy'.... - - var pattpart = formatComps[ii]; - - - - if (pattpart.match(/[y]+/)) inyear = datComps[ii]; // year found - - if (pattpart.match(/[m]+/)) inmonth = datComps[ii] - 1; // month found - - if (pattpart.match(/[d]+/)) inday= datComps[ii]; // day found - - - - } - - // create new date object - - calDate = new Date(inyear,inmonth,inday); - - - - } else { // pattern is somehow different, do not try to - - // parse it and use build-in JavaScript date constructor: - - calDate = new Date(inDate); - - // use English language in case format uses textual months - - // that would not be understood by dbforms - - selectedLanguage = 'en'; - - } - - // end of modifications - - // ---------------------------------------------------------- - - - - - - // IF THE INCOMING DATE IS INVALID, USE THE CURRENT DATE - - if (isNaN(calDate)) { - - - - // ADD CUSTOM DATE PARSING HERE - - // IF IT FAILS, SIMPLY CREATE A NEW DATE OBJECT WHICH DEFAULTS TO THE CURRENT DATE - - calDate = new Date(); - - } - - - - // KEEP TRACK OF THE CURRENT DAY VALUE - - calDay = calDate.getDate(); - - - - // SET DAY VALUE TO 1... TO AVOID JAVASCRIPT DATE CALCULATION ANOMALIES - - // (IF THE MONTH CHANGES TO FEB AND THE DAY IS 30, THE MONTH WOULD CHANGE TO MARCH - - // AND THE DAY WOULD CHANGE TO 2. SETTING THE DAY TO 1 WILL PREVENT THAT) - - calDate.setDate(1); - -} - - - - - -// POPUP A WINDOW WITH THE CALENDAR IN IT - -function showCalendar(dateField) { - - - - // SET INITIAL VALUE OF THE DATE FIELD AND CREATE TOP AND BOTTOM FRAMES - - setDateField(dateField); - - - - // USE THE JAVASCRIPT-GENERATED DOCUMENTS (calDocTop, calDocBottom) IN THE FRAMESET - - calDocFrameset = - - "JavaScript Calendar\n" + - - "\n" + - - " \n" + - - " \n" + - - "\n"; - - - - // DISPLAY THE CALENDAR IN A NEW POPUP WINDOW - - top.newWin = window.open("javascript:parent.opener.calDocFrameset", "calWin", winPrefs); - - top.newWin.focus(); - -} - - - - - -// CREATE THE TOP CALENDAR FRAME - -function buildTopCalFrame() { - - - - // CREATE THE TOP FRAME OF THE CALENDAR - - var calDoc = - - "" + - - "" + - - "" + - - "" + - - "
" + - - "
" + - - "" + - - "" + - - "" + - - "" + - - "" + - - "" + - - "
" + - - "
" + - - getMonthSelect() + - - "" + - - "
" + - - "
" + - - "" + - - "
" + - - "
" + - - "
" + - - "" + - - ""; - - - - return calDoc; - -} - - - - - -// CREATE THE BOTTOM CALENDAR FRAME - -// (THE MONTHLY CALENDAR) - -function buildBottomCalFrame() { - - - - // START CALENDAR DOCUMENT - - var calDoc = calendarBegin; - - - - // GET MONTH, AND YEAR FROM GLOBAL CALENDAR DATE - - month = calDate.getMonth(); - - year = calDate.getFullYear(); - - - - - - // GET GLOBALLY-TRACKED DAY VALUE (PREVENTS JAVASCRIPT DATE ANOMALIES) - - day = calDay; - - - - var i = 0; - - - - // DETERMINE THE NUMBER OF DAYS IN THE CURRENT MONTH - - var days = getDaysInMonth(); - - - - // IF GLOBAL DAY VALUE IS > THAN DAYS IN MONTH, HIGHLIGHT LAST DAY IN MONTH - - if (day > days) { - - day = days; - - } - - - - // DETERMINE WHAT DAY OF THE WEEK THE CALENDAR STARTS ON - - var firstOfMonth = new Date (year, month, 1); - - - - // GET THE DAY OF THE WEEK THE FIRST DAY OF THE MONTH FALLS ON - - var startingPos = firstOfMonth.getDay(); - - days += startingPos; - - - - // KEEP TRACK OF THE COLUMNS, START A NEW ROW AFTER EVERY 7 COLUMNS - - var columnCount = 0; - - - - // MAKE BEGINNING NON-DATE CELLS BLANK - - for (i = 0; i < startingPos; i++) { - - - - calDoc += blankCell; - - columnCount++; - - } - - - - // SET VALUES FOR DAYS OF THE MONTH - - var currentDay = 0; - - var dayType = "weekday"; - - - - // DATE CELLS CONTAIN A NUMBER - - for (i = startingPos; i < days; i++) { - - - - var paddingChar = " "; - - - - // ADJUST SPACING SO THAT ALL LINKS HAVE RELATIVELY EQUAL WIDTHS - - if (i-startingPos+1 < 10) { - - padding = "  "; - - } - - else { - - padding = " "; - - } - - - - // GET THE DAY CURRENTLY BEING WRITTEN - - currentDay = i-startingPos+1; - - - - // SET THE TYPE OF DAY, THE focusDay GENERALLY APPEARS AS A DIFFERENT COLOR - - if (currentDay == day) { - - dayType = "focusDay"; - - } - - else { - - dayType = "weekDay"; - - } - - - - // ADD THE DAY TO THE CALENDAR STRING - - calDoc += "" + - - "" + padding + currentDay + paddingChar + ""; - - - - columnCount++; - - - - // START A NEW ROW WHEN NECESSARY - - if (columnCount % 7 == 0) { - - calDoc += ""; - - } - - } - - - - // MAKE REMAINING NON-DATE CELLS BLANK - - for (i=days; i<42; i++) { - - - - calDoc += blankCell; - - columnCount++; - - - - // START A NEW ROW WHEN NECESSARY - - if (columnCount % 7 == 0) { - - calDoc += ""; - - if (i<41) { - - calDoc += ""; - - } - - } - - } - - - - // FINISH THE NEW CALENDAR PAGE - - calDoc += calendarEnd; - - - - // RETURN THE COMPLETED CALENDAR PAGE - - return calDoc; - -} - - - - - -// WRITE THE MONTHLY CALENDAR TO THE BOTTOM CALENDAR FRAME - -function writeCalendar() { - - - - // CREATE THE NEW CALENDAR FOR THE SELECTED MONTH & YEAR - - calDocBottom = buildBottomCalFrame(); - - - - // WRITE THE NEW CALENDAR TO THE BOTTOM FRAME - - top.newWin.frames['bottomCalFrame'].document.open(); - - top.newWin.frames['bottomCalFrame'].document.write(calDocBottom); - - top.newWin.frames['bottomCalFrame'].document.close(); - -} - - - - - -// SET THE CALENDAR TO TODAY'S DATE AND DISPLAY THE NEW CALENDAR - -function setToday() { - - - - // SET GLOBAL DATE TO TODAY'S DATE - - calDate = new Date(); - - - - // SET DAY MONTH AND YEAR TO TODAY'S DATE - - var month = calDate.getMonth(); - - var year = calDate.getFullYear(); - - - - // SET MONTH IN DROP-DOWN LIST - - top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex = month; - - - - // SET YEAR VALUE - - top.newWin.frames['topCalFrame'].document.calControl.year.value = year; - - - - // DISPLAY THE NEW CALENDAR - - writeCalendar(); - -} - - - - - -// SET THE GLOBAL DATE TO THE NEWLY ENTERED YEAR AND REDRAW THE CALENDAR - -function setYear() { - - - - // GET THE NEW YEAR VALUE - - var year = top.newWin.frames['topCalFrame'].document.calControl.year.value; - - - - // IF IT'S A FOUR-DIGIT YEAR THEN CHANGE THE CALENDAR - - if (isFourDigitYear(year)) { - - calDate.setFullYear(year); - - writeCalendar(); - - } - - else { - - // HIGHLIGHT THE YEAR IF THE YEAR IS NOT FOUR DIGITS IN LENGTH - - top.newWin.frames['topCalFrame'].document.calControl.year.focus(); - - top.newWin.frames['topCalFrame'].document.calControl.year.select(); - - } - -} - - - - - -// SET THE GLOBAL DATE TO THE SELECTED MONTH AND REDRAW THE CALENDAR - -function setCurrentMonth() { - - - - // GET THE NEWLY SELECTED MONTH AND CHANGE THE CALENDAR ACCORDINGLY - - var month = top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex; - - - - calDate.setMonth(month); - - writeCalendar(); - -} - - - - - -// SET THE GLOBAL DATE TO THE PREVIOUS YEAR AND REDRAW THE CALENDAR - -function setPreviousYear() { - - - - var year = top.newWin.frames['topCalFrame'].document.calControl.year.value; - - - - if (isFourDigitYear(year) && year > 1000) { - - year--; - - calDate.setFullYear(year); - - top.newWin.frames['topCalFrame'].document.calControl.year.value = year; - - writeCalendar(); - - } - -} - - - - - -// SET THE GLOBAL DATE TO THE PREVIOUS MONTH AND REDRAW THE CALENDAR - -function setPreviousMonth() { - - - - var year = top.newWin.frames['topCalFrame'].document.calControl.year.value; - - if (isFourDigitYear(year)) { - - var month = top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex; - - - - // IF MONTH IS JANUARY, SET MONTH TO DECEMBER AND DECREMENT THE YEAR - - if (month == 0) { - - month = 11; - - if (year > 1000) { - - year--; - - calDate.setFullYear(year); - - top.newWin.frames['topCalFrame'].document.calControl.year.value = year; - - } - - } - - else { - - month--; - - } - - calDate.setMonth(month); - - top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex = month; - - writeCalendar(); - - } - -} - - - - - -// SET THE GLOBAL DATE TO THE NEXT MONTH AND REDRAW THE CALENDAR - -function setNextMonth() { - - - - var year = top.newWin.frames['topCalFrame'].document.calControl.year.value; - - - - if (isFourDigitYear(year)) { - - var month = top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex; - - - - // IF MONTH IS DECEMBER, SET MONTH TO JANUARY AND INCREMENT THE YEAR - - if (month == 11) { - - month = 0; - - year++; - - calDate.setFullYear(year); - - top.newWin.frames['topCalFrame'].document.calControl.year.value = year; - - } - - else { - - month++; - - } - - calDate.setMonth(month); - - top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex = month; - - writeCalendar(); - - } - -} - - - - - -// SET THE GLOBAL DATE TO THE NEXT YEAR AND REDRAW THE CALENDAR - -function setNextYear() { - - - - var year = top.newWin.frames['topCalFrame'].document.calControl.year.value; - - if (isFourDigitYear(year)) { - - year++; - - calDate.setFullYear(year); - - top.newWin.frames['topCalFrame'].document.calControl.year.value = year; - - writeCalendar(); - - } - -} - - - - - -// GET NUMBER OF DAYS IN MONTH - -function getDaysInMonth() { - - - - var days; - - var month = calDate.getMonth()+1; - - var year = calDate.getFullYear(); - - - - // RETURN 31 DAYS - - if (month==1 || month==3 || month==5 || month==7 || month==8 || - - month==10 || month==12) { - - days=31; - - } - - // RETURN 30 DAYS - - else if (month==4 || month==6 || month==9 || month==11) { - - days=30; - - } - - // RETURN 29 DAYS - - else if (month==2) { - - if (isLeapYear(year)) { - - days=29; - - } - - // RETURN 28 DAYS - - else { - - days=28; - - } - - } - - return (days); - -} - - - - - -// CHECK TO SEE IF YEAR IS A LEAP YEAR - -function isLeapYear (Year) { - - - - if (((Year % 4)==0) && ((Year % 100)!=0) || ((Year % 400)==0)) { - - return (true); - - } - - else { - - return (false); - - } - -} - - - - - -// ENSURE THAT THE YEAR IS FOUR DIGITS IN LENGTH - -function isFourDigitYear(year) { - - - - if (year.length != 4) { - - top.newWin.frames['topCalFrame'].document.calControl.year.value = calDate.getFullYear(); - - top.newWin.frames['topCalFrame'].document.calControl.year.select(); - - top.newWin.frames['topCalFrame'].document.calControl.year.focus(); - - } - - else { - - return true; - - } - -} - - - - - -// BUILD THE MONTH SELECT LIST - -function getMonthSelect() { - - - - // BROWSER LANGUAGE CHECK DONE PREVIOUSLY (navigator.language()) - - // FIRST TWO CHARACTERS OF LANGUAGE STRING SPECIFIES THE LANGUAGE - - // (THE LAST THREE OPTIONAL CHARACTERS SPECIFY THE LANGUAGE SUBTYPE) - - // SET THE NAMES OF THE MONTH TO THE PROPER LANGUAGE (DEFAULT TO ENGLISH) - - - - // IF FRENCH - - if (selectedLanguage == "fr") { - - monthArray = new Array('Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', - - 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'); - - } - - // IF GERMAN - - else if (selectedLanguage == "de") { - - monthArray = new Array('Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', - - 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'); - - } - - // IF SPANISH - - else if (selectedLanguage == "es") { - - monthArray = new Array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', - - 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'); - - } - - // DEFAULT TO ENGLISH - - else { - - monthArray = new Array('January', 'February', 'March', 'April', 'May', 'June', - - 'July', 'August', 'September', 'October', 'November', 'December'); - - } - - - - // DETERMINE MONTH TO SET AS DEFAULT - - var activeMonth = calDate.getMonth(); - - - - // START HTML SELECT LIST ELEMENT - - monthSelect = ""; - - - - // RETURN A STRING VALUE WHICH CONTAINS A SELECT LIST OF ALL 12 MONTHS - - return monthSelect; - -} - - - - - -// SET DAYS OF THE WEEK DEPENDING ON LANGUAGE - -function createWeekdayList() { - - - - // IF FRENCH - - if (selectedLanguage == "fr") { - - weekdayList = new Array('Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'); - - weekdayArray = new Array('Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'); - - } - - // IF GERMAN - - else if (selectedLanguage == "de") { - - weekdayList = new Array('Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'); - - weekdayArray = new Array('So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'); - - } - - // IF SPANISH - - else if (selectedLanguage == "es") { - - weekdayList = new Array('Domingo', 'Lunes', 'Martes', 'Mi?rcoles', 'Jueves', 'Viernes', 'S?bado') - - weekdayArray = new Array('Do', 'Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa'); - - } - - else { - - weekdayList = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); - - weekdayArray = new Array('Su','Mo','Tu','We','Th','Fr','Sa'); - - } - - - - // START HTML TO HOLD WEEKDAY NAMES IN TABLE FORMAT - - var weekdays = ""; - - - - // LOOP THROUGH WEEKDAY ARRAY - - for (i in weekdayArray) { - - - - weekdays += "" + weekdayArray[i] + ""; - - } - - weekdays += ""; - - - - // RETURN TABLE ROW OF WEEKDAY ABBREVIATIONS TO DISPLAY ABOVE THE CALENDAR - - return weekdays; - -} - - - - - -// PRE-BUILD PORTIONS OF THE CALENDAR (FOR PERFORMANCE REASONS) - -function buildCalParts() { - - - - // GENERATE WEEKDAY HEADERS FOR THE CALENDAR - - weekdays = createWeekdayList(); - - - - // BUILD THE BLANK CELL ROWS - - blankCell = "   "; - - - - // BUILD THE TOP PORTION OF THE CALENDAR PAGE USING CSS TO CONTROL SOME DISPLAY ELEMENTS - - calendarBegin = - - "" + - - "" + - - // STYLESHEET DEFINES APPEARANCE OF CALENDAR - - "" + - - "" + - - ""; - - - - // NAVIGATOR NEEDS A TABLE CONTAINER TO DISPLAY THE TABLE OUTLINES PROPERLY - - if (isNav) { - - calendarBegin += - - "
"; - - } - - - - // BUILD WEEKDAY HEADINGS - - calendarBegin += - - "" + - - weekdays + - - ""; - - - - - - // BUILD THE BOTTOM PORTION OF THE CALENDAR PAGE - - calendarEnd = ""; - - - - // WHETHER OR NOT TO DISPLAY A THICK LINE BELOW THE CALENDAR - - if (bottomBorder) { - - calendarEnd += ""; - - } - - - - // NAVIGATOR NEEDS A TABLE CONTAINER TO DISPLAY THE BORDERS PROPERLY - - if (isNav) { - - calendarEnd += "
"; - - } - - - - // END THE TABLE AND HTML DOCUMENT - - calendarEnd += - - "
" + - - "
" + - - "" + - - ""; - -} - - - - - -// REPLACE ALL INSTANCES OF find WITH replace - -// inString: the string you want to convert - -// find: the value to search for - -// replace: the value to substitute - -// - -// usage: jsReplace(inString, find, replace); - -// example: jsReplace("To be or not to be", "be", "ski"); - -// result: "To ski or not to ski" - -// - -function jsReplace(inString, find, replace) { - - - - var outString = ""; - - - - if (!inString) { - - return ""; - - } - - - - // REPLACE ALL INSTANCES OF find WITH replace - - if (inString.indexOf(find) != -1) { - - // SEPARATE THE STRING INTO AN ARRAY OF STRINGS USING THE VALUE IN find - - t = inString.split(find); - - - - // JOIN ALL ELEMENTS OF THE ARRAY, SEPARATED BY THE VALUE IN replace - - return (t.join(replace)); - - } - - else { - - return inString; - - } - -} - - - - - -// JAVASCRIPT FUNCTION -- DOES NOTHING (USED FOR THE HREF IN THE CALENDAR CALL) - -function doNothing() { - -} - - - - - -// ENSURE THAT VALUE IS TWO DIGITS IN LENGTH - -function makeTwoDigit(inValue) { - - - - var numVal = parseInt(inValue, 10); - - - - // VALUE IS LESS THAN TWO DIGITS IN LENGTH - - if (numVal < 10) { - - - - // ADD A LEADING ZERO TO THE VALUE AND RETURN IT - - return("0" + numVal); - - } - - else { - - return numVal; - - } - -} - - - - - -// SET FIELD VALUE TO THE DATE SELECTED AND CLOSE THE CALENDAR WINDOW - -function returnDate(inDay) - -{ - - - - // inDay = THE DAY THE USER CLICKED ON - - calDate.setDate(inDay); - - - - // SET THE DATE RETURNED TO THE USER - - var day = calDate.getDate(); - - var month = calDate.getMonth()+1; - - var year = calDate.getFullYear(); - - var monthString = monthArray[calDate.getMonth()]; - - var monthAbbrev = monthString.substring(0,3); - - var weekday = weekdayList[calDate.getDay()]; - - var weekdayAbbrev = weekday.substring(0,3); - - - - outDate = calDateFormat; - - - - // RETURN TWO DIGIT DAY - - if (calDateFormat.indexOf("DD") != -1) { - - day = makeTwoDigit(day); - - outDate = jsReplace(outDate, "DD", day); - - } - - // RETURN ONE OR TWO DIGIT DAY - - else if (calDateFormat.indexOf("dd") != -1) { - - outDate = jsReplace(outDate, "dd", day); - - } - - - - // RETURN TWO DIGIT MONTH - - if (calDateFormat.indexOf("MM") != -1) { - - month = makeTwoDigit(month); - - outDate = jsReplace(outDate, "MM", month); - - } - - // RETURN ONE OR TWO DIGIT MONTH - - else if (calDateFormat.indexOf("mm") != -1) { - - outDate = jsReplace(outDate, "mm", month); - - } - - - - // RETURN FOUR-DIGIT YEAR - - if (calDateFormat.indexOf("yyyy") != -1) { - - outDate = jsReplace(outDate, "yyyy", year); - - } - - // RETURN TWO-DIGIT YEAR - - else if (calDateFormat.indexOf("yy") != -1) { - - var yearString = "" + year; - - var yearString = yearString.substring(2,4); - - outDate = jsReplace(outDate, "yy", yearString); - - } - - // RETURN FOUR-DIGIT YEAR - - else if (calDateFormat.indexOf("YY") != -1) { - - outDate = jsReplace(outDate, "YY", year); - - } - - - - // RETURN DAY OF MONTH (Initial Caps) - - if (calDateFormat.indexOf("Month") != -1) { - - outDate = jsReplace(outDate, "Month", monthString); - - } - - // RETURN DAY OF MONTH (lowercase letters) - - else if (calDateFormat.indexOf("month") != -1) { - - outDate = jsReplace(outDate, "month", monthString.toLowerCase()); - - } - - // RETURN DAY OF MONTH (UPPERCASE LETTERS) - - else if (calDateFormat.indexOf("MONTH") != -1) { - - outDate = jsReplace(outDate, "MONTH", monthString.toUpperCase()); - - } - - - - // RETURN DAY OF MONTH 3-DAY ABBREVIATION (Initial Caps) - - if (calDateFormat.indexOf("Mon") != -1) { - - outDate = jsReplace(outDate, "Mon", monthAbbrev); - - } - - // RETURN DAY OF MONTH 3-DAY ABBREVIATION (lowercase letters) - - else if (calDateFormat.indexOf("mon") != -1) { - - outDate = jsReplace(outDate, "mon", monthAbbrev.toLowerCase()); - - } - - // RETURN DAY OF MONTH 3-DAY ABBREVIATION (UPPERCASE LETTERS) - - else if (calDateFormat.indexOf("MON") != -1) { - - outDate = jsReplace(outDate, "MON", monthAbbrev.toUpperCase()); - - } - - - - // RETURN WEEKDAY (Initial Caps) - - if (calDateFormat.indexOf("Weekday") != -1) { - - outDate = jsReplace(outDate, "Weekday", weekday); - - } - - // RETURN WEEKDAY (lowercase letters) - - else if (calDateFormat.indexOf("weekday") != -1) { - - outDate = jsReplace(outDate, "weekday", weekday.toLowerCase()); - - } - - // RETURN WEEKDAY (UPPERCASE LETTERS) - - else if (calDateFormat.indexOf("WEEKDAY") != -1) { - - outDate = jsReplace(outDate, "WEEKDAY", weekday.toUpperCase()); - - } - - - - // RETURN WEEKDAY 3-DAY ABBREVIATION (Initial Caps) - - if (calDateFormat.indexOf("Wkdy") != -1) { - - outDate = jsReplace(outDate, "Wkdy", weekdayAbbrev); - - } - - // RETURN WEEKDAY 3-DAY ABBREVIATION (lowercase letters) - - else if (calDateFormat.indexOf("wkdy") != -1) { - - outDate = jsReplace(outDate, "wkdy", weekdayAbbrev.toLowerCase()); - - } - - // RETURN WEEKDAY 3-DAY ABBREVIATION (UPPERCASE LETTERS) - - else if (calDateFormat.indexOf("WKDY") != -1) { - - outDate = jsReplace(outDate, "WKDY", weekdayAbbrev.toUpperCase()); - - } - - - - // SET THE VALUE OF THE FIELD THAT WAS PASSED TO THE CALENDAR - if(calDateField.getAttribute("readonly")) - { - if(calDateField.getAttribute("readonly")!="true") -{ - calDateField.value = outDate; -} - } - else -{ - calDateField.value = outDate; -} - - - // GIVE FOCUS BACK TO THE DATE FIELD - - calDateField.focus(); - - - - // CLOSE THE CALENDAR WINDOW - - top.newWin.close() - -} - +// + +// JavaScript Calendar Component + +// Author: Robert W. Husted (robert.husted@iname.com) + +// Date: 8/22/1999 + +// Modified Date: 11/30/1999 + +// Modified By: Robert W. Husted + +// Notes: Added frameset support (changed reference for "newWin" to "top.newWin") + +// Also changed Spanish "March" from "Marcha" to "Marzo" + +// Fixed JavaScript Date Anomaly affecting days > 28 + +// + +// + +// + +// Usage: Add the following lines of code to your page to enable the Calendar + +// component. + +// + +// + +// // THIS LINE LOADS THE JS LIBRARY FOR THE CALENDAR COMPONENT + +// + +// + +// + +// + +// + +// // THIS LINE IS USED IN CONJUNCTION WITH A FORM FIELD (myDateField) IN A FORM (myForm). + +// // Replace "myForm" and "myDateField" WITH THE NAME OF YOUR FORM AND INPUT FIELD RESPECTIVELY + +// // WINDOW OPTIONS SET THE WIDTH, HEIGHT, AND X/Y POSITION OF THE CALENDAR WINDOW + +// // WITH TITLEBAR ON, ALL OTHER OPTIONS (TOOLBARS, ETC) ARE DISABLED BY DEFAULT + +// + +// + +// Popup Calendar + +// + +// + +// + +// Required Files: + +// + +// calendar.js - contains all JavaScript functions to make the calendar work + +// + +// calendar.html - frameset document (not required if you call the showCalendar() + +// function. However, calling showCalendar() directly causes + +// the Java Virtual Machine (JVM) to start which slows down the + +// loading of the calendar.) + +// + +// + +// Files Generally Included: + +// + +// calendar.gif - image that looks like a little calendar + +// + +// yourPage.html - page that contains a form and a date field which implements + +// the calendar component + +// + + + + + + + +// BEGIN USER-EDITABLE SECTION ----------------------------------------------------- + + + + + + + +// SPECIFY DATE FORMAT RETURNED BY THIS CALENDAR + +// (THIS IS ALSO THE DATE FORMAT RECOGNIZED BY THIS CALENDAR) + + + +// DATE FORMAT OPTIONS: + +// + +// dd = 1 or 2-digit Day + +// DD = 2-digit Day + +// mm = 1 or 2-digit Month + +// MM = 2-digit Month + +// yy = 2-digit Year + +// YY = 4-digit Year + +// yyyy = 4-digit Year + +// month = Month name in lowercase letters + +// Month = Month name in initial caps + +// MONTH = Month name in captital letters + +// mon = 3-letter month abbreviation in lowercase letters + +// Mon = 3-letter month abbreviation in initial caps + +// MON = 3-letter month abbreviation in uppercase letters + +// weekday = name of week in lowercase letters + +// Weekday = name of week in initial caps + +// WEEKDAY = name of week in uppercase letters + +// wkdy = 3-letter weekday abbreviation in lowercase letters + +// Wkdy = 3-letter weekday abbreviation in initial caps + +// WKDY = 3-letter weekday abbreviation in uppercase letters + +// + +// Examples: + +// + +// calDateFormat = "mm/dd/yy"; + +// calDateFormat = "Weekday, Month dd, yyyy"; + +// calDateFormat = "wkdy, mon dd, yyyy"; + +// calDateFormat = "DD.MM.YY"; // FORMAT UNSUPPORTED BY JAVASCRIPT -- REQUIRES CUSTOM PARSING + +// + + + +calDateFormat = "mm/dd/yyyy"; + + + + + +// CALENDAR COLORS + +topBackground = "white"; // BG COLOR OF THE TOP FRAME + +bottomBackground = "white"; // BG COLOR OF THE BOTTOM FRAME + +tableBGColor = "black"; // BG COLOR OF THE BOTTOM FRAME'S TABLE + +cellColor = "lightgrey"; // TABLE CELL BG COLOR OF THE DATE CELLS IN THE BOTTOM FRAME + +headingCellColor = "white"; // TABLE CELL BG COLOR OF THE WEEKDAY ABBREVIATIONS + +headingTextColor = "black"; // TEXT COLOR OF THE WEEKDAY ABBREVIATIONS + +dateColor = "blue"; // TEXT COLOR OF THE LISTED DATES (1-28+) + +focusColor = "#ff0000"; // TEXT COLOR OF THE SELECTED DATE (OR CURRENT DATE) + +hoverColor = "darkred"; // TEXT COLOR OF A LINK WHEN YOU HOVER OVER IT + +fontStyle = "12pt arial, helvetica"; // TEXT STYLE FOR DATES + +headingFontStyle = "bold 12pt arial, helvetica"; // TEXT STYLE FOR WEEKDAY ABBREVIATIONS + + + +// FORMATTING PREFERENCES + +bottomBorder = false; // TRUE/FALSE (WHETHER TO DISPLAY BOTTOM CALENDAR BORDER) + +tableBorder = 0; // SIZE OF CALENDAR TABLE BORDER (BOTTOM FRAME) 0=none + + + + + + + +// END USER-EDITABLE SECTION ------------------------------------------------------- + + + + + + + +// DETERMINE BROWSER BRAND + +var isNav = false; + +var isIE = false; + + + +// ASSUME IT'S EITHER NETSCAPE OR MSIE + +if (navigator.appName == "Netscape") { + + isNav = true; + +} + +else { + + isIE = true; + +} + + + +// GET CURRENTLY SELECTED LANGUAGE + +selectedLanguage = navigator.language; + + + +// PRE-BUILD PORTIONS OF THE CALENDAR WHEN THIS JS LIBRARY LOADS INTO THE BROWSER + +buildCalParts(); + + + + + + + +// CALENDAR FUNCTIONS BEGIN HERE --------------------------------------------------- + + + + + + + +// SET THE INITIAL VALUE OF THE GLOBAL DATE FIELD + +function setDateField(dateField) { + + + + // ASSIGN THE INCOMING FIELD OBJECT TO A GLOBAL VARIABLE + + calDateField = dateField; + + + + // GET THE VALUE OF THE INCOMING FIELD + + inDate = dateField.value; + + + + // SET calDate TO THE DATE IN THE INCOMING FIELD OR DEFAULT TO TODAY'S DATE + + setInitialDate(); + + + + // THE CALENDAR FRAMESET DOCUMENTS ARE CREATED BY JAVASCRIPT FUNCTIONS + + calDocTop = buildTopCalFrame(); + + calDocBottom = buildBottomCalFrame(); + +} + + + + + +// SET THE INITIAL CALENDAR DATE TO TODAY OR TO THE EXISTING VALUE IN dateField + +function setInitialDate() { + + + + // CREATE A NEW DATE OBJECT (WILL GENERALLY PARSE CORRECT DATE EXCEPT WHEN "." IS USED AS A DELIMITER) + + // (THIS ROUTINE DOES *NOT* CATCH ALL DATE FORMATS, IF YOU NEED TO PARSE A CUSTOM DATE FORMAT, DO IT HERE) + + + + // --------------------------------------------------------- + + // custom parsing of date format added by dikr, + + // added to support easy integration into dbforms tag library 2002-5-28: + + // ignore case when parsing + + var inputCalFormat = calDateFormat.toLowerCase(); + + + + // if pattern is made just of letters 'y','m','d' and + + // some delimiters '-','_','.' or '/' , e.g. 'dd.mm.yyyy', + + // 'yyyy-mm-dd', 'mm/dd/yyyy', we will try to automatically + + // parse it: + + if (inputCalFormat.match(/[ymd]+[\.\-_\/][ymd]+[\.\-_\/][ymd]+/)) { + + // transform pattern to use standard delimiter '-', + + // e.g. 'dd.mm.yyyy' => 'dd-mm-yyyy' + + // neccessary, because split function does not seem to + + // support regular expr. (or am I wrong?) + + inputCalFormat = inputCalFormat.replace(/[\.\-_\/]+/g,'-'); + + // split input format into components + + var formatComps = inputCalFormat.split('-'); + + + + // now the same for input date: transform to use + + // standard delimiter.... + + inDate2 = inDate.replace(/[\.\-_\/]+/g,'-'); + + // and split it into components: + + datComps = inDate2.split('-'); + + // now examine all (hopefully) three parts: + + for (var ii = 0 ; ii<3;ii++) { + + // ii-th part of pattern, hopefully 'dd', 'mm', 'yy'.... + + var pattpart = formatComps[ii]; + + + + if (pattpart.match(/[y]+/)) inyear = datComps[ii]; // year found + + if (pattpart.match(/[m]+/)) inmonth = datComps[ii] - 1; // month found + + if (pattpart.match(/[d]+/)) inday= datComps[ii]; // day found + + + + } + + // create new date object + + calDate = new Date(inyear,inmonth,inday); + + + + } else { // pattern is somehow different, do not try to + + // parse it and use build-in JavaScript date constructor: + + calDate = new Date(inDate); + + // use English language in case format uses textual months + + // that would not be understood by dbforms + + selectedLanguage = 'en'; + + } + + // end of modifications + + // ---------------------------------------------------------- + + + + + + // IF THE INCOMING DATE IS INVALID, USE THE CURRENT DATE + + if (isNaN(calDate)) { + + + + // ADD CUSTOM DATE PARSING HERE + + // IF IT FAILS, SIMPLY CREATE A NEW DATE OBJECT WHICH DEFAULTS TO THE CURRENT DATE + + calDate = new Date(); + + } + + + + // KEEP TRACK OF THE CURRENT DAY VALUE + + calDay = calDate.getDate(); + + + + // SET DAY VALUE TO 1... TO AVOID JAVASCRIPT DATE CALCULATION ANOMALIES + + // (IF THE MONTH CHANGES TO FEB AND THE DAY IS 30, THE MONTH WOULD CHANGE TO MARCH + + // AND THE DAY WOULD CHANGE TO 2. SETTING THE DAY TO 1 WILL PREVENT THAT) + + calDate.setDate(1); + +} + + + + + +// POPUP A WINDOW WITH THE CALENDAR IN IT + +function showCalendar(dateField) { + + + + // SET INITIAL VALUE OF THE DATE FIELD AND CREATE TOP AND BOTTOM FRAMES + + setDateField(dateField); + + + + // USE THE JAVASCRIPT-GENERATED DOCUMENTS (calDocTop, calDocBottom) IN THE FRAMESET + + calDocFrameset = + + "JavaScript Calendar\n" + + + "\n" + + + " \n" + + + " \n" + + + "\n"; + + + + // DISPLAY THE CALENDAR IN A NEW POPUP WINDOW + + top.newWin = window.open("javascript:parent.opener.calDocFrameset", "calWin", winPrefs); + + top.newWin.focus(); + +} + + + + + +// CREATE THE TOP CALENDAR FRAME + +function buildTopCalFrame() { + + + + // CREATE THE TOP FRAME OF THE CALENDAR + + var calDoc = + + "" + + + "" + + + "" + + + "" + + + "
" + + + "
" + + + "" + + + "" + + + "" + + + "" + + + "" + + + "" + + + "
" + + + "
" + + + getMonthSelect() + + + "" + + + "
" + + + "
" + + + "" + + + "
" + + + "
" + + + "
" + + + "" + + + ""; + + + + return calDoc; + +} + + + + + +// CREATE THE BOTTOM CALENDAR FRAME + +// (THE MONTHLY CALENDAR) + +function buildBottomCalFrame() { + + + + // START CALENDAR DOCUMENT + + var calDoc = calendarBegin; + + + + // GET MONTH, AND YEAR FROM GLOBAL CALENDAR DATE + + month = calDate.getMonth(); + + year = calDate.getFullYear(); + + + + + + // GET GLOBALLY-TRACKED DAY VALUE (PREVENTS JAVASCRIPT DATE ANOMALIES) + + day = calDay; + + + + var i = 0; + + + + // DETERMINE THE NUMBER OF DAYS IN THE CURRENT MONTH + + var days = getDaysInMonth(); + + + + // IF GLOBAL DAY VALUE IS > THAN DAYS IN MONTH, HIGHLIGHT LAST DAY IN MONTH + + if (day > days) { + + day = days; + + } + + + + // DETERMINE WHAT DAY OF THE WEEK THE CALENDAR STARTS ON + + var firstOfMonth = new Date (year, month, 1); + + + + // GET THE DAY OF THE WEEK THE FIRST DAY OF THE MONTH FALLS ON + + var startingPos = firstOfMonth.getDay(); + + days += startingPos; + + + + // KEEP TRACK OF THE COLUMNS, START A NEW ROW AFTER EVERY 7 COLUMNS + + var columnCount = 0; + + + + // MAKE BEGINNING NON-DATE CELLS BLANK + + for (i = 0; i < startingPos; i++) { + + + + calDoc += blankCell; + + columnCount++; + + } + + + + // SET VALUES FOR DAYS OF THE MONTH + + var currentDay = 0; + + var dayType = "weekday"; + + + + // DATE CELLS CONTAIN A NUMBER + + for (i = startingPos; i < days; i++) { + + + + var paddingChar = " "; + + + + // ADJUST SPACING SO THAT ALL LINKS HAVE RELATIVELY EQUAL WIDTHS + + if (i-startingPos+1 < 10) { + + padding = "  "; + + } + + else { + + padding = " "; + + } + + + + // GET THE DAY CURRENTLY BEING WRITTEN + + currentDay = i-startingPos+1; + + + + // SET THE TYPE OF DAY, THE focusDay GENERALLY APPEARS AS A DIFFERENT COLOR + + if (currentDay == day) { + + dayType = "focusDay"; + + } + + else { + + dayType = "weekDay"; + + } + + + + // ADD THE DAY TO THE CALENDAR STRING + + calDoc += "" + + + "" + padding + currentDay + paddingChar + ""; + + + + columnCount++; + + + + // START A NEW ROW WHEN NECESSARY + + if (columnCount % 7 == 0) { + + calDoc += ""; + + } + + } + + + + // MAKE REMAINING NON-DATE CELLS BLANK + + for (i=days; i<42; i++) { + + + + calDoc += blankCell; + + columnCount++; + + + + // START A NEW ROW WHEN NECESSARY + + if (columnCount % 7 == 0) { + + calDoc += ""; + + if (i<41) { + + calDoc += ""; + + } + + } + + } + + + + // FINISH THE NEW CALENDAR PAGE + + calDoc += calendarEnd; + + + + // RETURN THE COMPLETED CALENDAR PAGE + + return calDoc; + +} + + + + + +// WRITE THE MONTHLY CALENDAR TO THE BOTTOM CALENDAR FRAME + +function writeCalendar() { + + + + // CREATE THE NEW CALENDAR FOR THE SELECTED MONTH & YEAR + + calDocBottom = buildBottomCalFrame(); + + + + // WRITE THE NEW CALENDAR TO THE BOTTOM FRAME + + top.newWin.frames['bottomCalFrame'].document.open(); + + top.newWin.frames['bottomCalFrame'].document.write(calDocBottom); + + top.newWin.frames['bottomCalFrame'].document.close(); + +} + + + + + +// SET THE CALENDAR TO TODAY'S DATE AND DISPLAY THE NEW CALENDAR + +function setToday() { + + + + // SET GLOBAL DATE TO TODAY'S DATE + + calDate = new Date(); + + + + // SET DAY MONTH AND YEAR TO TODAY'S DATE + + var month = calDate.getMonth(); + + var year = calDate.getFullYear(); + + + + // SET MONTH IN DROP-DOWN LIST + + top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex = month; + + + + // SET YEAR VALUE + + top.newWin.frames['topCalFrame'].document.calControl.year.value = year; + + + + // DISPLAY THE NEW CALENDAR + + writeCalendar(); + +} + + + + + +// SET THE GLOBAL DATE TO THE NEWLY ENTERED YEAR AND REDRAW THE CALENDAR + +function setYear() { + + + + // GET THE NEW YEAR VALUE + + var year = top.newWin.frames['topCalFrame'].document.calControl.year.value; + + + + // IF IT'S A FOUR-DIGIT YEAR THEN CHANGE THE CALENDAR + + if (isFourDigitYear(year)) { + + calDate.setFullYear(year); + + writeCalendar(); + + } + + else { + + // HIGHLIGHT THE YEAR IF THE YEAR IS NOT FOUR DIGITS IN LENGTH + + top.newWin.frames['topCalFrame'].document.calControl.year.focus(); + + top.newWin.frames['topCalFrame'].document.calControl.year.select(); + + } + +} + + + + + +// SET THE GLOBAL DATE TO THE SELECTED MONTH AND REDRAW THE CALENDAR + +function setCurrentMonth() { + + + + // GET THE NEWLY SELECTED MONTH AND CHANGE THE CALENDAR ACCORDINGLY + + var month = top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex; + + + + calDate.setMonth(month); + + writeCalendar(); + +} + + + + + +// SET THE GLOBAL DATE TO THE PREVIOUS YEAR AND REDRAW THE CALENDAR + +function setPreviousYear() { + + + + var year = top.newWin.frames['topCalFrame'].document.calControl.year.value; + + + + if (isFourDigitYear(year) && year > 1000) { + + year--; + + calDate.setFullYear(year); + + top.newWin.frames['topCalFrame'].document.calControl.year.value = year; + + writeCalendar(); + + } + +} + + + + + +// SET THE GLOBAL DATE TO THE PREVIOUS MONTH AND REDRAW THE CALENDAR + +function setPreviousMonth() { + + + + var year = top.newWin.frames['topCalFrame'].document.calControl.year.value; + + if (isFourDigitYear(year)) { + + var month = top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex; + + + + // IF MONTH IS JANUARY, SET MONTH TO DECEMBER AND DECREMENT THE YEAR + + if (month == 0) { + + month = 11; + + if (year > 1000) { + + year--; + + calDate.setFullYear(year); + + top.newWin.frames['topCalFrame'].document.calControl.year.value = year; + + } + + } + + else { + + month--; + + } + + calDate.setMonth(month); + + top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex = month; + + writeCalendar(); + + } + +} + + + + + +// SET THE GLOBAL DATE TO THE NEXT MONTH AND REDRAW THE CALENDAR + +function setNextMonth() { + + + + var year = top.newWin.frames['topCalFrame'].document.calControl.year.value; + + + + if (isFourDigitYear(year)) { + + var month = top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex; + + + + // IF MONTH IS DECEMBER, SET MONTH TO JANUARY AND INCREMENT THE YEAR + + if (month == 11) { + + month = 0; + + year++; + + calDate.setFullYear(year); + + top.newWin.frames['topCalFrame'].document.calControl.year.value = year; + + } + + else { + + month++; + + } + + calDate.setMonth(month); + + top.newWin.frames['topCalFrame'].document.calControl.month.selectedIndex = month; + + writeCalendar(); + + } + +} + + + + + +// SET THE GLOBAL DATE TO THE NEXT YEAR AND REDRAW THE CALENDAR + +function setNextYear() { + + + + var year = top.newWin.frames['topCalFrame'].document.calControl.year.value; + + if (isFourDigitYear(year)) { + + year++; + + calDate.setFullYear(year); + + top.newWin.frames['topCalFrame'].document.calControl.year.value = year; + + writeCalendar(); + + } + +} + + + + + +// GET NUMBER OF DAYS IN MONTH + +function getDaysInMonth() { + + + + var days; + + var month = calDate.getMonth()+1; + + var year = calDate.getFullYear(); + + + + // RETURN 31 DAYS + + if (month==1 || month==3 || month==5 || month==7 || month==8 || + + month==10 || month==12) { + + days=31; + + } + + // RETURN 30 DAYS + + else if (month==4 || month==6 || month==9 || month==11) { + + days=30; + + } + + // RETURN 29 DAYS + + else if (month==2) { + + if (isLeapYear(year)) { + + days=29; + + } + + // RETURN 28 DAYS + + else { + + days=28; + + } + + } + + return (days); + +} + + + + + +// CHECK TO SEE IF YEAR IS A LEAP YEAR + +function isLeapYear (Year) { + + + + if (((Year % 4)==0) && ((Year % 100)!=0) || ((Year % 400)==0)) { + + return (true); + + } + + else { + + return (false); + + } + +} + + + + + +// ENSURE THAT THE YEAR IS FOUR DIGITS IN LENGTH + +function isFourDigitYear(year) { + + + + if (year.length != 4) { + + top.newWin.frames['topCalFrame'].document.calControl.year.value = calDate.getFullYear(); + + top.newWin.frames['topCalFrame'].document.calControl.year.select(); + + top.newWin.frames['topCalFrame'].document.calControl.year.focus(); + + } + + else { + + return true; + + } + +} + + + + + +// BUILD THE MONTH SELECT LIST + +function getMonthSelect() { + + + + // BROWSER LANGUAGE CHECK DONE PREVIOUSLY (navigator.language()) + + // FIRST TWO CHARACTERS OF LANGUAGE STRING SPECIFIES THE LANGUAGE + + // (THE LAST THREE OPTIONAL CHARACTERS SPECIFY THE LANGUAGE SUBTYPE) + + // SET THE NAMES OF THE MONTH TO THE PROPER LANGUAGE (DEFAULT TO ENGLISH) + + + + // IF FRENCH + + if (selectedLanguage == "fr") { + + monthArray = new Array('Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', + + 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'); + + } + + // IF GERMAN + + else if (selectedLanguage == "de") { + + monthArray = new Array('Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', + + 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'); + + } + + // IF SPANISH + + else if (selectedLanguage == "es") { + + monthArray = new Array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', + + 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'); + + } + + // DEFAULT TO ENGLISH + + else { + + monthArray = new Array('January', 'February', 'March', 'April', 'May', 'June', + + 'July', 'August', 'September', 'October', 'November', 'December'); + + } + + + + // DETERMINE MONTH TO SET AS DEFAULT + + var activeMonth = calDate.getMonth(); + + + + // START HTML SELECT LIST ELEMENT + + monthSelect = ""; + + + + // RETURN A STRING VALUE WHICH CONTAINS A SELECT LIST OF ALL 12 MONTHS + + return monthSelect; + +} + + + + + +// SET DAYS OF THE WEEK DEPENDING ON LANGUAGE + +function createWeekdayList() { + + + + // IF FRENCH + + if (selectedLanguage == "fr") { + + weekdayList = new Array('Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'); + + weekdayArray = new Array('Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'); + + } + + // IF GERMAN + + else if (selectedLanguage == "de") { + + weekdayList = new Array('Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'); + + weekdayArray = new Array('So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'); + + } + + // IF SPANISH + + else if (selectedLanguage == "es") { + + weekdayList = new Array('Domingo', 'Lunes', 'Martes', 'Mi?rcoles', 'Jueves', 'Viernes', 'S?bado') + + weekdayArray = new Array('Do', 'Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa'); + + } + + else { + + weekdayList = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); + + weekdayArray = new Array('Su','Mo','Tu','We','Th','Fr','Sa'); + + } + + + + // START HTML TO HOLD WEEKDAY NAMES IN TABLE FORMAT + + var weekdays = ""; + + + + // LOOP THROUGH WEEKDAY ARRAY + + for (i in weekdayArray) { + + + + weekdays += "" + weekdayArray[i] + ""; + + } + + weekdays += ""; + + + + // RETURN TABLE ROW OF WEEKDAY ABBREVIATIONS TO DISPLAY ABOVE THE CALENDAR + + return weekdays; + +} + + + + + +// PRE-BUILD PORTIONS OF THE CALENDAR (FOR PERFORMANCE REASONS) + +function buildCalParts() { + + + + // GENERATE WEEKDAY HEADERS FOR THE CALENDAR + + weekdays = createWeekdayList(); + + + + // BUILD THE BLANK CELL ROWS + + blankCell = "   "; + + + + // BUILD THE TOP PORTION OF THE CALENDAR PAGE USING CSS TO CONTROL SOME DISPLAY ELEMENTS + + calendarBegin = + + "" + + + "" + + + // STYLESHEET DEFINES APPEARANCE OF CALENDAR + + "" + + + "" + + + ""; + + + + // NAVIGATOR NEEDS A TABLE CONTAINER TO DISPLAY THE TABLE OUTLINES PROPERLY + + if (isNav) { + + calendarBegin += + + "
"; + + } + + + + // BUILD WEEKDAY HEADINGS + + calendarBegin += + + "" + + + weekdays + + + ""; + + + + + + // BUILD THE BOTTOM PORTION OF THE CALENDAR PAGE + + calendarEnd = ""; + + + + // WHETHER OR NOT TO DISPLAY A THICK LINE BELOW THE CALENDAR + + if (bottomBorder) { + + calendarEnd += ""; + + } + + + + // NAVIGATOR NEEDS A TABLE CONTAINER TO DISPLAY THE BORDERS PROPERLY + + if (isNav) { + + calendarEnd += "
"; + + } + + + + // END THE TABLE AND HTML DOCUMENT + + calendarEnd += + + "
" + + + "
" + + + "" + + + ""; + +} + + + + + +// REPLACE ALL INSTANCES OF find WITH replace + +// inString: the string you want to convert + +// find: the value to search for + +// replace: the value to substitute + +// + +// usage: jsReplace(inString, find, replace); + +// example: jsReplace("To be or not to be", "be", "ski"); + +// result: "To ski or not to ski" + +// + +function jsReplace(inString, find, replace) { + + + + var outString = ""; + + + + if (!inString) { + + return ""; + + } + + + + // REPLACE ALL INSTANCES OF find WITH replace + + if (inString.indexOf(find) != -1) { + + // SEPARATE THE STRING INTO AN ARRAY OF STRINGS USING THE VALUE IN find + + t = inString.split(find); + + + + // JOIN ALL ELEMENTS OF THE ARRAY, SEPARATED BY THE VALUE IN replace + + return (t.join(replace)); + + } + + else { + + return inString; + + } + +} + + + + + +// JAVASCRIPT FUNCTION -- DOES NOTHING (USED FOR THE HREF IN THE CALENDAR CALL) + +function doNothing() { + +} + + + + + +// ENSURE THAT VALUE IS TWO DIGITS IN LENGTH + +function makeTwoDigit(inValue) { + + + + var numVal = parseInt(inValue, 10); + + + + // VALUE IS LESS THAN TWO DIGITS IN LENGTH + + if (numVal < 10) { + + + + // ADD A LEADING ZERO TO THE VALUE AND RETURN IT + + return("0" + numVal); + + } + + else { + + return numVal; + + } + +} + + + + + +// SET FIELD VALUE TO THE DATE SELECTED AND CLOSE THE CALENDAR WINDOW + +function returnDate(inDay) + +{ + + + + // inDay = THE DAY THE USER CLICKED ON + + calDate.setDate(inDay); + + + + // SET THE DATE RETURNED TO THE USER + + var day = calDate.getDate(); + + var month = calDate.getMonth()+1; + + var year = calDate.getFullYear(); + + var monthString = monthArray[calDate.getMonth()]; + + var monthAbbrev = monthString.substring(0,3); + + var weekday = weekdayList[calDate.getDay()]; + + var weekdayAbbrev = weekday.substring(0,3); + + + + outDate = calDateFormat; + + + + // RETURN TWO DIGIT DAY + + if (calDateFormat.indexOf("DD") != -1) { + + day = makeTwoDigit(day); + + outDate = jsReplace(outDate, "DD", day); + + } + + // RETURN ONE OR TWO DIGIT DAY + + else if (calDateFormat.indexOf("dd") != -1) { + + outDate = jsReplace(outDate, "dd", day); + + } + + + + // RETURN TWO DIGIT MONTH + + if (calDateFormat.indexOf("MM") != -1) { + + month = makeTwoDigit(month); + + outDate = jsReplace(outDate, "MM", month); + + } + + // RETURN ONE OR TWO DIGIT MONTH + + else if (calDateFormat.indexOf("mm") != -1) { + + outDate = jsReplace(outDate, "mm", month); + + } + + + + // RETURN FOUR-DIGIT YEAR + + if (calDateFormat.indexOf("yyyy") != -1) { + + outDate = jsReplace(outDate, "yyyy", year); + + } + + // RETURN TWO-DIGIT YEAR + + else if (calDateFormat.indexOf("yy") != -1) { + + var yearString = "" + year; + + var yearString = yearString.substring(2,4); + + outDate = jsReplace(outDate, "yy", yearString); + + } + + // RETURN FOUR-DIGIT YEAR + + else if (calDateFormat.indexOf("YY") != -1) { + + outDate = jsReplace(outDate, "YY", year); + + } + + + + // RETURN DAY OF MONTH (Initial Caps) + + if (calDateFormat.indexOf("Month") != -1) { + + outDate = jsReplace(outDate, "Month", monthString); + + } + + // RETURN DAY OF MONTH (lowercase letters) + + else if (calDateFormat.indexOf("month") != -1) { + + outDate = jsReplace(outDate, "month", monthString.toLowerCase()); + + } + + // RETURN DAY OF MONTH (UPPERCASE LETTERS) + + else if (calDateFormat.indexOf("MONTH") != -1) { + + outDate = jsReplace(outDate, "MONTH", monthString.toUpperCase()); + + } + + + + // RETURN DAY OF MONTH 3-DAY ABBREVIATION (Initial Caps) + + if (calDateFormat.indexOf("Mon") != -1) { + + outDate = jsReplace(outDate, "Mon", monthAbbrev); + + } + + // RETURN DAY OF MONTH 3-DAY ABBREVIATION (lowercase letters) + + else if (calDateFormat.indexOf("mon") != -1) { + + outDate = jsReplace(outDate, "mon", monthAbbrev.toLowerCase()); + + } + + // RETURN DAY OF MONTH 3-DAY ABBREVIATION (UPPERCASE LETTERS) + + else if (calDateFormat.indexOf("MON") != -1) { + + outDate = jsReplace(outDate, "MON", monthAbbrev.toUpperCase()); + + } + + + + // RETURN WEEKDAY (Initial Caps) + + if (calDateFormat.indexOf("Weekday") != -1) { + + outDate = jsReplace(outDate, "Weekday", weekday); + + } + + // RETURN WEEKDAY (lowercase letters) + + else if (calDateFormat.indexOf("weekday") != -1) { + + outDate = jsReplace(outDate, "weekday", weekday.toLowerCase()); + + } + + // RETURN WEEKDAY (UPPERCASE LETTERS) + + else if (calDateFormat.indexOf("WEEKDAY") != -1) { + + outDate = jsReplace(outDate, "WEEKDAY", weekday.toUpperCase()); + + } + + + + // RETURN WEEKDAY 3-DAY ABBREVIATION (Initial Caps) + + if (calDateFormat.indexOf("Wkdy") != -1) { + + outDate = jsReplace(outDate, "Wkdy", weekdayAbbrev); + + } + + // RETURN WEEKDAY 3-DAY ABBREVIATION (lowercase letters) + + else if (calDateFormat.indexOf("wkdy") != -1) { + + outDate = jsReplace(outDate, "wkdy", weekdayAbbrev.toLowerCase()); + + } + + // RETURN WEEKDAY 3-DAY ABBREVIATION (UPPERCASE LETTERS) + + else if (calDateFormat.indexOf("WKDY") != -1) { + + outDate = jsReplace(outDate, "WKDY", weekdayAbbrev.toUpperCase()); + + } + + + + // SET THE VALUE OF THE FIELD THAT WAS PASSED TO THE CALENDAR + if(calDateField.getAttribute("readonly")) + { + if(calDateField.getAttribute("readonly")!="true") +{ + calDateField.value = outDate; +} + } + else +{ + calDateField.value = outDate; +} + + + // GIVE FOCUS BACK TO THE DATE FIELD + + calDateField.focus(); + + + + // CLOSE THE CALENDAR WINDOW + + top.newWin.close() + +} + diff --git a/superx/dbformslib/jscal/calendar.svg b/superx/dbformslib/jscal/calendar.svg index 0a334d0..e0645eb 100644 --- a/superx/dbformslib/jscal/calendar.svg +++ b/superx/dbformslib/jscal/calendar.svg @@ -1,55 +1,41 @@ - - - - - - image/svg+xml - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/superx/default/custom/custom_pdf.xsl.sam b/superx/default/custom/custom_pdf.xsl.sam new file mode 100644 index 0000000..ef85763 --- /dev/null +++ b/superx/default/custom/custom_pdf.xsl.sam @@ -0,0 +1,72 @@ + + + + + + + + + + + + + servlet/ + + + + + + + + + + + + + + + + Erzeugungsdatum: + + + / + + + + + + + + + + + + + + + + + + + + + + + + + + + Erzeugungsdatum: + + + / + + + + diff --git a/superx/default/custom/studierende_nach_semester_geschlecht.jrxml b/superx/default/custom/studierende_nach_semester_geschlecht.jrxml new file mode 100644 index 0000000..dfd7cdd --- /dev/null +++ b/superx/default/custom/studierende_nach_semester_geschlecht.jrxml @@ -0,0 +1,401 @@ + + + + + + + + /ergebnisse/ergebnis/ergebniselement/sqlerg/row + + + /ergebnisse/ergebnis[ @ordnr='0']/maskenname + + + /ergebnisse/ergebnis[ @ordnr='0']/explanation + + + /ergebnisse/generalinfo/REPORT_HEADING_INSTITUTION + + + /ergebnisse/generalinfo/REPORT_HEADING_URL + + + /ergebnisse/generalinfo/REPORT_LOGO_FILE + + + /ergebnisse/generalinfo/REPORT_HEADING_ADRESS + + + /ergebnisse/generalinfo/REPORT_EMAIL + + + /ergebnisse/generalinfo/REPORT_DOCUMENTATION_URL + + + col[@id="0" and wert!=""]/wert + + + col[@id="1" and wert!=""]/wert + + + col[@id="2" and wert!=""]/wert + + + col[@id="3" and wert!=""]/wert + + + col[@id="4" and wert!=""]/wert + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/@varname + + + /ergebnisse/ergebnis[ @ordnr='0']/completefields/feld [@tid=""]/value_caption + + + /ergebnisse/ergebnis[ @ordnr='0']/stand + + + /ergebnisse/user + + + + $F{Summe} + + + $F{Summe} + + + $F{Geschlecht} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/superx/default/custom/superx.gif b/superx/default/custom/superx.gif new file mode 100644 index 0000000..d78bdf7 Binary files /dev/null and b/superx/default/custom/superx.gif differ diff --git a/superx/default/custom/tabelle_8110000_anfaenger.jrxml b/superx/default/custom/tabelle_8110000_anfaenger.jrxml new file mode 100644 index 0000000..38b2b17 --- /dev/null +++ b/superx/default/custom/tabelle_8110000_anfaenger.jrxml @@ -0,0 +1,1058 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <band height="103"> + <textField> + <reportElement x="0" y="0" width="285" height="12"/> + <textElement> + <font fontName="Liberation Sans"/> + </textElement> + <textFieldExpression><![CDATA[$F{REPORT_HEADING_INSTITUTION}]]></textFieldExpression> + </textField> + <textField> + <reportElement x="0" y="20" width="285" height="12"/> + <textElement> + <font fontName="Liberation Sans"/> + </textElement> + <textFieldExpression><![CDATA[$F{REPORT_HEADING_URL}]]></textFieldExpression> + </textField> + <textField> + <reportElement x="0" y="44" width="303" height="18"/> + <textElement> + <font fontName="Liberation Sans" size="14" isBold="true"/> + </textElement> + <textFieldExpression><![CDATA[$F{Berichtsname}]]></textFieldExpression> + </textField> + <textField> + <reportElement x="24" y="62" width="100" height="10"/> + <textElement> + <font fontName="Liberation Sans" size="8"/> + </textElement> + <textFieldExpression><![CDATA[$F{standdatum}]]></textFieldExpression> + </textField> + <staticText> + <reportElement mode="Transparent" x="0" y="62" width="24" height="10" forecolor="#000000" backcolor="#FFFFFF"/> + <textElement textAlignment="Left" verticalAlignment="Top" rotation="None" markup="none"> + <font fontName="Liberation Sans" size="8" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false" pdfFontName="Liberation Sans" pdfEncoding="Cp1252" isPdfEmbedded="false"/> + <paragraph lineSpacing="Single"/> + </textElement> + <text><![CDATA[Stand:]]></text> + </staticText> + <image> + <reportElement x="730" y="0" width="80" height="32"/> + <imageExpression class="java.lang.String">"../"+$F{REPORT_LOGO_FILE}</imageExpression> + </image> + <textField> + <reportElement mode="Opaque" x="250" y="72" width="560" height="31" backcolor="#999999"/> + <textElement textAlignment="Center" verticalAlignment="Middle"> + <font size="18" isBold="true"/> + </textElement> + <textFieldExpression><![CDATA["Studentenjahrgang " + $F{Jahrgang}]]></textFieldExpression> + </textField> + </band> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/superx/default/custom/tabelle_8110020_studierende.jrxml b/superx/default/custom/tabelle_8110020_studierende.jrxml new file mode 100644 index 0000000..7231a81 --- /dev/null +++ b/superx/default/custom/tabelle_8110020_studierende.jrxml @@ -0,0 +1,1217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <band height="103"> + <textField> + <reportElement x="0" y="0" width="285" height="12" uuid="3d703551-372f-4320-90a2-d13833617b4f"/> + <textElement> + <font fontName="Liberation Sans"/> + </textElement> + <textFieldExpression><![CDATA[$F{REPORT_HEADING_INSTITUTION}]]></textFieldExpression> + </textField> + <textField> + <reportElement x="0" y="20" width="285" height="12" uuid="f8b3128b-77f7-4648-93e9-422072bcaec3"/> + <textElement> + <font fontName="Liberation Sans"/> + </textElement> + <textFieldExpression><![CDATA[$F{REPORT_HEADING_URL}]]></textFieldExpression> + </textField> + <textField> + <reportElement x="0" y="44" width="303" height="18" uuid="8a4a111d-b4af-40dc-9ad6-ce23ffb00fa6"/> + <textElement> + <font fontName="Liberation Sans" size="14" isBold="true"/> + </textElement> + <textFieldExpression><![CDATA[$F{Berichtsname}]]></textFieldExpression> + </textField> + <textField> + <reportElement x="24" y="62" width="100" height="10" uuid="714d217e-c2ca-4a09-bf28-ceed947c7a4f"/> + <textElement> + <font fontName="Liberation Sans" size="8"/> + </textElement> + <textFieldExpression><![CDATA[$F{standdatum}]]></textFieldExpression> + </textField> + <staticText> + <reportElement mode="Transparent" x="0" y="62" width="24" height="10" forecolor="#000000" backcolor="#FFFFFF" uuid="c4d8c362-84fd-45c4-83fe-4b93ebbce36a"/> + <textElement textAlignment="Left" verticalAlignment="Top" rotation="None" markup="none"> + <font fontName="Liberation Sans" size="8" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false" pdfFontName="Liberation Sans" pdfEncoding="Cp1252" isPdfEmbedded="false"/> + <paragraph lineSpacing="Single"/> + </textElement> + <text><![CDATA[Stand:]]></text> + </staticText> + <image> + <reportElement x="730" y="0" width="80" height="32" uuid="cabc0b51-4a18-4e0f-b3e8-05283949bc4d"/> + <imageExpression><![CDATA["../"+$F{REPORT_LOGO_FILE}]]></imageExpression> + </image> + <textField> + <reportElement mode="Opaque" x="300" y="72" width="510" height="31" backcolor="#999999" uuid="286f5595-6f1a-40f9-be6d-adfdd8b8febd"/> + <textElement textAlignment="Center" verticalAlignment="Middle"> + <font size="18" isBold="true"/> + </textElement> + <textFieldExpression><![CDATA["Studenten - Ist"]]></textFieldExpression> + </textField> + </band> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/superx/default/custom/tabelle_8110040_absolv.jrxml b/superx/default/custom/tabelle_8110040_absolv.jrxml new file mode 100644 index 0000000..bea4f62 --- /dev/null +++ b/superx/default/custom/tabelle_8110040_absolv.jrxml @@ -0,0 +1,2057 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <band height="103"> + <textField> + <reportElement x="0" y="20" width="520" height="12"/> + <textElement> + <font fontName="Liberation Sans"/> + </textElement> + <textFieldExpression><![CDATA[$F{REPORT_HEADING_URL}]]></textFieldExpression> + </textField> + <textField> + <reportElement mode="Opaque" x="250" y="72" width="1190" height="31" backcolor="#999999"/> + <textElement textAlignment="Center" verticalAlignment="Middle"> + <font size="18" isBold="true"/> + </textElement> + <textFieldExpression><![CDATA["Absolventen " + $F{Jahrgang}]]></textFieldExpression> + </textField> + <textField> + <reportElement x="0" y="0" width="520" height="12"/> + <textElement> + <font fontName="Liberation Sans"/> + </textElement> + <textFieldExpression><![CDATA[$F{REPORT_HEADING_INSTITUTION}]]></textFieldExpression> + </textField> + <textField> + <reportElement x="89" y="62" width="161" height="10"/> + <textElement> + <font fontName="Liberation Sans" size="8"/> + </textElement> + <textFieldExpression><![CDATA[$F{standdatum}]]></textFieldExpression> + </textField> + <staticText> + <reportElement mode="Transparent" x="0" y="62" width="89" height="10" forecolor="#000000" backcolor="#FFFFFF"/> + <textElement textAlignment="Left" verticalAlignment="Top" rotation="None" markup="none"> + <font fontName="Liberation Sans" size="8" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false" pdfFontName="Liberation Sans" pdfEncoding="Cp1252" isPdfEmbedded="false"/> + <paragraph lineSpacing="Single"/> + </textElement> + <text><![CDATA[Stand:]]></text> + </staticText> + <textField> + <reportElement x="0" y="44" width="520" height="18"/> + <textElement> + <font fontName="Liberation Sans" size="14" isBold="true"/> + </textElement> + <textFieldExpression><![CDATA[$F{Berichtsname}]]></textFieldExpression> + </textField> + </band> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/superx/doku/hierhin_die_doku.txt b/superx/doku/hierhin_die_doku.txt deleted file mode 100644 index e69de29..0000000 diff --git a/superx/edit/kern/webapp_manager.jsp b/superx/edit/kern/webapp_manager.jsp index 70707c9..c57125d 100644 --- a/superx/edit/kern/webapp_manager.jsp +++ b/superx/edit/kern/webapp_manager.jsp @@ -11,7 +11,6 @@ <% String platform=de.superx.servlet.SuperXManager.isHis1?"HISinOne":"SuperX"; -String restApiPath="ds/api"; //klappt noch nicht: platform.equals("HISinOne")?"ds/api":"sxrest"; %> @@ -26,7 +25,6 @@ String restApiPath="ds/api"; //klappt noch nicht: platform.equals("HISinOne")?"d - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-

-
- -
-
-

Product Unit Sales / Marital Status

-
-
-
-
-
- -
- -
-
-

Product Family per Quarter

-
-
-
-
- -
- - - - - - - - - - - diff --git a/superx/saiku/embed/embed.html b/superx/saiku/embed/embed.html deleted file mode 100644 index 5dd9e7a..0000000 --- a/superx/saiku/embed/embed.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - Saiku Embedded - Next Generation Open Source Analytics in your App! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - - - diff --git a/superx/saiku/embed/favicon.ico b/superx/saiku/embed/favicon.ico deleted file mode 100644 index 580836b..0000000 Binary files a/superx/saiku/embed/favicon.ico and /dev/null differ diff --git a/superx/saiku/embed/images/arrow-down.gif b/superx/saiku/embed/images/arrow-down.gif deleted file mode 100644 index 46a10b3..0000000 Binary files a/superx/saiku/embed/images/arrow-down.gif and /dev/null differ diff --git a/superx/saiku/embed/images/arrow-none.gif b/superx/saiku/embed/images/arrow-none.gif deleted file mode 100644 index 578f0c3..0000000 Binary files a/superx/saiku/embed/images/arrow-none.gif and /dev/null differ diff --git a/superx/saiku/embed/images/arrow-up.gif b/superx/saiku/embed/images/arrow-up.gif deleted file mode 100644 index 7ed2ce8..0000000 Binary files a/superx/saiku/embed/images/arrow-up.gif and /dev/null differ diff --git a/superx/saiku/embed/saiku.css b/superx/saiku/embed/saiku.css deleted file mode 100644 index 3d6209a..0000000 --- a/superx/saiku/embed/saiku.css +++ /dev/null @@ -1,159 +0,0 @@ -.workspace_results { - margin-right: 10px; - margin-left: 10px; - padding-top: 10px; - overflow: auto; - font-size: 11px; -} -.workspace_results table { - border-bottom: 1px solid #d5d5d5; - border-right: 1px solid #d5d5d5; - margin-bottom: 0; - table-layout: fixed; - width: auto; - border-collapse: separate; - border-spacing: 0; -} -.workspace_results table th, .workspace_results table td { - padding: 4px 10px 4px 5px; -} -.workspace_results table th { - border-left: 1px solid #d5d5d5; - margin-bottom: 0; - white-space: normal; - font-weight: 500; -} - -.workspace_results table th.all_null { - text-align: center; - border: none; -} -.workspace_results table th.col { - border-top: 1px solid #d5d5d5; - background: #f9f9f9; - text-align: left; -} -.workspace_results table th.col_null { - background: #f9f9f9; -} -.workspace_results table th.row { - border-top: 1px solid #d5d5d5; - background: #f9f9f9; -} - -.workspace_results table th.row_header { - border-top: 1px solid #d5d5d5; - /*border-bottom: 1px solid #d5d5d5;*/ - background: #E5E5E5; -} - -.workspace_results table th.row_null { - background: #f9f9f9; -} -.workspace_results table td.data { - border-left: 1px solid #d5d5d5; - border-top: 1px solid #d5d5d5; - text-align: right; - white-space: nowrap; -} -.workspace_results table tr:hover { - background: #edf4fa !important; -} - -.workspace_results table td.cellhighlight:hover { - background: #c2d5e2 !important; -} -.workspace_results table td.cellhighlight { - cursor: pointer; -} - -.workspace_results .headerhighlight:hover { - background: #c2d5e2 !important; -} -.workspace_results .headerhighlight { - cursor: pointer; -} - -.workspace_results table td.selected { - background: #c2d5e2 !important; -} - -.canvas_wrapper a.button { - display: block; - height: 16px; - padding: 12px; - width: 16px; - border-radius: 3px; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; -} - -.canvas_wrapper a.button:hover { - border: 1px solid #CCC; - padding: 11px; -} -.canvas_wrapper .zoomin { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABwUlEQVQ4EcVTu4rCUBCdGxUxvrBRC7W08Qv2C3abFSJibWcjiLaCrWBnr2BpZSy22D/wCywttPLB+ggYxVd2zmWFyCprtxeS3Mycc+bOyURYlkX9fp8f1pfD4Uil0+kZ/bFarVbvfD6rhULhTei6/un1el+FELRer43j8ZhkoWkulxN2nU6nY+XzeQGy3+/XWICWy+WHkyubOAUH6XK5BAzDmLKInSv3IDDZ8vl8FAwGUYyY96JompZhEq1WK+LKFA6HIfRLAEVisRi53W6Jnc/nBuNSCpDZbJYg4nK5JPGeAE4AEWD46NgnS6XSzAlGt9uVVSeTiQQ9EhiNRhIHMcZMmSoEVHnhdmMags8s2cIzwEeY/xeQJrbbbZhnwZwfg6hYLN540mg0LJh7zcO7Wq0mZAtX16PRqPzW13d73yAmEgmKx+PyS4GMvMLTFQFBVVUyTZNOp5OsYidjDwxy2+1WTi0L9BBXODjEGHs8HtrtdjQejyUYSfvCCZDDmIdCIQoEAlq1Wu0pnBgcDgc5npvNRi+Xy+JeC+h5v99HF4uFgf8AGI6pcpCazSYM1CuVSsZe9d6eq0aYOORrUK/X378BS8Uv9o0RXx4AAAAASUVORK5CYII=); - background-repeat: no-repeat; - background-position: 7px 7px; -} -.canvas_wrapper .zoomout { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABD0lEQVQ4EZ2SvWqFQBCFx0u8qK1tSHXB6raBkIezEXwAC2tBbLQK5A1SJFVIYZNesBHs/NfsETaIMoIO6A6659szO6NM00Qi8FKQHI3LniCKoq8gCD729rCAMAx/NE17Ngzj1ff9bw6irEuI4/jaNE2q6/rNNE0ahoGyLKM8z3+rqrrbtt0uYRsHdV2/t2372Pf9LB7HkVRVJQF96rrubSlGvnEgN3ieN1mWNUPSNKWyLF8cx/mU/+X6IJP1Kk4kuECJRVGQ67obMTQsQJTyD0DOBQuAA9SP5xRA3PjsAADAuGAd4FS08LSD5R2ccgARHKALYi64CvguSACUYoCOA3BqkiTzRcIJF+wkcoL19z/A5b0M1rYC6wAAAABJRU5ErkJggg==); - background-repeat: no-repeat; - background-position: 7px 7px; -} -.canvas_wrapper .rerender { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACJElEQVQ4EY1TTYuSURQ+4/sqjPixUEGFZtEPCMIfMctZTIKEm1bBVFSEMYEtB0oNDQMJhgLB8OsVZ1d7twOiC/EDlAYRCaSN5vfbc27dyWEc6MLhXM59nuecc8+9pOs6wXix32qxWOw4n88r284N9B9rOp3er9VqH7dBd1gVS89ms3ur1epwNpvtz+dzH7wLRiCT0+mkxWJBnU7nfTKZfLYpJARA1gE4s1qtB2azmUwmExkMBoIgLZdL4VmgWq1Su90+SafTYSmignyLs7jd7gObzSYy8iGquCSu12ux54oQN0kye3UymRza7XayWCyCxGLdbleYbMHr9YoWWq3Wh2Kx+PKKAAj73CMT2SqVyncQi/F4/IUEBgKBGlo41zTtiYxJr47H47v1ep1A+sULwHQikXgtAexRdk5V1TepVOqBTCS9Cs4rkD8B9EgGC4WCAt6x3+8/YYFSqSR8NBq9DdwR8LvgGbD/eTlG4HYYzCuXy31GJb5gMHjnT4QoEom8Q4J7Pp9vT1EUGg6H3O7XawKYShIX+pjHNhgMxL0gIzkcDvJ4PGQ0GsVEeKSNRuO5KjOwz2QyESa7XC4B4svdfAuYmHjqvV6PyWc4064IoMQ5Z+CMcvZSAD3TaDSifr9PzWaTyW/xPy6utYDxJfCgnnILnInF2HBhP+DPEf+GRFq5XL4Q1f/9YXD/fmI4HD4NhUK1zdhN+62/ESU+RMlfNu/npv1vX2Di9uFiybcAAAAASUVORK5CYII=); - background-repeat: no-repeat; - background-position: 7px 7px; -} - - -/** BLOCKUI MODIFIED FOR EMBED**/ - -.processing_container, .blockOverlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: #fff; - opacity: 0.5; - z-index: 9999; - padding: 20px; -} -.processing_content { - padding: 5px; - display: inline; -} -.processing, .blockElement { - background-color: #FFFFFF; - border-radius: 3px; - border: 1px solid #EAEAEA; - z-index: 10001; - position: fixed; - width: 20%; - min-width: 300px; - left: 40%; - top: 45%; - padding: 20px; -} -.processing_image { - display: inline-block; - background-image: url(data:image/gif;base64,R0lGODlhEAAQAPQAAP///2ZmZvr6+oeHh7S0tGlpaXx8fObm5srKynNzc6ysrKOjo+7u7sDAwNzc3JGRkZmZmQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH+GkNyZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAAKAAEALAAAAAAQABAAAAVoICCKR9KMaCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkEAAoAAgAsAAAAABAAEAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoLLoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkEAAoAAwAsAAAAABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAAKAAQALAAAAAAQABAAAAVgICCOZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYAqrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAAKAAUALAAAAAAQABAAAAVfICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0UaFBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA==); - background-repeat: no-repeat; - width: 16px; - height: 16px; - margin-right: 5px; -} - -.saiku_logo { - display: inline-block; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAVdSURBVHjarFdNiBxFFH7vVfXM7OzMJhAMCmazEEEJaEgOIQdNLv4cDAieQhT0FFAhoDkoHjyIeFNJUMSfg16CICaHXFQSiRtMFMWfDQR/ICQhSGLMkuzuZLu7uur5qmd6uqenZ2HjDPS87qpX9VW9971Xr/DYzAwAM/if/6d2CxgJInAQiIyd7XXghjrCwwS0kxEeIMC7pUN3B3LCzJcd85xlOJW45AQ5vqgAwTgGRIYJwfghjOBTeXpjUqGzl15ritX99zosCrANFe0PSD0hLWt9O3K23HwiaV1PCNsUwrMa9U1Gd8xZe1A6foKi/sAY2VcRvIeefU4S0Vu1IDhTI/VMBg4V4Fxo84tGgDWE9LTW+rRWdBCB21Xg/p0GwPPd39tQ6htF+JJ81jjvXBG8Pzn35wwCwv01rU7K+2ZmN2QFIukhzkyRNt6vA3UcCbcPAq4avOBM3BYEwQmFuBVTF6ZW6soPpqf7O59SatPaqanjTDgzNvBspyI6zl26tLj4SGKSP6mnQRfEBv65SKrB7fZhUjQOcMOlPtcl1fR9rdZnNUVNgwhWoox2NOqwo16Dh1qTB9Zptd06/t87lzleN0myT8y8WJzDeQ4o2lpvNF4xSkJcNk5GKUi03siBfnkc4F7I5uYlVD9aWo52W3Y3M33f55yDjfXagS3NiXvkAVqwFpjoOfFRe1w+l51r4RFcSeLZG3H8IvVV+wmvKat8IbQJ0CRiq65orxsj4bzwKfKuWk3I5z4Jrf0eC/Gf5grEPWKltSSJYocC2DAu8GKbEh/XkCQpuo+5FP+CeadW+KD04s5xg2f6yqdy4ZWk5FlpiXO97riA9C6S5LBlteDyOSdkels+zSjwLM2BkFzkNXY5GTMhUbGF5MTaxKvcueT2f42zB5ajeLfMch1G6PtsVxMyihu8z7k8v7RNiwXgjtswu1BHlm7ir2146ymfU4qm5VTm86XvFfOnh1YqV+lz7p1irBVcN+arThwdzlXKxy6Xzp+B+YWi3oS3Qzj/pTRYFUCY2HekNxkGz9+5kme8QNbZ8xU+lAoH/l6J7d4Kgdi+4YnG/KuQ8twQOJcWXFqYc3zZD5/j4Z0Lx+xjiXXPi4OWR4WaJ5kWliutfU32O47QYx4G94lJTHaWOok9VWF2mRZvhca8v9gRkmWVRMlSHlAKF5iQRxOFg0CjwTOyonWzhMacFh5cKftcBupAZBRGR6PYvIsjOKJ92ZOe94xQScLSL1/QvGDMUkB0Q8z9OUKZcJLFtAbVakIH+E05subL4FyIitG+HwDty+UkObqUmGvUkbJ7KYnfk5I6HCyjuop1TzZ2V621X1bFeXnyftz3PTGs5zPoleXw0NXlCOicS+BsEv/xTxwfCjCP5V51K+EAEAoDYudOjjQxQ0V1XK6qeryRyOE4/nC93CFmJI/o66abpX424RtNUrvXKNpsM31ZwaTUC80kAXTqKis9AtwvGEsrGcolLOcOdKw9fzGKXuvyWty8wZheucSL84tub2uqfUL4uS7zBcdy3kRymwkk6icag/4duHBUs72g1IzkQDqzsLTnQhzPZ8vV7UJY3IrNb4tx/ORUvf6FhF5jgMMII8F7BQaskAlrGvHmX2G070gc/0gFHW1KZ7SYYtbE5lG5H86rwi6GU+nKcZ5L510Z2SR5XLLlnEtbinfDoXhlf07/YmXL5BflXcQrmbjXhdUh1yP1ArCbK49L7wUjzeZNGoY+YWcgCnPAWoUrgiw7iszZKtvnyoVxL5FVmC01q+QHCZd+W8zuO2Ptq0LFXbKQ/gmKmSucuyzgx4Vo34o8ojBPVjBUFed30f8EGAADcKFb3PyBeQAAAABJRU5ErkJggg==); - background-repeat: no-repeat; - background-position: 50% 50%; - width: 32px; - height: 32px; - padding: 5px; - margin-right: 20px; -} \ No newline at end of file diff --git a/superx/saiku/favicon.ico b/superx/saiku/favicon.ico deleted file mode 100644 index 580836b..0000000 Binary files a/superx/saiku/favicon.ico and /dev/null differ diff --git a/superx/saiku/forward.html b/superx/saiku/forward.html deleted file mode 100644 index ad0782d..0000000 --- a/superx/saiku/forward.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - diff --git a/superx/saiku/images/arrow-down.gif b/superx/saiku/images/arrow-down.gif deleted file mode 100644 index 46a10b3..0000000 Binary files a/superx/saiku/images/arrow-down.gif and /dev/null differ diff --git a/superx/saiku/images/arrow-none.gif b/superx/saiku/images/arrow-none.gif deleted file mode 100644 index 578f0c3..0000000 Binary files a/superx/saiku/images/arrow-none.gif and /dev/null differ diff --git a/superx/saiku/images/arrow-up.gif b/superx/saiku/images/arrow-up.gif deleted file mode 100644 index 7ed2ce8..0000000 Binary files a/superx/saiku/images/arrow-up.gif and /dev/null differ diff --git a/superx/saiku/images/chart/100bar.png b/superx/saiku/images/chart/100bar.png deleted file mode 100644 index dc356bf..0000000 Binary files a/superx/saiku/images/chart/100bar.png and /dev/null differ diff --git a/superx/saiku/images/chart/area.png b/superx/saiku/images/chart/area.png deleted file mode 100644 index c8b9765..0000000 Binary files a/superx/saiku/images/chart/area.png and /dev/null differ diff --git a/superx/saiku/images/chart/area2.png b/superx/saiku/images/chart/area2.png deleted file mode 100644 index 82bec21..0000000 Binary files a/superx/saiku/images/chart/area2.png and /dev/null differ diff --git a/superx/saiku/images/chart/bar.png b/superx/saiku/images/chart/bar.png deleted file mode 100644 index c63809a..0000000 Binary files a/superx/saiku/images/chart/bar.png and /dev/null differ diff --git a/superx/saiku/images/chart/dot.png b/superx/saiku/images/chart/dot.png deleted file mode 100644 index 0427b9d..0000000 Binary files a/superx/saiku/images/chart/dot.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/100bar.png b/superx/saiku/images/chart/gray/100bar.png deleted file mode 100644 index 1059fc5..0000000 Binary files a/superx/saiku/images/chart/gray/100bar.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/area.png b/superx/saiku/images/chart/gray/area.png deleted file mode 100644 index 8bb863e..0000000 Binary files a/superx/saiku/images/chart/gray/area.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/area2.png b/superx/saiku/images/chart/gray/area2.png deleted file mode 100644 index 6aeba92..0000000 Binary files a/superx/saiku/images/chart/gray/area2.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/bar.png b/superx/saiku/images/chart/gray/bar.png deleted file mode 100644 index 1c5e07b..0000000 Binary files a/superx/saiku/images/chart/gray/bar.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/dot.png b/superx/saiku/images/chart/gray/dot.png deleted file mode 100644 index ddaf447..0000000 Binary files a/superx/saiku/images/chart/gray/dot.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/heat.png b/superx/saiku/images/chart/gray/heat.png deleted file mode 100644 index c34262b..0000000 Binary files a/superx/saiku/images/chart/gray/heat.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/line.png b/superx/saiku/images/chart/gray/line.png deleted file mode 100644 index c3a76ad..0000000 Binary files a/superx/saiku/images/chart/gray/line.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/multiple.png b/superx/saiku/images/chart/gray/multiple.png deleted file mode 100644 index 351d642..0000000 Binary files a/superx/saiku/images/chart/gray/multiple.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/multiplesunburst.png b/superx/saiku/images/chart/gray/multiplesunburst.png deleted file mode 100644 index ed2bf9d..0000000 Binary files a/superx/saiku/images/chart/gray/multiplesunburst.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/pie.png b/superx/saiku/images/chart/gray/pie.png deleted file mode 100644 index 124afe8..0000000 Binary files a/superx/saiku/images/chart/gray/pie.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/stackedbar.png b/superx/saiku/images/chart/gray/stackedbar.png deleted file mode 100644 index cc222f7..0000000 Binary files a/superx/saiku/images/chart/gray/stackedbar.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/sunburst.png b/superx/saiku/images/chart/gray/sunburst.png deleted file mode 100644 index 80764dd..0000000 Binary files a/superx/saiku/images/chart/gray/sunburst.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/treemap.png b/superx/saiku/images/chart/gray/treemap.png deleted file mode 100644 index 97a2604..0000000 Binary files a/superx/saiku/images/chart/gray/treemap.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/trendbars.png b/superx/saiku/images/chart/gray/trendbars.png deleted file mode 100644 index 866d7c0..0000000 Binary files a/superx/saiku/images/chart/gray/trendbars.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/trendline.png b/superx/saiku/images/chart/gray/trendline.png deleted file mode 100644 index 6e4970f..0000000 Binary files a/superx/saiku/images/chart/gray/trendline.png and /dev/null differ diff --git a/superx/saiku/images/chart/gray/waterfall.png b/superx/saiku/images/chart/gray/waterfall.png deleted file mode 100644 index 514c579..0000000 Binary files a/superx/saiku/images/chart/gray/waterfall.png and /dev/null differ diff --git a/superx/saiku/images/chart/heat.png b/superx/saiku/images/chart/heat.png deleted file mode 100644 index e2dbb5f..0000000 Binary files a/superx/saiku/images/chart/heat.png and /dev/null differ diff --git a/superx/saiku/images/chart/line.png b/superx/saiku/images/chart/line.png deleted file mode 100644 index be6a5c5..0000000 Binary files a/superx/saiku/images/chart/line.png and /dev/null differ diff --git a/superx/saiku/images/chart/multiple.png b/superx/saiku/images/chart/multiple.png deleted file mode 100644 index c89e6eb..0000000 Binary files a/superx/saiku/images/chart/multiple.png and /dev/null differ diff --git a/superx/saiku/images/chart/multiplesunburst.png b/superx/saiku/images/chart/multiplesunburst.png deleted file mode 100644 index 898d44d..0000000 Binary files a/superx/saiku/images/chart/multiplesunburst.png and /dev/null differ diff --git a/superx/saiku/images/chart/pie.png b/superx/saiku/images/chart/pie.png deleted file mode 100644 index 9a933e7..0000000 Binary files a/superx/saiku/images/chart/pie.png and /dev/null differ diff --git a/superx/saiku/images/chart/stackedbar.png b/superx/saiku/images/chart/stackedbar.png deleted file mode 100644 index d31590c..0000000 Binary files a/superx/saiku/images/chart/stackedbar.png and /dev/null differ diff --git a/superx/saiku/images/chart/sunburst.png b/superx/saiku/images/chart/sunburst.png deleted file mode 100644 index 71e26b8..0000000 Binary files a/superx/saiku/images/chart/sunburst.png and /dev/null differ diff --git a/superx/saiku/images/chart/treemap.png b/superx/saiku/images/chart/treemap.png deleted file mode 100644 index 9e32238..0000000 Binary files a/superx/saiku/images/chart/treemap.png and /dev/null differ diff --git a/superx/saiku/images/chart/trendbars.png b/superx/saiku/images/chart/trendbars.png deleted file mode 100644 index 4d7ade9..0000000 Binary files a/superx/saiku/images/chart/trendbars.png and /dev/null differ diff --git a/superx/saiku/images/chart/trendline.png b/superx/saiku/images/chart/trendline.png deleted file mode 100644 index 677311f..0000000 Binary files a/superx/saiku/images/chart/trendline.png and /dev/null differ diff --git a/superx/saiku/images/chart/waterfall.png b/superx/saiku/images/chart/waterfall.png deleted file mode 100644 index 49a2e3c..0000000 Binary files a/superx/saiku/images/chart/waterfall.png and /dev/null differ diff --git a/superx/saiku/images/hisinone_schriftzug_portal.gif b/superx/saiku/images/hisinone_schriftzug_portal.gif deleted file mode 100644 index a368b64..0000000 Binary files a/superx/saiku/images/hisinone_schriftzug_portal.gif and /dev/null differ diff --git a/superx/saiku/images/sprite.png b/superx/saiku/images/sprite.png deleted file mode 100644 index 9aabcc6..0000000 Binary files a/superx/saiku/images/sprite.png and /dev/null differ diff --git a/superx/saiku/images/src/README b/superx/saiku/images/src/README deleted file mode 100644 index bc05d31..0000000 --- a/superx/saiku/images/src/README +++ /dev/null @@ -1,3 +0,0 @@ -http://blog.twg.ca/2009/09/free-iphone-toolbar-icons/ -http://www.famfamfam.com/lab/icons/silk/ -http://www.gentleface.com/free_icon_set.html \ No newline at end of file diff --git a/superx/saiku/images/src/admin.png b/superx/saiku/images/src/admin.png deleted file mode 100644 index f75697a..0000000 Binary files a/superx/saiku/images/src/admin.png and /dev/null differ diff --git a/superx/saiku/images/src/admin_console.png b/superx/saiku/images/src/admin_console.png deleted file mode 100644 index ad890c1..0000000 Binary files a/superx/saiku/images/src/admin_console.png and /dev/null differ diff --git a/superx/saiku/images/src/arrow_in.png b/superx/saiku/images/src/arrow_in.png deleted file mode 100644 index 7ffa485..0000000 Binary files a/superx/saiku/images/src/arrow_in.png and /dev/null differ diff --git a/superx/saiku/images/src/arrow_refresh.png b/superx/saiku/images/src/arrow_refresh.png deleted file mode 100644 index 0de2656..0000000 Binary files a/superx/saiku/images/src/arrow_refresh.png and /dev/null differ diff --git a/superx/saiku/images/src/arrow_refresh_bnw.png b/superx/saiku/images/src/arrow_refresh_bnw.png deleted file mode 100644 index e4b83d7..0000000 Binary files a/superx/saiku/images/src/arrow_refresh_bnw.png and /dev/null differ diff --git a/superx/saiku/images/src/arrow_up_bnw.png b/superx/saiku/images/src/arrow_up_bnw.png deleted file mode 100644 index 7117bf0..0000000 Binary files a/superx/saiku/images/src/arrow_up_bnw.png and /dev/null differ diff --git a/superx/saiku/images/src/auto.png b/superx/saiku/images/src/auto.png deleted file mode 100644 index 5e2a566..0000000 Binary files a/superx/saiku/images/src/auto.png and /dev/null differ diff --git a/superx/saiku/images/src/bullet_blue.png b/superx/saiku/images/src/bullet_blue.png deleted file mode 100644 index f1c6c72..0000000 Binary files a/superx/saiku/images/src/bullet_blue.png and /dev/null differ diff --git a/superx/saiku/images/src/bullet_green.png b/superx/saiku/images/src/bullet_green.png deleted file mode 100644 index 058ad26..0000000 Binary files a/superx/saiku/images/src/bullet_green.png and /dev/null differ diff --git a/superx/saiku/images/src/bullet_red.png b/superx/saiku/images/src/bullet_red.png deleted file mode 100644 index 0cd8031..0000000 Binary files a/superx/saiku/images/src/bullet_red.png and /dev/null differ diff --git a/superx/saiku/images/src/button_blank.png b/superx/saiku/images/src/button_blank.png deleted file mode 100644 index 66e2336..0000000 Binary files a/superx/saiku/images/src/button_blank.png and /dev/null differ diff --git a/superx/saiku/images/src/button_cog_off.png b/superx/saiku/images/src/button_cog_off.png deleted file mode 100644 index c74153f..0000000 Binary files a/superx/saiku/images/src/button_cog_off.png and /dev/null differ diff --git a/superx/saiku/images/src/button_cog_on.png b/superx/saiku/images/src/button_cog_on.png deleted file mode 100644 index fa925ad..0000000 Binary files a/superx/saiku/images/src/button_cog_on.png and /dev/null differ diff --git a/superx/saiku/images/src/button_help.png b/superx/saiku/images/src/button_help.png deleted file mode 100644 index 7060092..0000000 Binary files a/superx/saiku/images/src/button_help.png and /dev/null differ diff --git a/superx/saiku/images/src/button_pdf.png b/superx/saiku/images/src/button_pdf.png deleted file mode 100644 index 8f8095e..0000000 Binary files a/superx/saiku/images/src/button_pdf.png and /dev/null differ diff --git a/superx/saiku/images/src/cancel3.png b/superx/saiku/images/src/cancel3.png deleted file mode 100644 index c719957..0000000 Binary files a/superx/saiku/images/src/cancel3.png and /dev/null differ diff --git a/superx/saiku/images/src/ccc3.png b/superx/saiku/images/src/ccc3.png deleted file mode 100644 index dc6c9c8..0000000 Binary files a/superx/saiku/images/src/ccc3.png and /dev/null differ diff --git a/superx/saiku/images/src/context_down.png b/superx/saiku/images/src/context_down.png deleted file mode 100644 index 9c5f7fa..0000000 Binary files a/superx/saiku/images/src/context_down.png and /dev/null differ diff --git a/superx/saiku/images/src/csv.png b/superx/saiku/images/src/csv.png deleted file mode 100644 index ab808fe..0000000 Binary files a/superx/saiku/images/src/csv.png and /dev/null differ diff --git a/superx/saiku/images/src/delete.png b/superx/saiku/images/src/delete.png deleted file mode 100644 index 08f2493..0000000 Binary files a/superx/saiku/images/src/delete.png and /dev/null differ diff --git a/superx/saiku/images/src/dialog_login.png b/superx/saiku/images/src/dialog_login.png deleted file mode 100644 index d548911..0000000 Binary files a/superx/saiku/images/src/dialog_login.png and /dev/null differ diff --git a/superx/saiku/images/src/dialog_login_red.png b/superx/saiku/images/src/dialog_login_red.png deleted file mode 100644 index 23cace5..0000000 Binary files a/superx/saiku/images/src/dialog_login_red.png and /dev/null differ diff --git a/superx/saiku/images/src/drillthrough.png b/superx/saiku/images/src/drillthrough.png deleted file mode 100644 index 17631af..0000000 Binary files a/superx/saiku/images/src/drillthrough.png and /dev/null differ diff --git a/superx/saiku/images/src/excel.png b/superx/saiku/images/src/excel.png deleted file mode 100644 index b977d7e..0000000 Binary files a/superx/saiku/images/src/excel.png and /dev/null differ diff --git a/superx/saiku/images/src/export_drillthrough.png b/superx/saiku/images/src/export_drillthrough.png deleted file mode 100644 index 3dcf87c..0000000 Binary files a/superx/saiku/images/src/export_drillthrough.png and /dev/null differ diff --git a/superx/saiku/images/src/fields.png b/superx/saiku/images/src/fields.png deleted file mode 100644 index 1c05cad..0000000 Binary files a/superx/saiku/images/src/fields.png and /dev/null differ diff --git a/superx/saiku/images/src/filter.png b/superx/saiku/images/src/filter.png deleted file mode 100644 index 9c96d69..0000000 Binary files a/superx/saiku/images/src/filter.png and /dev/null differ diff --git a/superx/saiku/images/src/folder.png b/superx/saiku/images/src/folder.png deleted file mode 100644 index 784e8fa..0000000 Binary files a/superx/saiku/images/src/folder.png and /dev/null differ diff --git a/superx/saiku/images/src/folder_add.png b/superx/saiku/images/src/folder_add.png deleted file mode 100644 index 529fe8f..0000000 Binary files a/superx/saiku/images/src/folder_add.png and /dev/null differ diff --git a/superx/saiku/images/src/folder_edit.png b/superx/saiku/images/src/folder_edit.png deleted file mode 100644 index ad669cc..0000000 Binary files a/superx/saiku/images/src/folder_edit.png and /dev/null differ diff --git a/superx/saiku/images/src/group_edit.png b/superx/saiku/images/src/group_edit.png deleted file mode 100644 index c88b945..0000000 Binary files a/superx/saiku/images/src/group_edit.png and /dev/null differ diff --git a/superx/saiku/images/src/help.png b/superx/saiku/images/src/help.png deleted file mode 100644 index 5c87017..0000000 Binary files a/superx/saiku/images/src/help.png and /dev/null differ diff --git a/superx/saiku/images/src/hide_parents.png b/superx/saiku/images/src/hide_parents.png deleted file mode 100644 index 88d2f2f..0000000 Binary files a/superx/saiku/images/src/hide_parents.png and /dev/null differ diff --git a/superx/saiku/images/src/information.png b/superx/saiku/images/src/information.png deleted file mode 100644 index 12cd1ae..0000000 Binary files a/superx/saiku/images/src/information.png and /dev/null differ diff --git a/superx/saiku/images/src/logo.png b/superx/saiku/images/src/logo.png deleted file mode 100644 index 158a4b3..0000000 Binary files a/superx/saiku/images/src/logo.png and /dev/null differ diff --git a/superx/saiku/images/src/logo_red.png b/superx/saiku/images/src/logo_red.png deleted file mode 100644 index d008efb..0000000 Binary files a/superx/saiku/images/src/logo_red.png and /dev/null differ diff --git a/superx/saiku/images/src/mac.png b/superx/saiku/images/src/mac.png deleted file mode 100644 index 29cdeec..0000000 Binary files a/superx/saiku/images/src/mac.png and /dev/null differ diff --git a/superx/saiku/images/src/main_toolbar_bug.png b/superx/saiku/images/src/main_toolbar_bug.png deleted file mode 100644 index 45ce255..0000000 Binary files a/superx/saiku/images/src/main_toolbar_bug.png and /dev/null differ diff --git a/superx/saiku/images/src/main_toolbar_doc_plus.png b/superx/saiku/images/src/main_toolbar_doc_plus.png deleted file mode 100644 index 7fb4ef9..0000000 Binary files a/superx/saiku/images/src/main_toolbar_doc_plus.png and /dev/null differ diff --git a/superx/saiku/images/src/main_toolbar_folder.png b/superx/saiku/images/src/main_toolbar_folder.png deleted file mode 100644 index 3c63a00..0000000 Binary files a/superx/saiku/images/src/main_toolbar_folder.png and /dev/null differ diff --git a/superx/saiku/images/src/main_toolbar_info.png b/superx/saiku/images/src/main_toolbar_info.png deleted file mode 100644 index 845ac86..0000000 Binary files a/superx/saiku/images/src/main_toolbar_info.png and /dev/null differ diff --git a/superx/saiku/images/src/main_toolbar_on_off.png b/superx/saiku/images/src/main_toolbar_on_off.png deleted file mode 100644 index bd5997c..0000000 Binary files a/superx/saiku/images/src/main_toolbar_on_off.png and /dev/null differ diff --git a/superx/saiku/images/src/main_toolbar_schema_designer.png b/superx/saiku/images/src/main_toolbar_schema_designer.png deleted file mode 100644 index f91353f..0000000 Binary files a/superx/saiku/images/src/main_toolbar_schema_designer.png and /dev/null differ diff --git a/superx/saiku/images/src/mdx.png b/superx/saiku/images/src/mdx.png deleted file mode 100644 index d6ca594..0000000 Binary files a/superx/saiku/images/src/mdx.png and /dev/null differ diff --git a/superx/saiku/images/src/meteorite_free.png b/superx/saiku/images/src/meteorite_free.png deleted file mode 100644 index 3dac244..0000000 Binary files a/superx/saiku/images/src/meteorite_free.png and /dev/null differ diff --git a/superx/saiku/images/src/non.png b/superx/saiku/images/src/non.png deleted file mode 100644 index 8caff58..0000000 Binary files a/superx/saiku/images/src/non.png and /dev/null differ diff --git a/superx/saiku/images/src/option_off.png b/superx/saiku/images/src/option_off.png deleted file mode 100644 index 93f32f0..0000000 Binary files a/superx/saiku/images/src/option_off.png and /dev/null differ diff --git a/superx/saiku/images/src/option_on.png b/superx/saiku/images/src/option_on.png deleted file mode 100644 index 0db3b24..0000000 Binary files a/superx/saiku/images/src/option_on.png and /dev/null differ diff --git a/superx/saiku/images/src/page_white.png b/superx/saiku/images/src/page_white.png deleted file mode 100644 index 8b8b1ca..0000000 Binary files a/superx/saiku/images/src/page_white.png and /dev/null differ diff --git a/superx/saiku/images/src/page_white_compressed.png b/superx/saiku/images/src/page_white_compressed.png deleted file mode 100644 index 2b6b100..0000000 Binary files a/superx/saiku/images/src/page_white_compressed.png and /dev/null differ diff --git a/superx/saiku/images/src/pencil.png b/superx/saiku/images/src/pencil.png deleted file mode 100644 index 0bfecd5..0000000 Binary files a/superx/saiku/images/src/pencil.png and /dev/null differ diff --git a/superx/saiku/images/src/query_scenario.png b/superx/saiku/images/src/query_scenario.png deleted file mode 100644 index 044b9ed..0000000 Binary files a/superx/saiku/images/src/query_scenario.png and /dev/null differ diff --git a/superx/saiku/images/src/run.png b/superx/saiku/images/src/run.png deleted file mode 100644 index 06215b5..0000000 Binary files a/superx/saiku/images/src/run.png and /dev/null differ diff --git a/superx/saiku/images/src/saiku.png b/superx/saiku/images/src/saiku.png deleted file mode 100644 index a544fba..0000000 Binary files a/superx/saiku/images/src/saiku.png and /dev/null differ diff --git a/superx/saiku/images/src/saiku_120.png b/superx/saiku/images/src/saiku_120.png deleted file mode 100644 index fc5fdb3..0000000 Binary files a/superx/saiku/images/src/saiku_120.png and /dev/null differ diff --git a/superx/saiku/images/src/saiku_16.png b/superx/saiku/images/src/saiku_16.png deleted file mode 100644 index 22a1c70..0000000 Binary files a/superx/saiku/images/src/saiku_16.png and /dev/null differ diff --git a/superx/saiku/images/src/saiku_16x16.png b/superx/saiku/images/src/saiku_16x16.png deleted file mode 100644 index a0ce2bf..0000000 Binary files a/superx/saiku/images/src/saiku_16x16.png and /dev/null differ diff --git a/superx/saiku/images/src/saiku_32.png b/superx/saiku/images/src/saiku_32.png deleted file mode 100644 index 7b92466..0000000 Binary files a/superx/saiku/images/src/saiku_32.png and /dev/null differ diff --git a/superx/saiku/images/src/saiku_32x32.png b/superx/saiku/images/src/saiku_32x32.png deleted file mode 100644 index bd81fc3..0000000 Binary files a/superx/saiku/images/src/saiku_32x32.png and /dev/null differ diff --git a/superx/saiku/images/src/saikux2.png b/superx/saiku/images/src/saikux2.png deleted file mode 100644 index 40e96b8..0000000 Binary files a/superx/saiku/images/src/saikux2.png and /dev/null differ diff --git a/superx/saiku/images/src/save.png b/superx/saiku/images/src/save.png deleted file mode 100644 index 99d532e..0000000 Binary files a/superx/saiku/images/src/save.png and /dev/null differ diff --git a/superx/saiku/images/src/sort_down.png b/superx/saiku/images/src/sort_down.png deleted file mode 100644 index 625e7de..0000000 Binary files a/superx/saiku/images/src/sort_down.png and /dev/null differ diff --git a/superx/saiku/images/src/sort_none.png b/superx/saiku/images/src/sort_none.png deleted file mode 100644 index ec7dc4f..0000000 Binary files a/superx/saiku/images/src/sort_none.png and /dev/null differ diff --git a/superx/saiku/images/src/sort_up.png b/superx/saiku/images/src/sort_up.png deleted file mode 100644 index 2048cb6..0000000 Binary files a/superx/saiku/images/src/sort_up.png and /dev/null differ diff --git a/superx/saiku/images/src/swap.png b/superx/saiku/images/src/swap.png deleted file mode 100644 index 1f489d9..0000000 Binary files a/superx/saiku/images/src/swap.png and /dev/null differ diff --git a/superx/saiku/images/src/switch_to_mdx.png b/superx/saiku/images/src/switch_to_mdx.png deleted file mode 100644 index 56fa382..0000000 Binary files a/superx/saiku/images/src/switch_to_mdx.png and /dev/null differ diff --git a/superx/saiku/images/src/table.png b/superx/saiku/images/src/table.png deleted file mode 100644 index 00adef2..0000000 Binary files a/superx/saiku/images/src/table.png and /dev/null differ diff --git a/superx/saiku/images/src/table2.png b/superx/saiku/images/src/table2.png deleted file mode 100644 index cf43279..0000000 Binary files a/superx/saiku/images/src/table2.png and /dev/null differ diff --git a/superx/saiku/images/src/table_across.png b/superx/saiku/images/src/table_across.png deleted file mode 100644 index 9c0e3b3..0000000 Binary files a/superx/saiku/images/src/table_across.png and /dev/null differ diff --git a/superx/saiku/images/src/tabs_cross_off.png b/superx/saiku/images/src/tabs_cross_off.png deleted file mode 100644 index 580e303..0000000 Binary files a/superx/saiku/images/src/tabs_cross_off.png and /dev/null differ diff --git a/superx/saiku/images/src/tabs_cross_on.png b/superx/saiku/images/src/tabs_cross_on.png deleted file mode 100644 index 1408d01..0000000 Binary files a/superx/saiku/images/src/tabs_cross_on.png and /dev/null differ diff --git a/superx/saiku/images/src/toolbar_delete.png b/superx/saiku/images/src/toolbar_delete.png deleted file mode 100644 index 58c1dce..0000000 Binary files a/superx/saiku/images/src/toolbar_delete.png and /dev/null differ diff --git a/superx/saiku/images/src/toolbar_error.png b/superx/saiku/images/src/toolbar_error.png deleted file mode 100644 index 8b4b4c1..0000000 Binary files a/superx/saiku/images/src/toolbar_error.png and /dev/null differ diff --git a/superx/saiku/images/src/toolbar_help.png b/superx/saiku/images/src/toolbar_help.png deleted file mode 100644 index 7060092..0000000 Binary files a/superx/saiku/images/src/toolbar_help.png and /dev/null differ diff --git a/superx/saiku/images/src/toolbar_info.png b/superx/saiku/images/src/toolbar_info.png deleted file mode 100644 index 33140ef..0000000 Binary files a/superx/saiku/images/src/toolbar_info.png and /dev/null differ diff --git a/superx/saiku/images/src/toolbar_logout.png b/superx/saiku/images/src/toolbar_logout.png deleted file mode 100644 index 4194f03..0000000 Binary files a/superx/saiku/images/src/toolbar_logout.png and /dev/null differ diff --git a/superx/saiku/images/src/toolbar_message.png b/superx/saiku/images/src/toolbar_message.png deleted file mode 100644 index de68fef..0000000 Binary files a/superx/saiku/images/src/toolbar_message.png and /dev/null differ diff --git a/superx/saiku/images/src/toolbar_new.png b/superx/saiku/images/src/toolbar_new.png deleted file mode 100644 index b71c8fc..0000000 Binary files a/superx/saiku/images/src/toolbar_new.png and /dev/null differ diff --git a/superx/saiku/images/src/toolbar_open.png b/superx/saiku/images/src/toolbar_open.png deleted file mode 100644 index 302b6ae..0000000 Binary files a/superx/saiku/images/src/toolbar_open.png and /dev/null differ diff --git a/superx/saiku/images/src/toolbar_save.png b/superx/saiku/images/src/toolbar_save.png deleted file mode 100644 index f184706..0000000 Binary files a/superx/saiku/images/src/toolbar_save.png and /dev/null differ diff --git a/superx/saiku/images/src/tree_collapsed.png b/superx/saiku/images/src/tree_collapsed.png deleted file mode 100644 index d727141..0000000 Binary files a/superx/saiku/images/src/tree_collapsed.png and /dev/null differ diff --git a/superx/saiku/images/src/tree_dimension.png b/superx/saiku/images/src/tree_dimension.png deleted file mode 100644 index 552f7c6..0000000 Binary files a/superx/saiku/images/src/tree_dimension.png and /dev/null differ diff --git a/superx/saiku/images/src/tree_expand.png b/superx/saiku/images/src/tree_expand.png deleted file mode 100644 index b9d57a7..0000000 Binary files a/superx/saiku/images/src/tree_expand.png and /dev/null differ diff --git a/superx/saiku/images/src/tree_folder_collapsed.png b/superx/saiku/images/src/tree_folder_collapsed.png deleted file mode 100644 index 988151f..0000000 Binary files a/superx/saiku/images/src/tree_folder_collapsed.png and /dev/null differ diff --git a/superx/saiku/images/src/tree_folder_expand.png b/superx/saiku/images/src/tree_folder_expand.png deleted file mode 100644 index b9576b4..0000000 Binary files a/superx/saiku/images/src/tree_folder_expand.png and /dev/null differ diff --git a/superx/saiku/images/src/tree_measure.png b/superx/saiku/images/src/tree_measure.png deleted file mode 100644 index 42fda84..0000000 Binary files a/superx/saiku/images/src/tree_measure.png and /dev/null differ diff --git a/superx/saiku/images/src/waiting.gif b/superx/saiku/images/src/waiting.gif deleted file mode 100644 index c91e84f..0000000 Binary files a/superx/saiku/images/src/waiting.gif and /dev/null differ diff --git a/superx/saiku/images/src/zoom.png b/superx/saiku/images/src/zoom.png deleted file mode 100644 index 908612e..0000000 Binary files a/superx/saiku/images/src/zoom.png and /dev/null differ diff --git a/superx/saiku/index.html b/superx/saiku/index.html deleted file mode 100644 index 0cd6fe1..0000000 --- a/superx/saiku/index.html +++ /dev/null @@ -1,987 +0,0 @@ - - - - - - - - - - - - Saiku - Next Generation Open Source Analytics - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-   - Loading... -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/superx/saiku/index_superx.html b/superx/saiku/index_superx.html deleted file mode 100644 index 5f11108..0000000 --- a/superx/saiku/index_superx.html +++ /dev/null @@ -1,989 +0,0 @@ - - - - - - - - - - - - - Saiku - Next Generation Open Source Analytics - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-   - Loading... -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/superx/saiku/js/ace/ace.js b/superx/saiku/js/ace/ace.js deleted file mode 100644 index f4237a0..0000000 --- a/superx/saiku/js/ace/ace.js +++ /dev/null @@ -1,11 +0,0 @@ -(function(){function o(e){var i=function(e,t){return r("",e,t)},s=t;e&&(t[e]||(t[e]={}),s=t[e]);if(!s.define||!s.define.packaged)n.original=s.define,s.define=n,s.define.packaged=!0;if(!s.require||!s.require.packaged)r.original=s.require,s.require=i,s.require.packaged=!0}var e="",t=function(){return this}();if(!e&&typeof requirejs!="undefined")return;var n=function(e,t,r){if(typeof e!="string"){n.original?n.original.apply(window,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=t),n.modules||(n.modules={},n.payloads={}),n.payloads[e]=r,n.modules[e]=null},r=function(e,t,n){if(Object.prototype.toString.call(t)==="[object Array]"){var i=[];for(var o=0,u=t.length;o1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;et.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+ta)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var n=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;n&&(o=1),o==1&&(u=e.clientX,a=e.clientY)}r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}),i.isOldIE&&t.addListener(e,"dblclick",function(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)})};var u=null,a=0;t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var s=null;r(e,"keydown",function(e){s=e.keyCode}),r(e,"keypress",function(e){return o(n,e,s)})}else{var a=null;r(e,"keydown",function(e){u[e.keyCode]=!0;var t=o(n,e,e.keyCode);return a=e.defaultPrevented,t}),r(e,"keypress",function(e){a&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),a=null)}),r(e,"keyup",function(e){u[e.keyCode]=null}),u||(u=Object.create(null),r(window,"focus",function(e){u=Object.create(null)}))}};if(window.postMessage&&!i.isOldIE){var f=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+f;t.addListener(n,"message",function i(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())}),n.postMessage(r,"*")}}t.nextFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame,t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(e,t,n){var r=e("./oop"),i=function(){var e={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,meta:8,command:8,cmd:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9","-13":"NumpadEnter",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",188:",",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"}};for(var t in e.FUNCTION_KEYS){var n=e.FUNCTION_KEYS[t].toLowerCase();e[n]=parseInt(t,10)}return r.mixin(e,e.MODIFIER_KEYS),r.mixin(e,e.PRINTABLE_KEYS),r.mixin(e,e.FUNCTION_KEYS),e.enter=e["return"],e.escape=e.esc,e.del=e["delete"],e[173]="-",e}();r.mixin(t,i),t.keyCodeToString=function(e){return(i[e]||String.fromCharCode(e)).toLowerCase()}}),define("ace/lib/oop",["require","exports","module"],function(e,t,n){t.inherits=function(){var e=function(){};return function(t,n){e.prototype=n.prototype,t.super_=n.prototype,t.prototype=new e,t.prototype.constructor=t}}(),t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),define("ace/lib/useragent",["require","exports","module"],function(e,t,n){t.OS={LINUX:"LINUX",MAC:"MAC",WINDOWS:"WINDOWS"},t.getOS=function(){return t.isMac?t.OS.MAC:t.isLinux?t.OS.LINUX:t.OS.WINDOWS};if(typeof navigator!="object")return;var r=(navigator.platform.match(/mac|win|linux/i)||["other"])[0].toLowerCase(),i=navigator.userAgent;t.isWin=r=="win",t.isMac=r=="mac",t.isLinux=r=="linux",t.isIE=(navigator.appName=="Microsoft Internet Explorer"||navigator.appName.indexOf("MSAppHost")>=0)&&parseFloat(navigator.userAgent.match(/MSIE ([0-9]+[\.0-9]+)/)[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=window.controllers&&window.navigator.product==="Gecko",t.isOldGecko=t.isGecko&&parseInt((navigator.userAgent.match(/rv\:(\d+)/)||[])[1],10)<4,t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isTouchPad=i.indexOf("TouchPad")>=0}),define("ace/editor",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/keyboard/textinput","ace/mouse/mouse_handler","ace/mouse/fold_handler","ace/keyboard/keybinding","ace/edit_session","ace/search","ace/range","ace/lib/event_emitter","ace/commands/command_manager","ace/commands/default_commands","ace/config"],function(e,t,n){e("./lib/fixoldbrowsers");var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/lang"),o=e("./lib/useragent"),u=e("./keyboard/textinput").TextInput,a=e("./mouse/mouse_handler").MouseHandler,f=e("./mouse/fold_handler").FoldHandler,l=e("./keyboard/keybinding").KeyBinding,c=e("./edit_session").EditSession,h=e("./search").Search,p=e("./range").Range,d=e("./lib/event_emitter").EventEmitter,v=e("./commands/command_manager").CommandManager,m=e("./commands/default_commands").commands,g=e("./config"),y=function(e,t){var n=e.getContainerElement();this.container=n,this.renderer=e,this.commands=new v(o.isMac?"mac":"win",m),this.textInput=new u(e.getTextAreaContainer(),this),this.renderer.textarea=this.textInput.getElement(),this.keyBinding=new l(this),this.$mouseHandler=new a(this),new f(this),this.$blockScrolling=0,this.$search=(new h).set({wrap:!0}),this.$historyTracker=this.$historyTracker.bind(this),this.commands.on("exec",this.$historyTracker),this.$initOperationListeners(),this._$emitInputEvent=s.delayedCall(function(){this._signal("input",{}),this.session.bgTokenizer&&this.session.bgTokenizer.scheduleStart()}.bind(this)),this.on("change",function(e,t){t._$emitInputEvent.schedule(31)}),this.setSession(t||new c("")),g.resetOptions(this),g._emit("editor",this)};(function(){r.implement(this,d),this.$initOperationListeners=function(){function e(e){return e[e.length-1]}this.selections=[],this.commands.on("exec",function(t){this.startOperation(t);var n=t.command;if(n.group=="fileJump"){var r=this.prevOp;if(!r||r.command.group!="fileJump")this.lastFileJumpPos=e(this.selections)}else this.lastFileJumpPos=null}.bind(this),!0),this.commands.on("afterExec",function(e){var t=e.command;if(t.group=="fileJump"&&this.lastFileJumpPos&&!this.curOp.selectionChanged){this.selection.fromJSON(this.lastFileJumpPos);return}this.endOperation(e)}.bind(this),!0),this.$opResetTimer=s.delayedCall(this.endOperation.bind(this)),this.on("change",function(){this.curOp||this.startOperation(),this.curOp.docChanged=!0}.bind(this),!0),this.on("changeSelection",function(){this.curOp||this.startOperation(),this.curOp.selectionChanged=!0}.bind(this),!0)},this.curOp=null,this.prevOp={},this.startOperation=function(e){if(this.curOp){if(!e||this.curOp.command)return;this.prevOp=this.curOp}e||(this.previousCommand=null,e={}),this.$opResetTimer.schedule(),this.curOp={command:e.command||{},args:e.args},this.selections.push(this.selection.toJSON())},this.endOperation=function(){this.curOp&&(this.prevOp=this.curOp,this.curOp=null)},this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=["backspace","del","insertstring"],r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e){if(!e)this.keyBinding.setKeyboardHandler(null);else if(typeof e=="string"){this.$keybindingId=e;var t=this;g.loadModule(["keybinding",e],function(n){t.$keybindingId==e&&t.keyBinding.setKeyboardHandler(n&&n.handler)})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e)},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;if(this.session){var t=this.session;this.session.removeEventListener("change",this.$onDocumentChange),this.session.removeEventListener("changeMode",this.$onChangeMode),this.session.removeEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.session.removeEventListener("changeTabSize",this.$onChangeTabSize),this.session.removeEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.session.removeEventListener("changeWrapMode",this.$onChangeWrapMode),this.session.removeEventListener("onChangeFold",this.$onChangeFold),this.session.removeEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.session.removeEventListener("changeBackMarker",this.$onChangeBackMarker),this.session.removeEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.session.removeEventListener("changeAnnotation",this.$onChangeAnnotation),this.session.removeEventListener("changeOverwrite",this.$onCursorChange),this.session.removeEventListener("changeScrollTop",this.$onScrollTopChange),this.session.removeEventListener("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.removeEventListener("changeCursor",this.$onCursorChange),n.removeEventListener("changeSelection",this.$onSelectionChange)}this.session=e,this.$onDocumentChange=this.onDocumentChange.bind(this),e.addEventListener("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.addEventListener("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.addEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.addEventListener("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.addEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.addEventListener("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.addEventListener("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.addEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.addEventListener("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.addEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.addEventListener("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.addEventListener("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.addEventListener("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.addEventListener("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.addEventListener("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.addEventListener("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull(),this._emit("changeSession",{session:e,oldSession:t})},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e){this.renderer.setTheme(e)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container,"fontSize")},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session.findMatchingBracket(e.getCursorPosition());if(t)var n=new p(t.row,t.column,t.row,t.column+1);else if(e.session.$mode.getMatching)var n=e.session.$mode.getMatching(e.session);n&&(e.session.$bracketHighlight=e.session.addMarker(n,"ace_bracket","text"))},50)},this.focus=function(){var e=this;setTimeout(function(){e.textInput.focus()}),this.textInput.focus()},this.isFocused=function(){return this.textInput.isFocused()},this.blur=function(){this.textInput.blur()},this.onFocus=function(){if(this.$isFocused)return;this.$isFocused=!0,this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit("focus")},this.onBlur=function(){if(!this.$isFocused)return;this.$isFocused=!1,this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit("blur")},this.$cursorChange=function(){this.renderer.updateCursor()},this.onDocumentChange=function(e){var t=e.data,n=t.range,r;n.start.row==n.end.row&&t.action!="insertLines"&&t.action!="removeLines"?r=n.end.row:r=Infinity,this.renderer.updateLines(n.start.row,r),this._emit("change",e),this.$cursorChange()},this.onTokenizerUpdate=function(e){var t=e.data;this.renderer.updateLines(t.first,t.last)},this.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},this.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},this.onCursorChange=function(){this.$cursorChange(),this.$blockScrolling||this.renderer.scrollCursorIntoView(),this.$highlightBrackets(),this.$updateHighlightActiveLine(),this._emit("changeSelection")},this.$updateHighlightActiveLine=function(){var e=this.getSession(),t;if(this.$highlightActiveLine){if(this.$selectionStyle!="line"||!this.selection.isMultiLine())t=this.getCursorPosition();this.renderer.$maxLines&&this.session.getLength()===1&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._emit("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._emit("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column-1,r=t.end.column+1,i=e.getLine(t.start.row),s=i.length,o=i.substring(Math.max(n,0),Math.min(r,s));if(n>=0&&/^[\w\d]/.test(o)||r<=s&&/[\w\d]$/.test(o))return;o=i.substring(t.start.column,t.end.column);if(!/^[\w\d]+$/.test(o))return;var u=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:o});return u},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText();return this._signal("copy",e),e},this.onCopy=function(){this.commands.exec("copy",this)},this.onCut=function(){this.commands.exec("cut",this)},this.onPaste=function(e){if(this.$readOnly)return;this._emit("paste",e),this.insert(e)},this.execCommand=function(e,t){this.commands.exec(e,this,t)},this.insert=function(e){var t=this.session,n=t.getMode(),r=this.getCursorPosition();if(this.getBehavioursEnabled()){var i=n.transformAction(t.getState(r.row),"insertion",this,t,e);i&&(e!==i.text&&(this.session.mergeUndoDeltas=!1,this.$mergeNextCommand=!1),e=i.text)}e==" "&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var s=this.getSelectionRange();r=this.session.remove(s),this.clearSelection()}else if(this.session.getOverwrite()){var s=new p.fromPoints(r,r);s.end.column+=e.length,this.session.remove(s)}if(e=="\n"||e=="\r\n"){var o=t.getLine(r.row);if(r.column>o.search(/\S|$/)){var u=o.substr(r.column).search(/\S|$/);t.doc.removeInLine(r.row,r.column,r.column+u)}}this.clearSelection();var a=r.column,f=t.getState(r.row),o=t.getLine(r.row),l=n.checkOutdent(f,o,e),c=t.insert(r,e);i&&i.selection&&(i.selection.length==2?this.selection.setSelectionRange(new p(r.row,a+i.selection[0],r.row,a+i.selection[1])):this.selection.setSelectionRange(new p(r.row+i.selection[0],i.selection[1],r.row+i.selection[2],i.selection[3])));if(t.getDocument().isNewLine(e)){var h=n.getNextLineIndent(f,o.slice(0,r.column),t.getTabString());t.insert({row:r.row+1,column:0},h)}l&&n.autoOutdent(f,t,r.row)},this.onTextInput=function(e){this.keyBinding.onTextInput(e)},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column==0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var r=new p(0,0,0,0);for(var i=e.first;i<=e.last;i++){var s=t.getLine(i);r.start.row=i,r.end.row=i,r.end.column=s.length,t.replace(r,n[i-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n=o)s[u].moveBy(i,0),u--}t.fromOrientedRange(t.ranges[0]),t.rangeList.attach(this.session)}},this.$getSelectedRows=function(){var e=this.getSelectionRange().collapseRows();return{first:e.start.row,last:e.end.row}},this.onCompositionStart=function(e){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);this.$blockScrolling++,t==1?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t==0&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection()),this.$blockScrolling--;var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e){var t=this.getCursorPosition(),n=this.session.getBracketRange(t);if(!n){n=this.find({needle:/[{}()\[\]]/g,preventScroll:!0,start:{row:t.row,column:t.column-1}});if(!n)return;var r=n.start;r.row==t.row&&Math.abs(r.column-t.column)<2&&(n=this.session.getBracketRange(r))}r=n&&n.cursor||r,r&&(e?n&&n.isEqual(this.getSelectionRange())?this.clearSelection():this.selection.selectTo(r.row,r.column):(this.clearSelection(),this.moveCursorTo(r.row,r.column)))},this.gotoLine=function(e,t,n){this.selection.clearSelection(),this.session.unfold({row:e-1,column:t||0}),this.$blockScrolling+=1,this.exitMultiSelectMode&&this.exitMultiSelectMode(),this.moveCursorTo(e-1,t||0),this.$blockScrolling-=1,this.isRowFullyVisible(e-1)||this.scrollToLine(e-1,!0,n)},this.navigateTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.navigateUp=function(e){if(this.selection.isMultiLine()&&!this.selection.isBackwards()){var t=this.selection.anchor.getPosition();return this.moveCursorToPosition(t)}this.selection.clearSelection(),e=e||1,this.selection.moveCursorBy(-e,0)},this.navigateDown=function(e){if(this.selection.isMultiLine()&&this.selection.isBackwards()){var t=this.selection.anchor.getPosition();return this.moveCursorToPosition(t)}this.selection.clearSelection(),e=e||1,this.selection.moveCursorBy(e,0)},this.navigateLeft=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().start;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorLeft()}this.clearSelection()},this.navigateRight=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().end;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorRight()}this.clearSelection()},this.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},this.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},this.navigateFileEnd=function(){var e=this.renderer.scrollTop;this.selection.moveCursorFileEnd(),this.clearSelection(),this.renderer.animateScrolling(e)},this.navigateFileStart=function(){var e=this.renderer.scrollTop;this.selection.moveCursorFileStart(),this.clearSelection(),this.renderer.animateScrolling(e)},this.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},this.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},this.replace=function(e,t){t&&this.$search.set(t);var n=this.$search.find(this.session),r=0;return n?(this.$tryReplace(n,e)&&(r=1),n!==null&&(this.selection.setSelectionRange(n),this.renderer.scrollSelectionIntoView(n.start,n.end)),r):r},this.replaceAll=function(e,t){t&&this.$search.set(t);var n=this.$search.findAll(this.session),r=0;if(!n.length)return r;this.$blockScrolling+=1;var i=this.getSelectionRange();this.clearSelection(),this.selection.moveCursorTo(0,0);for(var s=n.length-1;s>=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),this.$blockScrolling-=1,r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.$blockScrolling+=1,this.session.unfold(e),this.selection.setSelectionRange(e),this.$blockScrolling-=1;var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!=0&&this.renderer.animateScrolling(n)},this.undo=function(){this.$blockScrolling++,this.session.getUndoManager().undo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.$blockScrolling++,this.session.getUndoManager().redo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._emit("destroy",this)},this.setAutoScrollEditorIntoView=function(e){if(e===!1)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&n.isFocused()){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e===!0)return;delete this.setAutoScrollEditorIntoView,this.removeEventListener("changeSelection",s),this.renderer.removeEventListener("afterRender",u),this.renderer.removeEventListener("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(e=="smooth"),t.isBlinking=!this.$readOnly&&e!="wide"}}).call(y.prototype),g.defineOptions(y.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._emit("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.textInput.setReadOnly(e),this.$resetCursorStyle()},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",showGutter:"renderer",displayIndentGuides:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimout:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",tabSize:"session",wrap:"session",foldStyle:"session"}),t.Editor=y}),define("ace/lib/lang",["require","exports","module"],function(e,t,n){t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){var n="";while(t>0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=f(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=a(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=(new Date).getTime();(e>o||t-this.mousedownEvent.time>this.$focusTimout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);if(i){i.isEmpty()&&(i.start.column--,i.end.column++),this.$clickSelection=i,this.setState("select");return}this.$clickSelection=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines"),this.$clickSelection=n.selection.getLineRange(t.row)},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getShiftKey()||e.getAccelKey())return;var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()}}).call(u.prototype),t.DefaultHandlers=u}),define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/event"],function(e,t,n){function s(e){function f(){u=r.createElement("div"),u.className="ace_gutter-tooltip",u.style.display="none",t.container.appendChild(u)}function l(){u||f();var e=o.getDocumentPosition().row,r=n.$annotations[e];if(!r)return c();var i=t.session.getLength();if(e==i){var s=t.renderer.pixelToScreenCoordinates(0,o.y).row,l=o.$pos;if(s>t.session.documentToScreenRow(l.row,l.column))return c()}if(a==r)return;a=r.text.join("
"),u.style.display="block",u.innerHTML=a,t.on("mousewheel",c),h(o)}function c(){s&&(s=clearTimeout(s)),a&&(u.style.display="none",a=null,t.removeEventListener("mousewheel",c))}function h(e){var n=t.renderer.$gutter.getBoundingClientRect();u.style.left=e.x+15+"px";if(e.y+3*t.renderer.lineHeight+15a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=(new Date).getTime(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.$blockScrolling+=1,t.selection.fromOrientedRange(m),t.$blockScrolling-=1,t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),m=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n._top=n.offsetTop),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle("ace_dragging")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return h||(k(),y++),A!==null&&(A=null),p=e.clientX,d=e.clientY,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!h)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=(new Date).getTime()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle("ace_dragging"),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor.container;e.draggable=!0,this.editor.renderer.$cursorLayer.setBlinking(!1),this.editor.setStyle("ace_dragging"),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){this.mousedownEvent.time=(new Date).getTime();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){self.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),define("ace/config",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/lib/net","ace/lib/event_emitter"],function(e,t,n){"no use strict";function f(e){return e.replace(/-(.)/g,function(e,t){return t.toUpperCase()})}var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./lib/net"),o=e("./lib/event_emitter").EventEmitter,u=function(){return this}(),a={packaged:!1,workerPath:null,modePath:null,themePath:null,basePath:"",suffix:".js",$moduleUrls:{}};t.get=function(e){if(!a.hasOwnProperty(e))throw new Error("Unknown config key: "+e);return a[e]},t.set=function(e,t){if(!a.hasOwnProperty(e))throw new Error("Unknown config key: "+e);a[e]=t},t.all=function(){return r.copyObject(a)},i.implement(t,o),t.moduleUrl=function(e,t){if(a.$moduleUrls[e])return a.$moduleUrls[e];var n=e.split("/");t=t||n[n.length-2]||"";var r=t=="snippets"?"/":"-",i=n[n.length-1];if(r=="-"){var s=new RegExp("^"+t+"[\\-_]|[\\-_]"+t+"$","g");i=i.replace(s,"")}(!i||i==t)&&n.length>1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a)},t.init=function(){a.packaged=e.packaged||n.packaged||u.define&&define.packaged;if(!u.document)return"";var r={},i="",s=document.getElementsByTagName("script");for(var o=0;o=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,o),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.$deltasFold.length&&(t.$deltas.push({group:"fold",deltas:t.$deltasFold}),t.$deltasFold=[]),t.$deltasDoc.length&&(t.$deltas.push({group:"doc",deltas:t.$deltasDoc}),t.$deltasDoc=[]),t.$deltas.length>0&&e.execute({action:"aceupdate",args:[t.$deltas,t],merge:t.mergeUndoDeltas}),t.mergeUndoDeltas=!1,t.$deltas=[]},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize==0},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._emit("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._emit("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oo){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=e.length-1;r!=-1;r--){var i=e[r];i.group=="doc"?(this.doc.revertDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!0,n)):i.deltas.forEach(function(e){this.addFolds(e.folds)},this)}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=0;re.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,l=s.start,o=l.row-a.row,u=l.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.insert({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new f(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new f(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeLines(e,t);return this.doc.insertLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=[];for(var n=0;n1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._emit("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n,r=e.data.action,i=e.data.range.start.row,s=e.data.range.end.row,o=e.data.range.start,u=e.data.range.end,a=null;r.indexOf("Lines")!=-1?(r=="insertLines"?s=i+e.data.lines.length:s=i,n=e.data.lines?e.data.lines.length:s-i):n=s-i,this.$updating=!0;if(n!=0)if(r.indexOf("remove")!=-1){this[t?"$wrapData":"$rowLengthCache"].splice(i,n);var f=this.$foldData;a=this.getFoldsInRange(e.data.range),this.removeFolds(a);var l=this.getFoldLine(u.row),c=0;if(l){l.addRemoveChars(u.row,u.column,o.column-u.column),l.shiftRow(-n);var h=this.getFoldLine(i);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=u.row&&l.shiftRow(-n)}s=i}else{var p;if(t){p=[i,0];for(var d=0;d=i&&l.shiftRow(n)}}else{n=Math.abs(e.data.range.start.column-e.data.range.end.column),r.indexOf("remove")!=-1&&(a=this.getFoldsInRange(e.data.range),this.removeFolds(a),n=-n);var l=this.getFoldLine(i);l&&l.addRemoveChars(i,o.column,n)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(i,s):this.$updateRowLengthCache(i,s),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var n=this.doc.getAllLines(),r=this.getTabSize(),i=this.$wrapData,s=this.$wrapLimit,o,a,f=e;t=Math.min(t,n.length-1);while(f<=t)a=this.getFoldLine(f,a),a?(o=[],a.walk(function(e,t,r,i){var s;if(e!=null){s=this.$getDisplayTokens(e,o.length),s[0]=u;for(var a=1;at){var f=i+t;if(e[f-1]>=d&&e[f]>=d){a(f);continue}if(e[f]==u||e[f]==l){for(f;f!=i-1;f--)if(e[f]==u)break;if(f>i){a(f);continue}f=i+t;for(f;f>2)),i-1);while(f>c&&e[f]c&&e[f]c&&e[f]==p)f--}else while(f>c&&e[f]c){a(++f);continue}f=i+t,a(f)}return n},this.$getDisplayTokens=function(e,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(p):u>=4352&&g(u)?i.push(t,n):i.push(t)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&g(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.getRowLength=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getScreenLastRowColumn=function(e){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE);return this.documentToScreenColumn(t.row,t.column)},this.getDocumentLastRowColumn=function(e,t){var n=this.documentToScreenRow(e,t);return this.getScreenLastRowColumn(n)},this.getDocumentLastRowColumnPosition=function(e,t){var n=this.documentToScreenRow(e,t);return this.screenToDocumentPosition(n,Number.MAX_VALUE/10)},this.getRowSplitData=function(e){return this.$useWrapMode?this.$wrapData[e]:undefined},this.getScreenTabSize=function(e){return this.$tabSize-e%this.$tabSize},this.screenToDocumentRow=function(e,t){return this.screenToDocumentPosition(e,t).row},this.screenToDocumentColumn=function(e,t){return this.screenToDocumentPosition(e,t).column},this.screenToDocumentPosition=function(e,t){if(e<0)return{row:0,column:0};var n,r=0,i=0,s,o=0,u=0,a=this.$screenRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var o=a[f],r=this.$docRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getLength()-1,p=this.getNextFoldLine(r),d=p?p.start.row:Infinity;while(o<=e){u=this.getRowLength(r);if(o+u-1>=e||r>=h)break;o+=u,r++,r>d&&(r=p.end.row+1,p=this.getNextFoldLine(r,p),d=p?p.start.row:Infinity),c&&(this.$docRowCache.push(r),this.$screenRowCache.push(o))}if(p&&p.start.row<=r)n=this.getFoldDisplayLine(p),r=p.start.row;else{if(o+u<=e||r>h)return{row:h,column:this.getLine(h).length};n=this.getLine(r),p=null}if(this.$useWrapMode){var v=this.$wrapData[r];v&&(s=v[e-o],e>o&&v.length&&(i=v[e-o-1]||v[v.length-1],n=n.substring(i)))}return i+=this.$getStringScreenWidth(n,t)[1],this.$useWrapMode&&i>=s&&(i=s-1),p?p.idxToPosition(i):{row:r,column:i}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);if(this.$useWrapMode){var v=this.$wrapData[i],m=0;while(d.length>=v[m])r++,m++;d=d.substring(v[m-1]||0,d.length)}return{row:r,column:this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}return e}}).call(p.prototype),e("./edit_session/folding").Folding.call(p.prototype),e("./edit_session/bracket_match").BracketMatch.call(p.prototype),s.defineOptions(p.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}this.$wrap=e},get:function(){return this.getUseWrapMode()?this.getWrapLimitRange().min||"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=="auto"?this.$wrapAsCode=this.$mode.type!="text":this.$wrapAsCode=e!="text"},initialValue:"auto"},firstLineNumber:{set:function(){this._emit("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._emit("changeTabSize")},initialValue:4,handlesSet:!0},overwrite:{set:function(e){this._emit("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0}}),t.EditSession=p}),define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.lead=this.selectionLead=this.doc.createAnchor(0,0),this.anchor=this.selectionAnchor=this.doc.createAnchor(0,0);var t=this;this.lead.on("change",function(e){t._emit("changeCursor"),t.$isEmpty||t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.selectionAnchor.on("change",function(){t.$isEmpty||t._emit("changeSelection")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return this.isEmpty()?!1:this.getRange().isMultiLine()},this.getCursor=function(){return this.lead.getPosition()},this.setSelectionAnchor=function(e,t){this.anchor.setPosition(e,t),this.$isEmpty&&(this.$isEmpty=!1,this._emit("changeSelection"))},this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionLead=function(){return this.lead.getPosition()},this.shiftSelection=function(e){if(this.$isEmpty){this.moveCursorTo(this.lead.row,this.lead.column+e);return}var t=this.getSelectionAnchor(),n=this.getSelectionLead(),r=this.isBackwards();(!r||t.column!==0)&&this.setSelectionAnchor(t.row,t.column+e),(r||n.column!==0)&&this.$moveSelection(function(){this.moveCursorTo(n.row,n.column+e)})},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.isEmpty()?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var e=this.doc.getLength()-1;this.setSelectionAnchor(0,0),this.moveCursorTo(e,this.doc.getLine(e).length)},this.setRange=this.setSelectionRange=function(e,t){t?(this.setSelectionAnchor(e.end.row,e.end.column),this.selectTo(e.start.row,e.start.column)):(this.setSelectionAnchor(e.start.row,e.start.column),this.selectTo(e.end.row,e.end.column)),this.getRange().isEmpty()&&(this.$isEmpty=!0),this.$desiredColumn=null},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column==0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(e.column-n,e.column).split(" ").length-1==n?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var s=this.session.getFoldAt(e,t,1);if(s){this.moveCursorTo(s.end.row,s.end.column);return}if(i=this.session.nonTokenRe.exec(r))t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t);if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}if(o=this.session.tokenRe.exec(s))t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t,n=0,r,i=/\s/,s=this.session.tokenRe;s.lastIndex=0;if(t=this.session.tokenRe.exec(e))n=this.session.tokenRe.lastIndex;else{while((r=e[n])&&i.test(r))n++;if(n<1){s.lastIndex=0;while((r=e[n])&&!s.test(r)){s.lastIndex=0,n++;if(i.test(r)){if(n>2){n--;break}while((r=e[n])&&i.test(r))n++;if(n>2)break}}}}return s.lastIndex=0,n},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column);t===0&&(this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var r=this.session.screenToDocumentPosition(n.row+e,n.column);this.moveCursorTo(r.row,r.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0,this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e.isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),define("ace/range",["require","exports","module"],function(e,t,n){var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.row2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),a==Infinity&&(a=E,s=!1,o=!1),l&&a%f!=0&&(a=Math.floor(a/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new a(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,l=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new f(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new a(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new f(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);l.start.row==c&&(l.start.column+=h),l.end.row==c&&(l.end.column+=h),t.selection.fromOrientedRange(l)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)e[t]&&(this.$embeds.push(t),this.$modes[t]=new e[t]);var n=["toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t1?f.onMatch=this.$applyToken:f.onMatch=f.token);c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null),f.__proto__=null}u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){r=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var m=e.substring(l,v-d.length);c.type==h?c.value+=m:(c.type&&f.push(c),c={type:h,value:m})}for(var g=0;gr){while(l=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n}}).call(r.prototype),t.TokenIterator=r}),define("ace/document",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=e("./range").Range,o=e("./anchor").Anchor,u=function(e){this.$lines=[],e.length==0?this.$lines=[""]:Array.isArray(e)?this._insertLines(0,e):this.insert({row:0,column:0},e)};(function(){r.implement(this,i),this.setValue=function(e){var t=this.getLength();this.remove(new s(0,0,t,this.getLine(t-1).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new o(this,e,t)},"aaa".split(/a/).length==0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine}},this.$autoNewLine="\n",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){if(e.start.row==e.end.row)return this.getLine(e.start.row).substring(e.start.column,e.end.column);var t=this.getLines(e.start.row,e.end.row);t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;return e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column)),t.join(this.getNewLineCharacter())},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):e.row<0&&(e.row=0),e},this.insert=function(e,t){if(!t||t.length===0)return e;e=this.$clipPosition(e),this.getLength()<=1&&this.$detectNewLine(t);var n=this.$split(t),r=n.splice(0,1)[0],i=n.length==0?null:n.splice(n.length-1,1)[0];return e=this.insertInLine(e,r),i!==null&&(e=this.insertNewLine(e),e=this._insertLines(e.row,n),e=this.insertInLine(e,i||"")),e},this.insertLines=function(e,t){return e>=this.getLength()?this.insert({row:e,column:0},"\n"+t.join("\n")):this._insertLines(Math.max(e,0),t)},this._insertLines=function(e,t){if(t.length==0)return{row:e,column:0};if(t.length>65535){var n=this._insertLines(e,t.slice(65535));t=t.slice(0,65535)}var r=[e,0];r.push.apply(r,t),this.$lines.splice.apply(this.$lines,r);var i=new s(e,0,e+t.length,0),o={action:"insertLines",range:i,lines:t};return this._emit("change",{data:o}),n||i.end},this.insertNewLine=function(e){e=this.$clipPosition(e);var t=this.$lines[e.row]||"";this.$lines[e.row]=t.substring(0,e.column),this.$lines.splice(e.row+1,0,t.substring(e.column,t.length));var n={row:e.row+1,column:0},r={action:"insertText",range:s.fromPoints(e,n),text:this.getNewLineCharacter()};return this._emit("change",{data:r}),n},this.insertInLine=function(e,t){if(t.length==0)return e;var n=this.$lines[e.row]||"";this.$lines[e.row]=n.substring(0,e.column)+t+n.substring(e.column);var r={row:e.row,column:e.column+t.length},i={action:"insertText",range:s.fromPoints(e,r),text:t};return this._emit("change",{data:i}),r},this.remove=function(e){!e instanceof s&&(e=s.fromPoints(e.start,e.end)),e.start=this.$clipPosition(e.start),e.end=this.$clipPosition(e.end);if(e.isEmpty())return e.start;var t=e.start.row,n=e.end.row;if(e.isMultiLine()){var r=e.start.column==0?t:t+1,i=n-1;e.end.column>0&&this.removeInLine(n,0,e.end.column),i>=r&&this._removeLines(r,i),r!=t&&(this.removeInLine(t,e.start.column,this.getLine(t).length),this.removeNewLine(e.start.row))}else this.removeInLine(t,e.start.column,e.end.column);return e.start},this.removeInLine=function(e,t,n){if(t==n)return;var r=new s(e,t,e,n),i=this.getLine(e),o=i.substring(t,n),u=i.substring(0,t)+i.substring(n,i.length);this.$lines.splice(e,1,u);var a={action:"removeText",range:r,text:o};return this._emit("change",{data:a}),r.start},this.removeLines=function(e,t){return e<0||t>=this.getLength()?this.remove(new s(e,0,t+1,0)):this._removeLines(e,t)},this._removeLines=function(e,t){var n=new s(e,0,t+1,0),r=this.$lines.splice(e,t-e+1),i={action:"removeLines",range:n,nl:this.getNewLineCharacter(),lines:r};return this._emit("change",{data:i}),r},this.removeNewLine=function(e){var t=this.getLine(e),n=this.getLine(e+1),r=new s(e,t.length,e+1,0),i=t+n;this.$lines.splice(e,2,i);var o={action:"removeText",range:r,text:this.getNewLineCharacter()};this._emit("change",{data:o})},this.replace=function(e,t){!e instanceof s&&(e=s.fromPoints(e.start,e.end));if(t.length==0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);if(t)var n=this.insert(e.start,t);else n=e.start;return n},this.applyDeltas=function(e){for(var t=0;t=0;t--){var n=e[t],r=s.fromPoints(n.range.start,n.range.end);n.action=="insertLines"?this._removeLines(r.start.row,r.end.row-1):n.action=="insertText"?this.remove(r):n.action=="removeLines"?this._insertLines(r.start.row,n.lines):n.action=="removeText"&&this.insert(r.start,n.text)}},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;ithis.row)return;if(n.start.row==this.row&&n.start.column>this.column)return;var r=this.row,i=this.column,s=n.start,o=n.end;if(t.action==="insertText")if(s.row===r&&s.column<=i){if(s.column!==i||!this.$insertRight)s.row===o.row?i+=o.column-s.column:(i-=s.column,r+=o.row-s.row)}else s.row!==o.row&&s.row=i?i=s.column:i=Math.max(0,i-(o.column-s.column)):s.row!==o.row&&s.row=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),define("ace/background_tokenizer",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=function(e,t){this.running=!1,this.lines=[],this.states=[],this.currentLine=0,this.tokenizer=e;var n=this;this.$worker=function(){if(!n.running)return;var e=new Date,t=n.currentLine,r=-1,i=n.doc;while(n.lines[t])t++;var s=t,o=i.getLength(),u=0;n.running=!1;while(t20){n.running=setTimeout(n.$worker,20),n.currentLine=t;return}}n.currentLine=t,s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._emit("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.range,n=t.start.row,r=t.end.row-n;if(r===0)this.lines[n]=null;else if(e.action=="removeText"||e.action=="removeLines")this.lines.splice(n,r+1,null),this.states.splice(n,r+1,null);else{var i=Array(r+1);i.unshift(n,1),this.lines.splice.apply(this.lines,i),this.states.splice.apply(this.states,i)}this.currentLine=Math.min(n,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),define("ace/edit_session/folding",["require","exports","module","ace/range","ace/edit_session/fold_line","ace/edit_session/fold","ace/token_iterator"],function(e,t,n){function u(){this.getFoldAt=function(e,t,n){var r=this.getFoldLine(e);if(!r)return null;var i=r.folds;for(var s=0;s=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRange(n);if(t)this.removeFolds(i);else while(i.length)this.expandFolds(i),i=this.getFoldsInRange(n)},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row,i=0),t==null&&(t=e.end.row,n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(t=e)try{var u=this.addFold("...",s);u.collapseChildren=n,i=o}catch(a){}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.removeListener("change",this.$updateFoldWidgets),this._emit("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){var n=this.getFoldWidget(e),r=this.getLine(e);t=t.domEvent;var i=t.shiftKey,s=t.ctrlKey||t.metaKey,o=t.altKey,u=n==="end"?-1:1,a=this.getFoldAt(e,u===-1?0:r.length,u);if(a){i||s?this.removeFold(a):this.expandFold(a);return}var f=this.getFoldWidgetRange(e);if(f&&!f.isMultiLine()){a=this.getFoldAt(f.start.row,f.start.column,1);if(a&&f.isEqual(a.range)){this.removeFold(a);return}}if(o){var l=this.getParentFoldRangeData(e);if(l.range)var c=l.range.start.row+1,h=l.range.end.row;this.foldAll(c,h,s?1e4:0)}else if(i){var h=f?f.end.row:this.getLength();this.foldAll(e+1,f.end.row,s?1e4:0)}else f&&(s&&(f.collapseChildren=1e4),this.addFold("...",f));f||((t.target||t.srcElement).className+=" ace_invalid")},this.updateFoldWidgets=function(e){var t=e.data,n=t.range,r=n.start.row,i=n.end.row-r;if(i===0)this.foldWidgets[r]=null;else if(t.action=="removeText"||t.action=="removeLines")this.foldWidgets.splice(r,i+1,null);else{var s=Array(i+1);s.unshift(r,1),this.foldWidgets.splice.apply(this.foldWidgets,s)}}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.call(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.rowi)break;c.start.row==i&&c.start.column>=n.column&&(c.start.column!=n.column||!this.$insertRight)&&(c.start.column+=u,c.start.row+=o);if(c.end.row==i&&c.end.column>=n.column){if(c.end.column==n.column&&this.$insertRight)continue;c.end.column==n.column&&u>0&&fc.start.column&&c.end.column==a[f+1].start.column&&(c.end.column-=u),c.end.column+=u,c.end.row+=o}}if(o!=0&&f=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(aw&&o[c].end.row==n.end.row)c--;o=o.slice(m,c+1);for(m=0,c=o.length;m=0;u--)if(o(s[u],t,i))return!0};else var f=function(e,t,i){var s=r.getMatchOffsets(e,n);for(var u=0;u=o;r--)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=u,o=s.row;r>=o;r--)if(n(e.getLine(r),r))return}:function(n){var r=s.row,i=e.getLine(r).substr(s.column);if(n(i,r,s.column))return;for(r+=1;r<=u;r++)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=o,u=s.row;r<=u;r++)if(n(e.getLine(r),r))return};return{forEach:a}}}).call(o.prototype),t.Search=o}),define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../keyboard/hash_handler").HashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;var r={editor:t,command:e,args:n},i=this._emit("exec",r);return this._signal("afterExec",r),i===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){function s(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={};if(this.__defineGetter__&&this.__defineSetter__&&typeof console!="undefined"&&console.error){var n=!1,r=function(){n||(n=!0,console.error("commmandKeyBinding has too many m's. use commandKeyBinding"))};this.__defineGetter__("commmandKeyBinding",function(){return r(),this.commandKeyBinding}),this.__defineSetter__("commmandKeyBinding",function(e){return r(),this.commandKeyBinding=e})}else this.commmandKeyBinding=this.commandKeyBinding;this.addCommands(e)}var r=e("../lib/keys"),i=e("../lib/useragent");(function(){this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e){var t=typeof e=="string"?e:e.name;e=this.commands[t],delete this.commands[t];var n=this.commandKeyBinding;for(var r in n)for(var i in n[r])n[r][i]==e&&delete n[r][i]},this.bindKey=function(e,t){if(!e)return;if(typeof t=="function"){this.addCommand({exec:t,bindKey:e,name:t.name||e});return}var n=this.commandKeyBinding;e.split("|").forEach(function(e){var r=this.parseKeys(e,t),i=r.hashId;(n[i]||(n[i]={}))[r.key]=t},this)},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n}),n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){var t=e.bindKey;if(!t)return;var n=typeof t=="string"?t:t[this.platform];this.bindKey(n,e)},this.parseKeys=function(e){e.indexOf(" ")!=-1&&(e=e.split(/\s+/).pop());var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=this.commandKeyBinding;return r[t]&&r[t][n]},this.handleKeyboard=function(e,t,n,r){return{command:this.findKeyCommand(t,n)}}}).call(s.prototype),t.HashHandler=s}),define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config"],function(e,t,n){function s(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config");t.commands=[{name:"showSettingsMenu",bindKey:s("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"selectall",bindKey:s("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:s(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:s("Ctrl-L","Command-L"),exec:function(e){var t=parseInt(prompt("Enter line number:"),10);isNaN(t)||e.gotoLine(t)},readOnly:!0},{name:"fold",bindKey:s("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},readOnly:!0},{name:"unfold",bindKey:s("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},readOnly:!0},{name:"foldall",bindKey:s("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll()},readOnly:!0},{name:"unfoldall",bindKey:s("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},readOnly:!0},{name:"findnext",bindKey:s("Ctrl-K","Command-G"),exec:function(e){e.findNext()},readOnly:!0},{name:"findprevious",bindKey:s("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},readOnly:!0},{name:"find",bindKey:s("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:s("Ctrl-Shift-Home","Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,group:"fileJump"},{name:"gotostart",bindKey:s("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,group:"fileJump"},{name:"selectup",bindKey:s("Shift-Up","Shift-Up"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",readOnly:!0},{name:"golineup",bindKey:s("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selecttoend",bindKey:s("Ctrl-Shift-End","Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,group:"fileJump"},{name:"gotoend",bindKey:s("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,group:"fileJump"},{name:"selectdown",bindKey:s("Shift-Down","Shift-Down"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",readOnly:!0},{name:"golinedown",bindKey:s("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectwordleft",bindKey:s("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"gotowordleft",bindKey:s("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttolinestart",bindKey:s("Alt-Shift-Left","Command-Shift-Left"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"gotolinestart",bindKey:s("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"selectleft",bindKey:s("Shift-Left","Shift-Left"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoleft",bindKey:s("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectwordright",bindKey:s("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",readOnly:!0},{name:"gotowordright",bindKey:s("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttolineend",bindKey:s("Alt-Shift-Right","Command-Shift-Right"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"gotolineend",bindKey:s("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"selectright",bindKey:s("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoright",bindKey:s("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:s(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:s("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:s(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:s("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:s("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"togglerecording",bindKey:s("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:s("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:s("Ctrl-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttomatching",bindKey:s("Ctrl-Shift-P",null),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",readOnly:!0},{name:"cut",exec:function(e){var t=e.getSelectionRange();e._emit("cut",t),e.selection.isEmpty()||(e.session.remove(t),e.clearSelection())},multiSelectAction:"forEach"},{name:"removeline",bindKey:s("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},multiSelectAction:"forEachLine"},{name:"duplicateSelection",bindKey:s("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},multiSelectAction:"forEach"},{name:"sortlines",bindKey:s("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},multiSelectAction:"forEachLine"},{name:"togglecomment",bindKey:s("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine"},{name:"toggleBlockComment",bindKey:s("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach"},{name:"modifyNumberUp",bindKey:s("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:s("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},multiSelectAction:"forEach"},{name:"replace",bindKey:s("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",bindKey:s("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:s("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:s("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()}},{name:"movelinesup",bindKey:s("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()}},{name:"copylinesdown",bindKey:s("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()}},{name:"movelinesdown",bindKey:s("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()}},{name:"del",bindKey:s("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach"},{name:"backspace",bindKey:s("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach"},{name:"cut_or_delete",bindKey:s("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach"},{name:"removetolinestart",bindKey:s("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach"},{name:"removetolineend",bindKey:s("Alt-Delete","Ctrl-K"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach"},{name:"removewordleft",bindKey:s("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach"},{name:"removewordright",bindKey:s("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach"},{name:"outdent",bindKey:s("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach"},{name:"indent",bindKey:s("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach"},{name:"blockoutdent",bindKey:s("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine"},{name:"blockindent",bindKey:s("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach"},{name:"splitline",bindKey:s(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach"},{name:"transposeletters",bindKey:s("Ctrl-T","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)}},{name:"touppercase",bindKey:s("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach"},{name:"tolowercase",bindKey:s("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach"}]}),define("ace/undomanager",["require","exports","module"],function(e,t,n){var r=function(){this.reset()};(function(){this.execute=function(e){var t=e.args[0];this.$doc=e.args[1],e.merge&&this.hasUndo()&&(t=this.$undoStack.pop().concat(t)),this.$undoStack.push(t),this.$redoStack=[],this.dirtyCounter<0&&(this.dirtyCounter=NaN),this.dirtyCounter++},this.undo=function(e){var t=this.$undoStack.pop(),n=null;return t&&(n=this.$doc.undoChanges(t,e),this.$redoStack.push(t),this.dirtyCounter--),n},this.redo=function(e){var t=this.$redoStack.pop(),n=null;return t&&(n=this.$doc.redoChanges(t,e),this.$undoStack.push(t),this.dirtyCounter++),n},this.reset=function(){this.$undoStack=[],this.$redoStack=[],this.dirtyCounter=0},this.hasUndo=function(){return this.$undoStack.length>0},this.hasRedo=function(){return this.$redoStack.length>0},this.markClean=function(){this.dirtyCounter=0},this.isClean=function(){return this.dirtyCounter===0}}).call(r.prototype),t.UndoManager=r}),define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/useragent","ace/config","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/renderloop","ace/lib/event_emitter"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/useragent"),o=e("./config"),u=e("./layer/gutter").Gutter,a=e("./layer/marker").Marker,f=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").ScrollBarH,h=e("./scrollbar").ScrollBarV,p=e("./renderloop").RenderLoop,d=e("./lib/event_emitter").EventEmitter,v=".ace_editor {position: relative;overflow: hidden;font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;font-size: 12px;line-height: normal;color: black;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;}.ace_content {position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;cursor: text;}.ace_dragging, .ace_dragging * {cursor: move !important;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: '';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUM2OEZDQTQ4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUM2OEZDQTU4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQzY4RkNBMjhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQzY4RkNBMzhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PkgXxbAAAAJbSURBVHjapFNNaBNBFH4zs5vdZLP5sQmNpT82QY209heh1ioWisaDRcSKF0WKJ0GQnrzrxasHsR6EnlrwD0TagxJabaVEpFYxLWlLSS822tr87m66ccfd2GKyVhA6MMybgfe97/vmPUQphd0sZjto9XIn9OOsvlu2nkqRzVU+6vvlzPf8W6bk8dxQ0NPbxAALgCgg2JkaQuhzQau/El0zbmUA7U0Es8v2CiYmKQJHGO1QICCLoqilMhkmurDAyapKgqItezi/USRdJqEYY4D5jCy03ht2yMkkvL91jTTX10qzyyu2hruPRN7jgbH+EOsXcMLgYiThEgAMhABW85oqy1DXdRIdvP1AHJ2acQXvDIrVHcdQNrEKNYSVMSZGMjEzIIAwDXIo+6G/FxcGnzkC3T2oMhLjre49sBB+RRcHLqdafK6sYdE/GGBwU1VpFNj0aN8pJbe+BkZyevUrvLl6Xmm0W9IuTc0DxrDNAJd5oEvI/KRsNC3bQyNjPO9yQ1YHcfj2QvfQc/5TUhJTBc2iM0U7AWDQtc1nJHvD/cfO2s7jaGkiTEfa/Ep8coLu7zmNmh8+dc5lZDuUeFAGUNA/OY6JVaypQ0vjr7XYjUvJM37vt+j1vuTK5DgVfVUoTjVe+y3/LxMxY2GgU+CSLy4cpfsYorRXuXIOi0Vt40h67uZFTdIo6nLaZcwUJWAzwNS0tBnqqKzQDnjdG/iPyZxo46HaKUpbvYkj8qYRTZsBhge+JHhZyh0x9b95JqjVJkT084kZIPwu/mPWqPgfQ5jXh2+92Ay7HedfAgwA6KDWafb4w3cAAAAASUVORK5CYII=\");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUM2OEZDQTg4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUM2OEZDQTk4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQzY4RkNBNjhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQzY4RkNBNzhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pgd7PfIAAAGmSURBVHjaYvr//z8DJZiJgUIANoCRkREb9gLiSVAaQx4OQM7AAkwd7XU2/v++/rOttdYGEB9dASEvOMydGKfH8Gv/p4XTkvRBfLxeQAP+1cUhXopyvzhP7P/IoSj7g7Mw09cNKO6J1QQ0L4gICPIv/veg/8W+JdFvQNLHVsW9/nmn9zk7B+cCkDwhL7gt6knSZnx9/LuCEOcvkIAMP+cvto9nfqyZmmUAksfnBUtbM60gX/3/kgyv3/xSFOL5DZT+L8vP+Yfh5cvfPvp/xUHyQHXGyAYwgpwBjZYFT3Y1OEl/OfCH4ffv3wzc4iwMvNIsDJ+f/mH4+vIPAxsb631WW0Yln6ZpQLXdMK/DXGDflh+sIv37EivD5x//Gb7+YWT4y86sl7BCCkSD+Z++/1dkvsFRl+HnD1Rvje4F8whjMXmGj58YGf5zsDMwcnAwfPvKcml62DsQDeaDxN+/Y0qwlpEHqrdB94IRNIDUgfgfKJChGK4OikEW3gTiXUB950ASLFAF54AC94A0G9QAfOnmF9DCDzABFqS08IHYDIScdijOjQABBgC+/9awBH96jwAAAABJRU5ErkJggg==\");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url(\"data:image/gif;base64,R0lGODlhEAAQAMQAAAAAAEFBQVJSUl5eXmRkZGtra39/f4WFhYmJiZGRkaampry8vMPDw8zMzNXV1dzc3OTk5Orq6vDw8P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABQALAAAAAAQABAAAAUuICWOZGmeaBml5XGwFCQSBGyXRSAwtqQIiRuiwIM5BoYVbEFIyGCQoeJGrVptIQA7\");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRTk5MTVGREIxNDkxMUUxOTc5Q0FFREQyMTNGMjBFQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRTk5MTVGRUIxNDkxMUUxOTc5Q0FFREQyMTNGMjBFQyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkZFOTkxNUZCQjE0OTExRTE5NzlDQUVERDIxM0YyMEVDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkZFOTkxNUZDQjE0OTExRTE5NzlDQUVERDIxM0YyMEVDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+SIDkjAAAAJ1JREFUeNpi/P//PwMlgImBQkB7A6qrq/+DMC55FkIGKCoq4pVnpFkgTp069f/+/fv/r1u37r+tre1/kg0A+ptn9uzZYLaRkRHpLvjw4cNXWVlZhufPnzOcO3eOdAO0tbVPAjHDmzdvGA4fPsxIsgGSkpJmv379Ynj37h2DjIyMCMkG3LhxQ/T27dsMampqDHZ2dq/pH41DxwCAAAMAFdc68dUsFZgAAAAASUVORK5CYII=\");}.ace_scrollbar {position: absolute;overflow-x: hidden;overflow-y: auto;right: 0;top: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-h {position: absolute;overflow-x: auto;overflow-y: hidden;right: 0;left: 0;bottom: 0;z-index: 6;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;text-indent: -1em;}.ace_text-input.ace_composition {background: #f8f8f8;color: #111;z-index: 1000;opacity: 1;text-indent: 0;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;white-space: nowrap;height: 100%;width: 100%;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;/* setting pointer-events: auto; on node under the mouse, which changesduring scroll, will break mouse wheel scrolling in Safari */pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;}.ace_text-layer {font: inherit !important;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;border-left: 2px solid}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0px;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {-moz-transition: opacity 0.18s;-webkit-transition: opacity 0.18s;-o-transition: opacity 0.18s;-ms-transition: opacity 0.18s;transition: opacity 0.18s;}.ace_cursor[style*=\"opacity: 0\"]{-ms-filter: \"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)\";}.ace_editor.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_line {white-space: nowrap;}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;}.ace_line .ace_fold {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%3AIDAT8%11c%FC%FF%FF%7F%18%03%1A%60%01%F2%3F%A0%891%80%04%FF%11-%F8%17%9BJ%E2%05%B1ZD%81v%26t%E7%80%F8%A3%82h%A12%1A%20%A3%01%02%0F%01%BA%25%06%00%19%C0%0D%AEF%D5%3ES%00%00%00%00IEND%AEB%60%82\");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;-moz-border-radius: 2px;-webkit-border-radius: 2px;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%003IDAT8%11c%FC%FF%FF%7F%3E%03%1A%60%01%F2%3F%A3%891%80%04%FFQ%26%F8w%C0%B43%A1%DB%0C%E2%8F%0A%A2%85%CAh%80%8C%06%08%3C%04%E8%96%18%00%A3S%0D%CD%CF%D8%C1%9D%00%00%00%00IEND%AEB%60%82\");background-repeat: no-repeat, repeat-x;background-position: center center, top left;}.ace_gutter-tooltip {background-color: #FFF;background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1));background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;display: inline-block;max-width: 500px;padding: 4px;position: fixed;z-index: 999999;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;cursor: default;white-space: pre-line;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAe%8A%B1%0D%000%0C%C2%F2%2CK%96%BC%D0%8F9%81%88H%E9%D0%0E%96%C0%10%92%3E%02%80%5E%82%E4%A9*-%EEsw%C8%CC%11%EE%96w%D8%DC%E9*Eh%0C%151(%00%00%00%00IEND%AEB%60%82\");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAm%C7%C1%09%000%08C%D1%8C%ECE%C8E(%8E%EC%02)%1EZJ%F1%C1'%04%07I%E1%E5%EE%CAL%F5%A2%99%99%22%E2%D6%1FU%B5%FE0%D9x%A7%26Wz5%0E%D5%00%00%00%00IEND%AEB%60%82\");}.ace_fold-widget.ace_closed {background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%03%00%00%00%06%08%06%00%00%00%06%E5%24%0C%00%00%009IDATx%DA5%CA%C1%09%000%08%03%C0%AC*(%3E%04%C1%0D%BA%B1%23%A4Uh%E0%20%81%C0%CC%F8%82%81%AA%A2%AArGfr%88%08%11%11%1C%DD%7D%E0%EE%5B%F6%F6%CB%B8%05Q%2F%E9tai%D9%00%00%00%00IEND%AEB%60%82\");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);-moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);-webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);-moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);-webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}/*** Dark version for fold widgets*/.ace_dark .ace_fold-widget {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC\");}.ace_dark .ace_fold-widget.ace_end {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==\");}.ace_dark .ace_fold-widget.ace_closed {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==\");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {-moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);-webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {-moz-transition: opacity 0.4s ease 0.05s;-webkit-transition: opacity 0.4s ease 0.05s;-o-transition: opacity 0.4s ease 0.05s;-ms-transition: opacity 0.4s ease 0.05s;transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {-moz-transition: opacity 0.05s ease 0.05s;-webkit-transition: opacity 0.05s ease 0.05s;-o-transition: opacity 0.05s ease 0.05s;-ms-transition: opacity 0.05s ease 0.05s;transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}";i.importCssString(v,"ace_editor");var m=function(e,t){var n=this;this.container=e||i.createElement("div"),this.$keepTextAreaAtCursor=!0,i.addCssClass(this.container,"ace_editor"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new u(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new a(this.content);var r=this.$textLayer=new f(this.content);this.canvas=r.element,this.$markerFront=new a(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new h(this.container,this),this.scrollBarH=new c(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$textLayer.addEventListener("changeCharacterSize",function(){n.updateCharacterSize(),n.onResize(!0),n._signal("changeCharacterSize")}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$loop=new p(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),o.resetOptions(this),o._emit("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,d),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session=e,this.scroller.className="ace_scroller",this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e),this.$loop.schedule(this.CHANGE_FULL)},this.updateLines=function(e,t){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow||this.$changedLines.lastRow2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0)},this.$updateCachedSize=function(e,t,n,r){var i=0,s=this.$size,o={width:s.width,height:s.height,scrollerHeight:s.scrollerHeight,scrollerWidth:s.scrollerWidth};r&&(e||s.height!=r)&&(s.height=r,i=this.CHANGE_SIZE,s.scrollerHeight=s.height,this.$horizScroll&&(s.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",this.session&&(this.session.setScrollTop(this.getScrollTop()),i|=this.CHANGE_SCROLL));if(n&&(e||s.width!=n)){i=this.CHANGE_SIZE,s.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,this.scrollBarH.element.style.left=this.scroller.style.left=t+"px",s.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()),this.scrollBarH.element.style.right=this.scroller.style.right=this.scrollBarV.getWidth()+"px",this.scroller.style.bottom=this.scrollBarH.getHeight()+"px";if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)i|=this.CHANGE_FULL}return i&&this._signal("resize",o),i},this.onGutterResize=function(){var e=this.$showGutter?this.$gutter.offsetWidth:0;e!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,e,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):(this.$computeLayerConfig(),this.$loop.schedule(this.CHANGE_MARKER))},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updateGutterLineHighlight=function(){var e=this.$cursorLayer.$pixelPos,t=this.layerConfig.lineHeight;if(this.session.getUseWrapMode()){var n=this.session.selection.getCursor();n.column=0,e=this.$cursorLayer.getPixelPosition(n,!0),t*=this.session.getRowLength(n.row)}this.$gutterLineHighlight.style.top=e.top-this.layerConfig.offset+"px",this.$gutterLineHighlight.style.height=t+"px"},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.content},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(!this.$keepTextAreaAtCursor)return;var e=this.layerConfig,t=this.$cursorLayer.$pixelPos.top,n=this.$cursorLayer.$pixelPos.left;t-=e.offset;var r=this.lineHeight;if(t<0||t>e.height-r)return;var i=this.characterWidth;if(this.$composition){var s=this.textarea.value.replace(/^\x01+/,"");i*=this.session.$getStringScreenWidth(s)[0]+2,r+=2,t-=1}n-=this.scrollLeft,n>this.$size.scrollerWidth-i&&(n=this.$size.scrollerWidth-i),n-=this.scrollBar.width,this.textarea.style.height=r+"px",this.textarea.style.width=i+"px",this.textarea.style.right=Math.max(0,this.$size.scrollerWidth-n-i)+"px",this.textarea.style.bottom=Math.max(0,this.$size.height-t-r)+"px"},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=Math.floor((this.layerConfig.height+this.layerConfig.offset)/this.layerConfig.lineHeight);return this.layerConfig.firstRow-1+e},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){this.scrollBarV.setInnerHeight(this.layerConfig.maxHeight+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setInnerWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||!e&&!t){this.$changes|=e;return}if(!this.$size.width)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender");if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL)e|=this.$computeLayerConfig();e&this.CHANGE_H_SCROLL&&(this.$updateScrollBarH(),this.content.style.marginLeft=-this.scrollLeft+"px",this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$updateScrollBarV(),this.$updateScrollBarH(),this.$textLayer.update(this.layerConfig),this.$showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){this.$updateScrollBarV(),e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(this.layerConfig):this.$textLayer.scrollLines(this.layerConfig),this.$showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$textLayer.update(this.layerConfig),this.$showGutter&&this.$gutterLayer.update(this.layerConfig)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(this.layerConfig):(e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER)&&this.$showGutter&&this.$gutterLayer.update(this.layerConfig),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(this.layerConfig),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(this.layerConfig),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(this.layerConfig);if(e&this.CHANGE_SIZE||e&this.CHANGE_LINES)this.$updateScrollBarV(),this.$updateScrollBarH();this._signal("afterRender")},this.$autosize=function(e,t){var e=this.session.getScreenLength()*this.lineHeight,n=this.$maxLines*this.lineHeight,r=Math.max((this.$minLines||1)*this.lineHeight,Math.min(n,e)),i=e>n;if(r!=this.desiredHeight||this.$size.height!=this.desiredHeight||i!=this.$vScroll){i!=this.$vScroll&&(this.$vScroll=i,this.scrollBarV.setVisible(i));var s=this.container.clientWidth;this.container.style.height=r+"px",this.$updateCachedSize(!0,this.$gutterWidth,s,r),this.desiredHeight=r}},this.$computeLayerConfig=function(){this.$maxLines&&this.lineHeight>1&&this.$autosize();var e=this.session,t=this.$size.height<=2*this.lineHeight,n=this.session.getScreenLength(),r=n*this.lineHeight,i=this.scrollTop%this.lineHeight,s=this.$size.scrollerHeight+this.lineHeight,o=this.$getLongestLine(),u=!t&&(this.$hScrollBarAlwaysVisible||this.$size.scrollerWidth-o-2*this.$padding<0),a=this.$horizScroll!==u;a&&(this.$horizScroll=u,this.scrollBarH.setVisible(u)),!this.$maxLines&&this.$scrollPastEnd&&this.scrollTop>r-this.$size.scrollerHeight&&(r+=Math.min((this.$size.scrollerHeight-this.lineHeight)*this.$scrollPastEnd,this.scrollTop-r+this.$size.scrollerHeight));var f=!t&&(this.$vScrollBarAlwaysVisible||this.$size.scrollerHeight-r<0),l=this.$vScroll!==f;l&&(this.$vScroll=f,this.scrollBarV.setVisible(f)),this.session.setScrollTop(Math.max(-this.scrollMargin.top,Math.min(this.scrollTop,r-this.$size.scrollerHeight+this.scrollMargin.v))),this.session.setScrollLeft(Math.max(-this.scrollMargin.left,Math.min(this.scrollLeft,o+2*this.$padding-this.$size.scrollerWidth+this.scrollMargin.h)));var c=Math.ceil(s/this.lineHeight)-1,h=Math.max(0,Math.round((this.scrollTop-i)/this.lineHeight)),p=h+c,d,v,m=this.lineHeight;h=e.screenToDocumentRow(h,0);var g=e.getFoldLine(h);g&&(h=g.start.row),d=e.documentToScreenRow(h,0),v=e.getRowLength(h)*m,p=Math.min(e.screenToDocumentRow(p,0),e.getLength()-1),s=this.$size.scrollerHeight+e.getRowLength(p)*m+v,i=this.scrollTop-d*m;var y=0;if(a||l)y=this.$updateCachedSize(!0,this.gutterWidth,this.$size.width,this.$size.height),this._signal("scrollbarVisibilityChanged"),l&&(o=this.$getLongestLine());return this.layerConfig={width:o,padding:this.$padding,firstRow:h,firstRowScreen:d,lastRow:p,lineHeight:m,characterWidth:this.characterWidth,minHeight:s,maxHeight:r,offset:i,height:this.$size.scrollerHeight},this.$gutterLayer.element.style.marginTop=-i+"px",this.content.style.marginTop=-i+"px",this.content.style.width=o+2*this.$padding+"px",this.content.style.height=s+"px",y},this.$updateLines=function(){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(ti?(t&&(i-=t*this.$size.scrollerHeight),i==0?i=-this.scrollMargin.top:i==0&&(i=+this.scrollMargin.bottom),this.session.setScrollTop(i)):s+this.$size.scrollerHeightr?(r=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight-(this.$size.scrollerHeight-this.lineHeight)*this.$scrollPastEnd<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=(e+this.scrollLeft-n.left-this.$padding)/this.characterWidth,i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),s=Math.round(r);return{row:i,column:s,side:r-s>0?1:-1}},this.screenToTextCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=Math.round((e+this.scrollLeft-n.left-this.$padding)/this.characterWidth),i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight);return this.session.screenToDocumentPosition(i,Math.max(r,0))},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+Math.round(r.column*this.characterWidth),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition||(this.$composition={keepTextAreaAtCursor:this.$keepTextAreaAtCursor,cssText:this.textarea.style.cssText}),this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor()},this.setCompositionText=function(e){this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null},this.setTheme=function(e,t){function s(r){if(n.$themeValue!=e)return t&&t();if(!r.cssClass)return;i.importCssString(r.cssText,r.cssClass,n.container.ownerDocument),n.theme&&i.removeCssClass(n.container,n.theme.cssClass),n.$theme=r.cssClass,n.theme=r,i.addCssClass(n.container,r.cssClass),i.setCssClass(n.container,"ace_dark",r.isDark);var s="padding"in r?r.padding:4;n.$padding&&s!=n.$padding&&n.setPadding(s),n.$size&&(n.$size.width=0,n.onResize()),n._dispatchEvent("themeLoaded",{theme:r}),t&&t()}var n=this;this.$themeValue=e,n._dispatchEvent("themeChange",{theme:e});if(!e||typeof e=="string"){var r=e||"ace/theme/textmate";o.loadModule(["theme",r],s)}else s(e)},this.getTheme=function(){return this.$themeValue},this.setStyle=function(e,t){i.setCssClass(this.container,e,t!=0)},this.unsetStyle=function(e){i.removeCssClass(this.container,e)},this.setMouseCursor=function(e){this.content.style.cursor=e},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(m.prototype),o.defineOptions(m.prototype,"renderer",{animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e=="number"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.onGutterResize()},initialValue:!0},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightGutterLine:{set:function(e){if(!this.$gutterLineHighlight){this.$gutterLineHighlight=i.createElement("div"),this.$gutterLineHighlight.className="ace_gutter-active-line",this.$gutter.appendChild(this.$gutterLineHighlight);return}this.$gutterLineHighlight.style.display=e?"":"none",this.$cursorLayer.$pixelPos&&this.$updateGutterLineHighlight()},initialValue:!1,value:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e=="number"&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.updateFull()}},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}}}),t.VirtualRenderer=m}),define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/lang"),o=e("../lib/event_emitter").EventEmitter,u=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_gutter-layer",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this),this.$cells=[]};(function(){i.implement(this,o),this.setSession=function(e){this.session&&this.session.removeEventListener("change",this.$updateAnnotations),this.session=e,e.on("change",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.addGutterDecoration"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.removeGutterDecoration"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];var t,n;for(var r=0;rs&&(p=i.end.row+1,i=this.session.getNextFoldLine(p,i),s=i?i.start.row:Infinity);if(p>n){while(this.$cells.length>h+1)c=this.$cells.pop(),this.element.removeChild(c.element);break}c=this.$cells[++h],c||(c={element:null,textNode:null,foldWidget:null},c.element=r.createElement("div"),c.textNode=document.createTextNode(""),c.element.appendChild(c.textNode),this.element.appendChild(c.element),this.$cells[h]=c);var d="ace_gutter-cell ";u[p]&&(d+=u[p]),a[p]&&(d+=a[p]),this.$annotations[p]&&(d+=this.$annotations[p].className),c.element.className!=d&&(c.element.className=d);var v=this.session.getRowLength(p)*e.lineHeight+"px";v!=c.element.style.height&&(c.element.style.height=v);var m=l=p+f;m!=c.textNode.data&&(c.textNode.data=m);if(o){var g=o[p];g==null&&(g=o[p]=this.session.getFoldWidget(p))}if(g){c.foldWidget||(c.foldWidget=r.createElement("span"),c.element.appendChild(c.foldWidget));var d="ace_fold-widget ace_"+g;g=="start"&&p==s&&pn.right-t.right)return"foldWidgets"}}).call(u.prototype),t.Gutter=u}),define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.update=function(e){var e=e||this.config;if(!e)return;this.config=e;var t=[];for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var s=r.range.clipRows(e.firstRow,e.lastRow);if(s.isEmpty())continue;s=s.toScreenRange(this.session);if(r.renderer){var o=this.$getTop(s.start.row,e),u=this.$padding+s.start.column*e.characterWidth;r.renderer(t,s,u,o,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,s,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,s,r.clazz,e):s.isMultiLine()?r.type=="text"?this.drawTextMarker(t,s,r.clazz,e):this.drawMultiLineMarker(t,s,r.clazz,e):this.drawSingleLineMarker(t,s,r.clazz+" ace_start",e)}this.element=i.setInnerHtml(this.element,t.join(""))},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(e,t,n,i,s){var o=t.start.row,u=new r(o,t.start.column,o,this.session.getScreenLastRowColumn(o));this.drawSingleLineMarker(e,u,n+" ace_start",i,1,s),o=t.end.row,u=new r(o,0,o,t.end.column),this.drawSingleLineMarker(e,u,n,i,0,s);for(o=t.start.row+1;o"),u=this.$getTop(t.end.row,r);var f=t.end.column*r.characterWidth;e.push("
"),o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<0)return;u=this.$getTop(t.start.row+1,r),e.push("
")},this.drawSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;e.push("
")},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),e.push("
")},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;e.push("
")}}).call(s.prototype),t.Marker=s}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$characterSize={width:0,height:0},this.checkForSizeChanges(),this.$pollSizeChanges()};(function(){r.implement(this,u),this.EOF_CHAR="¶",this.EOL_CHAR="¬",this.TAB_CHAR="→",this.SPACE_CHAR="·",this.$padding=0,this.setPadding=function(e){this.$padding=e,this.element.style.padding="0 "+e+"px"},this.getLineHeight=function(){return this.$characterSize.height||0},this.getCharacterWidth=function(){return this.$characterSize.width||0},this.checkForSizeChanges=function(){var e=this.$measureSizes();if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$pollSizeChanges=function(){var e=this;this.$pollSizeChangesTimer=setInterval(function(){e.checkForSizeChanges()},500)},this.$fontStyles={fontFamily:1,fontSize:1,fontWeight:1,fontStyle:1,lineHeight:1},this.$measureSizes=o.isIE||o.isOldGecko?function(){var e=1e3;if(!this.$measureNode){var t=this.$measureNode=i.createElement("div"),n=t.style;n.width=n.height="auto",n.left=n.top=-e*40+"px",n.visibility="hidden",n.position="fixed",n.overflow="visible",n.whiteSpace="nowrap",t.innerHTML=s.stringRepeat("Xy",e);if(this.element.ownerDocument.body)this.element.ownerDocument.body.appendChild(t);else{var r=this.element.parentNode;while(!i.hasCssClass(r,"ace_editor"))r=r.parentNode;r.appendChild(t)}}if(!this.element.offsetWidth)return null;var n=this.$measureNode.style,o=i.computedStyle(this.element);for(var u in this.$fontStyles)n[u]=o[u];var a={height:this.$measureNode.offsetHeight,width:this.$measureNode.offsetWidth/(e*2)};return a.width==0||a.height==0?null:a}:function(){if(!this.$measureNode){var e=this.$measureNode=i.createElement("div"),t=e.style;t.width=t.height="auto",t.left=t.top="-100px",t.visibility="hidden",t.position="fixed",t.overflow="visible",t.whiteSpace="nowrap",e.innerHTML=s.stringRepeat("X",100);var n=this.element.parentNode;while(n&&!i.hasCssClass(n,"ace_editor"))n=n.parentNode;if(!n)return this.$measureNode=null;n.appendChild(e)}var r=this.$measureNode.getBoundingClientRect(),o={height:r.height,width:r.width/100};return o.width==0||o.height==0?null:o},this.setSession=function(e){this.session=e,this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;n"+this.TAB_CHAR+s.stringRepeat(" ",n-1)+""):t.push(s.stringRepeat(" ",n));if(this.displayIndentGuides){this.$indentGuideRe=/\s\S| \t|\t |\s$/;var r="ace_indent-guide";if(this.showInvisibles){r+=" ace_invisible";var i=s.stringRepeat(this.SPACE_CHAR,this.tabSize),o=this.TAB_CHAR+s.stringRepeat(" ",this.tabSize-1)}else var i=s.stringRepeat(" ",this.tabSize),o=i;this.$tabStrings[" "]=""+i+"",this.$tabStrings[" "]=""+o+""}},this.updateLines=function(e,t,n){(this.config.lastRow!=e.lastRow||this.config.firstRow!=e.firstRow)&&this.scrollLines(e),this.config=e;var r=Math.max(t,e.firstRow),s=Math.min(n,e.lastRow),o=this.element.childNodes,u=0;for(var a=e.firstRow;al&&(a=f.end.row+1,f=this.session.getNextFoldLine(a,f),l=f?f.start.row:Infinity);if(a>s)break;var c=o[u++];if(c){var h=[];this.$renderLine(h,a,!this.$useLineGroups(),a==l?f:!1),i.setInnerHtml(c,h.join(""))}a++}},this.scrollLines=function(e){var t=this.config;this.config=e;if(!t||t.lastRow0;r--)n.removeChild(n.firstChild);if(t.lastRow>e.lastRow)for(var r=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);r>0;r--)n.removeChild(n.lastChild);if(e.firstRowt.lastRow){var i=this.$renderLinesFragment(e,t.lastRow+1,e.lastRow);n.appendChild(i)}},this.$renderLinesFragment=function(e,t,n){var r=this.element.ownerDocument.createDocumentFragment(),s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=i.createElement("div"),f=[];this.$renderLine(f,s,!1,s==u?o:!1),a.innerHTML=f.join("");if(this.$useLineGroups())a.className="ace_line_group",r.appendChild(a);else{var l=a.childNodes;while(l.length)r.appendChild(l[0])}s++}return r},this.update=function(e){this.config=e;var t=[],n=e.firstRow,r=e.lastRow,s=n,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>r)break;this.$useLineGroups()&&t.push("
"),this.$renderLine(t,s,!1,s==u?o:!1),this.$useLineGroups()&&t.push("
"),s++}this.element=i.setInnerHtml(this.element,t.join(""))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/\t|&|<|( +)|([\x00-\x1f\x80-\xa0\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g,u=function(e,n,r,o,u){if(n)return i.showInvisibles?""+s.stringRepeat(i.SPACE_CHAR,e.length)+"":s.stringRepeat(" ",e.length);if(e=="&")return"&";if(e=="<")return"<";if(e==" "){var a=i.session.getScreenTabSize(t+o);return t+=a-1,i.$tabStrings[a]}if(e==" "){var f=i.showInvisibles?"ace_cjk ace_invisible":"ace_cjk",l=i.showInvisibles?i.SPACE_CHAR:"";return t+=1,""+l+""}return r?""+i.SPACE_CHAR+"":(t+=1,""+e+"")},a=r.replace(o,u);if(!this.$textToken[n.type]){var f="ace_"+n.type.replace(/\./g," ace_"),l="";n.type=="fold"&&(l=" style='width:"+n.value.length*this.config.characterWidth+"px;' "),e.push("",a,"")}else e.push(a);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);return r<=0||r>=n?t:t[0]==" "?(r-=r%this.tabSize,e.push(s.stringRepeat(this.$tabStrings[" "],r/this.tabSize)),t.substr(r)):t[0]==" "?(e.push(s.stringRepeat(this.$tabStrings[" "],r)),t.substr(r)):t},this.$renderWrappedLine=function(e,t,n,r){var i=0,s=0,o=n[0],u=0;for(var a=0;a=o)u=this.$renderToken(e,u,f,l.substring(0,o-i)),l=l.substring(o-i),i=o,r||e.push("","
"),s++,u=0,o=n[s]||Number.MAX_VALUE;l.length!=0&&(i+=l.length,u=this.$renderToken(e,u,f,l))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;s");if(i.length){var s=this.session.getRowSplitData(t);s&&s.length?this.$renderWrappedLine(e,i,s,n):this.$renderSimpleLine(e,i)}this.showInvisibles&&(r&&(t=r.end.row),e.push("",t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,"")),n||e.push("
")},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(a.prototype),t.Text=a}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){var r=e("../lib/dom"),i=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors")};(function(){this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&(this.smoothBlinking=e,e?r.addCssClass(this.element,"ace_smooth-blinking"):r.removeCssClass(this.element,"ace_smooth-blinking"),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking");for(var e=this.cursors.length;e--;)this.cursors[e].style.opacity="";if(!this.isBlinking||!this.blinkInterval||!this.isVisible)return;this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));var t=function(){this.timeoutId=setTimeout(function(){for(var e=this.cursors.length;e--;)this.cursors[e].style.opacity=0}.bind(this),.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){for(var e=this.cursors.length;e--;)this.cursors[e].style.opacity="";t()}.bind(this),this.blinkInterval),t()},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+n.column*this.config.characterWidth,i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,n=0,r=0;if(t===undefined||t.length===0)t=[{cursor:null}];for(var n=0,i=t.length;ne.height+e.offset||s.top<-e.offset)&&n>1)continue;var o=(this.cursors[r++]||this.addCursor()).style;o.left=s.left+"px",o.top=s.top+"px",o.width=e.characterWidth+"px",o.height=e.lineHeight+"px"}while(this.cursors.length>r)this.removeCursor();var u=this.session.getOverwrite();this.$setOverwrite(u),this.$pixelPos=s,this.restartTimer()},this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(i.prototype),t.Cursor=i}),define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=function(e,t){this.element=i.createElement("div"),this.element.className="ace_scrollbar",this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.fullWidth=this.width,this.inner.style.width=this.element.style.width=(this.width||15)+5+"px",this.setVisible(!1),this.element.style.overflowY="scroll",s.addListener(this.element,"scroll",this.onScrollV.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)},a=function(e,t){this.element=i.createElement("div"),this.element.className="ace_scrollbar-h",this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.height=t.$scrollbarWidth,this.fullHeight=this.height,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px",this.setVisible(!1),this.element.style.overflowX="scroll",s.addListener(this.element,"scroll",this.onScrollH.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){e?(this.element.style.display="",this.fullWidth&&(this.width=this.fullWidth),this.fullHeight&&(this.height=this.fullHeight)):(this.element.style.display="none",this.height=this.width=0)},this.onScrollV=function(){this.skipEvent||(this.scrollTop=this.element.scrollTop,this._emit("scroll",{data:this.scrollTop})),this.skipEvent=!1},this.onScrollH=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getWidth=function(){return this.width},this.getHeight=function(){return this.height},this.setHeight=function(e){this.element.style.height=e+"px"},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerHeight=function(e){this.inner.style.height=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=this.element.scrollTop=e)},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}).call(u.prototype),a.prototype=u.prototype,t.ScrollBar=u,t.ScrollBarV=u,t.ScrollBarH=a}),define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.window=t||window};(function(){this.schedule=function(e){this.changes=this.changes|e;if(!this.pending){this.pending=!0;var t=this;r.nextFrame(function(){t.pending=!1;var e;while(e=t.changes)t.changes=0,t.onRender(e)},this.window)}}}).call(i.prototype),t.RenderLoop=i}),define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/lib/event","ace/lib/lang","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor","ace/config"],function(e,t,n){function h(e,t,n){return c.$options.wrap=!0,c.$options.needle=t,c.$options.backwards=n==-1,c.find(e)}function v(e,t){return e.row==t.row&&e.column==t.column}function m(e){if(e.$multiselectOnSessionChange)return;e.$onAddRange=e.$onAddRange.bind(e),e.$onRemoveRange=e.$onRemoveRange.bind(e),e.$onMultiSelect=e.$onMultiSelect.bind(e),e.$onSingleSelect=e.$onSingleSelect.bind(e),e.$multiselectOnSessionChange=t.onSessionChange.bind(e),e.$multiselectOnSessionChange(e),e.on("changeSession",e.$multiselectOnSessionChange),e.on("mousedown",o),e.commands.addCommands(f.defaultCommands),g(e)}function g(e){function r(t){n&&(e.renderer.setMouseCursor(""),n=!1)}var t=e.textInput.getElement(),n=!1;u.addListener(t,"keydown",function(t){t.keyCode==18&&!(t.ctrlKey||t.shiftKey||t.metaKey)?n||(e.renderer.setMouseCursor("crosshair"),n=!0):n&&r()}),u.addListener(t,"keyup",r),u.addListener(t,"blur",r)}var r=e("./range_list").RangeList,i=e("./range").Range,s=e("./selection").Selection,o=e("./mouse/multi_select_handler").onMouseDown,u=e("./lib/event"),a=e("./lib/lang"),f=e("./commands/multi_select_commands");t.commands=f.defaultCommands.concat(f.multiSelectCommands);var l=e("./search").Search,c=new l,p=e("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(p.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(e,t){if(!e)return;if(!this.inMultiSelectMode&&this.rangeCount==0){var n=this.toOrientedRange();this.rangeList.add(n),this.rangeList.add(e);if(this.rangeList.ranges.length!=2)return this.rangeList.removeAll(),t||this.fromOrientedRange(e);this.rangeList.removeAll(),this.rangeList.add(n),this.$onAddRange(n)}e.cursor||(e.cursor=e.end);var r=this.rangeList.add(e);return this.$onAddRange(e),r.length&&this.$onRemoveRange(r),this.rangeCount>1&&!this.inMultiSelectMode&&(this._emit("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length?this.$onRemoveRange(e):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._emit("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._emit("removeRange",{ranges:e}),this.rangeCount==0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._emit("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.selectionLead),s=this.session.documentToScreenPosition(this.selectionAnchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)d--;if(d>0){var m=0;while(r[m].isEmpty())m++}for(var g=d;g>=m;g--)r[g].isEmpty()&&r.splice(g,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=this.session,i=this.selection,o=i.rangeList,u,a=i._eventRegistry;i._eventRegistry={};var f=new s(r);this.inVirtualSelectionMode=!0;for(var l=o.ranges.length;l--;){if(n)while(l>0&&o.ranges[l].start.row==o.ranges[l-1].end.row)l--;f.fromOrientedRange(o.ranges[l]),this.selection=r.selection=f;var c=e.exec(this,t||{});!u==undefined&&(u=c),f.toOrientedRange(o.ranges[l])}f.detach(),this.selection=r.selection=i,this.inVirtualSelectionMode=!1,i._eventRegistry=a,i.mergeOverlappingRanges();var h=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),h&&h.from==h.to&&this.renderer.animateScrolling(h.from),u},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;rn.length||t.length<2||!t[1])return this.commands.exec("insertstring",this,e);for(var r=n.length;r--;){var i=n[r];i.isEmpty()||this.session.remove(i),this.session.insert(i.start,t[r])}},this.findAll=function(e,t,n){t=t||{},t.needle=e||t.needle,this.$search.set(t);var r=this.$search.findAll(this.session);if(!r.length)return 0;this.$blockScrolling+=1;var i=this.multiSelect;n||i.toSingleRange(r[0]);for(var s=r.length;s--;)i.addRange(r[s],!0);return this.$blockScrolling-=1,r.length},this.selectMoreLines=function(e,t){var n=this.selection.toOrientedRange(),r=n.cursor==n.end,s=this.session.documentToScreenPosition(n.cursor);this.selection.$desiredColumn&&(s.column=this.selection.$desiredColumn);var o=this.session.screenToDocumentPosition(s.row+e,s.column);if(!n.isEmpty())var u=this.session.documentToScreenPosition(r?n.end:n.start),a=this.session.screenToDocumentPosition(u.row+e,u.column);else var a=o;if(r){var f=i.fromPoints(o,a);f.cursor=f.start}else{var f=i.fromPoints(a,o);f.cursor=f.end}f.desiredColumn=s.column;if(!this.selection.inMultiSelectMode)this.selection.addRange(n);else if(t)var l=n.cursor;this.selection.addRange(f),l&&this.selection.substractPoint(l)},this.transposeSelections=function(e){var t=this.session,n=t.multiSelect,r=n.ranges;for(var i=r.length;i--;){var s=r[i];if(s.isEmpty()){var o=t.getWordRange(s.start.row,s.start.column);s.start.row=o.start.row,s.start.column=o.start.column,s.end.row=o.end.row,s.end.column=o.end.column}}n.mergeOverlappingRanges();var u=[];for(var i=r.length;i--;){var s=r[i];u.unshift(t.getTextRange(s))}e<0?u.unshift(u.pop()):u.push(u.shift());for(var i=r.length;i--;){var s=r[i],o=s.clone();t.replace(s,u[i]),s.start.row=o.start.row,s.start.column=o.start.column}},this.selectMore=function(e,t){var n=this.session,r=n.multiSelect,i=r.toOrientedRange();if(i.isEmpty()){var i=n.getWordRange(i.start.row,i.start.column);i.cursor=e==-1?i.start:i.end,this.multiSelect.addRange(i);return}var s=n.getTextRange(i),o=h(n,s,e);o&&(o.cursor=e==-1?o.start:o.end,this.multiSelect.addRange(o)),t&&this.multiSelect.substractPoint(i.cursor)},this.alignCursors=function(){var e=this.session,t=e.multiSelect,n=t.ranges;if(!n.length){var r=this.selection.getRange(),s=r.start.row,o=r.end.row,u=this.session.doc.removeLines(s,o);u=this.$reAlignText(u),this.session.doc.insertLines(s,u),r.start.column=0,r.end.column=u[u.length-1].length,this.selection.setRange(r)}else{var f=-1,l=n.filter(function(e){if(e.cursor.row==f)return!0;f=e.cursor.row});t.$onRemoveRange(l);var c=0,h=Infinity,p=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>c&&(c=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=c,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e){function o(e){return a.stringRepeat(" ",e)}function u(e){return e[2]?o(r)+e[2]+o(i-e[2].length+s)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function f(e){return e[2]?o(r+i-e[2].length)+e[2]+o(s," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?o(r)+e[2]+o(s)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var t=!0,n=!0,r,i,s;return e.map(function(e){var o=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return o?r==null?(r=o[1].length,i=o[2].length,s=o[3].length,o):(r+i+s!=o[1].length+o[2].length+o[3].length&&(n=!1),r!=o[1].length&&(t=!1),r>o[1].length&&(r=o[1].length),io[3].length&&(s=o[3].length),o):[e]}).map(t?n?f:u:l)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t.multiSelect||(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.removeEventListener("addRange",this.$onAddRange),n.multiSelect.removeEventListener("removeRange",this.$onRemoveRange),n.multiSelect.removeEventListener("multiSelect",this.$onMultiSelect),n.multiSelect.removeEventListener("singleSelect",this.$onSingleSelect)),t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0}})}),define("ace/mouse/multi_select_handler",["require","exports","module","ace/lib/event"],function(e,t,n){function i(e,t){return e.row==t.row&&e.column==t.column}function s(e){var t=e.domEvent,n=t.altKey,s=t.shiftKey,o=e.getAccelKey(),u=e.getButton();if(e.editor.inMultiSelectMode&&u==2){e.editor.textInput.onContextMenu(e.domEvent);return}if(!o&&!n){u==0&&e.editor.inMultiSelectMode&&e.editor.exitMultiSelectMode();return}var a=e.editor,f=a.selection,l=a.inMultiSelectMode,c=e.getDocumentPosition(),h=f.getCursor(),p=e.inSelection()||f.isEmpty()&&i(c,h),d=e.x,v=e.y,m=function(e){d=e.clientX,v=e.clientY},g=function(){var e=a.renderer.pixelToScreenCoordinates(d,v),t=y.screenToDocumentPosition(e.row,e.column);if(i(w,e)&&i(t,f.selectionLead))return;w=e,a.selection.moveCursorToPosition(t),a.selection.clearSelection(),a.renderer.scrollCursorIntoView(),a.removeSelectionMarkers(x),x=f.rectangularRangeBlock(w,b),x.forEach(a.addSelectionMarker,a),a.updateSelectionMarkers()},y=a.session,b=a.renderer.pixelToScreenCoordinates(d,v),w=b;if(o&&!s&&!n&&u==0){if(!l&&p)return;if(!l){var E=f.toOrientedRange();a.addSelectionMarker(E)}var S=f.rangeList.rangeAtPoint(c);a.once("mouseup",function(){var e=f.toOrientedRange();S&&e.isEmpty()&&i(S.cursor,e.cursor)?f.substractPoint(e.cursor):(E&&(a.removeSelectionMarker(E),f.addRange(E)),f.addRange(e))})}else if(n&&u==0){e.stop(),l&&!o?f.toSingleRange():!l&&o&&f.addRange();var x=[];s?(b=y.documentToScreenPosition(f.lead),g()):(f.moveCursorToPosition(c),f.clearSelection());var T=function(e){clearInterval(C),a.removeSelectionMarkers(x);for(var t=0;t20&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})}}).call(o.prototype);var u=function(e,t,n){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var r=null,o=Object.create(i),u=this;this.$worker={},this.$worker.terminate=function(){},this.$worker.postMessage=function(e){u.messageBuffer.push(e),r&&setTimeout(a)};var a=function(){var e=u.messageBuffer.shift();e.command?r[e.command].apply(r,e.args):e.event&&o._emit(e.event,e.data)};o.postMessage=function(e){u.onMessage({data:e})},o.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},o.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},s.loadModule(["worker",t],function(e){r=new e[n](o);while(u.messageBuffer.length)a()})};u.prototype=o.prototype,t.UIWorkerClient=u,t.WorkerClient=o}),define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session,i=this.$pos;this.pos=t.createAnchor(i.row,i.column),this.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.pos.on("change",function(t){n.removeMarker(e.markerId),e.markerId=n.addMarker(new r(t.value.row,t.value.column,t.value.row,t.value.column+e.length),e.mainClass,null,!1)}),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1),n.on("change",function(i){e.removeMarker(n.markerId),n.markerId=e.addMarker(new r(i.value.row,i.value.column,i.value.row,i.value.column+t.length),t.othersClass,null,!1)})})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&n.start.column<=this.pos.column+this.length+1){var s=n.start.column-this.pos.column;this.length+=i;if(!this.session.$fromUndo){if(t.action==="insertText")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};u.row===n.start.row&&n.start.column=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};u.row===n.start.row&&n.start.column=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.pos.detach();for(var e=0;ef){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=this.getFoldWidget(e,u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;border-radius: 2px;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}); - (function() { - window.require(["ace/ace"], function(a) { - a && a.config.init(); - if (!window.ace) - window.ace = {}; - for (var key in a) if (a.hasOwnProperty(key)) - ace[key] = a[key]; - }); - })(); - \ No newline at end of file diff --git a/superx/saiku/js/ace/ext-keybinding_menu.js b/superx/saiku/js/ace/ext-keybinding_menu.js deleted file mode 100644 index 22e8834..0000000 --- a/superx/saiku/js/ace/ext-keybinding_menu.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/ext/keybinding_menu",["require","exports","module","ace/editor","ace/ext/menu_tools/overlay_page","ace/ext/menu_tools/get_editor_keyboard_shortcuts"],function(e,t,n){function i(t){if(!document.getElementById("kbshortcutmenu")){var n=e("./menu_tools/overlay_page").overlayPage,r=e("./menu_tools/get_editor_keyboard_shortcuts").getEditorKeybordShortcuts,i=r(t),s=document.createElement("div"),o=i.reduce(function(e,t){return e+'
'+t.command+" : "+''+t.key+"
"},"");s.id="kbshortcutmenu",s.innerHTML="

Keyboard Shortcuts

"+o+"",n(t,s,"0","0","0",null)}}var r=e("ace/editor").Editor;n.exports.init=function(e){r.prototype.showKeyboardShortcuts=function(){i(this)},e.commands.addCommands([{name:"showKeyboardShortcuts",bindKey:{win:"Ctrl-Alt-h",mac:"Command-Alt-h"},exec:function(e,t){e.showKeyboardShortcuts()}}])}}),define("ace/ext/menu_tools/overlay_page",["require","exports","module","ace/lib/dom"],function(e,t,n){var r=e("../../lib/dom"),i="#ace_settingsmenu, #kbshortcutmenu {background-color: #F7F7F7;color: black;box-shadow: -5px 4px 5px rgba(126, 126, 126, 0.55);padding: 1em 0.5em 2em 1em;overflow: auto;position: absolute;margin: 0;bottom: 0;right: 0;top: 0;z-index: 9991;cursor: default;}.ace_dark #ace_settingsmenu, .ace_dark #kbshortcutmenu {box-shadow: -20px 10px 25px rgba(126, 126, 126, 0.25);background-color: rgba(255, 255, 255, 0.6);color: black;}.ace_optionsMenuEntry:hover {background-color: rgba(100, 100, 100, 0.1);-webkit-transition: all 0.5s;transition: all 0.3s}.ace_closeButton {background: rgba(245, 146, 146, 0.5);border: 1px solid #F48A8A;border-radius: 50%;padding: 7px;position: absolute;right: -8px;top: -8px;z-index: 1000;}.ace_closeButton{background: rgba(245, 146, 146, 0.9);}.ace_optionsMenuKey {color: darkslateblue;font-weight: bold;}.ace_optionsMenuCommand {color: darkcyan;font-weight: normal;}";r.importCssString(i),n.exports.overlayPage=function(t,n,i,s,o,u){function l(e){e.keyCode===27&&a.click()}i=i?"top: "+i+";":"",o=o?"bottom: "+o+";":"",s=s?"right: "+s+";":"",u=u?"left: "+u+";":"";var a=document.createElement("div"),f=document.createElement("div");a.style.cssText="margin: 0; padding: 0; position: fixed; top:0; bottom:0; left:0; right:0;z-index: 9990; background-color: rgba(0, 0, 0, 0.3);",a.addEventListener("click",function(){document.removeEventListener("keydown",l),a.parentNode.removeChild(a),t.focus(),a=null}),document.addEventListener("keydown",l),f.style.cssText=i+s+o+u,f.addEventListener("click",function(e){e.stopPropagation()});var c=r.createElement("div");c.style.position="relative";var h=r.createElement("div");h.className="ace_closeButton",h.addEventListener("click",function(){a.click()}),c.appendChild(h),f.appendChild(c),f.appendChild(n),a.appendChild(f),document.body.appendChild(a),t.blur()}}),define("ace/ext/menu_tools/get_editor_keyboard_shortcuts",["require","exports","module","ace/lib/keys"],function(e,t,n){var r=e("../../lib/keys");n.exports.getEditorKeybordShortcuts=function(e){var t=r.KEY_MODS,n=[],i={};return e.keyBinding.$handlers.forEach(function(e){var r=e.commandKeyBinding;for(var s in r){var o=parseInt(s);o==-1?o="":isNaN(o)?o=s:o=""+(o&t.command?"Cmd-":"")+(o&t.ctrl?"Ctrl-":"")+(o&t.alt?"Alt-":"")+(o&t.shift?"Shift-":"");for(var u in r[s]){var a=r[s][u];typeof a!="string"&&(a=a.name),i[a]?i[a].key+="|"+o+u:(i[a]={key:o+u,command:a},n.push(i[a]))}}}),n}}) \ No newline at end of file diff --git a/superx/saiku/js/ace/ext-options.js b/superx/saiku/js/ace/ext-options.js deleted file mode 100644 index a569268..0000000 --- a/superx/saiku/js/ace/ext-options.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/ext/options",["require","exports","module"],function(e,t,n){function u(e,t){var n=document.getElementById(e);localStorage&&localStorage.getItem(e)&&(n.checked=localStorage.getItem(e)=="1");var r=function(){t(!!n.checked),saveOption(n)};n.onclick=r,r()}function a(e,t){var n=document.getElementById(e);localStorage&&localStorage.getItem(e)&&(n.value=localStorage.getItem(e));var r=function(){t(n.value),saveOption(n)};n.onchange=r,r()}function f(e,t){e.forEach(function(e){var n=document.createElement("option");n.setAttribute("value",e.name),n.innerHTML=e.desc,t.appendChild(n)})}function l(e,t){if(Array.isArray(e)){f(e,t);return}for(var n in e){var r=document.createElement("optgroup");r.setAttribute("label",n),f(e[n],r),t.appendChild(r)}}function c(e){if(e.values){var t=dom.createElement("select");t.setAttribute("size",e.visibleSize||1),l(e.values,t)}else var t=dom.createElement("checkbox");return t.setAttribute("name","opt_"+e.name),t}function h(e){if(e.values){var t=dom.createElement("select");t.setAttribute("size",e.visibleSize||1),l(e.values,t)}else var t=dom.createElement("checkbox");return t.setAttribute("name","opt_"+e.name),t}var r=modelist.modesByName,i=[["Document",function(e){doclist.loadDoc(e,function(e){if(!e)return;e=env.split.setSession(e),updateUIEditorOptions(),env.editor.focus()})},doclist.all],["Mode",function(e){env.editor.session.setMode(r[e].mode||r.text.mode),env.editor.session.modeName=e},function(e){return env.editor.session.modeName||"text"},modelist.modes],["Split",function(e){var t=env.split;if(e=="none")t.getSplits()==2&&(env.secondSession=t.getEditor(1).session),t.setSplits(1);else{var n=t.getSplits()==1;e=="below"?t.setOrientation(t.BELOW):t.setOrientation(t.BESIDE),t.setSplits(2);if(n){var r=env.secondSession||t.getEditor(0).session,i=t.setSession(r,1);i.name=r.name}}},["None","Beside","Below"]],["Theme",function(e){if(!e)return;env.editor.setTheme("ace/theme/"+e),themeEl.selectedValue=e},function(){return env.editor.getTheme()},{Bright:{chrome:"Chrome",clouds:"Clouds",crimson_editor:"Crimson Editor",dawn:"Dawn",dreamweaver:"Dreamweaver",eclipse:"Eclipse",github:"GitHub",solarized_light:"Solarized Light",textmate:"TextMate",tomorrow:"Tomorrow",xcode:"XCode"},Dark:{ambiance:"Ambiance",chaos:"Chaos",clouds_midnight:"Clouds Midnight",cobalt:"Cobalt",idle_fingers:"idleFingers",kr_theme:"krTheme",merbivore:"Merbivore",merbivore_soft:"Merbivore Soft",mono_industrial:"Mono Industrial",monokai:"Monokai",pastel_on_dark:"Pastel on dark",solarized_dark:"Solarized Dark",twilight:"Twilight",tomorrow_night:"Tomorrow Night",tomorrow_night_blue:"Tomorrow Night Blue",tomorrow_night_bright:"Tomorrow Night Bright",tomorrow_night_eighties:"Tomorrow Night 80s",vibrant_ink:"Vibrant Ink"}}],["Code Folding",function(e){env.editor.getSession().setFoldStyle(e),env.editor.setShowFoldWidgets(e!=="manual")},["manual","mark begin","mark begin and end"]],["Soft Wrap",function(e){e=e.toLowerCase();var t=env.editor.getSession(),n=env.editor.renderer;t.setUseWrapMode(e=="off");var r=parseInt(e)||null;n.setPrintMarginColumn(r||80),t.setWrapLimitRange(r,r)},["Off","40 Chars","80 Chars","Free"]],["Key Binding",function(e){env.editor.setKeyboardHandler(keybindings[e])},["Ace","Vim","Emacs","Custom"]],["Font Size",function(e){env.split.setFontSize(e+"px")},[10,11,12,14,16,20,24]],["Full Line Selection",function(e){env.editor.setSelectionStyle(e?"line":"text")}],["Highlight Active Line",function(e){env.editor.setHighlightActiveLine(e)}],["Show Invisibles",function(e){env.editor.setShowInvisibles(e)}],["Show Gutter",function(e){env.editor.renderer.setShowGutter(e)}],["Show Indent Guides",function(e){env.editor.renderer.setDisplayIndentGuides(e)}],["Show Print Margin",function(e){env.editor.renderer.setShowPrintMargin(e)}],["Persistent HScroll",function(e){env.editor.renderer.setHScrollBarAlwaysVisible(e)}],["Animate Scrolling",function(e){env.editor.setAnimatedScroll(e)}],["Use Soft Tab",function(e){env.editor.getSession().setUseSoftTabs(e)}],["Highlight Selected Word",function(e){env.editor.setHighlightSelectedWord(e)}],["Enable Behaviours",function(e){env.editor.setBehavioursEnabled(e)}],["Fade Fold Widgets",function(e){env.editor.setFadeFoldWidgets(e)}],["Show Token info",function(e){env.editor.setFadeFoldWidgets(e)}]],o=function(e){var t=[],n=document.createElement("div");n.style.cssText="position: absolute; overflow: hidden";var r=document.createElement("div");return r.style.cssText="width: 120%;height:100%;overflow: scroll",n.appendChild(r),t.push(""),e.forEach(function(e){}),t.push("","",""),t.push("
",'',"",'',"
"),n};o(i)}) \ No newline at end of file diff --git a/superx/saiku/js/ace/ext-searchbox.js b/superx/saiku/js/ace/ext-searchbox.js deleted file mode 100644 index e265f54..0000000 --- a/superx/saiku/js/ace/ext-searchbox.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/ext/searchbox",["require","exports","module","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/keyboard/hash_handler","ace/lib/keys"],function(e,t,n){var r=e("../lib/dom"),i=e("../lib/lang"),s=e("../lib/event"),o="/* ------------------------------------------------------------------------------------------* Editor Search Form* --------------------------------------------------------------------------------------- */.ace_search {background-color: #ddd;border: 1px solid #cbcbcb;border-top: 0 none;max-width: 297px;overflow: hidden;margin: 0;padding: 4px;padding-right: 6px;padding-bottom: 0;position: absolute;top: 0px;z-index: 99;}.ace_search.left {border-left: 0 none;border-radius: 0px 0px 5px 0px;left: 0;}.ace_search.right {border-radius: 0px 0px 0px 5px;border-right: 0 none;right: 0;}.ace_search_form, .ace_replace_form {border-radius: 3px;border: 1px solid #cbcbcb;float: left;margin-bottom: 4px;overflow: hidden;}.ace_search_form.ace_nomatch {outline: 1px solid red;}.ace_search_field {background-color: white;border-right: 1px solid #cbcbcb;border: 0 none;-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;display: block;float: left;height: 22px;outline: 0;padding: 0 7px;width: 214px;margin: 0;}.ace_searchbtn,.ace_replacebtn {background: #fff;border: 0 none;border-left: 1px solid #dcdcdc;cursor: pointer;display: block;float: left;height: 22px;margin: 0;padding: 0;position: relative;}.ace_searchbtn:last-child,.ace_replacebtn:last-child {border-top-right-radius: 3px;border-bottom-right-radius: 3px;}.ace_searchbtn:disabled {background: none;cursor: default;}.ace_searchbtn {background-position: 50% 50%;background-repeat: no-repeat;width: 27px;}.ace_searchbtn.prev {background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=); }.ace_searchbtn.next {background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=); }.ace_searchbtn_close {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;border-radius: 50%;border: 0 none;color: #656565;cursor: pointer;display: block;float: right;font-family: Arial;font-size: 16px;height: 14px;line-height: 16px;margin: 5px 1px 9px 5px;padding: 0;text-align: center;width: 14px;}.ace_searchbtn_close:hover {background-color: #656565;background-position: 50% 100%;color: white;}.ace_replacebtn.prev {width: 54px}.ace_replacebtn.next {width: 27px}.ace_button {margin-left: 2px;cursor: pointer;-webkit-user-select: none;-moz-user-select: none;-o-user-select: none;-ms-user-select: none;user-select: none;overflow: hidden;opacity: 0.7;border: 1px solid rgba(100,100,100,0.23);padding: 1px;-moz-box-sizing: border-box;box-sizing: border-box;color: black;}.ace_button:hover {background-color: #eee;opacity:1;}.ace_button:active {background-color: #ddd;}.ace_button.checked {border-color: #3399ff;opacity:1;}.ace_search_options{margin-bottom: 3px;text-align: right;-webkit-user-select: none;-moz-user-select: none;-o-user-select: none;-ms-user-select: none;user-select: none;}",u=e("../keyboard/hash_handler").HashHandler,a=e("../lib/keys");r.importCssString(o,"ace_searchbox");var f=''.replace(/>\s+/g,">"),l=function(e,t,n){var i=r.createElement("div");i.innerHTML=f,this.element=i.firstChild,this.$init(),this.setEditor(e)};(function(){this.setEditor=function(e){e.searchBox=this,e.container.appendChild(this.element),this.editor=e},this.$initElements=function(e){this.searchBox=e.querySelector(".ace_search_form"),this.replaceBox=e.querySelector(".ace_replace_form"),this.searchOptions=e.querySelector(".ace_search_options"),this.regExpOption=e.querySelector("[action=toggleRegexpMode]"),this.caseSensitiveOption=e.querySelector("[action=toggleCaseSensitive]"),this.wholeWordOption=e.querySelector("[action=toggleWholeWords]"),this.searchInput=this.searchBox.querySelector(".ace_search_field"),this.replaceInput=this.replaceBox.querySelector(".ace_search_field")},this.$init=function(){var e=this.element;this.$initElements(e);var t=this;s.addListener(e,"mousedown",function(e){setTimeout(function(){t.activeInput.focus()},0),s.stopPropagation(e)}),s.addListener(e,"click",function(e){var n=e.target||e.srcElement,r=n.getAttribute("action");r&&t[r]?t[r]():t.$searchBarKb.commands[r]&&t.$searchBarKb.commands[r].exec(t),s.stopPropagation(e)}),s.addCommandKeyListener(e,function(e,n,r){var i=a.keyCodeToString(r),o=t.$searchBarKb.findKeyCommand(n,i);o&&o.exec&&(o.exec(t),s.stopEvent(e))}),this.$onChange=i.delayedCall(function(){t.find(!1,!1)}),s.addListener(this.searchInput,"input",function(){t.$onChange.schedule(20)}),s.addListener(this.searchInput,"focus",function(){t.activeInput=t.searchInput,t.searchInput.value&&t.highlight()}),s.addListener(this.replaceInput,"focus",function(){t.activeInput=t.replaceInput,t.searchInput.value&&t.highlight()})},this.$closeSearchBarKb=new u([{bindKey:"Esc",name:"closeSearchBar",exec:function(e){e.searchBox.hide()}}]),this.$searchBarKb=new u,this.$searchBarKb.bindKeys({"Ctrl-f|Command-f|Ctrl-H|Command-Option-F":function(e){var t=e.isReplace=!e.isReplace;e.replaceBox.style.display=t?"":"none",e[t?"replaceInput":"searchInput"].focus()},"Ctrl-G|Command-G":function(e){e.findNext()},"Ctrl-Shift-G|Command-Shift-G":function(e){e.findPrev()},esc:function(e){setTimeout(function(){e.hide()})},Return:function(e){e.activeInput==e.replaceInput&&e.replace(),e.findNext()},"Shift-Return":function(e){e.activeInput==e.replaceInput&&e.replace(),e.findPrev()},Tab:function(e){(e.activeInput==e.replaceInput?e.searchInput:e.replaceInput).focus()}}),this.$searchBarKb.addCommands([{name:"toggleRegexpMode",bindKey:{win:"Alt-R|Alt-/",mac:"Ctrl-Alt-R|Ctrl-Alt-/"},exec:function(e){e.regExpOption.checked=!e.regExpOption.checked,e.$syncOptions()}},{name:"toggleCaseSensitive",bindKey:{win:"Alt-C|Alt-I",mac:"Ctrl-Alt-R|Ctrl-Alt-I"},exec:function(e){e.caseSensitiveOption.checked=!e.caseSensitiveOption.checked,e.$syncOptions()}},{name:"toggleWholeWords",bindKey:{win:"Alt-B|Alt-W",mac:"Ctrl-Alt-B|Ctrl-Alt-W"},exec:function(e){e.wholeWordOption.checked=!e.wholeWordOption.checked,e.$syncOptions()}}]),this.$syncOptions=function(){r.setCssClass(this.regExpOption,"checked",this.regExpOption.checked),r.setCssClass(this.wholeWordOption,"checked",this.wholeWordOption.checked),r.setCssClass(this.caseSensitiveOption,"checked",this.caseSensitiveOption.checked),this.find(!1,!1)},this.highlight=function(e){this.editor.session.highlight(e||this.editor.$search.$options.re),this.editor.renderer.updateBackMarkers()},this.find=function(e,t){var n=this.editor.find(this.searchInput.value,{skipCurrent:e,backwards:t,wrap:!0,regExp:this.regExpOption.checked,caseSensitive:this.caseSensitiveOption.checked,wholeWord:this.wholeWordOption.checked}),i=!n&&this.searchInput.value;r.setCssClass(this.searchBox,"ace_nomatch",i),this.editor._emit("findSearchBox",{match:!i}),this.highlight()},this.findNext=function(){this.find(!0,!1)},this.findPrev=function(){this.find(!0,!0)},this.replace=function(){this.editor.getReadOnly()||this.editor.replace(this.replaceInput.value)},this.replaceAndFindNext=function(){this.editor.getReadOnly()||(this.editor.replace(this.replaceInput.value),this.findNext())},this.replaceAll=function(){this.editor.getReadOnly()||this.editor.replaceAll(this.replaceInput.value)},this.hide=function(){this.element.style.display="none",this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb),this.editor.focus()},this.show=function(e,t){this.element.style.display="",this.replaceBox.style.display=t?"":"none",this.isReplace=t,e&&(this.searchInput.value=e),this.searchInput.focus(),this.searchInput.select(),this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb)}}).call(l.prototype),t.SearchBox=l,t.Search=function(e,t){var n=e.searchBox||new l(e);n.show(e.session.getTextRange(),t)}}) \ No newline at end of file diff --git a/superx/saiku/js/ace/ext-static_highlight.js b/superx/saiku/js/ace/ext-static_highlight.js deleted file mode 100644 index 88b4578..0000000 --- a/superx/saiku/js/ace/ext-static_highlight.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/ext/static_highlight",["require","exports","module","ace/edit_session","ace/layer/text","ace/config","ace/lib/dom"],function(e,t,n){var r=e("../edit_session").EditSession,i=e("../layer/text").Text,s=".ace_static_highlight {font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'Droid Sans Mono', monospace;font-size: 12px;}.ace_static_highlight .ace_gutter {width: 25px !important;display: block;float: left;text-align: right;padding: 0 3px 0 0;margin-right: 3px;position: static !important;}.ace_static_highlight .ace_line { clear: both; }.ace_static_highlight .ace_gutter-cell {-moz-user-select: -moz-none;-khtml-user-select: none;-webkit-user-select: none;user-select: none;}",o=e("../config"),u=e("../lib/dom");t.render=function(e,n,i,s,u,a){function c(){var r=t.renderSync(e,n,i,s,u);return a?a(r):r}var f=0,l=r.prototype.$modes;return typeof i=="string"&&(f++,o.loadModule(["theme",i],function(e){i=e,--f||c()})),typeof n=="string"&&(f++,o.loadModule(["mode",n],function(e){l[n]||(l[n]=new e.Mode),n=l[n],--f||c()})),f||c()},t.renderSync=function(e,t,n,o,u){o=parseInt(o||1,10);var a=new r("");a.setUseWorker(!1),a.setMode(t);var f=new i(document.createElement("div"));f.setSession(a),f.config={characterWidth:10,lineHeight:20},a.setValue(e);var l=[],c=a.getLength();for(var h=0;h"),u||l.push(""+(h+o)+""),f.$renderLine(l,h,!0,!1),l.push("");var p="
"+"
"+l.join("")+"
"+"
";return f.destroy(),{css:s+n.cssText,html:p}},t.highlight=function(e,n,r){var i=e.className.match(/lang-(\w+)/),s=n.mode||i&&"ace/mode/"+i[1];if(!s)return!1;var o=n.theme||"ace/theme/textmate",a="",f=[];if(e.firstElementChild){var l=0;for(var c=0;c");return}e.push("")}var s=null,o={mode:"Mode:",gutter:"Display Gutter:",theme:"Theme:",fontSize:"Font Size:",softWrap:"Soft Wrap:",keybindings:"Keyboard",showPrintMargin:"Show Print Margin:",useSoftTabs:"Use Soft Tabs:",showInvisibles:"Show Invisibles"},u={mode:{text:"Plain",javascript:"JavaScript",xml:"XML",html:"HTML",css:"CSS",scss:"SCSS",python:"Python",php:"PHP",java:"Java",ruby:"Ruby",c_cpp:"C/C++",coffee:"CoffeeScript",json:"json",perl:"Perl",clojure:"Clojure",ocaml:"OCaml",csharp:"C#",haxe:"haXe",svg:"SVG",textile:"Textile",groovy:"Groovy",liquid:"Liquid",Scala:"Scala"},theme:{clouds:"Clouds",clouds_midnight:"Clouds Midnight",cobalt:"Cobalt",crimson_editor:"Crimson Editor",dawn:"Dawn",eclipse:"Eclipse",idle_fingers:"Idle Fingers",kr_theme:"Kr Theme",merbivore:"Merbivore",merbivore_soft:"Merbivore Soft",mono_industrial:"Mono Industrial",monokai:"Monokai",pastel_on_dark:"Pastel On Dark",solarized_dark:"Solarized Dark",solarized_light:"Solarized Light",textmate:"Textmate",twilight:"Twilight",vibrant_ink:"Vibrant Ink"},gutter:s,fontSize:{"10px":"10px","11px":"11px","12px":"12px","14px":"14px","16px":"16px"},softWrap:{off:"Off",40:"40",80:"80",free:"Free"},keybindings:{ace:"ace",vim:"vim",emacs:"emacs"},showPrintMargin:s,useSoftTabs:s,showInvisibles:s},a=[];a.push("");for(var l in i)a.push(""),a.push("");a.push("
SettingValue
",o[l],""),f(a,l,u[l],i[l]),a.push("
"),e.innerHTML=a.join("");var c=function(e){var t=e.currentTarget;n.setOption(t.title,t.value)},h=function(e){var t=e.currentTarget;n.setOption(t.title,t.checked)},p=e.getElementsByTagName("select");for(var d=0;d30&&this.$data.shift()},get:function(e){return e=e||1,this.$data.slice(this.$data.length-e,this.$data.length).reverse().join("\n")},pop:function(){return this.$data.length>1&&this.$data.pop(),this.get()},rotate:function(){return this.$data.unshift(this.$data.pop()),this.get()}}}),define("ace/incremental_search",["require","exports","module","ace/lib/oop","ace/range","ace/search","ace/search_highlight","ace/commands/incremental_search_commands","ace/lib/dom","ace/commands/command_manager","ace/editor","ace/config"],function(e,t,n){function f(){this.$options={wrap:!1,skipCurrent:!1},this.$keyboardHandler=new a(this)}var r=e("./lib/oop"),i=e("./range").Range,s=e("./search").Search,o=e("./search_highlight").SearchHighlight,u=e("./commands/incremental_search_commands"),a=u.IncrementalSearchKeyboardHandler;r.inherits(f,s),function(){this.activate=function(e,t){this.$editor=e,this.$startPos=this.$currentPos=e.getCursorPosition(),this.$options.needle="",this.$options.backwards=t,e.keyBinding.addKeyboardHandler(this.$keyboardHandler),this.$mousedownHandler=e.addEventListener("mousedown",this.onMouseDown.bind(this)),this.selectionFix(e),this.statusMessage(!0)},this.deactivate=function(e){this.cancelSearch(e),this.$editor.keyBinding.removeKeyboardHandler(this.$keyboardHandler),this.$mousedownHandler&&(this.$editor.removeEventListener("mousedown",this.$mousedownHandler),delete this.$mousedownHandler),this.message("")},this.selectionFix=function(e){e.selection.isEmpty()&&!e.session.$emacsMark&&e.clearSelection()},this.highlight=function(e){var t=this.$editor.session,n=t.$isearchHighlight=t.$isearchHighlight||t.addDynamicMarker(new o(null,"ace_isearch-result","text"));n.setRegexp(e),t._emit("changeBackMarker")},this.cancelSearch=function(e){var t=this.$editor;return this.$prevNeedle=this.$options.needle,this.$options.needle="",e?(t.moveCursorToPosition(this.$startPos),this.$currentPos=this.$startPos):t.pushEmacsMark&&t.pushEmacsMark(this.$startPos,!1),this.highlight(null),i.fromPoints(this.$currentPos,this.$currentPos)},this.highlightAndFindWithNeedle=function(e,t){if(!this.$editor)return null;var n=this.$options;t&&(n.needle=t.call(this,n.needle||"")||"");if(n.needle.length===0)return this.statusMessage(!0),this.cancelSearch(!0);n.start=this.$currentPos;var r=this.$editor.session,s=this.find(r);return s&&(n.backwards&&(s=i.fromPoints(s.end,s.start)),this.$editor.moveCursorToPosition(s.end),e&&(this.$currentPos=s.end),this.highlight(n.re)),this.statusMessage(s),s},this.addChar=function(e){return this.highlightAndFindWithNeedle(!1,function(t){return t+e})},this.removeChar=function(e){return this.highlightAndFindWithNeedle(!1,function(e){return e.length>0?e.substring(0,e.length-1):e})},this.next=function(e){return e=e||{},this.$options.backwards=!!e.backwards,this.$currentPos=this.$editor.getCursorPosition(),this.highlightAndFindWithNeedle(!0,function(t){return e.useCurrentOrPrevSearch&&t.length===0?this.$prevNeedle||"":t})},this.onMouseDown=function(e){return this.deactivate(),!0},this.statusMessage=function(e){var t=this.$options,n="";n+=t.backwards?"reverse-":"",n+="isearch: "+t.needle,n+=e?"":" (not found)",this.message(n)},this.message=function(e){this.$editor.showCommandLine?(this.$editor.showCommandLine(e),this.$editor.focus()):console.log(e)}}.call(f.prototype),t.IncrementalSearch=f;var l=e("./lib/dom");l.importCssString&&l.importCssString(".ace_marker-layer .ace_isearch-result { position: absolute; z-index: 6; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;}div.ace_isearch-result { border-radius: 4px; background-color: rgba(255, 200, 0, 0.5); box-shadow: 0 0 4px rgb(255, 200, 0);}.ace_dark div.ace_isearch-result { background-color: rgb(100, 110, 160); box-shadow: 0 0 4px rgb(80, 90, 140);}","incremental-search-highlighting");var c=e("./commands/command_manager");(function(){this.setupIncrementalSearch=function(e,t){if(this.usesIncrementalSearch==t)return;this.usesIncrementalSearch=t;var n=u.iSearchStartCommands,r=t?"addCommands":"removeCommands";this[r](n)}}).call(c.CommandManager.prototype);var h=e("./editor").Editor;e("./config").defineOptions(h.prototype,"editor",{useIncrementalSearch:{set:function(e){this.keyBinding.$handlers.forEach(function(t){t.setupIncrementalSearch&&t.setupIncrementalSearch(this,e)}),this._emit("incrementalSearchSettingChanged",{isEnabled:e})}}})}),define("ace/commands/incremental_search_commands",["require","exports","module","ace/config","ace/lib/oop","ace/keyboard/hash_handler","ace/commands/occur_commands"],function(e,t,n){function u(e){this.$iSearch=e}var r=e("../config"),i=e("../lib/oop"),s=e("../keyboard/hash_handler").HashHandler,o=e("./occur_commands").occurStartCommand;t.iSearchStartCommands=[{name:"iSearch",bindKey:{win:"Ctrl-F",mac:"Command-F"},exec:function(e,t){r.loadModule(["core","ace/incremental_search"],function(n){var r=n.iSearch=n.iSearch||new n.IncrementalSearch;r.activate(e,t.backwards),t.jumpToFirstMatch&&r.next(t)})},readOnly:!0},{name:"iSearchBackwards",exec:function(e,t){e.execCommand("iSearch",{backwards:!0})},readOnly:!0},{name:"iSearchAndGo",bindKey:{win:"Ctrl-K",mac:"Command-G"},exec:function(e,t){e.execCommand("iSearch",{jumpToFirstMatch:!0,useCurrentOrPrevSearch:!0})},readOnly:!0},{name:"iSearchBackwardsAndGo",bindKey:{win:"Ctrl-Shift-K",mac:"Command-Shift-G"},exec:function(e){e.execCommand("iSearch",{jumpToFirstMatch:!0,backwards:!0,useCurrentOrPrevSearch:!0})},readOnly:!0}],t.iSearchCommands=[{name:"restartSearch",bindKey:{win:"Ctrl-F",mac:"Command-F"},exec:function(e){e.cancelSearch(!0)},readOnly:!0,isIncrementalSearchCommand:!0},{name:"searchForward",bindKey:{win:"Ctrl-S|Ctrl-K",mac:"Ctrl-S|Command-G"},exec:function(e,t){t.useCurrentOrPrevSearch=!0,e.next(t)},readOnly:!0,isIncrementalSearchCommand:!0},{name:"searchBackward",bindKey:{win:"Ctrl-R|Ctrl-Shift-K",mac:"Ctrl-R|Command-Shift-G"},exec:function(e,t){t.useCurrentOrPrevSearch=!0,t.backwards=!0,e.next(t)},readOnly:!0,isIncrementalSearchCommand:!0},{name:"extendSearchTerm",exec:function(e,t){e.addChar(t)},readOnly:!0,isIncrementalSearchCommand:!0},{name:"extendSearchTermSpace",bindKey:"space",exec:function(e){e.addChar(" ")},readOnly:!0,isIncrementalSearchCommand:!0},{name:"shrinkSearchTerm",bindKey:"backspace",exec:function(e){e.removeChar()},readOnly:!0,isIncrementalSearchCommand:!0},{name:"confirmSearch",bindKey:"return",exec:function(e){e.deactivate()},readOnly:!0,isIncrementalSearchCommand:!0},{name:"cancelSearch",bindKey:"esc|Ctrl-G",exec:function(e){e.deactivate(!0)},readOnly:!0,isIncrementalSearchCommand:!0},{name:"occurisearch",bindKey:"Ctrl-O",exec:function(e){var t=i.mixin({},e.$options);e.deactivate(),o.exec(e.$editor,t)},readOnly:!0,isIncrementalSearchCommand:!0}],i.inherits(u,s),function(){this.attach=function(e){var n=this.$iSearch;s.call(this,t.iSearchCommands,e.commands.platform),this.$commandExecHandler=e.commands.addEventListener("exec",function(e){return e.command.isIncrementalSearchCommand?(e.stopPropagation(),e.preventDefault(),e.command.exec(n,e.args||{})):undefined})},this.detach=function(e){if(!this.$commandExecHandler)return;e.commands.removeEventListener("exec",this.$commandExecHandler),delete this.$commandExecHandler};var e=this.handleKeyboard;this.handleKeyboard=function(t,n,r,i){var s=e.call(this,t,n,r,i);if(s.command)return s;if(n==-1){var o=this.commands.extendSearchTerm;if(o)return{command:o,args:r}}return{command:"null",passEvent:n==0||n==4}}}.call(u.prototype),t.IncrementalSearchKeyboardHandler=u}),define("ace/commands/occur_commands",["require","exports","module","ace/config","ace/occur","ace/keyboard/hash_handler","ace/lib/oop"],function(e,t,n){function f(){}var r=e("../config"),i=e("../occur").Occur,s={name:"occur",exec:function(e,t){var n=!!e.session.$occur,r=(new i).enter(e,t);r&&!n&&f.installIn(e)},readOnly:!0},o=[{name:"occurexit",bindKey:"esc|Ctrl-G",exec:function(e){var t=e.session.$occur;if(!t)return;t.exit(e,{}),e.session.$occur||f.uninstallFrom(e)},readOnly:!0},{name:"occuraccept",bindKey:"enter",exec:function(e){var t=e.session.$occur;if(!t)return;t.exit(e,{translatePosition:!0}),e.session.$occur||f.uninstallFrom(e)},readOnly:!0}],u=e("../keyboard/hash_handler").HashHandler,a=e("../lib/oop");a.inherits(f,u),function(){this.isOccurHandler=!0,this.attach=function(e){u.call(this,o,e.commands.platform),this.$editor=e};var e=this.handleKeyboard;this.handleKeyboard=function(t,n,r,i){var s=e.call(this,t,n,r,i);return s&&s.command?s:undefined}}.call(f.prototype),f.installIn=function(e){var t=new this;e.keyBinding.addKeyboardHandler(t),e.commands.addCommands(o)},f.uninstallFrom=function(e){e.commands.removeCommands(o);var t=e.getKeyboardHandler();t.isOccurHandler&&e.keyBinding.removeKeyboardHandler(t)},t.occurStartCommand=s}),define("ace/occur",["require","exports","module","ace/lib/oop","ace/range","ace/search","ace/edit_session","ace/search_highlight","ace/lib/dom"],function(e,t,n){function a(){}var r=e("./lib/oop"),i=e("./range").Range,s=e("./search").Search,o=e("./edit_session").EditSession,u=e("./search_highlight").SearchHighlight;r.inherits(a,s),function(){this.enter=function(e,t){if(!t.needle)return!1;var n=e.getCursorPosition();this.displayOccurContent(e,t);var r=this.originalToOccurPosition(e.session,n);return e.moveCursorToPosition(r),!0},this.exit=function(e,t){var n=t.translatePosition&&e.getCursorPosition(),r=n&&this.occurToOriginalPosition(e.session,n);return this.displayOriginalContent(e),r&&e.moveCursorToPosition(r),!0},this.highlight=function(e,t){var n=e.$occurHighlight=e.$occurHighlight||e.addDynamicMarker(new u(null,"ace_occur-highlight","text"));n.setRegexp(t),e._emit("changeBackMarker")},this.displayOccurContent=function(e,t){this.$originalSession=e.session;var n=this.matchingLines(e.session,t),r=n.map(function(e){return e.content}),i=new o(r.join("\n"));i.$occur=this,i.$occurMatchingLines=n,e.setSession(i),this.highlight(i,t.re),i._emit("changeBackMarker")},this.displayOriginalContent=function(e){e.setSession(this.$originalSession)},this.originalToOccurPosition=function(e,t){var n=e.$occurMatchingLines,r={row:0,column:0};if(!n)return r;for(var i=0;i1)return r.inputBuffer.idle&&u[n]?u[n]:(r.inputBuffer.push(f,n),{command:"null",passEvent:!1});if(n.length==1&&(t==0||t==4))return{command:"null",passEvent:!0};if(n=="esc"&&t==0)return{command:i.stop}}else if(n=="ctrl-w")return{command:"removewordleft"}},attach:function(e){e.on("click",t.onCursorMove),s.currentMode!=="insert"&&r.coreCommands.stop.exec(e),e.$vimModeHandler=this,this.updateMacCompositionHandlers(e,!0)},detach:function(e){e.removeListener("click",t.onCursorMove),s.noMode(e),s.currentMode="normal",this.updateMacCompositionHandlers(e,!1)},actions:r.actions,getStatusText:function(){return s.currentMode=="insert"?"INSERT":s.onVisualMode?(s.onVisualLineMode?"VISUAL LINE ":"VISUAL ")+r.inputBuffer.status:r.inputBuffer.status}},t.onCursorMove=function(e){r.onCursorMove(e.editor,e),t.onCursorMove.scheduled=!1}}),define("ace/keyboard/vim/commands",["require","exports","module","ace/lib/lang","ace/keyboard/vim/maps/util","ace/keyboard/vim/maps/motions","ace/keyboard/vim/maps/operators","ace/keyboard/vim/maps/aliases","ace/keyboard/vim/registers"],function(e,t,n){"never use strict";function y(e){g.previous={action:{action:{fn:e}}}}var r=e("../../lib/lang"),i=e("./maps/util"),s=e("./maps/motions"),o=e("./maps/operators"),u=e("./maps/aliases"),a=e("./registers"),f=1,l=2,c=3,h=4,p=8,d=function(t,n,r){while(0t.$size.scrollerHeight&&(i=t.$size.scrollerHeight/2),t.scrollTop>r-i&&t.session.setScrollTop(r-i),t.scrollTop+t.$size.scrollerHeight1&&(r=r=="return"?"\n":r=="tab"?" ":r),d(function(){e.insert(r)},n||1),e.navigateLeft())}},R:{fn:function(e,t,n,r){i.insertMode(e),e.setOverwrite(!0)}},"~":{fn:function(e,t,n){d(function(){var t=e.selection.getRange();t.isEmpty()&&t.end.column++;var n=e.session.getTextRange(t),r=n.toUpperCase();r==n?e.navigateRight():e.session.replace(t,r)},n||1)}},"*":{fn:function(e,t,n,r){e.selection.selectWord(),e.findNext(),v(e);var i=e.selection.getRange();e.selection.setSelectionRange(i,!0)}},"#":{fn:function(e,t,n,r){e.selection.selectWord(),e.findPrevious(),v(e);var i=e.selection.getRange();e.selection.setSelectionRange(i,!0)}},m:{param:!0,fn:function(e,t,n,r){var i=e.session,s=i.vimMarkers||(i.vimMarkers={}),o=e.getCursorPosition();s[r]||(s[r]=e.session.doc.createAnchor(o)),s[r].setPosition(o.row,o.column,!0)}},n:{fn:function(e,t,n,r){var i=e.getLastSearchOptions();i.backwards=!1,e.selection.moveCursorRight(),e.selection.clearSelection(),e.findNext(i),v(e);var s=e.selection.getRange();s.end.row=s.start.row,s.end.column=s.start.column,e.selection.setSelectionRange(s,!0)}},N:{fn:function(e,t,n,r){var i=e.getLastSearchOptions();i.backwards=!0,e.findPrevious(i),v(e);var s=e.selection.getRange();s.end.row=s.start.row,s.end.column=s.start.column,e.selection.setSelectionRange(s,!0)}},v:{fn:function(e,t,n,r){e.selection.selectRight(),i.visualMode(e,!1)},acceptsMotion:!0},V:{fn:function(e,t,n,r){var s=e.getCursorPosition().row;e.selection.clearSelection(),e.selection.moveCursorTo(s,0),e.selection.selectLineEnd(),e.selection.visualLineStart=s,i.visualMode(e,!0)},acceptsMotion:!0},Y:{fn:function(e,t,n,r){i.copyLine(e)}},p:{fn:function(e,t,n,i){var s=a._default;e.setOverwrite(!1);if(s.isLine){var o=e.getCursorPosition();o.column=e.session.getLine(o.row).length;var u=r.stringRepeat("\n"+s.text,n||1);e.session.insert(o,u),e.moveCursorTo(o.row+1,0)}else e.navigateRight(),e.insert(r.stringRepeat(s.text,n||1)),e.navigateLeft();e.setOverwrite(!0),e.selection.clearSelection()}},P:{fn:function(e,t,n,i){var s=a._default;e.setOverwrite(!1);if(s.isLine){var o=e.getCursorPosition();o.column=0;var u=r.stringRepeat(s.text+"\n",n||1);e.session.insert(o,u),e.moveCursorToPosition(o)}else e.insert(r.stringRepeat(s.text,n||1));e.setOverwrite(!0),e.selection.clearSelection()}},J:{fn:function(e,t,n,r){var i=e.session;t=e.getSelectionRange();var s={row:t.start.row,column:t.start.column};n=n||t.end.row-t.start.row;var o=Math.min(s.row+(n||1),i.getLength()-1);t.start.column=i.getLine(s.row).length,t.end.column=i.getLine(o).length,t.end.row=o;var u="";for(var a=s.row;a1&&(i=".,.+"+n+i),e.showCommandLine&&e.showCommandLine(i)}},"/":{fn:function(e,t,n,r){e.showCommandLine&&e.showCommandLine("/")}},"?":{fn:function(e,t,n,r){e.showCommandLine&&e.showCommandLine("?")}},".":{fn:function(e,t,n,r){i.onInsertReplaySequence=g.lastInsertCommands;var s=g.previous;s&&g.exec(e,s.action,s.param)}},"ctrl-x":{fn:function(e,t,n,r){e.modifyNumber(-(n||1))}},"ctrl-a":{fn:function(e,t,n,r){e.modifyNumber(n||1)}}},g=t.inputBuffer={accepting:[f,l,c,h],currentCmd:null,currentCount:"",status:"",operator:null,motion:null,lastInsertCommands:[],push:function(e,t,n){var r=this.status,i=!0;this.idle=!1;var a=this.waitingForParam;/^numpad\d+$/i.test(t)&&(t=t.substr(6));if(a)this.exec(e,a,t);else if(t==="0"&&!this.currentCount.length||!/^\d+$/.test(t)||!this.isAccepting(f))if(!this.operator&&this.isAccepting(l)&&o[t])this.operator={ch:t,count:this.getCount()},this.currentCmd=l,this.accepting=[f,c,h],this.exec(e,{operator:this.operator});else if(s[t]&&this.isAccepting(c)){this.currentCmd=c;var p={operator:this.operator,motion:{ch:t,count:this.getCount()}};s[t].param?this.waitForParam(p):this.exec(e,p)}else if(u[t]&&this.isAccepting(c))u[t].operator.count=this.getCount(),this.exec(e,u[t]);else if(m[t]&&this.isAccepting(h)){var d={action:{fn:m[t].fn,count:this.getCount()}};m[t].param?this.waitForParam(d):this.exec(e,d),m[t].acceptsMotion&&(this.idle=!1)}else this.operator?(this.operator.count=this.getCount(),this.exec(e,{operator:this.operator},t)):(i=t.length==1,this.reset());else this.currentCount+=t,this.currentCmd=f,this.accepting=[f,l,c,h];return this.waitingForParam||this.motion||this.operator?this.status+=t:this.currentCount?this.status=this.currentCount:this.status&&(this.status=""),this.status!=r&&e._emit("changeStatus"),i},waitForParam:function(e){this.waitingForParam=e},getCount:function(){var e=this.currentCount;return this.currentCount="",e&&parseInt(e,10)},exec:function(e,t,n){var r=t.motion,u=t.operator,a=t.action;n||(n=t.param),u&&(this.previous={action:t,param:n});if(u&&!e.selection.isEmpty()){o[u.ch].selFn&&(o[u.ch].selFn(e,e.getSelectionRange(),u.count,n),this.reset());return}if(!r&&!a&&u&&n)o[u.ch].fn(e,null,u.count,n),this.reset();else if(r){var f=function(t){t&&typeof t=="function"&&(r.count&&!l.handlesCount?d(t,r.count,[e,null,r.count,n]):t(e,null,r.count,n))},l=s[r.ch],c=l.sel;u?c&&d(function(){f(l.sel),o[u.ch].fn(e,e.getSelectionRange(),u.count,n)},u.count||1):(i.onVisualMode||i.onVisualLineMode)&&c?f(l.sel):f(l.nav),this.reset()}else a&&(a.fn(e,e.getSelectionRange(),a.count,n),this.reset());b(e)},isAccepting:function(e){return this.accepting.indexOf(e)!==-1},reset:function(){this.operator=null,this.motion=null,this.currentCount="",this.status="",this.accepting=[f,l,c,h],this.idle=!0,this.waitingForParam=null}};t.coreCommands={start:{exec:function w(e){i.insertMode(e),y(w)}},startBeginning:{exec:function E(e){e.navigateLineStart(),i.insertMode(e),y(E)}},stop:{exec:function(t){g.reset(),i.onVisualMode=!1,i.onVisualLineMode=!1,g.lastInsertCommands=i.normalMode(t)}},append:{exec:function S(e){var t=e.getCursorPosition(),n=e.session.getLine(t.row).length;n&&e.navigateRight(),i.insertMode(e),y(S)}},appendEnd:{exec:function x(e){e.navigateLineEnd(),i.insertMode(e),y(x)}}};var b=t.onCursorMove=function(e,t){if(i.currentMode==="insert"||b.running)return;if(!e.selection.isEmpty()){b.running=!0;if(i.onVisualLineMode){var n=e.selection.visualLineStart,r=e.getCursorPosition().row;if(n<=r){var s=e.session.getLine(r);e.selection.clearSelection(),e.selection.moveCursorTo(n,0),e.selection.selectTo(r,s.length)}else{var s=e.session.getLine(n);e.selection.clearSelection(),e.selection.moveCursorTo(n,s.length),e.selection.selectTo(r,0)}}b.running=!1;return}t&&(i.onVisualLineMode||i.onVisualMode)&&(e.selection.clearSelection(),i.normalMode(e)),b.running=!0;var o=e.getCursorPosition(),u=e.session.getLine(o.row).length;u&&o.column===u&&e.navigateLeft(),b.running=!1}}),define("ace/keyboard/vim/maps/util",["require","exports","module","ace/keyboard/vim/registers","ace/lib/dom"],function(e,t,n){var r=e("../registers"),i=e("../../../lib/dom");i.importCssString(".insert-mode .ace_cursor{ border-left: 2px solid #333333;}.ace_dark.insert-mode .ace_cursor{ border-left: 2px solid #eeeeee;}.normal-mode .ace_cursor{ border: 0!important; background-color: red; opacity: 0.5;}","vimMode"),n.exports={onVisualMode:!1,onVisualLineMode:!1,currentMode:"normal",noMode:function(e){e.unsetStyle("insert-mode"),e.unsetStyle("normal-mode"),e.commands.recording&&e.commands.toggleRecording(e),e.setOverwrite(!1)},insertMode:function(e){this.currentMode="insert",e.setStyle("insert-mode"),e.unsetStyle("normal-mode"),e.setOverwrite(!1),e.keyBinding.$data.buffer="",e.keyBinding.$data.state="insertMode",this.onVisualMode=!1,this.onVisualLineMode=!1,this.onInsertReplaySequence?(e.commands.macro=this.onInsertReplaySequence,e.commands.replay(e),this.onInsertReplaySequence=null,this.normalMode(e)):(e._emit("changeStatus"),e.commands.recording||e.commands.toggleRecording(e))},normalMode:function(e){this.currentMode="normal",e.unsetStyle("insert-mode"),e.setStyle("normal-mode"),e.clearSelection();var t;return e.getOverwrite()||(t=e.getCursorPosition(),t.column>0&&e.navigateLeft()),e.setOverwrite(!0),e.keyBinding.$data.buffer="",e.keyBinding.$data.state="start",this.onVisualMode=!1,this.onVisualLineMode=!1,e._emit("changeStatus"),e.commands.recording?(e.commands.toggleRecording(e),e.commands.macro):[]},visualMode:function(e,t){if(this.onVisualLineMode&&t||this.onVisualMode&&!t){this.normalMode(e);return}e.setStyle("insert-mode"),e.unsetStyle("normal-mode"),e._emit("changeStatus"),t?this.onVisualLineMode=!0:(this.onVisualMode=!0,this.onVisualLineMode=!1)},getRightNthChar:function(e,t,n,r){var i=e.getSession().getLine(t.row),s=i.substr(t.column+1).split(n);return r~!@#$%^&*|+=\[\]{}`~?]/,u=/[.\/\\()\"'-:,.;<>~!@#$%^&*|+=\[\]{}`~?]/,a=/\s/,f=function(e,t){var n=e.selection;this.range=n.getRange(),t=t||n.selectionLead,this.row=t.row,this.col=t.column;var r=e.session.getLine(this.row),i=e.session.getLength();this.ch=r[this.col]||"\n",this.skippedLines=0,this.next=function(){return this.ch=r[++this.col]||this.handleNewLine(1),this.ch},this.prev=function(){return this.ch=r[--this.col]||this.handleNewLine(-1),this.ch},this.peek=function(t){var n=r[this.col+t];return n?n:t==-1?"\n":this.col==r.length-1?"\n":e.session.getLine(this.row+1)[0]||"\n"},this.handleNewLine=function(t){if(t==1)return this.col==r.length?"\n":this.row==i-1?"":(this.col=0,this.row++,r=e.session.getLine(this.row),this.skippedLines++,r[0]||"\n");if(t==-1)return this.row===0?"":(this.row--,r=e.session.getLine(this.row),this.col=r.length,this.skippedLines--,"\n")},this.debug=function(){console.log(r.substring(0,this.col)+"|"+this.ch+"'"+this.col+"'"+r.substr(this.col+1))}},l=e("../../../search").Search,c=new l,p=e("../../../range").Range,d={};n.exports={w:new s(function(e){var t=new f(e);if(t.ch&&u.test(t.ch))while(t.ch&&u.test(t.ch))t.next();else while(t.ch&&!o.test(t.ch))t.next();while(t.ch&&a.test(t.ch)&&t.skippedLines<2)t.next();return t.skippedLines==2&&t.prev(),{column:t.col,row:t.row}}),W:new s(function(e){var t=new f(e);while(t.ch&&(!a.test(t.ch)||!!a.test(t.peek(1)))&&t.skippedLines<2)t.next();return t.skippedLines==2?t.prev():t.next(),{column:t.col,row:t.row}}),b:new s(function(e){var t=new f(e);t.prev();while(t.ch&&a.test(t.ch)&&t.skippedLines>-2)t.prev();if(t.ch&&u.test(t.ch))while(t.ch&&u.test(t.ch))t.prev();else while(t.ch&&!o.test(t.ch))t.prev();return t.ch&&t.next(),{column:t.col,row:t.row}}),B:new s(function(e){var t=new f(e);t.prev();while(t.ch&&(!!a.test(t.ch)||!a.test(t.peek(-1)))&&t.skippedLines>-2)t.prev();return t.skippedLines==-2&&t.next(),{column:t.col,row:t.row}}),e:new s(function(e){var t=new f(e);t.next();while(t.ch&&a.test(t.ch))t.next();if(t.ch&&u.test(t.ch))while(t.ch&&u.test(t.ch))t.next();else while(t.ch&&!o.test(t.ch))t.next();return t.ch&&t.prev(),{column:t.col,row:t.row}}),E:new s(function(e){var t=new f(e);t.next();while(t.ch&&(!!a.test(t.ch)||!a.test(t.peek(1))))t.next();return{column:t.col,row:t.row}}),l:{nav:function(e){var t=e.getCursorPosition(),n=t.column,r=e.session.getLine(t.row).length;r&&n!==r&&e.navigateRight()},sel:function(e){var t=e.getCursorPosition(),n=t.column,r=e.session.getLine(t.row).length;r&&n!==r&&e.selection.selectRight()}},h:{nav:function(e){var t=e.getCursorPosition();t.column>0&&e.navigateLeft()},sel:function(e){var t=e.getCursorPosition();t.column>0&&e.selection.selectLeft()}},H:{nav:function(e){var t=e.renderer.getScrollTopRow();e.moveCursorTo(t)},sel:function(e){var t=e.renderer.getScrollTopRow();e.selection.selectTo(t)}},M:{nav:function(e){var t=e.renderer.getScrollTopRow(),n=e.renderer.getScrollBottomRow(),r=t+(n-t)/2;e.moveCursorTo(r)},sel:function(e){var t=e.renderer.getScrollTopRow(),n=e.renderer.getScrollBottomRow(),r=t+(n-t)/2;e.selection.selectTo(r)}},L:{nav:function(e){var t=e.renderer.getScrollBottomRow();e.moveCursorTo(t)},sel:function(e){var t=e.renderer.getScrollBottomRow();e.selection.selectTo(t)}},k:{nav:function(e){e.navigateUp()},sel:function(e){e.selection.selectUp()}},j:{nav:function(e){e.navigateDown()},sel:function(e){e.selection.selectDown()}},i:{param:!0,sel:function(e,t,n,r){switch(r){case"w":e.selection.selectWord();break;case"W":e.selection.selectAWord();break;case"(":case"{":case"[":var i=e.getCursorPosition(),s=e.session.$findClosingBracket(r,i,/paren/);if(!s)return;var o=e.session.$findOpeningBracket(e.session.$brackets[r],i,/paren/);if(!o)return;o.column++,e.selection.setSelectionRange(p.fromPoints(o,s));break;case"'":case'"':case"/":var s=h(e,r,1);if(!s)return;var o=h(e,r,-1);if(!o)return;e.selection.setSelectionRange(p.fromPoints(o.end,s.start))}}},a:{param:!0,sel:function(e,t,n,r){switch(r){case"w":e.selection.selectAWord();break;case"W":e.selection.selectAWord();break;case"(":case"{":case"[":var i=e.getCursorPosition(),s=e.session.$findClosingBracket(r,i,/paren/);if(!s)return;var o=e.session.$findOpeningBracket(e.session.$brackets[r],i,/paren/);if(!o)return;s.column++,e.selection.setSelectionRange(p.fromPoints(o,s));break;case"'":case'"':case"/":var s=h(e,r,1);if(!s)return;var o=h(e,r,-1);if(!o)return;s.column++,e.selection.setSelectionRange(p.fromPoints(o.start,s.end))}}},f:new s({param:!0,handlesCount:!0,getPos:function(e,t,n,i,s,o){o||(d={ch:"f",param:i});var u=e.getCursorPosition(),a=r.getRightNthChar(e,u,i,n||1);if(typeof a=="number")return u.column+=a+(s?2:1),u}}),F:new s({param:!0,handlesCount:!0,getPos:function(e,t,n,i,s,o){o||(d={ch:"F",param:i});var u=e.getCursorPosition(),a=r.getLeftNthChar(e,u,i,n||1);if(typeof a=="number")return u.column-=a+1,u}}),t:new s({param:!0,handlesCount:!0,getPos:function(e,t,n,i,s,o){o||(d={ch:"t",param:i});var u=e.getCursorPosition(),a=r.getRightNthChar(e,u,i,n||1);if(o&&a==0&&!(n>1))var a=r.getRightNthChar(e,u,i,2);if(typeof a=="number")return u.column+=a+(s?1:0),u}}),T:new s({param:!0,handlesCount:!0,getPos:function(e,t,n,i,s,o){o||(d={ch:"T",param:i});var u=e.getCursorPosition(),a=r.getLeftNthChar(e,u,i,n||1);if(o&&a==0&&!(n>1))var a=r.getLeftNthChar(e,u,i,2);if(typeof a=="number")return u.column-=a,u}}),";":new s({handlesCount:!0,getPos:function(e,t,r,i,s){var o=d.ch;if(!o)return;return n.exports[o].getPos(e,t,r,d.param,s,!0)}}),",":new s({handlesCount:!0,getPos:function(e,t,r,i,s){var o=d.ch;if(!o)return;var u=o.toUpperCase();return o=o===u?o.toLowerCase():u,n.exports[o].getPos(e,t,r,d.param,s,!0)}}),"^":{nav:function(e){e.navigateLineStart()},sel:function(e){e.selection.selectLineStart()}},$:{nav:function(e){e.navigateLineEnd()},sel:function(e){e.selection.selectLineEnd()}},0:new s(function(e){return{row:e.selection.lead.row,column:0}}),G:{nav:function(e,t,n,r){!n&&n!==0&&(n=e.session.getLength()),e.gotoLine(n)},sel:function(e,t,n,r){!n&&n!==0&&(n=e.session.getLength()),e.selection.selectTo(n,0)}},g:{param:!0,nav:function(e,t,n,r){switch(r){case"m":console.log("Middle line");break;case"e":console.log("End of prev word");break;case"g":e.gotoLine(n||0);case"u":e.gotoLine(n||0);case"U":e.gotoLine(n||0)}},sel:function(e,t,n,r){switch(r){case"m":console.log("Middle line");break;case"e":console.log("End of prev word");break;case"g":e.selection.selectTo(n||0,0)}}},o:{nav:function(e,t,n,i){n=n||1;var s="";while(00?(e.navigateUp(),e.navigateLineEnd(),e.insert(o)):(e.session.insert({row:0,column:0},o),e.navigateUp()),r.insertMode(e))}},"%":new s(function(e){var t=/[\[\]{}()]/g,n=e.getCursorPosition(),r=e.session.getLine(n.row)[n.column];if(!t.test(r)){var i=h(e,t);if(!i)return;n=i.start}var s=e.session.findMatchingBracket({row:n.row,column:n.column+1});return s}),"{":new s(function(e){var t=e.session,n=t.selection.lead.row;while(n>0&&!/\S/.test(t.getLine(n)))n--;while(/\S/.test(t.getLine(n)))n--;return{column:0,row:n}}),"}":new s(function(e){var t=e.session,n=t.getLength(),r=t.selection.lead.row;while(r":{selFn:function(e,t,n,i){n=n||1;for(var s=0;s":var i=e.getCursorPosition();e.selection.selectLine();for(var s=0;s span {font-weight: normal !important;}.ace-github .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-github .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-github .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-github .ace_gutter-active-line {background-color : rgba(0, 0, 0, 0.07);}.ace-github .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-github .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-github .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) \ No newline at end of file diff --git a/superx/saiku/js/ace/theme-textmate.js b/superx/saiku/js/ace/theme-textmate.js deleted file mode 100644 index cd5a72d..0000000 --- a/superx/saiku/js/ace/theme-textmate.js +++ /dev/null @@ -1 +0,0 @@ -define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;border-radius: 2px;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) \ No newline at end of file diff --git a/superx/saiku/js/backbone/backbone-model-file-upload.js b/superx/saiku/js/backbone/backbone-model-file-upload.js deleted file mode 100644 index 6c9d4aa..0000000 --- a/superx/saiku/js/backbone/backbone-model-file-upload.js +++ /dev/null @@ -1,117 +0,0 @@ -// Backbone.Model File Upload v0.1 -// by Joe Vu - joe.vu@homeslicesolutions.com -// For all details and documentation: -// https://github.com/homeslicesolutions/backbone-model-file-upload - -!function(_, Backbone){ - - // Clone the original Backbone.Model.prototype - var backboneModelClone = _.clone( Backbone.Model.prototype ); - - // Extending out - _.extend(Backbone.Model.prototype, { - - // ! Default file attribute - can be overwritten - fileAttribute: 'file', - - // @ Save - overwritten - save: function(key, val, options) { - - // Variables - var attrs, attributes = this.attributes; - - // Signature parsing - taken directly from original Backbone.Model.save - // and it states: 'Handle both "key", value and {key: value} -style arguments.' - if (key == null || typeof key === 'object') { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - // Validate & wait options - taken directly from original Backbone.Model.save - options = _.extend({validate: true}, options); - if (attrs && !options.wait) { - if (!this.set(attrs, options)) return false; - } else { - if (!this._validate(attrs, options)) return false; - } - if (attrs && options.wait) { - this.attributes = _.extend({}, attributes, attrs); - } - - // Check for "formData" flag and check for if file exist. - if ( options.formData === true - || options.formData !== false - && this.attributes[ this.fileAttribute ] - && this.attributes[ this.fileAttribute ] instanceof File ) { - - // Flatten Attributes reapplying File Object - var formAttrs = _.clone( this.attributes ), - fileAttr = this.attributes[ this.fileAttribute ]; - formAttrs = this._flatten( formAttrs ); - formAttrs[ this.fileAttribute ] = fileAttr; - - // Converting Attributes to Form Data - var formData = new FormData(); - _.each( formAttrs, function( value, key ){ - formData.append( key, value ); - }); - - // Set options for AJAX call - options = options || {}; - options.data = formData; - options.processData = false; - options.contentType = false; - - // Apply custom XHR for processing status & listen to "progress" - var that = this; - options.xhr = function() { - var xhr = $.ajaxSettings.xhr(); - xhr.upload.addEventListener('progress', function(){ - - that._progressHandler.apply(that, arguments); - }, false); - return xhr; - } - } - - // Resume back to original state - if (attrs && options.wait) this.attributes = attributes; - - // Continue to call the existing "save" method - return backboneModelClone.save.call(this, attrs, options); - - }, - - // _ FlattenObject gist by "penguinboy". Thank You! - // https://gist.github.com/penguinboy/762197 - _flatten: function( obj ) { - var output = {}; - for (var i in obj) { - if (!obj.hasOwnProperty(i)) continue; - if (typeof obj[i] == 'object') { - var flatObject = this._flatten(obj[i]); - for (var x in flatObject) { - if (!flatObject.hasOwnProperty(x)) continue; - output[i + '.' + x] = flatObject[x]; - } - } else { - output[i] = obj[i]; - } - } - return output; - - }, - - // _ Get the Progress of the uploading file - _progressHandler: function( event ) { - if (event.lengthComputable) { - var percentComplete = event.loaded / event.total; - this.trigger( 'progress', percentComplete ); - } - } - - }); - -}(_, Backbone); diff --git a/superx/saiku/js/backbone/backbone.js b/superx/saiku/js/backbone/backbone.js deleted file mode 100644 index 24a550a..0000000 --- a/superx/saiku/js/backbone/backbone.js +++ /dev/null @@ -1,1608 +0,0 @@ -// Backbone.js 1.1.2 - -// (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors -// Backbone may be freely distributed under the MIT license. -// For all details and documentation: -// http://backbonejs.org - -(function(root, factory) { - - // Set up Backbone appropriately for the environment. Start with AMD. - if (typeof define === 'function' && define.amd) { - define(['underscore', 'jquery', 'exports'], function(_, $, exports) { - // Export global even in AMD case in case this script is loaded with - // others that may still expect a global Backbone. - root.Backbone = factory(root, exports, _, $); - }); - - // Next for Node.js or CommonJS. jQuery may not be needed as a module. - } else if (typeof exports !== 'undefined') { - var _ = require('underscore'); - factory(root, exports, _); - - // Finally, as a browser global. - } else { - root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); - } - -}(this, function(root, Backbone, _, $) { - - // Initial Setup - // ------------- - - // Save the previous value of the `Backbone` variable, so that it can be - // restored later on, if `noConflict` is used. - var previousBackbone = root.Backbone; - - // Create local references to array methods we'll want to use later. - var array = []; - var push = array.push; - var slice = array.slice; - var splice = array.splice; - - // Current version of the library. Keep in sync with `package.json`. - Backbone.VERSION = '1.1.2'; - - // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns - // the `$` variable. - Backbone.$ = $; - - // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable - // to its previous owner. Returns a reference to this Backbone object. - Backbone.noConflict = function() { - root.Backbone = previousBackbone; - return this; - }; - - // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option - // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and - // set a `X-Http-Method-Override` header. - Backbone.emulateHTTP = false; - - // Turn on `emulateJSON` to support legacy servers that can't deal with direct - // `application/json` requests ... will encode the body as - // `application/x-www-form-urlencoded` instead and will send the model in a - // form param named `model`. - Backbone.emulateJSON = false; - - // Backbone.Events - // --------------- - - // A module that can be mixed in to *any object* in order to provide it with - // custom events. You may bind with `on` or remove with `off` callback - // functions to an event; `trigger`-ing an event fires all callbacks in - // succession. - // - // var object = {}; - // _.extend(object, Backbone.Events); - // object.on('expand', function(){ alert('expanded'); }); - // object.trigger('expand'); - // - var Events = Backbone.Events = { - - // Bind an event to a `callback` function. Passing `"all"` will bind - // the callback to all events fired. - on: function(name, callback, context) { - if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; - this._events || (this._events = {}); - var events = this._events[name] || (this._events[name] = []); - events.push({callback: callback, context: context, ctx: context || this}); - return this; - }, - - // Bind an event to only be triggered a single time. After the first time - // the callback is invoked, it will be removed. - once: function(name, callback, context) { - if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; - var self = this; - var once = _.once(function() { - self.off(name, once); - callback.apply(this, arguments); - }); - once._callback = callback; - return this.on(name, once, context); - }, - - // Remove one or many callbacks. If `context` is null, removes all - // callbacks with that function. If `callback` is null, removes all - // callbacks for the event. If `name` is null, removes all bound - // callbacks for all events. - off: function(name, callback, context) { - var retain, ev, events, names, i, l, j, k; - if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; - if (!name && !callback && !context) { - this._events = void 0; - return this; - } - names = name ? [name] : _.keys(this._events); - for (i = 0, l = names.length; i < l; i++) { - name = names[i]; - if (events = this._events[name]) { - this._events[name] = retain = []; - if (callback || context) { - for (j = 0, k = events.length; j < k; j++) { - ev = events[j]; - if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || - (context && context !== ev.context)) { - retain.push(ev); - } - } - } - if (!retain.length) delete this._events[name]; - } - } - - return this; - }, - - // Trigger one or many events, firing all bound callbacks. Callbacks are - // passed the same arguments as `trigger` is, apart from the event name - // (unless you're listening on `"all"`, which will cause your callback to - // receive the true name of the event as the first argument). - trigger: function(name) { - if (!this._events) return this; - var args = slice.call(arguments, 1); - if (!eventsApi(this, 'trigger', name, args)) return this; - var events = this._events[name]; - var allEvents = this._events.all; - if (events) triggerEvents(events, args); - if (allEvents) triggerEvents(allEvents, arguments); - return this; - }, - - // Tell this object to stop listening to either specific events ... or - // to every object it's currently listening to. - stopListening: function(obj, name, callback) { - var listeningTo = this._listeningTo; - if (!listeningTo) return this; - var remove = !name && !callback; - if (!callback && typeof name === 'object') callback = this; - if (obj) (listeningTo = {})[obj._listenId] = obj; - for (var id in listeningTo) { - obj = listeningTo[id]; - obj.off(name, callback, this); - if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; - } - return this; - } - - }; - - // Regular expression used to split event strings. - var eventSplitter = /\s+/; - - // Implement fancy features of the Events API such as multiple event - // names `"change blur"` and jQuery-style event maps `{change: action}` - // in terms of the existing API. - var eventsApi = function(obj, action, name, rest) { - if (!name) return true; - - // Handle event maps. - if (typeof name === 'object') { - for (var key in name) { - obj[action].apply(obj, [key, name[key]].concat(rest)); - } - return false; - } - - // Handle space separated event names. - if (eventSplitter.test(name)) { - var names = name.split(eventSplitter); - for (var i = 0, l = names.length; i < l; i++) { - obj[action].apply(obj, [names[i]].concat(rest)); - } - return false; - } - - return true; - }; - - // A difficult-to-believe, but optimized internal dispatch function for - // triggering events. Tries to keep the usual cases speedy (most internal - // Backbone events have 3 arguments). - var triggerEvents = function(events, args) { - var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; - switch (args.length) { - case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; - case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; - case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; - case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; - default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; - } - }; - - var listenMethods = {listenTo: 'on', listenToOnce: 'once'}; - - // Inversion-of-control versions of `on` and `once`. Tell *this* object to - // listen to an event in another object ... keeping track of what it's - // listening to. - _.each(listenMethods, function(implementation, method) { - Events[method] = function(obj, name, callback) { - var listeningTo = this._listeningTo || (this._listeningTo = {}); - var id = obj._listenId || (obj._listenId = _.uniqueId('l')); - listeningTo[id] = obj; - if (!callback && typeof name === 'object') callback = this; - obj[implementation](name, callback, this); - return this; - }; - }); - - // Aliases for backwards compatibility. - Events.bind = Events.on; - Events.unbind = Events.off; - - // Allow the `Backbone` object to serve as a global event bus, for folks who - // want global "pubsub" in a convenient place. - _.extend(Backbone, Events); - - // Backbone.Model - // -------------- - - // Backbone **Models** are the basic data object in the framework -- - // frequently representing a row in a table in a database on your server. - // A discrete chunk of data and a bunch of useful, related methods for - // performing computations and transformations on that data. - - // Create a new model with the specified attributes. A client id (`cid`) - // is automatically generated and assigned for you. - var Model = Backbone.Model = function(attributes, options) { - var attrs = attributes || {}; - options || (options = {}); - this.cid = _.uniqueId('c'); - this.attributes = {}; - if (options.collection) this.collection = options.collection; - if (options.parse) attrs = this.parse(attrs, options) || {}; - attrs = _.defaults({}, attrs, _.result(this, 'defaults')); - this.set(attrs, options); - this.changed = {}; - this.initialize.apply(this, arguments); - }; - - // Attach all inheritable methods to the Model prototype. - _.extend(Model.prototype, Events, { - - // A hash of attributes whose current and previous value differ. - changed: null, - - // The value returned during the last failed validation. - validationError: null, - - // The default name for the JSON `id` attribute is `"id"`. MongoDB and - // CouchDB users may want to set this to `"_id"`. - idAttribute: 'id', - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Return a copy of the model's `attributes` object. - toJSON: function(options) { - return _.clone(this.attributes); - }, - - // Proxy `Backbone.sync` by default -- but override this if you need - // custom syncing semantics for *this* particular model. - sync: function() { - return Backbone.sync.apply(this, arguments); - }, - - // Get the value of an attribute. - get: function(attr) { - return this.attributes[attr]; - }, - - // Get the HTML-escaped value of an attribute. - escape: function(attr) { - return _.escape(this.get(attr)); - }, - - // Returns `true` if the attribute contains a value that is not null - // or undefined. - has: function(attr) { - return this.get(attr) != null; - }, - - // Set a hash of model attributes on the object, firing `"change"`. This is - // the core primitive operation of a model, updating the data and notifying - // anyone who needs to know about the change in state. The heart of the beast. - set: function(key, val, options) { - var attr, attrs, unset, changes, silent, changing, prev, current; - if (key == null) return this; - - // Handle both `"key", value` and `{key: value}` -style arguments. - if (typeof key === 'object') { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - options || (options = {}); - - // Run validation. - if (!this._validate(attrs, options)) return false; - - // Extract attributes and options. - unset = options.unset; - silent = options.silent; - changes = []; - changing = this._changing; - this._changing = true; - - if (!changing) { - this._previousAttributes = _.clone(this.attributes); - this.changed = {}; - } - current = this.attributes, prev = this._previousAttributes; - - // Check for changes of `id`. - if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; - - // For each `set` attribute, update or delete the current value. - for (attr in attrs) { - val = attrs[attr]; - if (!_.isEqual(current[attr], val)) changes.push(attr); - if (!_.isEqual(prev[attr], val)) { - this.changed[attr] = val; - } else { - delete this.changed[attr]; - } - unset ? delete current[attr] : current[attr] = val; - } - - // Trigger all relevant attribute changes. - if (!silent) { - if (changes.length) this._pending = options; - for (var i = 0, l = changes.length; i < l; i++) { - this.trigger('change:' + changes[i], this, current[changes[i]], options); - } - } - - // You might be wondering why there's a `while` loop here. Changes can - // be recursively nested within `"change"` events. - if (changing) return this; - if (!silent) { - while (this._pending) { - options = this._pending; - this._pending = false; - this.trigger('change', this, options); - } - } - this._pending = false; - this._changing = false; - return this; - }, - - // Remove an attribute from the model, firing `"change"`. `unset` is a noop - // if the attribute doesn't exist. - unset: function(attr, options) { - return this.set(attr, void 0, _.extend({}, options, {unset: true})); - }, - - // Clear all attributes on the model, firing `"change"`. - clear: function(options) { - var attrs = {}; - for (var key in this.attributes) attrs[key] = void 0; - return this.set(attrs, _.extend({}, options, {unset: true})); - }, - - // Determine if the model has changed since the last `"change"` event. - // If you specify an attribute name, determine if that attribute has changed. - hasChanged: function(attr) { - if (attr == null) return !_.isEmpty(this.changed); - return _.has(this.changed, attr); - }, - - // Return an object containing all the attributes that have changed, or - // false if there are no changed attributes. Useful for determining what - // parts of a view need to be updated and/or what attributes need to be - // persisted to the server. Unset attributes will be set to undefined. - // You can also pass an attributes object to diff against the model, - // determining if there *would be* a change. - changedAttributes: function(diff) { - if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; - var val, changed = false; - var old = this._changing ? this._previousAttributes : this.attributes; - for (var attr in diff) { - if (_.isEqual(old[attr], (val = diff[attr]))) continue; - (changed || (changed = {}))[attr] = val; - } - return changed; - }, - - // Get the previous value of an attribute, recorded at the time the last - // `"change"` event was fired. - previous: function(attr) { - if (attr == null || !this._previousAttributes) return null; - return this._previousAttributes[attr]; - }, - - // Get all of the attributes of the model at the time of the previous - // `"change"` event. - previousAttributes: function() { - return _.clone(this._previousAttributes); - }, - - // Fetch the model from the server. If the server's representation of the - // model differs from its current attributes, they will be overridden, - // triggering a `"change"` event. - fetch: function(options) { - options = options ? _.clone(options) : {}; - if (options.parse === void 0) options.parse = true; - var model = this; - var success = options.success; - options.success = function(resp) { - if (!model.set(model.parse(resp, options), options)) return false; - if (success) success(model, resp, options); - model.trigger('sync', model, resp, options); - }; - wrapError(this, options); - return this.sync('read', this, options); - }, - - // Set a hash of model attributes, and sync the model to the server. - // If the server returns an attributes hash that differs, the model's - // state will be `set` again. - save: function(key, val, options) { - var attrs, method, xhr, attributes = this.attributes; - - // Handle both `"key", value` and `{key: value}` -style arguments. - if (key == null || typeof key === 'object') { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - options = _.extend({validate: true}, options); - - // If we're not waiting and attributes exist, save acts as - // `set(attr).save(null, opts)` with validation. Otherwise, check if - // the model will be valid when the attributes, if any, are set. - if (attrs && !options.wait) { - if (!this.set(attrs, options)) return false; - } else { - if (!this._validate(attrs, options)) return false; - } - - // Set temporary attributes if `{wait: true}`. - if (attrs && options.wait) { - this.attributes = _.extend({}, attributes, attrs); - } - - // After a successful server-side save, the client is (optionally) - // updated with the server-side state. - if (options.parse === void 0) options.parse = true; - var model = this; - var success = options.success; - options.success = function(resp) { - // Ensure attributes are restored during synchronous saves. - model.attributes = attributes; - var serverAttrs = model.parse(resp, options); - if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); - if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { - return false; - } - if (success) success(model, resp, options); - model.trigger('sync', model, resp, options); - }; - wrapError(this, options); - - method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); - if (method === 'patch') options.attrs = attrs; - xhr = this.sync(method, this, options); - - // Restore attributes. - if (attrs && options.wait) this.attributes = attributes; - - return xhr; - }, - - // Destroy this model on the server if it was already persisted. - // Optimistically removes the model from its collection, if it has one. - // If `wait: true` is passed, waits for the server to respond before removal. - destroy: function(options) { - options = options ? _.clone(options) : {}; - var model = this; - var success = options.success; - - var destroy = function() { - model.trigger('destroy', model, model.collection, options); - }; - - options.success = function(resp) { - if (options.wait || model.isNew()) destroy(); - if (success) success(model, resp, options); - if (!model.isNew()) model.trigger('sync', model, resp, options); - }; - - if (this.isNew()) { - options.success(); - return false; - } - wrapError(this, options); - - var xhr = this.sync('delete', this, options); - if (!options.wait) destroy(); - return xhr; - }, - - // Default URL for the model's representation on the server -- if you're - // using Backbone's restful methods, override this to change the endpoint - // that will be called. - url: function() { - var base = - _.result(this, 'urlRoot') || - _.result(this.collection, 'url') || - urlError(); - if (this.isNew()) return base; - return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id); - }, - - // **parse** converts a response into the hash of attributes to be `set` on - // the model. The default implementation is just to pass the response along. - parse: function(resp, options) { - return resp; - }, - - // Create a new model with identical attributes to this one. - clone: function() { - return new this.constructor(this.attributes); - }, - - // A model is new if it has never been saved to the server, and lacks an id. - isNew: function() { - return !this.has(this.idAttribute); - }, - - // Check if the model is currently in a valid state. - isValid: function(options) { - return this._validate({}, _.extend(options || {}, { validate: true })); - }, - - // Run validation against the next complete set of model attributes, - // returning `true` if all is well. Otherwise, fire an `"invalid"` event. - _validate: function(attrs, options) { - if (!options.validate || !this.validate) return true; - attrs = _.extend({}, this.attributes, attrs); - var error = this.validationError = this.validate(attrs, options) || null; - if (!error) return true; - this.trigger('invalid', this, error, _.extend(options, {validationError: error})); - return false; - } - - }); - - // Underscore methods that we want to implement on the Model. - var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit']; - - // Mix in each Underscore method as a proxy to `Model#attributes`. - _.each(modelMethods, function(method) { - Model.prototype[method] = function() { - var args = slice.call(arguments); - args.unshift(this.attributes); - return _[method].apply(_, args); - }; - }); - - // Backbone.Collection - // ------------------- - - // If models tend to represent a single row of data, a Backbone Collection is - // more analagous to a table full of data ... or a small slice or page of that - // table, or a collection of rows that belong together for a particular reason - // -- all of the messages in this particular folder, all of the documents - // belonging to this particular author, and so on. Collections maintain - // indexes of their models, both in order, and for lookup by `id`. - - // Create a new **Collection**, perhaps to contain a specific type of `model`. - // If a `comparator` is specified, the Collection will maintain - // its models in sort order, as they're added and removed. - var Collection = Backbone.Collection = function(models, options) { - options || (options = {}); - if (options.model) this.model = options.model; - if (options.comparator !== void 0) this.comparator = options.comparator; - this._reset(); - this.initialize.apply(this, arguments); - if (models) this.reset(models, _.extend({silent: true}, options)); - }; - - // Default options for `Collection#set`. - var setOptions = {add: true, remove: true, merge: true}; - var addOptions = {add: true, remove: false}; - - // Define the Collection's inheritable methods. - _.extend(Collection.prototype, Events, { - - // The default model for a collection is just a **Backbone.Model**. - // This should be overridden in most cases. - model: Model, - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // The JSON representation of a Collection is an array of the - // models' attributes. - toJSON: function(options) { - return this.map(function(model){ return model.toJSON(options); }); - }, - - // Proxy `Backbone.sync` by default. - sync: function() { - return Backbone.sync.apply(this, arguments); - }, - - // Add a model, or list of models to the set. - add: function(models, options) { - return this.set(models, _.extend({merge: false}, options, addOptions)); - }, - - // Remove a model, or a list of models from the set. - remove: function(models, options) { - var singular = !_.isArray(models); - models = singular ? [models] : _.clone(models); - options || (options = {}); - var i, l, index, model; - for (i = 0, l = models.length; i < l; i++) { - model = models[i] = this.get(models[i]); - if (!model) continue; - delete this._byId[model.id]; - delete this._byId[model.cid]; - index = this.indexOf(model); - this.models.splice(index, 1); - this.length--; - if (!options.silent) { - options.index = index; - model.trigger('remove', model, this, options); - } - this._removeReference(model, options); - } - return singular ? models[0] : models; - }, - - // Update a collection by `set`-ing a new list of models, adding new ones, - // removing models that are no longer present, and merging models that - // already exist in the collection, as necessary. Similar to **Model#set**, - // the core operation for updating the data contained by the collection. - set: function(models, options) { - options = _.defaults({}, options, setOptions); - if (options.parse) models = this.parse(models, options); - var singular = !_.isArray(models); - models = singular ? (models ? [models] : []) : _.clone(models); - var i, l, id, model, attrs, existing, sort; - var at = options.at; - var targetModel = this.model; - var sortable = this.comparator && (at == null) && options.sort !== false; - var sortAttr = _.isString(this.comparator) ? this.comparator : null; - var toAdd = [], toRemove = [], modelMap = {}; - var add = options.add, merge = options.merge, remove = options.remove; - var order = !sortable && add && remove ? [] : false; - - // Turn bare objects into model references, and prevent invalid models - // from being added. - for (i = 0, l = models.length; i < l; i++) { - attrs = models[i] || {}; - if (attrs instanceof Model) { - id = model = attrs; - } else { - id = attrs[targetModel.prototype.idAttribute || 'id']; - } - - // If a duplicate is found, prevent it from being added and - // optionally merge it into the existing model. - if (existing = this.get(id)) { - if (remove) modelMap[existing.cid] = true; - if (merge) { - attrs = attrs === model ? model.attributes : attrs; - if (options.parse) attrs = existing.parse(attrs, options); - existing.set(attrs, options); - if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true; - } - models[i] = existing; - - // If this is a new, valid model, push it to the `toAdd` list. - } else if (add) { - model = models[i] = this._prepareModel(attrs, options); - if (!model) continue; - toAdd.push(model); - this._addReference(model, options); - } - - // Do not add multiple models with the same `id`. - model = existing || model; - if (order && (model.isNew() || !modelMap[model.id])) order.push(model); - modelMap[model.id] = true; - } - - // Remove nonexistent models if appropriate. - if (remove) { - for (i = 0, l = this.length; i < l; ++i) { - if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); - } - if (toRemove.length) this.remove(toRemove, options); - } - - // See if sorting is needed, update `length` and splice in new models. - if (toAdd.length || (order && order.length)) { - if (sortable) sort = true; - this.length += toAdd.length; - if (at != null) { - for (i = 0, l = toAdd.length; i < l; i++) { - this.models.splice(at + i, 0, toAdd[i]); - } - } else { - if (order) this.models.length = 0; - var orderedModels = order || toAdd; - for (i = 0, l = orderedModels.length; i < l; i++) { - this.models.push(orderedModels[i]); - } - } - } - - // Silently sort the collection if appropriate. - if (sort) this.sort({silent: true}); - - // Unless silenced, it's time to fire all appropriate add/sort events. - if (!options.silent) { - for (i = 0, l = toAdd.length; i < l; i++) { - (model = toAdd[i]).trigger('add', model, this, options); - } - if (sort || (order && order.length)) this.trigger('sort', this, options); - } - - // Return the added (or merged) model (or models). - return singular ? models[0] : models; - }, - - // When you have more items than you want to add or remove individually, - // you can reset the entire set with a new list of models, without firing - // any granular `add` or `remove` events. Fires `reset` when finished. - // Useful for bulk operations and optimizations. - reset: function(models, options) { - options || (options = {}); - for (var i = 0, l = this.models.length; i < l; i++) { - this._removeReference(this.models[i], options); - } - options.previousModels = this.models; - this._reset(); - models = this.add(models, _.extend({silent: true}, options)); - if (!options.silent) this.trigger('reset', this, options); - return models; - }, - - // Add a model to the end of the collection. - push: function(model, options) { - return this.add(model, _.extend({at: this.length}, options)); - }, - - // Remove a model from the end of the collection. - pop: function(options) { - var model = this.at(this.length - 1); - this.remove(model, options); - return model; - }, - - // Add a model to the beginning of the collection. - unshift: function(model, options) { - return this.add(model, _.extend({at: 0}, options)); - }, - - // Remove a model from the beginning of the collection. - shift: function(options) { - var model = this.at(0); - this.remove(model, options); - return model; - }, - - // Slice out a sub-array of models from the collection. - slice: function() { - return slice.apply(this.models, arguments); - }, - - // Get a model from the set by id. - get: function(obj) { - if (obj == null) return void 0; - return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid]; - }, - - // Get the model at the given index. - at: function(index) { - return this.models[index]; - }, - - // Return models with matching attributes. Useful for simple cases of - // `filter`. - where: function(attrs, first) { - if (_.isEmpty(attrs)) return first ? void 0 : []; - return this[first ? 'find' : 'filter'](function(model) { - for (var key in attrs) { - if (attrs[key] !== model.get(key)) return false; - } - return true; - }); - }, - - // Return the first model with matching attributes. Useful for simple cases - // of `find`. - findWhere: function(attrs) { - return this.where(attrs, true); - }, - - // Force the collection to re-sort itself. You don't need to call this under - // normal circumstances, as the set will maintain sort order as each item - // is added. - sort: function(options) { - if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); - options || (options = {}); - - // Run sort based on type of `comparator`. - if (_.isString(this.comparator) || this.comparator.length === 1) { - this.models = this.sortBy(this.comparator, this); - } else { - this.models.sort(_.bind(this.comparator, this)); - } - - if (!options.silent) this.trigger('sort', this, options); - return this; - }, - - // Pluck an attribute from each model in the collection. - pluck: function(attr) { - return _.invoke(this.models, 'get', attr); - }, - - // Fetch the default set of models for this collection, resetting the - // collection when they arrive. If `reset: true` is passed, the response - // data will be passed through the `reset` method instead of `set`. - fetch: function(options) { - options = options ? _.clone(options) : {}; - if (options.parse === void 0) options.parse = true; - var success = options.success; - var collection = this; - options.success = function(resp) { - var method = options.reset ? 'reset' : 'set'; - collection[method](resp, options); - if (success) success(collection, resp, options); - collection.trigger('sync', collection, resp, options); - }; - wrapError(this, options); - return this.sync('read', this, options); - }, - - // Create a new instance of a model in this collection. Add the model to the - // collection immediately, unless `wait: true` is passed, in which case we - // wait for the server to agree. - create: function(model, options) { - options = options ? _.clone(options) : {}; - if (!(model = this._prepareModel(model, options))) return false; - if (!options.wait) this.add(model, options); - var collection = this; - var success = options.success; - options.success = function(model, resp) { - if (options.wait) collection.add(model, options); - if (success) success(model, resp, options); - }; - model.save(null, options); - return model; - }, - - // **parse** converts a response into a list of models to be added to the - // collection. The default implementation is just to pass it through. - parse: function(resp, options) { - return resp; - }, - - // Create a new collection with an identical list of models as this one. - clone: function() { - return new this.constructor(this.models); - }, - - // Private method to reset all internal state. Called when the collection - // is first initialized or reset. - _reset: function() { - this.length = 0; - this.models = []; - this._byId = {}; - }, - - // Prepare a hash of attributes (or other model) to be added to this - // collection. - _prepareModel: function(attrs, options) { - if (attrs instanceof Model) return attrs; - options = options ? _.clone(options) : {}; - options.collection = this; - var model = new this.model(attrs, options); - if (!model.validationError) return model; - this.trigger('invalid', this, model.validationError, options); - return false; - }, - - // Internal method to create a model's ties to a collection. - _addReference: function(model, options) { - this._byId[model.cid] = model; - if (model.id != null) this._byId[model.id] = model; - if (!model.collection) model.collection = this; - model.on('all', this._onModelEvent, this); - }, - - // Internal method to sever a model's ties to a collection. - _removeReference: function(model, options) { - if (this === model.collection) delete model.collection; - model.off('all', this._onModelEvent, this); - }, - - // Internal method called every time a model in the set fires an event. - // Sets need to update their indexes when models change ids. All other - // events simply proxy through. "add" and "remove" events that originate - // in other collections are ignored. - _onModelEvent: function(event, model, collection, options) { - if ((event === 'add' || event === 'remove') && collection !== this) return; - if (event === 'destroy') this.remove(model, options); - if (model && event === 'change:' + model.idAttribute) { - delete this._byId[model.previous(model.idAttribute)]; - if (model.id != null) this._byId[model.id] = model; - } - this.trigger.apply(this, arguments); - } - - }); - - // Underscore methods that we want to implement on the Collection. - // 90% of the core usefulness of Backbone Collections is actually implemented - // right here: - var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', - 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', - 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', - 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest', - 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle', - 'lastIndexOf', 'isEmpty', 'chain', 'sample']; - - // Mix in each Underscore method as a proxy to `Collection#models`. - _.each(methods, function(method) { - Collection.prototype[method] = function() { - var args = slice.call(arguments); - args.unshift(this.models); - return _[method].apply(_, args); - }; - }); - - // Underscore methods that take a property name as an argument. - var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy']; - - // Use attributes instead of properties. - _.each(attributeMethods, function(method) { - Collection.prototype[method] = function(value, context) { - var iterator = _.isFunction(value) ? value : function(model) { - return model.get(value); - }; - return _[method](this.models, iterator, context); - }; - }); - - // Backbone.View - // ------------- - - // Backbone Views are almost more convention than they are actual code. A View - // is simply a JavaScript object that represents a logical chunk of UI in the - // DOM. This might be a single item, an entire list, a sidebar or panel, or - // even the surrounding frame which wraps your whole app. Defining a chunk of - // UI as a **View** allows you to define your DOM events declaratively, without - // having to worry about render order ... and makes it easy for the view to - // react to specific changes in the state of your models. - - // Creating a Backbone.View creates its initial element outside of the DOM, - // if an existing element is not provided... - var View = Backbone.View = function(options) { - this.cid = _.uniqueId('view'); - options || (options = {}); - _.extend(this, _.pick(options, viewOptions)); - this._ensureElement(); - this.initialize.apply(this, arguments); - this.delegateEvents(); - }; - - // Cached regex to split keys for `delegate`. - var delegateEventSplitter = /^(\S+)\s*(.*)$/; - - // List of view options to be merged as properties. - var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; - - // Set up all inheritable **Backbone.View** properties and methods. - _.extend(View.prototype, Events, { - - // The default `tagName` of a View's element is `"div"`. - tagName: 'div', - - // jQuery delegate for element lookup, scoped to DOM elements within the - // current view. This should be preferred to global lookups where possible. - $: function(selector) { - return this.$el.find(selector); - }, - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // **render** is the core function that your view should override, in order - // to populate its element (`this.el`), with the appropriate HTML. The - // convention is for **render** to always return `this`. - render: function() { - return this; - }, - - // Remove this view by taking the element out of the DOM, and removing any - // applicable Backbone.Events listeners. - remove: function() { - this.$el.remove(); - this.stopListening(); - return this; - }, - - // Change the view's element (`this.el` property), including event - // re-delegation. - setElement: function(element, delegate) { - if (this.$el) this.undelegateEvents(); - this.$el = element instanceof Backbone.$ ? element : Backbone.$(element); - this.el = this.$el[0]; - if (delegate !== false) this.delegateEvents(); - return this; - }, - - // Set callbacks, where `this.events` is a hash of - // - // *{"event selector": "callback"}* - // - // { - // 'mousedown .title': 'edit', - // 'click .button': 'save', - // 'click .open': function(e) { ... } - // } - // - // pairs. Callbacks will be bound to the view, with `this` set properly. - // Uses event delegation for efficiency. - // Omitting the selector binds the event to `this.el`. - // This only works for delegate-able events: not `focus`, `blur`, and - // not `change`, `submit`, and `reset` in Internet Explorer. - delegateEvents: function(events) { - if (!(events || (events = _.result(this, 'events')))) return this; - this.undelegateEvents(); - for (var key in events) { - var method = events[key]; - if (!_.isFunction(method)) method = this[events[key]]; - if (!method) continue; - - var match = key.match(delegateEventSplitter); - var eventName = match[1], selector = match[2]; - method = _.bind(method, this); - eventName += '.delegateEvents' + this.cid; - if (selector === '') { - this.$el.on(eventName, method); - } else { - this.$el.on(eventName, selector, method); - } - } - return this; - }, - - // Clears all callbacks previously bound to the view with `delegateEvents`. - // You usually don't need to use this, but may wish to if you have multiple - // Backbone views attached to the same DOM element. - undelegateEvents: function() { - this.$el.off('.delegateEvents' + this.cid); - return this; - }, - - // Ensure that the View has a DOM element to render into. - // If `this.el` is a string, pass it through `$()`, take the first - // matching element, and re-assign it to `el`. Otherwise, create - // an element from the `id`, `className` and `tagName` properties. - _ensureElement: function() { - if (!this.el) { - var attrs = _.extend({}, _.result(this, 'attributes')); - if (this.id) attrs.id = _.result(this, 'id'); - if (this.className) attrs['class'] = _.result(this, 'className'); - var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs); - this.setElement($el, false); - } else { - this.setElement(_.result(this, 'el'), false); - } - } - - }); - - // Backbone.sync - // ------------- - - // Override this function to change the manner in which Backbone persists - // models to the server. You will be passed the type of request, and the - // model in question. By default, makes a RESTful Ajax request - // to the model's `url()`. Some possible customizations could be: - // - // * Use `setTimeout` to batch rapid-fire updates into a single request. - // * Send up the models as XML instead of JSON. - // * Persist models via WebSockets instead of Ajax. - // - // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests - // as `POST`, with a `_method` parameter containing the true HTTP method, - // as well as all requests with the body as `application/x-www-form-urlencoded` - // instead of `application/json` with the model in a param named `model`. - // Useful when interfacing with server-side languages like **PHP** that make - // it difficult to read the body of `PUT` requests. - Backbone.sync = function(method, model, options) { - var type = methodMap[method]; - - // Default options, unless specified. - _.defaults(options || (options = {}), { - emulateHTTP: Backbone.emulateHTTP, - emulateJSON: Backbone.emulateJSON - }); - - // Default JSON-request options. - var params = {type: type, dataType: 'json'}; - - // Ensure that we have a URL. - if (!options.url) { - params.url = _.result(model, 'url') || urlError(); - } - - // Ensure that we have the appropriate request data. - if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { - params.contentType = 'application/json'; - params.data = JSON.stringify(options.attrs || model.toJSON(options)); - } - - // For older servers, emulate JSON by encoding the request into an HTML-form. - if (options.emulateJSON) { - params.contentType = 'application/x-www-form-urlencoded'; - params.data = params.data ? {model: params.data} : {}; - } - - // For older servers, emulate HTTP by mimicking the HTTP method with `_method` - // And an `X-HTTP-Method-Override` header. - if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { - params.type = 'POST'; - if (options.emulateJSON) params.data._method = type; - var beforeSend = options.beforeSend; - options.beforeSend = function(xhr) { - xhr.setRequestHeader('X-HTTP-Method-Override', type); - if (beforeSend) return beforeSend.apply(this, arguments); - }; - } - - // Don't process data on a non-GET request. - if (params.type !== 'GET' && !options.emulateJSON) { - params.processData = false; - } - - // If we're sending a `PATCH` request, and we're in an old Internet Explorer - // that still has ActiveX enabled by default, override jQuery to use that - // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8. - if (params.type === 'PATCH' && noXhrPatch) { - params.xhr = function() { - return new ActiveXObject("Microsoft.XMLHTTP"); - }; - } - - // Make the request, allowing the user to override any Ajax options. - var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); - model.trigger('request', model, xhr, options); - return xhr; - }; - - var noXhrPatch = - typeof window !== 'undefined' && !!window.ActiveXObject && - !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent); - - // Map from CRUD to HTTP for our default `Backbone.sync` implementation. - var methodMap = { - 'create': 'POST', - 'update': 'PUT', - 'patch': 'PATCH', - 'delete': 'DELETE', - 'read': 'GET' - }; - - // Set the default implementation of `Backbone.ajax` to proxy through to `$`. - // Override this if you'd like to use a different library. - Backbone.ajax = function() { - return Backbone.$.ajax.apply(Backbone.$, arguments); - }; - - // Backbone.Router - // --------------- - - // Routers map faux-URLs to actions, and fire events when routes are - // matched. Creating a new one sets its `routes` hash, if not set statically. - var Router = Backbone.Router = function(options) { - options || (options = {}); - if (options.routes) this.routes = options.routes; - this._bindRoutes(); - this.initialize.apply(this, arguments); - }; - - // Cached regular expressions for matching named param parts and splatted - // parts of route strings. - var optionalParam = /\((.*?)\)/g; - var namedParam = /(\(\?)?:\w+/g; - var splatParam = /\*\w+/g; - var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; - - // Set up all inheritable **Backbone.Router** properties and methods. - _.extend(Router.prototype, Events, { - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Manually bind a single named route to a callback. For example: - // - // this.route('search/:query/p:num', 'search', function(query, num) { - // ... - // }); - // - route: function(route, name, callback) { - if (!_.isRegExp(route)) route = this._routeToRegExp(route); - if (_.isFunction(name)) { - callback = name; - name = ''; - } - if (!callback) callback = this[name]; - var router = this; - Backbone.history.route(route, function(fragment) { - var args = router._extractParameters(route, fragment); - router.execute(callback, args); - router.trigger.apply(router, ['route:' + name].concat(args)); - router.trigger('route', name, args); - Backbone.history.trigger('route', router, name, args); - }); - return this; - }, - - // Execute a route handler with the provided parameters. This is an - // excellent place to do pre-route setup or post-route cleanup. - execute: function(callback, args) { - if (callback) callback.apply(this, args); - }, - - // Simple proxy to `Backbone.history` to save a fragment into the history. - navigate: function(fragment, options) { - Backbone.history.navigate(fragment, options); - return this; - }, - - // Bind all defined routes to `Backbone.history`. We have to reverse the - // order of the routes here to support behavior where the most general - // routes can be defined at the bottom of the route map. - _bindRoutes: function() { - if (!this.routes) return; - this.routes = _.result(this, 'routes'); - var route, routes = _.keys(this.routes); - while ((route = routes.pop()) != null) { - this.route(route, this.routes[route]); - } - }, - - // Convert a route string into a regular expression, suitable for matching - // against the current location hash. - _routeToRegExp: function(route) { - route = route.replace(escapeRegExp, '\\$&') - .replace(optionalParam, '(?:$1)?') - .replace(namedParam, function(match, optional) { - return optional ? match : '([^/?]+)'; - }) - .replace(splatParam, '([^?]*?)'); - return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); - }, - - // Given a route, and a URL fragment that it matches, return the array of - // extracted decoded parameters. Empty or unmatched parameters will be - // treated as `null` to normalize cross-browser behavior. - _extractParameters: function(route, fragment) { - var params = route.exec(fragment).slice(1); - return _.map(params, function(param, i) { - // Don't decode the search params. - if (i === params.length - 1) return param || null; - return param ? decodeURIComponent(param) : null; - }); - } - - }); - - // Backbone.History - // ---------------- - - // Handles cross-browser history management, based on either - // [pushState](http://diveintohtml5.info/history.html) and real URLs, or - // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) - // and URL fragments. If the browser supports neither (old IE, natch), - // falls back to polling. - var History = Backbone.History = function() { - this.handlers = []; - _.bindAll(this, 'checkUrl'); - - // Ensure that `History` can be used outside of the browser. - if (typeof window !== 'undefined') { - this.location = window.location; - this.history = window.history; - } - }; - - // Cached regex for stripping a leading hash/slash and trailing space. - var routeStripper = /^[#\/]|\s+$/g; - - // Cached regex for stripping leading and trailing slashes. - var rootStripper = /^\/+|\/+$/g; - - // Cached regex for detecting MSIE. - var isExplorer = /msie [\w.]+/; - - // Cached regex for removing a trailing slash. - var trailingSlash = /\/$/; - - // Cached regex for stripping urls of hash. - var pathStripper = /#.*$/; - - // Has the history handling already been started? - History.started = false; - - // Set up all inheritable **Backbone.History** properties and methods. - _.extend(History.prototype, Events, { - - // The default interval to poll for hash changes, if necessary, is - // twenty times a second. - interval: 50, - - // Are we at the app root? - atRoot: function() { - return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root; - }, - - // Gets the true hash value. Cannot use location.hash directly due to bug - // in Firefox where location.hash will always be decoded. - getHash: function(window) { - var match = (window || this).location.href.match(/#(.*)$/); - return match ? match[1] : ''; - }, - - // Get the cross-browser normalized URL fragment, either from the URL, - // the hash, or the override. - getFragment: function(fragment, forcePushState) { - if (fragment == null) { - if (this._hasPushState || !this._wantsHashChange || forcePushState) { - fragment = decodeURI(this.location.pathname + this.location.search); - var root = this.root.replace(trailingSlash, ''); - if (!fragment.indexOf(root)) fragment = fragment.slice(root.length); - } else { - fragment = this.getHash(); - } - } - return fragment.replace(routeStripper, ''); - }, - - // Start the hash change handling, returning `true` if the current URL matches - // an existing route, and `false` otherwise. - start: function(options) { - if (History.started) throw new Error("Backbone.history has already been started"); - History.started = true; - - // Figure out the initial configuration. Do we need an iframe? - // Is pushState desired ... is it available? - this.options = _.extend({root: '/'}, this.options, options); - this.root = this.options.root; - this._wantsHashChange = this.options.hashChange !== false; - this._wantsPushState = !!this.options.pushState; - this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); - var fragment = this.getFragment(); - var docMode = document.documentMode; - var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); - - // Normalize root to always include a leading and trailing slash. - this.root = ('/' + this.root + '/').replace(rootStripper, '/'); - - if (oldIE && this._wantsHashChange) { - var frame = Backbone.$('', - error : '

The requested content cannot be loaded.
Please try again later.

', - closeBtn : '', - next : '', - prev : '' - }, - - // Properties for each animation type - // Opening fancyBox - openEffect : 'fade', // 'elastic', 'fade' or 'none' - openSpeed : 250, - openEasing : 'swing', - openOpacity : true, - openMethod : 'zoomIn', - - // Closing fancyBox - closeEffect : 'fade', // 'elastic', 'fade' or 'none' - closeSpeed : 250, - closeEasing : 'swing', - closeOpacity : true, - closeMethod : 'zoomOut', - - // Changing next gallery item - nextEffect : 'elastic', // 'elastic', 'fade' or 'none' - nextSpeed : 250, - nextEasing : 'swing', - nextMethod : 'changeIn', - - // Changing previous gallery item - prevEffect : 'elastic', // 'elastic', 'fade' or 'none' - prevSpeed : 250, - prevEasing : 'swing', - prevMethod : 'changeOut', - - // Enable default helpers - helpers : { - overlay : true, - title : true - }, - - // Callbacks - onCancel : $.noop, // If canceling - beforeLoad : $.noop, // Before loading - afterLoad : $.noop, // After loading - beforeShow : $.noop, // Before changing in current item - afterShow : $.noop, // After opening - beforeChange : $.noop, // Before changing gallery item - beforeClose : $.noop, // Before closing - afterClose : $.noop // After closing - }, - - //Current state - group : {}, // Selected group - opts : {}, // Group options - previous : null, // Previous element - coming : null, // Element being loaded - current : null, // Currently loaded element - isActive : false, // Is activated - isOpen : false, // Is currently open - isOpened : false, // Have been fully opened at least once - - wrap : null, - skin : null, - outer : null, - inner : null, - - player : { - timer : null, - isActive : false - }, - - // Loaders - ajaxLoad : null, - imgPreload : null, - - // Some collections - transitions : {}, - helpers : {}, - - /* - * Static methods - */ - - open: function (group, opts) { - if (!group) { - return; - } - - if (!$.isPlainObject(opts)) { - opts = {}; - } - - // Close if already active - if (false === F.close(true)) { - return; - } - - // Normalize group - if (!$.isArray(group)) { - group = isQuery(group) ? $(group).get() : [group]; - } - - // Recheck if the type of each element is `object` and set content type (image, ajax, etc) - $.each(group, function(i, element) { - var obj = {}, - href, - title, - content, - type, - rez, - hrefParts, - selector; - - if ($.type(element) === "object") { - // Check if is DOM element - if (element.nodeType) { - element = $(element); - } - - if (isQuery(element)) { - obj = { - href : element.data('fancybox-href') || element.attr('href'), - title : element.data('fancybox-title') || element.attr('title'), - isDom : true, - element : element - }; - - if ($.metadata) { - $.extend(true, obj, element.metadata()); - } - - } else { - obj = element; - } - } - - href = opts.href || obj.href || (isString(element) ? element : null); - title = opts.title !== undefined ? opts.title : obj.title || ''; - - content = opts.content || obj.content; - type = content ? 'html' : (opts.type || obj.type); - - if (!type && obj.isDom) { - type = element.data('fancybox-type'); - - if (!type) { - rez = element.prop('class').match(/fancybox\.(\w+)/); - type = rez ? rez[1] : null; - } - } - - if (isString(href)) { - // Try to guess the content type - if (!type) { - if (F.isImage(href)) { - type = 'image'; - - } else if (F.isSWF(href)) { - type = 'swf'; - - } else if (href.charAt(0) === '#') { - type = 'inline'; - - } else if (isString(element)) { - type = 'html'; - content = element; - } - } - - // Split url into two pieces with source url and content selector, e.g, - // "/mypage.html #my_id" will load "/mypage.html" and display element having id "my_id" - if (type === 'ajax') { - hrefParts = href.split(/\s+/, 2); - href = hrefParts.shift(); - selector = hrefParts.shift(); - } - } - - if (!content) { - if (type === 'inline') { - if (href) { - content = $( isString(href) ? href.replace(/.*(?=#[^\s]+$)/, '') : href ); //strip for ie7 - - } else if (obj.isDom) { - content = element; - } - - } else if (type === 'html') { - content = href; - - } else if (!type && !href && obj.isDom) { - type = 'inline'; - content = element; - } - } - - $.extend(obj, { - href : href, - type : type, - content : content, - title : title, - selector : selector - }); - - group[ i ] = obj; - }); - - // Extend the defaults - F.opts = $.extend(true, {}, F.defaults, opts); - - // All options are merged recursive except keys - if (opts.keys !== undefined) { - F.opts.keys = opts.keys ? $.extend({}, F.defaults.keys, opts.keys) : false; - } - - F.group = group; - - return F._start(F.opts.index); - }, - - // Cancel image loading or abort ajax request - cancel: function () { - var coming = F.coming; - - if (!coming || false === F.trigger('onCancel')) { - return; - } - - F.hideLoading(); - - if (F.ajaxLoad) { - F.ajaxLoad.abort(); - } - - F.ajaxLoad = null; - - if (F.imgPreload) { - F.imgPreload.onload = F.imgPreload.onerror = null; - } - - if (coming.wrap) { - coming.wrap.stop(true, true).trigger('onReset').remove(); - } - - F.coming = null; - - // If the first item has been canceled, then clear everything - if (!F.current) { - F._afterZoomOut( coming ); - } - }, - - // Start closing animation if is open; remove immediately if opening/closing - close: function (event) { - F.cancel(); - - if (false === F.trigger('beforeClose')) { - return; - } - - F.unbindEvents(); - - if (!F.isActive) { - return; - } - - if (!F.isOpen || event === true) { - $('.fancybox-wrap').stop(true).trigger('onReset').remove(); - - F._afterZoomOut(); - - } else { - F.isOpen = F.isOpened = false; - F.isClosing = true; - - $('.fancybox-item, .fancybox-nav').remove(); - - F.wrap.stop(true, true).removeClass('fancybox-opened'); - - F.transitions[ F.current.closeMethod ](); - } - }, - - // Manage slideshow: - // $.fancybox.play(); - toggle slideshow - // $.fancybox.play( true ); - start - // $.fancybox.play( false ); - stop - play: function ( action ) { - var clear = function () { - clearTimeout(F.player.timer); - }, - set = function () { - clear(); - - if (F.current && F.player.isActive) { - F.player.timer = setTimeout(F.next, F.current.playSpeed); - } - }, - stop = function () { - clear(); - - D.unbind('.player'); - - F.player.isActive = false; - - F.trigger('onPlayEnd'); - }, - start = function () { - if (F.current && (F.current.loop || F.current.index < F.group.length - 1)) { - F.player.isActive = true; - - D.bind({ - 'onCancel.player beforeClose.player' : stop, - 'onUpdate.player' : set, - 'beforeLoad.player' : clear - }); - - set(); - - F.trigger('onPlayStart'); - } - }; - - if (action === true || (!F.player.isActive && action !== false)) { - start(); - } else { - stop(); - } - }, - - // Navigate to next gallery item - next: function ( direction ) { - var current = F.current; - - if (current) { - if (!isString(direction)) { - direction = current.direction.next; - } - - F.jumpto(current.index + 1, direction, 'next'); - } - }, - - // Navigate to previous gallery item - prev: function ( direction ) { - var current = F.current; - - if (current) { - if (!isString(direction)) { - direction = current.direction.prev; - } - - F.jumpto(current.index - 1, direction, 'prev'); - } - }, - - // Navigate to gallery item by index - jumpto: function ( index, direction, router ) { - var current = F.current; - - if (!current) { - return; - } - - index = getScalar(index); - - F.direction = direction || current.direction[ (index >= current.index ? 'next' : 'prev') ]; - F.router = router || 'jumpto'; - - if (current.loop) { - if (index < 0) { - index = current.group.length + (index % current.group.length); - } - - index = index % current.group.length; - } - - if (current.group[ index ] !== undefined) { - F.cancel(); - - F._start(index); - } - }, - - // Center inside viewport and toggle position type to fixed or absolute if needed - reposition: function (e, onlyAbsolute) { - var current = F.current, - wrap = current ? current.wrap : null, - pos; - - if (wrap) { - pos = F._getPosition(onlyAbsolute); - - if (e && e.type === 'scroll') { - delete pos.position; - - wrap.stop(true, true).animate(pos, 200); - - } else { - wrap.css(pos); - - current.pos = $.extend({}, current.dim, pos); - } - } - }, - - update: function (e) { - var type = (e && e.type), - anyway = !type || type === 'orientationchange'; - - if (anyway) { - clearTimeout(didUpdate); - - didUpdate = null; - } - - if (!F.isOpen || didUpdate) { - return; - } - - didUpdate = setTimeout(function() { - var current = F.current; - - if (!current || F.isClosing) { - return; - } - - F.wrap.removeClass('fancybox-tmp'); - - if (anyway || type === 'load' || (type === 'resize' && current.autoResize)) { - F._setDimension(); - } - - if (!(type === 'scroll' && current.canShrink)) { - F.reposition(e); - } - - F.trigger('onUpdate'); - - didUpdate = null; - - }, (anyway && !isTouch ? 0 : 300)); - }, - - // Shrink content to fit inside viewport or restore if resized - toggle: function ( action ) { - if (F.isOpen) { - F.current.fitToView = $.type(action) === "boolean" ? action : !F.current.fitToView; - - // Help browser to restore document dimensions - if (isTouch) { - F.wrap.removeAttr('style').addClass('fancybox-tmp'); - - F.trigger('onUpdate'); - } - - F.update(); - } - }, - - hideLoading: function () { - D.unbind('.loading'); - - $('#fancybox-loading').remove(); - }, - - showLoading: function () { - var el, viewport; - - F.hideLoading(); - - el = $('
').click(F.cancel).appendTo('body'); - - // If user will press the escape-button, the request will be canceled - D.bind('keydown.loading', function(e) { - if ((e.which || e.keyCode) === 27) { - e.preventDefault(); - - F.cancel(); - } - }); - - if (!F.defaults.fixed) { - viewport = F.getViewport(); - - el.css({ - position : 'absolute', - top : (viewport.h * 0.5) + viewport.y, - left : (viewport.w * 0.5) + viewport.x - }); - } - }, - - getViewport: function () { - var locked = (F.current && F.current.locked) || false, - rez = { - x: W.scrollLeft(), - y: W.scrollTop() - }; - - if (locked) { - rez.w = locked[0].clientWidth; - rez.h = locked[0].clientHeight; - - } else { - // See http://bugs.jquery.com/ticket/6724 - rez.w = isTouch && window.innerWidth ? window.innerWidth : W.width(); - rez.h = isTouch && window.innerHeight ? window.innerHeight : W.height(); - } - - return rez; - }, - - // Unbind the keyboard / clicking actions - unbindEvents: function () { - if (F.wrap && isQuery(F.wrap)) { - F.wrap.unbind('.fb'); - } - - D.unbind('.fb'); - W.unbind('.fb'); - }, - - bindEvents: function () { - var current = F.current, - keys; - - if (!current) { - return; - } - - // Changing document height on iOS devices triggers a 'resize' event, - // that can change document height... repeating infinitely - W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb') + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update); - - keys = current.keys; - - if (keys) { - D.bind('keydown.fb', function (e) { - var code = e.which || e.keyCode, - target = e.target || e.srcElement; - - // Skip esc key if loading, because showLoading will cancel preloading - if (code === 27 && F.coming) { - return false; - } - - // Ignore key combinations and key events within form elements - if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !(target && (target.type || $(target).is('[contenteditable]')))) { - $.each(keys, function(i, val) { - if (current.group.length > 1 && val[ code ] !== undefined) { - F[ i ]( val[ code ] ); - - e.preventDefault(); - return false; - } - - if ($.inArray(code, val) > -1) { - F[ i ] (); - - e.preventDefault(); - return false; - } - }); - } - }); - } - - if ($.fn.mousewheel && current.mouseWheel) { - F.wrap.bind('mousewheel.fb', function (e, delta, deltaX, deltaY) { - var target = e.target || null, - parent = $(target), - canScroll = false; - - while (parent.length) { - if (canScroll || parent.is('.fancybox-skin') || parent.is('.fancybox-wrap')) { - break; - } - - canScroll = isScrollable( parent[0] ); - parent = $(parent).parent(); - } - - if (delta !== 0 && !canScroll) { - if (F.group.length > 1 && !current.canShrink) { - if (deltaY > 0 || deltaX > 0) { - F.prev( deltaY > 0 ? 'down' : 'left' ); - - } else if (deltaY < 0 || deltaX < 0) { - F.next( deltaY < 0 ? 'up' : 'right' ); - } - - e.preventDefault(); - } - } - }); - } - }, - - trigger: function (event, o) { - var ret, obj = o || F.coming || F.current; - - if (!obj) { - return; - } - - if ($.isFunction( obj[event] )) { - ret = obj[event].apply(obj, Array.prototype.slice.call(arguments, 1)); - } - - if (ret === false) { - return false; - } - - if (obj.helpers) { - $.each(obj.helpers, function (helper, opts) { - if (opts && F.helpers[helper] && $.isFunction(F.helpers[helper][event])) { - F.helpers[helper][event]($.extend(true, {}, F.helpers[helper].defaults, opts), obj); - } - }); - } - - D.trigger(event); - }, - - isImage: function (str) { - return isString(str) && str.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i); - }, - - isSWF: function (str) { - return isString(str) && str.match(/\.(swf)((\?|#).*)?$/i); - }, - - _start: function (index) { - var coming = {}, - obj, - href, - type, - margin, - padding; - - index = getScalar( index ); - obj = F.group[ index ] || null; - - if (!obj) { - return false; - } - - coming = $.extend(true, {}, F.opts, obj); - - // Convert margin and padding properties to array - top, right, bottom, left - margin = coming.margin; - padding = coming.padding; - - if ($.type(margin) === 'number') { - coming.margin = [margin, margin, margin, margin]; - } - - if ($.type(padding) === 'number') { - coming.padding = [padding, padding, padding, padding]; - } - - // 'modal' propery is just a shortcut - if (coming.modal) { - $.extend(true, coming, { - closeBtn : false, - closeClick : false, - nextClick : false, - arrows : false, - mouseWheel : false, - keys : null, - helpers: { - overlay : { - closeClick : false - } - } - }); - } - - // 'autoSize' property is a shortcut, too - if (coming.autoSize) { - coming.autoWidth = coming.autoHeight = true; - } - - if (coming.width === 'auto') { - coming.autoWidth = true; - } - - if (coming.height === 'auto') { - coming.autoHeight = true; - } - - /* - * Add reference to the group, so it`s possible to access from callbacks, example: - * afterLoad : function() { - * this.title = 'Image ' + (this.index + 1) + ' of ' + this.group.length + (this.title ? ' - ' + this.title : ''); - * } - */ - - coming.group = F.group; - coming.index = index; - - // Give a chance for callback or helpers to update coming item (type, title, etc) - F.coming = coming; - - if (false === F.trigger('beforeLoad')) { - F.coming = null; - - return; - } - - type = coming.type; - href = coming.href; - - if (!type) { - F.coming = null; - - //If we can not determine content type then drop silently or display next/prev item if looping through gallery - if (F.current && F.router && F.router !== 'jumpto') { - F.current.index = index; - - return F[ F.router ]( F.direction ); - } - - return false; - } - - F.isActive = true; - - if (type === 'image' || type === 'swf') { - coming.autoHeight = coming.autoWidth = false; - coming.scrolling = 'visible'; - } - - if (type === 'image') { - coming.aspectRatio = true; - } - - if (type === 'iframe' && isTouch) { - coming.scrolling = 'scroll'; - } - - // Build the neccessary markup - coming.wrap = $(coming.tpl.wrap).addClass('fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-type-' + type + ' fancybox-tmp ' + coming.wrapCSS).appendTo( coming.parent || 'body' ); - - $.extend(coming, { - skin : $('.fancybox-skin', coming.wrap), - outer : $('.fancybox-outer', coming.wrap), - inner : $('.fancybox-inner', coming.wrap) - }); - - $.each(["Top", "Right", "Bottom", "Left"], function(i, v) { - coming.skin.css('padding' + v, getValue(coming.padding[ i ])); - }); - - F.trigger('onReady'); - - // Check before try to load; 'inline' and 'html' types need content, others - href - if (type === 'inline' || type === 'html') { - if (!coming.content || !coming.content.length) { - return F._error( 'content' ); - } - - } else if (!href) { - return F._error( 'href' ); - } - - if (type === 'image') { - F._loadImage(); - - } else if (type === 'ajax') { - F._loadAjax(); - - } else if (type === 'iframe') { - F._loadIframe(); - - } else { - F._afterLoad(); - } - }, - - _error: function ( type ) { - $.extend(F.coming, { - type : 'html', - autoWidth : true, - autoHeight : true, - minWidth : 0, - minHeight : 0, - scrolling : 'no', - hasError : type, - content : F.coming.tpl.error - }); - - F._afterLoad(); - }, - - _loadImage: function () { - // Reset preload image so it is later possible to check "complete" property - var img = F.imgPreload = new Image(); - - img.onload = function () { - this.onload = this.onerror = null; - - F.coming.width = this.width / F.opts.pixelRatio; - F.coming.height = this.height / F.opts.pixelRatio; - - F._afterLoad(); - }; - - img.onerror = function () { - this.onload = this.onerror = null; - - F._error( 'image' ); - }; - - img.src = F.coming.href; - - if (img.complete !== true) { - F.showLoading(); - } - }, - - _loadAjax: function () { - var coming = F.coming; - - F.showLoading(); - - F.ajaxLoad = $.ajax($.extend({}, coming.ajax, { - url: coming.href, - error: function (jqXHR, textStatus) { - if (F.coming && textStatus !== 'abort') { - F._error( 'ajax', jqXHR ); - - } else { - F.hideLoading(); - } - }, - success: function (data, textStatus) { - if (textStatus === 'success') { - coming.content = data; - - F._afterLoad(); - } - } - })); - }, - - _loadIframe: function() { - var coming = F.coming, - iframe = $(coming.tpl.iframe.replace(/\{rnd\}/g, new Date().getTime())) - .attr('scrolling', isTouch ? 'auto' : coming.iframe.scrolling) - .attr('src', coming.href); - - // This helps IE - $(coming.wrap).bind('onReset', function () { - try { - $(this).find('iframe').hide().attr('src', '//about:blank').end().empty(); - } catch (e) {} - }); - - if (coming.iframe.preload) { - F.showLoading(); - - iframe.one('load', function() { - $(this).data('ready', 1); - - // iOS will lose scrolling if we resize - if (!isTouch) { - $(this).bind('load.fb', F.update); - } - - // Without this trick: - // - iframe won't scroll on iOS devices - // - IE7 sometimes displays empty iframe - $(this).parents('.fancybox-wrap').width('100%').removeClass('fancybox-tmp').show(); - - F._afterLoad(); - }); - } - - coming.content = iframe.appendTo( coming.inner ); - - if (!coming.iframe.preload) { - F._afterLoad(); - } - }, - - _preloadImages: function() { - var group = F.group, - current = F.current, - len = group.length, - cnt = current.preload ? Math.min(current.preload, len - 1) : 0, - item, - i; - - for (i = 1; i <= cnt; i += 1) { - item = group[ (current.index + i ) % len ]; - - if (item.type === 'image' && item.href) { - new Image().src = item.href; - } - } - }, - - _afterLoad: function () { - var coming = F.coming, - previous = F.current, - placeholder = 'fancybox-placeholder', - current, - content, - type, - scrolling, - href, - embed; - - F.hideLoading(); - - if (!coming || F.isActive === false) { - return; - } - - if (false === F.trigger('afterLoad', coming, previous)) { - coming.wrap.stop(true).trigger('onReset').remove(); - - F.coming = null; - - return; - } - - if (previous) { - F.trigger('beforeChange', previous); - - previous.wrap.stop(true).removeClass('fancybox-opened') - .find('.fancybox-item, .fancybox-nav') - .remove(); - } - - F.unbindEvents(); - - current = coming; - content = coming.content; - type = coming.type; - scrolling = coming.scrolling; - - $.extend(F, { - wrap : current.wrap, - skin : current.skin, - outer : current.outer, - inner : current.inner, - current : current, - previous : previous - }); - - href = current.href; - - switch (type) { - case 'inline': - case 'ajax': - case 'html': - if (current.selector) { - content = $('
').html(content).find(current.selector); - - } else if (isQuery(content)) { - if (!content.data(placeholder)) { - content.data(placeholder, $('
').insertAfter( content ).hide() ); - } - - content = content.show().detach(); - - current.wrap.bind('onReset', function () { - if ($(this).find(content).length) { - content.hide().replaceAll( content.data(placeholder) ).data(placeholder, false); - } - }); - } - break; - - case 'image': - content = current.tpl.image.replace('{href}', href); - break; - - case 'swf': - content = ''; - embed = ''; - - $.each(current.swf, function(name, val) { - content += ''; - embed += ' ' + name + '="' + val + '"'; - }); - - content += ''; - break; - } - - if (!(isQuery(content) && content.parent().is(current.inner))) { - current.inner.append( content ); - } - - // Give a chance for helpers or callbacks to update elements - F.trigger('beforeShow'); - - // Set scrolling before calculating dimensions - current.inner.css('overflow', scrolling === 'yes' ? 'scroll' : (scrolling === 'no' ? 'hidden' : scrolling)); - - // Set initial dimensions and start position - F._setDimension(); - - F.reposition(); - - F.isOpen = false; - F.coming = null; - - F.bindEvents(); - - if (!F.isOpened) { - $('.fancybox-wrap').not( current.wrap ).stop(true).trigger('onReset').remove(); - - } else if (previous.prevMethod) { - F.transitions[ previous.prevMethod ](); - } - - F.transitions[ F.isOpened ? current.nextMethod : current.openMethod ](); - - F._preloadImages(); - }, - - _setDimension: function () { - var viewport = F.getViewport(), - steps = 0, - canShrink = false, - canExpand = false, - wrap = F.wrap, - skin = F.skin, - inner = F.inner, - current = F.current, - width = current.width, - height = current.height, - minWidth = current.minWidth, - minHeight = current.minHeight, - maxWidth = current.maxWidth, - maxHeight = current.maxHeight, - scrolling = current.scrolling, - scrollOut = current.scrollOutside ? current.scrollbarWidth : 0, - margin = current.margin, - wMargin = getScalar(margin[1] + margin[3]), - hMargin = getScalar(margin[0] + margin[2]), - wPadding, - hPadding, - wSpace, - hSpace, - origWidth, - origHeight, - origMaxWidth, - origMaxHeight, - ratio, - width_, - height_, - maxWidth_, - maxHeight_, - iframe, - body; - - // Reset dimensions so we could re-check actual size - wrap.add(skin).add(inner).width('auto').height('auto').removeClass('fancybox-tmp'); - - wPadding = getScalar(skin.outerWidth(true) - skin.width()); - hPadding = getScalar(skin.outerHeight(true) - skin.height()); - - // Any space between content and viewport (margin, padding, border, title) - wSpace = wMargin + wPadding; - hSpace = hMargin + hPadding; - - origWidth = isPercentage(width) ? (viewport.w - wSpace) * getScalar(width) / 100 : width; - origHeight = isPercentage(height) ? (viewport.h - hSpace) * getScalar(height) / 100 : height; - - if (current.type === 'iframe') { - iframe = current.content; - - if (current.autoHeight && iframe.data('ready') === 1) { - try { - if (iframe[0].contentWindow.document.location) { - inner.width( origWidth ).height(9999); - - body = iframe.contents().find('body'); - - if (scrollOut) { - body.css('overflow-x', 'hidden'); - } - - origHeight = body.outerHeight(true); - } - - } catch (e) {} - } - - } else if (current.autoWidth || current.autoHeight) { - inner.addClass( 'fancybox-tmp' ); - - // Set width or height in case we need to calculate only one dimension - if (!current.autoWidth) { - inner.width( origWidth ); - } - - if (!current.autoHeight) { - inner.height( origHeight ); - } - - if (current.autoWidth) { - origWidth = inner.width(); - } - - if (current.autoHeight) { - origHeight = inner.height(); - } - - inner.removeClass( 'fancybox-tmp' ); - } - - width = getScalar( origWidth ); - height = getScalar( origHeight ); - - ratio = origWidth / origHeight; - - // Calculations for the content - minWidth = getScalar(isPercentage(minWidth) ? getScalar(minWidth, 'w') - wSpace : minWidth); - maxWidth = getScalar(isPercentage(maxWidth) ? getScalar(maxWidth, 'w') - wSpace : maxWidth); - - minHeight = getScalar(isPercentage(minHeight) ? getScalar(minHeight, 'h') - hSpace : minHeight); - maxHeight = getScalar(isPercentage(maxHeight) ? getScalar(maxHeight, 'h') - hSpace : maxHeight); - - // These will be used to determine if wrap can fit in the viewport - origMaxWidth = maxWidth; - origMaxHeight = maxHeight; - - if (current.fitToView) { - maxWidth = Math.min(viewport.w - wSpace, maxWidth); - maxHeight = Math.min(viewport.h - hSpace, maxHeight); - } - - maxWidth_ = viewport.w - wMargin; - maxHeight_ = viewport.h - hMargin; - - if (current.aspectRatio) { - if (width > maxWidth) { - width = maxWidth; - height = getScalar(width / ratio); - } - - if (height > maxHeight) { - height = maxHeight; - width = getScalar(height * ratio); - } - - if (width < minWidth) { - width = minWidth; - height = getScalar(width / ratio); - } - - if (height < minHeight) { - height = minHeight; - width = getScalar(height * ratio); - } - - } else { - width = Math.max(minWidth, Math.min(width, maxWidth)); - - if (current.autoHeight && current.type !== 'iframe') { - inner.width( width ); - - height = inner.height(); - } - - height = Math.max(minHeight, Math.min(height, maxHeight)); - } - - // Try to fit inside viewport (including the title) - if (current.fitToView) { - inner.width( width ).height( height ); - - wrap.width( width + wPadding ); - - // Real wrap dimensions - width_ = wrap.width(); - height_ = wrap.height(); - - if (current.aspectRatio) { - while ((width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight) { - if (steps++ > 19) { - break; - } - - height = Math.max(minHeight, Math.min(maxHeight, height - 10)); - width = getScalar(height * ratio); - - if (width < minWidth) { - width = minWidth; - height = getScalar(width / ratio); - } - - if (width > maxWidth) { - width = maxWidth; - height = getScalar(width / ratio); - } - - inner.width( width ).height( height ); - - wrap.width( width + wPadding ); - - width_ = wrap.width(); - height_ = wrap.height(); - } - - } else { - width = Math.max(minWidth, Math.min(width, width - (width_ - maxWidth_))); - height = Math.max(minHeight, Math.min(height, height - (height_ - maxHeight_))); - } - } - - if (scrollOut && scrolling === 'auto' && height < origHeight && (width + wPadding + scrollOut) < maxWidth_) { - width += scrollOut; - } - - inner.width( width ).height( height ); - - wrap.width( width + wPadding ); - - width_ = wrap.width(); - height_ = wrap.height(); - - canShrink = (width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight; - canExpand = current.aspectRatio ? (width < origMaxWidth && height < origMaxHeight && width < origWidth && height < origHeight) : ((width < origMaxWidth || height < origMaxHeight) && (width < origWidth || height < origHeight)); - - $.extend(current, { - dim : { - width : getValue( width_ ), - height : getValue( height_ ) - }, - origWidth : origWidth, - origHeight : origHeight, - canShrink : canShrink, - canExpand : canExpand, - wPadding : wPadding, - hPadding : hPadding, - wrapSpace : height_ - skin.outerHeight(true), - skinSpace : skin.height() - height - }); - - if (!iframe && current.autoHeight && height > minHeight && height < maxHeight && !canExpand) { - inner.height('auto'); - } - }, - - _getPosition: function (onlyAbsolute) { - var current = F.current, - viewport = F.getViewport(), - margin = current.margin, - width = F.wrap.width() + margin[1] + margin[3], - height = F.wrap.height() + margin[0] + margin[2], - rez = { - position: 'absolute', - top : margin[0], - left : margin[3] - }; - - if (current.autoCenter && current.fixed && !onlyAbsolute && height <= viewport.h && width <= viewport.w) { - rez.position = 'fixed'; - - } else if (!current.locked) { - rez.top += viewport.y; - rez.left += viewport.x; - } - - rez.top = getValue(Math.max(rez.top, rez.top + ((viewport.h - height) * current.topRatio))); - rez.left = getValue(Math.max(rez.left, rez.left + ((viewport.w - width) * current.leftRatio))); - - return rez; - }, - - _afterZoomIn: function () { - var current = F.current; - - if (!current) { - return; - } - - F.isOpen = F.isOpened = true; - - F.wrap.css('overflow', 'visible').addClass('fancybox-opened'); - - F.update(); - - // Assign a click event - if ( current.closeClick || (current.nextClick && F.group.length > 1) ) { - F.inner.css('cursor', 'pointer').bind('click.fb', function(e) { - if (!$(e.target).is('a') && !$(e.target).parent().is('a')) { - e.preventDefault(); - - F[ current.closeClick ? 'close' : 'next' ](); - } - }); - } - - // Create a close button - if (current.closeBtn) { - $(current.tpl.closeBtn).appendTo(F.skin).bind('click.fb', function(e) { - e.preventDefault(); - - F.close(); - }); - } - - // Create navigation arrows - if (current.arrows && F.group.length > 1) { - if (current.loop || current.index > 0) { - $(current.tpl.prev).appendTo(F.outer).bind('click.fb', F.prev); - } - - if (current.loop || current.index < F.group.length - 1) { - $(current.tpl.next).appendTo(F.outer).bind('click.fb', F.next); - } - } - - F.trigger('afterShow'); - - // Stop the slideshow if this is the last item - if (!current.loop && current.index === current.group.length - 1) { - F.play( false ); - - } else if (F.opts.autoPlay && !F.player.isActive) { - F.opts.autoPlay = false; - - F.play(); - } - }, - - _afterZoomOut: function ( obj ) { - obj = obj || F.current; - - $('.fancybox-wrap').trigger('onReset').remove(); - - $.extend(F, { - group : {}, - opts : {}, - router : false, - current : null, - isActive : false, - isOpened : false, - isOpen : false, - isClosing : false, - wrap : null, - skin : null, - outer : null, - inner : null - }); - - F.trigger('afterClose', obj); - } - }); - - /* - * Default transitions - */ - - F.transitions = { - getOrigPosition: function () { - var current = F.current, - element = current.element, - orig = current.orig, - pos = {}, - width = 50, - height = 50, - hPadding = current.hPadding, - wPadding = current.wPadding, - viewport = F.getViewport(); - - if (!orig && current.isDom && element.is(':visible')) { - orig = element.find('img:first'); - - if (!orig.length) { - orig = element; - } - } - - if (isQuery(orig)) { - pos = orig.offset(); - - if (orig.is('img')) { - width = orig.outerWidth(); - height = orig.outerHeight(); - } - - } else { - pos.top = viewport.y + (viewport.h - height) * current.topRatio; - pos.left = viewport.x + (viewport.w - width) * current.leftRatio; - } - - if (F.wrap.css('position') === 'fixed' || current.locked) { - pos.top -= viewport.y; - pos.left -= viewport.x; - } - - pos = { - top : getValue(pos.top - hPadding * current.topRatio), - left : getValue(pos.left - wPadding * current.leftRatio), - width : getValue(width + wPadding), - height : getValue(height + hPadding) - }; - - return pos; - }, - - step: function (now, fx) { - var ratio, - padding, - value, - prop = fx.prop, - current = F.current, - wrapSpace = current.wrapSpace, - skinSpace = current.skinSpace; - - if (prop === 'width' || prop === 'height') { - ratio = fx.end === fx.start ? 1 : (now - fx.start) / (fx.end - fx.start); - - if (F.isClosing) { - ratio = 1 - ratio; - } - - padding = prop === 'width' ? current.wPadding : current.hPadding; - value = now - padding; - - F.skin[ prop ]( getScalar( prop === 'width' ? value : value - (wrapSpace * ratio) ) ); - F.inner[ prop ]( getScalar( prop === 'width' ? value : value - (wrapSpace * ratio) - (skinSpace * ratio) ) ); - } - }, - - zoomIn: function () { - var current = F.current, - startPos = current.pos, - effect = current.openEffect, - elastic = effect === 'elastic', - endPos = $.extend({opacity : 1}, startPos); - - // Remove "position" property that breaks older IE - delete endPos.position; - - if (elastic) { - startPos = this.getOrigPosition(); - - if (current.openOpacity) { - startPos.opacity = 0.1; - } - - } else if (effect === 'fade') { - startPos.opacity = 0.1; - } - - F.wrap.css(startPos).animate(endPos, { - duration : effect === 'none' ? 0 : current.openSpeed, - easing : current.openEasing, - step : elastic ? this.step : null, - complete : F._afterZoomIn - }); - }, - - zoomOut: function () { - var current = F.current, - effect = current.closeEffect, - elastic = effect === 'elastic', - endPos = {opacity : 0.1}; - - if (elastic) { - endPos = this.getOrigPosition(); - - if (current.closeOpacity) { - endPos.opacity = 0.1; - } - } - - F.wrap.animate(endPos, { - duration : effect === 'none' ? 0 : current.closeSpeed, - easing : current.closeEasing, - step : elastic ? this.step : null, - complete : F._afterZoomOut - }); - }, - - changeIn: function () { - var current = F.current, - effect = current.nextEffect, - startPos = current.pos, - endPos = { opacity : 1 }, - direction = F.direction, - distance = 200, - field; - - startPos.opacity = 0.1; - - if (effect === 'elastic') { - field = direction === 'down' || direction === 'up' ? 'top' : 'left'; - - if (direction === 'down' || direction === 'right') { - startPos[ field ] = getValue(getScalar(startPos[ field ]) - distance); - endPos[ field ] = '+=' + distance + 'px'; - - } else { - startPos[ field ] = getValue(getScalar(startPos[ field ]) + distance); - endPos[ field ] = '-=' + distance + 'px'; - } - } - - // Workaround for http://bugs.jquery.com/ticket/12273 - if (effect === 'none') { - F._afterZoomIn(); - - } else { - F.wrap.css(startPos).animate(endPos, { - duration : current.nextSpeed, - easing : current.nextEasing, - complete : F._afterZoomIn - }); - } - }, - - changeOut: function () { - var previous = F.previous, - effect = previous.prevEffect, - endPos = { opacity : 0.1 }, - direction = F.direction, - distance = 200; - - if (effect === 'elastic') { - endPos[ direction === 'down' || direction === 'up' ? 'top' : 'left' ] = ( direction === 'up' || direction === 'left' ? '-' : '+' ) + '=' + distance + 'px'; - } - - previous.wrap.animate(endPos, { - duration : effect === 'none' ? 0 : previous.prevSpeed, - easing : previous.prevEasing, - complete : function () { - $(this).trigger('onReset').remove(); - } - }); - } - }; - - /* - * Overlay helper - */ - - F.helpers.overlay = { - defaults : { - closeClick : true, // if true, fancyBox will be closed when user clicks on the overlay - speedOut : 200, // duration of fadeOut animation - showEarly : true, // indicates if should be opened immediately or wait until the content is ready - css : {}, // custom CSS properties - locked : !isTouch, // if true, the content will be locked into overlay - fixed : true // if false, the overlay CSS position property will not be set to "fixed" - }, - - overlay : null, // current handle - fixed : false, // indicates if the overlay has position "fixed" - el : $('html'), // element that contains "the lock" - - // Public methods - create : function(opts) { - opts = $.extend({}, this.defaults, opts); - - if (this.overlay) { - this.close(); - } - - this.overlay = $('
').appendTo( F.coming ? F.coming.parent : opts.parent ); - this.fixed = false; - - if (opts.fixed && F.defaults.fixed) { - this.overlay.addClass('fancybox-overlay-fixed'); - - this.fixed = true; - } - }, - - open : function(opts) { - var that = this; - - opts = $.extend({}, this.defaults, opts); - - if (this.overlay) { - this.overlay.unbind('.overlay').width('auto').height('auto'); - - } else { - this.create(opts); - } - - if (!this.fixed) { - W.bind('resize.overlay', $.proxy( this.update, this) ); - - this.update(); - } - - if (opts.closeClick) { - this.overlay.bind('click.overlay', function(e) { - if ($(e.target).hasClass('fancybox-overlay')) { - if (F.isActive) { - F.close(); - } else { - that.close(); - } - - return false; - } - }); - } - - this.overlay.css( opts.css ).show(); - }, - - close : function() { - var scrollV, scrollH; - - W.unbind('resize.overlay'); - - if (this.el.hasClass('fancybox-lock')) { - $('.fancybox-margin').removeClass('fancybox-margin'); - - scrollV = W.scrollTop(); - scrollH = W.scrollLeft(); - - this.el.removeClass('fancybox-lock'); - - W.scrollTop( scrollV ).scrollLeft( scrollH ); - } - - $('.fancybox-overlay').remove().hide(); - - $.extend(this, { - overlay : null, - fixed : false - }); - }, - - // Private, callbacks - - update : function () { - var width = '100%', offsetWidth; - - // Reset width/height so it will not mess - this.overlay.width(width).height('100%'); - - // jQuery does not return reliable result for IE - if (IE) { - offsetWidth = Math.max(document.documentElement.offsetWidth, document.body.offsetWidth); - - if (D.width() > offsetWidth) { - width = D.width(); - } - - } else if (D.width() > W.width()) { - width = D.width(); - } - - this.overlay.width(width).height(D.height()); - }, - - // This is where we can manipulate DOM, because later it would cause iframes to reload - onReady : function (opts, obj) { - var overlay = this.overlay; - - $('.fancybox-overlay').stop(true, true); - - if (!overlay) { - this.create(opts); - } - - if (opts.locked && this.fixed && obj.fixed) { - if (!overlay) { - this.margin = D.height() > W.height() ? $('html').css('margin-right').replace("px", "") : false; - } - - obj.locked = this.overlay.append( obj.wrap ); - obj.fixed = false; - } - - if (opts.showEarly === true) { - this.beforeShow.apply(this, arguments); - } - }, - - beforeShow : function(opts, obj) { - var scrollV, scrollH; - - if (obj.locked) { - if (this.margin !== false) { - $('*').filter(function(){ - return ($(this).css('position') === 'fixed' && !$(this).hasClass("fancybox-overlay") && !$(this).hasClass("fancybox-wrap") ); - }).addClass('fancybox-margin'); - - this.el.addClass('fancybox-margin'); - } - - scrollV = W.scrollTop(); - scrollH = W.scrollLeft(); - - this.el.addClass('fancybox-lock'); - - W.scrollTop( scrollV ).scrollLeft( scrollH ); - } - - this.open(opts); - }, - - onUpdate : function() { - if (!this.fixed) { - this.update(); - } - }, - - afterClose: function (opts) { - // Remove overlay if exists and fancyBox is not opening - // (e.g., it is not being open using afterClose callback) - //if (this.overlay && !F.isActive) { - if (this.overlay && !F.coming) { - this.overlay.fadeOut(opts.speedOut, $.proxy( this.close, this )); - } - } - }; - - /* - * Title helper - */ - - F.helpers.title = { - defaults : { - type : 'float', // 'float', 'inside', 'outside' or 'over', - position : 'bottom' // 'top' or 'bottom' - }, - - beforeShow: function (opts) { - var current = F.current, - text = current.title, - type = opts.type, - title, - target; - - if ($.isFunction(text)) { - text = text.call(current.element, current); - } - - if (!isString(text) || $.trim(text) === '') { - return; - } - - title = $('
' + text + '
'); - - switch (type) { - case 'inside': - target = F.skin; - break; - - case 'outside': - target = F.wrap; - break; - - case 'over': - target = F.inner; - break; - - default: // 'float' - target = F.skin; - - title.appendTo('body'); - - if (IE) { - title.width( title.width() ); - } - - title.wrapInner(''); - - //Increase bottom margin so this title will also fit into viewport - F.current.margin[2] += Math.abs( getScalar(title.css('margin-bottom')) ); - break; - } - - title[ (opts.position === 'top' ? 'prependTo' : 'appendTo') ](target); - } - }; - - // jQuery plugin initialization - $.fn.fancybox = function (options) { - var index, - that = $(this), - selector = this.selector || '', - run = function(e) { - var what = $(this).blur(), idx = index, relType, relVal; - - if (!(e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) && !what.is('.fancybox-wrap')) { - relType = options.groupAttr || 'data-fancybox-group'; - relVal = what.attr(relType); - - if (!relVal) { - relType = 'rel'; - relVal = what.get(0)[ relType ]; - } - - if (relVal && relVal !== '' && relVal !== 'nofollow') { - what = selector.length ? $(selector) : that; - what = what.filter('[' + relType + '="' + relVal + '"]'); - idx = what.index(this); - } - - options.index = idx; - - // Stop an event from bubbling if everything is fine - if (F.open(what, options) !== false) { - e.preventDefault(); - } - } - }; - - options = options || {}; - index = options.index || 0; - - if (!selector || options.live === false) { - that.unbind('click.fb-start').bind('click.fb-start', run); - - } else { - D.undelegate(selector, 'click.fb-start').delegate(selector + ":not('.fancybox-item, .fancybox-nav')", 'click.fb-start', run); - } - - this.filter('[data-fancybox-start=1]').trigger('click'); - - return this; - }; - - // Tests that need a body at doc ready - D.ready(function() { - var w1, w2; - - if ( $.scrollbarWidth === undefined ) { - // http://benalman.com/projects/jquery-misc-plugins/#scrollbarwidth - $.scrollbarWidth = function() { - var parent = $('
').appendTo('body'), - child = parent.children(), - width = child.innerWidth() - child.height( 99 ).innerWidth(); - - parent.remove(); - - return width; - }; - } - - if ( $.support.fixedPosition === undefined ) { - $.support.fixedPosition = (function() { - var elem = $('
').appendTo('body'), - fixed = ( elem[0].offsetTop === 20 || elem[0].offsetTop === 15 ); - - elem.remove(); - - return fixed; - }()); - } - - $.extend(F.defaults, { - scrollbarWidth : $.scrollbarWidth(), - fixed : $.support.fixedPosition, - parent : $('body') - }); - - //Get real width of page scroll-bar - w1 = $(window).width(); - - H.addClass('fancybox-lock-test'); - - w2 = $(window).width(); - - H.removeClass('fancybox-lock-test'); - - $("").appendTo("head"); - }); - -}(window, document, jQuery)); \ No newline at end of file diff --git a/superx/saiku/js/fancybox/jquery.fancybox.pack.js b/superx/saiku/js/fancybox/jquery.fancybox.pack.js deleted file mode 100644 index 73f7578..0000000 --- a/superx/saiku/js/fancybox/jquery.fancybox.pack.js +++ /dev/null @@ -1,46 +0,0 @@ -/*! fancyBox v2.1.5 fancyapps.com | fancyapps.com/fancybox/#license */ -(function(r,G,f,v){var J=f("html"),n=f(r),p=f(G),b=f.fancybox=function(){b.open.apply(this,arguments)},I=navigator.userAgent.match(/msie/i),B=null,s=G.createTouch!==v,t=function(a){return a&&a.hasOwnProperty&&a instanceof f},q=function(a){return a&&"string"===f.type(a)},E=function(a){return q(a)&&0
',image:'',iframe:'",error:'

The requested content cannot be loaded.
Please try again later.

',closeBtn:'',next:'',prev:''},openEffect:"fade",openSpeed:250,openEasing:"swing",openOpacity:!0, -openMethod:"zoomIn",closeEffect:"fade",closeSpeed:250,closeEasing:"swing",closeOpacity:!0,closeMethod:"zoomOut",nextEffect:"elastic",nextSpeed:250,nextEasing:"swing",nextMethod:"changeIn",prevEffect:"elastic",prevSpeed:250,prevEasing:"swing",prevMethod:"changeOut",helpers:{overlay:!0,title:!0},onCancel:f.noop,beforeLoad:f.noop,afterLoad:f.noop,beforeShow:f.noop,afterShow:f.noop,beforeChange:f.noop,beforeClose:f.noop,afterClose:f.noop},group:{},opts:{},previous:null,coming:null,current:null,isActive:!1, -isOpen:!1,isOpened:!1,wrap:null,skin:null,outer:null,inner:null,player:{timer:null,isActive:!1},ajaxLoad:null,imgPreload:null,transitions:{},helpers:{},open:function(a,d){if(a&&(f.isPlainObject(d)||(d={}),!1!==b.close(!0)))return f.isArray(a)||(a=t(a)?f(a).get():[a]),f.each(a,function(e,c){var k={},g,h,j,m,l;"object"===f.type(c)&&(c.nodeType&&(c=f(c)),t(c)?(k={href:c.data("fancybox-href")||c.attr("href"),title:c.data("fancybox-title")||c.attr("title"),isDom:!0,element:c},f.metadata&&f.extend(!0,k, -c.metadata())):k=c);g=d.href||k.href||(q(c)?c:null);h=d.title!==v?d.title:k.title||"";m=(j=d.content||k.content)?"html":d.type||k.type;!m&&k.isDom&&(m=c.data("fancybox-type"),m||(m=(m=c.prop("class").match(/fancybox\.(\w+)/))?m[1]:null));q(g)&&(m||(b.isImage(g)?m="image":b.isSWF(g)?m="swf":"#"===g.charAt(0)?m="inline":q(c)&&(m="html",j=c)),"ajax"===m&&(l=g.split(/\s+/,2),g=l.shift(),l=l.shift()));j||("inline"===m?g?j=f(q(g)?g.replace(/.*(?=#[^\s]+$)/,""):g):k.isDom&&(j=c):"html"===m?j=g:!m&&(!g&& -k.isDom)&&(m="inline",j=c));f.extend(k,{href:g,type:m,content:j,title:h,selector:l});a[e]=k}),b.opts=f.extend(!0,{},b.defaults,d),d.keys!==v&&(b.opts.keys=d.keys?f.extend({},b.defaults.keys,d.keys):!1),b.group=a,b._start(b.opts.index)},cancel:function(){var a=b.coming;a&&!1!==b.trigger("onCancel")&&(b.hideLoading(),b.ajaxLoad&&b.ajaxLoad.abort(),b.ajaxLoad=null,b.imgPreload&&(b.imgPreload.onload=b.imgPreload.onerror=null),a.wrap&&a.wrap.stop(!0,!0).trigger("onReset").remove(),b.coming=null,b.current|| -b._afterZoomOut(a))},close:function(a){b.cancel();!1!==b.trigger("beforeClose")&&(b.unbindEvents(),b.isActive&&(!b.isOpen||!0===a?(f(".fancybox-wrap").stop(!0).trigger("onReset").remove(),b._afterZoomOut()):(b.isOpen=b.isOpened=!1,b.isClosing=!0,f(".fancybox-item, .fancybox-nav").remove(),b.wrap.stop(!0,!0).removeClass("fancybox-opened"),b.transitions[b.current.closeMethod]())))},play:function(a){var d=function(){clearTimeout(b.player.timer)},e=function(){d();b.current&&b.player.isActive&&(b.player.timer= -setTimeout(b.next,b.current.playSpeed))},c=function(){d();p.unbind(".player");b.player.isActive=!1;b.trigger("onPlayEnd")};if(!0===a||!b.player.isActive&&!1!==a){if(b.current&&(b.current.loop||b.current.index=c.index?"next":"prev"],b.router=e||"jumpto",c.loop&&(0>a&&(a=c.group.length+a%c.group.length),a%=c.group.length),c.group[a]!==v&&(b.cancel(),b._start(a)))},reposition:function(a,d){var e=b.current,c=e?e.wrap:null,k;c&&(k=b._getPosition(d),a&&"scroll"===a.type?(delete k.position,c.stop(!0,!0).animate(k,200)):(c.css(k),e.pos=f.extend({},e.dim,k)))},update:function(a){var d= -a&&a.type,e=!d||"orientationchange"===d;e&&(clearTimeout(B),B=null);b.isOpen&&!B&&(B=setTimeout(function(){var c=b.current;c&&!b.isClosing&&(b.wrap.removeClass("fancybox-tmp"),(e||"load"===d||"resize"===d&&c.autoResize)&&b._setDimension(),"scroll"===d&&c.canShrink||b.reposition(a),b.trigger("onUpdate"),B=null)},e&&!s?0:300))},toggle:function(a){b.isOpen&&(b.current.fitToView="boolean"===f.type(a)?a:!b.current.fitToView,s&&(b.wrap.removeAttr("style").addClass("fancybox-tmp"),b.trigger("onUpdate")), -b.update())},hideLoading:function(){p.unbind(".loading");f("#fancybox-loading").remove()},showLoading:function(){var a,d;b.hideLoading();a=f('
').click(b.cancel).appendTo("body");p.bind("keydown.loading",function(a){if(27===(a.which||a.keyCode))a.preventDefault(),b.cancel()});b.defaults.fixed||(d=b.getViewport(),a.css({position:"absolute",top:0.5*d.h+d.y,left:0.5*d.w+d.x}))},getViewport:function(){var a=b.current&&b.current.locked||!1,d={x:n.scrollLeft(), -y:n.scrollTop()};a?(d.w=a[0].clientWidth,d.h=a[0].clientHeight):(d.w=s&&r.innerWidth?r.innerWidth:n.width(),d.h=s&&r.innerHeight?r.innerHeight:n.height());return d},unbindEvents:function(){b.wrap&&t(b.wrap)&&b.wrap.unbind(".fb");p.unbind(".fb");n.unbind(".fb")},bindEvents:function(){var a=b.current,d;a&&(n.bind("orientationchange.fb"+(s?"":" resize.fb")+(a.autoCenter&&!a.locked?" scroll.fb":""),b.update),(d=a.keys)&&p.bind("keydown.fb",function(e){var c=e.which||e.keyCode,k=e.target||e.srcElement; -if(27===c&&b.coming)return!1;!e.ctrlKey&&(!e.altKey&&!e.shiftKey&&!e.metaKey&&(!k||!k.type&&!f(k).is("[contenteditable]")))&&f.each(d,function(d,k){if(1h[0].clientWidth||h[0].clientHeight&&h[0].scrollHeight>h[0].clientHeight),h=f(h).parent();if(0!==c&&!j&&1g||0>k)b.next(0>g?"up":"right");d.preventDefault()}}))},trigger:function(a,d){var e,c=d||b.coming||b.current;if(c){f.isFunction(c[a])&&(e=c[a].apply(c,Array.prototype.slice.call(arguments,1)));if(!1===e)return!1;c.helpers&&f.each(c.helpers,function(d,e){if(e&&b.helpers[d]&&f.isFunction(b.helpers[d][a]))b.helpers[d][a](f.extend(!0, -{},b.helpers[d].defaults,e),c)});p.trigger(a)}},isImage:function(a){return q(a)&&a.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i)},isSWF:function(a){return q(a)&&a.match(/\.(swf)((\?|#).*)?$/i)},_start:function(a){var d={},e,c;a=l(a);e=b.group[a]||null;if(!e)return!1;d=f.extend(!0,{},b.opts,e);e=d.margin;c=d.padding;"number"===f.type(e)&&(d.margin=[e,e,e,e]);"number"===f.type(c)&&(d.padding=[c,c,c,c]);d.modal&&f.extend(!0,d,{closeBtn:!1,closeClick:!1,nextClick:!1,arrows:!1, -mouseWheel:!1,keys:null,helpers:{overlay:{closeClick:!1}}});d.autoSize&&(d.autoWidth=d.autoHeight=!0);"auto"===d.width&&(d.autoWidth=!0);"auto"===d.height&&(d.autoHeight=!0);d.group=b.group;d.index=a;b.coming=d;if(!1===b.trigger("beforeLoad"))b.coming=null;else{c=d.type;e=d.href;if(!c)return b.coming=null,b.current&&b.router&&"jumpto"!==b.router?(b.current.index=a,b[b.router](b.direction)):!1;b.isActive=!0;if("image"===c||"swf"===c)d.autoHeight=d.autoWidth=!1,d.scrolling="visible";"image"===c&&(d.aspectRatio= -!0);"iframe"===c&&s&&(d.scrolling="scroll");d.wrap=f(d.tpl.wrap).addClass("fancybox-"+(s?"mobile":"desktop")+" fancybox-type-"+c+" fancybox-tmp "+d.wrapCSS).appendTo(d.parent||"body");f.extend(d,{skin:f(".fancybox-skin",d.wrap),outer:f(".fancybox-outer",d.wrap),inner:f(".fancybox-inner",d.wrap)});f.each(["Top","Right","Bottom","Left"],function(a,b){d.skin.css("padding"+b,w(d.padding[a]))});b.trigger("onReady");if("inline"===c||"html"===c){if(!d.content||!d.content.length)return b._error("content")}else if(!e)return b._error("href"); -"image"===c?b._loadImage():"ajax"===c?b._loadAjax():"iframe"===c?b._loadIframe():b._afterLoad()}},_error:function(a){f.extend(b.coming,{type:"html",autoWidth:!0,autoHeight:!0,minWidth:0,minHeight:0,scrolling:"no",hasError:a,content:b.coming.tpl.error});b._afterLoad()},_loadImage:function(){var a=b.imgPreload=new Image;a.onload=function(){this.onload=this.onerror=null;b.coming.width=this.width/b.opts.pixelRatio;b.coming.height=this.height/b.opts.pixelRatio;b._afterLoad()};a.onerror=function(){this.onload= -this.onerror=null;b._error("image")};a.src=b.coming.href;!0!==a.complete&&b.showLoading()},_loadAjax:function(){var a=b.coming;b.showLoading();b.ajaxLoad=f.ajax(f.extend({},a.ajax,{url:a.href,error:function(a,e){b.coming&&"abort"!==e?b._error("ajax",a):b.hideLoading()},success:function(d,e){"success"===e&&(a.content=d,b._afterLoad())}}))},_loadIframe:function(){var a=b.coming,d=f(a.tpl.iframe.replace(/\{rnd\}/g,(new Date).getTime())).attr("scrolling",s?"auto":a.iframe.scrolling).attr("src",a.href); -f(a.wrap).bind("onReset",function(){try{f(this).find("iframe").hide().attr("src","//about:blank").end().empty()}catch(a){}});a.iframe.preload&&(b.showLoading(),d.one("load",function(){f(this).data("ready",1);s||f(this).bind("load.fb",b.update);f(this).parents(".fancybox-wrap").width("100%").removeClass("fancybox-tmp").show();b._afterLoad()}));a.content=d.appendTo(a.inner);a.iframe.preload||b._afterLoad()},_preloadImages:function(){var a=b.group,d=b.current,e=a.length,c=d.preload?Math.min(d.preload, -e-1):0,f,g;for(g=1;g<=c;g+=1)f=a[(d.index+g)%e],"image"===f.type&&f.href&&((new Image).src=f.href)},_afterLoad:function(){var a=b.coming,d=b.current,e,c,k,g,h;b.hideLoading();if(a&&!1!==b.isActive)if(!1===b.trigger("afterLoad",a,d))a.wrap.stop(!0).trigger("onReset").remove(),b.coming=null;else{d&&(b.trigger("beforeChange",d),d.wrap.stop(!0).removeClass("fancybox-opened").find(".fancybox-item, .fancybox-nav").remove());b.unbindEvents();e=a.content;c=a.type;k=a.scrolling;f.extend(b,{wrap:a.wrap,skin:a.skin, -outer:a.outer,inner:a.inner,current:a,previous:d});g=a.href;switch(c){case "inline":case "ajax":case "html":a.selector?e=f("
").html(e).find(a.selector):t(e)&&(e.data("fancybox-placeholder")||e.data("fancybox-placeholder",f('
').insertAfter(e).hide()),e=e.show().detach(),a.wrap.bind("onReset",function(){f(this).find(e).length&&e.hide().replaceAll(e.data("fancybox-placeholder")).data("fancybox-placeholder",!1)}));break;case "image":e=a.tpl.image.replace("{href}", -g);break;case "swf":e='',h="",f.each(a.swf,function(a,b){e+='';h+=" "+a+'="'+b+'"'}),e+='"}(!t(e)||!e.parent().is(a.inner))&&a.inner.append(e);b.trigger("beforeShow");a.inner.css("overflow","yes"===k?"scroll": -"no"===k?"hidden":k);b._setDimension();b.reposition();b.isOpen=!1;b.coming=null;b.bindEvents();if(b.isOpened){if(d.prevMethod)b.transitions[d.prevMethod]()}else f(".fancybox-wrap").not(a.wrap).stop(!0).trigger("onReset").remove();b.transitions[b.isOpened?a.nextMethod:a.openMethod]();b._preloadImages()}},_setDimension:function(){var a=b.getViewport(),d=0,e=!1,c=!1,e=b.wrap,k=b.skin,g=b.inner,h=b.current,c=h.width,j=h.height,m=h.minWidth,u=h.minHeight,n=h.maxWidth,p=h.maxHeight,s=h.scrolling,q=h.scrollOutside? -h.scrollbarWidth:0,x=h.margin,y=l(x[1]+x[3]),r=l(x[0]+x[2]),v,z,t,C,A,F,B,D,H;e.add(k).add(g).width("auto").height("auto").removeClass("fancybox-tmp");x=l(k.outerWidth(!0)-k.width());v=l(k.outerHeight(!0)-k.height());z=y+x;t=r+v;C=E(c)?(a.w-z)*l(c)/100:c;A=E(j)?(a.h-t)*l(j)/100:j;if("iframe"===h.type){if(H=h.content,h.autoHeight&&1===H.data("ready"))try{H[0].contentWindow.document.location&&(g.width(C).height(9999),F=H.contents().find("body"),q&&F.css("overflow-x","hidden"),A=F.outerHeight(!0))}catch(G){}}else if(h.autoWidth|| -h.autoHeight)g.addClass("fancybox-tmp"),h.autoWidth||g.width(C),h.autoHeight||g.height(A),h.autoWidth&&(C=g.width()),h.autoHeight&&(A=g.height()),g.removeClass("fancybox-tmp");c=l(C);j=l(A);D=C/A;m=l(E(m)?l(m,"w")-z:m);n=l(E(n)?l(n,"w")-z:n);u=l(E(u)?l(u,"h")-t:u);p=l(E(p)?l(p,"h")-t:p);F=n;B=p;h.fitToView&&(n=Math.min(a.w-z,n),p=Math.min(a.h-t,p));z=a.w-y;r=a.h-r;h.aspectRatio?(c>n&&(c=n,j=l(c/D)),j>p&&(j=p,c=l(j*D)),cz||y>r)&&(c>m&&j>u)&&!(19n&&(c=n,j=l(c/D)),g.width(c).height(j),e.width(c+x),a=e.width(),y=e.height();else c=Math.max(m,Math.min(c,c-(a-z))),j=Math.max(u,Math.min(j,j-(y-r)));q&&("auto"===s&&jz||y>r)&&c>m&&j>u;c=h.aspectRatio?cu&&j
').appendTo(b.coming?b.coming.parent:a.parent);this.fixed=!1;a.fixed&&b.defaults.fixed&&(this.overlay.addClass("fancybox-overlay-fixed"),this.fixed=!0)},open:function(a){var d=this;a=f.extend({},this.defaults,a);this.overlay?this.overlay.unbind(".overlay").width("auto").height("auto"):this.create(a);this.fixed||(n.bind("resize.overlay",f.proxy(this.update,this)),this.update());a.closeClick&&this.overlay.bind("click.overlay",function(a){if(f(a.target).hasClass("fancybox-overlay"))return b.isActive? -b.close():d.close(),!1});this.overlay.css(a.css).show()},close:function(){var a,b;n.unbind("resize.overlay");this.el.hasClass("fancybox-lock")&&(f(".fancybox-margin").removeClass("fancybox-margin"),a=n.scrollTop(),b=n.scrollLeft(),this.el.removeClass("fancybox-lock"),n.scrollTop(a).scrollLeft(b));f(".fancybox-overlay").remove().hide();f.extend(this,{overlay:null,fixed:!1})},update:function(){var a="100%",b;this.overlay.width(a).height("100%");I?(b=Math.max(G.documentElement.offsetWidth,G.body.offsetWidth), -p.width()>b&&(a=p.width())):p.width()>n.width()&&(a=p.width());this.overlay.width(a).height(p.height())},onReady:function(a,b){var e=this.overlay;f(".fancybox-overlay").stop(!0,!0);e||this.create(a);a.locked&&(this.fixed&&b.fixed)&&(e||(this.margin=p.height()>n.height()?f("html").css("margin-right").replace("px",""):!1),b.locked=this.overlay.append(b.wrap),b.fixed=!1);!0===a.showEarly&&this.beforeShow.apply(this,arguments)},beforeShow:function(a,b){var e,c;b.locked&&(!1!==this.margin&&(f("*").filter(function(){return"fixed"=== -f(this).css("position")&&!f(this).hasClass("fancybox-overlay")&&!f(this).hasClass("fancybox-wrap")}).addClass("fancybox-margin"),this.el.addClass("fancybox-margin")),e=n.scrollTop(),c=n.scrollLeft(),this.el.addClass("fancybox-lock"),n.scrollTop(e).scrollLeft(c));this.open(a)},onUpdate:function(){this.fixed||this.update()},afterClose:function(a){this.overlay&&!b.coming&&this.overlay.fadeOut(a.speedOut,f.proxy(this.close,this))}};b.helpers.title={defaults:{type:"float",position:"bottom"},beforeShow:function(a){var d= -b.current,e=d.title,c=a.type;f.isFunction(e)&&(e=e.call(d.element,d));if(q(e)&&""!==f.trim(e)){d=f('
'+e+"
");switch(c){case "inside":c=b.skin;break;case "outside":c=b.wrap;break;case "over":c=b.inner;break;default:c=b.skin,d.appendTo("body"),I&&d.width(d.width()),d.wrapInner(''),b.current.margin[2]+=Math.abs(l(d.css("margin-bottom")))}d["top"===a.position?"prependTo":"appendTo"](c)}}};f.fn.fancybox=function(a){var d, -e=f(this),c=this.selector||"",k=function(g){var h=f(this).blur(),j=d,k,l;!g.ctrlKey&&(!g.altKey&&!g.shiftKey&&!g.metaKey)&&!h.is(".fancybox-wrap")&&(k=a.groupAttr||"data-fancybox-group",l=h.attr(k),l||(k="rel",l=h.get(0)[k]),l&&(""!==l&&"nofollow"!==l)&&(h=c.length?f(c):e,h=h.filter("["+k+'="'+l+'"]'),j=h.index(this)),a.index=j,!1!==b.open(h,a)&&g.preventDefault())};a=a||{};d=a.index||0;!c||!1===a.live?e.unbind("click.fb-start").bind("click.fb-start",k):p.undelegate(c,"click.fb-start").delegate(c+ -":not('.fancybox-item, .fancybox-nav')","click.fb-start",k);this.filter("[data-fancybox-start=1]").trigger("click");return this};p.ready(function(){var a,d;f.scrollbarWidth===v&&(f.scrollbarWidth=function(){var a=f('
').appendTo("body"),b=a.children(),b=b.innerWidth()-b.height(99).innerWidth();a.remove();return b});if(f.support.fixedPosition===v){a=f.support;d=f('
').appendTo("body");var e=20=== -d[0].offsetTop||15===d[0].offsetTop;d.remove();a.fixedPosition=e}f.extend(b.defaults,{scrollbarWidth:f.scrollbarWidth(),fixed:f.support.fixedPosition,parent:f("body")});a=f(r).width();J.addClass("fancybox-lock-test");d=f(r).width();J.removeClass("fancybox-lock-test");f("").appendTo("head")})})(window,document,jQuery); \ No newline at end of file diff --git a/superx/saiku/js/fitvids/jquery.fitvids.js b/superx/saiku/js/fitvids/jquery.fitvids.js deleted file mode 100644 index 3742b4e..0000000 --- a/superx/saiku/js/fitvids/jquery.fitvids.js +++ /dev/null @@ -1,82 +0,0 @@ -/*jshint browser:true */ -/*! -* FitVids 1.1 -* -* Copyright 2013, Chris Coyier - http://css-tricks.com + Dave Rupert - http://daverupert.com -* Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/ -* Released under the WTFPL license - http://sam.zoy.org/wtfpl/ -* -*/ - -;(function( $ ){ - - 'use strict'; - - $.fn.fitVids = function( options ) { - var settings = { - customSelector: null, - ignore: null - }; - - if(!document.getElementById('fit-vids-style')) { - // appendStyles: https://github.com/toddmotto/fluidvids/blob/master/dist/fluidvids.js - var head = document.head || document.getElementsByTagName('head')[0]; - var css = '.fluid-width-video-wrapper{width:100%;position:relative;padding:0;}.fluid-width-video-wrapper iframe,.fluid-width-video-wrapper object,.fluid-width-video-wrapper embed {position:absolute;top:0;left:0;width:100%;height:100%;}'; - var div = document.createElement("div"); - div.innerHTML = '

x

'; - head.appendChild(div.childNodes[1]); - } - - if ( options ) { - $.extend( settings, options ); - } - - return this.each(function(){ - var selectors = [ - 'iframe[src*="player.vimeo.com"]', - 'iframe[src*="youtube.com"]', - 'iframe[src*="youtube-nocookie.com"]', - 'iframe[src*="kickstarter.com"][src*="video.html"]', - 'object', - 'embed' - ]; - - if (settings.customSelector) { - selectors.push(settings.customSelector); - } - - var ignoreList = '.fitvidsignore'; - - if(settings.ignore) { - ignoreList = ignoreList + ', ' + settings.ignore; - } - - var $allVideos = $(this).find(selectors.join(',')); - $allVideos = $allVideos.not('object object'); // SwfObj conflict patch - $allVideos = $allVideos.not(ignoreList); // Disable FitVids on this video. - - $allVideos.each(function(count){ - var $this = $(this); - if($this.parents(ignoreList).length > 0) { - return; // Disable FitVids on this video. - } - if (this.tagName.toLowerCase() === 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; } - if ((!$this.css('height') && !$this.css('width')) && (isNaN($this.attr('height')) || isNaN($this.attr('width')))) - { - $this.attr('height', 9); - $this.attr('width', 16); - } - var height = ( this.tagName.toLowerCase() === 'object' || ($this.attr('height') && !isNaN(parseInt($this.attr('height'), 10))) ) ? parseInt($this.attr('height'), 10) : $this.height(), - width = !isNaN(parseInt($this.attr('width'), 10)) ? parseInt($this.attr('width'), 10) : $this.width(), - aspectRatio = height / width; - if(!$this.attr('id')){ - var videoID = 'fitvid' + count; - $this.attr('id', videoID); - } - $this.wrap('
').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+'%'); - $this.removeAttr('height').removeAttr('width'); - }); - }); - }; -// Works with either jQuery or Zepto -})( window.jQuery || window.Zepto ); diff --git a/superx/saiku/js/ga.js b/superx/saiku/js/ga.js deleted file mode 100644 index fe494d0..0000000 --- a/superx/saiku/js/ga.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012 OSBI Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -var account = 'UA-16172251-20'; - -if (window.location.hostname && window.location.hostname == "try.meteorite.bi" ) { - account = 'UA-16172251-12'; -} else if (window.location.hostname && window.location.hostname == "demo.analytical-labs.com" ) { - account = 'UA-16172251-5'; -} - - - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - ga('create', account, 'analytical-labs.com'); - ga('send', 'pageview'); diff --git a/superx/saiku/js/jquery/jquery-mobile-touch.js b/superx/saiku/js/jquery/jquery-mobile-touch.js deleted file mode 100644 index 344e2b5..0000000 --- a/superx/saiku/js/jquery/jquery-mobile-touch.js +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Content-Type:text/javascript - * - * A bridge between iPad and iPhone touch events and jquery draggable, - * sortable etc. mouse interactions. - * @author Oleg Slobodskoi - * - * modified by John Hardy to use with any touch device - * fixed breakage caused by jquery.ui so that mouseHandled internal flag is reset - * before each touchStart event - * - * modified by Nik Graf to trigger click event inside widgets in case they haven't be moved - * - */ -(function( $ ) { -    $.support.touch = typeof Touch === 'object'; -    if (!$.support.touch) { -        return; -    } - -    var proto =  $.ui.mouse.prototype, -    _mouseInit = proto._mouseInit; - - var startX; - var dx = 0; - - -    $.extend( proto, { -        _mouseInit: function() { - if ($(this.element).hasClass("ui-dialog") || (!$(this.element).hasClass('ui-draggable') && !$(this.element).hasClass('ui-sortable'))) { - return false; - } - -            this.element -            .bind( "touchstart." + this.widgetName, $.proxy( this, "_touchStart" ) ); -            _mouseInit.apply( this, arguments ); -        }, - -        _touchStart: function( event ) { -            if ( event.originalEvent.targetTouches.length != 1 ) { -                return false; -            } - - - startX = event.originalEvent.pageX; -            // reset touchMoved to provide a clean state after touch start -            this._touchMoved = false; - dx = 0; - -            this.element -            .bind( "touchmove." + this.widgetName, $.proxy( this, "_touchMove" ) ) -            .bind( "touchend." + this.widgetName, $.proxy( this, "_touchEnd" ) ); - -            this._modifyEvent( event ); - -            //$( document ).trigger($.Event("mouseup")); //reset mouseHandled flag in ui.mouse -            this._mouseDown( event ); - if (this._mouseOn) { - this._mouseOn( event ); - } -            return false; -        }, - -        _touchMove: function( event ) { - if ($(event.target).hasClass('ui-draggable') || $(event.target).parent().hasClass('ui-draggable') || !$(event.target).parents("ui-dialog").length > 0) { -            this._touchMoved = true; -            this._modifyEvent( event ); -            this._mouseMove( event ); - } else { - $(event.target).click(); - $( document ).trigger($.Event("mouseup")); //reset mouseHandled flag in ui.mouse - } - -        }, - -        _touchEnd: function( event ) { -            this.element -            .unbind( "touchmove." + this.widgetName ) -            .unbind( "touchend." + this.widgetName ); - //console.log("Distance: " + dx); - - -            if (Math.abs(dx) < 10 || !this._touchMoved) { - //console.log("CLICK!"); -                this._touchMoved = false; -                // since $(event.target).click() does not work properly on ios we need to call the click event in this way -                // see http://stackoverflow.com/questions/3543733/jquery-trigger-click-mobile-safari-ipad - //               var clickEvent = document.createEvent("MouseEvents"); -// clickEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); - $(event.target).click(); - //console.log(clickEvent); - //               $(event.currentTarget)[0].dispatchEvent(clickEvent); -            } else { - event.preventDefault() ; - } - startX = null; - dx = 0; -            this._mouseUp( event ); - $( document ).trigger($.Event("mouseup")); //reset mouseHandled flag in ui.mouse - -        }, - -        _modifyEvent: function( event ) { -            event.which = 1; -            var target = event.originalEvent.targetTouches[0]; -            event.pageX = target.clientX; -            event.pageY = target.clientY; - dx = target.clientX - startX; -        } - -    }); - -})( jQuery ); \ No newline at end of file diff --git a/superx/saiku/js/jquery/jquery-ui-1.10.3.custom.js b/superx/saiku/js/jquery/jquery-ui-1.10.3.custom.js deleted file mode 100644 index dedd401..0000000 --- a/superx/saiku/js/jquery/jquery-ui-1.10.3.custom.js +++ /dev/null @@ -1,9083 +0,0 @@ -/*! jQuery UI - v1.10.3 - 2013-09-25 -* http://jqueryui.com -* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.position.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.dialog.js, jquery.ui.menu.js, jquery.ui.effect.js, jquery.ui.effect-drop.js, jquery.ui.effect-fade.js, jquery.ui.effect-highlight.js, jquery.ui.effect-slide.js -* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ - -(function( $, undefined ) { - -var uuid = 0, - runiqueId = /^ui-id-\d+$/; - -// $.ui might exist from components with no dependencies, e.g., $.ui.position -$.ui = $.ui || {}; - -$.extend( $.ui, { - version: "1.10.3", - - keyCode: { - BACKSPACE: 8, - COMMA: 188, - DELETE: 46, - DOWN: 40, - END: 35, - ENTER: 13, - ESCAPE: 27, - HOME: 36, - LEFT: 37, - NUMPAD_ADD: 107, - NUMPAD_DECIMAL: 110, - NUMPAD_DIVIDE: 111, - NUMPAD_ENTER: 108, - NUMPAD_MULTIPLY: 106, - NUMPAD_SUBTRACT: 109, - PAGE_DOWN: 34, - PAGE_UP: 33, - PERIOD: 190, - RIGHT: 39, - SPACE: 32, - TAB: 9, - UP: 38 - } -}); - -// plugins -$.fn.extend({ - focus: (function( orig ) { - return function( delay, fn ) { - return typeof delay === "number" ? - this.each(function() { - var elem = this; - setTimeout(function() { - $( elem ).focus(); - if ( fn ) { - fn.call( elem ); - } - }, delay ); - }) : - orig.apply( this, arguments ); - }; - })( $.fn.focus ), - - scrollParent: function() { - var scrollParent; - if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) { - scrollParent = this.parents().filter(function() { - return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); - }).eq(0); - } else { - scrollParent = this.parents().filter(function() { - return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); - }).eq(0); - } - - return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent; - }, - - zIndex: function( zIndex ) { - if ( zIndex !== undefined ) { - return this.css( "zIndex", zIndex ); - } - - if ( this.length ) { - var elem = $( this[ 0 ] ), position, value; - while ( elem.length && elem[ 0 ] !== document ) { - // Ignore z-index if position is set to a value where z-index is ignored by the browser - // This makes behavior of this function consistent across browsers - // WebKit always returns auto if the element is positioned - position = elem.css( "position" ); - if ( position === "absolute" || position === "relative" || position === "fixed" ) { - // IE returns 0 when zIndex is not specified - // other browsers return a string - // we ignore the case of nested elements with an explicit value of 0 - //
- value = parseInt( elem.css( "zIndex" ), 10 ); - if ( !isNaN( value ) && value !== 0 ) { - return value; - } - } - elem = elem.parent(); - } - } - - return 0; - }, - - uniqueId: function() { - return this.each(function() { - if ( !this.id ) { - this.id = "ui-id-" + (++uuid); - } - }); - }, - - removeUniqueId: function() { - return this.each(function() { - if ( runiqueId.test( this.id ) ) { - $( this ).removeAttr( "id" ); - } - }); - } -}); - -// selectors -function focusable( element, isTabIndexNotNaN ) { - var map, mapName, img, - nodeName = element.nodeName.toLowerCase(); - if ( "area" === nodeName ) { - map = element.parentNode; - mapName = map.name; - if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { - return false; - } - img = $( "img[usemap=#" + mapName + "]" )[0]; - return !!img && visible( img ); - } - return ( /input|select|textarea|button|object/.test( nodeName ) ? - !element.disabled : - "a" === nodeName ? - element.href || isTabIndexNotNaN : - isTabIndexNotNaN) && - // the element and all of its ancestors must be visible - visible( element ); -} - -function visible( element ) { - return $.expr.filters.visible( element ) && - !$( element ).parents().addBack().filter(function() { - return $.css( this, "visibility" ) === "hidden"; - }).length; -} - -$.extend( $.expr[ ":" ], { - data: $.expr.createPseudo ? - $.expr.createPseudo(function( dataName ) { - return function( elem ) { - return !!$.data( elem, dataName ); - }; - }) : - // support: jQuery <1.8 - function( elem, i, match ) { - return !!$.data( elem, match[ 3 ] ); - }, - - focusable: function( element ) { - return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); - }, - - tabbable: function( element ) { - var tabIndex = $.attr( element, "tabindex" ), - isTabIndexNaN = isNaN( tabIndex ); - return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); - } -}); - -// support: jQuery <1.8 -if ( !$( "" ).outerWidth( 1 ).jquery ) { - $.each( [ "Width", "Height" ], function( i, name ) { - var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], - type = name.toLowerCase(), - orig = { - innerWidth: $.fn.innerWidth, - innerHeight: $.fn.innerHeight, - outerWidth: $.fn.outerWidth, - outerHeight: $.fn.outerHeight - }; - - function reduce( elem, size, border, margin ) { - $.each( side, function() { - size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; - if ( border ) { - size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; - } - if ( margin ) { - size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; - } - }); - return size; - } - - $.fn[ "inner" + name ] = function( size ) { - if ( size === undefined ) { - return orig[ "inner" + name ].call( this ); - } - - return this.each(function() { - $( this ).css( type, reduce( this, size ) + "px" ); - }); - }; - - $.fn[ "outer" + name] = function( size, margin ) { - if ( typeof size !== "number" ) { - return orig[ "outer" + name ].call( this, size ); - } - - return this.each(function() { - $( this).css( type, reduce( this, size, true, margin ) + "px" ); - }); - }; - }); -} - -// support: jQuery <1.8 -if ( !$.fn.addBack ) { - $.fn.addBack = function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter( selector ) - ); - }; -} - -// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) -if ( $( "" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { - $.fn.removeData = (function( removeData ) { - return function( key ) { - if ( arguments.length ) { - return removeData.call( this, $.camelCase( key ) ); - } else { - return removeData.call( this ); - } - }; - })( $.fn.removeData ); -} - - - - - -// deprecated -$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); - -$.support.selectstart = "onselectstart" in document.createElement( "div" ); -$.fn.extend({ - disableSelection: function() { - return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + - ".ui-disableSelection", function( event ) { - event.preventDefault(); - }); - }, - - enableSelection: function() { - return this.unbind( ".ui-disableSelection" ); - } -}); - -$.extend( $.ui, { - // $.ui.plugin is deprecated. Use $.widget() extensions instead. - plugin: { - add: function( module, option, set ) { - var i, - proto = $.ui[ module ].prototype; - for ( i in set ) { - proto.plugins[ i ] = proto.plugins[ i ] || []; - proto.plugins[ i ].push( [ option, set[ i ] ] ); - } - }, - call: function( instance, name, args ) { - var i, - set = instance.plugins[ name ]; - if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) { - return; - } - - for ( i = 0; i < set.length; i++ ) { - if ( instance.options[ set[ i ][ 0 ] ] ) { - set[ i ][ 1 ].apply( instance.element, args ); - } - } - } - }, - - // only used by resizable - hasScroll: function( el, a ) { - - //If overflow is hidden, the element might have extra content, but the user wants to hide it - if ( $( el ).css( "overflow" ) === "hidden") { - return false; - } - - var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", - has = false; - - if ( el[ scroll ] > 0 ) { - return true; - } - - // TODO: determine which cases actually cause this to happen - // if the element doesn't have the scroll set, see if it's possible to - // set the scroll - el[ scroll ] = 1; - has = ( el[ scroll ] > 0 ); - el[ scroll ] = 0; - return has; - } -}); - -})( jQuery ); -(function( $, undefined ) { - -var uuid = 0, - slice = Array.prototype.slice, - _cleanData = $.cleanData; -$.cleanData = function( elems ) { - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { - try { - $( elem ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - } - _cleanData( elems ); -}; - -$.widget = function( name, base, prototype ) { - var fullName, existingConstructor, constructor, basePrototype, - // proxiedPrototype allows the provided prototype to remain unmodified - // so that it can be used as a mixin for multiple widgets (#8876) - proxiedPrototype = {}, - namespace = name.split( "." )[ 0 ]; - - name = name.split( "." )[ 1 ]; - fullName = namespace + "-" + name; - - if ( !prototype ) { - prototype = base; - base = $.Widget; - } - - // create selector for plugin - $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { - return !!$.data( elem, fullName ); - }; - - $[ namespace ] = $[ namespace ] || {}; - existingConstructor = $[ namespace ][ name ]; - constructor = $[ namespace ][ name ] = function( options, element ) { - // allow instantiation without "new" keyword - if ( !this._createWidget ) { - return new constructor( options, element ); - } - - // allow instantiation without initializing for simple inheritance - // must use "new" keyword (the code above always passes args) - if ( arguments.length ) { - this._createWidget( options, element ); - } - }; - // extend with the existing constructor to carry over any static properties - $.extend( constructor, existingConstructor, { - version: prototype.version, - // copy the object used to create the prototype in case we need to - // redefine the widget later - _proto: $.extend( {}, prototype ), - // track widgets that inherit from this widget in case this widget is - // redefined after a widget inherits from it - _childConstructors: [] - }); - - basePrototype = new base(); - // we need to make the options hash a property directly on the new instance - // otherwise we'll modify the options hash on the prototype that we're - // inheriting from - basePrototype.options = $.widget.extend( {}, basePrototype.options ); - $.each( prototype, function( prop, value ) { - if ( !$.isFunction( value ) ) { - proxiedPrototype[ prop ] = value; - return; - } - proxiedPrototype[ prop ] = (function() { - var _super = function() { - return base.prototype[ prop ].apply( this, arguments ); - }, - _superApply = function( args ) { - return base.prototype[ prop ].apply( this, args ); - }; - return function() { - var __super = this._super, - __superApply = this._superApply, - returnValue; - - this._super = _super; - this._superApply = _superApply; - - returnValue = value.apply( this, arguments ); - - this._super = __super; - this._superApply = __superApply; - - return returnValue; - }; - })(); - }); - constructor.prototype = $.widget.extend( basePrototype, { - // TODO: remove support for widgetEventPrefix - // always use the name + a colon as the prefix, e.g., draggable:start - // don't prefix for widgets that aren't DOM-based - widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name - }, proxiedPrototype, { - constructor: constructor, - namespace: namespace, - widgetName: name, - widgetFullName: fullName - }); - - // If this widget is being redefined then we need to find all widgets that - // are inheriting from it and redefine all of them so that they inherit from - // the new version of this widget. We're essentially trying to replace one - // level in the prototype chain. - if ( existingConstructor ) { - $.each( existingConstructor._childConstructors, function( i, child ) { - var childPrototype = child.prototype; - - // redefine the child widget using the same prototype that was - // originally used, but inherit from the new version of the base - $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); - }); - // remove the list of existing child constructors from the old constructor - // so the old child constructors can be garbage collected - delete existingConstructor._childConstructors; - } else { - base._childConstructors.push( constructor ); - } - - $.widget.bridge( name, constructor ); -}; - -$.widget.extend = function( target ) { - var input = slice.call( arguments, 1 ), - inputIndex = 0, - inputLength = input.length, - key, - value; - for ( ; inputIndex < inputLength; inputIndex++ ) { - for ( key in input[ inputIndex ] ) { - value = input[ inputIndex ][ key ]; - if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { - // Clone objects - if ( $.isPlainObject( value ) ) { - target[ key ] = $.isPlainObject( target[ key ] ) ? - $.widget.extend( {}, target[ key ], value ) : - // Don't extend strings, arrays, etc. with objects - $.widget.extend( {}, value ); - // Copy everything else by reference - } else { - target[ key ] = value; - } - } - } - } - return target; -}; - -$.widget.bridge = function( name, object ) { - var fullName = object.prototype.widgetFullName || name; - $.fn[ name ] = function( options ) { - var isMethodCall = typeof options === "string", - args = slice.call( arguments, 1 ), - returnValue = this; - - // allow multiple hashes to be passed on init - options = !isMethodCall && args.length ? - $.widget.extend.apply( null, [ options ].concat(args) ) : - options; - - if ( isMethodCall ) { - this.each(function() { - var methodValue, - instance = $.data( this, fullName ); - if ( !instance ) { - return $.error( "cannot call methods on " + name + " prior to initialization; " + - "attempted to call method '" + options + "'" ); - } - if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { - return $.error( "no such method '" + options + "' for " + name + " widget instance" ); - } - methodValue = instance[ options ].apply( instance, args ); - if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue && methodValue.jquery ? - returnValue.pushStack( methodValue.get() ) : - methodValue; - return false; - } - }); - } else { - this.each(function() { - var instance = $.data( this, fullName ); - if ( instance ) { - instance.option( options || {} )._init(); - } else { - $.data( this, fullName, new object( options, this ) ); - } - }); - } - - return returnValue; - }; -}; - -$.Widget = function( /* options, element */ ) {}; -$.Widget._childConstructors = []; - -$.Widget.prototype = { - widgetName: "widget", - widgetEventPrefix: "", - defaultElement: "
", - options: { - disabled: false, - - // callbacks - create: null - }, - _createWidget: function( options, element ) { - element = $( element || this.defaultElement || this )[ 0 ]; - this.element = $( element ); - this.uuid = uuid++; - this.eventNamespace = "." + this.widgetName + this.uuid; - this.options = $.widget.extend( {}, - this.options, - this._getCreateOptions(), - options ); - - this.bindings = $(); - this.hoverable = $(); - this.focusable = $(); - - if ( element !== this ) { - $.data( element, this.widgetFullName, this ); - this._on( true, this.element, { - remove: function( event ) { - if ( event.target === element ) { - this.destroy(); - } - } - }); - this.document = $( element.style ? - // element within the document - element.ownerDocument : - // element is window or document - element.document || element ); - this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); - } - - this._create(); - this._trigger( "create", null, this._getCreateEventData() ); - this._init(); - }, - _getCreateOptions: $.noop, - _getCreateEventData: $.noop, - _create: $.noop, - _init: $.noop, - - destroy: function() { - this._destroy(); - // we can probably remove the unbind calls in 2.0 - // all event bindings should go through this._on() - this.element - .unbind( this.eventNamespace ) - // 1.9 BC for #7810 - // TODO remove dual storage - .removeData( this.widgetName ) - .removeData( this.widgetFullName ) - // support: jquery <1.6.3 - // http://bugs.jquery.com/ticket/9413 - .removeData( $.camelCase( this.widgetFullName ) ); - this.widget() - .unbind( this.eventNamespace ) - .removeAttr( "aria-disabled" ) - .removeClass( - this.widgetFullName + "-disabled " + - "ui-state-disabled" ); - - // clean up events and states - this.bindings.unbind( this.eventNamespace ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); - }, - _destroy: $.noop, - - widget: function() { - return this.element; - }, - - option: function( key, value ) { - var options = key, - parts, - curOption, - i; - - if ( arguments.length === 0 ) { - // don't return a reference to the internal hash - return $.widget.extend( {}, this.options ); - } - - if ( typeof key === "string" ) { - // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } - options = {}; - parts = key.split( "." ); - key = parts.shift(); - if ( parts.length ) { - curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); - for ( i = 0; i < parts.length - 1; i++ ) { - curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; - curOption = curOption[ parts[ i ] ]; - } - key = parts.pop(); - if ( value === undefined ) { - return curOption[ key ] === undefined ? null : curOption[ key ]; - } - curOption[ key ] = value; - } else { - if ( value === undefined ) { - return this.options[ key ] === undefined ? null : this.options[ key ]; - } - options[ key ] = value; - } - } - - this._setOptions( options ); - - return this; - }, - _setOptions: function( options ) { - var key; - - for ( key in options ) { - this._setOption( key, options[ key ] ); - } - - return this; - }, - _setOption: function( key, value ) { - this.options[ key ] = value; - - if ( key === "disabled" ) { - this.widget() - .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) - .attr( "aria-disabled", value ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); - } - - return this; - }, - - enable: function() { - return this._setOption( "disabled", false ); - }, - disable: function() { - return this._setOption( "disabled", true ); - }, - - _on: function( suppressDisabledCheck, element, handlers ) { - var delegateElement, - instance = this; - - // no suppressDisabledCheck flag, shuffle arguments - if ( typeof suppressDisabledCheck !== "boolean" ) { - handlers = element; - element = suppressDisabledCheck; - suppressDisabledCheck = false; - } - - // no element argument, shuffle and use this.element - if ( !handlers ) { - handlers = element; - element = this.element; - delegateElement = this.widget(); - } else { - // accept selectors, DOM elements - element = delegateElement = $( element ); - this.bindings = this.bindings.add( element ); - } - - $.each( handlers, function( event, handler ) { - function handlerProxy() { - // allow widgets to customize the disabled handling - // - disabled as an array instead of boolean - // - disabled class as method for disabling individual parts - if ( !suppressDisabledCheck && - ( instance.options.disabled === true || - $( this ).hasClass( "ui-state-disabled" ) ) ) { - return; - } - return ( typeof handler === "string" ? instance[ handler ] : handler ) - .apply( instance, arguments ); - } - - // copy the guid so direct unbinding works - if ( typeof handler !== "string" ) { - handlerProxy.guid = handler.guid = - handler.guid || handlerProxy.guid || $.guid++; - } - - var match = event.match( /^(\w+)\s*(.*)$/ ), - eventName = match[1] + instance.eventNamespace, - selector = match[2]; - if ( selector ) { - delegateElement.delegate( selector, eventName, handlerProxy ); - } else { - element.bind( eventName, handlerProxy ); - } - }); - }, - - _off: function( element, eventName ) { - eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; - element.unbind( eventName ).undelegate( eventName ); - }, - - _delay: function( handler, delay ) { - function handlerProxy() { - return ( typeof handler === "string" ? instance[ handler ] : handler ) - .apply( instance, arguments ); - } - var instance = this; - return setTimeout( handlerProxy, delay || 0 ); - }, - - _hoverable: function( element ) { - this.hoverable = this.hoverable.add( element ); - this._on( element, { - mouseenter: function( event ) { - $( event.currentTarget ).addClass( "ui-state-hover" ); - }, - mouseleave: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-hover" ); - } - }); - }, - - _focusable: function( element ) { - this.focusable = this.focusable.add( element ); - this._on( element, { - focusin: function( event ) { - $( event.currentTarget ).addClass( "ui-state-focus" ); - }, - focusout: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-focus" ); - } - }); - }, - - _trigger: function( type, event, data ) { - var prop, orig, - callback = this.options[ type ]; - - data = data || {}; - event = $.Event( event ); - event.type = ( type === this.widgetEventPrefix ? - type : - this.widgetEventPrefix + type ).toLowerCase(); - // the original event may come from any element - // so we need to reset the target on the new event - event.target = this.element[ 0 ]; - - // copy original event properties over to the new event - orig = event.originalEvent; - if ( orig ) { - for ( prop in orig ) { - if ( !( prop in event ) ) { - event[ prop ] = orig[ prop ]; - } - } - } - - this.element.trigger( event, data ); - return !( $.isFunction( callback ) && - callback.apply( this.element[0], [ event ].concat( data ) ) === false || - event.isDefaultPrevented() ); - } -}; - -$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { - $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { - if ( typeof options === "string" ) { - options = { effect: options }; - } - var hasOptions, - effectName = !options ? - method : - options === true || typeof options === "number" ? - defaultEffect : - options.effect || defaultEffect; - options = options || {}; - if ( typeof options === "number" ) { - options = { duration: options }; - } - hasOptions = !$.isEmptyObject( options ); - options.complete = callback; - if ( options.delay ) { - element.delay( options.delay ); - } - if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { - element[ method ]( options ); - } else if ( effectName !== method && element[ effectName ] ) { - element[ effectName ]( options.duration, options.easing, callback ); - } else { - element.queue(function( next ) { - $( this )[ method ](); - if ( callback ) { - callback.call( element[ 0 ] ); - } - next(); - }); - } - }; -}); - -})( jQuery ); -(function( $, undefined ) { - -var mouseHandled = false; -$( document ).mouseup( function() { - mouseHandled = false; -}); - -$.widget("ui.mouse", { - version: "1.10.3", - options: { - cancel: "input,textarea,button,select,option", - distance: 1, - delay: 0 - }, - _mouseInit: function() { - var that = this; - - this.element - .bind("mousedown."+this.widgetName, function(event) { - return that._mouseDown(event); - }) - .bind("click."+this.widgetName, function(event) { - if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) { - $.removeData(event.target, that.widgetName + ".preventClickEvent"); - event.stopImmediatePropagation(); - return false; - } - }); - - this.started = false; - }, - - // TODO: make sure destroying one instance of mouse doesn't mess with - // other instances of mouse - _mouseDestroy: function() { - this.element.unbind("."+this.widgetName); - if ( this._mouseMoveDelegate ) { - $(document) - .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) - .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); - } - }, - - _mouseDown: function(event) { - // don't let more than one widget handle mouseStart - if( mouseHandled ) { return; } - - // we may have missed mouseup (out of window) - (this._mouseStarted && this._mouseUp(event)); - - this._mouseDownEvent = event; - - var that = this, - btnIsLeft = (event.which === 1), - // event.target.nodeName works around a bug in IE 8 with - // disabled inputs (#7620) - elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); - if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { - return true; - } - - this.mouseDelayMet = !this.options.delay; - if (!this.mouseDelayMet) { - this._mouseDelayTimer = setTimeout(function() { - that.mouseDelayMet = true; - }, this.options.delay); - } - - if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { - this._mouseStarted = (this._mouseStart(event) !== false); - if (!this._mouseStarted) { - event.preventDefault(); - return true; - } - } - - // Click event may never have fired (Gecko & Opera) - if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) { - $.removeData(event.target, this.widgetName + ".preventClickEvent"); - } - - // these delegates are required to keep context - this._mouseMoveDelegate = function(event) { - return that._mouseMove(event); - }; - this._mouseUpDelegate = function(event) { - return that._mouseUp(event); - }; - $(document) - .bind("mousemove."+this.widgetName, this._mouseMoveDelegate) - .bind("mouseup."+this.widgetName, this._mouseUpDelegate); - - event.preventDefault(); - - mouseHandled = true; - return true; - }, - - _mouseMove: function(event) { - // IE mouseup check - mouseup happened when mouse was out of window - if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) { - return this._mouseUp(event); - } - - if (this._mouseStarted) { - this._mouseDrag(event); - return event.preventDefault(); - } - - if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { - this._mouseStarted = - (this._mouseStart(this._mouseDownEvent, event) !== false); - (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); - } - - return !this._mouseStarted; - }, - - _mouseUp: function(event) { - $(document) - .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) - .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); - - if (this._mouseStarted) { - this._mouseStarted = false; - - if (event.target === this._mouseDownEvent.target) { - $.data(event.target, this.widgetName + ".preventClickEvent", true); - } - - this._mouseStop(event); - } - - return false; - }, - - _mouseDistanceMet: function(event) { - return (Math.max( - Math.abs(this._mouseDownEvent.pageX - event.pageX), - Math.abs(this._mouseDownEvent.pageY - event.pageY) - ) >= this.options.distance - ); - }, - - _mouseDelayMet: function(/* event */) { - return this.mouseDelayMet; - }, - - // These are placeholder methods, to be overriden by extending plugin - _mouseStart: function(/* event */) {}, - _mouseDrag: function(/* event */) {}, - _mouseStop: function(/* event */) {}, - _mouseCapture: function(/* event */) { return true; } -}); - -})(jQuery); -(function( $, undefined ) { - -$.ui = $.ui || {}; - -var cachedScrollbarWidth, - max = Math.max, - abs = Math.abs, - round = Math.round, - rhorizontal = /left|center|right/, - rvertical = /top|center|bottom/, - roffset = /[\+\-]\d+(\.[\d]+)?%?/, - rposition = /^\w+/, - rpercent = /%$/, - _position = $.fn.position; - -function getOffsets( offsets, width, height ) { - return [ - parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), - parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) - ]; -} - -function parseCss( element, property ) { - return parseInt( $.css( element, property ), 10 ) || 0; -} - -function getDimensions( elem ) { - var raw = elem[0]; - if ( raw.nodeType === 9 ) { - return { - width: elem.width(), - height: elem.height(), - offset: { top: 0, left: 0 } - }; - } - if ( $.isWindow( raw ) ) { - return { - width: elem.width(), - height: elem.height(), - offset: { top: elem.scrollTop(), left: elem.scrollLeft() } - }; - } - if ( raw.preventDefault ) { - return { - width: 0, - height: 0, - offset: { top: raw.pageY, left: raw.pageX } - }; - } - return { - width: elem.outerWidth(), - height: elem.outerHeight(), - offset: elem.offset() - }; -} - -$.position = { - scrollbarWidth: function() { - if ( cachedScrollbarWidth !== undefined ) { - return cachedScrollbarWidth; - } - var w1, w2, - div = $( "
" ), - innerDiv = div.children()[0]; - - $( "body" ).append( div ); - w1 = innerDiv.offsetWidth; - div.css( "overflow", "scroll" ); - - w2 = innerDiv.offsetWidth; - - if ( w1 === w2 ) { - w2 = div[0].clientWidth; - } - - div.remove(); - - return (cachedScrollbarWidth = w1 - w2); - }, - getScrollInfo: function( within ) { - var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ), - overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ), - hasOverflowX = overflowX === "scroll" || - ( overflowX === "auto" && within.width < within.element[0].scrollWidth ), - hasOverflowY = overflowY === "scroll" || - ( overflowY === "auto" && within.height < within.element[0].scrollHeight ); - return { - width: hasOverflowY ? $.position.scrollbarWidth() : 0, - height: hasOverflowX ? $.position.scrollbarWidth() : 0 - }; - }, - getWithinInfo: function( element ) { - var withinElement = $( element || window ), - isWindow = $.isWindow( withinElement[0] ); - return { - element: withinElement, - isWindow: isWindow, - offset: withinElement.offset() || { left: 0, top: 0 }, - scrollLeft: withinElement.scrollLeft(), - scrollTop: withinElement.scrollTop(), - width: isWindow ? withinElement.width() : withinElement.outerWidth(), - height: isWindow ? withinElement.height() : withinElement.outerHeight() - }; - } -}; - -$.fn.position = function( options ) { - if ( !options || !options.of ) { - return _position.apply( this, arguments ); - } - - // make a copy, we don't want to modify arguments - options = $.extend( {}, options ); - - var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, - target = $( options.of ), - within = $.position.getWithinInfo( options.within ), - scrollInfo = $.position.getScrollInfo( within ), - collision = ( options.collision || "flip" ).split( " " ), - offsets = {}; - - dimensions = getDimensions( target ); - if ( target[0].preventDefault ) { - // force left top to allow flipping - options.at = "left top"; - } - targetWidth = dimensions.width; - targetHeight = dimensions.height; - targetOffset = dimensions.offset; - // clone to reuse original targetOffset later - basePosition = $.extend( {}, targetOffset ); - - // force my and at to have valid horizontal and vertical positions - // if a value is missing or invalid, it will be converted to center - $.each( [ "my", "at" ], function() { - var pos = ( options[ this ] || "" ).split( " " ), - horizontalOffset, - verticalOffset; - - if ( pos.length === 1) { - pos = rhorizontal.test( pos[ 0 ] ) ? - pos.concat( [ "center" ] ) : - rvertical.test( pos[ 0 ] ) ? - [ "center" ].concat( pos ) : - [ "center", "center" ]; - } - pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; - pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; - - // calculate offsets - horizontalOffset = roffset.exec( pos[ 0 ] ); - verticalOffset = roffset.exec( pos[ 1 ] ); - offsets[ this ] = [ - horizontalOffset ? horizontalOffset[ 0 ] : 0, - verticalOffset ? verticalOffset[ 0 ] : 0 - ]; - - // reduce to just the positions without the offsets - options[ this ] = [ - rposition.exec( pos[ 0 ] )[ 0 ], - rposition.exec( pos[ 1 ] )[ 0 ] - ]; - }); - - // normalize collision option - if ( collision.length === 1 ) { - collision[ 1 ] = collision[ 0 ]; - } - - if ( options.at[ 0 ] === "right" ) { - basePosition.left += targetWidth; - } else if ( options.at[ 0 ] === "center" ) { - basePosition.left += targetWidth / 2; - } - - if ( options.at[ 1 ] === "bottom" ) { - basePosition.top += targetHeight; - } else if ( options.at[ 1 ] === "center" ) { - basePosition.top += targetHeight / 2; - } - - atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); - basePosition.left += atOffset[ 0 ]; - basePosition.top += atOffset[ 1 ]; - - return this.each(function() { - var collisionPosition, using, - elem = $( this ), - elemWidth = elem.outerWidth(), - elemHeight = elem.outerHeight(), - marginLeft = parseCss( this, "marginLeft" ), - marginTop = parseCss( this, "marginTop" ), - collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, - collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, - position = $.extend( {}, basePosition ), - myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); - - if ( options.my[ 0 ] === "right" ) { - position.left -= elemWidth; - } else if ( options.my[ 0 ] === "center" ) { - position.left -= elemWidth / 2; - } - - if ( options.my[ 1 ] === "bottom" ) { - position.top -= elemHeight; - } else if ( options.my[ 1 ] === "center" ) { - position.top -= elemHeight / 2; - } - - position.left += myOffset[ 0 ]; - position.top += myOffset[ 1 ]; - - // if the browser doesn't support fractions, then round for consistent results - if ( !$.support.offsetFractions ) { - position.left = round( position.left ); - position.top = round( position.top ); - } - - collisionPosition = { - marginLeft: marginLeft, - marginTop: marginTop - }; - - $.each( [ "left", "top" ], function( i, dir ) { - if ( $.ui.position[ collision[ i ] ] ) { - $.ui.position[ collision[ i ] ][ dir ]( position, { - targetWidth: targetWidth, - targetHeight: targetHeight, - elemWidth: elemWidth, - elemHeight: elemHeight, - collisionPosition: collisionPosition, - collisionWidth: collisionWidth, - collisionHeight: collisionHeight, - offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], - my: options.my, - at: options.at, - within: within, - elem : elem - }); - } - }); - - if ( options.using ) { - // adds feedback as second argument to using callback, if present - using = function( props ) { - var left = targetOffset.left - position.left, - right = left + targetWidth - elemWidth, - top = targetOffset.top - position.top, - bottom = top + targetHeight - elemHeight, - feedback = { - target: { - element: target, - left: targetOffset.left, - top: targetOffset.top, - width: targetWidth, - height: targetHeight - }, - element: { - element: elem, - left: position.left, - top: position.top, - width: elemWidth, - height: elemHeight - }, - horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", - vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" - }; - if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { - feedback.horizontal = "center"; - } - if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { - feedback.vertical = "middle"; - } - if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { - feedback.important = "horizontal"; - } else { - feedback.important = "vertical"; - } - options.using.call( this, props, feedback ); - }; - } - - elem.offset( $.extend( position, { using: using } ) ); - }); -}; - -$.ui.position = { - fit: { - left: function( position, data ) { - var within = data.within, - withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, - outerWidth = within.width, - collisionPosLeft = position.left - data.collisionPosition.marginLeft, - overLeft = withinOffset - collisionPosLeft, - overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, - newOverRight; - - // element is wider than within - if ( data.collisionWidth > outerWidth ) { - // element is initially over the left side of within - if ( overLeft > 0 && overRight <= 0 ) { - newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; - position.left += overLeft - newOverRight; - // element is initially over right side of within - } else if ( overRight > 0 && overLeft <= 0 ) { - position.left = withinOffset; - // element is initially over both left and right sides of within - } else { - if ( overLeft > overRight ) { - position.left = withinOffset + outerWidth - data.collisionWidth; - } else { - position.left = withinOffset; - } - } - // too far left -> align with left edge - } else if ( overLeft > 0 ) { - position.left += overLeft; - // too far right -> align with right edge - } else if ( overRight > 0 ) { - position.left -= overRight; - // adjust based on position and margin - } else { - position.left = max( position.left - collisionPosLeft, position.left ); - } - }, - top: function( position, data ) { - var within = data.within, - withinOffset = within.isWindow ? within.scrollTop : within.offset.top, - outerHeight = data.within.height, - collisionPosTop = position.top - data.collisionPosition.marginTop, - overTop = withinOffset - collisionPosTop, - overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, - newOverBottom; - - // element is taller than within - if ( data.collisionHeight > outerHeight ) { - // element is initially over the top of within - if ( overTop > 0 && overBottom <= 0 ) { - newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; - position.top += overTop - newOverBottom; - // element is initially over bottom of within - } else if ( overBottom > 0 && overTop <= 0 ) { - position.top = withinOffset; - // element is initially over both top and bottom of within - } else { - if ( overTop > overBottom ) { - position.top = withinOffset + outerHeight - data.collisionHeight; - } else { - position.top = withinOffset; - } - } - // too far up -> align with top - } else if ( overTop > 0 ) { - position.top += overTop; - // too far down -> align with bottom edge - } else if ( overBottom > 0 ) { - position.top -= overBottom; - // adjust based on position and margin - } else { - position.top = max( position.top - collisionPosTop, position.top ); - } - } - }, - flip: { - left: function( position, data ) { - var within = data.within, - withinOffset = within.offset.left + within.scrollLeft, - outerWidth = within.width, - offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, - collisionPosLeft = position.left - data.collisionPosition.marginLeft, - overLeft = collisionPosLeft - offsetLeft, - overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, - myOffset = data.my[ 0 ] === "left" ? - -data.elemWidth : - data.my[ 0 ] === "right" ? - data.elemWidth : - 0, - atOffset = data.at[ 0 ] === "left" ? - data.targetWidth : - data.at[ 0 ] === "right" ? - -data.targetWidth : - 0, - offset = -2 * data.offset[ 0 ], - newOverRight, - newOverLeft; - - if ( overLeft < 0 ) { - newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; - if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { - position.left += myOffset + atOffset + offset; - } - } - else if ( overRight > 0 ) { - newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; - if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { - position.left += myOffset + atOffset + offset; - } - } - }, - top: function( position, data ) { - var within = data.within, - withinOffset = within.offset.top + within.scrollTop, - outerHeight = within.height, - offsetTop = within.isWindow ? within.scrollTop : within.offset.top, - collisionPosTop = position.top - data.collisionPosition.marginTop, - overTop = collisionPosTop - offsetTop, - overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, - top = data.my[ 1 ] === "top", - myOffset = top ? - -data.elemHeight : - data.my[ 1 ] === "bottom" ? - data.elemHeight : - 0, - atOffset = data.at[ 1 ] === "top" ? - data.targetHeight : - data.at[ 1 ] === "bottom" ? - -data.targetHeight : - 0, - offset = -2 * data.offset[ 1 ], - newOverTop, - newOverBottom; - if ( overTop < 0 ) { - newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; - if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) { - position.top += myOffset + atOffset + offset; - } - } - else if ( overBottom > 0 ) { - newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; - if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) { - position.top += myOffset + atOffset + offset; - } - } - } - }, - flipfit: { - left: function() { - $.ui.position.flip.left.apply( this, arguments ); - $.ui.position.fit.left.apply( this, arguments ); - }, - top: function() { - $.ui.position.flip.top.apply( this, arguments ); - $.ui.position.fit.top.apply( this, arguments ); - } - } -}; - -// fraction support test -(function () { - var testElement, testElementParent, testElementStyle, offsetLeft, i, - body = document.getElementsByTagName( "body" )[ 0 ], - div = document.createElement( "div" ); - - //Create a "fake body" for testing based on method used in jQuery.support - testElement = document.createElement( body ? "div" : "body" ); - testElementStyle = { - visibility: "hidden", - width: 0, - height: 0, - border: 0, - margin: 0, - background: "none" - }; - if ( body ) { - $.extend( testElementStyle, { - position: "absolute", - left: "-1000px", - top: "-1000px" - }); - } - for ( i in testElementStyle ) { - testElement.style[ i ] = testElementStyle[ i ]; - } - testElement.appendChild( div ); - testElementParent = body || document.documentElement; - testElementParent.insertBefore( testElement, testElementParent.firstChild ); - - div.style.cssText = "position: absolute; left: 10.7432222px;"; - - offsetLeft = $( div ).offset().left; - $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11; - - testElement.innerHTML = ""; - testElementParent.removeChild( testElement ); -})(); - -}( jQuery ) ); -(function( $, undefined ) { - -$.widget("ui.draggable", $.ui.mouse, { - version: "1.10.3", - widgetEventPrefix: "drag", - options: { - addClasses: true, - appendTo: "parent", - axis: false, - connectToSortable: false, - containment: false, - cursor: "auto", - cursorAt: false, - grid: false, - handle: false, - helper: "original", - iframeFix: false, - opacity: false, - refreshPositions: false, - revert: false, - revertDuration: 500, - scope: "default", - scroll: true, - scrollSensitivity: 20, - scrollSpeed: 20, - snap: false, - snapMode: "both", - snapTolerance: 20, - stack: false, - zIndex: false, - - // callbacks - drag: null, - start: null, - stop: null - }, - _create: function() { - - if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) { - this.element[0].style.position = "relative"; - } - if (this.options.addClasses){ - this.element.addClass("ui-draggable"); - } - if (this.options.disabled){ - this.element.addClass("ui-draggable-disabled"); - } - - this._mouseInit(); - - }, - - _destroy: function() { - this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" ); - this._mouseDestroy(); - }, - - _mouseCapture: function(event) { - - var o = this.options; - - // among others, prevent a drag on a resizable-handle - if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) { - return false; - } - - //Quit if we're not on a valid handle - this.handle = this._getHandle(event); - if (!this.handle) { - return false; - } - - $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() { - $("
") - .css({ - width: this.offsetWidth+"px", height: this.offsetHeight+"px", - position: "absolute", opacity: "0.001", zIndex: 1000 - }) - .css($(this).offset()) - .appendTo("body"); - }); - - return true; - - }, - - _mouseStart: function(event) { - - var o = this.options; - - //Create and append the visible helper - this.helper = this._createHelper(event); - - this.helper.addClass("ui-draggable-dragging"); - - //Cache the helper size - this._cacheHelperProportions(); - - //If ddmanager is used for droppables, set the global draggable - if($.ui.ddmanager) { - $.ui.ddmanager.current = this; - } - - /* - * - Position generation - - * This block generates everything position related - it's the core of draggables. - */ - - //Cache the margins of the original element - this._cacheMargins(); - - //Store the helper's css position - this.cssPosition = this.helper.css( "position" ); - this.scrollParent = this.helper.scrollParent(); - this.offsetParent = this.helper.offsetParent(); - this.offsetParentCssPosition = this.offsetParent.css( "position" ); - - //The element's absolute position on the page minus margins - this.offset = this.positionAbs = this.element.offset(); - this.offset = { - top: this.offset.top - this.margins.top, - left: this.offset.left - this.margins.left - }; - - //Reset scroll cache - this.offset.scroll = false; - - $.extend(this.offset, { - click: { //Where the click happened, relative to the element - left: event.pageX - this.offset.left, - top: event.pageY - this.offset.top - }, - parent: this._getParentOffset(), - relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper - }); - - //Generate the original position - this.originalPosition = this.position = this._generatePosition(event); - this.originalPageX = event.pageX; - this.originalPageY = event.pageY; - - //Adjust the mouse offset relative to the helper if "cursorAt" is supplied - (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); - - //Set a containment if given in the options - this._setContainment(); - - //Trigger event + callbacks - if(this._trigger("start", event) === false) { - this._clear(); - return false; - } - - //Recache the helper size - this._cacheHelperProportions(); - - //Prepare the droppable offsets - if ($.ui.ddmanager && !o.dropBehaviour) { - $.ui.ddmanager.prepareOffsets(this, event); - } - - - this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position - - //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003) - if ( $.ui.ddmanager ) { - $.ui.ddmanager.dragStart(this, event); - } - - return true; - }, - - _mouseDrag: function(event, noPropagation) { - // reset any necessary cached properties (see #5009) - if ( this.offsetParentCssPosition === "fixed" ) { - this.offset.parent = this._getParentOffset(); - } - - //Compute the helpers position - this.position = this._generatePosition(event); - this.positionAbs = this._convertPositionTo("absolute"); - - //Call plugins and callbacks and use the resulting position if something is returned - if (!noPropagation) { - var ui = this._uiHash(); - if(this._trigger("drag", event, ui) === false) { - this._mouseUp({}); - return false; - } - this.position = ui.position; - } - - if(!this.options.axis || this.options.axis !== "y") { - this.helper[0].style.left = this.position.left+"px"; - } - if(!this.options.axis || this.options.axis !== "x") { - this.helper[0].style.top = this.position.top+"px"; - } - if($.ui.ddmanager) { - $.ui.ddmanager.drag(this, event); - } - - return false; - }, - - _mouseStop: function(event) { - - //If we are using droppables, inform the manager about the drop - var that = this, - dropped = false; - if ($.ui.ddmanager && !this.options.dropBehaviour) { - dropped = $.ui.ddmanager.drop(this, event); - } - - //if a drop comes from outside (a sortable) - if(this.dropped) { - dropped = this.dropped; - this.dropped = false; - } - - //if the original element is no longer in the DOM don't bother to continue (see #8269) - if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) { - return false; - } - - if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) { - $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() { - if(that._trigger("stop", event) !== false) { - that._clear(); - } - }); - } else { - if(this._trigger("stop", event) !== false) { - this._clear(); - } - } - - return false; - }, - - _mouseUp: function(event) { - //Remove frame helpers - $("div.ui-draggable-iframeFix").each(function() { - this.parentNode.removeChild(this); - }); - - //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003) - if( $.ui.ddmanager ) { - $.ui.ddmanager.dragStop(this, event); - } - - return $.ui.mouse.prototype._mouseUp.call(this, event); - }, - - cancel: function() { - - if(this.helper.is(".ui-draggable-dragging")) { - this._mouseUp({}); - } else { - this._clear(); - } - - return this; - - }, - - _getHandle: function(event) { - return this.options.handle ? - !!$( event.target ).closest( this.element.find( this.options.handle ) ).length : - true; - }, - - _createHelper: function(event) { - - var o = this.options, - helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element); - - if(!helper.parents("body").length) { - helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo)); - } - - if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) { - helper.css("position", "absolute"); - } - - return helper; - - }, - - _adjustOffsetFromHelper: function(obj) { - if (typeof obj === "string") { - obj = obj.split(" "); - } - if ($.isArray(obj)) { - obj = {left: +obj[0], top: +obj[1] || 0}; - } - if ("left" in obj) { - this.offset.click.left = obj.left + this.margins.left; - } - if ("right" in obj) { - this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; - } - if ("top" in obj) { - this.offset.click.top = obj.top + this.margins.top; - } - if ("bottom" in obj) { - this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; - } - }, - - _getParentOffset: function() { - - //Get the offsetParent and cache its position - var po = this.offsetParent.offset(); - - // This is a special case where we need to modify a offset calculated on start, since the following happened: - // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent - // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that - // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag - if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) { - po.left += this.scrollParent.scrollLeft(); - po.top += this.scrollParent.scrollTop(); - } - - //This needs to be actually done for all browsers, since pageX/pageY includes this information - //Ugly IE fix - if((this.offsetParent[0] === document.body) || - (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) { - po = { top: 0, left: 0 }; - } - - return { - top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), - left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) - }; - - }, - - _getRelativeOffset: function() { - - if(this.cssPosition === "relative") { - var p = this.element.position(); - return { - top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), - left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() - }; - } else { - return { top: 0, left: 0 }; - } - - }, - - _cacheMargins: function() { - this.margins = { - left: (parseInt(this.element.css("marginLeft"),10) || 0), - top: (parseInt(this.element.css("marginTop"),10) || 0), - right: (parseInt(this.element.css("marginRight"),10) || 0), - bottom: (parseInt(this.element.css("marginBottom"),10) || 0) - }; - }, - - _cacheHelperProportions: function() { - this.helperProportions = { - width: this.helper.outerWidth(), - height: this.helper.outerHeight() - }; - }, - - _setContainment: function() { - - var over, c, ce, - o = this.options; - - if ( !o.containment ) { - this.containment = null; - return; - } - - if ( o.containment === "window" ) { - this.containment = [ - $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left, - $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top, - $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left, - $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top - ]; - return; - } - - if ( o.containment === "document") { - this.containment = [ - 0, - 0, - $( document ).width() - this.helperProportions.width - this.margins.left, - ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top - ]; - return; - } - - if ( o.containment.constructor === Array ) { - this.containment = o.containment; - return; - } - - if ( o.containment === "parent" ) { - o.containment = this.helper[ 0 ].parentNode; - } - - c = $( o.containment ); - ce = c[ 0 ]; - - if( !ce ) { - return; - } - - over = c.css( "overflow" ) !== "hidden"; - - this.containment = [ - ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ), - ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) , - ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right, - ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom - ]; - this.relative_container = c; - }, - - _convertPositionTo: function(d, pos) { - - if(!pos) { - pos = this.position; - } - - var mod = d === "absolute" ? 1 : -1, - scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent; - - //Cache the scroll - if (!this.offset.scroll) { - this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()}; - } - - return { - top: ( - pos.top + // The absolute mouse position - this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod ) - ), - left: ( - pos.left + // The absolute mouse position - this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod ) - ) - }; - - }, - - _generatePosition: function(event) { - - var containment, co, top, left, - o = this.options, - scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent, - pageX = event.pageX, - pageY = event.pageY; - - //Cache the scroll - if (!this.offset.scroll) { - this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()}; - } - - /* - * - Position constraining - - * Constrain the position to a mix of grid, containment. - */ - - // If we are not dragging yet, we won't check for options - if ( this.originalPosition ) { - if ( this.containment ) { - if ( this.relative_container ){ - co = this.relative_container.offset(); - containment = [ - this.containment[ 0 ] + co.left, - this.containment[ 1 ] + co.top, - this.containment[ 2 ] + co.left, - this.containment[ 3 ] + co.top - ]; - } - else { - containment = this.containment; - } - - if(event.pageX - this.offset.click.left < containment[0]) { - pageX = containment[0] + this.offset.click.left; - } - if(event.pageY - this.offset.click.top < containment[1]) { - pageY = containment[1] + this.offset.click.top; - } - if(event.pageX - this.offset.click.left > containment[2]) { - pageX = containment[2] + this.offset.click.left; - } - if(event.pageY - this.offset.click.top > containment[3]) { - pageY = containment[3] + this.offset.click.top; - } - } - - if(o.grid) { - //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950) - top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY; - pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; - - left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX; - pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; - } - - } - - return { - top: ( - pageY - // The absolute mouse position - this.offset.click.top - // Click offset (relative to the element) - this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.top + // The offsetParent's offset without borders (offset + border) - ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) - ), - left: ( - pageX - // The absolute mouse position - this.offset.click.left - // Click offset (relative to the element) - this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.left + // The offsetParent's offset without borders (offset + border) - ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) - ) - }; - - }, - - _clear: function() { - this.helper.removeClass("ui-draggable-dragging"); - if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) { - this.helper.remove(); - } - this.helper = null; - this.cancelHelperRemoval = false; - }, - - // From now on bulk stuff - mainly helpers - - _trigger: function(type, event, ui) { - ui = ui || this._uiHash(); - $.ui.plugin.call(this, type, [event, ui]); - //The absolute position has to be recalculated after plugins - if(type === "drag") { - this.positionAbs = this._convertPositionTo("absolute"); - } - return $.Widget.prototype._trigger.call(this, type, event, ui); - }, - - plugins: {}, - - _uiHash: function() { - return { - helper: this.helper, - position: this.position, - originalPosition: this.originalPosition, - offset: this.positionAbs - }; - } - -}); - -$.ui.plugin.add("draggable", "connectToSortable", { - start: function(event, ui) { - - var inst = $(this).data("ui-draggable"), o = inst.options, - uiSortable = $.extend({}, ui, { item: inst.element }); - inst.sortables = []; - $(o.connectToSortable).each(function() { - var sortable = $.data(this, "ui-sortable"); - if (sortable && !sortable.options.disabled) { - inst.sortables.push({ - instance: sortable, - shouldRevert: sortable.options.revert - }); - sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page). - sortable._trigger("activate", event, uiSortable); - } - }); - - }, - stop: function(event, ui) { - - //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper - var inst = $(this).data("ui-draggable"), - uiSortable = $.extend({}, ui, { item: inst.element }); - - $.each(inst.sortables, function() { - if(this.instance.isOver) { - - this.instance.isOver = 0; - - inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance - this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work) - - //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid" - if(this.shouldRevert) { - this.instance.options.revert = this.shouldRevert; - } - - //Trigger the stop of the sortable - this.instance._mouseStop(event); - - this.instance.options.helper = this.instance.options._helper; - - //If the helper has been the original item, restore properties in the sortable - if(inst.options.helper === "original") { - this.instance.currentItem.css({ top: "auto", left: "auto" }); - } - - } else { - this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance - this.instance._trigger("deactivate", event, uiSortable); - } - - }); - - }, - drag: function(event, ui) { - - var inst = $(this).data("ui-draggable"), that = this; - - $.each(inst.sortables, function() { - - var innermostIntersecting = false, - thisSortable = this; - - //Copy over some variables to allow calling the sortable's native _intersectsWith - this.instance.positionAbs = inst.positionAbs; - this.instance.helperProportions = inst.helperProportions; - this.instance.offset.click = inst.offset.click; - - if(this.instance._intersectsWith(this.instance.containerCache)) { - innermostIntersecting = true; - $.each(inst.sortables, function () { - this.instance.positionAbs = inst.positionAbs; - this.instance.helperProportions = inst.helperProportions; - this.instance.offset.click = inst.offset.click; - if (this !== thisSortable && - this.instance._intersectsWith(this.instance.containerCache) && - $.contains(thisSortable.instance.element[0], this.instance.element[0]) - ) { - innermostIntersecting = false; - } - return innermostIntersecting; - }); - } - - - if(innermostIntersecting) { - //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once - if(!this.instance.isOver) { - - this.instance.isOver = 1; - //Now we fake the start of dragging for the sortable instance, - //by cloning the list group item, appending it to the sortable and using it as inst.currentItem - //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one) - this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true); - this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it - this.instance.options.helper = function() { return ui.helper[0]; }; - - event.target = this.instance.currentItem[0]; - this.instance._mouseCapture(event, true); - this.instance._mouseStart(event, true, true); - - //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes - this.instance.offset.click.top = inst.offset.click.top; - this.instance.offset.click.left = inst.offset.click.left; - this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left; - this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top; - - inst._trigger("toSortable", event); - inst.dropped = this.instance.element; //draggable revert needs that - //hack so receive/update callbacks work (mostly) - inst.currentItem = inst.element; - this.instance.fromOutside = inst; - - } - - //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable - if(this.instance.currentItem) { - this.instance._mouseDrag(event); - } - - } else { - - //If it doesn't intersect with the sortable, and it intersected before, - //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval - if(this.instance.isOver) { - - this.instance.isOver = 0; - this.instance.cancelHelperRemoval = true; - - //Prevent reverting on this forced stop - this.instance.options.revert = false; - - // The out event needs to be triggered independently - this.instance._trigger("out", event, this.instance._uiHash(this.instance)); - - this.instance._mouseStop(event, true); - this.instance.options.helper = this.instance.options._helper; - - //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size - this.instance.currentItem.remove(); - if(this.instance.placeholder) { - this.instance.placeholder.remove(); - } - - inst._trigger("fromSortable", event); - inst.dropped = false; //draggable revert needs that - } - - } - - }); - - } -}); - -$.ui.plugin.add("draggable", "cursor", { - start: function() { - var t = $("body"), o = $(this).data("ui-draggable").options; - if (t.css("cursor")) { - o._cursor = t.css("cursor"); - } - t.css("cursor", o.cursor); - }, - stop: function() { - var o = $(this).data("ui-draggable").options; - if (o._cursor) { - $("body").css("cursor", o._cursor); - } - } -}); - -$.ui.plugin.add("draggable", "opacity", { - start: function(event, ui) { - var t = $(ui.helper), o = $(this).data("ui-draggable").options; - if(t.css("opacity")) { - o._opacity = t.css("opacity"); - } - t.css("opacity", o.opacity); - }, - stop: function(event, ui) { - var o = $(this).data("ui-draggable").options; - if(o._opacity) { - $(ui.helper).css("opacity", o._opacity); - } - } -}); - -$.ui.plugin.add("draggable", "scroll", { - start: function() { - var i = $(this).data("ui-draggable"); - if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") { - i.overflowOffset = i.scrollParent.offset(); - } - }, - drag: function( event ) { - - var i = $(this).data("ui-draggable"), o = i.options, scrolled = false; - - if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") { - - if(!o.axis || o.axis !== "x") { - if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) { - i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed; - } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) { - i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed; - } - } - - if(!o.axis || o.axis !== "y") { - if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) { - i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed; - } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) { - i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed; - } - } - - } else { - - if(!o.axis || o.axis !== "x") { - if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) { - scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); - } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) { - scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); - } - } - - if(!o.axis || o.axis !== "y") { - if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) { - scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); - } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) { - scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); - } - } - - } - - if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) { - $.ui.ddmanager.prepareOffsets(i, event); - } - - } -}); - -$.ui.plugin.add("draggable", "snap", { - start: function() { - - var i = $(this).data("ui-draggable"), - o = i.options; - - i.snapElements = []; - - $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() { - var $t = $(this), - $o = $t.offset(); - if(this !== i.element[0]) { - i.snapElements.push({ - item: this, - width: $t.outerWidth(), height: $t.outerHeight(), - top: $o.top, left: $o.left - }); - } - }); - - }, - drag: function(event, ui) { - - var ts, bs, ls, rs, l, r, t, b, i, first, - inst = $(this).data("ui-draggable"), - o = inst.options, - d = o.snapTolerance, - x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width, - y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height; - - for (i = inst.snapElements.length - 1; i >= 0; i--){ - - l = inst.snapElements[i].left; - r = l + inst.snapElements[i].width; - t = inst.snapElements[i].top; - b = t + inst.snapElements[i].height; - - if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) { - if(inst.snapElements[i].snapping) { - (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); - } - inst.snapElements[i].snapping = false; - continue; - } - - if(o.snapMode !== "inner") { - ts = Math.abs(t - y2) <= d; - bs = Math.abs(b - y1) <= d; - ls = Math.abs(l - x2) <= d; - rs = Math.abs(r - x1) <= d; - if(ts) { - ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top; - } - if(bs) { - ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top; - } - if(ls) { - ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left; - } - if(rs) { - ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left; - } - } - - first = (ts || bs || ls || rs); - - if(o.snapMode !== "outer") { - ts = Math.abs(t - y1) <= d; - bs = Math.abs(b - y2) <= d; - ls = Math.abs(l - x1) <= d; - rs = Math.abs(r - x2) <= d; - if(ts) { - ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top; - } - if(bs) { - ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top; - } - if(ls) { - ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left; - } - if(rs) { - ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left; - } - } - - if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) { - (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); - } - inst.snapElements[i].snapping = (ts || bs || ls || rs || first); - - } - - } -}); - -$.ui.plugin.add("draggable", "stack", { - start: function() { - var min, - o = this.data("ui-draggable").options, - group = $.makeArray($(o.stack)).sort(function(a,b) { - return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0); - }); - - if (!group.length) { return; } - - min = parseInt($(group[0]).css("zIndex"), 10) || 0; - $(group).each(function(i) { - $(this).css("zIndex", min + i); - }); - this.css("zIndex", (min + group.length)); - } -}); - -$.ui.plugin.add("draggable", "zIndex", { - start: function(event, ui) { - var t = $(ui.helper), o = $(this).data("ui-draggable").options; - if(t.css("zIndex")) { - o._zIndex = t.css("zIndex"); - } - t.css("zIndex", o.zIndex); - }, - stop: function(event, ui) { - var o = $(this).data("ui-draggable").options; - if(o._zIndex) { - $(ui.helper).css("zIndex", o._zIndex); - } - } -}); - -})(jQuery); -(function( $, undefined ) { - -function isOverAxis( x, reference, size ) { - return ( x > reference ) && ( x < ( reference + size ) ); -} - -$.widget("ui.droppable", { - version: "1.10.3", - widgetEventPrefix: "drop", - options: { - accept: "*", - activeClass: false, - addClasses: true, - greedy: false, - hoverClass: false, - scope: "default", - tolerance: "intersect", - - // callbacks - activate: null, - deactivate: null, - drop: null, - out: null, - over: null - }, - _create: function() { - - var o = this.options, - accept = o.accept; - - this.isover = false; - this.isout = true; - - this.accept = $.isFunction(accept) ? accept : function(d) { - return d.is(accept); - }; - - //Store the droppable's proportions - this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight }; - - // Add the reference and positions to the manager - $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || []; - $.ui.ddmanager.droppables[o.scope].push(this); - - (o.addClasses && this.element.addClass("ui-droppable")); - - }, - - _destroy: function() { - var i = 0, - drop = $.ui.ddmanager.droppables[this.options.scope]; - - for ( ; i < drop.length; i++ ) { - if ( drop[i] === this ) { - drop.splice(i, 1); - } - } - - this.element.removeClass("ui-droppable ui-droppable-disabled"); - }, - - _setOption: function(key, value) { - - if(key === "accept") { - this.accept = $.isFunction(value) ? value : function(d) { - return d.is(value); - }; - } - $.Widget.prototype._setOption.apply(this, arguments); - }, - - _activate: function(event) { - var draggable = $.ui.ddmanager.current; - if(this.options.activeClass) { - this.element.addClass(this.options.activeClass); - } - if(draggable){ - this._trigger("activate", event, this.ui(draggable)); - } - }, - - _deactivate: function(event) { - var draggable = $.ui.ddmanager.current; - if(this.options.activeClass) { - this.element.removeClass(this.options.activeClass); - } - if(draggable){ - this._trigger("deactivate", event, this.ui(draggable)); - } - }, - - _over: function(event) { - - var draggable = $.ui.ddmanager.current; - - // Bail if draggable and droppable are same element - if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { - return; - } - - if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.hoverClass) { - this.element.addClass(this.options.hoverClass); - } - this._trigger("over", event, this.ui(draggable)); - } - - }, - - _out: function(event) { - - var draggable = $.ui.ddmanager.current; - - // Bail if draggable and droppable are same element - if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { - return; - } - - if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.hoverClass) { - this.element.removeClass(this.options.hoverClass); - } - this._trigger("out", event, this.ui(draggable)); - } - - }, - - _drop: function(event,custom) { - - var draggable = custom || $.ui.ddmanager.current, - childrenIntersection = false; - - // Bail if draggable and droppable are same element - if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { - return false; - } - - this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() { - var inst = $.data(this, "ui-droppable"); - if( - inst.options.greedy && - !inst.options.disabled && - inst.options.scope === draggable.options.scope && - inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) && - $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance) - ) { childrenIntersection = true; return false; } - }); - if(childrenIntersection) { - return false; - } - - if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.activeClass) { - this.element.removeClass(this.options.activeClass); - } - if(this.options.hoverClass) { - this.element.removeClass(this.options.hoverClass); - } - this._trigger("drop", event, this.ui(draggable)); - return this.element; - } - - return false; - - }, - - ui: function(c) { - return { - draggable: (c.currentItem || c.element), - helper: c.helper, - position: c.position, - offset: c.positionAbs - }; - } - -}); - -$.ui.intersect = function(draggable, droppable, toleranceMode) { - - if (!droppable.offset) { - return false; - } - - var draggableLeft, draggableTop, - x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width, - y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height, - l = droppable.offset.left, r = l + droppable.proportions.width, - t = droppable.offset.top, b = t + droppable.proportions.height; - - switch (toleranceMode) { - case "fit": - return (l <= x1 && x2 <= r && t <= y1 && y2 <= b); - case "intersect": - return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half - x2 - (draggable.helperProportions.width / 2) < r && // Left Half - t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half - y2 - (draggable.helperProportions.height / 2) < b ); // Top Half - case "pointer": - draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left); - draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top); - return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width ); - case "touch": - return ( - (y1 >= t && y1 <= b) || // Top edge touching - (y2 >= t && y2 <= b) || // Bottom edge touching - (y1 < t && y2 > b) // Surrounded vertically - ) && ( - (x1 >= l && x1 <= r) || // Left edge touching - (x2 >= l && x2 <= r) || // Right edge touching - (x1 < l && x2 > r) // Surrounded horizontally - ); - default: - return false; - } - -}; - -/* - This manager tracks offsets of draggables and droppables -*/ -$.ui.ddmanager = { - current: null, - droppables: { "default": [] }, - prepareOffsets: function(t, event) { - - var i, j, - m = $.ui.ddmanager.droppables[t.options.scope] || [], - type = event ? event.type : null, // workaround for #2317 - list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack(); - - droppablesLoop: for (i = 0; i < m.length; i++) { - - //No disabled and non-accepted - if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) { - continue; - } - - // Filter out elements in the current dragged item - for (j=0; j < list.length; j++) { - if(list[j] === m[i].element[0]) { - m[i].proportions.height = 0; - continue droppablesLoop; - } - } - - m[i].visible = m[i].element.css("display") !== "none"; - if(!m[i].visible) { - continue; - } - - //Activate the droppable if used directly from draggables - if(type === "mousedown") { - m[i]._activate.call(m[i], event); - } - - m[i].offset = m[i].element.offset(); - m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight }; - - } - - }, - drop: function(draggable, event) { - - var dropped = false; - // Create a copy of the droppables in case the list changes during the drop (#9116) - $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() { - - if(!this.options) { - return; - } - if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) { - dropped = this._drop.call(this, event) || dropped; - } - - if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - this.isout = true; - this.isover = false; - this._deactivate.call(this, event); - } - - }); - return dropped; - - }, - dragStart: function( draggable, event ) { - //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003) - draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() { - if( !draggable.options.refreshPositions ) { - $.ui.ddmanager.prepareOffsets( draggable, event ); - } - }); - }, - drag: function(draggable, event) { - - //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse. - if(draggable.options.refreshPositions) { - $.ui.ddmanager.prepareOffsets(draggable, event); - } - - //Run through all droppables and check their positions based on specific tolerance options - $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { - - if(this.options.disabled || this.greedyChild || !this.visible) { - return; - } - - var parentInstance, scope, parent, - intersects = $.ui.intersect(draggable, this, this.options.tolerance), - c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null); - if(!c) { - return; - } - - if (this.options.greedy) { - // find droppable parents with same scope - scope = this.options.scope; - parent = this.element.parents(":data(ui-droppable)").filter(function () { - return $.data(this, "ui-droppable").options.scope === scope; - }); - - if (parent.length) { - parentInstance = $.data(parent[0], "ui-droppable"); - parentInstance.greedyChild = (c === "isover"); - } - } - - // we just moved into a greedy child - if (parentInstance && c === "isover") { - parentInstance.isover = false; - parentInstance.isout = true; - parentInstance._out.call(parentInstance, event); - } - - this[c] = true; - this[c === "isout" ? "isover" : "isout"] = false; - this[c === "isover" ? "_over" : "_out"].call(this, event); - - // we just moved out of a greedy child - if (parentInstance && c === "isout") { - parentInstance.isout = false; - parentInstance.isover = true; - parentInstance._over.call(parentInstance, event); - } - }); - - }, - dragStop: function( draggable, event ) { - draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" ); - //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003) - if( !draggable.options.refreshPositions ) { - $.ui.ddmanager.prepareOffsets( draggable, event ); - } - } -}; - -})(jQuery); -(function( $, undefined ) { - -function num(v) { - return parseInt(v, 10) || 0; -} - -function isNumber(value) { - return !isNaN(parseInt(value, 10)); -} - -$.widget("ui.resizable", $.ui.mouse, { - version: "1.10.3", - widgetEventPrefix: "resize", - options: { - alsoResize: false, - animate: false, - animateDuration: "slow", - animateEasing: "swing", - aspectRatio: false, - autoHide: false, - containment: false, - ghost: false, - grid: false, - handles: "e,s,se", - helper: false, - maxHeight: null, - maxWidth: null, - minHeight: 10, - minWidth: 10, - // See #7960 - zIndex: 90, - - // callbacks - resize: null, - start: null, - stop: null - }, - _create: function() { - - var n, i, handle, axis, hname, - that = this, - o = this.options; - this.element.addClass("ui-resizable"); - - $.extend(this, { - _aspectRatio: !!(o.aspectRatio), - aspectRatio: o.aspectRatio, - originalElement: this.element, - _proportionallyResizeElements: [], - _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null - }); - - //Wrap the element if it cannot hold child nodes - if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) { - - //Create a wrapper element and set the wrapper to the new current internal element - this.element.wrap( - $("
").css({ - position: this.element.css("position"), - width: this.element.outerWidth(), - height: this.element.outerHeight(), - top: this.element.css("top"), - left: this.element.css("left") - }) - ); - - //Overwrite the original this.element - this.element = this.element.parent().data( - "ui-resizable", this.element.data("ui-resizable") - ); - - this.elementIsWrapper = true; - - //Move margins to the wrapper - this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") }); - this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0}); - - //Prevent Safari textarea resize - this.originalResizeStyle = this.originalElement.css("resize"); - this.originalElement.css("resize", "none"); - - //Push the actual element to our proportionallyResize internal array - this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" })); - - // avoid IE jump (hard set the margin) - this.originalElement.css({ margin: this.originalElement.css("margin") }); - - // fix handlers offset - this._proportionallyResize(); - - } - - this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" }); - if(this.handles.constructor === String) { - - if ( this.handles === "all") { - this.handles = "n,e,s,w,se,sw,ne,nw"; - } - - n = this.handles.split(","); - this.handles = {}; - - for(i = 0; i < n.length; i++) { - - handle = $.trim(n[i]); - hname = "ui-resizable-"+handle; - axis = $("
"); - - // Apply zIndex to all handles - see #7960 - axis.css({ zIndex: o.zIndex }); - - //TODO : What's going on here? - if ("se" === handle) { - axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se"); - } - - //Insert into internal handles object and append to element - this.handles[handle] = ".ui-resizable-"+handle; - this.element.append(axis); - } - - } - - this._renderAxis = function(target) { - - var i, axis, padPos, padWrapper; - - target = target || this.element; - - for(i in this.handles) { - - if(this.handles[i].constructor === String) { - this.handles[i] = $(this.handles[i], this.element).show(); - } - - //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls) - if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) { - - axis = $(this.handles[i], this.element); - - //Checking the correct pad and border - padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth(); - - //The padding type i have to apply... - padPos = [ "padding", - /ne|nw|n/.test(i) ? "Top" : - /se|sw|s/.test(i) ? "Bottom" : - /^e$/.test(i) ? "Right" : "Left" ].join(""); - - target.css(padPos, padWrapper); - - this._proportionallyResize(); - - } - - //TODO: What's that good for? There's not anything to be executed left - if(!$(this.handles[i]).length) { - continue; - } - } - }; - - //TODO: make renderAxis a prototype function - this._renderAxis(this.element); - - this._handles = $(".ui-resizable-handle", this.element) - .disableSelection(); - - //Matching axis name - this._handles.mouseover(function() { - if (!that.resizing) { - if (this.className) { - axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i); - } - //Axis, default = se - that.axis = axis && axis[1] ? axis[1] : "se"; - } - }); - - //If we want to auto hide the elements - if (o.autoHide) { - this._handles.hide(); - $(this.element) - .addClass("ui-resizable-autohide") - .mouseenter(function() { - if (o.disabled) { - return; - } - $(this).removeClass("ui-resizable-autohide"); - that._handles.show(); - }) - .mouseleave(function(){ - if (o.disabled) { - return; - } - if (!that.resizing) { - $(this).addClass("ui-resizable-autohide"); - that._handles.hide(); - } - }); - } - - //Initialize the mouse interaction - this._mouseInit(); - - }, - - _destroy: function() { - - this._mouseDestroy(); - - var wrapper, - _destroy = function(exp) { - $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing") - .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove(); - }; - - //TODO: Unwrap at same DOM position - if (this.elementIsWrapper) { - _destroy(this.element); - wrapper = this.element; - this.originalElement.css({ - position: wrapper.css("position"), - width: wrapper.outerWidth(), - height: wrapper.outerHeight(), - top: wrapper.css("top"), - left: wrapper.css("left") - }).insertAfter( wrapper ); - wrapper.remove(); - } - - this.originalElement.css("resize", this.originalResizeStyle); - _destroy(this.originalElement); - - return this; - }, - - _mouseCapture: function(event) { - var i, handle, - capture = false; - - for (i in this.handles) { - handle = $(this.handles[i])[0]; - if (handle === event.target || $.contains(handle, event.target)) { - capture = true; - } - } - - return !this.options.disabled && capture; - }, - - _mouseStart: function(event) { - - var curleft, curtop, cursor, - o = this.options, - iniPos = this.element.position(), - el = this.element; - - this.resizing = true; - - // bugfix for http://dev.jquery.com/ticket/1749 - if ( (/absolute/).test( el.css("position") ) ) { - el.css({ position: "absolute", top: el.css("top"), left: el.css("left") }); - } else if (el.is(".ui-draggable")) { - el.css({ position: "absolute", top: iniPos.top, left: iniPos.left }); - } - - this._renderProxy(); - - curleft = num(this.helper.css("left")); - curtop = num(this.helper.css("top")); - - if (o.containment) { - curleft += $(o.containment).scrollLeft() || 0; - curtop += $(o.containment).scrollTop() || 0; - } - - //Store needed variables - this.offset = this.helper.offset(); - this.position = { left: curleft, top: curtop }; - this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; - this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; - this.originalPosition = { left: curleft, top: curtop }; - this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() }; - this.originalMousePosition = { left: event.pageX, top: event.pageY }; - - //Aspect Ratio - this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1); - - cursor = $(".ui-resizable-" + this.axis).css("cursor"); - $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor); - - el.addClass("ui-resizable-resizing"); - this._propagate("start", event); - return true; - }, - - _mouseDrag: function(event) { - - //Increase performance, avoid regex - var data, - el = this.helper, props = {}, - smp = this.originalMousePosition, - a = this.axis, - prevTop = this.position.top, - prevLeft = this.position.left, - prevWidth = this.size.width, - prevHeight = this.size.height, - dx = (event.pageX-smp.left)||0, - dy = (event.pageY-smp.top)||0, - trigger = this._change[a]; - - if (!trigger) { - return false; - } - - // Calculate the attrs that will be change - data = trigger.apply(this, [event, dx, dy]); - - // Put this in the mouseDrag handler since the user can start pressing shift while resizing - this._updateVirtualBoundaries(event.shiftKey); - if (this._aspectRatio || event.shiftKey) { - data = this._updateRatio(data, event); - } - - data = this._respectSize(data, event); - - this._updateCache(data); - - // plugins callbacks need to be called first - this._propagate("resize", event); - - if (this.position.top !== prevTop) { - props.top = this.position.top + "px"; - } - if (this.position.left !== prevLeft) { - props.left = this.position.left + "px"; - } - if (this.size.width !== prevWidth) { - props.width = this.size.width + "px"; - } - if (this.size.height !== prevHeight) { - props.height = this.size.height + "px"; - } - el.css(props); - - if (!this._helper && this._proportionallyResizeElements.length) { - this._proportionallyResize(); - } - - // Call the user callback if the element was resized - if ( ! $.isEmptyObject(props) ) { - this._trigger("resize", event, this.ui()); - } - - return false; - }, - - _mouseStop: function(event) { - - this.resizing = false; - var pr, ista, soffseth, soffsetw, s, left, top, - o = this.options, that = this; - - if(this._helper) { - - pr = this._proportionallyResizeElements; - ista = pr.length && (/textarea/i).test(pr[0].nodeName); - soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height; - soffsetw = ista ? 0 : that.sizeDiff.width; - - s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) }; - left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null; - top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null; - - if (!o.animate) { - this.element.css($.extend(s, { top: top, left: left })); - } - - that.helper.height(that.size.height); - that.helper.width(that.size.width); - - if (this._helper && !o.animate) { - this._proportionallyResize(); - } - } - - $("body").css("cursor", "auto"); - - this.element.removeClass("ui-resizable-resizing"); - - this._propagate("stop", event); - - if (this._helper) { - this.helper.remove(); - } - - return false; - - }, - - _updateVirtualBoundaries: function(forceAspectRatio) { - var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b, - o = this.options; - - b = { - minWidth: isNumber(o.minWidth) ? o.minWidth : 0, - maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity, - minHeight: isNumber(o.minHeight) ? o.minHeight : 0, - maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity - }; - - if(this._aspectRatio || forceAspectRatio) { - // We want to create an enclosing box whose aspect ration is the requested one - // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension - pMinWidth = b.minHeight * this.aspectRatio; - pMinHeight = b.minWidth / this.aspectRatio; - pMaxWidth = b.maxHeight * this.aspectRatio; - pMaxHeight = b.maxWidth / this.aspectRatio; - - if(pMinWidth > b.minWidth) { - b.minWidth = pMinWidth; - } - if(pMinHeight > b.minHeight) { - b.minHeight = pMinHeight; - } - if(pMaxWidth < b.maxWidth) { - b.maxWidth = pMaxWidth; - } - if(pMaxHeight < b.maxHeight) { - b.maxHeight = pMaxHeight; - } - } - this._vBoundaries = b; - }, - - _updateCache: function(data) { - this.offset = this.helper.offset(); - if (isNumber(data.left)) { - this.position.left = data.left; - } - if (isNumber(data.top)) { - this.position.top = data.top; - } - if (isNumber(data.height)) { - this.size.height = data.height; - } - if (isNumber(data.width)) { - this.size.width = data.width; - } - }, - - _updateRatio: function( data ) { - - var cpos = this.position, - csize = this.size, - a = this.axis; - - if (isNumber(data.height)) { - data.width = (data.height * this.aspectRatio); - } else if (isNumber(data.width)) { - data.height = (data.width / this.aspectRatio); - } - - if (a === "sw") { - data.left = cpos.left + (csize.width - data.width); - data.top = null; - } - if (a === "nw") { - data.top = cpos.top + (csize.height - data.height); - data.left = cpos.left + (csize.width - data.width); - } - - return data; - }, - - _respectSize: function( data ) { - - var o = this._vBoundaries, - a = this.axis, - ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height), - isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height), - dw = this.originalPosition.left + this.originalSize.width, - dh = this.position.top + this.size.height, - cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a); - if (isminw) { - data.width = o.minWidth; - } - if (isminh) { - data.height = o.minHeight; - } - if (ismaxw) { - data.width = o.maxWidth; - } - if (ismaxh) { - data.height = o.maxHeight; - } - - if (isminw && cw) { - data.left = dw - o.minWidth; - } - if (ismaxw && cw) { - data.left = dw - o.maxWidth; - } - if (isminh && ch) { - data.top = dh - o.minHeight; - } - if (ismaxh && ch) { - data.top = dh - o.maxHeight; - } - - // fixing jump error on top/left - bug #2330 - if (!data.width && !data.height && !data.left && data.top) { - data.top = null; - } else if (!data.width && !data.height && !data.top && data.left) { - data.left = null; - } - - return data; - }, - - _proportionallyResize: function() { - - if (!this._proportionallyResizeElements.length) { - return; - } - - var i, j, borders, paddings, prel, - element = this.helper || this.element; - - for ( i=0; i < this._proportionallyResizeElements.length; i++) { - - prel = this._proportionallyResizeElements[i]; - - if (!this.borderDif) { - this.borderDif = []; - borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")]; - paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")]; - - for ( j = 0; j < borders.length; j++ ) { - this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 ); - } - } - - prel.css({ - height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0, - width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0 - }); - - } - - }, - - _renderProxy: function() { - - var el = this.element, o = this.options; - this.elementOffset = el.offset(); - - if(this._helper) { - - this.helper = this.helper || $("
"); - - this.helper.addClass(this._helper).css({ - width: this.element.outerWidth() - 1, - height: this.element.outerHeight() - 1, - position: "absolute", - left: this.elementOffset.left +"px", - top: this.elementOffset.top +"px", - zIndex: ++o.zIndex //TODO: Don't modify option - }); - - this.helper - .appendTo("body") - .disableSelection(); - - } else { - this.helper = this.element; - } - - }, - - _change: { - e: function(event, dx) { - return { width: this.originalSize.width + dx }; - }, - w: function(event, dx) { - var cs = this.originalSize, sp = this.originalPosition; - return { left: sp.left + dx, width: cs.width - dx }; - }, - n: function(event, dx, dy) { - var cs = this.originalSize, sp = this.originalPosition; - return { top: sp.top + dy, height: cs.height - dy }; - }, - s: function(event, dx, dy) { - return { height: this.originalSize.height + dy }; - }, - se: function(event, dx, dy) { - return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); - }, - sw: function(event, dx, dy) { - return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); - }, - ne: function(event, dx, dy) { - return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); - }, - nw: function(event, dx, dy) { - return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); - } - }, - - _propagate: function(n, event) { - $.ui.plugin.call(this, n, [event, this.ui()]); - (n !== "resize" && this._trigger(n, event, this.ui())); - }, - - plugins: {}, - - ui: function() { - return { - originalElement: this.originalElement, - element: this.element, - helper: this.helper, - position: this.position, - size: this.size, - originalSize: this.originalSize, - originalPosition: this.originalPosition - }; - } - -}); - -/* - * Resizable Extensions - */ - -$.ui.plugin.add("resizable", "animate", { - - stop: function( event ) { - var that = $(this).data("ui-resizable"), - o = that.options, - pr = that._proportionallyResizeElements, - ista = pr.length && (/textarea/i).test(pr[0].nodeName), - soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height, - soffsetw = ista ? 0 : that.sizeDiff.width, - style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) }, - left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null, - top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null; - - that.element.animate( - $.extend(style, top && left ? { top: top, left: left } : {}), { - duration: o.animateDuration, - easing: o.animateEasing, - step: function() { - - var data = { - width: parseInt(that.element.css("width"), 10), - height: parseInt(that.element.css("height"), 10), - top: parseInt(that.element.css("top"), 10), - left: parseInt(that.element.css("left"), 10) - }; - - if (pr && pr.length) { - $(pr[0]).css({ width: data.width, height: data.height }); - } - - // propagating resize, and updating values for each animation step - that._updateCache(data); - that._propagate("resize", event); - - } - } - ); - } - -}); - -$.ui.plugin.add("resizable", "containment", { - - start: function() { - var element, p, co, ch, cw, width, height, - that = $(this).data("ui-resizable"), - o = that.options, - el = that.element, - oc = o.containment, - ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc; - - if (!ce) { - return; - } - - that.containerElement = $(ce); - - if (/document/.test(oc) || oc === document) { - that.containerOffset = { left: 0, top: 0 }; - that.containerPosition = { left: 0, top: 0 }; - - that.parentData = { - element: $(document), left: 0, top: 0, - width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight - }; - } - - // i'm a node, so compute top, left, right, bottom - else { - element = $(ce); - p = []; - $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); }); - - that.containerOffset = element.offset(); - that.containerPosition = element.position(); - that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) }; - - co = that.containerOffset; - ch = that.containerSize.height; - cw = that.containerSize.width; - width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ); - height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch); - - that.parentData = { - element: ce, left: co.left, top: co.top, width: width, height: height - }; - } - }, - - resize: function( event ) { - var woset, hoset, isParent, isOffsetRelative, - that = $(this).data("ui-resizable"), - o = that.options, - co = that.containerOffset, cp = that.position, - pRatio = that._aspectRatio || event.shiftKey, - cop = { top:0, left:0 }, ce = that.containerElement; - - if (ce[0] !== document && (/static/).test(ce.css("position"))) { - cop = co; - } - - if (cp.left < (that._helper ? co.left : 0)) { - that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left)); - if (pRatio) { - that.size.height = that.size.width / that.aspectRatio; - } - that.position.left = o.helper ? co.left : 0; - } - - if (cp.top < (that._helper ? co.top : 0)) { - that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top); - if (pRatio) { - that.size.width = that.size.height * that.aspectRatio; - } - that.position.top = that._helper ? co.top : 0; - } - - that.offset.left = that.parentData.left+that.position.left; - that.offset.top = that.parentData.top+that.position.top; - - woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width ); - hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height ); - - isParent = that.containerElement.get(0) === that.element.parent().get(0); - isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position")); - - if(isParent && isOffsetRelative) { - woset -= that.parentData.left; - } - - if (woset + that.size.width >= that.parentData.width) { - that.size.width = that.parentData.width - woset; - if (pRatio) { - that.size.height = that.size.width / that.aspectRatio; - } - } - - if (hoset + that.size.height >= that.parentData.height) { - that.size.height = that.parentData.height - hoset; - if (pRatio) { - that.size.width = that.size.height * that.aspectRatio; - } - } - }, - - stop: function(){ - var that = $(this).data("ui-resizable"), - o = that.options, - co = that.containerOffset, - cop = that.containerPosition, - ce = that.containerElement, - helper = $(that.helper), - ho = helper.offset(), - w = helper.outerWidth() - that.sizeDiff.width, - h = helper.outerHeight() - that.sizeDiff.height; - - if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) { - $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); - } - - if (that._helper && !o.animate && (/static/).test(ce.css("position"))) { - $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); - } - - } -}); - -$.ui.plugin.add("resizable", "alsoResize", { - - start: function () { - var that = $(this).data("ui-resizable"), - o = that.options, - _store = function (exp) { - $(exp).each(function() { - var el = $(this); - el.data("ui-resizable-alsoresize", { - width: parseInt(el.width(), 10), height: parseInt(el.height(), 10), - left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10) - }); - }); - }; - - if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) { - if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); } - else { $.each(o.alsoResize, function (exp) { _store(exp); }); } - }else{ - _store(o.alsoResize); - } - }, - - resize: function (event, ui) { - var that = $(this).data("ui-resizable"), - o = that.options, - os = that.originalSize, - op = that.originalPosition, - delta = { - height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0, - top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0 - }, - - _alsoResize = function (exp, c) { - $(exp).each(function() { - var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {}, - css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"]; - - $.each(css, function (i, prop) { - var sum = (start[prop]||0) + (delta[prop]||0); - if (sum && sum >= 0) { - style[prop] = sum || null; - } - }); - - el.css(style); - }); - }; - - if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) { - $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); }); - }else{ - _alsoResize(o.alsoResize); - } - }, - - stop: function () { - $(this).removeData("resizable-alsoresize"); - } -}); - -$.ui.plugin.add("resizable", "ghost", { - - start: function() { - - var that = $(this).data("ui-resizable"), o = that.options, cs = that.size; - - that.ghost = that.originalElement.clone(); - that.ghost - .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 }) - .addClass("ui-resizable-ghost") - .addClass(typeof o.ghost === "string" ? o.ghost : ""); - - that.ghost.appendTo(that.helper); - - }, - - resize: function(){ - var that = $(this).data("ui-resizable"); - if (that.ghost) { - that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width }); - } - }, - - stop: function() { - var that = $(this).data("ui-resizable"); - if (that.ghost && that.helper) { - that.helper.get(0).removeChild(that.ghost.get(0)); - } - } - -}); - -$.ui.plugin.add("resizable", "grid", { - - resize: function() { - var that = $(this).data("ui-resizable"), - o = that.options, - cs = that.size, - os = that.originalSize, - op = that.originalPosition, - a = that.axis, - grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid, - gridX = (grid[0]||1), - gridY = (grid[1]||1), - ox = Math.round((cs.width - os.width) / gridX) * gridX, - oy = Math.round((cs.height - os.height) / gridY) * gridY, - newWidth = os.width + ox, - newHeight = os.height + oy, - isMaxWidth = o.maxWidth && (o.maxWidth < newWidth), - isMaxHeight = o.maxHeight && (o.maxHeight < newHeight), - isMinWidth = o.minWidth && (o.minWidth > newWidth), - isMinHeight = o.minHeight && (o.minHeight > newHeight); - - o.grid = grid; - - if (isMinWidth) { - newWidth = newWidth + gridX; - } - if (isMinHeight) { - newHeight = newHeight + gridY; - } - if (isMaxWidth) { - newWidth = newWidth - gridX; - } - if (isMaxHeight) { - newHeight = newHeight - gridY; - } - - if (/^(se|s|e)$/.test(a)) { - that.size.width = newWidth; - that.size.height = newHeight; - } else if (/^(ne)$/.test(a)) { - that.size.width = newWidth; - that.size.height = newHeight; - that.position.top = op.top - oy; - } else if (/^(sw)$/.test(a)) { - that.size.width = newWidth; - that.size.height = newHeight; - that.position.left = op.left - ox; - } else { - that.size.width = newWidth; - that.size.height = newHeight; - that.position.top = op.top - oy; - that.position.left = op.left - ox; - } - } - -}); - -})(jQuery); -(function( $, undefined ) { - -$.widget("ui.selectable", $.ui.mouse, { - version: "1.10.3", - options: { - appendTo: "body", - autoRefresh: true, - distance: 0, - filter: "*", - tolerance: "touch", - - // callbacks - selected: null, - selecting: null, - start: null, - stop: null, - unselected: null, - unselecting: null - }, - _create: function() { - var selectees, - that = this; - - this.element.addClass("ui-selectable"); - - this.dragged = false; - - // cache selectee children based on filter - this.refresh = function() { - selectees = $(that.options.filter, that.element[0]); - selectees.addClass("ui-selectee"); - selectees.each(function() { - var $this = $(this), - pos = $this.offset(); - $.data(this, "selectable-item", { - element: this, - $element: $this, - left: pos.left, - top: pos.top, - right: pos.left + $this.outerWidth(), - bottom: pos.top + $this.outerHeight(), - startselected: false, - selected: $this.hasClass("ui-selected"), - selecting: $this.hasClass("ui-selecting"), - unselecting: $this.hasClass("ui-unselecting") - }); - }); - }; - this.refresh(); - - this.selectees = selectees.addClass("ui-selectee"); - - this._mouseInit(); - - this.helper = $("
"); - }, - - _destroy: function() { - this.selectees - .removeClass("ui-selectee") - .removeData("selectable-item"); - this.element - .removeClass("ui-selectable ui-selectable-disabled"); - this._mouseDestroy(); - }, - - _mouseStart: function(event) { - var that = this, - options = this.options; - - this.opos = [event.pageX, event.pageY]; - - if (this.options.disabled) { - return; - } - - this.selectees = $(options.filter, this.element[0]); - - this._trigger("start", event); - - $(options.appendTo).append(this.helper); - // position helper (lasso) - this.helper.css({ - "left": event.pageX, - "top": event.pageY, - "width": 0, - "height": 0 - }); - - if (options.autoRefresh) { - this.refresh(); - } - - this.selectees.filter(".ui-selected").each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.startselected = true; - if (!event.metaKey && !event.ctrlKey) { - selectee.$element.removeClass("ui-selected"); - selectee.selected = false; - selectee.$element.addClass("ui-unselecting"); - selectee.unselecting = true; - // selectable UNSELECTING callback - that._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - }); - - $(event.target).parents().addBack().each(function() { - var doSelect, - selectee = $.data(this, "selectable-item"); - if (selectee) { - doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected"); - selectee.$element - .removeClass(doSelect ? "ui-unselecting" : "ui-selected") - .addClass(doSelect ? "ui-selecting" : "ui-unselecting"); - selectee.unselecting = !doSelect; - selectee.selecting = doSelect; - selectee.selected = doSelect; - // selectable (UN)SELECTING callback - if (doSelect) { - that._trigger("selecting", event, { - selecting: selectee.element - }); - } else { - that._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - return false; - } - }); - - }, - - _mouseDrag: function(event) { - - this.dragged = true; - - if (this.options.disabled) { - return; - } - - var tmp, - that = this, - options = this.options, - x1 = this.opos[0], - y1 = this.opos[1], - x2 = event.pageX, - y2 = event.pageY; - - if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; } - if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; } - this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1}); - - this.selectees.each(function() { - var selectee = $.data(this, "selectable-item"), - hit = false; - - //prevent helper from being selected if appendTo: selectable - if (!selectee || selectee.element === that.element[0]) { - return; - } - - if (options.tolerance === "touch") { - hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) ); - } else if (options.tolerance === "fit") { - hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2); - } - - if (hit) { - // SELECT - if (selectee.selected) { - selectee.$element.removeClass("ui-selected"); - selectee.selected = false; - } - if (selectee.unselecting) { - selectee.$element.removeClass("ui-unselecting"); - selectee.unselecting = false; - } - if (!selectee.selecting) { - selectee.$element.addClass("ui-selecting"); - selectee.selecting = true; - // selectable SELECTING callback - that._trigger("selecting", event, { - selecting: selectee.element - }); - } - } else { - // UNSELECT - if (selectee.selecting) { - if ((event.metaKey || event.ctrlKey) && selectee.startselected) { - selectee.$element.removeClass("ui-selecting"); - selectee.selecting = false; - selectee.$element.addClass("ui-selected"); - selectee.selected = true; - } else { - selectee.$element.removeClass("ui-selecting"); - selectee.selecting = false; - if (selectee.startselected) { - selectee.$element.addClass("ui-unselecting"); - selectee.unselecting = true; - } - // selectable UNSELECTING callback - that._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - } - if (selectee.selected) { - if (!event.metaKey && !event.ctrlKey && !selectee.startselected) { - selectee.$element.removeClass("ui-selected"); - selectee.selected = false; - - selectee.$element.addClass("ui-unselecting"); - selectee.unselecting = true; - // selectable UNSELECTING callback - that._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - } - } - }); - - return false; - }, - - _mouseStop: function(event) { - var that = this; - - this.dragged = false; - - $(".ui-unselecting", this.element[0]).each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.$element.removeClass("ui-unselecting"); - selectee.unselecting = false; - selectee.startselected = false; - that._trigger("unselected", event, { - unselected: selectee.element - }); - }); - $(".ui-selecting", this.element[0]).each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.$element.removeClass("ui-selecting").addClass("ui-selected"); - selectee.selecting = false; - selectee.selected = true; - selectee.startselected = true; - that._trigger("selected", event, { - selected: selectee.element - }); - }); - this._trigger("stop", event); - - this.helper.remove(); - - return false; - } - -}); - -})(jQuery); -(function( $, undefined ) { - -/*jshint loopfunc: true */ - -function isOverAxis( x, reference, size ) { - return ( x > reference ) && ( x < ( reference + size ) ); -} - -function isFloating(item) { - return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display")); -} - -$.widget("ui.sortable", $.ui.mouse, { - version: "1.10.3", - widgetEventPrefix: "sort", - ready: false, - options: { - appendTo: "parent", - axis: false, - connectWith: false, - containment: false, - cursor: "auto", - cursorAt: false, - dropOnEmpty: true, - forcePlaceholderSize: false, - forceHelperSize: false, - grid: false, - handle: false, - helper: "original", - items: "> *", - opacity: false, - placeholder: false, - revert: false, - scroll: true, - scrollSensitivity: 20, - scrollSpeed: 20, - scope: "default", - tolerance: "intersect", - zIndex: 1000, - - // callbacks - activate: null, - beforeStop: null, - change: null, - deactivate: null, - out: null, - over: null, - receive: null, - remove: null, - sort: null, - start: null, - stop: null, - update: null - }, - _create: function() { - - var o = this.options; - this.containerCache = {}; - this.element.addClass("ui-sortable"); - - //Get the items - this.refresh(); - - //Let's determine if the items are being displayed horizontally - this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false; - - //Let's determine the parent's offset - this.offset = this.element.offset(); - - //Initialize mouse events for interaction - this._mouseInit(); - - //We're ready to go - this.ready = true; - - }, - - _destroy: function() { - this.element - .removeClass("ui-sortable ui-sortable-disabled"); - this._mouseDestroy(); - - for ( var i = this.items.length - 1; i >= 0; i-- ) { - this.items[i].item.removeData(this.widgetName + "-item"); - } - - return this; - }, - - _setOption: function(key, value){ - if ( key === "disabled" ) { - this.options[ key ] = value; - - this.widget().toggleClass( "ui-sortable-disabled", !!value ); - } else { - // Don't call widget base _setOption for disable as it adds ui-state-disabled class - $.Widget.prototype._setOption.apply(this, arguments); - } - }, - - _mouseCapture: function(event, overrideHandle) { - var currentItem = null, - validHandle = false, - that = this; - - if (this.reverting) { - return false; - } - - if(this.options.disabled || this.options.type === "static") { - return false; - } - - //We have to refresh the items data once first - this._refreshItems(event); - - //Find out if the clicked node (or one of its parents) is a actual item in this.items - $(event.target).parents().each(function() { - if($.data(this, that.widgetName + "-item") === that) { - currentItem = $(this); - return false; - } - }); - if($.data(event.target, that.widgetName + "-item") === that) { - currentItem = $(event.target); - } - - if(!currentItem) { - return false; - } - if(this.options.handle && !overrideHandle) { - $(this.options.handle, currentItem).find("*").addBack().each(function() { - if(this === event.target) { - validHandle = true; - } - }); - if(!validHandle) { - return false; - } - } - - this.currentItem = currentItem; - this._removeCurrentsFromItems(); - return true; - - }, - - _mouseStart: function(event, overrideHandle, noActivation) { - - var i, body, - o = this.options; - - this.currentContainer = this; - - //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture - this.refreshPositions(); - - //Create and append the visible helper - this.helper = this._createHelper(event); - - //Cache the helper size - this._cacheHelperProportions(); - - /* - * - Position generation - - * This block generates everything position related - it's the core of draggables. - */ - - //Cache the margins of the original element - this._cacheMargins(); - - //Get the next scrolling parent - this.scrollParent = this.helper.scrollParent(); - - //The element's absolute position on the page minus margins - this.offset = this.currentItem.offset(); - this.offset = { - top: this.offset.top - this.margins.top, - left: this.offset.left - this.margins.left - }; - - $.extend(this.offset, { - click: { //Where the click happened, relative to the element - left: event.pageX - this.offset.left, - top: event.pageY - this.offset.top - }, - parent: this._getParentOffset(), - relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper - }); - - // Only after we got the offset, we can change the helper's position to absolute - // TODO: Still need to figure out a way to make relative sorting possible - this.helper.css("position", "absolute"); - this.cssPosition = this.helper.css("position"); - - //Generate the original position - this.originalPosition = this._generatePosition(event); - this.originalPageX = event.pageX; - this.originalPageY = event.pageY; - - //Adjust the mouse offset relative to the helper if "cursorAt" is supplied - (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); - - //Cache the former DOM position - this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] }; - - //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way - if(this.helper[0] !== this.currentItem[0]) { - this.currentItem.hide(); - } - - //Create the placeholder - this._createPlaceholder(); - - //Set a containment if given in the options - if(o.containment) { - this._setContainment(); - } - - if( o.cursor && o.cursor !== "auto" ) { // cursor option - body = this.document.find( "body" ); - - // support: IE - this.storedCursor = body.css( "cursor" ); - body.css( "cursor", o.cursor ); - - this.storedStylesheet = $( "" ).appendTo( body ); - } - - if(o.opacity) { // opacity option - if (this.helper.css("opacity")) { - this._storedOpacity = this.helper.css("opacity"); - } - this.helper.css("opacity", o.opacity); - } - - if(o.zIndex) { // zIndex option - if (this.helper.css("zIndex")) { - this._storedZIndex = this.helper.css("zIndex"); - } - this.helper.css("zIndex", o.zIndex); - } - - //Prepare scrolling - if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { - this.overflowOffset = this.scrollParent.offset(); - } - - //Call callbacks - this._trigger("start", event, this._uiHash()); - - //Recache the helper size - if(!this._preserveHelperProportions) { - this._cacheHelperProportions(); - } - - - //Post "activate" events to possible containers - if( !noActivation ) { - for ( i = this.containers.length - 1; i >= 0; i-- ) { - this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) ); - } - } - - //Prepare possible droppables - if($.ui.ddmanager) { - $.ui.ddmanager.current = this; - } - - if ($.ui.ddmanager && !o.dropBehaviour) { - $.ui.ddmanager.prepareOffsets(this, event); - } - - this.dragging = true; - - this.helper.addClass("ui-sortable-helper"); - this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position - return true; - - }, - - _mouseDrag: function(event) { - var i, item, itemElement, intersection, - o = this.options, - scrolled = false; - - //Compute the helpers position - this.position = this._generatePosition(event); - this.positionAbs = this._convertPositionTo("absolute"); - - if (!this.lastPositionAbs) { - this.lastPositionAbs = this.positionAbs; - } - - //Do scrolling - if(this.options.scroll) { - if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { - - if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) { - this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed; - } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) { - this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed; - } - - if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) { - this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed; - } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) { - this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed; - } - - } else { - - if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) { - scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); - } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) { - scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); - } - - if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) { - scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); - } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) { - scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); - } - - } - - if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) { - $.ui.ddmanager.prepareOffsets(this, event); - } - } - - //Regenerate the absolute position used for position checks - this.positionAbs = this._convertPositionTo("absolute"); - - //Set the helper position - if(!this.options.axis || this.options.axis !== "y") { - this.helper[0].style.left = this.position.left+"px"; - } - if(!this.options.axis || this.options.axis !== "x") { - this.helper[0].style.top = this.position.top+"px"; - } - - //Rearrange - for (i = this.items.length - 1; i >= 0; i--) { - - //Cache variables and intersection, continue if no intersection - item = this.items[i]; - itemElement = item.item[0]; - intersection = this._intersectsWithPointer(item); - if (!intersection) { - continue; - } - - // Only put the placeholder inside the current Container, skip all - // items form other containers. This works because when moving - // an item from one container to another the - // currentContainer is switched before the placeholder is moved. - // - // Without this moving items in "sub-sortables" can cause the placeholder to jitter - // beetween the outer and inner container. - if (item.instance !== this.currentContainer) { - continue; - } - - // cannot intersect with itself - // no useless actions that have been done before - // no action if the item moved is the parent of the item checked - if (itemElement !== this.currentItem[0] && - this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement && - !$.contains(this.placeholder[0], itemElement) && - (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true) - ) { - - this.direction = intersection === 1 ? "down" : "up"; - - if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) { - this._rearrange(event, item); - } else { - break; - } - - this._trigger("change", event, this._uiHash()); - break; - } - } - - //Post events to containers - this._contactContainers(event); - - //Interconnect with droppables - if($.ui.ddmanager) { - $.ui.ddmanager.drag(this, event); - } - - //Call callbacks - this._trigger("sort", event, this._uiHash()); - - this.lastPositionAbs = this.positionAbs; - return false; - - }, - - _mouseStop: function(event, noPropagation) { - - if(!event) { - return; - } - - //If we are using droppables, inform the manager about the drop - if ($.ui.ddmanager && !this.options.dropBehaviour) { - $.ui.ddmanager.drop(this, event); - } - - if(this.options.revert) { - var that = this, - cur = this.placeholder.offset(), - axis = this.options.axis, - animation = {}; - - if ( !axis || axis === "x" ) { - animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft); - } - if ( !axis || axis === "y" ) { - animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop); - } - this.reverting = true; - $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() { - that._clear(event); - }); - } else { - this._clear(event, noPropagation); - } - - return false; - - }, - - cancel: function() { - - if(this.dragging) { - - this._mouseUp({ target: null }); - - if(this.options.helper === "original") { - this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); - } else { - this.currentItem.show(); - } - - //Post deactivating events to containers - for (var i = this.containers.length - 1; i >= 0; i--){ - this.containers[i]._trigger("deactivate", null, this._uiHash(this)); - if(this.containers[i].containerCache.over) { - this.containers[i]._trigger("out", null, this._uiHash(this)); - this.containers[i].containerCache.over = 0; - } - } - - } - - if (this.placeholder) { - //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! - if(this.placeholder[0].parentNode) { - this.placeholder[0].parentNode.removeChild(this.placeholder[0]); - } - if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) { - this.helper.remove(); - } - - $.extend(this, { - helper: null, - dragging: false, - reverting: false, - _noFinalSort: null - }); - - if(this.domPosition.prev) { - $(this.domPosition.prev).after(this.currentItem); - } else { - $(this.domPosition.parent).prepend(this.currentItem); - } - } - - return this; - - }, - - serialize: function(o) { - - var items = this._getItemsAsjQuery(o && o.connected), - str = []; - o = o || {}; - - $(items).each(function() { - var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/)); - if (res) { - str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2])); - } - }); - - if(!str.length && o.key) { - str.push(o.key + "="); - } - - return str.join("&"); - - }, - - toArray: function(o) { - - var items = this._getItemsAsjQuery(o && o.connected), - ret = []; - - o = o || {}; - - items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); }); - return ret; - - }, - - /* Be careful with the following core functions */ - _intersectsWith: function(item) { - - var x1 = this.positionAbs.left, - x2 = x1 + this.helperProportions.width, - y1 = this.positionAbs.top, - y2 = y1 + this.helperProportions.height, - l = item.left, - r = l + item.width, - t = item.top, - b = t + item.height, - dyClick = this.offset.click.top, - dxClick = this.offset.click.left, - isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ), - isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ), - isOverElement = isOverElementHeight && isOverElementWidth; - - if ( this.options.tolerance === "pointer" || - this.options.forcePointerForContainers || - (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"]) - ) { - return isOverElement; - } else { - - return (l < x1 + (this.helperProportions.width / 2) && // Right Half - x2 - (this.helperProportions.width / 2) < r && // Left Half - t < y1 + (this.helperProportions.height / 2) && // Bottom Half - y2 - (this.helperProportions.height / 2) < b ); // Top Half - - } - }, - - _intersectsWithPointer: function(item) { - - var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height), - isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width), - isOverElement = isOverElementHeight && isOverElementWidth, - verticalDirection = this._getDragVerticalDirection(), - horizontalDirection = this._getDragHorizontalDirection(); - - if (!isOverElement) { - return false; - } - - return this.floating ? - ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 ) - : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) ); - - }, - - _intersectsWithSides: function(item) { - - var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height), - isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width), - verticalDirection = this._getDragVerticalDirection(), - horizontalDirection = this._getDragHorizontalDirection(); - - if (this.floating && horizontalDirection) { - return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf)); - } else { - return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf)); - } - - }, - - _getDragVerticalDirection: function() { - var delta = this.positionAbs.top - this.lastPositionAbs.top; - return delta !== 0 && (delta > 0 ? "down" : "up"); - }, - - _getDragHorizontalDirection: function() { - var delta = this.positionAbs.left - this.lastPositionAbs.left; - return delta !== 0 && (delta > 0 ? "right" : "left"); - }, - - refresh: function(event) { - this._refreshItems(event); - this.refreshPositions(); - return this; - }, - - _connectWith: function() { - var options = this.options; - return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith; - }, - - _getItemsAsjQuery: function(connected) { - - var i, j, cur, inst, - items = [], - queries = [], - connectWith = this._connectWith(); - - if(connectWith && connected) { - for (i = connectWith.length - 1; i >= 0; i--){ - cur = $(connectWith[i]); - for ( j = cur.length - 1; j >= 0; j--){ - inst = $.data(cur[j], this.widgetFullName); - if(inst && inst !== this && !inst.options.disabled) { - queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]); - } - } - } - } - - queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]); - - for (i = queries.length - 1; i >= 0; i--){ - queries[i][0].each(function() { - items.push(this); - }); - } - - return $(items); - - }, - - _removeCurrentsFromItems: function() { - - var list = this.currentItem.find(":data(" + this.widgetName + "-item)"); - - this.items = $.grep(this.items, function (item) { - for (var j=0; j < list.length; j++) { - if(list[j] === item.item[0]) { - return false; - } - } - return true; - }); - - }, - - _refreshItems: function(event) { - - this.items = []; - this.containers = [this]; - - var i, j, cur, inst, targetData, _queries, item, queriesLength, - items = this.items, - queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]], - connectWith = this._connectWith(); - - if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down - for (i = connectWith.length - 1; i >= 0; i--){ - cur = $(connectWith[i]); - for (j = cur.length - 1; j >= 0; j--){ - inst = $.data(cur[j], this.widgetFullName); - if(inst && inst !== this && !inst.options.disabled) { - queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]); - this.containers.push(inst); - } - } - } - } - - for (i = queries.length - 1; i >= 0; i--) { - targetData = queries[i][1]; - _queries = queries[i][0]; - - for (j=0, queriesLength = _queries.length; j < queriesLength; j++) { - item = $(_queries[j]); - - item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager) - - items.push({ - item: item, - instance: targetData, - width: 0, height: 0, - left: 0, top: 0 - }); - } - } - - }, - - refreshPositions: function(fast) { - - //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change - if(this.offsetParent && this.helper) { - this.offset.parent = this._getParentOffset(); - } - - var i, item, t, p; - - for (i = this.items.length - 1; i >= 0; i--){ - item = this.items[i]; - - //We ignore calculating positions of all connected containers when we're not over them - if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) { - continue; - } - - t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item; - - if (!fast) { - item.width = t.outerWidth(); - item.height = t.outerHeight(); - } - - p = t.offset(); - item.left = p.left; - item.top = p.top; - } - - if(this.options.custom && this.options.custom.refreshContainers) { - this.options.custom.refreshContainers.call(this); - } else { - for (i = this.containers.length - 1; i >= 0; i--){ - p = this.containers[i].element.offset(); - this.containers[i].containerCache.left = p.left; - this.containers[i].containerCache.top = p.top; - this.containers[i].containerCache.width = this.containers[i].element.outerWidth(); - this.containers[i].containerCache.height = this.containers[i].element.outerHeight(); - } - } - - return this; - }, - - _createPlaceholder: function(that) { - that = that || this; - var className, - o = that.options; - - if(!o.placeholder || o.placeholder.constructor === String) { - className = o.placeholder; - o.placeholder = { - element: function() { - - var nodeName = that.currentItem[0].nodeName.toLowerCase(), - element = $( "<" + nodeName + ">", that.document[0] ) - .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder") - .removeClass("ui-sortable-helper"); - - if ( nodeName === "tr" ) { - that.currentItem.children().each(function() { - $( " ", that.document[0] ) - .attr( "colspan", $( this ).attr( "colspan" ) || 1 ) - .appendTo( element ); - }); - } else if ( nodeName === "img" ) { - element.attr( "src", that.currentItem.attr( "src" ) ); - } - - if ( !className ) { - element.css( "visibility", "hidden" ); - } - - return element; - }, - update: function(container, p) { - - // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that - // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified - if(className && !o.forcePlaceholderSize) { - return; - } - - //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item - if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); } - if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); } - } - }; - } - - //Create the placeholder - that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem)); - - //Append it after the actual current item - that.currentItem.after(that.placeholder); - - //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317) - o.placeholder.update(that, that.placeholder); - - }, - - _contactContainers: function(event) { - var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating, - innermostContainer = null, - innermostIndex = null; - - // get innermost container that intersects with item - for (i = this.containers.length - 1; i >= 0; i--) { - - // never consider a container that's located within the item itself - if($.contains(this.currentItem[0], this.containers[i].element[0])) { - continue; - } - - if(this._intersectsWith(this.containers[i].containerCache)) { - - // if we've already found a container and it's more "inner" than this, then continue - if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) { - continue; - } - - innermostContainer = this.containers[i]; - innermostIndex = i; - - } else { - // container doesn't intersect. trigger "out" event if necessary - if(this.containers[i].containerCache.over) { - this.containers[i]._trigger("out", event, this._uiHash(this)); - this.containers[i].containerCache.over = 0; - } - } - - } - - // if no intersecting containers found, return - if(!innermostContainer) { - return; - } - - // move the item into the container if it's not there already - if(this.containers.length === 1) { - if (!this.containers[innermostIndex].containerCache.over) { - this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); - this.containers[innermostIndex].containerCache.over = 1; - } - } else { - - //When entering a new container, we will find the item with the least distance and append our item near it - dist = 10000; - itemWithLeastDistance = null; - floating = innermostContainer.floating || isFloating(this.currentItem); - posProperty = floating ? "left" : "top"; - sizeProperty = floating ? "width" : "height"; - base = this.positionAbs[posProperty] + this.offset.click[posProperty]; - for (j = this.items.length - 1; j >= 0; j--) { - if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) { - continue; - } - if(this.items[j].item[0] === this.currentItem[0]) { - continue; - } - if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) { - continue; - } - cur = this.items[j].item.offset()[posProperty]; - nearBottom = false; - if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){ - nearBottom = true; - cur += this.items[j][sizeProperty]; - } - - if(Math.abs(cur - base) < dist) { - dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j]; - this.direction = nearBottom ? "up": "down"; - } - } - - //Check if dropOnEmpty is enabled - if(!itemWithLeastDistance && !this.options.dropOnEmpty) { - return; - } - - if(this.currentContainer === this.containers[innermostIndex]) { - return; - } - - itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true); - this._trigger("change", event, this._uiHash()); - this.containers[innermostIndex]._trigger("change", event, this._uiHash(this)); - this.currentContainer = this.containers[innermostIndex]; - - //Update the placeholder - this.options.placeholder.update(this.currentContainer, this.placeholder); - - this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); - this.containers[innermostIndex].containerCache.over = 1; - } - - - }, - - _createHelper: function(event) { - - var o = this.options, - helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem); - - //Add the helper to the DOM if that didn't happen already - if(!helper.parents("body").length) { - $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]); - } - - if(helper[0] === this.currentItem[0]) { - this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") }; - } - - if(!helper[0].style.width || o.forceHelperSize) { - helper.width(this.currentItem.width()); - } - if(!helper[0].style.height || o.forceHelperSize) { - helper.height(this.currentItem.height()); - } - - return helper; - - }, - - _adjustOffsetFromHelper: function(obj) { - if (typeof obj === "string") { - obj = obj.split(" "); - } - if ($.isArray(obj)) { - obj = {left: +obj[0], top: +obj[1] || 0}; - } - if ("left" in obj) { - this.offset.click.left = obj.left + this.margins.left; - } - if ("right" in obj) { - this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; - } - if ("top" in obj) { - this.offset.click.top = obj.top + this.margins.top; - } - if ("bottom" in obj) { - this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; - } - }, - - _getParentOffset: function() { - - - //Get the offsetParent and cache its position - this.offsetParent = this.helper.offsetParent(); - var po = this.offsetParent.offset(); - - // This is a special case where we need to modify a offset calculated on start, since the following happened: - // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent - // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that - // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag - if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) { - po.left += this.scrollParent.scrollLeft(); - po.top += this.scrollParent.scrollTop(); - } - - // This needs to be actually done for all browsers, since pageX/pageY includes this information - // with an ugly IE fix - if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) { - po = { top: 0, left: 0 }; - } - - return { - top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), - left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) - }; - - }, - - _getRelativeOffset: function() { - - if(this.cssPosition === "relative") { - var p = this.currentItem.position(); - return { - top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), - left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() - }; - } else { - return { top: 0, left: 0 }; - } - - }, - - _cacheMargins: function() { - this.margins = { - left: (parseInt(this.currentItem.css("marginLeft"),10) || 0), - top: (parseInt(this.currentItem.css("marginTop"),10) || 0) - }; - }, - - _cacheHelperProportions: function() { - this.helperProportions = { - width: this.helper.outerWidth(), - height: this.helper.outerHeight() - }; - }, - - _setContainment: function() { - - var ce, co, over, - o = this.options; - if(o.containment === "parent") { - o.containment = this.helper[0].parentNode; - } - if(o.containment === "document" || o.containment === "window") { - this.containment = [ - 0 - this.offset.relative.left - this.offset.parent.left, - 0 - this.offset.relative.top - this.offset.parent.top, - $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left, - ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top - ]; - } - - if(!(/^(document|window|parent)$/).test(o.containment)) { - ce = $(o.containment)[0]; - co = $(o.containment).offset(); - over = ($(ce).css("overflow") !== "hidden"); - - this.containment = [ - co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left, - co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top, - co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left, - co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - ]; - } - - }, - - _convertPositionTo: function(d, pos) { - - if(!pos) { - pos = this.position; - } - var mod = d === "absolute" ? 1 : -1, - scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, - scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); - - return { - top: ( - pos.top + // The absolute mouse position - this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) - ), - left: ( - pos.left + // The absolute mouse position - this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) - ) - }; - - }, - - _generatePosition: function(event) { - - var top, left, - o = this.options, - pageX = event.pageX, - pageY = event.pageY, - scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); - - // This is another very weird special case that only happens for relative elements: - // 1. If the css position is relative - // 2. and the scroll parent is the document or similar to the offset parent - // we have to refresh the relative offset during the scroll so there are no jumps - if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) { - this.offset.relative = this._getRelativeOffset(); - } - - /* - * - Position constraining - - * Constrain the position to a mix of grid, containment. - */ - - if(this.originalPosition) { //If we are not dragging yet, we won't check for options - - if(this.containment) { - if(event.pageX - this.offset.click.left < this.containment[0]) { - pageX = this.containment[0] + this.offset.click.left; - } - if(event.pageY - this.offset.click.top < this.containment[1]) { - pageY = this.containment[1] + this.offset.click.top; - } - if(event.pageX - this.offset.click.left > this.containment[2]) { - pageX = this.containment[2] + this.offset.click.left; - } - if(event.pageY - this.offset.click.top > this.containment[3]) { - pageY = this.containment[3] + this.offset.click.top; - } - } - - if(o.grid) { - top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1]; - pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; - - left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0]; - pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; - } - - } - - return { - top: ( - pageY - // The absolute mouse position - this.offset.click.top - // Click offset (relative to the element) - this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.top + // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) - ), - left: ( - pageX - // The absolute mouse position - this.offset.click.left - // Click offset (relative to the element) - this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.left + // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) - ) - }; - - }, - - _rearrange: function(event, i, a, hardRefresh) { - - a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling)); - - //Various things done here to improve the performance: - // 1. we create a setTimeout, that calls refreshPositions - // 2. on the instance, we have a counter variable, that get's higher after every append - // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same - // 4. this lets only the last addition to the timeout stack through - this.counter = this.counter ? ++this.counter : 1; - var counter = this.counter; - - this._delay(function() { - if(counter === this.counter) { - this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove - } - }); - - }, - - _clear: function(event, noPropagation) { - - this.reverting = false; - // We delay all events that have to be triggered to after the point where the placeholder has been removed and - // everything else normalized again - var i, - delayedTriggers = []; - - // We first have to update the dom position of the actual currentItem - // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088) - if(!this._noFinalSort && this.currentItem.parent().length) { - this.placeholder.before(this.currentItem); - } - this._noFinalSort = null; - - if(this.helper[0] === this.currentItem[0]) { - for(i in this._storedCSS) { - if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") { - this._storedCSS[i] = ""; - } - } - this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); - } else { - this.currentItem.show(); - } - - if(this.fromOutside && !noPropagation) { - delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); }); - } - if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) { - delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed - } - - // Check if the items Container has Changed and trigger appropriate - // events. - if (this !== this.currentContainer) { - if(!noPropagation) { - delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); }); - delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer)); - delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer)); - } - } - - - //Post events to containers - for (i = this.containers.length - 1; i >= 0; i--){ - if(!noPropagation) { - delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i])); - } - if(this.containers[i].containerCache.over) { - delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i])); - this.containers[i].containerCache.over = 0; - } - } - - //Do what was originally in plugins - if ( this.storedCursor ) { - this.document.find( "body" ).css( "cursor", this.storedCursor ); - this.storedStylesheet.remove(); - } - if(this._storedOpacity) { - this.helper.css("opacity", this._storedOpacity); - } - if(this._storedZIndex) { - this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex); - } - - this.dragging = false; - if(this.cancelHelperRemoval) { - if(!noPropagation) { - this._trigger("beforeStop", event, this._uiHash()); - for (i=0; i < delayedTriggers.length; i++) { - delayedTriggers[i].call(this, event); - } //Trigger all delayed events - this._trigger("stop", event, this._uiHash()); - } - - this.fromOutside = false; - return false; - } - - if(!noPropagation) { - this._trigger("beforeStop", event, this._uiHash()); - } - - //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! - this.placeholder[0].parentNode.removeChild(this.placeholder[0]); - - if(this.helper[0] !== this.currentItem[0]) { - this.helper.remove(); - } - this.helper = null; - - if(!noPropagation) { - for (i=0; i < delayedTriggers.length; i++) { - delayedTriggers[i].call(this, event); - } //Trigger all delayed events - this._trigger("stop", event, this._uiHash()); - } - - this.fromOutside = false; - return true; - - }, - - _trigger: function() { - if ($.Widget.prototype._trigger.apply(this, arguments) === false) { - this.cancel(); - } - }, - - _uiHash: function(_inst) { - var inst = _inst || this; - return { - helper: inst.helper, - placeholder: inst.placeholder || $([]), - position: inst.position, - originalPosition: inst.originalPosition, - offset: inst.positionAbs, - item: inst.currentItem, - sender: _inst ? _inst.element : null - }; - } - -}); - -})(jQuery); -(function( $, undefined ) { - -// used to prevent race conditions with remote data sources -var requestIndex = 0; - -$.widget( "ui.autocomplete", { - version: "1.10.3", - defaultElement: "", - options: { - appendTo: null, - autoFocus: false, - delay: 300, - minLength: 1, - position: { - my: "left top", - at: "left bottom", - collision: "none" - }, - source: null, - - // callbacks - change: null, - close: null, - focus: null, - open: null, - response: null, - search: null, - select: null - }, - - pending: 0, - - _create: function() { - // Some browsers only repeat keydown events, not keypress events, - // so we use the suppressKeyPress flag to determine if we've already - // handled the keydown event. #7269 - // Unfortunately the code for & in keypress is the same as the up arrow, - // so we use the suppressKeyPressRepeat flag to avoid handling keypress - // events when we know the keydown event was used to modify the - // search term. #7799 - var suppressKeyPress, suppressKeyPressRepeat, suppressInput, - nodeName = this.element[0].nodeName.toLowerCase(), - isTextarea = nodeName === "textarea", - isInput = nodeName === "input"; - - this.isMultiLine = - // Textareas are always multi-line - isTextarea ? true : - // Inputs are always single-line, even if inside a contentEditable element - // IE also treats inputs as contentEditable - isInput ? false : - // All other element types are determined by whether or not they're contentEditable - this.element.prop( "isContentEditable" ); - - this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ]; - this.isNewMenu = true; - - this.element - .addClass( "ui-autocomplete-input" ) - .attr( "autocomplete", "off" ); - - this._on( this.element, { - keydown: function( event ) { - /*jshint maxcomplexity:15*/ - if ( this.element.prop( "readOnly" ) ) { - suppressKeyPress = true; - suppressInput = true; - suppressKeyPressRepeat = true; - return; - } - - suppressKeyPress = false; - suppressInput = false; - suppressKeyPressRepeat = false; - var keyCode = $.ui.keyCode; - switch( event.keyCode ) { - case keyCode.PAGE_UP: - suppressKeyPress = true; - this._move( "previousPage", event ); - break; - case keyCode.PAGE_DOWN: - suppressKeyPress = true; - this._move( "nextPage", event ); - break; - case keyCode.UP: - suppressKeyPress = true; - this._keyEvent( "previous", event ); - break; - case keyCode.DOWN: - suppressKeyPress = true; - this._keyEvent( "next", event ); - break; - case keyCode.ENTER: - case keyCode.NUMPAD_ENTER: - // when menu is open and has focus - if ( this.menu.active ) { - // #6055 - Opera still allows the keypress to occur - // which causes forms to submit - suppressKeyPress = true; - event.preventDefault(); - this.menu.select( event ); - } - break; - case keyCode.TAB: - if ( this.menu.active ) { - this.menu.select( event ); - } - break; - case keyCode.ESCAPE: - if ( this.menu.element.is( ":visible" ) ) { - this._value( this.term ); - this.close( event ); - // Different browsers have different default behavior for escape - // Single press can mean undo or clear - // Double press in IE means clear the whole form - event.preventDefault(); - } - break; - default: - suppressKeyPressRepeat = true; - // search timeout should be triggered before the input value is changed - this._searchTimeout( event ); - break; - } - }, - keypress: function( event ) { - if ( suppressKeyPress ) { - suppressKeyPress = false; - if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { - event.preventDefault(); - } - return; - } - if ( suppressKeyPressRepeat ) { - return; - } - - // replicate some key handlers to allow them to repeat in Firefox and Opera - var keyCode = $.ui.keyCode; - switch( event.keyCode ) { - case keyCode.PAGE_UP: - this._move( "previousPage", event ); - break; - case keyCode.PAGE_DOWN: - this._move( "nextPage", event ); - break; - case keyCode.UP: - this._keyEvent( "previous", event ); - break; - case keyCode.DOWN: - this._keyEvent( "next", event ); - break; - } - }, - input: function( event ) { - if ( suppressInput ) { - suppressInput = false; - event.preventDefault(); - return; - } - this._searchTimeout( event ); - }, - focus: function() { - this.selectedItem = null; - this.previous = this._value(); - }, - blur: function( event ) { - if ( this.cancelBlur ) { - delete this.cancelBlur; - return; - } - - clearTimeout( this.searching ); - this.close( event ); - this._change( event ); - } - }); - - this._initSource(); - this.menu = $( "