<commit_before>'use strict';

// Requires & plugins
var gulp = require('gulp');
var gutil = require('gulp-util');
var webserver = require('gulp-webserver'); // optional - use for POC or if a webserver is needed for serving project files (i.e. template only)
var livereload = require('gulp-livereload'); // livereload browser plugin is also required for this to work
var filesize = require('gulp-filesize');

// Styles
var sass = require('gulp-sass'); // LibSass = faster than Ruby Sass, not quite 100% Sass compliant.  require('gulp-ruby-sass') for Ruby Sass
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('gulp-autoprefixer');

// Scripts
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var jshint = require('gulp-jshint');
var map = require('map-stream');
var merge = require('merge-stream');

// Images
var imagemin = require('gulp-imagemin');
	// imagemin plugins
	var gifsicle = require('imagemin-gifsicle');
	var optipng = require('imagemin-optipng');
	var pngquant = require('imagemin-pngquant');
	var mozjpeg = require('imagemin-mozjpeg');
	var svgo = require('imagemin-svgo');

// Configuration and environment variables

// use "gulp --prod" to trigger production/build mode from commandline
var isProduction = false;
var sassStyle = 'expanded';
var sourceMap = true;
var showErrorStack = gutil.env.stacktrace;

if (gutil.env.prod) {
	isProduction = true;
	sassStyle = 'compressed';
	sourceMap = false;
}

// File reference variables
var basePaths = {
	src: 'src/',
	dest: 'src/'  // current recommendation is to compile files to the same folder
};

var paths = {
	html: {
		src: basePaths.src,
		dest: basePaths.dest
	},
	images: {
		src: basePaths.src + 'images/',
		dest: basePaths.dest
	},
	scripts: {
		src: basePaths.src + 'js/',
		dest: basePaths.dest + 'js/'
	},
	styles: {
		src: basePaths.src + 'css/' + 'sass/',   // sass is reference for the type of preprocessor, we use the SCSS file format in Sass
		dest: basePaths.dest + 'css/'
	}
};

var appFiles = {
	html: paths.html.src +  '**/*.html',
	images: paths.html.src +  '**/*.{jpg,jpeg,gif,svg}', //png fails on Windows 8.1 right now
	imagesPng: paths.html.src + '**/*.png',
	styles: paths.styles.src + '**/*.scss',
	vendorScriptFile: 'vendors.js',
	scriptFile: 'enlBase.js'
};
// Generally `/vendors` needs to be loaded first, exclude the built file(s)
appFiles.userScripts = [paths.scripts.src + '**/*.js', '!' + paths.scripts.src + 'vendors/**/*.js', '!' + paths.scripts.src + appFiles.scriptFile];
appFiles.vendorScripts = [paths.scripts.src + 'vendors/**/*.js'];
appFiles.allScripts = [paths.scripts.src + 'vendors/**/*.js', paths.scripts.src + '**/*.js', '!' + paths.scripts.src + appFiles.scriptFile];

// END Configuration

// Standard error handler
function errorHandler(err){
	gutil.beep();
	// Log to console
	gutil.log(gutil.colors.red('Error: '), err.message, ' - ', err.fileName);
	if (showErrorStack) {
		gutil.log(err.stack);
	}
	this.emit('end');
}

// CSS / Sass compilation
function styles() {
	var  stream = gulp.src(appFiles.styles)
		.pipe(sourcemaps.init())
		.pipe(sass({outputStyle: sassStyle})).on('error', errorHandler)
		.pipe(autoprefixer({ browsers: ['last 2 versions'], cascade: false })).on('error', errorHandler)
		.pipe(sourcemaps.write())
		.pipe(gulp.dest(paths.styles.dest))
		.pipe(filesize());

	return stream;
}

// JavaScript tasks and compilation
function jslinter(scriptsToLint) {

	// Create a custom reporter to show Lint Errors nicely formatted
	var customReporter = map(lintReporter);
	function lintReporter(file, cb) {
		if (!file.jshint.success) {
			var numErrors = file.jshint.results.length;
			var errIndex = 1;
			gutil.log('----------------------------------------');
			gutil.log(gutil.colors.bgRed('JSHint Error' + (numErrors > 1 ? '(s)' : '') + ': (' + numErrors + ')'));
			file.jshint.results.forEach(displayError);
			gutil.log('----------------------------------------');
			gutil.beep();
		}
		if (typeof cb === 'function') { cb(null, file); }

		function displayError(err) {
			var filepathparts = file.path.indexOf('/') > -1 ? file.path.split('/') : file.path.split('\\');
			var filename = filepathparts.length > 1 ?
					'.../' + filepathparts[filepathparts.length - 2] + '/' + filepathparts[filepathparts.length - 1] :
					file.path;
			if (err) {
				gutil.log(' ' + errIndex + ') ' +
							gutil.colors.magenta(filename) + ': ' +
							gutil.colors.yellow(err.error.line + ':' + err.error.character) + ' - ' +
							gutil.colors.blue(err.error.reason));
			}
			errIndex++;
		}
	}

	var stream = gulp.src(scriptsToLint)
		.pipe(jshint())
		.pipe(customReporter);

	return stream;
}

function lintjs() {
	jslinter(appFiles.userScripts); // don't lint `/vendor` scripts
}

function scripts() {
	// lint JavaScript files, but don't prevent scripts task from continuing
	lintjs();

	// don't generate map files for vendor files
	var vendorStream = gulp.src(appFiles.vendorScripts)
		.pipe(concat(appFiles.vendorScriptFile, {newLine: ';\r\n'})).on('error', errorHandler)
		.pipe(isProduction ? filesize() : gutil.noop())
		.pipe(isProduction ? uglify() : gutil.noop()).on('error', errorHandler)
		.pipe(gulp.dest(paths.scripts.dest))
		.pipe(filesize());

	var userStream = gulp.src(appFiles.userScripts)
		.pipe(sourcemaps.init())
		.pipe(concat(appFiles.scriptFile, {newLine: ';\r\n'})).on('error', errorHandler)
		.pipe(isProduction ? filesize() : gutil.noop())
		.pipe(isProduction ? uglify() : gutil.noop()).on('error', errorHandler)
		.pipe(sourcemaps.write())
		.pipe(gulp.dest(paths.scripts.dest))
		.pipe(filesize());

	// See this link for recipe for multiple streams:
	// https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md
	return merge(vendorStream, userStream);
}

// Image Minification and compression
// More info: http://www.devworkflows.com/posts/adding-image-optimization-to-your-gulp-workflow/
function compressImages() {
	var stream = gulp.src(appFiles.images)
		.pipe(imagemin({
			progressive: true,
			svgoPlugins: [{removeViewBox: false}],
			use: [
				gifsicle({interlaced: true}),
				optipng({optimizationLevel: 3}),
				pngquant({quality: '65-80', speed: 4}),
				mozjpeg({quality: '70'}),
				svgo()
			]
		})).on('error', errorHandler)
		.pipe(gulp.dest(paths.images.dest));

	return stream;
}

// TODO: This does not currently work (tested on Windows 8.1)
// TODO: Check on a Mac
// TODO: Figure out how to get PNG files working, recombine image compression tasks
function compressPngImages() {
	var stream = gulp.src(appFiles.imagesPng)
		.pipe(imagemin({
			progressive: true,
			use: [
				optipng({optimizationLevel: 3}),
				pngquant({quality: '65-80', speed: 4})
			]
		})).on('error', errorHandler)
		.pipe(gulp.dest(paths.images.dest));

	return stream;
}

// Webserver and watch
function startWebserver() {
	if (isProduction) { return; }

	var stream = gulp.src(basePaths.dest)
		.pipe(webserver({
			livereload: true,
			directoryListing: true,
			open: true,
			port: 8000
		}));

	return stream;
}

function watchAndServer(done) {
	if (isProduction) { return; }

	// Remove this if you do not need webserver to view files locally
	startWebserver();

	// TODO: Can't watch image files if writing back to the same directory, would create infinite loop
	// TODO: Need to look into seeing if there is a way to disable the watch, run the task, and re-enable the watch once done
	// gulp.watch(appFiles.images, compressImages);

	gulp.watch(appFiles.styles, ['styles']);
	gulp.watch([appFiles.allScripts,
				'!' + paths.scripts.src + appFiles.scriptFile,
				'!' + paths.scripts.src + appFiles.vendorScriptFile
			   ], ['scripts']);

	// return a callback function to signify the task has finished running (the watches will continue to run)
	if (typeof done === 'function') { done(); }
}

/*********************
	Task(s)

	- Tasks all listed at the bottom for easy scanning,
	- Each task references the function that will be called
	- Task functions should return a stream so Gulp can know when the task is finished
	    More info: https://github.com/gulpjs/gulp/blob/master/docs/API.md#async-task-support

*********************/

// Style Task(s)
gulp.task('styles', styles);

// Script Task(s)
gulp.task('lintjs', lintjs);
gulp.task('scripts', scripts);

// Image Compression Task(s)
gulp.task('imagemin', compressImages);
gulp.task('imagemin:png', compressPngImages);

// Webserver/Watch Task(s)
gulp.task('watch', watchAndServer);

// Default task
// by default it will run the dev process.
// Use "gulp --prod" to build for production
gulp.task('default', ['styles', 'scripts', 'watch']);

// Build task, skips the watch,
// same can be accomplished with gulp --prod
gulp.task('build', ['styles', 'scripts']);
<commit_msg>update gulpfile<commit_after>'use strict';

// Requires & plugins
//
// ****************************************************************************************************
//  Double check and update version numbers in the package.json file when setting
//  up a new project to ensure that the project is using up to date packages.
// ****************************************************************************************************

var gulp         = require('gulp');
var gutil        = require('gulp-util');
var webserver    = require('gulp-webserver'); // optional - use for POC or if a webserver is needed for serving project files (i.e. template only)
var livereload   = require('gulp-livereload'); // livereload browser plugin is also required for this to work
var filesize     = require('gulp-filesize');

// Styles
var sass         = require('gulp-sass'); // LibSass = faster than Ruby Sass, not quite 100% Sass compliant.  require('gulp-ruby-sass') for Ruby Sass
var sourcemaps   = require('gulp-sourcemaps');
var autoprefixer = require('gulp-autoprefixer');

// Scripts
var concat       = require('gulp-concat');
var uglify       = require('gulp-uglify');
var jshint       = require('gulp-jshint');
var map          = require('map-stream');
var merge        = require('merge-stream');

// Images
var imagemin     = require('gulp-imagemin');
	// imagemin plugins
	var gifsicle = require('imagemin-gifsicle');
	var optipng  = require('imagemin-optipng');
	var pngquant = require('imagemin-pngquant');
	var mozjpeg  = require('imagemin-mozjpeg');
	var svgo     = require('imagemin-svgo');

// Configuration and environment variables

// use "gulp --prod" to trigger production/build mode from commandline
var isProduction = false;
var sassStyle = 'expanded';
var sourceMap = true;
var showErrorStack = gutil.env.stacktrace;

if (gutil.env.prod) {
	isProduction = true;
	sassStyle = 'compressed';
	sourceMap = false;
}

// File reference variables
var basePaths = {
	src: 'src/',
	dest: 'src/'  // current recommendation is to compile files to the same folder
};

var paths = {
	html: {
		src: basePaths.src,
		dest: basePaths.dest
	},
	images: {
		src: basePaths.src + 'images/',
		dest: basePaths.dest
	},
	scripts: {
		src: basePaths.src + 'js/',
		dest: basePaths.dest + 'js/'
	},
	styles: {
		src: basePaths.src + 'css/' + 'sass/',   // sass is reference for the type of preprocessor, we use the SCSS file format in Sass
		dest: basePaths.dest + 'css/'
	}
};

var appFiles = {
	html: paths.html.src +  '**/*.html',
	images: paths.html.src +  '**/*.{jpg,jpeg,png,gif,svg}',
	styles: paths.styles.src + '**/*.scss',
	vendorScriptFile: 'vendors.js',
	scriptFile: 'enlBase.js'
};
// Generally `/vendors` needs to be loaded first, exclude the built file(s)
appFiles.userScripts = [paths.scripts.src + '**/*.js', '!' + paths.scripts.src + 'vendors/**/*.js', '!' + paths.scripts.src + appFiles.scriptFile];
appFiles.vendorScripts = [paths.scripts.src + 'vendors/**/*.js'];
appFiles.allScripts = [paths.scripts.src + 'vendors/**/*.js', paths.scripts.src + '**/*.js', '!' + paths.scripts.src + appFiles.scriptFile];

// END Configuration

// Standard error handler
function errorHandler(err){
	gutil.beep();
	// Log to console
	gutil.log(gutil.colors.red('Error: '), err.message, ' - ', err.fileName);
	if (showErrorStack) {
		gutil.log(err.stack);
	}
	this.emit('end');
}

// CSS / Sass compilation
function styles() {
	var  stream = gulp.src(appFiles.styles)
		.pipe(sourcemaps.init())
		.pipe(sass({outputStyle: sassStyle})).on('error', errorHandler)
		.pipe(autoprefixer({ browsers: ['last 2 versions'], cascade: false })).on('error', errorHandler)
		.pipe(sourcemaps.write())
		.pipe(gulp.dest(paths.styles.dest))
		.pipe(filesize());

	return stream;
}

// JavaScript tasks and compilation
function jslinter(scriptsToLint) {

	// Create a custom reporter to show Lint Errors nicely formatted
	var customReporter = map(lintReporter);
	function lintReporter(file, cb) {
		if (!file.jshint.success) {
			var numErrors = file.jshint.results.length;
			var errIndex = 1;
			gutil.log('----------------------------------------');
			gutil.log(gutil.colors.bgRed('JSHint Error' + (numErrors > 1 ? '(s)' : '') + ': (' + numErrors + ')'));
			file.jshint.results.forEach(displayError);
			gutil.log('----------------------------------------');
			gutil.beep();
		}
		if (typeof cb === 'function') { cb(null, file); }

		function displayError(err) {
			var filepathparts = file.path.indexOf('/') > -1 ? file.path.split('/') : file.path.split('\\');
			var filename = filepathparts.length > 1 ?
					'.../' + filepathparts[filepathparts.length - 2] + '/' + filepathparts[filepathparts.length - 1] :
					file.path;
			if (err) {
				gutil.log(' ' + errIndex + ') ' +
							gutil.colors.magenta(filename) + ': ' +
							gutil.colors.yellow(err.error.line + ':' + err.error.character) + ' - ' +
							gutil.colors.blue(err.error.reason));
			}
			errIndex++;
		}
	}

	var stream = gulp.src(scriptsToLint)
		.pipe(jshint())
		.pipe(customReporter);

	return stream;
}

function lintjs() {
	jslinter(appFiles.userScripts); // don't lint `/vendor` scripts
}

function scripts() {
	// lint JavaScript files, but don't prevent scripts task from continuing
	lintjs();

	// don't generate map files for vendor files
	var vendorStream = gulp.src(appFiles.vendorScripts)
		.pipe(concat(appFiles.vendorScriptFile, {newLine: ';\r\n'})).on('error', errorHandler)
		.pipe(isProduction ? filesize() : gutil.noop())
		.pipe(isProduction ? uglify() : gutil.noop()).on('error', errorHandler)
		.pipe(gulp.dest(paths.scripts.dest))
		.pipe(filesize());

	var userStream = gulp.src(appFiles.userScripts)
		.pipe(sourcemaps.init())
		.pipe(concat(appFiles.scriptFile, {newLine: ';\r\n'})).on('error', errorHandler)
		.pipe(isProduction ? filesize() : gutil.noop())
		.pipe(isProduction ? uglify() : gutil.noop()).on('error', errorHandler)
		.pipe(sourcemaps.write())
		.pipe(gulp.dest(paths.scripts.dest))
		.pipe(filesize());

	// See this link for recipe for multiple streams:
	// https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md
	return merge(vendorStream, userStream);
}

// Image Minification and compression
// More info: http://www.devworkflows.com/posts/adding-image-optimization-to-your-gulp-workflow/
function compressImages() {
	var stream = gulp.src(appFiles.images)
		.pipe(imagemin({
			progressive: true,
			svgoPlugins: [{removeViewBox: false}],
			use: [
				gifsicle({interlaced: true}),
				optipng({optimizationLevel: 3}),
				pngquant({quality: '65-80', speed: 4}),
				mozjpeg({quality: '70'}),
				svgo()
			]
		})).on('error', errorHandler)
		.pipe(gulp.dest(paths.images.dest));

	return stream;
}

// Webserver and watch
function startWebserver() {
	if (isProduction) { return; }

	var stream = gulp.src(basePaths.dest)
		.pipe(webserver({
			livereload: true,
			directoryListing: true,
			open: true,
			port: 8000
		}));

	return stream;
}

function watchAndServer(done) {
	if (isProduction) { return; }

	// Remove this if you do not need webserver to view files locally
	startWebserver();

	// TODO: Can't watch image files if writing back to the same directory, would create infinite loop
	// TODO: Need to look into seeing if there is a way to disable the watch, run the task, and re-enable the watch once done
	// gulp.watch(appFiles.images, compressImages);

	gulp.watch(appFiles.styles, ['styles']);
	gulp.watch([appFiles.allScripts,
				'!' + paths.scripts.src + appFiles.scriptFile,
				'!' + paths.scripts.src + appFiles.vendorScriptFile
			   ], ['scripts']);

	// return a callback function to signify the task has finished running (the watches will continue to run)
	if (typeof done === 'function') { done(); }
}

/*********************
	Task(s)

	- Tasks all listed at the bottom for easy scanning,
	- Each task references the function that will be called
	- Task functions should return a stream so Gulp can know when the task is finished
	    More info: https://github.com/gulpjs/gulp/blob/master/docs/API.md#async-task-support

*********************/

// Style Task(s)
gulp.task('styles', styles);

// Script Task(s)
gulp.task('lintjs', lintjs);
gulp.task('scripts', scripts);

// Image Compression Task(s)
gulp.task('imagemin', compressImages);

// Webserver/Watch Task(s)
gulp.task('watch', watchAndServer);

// Default task
// by default it will run the dev process.
// Use "gulp --prod" to build for production
gulp.task('default', ['styles', 'scripts', 'watch']);

// Build task, skips the watch,
// same can be accomplished with gulp --prod
gulp.task('build', ['styles', 'scripts']);
<|endoftext|>


<commit_before>dojo.provide("dijit.form.ValidationTextBox");

dojo.require("dojo.i18n");

dojo.require("dijit.form.TextBox");
dojo.require("dijit.Tooltip");

dojo.requireLocalization("dijit.form", "validate");

/*=====
	dijit.form.ValidationTextBox.__Constraints = function(){
		// locale: String
		//		locale used for validation, picks up value from this widget's lang attribute
		// _flags_: anything
		//		various flags passed to regExpGen function
		this.locale = "";
		this._flags_ = "";
	}
=====*/

dojo.declare(
	"dijit.form.ValidationTextBox",
	dijit.form.TextBox,
	{
		// summary:
		//		A TextBox subclass with the ability to validate content of various types and provide user feedback.

		templatePath: dojo.moduleUrl("dijit.form", "templates/ValidationTextBox.html"),
		baseClass: "dijitTextBox",

		// default values for new subclass properties
		// required: Boolean
		//		Can be true or false, default is false.
		required: false,

		// promptMessage: String
		//		Hint string
		promptMessage: "",

		// invalidMessage: String
		// 		The message to display if value is invalid.
		invalidMessage: "$_unset_$", // read from the message file if not overridden

		// constraints: dijit.form.ValidationTextBox.__Constraints
		//		user-defined object needed to pass parameters to the validator functions
		constraints: {},

		// regExp: String
		//		regular expression string used to validate the input
		//		Do not specify both regExp and regExpGen
		regExp: ".*",

		// regExpGen: Function
		//		user replaceable function used to generate regExp when dependent on constraints
		//		Do not specify both regExp and regExpGen
		regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/constraints){ return this.regExp; },

		// state: String
		//		Shows current state (ie, validation result) of input (Normal, Warning, or Error)
		state: "",

		//	tooltipPosition: String[]
		//		See description of dijit.Tooltip.defaultPosition for details on this parameter.
		tooltipPosition: [],

		setValue: function(){
			this.inherited(arguments);
			this.validate(this._focused);
		},

		validator: function(/*anything*/value, /*dijit.form.ValidationTextBox.__Constraints*/constraints){
			// summary: user replaceable function used to validate the text input against the regular expression.
			return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
				(!this.required || !this._isEmpty(value)) &&
				(this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
		},

		_isValidSubset: function(){
			// summary:
			//	Returns true if the value is either already valid or could be made valid by appending characters.
			return this.textbox.value.search(this._partialre) == 0;
		},

		isValid: function(/*Boolean*/ isFocused){
			// summary: Need to over-ride with your own validation code in subclasses
			return this.validator(this.textbox.value, this.constraints);
		},

		_isEmpty: function(value){
			// summary: Checks for whitespace
			return /^\s*$/.test(value); // Boolean
		},

		getErrorMessage: function(/*Boolean*/ isFocused){
			// summary: return an error message to show if appropriate
			return this.invalidMessage; // String
		},

		getPromptMessage: function(/*Boolean*/ isFocused){
			// summary: return a hint to show if appropriate
			return this.promptMessage; // String
		},

		_maskValidSubsetError: true,
		validate: function(/*Boolean*/ isFocused){
			// summary:
			//		Called by oninit, onblur, and onkeypress.
			// description:
			//		Show missing or invalid messages if appropriate, and highlight textbox field.
			var message = "";
			var isValid = this.disabled || this.isValid(isFocused);
			if(isValid){ this._maskValidSubsetError = true; }
			var isValidSubset = !isValid && isFocused && this._isValidSubset();
			var isEmpty = this._isEmpty(this.textbox.value);
			this.state = (isValid || (!this._hasBeenBlurred && isEmpty) || isValidSubset) ? "" : "Error";
			if(this.state == "Error"){ this._maskValidSubsetError = false; }
			this._setStateClass();
			dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
			if(isFocused){
				if(isEmpty){
					message = this.getPromptMessage(true);
				}
				if(!message && (this.state == "Error" || (isValidSubset && !this._maskValidSubsetError))){
					message = this.getErrorMessage(true);
				}
			}
			this.displayMessage(message);
			return isValid;
		},

		// currently displayed message
		_message: "",

		displayMessage: function(/*String*/ message){
			// summary:
			//		User overridable method to display validation errors/hints.
			//		By default uses a tooltip.
			if(this._message == message){ return; }
			this._message = message;
			dijit.hideTooltip(this.domNode);
			if(message){
				dijit.showTooltip(message, this.domNode, this.tooltipPosition);
			}
		},

		_refreshState: function(){
			this.validate(this._focused);
		},

		_update: function(/*Event*/e){
			this._refreshState();
			this._onMouse(e);	// update CSS classes
		},

		//////////// INITIALIZATION METHODS ///////////////////////////////////////

		constructor: function(){
			this.constraints = {};
		},

		postMixInProperties: function(){
			this.inherited(arguments);
			this.constraints.locale = this.lang;
			this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
			if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
			var p = this.regExpGen(this.constraints);
			this.regExp = p;
			var partialre = "";
			// parse the regexp and produce a new regexp that matches valid subsets
			// if the regexp is .* then there's no use in matching subsets since everything is valid
			if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
				function (re){
					switch(re.charAt(0)){
						case '{':
						case '+':
						case '?':
						case '*':
						case '^':
						case '$':
						case '|':
						case '(': partialre += re; break;
						case ")": partialre += "|$)"; break;
						 default: partialre += "(?:"+re+"|$)"; break;
					}
				}
			);}
			try{ // this is needed for now since the above regexp parsing needs more test verification
				"".search(partialre);
			}catch(e){ // should never be here unless the original RE is bad or the parsing is bad
				partialre = this.regExp;
				console.debug('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
			} // should never be here unless the original RE is bad or the parsing is bad
			this._partialre = "^(?:" + partialre + ")$";
		},

		setAttribute: function(/*String*/ attr, /*anything*/ value){
			this.inherited(arguments);
			switch(attr){
				case "disabled":
					if(this.valueNode){
						this.valueNode.disabled = this.disabled;
					}
					this._refreshState();
					break;
				case "required":
					dijit.setWaiState(this.focusNode,"required", this.required);
					this._refreshState();
			}
		},

		postCreate: function(){
			if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE
				var s = dojo.getComputedStyle(this.focusNode);
				if(s){
					var ff = s.fontFamily;
					if(ff){
						this.focusNode.style.fontFamily = ff;
					}
				}
			}
			dijit.setWaiState(this.focusNode,"required", this.required);
			this.inherited(arguments);
		}
	}
);

dojo.declare(
	"dijit.form.MappedTextBox",
	dijit.form.ValidationTextBox,
	{
		// summary:
		//		A dijit.form.ValidationTextBox subclass which provides a visible formatted display and a serializable
		//		value in a hidden input field which is actually sent to the server.  The visible display may
		//		be locale-dependent and interactive.  The value sent to the server is stored in a hidden
		//		input field which uses the `name` attribute declared by the original widget.  That value sent
		//		to the serveris defined by the dijit.form.MappedTextBox.serialize method and is typically
		//		locale-neutral.

		serialize: function(/*anything*/val, /*Object?*/options){
			// summary: user replaceable function used to convert the getValue() result to a String
			return val.toString ? val.toString() : ""; // String
		},

		toString: function(){
			// summary: display the widget as a printable string using the widget's value
			var val = this.filter(this.getValue());
			return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
		},

		validate: function(){
			if(this.valueNode){ this.valueNode.value = this.toString(); } // test valueNode in case this is called before postCreate runs
			return this.inherited(arguments);
		},

		postCreate: function(){
			var textbox = this.textbox;
			var valueNode = (this.valueNode = dojo.doc.createElement("input"));
			valueNode.setAttribute("type", textbox.type);
			valueNode.setAttribute("value", this.toString());
			dojo.style(valueNode, "display", "none");
			valueNode.name = this.textbox.name;
			valueNode.disabled = this.textbox.disabled;
			this.textbox.name = this.textbox.name + "_displayed_";
			this.textbox.removeAttribute("name");
			dojo.place(valueNode, textbox, "after");

			this.inherited(arguments);
		}
	}
);

/*=====
	dijit.form.RangeBoundTextBox.__Constraints = function(){
		// min: Number
		//		Minimum signed value.  Default is -Infinity
		// max: Number
		//		Maximum signed value.  Default is +Infinity
		this.min = min;
		this.max = max;
	}
=====*/

dojo.declare(
	"dijit.form.RangeBoundTextBox",
	dijit.form.MappedTextBox,
	{
		// summary:
		//		A dijit.form.MappedTextBox subclass which defines a range of valid values
		//
		// constraints: dijit.form.RangeBoundTextBox.__Constraints
		//
		// rangeMessage: String
		//		The message to display if value is out-of-range

		/*=====
		constraints: {},
		======*/
		rangeMessage: "",

		rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){
			// summary: user replaceable function used to validate the range of the numeric input value
			var isMin = "min" in constraints;
			var isMax = "max" in constraints;
			if(isMin || isMax){
				return (!isMin || this.compare(primitive,constraints.min) >= 0) &&
					(!isMax || this.compare(primitive,constraints.max) <= 0);
			}
			return true; // Boolean
		},

		isInRange: function(/*Boolean*/ isFocused){
			// summary: Need to over-ride with your own validation code in subclasses
			return this.rangeCheck(this.getValue(), this.constraints);
		},

		_isDefinitelyOutOfRange: function(){
			// summary:
			//	Returns true if the value is out of range and will remain
			//	out of range even if the user types more characters
			var val = this.getValue();
			var isTooLittle = false;
			var isTooMuch = false;
			if("min" in this.constraints){
				var min = this.constraints.min;
				val = this.compare(val, ((typeof min == "number") && min >= 0)? 0 : min);
				isTooLittle = (typeof val == "number") && val < 0;
			}
			if("max" in this.constraints){
				var max = this.constraints.max;
				val = this.compare(val, ((typeof max != "number") || max > 0)? max : 0);
				isTooMuch = (typeof val == "number") && val > 0;
			}
			return isTooLittle || isTooMuch;
		},

		_isValidSubset: function(){
			return this.inherited(arguments) && !this._isDefinitelyOutOfRange();
		},

		isValid: function(/*Boolean*/ isFocused){
			return this.inherited(arguments) &&
				((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean
		},

		getErrorMessage: function(/*Boolean*/ isFocused){
			if(dijit.form.RangeBoundTextBox.superclass.isValid.call(this, false) && !this.isInRange(isFocused)){ return this.rangeMessage; } // String
			return this.inherited(arguments);
		},

		postMixInProperties: function(){
			this.inherited(arguments);
			if(!this.rangeMessage){
				this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
				this.rangeMessage = this.messages.rangeMessage;
			}
		},

		postCreate: function(){
			this.inherited(arguments);
			if(this.constraints.min !== undefined){
				dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min);
			}
			if(this.constraints.max !== undefined){
				dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max);
			}
		},

		setValue: function(/*Number*/ value, /*Boolean?*/ priorityChange){
			dijit.setWaiState(this.focusNode, "valuenow", value);
			this.inherited('setValue', arguments);
		}
	}
);
<commit_msg>fixed range bug, fixes #7313<commit_after>dojo.provide("dijit.form.ValidationTextBox");

dojo.require("dojo.i18n");

dojo.require("dijit.form.TextBox");
dojo.require("dijit.Tooltip");

dojo.requireLocalization("dijit.form", "validate");

/*=====
	dijit.form.ValidationTextBox.__Constraints = function(){
		// locale: String
		//		locale used for validation, picks up value from this widget's lang attribute
		// _flags_: anything
		//		various flags passed to regExpGen function
		this.locale = "";
		this._flags_ = "";
	}
=====*/

dojo.declare(
	"dijit.form.ValidationTextBox",
	dijit.form.TextBox,
	{
		// summary:
		//		A TextBox subclass with the ability to validate content of various types and provide user feedback.

		templatePath: dojo.moduleUrl("dijit.form", "templates/ValidationTextBox.html"),
		baseClass: "dijitTextBox",

		// default values for new subclass properties
		// required: Boolean
		//		Can be true or false, default is false.
		required: false,

		// promptMessage: String
		//		Hint string
		promptMessage: "",

		// invalidMessage: String
		// 		The message to display if value is invalid.
		invalidMessage: "$_unset_$", // read from the message file if not overridden

		// constraints: dijit.form.ValidationTextBox.__Constraints
		//		user-defined object needed to pass parameters to the validator functions
		constraints: {},

		// regExp: String
		//		regular expression string used to validate the input
		//		Do not specify both regExp and regExpGen
		regExp: ".*",

		// regExpGen: Function
		//		user replaceable function used to generate regExp when dependent on constraints
		//		Do not specify both regExp and regExpGen
		regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/constraints){ return this.regExp; },

		// state: String
		//		Shows current state (ie, validation result) of input (Normal, Warning, or Error)
		state: "",

		//	tooltipPosition: String[]
		//		See description of dijit.Tooltip.defaultPosition for details on this parameter.
		tooltipPosition: [],

		setValue: function(){
			this.inherited(arguments);
			this.validate(this._focused);
		},

		validator: function(/*anything*/value, /*dijit.form.ValidationTextBox.__Constraints*/constraints){
			// summary: user replaceable function used to validate the text input against the regular expression.
			return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
				(!this.required || !this._isEmpty(value)) &&
				(this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
		},

		_isValidSubset: function(){
			// summary:
			//	Returns true if the value is either already valid or could be made valid by appending characters.
			return this.textbox.value.search(this._partialre) == 0;
		},

		isValid: function(/*Boolean*/ isFocused){
			// summary: Need to over-ride with your own validation code in subclasses
			return this.validator(this.textbox.value, this.constraints);
		},

		_isEmpty: function(value){
			// summary: Checks for whitespace
			return /^\s*$/.test(value); // Boolean
		},

		getErrorMessage: function(/*Boolean*/ isFocused){
			// summary: return an error message to show if appropriate
			return this.invalidMessage; // String
		},

		getPromptMessage: function(/*Boolean*/ isFocused){
			// summary: return a hint to show if appropriate
			return this.promptMessage; // String
		},

		_maskValidSubsetError: true,
		validate: function(/*Boolean*/ isFocused){
			// summary:
			//		Called by oninit, onblur, and onkeypress.
			// description:
			//		Show missing or invalid messages if appropriate, and highlight textbox field.
			var message = "";
			var isValid = this.disabled || this.isValid(isFocused);
			if(isValid){ this._maskValidSubsetError = true; }
			var isValidSubset = !isValid && isFocused && this._isValidSubset();
			var isEmpty = this._isEmpty(this.textbox.value);
			this.state = (isValid || (!this._hasBeenBlurred && isEmpty) || isValidSubset) ? "" : "Error";
			if(this.state == "Error"){ this._maskValidSubsetError = false; }
			this._setStateClass();
			dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
			if(isFocused){
				if(isEmpty){
					message = this.getPromptMessage(true);
				}
				if(!message && (this.state == "Error" || (isValidSubset && !this._maskValidSubsetError))){
					message = this.getErrorMessage(true);
				}
			}
			this.displayMessage(message);
			return isValid;
		},

		// currently displayed message
		_message: "",

		displayMessage: function(/*String*/ message){
			// summary:
			//		User overridable method to display validation errors/hints.
			//		By default uses a tooltip.
			if(this._message == message){ return; }
			this._message = message;
			dijit.hideTooltip(this.domNode);
			if(message){
				dijit.showTooltip(message, this.domNode, this.tooltipPosition);
			}
		},

		_refreshState: function(){
			this.validate(this._focused);
		},

		_update: function(/*Event*/e){
			this._refreshState();
			this._onMouse(e);	// update CSS classes
		},

		//////////// INITIALIZATION METHODS ///////////////////////////////////////

		constructor: function(){
			this.constraints = {};
		},

		postMixInProperties: function(){
			this.inherited(arguments);
			this.constraints.locale = this.lang;
			this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
			if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
			var p = this.regExpGen(this.constraints);
			this.regExp = p;
			var partialre = "";
			// parse the regexp and produce a new regexp that matches valid subsets
			// if the regexp is .* then there's no use in matching subsets since everything is valid
			if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
				function (re){
					switch(re.charAt(0)){
						case '{':
						case '+':
						case '?':
						case '*':
						case '^':
						case '$':
						case '|':
						case '(': partialre += re; break;
						case ")": partialre += "|$)"; break;
						 default: partialre += "(?:"+re+"|$)"; break;
					}
				}
			);}
			try{ // this is needed for now since the above regexp parsing needs more test verification
				"".search(partialre);
			}catch(e){ // should never be here unless the original RE is bad or the parsing is bad
				partialre = this.regExp;
				console.debug('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
			} // should never be here unless the original RE is bad or the parsing is bad
			this._partialre = "^(?:" + partialre + ")$";
		},

		setAttribute: function(/*String*/ attr, /*anything*/ value){
			this.inherited(arguments);
			switch(attr){
				case "disabled":
					if(this.valueNode){
						this.valueNode.disabled = this.disabled;
					}
					this._refreshState();
					break;
				case "required":
					dijit.setWaiState(this.focusNode,"required", this.required);
					this._refreshState();
			}
		},

		postCreate: function(){
			if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE
				var s = dojo.getComputedStyle(this.focusNode);
				if(s){
					var ff = s.fontFamily;
					if(ff){
						this.focusNode.style.fontFamily = ff;
					}
				}
			}
			dijit.setWaiState(this.focusNode,"required", this.required);
			this.inherited(arguments);
		}
	}
);

dojo.declare(
	"dijit.form.MappedTextBox",
	dijit.form.ValidationTextBox,
	{
		// summary:
		//		A dijit.form.ValidationTextBox subclass which provides a visible formatted display and a serializable
		//		value in a hidden input field which is actually sent to the server.  The visible display may
		//		be locale-dependent and interactive.  The value sent to the server is stored in a hidden
		//		input field which uses the `name` attribute declared by the original widget.  That value sent
		//		to the serveris defined by the dijit.form.MappedTextBox.serialize method and is typically
		//		locale-neutral.

		serialize: function(/*anything*/val, /*Object?*/options){
			// summary: user replaceable function used to convert the getValue() result to a String
			return val.toString ? val.toString() : ""; // String
		},

		toString: function(){
			// summary: display the widget as a printable string using the widget's value
			var val = this.filter(this.getValue());
			return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
		},

		validate: function(){
			if(this.valueNode){ this.valueNode.value = this.toString(); } // test valueNode in case this is called before postCreate runs
			return this.inherited(arguments);
		},

		postCreate: function(){
			var textbox = this.textbox;
			var valueNode = (this.valueNode = dojo.doc.createElement("input"));
			valueNode.setAttribute("type", textbox.type);
			valueNode.setAttribute("value", this.toString());
			dojo.style(valueNode, "display", "none");
			valueNode.name = this.textbox.name;
			valueNode.disabled = this.textbox.disabled;
			this.textbox.name = this.textbox.name + "_displayed_";
			this.textbox.removeAttribute("name");
			dojo.place(valueNode, textbox, "after");

			this.inherited(arguments);
		}
	}
);

/*=====
	dijit.form.RangeBoundTextBox.__Constraints = function(){
		// min: Number
		//		Minimum signed value.  Default is -Infinity
		// max: Number
		//		Maximum signed value.  Default is +Infinity
		this.min = min;
		this.max = max;
	}
=====*/

dojo.declare(
	"dijit.form.RangeBoundTextBox",
	dijit.form.MappedTextBox,
	{
		// summary:
		//		A dijit.form.MappedTextBox subclass which defines a range of valid values
		//
		// constraints: dijit.form.RangeBoundTextBox.__Constraints
		//
		// rangeMessage: String
		//		The message to display if value is out-of-range

		/*=====
		constraints: {},
		======*/
		rangeMessage: "",

		rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){
			// summary: user replaceable function used to validate the range of the numeric input value
			var isMin = "min" in constraints;
			var isMax = "max" in constraints;
			if(isMin || isMax){
				return (!isMin || this.compare(primitive,constraints.min) >= 0) &&
					(!isMax || this.compare(primitive,constraints.max) <= 0);
			}
			return true; // Boolean
		},

		isInRange: function(/*Boolean*/ isFocused){
			// summary: Need to over-ride with your own validation code in subclasses
			return this.rangeCheck(this.getValue(), this.constraints);
		},

		_isDefinitelyOutOfRange: function(){
			// summary:
			//	Returns true if the value is out of range and will remain
			//	out of range even if the user types more characters
			var val = this.getValue();
			var isTooLittle = false;
			var isTooMuch = false;
			if("min" in this.constraints){
				var min = this.constraints.min;
				val = this.compare(val, ((typeof min == "number") && min >= 0 && val !=0)? 0 : min);
				isTooLittle = (typeof val == "number") && val < 0;
			}
			if("max" in this.constraints){
				var max = this.constraints.max;
				val = this.compare(val, ((typeof max != "number") || max > 0)? max : 0);
				isTooMuch = (typeof val == "number") && val > 0;
			}
			return isTooLittle || isTooMuch;
		},

		_isValidSubset: function(){
			return this.inherited(arguments) && !this._isDefinitelyOutOfRange();
		},

		isValid: function(/*Boolean*/ isFocused){
			return this.inherited(arguments) &&
				((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean
		},

		getErrorMessage: function(/*Boolean*/ isFocused){
			if(dijit.form.RangeBoundTextBox.superclass.isValid.call(this, false) && !this.isInRange(isFocused)){ return this.rangeMessage; } // String
			return this.inherited(arguments);
		},

		postMixInProperties: function(){
			this.inherited(arguments);
			if(!this.rangeMessage){
				this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
				this.rangeMessage = this.messages.rangeMessage;
			}
		},

		postCreate: function(){
			this.inherited(arguments);
			if(this.constraints.min !== undefined){
				dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min);
			}
			if(this.constraints.max !== undefined){
				dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max);
			}
		},

		setValue: function(/*Number*/ value, /*Boolean?*/ priorityChange){
			dijit.setWaiState(this.focusNode, "valuenow", value);
			this.inherited('setValue', arguments);
		}
	}
);
<|endoftext|>


<commit_before>// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide('frappe.dashboards');
frappe.provide('frappe.dashboards.chart_sources');

frappe.pages['dashboard'].on_page_load = function(wrapper) {
	var page = frappe.ui.make_app_page({
		parent: wrapper,
		title: __("Dashboard"),
		single_column: true
	});

	frappe.dashboard = new Dashboard(wrapper);
	$(wrapper).bind('show', function() {
		frappe.dashboard.show();
	});
};

class Dashboard {
	constructor(wrapper) {
		this.wrapper = $(wrapper);
		$(`

`).appendTo(this.wrapper.find(".page-content").empty());
		this.container = this.wrapper.find(".dashboard-graph");
		this.page = wrapper.page;
	}

	show() {
		this.route = frappe.get_route();
		if (this.route.length > 1) {
			// from route
			this.show_dashboard(this.route.slice(-1)[0]);
		} else {
			// last opened
			if (frappe.last_dashboard) {
				frappe.set_route('dashboard', frappe.last_dashboard);
			} else {
				// default dashboard
				frappe.db.get_list('Dashboard', {filters: {is_default: 1}}).then(data => {
					if (data && data.length) {
						frappe.set_route('dashboard', data[0].name);
					} else {
						// no default, get the latest one
						frappe.db.get_list('Dashboard', {limit: 1}).then(data => {
							if (data && data.length) {
								frappe.set_route('dashboard', data[0].name);
							} else {
								// create a new dashboard!
								frappe.new_doc('Dashboard');
							}
						});
					}
				});
			}
		}
	}

	show_dashboard(current_dashboard_name) {
		if(this.dashboard_name !== current_dashboard_name) {
			this.dashboard_name = current_dashboard_name;
			let title = this.dashboard_name;
			if (!this.dashboard_name.toLowerCase().includes(__('dashboard'))) {
				// ensure dashboard title has "dashboard"
				title = __('{0} Dashboard', [title]);
			}
			this.page.set_title(title);
			this.set_dropdown();
			this.container.empty();
			this.refresh();
		}
		this.charts = {};
		frappe.last_dashboard = current_dashboard_name;
	}

	refresh() {
		this.get_dashboard_doc().then((doc) => {
			this.dashboard_doc = doc;
			this.charts = this.dashboard_doc.charts;

			this.charts.map((chart) => {
				let chart_container = $("

");
				chart_container.appendTo(this.container);

				frappe.model.with_doc("Dashboard Chart", chart.chart).then( chart_doc => {
					let dashboard_chart = new frappe.ui.DashboardChart(chart_doc, chart_container);
					dashboard_chart.show();
				});
			});
		});
	}

	get_dashboard_doc() {
		return frappe.model.with_doc('Dashboard', this.dashboard_name);
	}

	set_dropdown() {
		this.page.clear_menu();

		this.page.add_menu_item('Edit...', () => {
			frappe.set_route('Form', 'Dashboard', frappe.dashboard.dashboard_name);
		}, 1);

		this.page.add_menu_item('New...', () => {
			frappe.new_doc('Dashboard');
		}, 1);

		frappe.db.get_list("Dashboard").then(dashboards => {
			dashboards.map(dashboard => {
				let name = dashboard.name;
				if(name != this.dashboard_name){
					this.page.add_menu_item(name, () => frappe.set_route("dashboard", name));
				}
			});
		});
	}
}

class DashboardChart {
	constructor(chart_doc, chart_container, options) {
		this.chart_doc = chart_doc;
		this.container = chart_container;
		this.options = options || {};
		this.chart_args = {};
	}

	show() {
		this.get_settings().then(() => {
			this.prepare_chart_object();
			this.prepare_container();
			this.setup_filter_button();

			if (!this.options.hide_actions || this.options.hide_actions == undefined) {
				this.prepare_chart_actions();

				if (this.chart_doc.timeseries && this.chart_doc.chart_type !== 'Custom') {
					this.render_time_series_filters();
				}
			}

			this.prepare_chart_actions();
			this.fetch(this.filters).then( data => {
				if (this.chart_doc.chart_type == 'Report') {
					data = this.get_report_chart_data(data);
				}
				if (!this.options.hide_last_sync || this.options.hide_last_sync == undefined) {
					this.update_last_synced();
				}
				this.data = data;
				this.render();
			});
		});
	}

	prepare_container() {
		const column_width_map = {
			"Half": "6",
			"Full": "12",
		};
		let columns = column_width_map[this.chart_doc.width];
		this.chart_container = $(`

${__("Loading...")}

${__("No Data")}

`);
		this.chart_container.appendTo(this.container);

		if (!this.options.hide_last_sync || this.options.hide_last_sync == undefined) {
			let last_synced_text = $(`<span class="text-muted last-synced-text"></span>`);
			last_synced_text.prependTo(this.chart_container);
		}
	}

	render_time_series_filters() {
		let filters = [
			{
				label: this.chart_doc.timespan,
				options: ['Select Date Range', 'Last Year', 'Last Quarter', 'Last Month', 'Last Week'],
				action: (selected_item) => {
					this.selected_timespan = selected_item;

					if (this.selected_timespan === 'Select Date Range') {
						this.render_date_range_fields();
					} else {
						this.selected_from_date = null;
						this.selected_to_date = null;
						if (this.date_field_wrapper) this.date_field_wrapper.hide();
						this.fetch_and_update_chart();
					}
				}
			},
			{
				label: this.chart_doc.time_interval,
				options: ['Yearly', 'Quarterly', 'Monthly', 'Weekly', 'Daily'],
				action: (selected_item) => {
					this.selected_time_interval = selected_item;
					this.fetch_and_update_chart();
				}
			},
		];

		frappe.dashboard_utils.render_chart_filters(filters, 'chart-actions', this.chart_container, 1);
	}

	fetch_and_update_chart() {
		this.args = {
			timespan: this.selected_timespan,
			time_interval: this.selected_time_interval,
			from_date: this.selected_from_date,
			to_date: this.selected_to_date
		};

		this.fetch(this.filters, true, this.args).then(data => {
			if (this.chart_doc.chart_type == 'Report') {
				data = this.get_report_chart_data(data);
			}

			this.update_chart_object();
			this.data = data;
			this.render();
		});
	}

	render_date_range_fields() {
		if (!this.date_field_wrapper || !this.date_field_wrapper.is(':visible')) {
			this.date_field_wrapper =
				$(`<div class="dashboard-date-field pull-right"></div>`)
					.insertBefore(this.chart_container.find('.chart-wrapper'));

			this.date_range_field = frappe.ui.form.make_control({
				df: {
					fieldtype: 'DateRange',
					fieldname: 'from_date',
					placeholder: 'Date Range',
					input_class: 'input-xs',
					reqd: 1,
					change: () => {
						let selected_date_range = this.date_range_field.get_value();
						this.selected_from_date = selected_date_range[0];
						this.selected_to_date = selected_date_range[1];

						if (selected_date_range && selected_date_range.length == 2) {
							this.fetch_and_update_chart();
						}
					}
				},
				parent: this.date_field_wrapper,
				render_input: 1
			});
		}
	}

	get_report_chart_data(result) {
		if (result.chart && this.chart_doc.is_custom) {
			return result.chart.data;
		} else {
			let y_fields = [];
			this.chart_doc.y_axis.map( field => {
				y_fields.push(field.y_field);
			});

			let chart_fields = {
				y_fields: y_fields,
				x_field: this.chart_doc.x_field,
				chart_type: this.chart_doc.type,
				color: this.chart_doc.color
			};
			let columns = result.columns.map((col)=> {
				return frappe.report_utils.prepare_field_from_column(col);
			});

			let data = frappe.report_utils.make_chart_options(columns, result, chart_fields).data;
			return data;
		}
	}

	prepare_chart_actions() {
		let actions = [
			{
				label: __("Refresh"),
				action: 'action-refresh',
				handler: () => {
					this.fetch_and_update_chart();
				}
			},
			{
				label: __("Edit..."),
				action: 'action-edit',
				handler: () => {
					frappe.set_route('Form', 'Dashboard Chart', this.chart_doc.name);
				}
			}
		];
		if (this.chart_doc.document_type) {
			actions.push({
				label: __("{0} List", [this.chart_doc.document_type]),
				action: 'action-list',
				handler: () => {
					frappe.set_route('List', this.chart_doc.document_type);
				}
			});
		} else if (this.chart_doc.chart_type === 'Report') {
			actions.push({
				label: __("{0} Report", [this.chart_doc.report_name]),
				action: 'action-list',
				handler: () => {
					frappe.set_route('query-report', this.chart_doc.report_name);
				}
			})
		}
		this.set_chart_actions(actions);
	}

	setup_filter_button() {

		this.is_document_type = this.chart_doc.chart_type!== 'Report' && this.chart_doc.chart_type!=='Custom';
		this.filter_button =
			$(`<div class="filter-chart btn btn-default btn-xs pull-right">${__("Filter")}</div>`);
		this.filter_button.prependTo(this.chart_container);

		this.filter_button.on('click', () => {
			let fields;

			frappe.dashboard_utils.get_filters_for_chart_type(this.chart_doc)
				.then(filters => {
					if (!this.is_document_type) {
						if (!filters) {
							fields = [{
								fieldtype: "HTML",
								options: __("No Filters Set")
							}];
						} else {
							fields = filters.filter(f => {
								if (f.on_change && !f.reqd) {
									return false;
								}
								if (f.get_query || f.get_data) {
									f.read_only = 1;
								}
								return f.fieldname;
							});
						}
					} else {
						fields = [{
							fieldtype: 'HTML',
							fieldname: 'filter_area',
						}];
					}

					this.setup_filter_dialog(fields);
				});
		});
	}

	setup_filter_dialog(fields) {

		let me = this;
		let dialog = new frappe.ui.Dialog({
			title: __(`Set Filters for ${this.chart_doc.chart_name}`),
			fields: fields,
			primary_action: function() {
				let values = this.get_values();
				if (values) {
					this.hide();
					if (me.is_document_type) {
						me.filters = me.filter_group.get_filters();
					} else {
						me.filters = values;
					}
					me.fetch_and_update_chart();
				}
			},
			primary_action_label: "Set"
		});

		if (this.is_document_type) {
			this.create_filter_group_and_add_filters(dialog.get_field('filter_area').$wrapper);
		}

		dialog.show();
		dialog.set_values(this.filters);

	}

	create_filter_group_and_add_filters(parent) {
		this.filter_group = new frappe.ui.FilterGroup({
			parent: parent,
			doctype: this.chart_doc.document_type,
			on_change: () => {},
		});

		frappe.model.with_doctype(this.chart_doc.document_type, () => {
			this.filter_group.add_filters_to_filter_group(this.filters);
		});
	}

	set_chart_actions(actions) {
		this.chart_actions = $(`

				<button class="btn btn-default btn-xs"></button>

				${actions.map(action => `<li><a data-action="${action.action}">${action.label}</a></li>`).join('')}

		`);

		this.chart_actions.find("a[data-action]").each((i, o) => {
			const action = o.dataset.action;
			$(o).click(actions.find(a => a.action === action));
		});
		this.chart_actions.prependTo(this.chart_container);
	}

	fetch(filters, refresh=false, args) {
		this.chart_container.find('.chart-loading-state').removeClass('hide');
		let method = this.settings ? this.settings.method
			: 'frappe.desk.doctype.dashboard_chart.dashboard_chart.get';

		if (this.chart_doc.chart_type == 'Report') {
			args = {
				report_name: this.chart_doc.report_name,
				filters: filters,
			};
		} else {
			args = {
				chart_name: this.chart_doc.name,
				filters: filters,
				refresh: refresh ? 1 : 0,
				time_interval: args && args.time_interval? args.time_interval: null,
				timespan: args && args.timespan? args.timespan: null,
				from_date: args && args.from_date? args.from_date: null,
				to_date: args && args.to_date? args.to_date: null,
			};
		}
		return frappe.xcall(
			method,
			args
		);
	}

	render() {
		const chart_type_map = {
			'Line': 'line',
			'Bar': 'bar',
			'Percentage': 'percentage',
			'Pie': 'pie'
		};

		let colors = [];

		if (this.chart_doc.y_axis.length) {
			this.chart_doc.y_axis.map( field => {
				colors.push(field.color);
			});
		} else if (['Line', 'Bar'].includes(this.chart_doc.type)) {
			colors = [this.chart_doc.color || "light-blue"];
		}

		this.chart_container.find('.chart-loading-state').addClass('hide');
		if (!this.data) {
			this.chart_container.find('.chart-empty-state').removeClass('hide');
		} else {
			let title = null;
			if (!this.options.hide_title || this.options.hide_title == undefined) {
				title = this.chart_doc.chart_name;
			}

			let chart_args = {
				title: title,
				data: this.data,
				type: chart_type_map[this.chart_doc.type],
				colors: colors,
				axisOptions: {
					xIsSeries: this.chart_doc.timeseries,
					shortenYAxisNumbers: 1
				}
			};
			if (!this.chart) {
				this.chart = new frappe.Chart(this.chart_container.find(".chart-wrapper")[0], chart_args);
			} else {
				this.chart.update(this.data);
			}
		}
	}

	update_last_synced() {
		let last_synced_text = __("Last synced {0}", [comment_when(this.chart_doc.last_synced_on)]);
		this.container.find(".last-synced-text").html(last_synced_text);
	}

	update_chart_object() {
		frappe.db.get_doc("Dashboard Chart", this.chart_doc.name).then(doc => {
			this.chart_doc = doc;
			this.prepare_chart_object();
			this.update_last_synced();
		});
	}

	prepare_chart_object() {
		this.filters = this.filters || JSON.parse(this.chart_doc.filters_json || '[]');
	}

	get_settings() {
		if (this.chart_doc.chart_type == 'Custom') {
			// custom source
			if (frappe.dashboards.chart_sources[this.chart_doc.source]) {
				this.settings = frappe.dashboards.chart_sources[this.chart_doc.source];
				return Promise.resolve();
			} else {
				const method = 'frappe.desk.doctype.dashboard_chart_source.dashboard_chart_source.get_config';
				return frappe.xcall(method, {name: this.chart_doc.source}).then(config => {
					frappe.dom.eval(config);
					this.settings = frappe.dashboards.chart_sources[this.chart_doc.source];
				});
			}
		} else if (this.chart_doc.chart_type == 'Report') {
			this.settings = {
				'method': 'frappe.desk.query_report.run'
			};
			return Promise.resolve();
		} else {
			return Promise.resolve();
		}
	}
}<commit_msg>refactor: remove unused code<commit_after>// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide('frappe.dashboards');
frappe.provide('frappe.dashboards.chart_sources');

frappe.pages['dashboard'].on_page_load = function(wrapper) {
	var page = frappe.ui.make_app_page({
		parent: wrapper,
		title: __("Dashboard"),
		single_column: true
	});

	frappe.dashboard = new Dashboard(wrapper);
	$(wrapper).bind('show', function() {
		frappe.dashboard.show();
	});
};

class Dashboard {
	constructor(wrapper) {
		this.wrapper = $(wrapper);
		$(`

`).appendTo(this.wrapper.find(".page-content").empty());
		this.container = this.wrapper.find(".dashboard-graph");
		this.page = wrapper.page;
	}

	show() {
		this.route = frappe.get_route();
		if (this.route.length > 1) {
			// from route
			this.show_dashboard(this.route.slice(-1)[0]);
		} else {
			// last opened
			if (frappe.last_dashboard) {
				frappe.set_route('dashboard', frappe.last_dashboard);
			} else {
				// default dashboard
				frappe.db.get_list('Dashboard', {filters: {is_default: 1}}).then(data => {
					if (data && data.length) {
						frappe.set_route('dashboard', data[0].name);
					} else {
						// no default, get the latest one
						frappe.db.get_list('Dashboard', {limit: 1}).then(data => {
							if (data && data.length) {
								frappe.set_route('dashboard', data[0].name);
							} else {
								// create a new dashboard!
								frappe.new_doc('Dashboard');
							}
						});
					}
				});
			}
		}
	}

	show_dashboard(current_dashboard_name) {
		if(this.dashboard_name !== current_dashboard_name) {
			this.dashboard_name = current_dashboard_name;
			let title = this.dashboard_name;
			if (!this.dashboard_name.toLowerCase().includes(__('dashboard'))) {
				// ensure dashboard title has "dashboard"
				title = __('{0} Dashboard', [title]);
			}
			this.page.set_title(title);
			this.set_dropdown();
			this.container.empty();
			this.refresh();
		}
		this.charts = {};
		frappe.last_dashboard = current_dashboard_name;
	}

	refresh() {
		this.get_dashboard_doc().then((doc) => {
			this.dashboard_doc = doc;
			this.charts = this.dashboard_doc.charts;

			this.charts.map((chart) => {
				let chart_container = $("

");
				chart_container.appendTo(this.container);

				frappe.model.with_doc("Dashboard Chart", chart.chart).then( chart_doc => {
					let dashboard_chart = new frappe.ui.DashboardChart(chart_doc, chart_container);
					dashboard_chart.show();
				});
			});
		});
	}

	get_dashboard_doc() {
		return frappe.model.with_doc('Dashboard', this.dashboard_name);
	}

	set_dropdown() {
		this.page.clear_menu();

		this.page.add_menu_item('Edit...', () => {
			frappe.set_route('Form', 'Dashboard', frappe.dashboard.dashboard_name);
		}, 1);

		this.page.add_menu_item('New...', () => {
			frappe.new_doc('Dashboard');
		}, 1);

		frappe.db.get_list("Dashboard").then(dashboards => {
			dashboards.map(dashboard => {
				let name = dashboard.name;
				if(name != this.dashboard_name){
					this.page.add_menu_item(name, () => frappe.set_route("dashboard", name));
				}
			});
		});
	}
}<|endoftext|>


<commit_before>enyo.kind({
    name: "EntryItem",
    kind: "Control",
    classes: "entry-item",
    style: "position: relative;",
    events: {
        onItemSelected: "",
        onItemFavorite: "",
    },
    handlers: {
        ontap: "handleTap"
    },
    components: [
        {
            name: "itemFavorite",
            caption: "",
            classes: "itemFavorite",
            // style: "position: absolute; top: 10px; right: 10px; height: 24px; width: 24px;"
            style: "height: 24px; width: 24px;"
        },
        {
            name: "itemTitle",
            caption: "",
            style: "position: absolute; top: 10px; left: 39px;"
            //fit: true
        },
        {
            name: "itemStatus",
            classes: "itemStatus",
            caption: "",
            style: "position: absolute; top: 10px; right: 39px;"
        },
        {
            name: "itemStatusColor",
            style: "position: absolute; top: 10px; right: 10px; height: 24px; width: 24px;"
        }
    ],
    // ontap: "handleTap",
    published: {
        title: "",
        data: null,
        favorite: false
    },
    titleChanged: function(inSender, inEvent) {
        this.$.itemTitle.setContent(this.getTitle());
    },
    dataChanged: function(inSender, inEvent) {
        this.$.itemStatus.setContent(this.statusTranslation(this.getData().status).text);
        this.$.itemStatusColor.setClassAttribute(this.statusTranslation(this.getData().status).colorStyle);
    },
    statusTranslation: function(inString) {
        var statuses = {
            "PLANNED WORK": {
                text: "Planned Work",
                colorStyle: "orange-status"
            },
            "GOOD SERVICE": {
                text: "Good Service",
                colorStyle: "green-status"
            },
            "SERVICE CHANGE": {
                text: "Service Change",
                colorStyle: "red-status"
            },
            "DELAYS": {
                text: "Delays",
                colorStyle: "yellow-status"
            }
        };

        return statuses[inString];
    },
    favoriteChanged: function(inSender, inEvent) {
        this.$.itemFavorite.addRemoveClass('itemFavoriteSelected', this.getFavorite());
    },
    handleTap: function(inSender, inEvent) {
        var favorite, d;
        if (inSender.name === "itemFavorite") {
            d = this.getData();
            favorite = !d.favorite;
            d.favorite = favorite;
            this.setFavorite(favorite);
            this.doItemFavorite(d);
        }
        else {
            this.doItemSelected(this.getData());
        }
    }
});
<commit_msg>Fixing Favorited Item Select Bug<commit_after>enyo.kind({
    name: "EntryItem",
    kind: "Control",
    classes: "entry-item",
    style: "position: relative;",
    events: {
        onItemSelected: "",
        onItemFavorite: "",
    },
    handlers: {
        ontap: "handleTap"
    },
    components: [
        {
            name: "itemFavorite",
            caption: "",
            classes: "itemFavorite",
            // style: "position: absolute; top: 10px; right: 10px; height: 24px; width: 24px;"
            style: "height: 24px; width: 24px;"
        },
        {
            name: "itemTitle",
            caption: "",
            style: "position: absolute; top: 10px; left: 39px;"
            //fit: true
        },
        {
            name: "itemStatus",
            classes: "itemStatus",
            caption: "",
            style: "position: absolute; top: 10px; right: 39px;"
        },
        {
            name: "itemStatusColor",
            style: "position: absolute; top: 10px; right: 10px; height: 24px; width: 24px;"
        }
    ],
    // ontap: "handleTap",
    published: {
        title: "",
        data: null,
        favorite: false
    },
    titleChanged: function(inSender, inEvent) {
        this.$.itemTitle.setContent(this.getTitle());
    },
    dataChanged: function(inSender, inEvent) {
        this.$.itemStatus.setContent(this.statusTranslation(this.getData().status).text);
        this.$.itemStatusColor.setClassAttribute(this.statusTranslation(this.getData().status).colorStyle);
    },
    statusTranslation: function(inString) {
        var statuses = {
            "PLANNED WORK": {
                text: "Planned Work",
                colorStyle: "orange-status"
            },
            "GOOD SERVICE": {
                text: "Good Service",
                colorStyle: "green-status"
            },
            "SERVICE CHANGE": {
                text: "Service Change",
                colorStyle: "red-status"
            },
            "DELAYS": {
                text: "Delays",
                colorStyle: "yellow-status"
            }
        };

        return statuses[inString];
    },
    favoriteChanged: function(inSender, inEvent) {
        this.$.itemFavorite.addRemoveClass('itemFavoriteSelected', this.getFavorite());
    },
    handleTap: function(inSender, inEvent) {
        var favorite, d;
        d = this.getData();
        d.originator = this;
        if (inSender.name === "itemFavorite") {
            favorite = !d.favorite;
            d.favorite = favorite;
            this.setFavorite(favorite);
            this.doItemFavorite(d);
        }
        else {
            this.doItemSelected(d);
        }
        delete d.originator;
    }
});
<|endoftext|>


<commit_before>if (typeof define !== 'function') {
  var define = require('amdefine')(module);
}

define([ 'duino' ], function(duino) {

  /**
   * Arduino Plugin. This plugin is able to control an Arduino that is attached to the USB port of the Raspberry PI
   *
   * @class Arduino
   * @param {Object} app The express application
   * @constructor
   */
  var Arduino = function(app) {

    this.name = 'Arduino';
    this.collection = 'Arduino';
    this.icon = 'icon-external-link';

    this.app = app;
    this.id = this.name.toLowerCase();
    this.board = new duino.Board();

    this.pins = {};
    this.pluginHelper = app.get('plugin helper');

    this.values = {};

    this.sensorList = [];
    this.sensors = {};

    this.init();

    var that = this;

    app.get('events').on('settings-saved', function() {
      that.init();
    });

    app.get('sockets').on('connection', function(socket) {
      // Arduino toggle
      socket.on('arduino-rcswitch', function(data) {
        that.rcswitch(data);
      });
      // Arduino toggle
      socket.on('arduino-irremote', function(data) {
        that.irremote(data);
      });
      // Arduino toggle
      socket.on('arduino-led', function(data) {
        that.led(data);
      });
    });

  };

  /**
   * Toggle an Arduino port
   *
   * @method rcswitch
   * @param {Object} data The websocket data from the client
   * @param {String} data.id The ID of the database entry from the RC switch to use
   * @param {String} data.value The value to set (0 or 1)
   */
  Arduino.prototype.rcswitch = function(data) {
    var that = this;
    this.pluginHelper.findItem(that.collection, data.id, function(err, item, collection) {
      if ((!err) && (item)) {
        // Inform clients over websockets
        that.app.get('sockets').emit('arduino-rcswitch', data);

        item.value = (parseInt(data.value));
        that.values[item._id] = item.value;

        // Create RC object
        if (!that.pins[item.pin]) {
          that.pins[item.pin] = new duino.RC({
            board: that.board,
            pin: parseInt(item.pin)
          });
        }

        // Send RC code
        if (item.value) {
          return that.pins[item.pin].triState(item.code + "FF0F");
        } else {
          return that.pins[item.pin].triState(item.code + "FF00");
        }
      } else {
        console.log(err);
      }
    });
  };

  /**
   * Send an IR remote code
   *
   * @method irremote
   * @param {Object} data The websocket data from the client
   * @param {String} data.id The ID of the database entry from the IR to use
   * @param {String} data.value The value to set (0 or 1)
   */
  Arduino.prototype.irremote = function(data) {

    var that = this;
    this.pluginHelper.findItem(that.collection, data.id, function(err, item, collection) {
      if ((!err) && (item)) {
        var ir = new duino.IR({
          board: that.board
        });
        ir.send(item.irtype, item.ircode, item.irlength);
      } else {
        console.log(err);
      }
    });
  };

  /**
   * Turn an LED light on
   *
   * @method led
   * @param {Object} data The websocket data from the client
   * @param {String} data.id The ID of the database entry from the LED to use
   * @param {String} data.value The value to set (0 (off) or 1 (on))
   */
  Arduino.prototype.led = function(data) {

    var that = this;
    this.pluginHelper.findItem(that.collection, data.id, function(err, item, collection) {
      if ((!err) && (item)) {
        // Inform clients over websockets
        that.app.get('sockets').emit('arduino-led', data);

        item.value = (parseInt(data.value));
        that.values[item._id] = item.value;

        // Create LED object
        if (!that.pins[item.pin]) {
          that.pins[item.pin] = new duino.Led({
            board: that.board,
            pin: parseInt(item.pin)
          });
        }

        // Change LED status
        if(item.value == "1"){
          that.pins[item.pin].on();
        }else {
          that.pins[item.pin].off();
        }
      } else {
        console.log(err);
      }
    });
  };

  /**
   * Initialize the sensors attached to the Arduino
   *
   * @method init
   */
  Arduino.prototype.init = function() {

    var that = this;
    this.sensorList.forEach(function(sensor) {
      sensor.removeAllListeners();
    });
    this.sensorList = [];

    this.sensors = {};
    return this.app.get('db').collection(that.collection, function(err, collection) {
      collection.find({
        method: 'sensor'
      }).toArray(function(err, result) {
        if ((!err) && (result.length > 0)) {
          result.forEach(function(item) {
            that.sensors[item._id] = item;
            var sensor = new duino.Sensor({
              board: that.board,
              pin: item.pin,
              throttle: 500
            });
            sensor._id = item._id;
            sensor.on('read', function(err, value) {
              item = that.sensors[this._id + ''];
              if (isNaN(item.value)) {
                item.value = 0;
              }
              var val = parseFloat(eval(item.formula.replace('x', +value)));
              item.value = parseFloat(((item.value + val) / 2).toFixed(2));
              that.values[item._id] = item.value;
              that.app.get('sockets').emit('arduino-sensor', {
                id: item._id,
                value: item.value
              });
            });
            that.sensorList.push(sensor);
          });
        }
      });
    });
  };

  /**
   * Manipulate the items array before render
   *
   * @method beforeRender
   * @param {Array} items An array containing the items to be rendered
   * @param {Function} callback The callback method to execute after manipulation
   * @param {String} callback.err null if no error occured, otherwise the error
   * @param {Object} callback.result The manipulated items
   */
  Arduino.prototype.beforeRender = function(items, callback) {
    var that = this;
    items.forEach(function(item) {
      item.value = that.values[item._id] ? that.values[item._id] : 0;
    });
    return callback(null, items);
  }

  /**
   * API functions of the Arduino Plugin
   *
   * @method api
   * @param {Object} req The request
   * @param {Object} res The response
   */

  Arduino.prototype.api = function(req, res, next) {
    /*
     * GET
     */
    if (req.method == 'POST') {
      var that = this;
      var method = req.body.method;
      if(method === "rcswitch") {
        this.app.get('db').collection("Arduino", function(err, collection) {
            collection.find({}).toArray(function(err, items) {
              if (!err) {
              that.beforeRender(items, function() {
                res.send(200, items);
                });
              } else {
              res.send(500, '[]');
              }
              });
            });
      } else {
        next();
      }
    }
  };

  var exports = Arduino;

  return exports;

});
<commit_msg>format plugins/arduino/index.js properly<commit_after>if (typeof define !== 'function') {
  var define = require('amdefine')(module);
}

define([ 'duino' ], function(duino) {

  /**
   * Arduino Plugin. This plugin is able to control an Arduino that is attached to the USB port of the Raspberry PI
   *
   * @class Arduino
   * @param {Object} app The express application
   * @constructor
   */
  var Arduino = function(app) {

    this.name = 'Arduino';
    this.collection = 'Arduino';
    this.icon = 'icon-external-link';

    this.app = app;
    this.id = this.name.toLowerCase();
    this.board = new duino.Board();

    this.pins = {};
    this.pluginHelper = app.get('plugin helper');

    this.values = {};

    this.sensorList = [];
    this.sensors = {};

    this.init();

    var that = this;

    app.get('events').on('settings-saved', function() {
      that.init();
    });

    app.get('sockets').on('connection', function(socket) {
      // Arduino toggle
      socket.on('arduino-rcswitch', function(data) {
        that.rcswitch(data);
      });
      // Arduino toggle
      socket.on('arduino-irremote', function(data) {
        that.irremote(data);
      });
      // Arduino toggle
      socket.on('arduino-led', function(data) {
        that.led(data);
      });
    });

  };

  /**
   * Toggle an Arduino port
   *
   * @method rcswitch
   * @param {Object} data The websocket data from the client
   * @param {String} data.id The ID of the database entry from the RC switch to use
   * @param {String} data.value The value to set (0 or 1)
   */
  Arduino.prototype.rcswitch = function(data) {
    var that = this;
    this.pluginHelper.findItem(that.collection, data.id, function(err, item, collection) {
      if ((!err) && (item)) {
        // Inform clients over websockets
        that.app.get('sockets').emit('arduino-rcswitch', data);

        item.value = (parseInt(data.value));
        that.values[item._id] = item.value;

        // Create RC object
        if (!that.pins[item.pin]) {
          that.pins[item.pin] = new duino.RC({
            board: that.board,
            pin: parseInt(item.pin)
          });
        }

        // Send RC code
        if (item.value) {
          return that.pins[item.pin].triState(item.code + "FF0F");
        } else {
          return that.pins[item.pin].triState(item.code + "FF00");
        }
      } else {
        console.log(err);
      }
    });
  };

  /**
   * Send an IR remote code
   *
   * @method irremote
   * @param {Object} data The websocket data from the client
   * @param {String} data.id The ID of the database entry from the IR to use
   * @param {String} data.value The value to set (0 or 1)
   */
  Arduino.prototype.irremote = function(data) {

    var that = this;
    this.pluginHelper.findItem(that.collection, data.id, function(err, item, collection) {
      if ((!err) && (item)) {
        var ir = new duino.IR({
          board: that.board
        });
        ir.send(item.irtype, item.ircode, item.irlength);
      } else {
        console.log(err);
      }
    });
  };

  /**
   * Turn an LED light on
   *
   * @method led
   * @param {Object} data The websocket data from the client
   * @param {String} data.id The ID of the database entry from the LED to use
   * @param {String} data.value The value to set (0 (off) or 1 (on))
   */
  Arduino.prototype.led = function(data) {

    var that = this;
    this.pluginHelper.findItem(that.collection, data.id, function(err, item, collection) {
      if ((!err) && (item)) {
        // Inform clients over websockets
        that.app.get('sockets').emit('arduino-led', data);

        item.value = (parseInt(data.value));
        that.values[item._id] = item.value;

        // Create LED object
        if (!that.pins[item.pin]) {
          that.pins[item.pin] = new duino.Led({
            board: that.board,
            pin: parseInt(item.pin)
          });
        }

        // Change LED status
        if(item.value == "1"){
          that.pins[item.pin].on();
        }else {
          that.pins[item.pin].off();
        }
      } else {
        console.log(err);
      }
    });
  };

  /**
   * Initialize the sensors attached to the Arduino
   *
   * @method init
   */
  Arduino.prototype.init = function() {

    var that = this;
    this.sensorList.forEach(function(sensor) {
      sensor.removeAllListeners();
    });
    this.sensorList = [];

    this.sensors = {};
    return this.app.get('db').collection(that.collection, function(err, collection) {
      collection.find({
        method: 'sensor'
      }).toArray(function(err, result) {
        if ((!err) && (result.length > 0)) {
          result.forEach(function(item) {
            that.sensors[item._id] = item;
            var sensor = new duino.Sensor({
              board: that.board,
              pin: item.pin,
              throttle: 500
            });
            sensor._id = item._id;
            sensor.on('read', function(err, value) {
              item = that.sensors[this._id + ''];
              if (isNaN(item.value)) {
                item.value = 0;
              }
              var val = parseFloat(eval(item.formula.replace('x', +value)));
              item.value = parseFloat(((item.value + val) / 2).toFixed(2));
              that.values[item._id] = item.value;
              that.app.get('sockets').emit('arduino-sensor', {
                id: item._id,
                value: item.value
              });
            });
            that.sensorList.push(sensor);
          });
        }
      });
    });
  };

  /**
   * Manipulate the items array before render
   *
   * @method beforeRender
   * @param {Array} items An array containing the items to be rendered
   * @param {Function} callback The callback method to execute after manipulation
   * @param {String} callback.err null if no error occured, otherwise the error
   * @param {Object} callback.result The manipulated items
   */
  Arduino.prototype.beforeRender = function(items, callback) {
    var that = this;
    items.forEach(function(item) {
      item.value = that.values[item._id] ? that.values[item._id] : 0;
    });
    return callback(null, items);
  }

  /**
   * API functions of the Arduino Plugin
   *
   * @method api
   * @param {Object} req The request
   * @param {Object} res The response
   */

  Arduino.prototype.api = function(req, res, next) {
    /*
     * GET
     */
    if (req.method == 'POST') {
      var that = this;
      var method = req.body.method;
      if(method === "rcswitch") {
        this.app.get('db').collection("Arduino", function(err, collection) {
            collection.find({}).toArray(function(err, items) {
              if (!err) {
                that.beforeRender(items, function() {
                  res.send(200, items);
                });
              } else {
               res.send(500, '[]');
              }
            });
        });
      } else if(method === "sensors") {
        this.app.get('db').collection("Arduino", function(err, collection) {
            collection.find({}).toArray(function(err, items) {
              if (!err) {
                that.beforeRender(items, function() {
                  res.send(200, items);
                });
              } else {
               res.send(500, '[]');
              }
            });
        });
      } else {
        next();
      }
        next();
      }
    }
  };

  var exports = Arduino;

  return exports;

});
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

Network.RequestPreviewView = class extends Network.RequestResponseView {
  /**
   * @param {!SDK.NetworkRequest} request
   */
  constructor(request) {
    super(request);
  }

  /**
   * @override
   * @protected
   * @return {!Promise<!UI.Widget>}
   */
  async showPreview() {
    const view = await super.showPreview();
    if (!(view instanceof UI.SimpleView))
      return view;
    const toolbar = new UI.Toolbar('network-item-preview-toolbar', this.element);
    for (const item of view.syncToolbarItems())
      toolbar.appendToolbarItem(item);
    return view;
  }

  /**
   * @return {!Promise<?UI.Widget>}
   */
  async _htmlPreview() {
    const contentData = await this.request.contentData();
    if (contentData.error)
      return new UI.EmptyWidget(Common.UIString('Failed to load response data'));

    const whitelist = new Set(['text/html', 'text/plain', 'application/xhtml+xml']);
    if (!whitelist.has(this.request.mimeType))
      return null;

    // http://crbug.com/767393 - DevTools should recognize JSON regardless of the content type
    const jsonView = await SourceFrame.JSONView.createView(contentData.content);
    if (jsonView)
      return jsonView;

    const dataURL = Common.ContentProvider.contentAsDataURL(
        contentData.content, this.request.mimeType, contentData.encoded, contentData.encoded ? 'utf-8' : null);
    return dataURL ? new Network.RequestHTMLView(dataURL) : null;
  }

  /**
   * @override
   * @protected
   * @return {!Promise<!UI.Widget>}
   */
  async createPreview() {
    if (this.request.signedExchangeInfo())
      return new Network.SignedExchangeInfoView(this.request);

    const htmlErrorPreview = await this._htmlPreview();
    if (htmlErrorPreview)
      return htmlErrorPreview;

    // Try provider before the source view - so JSON and XML are not shown in generic editor
    const provided = await SourceFrame.PreviewFactory.createPreview(this.request, this.request.mimeType);
    if (provided)
      return provided;

    const sourceView = await Network.RequestResponseView.sourceViewForRequest(this.request);
    if (sourceView)
      return sourceView;
    return new UI.EmptyWidget(Common.UIString('Preview not available'));
  }
};
<commit_msg>Remove preview fallback to source view<commit_after>/*
 * Copyright (C) 2011 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

Network.RequestPreviewView = class extends Network.RequestResponseView {
  /**
   * @param {!SDK.NetworkRequest} request
   */
  constructor(request) {
    super(request);
  }

  /**
   * @override
   * @protected
   * @return {!Promise<!UI.Widget>}
   */
  async showPreview() {
    const view = await super.showPreview();
    if (!(view instanceof UI.SimpleView))
      return view;
    const toolbar = new UI.Toolbar('network-item-preview-toolbar', this.element);
    for (const item of view.syncToolbarItems())
      toolbar.appendToolbarItem(item);
    return view;
  }

  /**
   * @return {!Promise<?UI.Widget>}
   */
  async _htmlPreview() {
    const contentData = await this.request.contentData();
    if (contentData.error)
      return new UI.EmptyWidget(Common.UIString('Failed to load response data'));

    const whitelist = new Set(['text/html', 'text/plain', 'application/xhtml+xml']);
    if (!whitelist.has(this.request.mimeType))
      return null;

    // http://crbug.com/767393 - DevTools should recognize JSON regardless of the content type
    const jsonView = await SourceFrame.JSONView.createView(contentData.content);
    if (jsonView)
      return jsonView;

    const dataURL = Common.ContentProvider.contentAsDataURL(
        contentData.content, this.request.mimeType, contentData.encoded, contentData.encoded ? 'utf-8' : null);
    return dataURL ? new Network.RequestHTMLView(dataURL) : null;
  }

  /**
   * @override
   * @protected
   * @return {!Promise<!UI.Widget>}
   */
  async createPreview() {
    if (this.request.signedExchangeInfo())
      return new Network.SignedExchangeInfoView(this.request);

    const htmlErrorPreview = await this._htmlPreview();
    if (htmlErrorPreview)
      return htmlErrorPreview;

    const provided = await SourceFrame.PreviewFactory.createPreview(this.request, this.request.mimeType);
    if (provided)
      return provided;

    return new UI.EmptyWidget(Common.UIString('Preview not available'));
  }
};
<|endoftext|>


<commit_before>var stream = require('stream');
var util = require('util');
var Tile = require('./stream-util').Tile;
var StringDecoder = require('string_decoder').StringDecoder;

module.exports = Deserialize;
util.inherits(Deserialize, stream.Transform);

function Deserialize() {
    stream.Transform.call(this);
    this._writableState.objectMode = false;
    this._readableState.objectMode = true;
    this._buffer = '';
    this._decoder = new StringDecoder('utf8');
}

Deserialize.prototype._transform = function(chunk, enc, callback) {
    var push = this.push,
        emit = this.emit;

    this._buffer += this._decoder.write(chunk);
    var lines = this._buffer.split(/\r?\n/);
    this._buffer = lines.pop();

    var tile, line;
    for (var i = 0; i < lines.length; i++) {
        tile = new Tile();
        line = lines[i];
        try { tile.deserialize(line); }
        catch (err) { return this.emit('error', err); }
        this.push(tile);
    }
    // lines.forEach(function(line) {
    //     var tile = new Tile();
    //     try { tile.deserialize(line); }
    //     catch (err) { return emit('error', err); }
    //     console.log(tile);
    //     push(tile);
    // });

    callback();
};

Deserialize.prototype._flush = function(callback) {
    var leftover = this._buffer.trim();
    if (leftover) {
        var tile = new Tile();
        try { tile.deserialize(leftover); }
        catch (err) { return this.emit('error', err); }
        this.push(tile);
    }
};
<commit_msg>remove comments<commit_after>var stream = require('stream');
var util = require('util');
var Tile = require('./stream-util').Tile;
var StringDecoder = require('string_decoder').StringDecoder;

module.exports = Deserialize;
util.inherits(Deserialize, stream.Transform);

function Deserialize() {
    stream.Transform.call(this);
    this._writableState.objectMode = false;
    this._readableState.objectMode = true;
    this._buffer = '';
    this._decoder = new StringDecoder('utf8');
}

Deserialize.prototype._transform = function(chunk, enc, callback) {
    var push = this.push,
        emit = this.emit;

    this._buffer += this._decoder.write(chunk);
    var lines = this._buffer.split(/\r?\n/);
    this._buffer = lines.pop();

    var tile, line;
    for (var i = 0; i < lines.length; i++) {
        tile = new Tile();
        line = lines[i];
        try { tile.deserialize(line); }
        catch (err) { return this.emit('error', err); }
        this.push(tile);
    }

    callback();
};

Deserialize.prototype._flush = function(callback) {
    var leftover = this._buffer.trim();
    if (leftover) {
        var tile = new Tile();
        try { tile.deserialize(leftover); }
        catch (err) { return this.emit('error', err); }
        this.push(tile);
    }
};
<|endoftext|>


<commit_before>// @flow
import { HKT } from './HKT'
import type { Setoid } from './Setoid'
import type { Ord } from './Ord'
import type { Semigroup } from './Semigroup'
import type { Monoid } from './Monoid'
import type { Applicative } from './Applicative'
import type { Monad } from './Monad'
import type { Foldable } from './Foldable'
import type { Traversable } from './Traversable'
import type { Alt } from './Alt'
import type { Extend } from './Extend'
import type { Comonad } from './Comonad'

class Identity {}

// keep private
function prj<A>(fa: HKT<Identity, A>): A {
  return ((fa: any): A)
}

// keep private
function inj<A>(a: A): HKT<Identity, A> {
  return ((a: any): HKT<Identity, A>)
}

export function map<A, B>(f: (a: A) => B, fa: HKT<Identity, A>): HKT<Identity, B> {
  return inj(f(prj(fa)))
}

export function ap<A, B>(fab: HKT<Identity, (a: A) => B>, fa: HKT<Identity, A>): HKT<Identity, B> {
  return map(prj(fab), fa)
}

export const of = inj

export function chain<A, B>(f: (a: A) => HKT<Identity, B>, fa: HKT<Identity, A>): HKT<Identity, B> {
  return f(prj(fa))
}

export function reduce<A, B>(f: (a: A, b: B) => A, a: A, fb: HKT<Identity, B>): A {
  return f(a, prj(fb))
}

export function alt<A>(fx: HKT<Identity, A>, fy: HKT<Identity, A>): HKT<Identity, A> { // eslint-disable-line no-unused-vars
  return fx
}

export function sequence<F, A>(dictApplicative: Applicative<F>, tfa: HKT<Identity, HKT<F, A>>): HKT<F, HKT<Identity, A>> {
  return dictApplicative.map(of, prj(tfa))
}

export function extend<A, B>(f: (ea: HKT<Identity, A>) => B, ea: HKT<Identity, A>): HKT<Identity, B> {
  return of(f(ea))
}

export const extract = prj

export function getSetoid<A>(dictSetoid: Setoid<A>): Setoid<HKT<Identity, A>> {
  return {
    equals(fx, fy) {
      return dictSetoid.equals(prj(fx), prj(fy))
    }
  }
}

export function getOrd<A>(dictOrd: Ord<A>): Ord<HKT<Identity, A>> {
  return {
    equals: getSetoid(dictOrd).equals,
    compare(fx, fy) {
      return dictOrd.compare(prj(fx), prj(fy))
    }
  }
}

export function getSemigroup<A>(dictSemigroup: Semigroup<A>): Semigroup<HKT<Identity, A>> {
  return {
    concat(fx, fy) {
      return inj(dictSemigroup.concat(prj(fx), prj(fy)))
    }
  }
}

export function getMonoid<A>(dicMonoid: Monoid<A>): Monoid<HKT<Identity, A>> {
  return {
    concat: getSemigroup(dicMonoid).concat,
    empty() {
      return inj(dicMonoid.empty())
    }
  }
}

if (false) { // eslint-disable-line
  ({
    map,
    ap,
    of,
    chain,
    reduce,
    alt,
    sequence,
    extend,
    extract
  }: Monad<Identity> & Foldable<Identity> & Traversable<Identity> & Alt<Identity> & Extend<Identity> & Comonad<Identity>)
}
<commit_msg>add id function<commit_after>// @flow
import { HKT } from './HKT'
import type { Setoid } from './Setoid'
import type { Ord } from './Ord'
import type { Semigroup } from './Semigroup'
import type { Monoid } from './Monoid'
import type { Applicative } from './Applicative'
import type { Monad } from './Monad'
import type { Foldable } from './Foldable'
import type { Traversable } from './Traversable'
import type { Alt } from './Alt'
import type { Extend } from './Extend'
import type { Comonad } from './Comonad'

class Identity {}

// keep private
function prj<A>(fa: HKT<Identity, A>): A {
  return ((fa: any): A)
}

// keep private
function inj<A>(a: A): HKT<Identity, A> {
  return ((a: any): HKT<Identity, A>)
}

export function id<A>(a: A): A {
  return a
}

export function map<A, B>(f: (a: A) => B, fa: HKT<Identity, A>): HKT<Identity, B> {
  return inj(f(prj(fa)))
}

export function ap<A, B>(fab: HKT<Identity, (a: A) => B>, fa: HKT<Identity, A>): HKT<Identity, B> {
  return map(prj(fab), fa)
}

export const of = inj

export function chain<A, B>(f: (a: A) => HKT<Identity, B>, fa: HKT<Identity, A>): HKT<Identity, B> {
  return f(prj(fa))
}

export function reduce<A, B>(f: (a: A, b: B) => A, a: A, fb: HKT<Identity, B>): A {
  return f(a, prj(fb))
}

export function alt<A>(fx: HKT<Identity, A>, fy: HKT<Identity, A>): HKT<Identity, A> { // eslint-disable-line no-unused-vars
  return fx
}

export function sequence<F, A>(dictApplicative: Applicative<F>, tfa: HKT<Identity, HKT<F, A>>): HKT<F, HKT<Identity, A>> {
  return dictApplicative.map(of, prj(tfa))
}

export function extend<A, B>(f: (ea: HKT<Identity, A>) => B, ea: HKT<Identity, A>): HKT<Identity, B> {
  return of(f(ea))
}

export const extract = prj

export function getSetoid<A>(dictSetoid: Setoid<A>): Setoid<HKT<Identity, A>> {
  return {
    equals(fx, fy) {
      return dictSetoid.equals(prj(fx), prj(fy))
    }
  }
}

export function getOrd<A>(dictOrd: Ord<A>): Ord<HKT<Identity, A>> {
  return {
    equals: getSetoid(dictOrd).equals,
    compare(fx, fy) {
      return dictOrd.compare(prj(fx), prj(fy))
    }
  }
}

export function getSemigroup<A>(dictSemigroup: Semigroup<A>): Semigroup<HKT<Identity, A>> {
  return {
    concat(fx, fy) {
      return inj(dictSemigroup.concat(prj(fx), prj(fy)))
    }
  }
}

export function getMonoid<A>(dicMonoid: Monoid<A>): Monoid<HKT<Identity, A>> {
  return {
    concat: getSemigroup(dicMonoid).concat,
    empty() {
      return inj(dicMonoid.empty())
    }
  }
}

if (false) { // eslint-disable-line
  ({
    map,
    ap,
    of,
    chain,
    reduce,
    alt,
    sequence,
    extend,
    extract
  }: Monad<Identity> & Foldable<Identity> & Traversable<Identity> & Alt<Identity> & Extend<Identity> & Comonad<Identity>)
}
<|endoftext|>