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

	/*
	 * grunt 								: Run default development server
	 * grunt cluster 						: Run default development cluster server with 2 nodes
	 * grunt cluster --nodes=N 				: Run default development cluster server with N nodes
	 * grunt minijs							: Minify client js files
	 * grunt less							: Compile less sources to css
	 * grunt mini							: Minify js files and compile less
	 * grunt jshint							: Analyze js files to check style and best practices
	 * grunt prod							: Run server in production mode
	 *
	 *  The server reloads if you change something from server side.
	 *  The minified and css are compiled when client side is changed.
	 *
	*/
	var generateOptions = function(arg) {
		return {
            args: arg,
            watch: ['log.js','proxy.js','server.js','webrtc.io.js','app'],
            debug: true,
            delayTime: 1,
            cwd: __dirname
        };
	};

	var checkMongoOff = function() {
	    var mongodb = grunt.option('mongodb') || 'on';
	    // Do not run mongo in openshift environment
	    if (process.env.OPENSHIFT_NODEJS_PORT || mongodb==='off') {
	    	grunt.config.data.concurrent.prod.tasks.splice(0,1);
	    	grunt.config.data.concurrent.default.tasks.splice(0,1);
	    	grunt.config.data.concurrent.cluster.tasks.splice(0,1);
	    }
	};

    // Project Configuration
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        watch: {
            jade: {
                files: ['app/views/**'],
                tasks: ['jshint'],
                options: {
                    livereload: true,
                },
            },
            js: {
                files: ['client/js/**/*js'],
                tasks: ['minicheck'],
                options: {
                    livereload: true,
                },
            },
            html: {
                files: ['public/views/**'],
                options: {
                    livereload: true,
                },
            },
            css: {
                files: ['client/less/**'],
                tasks: ['less'],
                options: {
                    livereload: true
                }
            }
        },
        jshint: {
            all: {
            	files: {
            		src: ['*.js',
            		      'app/**/*.js',
            		      'spec/**/*.js',
            		      'client/js/**/*.js',
            		      '!client/js/**/*min.js'
            		      ]
            	},
            	options: {
            		jshintrc: '.jshintrc'
            	}
            }
        },
        shell: {
        	mongo: {
        		command: 'mongod --dbpath ./data',
        		options: {
        			async: true,
        			stdout: false,
        			stderr: true
        		}
        	}
        },
        nodemon: {
            prod: {
            	script: 'server.js',
            	options: {
            		watch: 'nothing.js'
            	}
            },
            dev: {
            	script: 'server.js'
            },
        	proxy: {
	        	script: 'proxy.js'
	        }
        },
        concurrent: {
        	default: {
	            tasks: ['shell','nodemon:dev', 'watch'],
	            options: {
	                logConcurrentOutput: true
	            }
        	},
        	prod: {
	            tasks: ['shell','nodemon:prod'],
	            options: {
	                logConcurrentOutput: true
	            }
        	},
        	cluster: {
	            tasks: ['shell', 'nodemon:proxy', 'watch'],
	            options: {
	                logConcurrentOutput: true
	            }
        	},
        	test: {
	            tasks: ['shell', 'jasmine_node'],
	            options: {
	                logConcurrentOutput: true
	            }
        	},
        	'travis_test': {
	            tasks: ['jasmine_node'],
	            options: {
	                logConcurrentOutput: true
	            }
        	}
        },
        uglify: {
        	options: {
        	   mangle: false,
        	   sourceMap: true,
        	   compress: false
        	},
        	build: {
        		files: {
        			'public/js/loowid.min.js':[
						'client/js/angular/angular.min.js',
						'client/js/angular/angular-*.js',
						'client/js/angular/ui-*.js',
						'client/js/angular/ng-*.js',
						'client/js/*.js',
						'client/js/services/*.js',
						'client/js/controllers/*.js'
					]
        		}
        	}
        },
        less: {
        	prod: {
        		options: {
        			paths:['client/less'],
        			cleancss:true
        		},
            	files: {
            		'public/css/loowid.min.css':'client/less/loowid.less'
            	}
        	}
        },
        'jasmine_node': {
            options: {
              forceExit: true,
              matchall: false,
              extensions: 'js',
              specNameMatcher: 'spec',
              junitreport: {
                report: true,
                savePath : './reports/',
                useDotNotation: true,
                consolidate: true
              }
            },
            coverage: {
                options : {
                    failTask: false,
                    branches : 100 ,
                    functions: 100,
                    statements:100,
                    lines:100
                }
            },
            all: ['spec/']
        }
    });

    //Load NPM tasks
    grunt.loadNpmTasks('grunt-shell');
    grunt.loadNpmTasks('grunt-nodemon');
    grunt.loadNpmTasks('grunt-concurrent');

    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-jasmine-node');
    grunt.loadNpmTasks('grunt-jasmine-node-istanbul');

    //Making grunt default to force in order not to break the project.
    grunt.option('force', true);

    //Default task(s).
    grunt.registerTask('default', ['minicheck','concurrent:default']);

    checkMongoOff();

    if (!process.env.OPENSHIFT_NODEJS_PORT) {
        process.env.LOOWID_HTTP_PORT = grunt.option('port') || 80;
        process.env.LOOWID_HTTPS_PORT = grunt.option('sport') || 443;
        process.env.LOOWID_BASE_PORT = grunt.option('bport') || 8000;
    }

    //Cluster local configuration N nodes BasePort + 1, BasePort + 2,...
    // grunt cluster --nodes=N
    var nodes = grunt.option('nodes') || 2;
    // Create nodemon tasks for cluster
    for (var k=0; k<nodes; k+=1) {
    	grunt.config.data.nodemon['dev'+k] = { script: 'server.js', options: generateOptions([Number(process.env.LOOWID_BASE_PORT)+k+1]) };
    	grunt.config.data.concurrent.cluster.tasks.push('nodemon:dev'+k);
    }

    // Setting number of cluster nodes
    grunt.config.data.nodemon.proxy.options = generateOptions([nodes]);
    grunt.config.data.nodemon.dev.options = generateOptions([]);
    grunt.registerTask('cluster', ['minicheck','concurrent:cluster']);

    grunt.registerTask('prod', ['mini','concurrent:prod']);

    // Minify tasks (generate min files)
    grunt.registerTask('minijs', ['uglify']);

    // Minify tasks (generate min files)
    grunt.registerTask('mini', ['minijs','less']);
    grunt.registerTask('minicheck', ['jshint','minijs','less']);

    // Run tests
    grunt.registerTask('test', ['jshint','concurrent:test']);

    grunt.registerTask('travis_test', ['jshint','concurrent:travis_test']);

};
<commit_msg>Avoid error if mongo data directory does not exist.<commit_after>'use strict';
module.exports = function(grunt) {

	/*
	 * grunt 								: Run default development server
	 * grunt cluster 						: Run default development cluster server with 2 nodes
	 * grunt cluster --nodes=N 				: Run default development cluster server with N nodes
	 * grunt minijs							: Minify client js files
	 * grunt less							: Compile less sources to css
	 * grunt mini							: Minify js files and compile less
	 * grunt jshint							: Analyze js files to check style and best practices
	 * grunt prod							: Run server in production mode
	 *
	 *  The server reloads if you change something from server side.
	 *  The minified and css are compiled when client side is changed.
	 *
	*/
	var generateOptions = function(arg) {
		return {
            args: arg,
            watch: ['log.js','proxy.js','server.js','webrtc.io.js','app'],
            debug: true,
            delayTime: 1,
            cwd: __dirname
        };
	};

	var checkMongoOff = function() {
	    var mongodb = grunt.option('mongodb') || 'on';
	    // Do not run mongo in openshift environment
	    if (process.env.OPENSHIFT_NODEJS_PORT || mongodb==='off') {
	    	grunt.config.data.concurrent.prod.tasks.splice(0,1);
	    	grunt.config.data.concurrent.default.tasks.splice(0,1);
	    	grunt.config.data.concurrent.cluster.tasks.splice(0,1);
	    }
	};

    // Project Configuration
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        watch: {
            jade: {
                files: ['app/views/**'],
                tasks: ['jshint'],
                options: {
                    livereload: true,
                },
            },
            js: {
                files: ['client/js/**/*js'],
                tasks: ['minicheck'],
                options: {
                    livereload: true,
                },
            },
            html: {
                files: ['public/views/**'],
                options: {
                    livereload: true,
                },
            },
            css: {
                files: ['client/less/**'],
                tasks: ['less'],
                options: {
                    livereload: true
                }
            }
        },
        jshint: {
            all: {
            	files: {
            		src: ['*.js',
            		      'app/**/*.js',
            		      'spec/**/*.js',
            		      'client/js/**/*.js',
            		      '!client/js/**/*min.js'
            		      ]
            	},
            	options: {
            		jshintrc: '.jshintrc'
            	}
            }
        },
        shell: {
        	mongo: {
        		command: 'mkdir -p ./data;mongod --dbpath ./data',
        		options: {
        			async: true,
        			stdout: false,
        			stderr: true
        		}
        	}
        },
        nodemon: {
            prod: {
            	script: 'server.js',
            	options: {
            		watch: 'nothing.js'
            	}
            },
            dev: {
            	script: 'server.js'
            },
        	proxy: {
	        	script: 'proxy.js'
	        }
        },
        concurrent: {
        	default: {
	            tasks: ['shell','nodemon:dev', 'watch'],
	            options: {
	                logConcurrentOutput: true
	            }
        	},
        	prod: {
	            tasks: ['shell','nodemon:prod'],
	            options: {
	                logConcurrentOutput: true
	            }
        	},
        	cluster: {
	            tasks: ['shell', 'nodemon:proxy', 'watch'],
	            options: {
	                logConcurrentOutput: true
	            }
        	},
        	test: {
	            tasks: ['shell', 'jasmine_node'],
	            options: {
	                logConcurrentOutput: true
	            }
        	},
        	'travis_test': {
	            tasks: ['jasmine_node'],
	            options: {
	                logConcurrentOutput: true
	            }
        	}
        },
        uglify: {
        	options: {
        	   mangle: false,
        	   sourceMap: true,
        	   compress: false
        	},
        	build: {
        		files: {
        			'public/js/loowid.min.js':[
						'client/js/angular/angular.min.js',
						'client/js/angular/angular-*.js',
						'client/js/angular/ui-*.js',
						'client/js/angular/ng-*.js',
						'client/js/*.js',
						'client/js/services/*.js',
						'client/js/controllers/*.js'
					]
        		}
        	}
        },
        less: {
        	prod: {
        		options: {
        			paths:['client/less'],
        			cleancss:true
        		},
            	files: {
            		'public/css/loowid.min.css':'client/less/loowid.less'
            	}
        	}
        },
        'jasmine_node': {
            options: {
              forceExit: true,
              matchall: false,
              extensions: 'js',
              specNameMatcher: 'spec',
              junitreport: {
                report: true,
                savePath : './reports/',
                useDotNotation: true,
                consolidate: true
              }
            },
            coverage: {
                options : {
                    failTask: false,
                    branches : 100 ,
                    functions: 100,
                    statements:100,
                    lines:100
                }
            },
            all: ['spec/']
        }
    });

    //Load NPM tasks
    grunt.loadNpmTasks('grunt-shell');
    grunt.loadNpmTasks('grunt-nodemon');
    grunt.loadNpmTasks('grunt-concurrent');

    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-jasmine-node');
    grunt.loadNpmTasks('grunt-jasmine-node-istanbul');

    //Making grunt default to force in order not to break the project.
    grunt.option('force', true);

    //Default task(s).
    grunt.registerTask('default', ['minicheck','concurrent:default']);

    checkMongoOff();

    if (!process.env.OPENSHIFT_NODEJS_PORT) {
        process.env.LOOWID_HTTP_PORT = grunt.option('port') || 80;
        process.env.LOOWID_HTTPS_PORT = grunt.option('sport') || 443;
        process.env.LOOWID_BASE_PORT = grunt.option('bport') || 8000;
    }

    //Cluster local configuration N nodes BasePort + 1, BasePort + 2,...
    // grunt cluster --nodes=N
    var nodes = grunt.option('nodes') || 2;
    // Create nodemon tasks for cluster
    for (var k=0; k<nodes; k+=1) {
    	grunt.config.data.nodemon['dev'+k] = { script: 'server.js', options: generateOptions([Number(process.env.LOOWID_BASE_PORT)+k+1]) };
    	grunt.config.data.concurrent.cluster.tasks.push('nodemon:dev'+k);
    }

    // Setting number of cluster nodes
    grunt.config.data.nodemon.proxy.options = generateOptions([nodes]);
    grunt.config.data.nodemon.dev.options = generateOptions([]);
    grunt.registerTask('cluster', ['minicheck','concurrent:cluster']);

    grunt.registerTask('prod', ['mini','concurrent:prod']);

    // Minify tasks (generate min files)
    grunt.registerTask('minijs', ['uglify']);

    // Minify tasks (generate min files)
    grunt.registerTask('mini', ['minijs','less']);
    grunt.registerTask('minicheck', ['jshint','minijs','less']);

    // Run tests
    grunt.registerTask('test', ['jshint','concurrent:test']);

    grunt.registerTask('travis_test', ['jshint','concurrent:travis_test']);

};
<|endoftext|>


<commit_before>var gulp = require("gulp");
var git = require("gulp-git");
var path = require("path");
var fs = require("fs");

var exec = require("child_process").exec;

gulp.task("installPuppeteerInCI", function (done) {
    exec("sudo yarn add puppeteer", { maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
        if (err) {
            throw err;
        }

        done();
    });
});

gulp.task("installPuppeteer", function (done) {
    console.log("sudo cp -rf ../Wonder-Benchmark/node_modules/puppeteer ./node_modules/puppeteer");

    exec("sudo cp -rf ../Wonder-Benchmark/node_modules/puppeteer ./node_modules/puppeteer", { maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
        if (err) {
            throw err;
        }

        console.log("sudo npm install in node_modules/puppeteer");

        exec("sudo npm install", { cwd: path.join(process.cwd(), "node_modules/puppeteer"), maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
            if (err) {
                throw err;
            }

            done();
        });
    });
});

// gulp.task("install", function (done) {
//     var packageFilePath = path.join(process.cwd(), "./package.json");

//     console.log("remove puppeteer in package.json...");

//     var packageContent = JSON.parse(fs.readFileSync(packageFilePath));

//     delete packageContent.devDependencies.puppeteer;

//     fs.writeFileSync(packageFilePath,
//         JSON.stringify(packageContent, null, "\t")
//     );

//     console.log("sudo rm -rf node_modules/ yarn.lock");

//     exec("sudo rm -rf node_modules/ yarn.lock", { maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
//         if (err) {
//             throw err;
//         }

//         console.log("sudo yarn install");

//         exec("sudo yarn install", { maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
//             if (err) {
//                 throw err;
//             }

//             console.log("sudo cp -rf ../Wonder-Benchmark/node_modules/puppeteer ./node_modules/puppeteer");

//             exec("sudo cp -rf ../Wonder-Benchmark/node_modules/puppeteer ./node_modules/puppeteer", { maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
//                 if (err) {
//                     throw err;
//                 }

//                 console.log("sudo npm install");

//                 exec("sudo npm install", { cwd: path.join(process.cwd(), "node_modules/puppeteer"), maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
//                     if (err) {
//                         throw err;
//                     }

//                     console.log("restore package.json");

//                     packageContent.devDependencies.puppeteer = "^0.13.0";

//                     fs.writeFileSync(packageFilePath,
//                         JSON.stringify(packageContent, null, "\t")
//                     );

//                     done();
//                 });
//             });
//         });
//     });
// });

<commit_msg>chore(ci): try pass render test<commit_after>var gulp = require("gulp");
var git = require("gulp-git");
var path = require("path");
var fs = require("fs");

var exec = require("child_process").exec;

gulp.task("installPuppeteerInCI", function (done) {
    exec("yarn add puppeteer", { maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
        if (err) {
            throw err;
        }

        done();
    });
});

gulp.task("installPuppeteer", function (done) {
    console.log("sudo cp -rf ../Wonder-Benchmark/node_modules/puppeteer ./node_modules/puppeteer");

    exec("sudo cp -rf ../Wonder-Benchmark/node_modules/puppeteer ./node_modules/puppeteer", { maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
        if (err) {
            throw err;
        }

        console.log("sudo npm install in node_modules/puppeteer");

        exec("sudo npm install", { cwd: path.join(process.cwd(), "node_modules/puppeteer"), maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
            if (err) {
                throw err;
            }

            done();
        });
    });
});

// gulp.task("install", function (done) {
//     var packageFilePath = path.join(process.cwd(), "./package.json");

//     console.log("remove puppeteer in package.json...");

//     var packageContent = JSON.parse(fs.readFileSync(packageFilePath));

//     delete packageContent.devDependencies.puppeteer;

//     fs.writeFileSync(packageFilePath,
//         JSON.stringify(packageContent, null, "\t")
//     );

//     console.log("sudo rm -rf node_modules/ yarn.lock");

//     exec("sudo rm -rf node_modules/ yarn.lock", { maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
//         if (err) {
//             throw err;
//         }

//         console.log("sudo yarn install");

//         exec("sudo yarn install", { maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
//             if (err) {
//                 throw err;
//             }

//             console.log("sudo cp -rf ../Wonder-Benchmark/node_modules/puppeteer ./node_modules/puppeteer");

//             exec("sudo cp -rf ../Wonder-Benchmark/node_modules/puppeteer ./node_modules/puppeteer", { maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
//                 if (err) {
//                     throw err;
//                 }

//                 console.log("sudo npm install");

//                 exec("sudo npm install", { cwd: path.join(process.cwd(), "node_modules/puppeteer"), maxBuffer: 8192 * 4000 }, function (err, stdout, stderr) {
//                     if (err) {
//                         throw err;
//                     }

//                     console.log("restore package.json");

//                     packageContent.devDependencies.puppeteer = "^0.13.0";

//                     fs.writeFileSync(packageFilePath,
//                         JSON.stringify(packageContent, null, "\t")
//                     );

//                     done();
//                 });
//             });
//         });
//     });
// });

<|endoftext|>


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

	// Initialize config
	grunt.initConfig({

		// Load package.json
		pkg: require('./package.json'),

		// Project paths
		project: {
			assets: 'assets',
			styles: '<%= project.assets %>/stylesheets',
			styles_scss: '<%= project.styles %>/scss',
			styles_critical: '<%= project.styles %>/critical',
			scripts: '<%= project.assets %>/javascript',
			scripts_main: '<%= project.scripts %>/main',
			scripts_modules: '<%= project.scripts %>/modules',
			scripts_plugins: '<%= project.scripts %>/lib/plugins',
			scripts_polyfills: '<%= project.scripts %>/lib/polyfills',
			scripts_utils: '<%= project.scripts %>/lib/utils',
		},

		// Project banner
		tag: {
			banner_basic: '/**\n' +
					' * <%= pkg.description %> — v<%= pkg.version %> — <%= grunt.template.today("yyyy-mm-dd") %>\n' +
					' * <%= pkg.url %>\n' +
					' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.copyright %>\n' +
					' * License <%= pkg.license %>\n' +
					' */\n',
			banner_extended: '/**\n' +
					' * <%= pkg.description %>\n' +
					' *\n' +
					' * @authors   <%= pkg.author %>\n' +
					' * @link      <%= pkg.url %>\n' +
					' * @version   <%= pkg.version %>\n' +
					' * @generated <%= grunt.template.today("yyyy-mm-dd:hh:mm") %>\n' +
					' * @copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.copyright %>\n' +
					' * @license   <%= pkg.license %>\n' +
					' */\n'
		},

		// JS files and order
		jsfiles: {
			head: [
				'<%= project.scripts_plugins %>/enhance/enhance.js',
				// '<%= project.scripts_plugins %>/modernizr/modernizr.dev.js',
				'<%= project.scripts_plugins %>/modernizr/modernizr.min.js',    // ALWAYS use custom build modernizr in production!
				'<%= project.scripts_main %>/head.scripts.js',
			],
			main: {
				plugins: [
					'<%= project.scripts_plugins %>/debounce/debounce.js',      // Out-comment when using filters module!
					'<%= project.scripts_plugins %>/domready/domready.js',      // ALWAYS out-comment. It is used in the `main` and `mobile` scripts files!
					'<%= project.scripts_plugins %>/fontfaceobserver/fontfaceobserver.js', // Out-comment when loading fonts
					// '<%= project.scripts_plugins %>/gumshoe/gumshoe.js',
					'<%= project.scripts_plugins %>/imageset/imageset.js',      // Out-comment when using imageset's lazyload
					'<%= project.scripts_plugins %>/imageset/ls.bgset.js',      // Out-comment when using imageset's lazyload + background images with a width descriptor, similar to how `srcset` works (e.g. for Safari 10.x)
					'<%= project.scripts_plugins %>/imageset/ls.parent-fit.js', // Out-comment when using imageset's lazyload + background images; when `bgset` plugin (background images) is used in combination with data-sizes="auto" and `background-size: cover|contain` (e.g. for Edge, IE11 and older, iOS 9, etc.)
					// '<%= project.scripts_plugins %>/transitionend/transitionend.js', // Out-comment when using alerts or navmain module!
					'<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.js',
					// '<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.polyfills.js',
					'<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll-without-hash.js',
					// '<%= project.scripts_plugins %>/throttle/throttle.js',
				],
				polyfills: [
					'<%= project.scripts_polyfills %>/classlist.js',            // Adds cross-browser full `element.classList` support (IE11)
					// '<%= project.scripts_polyfills %>/promise.js',
					// '<%= project.scripts_polyfills %>/ls.respimg.js',           // Only partial `respimage` polyfill (see: https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/respimg); out-comment when not conditionally loading `respimage.min.js` in `head.scripts.js`
					'<%= project.scripts_polyfills %>/svg4everybody.js',        // Adds external spritemaps support to otherwise svg-capable browsers (e.g. IE11)
				],
				utils: [
					// '<%= project.scripts_utils %>/ajax.util.js',
					// '<%= project.scripts_utils %>/cookie.util.js',              // Out-comment when enhance (and thus enhance.cookie) is not used
					// '<%= project.scripts_utils %>/domparents.util.js',
					// '<%= project.scripts_utils %>/extend.util.js',              // Out-comment when using alerts module!
					'<%= project.scripts_utils %>/isvisible.util.js',           // Out-comment when using filters module!
				],
				modules: [
					// '<%= project.scripts_modules %>/alerts.module.js',
					// '<%= project.scripts_modules %>/expand.module.js',
					'<%= project.scripts_modules %>/filters.module.js',
					'<%= project.scripts_modules %>/fontobserver.module.js',
					// '<%= project.scripts_modules %>/navmain.module.js',
					// '<%= project.scripts_modules %>/popup.module.js',
					'<%= project.scripts_modules %>/toggleview.module.js',
				// Main
					'<%= project.scripts_main %>/main.scripts.js',
				],
			},
			// mobile: {
			// 	plugins: [
			// 		'<%= project.scripts_plugins %>/debounce/debounce.js',      // Out-comment when using filters module!
			// 		'<%= project.scripts_plugins %>/domready/domready.js',      // ALWAYS out-comment! It is used in the `main` and `mobile` scripts files!
			// 		'<%= project.scripts_plugins %>/fontfaceobserver/fontfaceobserver.js', // Out-comment when loading fonts
			// 		'<%= project.scripts_plugins %>/imageset/imageset.js',      // Out-comment when using imageset's lazyload
			// 		'<%= project.scripts_plugins %>/imageset/ls.bgset.js',     // Out-comment when using imageset's lazyload + background images with a width descriptor, similar to how `srcset` works (e.g. for Safari 10.x)
			// 		'<%= project.scripts_plugins %>/imageset/ls.parent-fit.js', // Out-comment when using imageset's lazyload + background images; when `bgset` plugin (background images) is used in combination with data-sizes="auto" and `background-size: cover|contain` (e.g. for Edge, IE11 and older, iOS 9, etc.)
			// 		// '<%= project.scripts_plugins %>/transitionend/transitionend.js',
			// 		// '<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.js',
			// 		// '<%= project.scripts_plugins %>/throttle/throttle.js',
			// 	],
			// 	polyfills: [
			// 		'<%= project.scripts_polyfills %>/classlist.js',            // Adds cross-browser full `element.classList` support (IE11)
			// 		// '<%= project.scripts_polyfills %>/promise.js',
			// 		// '<%= project.scripts_polyfills %>/ls.respimg.js',           // Only partial `respimage` polyfill (see: https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/respimg); out-comment when not conditionally loading `respimage.min.js` in `head.scripts.js`
			// 		// '<%= project.scripts_polyfills %>/svg4everybody.js',        // Adds external spritemaps support to otherwise svg-capable browsers (e.g. IE11)
			// 	],
			// 	utils: [
			// 		// '<%= project.scripts_utils %>/ajax.util.js',
			// 		// '<%= project.scripts_utils %>/cookie.util.js',              // Out-comment when enhance (and thus enhance.cookie) is not used
			// 		// '<%= project.scripts_utils %>/domparents.util.js',
			// 		// '<%= project.scripts_utils %>/extend.util.js',              // Out-comment when using alerts module!
			// 		'<%= project.scripts_utils %>/isvisible.util.js',           // Out-comment when using filters module!
			// 	],
			// 	modules: [
			// 		// '<%= project.scripts_modules %>/alerts.module.js',
			// 		// '<%= project.scripts_modules %>/expand.module.js',
			// 		'<%= project.scripts_modules %>/filters.module.js',
			// 		'<%= project.scripts_modules %>/fontobserver.module.js',
			// 		// '<%= project.scripts_modules %>/navmain.module.js',
			// 	// Main
			// 		'<%= project.scripts_main %>/mobile.scripts.js',
			// 	],
			// },
		},
	});

	// Load per-task config from separate files
	grunt.loadTasks('grunt/config');

	// Register alias tasks from separate files
	grunt.loadTasks('grunt/tasks');

	// Register default task
	grunt.registerTask('default', ['develop']);

};
<commit_msg>Remove mobile section.<commit_after>module.exports = function(grunt) {

	// Initialize config
	grunt.initConfig({

		// Load package.json
		pkg: require('./package.json'),

		// Project paths
		project: {
			assets: 'assets',
			styles: '<%= project.assets %>/stylesheets',
			styles_scss: '<%= project.styles %>/scss',
			styles_critical: '<%= project.styles %>/critical',
			scripts: '<%= project.assets %>/javascript',
			scripts_main: '<%= project.scripts %>/main',
			scripts_modules: '<%= project.scripts %>/modules',
			scripts_plugins: '<%= project.scripts %>/lib/plugins',
			scripts_polyfills: '<%= project.scripts %>/lib/polyfills',
			scripts_utils: '<%= project.scripts %>/lib/utils',
		},

		// Project banner
		tag: {
			banner_basic: '/**\n' +
					' * <%= pkg.description %> — v<%= pkg.version %> — <%= grunt.template.today("yyyy-mm-dd") %>\n' +
					' * <%= pkg.url %>\n' +
					' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.copyright %>\n' +
					' * License <%= pkg.license %>\n' +
					' */\n',
			banner_extended: '/**\n' +
					' * <%= pkg.description %>\n' +
					' *\n' +
					' * @authors   <%= pkg.author %>\n' +
					' * @link      <%= pkg.url %>\n' +
					' * @version   <%= pkg.version %>\n' +
					' * @generated <%= grunt.template.today("yyyy-mm-dd:hh:mm") %>\n' +
					' * @copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.copyright %>\n' +
					' * @license   <%= pkg.license %>\n' +
					' */\n'
		},

		// JS files and order
		jsfiles: {
			head: [
				'<%= project.scripts_plugins %>/enhance/enhance.js',
				// '<%= project.scripts_plugins %>/modernizr/modernizr.dev.js',
				'<%= project.scripts_plugins %>/modernizr/modernizr.min.js',    // ALWAYS use custom build modernizr in production!
				'<%= project.scripts_main %>/head.scripts.js',
			],
			main: {
				plugins: [
					'<%= project.scripts_plugins %>/debounce/debounce.js',      // Out-comment when using filters module!
					'<%= project.scripts_plugins %>/domready/domready.js',      // ALWAYS out-comment. It is used in the `main` and `mobile` scripts files!
					'<%= project.scripts_plugins %>/fontfaceobserver/fontfaceobserver.js', // Out-comment when loading fonts
					// '<%= project.scripts_plugins %>/gumshoe/gumshoe.js',
					'<%= project.scripts_plugins %>/imageset/imageset.js',      // Out-comment when using imageset's lazyload
					'<%= project.scripts_plugins %>/imageset/ls.bgset.js',      // Out-comment when using imageset's lazyload + background images with a width descriptor, similar to how `srcset` works (e.g. for Safari 10.x)
					'<%= project.scripts_plugins %>/imageset/ls.parent-fit.js', // Out-comment when using imageset's lazyload + background images; when `bgset` plugin (background images) is used in combination with data-sizes="auto" and `background-size: cover|contain` (e.g. for Edge, IE11 and older, iOS 9, etc.)
					// '<%= project.scripts_plugins %>/transitionend/transitionend.js', // Out-comment when using alerts or navmain module!
					'<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.js',
					// '<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll.polyfills.js',
					'<%= project.scripts_plugins %>/smooth-scroll/smooth-scroll-without-hash.js',
					// '<%= project.scripts_plugins %>/throttle/throttle.js',
				],
				polyfills: [
					'<%= project.scripts_polyfills %>/classlist.js',            // Adds cross-browser full `element.classList` support (IE11)
					// '<%= project.scripts_polyfills %>/promise.js',
					// '<%= project.scripts_polyfills %>/ls.respimg.js',           // Only partial `respimage` polyfill (see: https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/respimg); out-comment when not conditionally loading `respimage.min.js` in `head.scripts.js`
					'<%= project.scripts_polyfills %>/svg4everybody.js',        // Adds external spritemaps support to otherwise svg-capable browsers (e.g. IE11)
				],
				utils: [
					// '<%= project.scripts_utils %>/ajax.util.js',
					// '<%= project.scripts_utils %>/cookie.util.js',              // Out-comment when enhance (and thus enhance.cookie) is not used
					// '<%= project.scripts_utils %>/domparents.util.js',
					// '<%= project.scripts_utils %>/extend.util.js',              // Out-comment when using alerts module!
					'<%= project.scripts_utils %>/isvisible.util.js',           // Out-comment when using filters module!
				],
				modules: [
					// '<%= project.scripts_modules %>/alerts.module.js',
					// '<%= project.scripts_modules %>/expand.module.js',
					'<%= project.scripts_modules %>/filters.module.js',
					'<%= project.scripts_modules %>/fontobserver.module.js',
					// '<%= project.scripts_modules %>/navmain.module.js',
					// '<%= project.scripts_modules %>/popup.module.js',
					'<%= project.scripts_modules %>/toggleview.module.js',
				// Main
					'<%= project.scripts_main %>/main.scripts.js',
				],
			},
		},
	});

	// Load per-task config from separate files
	grunt.loadTasks('grunt/config');

	// Register alias tasks from separate files
	grunt.loadTasks('grunt/tasks');

	// Register default task
	grunt.registerTask('default', ['develop']);

};
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function() {

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;

var MimeHeaderParser = Cc['@mozilla.org/messenger/headerparser;1']
                         .getService(Ci.nsIMsgHeaderParser);

var { Services } = Cu.import('resource://gre/modules/Services.jsm');

var BASE = 'extensions.send-to-internals@clear-code.com.';

var SendToInternalsHelper = {
  checkInternals : function()
  {
    var internalDomains = Services.prefs.getComplexValue(BASE + 'domains', Ci.nsISupportsString).value;
    internalDomains = internalDomains.split(/[,\|\s]+/)
                                     .map(function(aDomain) {
                                       return aDomain.trim();
                                     })
                                     .filter(function(aDomain) {
                                       return aDomain;
                                     });
    if (internalDomains.length === 0) {
      // alert here!
      return false;
    }

    var externals = this.getAllRecipients().filter(function(aAddress) {
      var domain = aAddress.address.split('@')[1];
      return internalDomains.indexOf(domain) < 0;
    }, this);
    if (externals.length !== 0) {
      this.highlightExternals(externals);
      // alert here!
      return false;
    }

    return true;
  },

  getAllRecipients: function() {
    var msgCompFields = gMsgCompose.compFields;
    Recipients2CompFields(msgCompFields);
    gMsgCompose.expandMailingLists();
    return this.splitRecipients(msgCompFields.to, 'To')
    		.concat(this.splitRecipients(msgCompFields.cc, 'Cc'))
            .concat(this.splitRecipients(msgCompFields.bcc, 'Bcc'));
  },

  splitRecipients: function(aAddressesSource, aType){
    var addresses = {};
    var names = {};
    var fullNames = {};
    var numAddresses = MimeHeaderParser.parseHeadersWithArray(
                         aAddressesSource, addresses, names, fullNames);
    var recipients = [];
    for (let i = 0; i < numAddresses; i++) {
      let address = addresses.value[i];
      let domain = address.split('@')[1];
      if (this.ignoredDomains.indexOf(domain) > -1)
        continue;
      recipients.push({
        address:  address,
        name:     names.value[i],
        fullName: fullNames.value[i],
        type:     aType
      });
    }
    return recipients;
  }
};
window.SendToInternalsHelper = SendToInternalsHelper;

window.addEventListener('DOMContentLoaded', function SendToInternalsOnLoad(aEvent) {
  window.removeEventListener(aEvent.type, SendToInternalsOnLoad, false);

  var toolbar = document.getElementById('composeToolbar2');
  var defaultSet = toolbar.getAttribute('defaultset');
  var currentSet = toolbar.getAttribute('currentset');

  if (!Services.prefs.getBoolPref(BASE + 'initialized')) {
    Services.prefs.setBoolPref(BASE + 'initialized', true);
    defaultSet = (defaultSet ? defaultSet + ',' : '' ) + 'sprint,button-sendToInternals';
    toolbar.setAttribute('defaultset', defaultSet);
    currentSet = (currentSet ? currentSet + ',' : '' ) + 'sprint,button-sendToInternals';
    toolbar.setAttribute('currentset', currentSet);
  }
}, false);

})();
<commit_msg>Initialize buttons correctly<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function() {

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;

var MimeHeaderParser = Cc['@mozilla.org/messenger/headerparser;1']
                         .getService(Ci.nsIMsgHeaderParser);

var { Services } = Cu.import('resource://gre/modules/Services.jsm');

var BASE = 'extensions.send-to-internals@clear-code.com.';

var SendToInternalsHelper = {
  checkInternals : function()
  {
    var internalDomains = Services.prefs.getComplexValue(BASE + 'domains', Ci.nsISupportsString).value;
    internalDomains = internalDomains.split(/[,\|\s]+/)
                                     .map(function(aDomain) {
                                       return aDomain.trim();
                                     })
                                     .filter(function(aDomain) {
                                       return aDomain;
                                     });
    if (internalDomains.length === 0) {
      // alert here!
      return false;
    }

    var externals = this.getAllRecipients().filter(function(aAddress) {
      var domain = aAddress.address.split('@')[1];
      return internalDomains.indexOf(domain) < 0;
    }, this);
    if (externals.length !== 0) {
      this.highlightExternals(externals);
      // alert here!
      return false;
    }

    return true;
  },

  getAllRecipients: function() {
    var msgCompFields = gMsgCompose.compFields;
    Recipients2CompFields(msgCompFields);
    gMsgCompose.expandMailingLists();
    return this.splitRecipients(msgCompFields.to, 'To')
    		.concat(this.splitRecipients(msgCompFields.cc, 'Cc'))
            .concat(this.splitRecipients(msgCompFields.bcc, 'Bcc'));
  },

  splitRecipients: function(aAddressesSource, aType){
    var addresses = {};
    var names = {};
    var fullNames = {};
    var numAddresses = MimeHeaderParser.parseHeadersWithArray(
                         aAddressesSource, addresses, names, fullNames);
    var recipients = [];
    for (let i = 0; i < numAddresses; i++) {
      let address = addresses.value[i];
      let domain = address.split('@')[1];
      if (this.ignoredDomains.indexOf(domain) > -1)
        continue;
      recipients.push({
        address:  address,
        name:     names.value[i],
        fullName: fullNames.value[i],
        type:     aType
      });
    }
    return recipients;
  }
};
window.SendToInternalsHelper = SendToInternalsHelper;

window.addEventListener('DOMContentLoaded', function SendToInternalsOnLoad(aEvent) {
  window.removeEventListener(aEvent.type, SendToInternalsOnLoad, false);

  var toolbar = document.getElementById('composeToolbar2');
  var defaultSet = toolbar.getAttribute('defaultset');
  var currentSet = toolbar.getAttribute('currentset');

  defaultSet = (defaultSet ? (defaultSet + ',') : '' ) + 'spring,button-sendToInternals';
  toolbar.setAttribute('defaultset', defaultSet);
  if (!Services.prefs.getBoolPref(BASE + 'initialized')) {
    Services.prefs.setBoolPref(BASE + 'initialized', true);
    currentSet = (currentSet ? (currentSet + ',') : '' ) + 'spring,button-sendToInternals';
    toolbar.setAttribute('currentset', currentSet);
  }
}, false);

})();
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var CheckAttachmentBeforeSendHelper = {
  BASE: 'extensions.check-attachment-before-send@clear-code.com.',

  get prefs() {
    delete this.prefs;
    var { prefs } = Components.utils.import('resource://check-attachment-before-send-modules/prefs.js', {});
    return this.prefs = prefs;
  },

  get ignoredDomains() {
    if (this._cachekdIgnoredDomains)
      return this._cachekdIgnoredDomains;

    var domains = this.prefs.getPref(this.BASE + 'ignoreDomains') || '';
    if (!domains)
      return this._cachekdIgnoredDomains = [];

    domains = domains.replace(/^[,|]|[,|]$/g, '').split(/\s*[,|]\s*|\s+/);
    return this._cachekdIgnoredDomains = domains.filter(function(aDomain) {
      return aDomain.trim() !== '';
    });
  },

  get confirmationPattern() {
    if (this._cachedConfirmationPattern)
      return this._cachedConfirmationPattern;

    var pattern = this.prefs.getPref(this.BASE + 'confirmationPattern') || '';
    if (!pattern)
      return this._cachedConfirmationPattern = null;

    pattern = pattern.trim();
    return this._cachedConfirmationPattern = new RegExp(pattern, 'i');
  },

  log: function(aMessage, ...aExtraArgs) {
    if (!this.prefs.getPref(this.BASE + 'debug')) {
      return;
    }

    aMessage = aMessage || '';
    aMessage = '[check-attachment-before-send] ' + aMessage;
    aExtraArgs.forEach(function(aArg) {
      aMessage += ', ' + JSON.stringify(aArg);
    });

    ConsoleService = Components.classes['@mozilla.org/consoleservice;1']
                       .getService(Components.interfaces.nsIConsoleService);
	ConsoleService.logStringMessage(aMessage);
  },

  get hasAttachment() {
    var attachmentsBacket = GetMsgAttachmentElement();
    var attachmentsCount = attachmentsBacket.itemCount;
    return attachmentsCount > 0;
  },

  get bodyText() {
    var holderNode = gMsgCompose.editor.document.body ||
                 gMsgCompose.editor.document.documentElement;
    return holderNode.textContent.replace(/\s+/g, '');
  },

  get subject() {
    return document.getElementById('msgSubject').value;
  },

  confirm: function() {
    // clear cache
    delete this._cachekdIgnoredDomains;
    delete this._cachedConfirmationPattern;

    if (!this.hasAttachment) {
      this.log('No attachment.');
      return true;
    }

    if (this.confirmationPattern &&
        !this.confirmationPattern.test(this.bodyText) &&
        !this.confirmationPattern.test(this.subject)) {
      this.log('Not matched to the confirmation pattern.', this.confirmationPattern.source);
      return true;
    }

    var recipients = this.getAllRecipients();
    if (recipients.to.length + recipients.cc.length + recipients.bcc.length === 0) {
      this.log('No external recipient.');
      return true;
    }
    this.log('External recipients are detected: ', recipients);

    return true;
  },

  getAllRecipients: function() {
    var msgCompFields = gMsgCompose.compFields;
    Recipients2CompFields(msgCompFields);
    gMsgCompose.expandMailingLists();

    return {
      to:  this.splitRecipients(msgCompFields.to, 'To'),
      cc:  this.splitRecipients(msgCompFields.cc, 'Cc'),
      bcc: this.splitRecipients(msgCompFields.bcc, 'Bcc')
    };
  },

  splitRecipients: function(aAddressesSource, aType){
    var gMimeHeaderParser = Components.classes['@mozilla.org/messenger/headerparser;1']
                              .getService(Components.interfaces.nsIMsgHeaderParser);
    var addresses = {};
    var names = {};
    var fullNames = {};
    var numAddresses = gMimeHeaderParser.parseHeadersWithArray(
                         aAddressesSource, addresses, names, fullNames);
    var recipients = [];
    for (let i = 0; i < numAddresses; i++) {
      let address = addresses.value[i];
      let domain = address.split('@')[1];
      if (this.ignoredDomains.indexOf(domain) > -1)
        continue;
      recipients.push({
        address:  address,
        name:     names.value[i],
        fullName: fullNames.value[i],
        type:     aType
      });
    }
    return recipients;
  },
};

window.addEventListener('load', function CheckAttachmentBeforeSendOnLoad(aEvent) {
  window.removeEventListener(aEvent.type, CheckAttachmentBeforeSendOnLoad, false);

  window.__checkattachmentbeforesend__SendMessage = window.SendMessage;
  window.SendMessage = function SendMessage(...aArgs) {
    if (!CheckAttachmentBeforeSendHelper.confirm()) {
      return;
    }
    window.__checkattachmentbeforesend__SendMessage.apply(this, aArgs);
  };

  window.__checkattachmentbeforesend__SendMessageWithCheck = window.SendMessageWithCheck;
  window.SendMessageWithCheck = function SendMessageWithCheck(...aArgs) {
    if (!CheckAttachmentBeforeSendHelper.confirm()) {
      return;
    }
    window.__checkattachmentbeforesend__SendMessageWithCheck.apply(this, aArgs);
  };

  window.__checkattachmentbeforesend__SendMessageLater = window.SendMessageLater;
  window.SendMessageLater = function SendMessageLater(...aArgs) {
    if (!CheckAttachmentBeforeSendHelper.confirm()) {
      return;
    }
    window.__checkattachmentbeforesend__SendMessageLater.apply(this, aArgs);
  };
}, false);
<commit_msg>Shorten with private variables<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function() {

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;

var MimeHeaderParser = Cc['@mozilla.org/messenger/headerparser;1']
                         .getService(Ci.nsIMsgHeaderParser);
var ConsoleService = Cc['@mozilla.org/consoleservice;1']
                       .getService(Ci.nsIConsoleService);

var CheckAttachmentBeforeSendHelper = {
  BASE: 'extensions.check-attachment-before-send@clear-code.com.',

  get prefs() {
    delete this.prefs;
    var { prefs } = Cu.import('resource://check-attachment-before-send-modules/prefs.js', {});
    return this.prefs = prefs;
  },

  get ignoredDomains() {
    if (this._cachekdIgnoredDomains)
      return this._cachekdIgnoredDomains;

    var domains = this.prefs.getPref(this.BASE + 'ignoreDomains') || '';
    if (!domains)
      return this._cachekdIgnoredDomains = [];

    domains = domains.replace(/^[,|]|[,|]$/g, '').split(/\s*[,|]\s*|\s+/);
    return this._cachekdIgnoredDomains = domains.filter(function(aDomain) {
      return aDomain.trim() !== '';
    });
  },

  get confirmationPattern() {
    if (this._cachedConfirmationPattern)
      return this._cachedConfirmationPattern;

    var pattern = this.prefs.getPref(this.BASE + 'confirmationPattern') || '';
    if (!pattern)
      return this._cachedConfirmationPattern = null;

    pattern = pattern.trim();
    return this._cachedConfirmationPattern = new RegExp(pattern, 'i');
  },

  log: function(aMessage, ...aExtraArgs) {
    if (!this.prefs.getPref(this.BASE + 'debug')) {
      return;
    }

    aMessage = aMessage || '';
    aMessage = '[check-attachment-before-send] ' + aMessage;
    aExtraArgs.forEach(function(aArg) {
      aMessage += ', ' + JSON.stringify(aArg);
    });

	ConsoleService.logStringMessage(aMessage);
  },

  get hasAttachment() {
    var attachmentsBacket = GetMsgAttachmentElement();
    var attachmentsCount = attachmentsBacket.itemCount;
    return attachmentsCount > 0;
  },

  get bodyText() {
    var holderNode = gMsgCompose.editor.document.body ||
                 gMsgCompose.editor.document.documentElement;
    return holderNode.textContent.replace(/\s+/g, '');
  },

  get subject() {
    return document.getElementById('msgSubject').value;
  },

  confirm: function() {
    // clear cache
    delete this._cachekdIgnoredDomains;
    delete this._cachedConfirmationPattern;

    if (!this.hasAttachment) {
      this.log('No attachment.');
      return true;
    }

    if (this.confirmationPattern &&
        !this.confirmationPattern.test(this.bodyText) &&
        !this.confirmationPattern.test(this.subject)) {
      this.log('Not matched to the confirmation pattern.', this.confirmationPattern.source);
      return true;
    }

    var recipients = this.getAllRecipients();
    if (recipients.to.length + recipients.cc.length + recipients.bcc.length === 0) {
      this.log('No external recipient.');
      return true;
    }
    this.log('External recipients are detected: ', recipients);

    return true;
  },

  getAllRecipients: function() {
    var msgCompFields = gMsgCompose.compFields;
    Recipients2CompFields(msgCompFields);
    gMsgCompose.expandMailingLists();

    return {
      to:  this.splitRecipients(msgCompFields.to, 'To'),
      cc:  this.splitRecipients(msgCompFields.cc, 'Cc'),
      bcc: this.splitRecipients(msgCompFields.bcc, 'Bcc')
    };
  },

  splitRecipients: function(aAddressesSource, aType){
    var addresses = {};
    var names = {};
    var fullNames = {};
    var numAddresses = MimeHeaderParser.parseHeadersWithArray(
                         aAddressesSource, addresses, names, fullNames);
    var recipients = [];
    for (let i = 0; i < numAddresses; i++) {
      let address = addresses.value[i];
      let domain = address.split('@')[1];
      if (this.ignoredDomains.indexOf(domain) > -1)
        continue;
      recipients.push({
        address:  address,
        name:     names.value[i],
        fullName: fullNames.value[i],
        type:     aType
      });
    }
    return recipients;
  },
};
window.CheckAttachmentBeforeSendHelper = CheckAttachmentBeforeSendHelper;

window.addEventListener('load', function CheckAttachmentBeforeSendOnLoad(aEvent) {
  window.removeEventListener(aEvent.type, CheckAttachmentBeforeSendOnLoad, false);

  window.__checkattachmentbeforesend__SendMessage = window.SendMessage;
  window.SendMessage = function SendMessage(...aArgs) {
    if (!CheckAttachmentBeforeSendHelper.confirm()) {
      return;
    }
    window.__checkattachmentbeforesend__SendMessage.apply(this, aArgs);
  };

  window.__checkattachmentbeforesend__SendMessageWithCheck = window.SendMessageWithCheck;
  window.SendMessageWithCheck = function SendMessageWithCheck(...aArgs) {
    if (!CheckAttachmentBeforeSendHelper.confirm()) {
      return;
    }
    window.__checkattachmentbeforesend__SendMessageWithCheck.apply(this, aArgs);
  };

  window.__checkattachmentbeforesend__SendMessageLater = window.SendMessageLater;
  window.SendMessageLater = function SendMessageLater(...aArgs) {
    if (!CheckAttachmentBeforeSendHelper.confirm()) {
      return;
    }
    window.__checkattachmentbeforesend__SendMessageLater.apply(this, aArgs);
  };
}, false);

})();
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(global) {

var { AutoCompleteResultCache } = Components.utils.import('resource://customizable-ldap-autocomplete-modules/AutoCompleteResultCache.jsm', {});

var AbRecipientImagePopup = {
  get popup() {
    return document.getElementById('ab-recipient-image-popup');
  },
  get image() {
    return document.getElementById('ab-recipient-image');
  },

  init: function() {
    window.removeEventListener('load', this, false);
    window.addEventListener('unload', this, false);
    window.addEventListener('keypress', this, true);
    window.addEventListener('popuphidden', this, true);
  },

  destroy: function() {
    window.removeEventListener('unload', this, false);
    window.removeEventListener('keypress', this, true);
    window.removeEventListener('popuphidden', this, true);
  },

  isRecipientField: function(aElement) {
    return (
      aElement.localName == 'textbox' &&
      aElement.getAttribute('type') == 'autocomplete' &&
      aElement.classList.contains('textbox-addressingWidget')
    );
  },

  handleUserOperation: function(aEvent) {
    var field = aEvent.target;
    if (!this.isRecipientField(field))
      return;

    setTimeout((function() {
      var maybeShown = this.handleUserOperationWithDelay(field);
      if (!maybeShown)
        this.hidePopup();
    }).bind(this), 0);
  },
  handleUserOperationWithDelay: function(aField) {
    if (!aField.popupOpen)
      return false;

    var result = AutoCompleteResultCache.get(aField.controller.searchString);
    if (!result)
      return false;

    var address = aField.controller.getValueAt(aField.popup.selectedIndex);
    var index = result.indexOfValue(address);
    if (index < 0)
      return false;

    var card = result.getCardAt(index);
    var book = result.getBookAt(index);
    this.showImageFor({
      card:          card,
      book:          book,
      anchorElement: aField.popup
    });
    return true;
  },

  showImageFor: function(aParams) {
    var card          = aParams.card;
    var book          = aParams.book;
    var anchorElement = aParams.anchorElement;

    this.hidePopup();

    if (book instanceof Compoments.interfaces.nsIAbLDAPDirectory &&
        'LDAPContactPhoto' in global) {
      let image = new Image();
      image.addEventListener('load', (function() {
        this.image.src = image.src;
        this.showPopupAt(anchorElement);
      }).bind(this), false);
      LDAPContactPhoto.fetchLDAPPhoto(card, book.URI, image);
      return;
    }

    switch (card.getProperty('PhotoType', null)) {
      case 'file':
      case 'web':
        let uri = card.getProperty('PhotoURI', null);
        if (uri) {
          this.image.src = uri;
          this.showPopupAt(anchorElement);
        }
        return;

      default:
        break;
    }
  },
  showPopupAt: function(aAnchorElement) {
    this.popup.openPopup(aAnchorElement, 'after_start', -1, -1, true, true);
  },

  hidePopup: function() {
    this.popup.hidePopup();
  },

  handleEvent: function(aEvent) {
    switch (aEvent.type) {
      case 'load':
        this.init();
        return;

      case 'unload':
        this.destroy();
        return;

      case 'keypress':
        this.handleUserOperation(aEvent);
        return;

      case 'popuphidden':
        if (aEvent.target != this.popup)
          this.hidePopup();
        return;
    }
  }
};

window.addEventListener('load', AbRecipientImagePopup, false);

global.AbRecipientImagePopup = AbRecipientImagePopup;
})(this);
<commit_msg>Support non-modified autocomplete results<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(global) {

var { AutoCompleteResultCache } = Components.utils.import('resource://customizable-ldap-autocomplete-modules/AutoCompleteResultCache.jsm', {});

var AbRecipientImagePopup = {
  get popup() {
    return document.getElementById('ab-recipient-image-popup');
  },
  get image() {
    return document.getElementById('ab-recipient-image');
  },

  init: function() {
    window.removeEventListener('load', this, false);
    window.addEventListener('unload', this, false);
    window.addEventListener('keypress', this, true);
    window.addEventListener('popuphidden', this, true);
  },

  destroy: function() {
    window.removeEventListener('unload', this, false);
    window.removeEventListener('keypress', this, true);
    window.removeEventListener('popuphidden', this, true);
  },

  isRecipientField: function(aElement) {
    return (
      aElement.localName == 'textbox' &&
      aElement.getAttribute('type') == 'autocomplete' &&
      aElement.classList.contains('textbox-addressingWidget')
    );
  },

  handleUserOperation: function(aEvent) {
    var field = aEvent.target;
    if (!this.isRecipientField(field))
      return;

    setTimeout((function() {
      var maybeShown = this.handleUserOperationWithDelay(field);
      if (!maybeShown)
        this.hidePopup();
    }).bind(this), 0);
  },
  handleUserOperationWithDelay: function(aField) {
    if (!aField.popupOpen)
      return false;

    var rawResult = AutoCompleteResultCache.get(aField.controller.searchString);
    var address = aField.controller.getValueAt(aField.popup.selectedIndex);
    if (typeof rawResult.indexOfValue == 'function') {
    let index = rawResult.indexOfValue(address);
    if (index < 0)
      return false;

    let card = result.getCardAt(index);
    let book = result.getBookAt(index);
    this.showImageFor({
      card:          card,
      book:          book,
      anchorElement: aField.popup
    });
    return true;
    }
    else {
      let card = result.getCardAt(index);
      this.showImageFor({
        card:          card,
        anchorElement: aField.popup
      });
    }
  },

  showImageFor: function(aParams) {
    var card          = aParams.card;
    var book          = aParams.book;
    var anchorElement = aParams.anchorElement;

    this.hidePopup();

    if (book &&
        book instanceof Compoments.interfaces.nsIAbLDAPDirectory &&
        'LDAPContactPhoto' in global) {
      let image = new Image();
      image.addEventListener('load', (function() {
        this.image.src = image.src;
        this.showPopupAt(anchorElement);
      }).bind(this), false);
      LDAPContactPhoto.fetchLDAPPhoto(card, book.URI, image);
      return;
    }

    switch (card.getProperty('PhotoType', null)) {
      case 'file':
      case 'web':
        let uri = card.getProperty('PhotoURI', null);
        if (uri) {
          this.image.src = uri;
          this.showPopupAt(anchorElement);
        }
        return;

      default:
        break;
    }
  },
  showPopupAt: function(aAnchorElement) {
    this.popup.openPopup(aAnchorElement, 'after_start', -1, -1, true, true);
  },

  hidePopup: function() {
    this.popup.hidePopup();
  },

  handleEvent: function(aEvent) {
    switch (aEvent.type) {
      case 'load':
        this.init();
        return;

      case 'unload':
        this.destroy();
        return;

      case 'keypress':
        this.handleUserOperation(aEvent);
        return;

      case 'popuphidden':
        if (aEvent.target != this.popup)
          this.hidePopup();
        return;
    }
  }
};

window.addEventListener('load', AbRecipientImagePopup, false);

global.AbRecipientImagePopup = AbRecipientImagePopup;
})(this);
<|endoftext|>


<commit_before>/**
 * Copyright 2017 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import {getMode} from '../../../src/mode';

//////////////////////////////////////////////////////////////////
//                                                              //
//     IMPORTANT: All keys in RTC_VENDORS must be lowercase     //
//       otherwise the vendor endpoint will not be used.        //
//                                                              //
//////////////////////////////////////////////////////////////////

// Note: disableKeyAppend is an option specifically for DoubleClick's
// implementation of RTC. It prevents the vendor ID from being
// appended onto each key of the RTC response, for each vendor.
// This appending is done to prevent a collision case during merge
// that would cause one RTC response to overwrite another if they
// share key names.
/** @typedef {{
    url: string,
    macros: Array<string>,
    disableKeyAppend: boolean}} */
let RtcVendorDef;

/** @const {!Object<string, RtcVendorDef>} */
export const RTC_VENDORS = {
  // Add vendors here
  medianet: {
    url: 'https://amprtc.media.net/rtb/getrtc?cid=CID&w=ATTR(width)&h=ATTR(height)&ow=ATTR(data-override-width)&oh=ATTR(data-override-height)&ms=ATTR(data-multi-size)&slot=ATTR(data-slot)&tgt=TGT&curl=CANONICAL_URL&to=TIMEOUT&purl=HREF',
    macros: ['CID'],
    disableKeyAppend: true,
  },
  prebidappnexus: {
    url: 'https://prebid.adnxs.com/pbs/v1/openrtb2/amp?tag_id=PLACEMENT_ID',
    macros: ['PLACEMENT_ID'],
    disableKeyAppend: true,
  },
  prebidrubicon: {
    url: 'https://prebid-server.rubiconproject.com/openrtb2/amp?tag_id=REQUEST_ID&w=ATTR(width)&h=ATTR(height)&ow=ATTR(data-override-width)&oh=ATTR(data-override-height)&ms=ATTR(data-multi-size)&slot=ATTR(data-slot)&tgt=TGT&curl=CANONICAL_URL&to=TIMEOUT&adc=ADCID&purl=HREF',
    macros: ['REQUEST_ID'],
    disableKeyAppend: true,
  },
  indexexchange: {
    url: 'https://amp.casalemedia.com/amprtc?v=1&w=ATTR(width)&h=ATTR(height)&ow=ATTR(data-override-width)&oh=ATTR(data-override-height)&ms=ATTR(data-multi-size)&s=SITE_ID&p=HREF',
    macros: ['SITE_ID'],
    disableKeyAppend: true,
  },
  lotame: {
    url: 'https://ad.crwdcntrl.net/5/pe=y/c=CLIENT_ID/an=AD_NETWORK',
    macros: ['CLIENT_ID', 'AD_NETWORK'],
  },
};

// DO NOT MODIFY: Setup for tests
if (getMode().localDev || getMode().test) {
  RTC_VENDORS['fakevendor'] = /** @type {RtcVendorDef} */({
    url: 'https://localhost:8000/examples/rtcE1.json?slot_id=SLOT_ID&page_id=PAGE_ID&foo_id=FOO_ID',
    macros: ['SLOT_ID', 'PAGE_ID', 'FOO_ID'],
  });
  RTC_VENDORS['fakevendor2'] = /** @type {RtcVendorDef} */({
    url: 'https://localhost:8000/examples/rtcE1.json?slot_id=SLOT_ID&page_id=PAGE_ID&foo_id=FOO_ID',
    disableKeyAppend: true,
  });
}
<commit_msg>🚀 Appnexus prebid server callout rtc update (#14155)<commit_after>/**
 * Copyright 2017 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import {getMode} from '../../../src/mode';

//////////////////////////////////////////////////////////////////
//                                                              //
//     IMPORTANT: All keys in RTC_VENDORS must be lowercase     //
//       otherwise the vendor endpoint will not be used.        //
//                                                              //
//////////////////////////////////////////////////////////////////

// Note: disableKeyAppend is an option specifically for DoubleClick's
// implementation of RTC. It prevents the vendor ID from being
// appended onto each key of the RTC response, for each vendor.
// This appending is done to prevent a collision case during merge
// that would cause one RTC response to overwrite another if they
// share key names.
/** @typedef {{
    url: string,
    macros: Array<string>,
    disableKeyAppend: boolean}} */
let RtcVendorDef;

/** @const {!Object<string, RtcVendorDef>} */
export const RTC_VENDORS = {
  // Add vendors here
  medianet: {
    url: 'https://amprtc.media.net/rtb/getrtc?cid=CID&w=ATTR(width)&h=ATTR(height)&ow=ATTR(data-override-width)&oh=ATTR(data-override-height)&ms=ATTR(data-multi-size)&slot=ATTR(data-slot)&tgt=TGT&curl=CANONICAL_URL&to=TIMEOUT&purl=HREF',
    macros: ['CID'],
    disableKeyAppend: true,
  },
  prebidappnexus: {
    url: 'https://prebid.adnxs.com/pbs/v1/openrtb2/amp?tag_id=PLACEMENT_ID&w=ATTR(width)&h=ATTR(height)&ow=ATTR(data-override-width)&oh=ATTR(data-override-height)&slot=ATTR(data-slot)&targeting=TGT&curl=CANONICAL_URL&timeout=TIMEOUT&adcid=ADCID&purl=HREF',
    macros: ['PLACEMENT_ID'],
    disableKeyAppend: true,
  },
  prebidrubicon: {
    url: 'https://prebid-server.rubiconproject.com/openrtb2/amp?tag_id=REQUEST_ID&w=ATTR(width)&h=ATTR(height)&ow=ATTR(data-override-width)&oh=ATTR(data-override-height)&ms=ATTR(data-multi-size)&slot=ATTR(data-slot)&targeting=TGT&curl=CANONICAL_URL&timeout=TIMEOUT&adc=ADCID&purl=HREF',
    macros: ['REQUEST_ID'],
    disableKeyAppend: true,
  },
  indexexchange: {
    url: 'https://amp.casalemedia.com/amprtc?v=1&w=ATTR(width)&h=ATTR(height)&ow=ATTR(data-override-width)&oh=ATTR(data-override-height)&ms=ATTR(data-multi-size)&s=SITE_ID&p=HREF',
    macros: ['SITE_ID'],
    disableKeyAppend: true,
  },
  lotame: {
    url: 'https://ad.crwdcntrl.net/5/pe=y/c=CLIENT_ID/an=AD_NETWORK',
    macros: ['CLIENT_ID', 'AD_NETWORK'],
  },
};

// DO NOT MODIFY: Setup for tests
if (getMode().localDev || getMode().test) {
  RTC_VENDORS['fakevendor'] = /** @type {RtcVendorDef} */({
    url: 'https://localhost:8000/examples/rtcE1.json?slot_id=SLOT_ID&page_id=PAGE_ID&foo_id=FOO_ID',
    macros: ['SLOT_ID', 'PAGE_ID', 'FOO_ID'],
  });
  RTC_VENDORS['fakevendor2'] = /** @type {RtcVendorDef} */({
    url: 'https://localhost:8000/examples/rtcE1.json?slot_id=SLOT_ID&page_id=PAGE_ID&foo_id=FOO_ID',
    disableKeyAppend: true,
  });
}
<|endoftext|>


<commit_before>var qs = require('querystring');

var util = require('./util');

/**
 * 对 request 请求的参数(即输入)做验证
 * 在接口配置中配置期望的值, 然后验证 request 中是否包含这些期望的值
 * 例如看 querystring 是否具有某个参数等等
 */
function validateRequest(request, response, mockRequest, onValid) {
    // 因为 puer 没有提供使用 express middleware 的机制, 不能用 body-parser 只好自己解析出 body 内容了
    // 由于是异步的, 只好将所有的验证逻辑都放在 end 事件中
    request._rawBody = '';
    request.on('data', function(chunk) {
        request._rawBody += chunk;
    });
    request.on('end', function() {
        var requestValues = getRequestValues(request, mockRequest);
        var errors = validateDataType(requestValues);
        if (errors.length > 0) {
            response.set(util.CORS_HEADER)
                    .status(400)
                    .jsonp({
                        status: 400,
                        statusInfo: errors
                    });
        } else { // 验证通过时正常返回接口数据
            onValid();
        }
    });
}

/**
 * 获取待验证的期望值和实际值
 *
 * @return {
 *     querystring: {
 *         "参数名": {
 *             expectedValue: "期望值",
 *             factValue: "实际值"
 *         }
 *     },
 *     header: {},
 *     body: {}
 * }
 */
function getRequestValues(request, mockRequestConfig) {
    var querystring = mockRequestConfig ? mockRequestConfig.querystring : {};
    var header = mockRequestConfig ? mockRequestConfig.header : {};
    var body = mockRequestConfig ? mockRequestConfig.body : {};

    // 先找出所有的必选参数, 最后一起做验证
    var requestValues = {
        querystring: {},
        header: {},
        body: {}
    };

    // 验证请求接口时是否包含特定的 querystring 参数
    if (querystring) {
        for (var expectedName in querystring) {
            // 只验证必传参数
            if (isRequired(expectedName)) {
                // 去除用作必要参数的标识
                var _expectedName = stripRequiredPlaceholder(expectedName);
                var expectedValue = querystring[expectedName];
                var factValue = request.query[_expectedName];

                requestValues.querystring[_expectedName] = {
                    expectedValue: expectedValue,
                    factValue: factValue
                };
            }
        }
    }

    // 验证请求接口时是否包含特定的 header
    if (header) {
        for (var expectedName in header) {
            if (isRequired(expectedName)) {
                var _expectedName = stripRequiredPlaceholder(expectedName);
                var expectedValue = header[expectedName];
                var factValue = request.get(_expectedName);

                requestValues.header[_expectedName] = {
                    expectedValue: expectedValue,
                    factValue: factValue ? decodeURIComponent(factValue) : factValue
                };
            }
        }
    }

    // 验证请求接口时是否包含特定的 body 数据
    if (body) {
        // request's "Content-Type" HTTP header field matches the MIME type
        // http://expressjs.com/en/4x/api.html#req.is
        if (request.is('application/x-www-form-urlencoded')) {
            // XXX nodejs 自带的 querystring 无法解析嵌套的对象
            // 例如: querystring 是 a%5Bb%5D%3D1, 解析出来为 {'a[b]': 1}
            // 但是我们想要的是 {a:{b:1}}
            var urlencoded = qs.parse(request._rawBody);

            for (var expectedName in body) {
                if (isRequired(expectedName)) {
                    var _expectedName = stripRequiredPlaceholder(expectedName);
                    var expectedValue = body[expectedName];
                    var factValue = urlencoded[_expectedName];

                    requestValues.body[_expectedName] = {
                        expectedValue: expectedValue,
                        factValue: factValue
                    };
                }
            }
        } else if (request.is('multipart/form-data')) {
            // multipart/form-data 一般用得比较少, 简单的使用正则表达式来解析
            var multipartRegExp = /Content-Disposition:\s*form-data;\s*name\s*=\s*"(\S+)"\s+(\S+)/mg;

            var multipartFormData = {};
            var result = null;
            while ((result = multipartRegExp.exec(request._rawBody)) != null) {
                // 从表单数据中匹配出字段名和字段值
                var fieldName = result[1];
                var fieldValue = result[2];
                multipartFormData[fieldName] = fieldValue;
            }

            for (var expectedName in body) {
                if (isRequired(expectedName)) {
                    var _expectedName = stripRequiredPlaceholder(expectedName);
                    var expectedValue = body[expectedName];
                    var factValue = multipartFormData[_expectedName];

                    requestValues.body[_expectedName] = {
                        expectedValue: expectedValue,
                        factValue: factValue
                    };
                }
            }
        } else if (request.is('json')) {
            var factValue = null;
            try {
                factValue = JSON.parse(decodeURIComponent(request._rawBody));
            } catch (e) {
                factValue = request._rawBody;
                console.warn('body json parse error', e);
            }

            // 在 body 中放置 raw 数据时, 不是 key-value 键值对形式的数据
            // 因此使用 Content-Type MIME 作为 key 来标记
            // 但是不验证里面具体的数据类型, 只验证其是否是 object 类型
            requestValues.body[request.get('Content-Type')] = {
                expectedValue: body,
                factValue: factValue
            };
        } else {
            // 针对其他的 MIME 类似数据不做验证, 都认为他们是字符串
            // 希望扩展的人可以扩展验证规则
            if (!util.isEmptyObject(body)) {
                requestValues.body[request.get('Content-Type')] = {
                    expectedValue: body.toString(),
                    _expectedValue: body,
                    factValue: request._rawBody + ' ' // 不管你有没有传 body, 都伪造了一个非空的值
                };
            }
        }
    }

    return requestValues;
}

/**
 * 验证数据类型
 *
 * @return [{
 *     required: true,
 *     type: "string", // string/number/boolean/object
 *     factValue: "实际值",
 *     querystring: "参数名" // header/body
 * }]
 */
function validateDataType(requestValues) {
    var errors = [];

    for (var paramType in requestValues) {
        var param = requestValues[paramType];

        for (var expectedName in param) {
            var assertion = param[expectedName];
            var expectedValue = assertion.expectedValue;
            var factValue = assertion.factValue;

            var error = null;

            if (typeof expectedValue == 'string') {
                if (!factValue) { // 当参数期望为字符串值时, 只需要判断是否有值即可
                    error = {
                        required: true,
                        type: 'string',
                        factValue: factValue
                    };
                    error[paramType] = expectedName;
                }
            } else if (typeof expectedValue == 'number') {
                if (!Number(factValue)) {
                    error = {
                        required: true,
                        type: 'number',
                        factValue: factValue
                    };
                    error[paramType] = expectedName;
                }
            } else if (typeof expectedValue == 'boolean') {
                var _factValue = factValue ? factValue.toLowerCase() : factValue;
                if (_factValue != 'true' && _factValue != 'false') {
                    error = {
                        required: true,
                        type: 'boolean',
                        factValue: factValue
                    };
                    error[paramType] = expectedName;
                }
            } else if (typeof expectedValue == 'object') {
                if (typeof factValue != 'object') {
                    error = {
                        required: true,
                        type: 'object',
                        factValue: factValue
                    };
                    error[paramType] = expectedName;
                }
            }

            if (error) {
                errors.push(error);
            }
        }
    }

    return errors;
}

/**
 * 必传参数的规则是在名字的前面或后面加一个星号(*)作为标识,
 * 从表单必填项获得的灵感
 */
function isRequired(name) {
    return name.indexOf('*') != -1;
}
/**
 * 去除参数名中包含的必传标识
 * 例如 *name -> name
 */
function stripRequiredPlaceholder(name) {
    return name.replace(/\*/g, '');
}

module.exports = validateRequest;<commit_msg>添加 express-validation 注释说明<commit_after>var qs = require('querystring');

var util = require('./util');

/**
 * 对 request 请求的参数(即输入)做验证
 * 在接口配置中配置期望的值, 然后验证 request 中是否包含这些期望的值
 * 例如看 querystring 是否具有某个参数等等
 *
 * 如果需要更加完整的验证功能, 请参考
 * express-validation: a middleware that validates the body, params, query, headers of a request and returns a 400 Bad request response if the validation fails.
 * https://github.com/AndrewKeig/express-validation
 */
function validateRequest(request, response, mockRequest, onValid) {
    // 因为 puer 没有提供使用 express middleware 的机制, 不能用 body-parser 只好自己解析出 body 内容了
    // 由于是异步的, 只好将所有的验证逻辑都放在 end 事件中
    request._rawBody = '';
    request.on('data', function(chunk) {
        request._rawBody += chunk;
    });
    request.on('end', function() {
        var requestValues = getRequestValues(request, mockRequest);
        var errors = validateDataType(requestValues);
        if (errors.length > 0) {
            response.set(util.CORS_HEADER)
                    .status(400)
                    .jsonp({
                        status: 400,
                        statusInfo: errors
                    });
        } else { // 验证通过时正常返回接口数据
            onValid();
        }
    });
}

/**
 * 获取待验证的期望值和实际值
 *
 * @return {
 *     querystring: {
 *         "参数名": {
 *             expectedValue: "期望值",
 *             factValue: "实际值"
 *         }
 *     },
 *     header: {},
 *     body: {}
 * }
 */
function getRequestValues(request, mockRequestConfig) {
    var querystring = mockRequestConfig ? mockRequestConfig.querystring : {};
    var header = mockRequestConfig ? mockRequestConfig.header : {};
    var body = mockRequestConfig ? mockRequestConfig.body : {};

    // 先找出所有的必选参数, 最后一起做验证
    var requestValues = {
        querystring: {},
        header: {},
        body: {}
    };

    // 验证请求接口时是否包含特定的 querystring 参数
    if (querystring) {
        for (var expectedName in querystring) {
            // 只验证必传参数
            if (isRequired(expectedName)) {
                // 去除用作必要参数的标识
                var _expectedName = stripRequiredPlaceholder(expectedName);
                var expectedValue = querystring[expectedName];
                var factValue = request.query[_expectedName];

                requestValues.querystring[_expectedName] = {
                    expectedValue: expectedValue,
                    factValue: factValue
                };
            }
        }
    }

    // 验证请求接口时是否包含特定的 header
    if (header) {
        for (var expectedName in header) {
            if (isRequired(expectedName)) {
                var _expectedName = stripRequiredPlaceholder(expectedName);
                var expectedValue = header[expectedName];
                var factValue = request.get(_expectedName);

                requestValues.header[_expectedName] = {
                    expectedValue: expectedValue,
                    factValue: factValue ? decodeURIComponent(factValue) : factValue
                };
            }
        }
    }

    // 验证请求接口时是否包含特定的 body 数据
    if (body) {
        // request's "Content-Type" HTTP header field matches the MIME type
        // http://expressjs.com/en/4x/api.html#req.is
        if (request.is('application/x-www-form-urlencoded')) {
            // XXX nodejs 自带的 querystring 无法解析嵌套的对象
            // 例如: querystring 是 a%5Bb%5D%3D1, 解析出来为 {'a[b]': 1}
            // 但是我们想要的是 {a:{b:1}}
            var urlencoded = qs.parse(request._rawBody);

            for (var expectedName in body) {
                if (isRequired(expectedName)) {
                    var _expectedName = stripRequiredPlaceholder(expectedName);
                    var expectedValue = body[expectedName];
                    var factValue = urlencoded[_expectedName];

                    requestValues.body[_expectedName] = {
                        expectedValue: expectedValue,
                        factValue: factValue
                    };
                }
            }
        } else if (request.is('multipart/form-data')) {
            // multipart/form-data 一般用得比较少, 简单的使用正则表达式来解析
            var multipartRegExp = /Content-Disposition:\s*form-data;\s*name\s*=\s*"(\S+)"\s+(\S+)/mg;

            var multipartFormData = {};
            var result = null;
            while ((result = multipartRegExp.exec(request._rawBody)) != null) {
                // 从表单数据中匹配出字段名和字段值
                var fieldName = result[1];
                var fieldValue = result[2];
                multipartFormData[fieldName] = fieldValue;
            }

            for (var expectedName in body) {
                if (isRequired(expectedName)) {
                    var _expectedName = stripRequiredPlaceholder(expectedName);
                    var expectedValue = body[expectedName];
                    var factValue = multipartFormData[_expectedName];

                    requestValues.body[_expectedName] = {
                        expectedValue: expectedValue,
                        factValue: factValue
                    };
                }
            }
        } else if (request.is('json')) {
            var factValue = null;
            try {
                factValue = JSON.parse(decodeURIComponent(request._rawBody));
            } catch (e) {
                factValue = request._rawBody;
                console.warn('body json parse error', e);
            }

            // 在 body 中放置 raw 数据时, 不是 key-value 键值对形式的数据
            // 因此使用 Content-Type MIME 作为 key 来标记
            // 但是不验证里面具体的数据类型, 只验证其是否是 object 类型
            requestValues.body[request.get('Content-Type')] = {
                expectedValue: body,
                factValue: factValue
            };
        } else {
            // 针对其他的 MIME 类似数据不做验证, 都认为他们是字符串
            // 希望扩展的人可以扩展验证规则
            if (!util.isEmptyObject(body)) {
                requestValues.body[request.get('Content-Type')] = {
                    expectedValue: body.toString(),
                    _expectedValue: body,
                    factValue: request._rawBody + ' ' // 不管你有没有传 body, 都伪造了一个非空的值
                };
            }
        }
    }

    return requestValues;
}

/**
 * 验证数据类型
 *
 * @return [{
 *     required: true,
 *     type: "string", // string/number/boolean/object
 *     factValue: "实际值",
 *     querystring: "参数名" // header/body
 * }]
 */
function validateDataType(requestValues) {
    var errors = [];

    for (var paramType in requestValues) {
        var param = requestValues[paramType];

        for (var expectedName in param) {
            var assertion = param[expectedName];
            var expectedValue = assertion.expectedValue;
            var factValue = assertion.factValue;

            var error = null;

            if (typeof expectedValue == 'string') {
                if (!factValue) { // 当参数期望为字符串值时, 只需要判断是否有值即可
                    error = {
                        required: true,
                        type: 'string',
                        factValue: factValue
                    };
                    error[paramType] = expectedName;
                }
            } else if (typeof expectedValue == 'number') {
                if (!Number(factValue)) {
                    error = {
                        required: true,
                        type: 'number',
                        factValue: factValue
                    };
                    error[paramType] = expectedName;
                }
            } else if (typeof expectedValue == 'boolean') {
                var _factValue = factValue ? factValue.toLowerCase() : factValue;
                if (_factValue != 'true' && _factValue != 'false') {
                    error = {
                        required: true,
                        type: 'boolean',
                        factValue: factValue
                    };
                    error[paramType] = expectedName;
                }
            } else if (typeof expectedValue == 'object') {
                if (typeof factValue != 'object') {
                    error = {
                        required: true,
                        type: 'object',
                        factValue: factValue
                    };
                    error[paramType] = expectedName;
                }
            }

            if (error) {
                errors.push(error);
            }
        }
    }

    return errors;
}

/**
 * 必传参数的规则是在名字的前面或后面加一个星号(*)作为标识,
 * 从表单必填项获得的灵感
 */
function isRequired(name) {
    return name.indexOf('*') != -1;
}
/**
 * 去除参数名中包含的必传标识
 * 例如 *name -> name
 */
function stripRequiredPlaceholder(name) {
    return name.replace(/\*/g, '');
}

module.exports = validateRequest;<|endoftext|>


<commit_before>/* global window, Uint8Array */

(function(Smile, undefined) {
  'use strict';

  var bitMask = [0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff];
  var shiftMultiplier = [1, 2, 4, 8, 16, 32, 64, 128];

  Smile.DecoderStream = function(inputStream) {
    this._inputStream = inputStream;
  };

  Smile.DecoderStream.prototype.isEof = function() {
    return this._inputStream.isEof();
  };

  Smile.DecoderStream.prototype.read = function() {
    return this._inputStream.read();
  };

  Smile.DecoderStream.prototype.peek = function() {
    return this._inputStream.peek();
  };

  Smile.DecoderStream.prototype.readUnsignedVint = function() {
    var value = 0,
      bits = 0;

    function safeLeftShift(n, shift) {
      if ((bits + shift) < 32) {
        value <<= shift;
        value |= n & bitMask[shift];
      } else {
        value *= shiftMultiplier[shift];
        value += n & bitMask[shift];
      }
      bits += shift;
    }

    while (true) {
      var n = this._inputStream.read();
      if (n & 0x80) {
        safeLeftShift(n, 6);
        return value;
      } else {
        safeLeftShift(n, 7);
      }
    }
  };

  Smile.DecoderStream.prototype.readSignedVint = function() {
    return Smile.Decoder.decodeZigZag(this.readUnsignedVint());
  };

  Smile.DecoderStream.prototype.readAscii = function(len) {
    return Smile.Decoder.decodeAscii(this._inputStream.readArray(len));
  };

  Smile.DecoderStream.prototype.readUtf8 = function(len) {
    return Smile.Decoder.decodeUtf8(this._inputStream.readArray(len));
  };

  Smile.DecoderStream.prototype.readFloat32 = function() {
    return Smile.Decoder.decodeFloat32(this.readFixedLengthBigEndianEncodedBits(32, true));
  };

  Smile.DecoderStream.prototype.readFloat64 = function() {
    return Smile.Decoder.decodeFloat64(this.readFixedLengthBigEndianEncodedBits(64, true));
  };

  Smile.DecoderStream.prototype.readFixedLengthBigEndianEncodedBits = function(bits, adjustEndianness) {
    var array = this._inputStream.readArray(Math.ceil(bits / 7));
    return Smile.Decoder.decodeFixedLengthBigEndianEncodedBits(array, bits, adjustEndianness);
  };

  Smile.DecoderStream.prototype.readSafeBinary = function() {
    var len = this.readUnsignedVint(),
      array = this._inputStream.readArray(Math.ceil(len * 8 / 7));
    return Smile.Decoder.decodeSafeBinaryEncodedBits(array, len * 8);
  };

  Smile.DecoderStream.prototype.readBigInt = function() {
    var array = this.readSafeBinary(),
      n = 0,
      i;
    for (i = 0; i < array.length; i++) {
      n = (n * 256) + array[i];
    }
    return n;
  };

  Smile.DecoderStream.prototype.readBigDecimal = function() {
    var scale = this.readSignedVint();
    var magnitude = this.readBigInt();
    return magnitude * Math.pow(10, scale);
  };

  Smile.DecoderStream.prototype.readLongString = function() {
    var buffer = new Uint8Array(), c;
    while (true) {
      c = this._inputStream().read();
      if (c === 0xfc) {
        break;
      }
      buffer.push(c);
    }
    return buffer;
  };

  Smile.DecoderStream.prototype.readLongAscii = function() {
    return Smile.Decoder.decodeAscii(this.readLongString());
  };

  Smile.DecoderStream.prototype.readLongUtf8 = function() {
    return Smile.Decoder.decodeUtf8(this.readLongString());
  };

}(window.Smile = window.Smile || {}));
<commit_msg>Bugfix.<commit_after>/* global window, Uint8Array */

(function(Smile, undefined) {
  'use strict';

  var bitMask = [0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff];
  var shiftMultiplier = [1, 2, 4, 8, 16, 32, 64, 128];

  Smile.DecoderStream = function(inputStream) {
    this._inputStream = inputStream;
  };

  Smile.DecoderStream.prototype.isEof = function() {
    return this._inputStream.isEof();
  };

  Smile.DecoderStream.prototype.read = function() {
    return this._inputStream.read();
  };

  Smile.DecoderStream.prototype.peek = function() {
    return this._inputStream.peek();
  };

  Smile.DecoderStream.prototype.readUnsignedVint = function() {
    var value = 0,
      bits = 0;

    function safeLeftShift(n, shift) {
      if ((bits + shift) < 32) {
        value <<= shift;
        value |= n & bitMask[shift];
      } else {
        value *= shiftMultiplier[shift];
        value += n & bitMask[shift];
      }
      bits += shift;
    }

    while (true) {
      var n = this._inputStream.read();
      if (n & 0x80) {
        safeLeftShift(n, 6);
        return value;
      } else {
        safeLeftShift(n, 7);
      }
    }
  };

  Smile.DecoderStream.prototype.readSignedVint = function() {
    return Smile.Decoder.decodeZigZag(this.readUnsignedVint());
  };

  Smile.DecoderStream.prototype.readAscii = function(len) {
    return Smile.Decoder.decodeAscii(this._inputStream.readArray(len));
  };

  Smile.DecoderStream.prototype.readUtf8 = function(len) {
    return Smile.Decoder.decodeUtf8(this._inputStream.readArray(len));
  };

  Smile.DecoderStream.prototype.readFloat32 = function() {
    return Smile.Decoder.decodeFloat32(this.readFixedLengthBigEndianEncodedBits(32, true));
  };

  Smile.DecoderStream.prototype.readFloat64 = function() {
    return Smile.Decoder.decodeFloat64(this.readFixedLengthBigEndianEncodedBits(64, true));
  };

  Smile.DecoderStream.prototype.readFixedLengthBigEndianEncodedBits = function(bits, adjustEndianness) {
    var array = this._inputStream.readArray(Math.ceil(bits / 7));
    return Smile.Decoder.decodeFixedLengthBigEndianEncodedBits(array, bits, adjustEndianness);
  };

  Smile.DecoderStream.prototype.readSafeBinary = function() {
    var len = this.readUnsignedVint(),
      array = this._inputStream.readArray(Math.ceil(len * 8 / 7));
    return Smile.Decoder.decodeSafeBinaryEncodedBits(array, len * 8);
  };

  Smile.DecoderStream.prototype.readBigInt = function() {
    var array = this.readSafeBinary(),
      n = 0,
      i;
    for (i = 0; i < array.length; i++) {
      n = (n * 256) + array[i];
    }
    return n;
  };

  Smile.DecoderStream.prototype.readBigDecimal = function() {
    var scale = this.readSignedVint();
    var magnitude = this.readBigInt();
    return magnitude * Math.pow(10, scale);
  };

  Smile.DecoderStream.prototype.readLongString = function() {
    var buffer = [], c;
    while (true) {
      c = this._inputStream.read();
      if (c === 0xfc) {
        break;
      }
      buffer.push(c);
    }
    return buffer;
  };

  Smile.DecoderStream.prototype.readLongAscii = function() {
    return Smile.Decoder.decodeAscii(this.readLongString());
  };

  Smile.DecoderStream.prototype.readLongUtf8 = function() {
    return Smile.Decoder.decodeUtf8(this.readLongString());
  };

}(window.Smile = window.Smile || {}));
<|endoftext|>