├── .gitignore ├── Gemfile ├── public ├── slides.pdf ├── images │ ├── minus.gif │ └── plus.gif ├── css │ ├── chosen-sprite.png │ ├── editor.css │ └── chosen.css ├── js │ ├── globals.js │ ├── ace │ │ ├── theme-vibrant_ink.js │ │ ├── theme-textmate.js │ │ └── mode-javascript.js │ ├── editor.js │ ├── stacktrace.js │ ├── chosen.jquery.min.js │ └── jquery-1.4.3.min.js └── index.html ├── tasks ├── 04. Iteration methods │ ├── 08. Summing it up │ ├── 04. Mapping data │ ├── 05. Check all those elements │ ├── 02. Traversing arrays with forEach │ ├── 01. Functional fibonaci │ ├── 03. Find what you want │ ├── 07. Reduce our answer │ ├── 06. Lets find that needle │ └── 09. Bringing it together, mapReduce... ├── 02. Functions │ ├── 08. What is this │ ├── 04. Variables by reference (intermediate) │ ├── 09. Constructing objects │ ├── 05. Binding scope │ ├── 07. Variables by reference, revisited (hard) │ ├── 02. Higher order functions │ ├── 11. Is this yours (intermediate) │ ├── 10. Context confusion (hard) │ ├── 01. How many arguments do you have │ ├── 03. JavaScript pwns your scope (hard) │ └── 06. Globally ugly, privately slow, closing sweet (intermediate) ├── 01. Object Literals │ ├── 01. Object properties │ ├── 02. Populating arrays │ ├── 04. Who are you │ └── 03. What have you got └── 03. Inheritance │ ├── 01. Pseudoclassical 1 (intermediate) │ ├── 04. Functional (intermediate) │ ├── 02. Pseudoclassical 2 (intermediate) │ └── 03. Prototypal (hard) ├── Gemfile.lock ├── README.md └── config.ru /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem "sinatra" 4 | gem "json" 5 | -------------------------------------------------------------------------------- /public/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnab/js-workshop/HEAD/public/slides.pdf -------------------------------------------------------------------------------- /public/images/minus.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnab/js-workshop/HEAD/public/images/minus.gif -------------------------------------------------------------------------------- /public/images/plus.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnab/js-workshop/HEAD/public/images/plus.gif -------------------------------------------------------------------------------- /public/css/chosen-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnab/js-workshop/HEAD/public/css/chosen-sprite.png -------------------------------------------------------------------------------- /tasks/04. Iteration methods/08. Summing it up: -------------------------------------------------------------------------------- 1 | Implement a basic sum function using what we have learned so far. 2 | -- 3 | -------------------------------------------------------------------------------- /tasks/02. Functions/08. What is this: -------------------------------------------------------------------------------- 1 | What is this? 2 | -- 3 | var add = function(a, b) { 4 | this.result = a + b; 5 | }; 6 | 7 | add(1, 2); 8 | 9 | assert( ... === 3); -------------------------------------------------------------------------------- /tasks/02. Functions/04. Variables by reference (intermediate): -------------------------------------------------------------------------------- 1 | Log numbers 0-4 correctly in by extracting the setTimeout call to bind variable i: 2 | -- 3 | var i; 4 | for (i = 0; i < 5; i++) { 5 | setTimeout(function () { 6 | console.log(i); 7 | }, i*1000); 8 | } 9 | -------------------------------------------------------------------------------- /tasks/02. Functions/09. Constructing objects: -------------------------------------------------------------------------------- 1 | Construct two car object instances. 2 | -- 3 | function Car (type) { 4 | this.type = type; 5 | } 6 | 7 | var honda = ... 8 | var bmw = ... 9 | 10 | assert(honda.type === 'honda'); 11 | assert(bmw.type === 'bmw'); 12 | -------------------------------------------------------------------------------- /tasks/02. Functions/05. Binding scope: -------------------------------------------------------------------------------- 1 | Make the function times return a function that returns the next multiple of n for each subsequent call. 2 | -- 3 | var times = function (n) { 4 | 5 | }; 6 | 7 | var times2 = times(2); 8 | times2(); // 2 9 | times2(); // 4 10 | times2(); // 6 11 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | json (1.5.3) 5 | rack (1.3.2) 6 | sinatra (1.2.6) 7 | rack (~> 1.1) 8 | tilt (>= 1.2.2, < 2.0) 9 | tilt (1.3.2) 10 | 11 | PLATFORMS 12 | ruby 13 | 14 | DEPENDENCIES 15 | json 16 | sinatra 17 | -------------------------------------------------------------------------------- /tasks/02. Functions/07. Variables by reference, revisited (hard): -------------------------------------------------------------------------------- 1 | Log numbers 0-4 correctly by wrapping the anonomous function in a function that is run immediately to bind variable i: 2 | -- 3 | var i; 4 | for (i = 0; i < 5; i++) { 5 | setTimeout(function () { 6 | console.log(i); 7 | }, i*1000); 8 | } 9 | -------------------------------------------------------------------------------- /tasks/01. Object Literals/01. Object properties: -------------------------------------------------------------------------------- 1 | Set the three properties, in three different ways. 2 | -- 3 | var person = { 4 | 5 | }; 6 | 7 | 8 | assert(person.name === 'bob', 'name should equal "bob"'); 9 | assert(person.gender === 'male', 'gender should equal "male"'); 10 | assert(person.weight === 100, 'weight should equal 100'); 11 | -------------------------------------------------------------------------------- /tasks/01. Object Literals/02. Populating arrays: -------------------------------------------------------------------------------- 1 | Populate the array, please. 2 | -- 3 | var arr = []; 4 | var i; 5 | for (i = 0; i < 10; i += 1) { 6 | // .. 7 | } 8 | 9 | assert(arr[0] === 0, 'first element should equal 0'); 10 | assert(arr[1] === 1, 'second element should equal 1'); 11 | assert(arr.length === 10, 'array length should be 10'); 12 | -------------------------------------------------------------------------------- /tasks/02. Functions/02. Higher order functions: -------------------------------------------------------------------------------- 1 | Create a function that returns new functions that return true! 2 | -- 3 | var createFunction = function () {}; 4 | 5 | assert(typeof createFunction() === 'function', 6 | 'createFunction() should return a function'); 7 | assert(createFunction()() === true, 8 | "calling createFunction's returned function should return true"); 9 | -------------------------------------------------------------------------------- /tasks/02. Functions/11. Is this yours (intermediate): -------------------------------------------------------------------------------- 1 | Use Bob's greet method to greet Ed! Don't repeat yourself, please. 2 | -- 3 | var bob = { 4 | name: 'Bob', 5 | greet: function () { 6 | return 'O hai ' + this.name + '!'; 7 | } 8 | }; 9 | 10 | var ed = { 11 | name: 'Ed' 12 | }; 13 | 14 | assert(bob.greet() === 'O hai Bob!'); 15 | assert( ... === 'O hai Ed!'); 16 | -------------------------------------------------------------------------------- /tasks/02. Functions/10. Context confusion (hard): -------------------------------------------------------------------------------- 1 | Fix the following code by adding one line and changing one word. 2 | -- 3 | var PI = { 4 | factor : 3.14, 5 | times : function (a) { 6 | 7 | var calculate = function (num) { 8 | return num * this.factor; // wtf? 9 | }; 10 | 11 | return calculate(a); 12 | } 13 | } 14 | 15 | assert(PI.times(2) === 6.28); 16 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/04. Mapping data: -------------------------------------------------------------------------------- 1 | Find the square root of all the elements using map() 2 | -- 3 | var data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | 5 | // Implement the map function so that it returns a new array with all the 6 | // results from the func calls. 7 | 8 | function map(array, func) { 9 | 10 | } 11 | 12 | var result = map(data, Math.sqrt); 13 | console.log(result); 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #js-workshop 2 | 3 | Workshop tasks and in-browser JavaScript editor and evaluator. 4 | 5 | ### Install 6 | git clone git://github.com/gnab/js-workshop.git 7 | cd js-workshop 8 | bundle install # You need Ruby and the bundler gem (gem install bundler) 9 | 10 | ### Run 11 | rackup 12 | 13 | The `rackup` command will launch the app at http://localhost:9292 by default. 14 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/05. Check all those elements: -------------------------------------------------------------------------------- 1 | Test all values in the data set 2 | -- 3 | function greaterThanZero(element, index, array) { 4 | return element > 0; 5 | } 6 | 7 | // Implement every so that it returns true if all calls to func where true. 8 | 9 | function every(array, func) { 10 | 11 | } 12 | 13 | assert(every([1, 2, 3], greaterThanZero) === true); 14 | assert(every([1, -2, 3], greaterThanZero) === false); 15 | -------------------------------------------------------------------------------- /tasks/02. Functions/01. How many arguments do you have: -------------------------------------------------------------------------------- 1 | Complete the sum function, making it sum all arguments, no matter how many you give it. 2 | -- 3 | 4 | function sum () { 5 | var s = 0; 6 | 7 | return s; 8 | } 9 | 10 | assert(sum() === 0, 'sum without arguments should be 0'); 11 | assert(sum(1, 2) === 3, 'sum with arguments 1 and 2 should equal 3'); 12 | assert(sum(1, 2, 3) === 6, 'sum with arguments 1, 2, and 3 should equal 6'); 13 | -------------------------------------------------------------------------------- /tasks/01. Object Literals/04. Who are you: -------------------------------------------------------------------------------- 1 | Make the doubleMe function return the argument type followed by the value doubled for numbers and the string repeated twice for strings. 2 | -- 3 | function doubleMe (val) { 4 | 5 | } 6 | 7 | assert(doubleMe(17) === 'number 34', 8 | '17 doubled should equal "number 34"'); 9 | assert(doubleMe('painnkaak') === 'string painnkaakpainnkaak', 10 | 'painnkaak doubled should equal "string painnkaakpainnkaak"'); 11 | -------------------------------------------------------------------------------- /tasks/03. Inheritance/01. Pseudoclassical 1 (intermediate): -------------------------------------------------------------------------------- 1 | Utilize the prototype property to have Car instances share the honk method rather than each creating their own. 2 | -- 3 | var Car = function Car () { 4 | this.honk = function () { 5 | console.log('Honk!'); 6 | }; 7 | }; 8 | 9 | //Car.proto.. 10 | 11 | var bmw = new Car(); 12 | bmw.honk(); 13 | 14 | var honda = new Car(); 15 | honda.honk(); 16 | 17 | assert(bmw.honk === honda.honk); 18 | -------------------------------------------------------------------------------- /tasks/03. Inheritance/04. Functional (intermediate): -------------------------------------------------------------------------------- 1 | Make honda h "inherit" car's honk method by changing one line of code. 2 | -- 3 | var car = function (spec) { 4 | spec = spec || {}; 5 | var that = {}; 6 | 7 | that.honk = function () { 8 | return spec.sound; 9 | }; 10 | 11 | return that; 12 | }; 13 | 14 | var honda = function (spec) { 15 | var that = {}; 16 | return that; 17 | }; 18 | 19 | var h = honda({ sound: 'honk!' }); 20 | assert(h.honk() === 'honk!'); 21 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/02. Traversing arrays with forEach: -------------------------------------------------------------------------------- 1 | Print all the numbers in the array using your own forEach 2 | -- 3 | var data = [1, 2, 3, 4, 5]; 4 | 5 | // Implement forEach so that it calls func with the current element, the index 6 | // and the array on all the items. 7 | 8 | function forEach(array, func) { 9 | 10 | } 11 | 12 | forEach(data, function(element, index, array) { 13 | console.log(element); 14 | }); 15 | 16 | // What about hashes, does your forEach support traversing them? 17 | -------------------------------------------------------------------------------- /tasks/03. Inheritance/02. Pseudoclassical 2 (intermediate): -------------------------------------------------------------------------------- 1 | Hondas cannot honk! Try to fix this so that new Hondas will share Car's honk method, utilizing Hondas prototype property! 2 | -- 3 | function Car () {} 4 | Car.prototype.honk = function () { 5 | console.log('Honk!'); 6 | }; 7 | 8 | function Honda () {} 9 | // Honda.proto.. 10 | 11 | var h1 = new Honda(); 12 | h1.honk(); 13 | 14 | var h2 = new Honda(); 15 | h2.honk(); 16 | 17 | assert(h1.honk === h2.honk, "the Hondas does not share Car's honk method"); 18 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/01. Functional fibonaci: -------------------------------------------------------------------------------- 1 | Write a basic functional fibonaci in JS :) 2 | -- 3 | 4 | // Feel free to skip this one if you feel you have done it enough times before 5 | // :) 6 | 7 | // Implement a fibonaci function using basic functional programing. 8 | 9 | function fib(n) { 10 | 11 | } 12 | 13 | assert(fib(1) == 1, 'fib(1) != 1'); 14 | assert(fib(2) == 1, 'fib(2) != 1'); 15 | assert(fib(3) == 2, 'fib(3) != 2'); 16 | assert(fib(4) == 3, 'fib(4) != 3'); 17 | assert(fib(9) == 34, 'fib(9) != 34'); 18 | -------------------------------------------------------------------------------- /tasks/03. Inheritance/03. Prototypal (hard): -------------------------------------------------------------------------------- 1 | Give the car the honk method using protoypal inheritance, inheriting directly from the utilities object. 2 | -- 3 | var utilities = { 4 | honk: function () { 5 | console.log('Honk!'); 6 | } 7 | }; 8 | 9 | var create = function create (prototype) { 10 | function F () {} 11 | // .. 12 | return new F(); 13 | }; 14 | 15 | var bmw = create(utilities); 16 | bmw.honk(); 17 | 18 | var honda = create(utilities); 19 | honda.honk(); 20 | 21 | assert(bmw.honk === honda.honk); 22 | -------------------------------------------------------------------------------- /tasks/01. Object Literals/03. What have you got: -------------------------------------------------------------------------------- 1 | Complete createCopy, so that all properties of the original are transfered to the copy. 2 | -- 3 | function createCopy (original) { 4 | var copy = []; 5 | for ( ... ) { 6 | copy[k] = original[k]; 7 | } 8 | return copy; 9 | } 10 | 11 | var a = []; 12 | a.key = 1; 13 | a['key2'] = 1; 14 | a.push(2); 15 | 16 | var b = createCopy(a); 17 | 18 | assert(b[0] === 2, 'element was not copied'); 19 | assert(b.key === 1, 'key was not copied'); 20 | assert(b['key2'] === 1, 'key2 was not copied'); 21 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/03. Find what you want: -------------------------------------------------------------------------------- 1 | Using your forEach function build a odd number filter 2 | -- 3 | var data = [1, 2, 3, 4, 5, 6, 7, 9, 10]; 4 | 5 | function isOdd(element, index, array) { 6 | return element % 2 == 1; 7 | } 8 | 9 | // Implement filter so that it returns an array with all the array elements that 10 | // func returned true for. 11 | 12 | function filter(array, func) { 13 | 14 | } 15 | 16 | var result = filter(data, isOdd); 17 | 18 | console.log(result); 19 | 20 | // How would you use this basis to create a filterOdd function? 21 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/07. Reduce our answer: -------------------------------------------------------------------------------- 1 | Now lets see if we can accumulate an answer... 2 | -- 3 | var data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | 5 | function multiply(previousValue, currentValue, index, array) { 6 | return previousValue * currentValue; 7 | } 8 | 9 | // The reduce function needs to keep track of an accumulator which is the 10 | // return value from the callback. The callback should be called in the current 11 | // accumulator, the current value, index and array like the other iterator 12 | // methods. 13 | 14 | function reduce(array, func, initial) { 15 | 16 | } 17 | 18 | var result = reduce(data, multiply, 1); 19 | 20 | assert(result === 3628800, result); 21 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | require 'json' 3 | 4 | mime_type :less, 'text/css' 5 | 6 | get '/' do 7 | File.new('public/index.html').readlines 8 | end 9 | 10 | get '/tasks.js' do 11 | JSON(Dir['tasks/*/*'].sort.inject({}) { |sections, path| 12 | section, task = path.split('/')[1..2] 13 | sections[section] ||= {:name => section, :tasks => []} 14 | 15 | description, code = File.readlines(path).join.split(/^--$/m). 16 | collect { |text| text.strip }[0..1] 17 | 18 | sections[section][:tasks].push({ 19 | :name => task, 20 | :description => description || '', 21 | :code => code || '' 22 | }) 23 | sections 24 | }.values.sort_by {|section| section[:name] }) 25 | end 26 | 27 | run Sinatra::Application 28 | -------------------------------------------------------------------------------- /tasks/02. Functions/03. JavaScript pwns your scope (hard): -------------------------------------------------------------------------------- 1 | Make all asserts pass by uncommenting each assert and only changing double dots into variables, function calls and typeof-s. 2 | -- 3 | var a = 1; 4 | b = 3; 5 | var s = 1; 6 | 7 | (function () { 8 | a = 2; 9 | b = 4; 10 | var C = 3; 11 | d = 1; 12 | s = 2; 13 | 14 | if (false) { 15 | var s; 16 | function f () { 17 | var s = 7; 18 | return s; 19 | } 20 | } 21 | 22 | assert( .. === 2); 23 | // assert( .. === 3); 24 | // assert( .. === 4); 25 | // assert( .. === 1); 26 | // assert( .. === 7); 27 | // assert( .. === 2); 28 | 29 | })(); 30 | 31 | // assert( .. === 2); 32 | // assert( .. === 4); 33 | // assert( .. === 'undefined'); 34 | // assert( .. === 1); 35 | // assert( .. === 'undefined'); 36 | // assert( .. === 1); 37 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/06. Lets find that needle: -------------------------------------------------------------------------------- 1 | Find the needle in the haystack 2 | -- 3 | var data = [/* 0 .. 100 */]; 4 | for (var i = 0; i < 100; i++) { 5 | data.push(i); 6 | } 7 | 8 | function sumEqualProduct(element, index, array) { 9 | var parts = (element + "").split("", 2); 10 | var first = parseInt(parts[0], 10); 11 | var second = parseInt(parts[1], 10); 12 | return first+second == first*second; 13 | } 14 | 15 | // Implement the some function so that it indicates if we have at least one 16 | // result from the function that is true. 17 | 18 | function some(array, func) { 19 | 20 | } 21 | 22 | console.log(some(data, sumEqualProduct)); 23 | 24 | // Now use another list manipulation technique to find out which number we 25 | // found :) 26 | 27 | // Does your implementation loop over all the values for all cases, is this 28 | // necessary? 29 | -------------------------------------------------------------------------------- /tasks/02. Functions/06. Globally ugly, privately slow, closing sweet (intermediate): -------------------------------------------------------------------------------- 1 | Create a third variant of the isOneTwoOrThree-methods utilizing JavaScript's closures; where data is private, the function is fast - it's sweet. 2 | -- 3 | var data = [1, 2, 3]; 4 | var uglyIsOneTwoOrThree = function (n) { 5 | return data.indexOf(n) != -1; 6 | }; 7 | assert(uglyIsOneTwoOrThree(1) === true); 8 | assert(uglyIsOneTwoOrThree(2) === true); 9 | assert(uglyIsOneTwoOrThree(4) === false); 10 | 11 | 12 | var slowIsOneTwoOrThree = function (n) { 13 | var data = [1, 2, 3]; 14 | return data.indexOf(n) != -1; 15 | }; 16 | assert(slowIsOneTwoOrThree(1) === true); 17 | assert(slowIsOneTwoOrThree(2) === true); 18 | assert(slowIsOneTwoOrThree(4) === false); 19 | 20 | 21 | var sweetIsOneTwoOrThree = function () { 22 | 23 | }(); 24 | assert(sweetIsOneTwoOrThree(1) === true); 25 | assert(sweetIsOneTwoOrThree(2) === true); 26 | assert(sweetIsOneTwoOrThree(4) === false); 27 | -------------------------------------------------------------------------------- /public/js/globals.js: -------------------------------------------------------------------------------- 1 | var iter = { 2 | forEach: function (list, func) { 3 | for (var key in list) { 4 | func(list[key], key, list); 5 | } 6 | }, 7 | filter: function(list, func) { 8 | var res = []; 9 | this.forEach(list, function(element, index, list) { 10 | if (func(element, index, list)) { 11 | res.push(element); 12 | } 13 | }); 14 | return res; 15 | }, 16 | map: function(list, func) { 17 | var res = []; 18 | this.forEach(list, function(element, index, list) { 19 | res.push(func(element, index, list)); 20 | }); 21 | return res; 22 | }, 23 | every: function(list, func) { 24 | var res = true; 25 | this.forEach(list, function(element, index, list) { 26 | res = res && func(element, index, list); 27 | }); 28 | return res; 29 | }, 30 | reduce: function(list, func, initial) { 31 | var accumelator = initial; 32 | this.forEach(list, function(element, index, list) { 33 | accumelator = func(accumelator, element, index, list); 34 | }); 35 | return accumelator; 36 | }, 37 | groupBy: function(list, keyFunc, valFunc) { 38 | var groups = {}; 39 | iter.forEach(list, function(element, index, list) { 40 | var key = element[0]; 41 | if (!groups[key]) { 42 | groups[key] = []; 43 | } 44 | groups[key].push(element[1]); 45 | }); 46 | return groups; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | js-workshop 8 | 9 | 10 | 19 |
20 | Choose a task in the drop down list to get started! 21 |
22 |
23 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/09. Bringing it together, mapReduce...: -------------------------------------------------------------------------------- 1 | Now lets try and combine these techniques... 2 | -- 3 | var text = ""; 4 | text += "Lorem ipsum dolor sit amet consectetur adipiscing elit Etiam euismod ipsum a"; 5 | text += "convallis pulvinar dui arcu scelerisque mauris luctus consectetur felis lectus"; 6 | text += "sit amet purus In hendrerit ligula at venenatis viverra ipsum dolor tincidunt"; 7 | text += "nulla a cursus ipsum sem id felis Nunc pretium aliquam nisi a molestie"; 8 | text += "Pellentesque laoreet malesuada arcu Nullam tempor dignissim lobortis"; 9 | text += "Suspendisse sed mi augue eu imperdiet orci Cras auctor odio id augue aliquet"; 10 | text += "suscipit Pellentesque laoreet elementum mauris non ullamcorper Sed ac urna"; 11 | text += "vitae eros semper molestie Donec consectetur nunc euismod vehicula adipiscing"; 12 | text += "massa lectus consequat erat eget bibendum nunc nibh eu sapien"; 13 | 14 | var words = text.split(' '); 15 | 16 | // For this task we want to count the occurrence of words of various lengths. 17 | // 18 | // Step 1: Generate key value pairs for the problem we are solving. 19 | // Normally this is spread across multiple machines, but for the sake 20 | // of simplicity we will only use one "node". Mapped should contain: 21 | // [[5, 'Lorem'], [5, 'ipsum'], [4, 'dolor'], ...] 22 | 23 | var mapped = iter.map(words, function(element, index, array) { 24 | 25 | }); 26 | 27 | // Step 2: Group the list of key value pairs by their key. 28 | // By using an hash as an initial value we should be able to group our 29 | // results in it with array values. We could also use a dedicated 30 | // groupBy function to perfom such a task. Grouped should contain: 31 | // {5: ['Lorem', 'ipsum', ...], 4: ['dolor', ...], ...} 32 | 33 | var grouped = iter.reduce(mapped, function(previousValue, currentValue, index, array) { 34 | 35 | }, {}); 36 | 37 | // Step 3: Reduce each group into an answer to your problem. 38 | // Normally this task would also be distributed, but as this is a single 39 | // "node" we will need to do all of them our selves. 40 | 41 | for (var key in grouped) { 42 | var count = iter.reduce( ) 43 | console.log('Words of length ' + key + ': ' + count); 44 | } 45 | -------------------------------------------------------------------------------- /public/js/ace/theme-vibrant_ink.js: -------------------------------------------------------------------------------- 1 | define("ace/theme/vibrant_ink",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-vibrant-ink .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-vibrant-ink .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-vibrant-ink .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-vibrant-ink .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-vibrant-ink .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-vibrant-ink .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-vibrant-ink .ace_scroller {\n background-color: #0F0F0F;\n}\n\n.ace-vibrant-ink .ace_text-layer {\n cursor: text;\n color: #FFFFFF;\n}\n\n.ace-vibrant-ink .ace_cursor {\n border-left: 2px solid #FFFFFF;\n}\n\n.ace-vibrant-ink .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #FFFFFF;\n}\n \n.ace-vibrant-ink .ace_marker-layer .ace_selection {\n background: #6699CC;\n}\n\n.ace-vibrant-ink .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-vibrant-ink .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #99CC99;\n}\n\n.ace-vibrant-ink .ace_marker-layer .ace_active_line {\n background: #333333;\n}\n\n \n.ace-vibrant-ink .ace_invisible {\n color: #404040;\n}\n\n.ace-vibrant-ink .ace_keyword {\n color:#FF6600;\n}\n\n.ace-vibrant-ink .ace_keyword.ace_operator {\n \n}\n\n.ace-vibrant-ink .ace_constant {\n \n}\n\n.ace-vibrant-ink .ace_constant.ace_language {\n color:#339999;\n}\n\n.ace-vibrant-ink .ace_constant.ace_library {\n \n}\n\n.ace-vibrant-ink .ace_constant.ace_numeric {\n color:#99CC99;\n}\n\n.ace-vibrant-ink .ace_invalid {\n color:#CCFF33;\n background-color:#000000;\n}\n\n.ace-vibrant-ink .ace_invalid.ace_illegal {\n \n}\n\n.ace-vibrant-ink .ace_invalid.ace_deprecated {\n color:#CCFF33;\n background-color:#000000;\n}\n\n.ace-vibrant-ink .ace_support {\n \n}\n\n.ace-vibrant-ink .ace_support.ace_function {\n color:#FFCC00;\n}\n\n.ace-vibrant-ink .ace_function.ace_buildin {\n \n}\n\n.ace-vibrant-ink .ace_string {\n color:#66FF00;\n}\n\n.ace-vibrant-ink .ace_string.ace_regexp {\n \n}\n\n.ace-vibrant-ink .ace_comment {\n color:#9933CC;\n}\n\n.ace-vibrant-ink .ace_comment.ace_doc {\n \n}\n\n.ace-vibrant-ink .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-vibrant-ink .ace_variable {\n \n}\n\n.ace-vibrant-ink .ace_variable.ace_language {\n \n}\n\n.ace-vibrant-ink .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-vibrant-ink"}) -------------------------------------------------------------------------------- /public/js/ace/theme-textmate.js: -------------------------------------------------------------------------------- 1 | define("ace/theme/textmate",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-tm .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-tm .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-tm .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-tm .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-tm .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-tm .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-tm .ace_text-layer {\n cursor: text;\n}\n\n.ace-tm .ace_cursor {\n border-left: 2px solid black;\n}\n\n.ace-tm .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid black;\n}\n \n.ace-tm .ace_line .ace_invisible {\n color: rgb(191, 191, 191);\n}\n\n.ace-tm .ace_line .ace_keyword {\n color: blue;\n}\n\n.ace-tm .ace_line .ace_constant.ace_buildin {\n color: rgb(88, 72, 246);\n}\n\n.ace-tm .ace_line .ace_constant.ace_language {\n color: rgb(88, 92, 246);\n}\n\n.ace-tm .ace_line .ace_constant.ace_library {\n color: rgb(6, 150, 14);\n}\n\n.ace-tm .ace_line .ace_invalid {\n background-color: rgb(153, 0, 0);\n color: white;\n}\n\n.ace-tm .ace_line .ace_fold {\n background-color: #E4E4E4;\n border-radius: 3px;\n}\n\n.ace-tm .ace_line .ace_support.ace_function {\n color: rgb(60, 76, 114);\n}\n\n.ace-tm .ace_line .ace_support.ace_constant {\n color: rgb(6, 150, 14);\n}\n\n.ace-tm .ace_line .ace_support.ace_type,\n.ace-tm .ace_line .ace_support.ace_class {\n color: rgb(109, 121, 222);\n}\n\n.ace-tm .ace_line .ace_keyword.ace_operator {\n color: rgb(104, 118, 135);\n}\n\n.ace-tm .ace_line .ace_string {\n color: rgb(3, 106, 7);\n}\n\n.ace-tm .ace_line .ace_comment {\n color: rgb(76, 136, 107);\n}\n\n.ace-tm .ace_line .ace_comment.ace_doc {\n color: rgb(0, 102, 255);\n}\n\n.ace-tm .ace_line .ace_comment.ace_doc.ace_tag {\n color: rgb(128, 159, 191);\n}\n\n.ace-tm .ace_line .ace_constant.ace_numeric {\n color: rgb(0, 0, 205);\n}\n\n.ace-tm .ace_line .ace_variable {\n color: rgb(49, 132, 149);\n}\n\n.ace-tm .ace_line .ace_xml_pe {\n color: rgb(104, 104, 91);\n}\n\n.ace-tm .ace_marker-layer .ace_selection {\n background: rgb(181, 213, 255);\n}\n\n.ace-tm .ace_marker-layer .ace_step {\n background: rgb(252, 255, 0);\n}\n\n.ace-tm .ace_marker-layer .ace_stack {\n background: rgb(164, 229, 101);\n}\n\n.ace-tm .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgb(192, 192, 192);\n}\n\n.ace-tm .ace_marker-layer .ace_active_line {\n background: rgba(0, 0, 0, 0.07);\n}\n\n.ace-tm .ace_marker-layer .ace_selected_word {\n background: rgb(250, 250, 255);\n border: 1px solid rgb(200, 200, 250);\n}\n\n.ace-tm .ace_string.ace_regex {\n color: rgb(255, 0, 0)\n}";d.importCssString(e),b.cssClass="ace-tm"}) -------------------------------------------------------------------------------- /public/css/editor.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | background-color: #fff; 3 | font-family: Helvetica, Arial; 4 | font-size: 13px; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | #header { 9 | border-bottom: 1px solid #bbb; 10 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e0e0e0)); 11 | background-image: -moz-linear-gradient(top, #f5f5f5, #e0e0e0); 12 | -webkit-box-shadow: 0 0 10px #ddd; 13 | -moz-box-shadow: 0 0 10px #ddd; 14 | box-shadow: 0 0 10px #ddd; 15 | padding: 14px 14px 10px 14px; 16 | position: relative; 17 | } 18 | #logo { 19 | color: #444; 20 | font-family: Amaranth, 'Gloria Hallelujah'; 21 | font-size: 3em; 22 | float: left; 23 | margin-top: -14px; 24 | margin-right: 1em; 25 | text-decoration: none; 26 | text-shadow: #aaa 0px 0px 6px; 27 | } 28 | #logo .js { 29 | color: #f5f5f5; 30 | } 31 | #tasks { 32 | width: 500px; 33 | } 34 | #tasks_chzn { 35 | float: left; 36 | margin-right: 1em; 37 | } 38 | .button { 39 | float: left; 40 | margin-right: 0.8em; 41 | padding: 0 8px 0 8px !important; 42 | } 43 | .clear { 44 | clear: both; 45 | } 46 | #description { 47 | position: absolute; 48 | top: 75px; 49 | left: 18px; 50 | right: 430px; 51 | height: 17px; 52 | white-space: nowrap; 53 | overflow: hidden; 54 | text-overflow: ellipsis; 55 | } 56 | #editor { 57 | bottom: 20px; 58 | font-family: Mono, Monaco, Consolas, "Lucida Console", "Courier New"; 59 | font-size: 14px; 60 | left: 10px; 61 | position: absolute; 62 | right: 430px; 63 | top: 110px; 64 | } 65 | #sidebar { 66 | border-left: 1px solid #ccc; 67 | bottom: 20px; 68 | position: absolute; 69 | right: 20px; 70 | top: 75px; 71 | width: 400px; 72 | } 73 | #log, #lint { 74 | left: 10px; 75 | position: absolute; 76 | right: 0; 77 | } 78 | #log { 79 | bottom: 210px; 80 | top: 0; 81 | } 82 | #lint { 83 | bottom: 0; 84 | height: 200px; 85 | } 86 | .header { 87 | border-bottom: 1px solid #bbb; 88 | padding-bottom: 8px; 89 | } 90 | #log .content, #lint .content { 91 | overflow: auto; 92 | position: absolute; 93 | bottom: 0; 94 | top: 30px; 95 | right: 0; 96 | left: 0; 97 | } 98 | #log .info, #lint .info { 99 | color: #aaa; 100 | font-style: italic; 101 | } 102 | #log .error, #lint .error { 103 | color: #d32; 104 | } 105 | #log pre { 106 | border: 1px solid #ccc; 107 | margin: 0; 108 | margin-left: -16px; 109 | overflow-x: auto; 110 | } 111 | #log .closed, #log .open { 112 | padding-left: 16px; 113 | background: transparent no-repeat 3px 3px; 114 | cursor: pointer; 115 | } 116 | 117 | #log .closed pre { 118 | display: none; 119 | } 120 | #log .closed { 121 | background-image: url(/images/plus.gif); 122 | } 123 | #log .open { 124 | background-image: url(/images/minus.gif); 125 | } 126 | #log .line, #lint .line { 127 | text-decoration: underline; 128 | cursor: pointer; 129 | display: inline-block; 130 | margin-right: 1em; 131 | } 132 | -------------------------------------------------------------------------------- /public/js/editor.js: -------------------------------------------------------------------------------- 1 | !function(context) { 2 | 3 | function setUpEditor() { 4 | var editor = ace.edit('editor'); 5 | editor.setTheme('ace/theme/textmate'); 6 | var jsMode = require('ace/mode/javascript').Mode; 7 | editor.getSession().setMode(new jsMode()); 8 | 9 | editor.setShowPrintMargin(false); 10 | editor.getSession().setUseSoftTabs(true); 11 | editor.getSession().setTabSize(2); 12 | editor.renderer.setPadding(10); 13 | 14 | editor.focus(); 15 | 16 | $('.ace_gutter').css({ 17 | background: 'transparent', 18 | color: '#999' 19 | }); 20 | $('.ace_scroller').css({ 21 | 'overflow-x': 'auto', 22 | }); 23 | $('.ace_sb').css({ 24 | 'overflow-y': 'auto', 25 | }); 26 | 27 | return editor; 28 | } 29 | 30 | function setUpLog() { 31 | var log = $('#log .content'); 32 | var lintlog = $('#lint .content'); 33 | 34 | if (typeof(console) === 'undefined') { 35 | console = {}; 36 | } 37 | 38 | extendMethod(console, 'log', function(obj) { 39 | appendToLog(log, obj); 40 | }); 41 | 42 | extendMethod(console, 'error', function(obj) { 43 | appendToLog(log, obj, 'error'); 44 | }); 45 | 46 | extendMethod(console, 'info', function(obj) { 47 | appendToLog(log, obj, 'info'); 48 | }); 49 | 50 | extendMethod(console, 'clear', function() { 51 | log.children().remove(); 52 | }); 53 | 54 | lintconsole = { 55 | 'info': function(obj) { 56 | appendToLog(lintlog, obj, 'info'); 57 | }, 58 | 'error': function(obj, line) { 59 | appendToLog(lintlog, obj, 'error', line); 60 | }, 61 | 'clear': function() { 62 | lintlog.children().remove() 63 | } 64 | }; 65 | 66 | console.clear(); 67 | lintconsole.clear(); 68 | } 69 | 70 | function extendMethod(obj, method, extra) { 71 | var _method = obj[method]; 72 | obj[method] = function() { 73 | var ret, args; 74 | if (_method) { 75 | ret = _method.apply(obj, arguments); 76 | } 77 | args = Array.prototype.slice.apply(arguments); 78 | args.push(ret); 79 | return extra.apply(obj, args); 80 | } 81 | } 82 | 83 | function appendToLog(log, obj, type, line) { 84 | var element = $('
'); 85 | if (type) { 86 | element.addClass(type); 87 | } 88 | if (typeof(obj) === 'undefined') { 89 | element.text('undefined'); 90 | } else if (obj === null) { 91 | element.text('null'); 92 | } else if (typeof(obj) != 'object') { 93 | element.text(obj); 94 | } else { 95 | var json = $('
');
 96 |       json.text(JSON.stringify(obj, null, '  '));
 97 |       if (obj instanceof Error) {
 98 |         element.text(obj + '');
 99 |       } else if (obj instanceof Array) {
100 |         if (obj.length > 3) {
101 |           element.text('[' + obj.slice(0, 3).join(', ') + ', ... ]');
102 |         } else {
103 |           element.text('[' + obj.join(', ') + ']');
104 |         }
105 |       } else {
106 |         element.text(Object.prototype.toString.call(obj));
107 |       }
108 |       element.addClass('closed');;
109 |       element.append(json)
110 |       element.click(function() {
111 |         element.toggleClass('closed');
112 |         element.toggleClass('open');
113 |       });
114 |     }
115 |     if (line) {
116 |       element.prepend($('').text('line ' + line));
117 |     }
118 |     log.append(element);
119 |     log.scrollTop(log[0].scrollHeight);
120 |   }
121 | 
122 |   function createSandbox(globals) {
123 |     /* Inspired by http://dean.edwards.name/weblog/2006/11/sandbox/ */
124 |     var iframe = document.createElement("iframe");
125 |     iframe.style.display = "none";
126 |     document.body.appendChild(iframe);
127 | 
128 | 
129 |     frames[frames.length - 1].document.write(
130 |       "