package org.geogebra.web.html5.main;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeSet;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.io.MyXMLio;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Macro;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.geos.GeoCasCell;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.main.App;
import org.geogebra.common.main.OpenFileListener;
import org.geogebra.common.plugin.GgbAPI;
import org.geogebra.common.util.Assignment;
import org.geogebra.common.util.Assignment.Result;
import org.geogebra.common.util.Exercise;
import org.geogebra.common.util.FileExtensions;
import org.geogebra.common.util.StringUtil;
import org.geogebra.common.util.debug.Log;
import org.geogebra.web.html5.Browser;
import org.geogebra.web.html5.css.GuiResourcesSimple;
import org.geogebra.web.html5.euclidian.EuclidianViewW;
import org.geogebra.web.html5.euclidian.EuclidianViewWInterface;
import org.geogebra.web.html5.gui.GuiManagerInterfaceW;
import org.geogebra.web.html5.gui.tooltip.ToolTipManagerW;
import org.geogebra.web.html5.util.ImageManagerW;
import org.geogebra.web.html5.util.ViewW;
import org.geogebra.web.resources.JavaScriptInjector;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RootPanel;
public class GgbAPIW extends GgbAPI {
public GgbAPIW(App app) {
this.app = app;
this.kernel = app.getKernel();
this.algebraprocessor = kernel.getAlgebraProcessor();
this.construction = kernel.getConstruction();
}
@Override
public byte[] getGGBfile() {
// TODO Auto-generated method stub
return null;
}
public Context2d getContext2D() {
return ((AppW) app).getCanvas().getContext2d();
}
@Override
public void setBase64(String base64) {
resetPerspective();
ViewW view = new ViewW(RootPanel.getBodyElement(), (AppW) app);
view.processBase64String(base64);
}
private void resetPerspective() {
if (((AppW) app).getArticleElement() != null) {
((AppW) app).getArticleElement().setAttribute(
"data-param-perspective", "");
}
}
public void setBase64(String base64, final JavaScriptObject callback) {
if (callback != null) {
OpenFileListener listener = new OpenFileListener() {
@Override
public void onOpenFile() {
ScriptManagerW.runCallback(callback);
app.unregisterOpenFileListener(this);
}
};
app.registerOpenFileListener(listener);
}
setBase64(base64);
}
public void openFile(String filename, final JavaScriptObject callback) {
if (callback != null) {
OpenFileListener listener = new OpenFileListener() {
@Override
public void onOpenFile() {
ScriptManagerW.runCallback(callback);
app.unregisterOpenFileListener(this);
}
};
app.registerOpenFileListener(listener);
}
openFile(filename);
}
@Override
public void setErrorDialogsActive(boolean flag) {
app.setErrorDialogsActive(flag);
}
@Override
public void refreshViews() {
app.refreshViews();
}
@Override
public void openFile(String filename) {
resetPerspective();
ViewW view = new ViewW(RootPanel.getBodyElement(), (AppW) app);
view.processFileName(filename);
}
@Override
public boolean writePNGtoFile(String filename, double exportScale,
boolean transparent, double DPI) {
// get export image
// DPI ignored (desktop only)
String url = ((EuclidianViewWInterface) app.getActiveEuclidianView())
.getExportImageDataUrl(exportScale, transparent);
// make browser save/download PNG file
Browser.exportImage(url, filename);
return true;
}
@Override
public String getPNGBase64(double exportScale, boolean transparent,
double DPI, boolean copyToClipboard) {
if (app.getGuiManager() != null) {
app.getGuiManager().getLayout().getDockManager().ensureFocus();
if (app.getGuiManager().getLayout().getDockManager()
.getFocusedViewId() == App.VIEW_PROBABILITY_CALCULATOR) {
return pngBase64(((EuclidianViewWInterface) app.getGuiManager()
.getPlotPanelEuclidanView()).getExportImageDataUrl(
exportScale, transparent));
}
}
return pngBase64(
((EuclidianViewWInterface) app.getActiveEuclidianView())
.getExportImageDataUrl(exportScale, transparent));
}
private static String pngBase64(String pngURL) {
return pngURL.substring("data:image/png;base64,".length());
}
public String getLaTeXBase64(String label, boolean value) {
Canvas c = Canvas.createIfSupported();
GeoElement geo = kernel.lookupLabel(label);
if (geo == null) {
return "";
}
String str;
if (value) {
str = geo.toValueString(StringTemplate.latexTemplate);
} else {
str = geo instanceof GeoCasCell ? ((GeoCasCell) geo)
.getLaTeXInput(StringTemplate.latexTemplate) : geo
.toString(StringTemplate.latexTemplate);
}
DrawEquationW.paintOnCanvasOutput(geo, str, c, app.getFontSizeWeb());
return c.toDataUrl().substring("data:image/png;base64,".length());
}
public void getGGB(final boolean includeThumbnail,
final JavaScriptObject callback) {
Map<String, String> archiveContent = createArchiveContent(includeThumbnail);
final boolean oldWorkers = setWorkerURL(zipJSworkerURL(), false);
final JavaScriptObject arch = prepareToEntrySet(archiveContent);
getGGBZipJs(arch, callback, nativeCallback(new StringHandler() {
public void handle(String s) {
if (oldWorkers && !isUsingWebWorkers()) {
Log.warn(
"Saving with workers failed, trying without workers.");
JavaScriptInjector
.inject(GuiResourcesSimple.INSTANCE.deflateJs());
getGGBZipJs(arch, callback, null);
}
}
}));
}
/**
* @return URL of zip worker directory
*/
public static String zipJSworkerURL() {
// FIXME disabled workers in Touch for now
if ("tablet".equals(GWT.getModuleName())
|| "phone".equals(GWT.getModuleName())) {
return "false";
}
return Browser.webWorkerSupported() ? GWT.getModuleBaseURL()
+ "js/zipjs/" : "false";
}
/**
* @param includeThumbnail
* whether to include thumbnail
* @param callback
* callback
*/
public void getBase64(boolean includeThumbnail, JavaScriptObject callback) {
Map<String, String> archiveContent = createArchiveContent(includeThumbnail);
getBase64ZipJs(prepareToEntrySet(archiveContent), callback,
zipJSworkerURL(), false);
}
public void getMacrosBase64(boolean includeThumbnail,
JavaScriptObject callback) {
Map<String, String> archiveContent = createMacrosArchive();
getBase64ZipJs(prepareToEntrySet(archiveContent), callback,
zipJSworkerURL(), false);
}
public JavaScriptObject getFileJSON(boolean includeThumbnail) {
Map<String, String> archiveContent = createArchiveContent(includeThumbnail);
return prepareToEntrySet(archiveContent);
}
public void setFileJSON(JavaScriptObject obj) {
resetPerspective();
ViewW view = new ViewW(RootPanel.getBodyElement(), (AppW) app);
view.processJSON(obj);
}
private static class StoreString implements StringHandler {
private String result = "";
protected StoreString() {
}
@Override
public void handle(String s) {
this.result = s;
}
public String getResult() {
return result;
}
}
@Override
public String getBase64(boolean includeThumbnail) {
StoreString storeString = new StoreString();
Map<String, String> archiveContent = createArchiveContent(includeThumbnail);
JavaScriptObject jso = prepareToEntrySet(archiveContent);
if (Browser.webWorkerSupported()) {
JavaScriptInjector.inject(GuiResourcesSimple.INSTANCE.deflateJs());
}
getBase64ZipJs(jso, nativeCallback(storeString), "false", true);
return storeString.getResult();
}
public String getMacrosBase64() {
StoreString storeString = new StoreString();
Map<String, String> archiveContent = createMacrosArchive();
JavaScriptObject jso = prepareToEntrySet(archiveContent);
if (Browser.webWorkerSupported()) {
JavaScriptInjector.inject(GuiResourcesSimple.INSTANCE.deflateJs());
}
getBase64ZipJs(jso, nativeCallback(storeString), "false", true);
return storeString.getResult();
}
/**
* @param includeThumbnail
* whether to include thumbnail
* @param callback
* callback
*/
public void getBase64(boolean includeThumbnail, StringHandler callback) {
getBase64(includeThumbnail, nativeCallback(callback));
}
/**
* @param includeThumbnail
* whether to include thumbnail
* @param callback
* callback
*/
public void getMacrosBase64(boolean includeThumbnail, StringHandler callback) {
getMacrosBase64(includeThumbnail, nativeCallback(callback));
}
private native JavaScriptObject nativeCallback(StringHandler callback) /*-{
return function(b) {
callback.@org.geogebra.web.html5.main.StringHandler::handle(Ljava/lang/String;)(b);
};
}-*/;
/**
* @param includeThumbnail
* whether to include thumbnail
* @return
*/
public HashMap<String, String> createArchiveContent(boolean includeThumbnail) {
HashMap<String, String> archiveContent = new HashMap<String, String>();
boolean isSaving = getKernel().isSaving();
// return getNativeBase64(includeThumbnail);
getKernel().setSaving(true);
adjustConstructionImages(getConstruction());
String constructionXml = getApplication().getXML();
String macroXml = getApplication().getMacroXMLorEmpty();
StringBuilder defaults2d = new StringBuilder();
StringBuilder defaults3d = null;
if (app.is3D()) {
defaults3d = new StringBuilder();
}
getKernel().getConstruction().getConstructionDefaults()
.getDefaultsXML(defaults2d, defaults3d);
String geogebra_javascript = getKernel().getLibraryJavaScript();
writeConstructionImages(getConstruction(), "", archiveContent);
// write construction thumbnails
if (includeThumbnail) {
addImageToZip(MyXMLio.XML_FILE_THUMBNAIL,
getViewForThumbnail()
.getCanvasBase64WithTypeString(), archiveContent);
}
if (!"".equals(macroXml)) {
writeMacroImages(archiveContent);
archiveContent.put(MyXMLio.XML_FILE_MACRO, macroXml);
}
if (defaults2d.length() > 0) {
archiveContent.put(MyXMLio.XML_FILE_DEFAULTS_2D,
defaults2d.toString());
}
if (defaults3d != null && defaults3d.length() > 0) {
archiveContent.put(MyXMLio.XML_FILE_DEFAULTS_3D,
defaults3d.toString());
}
archiveContent.put(MyXMLio.JAVASCRIPT_FILE, geogebra_javascript);
archiveContent.put(MyXMLio.XML_FILE, constructionXml);
getKernel().setSaving(isSaving);
return archiveContent;
}
private EuclidianViewWInterface getViewForThumbnail() {
if (app.hasEuclidianView3D() && app.showView(App.VIEW_EUCLIDIAN3D)) {
return (EuclidianViewWInterface) app.getEuclidianView3D();
}
return ((EuclidianViewWInterface) app.getActiveEuclidianView());
}
/**
* @return archive with macros + icons
*/
public HashMap<String, String> createMacrosArchive() {
HashMap<String, String> archiveContent = new HashMap<String, String>();
writeMacroImages(archiveContent);
String macroXml = getApplication().getMacroXMLorEmpty();
if (!"".equals(macroXml)) {
writeMacroImages(archiveContent);
archiveContent.put(MyXMLio.XML_FILE_MACRO, macroXml);
}
return archiveContent;
}
private JavaScriptObject prepareToEntrySet(Map<String, String> archive) {
JavaScriptObject nativeEntry = JavaScriptObject.createObject();
if (archive.entrySet() != null) {
for (Entry<String, String> entry : archive.entrySet()) {
pushIntoNativeEntry(entry.getKey(), entry.getValue(),
nativeEntry);
}
}
return nativeEntry;
}
private native void pushIntoNativeEntry(String key, String value,
JavaScriptObject ne) /*-{
if (typeof ne["archive"] === "undefined") { //needed because gwt gives an __objectId key :-(
ne["archive"] = [];
}
var obj = {};
obj.fileName = key;
obj.fileContent = value;
ne["archive"].push(obj);
}-*/;
private native void getGGBZipJs(JavaScriptObject arch,
JavaScriptObject clb, JavaScriptObject errorClb) /*-{
function encodeUTF8(string) {
var n, c1, enc, utftext = [], start = 0, end = 0, stringl = string.length;
for (n = 0; n < stringl; n++) {
c1 = string.charCodeAt(n);
enc = null;
if (c1 < 128)
end++;
else if (c1 > 127 && c1 < 2048)
enc = String.fromCharCode((c1 >> 6) | 192)
+ String.fromCharCode((c1 & 63) | 128);
else
enc = String.fromCharCode((c1 >> 12) | 224)
+ String.fromCharCode(((c1 >> 6) & 63) | 128)
+ String.fromCharCode((c1 & 63) | 128);
if (enc != null) {
if (end > start)
utftext += string.slice(start, end);
utftext += enc;
start = end = n + 1;
}
}
if (end > start)
utftext += string.slice(start, stringl);
return utftext;
}
function ASCIIReader(text) {
var that = this;
function init(callback, onerror) {
that.size = text.length;
callback();
}
function readUint8Array(index, length, callback, onerror) {
if (text.length <= index) {
return new $wnd.Uint8Array(0);
} else if (index < 0) {
return new $wnd.Uint8Array(0);
} else if (length <= 0) {
return new $wnd.Uint8Array(0);
} else if (text.length < index + length) {
length = text.length - index;
}
var i, data = new $wnd.Uint8Array(length);
for (i = index; i < index + length; i++)
data[i - index] = text.charCodeAt(i);
callback(data);
}
that.size = 0;
that.init = init;
that.readUint8Array = readUint8Array;
}
ASCIIReader.prototype = new $wnd.zip.Reader();
ASCIIReader.prototype.constructor = ASCIIReader;
$wnd.zip
.createWriter(
new $wnd.zip.BlobWriter(),
function(zipWriter) {
function addImage(name, data, callback) {
var data2 = data.substr(data.indexOf(',') + 1);
zipWriter.add(name,
new $wnd.zip.Data64URIReader(data2),
callback);
}
function addText(name, data, callback) {
zipWriter.add(name, new ASCIIReader(data),
callback);
}
function checkIfStillFilesToAdd() {
var item, imgExtensions = [ "jpg", "jpeg",
"png", "gif", "bmp" ];
if (arch.archive.length > 0) {
@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)("arch.archive.length: "+arch.archive.length);
item = arch.archive.shift();
var ind = item.fileName.lastIndexOf('.');
if (ind > -1
&& imgExtensions
.indexOf(item.fileName
.substr(ind + 1)
.toLowerCase()) > -1) {
//if (item.fileName.indexOf(".png") > -1)
//@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)("image zipped: " + item.fileName);
addImage(item.fileName,
item.fileContent, function() {
checkIfStillFilesToAdd();
});
} else {
//@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)("text zipped: " + item.fileName);
addText(item.fileName,
encodeUTF8(item.fileContent),
function() {
checkIfStillFilesToAdd();
});
}
} else {
zipWriter
.close(function(dataURI) {
if (typeof clb === "function") {
clb(dataURI);
// that's right, this truncation is necessary
//clb(dataURI.substr(dataURI.indexOf(',')+1));
} else {
@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)("not callback was given");
@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)(dataURI);
}
});
}
}
checkIfStillFilesToAdd();
},
function(error) {
if (typeof errorClb === "function") {
errorClb(error + "");
}
@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)("error occured while creating ggb zip");
});
}-*/;
void getBase64ZipJs(final JavaScriptObject arch, final JavaScriptObject clb,
String workerUrls, boolean sync) {
final boolean oldWorkers = setWorkerURL(workerUrls, sync);
getBase64ZipJs(arch, clb, nativeCallback(new StringHandler() {
public void handle(String s) {
if (oldWorkers && !isUsingWebWorkers()) {
Log.warn(
"Saving with workers failed, trying without workers.");
JavaScriptInjector
.inject(GuiResourcesSimple.INSTANCE.deflateJs());
getBase64ZipJs(arch, clb, "false", false);
}
}
}));
}
private native void getBase64ZipJs(JavaScriptObject arch,
JavaScriptObject clb, JavaScriptObject errorClb) /*-{
function encodeUTF8(string) {
var n, c1, enc, utftext = [], start = 0, end = 0, stringl = string.length;
for (n = 0; n < stringl; n++) {
c1 = string.charCodeAt(n);
enc = null;
if (c1 < 128)
end++;
else if (c1 > 127 && c1 < 2048)
enc = String.fromCharCode((c1 >> 6) | 192)
+ String.fromCharCode((c1 & 63) | 128);
else
enc = String.fromCharCode((c1 >> 12) | 224)
+ String.fromCharCode(((c1 >> 6) & 63) | 128)
+ String.fromCharCode((c1 & 63) | 128);
if (enc != null) {
if (end > start)
utftext += string.slice(start, end);
utftext += enc;
start = end = n + 1;
}
}
if (end > start)
utftext += string.slice(start, stringl);
return utftext;
}
function ASCIIReader(text) {
var that = this;
function init(callback, onerror) {
that.size = text.length;
callback();
}
function readUint8Array(index, length, callback, onerror) {
if (text.length <= index) {
return new $wnd.Uint8Array(0);
} else if (index < 0) {
return new $wnd.Uint8Array(0);
} else if (length <= 0) {
return new $wnd.Uint8Array(0);
} else if (text.length < index + length) {
length = text.length - index;
}
var i, data = new $wnd.Uint8Array(length);
for (i = index; i < index + length; i++)
data[i - index] = text.charCodeAt(i);
callback(data);
}
that.size = 0;
that.init = init;
that.readUint8Array = readUint8Array;
}
ASCIIReader.prototype = new $wnd.zip.Reader();
ASCIIReader.prototype.constructor = ASCIIReader;
//$wnd.zip.useWebWorkers = false;
$wnd.zip
.createWriter(
new $wnd.zip.Data64URIWriter(
"application/vnd.geogebra.file"),
function(zipWriter) {
function addImage(name, data, callback) {
var data2 = data.substr(data.indexOf(',') + 1);
zipWriter.add(name,
new $wnd.zip.Data64URIReader(data2),
callback);
}
function addText(name, data, callback) {
zipWriter.add(name, new ASCIIReader(data),
callback);
}
function checkIfStillFilesToAdd() {
var item, imgExtensions = [ "jpg", "jpeg",
"png", "gif", "bmp" ];
if (arch.archive.length > 0) {
item = arch.archive.shift();
var ind = item.fileName.lastIndexOf('.');
if (ind > -1
&& imgExtensions
.indexOf(item.fileName
.substr(ind + 1)
.toLowerCase()) > -1) {
@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)("image zipped: " + item.fileName);
addImage(item.fileName,
item.fileContent, function() {
checkIfStillFilesToAdd();
});
} else {
@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)("text zipped: " + item.fileName);
addText(item.fileName,
encodeUTF8(item.fileContent),
function() {
checkIfStillFilesToAdd();
});
}
} else {
zipWriter
.close(function(dataURI) {
if (typeof clb === "function") {
// that's right, this truncation is necessary
clb(dataURI.substr(dataURI
.indexOf(',') + 1));
} else {
@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)("not callback was given");
@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)(dataURI);
}
});
}
}
checkIfStillFilesToAdd();
},
function(error) {
@org.geogebra.common.util.debug.Log::debug(Ljava/lang/String;)("error occured while creating base64 zip");
if (typeof errorClb === "function") {
errorClb(error + "");
}
});
}-*/;
private void writeMacroImages(Map<String, String> archive) {
if (kernel.hasMacros()) {
ArrayList<Macro> macros = kernel.getAllMacros();
writeMacroImages(macros, archive);
}
}
private void writeMacroImages(ArrayList<Macro> macros,
Map<String, String> archive) {
if (macros == null) {
return;
}
for (int i = 0; i < macros.size(); i++) {
// save all images in macro construction
Macro macro = macros.get(i);
// writeConstructionImages(macro.getMacroConstruction(), filePath,
// archive);
String fileName = macro.getIconFileName();
if (fileName != null && !fileName.isEmpty()) {
String url = ((ImageManagerW) app.getImageManager())
.getExternalImageSrc(fileName);
if (url != null) {
FileExtensions ext = StringUtil.getFileExtension(fileName);
MyImageW img = new MyImageW(
ImageElement.as((new Image(url)).getElement()),
FileExtensions.SVG.equals(ext));
addImageToArchive("", fileName, url, ext, img, archive);
}
}
/*
* // save macro icon String fileName = macro.getIconFileName();
* BufferedImage img =
* ((Application)app).getExternalImage(fileName); if (img != null)
* // Modified for Intergeo File Format (Yves Kreis) --> //
* writeImageToZip(zip, fileName, img); writeImageToZip(zipjs,
* filePath + fileName, img); // <-- Modified for Intergeo File
* Format (Yves Kreis)
*/
}
}
private void adjustConstructionImages(Construction cons) {
// save all GeoImage images
// TreeSet images =
// cons.getGeoSetLabelOrder(GeoElement.GEO_CLASS_IMAGE);
TreeSet<GeoElement> geos = cons.getGeoSetLabelOrder();
if (geos == null) {
return;
}
Iterator<GeoElement> it = geos.iterator();
while (it.hasNext()) {
GeoElement geo = it.next();
String fileName = geo.getImageFileName();
// for some reason we sometimes get null and sometimes "" if there
// is no image used
if (fileName != null && fileName.length() > 0) {
geo.getGraphicsAdapter().convertToSaveableFormat();
String newName = geo.getGraphicsAdapter().getImageFileName();
((ImageManagerW) app.getImageManager()).replace(fileName,
newName);
}
}
}
private void writeConstructionImages(Construction cons, String filePath,
Map<String, String> archive) {
// save all GeoImage images
// TreeSet images =
// cons.getGeoSetLabelOrder(GeoElement.GEO_CLASS_IMAGE);
TreeSet<GeoElement> geos = cons.getGeoSetLabelOrder();
if (geos == null) {
return;
}
Iterator<GeoElement> it = geos.iterator();
while (it.hasNext()) {
GeoElement geo = it.next();
String fileName = geo.getImageFileName();
if (!"".equals(fileName)) {
String url = ((ImageManagerW) app.getImageManager())
.getExternalImageSrc(fileName);
FileExtensions ext = StringUtil.getFileExtension(fileName);
MyImageW img = (MyImageW) geo.getFillImage();
Log.debug("filename = " + fileName);
Log.debug("ext = " + ext);
addImageToArchive(filePath, fileName, url, ext, img, archive);
}
}
}
private static void addImageToArchive(String filePath, String fileName,
String url, FileExtensions ext, MyImageW img,
Map<String, String> archive) {
if (ext.equals(FileExtensions.SVG)) {
addSvgToArchive(fileName, img, archive);
return;
}
String dataURL;
if ((url == null || url.startsWith("http"))
&& (img != null && img.getImage() != null)) {
dataURL = convertImgToPng(img);
} else {
dataURL = url;
}
if (dataURL != null) {
if (ext.isAllowedImage()) {
// png, jpg, jpeg
// NOT SVG (filtered earlier)
addImageToZip(filePath + fileName, dataURL, archive);
} else {
// not supported, so saved as PNG
addImageToZip(
filePath
+ StringUtil.changeFileExtension(fileName,
FileExtensions.PNG), dataURL, archive);
}
}
}
private static String convertImgToPng(MyImageW img) {
String url;
Canvas cv = Canvas.createIfSupported();
cv.setCoordinateSpaceWidth(img.getWidth());
cv.setCoordinateSpaceHeight(img.getHeight());
Context2d c2d = cv.getContext2d();
c2d.drawImage(img.getImage(), 0, 0);
url = cv.toDataUrl("image/png");
// Opera and Safari cannot toDataUrl jpeg (much less the others)
// if ("jpg".equals(ext) || "jpeg".equals(ext))
// addImageToZip(filePath + fileName, cv.toDataUrl("image/jpg"));
// else
return url;
}
private static void addSvgToArchive(String fileName, MyImageW img,
Map<String, String> archive) {
ImageElement svg = img.getImage();
// TODO
// String svgAsXML =
// "<svg width=\"100\" height=\"100\"> <circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"green\" stroke-width=\"4\" fill=\"yellow\" /></svg>";
String svgAsXML = svg.getAttribute("src");
// remove eg data:image/svg+xml;base64,
int index = svgAsXML.indexOf(',');
svgAsXML = svgAsXML.substring(index + 1);
Log.debug("svgAsXML = " + svgAsXML);
svgAsXML = Browser.decodeBase64(svgAsXML);
Log.debug("svgAsXML (decoded) = " + svgAsXML);
archive.put(fileName, svgAsXML);
}
private static void addImageToZip(String filename, String base64img,
Map<String, String> archive) {
archive.put(filename, base64img);
}
/**
* @param material
* material ID
*/
public void openMaterial(final String material) {
((AppW) app).openMaterial(material, new Runnable() {
@Override
public void run() {
Log.debug("Loading failed for id" + material);
}
});
}
/**
* @param width
* setst the applet width
*/
public void setWidth(int width) {
((AppW) app).getAppletFrame().setWidth(width);
}
/**
* @param height
* sets the applet height
*/
public void setHeight(int height) {
((AppW) app).getAppletFrame().setHeight(height);
}
/**
* @param width
* height
* @param height
* width
*
* Sets the size of the applet
*/
public void setSize(int width, int height) {
((AppW) app).getAppletFrame().setSize(width, height);
}
/**
* @param show
*
* wheter show the toolbar in geogebra-web applets or not
*/
public void showToolBar(boolean show) {
((AppW) app).getAppletFrame().showToolBar(show);
}
/**
* @param show
*
* wheter show the menubar in geogebra-web applets or not
*/
public void showMenuBar(boolean show) {
((AppW) app).getAppletFrame().showMenuBar(show);
}
/**
* @param show
*
* wheter show the algebrainput in geogebra-web applets or not
*/
public void showAlgebraInput(boolean show) {
((AppW) app).getAppletFrame().showAlgebraInput(show);
}
/**
* @param show
*
* wheter show the reseticon in geogebra-web applets or not
*/
public void showResetIcon(boolean show) {
((AppW) app).getAppletFrame().showResetIcon(show);
}
public void insertImage(String s) {
((AppW) app).urlDropHappened(s, 0, 0);
}
/**
* recalculates euclidianviews environments
*/
public void recalculateEnvironments() {
((AppW) app).recalculateEnvironments();
}
/**
* remove applet from the page, and free memory. If applet is the last one,
* it remove the style elements injected by the applet too.
*/
public void removeApplet() {
((AppW) app).getAppletFrame().remove();
}
@Override
public void showTooltip(String tooltip) {
ToolTipManagerW.sharedInstance().showBottomMessage(tooltip, false,
(AppW) app);
}
/**
* If there are Macros or an Exercise present in the current file this can
* be used to check if parts of the construction are equivalent to the
* Macros in the file. <br />
* If you don't want that a Standard Exercise (using all the Macros in the
* Construction and setting each fraction to 100) will be created, check if
* this is a Exercise with {@link #isExercise()} first. <br>
* Hint will be empty unless specified otherwise with the ExerciseBuilder. <br />
* Fraction will be 0 or 1 unless specified otherwise with the
* ExerciseBuilder. <br />
* Result will be in {@link Result},i.e: <br />
* CORRECT, The assignment is CORRECT <br />
* WRONG, if the assignment is WRONG and we can't tell why <br />
* NOT_ENOUGH_INPUTS if there are not enough input geos, so we cannot check <br />
* WRONG_INPUT_TYPES, if there are enough input geos, but one or more are of
* the wrong type <br />
* WRONG_OUTPUT_TYPE, if there is no output geo matching our macro <br />
* WRONG_AFTER_RANDOMIZE, if the assignment was correct in the first place
* but wrong after randomization <br />
* UNKNOWN, if the assignment could not be checked
*
* @return JavaScriptObject representation of the exercise result. For
* Example: "{"Tool1":{ "result":"CORRECT", "hint":"",
* "fraction":1}}", will be empty if now Macros or Assignments have
* been found.
*/
@Override
public JavaScriptObject getExerciseResult() {
Exercise ex = kernel.getExercise();
ex.checkExercise();
JSONObject result = new JSONObject();
ArrayList<Assignment> parts = ex.getParts();
for (Assignment part : parts) {
JSONObject partresult = new JSONObject();
result.put(part.getDisplayName(), partresult);
partresult.put("result", new JSONString(part.getResult().name()));
String hint = part.getHint();
hint = hint == null ? "" : hint;
partresult.put("hint", new JSONString(hint));
partresult.put("fraction", new JSONNumber(part.getFraction()));
}
return result.getJavaScriptObject();
}
/**
* If you want to make use of the values of random geo a BoolAssignment
* depends on, this is an easy way to retrieve these values and stop
* randomizing them in order to store the same assignment that was presented
* to the student.
*
* @return JavaScriptObject containing all variables and values of which a
* BoolAssignment is depending and stops randomizing all these
* values. Example:
* "Object {level: 1, randNum: 5, a: 1, b: 1, answer: NaN}"
*/
public JavaScriptObject startExercise() {
ArrayList<GeoNumeric> randomizedVars = app.getKernel().getExercise()
.stopRandomizeAndGetValuesForBoolAssignments();
JSONObject vars = new JSONObject();
for (GeoNumeric geo : randomizedVars) {
JSONNumber var = new JSONNumber(geo.getDouble());
vars.put(geo.getLabelSimple(), var);
geo.setRandom(false);
}
return vars.getJavaScriptObject();
}
public void setExternalPath(String s) {
((AppW) app).setExternalPath(s);
}
public void checkSaved(final JavaScriptObject callback) {
((AppW) app).checkSaved(new Runnable() {
@Override
public void run() {
ScriptManagerW.runCallback(callback);
}
});
}
public void setCustomToolBar(String toolbarString) {
GuiManagerInterfaceW gm = ((GuiManagerInterfaceW) app.getGuiManager());
gm.setToolBarDefinition(toolbarString);
gm.setGeneralToolBarDefinition(toolbarString);
gm.updateToolbar();
}
public void getScreenshotBase64(JavaScriptObject callback) {
getScreenshotURL(((AppW) app).getPanel().getElement(), callback);
}
public native void getScreenshotURL(Element el, JavaScriptObject callback)/*-{
var canvas = document.createElement("canvas");
canvas.height = el.offsetHeight;
canvas.width = el.offsetWidth;
var context = canvas.getContext('2d');
el.className = el.className + " ggbScreenshot";
$wnd.domvas.toImage(el, function() {
// Look ma, I just converted this element to an image and can now to funky stuff!
context.drawImage(this, 0, 0);
el.className = el.className.replace(/\bggbScreenshot\b/, '');
callback(@org.geogebra.web.html5.main.GgbAPIW::pngBase64(Ljava/lang/String;)(canvas.toDataURL()));
});
}-*/;
public static native boolean setWorkerURL(String workerUrls,
boolean sync) /*-{
if (workerUrls === "false" || !workerUrls || sync) {
$wnd.zip.useWebWorkers = false;
$wnd.zip.synchronous = sync;
} else {
$wnd.zip.synchronous = false;
$wnd.zip.useWebWorkers = true;
$wnd.zip.workerScriptsPath = workerUrls;
}
return $wnd.zip.useWebWorkers;
}-*/;
native boolean isUsingWebWorkers()/*-{
return $wnd.zip.useWebWorkers;
}-*/;
/**
* GGB-1780 Experimental
*
* @return current construction as SVG
*/
final public String exportSVG() {
EuclidianView ev = app.getActiveEuclidianView();
if (ev instanceof EuclidianViewW) {
EuclidianViewW evw = (EuclidianViewW) ev;
return evw.getExportSVG(1, true);
}
return null;
}
}