<commit_before>/* global config, global, path, module, project, require */

// -------------------------------------
	// Load Dependencies
// -------------------------------------

var LessCssClean = require('less-plugin-clean-css');

global.fs = require('fs');
global.browserSync = require('browser-sync');
global.Wee = require('../script/wee').Wee;
global.path = require('path');
global.jshint = require('jshint').JSHINT;
global.JSCS = require('jscs');

Wee.fn.extend({
	// Build root or relative path
	buildPath: function(path, file) {
		return file.substring(0, 2) == './' ?
			file :
			global.path.join(path, file);
	},
	// Append minified extension
	getMinifiedExtension: function(dest, src, ext) {
		var dir = src.substring(0, src.lastIndexOf('/')),
			filename = src.substring(src.lastIndexOf('/'), src.length);
		filename = filename.substring(0, filename.lastIndexOf('.'));

		return dest + '/' + dir + filename + ext;
	},
	validate: function(config, grunt, filepath) {
		var ext = path.extname(filepath);

		if (filepath.indexOf('temp') == -1 &&
			filepath.indexOf('/vendor') == -1) {
			if (ext == '.js') {
				var js = grunt.file.read(filepath),
					errors = [],
					total = 0;

				if (project.script.validate.jshint) {
					// JSHint
					var jshintConfig = grunt.file.readJSON(
						project.script.validate.jshint === true ?
							config.paths.assets + '/wee/script/.jshintrc' :
							project.script.validate.jshint
					);

					if (! jshint(js, jshintConfig)) {
						var out = jshint.data();
						errors = out.errors,
						total = errors.length;

						grunt.log.header('Script validation errors found');

						grunt.log.error('JSHint error' +
							((total > 1) ? 's' : '') + ' in ' + filepath + '.');

						errors.forEach(function(message) {
							Wee.logError(grunt, message.line  + ':' + message.character, message.reason, message.evidence);
						});

						grunt.log.writeln();
						grunt.log.writeln();

						this.notify({
							title: 'JSHint Validation Error',
							message: 'Check console for error details'
						}, 'error');
					}
				}

				if (project.script.validate.jscs) {
					// JSCS
					var jscsConfig = grunt.file.readJSON(
							project.script.validate.jscs === true ?
							config.paths.assets + '/wee/script/.jscs.json' :
							project.script.validate.jscs
						),
						checker = new JSCS();

					checker.registerDefaultRules();
					checker.configure(jscsConfig);

					errors = checker.checkString(js);

					var errorList = errors.getErrorList();
					total = errorList.length;

					if (total > 0) {
						grunt.log.error('JSCS error' +
							((total > 1) ? 's' : '') + ' in ' + filepath + '.');

						errorList.forEach(function(message) {
							Wee.logError(grunt, message.line  + ':' + message.column, message.rule, message.message);
						});

						this.notify({
							title: 'JSCS Validation Error',
							message: 'Check console for error details'
						}, 'error');
					}
				}
			}
		}
	},
	logError: function(grunt, pos, msg, details) {
		grunt.log.writeln('['.cyan + pos + '] '.cyan + msg + ' ' + (details || '').magenta);
	},
	notify: function(data, type) {
		var notifier = require('node-notifier'),
			iconPath = config.paths.assets + '/wee/build/img/';
		type = type || 'notice';

		if (type == 'error') {
			console.error(data.message);
			data.icon = iconPath + 'error.png';
		} else {
			console.log(data.message);
			data.icon = iconPath + 'notice.png';
		}

		notifier.notify(data);
	}
});

// -------------------------------------
	// Configure Grunt
// -------------------------------------

module.exports = function(grunt) {
	global.config = {};
	global.server = {};
	global.moduleResponsive = [];
	global.legacy = {};
	global.reloadPaths = [];
	global.legacyBuild = [];
	global.legacyConvert = [];
	global.version = '2.2.2';

	grunt.initConfig({
		less: {
			options: {
				modifyVars: '<%= config.style.vars %>',
				strictMath: true,
				paths: [
					'<%= config.paths.root %>'
				],
				plugins: [
					new LessCssClean()
				]
			},
			core: {
				files: [
					{
						dest: '<%= config.paths.temp %>/wee.css',
						src: '<%= config.paths.temp %>/wee.less'
					}
				]
			},
			lib: {
				files: [{
					expand: true,
					cwd: '<%= config.paths.css %>/lib',
					dest: '<%= config.paths.css %>/lib',
					src: [
						'**/*.{css,less}',
						'!**/*.min.css'
					],
					rename: function(dest, src) {
						return Wee.getMinifiedExtension(dest, src, '.min.css');
					}
				}]
			}
		},
		uglify: {
			core: {
				files: [{
					dest: '<%= config.paths.js %>/script.min.js',
					src: '<%= config.script.files %>'
				}]
			},
			lib: {
				files: [{
					expand: true,
					cwd: '<%= config.paths.js %>/lib',
					dest: '<%= config.paths.js %>/lib',
					src: [
						'**/*.js',
						'!**/*.min.js'
					],
					rename: function(dest, src) {
						return Wee.getMinifiedExtension(dest, src, '.min.js');
					}
				}]
			}
		},
		concat: {
			style: {
				dest: '<%= config.paths.css %>/style.min.css',
				src: '<%= config.style.concat %>'
			}
		},
		imagemin: {
			options: {
				plugins: [
					{
						removeViewBox: false
					},
					{
						convertPathData: false
					}
				],
				svgoPlugins: [
					{
						removeViewBox: false
					}
				]
			},
			core: {
				files: [
					{
						expand: true,
						cwd: '<%= config.paths.assets %>',
						dest: '<%= config.paths.assets %>',
						src: [
							'**/*.{gif,jpg,png,svg}'
						]
					}
				]
			}
		},
		watch: {
			options: {
				spawn: false
			},
			images: {
				files: [
					'<%= config.paths.assets %>/**/*.{gif,jpg,png,svg}'
				],
				tasks: [
					'newer:imagemin',
					'notify:images'
				]
			},
			scriptCore: {
				files: '<%= config.script.files %>',
				tasks: [
					'uglify:core',
					'notify:script'
				]
			},
			scriptLib: {
				files: [
					'<%= config.paths.js %>/lib/**/*.js',
					'!<%= config.paths.js %>/lib/**/*.min.js'
				],
				tasks: [
					'uglify:lib',
					'notify:script'
				]
			},
			styleCore: {
				files: [
					'<%= config.paths.assets %>/wee/style/**/*.less',
					'<%= config.paths.css %>/custom/**/*.less',
					'!<%= config.paths.temp %>wee.legacy.less'
				],
				tasks: [
					'less:core',
					'concat:style'
				]
			},
			styleLib: {
				files: [
					'<%= config.paths.css %>/lib/**/*.{css,less}',
					'!<%= config.paths.css %>/lib/**/*.min.css'
				],
				tasks: [
					'less:lib',
					'notify:style'
				]
			},
			styleBuild: {
				files: [
					'<%= config.paths.css %>/build/**/*.{css,less}'
				],
				tasks: [
					'buildStyle',
					'notify:style'
				],
				options: {
					event: [
						'added',
						'deleted'
					]
				}
			},
			styleBuildUpdate: {
				files: [
					'<%= config.paths.css %>/build/**/*.{css,less}'
				],
				tasks: [
					'less:core',
					'concat:style',
					'notify:style'
				],
				options: {
					event: [
						'changed'
					]
				}
			},
			styleConcat: {
				files: [
					'<%= config.paths.temp %>/**/*.css'
				],
				tasks: [
					'concat:style',
					'notify:style'
				]
			},
			project: {
				files: [
					'<%= config.path %>',
					'<%= config.paths.modules %>/*/module.json'
				],
				tasks: [
					'default',
					'notify:project'
				]
			}
		}
	});

	// Watch for changes to validate
	if (project.script.validate.watch) {
		grunt.event.on('watch', function(action, file) {
			if (action !== 'deleted') {
				Wee.validate(config, grunt, file);
			}
		});
	}

	// -------------------------------------
		// Load Plugins
	// -------------------------------------

	grunt.loadNpmTasks('grunt-contrib-concat');
	grunt.loadNpmTasks('grunt-contrib-imagemin');
	grunt.loadNpmTasks('grunt-contrib-less');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-watch');
	grunt.loadNpmTasks('grunt-newer');

	// -----------------------------------
		// Grunt Tasks
	// -----------------------------------

	grunt.registerTask('default', [
		'init',
		'cleanup',
		'configStyle',
		'configScript',
		'configGenerator',
		'buildStyle',
		'configModules',
		'buildLegacy',
		'uglify:core',
		'uglify:lib',
		'imagemin'
	]);

	// Build + Watch
	grunt.registerTask('local', [
		'default',
		'proxy',
		'checkUpdates',
		'sync',
		'watch'
	]);

	// Build + Server + Open + Watch
	grunt.registerTask('static', [
		'default',
		'server',
		'checkUpdates',
		'sync',
		'watch'
	]);

	// Validate
	grunt.registerTask('validate', [
		'init',
		'runValidation'
	]);

	// Generate Site
	grunt.registerTask('generate', [
		'init',
		'configGenerator'
	]);

	// Update
	grunt.registerTask('update', [
		'checkUpdates'
	]);
};<commit_msg>Fix syntax in Grunt to ensure SVG viewboxes aren't removed<commit_after>/* global config, global, path, module, project, require */

// -------------------------------------
	// Load Dependencies
// -------------------------------------

var LessCssClean = require('less-plugin-clean-css');

global.fs = require('fs');
global.browserSync = require('browser-sync');
global.Wee = require('../script/wee').Wee;
global.path = require('path');
global.jshint = require('jshint').JSHINT;
global.JSCS = require('jscs');

Wee.fn.extend({
	// Build root or relative path
	buildPath: function(path, file) {
		return file.substring(0, 2) == './' ?
			file :
			global.path.join(path, file);
	},
	// Append minified extension
	getMinifiedExtension: function(dest, src, ext) {
		var dir = src.substring(0, src.lastIndexOf('/')),
			filename = src.substring(src.lastIndexOf('/'), src.length);
		filename = filename.substring(0, filename.lastIndexOf('.'));

		return dest + '/' + dir + filename + ext;
	},
	validate: function(config, grunt, filepath) {
		var ext = path.extname(filepath);

		if (filepath.indexOf('temp') == -1 &&
			filepath.indexOf('/vendor') == -1) {
			if (ext == '.js') {
				var js = grunt.file.read(filepath),
					errors = [],
					total = 0;

				if (project.script.validate.jshint) {
					// JSHint
					var jshintConfig = grunt.file.readJSON(
						project.script.validate.jshint === true ?
							config.paths.assets + '/wee/script/.jshintrc' :
							project.script.validate.jshint
					);

					if (! jshint(js, jshintConfig)) {
						var out = jshint.data();
						errors = out.errors,
						total = errors.length;

						grunt.log.header('Script validation errors found');

						grunt.log.error('JSHint error' +
							((total > 1) ? 's' : '') + ' in ' + filepath + '.');

						errors.forEach(function(message) {
							Wee.logError(grunt, message.line  + ':' + message.character, message.reason, message.evidence);
						});

						grunt.log.writeln();
						grunt.log.writeln();

						this.notify({
							title: 'JSHint Validation Error',
							message: 'Check console for error details'
						}, 'error');
					}
				}

				if (project.script.validate.jscs) {
					// JSCS
					var jscsConfig = grunt.file.readJSON(
							project.script.validate.jscs === true ?
							config.paths.assets + '/wee/script/.jscs.json' :
							project.script.validate.jscs
						),
						checker = new JSCS();

					checker.registerDefaultRules();
					checker.configure(jscsConfig);

					errors = checker.checkString(js);

					var errorList = errors.getErrorList();
					total = errorList.length;

					if (total > 0) {
						grunt.log.error('JSCS error' +
							((total > 1) ? 's' : '') + ' in ' + filepath + '.');

						errorList.forEach(function(message) {
							Wee.logError(grunt, message.line  + ':' + message.column, message.rule, message.message);
						});

						this.notify({
							title: 'JSCS Validation Error',
							message: 'Check console for error details'
						}, 'error');
					}
				}
			}
		}
	},
	logError: function(grunt, pos, msg, details) {
		grunt.log.writeln('['.cyan + pos + '] '.cyan + msg + ' ' + (details || '').magenta);
	},
	notify: function(data, type) {
		var notifier = require('node-notifier'),
			iconPath = config.paths.assets + '/wee/build/img/';
		type = type || 'notice';

		if (type == 'error') {
			console.error(data.message);
			data.icon = iconPath + 'error.png';
		} else {
			console.log(data.message);
			data.icon = iconPath + 'notice.png';
		}

		notifier.notify(data);
	}
});

// -------------------------------------
	// Configure Grunt
// -------------------------------------

module.exports = function(grunt) {
	global.config = {};
	global.server = {};
	global.moduleResponsive = [];
	global.legacy = {};
	global.reloadPaths = [];
	global.legacyBuild = [];
	global.legacyConvert = [];
	global.version = '2.3.0';

	grunt.initConfig({
		less: {
			options: {
				modifyVars: '<%= config.style.vars %>',
				strictMath: true,
				paths: [
					'<%= config.paths.root %>'
				],
				plugins: [
					new LessCssClean()
				]
			},
			core: {
				files: [
					{
						dest: '<%= config.paths.temp %>/wee.css',
						src: '<%= config.paths.temp %>/wee.less'
					}
				]
			},
			lib: {
				files: [{
					expand: true,
					cwd: '<%= config.paths.css %>/lib',
					dest: '<%= config.paths.css %>/lib',
					src: [
						'**/*.{css,less}',
						'!**/*.min.css'
					],
					rename: function(dest, src) {
						return Wee.getMinifiedExtension(dest, src, '.min.css');
					}
				}]
			}
		},
		uglify: {
			core: {
				files: [{
					dest: '<%= config.paths.js %>/script.min.js',
					src: '<%= config.script.files %>'
				}]
			},
			lib: {
				files: [{
					expand: true,
					cwd: '<%= config.paths.js %>/lib',
					dest: '<%= config.paths.js %>/lib',
					src: [
						'**/*.js',
						'!**/*.min.js'
					],
					rename: function(dest, src) {
						return Wee.getMinifiedExtension(dest, src, '.min.js');
					}
				}]
			}
		},
		concat: {
			style: {
				dest: '<%= config.paths.css %>/style.min.css',
				src: '<%= config.style.concat %>'
			}
		},
		imagemin: {
			options: {
				svgoPlugins: [
					{
						removeViewBox: false
					},
					{
						convertPathData: false
					}
				]
			},
			core: {
				files: [
					{
						expand: true,
						cwd: '<%= config.paths.assets %>',
						dest: '<%= config.paths.assets %>',
						src: [
							'**/*.{gif,jpg,png,svg}'
						]
					}
				]
			}
		},
		watch: {
			options: {
				spawn: false
			},
			images: {
				files: [
					'<%= config.paths.assets %>/**/*.{gif,jpg,png,svg}'
				],
				tasks: [
					'newer:imagemin',
					'notify:images'
				]
			},
			scriptCore: {
				files: '<%= config.script.files %>',
				tasks: [
					'uglify:core',
					'notify:script'
				]
			},
			scriptLib: {
				files: [
					'<%= config.paths.js %>/lib/**/*.js',
					'!<%= config.paths.js %>/lib/**/*.min.js'
				],
				tasks: [
					'uglify:lib',
					'notify:script'
				]
			},
			styleCore: {
				files: [
					'<%= config.paths.assets %>/wee/style/**/*.less',
					'<%= config.paths.css %>/custom/**/*.less',
					'!<%= config.paths.temp %>wee.legacy.less'
				],
				tasks: [
					'less:core',
					'concat:style'
				]
			},
			styleLib: {
				files: [
					'<%= config.paths.css %>/lib/**/*.{css,less}',
					'!<%= config.paths.css %>/lib/**/*.min.css'
				],
				tasks: [
					'less:lib',
					'notify:style'
				]
			},
			styleBuild: {
				files: [
					'<%= config.paths.css %>/build/**/*.{css,less}'
				],
				tasks: [
					'buildStyle',
					'notify:style'
				],
				options: {
					event: [
						'added',
						'deleted'
					]
				}
			},
			styleBuildUpdate: {
				files: [
					'<%= config.paths.css %>/build/**/*.{css,less}'
				],
				tasks: [
					'less:core',
					'concat:style',
					'notify:style'
				],
				options: {
					event: [
						'changed'
					]
				}
			},
			styleConcat: {
				files: [
					'<%= config.paths.temp %>/**/*.css'
				],
				tasks: [
					'concat:style',
					'notify:style'
				]
			},
			project: {
				files: [
					'<%= config.path %>',
					'<%= config.paths.modules %>/*/module.json'
				],
				tasks: [
					'default',
					'notify:project'
				]
			}
		}
	});

	// Watch for changes to validate
	if (project.script.validate.watch) {
		grunt.event.on('watch', function(action, file) {
			if (action !== 'deleted') {
				Wee.validate(config, grunt, file);
			}
		});
	}

	// -------------------------------------
		// Load Plugins
	// -------------------------------------

	grunt.loadNpmTasks('grunt-contrib-concat');
	grunt.loadNpmTasks('grunt-contrib-imagemin');
	grunt.loadNpmTasks('grunt-contrib-less');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-watch');
	grunt.loadNpmTasks('grunt-newer');

	// -----------------------------------
		// Grunt Tasks
	// -----------------------------------

	grunt.registerTask('default', [
		'init',
		'cleanup',
		'configStyle',
		'configScript',
		'configGenerator',
		'buildStyle',
		'configModules',
		'buildLegacy',
		'uglify:core',
		'uglify:lib',
		'imagemin'
	]);

	// Build + Watch
	grunt.registerTask('local', [
		'default',
		'proxy',
		'checkUpdates',
		'sync',
		'watch'
	]);

	// Build + Server + Open + Watch
	grunt.registerTask('static', [
		'default',
		'server',
		'checkUpdates',
		'sync',
		'watch'
	]);

	// Validate
	grunt.registerTask('validate', [
		'init',
		'runValidation'
	]);

	// Generate Site
	grunt.registerTask('generate', [
		'init',
		'configGenerator'
	]);

	// Update
	grunt.registerTask('update', [
		'checkUpdates'
	]);
};<|endoftext|>


<commit_before>import React from 'react';
import ReactDom from 'react-dom';
import actions from '../actions/ipsumActions.js';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import request from '../util/restHelpers';
import _ from 'underscore';
import { Panel, Grid, Row, Col, Clearfix, PageHeader, ListGroup, ListGroupItem } from 'react-bootstrap';
import barGraph from './AllAppsBarGraph';
import Select from 'react-select';
import style from '../styles/SelectStyle.css';

class MyAppHistory extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      filterMode: null, // 'total', 'route', or 'server'
      filterOptions: null, // an array
      days: null,
      graphData: null,
      selectedFilters: null
    };
  }

  graphIt(){
    if (this.state.days) {
      if (this.filterMode === 'total' || this.state.filterOptions && this.state.selectedFilters)
        this.formatGraphData(() => {
          if (this.state.graphData) {
            barGraph('historyBargraph', this.state.graphData)
          }
        });
    }
  }

  getData() {
    request.post('/getStats/myAppSummary', {
      userId: 1,
      appId: 1,
      days: 7
    }, (err, resp) => {
      console.log(err, resp)
    // parse the data object for route and server totals
      var data = JSON.parse(resp.text);
      var parsedData = this.parseHistoryResponse(data);
      // var parsedData = this.parseHistoryResponse(sampledata);
      // save parsed and formatted data to store
      this.props.dispatch(actions.ADD_MYAPP_HISTORY(parsedData));
      this.graphIt();
    });
  }

  parseHistoryResponse(response) {
    // make a copy
    var parsed = Object.assign({}, response);
    // collect server names for <Select>
    parsed.serverNames = parsed.Servers.map( Server => {
      return {
        value: Server.server,
        label: _.findWhere(this.props.state.servers, {id: Number(Server.server)} ).hostname
      };
    });
    // collect routenames for <Select>
    parsed.routeNames = parsed.Routes.map( Route => {
      return {
        value: Route.route,
        label: Route.route
      };
    });
    // collect dates for Graph
    parsed.dates = parsed.Total.map( Total => Total.date);
    // sort the totals by date
    parsed.Total.sort( (day1, day2) => (day1.date) - (day2.date));
    parsed.Routes.forEach( Route => {
      Route.data.sort( (day1, day2) => (day1.date) - (day2.date));
    });
    parsed['Servers'].forEach( Server => {
      Server.data.sort( (day1, day2) => (day1.date) - (day2.date));
    });
    return parsed;
  }

  formatGraphData(cb) {
    var mode = this.state.filterMode;

    if (mode === 'total') {

      this.setState({graphData: this.props.state.myAppHistory.Total});
      return;

    } else  {

      if (mode === 'server') {
        var identifier = 'server';
        var raw = this.props.state.myAppHistory.Servers;
      } else {
        var identifier = 'route';
        var raw = this.props.state.myAppHistory.Routes;
      }

      var results = {};

      raw.forEach( item => {
        if (_.findWhere(this.state.selectedFilters, {value: item[identifier]})) {
          var days = item.data;
          days.forEach( day => {
            // add the label to the results if not present
            if (!results.hasOwnProperty(day.date)) {
              results[day.date] = {date: day.date, value: 0}
            }
            results[day.date].value += day.value;
          })
        }
      })

      var arr = Object.keys(results).map( key => results[key]);
      arr.sort( (a, b) => {
        return a.date - b.date;
      });

      this.setState({graphData: arr}, () => cb() );
    }
  }

  selectDays(value) {
    this.setState({days: value.value, selectedFilters: null, filterOptions: null}, () => this.getData());
  }

  selectFilterMode(value) {
    // clear the old graph away
    document.getElementById('historyBargraph').innerHTML = '';

    if (value.value === 'server') { this.setState({filterMode: 'server'})}
    else if (value.value === 'route') { this.setState({filterMode: 'route'})}
    else { this.setState({filterMode: 'total', filterOptions: null, selectedFilters: null})}

    // clear the old filter options and load the new ones
    this.setState({selectedFilters: null}, () => {
      // load the new filter options
      var mode = {
        'route': this.props.state.myAppHistory.routeNames,
        'server': this.props.state.myAppHistory.serverNames
      };

      var options = mode[value.value];
      this.setState({filterOptions: options}, () => this.graphIt() );
    });
  }

  toggleFilterOption(value) {
    this.setState({selectedFilters: value}, () => {this.graphIt();});
  }

  render() {

    var dayOptions = [
      {'value': 1, 'label': '1 day'},
      {'value': 3, 'label': '3 days'},
      {'value': 7, 'label': '1 week'},
      {'value': 14, 'label': '2 weeks'},
      {'value': 28, 'label': '4 weeks'},
    ];

    var filterModes = [
      {'value': 'total', 'label': 'Total (no filter)'},
      {'value': 'route', 'label': 'By selected route(s)'},
      {'value': 'server', 'label': 'By selected server(s)'}
    ];

    return (
      <Row>
      <Col xs={12} md={12}>

Look back

      <Panel header={
Selected History
}>
        <Grid fluid>
        <Row>
          <Col xs={12} md={4}>

How far back?

            <Select value={this.state.days} ref='daysSelect' autofocus options={dayOptions} clearable={false} name='daysSelect' onChange={this.selectDays.bind(this)} />

Filter results by:

            <Select disabled={this.state.days ? false : true} value={this.state.filterMode} ref='filterModeSelect' autofocus options={filterModes} clearable={false} name='filterModeSelect' onChange={this.selectFilterMode.bind(this)} />

Options

            <Select disabled={this.state.filterOptions && this.state.filterMode !== 'total' ? false : true} multi value={this.state.selectedFilters} ref='filterSelect' autofocus options={this.state.filterOptions} clearable={false} name='filterSelect' onChange={this.toggleFilterOption.bind(this)} />

          </Col>
          <Col xs={12} md={8}>

Selected Data

          </Col>
        </Row>
        </Grid>
      </Panel>
      </Col>
      </Row>
    )
  }
}

MyAppHistory = connect(state => ({ state: state }))(MyAppHistory);
export default MyAppHistory<commit_msg>fix Total mode of history graph<commit_after>import React from 'react';
import ReactDom from 'react-dom';
import actions from '../actions/ipsumActions.js';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import request from '../util/restHelpers';
import _ from 'underscore';
import { Panel, Grid, Row, Col, Clearfix, PageHeader, ListGroup, ListGroupItem } from 'react-bootstrap';
import barGraph from './AllAppsBarGraph';
import Select from 'react-select';
import style from '../styles/SelectStyle.css';

class MyAppHistory extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      filterMode: null, // 'total', 'route', or 'server'
      filterOptions: null, // an array
      days: null,
      graphData: null,
      selectedFilters: null
    };
  }

  graphIt(){
    if (this.state.days) {
      if (this.state.filterMode === 'total' || this.state.filterOptions && this.state.selectedFilters)
        this.formatGraphData(() => {
          if (this.state.graphData) {
            barGraph('historyBargraph', this.state.graphData)
          }
        });
    }
  }

  getData() {
    request.post('/getStats/myAppSummary', {
      userId: 1,
      appId: 1,
      days: 7
    }, (err, resp) => {
      console.log(err, resp)
    // parse the data object for route and server totals
      var data = JSON.parse(resp.text);
      var parsedData = this.parseHistoryResponse(data);
      // var parsedData = this.parseHistoryResponse(sampledata);
      // save parsed and formatted data to store
      this.props.dispatch(actions.ADD_MYAPP_HISTORY(parsedData));
      this.graphIt();
    });
  }

  parseHistoryResponse(response) {
    // make a copy
    var parsed = Object.assign({}, response);
    // collect server names for <Select>
    parsed.serverNames = parsed.Servers.map( Server => {
      return {
        value: Server.server,
        label: _.findWhere(this.props.state.servers, {id: Number(Server.server)} ).hostname
      };
    });
    // collect routenames for <Select>
    parsed.routeNames = parsed.Routes.map( Route => {
      return {
        value: Route.route,
        label: Route.route
      };
    });
    // collect dates for Graph
    parsed.dates = parsed.Total.map( Total => Total.date);
    // sort the totals by date
    parsed.Total.sort( (day1, day2) => (day1.date) - (day2.date));
    parsed.Routes.forEach( Route => {
      Route.data.sort( (day1, day2) => (day1.date) - (day2.date));
    });
    parsed['Servers'].forEach( Server => {
      Server.data.sort( (day1, day2) => (day1.date) - (day2.date));
    });
    return parsed;
  }

  formatGraphData(cb) {
    var mode = this.state.filterMode;

    if (mode === 'total') {

      this.setState({graphData: this.props.state.myAppHistory.Total}, cb.bind(this));
      return;

    } else  {

      if (mode === 'server') {
        var identifier = 'server';
        var raw = this.props.state.myAppHistory.Servers;
      } else {
        var identifier = 'route';
        var raw = this.props.state.myAppHistory.Routes;
      }

      var results = {};

      raw.forEach( item => {
        if (_.findWhere(this.state.selectedFilters, {value: item[identifier]})) {
          var days = item.data;
          days.forEach( day => {
            // add the label to the results if not present
            if (!results.hasOwnProperty(day.date)) {
              results[day.date] = {date: day.date, value: 0}
            }
            results[day.date].value += day.value;
          })
        }
      })

      var arr = Object.keys(results).map( key => results[key]);
      arr.sort( (a, b) => {
        return a.date - b.date;
      });

      this.setState({graphData: arr}, () => cb() );
    }
  }

  selectDays(value) {
    this.setState({days: value.value, selectedFilters: null, filterOptions: null}, () => this.getData());
  }

  selectFilterMode(value) {
    // clear the old graph away
    document.getElementById('historyBargraph').innerHTML = '';

    var next = function() {
      // clear the old filter options and load the new ones
      this.setState({selectedFilters: null}, () => {
        // load the new filter options
        var mode = {
          'route': this.props.state.myAppHistory.routeNames,
          'server': this.props.state.myAppHistory.serverNames
        };

        var options = mode[value.value];
        this.setState({filterOptions: options}, () => this.graphIt() );
      });
    }

    if (value.value === 'server') { this.setState({filterMode: 'server'}, next.bind(this))}
    else if (value.value === 'route') { this.setState({filterMode: 'route'}, next.bind(this))}
    else { this.setState({filterMode: 'total', filterOptions: null, selectedFilters: null}, this.graphIt.bind(this))};

  }

  toggleFilterOption(value) {
    this.setState({selectedFilters: value}, () => {this.graphIt();});
  }

  render() {

    var dayOptions = [
      {'value': 1, 'label': '1 day'},
      {'value': 3, 'label': '3 days'},
      {'value': 7, 'label': '1 week'},
      {'value': 14, 'label': '2 weeks'},
      {'value': 28, 'label': '4 weeks'},
    ];

    var filterModes = [
      {'value': 'total', 'label': 'Total (no filter)'},
      {'value': 'route', 'label': 'By selected route(s)'},
      {'value': 'server', 'label': 'By selected server(s)'}
    ];

    return (
      <Row>
      <Col xs={12} md={12}>

Look back

      <Panel header={
Selected History
}>
        <Grid fluid>
        <Row>
          <Col xs={12} md={4}>

How far back?

            <Select value={this.state.days} ref='daysSelect' autofocus options={dayOptions} clearable={false} name='daysSelect' onChange={this.selectDays.bind(this)} />

Filter results by:

            <Select disabled={this.state.days ? false : true} value={this.state.filterMode} ref='filterModeSelect' autofocus options={filterModes} clearable={false} name='filterModeSelect' onChange={this.selectFilterMode.bind(this)} />

Options

            <Select disabled={this.state.filterOptions && this.state.filterMode !== 'total' ? false : true} multi value={this.state.selectedFilters} ref='filterSelect' autofocus options={this.state.filterOptions} clearable={false} name='filterSelect' onChange={this.toggleFilterOption.bind(this)} />

          </Col>
          <Col xs={12} md={8}>

Selected Data

          </Col>
        </Row>
        </Grid>
      </Panel>
      </Col>
      </Row>
    )
  }
}

MyAppHistory = connect(state => ({ state: state }))(MyAppHistory);
export default MyAppHistory<|endoftext|>


<commit_before>/* eslint-disable max-lines */
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer';
import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js';
import { timeOut } from '@polymer/polymer/lib/utils/async';
import { enqueueDebouncer } from '@polymer/polymer/lib/utils/flush';
import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class.js';
import { IronResizableBehavior } from '@polymer/iron-resizable-behavior';

import template from './cosmoz-bottom-bar.html.js';

const
	BOTTOM_BAR_TOOLBAR_SLOT = 'bottom-bar-toolbar',
	BOTTOM_BAR_MENU_SLOT = 'bottom-bar-menu';

/**
 * `<cosmoz-bottom-bar>` is a horizontal responsive bottom toolbar containing items that
 * can be used for actions.
 *
 * The items placed inside the `cosmoz-bottom-bar` are distributed into the toolbar in a horizontal container.
 * If the items do not fit the available width, those that do not fit are placed in a dropdown
 * menu triggered by a button in the toolbar.
 * The class specified by the property `toolbarClass` (default `cosmoz-bottom-bar-toolbar`)
 * is applied to items distributed to the toolbar.
 * The class specified in the property `menuClass` (default `cosmoz-bottom-bar-menu`)
 * is applied to items distributed to the menu.
 *
 * ### Usage
 *
 * See demo for example usage
 *
 * @element cosmoz-bottom-bar
 * @demo demo/bottom-bar.html Basic Demo
 * @demo demo/bottom-bar-match-parent.html match parent Demo
	*/
class CosmozBottomBar extends mixinBehaviors([IronResizableBehavior], PolymerElement) {
	static get template() { // eslint-disable-line max-lines-per-function
		return template;
	}

	// eslint-disable-next-line max-lines-per-function
	static get properties() {
		return {
			/**
			 * Whether the bar is active (shown)
			 */
			active: {
				type: Boolean,
				value: false,
				notify: true,
				reflectToAttribute: true
			},

			/**
			 * Bar height (not used when `matchParent` or `matchElementHeight` is set)
			 */
			barHeight: {
				type: Number,
				value: 64
			},

			/**
			 * Whether to match the height of parent
			 */
			matchParent: {
				type: Boolean,
				value: false
			},

			/**
			 * Whether this bottom bar has items distributed to the menu
			 */
			hasMenuItems: {
				type: Boolean,
				value: false,
				readOnly: true,
				notify: true
			},

			hasExtraItems: {
				type: Boolean,
				value: false
			},

			/**
			 * Class applied the the selected item
			 */
			selectedClass: {
				type: String,
				value: 'cosmoz-bottom-bar-selected-item'
			},

			/**
			 * Class applied to items distributed to the toolbar
			 */
			toolbarClass: {
				type: String,
				value: 'cosmoz-bottom-bar-toolbar'
			},

			/**
			 * Class applied to items distributed to the menu
			 */
			menuClass: {
				type: String,
				value: 'cosmoz-bottom-bar-menu'
			},

			/**
			 * Maximum number of items in toolbar, regardless of space
			 */
			maxToolbarItems: {
				type: Number,
				value: 1
			},

			/**
			 * The actual bar height, depending on if we `matchParent` or set `barHeight`
			 */
			computedBarHeight: {
				type: Number,
				computed: '_computeComputedBarHeight(_matchHeightElement, barHeight, _computedBarHeightKicker)',
				notify: true
			},

			/**
			 * Kicker to make `computedBarHeight` recalculate
			 */
			_computedBarHeightKicker: {
				type: Number
			},

			/**
			 * Whether the bar is visible (has actions and is `active`)
			 */
			visible: {
				type: Boolean,
				notify: true,
				readOnly: true,
				computed: '_computeVisible(hasActions, active, hasExtraItems)'
			},

			/**
			 * Whether we have any visible actions
			 */
			hasActions: {
				type: Boolean,
				value: false,
				readOnly: true,
				notify: true
			},

			/**
			 * Reference element from which to inherit height
			 */
			_matchHeightElement: {
				type: Object,
				computed: '_getHeightMatchingElement(matchParent)'
			}
		};
	}

	static get observers() {
		return [
			'_showHideBottomBar(visible, computedBarHeight)'
		];
	}

	constructor() {
		super();
		this._boundOnResize = this._onResize.bind(this);
		this._boundDropdownClosed = this._dropdownClosed.bind(this);
		this._boundChildrenUpdated = this._childrenUpdated.bind(this);
		this._boundLayoutActions = this._layoutActions.bind(this);
	}

	connectedCallback() {
		super.connectedCallback();

		this.addEventListener('iron-resize', this._boundOnResize);
		this.addEventListener('iron-closed-overlay', this._boundDropdownClosed);

		this._hiddenMutationObserver = new MutationObserver(() => {
			this._overflowWidth = undefined;
			this._debounceLayoutActions();
		});
		this._nodeObserver = new FlattenedNodesObserver(this.$.content, this._boundChildrenUpdated);
		this._nodeObserverExtra = new FlattenedNodesObserver(this.$.extraSlot, info =>
			this.set('hasExtraItems', info.addedNodes.length > 0)
		);
		this._computedBarHeightKicker = 0;
	}

	disconnectedCallback() {
		super.disconnectedCallback();

		this.removeEventListener('iron-resize', this._boundOnResize);
		this.removeEventListener('iron-closed-overlay', this._boundDropdownClosed);

		this._nodeObserver.disconnect();
		this._nodeObserverExtra.disconnect();
		this._hiddenMutationObserver.disconnect();
		this._layoutDebouncer.cancel();
	}

	_canAddMoreButtonToBar(width, bottomBarElements, menuElements) {
		const hasSpace = width > this._overflowWidth || this._overflowWidth === undefined,
			hasPlace = bottomBarElements.length < this.maxToolbarItems,
			hasCandidates = menuElements.length > 0;

		return hasSpace && hasPlace && hasCandidates;
	}

	_childrenUpdated(info) {
		const addedNodes = info.addedNodes.filter(this._isActionNode),
			removedNodes = info.removedNodes.filter(this._isActionNode);

		if (addedNodes.length === 0 && removedNodes.length === 0) {
			return;
		}

		addedNodes
		// ignore nodes that are moved between slots
			.filter(node => removedNodes.indexOf(node) === -1)
			.forEach(function (node) {
				this._hiddenMutationObserver.observe(node, {
					attributes: true,
					attributeFilter: [
						'hidden'
					]
				});
				if (node.parentNode !== this) {
					this.appendChild(node);
				}
				this._moveElement(node, true);
			//this._toolbarMoveToStart(node);
			}, this);

		this._debounceLayoutActions();
	}

	// eslint-disable-next-line no-unused-vars
	_computeComputedBarHeight(matchElementHeight, barHeight, kicker) {
		if (matchElementHeight) {
			return matchElementHeight.offsetHeight;
		}
		return barHeight;
	}

	_computeVisible(hasActions, active, hasExtraItems) {
		return (hasActions || hasExtraItems) && active;
	}

	_debounceLayoutActions() {
		this._layoutDebouncer = Debouncer.debounce(
			this._layoutDebouncer,
			timeOut.after(30),
			this._boundLayoutActions
		);
		enqueueDebouncer(this._layoutDebouncer);
	}

	_dropdownClosed() {
		this.$.dropdownButton.active = false;
	}

	_fireAction(item) {

		if (!item || !item.dispatchEvent) {
			return;
		}

		item.dispatchEvent(new window.CustomEvent('action', {
			bubbles: true,
			cancelable: true,
			detail: {
				item
			}
		}));
	}

	_getHeightMatchingElement(matchParent) {
		if (matchParent) {
			return this.parentElement;
		}

		return null;
	}

	_getHeightStyle(height) {
		return 'height: ' + height + 'px;';
	}

	_isActionNode(node) {
		return node.nodeType === Node.ELEMENT_NODE &&
			node.getAttribute('slot') !== 'info' &&
			node.tagName !== 'TEMPLATE' &&
			node.tagName !== 'DOM-REPEAT' &&
			node.tagName !== 'DOM-IF' &&
			node.getAttribute('slot') !== 'extra';
	}

	/**
	 * Layout the actions available as buttons or menu items
	 *
	 * If the window is resizing down, just make sure that all buttons fits, and if not,
	 * move one to menu and call itself async (to allow re-rendering) and see if we fit.
	 * Repeat until the button fits or no buttons are left.
	 *
	 * If the window is sizing up, try to place a menu item out as a button, call itself
	 * async (to allow re-rendering) and see if we fit - if we don't, remove the button again.
	 *
	 * We also need to keep track of `_scalingUp` between calls since the resize might fire
	 * a lot of events, and we don't want to be starting multiple "calculation processes"
	 * since this will result in an infinite loop.
	 *
	 * The actual layouting of actions will be performed by adding or removing the 'button'
	 * attribute from the action, which will cause it to match different content insertion
	 * points.
	 *
	 * @returns {void}
	 */
	_layoutActions() { // eslint-disable-line max-statements
		const elements = FlattenedNodesObserver.getFlattenedNodes(this).filter(n => n.nodeType === Node.ELEMENT_NODE)
				.filter(this._isActionNode)
				.filter(element => !element.hidden),
			toolbar = this.$.toolbar;

		let fits,
			newToolbarElement;

		this._setHasActions(elements.length > 0 || this.hasExtraItems);
		if (!this.hasActions) {
			// No need to render if we don't have any actions
			return;
		}

		const currentWidth = toolbar.clientWidth;
		fits = toolbar.scrollWidth <= currentWidth + 1;

		const toolbarElements = elements.filter(element => {
				if (element.getAttribute('slot') === BOTTOM_BAR_TOOLBAR_SLOT) {
					// make sure we only read scrollWidth and clientWidth until
					// know that we don't fit
					fits = fits && element.scrollWidth <= element.clientWidth;
					return true;
				}
				return false;
			}),

			menuElements = elements.filter(element => element.getAttribute('slot') === BOTTOM_BAR_MENU_SLOT);

		this._setHasMenuItems(menuElements.length > 0);

		fits = fits && toolbarElements.length <= this.maxToolbarItems;

		if (fits) {
			if (this._canAddMoreButtonToBar(currentWidth, toolbarElements, menuElements)) {
				newToolbarElement = menuElements[0];
				this._moveElement(newToolbarElement, true);
				// (pasleq) If we are moving the focused element from the menu to the toolbar
				// while the toolbar is open, this will cause an error in iron-control-state
				// that tries to handle lost of focus on an element that has been removed.
				if (toolbarElements.length > 0) {
					toolbarElements[0].focus();
				} else {
					newToolbarElement.focus();
				}
				this.$.menu.close();
				this._debounceLayoutActions();
			}
			return;
		}

		this._overflowWidth = currentWidth;

		if (toolbarElements.length < 1) {
			return;
		}

		const newMenuElement = toolbarElements[toolbarElements.length - 1];
		this._moveElement(newMenuElement, false);
		this._debounceLayoutActions();
	}

	_moveElement(element, toToolbar) {
		const slot = toToolbar ? BOTTOM_BAR_TOOLBAR_SLOT : BOTTOM_BAR_MENU_SLOT,
			tabindex = toToolbar ? '0' : '-1';
		element.setAttribute('slot', slot);
		element.setAttribute('tabindex', tabindex);
		this.toggleClass(this.menuClass, !toToolbar, element);
		this.toggleClass(this.toolbarClass, toToolbar, element);
		this.updateStyles();
	}

	_onActionSelected(event, detail) {
		this._fireAction(detail.item);
		event.currentTarget.selected = undefined;
	}

	_onResize() {
		this._computedBarHeightKicker += 1;
		this._debounceLayoutActions();
	}

	_showHideBottomBar(visible, barHeight) {
		this.style.display = '';
		const translateY = visible ? 0 : barHeight,
			onEnd = () => {
				clearTimeout(this._hideTimeout);
				this._hideTimeout = null;
				this.style.display = this.visible ? '' : 'none';
			};
		clearTimeout(this._hideTimeout);
		requestAnimationFrame(() => {
			this.translate3d('0px', translateY + 'px', '0px');
			this._hideTimeout = setTimeout(onEnd, 510);
		});
	}

	_toolbarMoveToStart(node) {
		const toolbar = this.$.toolbar;
		if (toolbar.children.length === 0) {
			toolbar.appendChild(node);
			return;
		}
		toolbar.insertBefore(node, toolbar.children[0]);
	}
}

customElements.define('cosmoz-bottom-bar', CosmozBottomBar);
<commit_msg>fix: improve initial distribution of toolbar elements<commit_after>/* eslint-disable max-lines */
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer';
import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js';
import { timeOut } from '@polymer/polymer/lib/utils/async';
import { enqueueDebouncer } from '@polymer/polymer/lib/utils/flush';
import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class.js';
import { IronResizableBehavior } from '@polymer/iron-resizable-behavior';

import template from './cosmoz-bottom-bar.html.js';

const
	BOTTOM_BAR_TOOLBAR_SLOT = 'bottom-bar-toolbar',
	BOTTOM_BAR_MENU_SLOT = 'bottom-bar-menu',

	slotEq = slot => element => element.getAttribute('slot') === slot;

/**
 * `<cosmoz-bottom-bar>` is a horizontal responsive bottom toolbar containing items that
 * can be used for actions.
 *
 * The items placed inside the `cosmoz-bottom-bar` are distributed into the toolbar in a horizontal container.
 * If the items do not fit the available width, those that do not fit are placed in a dropdown
 * menu triggered by a button in the toolbar.
 * The class specified by the property `toolbarClass` (default `cosmoz-bottom-bar-toolbar`)
 * is applied to items distributed to the toolbar.
 * The class specified in the property `menuClass` (default `cosmoz-bottom-bar-menu`)
 * is applied to items distributed to the menu.
 *
 * ### Usage
 *
 * See demo for example usage
 *
 * @element cosmoz-bottom-bar
 * @demo demo/bottom-bar.html Basic Demo
 * @demo demo/bottom-bar-match-parent.html match parent Demo
	*/
class CosmozBottomBar extends mixinBehaviors([IronResizableBehavior], PolymerElement) {
	static get template() { // eslint-disable-line max-lines-per-function
		return template;
	}

	// eslint-disable-next-line max-lines-per-function
	static get properties() {
		return {
			/**
			 * Whether the bar is active (shown)
			 */
			active: {
				type: Boolean,
				value: false,
				notify: true,
				reflectToAttribute: true
			},

			/**
			 * Bar height (not used when `matchParent` or `matchElementHeight` is set)
			 */
			barHeight: {
				type: Number,
				value: 64
			},

			/**
			 * Whether to match the height of parent
			 */
			matchParent: {
				type: Boolean,
				value: false
			},

			/**
			 * Whether this bottom bar has items distributed to the menu
			 */
			hasMenuItems: {
				type: Boolean,
				value: false,
				readOnly: true,
				notify: true
			},

			hasExtraItems: {
				type: Boolean,
				value: false
			},

			/**
			 * Class applied the the selected item
			 */
			selectedClass: {
				type: String,
				value: 'cosmoz-bottom-bar-selected-item'
			},

			/**
			 * Class applied to items distributed to the toolbar
			 */
			toolbarClass: {
				type: String,
				value: 'cosmoz-bottom-bar-toolbar'
			},

			/**
			 * Class applied to items distributed to the menu
			 */
			menuClass: {
				type: String,
				value: 'cosmoz-bottom-bar-menu'
			},

			/**
			 * Maximum number of items in toolbar, regardless of space
			 */
			maxToolbarItems: {
				type: Number,
				value: 1
			},

			/**
			 * The actual bar height, depending on if we `matchParent` or set `barHeight`
			 */
			computedBarHeight: {
				type: Number,
				computed: '_computeComputedBarHeight(_matchHeightElement, barHeight, _computedBarHeightKicker)',
				notify: true
			},

			/**
			 * Kicker to make `computedBarHeight` recalculate
			 */
			_computedBarHeightKicker: {
				type: Number
			},

			/**
			 * Whether the bar is visible (has actions and is `active`)
			 */
			visible: {
				type: Boolean,
				notify: true,
				readOnly: true,
				computed: '_computeVisible(hasActions, active, hasExtraItems)'
			},

			/**
			 * Whether we have any visible actions
			 */
			hasActions: {
				type: Boolean,
				value: false,
				readOnly: true,
				notify: true
			},

			/**
			 * Reference element from which to inherit height
			 */
			_matchHeightElement: {
				type: Object,
				computed: '_getHeightMatchingElement(matchParent)'
			}
		};
	}

	static get observers() {
		return [
			'_showHideBottomBar(visible, computedBarHeight)'
		];
	}

	constructor() {
		super();
		this._boundOnResize = this._onResize.bind(this);
		this._boundDropdownClosed = this._dropdownClosed.bind(this);
		this._boundChildrenUpdated = this._childrenUpdated.bind(this);
		this._boundLayoutActions = this._layoutActions.bind(this);
	}

	connectedCallback() {
		super.connectedCallback();

		this.addEventListener('iron-resize', this._boundOnResize);
		this.addEventListener('iron-closed-overlay', this._boundDropdownClosed);

		this._hiddenMutationObserver = new MutationObserver(() => {
			this._overflowWidth = undefined;
			this._debounceLayoutActions();
		});
		this._nodeObserver = new FlattenedNodesObserver(this.$.content, this._boundChildrenUpdated);
		this._nodeObserverExtra = new FlattenedNodesObserver(this.$.extraSlot, info =>
			this.set('hasExtraItems', info.addedNodes.length > 0)
		);
		this._computedBarHeightKicker = 0;
	}

	disconnectedCallback() {
		super.disconnectedCallback();

		this.removeEventListener('iron-resize', this._boundOnResize);
		this.removeEventListener('iron-closed-overlay', this._boundDropdownClosed);

		this._nodeObserver.disconnect();
		this._nodeObserverExtra.disconnect();
		this._hiddenMutationObserver.disconnect();
		this._layoutDebouncer.cancel();
	}

	_canAddMoreButtonToBar(width, bottomBarElements, menuElements) {
		const hasSpace = width > this._overflowWidth || this._overflowWidth === undefined,
			hasPlace = bottomBarElements.length < this.maxToolbarItems,
			hasCandidates = menuElements.length > 0;

		return hasSpace && hasPlace && hasCandidates;
	}

	_childrenUpdated(info) {
		const addedNodes = info.addedNodes.filter(this._isActionNode),
			removedNodes = info.removedNodes.filter(this._isActionNode);

		if (addedNodes.length === 0 && removedNodes.length === 0) {
			return;
		}
		const newNodes = addedNodes.filter(node => removedNodes.indexOf(node) === -1);
		if (newNodes.length === 0) {
			return;
		}

		const toolbarElements = this._getElements().filter(slotEq(BOTTOM_BAR_TOOLBAR_SLOT)),
			allowedToolbarCount = this.maxToolbarItems - toolbarElements.length;

		newNodes.forEach((node, i) => {
			this._hiddenMutationObserver.observe(node, {
				attributes: true,
				attributeFilter: [
					'hidden'
				]
			});
			if (node.parentNode !== this) {
				this.appendChild(node);
			}
			this._moveElement(node, i < allowedToolbarCount);
		});
		this._debounceLayoutActions();
	}

	// eslint-disable-next-line no-unused-vars
	_computeComputedBarHeight(matchElementHeight, barHeight, kicker) {
		if (matchElementHeight) {
			return matchElementHeight.offsetHeight;
		}
		return barHeight;
	}

	_computeVisible(hasActions, active, hasExtraItems) {
		return (hasActions || hasExtraItems) && active;
	}

	_debounceLayoutActions() {
		this._layoutDebouncer = Debouncer.debounce(
			this._layoutDebouncer,
			timeOut.after(30),
			this._boundLayoutActions
		);
		enqueueDebouncer(this._layoutDebouncer);
	}

	_dropdownClosed() {
		this.$.dropdownButton.active = false;
	}

	_fireAction(item) {

		if (!item || !item.dispatchEvent) {
			return;
		}

		item.dispatchEvent(new window.CustomEvent('action', {
			bubbles: true,
			cancelable: true,
			detail: {
				item
			}
		}));
	}

	_getHeightMatchingElement(matchParent) {
		if (matchParent) {
			return this.parentElement;
		}

		return null;
	}

	_getHeightStyle(height) {
		return 'height: ' + height + 'px;';
	}

	_isActionNode(node) {
		return node.nodeType === Node.ELEMENT_NODE &&
			node.getAttribute('slot') !== 'info' &&
			node.tagName !== 'TEMPLATE' &&
			node.tagName !== 'DOM-REPEAT' &&
			node.tagName !== 'DOM-IF' &&
			node.getAttribute('slot') !== 'extra';
	}

	_getElements() {
		return FlattenedNodesObserver.getFlattenedNodes(this).filter(n => n.nodeType === Node.ELEMENT_NODE)
			.filter(this._isActionNode)
			.filter(element => !element.hidden);
	}
	/**
	 * Layout the actions available as buttons or menu items
	 *
	 * If the window is resizing down, just make sure that all buttons fits, and if not,
	 * move one to menu and call itself async (to allow re-rendering) and see if we fit.
	 * Repeat until the button fits or no buttons are left.
	 *
	 * If the window is sizing up, try to place a menu item out as a button, call itself
	 * async (to allow re-rendering) and see if we fit - if we don't, remove the button again.
	 *
	 * We also need to keep track of `_scalingUp` between calls since the resize might fire
	 * a lot of events, and we don't want to be starting multiple "calculation processes"
	 * since this will result in an infinite loop.
	 *
	 * The actual layouting of actions will be performed by adding or removing the 'button'
	 * attribute from the action, which will cause it to match different content insertion
	 * points.
	 *
	 * @returns {void}
	 */
	_layoutActions() { // eslint-disable-line max-statements
		const elements = this._getElements(),
			toolbar = this.$.toolbar;

		let fits,
			newToolbarElement;

		this._setHasActions(elements.length > 0 || this.hasExtraItems);
		if (!this.hasActions) {
			// No need to render if we don't have any actions
			return;
		}

		const currentWidth = toolbar.clientWidth;
		fits = toolbar.scrollWidth <= currentWidth + 1;

		const toolbarElements = elements.filter(slotEq(BOTTOM_BAR_TOOLBAR_SLOT)),
			menuElements = elements.filter(slotEq(BOTTOM_BAR_MENU_SLOT));

		this._setHasMenuItems(menuElements.length > 0);

		fits = fits && toolbarElements.length <= this.maxToolbarItems;
		fits = fits && toolbarElements.every(element => element.scrollWidth <= element.clientWidth);

		if (fits) {
			if (this._canAddMoreButtonToBar(currentWidth, toolbarElements, menuElements)) {
				newToolbarElement = menuElements[0];
				this._moveElement(newToolbarElement, true);
				// (pasleq) If we are moving the focused element from the menu to the toolbar
				// while the toolbar is open, this will cause an error in iron-control-state
				// that tries to handle lost of focus on an element that has been removed.
				if (toolbarElements.length > 0) {
					toolbarElements[0].focus();
				} else {
					newToolbarElement.focus();
				}
				this.$.menu.close();
				this._debounceLayoutActions();
			}
			return;
		}

		this._overflowWidth = currentWidth;

		if (toolbarElements.length < 1) {
			return;
		}

		const newMenuElement = toolbarElements[toolbarElements.length - 1];
		this._moveElement(newMenuElement, false);
		this._debounceLayoutActions();
	}

	_moveElement(element, toToolbar) {
		const slot = toToolbar ? BOTTOM_BAR_TOOLBAR_SLOT : BOTTOM_BAR_MENU_SLOT,
			tabindex = toToolbar ? '0' : '-1';
		element.setAttribute('slot', slot);
		element.setAttribute('tabindex', tabindex);
		this.toggleClass(this.menuClass, !toToolbar, element);
		this.toggleClass(this.toolbarClass, toToolbar, element);
		this.updateStyles();
	}

	_onActionSelected(event, detail) {
		this._fireAction(detail.item);
		event.currentTarget.selected = undefined;
	}

	_onResize() {
		this._computedBarHeightKicker += 1;
		this._debounceLayoutActions();
	}

	_showHideBottomBar(visible, barHeight) {
		this.style.display = '';
		const translateY = visible ? 0 : barHeight,
			onEnd = () => {
				clearTimeout(this._hideTimeout);
				this._hideTimeout = null;
				this.style.display = this.visible ? '' : 'none';
			};
		clearTimeout(this._hideTimeout);
		requestAnimationFrame(() => {
			this.translate3d('0px', translateY + 'px', '0px');
			this._hideTimeout = setTimeout(onEnd, 510);
		});
	}
}

customElements.define('cosmoz-bottom-bar', CosmozBottomBar);
<|endoftext|>


<commit_before>/*global content, jQuery, MM, observable, setTimeout, clearTimeout, window, gapi, btoa, XMLHttpRequest */
MM.GoogleDriveRepository = function (clientId, apiKey, networkTimeoutMillis, contentType) {
	'use strict';
	observable(this);
	var driveLoaded,
		isAuthorised,
		dispatchEvent = this.dispatchEvent,
		saveFile = function (mapInfo, complete, fail) {
			var	boundary = '-------314159265358979323846',
				delimiter = "\r\n--" + boundary + "\r\n",
				close_delim = "\r\n--" + boundary + "--",
				metadata = {
					'title': mapInfo.idea.title + ".mup",
					'mimeType': contentType
				},
				//base64Data = btoa(JSON.stringify(mapInfo.idea)),
				data = JSON.stringify(mapInfo.idea),
				multipartRequestBody =
					delimiter +
					'Content-Type: application/json\r\n\r\n' +
					JSON.stringify(metadata) +
					delimiter +
					'Content-Type: ' + contentType + '\r\n' +
					'\r\n' +
					data +
					close_delim,
				request = gapi.client.request({
					'path': '/upload/drive/v2/files' + (mapInfo.googleId ? "/" + mapInfo.googleId : ""),
					'method': (mapInfo.googleId ? 'PUT' : 'POST'),
					'params': {'uploadType': 'multipart'},
					'headers': {
						'Content-Type': 'multipart/mixed; boundary="' + boundary + '"'
					},
					'body': multipartRequestBody
				});
			request.execute(function (resp) {
				if (resp.error) {
					if (fail) {
						fail(resp.error);
					}
				} else if (complete) {
					if (!mapInfo.googleId) {
						mapInfo.mapId = "g1" + resp.id;
						mapInfo.googleId = resp.id;
					}
					complete(mapInfo);
				}
			});
		},
		downloadFile = function (file, complete, fail) {
			if (file.downloadUrl) {
				var xhr = new XMLHttpRequest();
				xhr.open('GET', file.downloadUrl);
				if (file.title) {
					xhr.setRequestHeader('Authorization', 'Bearer ' + gapi.auth.getToken().access_token);
				}
				xhr.onload = function () {
					if (complete) {
						complete({
							title: file.title || 'unknown',
							body: JSON.parse(xhr.responseText)
						});
					}
				};
				xhr.onerror = function () {
					if (fail) {
						fail();
					}
				};
				xhr.send();
			} else if (fail) {
				fail();
			}
		},
		loadFile = function (fileId, complete, fail) {
			var request = gapi.client.drive.files.get({
				'fileId': fileId
			});
			fail = fail || function () {};
			request.execute(function (resp) {
				if (resp.error) {
					if (resp.error.code === 404) {
						fail('no-access-allowed');
					} else {
						fail(resp.error);
					}
				} else {
					downloadFile(resp, complete, fail);
				}
			});
		},
		checkAuth = function (showDialog, complete, failure) {
			gapi.auth.authorize(
				{
					'client_id': clientId,
					'scope': 'https://www.googleapis.com/auth/drive  https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.install https://www.googleapis.com/auth/userinfo.profile',
					'immediate': !showDialog
				},
				function (authResult) {
					if (authResult) {
						isAuthorised = true;
						if (complete) {
							complete();
						}
					} else if (failure) {
						isAuthorised = false;
						failure();
					}
				}
			);
		},
		authenticate = function (complete, failure) {
			checkAuth(false, complete, function () {
				dispatchEvent('authRequired', 'This operation requires authentication through Google!', function () {
					checkAuth(true, complete, failure);
				});
			});
		},
		loadApi = function (onComplete) {
			if (window.gapi && window.gapi.client) {
				onComplete();
			} else {
				window.googleClientLoaded = function () { onComplete(); };
				jQuery('<script src="https://apis.google.com/js/client.js?onload=googleClientLoaded"></script>').appendTo('body');
			}
		},
		makeReady = function () {
			var deferred = jQuery.Deferred();
			if (driveLoaded) {
				authenticate(deferred.resolve, deferred.reject);
				return;
			}
			loadApi(function () {
				gapi.client.setApiKey(apiKey);
				gapi.client.load('drive', 'v2', function () {
					driveLoaded = true;
					authenticate(deferred.resolve, deferred.reject);
				});
			});
			return deferred.promise();
		};
	this.ready = function () {
		var deferred = jQuery.Deferred();
		if (driveLoaded && isAuthorised) {
			deferred.resolve();
		} else {
			makeReady().then(deferred.resolve, deferred.reject);
		}
		return deferred.promise();
	};

	this.recognises = function (mapId) {
		return mapId && mapId[0] === "g";
	};

	this.retrieveAllFiles = function (callback) {
		var searchCriteria = "mimeType = '" + contentType + "' and not trashed",
			retrievePageOfFiles = function (request, result) {
				request.execute(function (resp) {
					result = result.concat(resp.items);
					var nextPageToken = resp.nextPageToken;
					if (nextPageToken) {
						request = gapi.client.drive.files.list({
							'pageToken': nextPageToken,
							q: searchCriteria
						});
						retrievePageOfFiles(request, result);
					} else {
						callback(result);
					}
				});
			},
			initialRequest = gapi.client.drive.files.list({
				'q': searchCriteria
			});
		retrievePageOfFiles(initialRequest, []);
	};

	this.loadMap = function (mapId) {
		var deferred = jQuery.Deferred(),
			googleId = mapId.substr(2),
			success = function (result) {
				var mapInfo = {
					mapId: mapId,
					googleId: googleId,
					idea: content(result.body)
				};
				deferred.resolve(mapInfo);
			};
		this.ready().then(
			function () {
				loadFile(googleId, success, deferred.reject);
			},
			deferred.reject
		);
		return deferred.promise();
	};

	this.saveMap = function (mapInfo) {
		var deferred = jQuery.Deferred(),
			timeout;
		this.ready().then(
			function () {
				timeout = setTimeout(deferred.reject, networkTimeoutMillis);
				saveFile(
					mapInfo,
					function (savedMapInfo) {
						clearTimeout(timeout);
						deferred.resolve(savedMapInfo);
					},
					deferred.reject
				);
			},
			deferred.reject
		);
		return deferred.promise();

	};
};

<commit_msg>change googleDriveRepository.authenticate to use promise<commit_after>/*global content, jQuery, MM, observable, setTimeout, clearTimeout, window, gapi, btoa, XMLHttpRequest */
MM.GoogleDriveRepository = function (clientId, apiKey, networkTimeoutMillis, contentType) {
	'use strict';
	observable(this);
	var driveLoaded,
		isAuthorised,
		dispatchEvent = this.dispatchEvent,
		saveFile = function (mapInfo, complete, fail) {
			var	boundary = '-------314159265358979323846',
				delimiter = "\r\n--" + boundary + "\r\n",
				close_delim = "\r\n--" + boundary + "--",
				metadata = {
					'title': mapInfo.idea.title + ".mup",
					'mimeType': contentType
				},
				//base64Data = btoa(JSON.stringify(mapInfo.idea)),
				data = JSON.stringify(mapInfo.idea),
				multipartRequestBody =
					delimiter +
					'Content-Type: application/json\r\n\r\n' +
					JSON.stringify(metadata) +
					delimiter +
					'Content-Type: ' + contentType + '\r\n' +
					'\r\n' +
					data +
					close_delim,
				request = gapi.client.request({
					'path': '/upload/drive/v2/files' + (mapInfo.googleId ? "/" + mapInfo.googleId : ""),
					'method': (mapInfo.googleId ? 'PUT' : 'POST'),
					'params': {'uploadType': 'multipart'},
					'headers': {
						'Content-Type': 'multipart/mixed; boundary="' + boundary + '"'
					},
					'body': multipartRequestBody
				});
			request.execute(function (resp) {
				if (resp.error) {
					if (fail) {
						fail(resp.error);
					}
				} else if (complete) {
					if (!mapInfo.googleId) {
						mapInfo.mapId = "g1" + resp.id;
						mapInfo.googleId = resp.id;
					}
					complete(mapInfo);
				}
			});
		},
		downloadFile = function (file, complete, fail) {
			if (file.downloadUrl) {
				var xhr = new XMLHttpRequest();
				xhr.open('GET', file.downloadUrl);
				if (file.title) {
					xhr.setRequestHeader('Authorization', 'Bearer ' + gapi.auth.getToken().access_token);
				}
				xhr.onload = function () {
					if (complete) {
						complete({
							title: file.title || 'unknown',
							body: JSON.parse(xhr.responseText)
						});
					}
				};
				xhr.onerror = function () {
					if (fail) {
						fail();
					}
				};
				xhr.send();
			} else if (fail) {
				fail();
			}
		},
		loadFile = function (fileId, complete, fail) {
			var request = gapi.client.drive.files.get({
				'fileId': fileId
			});
			fail = fail || function () {};
			request.execute(function (resp) {
				if (resp.error) {
					if (resp.error.code === 404) {
						fail('no-access-allowed');
					} else {
						fail(resp.error);
					}
				} else {
					downloadFile(resp, complete, fail);
				}
			});
		},
		checkAuth = function (showDialog, complete, failure) {
			gapi.auth.authorize(
				{
					'client_id': clientId,
					'scope': 'https://www.googleapis.com/auth/drive  https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.install https://www.googleapis.com/auth/userinfo.profile',
					'immediate': !showDialog
				},
				function (authResult) {
					if (authResult) {
						isAuthorised = true;
						if (complete) {
							complete();
						}
					} else if (failure) {
						isAuthorised = false;
						failure();
					}
				}
			);
		},
		authenticate = function () {
			var deferred = jQuery.Deferred();
			checkAuth(false, deferred.resolve, function () {
				dispatchEvent('authRequired', 'This operation requires authentication through Google!', function () {
					checkAuth(true, deferred.resolve, deferred.reject);
				});
			});
			return deferred.promise();
		},
		loadApi = function (onComplete) {
			if (window.gapi && window.gapi.client) {
				onComplete();
			} else {
				window.googleClientLoaded = function () { onComplete(); };
				jQuery('<script src="https://apis.google.com/js/client.js?onload=googleClientLoaded"></script>').appendTo('body');
			}
		},
		makeReady = function () {
			var deferred = jQuery.Deferred();
			if (driveLoaded) {
				authenticate().then(deferred.resolve, deferred.reject);
				return;
			}
			loadApi(function () {
				gapi.client.setApiKey(apiKey);
				gapi.client.load('drive', 'v2', function () {
					driveLoaded = true;
					authenticate().then(deferred.resolve, deferred.reject);
				});
			});
			return deferred.promise();
		};
	this.ready = function () {
		var deferred = jQuery.Deferred();
		if (driveLoaded && isAuthorised) {
			deferred.resolve();
		} else {
			makeReady().then(deferred.resolve, deferred.reject);
		}
		return deferred.promise();
	};

	this.recognises = function (mapId) {
		return mapId && mapId[0] === "g";
	};

	this.retrieveAllFiles = function (callback) {
		var searchCriteria = "mimeType = '" + contentType + "' and not trashed",
			retrievePageOfFiles = function (request, result) {
				request.execute(function (resp) {
					result = result.concat(resp.items);
					var nextPageToken = resp.nextPageToken;
					if (nextPageToken) {
						request = gapi.client.drive.files.list({
							'pageToken': nextPageToken,
							q: searchCriteria
						});
						retrievePageOfFiles(request, result);
					} else {
						callback(result);
					}
				});
			},
			initialRequest = gapi.client.drive.files.list({
				'q': searchCriteria
			});
		retrievePageOfFiles(initialRequest, []);
	};

	this.loadMap = function (mapId) {
		var deferred = jQuery.Deferred(),
			googleId = mapId.substr(2),
			success = function (result) {
				var mapInfo = {
					mapId: mapId,
					googleId: googleId,
					idea: content(result.body)
				};
				deferred.resolve(mapInfo);
			};
		this.ready().then(
			function () {
				loadFile(googleId, success, deferred.reject);
			},
			deferred.reject
		);
		return deferred.promise();
	};

	this.saveMap = function (mapInfo) {
		var deferred = jQuery.Deferred(),
			timeout;
		this.ready().then(
			function () {
				timeout = setTimeout(deferred.reject, networkTimeoutMillis);
				saveFile(
					mapInfo,
					function (savedMapInfo) {
						clearTimeout(timeout);
						deferred.resolve(savedMapInfo);
					},
					deferred.reject
				);
			},
			deferred.reject
		);
		return deferred.promise();

	};
};

<|endoftext|>


<commit_before>// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
$(function(){
    $("#messageid").click(function(){
      alert ("Hello world")
      $.ajax({
        type: 'POST',
        url: 'http://high-robot-603.heroku.com/jmsg',
        data: { msg: "hello world" },
       });
    });
});<commit_msg>js debug<commit_after>// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
$(function(){
    $("#messageid").click(function(){
      alert ("Hello world");
      $.ajax({
        type: 'POST',
        url: 'http://high-robot-603.heroku.com/jmsg',
        data: { msg: "hello world" },
       });
    });
});<|endoftext|>


<commit_before>// application function define

function set_title(photo_id){
  $('#photo_set_title_' + photo_id).show();
  $('#set_title_' + photo_id).hide();
  $('#delete_link_' + photo_id).hide();
  $('#sumbit_link_' + photo_id).show();
};

function more_timeline(dom){
  dom.html('读取中...');
  $.get(dom.attr('href'),function(data){
    dom.replaceWith(data);
    $('.timeago').trigger('replace.time');
    $('pre').trigger('replace.url');
  });

  return false;
};

function redirect_clear(id, type){
  $.post('/notifications/redirect_clear',{'id':id,'type':type});
};

$(document).ready(function(){

  $('.timeago').live('replace.time', function() {
      $(this).html('(' + jQuery.timeago($(this).html()) + ')').removeClass('timeago');
  }).trigger('replace.time');

  $('pre').live('replace.url', function() {
    var rURL = /(http:\/\/|https:\/\/)((\w|=|\?|\.|\/|&|-|!|#|%)+)/g;
    $(this).html(
      $(this).html().replace(rURL, "$1$2")
    );
  }).trigger('replace.url');

  $('.event_reply').live('click', function() {
    $(this).parent().next().toggle();
    e.preventDefault();
  });

  $('.reply_reply').live('click', function(e){
    var reply_field = $(this).parent().parent().parent().parent().find('input[type=text]');
    reply_field.val($(this).attr('title'));
    reply_field.focus();
    e.preventDefault();
  });

  $('form').live('submit', function() {
    $(this).find('input:submit').attr('disabled',true);
  });

  $(".zoom_photo").live('click', function(e) {
    var childrens = $(this).children();
    childrens.first().toggle();
    childrens.last().toggle();
    e.preventDefault();
  });

  $(".event_box")
    .live('mouseover', function() {$(this).addClass("hover")})
    .live('mouseout', function() {$(this).removeClass("hover")});

  $(".open_dialog").click(function(e) {
    IG.dialog.init({title: $(this).attr('title'),url: $(this).attr('href')});
    e.preventDefault();
  });

  $(".answer").click(function(e) {
    var id = $(this).attr('href');
    IG.dialog.init({title: $(this).attr('title'),content: $(id).html()});
    e.preventDefault();
  });

  $('#dialog_flash a').click();

  $('.with_tip').poshytip({
    className: 'tip-yellowsimple',
    showOn: 'focus',
    alignTo: 'target',
    alignX: 'right',
    alignY: 'center',
    offsetX: 5
  });

  $('.with_explain').poshytip({
    className: 'tip-yellowsimple',
    showTimeout: 1,
    alignTo: 'target',
    alignX: 'center',
    offsetY: 5,
    allowTipHover: false
  });

  if(!('placeholder' in document.createElement('input'))){
    $('input[placeholder!=""]').hint();
  };

  $(".tabContents").hide().first().show();
  $("#tabNav li a:first").addClass("active");
  $("#tabNav li a").click(function(){
    var activeTab = $(this).attr("href");
    $("#tabNav li a").removeClass("active");
    $(this).addClass("active");
    $(".tabContents").hide();
    $(activeTab).fadeIn();
    return false;
  });

});
<commit_msg>add the missing event parameter<commit_after>// application function define

function set_title(photo_id){
  $('#photo_set_title_' + photo_id).show();
  $('#set_title_' + photo_id).hide();
  $('#delete_link_' + photo_id).hide();
  $('#sumbit_link_' + photo_id).show();
};

function more_timeline(dom){
  dom.html('读取中...');
  $.get(dom.attr('href'),function(data){
    dom.replaceWith(data);
    $('.timeago').trigger('replace.time');
    $('pre').trigger('replace.url');
  });

  return false;
};

function redirect_clear(id, type){
  $.post('/notifications/redirect_clear',{'id':id,'type':type});
};

$(document).ready(function(){

  $('.timeago').live('replace.time', function() {
    $(this).html('(' + jQuery.timeago($(this).html()) + ')').removeClass('timeago');
  }).trigger('replace.time');

  $('pre').live('replace.url', function() {
    var rURL = /(http:\/\/|https:\/\/)((\w|=|\?|\.|\/|&|-|!|#|%)+)/g;
    $(this).html(
      $(this).html().replace(rURL, "$1$2")
    );
  }).trigger('replace.url');

  $('.event_reply').live('click', function(e) {
    $(this).parent().next().toggle();
    e.preventDefault();
  });

  $('.reply_reply').live('click', function(e){
    var reply_field = $(this).parent().parent().parent().parent().find('input[type=text]');
    reply_field.val($(this).attr('title'));
    reply_field.focus();
    e.preventDefault();
  });

  $('form').live('submit', function() {
    $(this).find('input:submit').attr('disabled',true);
  });

  $(".zoom_photo").live('click', function(e) {
    var childrens = $(this).children();
    childrens.first().toggle();
    childrens.last().toggle();
    e.preventDefault();
  });

  $(".event_box")
    .live('mouseover', function() {$(this).addClass("hover")})
    .live('mouseout', function() {$(this).removeClass("hover")});

  $(".open_dialog").click(function(e) {
    IG.dialog.init({title: $(this).attr('title'),url: $(this).attr('href')});
    e.preventDefault();
  });

  $(".answer").click(function(e) {
    var id = $(this).attr('href');
    IG.dialog.init({title: $(this).attr('title'),content: $(id).html()});
    e.preventDefault();
  });

  $('#dialog_flash a').click();

  $('.with_tip').poshytip({
    className: 'tip-yellowsimple',
    showOn: 'focus',
    alignTo: 'target',
    alignX: 'right',
    alignY: 'center',
    offsetX: 5
  });

  $('.with_explain').poshytip({
    className: 'tip-yellowsimple',
    showTimeout: 1,
    alignTo: 'target',
    alignX: 'center',
    offsetY: 5,
    allowTipHover: false
  });

  if(!('placeholder' in document.createElement('input'))){
    $('input[placeholder!=""]').hint();
  };

  $(".tabContents").hide().first().show();
  $("#tabNav li a:first").addClass("active");
  $("#tabNav li a").click(function(){
    var activeTab = $(this).attr("href");
    $("#tabNav li a").removeClass("active");
    $(this).addClass("active");
    $(".tabContents").hide();
    $(activeTab).fadeIn();
    return false;
  });

});
<|endoftext|>


<commit_before>// Controller for the poll list
function PollListCtrl($scope, Poll) {
	$scope.polls = Poll.query();
}

// Controller for an individual poll
function PollItemCtrl($scope, $routeParams, socket, Poll) {
	$scope.poll = Poll.get({pollId: $routeParams.pollId});
	console.log('pregunta');
console.log(poll.question);

	socket.on('myvote', function(data) {
		console.dir(data);
		if(data._id === $routeParams.pollId) {
			$scope.poll = data;
		}
	});

	socket.on('vote', function(data) {
		console.dir(data);
		if(data._id === $routeParams.pollId) {
			$scope.poll.choices = data.choices;
			$scope.poll.totalVotes = data.totalVotes;
		}
	});

	$scope.vote = function() {
		var pollId = $scope.poll._id,
				choiceId = $scope.poll.userVote;

		if(choiceId) {
			var voteObj = { poll_id: pollId, choice: choiceId };
			socket.emit('send:vote', voteObj);
		} else {
			alert('You must select an option to vote for');
		}
	};
}

// Controller for creating a new poll
function PollNewCtrl($scope, $location, Poll) {
	// Define an empty poll model object
	$scope.poll = {
		question: '',
		choices: [ { text: '' }, { text: '' }, { text: '' }]
	};

	// Method to add an additional choice option
	$scope.addChoice = function() {
		$scope.poll.choices.push({ text: '' });
	};

	// Validate and save the new poll to the database
	$scope.createPoll = function() {
		var poll = $scope.poll;

		// Check that a question was provided
		if(poll.question.length > 0) {
			var choiceCount = 0;

			// Loop through the choices, make sure at least two provided
			for(var i = 0, ln = poll.choices.length; i < ln; i++) {
				var choice = poll.choices[i];

				if(choice.text.length > 0) {
					choiceCount++
				}
			}

			if(choiceCount > 1) {
				// Create a new poll from the model
				var newPoll = new Poll(poll);

				// Call API to save poll to the database
				newPoll.$save(function(p, resp) {
					if(!p.error) {
						// If there is no error, redirect to the main view
						$location.path('polls');
					} else {
						alert('Could not create poll');
					}
				});
			} else {
				alert('You must enter at least two choices');
			}
		} else {
			alert('You must enter a question');
		}
	};
}<commit_msg>Rollback<commit_after>// Controller for the poll list
function PollListCtrl($scope, Poll) {
	$scope.polls = Poll.query();
}

// Controller for an individual poll
function PollItemCtrl($scope, $routeParams, socket, Poll) {
	$scope.poll = Poll.get({pollId: $routeParams.pollId});

	socket.on('myvote', function(data) {
		console.dir(data);
		if(data._id === $routeParams.pollId) {
			$scope.poll = data;
		}
	});

	socket.on('vote', function(data) {
		console.dir(data);
		if(data._id === $routeParams.pollId) {
			$scope.poll.choices = data.choices;
			$scope.poll.totalVotes = data.totalVotes;
		}
	});

	$scope.vote = function() {
		var pollId = $scope.poll._id,
				choiceId = $scope.poll.userVote;

		if(choiceId) {
			var voteObj = { poll_id: pollId, choice: choiceId };
			socket.emit('send:vote', voteObj);
		} else {
			alert('You must select an option to vote for');
		}
	};
}

// Controller for creating a new poll
function PollNewCtrl($scope, $location, Poll) {
	// Define an empty poll model object
	$scope.poll = {
		question: '',
		choices: [ { text: '' }, { text: '' }, { text: '' }]
	};

	// Method to add an additional choice option
	$scope.addChoice = function() {
		$scope.poll.choices.push({ text: '' });
	};

	// Validate and save the new poll to the database
	$scope.createPoll = function() {
		var poll = $scope.poll;

		// Check that a question was provided
		if(poll.question.length > 0) {
			var choiceCount = 0;

			// Loop through the choices, make sure at least two provided
			for(var i = 0, ln = poll.choices.length; i < ln; i++) {
				var choice = poll.choices[i];

				if(choice.text.length > 0) {
					choiceCount++
				}
			}

			if(choiceCount > 1) {
				// Create a new poll from the model
				var newPoll = new Poll(poll);

				// Call API to save poll to the database
				newPoll.$save(function(p, resp) {
					if(!p.error) {
						// If there is no error, redirect to the main view
						$location.path('polls');
					} else {
						alert('Could not create poll');
					}
				});
			} else {
				alert('You must enter at least two choices');
			}
		} else {
			alert('You must enter a question');
		}
	};
}
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2011 Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

define(['jquery'], function($) {
  return function(inputs) {
    var body       = $('body'),
        inputCover = $('<div />', { 'class': 'input_cover' });

    inputCover.on('mouseleave', function(e) { $(this).remove(); });

    $(inputs).on('mouseenter', function(e) {
      var el    = $(this),
          cover = inputCover.clone(true);

      cover.css({
        height   : el.height() + 10,
        width    : el.width() + 10,
        position : 'absolute',
        left     : el.offset().left - 5,
        top      : el.offset().top - 5,
        zIndex   : 15
      });

      body.append(cover);
    });
  };
});
<commit_msg>fix input overlays in IE, fixes #10223<commit_after>/**
 * Copyright (C) 2011 Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

define(['jquery'], function($) {
  return function(inputs) {
    var body       = $('body'),
        inputCover = $('<div />', { 'class': 'input_cover' });

    inputCover.on('mouseleave', function(e) { $(this).remove(); });

    $(inputs).on('mouseenter', function(e) {
      var el    = $(this),
          cover = inputCover.clone(true);

      cover.css({
        height   : el.height() + 12,
        width    : el.width() + 12,
        position : 'absolute',
        left     : el.offset().left - 6,
        top      : el.offset().top - 6,
        zIndex   : 15,
        background: 'url(/images/blank.png) 0 0 repeat'
      });

      body.append(cover);
    });
  };
});
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2011 Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

I18n.scoped('user_logins', function(I18n) {
$(document).ready(function() {
  var $form = $("#edit_pseudonym_form");
  var passwordable_account_ids = $("#passwordable_account_ids").text().split(",");
  $form.formSubmit({
    processData: function(data) {
      if(!$(this).hasClass('passwordable') || (!data['pseudonym[password]'] && !data['pseudonym[password_confirmation]'])) {
        delete data['pseudonym[password]'];
        delete data['pseudonym[password_confirmation]'];
      }
      if((data['pseudonym[password]'] || data['pseudonym[password_confirmation]']) && data['pseudonym[password]'] != data['pseudonym[password_confirmation]']) {
        $(this).formErrors({'pseudonym[password]': I18n.t('errors.passwords_do_not_match', 'passwords do not match')});
        return false;
      }
    },
    beforeSubmit: function(data) {
      $(this).find("button").attr('disabled', true)
        .filter(".submit_button").text(I18n.t('messages.saving', "Saving..."));
      var select = $(this).find(".account_id select")[0];
      var idx = select && select.selectedIndex;
      $(this).data('account_name', null);
      $(this).data('account_name', select && select.options[idx] && select.options[idx].innerHTML);
    },
    success: function(data) {
      $(this).find("button").attr('disabled', false)
        .filter(".submit_button").text(I18n.t('buttons.save', "Save"));
      $(this).dialog('close');
      if($(this).data('unique_id_text')) {
        var $login = $(this).data('unique_id_text').parents(".login");
      } else {
        var $login = $("#login_information .login.blank").clone(true);
        $("#login_information .add_holder").before($login.show());
        data.pseudonym.account_name = $(this).data('account_name') || $(this).find(".default_account_name").text();
      }
      $login.fillTemplateData({
        data: data.pseudonym,
        hrefValues: ['account_id']
      });
      if($.inArray(data.pseudonym.account_id.toString(), passwordable_account_ids) != -1) {
        $login.find(".links").addClass('passwordable');
      }
      $("#login_information .login .delete_pseudonym_link").show();
    },
    error: function(data) {
      $(this).find("button").attr('disabled', false)
        .filter(".submit_button").text(I18n.t('errors.save_failed', "Save Failed"));
    }
  });
  $("#edit_pseudonym_form .account_id_select").change(function() {
    $("#edit_pseudonym_form").find("tr.password").showIf($.inArray($(this).val(), passwordable_account_ids) != -1);
  });
  $("#edit_pseudonym_form .cancel_button").click(function() {
    $form.dialog('close');
  });
  $("#login_information").delegate('.login_details_link', 'click', function(event) {
    event.preventDefault();
    $(this).parents("tr").find(".login_details").show();
    $(this).hide();
  })
  .delegate('.edit_pseudonym_link', 'click', function(event) {
    event.preventDefault();
    var $form = $("#edit_pseudonym_form"),
        $sis_row = $form.find('.sis_user_id');
    $sis_row.hide();
    $form.attr('action', $(this).attr('rel')).attr('method', 'PUT');
    var data = $(this).parents(".login").getTemplateData({textValues: ['unique_id', 'sis_user_id', 'can_edit_sis_user_id']});
    data.password = "";
    data.password_confirmation = "";
    $form.fillFormData(data, {object_name: 'pseudonym'});
    if( data.can_edit_sis_user_id == 'true' ){
      $sis_row.show();
    }
    var passwordable = $(this).parents(".links").hasClass('passwordable');
    $form.toggleClass('passwordable', passwordable);
    $form.find("tr.password").showIf(passwordable);
    $form.find(".account_id").hide();
    $form.dialog('close').dialog({
      autoOpen: false,
      width: 'auto',
      close: function() {
        if($form.data('unique_id_text') && $form.data('unique_id_text').parents(".login").hasClass('blank')) {
          $form.data('unique_id_text').parents(".login").remove();
        }
      }
    }).dialog('open');
    $form.dialog('option', 'title', I18n.t('titles.update_login', 'Update Login'))
      .find(".submit_button").text(I18n.t('buttons.update_login', "Update Login"));
    var $unique_id = $(this).parents(".login").find(".unique_id");
    $form.data('unique_id_text', $unique_id);
    $form.find(":input:visible:first").focus().select();
  })
  .delegate('.delete_pseudonym_link', 'click', function(event) {
    event.preventDefault();
    if($("#login_information .login:visible").length < 2) {
      alert(I18n.t('notices.cant_delete_last_login', "You can't delete the last login for a user"));
      return;
    }
    var login = $(this).parents(".login").find(".unique_id").text();
    $(this).parents(".login").confirmDelete({
      message: I18n.t('confirms.delete_login', "Are you sure you want to delete the login, \"%{login}\"?", {login: login}),
      url: $(this).attr('rel'),
      success: function() {
        $(this).fadeOut();
        if($("#login_information .login:visible").length < 2) {
          $("#login_information .login .delete_pseudonym_link").hide();
        }
      }
    });
  })
  .delegate('.add_pseudonym_link', 'click', function(event) {
    event.preventDefault();
    $("#login_information .login.blank .edit_pseudonym_link").click();
    $form.attr('action', $(this).attr('rel')).attr('method', 'POST');
    $form.fillFormData({'pseudonym[unique_id]': ''});
    $form.dialog('option', 'title', I18n.t('titles.add_login', 'Add Login'))
      .find(".submit_button").text(I18n.t('buttons.add_login', "Add Login"));
    $form.find(".account_id").show();
    $form.find(".account_id_select").change();
    $form.data('unique_id_text', null);
  });
});
});
<commit_msg>fix #6014 deleting a pseudonym you just added<commit_after>/**
 * Copyright (C) 2011 Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

I18n.scoped('user_logins', function(I18n) {
$(document).ready(function() {
  var $form = $("#edit_pseudonym_form");
  var passwordable_account_ids = $("#passwordable_account_ids").text().split(",");
  $form.formSubmit({
    processData: function(data) {
      if(!$(this).hasClass('passwordable') || (!data['pseudonym[password]'] && !data['pseudonym[password_confirmation]'])) {
        delete data['pseudonym[password]'];
        delete data['pseudonym[password_confirmation]'];
      }
      if((data['pseudonym[password]'] || data['pseudonym[password_confirmation]']) && data['pseudonym[password]'] != data['pseudonym[password_confirmation]']) {
        $(this).formErrors({'pseudonym[password]': I18n.t('errors.passwords_do_not_match', 'passwords do not match')});
        return false;
      }
    },
    beforeSubmit: function(data) {
      $(this).find("button").attr('disabled', true)
        .filter(".submit_button").text(I18n.t('messages.saving', "Saving..."));
      var select = $(this).find(".account_id select")[0];
      var idx = select && select.selectedIndex;
      $(this).data('account_name', null);
      $(this).data('account_name', select && select.options[idx] && select.options[idx].innerHTML);
    },
    success: function(data) {
      $(this).find("button").attr('disabled', false)
        .filter(".submit_button").text(I18n.t('buttons.save', "Save"));
      $(this).dialog('close');
      if($(this).data('unique_id_text')) {
        var $login = $(this).data('unique_id_text').parents(".login");
      } else {
        var $login = $("#login_information .login.blank").clone(true);
        $("#login_information .add_holder").before($login.show());
        data.pseudonym.account_name = $(this).data('account_name') || $(this).find(".default_account_name").text();
      }
      $login.fillTemplateData({
        data: data.pseudonym,
        hrefValues: ['id', 'account_id']
      });
      if($.inArray(data.pseudonym.account_id.toString(), passwordable_account_ids) != -1) {
        $login.find(".links").addClass('passwordable');
      }
      $("#login_information .login .delete_pseudonym_link").show();
    },
    error: function(data) {
      $(this).find("button").attr('disabled', false)
        .filter(".submit_button").text(I18n.t('errors.save_failed', "Save Failed"));
    }
  });
  $("#edit_pseudonym_form .account_id_select").change(function() {
    $("#edit_pseudonym_form").find("tr.password").showIf($.inArray($(this).val(), passwordable_account_ids) != -1);
  });
  $("#edit_pseudonym_form .cancel_button").click(function() {
    $form.dialog('close');
  });
  $("#login_information").delegate('.login_details_link', 'click', function(event) {
    event.preventDefault();
    $(this).parents("tr").find(".login_details").show();
    $(this).hide();
  })
  .delegate('.edit_pseudonym_link', 'click', function(event) {
    event.preventDefault();
    var $form = $("#edit_pseudonym_form"),
        $sis_row = $form.find('.sis_user_id');
    $sis_row.hide();
    $form.attr('action', $(this).attr('rel')).attr('method', 'PUT');
    var data = $(this).parents(".login").getTemplateData({textValues: ['unique_id', 'sis_user_id', 'can_edit_sis_user_id']});
    data.password = "";
    data.password_confirmation = "";
    $form.fillFormData(data, {object_name: 'pseudonym'});
    if( data.can_edit_sis_user_id == 'true' ){
      $sis_row.show();
    }
    var passwordable = $(this).parents(".links").hasClass('passwordable');
    $form.toggleClass('passwordable', passwordable);
    $form.find("tr.password").showIf(passwordable);
    $form.find(".account_id").hide();
    $form.dialog('close').dialog({
      autoOpen: false,
      width: 'auto',
      close: function() {
        if($form.data('unique_id_text') && $form.data('unique_id_text').parents(".login").hasClass('blank')) {
          $form.data('unique_id_text').parents(".login").remove();
        }
      }
    }).dialog('open');
    $form.dialog('option', 'title', I18n.t('titles.update_login', 'Update Login'))
      .find(".submit_button").text(I18n.t('buttons.update_login', "Update Login"));
    var $unique_id = $(this).parents(".login").find(".unique_id");
    $form.data('unique_id_text', $unique_id);
    $form.find(":input:visible:first").focus().select();
  })
  .delegate('.delete_pseudonym_link', 'click', function(event) {
    event.preventDefault();
    if($("#login_information .login:visible").length < 2) {
      alert(I18n.t('notices.cant_delete_last_login', "You can't delete the last login for a user"));
      return;
    }
    var login = $(this).parents(".login").find(".unique_id").text();
    $(this).parents(".login").confirmDelete({
      message: I18n.t('confirms.delete_login', "Are you sure you want to delete the login, \"%{login}\"?", {login: login}),
      url: $(this).attr('rel'),
      success: function() {
        $(this).fadeOut();
        if($("#login_information .login:visible").length < 2) {
          $("#login_information .login .delete_pseudonym_link").hide();
        }
      }
    });
  })
  .delegate('.add_pseudonym_link', 'click', function(event) {
    event.preventDefault();
    $("#login_information .login.blank .edit_pseudonym_link").click();
    $form.attr('action', $(this).attr('rel')).attr('method', 'POST');
    $form.fillFormData({'pseudonym[unique_id]': ''});
    $form.dialog('option', 'title', I18n.t('titles.add_login', 'Add Login'))
      .find(".submit_button").text(I18n.t('buttons.add_login', "Add Login"));
    $form.find(".account_id").show();
    $form.find(".account_id_select").change();
    $form.data('unique_id_text', null);
  });
});
});
<|endoftext|>