├── .gitignore ├── CNAME ├── Gruntfile.js ├── README.md ├── blab.md ├── blabs ├── gists.coffee ├── gists.js ├── index.html └── style.css ├── build ├── blabr-main.js ├── demo-runner.js ├── guide.js ├── typed.js ├── utils.js └── widgets.js ├── compute.coffee ├── css ├── style.css └── widgets.css ├── defs.coffee ├── demo.coffee ├── img ├── TwitterLogo.png ├── UI_175.png ├── UI_76.png ├── UI_78.png ├── arrow.jpg ├── blab.png ├── blabr-logo.png ├── play.png ├── play150.png └── pointer.png ├── index.html ├── js ├── blabr.js ├── blabr.min.js ├── components.js ├── ga.js ├── lecture.js ├── style.js └── widgets.js ├── layout.coffee ├── package.json ├── resources.coffee ├── src ├── blabr.coffee ├── demo-runner.coffee ├── guide.coffee ├── lecture.coffee └── utils.coffee ├── start.coffee ├── tables.json └── widgets ├── css └── style.css ├── input ├── component.coffee ├── defs.coffee ├── style.css ├── test │ ├── index.html │ ├── resources.coffee │ └── test.coffee └── widget.coffee ├── menu ├── component.coffee ├── defs.coffee ├── style.css ├── test │ ├── index.html │ ├── resources.coffee │ └── test.coffee └── widget.coffee ├── plot ├── component.coffee ├── defs.coffee ├── style.css ├── test │ ├── index.html │ ├── resources.coffee │ └── test.coffee └── widget.coffee ├── slider ├── component.coffee ├── defs.coffee ├── style.css ├── test │ ├── index.html │ ├── resources.coffee │ └── test.coffee └── widget.coffee └── table ├── component.coffee ├── style.css ├── test ├── index.html ├── resources.coffee ├── tables.json └── test.coffee └── widget.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | blabr.io 2 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | coffee: { 7 | compile: { 8 | files: { 9 | 'build/blabr-main.js': ['src/blabr.coffee'], 10 | 'build/guide.js': ['src/guide.coffee'], 11 | 'build/utils.js': ['src/utils.coffee'], 12 | 'build/demo-runner.js': ['src/demo-runner.coffee'], 13 | 'js/components.js': [ 14 | 'widgets/input/component.coffee', 15 | 'widgets/menu/component.coffee', 16 | 'widgets/slider/component.coffee', 17 | 'widgets/plot/component.coffee', 18 | 'widgets/table/component.coffee' 19 | ], 20 | 'build/widgets.js': [ 21 | 'widgets/input/widget.coffee', 22 | 'widgets/menu/widget.coffee', 23 | 'widgets/slider/widget.coffee', 24 | 'widgets/plot/widget.coffee', 25 | 'widgets/table/widget.coffee' 26 | ], 27 | // 'js/widgets.js': ['src/widgets.coffee'], 28 | 'js/lecture.js': ['src/lecture.coffee'] 29 | } 30 | } 31 | }, 32 | concat: { 33 | options: { 34 | separator: '' // ';' 35 | }, 36 | dist: { 37 | src: ['build/**/*.js'], 38 | dest: 'js/<%= pkg.name %>.js' 39 | }, 40 | js: { 41 | options: { 42 | separator: '\n\n' 43 | }, 44 | src: [ 45 | 'js/components.js', 46 | 'build/widgets.js' 47 | ], 48 | dest: 'js/widgets.js' 49 | }, 50 | css: { 51 | options: { 52 | banner: '/* Auto-generated from widgets style.css files */\n\n' 53 | }, 54 | src: [ 55 | 'widgets/input/style.css', 56 | 'widgets/menu/style.css', 57 | 'widgets/slider/style.css', 58 | 'widgets/plot/style.css', 59 | 'widgets/table/style.css' 60 | ], 61 | dest: 'css/widgets.css' 62 | } 63 | }, 64 | // uglify: { 65 | // options: { 66 | // banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' 67 | // }, 68 | // build: { 69 | // src: 'js/<%= pkg.name %>.js', 70 | // dest: 'js/<%= pkg.name %>.min.js' 71 | // } 72 | // }, 73 | watch: { 74 | files: ['src/*.coffee', 'widgets/*/*.coffee', 'widgets/*/*.css'], 75 | tasks: ['coffee', 'concat'] 76 | // tasks: ['coffee', 'concat', 'uglify'] 77 | } 78 | }); 79 | 80 | // Load the plugins. 81 | grunt.loadNpmTasks('grunt-contrib-coffee'); 82 | grunt.loadNpmTasks('grunt-contrib-concat'); 83 | //grunt.loadNpmTasks('grunt-contrib-uglify'); 84 | grunt.loadNpmTasks('grunt-contrib-watch'); 85 | 86 | // Default task(s). 87 | grunt.registerTask('default', ['coffee', 'concat']); 88 | //grunt.registerTask('default', ['coffee', 'concat', 'uglify']); 89 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Blabr 2 | ===== 3 | Web Lab 4 | -------------------------------------------------------------------------------- /blab.md: -------------------------------------------------------------------------------- 1 | # Untitled 2 | -------------------------------------------------------------------------------- /blabs/gists.coffee: -------------------------------------------------------------------------------- 1 | class Gists 2 | 3 | api: "https://api.github.com" 4 | 5 | constructor: -> 6 | 7 | @container = $("#list-gists") 8 | 9 | @username = $.cookie("gh_user") 10 | @key = $.cookie("gh_key") 11 | 12 | unless @username 13 | @container.append "Set username in blab save credentials form" 14 | return 15 | 16 | @setCredentials() if @username and @key 17 | 18 | $.ajax 19 | type: "GET" 20 | url: @api+"/gists?per_page=200" 21 | beforeSend: (xhr) => @authBeforeSend(xhr) 22 | success: (data) => 23 | console.log "Gist data", data 24 | @display data 25 | 26 | display: (data) -> 27 | @container.append "

#{if @username then @username else ""}

" 28 | @container.append "

Gray links are secret gists.

" 29 | table = $ "" 30 | @container.append table 31 | 32 | headings = ["Comments", "Description", "Created", "Updated"] 33 | 34 | for h in headings 35 | table.append "" 36 | 37 | for d in data 38 | tr = $ "" 39 | table.append tr 40 | @cell tr, (if d.comments then d.comments else "") 41 | @link tr, d 42 | @cell tr, @date(d.created_at) 43 | @cell tr, @date(d.updated_at) 44 | 45 | link: (tr, d) -> 46 | 47 | td = $ "
#{h}
" 48 | tr.append td 49 | 50 | re = /\[http:/ 51 | m = d.description.match(re) 52 | desc = if m.index then d.description.substring(0, m.index-1) else d.description 53 | if m.index 54 | p = d.description.substring(m.index+1).slice(0, -1) 55 | 56 | a = $ "", 57 | class: (if d.public then "public-gist-link" else "secret-gist-link") 58 | href: d.html_url 59 | target: "_blank" 60 | text: desc 61 | 62 | td.append(a) 63 | 64 | edit = $ "", 65 | class: "edit-gist-link" 66 | href: "//gist.github.com/#{d.owner.login}/#{d.id}/edit" 67 | target: "_blank" 68 | text: "Edit" 69 | 70 | td.append(edit) 71 | 72 | if p 73 | a2 = $ "", 74 | class: "app-link" 75 | href: p 76 | target: "_blank" 77 | text: "[#{p}]" 78 | td.append("
").append(a2) 79 | 80 | cell: (tr, txt) -> 81 | td = $ "
" 82 | tr.append td 83 | td.append txt 84 | td 85 | 86 | date: (str) -> 87 | d = new Date(str) 88 | d.toLocaleString() #toUTCString() 89 | 90 | setCredentials: -> 91 | 92 | console.log "username/key", @username, @key 93 | 94 | make_base_auth = (user, password) -> 95 | tok = user + ':' + password 96 | hash = btoa(tok) 97 | "Basic " + hash 98 | 99 | if @username and @key 100 | @auth = make_base_auth @username, @key 101 | 102 | authBeforeSend: (xhr) -> 103 | return unless @auth 104 | console.log "Set request header", @auth 105 | xhr.setRequestHeader('Authorization', @auth) 106 | 107 | 108 | new Gists -------------------------------------------------------------------------------- /blabs/gists.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | (function() { 3 | var Gists; 4 | 5 | Gists = (function() { 6 | Gists.prototype.api = "https://api.github.com"; 7 | 8 | function Gists() { 9 | this.container = $("#list-gists"); 10 | this.username = $.cookie("gh_user"); 11 | this.key = $.cookie("gh_key"); 12 | if (!this.username) { 13 | this.container.append("Set username in blab save credentials form"); 14 | return; 15 | } 16 | if (this.username && this.key) { 17 | this.setCredentials(); 18 | } 19 | $.ajax({ 20 | type: "GET", 21 | url: this.api + "/gists?per_page=200", 22 | beforeSend: (function(_this) { 23 | return function(xhr) { 24 | return _this.authBeforeSend(xhr); 25 | }; 26 | })(this), 27 | success: (function(_this) { 28 | return function(data) { 29 | console.log("Gist data", data); 30 | return _this.display(data); 31 | }; 32 | })(this) 33 | }); 34 | } 35 | 36 | Gists.prototype.display = function(data) { 37 | var d, h, headings, table, tr, _i, _j, _len, _len1, _results; 38 | this.container.append("

" + (this.username ? this.username : "") + "

"); 39 | this.container.append("

Gray links are secret gists.

"); 40 | table = $(""); 41 | this.container.append(table); 42 | headings = ["Comments", "Description", "Created", "Updated"]; 43 | for (_i = 0, _len = headings.length; _i < _len; _i++) { 44 | h = headings[_i]; 45 | table.append(""); 46 | } 47 | _results = []; 48 | for (_j = 0, _len1 = data.length; _j < _len1; _j++) { 49 | d = data[_j]; 50 | tr = $(""); 51 | table.append(tr); 52 | this.cell(tr, (d.comments ? d.comments : "")); 53 | this.link(tr, d); 54 | this.cell(tr, this.date(d.created_at)); 55 | _results.push(this.cell(tr, this.date(d.updated_at))); 56 | } 57 | return _results; 58 | }; 59 | 60 | Gists.prototype.link = function(tr, d) { 61 | var a, a2, desc, edit, m, p, re, td; 62 | td = $("
" + h + "
"); 63 | tr.append(td); 64 | re = /\[http:/; 65 | m = d.description.match(re); 66 | desc = m.index ? d.description.substring(0, m.index - 1) : d.description; 67 | if (m.index) { 68 | p = d.description.substring(m.index + 1).slice(0, -1); 69 | } 70 | a = $("", { 71 | "class": (d["public"] ? "public-gist-link" : "secret-gist-link"), 72 | href: d.html_url, 73 | target: "_blank", 74 | text: desc 75 | }); 76 | td.append(a); 77 | edit = $("", { 78 | "class": "edit-gist-link", 79 | href: "//gist.github.com/" + d.owner.login + "/" + d.id + "/edit", 80 | target: "_blank", 81 | text: "Edit" 82 | }); 83 | td.append(edit); 84 | if (p) { 85 | a2 = $("", { 86 | "class": "app-link", 87 | href: p, 88 | target: "_blank", 89 | text: "[" + p + "]" 90 | }); 91 | return td.append("
").append(a2); 92 | } 93 | }; 94 | 95 | Gists.prototype.cell = function(tr, txt) { 96 | var td; 97 | td = $("
"); 98 | tr.append(td); 99 | td.append(txt); 100 | return td; 101 | }; 102 | 103 | Gists.prototype.date = function(str) { 104 | var d; 105 | d = new Date(str); 106 | return d.toLocaleString(); 107 | }; 108 | 109 | Gists.prototype.setCredentials = function() { 110 | var make_base_auth; 111 | console.log("username/key", this.username, this.key); 112 | make_base_auth = function(user, password) { 113 | var hash, tok; 114 | tok = user + ':' + password; 115 | hash = btoa(tok); 116 | return "Basic " + hash; 117 | }; 118 | if (this.username && this.key) { 119 | return this.auth = make_base_auth(this.username, this.key); 120 | } 121 | }; 122 | 123 | Gists.prototype.authBeforeSend = function(xhr) { 124 | if (!this.auth) { 125 | return; 126 | } 127 | console.log("Set request header", this.auth); 128 | return xhr.setRequestHeader('Authorization', this.auth); 129 | }; 130 | 131 | return Gists; 132 | 133 | })(); 134 | 135 | new Gists; 136 | 137 | }).call(this); 138 | -------------------------------------------------------------------------------- /blabs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Blabr - Scientific computing for the web 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /blabs/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Verdana, 'DejaVu Sans', sans-serif; 3 | font-size: 10pt; 4 | } 5 | 6 | #list-gists { 7 | width: 1000px; 8 | padding: 0px 0px 20px 0px; 9 | margin: 0 auto; 10 | } 11 | 12 | #list-gists table, th, tr, td { 13 | border: 1px solid black; 14 | border-collapse: collapse; 15 | } 16 | 17 | #list-gists td, th { 18 | padding: 5px; 19 | max-width: 500px; 20 | } 21 | 22 | .public-gist-link { 23 | color: blue; 24 | } 25 | 26 | .secret-gist-link { 27 | color: #999; 28 | } 29 | 30 | .app-link { 31 | font-size: 8pt; 32 | color: #6c6; 33 | } 34 | 35 | .edit-gist-link { 36 | margin-left: 10px; 37 | padding: 2px 5px 2px 5px; 38 | background: #fc9; 39 | color: white; 40 | border-radius: 5px; 41 | text-decoration: none; 42 | font-size: 8pt; 43 | } 44 | -------------------------------------------------------------------------------- /build/guide.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Credits, Guide, GuideControl, content; 3 | 4 | console.log("Blabr Guide"); 5 | 6 | content = { 7 | syntaxTips: ["k = slider \"k\"", "table \"my-table\", x, y", "x = table \"t\", [], [-> z]", "plot \"my-plot\", x, y", "x = [1, 2, 3]", "x = [1..3]", "x = linspace 1, 3, 3", "square = (x) -> x*x", "# comment"], 8 | demos: [ 9 | { 10 | text: "Basic math", 11 | id: "58ef3095767efcdf1977" 12 | }, { 13 | text: "Basic plot", 14 | id: "3b19d13e5eaa11a4a540" 15 | }, { 16 | text: "Text", 17 | id: "277bf74a4b1e7364df29" 18 | }, { 19 | text: "Tables", 20 | id: "0a248098997a6f95635c" 21 | }, { 22 | text: "Sliders", 23 | id: "9b6fbf80ed838d1e1cef" 24 | }, { 25 | text: "Layout of components", 26 | id: "c7837da7dd136710e2ba" 27 | }, { 28 | text: "Function definitions", 29 | id: "e8457f2a62b292e1d0a2" 30 | }, { 31 | text: "Importing definitions", 32 | id: "d1889126b58315ba2239" 33 | }, { 34 | text: "Programming", 35 | id: "01df7d9c87dab79fccf0" 36 | }, { 37 | text: "Example: lunar crust I", 38 | id: "2c7e4d04634ee1d2aa40" 39 | }, { 40 | text: "Example: lunar crust II", 41 | id: "e9f3424ada245162d24f" 42 | } 43 | ], 44 | examples: [ 45 | { 46 | text: "Mystery Curve", 47 | img: "//blabr.github.io/img/mystery-curve.png", 48 | id: "4bd90a0b619bff7707b3" 49 | }, { 50 | text: "Basic Properties of Mars as a Planetary Body", 51 | img: "//spacemath.github.io/resources/images/thumbs/mars2.png", 52 | id: "3df6e2368e89a8c3f780" 53 | }, { 54 | text: "Exploring the Interior of Pluto", 55 | img: "//spacemath.github.io/resources/images/thumbs/planet-core.png", 56 | id: "d6927773b95652943582" 57 | }, { 58 | text: "Star Trek's solitons are real", 59 | img: "//blabr.github.io/img/solitons.png", 60 | id: "2a55efd937f9d3e87d29" 61 | }, { 62 | text: "A toy problem for compressive sensing", 63 | img: "//blabr.github.io/img/cs-intro.png", 64 | id: "e8a066234715f21c21fd" 65 | } 66 | ], 67 | references: [ 68 | { 69 | text: "Widgets", 70 | id: "ff66265ccd580d6a9b04" 71 | }, { 72 | text: "Language overview", 73 | id: "cac35c998a6640457c39" 74 | }, { 75 | text: "Definitions and imports", 76 | id: "919000f98b993fcfeb81" 77 | }, { 78 | text: "Math functions", 79 | id: "c19c10d7828efd13ddee" 80 | }, { 81 | text: "Vectors and matrices", 82 | id: "cb9ef53d61658dcedd45" 83 | }, { 84 | text: "Complex numbers", 85 | id: "c182256cc10492eb43b5" 86 | }, { 87 | text: "Linear algebra and numeric", 88 | id: "19516c877c92649672f4" 89 | }, { 90 | text: "Utilities", 91 | id: "ccd42df2e696df7e9317" 92 | } 93 | ], 94 | credits: [ 95 | { 96 | name: "Ace", 97 | url: "ace.c9.io" 98 | }, { 99 | name: "CoffeeScript", 100 | url: "coffeescript.org" 101 | }, { 102 | name: "MathJax", 103 | url: "mathjax.org" 104 | }, { 105 | name: "numericjs", 106 | url: "numericjs.com" 107 | }, { 108 | name: "Flot", 109 | url: "flotcharts.org" 110 | }, { 111 | name: "PaperScript", 112 | url: "paperjs.org/reference/paperscript" 113 | }, { 114 | name: "jQuery", 115 | url: "jquery.com" 116 | }, { 117 | name: "GitHub", 118 | url: "github.com" 119 | }, { 120 | name: "SpaceMath", 121 | url: "spacemath.gsfc.nasa.gov" 122 | } 123 | ] 124 | }; 125 | 126 | Guide = (function() { 127 | function Guide() { 128 | var refsCol; 129 | this.container = $("#demo-list"); 130 | this.container.hide(); 131 | this.isMain = $blab.resources.getSource == null; 132 | this.container.empty(); 133 | new $blab.utils.CloseButton(this.container, (function(_this) { 134 | return function() { 135 | return _this.container.slideUp(500, function() { 136 | var ref; 137 | return (ref = _this.guideControl) != null ? ref.show() : void 0; 138 | }); 139 | }; 140 | })(this)); 141 | this.tips(); 142 | this.demos(); 143 | this.examples(); 144 | refsCol = this.references(); 145 | new Credits(this.container, refsCol, content.credits); 146 | } 147 | 148 | Guide.prototype.append = function(txt) { 149 | return this.container.append(txt); 150 | }; 151 | 152 | Guide.prototype.slideDown = function() { 153 | return this.container.slideDown(500, (function(_this) { 154 | return function() { 155 | return _this.scroll(); 156 | }; 157 | })(this)); 158 | }; 159 | 160 | Guide.prototype.slideToggle = function() { 161 | return this.container.slideToggle(500, (function(_this) { 162 | return function() { 163 | return _this.scroll(); 164 | }; 165 | })(this)); 166 | }; 167 | 168 | Guide.prototype.scroll = function() { 169 | var cTop, ch, diff, wTop, wh; 170 | if (!this.container.is(":visible")) { 171 | return; 172 | } 173 | wTop = $(window).scrollTop(); 174 | cTop = this.container.offset().top; 175 | wh = $(window).height(); 176 | ch = this.container.height(); 177 | diff = cTop + ch - (wTop + wh); 178 | if (diff > 0) { 179 | return $("html, body").animate({ 180 | scrollTop: wTop + diff + 70 181 | }, 400); 182 | } 183 | }; 184 | 185 | Guide.prototype.tips = function() { 186 | var i, len, ref, str, tip; 187 | str = ""; 188 | ref = content.syntaxTips; 189 | for (i = 0, len = ref.length; i < len; i++) { 190 | tip = ref[i]; 191 | str += "" + tip + "
"; 192 | } 193 | return this.append("
\n

Quick Syntax Tips

\n " + str + "\n
"); 194 | }; 195 | 196 | Guide.prototype.demos = function() { 197 | return this.append("
\n

Demos

\n " + ($blab.demoListHtml({ 198 | blank: true 199 | })) + "\n
"); 200 | }; 201 | 202 | Guide.prototype.examples = function() { 203 | var example, i, len, ref, str; 204 | str = ""; 205 | ref = content.examples; 206 | for (i = 0, len = ref.length; i < len; i++) { 207 | example = ref[i]; 208 | str += ""; 209 | } 210 | return this.append("
\n

Examples

\n " + str + "\n More blabs\n
"); 211 | }; 212 | 213 | Guide.prototype.references = function() { 214 | var col; 215 | col = $("
", { 216 | "class": "guide-col" 217 | }); 218 | this.append(col); 219 | col.append("

Reference

\n" + ($blab.refListHtml({ 220 | blank: true 221 | })) + "\n
\n
CoffeeScript language guide\n

"); 222 | return col; 223 | }; 224 | 225 | return Guide; 226 | 227 | })(); 228 | 229 | GuideControl = (function() { 230 | function GuideControl(guide) { 231 | this.guide = guide; 232 | this.tagline = $("#blabr-tagline"); 233 | this.tagline.show(); 234 | this.button = $("#demo-list-button"); 235 | this.button.click((function(_this) { 236 | return function() { 237 | return typeof _this.click === "function" ? _this.click() : void 0; 238 | }; 239 | })(this)); 240 | this.show(); 241 | } 242 | 243 | GuideControl.prototype.show = function(show) { 244 | if (show == null) { 245 | show = true; 246 | } 247 | this.click = function() {}; 248 | this.tagline.animate({ 249 | opacity: (show ? 1 : 0) 250 | }); 251 | this.tagline.css({ 252 | cursor: (show ? "text" : "default") 253 | }); 254 | this.button.css({ 255 | cursor: (show ? "pointer" : "default") 256 | }); 257 | if (!show) { 258 | return; 259 | } 260 | return this.click = (function(_this) { 261 | return function() { 262 | _this.show(false); 263 | return _this.guide.slideDown(500); 264 | }; 265 | })(this); 266 | }; 267 | 268 | return GuideControl; 269 | 270 | })(); 271 | 272 | Credits = (function() { 273 | function Credits(container, containerButton, credits) { 274 | var credit, l, str; 275 | this.container = container; 276 | this.containerButton = containerButton; 277 | this.credits = credits; 278 | this.button = $("