diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java
index 2990e1af7..f574ff826 100644
--- a/app/src/processing/app/Base.java
+++ b/app/src/processing/app/Base.java
@@ -34,6 +34,7 @@ import javax.swing.tree.*;
import processing.core.*;
import processing.mode.android.AndroidMode;
+import processing.mode.javascript.JavaScriptMode;
import processing.mode.java.*;
@@ -215,9 +216,9 @@ public class Base {
// TODO this will be dynamically loading modes in no time
defaultMode = new JavaMode(this, getContentFile("modes/java"));
Mode androidMode = new AndroidMode(this, getContentFile("modes/android"));
-// Mode javaScriptMode = new JavaScriptMode(this, getContentFile("modes/javascript"));
-// modeList = new Mode[] { defaultMode, androidMode, javaScriptMode };
- modeList = new Mode[] { defaultMode, androidMode };
+ Mode javaScriptMode = new JavaScriptMode(this, getContentFile("modes/javascript"));
+ modeList = new Mode[] { defaultMode, androidMode, javaScriptMode };
+// modeList = new Mode[] { defaultMode, androidMode };
// Get the sketchbook path, and make sure it's set properly
determineSketchbookFolder();
diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java
index 7ec43dcc4..92c801309 100644
--- a/app/src/processing/app/Editor.java
+++ b/app/src/processing/app/Editor.java
@@ -58,7 +58,7 @@ public abstract class Editor extends JFrame implements RunnerListener {
/**
* true if this file has not yet been given a name by the user
*/
- boolean untitled;
+ protected boolean untitled;
private PageFormat pageFormat;
private PrinterJob printerJob;
diff --git a/app/src/processing/mode/javascript/DirectivesEditor.java b/app/src/processing/mode/javascript/DirectivesEditor.java
new file mode 100644
index 000000000..dce55f386
--- /dev/null
+++ b/app/src/processing/mode/javascript/DirectivesEditor.java
@@ -0,0 +1,538 @@
+package processing.mode.javascript;
+
+import processing.mode.java.JavaEditor;
+import processing.app.Base;
+import processing.app.Sketch;
+import processing.app.SketchCode;
+
+import processing.core.PApplet;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.border.*;
+
+import java.io.*;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Vector;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/* http://processingjs.org/reference/pjs%20directive */
+
+public class DirectivesEditor
+{
+ JavaScriptEditor editor;
+
+ static JFrame frame;
+ JCheckBox crispBox;
+ JTextField fontField;
+ JCheckBox globalKeyEventsBox;
+ JCheckBox pauseOnBlurBox;
+ JTextField preloadField;
+ JCheckBox transparentBox;
+
+ private final static ArrayList validKeys = new ArrayList();
+ 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);
+ //editor.statusError("Please save your sketch before changing the directives.");
+ 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();
+
+ /* remove framing */
+ mm = mm.replaceAll("^\\/\\*\\s*@pjs","").replaceAll("\\s*\\*\\/\\s*$","");
+ /* fix multiline version without semicolons */
+ mm = mm.replaceAll("([^;\\s\\n\\r]+)[\\s]*[\\n\\r]+","$1;");
+ mm = mm.replaceAll("\n","").replaceAll("\r","");
+
+ 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();
+
+ 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);
+ }
+ });
+ Base.registerWindowCloseKeys(frame.getRootPane(), new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ frame.setVisible(false);
+ }
+ });
+ Base.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 newFileList = new ArrayList();
+ 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 vector = new Vector();
+ 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 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);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/processing/mode/javascript/JavaScriptBuild.java b/app/src/processing/mode/javascript/JavaScriptBuild.java
index ea3796550..76f2f97e9 100644
--- a/app/src/processing/mode/javascript/JavaScriptBuild.java
+++ b/app/src/processing/mode/javascript/JavaScriptBuild.java
@@ -4,33 +4,41 @@ 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.core.PApplet;
+
import processing.mode.java.JavaBuild;
+import processing.mode.java.preproc.PdePreprocessor;
+import processing.mode.java.preproc.PreprocessorResult;
-/**
- * A collection of static methods to aid in building a
- * web page with Processing.js from a Sketch object.
- */
-public class JavaScriptBuild {
-
- // public static final String SIZE_REGEX
-
- // public static final String PACKAGE_REGEX
+public class JavaScriptBuild
+{
+ public final static String TEMPLATE_FOLDER_NAME = "template_js";
+ public final static String EXPORTED_FOLDER_NAME = "applet_js";
+ public final static String TEMPLATE_FILE_NAME = "template.html";
/**
* 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) {
+ public static String getDocString ( String s )
+ {
String[] javadoc = PApplet.match(s, "/\\*{2,}(.*?)\\*+/");
- if (javadoc != null) {
+
+ if (javadoc != null)
+ {
StringBuffer dbuffer = new StringBuffer();
String[] pieces = PApplet.split(javadoc[1], '\n');
for (String line : pieces) {
@@ -58,9 +66,11 @@ public class JavaScriptBuild {
* @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 fields) throws IOException {
+ public static void writeTemplate ( File template, File output, Map fields )
+ throws IOException
+ {
BufferedReader reader = PApplet.createReader(template);
- PrintWriter theOutWriter = PApplet.createWriter(output);
+ PrintWriter writer = PApplet.createWriter(output);
String line = null;
while ((line = reader.readLine()) != null) {
@@ -80,9 +90,9 @@ public class JavaScriptBuild {
}
line = sb.toString();
}
- theOutWriter.println(line);
+ writer.println(line);
}
- theOutWriter.close();
+ writer.close();
}
// -----------------------------------------------------
@@ -100,8 +110,8 @@ public class JavaScriptBuild {
protected File binFolder;
-
- public JavaScriptBuild(Sketch sketch) {
+ public JavaScriptBuild ( Sketch sketch )
+ {
this.sketch = sketch;
this.mode = sketch.getMode();
}
@@ -116,32 +126,45 @@ public class JavaScriptBuild {
* 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
+ *
*
* @param bin the output folder for the built sketch
* @return boolean whether the build was successful
*/
- public boolean build(File bin) {
+ public boolean build ( File bin )
+ {
// make sure the user isn't playing "hide-the-sketch-folder" again
sketch.ensureExistence();
this.binFolder = bin;
- if (bin.exists()) {
+ if ( bin.exists() )
+ {
Base.removeDescendants(bin);
} //else will be created during preprocesss
- try {
+ // pass through preprocessor to catch syntax errors
+ try
+ {
preprocess(bin);
- } catch (IOException e) {
- final String msg = "A problem occured while writing to the output folder.";
- Base.showWarning("Could not build the sketch", msg, e);
- return false;
- }
- // move the data files
- if (sketch.hasDataFolder()) {
+ } catch ( IOException ioe ) {
+ final String msg = "A problem occured while writing to the output folder.";
+ Base.showWarning("Could not build the sketch", msg, ioe);
+ return false;
+
+ } catch ( SketchException se ) {
+ final String msg = "The preprocessor found a problem in your code.";
+ Base.showWarning("Could not build the sketch", msg, se);
+ return false;
+ }
+
+ // move the data files, copies contents of sketch/data/ to applet_js/
+ 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 " +
@@ -151,20 +174,52 @@ public class JavaScriptBuild {
}
}
- // TODO Code folder contents ending in .js could be moved and added as script tags?
+ // 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;
+ }
+ }
// get width and height
int wide = PApplet.DEFAULT_WIDTH;
int high = PApplet.DEFAULT_HEIGHT;
+ // 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 = JavaBuild.scrubComments(sketch.getCode(0).getProgram());
String[] matches = PApplet.match(scrubbed, JavaBuild.SIZE_REGEX);
- if (matches != null) {
- try {
+ if (matches != null)
+ {
+ try
+ {
wide = Integer.parseInt(matches[1]);
high = Integer.parseInt(matches[2]);
// renderer
+
} catch (NumberFormatException e) {
// found a reference to size, but it didn't seem to contain numbers
final String message =
@@ -177,21 +232,47 @@ public class JavaScriptBuild {
}
} // else no size() command found, defaults will be used
- // final prep and write to template
- File templateFile = sketch.getMode().getContentFile("applet_js/template.html");
+ // 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 templateFields = new HashMap();
- templateFields.put("width", String.valueOf(wide));
- templateFields.put("height", String.valueOf(high));
- templateFields.put("sketch", sketch.getName());
- templateFields.put("description", getSketchDescription());
- templateFields.put("source",
- "" +
- sketch.getName() + "");
+ templateFields.put( "width", String.valueOf(wide) );
+ templateFields.put( "height", String.valueOf(high) );
+ templateFields.put( "sketch", sketch.getName() );
+ templateFields.put( "description", getSketchDescription() );
- try{
+ // generate an ID for the sketch to use with
+ String sketchID = sketch.getName().replaceAll("[^a-zA-Z0-9]+", "").replaceAll("^[^a-zA-Z]+","");
+ // add a handy method to read the generated sketchID
+ String scriptFiles = "\n";
+
+ // main .pde file first
+ String sourceFiles = "" +
+ sketch.getName() + " ";
+
+ // add all other files (both types: .pde and .js)
+ if ( sketchFolderFiles != null )
+ {
+ for ( String s : sketchFolderFiles )
+ {
+ sourceFiles += "" + s + " ";
+ scriptFiles += "\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.";
@@ -200,9 +281,14 @@ public class JavaScriptBuild {
}
// finally, add Processing.js
- try {
- Base.copyFile(sketch.getMode().getContentFile("applet_js/processing.js"),
- new File(bin, "processing.js"));
+ try
+ {
+ Base.copyFile( sketch.getMode().getContentFile(
+ EXPORTED_FOLDER_NAME+"/processing.js"
+ ),
+ new File( bin, "processing.js")
+ );
+
} catch (IOException ioe) {
final String msg = "There was a problem copying processing.js to the " +
"build folder. You will have to manually add " +
@@ -214,14 +300,59 @@ public class JavaScriptBuild {
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_js"
+ * folder exists then it's contents will be copied over to "applet_js" 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 applet_js
+ 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(
+ EXPORTED_FOLDER_NAME + File.separator + TEMPLATE_FILE_NAME
+ );
+ }
+ }
+ else
+ return sketch.getMode().getContentFile(
+ EXPORTED_FOLDER_NAME + File.separator + TEMPLATE_FILE_NAME
+ );
+ }
/**
- * Prepares the sketch code objects for use with Processing.js
+ * 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 {
+ public void preprocess ( File bin ) throws IOException, SketchException
+ {
+ // COLLECT .pde FILES INTO ONE,
// essentially... cat sketchFolder/*.pde > bin/sketchname.pde
+
StringBuffer bigCode = new StringBuffer();
for (SketchCode sc : sketch.getCode()){
if (sc.isExtension("pde")) {
@@ -234,9 +365,150 @@ public class JavaScriptBuild {
bin.mkdirs();
}
File bigFile = new File(bin, sketch.getName() + ".pde");
- Base.saveFile(bigCode.toString(), bigFile);
+ 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 = 1; i < sketch.getCodeCount(); 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
@@ -256,9 +528,10 @@ public class JavaScriptBuild {
* Export the sketch to the default applet_js folder.
* @return success of the operation
*/
- public boolean export() throws IOException {
- File applet_js = new File(sketch.getFolder(), "applet_js");
- return exportApplet_js(applet_js);
+ public boolean export() throws IOException
+ {
+ File applet_js = new File(sketch.getFolder(), EXPORTED_FOLDER_NAME);
+ return exportToFolder( applet_js );
}
@@ -266,7 +539,8 @@ public class JavaScriptBuild {
* Export the sketch to the provided folder
* @return success of the operation
*/
- public boolean exportApplet_js(File appletfolder) throws IOException {
- return build(appletfolder);
+ public boolean exportToFolder( File exportFolder ) throws IOException
+ {
+ return build( exportFolder );
}
}
diff --git a/app/src/processing/mode/javascript/JavaScriptEditor.java b/app/src/processing/mode/javascript/JavaScriptEditor.java
index d99d2c16f..444cadae8 100644
--- a/app/src/processing/mode/javascript/JavaScriptEditor.java
+++ b/app/src/processing/mode/javascript/JavaScriptEditor.java
@@ -11,26 +11,49 @@ import javax.swing.JOptionPane;
import processing.app.Base;
import processing.app.Editor;
import processing.app.EditorToolbar;
+import processing.app.Sketch;
import processing.app.Formatter;
import processing.app.Mode;
import processing.mode.java.AutoFormat;
+import processing.mode.java.JavaEditor;
-public class JavaScriptEditor extends Editor {
+
+import javax.swing.*;
+
+public class JavaScriptEditor extends Editor
+{
+
private JavaScriptMode jsMode;
+ private JavaScriptServer jsServer;
-
- protected JavaScriptEditor(Base base, String path, int[] location, Mode mode) {
+ private DirectivesEditor directivesEditor;
+
+ // TODO how to handle multiple servers
+ // TODO read settings from sketch.properties
+ // NOTE 0.0.0.0 does not work on XP
+ private static final String localDomain = "http://127.0.0.1";
+
+ // tapping into Java mode might not be wanted?
+ processing.mode.java.PdeKeyListener listener;
+
+ protected JavaScriptEditor ( Base base, String path, int[] location, Mode mode )
+ {
super(base, path, location, mode);
+
+ listener = new processing.mode.java.PdeKeyListener(this,textarea);
+
jsMode = (JavaScriptMode) mode;
}
- public EditorToolbar createToolbar() {
+ public EditorToolbar createToolbar ()
+ {
return new JavaScriptToolbar(this, base);
}
- public Formatter createFormatter() {
+ public Formatter createFormatter ()
+ {
return new AutoFormat();
}
@@ -39,26 +62,79 @@ public class JavaScriptEditor extends Editor {
// Menu methods
- public JMenu buildFileMenu() {
+ public JMenu buildFileMenu ()
+ {
JMenuItem exportItem = Base.newJMenuItem("export title", 'E');
exportItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
- handleExport();
+ handleExport( true );
}
});
return buildFileMenu(new JMenuItem[] { exportItem });
}
- public JMenu buildSketchMenu() {
- return buildSketchMenu(new JMenuItem[] {});
+ public JMenu buildSketchMenu ()
+ {
+ JMenuItem startServerItem = Base.newJMenuItem("Start server", 'R');
+ startServerItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ handleStartServer();
+ }
+ });
+
+ JMenuItem stopServerItem = new JMenuItem("Stop server");
+ stopServerItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ handleStopServer();
+ }
+ });
+
+ JMenuItem showDirectivesWindow = new JMenuItem("Directives");
+ showDirectivesWindow.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ handleShowDirectivesEditor();
+ }
+ });
+
+ JMenuItem copyTemplate = new JMenuItem("Copy template to sketch");
+ copyTemplate.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ Sketch sketch = getSketch();
+ File ajs = sketch.getMode().getContentFile(JavaScriptBuild.EXPORTED_FOLDER_NAME);
+ File tjs = new File( sketch.getFolder(), JavaScriptBuild.TEMPLATE_FOLDER_NAME );
+ 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." );
+ }
+ });
+
+ return buildSketchMenu(new JMenuItem[] {
+ startServerItem, stopServerItem,
+ showDirectivesWindow, copyTemplate
+ });
}
- public JMenu buildHelpMenu() {
+ public JMenu buildHelpMenu ()
+ {
JMenu menu = new JMenu("Help ");
JMenuItem item;
+ // TODO switch to "http://js.processing.org/"?
+
item = new JMenuItem("QuickStart for JS Devs");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
@@ -108,13 +184,7 @@ public class JavaScriptEditor extends Editor {
item = Base.newJMenuItemShift("Find in Reference", 'F');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
- if (textarea.isSelectionActive()) {
- Base.openURL(
- "http://www.google.com/search?q=" +
- textarea.getSelectedText() +
- "+site%3Ahttp%3A%2F%2Fprocessingjs.org%2Freference"
- );
- }
+ handleFindReferenceHACK();
}
});
menu.add(item);
@@ -156,25 +226,129 @@ public class JavaScriptEditor extends Editor {
// - - - - - - - - - - - - - - - - - -
- public String getCommentPrefix() {
+ public String getCommentPrefix ()
+ {
return "//";
}
// - - - - - - - - - - - - - - - - - -
+
+ private void handleShowDirectivesEditor ()
+ {
+ if ( directivesEditor == null )
+ {
+ directivesEditor = new DirectivesEditor(this);
+ }
+
+ directivesEditor.show();
+ }
+
+ // this catches the textarea right-click events
+ public void showReference( String filename )
+ {
+ // TODO: catch handleFindReference directly
+ handleFindReferenceHACK();
+ }
+
+ private void handleFindReferenceHACK ()
+ {
+ if (textarea.isSelectionActive()) {
+ Base.openURL(
+ "http://www.google.com/search?q=" +
+ textarea.getSelectedText().trim() +
+ "+site%3Ahttp%3A%2F%2Fprocessingjs.org%2Freference"
+ );
+ }
+ }
+
+ public void handleStartStopServer ()
+ {
+ if ( jsServer != null && jsServer.isRunning())
+ {
+ handleStopServer();
+ }
+ else
+ {
+ handleStartServer();
+ }
+ }
+ /**
+ * Replacement for RUN:
+ * export to folder, start server, open in default browser.
+ */
+ public void handleStartServer ()
+ {
+ handleExport( false );
+
+ File serverRoot = new File(sketch.getFolder(), JavaScriptBuild.EXPORTED_FOLDER_NAME);
+
+ // if server hung or something else went wrong .. stop it.
+ if ( jsServer != null && (!jsServer.isRunning() || !jsServer.getRoot().equals(serverRoot)) )
+ {
+ jsServer.shutDown();
+ jsServer = null;
+ }
+
+ if ( jsServer == null )
+ {
+ jsServer = new JavaScriptServer( serverRoot );
+ jsServer.start();
+ System.out.println( "Server started." );
+
+ while ( !jsServer.isRunning() ) {}
+
+ String location = localDomain + ":" + jsServer.getPort() + "/";
+ System.out.println( location );
+
+ if ( !System.getProperty("os.name").startsWith("Mac OS") )
+ Base.openURL( location );
+ else
+ {
+ try {
+ String scpt = "osascript -e "+
+ "\"tell application \\\"Finder\\\" to open location \\\"" + location + "\\\"\"";
+ String[] cmd = { "/bin/bash", "-c", scpt };
+ Process process = new ProcessBuilder( cmd ).start();
+ } catch ( Exception e ) {
+ Base.openURL( location );
+ }
+ }
+ }
+ else if ( jsServer.isRunning() )
+ {
+ System.out.println( "Server running, reload your browser window." );
+ System.out.println( localDomain + ":" + jsServer.getPort() + "/" );
+ }
+ toolbar.activate(JavaScriptToolbar.RUN);
+ }
+
+ /**
+ * Replacement for STOP: stop server.
+ */
+ public void handleStopServer ()
+ {
+ if ( jsServer != null && jsServer.isRunning() )
+ jsServer.shutDown();
+
+ System.out.println("Server stopped.");
+ toolbar.deactivate(JavaScriptToolbar.RUN);
+ }
/**
* Call the export method of the sketch and handle the gui stuff
*/
- public void handleExport() {
+ public void handleExport ( boolean openFolder )
+ {
if (handleExportCheckModified()) {
toolbar.activate(JavaScriptToolbar.EXPORT);
try {
boolean success = jsMode.handleExport(sketch);
- if (success) {
- File appletJSFolder = new File(sketch.getFolder(), "applet_js");
+ if ( success && openFolder ) {
+ File appletJSFolder = new File(sketch.getFolder(), JavaScriptBuild.EXPORTED_FOLDER_NAME );
Base.openFolder(appletJSFolder);
+
statusNotice("Finished exporting.");
} else {
// error message already displayed by handleExport
@@ -186,8 +360,34 @@ public class JavaScriptEditor extends Editor {
}
}
+ /**
+ * Changed from Editor.java to automaticaly export and
+ * handle the server when it's running. Normal save ops otherwise.
+ */
+ public boolean handleSaveRequest(boolean immediately)
+ {
+ if (untitled) {
+ return handleSaveAs();
+ // need to get the name, user might also cancel here
+
+ } else if (immediately) {
+ handleSave();
+ if ( jsServer != null && jsServer.isRunning() )
+ handleStartServer();
+ } else {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ handleSave();
+ if ( jsServer != null && jsServer.isRunning() )
+ handleStartServer();
+ }
+ });
+ }
+ return true;
+ }
- public boolean handleExportCheckModified() {
+ public boolean handleExportCheckModified ()
+ {
if (sketch.isModified()) {
Object[] options = { "OK", "Cancel" };
int result = JOptionPane.showOptionDialog(this,
@@ -215,14 +415,16 @@ public class JavaScriptEditor extends Editor {
}
- public void handleSave() {
+ public void handleSave ()
+ {
toolbar.activate(JavaScriptToolbar.SAVE);
super.handleSave();
toolbar.deactivate(JavaScriptToolbar.SAVE);
}
- public boolean handleSaveAs() {
+ public boolean handleSaveAs ()
+ {
toolbar.activate(JavaScriptToolbar.SAVE);
boolean result = super.handleSaveAs();
toolbar.deactivate(JavaScriptToolbar.SAVE);
@@ -230,18 +432,17 @@ public class JavaScriptEditor extends Editor {
}
- public void handleImportLibrary(String item) {
+ public void handleImportLibrary (String item)
+ {
Base.showWarning("Processing.js doesn't support libraries",
"Libraries are not supported. Import statements are " +
"ignored, and code relying on them will break.",
null);
}
-
/** JavaScript mode has no runner. This method is empty. */
- public void internalCloseRunner() { }
-
+ public void internalCloseRunner () { }
/** JavaScript mode does not run anything. This method is empty. */
- public void deactivateRun() { }
+ public void deactivateRun () { }
}
diff --git a/app/src/processing/mode/javascript/JavaScriptMode.java b/app/src/processing/mode/javascript/JavaScriptMode.java
index 2f5dc29b5..8a869c277 100644
--- a/app/src/processing/mode/javascript/JavaScriptMode.java
+++ b/app/src/processing/mode/javascript/JavaScriptMode.java
@@ -13,19 +13,16 @@ import processing.app.syntax.PdeKeywords;
import processing.core.PApplet;
/**
- * JS Mode is very simple. Since P.js is dependent on a browser there is
- * no runner, just an export so that users can debug in the browser of their
- * choice.
+ * JS Mode for Processing based on Processing.js. Comes with a server as
+ * replacement for the normal runner.
*/
public class JavaScriptMode extends Mode {
-
// create a new editor with the mode
public Editor createEditor(Base base, String path, int[] location) {
return new JavaScriptEditor(base, path, location, this);
}
-
public JavaScriptMode(Base base, File folder) {
super(base, folder);
@@ -37,7 +34,6 @@ public class JavaScriptMode extends Mode {
}
}
-
protected void loadKeywords() throws IOException {
File file = new File(folder, "keywords.txt");
BufferedReader reader = PApplet.createReader(file);
@@ -102,16 +98,16 @@ public class JavaScriptMode extends Mode {
return "pde";
}
-
// all file extensions it supports
public String[] getExtensions() {
- return new String[] {"pde", "pjs"};
+ return new String[] {"pde", "js"};
}
-
public String[] getIgnorable() {
return new String[] {
- "applet_js" // not sure what color to paint this bike shed
+ "applet",
+ "applet_js",
+ "template_js"
};
}
diff --git a/app/src/processing/mode/javascript/JavaScriptServer.java b/app/src/processing/mode/javascript/JavaScriptServer.java
new file mode 100644
index 000000000..164d8f96b
--- /dev/null
+++ b/app/src/processing/mode/javascript/JavaScriptServer.java
@@ -0,0 +1,692 @@
+package processing.mode.javascript;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * Based on Sun tutorial at: http://bit.ly/fpoHAF
+ *
+ * Changed to accept a document root.
+ */
+class JavaScriptServer implements HttpConstants, Runnable
+{
+ Thread thread = null;
+ ServerSocket server = null;
+
+ private ArrayList threads = new ArrayList();
+ private int workers = 5;
+
+ private File virtualRoot;
+ private int timeout = 5000;
+ private int port = 0; /* using whatever port is available */
+
+ private boolean running = false, inited = false;
+ private boolean stopping = false;
+
+ JavaScriptServer ( File root )
+ {
+ if ( virtualRoot == null && root.exists() && root.canRead() )
+ {
+ virtualRoot = root;
+ }
+ }
+
+ public File getRoot ()
+ {
+ return virtualRoot;
+ }
+
+ public void setRoot ( File root )
+ {
+ virtualRoot = root;
+ }
+
+ public int getTimeout ()
+ {
+ return timeout;
+ }
+
+ public int getPort ()
+ {
+ return port;
+ }
+
+ public void start ()
+ {
+ thread = null;
+ thread = new Thread(this, "ProcessingJSServer");
+ thread.start();
+ }
+
+ public void restart ()
+ {
+ if ( running )
+ shutDown();
+
+ start();
+ }
+
+ // http://bit.ly/eA8iGj
+ public void shutDown ()
+ {
+ if ( threads != null )
+ {
+ for ( Worker w : threads )
+ {
+ w.stop();
+ }
+ }
+
+ thread = null;
+ try {
+ if ( server != null )
+ server.close();
+ } catch ( Exception e ) {;}
+ }
+
+ public boolean isRunning ()
+ {
+ return running && inited;
+ }
+
+ public void run ()
+ {
+ if ( virtualRoot == null )
+ {
+ System.err.println( "ProcessingJSServer: virtual root is null." );
+ return;
+ }
+
+ try
+ {
+ running = true;
+ server = new ServerSocket( 0 );
+
+ port = server.getLocalPort();
+
+ inited = true;
+
+ while ( thread != null )
+ {
+ Socket s = server.accept();
+
+ Worker ws = new Worker( virtualRoot );
+ ws.setSocket(s);
+ (new Thread(ws, "ProcessingJSServer Worker")).start();
+ }
+ } catch ( IOException ioe ) {
+ //ioe.printStackTrace();
+ }
+ running = false;
+ }
+}
+
+
+class Worker extends JavaScriptServer
+implements HttpConstants, Runnable
+{
+ final static int BUF_SIZE = 2048;
+
+ static final byte[] EOL = {(byte)'\r', (byte)'\n' };
+
+ /* buffer to use for requests */
+ byte[] buf;
+ /* Socket to client we're handling */
+ private Socket s;
+
+ private boolean stopping = false;
+
+ Worker ( File root )
+ {
+ super( root );
+ buf = new byte[BUF_SIZE];
+ s = null;
+ }
+
+ synchronized void setSocket( Socket s )
+ {
+ this.s = s;
+ notify();
+ }
+
+ void stop ()
+ {
+ stopping = true;
+ notify();
+ }
+
+ public synchronized void run ()
+ {
+ while ( true && !stopping )
+ {
+ if (s == null)
+ {
+ /* nothing to do */
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ /* should not happen */
+ continue;
+ }
+ }
+ if ( s != null && !stopping )
+ {
+ try {
+ handleClient();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ s = null;
+ return;
+ }
+ }
+
+ void handleClient() throws IOException {
+ InputStream is = new BufferedInputStream(s.getInputStream());
+ PrintStream ps = new PrintStream(s.getOutputStream());
+ /* we will only block in read for this many milliseconds
+ * before we fail with java.io.InterruptedIOException,
+ * at which point we will abandon the connection.
+ */
+ s.setSoTimeout( getTimeout() );
+ s.setTcpNoDelay(true);
+ /* zero out the buffer from last time */
+ for (int i = 0; i < BUF_SIZE; i++) {
+ buf[i] = 0;
+ }
+ try {
+ /* We only support HTTP GET/HEAD, and don't
+ * support any fancy HTTP options,
+ * so we're only interested really in
+ * the first line.
+ */
+ int nread = 0, r = 0;
+
+outerloop:
+ while (nread < BUF_SIZE) {
+ r = is.read(buf, nread, BUF_SIZE - nread);
+ if (r == -1) {
+ /* EOF */
+ return;
+ }
+ int i = nread;
+ nread += r;
+ for (; i < nread; i++) {
+ if (buf[i] == (byte)'\n' || buf[i] == (byte)'\r') {
+ /* read one line */
+ break outerloop;
+ }
+ }
+ }
+
+ /* are we doing a GET or just a HEAD */
+ boolean doingGet;
+ /* beginning of file name */
+ int index;
+ if (buf[0] == (byte)'G' &&
+ buf[1] == (byte)'E' &&
+ buf[2] == (byte)'T' &&
+ buf[3] == (byte)' ') {
+ doingGet = true;
+ index = 4;
+ } else if (buf[0] == (byte)'H' &&
+ buf[1] == (byte)'E' &&
+ buf[2] == (byte)'A' &&
+ buf[3] == (byte)'D' &&
+ buf[4] == (byte)' ') {
+ doingGet = false;
+ index = 5;
+ } else {
+ /* we don't support this method */
+ ps.print("HTTP/1.0 " + HTTP_BAD_METHOD +
+ " unsupported method type: ");
+ ps.write(buf, 0, 5);
+ ps.write(EOL);
+ ps.flush();
+ s.close();
+ return;
+ }
+
+ int i = 0;
+ /* find the file name, from:
+ * GET /foo/bar.html HTTP/1.0
+ * extract "/foo/bar.html"
+ */
+ for (i = index; i < nread; i++) {
+ if (buf[i] == (byte)' ') {
+ break;
+ }
+ }
+ String fname = (new String(buf, 0, index, i-index)).replace('/', File.separatorChar);
+ if (fname.startsWith(File.separator))
+ {
+ fname = fname.substring(1);
+ }
+ fname = java.net.URLDecoder.decode(fname);
+ File targ = new File( getRoot(), fname );
+ if (targ.isDirectory())
+ {
+ File ind = new File(targ, "index.html");
+ if (ind.exists()) {
+ targ = ind;
+ }
+ }
+ boolean OK = printHeaders(targ, ps);
+ if (doingGet)
+ {
+ if (OK) {
+ sendFile(targ, ps);
+ } else {
+ send404(targ, ps);
+ }
+ }
+ } finally {
+ s.close();
+ }
+ }
+
+ boolean printHeaders(File targ, PrintStream ps) throws IOException {
+ boolean ret = false;
+ int rCode = 0;
+ if (!targ.exists()) {
+ rCode = HTTP_NOT_FOUND;
+ ps.print("HTTP/1.0 " + HTTP_NOT_FOUND + " not found");
+ ps.write(EOL);
+ ret = false;
+ } else {
+ rCode = HTTP_OK;
+ ps.print("HTTP/1.0 " + HTTP_OK+" OK");
+ ps.write(EOL);
+ ret = true;
+ }
+ //System.out.println( "From " +s.getInetAddress().getHostAddress()+": GET " +
+ // targ.getAbsolutePath()+" --> "+rCode);
+
+ ps.print("Server: Simple java");
+ ps.write(EOL);
+ ps.print("Date: " + (new Date()));
+ ps.write(EOL);
+ if (ret) {
+ if (!targ.isDirectory()) {
+ ps.print("Content-length: "+targ.length());
+ ps.write(EOL);
+ ps.print("Last Modified: " + (new
+ Date(targ.lastModified())));
+ ps.write(EOL);
+ String name = targ.getName();
+ int ind = name.lastIndexOf('.');
+ String ct = null;
+ if (ind > 0) {
+ ct = (String) map.get(name.substring(ind));
+ }
+ if (ct == null) {
+ ct = "unknown/unknown";
+ }
+ ps.print("Content-type: " + ct);
+ ps.write(EOL);
+ } else {
+ ps.print("Content-type: text/html");
+ ps.write(EOL);
+ }
+ }
+ return ret;
+ }
+
+ void send404(File targ, PrintStream ps) throws IOException {
+ ps.write(EOL);
+ ps.write(EOL);
+ ps.println("Not Found\n\n"+
+ "The requested resource was not found.\n");
+ }
+
+ void sendFile(File targ, PrintStream ps) throws IOException {
+ InputStream is = null;
+ ps.write(EOL);
+ if (targ.isDirectory()) {
+ listDirectory(targ, ps);
+ return;
+ } else {
+ is = new FileInputStream(targ.getAbsolutePath());
+ }
+
+ try {
+ int n;
+ while ((n = is.read(buf)) > 0) {
+ ps.write(buf, 0, n);
+ }
+ } finally {
+ is.close();
+ }
+ }
+
+ /* mapping of file extensions to content-types */
+ static java.util.Hashtable map = new java.util.Hashtable();
+
+ static {
+ fillMap();
+ }
+
+ static void setSuffix(String k, String v) {
+ map.put(k, v);
+ }
+
+ static void fillMap()
+ {
+ // this probably can be shortened a lot since this is not a normal server ..
+ setSuffix("", "content/unknown");
+ setSuffix(".3dm", "x-world/x-3dmf");
+ setSuffix(".3dmf", "x-world/x-3dmf");
+ setSuffix(".ai", "application/pdf");
+ setSuffix(".aif", "audio/x-aiff");
+ setSuffix(".aifc", "audio/x-aiff");
+ setSuffix(".aiff", "audio/x-aiff");
+ setSuffix(".asc", "text/plain");
+ setSuffix(".asd", "application/astound");
+ setSuffix(".asn", "application/astound");
+ setSuffix(".atom", "application/atom+xml");
+ setSuffix(".au", "audio/basic");
+ setSuffix(".avi", "video/x-msvideo");
+ setSuffix(".avi", "video/x-msvideo");
+ setSuffix(".bcpio", "application/x-bcpio");
+ setSuffix(".bin", "application/octet-stream");
+ setSuffix(".bmp", "image/bmp");
+ setSuffix(".c", "text/plain");
+ setSuffix(".c++", "text/plain");
+ setSuffix(".cab", "application/x-shockwave-flash");
+ setSuffix(".cc", "text/plain");
+ setSuffix(".cdf", "application/x-netcdf");
+ setSuffix(".cgm", "image/cgm");
+ setSuffix(".chm", "application/mshelp");
+ setSuffix(".cht", "audio/x-dspeeh");
+ setSuffix(".class", "application/octet-stream");
+ setSuffix(".cod", "image/cis-cod");
+ setSuffix(".com", "application/octet-stream");
+ setSuffix(".cpio", "application/x-cpio");
+ setSuffix(".cpt", "application/mac-compactpro");
+ setSuffix(".csh", "application/x-csh");
+ setSuffix(".css", "text/css");
+ setSuffix(".csv", "text/comma-separated-values");
+ setSuffix(".dcr", "application/x-director");
+ setSuffix(".dif", "video/x-dv");
+ setSuffix(".dir", "application/x-director");
+ setSuffix(".djv", "image/vnd.djvu");
+ setSuffix(".djvu", "image/vnd.djvu");
+ setSuffix(".dll", "application/octet-stream");
+ setSuffix(".dmg", "application/octet-stream");
+ setSuffix(".dms", "application/octet-stream");
+ setSuffix(".doc", "application/msword");
+ setSuffix(".dot", "application/msword");
+ setSuffix(".dtd", "application/xml-dtd");
+ setSuffix(".dus", "audio/x-dspeeh");
+ setSuffix(".dv", "video/x-dv");
+ setSuffix(".dvi", "application/x-dvi");
+ setSuffix(".dwf", "drawing/x-dwf");
+ setSuffix(".dwg", "application/acad");
+ setSuffix(".dxf", "application/dxf");
+ setSuffix(".dxr", "application/x-director");
+ setSuffix(".eps", "application/pdf");
+ setSuffix(".es", "audio/echospeech");
+ setSuffix(".etx", "text/x-setext");
+ setSuffix(".etx", "text/x-setext");
+ setSuffix(".evy", "application/x-envoy");
+ setSuffix(".exe", "application/octet-stream");
+ setSuffix(".ez", "application/andrew-inset");
+ setSuffix(".fh4", "image/x-freehand");
+ setSuffix(".fh5", "image/x-freehand");
+ setSuffix(".fhc", "image/x-freehand");
+ setSuffix(".fif", "image/fif");
+ setSuffix(".gif", "image/gif");
+ setSuffix(".gram", "application/srgs");
+ setSuffix(".grxml", "application/srgs+xml");
+ setSuffix(".gtar", "application/x-gtar");
+ setSuffix(".gtar", "application/x-gtar");
+ setSuffix(".gz", "application/gzip");
+ setSuffix(".h", "text/plain");
+ setSuffix(".hdf", "application/x-hdf");
+ setSuffix(".hlp", "application/mshelp");
+ setSuffix(".hqx", "application/mac-binhex40");
+ setSuffix(".htm", "text/html");
+ setSuffix(".html", "text/html");
+ setSuffix(".ice", "x-conference/x-cooltalk");
+ setSuffix(".ico", "image/x-icon");
+ setSuffix(".ics", "text/calendar");
+ setSuffix(".ief", "image/ief");
+ setSuffix(".ifb", "text/calendar");
+ setSuffix(".iges", "model/iges");
+ setSuffix(".igs", "model/iges");
+ setSuffix(".java", "text/plain");
+ setSuffix(".jnlp", "application/x-java-jnlp-file");
+ setSuffix(".jp2", "image/jp2");
+ setSuffix(".jpe", "image/jpeg");
+ setSuffix(".jpeg", "image/jpeg");
+ setSuffix(".jpg", "image/jpeg");
+ setSuffix(".js", "text/javascript");
+ setSuffix(".kar", "audio/midi");
+ setSuffix(".latex", "application/x-latex");
+ setSuffix(".latex", "application/x-latex");
+ setSuffix(".lha", "application/octet-stream");
+ setSuffix(".lzh", "application/octet-stream");
+ setSuffix(".m3u", "audio/x-mpegurl");
+ setSuffix(".m4a", "audio/mp4a-latm");
+ setSuffix(".m4b", "audio/mp4a-latm");
+ setSuffix(".m4p", "audio/mp4a-latm");
+ setSuffix(".m4u", "video/vnd.mpegurl");
+ setSuffix(".m4v", "video/x-m4v");
+ setSuffix(".mac", "image/x-macpaint");
+ setSuffix(".man", "application/x-troff-man");
+ setSuffix(".mathml", "application/mathml+xml");
+ setSuffix(".mbd", "application/mbedlet");
+ setSuffix(".mcf", "image/vasa");
+ setSuffix(".me", "application/x-troff-me");
+ setSuffix(".mesh", "model/mesh");
+ setSuffix(".mid", "audio/midi");
+ setSuffix(".midi", "audio/midi");
+ setSuffix(".mif", "application/mif");
+ setSuffix(".mov", "video/quicktime");
+ setSuffix(".movie", "video/x-sgi-movie");
+ setSuffix(".mp2", "audio/mpeg");
+ setSuffix(".mp3", "audio/mpeg");
+ setSuffix(".mp4", "video/mp4");
+ setSuffix(".mpe", "video/mpeg");
+ setSuffix(".mpeg", "video/mpeg");
+ setSuffix(".mpg", "video/mpeg");
+ setSuffix(".mpga", "audio/mpeg");
+ setSuffix(".ms", "application/x-troff-ms");
+ setSuffix(".msh", "model/mesh");
+ setSuffix(".mxu", "video/vnd.mpegurl");
+ setSuffix(".nc", "application/x-netcdf");
+ setSuffix(".nsc", "application/x-nschat");
+ setSuffix(".oda", "application/oda");
+ setSuffix(".oga", "audio/ogg");
+ setSuffix(".ogg", "application/ogg");
+ setSuffix(".ogv", "video/ogg");
+ setSuffix(".pbm", "image/x-portable-bitmap");
+ setSuffix(".pct", "image/pict");
+ setSuffix(".pdb", "chemical/x-pdb");
+ setSuffix(".pde", "text/plain");
+ setSuffix(".pdf", "application/pdf");
+ setSuffix(".pgm", "image/x-portable-graymap");
+ setSuffix(".pgn", "application/x-chess-pgn");
+ setSuffix(".php", "application/x-httpd-php");
+ setSuffix(".phtml", "application/x-httpd-php");
+ setSuffix(".pic", "image/pict");
+ setSuffix(".pict", "image/pict");
+ setSuffix(".pl", "text/plain");
+ setSuffix(".png", "image/png");
+ setSuffix(".pnm", "image/x-portable-anymap");
+ setSuffix(".pnt", "image/x-macpaint");
+ setSuffix(".pntg", "image/x-macpaint");
+ setSuffix(".pot", "application/mspowerpoint");
+ setSuffix(".ppm", "image/x-portable-pixmap");
+ setSuffix(".pps", "application/mspowerpoint");
+ setSuffix(".ppt", "application/mspowerpoint");
+ setSuffix(".ppz", "application/mspowerpoint");
+ setSuffix(".ps", "application/postscript");
+ setSuffix(".ps", "application/postscript");
+ setSuffix(".ptlk", "application/listenup");
+ setSuffix(".qd3", "x-world/x-3dmf");
+ setSuffix(".qd3d", "x-world/x-3dmf");
+ setSuffix(".qt", "video/quicktime");
+ setSuffix(".qti", "image/x-quicktime");
+ setSuffix(".qtif", "image/x-quicktime");
+ setSuffix(".ra", "audio/x-pn-realaudio");
+ setSuffix(".ra", "audio/x-pn-realaudio");
+ setSuffix(".ram", "audio/x-mpeg");
+ setSuffix(".ras", "image/cmu-raster");
+ setSuffix(".rdf", "application/rdf+xml");
+ setSuffix(".rgb", "image/x-rgb");
+ setSuffix(".rm", "application/vnd.rn-realmedia");
+ setSuffix(".roff", "application/x-troff");
+ setSuffix(".rpm", "audio/x-pn-realaudio-plugin");
+ setSuffix(".rtc", "application/rtc");
+ setSuffix(".rtf", "text/rtf");
+ setSuffix(".rtx", "text/richtext");
+ setSuffix(".sca", "application/x-supercard");
+ setSuffix(".sgm", "text/sgml");
+ setSuffix(".sgml", "text/sgml");
+ setSuffix(".sh", "application/x-sh");
+ setSuffix(".shar", "application/x-shar");
+ setSuffix(".shtml", "text/html");
+ setSuffix(".silo", "model/mesh");
+ setSuffix(".sit", "application/x-stuffit");
+ setSuffix(".skd", "application/x-koan");
+ setSuffix(".skm", "application/x-koan");
+ setSuffix(".skp", "application/x-koan");
+ setSuffix(".skt", "application/x-koan");
+ setSuffix(".smi", "application/smil");
+ setSuffix(".smil", "application/smil");
+ setSuffix(".smp", "application/studiom");
+ setSuffix(".snd", "audio/basic");
+ setSuffix(".so", "application/octet-stream");
+ setSuffix(".spc", "text/x-speech");
+ setSuffix(".spl", "application/futuresplash");
+ setSuffix(".spr", "application/x-sprite");
+ setSuffix(".sprite", "application/x-sprite");
+ setSuffix(".src", "application/x-wais-source");
+ setSuffix(".stream", "audio/x-qt-stream");
+ setSuffix(".sv4cpio", "application/x-sv4cpio");
+ setSuffix(".sv4crc", "application/x-sv4crc");
+ setSuffix(".svg", "image/svg+xml");
+ setSuffix(".swf", "application/x-shockwave-flash");
+ setSuffix(".t", "application/x-troff");
+ setSuffix(".talk", "text/x-speech");
+ setSuffix(".tar", "application/x-tar");
+ setSuffix(".tbk", "application/toolbook");
+ setSuffix(".tcl", "application/x-tcl");
+ setSuffix(".tex", "application/x-tex");
+ setSuffix(".texi", "application/x-texinfo");
+ setSuffix(".texinfo", "text/plain");
+ setSuffix(".text", "text/plain");
+ setSuffix(".tif", "image/tiff");
+ setSuffix(".tiff", "image/tiff");
+ setSuffix(".tr", "application/x-troff");
+ setSuffix(".troff", "application/x-troff-man");
+ setSuffix(".tsi", "audio/tsplayer");
+ setSuffix(".tsp", "application/dsptype");
+ setSuffix(".tsv", "text/tab-separated-values");
+ setSuffix(".tsv", "text/tab-separated-values");
+ setSuffix(".txt", "text/plain");
+ setSuffix(".ustar", "application/x-ustar");
+ setSuffix(".uu", "application/octet-stream");
+ setSuffix(".vcd", "application/x-cdlink");
+ setSuffix(".viv", "video/vnd.vivo");
+ setSuffix(".vivo", "video/vnd.vivo");
+ setSuffix(".vmd", "application/vocaltec-media-desc");
+ setSuffix(".vmf", "application/vocaltec-media-file");
+ setSuffix(".vox", "audio/voxware");
+ setSuffix(".vrml", "model/vrml");
+ setSuffix(".vts", "workbook/formulaone");
+ setSuffix(".vtts", "workbook/formulaone");
+ setSuffix(".vxml", "application/voicexml+xml");
+ setSuffix(".wav", "audio/x-wav");
+ setSuffix(".wav", "audio/x-wav");
+ setSuffix(".wbmp", "image/vnd.wap.wbmp");
+ setSuffix(".wbmxl", "application/vnd.wap.wbxml");
+ setSuffix(".wml", "text/vnd.wap.wml");
+ setSuffix(".wmlc", "application/vnd.wap.wmlc");
+ setSuffix(".wmls", "text/vnd.wap.wmlscript");
+ setSuffix(".wmlsc", "application/vnd.wap.wmlscriptc");
+ setSuffix(".wrl", "model/vrml");
+ setSuffix(".xbm", "image/x-xbitmap");
+ setSuffix(".xht", "application/xhtml+xml");
+ setSuffix(".xhtml", "application/xhtml+xml");
+ setSuffix(".xla", "application/msexcel");
+ setSuffix(".xls", "application/msexcel");
+ setSuffix(".xml", "text/xml");
+ setSuffix(".xpm", "image/x-xpixmap");
+ setSuffix(".xsl", "application/xml");
+ setSuffix(".xslt", "application/xslt+xml");
+ setSuffix(".xul", "application/vnd.mozilla.xul+xml");
+ setSuffix(".xwd", "image/x-windowdump");
+ setSuffix(".xyz", "chemical/x-xyz");
+ setSuffix(".z", "application/x-compress");
+ setSuffix(".zip", "application/zip");
+ }
+
+ void listDirectory(File dir, PrintStream ps) throws IOException
+ {
+ ps.println("Directory listing\n");
+ ps.println("Parent Directory
\n");
+ String[] list = dir.list();
+ for (int i = 0; list != null && i < list.length; i++) {
+ File f = new File(dir, list[i]);
+ if (f.isDirectory()) {
+ ps.println(""+list[i]+"/
");
+ } else {
+ ps.println(""+list[i]+"
" + (new Date()) + "");
+ }
+
+}
+
+interface HttpConstants {
+ /** 2XX: generally "OK" */
+ public static final int HTTP_OK = 200;
+ public static final int HTTP_CREATED = 201;
+ public static final int HTTP_ACCEPTED = 202;
+ public static final int HTTP_NOT_AUTHORITATIVE = 203;
+ public static final int HTTP_NO_CONTENT = 204;
+ public static final int HTTP_RESET = 205;
+ public static final int HTTP_PARTIAL = 206;
+
+ /** 3XX: relocation/redirect */
+ public static final int HTTP_MULT_CHOICE = 300;
+ public static final int HTTP_MOVED_PERM = 301;
+ public static final int HTTP_MOVED_TEMP = 302;
+ public static final int HTTP_SEE_OTHER = 303;
+ public static final int HTTP_NOT_MODIFIED = 304;
+ public static final int HTTP_USE_PROXY = 305;
+
+ /** 4XX: client error */
+ public static final int HTTP_BAD_REQUEST = 400;
+ public static final int HTTP_UNAUTHORIZED = 401;
+ public static final int HTTP_PAYMENT_REQUIRED = 402;
+ public static final int HTTP_FORBIDDEN = 403;
+ public static final int HTTP_NOT_FOUND = 404;
+ public static final int HTTP_BAD_METHOD = 405;
+ public static final int HTTP_NOT_ACCEPTABLE = 406;
+ public static final int HTTP_PROXY_AUTH = 407;
+ public static final int HTTP_CLIENT_TIMEOUT = 408;
+ public static final int HTTP_CONFLICT = 409;
+ public static final int HTTP_GONE = 410;
+ public static final int HTTP_LENGTH_REQUIRED = 411;
+ public static final int HTTP_PRECON_FAILED = 412;
+ public static final int HTTP_ENTITY_TOO_LARGE = 413;
+ public static final int HTTP_REQ_TOO_LONG = 414;
+ public static final int HTTP_UNSUPPORTED_TYPE = 415;
+
+ /** 5XX: server error */
+ public static final int HTTP_SERVER_ERROR = 500;
+ public static final int HTTP_INTERNAL_ERROR = 501;
+ public static final int HTTP_BAD_GATEWAY = 502;
+ public static final int HTTP_UNAVAILABLE = 503;
+ public static final int HTTP_GATEWAY_TIMEOUT = 504;
+ public static final int HTTP_VERSION = 505;
+}
+
+
+
diff --git a/app/src/processing/mode/javascript/JavaScriptToolbar.java b/app/src/processing/mode/javascript/JavaScriptToolbar.java
index 872bf2f9a..c997fc6f5 100644
--- a/app/src/processing/mode/javascript/JavaScriptToolbar.java
+++ b/app/src/processing/mode/javascript/JavaScriptToolbar.java
@@ -11,18 +11,22 @@ 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 RUN = 0;
+ static protected final int STOP = 1;
- static protected final int NEW = 0;
- static protected final int OPEN = 1;
- static protected final int SAVE = 2;
- static protected final int EXPORT = 3;
+ 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 NEW: return !shift ? "New" : "New Editor Window";
+ static public String getTitle ( int index, boolean shift )
+ {
+ switch (index)
+ {
+ case RUN: return "Start server";
+ case STOP: return "Stop server";
+ case NEW: return !shift ? "New" : "New Editor Window";
case OPEN: return !shift ? "Open" : "Open in Another Window";
case SAVE: return "Save";
case EXPORT: return "Export for Web";
@@ -31,24 +35,35 @@ public class JavaScriptToolbar extends EditorToolbar {
}
- public JavaScriptToolbar(Editor editor, Base base) {
+ public JavaScriptToolbar ( Editor editor, Base base )
+ {
super(editor, base);
}
- public void init() {
+ public void init ()
+ {
Image[][] images = loadImages();
- for (int i = 0; i < 4; i++) {
- addButton(getTitle(i, false), getTitle(i, true), images[i], i == NEW);
+ 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) {
+ 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();
@@ -68,7 +83,7 @@ public class JavaScriptToolbar extends EditorToolbar {
break;
case EXPORT:
- jsEditor.handleExport();
+ jsEditor.handleExport( true );
break;
}
}
diff --git a/build/build.xml b/build/build.xml
index a4e571ffe..69b2c0de4 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -134,13 +134,11 @@
-->
-
diff --git a/javascript/applet_js/processing.js b/javascript/applet_js/processing.js
index bc35ab660..e36e9e220 100644
--- a/javascript/applet_js/processing.js
+++ b/javascript/applet_js/processing.js
@@ -1,26 +1,28 @@
-/*
+/***
- P R O C E S S I N G . J S - 1.1.0
+ P R O C E S S I N G . J S - 1.2.1
a port of the Processing visualization language
- License : MIT
- Developer : John Resig: http://ejohn.org
- Web Site : http://processingjs.org
- Java Version : http://processing.org
- Github Repo. : http://github.com/jeresig/processing-js
- Bug Tracking : http://processing-js.lighthouseapp.com
- Mozilla POW! : http://wiki.Mozilla.org/Education/Projects/ProcessingForTheWeb
- Maintained by : Seneca: http://zenit.senecac.on.ca/wiki/index.php/Processing.js
- Hyper-Metrix: http://hyper-metrix.com/#Processing
- BuildingSky: http://weare.buildingsky.net/pages/processing-js
+ Processing.js is licensed under the MIT License, see LICENSE.
+ For a list of copyright holders, please refer to AUTHORS.
- */
+ http://processingjs.org
-(function() {
+***/
- var undef; // intentionally left undefined
+(function(window, document, Math, nop, undef) {
- var ajax = function ajax(url) {
+ var debug = (function() {
+ if ("console" in window) {
+ return function(msg) {
+ window.console.log('Processing.js: ' + msg);
+ };
+ } else {
+ return nop();
+ }
+ }());
+
+ var ajax = function(url) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
if (xhr.overrideMimeType) {
@@ -36,38 +38,6 @@
var isDOMPresent = ("document" in this) && !("fake" in this.document);
/* Browsers fixes start */
- function fixReplaceByRegExp() {
- var re = /t/g;
- if ("t".replace(re,"") !== null && re.exec("t")) {
- return; // it is not necessary
- }
- var _ie_replace = String.prototype.replace;
- String.prototype.replace = function(searchValue, repaceValue) {
- var result = _ie_replace.apply(this, arguments);
- if (searchValue instanceof RegExp && searchValue.global) {
- searchValue.lastIndex = 0;
- }
- return result;
- };
- }
-
- function fixMatchByRegExp() {
- var re = /t/g;
- if ("t".match(re) !== null && re.exec("t")) {
- return; // it is not necessary
- }
- var _ie_match = String.prototype.match;
- String.prototype.match = function(searchValue) {
- var result = _ie_match.apply(this, arguments);
- if(searchValue instanceof RegExp && searchValue.global) {
- searchValue.lastIndex = 0;
- }
- return result;
- };
- }
- fixReplaceByRegExp();
- fixMatchByRegExp();
-
(function fixOperaCreateImageData() {
try {
if (!("createImageData" in CanvasRenderingContext2D.prototype)) {
@@ -77,6 +47,36 @@
}
} catch(e) {}
}());
+
+ // Typed Arrays: fallback to WebGL arrays or Native JS arrays if unavailable
+ function setupTypedArray(name, fallback) {
+ // Check if TypedArray exists, and use if so.
+ if (name in window) {
+ return window[name];
+ }
+
+ // Check if WebGLArray exists
+ if (typeof window[fallback] === "function") {
+ return window[fallback];
+ } else {
+ // Use Native JS array
+ return function(obj) {
+ if (obj instanceof Array) {
+ return obj;
+ } else if (typeof obj === "number") {
+ var arr = [];
+ arr.length = obj;
+ return arr;
+ }
+ };
+ }
+ }
+
+ var Float32Array = setupTypedArray("Float32Array", "WebGLFloatArray"),
+ Int32Array = setupTypedArray("Int32Array", "WebGLIntArray"),
+ Uint16Array = setupTypedArray("Uint16Array", "WebGLUnsignedShortArray"),
+ Uint8Array = setupTypedArray("Uint8Array", "WebGLUnsignedByteArray");
+
/* Browsers fixes end */
var PConstants = {
@@ -320,8 +320,6 @@
UP: 38,
RIGHT: 39,
DOWN: 40,
- INS: 45,
- DEL: 46,
F1: 112,
F2: 113,
F3: 114,
@@ -335,6 +333,8 @@
F11: 122,
F12: 123,
NUMLK: 144,
+ META: 157,
+ INSERT: 155,
// Cursor types
ARROW: 'default',
@@ -373,34 +373,6 @@
MAX_LIGHTS: 8
};
- // Typed Arrays: fallback to WebGL arrays or Native JS arrays if unavailable
- function setupTypedArray(name, fallback) {
- // check if TypedArray exists
- // typeof on Minefield and Chrome return function, typeof on Webkit returns object.
- if (typeof this[name] !== "function" && typeof this[name] !== "object") {
- // nope.. check if WebGLArray exists
- if (typeof this[fallback] === "function") {
- this[name] = this[fallback];
- } else {
- // nope.. set as Native JS array
- this[name] = function(obj) {
- if (obj instanceof Array) {
- return obj;
- } else if (typeof obj === "number") {
- var arr = [];
- arr.length = obj;
- return arr;
- }
- };
- }
- }
- }
-
- setupTypedArray("Float32Array", "WebGLFloatArray");
- setupTypedArray("Int32Array", "WebGLIntArray");
- setupTypedArray("Uint16Array", "WebGLUnsignedShortArray");
- setupTypedArray("Uint8Array", "WebGLUnsignedByteArray");
-
/**
* Returns Java hashCode() result for the object. If the object has the "hashCode" function,
* it preforms the call of this function. Otherwise it uses/creates the "$id" property,
@@ -452,6 +424,31 @@
}
}
+ /**
+ * A ObjectIterator is an iterator wrapper for objects. If passed object contains
+ * the iterator method, the object instance will be replaced by the result returned by
+ * this method call. If passed object is an array, the ObjectIterator instance iterates
+ * through its items.
+ *
+ * @param {Object} obj The object to be iterated.
+ */
+ var ObjectIterator = function(obj) {
+ if (obj.iterator instanceof Function) {
+ return obj.iterator();
+ } else if (obj instanceof Array) {
+ // iterate through array items
+ var index = -1;
+ this.hasNext = function() {
+ return ++index < obj.length;
+ };
+ this.next = function() {
+ return obj[index];
+ };
+ } else {
+ throw "Unable to iterate: " + obj;
+ }
+ };
+
/**
* An ArrayList stores a variable number of objects.
*
@@ -506,14 +503,25 @@
* @returns {boolean} true if the specified element is present; false otherwise.
*/
this.contains = function(item) {
+ return this.indexOf(item)>-1;
+ };
+ /**
+ * @member ArrayList
+ * ArrayList.indexOf() Returns the position this element takes in the list, or -1 if the element is not found.
+ *
+ * @param {Object} item element whose position in this List is to be tested.
+ *
+ * @returns {int} the list position that the first match for this element holds in the list, or -1 if it is not in the list.
+ */
+ this.indexOf = function(item) {
for (var i = 0, len = array.length; i < len; ++i) {
if (virtEquals(item, array[i])) {
- return true;
+ return i;
}
}
- return false;
+ return -1;
};
- /**
+ /**
* @member ArrayList
* ArrayList.add() Adds the specified element to this list.
*
@@ -538,7 +546,39 @@
throw("Please use the proper number of parameters.");
}
};
-
+ /**
+ * @member ArrayList
+ * ArrayList.addAll(collection) appends all of the elements in the specified
+ * Collection to the end of this list, in the order that they are returned by
+ * the specified Collection's Iterator.
+ *
+ * When called as addAll(index, collection) the elements are inserted into
+ * this list at the position indicated by index.
+ *
+ * @param {index} Optional; specifies the position the colletion should be inserted at
+ * @param {collection} Any iterable object (ArrayList, HashMap.keySet(), etc.)
+ * @throws out of bounds error for negative index, or index greater than list size.
+ */
+ this.addAll = function(arg1, arg2) {
+ // addAll(int, Collection)
+ var it;
+ if (typeof arg1 === "number") {
+ if (arg1 < 0 || arg1 > array.length) {
+ throw("Index out of bounds for addAll: " + arg1 + " greater or equal than " + array.length);
+ }
+ it = new ObjectIterator(arg2);
+ while (it.hasNext()) {
+ array.splice(arg1++, 0, it.next());
+ }
+ }
+ // addAll(Collection)
+ else {
+ it = new ObjectIterator(arg1);
+ while (it.hasNext()) {
+ array.push(it.next());
+ }
+ }
+ };
/**
* @member ArrayList
* ArrayList.set() Replaces the element at the specified position in this list with the specified element.
@@ -583,15 +623,24 @@
/**
* @member ArrayList
- * ArrayList.remove() Removes the element at the specified position in this list.
- * Shifts any subsequent elements to the left (subtracts one from their indices).
+ * ArrayList.remove() Removes an element either based on index, if the argument is a number, or
+ * by equality check, if the argument is an object.
*
- * @param {int} index the index of the element to removed.
+ * @param {int|Object} item either the index of the element to be removed, or the element itself.
*
- * @returns {Object} the element that was removed from the list
+ * @returns {Object|boolean} If removal is by index, the element that was removed, or null if nothing was removed. If removal is by object, true if removal occurred, otherwise false.
*/
- this.remove = function(i) {
- return array.splice(i, 1)[0];
+ this.remove = function(item) {
+ if (typeof item === 'number') {
+ return array.splice(item, 1)[0];
+ } else {
+ item = this.indexOf(item);
+ if (item > -1) {
+ array.splice(item, 1);
+ return true;
+ }
+ return false;
+ }
};
/**
@@ -664,6 +713,10 @@
var count = 0;
var hashMap = this;
+ function getBucketIndex(key) {
+ var index = virtHashCode(key) % buckets.length;
+ return index < 0 ? buckets.length + index : index;
+ }
function ensureLoad() {
if (count <= loadFactor * buckets.length) {
return;
@@ -674,10 +727,11 @@
allEntries = allEntries.concat(buckets[i]);
}
}
+ var newBucketsLength = buckets.length * 2;
buckets = [];
- buckets.length = buckets.length * 2;
+ buckets.length = newBucketsLength;
for (var j = 0; j < allEntries.length; ++j) {
- var index = virtHashCode(allEntries[j].key) % buckets.length;
+ var index = getBucketIndex(allEntries[j].key);
var bucket = buckets[index];
if (bucket === undef) {
buckets[index] = bucket = [];
@@ -853,7 +907,7 @@
};
this.containsKey = function(key) {
- var index = virtHashCode(key) % buckets.length;
+ var index = getBucketIndex(key);
var bucket = buckets[index];
if (bucket === undef) {
return false;
@@ -898,7 +952,7 @@
};
this.get = function(key) {
- var index = virtHashCode(key) % buckets.length;
+ var index = getBucketIndex(key);
var bucket = buckets[index];
if (bucket === undef) {
return null;
@@ -932,7 +986,7 @@
};
this.put = function(key, value) {
- var index = virtHashCode(key) % buckets.length;
+ var index = getBucketIndex(key);
var bucket = buckets[index];
if (bucket === undef) {
++count;
@@ -968,7 +1022,7 @@
};
this.remove = function(key) {
- var index = virtHashCode(key) % buckets.length;
+ var index = getBucketIndex(key);
var bucket = buckets[index];
if (bucket === undef) {
return null;
@@ -1150,31 +1204,6 @@
return PVector;
}());
- /**
- * A ObjectIterator is an iterator wrapper for objects. If passed object contains
- * the iterator method, the object instance will be replaced by the result returned by
- * this method call. If passed object is an array, the ObjectIterator instance iterates
- * through its items.
- *
- * @param {Object} obj The object to be iterated.
- */
- var ObjectIterator = function(obj) {
- if (obj.iterator instanceof Function) {
- return obj.iterator();
- } else if (obj instanceof Array) {
- // iterate through array items
- var index = -1;
- this.hasNext = function() {
- return ++index < obj.length;
- };
- this.next = function() {
- return obj[index];
- };
- } else {
- throw "Unable to iterate: " + obj;
- }
- };
-
// Building defaultScope. Changing of the prototype protects
// internal Processing code from the changes in defaultScope
function DefaultScope() {}
@@ -1189,16 +1218,188 @@
//defaultScope.PShape = PShape; // TODO
//defaultScope.PShapeSVG = PShapeSVG; // TODO
- var Processing = this.Processing = function Processing(curElement, aCode) {
+
+ var colors = {
+ aliceblue: "#f0f8ff",
+ antiquewhite: "#faebd7",
+ aqua: "#00ffff",
+ aquamarine: "#7fffd4",
+ azure: "#f0ffff",
+ beige: "#f5f5dc",
+ bisque: "#ffe4c4",
+ black: "#000000",
+ blanchedalmond: "#ffebcd",
+ blue: "#0000ff",
+ blueviolet: "#8a2be2",
+ brown: "#a52a2a",
+ burlywood: "#deb887",
+ cadetblue: "#5f9ea0",
+ chartreuse: "#7fff00",
+ chocolate: "#d2691e",
+ coral: "#ff7f50",
+ cornflowerblue: "#6495ed",
+ cornsilk: "#fff8dc",
+ crimson: "#dc143c",
+ cyan: "#00ffff",
+ darkblue: "#00008b",
+ darkcyan: "#008b8b",
+ darkgoldenrod: "#b8860b",
+ darkgray: "#a9a9a9",
+ darkgreen: "#006400",
+ darkkhaki: "#bdb76b",
+ darkmagenta: "#8b008b",
+ darkolivegreen: "#556b2f",
+ darkorange: "#ff8c00",
+ darkorchid: "#9932cc",
+ darkred: "#8b0000",
+ darksalmon: "#e9967a",
+ darkseagreen: "#8fbc8f",
+ darkslateblue: "#483d8b",
+ darkslategray: "#2f4f4f",
+ darkturquoise: "#00ced1",
+ darkviolet: "#9400d3",
+ deeppink: "#ff1493",
+ deepskyblue: "#00bfff",
+ dimgray: "#696969",
+ dodgerblue: "#1e90ff",
+ firebrick: "#b22222",
+ floralwhite: "#fffaf0",
+ forestgreen: "#228b22",
+ fuchsia: "#ff00ff",
+ gainsboro: "#dcdcdc",
+ ghostwhite: "#f8f8ff",
+ gold: "#ffd700",
+ goldenrod: "#daa520",
+ gray: "#808080",
+ green: "#008000",
+ greenyellow: "#adff2f",
+ honeydew: "#f0fff0",
+ hotpink: "#ff69b4",
+ indianred: "#cd5c5c",
+ indigo: "#4b0082",
+ ivory: "#fffff0",
+ khaki: "#f0e68c",
+ lavender: "#e6e6fa",
+ lavenderblush: "#fff0f5",
+ lawngreen: "#7cfc00",
+ lemonchiffon: "#fffacd",
+ lightblue: "#add8e6",
+ lightcoral: "#f08080",
+ lightcyan: "#e0ffff",
+ lightgoldenrodyellow: "#fafad2",
+ lightgrey: "#d3d3d3",
+ lightgreen: "#90ee90",
+ lightpink: "#ffb6c1",
+ lightsalmon: "#ffa07a",
+ lightseagreen: "#20b2aa",
+ lightskyblue: "#87cefa",
+ lightslategray: "#778899",
+ lightsteelblue: "#b0c4de",
+ lightyellow: "#ffffe0",
+ lime: "#00ff00",
+ limegreen: "#32cd32",
+ linen: "#faf0e6",
+ magenta: "#ff00ff",
+ maroon: "#800000",
+ mediumaquamarine: "#66cdaa",
+ mediumblue: "#0000cd",
+ mediumorchid: "#ba55d3",
+ mediumpurple: "#9370d8",
+ mediumseagreen: "#3cb371",
+ mediumslateblue: "#7b68ee",
+ mediumspringgreen: "#00fa9a",
+ mediumturquoise: "#48d1cc",
+ mediumvioletred: "#c71585",
+ midnightblue: "#191970",
+ mintcream: "#f5fffa",
+ mistyrose: "#ffe4e1",
+ moccasin: "#ffe4b5",
+ navajowhite: "#ffdead",
+ navy: "#000080",
+ oldlace: "#fdf5e6",
+ olive: "#808000",
+ olivedrab: "#6b8e23",
+ orange: "#ffa500",
+ orangered: "#ff4500",
+ orchid: "#da70d6",
+ palegoldenrod: "#eee8aa",
+ palegreen: "#98fb98",
+ paleturquoise: "#afeeee",
+ palevioletred: "#d87093",
+ papayawhip: "#ffefd5",
+ peachpuff: "#ffdab9",
+ peru: "#cd853f",
+ pink: "#ffc0cb",
+ plum: "#dda0dd",
+ powderblue: "#b0e0e6",
+ purple: "#800080",
+ red: "#ff0000",
+ rosybrown: "#bc8f8f",
+ royalblue: "#4169e1",
+ saddlebrown: "#8b4513",
+ salmon: "#fa8072",
+ sandybrown: "#f4a460",
+ seagreen: "#2e8b57",
+ seashell: "#fff5ee",
+ sienna: "#a0522d",
+ silver: "#c0c0c0",
+ skyblue: "#87ceeb",
+ slateblue: "#6a5acd",
+ slategray: "#708090",
+ snow: "#fffafa",
+ springgreen: "#00ff7f",
+ steelblue: "#4682b4",
+ tan: "#d2b48c",
+ teal: "#008080",
+ thistle: "#d8bfd8",
+ tomato: "#ff6347",
+ turquoise: "#40e0d0",
+ violet: "#ee82ee",
+ wheat: "#f5deb3",
+ white: "#ffffff",
+ whitesmoke: "#f5f5f5",
+ yellow: "#ffff00",
+ yellowgreen: "#9acd32"
+ };
+
+ // Manage multiple Processing instances
+ var processingInstances = [];
+ var processingInstanceIds = {};
+
+ var removeInstance = function(id) {
+ processingInstances.splice(processingInstanceIds[id], 1);
+ delete processingInstanceIds[id];
+ };
+
+ var addInstance = function(processing) {
+ if (processing.externals.canvas.id === undef || !processing.externals.canvas.id.length) {
+ processing.externals.canvas.id = "__processing" + processingInstances.length;
+ }
+ processingInstanceIds[processing.externals.canvas.id] = processingInstances.length;
+ processingInstances.push(processing);
+ };
+
+
+ var Processing = this.Processing = function(curElement, aCode) {
// Previously we allowed calling Processing as a func instead of ctor, but no longer.
if (!(this instanceof Processing)) {
throw("called Processing constructor as if it were a function: missing 'new'.");
}
+ function unimplemented(s) {
+ Processing.debug('Unimplemented - ' + s);
+ }
+
// When something new is added to "p." it must also be added to the "names" array.
// The names array contains the names of everything that is inside "p."
var p = this;
+ var pgraphicsMode = (arguments.length === 0);
+ if (pgraphicsMode) {
+ curElement = document.createElement("canvas");
+ p.canvas = curElement;
+ }
+
// PJS specific (non-p5) methods and properties to externalize
p.externals = {
canvas: curElement,
@@ -1254,14 +1455,14 @@
// Remapped vars
p.__mousePressed = false;
p.__keyPressed = false;
- p.__frameRate = 0;
+ p.__frameRate = 60;
// The current animation frame
p.frameCount = 0;
// The height/width of the canvas
- p.width = curElement.width - 0;
- p.height = curElement.height - 0;
+ p.width = 100;
+ p.height = 100;
p.defineProperty = function(obj, name, desc) {
if("defineProperty" in Object) {
@@ -1279,6 +1480,7 @@
// "Private" variables used to maintain state
var curContext,
curSketch,
+ drawing, // hold a Drawing2D or Drawing3D object
online = true,
doFill = true,
fillStyle = [1.0, 1.0, 1.0, 1.0],
@@ -1300,15 +1502,16 @@
normalMode = PConstants.NORMAL_MODE_AUTO,
inDraw = false,
curFrameRate = 60,
+ curMsPerFrame = 1000/curFrameRate,
curCursor = PConstants.ARROW,
oldCursor = curElement.style.cursor,
- curMsPerFrame = 1,
curShape = PConstants.POLYGON,
curShapeCount = 0,
curvePoints = [],
curTightness = 0,
curveDet = 20,
curveInited = false,
+ backgroundObj = -3355444, // rgb(204, 204, 204) is the default gray background colour
bezDetail = 20,
colorModeA = 255,
colorModeX = 255,
@@ -1375,10 +1578,12 @@
isContextReplaced = false,
setPixelsCached,
maxPixelsCached = 1000,
+ pressedKeysMap = [],
+ lastPressedKeyCode = null,
codedKeys = [ PConstants.SHIFT, PConstants.CONTROL, PConstants.ALT, PConstants.CAPSLK, PConstants.PGUP, PConstants.PGDN,
PConstants.END, PConstants.HOME, PConstants.LEFT, PConstants.UP, PConstants.RIGHT, PConstants.DOWN, PConstants.NUMLK,
- PConstants.INS, PConstants.F1, PConstants.F2, PConstants.F3, PConstants.F4, PConstants.F5, PConstants.F6, PConstants.F7,
- PConstants.F8, PConstants.F9, PConstants.F10, PConstants.F11, PConstants.F12 ];
+ PConstants.INSERT, PConstants.F1, PConstants.F2, PConstants.F3, PConstants.F4, PConstants.F5, PConstants.F6, PConstants.F7,
+ PConstants.F8, PConstants.F9, PConstants.F10, PConstants.F11, PConstants.F12, PConstants.META ];
// Get padding and border style widths for mouse offsets
var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
@@ -1412,17 +1617,18 @@
modelView,
modelViewInv,
userMatrixStack,
+ userReverseMatrixStack,
inverseCopy,
projection,
manipulatingCamera = false,
frustumMode = false,
cameraFOV = 60 * (Math.PI / 180),
- cameraX = curElement.width / 2,
- cameraY = curElement.height / 2,
+ cameraX = p.width / 2,
+ cameraY = p.height / 2,
cameraZ = cameraY / Math.tan(cameraFOV / 2),
cameraNear = cameraZ / 10,
cameraFar = cameraZ * 10,
- cameraAspect = curElement.width / curElement.height;
+ cameraAspect = p.width / p.height;
var vertArray = [],
curveVertArray = [],
@@ -1434,149 +1640,6 @@
//PShape stuff
var curShapeMode = PConstants.CORNER;
- var colors = {
- aliceblue: "#f0f8ff",
- antiquewhite: "#faebd7",
- aqua: "#00ffff",
- aquamarine: "#7fffd4",
- azure: "#f0ffff",
- beige: "#f5f5dc",
- bisque: "#ffe4c4",
- black: "#000000",
- blanchedalmond: "#ffebcd",
- blue: "#0000ff",
- blueviolet: "#8a2be2",
- brown: "#a52a2a",
- burlywood: "#deb887",
- cadetblue: "#5f9ea0",
- chartreuse: "#7fff00",
- chocolate: "#d2691e",
- coral: "#ff7f50",
- cornflowerblue: "#6495ed",
- cornsilk: "#fff8dc",
- crimson: "#dc143c",
- cyan: "#00ffff",
- darkblue: "#00008b",
- darkcyan: "#008b8b",
- darkgoldenrod: "#b8860b",
- darkgray: "#a9a9a9",
- darkgreen: "#006400",
- darkkhaki: "#bdb76b",
- darkmagenta: "#8b008b",
- darkolivegreen: "#556b2f",
- darkorange: "#ff8c00",
- darkorchid: "#9932cc",
- darkred: "#8b0000",
- darksalmon: "#e9967a",
- darkseagreen: "#8fbc8f",
- darkslateblue: "#483d8b",
- darkslategray: "#2f4f4f",
- darkturquoise: "#00ced1",
- darkviolet: "#9400d3",
- deeppink: "#ff1493",
- deepskyblue: "#00bfff",
- dimgray: "#696969",
- dodgerblue: "#1e90ff",
- firebrick: "#b22222",
- floralwhite: "#fffaf0",
- forestgreen: "#228b22",
- fuchsia: "#ff00ff",
- gainsboro: "#dcdcdc",
- ghostwhite: "#f8f8ff",
- gold: "#ffd700",
- goldenrod: "#daa520",
- gray: "#808080",
- green: "#008000",
- greenyellow: "#adff2f",
- honeydew: "#f0fff0",
- hotpink: "#ff69b4",
- indianred: "#cd5c5c",
- indigo: "#4b0082",
- ivory: "#fffff0",
- khaki: "#f0e68c",
- lavender: "#e6e6fa",
- lavenderblush: "#fff0f5",
- lawngreen: "#7cfc00",
- lemonchiffon: "#fffacd",
- lightblue: "#add8e6",
- lightcoral: "#f08080",
- lightcyan: "#e0ffff",
- lightgoldenrodyellow: "#fafad2",
- lightgrey: "#d3d3d3",
- lightgreen: "#90ee90",
- lightpink: "#ffb6c1",
- lightsalmon: "#ffa07a",
- lightseagreen: "#20b2aa",
- lightskyblue: "#87cefa",
- lightslategray: "#778899",
- lightsteelblue: "#b0c4de",
- lightyellow: "#ffffe0",
- lime: "#00ff00",
- limegreen: "#32cd32",
- linen: "#faf0e6",
- magenta: "#ff00ff",
- maroon: "#800000",
- mediumaquamarine: "#66cdaa",
- mediumblue: "#0000cd",
- mediumorchid: "#ba55d3",
- mediumpurple: "#9370d8",
- mediumseagreen: "#3cb371",
- mediumslateblue: "#7b68ee",
- mediumspringgreen: "#00fa9a",
- mediumturquoise: "#48d1cc",
- mediumvioletred: "#c71585",
- midnightblue: "#191970",
- mintcream: "#f5fffa",
- mistyrose: "#ffe4e1",
- moccasin: "#ffe4b5",
- navajowhite: "#ffdead",
- navy: "#000080",
- oldlace: "#fdf5e6",
- olive: "#808000",
- olivedrab: "#6b8e23",
- orange: "#ffa500",
- orangered: "#ff4500",
- orchid: "#da70d6",
- palegoldenrod: "#eee8aa",
- palegreen: "#98fb98",
- paleturquoise: "#afeeee",
- palevioletred: "#d87093",
- papayawhip: "#ffefd5",
- peachpuff: "#ffdab9",
- peru: "#cd853f",
- pink: "#ffc0cb",
- plum: "#dda0dd",
- powderblue: "#b0e0e6",
- purple: "#800080",
- red: "#ff0000",
- rosybrown: "#bc8f8f",
- royalblue: "#4169e1",
- saddlebrown: "#8b4513",
- salmon: "#fa8072",
- sandybrown: "#f4a460",
- seagreen: "#2e8b57",
- seashell: "#fff5ee",
- sienna: "#a0522d",
- silver: "#c0c0c0",
- skyblue: "#87ceeb",
- slateblue: "#6a5acd",
- slategray: "#708090",
- snow: "#fffafa",
- springgreen: "#00ff7f",
- steelblue: "#4682b4",
- tan: "#d2b48c",
- teal: "#008080",
- thistle: "#d8bfd8",
- tomato: "#ff6347",
- turquoise: "#40e0d0",
- violet: "#ee82ee",
- wheat: "#f5deb3",
- white: "#ffffff",
- whitesmoke: "#f5f5f5",
- yellow: "#ffff00",
- yellowgreen: "#9acd32"
- };
-
// Stores states for pushStyle() and popStyle().
var styleArray = [];
@@ -1609,7 +1672,7 @@
// These verts are used for the fill and stroke using TRIANGLE_FAN and LINE_LOOP
var rectVerts = new Float32Array([0,0,0, 0,1,0, 1,1,0, 1,0,0]);
- var rectNorms = new Float32Array([0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1]);
+ var rectNorms = new Float32Array([0,0,1, 0,0,1, 0,0,1, 0,0,1]);
// Vertex shader for points and lines
var vShaderSrcUnlitShape =
@@ -1679,6 +1742,8 @@
" }"+
"}";
+ var webglMaxTempsWorkaround = /Windows/.test(navigator.userAgent);
+
// Vertex shader for boxes and spheres
var vertexShaderSource3D =
"varying vec4 frontColor;" +
@@ -1754,7 +1819,7 @@
"void DirectionalLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
" float powerfactor = 0.0;" +
" float nDotVP = max(0.0, dot( vertNormal, normalize(-light.position) ));" +
- " float nDotVH = max(0.0, dot( vertNormal, normalize(-light.position-ecPos )));" +
+ " float nDotVH = max(0.0, dot( vertNormal, normalize(-light.position-normalize(ecPos) )));" +
" if( nDotVP != 0.0 ){" +
" powerfactor = pow( nDotVH, shininess );" +
@@ -1764,7 +1829,7 @@
" spec += specular * powerfactor;" +
"}" +
- "void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" +
+ "void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
" float powerfactor;" +
// Get the vector from the light to the vertex
@@ -1779,7 +1844,7 @@
" float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" +
" float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
- " vec3 halfVector = normalize( VP + eye );" +
+ " vec3 halfVector = normalize( VP - normalize(ecPos) );" +
" float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
" if( nDotVP == 0.0) {" +
@@ -1795,7 +1860,7 @@
/*
*/
- "void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" +
+ "void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
" float spotAttenuation;" +
" float powerfactor;" +
@@ -1813,18 +1878,19 @@
" float spotDot = dot( VP, ldir );" +
// if the vertex falls inside the cone
- // The following is failing on Windows systems
- // removed until we find a workaround
- //" if( spotDot < cos( light.angle ) ) {" +
- //" spotAttenuation = pow( spotDot, light.concentration );" +
- //" }" +
- //" else{" +
- " spotAttenuation = 1.0;" +
- //" }" +
+ (webglMaxTempsWorkaround ? // Windows reports max temps error if light.angle is used
+ " spotAttenuation = 1.0; " :
+ " if( spotDot > cos( light.angle ) ) {" +
+ " spotAttenuation = pow( spotDot, light.concentration );" +
+ " }" +
+ " else{" +
+ " spotAttenuation = 0.0;" +
+ " }" +
" attenuation *= spotAttenuation;" +
+ "") +
" float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
- " vec3 halfVector = normalize( VP + eye );" +
+ " vec3 halfVector = normalize( VP - normalize(ecPos) );" +
" float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
" if( nDotVP == 0.0 ) {" +
@@ -1849,11 +1915,14 @@
" col = aColor;" +
" }" +
- " vec3 norm = vec3( normalTransform * vec4( Normal, 0.0 ) );" +
+ // We use the sphere vertices as the normals when we create the sphere buffer.
+ // But this only works if the sphere vertices are unit length, so we
+ // have to normalize the normals here. Since this is only required for spheres
+ // we could consider placing this in a conditional later on.
+ " vec3 norm = normalize(vec3( normalTransform * vec4( Normal, 0.0 ) ));" +
" vec4 ecPos4 = view * model * vec4(Vertex,1.0);" +
" vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" +
- " vec3 eye = vec3( 0.0, 0.0, 1.0 );" +
// If there were no lights this draw call, just use the
// assigned fill color of the shape and the specular value
@@ -1879,10 +1948,10 @@
" DirectionalLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
" }" +
" else if( l.type == 2 ) {" +
- " PointLight( finalDiffuse, finalSpecular, norm, ecPos, eye, l );" +
+ " PointLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
" }" +
" else {" +
- " SpotLight( finalDiffuse, finalSpecular, norm, ecPos, eye, l );" +
+ " SpotLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
" }" +
" }" +
@@ -2102,7 +2171,7 @@
}
}
- var imageModeCorner = function imageModeCorner(x, y, w, h, whAreSizes) {
+ var imageModeCorner = function(x, y, w, h, whAreSizes) {
return {
x: x,
y: y,
@@ -2112,7 +2181,7 @@
};
var imageModeConvert = imageModeCorner;
- var imageModeCorners = function imageModeCorners(x, y, w, h, whAreSizes) {
+ var imageModeCorners = function(x, y, w, h, whAreSizes) {
return {
x: x,
y: y,
@@ -2121,7 +2190,7 @@
};
};
- var imageModeCenter = function imageModeCenter(x, y, w, h, whAreSizes) {
+ var imageModeCenter = function(x, y, w, h, whAreSizes) {
return {
x: x - w / 2,
y: y - h / 2,
@@ -2164,14 +2233,39 @@
return programObject;
};
+ ////////////////////////////////////////////////////////////////////////////
+ // 2D/3D drawing handling
+ ////////////////////////////////////////////////////////////////////////////
+ // Objects for shared, 2D and 3D contexts
+ var DrawingShared = function() {};
+ var Drawing2D = function() {};
+ var Drawing3D = function() {};
+ var DrawingPre = function() {};
+
+ // Setup the prototype chain
+ Drawing2D.prototype = new DrawingShared();
+ Drawing2D.prototype.constructor = Drawing2D;
+ Drawing3D.prototype = new DrawingShared();
+ Drawing3D.prototype.constructor = Drawing3D;
+ DrawingPre.prototype = new DrawingShared();
+ DrawingPre.prototype.constructor = DrawingPre;
+
+ // A no-op function for when the user calls 3D functions from a 2D sketch
+ // We can change this to a throw or console.error() later if we want
+ DrawingShared.prototype.a3DOnlyFunction = function(){};
+
////////////////////////////////////////////////////////////////////////////
// Char handling
////////////////////////////////////////////////////////////////////////////
var charMap = {};
- var Char = p.Character = function Char(chr) {
+ var Char = p.Character = function(chr) {
if (typeof chr === 'string' && chr.length === 1) {
this.code = chr.charCodeAt(0);
+ } else if (typeof chr === 'number') {
+ this.code = chr;
+ } else if (chr instanceof Char) {
+ this.code = chr;
} else {
this.code = NaN;
}
@@ -2996,53 +3090,60 @@
*
* @return {PMatrix2D} a PMatrix2D
*/
- PShapeSVG.prototype.parseMatrix = function(str) {
- this.checkMatrix(2);
- var pieces = [];
- str.replace(/\s*(\w+)\((.*?)\)/g, function(all) {
- // get a list of transform definitions
- pieces.push(p.trim(all));
- });
- if (pieces.length === 0) {
- //p.println("Transformation:" + str + " is empty");
- return null;
- }
- for (var i = 0, j = pieces.length; i < j; i++) {
+ PShapeSVG.prototype.parseMatrix = (function() {
+ function getCoords(s) {
var m = [];
- pieces[i].replace(/\((.*?)\)/, (function() {
+ s.replace(/\((.*?)\)/, (function() {
return function(all, params) {
// get the coordinates that can be separated by spaces or a comma
m = params.replace(/,+/g, " ").split(/\s+/);
};
}()));
-
- if (pieces[i].indexOf("matrix") !== -1) {
- this.matrix.set(m[0], m[2], m[4], m[1], m[3], m[5]);
- } else if (pieces[i].indexOf("translate") !== -1) {
- var tx = m[0];
- var ty = (m.length === 2) ? m[1] : 0;
- this.matrix.translate(tx,ty);
- } else if (pieces[i].indexOf("scale") !== -1) {
- var sx = m[0];
- var sy = (m.length === 2) ? m[1] : m[0];
- this.matrix.scale(sx,sy);
- } else if (pieces[i].indexOf("rotate") !== -1) {
- var angle = m[0];
- if (m.length === 1) {
- this.matrix.rotate(p.radians(angle));
- } else if (m.length === 3) {
- this.matrix.translate(m[1], m[2]);
- this.matrix.rotate(p.radians(m[0]));
- this.matrix.translate(-m[1], -m[2]);
- }
- } else if (pieces[i].indexOf("skewX") !== -1) {
- this.matrix.skewX(parseFloat(m[0]));
- } else if (pieces[i].indexOf("skewY") !== -1) {
- this.matrix.skewY(m[0]);
- }
}
- return this.matrix;
- };
+
+ return function(str) {
+ this.checkMatrix(2);
+ var pieces = [];
+ str.replace(/\s*(\w+)\((.*?)\)/g, function(all) {
+ // get a list of transform definitions
+ pieces.push(p.trim(all));
+ });
+ if (pieces.length === 0) {
+ return null;
+ }
+
+ for (var i = 0, j = pieces.length; i < j; i++) {
+ var m = getCoords(pieces[i]);
+
+ if (pieces[i].indexOf("matrix") !== -1) {
+ this.matrix.set(m[0], m[2], m[4], m[1], m[3], m[5]);
+ } else if (pieces[i].indexOf("translate") !== -1) {
+ var tx = m[0];
+ var ty = (m.length === 2) ? m[1] : 0;
+ this.matrix.translate(tx,ty);
+ } else if (pieces[i].indexOf("scale") !== -1) {
+ var sx = m[0];
+ var sy = (m.length === 2) ? m[1] : m[0];
+ this.matrix.scale(sx,sy);
+ } else if (pieces[i].indexOf("rotate") !== -1) {
+ var angle = m[0];
+ if (m.length === 1) {
+ this.matrix.rotate(p.radians(angle));
+ } else if (m.length === 3) {
+ this.matrix.translate(m[1], m[2]);
+ this.matrix.rotate(p.radians(m[0]));
+ this.matrix.translate(-m[1], -m[2]);
+ }
+ } else if (pieces[i].indexOf("skewX") !== -1) {
+ this.matrix.skewX(parseFloat(m[0]));
+ } else if (pieces[i].indexOf("skewY") !== -1) {
+ this.matrix.skewY(m[0]);
+ }
+ }
+ return this.matrix;
+ };
+ }());
+
/**
* @member PShapeSVG
* The parseChildren() function parses the specified XMLElement
@@ -3109,17 +3210,19 @@
shape.parsePath();
} else if (name === "radialGradient") {
//return new RadialGradient(this, elem);
+ unimplemented('PShapeSVG.prototype.parseChild, name = radialGradient');
} else if (name === "linearGradient") {
//return new LinearGradient(this, elem);
+ unimplemented('PShapeSVG.prototype.parseChild, name = linearGradient');
} else if (name === "text") {
- //p.println("Text in SVG files is not currently supported " +
- // "convert text to outlines instead.");
+ unimplemented('PShapeSVG.prototype.parseChild, name = text');
} else if (name === "filter") {
- //p.println("Filters are not supported.");
+ unimplemented('PShapeSVG.prototype.parseChild, name = filter');
} else if (name === "mask") {
- //p.println("Masks are not supported.");
+ unimplemented('PShapeSVG.prototype.parseChild, name = mask');
} else {
- //p.println("Ignoring <" + name + "> tag.");
+ // ignoring
+ nop();
}
return shape;
};
@@ -3470,6 +3573,7 @@
}
} else if (valOf === 90) {
//Z
+ nop();
} else if (valOf === 122) { //z
this.close = true;
}
@@ -3581,8 +3685,7 @@
this.vertices.push(verts);
}
} else {
- //p.println("Error parsing polygon points: " +
- // "odd number of coordinates provided");
+ throw("Error parsing polygon points: odd number of coordinates provided");
}
}
};
@@ -4107,9 +4210,18 @@
* @param {Integer }lineNr the line in the XML data where the element starts
*/
var XMLElement = p.XMLElement = function() {
+ this.attributes = [];
+ this.children = [];
+ this.fullName = null;
+ this.name = null;
+ this.namespace = "";
+ this.content = null;
+ this.parent = null;
+ this.lineNr = "";
+ this.systemID = "";
+ this.type = "ELEMENT";
+
if (arguments.length === 4) {
- this.attributes = [];
- this.children = [];
this.fullName = arguments[0] || "";
if (arguments[1]) {
this.name = arguments[1];
@@ -4122,46 +4234,15 @@
}
}
this.namespace = arguments[1];
- this.content = "";
this.lineNr = arguments[3];
this.systemID = arguments[2];
- this.parent = null;
}
- else if ((arguments.length === 2 && arguments[1].indexOf(".") > -1) ) { // filename or svg xml element
- this.attributes = [];
- this.children = [];
- this.fullName = "";
- this.name = "";
- this.namespace = "";
- this.content = "";
- this.systemID = "";
- this.lineNr = "";
- this.parent = null;
+ else if ((arguments.length === 2 && arguments[1].indexOf(".") > -1) ) {
+ // filename or svg xml element
this.parse(arguments[arguments.length -1]);
} else if (arguments.length === 1 && typeof arguments[0] === "string"){
- //xml string
- this.attributes = [];
- this.children = [];
- this.fullName = "";
- this.name = "";
- this.namespace = "";
- this.content = "";
- this.systemID = "";
- this.lineNr = "";
- this.parent = null;
this.parse(arguments[0]);
}
- else { //empty ctor
- this.attributes = [];
- this.children = [];
- this.fullName = "";
- this.name = "";
- this.namespace = "";
- this.content = "";
- this.systemID = "";
- this.lineNr = "";
- this.parent = null;
- }
};
/**
* XMLElement methods
@@ -4171,7 +4252,8 @@
XMLElement.prototype = {
/**
* @member XMLElement
- * The parse() function retrieves the file via ajax() and uses DOMParser() parseFromString method to make an XML document
+ * The parse() function retrieves the file via ajax() and uses DOMParser()
+ * parseFromString method to make an XML document
* @addon
*
* @param {String} filename name of the XML/SVG file to load
@@ -4198,6 +4280,61 @@
throw(e);
}
},
+ /**
+ * @member XMLElement
+ * Internal helper function for parse().
+ * Loops through the
+ * @addon
+ *
+ * @param {XMLElement} parent the parent node
+ * @param {XML document childNodes} elementpath the remaining nodes that need parsing
+ *
+ * @return {XMLElement} the new element and its children elements
+ */
+ parseChildrenRecursive: function (parent , elementpath){
+ var xmlelement,
+ xmlattribute,
+ tmpattrib,
+ l, m,
+ child;
+ if (!parent) { // this element is the root element
+ this.fullName = elementpath.localName;
+ this.name = elementpath.nodeName;
+ xmlelement = this;
+ } else { // this element has a parent
+ xmlelement = new XMLElement(elementpath.localName, elementpath.nodeName, "", "");
+ xmlelement.parent = parent;
+ }
+
+ // if this is a text node, return a PCData element, instead of an XML element.
+ if(elementpath.nodeType === 3 && elementpath.textContent !== "") {
+ return this.createPCDataElement(elementpath.textContent);
+ }
+
+ // bind all attributes
+ for (l = 0, m = elementpath.attributes.length; l < m; l++) {
+ tmpattrib = elementpath.attributes[l];
+ xmlattribute = new XMLAttribute(tmpattrib.getname,
+ tmpattrib.nodeName,
+ tmpattrib.namespaceURI,
+ tmpattrib.nodeValue,
+ tmpattrib.nodeType);
+ xmlelement.attributes.push(xmlattribute);
+ }
+
+ // bind all children
+ for (l = 0, m = elementpath.childNodes.length; l < m; l++) {
+ var node = elementpath.childNodes[l];
+ if (node.nodeType === 1 || node.nodeType === 3) { // ELEMENT_NODE or TEXT_NODE
+ child = xmlelement.parseChildrenRecursive(xmlelement, node);
+ if (child !== null) {
+ xmlelement.children.push(child);
+ }
+ }
+ }
+
+ return xmlelement;
+ },
/**
* @member XMLElement
* The createElement() function Creates an empty element
@@ -4214,6 +4351,23 @@
return new XMLElement(arguments[0], arguments[1], arguments[2], arguments[3]);
}
},
+ /**
+ * @member XMLElement
+ * The createPCDataElement() function creates an element to be used for #PCDATA content.
+ * Because Processing discards whitespace TEXT nodes, this method will not build an element
+ * if the passed content is empty after trimming for whitespace.
+ *
+ * @return {XMLElement} new "test" XMLElement, or null if content consists only of whitespace
+ */
+ createPCDataElement: function (content) {
+ if(content.replace(/^\s+$/g,"") === "") {
+ return null;
+ }
+ var pcdata = new XMLElement();
+ pcdata.content = content;
+ pcdata.type = "TEXT";
+ return pcdata;
+ },
/**
* @member XMLElement
* The hasAttribute() function returns whether an attribute exists
@@ -4232,52 +4386,42 @@
},
/**
* @member XMLElement
- * The createPCDataElement() function creates an element to be used for #PCDATA content
- *
- * @return {XMLElement} new XMLElement element
- */
- createPCDataElement: function () {
- return new XMLElement();
- },
- /**
- * @member XMLElement
- * The equals() function checks to see if the element being passed in equals another element
- *
- * @param {Object} rawElement the element to compare to
- *
- * @return {boolean} true if the element equals another element
- */
- equals: function(object){
- if (typeof object === "Object") {
- return this.equalsXMLElement(object);
- }
- },
- /**
- * @member XMLElement
- * The equalsXMLElement() function checks to see if the XMLElement being passed in equals another XMLElement
+ * The equals() function checks to see if the XMLElement being passed in equals another XMLElement
*
* @param {XMLElement} rawElement the element to compare to
*
* @return {boolean} true if the element equals another element
*/
- equalsXMLElement: function (object) {
- if (object instanceof XMLElement) {
- var i, j;
- if (this.name !== object.getLocalName()) { return false; }
- if (this.attributes.length !== object.getAttributeCount()) { return false; }
- for (i = 0, j = this.attributes.length; i < j; i++){
- if (! object.hasAttribute(this.attributes[i].getName(), this.attributes[i].getNamespace())) { return false; }
- if (this.attributes[i].getValue() !== object.attributes[i].getValue()) { return false; }
- if (this.attributes[i].getType() !== object.attributes[i].getType()) { return false; }
- }
- if (this.children.length !== object.getChildCount()) { return false; }
+ equals: function(other) {
+ if (!(other instanceof XMLElement)) {
+ return false;
+ }
+ var i, j;
+ if (this.name !== other.getLocalName()) { return false; }
+ if (this.attributes.length !== other.getAttributeCount()) { return false; }
+ // attributes may be ordered differently
+ if (this.attributes.length !== other.attributes.length) { return false; }
+ var attr_name, attr_ns, attr_value, attr_type, attr_other;
+ for (i = 0, j = this.attributes.length; i < j; i++) {
+ attr_name = this.attributes[i].getName();
+ attr_ns = this.attributes[i].getNamespace();
+ attr_other = other.findAttribute(attr_name, attr_ns);
+ if (attr_other === null) { return false; }
+ if (this.attributes[i].getValue() !== attr_other.getValue()) { return false; }
+ if (this.attributes[i].getType() !== attr_other.getType()) { return false; }
+ }
+ // children must be ordered identically
+ if (this.children.length !== other.getChildCount()) { return false; }
+ if (this.children.length>0) {
var child1, child2;
for (i = 0, j = this.children.length; i < j; i++) {
- child1 = this.getChildAtIndex(i);
- child2 = object.getChildAtIndex(i);
- if (! child1.equalsXMLElement(child2)) { return false; }
+ child1 = this.getChild(i);
+ child2 = other.getChild(i);
+ if (!child1.equals(child2)) { return false; }
}
return true;
+ } else {
+ return (this.content === other.content);
}
},
/**
@@ -4287,7 +4431,12 @@
* @return {String} the (possibly null) content
*/
getContent: function(){
- return this.content;
+ if (this.type === "TEXT") {
+ return this.content; }
+ else if (this.children.length === 1 && this.children[0].type === "TEXT") {
+ return this.children[0].content;
+ }
+ return null;
},
/**
* @member XMLElement
@@ -4344,6 +4493,13 @@
return this.getAttribute(arguments[0], arguments[1],arguments[2]);
}
},
+ /**
+ * Processing 1.5 XML API wrapper for the generic String
+ * attribute getter. This may only take one argument.
+ */
+ getString: function(attributeName) {
+ return this.getStringAttribute(attributeName);
+ },
/**
* @member XMLElement
* The getFloatAttribute() function returns the float attribute of the element.
@@ -4364,6 +4520,13 @@
return this.getAttribute(arguments[0], arguments[1],arguments[2]);
}
},
+ /**
+ * Processing 1.5 XML API wrapper for the generic float
+ * attribute getter. This may only take one argument.
+ */
+ getFloat: function(attributeName) {
+ return this.getFloatAttribute(attributeName);
+ },
/**
* @member XMLElement
* The getIntAttribute() function returns the integer attribute of the element.
@@ -4384,6 +4547,13 @@
return this.getAttribute(arguments[0], arguments[1],arguments[2]);
}
},
+ /**
+ * Processing 1.5 XML API wrapper for the generic int
+ * attribute getter. This may only take one argument.
+ */
+ getInt: function(attributeName) {
+ return this.getIntAttribute(attributeName);
+ },
/**
* @member XMLElement
* The hasChildren() function returns whether the element has children.
@@ -4546,51 +4716,6 @@
}
return kidMatches;
},
- /**
- * @member XMLElement
- * Internal helper function for parse().
- * Loops through the
- * @addon
- *
- * @param {XMLElement} parent the parent node
- * @param {XML document childNodes} elementpath the remaining nodes that need parsing
- *
- * @return {XMLElement} the new element and its children elements
- */
- parseChildrenRecursive: function (parent , elementpath){
- var xmlelement,
- xmlattribute,
- tmpattrib,
- l, m;
- if (!parent) {
- this.fullName = elementpath.localName;
- this.name = elementpath.nodeName;
- this.content = elementpath.textContent || "";
- xmlelement = this;
- } else { // a parent
- xmlelement = new XMLElement(elementpath.localName, elementpath.nodeName, "", "");
- xmlelement.content = elementpath.textContent || "";
- xmlelement.parent = parent;
- }
-
- for (l = 0, m = elementpath.attributes.length; l < m; l++) {
- tmpattrib = elementpath.attributes[l];
- xmlattribute = new XMLAttribute(tmpattrib.getname,
- tmpattrib.nodeName,
- tmpattrib.namespaceURI,
- tmpattrib.nodeValue,
- tmpattrib.nodeType);
- xmlelement.attributes.push(xmlattribute);
- }
-
- for (l = 0, m = elementpath.childNodes.length; l < m; l++) {
- var node = elementpath.childNodes[l];
- if (node.nodeType === 1) { // ELEMENT_NODE type
- xmlelement.children.push(xmlelement.parseChildrenRecursive(xmlelement, node));
- }
- }
- return xmlelement;
- },
/**
* @member XMLElement
* The isLeaf() function returns whether the element is a leaf element.
@@ -4639,7 +4764,7 @@
removeChild: function(child) {
if (child) {
for (var i = 0, j = this.children.length; i < j; i++) {
- if (this.children[i].equalsXMLElement(child)) {
+ if (this.children[i].equals(child)) {
this.children.splice(i, 1);
break;
}
@@ -4655,7 +4780,6 @@
removeChildAtIndex: function(index) {
if (this.children.length > index) { //make sure its not outofbounds
this.children.splice(index, 1);
- return;
}
},
/**
@@ -4674,6 +4798,7 @@
return this.attributes[i];
}
}
+ return null;
},
/**
* @member XMLElement
@@ -4687,7 +4812,7 @@
if (arguments.length === 3) {
var index = arguments[0].indexOf(':');
var name = arguments[0].substring(index + 1);
- attr = this.findAttribute( name, arguments[1] );
+ attr = this.findAttribute(name, arguments[1]);
if (attr) {
attr.setValue(arguments[2]);
} else {
@@ -4704,6 +4829,27 @@
}
}
},
+ /**
+ * Processing 1.5 XML API wrapper for the generic String
+ * attribute setter. This must take two arguments.
+ */
+ setString: function(attribute, value) {
+ this.setAttribute(attribute, value);
+ },
+ /**
+ * Processing 1.5 XML API wrapper for the generic int
+ * attribute setter. This must take two arguments.
+ */
+ setInt: function(attribute, value) {
+ this.setAttribute(attribute, value);
+ },
+ /**
+ * Processing 1.5 XML API wrapper for the generic float
+ * attribute setter. This must take two arguments.
+ */
+ setFloat: function(attribute, value) {
+ this.setAttribute(attribute, value);
+ },
/**
* @member XMLElement
* The setContent() function sets the #PCDATA content. It is an error to call this method with a
@@ -4712,6 +4858,8 @@
* @param {String} content the (possibly null) content
*/
setContent: function(content) {
+ if (this.children.length>0) {
+ Processing.debug("Tried to set content for XMLElement with children"); }
this.content = content;
},
/**
@@ -4748,14 +4896,74 @@
getName: function() {
return this.fullName;
},
+ /**
+ * @member XMLElement
+ * The getLocalName() function returns the local name (i.e. the name excluding an eventual namespace
+ * prefix) of the element.
+ *
+ * @return {String} the name, or null if the element only contains #PCDATA.
+ */
getLocalName: function() {
return this.name;
},
+ /**
+ * @member XMLElement
+ * The getAttributeCount() function returns the number of attributes for the node
+ * that this XMLElement represents.
+ *
+ * @return {int} the number of attributes in this XMLelement
+ */
getAttributeCount: function() {
return this.attributes.length;
- }
+ },
+ /**
+ * @member XMLElement
+ * The toString() function returns the XML definition of an XMLElement.
+ *
+ * @return {String} the XML definition of this XMLElement
+ */
+ toString: function() {
+ // shortcut for text nodes
+ if(this.type==="TEXT") { return this.content; }
+
+ // real XMLElements
+ var tagstring = (this.namespace !== "" && this.namespace !== this.name ? this.namespace + ":" : "") + this.name;
+ var xmlstring = "<" + tagstring;
+ var a,c;
+
+ // serialize the attributes to XML string
+ for (a = 0; a";
+ } else {
+ xmlstring += ">" + this.content + ""+tagstring+">";
+ }
+ } else {
+ xmlstring += ">";
+ for (c = 0; c";
+ }
+ return xmlstring;
+ }
};
+ /**
+ * static Processing 1.5 XML API wrapper for the
+ * parse method. This may only take one argument.
+ */
+ XMLElement.parse = function(xmlstring) {
+ var element = new XMLElement();
+ element.parse(xmlstring);
+ return element;
+ };
////////////////////////////////////////////////////////////////////////////
// 2D Matrix
@@ -4765,7 +4973,7 @@
* in the matrix, then number of digits left of the decimal.
* Call from PMatrix2D and PMatrix3D's print() function.
*/
- var printMatrixHelper = function printMatrixHelper(elements) {
+ var printMatrixHelper = function(elements) {
var big = 0;
for (var i = 0; i < elements.length; i++) {
if (i !== 0) {
@@ -4870,6 +5078,16 @@
translate: function(tx, ty) {
this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
+ },
+ /**
+ * @member PMatrix2D
+ * The invTranslate() function translates this matrix by moving the current coordinates to the negative location specified by tx and ty.
+ *
+ * @param {float} tx the x-axis coordinate to move to
+ * @param {float} ty the y-axis coordinate to move to
+ */
+ invTranslate: function(tx, ty) {
+ this.translate(-tx, -ty);
},
/**
* @member PMatrix2D
@@ -5012,6 +5230,20 @@
this.elements[3] *= sx;
this.elements[4] *= sy;
}
+ },
+ /**
+ * @member PMatrix2D
+ * The invScale() function decreases or increases the size of a shape by contracting and expanding vertices. When only one parameter is specified scale will occur in all dimensions.
+ * This is equivalent to a two parameter call.
+ *
+ * @param {float} sx the amount to scale on the x-axis
+ * @param {float} sy the amount to scale on the y-axis
+ */
+ invScale: function(sx, sy) {
+ if (sx && !sy) {
+ sy = sx;
+ }
+ this.scale(1 / sx, 1 / sy);
},
/**
* @member PMatrix2D
@@ -5102,6 +5334,15 @@
rotateZ: function(angle) {
this.rotate(angle);
},
+ /**
+ * @member PMatrix2D
+ * The invRotateZ() function rotates the matrix in opposite direction.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ invRotateZ: function(angle) {
+ this.rotateZ(angle - Math.PI);
+ },
/**
* @member PMatrix2D
* The print() function prints out the elements of this matrix
@@ -5122,7 +5363,7 @@
* PMatrix3D is a 4x4 matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements.
* If no parameters are provided the matrix is set to the identity matrix.
*/
- var PMatrix3D = p.PMatrix3D = function PMatrix3D() {
+ var PMatrix3D = p.PMatrix3D = function() {
// When a matrix is created, it is set to an identity matrix
this.reset();
};
@@ -5209,7 +5450,7 @@
this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
},
/**
- * @member PMatrix2D
+ * @member PMatrix3D
* The transpose() function transpose this matrix.
*/
transpose: function() {
@@ -5406,7 +5647,7 @@
}
},
/**
- * @member PMatrix2D
+ * @member PMatrix3D
* The invApply() function applies the inverted matrix to this matrix.
*
* @param {float} m00 the first element of the matrix
@@ -5681,7 +5922,7 @@
* @private
* The matrix stack stores the transformations and translations that occur within the space.
*/
- var PMatrixStack = p.PMatrixStack = function PMatrixStack() {
+ var PMatrixStack = p.PMatrixStack = function() {
this.matrixStack = [];
};
@@ -5691,13 +5932,8 @@
*
* @param {Object | Array} matrix the matrix to be pushed into the stack
*/
- PMatrixStack.prototype.load = function load() {
- var tmpMatrix;
- if (p.use3DContext) {
- tmpMatrix = new PMatrix3D();
- } else {
- tmpMatrix = new PMatrix2D();
- }
+ PMatrixStack.prototype.load = function() {
+ var tmpMatrix = drawing.$newPMatrix();
if (arguments.length === 1) {
tmpMatrix.set(arguments[0]);
@@ -5707,11 +5943,19 @@
this.matrixStack.push(tmpMatrix);
};
+ Drawing2D.prototype.$newPMatrix = function() {
+ return new PMatrix2D();
+ };
+
+ Drawing3D.prototype.$newPMatrix = function() {
+ return new PMatrix3D();
+ };
+
/**
* @member PMatrixStack
* push adds a duplicate of the top of the stack onto the stack - uses the peek function
*/
- PMatrixStack.prototype.push = function push() {
+ PMatrixStack.prototype.push = function() {
this.matrixStack.push(this.peek());
};
@@ -5721,7 +5965,7 @@
*
* @returns {Object} the matrix at the top of the stack
*/
- PMatrixStack.prototype.pop = function pop() {
+ PMatrixStack.prototype.pop = function() {
return this.matrixStack.pop();
};
@@ -5731,13 +5975,8 @@
*
* @returns {Object} the matrix at the top of the stack
*/
- PMatrixStack.prototype.peek = function peek() {
- var tmpMatrix;
- if (p.use3DContext) {
- tmpMatrix = new PMatrix3D();
- } else {
- tmpMatrix = new PMatrix2D();
- }
+ PMatrixStack.prototype.peek = function() {
+ var tmpMatrix = drawing.$newPMatrix();
tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]);
return tmpMatrix;
@@ -5749,7 +5988,7 @@
*
* @param {Object | Array} matrix the matrix to be multiplied into the stack
*/
- PMatrixStack.prototype.mult = function mult(matrix) {
+ PMatrixStack.prototype.mult = function(matrix) {
this.matrixStack[this.matrixStack.length - 1].apply(matrix);
};
@@ -5981,7 +6220,7 @@
*/
p.subset = function(array, offset, length) {
if (arguments.length === 2) {
- return array.slice(offset, array.length - offset);
+ return array.slice(offset, array.length);
} else if (arguments.length === 3) {
return array.slice(offset, offset + length);
}
@@ -6427,7 +6666,7 @@
*
* @see colorMode
*/
- p.color = function color(aValue1, aValue2, aValue3, aValue4) {
+ p.color = function(aValue1, aValue2, aValue3, aValue4) {
// 4 arguments: (R, G, B, A) or (H, S, B, A)
if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) {
@@ -6600,7 +6839,7 @@
return p.color.toHSB(colInt)[0];
};
- var verifyChannel = function verifyChannel(aColor) {
+ var verifyChannel = function(aColor) {
if (aColor.constructor === Array) {
return aColor;
} else {
@@ -6702,7 +6941,7 @@
* @see blendColor
* @see color
*/
- p.lerpColor = function lerpColor(c1, c2, amt) {
+ p.lerpColor = function(c1, c2, amt) {
// Get RGBA values for Color 1 to floats
var colorBits1 = p.color(c1);
var r1 = (colorBits1 & PConstants.RED_MASK) >>> 16;
@@ -6766,7 +7005,7 @@
* @see fill
* @see stroke
*/
- p.colorMode = function colorMode() { // mode, range1, range2, range3, range4
+ p.colorMode = function() { // mode, range1, range2, range3, range4
curColorMode = arguments[0];
if (arguments.length > 1) {
colorModeX = arguments[1];
@@ -6866,7 +7105,7 @@
* @see resetMatrix
* @see applyMatrix
*/
- p.printMatrix = function printMatrix() {
+ p.printMatrix = function() {
modelView.print();
};
@@ -6893,13 +7132,15 @@
* @see rotateY
* @see rotateZ
*/
- p.translate = function translate(x, y, z) {
- if (p.use3DContext) {
- forwardTransform.translate(x, y, z);
- reverseTransform.invTranslate(x, y, z);
- } else {
- curContext.translate(x, y);
- }
+ Drawing2D.prototype.translate = function(x, y) {
+ forwardTransform.translate(x, y);
+ reverseTransform.invTranslate(x, y);
+ curContext.translate(x, y);
+ };
+
+ Drawing3D.prototype.translate = function(x, y, z) {
+ forwardTransform.translate(x, y, z);
+ reverseTransform.invTranslate(x, y, z);
};
/**
@@ -6926,13 +7167,15 @@
* @see rotateY
* @see rotateZ
*/
- p.scale = function scale(x, y, z) {
- if (p.use3DContext) {
- forwardTransform.scale(x, y, z);
- reverseTransform.invScale(x, y, z);
- } else {
- curContext.scale(x, y || x);
- }
+ Drawing2D.prototype.scale = function(x, y) {
+ forwardTransform.scale(x, y);
+ reverseTransform.invScale(x, y);
+ curContext.scale(x, y || x);
+ };
+
+ Drawing3D.prototype.scale = function(x, y, z) {
+ forwardTransform.scale(x, y, z);
+ reverseTransform.invScale(x, y, z);
};
/**
@@ -6951,12 +7194,15 @@
* @see rotateY
* @see rotateZ
*/
- p.pushMatrix = function pushMatrix() {
- if (p.use3DContext) {
- userMatrixStack.load(modelView);
- } else {
- saveContext();
- }
+ Drawing2D.prototype.pushMatrix = function() {
+ userMatrixStack.load(modelView);
+ userReverseMatrixStack.load(modelViewInv);
+ saveContext();
+ };
+
+ Drawing3D.prototype.pushMatrix = function() {
+ userMatrixStack.load(modelView);
+ userReverseMatrixStack.load(modelViewInv);
};
/**
@@ -6970,12 +7216,15 @@
* @see popMatrix
* @see pushMatrix
*/
- p.popMatrix = function popMatrix() {
- if (p.use3DContext) {
- modelView.set(userMatrixStack.pop());
- } else {
- restoreContext();
- }
+ Drawing2D.prototype.popMatrix = function() {
+ modelView.set(userMatrixStack.pop());
+ modelViewInv.set(userReverseMatrixStack.pop());
+ restoreContext();
+ };
+
+ Drawing3D.prototype.popMatrix = function() {
+ modelView.set(userMatrixStack.pop());
+ modelViewInv.set(userReverseMatrixStack.pop());
};
/**
@@ -6988,13 +7237,15 @@
* @see applyMatrix
* @see printMatrix
*/
- p.resetMatrix = function resetMatrix() {
- if (p.use3DContext) {
- forwardTransform.reset();
- reverseTransform.reset();
- } else {
- curContext.setTransform(1,0,0,1,0,0);
- }
+ Drawing2D.prototype.resetMatrix = function() {
+ forwardTransform.reset();
+ reverseTransform.reset();
+ curContext.setTransform(1,0,0,1,0,0);
+ };
+
+ Drawing3D.prototype.resetMatrix = function() {
+ forwardTransform.reset();
+ reverseTransform.reset();
};
/**
@@ -7011,19 +7262,21 @@
* @see resetMatrix
* @see printMatrix
*/
- p.applyMatrix = function applyMatrix() {
+ DrawingShared.prototype.applyMatrix = function() {
var a = arguments;
- if (!p.use3DContext) {
- for (var cnt = a.length; cnt < 16; cnt++) {
- a[cnt] = 0;
- }
- a[10] = a[15] = 1;
- }
-
forwardTransform.apply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
reverseTransform.invApply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
};
+ Drawing2D.prototype.applyMatrix = function() {
+ var a = arguments;
+ for (var cnt = a.length; cnt < 16; cnt++) {
+ a[cnt] = 0;
+ }
+ a[10] = a[15] = 1;
+ DrawingShared.prototype.applyMatrix.apply(this, a);
+ };
+
/**
* Rotates a shape around the x-axis the amount specified by the angle parameter. Angles should be
* specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
@@ -7076,6 +7329,10 @@
p.rotateZ = function(angleInRadians) {
forwardTransform.rotateZ(angleInRadians);
reverseTransform.invRotateZ(angleInRadians);
+ if (p.use3DContext) {
+ return;
+ }
+ curContext.rotate(angleInRadians);
};
/**
@@ -7128,13 +7385,12 @@
* @see popMatrix
* @see pushMatrix
*/
- p.rotate = function rotate(angleInRadians) {
- if (p.use3DContext) {
- forwardTransform.rotateZ(angleInRadians);
- reverseTransform.invRotateZ(angleInRadians);
- } else {
- curContext.rotate(angleInRadians);
- }
+ Drawing2D.prototype.rotate = function(angleInRadians) {
+ p.rotateZ(angleInRadians);
+ };
+
+ Drawing3D.prototype.rotate = function(angleInRadians) {
+ p.rotateZ(angleInRadians);
};
/**
@@ -7151,7 +7407,7 @@
*
* @see popStyle
*/
- p.pushStyle = function pushStyle() {
+ p.pushStyle = function() {
// Save the canvas state.
saveContext();
@@ -7186,7 +7442,7 @@
*
* @see pushStyle
*/
- p.popStyle = function popStyle() {
+ p.popStyle = function() {
var oldState = styleArray.pop();
if (oldState) {
@@ -7229,7 +7485,7 @@
* @see day
* @see month
*/
- p.year = function year() {
+ p.year = function() {
return new Date().getFullYear();
};
/**
@@ -7245,7 +7501,7 @@
* @see day
* @see year
*/
- p.month = function month() {
+ p.month = function() {
return new Date().getMonth() + 1;
};
/**
@@ -7261,7 +7517,7 @@
* @see month
* @see year
*/
- p.day = function day() {
+ p.day = function() {
return new Date().getDate();
};
/**
@@ -7277,7 +7533,7 @@
* @see day
* @see year
*/
- p.hour = function hour() {
+ p.hour = function() {
return new Date().getHours();
};
/**
@@ -7293,7 +7549,7 @@
* @see day
* @see year
*/
- p.minute = function minute() {
+ p.minute = function() {
return new Date().getMinutes();
};
/**
@@ -7309,7 +7565,7 @@
* @see day
* @see year
*/
- p.second = function second() {
+ p.second = function() {
return new Date().getSeconds();
};
/**
@@ -7325,7 +7581,7 @@
* @see day
* @see year
*/
- p.millis = function millis() {
+ p.millis = function() {
return new Date().getTime() - start;
};
@@ -7343,7 +7599,7 @@
* @see noLoop
* @see loop
*/
- p.redraw = function redraw() {
+ DrawingShared.prototype.redraw = function() {
var sec = (new Date().getTime() - timeSinceLastFPS) / 1000;
framesSinceLastFPS++;
var fps = framesSinceLastFPS / sec;
@@ -7356,28 +7612,40 @@
}
p.frameCount++;
+ };
+
+ Drawing2D.prototype.redraw = function() {
+ DrawingShared.prototype.redraw.apply(this, arguments);
+
+ curContext.lineWidth = lineWidth;
+ inDraw = true;
+
+ saveContext();
+ p.draw();
+ restoreContext();
+
+ inDraw = false;
+ };
+
+ Drawing3D.prototype.redraw = function() {
+ DrawingShared.prototype.redraw.apply(this, arguments);
inDraw = true;
- if (p.use3DContext) {
- // even if the color buffer isn't cleared with background(),
- // the depth buffer needs to be cleared regardless.
- curContext.clear(curContext.DEPTH_BUFFER_BIT);
- curContextCache = { attributes: {}, locations: {} };
- // Delete all the lighting states and the materials the
- // user set in the last draw() call.
- p.noLights();
- p.lightFalloff(1, 0, 0);
- p.shininess(1);
- p.ambient(255, 255, 255);
- p.specular(0, 0, 0);
- p.camera();
- p.draw();
- } else {
- saveContext();
- p.draw();
- restoreContext();
- }
+ // even if the color buffer isn't cleared with background(),
+ // the depth buffer needs to be cleared regardless.
+ curContext.clear(curContext.DEPTH_BUFFER_BIT);
+ curContextCache = { attributes: {}, locations: {} };
+ // Delete all the lighting states and the materials the
+ // user set in the last draw() call.
+ p.noLights();
+ p.lightFalloff(1, 0, 0);
+ p.shininess(1);
+ p.ambient(255, 255, 255);
+ p.specular(0, 0, 0);
+ p.emissive(0, 0, 0);
+ p.camera();
+ p.draw();
inDraw = false;
};
@@ -7401,7 +7669,7 @@
* @see draw
* @see loop
*/
- p.noLoop = function noLoop() {
+ p.noLoop = function() {
doLoop = false;
loopStarted = false;
clearInterval(looping);
@@ -7415,7 +7683,7 @@
*
* @see noLoop
*/
- p.loop = function loop() {
+ p.loop = function() {
if (loopStarted) {
return;
}
@@ -7447,7 +7715,7 @@
*
* @see delay
*/
- p.frameRate = function frameRate(aRate) {
+ p.frameRate = function(aRate) {
curFrameRate = aRate;
curMsPerFrame = 1000 / curFrameRate;
@@ -7469,7 +7737,7 @@
*
* @returns none
*/
- p.exit = function exit() {
+ p.exit = function() {
window.clearInterval(looping);
removeInstance(p.externals.canvas.id);
@@ -7516,7 +7784,7 @@
*
* @see noCursor
*/
- p.cursor = function cursor() {
+ p.cursor = function() {
if (arguments.length > 1 || (arguments.length === 1 && arguments[0] instanceof p.PImage)) {
var image = arguments[0],
x, y;
@@ -7550,7 +7818,7 @@
*
* @see cursor
*/
- p.noCursor = function noCursor() {
+ p.noCursor = function() {
curCursor = curElement.style.cursor = PConstants.NOCURSOR;
};
@@ -7574,11 +7842,11 @@
// PGraphics methods
// TODO: These functions are suppose to be called before any operations are called on the
// PGraphics object. They currently do nothing.
- p.beginDraw = function beginDraw() {};
- p.endDraw = function endDraw() {};
+ p.beginDraw = function() {};
+ p.endDraw = function() {};
// Imports an external Processing.js library
- p.Import = function Import(lib) {
+ p.Import = function(lib) {
// Replace evil-eval method with a DOM