mirror of
https://github.com/processing/processing4.git
synced 2026-06-16 04:26:26 +02:00
@@ -273,11 +273,11 @@ public class Base {
|
||||
Mode androidMode =
|
||||
ModeContribution.load(this, getContentFile("modes/android"),
|
||||
"processing.mode.android.AndroidMode").getMode();
|
||||
Mode javaScriptMode =
|
||||
ModeContribution.load(this, getContentFile("modes/javascript"),
|
||||
"processing.mode.javascript.JavaScriptMode").getMode();
|
||||
// Mode javaScriptMode =
|
||||
// ModeContribution.load(this, getContentFile("modes/javascript"),
|
||||
// "processing.mode.javascript.JavaScriptMode").getMode();
|
||||
|
||||
coreModes = new Mode[] { javaMode, androidMode, javaScriptMode };
|
||||
coreModes = new Mode[] { javaMode, androidMode };
|
||||
|
||||
// check for the new mode in case it's available
|
||||
// try {
|
||||
@@ -287,7 +287,7 @@ public class Base {
|
||||
"processing.mode.experimental.ExperimentalMode");
|
||||
if (experimentalContrib != null) {
|
||||
Mode experimentalMode = experimentalContrib.getMode();
|
||||
coreModes = new Mode[] { javaMode, androidMode, javaScriptMode, experimentalMode };
|
||||
coreModes = new Mode[] { javaMode, androidMode, experimentalMode };
|
||||
}
|
||||
// } catch (ClassNotFoundException e) { }
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,548 +0,0 @@
|
||||
package processing.mode.javascript;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.app.Sketch;
|
||||
import processing.app.SketchCode;
|
||||
import processing.app.Toolkit;
|
||||
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
/**
|
||||
* DirectivesEditor, is a simple frontend to Processing.js playback settings a.k.a. "directives".
|
||||
*
|
||||
* @see <a href="http://processingjs.org/reference/pjs%20directive">Processing.js directives</a>
|
||||
*/
|
||||
public class DirectivesEditor
|
||||
{
|
||||
JavaScriptEditor editor;
|
||||
|
||||
JFrame frame;
|
||||
JCheckBox crispBox;
|
||||
JTextField fontField;
|
||||
JCheckBox globalKeyEventsBox;
|
||||
JCheckBox pauseOnBlurBox;
|
||||
JTextField preloadField;
|
||||
//JCheckBox transparentBox;
|
||||
|
||||
private final static ArrayList<String> validKeys = new ArrayList<String>();
|
||||
static {
|
||||
validKeys.add("crisp");
|
||||
validKeys.add("font");
|
||||
validKeys.add("globalKeyEvents");
|
||||
validKeys.add("pauseOnBlur");
|
||||
validKeys.add("preload");
|
||||
validKeys.add("transparent");
|
||||
}
|
||||
private final static int CRISP = 0;
|
||||
private final static int FONT = 1;
|
||||
private final static int GLOBAL_KEY_EVENTS = 2;
|
||||
private final static int PAUSE_ON_BLUR = 3;
|
||||
private final static int PRELOAD = 4;
|
||||
private final static int TRANSPARENT = 5;
|
||||
|
||||
private Pattern pjsPattern;
|
||||
|
||||
public DirectivesEditor ( JavaScriptEditor e )
|
||||
{
|
||||
editor = e;
|
||||
|
||||
if ( frame == null ) createFrame();
|
||||
|
||||
// see processing-1.2.0.js
|
||||
pjsPattern = Pattern.compile(
|
||||
"\\/\\*\\s*@pjs\\s+((?:[^\\*]|\\*+[^\\*\\/])*)\\*\\/\\s*",
|
||||
Pattern.DOTALL );
|
||||
}
|
||||
|
||||
public void show ()
|
||||
{
|
||||
if ( editor.getSketch().isModified())
|
||||
{
|
||||
Base.showWarning( "Directives Editor",
|
||||
"Please save your sketch before changing "+
|
||||
"the directives.", null);
|
||||
return;
|
||||
}
|
||||
|
||||
resetInterface();
|
||||
findRemoveDirectives(false);
|
||||
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private void resetInterface ()
|
||||
{
|
||||
for ( JCheckBox b : new JCheckBox[] {
|
||||
crispBox, globalKeyEventsBox, pauseOnBlurBox /*, transparentBox*/ } )
|
||||
{
|
||||
b.setSelected(false);
|
||||
}
|
||||
for ( JTextField f : new JTextField[]{ fontField, preloadField } )
|
||||
{
|
||||
f.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
public void hide ()
|
||||
{
|
||||
frame.setVisible(false);
|
||||
}
|
||||
|
||||
void applyDirectives ()
|
||||
{
|
||||
findRemoveDirectives(true);
|
||||
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
String head = "", toe = "; \n";
|
||||
|
||||
if ( crispBox.isSelected() )
|
||||
buffer.append( head + "crisp=true" + toe );
|
||||
if ( !fontField.getText().trim().equals("") )
|
||||
buffer.append( head + "font=\""+fontField.getText().trim()+"\"" + toe );
|
||||
if ( globalKeyEventsBox.isSelected() )
|
||||
buffer.append( head + "globalKeyEvents=true" + toe );
|
||||
if ( pauseOnBlurBox.isSelected() )
|
||||
buffer.append( head + "pauseOnBlur=true" + toe );
|
||||
if ( !preloadField.getText().trim().equals("") )
|
||||
buffer.append( head + "preload=\""+preloadField.getText().trim()+"\"" + toe );
|
||||
/*if ( transparentBox.isSelected() )
|
||||
buffer.append( head + "transparent=true" + toe );*/
|
||||
|
||||
Sketch sketch = editor.getSketch();
|
||||
SketchCode code = sketch.getCode(0); // first tab
|
||||
if ( buffer.length() > 0 )
|
||||
{
|
||||
code.setProgram( "/* @pjs " + buffer.toString() + " */\n\n" + code.getProgram() );
|
||||
if ( sketch.getCurrentCode() == code ) // update textarea if on first tab
|
||||
{
|
||||
editor.setText(sketch.getCurrentCode().getProgram());
|
||||
editor.setSelection(0,0);
|
||||
}
|
||||
|
||||
sketch.setModified( false );
|
||||
sketch.setModified( true );
|
||||
}
|
||||
}
|
||||
|
||||
void findRemoveDirectives ( boolean clean )
|
||||
{
|
||||
//if ( clean ) editor.startCompoundEdit();
|
||||
|
||||
Sketch sketch = editor.getSketch();
|
||||
for (int i = 0; i < sketch.getCodeCount(); i++)
|
||||
{
|
||||
SketchCode code = sketch.getCode(i);
|
||||
String program = code.getProgram();
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
||||
Matcher m = pjsPattern.matcher( program );
|
||||
while (m.find())
|
||||
{
|
||||
String mm = m.group();
|
||||
|
||||
// TODO this urgently needs tests ..
|
||||
|
||||
/* remove framing */
|
||||
mm = mm.replaceAll("^\\/\\*\\s*@pjs","").replaceAll("\\s*\\*\\/\\s*$","");
|
||||
/* fix multiline nice formatting */
|
||||
mm = mm.replaceAll("[\\s]*([^;\\s\\n\\r]+)[\\s]*,[\\s]*[\\n\\r]+","$1,");
|
||||
/* fix multiline version without semicolons */
|
||||
mm = mm.replaceAll("[\\s]*([^;\\s\\n\\r]+)[\\s]*[\\n\\r]+","$1;");
|
||||
mm = mm.replaceAll("\n"," ").replaceAll("\r"," ");
|
||||
|
||||
//System.out.println(mm);
|
||||
|
||||
if ( clean )
|
||||
{
|
||||
m.appendReplacement(buffer, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
String[] directives = mm.split(";");
|
||||
for ( String d : directives )
|
||||
{
|
||||
//System.out.println(d);
|
||||
parseDirective(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( clean )
|
||||
{
|
||||
m.appendTail(buffer);
|
||||
|
||||
// TODO: not working!
|
||||
code.setProgram( buffer.toString() );
|
||||
code.setModified( true );
|
||||
}
|
||||
}
|
||||
|
||||
if ( clean )
|
||||
{
|
||||
//editor.stopCompoundEdit();
|
||||
editor.setText(sketch.getCurrentCode().getProgram());
|
||||
sketch.setModified( false );
|
||||
sketch.setModified( true );
|
||||
}
|
||||
}
|
||||
|
||||
private void parseDirective ( String directive )
|
||||
{
|
||||
if ( directive == null )
|
||||
{
|
||||
System.err.println( "Directive is null." );
|
||||
return;
|
||||
}
|
||||
|
||||
String[] pair = directive.split("=");
|
||||
if ( pair == null || pair.length != 2 )
|
||||
{
|
||||
System.err.println("Unable to parse directive: \"" + directive + "\" Ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
String key = pair[0].trim(),
|
||||
value = pair[1].trim();
|
||||
|
||||
// clean these, might have too much whitespace around commas
|
||||
if ( validKeys.indexOf(key) == FONT || validKeys.indexOf(key) == PRELOAD )
|
||||
{
|
||||
value = value.replaceAll("[\\s]*,[\\s]*", ",");
|
||||
}
|
||||
|
||||
if ( validKeys.indexOf(key) == -1 )
|
||||
{
|
||||
System.err.println("Directive key not recognized: \"" + key + "\" Ignored." );
|
||||
return;
|
||||
}
|
||||
if ( value.equals("") )
|
||||
{
|
||||
System.err.println("Directive value empty. Ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
value = value.replaceAll("^\"|\"$","").replaceAll("^'|'$","");
|
||||
|
||||
//System.out.println( key + " = " + value );
|
||||
|
||||
boolean v;
|
||||
switch ( validKeys.indexOf(key) )
|
||||
{
|
||||
case CRISP:
|
||||
v = value.toLowerCase().equals("true");
|
||||
crispBox.setSelected(v);
|
||||
break;
|
||||
case FONT:
|
||||
fontField.setText(value);
|
||||
break;
|
||||
case GLOBAL_KEY_EVENTS:
|
||||
v = value.toLowerCase().equals("true");
|
||||
globalKeyEventsBox.setSelected(v);
|
||||
break;
|
||||
case PAUSE_ON_BLUR:
|
||||
v = value.toLowerCase().equals("true");
|
||||
pauseOnBlurBox.setSelected(v);
|
||||
break;
|
||||
case PRELOAD:
|
||||
preloadField.setText(value);
|
||||
break;
|
||||
case TRANSPARENT:
|
||||
v = value.toLowerCase().equals("true");
|
||||
//transparentBox.setSelected(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void createFrame ()
|
||||
{
|
||||
/* see Preferences.java */
|
||||
int GUI_BIG = 13;
|
||||
int GUI_BETWEEN = 10;
|
||||
int GUI_SMALL = 6;
|
||||
int FIELD_SIZE = 30;
|
||||
|
||||
int left = GUI_BIG;
|
||||
int top = GUI_BIG;
|
||||
int right = 0;
|
||||
|
||||
Dimension d;
|
||||
|
||||
frame = new JFrame("Directives Editor");
|
||||
Container pane = frame.getContentPane();
|
||||
pane.setLayout(null);
|
||||
|
||||
JLabel label = new JLabel("Click here to read about directives.");
|
||||
label.addMouseListener(new MouseListener(){
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
Base.openURL("http://processingjs.org/reference/pjs%20directive");
|
||||
}
|
||||
public void mouseEntered(MouseEvent e) {}
|
||||
public void mouseExited(MouseEvent e) {}
|
||||
public void mousePressed(MouseEvent e) {}
|
||||
public void mouseReleased(MouseEvent e) {}
|
||||
});
|
||||
pane.add(label);
|
||||
d = label.getPreferredSize();
|
||||
label.setBounds(left, top, d.width, d.height);
|
||||
top += d.height + GUI_BETWEEN + GUI_BETWEEN;
|
||||
|
||||
// CRISP
|
||||
|
||||
crispBox =
|
||||
new JCheckBox("\"crisp\": disable antialiasing for line(), triangle() and rect()");
|
||||
pane.add(crispBox);
|
||||
d = crispBox.getPreferredSize();
|
||||
crispBox.setBounds(left, top, d.width + 10, d.height);
|
||||
right = Math.max(right, left + d.width);
|
||||
top += d.height + GUI_BETWEEN;
|
||||
|
||||
// FONTS
|
||||
|
||||
label = new JLabel("\"font\": to load (comma separated)");
|
||||
pane.add(label);
|
||||
d = label.getPreferredSize();
|
||||
label.setBounds(left, top, d.width, d.height);
|
||||
top += d.height + GUI_SMALL;
|
||||
|
||||
fontField = new JTextField(FIELD_SIZE);
|
||||
pane.add(fontField);
|
||||
d = fontField.getPreferredSize();
|
||||
fontField.setBounds(left, top, d.width, d.height);
|
||||
|
||||
JButton button = new JButton("scan");
|
||||
button.addActionListener(new ActionListener(){
|
||||
public void actionPerformed (ActionEvent e) {
|
||||
handleScanFonts();
|
||||
}
|
||||
});
|
||||
pane.add(button);
|
||||
Dimension d2 = button.getPreferredSize();
|
||||
button.setBounds(left + d.width + GUI_SMALL, top, d2.width, d2.height);
|
||||
right = Math.max(right, left + d.width + GUI_SMALL + d2.width);
|
||||
top += d.height + GUI_BETWEEN;
|
||||
|
||||
// GLOBAL_KEY_EVENTS
|
||||
|
||||
globalKeyEventsBox =
|
||||
new JCheckBox("\"globalKeyEvents\": receive global key events");
|
||||
pane.add(globalKeyEventsBox);
|
||||
d = globalKeyEventsBox.getPreferredSize();
|
||||
globalKeyEventsBox.setBounds(left, top, d.width + 10, d.height);
|
||||
right = Math.max(right, left + d.width);
|
||||
top += d.height + GUI_BETWEEN;
|
||||
|
||||
// PAUSE_ON_BLUR
|
||||
|
||||
pauseOnBlurBox =
|
||||
new JCheckBox("\"pauseOnBlur\": pause if applet loses focus");
|
||||
pane.add(pauseOnBlurBox);
|
||||
d = pauseOnBlurBox.getPreferredSize();
|
||||
pauseOnBlurBox.setBounds(left, top, d.width + 10, d.height);
|
||||
right = Math.max(right, left + d.width);
|
||||
top += d.height + GUI_BETWEEN;
|
||||
|
||||
// PRELOAD images
|
||||
|
||||
label = new JLabel("\"preload\": images (comma separated)");
|
||||
pane.add(label);
|
||||
d = label.getPreferredSize();
|
||||
label.setBounds(left, top, d.width, d.height);
|
||||
top += d.height + GUI_SMALL;
|
||||
|
||||
preloadField = new JTextField(FIELD_SIZE);
|
||||
pane.add(preloadField);
|
||||
d = preloadField.getPreferredSize();
|
||||
preloadField.setBounds(left, top, d.width, d.height);
|
||||
|
||||
button = new JButton("scan");
|
||||
button.addActionListener(new ActionListener(){
|
||||
public void actionPerformed (ActionEvent e) {
|
||||
handleScanImages();
|
||||
}
|
||||
});
|
||||
pane.add(button);
|
||||
d2 = button.getPreferredSize();
|
||||
button.setBounds(left + d.width + GUI_SMALL, top, d2.width, d2.height);
|
||||
right = Math.max(right, left + d.width + GUI_SMALL + d2.width);
|
||||
top += d.height + GUI_BETWEEN;
|
||||
|
||||
// TRANSPARENT
|
||||
|
||||
/*transparentBox =
|
||||
new JCheckBox("\"transparent\": set applet background to be transparent");
|
||||
pane.add(transparentBox);
|
||||
d = transparentBox.getPreferredSize();
|
||||
transparentBox.setBounds(left, top, d.width + 10, d.height);
|
||||
right = Math.max(right, left + d.width);
|
||||
top += d.height + GUI_BETWEEN;*/
|
||||
|
||||
// APPLY / OK
|
||||
|
||||
button = new JButton("OK");
|
||||
button.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
applyDirectives();
|
||||
hide();
|
||||
}
|
||||
});
|
||||
pane.add(button);
|
||||
d2 = button.getPreferredSize();
|
||||
int BUTTON_HEIGHT = d2.height;
|
||||
int BUTTON_WIDTH = 80;
|
||||
|
||||
int h = right - (BUTTON_WIDTH + GUI_SMALL + BUTTON_WIDTH);
|
||||
button.setBounds(h, top, BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
h += BUTTON_WIDTH + GUI_SMALL;
|
||||
|
||||
button = new JButton("Cancel");
|
||||
button.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
pane.add(button);
|
||||
button.setBounds(h, top, BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
|
||||
top += BUTTON_HEIGHT + GUI_BETWEEN;
|
||||
|
||||
//frame.getContentPane().add(box);
|
||||
frame.pack();
|
||||
Insets insets = frame.getInsets();
|
||||
frame.setSize(right + GUI_BIG + insets.left + insets.right,
|
||||
top + GUI_SMALL + insets.top + insets.bottom);
|
||||
|
||||
//frame.setResizable(false);
|
||||
|
||||
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent e) {
|
||||
frame.setVisible(false);
|
||||
}
|
||||
});
|
||||
Toolkit.registerWindowCloseKeys(frame.getRootPane(), new ActionListener() {
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
frame.setVisible(false);
|
||||
}
|
||||
});
|
||||
Toolkit.setIcon(frame);
|
||||
}
|
||||
|
||||
void handleScanFonts()
|
||||
{
|
||||
handleScanFiles( fontField, new String[]{
|
||||
"woff", "svg", "eot", "ttf", "otf"
|
||||
});
|
||||
}
|
||||
|
||||
void handleScanImages()
|
||||
{
|
||||
handleScanFiles( preloadField, new String[]{
|
||||
"gif", "jpg", "jpeg", "png", "tga"
|
||||
});
|
||||
}
|
||||
|
||||
void handleScanFiles ( JTextField field, String[] extensions )
|
||||
{
|
||||
String[] dataFiles = scanDataFolderForFilesByType(extensions);
|
||||
if ( dataFiles == null || dataFiles.length == 0 )
|
||||
return;
|
||||
|
||||
String[] oldFileList = field.getText().trim().split(",");
|
||||
ArrayList<String> newFileList = new ArrayList<String>();
|
||||
for ( String c : oldFileList )
|
||||
{
|
||||
c = c.trim();
|
||||
if ( !c.equals("") && newFileList.indexOf(c) == -1 ) // TODO check exists() here?
|
||||
{
|
||||
newFileList.add( c );
|
||||
}
|
||||
}
|
||||
for ( String c : dataFiles )
|
||||
{
|
||||
c = c.trim();
|
||||
if ( !c.equals("") && newFileList.indexOf(c) == -1 )
|
||||
{
|
||||
newFileList.add( c );
|
||||
}
|
||||
}
|
||||
Collections.sort(newFileList);
|
||||
String finalFileList = ""; int i = 0;
|
||||
for ( String s : newFileList )
|
||||
{
|
||||
finalFileList += (i > 0 ? ", " : "") + s;
|
||||
i++;
|
||||
}
|
||||
field.setText(finalFileList);
|
||||
}
|
||||
|
||||
String[] scanDataFolderForFilesByType ( String[] extensions )
|
||||
{
|
||||
ArrayList files = new ArrayList();
|
||||
File dataFolder = editor.getSketch().getDataFolder();
|
||||
|
||||
if ( !dataFolder.exists() ) return null; // TODO no folder present .. warn?
|
||||
|
||||
for ( String ext : extensions )
|
||||
{
|
||||
String[] found = listFiles(dataFolder, true, ext);
|
||||
if ( found == null || found.length == 0 ) continue;
|
||||
|
||||
for ( String f : found )
|
||||
{
|
||||
if ( files.indexOf(f) == -1 )
|
||||
files.add(f);
|
||||
}
|
||||
}
|
||||
|
||||
return (String[])files.toArray(new String[0]);
|
||||
}
|
||||
|
||||
// #718
|
||||
// http://code.google.com/p/processing/issues/detail?id=718
|
||||
private String[] listFiles(File folder, boolean relative, String extension) {
|
||||
String path = folder.getAbsolutePath();
|
||||
Vector<String> vector = new Vector<String>();
|
||||
if (extension != null) {
|
||||
if (!extension.startsWith(".")) {
|
||||
extension = "." + extension;
|
||||
}
|
||||
}
|
||||
listFiles(relative ? (path + File.separator) : "", path, extension, vector);
|
||||
String outgoing[] = new String[vector.size()];
|
||||
vector.copyInto(outgoing);
|
||||
return outgoing;
|
||||
}
|
||||
|
||||
|
||||
private void listFiles(String basePath,
|
||||
String path, String extension,
|
||||
Vector<String> vector) {
|
||||
File folder = new File(path);
|
||||
String[] list = folder.list();
|
||||
if (list != null) {
|
||||
for (String item : list) {
|
||||
if (item.charAt(0) == '.') continue;
|
||||
File file = new File(path, item);
|
||||
String newPath = file.getAbsolutePath();
|
||||
if (newPath.startsWith(basePath)) {
|
||||
newPath = newPath.substring(basePath.length());
|
||||
}
|
||||
if (extension == null || item.toLowerCase().endsWith(extension)) {
|
||||
vector.add(newPath);
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
listFiles(basePath, file.getAbsolutePath(), extension, vector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,636 +0,0 @@
|
||||
package processing.mode.javascript;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileWriter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.app.Mode;
|
||||
import processing.app.Sketch;
|
||||
import processing.app.SketchCode;
|
||||
import processing.app.SketchException;
|
||||
import processing.app.Library;
|
||||
|
||||
import processing.core.PApplet;
|
||||
|
||||
import processing.mode.java.preproc.PdePreprocessor;
|
||||
|
||||
|
||||
public class JavaScriptBuild
|
||||
{
|
||||
public final static String TEMPLATE_FOLDER_NAME = "template";
|
||||
public final static String EXPORTED_FOLDER_NAME = "web-export";
|
||||
public final static String TEMPLATE_FILE_NAME = "template.html";
|
||||
|
||||
public final static String IMPORT_REGEX =
|
||||
"^[\\s]*import[\\s]+([^\\s]+)[\\s]*";
|
||||
|
||||
/**
|
||||
* Answers with the first java doc style comment in the string,
|
||||
* or an empty string if no such comment can be found.
|
||||
*/
|
||||
public static String getDocString ( String s )
|
||||
{
|
||||
String[] javadoc = PApplet.match(s, "/\\*{2,}(.*?)\\*+/");
|
||||
|
||||
if (javadoc != null)
|
||||
{
|
||||
StringBuffer dbuffer = new StringBuffer();
|
||||
String[] pieces = PApplet.split(javadoc[1], '\n');
|
||||
for (String line : pieces) {
|
||||
// if this line starts with * characters, remove 'em
|
||||
String[] m = PApplet.match(line, "^\\s*\\*+(.*)");
|
||||
dbuffer.append(m != null ? m[1] : line);
|
||||
// insert the new line into the html to help w/ line breaks
|
||||
dbuffer.append('\n');
|
||||
}
|
||||
return dbuffer.toString().trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads in a simple template file, with fields of the form '@@somekey@@'
|
||||
* and replaces each field with the value in the map for 'somekey', writing
|
||||
* the output to the output file.
|
||||
*
|
||||
* Keys not in the map will be replaced with empty strings.
|
||||
*
|
||||
* @param template File object mapping to the template
|
||||
* @param output File object handle to the output
|
||||
* @param args template keys, data values to replace them
|
||||
* @throws IOException when there are problems writing to or from the files
|
||||
*/
|
||||
public static void writeTemplate ( File template, File output, Map<String, String> fields )
|
||||
throws IOException
|
||||
{
|
||||
BufferedReader reader = PApplet.createReader(template);
|
||||
PrintWriter writer = PApplet.createWriter(output);
|
||||
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.indexOf("@@") != -1) {
|
||||
StringBuffer sb = new StringBuffer(line);
|
||||
int start = 0, end = 0;
|
||||
while ((start = sb.indexOf("@@")) != -1) {
|
||||
if ((end = sb.indexOf("@@", start+1)) != -1) {
|
||||
String value = fields.get(sb.substring(start+2, end));
|
||||
sb.replace(start, end+2, value == null ? "" : value );
|
||||
} else {
|
||||
Base.showWarning("Problem writing file from template",
|
||||
"The template appears to have an unterminated " +
|
||||
"field. The output may look a little funny.",
|
||||
null);
|
||||
}
|
||||
}
|
||||
line = sb.toString();
|
||||
}
|
||||
writer.println(line);
|
||||
}
|
||||
writer.close();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* The sketch this builder is working on.
|
||||
* <p>
|
||||
* Each builder instance should only work on a single sketch, so if
|
||||
* you have more than one sketch each will need a separate builder.
|
||||
*/
|
||||
protected Sketch sketch;
|
||||
|
||||
protected Mode mode;
|
||||
|
||||
protected File binFolder;
|
||||
|
||||
public JavaScriptBuild ( Sketch sketch )
|
||||
{
|
||||
this.sketch = sketch;
|
||||
this.mode = sketch.getMode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds the sketch
|
||||
* <p>
|
||||
* The process goes a little something like this:
|
||||
* 1. rm -R bin/*
|
||||
* 2. cat *.pde > bin/sketchname.pde
|
||||
* 3. cp -r sketch/data/* bin/ (p.js doesn't recognize the data folder)
|
||||
* 4. series of greps to find height, width, name, desc
|
||||
* 5. cat template.html | sed 's/@@sketch@@/[name]/g' ... [many sed filters] > bin/index.html
|
||||
* </p>
|
||||
*
|
||||
* @param bin the output folder for the built sketch
|
||||
* @return boolean whether the build was successful
|
||||
*/
|
||||
public boolean build ( File bin ) throws IOException, SketchException
|
||||
{
|
||||
// make sure the user isn't playing "hide-the-sketch-folder" again
|
||||
sketch.ensureExistence();
|
||||
|
||||
this.binFolder = bin;
|
||||
|
||||
// we need these ..
|
||||
// JavaScriptMode jsMode = (JavaScriptMode)mode;
|
||||
// JavaScriptEditor jsEditor = (JavaScriptEditor)jsMode.getEditor();
|
||||
// BasicServer jsServer = jsEditor.getServer();
|
||||
|
||||
if ( bin.exists() )
|
||||
{
|
||||
Base.removeDescendants(bin);
|
||||
} //else will be created during preprocesss
|
||||
|
||||
// pass through preprocessor to catch syntax errors
|
||||
// .. exceptions bubble up.
|
||||
preprocess(bin);
|
||||
|
||||
// move the data files, copies contents of sketch/data/ to web-export/
|
||||
if (sketch.hasDataFolder())
|
||||
{
|
||||
try {
|
||||
Base.copyDir(sketch.getDataFolder(), bin);
|
||||
|
||||
} catch (IOException e) {
|
||||
final String msg = "An exception occured while trying to copy the data folder. " +
|
||||
"You may have to manually move the contents of sketch/data to " +
|
||||
"the web-export/ folder. Processing.js doesn't look for a data " +
|
||||
"folder, so lump them together.";
|
||||
Base.showWarning("Problem building the sketch", msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
// as .js files are allowed now include these into the mix,
|
||||
// first find 'em ..
|
||||
String[] sketchFolderFilesRaw = sketch.getFolder().list();
|
||||
String[] sketchFolderFiles = new String[0];
|
||||
ArrayList sffList = new ArrayList();
|
||||
if ( sketchFolderFilesRaw != null )
|
||||
{
|
||||
for ( String s : sketchFolderFilesRaw )
|
||||
{
|
||||
if ( s.toLowerCase().startsWith(".") ) continue;
|
||||
if ( !s.toLowerCase().endsWith(".js") ) continue;
|
||||
sffList.add(s);
|
||||
}
|
||||
if ( sffList.size() > 0 )
|
||||
sketchFolderFiles = (String[])sffList.toArray(new String[0]);
|
||||
}
|
||||
for ( String s : sketchFolderFiles )
|
||||
{
|
||||
try {
|
||||
Base.copyFile( new File(sketch.getFolder(), s), new File(bin, s) );
|
||||
} catch ( IOException ioe ) {
|
||||
String msg = "Unable to copy file: "+s;
|
||||
Base.showWarning("Problem building the sketch", msg, ioe);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Really scrub comments from code?
|
||||
// Con: larger files, PJS needs to do it later
|
||||
// Pro: being literate as we are in a script language.
|
||||
String scrubbed = PdePreprocessor.scrubComments(sketch.getCode(0).getProgram());
|
||||
|
||||
// get width and height
|
||||
int wide = PApplet.DEFAULT_WIDTH;
|
||||
int high = PApplet.DEFAULT_HEIGHT;
|
||||
String[] matches = PApplet.match(scrubbed, PdePreprocessor.SIZE_REGEX);
|
||||
if (matches != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
wide = Integer.parseInt(matches[1]);
|
||||
high = Integer.parseInt(matches[2]);
|
||||
// renderer
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
if ( ((JavaScriptMode)mode).showSizeWarning ) {
|
||||
// found a reference to size, but it didn't seem to contain numbers
|
||||
final String message =
|
||||
"The size of this applet could not automatically be\n" +
|
||||
"determined from your code. You'll have to edit the\n" +
|
||||
"HTML file to set the size of the applet.\n" +
|
||||
"Use only numeric values (not variables) for the size()\n" +
|
||||
"command. See the size() reference for an explanation.";
|
||||
Base.showWarning("Could not find applet size", message, null);
|
||||
// warn only once ..
|
||||
((JavaScriptMode)mode).showSizeWarning = false;
|
||||
}
|
||||
}
|
||||
} // else no size() command found, defaults will be used
|
||||
|
||||
// try resolve imports
|
||||
ArrayList<String> importPackages = new ArrayList<String>();
|
||||
String[] lines = scrubbed.split( "\n" );
|
||||
for ( String l : lines )
|
||||
{
|
||||
int iIndex = l.indexOf( "import" );
|
||||
if ( iIndex != -1 )
|
||||
{
|
||||
String[] iStatements = l.split(";");
|
||||
for ( String iExpression : iStatements )
|
||||
{
|
||||
matches = PApplet.match( iExpression, JavaScriptBuild.IMPORT_REGEX );
|
||||
if ( matches != null && matches.length >= 2 && matches[1] != null )
|
||||
{
|
||||
String iPackage = matches[1];
|
||||
iPackage = iPackage.trim();
|
||||
|
||||
if ( iPackage.indexOf(".*") != -1 ) {
|
||||
// de.bezier.tutto.*
|
||||
iPackage = iPackage.replace( ".*", "" );
|
||||
} else {
|
||||
// de.bezier.uno.SingleClass
|
||||
iPackage = iPackage.replaceAll( "\\.[^.]+$", "" );
|
||||
}
|
||||
if ( !importPackages.contains(iPackage) ) // is this a "==" or ".equals()" ?
|
||||
importPackages.add( iPackage );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArrayList<String> jsImports = new ArrayList<String>();
|
||||
if ( importPackages.size() > 0 )
|
||||
{
|
||||
File libsExport = new File( bin, "libs" );
|
||||
if ( !libsExport.mkdir() )
|
||||
{
|
||||
Base.showWarning( "Error",
|
||||
"Unable to create 'libs' in export folder.",
|
||||
null );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for ( String pack : importPackages )
|
||||
{
|
||||
Library lib = mode.getLibrary( pack );
|
||||
if ( lib != null )
|
||||
{
|
||||
String libPath = lib.getJarPath();
|
||||
File libJar = new File( libPath );
|
||||
if ( libJar.exists() )
|
||||
{
|
||||
File libJS = new File( libJar.getParent(), libJar.getName().replace(".jar",".js") );
|
||||
//System.out.println( libJS.getPath() );
|
||||
if ( libJS.exists() )
|
||||
{
|
||||
String libJSDest = "libs" + File.separator + libJS.getName();
|
||||
File libJSDestFile = new File( bin, libJSDest );
|
||||
if ( libJSDestFile.exists() )
|
||||
{
|
||||
System.out.println( "Duplicate import!" );
|
||||
}
|
||||
try
|
||||
{
|
||||
Base.copyFile( libJS,
|
||||
libJSDestFile );
|
||||
jsImports.add( libJSDest );
|
||||
|
||||
} catch ( Exception se ) {
|
||||
se.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// final prep and write to template.
|
||||
// getTemplateFile() is very important as it looks and preps
|
||||
// any custom templates present in the sketch folder.
|
||||
File templateFile = getTemplateFile();
|
||||
File htmlOutputFile = new File(bin, "index.html");
|
||||
|
||||
Map<String, String> templateFields = new HashMap<String, String>();
|
||||
templateFields.put( "width", String.valueOf(wide) );
|
||||
templateFields.put( "height", String.valueOf(high) );
|
||||
templateFields.put( "sketch", sketch.getName() );
|
||||
templateFields.put( "description", getSketchDescription() );
|
||||
|
||||
// generate an ID for the sketch to use with <canvas id="XXXX"></canvas>
|
||||
String sketchID = sketch.getName().replaceAll("[^a-zA-Z0-9]+", "").replaceAll("^[^a-zA-Z]+","");
|
||||
|
||||
// add a handy method to read the generated sketchID
|
||||
String scriptFiles = "<script type=\"text/javascript\">\n";
|
||||
|
||||
scriptFiles += "// convenience function to get the id attribute of generated sketch html element\n" +
|
||||
"function getProcessingSketchId () { return '"+sketchID+"'; }\n";
|
||||
|
||||
// ArrayList<String> addresses = jsServer.getInetAddresses();
|
||||
// int port = jsServer.getPort();
|
||||
|
||||
// scriptFiles += "var getServerAddresses = function () {\nreturn [\n";
|
||||
// for ( String addr : addresses )
|
||||
// {
|
||||
// scriptFiles += "\"http://" + addr + ":" + port + "/\", \n";
|
||||
// }
|
||||
// scriptFiles += "];\n}\n";
|
||||
|
||||
scriptFiles += "</script>\n";
|
||||
|
||||
// add imports if any ...
|
||||
for ( String importScript : jsImports )
|
||||
{
|
||||
scriptFiles += "<script type=\"text/javascript\" src=\""+importScript+"\"></script>";
|
||||
}
|
||||
|
||||
// main .pde file first
|
||||
String sourceFiles = "<a href=\"" + sketch.getName() + ".pde\">" +
|
||||
sketch.getName() + "</a> ";
|
||||
|
||||
// add all other files (both types: .pde and .js)
|
||||
if ( sketchFolderFiles != null )
|
||||
{
|
||||
for ( String s : sketchFolderFiles )
|
||||
{
|
||||
sourceFiles += "<a href=\"" + s + "\">" + s + "</a> ";
|
||||
scriptFiles += "<script src=\""+ s +"\" type=\"text/javascript\"></script>\n";
|
||||
}
|
||||
}
|
||||
templateFields.put( "source", sourceFiles );
|
||||
templateFields.put( "scripts", scriptFiles );
|
||||
templateFields.put( "id", sketchID );
|
||||
|
||||
// process template replace tokens with content
|
||||
try
|
||||
{
|
||||
writeTemplate(templateFile, htmlOutputFile, templateFields);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
final String msg = "There was a problem writing the html template " +
|
||||
"to the build folder.";
|
||||
Base.showWarning("A problem occured during the build", msg, ioe);
|
||||
return false;
|
||||
}
|
||||
|
||||
// finally, add files processing.js
|
||||
String[] defaultJSFiles = new String[]{
|
||||
"processing.js" /*, "qrcode.js"*/
|
||||
};
|
||||
for ( String defaultJSFile : defaultJSFiles )
|
||||
{
|
||||
try
|
||||
{
|
||||
Base.copyFile( sketch.getMode().getContentFile(
|
||||
TEMPLATE_FOLDER_NAME + File.separator + defaultJSFile
|
||||
),
|
||||
new File( bin, defaultJSFile )
|
||||
);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
final String msg = "There was a problem copying " +defaultJSFile+ " to the " +
|
||||
"build folder. You will have to manually add " +
|
||||
defaultJSFile +" to the build folder before the sketch " +
|
||||
"will run.";
|
||||
Base.showWarning( "There was a problem writing to the build folder", msg, ioe);
|
||||
//return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the template HTML file to use. This also checks for custom
|
||||
* templates that might be living in the sketch folder. If such a "template"
|
||||
* folder exists then it's contents will be copied over to "web-export" and
|
||||
* it's template.html will be used as template.
|
||||
*/
|
||||
private File getTemplateFile ()
|
||||
{
|
||||
File sketchFolder = sketch.getFolder();
|
||||
File customTemplateFolder = new File( sketchFolder, TEMPLATE_FOLDER_NAME );
|
||||
if ( customTemplateFolder.exists() &&
|
||||
customTemplateFolder.isDirectory() &&
|
||||
customTemplateFolder.canRead() )
|
||||
{
|
||||
File appletJsFolder = new File( sketchFolder, EXPORTED_FOLDER_NAME );
|
||||
|
||||
try {
|
||||
//TODO: this is potentially dangerous as it might override files in "web-export"
|
||||
Base.copyDir( customTemplateFolder, appletJsFolder );
|
||||
if ( !(new File( appletJsFolder, TEMPLATE_FILE_NAME )).delete() )
|
||||
{
|
||||
// ignore?
|
||||
}
|
||||
return new File( customTemplateFolder, TEMPLATE_FILE_NAME );
|
||||
} catch ( Exception e ) {
|
||||
String msg = "";
|
||||
Base.showWarning("There was a problem copying your custom template folder", msg, e);
|
||||
return sketch.getMode().getContentFile(
|
||||
TEMPLATE_FOLDER_NAME + File.separator + TEMPLATE_FILE_NAME
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
return sketch.getMode().getContentFile(
|
||||
TEMPLATE_FOLDER_NAME + File.separator + TEMPLATE_FILE_NAME
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Collects the sketch code and runs it by the Java-mode preprocessor
|
||||
* to fish for errors.
|
||||
*
|
||||
* @param bin the output folder
|
||||
*
|
||||
* @see processing.mode.java.JavaBuild#preprocess(java.io.File)
|
||||
*/
|
||||
public void preprocess ( File bin ) throws IOException, SketchException
|
||||
{
|
||||
// COLLECT .pde FILES INTO ONE,
|
||||
// essentially... cat sketchFolder/*.pde > bin/sketchname.pde
|
||||
|
||||
StringBuffer bigCode = new StringBuffer();
|
||||
int bigCount = 0;
|
||||
for (SketchCode sc : sketch.getCode()) {
|
||||
if (sc.isExtension("pde")) {
|
||||
sc.setPreprocOffset(bigCount);
|
||||
bigCode.append(sc.getProgram());
|
||||
bigCode.append('\n');
|
||||
bigCount += sc.getLineCount();
|
||||
}
|
||||
}
|
||||
|
||||
if (!bin.exists()) {
|
||||
bin.mkdirs();
|
||||
}
|
||||
File bigFile = new File(bin, sketch.getName() + ".pde");
|
||||
String bigCodeContents = bigCode.toString();
|
||||
Base.saveFile( bigCodeContents, bigFile );
|
||||
|
||||
// RUN THROUGH JAVA-MODE PREPROCESSOR,
|
||||
// some minor changes made since we are not running the result
|
||||
// but are only interested in any possible errors that may
|
||||
// surface
|
||||
|
||||
PdePreprocessor preprocessor = new PdePreprocessor( sketch.getName() );
|
||||
//PreprocessorResult result;
|
||||
|
||||
try
|
||||
{
|
||||
File outputFolder = sketch.makeTempFolder();
|
||||
final File java = new File( outputFolder, sketch.getName() + ".java" );
|
||||
final PrintWriter stream = new PrintWriter( new FileWriter(java) );
|
||||
try {
|
||||
/*result =*/ preprocessor.write( stream, bigCodeContents, null );
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
fnfe.printStackTrace();
|
||||
String msg = "Build folder disappeared or could not be written";
|
||||
throw new SketchException(msg);
|
||||
|
||||
} catch (antlr.RecognitionException re) {
|
||||
// re also returns a column that we're not bothering with for now
|
||||
|
||||
// first assume that it's the main file
|
||||
int errorLine = re.getLine() - 1;
|
||||
|
||||
// then search through for anyone else whose preprocName is null,
|
||||
// since they've also been combined into the main pde.
|
||||
int errorFile = findErrorFile(errorLine);
|
||||
errorLine -= sketch.getCode(errorFile).getPreprocOffset();
|
||||
|
||||
String msg = re.getMessage();
|
||||
|
||||
if (msg.equals("expecting RCURLY, found 'null'")) {
|
||||
// This can be a problem since the error is sometimes listed as a line
|
||||
// that's actually past the number of lines. For instance, it might
|
||||
// report "line 15" of a 14 line program. Added code to highlightLine()
|
||||
// inside Editor to deal with this situation (since that code is also
|
||||
// useful for other similar situations).
|
||||
throw new SketchException("Found one too many { characters " +
|
||||
"without a } to match it.",
|
||||
errorFile, errorLine, re.getColumn());
|
||||
}
|
||||
|
||||
if (msg.indexOf("expecting RBRACK") != -1) {
|
||||
System.err.println(msg);
|
||||
throw new SketchException("Syntax error, " +
|
||||
"maybe a missing ] character?",
|
||||
errorFile, errorLine, re.getColumn());
|
||||
}
|
||||
|
||||
if (msg.indexOf("expecting SEMI") != -1) {
|
||||
System.err.println(msg);
|
||||
throw new SketchException("Syntax error, " +
|
||||
"maybe a missing semicolon?",
|
||||
errorFile, errorLine, re.getColumn());
|
||||
}
|
||||
|
||||
if (msg.indexOf("expecting RPAREN") != -1) {
|
||||
System.err.println(msg);
|
||||
throw new SketchException("Syntax error, " +
|
||||
"maybe a missing right parenthesis?",
|
||||
errorFile, errorLine, re.getColumn());
|
||||
}
|
||||
|
||||
if (msg.indexOf("preproc.web_colors") != -1) {
|
||||
throw new SketchException("A web color (such as #ffcc00) " +
|
||||
"must be six digits.",
|
||||
errorFile, errorLine, re.getColumn(), false);
|
||||
}
|
||||
|
||||
//System.out.println("msg is " + msg);
|
||||
throw new SketchException(msg, errorFile,
|
||||
errorLine, re.getColumn());
|
||||
|
||||
} catch (antlr.TokenStreamRecognitionException tsre) {
|
||||
// while this seems to store line and column internally,
|
||||
// there doesn't seem to be a method to grab it..
|
||||
// so instead it's done using a regexp
|
||||
|
||||
// TODO not tested since removing ORO matcher.. ^ could be a problem
|
||||
String mess = "^line (\\d+):(\\d+):\\s";
|
||||
|
||||
String[] matches = PApplet.match(tsre.toString(), mess);
|
||||
if (matches != null) {
|
||||
int errorLine = Integer.parseInt(matches[1]) - 1;
|
||||
int errorColumn = Integer.parseInt(matches[2]);
|
||||
|
||||
int errorFile = 0;
|
||||
for (int i = 1; i < sketch.getCodeCount(); i++) {
|
||||
SketchCode sc = sketch.getCode(i);
|
||||
if (sc.isExtension("pde") &&
|
||||
(sc.getPreprocOffset() < errorLine)) {
|
||||
errorFile = i;
|
||||
}
|
||||
}
|
||||
errorLine -= sketch.getCode(errorFile).getPreprocOffset();
|
||||
|
||||
throw new SketchException(tsre.getMessage(),
|
||||
errorFile, errorLine, errorColumn);
|
||||
|
||||
} else {
|
||||
// this is bad, defaults to the main class.. hrm.
|
||||
String msg = tsre.toString();
|
||||
throw new SketchException(msg, 0, -1, -1);
|
||||
}
|
||||
|
||||
} catch (SketchException pe) {
|
||||
// RunnerExceptions are caught here and re-thrown, so that they don't
|
||||
// get lost in the more general "Exception" handler below.
|
||||
throw pe;
|
||||
|
||||
} catch (Exception ex) {
|
||||
// TODO better method for handling this?
|
||||
System.err.println("Uncaught exception type:" + ex.getClass());
|
||||
ex.printStackTrace();
|
||||
throw new SketchException(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from JavaBuild as it is protected there.
|
||||
* @see processing.mode.java.JavaBuild#findErrorFile(int)
|
||||
*/
|
||||
protected int findErrorFile(int errorLine) {
|
||||
for (int i = sketch.getCodeCount() - 1; i > 0; i--) {
|
||||
SketchCode sc = sketch.getCode(i);
|
||||
if (sc.isExtension("pde") && (sc.getPreprocOffset() < errorLine)) {
|
||||
// keep looping until the errorLine is past the offset
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0; // i give up
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the sketch to retrieve it's description. Answers with the first
|
||||
* java doc style comment in the main sketch file, or an empty string if
|
||||
* no such comment exists.
|
||||
*/
|
||||
public String getSketchDescription() {
|
||||
return getDocString(sketch.getCode(0).getProgram());
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Export
|
||||
|
||||
|
||||
/**
|
||||
* Export the sketch to the default "web-export" folder.
|
||||
* @return success of the operation
|
||||
*/
|
||||
public boolean export() throws IOException, SketchException
|
||||
{
|
||||
File webExport = new File(sketch.getFolder(), EXPORTED_FOLDER_NAME);
|
||||
return build( webExport );
|
||||
}
|
||||
}
|
||||
@@ -1,721 +0,0 @@
|
||||
package processing.mode.javascript;
|
||||
|
||||
import processing.mode.javascript.ServingEditor;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
|
||||
import processing.app.*;
|
||||
import processing.mode.java.AutoFormat;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class JavaScriptEditor extends ServingEditor
|
||||
{
|
||||
final static String PROP_KEY_MODE = "mode";
|
||||
final static String PROP_VAL_MODE = "JavaScript";
|
||||
|
||||
private JavaScriptMode jsMode;
|
||||
|
||||
private DirectivesEditor directivesEditor;
|
||||
|
||||
// tapping into Java mode might not be wanted?
|
||||
processing.mode.java.PdeKeyListener listener;
|
||||
|
||||
/**
|
||||
* Constructor, overrides ServingEditor( .. )
|
||||
*
|
||||
* @see processing.mode.javascript.ServingEditor
|
||||
*/
|
||||
protected JavaScriptEditor ( Base base, String path, EditorState state, Mode mode )
|
||||
{
|
||||
super(base, path, state, mode);
|
||||
|
||||
listener = new processing.mode.java.PdeKeyListener(this,textarea);
|
||||
|
||||
jsMode = (JavaScriptMode) mode;
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// abstract Editor implementations
|
||||
// and standard overrides
|
||||
// ----------------------------------------
|
||||
|
||||
/**
|
||||
* Create and return the toolbar (tools above text area),
|
||||
* implements abstract Editor.createToolbar(),
|
||||
* called in Editor constructor to add the toolbar to the window.
|
||||
*
|
||||
* @return an EditorToolbar, in our case a JavaScriptToolbar
|
||||
* @see processing.mode.javascript.JavaScriptToolbar
|
||||
*/
|
||||
public EditorToolbar createToolbar ()
|
||||
{
|
||||
return new JavaScriptToolbar(this, base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a formatter to prettify code,
|
||||
* implements abstract Editor.createFormatter(),
|
||||
* called by Editor.handleAutoFormat() to handle menu item or shortcut
|
||||
*
|
||||
* @return the formatter to handle formatting of code.
|
||||
*/
|
||||
public Formatter createFormatter ()
|
||||
{
|
||||
return new AutoFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the "File" menu,
|
||||
* implements abstract Editor.buildFileMenu(),
|
||||
* called by Editor.buildMenuBar() to generate the app menu for the editor window
|
||||
*
|
||||
* @return JMenu containing the menu items for "File" menu
|
||||
*/
|
||||
public JMenu buildFileMenu ()
|
||||
{
|
||||
JMenuItem exportItem = Toolkit.newJMenuItem("Export", 'E');
|
||||
exportItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleExport( true );
|
||||
}
|
||||
});
|
||||
return buildFileMenu(new JMenuItem[] { exportItem });
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the "Sketch" menu,
|
||||
* implements abstract Editor.buildSketchMenu(),
|
||||
* called by Editor.buildMenuBar() to generate the app menu for the editor window
|
||||
*
|
||||
* @return JMenu containing the menu items for "Sketch" menu
|
||||
*/
|
||||
public JMenu buildSketchMenu ()
|
||||
{
|
||||
JMenuItem startServerItem = Toolkit.newJMenuItem("Run in Browser", 'R');
|
||||
startServerItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleStartServer();
|
||||
}
|
||||
});
|
||||
|
||||
JMenuItem openInBrowserItem = Toolkit.newJMenuItem("Reopen in Browser", 'B');
|
||||
openInBrowserItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleOpenInBrowser();
|
||||
}
|
||||
});
|
||||
|
||||
JMenuItem stopServerItem = new JMenuItem("Stop");
|
||||
stopServerItem.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleStopServer();
|
||||
}
|
||||
});
|
||||
|
||||
return buildSketchMenu(new JMenuItem[] {
|
||||
startServerItem,
|
||||
openInBrowserItem,
|
||||
stopServerItem
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the mode menu,
|
||||
* overrides Editor.buildModeMenu(),
|
||||
* called by Editor.buildMenuBar() to generate the app menu for the editor window
|
||||
*
|
||||
* @return JMenu containing the menu items for "JavaScript" menu
|
||||
*/
|
||||
public JMenu buildModeMenu()
|
||||
{
|
||||
JMenu menu = new JMenu("JavaScript");
|
||||
JMenuItem item;
|
||||
|
||||
item = new JMenuItem("Playback Settings (Directives)");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleShowDirectivesEditor();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
JMenuItem copyServerAddressItem = new JMenuItem("Copy Server Address");
|
||||
copyServerAddressItem.addActionListener(new ActionListener(){
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleCopyServerAddress();
|
||||
}
|
||||
});
|
||||
menu.add( copyServerAddressItem );
|
||||
|
||||
JMenuItem setServerPortItem = new JMenuItem("Set Server Port");
|
||||
setServerPortItem.addActionListener(new ActionListener(){
|
||||
public void actionPerformed (ActionEvent e) {
|
||||
handleSetServerPort();
|
||||
}
|
||||
});
|
||||
menu.add( setServerPortItem );
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
item = new JMenuItem("Start Custom Template");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleCreateCustomTemplate();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem("Show Custom Template");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleOpenCustomTemplateFolder();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the "Help" menu,
|
||||
* implements abstract Editor.buildHelpMenu(),
|
||||
* called by Editor.buildMenuBar() to generate the app menu for the editor window
|
||||
*
|
||||
* @return JMenu containing the menu items for "Help" menu
|
||||
*/
|
||||
public JMenu buildHelpMenu ()
|
||||
{
|
||||
JMenu menu = new JMenu("Help ");
|
||||
JMenuItem item;
|
||||
|
||||
// TODO switch to "http://js.processing.org/"?
|
||||
|
||||
item = new JMenuItem("QuickStart for Processing Devs");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.openURL("http://processingjs.org/reference/articles/p5QuickStart");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem("QuickStart for JavaScript Devs");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.openURL("http://processingjs.org/reference/articles/jsQuickStart");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
/* TODO Implement an environment page
|
||||
item = new JMenuItem("Environment");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
showReference("environment" + File.separator + "index.html");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
*/
|
||||
|
||||
/* TODO Implement a troubleshooting page
|
||||
item = new JMenuItem("Troubleshooting");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.openURL("http://wiki.processing.org/w/Troubleshooting");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
*/
|
||||
|
||||
item = new JMenuItem("Reference");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
//TODO get offline reference archive corresponding to the release
|
||||
// packaged with this mode see: P.js ticket 1146 "Offline Reference"
|
||||
Base.openURL("http://processingjs.org/reference");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
item = Toolkit.newJMenuItemShift("Find in Reference", 'F');
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
//handleFindReferenceImpl();
|
||||
handleFindReference();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
/* TODO FAQ
|
||||
item = new JMenuItem("Frequently Asked Questions");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.openURL("http://wiki.processing.org/w/FAQ");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
*/
|
||||
|
||||
item = new JMenuItem("Visit Processingjs.org");
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Base.openURL("http://processingjs.org/");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
// OSX has its own about menu
|
||||
if (!Base.isMacOS()) {
|
||||
menu.addSeparator();
|
||||
item = new JMenuItem("About Processing");
|
||||
item.addActionListener( new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
base.handleAbout();
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default commenting prefix for comment/uncomment command,
|
||||
* implements abstract Editor.getCommentPrefix(),
|
||||
* called from Editor.handleCommentUncomment()
|
||||
*
|
||||
* @return the comment prefix as String
|
||||
*/
|
||||
public String getCommentPrefix ()
|
||||
{
|
||||
return "//";
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the runner, in our case this is the server,
|
||||
* implements abstract Editor.internalCloseRunner(),
|
||||
* called from Editor.prepareRun()
|
||||
*
|
||||
* Called when the window is going to be reused for another sketch.
|
||||
*/
|
||||
public void internalCloseRunner ()
|
||||
{
|
||||
handleStopServer();
|
||||
if ( directivesEditor != null )
|
||||
{
|
||||
directivesEditor.hide();
|
||||
directivesEditor = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements abstract Editor.deactivateRun()
|
||||
*/
|
||||
public void deactivateRun ()
|
||||
{
|
||||
// not sure what to do here ..
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// handlers ... mainly for menu items
|
||||
// ----------------------------------------
|
||||
|
||||
/**
|
||||
* Menu item callback, let's users set the server port number
|
||||
*/
|
||||
private void handleSetServerPort ()
|
||||
{
|
||||
statusEmpty();
|
||||
|
||||
boolean wasRunning = serverRunning();
|
||||
if ( wasRunning )
|
||||
{
|
||||
statusNotice("Server was running, changing the port requires a restart.");
|
||||
stopServer();
|
||||
}
|
||||
|
||||
setServerPort();
|
||||
saveSketchSettings();
|
||||
|
||||
if ( wasRunning ) {
|
||||
startServer( getExportFolder() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback, copy basic template to sketch folder
|
||||
*/
|
||||
private void handleCreateCustomTemplate ()
|
||||
{
|
||||
Sketch sketch = getSketch();
|
||||
|
||||
File ajs = sketch.getMode().
|
||||
getContentFile( JavaScriptBuild.TEMPLATE_FOLDER_NAME );
|
||||
|
||||
File tjs = getCustomTemplateFolder();
|
||||
|
||||
if ( !tjs.exists() )
|
||||
{
|
||||
try {
|
||||
Base.copyDir( ajs, tjs );
|
||||
statusNotice( "Default template copied." );
|
||||
Base.openFolder( tjs );
|
||||
} catch ( java.io.IOException ioe ) {
|
||||
Base.showWarning("Copy default template folder",
|
||||
"Something went wrong when copying the template folder.", ioe);
|
||||
}
|
||||
}
|
||||
else
|
||||
statusError( "You need to remove the current "+
|
||||
"\""+JavaScriptBuild.TEMPLATE_FOLDER_NAME+"\" "+
|
||||
"folder from the sketch." );
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback, open custom template folder from inside sketch folder
|
||||
*/
|
||||
private void handleOpenCustomTemplateFolder ()
|
||||
{
|
||||
File tjs = getCustomTemplateFolder();
|
||||
if ( tjs.exists() )
|
||||
{
|
||||
Base.openFolder( tjs );
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: promt to create one?
|
||||
statusNotice( "You have no custom template with this sketch. Create one from the menu!" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback, copy server address to clipboard
|
||||
*/
|
||||
private void handleCopyServerAddress ()
|
||||
{
|
||||
String address = getServerAddress();
|
||||
|
||||
if ( address != null )
|
||||
{
|
||||
java.awt.datatransfer.StringSelection stringSelection =
|
||||
new java.awt.datatransfer.StringSelection( address );
|
||||
java.awt.datatransfer.Clipboard clipboard =
|
||||
java.awt.Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
clipboard.setContents( stringSelection, null );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback, open the playback settings frontend
|
||||
*/
|
||||
private void handleShowDirectivesEditor ()
|
||||
{
|
||||
if ( directivesEditor == null )
|
||||
{
|
||||
directivesEditor = new DirectivesEditor(this);
|
||||
}
|
||||
|
||||
directivesEditor.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Catches textarea right-click events,
|
||||
* overrides Editor.showReference()
|
||||
*
|
||||
* @param filename the reference filename to open, provided by keywords.txt
|
||||
*/
|
||||
public void showReference ( String filename )
|
||||
{
|
||||
// TODO: catch handleFindReference directly
|
||||
//handleFindReferenceImpl();
|
||||
|
||||
File file = new File( jsMode.getDefaultMode().getReferenceFolder(), filename );
|
||||
// Prepend with file:// and also encode spaces & other characters
|
||||
Base.openURL( file.toURI().toString() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback, handles showing a reference page.
|
||||
*/
|
||||
/*private void handleFindReferenceImpl ()
|
||||
{
|
||||
if ( textarea.isSelectionActive() ) {
|
||||
Base.openURL(
|
||||
"http://www.google.com/search?q=" +
|
||||
textarea.getSelectedText().trim() +
|
||||
"+site%3Ahttp%3A%2F%2Fprocessingjs.org%2Freference"
|
||||
);
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Menu item callback, replacement for RUN:
|
||||
* export to folder, start server, open in default browser.
|
||||
*/
|
||||
public void handleStartServer ()
|
||||
{
|
||||
statusEmpty();
|
||||
|
||||
if ( !startServer( getExportFolder() ) )
|
||||
{
|
||||
if ( !handleExport( false ) ) return;
|
||||
toolbar.activate(JavaScriptToolbar.RUN);
|
||||
}
|
||||
|
||||
// waiting for server to call "serverStarted() below ..."
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback, open running server address in a browser
|
||||
*/
|
||||
private void handleOpenInBrowser ()
|
||||
{
|
||||
openBrowserForServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback, replacement for STOP: stop server.
|
||||
*/
|
||||
public void handleStopServer ()
|
||||
{
|
||||
stopServer();
|
||||
|
||||
toolbar.deactivate(JavaScriptToolbar.RUN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback, call the export method of the sketch
|
||||
* and handle the gui stuff
|
||||
*/
|
||||
public boolean handleExport ( boolean openFolder )
|
||||
{
|
||||
if ( !handleExportCheckModified() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
toolbar.activate(JavaScriptToolbar.EXPORT);
|
||||
try
|
||||
{
|
||||
boolean success = jsMode.handleExport(sketch);
|
||||
if ( success && openFolder )
|
||||
{
|
||||
File exportFolder = new File( sketch.getFolder(),
|
||||
JavaScriptBuild.EXPORTED_FOLDER_NAME );
|
||||
Base.openFolder( exportFolder );
|
||||
|
||||
statusNotice("Finished exporting.");
|
||||
} else if ( !success ) {
|
||||
// error message already displayed by handleExport
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
statusError(e);
|
||||
toolbar.deactivate(JavaScriptToolbar.EXPORT);
|
||||
return false;
|
||||
}
|
||||
toolbar.deactivate(JavaScriptToolbar.EXPORT);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback, changed from Editor.java to automaticaly
|
||||
* export and handle the server when it's running.
|
||||
* Normal save ops otherwise.
|
||||
*
|
||||
* @param immediately set to false to allow it to be run in a Swing optimized manner
|
||||
*/
|
||||
public boolean handleSave ( boolean immediately )
|
||||
{
|
||||
if (sketch.isUntitled())
|
||||
{
|
||||
return handleSaveAs();
|
||||
}
|
||||
else if (immediately)
|
||||
{
|
||||
handleSave();
|
||||
statusEmpty();
|
||||
if ( serverRunning() ) handleStartServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
SwingUtilities.invokeLater(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
handleSave();
|
||||
statusEmpty();
|
||||
if ( serverRunning() ) handleStartServer();
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from handleSave( true/false )
|
||||
*/
|
||||
public void handleSave ()
|
||||
{
|
||||
toolbar.activate(JavaScriptToolbar.SAVE);
|
||||
handleSaveImpl();
|
||||
toolbar.deactivate(JavaScriptToolbar.SAVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from handleExport()
|
||||
*/
|
||||
private boolean handleExportCheckModified ()
|
||||
{
|
||||
if (sketch.isModified()) {
|
||||
Object[] options = { "OK", "Cancel" };
|
||||
int result = JOptionPane.showOptionDialog(this,
|
||||
"Save changes before export?",
|
||||
"Save",
|
||||
JOptionPane.OK_CANCEL_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE,
|
||||
null,
|
||||
options,
|
||||
options[0]);
|
||||
|
||||
if (result == JOptionPane.OK_OPTION) {
|
||||
handleSave(true);
|
||||
|
||||
} else {
|
||||
statusNotice("Export canceled, changes must first be saved.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback
|
||||
*/
|
||||
public boolean handleSaveAs ()
|
||||
{
|
||||
toolbar.activate(JavaScriptToolbar.SAVE);
|
||||
boolean result = super.handleSaveAs();
|
||||
toolbar.deactivate(JavaScriptToolbar.SAVE);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu item callback for Sketch -> Import Library -> XXX
|
||||
*
|
||||
* Copied from JavaEditor.java
|
||||
*/
|
||||
public void handleImportLibrary ( String jarPath )
|
||||
{
|
||||
// Base.showWarning("Processing.js doesn't support libraries",
|
||||
// "Libraries are not supported. Import statements are " +
|
||||
// "ignored, and code relying on them will break.",
|
||||
// null);
|
||||
|
||||
// make sure the user didn't hide the sketch folder
|
||||
sketch.ensureExistence();
|
||||
|
||||
// import statements into the main sketch file (code[0])
|
||||
// if the current code is a .java file, insert into current
|
||||
if (mode.isDefaultExtension(sketch.getCurrentCode()))
|
||||
{
|
||||
sketch.setCurrentCode(0);
|
||||
}
|
||||
|
||||
// could also scan the text in the file to see if each import
|
||||
// statement is already in there, but if the user has the import
|
||||
// commented out, then this will be a problem.
|
||||
String[] list = Base.packageListFromClassPath(jarPath);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for ( int i = 0; i < list.length; i++ )
|
||||
{
|
||||
buffer.append("import ");
|
||||
buffer.append(list[i]);
|
||||
buffer.append(".*;\n");
|
||||
}
|
||||
buffer.append('\n');
|
||||
buffer.append(getText());
|
||||
setText(buffer.toString());
|
||||
setSelection(0, 0); // scroll to start
|
||||
sketch.setModified(true);
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// implementation BasicServerListener
|
||||
// ----------------------------------------
|
||||
|
||||
/**
|
||||
* BasicServerListener implementation,
|
||||
* called by server once it starts serving
|
||||
*/
|
||||
public void serverStarted ()
|
||||
{
|
||||
super.serverStarted();
|
||||
|
||||
if ( !handleExport( false ) ) return;
|
||||
toolbar.activate(JavaScriptToolbar.RUN);
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// other methods
|
||||
// ----------------------------------------
|
||||
|
||||
/**
|
||||
* Return the current export folder in a sane way
|
||||
*
|
||||
* @return the export folder as File
|
||||
*/
|
||||
private File getExportFolder ()
|
||||
{
|
||||
return new File( getSketch().getFolder(),
|
||||
JavaScriptBuild.EXPORTED_FOLDER_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the custom template folder
|
||||
*
|
||||
* @return the custom template folder as File
|
||||
*/
|
||||
private File getCustomTemplateFolder ()
|
||||
{
|
||||
return new File( getSketch().getFolder(),
|
||||
JavaScriptBuild.TEMPLATE_FOLDER_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current sketch settings, this adds the server port to them
|
||||
*/
|
||||
private void saveSketchSettings ()
|
||||
{
|
||||
statusEmpty();
|
||||
|
||||
File sketchProps = getSketchPropertiesFile();
|
||||
Settings settings;
|
||||
|
||||
try {
|
||||
settings = new Settings(sketchProps);
|
||||
} catch ( IOException ioe ) {
|
||||
ioe.printStackTrace();
|
||||
return;
|
||||
}
|
||||
if ( settings == null )
|
||||
{
|
||||
statusError( "Unable to create sketch properties file!" );
|
||||
return;
|
||||
}
|
||||
settings.set( PROP_KEY_MODE, PROP_VAL_MODE );
|
||||
|
||||
int port = getServerPort();
|
||||
if ( port > 0 ) settings.set( PROP_KEY_SERVER_PORT, (port+"") );
|
||||
|
||||
settings.save();
|
||||
}
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
package processing.mode.javascript;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import processing.app.*;
|
||||
import processing.mode.java.JavaMode;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.*;
|
||||
|
||||
/**
|
||||
* JS Mode for Processing based on Processing.js. Comes with a server as
|
||||
* replacement for the normal runner.
|
||||
*/
|
||||
public class JavaScriptMode extends Mode
|
||||
{
|
||||
// show that warning only once per run-cycle as we are
|
||||
// continously exporting behind the scenes at every save
|
||||
public boolean showSizeWarning = true;
|
||||
|
||||
private JavaScriptEditor jsEditor;
|
||||
private JavaMode defaultJavaMode;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param base the Processing editor base
|
||||
* @param folder the folder that this mode is started from
|
||||
*/
|
||||
public JavaScriptMode ( Base base, File folder )
|
||||
{
|
||||
super(base, folder);
|
||||
|
||||
// try {
|
||||
// loadKeywords(); // in JavaMode, sets tokenMarker
|
||||
// loadAdditionalKeywords(
|
||||
// new File(Base.getContentFile("modes/java"), "keywords.txt" ),
|
||||
// tokenMarker
|
||||
// );
|
||||
// }
|
||||
// catch ( IOException e )
|
||||
// {
|
||||
// Base.showError( "Problem loading keywords",
|
||||
// "Could not load keywords.txt, please re-install Processing.", e);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to create the actual editor when needed (once per Sketch)
|
||||
*/
|
||||
public Editor createEditor( Base base, String path, EditorState state )
|
||||
{
|
||||
jsEditor = new JavaScriptEditor( base, path, state, this );
|
||||
|
||||
return jsEditor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from Base to get the Editor for this mode.
|
||||
*/
|
||||
public Editor getEditor ()
|
||||
{
|
||||
return jsEditor;
|
||||
}
|
||||
|
||||
public JavaMode getDefaultMode ()
|
||||
{
|
||||
if ( defaultJavaMode == null ) {
|
||||
for ( Mode m : base.getModeList() )
|
||||
{
|
||||
if ( m.getClass() == JavaMode.class )
|
||||
{
|
||||
defaultJavaMode = (JavaMode)m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultJavaMode;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Loads default Java keywords, JS keywords
|
||||
// * were already loaded in constructor.
|
||||
// */
|
||||
// protected void loadAdditionalKeywords ( File keywords, PdeKeywords tokenMarker ) throws IOException
|
||||
// {
|
||||
// if ( keywordToReference == null )
|
||||
// keywordToReference = new HashMap<String, String>();
|
||||
//
|
||||
// BufferedReader reader = PApplet.createReader( keywords );
|
||||
// String line = null;
|
||||
// while ((line = reader.readLine()) != null)
|
||||
// {
|
||||
// String[] pieces = PApplet.trim(PApplet.split(line, '\t'));
|
||||
// if (pieces.length >= 2)
|
||||
// {
|
||||
// String keyword = pieces[0];
|
||||
// String coloring = pieces[1];
|
||||
// if (coloring.length() > 0) {
|
||||
// tokenMarker.addColoring(keyword, coloring);
|
||||
// }
|
||||
// if (pieces.length == 3) {
|
||||
// String htmlFilename = pieces[2];
|
||||
// if (htmlFilename.length() > 0) {
|
||||
// keywordToReference.put(keyword, htmlFilename);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * load the keywords from file, copied from JavaMode.java
|
||||
// */
|
||||
// protected void loadKeywords() throws IOException
|
||||
// {
|
||||
// File file = new File(folder, "keywords.txt");
|
||||
// BufferedReader reader = PApplet.createReader(file);
|
||||
//
|
||||
// tokenMarker = new PdeKeywords();
|
||||
// keywordToReference = new HashMap<String, String>();
|
||||
//
|
||||
// String line = null;
|
||||
// while ((line = reader.readLine()) != null) {
|
||||
// String[] pieces = PApplet.trim(PApplet.split(line, '\t'));
|
||||
// if (pieces.length >= 2) {
|
||||
// String keyword = pieces[0];
|
||||
// String coloring = pieces[1];
|
||||
//
|
||||
// if (coloring.length() > 0) {
|
||||
// tokenMarker.addColoring(keyword, coloring);
|
||||
// }
|
||||
// if (pieces.length == 3) {
|
||||
// String htmlFilename = pieces[2];
|
||||
// if (htmlFilename.length() > 0) {
|
||||
// keywordToReference.put(keyword, htmlFilename);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
public File[] getKeywordFiles() {
|
||||
return new File[] {
|
||||
Base.getContentFile("modes/java/keywords.txt"),
|
||||
new File(folder, "keywords.txt")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * Override getTokenMarker in Mode
|
||||
// */
|
||||
// public TokenMarker getTokenMarker ()
|
||||
// {
|
||||
// if ( tokenMarker == null )
|
||||
// tokenMarker = new PdeKeywords();
|
||||
// return tokenMarker;
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* Return pretty title of this mode for menu listing and such
|
||||
*/
|
||||
public String getTitle()
|
||||
{
|
||||
return "JavaScript";
|
||||
}
|
||||
|
||||
// public EditorToolbar createToolbar(Editor editor) { }
|
||||
|
||||
// public Formatter createFormatter() { }
|
||||
|
||||
// public Editor createEditor(Base ibase, String path, int[] location) { }
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
/**
|
||||
* Fetch and return examples from JS and Java mode
|
||||
*/
|
||||
public File[] getExampleCategoryFolders()
|
||||
{
|
||||
// find included example subdirs
|
||||
File[] inclExamples = examplesFolder.listFiles(new java.io.FileFilter(){
|
||||
public boolean accept (File f) {
|
||||
// only the subfolders
|
||||
return f.isDirectory();
|
||||
}
|
||||
});
|
||||
java.util.Arrays.sort(inclExamples);
|
||||
|
||||
// add JavaMode examples as these are supposed to run in JSMode
|
||||
JavaMode jMode = getDefaultMode();
|
||||
if ( jMode == null )
|
||||
return inclExamples; // js examples only
|
||||
|
||||
File jExamples = jMode.getContentFile("examples");
|
||||
File[] jModeExamples = new File[] {
|
||||
new File(jExamples, "Basics"),
|
||||
//new File(jExamples, "Topics"),
|
||||
//new File(jExamples, "3D") ,
|
||||
//new File(jExamples, "Books")
|
||||
};
|
||||
|
||||
// merge them all
|
||||
File[] finalExamples = new File[inclExamples.length + jModeExamples.length];
|
||||
for ( int i = 0; i < inclExamples.length; i++ )
|
||||
finalExamples[i] = inclExamples[i];
|
||||
for ( int i = 0; i < jModeExamples.length; i++ )
|
||||
finalExamples[inclExamples.length+i] = jModeExamples[i];
|
||||
|
||||
java.util.Arrays.sort(finalExamples);
|
||||
|
||||
return finalExamples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overriding this from Mode.java to remove "Contributed Libraries"
|
||||
*/
|
||||
|
||||
public JTree buildExamplesTree()
|
||||
{
|
||||
JTree superTree = super.buildExamplesTree();
|
||||
|
||||
DefaultTreeModel model = (DefaultTreeModel) superTree.getModel();
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
|
||||
DefaultMutableTreeNode contribExamples = (DefaultMutableTreeNode) root.getLastChild();
|
||||
Object title = contribExamples.getUserObject();
|
||||
if ( title != null && title.getClass() == String.class && ((String)title).equals("Contributed Libraries") )
|
||||
{
|
||||
root.remove( contribExamples );
|
||||
}
|
||||
|
||||
return superTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default extension for this mode, same as Java
|
||||
*/
|
||||
public String getDefaultExtension()
|
||||
{
|
||||
return "pde";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return allowed extensions
|
||||
*/
|
||||
public String[] getExtensions ()
|
||||
{
|
||||
return new String[] { "pde", "js" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of file- / folder-names that should be ignored when
|
||||
* sketch is being copied or saved as
|
||||
*/
|
||||
public String[] getIgnorable ()
|
||||
{
|
||||
return new String[] {
|
||||
"applet",
|
||||
"applet_js",
|
||||
JavaScriptBuild.EXPORTED_FOLDER_NAME
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Override Mode.getLibrary to add our own discovery of JS-only libraries.
|
||||
*
|
||||
* fjenett 20121202
|
||||
*/
|
||||
public Library getLibrary ( String pkgName ) throws SketchException
|
||||
{
|
||||
return super.getLibrary( pkgName );
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------
|
||||
|
||||
/**
|
||||
* Build and export a sketch
|
||||
*/
|
||||
public boolean handleExport(Sketch sketch) throws IOException, SketchException
|
||||
{
|
||||
JavaScriptBuild build = new JavaScriptBuild(sketch);
|
||||
return build.export();
|
||||
}
|
||||
|
||||
//public boolean handleExportApplet(Sketch sketch) throws SketchException, IOException { }
|
||||
|
||||
//public boolean handleExportApplication(Sketch sketch) throws SketchException, IOException { }
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package processing.mode.javascript;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.app.Editor;
|
||||
import processing.app.EditorToolbar;
|
||||
|
||||
public class JavaScriptToolbar extends EditorToolbar {
|
||||
|
||||
static protected final int RUN = 0;
|
||||
static protected final int STOP = 1;
|
||||
|
||||
static protected final int NEW = 2;
|
||||
static protected final int OPEN = 3;
|
||||
static protected final int SAVE = 4;
|
||||
static protected final int EXPORT = 5;
|
||||
|
||||
|
||||
static public String getTitle ( int index, boolean shift )
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case RUN: return "Start server";
|
||||
case STOP: return "Stop server";
|
||||
case NEW: return "New";
|
||||
case OPEN: return "Open";
|
||||
case SAVE: return "Save";
|
||||
case EXPORT: return "Export for Web";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public JavaScriptToolbar ( Editor editor, Base base )
|
||||
{
|
||||
super(editor, base);
|
||||
}
|
||||
|
||||
|
||||
public void init ()
|
||||
{
|
||||
Image[][] images = loadImages();
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
addButton( getTitle(i, false), getTitle(i, true), images[i], i == NEW );
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePressed ( MouseEvent e, int index )
|
||||
{
|
||||
// boolean shift = e.isShiftDown();
|
||||
JavaScriptEditor jsEditor = (JavaScriptEditor) editor;
|
||||
|
||||
switch (index) {
|
||||
|
||||
case RUN:
|
||||
jsEditor.handleStartServer();
|
||||
break;
|
||||
|
||||
case STOP:
|
||||
jsEditor.handleStopServer();
|
||||
break;
|
||||
|
||||
case OPEN:
|
||||
JPopupMenu popup = editor.getMode().getToolbarMenu().getPopupMenu();
|
||||
popup.show(this, e.getX(), e.getY());
|
||||
break;
|
||||
|
||||
case NEW:
|
||||
// if (shift) {
|
||||
base.handleNew();
|
||||
// } else {
|
||||
// base.handleNewReplace();
|
||||
// }
|
||||
break;
|
||||
|
||||
case SAVE:
|
||||
jsEditor.handleSave(false);
|
||||
break;
|
||||
|
||||
case EXPORT:
|
||||
jsEditor.handleExport( true );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
package processing.mode.javascript;
|
||||
|
||||
import processing.mode.javascript.BasicServer;
|
||||
import processing.app.Base;
|
||||
import processing.app.Mode;
|
||||
import processing.app.Editor;
|
||||
import processing.app.EditorState;
|
||||
import processing.app.Settings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
/**
|
||||
* This is the basis for the JavaScript mode, an editor that serves files from a
|
||||
* given root directory.
|
||||
*/
|
||||
public abstract class ServingEditor extends Editor implements BasicServerListener
|
||||
{
|
||||
public final static String PROP_KEY_SERVER_PORT = "basicserver.port";
|
||||
|
||||
BasicServer server;
|
||||
|
||||
public boolean showSizeWarning = true;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param base the Processing Base this runs of
|
||||
* @param path the path to a sketch to open
|
||||
* @param editor state
|
||||
* @param the mode to open in
|
||||
* @see processing.app.Editor
|
||||
* @see processing.app.Base
|
||||
*/
|
||||
protected ServingEditor ( Base base, String path, EditorState state, Mode mode )
|
||||
{
|
||||
super( base, path, state, mode );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the server port, shows an input dialog to enter a port number
|
||||
*/
|
||||
protected void setServerPort ()
|
||||
{
|
||||
String pString = null;
|
||||
String msg = "Set the server port (1024 < port < 65535)";
|
||||
int currentPort = -1;
|
||||
|
||||
if ( server != null ) currentPort = server.getPort();
|
||||
|
||||
if ( currentPort > 0 )
|
||||
pString = JOptionPane.showInputDialog( msg, (currentPort+"") );
|
||||
else
|
||||
pString = JOptionPane.showInputDialog( msg );
|
||||
|
||||
if ( pString == null ) return;
|
||||
|
||||
int port = -1;
|
||||
try {
|
||||
port = Integer.parseInt(pString);
|
||||
} catch ( Exception e ) {
|
||||
// sending foobar? you lil' hacker you ...
|
||||
statusError("That number was not okay ..");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( port < BasicServer.MIN_PORT || port > BasicServer.MAX_PORT )
|
||||
{
|
||||
statusError( "That port number is out of range" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( server != null )
|
||||
{
|
||||
server.setPort(port);
|
||||
}
|
||||
|
||||
File sketchProps = getSketchPropertiesFile();
|
||||
if ( sketchProps.exists() ) {
|
||||
try {
|
||||
Settings settings = new Settings(sketchProps);
|
||||
settings.set( PROP_KEY_SERVER_PORT, (port + "") );
|
||||
settings.save();
|
||||
} catch ( IOException ioe ) {
|
||||
statusError(ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter, returns the server port
|
||||
*
|
||||
* @return the server port as int or -1
|
||||
*/
|
||||
public int getServerPort ()
|
||||
{
|
||||
if ( server != null ) return server.getPort();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter, returns the current server address
|
||||
*
|
||||
* @return the server address as URL string or null
|
||||
*/
|
||||
public String getServerAddress ()
|
||||
{
|
||||
if ( server != null && server.isRunning() ) return server.getAddress();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter, returns the server
|
||||
*
|
||||
* @return the BasicServer of this editor
|
||||
*/
|
||||
public BasicServer getServer ()
|
||||
{
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* A toggle to start/stop the server
|
||||
*
|
||||
* @param root the root folder to start from if it needs to be started
|
||||
*/
|
||||
protected void startStopServer ( File root )
|
||||
{
|
||||
if ( serverRunning() )
|
||||
{
|
||||
stopServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
startServer( root );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a server to server from given root dir
|
||||
*
|
||||
* @param root the root folder to server from
|
||||
* @return the BasicServer instance running or created
|
||||
*/
|
||||
protected BasicServer createServer ( File root )
|
||||
{
|
||||
if ( server != null ) return server;
|
||||
|
||||
if ( !root.exists() && !root.mkdir() )
|
||||
{
|
||||
// bad .. let server handle the complaining ..
|
||||
}
|
||||
|
||||
server = new BasicServer( root );
|
||||
server.addListener( this );
|
||||
|
||||
File sketchProps = getSketchPropertiesFile();
|
||||
if ( sketchProps.exists() ) {
|
||||
try {
|
||||
Settings props = new Settings(sketchProps);
|
||||
String portString = props.get( PROP_KEY_SERVER_PORT );
|
||||
if ( portString != null && !portString.trim().equals("") )
|
||||
{
|
||||
int port = Integer.parseInt(portString);
|
||||
server.setPort(port);
|
||||
}
|
||||
} catch ( IOException ioe ) {
|
||||
statusError(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the internal server for this sketch.
|
||||
*
|
||||
* @param root the root folder for the server to serve from
|
||||
* @return true if it was started anew, false if it was running
|
||||
*/
|
||||
protected boolean startServer ( File root )
|
||||
{
|
||||
if ( server != null &&
|
||||
( !server.isRunning() || !server.getRoot().equals(root) ) )
|
||||
{
|
||||
// if server hung or something else went wrong .. stop it.
|
||||
server.shutDown();
|
||||
server = null;
|
||||
}
|
||||
|
||||
if ( server == null )
|
||||
{
|
||||
server = createServer( root );
|
||||
}
|
||||
|
||||
if ( !server.isRunning() )
|
||||
{
|
||||
server.setRoot( root );
|
||||
server.start();
|
||||
statusNotice( "Waiting for server to start ..." );
|
||||
}
|
||||
else if ( server.isRunning() )
|
||||
{
|
||||
statusNotice( "Server running (" +
|
||||
server.getAddress() +
|
||||
"), reload your browser window." );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if server is running
|
||||
*
|
||||
* @return true if server is running
|
||||
*/
|
||||
protected boolean serverRunning ()
|
||||
{
|
||||
return server != null && server.isRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop server
|
||||
*/
|
||||
protected void stopServer ()
|
||||
{
|
||||
if ( serverRunning() ) server.shutDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or get the sketch's properties file
|
||||
*
|
||||
* @return the sketch properties file or null
|
||||
*/
|
||||
protected File getSketchPropertiesFile ()
|
||||
{
|
||||
File sketchPropsFile = new File( getSketch().getFolder(), "sketch.properties");
|
||||
if ( !sketchPropsFile.exists() )
|
||||
{
|
||||
try {
|
||||
sketchPropsFile.createNewFile();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
statusError( "Unable to create sketch properties file!" );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return sketchPropsFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new browser window or tab with the server address
|
||||
*/
|
||||
protected void openBrowserForServer ()
|
||||
{
|
||||
if ( serverRunning() )
|
||||
{
|
||||
Base.openURL( server.getAddress() );
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// interface BasicServerListener
|
||||
// ------------------------------------------
|
||||
|
||||
/**
|
||||
* interface BasicServerListener
|
||||
* Called after the server was started from the server thread
|
||||
*/
|
||||
public void serverStarted ()
|
||||
{
|
||||
String location = server.getAddress();
|
||||
statusNotice( "Server started: " + location );
|
||||
openBrowserForServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* interface BasicServerListener
|
||||
* Called from server thread after the server stopped
|
||||
*/
|
||||
public void serverStopped ()
|
||||
{
|
||||
statusNotice( "Server stopped." );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user