You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2017/03/21 22:23:38 UTC
[3/7] wicket git commit: WICKET-6286 ajax download
WICKET-6286 ajax download
Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/5578e698
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/5578e698
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/5578e698
Branch: refs/heads/master
Commit: 5578e6982b93f81be23c56c8ece90b6f68440eb7
Parents: fd6befd
Author: Sven Meier <sv...@apache.org>
Authored: Wed Jan 4 00:00:01 2017 +0100
Committer: Sven Meier <sv...@apache.org>
Committed: Tue Mar 21 23:23:16 2017 +0100
----------------------------------------------------------------------
.../examples/ajax/builtin/AjaxApplication.java | 2 +-
.../examples/ajax/builtin/AjaxDownloadPage.html | 41 +++
.../examples/ajax/builtin/AjaxDownloadPage.java | 244 +++++++++++++++
.../wicket/examples/ajax/builtin/Index.html | 2 +
.../wicket/extensions/ajax/AjaxDownload.java | 295 +++++++++++++++++++
.../extensions/ajax/wicket-ajaxdownload.js | 60 ++++
6 files changed, 643 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/wicket/blob/5578e698/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxApplication.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxApplication.java b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxApplication.java
index 871fdbf..dd44b56 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxApplication.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxApplication.java
@@ -57,7 +57,7 @@ public class AjaxApplication extends WicketExampleApplication
mountPage("todo-list", TodoList.class);
mountPage("world-clock", WorldClockPage.class);
mountPage("upload", FileUploadPage.class);
-
+ mountPage("download", AjaxDownloadPage.class);
}
/**
http://git-wip-us.apache.org/repos/asf/wicket/blob/5578e698/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxDownloadPage.html
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxDownloadPage.html b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxDownloadPage.html
new file mode 100644
index 0000000..2764b25
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxDownloadPage.html
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<html xmlns:wicket="http://wicket.apache.org">
+<head>
+<wicket:head>
+ <style>
+ .download-veil {
+ position: absolute;
+ left: 20%;
+ right: 20%;
+ color: white;
+ background-color: darkorange;
+ font-size: 40px;
+ text-align: center;
+ }
+
+ .download-veil span {
+ line-height: 128px;
+ }
+ </style>
+</wicket:head>
+</head>
+<body>
+<wicket:extend>
+
+<p>
+This example demonstrates a <a wicket:id="download">download</a> initiated via Ajax.
+</p>
+
+<p>
+This download <a wicket:id="downloadFailure">fails</a>.
+</p>
+
+<p>
+A resource reference can be <a wicket:id="downloadReference">used too</a>.
+</p>
+
+<div wicket:id="downloading" class="download-veil"><span>Preparing download ...</span></div>
+
+</wicket:extend>
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/wicket/blob/5578e698/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxDownloadPage.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxDownloadPage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxDownloadPage.java
new file mode 100644
index 0000000..a23368c
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/AjaxDownloadPage.java
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.wicket.examples.ajax.builtin;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.extensions.ajax.AjaxDownload;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.request.http.flow.AbortWithHttpErrorCodeException;
+import org.apache.wicket.request.resource.ContentDisposition;
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.request.resource.ResourceStreamResource;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.StringResourceStream;
+import org.apache.wicket.util.time.Duration;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Ajax download.
+ *
+ * @author svenmeier
+ */
+public class AjaxDownloadPage extends BasePage
+{
+ private WebMarkupContainer downloadingContainer;
+
+ /**
+ * Constructor
+ */
+ public AjaxDownloadPage()
+ {
+ downloadingContainer = new WebMarkupContainer("downloading");
+ downloadingContainer.setOutputMarkupPlaceholderTag(true);
+ downloadingContainer.setVisible(false);
+ add(downloadingContainer);
+
+ initDownload();
+
+ initDownloadFailure();
+
+ initDownloadReference();
+ }
+
+ private void initDownload()
+ {
+ IResource resource = new ResourceStreamResource() {
+ protected IResourceStream getResourceStream() {
+ // simulate delay
+ try
+ {
+ TimeUnit.MILLISECONDS.sleep(5000);
+ }
+ catch (InterruptedException e)
+ {
+ }
+
+ return new StringResourceStream("downloaded via ajax");
+ };
+
+ }.setFileName("File-from-IResource.txt").setContentDisposition(ContentDisposition.ATTACHMENT).setCacheDuration(Duration.NONE);
+
+ final AjaxDownload download = new AjaxDownload(resource) {
+
+ @Override
+ protected void onBeforeDownload(AjaxRequestTarget target)
+ {
+ downloadingContainer.setVisible(true);
+ target.add(downloadingContainer);
+ }
+
+ @Override
+ protected void onDownloadSuccess(AjaxRequestTarget target)
+ {
+ downloadingContainer.setVisible(false);
+ target.add(downloadingContainer);
+ }
+
+ @Override
+ protected void onDownloadFailed(AjaxRequestTarget target)
+ {
+ downloadingContainer.setVisible(false);
+ target.add(downloadingContainer);
+ }
+ };
+ add(download);
+
+ add(new AjaxLink<Void>("download")
+ {
+ @Override
+ public void onClick(AjaxRequestTarget target)
+ {
+ download.initiate(target);
+ }
+ });
+ }
+
+ private void initDownloadFailure()
+ {
+ IResource resource = new ResourceStreamResource() {
+ protected IResourceStream getResourceStream() {
+ // simulate delay
+ try
+ {
+ TimeUnit.MILLISECONDS.sleep(2000);
+ }
+ catch (InterruptedException e)
+ {
+ }
+
+ throw new AbortWithHttpErrorCodeException(500);
+ };
+
+ }.setFileName("file").setContentDisposition(ContentDisposition.ATTACHMENT).setCacheDuration(Duration.NONE);
+
+ final AjaxDownload download = new AjaxDownload(resource) {
+
+ @Override
+ protected void onBeforeDownload(AjaxRequestTarget target)
+ {
+ downloadingContainer.setVisible(true);
+ target.add(downloadingContainer);
+ }
+
+ @Override
+ protected void onDownloadSuccess(AjaxRequestTarget target)
+ {
+ downloadingContainer.setVisible(false);
+ target.add(downloadingContainer);
+ }
+
+ @Override
+ protected void onDownloadFailed(AjaxRequestTarget target)
+ {
+ downloadingContainer.setVisible(false);
+ target.add(downloadingContainer);
+
+ target.appendJavaScript("alert('Download failed');");
+ }
+ };
+ add(download);
+
+ add(new AjaxLink<Void>("downloadFailure")
+ {
+ @Override
+ public void onClick(AjaxRequestTarget target)
+ {
+ download.initiate(target);
+ }
+ });
+ }
+
+ private void initDownloadReference()
+ {
+ ResourceReference reference = new ResourceReference("referenceToResource") {
+ @Override
+ public IResource getResource()
+ {
+ return new StaticResource();
+ }
+ };
+
+ final AjaxDownload download = new AjaxDownload(reference) {
+
+ @Override
+ protected void onBeforeDownload(AjaxRequestTarget target)
+ {
+ downloadingContainer.setVisible(true);
+ target.add(downloadingContainer);
+ }
+
+ @Override
+ protected void onDownloadSuccess(AjaxRequestTarget target)
+ {
+ downloadingContainer.setVisible(false);
+ target.add(downloadingContainer);
+ }
+
+ @Override
+ protected void onDownloadFailed(AjaxRequestTarget target)
+ {
+ downloadingContainer.setVisible(false);
+ target.add(downloadingContainer);
+ }
+ };
+ add(download);
+
+ add(new AjaxLink<Void>("downloadReference")
+ {
+ @Override
+ public void onClick(AjaxRequestTarget target)
+ {
+ download.initiate(target);
+ }
+ });
+ }
+
+ public static class StaticResource extends ResourceStreamResource {
+
+ StaticResource() {
+ setFileName("File-from-ResourceReference");
+ setContentDisposition(ContentDisposition.ATTACHMENT);
+ setCacheDuration(Duration.NONE);
+ }
+
+ @Override
+ public void respond(Attributes attributes)
+ {
+ AjaxDownload.markCompleted(attributes);
+
+ super.respond(attributes);
+ }
+
+ @Override
+ protected IResourceStream getResourceStream()
+ {
+ // simulate delay
+ try
+ {
+ TimeUnit.MILLISECONDS.sleep(5000);
+ }
+ catch (InterruptedException e)
+ {
+ }
+
+ return new StringResourceStream("downloaded via ajax with resource reference");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/wicket/blob/5578e698/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/Index.html
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/Index.html b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/Index.html
index 4813972..2da2160 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/Index.html
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/Index.html
@@ -35,6 +35,8 @@
<a href="TodoList.html">Todo list Example</a>: shows ajax todo list page without writing any JavaScript
<br/><br/>
<a href="WorldClockPage.html">World Clock Example</a>: demonstrates a single component with AjaxTimerBehavior updating multiple components
+<br/><br/>
+<a href="AjaxDownloadPage.html">Ajax Download</a>: download initiated via Ajax
</wicket:link>
</wicket:extend>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/wicket/blob/5578e698/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/AjaxDownload.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/AjaxDownload.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/AjaxDownload.java
new file mode 100644
index 0000000..a879d5b
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/AjaxDownload.java
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.wicket.extensions.ajax;
+
+import javax.annotation.Resource;
+import javax.servlet.http.Cookie;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.IResourceListener;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.json.JSONObject;
+import org.apache.wicket.ajax.json.JsonFunction;
+import org.apache.wicket.behavior.Behavior;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.request.Response;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
+import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.request.resource.IResource.Attributes;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.resource.JQueryPluginResourceReference;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * Download resources via Ajax.
+ * <p>
+ * Usage:
+ *
+ * <pre>
+ * final AjaxDownload download = new AjaxDownload(resource);
+ * add(download);
+ *
+ * add(new AjaxButton("download")
+ * {
+ * @Override
+ * protected void onSubmit(AjaxRequestTarget target, Form<?> form)
+ * {
+ * download.initiate(target);
+ * }
+ * });
+ * </pre>
+ *
+ * @author svenmeier
+ */
+public class AjaxDownload extends AbstractDefaultAjaxBehavior
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Name of parameter used to transfer the download identifier to the resource.
+ *
+ * @see #markCompleted(Attributes)
+ */
+ private static final String RESOURCE_PARAMETER_NAME = "wicket-ajaxdownload";
+
+ private static final ResourceReference JS = new JQueryPluginResourceReference(
+ AjaxDownload.class, "wicket-ajaxdownload.js");
+
+ private final ResourceReference resourceReference;
+
+ private final ResourceBehavior resourceBehavior;
+
+ private PageParameters resourceParameters;
+
+ /**
+ * Download of a {@link Resource}.
+ *
+ * @param resource
+ * resource to download
+ */
+ public AjaxDownload(IResource resource)
+ {
+ Args.notNull(resource, "resource");
+ this.resourceBehavior = new ResourceBehavior(resource);
+ this.resourceReference = null;
+ }
+
+ /**
+ * Download of a {@link ResourceReference}.
+ * <p>
+ * The {@link IResource} returned by {@link ResourceReference#getResource()} must call
+ * {@link #markCompleted(Attributes)} when responding, otherwise the callback
+ * {@link #onDownloadSuccess(AjaxRequestTarget)} will not work.
+ *
+ * @param reference
+ * reference to resource to download
+ */
+ public AjaxDownload(ResourceReference reference)
+ {
+ this(reference, null);
+ }
+
+ /**
+ * Download of a {@link ResourceReference}.
+ * <p>
+ * The {@link IResource} returned by {@link ResourceReference#getResource()} must call
+ * {@link #markCompleted(Attributes)} when responding, otherwise the callback
+ * {@link #onDownloadSuccess(AjaxRequestTarget)} will not work.
+ *
+ * @param reference
+ * reference to resource to download
+ * @param resourceParameters
+ * parameters for the resource
+ */
+ public AjaxDownload(ResourceReference reference, PageParameters resourceParameters)
+ {
+ this.resourceBehavior = null;
+
+ this.resourceReference = Args.notNull(reference, "reference");
+ this.resourceParameters = resourceParameters;
+ }
+
+ @Override
+ protected void onBind()
+ {
+ super.onBind();
+
+ if (resourceBehavior != null)
+ {
+ getComponent().add(resourceBehavior);
+ }
+ }
+
+ @Override
+ protected void onUnbind()
+ {
+ super.onUnbind();
+
+ if (resourceBehavior != null)
+ {
+ getComponent().remove(resourceBehavior);
+ }
+ }
+
+ /**
+ * Call this method to initiate the download.
+ *
+ * @param target
+ * the initiating Ajax target
+ */
+ public void initiate(AjaxRequestTarget target)
+ {
+ if (getComponent() == null)
+ {
+ throw new WicketRuntimeException("not bound to a component");
+ }
+
+ ((WebResponse)RequestCycle.get().getResponse()).clearCookie(cookie(getName()));
+
+ CharSequence url;
+ if (resourceBehavior == null)
+ {
+ if (resourceReference.canBeRegistered())
+ {
+ getComponent().getApplication().getResourceReferenceRegistry()
+ .registerResourceReference(resourceReference);
+ }
+
+ PageParameters parameters = new PageParameters();
+ if (resourceParameters != null)
+ {
+ parameters.mergeWith(resourceParameters);
+ }
+ parameters.set(RESOURCE_PARAMETER_NAME, getName());
+
+ url = getComponent().getRequestCycle()
+ .urlFor(new ResourceReferenceRequestHandler(resourceReference, parameters));
+ }
+ else
+ {
+ url = resourceBehavior.getUrl();
+ }
+
+ JSONObject settings = new JSONObject();
+ settings.put("attributes", new JsonFunction(renderAjaxAttributes(getComponent())));
+ settings.put("name", getName());
+ settings.put("url", url);
+
+ target.appendJavaScript(String.format("Wicket.AjaxDownload.initiate(%s);", settings));
+
+ onBeforeDownload(target);
+ }
+
+ protected void onBeforeDownload(AjaxRequestTarget target)
+ {
+ }
+
+ protected void onDownloadSuccess(AjaxRequestTarget target)
+ {
+ }
+
+ protected void onDownloadFailed(AjaxRequestTarget target)
+ {
+ }
+
+ @Override
+ public void renderHead(Component component, IHeaderResponse response)
+ {
+ super.renderHead(component, response);
+
+ response.render(JavaScriptHeaderItem.forReference(JS));
+ }
+
+ @Override
+ protected void respond(AjaxRequestTarget target)
+ {
+ String result = getComponent().getRequest().getRequestParameters().getParameterValue("result").toOptionalString();
+ if ("success".equals(result)) {
+ onDownloadSuccess(target);
+ } else if ("failed".equals(result)) {
+ onDownloadFailed(target);
+ }
+ }
+
+ /**
+ * Identifying name of this behavior.
+ */
+ private String getName()
+ {
+ return String.format("wicket-ajaxdownload-%s-%s", getComponent().getMarkupId(),
+ getComponent().getBehaviorId(this));
+ }
+
+ /**
+ * The behavior responding with the actual resource.
+ */
+ private class ResourceBehavior extends Behavior implements IResourceListener
+ {
+ private final IResource resource;
+
+ private ResourceBehavior(IResource resource)
+ {
+ this.resource = Args.notNull(resource, "resource");
+ }
+
+ @Override
+ public void onResourceRequested()
+ {
+ final RequestCycle requestCycle = RequestCycle.get();
+ final Response response = requestCycle.getResponse();
+ ((WebResponse) response).addCookie(cookie(getName()));
+
+ Attributes a = new Attributes(requestCycle.getRequest(), response, null);
+
+ resource.respond(a);
+ }
+
+ public CharSequence getUrl()
+ {
+ return getComponent().urlFor(this, IResourceListener.INTERFACE, null);
+ }
+ }
+
+ /**
+ * Mark a resource as complete.
+ * <p>
+ * Has to be called from {@link IResource#respond(Attributes)} when downloaded via
+ * {@link #AjaxDownload(IResource)}.
+ *
+ * @param attributes
+ * resource attributes
+ */
+ public static void markCompleted(IResource.Attributes attributes)
+ {
+ String cookieName = attributes.getParameters().get(RESOURCE_PARAMETER_NAME).toString();
+
+ ((WebResponse)attributes.getResponse()).addCookie(cookie(cookieName));
+ }
+
+ private static Cookie cookie(String name)
+ {
+ Cookie cookie = new Cookie(name, "complete");
+ cookie.setPath("/");
+ return cookie;
+ }
+}
http://git-wip-us.apache.org/repos/asf/wicket/blob/5578e698/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/wicket-ajaxdownload.js
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/wicket-ajaxdownload.js b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/wicket-ajaxdownload.js
new file mode 100644
index 0000000..52471df
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/wicket-ajaxdownload.js
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+;(function (undefined) {
+ 'use strict';
+
+ if (!window.Wicket) {
+ window.Wicket = {};
+ }
+
+ if (Wicket.AjaxDownload) {
+ return;
+ }
+
+ Wicket.AjaxDownload = {
+ initiate : function(settings) {
+
+ var frame = jQuery("<iframe>").hide().prop("src", settings.url).appendTo("body");
+
+ var checkComplete = function() {
+ var result;
+
+ if (document.cookie.indexOf(settings.name + '=') > -1) {
+ result = "success";
+ } else {
+ var html = frame.contents().find('body').html();
+ if (html && html.length) {
+ result = "failed";
+ }
+ }
+
+ if (result) {
+ setTimeout(function() { frame.remove(); }, 0);
+
+ settings.attributes.ep = settings.attributes.ep || {};
+ settings.attributes.ep['result'] = result;
+ Wicket.Ajax.ajax(settings.attributes);
+ } else {
+ setTimeout(checkComplete, 100);
+ }
+ };
+
+ checkComplete();
+ }
+ };
+
+})();
\ No newline at end of file