Merge pull request #1665 from fjenett/master

Bye-bye JS Mode
This commit is contained in:
Ben Fry
2013-04-11 05:20:11 -07:00
77 changed files with 7 additions and 17262 deletions
+5 -5
View File
@@ -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." );
}
}