├── .bowerrc
├── .editorconfig
├── .gitattributes
├── .gitignore
├── Gruntfile.js
├── LICENSE
├── README.md
├── app
├── .htaccess
├── 404.html
├── app.html
├── customizer.html
├── images
│ ├── cloudbased.png
│ ├── csg.png
│ ├── glyphicons-halflings-white.png
│ ├── glyphicons-halflings.png
│ ├── math.png
│ ├── programming.png
│ ├── thumb.csg copy.png
│ ├── thumb.csg.png
│ ├── thumb.math copy.png
│ ├── thumb.math.png
│ ├── thumb.programming copy.png
│ ├── thumb.programming.png
│ ├── toggle_vis.png
│ └── vis_off.png
├── index.html
├── package.json
├── robots.txt
├── scripts
│ ├── collections
│ │ ├── .DS_Store
│ │ ├── 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
│ │ ├── .DS_Store
│ │ ├── 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
├── extra
└── screenshot.png
├── 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-social.less
│ │ │ ├── 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
│ │ │ └── font-awesome.min.css
│ │ ├── styles.less
│ │ └── themes
│ │ │ ├── default.less
│ │ │ ├── flatly.less
│ │ │ └── ios7.less
│ ├── fonts
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ └── fontawesome-webfont.woff
│ ├── img
│ │ ├── npm-logo.png
│ │ └── paypal.png
│ └── js
│ │ ├── application.js
│ │ ├── lib
│ │ ├── bootstrap.min.js
│ │ └── jquery-2.1.0.min.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
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "app/bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = false
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | temp
3 | .sass-cache
4 | app/bower_components
5 | .tmp/
6 | dist_desktop
7 | .DS_Store
8 | dist
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Peter Boyer 2013
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | ## flood
5 |
6 | ### What is it?
7 |
8 | 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).
9 |
10 | ### Features
11 |
12 | * Constructive solid geometry - Cube, cylinder, sphere, union, intersect, subtract
13 | * Formula node - evaluate javascript in a node
14 | * Never save your work
15 | * Fast node library search
16 | * Undo/redo across user sessions
17 | * Copy/paste
18 | * Multiple workspaces
19 | * Async evaluation
20 | * Partial function application
21 | * "Always on" continuous execution
22 |
23 | 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.
24 |
25 | ### Getting started
26 |
27 | 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.
28 |
29 | flood uses [require.js](http://requirejs.org/) to manage dependencies between JavaScript files and [backbone.js](http://backbonejs.org/) to stick it all together.
30 |
31 | flood also has a server written in node.js that handles user authentication and model synchronization.
32 |
33 | #### Installing dependencies for the app
34 |
35 | To install all of the dependencies for the flood app, run the following commands in the root directory:
36 |
37 | bower install
38 | npm install
39 |
40 | This will install all of the development dependencies for Grunt and all of the public dependencies with bower.
41 |
42 | #### Installing dependencies for the server
43 |
44 | To install all of the node.js dependencies for the flood server, run the following commands in the "server" directory:
45 |
46 | npm install
47 |
48 | 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).
49 |
50 |
51 | #### Running the server
52 |
53 | For development, I recommend using the great nodemon tool:
54 |
55 | npm install -g nodemon
56 |
57 | Go to the "server" directory and run:
58 |
59 | nodemon app.js
60 |
61 | You can also run the server using:
62 |
63 | node app.js
64 |
65 |
66 | #### Building for the web (outdated)
67 |
68 | The entire app can be compressed into lightweight, minified, and concatenated css, js, and html files using Grunt:
69 |
70 | grunt
71 |
72 |
73 | #### Building for the desktop
74 |
75 | flood can be used as a standalone application via node-webkit. Just do this:
76 |
77 | grunt desktop
78 |
79 | This will generate binaries for use on Mac and Windows in the dist_desktop folder.
80 |
81 |
82 | ### License
83 |
84 | The MIT License (MIT)
85 |
86 | Copyright (c) Peter Boyer 2014-2020
87 |
88 | Permission is hereby granted, free of charge, to any person obtaining a copy
89 | of this software and associated documentation files (the "Software"), to deal
90 | in the Software without restriction, including without limitation the rights
91 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
92 | copies of the Software, and to permit persons to whom the Software is
93 | furnished to do so, subject to the following conditions:
94 |
95 | The above copyright notice and this permission notice shall be included in
96 | all copies or substantial portions of the Software.
97 |
98 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
99 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
100 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
101 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
102 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
103 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
104 | THE SOFTWARE.
105 |
106 |
--------------------------------------------------------------------------------
/app/images/cloudbased.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/cloudbased.png
--------------------------------------------------------------------------------
/app/images/csg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/csg.png
--------------------------------------------------------------------------------
/app/images/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/app/images/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/glyphicons-halflings.png
--------------------------------------------------------------------------------
/app/images/math.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/math.png
--------------------------------------------------------------------------------
/app/images/programming.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/programming.png
--------------------------------------------------------------------------------
/app/images/thumb.csg copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/thumb.csg copy.png
--------------------------------------------------------------------------------
/app/images/thumb.csg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/thumb.csg.png
--------------------------------------------------------------------------------
/app/images/thumb.math copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/thumb.math copy.png
--------------------------------------------------------------------------------
/app/images/thumb.math.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/thumb.math.png
--------------------------------------------------------------------------------
/app/images/thumb.programming copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/thumb.programming copy.png
--------------------------------------------------------------------------------
/app/images/thumb.programming.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/thumb.programming.png
--------------------------------------------------------------------------------
/app/images/toggle_vis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/toggle_vis.png
--------------------------------------------------------------------------------
/app/images/vis_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/images/vis_off.png
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flood",
3 | "version": "0.0.4",
4 | "description": "A visual programming language for JavaScript, based on Scheme",
5 | "main": "index.html",
6 | "window": {
7 | "toolbar": false,
8 | "frame": true,
9 | "width": 1280,
10 | "height": 960
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org
2 |
3 | User-agent: *
4 |
--------------------------------------------------------------------------------
/app/scripts/collections/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/scripts/collections/.DS_Store
--------------------------------------------------------------------------------
/app/scripts/collections/Connections.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'Connection'], function(Backbone, Connection) {
2 |
3 | return Backbone.Collection.extend({
4 |
5 | model: Connection
6 |
7 | });
8 |
9 | });
--------------------------------------------------------------------------------
/app/scripts/collections/Nodes.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'Node'], function(Backbone, Node) {
2 |
3 | return Backbone.Collection.extend({
4 |
5 | model: Node,
6 |
7 | initialDragPositions: [],
8 |
9 | toJSON: function(){
10 |
11 | return this.models.map(function(x){
12 | return x.serialize();
13 | });
14 |
15 | },
16 |
17 | selectAll: function() {
18 | this.where({selected: false}).forEach( function(e){ e.set({selected: true}) } );
19 | },
20 |
21 | deselectAll: function() {
22 | this.where({selected: true}).forEach( function(e){
23 | e.set('selected', false);
24 | });
25 | },
26 |
27 | moveSelected: function(offset, masterEle) {
28 | var that = this;
29 | var count = 0;
30 | this.where({selected: true}).forEach( function(e, i){
31 | if (e === masterEle) return;
32 | var initialPos = that.initialDragPositions[count++];
33 | e.set('position', [initialPos[0] + offset[0], initialPos[1] + offset[1]]);
34 | });
35 | },
36 |
37 | startDragging: function(masterEle){
38 | this.initialDragPositions = [];
39 | var that = this;
40 | this.where({selected: true}).forEach( function(e){
41 | if (e === masterEle) return;
42 | that.initialDragPositions.push(e.get('position'));
43 | });
44 | }
45 |
46 | });
47 |
48 | });
49 |
50 |
--------------------------------------------------------------------------------
/app/scripts/collections/SearchElements.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'SearchElement', 'FLOOD'], function(Backbone, SearchElement, FLOOD) {
2 |
3 | return Backbone.Collection.extend({
4 |
5 | model: SearchElement,
6 |
7 | initialize: function(atts) {
8 | this.app = atts.app;
9 | },
10 |
11 | addCustomNode: function(customNode){
12 |
13 | var match = this.where({ functionId: customNode.functionId });
14 | if (match) this.remove( match );
15 |
16 | this.add(new SearchElement({name: customNode.functionName, functionName: customNode.functionName,
17 | isCustomNode: true, functionId: customNode.functionId, app: this.app, numInputs: customNode.inputs.length,
18 | numOutputs: customNode.outputs.length }));
19 |
20 | },
21 |
22 | fetch: function() {
23 |
24 | this.models.length = 0;
25 |
26 | for (var key in FLOOD.nodeTypes){
27 | this.models.push( new SearchElement({name: key, app: this.app}) );
28 | }
29 |
30 | }
31 |
32 | });
33 | });
34 |
35 |
--------------------------------------------------------------------------------
/app/scripts/collections/WorkspaceBrowserElements.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'WorkspaceBrowserElement'], function(Backbone, WorkspaceBrowserElement) {
2 |
3 | return Backbone.Collection.extend({
4 |
5 | url: '/ws',
6 | model: WorkspaceBrowserElement
7 |
8 | });
9 |
10 | });
--------------------------------------------------------------------------------
/app/scripts/collections/Workspaces.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'Workspace'], function(Backbone, Workspace) {
2 |
3 | return Backbone.Collection.extend({
4 |
5 | model: Workspace
6 |
7 | });
8 |
9 | });
10 |
--------------------------------------------------------------------------------
/app/scripts/customizer.js:
--------------------------------------------------------------------------------
1 | require(["config"], function() {
2 |
3 | require(['backbone', 'CustomizerApp', 'CustomizerAppView', 'Three', 'FLOODCSG', 'bootstrap' ], function (Backbone, CustomizerApp, CustomizerAppView, THREE) {
4 |
5 | var app = new CustomizerApp();
6 |
7 | app.fetch({
8 | error: function(result) {
9 | console.error('error');
10 | console.error(result);
11 | },
12 | success: function(){
13 | }
14 | });
15 |
16 | var appView = new CustomizerAppView({model: app});
17 |
18 | });
19 |
20 | });
21 |
22 |
--------------------------------------------------------------------------------
/app/scripts/lib/Viewport.js:
--------------------------------------------------------------------------------
1 | var container, $container;
2 |
3 | var camera, controls, scene, renderer;
4 |
5 | var geometry, group;
6 |
7 | var mouse = new THREE.Vector2(),
8 | offset = new THREE.Vector3(),
9 | INTERSECTED, SELECTED;
10 |
11 | var objects = [], plane;
12 |
13 | var mouseX = 0, mouseY = 0;
14 |
15 | var windowHalfX = window.innerWidth / 2;
16 | var windowHalfY = window.innerHeight / 2;
17 |
18 | init();
19 | render();
20 |
21 | function init() {
22 |
23 | container = document.getElementById("viewer");
24 | $container = $(container);
25 |
26 | camera = new THREE.PerspectiveCamera( 30, $container.width() / $container.height(), 1, 10000 );
27 |
28 | camera.position.set( 140, 140, 140 );
29 | camera.up.set( 0, 0, 1 );
30 | camera.lookAt( new THREE.Vector3(0,0,0) );
31 |
32 | scene = new THREE.Scene();
33 |
34 | renderer = new THREE.WebGLRenderer({antialias: true});
35 | renderer.setClearColor( 0xffffff, 1 );
36 | renderer.setSize( $container.width(), $container.height() );
37 | renderer.sortObjects = false;
38 |
39 | container.appendChild( renderer.domElement );
40 | renderer.domElement.setAttribute("id", "renderer_canvas");
41 |
42 | // add subtle ambient lighting
43 | var ambientLight = new THREE.AmbientLight(0x555555);
44 | scene.add(ambientLight);
45 |
46 | // add directional light source
47 | var directionalLight = new THREE.DirectionalLight(0xbbbbbb);
48 | directionalLight.position.set(50, 30, 50);
49 | scene.add(directionalLight);
50 |
51 | var directionalLight = new THREE.DirectionalLight(0xaaaaaa);
52 | directionalLight.position.set(-0.2, -0.8, 1).normalize();
53 | scene.add(directionalLight);
54 |
55 | makeGrid();
56 |
57 | controls = new THREE.OrbitControls(camera, container);
58 |
59 | window.addEventListener( 'resize', onWindowResize, false );
60 |
61 | // full screen blur
62 | // composer = new THREE.EffectComposer( renderer );
63 | // composer.addPass( new THREE.RenderPass( scene, camera ) );
64 |
65 | // hblur = new THREE.ShaderPass( THREE.HorizontalBlurShader );
66 | // composer.addPass( hblur );
67 |
68 | // vblur = new THREE.ShaderPass( THREE.VerticalBlurShader );
69 | // // set this shader pass to render to screen so we can see the effects
70 | // vblur.renderToScreen = true;
71 | // composer.addPass( vblur );
72 |
73 |
74 | animate();
75 |
76 | }
77 |
78 | function makeGrid(){
79 |
80 | var l = 60;
81 |
82 | var axisHelper = new THREE.AxisHelper( l );
83 | scene.add( axisHelper );
84 |
85 | var geometry = new THREE.Geometry();
86 | var geometryThick = new THREE.Geometry();
87 |
88 | var n = l;
89 | var inc = 2 * l / n;
90 | var rate = 10;
91 |
92 | for (var i = 0; i < n + 1; i++){
93 |
94 | var v1 = new THREE.Vector3(-l, -l + i * inc, 0);
95 | var v2 = new THREE.Vector3(l, -l + i * inc, 0);
96 |
97 | geometry.vertices.push(v1);
98 | geometry.vertices.push(v2);
99 |
100 | if (i % rate == 0){
101 | geometryThick.vertices.push(v1);
102 | geometryThick.vertices.push(v2);
103 | }
104 | }
105 |
106 | for (var i = 0; i < n + 1; i++){
107 | var v1 = new THREE.Vector3(-l + i * inc, l, 0);
108 | var v2 = new THREE.Vector3(-l + i * inc, -l, 0);
109 |
110 | geometry.vertices.push(v1);
111 | geometry.vertices.push(v2);
112 |
113 | if (i % rate == 0){
114 | geometryThick.vertices.push(v1);
115 | geometryThick.vertices.push(v2);
116 | }
117 | }
118 |
119 | var material = new THREE.LineBasicMaterial({
120 | color: 0xeeeeee,
121 | linewidth: 0.1
122 | });
123 |
124 | var materialThick = new THREE.LineBasicMaterial({
125 | color: 0xeeeeee,
126 | linewidth: 2
127 | });
128 |
129 | var line = new THREE.Line(geometry, material, THREE.LinePieces);
130 | var lineThick = new THREE.Line(geometryThick, materialThick, THREE.LinePieces);
131 |
132 | scene.add(line);
133 | scene.add(lineThick);
134 |
135 | }
136 |
137 | function onWindowResize() {
138 |
139 | windowHalfX = $container.width() / 2;
140 | windowHalfY = $container.height() / 2;
141 |
142 | camera.aspect = windowHalfX/ windowHalfY;
143 | camera.updateProjectionMatrix();
144 |
145 | renderer.setSize( 2*windowHalfX, 2*windowHalfY );
146 |
147 | render();
148 |
149 | }
150 |
151 | function animate() {
152 |
153 | requestAnimationFrame( animate );
154 | render();
155 |
156 | }
157 |
158 | var doBlur = true;
159 |
160 | function render() {
161 |
162 | controls.update();
163 | renderer.render( scene, camera );
164 |
165 | if (doBlur){
166 | // composer.render();
167 | }
168 |
169 | }
170 |
171 |
172 |
--------------------------------------------------------------------------------
/app/scripts/lib/flood/test/flood_lambda_test.js:
--------------------------------------------------------------------------------
1 | var FLOOD = new require('../flood.js')
2 | , assert = require('assert')
3 | , scheme = require('../scheme.js');
4 |
5 |
6 | (function(scheme, FLOOD) {
7 |
8 | var nodes = [];
9 |
10 | var input0 = new FLOOD.nodeTypes.Input("A");
11 | var input1 = new FLOOD.nodeTypes.Input("B");
12 |
13 | nodes.push(input0);
14 | nodes.push(input1);
15 |
16 | var add = new FLOOD.nodeTypes.Add();
17 | nodes.push(add);
18 |
19 | add.inputs[0].connect( input0 );
20 | add.inputs[1].connect( input1 );
21 |
22 | var mult = new FLOOD.nodeTypes.Multiply();
23 | nodes.push(mult);
24 |
25 | mult.inputs[0].connect( input0 );
26 | mult.inputs[1].connect( input1 );
27 |
28 | var output = new FLOOD.nodeTypes.Output("A");
29 | var output1 = new FLOOD.nodeTypes.Output("B");
30 | nodes.push(output);
31 | nodes.push(output1);
32 |
33 | // compile this into a function
34 | output.inputs[0].connect( add );
35 | output1.inputs[0].connect( mult );
36 |
37 | var lambda = FLOOD.compileNodesToLambda( nodes );
38 |
39 | var S = new scheme.Interpreter();
40 |
41 | var eres = S.eval( [ lambda, 5, 7 ] );
42 |
43 | assert.equal( eres[0], 12 );
44 | assert.equal( eres[1], 35 );
45 |
46 | })(scheme, FLOOD);
--------------------------------------------------------------------------------
/app/scripts/lib/flood/test/flood_runner_test.html:
--------------------------------------------------------------------------------
1 |
30 |
31 |
--------------------------------------------------------------------------------
/app/scripts/lib/flood/test/scheme_async_test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
151 |
152 |
--------------------------------------------------------------------------------
/app/scripts/lib/flood/test/scheme_eval_async_test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert')
2 | , async = require('../async.js')
3 | , scheme = require('../scheme.js');
4 |
5 |
6 | (function(scheme) {
7 |
8 | var S = new scheme.Interpreter();
9 |
10 | S.eval_async( ["begin", 1, 2, 4], undefined, function(res){
11 | console.log(res);
12 | });
13 |
14 | S.eval_async( [function(x){ return x * 3; }, [ function(){ return 2; } ] ], undefined, function(res){
15 | console.log(res);
16 | });
17 |
18 | S.eval_async( [ "quote", "cool yo" ], undefined, function(res){
19 | console.log(res);
20 | });
21 |
22 | // allows interpreter execution to not block!
23 |
24 | })(scheme);
--------------------------------------------------------------------------------
/app/scripts/main.js:
--------------------------------------------------------------------------------
1 | require(["config"], function() {
2 |
3 | require(['backbone', 'App', 'AppView', 'Three', 'Viewport', 'FLOODCSG', 'bootstrap'], function (Backbone, App, AppView) {
4 |
5 | var app = new App();
6 |
7 | app.fetch({
8 | error: function(result) {
9 | console.error('error');
10 | console.error(result);
11 | },
12 | success: function(){
13 | app.enableAutosave();
14 | }
15 | });
16 |
17 | var appView = new AppView({model: app});
18 |
19 | });
20 |
21 | });
22 |
23 |
--------------------------------------------------------------------------------
/app/scripts/models/Connection.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.Model.extend({
4 |
5 | idAttribute: "_id",
6 |
7 | defaults: {
8 | startNodeId: 0
9 | , endNodeId: 0
10 | , startPortIndex: 0
11 | , endPortIndex: 0
12 | , startProxy: false
13 | , endProxy: false
14 | , startProxyPosition: [0,0]
15 | , endProxyPosition: [0,0]
16 | , hidden: false
17 | },
18 |
19 | workspace : null,
20 | startNode: null,
21 | endNode: null,
22 |
23 | initialize: function(args, options){
24 |
25 | this.workspace = options.workspace;
26 |
27 | // proxyconnections bind to the proxyMove event on the workspace
28 | if ( args.startProxy || args.endProxy ) {
29 | this.workspace.bind('proxyMove', this.proxyMove, this);
30 | } else {
31 |
32 | // bind to end nodes
33 | this.startNode = this.workspace.get('nodes').get(args.startNodeId);
34 | this.endNode = this.workspace.get('nodes').get(args.endNodeId);
35 | }
36 |
37 | },
38 |
39 | getOpposite: function(startNode){
40 |
41 | if ( startNode === this.startNode )
42 | {
43 | return {node: this.endNode, portIndex: this.get('endPortIndex')};
44 | }
45 |
46 | if ( startNode === this.endNode )
47 | {
48 | return {node: this.startNode, portIndex: this.get('startPortIndex')};
49 | }
50 |
51 | return {};
52 |
53 | }
54 |
55 | });
56 |
57 | });
58 |
--------------------------------------------------------------------------------
/app/scripts/models/Feedback.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.Model.extend({
4 |
5 | defaults: {
6 | showing: false,
7 | failure: false,
8 | failureMessage: "Unidentified error"
9 | },
10 |
11 | send: function(data){
12 |
13 | if ( !data.subject ){
14 | this.set('failureMessage', 'Please supply a subject for your feedback!');
15 | this.set('failure', true);
16 | return;
17 | }
18 |
19 | var that = this;
20 | $.post('/feedback', data, function(e){
21 |
22 | if (e.length && e.length > 0 ) {
23 | that.set('failureMessage', e[0].msg);
24 | return that.set('failure', true);
25 | }
26 |
27 | that.set('failure', false);
28 | that.trigger('success');
29 | });
30 |
31 | }
32 |
33 | });
34 |
35 | });
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/scripts/models/GeometryExport.js:
--------------------------------------------------------------------------------
1 | define(['FileSaver'], function(FileSaver) {
2 |
3 | /// Adapted from: https://github.com/stephomi/sculptgl/blob/master/src/misc/ExportSTL.js
4 |
5 | 'use strict';
6 |
7 | var Export = {};
8 | var Utils = {};
9 |
10 | /** Export STL file */
11 | Export.toSTL = function (scene, filename) {
12 |
13 | // merge all Geometry in three.js scene into one big bag of triangles
14 | var numTris = 0;
15 | var vertices = [];
16 | var faces = [];
17 | var faceNormals = [];
18 | var vertOffset = 0;
19 |
20 | scene.traverse(function(ele) {
21 |
22 | if (!ele.visible || !(ele instanceof THREE.Mesh) ) return;
23 |
24 | // collect vertices
25 | ele.geometry.vertices.forEach(function(v){
26 | vertices.push( v.x );
27 | vertices.push( v.y );
28 | vertices.push( v.z );
29 | });
30 |
31 | // collect faces, face normals
32 | ele.geometry.faces.forEach(function(face){
33 | faces.push(vertOffset + face.a);
34 | faces.push(vertOffset + face.b);
35 | faces.push(vertOffset + face.c);
36 |
37 | faceNormals.push( face.normal.x );
38 | faceNormals.push( face.normal.y );
39 | faceNormals.push( face.normal.z );
40 |
41 | numTris += 1;
42 | });
43 |
44 | vertOffset += ele.geometry.vertices.length;
45 |
46 | });
47 |
48 | var blob = Export.toAsciiSTL(vertices, faces, faceNormals, numTris );
49 |
50 | FileSaver( blob, filename );
51 |
52 | };
53 |
54 | Utils.normalizeArrayVec3 = function (array, out) {
55 | var arrayOut = out || array;
56 | for (var i = 0, l = array.length; i < l; ++i) {
57 | var j = i * 3;
58 | var nx = array[j];
59 | var ny = array[j + 1];
60 | var nz = array[j + 2];
61 | var len = 1.0 / Math.sqrt(nx * nx + ny * ny + nz * nz);
62 | arrayOut[j] = nx * len;
63 | arrayOut[j + 1] = ny * len;
64 | arrayOut[j + 2] = nz * len;
65 | }
66 | return arrayOut;
67 | };
68 |
69 | /** Return a buffer array which is at least nbBytes long */
70 | Utils.getMemory = (function () {
71 | var pool = new ArrayBuffer(100000);
72 | return function (nbBytes) {
73 | if (pool.byteLength >= nbBytes)
74 | return pool;
75 | pool = new ArrayBuffer(nbBytes);
76 | return pool;
77 | };
78 | })();
79 |
80 | /** Export Ascii STL file */
81 | Export.toAsciiSTL = function (vAr, iAr, origFN, nbTriangles) {
82 |
83 | var faceNormals = new Float32Array(Utils.getMemory(origFN.length * 4), 0, origFN.length);
84 | Utils.normalizeArrayVec3(origFN, faceNormals);
85 | var data = 'solid mesh\n';
86 |
87 | for (var i = 0; i < nbTriangles; ++i) {
88 | var j = i * 3;
89 | data += ' facet normal ' + faceNormals[j] + ' ' + faceNormals[j + 1] + ' ' + faceNormals[j + 2] + '\n';
90 | data += ' outer loop\n';
91 | var iv1 = iAr[j] * 3;
92 | var iv2 = iAr[j + 1] * 3;
93 | var iv3 = iAr[j + 2] * 3;
94 | data += ' vertex ' + vAr[iv1] + ' ' + vAr[iv1 + 1] + ' ' + vAr[iv1 + 2] + '\n';
95 | data += ' vertex ' + vAr[iv2] + ' ' + vAr[iv2 + 1] + ' ' + vAr[iv2 + 2] + '\n';
96 | data += ' vertex ' + vAr[iv3] + ' ' + vAr[iv3 + 1] + ' ' + vAr[iv3 + 2] + '\n';
97 | data += ' endloop\n';
98 | data += ' endfacet\n';
99 | }
100 | data += 'endsolid mesh\n';
101 |
102 | return new Blob([data]);
103 | };
104 |
105 | return Export;
106 |
107 | });
--------------------------------------------------------------------------------
/app/scripts/models/Help.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.Model.extend({
4 |
5 | defaults: {
6 | sections:
7 | [ { title: "Workspace Tabs",
8 | targetId : "add-workspace-button",
9 | offset: [5, 5],
10 | text: "Switch between or add new active workspaces"
11 | },
12 | { title: "Node Library",
13 | targetId : "bottom-search",
14 | offset: [5, -110],
15 | text: "This is one place where you can add new nodes. You can also double click on the workspace to add nodes."
16 | },
17 | { title: "3D Focus Control",
18 | targetId : "workspace_hide",
19 | offset: [-170,-100],
20 | text: "Toggle between the 3D view and your workspace"
21 | },
22 | { title: "Workspace Browser",
23 | targetId : "workspace-browser-button",
24 | offset: [-170,0],
25 | text: "You can find all of your past work in the Workspace Browser"
26 | },
27 | { title: "Export STL",
28 | targetId : "export-button",
29 | offset: [20,-110],
30 | text: "Export all visible geometry to STL for 3D printing"
31 | },
32 | { title: "Share a customizer",
33 | targetId : "share-button",
34 | offset: [-200,10],
35 | text: "Share a customizer with anyone on the internet!"
36 | },
37 | // { title: "Zoom",
38 | // targetId : "zoomreset-button",
39 | // offset: [20,-110],
40 | // text: "Use this control the workspace zoom, or use Ctrl +, Ctrl -"
41 | // }
42 | ]
43 | }
44 |
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/app/scripts/models/Login.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.Model.extend({
4 |
5 | defaults: {
6 | isLoggedIn : false,
7 | failed : false,
8 | failureMessage : "",
9 | showing: false,
10 | email : ""
11 | },
12 |
13 | initialize: function(atts, vals) {
14 | this.app = vals.app;
15 | },
16 |
17 | fetch : function(){
18 |
19 | var that = this;
20 | $.get('/email', function(e){
21 | if (e.email) {
22 | that.set('email', e.email);
23 | that.set('isLoggedIn', true);
24 | } else {
25 | that.set('isLoggedIn', false);
26 | }
27 | });
28 |
29 | },
30 |
31 | toggle: function(){
32 | return this.get('showing') ? this.hide() : this.show();
33 | },
34 |
35 | show: function() {
36 | this.set('showing', true);
37 | },
38 |
39 | hide: function() {
40 | this.set('showing', false);
41 | },
42 |
43 | signup: function(data){
44 |
45 | var that = this;
46 | $.post('/signup', data, function(e){
47 |
48 | if (e.length && e.length > 0 ) {
49 | that.set('failureMessage', e[0].msg);
50 | return that.set('failed', true);
51 | }
52 |
53 | that.set('failed', false);
54 | that.app.fetch();
55 | });
56 |
57 | },
58 |
59 | login: function(data){
60 |
61 | var that = this;
62 | $.post('/login', data, function(e){
63 |
64 | if (e.length && e.length > 0 ) {
65 | that.set('failureMessage', e[0].msg);
66 | return that.set('failed', true);
67 | }
68 |
69 | that.set('failed', false);
70 | that.app.fetch();
71 | });
72 |
73 | },
74 |
75 | logout: function(){
76 |
77 | var that = this;
78 | $.get('/logout', {}, function(e){
79 | that.app.fetch();
80 | });
81 |
82 | }
83 |
84 | });
85 | });
86 |
--------------------------------------------------------------------------------
/app/scripts/models/Marquee.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.Model.extend({
4 |
5 | idAttribute: "_id",
6 |
7 | defaults: {
8 | x: 10
9 | , y: 10
10 | , width: 50
11 | , height: 50
12 | , hidden: true
13 | },
14 |
15 | startCorner: [10,10],
16 |
17 | endCorner: [20, 20],
18 |
19 | initialize: function(args, options){
20 |
21 | this.workspace = options.workspace;
22 |
23 | },
24 |
25 | updateRawValues: function(){
26 |
27 | this.set('width', Math.abs( this.startCorner[0] - this.endCorner[0] ) );
28 | this.set('height', Math.abs( this.startCorner[1] - this.endCorner[1] ) );
29 | this.set('x', Math.min( this.startCorner[0], this.endCorner[0] ) );
30 | this.set('y', Math.min( this.startCorner[1], this.endCorner[1] ) );
31 |
32 | },
33 |
34 | setStartCorner: function( posInWorkspace ){
35 |
36 | this.startCorner = posInWorkspace;
37 | this.endCorner = posInWorkspace;
38 |
39 | this.updateRawValues();
40 |
41 | },
42 |
43 | setEndCorner: function( posInWorkspace ){
44 |
45 | this.endCorner = posInWorkspace;
46 |
47 | this.updateRawValues();
48 |
49 | }
50 |
51 | });
52 |
53 | });
54 |
--------------------------------------------------------------------------------
/app/scripts/models/Search.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.Model.extend({
4 |
5 | defaults: {
6 |
7 | },
8 |
9 | initialize: function(atts, vals) {
10 |
11 | }
12 |
13 | });
14 |
15 | });
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/scripts/models/SearchElement.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.Model.extend({
4 |
5 | defaults: {
6 | name: null,
7 | isCustomNode: false,
8 | functionId: -1
9 | },
10 |
11 | initialize: function(a, b) {
12 | this.app = a.app;
13 | }
14 |
15 | });
16 |
17 | });
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/scripts/models/Share.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.Model.extend({
4 |
5 | defaults: {
6 | isProjectWorkspace: false,
7 | },
8 |
9 | initialize: function(atts, vals) {
10 |
11 | this.app = atts.app;
12 |
13 | // this.app.on('change:currentWorkspace', function(x){
14 | // console.log( this.app.getCurrentWorkspace() );
15 | // }, this);
16 |
17 | },
18 |
19 | });
20 |
21 | });
--------------------------------------------------------------------------------
/app/scripts/models/WorkspaceBrowser.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'WorkspaceBrowserElements'], function(Backbone, WorkspaceBrowserElements) {
2 |
3 | return Backbone.Model.extend({
4 |
5 | defaults: {
6 | workspaces: new WorkspaceBrowserElements()
7 | },
8 |
9 | initialize: function(atts, vals) {
10 | this.get('workspaces').fetch();
11 | },
12 |
13 | refresh: function(){
14 | this.get('workspaces').reset();
15 | this.get('workspaces').fetch();
16 | }
17 |
18 | });
19 | });
--------------------------------------------------------------------------------
/app/scripts/models/WorkspaceBrowserElement.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.Model.extend({
4 |
5 | idAttribute: "_id",
6 |
7 | defaults: {
8 | name: "Unnamed Workspace",
9 | isPublic: false,
10 | isCustomNode: false,
11 | lastSaved: Date.now()
12 | },
13 |
14 | initialize: function(atts, vals) {
15 |
16 | }
17 |
18 | });
19 | });
--------------------------------------------------------------------------------
/app/scripts/models/customizer/CustomizerApp.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'App'],
2 | function(Backbone, App){
3 |
4 | return App.extend({
5 |
6 | url: function() {
7 |
8 | // get the url from the page
9 | // customize this url
10 |
11 | var comps = document.URL.split('/customize-');
12 | return '/custdata/' + comps[ comps.length - 1];
13 | },
14 |
15 | parse : function(resp) {
16 |
17 | resp._id = 1;
18 |
19 | this.get('workspaces').add(resp, {app: this});
20 |
21 | var modresp = {};
22 |
23 | modresp.name = resp.name;
24 | modresp.currentWorkspace = 1;
25 |
26 | return modresp;
27 |
28 | },
29 |
30 | fetch : function(options){
31 |
32 | // this.login.fetch();
33 | Backbone.Model.prototype.fetch.call(this, options);
34 |
35 | },
36 |
37 | enableAutosave: function(){
38 |
39 | // this.get('workspaces').on('add remove', function(){ this.sync("update", this); }, this );
40 | // this.on('change:currentWorkspace', function(){ this.sync("update", this); }, this);
41 | // this.on('change:isFirstExperience', function(){ this.sync("update", this); }, this);
42 | // this.on('change:backgroundWorkspaces', function(){ this.sync("update", this); }, this);
43 |
44 | }
45 |
46 | });
47 |
48 | })
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/scripts/views/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/app/scripts/views/.DS_Store
--------------------------------------------------------------------------------
/app/scripts/views/ConnectionView.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.View.extend({
4 |
5 | template: _.template( $('#connection-template').html() ),
6 |
7 | workspaceView : null,
8 |
9 | initialize: function( args ) {
10 |
11 | this.model = args.model;
12 |
13 | if (args.isProxy ){
14 | this.isProxy = true;
15 | }
16 |
17 | this.workspace = args.workspace;
18 | this.workspaceView = args.workspaceView;
19 |
20 | if (this.model.startNode){
21 | this.model.startNode.on('change:ignoreDefaults', this.render, this);
22 | }
23 |
24 | },
25 |
26 | delegateEvents: function() {
27 |
28 | Backbone.View.prototype.delegateEvents.apply(this, arguments);
29 |
30 | if (!this.isProxy ){
31 | this.startId = this.model.get('startNodeId');
32 | this.endId = this.model.get('endNodeId');
33 | var nodes = this.workspace.get('nodes');
34 |
35 | if (nodes.get(this.endId) != undefined ){
36 | this.listenTo( nodes.get(this.endId), 'change:position resized', this.render);
37 | }
38 |
39 | if (nodes.get(this.startId) != undefined ){
40 | this.listenTo( nodes.get(this.startId), 'change:position resized', this.render);
41 | }
42 | }
43 |
44 | this.listenTo(this.model, 'change', this.render);
45 |
46 | },
47 |
48 | render: function() {
49 |
50 | this.makeCurveOnce();
51 |
52 | return this.updateControlPoints()
53 | .updateHidden()
54 | .updateColor();
55 |
56 | },
57 |
58 | updateControlPoints: function(){
59 | this.el.setAttribute('d', this.template( this.getControlPoints() ))
60 | return this;
61 | },
62 |
63 | updateHidden: function(){
64 |
65 | if (this.model.get('hidden')) {
66 | this.el.setAttribute('class','connection collapsed');
67 | } else {
68 | this.el.setAttribute('class','connection');
69 | }
70 |
71 | return this;
72 |
73 | },
74 |
75 | updateColor: function(){
76 |
77 | var startNode = this.model.startNode;
78 |
79 | if (!startNode) return this;
80 |
81 | if (startNode.isPartialFunctionApplication()){
82 | this.el.setAttribute('class','partial-function-connection');
83 | } else {
84 | this.el.setAttribute('class','connection');
85 | }
86 |
87 | return this;
88 | },
89 |
90 | curveInit: false,
91 |
92 | makeCurveOnce: function() {
93 |
94 | if (!this.curveInit) {
95 | var crv = document.createElementNS('http://www.w3.org/2000/svg','path');
96 | crv.setAttribute('class','connection');
97 | this.el = crv;
98 | this.$el = $(crv);
99 | this.curveInit = true;
100 | }
101 | return this.el;
102 |
103 | },
104 |
105 | // construct the control points for a bezier curve
106 | getControlPoints: function() {
107 |
108 | if (!this.model.get('startProxy') || !this.model.get('endProxy')) {
109 |
110 | var nodeViews = this.workspaceView.nodeViews
111 | , startId = this.model.get('startNodeId')
112 | , endId = this.model.get('endNodeId')
113 | , startPortIndex = this.model.get('startPortIndex')
114 | , endPortIndex = this.model.get('endPortIndex')
115 | }
116 |
117 | if (!this.model.get('startProxy')) {
118 | if (!this.workspaceView.nodeViews[startId] ){
119 | startId = this.model.workspace.get('proxyStartId');
120 | startPortIndex = this.model.workspace.get('proxyStartPortIndex');
121 | }
122 |
123 | startPos = this.workspaceView.nodeViews[startId].getPortPosition(startPortIndex, true);
124 | } else {
125 | startPos = this.model.get('startProxyPosition');
126 | }
127 |
128 | if (!this.model.get('endProxy')) {
129 | endPos = nodeViews[endId].getPortPosition(endPortIndex, false);
130 | } else {
131 | endPos = this.model.get('endProxyPosition');
132 | }
133 |
134 | var offset = 0.65 * Math.sqrt( Math.pow( endPos[0]-startPos[0], 2 ) + Math.pow( startPos[1]-endPos[1], 2 ) );
135 |
136 | return {
137 | aX : startPos[0]
138 | , aY : startPos[1]
139 | , bX : startPos[0] + offset
140 | , bY : startPos[1]
141 | , dX : endPos[0]
142 | , dY : endPos[1]
143 | , cX : endPos[0] - offset
144 | , cY : endPos[1]
145 | };
146 | }
147 |
148 | });
149 |
150 | });
151 |
--------------------------------------------------------------------------------
/app/scripts/views/FeedbackView.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | 'use strict';
4 |
5 | return Backbone.View.extend({
6 |
7 | el: '#feedback',
8 |
9 | events: { 'click #exit-feedback' : 'clickExit',
10 | 'submit #submit-feedback' : 'clickSend',
11 | 'click #submit-feedback' : 'clickSend',
12 | 'click' : 'clickExit',
13 | 'click .modal-box': 'stopPropagation'
14 | },
15 |
16 | template: _.template( $('#feedback-template').html() ),
17 |
18 | initialize: function( args, atts ) {
19 | this.app = atts.app;
20 |
21 | this.model.on('change:failure', this.fail, this );
22 | this.model.on('success', this.success, this );
23 | },
24 |
25 | render: function() {
26 |
27 | this.$el.html( this.template( this.model.toJSON() ) );
28 |
29 | this.subject = this.$el.find('#feedback-subject');
30 | this.message = this.$el.find('#feedback-message');
31 | this.failureView = this.$el.find('#feedback-failure-message');
32 | this.successView = this.$el.find('#feedback-success-message');
33 | this.sendingView = this.$el.find('#feedback-sending-message');
34 |
35 | return this;
36 |
37 | },
38 |
39 | stopPropagation: function(e){
40 | e.stopPropagation();
41 | },
42 |
43 | fail: function(){
44 |
45 | this.sendingView.hide();
46 |
47 | if (!this.failureView) return;
48 |
49 | if (!this.model.get('failure')) {
50 | this.failureView.hide();
51 | return;
52 | }
53 |
54 | this.failureView.html( this.model.get('failureMessage') );
55 | this.failureView.show();
56 |
57 | },
58 |
59 | success: function(){
60 |
61 | this.sendingView.hide();
62 | this.successView.show();
63 |
64 | var that = this;
65 |
66 | setTimeout(function(){
67 | that.app.set("showingFeedback", false);
68 |
69 | that.subject.val("");
70 | that.message.val("");
71 |
72 | that.successView.fadeOut();
73 | }, 800);
74 |
75 | },
76 |
77 | clickExit: function(e){
78 | this.app.set("showingFeedback", false);
79 | },
80 |
81 | clickSend: function(e) {
82 |
83 | e.preventDefault();
84 | this.sendingView.show();
85 | this.model.send({ subject: this.subject.val(), message: this.message.val() });
86 |
87 | }
88 |
89 | });
90 | });
--------------------------------------------------------------------------------
/app/scripts/views/HelpView.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.View.extend({
4 |
5 | el: '#help',
6 |
7 | events: { "click .exit-help": 'hide',
8 | 'click .enter-help-section' : 'clickEnterHelpSection',
9 | 'click .exit-all-help' : 'hide',
10 | 'click .exit-help-section' : 'clickExitHelpSection'
11 | },
12 |
13 | initialize: function( args, atts ) {
14 | this.app = atts.app;
15 | },
16 |
17 | render: function() {
18 |
19 | var template = _.template( $('#help-section-template').html() );
20 |
21 | var that = this;
22 |
23 | this.$el.empty();
24 |
25 | this.model.get('sections').forEach(function(section){
26 |
27 | var el = $('#' + section.targetId );
28 |
29 | if (!el) return;
30 |
31 | var offset = el.offset();
32 | var height = el.height();
33 | var width = el.width();
34 |
35 | if (section.offset[0] < 0) {
36 | width = -10;
37 | }
38 |
39 | if (section.offset[1] < 0) {
40 | height = -10;
41 | width -= 10;
42 | }
43 |
44 | section.elementPosition = [ offset.left + width, offset.top + height ];
45 | that.$el.append(template( section ));
46 |
47 | });
48 |
49 | return this;
50 |
51 | },
52 |
53 | getHelpSection: function(e){
54 |
55 | var ui = $(e.target);
56 | var attr = ui.attr('data-target-id');
57 |
58 | return this.$el.find(".help-section[data-target-id='" + attr + "']");
59 |
60 | },
61 |
62 | clickEnterHelpSection: function(e){
63 |
64 | var ele = this.getHelpSection(e);
65 | var ui = $(e.target);
66 |
67 | ele.fadeIn();
68 | ui.fadeOut();
69 |
70 | },
71 |
72 | clickExitHelpSection: function(e){
73 |
74 | var ele = this.getHelpSection(e);
75 | ele.fadeOut();
76 |
77 | },
78 |
79 | hide: function() {
80 | this.app.set('showingHelp', false);
81 | }
82 |
83 | });
84 | });
--------------------------------------------------------------------------------
/app/scripts/views/LoginView.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.View.extend({
4 |
5 | el: '#login',
6 |
7 | template: _.template( $('#login-template').html() ),
8 |
9 | events: { 'submit #login-form' : 'login',
10 | 'submit #signup-form' : 'signup',
11 | 'click #logout' : 'logout',
12 | "click .exit-login": 'hide',
13 | 'click #signup-tab-button': 'focusSignup',
14 | 'click #login-tab-button': 'focusLogin'},
15 |
16 | initialize: function( args, atts ) {
17 |
18 | this.app = atts.app;
19 | this.model.on('change:showing', this.render, this);
20 | this.model.on('change:isLoggedIn', this.render, this);
21 | this.model.on('change:failed', this.render, this);
22 | this.model.on('change:failureMessage', this.render, this);
23 |
24 | var that = this;
25 | $('#login-button').click(function(){ that.tabClick.call(that); });
26 | },
27 |
28 | rendered : false,
29 |
30 | render: function() {
31 |
32 | if ( !this.rendered ) {
33 | this.$el.find('.login-container').html( this.template( this.model.toJSON() ) );
34 | this.rendered = true;
35 |
36 | this.$el.find('.login-container').show();
37 | }
38 |
39 | var failureMessage = this.$el.find('#login-failure-message');
40 |
41 | if (this.model.get('failed')){
42 | failureMessage.html( this.model.get('failureMessage') );
43 | failureMessage.show();
44 | } else {
45 | failureMessage.hide();
46 | }
47 |
48 | if (this.model.get('showing') === true){
49 | this.$el.show();
50 | } else {
51 | this.$el.hide();
52 | }
53 |
54 | this.renderLoginState();
55 |
56 | return this;
57 | },
58 |
59 | focusSignup: function(e){
60 |
61 | this.model.set('failed', false);
62 | this.$el.find('#login-tab-button').removeClass('tab-button-hilite');
63 | this.$el.find('#signup-tab-button').addClass('tab-button-hilite');
64 | this.$el.find('#login-form').hide();
65 | this.$el.find('#signup-form').show();
66 |
67 | },
68 |
69 | focusLogin: function(e){
70 |
71 | this.model.set('failed', false);
72 | this.$el.find('#signup-tab-button').removeClass('tab-button-hilite');
73 | this.$el.find('#login-tab-button').addClass('tab-button-hilite');
74 | this.$el.find('#signup-form').hide();
75 | this.$el.find('#login-form').show();
76 | },
77 |
78 | renderLoginState: function(){
79 |
80 | if( this.model.get('isLoggedIn') ){
81 | this.model.hide();
82 | } else {
83 | this.$el.show();
84 | }
85 |
86 | return this;
87 | },
88 |
89 | tabClick: function(){
90 |
91 | if (this.model.get('isLoggedIn')){
92 | this.model.logout();
93 | } else {
94 | this.model.toggle();
95 | }
96 |
97 | },
98 |
99 | signup: function(e) {
100 | e.preventDefault();
101 | this.model.signup( this.$('#signup-form').serialize());
102 | },
103 |
104 | login: function(e) {
105 | e.preventDefault();
106 | this.model.login( this.$('#login-form').serialize() );
107 | }
108 |
109 | });
110 | });
--------------------------------------------------------------------------------
/app/scripts/views/MarqueeView.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.View.extend({
4 |
5 | workspaceView : null,
6 |
7 | initialize: function( args ) {
8 |
9 | this.model = args.model;
10 | this.workspace = args.workspace;
11 | this.workspaceView = args.workspaceView;
12 |
13 | this.listenTo(this.model, 'change', this.render);
14 |
15 | },
16 |
17 | render: function() {
18 |
19 | this.makeElementOnce();
20 |
21 | return this.updateCorners()
22 | .updateHidden()
23 | .updateColor()
24 | .updateStroke();
25 |
26 | },
27 |
28 | updateCorners: function(){
29 | this.el.setAttribute('d', this.template( this.getCorners() ))
30 | return this;
31 | },
32 |
33 | updateHidden: function(){
34 |
35 | if (this.model.get('hidden')) {
36 | this.el.setAttribute('class','marquee collapsed');
37 | } else {
38 | this.el.setAttribute('class','marquee');
39 | }
40 |
41 | return this;
42 |
43 | },
44 |
45 | updateColor: function(){
46 | return this;
47 | },
48 |
49 | curveInit: false,
50 |
51 | makeElementOnce: function() {
52 |
53 | if (!this.curveInit) {
54 |
55 | var crv = document.createElementNS('http://www.w3.org/2000/svg','rect');
56 | crv.setAttribute('class','marquee');
57 |
58 | this.updateCorners();
59 | this.updateStroke();
60 |
61 | this.el = crv;
62 | this.$el = $(crv);
63 | this.curveInit = true;
64 |
65 | }
66 | return this.el;
67 |
68 | },
69 |
70 | updateCorners: function() {
71 |
72 | this.el.setAttribute('x',this.model.get('x'));
73 | this.el.setAttribute('y',this.model.get('y'));
74 | this.el.setAttribute('width',this.model.get('width'));
75 | this.el.setAttribute('height',this.model.get('height'));
76 |
77 | return this;
78 |
79 | },
80 |
81 | updateStroke: function() {
82 |
83 | // we scale the stroke to avoid the marquee being overly thin or thick
84 | // when zoomed
85 | var zoom = 1 / this.workspace.get('zoom');
86 |
87 | this.el.setAttribute('stroke-dasharray', (zoom * 4) + "," + (zoom * 4) );
88 | this.el.setAttribute('stroke', zoom * 2 );
89 |
90 | return this;
91 | }
92 |
93 | });
94 |
95 | });
--------------------------------------------------------------------------------
/app/scripts/views/NodeViews/CustomNode.js:
--------------------------------------------------------------------------------
1 | define(['underscore', 'jquery', 'ThreeCSGNodeView'], function(_, $, ThreeCSGNodeView) {
2 |
3 | return ThreeCSGNodeView.extend({
4 |
5 | innerTemplate : _.template( $('#node-custom-template').html() ),
6 |
7 | getCustomContents: function() {
8 |
9 | var that = this;
10 |
11 | // open the parent workspace on double click
12 | this.$el.bind('dblclick', function(){
13 | this.model.workspace.app.openWorkspace( this.model.get('type').functionId );
14 | }.bind(this) );
15 |
16 | var js = this.model.toJSON() ;
17 | if (!js.extra.script) js.extra.script = this.model.get('type').script;
18 |
19 | return this.innerTemplate( js );
20 |
21 | }
22 |
23 | });
24 |
25 | });
--------------------------------------------------------------------------------
/app/scripts/views/NodeViews/Input.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'underscore', 'jquery', 'BaseNodeView'], function(Backbone, _, $, BaseNodeView) {
2 |
3 | return BaseNodeView.extend({
4 |
5 | template: _.template( $('#node-input-template').html() ),
6 |
7 | initialize: function(args) {
8 | BaseNodeView.prototype.initialize.apply(this, arguments);
9 |
10 | this.model.on('change:extra', function() {
11 |
12 | var ex = this.model.get('extra') ;
13 | var name = ex != undefined ? ex.name : "";
14 |
15 | this.silentSyncUI( name );
16 | this.model.trigger('updateRunner');
17 |
18 | }, this);
19 |
20 | },
21 |
22 | render: function(){
23 |
24 | BaseNodeView.prototype.render.apply(this, arguments);
25 |
26 | this.$el.addClass('input-node');
27 |
28 | var that = this;
29 | var extra = this.model.get('extra');
30 | var name = extra.name != undefined ? extra.name : "";
31 |
32 | this.inputText = this.$el.find(".text-input");
33 | this.inputText.val( name );
34 | this.inputText.change( function(e){ that.nameChanged.call(that, e); e.stopPropagation(); });
35 |
36 | return this;
37 |
38 | },
39 |
40 | nameChanged: function(){
41 | this.inputSet();
42 | this.model.workspace.trigger('updateRunner');
43 | },
44 |
45 | silentSyncUI: function(name){
46 |
47 | this.silent = true;
48 | this.inputText.val( name );
49 | this.silent = false;
50 |
51 | },
52 |
53 | inputSet: function(e,ui) {
54 |
55 | if ( this.silent ) return;
56 |
57 | var newValue = { name: this.inputText.val() };
58 | this.model.workspace.setNodeProperty({property: 'extra', _id: this.model.get('_id'), newValue: newValue });
59 |
60 | }
61 |
62 | });
63 |
64 | });
65 |
--------------------------------------------------------------------------------
/app/scripts/views/NodeViews/NodeViews.js:
--------------------------------------------------------------------------------
1 | define(['BaseNodeView', 'WatchNodeView', 'NumNodeView', 'ThreeCSGNodeView', 'ScriptView', 'OutputView', 'InputView','CustomNodeView'],
2 | function(BaseNodeView, WatchNodeView, NumNodeView, ThreeCSGNodeView, ScriptView, OutputView, InputView, CustomNodeView){
3 |
4 | var nodeViewTypes = {};
5 |
6 | nodeViewTypes.Base = ThreeCSGNodeView;
7 | nodeViewTypes.Show = WatchNodeView;
8 | nodeViewTypes.Number = NumNodeView;
9 | nodeViewTypes.CustomNode = CustomNodeView;
10 |
11 | nodeViewTypes.Script = ScriptView;
12 | nodeViewTypes.Input = InputView;
13 | nodeViewTypes.Output = OutputView;
14 |
15 | return nodeViewTypes;
16 |
17 | });
18 |
--------------------------------------------------------------------------------
/app/scripts/views/NodeViews/Output.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'underscore', 'jquery', 'BaseNodeView'], function(Backbone, _, $, BaseNodeView) {
2 |
3 | return BaseNodeView.extend({
4 |
5 | template: _.template( $('#node-output-template').html() ),
6 |
7 | initialize: function(args) {
8 | BaseNodeView.prototype.initialize.apply(this, arguments);
9 |
10 | this.model.on('change:extra', function() {
11 |
12 | var ex = this.model.get('extra') ;
13 | var name = ex != undefined ? ex.name : "";
14 |
15 | this.silentSyncUI( name );
16 | this.model.trigger('updateRunner');
17 |
18 | }, this);
19 |
20 | },
21 |
22 | render: function(){
23 |
24 | BaseNodeView.prototype.render.apply(this, arguments);
25 |
26 | this.$el.addClass('output-node');
27 |
28 | var that = this;
29 | var extra = this.model.get('extra');
30 | var name = extra.name != undefined ? extra.name : "";
31 |
32 | this.inputText = this.$el.find(".text-input");
33 | this.inputText.val( name );
34 | this.inputText.change( function(e){ that.nameChanged.call(that, e); e.stopPropagation(); });
35 |
36 | return this;
37 |
38 | },
39 |
40 | nameChanged: function(){
41 | this.inputSet();
42 | this.model.workspace.trigger('updateRunner');
43 | },
44 |
45 | silentSyncUI: function(name){
46 | this.silent = true;
47 | this.inputText.val( name );
48 | this.silent = false;
49 | },
50 |
51 | inputSet: function(e,ui) {
52 | if ( this.silent ) return;
53 |
54 | var newValue = { name: this.inputText.val() };
55 | this.model.workspace.setNodeProperty({property: 'extra', _id: this.model.get('_id'), newValue: newValue });
56 | }
57 |
58 | });
59 |
60 | });
61 |
--------------------------------------------------------------------------------
/app/scripts/views/NodeViews/Script.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'underscore', 'jquery', 'ThreeCSGNodeView', 'FLOOD', 'codemirror', 'codemirror/mode/javascript/javascript','codemirror/addon/hint/show-hint', 'codemirror/addon/hint/javascript-hint' ], function(Backbone, _, $, ThreeCSGNodeView, FLOOD, CodeMirror) {
2 |
3 | return ThreeCSGNodeView.extend({
4 |
5 | initialize: function(args) {
6 | ThreeCSGNodeView.prototype.initialize.apply(this, arguments);
7 | this.listenTo(this.model,'change:extra', this.onChangedExtra);
8 | },
9 |
10 | innerTemplate : _.template( $('#node-script-template').html() ),
11 |
12 | getCustomContents: function() {
13 |
14 | var js = this.model.toJSON() ;
15 | if (!js.extra.script) js.extra.script = this.model.get('type').script;
16 |
17 | return this.innerTemplate( js );
18 |
19 | },
20 |
21 | onChangedExtra: function(){
22 |
23 | var ex = this.model.get('extra') || {};
24 |
25 | if (ex.numInputs != undefined ){
26 | this.setNumInputConnections( ex.numInputs )
27 | this.model.get('type').setNumInputs( ex.numInputs );
28 | }
29 |
30 | this.render();
31 | this.model.trigger('updateRunner');
32 | this.model.workspace.run();
33 | },
34 |
35 | setNumInputConnections: function(num){
36 |
37 | if (num === undefined) return;
38 |
39 | var inputConns = this.model.get('inputConnections');
40 |
41 | var diff = num - inputConns.length;
42 |
43 | if (diff === 0) return;
44 |
45 | if (diff > 0){
46 | for (var i = 0; i < diff; i++){
47 | inputConns.push([]);
48 | }
49 | } else {
50 | for (var i = 0; i < -diff; i++){
51 |
52 | var conn = this.model.getConnectionAtIndex(inputConns.length - 1);
53 |
54 | if (conn != null){
55 | this.model.workspace.removeConnection(conn);
56 | }
57 |
58 | inputConns.pop();
59 | }
60 |
61 | }
62 |
63 | },
64 |
65 | extendScope: _.once(function(){
66 |
67 | var or = CodeMirror.hint.javascript;
68 | CodeMirror.hint.javascript = function(editor, options){
69 | options.globalScope = FLOOD.nodeTypes;
70 | return or(editor, options);
71 | }
72 | }),
73 |
74 | renderNode: function() {
75 | ThreeCSGNodeView.prototype.renderNode.apply(this, arguments);
76 |
77 | this.extendScope();
78 |
79 | var ta = this.$el.find('.script-text-input');
80 | var cm = CodeMirror.fromTextArea(ta[0],
81 | { extraKeys: {"Ctrl-Space": "autocomplete"},
82 | mode: { name :"javascript" }
83 | });
84 |
85 | var that = this;
86 |
87 | cm.on("focus",function(e){
88 | that.selectable = false;
89 | that.model.set('selected', false);
90 | });
91 |
92 | cm.on("change", function(){
93 | // we need to update the ports and connectors after moving the node
94 | setTimeout(function(){
95 | // update the position of the node ports
96 | that.renderPorts();
97 | // update the position of the connections
98 | that.model.trigger('resized');
99 | }, 0);
100 | });
101 |
102 | cm.on("blur",function(){
103 | var ex = JSON.parse( JSON.stringify( that.model.get('extra') ) );
104 | if ( ex.script === cm.getValue() ) return;
105 |
106 | ex.script = cm.getValue();
107 |
108 | that.model.workspace.setNodeProperty({property: "extra", _id: that.model.get('_id'), newValue: ex });
109 | that.selectable = true;
110 | });
111 |
112 | this.$el.find('.add-input').click(function(){ that.addInput.call(that); });
113 | this.$el.find('.remove-input').click(function(){ that.removeInput.call(that); });
114 |
115 | return this;
116 | },
117 |
118 | setNumInputsProperty: function(numInputs){
119 | if (numInputs === undefined) return;
120 |
121 | var ex = this.model.get('extra');
122 | var exCopy = JSON.parse( JSON.stringify( ex ) );
123 |
124 | exCopy.numInputs = numInputs;
125 | this.model.workspace.setNodeProperty({property: "extra", _id: this.model.get('_id'), newValue: exCopy, oldValue: ex });
126 | },
127 |
128 | addInput: function(){
129 |
130 | var type = this.model.get('type');
131 | var ex = this.model.get('extra');
132 | var numInputs = ex.numInputs;
133 |
134 | if (numInputs === undefined) numInputs = 0;
135 | if (type.inputs.length === 26) return;
136 |
137 | this.setNumInputsProperty(numInputs + 1);
138 |
139 | },
140 |
141 | removeInput: function(){
142 |
143 | var type = this.model.get('type');
144 | var ex = this.model.get('extra');
145 | var numInputs = ex.numInputs;
146 |
147 | if (numInputs === undefined) numInputs = 1;
148 | if (type.inputs.length === 0) return;
149 |
150 | this.setNumInputsProperty(numInputs - 1);
151 |
152 | }
153 |
154 | });
155 |
156 | });
157 |
--------------------------------------------------------------------------------
/app/scripts/views/NodeViews/Watch.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'underscore', 'jquery', 'BaseNodeView'], function(Backbone, _, $, BaseNodeView) {
2 |
3 | return BaseNodeView.extend({
4 |
5 | template: _.template( $('#node-watch-template').html() ),
6 |
7 | initialize: function(args) {
8 |
9 | BaseNodeView.prototype.initialize.apply(this, arguments);
10 | this.model.on( 'change:lastValue', this.renderNode, this );
11 | this.model.on( 'disconnection', this.renderNode, this );
12 |
13 | },
14 |
15 | renderNode: function(){
16 |
17 | var pretty = this.model.get('lastValue') != undefined ? JSON.stringify(this.model.get('lastValue'), this.prettyPrint, 2) : this.model.get('lastValue');
18 | this.model.set('prettyValue', pretty );
19 |
20 | return BaseNodeView.prototype.renderNode.apply(this, arguments);
21 |
22 | },
23 |
24 | prettyPrint: function(key, val){
25 |
26 | if (typeof val === "number"){
27 | return val.toPrecision(4);
28 | }
29 |
30 | if (typeof val === "string"){
31 | return val.replace(new RegExp("\t", 'g'), "").replace(new RegExp("\n", 'g'), "
")
32 | }
33 |
34 | return val;
35 | }
36 |
37 | });
38 |
39 | });
40 |
--------------------------------------------------------------------------------
/app/scripts/views/SearchElementView.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.View.extend({
4 |
5 | tagName: 'li',
6 | className: 'search-element',
7 |
8 | template: _.template( $('#search-element-template').html() ),
9 |
10 | events: {
11 | 'click': 'click'
12 | },
13 |
14 | initialize: function(a, arr){
15 | this.app = arr.app;
16 | this.appView = arr.appView;
17 | this.elementClick = arr.click;
18 | },
19 |
20 | render: function() {
21 | this.$el.html( this.template( this.model.toJSON() ) );
22 | },
23 |
24 | click: function(e) {
25 | if (!this.elementClick) return;
26 | this.elementClick(this);
27 | }
28 |
29 | });
30 |
31 | });
--------------------------------------------------------------------------------
/app/scripts/views/SearchView.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'List', 'SearchElementView'], function(Backbone, List, SearchElementView) {
2 |
3 | return Backbone.View.extend({
4 |
5 | tagName: 'div',
6 | className: 'search-container row',
7 |
8 | initialize: function(atts, arr) {
9 | this.app = arr.app;
10 | this.app.on('change:showingSearch', this.highlightInput, this );
11 | this.app.SearchElements.on('add remove', this.render, this);
12 | },
13 |
14 | template: _.template( $('#search-template').html() ),
15 |
16 | events: {
17 | 'keyup .library-search-input': 'searchKeyup'
18 | },
19 |
20 | render: function(arg) {
21 |
22 | this.$el.html( this.template( this.model.toJSON() ) );
23 |
24 | this.$input = this.$('.library-search-input');
25 | this.$list = this.$('.search-list');
26 |
27 | this.$list.empty();
28 |
29 | var that = this;
30 |
31 | this.app.SearchElements.forEach(function(ele) {
32 |
33 | if (ele.name === null) return;
34 |
35 | var eleView = new SearchElementView({ model: ele }, { app: that.app, click: function(e){ that.elementClick.call(that, e) } });
36 |
37 | eleView.render();
38 | that.$list.append( eleView.$el );
39 |
40 | });
41 |
42 | var options = {
43 | valueNames: [ 'name' ]
44 | };
45 |
46 | this.list = new List(this.el, options);
47 |
48 | },
49 |
50 | highlightInput: function(){
51 |
52 | if (this.app.get('showingSearch')) {
53 | this.$input.focus().select();
54 | } else {
55 | this.$input.blur();
56 | }
57 | },
58 |
59 | addNode: function(name){
60 | this.app.getCurrentWorkspace().addNodeByNameAndPosition( name, this.app.newNodePosition );
61 | },
62 |
63 | elementClick: function(ele){
64 |
65 | this.addNode(ele.model.get('name'));
66 | this.app.set('showingSearch', false);
67 |
68 | },
69 |
70 | searchKeyup: function(event) {
71 |
72 | if ( event.keyCode === 13) { // enter key causes first result to be inserted
73 | var nodeName = this.$list.find('.search-element').first().find('.name').first().html();
74 | if (nodeName === undefined ) return;
75 |
76 | this.addNode( nodeName );
77 | this.app.set('showingSearch', false);
78 |
79 | } else if ( event.keyCode === 27) { // esc key exits search
80 | this.app.set('showingSearch', false);
81 | }
82 | }
83 |
84 | });
85 |
86 | });
87 |
88 |
--------------------------------------------------------------------------------
/app/scripts/views/ShareView.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.View.extend({
4 |
5 | el: '#share',
6 |
7 | events: {
8 | 'click #exit-share' : 'clickExit',
9 | 'click' : 'clickExit',
10 | 'click .modal-box': 'stopPropagation',
11 | 'click .is-customizer-checkbox': 'checkboxChanged'
12 | },
13 |
14 | template: _.template( $('#share-template').html() ),
15 |
16 | initialize: function( args, atts ) {
17 | this.app = atts.app;
18 |
19 | this.model.on('success', this.success, this );
20 | },
21 |
22 | stopPropagation: function(e){
23 | e.stopPropagation();
24 | },
25 |
26 | render: function() {
27 |
28 | var ws = this.app.getCurrentWorkspace();
29 |
30 | this.$el.html( this.template({
31 | name: ws.get('name'),
32 | isCustomizer: ws.get('isCustomizer'),
33 | url: ws.getCustomizerUrl()
34 | }) );
35 |
36 | if (!ws.isCustomizer){
37 | // we defer this as the input isn't necessarily yet in the dom
38 | _.defer(function(){ this.$el.find('.customizer-url').focus().select(); }.bind(this) );
39 | }
40 |
41 | this.updatePublicPrivate();
42 |
43 | return this;
44 |
45 | },
46 |
47 | updatePublicPrivate: function(){
48 |
49 | if ( this.app.getCurrentWorkspace().get('isCustomizer') ){
50 | this.$el.find('.is-customizer-checkbox').removeClass("fa-toggle-off");
51 | this.$el.find('.is-customizer-checkbox').addClass("fa-toggle-on");
52 | this.$el.find('.public').show();
53 | this.$el.find('.private').hide();
54 | } else {
55 | this.$el.find('.is-customizer-checkbox').removeClass("fa-toggle-on");
56 | this.$el.find('.is-customizer-checkbox').addClass("fa-toggle-off");
57 | this.$el.find('.public').hide();
58 | this.$el.find('.private').show();
59 | }
60 |
61 | },
62 |
63 | checkboxChanged: function(){
64 |
65 | var ws = this.app.getCurrentWorkspace();
66 | ws.set('isCustomizer', !ws.get('isCustomizer') );
67 |
68 | this.updatePublicPrivate();
69 |
70 | },
71 |
72 | clickExit: function(e){
73 | this.app.set("showingShare", false);
74 | }
75 |
76 | });
77 | });
--------------------------------------------------------------------------------
/app/scripts/views/WorkspaceBrowserElementView.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.View.extend({
4 |
5 | tagName: 'li',
6 | className: 'workspace-browser-element',
7 |
8 | template: _.template( $('#workspace-browser-element-template').html() ),
9 |
10 | events: {
11 | 'click': 'clickOpen',
12 | 'click .delete-workspace': 'clickDelete',
13 | 'mouseenter': 'mouseenter',
14 | 'mouseleave': 'mouseleave'
15 | },
16 |
17 | initialize: function(_, vals){
18 | this.app = vals.app;
19 | },
20 |
21 | render: function() {
22 |
23 | var v = this.model.toJSON();
24 | var lastSave = this.model.get('lastSaved');
25 |
26 | if (typeof lastSave === "string") {
27 | v["prettyDate"] = this.prettyDate( new Date(lastSave) );
28 | } else {
29 | v["prettyDate"] = "Unknown";
30 | }
31 |
32 | this.$el.html( this.template( v ) );
33 | this.$open = this.$el.find('.workspace-browser-element-open');
34 |
35 | },
36 |
37 | prettyDate: function(date){
38 |
39 | var diff = (((new Date()).getTime() - date.getTime()) / 1000),
40 | day_diff = Math.floor(diff / 86400);
41 |
42 | if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
43 | return;
44 |
45 | return day_diff == 0 && (
46 | diff < 60 && "just now" ||
47 | diff < 120 && "1 minute ago" ||
48 | diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
49 | diff < 7200 && "1 hour ago" ||
50 | diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
51 | day_diff == 1 && "Yesterday" ||
52 | day_diff < 7 && day_diff + " days ago" ||
53 | day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago";
54 | },
55 |
56 | clickOpen: function(e) {
57 | this.app.openWorkspace( this.model.get('_id') );
58 | },
59 |
60 | clickDelete: function(e){
61 |
62 | }
63 |
64 | });
65 |
66 | });
--------------------------------------------------------------------------------
/app/scripts/views/WorkspaceBrowserView.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'WorkspaceBrowserElementView'], function(Backbone, WorkspaceBrowserElementView) {
2 |
3 | return Backbone.View.extend({
4 |
5 | el: '#workspace-browser',
6 |
7 | initialize: function(atts, arr) {
8 | this.app = arr.app;
9 |
10 | this.model.get('workspaces').on('reset', this.render, this );
11 | this.model.get('workspaces').on('add', this.addWorkspaceElement, this );
12 | this.model.get('workspaces').on('remove', this.removeWorkspaceElement, this );
13 |
14 | this.render();
15 | },
16 |
17 | template: _.template( $('#workspace-browser-template').html() ),
18 |
19 | events: {
20 | 'click .workspace-browser-refresh': "refreshClick",
21 | 'click #workspace-browser-header-custom-nodes': "customNodeHeaderClick",
22 | 'click #workspace-browser-header-projects': "projectHeaderClick"
23 | },
24 |
25 | render: function(arg) {
26 |
27 | if (!this.rendered) {
28 | this.$el.html( this.template( this.model.toJSON() ) );
29 | this.contents = this.$el.find('#workspace-browser-contents');
30 | this.customNodes = this.$el.find('#workspace-browser-custom-nodes');
31 | this.projects = this.$el.find('#workspace-browser-projects');
32 | }
33 |
34 | this.rendered = true;
35 |
36 | this.customNodes.empty();
37 | this.projects.empty();
38 |
39 | },
40 |
41 | refreshClick: function(e){
42 | this.model.refresh();
43 | e.stopPropagation();
44 | },
45 |
46 | customNodeHeaderClick: function(e){
47 |
48 | // do not resize when refresh is clicked
49 | if ( $(e.target).hasClass('workspace-browser-refresh') ) return;
50 |
51 | this.projects.hide();
52 | this.customNodes.show();
53 |
54 | $('#workspace-browser-header-custom-nodes').css('bottom','').css('top','40px');
55 |
56 | },
57 |
58 | projectHeaderClick: function(e){
59 |
60 | if ( $(e.target).hasClass('workspace-browser-refresh') ) return;
61 |
62 | this.customNodes.hide();
63 | this.projects.show();
64 |
65 | $('#workspace-browser-header-custom-nodes').css('bottom','0').css('top','');
66 |
67 | },
68 |
69 | addWorkspaceElement: function(x){
70 |
71 | if (!this.contents) this.render();
72 |
73 | var v = new WorkspaceBrowserElementView( { model: x }, { app : this.app } );
74 | v.render();
75 |
76 | if ( x.get('isCustomNode') ){
77 | this.customNodes.append( v.$el );
78 | } else {
79 | this.projects.append( v.$el );
80 | }
81 |
82 | },
83 |
84 | removeWorkspaceElement: function(ws){
85 |
86 | if (!this.contents) return;
87 | this.contents.find('.workspace-browser-element[data-id*=' + ws.get('_id') + ']').remove();
88 |
89 | }
90 |
91 | });
92 |
93 | });
94 |
95 |
--------------------------------------------------------------------------------
/app/scripts/views/WorkspaceTabView.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.View.extend({
4 |
5 | tagName: 'div',
6 | className: 'workspace-tab',
7 |
8 | initialize: function(atts) {
9 |
10 | this.app = this.model.app;
11 | this.listenTo( this.model, 'change:name', this.render);
12 | this.listenTo( this.model, 'change:current', this.render);
13 |
14 | },
15 |
16 | template: _.template( $('#workspace-tab-template').html() ),
17 |
18 | events: {
19 |
20 | 'click': 'click',
21 | 'click .remove-button': 'remove',
22 | 'mouseover': 'showEditButton',
23 | 'mouseout': 'hideEditButton',
24 | 'click .edit-button': 'startEdit',
25 | 'blur .workspace-name': 'endEdit',
26 |
27 | // touch
28 | 'touchstart .edit-button': 'startEdit',
29 | 'touchstart': 'toggleShowingEditButton'
30 |
31 | },
32 |
33 | render: function() {
34 |
35 | this.$el.html( this.template( this.model.toJSON() ) );
36 |
37 | if (this.model.get('current') === true){
38 | this.$el.addClass('current-workspace')
39 | } else {
40 | this.$el.removeClass('current-workspace')
41 | }
42 |
43 | this.$input = this.$('.workspace-name');
44 |
45 | },
46 |
47 | toggleShowingEditButton: function(){
48 |
49 | if ( this.editButtonShown ){
50 | this.editButtonShown = false;
51 | this.hideEditButton();
52 | } else {
53 | this.editButtonShown = true;
54 | this.showEditButton();
55 | }
56 |
57 | },
58 |
59 | showEditButton: function() {
60 | this.$('.edit-button').css('visibility', 'visible');
61 | },
62 |
63 | hideEditButton: function() {
64 | this.$('.edit-button').css('visibility', 'hidden');
65 | },
66 |
67 | startEdit: function(e) {
68 |
69 | this.$input.prop('disabled', false);
70 | this.$input.focus().select();
71 | this.$input.css('pointer-events', 'auto');
72 |
73 | e.stopPropagation();
74 | },
75 |
76 | endEdit: function() {
77 |
78 | // the edit button is still visible on touch devices
79 | this.hideEditButton();
80 |
81 | this.$input.prop('disabled', true);
82 | this.$input.css('pointer-events', 'none');
83 | this.model.set('name', this.$input.val() );
84 | },
85 |
86 | click: function(e) {
87 | this.model.app.set('currentWorkspace', this.model.get('_id'));
88 | },
89 |
90 | remove: function(e){
91 | this.model.app.get('workspaces').remove(this.model);
92 | e.stopPropagation();
93 | }
94 |
95 | });
96 |
97 | });
--------------------------------------------------------------------------------
/app/scripts/views/customizer/CustomizerAppView.js:
--------------------------------------------------------------------------------
1 | // the application is constituted by
2 |
3 | // CustomizerAppView, CustomizerApp - controls visibility of widget
4 | // CustomizerWorkspaceView - collection of node widgets, similar to a nodeview, includes mask for which widgets will be visible
5 | // CustomizerNodeView - similar to a nodeview, but no inputs/outputs, simplified controls, potentially multiple options with labels
6 | // CustomizerViewer - allows you to view your customized geometry, with saved camera position
7 |
8 | define(['backbone', 'CustomizerViewerView', 'CustomizerHeaderView', 'CustomizerWorkspaceView'],
9 | function(Backbone, CustomizerViewer, CustomizerHeaderView, CustomizerWorkspaceView ) {
10 |
11 | 'use strict';
12 |
13 | return Backbone.View.extend({
14 |
15 | el: '#customizer-app ',
16 |
17 | events: { },
18 |
19 | initialize: function( args, atts ) {
20 | this.listenTo(this.model, 'change', this.render);
21 | },
22 |
23 | render: _.once(function() {
24 |
25 | (new CustomizerViewer()).render();
26 | var hv = new CustomizerHeaderView({model: this.model.getCurrentWorkspace() })
27 | this.listenTo( hv, "download-stl", function(){ this.model.getCurrentWorkspace().exportSTL() } );;
28 | hv.render();
29 | (new CustomizerWorkspaceView({model: this.model.getCurrentWorkspace() })).render();
30 |
31 | return this;
32 |
33 | }),
34 |
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/app/scripts/views/customizer/CustomizerHeaderView.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.View.extend({
4 |
5 | el: '#customizer-header',
6 |
7 | template: _.template( $('#header-template').html() ),
8 |
9 | events: { "click .stl-download" : "downloadStl" },
10 |
11 | initialize: function( args, atts ) {
12 | this.listenTo(this.model, 'change', this.render);
13 | },
14 |
15 | render: function() {
16 |
17 | this.$el.html( this.template( this.model.toJSON() ) );
18 | return this;
19 |
20 | },
21 |
22 | downloadStl: function() {
23 | this.trigger("download-stl")
24 | }
25 |
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/app/scripts/views/customizer/CustomizerViewerView.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'Three', 'OrbitControls'], function(Backbone) {
2 |
3 | var container, $container;
4 | var camera, controls, renderer;
5 | var geometry, group;
6 |
7 | scene = {};
8 |
9 | var mouse = new THREE.Vector2(),
10 | offset = new THREE.Vector3(),
11 | INTERSECTED, SELECTED;
12 |
13 | var objects = [], plane;
14 |
15 | var mouseX = 0, mouseY = 0;
16 | var animate;
17 |
18 | var windowHalfX = window.innerWidth / 2;
19 | var windowHalfY = window.innerHeight / 2;
20 |
21 | return Backbone.View.extend({
22 |
23 | el: '#customizer-viewer',
24 |
25 | events: { },
26 |
27 | initialize: function( args, atts ) {
28 |
29 | },
30 |
31 | done: false,
32 |
33 | render: _.once(function() {
34 |
35 | this.init();
36 | this.renderView();
37 |
38 | }),
39 |
40 | init: function() {
41 |
42 | container = document.getElementById("customizer-viewer");
43 | $container = $(container);
44 |
45 | camera = new THREE.PerspectiveCamera( 30, $container.width() / $container.height(), 1, 10000 );
46 |
47 | camera.position.set( 140, 140, 140 );
48 | camera.up.set( 0, 0, 1 );
49 | camera.lookAt( new THREE.Vector3(0,0,0) );
50 |
51 | scene = new THREE.Scene();
52 |
53 | renderer = new THREE.WebGLRenderer({antialias: true});
54 | renderer.setClearColor( 0xffffff, 1 );
55 | renderer.setSize( $container.width(), $container.height() );
56 | renderer.sortObjects = false;
57 |
58 | container.appendChild( renderer.domElement );
59 | renderer.domElement.setAttribute("id", "renderer_canvas");
60 |
61 | // add subtle ambient lighting
62 | var ambientLight = new THREE.AmbientLight(0x555555);
63 | scene.add(ambientLight);
64 |
65 | // add directional light source
66 | var directionalLight = new THREE.DirectionalLight(0xbbbbbb);
67 | directionalLight.position.set(50, 30, 50);
68 | scene.add(directionalLight);
69 |
70 | var directionalLight = new THREE.DirectionalLight(0xaaaaaa);
71 | directionalLight.position.set(-0.2, -0.8, 1).normalize();
72 | scene.add(directionalLight);
73 |
74 | this.makeGrid();
75 |
76 | controls = new THREE.OrbitControls(camera, container);
77 |
78 | var that = this;
79 |
80 | animate = function(){
81 | requestAnimationFrame( animate );
82 | that.renderView();
83 | };
84 |
85 | requestAnimationFrame(animate);
86 |
87 | window.addEventListener( 'resize', this.onWindowResize.bind(this), false );
88 |
89 | },
90 |
91 | onWindowResize : function() {
92 |
93 | windowHalfX = $container.width() / 2;
94 | windowHalfY = $container.height() / 2;
95 |
96 | camera.aspect = windowHalfX/ windowHalfY;
97 | camera.updateProjectionMatrix();
98 |
99 | renderer.setSize( 2*windowHalfX, 2*windowHalfY );
100 |
101 | this.render();
102 |
103 | },
104 |
105 | renderView: function() {
106 |
107 | controls.update();
108 | renderer.render( scene, camera );
109 |
110 | },
111 |
112 | makeGrid: function(){
113 |
114 | var l = 60;
115 |
116 | var axisHelper = new THREE.AxisHelper( l );
117 | scene.add( axisHelper );
118 |
119 | var geometry = new THREE.Geometry();
120 | var geometryThick = new THREE.Geometry();
121 |
122 | var n = l;
123 | var inc = 2 * l / n;
124 | var rate = 10;
125 |
126 | for (var i = 0; i < n + 1; i++){
127 |
128 | var v1 = new THREE.Vector3(-l, -l + i * inc, 0);
129 | var v2 = new THREE.Vector3(l, -l + i * inc, 0);
130 |
131 | geometry.vertices.push(v1);
132 | geometry.vertices.push(v2);
133 |
134 | if (i % rate == 0){
135 | geometryThick.vertices.push(v1);
136 | geometryThick.vertices.push(v2);
137 | }
138 | }
139 |
140 | for (var i = 0; i < n + 1; i++){
141 | var v1 = new THREE.Vector3(-l + i * inc, l, 0);
142 | var v2 = new THREE.Vector3(-l + i * inc, -l, 0);
143 |
144 | geometry.vertices.push(v1);
145 | geometry.vertices.push(v2);
146 |
147 | if (i % rate == 0){
148 | geometryThick.vertices.push(v1);
149 | geometryThick.vertices.push(v2);
150 | }
151 | }
152 |
153 | var material = new THREE.LineBasicMaterial({
154 | color: 0xeeeeee,
155 | linewidth: 0.1
156 | });
157 |
158 | var materialThick = new THREE.LineBasicMaterial({
159 | color: 0xeeeeee,
160 | linewidth: 2
161 | });
162 |
163 | var line = new THREE.Line(geometry, material, THREE.LinePieces);
164 | var lineThick = new THREE.Line(geometryThick, materialThick, THREE.LinePieces);
165 |
166 | scene.add(line);
167 | scene.add(lineThick);
168 |
169 | }
170 |
171 | });
172 | });
173 |
174 |
175 |
176 |
177 |
--------------------------------------------------------------------------------
/app/scripts/views/customizer/CustomizerWorkspaceView.js:
--------------------------------------------------------------------------------
1 | define(['backbone', 'BaseWidgetView', 'GeometryWidgetView', 'NumberWidgetView'],
2 | function(Backbone, BaseWidgetView, GeometryWidgetView, NumberWidgetView) {
3 |
4 | return Backbone.View.extend({
5 |
6 | el: '#customizer-workspace',
7 |
8 | events: {
9 | "click #hide-workspace" : "hideWorkspace"
10 | },
11 |
12 | initialize: function( args, atts ) {
13 |
14 | },
15 |
16 | map: {
17 | "Number" : NumberWidgetView
18 | },
19 |
20 | hasWidgets: false,
21 |
22 | buildWidget: function(x){
23 |
24 | if (x.get('extra') != undefined && x.get('extra').lock) return;
25 |
26 | var widgetView = GeometryWidgetView;
27 |
28 | if (x.get('type').typeName in this.map){
29 | widgetView = this.map[x.get('type').typeName];
30 | }
31 |
32 | var widget = new widgetView({model: x});
33 |
34 | if (x.get('type').typeName in this.map){
35 | this.$el.append( widget.render().$el );
36 | this.hasWidgets = true;
37 | }
38 |
39 | },
40 |
41 | visible: true,
42 |
43 | hideWorkspace: function(){
44 |
45 | if (this.visible){
46 | this.$el.addClass('workspace-contracted');
47 | this.$el.find('.widget').css('visibility', 'hidden');
48 | this.$el.find('#hide-workspace i').removeClass('fa-arrow-circle-left');
49 | this.$el.find('#hide-workspace i').addClass('fa-arrow-circle-right');
50 | this.visible = false;
51 | } else {
52 | this.$el.removeClass('workspace-contracted');
53 | this.$el.find('.widget').css('visibility', 'visible');
54 | this.$el.find('#hide-workspace i').removeClass('fa-arrow-circle-right');
55 | this.$el.find('#hide-workspace i').addClass('fa-arrow-circle-left');
56 | this.visible = true;
57 | }
58 |
59 | },
60 |
61 | render: function() {
62 |
63 | this.model.get('nodes').each(this.buildWidget.bind(this));
64 |
65 | if (!this.hasWidgets) this.$el.hide();
66 |
67 | return this;
68 |
69 | }
70 |
71 | });
72 | });
--------------------------------------------------------------------------------
/app/scripts/views/customizer/widgets/Base.js:
--------------------------------------------------------------------------------
1 | define(['backbone'], function(Backbone) {
2 |
3 | return Backbone.View.extend({
4 |
5 | tagName: 'div',
6 | className: 'widget',
7 |
8 | template: _.template( $('#widget-template').html() ),
9 |
10 | initialize: function(args) {
11 |
12 | this.model.on('evalFailed', this.onEvalFailed, this );
13 | this.model.on('evalBegin', this.onEvalBegin, this );
14 |
15 | this.listenTo(this.model, 'requestRender', this.render );
16 | this.listenTo(this.model, 'change:position', this.move );
17 | this.listenTo(this.model, 'change:lastValue', this.renderLastValue );
18 | this.listenTo(this.model, 'change:failureMessage', this.renderLastValue );
19 | this.listenTo(this.model, 'change:ignoreDefaults', this.colorPorts );
20 | this.listenTo(this.model, 'change:selected', this.colorSelected);
21 | this.listenTo(this.model, 'change:visible', this.render);
22 | this.listenTo(this.model, 'change:isEvaluating', this.colorEvaluating);
23 |
24 | this.model.on('evalFailed', this.onEvalFailed, this );
25 | this.model.on('evalBegin', this.onEvalBegin, this );
26 |
27 | },
28 |
29 | colorSelected: function(){
30 |
31 | },
32 |
33 | render: function() {
34 |
35 | this.$el.html( this.template( this.model.toJSON() ) );
36 | return this;
37 |
38 | }
39 |
40 | });
41 |
42 | });
43 |
--------------------------------------------------------------------------------
/app/styles/customizer.css:
--------------------------------------------------------------------------------
1 | #customizer-app {
2 | background: #e3e3e3;
3 | top: 0;
4 | right: 0;
5 | left: 0;
6 | bottom: 0;
7 | position: absolute;
8 | }
9 |
10 | #customizer-workspace-header {
11 | position: absolute;
12 | bottom: -12px;
13 | right: -12px;
14 | color: #444;
15 | height: 20px;
16 | padding: 10px;
17 | font-size: 18px;
18 | text-shadow: 0px 1px 1px whitesmoke;
19 | text-align: right;
20 | }
21 |
22 |
23 | #customizer-workspace {
24 | color: black;
25 | position: absolute;
26 | top: 100px;
27 | left: 0;
28 | width: 380px;
29 | z-index: 1;
30 | background: rgba(0, 0, 0, 0.14);
31 | }
32 |
33 | #customizer-workspace.workspace-contracted {
34 | width: 20px;
35 | }
36 |
37 | #customizer-viewer {
38 | position: absolute;
39 | top: 0;
40 | right: 0;
41 | bottom: 0;
42 | left: 0;
43 | background: black;
44 | z-index: 0;
45 | }
46 |
47 | #customizer-header {
48 | position: absolute;
49 | top: 0;
50 | right: 0;
51 | left: 0;
52 | height: 50px;
53 | z-index: 2;
54 | font-size: 20px;
55 | display: inline-block;
56 | padding: 20px;
57 | padding-left: 30px;
58 | font-color: #222;
59 | text-shadow: 0px 1px 1px whitesmoke;
60 | }
61 |
62 | .indicator {
63 | margin-right: 30px;
64 | }
65 |
66 | #customize-header-tag {
67 | text-transform: uppercase;
68 | font-size: 13px;
69 | color: grey;
70 | font-weight: bold;
71 | }
72 |
73 | #powered-by {
74 | position: absolute;
75 | z-index: 2;
76 | right: 0;
77 | bottom: 0;
78 | height: 36px;
79 | padding: 10px;
80 | font-size: 12px;
81 | color: black;
82 | text-shadow: 0px 1px 0px grey;
83 | }
84 |
85 | #customize-header-title {
86 | padding-left: 20px;
87 | font-size: 24px;
88 | }
89 |
90 | #customizer-header-controls {
91 | padding: 30px;
92 | position: absolute;
93 | top: 0;
94 | right: 0;
95 | }
96 |
97 | .widget {
98 | width:370px;
99 | margin: 20px;
100 | }
101 |
102 | .widget .name {
103 | font-weight: bold;
104 | text-transform: uppercase;
105 | color: #222;
106 | display: inline-block;
107 | width: 130px;
108 | text-align: right;
109 | padding-right: 15px;
110 | font-size: 11px;
111 | text-shadow: 0px 1px 1px white;
112 | }
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flood",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "jquery": "1.9.1",
6 | "requirejs": "~2.1.5",
7 | "requirejs-text": "~2.0.5",
8 | "backbone-amd": "~1.0.0",
9 | "almond": "0.2.6",
10 | "bootstrap": "3.0.3",
11 | "underscore-amd": "~1.4.4",
12 | "threejs": "r67",
13 | "jquery.ui": "1.10.3",
14 | "listjs": "1.0.0",
15 | "modernizr": "~2.6.2",
16 | "q": "1.0",
17 | "jqueryui-touch-punch": "furf/jquery-ui-touch-punch",
18 | "pace": "0.5.1",
19 | "components-font-awesome": "4.3.0",
20 | "hammerjs": "~2.0.4",
21 | "fastclick": "~1.0.3",
22 | "FileSaver": "eliGrey/FileSaver.js",
23 | "CodeMirror": "~5.3.0"
24 | },
25 | "devDependencies": {}
26 | }
27 |
--------------------------------------------------------------------------------
/extra/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/extra/screenshot.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flood",
3 | "version": "0.0.4",
4 | "description": "A visual programming language for JavaScript, based on Scheme",
5 | "dependencies": {
6 |
7 | },
8 | "devDependencies": {
9 | "grunt-node-webkit-builder": "~0.1.13",
10 | "grunt": "0.4.2",
11 | "grunt-processhtml": "~0.2.6",
12 | "grunt-contrib-copy": "0.4.1",
13 | "grunt-contrib-concat": "0.3.0",
14 | "grunt-contrib-uglify": "0.2.7",
15 | "grunt-contrib-compass": "0.7.0",
16 | "grunt-contrib-jshint": "0.7.2",
17 | "grunt-contrib-cssmin": "0.7.0",
18 | "grunt-contrib-connect": "0.3.0",
19 | "grunt-contrib-clean": "0.5.0",
20 | "grunt-contrib-imagemin": "0.4.0",
21 | "grunt-contrib-livereload": "0.1.2",
22 | "grunt-mocha": "0.4.7",
23 | "grunt-bower-requirejs": "0.8.0",
24 | "grunt-requirejs": "0.4.0",
25 | "grunt-regarde": "0.1.1",
26 | "grunt-open": "0.2.2",
27 | "matchdep": "0.3.0",
28 | "amdefine": ">=0.0.5",
29 | "webworker": "*"
30 | },
31 | "engines": {
32 | "node": ">=0.10.0"
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 | *.swp
10 |
11 | pids
12 | logs
13 | results
14 | tmp
15 |
16 | npm-debug.log
17 | node_modules
18 | .idea
19 | *.iml
20 | .DS_Store
21 | Thumbs.db
22 |
23 | public/css/styles.css
24 |
25 | builtAssets
26 |
27 | ssl
28 |
--------------------------------------------------------------------------------
/server/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | services:
4 | - mongodb
5 |
6 | node_js:
7 | - '0.10'
--------------------------------------------------------------------------------
/server/cluster_app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Module dependencies.
3 | */
4 |
5 | var os = require('os');
6 | var cluster = require('cluster');
7 |
8 | /**
9 | * Cluster setup.
10 | */
11 |
12 | // Setup the cluster to use app.js
13 | cluster.setupMaster({
14 | exec: 'app.js'
15 | });
16 |
17 | // Listen for dying workers
18 | cluster.on('exit', function(worker) {
19 | console.log('Worker ' + worker.id + ' died');
20 | // Replace the dead worker
21 | cluster.fork();
22 | });
23 |
24 | // Fork a worker for each available CPU
25 | for (var i = 0; i < os.cpus().length; i++) {
26 | cluster.fork();
27 | }
28 |
--------------------------------------------------------------------------------
/server/config/secrets.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | db: process.env.MONGODB|| 'mongodb://localhost:27017/test',
3 |
4 | sessionSecret: process.env.SESSION_SECRET || 'Your Session Secret goes here',
5 |
6 | localAuth: true,
7 |
8 | feedback: {
9 | email: process.env.FEEDBACK_EMAIL || "Your email"
10 | },
11 |
12 | mailgun: {
13 | login: process.env.MAILGUN_LOGIN || 'Your Mailgun SMTP Username',
14 | password: process.env.MAILGUN_PASSWORD || 'Your Mailgun SMTP Password'
15 | },
16 |
17 | sendgrid: {
18 | user: process.env.SENDGRID_USER || 'Your SendGrid Username',
19 | password: process.env.SENDGRID_PASSWORD || 'Your SendGrid Password'
20 | },
21 |
22 | nyt: {
23 | key: process.env.NYT_KEY || 'Your New York Times API Key'
24 | },
25 |
26 | lastfm: {
27 | api_key: process.env.LASTFM_KEY || 'Your API Key',
28 | secret: process.env.LASTFM_SECRET || 'Your API Secret'
29 | },
30 |
31 | facebookAuth: true,
32 | facebook: {
33 | clientID: process.env.FACEBOOK_ID || 'Your App ID',
34 | clientSecret: process.env.FACEBOOK_SECRET || 'Your App Secret',
35 | callbackURL: '/auth/facebook/callback',
36 | passReqToCallback: true
37 | },
38 |
39 | githubAuth: false,
40 | github: {
41 | clientID: process.env.GITHUB_ID || 'Your Client ID',
42 | clientSecret: process.env.GITHUB_SECRET || 'Your Client Secret',
43 | callbackURL: '/auth/github/callback',
44 | passReqToCallback: true
45 | },
46 |
47 | twitterAuth: false,
48 | twitter: {
49 | consumerKey: process.env.TWITTER_KEY || 'Your Consumer Key',
50 | consumerSecret: process.env.TWITTER_SECRET || 'Your Consumer Secret',
51 | callbackURL: '/auth/twitter/callback',
52 | passReqToCallback: true
53 | },
54 |
55 | googleAuth: true,
56 | google: {
57 | clientID: process.env.GOOGLE_ID || 'Your Client ID',
58 | clientSecret: process.env.GOOGLE_SECRET || 'Your Client Secret',
59 | callbackURL: '/auth/google/callback',
60 | passReqToCallback: true
61 | },
62 |
63 | linkedinAuth: false,
64 | linkedin: {
65 | clientID: process.env.LINKEDIN_ID || 'Your Client ID',
66 | clientSecret: process.env.LINKEDIN_SECRET || 'Your Client Secret',
67 | callbackURL: '/auth/linkedin/callback',
68 | scope: ['r_fullprofile', 'r_emailaddress', 'r_network'],
69 | passReqToCallback: true
70 | },
71 |
72 | steam: {
73 | apiKey: process.env.STEAM_KEY || 'Your Steam API Key'
74 | },
75 |
76 | twilio: {
77 | sid: process.env.TWILIO_SID || 'Your Twilio SID',
78 | token: process.env.TWILIO_TOKEN || 'Your Twilio token'
79 | },
80 |
81 | clockwork: {
82 | apiKey: process.env.CLOCKWORK_KEY || 'Your Clockwork SMS API Key'
83 | },
84 |
85 | tumblr: {
86 | consumerKey: process.env.TUMBLR_KEY || 'Your Consumer Key',
87 | consumerSecret: process.env.TUMBLR_SECRET || 'Your Consumer Secret',
88 | callbackURL: '/auth/tumblr/callback'
89 | },
90 |
91 | foursquare: {
92 | clientId: process.env.FOURSQUARE_ID || 'Your Client ID',
93 | clientSecret: process.env.FOURSQUARE_SECRET || 'Your Client Secret',
94 | redirectUrl: process.env.FOURSQUARE_REDIRECT_URL || 'http://localhost:3000/auth/foursquare/callback'
95 | },
96 |
97 | venmo: {
98 | clientId: process.env.VENMO_ID || 'Your Venmo Client ID',
99 | clientSecret: process.env.VENMO_SECRET || 'Your Venmo Client Secret',
100 | redirectUrl: process.env.VENMO_REDIRECT_URL || 'http://localhost:3000/auth/venmo/callback'
101 | },
102 |
103 | paypal: {
104 | host: process.env.PAYPAL_HOST || 'api.sandbox.paypal.com',
105 | client_id: process.env.PAYPAL_ID || 'Your Client ID',
106 | client_secret: process.env.PAYPAL_SECRET || 'Your Client Secret',
107 | returnUrl: process.env.PAYPAL_RETURN_URL || 'http://localhost:3000/api/paypal/success',
108 | cancelUrl: process.env.PAYPAL_CANCEL_URL || 'http://localhost:3000/api/paypal/cancel'
109 | }
110 | };
111 |
--------------------------------------------------------------------------------
/server/controllers/contact.js:
--------------------------------------------------------------------------------
1 | var secrets = require('../config/secrets');
2 | var nodemailer = require("nodemailer");
3 | var smtpTransport = nodemailer.createTransport('SMTP', {
4 | // service: 'Mailgun',
5 | // auth: {
6 | // user: secrets.mailgun.login,
7 | // pass: secrets.mailgun.password
8 | // }
9 | service: 'SendGrid',
10 | auth: {
11 | user: secrets.sendgrid.user,
12 | pass: secrets.sendgrid.password
13 | }
14 | });
15 |
16 | /**
17 | * GET /contact
18 | * Contact form page.
19 | */
20 |
21 | exports.getContact = function(req, res) {
22 | res.render('contact', {
23 | title: 'Contact'
24 | });
25 | };
26 |
27 | /**
28 | * POST /contact
29 | * Send a contact form via Nodemailer.
30 | * @param email
31 | * @param name
32 | * @param message
33 | */
34 |
35 | exports.postContact = function(req, res) {
36 | req.assert('name', 'Name cannot be blank').notEmpty();
37 | req.assert('email', 'Email is not valid').isEmail();
38 | req.assert('message', 'Message cannot be blank').notEmpty();
39 |
40 | var errors = req.validationErrors();
41 |
42 | if (errors) {
43 | req.flash('errors', errors);
44 | return res.redirect('/contact');
45 | }
46 |
47 | var from = req.body.email;
48 | var name = req.body.name;
49 | var body = req.body.message;
50 | var to = 'your@email.com';
51 | var subject = 'Contact Form | Flood';
52 |
53 | var mailOptions = {
54 | to: to,
55 | from: from,
56 | subject: subject,
57 | text: body
58 | };
59 |
60 | smtpTransport.sendMail(mailOptions, function(err) {
61 | if (err) {
62 | req.flash('errors', { msg: err.message });
63 | return res.redirect('/contact');
64 | }
65 | req.flash('success', { msg: 'Email has been sent successfully!' });
66 | res.redirect('/contact');
67 | });
68 | };
69 |
--------------------------------------------------------------------------------
/server/controllers/feedback.js:
--------------------------------------------------------------------------------
1 | var _ = require('underscore')
2 | , nodemailer = require('nodemailer')
3 | , secrets = require('../config/secrets');
4 |
5 |
6 | var smtpTransport = nodemailer.createTransport('SMTP', {
7 | service: 'Mailgun',
8 | auth: {
9 | user: secrets.mailgun.login,
10 | pass: secrets.mailgun.password
11 | }
12 | });
13 |
14 | var emailTarget = secrets.feedback.email;
15 |
16 | exports.postFeedback = function(req, res) {
17 |
18 | if (!req.user) return res.status(401).send("You are not logged in");
19 |
20 | if (!req.body) return res.status(500).send('Malformed body');
21 |
22 | var ns = req.body;
23 |
24 | if (!emailTarget) return res.status(500).send({ msg: "Feedback temporarily unsupported" });
25 |
26 | smtpTransport.sendMail({
27 | from: req.user.email,
28 | to: emailTarget,
29 | subject: "[FLOOD FEEDBACK] : \"" + ns.subject ? ns.subject : "Empty subject" + "\"",
30 | text: ns.message ? ns.message : "Empty body",
31 | }, function(err) {
32 | if (err) {
33 | return res.status(500).send({ msg: "Could not send feedback! Try again later!" });
34 | }
35 | return res.send({ msg: 'Feedback has been sent successfully!' });
36 | });
37 |
38 | };
--------------------------------------------------------------------------------
/server/controllers/home.js:
--------------------------------------------------------------------------------
1 | var flood = require('../models/Workspace');
2 |
3 | /**
4 | * GET /
5 | * Home page.
6 | */
7 |
8 | exports.index = function(req, res) {
9 | res.render('home', {
10 | title: 'Home'
11 | });
12 | };
--------------------------------------------------------------------------------
/server/controllers/workspaces.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/server/controllers/workspaces.js
--------------------------------------------------------------------------------
/server/models/Session.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pboyer/flood/c86ab0b96034130c82c8efd92a62914f2d771311/server/models/Session.js
--------------------------------------------------------------------------------
/server/models/User.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose');
2 | var bcrypt = require('bcrypt-nodejs');
3 | var crypto = require('crypto');
4 | var models = require('./Workspace')
5 |
6 | var userSchema = new mongoose.Schema({
7 |
8 | email: { type: String, unique: true, lowercase: true },
9 | password: String,
10 |
11 | facebook: String,
12 | twitter: String,
13 | google: String,
14 | github: String,
15 | linkedin: String,
16 | tokens: Array,
17 |
18 | profile: {
19 | name: { type: String, default: '' },
20 | gender: { type: String, default: '' },
21 | location: { type: String, default: '' },
22 | website: { type: String, default: '' },
23 | picture: { type: String, default: '' }
24 | },
25 |
26 | resetPasswordToken: String,
27 | resetPasswordExpires: Date,
28 |
29 | lastSession: {type: mongoose.Schema.ObjectId, ref: 'Session' },
30 | workspaces: [{type: mongoose.Schema.ObjectId, ref: 'Workspace' }]
31 |
32 | });
33 |
34 | /**
35 | * Hash the password for security.
36 | * "Pre" is a Mongoose middleware that executes before each user.save() call.
37 | */
38 |
39 | userSchema.pre('save', function(next) {
40 | var user = this;
41 |
42 | if (!user.isModified('password')) return next();
43 |
44 | bcrypt.genSalt(5, function(err, salt) {
45 | if (err) return next(err);
46 |
47 | bcrypt.hash(user.password, salt, null, function(err, hash) {
48 | if (err) return next(err);
49 | user.password = hash;
50 | next();
51 | });
52 | });
53 | });
54 |
55 | /**
56 | * Validate user's password.
57 | * Used by Passport-Local Strategy for password validation.
58 | */
59 |
60 | userSchema.methods.comparePassword = function(candidatePassword, cb) {
61 | bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
62 | if (err) return cb(err);
63 | cb(null, isMatch);
64 | });
65 | };
66 |
67 | /**
68 | * Get URL to a user's gravatar.
69 | * Used in Navbar and Account Management page.
70 | */
71 |
72 | userSchema.methods.gravatar = function(size, defaults) {
73 | if (!size) size = 200;
74 | if (!defaults) defaults = 'retro';
75 |
76 | if (!this.email) {
77 | return 'https://gravatar.com/avatar/?s=' + size + '&d=' + defaults;
78 | }
79 |
80 | var md5 = crypto.createHash('md5').update(this.email);
81 | return 'https://gravatar.com/avatar/' + md5.digest('hex').toString() + '?s=' + size + '&d=' + defaults;
82 | };
83 |
84 | module.exports = mongoose.model('User', userSchema);
85 |
--------------------------------------------------------------------------------
/server/models/Workspace.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose')
2 | , Schema = mongoose.Schema;
3 |
4 | var sessionSchema = new Schema({
5 | name: { type: String, default: '' }
6 | , currentWorkspace: {type: Schema.ObjectId, ref: 'Workspace' }
7 | , workspaces: [ {type: Schema.ObjectId, ref: 'Workspace' } ]
8 | , lastSaved: Date
9 | , isFirstExperience: { type: Boolean, default: true }
10 | });
11 |
12 | var workspaceSchema = new Schema({
13 | name: { type: String, default: '' }
14 | , nodes: [ Schema.Types.Mixed ]
15 | , connections: [ Schema.Types.Mixed ]
16 | , selectedNodes: [ Schema.Types.Mixed ]
17 | , isPublic: { type: Boolean, default: false }
18 | , zoom: { type: Number, default: 1 }
19 | , offset: [{ type: Number }]
20 | , lastSaved: Date
21 | , maintainers: [{type: Schema.ObjectId, ref: 'User' }]
22 | , undoStack: [ Schema.Types.Mixed ]
23 | , redoStack: [ Schema.Types.Mixed ]
24 | , isEdited: { type: Boolean, default: false }
25 | , isCustomNode: { type: Boolean, default: false }
26 | , isCustomizer: { type: Boolean, default: false }
27 | , workspaceDependencyIds: [{type: Schema.ObjectId, ref: 'Workspace' }]
28 | });
29 |
30 | exports.SessionModel = mongoose.model('Session', sessionSchema);
31 | exports.WorkspaceModel = mongoose.model('Workspace', workspaceSchema);
32 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flood-server",
3 | "version": "0.0.0",
4 | "repository": {
5 | "type": "git",
6 | "url": ""
7 | },
8 | "scripts": {
9 | "start": "node app.js",
10 | "test": "mocha"
11 | },
12 | "dependencies": {
13 | "async": "^0.7.0",
14 | "bcrypt-nodejs": "^0.0.3",
15 | "body-parser": "^1.0.1",
16 | "cheerio": "^0.15.0",
17 | "clockwork": "^0.1.1",
18 | "compression": "^1.0.1",
19 | "connect-assets": "^3.0.0-beta2",
20 | "connect-mongo": "^0.4.0",
21 | "cookie-parser": "^1.0.1",
22 | "csso": "^1.3.11",
23 | "csurf": "^1.1.0",
24 | "errorhandler": "^1.0.0",
25 | "express": "^4.0.0",
26 | "express-flash": "^0.0.2",
27 | "express-session": "^1.0.2",
28 | "express-validator": "^2.1.1",
29 | "fbgraph": "^0.2.10",
30 | "github-api": "^0.7.0",
31 | "jade": "^1.3.0",
32 | "lastfm": "^0.9.0",
33 | "less": "^1.7.0",
34 | "method-override": "^1.0.0",
35 | "mongoose": "4.0.5",
36 | "morgan": "^1.0.0",
37 | "node-foursquare": "^0.2.0",
38 | "node-linkedin": "^0.1.5",
39 | "nodemailer": "^0.6.1",
40 | "passport": "^0.2.0",
41 | "passport-facebook": "^1.0.3",
42 | "passport-github": "^0.1.5",
43 | "passport-google-oauth": "^0.1.5",
44 | "passport-linkedin-oauth2": "^1.1.1",
45 | "passport-local": "^1.0.0",
46 | "passport-oauth": "^1.0.0",
47 | "passport-twitter": "^1.0.2",
48 | "paypal-rest-sdk": "^0.7.0",
49 | "request": "^2.34.0",
50 | "static-favicon": "^1.0.2",
51 | "tumblr.js": "^0.0.4",
52 | "twilio": "^1.6.0",
53 | "twit": "^1.1.12",
54 | "uglify-js": "^2.4.12",
55 | "underscore": "^1.6.0",
56 | "validator": "^3.8.0"
57 | },
58 | "devDependencies": {
59 | "mocha": "^1.18.2",
60 | "chai": "^1.9.1",
61 | "supertest": "^0.10.0"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap-social.less:
--------------------------------------------------------------------------------
1 | /*
2 | * Social Buttons for Bootstrap
3 | *
4 | * Copyright 2013-2014 Panayiotis Lipiridis
5 | * Licensed under the MIT License
6 | *
7 | * https://github.com/lipis/bootstrap-social
8 | */
9 |
10 | .btn-social {
11 | @bs-height-base: (@line-height-computed + @padding-base-vertical * 2);
12 | @bs-height-lg: (floor(@font-size-large * @line-height-base) + @padding-large-vertical * 2);
13 | @bs-height-sm: (floor(@font-size-small * 1.5) + @padding-small-vertical * 2);
14 | @bs-height-xs: (floor(@font-size-small * 1.2) + @padding-small-vertical + 1);
15 |
16 | position: relative;
17 | padding-left: @bs-height-base + @padding-base-horizontal;
18 | text-align: left;
19 | white-space: nowrap;
20 | overflow: hidden;
21 | text-overflow: ellipsis;
22 | :first-child {
23 | position: absolute;
24 | left: 0;
25 | top: 0;
26 | bottom: 0;
27 | width: @bs-height-base;
28 | line-height: (@bs-height-base + 2);
29 | font-size: 1.6em;
30 | text-align: center;
31 | border-right: 1px solid rgba(0, 0, 0, 0.2);
32 | }
33 | &.btn-lg {
34 | padding-left: @bs-height-lg + @padding-large-horizontal;
35 | :first-child {
36 | line-height: @bs-height-lg;
37 | width: @bs-height-lg;
38 | font-size: 1.8em;
39 | }
40 | }
41 | &.btn-sm {
42 | padding-left: @bs-height-sm + @padding-small-horizontal;
43 | :first-child {
44 | line-height: @bs-height-sm;
45 | width: @bs-height-sm;
46 | font-size: 1.4em;
47 | }
48 | }
49 | &.btn-xs {
50 | padding-left: @bs-height-xs + @padding-small-horizontal;
51 | :first-child {
52 | line-height: @bs-height-xs;
53 | width: @bs-height-xs;
54 | font-size: 1.2em;
55 | }
56 | }
57 | }
58 |
59 | .btn-social-icon {
60 | .btn-social;
61 | height: (@bs-height-base + 2);
62 | width: (@bs-height-base + 2);
63 | padding: 0;
64 | :first-child {
65 | border: none;
66 | text-align: center;
67 | width: 100% !important;
68 | }
69 | &.btn-lg {
70 | height: @bs-height-lg;
71 | width: @bs-height-lg;
72 | padding-left: 0;
73 | padding-right: 0;
74 | }
75 | &.btn-sm {
76 | height: (@bs-height-sm + 2);
77 | width: (@bs-height-sm + 2);
78 | padding-left: 0;
79 | padding-right: 0;
80 | }
81 | &.btn-xs {
82 | height: (@bs-height-xs + 2);
83 | width: (@bs-height-xs + 2);
84 | padding-left: 0;
85 | padding-right: 0;
86 | }
87 | }
88 |
89 | .btn-social(@color-bg, @color: white) {
90 | background-color: @color-bg;
91 | .button-variant(@color, @color-bg, rgba(0, 0, 0, 0.2));
92 | }
93 |
94 | .btn-bitbucket {
95 | .btn-social(#205081);
96 | }
97 |
98 | .btn-dropbox {
99 | .btn-social(#1087dd);
100 | }
101 |
102 | .btn-facebook {
103 | .btn-social(#3b5998);
104 | }
105 |
106 | .btn-flickr {
107 | .btn-social(#ff0084);
108 | }
109 |
110 | .btn-foursquare {
111 | .btn-social(#0072b1);
112 | }
113 |
114 | .btn-github {
115 | .btn-social(#444444);
116 | }
117 |
118 | .btn-google-plus {
119 | .btn-social(#dd4b39);
120 | }
121 |
122 | .btn-instagram {
123 | .btn-social(#3f729b);
124 | }
125 |
126 | .btn-linkedin {
127 | .btn-social(#007bb6);
128 | }
129 |
130 | .btn-tumblr {
131 | .btn-social(#2c4762);
132 | }
133 |
134 | .btn-twitter {
135 | .btn-social(#55acee);
136 | }
137 |
138 | .btn-vk {
139 | .btn-social(#587ea3);
140 | }
141 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/alerts.less:
--------------------------------------------------------------------------------
1 | //
2 | // Alerts
3 | // --------------------------------------------------
4 |
5 |
6 | // Base styles
7 | // -------------------------
8 |
9 | .alert {
10 | padding: @alert-padding;
11 | margin-bottom: @line-height-computed;
12 | border: 1px solid transparent;
13 | border-radius: @alert-border-radius;
14 |
15 | // Headings for larger alerts
16 | h4 {
17 | margin-top: 0;
18 | // Specified for the h4 to prevent conflicts of changing @headings-color
19 | color: inherit;
20 | }
21 | // Provide class for links that match alerts
22 | .alert-link {
23 | font-weight: @alert-link-font-weight;
24 | }
25 |
26 | // Improve alignment and spacing of inner content
27 | > p,
28 | > ul {
29 | margin-bottom: 0;
30 | }
31 | > p + p {
32 | margin-top: 5px;
33 | }
34 | }
35 |
36 | // Dismissable alerts
37 | //
38 | // Expand the right padding and account for the close button's positioning.
39 |
40 | .alert-dismissable {
41 | padding-right: (@alert-padding + 20);
42 |
43 | // Adjust close link position
44 | .close {
45 | position: relative;
46 | top: -2px;
47 | right: -21px;
48 | color: inherit;
49 | }
50 | }
51 |
52 | // Alternate styles
53 | //
54 | // Generate contextual modifier classes for colorizing the alert.
55 |
56 | .alert-success {
57 | .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);
58 | }
59 | .alert-info {
60 | .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);
61 | }
62 | .alert-warning {
63 | .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);
64 | }
65 | .alert-danger {
66 | .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);
67 | }
68 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/badges.less:
--------------------------------------------------------------------------------
1 | //
2 | // Badges
3 | // --------------------------------------------------
4 |
5 |
6 | // Base classes
7 | .badge {
8 | display: inline-block;
9 | min-width: 10px;
10 | padding: 3px 7px;
11 | font-size: @font-size-small;
12 | font-weight: @badge-font-weight;
13 | color: @badge-color;
14 | line-height: @badge-line-height;
15 | vertical-align: baseline;
16 | white-space: nowrap;
17 | text-align: center;
18 | background-color: @badge-bg;
19 | border-radius: @badge-border-radius;
20 |
21 | // Empty badges collapse automatically (not available in IE8)
22 | &:empty {
23 | display: none;
24 | }
25 |
26 | // Quick fix for badges in buttons
27 | .btn & {
28 | position: relative;
29 | top: -1px;
30 | }
31 | .btn-xs & {
32 | top: 0;
33 | padding: 1px 5px;
34 | }
35 | }
36 |
37 | // Hover state, but only for links
38 | a.badge {
39 | &:hover,
40 | &:focus {
41 | color: @badge-link-hover-color;
42 | text-decoration: none;
43 | cursor: pointer;
44 | }
45 | }
46 |
47 | // Account for counters in navs
48 | a.list-group-item.active > .badge,
49 | .nav-pills > .active > a > .badge {
50 | color: @badge-active-color;
51 | background-color: @badge-active-bg;
52 | }
53 | .nav-pills > li > a > .badge {
54 | margin-left: 3px;
55 | }
56 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/bootstrap.less:
--------------------------------------------------------------------------------
1 | // Core variables and mixins
2 | @import "variables.less";
3 | @import "mixins.less";
4 |
5 | // Reset
6 | @import "normalize.less";
7 | @import "print.less";
8 |
9 | // Core CSS
10 | @import "scaffolding.less";
11 | @import "type.less";
12 | @import "code.less";
13 | @import "grid.less";
14 | @import "tables.less";
15 | @import "forms.less";
16 | @import "buttons.less";
17 |
18 | // Components
19 | @import "component-animations.less";
20 | @import "glyphicons.less";
21 | @import "dropdowns.less";
22 | @import "button-groups.less";
23 | @import "input-groups.less";
24 | @import "navs.less";
25 | @import "navbar.less";
26 | @import "breadcrumbs.less";
27 | @import "pagination.less";
28 | @import "pager.less";
29 | @import "labels.less";
30 | @import "badges.less";
31 | @import "jumbotron.less";
32 | @import "thumbnails.less";
33 | @import "alerts.less";
34 | @import "progress-bars.less";
35 | @import "media.less";
36 | @import "list-group.less";
37 | @import "panels.less";
38 | @import "wells.less";
39 | @import "close.less";
40 |
41 | // Components w/ JavaScript
42 | @import "modals.less";
43 | @import "tooltip.less";
44 | @import "popovers.less";
45 | @import "carousel.less";
46 |
47 | // Utility classes
48 | @import "utilities.less";
49 | @import "responsive-utilities.less";
50 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/breadcrumbs.less:
--------------------------------------------------------------------------------
1 | //
2 | // Breadcrumbs
3 | // --------------------------------------------------
4 |
5 |
6 | .breadcrumb {
7 | padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;
8 | margin-bottom: @line-height-computed;
9 | list-style: none;
10 | background-color: @breadcrumb-bg;
11 | border-radius: @border-radius-base;
12 |
13 | > li {
14 | display: inline-block;
15 |
16 | + li:before {
17 | content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space
18 | padding: 0 5px;
19 | color: @breadcrumb-color;
20 | }
21 | }
22 |
23 | > .active {
24 | color: @breadcrumb-active-color;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/buttons.less:
--------------------------------------------------------------------------------
1 | //
2 | // Buttons
3 | // --------------------------------------------------
4 |
5 | // Base styles
6 | // --------------------------------------------------
7 |
8 | .btn {
9 | display: inline-block;
10 | margin-bottom: 0; // For input.btn
11 | font-weight: @btn-font-weight;
12 | text-align: center;
13 | vertical-align: middle;
14 | cursor: pointer;
15 | background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214
16 | border: 1px solid transparent;
17 | white-space: nowrap;
18 | .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base);
19 | .user-select(none);
20 |
21 | &,
22 | &:active,
23 | &.active {
24 | &:focus {
25 | .tab-focus();
26 | }
27 | }
28 |
29 | &:hover,
30 | &:focus {
31 | color: @btn-default-color;
32 | text-decoration: none;
33 | }
34 |
35 | &:active,
36 | &.active {
37 | outline: 0;
38 | background-image: none;
39 | .box-shadow(inset 0 3px 5px rgba(0, 0, 0, .125));
40 | }
41 |
42 | &.disabled,
43 | &[disabled],
44 | fieldset[disabled] & {
45 | cursor: not-allowed;
46 | pointer-events: none; // Future-proof disabling of clicks
47 | .opacity(.65);
48 | .box-shadow(none);
49 | }
50 | }
51 |
52 | // Alternate buttons
53 | // --------------------------------------------------
54 |
55 | .btn-default {
56 | .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);
57 | }
58 |
59 | .btn-primary {
60 | .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);
61 | }
62 |
63 | // Success appears as green
64 | .btn-success {
65 | .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);
66 | }
67 |
68 | // Info appears as blue-green
69 | .btn-info {
70 | .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);
71 | }
72 |
73 | // Warning appears as orange
74 | .btn-warning {
75 | .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);
76 | }
77 |
78 | // Danger and error appear as red
79 | .btn-danger {
80 | .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);
81 | }
82 |
83 | // Link buttons
84 | // -------------------------
85 |
86 | // Make a button look and behave like a link
87 | .btn-link {
88 | color: @link-color;
89 | font-weight: normal;
90 | cursor: pointer;
91 | border-radius: 0;
92 |
93 | &,
94 | &:active,
95 | &[disabled],
96 | fieldset[disabled] & {
97 | background-color: transparent;
98 | .box-shadow(none);
99 | }
100 | &,
101 | &:hover,
102 | &:focus,
103 | &:active {
104 | border-color: transparent;
105 | }
106 | &:hover,
107 | &:focus {
108 | color: @link-hover-color;
109 | text-decoration: underline;
110 | background-color: transparent;
111 | }
112 | &[disabled],
113 | fieldset[disabled] & {
114 | &:hover,
115 | &:focus {
116 | color: @btn-link-disabled-color;
117 | text-decoration: none;
118 | }
119 | }
120 | }
121 |
122 | // Button Sizes
123 | // --------------------------------------------------
124 |
125 | .btn-lg {
126 | // line-height: ensure even-numbered height of button next to large input
127 | .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);
128 | }
129 |
130 | .btn-sm {
131 | // line-height: ensure proper height of button next to small input
132 | .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);
133 | }
134 |
135 | .btn-xs {
136 | .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small);
137 | }
138 |
139 | // Block button
140 | // --------------------------------------------------
141 |
142 | .btn-block {
143 | display: block;
144 | width: 100%;
145 | padding-left: 0;
146 | padding-right: 0;
147 | }
148 |
149 | // Vertically space out multiple block buttons
150 | .btn-block + .btn-block {
151 | margin-top: 5px;
152 | }
153 |
154 | // Specificity overrides
155 | input[type="submit"],
156 | input[type="reset"],
157 | input[type="button"] {
158 | &.btn-block {
159 | width: 100%;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/close.less:
--------------------------------------------------------------------------------
1 | //
2 | // Close icons
3 | // --------------------------------------------------
4 |
5 |
6 | .close {
7 | float: right;
8 | font-size: (@font-size-base * 1.5);
9 | font-weight: @close-font-weight;
10 | line-height: 1;
11 | color: @close-color;
12 | text-shadow: @close-text-shadow;
13 | .opacity(.2);
14 |
15 | &:hover,
16 | &:focus {
17 | color: @close-color;
18 | text-decoration: none;
19 | cursor: pointer;
20 | .opacity(.5);
21 | }
22 |
23 | // Additional properties for button version
24 | // iOS requires the button element instead of an anchor tag.
25 | // If you want the anchor version, it requires `href="#"`.
26 | button& {
27 | padding: 0;
28 | cursor: pointer;
29 | background: transparent;
30 | border: 0;
31 | -webkit-appearance: none;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/code.less:
--------------------------------------------------------------------------------
1 | //
2 | // Code (inline and block)
3 | // --------------------------------------------------
4 |
5 |
6 | // Inline and block code styles
7 | code,
8 | kbd,
9 | pre,
10 | samp {
11 | font-family: @font-family-monospace;
12 | }
13 |
14 | // Inline code
15 | code {
16 | padding: 2px 4px;
17 | font-size: 90%;
18 | color: @code-color;
19 | background-color: @code-bg;
20 | white-space: nowrap;
21 | border-radius: @border-radius-base;
22 | }
23 |
24 | // User input typically entered via keyboard
25 | kbd {
26 | padding: 2px 4px;
27 | font-size: 90%;
28 | color: @kbd-color;
29 | background-color: @kbd-bg;
30 | border-radius: @border-radius-small;
31 | box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);
32 | }
33 |
34 | // Blocks of code
35 | pre {
36 | display: block;
37 | padding: ((@line-height-computed - 1) / 2);
38 | margin: 0 0 (@line-height-computed / 2);
39 | font-size: (@font-size-base - 1); // 14px to 13px
40 | line-height: @line-height-base;
41 | word-break: break-all;
42 | word-wrap: break-word;
43 | color: @pre-color;
44 | background-color: @pre-bg;
45 | border: 1px solid @pre-border-color;
46 | border-radius: @border-radius-base;
47 |
48 | // Account for some code outputs that place code tags in pre tags
49 | code {
50 | padding: 0;
51 | font-size: inherit;
52 | color: inherit;
53 | white-space: pre-wrap;
54 | background-color: transparent;
55 | border-radius: 0;
56 | }
57 | }
58 |
59 | // Enable scrollable blocks of code
60 | .pre-scrollable {
61 | max-height: @pre-scrollable-max-height;
62 | overflow-y: scroll;
63 | }
64 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/component-animations.less:
--------------------------------------------------------------------------------
1 | //
2 | // Component animations
3 | // --------------------------------------------------
4 |
5 | // Heads up!
6 | //
7 | // We don't use the `.opacity()` mixin here since it causes a bug with text
8 | // fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552.
9 |
10 | .fade {
11 | opacity: 0;
12 | .transition(opacity .15s linear);
13 | &.in {
14 | opacity: 1;
15 | }
16 | }
17 |
18 | .collapse {
19 | display: none;
20 | &.in {
21 | display: block;
22 | }
23 | }
24 | .collapsing {
25 | position: relative;
26 | height: 0;
27 | overflow: hidden;
28 | .transition(height .35s ease);
29 | }
30 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/grid.less:
--------------------------------------------------------------------------------
1 | //
2 | // Grid system
3 | // --------------------------------------------------
4 |
5 | // Container widths
6 | //
7 | // Set the container width, and override it for fixed navbars in media queries.
8 |
9 | .container {
10 | .container-fixed();
11 |
12 | @media (min-width: @screen-sm-min) {
13 | width: @container-sm;
14 | }
15 | @media (min-width: @screen-md-min) {
16 | width: @container-md;
17 | }
18 | @media (min-width: @screen-lg-min) {
19 | width: @container-lg;
20 | }
21 | }
22 |
23 | // Fluid container
24 | //
25 | // Utilizes the mixin meant for fixed width containers, but without any defined
26 | // width for fluid, full width layouts.
27 |
28 | .container-fluid {
29 | .container-fixed();
30 | }
31 |
32 | // Row
33 | //
34 | // Rows contain and clear the floats of your columns.
35 |
36 | .row {
37 | .make-row();
38 | }
39 |
40 | // Columns
41 | //
42 | // Common styles for small and large grid columns
43 |
44 | .make-grid-columns();
45 |
46 | // Extra small grid
47 | //
48 | // Columns, offsets, pushes, and pulls for extra small devices like
49 | // smartphones.
50 |
51 | .make-grid(xs);
52 |
53 | // Small grid
54 | //
55 | // Columns, offsets, pushes, and pulls for the small device range, from phones
56 | // to tablets.
57 |
58 | @media (min-width: @screen-sm-min) {
59 | .make-grid(sm);
60 | }
61 |
62 | // Medium grid
63 | //
64 | // Columns, offsets, pushes, and pulls for the desktop device range.
65 |
66 | @media (min-width: @screen-md-min) {
67 | .make-grid(md);
68 | }
69 |
70 | // Large grid
71 | //
72 | // Columns, offsets, pushes, and pulls for the large desktop device range.
73 |
74 | @media (min-width: @screen-lg-min) {
75 | .make-grid(lg);
76 | }
77 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/jumbotron.less:
--------------------------------------------------------------------------------
1 | //
2 | // Jumbotron
3 | // --------------------------------------------------
4 |
5 |
6 | .jumbotron {
7 | padding: @jumbotron-padding;
8 | margin-bottom: @jumbotron-padding;
9 | color: @jumbotron-color;
10 | background-color: @jumbotron-bg;
11 |
12 | h1,
13 | .h1 {
14 | color: @jumbotron-heading-color;
15 | }
16 | p {
17 | margin-bottom: (@jumbotron-padding / 2);
18 | font-size: @jumbotron-font-size;
19 | font-weight: 200;
20 | }
21 |
22 | .container & {
23 | border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container
24 | }
25 |
26 | .container {
27 | max-width: 100%;
28 | }
29 |
30 | @media screen and (min-width: @screen-sm-min) {
31 | padding-top: (@jumbotron-padding * 1.6);
32 | padding-bottom: (@jumbotron-padding * 1.6);
33 |
34 | .container & {
35 | padding-left: (@jumbotron-padding * 2);
36 | padding-right: (@jumbotron-padding * 2);
37 | }
38 |
39 | h1,
40 | .h1 {
41 | font-size: (@font-size-base * 4.5);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/labels.less:
--------------------------------------------------------------------------------
1 | //
2 | // Labels
3 | // --------------------------------------------------
4 |
5 | .label {
6 | display: inline;
7 | padding: .2em .6em .3em;
8 | font-size: 75%;
9 | font-weight: bold;
10 | line-height: 1;
11 | color: @label-color;
12 | text-align: center;
13 | white-space: nowrap;
14 | vertical-align: baseline;
15 | border-radius: .25em;
16 |
17 | // Add hover effects, but only for links
18 | &[href] {
19 | &:hover,
20 | &:focus {
21 | color: @label-link-hover-color;
22 | text-decoration: none;
23 | cursor: pointer;
24 | }
25 | }
26 |
27 | // Empty labels collapse automatically (not available in IE8)
28 | &:empty {
29 | display: none;
30 | }
31 |
32 | // Quick fix for labels in buttons
33 | .btn & {
34 | position: relative;
35 | top: -1px;
36 | }
37 | }
38 |
39 | // Colors
40 | // Contextual variations (linked labels get darker on :hover)
41 |
42 | .label-default {
43 | .label-variant(@label-default-bg);
44 | }
45 |
46 | .label-primary {
47 | .label-variant(@label-primary-bg);
48 | }
49 |
50 | .label-success {
51 | .label-variant(@label-success-bg);
52 | }
53 |
54 | .label-info {
55 | .label-variant(@label-info-bg);
56 | }
57 |
58 | .label-warning {
59 | .label-variant(@label-warning-bg);
60 | }
61 |
62 | .label-danger {
63 | .label-variant(@label-danger-bg);
64 | }
65 |
--------------------------------------------------------------------------------
/server/public/css/lib/bootstrap/list-group.less:
--------------------------------------------------------------------------------
1 | //
2 | // List groups
3 | // --------------------------------------------------
4 |
5 |
6 | // Base class
7 | //
8 | // Easily usable on