<commit_before>module.exports = function(grunt) {

    // Load all grunt tasks
    require( 'load-grunt-tasks' )(grunt);

    // Configurable paths
    var config = {
        src: 'src',
        build: 'build'
    };

    // Project configuration.
    grunt.initConfig({
        pkg: grunt.file.readJSON( 'package.json' ),
        config: config,

        // Watch for file changes
        watch: {
            sass: {
                files: [ '<%= config.src %>/sass/**/*.scss' ],
                tasks: [ 'sass:dev', 'autoprefixer' ],
            },
            js: {
                files: [ '<%= config.src %>/js/{,*/}*.js' ],
                tasks: [ 'newer:jshint:js' ] // Runs jshint on changed files
            },
            images: {
                files: [ '<%= config.src %>/images/{,*/}*' ],
                tasks: [ 'newer:imagemin' ] // Runs imagemin on changed images
            },
            grunt: {
                files: [ 'Gruntfile.js' ],
                tasks: [ 'jshint:grunt' ]
            }
        },

        // Compile Sass
        sass: {
            dev: {
                files: {
                    '<%= config.src %>/css/style.css': '<%= config.src %>/sass/main.scss'
                },
                options: {
                    style: 'expanded',
                }
            }
        },

        // Autoprefix the compiled CSS
        autoprefixer: {
            dev: {
                options: {
                    browsers: [ 'last 2 version', 'ie 9' ]
                },
                src: '<%= config.src %>/css/style.css'
            }
        },

        // Make sure there are no obvious mistakes in the JS files
        jshint: {
            grunt: {
                src: [ 'Gruntfile.js' ]
            },
            js: {
                src: [ '<%= config.src %>/js/{,*/}*.js' ]
            }
        },

        // Minify CSS
        cssmin: {
            build: {
                expand: true,
                src: [ 'css/style.css' ],
                cwd: '<%= config.build %>/',
                dest: '<%= config.build %>/',
                ext: '.min.css'
            }
        },

        // Minify images
        imagemin: {
            dynamic: {
                files: [{
                    expand: true,
                    cwd: '<%= config.src %>/images/',
                    dest: '<%= config.src %>/images/',
                    src: [ '**/*.{png,jpg,gif}' ]
                }]
            }
        },

        // Process HTML files
        processhtml: {
            options: {

            },
            build: {
                files: {
                    '<%= config.build %>/index.html': ['<%= config.src %>/index.html'],
                    '<%= config.build %>/contact.html': ['<%= config.src %>/contact.html'],
                    '<%= config.build %>/test.html': ['<%= config.src %>/test.html']
                }
            }
        },

        // Clean the build folder
        clean: {
            build: {
                src: [ '<%= config.build %>/' ]
            },
        },

        // Copy to build folder
        copy: {
            build: {
                expand: true,
                cwd: '<%= config.src %>/',
                src: [
                    '**',
                    '!bower_components/**', // Skip Bower components
                    '!.css.map', // Skip Sass source maps
                ],
                dest: '<%= config.build %>/',
            },
        },
        // Copy Bower components to build folder
        bower: {
            build: {
                dest: '<%= config.build %>',
                js_dest: '<%= config.build %>/js',
                css_dest: '<%= config.build %>/css',
                options: {
                    ignorePackages: [
                        'google-code-prettify',
                        'modernizr' // Will be done by the task
                    ],
                    packageSpecific: {
                        // Move the minified version of jQuery
                        'jquery' : {
                            files: [ 'dist/jquery.min.js' ]
                        }
                    }
                }
            }
        },

        // Create custom Modernizr build in build folder
        modernizr: {
            build: {
                'devFile': '<%= config.src %>/bower_components/modernizr/modernizr.js',
                'outputFile': '<%= config.build %>/js/modernizr-custom.js',
                'uglify': false, // Will be done by the uglify task
                'extra': { 'load': false }, // Skip Modernizr.load (yepnope.js)

                // The tests we need - add more if you need them
                'tests': [ 'csscolumns', 'flexbox', 'css-calc' ],
            }
        },

        // Minify JS in build folder
        uglify: {
            build: {
                options: {
                    preserveComments: 'some'
                },
                files: [{
                    expand: true,
                    cwd: '<%= config.build %>/js',
                    dest: '<%= config.build %>/js',
                    src: [ '**/*.js', '!**/*.min.js' ], // Uglify all .js files, but skip .min.js files
                    ext: '.min.js'
              }],
            }
        },

        // Compress the build folder into a zip file
        compress: {
            build: {
                options: {
                    archive: '<%= config.build %>/<%= pkg.name %>-<%= pkg.version %>.zip'
                },
                cwd: '<%= config.build %>/',
                src: ['**/*'],
                dest: '<%= pkg.name %>/'
            }
        }
    });

    // Default task
    grunt.registerTask( 'default', [ 'watch' ] );

    // Pre-commit task
    grunt.registerTask( 'pre-commit', [ 'sass', 'autoprefixer', 'jshint', 'imagemin' ]);

    // Build task
    grunt.registerTask( 'build', [
        'clean:build',
        'copy:build',
        'bower:build',
        'modernizr:build',
        'uglify:build',
        'cssmin:build',
        'processhtml:build',
    ]);

};
<commit_msg>Fix type<commit_after>module.exports = function(grunt) {

    // Load all grunt tasks
    require( 'load-grunt-tasks' )(grunt);

    // Configurable paths
    var config = {
        src: 'src',
        build: 'build'
    };

    // Project configuration.
    grunt.initConfig({
        pkg: grunt.file.readJSON( 'package.json' ),
        config: config,

        // Watch for file changes
        watch: {
            sass: {
                files: [ '<%= config.src %>/sass/**/*.scss' ],
                tasks: [ 'sass:dev', 'autoprefixer' ],
            },
            js: {
                files: [ '<%= config.src %>/js/{,*/}*.js' ],
                tasks: [ 'newer:jshint:js' ] // Runs jshint on changed files
            },
            images: {
                files: [ '<%= config.src %>/images/{,*/}*' ],
                tasks: [ 'newer:imagemin' ] // Runs imagemin on changed images
            },
            grunt: {
                files: [ 'Gruntfile.js' ],
                tasks: [ 'jshint:grunt' ]
            }
        },

        // Compile Sass
        sass: {
            dev: {
                files: {
                    '<%= config.src %>/css/style.css': '<%= config.src %>/sass/main.scss'
                },
                options: {
                    style: 'expanded',
                }
            }
        },

        // Autoprefix the compiled CSS
        autoprefixer: {
            dev: {
                options: {
                    browsers: [ 'last 2 version', 'ie 9' ]
                },
                src: '<%= config.src %>/css/style.css'
            }
        },

        // Make sure there are no obvious mistakes in the JS files
        jshint: {
            grunt: {
                src: [ 'Gruntfile.js' ]
            },
            js: {
                src: [ '<%= config.src %>/js/{,*/}*.js' ]
            }
        },

        // Minify CSS
        cssmin: {
            build: {
                expand: true,
                src: [ 'css/style.css' ],
                cwd: '<%= config.build %>/',
                dest: '<%= config.build %>/',
                ext: '.min.css'
            }
        },

        // Minify images
        imagemin: {
            dynamic: {
                files: [{
                    expand: true,
                    cwd: '<%= config.src %>/images/',
                    dest: '<%= config.src %>/images/',
                    src: [ '**/*.{png,jpg,gif}' ]
                }]
            }
        },

        // Process HTML files
        processhtml: {
            options: {

            },
            build: {
                files: {
                    '<%= config.build %>/index.html': ['<%= config.src %>/index.html'],
                    '<%= config.build %>/contact.html': ['<%= config.src %>/contact.html'],
                    '<%= config.build %>/test.html': ['<%= config.src %>/test.html']
                }
            }
        },

        // Clean the build folder
        clean: {
            build: {
                src: [ '<%= config.build %>/' ]
            },
        },

        // Copy to build folder
        copy: {
            build: {
                expand: true,
                cwd: '<%= config.src %>/',
                src: [
                    '**',
                    '!bower_components/**', // Skip Bower components
                    '!.css.map', // Skip Sass source maps
                ],
                dest: '<%= config.build %>/',
            },
        },
        // Copy Bower components to build folder
        bower: {
            build: {
                dest: '<%= config.build %>',
                js_dest: '<%= config.build %>/js',
                css_dest: '<%= config.build %>/css',
                options: {
                    ignorePackages: [
                        'google-code-prettify',
                        'modernizr' // Will be done by the modernizr task
                    ],
                    packageSpecific: {
                        // Move the minified version of jQuery
                        'jquery' : {
                            files: [ 'dist/jquery.min.js' ]
                        }
                    }
                }
            }
        },

        // Create custom Modernizr build in build folder
        modernizr: {
            build: {
                'devFile': '<%= config.src %>/bower_components/modernizr/modernizr.js',
                'outputFile': '<%= config.build %>/js/modernizr-custom.js',
                'uglify': false, // Will be done by the uglify task
                'extra': { 'load': false }, // Skip Modernizr.load (yepnope.js)

                // The tests we need - add more if you need them
                'tests': [ 'csscolumns', 'flexbox', 'css-calc' ],
            }
        },

        // Minify JS in build folder
        uglify: {
            build: {
                options: {
                    preserveComments: 'some'
                },
                files: [{
                    expand: true,
                    cwd: '<%= config.build %>/js',
                    dest: '<%= config.build %>/js',
                    src: [ '**/*.js', '!**/*.min.js' ], // Uglify all .js files, but skip .min.js files
                    ext: '.min.js'
              }],
            }
        },

        // Compress the build folder into a zip file
        compress: {
            build: {
                options: {
                    archive: '<%= config.build %>/<%= pkg.name %>-<%= pkg.version %>.zip'
                },
                cwd: '<%= config.build %>/',
                src: ['**/*'],
                dest: '<%= pkg.name %>/'
            }
        }
    });

    // Default task
    grunt.registerTask( 'default', [ 'watch' ] );

    // Pre-commit task
    grunt.registerTask( 'pre-commit', [ 'sass', 'autoprefixer', 'jshint', 'imagemin' ]);

    // Build task
    grunt.registerTask( 'build', [
        'clean:build',
        'copy:build',
        'bower:build',
        'modernizr:build',
        'uglify:build',
        'cssmin:build',
        'processhtml:build',
    ]);

};
<|endoftext|>


<commit_before>// this accepts the format {lat: 0.0, lng: 0.0}
function placeMarker(location) {
  var marker = new google.maps.Marker({
    position: location,
    map: GoogleMaps.maps.gmap.instance
  });
}
if (Meteor.isClient) {
  Meteor.startup(function() {
    GoogleMaps.load();
    Tracker.autorun(function () {
      console.info(Geolocation.latLng());
      if (Geolocation.error() !== null) {
        var center = new google.maps.LatLng(Geolocation.latLng().lat, Geolocation.latLng().lng);
        GoogleMaps.maps.gmap.instance.panTo(center);
      };
    })
    // Detect clicks on maps
    google.maps.event.addListener(GoogleMaps.maps.gmap.instance, 'click', function(event) {
      placeMarker(event.latLng);
    }
  });
  Template.page_map.helpers({
    gmapOptions: function() {
      // Make sure the maps API has loaded
      if (GoogleMaps.loaded()) {
        // We can use the `ready` callback to interact with the map API on3ce the map is ready.
        GoogleMaps.ready('gmap', function(map) {
          // Add a marker to the map once it's ready
          // var marker = new google.maps.Marker({
          //   position: map.options.center,
          //   map: map.instance
          // });
      });

        // Map initialization options
        if (environment === 'production') {
          var lat = 0
          var lng = 0
        } else{
          var lat = 39.958
          var lng = -75.195
        };
        return {
          center: new google.maps.LatLng(Geolocation.latLng().lat || lat, Geolocation.latLng().lng) || lng,
          zoom: 18
        };
      }
    }
  });
}
<commit_msg>Fix for map click listener<commit_after>// this accepts the format {lat: 0.0, lng: 0.0}
function placeMarker(location) {
  var marker = new google.maps.Marker({
    position: location,
    map: GoogleMaps.maps.gmap.instance
  });
}
if (Meteor.isClient) {
  Meteor.startup(function() {
    GoogleMaps.load();
    Tracker.autorun(function () {
      console.info(Geolocation.latLng());
      if (Geolocation.error() !== null) {
        var center = new google.maps.LatLng(Geolocation.latLng().lat, Geolocation.latLng().lng);
        GoogleMaps.maps.gmap.instance.panTo(center);
      };
    })
    // function resizeIframe() {
    //   $('canvas')window.innerWidth
    //   window.innerHeight
    // }
    // Detect clicks on maps
  });
  Template.page_map.helpers({
    gmapOptions: function() {
      // Make sure the maps API has loaded
      if (GoogleMaps.loaded()) {
        // We can use the `ready` callback to interact with the map API on3ce the map is ready.
        GoogleMaps.ready('gmap', function(map) {
          google.maps.event.addListener(GoogleMaps.maps.gmap.instance, 'click', function(event) {
            placeMarker(event.latLng);
          })
          // Add a marker to the map once it's ready
          // var marker = new google.maps.Marker({
          //   position: map.options.center,
          //   map: map.instance
          // });
      });

        // Map initialization options
        if (environment === 'production') {
          var lat = 0
          var lng = 0
        } else{
          var lat = 39.958
          var lng = -75.195
        };
        return {
          center: new google.maps.LatLng(Geolocation.latLng().lat || lat, Geolocation.latLng().lng) || lng,
          zoom: 18
        };
      }
    }
  });
}
<|endoftext|>


<commit_before>/*
 * Generated on 2014-04-01
 * generator-assemble v0.4.11
 * https://github.com/assemble/generator-assemble
 *
 * Copyright (c) 2014 Hariadi Hinta
 * Licensed under the MIT license.
 */

'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// '<%= config.src %>/templates/pages/{,*/}*.hbs'
// use this if you want to match all subfolders:
// '<%= config.src %>/templates/pages/**/*.hbs'

module.exports = function(grunt) {

  require('time-grunt')(grunt);

  // Project configuration.
  grunt.initConfig({

    config: {
      src: 'src',
      dist: 'dist'
    },

    watch: {
      assemble: {
        files: ['<%= config.src %>/{content,data,templates,partials}/{,*/}*.{md,hbs,yml,json}'],
        tasks: ['assemble']
      },
      sass: {
        files: ['<%= config.src %>/assets/sass/**/{,*/}*.scss'],
        tasks: ['newer:sass']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= config.dist %>/{,*/}*.html',
          '<%= config.dist %>/assets/{,*/}*.css',
          '<%= config.dist %>/assets/{,*/}*.js',
          '<%= config.dist %>/assets/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    connect: {
      options: {
        port: 9000,
        livereload: 35729,
        // change this to '0.0.0.0' to access the server from outside
        hostname: 'localhost'
      },
      livereload: {
        options: {
          open: true,
          base: [
            '<%= config.dist %>'
          ]
        }
      }
    },

    assemble: {
      pages: {
        options: {
          flatten: true,
          assets: '<%= config.dist %>/assets',
          layout: '<%= config.src %>/templates/layouts/default.hbs',
          data: '<%= config.src %>/data/*.{json,yml}',
          partials: '<%= config.src %>/templates/partials/*.hbs',
          minify: {
            removeAttributeQuotes: false,
            removeEmptyAttributes: true,
          }
        },
        files: {
          '<%= config.dist %>/': ['<%= config.src %>/templates/pages/*.hbs']
        }
      }
    },

    sass: {
      dist: {
        options: {
          style: 'compressed'
        },
        files: [{
          expand: true,
          cwd: '<%= config.src %>/assets/sass',
          src: ['*.scss'],
          dest: '<%= config.dist %>/assets/css',
          ext: '.css'
        }]
      }
    },

    imagemin: {
      static: {
        options: {
          optimizationLevel: 3
        },
        files: [{
          expand: true,
          cwd: '<%= config.src %>/assets/img',
          src: ['**/*.{png,jpg,gif}'],
          dest: '<%= config.dist %>/assets/img'
        }]
      }
    },

    // Before generating any new files,
    // remove any previously-created files.
    clean: ['<%= config.dist %>/**/*.{html,xml,css}']

  });

  grunt.loadNpmTasks('assemble');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.loadNpmTasks('grunt-newer');
  grunt.loadNpmTasks('grunt-contrib-imagemin');

  grunt.registerTask('server', [
    'clean',
    'assemble',
    'sass',
    'connect:livereload',
    'watch'
  ]);

  grunt.registerTask('build', [
    'clean',
    'sass',
    'assemble',
    'imagemin'
  ]);

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

};
<commit_msg>Fix server task<commit_after>/*
 * Generated on 2014-04-01
 * generator-assemble v0.4.11
 * https://github.com/assemble/generator-assemble
 *
 * Copyright (c) 2014 Hariadi Hinta
 * Licensed under the MIT license.
 */

'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// '<%= config.src %>/templates/pages/{,*/}*.hbs'
// use this if you want to match all subfolders:
// '<%= config.src %>/templates/pages/**/*.hbs'

module.exports = function(grunt) {

  require('time-grunt')(grunt);

  // Project configuration.
  grunt.initConfig({

    config: {
      src: 'src',
      dist: 'dist'
    },

    watch: {
      assemble: {
        files: ['<%= config.src %>/{content,data,templates,partials}/{,*/}*.{md,hbs,yml,json}'],
        tasks: ['assemble']
      },
      sass: {
        files: ['<%= config.src %>/assets/sass/**/{,*/}*.scss'],
        tasks: ['newer:sass']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= config.dist %>/{,*/}*.html',
          '<%= config.dist %>/assets/{,*/}*.css',
          '<%= config.dist %>/assets/{,*/}*.js',
          '<%= config.dist %>/assets/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    connect: {
      options: {
        port: 9000,
        livereload: 35729,
        // change this to '0.0.0.0' to access the server from outside
        hostname: 'localhost'
      },
      livereload: {
        options: {
          open: true,
          base: [
            '<%= config.dist %>'
          ]
        }
      }
    },

    assemble: {
      pages: {
        options: {
          flatten: true,
          assets: '<%= config.dist %>/assets',
          layout: '<%= config.src %>/templates/layouts/default.hbs',
          data: '<%= config.src %>/data/*.{json,yml}',
          partials: '<%= config.src %>/templates/partials/*.hbs',
          minify: {
            removeAttributeQuotes: false,
            removeEmptyAttributes: true,
          }
        },
        files: {
          '<%= config.dist %>/': ['<%= config.src %>/templates/pages/*.hbs']
        }
      }
    },

    sass: {
      dist: {
        options: {
          style: 'compressed'
        },
        files: [{
          expand: true,
          cwd: '<%= config.src %>/assets/sass',
          src: ['*.scss'],
          dest: '<%= config.dist %>/assets/css',
          ext: '.css'
        }]
      }
    },

    imagemin: {
      static: {
        options: {
          optimizationLevel: 3
        },
        files: [{
          expand: true,
          cwd: '<%= config.src %>/assets/img',
          src: ['**/*.{png,jpg,gif}'],
          dest: '<%= config.dist %>/assets/img'
        }]
      }
    },

    // Before generating any new files,
    // remove any previously-created files.
    clean: ['<%= config.dist %>/**/*.{html,xml,css}']

  });

  grunt.loadNpmTasks('assemble');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.loadNpmTasks('grunt-newer');
  grunt.loadNpmTasks('grunt-contrib-imagemin');

  grunt.registerTask('server', [
    'build',
    'connect:livereload',
    'watch'
  ]);

  grunt.registerTask('build', [
    'clean',
    'sass',
    'assemble',
    'imagemin'
  ]);

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

};
<|endoftext|>


<commit_before>var user;

$(setup)

function setup() {
  user = parseUserCookie()
  $.fn.select2.defaults.formatNoMatches = 'Ei tuloksia'
  $.fn.select2.defaults.formatSearching = 'Haetaan...'
  $.fn.select2.defaults.adaptDropdownCssClass = function(c) {  return c == 'required' ? c : null }

  var login = loginPage()
  var error = errorDialog()

  $.ajaxSetup({ dataType: 'json', processData: false, contentType: 'application/json' })

  $(document).ajaxError(function(e, req) {
    if (req.status == 403) {
      login.show()
    } else {
      error.show()
    }
  })

  $('#overlay').on('click', closeDialog)

  $('body').on('click', '.dialog', stopPropagation)

  var navigation = navi()
  searchPage()
  programDetails()
  buyerPage()
  billingPage()
  navigation.start()
}

function errorDialog() {
  var $overlay = $('#error-overlay')
  var $dialog = $('#error-dialog')
  $dialog.find('a').click(function(e) { e.preventDefault(); location.reload() })
  return { show: function() { $dialog.add($overlay).show() } }
}

function loginPage() {
  var $overlay = $('#login-overlay')
  var $form = $('#login').submit(function(e) { e.preventDefault() })
  var $username = $form.find('input[name="username"]').on('input', checkInput)
  var $password = $form.find('input[name="password"]').on('input', checkInput)
  var $feedback = $form.find('.feedback')
  var $button = $form.find('button')
  var $info = $('#header .user-info').toggle(!!user)
  $info.find('.name').text(user ? user.name : '')

  $info.find('.logout').click(function() {
    $.post('/logout').done(function() { location.reload() })
  })

  $form.on('validate', function() {
    var invalid = $username.hasClass('invalid') || $password.hasClass('invalid')
    $button.prop('disabled', invalid? 'disabled' : '')
    $feedback.slideUp()
  })

  $button.click(function() {
    $.post('/login', JSON.stringify({ username: $username.val(), password: $password.val() }))
      .done(function() { location.reload() })
      .fail(function(jqXHR) {
        if (jqXHR.status == 403) {
          $password.val('').trigger('input').focus()
          $feedback.slideDown()
        }
      })
  })

  return { show: show }

  function show() {
    $form.add($overlay).show()
    $username.focus()
  }
  function checkInput() {
    $(this).toggleClass('invalid', $(this).val() === '').trigger('validate')
  }
}

function navi() {
  var $navi = $('#header .navi')

  $navi.find('a[data-href="#billing-page"]').parent().toggle(hasRole('kavi'))

  $navi.find('a').on('click', function(e) {
    e.preventDefault()
    show($(this)).trigger('show')
  })

  function start() {
    var hash = location.hash
    if (hash == '') {
      $navi.find('a:first').click()
    } else {
      var parts = hash.split('/').map(decodeURIComponent)
      var $a = $navi.find('a[href='+parts.shift()+']')
      show($a).trigger('show', parts)
    }
  }

  function show($a) {
    $navi.find('a.active').removeClass('active')
    $a.addClass('active')
    $('body').children('.page').hide()
    return $($a.data('href')).show()
  }

  return { start: start }
}

function buyerPage() { $('#buyer-page').on('show', function() { location.hash = '#tilaajat' }) }
function billingPage() {
  $.dateRangePickerLanguages.fi = {
    'selected': 'Valittu:',
    'day':'Päivä',
    'days': ' päivää',
    'apply': 'Sulje',
    'week-1' : 'MA',
    'week-2' : 'TI',
    'week-3' : 'KE',
    'week-4' : 'TO',
    'week-5' : 'PE',
    'week-6' : 'LA',
    'week-7' : 'SU',
    'month-name': ['Tammikuu','Helmikuu','Maaliskuu','Huhtikuu','Toukokuu','Kesäkuu','Heinäkuu','Elokuu','Syyskuu','Lokakuu','Marraskuu','Joulukuu'],
    'shortcuts' : 'Valitse',
    'past': 'Past',
    'following':'Seuraavat',
    'previous' : 'edellinen',
    'prev-week' : 'viikko',
    'prev-month' : 'kuukausi',
    'prev-year' : 'vuosi',
    'next':'seuraava',
    'next-week':'viikko',
    'next-month':'kuukausi',
    'next-year':'vuosi',
    'less-than' : 'Date range should not be more than %d days',
    'more-than' : 'Date range should not be less than %d days',
    'default-more' : 'Please select a date range longer than %d days',
    'default-single' : 'Please select a date',
    'default-less' : 'Please select a date range less than %d days',
    'default-range' : 'Please select a date range between %d and %d days',
    'default-default': 'Ole hyvä ja valitse alku- ja loppupäivä'
  }

  var $page = $('#billing-page')
  var $datePicker = $page.find('.datepicker')
  var format = 'DD.MM.YYYY'

  $datePicker.click(function(e) {
    e.preventDefault()
  })

  $datePicker.dateRangePicker({
    language: 'fi',
    format: format,
    separator: ' - ',
    startOfWeek: 'monday',
    shortcuts: {'next-days': null, 'next': null, 'prev-days': null, prev: ['month']},
    getValue: function() { return $datePicker.find('span').text() },
    setValue: function(s) { $datePicker.find('span').text(s) }
  }).bind('datepicker-change',function(event, obj) {
    fetchInvoiceRows(obj.date1, obj.date2)
  })
  var first = moment().subtract('months', 1).startOf('month')
  var last = moment().subtract('months', 1).endOf('month')
  $datePicker.data('dateRangePicker').setDateRange(first.format(format),last.format(format))
  fetchInvoiceRows(first, last)

  function fetchInvoiceRows(date1, date2) {
    var begin = moment(date1).format(format)
    var end = moment(date2).format(format)
    $.get('/invoicerows/' + begin + '/' + end).done(function(rows) {
      var $accounts = $page.find('.accounts')
      var accounts = _.groupBy(rows, function(x) { return x.account.name })
      $accounts.empty()
      _.pairs(accounts).forEach(function(account) {
        var name = account[0]
        var rows = account[1]
        var $account = $("#templates").find('.invoice-account').clone()
        var $rows = $account.find('table')
        $account.find('.name').text(name)
        rows.forEach(function(row) {
          var $row = $("#templates").find('.invoicerow tr').clone()
          $row
            .find('.type').text(enums.invoiceRowType[row.type]).end()
            .find('.name').text(row.name).end()
            .find('.duration').text(utils.secondsToDuration(row.duration)).end()
            .find('.registrationDate').text(moment(row.registrationDate).format(format)).end()
            .find('.price').text(formatCentsAsEuros(row.price)).end()
          $rows.find('tbody').append($row)
        })
        $rows.find('tfoot span').text(formatCentsAsEuros(_.reduce(rows, function(acc, row) { return acc + row.price }, 0)))
        $accounts.append($account)
      })
    })
  }

  function formatCentsAsEuros(cents) {
    return cents / 100 + ' €'
  }

  $page.on('show', function() { location.hash = '#laskutus'})
}

function stopPropagation(e) { e.stopPropagation() }

function isNotEmpty(val) {
  return (val.trim().length) > 0
}

function isEmail(txt) {
  var regexp = /^([A-Za-z0-9\x27\x2f!#$%&*+=?^_`{|}~-]+(\.[A-Za-z0-9\x27\x2f!#$%&*+=?^_`{|}~-]+)*)@(([a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]|[a-zA-Z0-9]{1,63})(\.([a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]|[a-zA-Z0-9]{1,63}))*\.[a-zA-Z0-9]{2,63})$/
  return regexp.test(txt)
}

function validate(f) {
  return function() {
    $(this).toggleClass('invalid', !f($(this).val())).trigger('validation')
  }
}

function validateTextChange($el, validatorFn) {
  var validator = validate(validatorFn)
  $el.on('input change validate', validator).on('blur', function() { $(this).addClass('touched') })
}

function requiredCheckboxGroup($el) {
  $el.on('change validate', 'input:checkbox', function() {
    $el.toggleClass('invalid', $el.find('input:checkbox:checked').length == 0).trigger('validation')
  })
}

function showDialog($html) {
  $('#overlay').show()
  $('#dialog').show().append($html)
}

function closeDialog() {
  $('#dialog').empty().hide()
  $('#overlay').hide()
}

$.fn.throttledInput = function(fn) {
  return $(this).each(function() {
    var prev = undefined
    var timeout = null
    var $input = $(this).on('keyup', function() {
      var txt = $input.val()
      var that = this
      if (timeout) clearTimeout(timeout)
      timeout = setTimeout(function() {
        if (prev == txt) return
        prev = txt
        fn.call(that, txt)
      }, 400)
    })
    $input.on('reset', reset)
    $input.on('fire', function() {
      reset()
      fn.call(this, prev)
    })

    function reset() {
      if (timeout) clearTimeout(timeout)
      prev = $input.val()
    }
  })
}

function ageLimitIcon(summary) {
  return 'images/agelimit-'+summary.age.toString().toLowerCase()+'.png'
}
function warningIcons(summary) {
  return summary.warnings.map(function(w) { return $('<span>', { class:'warning ' + w.category }) })
}

function notIn(arr, el) {
  return _.indexOf(arr, el) === -1
}

function parseUserCookie() {
  var cookie = $.cookie('user')
  if (!cookie) return null
  return JSON.parse(cookie.substring(4, cookie.lastIndexOf('.')))
}

function hasRole(role) {
  var roles = ['root', 'kavi', 'user']
  if (!user) return false
  return roles.indexOf(role) >= roles.indexOf(user.role)
}<commit_msg>Billing: do not load invoicerows on initial page load<commit_after>var user;

$(setup)

function setup() {
  user = parseUserCookie()
  $.fn.select2.defaults.formatNoMatches = 'Ei tuloksia'
  $.fn.select2.defaults.formatSearching = 'Haetaan...'
  $.fn.select2.defaults.adaptDropdownCssClass = function(c) {  return c == 'required' ? c : null }

  var login = loginPage()
  var error = errorDialog()

  $.ajaxSetup({ dataType: 'json', processData: false, contentType: 'application/json' })

  $(document).ajaxError(function(e, req) {
    if (req.status == 403) {
      login.show()
    } else {
      error.show()
    }
  })

  $('#overlay').on('click', closeDialog)

  $('body').on('click', '.dialog', stopPropagation)

  var navigation = navi()
  searchPage()
  programDetails()
  buyerPage()
  billingPage()
  navigation.start()
}

function errorDialog() {
  var $overlay = $('#error-overlay')
  var $dialog = $('#error-dialog')
  $dialog.find('a').click(function(e) { e.preventDefault(); location.reload() })
  return { show: function() { $dialog.add($overlay).show() } }
}

function loginPage() {
  var $overlay = $('#login-overlay')
  var $form = $('#login').submit(function(e) { e.preventDefault() })
  var $username = $form.find('input[name="username"]').on('input', checkInput)
  var $password = $form.find('input[name="password"]').on('input', checkInput)
  var $feedback = $form.find('.feedback')
  var $button = $form.find('button')
  var $info = $('#header .user-info').toggle(!!user)
  $info.find('.name').text(user ? user.name : '')

  $info.find('.logout').click(function() {
    $.post('/logout').done(function() { location.reload() })
  })

  $form.on('validate', function() {
    var invalid = $username.hasClass('invalid') || $password.hasClass('invalid')
    $button.prop('disabled', invalid? 'disabled' : '')
    $feedback.slideUp()
  })

  $button.click(function() {
    $.post('/login', JSON.stringify({ username: $username.val(), password: $password.val() }))
      .done(function() { location.reload() })
      .fail(function(jqXHR) {
        if (jqXHR.status == 403) {
          $password.val('').trigger('input').focus()
          $feedback.slideDown()
        }
      })
  })

  return { show: show }

  function show() {
    $form.add($overlay).show()
    $username.focus()
  }
  function checkInput() {
    $(this).toggleClass('invalid', $(this).val() === '').trigger('validate')
  }
}

function navi() {
  var $navi = $('#header .navi')

  $navi.find('a[data-href="#billing-page"]').parent().toggle(hasRole('kavi'))

  $navi.find('a').on('click', function(e) {
    e.preventDefault()
    show($(this)).trigger('show')
  })

  function start() {
    var hash = location.hash
    if (hash == '') {
      $navi.find('a:first').click()
    } else {
      var parts = hash.split('/').map(decodeURIComponent)
      var $a = $navi.find('a[href='+parts.shift()+']')
      show($a).trigger('show', parts)
    }
  }

  function show($a) {
    $navi.find('a.active').removeClass('active')
    $a.addClass('active')
    $('body').children('.page').hide()
    return $($a.data('href')).show()
  }

  return { start: start }
}

function buyerPage() { $('#buyer-page').on('show', function() { location.hash = '#tilaajat' }) }
function billingPage() {
  $.dateRangePickerLanguages.fi = {
    'selected': 'Valittu:',
    'day':'Päivä',
    'days': ' päivää',
    'apply': 'Sulje',
    'week-1' : 'MA',
    'week-2' : 'TI',
    'week-3' : 'KE',
    'week-4' : 'TO',
    'week-5' : 'PE',
    'week-6' : 'LA',
    'week-7' : 'SU',
    'month-name': ['Tammikuu','Helmikuu','Maaliskuu','Huhtikuu','Toukokuu','Kesäkuu','Heinäkuu','Elokuu','Syyskuu','Lokakuu','Marraskuu','Joulukuu'],
    'shortcuts' : 'Valitse',
    'past': 'Past',
    'following':'Seuraavat',
    'previous' : 'edellinen',
    'prev-week' : 'viikko',
    'prev-month' : 'kuukausi',
    'prev-year' : 'vuosi',
    'next':'seuraava',
    'next-week':'viikko',
    'next-month':'kuukausi',
    'next-year':'vuosi',
    'less-than' : 'Date range should not be more than %d days',
    'more-than' : 'Date range should not be less than %d days',
    'default-more' : 'Please select a date range longer than %d days',
    'default-single' : 'Please select a date',
    'default-less' : 'Please select a date range less than %d days',
    'default-range' : 'Please select a date range between %d and %d days',
    'default-default': 'Ole hyvä ja valitse alku- ja loppupäivä'
  }

  var $page = $('#billing-page')
  var $datePicker = $page.find('.datepicker')
  var format = 'DD.MM.YYYY'

  $datePicker.click(function(e) {
    e.preventDefault()
  })

  $datePicker.dateRangePicker({
    language: 'fi',
    format: format,
    separator: ' - ',
    startOfWeek: 'monday',
    shortcuts: {'next-days': null, 'next': null, 'prev-days': null, prev: ['month']},
    getValue: function() { return $datePicker.find('span').text() },
    setValue: function(s) { $datePicker.find('span').text(s) }
  }).bind('datepicker-change',function(event, obj) {
    fetchInvoiceRows(obj.date1, obj.date2)
  })

  function fetchInvoiceRows(date1, date2) {
    var begin = moment(date1).format(format)
    var end = moment(date2).format(format)
    $.get('/invoicerows/' + begin + '/' + end).done(function(rows) {
      var $accounts = $page.find('.accounts')
      var accounts = _.groupBy(rows, function(x) { return x.account.name })
      $accounts.empty()
      _.pairs(accounts).forEach(function(account) {
        var name = account[0]
        var rows = account[1]
        var $account = $("#templates").find('.invoice-account').clone()
        var $rows = $account.find('table')
        $account.find('.name').text(name)
        rows.forEach(function(row) {
          var $row = $("#templates").find('.invoicerow tr').clone()
          $row
            .find('.type').text(enums.invoiceRowType[row.type]).end()
            .find('.name').text(row.name).end()
            .find('.duration').text(utils.secondsToDuration(row.duration)).end()
            .find('.registrationDate').text(moment(row.registrationDate).format(format)).end()
            .find('.price').text(formatCentsAsEuros(row.price)).end()
          $rows.find('tbody').append($row)
        })
        $rows.find('tfoot span').text(formatCentsAsEuros(_.reduce(rows, function(acc, row) { return acc + row.price }, 0)))
        $accounts.append($account)
      })
    })
  }

  function formatCentsAsEuros(cents) {
    return cents / 100 + ' €'
  }

  $page.on('show', function() {
    location.hash = '#laskutus'
    var first = moment().subtract('months', 1).startOf('month')
    var last = moment().subtract('months', 1).endOf('month')
    $datePicker.data('dateRangePicker').setDateRange(first.format(format),last.format(format))
    fetchInvoiceRows(first, last)
  })
}

function stopPropagation(e) { e.stopPropagation() }

function isNotEmpty(val) {
  return (val.trim().length) > 0
}

function isEmail(txt) {
  var regexp = /^([A-Za-z0-9\x27\x2f!#$%&*+=?^_`{|}~-]+(\.[A-Za-z0-9\x27\x2f!#$%&*+=?^_`{|}~-]+)*)@(([a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]|[a-zA-Z0-9]{1,63})(\.([a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]|[a-zA-Z0-9]{1,63}))*\.[a-zA-Z0-9]{2,63})$/
  return regexp.test(txt)
}

function validate(f) {
  return function() {
    $(this).toggleClass('invalid', !f($(this).val())).trigger('validation')
  }
}

function validateTextChange($el, validatorFn) {
  var validator = validate(validatorFn)
  $el.on('input change validate', validator).on('blur', function() { $(this).addClass('touched') })
}

function requiredCheckboxGroup($el) {
  $el.on('change validate', 'input:checkbox', function() {
    $el.toggleClass('invalid', $el.find('input:checkbox:checked').length == 0).trigger('validation')
  })
}

function showDialog($html) {
  $('#overlay').show()
  $('#dialog').show().append($html)
}

function closeDialog() {
  $('#dialog').empty().hide()
  $('#overlay').hide()
}

$.fn.throttledInput = function(fn) {
  return $(this).each(function() {
    var prev = undefined
    var timeout = null
    var $input = $(this).on('keyup', function() {
      var txt = $input.val()
      var that = this
      if (timeout) clearTimeout(timeout)
      timeout = setTimeout(function() {
        if (prev == txt) return
        prev = txt
        fn.call(that, txt)
      }, 400)
    })
    $input.on('reset', reset)
    $input.on('fire', function() {
      reset()
      fn.call(this, prev)
    })

    function reset() {
      if (timeout) clearTimeout(timeout)
      prev = $input.val()
    }
  })
}

function ageLimitIcon(summary) {
  return 'images/agelimit-'+summary.age.toString().toLowerCase()+'.png'
}
function warningIcons(summary) {
  return summary.warnings.map(function(w) { return $('<span>', { class:'warning ' + w.category }) })
}

function notIn(arr, el) {
  return _.indexOf(arr, el) === -1
}

function parseUserCookie() {
  var cookie = $.cookie('user')
  if (!cookie) return null
  return JSON.parse(cookie.substring(4, cookie.lastIndexOf('.')))
}

function hasRole(role) {
  var roles = ['root', 'kavi', 'user']
  if (!user) return false
  return roles.indexOf(role) >= roles.indexOf(user.role)
}<|endoftext|>


<commit_before>module.exports = function(grunt) {
  'use strict';

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    config: {
      banner: '/*\n' +
        ' TechanJS v<%= pkg.version %>\n' +
        " (c) 2014 - <%= grunt.template.today('yyyy') %> Andre Dumas | https://github.com/andredumas/techan.js\n" +
        '*/',
      dist: 'techan-<%= pkg.version %>'
    },

    clean: {
      dist: 'dist',
      build: 'build'
    },

    filegen: {
      version: {
        options: {
          content: "'use strict';module.exports='<%= pkg.version %>';"
        },
        dest: '<%= clean.build %>/version.js'
      }
    },

    browserify: {
      dev: {
        options: {
          // Thanks https://github.com/amitayd/grunt-browserify-jasmine-node-example/blob/2488181e29b09226f2a87202a851f996820eafb6/Gruntfile.js#L51
          require: grunt.file.expand({filter: 'isFile'}, './src/**/*.js', './<%= filegen.version.dest %>'),
          bundleOptions: {
            debug: true
          }
        },
        src: 'src/techan.js',
        dest: '<%= clean.build %>/techan-bundle.js'
      },
      test: {
        options: {
          external: 'src/**/*.js',
          bundleOptions: {
            debug: true
          }
        },
        src: 'test/spec/bundle/**/*.js',
        dest: '<%= clean.build %>/specs-bundle.js'
      },
      dist: {
        options: {
          bundleOptions: {
            standalone: 'techan'
          }
        },
        src: 'src/techan.js',
        dest: '<%= clean.dist %>/techan.js'
      }
    },

    usebanner: {
      options: {
        position: 'top',
        banner: '<%= config.banner %>',
        linebreak: true
      },
      dist: {
        files: {
          src: '<%= browserify.dist.dest %>'
        }
      }
    },

    jsonlint: {
      bower: {
        src: 'bower.json'
      },
      dist: {
        src: 'package.json'
      }
    },

    jshint: {
      options: {
        jshintrc: ".jshintrc"
      },
      dev: {
        src: ['src/**/*.js', 'lib/**/*.js', 'Gruntfile.js', 'test/**/*.js', '<%= filegen.version.dest %>']
      }
    },

    jscs: {
      options: {
        config: '.jscs.json'
      },
      dev: {
        src: ['src/**/*.js', 'Gruntfile.js', 'test/**/*.js', '<%= filegen.version.dest %>']
      }
    },

    watch: {
      files: ['<%= jshint.dev.src %>', 'examples/**'],
      tasks: ['examples', 'dev']
    },

    jasmine: {
      options: {
        vendor: ['bower_components/d3/d3.js', 'test/spec/common/**/*.js'],
        keepRunner: true
      },
      test: {
        options: {
          specs: '<%= browserify.test.dest %>',
          outfile: '<%= clean.build %>/bundleSpecRunner.html'
        },
        src: '<%= browserify.dev.dest %>'
      },
      dist: {
        options: {
          specs: 'test/spec/standalone/**/*.js',
          outfile: '<%= clean.build %>/standaloneSpecRunner.html'
        },
        src: '<%= browserify.dist.dest %>'
      },
      minify: {
        options: {
          specs: 'test/spec/standalone/**/*.js',
          outfile: '<%= clean.build %>/standaloneMinSpecRunner.html'
        },
        src: '<%= uglify.dist.dest %>'
      }
    },

    uglify: {
      options: {
        banner: '<%= config.banner %>',
        sourceMap: true,
        report: 'min'
      },
      dist: {
        src: '<%= browserify.dist.dest %>',
        dest: '<%= clean.dist %>/techan.min.js'
      }
    },

    compress: {
      dist: {
        options: {
          archive: '<%= clean.dist %>/techan.zip'
        },
        expand: true,
        cwd: '<%= clean.dist %>',
        src: ['*.js*'],
        dest: '<%= config.dist %>'
      }
    },

    bump: {
      options: {
        commit: true,
        commitMessage: 'Release %VERSION%',
        commitFiles: ['-a'],
        createTag: true,
        tagName: '%VERSION%',
        tagMessage: '%VERSION%',
        push: false
      }
    },

    connect: {
      server: {}
    },

    copy: {
      examples: {
        files: [
          { expand: true, cwd: 'examples', src: ['**', '!**/*.html'], dest: '<%= clean.build %>/examples/' }
        ]
      }
    },

    replace: {
      main: {
        options: {
          patterns: [
            { match: /http:\/\/d3js\.org\/d3\.v3\.min\.js/g, replacement: '/bower_components/d3/d3.js' },
            { match: /http:\/\/techanjs\.org\/techan\.min\.js/g, replacement: '/<%= browserify.dist.dest %>' }
          ]
        },
        files: [
          { expand: true, cwd: 'examples', src: '**/*.html', dest: '<%= clean.build %>/examples/' }
        ]
      }
    }

  });

  require('load-grunt-tasks')(grunt);
  grunt.loadTasks('lib/grunt');

  grunt.registerTask('lint', ['newer:jshint', 'newer:jscs']);
  grunt.registerTask('dev', ['lint', 'newer:browserify:dev', 'newer:browserify:test', 'jasmine:test']);
  grunt.registerTask('examples', ['newer:replace', 'newer:copy']);
  grunt.registerTask('dist', ['browserify:dist', 'usebanner', 'jasmine:dist']);
  grunt.registerTask('minify', ['uglify', 'jasmine:minify']);
  grunt.registerTask('serve', ['filegen:version', 'examples', 'dev', 'connect', 'watch']);
  grunt.registerTask('release:pre', ['bump-only:prerelease', 'default']);
  grunt.registerTask('release:minor', ['bump-only:minor', 'default', 'bump-commit']);
  grunt.registerTask('release:major', ['bump-only:major', 'default', 'bump-commit']);

  grunt.registerTask('default', ['jsonlint', 'bower', 'clean', 'filegen:version', 'dev', 'dist', 'minify', 'compress']);
};<commit_msg>Correctly reference the dev bundle build for examples<commit_after>module.exports = function(grunt) {
  'use strict';

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    config: {
      banner: '/*\n' +
        ' TechanJS v<%= pkg.version %>\n' +
        " (c) 2014 - <%= grunt.template.today('yyyy') %> Andre Dumas | https://github.com/andredumas/techan.js\n" +
        '*/',
      dist: 'techan-<%= pkg.version %>'
    },

    clean: {
      dist: 'dist',
      build: 'build'
    },

    filegen: {
      version: {
        options: {
          content: "'use strict';module.exports='<%= pkg.version %>';"
        },
        dest: '<%= clean.build %>/version.js'
      }
    },

    browserify: {
      dev: {
        options: {
          // Thanks https://github.com/amitayd/grunt-browserify-jasmine-node-example/blob/2488181e29b09226f2a87202a851f996820eafb6/Gruntfile.js#L51
          require: grunt.file.expand({filter: 'isFile'}, './src/**/*.js', './<%= filegen.version.dest %>'),
          bundleOptions: {
            debug: true,
            standalone: 'techan'
          }
        },
        src: 'src/techan.js',
        dest: '<%= clean.build %>/techan-bundle.js'
      },
      test: {
        options: {
          external: 'src/**/*.js',
          bundleOptions: {
            debug: true
          }
        },
        src: 'test/spec/bundle/**/*.js',
        dest: '<%= clean.build %>/specs-bundle.js'
      },
      dist: {
        options: {
          bundleOptions: {
            standalone: 'techan'
          }
        },
        src: 'src/techan.js',
        dest: '<%= clean.dist %>/techan.js'
      }
    },

    usebanner: {
      options: {
        position: 'top',
        banner: '<%= config.banner %>',
        linebreak: true
      },
      dist: {
        files: {
          src: '<%= browserify.dist.dest %>'
        }
      }
    },

    jsonlint: {
      bower: {
        src: 'bower.json'
      },
      dist: {
        src: 'package.json'
      }
    },

    jshint: {
      options: {
        jshintrc: ".jshintrc"
      },
      dev: {
        src: ['src/**/*.js', 'lib/**/*.js', 'Gruntfile.js', 'test/**/*.js', '<%= filegen.version.dest %>']
      }
    },

    jscs: {
      options: {
        config: '.jscs.json'
      },
      dev: {
        src: ['src/**/*.js', 'Gruntfile.js', 'test/**/*.js', '<%= filegen.version.dest %>']
      }
    },

    watch: {
      files: ['<%= jshint.dev.src %>', 'examples/**'],
      tasks: ['examples', 'dev']
    },

    jasmine: {
      options: {
        vendor: ['bower_components/d3/d3.js', 'test/spec/common/**/*.js'],
        keepRunner: true
      },
      test: {
        options: {
          specs: '<%= browserify.test.dest %>',
          outfile: '<%= clean.build %>/bundleSpecRunner.html'
        },
        src: '<%= browserify.dev.dest %>'
      },
      dist: {
        options: {
          specs: 'test/spec/standalone/**/*.js',
          outfile: '<%= clean.build %>/standaloneSpecRunner.html'
        },
        src: '<%= browserify.dist.dest %>'
      },
      minify: {
        options: {
          specs: 'test/spec/standalone/**/*.js',
          outfile: '<%= clean.build %>/standaloneMinSpecRunner.html'
        },
        src: '<%= uglify.dist.dest %>'
      }
    },

    uglify: {
      options: {
        banner: '<%= config.banner %>',
        sourceMap: true,
        report: 'min'
      },
      dist: {
        src: '<%= browserify.dist.dest %>',
        dest: '<%= clean.dist %>/techan.min.js'
      }
    },

    compress: {
      dist: {
        options: {
          archive: '<%= clean.dist %>/techan.zip'
        },
        expand: true,
        cwd: '<%= clean.dist %>',
        src: ['*.js*'],
        dest: '<%= config.dist %>'
      }
    },

    bump: {
      options: {
        commit: true,
        commitMessage: 'Release %VERSION%',
        commitFiles: ['-a'],
        createTag: true,
        tagName: '%VERSION%',
        tagMessage: '%VERSION%',
        push: false
      }
    },

    connect: {
      server: {}
    },

    copy: {
      examples: {
        files: [
          { expand: true, cwd: 'examples', src: ['**', '!**/*.html'], dest: '<%= clean.build %>/examples/' }
        ]
      }
    },

    replace: {
      main: {
        options: {
          patterns: [
            { match: /http:\/\/d3js\.org\/d3\.v3\.min\.js/g, replacement: '/bower_components/d3/d3.js' },
            { match: /http:\/\/techanjs\.org\/techan\.min\.js/g, replacement: '/<%= browserify.dev.dest %>' }
          ]
        },
        files: [
          { expand: true, cwd: 'examples', src: '**/*.html', dest: '<%= clean.build %>/examples/' }
        ]
      }
    }

  });

  require('load-grunt-tasks')(grunt);
  grunt.loadTasks('lib/grunt');

  grunt.registerTask('lint', ['newer:jshint', 'newer:jscs']);
  grunt.registerTask('dev', ['lint', 'newer:browserify:dev', 'newer:browserify:test', 'jasmine:test']);
  grunt.registerTask('examples', ['newer:replace', 'newer:copy']);
  grunt.registerTask('dist', ['browserify:dist', 'usebanner', 'jasmine:dist']);
  grunt.registerTask('minify', ['uglify', 'jasmine:minify']);
  grunt.registerTask('serve', ['filegen:version', 'examples', 'dev', 'connect', 'watch']);
  grunt.registerTask('release:pre', ['bump-only:prerelease', 'default']);
  grunt.registerTask('release:minor', ['bump-only:minor', 'default', 'bump-commit']);
  grunt.registerTask('release:major', ['bump-only:major', 'default', 'bump-commit']);

  grunt.registerTask('default', ['jsonlint', 'bower', 'clean', 'filegen:version', 'dev', 'dist', 'minify', 'compress']);
};<|endoftext|>


<commit_before>/* globals $ */

// TODO: Logger class
// TODO: Pluggable log backend, e.g. console.log

function _send(level, args) {
  // TODO: Inspect object instead of sending [object Object]
  // TODO: Remove the implied jQuery dependency
  $.post('/log', {
    level: level,
    message: args.join(' ')
  }).fail(function(xhr, textStatus, errorThrown) {
    // TODO: Notify user on the page and show message if console.log doesn't exist
    if (console && console.log) {
      console.log(xhr.responseText);
    }
  });
}

function error(message) {
  return _send('error', Array.prototype.slice.call(arguments));
}

function info(message) {
  return _send('info', Array.prototype.slice.call(arguments));
}

function log(message) {
  return _send('log', Array.prototype.slice.call(arguments));
}

function warn(message) {
  return _send('warn', Array.prototype.slice.call(arguments));
}

module.exports = {
  error: error,
  info: info,
  log: log,
  warn: warn
};
<commit_msg>Use XMLHttpRequest instead of jQuery for logging.<commit_after>// TODO: Logger class
// TODO: Pluggable log backend, e.g. console.log

// http://stackoverflow.com/questions/6418220/javascript-send-json-object-with-ajax
// http://stackoverflow.com/questions/9713058/sending-post-data-with-a-xmlhttprequest
// http://stackoverflow.com/questions/332872/encode-url-in-javascript
function _send(level, args) {
  var payload = (
    'level=' + encodeURIComponent(level) +
    '&message=' + encodeURIComponent(args.join(' ')));

  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/log');
  xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  xhr.onreadystatechange = function () {
    // Check for error state
    if (xhr.readyState === 4 && xhr.status !== 200) {
      // TODO: Notify user on the page and show message if console.log doesn't exist
      if (console && console.log) {
        console.log(xhr.responseText);
      }
    }
  };
  xhr.send(payload);
}

function error(message) {
  return _send('error', Array.prototype.slice.call(arguments));
}

function info(message) {
  return _send('info', Array.prototype.slice.call(arguments));
}

function log(message) {
  return _send('log', Array.prototype.slice.call(arguments));
}

function warn(message) {
  return _send('warn', Array.prototype.slice.call(arguments));
}

module.exports = {
  error: error,
  info: info,
  log: log,
  warn: warn
};
<|endoftext|>


<commit_before>// Generated on 2014-10-08 using
// generator-webapp 0.5.1
'use strict';

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

module.exports = function (grunt) {

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Configurable paths
  var config = {
    app: 'app',
    dist: 'dist'
  };

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    config: config,

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= config.app %>/scripts/{,*/}*.js'],
        tasks: ['jshint'],
        options: {
          livereload: true
        }
      },
      jstest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['test:watch']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      sass: {
        files: ['<%= config.app %>/styles/{,*/}*.{scss,sass}'],
        tasks: ['sass:server', 'autoprefixer']
      },
      styles: {
        files: ['<%= config.app %>/styles/{,*/}*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= config.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= config.app %>/images/{,*/}*'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        open: true,
        livereload: 35729,
        // Change this to '0.0.0.0' to access the server from outside
        hostname: 'localhost'
      },
      livereload: {
        options: {
          middleware: function(connect) {
            return [
              connect.static('.tmp'),
              connect().use('/bower_components', connect.static('./bower_components')),
              connect.static(config.app)
            ];
          }
        }
      },
      test: {
        options: {
          open: false,
          port: 9001,
          middleware: function(connect) {
            return [
              connect.static('.tmp'),
              connect.static('test'),
              connect().use('/bower_components', connect.static('./bower_components')),
              connect.static(config.app)
            ];
          }
        }
      },
      dist: {
        options: {
          base: '<%= config.dist %>',
          livereload: false
        }
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= config.dist %>/*',
            '!<%= config.dist %>/.git*'
          ]
        }]
      },
      server: '.tmp'
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        'Gruntfile.js',
        '<%= config.app %>/scripts/{,*/}*.js',
        '!<%= config.app %>/scripts/vendor/*',
        'test/spec/{,*/}*.js'
      ]
    },

    // Mocha testing framework configuration options
    mocha: {
      all: {
        options: {
          run: true,
          urls: ['http://<%= connect.test.options.hostname %>:<%= connect.test.options.port %>/index.html']
        }
      }
    },

    // Compiles Sass to CSS and generates necessary files if requested
    sass: {
      options: {
        loadPath: 'bower_components'
      },
      dist: {
        files: [{
          expand: true,
          cwd: '<%= config.app %>/styles',
          src: ['*.{scss,sass}'],
          dest: '.tmp/styles',
          ext: '.css'
        }]
      },
      server: {
        files: [{
          expand: true,
          cwd: '<%= config.app %>/styles',
          src: ['*.{scss,sass}'],
          dest: '.tmp/styles',
          ext: '.css'
        }]
      }
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['> 1%', 'last 2 versions', 'Firefox ESR', 'Opera 12.1']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the HTML file
    wiredep: {
      app: {
        ignorePath: /^\/|\.\.\//,
        src: ['<%= config.app %>/*.html'],
        exclude: ['bower_components/bootstrap-sass-official/assets/javascripts/bootstrap.js']
      },
      sass: {
        src: ['<%= config.app %>/styles/{,*/}*.{scss,sass}'],
        ignorePath: /(\.\.\/){1,2}bower_components\//
      }
    },

    // Renames files for browser caching purposes
    rev: {
      dist: {
        files: {
          src: [
            '<%= config.dist %>/scripts/{,*/}*.js',
            '<%= config.dist %>/styles/{,*/}*.css',
            '<%= config.dist %>/images/{,*/}*.*',
            '<%= config.dist %>/styles/fonts/{,*/}*.*',
            '<%= config.dist %>/*.{ico,png}'
          ]
        }
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      options: {
        dest: '<%= config.dist %>'
      },
      html: '<%= config.app %>/index.html'
    },

    // Performs rewrites based on rev and the useminPrepare configuration
    usemin: {
      options: {
        assetsDirs: [
          '<%= config.dist %>',
          '<%= config.dist %>/images',
          '<%= config.dist %>/styles'
        ]
      },
      html: ['<%= config.dist %>/{,*/}*.html'],
      css: ['<%= config.dist %>/styles/{,*/}*.css']
    },

    // The following *-min tasks produce minified files in the dist folder
    // imagemin: {
    //   dist: {
    //     files: [{
    //       expand: true,
    //       cwd: '<%= config.app %>/images',
    //       src: '{,*/}*.{gif,jpeg,jpg,png}',
    //       dest: '<%= config.dist %>/images'
    //     }]
    //   }
    // },

    // svgmin: {
    //   dist: {
    //     files: [{
    //       expand: true,
    //       cwd: '<%= config.app %>/images',
    //       src: '{,*/}*.svg',
    //       dest: '<%= config.dist %>/images'
    //     }]
    //   }
    // },

    htmlmin: {
      dist: {
        options: {
          collapseBooleanAttributes: true,
          collapseWhitespace: true,
          conservativeCollapse: true,
          removeAttributeQuotes: true,
          removeCommentsFromCDATA: true,
          removeEmptyAttributes: true,
          removeOptionalTags: true,
          removeRedundantAttributes: true,
          useShortDoctype: true
        },
        files: [{
          expand: true,
          cwd: '<%= config.dist %>',
          src: '{,*/}*.html',
          dest: '<%= config.dist %>'
        }]
      }
    },

    // By default, your `index.html`'s <!-- Usemin block --> will take care
    // of minification. These next options are pre-configured if you do not
    // wish to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= config.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css',
    //         '<%= config.app %>/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= config.dist %>/scripts/scripts.js': [
    //         '<%= config.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= config.app %>',
          dest: '<%= config.dist %>',
          src: [
            '*.{ico,png,txt}',
            'images/{,*/}*.webp',
            '{,*/}*.html',
            'styles/fonts/{,*/}*.*'
          ]
        }, {
          src: 'node_modules/apache-server-configs/dist/.htaccess',
          dest: '<%= config.dist %>/.htaccess'
        }, {
          expand: true,
          dot: true,
          cwd: '.',
          src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
          dest: '<%= config.dist %>'
        }]
      },
      styles: {
        expand: true,
        dot: true,
        cwd: '<%= config.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up build process
    concurrent: {
      server: [
        'sass:server',
        'copy:styles'
      ],
      test: [
        'copy:styles'
      ],
      dist: [
        'sass',
        'copy:styles',
        'imagemin',
        'svgmin'
      ]
    }
  });

  grunt.registerTask('serve', 'start the server and preview your app, --allow-remote for remote access', function (target) {
    if (grunt.option('allow-remote')) {
      grunt.config.set('connect.options.hostname', '0.0.0.0');
    }
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'wiredep',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run([target ? ('serve:' + target) : 'serve']);
  });
  grunt.registerTask('test', function (target) {
    if (target !== 'watch') {
      grunt.task.run([
        'clean:server',
        'concurrent:test',
        'autoprefixer'
      ]);
    }

    grunt.task.run([
      'connect:test',
      'mocha'
    ]);
  });

  grunt.registerTask('build', [
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'cssmin',
    'uglify',
    'copy:dist',
    'rev',
    'usemin',
    'htmlmin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<commit_msg>Nu aber!<commit_after>// Generated on 2014-10-08 using
// generator-webapp 0.5.1
'use strict';

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

module.exports = function (grunt) {

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Configurable paths
  var config = {
    app: 'app',
    dist: 'dist'
  };

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    config: config,

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= config.app %>/scripts/{,*/}*.js'],
        tasks: ['jshint'],
        options: {
          livereload: true
        }
      },
      jstest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['test:watch']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      sass: {
        files: ['<%= config.app %>/styles/{,*/}*.{scss,sass}'],
        tasks: ['sass:server', 'autoprefixer']
      },
      styles: {
        files: ['<%= config.app %>/styles/{,*/}*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= config.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= config.app %>/images/{,*/}*'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        open: true,
        livereload: 35729,
        // Change this to '0.0.0.0' to access the server from outside
        hostname: 'localhost'
      },
      livereload: {
        options: {
          middleware: function(connect) {
            return [
              connect.static('.tmp'),
              connect().use('/bower_components', connect.static('./bower_components')),
              connect.static(config.app)
            ];
          }
        }
      },
      test: {
        options: {
          open: false,
          port: 9001,
          middleware: function(connect) {
            return [
              connect.static('.tmp'),
              connect.static('test'),
              connect().use('/bower_components', connect.static('./bower_components')),
              connect.static(config.app)
            ];
          }
        }
      },
      dist: {
        options: {
          base: '<%= config.dist %>',
          livereload: false
        }
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= config.dist %>/*',
            '!<%= config.dist %>/.git*'
          ]
        }]
      },
      server: '.tmp'
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        'Gruntfile.js',
        '<%= config.app %>/scripts/{,*/}*.js',
        '!<%= config.app %>/scripts/vendor/*',
        'test/spec/{,*/}*.js'
      ]
    },

    // Mocha testing framework configuration options
    mocha: {
      all: {
        options: {
          run: true,
          urls: ['http://<%= connect.test.options.hostname %>:<%= connect.test.options.port %>/index.html']
        }
      }
    },

    // Compiles Sass to CSS and generates necessary files if requested
    sass: {
      options: {
        loadPath: 'bower_components'
      },
      dist: {
        files: [{
          expand: true,
          cwd: '<%= config.app %>/styles',
          src: ['*.{scss,sass}'],
          dest: '.tmp/styles',
          ext: '.css'
        }]
      },
      server: {
        files: [{
          expand: true,
          cwd: '<%= config.app %>/styles',
          src: ['*.{scss,sass}'],
          dest: '.tmp/styles',
          ext: '.css'
        }]
      }
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['> 1%', 'last 2 versions', 'Firefox ESR', 'Opera 12.1']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the HTML file
    wiredep: {
      app: {
        ignorePath: /^\/|\.\.\//,
        src: ['<%= config.app %>/*.html'],
        exclude: ['bower_components/bootstrap-sass-official/assets/javascripts/bootstrap.js']
      },
      sass: {
        src: ['<%= config.app %>/styles/{,*/}*.{scss,sass}'],
        ignorePath: /(\.\.\/){1,2}bower_components\//
      }
    },

    // Renames files for browser caching purposes
    rev: {
      dist: {
        files: {
          src: [
            '<%= config.dist %>/scripts/{,*/}*.js',
            '<%= config.dist %>/styles/{,*/}*.css',
            '<%= config.dist %>/images/{,*/}*.*',
            '<%= config.dist %>/styles/fonts/{,*/}*.*',
            '<%= config.dist %>/*.{ico,png}'
          ]
        }
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      options: {
        dest: '<%= config.dist %>'
      },
      html: '<%= config.app %>/index.html'
    },

    // Performs rewrites based on rev and the useminPrepare configuration
    usemin: {
      options: {
        assetsDirs: [
          '<%= config.dist %>',
          '<%= config.dist %>/images',
          '<%= config.dist %>/styles'
        ]
      },
      html: ['<%= config.dist %>/{,*/}*.html'],
      css: ['<%= config.dist %>/styles/{,*/}*.css']
    },

    // The following *-min tasks produce minified files in the dist folder
    // imagemin: {
    //   dist: {
    //     files: [{
    //       expand: true,
    //       cwd: '<%= config.app %>/images',
    //       src: '{,*/}*.{gif,jpeg,jpg,png}',
    //       dest: '<%= config.dist %>/images'
    //     }]
    //   }
    // },

    // svgmin: {
    //   dist: {
    //     files: [{
    //       expand: true,
    //       cwd: '<%= config.app %>/images',
    //       src: '{,*/}*.svg',
    //       dest: '<%= config.dist %>/images'
    //     }]
    //   }
    // },

    htmlmin: {
      dist: {
        options: {
          collapseBooleanAttributes: true,
          collapseWhitespace: true,
          conservativeCollapse: true,
          removeAttributeQuotes: true,
          removeCommentsFromCDATA: true,
          removeEmptyAttributes: true,
          removeOptionalTags: true,
          removeRedundantAttributes: true,
          useShortDoctype: true
        },
        files: [{
          expand: true,
          cwd: '<%= config.dist %>',
          src: '{,*/}*.html',
          dest: '<%= config.dist %>'
        }]
      }
    },

    // By default, your `index.html`'s <!-- Usemin block --> will take care
    // of minification. These next options are pre-configured if you do not
    // wish to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= config.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css',
    //         '<%= config.app %>/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= config.dist %>/scripts/scripts.js': [
    //         '<%= config.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= config.app %>',
          dest: '<%= config.dist %>',
          src: [
            '*.{ico,png,txt}',
            'images/{,*/}*.webp',
            '{,*/}*.html',
            'styles/fonts/{,*/}*.*'
          ]
        }, {
          src: 'node_modules/apache-server-configs/dist/.htaccess',
          dest: '<%= config.dist %>/.htaccess'
        }, {
          expand: true,
          dot: true,
          cwd: '.',
          src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
          dest: '<%= config.dist %>'
        }]
      },
      styles: {
        expand: true,
        dot: true,
        cwd: '<%= config.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up build process
    concurrent: {
      server: [
        'sass:server',
        'copy:styles'
      ],
      test: [
        'copy:styles'
      ],
      dist: [
        'sass',
        'copy:styles'
      ]
    }
  });

  grunt.registerTask('serve', 'start the server and preview your app, --allow-remote for remote access', function (target) {
    if (grunt.option('allow-remote')) {
      grunt.config.set('connect.options.hostname', '0.0.0.0');
    }
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'wiredep',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run([target ? ('serve:' + target) : 'serve']);
  });
  grunt.registerTask('test', function (target) {
    if (target !== 'watch') {
      grunt.task.run([
        'clean:server',
        'concurrent:test',
        'autoprefixer'
      ]);
    }

    grunt.task.run([
      'connect:test',
      'mocha'
    ]);
  });

  grunt.registerTask('build', [
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'cssmin',
    'uglify',
    'copy:dist',
    'rev',
    'usemin',
    'htmlmin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<|endoftext|>


<commit_before>/// <binding ProjectOpened='watch' />
/**!
 Gruntfile to perform wwt webclient less compilation,
 script concatenation/minification, and component updates
 (using grunt bower:install)

 Once you have run npm install and npm update
 and bower is installed (npm install -g bower)
 run grunt watch

 **/

module.exports = function (grunt) {
  'use strict';

  // Force use of Unix newlines
  grunt.util.linefeed = '\n';

  RegExp.quote = function (string) {
    return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
  };

  // Project configuration.
  grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),
    banner: '/**\n' +
    '* WorldWide Telescope Web Client\n' +
    '* Copyright 2014-2015 WorldWide Telescope\n' +
    '* Licensed under <%= pkg.license.type %> (<%= pkg.license.url %>)\n' +
    '**/\n',

    // Task configuration.

    concat: {
      options: {
        banner: '<%= banner %>'
      },
      webclient: {
        src: [
          'ext/intro.js',
          'ext/angular-intro.js',
          'app.js',
          'directives/Scroll.js',
          'directives/Localize.js',
          'directives/ContextMenu.js',
          'directives/EditSlideValues.js',
          'directives/Movable.js',
          'directives/CopyToClipboard.js',
          'factories/appstate.js',
          'factories/autohidepanels.js',
          'factories/localization.js',
          'factories/FinderScope.js',
          'factories/ThumbList.js',
          'factories/Util.js',
          'factories/UILibrary.js',
          'factories/SearchUtil.js',
          'factories/Skyball.js',
          'factories/HashManager.js',
          'factories/MediaFile.js',
          'dataproxy/Places.js',
          'dataproxy/Tours.js',
          'dataproxy/SearchData.js',
          'dataproxy/Astrometry.js',
          'dataproxy/Community.js',
          'controllers/ContextPanelController.js',
          'controllers/MainController.js',
          'controllers/IntroController.js',
          'controllers/MobileNavController.js',
          'controllers/LayerManagerController.js',
          'controllers/tabs/AdsController.js',
          'controllers/tabs/ExploreController.js',
          'controllers/tabs/SearchController.js',
          'controllers/tabs/SettingsController.js',
          'controllers/tabs/ViewController.js',
          'controllers/tabs/ToursController.js',
          'controllers/tabs/CommunityController.js',
          'controllers/tabs/CurrentTourController.js',
          'controllers/modals/TourSlideText.js',
          'controllers/modals/ShareController.js',
          'controllers/modals/OpenItemController.js',
          'controllers/modals/ObservingTimeController.js',
          'controllers/modals/SlideSelectionController.js',
          'controllers/modals/VoConeSearchController.js',
          'controllers/modals/VOTableViewerController.js',
          'controllers/modals/colorpickerController.js',
          'controllers/modals/refframeController.js',
          'controllers/modals/GreatCircleController.js',
          'controllers/modals/DataVizController.js',
          'controllers/modals/EmbedController.js',
          'controllers/modals/ObservingLocationController.js',
          'controllers/LoginController.js',
          'controls/move.js',
          'controls/util.js'
        ],
        dest: 'wwtwebclient.js'
      },
      sdk: {
        src: [
          'sdk/ss.js',
          'sdk/wwtlib.js'
        ],
        dest: 'sdk/wwtsdk.js'
      },
      oldsdk: {
        src: [
          'sdk/old/mscorlib.debug.js',
          'sdk/old/wwtlib.debug.js'
        ],
        dest: 'sdk/old/wwtlib_full.js'
      }
    },
    //remove the AMD dependancy from scriptsharp output
    replace: {
      wwtlib: {
        src: ['sdk/wwtlib.js'],
        dest: 'sdk/wwtlib.js',
        replacements: [
          {
            from: "define('wwtlib', ['ss'], function(ss) {",
            to: 'window.wwtlib = function(){'
          }, {
            from: 'return $exports;\n});',
            to: 'return $exports;\n}();'
          }
        ]
      }
    },

    uglify: {
      options: {
        preserveComments: 'some',
        banner: '<%= banner %>'
      },
      webclient: {
        src: '<%= concat.webclient.dest %>',
        dest: 'wwtwebclient.min.js'
      },
      searchData: {
        src: 'searchdataraw.js',
        dest: 'searchdata.min.js'
      },
      sdk: {
        src: '<%= concat.sdk.dest %>',
        dest: 'sdk/wwtsdk.min.js'
      },
      oldsdk: {
        src: '<%= concat.oldsdk.dest %>',
        dest: 'sdk/old/wwtlib_full.min.js'
      }
    },
    less: {
      compileCore: {
        options: {
          strictMath: true,
          sourceMap: true,
          outputSourceFiles: true,
          sourceMapURL: 'webclient.css.map',
          sourceMapFilename: 'css/webclient.css.map'
        },
        src: 'css/webclient.less',
        dest: 'css/webclient.css'
      }
    },
    autoprefixer: {
      options: {
        browsers: [
          "Android 2.3",
          "Android >= 4",
          "Chrome >= 20",
          "Firefox >= 24",
          "Explorer >= 10",
          "iOS >= 6",
          "Opera >= 12",
          "Safari >= 6"
        ]
      },
      core: {
        options: {
          map: true
        },
        src: 'css/webclient.css'
      }
    },
    cssmin: {
      options: {
        compatibility: 'ie10',
        keepSpecialComments: '*',
        noAdvanced: true
      },
      minifyCore: {
        src: 'css/webclient.css',
        dest: 'css/webclient.min.css'
      }
    },
    csscomb: {
      options: {
        config: 'bootstrap/less/.csscomb.json'
      },
      dist: {
        expand: true,
        cwd: 'css/',
        src: ['*.css', '!*.min.css'],
        dest: 'css/'
      }
    },

    watch: {
      sdk: {
        files: 'sdk/wwtlib.js',
        tasks: ['sdk']
      },

      // call out only the directories to watch prevents
      // watch from watching recursive node_modules folders e.g.: '../**/*.js'
      scripts: {
        files: [
          'controllers/**/*.js',
          'controllers/*.js',
          'controls/*.js',
          'directives/*.js',
          'dataproxy/*.js',
          'factories/*.js',
          'app.js'],
        tasks: ['dist-js']
      },

      less: {
        files: 'css/*.less',
        tasks: ['dist-css']
      }
    }
  });

  // Dependencies
  require('load-grunt-tasks')(grunt, {scope: 'devDependencies'});

  // JS concatenation and minification
  grunt.registerTask('dist-js', ['concat:webclient', 'uglify:webclient']);

  // Takes HTML5SDK generated script and packages into single usable lib. (scriptsharp v0.8).
  grunt.registerTask('sdk', ['replace:wwtlib', 'concat:sdk', 'uglify:sdk', 'concat:webclient', 'uglify:webclient']);

  // Minify the generated search data (rare - internal only)
  grunt.registerTask('dist-searchdata', ['uglify:searchData']);

  // CSS  (csscomb seems like too much, so commented out for now)
  grunt.registerTask('dist-css', ['less:compileCore', 'autoprefixer:core', /*'csscomb:dist',*/'cssmin:minifyCore']);

};
<commit_msg>Gruntfile.js: import revised version from StarHunt<commit_after>module.exports = function (grunt) {
  'use strict';

  // Force use of Unix newlines
  grunt.util.linefeed = '\n';

  RegExp.quote = function (string) {
    return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
  };

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    banner: '/**\n' +
      '* WorldWide Telescope Web Client\n' +
      '* Copyright 2014-2020 .NET Foundation\n' +
      '* Licensed under <%= pkg.license.type %> (<%= pkg.license.url %>)\n' +
      '* Git hash <%= gitinfo.local.branch.current.SHA %>\n' +
      '**/\n',

    // Triger the loading of the Git version info
    gitinfo: {},

    // Task configuration.

    concat: {
      options: {
        banner: '<%= banner %>',

        process: function(src, filepath) {
          if (filepath == 'app.js' || filepath == 'index.html') {
            return grunt.template.process(src);
          } else {
            return src;
          }
        },
      },

      webclient: {
        src: [
          'ext/intro.js',
          'ext/angular-intro.js',
          'app.js',
          'directives/Scroll.js',
          'directives/Localize.js',
          'directives/ContextMenu.js',
          'directives/editslidevalues.js',
          'directives/movable.js',
          'directives/CopyToClipboard.js',
          'factories/AppState.js',
          'factories/autohidepanels.js',
          'factories/Localization.js',
          'factories/FinderScope.js',
          'factories/ThumbList.js',
          'factories/Util.js',
          'factories/UILibrary.js',
          'factories/SearchUtil.js',
          'factories/Skyball.js',
          'factories/HashManager.js',
          'factories/mediafile.js',
          'dataproxy/Places.js',
          'dataproxy/Tours.js',
          'dataproxy/SearchData.js',
          'dataproxy/Astrometry.js',
          'dataproxy/Community.js',
          'controls/util.js',
          'controls/move.js',
          'controllers/ContextPanelController.js',
          'controllers/MainController.js',
          'controllers/IntroController.js',
          'controllers/LoginController.js',
          'controllers/MobileNavController.js',
          'controllers/LayerManagerController.js',
          'controllers/tabs/AdsController.js',
          'controllers/tabs/ExploreController.js',
          'controllers/tabs/SearchController.js',
          'controllers/tabs/SettingsController.js',
          'controllers/tabs/ViewController.js',
          'controllers/tabs/ToursController.js',
          'controllers/tabs/CommunityController.js',
          'controllers/tabs/CurrentTourController.js',
          'controllers/modals/TourSlideText.js',
          'controllers/modals/ShareController.js',
          'controllers/modals/OpenItemController.js',
          'controllers/modals/ObservingTimeController.js',
          'controllers/modals/SlideSelectionController.js',
          'controllers/modals/VoConeSearchController.js',
          'controllers/modals/VOTableViewerController.js',
          'controllers/modals/colorpickerController.js',
          'controllers/modals/refFrameController.js',
          'controllers/modals/GreatCircleController.js',
          'controllers/modals/DataVizController.js',
          'controllers/modals/EmbedController.js',
          'controllers/modals/ObservingLocationController.js',
          'misc/move.js',
          'misc/util.js'
        ],
        dest: 'dist/wwtwebclient.js',
        nonull: true,
      }
    },

    uglify: {
      options: {
        preserveComments: 'some',
        banner: '<%= banner %>'
      },
      webclient: {
        src: '<%= concat.webclient.dest %>',
        dest: 'dist/wwtwebclient.min.js'
      },
    },

    less: {
      compileCore: {
        options: {
          strictMath: true,
          sourceMap: true,
          outputSourceFiles: true,
          sourceMapURL: 'webclient.css.map',
          sourceMapFilename: 'dist/css/webclient.css.map'
        },
        src: 'css/webclient.less',
        dest: 'dist/css/webclient.css'
      }
    },

    autoprefixer: {
      options: {
        browsers: [
          "Android 2.3",
          "Android >= 4",
          "Chrome >= 20",
          "Firefox >= 24",
          "Explorer >= 10",
          "iOS >= 6",
          "Opera >= 12",
          "Safari >= 6"
        ]
      },
      core: {
        options: {
          map: true
        },
        src: '<%= less.compileCore.dest %>'
      }
    },

    cssmin: {
      options: {
        compatibility: 'ie10',
        keepSpecialComments: '*',
        noAdvanced: true
      },
      minifyCore: {
        src: '<%= autoprefixer.core.src %>',
        dest: 'dist/css/webclient.min.css'
      }
    },

    template: {
      options: {
        data: function() {
          return {
            shortSHA: grunt.config.get('gitinfo.local.branch.current.shortSHA')
          }
        }
      },

      indexhtml: {
        files: {
          'dist/index.html': 'index.html'
        }
      }
    },

    copy: {
      dist: {
        files: [
          {
            expand: false,
            src: [
              'css/introjs.css',
              'css/angular-motion.css',
              'css/skin.min.css',
              'favicon.ico'
            ],
            dest: 'dist/'
          }, {
            expand: true,
            src: [
              'fonts/*',
              'Images/*',
              'views/**'
            ],
            dest: 'dist/',
            filter: 'isFile'
          }
        ]
      }
    }
  });

  require('load-grunt-tasks')(grunt, {scope: 'devDependencies'});

  grunt.registerTask('dist-js', ['gitinfo', 'concat:webclient', 'uglify:webclient']);
  grunt.registerTask('dist-css', ['less:compileCore', 'autoprefixer:core', 'cssmin:minifyCore']);
  grunt.registerTask('dist-all', ['dist-js', 'dist-css', 'template:indexhtml', 'copy:dist']);
};
<|endoftext|>


<commit_before>var Request = {
    _img: null,
    make(url, callback) {
        console.log('Making request to ' + url);

        this._img = new Image();
        this._img.onerror = callback;
        this._img.src = url;
    },
    cancel() {
        this._img.src = '';
    }
};

var Collection = {
    _ONE_REQUEST_DEFAULT_TIMEOUT: 5000,
    create(url, {amount, oneRequestTimeout}, onOneSuccess, onAllSuccess, onError) {
        var requests = [];
        var loadingTimeout;
        var loadedCount = 0;

        if (oneRequestTimeout === 0) {
            throw 'oneRequestTimeout should not be zero';
        }

        oneRequestTimeout = oneRequestTimeout || this._ONE_REQUEST_DEFAULT_TIMEOUT;

        console.log('Creating request collection of ' + amount + ' requests to URL: ' + url);
        console.log('Using one request time out value of ' + oneRequestTimeout);

        const resetTimeout = () => {
            clearTimeout(loadingTimeout);
            loadingTimeout = setTimeout(() => {
                for (var i = 0; i < amount; ++i) {
                    requests[i].cancel();
                }
                clearTimeout(loadingTimeout);
                onError();
            }, oneRequestTimeout);
        }
        const allCompleted = () => {
            clearTimeout(loadingTimeout);
            onAllSuccess();
        }
        const oneLoaded = (index) => {
            console.log();

            resetTimeout();
            console.log('Completed request: ' + index);
            ++loadedCount;

            onOneSuccess(index);

            if (loadedCount == amount) {
                allCompleted();
            }
        }

        antiBrowserCaching = Math.random() * Number.MAX_SAFE_INTEGER;
        for (var i = 0; i < amount; ++i) {
            var request = Request.make(url + '?' + (antiBrowserCaching + i), oneLoaded.bind({}, i));
            requests.push(request);
        }
    }
};

module.exports = {Request, Collection};
<commit_msg>Block alignment padding<commit_after>var Request = {
    _img: null,
    make(url, callback) {
        console.log('Making request to ' + url);

        this._img = new Image();
        this._img.onerror = callback;
        this._img.src = url;
    },
    cancel() {
        this._img.src = '';
    }
};

var Collection = {
    _ONE_REQUEST_DEFAULT_TIMEOUT: 5000,
    create(url, {amount, oneRequestTimeout}, onOneSuccess, onAllSuccess, onError) {
        var requests = [];
        var loadingTimeout;
        var loadedCount = 0;

        if (oneRequestTimeout === 0) {
            throw 'oneRequestTimeout should not be zero';
        }

        oneRequestTimeout = oneRequestTimeout || this._ONE_REQUEST_DEFAULT_TIMEOUT;

        console.log('Creating request collection of ' + amount + ' requests to URL: ' + url);
        console.log('Using one request time out value of ' + oneRequestTimeout);

        const resetTimeout = () => {
            clearTimeout(loadingTimeout);
            loadingTimeout = setTimeout(() => {
                for (var i = 0; i < amount; ++i) {
                    requests[i].cancel();
                }
                clearTimeout(loadingTimeout);
                onError();
            }, oneRequestTimeout);
        }
        const allCompleted = () => {
            clearTimeout(loadingTimeout);
            onAllSuccess();
        }
        const oneLoaded = (index) => {
            console.log();

            resetTimeout();
            console.log('Completed request: ' + index);
            ++loadedCount;

            onOneSuccess(index);

            if (loadedCount == amount) {
                allCompleted();
            }
        }

        antiBrowserCaching = Math.random() * Number.MAX_SAFE_INTEGER;
        for (var i = 0; i < amount; ++i) {
            var symbols = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
            var padding = symbols.substr(0, i % 16);
            var request = Request.make(url + padding + '?' + (antiBrowserCaching + i), oneLoaded.bind({}, i));
            requests.push(request);
        }
    }
};

module.exports = {Request, Collection};
<|endoftext|>