├── libpeerconnection.log
├── src
├── scripts
│ ├── libs
│ │ ├── version.txt
│ │ ├── qa
│ │ │ ├── graphs.txt
│ │ │ ├── geometry.ls
│ │ │ ├── fpolys.ls
│ │ │ └── guessExact.ls
│ │ ├── console-helper.js
│ │ └── html5shiv-printshiv.js
│ ├── pubs
│ │ ├── frogs
│ │ │ ├── services
│ │ │ │ └── semver.ls
│ │ │ ├── views
│ │ │ │ └── nav.html
│ │ │ ├── routes.ls
│ │ │ ├── styles.less
│ │ │ ├── directives
│ │ │ │ ├── frogs.ls
│ │ │ │ └── frog.ls
│ │ │ ├── index.html
│ │ │ ├── main.ls
│ │ │ └── controllers
│ │ │ │ └── frogController.ls
│ │ ├── mathmo
│ │ │ ├── services
│ │ │ │ ├── semver.ls
│ │ │ │ ├── seeder.ls
│ │ │ │ └── questionStore.ls
│ │ │ ├── img
│ │ │ │ ├── icon.png
│ │ │ │ ├── well.png
│ │ │ │ ├── mathmoIcon.png
│ │ │ │ └── nrichIcon.png
│ │ │ ├── views
│ │ │ │ └── nav.html
│ │ │ ├── routes.ls
│ │ │ ├── styles.less
│ │ │ ├── directives
│ │ │ │ └── mathmoPlot.ls
│ │ │ ├── index.html
│ │ │ └── main.ls
│ │ ├── todo
│ │ │ ├── services
│ │ │ │ └── semver.ls
│ │ │ ├── views
│ │ │ │ └── nav.html
│ │ │ ├── routes.ls
│ │ │ ├── controllers
│ │ │ │ └── todoController.ls
│ │ │ ├── index.html
│ │ │ └── main.ls
│ │ ├── boomerangs
│ │ │ ├── services
│ │ │ │ └── semver.ls
│ │ │ ├── views
│ │ │ │ └── nav.html
│ │ │ ├── styles.less
│ │ │ ├── routes.ls
│ │ │ ├── controllers
│ │ │ │ └── boomerangController.ls
│ │ │ ├── index.html
│ │ │ └── main.ls
│ │ ├── probability
│ │ │ ├── services
│ │ │ │ └── semver.ls
│ │ │ ├── styles.less
│ │ │ ├── views
│ │ │ │ └── nav.html
│ │ │ ├── routes.ls
│ │ │ ├── controllers
│ │ │ │ ├── sampleSpinController.ls
│ │ │ │ ├── prob9546ResultsController.ls
│ │ │ │ └── prob9525ResultsController.ls
│ │ │ ├── index.html
│ │ │ └── main.ls
│ │ └── tilted
│ │ │ ├── views
│ │ │ └── nav.html
│ │ │ ├── routes.ls
│ │ │ ├── styles.less
│ │ │ ├── index.html
│ │ │ └── main.ls
│ ├── directives
│ │ ├── appVersion.ls
│ │ ├── svgCheck.ls
│ │ └── d3DotGrid.ls
│ ├── views.ls
│ ├── bootstrap.ls
│ ├── services
│ │ ├── semver.ls
│ │ ├── d3LineChart.ls
│ │ └── d3MultiLineChart.ls
│ ├── routes.ls
│ ├── controllers
│ │ └── appController.ls
│ └── app.ls
├── img
│ ├── redFrog.png
│ ├── blueFrog.png
│ ├── glyphicons-halflings.png
│ └── glyphicons-halflings-white.png
├── styles
│ ├── d3DotGrid.less
│ ├── d3Axis.less
│ ├── styles.less
│ ├── layouts.less
│ ├── component-animations.less
│ ├── utilities.less
│ ├── grid.less
│ ├── breadcrumbs.less
│ ├── responsive-768px-979px.less
│ ├── d3Vis.less
│ ├── hero-unit.less
│ ├── wells.less
│ ├── responsive-1200px-min.less
│ ├── close.less
│ ├── accordion.less
│ ├── pager.less
│ ├── media.less
│ ├── scaffolding.less
│ ├── responsive.less
│ ├── thumbnails.less
│ ├── code.less
│ ├── alerts.less
│ ├── bootstrap.less
│ ├── responsive-utilities.less
│ ├── tooltip.less
│ ├── labels-badges.less
│ ├── modals.less
│ ├── pagination.less
│ ├── carousel.less
│ ├── progress-bars.less
│ ├── popovers.less
│ └── responsive-767px-max.less
├── views
│ ├── directives
│ │ └── svgCheck.html
│ ├── todo.html
│ ├── tilted.html
│ ├── nav.html
│ ├── boomerangs.html
│ ├── spinners.html
│ └── frogs.html
└── index.html
├── test
├── scripts
│ ├── libs
│ │ └── version.txt
│ ├── controllers
│ │ └── todoSpec.ls
│ └── directives
│ │ └── d3VisSpec.ls
├── test.sh
├── pubs
│ ├── mathmo
│ │ ├── TODO.md
│ │ └── mathmo-tests.md
│ └── frogs
│ │ ├── frogDirectiveSpec.ls
│ │ └── frogControllerSpec.ls
├── unit
│ └── problemsSpec.js
├── mathmoTestsTodo
│ ├── lib
│ │ └── jasmine-1.0.2
│ │ │ ├── MIT.LICENSE
│ │ │ └── jasmine.css
│ ├── SpecRunner.html
│ └── spec
│ │ ├── problems.js
│ │ └── fractions.js
└── runner.html
├── boilerplate
├── styles.less
├── services
│ └── semver.ls
├── controllers
│ └── maskController.ls
├── views
│ └── nav.html
├── directives
│ └── maskDirective.ls
├── routes.ls
├── main.ls
└── index.html
├── Procfile
├── assets
├── frogs.ai
├── mathmo
│ └── newraph.psd
└── boomerangs
│ ├── 0-concrete
│ ├── ctrlConcrete.coffee
│ └── expConcrete.html
│ ├── styles.css
│ ├── 1-FirstPass
│ ├── ctrlFirstPass.coffee
│ └── expFirstPass.html
│ ├── 2-Abstract
│ ├── ctrlAbstract.coffee
│ └── expAbstract.html
│ └── question.html
├── .gitignore
├── publish
├── mathmo.sh
├── frogs.sh
└── probability.sh
├── todo
└── gruntCreateApp
├── .editorconfig
├── .jshintrc
├── server.coffee
├── karma-e2e.conf.js
├── routes.coffee
├── LICENSE
├── karma.conf.js
├── package.json
└── README.md
/libpeerconnection.log:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/scripts/libs/version.txt:
--------------------------------------------------------------------------------
1 | 1.0.7
--------------------------------------------------------------------------------
/test/scripts/libs/version.txt:
--------------------------------------------------------------------------------
1 | 1.0.5
2 |
--------------------------------------------------------------------------------
/boilerplate/styles.less:
--------------------------------------------------------------------------------
1 | /* add app specific styles here */
2 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: "./node_modules/.bin/grunt" prod && "./node_modules/.bin/grunt" server
--------------------------------------------------------------------------------
/boilerplate/services/semver.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').factory 'semver', ->"0.1.0"
--------------------------------------------------------------------------------
/assets/frogs.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexwlchan/Apps1/master/assets/frogs.ai
--------------------------------------------------------------------------------
/src/img/redFrog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexwlchan/Apps1/master/src/img/redFrog.png
--------------------------------------------------------------------------------
/src/scripts/pubs/frogs/services/semver.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').factory 'semver', ->"1.0.1"
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/services/semver.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').factory 'semver', ->"0.9.5"
--------------------------------------------------------------------------------
/src/scripts/pubs/todo/services/semver.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').factory 'semver', ->"0.1.6"
--------------------------------------------------------------------------------
/src/img/blueFrog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexwlchan/Apps1/master/src/img/blueFrog.png
--------------------------------------------------------------------------------
/src/scripts/pubs/boomerangs/services/semver.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').factory 'semver', ->"0.9.0"
--------------------------------------------------------------------------------
/src/scripts/pubs/probability/services/semver.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').factory 'semver', ->"0.9.0"
--------------------------------------------------------------------------------
/assets/mathmo/newraph.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexwlchan/Apps1/master/assets/mathmo/newraph.psd
--------------------------------------------------------------------------------
/src/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexwlchan/Apps1/master/src/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/img/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexwlchan/Apps1/master/src/scripts/pubs/mathmo/img/icon.png
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/img/well.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexwlchan/Apps1/master/src/scripts/pubs/mathmo/img/well.png
--------------------------------------------------------------------------------
/src/styles/d3DotGrid.less:
--------------------------------------------------------------------------------
1 | /* d3DotGrid */
2 | @grid-icon-fill: #22cc88;
3 |
4 | .grid-dot {
5 | fill: @grid-icon-fill;
6 | }
--------------------------------------------------------------------------------
/src/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexwlchan/Apps1/master/src/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/img/mathmoIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexwlchan/Apps1/master/src/scripts/pubs/mathmo/img/mathmoIcon.png
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/img/nrichIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexwlchan/Apps1/master/src/scripts/pubs/mathmo/img/nrichIcon.png
--------------------------------------------------------------------------------
/src/scripts/libs/qa/graphs.txt:
--------------------------------------------------------------------------------
1 | makeModulus
2 | var qString="Sketch the graph of \\(|"+a+"-|x||\\) for \\("+l+"\\leq{x}\\leq"+r+"\\).";
3 |
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/services/seeder.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').factory 'seeder', ->
2 |
3 | newSeed = (seed) ->
4 | Math.seedrandom
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | dist/
3 | node_modules/
4 | .temp/
5 | dist_test/
6 | incoming
7 | src/scripts/pubs/styles.less
8 | *~
9 | .appmask
10 | apps
--------------------------------------------------------------------------------
/src/scripts/directives/appVersion.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').directive 'appVersion', [
2 | 'semver'
3 | (semver) -> (scope, elm, attrs) -> elm.text(semver)
4 | ]
--------------------------------------------------------------------------------
/boilerplate/controllers/maskController.ls:
--------------------------------------------------------------------------------
1 | (angular.module 'app')
2 | .controller '<%= mask %>Controller', [
3 | '$scope'
4 | ($scope) ->
5 |
6 |
7 | ]
8 |
9 |
--------------------------------------------------------------------------------
/publish/mathmo.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cd ~/angular/Apps1
3 | grunt mask:mathmo
4 | grunt prod
5 | rsync -av ~/angular/Apps1/dist/ gmp26@maths.org:/www/nrich/html/mathmoApp
--------------------------------------------------------------------------------
/publish/frogs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cd ~/angular/Apps1
3 | grunt mask:frogs
4 | grunt prod
5 | rsync -av ~/angular/Frogs/dist/ gmp26@maths.org:/www/nrich/html/content/00/12/game1/frogs
--------------------------------------------------------------------------------
/publish/probability.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cd ~/angular/Apps1
3 | grunt mask:probability
4 | grunt prod
5 | rsync -av ~/angular/Apps1/dist/ gmp26@maths.org:/www/nrich/html/probabilityApps
--------------------------------------------------------------------------------
/src/scripts/pubs/todo/views/nav.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/scripts/views.ls:
--------------------------------------------------------------------------------
1 | # this file kept intentionally blank
2 | # this file needs to be here for the dev build
3 | # it is effectively ignored
4 | # views will be loaded as html files
5 | 0
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/views/nav.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/scripts/pubs/tilted/views/nav.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/scripts/pubs/boomerangs/views/nav.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/boilerplate/views/nav.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/scripts/bootstrap.ls:
--------------------------------------------------------------------------------
1 | angular.bootstrap document, [
2 | #
3 | # angular module dependencies
4 | #
5 | 'app'
6 | 'ui.bootstrap.collapse'
7 | 'ui.bootstrap.pagination'
8 | 'ui.bootstrap.alert'
9 | ]
--------------------------------------------------------------------------------
/assets/boomerangs/0-concrete/ctrlConcrete.coffee:
--------------------------------------------------------------------------------
1 |
2 | #concrete
3 |
4 | angular.module('app')
5 | .controller 'boomerangController', ($scope) ->
6 |
7 | $scope.small = 2
8 | $scope.large = 2
9 |
10 |
--------------------------------------------------------------------------------
/boilerplate/directives/maskDirective.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').directive '<%= mask %>Directive', [
2 | '$timeout'
3 | ($timeout) ->
4 | restrict: 'EA'
5 | link: (scope, element, attrs) ->
6 | console.log '<%= mask %>Directive'
7 | ]
8 |
--------------------------------------------------------------------------------
/src/scripts/services/semver.ls:
--------------------------------------------------------------------------------
1 | #
2 | # Define the app version number.
3 | # Override in pubs
4 | #
5 | # If a pubs app needs uses prelude you can import it there like so:
6 | #
7 | # import prelude
8 | #
9 | angular.module('app').factory 'semver', ->"0.1.6"
--------------------------------------------------------------------------------
/src/scripts/pubs/probability/styles.less:
--------------------------------------------------------------------------------
1 | #spinset1 input[type=number] {
2 | width:100px;
3 | }
4 | div[d3-vis] {
5 | display: inline-block;
6 | }
7 | .twoway-strong {
8 | vertical-align: top;
9 | text-align:right !important;
10 | font-weight:bold;
11 | }
--------------------------------------------------------------------------------
/test/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BASE_DIR=`dirname $0`
4 |
5 | echo ""
6 | echo "Starting Karma Server (http://karma-runner.github.io)"
7 | echo "-------------------------------------------------------------------"
8 |
9 | karma start $BASE_DIR/../karma.conf.js $*
10 |
--------------------------------------------------------------------------------
/src/scripts/pubs/probability/views/nav.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/boilerplate/routes.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').config [
2 | '$routeProvider'
3 | '$locationProvider'
4 | ($routeProvider, $locationProvider) ->
5 |
6 | $routeProvider
7 | .when '/<%= mask %>', templateUrl: '/views/<%= mask %>.html'
8 | .otherwise redirectTo: '/<%= mask %>'
9 |
10 | ]
--------------------------------------------------------------------------------
/src/scripts/pubs/boomerangs/styles.less:
--------------------------------------------------------------------------------
1 | input[type="number"] {
2 | width:50px;
3 | height: 30px;
4 | font-size: 18px;
5 | }
6 |
7 | em {
8 | font-style: normal;
9 | font-weight:bold;
10 | font-size:1.4em;
11 | }
12 |
13 | .bad {
14 | color:blue;
15 | }
16 |
17 | .good {
18 | color:red;
19 | }
--------------------------------------------------------------------------------
/src/scripts/pubs/frogs/views/nav.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/todo/gruntCreateApp:
--------------------------------------------------------------------------------
1 | Make a grunt task to create a new App.
2 |
3 | grunt init mathmo
4 |
5 | it should:
6 | create pubs/mathmo
7 | create pubs/mathmo/controllers, services, directives,views
8 | from generic code, copy
9 | main.ls, routes.ls
10 | create a main view in generics
11 | mathmo.html
--------------------------------------------------------------------------------
/src/views/directives/svgCheck.html:
--------------------------------------------------------------------------------
1 |
2 |
×
3 | This collection will only run in a browser which supports
SVG .
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/assets/boomerangs/styles.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/styles/d3Axis.less:
--------------------------------------------------------------------------------
1 | /* d3Axis styles */
2 |
3 | @axis-stroke: #666;
4 | @axis-fill: none;
5 | @axis-width: 1.5px;
6 |
7 | .axis line,
8 | .axis tick,
9 | .axis path {
10 | fill: @axis-fill;
11 | stroke: @axis-stroke;
12 | shape-rendering: crispEdges;
13 | }
14 |
15 | .arrow {
16 | stroke: @axis-stroke;
17 | stroke-width: @axis-width;
18 | }
19 |
--------------------------------------------------------------------------------
/src/scripts/pubs/todo/routes.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').config [
2 | '$routeProvider'
3 | '$locationProvider'
4 | ($routeProvider, $locationProvider) ->
5 |
6 | # use /tiltedApp rather than #/tiltedApp
7 | #$locationProvider.html5Mode true
8 |
9 | $routeProvider
10 | .when '/todo', templateUrl: '/views/todo.html'
11 | .otherwise redirectTo: '/todo'
12 |
13 | ]
--------------------------------------------------------------------------------
/src/styles/styles.less:
--------------------------------------------------------------------------------
1 | @import "bootstrap.less";
2 | @import "responsive.less";
3 |
4 | @import "d3Vis.less";
5 | @import "d3DotGrid.less";
6 | @import "d3Axis.less";
7 |
8 | @import "../scripts/pubs/styles.less";
9 |
10 |
11 | .ng-cloak {
12 | display: none;
13 | }
14 |
15 | .view {
16 | display: inline-block;
17 | }
18 |
19 | .header {
20 | padding-left:20px
21 | }
22 |
--------------------------------------------------------------------------------
/src/scripts/pubs/tilted/routes.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').config [
2 | '$routeProvider'
3 | '$locationProvider'
4 | ($routeProvider, $locationProvider) ->
5 |
6 | # use /tiltedApp rather than #/tiltedApp
7 | #$locationProvider.html5Mode true
8 |
9 | $routeProvider
10 | .when '/tilted', templateUrl: '/views/tilted.html'
11 | .otherwise redirectTo: '/tilted'
12 |
13 | ]
--------------------------------------------------------------------------------
/src/scripts/pubs/boomerangs/routes.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').config [
2 | '$routeProvider'
3 | '$locationProvider'
4 | ($routeProvider, $locationProvider) ->
5 |
6 | # use /tiltedApp rather than #/tiltedApp
7 | #$locationProvider.html5Mode true
8 |
9 | $routeProvider
10 | .when '/boomerangs', templateUrl: '/views/boomerangs.html'
11 | .otherwise redirectTo: '/boomerangs'
12 |
13 | ]
--------------------------------------------------------------------------------
/src/styles/layouts.less:
--------------------------------------------------------------------------------
1 | //
2 | // Layouts
3 | // --------------------------------------------------
4 |
5 |
6 | // Container (centered, fixed-width layouts)
7 | .container {
8 | .container-fixed();
9 | }
10 |
11 | // Fluid layouts (left aligned, with sidebar, min- & max-width content)
12 | .container-fluid {
13 | padding-right: @gridGutterWidth;
14 | padding-left: @gridGutterWidth;
15 | .clearfix();
16 | }
--------------------------------------------------------------------------------
/src/scripts/pubs/frogs/routes.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').config [
2 | '$routeProvider'
3 | '$locationProvider'
4 | ($routeProvider, $locationProvider) ->
5 |
6 | # use /tiltedApp rather than #/tiltedApp
7 | #$locationProvider.html5Mode true
8 |
9 | $routeProvider
10 | .when '/frogs/:users/:id/:reds/:blues' templateUrl: '/views/frogs.html'
11 | .otherwise redirectTo: '/frogs/single/1246/2/2'
12 |
13 | ]
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/routes.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').config [
2 | '$routeProvider'
3 | '$locationProvider'
4 | ($routeProvider, $locationProvider) ->
5 |
6 | $routeProvider
7 | .when '/mathmo/:help' templateUrl: '/views/mathmo.html'
8 | .when '/mathmo/:cmd/:x/:t/:q' templateUrl: '/views/mathmo.html'
9 | .when '/mathmo', templateUrl: '/views/mathmo.html'
10 | .otherwise redirectTo: '/mathmo'
11 |
12 | ]
--------------------------------------------------------------------------------
/src/scripts/pubs/tilted/styles.less:
--------------------------------------------------------------------------------
1 | /* TiltedSquares App */
2 |
3 | .tilted {
4 | fill: #a04;
5 | opacity: 0.5;
6 | }
7 |
8 | .tilted-control0 {
9 | fill: #048;
10 | opacity: 0.5;
11 | &:hover, &:active {
12 | opacity:0.7;
13 | cursor:pointer;
14 | }
15 | }
16 |
17 | .tilted-control1 {
18 | fill: #f00;
19 | opacity: 0.5;
20 | &:hover, &:active {
21 | opacity:0.7;
22 | cursor:pointer;
23 | }
24 | }
--------------------------------------------------------------------------------
/src/styles/component-animations.less:
--------------------------------------------------------------------------------
1 | //
2 | // Component animations
3 | // --------------------------------------------------
4 |
5 |
6 | .fade {
7 | opacity: 0;
8 | .transition(opacity .15s linear);
9 | &.in {
10 | opacity: 1;
11 | }
12 | }
13 |
14 | .collapse {
15 | position: relative;
16 | height: 0;
17 | overflow: hidden;
18 | .transition(height .35s ease);
19 | &.in {
20 | height: auto;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/scripts/libs/console-helper.js:
--------------------------------------------------------------------------------
1 | // Make it safe to do console.log() always.
2 | (function (con) {
3 | var method;
4 | var dummy = function() {};
5 | var methods = ('assert,count,debug,dir,dirxml,error,exception,group,' +
6 | 'groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,' +
7 | 'time,timeEnd,trace,warn').split(',');
8 | while (method = methods.pop()) {
9 | con[method] = con[method] || dummy;
10 | }
11 | })(window.console = window.console || {});
12 |
--------------------------------------------------------------------------------
/src/styles/utilities.less:
--------------------------------------------------------------------------------
1 | //
2 | // Utility classes
3 | // --------------------------------------------------
4 |
5 |
6 | // Quick floats
7 | .pull-right {
8 | float: right;
9 | }
10 | .pull-left {
11 | float: left;
12 | }
13 |
14 | // Toggling content
15 | .hide {
16 | display: none;
17 | }
18 | .show {
19 | display: block;
20 | }
21 |
22 | // Visibility
23 | .invisible {
24 | visibility: hidden;
25 | }
26 |
27 | // For Affix plugin
28 | .affix {
29 | position: fixed;
30 | }
31 |
--------------------------------------------------------------------------------
/test/pubs/mathmo/TODO.md:
--------------------------------------------------------------------------------
1 | Mathmo Tests
2 | ============
3 |
4 | Exercise names less than 4 chars long should cause +Add button to be disabled and show red input border
5 | Add exercise should place the exercise name in a tab
6 |
7 | A successful add Question Topic should remove the topic from the accordion
8 | Question topics restored from local store should not appear in the add Question accordion
9 | Add Question should pop up the Add Question dialog
10 | Close button should close AddQuestion Dialog
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # http://editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | # Change these settings to your own preference
9 | indent_style = space
10 | indent_size = 2
11 |
12 | # We recommend you to keep these unchanged
13 | end_of_line = lf
14 | charset = utf-8
15 | trim_trailing_whitespace = true
16 | insert_final_newline = true
17 |
18 | [*.md]
19 | trim_trailing_whitespace = false
20 |
--------------------------------------------------------------------------------
/src/scripts/pubs/probability/routes.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').config [
2 | '$routeProvider'
3 | '$locationProvider'
4 | ($routeProvider, $locationProvider) ->
5 |
6 | # use /tiltedApp rather than #/tiltedApp
7 | #$locationProvider.html5Mode true
8 |
9 | $routeProvider
10 | #.when '/spinners', templateUrl: '/views/spinners.html'
11 | .when '/prob9546', templateUrl: '/views/prob9546.html'
12 | .when '/prob9525', templateUrl: '/views/prob9525.html'
13 | .otherwise redirectTo: '/prob9546'
14 |
15 | ]
--------------------------------------------------------------------------------
/src/styles/grid.less:
--------------------------------------------------------------------------------
1 | //
2 | // Grid system
3 | // --------------------------------------------------
4 |
5 |
6 | // Fixed (940px)
7 | #grid > .core(@gridColumnWidth, @gridGutterWidth);
8 |
9 | // Fluid (940px)
10 | #grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth);
11 |
12 | // Reset utility classes due to specificity
13 | [class*="span"].hide,
14 | .row-fluid [class*="span"].hide {
15 | display: none;
16 | }
17 |
18 | [class*="span"].pull-right,
19 | .row-fluid [class*="span"].pull-right {
20 | float: right;
21 | }
22 |
--------------------------------------------------------------------------------
/assets/boomerangs/1-FirstPass/ctrlFirstPass.coffee:
--------------------------------------------------------------------------------
1 | # First Pass
2 |
3 | angular.module('app')
4 | .controller 'boomerangController', ($scope) ->
5 |
6 | $scope.small = 2
7 | $scope.large = 2
8 |
9 | $scope.decTime = () -> @small + @large
10 |
11 | $scope.carveTime = () -> 2*@small + 3*@large
12 |
13 |
14 | $scope.decTimeOK = () -> if 0 <= @decTime() <= 10 then "good" else "bad"
15 |
16 | $scope.carveTimeOK = () -> if 0 <= @carveTime() <= 24 then "good" else "bad"
17 |
18 |
19 | $scope.totalIncome = () -> @small * 8 + @large * 10
20 |
21 |
--------------------------------------------------------------------------------
/src/scripts/directives/svgCheck.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').directive 'svgCheck',
2 | [
3 | '$timeout', '$window'
4 | ($timeout, $window) ->
5 |
6 | restrict: 'A'
7 | templateUrl: '/views/directives/svgCheck.html'
8 | replace: false
9 | transclude: true
10 |
11 | link: (scope, element, attrs) ->
12 |
13 | # check that browser is SVG capable.
14 | scope.svgOK = $window.document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature" + \#BasicStructure, "1.1")
15 | console.log("svgCheck scope = ", scope.$id)
16 | ]
--------------------------------------------------------------------------------
/src/styles/breadcrumbs.less:
--------------------------------------------------------------------------------
1 | //
2 | // Breadcrumbs
3 | // --------------------------------------------------
4 |
5 |
6 | .breadcrumb {
7 | padding: 8px 15px;
8 | margin: 0 0 @baseLineHeight;
9 | list-style: none;
10 | background-color: #f5f5f5;
11 | .border-radius(@baseBorderRadius);
12 | > li {
13 | display: inline-block;
14 | .ie7-inline-block();
15 | text-shadow: 0 1px 0 @white;
16 | > .divider {
17 | padding: 0 5px;
18 | color: #ccc;
19 | }
20 | }
21 | > .active {
22 | color: @grayLight;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/styles/responsive-768px-979px.less:
--------------------------------------------------------------------------------
1 | //
2 | // Responsive: Tablet to desktop
3 | // --------------------------------------------------
4 |
5 |
6 | @media (min-width: 768px) and (max-width: 979px) {
7 |
8 | // Fixed grid
9 | #grid > .core(@gridColumnWidth768, @gridGutterWidth768);
10 |
11 | // Fluid grid
12 | #grid > .fluid(@fluidGridColumnWidth768, @fluidGridGutterWidth768);
13 |
14 | // Input grid
15 | #grid > .input(@gridColumnWidth768, @gridGutterWidth768);
16 |
17 | // No need to reset .thumbnails here since it's the same @gridGutterWidth
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/scripts/pubs/boomerangs/controllers/boomerangController.ls:
--------------------------------------------------------------------------------
1 | # First Pass
2 |
3 | angular.module('app')
4 | .controller 'boomerangController',
5 | [
6 | '$scope'
7 | ($scope) ->
8 |
9 | $scope.small = 1
10 | $scope.large = 1
11 |
12 | $scope.decTime = -> @small + @large
13 |
14 | $scope.carveTime = -> 2 * @small + 3 * @large
15 |
16 | $scope.decTimeOK = -> if 0 <= @decTime() <= 10 then "good" else "bad"
17 |
18 | $scope.carveTimeOK = -> if 0 <= @carveTime() <= 24 then "good" else "bad"
19 |
20 |
21 | $scope.totalIncome = -> @small * 8 + @large * 10
22 | ]
23 |
--------------------------------------------------------------------------------
/src/styles/d3Vis.less:
--------------------------------------------------------------------------------
1 | /* d3Vis styles */
2 | @base-font-size: 14px;
3 | @base-font-family: sans-serif;
4 | @outer-fill: none;
5 | @outer-stroke: #DDD;
6 | @inner-fill: none;
7 | @inner-stroke: #888;
8 |
9 | svg {
10 | font: @base-font-size;
11 | font-family: @base-font-family;
12 | }
13 |
14 | .hide {
15 | display:none;
16 | }
17 |
18 | .outer,
19 | .inner {
20 | shape-rendering: crispEdges;
21 | }
22 |
23 | .outer {
24 | fill: none;
25 | stroke: @outer-stroke;
26 | }
27 |
28 | .inner {
29 | fill: @inner-fill;
30 | stroke: @inner-stroke;
31 | stroke-dasharray: 3, 4;
32 | }
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/styles/hero-unit.less:
--------------------------------------------------------------------------------
1 | //
2 | // Hero unit
3 | // --------------------------------------------------
4 |
5 |
6 | .hero-unit {
7 | padding: 60px;
8 | margin-bottom: 30px;
9 | font-size: 18px;
10 | font-weight: 200;
11 | line-height: @baseLineHeight * 1.5;
12 | color: @heroUnitLeadColor;
13 | background-color: @heroUnitBackground;
14 | .border-radius(6px);
15 | h1 {
16 | margin-bottom: 0;
17 | font-size: 60px;
18 | line-height: 1;
19 | color: @heroUnitHeadingColor;
20 | letter-spacing: -1px;
21 | }
22 | li {
23 | line-height: @baseLineHeight * 1.5; // Reset since we specify in type.less
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/styles/wells.less:
--------------------------------------------------------------------------------
1 | //
2 | // Wells
3 | // --------------------------------------------------
4 |
5 |
6 | // Base class
7 | .well {
8 | min-height: 20px;
9 | padding: 19px;
10 | margin-bottom: 20px;
11 | background-color: @wellBackground;
12 | border: 1px solid darken(@wellBackground, 7%);
13 | .border-radius(@baseBorderRadius);
14 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));
15 | blockquote {
16 | border-color: #ddd;
17 | border-color: rgba(0,0,0,.15);
18 | }
19 | }
20 |
21 | // Sizes
22 | .well-large {
23 | padding: 24px;
24 | .border-radius(@borderRadiusLarge);
25 | }
26 | .well-small {
27 | padding: 9px;
28 | .border-radius(@borderRadiusSmall);
29 | }
30 |
--------------------------------------------------------------------------------
/src/views/todo.html:
--------------------------------------------------------------------------------
1 | Todo
2 | {{remaining()}} of {{todos.length}} remaining [
archive ]
3 |
10 |
16 |
--------------------------------------------------------------------------------
/src/styles/responsive-1200px-min.less:
--------------------------------------------------------------------------------
1 | //
2 | // Responsive: Large desktop and up
3 | // --------------------------------------------------
4 |
5 |
6 | @media (min-width: 1200px) {
7 |
8 | // Fixed grid
9 | #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200);
10 |
11 | // Fluid grid
12 | #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200);
13 |
14 | // Input grid
15 | #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200);
16 |
17 | // Thumbnails
18 | .thumbnails {
19 | margin-left: -@gridGutterWidth1200;
20 | }
21 | .thumbnails > li {
22 | margin-left: @gridGutterWidth1200;
23 | }
24 | .row-fluid .thumbnails {
25 | margin-left: 0;
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/assets/boomerangs/0-concrete/expConcrete.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Let's experiment
5 |
6 |
7 |
8 |
Suppose Phil makes 1 small and 1 large boomerangs.
9 |
10 |
Carving 1 small boomerangs will take 2 hours.
11 |
Carving 1 large boomerangs will take 3 hours.
12 |
13 |
Phil will need 5 hours to carve them all.
14 |
Cath has to decorate 2 boomerangs.
15 |
16 |
1 small boomerangs will make $8 .
17 |
1 large boomerangs will make $10 .
18 |
19 |
Total amount made will be $18
20 |
21 |
--------------------------------------------------------------------------------
/assets/boomerangs/2-Abstract/ctrlAbstract.coffee:
--------------------------------------------------------------------------------
1 | #abstract
2 |
3 | angular.module('app')
4 | .controller 'boomerangController', ($scope) ->
5 |
6 | $scope.x = 2
7 | $scope.y = 2
8 |
9 | $scope.smallTime = (x) -> 2*x
10 |
11 | $scope.largeTime = (x) -> 3*x
12 |
13 | $scope.smallIncome = (x) -> 8*x
14 |
15 | $scope.largeIncome = (x) -> 10*x
16 |
17 | $scope.decTime = (x,y) -> x + y
18 |
19 | $scope.carveTime = (x,y) -> @smallTime(x) + @largeTime(y)
20 |
21 | $scope.totalIncome = (x,y) -> @smallIncome(x) + @largeIncome(y)
22 |
23 |
24 | $scope.decTimeOK = (x,y) -> if 0 <= @decTime(x,y) <= 10 then "good" else "bad"
25 |
26 | $scope.carveTimeOK = (x,y) -> if 0 <= @carveTime(x,y) <= 24 then "good" else "bad"
27 |
--------------------------------------------------------------------------------
/src/styles/close.less:
--------------------------------------------------------------------------------
1 | //
2 | // Close icons
3 | // --------------------------------------------------
4 |
5 |
6 | .close {
7 | float: right;
8 | font-size: 20px;
9 | font-weight: bold;
10 | line-height: @baseLineHeight;
11 | color: @black;
12 | text-shadow: 0 1px 0 rgba(255,255,255,1);
13 | .opacity(20);
14 | &:hover,
15 | &:focus {
16 | color: @black;
17 | text-decoration: none;
18 | cursor: pointer;
19 | .opacity(40);
20 | }
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.close {
27 | padding: 0;
28 | cursor: pointer;
29 | background: transparent;
30 | border: 0;
31 | -webkit-appearance: none;
32 | }
--------------------------------------------------------------------------------
/src/views/tilted.html:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
16 |
17 |
18 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "angular": true
4 | },
5 |
6 | "node": true,
7 | "es5": false,
8 | "browser": true,
9 | "boss": false,
10 | "curly": false,
11 | "debug": false,
12 | "devel": false,
13 | "eqeqeq": true,
14 | "eqnull": true,
15 | "evil": true,
16 | "expr": true,
17 | "forin": false,
18 | "immed": true,
19 | "laxbreak": true,
20 | "loopfunc": true,
21 | "multistr": true,
22 | "newcap": true,
23 | "noarg": true,
24 | "noempty": false,
25 | "nonew": false,
26 | "nomen": false,
27 | "onevar": false,
28 | "plusplus": false,
29 | "regexp": false,
30 | "undef": true,
31 | "sub": true,
32 | "strict": false,
33 | "white": false,
34 | "unused": true
35 | }
--------------------------------------------------------------------------------
/src/styles/accordion.less:
--------------------------------------------------------------------------------
1 | //
2 | // Accordion
3 | // --------------------------------------------------
4 |
5 |
6 | // Parent container
7 | .accordion {
8 | margin-bottom: @baseLineHeight;
9 | }
10 |
11 | // Group == heading + body
12 | .accordion-group {
13 | margin-bottom: 2px;
14 | border: 1px solid #e5e5e5;
15 | .border-radius(@baseBorderRadius);
16 | }
17 | .accordion-heading {
18 | border-bottom: 0;
19 | }
20 | .accordion-heading .accordion-toggle {
21 | display: block;
22 | padding: 8px 15px;
23 | }
24 |
25 | // General toggle styles
26 | .accordion-toggle {
27 | cursor: pointer;
28 | }
29 |
30 | // Inner needs the styles because you can't animate properly with any styles on the element
31 | .accordion-inner {
32 | padding: 9px 15px;
33 | border-top: 1px solid #e5e5e5;
34 | }
35 |
--------------------------------------------------------------------------------
/src/scripts/pubs/frogs/styles.less:
--------------------------------------------------------------------------------
1 | .frog-space {
2 | position:relative;
3 | height: 100px;
4 | padding:10px 0px 10px 10px;
5 | .border-radius(20px);
6 |
7 | .blue {
8 | background-image: url("../img/blueFrog.png");
9 | }
10 |
11 | .red {
12 | background-image: url("../img/redFrog.png");
13 | }
14 |
15 | .frog {
16 | width: 100px;
17 | height: 100px;
18 | display: inline-block;
19 | opacity: 0.7;
20 |
21 | position: absolute;
22 | .transition(.2s ease-in-out left);
23 |
24 | &:hover {
25 | cursor: pointer;
26 | opacity:1;
27 | }
28 | }
29 | }
30 |
31 | .froginput {
32 | width: 40px;
33 | height: 30px;
34 | font-size: 18px;
35 | }
36 |
37 | /*
38 | [ng-controller = frogController] .pagination {
39 | margin-bottom:0px
40 | }
41 | */
42 |
--------------------------------------------------------------------------------
/src/scripts/pubs/todo/controllers/todoController.ls:
--------------------------------------------------------------------------------
1 | (angular.module 'app')
2 | .controller('todoController', [
3 | '$scope'
4 |
5 | ($scope) ->
6 |
7 | $scope.todos = [
8 | text: "learn angular"
9 | done: true
10 | ,
11 | text: "build an angular app"
12 | done: false
13 | ]
14 |
15 | $scope.addTodo = ->
16 | $scope.todos.push {
17 | text: $scope.todoText
18 | done: false
19 | }
20 |
21 | $scope.todoText = ""
22 |
23 | $scope.remaining = ->
24 | count = 0
25 | angular.forEach $scope.todos, (todo) ->
26 | count += (if todo.done then 0 else 1)
27 |
28 | count
29 |
30 | $scope.archive = ->
31 | oldTodos = $scope.todos
32 | $scope.todos = []
33 | angular.forEach oldTodos, (todo) ->
34 | $scope.todos.push todo unless todo.done
35 |
36 | ])
37 |
38 |
--------------------------------------------------------------------------------
/assets/boomerangs/question.html:
--------------------------------------------------------------------------------
1 |
2 | Boomerangs
3 |
4 | Phil and Cath make and sell boomerangs for a school event.
5 | The money they raise will go to charity.
6 |
7 | They plan to make them in two sizes: small and large.
8 |
9 | Phil will carve them from wood.
10 | The small boomerang takes 2 hours to carve and the large one takes 3
11 | hours to carve.
12 | Phil has a total of 24 hours available for carving.
13 |
14 | Cath will decorate them.
15 | She only has time to decorate 10 boomerangs of either size.
16 |
17 | The small boomerang will make $8 for charity.
18 | The large boomerang will make $10 for charity.
19 | They want to make as much money for charity as they can.
20 |
21 | How many small and large boomerangs should they make?
22 |
23 | How much money will they then make?
24 |
--------------------------------------------------------------------------------
/server.coffee:
--------------------------------------------------------------------------------
1 | express = require 'express'
2 | routes = require './routes'
3 | dir = "#{__dirname}/dist"
4 | port = process.env.PORT ? process.argv.splice(2)[0] ? 3005
5 |
6 | # Create express facility.
7 | app = express()
8 | # Create a HTTP server object.
9 | server = require('http').createServer(app)
10 | # Create SocketIO binding.
11 | io = require('socket.io').listen(server)
12 |
13 | app.configure ->
14 | # use livereload middleware
15 | app.use require('grunt-contrib-livereload/lib/utils').livereloadSnippet
16 | app.use express.logger 'dev'
17 | app.use express.bodyParser()
18 | app.use express.methodOverride()
19 | app.use express.errorHandler()
20 | app.use express.static dir
21 | app.use app.router
22 | routes app, dir
23 |
24 | module.exports = server
25 |
26 | # Override: Provide an "use" used by grunt-express.
27 | module.exports.use = -> app.use.apply app, arguments
--------------------------------------------------------------------------------
/test/unit/problemsSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for controllers go here */
4 | describe("mathmoController", function() {
5 |
6 | beforeEach(module('app'));
7 |
8 | var scope;
9 | beforeEach(inject(function($rootScope, $controller) {
10 | scope = $rootScope.$new();
11 | $controller("mathmoController", {
12 | $scope: scope
13 | });
14 |
15 | var pane = scope.addQSet('unit-test')
16 | }));
17 |
18 | describe("makePartial", function() {
19 |
20 | var mypane = {"name": "unit-test"};
21 |
22 | it("should produce lists", function() {
23 | expect(scope.testQ("C11", mypane)[0]).toBe("By completing the square, find (for real \\(x\\)) the minimum value of$$x^2 + 4x + 9.$$");
24 | expect(scope.testQ("C11", mypane)[1]).toBe("The minimum value is \\(5\\) which occurs at \\(x=-2\\)");
25 | });
26 |
27 | });
28 |
29 | });
30 |
31 |
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/styles.less:
--------------------------------------------------------------------------------
1 | /* add mathmo specific styles here */
2 |
3 | input[type="text"]:focus.ng-invalid {
4 | border-color: #e9322d;
5 | -webkit-box-shadow: 0 0 6px #f8b9b7;
6 | -moz-box-shadow: 0 0 6px #f8b9b7;
7 | box-shadow: 0 0 6px #f8b9b7;
8 | }
9 |
10 | .line {
11 | stroke-width: 1;
12 | stroke-opacity: 1.0;
13 | fill: none;
14 | }
15 |
16 | .tick text {
17 | font-size:10px;
18 | }
19 |
20 | .mathmo .nav {
21 | margin-bottom:10px;
22 | }
23 |
24 | .modal-body.share textarea {
25 | width: 80%;
26 | }
27 |
28 | .exercise {
29 | list-style-type: lower-roman;
30 | }
31 |
32 | .subexercise {
33 | list-style-type: lower-alpha;
34 | }
35 |
36 | /*
37 | .container {
38 | margin-left:15px;
39 | min-width:50%;
40 | -moz-transition:width 100ms;
41 | -webkit-transition:width 100ms;
42 | -o-transition:width 100ms;
43 | -transition:width 100ms;
44 | }
45 | */
46 |
--------------------------------------------------------------------------------
/src/views/nav.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/scripts/routes.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').config [
2 | '$routeProvider'
3 | '$locationProvider'
4 | ($routeProvider, $locationProvider) ->
5 |
6 | # use /tiltedApp rather than #/tiltedApp
7 | #$locationProvider.html5Mode true
8 |
9 | $routeProvider
10 | .when '/tilted', templateUrl: '/views/tilted.html'
11 | .when '/todo', templateUrl: '/views/todo.html'
12 | .when '/spinners', templateUrl: '/views/spinners.html'
13 | .when '/prob9546', templateUrl: '/views/prob9546.html'
14 | .when '/prob9525', templateUrl: '/views/prob9525.html'
15 | .when '/boomerangs', templateUrl: '/views/boomerangs.html'
16 | .when '/frogs/:users/:id/:reds/:blues' templateUrl: '/views/frogs.html'
17 | .when '/mathmo', templateUrl: '/views/mathmo.html'
18 | .when '/mathmo/:cmd/:x/:t/:q' templateUrl: '/views/mathmo.html'
19 | .when '/mathmo', templateUrl: '/views/mathmo.html'
20 | .otherwise redirectTo: '/tilted'
21 |
22 | ]
--------------------------------------------------------------------------------
/assets/boomerangs/1-FirstPass/expFirstPass.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Let's experiment
4 |
5 |
6 |
Suppose Phil makes small and large boomerangs.
7 |
Carving {{small}} small boomerangs will take {{2*small}} hours.
8 |
Carving {{large}} large boomerangs will take {{3*large}} hours.
9 |
10 |
Phil will need {{ 2*small + 3*large }} hours to carve them all.
11 |
Cath has to decorate {{ small + large }} boomerangs.
12 |
13 |
{{small}} small boomerangs will make ${{8*small}} .
14 |
{{large}} large boomerangs will make ${{10*large}} .
15 |
16 |
Total amount made will be ${{ 8*small + 10*large }}
17 |
18 |
--------------------------------------------------------------------------------
/src/styles/pager.less:
--------------------------------------------------------------------------------
1 | //
2 | // Pager pagination
3 | // --------------------------------------------------
4 |
5 |
6 | .pager {
7 | margin: @baseLineHeight 0;
8 | list-style: none;
9 | text-align: center;
10 | .clearfix();
11 | }
12 | .pager li {
13 | display: inline;
14 | }
15 | .pager li > a,
16 | .pager li > span {
17 | display: inline-block;
18 | padding: 5px 14px;
19 | background-color: #fff;
20 | border: 1px solid #ddd;
21 | .border-radius(15px);
22 | }
23 | .pager li > a:hover,
24 | .pager li > a:focus {
25 | text-decoration: none;
26 | background-color: #f5f5f5;
27 | }
28 | .pager .next > a,
29 | .pager .next > span {
30 | float: right;
31 | }
32 | .pager .previous > a,
33 | .pager .previous > span {
34 | float: left;
35 | }
36 | .pager .disabled > a,
37 | .pager .disabled > a:hover,
38 | .pager .disabled > a:focus,
39 | .pager .disabled > span {
40 | color: @grayLight;
41 | background-color: #fff;
42 | cursor: default;
43 | }
--------------------------------------------------------------------------------
/assets/boomerangs/2-Abstract/expAbstract.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Let's experiment
4 |
5 |
6 |
Suppose Phil makes small and large boomerangs.
7 |
Carving {{x}} small boomerangs will take {{smallTime(x)}} hours.
8 |
Carving {{y}} large boomerangs will take {{largeTime(x)}} hours.
9 |
10 |
Phil will need {{ carveTime(x,y) }} hours to carve them all.
11 |
Cath has to decorate {{ decTime(x,y) }} boomerangs.
12 |
13 |
{{x}} small boomerangs will make ${{smallIncome(x)}} .
14 |
{{y}} large boomerangs will make ${{largeIncome(y)}} .
15 |
16 |
Total amount made will be ${{ totalIncome(x,y) }}
17 |
18 |
19 |
--------------------------------------------------------------------------------
/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | basePath = './';
2 |
3 | files = [
4 | ANGULAR_SCENARIO,
5 | ANGULAR_SCENARIO_ADAPTER,
6 | './dist/scripts/libs/jquery.js',
7 | './dist/scripts/libs/d3.v3.js',
8 | './dist/scripts/libs/angular.js',
9 | './dist/scripts/libs/angular-resource.js',
10 | './test/scripts/libs/angular-mocks.js',
11 | './dist/scripts/app.js',
12 | './dist/scripts/templates.js',
13 | './dist/scripts/controllers/*.js',
14 | './dist/scripts/directives/*.js',
15 | // './dist/scripts/filters/*.js',
16 | './dist/scripts/services/*.js',
17 |
18 | './test/scripts/e2e/**/*.js'
19 | /*,
20 | './test/scripts/directives/*.js',
21 | './test/scripts/filters/*.js',
22 | './test/scripts/services/*.js',
23 | */
24 | ];
25 |
26 | autoWatch = false;
27 |
28 | browsers = ['Chrome'];
29 |
30 | singleRun = true;
31 |
32 | proxies = {
33 | '/': 'http://localhost:3005/'
34 | };
35 |
36 | junitReporter = {
37 | outputFile: 'test_out/e2e.xml',
38 | suite: 'e2e'
39 | };
--------------------------------------------------------------------------------
/src/scripts/pubs/frogs/directives/frogs.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').directive 'frogs', [
2 | '$window'
3 | ($window) ->
4 | template: '
'
5 | replace: false
6 | link: (scope,element,attrs) ->
7 |
8 | #console.log "woff=", ~~attrs.woff
9 |
10 | rescale = (winSize, padCount) ->
11 | zoom = (winSize)/(130*padCount)
12 | #console.log "zoom=", zoom
13 | element.css("zoom", zoom)
14 |
15 | resizeHandler = (event) ->
16 | #console.log "innerWidth =", $window.innerWidth
17 | rescale($window.innerWidth, scope.frogs.length)
18 |
19 | scope.$watch 'frogs', (val) ->
20 | #console.log("pads =", val.length)
21 | rescale($window.innerWidth, val.length)
22 |
23 | #console.log("scope=",scope.$id)
24 | #console.log("window=", $window)
25 |
26 | win = angular.element($window)
27 | win.bind "resize", resizeHandler
28 |
29 | ]
30 |
31 |
--------------------------------------------------------------------------------
/src/styles/media.less:
--------------------------------------------------------------------------------
1 | // Media objects
2 | // Source: http://stubbornella.org/content/?p=497
3 | // --------------------------------------------------
4 |
5 |
6 | // Common styles
7 | // -------------------------
8 |
9 | // Clear the floats
10 | .media,
11 | .media-body {
12 | overflow: hidden;
13 | *overflow: visible;
14 | zoom: 1;
15 | }
16 |
17 | // Proper spacing between instances of .media
18 | .media,
19 | .media .media {
20 | margin-top: 15px;
21 | }
22 | .media:first-child {
23 | margin-top: 0;
24 | }
25 |
26 | // For images and videos, set to block
27 | .media-object {
28 | display: block;
29 | }
30 |
31 | // Reset margins on headings for tighter default spacing
32 | .media-heading {
33 | margin: 0 0 5px;
34 | }
35 |
36 |
37 | // Media image alignment
38 | // -------------------------
39 |
40 | .media > .pull-left {
41 | margin-right: 10px;
42 | }
43 | .media > .pull-right {
44 | margin-left: 10px;
45 | }
46 |
47 |
48 | // Media list variation
49 | // -------------------------
50 |
51 | // Undo default ul/ol styles
52 | .media-list {
53 | margin-left: 0;
54 | list-style: none;
55 | }
56 |
--------------------------------------------------------------------------------
/src/styles/scaffolding.less:
--------------------------------------------------------------------------------
1 | //
2 | // Scaffolding
3 | // --------------------------------------------------
4 |
5 |
6 | // Body reset
7 | // -------------------------
8 |
9 | body {
10 | margin: 0;
11 | font-family: @baseFontFamily;
12 | font-size: @baseFontSize;
13 | line-height: @baseLineHeight;
14 | color: @textColor;
15 | background-color: @bodyBackground;
16 | }
17 |
18 |
19 | // Links
20 | // -------------------------
21 |
22 | a {
23 | color: @linkColor;
24 | text-decoration: none;
25 | }
26 | a:hover,
27 | a:focus {
28 | color: @linkColorHover;
29 | text-decoration: underline;
30 | }
31 |
32 |
33 | // Images
34 | // -------------------------
35 |
36 | // Rounded corners
37 | .img-rounded {
38 | .border-radius(6px);
39 | }
40 |
41 | // Add polaroid-esque trim
42 | .img-polaroid {
43 | padding: 4px;
44 | background-color: #fff;
45 | border: 1px solid #ccc;
46 | border: 1px solid rgba(0,0,0,.2);
47 | .box-shadow(0 1px 3px rgba(0,0,0,.1));
48 | }
49 |
50 | // Perfect circle
51 | .img-circle {
52 | .border-radius(500px); // crank the border-radius so it works with most reasonably sized images
53 | }
54 |
--------------------------------------------------------------------------------
/routes.coffee:
--------------------------------------------------------------------------------
1 | module.exports = (app, dir) ->
2 | app.get '/', (req, res) ->
3 | res.render "#{dir}/index.html"
4 |
5 | ###
6 | #
7 | # Original AngularFun app server - useful reference
8 | #
9 |
10 | nextId = 0
11 |
12 | people = [
13 | {"id": "#{nextId++}", "name": "Saasha", "age": "5"}
14 | {"id": "#{nextId++}", "name": "Planet", "age": "7"}
15 | ]
16 |
17 | isUniqueName = (name) ->
18 | (name for person in people when person.name is name).length is 0
19 |
20 | app.get '/people', (req, res) ->
21 | res.json people
22 |
23 | app.post '/people', (req, res) ->
24 | name = req.body.name
25 | age = req.body.age
26 |
27 | message =
28 | "title": "Duplicate!"
29 | "message": "#{name} is a duplicate. Please enter a new name."
30 |
31 | return res.send(message, 403) if not isUniqueName name
32 |
33 | person =
34 | "id": "#{nextId++}"
35 | "name": "#{name}"
36 | "age": "#{age}"
37 |
38 | people.push person
39 | res.json person
40 |
41 | app.get '/people/:id', (req, res) ->
42 | id = req.params.id
43 | current = person for person in people when parseInt(person.id, 10) is parseInt(id, 10)
44 |
45 | res.json current
46 | ###
--------------------------------------------------------------------------------
/src/scripts/controllers/appController.ls:
--------------------------------------------------------------------------------
1 | #
2 | # do this once only!
3 | #
4 | # TODO: duplicate in pubs and remove prelude from any that don't need it.
5 | #
6 | # import prelude
7 |
8 | angular.module('app').controller('appController', [
9 | '$scope'
10 | '$location'
11 | '$resource'
12 | '$rootScope'
13 |
14 | ($scope, $location, $resource, $rootScope) ->
15 |
16 | # Uses the url to determine if the selected
17 | # menu item should have the class active.
18 | $scope.$location = $location
19 |
20 | $scope.$watch '$location.path()', (path) ->
21 |
22 | #console.log 'path=', path
23 | $scope.activeNavId = path || '/'
24 |
25 |
26 | # getClass compares the current url with the id.
27 | # If the current url starts with the id it returns 'active'
28 | # otherwise it will return '' an empty string. E.g.
29 | #
30 | # # current url = '/products/1'
31 | # getClass('/products') # returns 'active'
32 | # getClass('/orders') # returns ''
33 | #
34 | $scope.getClass = (id) ->
35 |
36 | if $scope.activeNavId && $scope.activeNavId.indexOf(id) == 0
37 | return 'active'
38 | else
39 | return ''
40 | ])
41 |
42 |
--------------------------------------------------------------------------------
/test/mathmoTestsTodo/lib/jasmine-1.0.2/MIT.LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008-2010 Pivotal Labs
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2013 University of Cambridge
4 | Copyright (c) 2013 Cary Landholt
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
--------------------------------------------------------------------------------
/src/scripts/app.ls:
--------------------------------------------------------------------------------
1 | (angular.module 'app', ['ngResource', 'ui.bootstrap'])
2 | .run ['$rootScope', '$log', ($rootScope, $log) ->
3 |
4 | # fire an event related to the current route
5 | $rootScope.$on '$routeChangeSuccess', (event, currentRoute, priorRoute) ->
6 | $rootScope.$broadcast "#{currentRoute.controller}$routeChangeSuccess", currentRoute, priorRoute
7 |
8 | ]
9 |
10 | /*
11 | See https://github.com/angular/angular.js/issues/1050
12 | and https://github.com/angular/angular.js/issues/1925
13 |
14 | The code below is based on suggestions in 1050, but these
15 | still fail when the svg attributes contain interpolate values.
16 |
17 | So instead use gmp26 angular patch to Angular v1.0.4 documented
18 | in 1925. Hopefully the Chrome/Angular teams will sort this out.
19 |
20 | .config () ->
21 | for name in ['width', 'height', 'x1', 'x2', 'y1', 'y2']
22 | svgName = 'svg' + name[0].toUpperCase() + name.slice(1)
23 | angular.module('app').directive svgName, () ->
24 | link: (scope, element, attrs) ->
25 | attrs.$observe ngName, (value) ->
26 | # NB watch can fail initially if attribute value is interpolated
27 | if(value)
28 | attrs.$set(name, value)
29 | */
--------------------------------------------------------------------------------
/src/styles/responsive.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.3.1
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */
10 |
11 |
12 | // Responsive.less
13 | // For phone and tablet devices
14 | // -------------------------------------------------------------
15 |
16 |
17 | // REPEAT VARIABLES & MIXINS
18 | // -------------------------
19 | // Required since we compile the responsive stuff separately
20 |
21 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc
22 | @import "mixins.less";
23 |
24 |
25 | // RESPONSIVE CLASSES
26 | // ------------------
27 |
28 | @import "responsive-utilities.less";
29 |
30 |
31 | // MEDIA QUERIES
32 | // ------------------
33 |
34 | // Large desktops
35 | @import "responsive-1200px-min.less";
36 |
37 | // Tablets to regular desktops
38 | @import "responsive-768px-979px.less";
39 |
40 | // Phones to portrait tablets and narrow desktops
41 | @import "responsive-767px-max.less";
42 |
43 |
44 | // RESPONSIVE NAVBAR
45 | // ------------------
46 |
47 | // From 979px and below, show a button to toggle navbar contents
48 | @import "responsive-navbar.less";
49 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // list of files / patterns to load in the browser
2 | files = [
3 | JASMINE,
4 | JASMINE_ADAPTER,
5 | './dist/scripts/libs/angular.js',
6 | './dist/scripts/libs/angular-resource.js',
7 | './dist/scripts/libs/ui-bootstrap-tpls.js',
8 | './dist/scripts/libs/d3.v3.js',
9 | './test/scripts/libs/angular-mocks.js',
10 | './test/scripts/libs/prelude-browser-min.js',
11 |
12 | /* things under test */
13 | './dist/scripts/app.js',
14 | './dist/scripts/**/controllers/*.js',
15 | './dist/scripts/**/directives/*.js',
16 | './dist/scripts/**/filters/*.js',
17 | './dist/scripts/**/responseInterceptors/*.js',
18 | './dist/scripts/**/services/*.js',
19 |
20 | /* test scripts */
21 | // './dist_test/scripts/controllers/*.js',
22 | // './dist_test/scripts/directives/*.js',
23 | // './dist_test/scripts/filters/*.js',
24 | // './dist_test/scripts/services/*.js',
25 |
26 | /* This line needs to be specific or bootstrap.js throws an error :( */
27 | /* See http://stackoverflow.com/questions/9227406/ */
28 | './dist/scripts/libs/qa/*.js',
29 |
30 | /* Unit tests for mathmo */
31 | './dist/scripts/pubs/mathmo/services/*.js',
32 | './dist/scripts/libs/seedrandom.js',
33 | './dist_test/pubs/**/*.js'
34 | // './test/unit/**/*.js'
35 | ];
36 |
37 | // level of logging
38 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
39 | logLevel = LOG_INFO;
40 |
--------------------------------------------------------------------------------
/test/scripts/controllers/todoSpec.ls:
--------------------------------------------------------------------------------
1 | beforeEach module 'app'
2 |
3 | describe 'App', (_) ->
4 |
5 | # we want to make the scope available generally
6 | scope = {}
7 |
8 | beforeEach inject ($controller, $rootScope) ->
9 |
10 | scope := $rootScope.$new()
11 | $controller 'todoController',
12 | $scope: scope
13 |
14 | describe 'add', (_) ->
15 |
16 | it 'should add new todo', ->
17 | scope.todos = []
18 | scope.todoText = 'FAKE TODO'
19 |
20 | scope.addTodo()
21 |
22 | expect(scope.todos.length).toBe 1
23 | expect(scope.todos[0].text).toBe 'FAKE TODO'
24 |
25 |
26 | it 'should reset newText', ->
27 | scope.todos = []
28 | scope.todoText = 'SOME TEXT'
29 | scope.addTodo()
30 |
31 | expect(scope.todoText).toBe ''
32 |
33 |
34 | describe 'remaining', (_) ->
35 |
36 | it 'should return number of todos that are not done', ->
37 | scope.todos = [
38 | {done: false}
39 | {done: false}
40 | {done: false}
41 | {done: false}
42 | ]
43 | expect(scope.remaining()).toBe 4
44 |
45 | scope.todos[0].done = true
46 | expect(scope.remaining()).toBe 3
47 |
48 | describe 'archive', (_) ->
49 |
50 | it 'should remove todos that are done', ->
51 | scope.todos = [
52 | {done: false}
53 | {done: true}
54 | {done: false}
55 | ]
56 |
57 | expect(scope.todos.length).toBe 3
58 |
59 | scope.archive()
60 | expect(scope.todos.length).toBe 2
61 |
--------------------------------------------------------------------------------
/test/mathmoTestsTodo/SpecRunner.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 | Jasmine Test Runner
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/styles/thumbnails.less:
--------------------------------------------------------------------------------
1 | //
2 | // Thumbnails
3 | // --------------------------------------------------
4 |
5 |
6 | // Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files
7 |
8 | // Make wrapper ul behave like the grid
9 | .thumbnails {
10 | margin-left: -@gridGutterWidth;
11 | list-style: none;
12 | .clearfix();
13 | }
14 | // Fluid rows have no left margin
15 | .row-fluid .thumbnails {
16 | margin-left: 0;
17 | }
18 |
19 | // Float li to make thumbnails appear in a row
20 | .thumbnails > li {
21 | float: left; // Explicity set the float since we don't require .span* classes
22 | margin-bottom: @baseLineHeight;
23 | margin-left: @gridGutterWidth;
24 | }
25 |
26 | // The actual thumbnail (can be `a` or `div`)
27 | .thumbnail {
28 | display: block;
29 | padding: 4px;
30 | line-height: @baseLineHeight;
31 | border: 1px solid #ddd;
32 | .border-radius(@baseBorderRadius);
33 | .box-shadow(0 1px 3px rgba(0,0,0,.055));
34 | .transition(all .2s ease-in-out);
35 | }
36 | // Add a hover/focus state for linked versions only
37 | a.thumbnail:hover,
38 | a.thumbnail:focus {
39 | border-color: @linkColor;
40 | .box-shadow(0 1px 4px rgba(0,105,214,.25));
41 | }
42 |
43 | // Images and captions
44 | .thumbnail > img {
45 | display: block;
46 | max-width: 100%;
47 | margin-left: auto;
48 | margin-right: auto;
49 | }
50 | .thumbnail .caption {
51 | padding: 9px;
52 | color: @gray;
53 | }
54 |
--------------------------------------------------------------------------------
/src/styles/code.less:
--------------------------------------------------------------------------------
1 | //
2 | // Code (inline and blocK)
3 | // --------------------------------------------------
4 |
5 |
6 | // Inline and block code styles
7 | code,
8 | pre {
9 | padding: 0 3px 2px;
10 | #font > #family > .monospace;
11 | font-size: @baseFontSize - 2;
12 | color: @grayDark;
13 | .border-radius(3px);
14 | }
15 |
16 | // Inline code
17 | code {
18 | padding: 2px 4px;
19 | color: #d14;
20 | background-color: #f7f7f9;
21 | border: 1px solid #e1e1e8;
22 | white-space: nowrap;
23 | }
24 |
25 | // Blocks of code
26 | pre {
27 | display: block;
28 | padding: (@baseLineHeight - 1) / 2;
29 | margin: 0 0 @baseLineHeight / 2;
30 | font-size: @baseFontSize - 1; // 14px to 13px
31 | line-height: @baseLineHeight;
32 | word-break: break-all;
33 | word-wrap: break-word;
34 | white-space: pre;
35 | white-space: pre-wrap;
36 | background-color: #f5f5f5;
37 | border: 1px solid #ccc; // fallback for IE7-8
38 | border: 1px solid rgba(0,0,0,.15);
39 | .border-radius(@baseBorderRadius);
40 |
41 | // Make prettyprint styles more spaced out for readability
42 | &.prettyprint {
43 | margin-bottom: @baseLineHeight;
44 | }
45 |
46 | // Account for some code outputs that place code tags in pre tags
47 | code {
48 | padding: 0;
49 | color: inherit;
50 | white-space: pre;
51 | white-space: pre-wrap;
52 | background-color: transparent;
53 | border: 0;
54 | }
55 | }
56 |
57 | // Enable scrollable blocks of code
58 | .pre-scrollable {
59 | max-height: 340px;
60 | overflow-y: scroll;
61 | }
--------------------------------------------------------------------------------
/test/pubs/frogs/frogDirectiveSpec.ls:
--------------------------------------------------------------------------------
1 | beforeEach module 'app'
2 |
3 | describe 'App', ->
4 |
5 | # we want to make the scope available generally
6 | scope = {}
7 |
8 | beforeEach inject ($controller, $rootScope) ->
9 |
10 | scope := $rootScope.$new()
11 |
12 | $controller 'frogController',
13 | $scope: scope
14 |
15 |
16 | /*
17 | describe 'frog colours', ->
18 | it 'should return a red frog on pad 0', ->
19 | expect(scope.getFrog(0)).toBe "pad redfrog"
20 | describe 'add', ->
21 |
22 | it 'should add new todo', ->
23 | scope.todos = []
24 | scope.todoText = 'FAKE TODO'
25 |
26 | scope.addTodo()
27 |
28 | expect(scope.todos.length).toBe 1
29 | expect(scope.todos[0].text).toBe 'FAKE TODO'
30 |
31 |
32 | it 'should reset newText', ->
33 | scope.todos = []
34 | scope.todoText = 'SOME TEXT'
35 | scope.addTodo()
36 |
37 | expect(scope.todoText).toBe ''
38 |
39 |
40 | describe 'remaining', ->
41 |
42 | it 'should return number of todos that are not done', ->
43 | scope.todos = [
44 | {done: false}
45 | {done: false}
46 | {done: false}
47 | {done: false}
48 | ]
49 | expect(scope.remaining()).toBe 4
50 |
51 | scope.todos[0].done = true
52 | expect(scope.remaining()).toBe 3
53 |
54 | describe 'archive', ->
55 |
56 | it 'should remove todos that are done', ->
57 | scope.todos = [
58 | {done: false}
59 | {done: true}
60 | {done: false}
61 | ]
62 |
63 | expect(scope.todos.length).toBe 3
64 |
65 | scope.archive()
66 | expect(scope.todos.length).toBe 2
67 | */
--------------------------------------------------------------------------------
/src/scripts/pubs/probability/controllers/sampleSpinController.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').controller 'sampleSpinController', [
2 | '$scope'
3 | ($scope) ->
4 | console.log "results controller scope = #{$scope.$id}"
5 |
6 | $scope.spinnerConfigs =
7 | spin1: # an array of spinner configurations
8 | * "label": \r
9 | "weight": 10
10 | "fill" : \red
11 | * "label": \b
12 | "weight": 10
13 | "fill" : \blue
14 | * "label": \y
15 | "weight": 10
16 | "fill" : \yellow
17 | * "label": \g
18 | "weight": 10
19 | "fill" : \green
20 | * "label": \p
21 | "weight": 10
22 | "fill" : \pink
23 | * "label": \g
24 | "weight": 10
25 | "fill" : \grey
26 |
27 | spin2:
28 | * "label": \Y
29 | "weight": 10
30 | "fill" : \#fe2
31 | * "label": \Y
32 | "weight": 10
33 | "fill" : \#fe2
34 | * "label": \Y
35 | "weight": 10
36 | "fill" : \#fe2
37 | * "label": \Y
38 | "weight": 10
39 | "fill" : \#fe2
40 | * "label": \B
41 | "weight": 10
42 | "fill" : \#6af
43 | * "label": \B
44 | "weight": 10
45 | "fill" : \#4af
46 |
47 | $scope.model =
48 | spin1: 0
49 | spin2: 0
50 |
51 | $scope.reset = !->
52 |
53 | # called by spinner when spin stops to log the result
54 | $scope.spinLog = (name, sectorIndex, label) ->
55 | console.log "name [#sectorIndex] -> #label"
56 |
57 | ]
--------------------------------------------------------------------------------
/src/styles/alerts.less:
--------------------------------------------------------------------------------
1 | //
2 | // Alerts
3 | // --------------------------------------------------
4 |
5 |
6 | // Base styles
7 | // -------------------------
8 |
9 | .alert {
10 | padding: 8px 35px 8px 14px;
11 | margin-bottom: @baseLineHeight;
12 | text-shadow: 0 1px 0 rgba(255,255,255,.5);
13 | background-color: @warningBackground;
14 | border: 1px solid @warningBorder;
15 | .border-radius(@baseBorderRadius);
16 | }
17 | .alert,
18 | .alert h4 {
19 | // Specified for the h4 to prevent conflicts of changing @headingsColor
20 | color: @warningText;
21 | }
22 | .alert h4 {
23 | margin: 0;
24 | }
25 |
26 | // Adjust close link position
27 | .alert .close {
28 | position: relative;
29 | top: -2px;
30 | right: -21px;
31 | line-height: @baseLineHeight;
32 | }
33 |
34 |
35 | // Alternate styles
36 | // -------------------------
37 |
38 | .alert-success {
39 | background-color: @successBackground;
40 | border-color: @successBorder;
41 | color: @successText;
42 | }
43 | .alert-success h4 {
44 | color: @successText;
45 | }
46 | .alert-danger,
47 | .alert-error {
48 | background-color: @errorBackground;
49 | border-color: @errorBorder;
50 | color: @errorText;
51 | }
52 | .alert-danger h4,
53 | .alert-error h4 {
54 | color: @errorText;
55 | }
56 | .alert-info {
57 | background-color: @infoBackground;
58 | border-color: @infoBorder;
59 | color: @infoText;
60 | }
61 | .alert-info h4 {
62 | color: @infoText;
63 | }
64 |
65 |
66 | // Block alerts
67 | // -------------------------
68 |
69 | .alert-block {
70 | padding-top: 14px;
71 | padding-bottom: 14px;
72 | }
73 | .alert-block > p,
74 | .alert-block > ul {
75 | margin-bottom: 0;
76 | }
77 | .alert-block p + p {
78 | margin-top: 5px;
79 | }
80 |
--------------------------------------------------------------------------------
/boilerplate/main.ls:
--------------------------------------------------------------------------------
1 | require {
2 | shim:
3 | 'controllers/appController':
4 | deps:
5 | * 'app'
6 | ...
7 | 'controllers/<%= mask %>Controller':
8 | deps:
9 | * 'app'
10 | ...
11 | 'controllers/<%= mask %>Directive':
12 | deps:
13 | * 'app'
14 | ...
15 | 'directives/d3Vis':
16 | deps:
17 | * 'app'
18 | 'libs/d3.v3'
19 | 'directives/svgCheck'
20 | 'directives/d3DotGrid':
21 | deps:
22 | * 'app'
23 | 'directives/d3Vis'
24 | 'directives/svgCheck':
25 | deps:
26 | * 'app'
27 | 'libs/d3.v3'
28 | 'directives/appVersion':
29 | deps:
30 | * 'app'
31 | 'services/semver'
32 | 'services/semver':
33 | deps:
34 | * 'app'
35 | ...
36 | 'bootstrap':
37 | deps:
38 | * 'app'
39 | ...
40 | 'libs/bootstrap':
41 | deps:
42 | * 'libs/jquery'
43 | ...
44 | 'libs/angular-resource':
45 | deps:
46 | * 'libs/angular'
47 | ...
48 | 'libs/ui-bootstrap-tpls':
49 | deps:
50 | * 'libs/angular'
51 | ...
52 | 'app':
53 | deps:
54 | * 'libs/angular'
55 | 'libs/angular-resource'
56 | 'libs/ui-bootstrap-tpls'
57 | 'routes':
58 | deps:
59 | * 'app'
60 | ...
61 | 'views':
62 | deps:
63 | * 'app'
64 | ...
65 | },
66 | * 'require'
67 | 'controllers/appController'
68 | 'pubs/<%= mask %>/controllers/<%= mask %>Controller'
69 | 'pubs/<%= mask %>/directives/<%= mask %>Directive'
70 | 'directives/appVersion'
71 | 'routes'
72 | 'views'
73 | , (require) -> require ['bootstrap']
--------------------------------------------------------------------------------
/src/styles/bootstrap.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v2.3.1
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */
10 |
11 | // Core variables and mixins
12 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc
13 | @import "mixins.less";
14 |
15 | // CSS Reset
16 | @import "reset.less";
17 |
18 | // Grid system and page structure
19 | @import "scaffolding.less";
20 | @import "grid.less";
21 | @import "layouts.less";
22 |
23 | // Base CSS
24 | @import "type.less";
25 | @import "code.less";
26 | @import "forms.less";
27 | @import "tables.less";
28 |
29 | // Components: common
30 | @import "sprites.less";
31 | @import "dropdowns.less";
32 | @import "wells.less";
33 | @import "component-animations.less";
34 | @import "close.less";
35 |
36 | // Components: Buttons & Alerts
37 | @import "buttons.less";
38 | @import "button-groups.less";
39 | @import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
40 |
41 | // Components: Nav
42 | @import "navs.less";
43 | @import "navbar.less";
44 | @import "breadcrumbs.less";
45 | @import "pagination.less";
46 | @import "pager.less";
47 |
48 | // Components: Popovers
49 | @import "modals.less";
50 | @import "tooltip.less";
51 | @import "popovers.less";
52 |
53 | // Components: Misc
54 | @import "thumbnails.less";
55 | @import "media.less";
56 | @import "labels-badges.less";
57 | @import "progress-bars.less";
58 | @import "accordion.less";
59 | @import "carousel.less";
60 | @import "hero-unit.less";
61 |
62 | // Utility classes
63 | @import "utilities.less"; // Has to be last to override when necessary
64 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Apps1",
3 | "version": "0.1.6",
4 | "author": {
5 | "name": "Mike Pearson",
6 | "email": "gmp26@cam.ac.uk",
7 | "url": "https://github.com/gmp26"
8 | },
9 | "description": "NRICH App Collection Architecture",
10 | "contributors": [
11 | {
12 | "name": "Alison Kiddle",
13 | "email": "ajk44@cam.ac.uk",
14 | "url": "https://github.com/ajk44"
15 | },
16 | {
17 | "name": "Cary Landholt",
18 | "email": "cary@landholt.com",
19 | "url": "https://github.com/CaryLandholt"
20 | },
21 | {
22 | "name": "David Bochenski",
23 | "email": "david@bochenski.co.uk",
24 | "url": "https://github.com/Bochenski"
25 | }
26 | ],
27 | "main": "./server.coffee",
28 | "repository": {
29 | "type": "git",
30 | "url": "https://github.com/gmp26/Apps1.git"
31 | },
32 | "dependencies": {
33 | "express": ">=3.0.3",
34 | "grunt-hustler": ">=0.7.4"
35 | },
36 | "devDependencies": {
37 | "coffee-script": ">=1.6.1",
38 | "express": ">=3.1.0",
39 | "grunt": ">=0.4.0",
40 | "grunt-contrib-clean": ">=0.4.0",
41 | "grunt-contrib-coffee": ">=0.6.0",
42 | "grunt-contrib-copy": ">=0.4.0",
43 | "grunt-contrib-imagemin": ">=0.1.2",
44 | "grunt-contrib-less": ">=0.5.0",
45 | "grunt-contrib-livereload": ">=0.1.2",
46 | "grunt-contrib-requirejs": ">=0.4.0",
47 | "grunt-contrib-watch": ">=0.3.1",
48 | "grunt-express": ">=0.2.1",
49 | "grunt-hustler": ">=0.11.2",
50 | "grunt-regarde": ">=0.1.1",
51 | "grunt-karma": ">=0.4",
52 | "socket.io": ">=0.9.13",
53 | "grunt-livescript": ">=0.5.0",
54 | "prelude-ls": ">=0.6.0"
55 | },
56 | "engines": {
57 | "node": ">=0.8.x",
58 | "npm": ">=1.1.x"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/views/boomerangs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Boomerangs
5 |
6 | Phil and Cath make and sell boomerangs for a school event.
7 | The money they raise will go to charity.
8 |
9 | They plan to make them in two sizes: small and large.
10 |
11 | Phil will carve them from wood.
12 | The small boomerang takes 2 hours to carve and the large one takes 3
13 | hours to carve.
14 | Phil has a total of 24 hours available for carving.
15 |
16 | Cath will decorate them.
17 | She only has time to decorate 10 boomerangs of either size.
18 |
19 | The small boomerang will make $8 for charity.
20 | The large boomerang will make $10 for charity.
21 | They want to make as much money for charity as they can.
22 |
23 | How many small and large boomerangs should they make?
24 |
25 | How much money will they then make?
26 |
27 |
28 |
29 | Let's experiment
30 |
31 |
32 |
Suppose Phil makes small and large boomerangs.
33 |
Carving {{small}} small boomerangs will take {{3*small}} hours.
34 |
Carving {{large}} large boomerangs will take {{3*large}} hours.
35 |
36 |
Phil will need {{ 2*small + 3*large }} hours to carve them all.
37 |
Cath has to decorate {{ small + large }} boomerangs.
38 |
39 |
{{small}} small boomerangs will make ${{8*small}} .
40 |
{{large}} large boomerangs will make ${{10*large}} .
41 |
42 |
Total amount made will be ${{ 8*small + 10*large }}
43 |
44 |
--------------------------------------------------------------------------------
/src/styles/responsive-utilities.less:
--------------------------------------------------------------------------------
1 | //
2 | // Responsive: Utility classes
3 | // --------------------------------------------------
4 |
5 |
6 | // IE10 Metro responsive
7 | // Required for Windows 8 Metro split-screen snapping with IE10
8 | // Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/
9 | @-ms-viewport{
10 | width: device-width;
11 | }
12 |
13 | // Hide from screenreaders and browsers
14 | // Credit: HTML5 Boilerplate
15 | .hidden {
16 | display: none;
17 | visibility: hidden;
18 | }
19 |
20 | // Visibility utilities
21 |
22 | // For desktops
23 | .visible-phone { display: none !important; }
24 | .visible-tablet { display: none !important; }
25 | .hidden-phone { }
26 | .hidden-tablet { }
27 | .hidden-desktop { display: none !important; }
28 | .visible-desktop { display: inherit !important; }
29 |
30 | // Tablets & small desktops only
31 | @media (min-width: 768px) and (max-width: 979px) {
32 | // Hide everything else
33 | .hidden-desktop { display: inherit !important; }
34 | .visible-desktop { display: none !important ; }
35 | // Show
36 | .visible-tablet { display: inherit !important; }
37 | // Hide
38 | .hidden-tablet { display: none !important; }
39 | }
40 |
41 | // Phones only
42 | @media (max-width: 767px) {
43 | // Hide everything else
44 | .hidden-desktop { display: inherit !important; }
45 | .visible-desktop { display: none !important; }
46 | // Show
47 | .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior
48 | // Hide
49 | .hidden-phone { display: none !important; }
50 | }
51 |
52 | // Print utilities
53 | .visible-print { display: none !important; }
54 | .hidden-print { }
55 |
56 | @media print {
57 | .visible-print { display: inherit !important; }
58 | .hidden-print { display: none !important; }
59 | }
60 |
--------------------------------------------------------------------------------
/src/scripts/pubs/frogs/directives/frog.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').directive 'frog', [
2 | '$timeout'
3 | ($timeout) ->
4 | restrict: 'EA'
5 | link: (scope, element, attrs) ->
6 |
7 | # console.log "froggy element ", element
8 |
9 | # we forgot the "px"; and browsers simply ignore unitless properties!!!
10 | X = (x) -> 100*x + "px"
11 |
12 | scope.$watch 'frog.x', (newX, oldX) ->
13 | # console.log "moving #oldX to #newX"
14 | element.css("left", X(newX))
15 | element.css("z-index", 1)
16 |
17 | # respond to a click on a frog
18 | jump = (me) ->
19 | # currently in browser event context
20 |
21 | # apply this function in angular context
22 | # remove this line and moveCount fails to update
23 | scope.$apply ->
24 |
25 | # ground all the frogs while filtering for the empty pad
26 | emptyPad = (scope.frogs.filter (d) ->
27 | d.element.css("z-index", 0)
28 | d.colour == 1
29 | )[0]
30 |
31 | # can we hop?
32 | diff = Math.abs(me.x - emptyPad.x)
33 | if diff == 1 or diff == 2
34 |
35 | # yes! update the model
36 | scope.hop(me, emptyPad)
37 |
38 | # & update the screen
39 | #me.move(emptyPad)
40 |
41 |
42 | # map colour to css class
43 | classBy = (colour) ->
44 | switch colour
45 | | 0 => "frog red"
46 | | 1 => "frog"
47 | | 2 => "frog blue"
48 | default throw new Error("invalid frog colour")
49 |
50 | element.addClass(classBy(scope.frog.colour))
51 |
52 | # added to put frogs in right place on startup
53 | element.css("left", X(scope.frog.x))
54 |
55 | # set up a click handler on the frog
56 | element.bind "click", (event) -> jump(scope.frog)
57 |
58 | scope.frog.element = element
59 |
60 | ]
61 |
--------------------------------------------------------------------------------
/test/pubs/mathmo/mathmo-tests.md:
--------------------------------------------------------------------------------
1 | # Mathmo tests
2 |
3 | ## Notes
4 |
5 | When you're populating `testlist` with questions and answers, remember to escape backslashes (`\\`) and quote marks (`\"`) correctly. I'm using a [TextExpander][te] snippet to escape these automatically. I have the following AppleScript:
6 |
7 | on findAndReplace(tofind, toreplace, TheString)
8 | set ditd to text item delimiters
9 | set text item delimiters to tofind
10 | set textItems to text items of TheString
11 | set text item delimiters to toreplace
12 | if (class of TheString is string) then
13 | set res to textItems as string
14 | else -- if (class of TheString is Unicode text) then
15 | set res to textItems as Unicode text
16 | end if
17 | set text item delimiters to ditd
18 | return res
19 | end findAndReplace
20 |
21 | set s to findAndReplace("\\", "\\\\", the clipboard)
22 | set t to findAndReplace("\"", "\\\"", s)
23 |
24 | return t
25 |
26 | bound to `;ls` (for LiveScript, even though the test file is CoffeeScript) to do this automatically. Yes, even if AppleScript is awful for text manipulation. At the core of this project is JavaScript, so we're in no position to judge.
27 |
28 | Notes on `problems.js`:
29 |
30 | * Use `\DeclareMathOperator*` (or even a global stylesheet) rather then `{\rm artanh}` where appropriate.
31 | * Fix `\,\mathrm{d}x` for integrals
32 | * Use an `align*` environment for Newton-Raphson (and possibly a `\phantom{-}` if we can manage it)
33 | * Give the ``'s used in the exercises their own class in CSS, then the formatting can be consistent and global. (And use a list in some of the answers where it isn’t already.)
34 | * Fix `makeMatXforms`
35 | * Add non-breaking spaces in the `3 DP.`
36 | * Explore use of `\left(` and `\right)` delimiters.
37 |
38 | [te]: http://smilesoftware.com/TextExpander/index.html
39 |
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/directives/mathmoPlot.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').directive 'mathmoPlot' [
2 | '$parse'
3 | 'd3MultiLineChart'
4 | ($parse, chartFactory) ->
5 | return
6 | restrict: 'A'
7 |
8 | link: (scope, element, attrs) ->
9 |
10 | console.log 'mathmoPlot'
11 |
12 | # remember last draw space
13 | savedContainer = null
14 | savedWidth = 400
15 | savedHeight = 300
16 |
17 | chart = null
18 | data = null
19 |
20 | draw = (event, container, width, height) ->
21 | console.log 'draw'
22 |
23 | savedContainer := container
24 | savedWidth := width
25 | savedHeight := height
26 |
27 | scope.$parent.$watch attrs.data, (data) ->
28 | console.log 'data changed'
29 | console.log data
30 | scope.$broadcast 'resize', savedContainer, savedWidth, savedHeight
31 |
32 | # since we're nested in d3Vis scope, must evaluate attributes on parent scope
33 | getData = $parse(attrs.data)
34 | data := getData(scope.$parent)
35 |
36 | chart := chartFactory()
37 | .width(width)
38 | .height(height)
39 | .x((d) -> d[0])
40 | .y((d) -> d[1])
41 |
42 | container.datum(data).call(chart)
43 |
44 | resize = (event, container = savedContainer, width = savedWidth, height = savedHeight) ->
45 | console.log 'resize'
46 |
47 | savedContainer := container
48 | savedWidth := width
49 | savedHeight := height
50 |
51 | # since we're nested in d3Vis scope, must evaluate attributes on parent scope
52 | getData = $parse(attrs.data)
53 | data := getData(scope.$parent)
54 |
55 | chart.width(width).height(height)
56 | container.datum(data).call(chart)
57 |
58 | # listen for redraw events
59 | scope.$on 'draw', draw
60 | scope.$on 'resize', resize
61 |
62 | ]
63 |
--------------------------------------------------------------------------------
/src/styles/tooltip.less:
--------------------------------------------------------------------------------
1 | //
2 | // Tooltips
3 | // --------------------------------------------------
4 |
5 |
6 | // Base class
7 | .tooltip {
8 | position: absolute;
9 | z-index: @zindexTooltip;
10 | display: block;
11 | visibility: visible;
12 | font-size: 11px;
13 | line-height: 1.4;
14 | .opacity(0);
15 | &.in { .opacity(80); }
16 | &.top { margin-top: -3px; padding: 5px 0; }
17 | &.right { margin-left: 3px; padding: 0 5px; }
18 | &.bottom { margin-top: 3px; padding: 5px 0; }
19 | &.left { margin-left: -3px; padding: 0 5px; }
20 | }
21 |
22 | // Wrapper for the tooltip content
23 | .tooltip-inner {
24 | max-width: 200px;
25 | padding: 8px;
26 | color: @tooltipColor;
27 | text-align: center;
28 | text-decoration: none;
29 | background-color: @tooltipBackground;
30 | .border-radius(@baseBorderRadius);
31 | }
32 |
33 | // Arrows
34 | .tooltip-arrow {
35 | position: absolute;
36 | width: 0;
37 | height: 0;
38 | border-color: transparent;
39 | border-style: solid;
40 | }
41 | .tooltip {
42 | &.top .tooltip-arrow {
43 | bottom: 0;
44 | left: 50%;
45 | margin-left: -@tooltipArrowWidth;
46 | border-width: @tooltipArrowWidth @tooltipArrowWidth 0;
47 | border-top-color: @tooltipArrowColor;
48 | }
49 | &.right .tooltip-arrow {
50 | top: 50%;
51 | left: 0;
52 | margin-top: -@tooltipArrowWidth;
53 | border-width: @tooltipArrowWidth @tooltipArrowWidth @tooltipArrowWidth 0;
54 | border-right-color: @tooltipArrowColor;
55 | }
56 | &.left .tooltip-arrow {
57 | top: 50%;
58 | right: 0;
59 | margin-top: -@tooltipArrowWidth;
60 | border-width: @tooltipArrowWidth 0 @tooltipArrowWidth @tooltipArrowWidth;
61 | border-left-color: @tooltipArrowColor;
62 | }
63 | &.bottom .tooltip-arrow {
64 | top: 0;
65 | left: 50%;
66 | margin-left: -@tooltipArrowWidth;
67 | border-width: 0 @tooltipArrowWidth @tooltipArrowWidth;
68 | border-bottom-color: @tooltipArrowColor;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/boilerplate/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= mask %>
7 |
8 |
9 |
10 | <% if (config.environment === 'prod') { %>
11 |
12 |
13 | <% } else { %>
14 |
15 | <% } %>
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
40 |
45 | <% if (config.environment === 'prod') { %>
46 |
47 | <% } else { %>
48 |
49 | <% } %>
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/scripts/pubs/todo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Todo App example
7 |
8 |
9 |
10 | <% if (config.environment === 'prod') { %>
11 |
12 |
13 | <% } else { %>
14 |
15 | <% } %>
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
40 |
45 | <% if (config.environment === 'prod') { %>
46 |
47 | <% } else { %>
48 |
49 | <% } %>
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/scripts/pubs/frogs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | NRICH Frogs
7 |
8 |
9 |
10 | <% if (config.environment === 'prod') { %>
11 |
12 |
13 | <% } else { %>
14 |
15 | <% } %>
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
40 |
45 | <% if (config.environment === 'prod') { %>
46 |
47 | <% } else { %>
48 |
49 | <% } %>
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/scripts/pubs/tilted/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Tilted Squares
7 |
8 |
9 |
10 | <% if (config.environment === 'prod') { %>
11 |
12 |
13 | <% } else { %>
14 |
15 | <% } %>
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
40 |
45 | <% if (config.environment === 'prod') { %>
46 |
47 | <% } else { %>
48 |
49 | <% } %>
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/scripts/pubs/boomerangs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Word problems
7 |
8 |
9 |
10 | <% if (config.environment === 'prod') { %>
11 |
12 |
13 | <% } else { %>
14 |
15 | <% } %>
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
40 |
45 | <% if (config.environment === 'prod') { %>
46 |
47 | <% } else { %>
48 |
49 | <% } %>
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/scripts/pubs/probability/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Probability Interactives
7 |
8 |
9 |
10 | <% if (config.environment === 'prod') { %>
11 |
12 |
13 | <% } else { %>
14 |
15 | <% } %>
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
40 |
45 | <% if (config.environment === 'prod') { %>
46 |
47 | <% } else { %>
48 |
49 | <% } %>
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/styles/labels-badges.less:
--------------------------------------------------------------------------------
1 | //
2 | // Labels and badges
3 | // --------------------------------------------------
4 |
5 |
6 | // Base classes
7 | .label,
8 | .badge {
9 | display: inline-block;
10 | padding: 2px 4px;
11 | font-size: @baseFontSize * .846;
12 | font-weight: bold;
13 | line-height: 14px; // ensure proper line-height if floated
14 | color: @white;
15 | vertical-align: baseline;
16 | white-space: nowrap;
17 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
18 | background-color: @grayLight;
19 | }
20 | // Set unique padding and border-radii
21 | .label {
22 | .border-radius(3px);
23 | }
24 | .badge {
25 | padding-left: 9px;
26 | padding-right: 9px;
27 | .border-radius(9px);
28 | }
29 |
30 | // Empty labels/badges collapse
31 | .label,
32 | .badge {
33 | &:empty {
34 | display: none;
35 | }
36 | }
37 |
38 | // Hover/focus state, but only for links
39 | a {
40 | &.label:hover,
41 | &.label:focus,
42 | &.badge:hover,
43 | &.badge:focus {
44 | color: @white;
45 | text-decoration: none;
46 | cursor: pointer;
47 | }
48 | }
49 |
50 | // Colors
51 | // Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute)
52 | .label,
53 | .badge {
54 | // Important (red)
55 | &-important { background-color: @errorText; }
56 | &-important[href] { background-color: darken(@errorText, 10%); }
57 | // Warnings (orange)
58 | &-warning { background-color: @orange; }
59 | &-warning[href] { background-color: darken(@orange, 10%); }
60 | // Success (green)
61 | &-success { background-color: @successText; }
62 | &-success[href] { background-color: darken(@successText, 10%); }
63 | // Info (turquoise)
64 | &-info { background-color: @infoText; }
65 | &-info[href] { background-color: darken(@infoText, 10%); }
66 | // Inverse (black)
67 | &-inverse { background-color: @grayDark; }
68 | &-inverse[href] { background-color: darken(@grayDark, 10%); }
69 | }
70 |
71 | // Quick fix for labels/badges in buttons
72 | .btn {
73 | .label,
74 | .badge {
75 | position: relative;
76 | top: -1px;
77 | }
78 | }
79 | .btn-mini {
80 | .label,
81 | .badge {
82 | top: 0;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | NRICH Apps:1
7 |
8 |
9 |
10 | <% if (config.environment === 'prod') { %>
11 |
12 |
13 | <% } else { %>
14 |
15 | <% } %>
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
40 |
45 | <% if (config.environment === 'prod') { %>
46 |
47 | <% } else { %>
48 |
49 | <% } %>
50 |
51 |
52 |
53 |
56 |
57 |
--------------------------------------------------------------------------------
/src/styles/modals.less:
--------------------------------------------------------------------------------
1 | //
2 | // Modals
3 | // --------------------------------------------------
4 |
5 | // Background
6 | .modal-backdrop {
7 | position: fixed;
8 | top: 0;
9 | right: 0;
10 | bottom: 0;
11 | left: 0;
12 | z-index: @zindexModalBackdrop;
13 | background-color: @black;
14 | // Fade for backdrop
15 | &.fade { opacity: 0; }
16 | }
17 |
18 | .modal-backdrop,
19 | .modal-backdrop.fade.in {
20 | .opacity(80);
21 | }
22 |
23 | // Base modal
24 | .modal {
25 | position: fixed;
26 | top: 10%;
27 | left: 50%;
28 | z-index: @zindexModal;
29 | width: 560px;
30 | margin-left: -280px;
31 | background-color: @white;
32 | border: 1px solid #999;
33 | border: 1px solid rgba(0,0,0,.3);
34 | *border: 1px solid #999; /* IE6-7 */
35 | .border-radius(6px);
36 | .box-shadow(0 3px 7px rgba(0,0,0,0.3));
37 | .background-clip(padding-box);
38 | // Remove focus outline from opened modal
39 | outline: none;
40 |
41 | &.fade {
42 | .transition(e('opacity .3s linear, top .3s ease-out'));
43 | top: -25%;
44 | }
45 | &.fade.in { top: 10%; }
46 | }
47 | .modal-header {
48 | padding: 9px 15px;
49 | border-bottom: 1px solid #eee;
50 | // Close icon
51 | .close { margin-top: 2px; }
52 | // Heading
53 | h3 {
54 | margin: 0;
55 | line-height: 30px;
56 | }
57 | }
58 |
59 | // Body (where all modal content resides)
60 | .modal-body {
61 | position: relative;
62 | overflow-y: auto;
63 | max-height: 400px;
64 | padding: 15px;
65 | }
66 | // Remove bottom margin if need be
67 | .modal-form {
68 | margin-bottom: 0;
69 | }
70 |
71 | // Footer (for actions)
72 | .modal-footer {
73 | padding: 14px 15px 15px;
74 | margin-bottom: 0;
75 | text-align: right; // right align buttons
76 | background-color: #f5f5f5;
77 | border-top: 1px solid #ddd;
78 | .border-radius(0 0 6px 6px);
79 | .box-shadow(inset 0 1px 0 @white);
80 | .clearfix(); // clear it in case folks use .pull-* classes on buttons
81 |
82 | // Properly space out buttons
83 | .btn + .btn {
84 | margin-left: 5px;
85 | margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs
86 | }
87 | // but override that for button groups
88 | .btn-group .btn + .btn {
89 | margin-left: -1px;
90 | }
91 | // and override it for block buttons as well
92 | .btn-block + .btn-block {
93 | margin-left: 0;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/scripts/pubs/tilted/main.ls:
--------------------------------------------------------------------------------
1 | require {
2 | shim:
3 | 'controllers/appController':
4 | deps:
5 | * 'app'
6 | ...
7 | 'controllers/todoController':
8 | deps:
9 | * 'app'
10 | ...
11 | 'pubs/frogs/controllers/frogController':
12 | deps:
13 | * 'app'
14 | ...
15 | 'pubs/boomerangs/controllers/boomerangController':
16 | deps:
17 | * 'app'
18 | ...
19 | 'pubs/probability/controllers/sampleSpinController':
20 | deps:
21 | * 'app'
22 | 'libs/d3.v3'
23 | 'directives/svgCheck'
24 | 'pubs/frogs/directives/frogs':
25 | deps:
26 | * 'app'
27 | ...
28 | 'pubs/frogs/directives/frog':
29 | deps:
30 | * 'app'
31 | ...
32 | 'pubs/probability/directives/d3Spinner':
33 | deps:
34 | * 'app'
35 | 'directives/d3Vis'
36 | 'libs/d3.v3'
37 | 'directives/d3Vis':
38 | deps:
39 | * 'app'
40 | 'libs/d3.v3'
41 | 'directives/svgCheck'
42 | 'directives/d3DotGrid':
43 | deps:
44 | * 'app'
45 | 'directives/d3Vis'
46 | 'pubs/tilted/directives/d3TiltedSquare':
47 | deps:
48 | * 'app'
49 | 'directives/d3DotGrid'
50 | 'directives/svgCheck':
51 | deps:
52 | * 'app'
53 | 'libs/d3.v3'
54 | 'directives/appVersion':
55 | deps:
56 | * 'app'
57 | 'services/semver'
58 | 'services/semver':
59 | deps:
60 | * 'app'
61 | ...
62 | 'bootstrap':
63 | deps:
64 | * 'app'
65 | 'libs/angular'
66 | 'libs/bootstrap':
67 | deps:
68 | * 'libs/jquery'
69 | ...
70 | 'libs/angular-resource':
71 | deps:
72 | * 'libs/angular'
73 | ...
74 | 'libs/ui-bootstrap-tpls':
75 | deps:
76 | * 'libs/angular'
77 | ...
78 | 'app':
79 | deps:
80 | * 'libs/angular'
81 | 'libs/angular-resource'
82 | 'libs/ui-bootstrap-tpls'
83 | 'routes':
84 | deps:
85 | * 'app'
86 | ...
87 | 'views':
88 | deps:
89 | * 'app'
90 | ...
91 | },
92 | * 'require'
93 | 'controllers/appController'
94 | 'pubs/tilted/directives/d3TiltedSquare'
95 | 'directives/appVersion'
96 | 'routes'
97 | 'views'
98 | , (require) -> require ['bootstrap']
--------------------------------------------------------------------------------
/src/scripts/pubs/boomerangs/main.ls:
--------------------------------------------------------------------------------
1 | require {
2 | shim:
3 | 'controllers/appController':
4 | deps:
5 | * 'app'
6 | ...
7 | 'controllers/todoController':
8 | deps:
9 | * 'app'
10 | ...
11 | 'pubs/frogs/controllers/frogController':
12 | deps:
13 | * 'app'
14 | ...
15 | 'pubs/boomerangs/controllers/boomerangController':
16 | deps:
17 | * 'app'
18 | ...
19 | 'pubs/probability/controllers/sampleSpinController':
20 | deps:
21 | * 'app'
22 | 'libs/d3.v3'
23 | 'directives/svgCheck'
24 | 'pubs/frogs/directives/frogs':
25 | deps:
26 | * 'app'
27 | ...
28 | 'pubs/frogs/directives/frog':
29 | deps:
30 | * 'app'
31 | ...
32 | 'pubs/probability/directives/d3Spinner':
33 | deps:
34 | * 'app'
35 | 'directives/d3Vis'
36 | 'libs/d3.v3'
37 | 'directives/d3Vis':
38 | deps:
39 | * 'app'
40 | 'libs/d3.v3'
41 | 'directives/svgCheck'
42 | 'directives/d3DotGrid':
43 | deps:
44 | * 'app'
45 | 'directives/d3Vis'
46 | 'pubs/tilted/directives/d3TiltedSquare':
47 | deps:
48 | * 'app'
49 | 'directives/d3DotGrid'
50 | 'directives/svgCheck':
51 | deps:
52 | * 'app'
53 | 'libs/d3.v3'
54 | 'directives/appVersion':
55 | deps:
56 | * 'app'
57 | 'services/semver'
58 | 'services/semver':
59 | deps:
60 | * 'app'
61 | ...
62 | 'bootstrap':
63 | deps:
64 | * 'app'
65 | 'libs/angular'
66 | 'libs/bootstrap':
67 | deps:
68 | * 'libs/jquery'
69 | ...
70 | 'libs/angular-resource':
71 | deps:
72 | * 'libs/angular'
73 | ...
74 | 'libs/ui-bootstrap-tpls':
75 | deps:
76 | * 'libs/angular'
77 | ...
78 | 'app':
79 | deps:
80 | * 'libs/angular'
81 | 'libs/angular-resource'
82 | 'libs/ui-bootstrap-tpls'
83 | 'routes':
84 | deps:
85 | * 'app'
86 | ...
87 | 'views':
88 | deps:
89 | * 'app'
90 | ...
91 | },
92 | * 'require'
93 | 'controllers/appController'
94 | 'pubs/boomerangs/controllers/boomerangController'
95 | 'directives/appVersion'
96 | 'routes'
97 | 'views'
98 | , (require) -> require ['bootstrap']
--------------------------------------------------------------------------------
/test/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Angular Fun
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
41 |
42 |
43 |
52 |
53 |
--------------------------------------------------------------------------------
/src/scripts/pubs/frogs/main.ls:
--------------------------------------------------------------------------------
1 | require {
2 | shim:
3 | 'controllers/appController':
4 | deps:
5 | * 'app'
6 | ...
7 | 'controllers/todoController':
8 | deps:
9 | * 'app'
10 | ...
11 | 'pubs/frogs/controllers/frogController':
12 | deps:
13 | * 'app'
14 | ...
15 | 'pubs/boomerangs/controllers/boomerangController':
16 | deps:
17 | * 'app'
18 | ...
19 | 'pubs/probability/controllers/sampleSpinController':
20 | deps:
21 | * 'app'
22 | 'libs/d3.v3'
23 | 'directives/svgCheck'
24 | 'pubs/frogs/directives/frogs':
25 | deps:
26 | * 'app'
27 | ...
28 | 'pubs/frogs/directives/frog':
29 | deps:
30 | * 'app'
31 | ...
32 | 'pubs/probability/directives/d3Spinner':
33 | deps:
34 | * 'app'
35 | 'directives/d3Vis'
36 | 'libs/d3.v3'
37 | 'directives/d3Vis':
38 | deps:
39 | * 'app'
40 | 'libs/d3.v3'
41 | 'directives/svgCheck'
42 | 'directives/d3DotGrid':
43 | deps:
44 | * 'app'
45 | 'directives/d3Vis'
46 | 'pubs/tilted/directives/d3TiltedSquare':
47 | deps:
48 | * 'app'
49 | 'directives/d3DotGrid'
50 | 'directives/svgCheck':
51 | deps:
52 | * 'app'
53 | 'libs/d3.v3'
54 | 'directives/appVersion':
55 | deps:
56 | * 'app'
57 | 'services/semver'
58 | 'services/semver':
59 | deps:
60 | * 'app'
61 | ...
62 | 'bootstrap':
63 | deps:
64 | * 'app'
65 | 'libs/angular'
66 | 'libs/bootstrap':
67 | deps:
68 | * 'libs/jquery'
69 | ...
70 | 'libs/angular-resource':
71 | deps:
72 | * 'libs/angular'
73 | ...
74 | 'libs/ui-bootstrap-tpls':
75 | deps:
76 | * 'libs/angular'
77 | ...
78 | 'app':
79 | deps:
80 | * 'libs/angular'
81 | 'libs/angular-resource'
82 | 'libs/ui-bootstrap-tpls'
83 | 'routes':
84 | deps:
85 | * 'app'
86 | ...
87 | 'views':
88 | deps:
89 | * 'app'
90 | ...
91 | },
92 | * 'require'
93 | 'controllers/appController'
94 | 'pubs/frogs/controllers/frogController'
95 | 'pubs/frogs/directives/frogs'
96 | 'pubs/frogs/directives/frog'
97 | 'directives/appVersion'
98 | 'routes'
99 | 'views'
100 | , (require) -> require ['bootstrap']
--------------------------------------------------------------------------------
/src/scripts/libs/qa/geometry.ls:
--------------------------------------------------------------------------------
1 | #
2 | # geometry.ls - LaTeX formatting of equations of lines and circles
3 | #
4 |
5 | # Equations of the form
6 | # (x-a)^2 + (y-b)^2 = r^2
7 | circleEq1 = (a,b,r) ->
8 |
9 | if a ~= 0
10 | eqString = "x^2"
11 | else
12 | eqString = "(x"+signedNumber(-a)+")^2"
13 |
14 | if b ~= 0
15 | eqString += "+y^2="
16 | else
17 | eqString += "+(y"+signedNumber(-b)+")^2="
18 |
19 | eqString += r^2
20 |
21 | # Equations of the form
22 | # x^2 - 2ax + y^2 - 2bx = c
23 | circleEq2 = (a,b,r) ->
24 |
25 | C = r^2 - a^2 - b^2
26 |
27 | if a ~= 0
28 | eqString = "x^2"
29 | else
30 | eqString = "x^2"+signedNumber(-2*a)+"x"
31 |
32 | if b ~= 0
33 | eqString += "+y^2"
34 | else
35 | eqString += "+y^2"+signedNumber(-2*b)+"y"
36 |
37 | if C < 0
38 | eqString += signedNumber(-C)+"=0"
39 | else
40 | eqString += "="+C
41 |
42 | # Given points (a,b) and (c,d) on the line, return a
43 | # LaTeX-formatted equation in the form
44 | # ax + by + c = 0
45 | # Given a line in the form y=mx+c, use x=0 and x=1
46 | lineEq1 = (a,b,c,d) ->
47 |
48 | xcoeff = b - d
49 | ycoeff = c - a
50 | concoeff = -b * (c - a) + (d - b) * a
51 |
52 | # Put the terms in lowest common form
53 | h = gcd(xcoeff, ycoeff, concoeff)
54 |
55 | xcoeff /= h
56 | ycoeff /= h
57 | concoeff /= h
58 |
59 | # Tidying it up for prettier printing
60 | if xcoeff < 0
61 | xcoeff *= -1
62 | ycoeff *= -1
63 | concoeff *= -1
64 |
65 | if xcoeff ~= 0 and ycoeff < 0
66 | ycoeff *= -1
67 | concoeff *= -1
68 |
69 | # x-term
70 | if xcoeff ~= 1
71 | eqString = "x"
72 | else if xcoeff != 0
73 | eqString = xcoeff+"x"
74 |
75 | # y-term
76 | if xcoeff ~= 0
77 | if ycoeff ~= 1
78 | eqString = "y"
79 | else if ycoeff ~= -1
80 | eqString = "-y"
81 | else
82 | eqString = ycoeff+"y"
83 | else
84 | if ycoeff ~= 1
85 | eqString += "+y"
86 | else if ycoeff ~= -1
87 | eqString += "-y"
88 | else if ycoeff != 0
89 | eqString += signedNumber(ycoeff)+"y"
90 |
91 | if concoeff is not 0
92 | eqString += signedNumber(concoeff) + "=0"
93 | else
94 | eqString += "=0"
95 |
96 | return eqString
97 |
98 | # Given a gradient and an intercept, return a
99 | # LaTeX-formatted equation in the form
100 | # y = mx + c
101 | lineEq2 = (m,c) ->
102 |
103 | eqString = "y="
104 |
105 | if m ~= -1
106 | eqString += "-x"
107 | else if m ~= 1
108 | eqString += "x"
109 | else if m != 0
110 | eqString += m+"x"
111 |
112 | if c != 0
113 | if m ~= 0
114 | eqString += c
115 | else
116 | eqString += signedNumber(c)
117 |
118 | return eqString
119 |
--------------------------------------------------------------------------------
/src/scripts/pubs/todo/main.ls:
--------------------------------------------------------------------------------
1 | require {
2 | shim:
3 | 'controllers/appController':
4 | deps:
5 | * 'app'
6 | ...
7 | 'controllers/todoController':
8 | deps:
9 | * 'app'
10 | ...
11 | 'pubs/frogs/controllers/frogController':
12 | deps:
13 | * 'app'
14 | ...
15 | 'pubs/boomerangs/controllers/boomerangController':
16 | deps:
17 | * 'app'
18 | ...
19 | 'pubs/probability/controllers/sampleSpinController':
20 | deps:
21 | * 'app'
22 | 'libs/d3.v3'
23 | 'directives/svgCheck'
24 | 'pubs/probability/controllers/spinGroupController':
25 | deps:
26 | * 'app'
27 | 'libs/d3.v3'
28 | 'directives/svgCheck'
29 | 'pubs/probability/controllers/prob9546ResultsController':
30 | deps:
31 | * 'app'
32 | 'libs/d3.v3'
33 | 'directives/svgCheck'
34 | 'pubs/frogs/directives/frogs':
35 | deps:
36 | * 'app'
37 | ...
38 | 'pubs/frogs/directives/frog':
39 | deps:
40 | * 'app'
41 | ...
42 | 'pubs/probability/directives/d3Spinner':
43 | deps:
44 | * 'app'
45 | 'directives/d3Vis'
46 | 'libs/d3.v3'
47 | 'directives/d3Vis':
48 | deps:
49 | * 'app'
50 | 'libs/d3.v3'
51 | 'directives/svgCheck'
52 | 'directives/d3DotGrid':
53 | deps:
54 | * 'app'
55 | 'directives/d3Vis'
56 | 'pubs/tilted/directives/d3TiltedSquare':
57 | deps:
58 | * 'app'
59 | 'directives/d3DotGrid'
60 | 'directives/svgCheck':
61 | deps:
62 | * 'app'
63 | 'libs/d3.v3'
64 | 'directives/appVersion':
65 | deps:
66 | * 'app'
67 | 'services/semver'
68 | 'services/semver':
69 | deps:
70 | * 'app'
71 | ...
72 | 'bootstrap':
73 | deps:
74 | * 'app'
75 | ...
76 | 'libs/bootstrap':
77 | deps:
78 | * 'libs/jquery'
79 | ...
80 | 'libs/angular-resource':
81 | deps:
82 | * 'libs/angular'
83 | ...
84 | 'libs/ui-bootstrap-tpls':
85 | deps:
86 | * 'libs/angular'
87 | ...
88 | 'app':
89 | deps:
90 | * 'libs/angular'
91 | 'libs/angular-resource'
92 | 'libs/ui-bootstrap-tpls'
93 | 'routes':
94 | deps:
95 | * 'app'
96 | ...
97 | 'views':
98 | deps:
99 | * 'app'
100 | ...
101 | },
102 | * 'require'
103 | 'controllers/appController'
104 | 'pubs/todo/controllers/todoController'
105 | 'directives/appVersion'
106 | 'routes'
107 | 'views'
108 | , (require) -> require ['bootstrap']
--------------------------------------------------------------------------------
/test/mathmoTestsTodo/spec/problems.js:
--------------------------------------------------------------------------------
1 | beforeEach(function() {
2 | this.addMatchers({
3 | nonEmpty: function() {
4 | return this.actual.length > 0;
5 | },
6 | syntaxCorrect: function() {
7 | var pieces = this.actual.split('$$');
8 |
9 | // check the LaTeX is properly terminated
10 | if((pieces.length % 2) != 1)
11 | return false;
12 |
13 | // in each LaTeX piece, check curly braces match
14 | for(var i = 1; i < pieces.length; i += 2)
15 | {
16 | var s = pieces[i];
17 | var nesting = 0;
18 |
19 | for(var j = 0; j < s.length; j++)
20 | {
21 | switch(s.charAt(j))
22 | {
23 | case '{':
24 | nesting += 1;
25 | break;
26 | case '}':
27 | nesting -= 1;
28 | if(nesting < 0)
29 | return false;
30 | break;
31 | default:
32 | break;
33 | }
34 | }
35 |
36 | if(nesting != 0)
37 | return false;
38 | }
39 |
40 | return true;
41 | }
42 | });
43 | });
44 |
45 | describe("LaTeX syntax", function() {
46 | // a list of functions that are supposed to produce pairs [q,a]
47 | // such that q and a both pass the syntax check
48 | var makers = [
49 | 'makePartial',
50 | 'makeBinomial2',
51 | 'makePolyInt',
52 | 'makeTrigInt',
53 | 'makeVector',
54 | 'makeLines',
55 | 'makeIneq',
56 | 'makeAP',
57 | 'makeFactor',
58 | 'makeQuadratic',
59 | 'makeComplete',
60 | 'makeBinExp',
61 | 'makeLog',
62 | 'makeStationary',
63 | 'makeTriangle',
64 | 'makeCircle',
65 | 'makeSolvingTrig',
66 | 'makeVectorEq',
67 | 'makeImplicit',
68 | 'makeChainRule',
69 | 'makeProductRule',
70 | 'makeQuotientRule',
71 | 'makeGP',
72 | 'makeModulus',
73 | 'makeTransformation',
74 | 'makeComposition',
75 | 'makeParametric',
76 | 'makeImplicitFunction',
77 | 'makeIntegration',
78 | 'makeDE',
79 | 'makePowers',
80 | 'makeCArithmetic',
81 | 'makeCPolar',
82 | 'makeDETwoHard',
83 | 'makeMatrix2',
84 | 'makeTaylor',
85 | 'makePolarSketch',
86 | 'makeMatrix3',
87 | 'makeFurtherVector',
88 | 'makeNewtonRaphson',
89 | 'makeFurtherIneq',
90 | 'makeSubstInt',
91 | 'makeRevolution',
92 | 'makeMatXforms',
93 | 'makeDiscreteDistn',
94 | 'makeContinDistn',
95 | 'makeHypTest',
96 | 'makeConfidInt',
97 | 'makeChiSquare',
98 | 'makeProductMoment'
99 | ];
100 |
101 | for(var k = 0; k < makers.length; k++)
102 | {
103 | describe(makers[k], function() {
104 | var qa;
105 | var i = k; // save the value of k in the closure
106 |
107 | beforeEach(function() {
108 | qa = window[makers[i]]();
109 | });
110 |
111 | it("should produce correct LaTeX", function() {
112 | expect(qa[0]).syntaxCorrect();
113 | expect(qa[1]).syntaxCorrect();
114 | });
115 | });
116 | }
117 | });
118 |
--------------------------------------------------------------------------------
/src/scripts/services/d3LineChart.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').factory 'd3LineChart', ->
2 | lineChart = ->
3 | chart = (selection) ->
4 | selection.each (data) ->
5 |
6 | # Convert data to standard representation greedily;
7 | # this is needed for nondeterministic accessors.
8 | data = data.map (d, i) -> [xValue.call(data, d, i), yValue.call(data, d, i)]
9 |
10 | # Update the x-scale.
11 | xScale.domain(d3.extent(data, (d) -> d[0])).range [0, width]
12 |
13 | # Update the y-scale.
14 | yScale.domain(d3.extent(data, (d) -> d[1])).range [height, 0]
15 |
16 | # Select the svg element, if it exists.
17 | gplot = d3.select(this).selectAll("g.plot")
18 | .data([data])
19 | .attr "width", width
20 | .attr "height", height
21 |
22 | gPlotEnter = gplot.enter().append("g").attr "class", "plot"
23 |
24 | # Otherwise, create the skeletal chart.
25 | #gEnter = gPlotEnter.append("g")
26 | gPlotEnter.append("path").attr "class", "line"
27 | gPlotEnter.append("g").attr "class", "x axis"
28 | gPlotEnter.append("g").attr "class", "y axis"
29 |
30 | # Update the line path.
31 | gplot.select(".line").attr "d", line
32 |
33 | # Update the axes.
34 | gplot.select(".x.axis").attr("transform", "translate(0," + yScale.range()[0] + ")").call xAxis
35 | gplot.select(".y.axis").attr("transform", "translate(" + xScale.range()[0] + ", 0)").call yAxis
36 |
37 |
38 | # The x-accessor for the path generator; xScale ∘ xValue.
39 | X = (d) -> xScale d[0]
40 |
41 | # The x-accessor for the path generator; yScale ∘ yValue.
42 | Y = (d) -> yScale d[1]
43 |
44 | width = 300
45 | height = 250
46 | xValue = (d) -> d[0]
47 | yValue = (d) -> d[1]
48 | xScale = d3.scale.linear()
49 | yScale = d3.scale.linear()
50 | xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(6, 0)
51 | yAxis = d3.svg.axis().scale(yScale).orient("left").tickSize(6,0)
52 |
53 | # Note that isNaN(null) is weirdly false in javascript.
54 | line = d3.svg.line()
55 | .x(X)
56 | .y(Y)
57 | .defined (d) -> !(d[0]==null or d[1]==null or isNaN(d[0]) or isNaN(d[1]))
58 |
59 | chart.margin = (_) ->
60 | return margin unless arguments.length
61 | margin = _
62 | chart
63 |
64 | chart.width = (_) ->
65 | return width unless arguments.length
66 | width := _
67 | chart
68 |
69 | chart.height = (_) ->
70 | return height unless arguments.length
71 | height := _
72 | chart
73 |
74 | chart.x = (_) ->
75 | return xValue unless arguments.length
76 | xValue := _
77 | chart
78 |
79 | chart.y = (_) ->
80 | return yValue unless arguments.length
81 | yValue := _
82 | chart
83 |
84 | chart
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Mathmo
7 |
8 |
9 |
10 |
11 |
12 |
13 | <% if (config.environment === 'prod') { %>
14 |
15 |
16 | <% } else { %>
17 |
18 | <% } %>
19 |
20 |
23 |
24 |
26 |
27 |
28 |
29 |
30 |
31 | Mathmo interactive A-level workbook
32 | Mathmo generates an endless number of A-level mathematics questions for the UK A-level mathematics syllabus which you can use to improve your fluency.
33 |
34 |
35 |
52 |
57 | <% if (config.environment === 'prod') { %>
58 |
59 | <% } else { %>
60 |
61 | <% } %>
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/views/spinners.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Spinner support for probability experiments.
4 |
Spinners provide a good screen-friendly way to simulate throwing dice,
5 | spinning a coin, or choosing things from a bag.
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
26 |
40 |
Go
41 |
42 |
43 |
To do:
44 |
45 | n colour spinner with configurable probabilities.
46 |
47 | Denomimator delineated by numbered sectors.
48 | result bound to model.
49 | Adjustable radius
50 | Fast forward
51 |
52 |
53 | n colour bag with configurable probabilities.
54 |
55 | Result is removed from bag
56 | Optional animated replacement
57 |
58 |
59 | One-arm-bandit
60 | Stackable horizontally, vertical spin
61 | Buttons
62 |
63 | Go
64 | Replace and Go butt
65 | Hold
66 |
67 | Results
68 |
69 | Individual Result Display
70 | Tabular Result Displays
71 |
72 |
73 | Pack of cards
74 |
75 | shuffle - zoom out, distribute, reassemble
76 | turn over
77 | deal card to hand
78 | deal card from hand
79 | stack
80 | sort
81 |
82 |
83 | Tree diagram.
84 |
85 |
--------------------------------------------------------------------------------
/test/mathmoTestsTodo/lib/jasmine-1.0.2/jasmine.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
3 | }
4 |
5 |
6 | .jasmine_reporter a:visited, .jasmine_reporter a {
7 | color: #303;
8 | }
9 |
10 | .jasmine_reporter a:hover, .jasmine_reporter a:active {
11 | color: blue;
12 | }
13 |
14 | .run_spec {
15 | float:right;
16 | padding-right: 5px;
17 | font-size: .8em;
18 | text-decoration: none;
19 | }
20 |
21 | .jasmine_reporter {
22 | margin: 0 5px;
23 | }
24 |
25 | .banner {
26 | color: #303;
27 | background-color: #fef;
28 | padding: 5px;
29 | }
30 |
31 | .logo {
32 | float: left;
33 | font-size: 1.1em;
34 | padding-left: 5px;
35 | }
36 |
37 | .logo .version {
38 | font-size: .6em;
39 | padding-left: 1em;
40 | }
41 |
42 | .runner.running {
43 | background-color: yellow;
44 | }
45 |
46 |
47 | .options {
48 | text-align: right;
49 | font-size: .8em;
50 | }
51 |
52 |
53 |
54 |
55 | .suite {
56 | border: 1px outset gray;
57 | margin: 5px 0;
58 | padding-left: 1em;
59 | }
60 |
61 | .suite .suite {
62 | margin: 5px;
63 | }
64 |
65 | .suite.passed {
66 | background-color: #dfd;
67 | }
68 |
69 | .suite.failed {
70 | background-color: #fdd;
71 | }
72 |
73 | .spec {
74 | margin: 5px;
75 | padding-left: 1em;
76 | clear: both;
77 | }
78 |
79 | .spec.failed, .spec.passed, .spec.skipped {
80 | padding-bottom: 5px;
81 | border: 1px solid gray;
82 | }
83 |
84 | .spec.failed {
85 | background-color: #fbb;
86 | border-color: red;
87 | }
88 |
89 | .spec.passed {
90 | background-color: #bfb;
91 | border-color: green;
92 | }
93 |
94 | .spec.skipped {
95 | background-color: #bbb;
96 | }
97 |
98 | .messages {
99 | border-left: 1px dashed gray;
100 | padding-left: 1em;
101 | padding-right: 1em;
102 | }
103 |
104 | .passed {
105 | background-color: #cfc;
106 | display: none;
107 | }
108 |
109 | .failed {
110 | background-color: #fbb;
111 | }
112 |
113 | .skipped {
114 | color: #777;
115 | background-color: #eee;
116 | display: none;
117 | }
118 |
119 |
120 | /*.resultMessage {*/
121 | /*white-space: pre;*/
122 | /*}*/
123 |
124 | .resultMessage span.result {
125 | display: block;
126 | line-height: 2em;
127 | color: black;
128 | }
129 |
130 | .resultMessage .mismatch {
131 | color: black;
132 | }
133 |
134 | .stackTrace {
135 | white-space: pre;
136 | font-size: .8em;
137 | margin-left: 10px;
138 | max-height: 5em;
139 | overflow: auto;
140 | border: 1px inset red;
141 | padding: 1em;
142 | background: #eef;
143 | }
144 |
145 | .finished-at {
146 | padding-left: 1em;
147 | font-size: .6em;
148 | }
149 |
150 | .show-passed .passed,
151 | .show-skipped .skipped {
152 | display: block;
153 | }
154 |
155 |
156 | #jasmine_content {
157 | position:fixed;
158 | right: 100%;
159 | }
160 |
161 | .runner {
162 | border: 1px solid gray;
163 | display: block;
164 | margin: 5px 0;
165 | padding: 2px 0 2px 10px;
166 | }
167 |
--------------------------------------------------------------------------------
/test/mathmoTestsTodo/spec/fractions.js:
--------------------------------------------------------------------------------
1 | beforeEach(function() {
2 | this.addMatchers({
3 | equals: function(other) {
4 | return this.actual.equals(other)
5 | }
6 | });
7 | });
8 |
9 | describe("frac", function() {
10 | var a;
11 |
12 | beforeEach(function() {
13 | a = randfrac(20);
14 | });
15 |
16 | it("should be in lowest terms", function() {
17 | expect(gcd(a.top, a.bot)).toEqual(1);
18 | });
19 |
20 | it("should equal itself", function() {
21 | expect(a).equals(a);
22 | });
23 |
24 | it("should equal its clone", function() {
25 | expect(a).equals(a.clone());
26 | });
27 |
28 | it("should not equal its clone plus 1", function() {
29 | expect(a).not.equals(a.clone().add(1,1))
30 | });
31 | });
32 |
33 | describe("fmatrix", function() {
34 | var one, zero; // fractions
35 | var eye; // identity
36 | var a, b; // random matrices
37 | var s; // a singular matrix
38 |
39 | beforeEach(function () {
40 | one = new frac(1,1);
41 | zero = new frac(0,1);
42 |
43 | eye = new fmatrix(3);
44 | eye.set(one,zero,zero,zero,one,zero,zero,zero,one);
45 |
46 | a = new fmatrix(3);
47 | b = new fmatrix(3);
48 | a.setrand(4);
49 | b.setrand(4);
50 |
51 | var i, p = randfrac(5), q = randfrac(5);
52 | s = new fmatrix(3);
53 | s.setrand(4);
54 | // make the first column a combination of the others
55 | for(i = 0; i < 3; i++)
56 | {
57 | var r = s[i][1].clone().prod(p);
58 | s[i][0] = s[i][2].clone().prod(q).add(r.top, r.bot);
59 | }
60 | });
61 |
62 | describe("identity matrix", function() {
63 | it("should have trace = dim", function() {
64 | var df = new frac(eye.dim);
65 | expect(eye.tr()).equals(df);
66 | });
67 |
68 | it("should have det = 1", function() {
69 | expect(eye.det()).equals(one);
70 | });
71 | });
72 |
73 | describe("determinant", function() {
74 | it("should be a homomorphism", function() {
75 | var c = b.times(a);
76 | expect(c.det()).equals(b.det().prod(a.det()));
77 | });
78 |
79 | it("should be 0 on singular matrices", function() {
80 | expect(s.det()).equals(zero);
81 | });
82 |
83 | it("should be antisymmetric", function() {
84 | var c = a.clone();
85 | // swap a random pair of columns
86 | var cols = distrand(2, 0, c.dim-1);
87 | for(i = 0; i < c.dim; i++)
88 | {
89 | var t = c[i][cols[0]];
90 | c[i][cols[0]] = c[i][cols[1]];
91 | c[i][cols[1]] = t;
92 | }
93 | expect(c.det().prod(-1)).equals(a.det());
94 | });
95 |
96 | it("should be multilinear", function() {
97 | var c = a.clone();
98 | // multiply a random column by a random constant
99 | var p = randfrac(5);
100 | var col = rand(0, c.dim-1);
101 | for(var i = 0; i < c.dim; i++)
102 | c[i][col].prod(p);
103 | expect(c.det()).equals(a.det().prod(p));
104 | });
105 | });
106 |
107 | describe("transpose", function() {
108 | var t;
109 |
110 | beforeEach(function () {
111 | t = a.T();
112 | });
113 |
114 | it("should have the same det+trace", function() {
115 | expect(t.det()).equals(a.det());
116 | expect(t.tr()).equals(a.tr());
117 | });
118 | });
119 | });
120 |
--------------------------------------------------------------------------------
/src/scripts/pubs/probability/main.ls:
--------------------------------------------------------------------------------
1 | require {
2 | shim:
3 | 'controllers/appController':
4 | deps:
5 | * 'app'
6 | ...
7 | 'controllers/todoController':
8 | deps:
9 | * 'app'
10 | ...
11 | 'pubs/frogs/controllers/frogController':
12 | deps:
13 | * 'app'
14 | ...
15 | 'pubs/boomerangs/controllers/boomerangController':
16 | deps:
17 | * 'app'
18 | ...
19 | 'pubs/probability/controllers/sampleSpinController':
20 | deps:
21 | * 'app'
22 | 'libs/d3.v3'
23 | 'directives/svgCheck'
24 | 'pubs/probability/controllers/spinGroupController':
25 | deps:
26 | * 'app'
27 | 'libs/d3.v3'
28 | 'directives/svgCheck'
29 | 'pubs/probability/controllers/prob9546ResultsController':
30 | deps:
31 | * 'app'
32 | 'libs/d3.v3'
33 | 'directives/svgCheck'
34 | 'pubs/probability/controllers/prob9525ResultsController':
35 | deps:
36 | * 'app'
37 | 'libs/d3.v3'
38 | 'directives/svgCheck'
39 | 'pubs/frogs/directives/frogs':
40 | deps:
41 | * 'app'
42 | ...
43 | 'pubs/frogs/directives/frog':
44 | deps:
45 | * 'app'
46 | ...
47 | 'pubs/probability/directives/d3Spinner':
48 | deps:
49 | * 'app'
50 | 'directives/d3Vis'
51 | 'libs/d3.v3'
52 | 'directives/d3Vis':
53 | deps:
54 | * 'app'
55 | 'libs/d3.v3'
56 | 'directives/svgCheck'
57 | 'directives/d3DotGrid':
58 | deps:
59 | * 'app'
60 | 'directives/d3Vis'
61 | 'pubs/tilted/directives/d3TiltedSquare':
62 | deps:
63 | * 'app'
64 | 'directives/d3DotGrid'
65 | 'directives/svgCheck':
66 | deps:
67 | * 'app'
68 | 'libs/d3.v3'
69 | 'directives/appVersion':
70 | deps:
71 | * 'app'
72 | 'services/semver'
73 | 'services/semver':
74 | deps:
75 | * 'app'
76 | ...
77 | 'bootstrap':
78 | deps:
79 | * 'app'
80 | ...
81 | 'libs/bootstrap':
82 | deps:
83 | * 'libs/jquery'
84 | ...
85 | 'libs/angular-resource':
86 | deps:
87 | * 'libs/angular'
88 | ...
89 | 'libs/ui-bootstrap-tpls':
90 | deps:
91 | * 'libs/angular'
92 | ...
93 | 'app':
94 | deps:
95 | * 'libs/angular'
96 | 'libs/angular-resource'
97 | 'libs/ui-bootstrap-tpls'
98 | 'routes':
99 | deps:
100 | * 'app'
101 | ...
102 | 'views':
103 | deps:
104 | * 'app'
105 | ...
106 | },
107 | * 'require'
108 | #'libs/prelude-browser-min'
109 | 'controllers/appController'
110 | #'pubs/probability/controllers/sampleSpinController'
111 | 'pubs/probability/controllers/spinGroupController'
112 | 'pubs/probability/controllers/prob9546ResultsController'
113 | 'pubs/probability/controllers/prob9525ResultsController'
114 | 'pubs/probability/directives/d3Spinner'
115 | 'directives/appVersion'
116 | 'routes'
117 | 'views'
118 | , (require) -> require ['bootstrap']
--------------------------------------------------------------------------------
/src/styles/pagination.less:
--------------------------------------------------------------------------------
1 | //
2 | // Pagination (multiple pages)
3 | // --------------------------------------------------
4 |
5 | // Space out pagination from surrounding content
6 | .pagination {
7 | margin: @baseLineHeight 0;
8 | }
9 |
10 | .pagination ul {
11 | // Allow for text-based alignment
12 | display: inline-block;
13 | .ie7-inline-block();
14 | // Reset default ul styles
15 | margin-left: 0;
16 | margin-bottom: 0;
17 | // Visuals
18 | .border-radius(@baseBorderRadius);
19 | .box-shadow(0 1px 2px rgba(0,0,0,.05));
20 | }
21 | .pagination ul > li {
22 | display: inline; // Remove list-style and block-level defaults
23 | }
24 | .pagination ul > li > a,
25 | .pagination ul > li > span {
26 | float: left; // Collapse white-space
27 | padding: 4px 12px;
28 | line-height: @baseLineHeight;
29 | text-decoration: none;
30 | background-color: @paginationBackground;
31 | border: 1px solid @paginationBorder;
32 | border-left-width: 0;
33 | }
34 | .pagination ul > li > a:hover,
35 | .pagination ul > li > a:focus,
36 | .pagination ul > .active > a,
37 | .pagination ul > .active > span {
38 | background-color: @paginationActiveBackground;
39 | }
40 | .pagination ul > .active > a,
41 | .pagination ul > .active > span {
42 | color: @grayLight;
43 | cursor: default;
44 | }
45 | .pagination ul > .disabled > span,
46 | .pagination ul > .disabled > a,
47 | .pagination ul > .disabled > a:hover,
48 | .pagination ul > .disabled > a:focus {
49 | color: @grayLight;
50 | background-color: transparent;
51 | cursor: default;
52 | }
53 | .pagination ul > li:first-child > a,
54 | .pagination ul > li:first-child > span {
55 | border-left-width: 1px;
56 | .border-left-radius(@baseBorderRadius);
57 | }
58 | .pagination ul > li:last-child > a,
59 | .pagination ul > li:last-child > span {
60 | .border-right-radius(@baseBorderRadius);
61 | }
62 |
63 |
64 | // Alignment
65 | // --------------------------------------------------
66 |
67 | .pagination-centered {
68 | text-align: center;
69 | }
70 | .pagination-right {
71 | text-align: right;
72 | }
73 |
74 |
75 | // Sizing
76 | // --------------------------------------------------
77 |
78 | // Large
79 | .pagination-large {
80 | ul > li > a,
81 | ul > li > span {
82 | padding: @paddingLarge;
83 | font-size: @fontSizeLarge;
84 | }
85 | ul > li:first-child > a,
86 | ul > li:first-child > span {
87 | .border-left-radius(@borderRadiusLarge);
88 | }
89 | ul > li:last-child > a,
90 | ul > li:last-child > span {
91 | .border-right-radius(@borderRadiusLarge);
92 | }
93 | }
94 |
95 | // Small and mini
96 | .pagination-mini,
97 | .pagination-small {
98 | ul > li:first-child > a,
99 | ul > li:first-child > span {
100 | .border-left-radius(@borderRadiusSmall);
101 | }
102 | ul > li:last-child > a,
103 | ul > li:last-child > span {
104 | .border-right-radius(@borderRadiusSmall);
105 | }
106 | }
107 |
108 | // Small
109 | .pagination-small {
110 | ul > li > a,
111 | ul > li > span {
112 | padding: @paddingSmall;
113 | font-size: @fontSizeSmall;
114 | }
115 | }
116 | // Mini
117 | .pagination-mini {
118 | ul > li > a,
119 | ul > li > span {
120 | padding: @paddingMini;
121 | font-size: @fontSizeMini;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/styles/carousel.less:
--------------------------------------------------------------------------------
1 | //
2 | // Carousel
3 | // --------------------------------------------------
4 |
5 |
6 | .carousel {
7 | position: relative;
8 | margin-bottom: @baseLineHeight;
9 | line-height: 1;
10 | }
11 |
12 | .carousel-inner {
13 | overflow: hidden;
14 | width: 100%;
15 | position: relative;
16 | }
17 |
18 | .carousel-inner {
19 |
20 | > .item {
21 | display: none;
22 | position: relative;
23 | .transition(.6s ease-in-out left);
24 |
25 | // Account for jankitude on images
26 | > img,
27 | > a > img {
28 | display: block;
29 | line-height: 1;
30 | }
31 | }
32 |
33 | > .active,
34 | > .next,
35 | > .prev { display: block; }
36 |
37 | > .active {
38 | left: 0;
39 | }
40 |
41 | > .next,
42 | > .prev {
43 | position: absolute;
44 | top: 0;
45 | width: 100%;
46 | }
47 |
48 | > .next {
49 | left: 100%;
50 | }
51 | > .prev {
52 | left: -100%;
53 | }
54 | > .next.left,
55 | > .prev.right {
56 | left: 0;
57 | }
58 |
59 | > .active.left {
60 | left: -100%;
61 | }
62 | > .active.right {
63 | left: 100%;
64 | }
65 |
66 | }
67 |
68 | // Left/right controls for nav
69 | // ---------------------------
70 |
71 | .carousel-control {
72 | position: absolute;
73 | top: 40%;
74 | left: 15px;
75 | width: 40px;
76 | height: 40px;
77 | margin-top: -20px;
78 | font-size: 60px;
79 | font-weight: 100;
80 | line-height: 30px;
81 | color: @white;
82 | text-align: center;
83 | background: @grayDarker;
84 | border: 3px solid @white;
85 | .border-radius(23px);
86 | .opacity(50);
87 |
88 | // we can't have this transition here
89 | // because webkit cancels the carousel
90 | // animation if you trip this while
91 | // in the middle of another animation
92 | // ;_;
93 | // .transition(opacity .2s linear);
94 |
95 | // Reposition the right one
96 | &.right {
97 | left: auto;
98 | right: 15px;
99 | }
100 |
101 | // Hover/focus state
102 | &:hover,
103 | &:focus {
104 | color: @white;
105 | text-decoration: none;
106 | .opacity(90);
107 | }
108 | }
109 |
110 | // Carousel indicator pips
111 | // -----------------------------
112 | .carousel-indicators {
113 | position: absolute;
114 | top: 15px;
115 | right: 15px;
116 | z-index: 5;
117 | margin: 0;
118 | list-style: none;
119 |
120 | li {
121 | display: block;
122 | float: left;
123 | width: 10px;
124 | height: 10px;
125 | margin-left: 5px;
126 | text-indent: -999px;
127 | background-color: #ccc;
128 | background-color: rgba(255,255,255,.25);
129 | border-radius: 5px;
130 | }
131 | .active {
132 | background-color: #fff;
133 | }
134 | }
135 |
136 | // Caption for text below images
137 | // -----------------------------
138 |
139 | .carousel-caption {
140 | position: absolute;
141 | left: 0;
142 | right: 0;
143 | bottom: 0;
144 | padding: 15px;
145 | background: @grayDark;
146 | background: rgba(0,0,0,.75);
147 | }
148 | .carousel-caption h4,
149 | .carousel-caption p {
150 | color: @white;
151 | line-height: @baseLineHeight;
152 | }
153 | .carousel-caption h4 {
154 | margin: 0 0 5px;
155 | }
156 | .carousel-caption p {
157 | margin-bottom: 0;
158 | }
159 |
--------------------------------------------------------------------------------
/src/views/frogs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
14 |
15 |
16 |
21 |
22 |
26 |
27 |
You have made {{ moves.list.length }} moves.
28 |
29 |
You could have swapped the frogs in fewer moves. Have another go.
30 |
Well done, you have swapped all the frogs in the minimum number of moves!
31 |
32 |
33 |
34 |
35 | Reset
36 |
37 |
38 | Replay
39 |
40 |
41 |
42 |
43 |
44 |
45 |
Replay list
46 |
47 |
48 |
49 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Name
61 | Frogs
62 | Moves
63 |
64 |
65 |
66 |
67 |
68 |
69 | {{saved.tag}}
70 |
71 |
72 | {{saved.red}},{{saved.blue}}
73 |
74 |
75 | {{saved.list.length}}
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/src/styles/progress-bars.less:
--------------------------------------------------------------------------------
1 | //
2 | // Progress bars
3 | // --------------------------------------------------
4 |
5 |
6 | // ANIMATIONS
7 | // ----------
8 |
9 | // Webkit
10 | @-webkit-keyframes progress-bar-stripes {
11 | from { background-position: 40px 0; }
12 | to { background-position: 0 0; }
13 | }
14 |
15 | // Firefox
16 | @-moz-keyframes progress-bar-stripes {
17 | from { background-position: 40px 0; }
18 | to { background-position: 0 0; }
19 | }
20 |
21 | // IE9
22 | @-ms-keyframes progress-bar-stripes {
23 | from { background-position: 40px 0; }
24 | to { background-position: 0 0; }
25 | }
26 |
27 | // Opera
28 | @-o-keyframes progress-bar-stripes {
29 | from { background-position: 0 0; }
30 | to { background-position: 40px 0; }
31 | }
32 |
33 | // Spec
34 | @keyframes progress-bar-stripes {
35 | from { background-position: 40px 0; }
36 | to { background-position: 0 0; }
37 | }
38 |
39 |
40 |
41 | // THE BARS
42 | // --------
43 |
44 | // Outer container
45 | .progress {
46 | overflow: hidden;
47 | height: @baseLineHeight;
48 | margin-bottom: @baseLineHeight;
49 | #gradient > .vertical(#f5f5f5, #f9f9f9);
50 | .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));
51 | .border-radius(@baseBorderRadius);
52 | }
53 |
54 | // Bar of progress
55 | .progress .bar {
56 | width: 0%;
57 | height: 100%;
58 | color: @white;
59 | float: left;
60 | font-size: 12px;
61 | text-align: center;
62 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
63 | #gradient > .vertical(#149bdf, #0480be);
64 | .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));
65 | .box-sizing(border-box);
66 | .transition(width .6s ease);
67 | }
68 | .progress .bar + .bar {
69 | .box-shadow(~"inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15)");
70 | }
71 |
72 | // Striped bars
73 | .progress-striped .bar {
74 | #gradient > .striped(#149bdf);
75 | .background-size(40px 40px);
76 | }
77 |
78 | // Call animation for the active one
79 | .progress.active .bar {
80 | -webkit-animation: progress-bar-stripes 2s linear infinite;
81 | -moz-animation: progress-bar-stripes 2s linear infinite;
82 | -ms-animation: progress-bar-stripes 2s linear infinite;
83 | -o-animation: progress-bar-stripes 2s linear infinite;
84 | animation: progress-bar-stripes 2s linear infinite;
85 | }
86 |
87 |
88 |
89 | // COLORS
90 | // ------
91 |
92 | // Danger (red)
93 | .progress-danger .bar, .progress .bar-danger {
94 | #gradient > .vertical(#ee5f5b, #c43c35);
95 | }
96 | .progress-danger.progress-striped .bar, .progress-striped .bar-danger {
97 | #gradient > .striped(#ee5f5b);
98 | }
99 |
100 | // Success (green)
101 | .progress-success .bar, .progress .bar-success {
102 | #gradient > .vertical(#62c462, #57a957);
103 | }
104 | .progress-success.progress-striped .bar, .progress-striped .bar-success {
105 | #gradient > .striped(#62c462);
106 | }
107 |
108 | // Info (teal)
109 | .progress-info .bar, .progress .bar-info {
110 | #gradient > .vertical(#5bc0de, #339bb9);
111 | }
112 | .progress-info.progress-striped .bar, .progress-striped .bar-info {
113 | #gradient > .striped(#5bc0de);
114 | }
115 |
116 | // Warning (orange)
117 | .progress-warning .bar, .progress .bar-warning {
118 | #gradient > .vertical(lighten(@orange, 15%), @orange);
119 | }
120 | .progress-warning.progress-striped .bar, .progress-striped .bar-warning {
121 | #gradient > .striped(lighten(@orange, 15%));
122 | }
123 |
--------------------------------------------------------------------------------
/src/scripts/pubs/probability/controllers/prob9546ResultsController.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').controller 'prob9546ResultsController', [
2 | '$scope'
3 | ($scope) ->
4 | console.log "results controller scope = #{$scope.$id}"
5 |
6 | $scope.spinnerConfigs =
7 | triggers:
8 | goal1:
9 | "Y": \goal2
10 | "B": \goal2
11 |
12 | goal1: # an array of spinner configurations
13 | * "label": \Y
14 | "weight": 10
15 | "fill" : \#fe2
16 | * "label": \Y
17 | "weight": 10
18 | "fill" : \#fe2
19 | * "label": \Y
20 | "weight": 10
21 | "fill" : \#fe2
22 | * "label": \Y
23 | "weight": 10
24 | "fill" : \#fe2
25 | * "label": \B
26 | "weight": 10
27 | "fill" : \#6af
28 | * "label": \B
29 | "weight": 10
30 | "fill" : \#4af
31 |
32 | goal2:
33 | * "label": \Y
34 | "weight": 10
35 | "fill" : \#fe2
36 | * "label": \Y
37 | "weight": 10
38 | "fill" : \#fe2
39 | * "label": \Y
40 | "weight": 10
41 | "fill" : \#fe2
42 | * "label": \Y
43 | "weight": 10
44 | "fill" : \#fe2
45 | * "label": \B
46 | "weight": 10
47 | "fill" : \#6af
48 | * "label": \B
49 | "weight": 10
50 | "fill" : \#4af
51 |
52 | $scope.model =
53 | goal1: 0
54 | goal2: 0
55 |
56 | $scope.runOptions =
57 | repeats: 1
58 | delay: 1000
59 | parallel: false
60 | spinners: [ \goal1, \goal2 ]
61 | resultHandler: ->
62 | console.log "resultHandled"
63 |
64 | $scope.summary = {}
65 | $scope.matches = []
66 | goalScorers = {}
67 |
68 | reset = ->
69 | $scope.summary :=
70 | wonByB:0
71 | wonByY:0
72 | drawYB:0
73 | drawBY:0
74 | $scope.matches := []
75 | goalScorers :=
76 | goal1: []
77 | goal2: []
78 | $scope.model =
79 | goal1: 0
80 | goal2: 0
81 |
82 | $scope.$on "resetSpinners", reset
83 |
84 | # initial view setup
85 | reset()
86 |
87 | drawDiscriminator = (goal1Scorer) ->
88 | s = $scope.summary
89 | if goal1Scorer == \Y
90 | s.drawYB++
91 | return "draw YB"
92 | else
93 | s.drawBY++
94 | return "draw BY"
95 |
96 | # view update
97 | logMatch = (goal1Scorer, goal2Scorer) ->
98 | yGoals = +(goal1Scorer == \Y) + +(goal2Scorer == \Y)
99 | bGoals = 2 - yGoals
100 | s = $scope.summary
101 | number = $scope.matches.length + 1
102 | $scope.matches.unshift {
103 | number: number
104 | goal1: goal1Scorer
105 | goal2: goal2Scorer
106 | score: "Y: #yGoals, B: #bGoals"
107 | result: switch yGoals
108 | | 0 => s.wonByB++; "B"
109 | | 1 => drawDiscriminator(goal1Scorer)
110 | | 2 => s.wonByY++; "Y"
111 | }
112 |
113 | # watch for single spinner settle events
114 | $scope.$on "spinDone", (event, name, sectorIndex, label) ->
115 | event.stopPropagation()
116 | goalScorers[name].push label
117 |
118 | # and all spinners finished
119 | $scope.$on "spinGroupDone", (event) ->
120 | event.stopPropagation()
121 |
122 | # update the view with this result
123 | lastx = goalScorers.goal1.length - 1
124 | if lastx == goalScorers.goal2.length - 1
125 | logMatch goalScorers.goal1[lastx], goalScorers.goal2[lastx]
126 |
127 | ]
--------------------------------------------------------------------------------
/src/scripts/pubs/probability/controllers/prob9525ResultsController.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').controller 'prob9525ResultsController', [
2 | '$scope'
3 | ($scope) ->
4 | console.log "results controller scope = #{$scope.$id}"
5 |
6 | $scope.spinnerConfigs =
7 | triggers:
8 | excuse:
9 | "T": \accuse
10 | "L": \alwaysA
11 |
12 | excuse: # an array of spinner configurations
13 | * "label": \T
14 | "weight": 10
15 | "fill" : \#6af
16 |
17 | * "label": \T
18 | "weight": 10
19 | "fill" : \#6af
20 |
21 | * "label": \T
22 | "weight": 10
23 | "fill" : \#6af
24 |
25 | * "label": \T
26 | "weight": 10
27 | "fill" : \#6af
28 |
29 | * "label": \T
30 | "weight": 10
31 | "fill" : \#6af
32 |
33 | * "label": \L
34 | "weight": 10
35 | "fill" : \#f88
36 |
37 | accuse:
38 | * "label": \A
39 | "weight": 10
40 | "fill" : \#fe2
41 | * "label": \B
42 | "weight": 10
43 | "fill" : \#4f6
44 | * "label": \B
45 | "weight": 10
46 | "fill" : \#4f6
47 | * "label": \B
48 | "weight": 10
49 | "fill" : \#4f6
50 | * "label": \B
51 | "weight": 10
52 | "fill" : \#4f6
53 | * "label": \B
54 | "weight": 10
55 | "fill" : \#4f6
56 |
57 | alwaysA:
58 | * "label": \A
59 | "weight": 10
60 | "fill" : \#fe2
61 | ...
62 |
63 |
64 | $scope.model =
65 | excuse: 0
66 | accuse: 0
67 | alwaysA: 0
68 |
69 | $scope.summary = {}
70 | $scope.lessons = []
71 | decisions = {}
72 |
73 | reset = ->
74 | $scope.summary :=
75 | LA:0
76 | LB:0
77 | LTotal:0
78 | TA:0
79 | TB:0
80 | TTotal:0
81 | ATotal:0
82 | BTotal:0
83 | Total:0
84 | $scope.lessons := []
85 | decisions :=
86 | excuse: []
87 | accuse: []
88 | $scope.model =
89 | excuse: 0
90 | accuse: 0
91 |
92 | $scope.$on "resetSpinners", reset
93 |
94 | # initial view setup
95 | reset()
96 |
97 | # view update
98 | logEncounter = (excuseResult, accuseResult) ->
99 |
100 | # ignore accuse spinner if lying
101 | if excuseResult == \L
102 | accuseResult = \A
103 |
104 | s = $scope.summary
105 | number = $scope.lessons.length + 1
106 | abbrOutcome = excuseResult + accuseResult
107 | s[abbrOutcome]++
108 | if excuseResult == \T
109 | s.TTotal++
110 | else
111 | s.LTotal++
112 | if accuseResult == \A
113 | s.ATotal++
114 | else
115 | s.BTotal++
116 | s.Total++
117 |
118 | $scope.lessons.unshift {
119 | number: number
120 | excuse: excuseResult
121 | accuse: accuseResult
122 | outcome: abbrOutcome
123 | }
124 |
125 | # watch for single spinner settle events
126 | $scope.$on "spinDone", (event, name, sectorIndex, label) ->
127 | event.stopPropagation()
128 | decisions[name].push label unless name == \alwaysA
129 |
130 | # and all spinners finished
131 | $scope.$on "spinGroupDone", (event) ->
132 | event.stopPropagation()
133 |
134 | # update the view with this result
135 | lastx = decisions.excuse.length - 1
136 | if lastx == decisions.accuse.length - 1
137 | logEncounter decisions.excuse[lastx], decisions.accuse[lastx]
138 |
139 | ]
--------------------------------------------------------------------------------
/src/styles/popovers.less:
--------------------------------------------------------------------------------
1 | //
2 | // Popovers
3 | // --------------------------------------------------
4 |
5 |
6 | .popover {
7 | position: absolute;
8 | top: 0;
9 | left: 0;
10 | z-index: @zindexPopover;
11 | display: none;
12 | max-width: 276px;
13 | padding: 1px;
14 | text-align: left; // Reset given new insertion method
15 | background-color: @popoverBackground;
16 | -webkit-background-clip: padding-box;
17 | -moz-background-clip: padding;
18 | background-clip: padding-box;
19 | border: 1px solid #ccc;
20 | border: 1px solid rgba(0,0,0,.2);
21 | .border-radius(6px);
22 | .box-shadow(0 5px 10px rgba(0,0,0,.2));
23 |
24 | // Overrides for proper insertion
25 | white-space: normal;
26 |
27 | // Offset the popover to account for the popover arrow
28 | &.top { margin-top: -10px; }
29 | &.right { margin-left: 10px; }
30 | &.bottom { margin-top: 10px; }
31 | &.left { margin-left: -10px; }
32 | }
33 |
34 | .popover-title {
35 | margin: 0; // reset heading margin
36 | padding: 8px 14px;
37 | font-size: 14px;
38 | font-weight: normal;
39 | line-height: 18px;
40 | background-color: @popoverTitleBackground;
41 | border-bottom: 1px solid darken(@popoverTitleBackground, 5%);
42 | .border-radius(5px 5px 0 0);
43 |
44 | &:empty {
45 | display: none;
46 | }
47 | }
48 |
49 | .popover-content {
50 | padding: 9px 14px;
51 | }
52 |
53 | // Arrows
54 | //
55 | // .arrow is outer, .arrow:after is inner
56 |
57 | .popover .arrow,
58 | .popover .arrow:after {
59 | position: absolute;
60 | display: block;
61 | width: 0;
62 | height: 0;
63 | border-color: transparent;
64 | border-style: solid;
65 | }
66 | .popover .arrow {
67 | border-width: @popoverArrowOuterWidth;
68 | }
69 | .popover .arrow:after {
70 | border-width: @popoverArrowWidth;
71 | content: "";
72 | }
73 |
74 | .popover {
75 | &.top .arrow {
76 | left: 50%;
77 | margin-left: -@popoverArrowOuterWidth;
78 | border-bottom-width: 0;
79 | border-top-color: #999; // IE8 fallback
80 | border-top-color: @popoverArrowOuterColor;
81 | bottom: -@popoverArrowOuterWidth;
82 | &:after {
83 | bottom: 1px;
84 | margin-left: -@popoverArrowWidth;
85 | border-bottom-width: 0;
86 | border-top-color: @popoverArrowColor;
87 | }
88 | }
89 | &.right .arrow {
90 | top: 50%;
91 | left: -@popoverArrowOuterWidth;
92 | margin-top: -@popoverArrowOuterWidth;
93 | border-left-width: 0;
94 | border-right-color: #999; // IE8 fallback
95 | border-right-color: @popoverArrowOuterColor;
96 | &:after {
97 | left: 1px;
98 | bottom: -@popoverArrowWidth;
99 | border-left-width: 0;
100 | border-right-color: @popoverArrowColor;
101 | }
102 | }
103 | &.bottom .arrow {
104 | left: 50%;
105 | margin-left: -@popoverArrowOuterWidth;
106 | border-top-width: 0;
107 | border-bottom-color: #999; // IE8 fallback
108 | border-bottom-color: @popoverArrowOuterColor;
109 | top: -@popoverArrowOuterWidth;
110 | &:after {
111 | top: 1px;
112 | margin-left: -@popoverArrowWidth;
113 | border-top-width: 0;
114 | border-bottom-color: @popoverArrowColor;
115 | }
116 | }
117 |
118 | &.left .arrow {
119 | top: 50%;
120 | right: -@popoverArrowOuterWidth;
121 | margin-top: -@popoverArrowOuterWidth;
122 | border-right-width: 0;
123 | border-left-color: #999; // IE8 fallback
124 | border-left-color: @popoverArrowOuterColor;
125 | &:after {
126 | right: 1px;
127 | border-right-width: 0;
128 | border-left-color: @popoverArrowColor;
129 | bottom: -@popoverArrowWidth;
130 | }
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/scripts/libs/html5shiv-printshiv.js:
--------------------------------------------------------------------------------
1 | (function(j,f){function s(a,b){var c=a.createElement("p"),m=a.getElementsByTagName("head")[0]||a.documentElement;c.innerHTML="x";return m.insertBefore(c.lastChild,m.firstChild)}function o(){var a=d.elements;return"string"==typeof a?a.split(" "):a}function n(a){var b=t[a[u]];b||(b={},p++,a[u]=p,t[p]=b);return b}function v(a,b,c){b||(b=f);if(e)return b.createElement(a);c||(c=n(b));b=c.cache[a]?c.cache[a].cloneNode():y.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);
2 | return b.canHaveChildren&&!z.test(a)?c.frag.appendChild(b):b}function A(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();a.createElement=function(c){return!d.shivMethods?b.createElem(c):v(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+o().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(d,b.frag)}
3 | function w(a){a||(a=f);var b=n(a);if(d.shivCSS&&!q&&!b.hasCSS)b.hasCSS=!!s(a,"article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}mark{background:#FF0;color:#000}");e||A(a,b);return a}function B(a){for(var b,c=a.attributes,m=c.length,f=a.ownerDocument.createElement(l+":"+a.nodeName);m--;)b=c[m],b.specified&&f.setAttribute(b.nodeName,b.nodeValue);f.style.cssText=a.style.cssText;return f}function x(a){function b(){clearTimeout(d._removeSheetTimer);c&&c.removeNode(!0);c=null}
4 | var c,f,d=n(a),e=a.namespaces,j=a.parentWindow;if(!C||a.printShived)return a;"undefined"==typeof e[l]&&e.add(l);j.attachEvent("onbeforeprint",function(){b();var g,i,d;d=a.styleSheets;for(var e=[],h=d.length,k=Array(h);h--;)k[h]=d[h];for(;d=k.pop();)if(!d.disabled&&D.test(d.media)){try{g=d.imports,i=g.length}catch(j){i=0}for(h=0;h+~])("+o().join("|")+")(?=[[\\s,>+~#.:]|$)","gi");for(k=
5 | "$1"+l+"\\:$2";i--;)e=g[i]=g[i].split("}"),e[e.length-1]=e[e.length-1].replace(h,k),g[i]=e.join("}");e=g.join("{");i=a.getElementsByTagName("*");h=i.length;k=RegExp("^(?:"+o().join("|")+")$","i");for(d=[];h--;)g=i[h],k.test(g.nodeName)&&d.push(g.applyElement(B(g)));f=d;c=s(a,e)});j.attachEvent("onafterprint",function(){for(var a=f,c=a.length;c--;)a[c].removeNode();clearTimeout(d._removeSheetTimer);d._removeSheetTimer=setTimeout(b,500)});a.printShived=!0;return a}var r=j.html5||{},z=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,
6 | y=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q,u="_html5shiv",p=0,t={},e;(function(){try{var a=f.createElement("a");a.innerHTML=" ";q="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}e=b}catch(d){e=q=!0}})();var d={elements:r.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",
7 | shivCSS:!1!==r.shivCSS,supportsUnknownElements:e,shivMethods:!1!==r.shivMethods,type:"default",shivDocument:w,createElement:v,createDocumentFragment:function(a,b){a||(a=f);if(e)return a.createDocumentFragment();for(var b=b||n(a),c=b.frag.cloneNode(),d=0,j=o(),l=j.length;d
5 |
6 | localStore = $window.localStorage
7 |
8 | # Clear the question store
9 | #
10 | # All data is stored in localStore.qSets - keyed by exercise name.
11 | #
12 | # NB we store question topicIds and seeds, trusting that
13 | # given the same seed we can regenerate the same question.
14 | #
15 | # We store the mathmo version number in case a future version needs
16 | # to update the stored data
17 | #
18 |
19 |
20 | var lsok
21 |
22 | unavail = ->
23 | alert "Storage unavailable. Mathmo will forget your questions."
24 | lsok := false
25 |
26 | clear = ->
27 | if lsok
28 | try
29 | localStore.setItem "qSets", JSON.stringify {mathmo:semver}
30 |
31 | save = (qSets) ->
32 | if lsok
33 | try
34 | localStore.setItem "qSets", JSON.stringify qSets
35 | catch e
36 | alert "Local storage exceeded and is being cleared"
37 | clear!
38 | try
39 | localStore.setItem "qSets", JSON.stringify qSets
40 | catch e
41 | unavail!
42 |
43 | load = ->
44 | if lsok
45 | try
46 | JSON.parse localStore.getItem "qSets"
47 | catch e
48 | unavail!
49 |
50 |
51 | # constructor
52 | init = ->
53 | unavail = "Storage unavailable. Mathmo will forget your questions."
54 | lsok := true
55 | try
56 | qSets = load!
57 | catch e
58 | qSets = {}
59 |
60 | # clear it unless it's already initialised for mathmo
61 | if not qSets? or not qSets.mathmo
62 | clear!
63 |
64 | init!
65 |
66 | # append a question
67 | appendQ = (name, topicId) ->
68 | qSets = load!
69 | qSets = {} unless angular.isObject qSets
70 | qSets[name].push topicId
71 | save(qSets)
72 |
73 | # update a question number after prev or next
74 | updateQ = (name, topicId, qNo) ->
75 | qSets = JSON.parse localStore.getItem "qSets"
76 | set = qSets[name]
77 | if set
78 | # copy the exercise, while editing qNo for question at topicId
79 | qSets[name] = set.map (q) ->
80 | if q.indexOf(topicId) == 0
81 | parts = q.split \:
82 | parts[1] = qNo
83 | q = parts.join \:
84 | return q
85 | save(qSets)
86 |
87 | # Save this question set in local storage by name
88 | # We have to serialise and deserialise using JSON
89 | # since localStorage only saves strings.
90 | saveAs = (name, topicIds) ->
91 | qSets = JSON.parse localStore.getItem "qSets"
92 | qSets = {} unless angular.isObject qSets
93 | qSets["mathmo"] = semver
94 | qSets[name] = topicIds
95 | save(qSets)
96 | return topicIds
97 |
98 | newQSet = (name) ->
99 | return saveAs(name, [])
100 |
101 | getQSet = (name) ->
102 | qSets = JSON.parse localStore.getItem "qSets"
103 | return qSets[name]
104 |
105 | # forget about a saved question set
106 | remove = (name) ->
107 | qSets = JSON.parse localStore.getItem "qSets"
108 | delete qSets[name]
109 | save(qSets)
110 |
111 | # list all stored qSets by name
112 | list = ->
113 | qSets = JSON.parse localStore.getItem "qSets"
114 | [name for name of qSets when name != 'mathmo']
115 |
116 | return
117 | init: init
118 | clear: clear
119 | appendQ: appendQ # can throw
120 | updateQ: updateQ
121 | saveAs: saveAs
122 | newQSet: newQSet
123 | getQSet: getQSet
124 | remove: remove
125 | list: list
126 |
127 | ]
--------------------------------------------------------------------------------
/test/scripts/directives/d3VisSpec.ls:
--------------------------------------------------------------------------------
1 | #
2 | # 'it' is a reserved word in livescript - it means the value of a dummy argument.
3 | # So we need to insert a real argument '_' to decsribe to avoid 'it' being inserted there.
4 | # Mildly yukky.
5 | #
6 | describe 'd3Vis directive', (_) ->
7 |
8 | scope={}
9 | compile={}
10 | el = {}
11 | flag = false
12 | window = {}
13 |
14 | beforeEach module 'app'
15 |
16 | beforeEach inject ($rootScope, $compile, $timeout, $window) ->
17 |
18 | spyOn(d3, 'select').andCallThrough()
19 |
20 | scope := $rootScope
21 | compile := $compile
22 | el := angular.element('
')
23 | compile(el)(scope)
24 | scope.$digest()
25 | $timeout.flush()
26 | window := $window
27 |
28 | afterEach -> el.remove()
29 |
30 | it "should have called d3.select", ->
31 | expect(d3.select).toHaveBeenCalled()
32 |
33 | it "should have appended an svg", ->
34 | expect(el.find('svg').length).toBe 1
35 |
36 | describe "svg width", (_) ->
37 |
38 | it "should be defined", ->
39 | expect(el.find('svg').eq(0).attr('width')).toBeDefined()
40 |
41 | it "should be window width - woff + 1", ->
42 | w = ~~el.find('svg').eq(0).attr('width')
43 | ww = window.innerWidth
44 | woff = 41
45 | expected = if(w <= ww - woff) then 601 else (ww-woff+1)
46 | expect(w).toBe expected
47 |
48 | describe "border defaults", (_) ->
49 | s = {}
50 |
51 | beforeEach ->
52 | s := el.scope()
53 |
54 | it "for margins should be zero", ->
55 | expect(s._margin.top == s._margin.bottom).toBe true
56 | expect(s._margin.left == s._margin.right).toBe true
57 |
58 | it "for padding should be zero", ->
59 | expect(s._padding.top == s._padding.bottom).toBe true
60 | expect(s._padding.left == s._padding.right).toBe true
61 |
62 | describe "borders", (_) ->
63 |
64 | s = {}
65 |
66 | beforeEach inject ($timeout) ->
67 | el.remove()
68 | el := angular.element('
')
69 | compile(el)(scope)
70 | scope.$digest()
71 | s := el.scope()
72 | $timeout.flush()
73 |
74 | it "can be set from attribute list", ->
75 | expect(s._margin.top).toBe 1
76 | expect(s._margin.right).toBe 2
77 | expect(s._margin.bottom).toBe 3
78 | expect(s._margin.left).toBe 4
79 | expect(s._padding.top).toBe 5
80 | expect(s._padding.right).toBe 6
81 | expect(s._padding.bottom).toBe 5
82 | expect(s._padding.left).toBe 6
83 |
84 | describe "margins and padding", (_) ->
85 | s = {}
86 |
87 | beforeEach inject ($timeout) ->
88 | el.remove()
89 | el := angular.element('
')
90 | compile(el)(scope)
91 | scope.$digest()
92 | s := el.scope()
93 | $timeout.flush()
94 |
95 | it "affect the width and innerWidth", ->
96 | /*
97 | console.log "width", s.width
98 | console.log "innerwidth", s.innerWidth
99 | console.log "m.left", s._margin.left
100 | console.log "m.right", s._margin.right
101 | console.log "s.outerWidth", s.outerWidth
102 | */
103 |
104 | expect(s.innerWidth + s._margin.left + s._margin.right == s.outerWidth).toBe true
105 | expect(s.width + s._padding.left + s._padding.right == s.innerWidth).toBe true
106 |
107 | describe "visibility", (_) ->
108 | s = {}
109 |
110 | beforeEach inject ($timeout) ->
111 | el.remove()
112 | el := angular.element('
')
113 | compile(el)(scope)
114 | scope.$digest()
115 | s := el.scope()
116 | $timeout.flush()
117 |
118 | it "is hidden if visible is false", ->
119 | r1 = s.svg.select("g > rect")
120 | c1 = r1.attr("class")
121 | expect(c1).toBe "hide"
122 |
123 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/src/scripts/libs/qa/fpolys.ls:
--------------------------------------------------------------------------------
1 | #
2 | # fpolys.ls - polynomials with fractions
3 | #
4 |
5 | # write a fractional coefficient
6 | fcoeff = (f,t) ->
7 | if f.top is f.bot
8 | t
9 | else if f.top + f.bot is 0
10 | "-" + t
11 | else if f.top is 0
12 | ""
13 | else
14 | f.write() + t
15 |
16 |
17 |
18 | # write a fractional coefficient's modulus
19 | fbcoeff = (f,t) ->
20 | g = new frac(Math.abs(f.top), Math.abs(f.bot))
21 | if g.top is g.bot
22 | t
23 | else if g.top is 0
24 | ""
25 | else
26 | g.write() + t
27 |
28 |
29 |
30 | # Polynomial over Q
31 | class fpoly
32 | (rank) ->
33 | @rank = rank
34 |
35 | terms: ->
36 | n = 0
37 | for i from 0 to @rank
38 | if @[i] then n++
39 | return n
40 |
41 | set: ->
42 | @rank = @set.arguments.length - 1
43 | for i from 0 to @rank
44 | @[i] = toFrac(@set.arguments[i])
45 |
46 | setrand: (maxentry) ->
47 | for i from 0 to @rank
48 | @[i] = randfrac(12)
49 | if @[@rank].top is 0
50 | @[@rank].top = maxentry
51 | @[@rank].reduce()
52 |
53 | # set from a poly (over Z) object
54 | setpoly: (a) ->
55 | @rank = a.rank
56 | for i from 0 to @rank
57 | @[i] = new frac(a[i], 1)
58 |
59 | compute: (x) ->
60 | x = toFrac(x)
61 | y = new frac(0,1)
62 | for i from 0 to @rank
63 | y.add(@[i].top * (x.top)^i, @[i].bot * (x.bot)^i)
64 | y.reduce()
65 | return y
66 |
67 | gcd: ->
68 | g = new frac(0,1)
69 | for i from 0 to @rank - 1
70 | g.bot *= @[i].bot
71 | a = @[@rank].top * g.bot / @[@rank].bot
72 | for i from 0 to @rank - 1
73 | a = gcd(a, @[i].top * g.bot / @[i].bot)
74 | g.reduce()
75 |
76 | xthru: (x) ->
77 | for i from 0 to @rank
78 | @[i].prod(x)
79 |
80 | diff: (d) ->
81 | d.rank = rank - 1
82 | for i from 0 to @rank - 1
83 | d[i] = @[i+1]
84 | d[i].prod(frac(i + 1, 1))
85 | return d
86 |
87 | integ: (d) ->
88 | d.rank = @rank + 1
89 | d[0] = new frac()
90 | for i from 0 to @rank
91 | d[i + 1] = @[i]
92 | d[i + 1].bot *= i + 1
93 | d[i + 1].reduce()
94 | return d
95 |
96 | write: ->
97 | q = ""
98 | j = false
99 |
100 | for i from @rank to 0 by -1
101 | val = @[i].top / @[i].bot
102 |
103 | if val < 0
104 | if j then q += " "
105 | q += "- "
106 | j = false
107 | else if j and val
108 | q += " + "
109 | j = false
110 |
111 | if val
112 | a = new frac(Math.abs(@[i].top, @[i].bot))
113 | switch i
114 | case 0
115 | q += a.write()
116 | j = true
117 | case 1
118 | if a.top is a.bot then q += "x"
119 | else q += a.write() + "x"
120 | j = true
121 | default
122 | if a.top is a.bot then q += "x^" + i
123 | else q += a.write() + "x^" + i
124 | j = true
125 |
126 | return q
127 |
128 | rwrite: ->
129 | q = ""
130 | j = false
131 |
132 | for i from 0 to @rank
133 | val = @[i].top / @[i].bot
134 |
135 | if val < 0
136 | if j then q += " "
137 | q += "- "
138 | j = false
139 | else if j and val
140 | q += " + "
141 | j = false
142 |
143 | if val
144 | a = new frac(Math.abs(@[i].top), Math.abs(@[i].bot))
145 | switch i
146 | case 0
147 | q += a.write()
148 | j = true
149 | case 1
150 | if a.top is a.bot then q += "x"
151 | else q += a.write() + "x"
152 | j = true
153 | default
154 | if a.top is a.bot then q += "x^" + i
155 | else q += a.write() + "x^" + i
156 | j = true
157 |
158 | return q
159 |
--------------------------------------------------------------------------------
/src/scripts/pubs/mathmo/main.ls:
--------------------------------------------------------------------------------
1 | require {
2 | shim:
3 | 'libs/qa/helpers':
4 | deps:
5 | * 'libs/seedrandom'
6 | ...
7 |
8 | 'libs/qa/complex':
9 | deps:
10 | * 'libs/seedrandom'
11 | 'libs/qa/polys'
12 | 'libs/qa/guessExact'
13 | 'libs/qa/helpers'
14 |
15 | 'libs/qa/fractions':
16 | deps:
17 | * 'libs/seedrandom'
18 | 'libs/qa/helpers'
19 |
20 | 'libs/qa/polys':
21 | deps:
22 | * 'libs/seedrandom'
23 | 'libs/qa/guessExact'
24 | 'libs/qa/helpers'
25 |
26 | 'libs/qa/fpolys':
27 | deps:
28 | * 'libs/seedrandom'
29 | 'libs/qa/helpers'
30 | 'libs/qa/fractions'
31 |
32 | 'libs/qa/guessExact':
33 | deps:
34 | * 'libs/seedrandom'
35 | 'libs/qa/helpers'
36 | 'libs/qa/fractions'
37 |
38 | 'libs/qa/stats':
39 | deps:
40 | * 'libs/seedrandom'
41 | 'libs/qa/helpers'
42 |
43 | 'libs/qa/geometry':
44 | deps:
45 | * 'libs/seedrandom'
46 | 'libs/qa/helpers'
47 |
48 | 'libs/qa/problems':
49 | deps:
50 | * 'libs/seedrandom'
51 | 'libs/qa/complex'
52 | 'libs/qa/fpolys'
53 | 'libs/qa/fractions'
54 | 'libs/qa/guessExact'
55 | 'libs/qa/helpers'
56 | 'libs/qa/polys'
57 | 'libs/qa/stats'
58 | 'libs/qa/geometry'
59 |
60 | 'pubs/mathmo/services/config':
61 | deps:
62 | * 'app'
63 | 'libs/qa/problems'
64 |
65 | 'pubs/mathmo/services/questionStore':
66 | deps:
67 | * 'app'
68 | 'libs/seedrandom'
69 |
70 | 'pubs/mathmo/controllers/mathmoController':
71 | deps:
72 | * 'app'
73 | 'pubs/mathmo/services/config'
74 | 'pubs/mathmo/services/questionStore'
75 |
76 | 'pubs/mathmo/directives/mathmoPlot':
77 | deps:
78 | * 'app'
79 | 'pubs/mathmo/controllers/mathmoController'
80 | 'directives/d3Vis'
81 | 'services/d3MultiLineChart'
82 | 'libs/d3.v3'
83 |
84 | 'controllers/appController':
85 | deps:
86 | * 'app'
87 | ...
88 |
89 | 'directives/d3Vis':
90 | deps:
91 | * 'app'
92 | 'libs/d3.v3'
93 | 'directives/svgCheck'
94 |
95 | 'directives/d3DotGrid':
96 | deps:
97 | * 'app'
98 | 'directives/d3Vis'
99 |
100 | 'directives/svgCheck':
101 | deps:
102 | * 'app'
103 | 'libs/d3.v3'
104 |
105 | 'directives/appVersion':
106 | deps:
107 | * 'app'
108 | 'services/semver'
109 |
110 | 'services/semver':
111 | deps:
112 | * 'app'
113 | ...
114 |
115 | 'services/d3MultiLineChart':
116 | deps:
117 | * 'app'
118 | 'libs/d3.v3'
119 | ...
120 |
121 | 'bootstrap':
122 | deps:
123 | * 'app'
124 | ...
125 |
126 | 'libs/bootstrap':
127 | deps:
128 | * 'libs/jquery'
129 | ...
130 | 'libs/angular-resource':
131 | deps:
132 | * 'libs/angular'
133 | ...
134 |
135 | 'libs/ui-bootstrap-tpls':
136 | deps:
137 | * 'libs/angular'
138 | ...
139 |
140 | 'app':
141 | deps:
142 | * 'libs/angular'
143 | 'libs/angular-resource'
144 | 'libs/ui-bootstrap-tpls'
145 |
146 | 'routes':
147 | deps:
148 | * 'app'
149 | ...
150 |
151 | 'views':
152 | deps:
153 | * 'app'
154 | ...
155 | },
156 | * 'require'
157 | 'controllers/appController'
158 | 'pubs/mathmo/controllers/mathmoController'
159 | 'pubs/mathmo/directives/mathmoPlot'
160 | 'directives/appVersion'
161 | 'routes'
162 | 'views'
163 | , (require) -> require ['bootstrap']
164 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Apps1
2 | A collection of Apps for [NRICH](http://nrich.maths.org)
3 | *By [@grumplet](https://twitter.com/grumplet)*
4 |
5 | Derived from AngularFun
6 | *By [@CaryLandholt](https://twitter.com/carylandholt)*
7 |
8 | The aim is to make this app host a few related apps and form the basis of similar collections.
9 |
10 | Added some grunt tasks:
11 |
12 | * `grunt mask:` causes only the named app to be generated in the final dist.
13 | * `grunt unmask` causes all apps to be present. Useful for test purposes.
14 |
15 | Converted codebase to [LiveScript](http://livescript.net).
16 |
17 | Updated to grunt-0.4 in all but the playlist branch which is
18 | still at grunt-0.3.
19 |
20 | ## About
21 | Apps1 is an [AngularJS](http://angularjs.org/) app collection.
22 |
23 | Follow the patterns and you'll get a complete development workflow, including:
24 |
25 | * file organization
26 | * transpilation of [CoffeeScript](http://coffeescript.org/) files (_if you prefer plain JavaScript, see [JS Love](#js-love)_)
27 | * transpilation of [LESS](http://lesscss.org/) files
28 | * three build configurations
29 | * **default** - compilation with no optimizations
30 | * **dev** - compilation with no optimizations but includes file watching to monitor changes and build changed files on-the-fly
31 | * **prod** - compilation with all optimizations, including concatenation and minification of PNG, JavaScript, CSS, and HTML files.
32 | * full dependency management (file loading and dependency resolution)
33 | * an in-browser unit testing strategy
34 | * a server to run the application
35 |
36 | ## Prerequisites
37 | * Text editor - preferably TextMate or Sublime Text, set to translate tabs
38 | to 2 spaces. We use 2 spaces per indent for LiveScript. If using these
39 | editors, install the [livescript.tmbundle](https://github.com/paulmillr/livescript.tmbundle) for syntax checking.
40 |
41 | * Must have [Git](http://git-scm.com/) installed
42 | * Must have [node.js (at least v0.8.1)](http://nodejs.org/) installed with npm (Node Package Manager)
43 | * Must have [CoffeeScript](https://npmjs.org/package/coffee-script) node package installed globally. `npm install -g coffee-script`
44 | * Must have [Grunt](https://github.com/gruntjs/grunt) node package installed globally. `npm install -g grunt-cli`
45 |
46 | ## Install Apps1
47 | Enter the following commands at a command line:
48 |
49 | git clone git://github.com/gmp26/Apps1.git
50 | cd Apps1
51 | npm install
52 |
53 | ## Compile Apps1
54 | You have three options.
55 |
56 | 1. `grunt` - will compile the app preserving individual files (when run, files will be loaded on-demand)
57 | 2. `grunt dev` - same as `grunt` but will watch for file changes and recompile on-the-fly
58 | 3. `grunt prod` - will compile using optimizations. This will create one JavaScript file and one CSS file to demonstrate the power of [r.js](http://requirejs.org/docs/optimization.html), the build optimization tool for RequireJS. And take a look at the `index.html` file. Yep - it's minified too.
59 |
60 | ## Using Javascript instead of Coffeescript
61 | To work with plain old JavaScript run the following grunt task.
62 | `grunt jslove` - it will transpile all of the CoffeeScript files to JavaScript and throw out the Coffee.
63 |
64 | ## Run It
65 | 1. Navigate to the root of the project
66 | 2. `grunt server`
67 | 3. Open the [app](http://localhost:3005/) in your browser to run the app
68 |
69 | ## Making Changes
70 | * `grunt dev` will watch for any CoffeeScript (`.coffee`), Less (`.less`), or `.template` file changes. When changes are detected, the files will be linted, compiled, and ready for you to refresh the browser.
71 |
72 | ## Running Tests
73 | You have two options.
74 |
75 | 1. [Jasmine](http://pivotal.github.com/jasmine/) HTML runner - run `grunt` - Then open `/test/runner.html` in your browser to run the unit tests using Jasmine.
76 | 2. [Karma](http://vojtajina.github.com/karma/) - `grunt test` - Defaults to running the tests in Chrome, but you can easily change this in `karma.conf.js` browsers section as required.
77 |
--------------------------------------------------------------------------------
/test/pubs/frogs/frogControllerSpec.ls:
--------------------------------------------------------------------------------
1 | import prelude
2 |
3 | beforeEach module 'app'
4 |
5 | describe 'App', (_) ->
6 |
7 | # we want to make the scope available generally
8 | scope = null
9 | timeout = null
10 |
11 | beforeEach inject ($controller, $rootScope, $timeout) ->
12 | scope := $rootScope.$new()
13 | $controller 'frogController', $scope: scope
14 | timeout := $timeout
15 |
16 | describe 'spawning', (_) ->
17 |
18 | it 'should start with 2 red, 2 blue', ->
19 | expect scope._red .toBe 2
20 | expect scope._blue .toBe 2
21 |
22 | it 'should put them in a frog array', ->
23 | expect scope.frogs.length .toBe 5
24 |
25 | it 'should make a move list', ->
26 | expect scope.moves .toBeDefined()
27 | expect scope.moves.red .toBe 2
28 | expect scope.moves.list.length .toBe 0
29 |
30 | describe 'hopping', (_) ->
31 |
32 | it 'should allow hops to space', ->
33 | scope.hop scope.frogs.0, scope.frogs.2
34 | expect scope.frogs.0.x .toBe 2
35 | expect scope.frogs.2.x .toBe 0
36 |
37 | it 'should save moves', ->
38 | scope.hop scope.frogs.0, scope.frogs.2
39 | expect scope.moves.list.length .toBe 1
40 | expect scope.moves.list.0 .toEqual frogx: 0, spacex: 2
41 |
42 | it 'should check whether for completion and minimum moves' ->
43 | scope._red = 1
44 | scope._blue = 1
45 | scope.reset()
46 |
47 | expect scope.done .toBe false
48 |
49 | # Make enough moves to swap frogs
50 | scope.hop scope.frogs.0, scope.frogs.1
51 | scope.hop scope.frogs.2, scope.frogs.1
52 | scope.hop scope.frogs.1, scope.frogs.0
53 |
54 | expect scope.done .toBe true
55 | expect scope.minimum .toBe true
56 |
57 | scope.hop scope.frogs.0, scope.frogs.1
58 | scope.hop scope.frogs.1, scope.frogs.0
59 |
60 | expect scope.minimum .toBe false
61 |
62 |
63 | describe 'replay', (_) ->
64 |
65 | it 'should reset then return to final state after 1s', ->
66 | scope.hop scope.frogs.0, scope.frogs.2
67 | scope.replay(null)
68 | expect scope.frogs.0.x .toBe 0
69 | expect scope.frogs.2.x .toBe 2
70 | timeout.flush()
71 | expect scope.frogs.0.x .toBe 2
72 | expect scope.frogs.2.x .toBe 0
73 |
74 | describe 'save', (_) ->
75 |
76 | it 'should save the last set of moves', ->
77 | scope.hop scope.frogs.0, scope.frogs.2
78 | scope.hop scope.frogs.1, scope.frogs.0
79 | scope.hop scope.frogs.2, scope.frogs.1
80 | expect scope.moves.list.length .toBe 3
81 |
82 | scope.save()
83 | scope.save()
84 |
85 | expect scope.savedMoves.length .toEqual 2
86 |
87 | # patch in the tag, which should have changed
88 | scope.savedMoves.map (.tag = void)
89 |
90 | expect scope.savedMoves.0 .toEqual(scope.moves)
91 | expect scope.savedMoves.1 .toEqual(scope.moves)
92 |
93 | describe 'forget', (_) ->
94 |
95 | it 'should delete a saved move', ->
96 | # save a 3 hop sequence
97 | scope.hop scope.frogs.0, scope.frogs.2
98 | scope.hop scope.frogs.1, scope.frogs.0
99 | scope.hop scope.frogs.2, scope.frogs.1
100 | expect scope.moves.list.length .toBe 3
101 | scope.save!
102 |
103 | # reset and save a 2 hop sequence
104 | scope.reset!
105 | scope.hop scope.frogs.0, scope.frogs.2
106 | scope.hop scope.frogs.1, scope.frogs.0
107 | expect scope.moves.list.length .toBe 2
108 | scope.save()
109 |
110 | # should have 2 saves
111 | expect scope.savedMoves.length .toBe 2
112 |
113 | # forget the first
114 | scope.forget 0
115 |
116 | # check that the right one went
117 | expect scope.savedMoves.length .toBe 1
118 | expect scope.savedMoves.0.list.length .toBe 2
119 |
120 | describe 'reset', (_) ->
121 |
122 | beforeEach ->
123 | scope._red = 3
124 | scope._blue = 4
125 | scope.hop scope.frogs.0, scope.frogs.2
126 | scope.hop scope.frogs.1, scope.frogs.0
127 | scope.hop scope.frogs.2, scope.frogs.1
128 | scope.reset()
129 |
130 | it 'should reset to the current initial counts', ->
131 | expect scope.frogs.length .toBe 8
132 |
133 | reds = take 3, scope.frogs
134 | expect reds.every (.colour == 0) .toBe true
135 |
136 | blues = drop 4, scope.frogs
137 | expect blues.every (.colour == 2) .toBe true
138 |
139 | it 'should calculate minimum moves', ->
140 | expect scope.minMove .toBe 19
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/src/scripts/pubs/frogs/controllers/frogController.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').controller 'frogController', [
2 | '$scope'
3 | '$timeout'
4 | '$routeParams'
5 | ($scope, $timeout, $routeParams) ->
6 |
7 | $scope._red = 2
8 | $scope._blue = 2
9 | if $routeParams
10 | $scope.resourceId = $routeParams.id ? 1246
11 | $scope.single = $routeParams.users != "class"
12 | if $routeParams.reds?
13 | $scope._red = Math.min(Math.max(1, ~~$routeParams.reds), 9)
14 | if $routeParams.blues?
15 | $scope._blue = Math.min(Math.max(1, ~~$routeParams.blues), 9)
16 | $scope.container.width = 650 if $scope.container?
17 | $scope.minMove = 8
18 | $scope.savedMoves = []
19 | $scope.showReplay = false
20 |
21 | # spawn an array of frog objects containing colour and pad index
22 | spawn = (red,blue) ->
23 | [-red to blue].map (d,i) ->
24 | colour: if d < 0 then 0 else if d == 0 then 1 else 2
25 | x: i
26 |
27 | initialState = null
28 |
29 | /*
30 | * class MoveList
31 | * an easy to clone list of moves annotated with a tag string
32 | * and the current red and blue counts.
33 | */
34 | class MoveList
35 | (@list, @tag, @red, @blue) ->
36 | clone: ->
37 | new MoveList @list.concat(), @tag, @red, @blue
38 |
39 | reset = ->
40 |
41 | # this will be constant
42 | initialState := spawn($scope._red, $scope._blue)
43 |
44 | # this will change
45 | $scope.frogs = spawn($scope._red, $scope._blue)
46 |
47 | # tell the container how much room we need
48 | # $scope.container.width = 130*$scope.frogs.length if $scope.container?
49 |
50 | # create a move list for current frog counts
51 | $scope.moves = new MoveList(
52 | [],
53 | void,
54 | $scope._red,
55 | $scope._blue
56 | )
57 |
58 | $scope.minMove = $scope._red*$scope._blue+$scope._red+$scope._blue
59 | $scope.done = false
60 | $scope.minimum = false
61 | $scope.showReplay = $scope.savedMoves.length > 0
62 |
63 | # cancel any moves that are outstanding
64 | # promises.forEach (p) -> $timeout.cancel p
65 | $scope.moves.list.forEach (d) -> $timeout.cancel d.promisedHop
66 |
67 | reset()
68 |
69 | # check whether frog states a and b are mirrored
70 | reversed = (a, b) ->
71 | a.every (d, i) ->
72 | mirrorx = a.length - d.x - 1
73 | d.colour == b[mirrorx].colour
74 |
75 | resetIfChanged = (newVal, oldVal) -> reset() if(newVal != oldVal)
76 | $scope.$watch "_red", resetIfChanged
77 | $scope.$watch "_blue", resetIfChanged
78 |
79 | $scope.reset = reset
80 |
81 | $scope.hop = (frog, space) ->
82 | #console.log frog.x, space.x
83 |
84 | # save the move
85 | $scope.moves.list.push {frogx: frog.x, spacex: space.x}
86 |
87 | # and swap places
88 | [frog.x, space.x] = [space.x, frog.x]
89 |
90 | # check for end
91 | $scope.done = reversed($scope.frogs, initialState)
92 | $scope.minimum = $scope.done && $scope.moves.list.length == $scope.minMove
93 | $scope.showReplay ||= $scope.done
94 |
95 | $scope.replay = (index = null) ->
96 | #console.log "replay(", index, ")"
97 |
98 | # replay saved moves if indicated, or the current moves
99 | if index?
100 | moves = $scope.savedMoves[index].clone()
101 | else
102 | moves = $scope.moves.clone()
103 |
104 | # reset before replay using saved frog counts
105 | $scope._red = moves.red
106 | $scope._blue = moves.blue
107 | reset()
108 |
109 | moves.list.forEach (d, i) ->
110 | # schedule each hop for playback, saving
111 | # the promises in case we have to cancel them
112 | d.promisedHop = $timeout ->
113 | frog = [f for f in $scope.frogs when f.x == d.frogx][0]
114 | space = [f for f in $scope.frogs when f.x == d.spacex][0]
115 | $scope.hop(frog, space)
116 | , 800*(i+0.2)
117 |
118 | newTag = ->
119 | "try " + ($scope.savedMoves.length + 1)
120 |
121 | $scope.save = ->
122 | saved = $scope.moves.clone()
123 | saved.tag = if $scope.moves.tag then $scope.moves.tag else newTag()
124 | $scope.moves.tag = void
125 | $scope.savedMoves.push(saved)
126 |
127 | $scope.forget = (index) ->
128 | $scope.savedMoves.splice(index, 1)
129 |
130 | $scope.clear = ->
131 | $scope.savedMoves = []
132 | reset()
133 |
134 | ]
135 |
136 |
--------------------------------------------------------------------------------
/src/styles/responsive-767px-max.less:
--------------------------------------------------------------------------------
1 | //
2 | // Responsive: Landscape phone to desktop/tablet
3 | // --------------------------------------------------
4 |
5 |
6 | @media (max-width: 767px) {
7 |
8 | // Padding to set content in a bit
9 | body {
10 | padding-left: 20px;
11 | padding-right: 20px;
12 | }
13 | // Negative indent the now static "fixed" navbar
14 | .navbar-fixed-top,
15 | .navbar-fixed-bottom,
16 | .navbar-static-top {
17 | margin-left: -20px;
18 | margin-right: -20px;
19 | }
20 | // Remove padding on container given explicit padding set on body
21 | .container-fluid {
22 | padding: 0;
23 | }
24 |
25 | // TYPOGRAPHY
26 | // ----------
27 | // Reset horizontal dl
28 | .dl-horizontal {
29 | dt {
30 | float: none;
31 | clear: none;
32 | width: auto;
33 | text-align: left;
34 | }
35 | dd {
36 | margin-left: 0;
37 | }
38 | }
39 |
40 | // GRID & CONTAINERS
41 | // -----------------
42 | // Remove width from containers
43 | .container {
44 | width: auto;
45 | }
46 | // Fluid rows
47 | .row-fluid {
48 | width: 100%;
49 | }
50 | // Undo negative margin on rows and thumbnails
51 | .row,
52 | .thumbnails {
53 | margin-left: 0;
54 | }
55 | .thumbnails > li {
56 | float: none;
57 | margin-left: 0; // Reset the default margin for all li elements when no .span* classes are present
58 | }
59 | // Make all grid-sized elements block level again
60 | [class*="span"],
61 | .uneditable-input[class*="span"], // Makes uneditable inputs full-width when using grid sizing
62 | .row-fluid [class*="span"] {
63 | float: none;
64 | display: block;
65 | width: 100%;
66 | margin-left: 0;
67 | .box-sizing(border-box);
68 | }
69 | .span12,
70 | .row-fluid .span12 {
71 | width: 100%;
72 | .box-sizing(border-box);
73 | }
74 | .row-fluid [class*="offset"]:first-child {
75 | margin-left: 0;
76 | }
77 |
78 | // FORM FIELDS
79 | // -----------
80 | // Make span* classes full width
81 | .input-large,
82 | .input-xlarge,
83 | .input-xxlarge,
84 | input[class*="span"],
85 | select[class*="span"],
86 | textarea[class*="span"],
87 | .uneditable-input {
88 | .input-block-level();
89 | }
90 | // But don't let it screw up prepend/append inputs
91 | .input-prepend input,
92 | .input-append input,
93 | .input-prepend input[class*="span"],
94 | .input-append input[class*="span"] {
95 | display: inline-block; // redeclare so they don't wrap to new lines
96 | width: auto;
97 | }
98 | .controls-row [class*="span"] + [class*="span"] {
99 | margin-left: 0;
100 | }
101 |
102 | // Modals
103 | .modal {
104 | position: fixed;
105 | top: 20px;
106 | left: 20px;
107 | right: 20px;
108 | width: auto;
109 | margin: 0;
110 | &.fade { top: -100px; }
111 | &.fade.in { top: 20px; }
112 | }
113 |
114 | }
115 |
116 |
117 |
118 | // UP TO LANDSCAPE PHONE
119 | // ---------------------
120 |
121 | @media (max-width: 480px) {
122 |
123 | // Smooth out the collapsing/expanding nav
124 | .nav-collapse {
125 | -webkit-transform: translate3d(0, 0, 0); // activate the GPU
126 | }
127 |
128 | // Block level the page header small tag for readability
129 | .page-header h1 small {
130 | display: block;
131 | line-height: @baseLineHeight;
132 | }
133 |
134 | // Update checkboxes for iOS
135 | input[type="checkbox"],
136 | input[type="radio"] {
137 | border: 1px solid #ccc;
138 | }
139 |
140 | // Remove the horizontal form styles
141 | .form-horizontal {
142 | .control-label {
143 | float: none;
144 | width: auto;
145 | padding-top: 0;
146 | text-align: left;
147 | }
148 | // Move over all input controls and content
149 | .controls {
150 | margin-left: 0;
151 | }
152 | // Move the options list down to align with labels
153 | .control-list {
154 | padding-top: 0; // has to be padding because margin collaspes
155 | }
156 | // Move over buttons in .form-actions to align with .controls
157 | .form-actions {
158 | padding-left: 10px;
159 | padding-right: 10px;
160 | }
161 | }
162 |
163 | // Medias
164 | // Reset float and spacing to stack
165 | .media .pull-left,
166 | .media .pull-right {
167 | float: none;
168 | display: block;
169 | margin-bottom: 10px;
170 | }
171 | // Remove side margins since we stack instead of indent
172 | .media-object {
173 | margin-right: 0;
174 | margin-left: 0;
175 | }
176 |
177 | // Modals
178 | .modal {
179 | top: 10px;
180 | left: 10px;
181 | right: 10px;
182 | }
183 | .modal-header .close {
184 | padding: 10px;
185 | margin: -10px;
186 | }
187 |
188 | // Carousel
189 | .carousel-caption {
190 | position: static;
191 | }
192 |
193 | }
194 |
--------------------------------------------------------------------------------
/src/scripts/directives/d3DotGrid.ls:
--------------------------------------------------------------------------------
1 | ###
2 | # d3-dot-grid draws a rectangular grid of dots.
3 | ###
4 | angular.module('app').directive 'd3DotGrid', ->
5 |
6 | data = {}
7 |
8 | return {
9 | restrict: 'A'
10 |
11 |
12 | link: (scope, element, attrs) ->
13 |
14 | #
15 | # attributes must be observed in case they are interpolated
16 | #
17 |
18 | # along is the preferred number of columns along
19 | attrs.$observe 'along', (val) ->
20 | scope.along = if val? then ~~val else 10
21 | console.log "along = ", scope.along
22 |
23 | #down is the preferred number of rows down
24 | attrs.$observe 'down', (val) ->
25 | scope.down = if val? then ~~val else 10
26 | console.log "down = ", scope.down
27 |
28 | # hspace is the preferred horizontal space between dot centres
29 | # Set to "auto" to calculate this from available width and column count.
30 | # Set to a number otherwise.
31 | # The default is 10.
32 | attrs.$observe 'hspace', (val) ->
33 | scope.hspace = if val=="auto" then "auto" else if val? then ~~val else 10
34 | console.log "hspace = ", scope.hspace
35 |
36 | # vspace is the preferred vertical space between dot centres.
37 | # Set to "auto" to calculate this from available height and row count.
38 | # Set to a number otherwise.
39 | # The default is 10.
40 | attrs.$observe 'vspace', (val) ->
41 | scope.vspace = if val=="auto" then "auto" else if val? then ~~val else 10
42 | console.log "vspace = ", scope.vspace
43 |
44 | # minspace is the minimum space allowed between dot centres
45 | attrs.$observe 'minspace', (val) ->
46 | scope.minspace = if val=="auto" then "auto" else if val? then ~~val else 10
47 | console.log "minspace = ", scope.minspace
48 |
49 | # radius is the radius of each dot
50 | attrs.$observe 'radius', (r) ->
51 | scope.radius = if r? then ~~r else 3
52 |
53 | # If square is truthy then vspace and hspace are set to
54 | # whichever is lowest.
55 | attrs.$observe 'square', (val) ->
56 | scope.square = val? and val != "false"
57 |
58 | #
59 | # The parent container (e.g. d3Vis) issues draw
60 | # and resize events
61 | #
62 | scope.$on 'draw', (event, container, width, height) ->
63 | scope.draw(container, width, height)
64 |
65 | scope.$on 'resize', (event, container, width, height) ->
66 | scope.draw(container, width, height)
67 |
68 | scope.draw = (container, width, height) ->
69 |
70 | #set spacing
71 | @container = container
72 | #@width = width
73 | #@height = height
74 |
75 | @rows = @down
76 | @cols = @along
77 | @_vspace = @vspace
78 | @_hspace = @hspace
79 |
80 | if @vspace == "auto"
81 | @_vspace = height / (@rows - 1)
82 | if !isNaN(@minspace) and @_vspace < @minspace
83 | @_vspace = @minspace
84 | @rows = Math.floor(height/@_vspace + 1)
85 |
86 | if @hspace == "auto"
87 | @_hspace = width / (@cols - 1)
88 | if !isNaN(@minspace) and @_hspace < @minspace
89 | @_hspace = @minspace
90 | @cols = Math.floor(width/@_hspace + 1)
91 |
92 | @width = (@cols - 1)*@_hspace
93 | @height = (@rows - 1)*@_vspace
94 |
95 | console.log("w:h = ", @width, ":", @height)
96 |
97 | if @square
98 | space = Math.min(@_vspace, @_hspace)
99 | @_vspace = @_hspace = space
100 |
101 | @X = (col) -> (col)*scope._hspace
102 | @Y = (row) -> (row)*scope._vspace
103 | @COL = (x) -> (x)/scope._hspace
104 | @ROW = (y) -> (y)/scope._vspace
105 |
106 | console.log "rows=", @rows, "cols=", @cols
107 |
108 | # We want array nesting here.
109 | data = [[{x:c, y:r} for r in [0 til @rows]] for c in [0 til @cols]]
110 |
111 | #
112 | # d3 magic starts here
113 | #
114 | gridData = [scope.$id]
115 |
116 | theGrid = @container.selectAll(\#grid)
117 | .data(gridData)
118 |
119 | theGrid.enter().append("g")
120 | .attr("id", "grid")
121 |
122 | columns = theGrid.selectAll("g")
123 | .data(data)
124 |
125 | columns.enter()
126 | .append("g")
127 |
128 | columns.exit()
129 | .remove()
130 |
131 | circles = columns.selectAll("circle")
132 | .data((d)->d)
133 | .each -> d3.select(this)
134 | .attr("cx", (d)->scope.X(d.x))
135 | .attr("cy", (d)->scope.Y(d.y))
136 |
137 | circles.enter().append("circle")
138 | .data((d)->d)
139 | .attr("r", @radius)
140 | .attr("class", "grid-dot")
141 | .attr("cx", (d)->scope.X(d.x))
142 | .attr("cy", (d)->scope.Y(d.y))
143 | circles .exit().remove()
144 |
145 | # tell any child directives that the grid has been redrawn
146 | console.log "dotGRid scope =", scope.$id
147 | @$broadcast 'dotGridUpdated', scope
148 |
149 |
150 | }
--------------------------------------------------------------------------------
/src/scripts/libs/qa/guessExact.ls:
--------------------------------------------------------------------------------
1 | #
2 | # guessExact.ls - guesses the exact representation of a floating-point variable.
3 | #
4 |
5 | # TODO:
6 | # make it compute a load of guesses, and weight each one's closeness by likelihood, and pick the best.
7 | #
8 | # ADVISE-AVOID:
9 | # At the moment it occasionally fails to guess the right thing at all, and sometimes guesses something else first
10 | #
11 | # Two versions, one is commented out.
12 |
13 | /*
14 | guessExact = (x) ->
15 | n = proxInt(x)
16 | if n[0] then return n[1]
17 | fac = 1
18 |
19 | for s from 1 to 6
20 | fac *= s
21 | d = fac * 12
22 | n = proxInt(x * d)
23 |
24 | if n[0]
25 | f = new frac(n[1], d)
26 | f.reduce()
27 | return f.write()
28 |
29 | t = 2^s
30 |
31 | for i from -t to t by 1
32 | for j from -t to t by 1
33 | for k from 1 to s
34 | p = (x - (i / j)) * k
35 | n = proxInt(p^2)
36 |
37 | f = new frac(i,j)
38 | f.reduce()
39 |
40 | if n[0] and n[1] > 1
41 | v = new sqroot(n[1]) # v.a*root(v.n)
42 | g = new frac(v.a, k)
43 | g.reduce()
44 |
45 | # LS doesn't have a proper ternary operator :(
46 | if g.top > 0 then sign = " + " else sign = " - "
47 | return f.write + sign + fbcoeff(g, "\\sqrt{" + v.n + "}")
48 |
49 | p = (x + (i / j)) * k
50 | n = proxInt(p^2)
51 |
52 | if n[0] and n[1] > 1
53 | v = new sqroot(n[1]) # v.a*root v.n
54 | g = new frac(v.a, k)
55 | g.reduce()
56 |
57 | if g.top > 0 then sign = " + " else sign " - "
58 | return "-" + f.write() + sign + fbcoeff(g, "\\sqrt{" + v.n + "}")
59 |
60 | return x
61 | */
62 |
63 | guessExact = (x) ->
64 | n = proxInt(x)
65 | if n[0] then return n[1]
66 |
67 | # compensating for the lack of ternary operator
68 | sgn1 = (x) -> if x < 0 then return "-" else return ""
69 | sgn2 = (x) -> if x < 0 then return "-" else return "+"
70 |
71 | for s from 1 to 17
72 |
73 | for i from 2 to (3 + 2 * s)
74 | n = proxInt(x * i)
75 | if n[0]
76 | top = n[1]
77 | bot = i
78 | c = gcd(top, bot)
79 | top /= c
80 | bot /= c
81 |
82 | return sgn1(x) + "\\frac{" + Math.abs(top) + "}{" + bot + "}"
83 |
84 | n = proxInt(x^2)
85 |
86 | if n[0] and n[1] < s * 10
87 | v = new sqroot(n[1])
88 | return sgn1(x) + v.write()
89 |
90 | for i from 2 to 1 + s
91 | n = proxInt((x * i)^2)
92 | if n[0] and n[1] < s * 10
93 | v = new sqroot(n[1])
94 | return sgn1(x) + "\\frac{" + v.write() + "}{" + i + "}"
95 |
96 | for j from 1 to 3 * s
97 |
98 | a = (x - j)
99 | n = proxInt(a ^ 2)
100 | if n[0] and n[1] < s * 10
101 | v = new sqroot(n[1])
102 | return "\\left(" + j + sgn2(a) + v.write() + "\\right)"
103 |
104 | a = x + j
105 | n = proxInt(a ^ 2)
106 | if n[0] and n[1] < s * 10
107 | v = new sqroot(n[1])
108 | return "\\left(-" + j + sgn2(a) + v.write() + "\\right)"
109 |
110 | for k from 2 to (2 + (2 * s))
111 | for j from 1 to (1 + 2 * s)
112 |
113 | a = x - (j / k)
114 | n = proxInt(a ^ 2)
115 | if n[0] and n[1] < s * 10
116 | v = new sqroot(n[1])
117 | return "\\left(\\frac{" + j + "}{" + k + "}" + sgn2(a) + v.write() + "\\right)"
118 |
119 | a = x + (j / k)
120 | n = proxInt(a ^ 2)
121 | if n[0] and n[1] < s * 10
122 | v = new sqroot(n[1])
123 | return "\\left(-\\frac{" + j + "}{" + k + sgn2(a) + v.write() + "\\right)"
124 |
125 | for i from 2 to (s - 1)
126 | for j from 1 to (2 + (2 * s))
127 |
128 | a = (x - j) * i
129 | n = proxInt(a ^ 2)
130 | if n[0] and n[1] < s * 10
131 | v = new sqroot(n[1])
132 | return "\\left(" + j + sgn2(x - j) + "\\frac{" + v.write() + "}{" + i + "}\\right)"
133 |
134 | a = (x + j) * i
135 | n = proxInt(a ^ 2)
136 | if n[0] and n[1] < s * 10
137 | v = new sqroot(n[1])
138 | return "\\left(-" + j + sgn2(x + j) + "\\frac{" + v.write() + "}{" + i + "}\\right)"
139 |
140 | for i from 2 to (s - 1)
141 | for k from 2 to (1 + (2 * s))
142 | for j from 1 to (2 * s)
143 |
144 | a = (x - (j / k))
145 | n = proxInt((a * i) ^ 2)
146 | if (n[0] and n[1] < s * 10) and Math.sqrt(n[1]) is not Math.floor(Math.sqrt(n[1]))
147 | v = new sqroot(n[1])
148 | return "\\left(\\frac{" + j + "}{" + k + "}" + sgn2(a) + "\\frac{" + v.write() + "}{" + i + "}\\right)"
149 |
150 | a = x + (j / k)
151 | n = proxInt((a * i) ^ 2)
152 | if (n[0] and n[1] < s * 10) and Math.sqrt(n[1]) is not Math.floor(Math.sqrt(n[1]))
153 | v = new sqroot(n[1])
154 | return "\\left(-\\frac{" + j + "}{" + k + "}" + sgn2(a) + "\\frac{" + v.write() + "}{" + i + "}\\right)"
155 |
156 | return x
157 |
158 | proxInt = (x) ->
159 | n = Math.round(x)
160 |
161 | if Math.abs(n - x) < (Math.abs(x) + 0.5) * 1e-8
162 | return [true, n]
163 | else
164 | return [false]
165 |
--------------------------------------------------------------------------------
/src/scripts/services/d3MultiLineChart.ls:
--------------------------------------------------------------------------------
1 | angular.module('app').factory 'd3MultiLineChart', ->
2 |
3 | /*
4 |
5 | multiLineChart returns a function that plots dataSeries in a line plot on a d3 selection.
6 | Data is joined to the selection and contains an array of data series - one for each line
7 | Each dataSeries is an array of [x,y] values to join.
8 |
9 | Undefined x and y values cause breaks in the plot (Pen up events)
10 |
11 | */
12 | multiLineChart = ->
13 |
14 | # Return the total extent of data in all the series for a given accessor function
15 | # Used to define the axis ranges as these are common to all series.
16 | seriesExtent = (series, accessor) ->
17 | extent = [1e12,-1e12]
18 | series.forEach (data) ->
19 | [min, max] = d3.extent(data, accessor)
20 | extent[0] = min if min < extent[0]
21 | extent[1] = max if max > extent[1]
22 | return extent
23 |
24 | chart = (selection) ->
25 |
26 | selection.each (series) ->
27 |
28 | # Convert data to standard representation greedily;
29 | # this is needed for nondeterministic accessors.
30 | series = series.map (data) ->
31 | data.map (d, i) -> [xValue.call(data, d, i), yValue.call(data, d, i)]
32 |
33 | xExtent = seriesExtent series, (d) -> d[0]
34 | xScale.domain(xExtent).range [0, width]
35 |
36 | # Update the y-scale.
37 | yExtent = seriesExtent series, (d) -> d[1]
38 | yExtent[0] = -yMax if yExtent[0] < -yMax
39 | yExtent[1] = yMax if yExtent[1] > yMax
40 | yScale.domain(yExtent).range [height, 0]
41 |
42 | # Select the plot element, if it exists, and join it with the data series
43 | gplot = d3.select(this).selectAll("g.plot")
44 | .data(series)
45 | .attr "width", width
46 | .attr "height", height
47 |
48 | gPlotEnter = gplot.enter().append("g").attr "class", "plot"
49 |
50 | firstPlot = gPlotEnter.filter (d, i) -> i == 0
51 |
52 | firstPlot.append('rect')
53 | .attr 'class', 'ground'
54 | .attr 'width', width
55 | .attr 'height', height
56 | .style 'fill', '#ffffff'
57 | .style 'stroke', '#000000'
58 | .style 'stroke-width', 0.5
59 | .style 'stroke-opacity', 0.3
60 |
61 | firstPlot.append('clipPath')
62 | .attr "id" "clip"
63 | .append("rect")
64 | .attr "width" width
65 | .attr "height" height
66 |
67 | gPlotEnter.append("path")
68 | .attr "class", "line"
69 | .style "stroke", (d, i)->color(i)
70 | .attr "clip-path", 'url(#clip)'
71 |
72 | # We are plotting all series on the same axes
73 | firstPlot.append("g").attr "class", "x axis"
74 | firstPlot.append("g").attr "class", "y axis"
75 |
76 | # Update the axes.
77 | gplot.select(".x.axis").attr("transform", "translate(0," + yScale.range()[0] + ")").call xAxis
78 | gplot.select(".y.axis").attr("transform", "translate(" + xScale.range()[0] + ", 0)").call yAxis
79 |
80 | # Update the ground
81 | gplot.select('rect.ground')
82 | .attr "width" width
83 | .attr "height" height
84 |
85 | # Update the clip path
86 | gplot.select('#clip rect')
87 | .attr "width" width
88 | .attr "height" height
89 |
90 | # Update the line path.
91 | gplot.select(".line").attr "d", line
92 |
93 |
94 | # The x-accessor for the path generator; xScale ∘ xValue.
95 | X = (d) -> xScale d[0]
96 |
97 | # The x-accessor for the path generator; yScale ∘ yValue.
98 | Y = (d) -> yScale d[1]
99 |
100 | width = 300
101 | height = 250
102 | yMax = 10 # default extent becomes -yMax to yMax.
103 | xValue = (d) -> d[0]
104 | yValue = (d) -> d[1]
105 |
106 | xScale = d3.scale.linear()
107 | yScale = d3.scale.linear()
108 | xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(6, 0)
109 | yAxis = d3.svg.axis().scale(yScale).orient("left").tickSize(6, 0)
110 | color = d3.scale.category10()
111 | line = d3.svg.line().x(X).y(Y)
112 |
113 | # Chop the line path into defined sub paths
114 | # Note that isNaN(null) is weirdly false in javascript.
115 | line.defined (d) -> not (d[0] is null or d[1] is null or isNaN d[0] or isNaN d[1])
116 |
117 | chart.width = (_) ->
118 | return width unless arguments.length
119 | width := _
120 | return chart
121 |
122 | chart.height = (_) ->
123 | return height unless arguments.length
124 | height := _
125 | return chart
126 |
127 | chart.x = (_) ->
128 | return xValue unless arguments.length
129 | xValue := _
130 | return chart
131 |
132 | chart.y = (_) ->
133 | return yValue unless arguments.length
134 | yValue := _
135 | return chart
136 |
137 | # TODO: refactor x and y extents so we can drag the chart around and
138 | # regenerate the plot area appropriately.
139 | chart.yMax = (_) ->
140 | return yMax unless arguments.length
141 | yMax := _
142 | return chart
143 |
144 | return chart
145 |
--------------------------------------------------------------------------------