Full Code of pboyer/flood for AI

master c86ab0b96034 cached
194 files
1.2 MB
342.4k tokens
165 symbols
1 requests
Download .txt
Showing preview only (1,311K chars total). Download the full file or copy to clipboard to get everything.
Repository: pboyer/flood
Branch: master
Commit: c86ab0b96034
Files: 194
Total size: 1.2 MB

Directory structure:
gitextract_88nis6rk/

├── .bowerrc
├── .editorconfig
├── .gitattributes
├── .gitignore
├── Gruntfile.js
├── LICENSE
├── README.md
├── app/
│   ├── .htaccess
│   ├── 404.html
│   ├── app.html
│   ├── customizer.html
│   ├── index.html
│   ├── package.json
│   ├── robots.txt
│   ├── scripts/
│   │   ├── collections/
│   │   │   ├── Connections.js
│   │   │   ├── Nodes.js
│   │   │   ├── SearchElements.js
│   │   │   ├── WorkspaceBrowserElements.js
│   │   │   └── Workspaces.js
│   │   ├── config.js
│   │   ├── customizer.js
│   │   ├── lib/
│   │   │   ├── OrbitControls.js
│   │   │   ├── Viewport.js
│   │   │   └── flood/
│   │   │       ├── async.js
│   │   │       ├── csg.js
│   │   │       ├── flood.js
│   │   │       ├── flood_csg.js
│   │   │       ├── flood_runner.js
│   │   │       ├── scheme.js
│   │   │       ├── scheme_async.js
│   │   │       └── test/
│   │   │           ├── flood_csg_test.js
│   │   │           ├── flood_lambda_test.js
│   │   │           ├── flood_runner_test.html
│   │   │           ├── flood_test.js
│   │   │           ├── scheme_async_test.html
│   │   │           └── scheme_eval_async_test.js
│   │   ├── main.js
│   │   ├── models/
│   │   │   ├── App.js
│   │   │   ├── Connection.js
│   │   │   ├── Feedback.js
│   │   │   ├── GeometryExport.js
│   │   │   ├── Help.js
│   │   │   ├── Login.js
│   │   │   ├── Marquee.js
│   │   │   ├── Node.js
│   │   │   ├── Runner.js
│   │   │   ├── Search.js
│   │   │   ├── SearchElement.js
│   │   │   ├── Share.js
│   │   │   ├── Workspace.js
│   │   │   ├── WorkspaceBrowser.js
│   │   │   ├── WorkspaceBrowserElement.js
│   │   │   ├── WorkspaceResolver.js
│   │   │   └── customizer/
│   │   │       └── CustomizerApp.js
│   │   └── views/
│   │       ├── AppView.js
│   │       ├── ConnectionView.js
│   │       ├── FeedbackView.js
│   │       ├── HelpView.js
│   │       ├── LoginView.js
│   │       ├── MarqueeView.js
│   │       ├── NodeViews/
│   │       │   ├── Base.js
│   │       │   ├── CustomNode.js
│   │       │   ├── Input.js
│   │       │   ├── NodeViews.js
│   │       │   ├── Num.js
│   │       │   ├── Output.js
│   │       │   ├── Script.js
│   │       │   ├── ThreeCSG.js
│   │       │   └── Watch.js
│   │       ├── SearchElementView.js
│   │       ├── SearchView.js
│   │       ├── ShareView.js
│   │       ├── WorkspaceBrowserElementView.js
│   │       ├── WorkspaceBrowserView.js
│   │       ├── WorkspaceControlsView.js
│   │       ├── WorkspaceTabView.js
│   │       ├── WorkspaceView.js
│   │       └── customizer/
│   │           ├── CustomizerAppView.js
│   │           ├── CustomizerHeaderView.js
│   │           ├── CustomizerViewerView.js
│   │           ├── CustomizerWorkspaceView.js
│   │           └── widgets/
│   │               ├── Base.js
│   │               ├── Geometry.js
│   │               └── Number.js
│   └── styles/
│       ├── bootstrap.css
│       ├── customizer.css
│       └── main.css
├── bower.json
├── package.json
├── server/
│   ├── .gitignore
│   ├── .travis.yml
│   ├── app.js
│   ├── cluster_app.js
│   ├── config/
│   │   ├── passport.js
│   │   └── secrets.js
│   ├── controllers/
│   │   ├── api.js
│   │   ├── contact.js
│   │   ├── exampleWorkspaces.js
│   │   ├── feedback.js
│   │   ├── flood.js
│   │   ├── home.js
│   │   ├── user.js
│   │   └── workspaces.js
│   ├── models/
│   │   ├── Session.js
│   │   ├── User.js
│   │   └── Workspace.js
│   ├── package.json
│   ├── public/
│   │   ├── css/
│   │   │   ├── lib/
│   │   │   │   ├── animate.css
│   │   │   │   ├── bootstrap/
│   │   │   │   │   ├── alerts.less
│   │   │   │   │   ├── badges.less
│   │   │   │   │   ├── bootstrap.less
│   │   │   │   │   ├── breadcrumbs.less
│   │   │   │   │   ├── button-groups.less
│   │   │   │   │   ├── buttons.less
│   │   │   │   │   ├── carousel.less
│   │   │   │   │   ├── close.less
│   │   │   │   │   ├── code.less
│   │   │   │   │   ├── component-animations.less
│   │   │   │   │   ├── dropdowns.less
│   │   │   │   │   ├── forms.less
│   │   │   │   │   ├── glyphicons.less
│   │   │   │   │   ├── grid.less
│   │   │   │   │   ├── input-groups.less
│   │   │   │   │   ├── jumbotron.less
│   │   │   │   │   ├── labels.less
│   │   │   │   │   ├── list-group.less
│   │   │   │   │   ├── media.less
│   │   │   │   │   ├── mixins.less
│   │   │   │   │   ├── modals.less
│   │   │   │   │   ├── navbar.less
│   │   │   │   │   ├── navs.less
│   │   │   │   │   ├── normalize.less
│   │   │   │   │   ├── pager.less
│   │   │   │   │   ├── pagination.less
│   │   │   │   │   ├── panels.less
│   │   │   │   │   ├── popovers.less
│   │   │   │   │   ├── print.less
│   │   │   │   │   ├── progress-bars.less
│   │   │   │   │   ├── responsive-utilities.less
│   │   │   │   │   ├── scaffolding.less
│   │   │   │   │   ├── tables.less
│   │   │   │   │   ├── theme.less
│   │   │   │   │   ├── thumbnails.less
│   │   │   │   │   ├── tooltip.less
│   │   │   │   │   ├── type.less
│   │   │   │   │   ├── utilities.less
│   │   │   │   │   ├── variables.less
│   │   │   │   │   └── wells.less
│   │   │   │   └── bootstrap-social.less
│   │   │   ├── styles.less
│   │   │   └── themes/
│   │   │       ├── default.less
│   │   │       ├── flatly.less
│   │   │       └── ios7.less
│   │   ├── fonts/
│   │   │   └── FontAwesome.otf
│   │   └── js/
│   │       ├── application.js
│   │       └── main.js
│   ├── test/
│   │   ├── app.js
│   │   ├── mocha.opts
│   │   └── models.js
│   └── views/
│       ├── 404.jade
│       ├── account/
│       │   ├── forgot.jade
│       │   ├── login.jade
│       │   ├── profile.jade
│       │   ├── reset.jade
│       │   └── signup.jade
│       ├── api/
│       │   ├── aviary.jade
│       │   ├── clockwork.jade
│       │   ├── facebook.jade
│       │   ├── foursquare.jade
│       │   ├── github.jade
│       │   ├── index.jade
│       │   ├── lastfm.jade
│       │   ├── linkedin.jade
│       │   ├── nyt.jade
│       │   ├── paypal.jade
│       │   ├── scraping.jade
│       │   ├── steam.jade
│       │   ├── tumblr.jade
│       │   ├── twilio.jade
│       │   ├── twitter.jade
│       │   └── venmo.jade
│       ├── contact.jade
│       ├── home.jade
│       ├── layout.jade
│       └── partials/
│           ├── flash.jade
│           ├── footer.jade
│           └── navigation.jade
├── test/
│   ├── index.html
│   ├── lib/
│   │   ├── chai.js
│   │   ├── expect.js
│   │   └── mocha/
│   │       ├── mocha.css
│   │       └── mocha.js
│   └── spec/
│       └── test.js
└── todo.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .bowerrc
================================================
{
    "directory": "app/bower_components"
}


================================================
FILE: .editorconfig
================================================
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org

root = false


[*]

# Change these settings to your own preference
indent_style = space
indent_size = 2

# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .gitattributes
================================================
* text=auto

================================================
FILE: .gitignore
================================================
node_modules
temp
.sass-cache
app/bower_components
.tmp/
dist_desktop
.DS_Store
dist


================================================
FILE: Gruntfile.js
================================================
'use strict';
var lrSnippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;
var mountFolder = function (connect, dir) {
    return connect.static(require('path').resolve(dir));
};

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to match all subfolders:
// 'test/spec/**/*.js'
// templateFramework: 'lodash'

module.exports = function (grunt) {
    // load all grunt tasks
    require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'dist'
    };

    grunt.initConfig({
        yeoman: yeomanConfig,
        clean: {
            dist: ['.tmp', '<%= yeoman.dist %>/*'],
            server: '.tmp'
        },
        // This task uses James Burke's excellent r.js AMD builder to take all
        // modules and concatenate them into a single file.
        requirejs: {
          release: {
            options: {

              mainConfigFile: "app/scripts/config.js",

              include: ["main"],
              insertRequire: ["main"],

              out: "dist/source.min.js",
              optimize: "uglify",

              // Since we bootstrap with nested `require` calls this option allows
              // R.js to find them.
              findNestedDependencies: true,

              // Include a minimal AMD implementation shim.
              name: "almond",

              // Setting the base url to the distribution directory allows the
              // Uglify minification process to correctly map paths for Source
              // Maps.
              baseUrl: "app/scripts",

              // Wrap everything in an IIFE.
              wrap: true,

              // Do not preserve any license comments when working with source
              // maps.  These options are incompatible.
              preserveLicenseComments: true
            }
          },
          customizer: {
            options: {

              mainConfigFile: "app/scripts/config.js",

              include: ["customizer"],
              insertRequire: ["customizer"],

              out: "dist/customizer.min.js",
              optimize: "uglify",
              findNestedDependencies: true,
              name: "almond",
              baseUrl: "app/scripts",
              wrap: true,
              preserveLicenseComments: true
            }
          }
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            },
            jqueryui: {
                files: [{
                    expand: true,
                    flatten: true,
                    cwd: '<%= yeoman.app %>',
                    src: 'bower_components/jquery.ui/themes/base/images/*.png',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        cssmin: {
            dist: {
                files: {
                    '<%= yeoman.dist %>/app.min.css': [
                        '.tmp/styles/{,*/}*.css',
                        '<%= yeoman.app %>/bower_components/jquery.ui/themes/base/*.css',
                        '<%= yeoman.app %>/bower_components/components-font-awesome/css/font-awesome.min.css',
                        '<%= yeoman.app %>/styles/bootstrap.css',
			'<%= yeoman.app %>/bower_components/CodeMirror/lib/codemirror.css',
			'<%= yeoman.app %>/bower_components/CodeMirror/addon/hint/show-hint.css',
			'<%= yeoman.app %>/styles/main.css',
                    ],
                    '<%= yeoman.dist %>/customizer.min.css': [
                        '.tmp/styles/{,*/}*.css',
                        '<%= yeoman.app %>/bower_components/jquery.ui/themes/base/*.css',
                        '<%= yeoman.app %>/bower_components/components-font-awesome/css/font-awesome.min.css',
                        '<%= yeoman.app %>/styles/bootstrap.css',
                        '<%= yeoman.app %>/styles/main.css',
                        '<%= yeoman.app %>/styles/customizer.css'
                    ]
                }
            }
        },
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{ico,txt}',
                        '.htaccess',
                        '*.html',
                        '*.json',
                        'images/{,*/}*.{webp,gif,png}',
                        'bower_components/jquery/jquery.js',
                        'bower_components/components-font-awesome/css/font-awesome.min.css',
                        'bower_components/components-font-awesome/fonts/*.{ttf,eot,svg,woff,otf}',
                        'scripts/lib/flood/*.js'
                    ]
                }]
            },
            fonts: {
                files: [{
                    expand: true,
                    dot: true,
                    flatten: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>/fonts',
                    src: [
                        'bower_components/components-font-awesome/fonts/*.{ttf,eot,svg,woff,otf}'
                    ]
                }]
            }
        },
        bower: {
            all: {
                rjsConfig: '<%= yeoman.app %>/scripts/main.js'
            }
        },
        processhtml: {
          release: {
            files: {
              "dist/app.html": ["app/app.html"],
              "dist/customizer.html": ["app/customizer.html"]
            }
          }
        },
        nodewebkit: {
            options: {
                version: '0.8.1',
                build_dir: './dist_desktop', // Where the build version of my node-webkit app is saved
                mac: true, // We want to build it for mac
                win: true, // We want to build it for win
                linux32: false, // We don't need linux32
                linux64: false // We don't need linux64
            },
            src: ['./dist/**'] // Your node-wekit app
        }
    });

    grunt.registerTask('build', [
        'clean:dist',
        'requirejs',
        'imagemin',
        'cssmin',
        'copy',
        'processhtml'
    ]);

    grunt.registerTask('desktop', [
        'clean:dist',
        'requirejs',
        'imagemin',
        'cssmin',
        'copy',
        'processhtml',
        'nodewebkit'
    ]);

    grunt.registerTask('default', [
        'build'
    ]);
};


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) Peter Boyer 2013

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

================================================
FILE: README.md
================================================
![Image](https://raw.github.com/pboyer/flood/master/extra/screenshot.png) 


## flood

### What is it?

flood is a [dataflow](http://en.wikipedia.org/wiki/Dataflow_programming)-style visual programming language based on Scheme written in JavaScript.  flood runs in a browser and as a standalone application on all platforms via [node-webkit](https://github.com/rogerwang/node-webkit).  

### Features

* Constructive solid geometry - Cube, cylinder, sphere, union, intersect, subtract
* Formula node - evaluate javascript in a node
* Never save your work
* Fast node library search
* Undo/redo across user sessions
* Copy/paste
* Multiple workspaces
* Async evaluation
* Partial function application
* "Always on" continuous execution

flood, like early versions of [Dynamo](http://github.com/ikeough/Dynamo), is based on Scheme and thus has many of the features of that language.  It uses a [lightweight scheme interpreter](http://github.com/pboyer/scheme.js) I wrote called scheme.js.

### Getting started

The flood app is scaffolded with [Yeoman](http://yeoman.io/), uses [Grunt](http://gruntjs.com/) for task management and [Bower](http://bower.io/) for web package management.  If you're not familiar with these tools, you should take a look at the docs and get them installed.  

flood uses [require.js](http://requirejs.org/) to manage dependencies between JavaScript files and [backbone.js](http://backbonejs.org/) to stick it all together. 

flood also has a server written in node.js that handles user authentication and model synchronization. 

#### Installing dependencies for the app

To install all of the dependencies for the flood app, run the following commands in the root directory:

	bower install
	npm install

This will install all of the development dependencies for Grunt and all of the public dependencies with bower.

#### Installing dependencies for the server

To install all of the node.js dependencies for the flood server, run the following commands in the "server" directory:

	npm install

You will also need to install MongoDB and run an instance on port 27017, the default port for MongoDB.  You can get MongoDB [here](http://www.mongodb.org/downloads).


#### Running the server

For development, I recommend using the great nodemon tool:

	npm install -g nodemon

Go to the "server" directory and run:

	nodemon app.js

You can also run the server using:

	node app.js


#### Building for the web (outdated)

The entire app can be compressed into lightweight, minified, and concatenated css, js, and html files using Grunt:

	grunt 


#### Building for the desktop

flood can be used as a standalone application via node-webkit.  Just do this:

	grunt desktop

This will generate binaries for use on Mac and Windows in the dist_desktop folder.


### License

The MIT License (MIT)

Copyright (c) Peter Boyer 2014-2020

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.



================================================
FILE: app/.htaccess
================================================
# Apache configuration file
# httpd.apache.org/docs/2.2/mod/quickreference.html

# Note .htaccess files are an overhead, this logic should be in your Apache
# config if possible: httpd.apache.org/docs/2.2/howto/htaccess.html

# Techniques in here adapted from all over, including:
#   Kroc Camen: camendesign.com/.htaccess
#   perishablepress.com/press/2006/01/10/stupid-htaccess-tricks/
#   Sample .htaccess file of CMS MODx: modxcms.com


# ----------------------------------------------------------------------
# Better website experience for IE users
# ----------------------------------------------------------------------

# Force the latest IE version, in various cases when it may fall back to IE7 mode
#  github.com/rails/rails/commit/123eb25#commitcomment-118920
# Use ChromeFrame if it's installed for a better experience for the poor IE folk

<IfModule mod_headers.c>
  Header set X-UA-Compatible "IE=Edge,chrome=1"
  # mod_headers can't match by content-type, but we don't want to send this header on *everything*...
  <FilesMatch "\.(js|css|gif|png|jpe?g|pdf|xml|oga|ogg|m4a|ogv|mp4|m4v|webm|svg|svgz|eot|ttf|otf|woff|ico|webp|appcache|manifest|htc|crx|oex|xpi|safariextz|vcf)$" >
    Header unset X-UA-Compatible
  </FilesMatch>
</IfModule>


# ----------------------------------------------------------------------
# Cross-domain AJAX requests
# ----------------------------------------------------------------------

# Serve cross-domain Ajax requests, disabled by default.
# enable-cors.org
# code.google.com/p/html5security/wiki/CrossOriginRequestSecurity

#  <IfModule mod_headers.c>
#    Header set Access-Control-Allow-Origin "*"
#  </IfModule>


# ----------------------------------------------------------------------
# CORS-enabled images (@crossorigin)
# ----------------------------------------------------------------------

# Send CORS headers if browsers request them; enabled by default for images.
# developer.mozilla.org/en/CORS_Enabled_Image
# blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
# hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
# wiki.mozilla.org/Security/Reviews/crossoriginAttribute

<IfModule mod_setenvif.c>
  <IfModule mod_headers.c>
    # mod_headers, y u no match by Content-Type?!
    <FilesMatch "\.(gif|png|jpe?g|svg|svgz|ico|webp)$">
      SetEnvIf Origin ":" IS_CORS
      Header set Access-Control-Allow-Origin "*" env=IS_CORS
    </FilesMatch>
  </IfModule>
</IfModule>


# ----------------------------------------------------------------------
# Webfont access
# ----------------------------------------------------------------------

# Allow access from all domains for webfonts.
# Alternatively you could only whitelist your
# subdomains like "subdomain.example.com".

<IfModule mod_headers.c>
  <FilesMatch "\.(ttf|ttc|otf|eot|woff|font.css)$">
    Header set Access-Control-Allow-Origin "*"
  </FilesMatch>
</IfModule>


# ----------------------------------------------------------------------
# Proper MIME type for all files
# ----------------------------------------------------------------------

# JavaScript
#   Normalize to standard type (it's sniffed in IE anyways)
#   tools.ietf.org/html/rfc4329#section-7.2
AddType application/javascript         js jsonp
AddType application/json               json

# Audio
AddType audio/ogg                      oga ogg
AddType audio/mp4                      m4a f4a f4b

# Video
AddType video/ogg                      ogv
AddType video/mp4                      mp4 m4v f4v f4p
AddType video/webm                     webm
AddType video/x-flv                    flv

# SVG
#   Required for svg webfonts on iPad
#   twitter.com/FontSquirrel/status/14855840545
AddType     image/svg+xml              svg svgz
AddEncoding gzip                       svgz

# Webfonts
AddType application/vnd.ms-fontobject  eot
AddType application/x-font-ttf         ttf ttc
AddType font/opentype                  otf
AddType application/x-font-woff        woff

# Assorted types
AddType image/x-icon                        ico
AddType image/webp                          webp
AddType text/cache-manifest                 appcache manifest
AddType text/x-component                    htc
AddType application/xml                     rss atom xml rdf
AddType application/x-chrome-extension      crx
AddType application/x-opera-extension       oex
AddType application/x-xpinstall             xpi
AddType application/octet-stream            safariextz
AddType application/x-web-app-manifest+json webapp
AddType text/x-vcard                        vcf
AddType application/x-shockwave-flash       swf
AddType text/vtt                            vtt


# ----------------------------------------------------------------------
# Allow concatenation from within specific js and css files
# ----------------------------------------------------------------------

# e.g. Inside of script.combined.js you could have
#   <!--#include file="libs/jquery-1.5.0.min.js" -->
#   <!--#include file="plugins/jquery.idletimer.js" -->
# and they would be included into this single file.

# This is not in use in the boilerplate as it stands. You may
# choose to use this technique if you do not have a build process.

#<FilesMatch "\.combined\.js$">
#  Options +Includes
#  AddOutputFilterByType INCLUDES application/javascript application/json
#  SetOutputFilter INCLUDES
#</FilesMatch>

#<FilesMatch "\.combined\.css$">
#  Options +Includes
#  AddOutputFilterByType INCLUDES text/css
#  SetOutputFilter INCLUDES
#</FilesMatch>


# ----------------------------------------------------------------------
# Gzip compression
# ----------------------------------------------------------------------

<IfModule mod_deflate.c>

  # Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
  <IfModule mod_setenvif.c>
    <IfModule mod_headers.c>
      SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
      RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
    </IfModule>
  </IfModule>

  # HTML, TXT, CSS, JavaScript, JSON, XML, HTC:
  <IfModule filter_module>
    FilterDeclare   COMPRESS
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/html
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/css
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/plain
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/x-component
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/javascript
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/json
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xhtml+xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/rss+xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/atom+xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/vnd.ms-fontobject
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $image/svg+xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $image/x-icon
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/x-font-ttf
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $font/opentype
    FilterChain     COMPRESS
    FilterProtocol  COMPRESS  DEFLATE change=yes;byteranges=no
  </IfModule>

  <IfModule !mod_filter.c>
    # Legacy versions of Apache
    AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
    AddOutputFilterByType DEFLATE application/javascript
    AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
    AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml
    AddOutputFilterByType DEFLATE image/x-icon image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype
  </IfModule>

</IfModule>


# ----------------------------------------------------------------------
# Expires headers (for better cache control)
# ----------------------------------------------------------------------

# These are pretty far-future expires headers.
# They assume you control versioning with filename-based cache busting
# Additionally, consider that outdated proxies may miscache
#   www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/

# If you don't use filenames to version, lower the CSS and JS to something like
# "access plus 1 week".

<IfModule mod_expires.c>
  ExpiresActive on

# Perhaps better to whitelist expires rules? Perhaps.
  ExpiresDefault                          "access plus 1 month"

# cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5)
  ExpiresByType text/cache-manifest       "access plus 0 seconds"

# Your document html
  ExpiresByType text/html                 "access plus 0 seconds"

# Data
  ExpiresByType text/xml                  "access plus 0 seconds"
  ExpiresByType application/xml           "access plus 0 seconds"
  ExpiresByType application/json          "access plus 0 seconds"

# Feed
  ExpiresByType application/rss+xml       "access plus 1 hour"
  ExpiresByType application/atom+xml      "access plus 1 hour"

# Favicon (cannot be renamed)
  ExpiresByType image/x-icon              "access plus 1 week"

# Media: images, video, audio
  ExpiresByType image/gif                 "access plus 1 month"
  ExpiresByType image/png                 "access plus 1 month"
  ExpiresByType image/jpeg                "access plus 1 month"
  ExpiresByType video/ogg                 "access plus 1 month"
  ExpiresByType audio/ogg                 "access plus 1 month"
  ExpiresByType video/mp4                 "access plus 1 month"
  ExpiresByType video/webm                "access plus 1 month"

# HTC files  (css3pie)
  ExpiresByType text/x-component          "access plus 1 month"

# Webfonts
  ExpiresByType application/x-font-ttf    "access plus 1 month"
  ExpiresByType font/opentype             "access plus 1 month"
  ExpiresByType application/x-font-woff   "access plus 1 month"
  ExpiresByType image/svg+xml             "access plus 1 month"
  ExpiresByType application/vnd.ms-fontobject "access plus 1 month"

# CSS and JavaScript
  ExpiresByType text/css                  "access plus 1 year"
  ExpiresByType application/javascript    "access plus 1 year"

</IfModule>


# ----------------------------------------------------------------------
# Prevent mobile network providers from modifying your site
# ----------------------------------------------------------------------

# The following header prevents modification of your code over 3G on some
# European providers.
# This is the official 'bypass' suggested by O2 in the UK.

# <IfModule mod_headers.c>
# Header set Cache-Control "no-transform"
# </IfModule>


# ----------------------------------------------------------------------
# ETag removal
# ----------------------------------------------------------------------

# FileETag None is not enough for every server.
<IfModule mod_headers.c>
  Header unset ETag
</IfModule>

# Since we're sending far-future expires, we don't need ETags for
# static content.
#   developer.yahoo.com/performance/rules.html#etags
FileETag None


# ----------------------------------------------------------------------
# Stop screen flicker in IE on CSS rollovers
# ----------------------------------------------------------------------

# The following directives stop screen flicker in IE on CSS rollovers - in
# combination with the "ExpiresByType" rules for images (see above).

# BrowserMatch "MSIE" brokenvary=1
# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
# BrowserMatch "Opera" !brokenvary
# SetEnvIf brokenvary 1 force-no-vary


# ----------------------------------------------------------------------
# Set Keep-Alive Header
# ----------------------------------------------------------------------

# Keep-Alive allows the server to send multiple requests through one
# TCP-connection. Be aware of possible disadvantages of this setting. Turn on
# if you serve a lot of static content.

# <IfModule mod_headers.c>
#   Header set Connection Keep-Alive
# </IfModule>


# ----------------------------------------------------------------------
# Cookie setting from iframes
# ----------------------------------------------------------------------

# Allow cookies to be set from iframes (for IE only)
# If needed, specify a path or regex in the Location directive.

# <IfModule mod_headers.c>
#   Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
# </IfModule>


# ----------------------------------------------------------------------
# Start rewrite engine
# ----------------------------------------------------------------------

# Turning on the rewrite engine is necessary for the following rules and
# features. FollowSymLinks must be enabled for this to work.

# Some cloud hosting services require RewriteBase to be set: goo.gl/HOcPN
# If using the h5bp in a subdirectory, use `RewriteBase /foo` instead where
# 'foo' is your directory.

# If your web host doesn't allow the FollowSymlinks option, you may need to
# comment it out and use `Options +SymLinksOfOwnerMatch`, but be aware of the
# performance impact: http://goo.gl/Mluzd

<IfModule mod_rewrite.c>
  Options +FollowSymlinks
# Options +SymLinksIfOwnerMatch
  Options +FollowSymlinks
  RewriteEngine On
# RewriteBase /
</IfModule>


# ----------------------------------------------------------------------
# Suppress or force the "www." at the beginning of URLs
# ----------------------------------------------------------------------

# The same content should never be available under two different URLs -
# especially not with and without "www." at the beginning, since this can cause
# SEO problems (duplicate content). That's why you should choose one of the
# alternatives and redirect the other one.

# By default option 1 (no "www.") is activated.
# no-www.org/faq.php?q=class_b

# If you'd prefer to use option 2, just comment out all option 1 lines
# and uncomment option 2.

# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!

# ----------------------------------------------------------------------

# Option 1:
# Rewrite "www.example.com -> example.com".

<IfModule mod_rewrite.c>
  RewriteCond %{HTTPS} !=on
  RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
  RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
</IfModule>

# ----------------------------------------------------------------------

# Option 2:
# Rewrite "example.com -> www.example.com".
# Be aware that the following rule might not be a good idea if you use "real"
# subdomains for certain parts of your website.

# <IfModule mod_rewrite.c>
#   RewriteCond %{HTTPS} !=on
#   RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
#   RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# </IfModule>


# ----------------------------------------------------------------------
# Built-in filename-based cache busting
# ----------------------------------------------------------------------

# If you're not using the build script to manage your filename version revving,
# you might want to consider enabling this, which will route requests for
# /css/style.20110203.css to /css/style.css

# To understand why this is important and a better idea than all.css?v1231,
# read: github.com/h5bp/html5-boilerplate/wiki/cachebusting

# <IfModule mod_rewrite.c>
#   RewriteCond %{REQUEST_FILENAME} !-f
#   RewriteCond %{REQUEST_FILENAME} !-d
#   RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
# </IfModule>


# ----------------------------------------------------------------------
# Prevent SSL cert warnings
# ----------------------------------------------------------------------

# Rewrite secure requests properly to prevent SSL cert warnings, e.g. prevent
# https://www.example.com when your cert only allows https://secure.example.com

# <IfModule mod_rewrite.c>
#   RewriteCond %{SERVER_PORT} !^443
#   RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
# </IfModule>


# ----------------------------------------------------------------------
# Prevent 404 errors for non-existing redirected folders
# ----------------------------------------------------------------------

# without -MultiViews, Apache will give a 404 for a rewrite if a folder of the
# same name does not exist.
# webmasterworld.com/apache/3808792.htm

Options -MultiViews


# ----------------------------------------------------------------------
# Custom 404 page
# ----------------------------------------------------------------------

# You can add custom pages to handle 500 or 403 pretty easily, if you like.
# If you are hosting your site in subdirectory, adjust this accordingly
#    e.g. ErrorDocument 404 /subdir/404.html
ErrorDocument 404 /404.html


# ----------------------------------------------------------------------
# UTF-8 encoding
# ----------------------------------------------------------------------

# Use UTF-8 encoding for anything served text/plain or text/html
AddDefaultCharset utf-8

# Force UTF-8 for a number of file formats
AddCharset utf-8 .atom .css .js .json .rss .vtt .xml


# ----------------------------------------------------------------------
# A little more security
# ----------------------------------------------------------------------

# To avoid displaying the exact version number of Apache being used, add the
# following to httpd.conf (it will not work in .htaccess):
# ServerTokens Prod

# "-Indexes" will have Apache block users from browsing folders without a
# default document Usually you should leave this activated, because you
# shouldn't allow everybody to surf through every folder on your server (which
# includes rather private places like CMS system folders).
<IfModule mod_autoindex.c>
  Options -Indexes
</IfModule>

# Block access to "hidden" directories or files whose names begin with a
# period. This includes directories used by version control systems such as
# Subversion or Git.
<IfModule mod_rewrite.c>
  RewriteCond %{SCRIPT_FILENAME} -d [OR]
  RewriteCond %{SCRIPT_FILENAME} -f
  RewriteRule "(^|/)\." - [F]
</IfModule>

# Block access to backup and source files. These files may be left by some
# text/html editors and pose a great security danger, when anyone can access
# them.
<FilesMatch "(\.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)|~)$">
  Order allow,deny
  Deny from all
  Satisfy All
</FilesMatch>

# If your server is not already configured as such, the following directive
# should be uncommented in order to set PHP's register_globals option to OFF.
# This closes a major security hole that is abused by most XSS (cross-site
# scripting) attacks. For more information: http://php.net/register_globals
#
# IF REGISTER_GLOBALS DIRECTIVE CAUSES 500 INTERNAL SERVER ERRORS:
#
# Your server does not allow PHP directives to be set via .htaccess. In that
# case you must make this change in your php.ini file instead. If you are
# using a commercial web host, contact the administrators for assistance in
# doing this. Not all servers allow local php.ini files, and they should
# include all PHP configurations (not just this one), or you will effectively
# reset everything to PHP defaults. Consult www.php.net for more detailed
# information about setting PHP directives.

# php_flag register_globals Off

# Rename session cookie to something else, than PHPSESSID
# php_value session.name sid

# Disable magic quotes (This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.)
# php_flag magic_quotes_gpc Off

# Do not show you are using PHP
# Note: Move this line to php.ini since it won't work in .htaccess
# php_flag expose_php Off

# Level of log detail - log all errors
# php_value error_reporting -1

# Write errors to log file
# php_flag log_errors On

# Do not display errors in browser (production - Off, development - On)
# php_flag display_errors Off

# Do not display startup errors (production - Off, development - On)
# php_flag display_startup_errors Off

# Format errors in plain text
# Note: Leave this setting 'On' for xdebug's var_dump() output
# php_flag html_errors Off

# Show multiple occurrence of error
# php_flag ignore_repeated_errors Off

# Show same errors from different sources
# php_flag ignore_repeated_source Off

# Size limit for error messages
# php_value log_errors_max_len 1024

# Don't precede error with string (doesn't accept empty string, use whitespace if you need)
# php_value error_prepend_string " "

# Don't prepend to error (doesn't accept empty string, use whitespace if you need)
# php_value error_append_string " "

# Increase cookie security
<IfModule php5_module>
  php_value session.cookie_httponly true
</IfModule>


================================================
FILE: app/404.html
================================================
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Page Not Found :(</title>
        <style>
            ::-moz-selection {
                background: #b3d4fc;
                text-shadow: none;
            }

            ::selection {
                background: #b3d4fc;
                text-shadow: none;
            }

            html {
                padding: 30px 10px;
                font-size: 20px;
                line-height: 1.4;
                color: #737373;
                background: #f0f0f0;
                -webkit-text-size-adjust: 100%;
                -ms-text-size-adjust: 100%;
            }

            html,
            input {
                font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
            }

            body {
                max-width: 500px;
                _width: 500px;
                padding: 30px 20px 50px;
                border: 1px solid #b3b3b3;
                border-radius: 4px;
                margin: 0 auto;
                box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
                background: #fcfcfc;
            }

            h1 {
                margin: 0 10px;
                font-size: 50px;
                text-align: center;
            }

            h1 span {
                color: #bbb;
            }

            h3 {
                margin: 1.5em 0 0.5em;
            }

            p {
                margin: 1em 0;
            }

            ul {
                padding: 0 0 0 40px;
                margin: 1em 0;
            }

            .container {
                max-width: 380px;
                _width: 380px;
                margin: 0 auto;
            }

            /* google search */

            #goog-fixurl ul {
                list-style: none;
                padding: 0;
                margin: 0;
            }

            #goog-fixurl form {
                margin: 0;
            }

            #goog-wm-qt,
            #goog-wm-sb {
                border: 1px solid #bbb;
                font-size: 16px;
                line-height: normal;
                vertical-align: top;
                color: #444;
                border-radius: 2px;
            }

            #goog-wm-qt {
                width: 220px;
                height: 20px;
                padding: 5px;
                margin: 5px 10px 0 0;
                box-shadow: inset 0 1px 1px #ccc;
            }

            #goog-wm-sb {
                display: inline-block;
                height: 32px;
                padding: 0 10px;
                margin: 5px 0 0;
                white-space: nowrap;
                cursor: pointer;
                background-color: #f5f5f5;
                background-image: -webkit-linear-gradient(rgba(255,255,255,0), #f1f1f1);
                background-image: -moz-linear-gradient(rgba(255,255,255,0), #f1f1f1);
                background-image: -ms-linear-gradient(rgba(255,255,255,0), #f1f1f1);
                background-image: -o-linear-gradient(rgba(255,255,255,0), #f1f1f1);
                -webkit-appearance: none;
                -moz-appearance: none;
                appearance: none;
                *overflow: visible;
                *display: inline;
                *zoom: 1;
            }

            #goog-wm-sb:hover,
            #goog-wm-sb:focus {
                border-color: #aaa;
                box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
                background-color: #f8f8f8;
            }

            #goog-wm-qt:hover,
            #goog-wm-qt:focus {
                border-color: #105cb6;
                outline: 0;
                color: #222;
            }

            input::-moz-focus-inner {
                padding: 0;
                border: 0;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <h1>Not found <span>:(</span></h1>
            <p>Sorry, but the page you were trying to view does not exist.</p>
            <p>It looks like this was the result of either:</p>
            <ul>
                <li>a mistyped address</li>
                <li>an out-of-date link</li>
            </ul>
            <script>
                var GOOG_FIXURL_LANG = (navigator.language || '').slice(0,2),GOOG_FIXURL_SITE = location.host;
            </script>
            <script src="http://linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script>
        </div>
    </body>
</html>


================================================
FILE: app/app.html
================================================
<!doctype html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title>Editor Beta - Flood</title>
        <meta name="description" content="">
        <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">

        <!-- build:css app.min.css -->
        <link rel="stylesheet" href="bower_components/jquery.ui/themes/base/jquery.ui.all.css">
        <link rel="stylesheet" href="bower_components/components-font-awesome/css/font-awesome.min.css">
        <link rel="stylesheet" href="styles/bootstrap.css">
        <link rel="stylesheet" href="bower_components/CodeMirror/lib/codemirror.css">
	<link rel="stylesheet" href="bower_components/CodeMirror/addon/hint/show-hint.css">
        <link rel="stylesheet" href="styles/main.css">
        <!-- /build -->

        <style>

          body {
            margin: 0;
            padding: 0;
            font-family:Helvetica, Arial, "Lucida Grande", sans-serif;
          }

          #login {
            background: #e3e3e3;
            text-align: center;
            z-index: 120;
            right: 0;
            left: 0;
            top: 0;
            bottom: 0;

            background-image: -ms-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
            background-image: -moz-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
            background-image: -o-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
            background-image: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, #E3E3E3), color-stop(1, #C4C4C4));
            background-image: -webkit-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
            background-image: radial-gradient(circle closest-corner at center, #E3E3E3 0%, #C4C4C4 100%);
          }

          .editor-container {
            letter-spacing: 60px;
            padding-top: 230px;
            width: 450px;
            margin-left: auto;
            margin-right: auto;
            font-size: 16px;
            color: #666;
            text-shadow: 0 1px white;
          }

          .editor-container span {
            margin-left: 45px;
          }

          #title a {
            text-decoration: none;
          }

          .title-box {
            font-family:Helvetica, Arial, "Lucida Grande", sans-serif;
            padding: 0;
            margin: 0;
            font-size: 180px;

            text-shadow: 0 1px white;
            width: 500px;
            left: 50%;
            margin-left: -250px;

            position: absolute;
            overflow: hidden;

            transition: height 5s;
            transition-timing-function: ease-out;
          }


          #title-grey {
            height: 145px;
            z-index: 122;
            color: #272822;
            width: 500px;
          }

          #title-blue {
            position: absolute;
            color: #589cc8;
            width: 500px;
          }

          .form-group {padding: 5px;}

          .form-group label { color: #AAA; }

          .login-container {
            padding-top: 50px;
            width: 300px;
            margin-left: auto;
            margin-right: auto;
          }

          .login-container h5 {
            font-size: 18px;
            color: #666;
            text-shadow: 0 1px white;
          }

          #app-fail {
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            background: white;
            color: #333;
            z-index: 125;
            background: #960018;
          }

          #app-fail-container {
            border-radius: 6px;
            width: 450px;
            margin-left: auto;
            margin-right: auto;
            padding: 20px;
            background: whitesmoke;
            margin-top: 50px;
          }
    
          #app-fail-container i {
            font-size: 150px;
            color: grey;
          }

          #app-fail-container h1 {
            text-align: center;
          }

          #app-fail-container p {
            padding-top: 20px;
            color: grey;
          }

        </style>

    </head>
    
    <body>

        <div id="app" class="row col">

          <div id="top_container" class="row" style="overflow: visible;">
                
            <div id="workspace-tabs"></div>

            <div id="add-workspace-button" class="menu-element" style="float: left;">
              <i class="fa fa-file dim-button"></i>
            </div>

            <div id="add-workspace-select-element">
              <div id="add-workspace-select-container">
                <ul>
                  <li id="add-project-workspace">Project</li>
                  <li id="add-node-workspace">Custom node</li>
                </ul>
              </div>
            </div>

            <div id="more-menu">
              <div id="login-button" class="menu-element">
                <i class="fa fa-sign-out fa-fw"></i>
                Logout
              </div>
              <div id="workspace-browser-button" class="menu-element">
                <i class="fa fa-list-ul fa-fw"></i>Workspaces
              </div>
            </div>
  
          </div> 

          <div id="extra-menu">
            <div id="feedback-button" class="menu-element">
              <i class="fa fa-comment fa-fw"></i>
            </div>
            <div id="help-button" class="menu-element">
              <i class="fa fa-question fa-fw"></i>
            </div>

            <div id="share-button" style="margin-top: 15px" class="menu-element">
              <i class="fa fa-share fa-fw"></i>
            </div>
          </div>

          <div id="workspaces">

            <div id="workspace_hide" class="rightside workspace-search-button-active">
              <span>3D</span> <i class="fa fa-fw fa-cube fa-2x" style="margin-left: 10px"></i>
            </div>
            <div class="workspaces_curtain row"></div>

          </div>

          <div id="login" class="row col scroll-y">

            <div id="title-container">
              <div class="title-box" id="title-grey">flood</div>
              <div class="title-box" id="title-blue">flood</div>
            </div>

            <div class="editor-container"><span>EDITOR</span></div>

            <div class="login-container">
              <h5>Wait for it...</h5>
            </div>
          </div>

          <div id="help" class="col">
            
          </div>

          <div id="feedback" class="modal row col">
            
          </div>

          <div id="share" class="modal row col">
            
          </div>

          <div id="workspace-browser" class="col">
            
          </div>

          <div id="viewer" class="row blur"></div>

          <div id="app-fail" style="display: none">
  
            <div id="app-fail-container">
              <h1><i class="fa fa-fw fa-2x fa-exclamation-triangle"></i></h1>
              <h1>Flood requires WebGL!</h1>
              <p>
                It's also possible that WebGL is disabled in your browser or has failed to load.  You'll need to use a 
                web browser that <a href="http://caniuse.com/#search=webgl">supports WebGL</a> to use Flood.  
              </p>
            </div>

          </div>

        </div>

      </body>

      <script>
        setTimeout(function(){ 
          document.getElementById("title-grey").style.height = "10px";
        }, 10);
      </script>

      <script type="text/template" id="node-template">

        <div class="node-last-value-container">
          <div class="node-last-value"><%- preview %></div>
          <div class="node-failure-message"><%- failureMessage %></div>
        </div>

        <ul class="node-inputs">

          <% _.each(type.inputs, function(ele, index) { %> 
            <li class="node-port node-port-input" data-index=" <%= index %> "> 
              <span class="node-port-name"><%= ele.name %></span>
            </li>
          <% }); %>
        </ul>

        <ul class="node-inputs-extra">
          <% _.each(type.inputs, function(ele, index) { %> 
            <li class="node-port-extra" data-index=" <%= index %> "> 
              
              <div class="dropdown">

                <a data-toggle="dropdown" href="#">&#8942;</a>

                <ul class="dropdown-menu node-dropdown-menu" role="menu" aria-labelledby="dLabel">

                  <% if (ele.typeName) { %>
                    <li class="dropdown-header">Type</li>
                    <span class="node-dropown-menu-item"><%= ele.typeName %></span>
                  <% } %>

                  <li role="presentation" style="color: black">
                    <li class="dropdown-header" style="padding: 0">Use Default</li>
                    
                    <span class="node-dropown-menu-item"> 
                      <input type="checkbox" class="use-default-checkbox" data-index="<%= index %>" <%= !ignoreDefaults[index] ? "checked" : ""%> >
                      <%= ele.defaultVal != undefined ? JSON.stringify( ele.defaultVal ) : "None" %>
                    </span>
                  </li>

                </ul>
              </div>
                    
            </li>
          <% }); %>
        </ul>

        <div class="node-data-container" >
          <span class="name">
            <% if ( !name || name === "DefaultNodeName" || name === "") { %>
              <%= typeName != "CustomNode" ? typeName : type.functionName  %>
            <% } else { %>
              <%= name %>
            <% } %>
          </span>
        </div>

        <ul class="node-outputs" >
          <% _.each(type.outputs, function(ele, index) { %> 
            <li class="node-port node-port-output" data-index=" <%= index %> "> <span class="node-port-name"> <%= ele.name %> </span> </li> 
          <% }); %>
        </ul>

        <div class="dropdown node-settings" style="position:absolute">

          <a data-toggle="dropdown" href="#">
            <span style="color:black; padding: 1px">...</span>
          </a>

          <ul class="dropdown-menu node-dropdown-menu" role="menu" aria-labelledby="dLabel">

            <li class="dropdown-header" style="font-size: 10px; padding: 0" >Visibility</li>

            <li class="toggle-vis node-dropown-menu-item" style="display:block">
              <i class="fa fa-fw fa-eye"></i><span style="padding-left: 5px">Hide geometry</span>
            </li>

            <li class="dropdown-header">Replication</li>

            <li class="node-dropown-menu-item">
              <div class="btn-group btn-group-xs" style="padding: 0px; padding-top: 5px; font-size: 10px">
                <button type="button" class="rep-type btn btn-default"  style="font-size: 10px" data-rep-type="applyLongest">Long</button>
                <button type="button" class="rep-type btn btn-default"  style="font-size: 10px" data-rep-type="applyCartesian">Cross</button>
              </div>
            </li>

            <li class="dropdown-header">Name</li>
            <li class="node-dropown-menu-item">
              <input class="name-input" value="<%= name %>">
            </li>

          </ul>
        </div>

      </script>

      <script type="text/template" id="node-custom-template">

          <i class="fa fa-fw fa-bolt"></i>          
          <span class="name searchfield"><%= type.functionName  %></span>

      </script>

      <script type="text/template" id="node-script-template">
	<div class="script-add-remove-ports">
		<a href="#" class="add-input"> + </a> 
        	<a href="#" class="remove-input" style="padding-left: 10px; padding-right: 10px"> - </a>
	</div>
        <textarea type="text" class="script-text-input" placeholder="Custom formula"><%= extra.script %></textarea>
      </script>

      <script type="text/template" id="node-input-template">

        <div class="node-data-container node-input-container">
          <input type="text" class="text-input" placeholder="Input name" value="<%= extra.script %>">
        </div>

        <ul class="node-outputs" >
          <% _.each(type.outputs, function(ele, index) { %> 
            <li class="node-port node-port-output" data-index=" <%= index %> "> <span class="node-port-name"> <%= ele.name %> </span> </li> 
          <% }); %>
        </ul>

      </script>

      <script type="text/template" id="node-output-template">

        <ul class="node-inputs">
          <% _.each(type.inputs, function(ele, index) { %> 
            <li class="node-port node-port-input" data-index=" <%= index %> "> <span class="node-port-name"> <%= ele.name %> </span>  </li> 
          <% }); %>
        </ul>

        <div class="node-data-container node-output-container">
          <input type="text" class="text-input" placeholder="Output name" value="<%= extra.script %>">
        </div>

      </script>

      <script type="text/template" id="node-watch-template">

        <ul class="node-inputs">
          <% _.each(type.inputs, function(ele, index) { %> 
            <li class="node-port node-port-input" data-index=" <%= index %> "> <span class="node-port-name"> <%= ele.name %> </span>  </li> 
          <% }); %>
        </ul>

        <div class="node-data-container watch-data-container">
          <span class="name searchfield"><pre><%= prettyValue ? prettyValue : "Nothing" %></pre></span>
        </div>

      </script>

      <script type="text/template" id="workspace-tab-template">

        <i class="fa fa-fw fa-pencil dim-button edit-button" style="visibility: hidden; margin-left: 10px"></i>
        <span class="workspace-tab-function <%= isCustomNode ? "" : "hidden" %>">
          <i class="fa fa-bolt"></i>
        </span>
        <input class="workspace-name" disabled="disabled" value="<%= name %>"></input>
        <i class="fa fa-fw fa-times dim-button remove-button"></i>

      </script>

      <script type="text/template" id="node-num-template">

        <div class="node-data-container">

          <% if (name && name != "DefaultNodeName" && name != "") { %>
            <span style="margin-right: 10px" class="name">
              <%= name %>
            </span>
          <% } %>

          <div class="slider" style="display: inline-block; width: 120px; height: 8px; margin: 0; padding: 0"></div>

          <div class="num-data-container" style="display: inline-block;">
            <input type="number" class="currentValue" style="number-node-input" value="<%= type.value %>" />
          </div>

        </div>

        <ul class="node-outputs" >
          <% _.each(type.outputs, function(ele, index) { %> 
            <li class="node-port node-port-output" data-index=" <%= index %> "> <span class="node-port-name"> <%= ele.name %> </span> </li> 
          <% }); %>
        </ul>

         <div class="dropdown keep-open node-settings" style="position:absolute">

          <a href="#" data-toggle="dropdown" href="#">
            <span style="color:black; padding: 1px">...</span>
          </a>

          <ul class="dropdown-menu node-dropdown-menu" role="menu" style="color: black" aria-labelledby="dLabel">

            <li class="dropdown-header">Min</li>
            <li class="node-dropown-menu-item">
              <input type="number" class="num-min">
            </li>

            <li class="dropdown-header">Max</li>
            <li class="node-dropown-menu-item">
              <input type="number" class="num-max">
            </li>

            <li class="dropdown-header">Stepsize</li>
            <li class="node-dropown-menu-item">
              <input type="number" class="num-step">
            </li>

            <li class="dropdown-header">Name</li>
            <li class="node-dropown-menu-item">
              <input class="name-input" value="<%= name %>">
            </li>
            
            <li class="dropdown-header">
              Hide in customizer <input type="checkbox" style="margin-left: 10px" <%= extra.lock ? "checked" : ""%> class="lock-input">
            </li>

          </ul>
        </div>

      </script>

      <script type="text/template" id="search-template">
        <div class="search-top-container">
          <input placeholder="Add a node..." class="library-search-input search"></input>
        </div>
        <ul class="list search-list">
        </ul>
      </script>

      <script type="text/template" id="workspace-browser-template">

        <div id="workspace-browser-contents">

          <span class="workspace-browser-header row" id="workspace-browser-header-projects">
            Projects
            <i class="workspace-browser-header-refresh icon-refresh dim-button workspace-browser-refresh" ></i>
          </span>
          <ul class="workspace-browser-element-list row scroll-y" id="workspace-browser-projects"></ul>

          <span class="workspace-browser-header row" id="workspace-browser-header-custom-nodes">
            Custom Nodes
            <i class="workspace-browser-header-refresh icon-refresh dim-button workspace-browser-refresh" ></i>
          </span>
          <ul class="workspace-browser-element-list row scroll-y" id="workspace-browser-custom-nodes"></ul>

        </div>
      </script>

      <script type="text/template" id="workspace-browser-element-template">

        <div style="display: inline-block;" class="workspace-browser-element-data">
          <%= name %>
          <br>
          <span class="workspace-browser-element-date"><%= prettyDate %></span>
        </div>
        
        <div style="display: inline-block; visibility: hidden" class="dropdown workspace-browser-element-ellipsis" >

          <a data-toggle="dropdown" href="#">&#8230;</a>

          <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">

            <li role="presentation" class="deleteWorkspace">
              <a role="menuitem" tabindex="-1" href="#">
                Delete...
              </a>
            </li>

          </ul>
        </div>

        <div style="float: right; visibility: hidden" class="workspace-browser-element-open">
          Open
        </div>
      </script>

      <script type="text/template" id="workspace-search-template">
        
        <div class="workspace-search-container" id="workspace-search-input-container">

          <ul class="list search-list" style="display:none;">
          </ul>

          <div class="search-bottom-container">

            <i style="display:inline-block;" class="icon-search icon-white dim-button" ></i>
            <input placeholder="Add a node..." id="bottom-search" class="library-search-input search"></input>

          </div>

        </div>

        <div class="workspace-search-container">

          <div id="undo-button" class="workspace-search-button workspace-search-button-active">

            <i class="fa fa-lg fa-fw dim-button fa-rotate-left white-icon"></i>

          </div>

          <div id="redo-button" class="workspace-search-button workspace-search-button-active">

             <i class="fa fa-lg fa-fw  dim-button fa-repeat white-icon"></i>

          </div>

        </div>

        <div class="workspace-search-container">

          <div id="copy-button" class="workspace-search-button workspace-search-button-active">

            <i class="fa fa-lg fa-fw dim-button fa-copy white-icon"></i>

          </div>

          <div id="paste-button" class="workspace-search-button workspace-search-button-active">

            <i class="fa fa-lg fa-fw dim-button fa-paste white-icon"></i>

          </div>

          <div id="delete-button" class="workspace-search-button workspace-search-button-active">

            <i class="fa fa-lg fa-fw dim-button fa-eraser white-icon"></i>

          </div>

        </div>

        <div class="workspace-search-container">

          <div id="zoomin-button" class="workspace-search-button workspace-search-button-active">
              <i class="fa fa-lg fa-fw dim-button fa-plus white-icon"></i>
          </div>

          <div id="zoomout-button" class="workspace-search-button workspace-search-button-active">
            <i class="fa fa-lg fa-fw dim-button fa-minus white-icon"></i>
          </div>

          <div id="zoomreset-button" class="workspace-search-button workspace-search-button-active">
             <i class="fa fa-lg fa-fw dim-button fa-arrows-alt white-icon"></i>
          </div>

        </div>

        <div class="workspace-search-container">

          <div id="export-button" class="workspace-search-button workspace-search-button-active">
            <i class="fa fa-lg fa-fw dim-button fa-download white-icon"></i>
          </div>

        </div>

      </script>

      <script type="text/template" id="workspace-runner-status-template">

      </script>

      <script type="text/template" id="search-element-template">
        <span class="name"><%= name %></span>
      </script>

      <script type="text/template" id="connection-template">
        M<%-aX%>,<%-aY%> C<%-bX%>,<%-bY%> <%-cX%>,<%-cY%> <%-dX%>,<%-dY%>
      </script>

      <script type="text/template" id="login-template">

        <div id="login-failure-message">
        </div>

        <div id="tab-button-container">
          <a id="login-tab-button" href="#" class="btn btn-block btn-flood btn-social"><i class="fa fa-check"></i>Sign in with Flood</a>
        </div>

        <form id="signup-form" style="display:none">

          <input type="hidden" name="_csrf">

          <div class="form-group">
            <label for="email" class="control-label">Email</label>
            <input type="email" name="email" id="email" placeholder="Email" autofocus class="form-control">
          </div>

          <div class="form-group">
            <label for="password" class="control-label">Password</label>
            <input type="password" name="password" id="password" placeholder="Password" class="form-control">
          </div>

          <div class="form-group">
            <label for="confirmPassword" class="control-label">Confirm Password</label>
            <input type="password" name="confirmPassword" id="confirmPassword" placeholder="Confirm Password" class="form-control">
          </div>

          <div class="form-group">
            <button type="submit" class="btn btn-success"><i class="fa fa-fw fa-check"></i>Signup</button>
          </div>

        </form>

        <form id="login-form" style="display:none">

          <input type="hidden" name="_csrf">

          <div class="form-group">
            <label for="email" class="control-label">Email</label>
            <input type="email" name="email" id="login-email" placeholder="Email" autofocus="" class="form-control">
          </div>

          <div class="form-group">
            <label for="password" class="control-label">Password</label>
            <input type="password" name="password" id="login-password" placeholder="Password" class="form-control">
          </div>

          <div class="form-group">
            <button type="submit" id="submit-login" class="btn btn-primary"><i class="fa fa-fw fa-unlock-alt"></i>Login</button>
          </div>

        </form> 

        <div id="signup-tab-button" class="tab-button"><a href="#">New account...</a></div>

        <a href="/auth/google" class="btn btn-block btn-google-plus btn-social"><i class="fa fa-google-plus"></i>Sign in with Google</a>
        <a href="/auth/facebook" class="btn btn-block btn-facebook btn-social"><i class="fa fa-facebook"></i>Sign in with Facebook</a>

      </script>

      <script type="text/template" id="share-template">

        <div id="share-box" class="modal-box">

          <i id="exit-share" class="fa fa-fw fa-times dim-button"></i>
          <h3 id="feedback-title">
            <i class="fa fa-share fa-2x fa-fw" style="padding-right: 10px"></i>Share
          </h3>
          <p>Share your project as a customizable product website</p>

          <div class="inner-box">
           <span class="label">Sharing</span> 
           <br>
            <i class="fa fa-fw fa-toggle-off dim-button is-customizer-checkbox"></i> 
            <span class="indicator public">public</span> <span class="indicator private">private</span>
          </div>

          <div class="inner-box">
            <span class="label">Stable URL</span> <a href="http://<%= url %>" target="_blank" style="font-style: italic; ">Visit</a>
            <br>
            <input type="text" readonly class="customizer-url" placeholder="Project Url" value="<%= url %>">
          </div>

        </div>

      </script>

      <script type="text/template" id="feedback-template">

        <div id="feedback-box" class="modal-box">
          <i id="exit-feedback" class="fa fa-fw fa-times dim-button"></i>
          <h3 id="feedback-title">
            <i class="fa fa-comment fa-2x fa-fw" style="padding-right: 10px"></i>Send Feedback
          </h3>
          <p>Feel free to send comments, suggestions, or issues.</p>

          <div id="feedback-failure-message"></div>
          <div id="feedback-sending-message">Sending...</div>
          <div id="feedback-success-message">Sent!</div>

          <form id="feedback-form">

            <input type="hidden" name="_csrf">

            <div class="form-group">
              <label for="subject" class="control-label">Subject</label>
              <input type="text" name="subject" id="feedback-subject" placeholder="Subject" autofocus="" class="form-control">
            </div>

            <div class="form-group">
              <label for="message" class="control-label">Message</label>
              <textarea name="message" id="feedback-message" placeholder="Message" autofocus="" class="form-control"></textarea>
            </div>

            <div class="form-group">
              <button type="submit" id="submit-feedback" class="btn btn-primary">Submit</button>
            </div>

          </form>
        </div>
        </div>

      </script>

      <script type="text/template" id="help-section-template">

        <div class="enter-help-section" data-target-id="<%= targetId %>" style="left: <%= elementPosition[0] %>px; top: <%= elementPosition[1] %>px;">
          ?
        </div>
        <div class="help-section" data-target-id="<%= targetId %>" style="left: <%= elementPosition[0] + offset[0] %>px; top: <%= elementPosition[1] + offset[1]  %>px;">
          <span><%= title %> </span>
          <p><%= text %></p>
          <div class="exit-help-section" data-target-id="<%= targetId %>">Ok, got it!</div>
          <div class="exit-help" data-target-id="<%= targetId %>">Exit all</div>
        </div>

      </script>


      <script>

        function checkWebGL() { 
           try{
            var canvas = document.createElement( 'canvas' ); 
            return !! window.WebGLRenderingContext && ( 
                 canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) );
           }catch( e ) { return false; } 
         };

        if (!checkWebGL()) {
          var appfail = document.getElementById("app-fail");
          appfail.style.display = '';
        }

      </script>

        <!--[if lt IE 7]>
            <p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a> to improve your experience.</p>
        <![endif]-->

    <!-- Application source. -->
    <!-- build:[src] source.min.js -->
    <script data-main="/scripts/main" src="/bower_components/requirejs/require.js"></script>
    <!-- /build -->

    <script>

      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

      ga('create', 'UA-54947421-1', 'auto');
      ga('send', 'pageview');

    </script>

</body>
</html>


================================================
FILE: app/customizer.html
================================================
<!doctype html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title>Customizer - Flood</title>
        <meta name="description" content="">
        <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">

        <!-- build:css customizer.min.css -->
        <link rel="stylesheet" href="bower_components/jquery.ui/themes/base/jquery.ui.all.css">
        <link rel="stylesheet" href="bower_components/components-font-awesome/css/font-awesome.min.css">
        <link rel="stylesheet" href="styles/customizer.css">
        <link rel="stylesheet" href="styles/bootstrap.css">
        <link rel="stylesheet" href="styles/main.css">
        <!-- /build -->

        <style>

          body {
            margin: 0;
            padding: 0;
            font-family:Helvetica, Arial, "Lucida Grande", sans-serif;
          }

          #login {
            background: #e3e3e3;
            text-align: center;
            z-index: 120;
            right: 0;
            left: 0;
            top: 0;
            bottom: 0;

            background-image: -ms-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
            background-image: -moz-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
            background-image: -o-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
            background-image: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, #E3E3E3), color-stop(1, #C4C4C4));
            background-image: -webkit-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
            background-image: radial-gradient(circle closest-corner at center, #E3E3E3 0%, #C4C4C4 100%);
          }

          .editor-container {
            letter-spacing: 60px;
            padding-top: 230px;
            width: 450px;
            margin-left: auto;
            margin-right: auto;
            font-size: 16px;
            color: #666;
            text-shadow: 0 1px white;
          }

          .editor-container span {
            margin-left: 45px;
          }

          #title a {
            text-decoration: none;
          }

          .title-box {
            font-family:Helvetica, Arial, "Lucida Grande", sans-serif;
            padding: 0;
            margin: 0;
            font-size: 180px;

            text-shadow: 0 1px white;
            width: 500px;
            left: 50%;
            margin-left: -250px;

            position: absolute;
            overflow: hidden;

            transition: height 5s;
            transition-timing-function: ease-out;
          }


          #title-grey {
            height: 145px;
            z-index: 122;
            color: #272822;
            width: 500px;
          }

          #title-blue {
            position: absolute;
            color: #589cc8;
            width: 500px;
          }

          .form-group {padding: 5px;}

          .form-group label { color: #AAA; }

          .login-container {
            padding-top: 50px;
            width: 300px;
            margin-left: auto;
            margin-right: auto;
          }

          .login-container h5 {
            font-size: 18px;
            color: #666;
            text-shadow: 0 1px white;
          }

          #app-fail {
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            background: white;
            color: #333;
            z-index: 125;
            background: #960018;
          }

          #app-fail-container {
            border-radius: 6px;
            width: 450px;
            margin-left: auto;
            margin-right: auto;
            padding: 20px;
            background: whitesmoke;
            margin-top: 50px;
          }
    
          #app-fail-container i {
            font-size: 150px;
            color: grey;
          }

          #app-fail-container h1 {
            text-align: center;
          }

          #app-fail-container p {
            padding-top: 20px;
            color: grey;
          }

        </style>

    </head>
    
    <body>

        <div id="customizer-app">

          <div id="customizer-header">
          </div>

          <div id="customizer-workspace">
            <div id="customizer-workspace-header">
              <div id="hide-workspace" class="dim-button"><i class="fa fa-fw fa-arrow-circle-left"></i></div>
            </div>
          </div>

          <div id="customizer-viewer"></div>

          <div id="powered-by">Powered by <a href="http://www.floodeditor.com">FLOOD</a></div>

          <div id="customizer-login" class="row col scroll-y" style="display: none">

            <div id="title-container">
              <div class="title-box" id="title-grey">flood</div>
              <div class="title-box" id="title-blue">flood</div>
            </div>

            <div class="editor-container"><span>EDITOR</span></div>

            <div class="customizer-login-container">
              <h5>Wait for it...</h5>
            </div>
          </div>

          <div id="app-fail" style="display: none">
  
            <div id="app-fail-container">
              <h1><i class="fa fa-fw fa-2x fa-exclamation-triangle"></i></h1>
              <h1>Requires WebGL!</h1>
              <p>
                It's also possible that WebGL is disabled in your browser or has failed to load.  You'll need to use a 
                web browser that <a href="http://caniuse.com/#search=webgl">supports WebGL</a> to use this app.  
              </p>
            </div>

          </div>

        </div>

      </body>

      <script>
        setTimeout(function(){ 
          document.getElementById("title-grey").style.height = "10px";
        }, 10);
      </script>

      <script type="text/template" id="widget-template">
        
        <span class="name">
          <% if ( !name || name === "DefaultNodeName" || name === "") { %>
            <%= typeName != "CustomNode" ? typeName : type.functionName  %>
          <% } else { %>
            <%= name %>
          <% } %>
        </span>

      </script>

      <script type="text/template" id="widget-number-template">

        <span class="name">
          <% if ( !name || name === "DefaultNodeName" || name === "") { %>
            <%= typeName != "CustomNode" ? typeName : type.functionName  %>
          <% } else { %>
            <%= name %>
          <% } %>
        </span>

        <div class="slider" style="display: inline-block; width: 140px; height: 8px; margin: 0; padding: 0"></div>

        <div class="num-data-container" style="display: inline-block;">
          <input type="number" class="currentValue" style="number-node-input" value="<%= type.value %>" />
        </div>

      </script>

      <script type="text/template" id="header-template">

        <div class="customize-header-container">
          <span class="customize-header" id="customize-header-tag">Custom</span>
          <span class="customize-header" id="customize-header-title"><%= name %></span>
        </div>

        <div id="customizer-header-controls" class="dropdown">

          <% if (!isCustomizer) { %> 
            <span class="indicator private">private</span>
          <% } %>

          <i class="fa fa-download fa-fw stl-download dim-button"></i>

        </div>

        <!--

        <div id="customizer-header-controls" class="dropdown">

          <a href="#" data-toggle="dropdown" href="#">
            <i class="fa fa-download fa-fw"></i>
          </a>

          <ul class="dropdown-menu" role="menu" style="color: black" aria-labelledby="dLabel">

            <span class="dropdown-header">Min</span>
            <input type="number" class="num-min">

            <span class="dropdown-header">Max</span>
            <input type="number" class="num-max">

          </ul>
        </div>

        !-->

      </script>

      <script>

        function checkWebGL() { 
           try{
            var canvas = document.createElement( 'canvas' ); 
            return !! window.WebGLRenderingContext && ( 
                 canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) );
           }catch( e ) { return false; } 
         };

        if (!checkWebGL()) {
          var appfail = document.getElementById("app-fail");
          appfail.style.display = '';
        }

      </script>

        <!--[if lt IE 7]>
            <p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a> to improve your experience.</p>
        <![endif]-->

    <!-- Application source. -->
    <!-- build:[src] customizer.min.js -->
    <script data-main="/scripts/customizer" src="/bower_components/requirejs/require.js"></script>
    <!-- /build -->

    <script>

      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

      ga('create', 'UA-54947421-1', 'auto');
      ga('send', 'pageview');

    </script>

</body>
</html>


================================================
FILE: app/index.html
================================================
<!doctype html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
      <title>Welcome - Flood</title>
      <meta name="description" content="">
      <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">

      <link rel="stylesheet" href="bower_components/components-font-awesome/css/font-awesome.min.css">
      <script src="bower_components/jquery/jquery.js"></script>

      <style>

        body {
          margin: 0;
          padding: 0;
          font-family:Helvetica, Arial, "Lucida Grande", sans-serif;
          background: #272822;
        }

        .row, .col { overflow: hidden; position: absolute; }
        .row { left: 0; right: 0; }
        .col { top: 0; bottom: 0; }
        .scroll-x { overflow-x: auto; }
        .scroll-y { overflow-y: auto; }

        #title {
          font-family:Helvetica, Arial, "Lucida Grande", sans-serif;
          background: #e3e3e3;
          text-align: center;
          padding: 40px;
          right: 0;
          left: 0;
          top: 0;
          height: 400px;

          background-image: -ms-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
          background-image: -moz-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
          background-image: -o-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
          background-image: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, #E3E3E3), color-stop(1, #C4C4C4));
          background-image: -webkit-radial-gradient(center, circle closest-corner, #E3E3E3 0%, #C4C4C4 100%);
          background-image: radial-gradient(circle closest-corner at center, #E3E3E3 0%, #C4C4C4 100%);
        }

        #title a {
          text-decoration: none;
        }

        .title-box { 
          padding: 0;
          margin: 0;
          font-size: 180px;
          text-shadow: 0 1px white;
          left: 50%;
          margin-left: -250px;

          position: absolute;
          overflow: hidden;

          transition: height 15s;
          transition-timing-function: ease-out;
        }

        #title-grey {
          height: 145px;
          z-index: 122;
          color: #272822;
          width: 500px;
        }

        #title-blue {
          position: absolute;
          color: #589cc8;
          width: 500px;
        }

        .editor-container {
          letter-spacing: 60px;
          padding-top: 200px;
          width: 450px;
          margin-left: auto;
          margin-right: auto;
          font-size: 16px;
          color: #666;
          text-shadow: 0 1px white;
        }

        .editor-container span {
          margin-left: 45px;
        }

        #tagline {
          background: #272822;
          padding: 30px;
          font-size: 24px;
          text-align: center;
          right: 0;
          left: 0;
          top: 400px;
          height: 100px;
          color: #DDD;
        }

        .link-button {
          display: inline-block;
          padding: 20px;
          color: grey;
          background: whitesmoke;
          font-size: 20px;
          border-radius: 3px;
          box-shadow: 1px 3px 5px #999;
        }

        #tryit {
          width: 150px;
        }

        #github {
          font-size: 17px;
          background: none;
          color: #BBB;
          box-shadow: none;
        }

        #github:hover {
          color: black;
        }

        .link-button:hover {
          color: #111;
          background: white;
        }

        #links-container {
          margin-top: 60px;
          width: 400px;
          font-size: 20px;
          margin-left: auto;
          margin-right: auto;
        }

        #links-container span {
          color: #777;
          margin: 18px;
          display: inline-block;
          font-size: 14px;
        }

        .example {
          position: relative;
          width: 700px;
          margin-top: 20px;
          margin-bottom: 20px;
          margin-left: auto;
          margin-right: auto;
          box-shadow: 0px 0px 20px #111;
        }

        .hidden {
          display: none;
        }

        #example-row {
          font-weight: 300;
          background: #272822;
          padding-top: 35px;
          padding-bottom: 25px;
          top: 500px;
          bottom: 60px;
        }

        #example-nav {
          text-align: center;
          width: 700px;
          margin-left: auto;
          margin-right: auto;
        }

        .example-nav-button {
          display: inline-block;
          color: white;
          padding: 25px;
          font-size: 20px;
          opacity: 0.3;
          font-size: 20px;
          font-weight: 300;
          text-shadow: 0px 2px 2px #111;
        }

        .example-nav-button:hover {
          opacity: 0.9;
        }

        .selected {
          opacity:1.0;
        }

        .example-text {
          background: black;
          z-index: 1;
          color: white;
          position: absolute;
          top: 0;
          right: 0;
          bottom: 0;
          width: 300px;
          color: #DDD;
          background: #555;
          font-size: 14px;
        }

        .example-text-internal {
          padding: 25px;
        }

        .example-image {
          z-index: 0;;
        }

        .example-text h3 {
          font-size: 26px;
          font-weight: 300;
        }
        
        #footer {
          background: #e3e3e3;
          color: #333;
          text-align: right;
          height: 50px;
          left: 0;
          right: 0;
          bottom: 0;
          font-size: 14px;
          position: absolute;
        }
  
        #footer p {
          display: inline-block;
          padding-left: 15px;
          color: grey;
          padding: 7px;
        }

        #container {
          top: 0;
          left: 0;
          right: 0;
          bottom: 50px;
        }

        #footer a {
          color: #333;
          text-decoration: none;
        }

        #footer a:hover {
          color: black;
          text-decoration: underline;
        }

      </style>

    </head>
    
    <body>
    
      <div id="container" class="row scroll-y">
  
        <div id="title">
            
          <div id="title-container">
            <div class="title-box" id="title-grey">flood</div>
            <div class="title-box" id="title-blue">flood</div>
          </div>

          <div class="editor-container"><span>EDITOR</span></div>

          <div id="links-container">
            <a href="https://www.floodeditor.com/app.html">
              <div class="link-button" id="tryit">Try the beta!</div>
            </a>
            <br>
            <span>Firefox, Safari, or Chrome with WebGL recommended</span>
          </div>

        </div>

        <div id="example-row">

          <div id="example-nav">
            <div class="example-nav-button selected" id="example-nav-button-3d">3D Modeling & Printing</div>
            <div class="example-nav-button" id="example-nav-button-programming">Visual Programming</div>
            <div class="example-nav-button" id="example-nav-button-cloudbased">Cloud-Based</div>
          </div>

          <div id="example-3d" class="example">

            <div class="example-text">
              <div class="example-text-internal">
                <h3>Parametric solid modeling for 3D printing</h3>
                <p>Flood beta is a parametric solid modeler in your browser.  It provides:</p>
                <ul>
                  <li>An intuitive, familiar modeling process</li>
                  <li>Watertight meshes suitable for 3D printing</li>
                  <li>STL export (accepted by Shapeways)</li>
                </ul>
                <p>Further, because Flood is a fully-fledged programming language, you can create your own solid types
                  and combine them however you want.
                </p>
              </div>
            </div>

            <div class="example-image">
              <img src="images/thumb.csg.png">
            </div>

          </div>

          <div id="example-programming" class="example hidden">
            <div class="example-image">
              <img src="images/thumb.programming.png">
            </div>
            <div class="example-text">
              <div class="example-text-internal">
                <h3>Visual programming powered by HTML5</h3>
                <p>Flood is a fully-fledged, open-source visual programming language.  It includes:</p>
                <ul>
                  <li>Custom nodes - Create your own nodes for reuse</li>
                  <li>Write JavaScript inline in your nodes</li>
                  <li>Functional programming - Map, filter, reduce</li>
                </ul>
                <p>All execution is performed in a background thread in your browser, so Flood is responsive and safe.
              </div>
            </div>
          </div>

          <div id="example-cloudbased" class="example hidden">
            <div class="example-image">
              <img src="images/cloudbased.png">
            </div>
            <div class="example-text">
              <div class="example-text-internal">
                <h3>No need to save</h3>
                <p>
                  Like most familiar cloud-based applications, you never have to save in Flood.  Your work is
                  automatically synced, and everytime you open the application you come right back 
                  to the state you were in before.  
                </p>

                <ul>
                  <li>Continuous autosave</li>
                  <li>Undo-redo</li>
                  <li>Come back to your previous session with zero effort</li>
                </ul>
                <p>
                  So feel free to come back to your work wherever 
                  and whenever you want!
                </p>
              </div>
            </div>
          </div>

        </div>

      </div>

      <div id="footer" class="row">
        <p style="">
          © 2014 Peter Boyer. Code licensed under the <a href="http://footer.org/licenses/MIT">MIT License.</a>
        </p>
        <p>
          <a href="http://www.github.com/pboyer/flood"><i class="fa fa-github-square"></i> Check it out on Github</a>
        </p>
        <p>
          <a href="http://www.twitter.com/ptrbyr"><i class="fa fa-twitter"></i> Follow me on Twitter</a>
        </p>
      </div>
      
    </body>

    <script>
      // start the title animation
      setTimeout(function(){ 
        document.getElementById("title-grey").style.height = "10px";
      }, 10);

      $('.example-nav-button').click(function(x){
        
        $('.example-nav-button').removeClass('selected');
        $("#" + x.target.id).addClass('selected');

        $('.example').addClass('hidden');
        $('#example-' + x.target.id.substring(19)).removeClass('hidden');
        
      });

    </script>

    <!--[if lt IE 7]>
        <p class="chromeframe">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a> to improve your experience.</p>
    <![endif]-->

    <script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

      ga('create', 'UA-54947421-1', 'auto');
      ga('send', 'pageview');

    </script>

</body>
</html>


================================================
FILE: app/package.json
================================================
{
  "name": "flood",
  "version": "0.0.4",
  "description": "A visual programming language for JavaScript, based on Scheme",
  "main": "index.html",
  "window": {
    "toolbar": false,
    "frame": true,
    "width": 1280,
    "height": 960
  }
}


================================================
FILE: app/robots.txt
================================================
# robotstxt.org

User-agent: *


================================================
FILE: app/scripts/collections/Connections.js
================================================
define(['backbone', 'Connection'], function(Backbone, Connection) {

	return Backbone.Collection.extend({

		model: Connection

	});

});

================================================
FILE: app/scripts/collections/Nodes.js
================================================
define(['backbone', 'Node'], function(Backbone, Node) {

	return Backbone.Collection.extend({

		model: Node,

		initialDragPositions: [],

		toJSON: function(){

			return this.models.map(function(x){
				return x.serialize();
			});

		},

		selectAll: function() {
			this.where({selected: false}).forEach( function(e){ e.set({selected: true}) } );
		},
		
		deselectAll: function() {
			this.where({selected: true}).forEach( function(e){ 
					e.set('selected', false);
				});
		},

		moveSelected: function(offset, masterEle) {
			var that = this;
			var count = 0;
			this.where({selected: true}).forEach( function(e, i){ 
					if (e === masterEle) return;
					var initialPos = that.initialDragPositions[count++];
					e.set('position', [initialPos[0] + offset[0], initialPos[1] + offset[1]]);
				});
		},

		startDragging: function(masterEle){
			this.initialDragPositions = [];
			var that = this;
			this.where({selected: true}).forEach( function(e){ 
					if (e === masterEle) return;
					that.initialDragPositions.push(e.get('position'));
				});
		}

	});

});



================================================
FILE: app/scripts/collections/SearchElements.js
================================================
define(['backbone', 'SearchElement', 'FLOOD'], function(Backbone, SearchElement, FLOOD) {

	return Backbone.Collection.extend({

		model: SearchElement,

		initialize: function(atts) {
			this.app = atts.app;
		},

		addCustomNode: function(customNode){

			var match = this.where({ functionId: customNode.functionId });
			if (match) this.remove( match );

			this.add(new SearchElement({name: customNode.functionName, functionName: customNode.functionName, 
				isCustomNode: true, functionId: customNode.functionId, app: this.app, numInputs: customNode.inputs.length, 
				numOutputs: customNode.outputs.length }));

		},

		fetch: function() {

			this.models.length = 0;

			for (var key in FLOOD.nodeTypes){
				this.models.push( new SearchElement({name: key, app: this.app}) );	
			}

		}

	});
});



================================================
FILE: app/scripts/collections/WorkspaceBrowserElements.js
================================================
define(['backbone', 'WorkspaceBrowserElement'], function(Backbone, WorkspaceBrowserElement) {

	return Backbone.Collection.extend({

    url: '/ws',
		model: WorkspaceBrowserElement

	});

});

================================================
FILE: app/scripts/collections/Workspaces.js
================================================
define(['backbone', 'Workspace'], function(Backbone, Workspace) {

	return Backbone.Collection.extend({

	  model: Workspace

	});

});


================================================
FILE: app/scripts/config.js
================================================
/*global require*/
'use strict';

require.config({
    shim: {
        underscore: {
            exports: '_'
        },
        backbone: {
            deps: [
                'underscore',
                'jquery'
            ],
            exports: 'Backbone'
        },
        List: {
            deps: [
                'jquery'
            ],
            exports: 'List'
        },
        Three: {
            exports: 'Three'
        },
        CSG: {
            exports: 'CSG'
        },
        FLOODCSG: {
            deps: ['CSG'],
            exports: 'FLOODCSG'
        },
        Viewport: {
            deps: [
                'Three',
                'OrbitControls'
            ],
            exports: 'Viewport'
        },
        OrbitControls: {
            deps: [
                'Three'
            ],
            exports: 'OrbitControls'
        },
        jqueryuicore: {
            deps: [
                'jquery'
            ],
            exports: 'jqueryuicore'
        },
        jqueryuiwidget: {
            deps: [
                'jquery'
            ],
            exports: 'jqueryuiwidget'
        },
        jqueryuimouse: {
            deps: [
                'jquery',
                'jqueryuiwidget'
            ],
            exports: 'jqueryuimouse'
        },
        jqueryuitouchpunch: {
            deps: [
                'jquery',
                'jqueryuicore',
                'jqueryuimouse'
            ],
            exports: 'jqueryuitouchpunch'
        },
        jqueryuislider: {
            deps: [
                'jquery',
                'jqueryuitouchpunch',
                'jqueryuimouse',
                'jqueryuicore',
                'jqueryuiwidget'
            ],
            exports: 'jqueryuislider'
        },
        jqueryuidraggable: {
            deps: [
                'jquery',
                'jqueryuitouchpunch',
                'jqueryuimouse',
                'jqueryuicore',
                'jqueryuiwidget'
            ],
            exports: 'jqueryuidraggable'
        },
        bootstrap: {
            deps: [
                'jquery'
            ],
            exports: 'bootstrap'
        },
        almond: {
            deps: [
            ],
            exports: 'almond'
        }
   },
    packages: [{
        name: "codemirror",
        location: "../bower_components/CodeMirror/",
        main: "lib/codemirror"
    }],
    paths: {

        // backbone collections
        Connections: 'collections/Connections',
        SearchElements: 'collections/SearchElements',
        Nodes: 'collections/Nodes',
        Workspaces: 'collections/Workspaces',
        WorkspaceBrowserElements: 'collections/WorkspaceBrowserElements',

        // backbone models

            // customizer
            CustomizerApp: 'models/customizer/CustomizerApp',

            // editor
            App: 'models/App',
            Connection: 'models/Connection',
            Marquee: 'models/Marquee',
            Node: 'models/Node',
            Search: 'models/Search',
            SearchElement: 'models/SearchElement',
            Workspace: 'models/Workspace',
            Runner: 'models/Runner',
            Help: 'models/Help',
            Feedback: 'models/Feedback',
            Share: 'models/Share',
            Login: 'models/Login',
            GeometryExport: 'models/GeometryExport',
            WorkspaceBrowserElement: 'models/WorkspaceBrowserElement',
            WorkspaceBrowser: 'models/WorkspaceBrowser',
            WorkspaceResolver: 'models/WorkspaceResolver',

        // backbone views

            // customizer
            BaseWidgetView: 'views/customizer/widgets/Base',
            GeometryWidgetView: 'views/customizer/widgets/Geometry',
            NumberWidgetView: 'views/customizer/widgets/Number',

            CustomizerAppView: 'views/customizer/CustomizerAppView',
            CustomizerHeaderView: 'views/customizer/CustomizerHeaderView',
            CustomizerViewerView: 'views/customizer/CustomizerViewerView',
            CustomizerWorkspaceView: 'views/customizer/CustomizerWorkspaceView',

            // editor
            AppView: 'views/AppView',
            ConnectionView: 'views/ConnectionView',
            MarqueeView: 'views/MarqueeView',
            SearchView: 'views/SearchView',
            WorkspaceControlsView: 'views/WorkspaceControlsView',
            SearchElementView: 'views/SearchElementView',
            WorkspaceView: 'views/WorkspaceView',
            WorkspaceTabView: 'views/WorkspaceTabView',
            HelpView: 'views/HelpView',
            FeedbackView: 'views/FeedbackView',
            ShareView: 'views/ShareView',
            LoginView: 'views/LoginView',
            WorkspaceBrowserElementView: 'views/WorkspaceBrowserElementView',
            WorkspaceBrowserView: 'views/WorkspaceBrowserView',

        // node backbone views
        NodeViewTypes: 'views/nodeviews/NodeViews',
        BaseNodeView: 'views/nodeviews/Base',
        WatchNodeView: 'views/nodeviews/Watch',
        NumNodeView: 'views/nodeviews/Num',
        ScriptView: 'views/nodeviews/Script',
        InputView: 'views/nodeviews/Input',
        OutputView: 'views/nodeviews/Output',
        CustomNodeView: 'views/nodeviews/CustomNode',
        ThreeCSGNodeView: 'views/nodeviews/ThreeCSG',

        OrbitControls: 'lib/OrbitControls',
        Viewport: 'lib/Viewport',
        FLOODCSG: 'lib/flood/flood_csg',
        FLOOD: 'lib/flood/flood',
        CSG: 'lib/flood/csg',
        scheme: 'lib/flood/scheme',

        // bower

        Hammer: '../bower_components/hammerjs/hammer',
        almond: '../bower_components/almond/almond',
        bootstrap: '../bower_components/bootstrap/dist/js/bootstrap',
        List: '../bower_components/listjs/dist/list.min',
        Three: '../bower_components/threejs/build/three.min',
        jqueryuitouchpunch: '../bower_components/jqueryui-touch-punch/jquery.ui.touch-punch',
        jqueryuislider: '../bower_components/jquery.ui/ui/jquery.ui.slider',
        jqueryuidraggable: '../bower_components/jquery.ui/ui/jquery.ui.draggable',
        jqueryuicore: '../bower_components/jquery.ui/ui/jquery.ui.core',
        jqueryuimouse: '../bower_components/jquery.ui/ui/jquery.ui.mouse',
        jqueryuiwidget: '../bower_components/jquery.ui/ui/jquery.ui.widget',
        jquery: '../bower_components/jquery/jquery.min',
        backbone: '../bower_components/backbone-amd/backbone-min',
        underscore: '../bower_components/underscore-amd/underscore-min',
        fastclick: '../bower_components/fastclick/lib/fastclick',
        FileSaver: '../bower_components/FileSaver/FileSaver'
    }

});





================================================
FILE: app/scripts/customizer.js
================================================
require(["config"], function() {

  require(['backbone', 'CustomizerApp', 'CustomizerAppView', 'Three', 'FLOODCSG', 'bootstrap' ], function (Backbone, CustomizerApp, CustomizerAppView, THREE) {

    var app = new CustomizerApp();

    app.fetch({
      error: function(result) {
        console.error('error');
        console.error(result);
      },
      success: function(){
      }
    });

    var appView = new CustomizerAppView({model: app});

  });

});



================================================
FILE: app/scripts/lib/OrbitControls.js
================================================
/**
 * @author qiao / https://github.com/qiao
 * @author mrdoob / http://mrdoob.com
 * @author alteredq / http://alteredqualia.com/
 * @author WestLangley / http://github.com/WestLangley
 * @author erich666 / http://erichaines.com
 */
/*global THREE, console */

// This set of controls performs orbiting, dollying (zooming), and panning. It maintains
// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
// supported.
//
//    Orbit - left mouse / touch: one finger move
//    Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
//    Pan - right mouse, or arrow keys / touch: three finter swipe
//
// This is a drop-in replacement for (most) TrackballControls used in examples.
// That is, include this js file and wherever you see:
//    	controls = new THREE.TrackballControls( camera );
//      controls.target.z = 150;
// Simple substitute "OrbitControls" and the control should work as-is.

THREE.OrbitControls = function ( object, domElement ) {

	this.object = object;
	this.domElement = ( domElement !== undefined ) ? domElement : document;

	// API

	// Set to false to disable this control
	this.enabled = true;

	// "target" sets the location of focus, where the control orbits around
	// and where it pans with respect to.
	this.target = new THREE.Vector3();

	// center is old, deprecated; use "target" instead
	this.center = this.target;

	// This option actually enables dollying in and out; left as "zoom" for
	// backwards compatibility
	this.noZoom = false;
	this.zoomSpeed = 1.0;

	// Limits to how far you can dolly in and out
	this.minDistance = 0;
	this.maxDistance = Infinity;

	// Set to true to disable this control
	this.noRotate = false;
	this.rotateSpeed = 1.0;

	// Set to true to disable this control
	this.noPan = false;
	this.keyPanSpeed = 7.0;	// pixels moved per arrow key push

	// Set to true to automatically rotate around the target
	this.autoRotate = false;
	this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60

	// How far you can orbit vertically, upper and lower limits.
	// Range is 0 to Math.PI radians.
	this.minPolarAngle = 0; // radians
	this.maxPolarAngle = Math.PI; // radians

	// Set to true to disable use of the keys
	this.noKeys = true;

	// The four arrow keys
	this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };

	////////////
	// internals

	var scope = this;

	var EPS = 0.000001;

	var rotateStart = new THREE.Vector2();
	var rotateEnd = new THREE.Vector2();
	var rotateDelta = new THREE.Vector2();

	var panStart = new THREE.Vector2();
	var panEnd = new THREE.Vector2();
	var panDelta = new THREE.Vector2();
	var panOffset = new THREE.Vector3();

	var offset = new THREE.Vector3();

	var dollyStart = new THREE.Vector2();
	var dollyEnd = new THREE.Vector2();
	var dollyDelta = new THREE.Vector2();

	var phiDelta = 0;
	var thetaDelta = 0;
	var scale = 1;
	var pan = new THREE.Vector3();

	var lastPosition = new THREE.Vector3();

	var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };

	var state = STATE.NONE;

	// for reset

	this.target0 = this.target.clone();
	this.position0 = this.object.position.clone();

	// so camera.up is the orbit axis

	var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
	var quatInverse = quat.clone().inverse();

	// events

	var changeEvent = { type: 'change' };
	var startEvent = { type: 'start'};
	var endEvent = { type: 'end'};

	this.rotateLeft = function ( angle ) {

		if ( angle === undefined ) {

			angle = getAutoRotationAngle();

		}

		thetaDelta -= angle;

	};

	this.rotateUp = function ( angle ) {

		if ( angle === undefined ) {

			angle = getAutoRotationAngle();

		}

		phiDelta -= angle;

	};

	// pass in distance in world space to move left
	this.panLeft = function ( distance ) {

		var te = this.object.matrix.elements;

		// get X column of matrix
		panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] );
		panOffset.multiplyScalar( - distance );
		
		pan.add( panOffset );

	};

	// pass in distance in world space to move up
	this.panUp = function ( distance ) {

		var te = this.object.matrix.elements;

		// get Y column of matrix
		panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] );
		panOffset.multiplyScalar( distance );
		
		pan.add( panOffset );

	};
	
	// pass in x,y of change desired in pixel space,
	// right and down are positive
	this.pan = function ( deltaX, deltaY ) {

		var element = scope.domElement === document ? scope.domElement.body : scope.domElement;

		if ( scope.object.fov !== undefined ) {

			// perspective
			var position = scope.object.position;
			var offset = position.clone().sub( scope.target );
			var targetDistance = offset.length();

			// half of the fov is center to top of screen
			targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );

			// we actually don't use screenWidth, since perspective camera is fixed to screen height
			scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight );
			scope.panUp( 2 * deltaY * targetDistance / element.clientHeight );

		} else if ( scope.object.top !== undefined ) {

			// orthographic
			scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth );
			scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight );

		} else {

			// camera neither orthographic or perspective
			console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );

		}

	};

	this.dollyIn = function ( dollyScale ) {

		if ( dollyScale === undefined ) {

			dollyScale = getZoomScale();

		}

		scale /= dollyScale;

	};

	this.dollyOut = function ( dollyScale ) {

		if ( dollyScale === undefined ) {

			dollyScale = getZoomScale();

		}

		scale *= dollyScale;

	};

	this.update = function () {

		var position = this.object.position;

		offset.copy( position ).sub( this.target );

		// rotate offset to "y-axis-is-up" space
		offset.applyQuaternion( quat );

		// angle from z-axis around y-axis

		var theta = Math.atan2( offset.x, offset.z );

		// angle from y-axis

		var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );

		if ( this.autoRotate ) {

			this.rotateLeft( getAutoRotationAngle() );

		}

		theta += thetaDelta;
		phi += phiDelta;

		// restrict phi to be between desired limits
		phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );

		// restrict phi to be betwee EPS and PI-EPS
		phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );

		var radius = offset.length() * scale;

		// restrict radius to be between desired limits
		radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
		
		// move target to panned location
		this.target.add( pan );

		offset.x = radius * Math.sin( phi ) * Math.sin( theta );
		offset.y = radius * Math.cos( phi );
		offset.z = radius * Math.sin( phi ) * Math.cos( theta );

		// rotate offset back to "camera-up-vector-is-up" space
		offset.applyQuaternion( quatInverse );

		position.copy( this.target ).add( offset );

		this.object.lookAt( this.target );

		thetaDelta = 0;
		phiDelta = 0;
		scale = 1;
		pan.set( 0, 0, 0 );

		if ( lastPosition.distanceToSquared( this.object.position ) > EPS ) {

			this.dispatchEvent( changeEvent );

			lastPosition.copy( this.object.position );

		}

	};


	this.reset = function () {

		state = STATE.NONE;

		this.target.copy( this.target0 );
		this.object.position.copy( this.position0 );

		this.update();

	};

	function getAutoRotationAngle() {

		return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;

	}

	function getZoomScale() {

		return Math.pow( 0.95, scope.zoomSpeed );

	}

	function onMouseDown( event ) {

		if ( scope.enabled === false ) return;
		event.preventDefault();

		if ( event.button === 0 && !event.ctrlKey ) {
			if ( scope.noRotate === true ) return;

			state = STATE.ROTATE;

			rotateStart.set( event.clientX, event.clientY );

		} else if ( event.button === 2 ) {
			if ( scope.noZoom === true ) return;

			state = STATE.DOLLY;

			dollyStart.set( event.clientX, event.clientY );

		} else if ( event.button === 1 || (event.button === 0 && event.ctrlKey) ) {
			if ( scope.noPan === true ) return;

			state = STATE.PAN;

			panStart.set( event.clientX, event.clientY );

		}

		scope.domElement.addEventListener( 'mousemove', onMouseMove, false );
		scope.domElement.addEventListener( 'mouseup', onMouseUp, false );
		scope.dispatchEvent( startEvent );

	}

	function onMouseMove( event ) {

		if ( scope.enabled === false ) return;

		event.preventDefault();

		var element = scope.domElement === document ? scope.domElement.body : scope.domElement;

		if ( state === STATE.ROTATE ) {

			if ( scope.noRotate === true ) return;

			rotateEnd.set( event.clientX, event.clientY );
			rotateDelta.subVectors( rotateEnd, rotateStart );

			// rotating across whole screen goes 360 degrees around
			scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );

			// rotating up and down along whole screen attempts to go 360, but limited to 180
			scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );

			rotateStart.copy( rotateEnd );

		} else if ( state === STATE.DOLLY ) {

			if ( scope.noZoom === true ) return;

			dollyEnd.set( event.clientX, event.clientY );
			dollyDelta.subVectors( dollyEnd, dollyStart );

			if ( dollyDelta.y > 0 ) {

				scope.dollyIn();

			} else {

				scope.dollyOut();

			}

			dollyStart.copy( dollyEnd );

		} else if ( state === STATE.PAN ) {

			if ( scope.noPan === true ) return;

			panEnd.set( event.clientX, event.clientY );
			panDelta.subVectors( panEnd, panStart );
			
			scope.pan( panDelta.x, panDelta.y );

			panStart.copy( panEnd );

		}

		scope.update();

	}

	function onMouseUp( /* event */ ) {

		if ( scope.enabled === false ) return;

		scope.domElement.removeEventListener( 'mousemove', onMouseMove, false );
		scope.domElement.removeEventListener( 'mouseup', onMouseUp, false );
		scope.dispatchEvent( endEvent );
		state = STATE.NONE;

	}

	function onMouseWheel( event ) {

		if ( scope.enabled === false || scope.noZoom === true ) return;

		event.preventDefault();
		event.stopPropagation();

		var delta = 0;

		if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9

			delta = event.wheelDelta;

		} else if ( event.detail !== undefined ) { // Firefox

			delta = - event.detail;

		}

		if ( delta > 0 ) {

			scope.dollyOut();

		} else {

			scope.dollyIn();

		}

		scope.update();
		scope.dispatchEvent( startEvent );
		scope.dispatchEvent( endEvent );

	}

	function onKeyDown( event ) {

		if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return;
		
		switch ( event.keyCode ) {

			case scope.keys.UP:
				scope.pan( 0, scope.keyPanSpeed );
				scope.update();
				break;

			case scope.keys.BOTTOM:
				scope.pan( 0, - scope.keyPanSpeed );
				scope.update();
				break;

			case scope.keys.LEFT:
				scope.pan( scope.keyPanSpeed, 0 );
				scope.update();
				break;

			case scope.keys.RIGHT:
				scope.pan( - scope.keyPanSpeed, 0 );
				scope.update();
				break;

		}

	}

	function touchstart( event ) {

		if ( scope.enabled === false ) return;

		switch ( event.touches.length ) {

			case 1:	// one-fingered touch: rotate

				if ( scope.noRotate === true ) return;

				state = STATE.TOUCH_ROTATE;

				rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
				break;

			case 2:	// two-fingered touch: dolly

				if ( scope.noZoom === true ) return;

				state = STATE.TOUCH_DOLLY;

				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
				var distance = Math.sqrt( dx * dx + dy * dy );
				dollyStart.set( 0, distance );
				break;

			case 3: // three-fingered touch: pan

				if ( scope.noPan === true ) return;

				state = STATE.TOUCH_PAN;

				panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
				break;

			default:

				state = STATE.NONE;

		}

		scope.dispatchEvent( startEvent );

	}

	function touchmove( event ) {

		if ( scope.enabled === false ) return;

		event.preventDefault();
		event.stopPropagation();

		var element = scope.domElement === document ? scope.domElement.body : scope.domElement;

		switch ( event.touches.length ) {

			case 1: // one-fingered touch: rotate

				if ( scope.noRotate === true ) return;
				if ( state !== STATE.TOUCH_ROTATE ) return;

				rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
				rotateDelta.subVectors( rotateEnd, rotateStart );

				// rotating across whole screen goes 360 degrees around
				scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
				// rotating up and down along whole screen attempts to go 360, but limited to 180
				scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );

				rotateStart.copy( rotateEnd );

				scope.update();
				break;

			case 2: // two-fingered touch: dolly

				if ( scope.noZoom === true ) return;
				if ( state !== STATE.TOUCH_DOLLY ) return;

				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
				var distance = Math.sqrt( dx * dx + dy * dy );

				dollyEnd.set( 0, distance );
				dollyDelta.subVectors( dollyEnd, dollyStart );

				if ( dollyDelta.y > 0 ) {

					scope.dollyOut();

				} else {

					scope.dollyIn();

				}

				dollyStart.copy( dollyEnd );

				scope.update();
				break;

			case 3: // three-fingered touch: pan

				if ( scope.noPan === true ) return;
				if ( state !== STATE.TOUCH_PAN ) return;

				panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
				panDelta.subVectors( panEnd, panStart );
				
				scope.pan( panDelta.x, panDelta.y );

				panStart.copy( panEnd );

				scope.update();
				break;

			default:

				state = STATE.NONE;

		}

	}

	function touchend( /* event */ ) {

		if ( scope.enabled === false ) return;

		scope.dispatchEvent( endEvent );
		state = STATE.NONE;

	}

	this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
	this.domElement.addEventListener( 'mousedown', onMouseDown, false );
	this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
	this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox

	this.domElement.addEventListener( 'touchstart', touchstart, false );
	this.domElement.addEventListener( 'touchend', touchend, false );
	this.domElement.addEventListener( 'touchmove', touchmove, false );

	window.addEventListener( 'keydown', onKeyDown, false );

	// force an update at start
	this.update();

};

THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );




================================================
FILE: app/scripts/lib/Viewport.js
================================================
var container, $container;

var camera, controls, scene, renderer;

var geometry, group;

var mouse = new THREE.Vector2(),
offset = new THREE.Vector3(),
INTERSECTED, SELECTED;

var objects = [], plane;

var mouseX = 0, mouseY = 0;

var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

init();
render();

function init() {

	container = document.getElementById("viewer");
	$container = $(container);

	camera = new THREE.PerspectiveCamera( 30, $container.width() / $container.height(), 1, 10000 );

	camera.position.set( 140, 140, 140 );
	camera.up.set( 0, 0, 1 );
	camera.lookAt( new THREE.Vector3(0,0,0) );

	scene = new THREE.Scene();

	renderer = new THREE.WebGLRenderer({antialias: true});
	renderer.setClearColor( 0xffffff, 1 );
	renderer.setSize( $container.width(), $container.height() );
	renderer.sortObjects = false;

	container.appendChild( renderer.domElement );
	renderer.domElement.setAttribute("id", "renderer_canvas");

	// add subtle ambient lighting
	var ambientLight = new THREE.AmbientLight(0x555555);
	scene.add(ambientLight);

	// add directional light source
	var directionalLight = new THREE.DirectionalLight(0xbbbbbb);
	directionalLight.position.set(50, 30, 50);
	scene.add(directionalLight);

	var directionalLight = new THREE.DirectionalLight(0xaaaaaa);
	directionalLight.position.set(-0.2, -0.8, 1).normalize();
	scene.add(directionalLight);

	makeGrid();

	controls = new THREE.OrbitControls(camera, container);

	window.addEventListener( 'resize', onWindowResize, false );

	// full screen blur
	// composer = new THREE.EffectComposer( renderer );
	// composer.addPass( new THREE.RenderPass( scene, camera ) );

	// hblur = new THREE.ShaderPass( THREE.HorizontalBlurShader );
	// composer.addPass( hblur );

	// vblur = new THREE.ShaderPass( THREE.VerticalBlurShader );
	// // set this shader pass to render to screen so we can see the effects
	// vblur.renderToScreen = true;
	// composer.addPass( vblur );


	animate();

}

function makeGrid(){

	var l = 60;

	var axisHelper = new THREE.AxisHelper( l );
	scene.add( axisHelper );

	var geometry = new THREE.Geometry();
	var geometryThick = new THREE.Geometry();

	var n = l;
	var inc = 2 * l / n;
	var rate = 10;

	for (var i = 0; i < n + 1; i++){

    	var v1 = new THREE.Vector3(-l, -l + i * inc, 0);
		var v2 = new THREE.Vector3(l, -l + i * inc, 0);

    	geometry.vertices.push(v1);
    	geometry.vertices.push(v2);

    	if (i % rate == 0){
			geometryThick.vertices.push(v1);
    		geometryThick.vertices.push(v2);
    	}
	}

	for (var i = 0; i < n + 1; i++){
		var v1 = new THREE.Vector3(-l + i * inc, l, 0);
		var v2 = new THREE.Vector3(-l + i * inc, -l, 0);

		geometry.vertices.push(v1);
    	geometry.vertices.push(v2);

    	if (i % rate == 0){
			geometryThick.vertices.push(v1);
    		geometryThick.vertices.push(v2);
    	}
	}

	var material = new THREE.LineBasicMaterial({
        color: 0xeeeeee,
        linewidth: 0.1
    });

    var materialThick = new THREE.LineBasicMaterial({
        color: 0xeeeeee,
        linewidth: 2
    });

    var line = new THREE.Line(geometry, material, THREE.LinePieces);
    var lineThick = new THREE.Line(geometryThick, materialThick, THREE.LinePieces);

    scene.add(line);
    scene.add(lineThick);

}

function onWindowResize() {

	windowHalfX = $container.width() / 2;
	windowHalfY = $container.height() / 2;

	camera.aspect = windowHalfX/ windowHalfY;
	camera.updateProjectionMatrix();

	renderer.setSize( 2*windowHalfX, 2*windowHalfY );

	render();

}

function animate() {

	requestAnimationFrame( animate );
	render();

}

var doBlur = true;

function render() {

	controls.update();
	renderer.render( scene, camera );
	
	if (doBlur){
		// composer.render();
	}

}




================================================
FILE: app/scripts/lib/flood/async.js
================================================
/*!
 * async
 * https://github.com/caolan/async
 *
 * Copyright 2010-2014 Caolan McMahon
 * Released under the MIT license
 */
/*jshint onevar: false, indent:4 */
/*global setImmediate: false, setTimeout: false, console: false */
(function () {

    var async = {};

    // global on the server, window in the browser
    var root, previous_async;

    root = this;
    if (root != null) {
      previous_async = root.async;
    }

    async.noConflict = function () {
        root.async = previous_async;
        return async;
    };

    function only_once(fn) {
        var called = false;
        return function() {
            if (called) throw new Error("Callback was already called.");
            called = true;
            fn.apply(root, arguments);
        }
    }

    //// cross-browser compatiblity functions ////

    var _toString = Object.prototype.toString;

    var _isArray = Array.isArray || function (obj) {
        return _toString.call(obj) === '[object Array]';
    };

    var _each = function (arr, iterator) {
        if (arr.forEach) {
            return arr.forEach(iterator);
        }
        for (var i = 0; i < arr.length; i += 1) {
            iterator(arr[i], i, arr);
        }
    };

    var _map = function (arr, iterator) {
        if (arr.map) {
            return arr.map(iterator);
        }
        var results = [];
        _each(arr, function (x, i, a) {
            results.push(iterator(x, i, a));
        });
        return results;
    };

    var _reduce = function (arr, iterator, memo) {
        if (arr.reduce) {
            return arr.reduce(iterator, memo);
        }
        _each(arr, function (x, i, a) {
            memo = iterator(memo, x, i, a);
        });
        return memo;
    };

    var _keys = function (obj) {
        if (Object.keys) {
            return Object.keys(obj);
        }
        var keys = [];
        for (var k in obj) {
            if (obj.hasOwnProperty(k)) {
                keys.push(k);
            }
        }
        return keys;
    };

    //// exported async module functions ////

    //// nextTick implementation with browser-compatible fallback ////
    if (typeof process === 'undefined' || !(process.nextTick)) {
        if (typeof setImmediate === 'function') {
            async.nextTick = function (fn) {
                // not a direct alias for IE10 compatibility
                setImmediate(fn);
            };
            async.setImmediate = async.nextTick;
        }
        else {
            async.nextTick = function (fn) {
                setTimeout(fn, 0);
            };
            async.setImmediate = async.nextTick;
        }
    }
    else {
        async.nextTick = process.nextTick;
        if (typeof setImmediate !== 'undefined') {
            async.setImmediate = function (fn) {
              // not a direct alias for IE10 compatibility
              setImmediate(fn);
            };
        }
        else {
            async.setImmediate = async.nextTick;
        }
    }

    async.each = function (arr, iterator, callback) {
        callback = callback || function () {};
        if (!arr.length) {
            return callback();
        }
        var completed = 0;
        _each(arr, function (x) {
            iterator(x, only_once(done) );
        });
        function done(err) {
          if (err) {
              callback(err);
              callback = function () {};
          }
          else {
              completed += 1;
              if (completed >= arr.length) {
                  callback();
              }
          }
        }
    };
    async.forEach = async.each;

    async.eachSeries = function (arr, iterator, callback) {
        callback = callback || function () {};
        if (!arr.length) {
            return callback();
        }
        var completed = 0;
        var iterate = function () {
            iterator(arr[completed], function (err) {
                if (err) {
                    callback(err);
                    callback = function () {};
                }
                else {
                    completed += 1;
                    if (completed >= arr.length) {
                        callback();
                    }
                    else {
                        iterate();
                    }
                }
            });
        };
        iterate();
    };
    async.forEachSeries = async.eachSeries;

    async.eachLimit = function (arr, limit, iterator, callback) {
        var fn = _eachLimit(limit);
        fn.apply(null, [arr, iterator, callback]);
    };
    async.forEachLimit = async.eachLimit;

    var _eachLimit = function (limit) {

        return function (arr, iterator, callback) {
            callback = callback || function () {};
            if (!arr.length || limit <= 0) {
                return callback();
            }
            var completed = 0;
            var started = 0;
            var running = 0;

            (function replenish () {
                if (completed >= arr.length) {
                    return callback();
                }

                while (running < limit && started < arr.length) {
                    started += 1;
                    running += 1;
                    iterator(arr[started - 1], function (err) {
                        if (err) {
                            callback(err);
                            callback = function () {};
                        }
                        else {
                            completed += 1;
                            running -= 1;
                            if (completed >= arr.length) {
                                callback();
                            }
                            else {
                                replenish();
                            }
                        }
                    });
                }
            })();
        };
    };


    var doParallel = function (fn) {
        return function () {
            var args = Array.prototype.slice.call(arguments);
            return fn.apply(null, [async.each].concat(args));
        };
    };
    var doParallelLimit = function(limit, fn) {
        return function () {
            var args = Array.prototype.slice.call(arguments);
            return fn.apply(null, [_eachLimit(limit)].concat(args));
        };
    };
    var doSeries = function (fn) {
        return function () {
            var args = Array.prototype.slice.call(arguments);
            return fn.apply(null, [async.eachSeries].concat(args));
        };
    };


    var _asyncMap = function (eachfn, arr, iterator, callback) {
        arr = _map(arr, function (x, i) {
            return {index: i, value: x};
        });
        if (!callback) {
            eachfn(arr, function (x, callback) {
                iterator(x.value, function (err) {
                    callback(err);
                });
            });
        } else {
            var results = [];
            eachfn(arr, function (x, callback) {
                iterator(x.value, function (err, v) {
                    results[x.index] = v;
                    callback(err);
                });
            }, function (err) {
                callback(err, results);
            });
        }
    };
    async.map = doParallel(_asyncMap);
    async.mapSeries = doSeries(_asyncMap);
    async.mapLimit = function (arr, limit, iterator, callback) {
        return _mapLimit(limit)(arr, iterator, callback);
    };

    var _mapLimit = function(limit) {
        return doParallelLimit(limit, _asyncMap);
    };

    // reduce only has a series version, as doing reduce in parallel won't
    // work in many situations.
    async.reduce = function (arr, memo, iterator, callback) {
        async.eachSeries(arr, function (x, callback) {
            iterator(memo, x, function (err, v) {
                memo = v;
                callback(err);
            });
        }, function (err) {
            callback(err, memo);
        });
    };
    // inject alias
    async.inject = async.reduce;
    // foldl alias
    async.foldl = async.reduce;

    async.reduceRight = function (arr, memo, iterator, callback) {
        var reversed = _map(arr, function (x) {
            return x;
        }).reverse();
        async.reduce(reversed, memo, iterator, callback);
    };
    // foldr alias
    async.foldr = async.reduceRight;

    var _filter = function (eachfn, arr, iterator, callback) {
        var results = [];
        arr = _map(arr, function (x, i) {
            return {index: i, value: x};
        });
        eachfn(arr, function (x, callback) {
            iterator(x.value, function (v) {
                if (v) {
                    results.push(x);
                }
                callback();
            });
        }, function (err) {
            callback(_map(results.sort(function (a, b) {
                return a.index - b.index;
            }), function (x) {
                return x.value;
            }));
        });
    };
    async.filter = doParallel(_filter);
    async.filterSeries = doSeries(_filter);
    // select alias
    async.select = async.filter;
    async.selectSeries = async.filterSeries;

    var _reject = function (eachfn, arr, iterator, callback) {
        var results = [];
        arr = _map(arr, function (x, i) {
            return {index: i, value: x};
        });
        eachfn(arr, function (x, callback) {
            iterator(x.value, function (v) {
                if (!v) {
                    results.push(x);
                }
                callback();
            });
        }, function (err) {
            callback(_map(results.sort(function (a, b) {
                return a.index - b.index;
            }), function (x) {
                return x.value;
            }));
        });
    };
    async.reject = doParallel(_reject);
    async.rejectSeries = doSeries(_reject);

    var _detect = function (eachfn, arr, iterator, main_callback) {
        eachfn(arr, function (x, callback) {
            iterator(x, function (result) {
                if (result) {
                    main_callback(x);
                    main_callback = function () {};
                }
                else {
                    callback();
                }
            });
        }, function (err) {
            main_callback();
        });
    };
    async.detect = doParallel(_detect);
    async.detectSeries = doSeries(_detect);

    async.some = function (arr, iterator, main_callback) {
        async.each(arr, function (x, callback) {
            iterator(x, function (v) {
                if (v) {
                    main_callback(true);
                    main_callback = function () {};
                }
                callback();
            });
        }, function (err) {
            main_callback(false);
        });
    };
    // any alias
    async.any = async.some;

    async.every = function (arr, iterator, main_callback) {
        async.each(arr, function (x, callback) {
            iterator(x, function (v) {
                if (!v) {
                    main_callback(false);
                    main_callback = function () {};
                }
                callback();
            });
        }, function (err) {
            main_callback(true);
        });
    };
    // all alias
    async.all = async.every;

    async.sortBy = function (arr, iterator, callback) {
        async.map(arr, function (x, callback) {
            iterator(x, function (err, criteria) {
                if (err) {
                    callback(err);
                }
                else {
                    callback(null, {value: x, criteria: criteria});
                }
            });
        }, function (err, results) {
            if (err) {
                return callback(err);
            }
            else {
                var fn = function (left, right) {
                    var a = left.criteria, b = right.criteria;
                    return a < b ? -1 : a > b ? 1 : 0;
                };
                callback(null, _map(results.sort(fn), function (x) {
                    return x.value;
                }));
            }
        });
    };

    async.auto = function (tasks, callback) {
        callback = callback || function () {};
        var keys = _keys(tasks);
        var remainingTasks = keys.length
        if (!remainingTasks) {
            return callback();
        }

        var results = {};

        var listeners = [];
        var addListener = function (fn) {
            listeners.unshift(fn);
        };
        var removeListener = function (fn) {
            for (var i = 0; i < listeners.length; i += 1) {
                if (listeners[i] === fn) {
                    listeners.splice(i, 1);
                    return;
                }
            }
        };
        var taskComplete = function () {
            remainingTasks--
            _each(listeners.slice(0), function (fn) {
                fn();
            });
        };

        addListener(function () {
            if (!remainingTasks) {
                var theCallback = callback;
                // prevent final callback from calling itself if it errors
                callback = function () {};

                theCallback(null, results);
            }
        });

        _each(keys, function (k) {
            var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
            var taskCallback = function (err) {
                var args = Array.prototype.slice.call(arguments, 1);
                if (args.length <= 1) {
                    args = args[0];
                }
                if (err) {
                    var safeResults = {};
                    _each(_keys(results), function(rkey) {
                        safeResults[rkey] = results[rkey];
                    });
                    safeResults[k] = args;
                    callback(err, safeResults);
                    // stop subsequent errors hitting callback multiple times
                    callback = function () {};
                }
                else {
                    results[k] = args;
                    async.setImmediate(taskComplete);
                }
            };
            var requires = task.slice(0, Math.abs(task.length - 1)) || [];
            var ready = function () {
                return _reduce(requires, function (a, x) {
                    return (a && results.hasOwnProperty(x));
                }, true) && !results.hasOwnProperty(k);
            };
            if (ready()) {
                task[task.length - 1](taskCallback, results);
            }
            else {
                var listener = function () {
                    if (ready()) {
                        removeListener(listener);
                        task[task.length - 1](taskCallback, results);
                    }
                };
                addListener(listener);
            }
        });
    };

    async.retry = function(times, task, callback) {
        var DEFAULT_TIMES = 5;
        var attempts = [];
        // Use defaults if times not passed
        if (typeof times === 'function') {
            callback = task;
            task = times;
            times = DEFAULT_TIMES;
        }
        // Make sure times is a number
        times = parseInt(times, 10) || DEFAULT_TIMES;
        var wrappedTask = function(wrappedCallback, wrappedResults) {
            var retryAttempt = function(task, finalAttempt) {
                return function(seriesCallback) {
                    task(function(err, result){
                        seriesCallback(!err || finalAttempt, {err: err, result: result});
                    }, wrappedResults);
                };
            };
            while (times) {
                attempts.push(retryAttempt(task, !(times-=1)));
            }
            async.series(attempts, function(done, data){
                data = data[data.length - 1];
                (wrappedCallback || callback)(data.err, data.result);
            });
        }
        // If a callback is passed, run this as a controll flow
        return callback ? wrappedTask() : wrappedTask
    };

    async.waterfall = function (tasks, callback) {
        callback = callback || function () {};
        if (!_isArray(tasks)) {
          var err = new Error('First argument to waterfall must be an array of functions');
          return callback(err);
        }
        if (!tasks.length) {
            return callback();
        }
        var wrapIterator = function (iterator) {
            return function (err) {
                if (err) {
                    callback.apply(null, arguments);
                    callback = function () {};
                }
                else {
                    var args = Array.prototype.slice.call(arguments, 1);
                    var next = iterator.next();
                    if (next) {
                        args.push(wrapIterator(next));
                    }
                    else {
                        args.push(callback);
                    }
                    async.setImmediate(function () {
                        iterator.apply(null, args);
                    });
                }
            };
        };
        wrapIterator(async.iterator(tasks))();
    };

    var _parallel = function(eachfn, tasks, callback) {
        callback = callback || function () {};
        if (_isArray(tasks)) {
            eachfn.map(tasks, function (fn, callback) {
                if (fn) {
                    fn(function (err) {
                        var args = Array.prototype.slice.call(arguments, 1);
                        if (args.length <= 1) {
                            args = args[0];
                        }
                        callback.call(null, err, args);
                    });
                }
            }, callback);
        }
        else {
            var results = {};
            eachfn.each(_keys(tasks), function (k, callback) {
                tasks[k](function (err) {
                    var args = Array.prototype.slice.call(arguments, 1);
                    if (args.length <= 1) {
                        args = args[0];
                    }
                    results[k] = args;
                    callback(err);
                });
            }, function (err) {
                callback(err, results);
            });
        }
    };

    async.parallel = function (tasks, callback) {
        _parallel({ map: async.map, each: async.each }, tasks, callback);
    };

    async.parallelLimit = function(tasks, limit, callback) {
        _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
    };

    async.series = function (tasks, callback) {
        callback = callback || function () {};
        if (_isArray(tasks)) {
            async.mapSeries(tasks, function (fn, callback) {
                if (fn) {
                    fn(function (err) {
                        var args = Array.prototype.slice.call(arguments, 1);
                        if (args.length <= 1) {
                            args = args[0];
                        }
                        callback.call(null, err, args);
                    });
                }
            }, callback);
        }
        else {
            var results = {};
            async.eachSeries(_keys(tasks), function (k, callback) {
                tasks[k](function (err) {
                    var args = Array.prototype.slice.call(arguments, 1);
                    if (args.length <= 1) {
                        args = args[0];
                    }
                    results[k] = args;
                    callback(err);
                });
            }, function (err) {
                callback(err, results);
            });
        }
    };

    async.iterator = function (tasks) {
        var makeCallback = function (index) {
            var fn = function () {
                if (tasks.length) {
                    tasks[index].apply(null, arguments);
                }
                return fn.next();
            };
            fn.next = function () {
                return (index < tasks.length - 1) ? makeCallback(index + 1): null;
            };
            return fn;
        };
        return makeCallback(0);
    };

    async.apply = function (fn) {
        var args = Array.prototype.slice.call(arguments, 1);
        return function () {
            return fn.apply(
                null, args.concat(Array.prototype.slice.call(arguments))
            );
        };
    };

    var _concat = function (eachfn, arr, fn, callback) {
        var r = [];
        eachfn(arr, function (x, cb) {
            fn(x, function (err, y) {
                r = r.concat(y || []);
                cb(err);
            });
        }, function (err) {
            callback(err, r);
        });
    };
    async.concat = doParallel(_concat);
    async.concatSeries = doSeries(_concat);

    async.whilst = function (test, iterator, callback) {
        if (test()) {
            iterator(function (err) {
                if (err) {
                    return callback(err);
                }
                async.whilst(test, iterator, callback);
            });
        }
        else {
            callback();
        }
    };

    async.doWhilst = function (iterator, test, callback) {
        iterator(function (err) {
            if (err) {
                return callback(err);
            }
            var args = Array.prototype.slice.call(arguments, 1);
            if (test.apply(null, args)) {
                async.doWhilst(iterator, test, callback);
            }
            else {
                callback();
            }
        });
    };

    async.until = function (test, iterator, callback) {
        if (!test()) {
            iterator(function (err) {
                if (err) {
                    return callback(err);
                }
                async.until(test, iterator, callback);
            });
        }
        else {
            callback();
        }
    };

    async.doUntil = function (iterator, test, callback) {
        iterator(function (err) {
            if (err) {
                return callback(err);
            }
            var args = Array.prototype.slice.call(arguments, 1);
            if (!test.apply(null, args)) {
                async.doUntil(iterator, test, callback);
            }
            else {
                callback();
            }
        });
    };

    async.queue = function (worker, concurrency) {
        if (concurrency === undefined) {
            concurrency = 1;
        }
        function _insert(q, data, pos, callback) {
          if (!q.started){
            q.started = true;
          }
          if (!_isArray(data)) {
              data = [data];
          }
          if(data.length == 0) {
             // call drain immediately if there are no tasks
             return async.setImmediate(function() {
                 if (q.drain) {
                     q.drain();
                 }
             });
          }
          _each(data, function(task) {
              var item = {
                  data: task,
                  callback: typeof callback === 'function' ? callback : null
              };

              if (pos) {
                q.tasks.unshift(item);
              } else {
                q.tasks.push(item);
              }

              if (q.saturated && q.tasks.length === q.concurrency) {
                  q.saturated();
              }
              async.setImmediate(q.process);
          });
        }

        var workers = 0;
        var q = {
            tasks: [],
            concurrency: concurrency,
            saturated: null,
            empty: null,
            drain: null,
            started: false,
            paused: false,
            push: function (data, callback) {
              _insert(q, data, false, callback);
            },
            kill: function () {
              q.drain = null;
              q.tasks = [];
            },
            unshift: function (data, callback) {
              _insert(q, data, true, callback);
            },
            process: function () {
                if (!q.paused && workers < q.concurrency && q.tasks.length) {
                    var task = q.tasks.shift();
                    if (q.empty && q.tasks.length === 0) {
                        q.empty();
                    }
                    workers += 1;
                    var next = function () {
                        workers -= 1;
                        if (task.callback) {
                            task.callback.apply(task, arguments);
                        }
                        if (q.drain && q.tasks.length + workers === 0) {
                            q.drain();
                        }
                        q.process();
                    };
                    var cb = only_once(next);
                    worker(task.data, cb);
                }
            },
            length: function () {
                return q.tasks.length;
            },
            running: function () {
                return workers;
            },
            idle: function() {
                return q.tasks.length + workers === 0;
            },
            pause: function () {
                if (q.paused === true) { return; }
                q.paused = true;
            },
            resume: function () {
                if (q.paused === false) { return; }
                q.paused = false;
                // Need to call q.process once per concurrent
                // worker to preserve full concurrency after pause
                for (var w = 1; w <= q.concurrency; w++) {
                    async.setImmediate(q.process);
                }
            }
        };
        return q;
    };

    async.priorityQueue = function (worker, concurrency) {

        function _compareTasks(a, b){
          return a.priority - b.priority;
        };

        function _binarySearch(sequence, item, compare) {
          var beg = -1,
              end = sequence.length - 1;
          while (beg < end) {
            var mid = beg + ((end - beg + 1) >>> 1);
            if (compare(item, sequence[mid]) >= 0) {
              beg = mid;
            } else {
              end = mid - 1;
            }
          }
          return beg;
        }

        function _insert(q, data, priority, callback) {
          if (!q.started){
            q.started = true;
          }
          if (!_isArray(data)) {
              data = [data];
          }
          if(data.length == 0) {
             // call drain immediately if there are no tasks
             return async.setImmediate(function() {
                 if (q.drain) {
                     q.drain();
                 }
             });
          }
          _each(data, function(task) {
              var item = {
                  data: task,
                  priority: priority,
                  callback: typeof callback === 'function' ? callback : null
              };

              q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);

              if (q.saturated && q.tasks.length === q.concurrency) {
                  q.saturated();
              }
              async.setImmediate(q.process);
          });
        }

        // Start with a normal queue
        var q = async.queue(worker, concurrency);

        // Override push to accept second parameter representing priority
        q.push = function (data, priority, callback) {
          _insert(q, data, priority, callback);
        };

        // Remove unshift function
        delete q.unshift;

        return q;
    };

    async.cargo = function (worker, payload) {
        var working     = false,
            tasks       = [];

        var cargo = {
            tasks: tasks,
            payload: payload,
            saturated: null,
            empty: null,
            drain: null,
            drained: true,
            push: function (data, callback) {
                if (!_isArray(data)) {
                    data = [data];
                }
                _each(data, function(task) {
                    tasks.push({
                        data: task,
                        callback: typeof callback === 'function' ? callback : null
                    });
                    cargo.drained = false;
                    if (cargo.saturated && tasks.length === payload) {
                        cargo.saturated();
                    }
                });
                async.setImmediate(cargo.process);
            },
            process: function process() {
                if (working) return;
                if (tasks.length === 0) {
                    if(cargo.drain && !cargo.drained) cargo.drain();
                    cargo.drained = true;
                    return;
                }

                var ts = typeof payload === 'number'
                            ? tasks.splice(0, payload)
                            : tasks.splice(0, tasks.length);

                var ds = _map(ts, function (task) {
                    return task.data;
                });

                if(cargo.empty) cargo.empty();
                working = true;
                worker(ds, function () {
                    working = false;

                    var args = arguments;
                    _each(ts, function (data) {
                        if (data.callback) {
                            data.callback.apply(null, args);
                        }
                    });

                    process();
                });
            },
            length: function () {
                return tasks.length;
            },
            running: function () {
                return working;
            }
        };
        return cargo;
    };

    var _console_fn = function (name) {
        return function (fn) {
            var args = Array.prototype.slice.call(arguments, 1);
            fn.apply(null, args.concat([function (err) {
                var args = Array.prototype.slice.call(arguments, 1);
                if (typeof console !== 'undefined') {
                    if (err) {
                        if (console.error) {
                            console.error(err);
                        }
                    }
                    else if (console[name]) {
                        _each(args, function (x) {
                            console[name](x);
                        });
                    }
                }
            }]));
        };
    };
    async.log = _console_fn('log');
    async.dir = _console_fn('dir');
    /*async.info = _console_fn('info');
    async.warn = _console_fn('warn');
    async.error = _console_fn('error');*/

    async.memoize = function (fn, hasher) {
        var memo = {};
        var queues = {};
        hasher = hasher || function (x) {
            return x;
        };
        var memoized = function () {
            var args = Array.prototype.slice.call(arguments);
            var callback = args.pop();
            var key = hasher.apply(null, args);
            if (key in memo) {
                async.nextTick(function () {
                    callback.apply(null, memo[key]);
                });
            }
            else if (key in queues) {
                queues[key].push(callback);
            }
            else {
                queues[key] = [callback];
                fn.apply(null, args.concat([function () {
                    memo[key] = arguments;
                    var q = queues[key];
                    delete queues[key];
                    for (var i = 0, l = q.length; i < l; i++) {
                      q[i].apply(null, arguments);
                    }
                }]));
            }
        };
        memoized.memo = memo;
        memoized.unmemoized = fn;
        return memoized;
    };

    async.unmemoize = function (fn) {
      return function () {
        return (fn.unmemoized || fn).apply(null, arguments);
      };
    };

    async.times = function (count, iterator, callback) {
        var counter = [];
        for (var i = 0; i < count; i++) {
            counter.push(i);
        }
        return async.map(counter, iterator, callback);
    };

    async.timesSeries = function (count, iterator, callback) {
        var counter = [];
        for (var i = 0; i < count; i++) {
            counter.push(i);
        }
        return async.mapSeries(counter, iterator, callback);
    };

    async.seq = function (/* functions... */) {
        var fns = arguments;
        return function () {
            var that = this;
            var args = Array.prototype.slice.call(arguments);
            var callback = args.pop();
            async.reduce(fns, args, function (newargs, fn, cb) {
                fn.apply(that, newargs.concat([function () {
                    var err = arguments[0];
                    var nextargs = Array.prototype.slice.call(arguments, 1);
                    cb(err, nextargs);
                }]))
            },
            function (err, results) {
                callback.apply(that, [err].concat(results));
            });
        };
    };

    async.compose = function (/* functions... */) {
      return async.seq.apply(null, Array.prototype.reverse.call(arguments));
    };

    var _applyEach = function (eachfn, fns /*args...*/) {
        var go = function () {
            var that = this;
            var args = Array.prototype.slice.call(arguments);
            var callback = args.pop();
            return eachfn(fns, function (fn, cb) {
                fn.apply(that, args.concat([cb]));
            },
            callback);
        };
        if (arguments.length > 2) {
            var args = Array.prototype.slice.call(arguments, 2);
            return go.apply(this, args);
        }
        else {
            return go;
        }
    };
    async.applyEach = doParallel(_applyEach);
    async.applyEachSeries = doSeries(_applyEach);

    async.forever = function (fn, callback) {
        function next(err) {
            if (err) {
                if (callback) {
                    return callback(err);
                }
                throw err;
            }
            fn(next);
        }
        next();
    };

    // Node.js
    if (typeof module !== 'undefined' && module.exports) {
        module.exports = async;
    }
    // AMD / RequireJS
    else if (typeof define !== 'undefined' && define.amd) {
        define([], function () {
            return async;
        });
    }
    // included directly via <script> tag
    else {
        root.async = async;
    }

}());

================================================
FILE: app/scripts/lib/flood/csg.js
================================================
// Constructive Solid Geometry (CSG) is a modeling technique that uses Boolean
// operations like union and intersection to combine 3D solids. This library
// implements CSG operations on meshes elegantly and concisely using BSP trees,
// and is meant to serve as an easily understandable implementation of the
// algorithm. All edge cases involving overlapping coplanar polygons in both
// solids are correctly handled.
// 
// Example usage:
// 
//     var cube = CSG.cube();
//     var sphere = CSG.sphere({ radius: 1.3 });
//     var polygons = cube.subtract(sphere).toPolygons();
// 
// ## Implementation Details
// 
// All CSG operations are implemented in terms of two functions, `clipTo()` and
// `invert()`, which remove parts of a BSP tree inside another BSP tree and swap
// solid and empty space, respectively. To find the union of `a` and `b`, we
// want to remove everything in `a` inside `b` and everything in `b` inside `a`,
// then combine polygons from `a` and `b` into one solid:
// 
//     a.clipTo(b);
//     b.clipTo(a);
//     a.build(b.allPolygons());
// 
// The only tricky part is handling overlapping coplanar polygons in both trees.
// The code above keeps both copies, but we need to keep them in one tree and
// remove them in the other tree. To remove them from `b` we can clip the
// inverse of `b` against `a`. The code for union now looks like this:
// 
//     a.clipTo(b);
//     b.clipTo(a);
//     b.invert();
//     b.clipTo(a);
//     b.invert();
//     a.build(b.allPolygons());
// 
// Subtraction and intersection naturally follow from set operations. If
// union is `A | B`, subtraction is `A - B = ~(~A | B)` and intersection is
// `A & B = ~(~A | ~B)` where `~` is the complement operator.
// 
// ## License
// 
// Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license.
// Parts Copyright (c) 2012 Joost Nieuwenhuijse (joost@newhouse.nl) under the MIT license.

// # class CSG

// Holds a binary space partition tree representing a 3D solid. Two solids can
// be combined using the `union()`, `subtract()`, and `intersect()` methods.

CSG = function() {
  this.polygons = [];
};

// Construct a CSG solid from a list of `CSG.Polygon` instances.
CSG.fromPolygons = function(polygons) {
  var csg = new CSG();
  csg.polygons = polygons;
  return csg;
};

CSG.prototype = {
  clone: function() {
    var csg = new CSG();
    csg.polygons = this.polygons.map(function(p) { return p.clone(); });
    return csg;
  },

  toPolygons: function() {
    return this.polygons;
  },

  // Return a new CSG solid representing space in either this solid or in the
  // solid `csg`. Neither this solid nor the solid `csg` are modified.
  // 
  //     A.union(B)
  // 
  //     +-------+            +-------+
  //     |       |            |       |
  //     |   A   |            |       |
  //     |    +--+----+   =   |       +----+
  //     +----+--+    |       +----+       |
  //          |   B   |            |       |
  //          |       |            |       |
  //          +-------+            +-------+
  // 
  union: function(csg) {
    var a = new CSG.Node(this.clone().polygons);
    var b = new CSG.Node(csg.clone().polygons);
    a.clipTo(b);
    b.clipTo(a);
    b.invert();
    b.clipTo(a);
    b.invert();
    a.build(b.allPolygons());
    return CSG.fromPolygons(a.allPolygons());
  },

  // Return a new CSG solid representing space in this solid but not in the
  // solid `csg`. Neither this solid nor the solid `csg` are modified.
  // 
  //     A.subtract(B)
  // 
  //     +-------+            +-------+
  //     |       |            |       |
  //     |   A   |            |       |
  //     |    +--+----+   =   |    +--+
  //     +----+--+    |       +----+
  //          |   B   |
  //          |       |
  //          +-------+
  // 
  subtract: function(csg) {
    var a = new CSG.Node(this.clone().polygons);
    var b = new CSG.Node(csg.clone().polygons);
    a.invert();
    a.clipTo(b);
    b.clipTo(a);
    b.invert();
    b.clipTo(a);
    b.invert();
    a.build(b.allPolygons());
    a.invert();
    return CSG.fromPolygons(a.allPolygons());
  },

  // Return a new CSG solid representing space both this solid and in the
  // solid `csg`. Neither this solid nor the solid `csg` are modified.
  // 
  //     A.intersect(B)
  // 
  //     +-------+
  //     |       |
  //     |   A   |
  //     |    +--+----+   =   +--+
  //     +----+--+    |       +--+
  //          |   B   |
  //          |       |
  //          +-------+
  // 
  intersect: function(csg) {
    var a = new CSG.Node(this.clone().polygons);
    var b = new CSG.Node(csg.clone().polygons);
    a.invert();
    b.clipTo(a);
    b.invert();
    a.clipTo(b);
    b.clipTo(a);
    a.build(b.allPolygons());
    a.invert();
    return CSG.fromPolygons(a.allPolygons());
  },

  // Return a new CSG solid with solid and empty space switched. This solid is
  // not modified.
  inverse: function() {
    var csg = this.clone();
    csg.polygons.map(function(p) { p.flip(); });
    return csg;
  },
  
  // Affine transformation of CSG object. Returns a new CSG object
  transform: function(matrix4x4) {
    var newpolygons = this.polygons.map(function(p) { return p.transform(matrix4x4); } );
    return CSG.fromPolygons(newpolygons);  
  },
  
  translate: function(v) {
    return this.transform(CSG.Matrix4x4.translation(v));
  },
  
  scale: function(f) {
    return this.transform(CSG.Matrix4x4.scaling(f));
  },
  
  rotateX: function(deg) {
    return this.transform(CSG.Matrix4x4.rotationX(deg));
  },
  
  rotateY: function(deg) {
    return this.transform(CSG.Matrix4x4.rotationY(deg));
  },
  
  rotateZ: function(deg) {
    return this.transform(CSG.Matrix4x4.rotationZ(deg));
  },
  
  toStlString: function() {
    var result="solid csg.js\n";
    this.polygons.map(function(p){ result += p.toStlString(); });
    result += "endsolid csg.js\n";
    return result;
  }
};

// Construct an axis-aligned solid cuboid. Optional parameters are `center` and
// `radius`, which default to `[0, 0, 0]` and `[1, 1, 1]`. The radius can be
// specified using a single number or a list of three numbers, one for each axis.
// 
// Example code:
// 
//     var cube = CSG.cube({
//       center: [0, 0, 0],
//       radius: 1
//     });
CSG.cube = function(options) {
  options = options || {};
  var c = new CSG.Vector(options.center || [0, 0, 0]);
  var r = !options.radius ? [1, 1, 1] : options.radius.length ?
           options.radius : [options.radius, options.radius, options.radius];
  return CSG.fromPolygons([
    [[0, 4, 6, 2], [-1, 0, 0]],
    [[1, 3, 7, 5], [+1, 0, 0]],
    [[0, 1, 5, 4], [0, -1, 0]],
    [[2, 6, 7, 3], [0, +1, 0]],
    [[0, 2, 3, 1], [0, 0, -1]],
    [[4, 5, 7, 6], [0, 0, +1]]
  ].map(function(info) {
    return new CSG.Polygon(info[0].map(function(i) {
      var pos = new CSG.Vector(
        c.x + r[0] * (2 * !!(i & 1) - 1),
        c.y + r[1] * (2 * !!(i & 2) - 1),
        c.z + r[2] * (2 * !!(i & 4) - 1)
      );
      return new CSG.Vertex(pos, new CSG.Vector(info[1]));
    }));
  }));
};

// Construct a solid sphere. Optional parameters are `center`, `radius`,
// `slices`, and `stacks`, which default to `[0, 0, 0]`, `1`, `16`, and `8`.
// The `slices` and `stacks` parameters control the tessellation along the
// longitude and latitude directions.
// 
// Example usage:
// 
//     var sphere = CSG.sphere({
//       center: [0, 0, 0],
//       radius: 1,
//       slices: 16,
//       stacks: 8
//     });
CSG.sphere = function(options) {
  options = options || {};
  var c = new CSG.Vector(options.center || [0, 0, 0]);
  var r = options.radius || 1;
  var slices = options.slices || 16;
  var stacks = options.stacks || 8;
  var polygons = [], vertices;
  function vertex(theta, phi) {
    theta *= Math.PI * 2;
    phi *= Math.PI;
    var dir = new CSG.Vector(
      Math.cos(theta) * Math.sin(phi),
      Math.cos(phi),
      Math.sin(theta) * Math.sin(phi)
    );
    vertices.push(new CSG.Vertex(c.plus(dir.times(r)), dir));
  }
  for (var i = 0; i < slices; i++) {
    for (var j = 0; j < stacks; j++) {
      vertices = [];
      vertex(i / slices, j / stacks);
      if (j > 0) vertex((i + 1) / slices, j / stacks);
      if (j < stacks - 1) vertex((i + 1) / slices, (j + 1) / stacks);
      vertex(i / slices, (j + 1) / stacks);
      polygons.push(new CSG.Polygon(vertices));
    }
  }
  return CSG.fromPolygons(polygons);
};

// Construct a solid cylinder. Optional parameters are `start`, `end`,
// `radius`, and `slices`, which default to `[0, -1, 0]`, `[0, 1, 0]`, `1`, and
// `16`. The `slices` parameter controls the tessellation.
// 
// Example usage:
// 
//     var cylinder = CSG.cylinder({
//       start: [0, -1, 0],
//       end: [0, 1, 0],
//       radius: 1,
//       slices: 16
//     });
CSG.cylinder = function(options) {
  options = options || {};
  var s = new CSG.Vector(options.start || [0, -1, 0]);
  var e = new CSG.Vector(options.end || [0, 1, 0]);
  var ray = e.minus(s);
  var r = options.radius || 1;
  var slices = options.slices || 16;
  var axisZ = ray.unit(), isY = (Math.abs(axisZ.y) > 0.5);
  var axisX = new CSG.Vector(isY, !isY, 0).cross(axisZ).unit();
  var axisY = axisX.cross(axisZ).unit();
  var start = new CSG.Vertex(s, axisZ.negated());
  var end = new CSG.Vertex(e, axisZ.unit());
  var polygons = [];
  function point(stack, slice, normalBlend) {
    var angle = slice * Math.PI * 2;
    var out = axisX.times(Math.cos(angle)).plus(axisY.times(Math.sin(angle)));
    var pos = s.plus(ray.times(stack)).plus(out.times(r));
    var normal = out.times(1 - Math.abs(normalBlend)).plus(axisZ.times(normalBlend));
    return new CSG.Vertex(pos, normal);
  }
  for (var i = 0; i < slices; i++) {
    var t0 = i / slices, t1 = (i + 1) / slices;
    polygons.push(new CSG.Polygon([start, point(0, t0, -1), point(0, t1, -1)]));
    polygons.push(new CSG.Polygon([point(0, t1, 0), point(0, t0, 0), point(1, t0, 0), point(1, t1, 0)]));
    polygons.push(new CSG.Polygon([end, point(1, t1, 1), point(1, t0, 1)]));
  }
  return CSG.fromPolygons(polygons);
};

// # class Vector

// Represents a 3D vector.
// 
// Example usage:
// 
//     new CSG.Vector(1, 2, 3);
//     new CSG.Vector([1, 2, 3]);
//     new CSG.Vector({ x: 1, y: 2, z: 3 });

CSG.Vector = function(x, y, z) {
  if (arguments.length == 3) {
    this.x = x;
    this.y = y;
    this.z = z;
  } else if ('x' in x) {
    this.x = x.x;
    this.y = x.y;
    this.z = x.z;
  } else {
    this.x = x[0];
    this.y = x[1];
    this.z = x[2];
  }
};

CSG.Vector.prototype = {
  clone: function() {
    return new CSG.Vector(this.x, this.y, this.z);
  },

  negated: function() {
    return new CSG.Vector(-this.x, -this.y, -this.z);
  },

  plus: function(a) {
    return new CSG.Vector(this.x + a.x, this.y + a.y, this.z + a.z);
  },

  minus: function(a) {
    return new CSG.Vector(this.x - a.x, this.y - a.y, this.z - a.z);
  },

  times: function(a) {
    return new CSG.Vector(this.x * a, this.y * a, this.z * a);
  },

  dividedBy: function(a) {
    return new CSG.Vector(this.x / a, this.y / a, this.z / a);
  },

  dot: function(a) {
    return this.x * a.x + this.y * a.y + this.z * a.z;
  },

  lerp: function(a, t) {
    return this.plus(a.minus(this).times(t));
  },

  length: function() {
    return Math.sqrt(this.dot(this));
  },

  unit: function() {
    return this.dividedBy(this.length());
  },

  cross: function(a) {
    return new CSG.Vector(
      this.y * a.z - this.z * a.y,
      this.z * a.x - this.x * a.z,
      this.x * a.y - this.y * a.x
    );
  },
  
  // Right multiply by a 4x4 matrix (the vector is interpreted as a row vector)
  // Returns a new CSG.Vector
  multiply4x4: function(matrix4x4) {
    return matrix4x4.rightMultiply1x3Vector(this);
  },
  
  toStlString: function() {
    return this.x+" "+this.y+" "+this.z;
  }
};

// # class Vertex

// Represents a vertex of a polygon. Use your own vertex class instead of this
// one to provide additional features like texture coordinates and vertex
// colors. Custom vertex classes need to provide a `pos` property and `clone()`,
// `flip()`, and `interpolate()` methods that behave analogous to the ones
// defined by `CSG.Vertex`. This class provides `normal` so convenience
// functions like `CSG.sphere()` can return a smooth vertex normal, but `normal`
// is not used anywhere else.

CSG.Vertex = function(pos, normal) {
  this.pos = new CSG.Vector(pos);
  this.normal = new CSG.Vector(normal);
};

CSG.Vertex.prototype = {
  clone: function() {
    return new CSG.Vertex(this.pos.clone(), this.normal.clone());
  },

  // Invert all orientation-specific data (e.g. vertex normal). Called when the
  // orientation of a polygon is flipped.
  flip: function() {
    this.normal = this.normal.negated();
  },

  // Create a new vertex between this vertex and `other` by linearly
  // interpolating all properties using a parameter of `t`. Subclasses should
  // override this to interpolate additional properties.
  interpolate: function(other, t) {
    return new CSG.Vertex(
      this.pos.lerp(other.pos, t),
      this.normal.lerp(other.normal, t)
    );
  },
  
  // Affine transformation of vertex. Returns a new CSG.Vertex
  transform: function(matrix4x4) {
    var newpos = this.pos.multiply4x4(matrix4x4);
    var posPlusNormal = this.pos.plus(this.normal);
    var newPosPlusNormal = posPlusNormal.multiply4x4(matrix4x4);
    var newnormal = newPosPlusNormal.minus(newpos).unit();
    return new CSG.Vertex(newpos, newnormal);  
  },
  
  toStlString: function() {
    return "vertex "+this.pos.toStlString()+"\n";
  }
};

// # class Plane

// Represents a plane in 3D space.

CSG.Plane = function(normal, w) {
  this.normal = normal;
  this.w = w; // the distance from the origin along the normal

  
};

// `CSG.Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a
// point is on the plane.
CSG.Plane.EPSILON = 1e-5;

CSG.Plane.fromPoints = function(a, b, c) {
  var n = b.minus(a).cross(c.minus(a)).unit();
  return new CSG.Plane(n, n.dot(a));
};

CSG.Plane.prototype = {
  clone: function() {
    return new CSG.Plane(this.normal.clone(), this.w);
  },

  flip: function() {
    this.normal = this.normal.negated();
    this.w = -this.w;
  },

  // Split `polygon` by this plane if needed, then put the polygon or polygon
  // fragments in the appropriate lists. Coplanar polygons go into either
  // `coplanarFront` or `coplanarBack` depending on their orientation with
  // respect to this plane. Polygons in front or in back of this plane go into
  // either `front` or `back`.
  splitPolygon: function(polygon, coplanarFront, coplanarBack, front, back) {
    var COPLANAR = 0;
    var FRONT = 1;
    var BACK = 2;
    var SPANNING = 3;

    // Classify each point as well as the entire polygon into one of the above
    // four classes.
    var polygonType = 0;
    var types = [];
    for (var i = 0; i < polygon.vertices.length; i++) {
      var t = this.normal.dot(polygon.vertices[i].pos) - this.w;
      var type = (t < -CSG.Plane.EPSILON) ? BACK : (t > CSG.Plane.EPSILON) ? FRONT : COPLANAR;
      polygonType |= type;
      types.push(type);
    }

    // Put the polygon in the correct list, splitting it when necessary.
    switch (polygonType) {
      case COPLANAR:
        (this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon);
        break;
      case FRONT:
        front.push(polygon);
        break;
      case BACK:
        back.push(polygon);
        break;
      case SPANNING:
        var f = [], b = [];
        for (var i = 0; i < polygon.vertices.length; i++) {
          var j = (i + 1) % polygon.vertices.length;
          var ti = types[i], tj = types[j];
          var vi = polygon.vertices[i], vj = polygon.vertices[j];
          if (ti != BACK) f.push(vi);
          if (ti != FRONT) b.push(ti != BACK ? vi.clone() : vi);
          if ((ti | tj) == SPANNING) {
            var t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(vj.pos.minus(vi.pos));
            var v = vi.interpolate(vj, t);
            f.push(v);
            b.push(v.clone());
          }
        }
        if (f.length >= 3) front.push(new CSG.Polygon(f, polygon.shared));
        if (b.length >= 3) back.push(new CSG.Polygon(b, polygon.shared));
        break;
    }
  }
};

// # class Polygon

// Represents a convex polygon. The vertices used to initialize a polygon must
// be coplanar and form a convex loop. They do not have to be `CSG.Vertex`
// instances but they must behave similarly (duck typing can be used for
// customization).
// 
// Each convex polygon has a `shared` property, which is shared between all
// polygons that are clones of each other or were split from the same polygon.
// This can be used to define per-polygon properties (such as surface color).

CSG.Polygon = function(vertices, shared) {
  this.vertices = vertices;
  this.shared = shared;
  this.plane = CSG.Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos);
};

CSG.Polygon.prototype = {
  clone: function() {
    var vertices = this.vertices.map(function(v) { return v.clone(); });
    return new CSG.Polygon(vertices, this.shared);
  },

  flip: function() {
    this.vertices.reverse().map(function(v) { v.flip(); });
    this.plane.flip();
  },
  
  // Affine transformation of polygon. Returns a new CSG.Polygon
  transform: function(matrix4x4) {
    var newvertices = this.vertices.map(function(v) { return v.transform(matrix4x4); } );
    return new CSG.Polygon(newvertices, this.shared);
  },

  translate: function(matrix4x4) {
    var newvertices = this.vertices.map(function(v) { return v.transform(matrix4x4); } );
    return new CSG.Polygon(newvertices, this.shared);
  },

  scale: function(matrix4x4) {
    var newvertices = this.vertices.map(function(v) { return v.transform(matrix4x4); } );
    return new CSG.Polygon(newvertices, this.shared);
  },
  
  toStlString: function() {
    var result="";
    if(this.vertices.length >= 3) // should be!
    {
      // SLT requires triangular polygons. If our polygon has more vertices, create
      // multiple triangles:
      var firstVertexStl = this.vertices[0].toStlString();
      for(var i=0; i < this.vertices.length-2; i++)
      {
        result += "facet normal "+this.plane.normal.toStlString()+"\nouter loop\n";
        result += firstVertexStl;
        result += this.vertices[i+1].toStlString();
        result += this.vertices[i+2].toStlString();
        result += "endloop\nendfacet\n";    
      } 
    }
    return result;
  }
};

// Create a polygon from the given points
CSG.Polygon.createFromPoints = function(points, shared) {
  // initially set a dummy vertex normal:
  var dummynormal = new CSG.Vector(0, 0, 0);
  var vertices = [];
  points.map( function(p) {
    var vec = new CSG.Vector(p);
    var vertex = new CSG.Vertex(vec, dummynormal);
    vertices.push(vertex); 
  });            
  var polygon = new CSG.Polygon(vertices, shared);
  // now set the vertex normals to the polygon normal:
  var normal = polygon.plane.normal;
  polygon.vertices.map( function(v) {
    v.normal = normal;
  });
  return polygon;
};

// # class Node

// Holds a node in a BSP tree. A BSP tree is built from a collection of polygons
// by picking a polygon to split along. That polygon (and all other coplanar
// polygons) are added directly to that node and the other polygons are added to
// the front and/or back subtrees. This is not a leafy BSP tree since there is
// no distinction between internal and leaf nodes.

CSG.Node = function(polygons) {
  this.plane = null;
  this.front = null;
  this.back = null;
  this.polygons = [];
  if (polygons) this.build(polygons);
};

CSG.Node.prototype = {
  clone: function() {
    var node = new CSG.Node();
    node.plane = this.plane && this.plane.clone();
    node.front = this.front && this.front.clone();
    node.back = this.back && this.back.clone();
    node.polygons = this.polygons.map(function(p) { return p.clone(); });
    return node;
  },

  // Convert solid space to empty space and empty space to solid space.
  invert: function() {
    for (var i = 0; i < this.polygons.length; i++) {
      this.polygons[i].flip();
    }
    this.plane.flip();
    if (this.front) this.front.invert();
    if (this.back) this.back.invert();
    var temp = this.front;
    this.front = this.back;
    this.back = temp;
  },

  // Recursively remove all polygons in `polygons` that are inside this BSP
  // tree.
  clipPolygons: function(polygons) {
    if (!this.plane) return polygons.slice();
    var front = [], back = [];
    for (var i = 0; i < polygons.length; i++) {
      this.plane.splitPolygon(polygons[i], front, back, front, back);
    }
    if (this.front) front = this.front.clipPolygons(front);
    if (this.back) back = this.back.clipPolygons(back);
    else back = [];
    return front.concat(back);
  },

  // Remove all polygons in this BSP tree that are inside the other BSP tree
  // `bsp`.
  clipTo: function(bsp) {
    this.polygons = bsp.clipPolygons(this.polygons);
    if (this.front) this.front.clipTo(bsp);
    if (this.back) this.back.clipTo(bsp);
  },

  // Return a list of all polygons in this BSP tree.
  allPolygons: function() {
    var polygons = this.polygons.slice();
    if (this.front) polygons = polygons.concat(this.front.allPolygons());
    if (this.back) polygons = polygons.concat(this.back.allPolygons());
    return polygons;
  },

  // Build a BSP tree out of `polygons`. When called on an existing tree, the
  // new polygons are filtered down to the bottom of the tree and become new
  // nodes there. Each set of polygons is partitioned using the first polygon
  // (no heuristic is used to pick a good split).
  build: function(polygons) {
    if (!polygons.length) return;
    if (!this.plane) this.plane = polygons[0].plane.clone();
    var front = [], back = [];
    for (var i = 0; i < polygons.length; i++) {
      this.plane.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
    }
    if (front.length) {
      if (!this.front) this.front = new CSG.Node();
      this.front.build(front);
    }
    if (back.length) {
      if (!this.back) this.back = new CSG.Node();
      this.back.build(back);
    }
  }
};

// # class Matrix4x4:
// Represents a 4x4 matrix. Elements are specified in row order
CSG.Matrix4x4 = function(elements) {
  if (arguments.length >= 1) {
    this.elements=elements;
  }
  else
  {
    // if no arguments passed: create unity matrix  
    this.elements=[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
  }
}

CSG.Matrix4x4.prototype = {
  plus: function(m) {
    var r=[];
    for(var i=0; i < 16; i++)
    {
      r[i]=this.elements[i]+m.elements[i];
    }
    return new CSG.Matrix4x4(r);
  },
  
  minus: function(m) {
    var r=[];
    for(var i=0; i < 16; i++)
    {
      r[i]=this.elements[i]-m.elements[i];
    }
    return new CSG.Matrix4x4(r);
  },

  // right multiply by another 4x4 matrix:
  multiply: function(m) {
    // cache elements in local variables, for speedup:
    var this0=this.elements[0];
    var this1=this.elements[1];
    var this2=this.elements[2];
    var this3=this.elements[3];
    var this4=this.elements[4];
    var this5=this.elements[5];
    var this6=this.elements[6];
    var this7=this.elements[7];
    var this8=this.elements[8];
    var this9=this.elements[9];
    var this10=this.elements[10];
    var this11=this.elements[11];
    var this12=this.elements[12];
    var this13=this.elements[13];
    var this14=this.elements[14];
    var this15=this.elements[15];
    var m0=m.elements[0];
    var m1=m.elements[1];
    var m2=m.elements[2];
    var m3=m.elements[3];
    var m4=m.elements[4];
    var m5=m.elements[5];
    var m6=m.elements[6];
    var m7=m.elements[7];
    var m8=m.elements[8];
    var m9=m.elements[9];
    var m10=m.elements[10];
    var m11=m.elements[11];
    var m12=m.elements[12];
    var m13=m.elements[13];
    var m14=m.elements[14];
    var m15=m.elements[15];
    
    var result=[];
    result[0] = this0*m0 + this1*m4 + this2*m8 + this3*m12;
    result[1] = this0*m1 + this1*m5 + this2*m9 + this3*m13;
    result[2] = this0*m2 + this1*m6 + this2*m10 + this3*m14;
    result[3] = this0*m3 + this1*m7 + this2*m11 + this3*m15;
    result[4] = this4*m0 + this5*m4 + this6*m8 + this7*m12;
    result[5] = this4*m1 + this5*m5 + this6*m9 + this7*m13;
    result[6] = this4*m2 + this5*m6 + this6*m10 + this7*m14;
    result[7] = this4*m3 + this5*m7 + this6*m11 + this7*m15;
    result[8] = this8*m0 + this9*m4 + this10*m8 + this11*m12;
    result[9] = this8*m1 + this9*m5 + this10*m9 + this11*m13;
    result[10] = this8*m2 + this9*m6 + this10*m10 + this11*m14;
    result[11] = this8*m3 + this9*m7 + this10*m11 + this11*m15;
    result[12] = this12*m0 + this13*m4 + this14*m8 + this15*m12;
    result[13] = this12*m1 + this13*m5 + this14*m9 + this15*m13;
    result[14] = this12*m2 + this13*m6 + this14*m10 + this15*m14;
    result[15] = this12*m3 + this13*m7 + this14*m11 + this15*m15;
    return new CSG.Matrix4x4(result);
  },
  
  clone: function() {
    var elements = this.elements.map(function(p) { return p; }); 
    return new CSG.Matrix4x4(elements);
  },
  
  // Multiply a CSG.Vector (interpreted as 1 row, 3 column) by this matrix 
  // Fourth element is taken as 1
  rightMultiply1x3Vector: function(v) {
    var v0 = v.x;
    var v1 = v.y;
    var v2 = v.z;
    var v3 = 1;    
    var x = v0*this.elements[0] + v1*this.elements[1] + v2*this.elements[2] + v3*this.elements[3];    
    var y = v0*this.elements[4] + v1*this.elements[5] + v2*this.elements[6] + v3*this.elements[7];    
    var z = v0*this.elements[8] + v1*this.elements[9] + v2*this.elements[10] + v3*this.elements[11];    
    var w = v0*this.elements[12] + v1*this.elements[13] + v2*this.elements[14] + v3*this.elements[15];
    // scale such that fourth element becomes 1:
    if(w != 1)
    {
      var invw=1.0/w;
      x *= invw;
      y *= invw;
      z *= invw;
    }
    return new CSG.Vector(x,y,z);       
  },
  
  // Multiply a CSG.Vector2D (interpreted as 1 row, 2 column) by this matrix 
  // Fourth element is taken as 1
  rightMultiply1x2Vector: function(v) {
    var v0 = v.x;
    var v1 = v.y;
    var v2 = 0;
    var v3 = 1;    
    var x = v0*this.elements[0] + v1*this.elements[1] + v2*this.elements[2] + v3*this.elements[3];    
    var y = v0*this.elements[4] + v1*this.elements[5] + v2*this.elements[6] + v3*this.elements[7];    
    var z = v0*this.elements[8] + v1*this.elements[9] + v2*this.elements[10] + v3*this.elements[11];    
    var w = v0*this.elements[12] + v1*this.elements[13] + v2*this.elements[14] + v3*this.elements[15];
    // scale such that fourth element becomes 1:
    if(w != 1)
    {
      var invw=1.0/w;
      x *= invw;
      y *= invw;
      z *= invw;
    }
    return new CSG.Vector2D(x,y);       
  },
};

// return the unity matrix
CSG.Matrix4x4.unity = function() {
  return new CSG.Matrix4x4(); 
};

// Create a rotation matrix for rotating around the x axis
CSG.Matrix4x4.rotationX = function(degrees) {
  var radians = degrees * Math.PI * (1.0/180.0);
  var cos = Math.cos(radians);
  var sin = Math.sin(radians);
  var els = [
    1, 0, 0, 0,
    0, cos, -sin, 0,
    0, sin, cos, 0,
    0, 0, 0, 1
  ];
  return new CSG.Matrix4x4(els);
};

// Create a rotation matrix for rotating around the y axis
CSG.Matrix4x4.rotationY = function(degrees) {
  var radians = degrees * Math.PI * (1.0/180.0);
  var cos = Math.cos(radians);
  var sin = Math.sin(radians);
  var els = [
    cos, 0, sin, 0,
    0, 1, 0, 0,
    -sin, 0, cos, 0,
    0, 0, 0, 1
  ];
  return new CSG.Matrix4x4(els);
};

// Create a rotation matrix for rotating around the z axis
CSG.Matrix4x4.rotationZ = function(degrees) {
  var radians = degrees * Math.PI * (1.0/180.0);
  var cos = Math.cos(radians);
  var sin = Math.sin(radians);
  var els = [
    cos, -sin, 0, 0,
    sin, cos, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1
  ];
  return new CSG.Matrix4x4(els);
};

// Create an affine matrix for translation:
CSG.Matrix4x4.translation = function(v) {
  // parse as CSG.Vector, so we can pass an array or a CSG.Vector
  var vec = new CSG.Vector(v);
  var els = [
    1, 0, 0, vec.x,
    0, 1, 0, vec.y,
    0, 0, 1, vec.z,
    0, 0, 0, 1
  ];
  return new CSG.Matrix4x4(els);
};

// Create an affine matrix for scaling:
CSG.Matrix4x4.scaling = function(v) {
  // parse as CSG.Vector, so we can pass an array or a CSG.Vector
  var vec = new CSG.Vector(v);
  var els = [
    vec.x, 0, 0, 0,
    0, vec.y, 0, 0,
    0, 0, vec.z, 0,
    0, 0, 0, 1
  ];
  return new CSG.Matrix4x4(els);
};

///////////////////////////////////////////////////

// # class Vector2D:
// Represents a 2 element vector
CSG.Vector2D = function(x, y) {
  if (arguments.length == 2) {
    this.x = x;
    this.y = y;
  } else if ('x' in x) {
    this.x = x.x;
    this.y = x.y;
  } else {
    this.x = x[0];
    this.y = x[1];
  }
};

CSG.Vector2D.prototype = {
  // extend to a 3D vector by adding a z coordinate:
  toVector3D: function(z) {
    return new CSG.Vector(this.x, this.y, z);
  },
  
  clone: function() {
    return new CSG.Vector(this.x, this.y);
  },

  negated: function() {
    return new CSG.Vector(-this.x, -this.y);
  },

  plus: function(a) {
    return new CSG.Vector(this.x + a.x, this.y + a.y);
  },

  minus: function(a) {
    return new CSG.Vector(this.x - a.x, this.y - a.y);
  },

  times: function(a) {
    return new CSG.Vector(this.x * a, this.y * a);
  },

  dividedBy: function(a) {
    return new CSG.Vector(this.x / a, this.y / a);
  },

  dot: function(a) {
    return this.x * a.x + this.y * a.y;
  },

  lerp: function(a, t) {
    return this.plus(a.minus(this).times(t));
  },

  length: function() {
    return Math.sqrt(this.dot(this));
  },

  unit: function() {
    return this.dividedBy(this.length());
  },

  // Right multiply by a 4x4 matrix (the vector is interpreted as a row vector)
  // Returns a new CSG.Vector2D
  multiply4x4: function(matrix4x4) {
    return matrix4x4.rightMultiply1x2Vector(this);
  },
};


// A polygon in 2D space:
CSG.Polygon2D = function(points, shared) {
  var vectors = [];
  if(arguments.length >= 1) {
    points.map( function(p) {
      vectors.push(new CSG.Vector2D(p) );
    });    
  }
  this.points = vectors;
  this.shared = shared;
};

CSG.Polygon2D.prototype = {
  // Matrix transformation of polygon. Returns a new CSG.Polygon2D
  transform: function(matrix4x4) {
    var newpoints = this.points.map(function(p) { return p.multiply4x4(matrix4x4); } );
    return new CSG.Polygon2D(newpoints, this.shared);
  },
  
  translate: function(v) {
    v=new CSG.Vector2D(v);
    return this.transform(CSG.Matrix4x4.translation(v.toVector3D(0)));
  },
  
  scale: function(f) {
    f=new CSG.Vector2D(f);
    return this.transform(CSG.Matrix4x4.scaling(f.toVector3D(1)));
  },
  
  rotate: function(deg) {
    return this.transform(CSG.Matrix4x4.rotationZ(deg));
  },    
  
  // convert into a CSG.Polygon; set z coordinate to the given value
  toPolygon3D: function(z) {
    var points3d=[];
    this.points.map( function(p) {
      var vec3d = p.toVector3D(z);      
      points3d.push(vec3d);
    });
    return CSG.Polygon.createFromPoints(points3d, this.shared);
  },
  
  // extruded=shape2d.extrude({offset: [0,0,10], twistangle: 360, twiststeps: 100});
  // linear extrusion of 2D polygon, with optional twist
  // The 2d polygon is placed in in z=0 plane and extruded into direction <offset> (a CSG.Vector)
  // The final face is rotated <twistangle> degrees. Rotation is done around the origin of the 2d shape (i.e. x=0, y=0)
  // twiststeps determines the resolution of the twist (should be >= 1)  
  // returns a CSG object
  extrude: function(params) {
    // parse parameters:
    if(!params) params={};
    var offsetvector;
    if("offset" in params)
    {
      offsetvector = new CSG.Vector(params.offset); // reparse as a CSG.Vector
    }
    else
    {
      offsetvector = new CSG.Vector(0,0,1);
    }
    
    var twistangle=0;
    if("twistangle" in params)
    {
      twistangle = params.twistangle;
    }
    
    var twiststeps=10;
    if("twiststeps" in params)
    {
      twiststeps = params.twiststeps;
    }
    if(twistangle == 0) twiststeps=1;
    if(twiststeps < 1) twiststeps=1;

    // create the polygons:        
    var newpolygons = [];
    
    // bottom face polygon:
    var bottomfacepolygon = this.toPolygon3D(0);
    var direction = bottomfacepolygon.plane.normal.dot(offsetvector);
    if(direction > 0)
    {
      bottomfacepolygon.flip();
    }
    newpolygons.push(bottomfacepolygon);
    
    var getTwistedPolygon = function(twiststep) {

      var fraction = (twiststep + 1) / twiststeps;
      var rotation = twistangle * fraction;
      var offset = offsetvector.times(fraction);
      var transformmatrix = CSG.Matrix4x4.rotationZ(rotation).multiply( CSG.Matrix4x4.translation(offset) );
      var polygon = bottomfacepolygon.transform(transformmatrix);      
      return polygon;

    };

    // create the side face polygons:
    var numvertices = bottomfacepolygon.vertices.length;
    var prevlevelpolygon = bottomfacepolygon;
    for(var twiststep=0; twiststep < twiststeps; ++twiststep)
    {
      var levelpolygon = getTwistedPolygon(twiststep);
      for(var i=0; i < numvertices; i++)
      {
        var sidefacepoints = [];
        var nexti = (i < (numvertices-1))? i+1:0;
        sidefacepoints.push(prevlevelpolygon.vertices[i].pos);
        sidefacepoints.push(levelpolygon.vertices[i].pos);
        sidefacepoints.push(levelpolygon.vertices[nexti].pos);
        sidefacepoints.push(prevlevelpolygon.vertices[nexti].pos);
        var sidefacepolygon=CSG.Polygon.createFromPoints(sidefacepoints, this.shared);
        newpolygons.push(sidefacepolygon);
      }
      if(twiststep == (twiststeps -1) )
      {
        // last level; add the top face polygon:
        levelpolygon.flip(); // flip so that the normal points outwards
        newpolygons.push(levelpolygon);
      }
      prevlevelpolygon = levelpolygon;
    }

    return CSG.fromPolygons(newpolygons);
  }
};


================================================
FILE: app/scripts/lib/flood/flood.js
================================================
if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

// Webworker context
if (typeof require != 'function' && typeof window != "object") { 

	var FLOOD = {};
	var define = function(x, y){
		if (typeof x === "function") FLOOD = x();
		if (typeof y === "function") FLOOD = y();
	};

}

define('FLOOD',function() {

	// initialize core types
	if (!FLOOD) var FLOOD = {};

	FLOOD.baseTypes = {};
	FLOOD.nodeTypes = {};
	FLOOD.internalNodeTypes = {};

	if (typeof Object.create !== 'function') {
	    Object.create = function (o) {
	        function F() {}
	        F.prototype = o;
	        return new F();
	    };
	}

	// partial function application
	Function.prototype.curry = function() {
    var fn = this, args = Array.prototype.slice.call(arguments);

    return function() {
      return fn.apply(this, args.concat(
        Array.prototype.slice.call(arguments)));
    };
  };

  Function.prototype.partial = function(){
    var fn = this, args = Array.prototype.slice.call(arguments);
    return function(){
      var arg = 0;
      for ( var i = 0; i < args.length && arg < arguments.length; i++ )
        if ( args[i] === undefined )
          args[i] = arguments[arg++];
      return fn.apply(this, args);
    };
  };

	Array.prototype.remove = function(from, to) {
	  var rest = this.slice((to || from) + 1 || this.length);
	  this.length = from < 0 ? this.length + from : from;
	  return this.push.apply(this, rest);
	};

	Array.prototype.last = function() {
	    return this[this.length-1];
	}

	Function.prototype.method = function (name, func) {
	    this.prototype[name] = func;
	    return this;
	};

	Function.method('inherits', function (parent) {
	    this.prototype = new parent();
	    var d = {}, 
	        p = this.prototype;
	    this.prototype.constructor = parent; 
	    return this;
	});

	// MultiOutResult
	// ===========
	//

	function MultiOutResult(object){
		for (var i in object){
			this[i] = object[i];
		}
	};
	MultiOutResult.prototype = new Object();

	MultiOutResult.wrap = function(){

		if (arguments.length === 0) return undefined;

		return arguments.length === 1 ? arguments[0] : 
			new FLOOD.MultiOutResult( arguments );

	};

	MultiOutResult.prototype.constructor = MultiOutResult;
	FLOOD.MultiOutResult = MultiOutResult;

	// QuotedArray
	// ===========
	// QuotedArray is a slight modification of Array.  It makes it 
	// clear to the interpreter whether we're intending to pass
	// an expression Array or an Array as a value.  The interpreter checks
	// the constructor function in order to check the type.

	function QuotedArray(){
		Array.apply(this, arguments);
	};

	QuotedArray.prototype = new Array();
	QuotedArray.prototype.toString = function(){
		if (this.length === 0) return "[]";
		var eles = []; 
		for (var i = 0; i < this.length; i++){
			eles.push( this[i] );
		}
		return "[ " + eles.join(", ") + " ]"
	};

	Array.prototype.flatten = function(){

		if (this.length === 0) return [];

		if ( isObjectTypeMatch( this[0], Array) ){
			return this[0].flatten().concat( this.slice(1).flatten() );
		} 

		return [ this[0] ].concat( this.slice(1).flatten() );
	};

	Array.prototype.toQuotedArray = function(){

		var qa = new QuotedArray();
		for (var i = 0; i < this.length; i++){
			qa.push( this[i].toQuotedArray ? this[i].toQuotedArray() : this[i] );
		}
		return qa;
	};

	QuotedArray.prototype.constructor = QuotedArray;
	FLOOD.QuotedArray = QuotedArray;

	FLOOD.baseTypes.NodeType = function(options) {

		var that = this;
		var options = options || {};
		this.replication = options.replication || "applyLongest";

		// tell the inputs about their parent node & index
		if (options.inputs) {
			options.inputs.forEach( function(e, i) {
				e.parentNode = that;
				e.parentIndex = i;
			});

			this.inputs = options.inputs;
		} else {
			this.inputs = [];
		}

		// tell the outputs about their parent node & index
		if (options.outputs) {
			options.outputs.forEach( function(e, i) {
				e.parentNode = that;
				e.parentIndex = i;
			});

			this.outputs = options.outputs;
		} else {
			this.outputs = [];
		}

		this.typeName = options.typeName || "noTypeName";

		var _isDirty = true;

		this.extend = function(args){

		}

		this.alwaysDirty = false;
		this.doPostProcess = true;

		this.isDirty = function() {
			return this.alwaysDirty ? true : _isDirty;
		};

		this.markClean = function() {
			_isDirty = false;
		};

		this.setDirty = function() {
			_isDirty = true;
		};

		this.inputTypes = function(){
			return this.inputs.map(function(x){ return x.type; });
		};

		this.getIndexOfInputNode = function( otherNode ){
			for (var i = 0; i < this.inputs.length; i++){
				if ( this.inputs[i].isConnectedTo( otherNode ) ) return i;
			}

			return -1;
		};

		this.markDirty = function() {

			_isDirty = this.inputs.reduce(function(m, n){ 

				// there's nothing connected to this port
				if ( !n.oppNode ){
					return m;
				}

				return n.oppNode.markDirty() || m; 

			}, false) || _isDirty;
			return _isDirty;

		};

		this.printExpression = function() { 
			return "(" + this.typeName + " " + this.inputs.map(function(n){ return n.printExpression(); }).join(' ') + ")";					
		};

		this.addInputPort = function(name, type, defaultVal){
			this.inputs.push( new FLOOD.baseTypes.InputPort( name, type, defaultVal, this, this.inputs.length ) );
		}

		this.addOutputPort = function(name, type){
			this.outputs.push( new FLOOD.baseTypes.OutputPort( name, type, this, this.outputs.length ) );
		}

		this.evalComplete = function() {};
		this.evalFailed = function() {};
		this.evalBegin = function() {};

		this.compile = function() { 
			
			var that = this;

			var partialEvalClosure = (function() { 

					// if we have enough args, eval, otherwise return function
					return function() {

						var dirty = that.isDirty();

						try {

							that.evalBegin(that, dirty);

							if ( dirty ){

								// if any argument is undefined, perform partial function application
								var noUndefinedArgs = true;
								for (var i = 0; i < arguments.length; i++){
									if ( arguments[i] === undefined ){
										noUndefinedArgs = false;
										break;
									}
								}

								if (noUndefinedArgs){ 
									
									// build replication options and types
									var options = {
										replication: that.replication,
										expected_arg_types: that.inputTypes()
									};

									try {
										// actually evaluate the function!
										that.value = that.eval.mapApply(that, Array.prototype.slice.call(arguments, 0), options);
									} catch (e) {
										that.value = null;
										that.evalFailed(that, e);
									}

								} else { 
									// return a partial function application
									var originalArgs = arguments;
									that.value = (function(){
										// return a closure
										return function(){
											return that.eval.partial.apply(that.eval, originalArgs).apply(that, arguments);
										}
									})();
								}
								
								that.markClean();

								if ( that.doPostProcess && that.postProcess ){
									that.prettyValue = that.postProcess( that.value );
								}
							}

							// tell listeners that the evalation is complete
							that.evalComplete( that, arguments, dirty, that.value, that.prettyValue );

						} catch (e) {
							that.evalFailed(that, e);
							return null;
						}

						// yield the value
						return that.value;
					};

			})();

			// return an s-expression, represented by the function to execute, and the list
			// of arguments to apply to it
			return [partialEvalClosure].concat( this.inputs.map(function(input){
				return input.compile();
			}));
		
		};

	}

	FLOOD.baseTypes.NodePort = function(name, type, parentNode, parentIndex, oppNode, oppIndex) {
		
		function constructPortTypeName(type) { 
			if (!type) return;
			if (type instanceof Array) return type.map( constructPortTypeName ).join(" of ");
			if (type.floodTypeName) return type.floodTypeName;

			var funcNameRegex = /function (.{1,})\(/;
			var results = (funcNameRegex).exec(type.toString());
			return (results && results.length > 1) ? results[1] : "";
		};

		this.name = name;
		this.type = type;
		this.typeName = constructPortTypeName( type );
		this.parentNode = parentNode;
		this.parentIndex = parentIndex != undefined ? parentIndex : 0;
		this.oppNode = oppNode;
		this.oppIndex = oppIndex != undefined ? oppIndex : 0;

	};

	FLOOD.baseTypes.OutputPort = function(name, type, parentNode, parentIndex, oppNode, oppIndex) {
		
		FLOOD.baseTypes.NodePort.call(this, name, type, parentNode, parentIndex, oppNode, oppIndex );

		this.value = function(){
			if ( !(this.parentNode.value instanceof Array) )
				return this.parentNode.value;
			return this.parentNode.value[this.parentIndex];
		};

		this.asInputPort = function(parentNode, parentIndex, defaultVal) {
			return new FLOOD.baseTypes.InputPort( this.name, this.type, defaultVal, parentNode, parentIndex );
		}

	}.inherits( FLOOD.baseTypes.NodePort );

	FLOOD.baseTypes.InputPort = function(name, type, defaultVal, parentNode, parentIndex, oppNode, oppIndex) {

		FLOOD.baseTypes.NodePort.call(this, name, type, parentNode, parentIndex, oppNode, oppIndex );
		this.defaultVal = defaultVal;
		this.useDefault = defaultVal === undefined ? false : true;

		this.printExpression = function() {
			if (this.oppNode && this.oppIndex === 0 && this.oppNode.outputs.length === 1)
				return this.oppNode.printExpression();
			if (this.oppNode)
				return '(pick ' + this.oppIndex + ' ' + this.oppNode.printExpression() + ')';
			if (this.useDefault === true )
				return this.defaultVal;
			return "_";
		}

		var autoPick = function(name, x){

			if ( isObjectTypeMatch(x, QuotedArray) ){
				return x.map( autoPick.curry( name ) );
			} else if ( isObjectTypeMatch(x, MultiOutResult ) ){
				return x[name] != undefined ? x[name] : null;
			}

			return null;
		};

		this.compile = function() {

			var val = null;

			if (this.oppNode && this.oppIndex === 0 && this.oppNode.outputs.length === 1){
				val = this.oppNode.compile();
				return val;
			} else if (this.oppNode){
				val = [ autoPick, this.oppIndex, this.oppNode.compile() ];
				return val;
			} else if (this.useDefault === true) {
				val = this.defaultVal;
				return val;
			}

			// undefined value causes partial function application
			return undefined;
		}

		this.isConnectedTo = function( otherNode ){
			return this.oppNode === otherNode;
		}

		this.connect = function(otherNode, outIndexOnOtherNode){
			this.oppNode = otherNode;
			this.oppIndex = outIndexOnOtherNode != undefined ? outIndexOnOtherNode : 0;
			this.parentNode.setDirty();
		}

		this.disconnect = function(){
			this.oppNode = null;
			this.oppIndex = 0;
			this.parentNode.setDirty();
		}

	}.inherits( FLOOD.baseTypes.NodePort );

	FLOOD.internalNodeTypes.CustomNode = function(functionName, functionId, lambda) {

		var typeData = {
			typeName: "CustomNode"
		};

		this.functionName = functionName;
		this.functionId = functionI
Download .txt
gitextract_88nis6rk/

├── .bowerrc
├── .editorconfig
├── .gitattributes
├── .gitignore
├── Gruntfile.js
├── LICENSE
├── README.md
├── app/
│   ├── .htaccess
│   ├── 404.html
│   ├── app.html
│   ├── customizer.html
│   ├── index.html
│   ├── package.json
│   ├── robots.txt
│   ├── scripts/
│   │   ├── collections/
│   │   │   ├── Connections.js
│   │   │   ├── Nodes.js
│   │   │   ├── SearchElements.js
│   │   │   ├── WorkspaceBrowserElements.js
│   │   │   └── Workspaces.js
│   │   ├── config.js
│   │   ├── customizer.js
│   │   ├── lib/
│   │   │   ├── OrbitControls.js
│   │   │   ├── Viewport.js
│   │   │   └── flood/
│   │   │       ├── async.js
│   │   │       ├── csg.js
│   │   │       ├── flood.js
│   │   │       ├── flood_csg.js
│   │   │       ├── flood_runner.js
│   │   │       ├── scheme.js
│   │   │       ├── scheme_async.js
│   │   │       └── test/
│   │   │           ├── flood_csg_test.js
│   │   │           ├── flood_lambda_test.js
│   │   │           ├── flood_runner_test.html
│   │   │           ├── flood_test.js
│   │   │           ├── scheme_async_test.html
│   │   │           └── scheme_eval_async_test.js
│   │   ├── main.js
│   │   ├── models/
│   │   │   ├── App.js
│   │   │   ├── Connection.js
│   │   │   ├── Feedback.js
│   │   │   ├── GeometryExport.js
│   │   │   ├── Help.js
│   │   │   ├── Login.js
│   │   │   ├── Marquee.js
│   │   │   ├── Node.js
│   │   │   ├── Runner.js
│   │   │   ├── Search.js
│   │   │   ├── SearchElement.js
│   │   │   ├── Share.js
│   │   │   ├── Workspace.js
│   │   │   ├── WorkspaceBrowser.js
│   │   │   ├── WorkspaceBrowserElement.js
│   │   │   ├── WorkspaceResolver.js
│   │   │   └── customizer/
│   │   │       └── CustomizerApp.js
│   │   └── views/
│   │       ├── AppView.js
│   │       ├── ConnectionView.js
│   │       ├── FeedbackView.js
│   │       ├── HelpView.js
│   │       ├── LoginView.js
│   │       ├── MarqueeView.js
│   │       ├── NodeViews/
│   │       │   ├── Base.js
│   │       │   ├── CustomNode.js
│   │       │   ├── Input.js
│   │       │   ├── NodeViews.js
│   │       │   ├── Num.js
│   │       │   ├── Output.js
│   │       │   ├── Script.js
│   │       │   ├── ThreeCSG.js
│   │       │   └── Watch.js
│   │       ├── SearchElementView.js
│   │       ├── SearchView.js
│   │       ├── ShareView.js
│   │       ├── WorkspaceBrowserElementView.js
│   │       ├── WorkspaceBrowserView.js
│   │       ├── WorkspaceControlsView.js
│   │       ├── WorkspaceTabView.js
│   │       ├── WorkspaceView.js
│   │       └── customizer/
│   │           ├── CustomizerAppView.js
│   │           ├── CustomizerHeaderView.js
│   │           ├── CustomizerViewerView.js
│   │           ├── CustomizerWorkspaceView.js
│   │           └── widgets/
│   │               ├── Base.js
│   │               ├── Geometry.js
│   │               └── Number.js
│   └── styles/
│       ├── bootstrap.css
│       ├── customizer.css
│       └── main.css
├── bower.json
├── package.json
├── server/
│   ├── .gitignore
│   ├── .travis.yml
│   ├── app.js
│   ├── cluster_app.js
│   ├── config/
│   │   ├── passport.js
│   │   └── secrets.js
│   ├── controllers/
│   │   ├── api.js
│   │   ├── contact.js
│   │   ├── exampleWorkspaces.js
│   │   ├── feedback.js
│   │   ├── flood.js
│   │   ├── home.js
│   │   ├── user.js
│   │   └── workspaces.js
│   ├── models/
│   │   ├── Session.js
│   │   ├── User.js
│   │   └── Workspace.js
│   ├── package.json
│   ├── public/
│   │   ├── css/
│   │   │   ├── lib/
│   │   │   │   ├── animate.css
│   │   │   │   ├── bootstrap/
│   │   │   │   │   ├── alerts.less
│   │   │   │   │   ├── badges.less
│   │   │   │   │   ├── bootstrap.less
│   │   │   │   │   ├── breadcrumbs.less
│   │   │   │   │   ├── button-groups.less
│   │   │   │   │   ├── buttons.less
│   │   │   │   │   ├── carousel.less
│   │   │   │   │   ├── close.less
│   │   │   │   │   ├── code.less
│   │   │   │   │   ├── component-animations.less
│   │   │   │   │   ├── dropdowns.less
│   │   │   │   │   ├── forms.less
│   │   │   │   │   ├── glyphicons.less
│   │   │   │   │   ├── grid.less
│   │   │   │   │   ├── input-groups.less
│   │   │   │   │   ├── jumbotron.less
│   │   │   │   │   ├── labels.less
│   │   │   │   │   ├── list-group.less
│   │   │   │   │   ├── media.less
│   │   │   │   │   ├── mixins.less
│   │   │   │   │   ├── modals.less
│   │   │   │   │   ├── navbar.less
│   │   │   │   │   ├── navs.less
│   │   │   │   │   ├── normalize.less
│   │   │   │   │   ├── pager.less
│   │   │   │   │   ├── pagination.less
│   │   │   │   │   ├── panels.less
│   │   │   │   │   ├── popovers.less
│   │   │   │   │   ├── print.less
│   │   │   │   │   ├── progress-bars.less
│   │   │   │   │   ├── responsive-utilities.less
│   │   │   │   │   ├── scaffolding.less
│   │   │   │   │   ├── tables.less
│   │   │   │   │   ├── theme.less
│   │   │   │   │   ├── thumbnails.less
│   │   │   │   │   ├── tooltip.less
│   │   │   │   │   ├── type.less
│   │   │   │   │   ├── utilities.less
│   │   │   │   │   ├── variables.less
│   │   │   │   │   └── wells.less
│   │   │   │   └── bootstrap-social.less
│   │   │   ├── styles.less
│   │   │   └── themes/
│   │   │       ├── default.less
│   │   │       ├── flatly.less
│   │   │       └── ios7.less
│   │   ├── fonts/
│   │   │   └── FontAwesome.otf
│   │   └── js/
│   │       ├── application.js
│   │       └── main.js
│   ├── test/
│   │   ├── app.js
│   │   ├── mocha.opts
│   │   └── models.js
│   └── views/
│       ├── 404.jade
│       ├── account/
│       │   ├── forgot.jade
│       │   ├── login.jade
│       │   ├── profile.jade
│       │   ├── reset.jade
│       │   └── signup.jade
│       ├── api/
│       │   ├── aviary.jade
│       │   ├── clockwork.jade
│       │   ├── facebook.jade
│       │   ├── foursquare.jade
│       │   ├── github.jade
│       │   ├── index.jade
│       │   ├── lastfm.jade
│       │   ├── linkedin.jade
│       │   ├── nyt.jade
│       │   ├── paypal.jade
│       │   ├── scraping.jade
│       │   ├── steam.jade
│       │   ├── tumblr.jade
│       │   ├── twilio.jade
│       │   ├── twitter.jade
│       │   └── venmo.jade
│       ├── contact.jade
│       ├── home.jade
│       ├── layout.jade
│       └── partials/
│           ├── flash.jade
│           ├── footer.jade
│           └── navigation.jade
├── test/
│   ├── index.html
│   ├── lib/
│   │   ├── chai.js
│   │   ├── expect.js
│   │   └── mocha/
│   │       ├── mocha.css
│   │       └── mocha.js
│   └── spec/
│       └── test.js
└── todo.txt
Download .txt
SYMBOL INDEX (165 symbols across 13 files)

FILE: app/scripts/lib/OrbitControls.js
  function getAutoRotationAngle (line 309) | function getAutoRotationAngle() {
  function getZoomScale (line 315) | function getZoomScale() {
  function onMouseDown (line 321) | function onMouseDown( event ) {
  function onMouseMove (line 355) | function onMouseMove( event ) {
  function onMouseUp (line 414) | function onMouseUp( /* event */ ) {
  function onMouseWheel (line 425) | function onMouseWheel( event ) {
  function onKeyDown (line 460) | function onKeyDown( event ) {
  function touchstart (line 490) | function touchstart( event ) {
  function touchmove (line 536) | function touchmove( event ) {
  function touchend (line 615) | function touchend( /* event */ ) {

FILE: app/scripts/lib/Viewport.js
  function init (line 21) | function init() {
  function makeGrid (line 78) | function makeGrid(){
  function onWindowResize (line 137) | function onWindowResize() {
  function animate (line 151) | function animate() {
  function render (line 160) | function render() {

FILE: app/scripts/lib/flood/async.js
  function only_once (line 27) | function only_once(fn) {
  function done (line 127) | function done(err) {
  function _insert (line 738) | function _insert(q, data, pos, callback) {
  function _compareTasks (line 840) | function _compareTasks(a, b){
  function _binarySearch (line 844) | function _binarySearch(sequence, item, compare) {
  function _insert (line 858) | function _insert(q, data, priority, callback) {
  function next (line 1099) | function next(err) {

FILE: app/scripts/lib/flood/csg.js
  function vertex (line 251) | function vertex(theta, phi) {
  function point (line 299) | function point(stack, slice, normalBlend) {

FILE: app/scripts/lib/flood/flood.js
  function F (line 27) | function F() {}
  function MultiOutResult (line 81) | function MultiOutResult(object){
  function QuotedArray (line 107) | function QuotedArray(){
  function constructPortTypeName (line 327) | function constructPortTypeName(type) {
  function AnyType (line 1509) | function AnyType() {}
  function AnyTypeButQuotedArray (line 1512) | function AnyTypeButQuotedArray() {}
  function isObjectTypeMatch (line 1519) | function isObjectTypeMatch(arg, arg_type) {
  function isFastTypeMatch (line 1549) | function isFastTypeMatch( arg, arg_type ){
  function doAllTypesMatch (line 1572) | function doAllTypesMatch( node_args, expected_arg_types ){
  function allQuotedArrays (line 1595) | function allQuotedArrays( array ){
  function newNestedQuotedArrayByElements (line 1607) | function newNestedQuotedArrayByElements(eles){

FILE: app/scripts/lib/flood/flood_runner.js
  function clone (line 425) | function clone (oToBeCloned) {
  function objectLength (line 446) | function objectLength( object ) {

FILE: app/scripts/lib/flood/scheme.js
  function add_globals (line 82) | function add_globals(env) {
  function defer (line 98) | function defer(x){

FILE: app/scripts/lib/flood/scheme_async.js
  function add_globals (line 189) | function add_globals(env) {

FILE: app/scripts/lib/flood/test/flood_csg_test.js
  function Turtle (line 138) | function Turtle(){}

FILE: app/scripts/lib/flood/test/flood_test.js
  function Turtle (line 171) | function Turtle(){}

FILE: app/scripts/views/WorkspaceView.js
  function isTouchDevice (line 89) | function isTouchDevice() {

FILE: test/lib/chai.js
  function require (line 13) | function require(p) {
  function Assertion (line 177) | function Assertion (obj, msg, stack) {
  function an (line 373) | function an(type, msg) {
  function includeChainingBehavior (line 409) | function includeChainingBehavior () {
  function include (line 413) | function include (val, msg) {
  function checkArguments (line 601) | function checkArguments () {
  function assertEqual (line 636) | function assertEqual (val, msg) {
  function assertAbove (line 706) | function assertAbove (n, msg) {
  function assertLeast (line 754) | function assertLeast (n, msg) {
  function assertBelow (line 802) | function assertBelow (n, msg) {
  function assertMost (line 850) | function assertMost (n, msg) {
  function assertInstanceOf (line 936) | function assertInstanceOf (constructor, msg) {
  function assertOwnProperty (line 1058) | function assertOwnProperty (name, msg) {
  function assertLengthChain (line 1097) | function assertLengthChain () {
  function assertLength (line 1101) | function assertLength (n, msg) {
  function assertKeys (line 1184) | function assertKeys (keys) {
  function assertThrows (line 1271) | function assertThrows (constructor, errMsg, msg) {
  function AssertionError (line 1496) | function AssertionError (options) {
  function loadShould (line 2546) | function loadShould () {
  function _deepEqual (line 2791) | function _deepEqual(actual, expected, memos) {
  function isUndefinedOrNull (line 2827) | function isUndefinedOrNull(value) {
  function isArguments (line 2831) | function isArguments(object) {
  function objEquiv (line 2835) | function objEquiv(a, b, memos) {
  function parsePath (line 3098) | function parsePath (path) {
  function _getPathValue (line 3123) | function _getPathValue (parsed, obj) {
  function inspect (line 3268) | function inspect(obj, showHidden, depth, colors) {
  function formatValue (line 3307) | function formatValue(ctx, value, recurseTimes) {
  function formatPrimitive (line 3415) | function formatPrimitive(ctx, value) {
  function formatError (line 3439) | function formatError(value) {
  function formatArray (line 3444) | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  function formatProperty (line 3464) | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, arra...
  function reduceToSingleString (line 3524) | function reduceToSingleString(output, base, braces) {
  function isArray (line 3544) | function isArray(ar) {
  function isRegExp (line 3549) | function isRegExp(re) {
  function isDate (line 3553) | function isDate(d) {
  function isError (line 3557) | function isError(e) {
  function objectToString (line 3561) | function objectToString(o) {

FILE: test/lib/mocha/mocha.js
  function require (line 6) | function require(p){
  function clonePath (line 78) | function clonePath(path) {
  function removeEmpty (line 81) | function removeEmpty(array) {
  function escapeHTML (line 90) | function escapeHTML(s) {
  function contextLines (line 251) | function contextLines(lines) {
  function eofNL (line 254) | function eofNL(curRange, i, current) {
  function isArray (line 363) | function isArray(obj) {
  function EventEmitter (line 373) | function EventEmitter(){}
  function on (line 408) | function on () {
  function Progress (line 552) | function Progress() {
  function Context (line 694) | function Context(){}
  function Hook (line 775) | function Hook(title, fn) {
  function F (line 784) | function F(){}
  function visit (line 979) | function visit(obj) {
  function image (line 1284) | function image(name) {
  function Mocha (line 1306) | function Mocha(options) {
  function parse (line 1594) | function parse(str) {
  function format (line 1633) | function format(ms) {
  function Base (line 1869) | function Base(runner) {
  function pluralize (line 1937) | function pluralize(n) {
  function pad (line 1987) | function pad(str, len) {
  function errorDiff (line 2000) | function errorDiff(err, type, escape) {
  function colorLines (line 2023) | function colorLines(name, str) {
  function Doc (line 2053) | function Doc(runner) {
  function Dot (line 2113) | function Dot(runner) {
  function F (line 2153) | function F(){}
  function HTMLCov (line 2182) | function HTMLCov(runner) {
  function coverageClass (line 2206) | function coverageClass(n) {
  function HTML (line 2259) | function HTML(runner, root) {
  function error (line 2400) | function error(msg) {
  function fragment (line 2408) | function fragment(html) {
  function hideSuitesWithout (line 2428) | function hideSuitesWithout(classname) {
  function unhide (line 2440) | function unhide() {
  function text (line 2451) | function text(el, str) {
  function on (line 2463) | function on(el, event, fn) {
  function JSONCov (line 2518) | function JSONCov(runner, output) {
  function map (line 2561) | function map(cov) {
  function coverage (line 2600) | function coverage(filename, data) {
  function clean (line 2643) | function clean(test) {
  function List (line 2675) | function List(runner) {
  function clean (line 2708) | function clean(test) {
  function JSONReporter (line 2740) | function JSONReporter(runner) {
  function clean (line 2781) | function clean(test) {
  function Landing (line 2831) | function Landing(runner) {
  function F (line 2887) | function F(){}
  function List (line 2917) | function List(runner) {
  function F (line 2958) | function F(){}
  function Markdown (line 2987) | function Markdown(runner) {
  function Min (line 3081) | function Min(runner) {
  function F (line 3098) | function F(){}
  function NyanCat (line 3127) | function NyanCat(runner) {
  function draw (line 3193) | function draw(color, n) {
  function write (line 3356) | function write(string) {
  function F (line 3364) | function F(){}
  function Progress (line 3402) | function Progress(runner, options) {
  function F (line 3458) | function F(){}
  function Spec (line 3489) | function Spec(runner) {
  function F (line 3553) | function F(){}
  function TAP (line 3584) | function TAP(runner) {
  function title (line 3632) | function title(test) {
  function Teamcity (line 3659) | function Teamcity(runner) {
  function escape (line 3692) | function escape(str) {
  function XUnit (line 3740) | function XUnit(runner) {
  function F (line 3774) | function F(){}
  function test (line 3784) | function test(test) {
  function tag (line 3806) | function tag(name, attrs, close, content) {
  function cdata (line 3824) | function cdata(str) {
  function Runnable (line 3870) | function Runnable(title, fn) {
  function F (line 3884) | function F(){}
  function multiple (line 4008) | function multiple(err) {
  function done (line 4015) | function done(err) {
  function Runner (line 4109) | function Runner(suite) {
  function F (line 4125) | function F(){}
  function next (line 4285) | function next(i) {
  function next (line 4325) | function next(suite) {
  function next (line 4424) | function next(err) {
  function next (line 4491) | function next() {
  function done (line 4497) | function done() {
  function filterLeaks (line 4584) | function filterLeaks(ok, globals) {
  function Suite (line 4647) | function Suite(title, ctx) {
  function F (line 4667) | function F(){}
  function Test (line 4924) | function Test(title, fn) {
  function F (line 4934) | function F(){}
  function ignored (line 5088) | function ignored(path){
  function highlight (line 5200) | function highlight(js) {
Condensed preview — 194 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,350K chars).
[
  {
    "path": ".bowerrc",
    "chars": 44,
    "preview": "{\n    \"directory\": \"app/bower_components\"\n}\n"
  },
  {
    "path": ".editorconfig",
    "chars": 416,
    "preview": "# EditorConfig helps developers define and maintain consistent\n# coding styles between different editors and IDEs\n# edit"
  },
  {
    "path": ".gitattributes",
    "chars": 11,
    "preview": "* text=auto"
  },
  {
    "path": ".gitignore",
    "chars": 85,
    "preview": "node_modules\ntemp\n.sass-cache\napp/bower_components\n.tmp/\ndist_desktop\n.DS_Store\ndist\n"
  },
  {
    "path": "Gruntfile.js",
    "chars": 6799,
    "preview": "'use strict';\nvar lrSnippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;\nvar mountFolder = functio"
  },
  {
    "path": "LICENSE",
    "chars": 1077,
    "preview": "The MIT License (MIT)\n\nCopyright (c) Peter Boyer 2013\n\nPermission is hereby granted, free of charge, to any person obtai"
  },
  {
    "path": "README.md",
    "chars": 3881,
    "preview": "![Image](https://raw.github.com/pboyer/flood/master/extra/screenshot.png) \n\n\n## flood\n\n### What is it?\n\nflood is a [data"
  },
  {
    "path": "app/.htaccess",
    "chars": 20968,
    "preview": "# Apache configuration file\n# httpd.apache.org/docs/2.2/mod/quickreference.html\n\n# Note .htaccess files are an overhead,"
  },
  {
    "path": "app/404.html",
    "chars": 4464,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <meta charset=\"utf-8\">\n        <title>Page Not Found :(</title>\n    "
  },
  {
    "path": "app/app.html",
    "chars": 28527,
    "preview": "<!doctype html>\n<!--[if lt IE 7]>      <html class=\"no-js lt-ie9 lt-ie8 lt-ie7\"> <![endif]-->\n<!--[if IE 7]>         <ht"
  },
  {
    "path": "app/customizer.html",
    "chars": 9767,
    "preview": "<!doctype html>\n<!--[if lt IE 7]>      <html class=\"no-js lt-ie9 lt-ie8 lt-ie7\"> <![endif]-->\n<!--[if IE 7]>         <ht"
  },
  {
    "path": "app/index.html",
    "chars": 12151,
    "preview": "<!doctype html>\n<!--[if lt IE 7]>      <html class=\"no-js lt-ie9 lt-ie8 lt-ie7\"> <![endif]-->\n<!--[if IE 7]>         <ht"
  },
  {
    "path": "app/package.json",
    "chars": 247,
    "preview": "{\n  \"name\": \"flood\",\n  \"version\": \"0.0.4\",\n  \"description\": \"A visual programming language for JavaScript, based on Sche"
  },
  {
    "path": "app/robots.txt",
    "chars": 31,
    "preview": "# robotstxt.org\n\nUser-agent: *\n"
  },
  {
    "path": "app/scripts/collections/Connections.js",
    "chars": 137,
    "preview": "define(['backbone', 'Connection'], function(Backbone, Connection) {\n\n\treturn Backbone.Collection.extend({\n\n\t\tmodel: Conn"
  },
  {
    "path": "app/scripts/collections/Nodes.js",
    "chars": 1078,
    "preview": "define(['backbone', 'Node'], function(Backbone, Node) {\n\n\treturn Backbone.Collection.extend({\n\n\t\tmodel: Node,\n\n\t\tinitial"
  },
  {
    "path": "app/scripts/collections/SearchElements.js",
    "chars": 807,
    "preview": "define(['backbone', 'SearchElement', 'FLOOD'], function(Backbone, SearchElement, FLOOD) {\n\n\treturn Backbone.Collection.e"
  },
  {
    "path": "app/scripts/collections/WorkspaceBrowserElements.js",
    "chars": 192,
    "preview": "define(['backbone', 'WorkspaceBrowserElement'], function(Backbone, WorkspaceBrowserElement) {\n\n\treturn Backbone.Collecti"
  },
  {
    "path": "app/scripts/collections/Workspaces.js",
    "chars": 136,
    "preview": "define(['backbone', 'Workspace'], function(Backbone, Workspace) {\n\n\treturn Backbone.Collection.extend({\n\n\t  model: Works"
  },
  {
    "path": "app/scripts/config.js",
    "chars": 6689,
    "preview": "/*global require*/\n'use strict';\n\nrequire.config({\n    shim: {\n        underscore: {\n            exports: '_'\n        },"
  },
  {
    "path": "app/scripts/customizer.js",
    "chars": 463,
    "preview": "require([\"config\"], function() {\n\n  require(['backbone', 'CustomizerApp', 'CustomizerAppView', 'Three', 'FLOODCSG', 'boo"
  },
  {
    "path": "app/scripts/lib/OrbitControls.js",
    "chars": 14961,
    "preview": "/**\n * @author qiao / https://github.com/qiao\n * @author mrdoob / http://mrdoob.com\n * @author alteredq / http://altered"
  },
  {
    "path": "app/scripts/lib/Viewport.js",
    "chars": 3744,
    "preview": "var container, $container;\n\nvar camera, controls, scene, renderer;\n\nvar geometry, group;\n\nvar mouse = new THREE.Vector2("
  },
  {
    "path": "app/scripts/lib/flood/async.js",
    "chars": 34730,
    "preview": "/*!\n * async\n * https://github.com/caolan/async\n *\n * Copyright 2010-2014 Caolan McMahon\n * Released under the MIT licen"
  },
  {
    "path": "app/scripts/lib/flood/csg.js",
    "chars": 34190,
    "preview": "// Constructive Solid Geometry (CSG) is a modeling technique that uses Boolean\n// operations like union and intersection"
  },
  {
    "path": "app/scripts/lib/flood/flood.js",
    "chars": 43348,
    "preview": "if (typeof define !== 'function') {\n    var define = require('amdefine')(module);\n}\n\n// Webworker context\nif (typeof req"
  },
  {
    "path": "app/scripts/lib/flood/flood_csg.js",
    "chars": 24853,
    "preview": "if (typeof define !== 'function') {\n    var define = require('amdefine')(module);\n}\n\n// web worker context\nif (typeof re"
  },
  {
    "path": "app/scripts/lib/flood/flood_runner.js",
    "chars": 12934,
    "preview": "importScripts( 'scheme.js', 'flood.js', 'csg.js', 'flood_csg.js'); \n\n// Routing\nvar that = this;\n\nonmessage = function ("
  },
  {
    "path": "app/scripts/lib/flood/scheme.js",
    "chars": 6993,
    "preview": "if (typeof define !== 'function' && typeof require === 'function') {\n    var define = require('amdefine')(module);\n    v"
  },
  {
    "path": "app/scripts/lib/flood/scheme_async.js",
    "chars": 9567,
    "preview": "if (typeof define !== 'function' && typeof require === 'function') {\n    var define = require('amdefine')(module);\n} \n\ni"
  },
  {
    "path": "app/scripts/lib/flood/test/flood_csg_test.js",
    "chars": 8302,
    "preview": "var FLOOD = new require('../flood.js')\n\t, assert = require('assert')\n\t, scheme = require('../scheme.js')\n\t, f = require("
  },
  {
    "path": "app/scripts/lib/flood/test/flood_lambda_test.js",
    "chars": 1003,
    "preview": "var FLOOD = new require('../flood.js')\n\t, assert = require('assert')\n\t, scheme = require('../scheme.js');\n\n\n(function(sc"
  },
  {
    "path": "app/scripts/lib/flood/test/flood_runner_test.html",
    "chars": 1641,
    "preview": "<script>\n  var worker = new Worker('../flood_router.js');\n\n  worker.addEventListener('message', function(e) {\n    consol"
  },
  {
    "path": "app/scripts/lib/flood/test/flood_test.js",
    "chars": 9196,
    "preview": "var FLOOD = new require('../flood.js')\n\t, assert = require('assert')\n\t, scheme = require('../scheme.js');\n\n// mapApply -"
  },
  {
    "path": "app/scripts/lib/flood/test/scheme_async_test.html",
    "chars": 2575,
    "preview": "\n<script src=\"../../../../bower_components/q/q.js\"></script>\n\n<script>\n\n\n  // an async promise implementation\n  var Prom"
  },
  {
    "path": "app/scripts/lib/flood/test/scheme_eval_async_test.js",
    "chars": 527,
    "preview": "var assert = require('assert')\n\t, async = require('../async.js')\n\t, scheme = require('../scheme.js');\n\n\n(function(scheme"
  },
  {
    "path": "app/scripts/main.js",
    "chars": 437,
    "preview": "require([\"config\"], function() {\n\n  require(['backbone', 'App', 'AppView', 'Three', 'Viewport', 'FLOODCSG', 'bootstrap']"
  },
  {
    "path": "app/scripts/models/App.js",
    "chars": 5931,
    "preview": "define(['backbone', 'Workspaces', 'Node', 'Login', 'Workspace', 'SearchElements'], \n    function(Backbone, Workspaces, N"
  },
  {
    "path": "app/scripts/models/Connection.js",
    "chars": 1240,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.Model.extend({\n\n    idAttribute: \"_id\",\n\n    defaults: {\n  "
  },
  {
    "path": "app/scripts/models/Feedback.js",
    "chars": 648,
    "preview": "define(['backbone'], function(Backbone) {\n\n\treturn Backbone.Model.extend({\n\n\t  defaults: {\n\t  \tshowing: false,\n\t  \tfailu"
  },
  {
    "path": "app/scripts/models/GeometryExport.js",
    "chars": 2877,
    "preview": "define(['FileSaver'], function(FileSaver) {\n\n\t/// Adapted from: https://github.com/stephomi/sculptgl/blob/master/src/mis"
  },
  {
    "path": "app/scripts/models/Help.js",
    "chars": 1403,
    "preview": "define(['backbone'], function(Backbone) {\n\n\treturn Backbone.Model.extend({\n\n\t  defaults: {\n\t  \tsections:\n\t  \t\t[ { title:"
  },
  {
    "path": "app/scripts/models/Login.js",
    "chars": 1428,
    "preview": "define(['backbone'], function(Backbone) {\n\n\treturn Backbone.Model.extend({\n\n\t  defaults: {\n\t  \tisLoggedIn : false,\n\t  \tf"
  },
  {
    "path": "app/scripts/models/Marquee.js",
    "chars": 1011,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.Model.extend({\n\n    idAttribute: \"_id\",\n\n    defaults: {\n  "
  },
  {
    "path": "app/scripts/models/Node.js",
    "chars": 8033,
    "preview": "var app = app || {};\n\ndefine(['backbone', 'FLOOD'], function(Backbone, FLOOD) {\n\n  return Backbone.Model.extend({\n\n    i"
  },
  {
    "path": "app/scripts/models/Runner.js",
    "chars": 5095,
    "preview": "define(['backbone','underscore'], function(Backbone, _) {\n\n\treturn Backbone.Model.extend({\n\n\t  defaults: {\n\t  \tisRunning"
  },
  {
    "path": "app/scripts/models/Search.js",
    "chars": 161,
    "preview": "define(['backbone'], function(Backbone) {\n\n\treturn Backbone.Model.extend({\n\n\t  defaults: {\n\n\t  },\n\n\t  initialize: functi"
  },
  {
    "path": "app/scripts/models/SearchElement.js",
    "chars": 249,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.Model.extend({\n\n    defaults: {\n      name: null,\n      isC"
  },
  {
    "path": "app/scripts/models/Share.js",
    "chars": 342,
    "preview": "define(['backbone'], function(Backbone) {\n\n\treturn Backbone.Model.extend({\n\n\t  defaults: {\n\t  \tisProjectWorkspace: false"
  },
  {
    "path": "app/scripts/models/Workspace.js",
    "chars": 22004,
    "preview": "define(['backbone', 'Nodes', 'Connection', 'Connections', 'scheme', 'FLOOD', 'Runner', 'Node', 'Marquee', 'WorkspaceReso"
  },
  {
    "path": "app/scripts/models/WorkspaceBrowser.js",
    "chars": 392,
    "preview": "define(['backbone', 'WorkspaceBrowserElements'], function(Backbone, WorkspaceBrowserElements) {\n\n\treturn Backbone.Model."
  },
  {
    "path": "app/scripts/models/WorkspaceBrowserElement.js",
    "chars": 287,
    "preview": "define(['backbone'], function(Backbone) {\n\n\treturn Backbone.Model.extend({\n\n\t\tidAttribute: \"_id\",\n\n    defaults: {\n     "
  },
  {
    "path": "app/scripts/models/WorkspaceResolver.js",
    "chars": 8703,
    "preview": "define(['backbone', 'FLOOD'], \n    function(Backbone, FLOOD) {\n\n  return Backbone.Model.extend({\n\n    initialize: functi"
  },
  {
    "path": "app/scripts/models/customizer/CustomizerApp.js",
    "chars": 1086,
    "preview": "define(['backbone', 'App'], \n    function(Backbone, App){\n\n  return App.extend({\n\n    url: function() {\n\n      // get th"
  },
  {
    "path": "app/scripts/views/AppView.js",
    "chars": 14321,
    "preview": "define([  'backbone', \n          'App', \n          'WorkspaceView', \n          'Search', \n          'SearchView', \n     "
  },
  {
    "path": "app/scripts/views/ConnectionView.js",
    "chars": 3850,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.View.extend({\n\n    template: _.template( $('#connection-tem"
  },
  {
    "path": "app/scripts/views/FeedbackView.js",
    "chars": 2023,
    "preview": "define(['backbone'], function(Backbone) {\n\n  'use strict';\n\n  return Backbone.View.extend({\n\n    el: '#feedback',\n\n    e"
  },
  {
    "path": "app/scripts/views/HelpView.js",
    "chars": 1675,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.View.extend({\n\n    el: '#help',\n\n    events: { \"click .exit"
  },
  {
    "path": "app/scripts/views/LoginView.js",
    "chars": 2783,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.View.extend({\n\n    el: '#login',\n\n    template: _.template("
  },
  {
    "path": "app/scripts/views/MarqueeView.js",
    "chars": 1941,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.View.extend({\n\n    workspaceView : null,\n\n    initialize: f"
  },
  {
    "path": "app/scripts/views/NodeViews/Base.js",
    "chars": 14575,
    "preview": "define(['backbone', 'jqueryuidraggable', 'bootstrap', 'Hammer'], function(Backbone, jqueryuidraggable, bootstrap, Hammer"
  },
  {
    "path": "app/scripts/views/NodeViews/CustomNode.js",
    "chars": 631,
    "preview": "define(['underscore', 'jquery', 'ThreeCSGNodeView'], function(_, $, ThreeCSGNodeView) {\n\n  return ThreeCSGNodeView.exten"
  },
  {
    "path": "app/scripts/views/NodeViews/Input.js",
    "chars": 1533,
    "preview": "define(['backbone', 'underscore', 'jquery', 'BaseNodeView'], function(Backbone, _, $, BaseNodeView) {\n\n  return BaseNode"
  },
  {
    "path": "app/scripts/views/NodeViews/NodeViews.js",
    "chars": 589,
    "preview": "define(['BaseNodeView', 'WatchNodeView', 'NumNodeView', 'ThreeCSGNodeView', 'ScriptView', 'OutputView', 'InputView','Cus"
  },
  {
    "path": "app/scripts/views/NodeViews/Num.js",
    "chars": 5180,
    "preview": "define(['backbone', 'underscore', 'jquery', 'BaseNodeView', 'jqueryuislider'], function(Backbone, _, $, BaseNodeView) {\n"
  },
  {
    "path": "app/scripts/views/NodeViews/Output.js",
    "chars": 1531,
    "preview": "define(['backbone', 'underscore', 'jquery', 'BaseNodeView'], function(Backbone, _, $, BaseNodeView) {\n\n  return BaseNode"
  },
  {
    "path": "app/scripts/views/NodeViews/Script.js",
    "chars": 4191,
    "preview": "define(['backbone', 'underscore', 'jquery', 'ThreeCSGNodeView', 'FLOOD', 'codemirror', 'codemirror/mode/javascript/javas"
  },
  {
    "path": "app/scripts/views/NodeViews/ThreeCSG.js",
    "chars": 7857,
    "preview": "define(['backbone', 'underscore', 'jquery', 'BaseNodeView'], function(Backbone, _, $, BaseNodeView) {\n\n  return BaseNode"
  },
  {
    "path": "app/scripts/views/NodeViews/Watch.js",
    "chars": 1028,
    "preview": "define(['backbone', 'underscore', 'jquery', 'BaseNodeView'], function(Backbone, _, $, BaseNodeView) {\n\n  return BaseNode"
  },
  {
    "path": "app/scripts/views/SearchElementView.js",
    "chars": 586,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.View.extend({\n\n    tagName: 'li',\n    className: 'search-el"
  },
  {
    "path": "app/scripts/views/SearchView.js",
    "chars": 2129,
    "preview": "define(['backbone', 'List', 'SearchElementView'], function(Backbone, List, SearchElementView) {\n\n  return Backbone.View."
  },
  {
    "path": "app/scripts/views/ShareView.js",
    "chars": 1927,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.View.extend({\n\n    el: '#share',\n\n    events: { \n      'cli"
  },
  {
    "path": "app/scripts/views/WorkspaceBrowserElementView.js",
    "chars": 1707,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.View.extend({\n\n    tagName: 'li',\n    className: 'workspace"
  },
  {
    "path": "app/scripts/views/WorkspaceBrowserView.js",
    "chars": 2456,
    "preview": "define(['backbone', 'WorkspaceBrowserElementView'], function(Backbone, WorkspaceBrowserElementView) {\n\n  return Backbone"
  },
  {
    "path": "app/scripts/views/WorkspaceControlsView.js",
    "chars": 4390,
    "preview": "define(['backbone', 'List', 'SearchElementView', 'bootstrap', 'jquery'], function(Backbone, List, SearchElementView, boo"
  },
  {
    "path": "app/scripts/views/WorkspaceTabView.js",
    "chars": 2202,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.View.extend({\n\n    tagName: 'div',\n    className: 'workspac"
  },
  {
    "path": "app/scripts/views/WorkspaceView.js",
    "chars": 18159,
    "preview": "define(['backbone', 'Workspace', 'ConnectionView', 'MarqueeView', 'NodeViewTypes', 'Hammer'], function(Backbone, Workspa"
  },
  {
    "path": "app/scripts/views/customizer/CustomizerAppView.js",
    "chars": 1261,
    "preview": "// the application is constituted by\n\n// CustomizerAppView, CustomizerApp - controls visibility of widget\n// CustomizerW"
  },
  {
    "path": "app/scripts/views/customizer/CustomizerHeaderView.js",
    "chars": 526,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.View.extend({\n\n    el: '#customizer-header',\n\n    template:"
  },
  {
    "path": "app/scripts/views/customizer/CustomizerViewerView.js",
    "chars": 3851,
    "preview": "define(['backbone', 'Three', 'OrbitControls'], function(Backbone) {\n\n\tvar container, $container;\n\tvar camera, controls, "
  },
  {
    "path": "app/scripts/views/customizer/CustomizerWorkspaceView.js",
    "chars": 1763,
    "preview": "define(['backbone', 'BaseWidgetView', 'GeometryWidgetView', 'NumberWidgetView'], \n  function(Backbone, BaseWidgetView, G"
  },
  {
    "path": "app/scripts/views/customizer/widgets/Base.js",
    "chars": 1206,
    "preview": "define(['backbone'], function(Backbone) {\n\n  return Backbone.View.extend({\n\n    tagName: 'div',\n    className: 'widget',"
  },
  {
    "path": "app/scripts/views/customizer/widgets/Geometry.js",
    "chars": 6447,
    "preview": "define(['backbone', 'underscore', 'jquery', 'BaseWidgetView'], function(Backbone, _, $, BaseWidgetView) {\n\n  return Base"
  },
  {
    "path": "app/scripts/views/customizer/widgets/Number.js",
    "chars": 4928,
    "preview": "define(['backbone', 'underscore', 'jquery', 'BaseWidgetView', 'jqueryuislider'], function(Backbone, _, $, BaseWidgetView"
  },
  {
    "path": "app/styles/bootstrap.css",
    "chars": 99154,
    "preview": "/*!\n * Bootstrap v3.0.3 (http://getbootstrap.com)\n * Copyright 2013 Twitter, Inc.\n * Licensed under http://www.apache.or"
  },
  {
    "path": "app/styles/customizer.css",
    "chars": 1608,
    "preview": "#customizer-app {\n\tbackground: #e3e3e3;\n\ttop: 0;\n\tright: 0;\n\tleft: 0;\n\tbottom: 0;\n\tposition: absolute;\n}\n\n#customizer-wo"
  },
  {
    "path": "app/styles/main.css",
    "chars": 20562,
    "preview": "body { \n\twidth:  100%;\n  \theight: 100%;\n  \tmargin: 0px;\n  \tmargin: 0;\n  \t-webkit-backface-visibility: hidden;\n  }\n\n.row,"
  },
  {
    "path": "bower.json",
    "chars": 642,
    "preview": "{\n  \"name\": \"flood\",\n  \"version\": \"0.0.0\",\n  \"dependencies\": {\n    \"jquery\": \"1.9.1\",\n    \"requirejs\": \"~2.1.5\",\n    \"re"
  },
  {
    "path": "package.json",
    "chars": 932,
    "preview": "{\n  \"name\": \"flood\",\n  \"version\": \"0.0.4\",\n  \"description\": \"A visual programming language for JavaScript, based on Sche"
  },
  {
    "path": "server/.gitignore",
    "chars": 180,
    "preview": "lib-cov\n*.seed\n*.log\n*.csv\n*.dat\n*.out\n*.pid\n*.gz\n*.swp\n\npids\nlogs\nresults\ntmp\n\nnpm-debug.log\nnode_modules\n.idea\n*.iml\n."
  },
  {
    "path": "server/.travis.yml",
    "chars": 61,
    "preview": "language: node_js\n\nservices:\n  - mongodb\n\nnode_js:\n  - '0.10'"
  },
  {
    "path": "server/app.js",
    "chars": 9343,
    "preview": "/**\n * Module dependencies.\n */\n\nvar express = require('express');\nvar cookieParser = require('cookie-parser');\nvar comp"
  },
  {
    "path": "server/cluster_app.js",
    "chars": 469,
    "preview": "/**\n * Module dependencies.\n */\n\nvar os = require('os');\nvar cluster = require('cluster');\n\n/**\n * Cluster setup.\n */\n\n/"
  },
  {
    "path": "server/config/passport.js",
    "chars": 15000,
    "preview": "var _ = require('underscore');\nvar passport = require('passport');\nvar LocalStrategy = require('passport-local').Strateg"
  },
  {
    "path": "server/config/secrets.js",
    "chars": 3447,
    "preview": "module.exports = {\n  db: process.env.MONGODB|| 'mongodb://localhost:27017/test',\n\n  sessionSecret: process.env.SESSION_S"
  },
  {
    "path": "server/controllers/api.js",
    "chars": 14469,
    "preview": "var secrets = require('../config/secrets');\nvar User = require('../models/User');\nvar querystring = require('querystring"
  },
  {
    "path": "server/controllers/contact.js",
    "chars": 1507,
    "preview": "var secrets = require('../config/secrets');\nvar nodemailer = require(\"nodemailer\");\nvar smtpTransport = nodemailer.creat"
  },
  {
    "path": "server/controllers/exampleWorkspaces.js",
    "chars": 11107,
    "preview": "exports.myFirstProject = \n{  \n  \"connections\":[  \n    {  \n      \"kind\":\"addConnection\",\n      \"startNodeId\":398809118,\n "
  },
  {
    "path": "server/controllers/feedback.js",
    "chars": 1044,
    "preview": "var _ = require('underscore')\n\t, nodemailer = require('nodemailer')\n\t, secrets = require('../config/secrets');\n\n\nvar smt"
  },
  {
    "path": "server/controllers/flood.js",
    "chars": 8082,
    "preview": "var mongoose = require('mongoose')\n\t, Session = require('../models/Workspace').SessionModel\n\t, Workspace = require('../m"
  },
  {
    "path": "server/controllers/home.js",
    "chars": 163,
    "preview": "var flood = require('../models/Workspace');\n\n/**\n * GET /\n * Home page.\n */\n\nexports.index = function(req, res) {\n  res."
  },
  {
    "path": "server/controllers/user.js",
    "chars": 9899,
    "preview": "var _ = require('underscore');\nvar async = require('async');\nvar crypto = require('crypto');\nvar nodemailer = require('n"
  },
  {
    "path": "server/controllers/workspaces.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "server/models/Session.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "server/models/User.js",
    "chars": 2115,
    "preview": "var mongoose = require('mongoose');\nvar bcrypt = require('bcrypt-nodejs');\nvar crypto = require('crypto');\nvar models = "
  },
  {
    "path": "server/models/Workspace.js",
    "chars": 1172,
    "preview": "var mongoose = require('mongoose')\n  , Schema = mongoose.Schema;\n\nvar sessionSchema = new Schema({\n  name: { type: Strin"
  },
  {
    "path": "server/package.json",
    "chars": 1582,
    "preview": "{\n  \"name\": \"flood-server\",\n  \"version\": \"0.0.0\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"\"\n  },\n  \"scripts\": {"
  },
  {
    "path": "server/public/css/lib/animate.css",
    "chars": 56860,
    "preview": "@charset \"UTF-8\";\n\n\n/*!\nAnimate.css - http://daneden.me/animate\nLicensed under the MIT license\n\nCopyright (c) 2013 Danie"
  },
  {
    "path": "server/public/css/lib/bootstrap/alerts.less",
    "chars": 1430,
    "preview": "//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert"
  },
  {
    "path": "server/public/css/lib/bootstrap/badges.less",
    "chars": 1057,
    "preview": "//\n// Badges\n// --------------------------------------------------\n\n\n// Base classes\n.badge {\n  display: inline-block;\n "
  },
  {
    "path": "server/public/css/lib/bootstrap/bootstrap.less",
    "chars": 1071,
    "preview": "// Core variables and mixins\n@import \"variables.less\";\n@import \"mixins.less\";\n\n// Reset\n@import \"normalize.less\";\n@impor"
  },
  {
    "path": "server/public/css/lib/bootstrap/breadcrumbs.less",
    "chars": 594,
    "preview": "//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n  padding: @breadcrumb-padding-v"
  },
  {
    "path": "server/public/css/lib/bootstrap/button-groups.less",
    "chars": 5035,
    "preview": "//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-gro"
  },
  {
    "path": "server/public/css/lib/bootstrap/buttons.less",
    "chars": 3544,
    "preview": "//\n// Buttons\n// --------------------------------------------------\n\n// Base styles\n// ---------------------------------"
  },
  {
    "path": "server/public/css/lib/bootstrap/carousel.less",
    "chars": 4609,
    "preview": "//\n// Carousel\n// --------------------------------------------------\n\n// Wrapper for the slide container and indicators\n"
  },
  {
    "path": "server/public/css/lib/bootstrap/close.less",
    "chars": 683,
    "preview": "//\n// Close icons\n// --------------------------------------------------\n\n\n.close {\n  float: right;\n  font-size: (@font-s"
  },
  {
    "path": "server/public/css/lib/bootstrap/code.less",
    "chars": 1329,
    "preview": "//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\nco"
  },
  {
    "path": "server/public/css/lib/bootstrap/component-animations.less",
    "chars": 509,
    "preview": "//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `."
  },
  {
    "path": "server/public/css/lib/bootstrap/dropdowns.less",
    "chars": 4645,
    "preview": "//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n  display:"
  },
  {
    "path": "server/public/css/lib/bootstrap/forms.less",
    "chars": 11018,
    "preview": "//\n// Forms\n// --------------------------------------------------\n\n// Normalize non-controls\n//\n// Restyle and baseline "
  },
  {
    "path": "server/public/css/lib/bootstrap/glyphicons.less",
    "chars": 14877,
    "preview": "//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus a"
  },
  {
    "path": "server/public/css/lib/bootstrap/grid.less",
    "chars": 1379,
    "preview": "//\n// Grid system\n// --------------------------------------------------\n\n// Container widths\n//\n// Set the container wid"
  },
  {
    "path": "server/public/css/lib/bootstrap/input-groups.less",
    "chars": 4219,
    "preview": "//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.i"
  },
  {
    "path": "server/public/css/lib/bootstrap/jumbotron.less",
    "chars": 901,
    "preview": "//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n  padding: @jumbotron-padding;\n  ma"
  },
  {
    "path": "server/public/css/lib/bootstrap/labels.less",
    "chars": 1084,
    "preview": "//\n// Labels\n// --------------------------------------------------\n\n.label {\n  display: inline;\n  padding: .2em .6em .3e"
  },
  {
    "path": "server/public/css/lib/bootstrap/list-group.less",
    "chars": 2619,
    "preview": "//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on <ul>, <ol"
  },
  {
    "path": "server/public/css/lib/bootstrap/media.less",
    "chars": 848,
    "preview": "// Media objects\n// Source: http://stubbornella.org/content/?p=497\n// --------------------------------------------------"
  },
  {
    "path": "server/public/css/lib/bootstrap/mixins.less",
    "chars": 26286,
    "preview": "//\n// Mixins\n// --------------------------------------------------\n\n// Utilities\n// -------------------------\n\n// Clearf"
  },
  {
    "path": "server/public/css/lib/bootstrap/modals.less",
    "chars": 3444,
    "preview": "//\n// Modals\n// --------------------------------------------------\n\n// .modal-open      - body class for killing the scr"
  },
  {
    "path": "server/public/css/lib/bootstrap/navbar.less",
    "chars": 13940,
    "preview": "//\n// Navbars\n// --------------------------------------------------\n\n// Wrapper and base class\n//\n// Provide a static na"
  },
  {
    "path": "server/public/css/lib/bootstrap/navs.less",
    "chars": 4926,
    "preview": "//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// ------------------------------------"
  },
  {
    "path": "server/public/css/lib/bootstrap/normalize.less",
    "chars": 7434,
    "preview": "/*! normalize.css v3.0.0 | MIT License | git.io/normalize */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prev"
  },
  {
    "path": "server/public/css/lib/bootstrap/pager.less",
    "chars": 857,
    "preview": "//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n  padding-left: 0;\n  margin: @li"
  },
  {
    "path": "server/public/css/lib/bootstrap/pagination.less",
    "chars": 1996,
    "preview": "//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n  display: inline-"
  },
  {
    "path": "server/public/css/lib/bootstrap/panels.less",
    "chars": 5553,
    "preview": "//\n// Panels\n// --------------------------------------------------\n\n// Base class\n.panel {\n  margin-bottom: @line-height"
  },
  {
    "path": "server/public/css/lib/bootstrap/popovers.less",
    "chars": 3378,
    "preview": "//\n// Popovers\n// --------------------------------------------------\n\n.popover {\n  position: absolute;\n  top: 0;\n  left:"
  },
  {
    "path": "server/public/css/lib/bootstrap/print.less",
    "chars": 1613,
    "preview": "//\n// Basic print styles\n// --------------------------------------------------\n// Source: https://github.com/h5bp/html5-"
  },
  {
    "path": "server/public/css/lib/bootstrap/progress-bars.less",
    "chars": 1589,
    "preview": "//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -----------------------"
  },
  {
    "path": "server/public/css/lib/bootstrap/responsive-utilities.less",
    "chars": 2008,
    "preview": "//\n// Responsive: Utility classes\n// --------------------------------------------------\n\n// IE10 in Windows (Phone) 8\n//"
  },
  {
    "path": "server/public/css/lib/bootstrap/scaffolding.less",
    "chars": 2268,
    "preview": "//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This r"
  },
  {
    "path": "server/public/css/lib/bootstrap/tables.less",
    "chars": 4470,
    "preview": "//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n  max-width: 100%;\n  background-color: @tab"
  },
  {
    "path": "server/public/css/lib/bootstrap/theme.less",
    "chars": 6854,
    "preview": "\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@"
  },
  {
    "path": "server/public/css/lib/bootstrap/thumbnails.less",
    "chars": 752,
    "preview": "//\n// Thumbnails\n// --------------------------------------------------\n\n// Mixin and adjust the regular image class\n.thu"
  },
  {
    "path": "server/public/css/lib/bootstrap/tooltip.less",
    "chars": 2590,
    "preview": "//\n// Tooltips\n// --------------------------------------------------\n\n\n// Base class\n.tooltip {\n  position: absolute;\n  "
  },
  {
    "path": "server/public/css/lib/bootstrap/type.less",
    "chars": 5754,
    "preview": "//\n// Typography\n// --------------------------------------------------\n\n// Headings\n// -------------------------\n\nh1, h2"
  },
  {
    "path": "server/public/css/lib/bootstrap/utilities.less",
    "chars": 780,
    "preview": "//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.c"
  },
  {
    "path": "server/public/css/lib/bootstrap/variables.less",
    "chars": 21261,
    "preview": "//\n// Variables\n// --------------------------------------------------\n\n//== Colors\n//\n//## Gray and brand colors for use"
  },
  {
    "path": "server/public/css/lib/bootstrap/wells.less",
    "chars": 527,
    "preview": "//\n// Wells\n// --------------------------------------------------\n\n\n// Base class\n.well {\n  min-height: 20px;\n  padding:"
  },
  {
    "path": "server/public/css/lib/bootstrap-social.less",
    "chars": 2710,
    "preview": "/*\n * Social Buttons for Bootstrap\n *\n * Copyright 2013-2014 Panayiotis Lipiridis\n * Licensed under the MIT License\n *\n "
  },
  {
    "path": "server/public/css/styles.less",
    "chars": 1616,
    "preview": "@import (less) \"lib/animate.css\";\n@import (less) \"lib/font-awesome.min.css\";\n@import \"lib/bootstrap/bootstrap\";\n@import "
  },
  {
    "path": "server/public/css/themes/default.less",
    "chars": 1704,
    "preview": "@import url(http://fonts.googleapis.com/css?family=Open+Sans:400italic,400,600,300,700,800);\n\n// Brand Colors\n// -------"
  },
  {
    "path": "server/public/css/themes/flatly.less",
    "chars": 27646,
    "preview": "// Flatly 3.1.1\n// Variables\n// --------------------------------------------------\n\n//== Colors\n//\n//## Gray and brand c"
  },
  {
    "path": "server/public/css/themes/ios7.less",
    "chars": 53308,
    "preview": "// iOS 7 Bootstrap Theme\n// -----------------------------------------------------\n\n// Variables  ======================="
  },
  {
    "path": "server/public/js/application.js",
    "chars": 426,
    "preview": "/**\n * This is a manifest file that will be compiled into application.js, which will\n * include all the files listed bel"
  },
  {
    "path": "server/public/js/main.js",
    "chars": 72,
    "preview": "$(document).ready(function() {\n\n  // Place JavaScript code here...\n\n});\n"
  },
  {
    "path": "server/test/app.js",
    "chars": 1003,
    "preview": "var request = require('supertest');\nvar app = require('../app.js');\n\ndescribe('GET /', function() {\n  it('should return "
  },
  {
    "path": "server/test/mocha.opts",
    "chars": 30,
    "preview": "--reporter spec\n--timeout 5000"
  },
  {
    "path": "server/test/models.js",
    "chars": 1034,
    "preview": "var chai = require('chai');\nvar should = chai.should();\nvar User = require('../models/User');\n\ndescribe('User Model', fu"
  },
  {
    "path": "server/views/404.jade",
    "chars": 1203,
    "preview": "doctype html\nhtml\n  head\n    title The page you were looking for doesn't exist (404)\n    style.\n      body {\n        bac"
  },
  {
    "path": "server/views/account/forgot.jade",
    "chars": 541,
    "preview": "extends ../layout\n\nblock content\n  .col-sm-8.col-sm-offset-2\n    form(method='POST')\n      legend Forgot Password\n      "
  },
  {
    "path": "server/views/account/login.jade",
    "chars": 1522,
    "preview": "extends ../layout\n\nblock content\n  form(method='POST')\n    legend Sign In\n    input(type='hidden', name='_csrf', value=_"
  },
  {
    "path": "server/views/account/profile.jade",
    "chars": 3741,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h3 Profile Information\n\n  form.form-horizontal(action='/account/prof"
  },
  {
    "path": "server/views/account/reset.jade",
    "chars": 640,
    "preview": "extends ../layout\n\nblock content\n  .col-sm-8.col-sm-offset-2\n    form(method='POST')\n      legend Reset Password\n      i"
  },
  {
    "path": "server/views/account/signup.jade",
    "chars": 902,
    "preview": "extends ../layout\n\nblock content\n  form.form-horizontal(id='signup-form', method='POST')\n    input(type='hidden', name='"
  },
  {
    "path": "server/views/api/aviary.jade",
    "chars": 1367,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-picture-o\n      | Aviary API\n  \n  .btn-group.btn-gr"
  },
  {
    "path": "server/views/api/clockwork.jade",
    "chars": 822,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-phone\n      | Clockwork SMS API\n\n  .btn-group.btn-g"
  },
  {
    "path": "server/views/api/facebook.jade",
    "chars": 1225,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-facebook-square(style='color: #335397')\n      | Fac"
  },
  {
    "path": "server/views/api/foursquare.jade",
    "chars": 1962,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-foursquare\n      | Foursquare API\n  \n  .btn-group.b"
  },
  {
    "path": "server/views/api/github.jade",
    "chars": 1294,
    "preview": "extends ../layout\n\nblock content\n  h2\n    i.fa.fa-github\n    | GitHub API\n\n  .btn-group.btn-group-justified\n    a.btn.bt"
  },
  {
    "path": "server/views/api/index.jade",
    "chars": 934,
    "preview": "extends ../layout\n\nblock content\n  h2 API Browser\n\n  ol\n    li\n      a(href='/api/aviary') Aviary\n    li\n      a(href='/"
  },
  {
    "path": "server/views/api/lastfm.jade",
    "chars": 1318,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-music\n      | Last.fm API\n\n  .btn-group.btn-group-j"
  },
  {
    "path": "server/views/api/linkedin.jade",
    "chars": 2212,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-linkedin-square\n      | LinkedIn API\n\n  .btn-group."
  },
  {
    "path": "server/views/api/nyt.jade",
    "chars": 1010,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-building-o\n      | New York Times API\n\n  .btn-group"
  },
  {
    "path": "server/views/api/paypal.jade",
    "chars": 903,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-dollar\n      | PayPal API\n\n  .btn-group.btn-group-j"
  },
  {
    "path": "server/views/api/scraping.jade",
    "chars": 600,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-crop\n      | Web Scraping\n\n  .btn-group.btn-group-j"
  },
  {
    "path": "server/views/api/steam.jade",
    "chars": 1376,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-gamepad\n      | Steam Web API\n\n  .btn-group.btn-gro"
  },
  {
    "path": "server/views/api/tumblr.jade",
    "chars": 753,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-tumblr-square\n      | Tumblr API\n\n  .btn-group.btn-"
  },
  {
    "path": "server/views/api/twilio.jade",
    "chars": 873,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-phone\n      | Twilio API\n\n  .btn-group.btn-group-ju"
  },
  {
    "path": "server/views/api/twitter.jade",
    "chars": 1054,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-twitter(style='color: #4099ff')\n      | Twitter API"
  },
  {
    "path": "server/views/api/venmo.jade",
    "chars": 2437,
    "preview": "extends ../layout\n\nblock content\n  .page-header\n    h2\n      i.fa.fa-money\n      | Venmo API\n\n  .btn-group.btn-group-jus"
  },
  {
    "path": "server/views/contact.jade",
    "chars": 824,
    "preview": "extends layout\n\nblock content\n  .page-header\n    h3 Contact Form\n\n  form.form-horizontal(role='form', method='POST')\n   "
  },
  {
    "path": "server/views/home.jade",
    "chars": 213,
    "preview": "extends layout\n\nblock content\n  h1 Bootstrap starter template\n  p.lead\n    | Use this document as a way to quickly start"
  },
  {
    "path": "server/views/layout.jade",
    "chars": 1010,
    "preview": "doctype html\nhtml\n  head\n    meta(charset='utf-8')\n    meta(http-equiv='X-UA-Compatible', content='IE=edge')\n    meta(na"
  },
  {
    "path": "server/views/partials/flash.jade",
    "chars": 362,
    "preview": "#flash\n  if messages.errors\n    .alert.alert-danger.animated.fadeIn\n      for error in messages.errors\n        div= erro"
  },
  {
    "path": "server/views/partials/footer.jade",
    "chars": 365,
    "preview": "#footer\n  .container\n    ul.list-inline.pull-left\n      li © 2014 Company, Inc.\n    ul.list-inline.pull-right\n      li\n "
  },
  {
    "path": "server/views/partials/navigation.jade",
    "chars": 1494,
    "preview": ".navbar.navbar-default.navbar-fixed-top\n  .container\n    .navbar-header\n      button.navbar-toggle(type='button', data-t"
  },
  {
    "path": "test/index.html",
    "chars": 632,
    "preview": "<!doctype html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\">\n   "
  },
  {
    "path": "test/lib/chai.js",
    "chars": 110818,
    "preview": "!function (name, context, definition) {\n  if (typeof require === 'function' && typeof exports === 'object' && typeof mod"
  },
  {
    "path": "test/lib/expect.js",
    "chars": 233,
    "preview": "/*!\n * chai\n * Copyright(c) 2011-2012 Jake Luer <jake@alogicalparadox.com>\n * MIT Licensed\n */\n\nmodule.exports = functio"
  },
  {
    "path": "test/lib/mocha/mocha.css",
    "chars": 3548,
    "preview": "@charset \"utf-8\";\n\nbody {\n  font: 20px/1.5 \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  padding: 60px 50px;\n}\n\n#moc"
  },
  {
    "path": "test/lib/mocha/mocha.js",
    "chars": 111002,
    "preview": ";(function(){\n\n\n// CommonJS require()\n\nfunction require(p){\n    var path = require.resolve(p)\n      , mod = require.modu"
  },
  {
    "path": "test/spec/test.js",
    "chars": 275,
    "preview": "/*global describe, it */\n'use strict';\n(function () {\n    describe('Give it some context', function () {\n        describ"
  },
  {
    "path": "todo.txt",
    "chars": 1126,
    "preview": "fix compression issue with codemirror\nfix issue where +/- buttons dont work on script node\nfont-awesome 404\n\ndo not allo"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the pboyer/flood GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 194 files (1.2 MB), approximately 342.4k tokens, and a symbol index with 165 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!