<commit_before>/*
	The MIT License (MIT)

	Copyright (c) 2014 Julian Xhokaxhiu

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/
module.exports = function(grunt) {

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

    // Configure the tasks
    grunt.initConfig({
      pkg: grunt.file.readJSON('package.json'),
      app: grunt.file.readJSON('app.json'),
      cssMin: 'Resources/Public/css/<%= pkg.name %>.min.css',
      cssPrintMin: 'Resources/Public/css/<%= pkg.name %>.min.print.css',
      jsMin: 'Resources/Public/js/<%= pkg.name %>.min.js',
      clean: {
        options: {
          force: true
        },
        output: [
          'Resources/Temp',
          'Resources/Public',
          'Resources/Private/Scss/_<%= pkg.name %>-icons.scss'
        ],
        cssFiles: [
          'Resources/Public/css/*'
        ],
        temp: [
          'Resources/Temp'
        ]
      },
  		sass: {
        options: {
          style: 'compressed',
          loadPath: '.'
        },
  			build: {
  				files: {
  					'Resources/Temp/css/99_<%= pkg.name %>.css' : [ 'Resources/Private/Scss/screen.scss' ],
  					'Resources/Temp/css-print/99_<%= pkg.name %>.css' : [ 'Resources/Private/Scss/print.scss' ]
  				}
  			}
  		},
  		fontgen: {
  			build: {
  				options: {
  					path_prefix: '../fonts/'
  				},
  				files: {
  					'Resources/Temp/webfonts/' : [
            	'Resources/Private/Fonts/*.otf',
    					'Resources/Private/Fonts/*.ttf'
            ]
  				}
  			}
  		},
  		webfont: {
  			icons: {
  				src: 'Resources/Private/Icons/*.svg',
  				dest: 'Resources/Temp/webicons/',
          destCss: 'Resources/Private/Scss/',
  				options: {
    				template: 'Resources/Private/icons.css',
            relativeFontPath: '../fonts/',
    				font: '<%= pkg.name %>-icons',
            stylesheet: 'scss',
            htmlDemo: false,
            templateOptions: {
              baseClass: '<%= pkg.name %>-icon',
              classPrefix: '<%= pkg.name %>-icon-',
              mixinPrefix: '<%= pkg.name %>-icon-'
            }
  			  }
  			}
  		},
      concat: {
      	webfonts: {
      		files: {
      			'Resources/Temp/css/98_webfonts.css' : [ 'Resources/Temp/webfonts/*.css' ],
      			'Resources/Temp/css-print/98_webfonts.css' : [ 'Resources/Temp/webfonts/*.css' ]
      		}
      	},
        css: {
            filter: 'isFile',
            src: 'Resources/Temp/css/**/*.css',
            dest: '<%= cssMin %>'
        },
        cssPrint: {
            filter: 'isFile',
            src: 'Resources/Temp/css-print/**/*.css',
            dest: '<%= cssPrintMin %>'
        },
        js: {
        	files: {
        		'<%= jsMin %>' : [
              '<%= app.assets.js %>',
              'Resources/Private/JavaScript/**/*.js'
            ]
        	}
        }
      },
      autoprefixer: {
        options: {
          browsers: [
            "Android 2.3",
            "Android >= 4",
            "Chrome >= 20",
            "Firefox >= 24",
            "Explorer >= 8",
            "iOS >= 6",
            "Opera >= 12",
            "Safari >= 6"
          ]
        },
        build: {
        	options: {
        		map: true
        	},
          src: 'Resources/Public/css/*.css'
        },
        deploy: {
          options: {
            map: false
          },
          src: 'Resources/Public/css/*.css'
        }
      },
      cssmin: {
          build: {
              files: {
              	'<%= cssMin %>' : [ '<%= cssMin %>' ],
              	'<%= cssPrintMin %>' : [ '<%= cssPrintMin %>' ]
              }
          }
      },
      closurecompiler: {
          build: {
          	files: {
          		'<%= jsMin %>': [
                '<%= app.assets.js %>',
                'Resources/Private/JavaScript/**/*.js'
              ]
          	},
          	options: {
                // Any options supported by Closure Compiler, for example:
                "compilation_level": "SIMPLE_OPTIMIZATIONS",

                // Plus a simultaneous processes limit
                "max_processes": 5,
            }
          }
      },
      copy: {
          deploy: {
              files: [
                // Internal Misc files
                {
                  expand: true,
                  src: 'Resources/Private/Misc/**/*',
                  dest: 'Resources/Public/'
                },
              	// External Packages Fonts
              	{
              		expand: true,
              		flatten: true,
              		src: '<%= app.assets.fonts %>',
              		dest: 'Resources/Public/fonts/'
              	},
              	// The project Webfonts
              	{
              		expand: true,
              		flatten: true,
              		cwd: 'Resources/Temp/webfonts/',
              		src: ['**/*.{ttf,woff,eot,svg,otf}'],
              		dest: 'Resources/Public/fonts/'
              	},
              	// The project Webicons
              	{
              		expand: true,
              		flatten: true,
              		cwd: 'Resources/Temp/webicons/',
              		src: ['**/*.{ttf,woff,eot,svg,otf}'],
              		dest: 'Resources/Public/fonts/'
              	},
              	// Webicons sass generated file
              	{
                  expand: true,
                  cwd: 'Resources/Temp/webicons/',
                  src: [
                    '*.scss'
                  ],
                  dest: 'Resources/Private/Scss/'
                },
                // Images
                {
                  expand: true,
                  cwd: 'Resources/Private/Images/',
                  src: '**/*',
                  dest: 'Resources/Public/img/'
                }
              ]
          }
      },
      watch: {
      	options: {
      		livereload: -1
      	},
          css: {
              files: [
                  'Resources/Private/**/*.scss',
              ],
              tasks: [
                  'rebuild:css'
              ]
          },
          js: {
              files: [
                  'Resources/Private/**/*.js'
              ],
              tasks: [
                  'rebuild:js'
              ]
          }
      },
      exec: {
          npm_update: {
              cmd: 'npm update'
          },
          bower_update: {
              cmd: 'bower update'
          },
          npm_install: {
              cmd: 'npm install'
          },
          bower_install: {
              cmd: 'bower install'
          }
      },
      periodic: {
          update: {
              when: 'daily',
              tasks: [
                  'exec:npm_update',
                  'exec:bower_update'
              ]
          },
          npm_install: {
              when: 'newer',
              tasks: [
                  'exec:npm_install',
                  'exec:npm_update'
              ],
              files: [{
                  src: [ 'package.json' ]
              }]
          },
          bower_install: {
              when: 'newer',
              tasks: [
                  'exec:bower_install',
                  'exec:bower_update'
              ],
              files: [{
                  src: [ 'bower.json' ]
              }]
          }
      }
    });

    grunt.registerTask('default', 'Gruntfile for Mockups', function() {
      grunt.warn('No task name specified.\n\nAvailable tasks: build, deploy"\n\n');
    });

    grunt.registerTask('build', 'Build a mockup and watch for file modifications', [
  		'rebuild',
  		'watch'
    ]);

    grunt.registerTask('rebuild', 'Build a mockup without watching for file modifications', function ( watchTask ) {
      var tasks = [];

      if ( watchTask === undefined ) watchTask = 'all';

      if ( watchTask == 'all' ) {
          tasks = tasks.concat([
            'periodic',
            'clean',
            'fontgen',
            'webfont',
            'copy',
            'concat:webfonts',
            'sass',
            'concat:css',
            'concat:cssPrint',
            'concat:js',
            'autoprefixer:build',
            'clean:temp'
          ]);
      } else if ( watchTask == 'css' ) {
          tasks = tasks.concat([
            'clean:cssFiles',
            'sass',
            'concat:css',
            'concat:cssPrint',
            'autoprefixer:build'
          ]);
      } else if ( watchTask == 'js' ) {
          tasks = tasks.concat([
            'concat:js'
          ]);
      }
      grunt.task.run(tasks);
    });

    grunt.registerTask('build_deploy', 'Build a mockup ready for deployment sync', [
    	'clean',
      'fontgen',
      'webfont',
      'copy',
      'concat:webfonts',
      'sass',
      'concat:css',
      'concat:cssPrint',
      'cssmin',
      'autoprefixer:deploy',
      'closurecompiler',
      'clean:temp'
    ]);

    grunt.registerTask('deploy', 'Deploy a mockup to the Mockups Server', [
		  'build_deploy'
    ]);
};
<commit_msg>Avoid recreating paths in Public.<commit_after>/*
	The MIT License (MIT)

	Copyright (c) 2014 Julian Xhokaxhiu

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/
module.exports = function(grunt) {

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

    // Configure the tasks
    grunt.initConfig({
      pkg: grunt.file.readJSON('package.json'),
      app: grunt.file.readJSON('app.json'),
      cssMin: 'Resources/Public/css/<%= pkg.name %>.min.css',
      cssPrintMin: 'Resources/Public/css/<%= pkg.name %>.min.print.css',
      jsMin: 'Resources/Public/js/<%= pkg.name %>.min.js',
      clean: {
        options: {
          force: true
        },
        output: [
          'Resources/Temp',
          'Resources/Public',
          'Resources/Private/Scss/_<%= pkg.name %>-icons.scss'
        ],
        cssFiles: [
          'Resources/Public/css/*'
        ],
        temp: [
          'Resources/Temp'
        ]
      },
  		sass: {
        options: {
          style: 'compressed',
          loadPath: '.'
        },
  			build: {
  				files: {
  					'Resources/Temp/css/99_<%= pkg.name %>.css' : [ 'Resources/Private/Scss/screen.scss' ],
  					'Resources/Temp/css-print/99_<%= pkg.name %>.css' : [ 'Resources/Private/Scss/print.scss' ]
  				}
  			}
  		},
  		fontgen: {
  			build: {
  				options: {
  					path_prefix: '../fonts/'
  				},
  				files: {
  					'Resources/Temp/webfonts/' : [
            	'Resources/Private/Fonts/*.otf',
    					'Resources/Private/Fonts/*.ttf'
            ]
  				}
  			}
  		},
  		webfont: {
  			icons: {
  				src: 'Resources/Private/Icons/*.svg',
  				dest: 'Resources/Temp/webicons/',
          destCss: 'Resources/Private/Scss/',
  				options: {
    				template: 'Resources/Private/icons.css',
            relativeFontPath: '../fonts/',
    				font: '<%= pkg.name %>-icons',
            stylesheet: 'scss',
            htmlDemo: false,
            templateOptions: {
              baseClass: '<%= pkg.name %>-icon',
              classPrefix: '<%= pkg.name %>-icon-',
              mixinPrefix: '<%= pkg.name %>-icon-'
            }
  			  }
  			}
  		},
      concat: {
      	webfonts: {
      		files: {
      			'Resources/Temp/css/98_webfonts.css' : [ 'Resources/Temp/webfonts/*.css' ],
      			'Resources/Temp/css-print/98_webfonts.css' : [ 'Resources/Temp/webfonts/*.css' ]
      		}
      	},
        css: {
            filter: 'isFile',
            src: 'Resources/Temp/css/**/*.css',
            dest: '<%= cssMin %>'
        },
        cssPrint: {
            filter: 'isFile',
            src: 'Resources/Temp/css-print/**/*.css',
            dest: '<%= cssPrintMin %>'
        },
        js: {
        	files: {
        		'<%= jsMin %>' : [
              '<%= app.assets.js %>',
              'Resources/Private/JavaScript/**/*.js'
            ]
        	}
        }
      },
      autoprefixer: {
        options: {
          browsers: [
            "Android 2.3",
            "Android >= 4",
            "Chrome >= 20",
            "Firefox >= 24",
            "Explorer >= 8",
            "iOS >= 6",
            "Opera >= 12",
            "Safari >= 6"
          ]
        },
        build: {
        	options: {
        		map: true
        	},
          src: 'Resources/Public/css/*.css'
        },
        deploy: {
          options: {
            map: false
          },
          src: 'Resources/Public/css/*.css'
        }
      },
      cssmin: {
          build: {
              files: {
              	'<%= cssMin %>' : [ '<%= cssMin %>' ],
              	'<%= cssPrintMin %>' : [ '<%= cssPrintMin %>' ]
              }
          }
      },
      closurecompiler: {
          build: {
          	files: {
          		'<%= jsMin %>': [
                '<%= app.assets.js %>',
                'Resources/Private/JavaScript/**/*.js'
              ]
          	},
          	options: {
                // Any options supported by Closure Compiler, for example:
                "compilation_level": "SIMPLE_OPTIMIZATIONS",

                // Plus a simultaneous processes limit
                "max_processes": 5,
            }
          }
      },
      copy: {
          deploy: {
              files: [
                // Internal Misc files
                {
                  expand: true,
                  cwd: 'Resources/Private/Misc/',
                  src: [ '**/*' ],
                  dest: 'Resources/Public/'
                },
              	// External Packages Fonts
              	{
              		expand: true,
              		flatten: true,
              		src: '<%= app.assets.fonts %>',
              		dest: 'Resources/Public/fonts/'
              	},
              	// The project Webfonts
              	{
              		expand: true,
              		flatten: true,
              		cwd: 'Resources/Temp/webfonts/',
              		src: ['**/*.{ttf,woff,eot,svg,otf}'],
              		dest: 'Resources/Public/fonts/'
              	},
              	// The project Webicons
              	{
              		expand: true,
              		flatten: true,
              		cwd: 'Resources/Temp/webicons/',
              		src: ['**/*.{ttf,woff,eot,svg,otf}'],
              		dest: 'Resources/Public/fonts/'
              	},
              	// Webicons sass generated file
              	{
                  expand: true,
                  cwd: 'Resources/Temp/webicons/',
                  src: [
                    '*.scss'
                  ],
                  dest: 'Resources/Private/Scss/'
                },
                // Images
                {
                  expand: true,
                  cwd: 'Resources/Private/Images/',
                  src: '**/*',
                  dest: 'Resources/Public/img/'
                }
              ]
          }
      },
      watch: {
      	options: {
      		livereload: -1
      	},
          css: {
              files: [
                  'Resources/Private/**/*.scss',
              ],
              tasks: [
                  'rebuild:css'
              ]
          },
          js: {
              files: [
                  'Resources/Private/**/*.js'
              ],
              tasks: [
                  'rebuild:js'
              ]
          }
      },
      exec: {
          npm_update: {
              cmd: 'npm update'
          },
          bower_update: {
              cmd: 'bower update'
          },
          npm_install: {
              cmd: 'npm install'
          },
          bower_install: {
              cmd: 'bower install'
          }
      },
      periodic: {
          update: {
              when: 'daily',
              tasks: [
                  'exec:npm_update',
                  'exec:bower_update'
              ]
          },
          npm_install: {
              when: 'newer',
              tasks: [
                  'exec:npm_install',
                  'exec:npm_update'
              ],
              files: [{
                  src: [ 'package.json' ]
              }]
          },
          bower_install: {
              when: 'newer',
              tasks: [
                  'exec:bower_install',
                  'exec:bower_update'
              ],
              files: [{
                  src: [ 'bower.json' ]
              }]
          }
      }
    });

    grunt.registerTask('default', 'Gruntfile for Mockups', function() {
      grunt.warn('No task name specified.\n\nAvailable tasks: build, deploy"\n\n');
    });

    grunt.registerTask('build', 'Build a mockup and watch for file modifications', [
  		'rebuild',
  		'watch'
    ]);

    grunt.registerTask('rebuild', 'Build a mockup without watching for file modifications', function ( watchTask ) {
      var tasks = [];

      if ( watchTask === undefined ) watchTask = 'all';

      if ( watchTask == 'all' ) {
          tasks = tasks.concat([
            'periodic',
            'clean',
            'fontgen',
            'webfont',
            'copy',
            'concat:webfonts',
            'sass',
            'concat:css',
            'concat:cssPrint',
            'concat:js',
            'autoprefixer:build',
            'clean:temp'
          ]);
      } else if ( watchTask == 'css' ) {
          tasks = tasks.concat([
            'clean:cssFiles',
            'sass',
            'concat:css',
            'concat:cssPrint',
            'autoprefixer:build'
          ]);
      } else if ( watchTask == 'js' ) {
          tasks = tasks.concat([
            'concat:js'
          ]);
      }
      grunt.task.run(tasks);
    });

    grunt.registerTask('build_deploy', 'Build a mockup ready for deployment sync', [
    	'clean',
      'fontgen',
      'webfont',
      'copy',
      'concat:webfonts',
      'sass',
      'concat:css',
      'concat:cssPrint',
      'cssmin',
      'autoprefixer:deploy',
      'closurecompiler',
      'clean:temp'
    ]);

    grunt.registerTask('deploy', 'Deploy a mockup to the Mockups Server', [
		  'build_deploy'
    ]);
};
<|endoftext|>


<commit_before>window.issueTracker = 'https://github.com/scraperwiki/spreadsheet-download-tool/issues'

var reportAjaxError = function(jqXHR, textStatus, errorThrown, source){
  console.log(source + ' returned an ajax error:', jqXHR, textStatus, errorThrown)
  var message = 'The ' + source + ' function returned an ajax ' + textStatus + ' error.'
  if(typeof jqXHR.responseText == 'string'){
    message += ' The response text was:
' + $.trim(jqXHR.responseText) + '
'
  }
  message += 'Click here to log this as a bug.'
  scraperwiki.alert('There was a problem reading your dataset', message, true)
}

var resetGlobalVariables = function(){
  window.tablesAndGrids = {
    "tables": [],
    "grids": []
  }
  window.files = []
  window.timer = null
}

var getDatasetTablesAndGrids = function(cb){
  // calls the `cb` callback with an object containing
  // lists of tables and grids in the parent dataset
  // eg: {"tables": [{"id":"_grids", "name":"_grids"}], "grids": [...]}
  console.log('getDatasetTablesAndGrids()')
  scraperwiki.dataset.sql.meta().fail(function(jqXHR, textStatus, errorThrown){
    reportAjaxError(jqXHR, textStatus, errorThrown, 'scraperwiki.dataset.sql.meta()')
    cb(window.tablesAndGrids)
  }).done(function(meta){
    if(meta.table.length == 0){
      console.log('getDatasetTablesAndGrids() ... meta.table.length == 0')
      scraperwiki.alert('Your dataset has no tables', 'This shouldn’t really be an error. We should handle this more gracefully.')
      cb(window.tablesAndGrids)
    } else {
      console.log('getDatasetTablesAndGrids() ... found', _.keys(meta.table).length, 'tables:', _.keys(meta.table).join(', '))
      // add tables to window.tablesAndGrids
      $.each(meta.table, function(table_name, table_meta){
        // ignore tables beginning with an underscore
        if(table_name.indexOf('_') != 0){
          window.tablesAndGrids.tables.push({"id": table_name, "name": table_name})
        }
      })
      if('_grids' in meta.table){
        // add grids to window.tablesAndGrids
        console.log('getDatasetTablesAndGrids() ... found _grids table')
        scraperwiki.dataset.sql('SELECT * FROM _grids ORDER BY number').fail(function(jqXHR, textStatus, errorThrown){
          reportAjaxError(jqXHR, textStatus, errorThrown, 'scraperwiki.dataset.sql()')
          cb(window.tablesAndGrids)
        }).done(function(grids){
          console.log('getDatasetTablesAndGrids() ... found', grids.length, 'grids')
          $.each(grids, function(i, grid){
            window.tablesAndGrids.grids.push({"id": grid.checksum, "name": grid.title})
          })
          cb(window.tablesAndGrids)
        })
      } else {
        // no grids: ust return what we've got
        console.log('getDatasetTablesAndGrids() ... no _grids table')
        cb(window.tablesAndGrids)
      }
    }
  })
}

var generateFileList = function(cb){
  // given a load of sources in window.tablesAndGrids, and
  // information from the _state_files SQL table, this function
  // contructs a list of files generated / to be generated
  console.log('generateFileList() from', window.tablesAndGrids.tables.length, 'tables and', window.tablesAndGrids.grids.length, 'grids')
  $.each(window.tablesAndGrids.grids, function(i, grid){
    window.files.push({
      'filename': makeFilename(grid.name) + '.csv',
      'state': 'waiting',
      'created': null,
      'source_type': 'table',
      'source_id': grid.id
    })
  })
  window.files.push({
    'filename': 'all_tables.xlsx',
    'state': 'waiting',
    'created': null,
    'source_type': null,
    'source_id': 'all_tables'
  })
  updateFileList(cb)
}

var updateFileList = function(cb){
  console.log('updateFileList() called')
  scraperwiki.tool.sql('SELECT * FROM _state_files').done(function(files){
    console.log('updateFileList() got files')
    $.each(files, function(i, file){
      fileRecordToUpdate = _.findWhere(window.files, {'filename':file.filename})
      if(typeof fileRecordToUpdate !== 'undefined'){
        fileRecordToUpdate.state = file.state
        fileRecordToUpdate.created = file.created
      }
    })
    cb() // this callback is usually renderFiles()
  }).fail(function(jqXHR, textStatus, errorThrown){
    console.log('updateFileList() ajax error', jqXHR.responseText, textStatus, errorThrown)
    if(/does not exist/.test(jqXHR.responseText) || /no such table/.test(jqXHR.responseText) || /no such column/.test(jqXHR.responseText)){
      console.log('updateFileList() ... database or table does not exist')
      // kick off regeneration if we're not already running
      console.log('updateFileList() ... kicking off regenerate()')
      regenerate()
      cb() // this callback is usually renderFiles()
    } else {
      reportAjaxError(jqXHR, textStatus, errorThrown, 'scraperwiki.tool.sql("SELECT filename, state, created FROM _state_files")')
    }
  })
}

var makeFilename = function(naughtyString){
  return naughtyString.toLowerCase().replace(/\s+/g, '_').replace(/[^a-z0-9-_.]+/g, '')
}

var saveDatasetUrl = function(cb){
  console.log('saveDatasetUrl()')
  scraperwiki.tool.exec('echo "' + scraperwiki.readSettings().target.url + '" > ~/dataset_url.txt')
  if(typeof cb != 'undefined'){
    cb()
  }
}

var showEmptyDatasetMessage = function(){
  $('body').html('

This dataset is empty.

Once your dataset contains data,
your downloads will be generated here.

')
}

$(function(){

  resetGlobalVariables()

  saveDatasetUrl()

  getDatasetTablesAndGrids(function(){
    if(window.tablesAndGrids.tables.length + window.tablesAndGrids.grids.length == 0){
      showEmptyDatasetMessage()
    } else {
      generateFileList(function(){
        renderFiles()
        setTimer()
      })
    }
  })

  datasetUrl = scraperwiki.readSettings().target.url

  scraperwiki.sql.meta().done(function(metadata){
    $('#feeds').show()
    $('#loading').hide()

    $.each(metadata.table, function(name) {
      if (/^_/.test(name)) {
        return
      }
      var csvUrl = datasetUrl + "/cgi-bin/csv/"
      li = ('
'+
            name + '.csvlive
')
      $('#files').append(li)

      var xlsxUrl = datasetUrl + "/cgi-bin/xlsx/"
      li = ('
'+
            name + '.xlsxlive
')
      $('#archives').append(li)
    })
  })

  $('#refresh').on('click', refresh_click)

})
<commit_msg>Generate all_tables.xlsx<commit_after>window.issueTracker = 'https://github.com/scraperwiki/spreadsheet-download-tool/issues'

var reportAjaxError = function(jqXHR, textStatus, errorThrown, source){
  console.log(source + ' returned an ajax error:', jqXHR, textStatus, errorThrown)
  var message = 'The ' + source + ' function returned an ajax ' + textStatus + ' error.'
  if(typeof jqXHR.responseText == 'string'){
    message += ' The response text was:
' + $.trim(jqXHR.responseText) + '
'
  }
  message += 'Click here to log this as a bug.'
  scraperwiki.alert('There was a problem reading your dataset', message, true)
}

var resetGlobalVariables = function(){
  window.tablesAndGrids = {
    "tables": [],
    "grids": []
  }
  window.files = []
  window.timer = null
}

var getDatasetTablesAndGrids = function(cb){
  // calls the `cb` callback with an object containing
  // lists of tables and grids in the parent dataset
  // eg: {"tables": [{"id":"_grids", "name":"_grids"}], "grids": [...]}
  console.log('getDatasetTablesAndGrids()')
  scraperwiki.dataset.sql.meta().fail(function(jqXHR, textStatus, errorThrown){
    reportAjaxError(jqXHR, textStatus, errorThrown, 'scraperwiki.dataset.sql.meta()')
    cb(window.tablesAndGrids)
  }).done(function(meta){
    if(meta.table.length == 0){
      console.log('getDatasetTablesAndGrids() ... meta.table.length == 0')
      scraperwiki.alert('Your dataset has no tables', 'This shouldn’t really be an error. We should handle this more gracefully.')
      cb(window.tablesAndGrids)
    } else {
      console.log('getDatasetTablesAndGrids() ... found', _.keys(meta.table).length, 'tables:', _.keys(meta.table).join(', '))
      // add tables to window.tablesAndGrids
      $.each(meta.table, function(table_name, table_meta){
        // ignore tables beginning with an underscore
        if(table_name.indexOf('_') != 0){
          window.tablesAndGrids.tables.push({"id": table_name, "name": table_name})
        }
      })
      if('_grids' in meta.table){
        // add grids to window.tablesAndGrids
        console.log('getDatasetTablesAndGrids() ... found _grids table')
        scraperwiki.dataset.sql('SELECT * FROM _grids ORDER BY number').fail(function(jqXHR, textStatus, errorThrown){
          reportAjaxError(jqXHR, textStatus, errorThrown, 'scraperwiki.dataset.sql()')
          cb(window.tablesAndGrids)
        }).done(function(grids){
          console.log('getDatasetTablesAndGrids() ... found', grids.length, 'grids')
          $.each(grids, function(i, grid){
            window.tablesAndGrids.grids.push({"id": grid.checksum, "name": grid.title})
          })
          cb(window.tablesAndGrids)
        })
      } else {
        // no grids: ust return what we've got
        console.log('getDatasetTablesAndGrids() ... no _grids table')
        cb(window.tablesAndGrids)
      }
    }
  })
}

var generateFileList = function(cb){
  // given a load of sources in window.tablesAndGrids, and
  // information from the _state_files SQL table, this function
  // contructs a list of files generated / to be generated
  console.log('generateFileList() from', window.tablesAndGrids.tables.length, 'tables and', window.tablesAndGrids.grids.length, 'grids')
  $.each(window.tablesAndGrids.grids, function(i, grid){
    window.files.push({
      'filename': makeFilename(grid.name) + '.csv',
      'state': 'waiting',
      'created': null,
      'source_type': 'table',
      'source_id': grid.id
    })
  })
  window.files.push({
    'filename': 'all_tables.xlsx',
    'state': 'waiting',
    'created': null,
    'source_type': null,
    'source_id': 'all_tables'
  })
  updateFileList(cb)
}

var updateFileList = function(cb){
  console.log('updateFileList() called')
  scraperwiki.tool.sql('SELECT * FROM _state_files').done(function(files){
    console.log('updateFileList() got files')
    $.each(files, function(i, file){
      fileRecordToUpdate = _.findWhere(window.files, {'filename':file.filename})
      if(typeof fileRecordToUpdate !== 'undefined'){
        fileRecordToUpdate.state = file.state
        fileRecordToUpdate.created = file.created
      }
    })
    cb() // this callback is usually renderFiles()
  }).fail(function(jqXHR, textStatus, errorThrown){
    console.log('updateFileList() ajax error', jqXHR.responseText, textStatus, errorThrown)
    if(/does not exist/.test(jqXHR.responseText) || /no such table/.test(jqXHR.responseText) || /no such column/.test(jqXHR.responseText)){
      console.log('updateFileList() ... database or table does not exist')
      // kick off regeneration if we're not already running
      console.log('updateFileList() ... kicking off regenerate()')
      regenerate()
      cb() // this callback is usually renderFiles()
    } else {
      reportAjaxError(jqXHR, textStatus, errorThrown, 'scraperwiki.tool.sql("SELECT filename, state, created FROM _state_files")')
    }
  })
}

var makeFilename = function(naughtyString){
  return naughtyString.toLowerCase().replace(/\s+/g, '_').replace(/[^a-z0-9-_.]+/g, '')
}

var saveDatasetUrl = function(cb){
  console.log('saveDatasetUrl()')
  scraperwiki.tool.exec('echo "' + scraperwiki.readSettings().target.url + '" > ~/dataset_url.txt')
  if(typeof cb != 'undefined'){
    cb()
  }
}

var showEmptyDatasetMessage = function(){
  $('body').html('

This dataset is empty.

Once your dataset contains data,
your downloads will be generated here.

')
}

$(function(){

  resetGlobalVariables()

  saveDatasetUrl()

  getDatasetTablesAndGrids(function(){
    if(window.tablesAndGrids.tables.length + window.tablesAndGrids.grids.length == 0){
      showEmptyDatasetMessage()
    } else {
      var xlsxUrl = datasetUrl + "/cgi-bin/xlsx/"
      li = ('
'+
            'all_tables.xlsxlive
')
      $('#archives').append(li)

    }
  })

  datasetUrl = scraperwiki.readSettings().target.url

  scraperwiki.sql.meta().done(function(metadata){
    $('#feeds').show()
    $('#loading').hide()

    $.each(metadata.table, function(name) {
      if (/^_/.test(name)) {
        return
      }
      var csvUrl = datasetUrl + "/cgi-bin/csv/"
      li = ('
'+
            name + '.csvlive
')
      $('#files').append(li)

      var xlsxUrl = datasetUrl + "/cgi-bin/xlsx/"
      li = ('
'+
            name + '.xlsxlive
')
      $('#archives').append(li)
    })
  })

  $('#refresh').on('click', refresh_click)

})
<|endoftext|>


<commit_before>'use strict';

/**
 * The proxy implementation for http/s imposters
 * @module
 */

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

/**
 * Creates the proxy
 * @param {Object} logger - The logger
 * @returns {Object}
 */
function create (logger) {
    const BINARY_CONTENT_ENCODINGS = [
        "gzip", "br", "compress", "deflate"
    ];
    const BINARY_MIME_TYPES = [
        'audio/',
        'application/epub+zip',
        'application/gzip',
        'application/java-archive',
        'application/msword',
        'application/octet-stream',
        'application/pdf',
        'application/rtf',
        'application/vnd.ms-excel',
        'application/vnd.ms-fontobject',
        'application/vnd.ms-powerpoint',
        'application/vnd.visio',
        'application/x-shockwave-flash',
        'application/x-tar',
        'application/zip',
        'font/',
        'image/',
        'model/',
        'video/'
    ];

    function addInjectedHeadersTo (request, headersToInject) {
        Object.keys(headersToInject || {}).forEach(key => {
            request.headers[key] = headersToInject[key];
        });
    }

    function toUrl (path, query, requestDetails) {
        if (requestDetails) {
            // Not passed in outOfProcess mode
            return requestDetails.rawUrl;
        }

        const querystring = require('querystring'),
            tail = querystring.stringify(query);

        if (tail === '') {
            return path;
        }
        return `${path}?${tail}`;
    }

    function hostnameFor (protocol, host, port) {
        let result = host;
        if ((protocol === 'http:' && port !== 80) || (protocol === 'https:' && port !== 443)) {
            result += `:${port}`;
        }
        return result;
    }

    function setProxyAgent (parts, options) {
        const HttpProxyAgent = require('http-proxy-agent'),
            HttpsProxyAgent = require('https-proxy-agent');

        if (process.env.http_proxy && parts.protocol === 'http:') {
            options.agent = new HttpProxyAgent(process.env.http_proxy);
        }
        else if (process.env.https_proxy && parts.protocol === 'https:') {
            options.agent = new HttpsProxyAgent(process.env.https_proxy);
        }
    }

    function getProxyRequest (baseUrl, originalRequest, proxyOptions, requestDetails) {
        /* eslint complexity: 0 */
        const helpers = require('../../util/helpers'),
            headersMap = require('./headersMap'),
            parts = new URL(baseUrl),
            protocol = parts.protocol === 'https:' ? require('https') : require('http'),
            defaultPort = parts.protocol === 'https:' ? 443 : 80,
            options = {
                method: originalRequest.method,
                hostname: parts.hostname,
                port: parts.port || defaultPort,
                auth: parts.auth,
                path: toUrl(originalRequest.path, originalRequest.query, requestDetails),
                headers: helpers.clone(originalRequest.headers),
                cert: proxyOptions.cert,
                key: proxyOptions.key,
                ciphers: proxyOptions.ciphers || 'ALL',
                secureProtocol: proxyOptions.secureProtocol,
                passphrase: proxyOptions.passphrase,
                rejectUnauthorized: false
            };

        // Only set host header if not overridden via injectHeaders (issue #388)
        if (!proxyOptions.injectHeaders || !headersMap.of(proxyOptions.injectHeaders).has('host')) {
            options.headers.host = hostnameFor(parts.protocol, parts.hostname, options.port);
        }
        setProxyAgent(parts, options);

        // Avoid implicit chunked encoding (issue #132)
        if (originalRequest.body &&
            !headersMap.of(originalRequest.headers).has('Transfer-Encoding') &&
            !headersMap.of(originalRequest.headers).has('Content-Length')) {
            options.headers['Content-Length'] = Buffer.byteLength(originalRequest.body);
        }

        const proxiedRequest = protocol.request(options);
        if (originalRequest.body) {
            proxiedRequest.write(originalRequest.body);
        }
        return proxiedRequest;
    }

    function isBinaryResponse (headers) {
        const contentEncoding = headers['content-encoding'] || '',
            contentType = headers['content-type'] || '';

        if (BINARY_CONTENT_ENCODINGS.some( binEncoding => contentEncoding.indexOf(binEncoding) > 0))
        {
            return true;
        }

        return BINARY_MIME_TYPES.some(typeName => contentType.indexOf(typeName) >= 0);
    }

    function proxy (proxiedRequest) {
        return new Promise(resolve => {
            proxiedRequest.end();

            proxiedRequest.once('response', response => {
                const packets = [];

                response.on('data', chunk => {
                    packets.push(chunk);
                });

                response.on('end', () => {
                    const body = Buffer.concat(packets),
                        mode = isBinaryResponse(response.headers) ? 'binary' : 'text',
                        encoding = mode === 'binary' ? 'base64' : 'utf8',
                        headersMap = require('./headersMap'),
                        stubResponse = {
                            statusCode: response.statusCode,
                            headers: headersMap.ofRaw(response.rawHeaders).all(),
                            body: body.toString(encoding),
                            _mode: mode
                        };
                    resolve(stubResponse);
                });
            });
        });
    }

    /**
     * Proxies an http/s request to a destination
     * @memberOf module:models/http/httpProxy#
     * @param {string} proxyDestination - The base URL to proxy to, without a path (e.g. http://www.google.com)
     * @param {Object} originalRequest - The original http/s request to forward on to proxyDestination
     * @param {Object} options - Proxy options
     * @param {string} [options.cert] - The certificate, in case the destination requires mutual authentication
     * @param {string} [options.key] - The private key, in case the destination requires mutual authentication
     * @param {Object} [options.injectHeaders] - The headers to inject in the proxied request
     * @param {Object} [options.passphrase] - The passphrase for the private key
     * @param {Object} requestDetails - Additional details about the request not stored in the simplified JSON
     * @returns {Object} - Promise resolving to the response
     */
    function to (proxyDestination, originalRequest, options, requestDetails) {

        addInjectedHeadersTo(originalRequest, options.injectHeaders);

        function log (direction, what) {
            logger.debug('Proxy %s %s %s %s %s',
                originalRequest.requestFrom, direction, JSON.stringify(what), direction, proxyDestination);
        }

        return new Promise((resolve, reject) => {
            const errors = require('../../util/errors');

            let proxiedRequest;
            try {
                proxiedRequest = getProxyRequest(proxyDestination, originalRequest, options, requestDetails);
            }
            catch (e) {
                reject(errors.InvalidProxyError(`Unable to connect to ${JSON.stringify(proxyDestination)}`));
            }

            log('=>', originalRequest);

            proxiedRequest.once('error', error => {
                if (error.code === 'ENOTFOUND') {
                    reject(errors.InvalidProxyError(`Cannot resolve ${JSON.stringify(proxyDestination)}`));
                }
                else if (error.code === 'ECONNREFUSED' || error.code === 'ECONNRESET') {
                    reject(errors.InvalidProxyError(`Unable to connect to ${JSON.stringify(proxyDestination)}`));
                }
                else {
                    reject(error);
                }
            });

            proxy(proxiedRequest).then(response => {
                log('<=', response);
                resolve(response);
            });
        });
    }

    return { to };
}

module.exports = { create };
<commit_msg>Fixed `gzip` was missed in isBinaryResponse check<commit_after>'use strict';

/**
 * The proxy implementation for http/s imposters
 * @module
 */

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

/**
 * Creates the proxy
 * @param {Object} logger - The logger
 * @returns {Object}
 */
function create (logger) {
    const BINARY_CONTENT_ENCODINGS = [
        "gzip", "br", "compress", "deflate"
    ];
    const BINARY_MIME_TYPES = [
        'audio/',
        'application/epub+zip',
        'application/gzip',
        'application/java-archive',
        'application/msword',
        'application/octet-stream',
        'application/pdf',
        'application/rtf',
        'application/vnd.ms-excel',
        'application/vnd.ms-fontobject',
        'application/vnd.ms-powerpoint',
        'application/vnd.visio',
        'application/x-shockwave-flash',
        'application/x-tar',
        'application/zip',
        'font/',
        'image/',
        'model/',
        'video/'
    ];

    function addInjectedHeadersTo (request, headersToInject) {
        Object.keys(headersToInject || {}).forEach(key => {
            request.headers[key] = headersToInject[key];
        });
    }

    function toUrl (path, query, requestDetails) {
        if (requestDetails) {
            // Not passed in outOfProcess mode
            return requestDetails.rawUrl;
        }

        const querystring = require('querystring'),
            tail = querystring.stringify(query);

        if (tail === '') {
            return path;
        }
        return `${path}?${tail}`;
    }

    function hostnameFor (protocol, host, port) {
        let result = host;
        if ((protocol === 'http:' && port !== 80) || (protocol === 'https:' && port !== 443)) {
            result += `:${port}`;
        }
        return result;
    }

    function setProxyAgent (parts, options) {
        const HttpProxyAgent = require('http-proxy-agent'),
            HttpsProxyAgent = require('https-proxy-agent');

        if (process.env.http_proxy && parts.protocol === 'http:') {
            options.agent = new HttpProxyAgent(process.env.http_proxy);
        }
        else if (process.env.https_proxy && parts.protocol === 'https:') {
            options.agent = new HttpsProxyAgent(process.env.https_proxy);
        }
    }

    function getProxyRequest (baseUrl, originalRequest, proxyOptions, requestDetails) {
        /* eslint complexity: 0 */
        const helpers = require('../../util/helpers'),
            headersMap = require('./headersMap'),
            parts = new URL(baseUrl),
            protocol = parts.protocol === 'https:' ? require('https') : require('http'),
            defaultPort = parts.protocol === 'https:' ? 443 : 80,
            options = {
                method: originalRequest.method,
                hostname: parts.hostname,
                port: parts.port || defaultPort,
                auth: parts.auth,
                path: toUrl(originalRequest.path, originalRequest.query, requestDetails),
                headers: helpers.clone(originalRequest.headers),
                cert: proxyOptions.cert,
                key: proxyOptions.key,
                ciphers: proxyOptions.ciphers || 'ALL',
                secureProtocol: proxyOptions.secureProtocol,
                passphrase: proxyOptions.passphrase,
                rejectUnauthorized: false
            };

        // Only set host header if not overridden via injectHeaders (issue #388)
        if (!proxyOptions.injectHeaders || !headersMap.of(proxyOptions.injectHeaders).has('host')) {
            options.headers.host = hostnameFor(parts.protocol, parts.hostname, options.port);
        }
        setProxyAgent(parts, options);

        // Avoid implicit chunked encoding (issue #132)
        if (originalRequest.body &&
            !headersMap.of(originalRequest.headers).has('Transfer-Encoding') &&
            !headersMap.of(originalRequest.headers).has('Content-Length')) {
            options.headers['Content-Length'] = Buffer.byteLength(originalRequest.body);
        }

        const proxiedRequest = protocol.request(options);
        if (originalRequest.body) {
            proxiedRequest.write(originalRequest.body);
        }
        return proxiedRequest;
    }

    function isBinaryResponse (headers) {
        const contentEncoding = headers['content-encoding'] || '',
            contentType = headers['content-type'] || '';

        if (BINARY_CONTENT_ENCODINGS.some( binEncoding => contentEncoding.indexOf(binEncoding) >= 0))
        {
            return true;
        }

        return BINARY_MIME_TYPES.some(typeName => contentType.indexOf(typeName) >= 0);
    }

    function proxy (proxiedRequest) {
        return new Promise(resolve => {
            proxiedRequest.end();

            proxiedRequest.once('response', response => {
                const packets = [];

                response.on('data', chunk => {
                    packets.push(chunk);
                });

                response.on('end', () => {
                    const body = Buffer.concat(packets),
                        mode = isBinaryResponse(response.headers) ? 'binary' : 'text',
                        encoding = mode === 'binary' ? 'base64' : 'utf8',
                        headersMap = require('./headersMap'),
                        stubResponse = {
                            statusCode: response.statusCode,
                            headers: headersMap.ofRaw(response.rawHeaders).all(),
                            body: body.toString(encoding),
                            _mode: mode
                        };
                    resolve(stubResponse);
                });
            });
        });
    }

    /**
     * Proxies an http/s request to a destination
     * @memberOf module:models/http/httpProxy#
     * @param {string} proxyDestination - The base URL to proxy to, without a path (e.g. http://www.google.com)
     * @param {Object} originalRequest - The original http/s request to forward on to proxyDestination
     * @param {Object} options - Proxy options
     * @param {string} [options.cert] - The certificate, in case the destination requires mutual authentication
     * @param {string} [options.key] - The private key, in case the destination requires mutual authentication
     * @param {Object} [options.injectHeaders] - The headers to inject in the proxied request
     * @param {Object} [options.passphrase] - The passphrase for the private key
     * @param {Object} requestDetails - Additional details about the request not stored in the simplified JSON
     * @returns {Object} - Promise resolving to the response
     */
    function to (proxyDestination, originalRequest, options, requestDetails) {

        addInjectedHeadersTo(originalRequest, options.injectHeaders);

        function log (direction, what) {
            logger.debug('Proxy %s %s %s %s %s',
                originalRequest.requestFrom, direction, JSON.stringify(what), direction, proxyDestination);
        }

        return new Promise((resolve, reject) => {
            const errors = require('../../util/errors');

            let proxiedRequest;
            try {
                proxiedRequest = getProxyRequest(proxyDestination, originalRequest, options, requestDetails);
            }
            catch (e) {
                reject(errors.InvalidProxyError(`Unable to connect to ${JSON.stringify(proxyDestination)}`));
            }

            log('=>', originalRequest);

            proxiedRequest.once('error', error => {
                if (error.code === 'ENOTFOUND') {
                    reject(errors.InvalidProxyError(`Cannot resolve ${JSON.stringify(proxyDestination)}`));
                }
                else if (error.code === 'ECONNREFUSED' || error.code === 'ECONNRESET') {
                    reject(errors.InvalidProxyError(`Unable to connect to ${JSON.stringify(proxyDestination)}`));
                }
                else {
                    reject(error);
                }
            });

            proxy(proxiedRequest).then(response => {
                log('<=', response);
                resolve(response);
            });
        });
    }

    return { to };
}

module.exports = { create };
<|endoftext|>


<commit_before>var _ = require('lodash');
var Node = require('./node');

function Network(config, game, rapidMutation) {
  this.nodes = [];
  this.config = config;
  this.game = game;
  this.rapidMutation = rapidMutation;
  this.generation = 0;
  this.defaultSizeWeighting = 1;

  // This means about 10% - 20% of the population will incur a node swap.
  this.swapChance = 2 / config.length;

  if (this.rapidMutation) {
    this.swapChance = 4 / config.length;
  }

  this.weighting = {
    size: this.defaultSizeWeighting,
    area: 10,
    lines: 100,
    intersect: 200,
    outOfBounds: 6000,
  };

  // We don't want to shrink the network initially, this ease reorganising the items.
  if (this.rapidMutation) {
    this.weighting.size = 0;
  }
}

Network.prototype.disableRapidMutation = function() {
  this.weighting.size = this.defaultSizeWeighting;
  this.rapidMutation = false;
};

Network.prototype.getFirstGeneration = function() {
  _.each(this.config, _.bind(function(nodeConfig) {
    this.nodes.push(new Node(nodeConfig, null, this.game, this.rapidMutation));
  }, this));

  this.connectNodes();
};

Network.prototype.connectNodes = function() {
  _.each(this.nodes, _.bind(function (node) {
    node.setConnections(this.nodes);
  }, this));

  this.genotype = this.getGenotype();
  this.fitness = this.getFitness();
};

Network.prototype.mutate = function(generation) {
  var child = new Network(this.config, this.game, this.rapidMutation);
  child.generation = generation;

  _.each(this.nodes, _.bind(function(node) {
    child.nodes.push(new Node(node.config, node, this.game, this.rapidMutation));
  }, this));

  child.swapSomeNodes();

  child.connectNodes(child.nodes);
  return child;
};

Network.prototype.swapSomeNodes = function() {

  if (Math.random() < this.swapChance) {
    var a = _.sample(this.nodes);
    var b = _.sample(this.nodes);

    var tempPosition = {
      x: b.x,
      y: b.y
    };

    b.setPosition(a.x, a.y);
    a.setPosition(tempPosition.x, tempPosition.y);
  }

};

Network.prototype.getGenotype = function() {
  return _.map(this.nodes, 'genotype');
};

Network.prototype.reportFitness = function() {
  this.getFitness(true);
};

Network.prototype.getFitness = function(doReporting) {

  var fitness = 0;
  var results = [];

  _.each(this.nodes, _.bind(function(node) {

    // Calculate total line distance.
    var size = node.getConnectionDistance() * this.weighting.size;

    // Check for nodes overlapping.
    var areaOfOverlappingNodes = node.getAreaOfOverlappingNodes() * this.weighting.area;

    // check for line intersections.
    var numberOfOverlappingLines = node.getNumberOfIntersectingLines() * this.weighting.lines;

    // check if lines intersect with other nodes.
    var numberOfLinesIntersectingNodes = node.getNumberOfLineNodeIntersects() * this.weighting.intersect;

    var outOfBounds = 0;

    if (node.isOutOfBounds()) {
      outOfBounds = 1 * this.weighting.outOfBounds;
    }

    if (doReporting) {
      results.push({
        name: node.name,
        size: size,
        areaOfOverlappingNodes: areaOfOverlappingNodes,
        numberOfOverlappingLines: numberOfOverlappingLines,
        outOfBounds: outOfBounds,
        numberOfLinesIntersectingNodes: numberOfLinesIntersectingNodes
      });
    }

    fitness += size + outOfBounds + areaOfOverlappingNodes + numberOfOverlappingLines + numberOfLinesIntersectingNodes;
  }, this));

  if (doReporting) {
    console.table(results);
  }

  return fitness;
};

Network.prototype.draw = function() {

  _.each(this.nodes, function(node) {
    node.drawBox();
  });

  _.each(this.nodes, function(node) {
    node.drawConnections();
  });

  this.drawStats();
};

Network.prototype.drawStats = function() {
  var textStyle = {
    fill: 'red',
    fontSize: '22px'
  };

  var species = this.rapidMutation ? 'Island' : 'Mainland';

  this.game.add.text(40, 20, 'Generation: ' + this.generation, textStyle);
  this.game.add.text(40, 70, 'Species: ' + species, textStyle);
  this.game.add.text(40, 45, 'Fitness: ' + this.fitness, textStyle);
};

module.exports = Network;
<commit_msg>Drawing lines under boxes.<commit_after>var _ = require('lodash');
var Node = require('./node');

function Network(config, game, rapidMutation) {
  this.nodes = [];
  this.config = config;
  this.game = game;
  this.rapidMutation = rapidMutation;
  this.generation = 0;
  this.defaultSizeWeighting = 1;

  // This means about 10% - 20% of the population will incur a node swap.
  this.swapChance = 2 / config.length;

  if (this.rapidMutation) {
    this.swapChance = 4 / config.length;
  }

  this.weighting = {
    size: this.defaultSizeWeighting,
    area: 10,
    lines: 100,
    intersect: 200,
    outOfBounds: 6000,
  };

  // We don't want to shrink the network initially, this ease reorganising the items.
  if (this.rapidMutation) {
    this.weighting.size = 0;
  }
}

Network.prototype.disableRapidMutation = function() {
  this.weighting.size = this.defaultSizeWeighting;
  this.rapidMutation = false;
};

Network.prototype.getFirstGeneration = function() {
  _.each(this.config, _.bind(function(nodeConfig) {
    this.nodes.push(new Node(nodeConfig, null, this.game, this.rapidMutation));
  }, this));

  this.connectNodes();
};

Network.prototype.connectNodes = function() {
  _.each(this.nodes, _.bind(function (node) {
    node.setConnections(this.nodes);
  }, this));

  this.genotype = this.getGenotype();
  this.fitness = this.getFitness();
};

Network.prototype.mutate = function(generation) {
  var child = new Network(this.config, this.game, this.rapidMutation);
  child.generation = generation;

  _.each(this.nodes, _.bind(function(node) {
    child.nodes.push(new Node(node.config, node, this.game, this.rapidMutation));
  }, this));

  child.swapSomeNodes();

  child.connectNodes(child.nodes);
  return child;
};

Network.prototype.swapSomeNodes = function() {

  if (Math.random() < this.swapChance) {
    var a = _.sample(this.nodes);
    var b = _.sample(this.nodes);

    var tempPosition = {
      x: b.x,
      y: b.y
    };

    b.setPosition(a.x, a.y);
    a.setPosition(tempPosition.x, tempPosition.y);
  }

};

Network.prototype.getGenotype = function() {
  return _.map(this.nodes, 'genotype');
};

Network.prototype.reportFitness = function() {
  this.getFitness(true);
};

Network.prototype.getFitness = function(doReporting) {

  var fitness = 0;
  var results = [];

  _.each(this.nodes, _.bind(function(node) {

    // Calculate total line distance.
    var size = node.getConnectionDistance() * this.weighting.size;

    // Check for nodes overlapping.
    var areaOfOverlappingNodes = node.getAreaOfOverlappingNodes() * this.weighting.area;

    // check for line intersections.
    var numberOfOverlappingLines = node.getNumberOfIntersectingLines() * this.weighting.lines;

    // check if lines intersect with other nodes.
    var numberOfLinesIntersectingNodes = node.getNumberOfLineNodeIntersects() * this.weighting.intersect;

    var outOfBounds = 0;

    if (node.isOutOfBounds()) {
      outOfBounds = 1 * this.weighting.outOfBounds;
    }

    if (doReporting) {
      results.push({
        name: node.name,
        size: size,
        areaOfOverlappingNodes: areaOfOverlappingNodes,
        numberOfOverlappingLines: numberOfOverlappingLines,
        outOfBounds: outOfBounds,
        numberOfLinesIntersectingNodes: numberOfLinesIntersectingNodes
      });
    }

    fitness += size + outOfBounds + areaOfOverlappingNodes + numberOfOverlappingLines + numberOfLinesIntersectingNodes;
  }, this));

  if (doReporting) {
    console.table(results);
  }

  return fitness;
};

Network.prototype.draw = function() {

  _.each(this.nodes, function(node) {
    node.drawConnections();
  });

  _.each(this.nodes, function(node) {
    node.drawBox();
  });

  this.drawStats();
};

Network.prototype.drawStats = function() {
  var textStyle = {
    fill: 'red',
    fontSize: '22px'
  };

  var species = this.rapidMutation ? 'Island' : 'Mainland';

  this.game.add.text(40, 20, 'Generation: ' + this.generation, textStyle);
  this.game.add.text(40, 70, 'Species: ' + species, textStyle);
  this.game.add.text(40, 45, 'Fitness: ' + this.fitness, textStyle);
};

module.exports = Network;
<|endoftext|>


<commit_before>'use strict';

import { axios as http } from 'axios';

function instapago({keyId, publicKeyId, strict = true}) {
  if (!keyId || !publicKeyId) {
    throw new Error('Los parámetos keyId y publicKeyId son requerido');
  } else if (typeof keyId !== 'string' || typeof publicKeyId !== 'string') {
    throw new Error('Los parámetos keyId y publicKeyId deben ser String.');
  }

  const config = {
    keyId,
    publicKeyId,
    strict
  };

  return {
    pay: data => processPayment('pay', config, data),
    resume: data => processPayment('resume', config, data),
    cancel: data => processPayment('cancel', config, data),
    view: data => processPayment('view', config, data)
  }
}

function processPayment(type, config, data) {
  const validation = validatePaymentData(type, data);
  const params = Object.assign({}, config, data);
  let endpoint;
  let method;

  switch (type) {
    case 'pay':
      endpoint = 'payment';
      method = 'POST';
      break;

    case 'resume':
      endpoint = 'complete';
      method = 'POST';
      break;

    case 'cancel':
      endpoint = 'payment';
      method = 'DELETE';
      break;

    case 'view':
      endpoint = 'payment';
      method = 'GET';
      break;
  }

  if (config.strict && validation.error) {
    return new Promise((resolve, reject) => reject(validation.error));
  } else {
    return http({
      method,
      url: `https://api.instapago.com/${endpoint}`,
      params: params
    });
  }
}

function validatePaymentData(type, data) {
  const result = {};
  const _data = {};

  Object.keys(data).forEach(key => {
    Object.assign(_data, {[key.toLowerCase()]: data[key]});
  });

  const rules = [
    {
      param: 'amount',
      rule: 'Sólo caracteres numéricos y punto (.) como separador decimal.',
      valid: /[0-9]/.test(_data.amount)
    },
    {
      param: 'description',
      rule: 'Cadena de caracteres con los detalles de la operación.',
      valid: typeof _data.description === 'string'
    },
    {
      param: 'cardholder',
      rule: 'Sólo caracteres alfabéticos, incluyendo la letra ñ y espacio.',
      valid: /^[ñA-Za-z\s]*$/.test(_data.cardholder)
    },
    {
      param: 'cardholderid',
      rule: 'Sólo caracteres numéricos; mínimo 6 dígitos y máximo 8.',
      valid: /^[0-9]{6,8}$/.test(_data.cardholderid)
    },
    {
      param: 'cardnumber',
      rule: 'Sólo caracteres numéricos; mínimo 15 dígitos y máximo 16.',
      valid: /^[0-9]{15,16}$/.test(_data.cardnumber)
    },
    {
      param: 'cvc',
      rule: 'Sólo caracteres numéricos; deben ser 3 dígitos.',
      valid: /^[0-9]{3}$/.test(_data.cvc)
    },
    {
      param: 'expirationdate',
      rule: 'Sólo fechas mayores a la fecha en curso, en formato MM/YYYY.',
      valid: isCardExpired(_data.expirationdate)
    },
    {
      param: 'statusid',
      rule: 'Sólo caracteres numéricos; debe ser 1 dígito.',
      valid: /^[1-2]{1}$/.test(_data.statusid)
    },
    {
      param: 'ip',
      rule: 'Dirección IP del cliente que genera la solicitud del pago.',
      valid: typeof _data.description === 'string'
    }
  ];
  let requiredParams = ['id'];

  if (type === 'pay') {
    requiredParams = [
      'amount',
      'description',
      'cardholder',
      'cardholderid',
      'cardnumber',
      'cvc',
      'expirationdate',
      'statusid',
      'ip'
    ];
  } else if (type === 'resume') {
    requiredParams = ['id', 'amount'];
  }

  requiredParams.every(param => {
    const _param = rules.find(rule => rule.param === param);

    if (_data[param] && _param.valid) {
      return true;
    }

    result.error = new Error(`Parámetro inválido (${param}): ${_param.rule}`);

    return false;
  });

  return result;
}

function isCardExpired(date) {
  const _date = date ? date.split('/') : null;

  if (_date) {
    const yearOnCard = parseInt(_date[1]);
    const monthOnCard = parseInt(_date[0]);
    const year = new Date().getFullYear();
    const month = new Date().getUTCMonth() + 1;

    return ((yearOnCard === year && monthOnCard >= month) || yearOnCard > year);
  }

  return false;
}

export default instapago;
<commit_msg>Mejora instapago & processPayment<commit_after>'use strict';

import { axios as http } from 'axios';

function instapago(keyId, publicKeyId, strict = true) {
  if (!keyId || !publicKeyId) {
    throw new Error('Los parámetros keyId y publicKeyId son requeridos.');
  } else if (typeof keyId !== 'string' || typeof publicKeyId !== 'string') {
    throw new Error('Los parámetros keyId y publicKeyId deben ser String.');
  }

  const config = {
    strict,
    keys: {
      keyId,
      publicKeyId
    }
  };

  return {
    pay: data => processPayment('pay', config, data),
    resume: data => processPayment('resume', config, data),
    cancel: data => processPayment('cancel', config, data),
    view: data => processPayment('view', config, data)
  }
}

function processPayment(type, config, data) {
  const validation = validatePaymentData(type, data);
  const params = Object.assign({}, config.keys, data);
  let endpoint = 'payment';
  let method = 'POST';

  if (type !== 'pay') {
    if (type === 'resume') endpoint = 'complete';
    if (type === 'cancel') method = 'DELETE';
    if (type === 'view') method = 'GET';
  }

  if (config.strict && validation.error) {
    return new Promise((resolve, reject) => reject(validation.error));
  } else {
    return http({
      method,
      url: `https://api.instapago.com/${endpoint}`,
      params: params
    });
  }
}

function validatePaymentData(type, data) {
  const result = {};
  const _data = {};

  Object.keys(data).forEach(key => {
    Object.assign(_data, {[key.toLowerCase()]: data[key]});
  });

  const rules = [
    {
      param: 'amount',
      rule: 'Sólo caracteres numéricos y punto (.) como separador decimal.',
      valid: /[0-9]/.test(_data.amount)
    },
    {
      param: 'description',
      rule: 'Cadena de caracteres con los detalles de la operación.',
      valid: typeof _data.description === 'string'
    },
    {
      param: 'cardholder',
      rule: 'Sólo caracteres alfabéticos, incluyendo la letra ñ y espacio.',
      valid: /^[ñA-Za-z\s]*$/.test(_data.cardholder)
    },
    {
      param: 'cardholderid',
      rule: 'Sólo caracteres numéricos; mínimo 6 dígitos y máximo 8.',
      valid: /^[0-9]{6,8}$/.test(_data.cardholderid)
    },
    {
      param: 'cardnumber',
      rule: 'Sólo caracteres numéricos; mínimo 15 dígitos y máximo 16.',
      valid: /^[0-9]{15,16}$/.test(_data.cardnumber)
    },
    {
      param: 'cvc',
      rule: 'Sólo caracteres numéricos; deben ser 3 dígitos.',
      valid: /^[0-9]{3}$/.test(_data.cvc)
    },
    {
      param: 'expirationdate',
      rule: 'Sólo fechas mayores a la fecha en curso, en formato MM/YYYY.',
      valid: isCardExpired(_data.expirationdate)
    },
    {
      param: 'statusid',
      rule: 'Sólo caracteres numéricos; debe ser 1 dígito.',
      valid: /^[1-2]{1}$/.test(_data.statusid)
    },
    {
      param: 'ip',
      rule: 'Dirección IP del cliente que genera la solicitud del pago.',
      valid: typeof _data.description === 'string'
    }
  ];
  let requiredParams = ['id'];

  if (type === 'pay') {
    requiredParams = [
      'amount',
      'description',
      'cardholder',
      'cardholderid',
      'cardnumber',
      'cvc',
      'expirationdate',
      'statusid',
      'ip'
    ];
  } else if (type === 'resume') {
    requiredParams = ['id', 'amount'];
  }

  requiredParams.every(param => {
    const _param = rules.find(rule => rule.param === param);

    if (_data[param] && _param.valid) {
      return true;
    }

    result.error = new Error(`Parámetro inválido (${param}): ${_param.rule}`);

    return false;
  });

  return result;
}

function isCardExpired(date) {
  const _date = date ? date.split('/') : null;

  if (_date) {
    const yearOnCard = parseInt(_date[1]);
    const monthOnCard = parseInt(_date[0]);
    const year = new Date().getFullYear();
    const month = new Date().getUTCMonth() + 1;

    return ((yearOnCard === year && monthOnCard >= month) || yearOnCard > year);
  }

  return false;
}

export default instapago;
<|endoftext|>


<commit_before>const _ = require('lodash');
const properties = require('../properties');

const play = {};

function createWater(game, height) {
    const water = game.add.tileSprite(0, height - 128, game.world.width, 128, 'water');

    game.physics.arcade.enable([water]);
    // With the current texture (view angle),
    // the point of contact is somewhere in the middle
    water.body.setSize(water.width, water.height * 0.6, 0, water.height * 0.4);
    water.body.allowGravity = false;
    water.body.immovable = true;

    return water;
}

function createDisc(game) {
    const disc = game.add.sprite(32, game.world.height * 0.7, 'disc');
    const anim = disc.animations.add('rotate');
    anim.play(40, true);

    disc.anchor.setTo(0.5, 0.5);

    game.physics.arcade.enable([disc]);
    disc.body.bounce.y = 0.95;
    disc.body.collideWorldBounds = true;

    return disc;
}

function createSky(game, height) {
    const sky = game.add.tileSprite(0, height - 172, game.world.width, 88, 'sky');
    sky.scale.setTo(2, 2);
    game.stage.backgroundColor = '#0098f8';
    return sky;
}

function makeSplash(game, disc) {
    // Ignore any weird/low speed
    if (disc.body.velocity.y > 0) {
        return;
    }

    // Create the splash sprite below the disc
    const splash = game.add.sprite(disc.centerX, disc.bottom, 'splash', 5);
    splash.anchor.setTo(0.5, 1);

    // Adapt animation to disc velocity
    const lastFrame = _.clamp(1, _.floor(-disc.body.velocity.y / 25), 6);
    const frameOrder = _.range(0, lastFrame);
    splash.animations.add('up', frameOrder);
    splash.animations.add('down', _.reverse(frameOrder));

    // Animation up
    splash.animations.play(
        'up',
        20, // framerate
    ).onComplete.add(() => {
        // then down
        splash.animations.play(
            'down',
            20,
            false, // no loop
            true, // kill the sprite on complete
        );
    });
}

function updateCapture({ total, pressed, release }, gap, isPressed) {
    if (gap > 50) {
        return { total, pressed, release: !isPressed };
    } else if (!release) {
        return { total: total + 1, pressed, release: !isPressed };
    }

    return { total: total + 1, pressed: pressed + (isPressed ? 1 : 0), release: true };
}

function resetCapture({ release }) {
    return { total: 1, pressed: 1, release };
}

play.create = function create() {
    this.game.world.setBounds(0, 0, properties.size.x * 30, properties.size.y);
    this.game.stage.backgroundColor = '#eeeeee';
    this.game.physics.startSystem(Phaser.Physics.ARCADE);

    this.water = createWater(this.game, this.game.height);
    this.sky = createSky(this.game, this.water.top);
    this.disc = createDisc(this.game);

    this.disc.body.velocity.x = 250;
    this.disc.body.gravity.y = 200;
    this.disc.body.bounce.y = 1.2; // Higher will be less punitive for the player
    this.disc.falling = true;

    this.game.camera.follow(this.disc, Phaser.Camera.FOLLOW_LOCKON, 0.1, 0.1);

    this.capture = resetCapture({ release: true });
};

play.update = function update() {
    const { disc, water, game } = this;
    game.physics.arcade.collide(disc, water, () => {
        makeSplash(game, disc);
    });

    const gap = Math.max(0, water.body.position.y - disc.body.position.y);
    const isPressed = game.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR);
    this.capture = updateCapture(this.capture, gap, isPressed);

    if (this.disc.falling && disc.body.velocity.y < 0) { // That's the bounce
        const ratioPressure = this.capture.pressed / this.capture.total;
        this.disc.falling = false;
        this.capture = resetCapture(this.capture);
        this.disc.body.velocity.y *= ratioPressure;
    } else if (!this.disc.falling && disc.body.velocity.y >= 0) {
        this.disc.falling = true;
    }
};

play.render = function render() {
    // const { disc, water, game } = this;
    // game.debug.body(disc);
    // game.debug.bodyInfo(disc, 32, 72);
    // game.debug.body(water);
};

module.exports = play;
<commit_msg>change bouncing strategy: distance to optimal bounce<commit_after>const _ = require('lodash');
const properties = require('../properties');

const play = {};

function createWater(game, height) {
    const water = game.add.tileSprite(0, height - 128, game.world.width, 128, 'water');

    game.physics.arcade.enable([water]);
    // With the current texture (view angle),
    // the point of contact is somewhere in the middle
    water.body.setSize(water.width, water.height * 0.6, 0, water.height * 0.4);
    water.body.allowGravity = false;
    water.body.immovable = true;

    return water;
}

function createDisc(game) {
    const disc = game.add.sprite(32, game.world.height * 0.5, 'disc');
    const anim = disc.animations.add('rotate');
    anim.play(40, true);

    disc.anchor.setTo(0.5, 0.5);

    game.physics.arcade.enable([disc]);
    disc.body.collideWorldBounds = true;
    disc.body.velocity.x = 250;
    disc.body.gravity.y = 200;
    disc.body.bounce.y = 1; // Higher will be less punitive for the player
    disc.body.maxVelocity.y = 250;

    return disc;
}

function createSky(game, height) {
    const sky = game.add.tileSprite(0, height - 172, game.world.width, 88, 'sky');
    sky.scale.setTo(2, 2);
    game.stage.backgroundColor = '#0098f8';
    return sky;
}

function makeSplash(game, disc) {
    // Ignore any weird/low speed
    if (disc.body.velocity.y > 0) {
        return;
    }

    // Create the splash sprite below the disc
    const splash = game.add.sprite(disc.centerX, disc.bottom, 'splash', 5);
    splash.anchor.setTo(0.5, 1);

    // Adapt animation to disc velocity
    const lastFrame = _.clamp(1, _.floor(-disc.body.velocity.y / 25), 6);
    const frameOrder = _.range(0, lastFrame);
    splash.animations.add('up', frameOrder);
    splash.animations.add('down', _.reverse(frameOrder));

    // Animation up
    splash.animations.play(
        'up',
        20, // framerate
    ).onComplete.add(() => {
        // then down
        splash.animations.play(
            'down',
            20,
            false, // no loop
            true, // kill the sprite on complete
        );
    });
}

function Bouncer(opts = { actor: null, lower: 2, upper: 10 }) {
    Object.assign(this, opts);
    this.frame = 0;
    this.pressedAt = 0;
}

Bouncer.prototype = {
    bounce() {
        const distance = this.frame - this.pressedAt;

        if (distance > this.upper) {
            this.actor.kill();
        } else if (distance < this.lower) {
            this.actor.body.velocity.y *= Math.sqrt(1 + (this.lower / this.upper));
        } else {
            this.actor.body.velocity.y *= distance / this.upper;
        }
    },

    update(trigger) {
        this.frame += 1;
        this.pressedAt = trigger ? this.frame : this.pressedAt;
    },
};

play.create = function create() {
    this.game.world.setBounds(0, 0, properties.size.x * 30, properties.size.y);
    this.game.stage.backgroundColor = '#eeeeee';
    this.game.physics.startSystem(Phaser.Physics.ARCADE);

    this.water = createWater(this.game, this.game.height);
    this.sky = createSky(this.game, this.water.top);
    this.disc = createDisc(this.game);

    this.game.camera.follow(this.disc, Phaser.Camera.FOLLOW_LOCKON, 0.1, 0.1);

    this.bouncer = new Bouncer({
        actor: this.disc,
        lower: 2,
        upper: 10,
    });
};

play.update = function update() {
    const { disc, water, game } = this;

    this.bouncer.update(game.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR));

    game.physics.arcade.collide(disc, water, () => {
        makeSplash(game, disc);
        this.bouncer.bounce();
    });

    if (!disc.alive) {
        setTimeout(() => {
            this.game.state.clearCurrentState();
            this.game.state.restart();
        }, 750);
    }
};

play.render = function render() {
    // Empty
};

module.exports = play;
<|endoftext|>


<commit_before>/* to define width of scrollbar */
;(function ($, _, undefined)
{
	'use strict';

	_.globalScrollbar = {
		prop: 'margin',
		side: 'right',
		selector: '.js-scrollbar-offset',

		_hidden: false,
		_width: 0,
		_prevOverflow: '',
		_prevMargin: '',

		_num: 0,

		_first: true,

		hide: function (_callback)
		{
			if (this._first)
			{
				this.prop = this.prop ? (this.prop + '-') : '';
				this._first = false;
			}

			this._num++;

			if (this._hidden)
			{
				return false;
			}

			this._hidden = true;

			var nHTML = document.documentElement,
				iWidth = Math.round(window.innerWidth - nHTML.clientWidth),
				sProp = this.prop,
				sSide = this.side;

			this._width = iWidth;
			this._prevOverflow = nHTML.style.overflow;
			this._prevMargin = nHTML.style[sProp + sSide];

			nHTML.style.overflow = 'hidden';
			nHTML.style[sProp + sSide] = iWidth + 'px';

			var anNeedOffset = document.querySelectorAll(this.selector),
				nElem, sDataProp,
				i, L;

			for (i = 0, L = anNeedOffset.length; i < L; i++)
			{
				nElem = anNeedOffset[i];
				sDataProp = nElem.getAttribute('data-offset-prop');

				if (sDataProp === 'true')
				{
					nElem.style[sSide] = iWidth + 'px';
				}
				else if (sDataProp)
				{
					nElem.style[sDataProp + '-' + sSide] = iWidth + 'px';
				}
				else
				{
					nElem.style[sProp + sSide] = iWidth + 'px';
				}
			}

			if (_callback)
			{
				_callback(iWidth, sSide, sProp);
			}
		},
		restore: function (_callback, _bRightNow)
		{
			this._num--;

			if (!this._hidden || (this._num > 0 && !_bRightNow))
			{
				return false;
			}

			this._hidden = false;

			var sProp = this.prop,
				sSide = this.side,
				nHTML = document.documentElement;

			nHTML.style.overflow = this._prevOverflow;
			nHTML.style[sProp + sSide] = this._prevMargin;

			this._prevOverflow = '';
			this._prevMargin = '';

			var anNeedOffset = document.querySelectorAll(this.selector),
				nElem, sDataProp,
				i, L;

			for (i = 0, L = anNeedOffset.length; i < L; i++)
			{
				nElem = anNeedOffset[i];
				sDataProp = nElem.getAttribute('data-offset-prop');

				if (sDataProp === 'true')
				{
					nElem.style[sSide] = '';
				}
				else if (sDataProp)
				{
					nElem.style[sDataProp + '-' + sSide] = '';
				}
				else
				{
					nElem.style[sProp + sSide] = '';
				}
			}

			if (_callback)
			{
				_callback(this._width, sSide, sProp);
			}
		}
	};
})(window.jQuery, window._);
<commit_msg>add method to get width of scrollbar<commit_after>/* to define width of scrollbar */
;(function ($, _, undefined)
{
	'use strict';

	_.globalScrollbar = {
		prop: 'margin',
		side: 'right',
		selector: '.js-scrollbar-offset',

		_hidden: false,
		_width: 0,
		_prevOverflow: '',
		_prevMargin: '',

		_num: 0,

		_first: true,

		hide: function (_callback)
		{
			if (this._first)
			{
				this.prop = this.prop ? (this.prop + '-') : '';
				this._first = false;
			}

			this._num++;

			if (this._hidden)
			{
				return false;
			}

			this._hidden = true;

			var nHTML = document.documentElement,
				iWidth = this.width(),
				sProp = this.prop,
				sSide = this.side;

			this._width = iWidth;
			this._prevOverflow = nHTML.style.overflow;
			this._prevMargin = nHTML.style[sProp + sSide];

			nHTML.style.overflow = 'hidden';
			nHTML.style[sProp + sSide] = iWidth + 'px';

			var anNeedOffset = document.querySelectorAll(this.selector),
				nElem, sDataProp,
				i, L;

			for (i = 0, L = anNeedOffset.length; i < L; i++)
			{
				nElem = anNeedOffset[i];
				sDataProp = nElem.getAttribute('data-offset-prop');

				if (sDataProp === 'true')
				{
					nElem.style[sSide] = iWidth + 'px';
				}
				else if (sDataProp)
				{
					nElem.style[sDataProp + '-' + sSide] = iWidth + 'px';
				}
				else
				{
					nElem.style[sProp + sSide] = iWidth + 'px';
				}
			}

			if (_callback)
			{
				_callback(iWidth, sSide, sProp);
			}
		},
		restore: function (_callback, _bRightNow)
		{
			this._num--;

			if (!this._hidden || (this._num > 0 && !_bRightNow))
			{
				return false;
			}

			this._hidden = false;

			var sProp = this.prop,
				sSide = this.side,
				nHTML = document.documentElement;

			nHTML.style.overflow = this._prevOverflow;
			nHTML.style[sProp + sSide] = this._prevMargin;

			this._prevOverflow = '';
			this._prevMargin = '';

			var anNeedOffset = document.querySelectorAll(this.selector),
				nElem, sDataProp,
				i, L;

			for (i = 0, L = anNeedOffset.length; i < L; i++)
			{
				nElem = anNeedOffset[i];
				sDataProp = nElem.getAttribute('data-offset-prop');

				if (sDataProp === 'true')
				{
					nElem.style[sSide] = '';
				}
				else if (sDataProp)
				{
					nElem.style[sDataProp + '-' + sSide] = '';
				}
				else
				{
					nElem.style[sProp + sSide] = '';
				}
			}

			if (_callback)
			{
				_callback(this._width, sSide, sProp);
			}
		},
		width: function ()
		{
			return Math.round(window.innerWidth - document.documentElement.clientWidth);
		}
	};
})(window.jQuery, window._);
<|endoftext|>


<commit_before>
import * as pg from '../../lib/pg';
import { TABLES, TYPES } from '../../lib/schema';

const MAX_LIMIT = 1024;

const operators = {
	gt: '>',
	lt: '<',
	in: 'IN',
	neq: '<>',
	gte: '>=',
	lte: '<=',
	cts: '@>',
	ctd: '<@',
	mts: '@@'
};

function getPropOp(prop) {
	const i = prop.lastIndexOf('_');

	if (i > 0) {
		return [ prop.substr(i + 1, prop.length), prop.substr(0, i) ];
	} else {
		return '';
	}
}

function fromPart (slice) {
	const fields = [], joins = [];

	fields.push('row_to_json("' + TABLES[TYPES[slice.type]] + '".*)::jsonb as "' + slice.type + '"');
	joins.push('"' + TABLES[TYPES[slice.type]] + '"');

	if (slice.join) {
		for (const type in slice.join) {
			joins.push(
				'LEFT OUTER JOIN "' + TABLES[TYPES[type]] + '" ON "' +
				TABLES[TYPES[type]] + '"."' + slice.join[type] + '" = "' +
				TABLES[TYPES[slice.type]] + '"."id"'
			);
			fields.push('row_to_json("' + TABLES[TYPES[type]] + '".*)::jsonb as "' + type + '"');
		}
	}

	if (slice.link) {
		for (const type in slice.link) {
			joins.push(
				'LEFT OUTER JOIN "' + TABLES[TYPES[type]] + '" ON "' +
				TABLES[TYPES[slice.type]] + '"."' + slice.link[type] + '" = "' +
				TABLES[TYPES[type]] + '"."id"'
			);

			fields.push('row_to_json("' + TABLES[TYPES[type]] + '".*)::jsonb as "' + type + '"');
		}
	}

	return pg.cat([ 'SELECT ', pg.cat(fields, ','), 'FROM', pg.cat(joins, ' ') ], ' ');
}

function wherePart (f) {
	const sql = [];
	let filter = f;

	for (const prop in filter) {
		const [ op, name ] = getPropOp(prop);

		if (Number.POSITIVE_INFINITY === filter[prop] || Number.NEGATIVE_INFINITY === filter[prop]) {
			continue;
		}
		switch (op) {
		case 'gt':
		case 'lt':
		case 'neq':
		case 'gte':
		case 'lte':
		case 'in':
		case 'cts':
		case 'ctd':
		case 'mts':
			sql.push(`"${name.toLowerCase()}" ${operators[op]} &{${prop}}`);
			break;
		default:
			sql.push(`"${prop.toLowerCase()}" = &{${prop}}`);
		}
	}

	if (sql.length) {
		filter = Object.create(filter);
		filter.$ = 'WHERE ' + sql.join(' AND ');
		return filter;
	} else {
		return '';
	}
}

function orderPart(order, limit) {
	if (limit < 0) {
		return `ORDER BY "${order.toLowerCase()}" DESC LIMIT ${-limit}`;
	} else {
		return `ORDER BY "${order.toLowerCase()}" ASC LIMIT ${limit}`;
	}
}

function simpleQuery(slice, limit) {
	return pg.cat([
		fromPart(slice),
		wherePart(slice.filter),
		orderPart(slice.order, limit)
	], ' ');
}

function boundQuery (slice, start, end) {
	slice.filter[slice.order + '_gte'] = start;
	slice.filter[slice.order + '_lte'] = end;
	const query = simpleQuery(slice, MAX_LIMIT);

	delete slice.filter[slice.order + '_gte'];
	delete slice.filter[slice.order + '_lte'];

	return query;
}

function beforeQuery (slice, start, before, exclude) {
	slice.filter[slice.order + (exclude ? '_lt' : '_lte')] = start;
	const query = simpleQuery(slice, Math.max(-MAX_LIMIT, -before));

	delete slice.filter[slice.order + (exclude ? '_lt' : '_lte')];

	return pg.cat([
		'SELECT * FROM (',
		query,
		{
			$: `) r ORDER BY ${slice.type.toLowerCase()}->&{order} ASC`,
			order: slice.order.toLowerCase()
		}

	], ' ');
}

function afterQuery (slice, start, after, exclude) {
	if (!slice.filter) slice.filter = {};
	slice.filter[slice.order + (exclude ? '_gt' : '_gte')] = start;
	const query = simpleQuery(slice, Math.min(MAX_LIMIT, after));

	delete slice.filter[slice.order + (exclude ? '_gt' : '_gte')];

	return query;
}

export default function (slice, range) {
	let query;

	if (slice.order) {
		if (range.length === 2) {
			query = boundQuery(slice, range[0], range[1]);
		} else {
			if (range[1] > 0 && range[2] > 0) {
				query = pg.cat([
					'(',
					beforeQuery(slice, range[0], range[1], true),
					')',
					'UNION ALL',
					'(',
					afterQuery(slice, range[0], range[2]),
					')'
				], ' ');
			} else if (range[1] > 0) {
				query = beforeQuery(slice, range[0], range[1]);
			} else if (range[2] > 0) {
				query = afterQuery(slice, range[0], range[2]);
			}
		}
	} else {
		query = simpleQuery(slice, MAX_LIMIT);
	}

	return query;
}
<commit_msg>Fix mts filters<commit_after>
import * as pg from '../../lib/pg';
import { TABLES, TYPES } from '../../lib/schema';

const MAX_LIMIT = 1024;

const operators = {
	gt: '>',
	lt: '<',
	in: 'IN',
	neq: '<>',
	gte: '>=',
	lte: '<=',
	cts: '@>',
	ctd: '<@',
	mts: 'like'
};

function getPropOp(prop) {
	const i = prop.lastIndexOf('_');

	if (i > 0) {
		return [ prop.substr(i + 1, prop.length), prop.substr(0, i) ];
	} else {
		return '';
	}
}

function fromPart (slice) {
	const fields = [], joins = [];

	fields.push('row_to_json("' + TABLES[TYPES[slice.type]] + '".*)::jsonb as "' + slice.type + '"');
	joins.push('"' + TABLES[TYPES[slice.type]] + '"');

	if (slice.join) {
		for (const type in slice.join) {
			joins.push(
				'LEFT OUTER JOIN "' + TABLES[TYPES[type]] + '" ON "' +
				TABLES[TYPES[type]] + '"."' + slice.join[type] + '" = "' +
				TABLES[TYPES[slice.type]] + '"."id"'
			);
			fields.push('row_to_json("' + TABLES[TYPES[type]] + '".*)::jsonb as "' + type + '"');
		}
	}

	if (slice.link) {
		for (const type in slice.link) {
			joins.push(
				'LEFT OUTER JOIN "' + TABLES[TYPES[type]] + '" ON "' +
				TABLES[TYPES[slice.type]] + '"."' + slice.link[type] + '" = "' +
				TABLES[TYPES[type]] + '"."id"'
			);

			fields.push('row_to_json("' + TABLES[TYPES[type]] + '".*)::jsonb as "' + type + '"');
		}
	}

	return pg.cat([ 'SELECT ', pg.cat(fields, ','), 'FROM', pg.cat(joins, ' ') ], ' ');
}

function wherePart (f) {
	const sql = [];
	let filter = f;

	for (const prop in filter) {
		const [ op, name ] = getPropOp(prop);

		if (Number.POSITIVE_INFINITY === filter[prop] || Number.NEGATIVE_INFINITY === filter[prop]) {
			continue;
		}
		switch (op) {
		case 'gt':
		case 'lt':
		case 'neq':
		case 'gte':
		case 'lte':
		case 'in':
		case 'cts':
		case 'ctd':
		case 'mts':
			sql.push(`"${name.toLowerCase()}" ${operators[op]} &{${prop}}`);
			break;
		default:
			sql.push(`"${prop.toLowerCase()}" = &{${prop}}`);
		}
	}

	if (sql.length) {
		filter = Object.create(filter);
		filter.$ = 'WHERE ' + sql.join(' AND ');
		return filter;
	} else {
		return '';
	}
}

function orderPart(order, limit) {
	if (limit < 0) {
		return `ORDER BY "${order.toLowerCase()}" DESC LIMIT ${-limit}`;
	} else {
		return `ORDER BY "${order.toLowerCase()}" ASC LIMIT ${limit}`;
	}
}

function simpleQuery(slice, limit) {
	return pg.cat([
		fromPart(slice),
		wherePart(slice.filter),
		orderPart(slice.order, limit)
	], ' ');
}

function boundQuery (slice, start, end) {
	slice.filter[slice.order + '_gte'] = start;
	slice.filter[slice.order + '_lte'] = end;
	const query = simpleQuery(slice, MAX_LIMIT);

	delete slice.filter[slice.order + '_gte'];
	delete slice.filter[slice.order + '_lte'];

	return query;
}

function beforeQuery (slice, start, before, exclude) {
	slice.filter[slice.order + (exclude ? '_lt' : '_lte')] = start;
	const query = simpleQuery(slice, Math.max(-MAX_LIMIT, -before));

	delete slice.filter[slice.order + (exclude ? '_lt' : '_lte')];

	return pg.cat([
		'SELECT * FROM (',
		query,
		{
			$: `) r ORDER BY ${slice.type.toLowerCase()}->&{order} ASC`,
			order: slice.order.toLowerCase()
		}

	], ' ');
}

function afterQuery (slice, start, after, exclude) {
	if (!slice.filter) slice.filter = {};
	slice.filter[slice.order + (exclude ? '_gt' : '_gte')] = start;
	const query = simpleQuery(slice, Math.min(MAX_LIMIT, after));

	delete slice.filter[slice.order + (exclude ? '_gt' : '_gte')];

	return query;
}

export default function (slice, range) {
	let query;

	if (slice.order) {
		if (range.length === 2) {
			query = boundQuery(slice, range[0], range[1]);
		} else {
			if (range[1] > 0 && range[2] > 0) {
				query = pg.cat([
					'(',
					beforeQuery(slice, range[0], range[1], true),
					')',
					'UNION ALL',
					'(',
					afterQuery(slice, range[0], range[2]),
					')'
				], ' ');
			} else if (range[1] > 0) {
				query = beforeQuery(slice, range[0], range[1]);
			} else if (range[2] > 0) {
				query = afterQuery(slice, range[0], range[2]);
			}
		}
	} else {
		query = simpleQuery(slice, MAX_LIMIT);
	}

	return query;
}
<|endoftext|>


<commit_before>const TriviaCore = require('./TriviaCore')
const Promise = require('promise')
const request = require('request')
const assign = require('object-assign')

function normalizeAnswer(a) {
  return a.toLowerCase().replace(/\s+/g, ' ')
}

export default class Trivia extends TriviaCore {

  constructor(sekshi, options) {
    super(sekshi, options)

    this.author = 'WE ♥ KPOP'
    this.version = '0.0.0'
    this.description = ''

    this.permissions = {
      trivia: sekshi.USERROLE.MANAGER,
      trivquit: sekshi.USERROLE.MANAGER,
      trivpoints: sekshi.USERROLE.BOUNCER
    }
  }

  init() {
    super.init()
  }
  destroy() {
    super.destroy()
  }

  defaultOptions() {
    return assign(super.defaultOptions(), {
      points: 3,
      interval: 5
    })
  }

  nextQuestion() {
    const target = this.options.points
    const interval = this.options.interval
    super.nextQuestion().then(({ winner, question }) => {
      if (winner) {
        this.addPoints(winner)
        if (this.getPoints(winner) >= target) {
          this.sekshi.sendChat(`[Trivia] @${winner.username} answered correctly and reached ${target} points!` +
                               ` Congratulations! :D`)
          this.sekshi.moveDJ(winner.id, 1)
          this.stopTrivia()
        }
        else {
          this.sekshi.sendChat(`[Trivia] @${winner.username} answered correctly! Next question in ${interval} seconds!`)
          setTimeout(() => this.nextQuestion(), interval * 1000)
        }
      }
      else {
        this.sekshi.sendChat(`[Trivia] Nobody answered correctly! ` +
                             `The right answer was "${question.answers[0]}". Next question in ${interval} seconds!`)
        setTimeout(() => this.nextQuestion(), interval * 1000)
      }
    }).catch(e => {
      this.sekshi.sendChat(`[Trivia] Something went wrong! Stopping trivia before I explode... :boom:`)
      this.stopTrivia()
      console.error('trivia', e)
    })
  }

  // Chat Commands
  trivia(user, test = false) {
    this.sekshi.sendChat(`[Trivia] @everyone ${user.username} started Trivia! ` +
                         `First to ${this.options.points} points gets waitlist spot #2 :)`)
    this.startTrivia().then(() => {
      this.sekshi.sendChat(`Loaded ${this.questions.length} questions. First question in 5 seconds!`)
      setTimeout(() => this.nextQuestion(), 5 * 1000)
    })
  }

  trivquit(user) {
    if (this.isRunning()) {
      this.stopTrivia()
      if (user) {
        this.sekshi.sendChat(`[Trivia] ${user.username} stopped Trivia.`)
      }
    }
  }

  trivpoints(user) {
    if (this.isRunning()) {
      let points = Object.keys(this._points).map(uid => {
        return { uid: uid, points: this._points[uid] }
      })
      points.sort((a, b) => a.points > b.points ? -1 : a.points < b.points ? 1 : 0)

      this.sekshi.sendChat('Current top points: ' + points.slice(0, 5).map((x, i) => {
        const u = this.sekshi.getUserByID(x.uid)
        return `#${i + 1} ${u.username} (${x.points})`
      }).join(', '))
    }
  }

}
<commit_msg>trivia: ping djs instead of everyone<commit_after>const TriviaCore = require('./TriviaCore')
const Promise = require('promise')
const request = require('request')
const assign = require('object-assign')

function normalizeAnswer(a) {
  return a.toLowerCase().replace(/\s+/g, ' ')
}

export default class Trivia extends TriviaCore {

  constructor(sekshi, options) {
    super(sekshi, options)

    this.author = 'WE ♥ KPOP'
    this.version = '0.0.0'
    this.description = ''

    this.permissions = {
      trivia: sekshi.USERROLE.MANAGER,
      trivquit: sekshi.USERROLE.MANAGER,
      trivpoints: sekshi.USERROLE.BOUNCER
    }
  }

  init() {
    super.init()
  }
  destroy() {
    super.destroy()
  }

  defaultOptions() {
    return assign(super.defaultOptions(), {
      points: 3,
      interval: 5
    })
  }

  nextQuestion() {
    const target = this.options.points
    const interval = this.options.interval
    super.nextQuestion().then(({ winner, question }) => {
      if (winner) {
        this.addPoints(winner)
        if (this.getPoints(winner) >= target) {
          this.sekshi.sendChat(`[Trivia] @${winner.username} answered correctly and reached ${target} points!` +
                               ` Congratulations! :D`)
          this.sekshi.moveDJ(winner.id, 1)
          this.stopTrivia()
        }
        else {
          this.sekshi.sendChat(`[Trivia] @${winner.username} answered correctly! Next question in ${interval} seconds!`)
          setTimeout(() => this.nextQuestion(), interval * 1000)
        }
      }
      else {
        this.sekshi.sendChat(`[Trivia] Nobody answered correctly! ` +
                             `The right answer was "${question.answers[0]}". Next question in ${interval} seconds!`)
        setTimeout(() => this.nextQuestion(), interval * 1000)
      }
    }).catch(e => {
      this.sekshi.sendChat(`[Trivia] Something went wrong! Stopping trivia before I explode... :boom:`)
      this.stopTrivia()
      console.error('trivia', e)
    })
  }

  // Chat Commands
  trivia(user, test = false) {
    this.sekshi.sendChat(`[Trivia] @djs ${user.username} started Trivia! ` +
                         `First to ${this.options.points} points gets waitlist spot #2 :)`)
    this.startTrivia().then(() => {
      this.sekshi.sendChat(`Loaded ${this.questions.length} questions. First question in 5 seconds!`)
      setTimeout(() => this.nextQuestion(), 5 * 1000)
    })
  }

  trivquit(user) {
    if (this.isRunning()) {
      this.stopTrivia()
      if (user) {
        this.sekshi.sendChat(`[Trivia] ${user.username} stopped Trivia.`)
      }
    }
  }

  trivpoints(user) {
    if (this.isRunning()) {
      let points = Object.keys(this._points).map(uid => {
        return { uid: uid, points: this._points[uid] }
      })
      points.sort((a, b) => a.points > b.points ? -1 : a.points < b.points ? 1 : 0)

      this.sekshi.sendChat('Current top points: ' + points.slice(0, 5).map((x, i) => {
        const u = this.sekshi.getUserByID(x.uid)
        return `#${i + 1} ${u.username} (${x.points})`
      }).join(', '))
    }
  }

}
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { EditorState, convertToRaw, ContentState, convertFromHTML } from 'draft-js'
import { Editor } from 'react-draft-wysiwyg'
import draftToHtml from 'draftjs-to-html'

import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'

class WYSIWYG extends Component {
  static propTypes = {
    value: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired,
  }

  constructor(props) {
    super(props)

    this.state = {
      editorState: this.convertHtmlToEditorState(this.props.value),
    }

    this.onEditorStateChange = this.onEditorStateChange.bind(this)
  }

  convertHtmlToEditorState() {
    const { value } = this.props
    const blocksFromHTML = convertFromHTML(value)
    const state = ContentState.createFromBlockArray(
      blocksFromHTML.contentBlocks,
      blocksFromHTML.entityMap
    )

    return EditorState.createWithContent(state)
  }

  onEditorStateChange(editorState) {
    this.setState({
      editorState,
    })

    this.props.onChange(draftToHtml(convertToRaw(editorState.getCurrentContent())))
  }

  render() {
    const { editorState } = this.state
    return (

        <Editor
          editorState={editorState}
          wrapperClassName="demo-wrapper"
          editorClassName="form-control"
          onEditorStateChange={this.onEditorStateChange}
        />

    )
  }
}

export default WYSIWYG
<commit_msg>fix #9 new article page is not working due to null state<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { EditorState, convertToRaw, ContentState, convertFromHTML } from 'draft-js'
import { Editor } from 'react-draft-wysiwyg'
import draftToHtml from 'draftjs-to-html'

import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'

class WYSIWYG extends Component {
  static propTypes = {
    value: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired,
  }

  constructor(props) {
    super(props)

    this.state = {
      editorState: this.convertHtmlToEditorState(this.props.value),
    }

    this.onEditorStateChange = this.onEditorStateChange.bind(this)
  }

  convertHtmlToEditorState(value) {
    if (value){
      const blocksFromHTML = convertFromHTML(value)
      const state = ContentState.createFromBlockArray(
        blocksFromHTML.contentBlocks,
        blocksFromHTML.entityMap
      )

      return EditorState.createWithContent(state)
    }
  }

  onEditorStateChange(editorState) {
    this.setState({
      editorState,
    })

    this.props.onChange(draftToHtml(convertToRaw(editorState.getCurrentContent())))
  }

  render() {
    const { editorState } = this.state
    return (

        <Editor
          editorState={editorState}
          wrapperClassName="demo-wrapper"
          editorClassName="form-control"
          onEditorStateChange={this.onEditorStateChange}
        />

    )
  }
}

export default WYSIWYG
<|endoftext|>