<commit_before>module.exports = function(grunt) {
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		dirs: {
			htdocs:   'htdocs/',
			template: '<%= dirs.htdocs %>',
			images:   '<%= dirs.htdocs %>images/',
			docs:     'docs/',
			setup:    'setup/'
		},

		jshint: {
			build: {
				files: {
					src: [
						'<%= dirs.template %>js/*.js',
						'!<%= dirs.template %>js/*.min.js'
					]
				},
				options: { // see https://github.com/jshint/jshint/blob/master/examples/.jshintrc
					browser: true,
					jquery: true,
					strict: true,
					curly: true
				}
			}
		},

		uglify: {
			build: {
				 options: {
					// mangle: false,
					// beautify: true,
					// compress: false,
					maxLineLen: 9000,
					sourceMap: true
				},
				files: {
					'<%= dirs.template %>js/main.min.js': [
						'<%= dirs.template %>js/main.js', // add your vendors here
						'!<%= dirs.template %>js/main.min.js'
					]
				}
			}
		},

		sass: {
			build: {
				options: {
					style: 'compact'
				},
				files: [{
					expand: true,
					cwd: '<%= dirs.template %>sass',
					src: ['*.scss'],
					dest: '<%= dirs.template %>css',
					ext: '.css'
				}]
			}
		},

		postcss: {
			build: {
				options: {
					map: true,
					processors: [
						require('autoprefixer-core')({browsers: ['last 2 versions', '> 5%', 'ie 8', 'ie 9']})
					]
				},
				src: '<%= dirs.template %>css/*.css'
			},
			rtl: {
				options: {
					map: true,
					processors: [
						require('rtlcss')()
					]
				},
				src: '<%= dirs.template %>css/styles.css',
				dest: '<%= dirs.template %>css/rtl.css'
			}
		},

		/*compass: {
			build: {
				options: {
					basePath: '<%= dirs.template %>',
					config: '<%= dirs.template %>config.rb'
				}
			}
		},*/

		kss : {
			build: {
				files: {
					'<%= dirs.docs %>styleguide': '<%= dirs.template %>sass/'
				}
			}
		},

		replace: {
			oldie: {
				src: ['<%= dirs.template %>css/*.css'],
				dest: '<%= dirs.template %>css/oldie/',
				replacements: [
					{from: /\/\*#.+?\*\//g, to: ''},
					{from: /@media[^\{]+tty[^\{]+\{ (.+ \}) \}(\s*)/g, to: '$1$2'},
					{from: /(@media[^\{]+device-pixel-ratio[^\{]+\{ [\s\S]+? \} \}\s*)/g, to: ''},
					{from: /(@media screen) [^\{]+ \(max-width: \d+px\)( \{ [\s\S]+? \} \}\s*)/g, to: ''},
					{from: /(@media screen) and \(.+?\)( \{ [\s\S]+? \} \}\s*)/g, to: '$1$2'},
					{from: /-moz-[^\{]+?:.+?;\s*/g, to: ''},
					{from: /-webkit-[^\{]+?:.+?;\s*/g, to: ''},
					{from: /(transition|border-[\S]*radius):.+?;\s*/g, to: ''},
					{from: /opacity: 0;\s*/g, to: 'visibility: hidden; '},
					{from: /opacity: 1;\s*/g, to: 'visibility: visible; '},
					{from: /(rgba\(.+?),\s?[\d\.]+(\))/g, to: '$1$2'},
					{from: /\s\S+\s?\{\s+\}/g, to: ''},
					{from: /([\d\.]+)vw/g,  to: function (matchedWord, index, fullText, regexMatches) {
						return Math.round(parseFloat(regexMatches[0]) * 10.24) + 'px'; // matches 1024px
					}},
					{from: /([\d\.]+)vh/g,  to: function (matchedWord, index, fullText, regexMatches) {
						return Math.round(parseFloat(regexMatches[0]) * 7.68) + 'px'; // matches 768 px
					}},
					{from: /([\d\.]+)rem/g, to: function (matchedWord, index, fullText, regexMatches) {
						return Math.round(parseFloat(regexMatches[0]) * 12) + 'px'; // matches $fontsize-default
					}}
				]
			},
			notty: {
				src: ['<%= dirs.template %>css/*.css'],
				overwrite: true,
				replacements: [
					{from: /(@media[^\{]+tty[^\{]+\{ [\s\S]+? \} \}\s*)/g, to: ''}
				]
			},
			htmlrtl: {
				src: ['<%= dirs.template %>index.html'],
				dest: '<%= dirs.template %>rtl.html',
				replacements: [
					{from: / lang="de"/g, to: ' lang="ar" dir="rtl"'},
					{from: /styles\.css/g, to: 'rtl.css'}
				]
			},
			variables: {
				src: ['<%= dirs.template %>*.html', '<%= dirs.template %>*.xml'],
				overwrite: true,
				replacements: [
					{from: /\{\{ BASE_URL \}\}/g, to: '<?php echo(htmlspecialchars(\'http://\'.$_SERVER[\'HTTP_HOST\']));?>'},
					{from: /\{\{ SITENAME \}\}/g, to: '<?php echo(htmlspecialchars($sitename));?>'},
					{from: /\{\{ PAGE_RELATIVE_URL \}\}/g, to: '<?php echo(htmlspecialchars($_SERVER[\'SCRIPT_URL\']));?>'},
					{from: /\{\{ PAGE_TITLE \}\}/g, to: '<?php echo(htmlspecialchars($title));?>'},
					{from: /\{\{ PAGE_DESCRIPTION \}\}/g, to: '<?php echo(htmlspecialchars($description));?>'},
					{from: /\{\{ THEME_COLOR \}\}/g, to: '#aa00ff'}
				]
			}
		},

		image_resize: {
			options: {overwrite: true, upscale: true, crop: true, gravity: 'Center', quality: 0.7},
			fav32: {
				options: {width: 32},
				files:   {'<%= dirs.template %>favicon.ico':'<%= dirs.images %>logo.png'}
			},
			fav96: {
				options: {width: 96},
				files:   {'<%= dirs.template %>favicon-96x96.png':'<%= dirs.images %>logo.png'}
			},
			fav152: {
				options: {width: 152},
				files:   {'<%= dirs.template %>apple-touch-icon-precomposed.png':'<%= dirs.images %>logo.png'}
			},
			fav196: {
				options: {width: 196},
				files:   {'<%= dirs.template %>favicon-196x196.png':'<%= dirs.images %>logo.png'}
			},
			tile128: {
				options: {width: 128},
				files:   {'<%= dirs.images %>tile-128x128.png':'<%= dirs.images %>logo.png'}
			},
			tile270: {
				options: {width: 270},
				files:   {'<%= dirs.images %>tile-270x270.png':'<%= dirs.images %>logo.png'}
			},
			tilewide: {
				options: {width: 558, height:270},
				files:   {'<%= dirs.images %>tile-558x270.png':'<%= dirs.images %>logo.png'}
			},
			tile558: {
				options: {width: 558},
				files:   {'<%= dirs.images %>tile-558x558.png':'<%= dirs.images %>logo.png'}
			},
			article_images: {
				options: {width: 1280, height: 720, crop: true, gravity: 'Center'},
				src:  '<%= dirs.images %>originals/*.jpg',
				dest: '<%= dirs.images %>articles/'
			},
			article_images2: {
				options: {width: 2560, height: 1440, crop: true, gravity: 'Center'},
				src:  '<%= dirs.images %>originals/*.jpg',
				dest: '<%= dirs.images %>articles@2x/'
			}
		},

		shell: {
			deploy:  { command: '<%= dirs.setup %>deploy.sh'}
		},

		watch: {
			grunt: {
				files: ['Gruntfile.js']
			},
			sass: {
				options: {livereload: true},
				files: ['<%= dirs.template %>sass/**/*.scss'],
				tasks: ['build-sass']
			},
			js: {
				options: {livereload: true},
				files: ['<%= jshint.build.files.src %>'],
				tasks: ['build-js']
			},
			logo: {
				files: ['<%= dirs.images %>logo.png'],
				tasks: ['build-icons']
			},
			article_images: {
				files: ['<%= image_resize.article_images.src %>'],
				tasks: ['build-article-images']
			},
			livereload: {
				options: {livereload: true},
				files: ['<%= dirs.template %>*.html','<%= dirs.template %>images/*']
			}
		}
	});

	// Load the plugins
	require('jit-grunt')(grunt, {
		replace: 'grunt-text-replace',
		postcss: 'grunt-postcss'
	});

	// Default task(s).
	grunt.registerTask('build-sass',  ['sass','postcss','replace:oldie','replace:notty']);
	grunt.registerTask('build-js',    ['jshint','uglify']);
	grunt.registerTask('build-icons', ['image_resize:fav32','image_resize:fav96','image_resize:fav152','image_resize:fav196','image_resize:tile128','image_resize:tile270','image_resize:tilewide','image_resize:tile558']);
	grunt.registerTask('build-article-images', ['image_resize:article_images','image_resize:article_images2']);
	grunt.registerTask('default',     ['build-js','build-sass','build-icons']);
	grunt.registerTask('deploy',      ['shell:deploy']);
};
<commit_msg>Mini-improvement<commit_after>module.exports = function(grunt) {
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		dirs: {
			htdocs:   'htdocs/',
			template: '<%= dirs.htdocs %>',
			images:   '<%= dirs.htdocs %>images/',
			docs:     'docs/',
			setup:    'setup/'
		},

		jshint: {
			build: {
				files: {
					src: [
						'<%= dirs.template %>js/*.js',
						'!<%= dirs.template %>js/*.min.js'
					]
				},
				options: { // see https://github.com/jshint/jshint/blob/master/examples/.jshintrc
					browser: true,
					jquery: true,
					strict: true,
					curly: true
				}
			}
		},

		uglify: {
			build: {
				 options: {
					// mangle: false,
					// beautify: true,
					// compress: false,
					maxLineLen: 9000,
					sourceMap: true
				},
				files: {
					'<%= dirs.template %>js/main.min.js': [
						'<%= dirs.template %>js/main.js', // add your vendors here
						'!<%= dirs.template %>js/main.min.js'
					]
				}
			}
		},

		sass: {
			build: {
				options: {
					style: 'compact'
				},
				files: [{
					expand: true,
					cwd: '<%= dirs.template %>sass',
					src: ['*.scss'],
					dest: '<%= dirs.template %>css',
					ext: '.css'
				}]
			}
		},

		postcss: {
			build: {
				options: {
					map: true,
					processors: [
						require('autoprefixer-core')({browsers: ['last 2 versions', '> 5%', 'ie 8', 'ie 9']})
					]
				},
				src: '<%= dirs.template %>css/*.css'
			},
			rtl: {
				options: {
					map: true,
					processors: [
						require('rtlcss')()
					]
				},
				src: '<%= dirs.template %>css/styles.css',
				dest: '<%= dirs.template %>css/rtl.css'
			}
		},

		/*compass: {
			build: {
				options: {
					basePath: '<%= dirs.template %>',
					config: '<%= dirs.template %>config.rb'
				}
			}
		},*/

		kss : {
			build: {
				files: {
					'<%= dirs.docs %>styleguide': '<%= dirs.template %>sass/'
				}
			}
		},

		replace: {
			oldie: {
				src: ['<%= dirs.template %>css/*.css'],
				dest: '<%= dirs.template %>css/oldie/',
				replacements: [
					{from: /\/\*#.+?\*\//g, to: ''},
					{from: /@media[^\{]+tty[^\{]+\{ (.+ \}) \}(\s*)/g, to: '$1$2'},
					{from: /(@media[^\{]+device-pixel-ratio[^\{]+\{ [\s\S]+? \} \}\s*)/g, to: ''},
					{from: /(@media screen) [^\{]+ \(max-width: \d+px\)( \{ [\s\S]+? \} \}\s*)/g, to: ''},
					{from: /(@media screen) and \(.+?\)( \{ [\s\S]+? \} \}\s*)/g, to: '$1$2'},
					{from: /-(moz|webkit)-[^\{]+?:.+?;\s*/g, to: ''},
					{from: /(transition|border-[\S]*radius):.+?;\s*/g, to: ''},
					{from: /opacity: 0;\s*/g, to: 'visibility: hidden; '},
					{from: /opacity: 1;\s*/g, to: 'visibility: visible; '},
					{from: /(rgba\(.+?),\s?[\d\.]+(\))/g, to: '$1$2'},
					{from: /\s\S+\s?\{\s+\}/g, to: ''},
					{from: /([\d\.]+)vw/g,  to: function (matchedWord, index, fullText, regexMatches) {
						return Math.round(parseFloat(regexMatches[0]) * 10.24) + 'px'; // matches 1024px
					}},
					{from: /([\d\.]+)vh/g,  to: function (matchedWord, index, fullText, regexMatches) {
						return Math.round(parseFloat(regexMatches[0]) * 7.68) + 'px'; // matches 768 px
					}},
					{from: /([\d\.]+)rem/g, to: function (matchedWord, index, fullText, regexMatches) {
						return Math.round(parseFloat(regexMatches[0]) * 12) + 'px'; // matches $fontsize-default
					}}
				]
			},
			notty: {
				src: ['<%= dirs.template %>css/*.css'],
				overwrite: true,
				replacements: [
					{from: /(@media[^\{]+tty[^\{]+\{ [\s\S]+? \} \}\s*)/g, to: ''}
				]
			},
			htmlrtl: {
				src: ['<%= dirs.template %>index.html'],
				dest: '<%= dirs.template %>rtl.html',
				replacements: [
					{from: / lang="de"/g, to: ' lang="ar" dir="rtl"'},
					{from: /styles\.css/g, to: 'rtl.css'}
				]
			},
			variables: {
				src: ['<%= dirs.template %>*.html', '<%= dirs.template %>*.xml'],
				overwrite: true,
				replacements: [
					{from: /\{\{ BASE_URL \}\}/g, to: '<?php echo(htmlspecialchars(\'http://\'.$_SERVER[\'HTTP_HOST\']));?>'},
					{from: /\{\{ SITENAME \}\}/g, to: '<?php echo(htmlspecialchars($sitename));?>'},
					{from: /\{\{ PAGE_RELATIVE_URL \}\}/g, to: '<?php echo(htmlspecialchars($_SERVER[\'SCRIPT_URL\']));?>'},
					{from: /\{\{ PAGE_TITLE \}\}/g, to: '<?php echo(htmlspecialchars($title));?>'},
					{from: /\{\{ PAGE_DESCRIPTION \}\}/g, to: '<?php echo(htmlspecialchars($description));?>'},
					{from: /\{\{ THEME_COLOR \}\}/g, to: '#aa00ff'}
				]
			}
		},

		image_resize: {
			options: {overwrite: true, upscale: true, crop: true, gravity: 'Center', quality: 0.7},
			fav32: {
				options: {width: 32},
				files:   {'<%= dirs.template %>favicon.ico':'<%= dirs.images %>logo.png'}
			},
			fav96: {
				options: {width: 96},
				files:   {'<%= dirs.template %>favicon-96x96.png':'<%= dirs.images %>logo.png'}
			},
			fav152: {
				options: {width: 152},
				files:   {'<%= dirs.template %>apple-touch-icon-precomposed.png':'<%= dirs.images %>logo.png'}
			},
			fav196: {
				options: {width: 196},
				files:   {'<%= dirs.template %>favicon-196x196.png':'<%= dirs.images %>logo.png'}
			},
			tile128: {
				options: {width: 128},
				files:   {'<%= dirs.images %>tile-128x128.png':'<%= dirs.images %>logo.png'}
			},
			tile270: {
				options: {width: 270},
				files:   {'<%= dirs.images %>tile-270x270.png':'<%= dirs.images %>logo.png'}
			},
			tilewide: {
				options: {width: 558, height:270},
				files:   {'<%= dirs.images %>tile-558x270.png':'<%= dirs.images %>logo.png'}
			},
			tile558: {
				options: {width: 558},
				files:   {'<%= dirs.images %>tile-558x558.png':'<%= dirs.images %>logo.png'}
			},
			article_images: {
				options: {width: 1280, height: 720, crop: true, gravity: 'Center'},
				src:  '<%= dirs.images %>originals/*.jpg',
				dest: '<%= dirs.images %>articles/'
			},
			article_images2: {
				options: {width: 2560, height: 1440, crop: true, gravity: 'Center'},
				src:  '<%= dirs.images %>originals/*.jpg',
				dest: '<%= dirs.images %>articles@2x/'
			}
		},

		shell: {
			deploy:  { command: '<%= dirs.setup %>deploy.sh'}
		},

		watch: {
			grunt: {
				files: ['Gruntfile.js']
			},
			sass: {
				options: {livereload: true},
				files: ['<%= dirs.template %>sass/**/*.scss'],
				tasks: ['build-sass']
			},
			js: {
				options: {livereload: true},
				files: ['<%= jshint.build.files.src %>'],
				tasks: ['build-js']
			},
			logo: {
				files: ['<%= dirs.images %>logo.png'],
				tasks: ['build-icons']
			},
			article_images: {
				files: ['<%= image_resize.article_images.src %>'],
				tasks: ['build-article-images']
			},
			livereload: {
				options: {livereload: true},
				files: ['<%= dirs.template %>*.html','<%= dirs.template %>images/*']
			}
		}
	});

	// Load the plugins
	require('jit-grunt')(grunt, {
		replace: 'grunt-text-replace',
		postcss: 'grunt-postcss'
	});

	// Default task(s).
	grunt.registerTask('build-sass',  ['sass','postcss','replace:oldie','replace:notty']);
	grunt.registerTask('build-js',    ['jshint','uglify']);
	grunt.registerTask('build-icons', ['image_resize:fav32','image_resize:fav96','image_resize:fav152','image_resize:fav196','image_resize:tile128','image_resize:tile270','image_resize:tilewide','image_resize:tile558']);
	grunt.registerTask('build-article-images', ['image_resize:article_images','image_resize:article_images2']);
	grunt.registerTask('default',     ['build-js','build-sass','build-icons']);
	grunt.registerTask('deploy',      ['shell:deploy']);
};
<|endoftext|>


<commit_before>var Joi = require('joi'),
    P = require('bluebird'),
    _ = require('lodash');

module.exports = function(server, config, log) {
    "use strict";

    var dbName = config.get('plugins:restmod:dbname','MAIN');

    class BaseModel {

        constructor(data) {
            _.merge(this, data);
        }

        static get validation() {
            var params = {};
            params[this.constructor.idField] = Joi.string().required();
            return {
                create: {
                    payload: this.constructor.Schema
                },
                update: {
                    payload: this.constructor.Schema
                },
                upsert: {
                    payload: this.constructor.Schema
                },
                remove: {
                    params: params
                },
                show: {
                    params: params
                },
                list: {
                    query: {
                        offset: Joi.number().precision(0),
                        size: Joi.number().precision(0)
                    }
                }
            }
        }

        static get auth() {
            return "token";
        }

        static get db() {
            return server.plugins['covistra-mongodb'][dbName];
        }

        static get idField() {
            return "id";
        }

        // THESE METHODS MUST BE OVERRIDEN BY SUBCLASSES

        static get collection() {
            throw new Error("collection must be overriden for model "+this);
        }

        static get endpoint() {
            throw new Error("endpoint must be overriden for model "+this);
        }

        static get name() {
            throw new Error("name must be overriden for model "+this);
        }

        static get Schema() {
            return Joi.object();
        }

        static list(options) {
            log.debug("list %s", this.constructor.name);
            options = options || {};
            var coll = this.db.collection(this.collection);
            return coll.find(options.filter).toArray().then((results) => {
                if(options.wrap) {
                    return P.map(results, function(r){ return this.wrap(r)});
                }
                else {
                    return results;
                }
            });
        }

        static show(id) {
            log.debug("show %s ", this.name, id);
            var _this = this;
            var coll = this.db.collection(this.collection);
            var q = {};
            q[this.idField] = id;
            return coll.findOne(q).then(function(data) {
                return _this.wrap(data);
            });
        }

        static wrap(data) {
            if(!(data instanceof this)) {
                return new this(data);
            }
            else
                return data;
        }

        /**
         * Helpful wrapper to create instances we know for sure doesn't exists
         * @returns {*}
         */
        create() {
            return this.save(null,{upsert: true});
        }

        save(data, options) {
            data = data || this;
            log.debug("Save", data[this.constructor.idField] || this[this.constructor.idField]);
            options = options || {upsert: false};
            var val = Joi.validate(data, this.constructor.Schema);
            if(val.error) {
                throw val.error;
            }
            else {
                // Update our internal values
                var fields = _.omit(this.toJSON(), this.constructor.idField);
                var q = {};
                q[this.constructor.idField] = this[this.constructor.idField];

                log.debug("Retrieving collection %s from db", this.constructor.collection, this.constructor.db);
                var coll = this.constructor.db.collection(this.constructor.collection);
                return coll.updateOne(q, {$set: fields }, { upsert: options.upsert});
            }
        }

        static remove(id) {
            log.debug("Remove", id);
            var coll = this.db.collection(this.collection);
            var q= {};
            q[this.idField] = id;
            return coll.removeOne(q);
        }

        remove() {
            log.debug("Remove", this[this.constructor.idField]);
            var coll = this.constructor.db.collection(this.constructor.collection);
            var q= {};
            q[this.constructor.idField] = this[this.constructor.idField];
            return coll.removeOne(q);
        }

        toJSON() {
            var val = Joi.validate(this, this.constructor.Schema);
            if(val.error) {
                throw val.error;
            }
            else {
                return val.value;
            }
        };

        isUnique() {
            var coll = this.constructor.db.collection(this.constructor.collection);
            var q= {};
            q[this.constructor.idField] = this[this.constructor.idField];
            return coll.findOne(q).then(function(existing) {
                return !existing;
            });
        };

    }

    return BaseModel;
};
<commit_msg>Make that validation assumes params is always named id<commit_after>var Joi = require('joi'),
    P = require('bluebird'),
    _ = require('lodash');

module.exports = function(server, config, log) {
    "use strict";

    var dbName = config.get('plugins:restmod:dbname','MAIN');

    class BaseModel {

        constructor(data) {
            _.merge(this, data);
        }

        static get validation() {
            var params = {};
            params.id = Joi.string().required();
            return {
                create: {
                    payload: this.constructor.Schema
                },
                update: {
                    payload: this.constructor.Schema
                },
                upsert: {
                    payload: this.constructor.Schema
                },
                remove: {
                    params: params
                },
                show: {
                    params: params
                },
                list: {
                    query: {
                        filter: Joi.string(),
                        offset: Joi.number().precision(0),
                        size: Joi.number().precision(0)
                    }
                }
            }
        }

        static get auth() {
            return "token";
        }

        static get db() {
            return server.plugins['covistra-mongodb'][dbName];
        }

        static get idField() {
            return "id";
        }

        // THESE METHODS MUST BE OVERRIDEN BY SUBCLASSES

        static get collection() {
            throw new Error("collection must be overriden for model "+this);
        }

        static get endpoint() {
            throw new Error("endpoint must be overriden for model "+this);
        }

        static get name() {
            throw new Error("name must be overriden for model "+this);
        }

        static get Schema() {
            return Joi.object();
        }

        static list(options) {
            log.debug("list %s", this.constructor.name);
            options = options || {};
            var coll = this.db.collection(this.collection);
            return coll.find(options.filter).toArray().then((results) => {
                if(options.wrap) {
                    return P.map(results, function(r){ return this.wrap(r)});
                }
                else {
                    return results;
                }
            });
        }

        static show(id) {
            log.debug("show %s ", this.name, id);
            var _this = this;
            var coll = this.db.collection(this.collection);
            var q = {};
            q[this.idField] = id;
            return coll.findOne(q).then(function(data) {
                return _this.wrap(data);
            });
        }

        static wrap(data) {
            if(!(data instanceof this)) {
                return new this(data);
            }
            else
                return data;
        }

        /**
         * Helpful wrapper to create instances we know for sure doesn't exists
         * @returns {*}
         */
        create() {
            return this.save(null,{upsert: true});
        }

        save(data, options) {
            data = data || this;
            log.debug("Save", data[this.constructor.idField] || this[this.constructor.idField]);
            options = options || {upsert: false};
            var val = Joi.validate(data, this.constructor.Schema);
            if(val.error) {
                throw val.error;
            }
            else {
                // Update our internal values
                var fields = _.omit(this.toJSON(), this.constructor.idField);
                var q = {};
                q[this.constructor.idField] = this[this.constructor.idField];

                log.debug("Retrieving collection %s from db", this.constructor.collection, this.constructor.db);
                var coll = this.constructor.db.collection(this.constructor.collection);
                return coll.updateOne(q, {$set: fields }, { upsert: options.upsert});
            }
        }

        static remove(id) {
            log.debug("Remove", id);
            var coll = this.db.collection(this.collection);
            var q= {};
            q[this.idField] = id;
            return coll.removeOne(q);
        }

        remove() {
            log.debug("Remove", this[this.constructor.idField]);
            var coll = this.constructor.db.collection(this.constructor.collection);
            var q= {};
            q[this.constructor.idField] = this[this.constructor.idField];
            return coll.removeOne(q);
        }

        toJSON() {
            var val = Joi.validate(this, this.constructor.Schema);
            if(val.error) {
                throw val.error;
            }
            else {
                return val.value;
            }
        };

        isUnique() {
            var coll = this.constructor.db.collection(this.constructor.collection);
            var q= {};
            q[this.constructor.idField] = this[this.constructor.idField];
            return coll.findOne(q).then(function(existing) {
                return !existing;
            });
        };

    }

    return BaseModel;
};
<|endoftext|>


<commit_before>import classNames from 'classnames';
import {Link} from 'react-router';
import React from 'react';

import CheckboxTable from './CheckboxTable';
import CompositeState from '../structs/CompositeState';
import Icon from './Icon';
import DCOSStore from '../stores/DCOSStore';
import ResourceTableUtil from '../utils/ResourceTableUtil';
import TaskStates from '../constants/TaskStates';
import TaskEndpointsList from './TaskEndpointsList';
import TaskTableHeaderLabels from '../constants/TaskTableHeaderLabels';
import TaskUtil from '../utils/TaskUtil';
import TableUtil from '../utils/TableUtil';
import Units from '../utils/Units';

const METHODS_TO_BIND = [
  'renderHeadline',
  'renderHost',
  'renderLog',
  'renderStatus',
  'renderStats',
  'renderVersion'
];

const RIGHT_ALIGN_PROPS = ['cpus', 'disk', 'log', 'mem', 'version'];

class TaskTable extends React.Component {
  constructor() {
    super(...arguments);

    METHODS_TO_BIND.forEach(function (method) {
      this[method] = this[method].bind(this);
    }, this);
  }

  getStatValue(task, prop) {
    return task.resources[prop];
  }

  getStatusValue(task) {
    return TaskStates[task.state].displayName;
  }

  getVersionValue(task) {
    let marathonTask = DCOSStore.serviceTree.getTaskFromTaskID(task.id);
    if (marathonTask == null) {
      return null;
    }

    return marathonTask.version;
  }

  getClassName(prop, sortBy, row) {
    return classNames({
      'text-align-right': RIGHT_ALIGN_PROPS.includes(prop),
      'hidden-mini': ['host', 'status', 'cpus', 'mem'].includes(prop),
      'hidden-medium hidden-small hidden-mini':
        ['version', 'log'].includes(prop),
      'highlight': prop === sortBy.prop,
      'clickable': row == null // this is a header
    });
  }

  getColumns() {
    var className = this.getClassName;
    var heading = ResourceTableUtil.renderHeading(TaskTableHeaderLabels);
    let sortFunction = ResourceTableUtil.getSortFunction('id');

    return [
      {
        className,
        headerClassName: className,
        heading,
        prop: 'id',
        render: this.renderHeadline,
        sortable: true,
        sortFunction
      },
      {
        className,
        headerClassName: className,
        heading,
        prop: 'host',
        render: this.renderHost,
        sortable: true,
        sortFunction
      },
      {
        cacheCell: true,
        className,
        getValue: this.getStatusValue,
        headerClassName: className,
        heading,
        prop: 'status',
        render: this.renderStatus,
        sortable: true,
        sortFunction
      },
      {
        cacheCell: true,
        className,
        headerClassName: className,
        heading,
        prop: 'log',
        render: this.renderLog,
        sortable: false,
        sortFunction
      },
      {
        cacheCell: true,
        className,
        getValue: this.getStatValue,
        headerClassName: className,
        heading,
        prop: 'cpus',
        render: this.renderStats,
        sortable: true,
        sortFunction
      },
      {
        cacheCell: true,
        className,
        getValue: this.getStatValue,
        headerClassName: className,
        heading,
        prop: 'mem',
        render: this.renderStats,
        sortable: true,
        sortFunction
      },
      {
        className,
        headerClassName: className,
        heading,
        prop: 'updated',
        render: ResourceTableUtil.renderUpdated,
        sortable: true,
        sortFunction
      },
      {
        cacheCell: true,
        className,
        getValue: this.getVersionValue,
        headerClassName: className,
        heading,
        prop: 'version',
        render: this.renderVersion,
        sortable: true,
        sortFunction: TableUtil.getSortFunction('id', (task) => {
          let version = this.getVersionValue(task);
          if (version == null) {
            return null;
          }

          return new Date(version);
        })
      }
    ];
  }

  getColGroup() {
    return (
      <colgroup>
        <col style={{width: '40px'}} />
        <col />
        <col style={{width: '15%'}} className="hidden-mini" />
        <col style={{width: '105px'}} />
        <col style={{width: '40px'}} className="hidden-medium hidden-small hidden-mini" />
        <col style={{width: '85px'}} className="hidden-mini" />
        <col style={{width: '85px'}} className="hidden-mini" />
        <col style={{width: '120px'}} />
        <col style={{width: '110px'}} className="hidden-medium hidden-small hidden-mini"/>
      </colgroup>
    );
  }

  renderHeadline(prop, task) {
    let title = task.id;
    let params = this.props.parentRouter.getCurrentParams();
    let routeParams = Object.assign({taskID: task.id}, params);

    let linkTo = 'services-task-details';
    if (params.nodeID != null) {
      linkTo = 'nodes-task-details';
    }

    return (

          <Link
            className="emphasize clickable text-overflow"
            to={linkTo}
            params={routeParams}
            title={title}>
            {title}
          </Link>

    );
  }

  renderLog(prop, task) {
    let title = task.name || task.id;
    let params = this.props.parentRouter.getCurrentParams();
    let routeParams = Object.assign({taskID: task.id}, params);

    let linkTo = 'services-task-details-logs';
    if (params.nodeID != null) {
      linkTo = 'nodes-task-details-logs';
    }

    return (
      <Link
        className="emphasize clickable text-overflow"
        to={linkTo}
        params={routeParams}
        title={title}>
        <Icon color="grey" id="page" size="mini" family="mini" />
      </Link>
    );
  }

  renderHost(prop, task) {
    let node = CompositeState.getNodesList()
      .filter({ids: [task.slave_id]}).last();

    return (
      <TaskEndpointsList
        key={task.id}
        portLimit={3}
        node={node}
        task={task} />
    );
  }

  renderStats(prop, task) {
    return (

        {Units.formatResource(prop, this.getStatValue(task, prop))}

    );
  }

  renderStatus(prop, task) {
    let statusClassName = TaskUtil.getTaskStatusClassName(task);
    let statusLabelClasses = `${statusClassName} table-cell-value`;

    let {state} = task;

    let dangerState = TaskStates[state].stateTypes.includes('failure');

    let healthy = task.statuses.some(function (status) {
      return status.healthy;
    });

    let unknown = task.statuses.length === 0 || task.statuses.some(function (status) {
      return status.healthy == null;
    });

    let activeState = TaskStates[state].stateTypes.includes('active');

    let running = ['TASK_RUNNING', 'TASK_STARTING'].includes(state) && unknown;
    let success = healthy && state === 'TASK_RUNNING';
    let danger = (dangerState && !activeState &&
      ['TASK_ERROR', 'TASK_FAILED'].includes(state)) ||
      (healthy === false && task.statuses.length !== 0);

    let statusClass = classNames({
      'dot': true,
      'inactive': !activeState,
      'success': success,
      'running': running,
      'danger': danger
    });

    return (

          {this.getStatusValue(task)}

    );
  }

  renderVersion(prop, task) {
    let version = this.getVersionValue(task);

    if (version == null) {
      return null;
    }

    let localeVersion = new Date(version).toLocaleString();

    return (

        {localeVersion}

    );
  }

  render() {
    let {checkedItemsMap, className, onCheckboxChange, tasks} = this.props;

    return (
      <CheckboxTable
        checkedItemsMap={checkedItemsMap}
        className={className}
        columns={this.getColumns()}
        data={tasks.slice()}
        getColGroup={this.getColGroup}
        onCheckboxChange={onCheckboxChange}
        sortBy={{prop: 'updated', order: 'desc'}}
        sortOrder="desc"
        sortProp="updated"
        uniqueProperty="id" />
    );
  }
}

TaskTable.propTypes = {
  checkedItemsMap: React.PropTypes.object,
  className: React.PropTypes.string,
  onCheckboxChange: React.PropTypes.func,
  parentRouter: React.PropTypes.oneOfType([
    React.PropTypes.func,
    React.PropTypes.object
  ]).isRequired,
  tasks: React.PropTypes.array.isRequired
};

TaskTable.defaultProps = {
  className: 'table table-borderless-outer table-borderless-inner-columns flush-bottom',
  tasks: []
};

module.exports = TaskTable;
<commit_msg>Displaying hostname under host, and not endpoints<commit_after>import classNames from 'classnames';
import {Link} from 'react-router';
import React from 'react';

import CheckboxTable from './CheckboxTable';
import CompositeState from '../structs/CompositeState';
import Icon from './Icon';
import DCOSStore from '../stores/DCOSStore';
import ResourceTableUtil from '../utils/ResourceTableUtil';
import TaskStates from '../constants/TaskStates';
import TaskTableHeaderLabels from '../constants/TaskTableHeaderLabels';
import TaskUtil from '../utils/TaskUtil';
import TableUtil from '../utils/TableUtil';
import Units from '../utils/Units';

const METHODS_TO_BIND = [
  'renderHeadline',
  'renderHost',
  'renderLog',
  'renderStatus',
  'renderStats',
  'renderVersion'
];

const RIGHT_ALIGN_PROPS = ['cpus', 'disk', 'log', 'mem', 'version'];

class TaskTable extends React.Component {
  constructor() {
    super(...arguments);

    METHODS_TO_BIND.forEach(function (method) {
      this[method] = this[method].bind(this);
    }, this);
  }

  getStatValue(task, prop) {
    return task.resources[prop];
  }

  getStatusValue(task) {
    return TaskStates[task.state].displayName;
  }

  getVersionValue(task) {
    let marathonTask = DCOSStore.serviceTree.getTaskFromTaskID(task.id);
    if (marathonTask == null) {
      return null;
    }

    return marathonTask.version;
  }

  getClassName(prop, sortBy, row) {
    return classNames({
      'text-align-right': RIGHT_ALIGN_PROPS.includes(prop),
      'hidden-mini': ['host', 'status', 'cpus', 'mem'].includes(prop),
      'hidden-medium hidden-small hidden-mini':
        ['version', 'log'].includes(prop),
      'highlight': prop === sortBy.prop,
      'clickable': row == null // this is a header
    });
  }

  getColumns() {
    var className = this.getClassName;
    var heading = ResourceTableUtil.renderHeading(TaskTableHeaderLabels);
    let sortFunction = ResourceTableUtil.getSortFunction('id');

    return [
      {
        className,
        headerClassName: className,
        heading,
        prop: 'id',
        render: this.renderHeadline,
        sortable: true,
        sortFunction
      },
      {
        className,
        headerClassName: className,
        heading,
        prop: 'host',
        render: this.renderHost,
        sortable: true,
        sortFunction
      },
      {
        cacheCell: true,
        className,
        getValue: this.getStatusValue,
        headerClassName: className,
        heading,
        prop: 'status',
        render: this.renderStatus,
        sortable: true,
        sortFunction
      },
      {
        cacheCell: true,
        className,
        headerClassName: className,
        heading,
        prop: 'log',
        render: this.renderLog,
        sortable: false,
        sortFunction
      },
      {
        cacheCell: true,
        className,
        getValue: this.getStatValue,
        headerClassName: className,
        heading,
        prop: 'cpus',
        render: this.renderStats,
        sortable: true,
        sortFunction
      },
      {
        cacheCell: true,
        className,
        getValue: this.getStatValue,
        headerClassName: className,
        heading,
        prop: 'mem',
        render: this.renderStats,
        sortable: true,
        sortFunction
      },
      {
        className,
        headerClassName: className,
        heading,
        prop: 'updated',
        render: ResourceTableUtil.renderUpdated,
        sortable: true,
        sortFunction
      },
      {
        cacheCell: true,
        className,
        getValue: this.getVersionValue,
        headerClassName: className,
        heading,
        prop: 'version',
        render: this.renderVersion,
        sortable: true,
        sortFunction: TableUtil.getSortFunction('id', (task) => {
          let version = this.getVersionValue(task);
          if (version == null) {
            return null;
          }

          return new Date(version);
        })
      }
    ];
  }

  getColGroup() {
    return (
      <colgroup>
        <col style={{width: '40px'}} />
        <col />
        <col style={{width: '15%'}} className="hidden-mini" />
        <col style={{width: '105px'}} />
        <col style={{width: '40px'}} className="hidden-medium hidden-small hidden-mini" />
        <col style={{width: '85px'}} className="hidden-mini" />
        <col style={{width: '85px'}} className="hidden-mini" />
        <col style={{width: '120px'}} />
        <col style={{width: '110px'}} className="hidden-medium hidden-small hidden-mini"/>
      </colgroup>
    );
  }

  renderHeadline(prop, task) {
    let title = task.id;
    let params = this.props.parentRouter.getCurrentParams();
    let routeParams = Object.assign({taskID: task.id}, params);

    let linkTo = 'services-task-details';
    if (params.nodeID != null) {
      linkTo = 'nodes-task-details';
    }

    return (

          <Link
            className="emphasize clickable text-overflow"
            to={linkTo}
            params={routeParams}
            title={title}>
            {title}
          </Link>

    );
  }

  renderLog(prop, task) {
    let title = task.name || task.id;
    let params = this.props.parentRouter.getCurrentParams();
    let routeParams = Object.assign({taskID: task.id}, params);

    let linkTo = 'services-task-details-logs';
    if (params.nodeID != null) {
      linkTo = 'nodes-task-details-logs';
    }

    return (
      <Link
        className="emphasize clickable text-overflow"
        to={linkTo}
        params={routeParams}
        title={title}>
        <Icon color="grey" id="page" size="mini" family="mini" />
      </Link>
    );
  }

  renderHost(prop, task) {
    let node = CompositeState.getNodesList()
      .filter({ids: [task.slave_id]}).last();

    if (!node) {
      return 'N/A';
    }

    return (
      <Link
        className="emphasize clickable text-overflow"
        to="node-detail"
        params={{nodeID: task.slave_id}}
        title={node.hostname}>
        {node.hostname}
      </Link>
    );
  }

  renderStats(prop, task) {
    return (

        {Units.formatResource(prop, this.getStatValue(task, prop))}

    );
  }

  renderStatus(prop, task) {
    let statusClassName = TaskUtil.getTaskStatusClassName(task);
    let statusLabelClasses = `${statusClassName} table-cell-value`;

    let {state} = task;

    let dangerState = TaskStates[state].stateTypes.includes('failure');

    let healthy = task.statuses.some(function (status) {
      return status.healthy;
    });

    let unknown = task.statuses.length === 0 || task.statuses.some(function (status) {
      return status.healthy == null;
    });

    let activeState = TaskStates[state].stateTypes.includes('active');

    let running = ['TASK_RUNNING', 'TASK_STARTING'].includes(state) && unknown;
    let success = healthy && state === 'TASK_RUNNING';
    let danger = (dangerState && !activeState &&
      ['TASK_ERROR', 'TASK_FAILED'].includes(state)) ||
      (healthy === false && task.statuses.length !== 0);

    let statusClass = classNames({
      'dot': true,
      'inactive': !activeState,
      'success': success,
      'running': running,
      'danger': danger
    });

    return (

          {this.getStatusValue(task)}

    );
  }

  renderVersion(prop, task) {
    let version = this.getVersionValue(task);

    if (version == null) {
      return null;
    }

    let localeVersion = new Date(version).toLocaleString();

    return (

        {localeVersion}

    );
  }

  render() {
    let {checkedItemsMap, className, onCheckboxChange, tasks} = this.props;

    return (
      <CheckboxTable
        checkedItemsMap={checkedItemsMap}
        className={className}
        columns={this.getColumns()}
        data={tasks.slice()}
        getColGroup={this.getColGroup}
        onCheckboxChange={onCheckboxChange}
        sortBy={{prop: 'updated', order: 'desc'}}
        sortOrder="desc"
        sortProp="updated"
        uniqueProperty="id" />
    );
  }
}

TaskTable.propTypes = {
  checkedItemsMap: React.PropTypes.object,
  className: React.PropTypes.string,
  onCheckboxChange: React.PropTypes.func,
  parentRouter: React.PropTypes.oneOfType([
    React.PropTypes.func,
    React.PropTypes.object
  ]).isRequired,
  tasks: React.PropTypes.array.isRequired
};

TaskTable.defaultProps = {
  className: 'table table-borderless-outer table-borderless-inner-columns flush-bottom',
  tasks: []
};

module.exports = TaskTable;
<|endoftext|>


<commit_before>/*
 * mockserver
 * http://mock-server.com
 *
 * Copyright (c) 2014 James Bloom
 * Licensed under the Apache License, Version 2.0
 */

'use strict';

module.exports = function (grunt) {

    grunt.initConfig({
        exec: {
            stop_existing_mockservers: './stop_MockServer.sh'
        },
        jshint: {
            all: [
                'Gruntfile.js',
                'tasks/*.js',
                '<%= nodeunit.started %>',
                '<%= nodeunit.stopped %>'
            ],
            options: {
                jshintrc: '.jshintrc'
            }
        },
        start_mockserver: {
            start: {
                options: {
                    serverPort: 1080,
                    proxyPort: 1090
                }
            }
        },
        stop_mockserver: {
            stop: {

            }
        },
        nodeunit: {
            started: [
                'test/started/*_test.js'
            ],
            stopped: [
                'test/stopped/*_test.js'
            ],
            options: {
                reporter: 'nested'
            }
        }
    });

    grunt.registerTask('download_jar', 'Download latest MockServer jar version', function () {
        var done = this.async();
        require('./downloadJar').downloadJar('3.9.3').then(function () {
            done(true);
        }, function () {
            done(false);
        });
    });

    // load this plugin's task
    grunt.loadTasks('tasks');

    grunt.loadNpmTasks('grunt-exec');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-nodeunit');

    grunt.registerTask('test', ['start_mockserver:start', 'nodeunit:started', 'stop_mockserver:stop', 'nodeunit:stopped']);

    grunt.registerTask('wrecker', ['download_jar', 'jshint', 'test']);
    grunt.registerTask('default', ['exec', 'wrecker']);
};
<commit_msg>fixing download setting in build file<commit_after>/*
 * mockserver
 * http://mock-server.com
 *
 * Copyright (c) 2014 James Bloom
 * Licensed under the Apache License, Version 2.0
 */

'use strict';

module.exports = function (grunt) {

    grunt.initConfig({
        exec: {
            stop_existing_mockservers: './stop_MockServer.sh'
        },
        jshint: {
            all: [
                'Gruntfile.js',
                'tasks/*.js',
                '<%= nodeunit.started %>',
                '<%= nodeunit.stopped %>'
            ],
            options: {
                jshintrc: '.jshintrc'
            }
        },
        start_mockserver: {
            start: {
                options: {
                    serverPort: 1080,
                    proxyPort: 1090
                }
            }
        },
        stop_mockserver: {
            stop: {

            }
        },
        nodeunit: {
            started: [
                'test/started/*_test.js'
            ],
            stopped: [
                'test/stopped/*_test.js'
            ],
            options: {
                reporter: 'nested'
            }
        }
    });

    grunt.registerTask('download_jar', 'Download latest MockServer jar version', function () {
        var done = this.async();
        require('./downloadJar').downloadJar('3.9.6').then(function () {
            done(true);
        }, function () {
            done(false);
        });
    });

    // load this plugin's task
    grunt.loadTasks('tasks');

    grunt.loadNpmTasks('grunt-exec');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-nodeunit');

    grunt.registerTask('test', ['start_mockserver:start', 'nodeunit:started', 'stop_mockserver:stop', 'nodeunit:stopped']);

    grunt.registerTask('wrecker', ['download_jar', 'jshint', 'test']);
    grunt.registerTask('default', ['exec', 'wrecker']);
};
<|endoftext|>


<commit_before>var shortener = require('../lib/shortener');

exports['urlshorener goo.gle'] = function (test) {
	var url = 'http://www.google.com';
	var key = 'KEY';
	var s = new shortener.Shortener();
	s.google(url, key, function(result) {
		test.deepEqual(result
					   , {
						   shortUrl: 'http://goo.gl/fbsS'
						   , longUrl: 'http://www.google.com/'
					   }
					   , "Deep equal for www.goo.gl");
    test.done();
	});
};

exports['urlshorener bit.ly'] = function (test) {
  	var url = 'http://bit.ly';
  	var key = 'KEY';
  	var format = 'json';
  	var s = new shortener.Shortener();
  	s.bitly(url, key, format, function(result) {
  		test.deepEqual(result
                  		, {
                  			shortUrl: 'http://bit.ly/wq5Qeg'
                  			, longUrl: 'http://bit.ly/'
                  		}
                  		, "Deep equal for bit.ly");
    test.done();
	});
};

exports['urlshorener readability'] = function (test) {
  	var url = 'https://www.readability.com/';
  	var s = new shortener.Shortener();
  	s.bitly(url, function(result) {
  		test.deepEqual(result
  						, {
                   			shortUrl: 'http://rdd.me/-6tqmqpqj'
                   			, longUrl: 'https://www.readability.com/'
                   		}
                   		, "Deep equal for bit.ly");
    test.done();
	});
};<commit_msg>Fix indetation<commit_after>var shortener = require('../lib/shortener');

exports['urlshorener goo.gle'] = function (test) {
	var url = 'http://www.google.com';
	var key = 'KEY';
	var s = new shortener.Shortener();
	s.google(url, key, function(result) {
		test.deepEqual(result
					   , {
						   shortUrl: 'http://goo.gl/fbsS'
						   , longUrl: 'http://www.google.com/'
					   }
					   , "Deep equal for www.goo.gl");
    test.done();
	});
};

exports['urlshorener bit.ly'] = function (test) {
  	var url = 'http://bit.ly';
  	var key = 'KEY';
  	var format = 'json';
  	var s = new shortener.Shortener();
  	s.bitly(url, key, format, function(result) {
  		test.deepEqual(result
					   , {
						   shortUrl: 'http://bit.ly/wq5Qeg'
						   , longUrl: 'http://bit.ly/'
					   }
					   , "Deep equal for bit.ly");
    test.done();
	});
};

exports['urlshorener readability'] = function (test) {
  	var url = 'https://www.readability.com/';
  	var s = new shortener.Shortener();
  	s.bitly(url, function(result) {
  		test.deepEqual(result
					   , {
						   shortUrl: 'http://rdd.me/-6tqmqpqj'
						   , longUrl: 'https://www.readability.com/'
					   }
					   , "Deep equal for bit.ly");
    test.done();
	});
};<|endoftext|>


<commit_before>var _ = require('lodash');
var urljoin = require('url-join');
var debug = require('debug')('4front-apphost:custom-errors');

// Middleware for declaring custom error pages for different
// http status codes.
// Configure in the _virtualApp section of package.json like so:
// router: [
//   {
//     module: "custom-errors",
//     options: {
//       errors: {
//         500: "errors/500.html",
//         404: "errors/404.html",
//         401: "errors.401.html"
//       }
//     }
//   }
// ]
module.exports = function(options) {
  options = _.defaults(options || {}, {
    errors: {} // This is a map of status codes to custom error pages
  });

  return function(err, req, res, next) {
    // Some errors should not show a custom error page. For example the
    // dev-sandbox sets this property to true.
    if (err.bypassCustomErrorPage === true)
      return next(err);

    // Can't render a custom error page if the virtualApp
    // or virtualAppVersion is not known.
    if (!req.ext.virtualApp || !req.ext.virtualAppVersion || !err.status)
      return next(err);

    // Look for a custom error page specific to this statusCode
    req.ext.webPagePath = options.errors[err.status] || options.errors[err.status.toString()];
    if (!req.ext.webPagePath) {
      debug("no custom error page for status code %s", err.status);
      return next(err);
    }

    if (_.isFunction(req.ext.loadPageMiddleware)) {
      req.ext.loadPageMiddleware(req, res, function(streamErr) {
        // Important that we call next with the original err, not streamErr.
        if (_.isError(streamErr)) return next(streamErr);

        streamResponse();
      });
    }
    else {
      // Load the custom error page from deployments
      req.ext.webPageStream = req.app.settings.storage.readFileStream(
        urljoin(req.ext.virtualApp.appId,
          req.ext.virtualAppVersion.versionId,
          req.ext.webPagePath));

      streamResponse();
    }

    function streamResponse() {
      // If the custom error page is missing proceed to the fallback error middleware
      req.ext.webPageStream.on('missing', function() {
        debug('custom error page %s not found', req.ext.webPagePath);
        // If the requested page was not found, set the status code
        // and advance to the next middleware. If the custom-errors
        // route is configured, it will handle returning the
        // custom error page. Important that we call next with the
        // original error, not streamErr
        return next(err);
      })
      .on('readError', function(streamErr) {
        debug("error reading page stream", streamErr.stack);
        // Important that we call next with the original error, not streamErr
        return next(err);
      });

      // Send the error to the logger
      req.app.settings.logger.error(err, req);

      // If there is an error code, write it as a header
      if (err.code)
        res.set('Error-Code', err.code);

      res.set('Content-Type', 'text/html');
      res.set('Cache-Control', 'no-cache');
      res.status(err.status);

      // Pipe the custom error page to the response
      debug("piping page to response");

      req.ext.webPageStream.pipe(res);
    }
  };
};
<commit_msg>Only log error from custom-errors plugin if status is greater than 500<commit_after>var _ = require('lodash');
var urljoin = require('url-join');
var debug = require('debug')('4front-apphost:custom-errors');

// Middleware for declaring custom error pages for different
// http status codes.
// Configure in the _virtualApp section of package.json like so:
// router: [
//   {
//     module: "custom-errors",
//     options: {
//       errors: {
//         500: "errors/500.html",
//         404: "errors/404.html",
//         401: "errors.401.html"
//       }
//     }
//   }
// ]
module.exports = function(options) {
  options = _.defaults(options || {}, {
    errors: {} // This is a map of status codes to custom error pages
  });

  return function(err, req, res, next) {
    // Some errors should not show a custom error page. For example the
    // dev-sandbox sets this property to true.
    if (err.bypassCustomErrorPage === true)
      return next(err);

    // Can't render a custom error page if the virtualApp
    // or virtualAppVersion is not known.
    if (!req.ext.virtualApp || !req.ext.virtualAppVersion || !err.status)
      return next(err);

    // Look for a custom error page specific to this statusCode
    req.ext.webPagePath = options.errors[err.status] || options.errors[err.status.toString()];
    if (!req.ext.webPagePath) {
      debug("no custom error page for status code %s", err.status);
      return next(err);
    }

    if (_.isFunction(req.ext.loadPageMiddleware)) {
      req.ext.loadPageMiddleware(req, res, function(streamErr) {
        // Important that we call next with the original err, not streamErr.
        if (_.isError(streamErr)) return next(streamErr);

        streamResponse();
      });
    }
    else {
      // Load the custom error page from deployments
      req.ext.webPageStream = req.app.settings.storage.readFileStream(
        urljoin(req.ext.virtualApp.appId,
          req.ext.virtualAppVersion.versionId,
          req.ext.webPagePath));

      streamResponse();
    }

    function streamResponse() {
      // If the custom error page is missing proceed to the fallback error middleware
      req.ext.webPageStream.on('missing', function() {
        debug('custom error page %s not found', req.ext.webPagePath);
        // If the requested page was not found, set the status code
        // and advance to the next middleware. If the custom-errors
        // route is configured, it will handle returning the
        // custom error page. Important that we call next with the
        // original error, not streamErr
        return next(err);
      })
      .on('readError', function(streamErr) {
        debug("error reading page stream", streamErr.stack);
        // Important that we call next with the original error, not streamErr
        return next(err);
      });

      // Send the error to the logger. We don't need to log 400 and 404 errors
      if (err.status >= 500)
        req.app.settings.logger.error(err, req);

      // If there is an error code, write it as a header
      if (err.code)
        res.set('Error-Code', err.code);

      res.set('Content-Type', 'text/html');
      res.set('Cache-Control', 'no-cache');
      res.status(err.status);

      // Pipe the custom error page to the response
      debug("piping page to response");

      req.ext.webPageStream.pipe(res);
    }
  };
};
<|endoftext|>


<commit_before>// ┌─────────────┐
// │ Gruntfile   │
// └─────────────┘
// Grunt wraps several tasks to ease development
// runs middleman, deploys the site, and tags new releases

// Javascript banner
var banner = '/* <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
                '*  <%= pkg.homepage %>\n' +
                '*  Copyright (c) <%= grunt.template.today("yyyy") %> Environmental Systems Research Institute, Inc.\n' +
                '*  Apache 2.0 License */\n';

var currentVersion = require('./package.json').version;

module.exports = function(grunt) {

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

    // Running a development server
    'http-server': {
      'dev': {
        root: 'docs/build',
        port: 8888,
        host: '0.0.0.0',
        cache: 0,
        showDir : true,
        autoIndex: true,
        ext: 'html',
        runInBackground: true
      }
    },

    // Watch files
    'watch': {
      scripts: {
        files: ['lib/js/calcite-web.js'],
        tasks: [
          'concat:doc',
          'copy:doc',
          'jshint'
        ]
      },
      images: {
        files: ['lib/img/**/*'],
        tasks: [
          'newer:imagemin:doc',
          'copy:doc'
        ]
      },
      libsass: {
        files: ['lib/sass/**/*', 'docs/source/assets/css/**/*'],
        tasks: [
          'sass:doc',
          'copy:doc'
        ]
      },
      docs: {
        files: ['docs/source/**'],
        tasks: [
          'shell:acetate'
        ]
      }
    },

    // Check Javascript for errors
    'jshint': {
      all: ['lib/js/calcite-web.js']
    },

    // Build CSS files from SASS
    'sass': {

      options: {
        includePaths: ['lib/sass/']
      },

      expanded: {
        src: ['lib/sass/*.scss'],
        dest: 'dist/css/'
      },

      doc: {
        expand: true,
        cwd: 'docs/source/assets/css',
        src: ['**/*.scss'],
        dest: 'docs/build/assets/css',
        ext: '.css'
      },

      homepage: {
        files: {
          'docs/build/assets/css/homepage.css': 'docs/source/assets/css/homepage.scss'
        }
      }
    },

    // Create a webfont full of icons from a folder of svgs
    'webfont': {
      icons: {
        src: 'lib/img/icons/ui/*.svg',
        dest: 'lib/fonts',
        destCss: 'lib/sass/calcite-web/icons',
        options: {
          font: 'calcite-ui',
          template: 'bin/webfont-template.scss',
          relativeFontPath: '#{$font-path}',
          stylesheet: 'scss',
          htmlDemo: false,
          engine: 'fontforge'
        }
      }
    },

    // Create minified version of build css
    'cssmin': {
      target: {
        files: {
          'dist/css/calcite-web.min.css': ['dist/css/calcite-web.css'],
          'dist/css/calcite-web-no-fonts.min.css': ['dist/css/calcite-web-no-fonts.css'],
        }
      }
    },

    // Build minified Javascript file to dist
    'uglify': {
      options: {
        mangle: false,
        banner: banner
      },
      dist: {
        files: {
          'dist/js/calcite-web.min.js': ['lib/js/calcite-web.js']
        }
      }
    },

    // Copy libsass files to dist, doc assets to build
    'copy': {
      libsass: {
        expand: true,
        cwd: 'lib/',
        src: ['sass/**/*'],
        dest: 'dist/'
      },
      doc: {
        expand: true,
        cwd: 'docs/source/',
        src: ['assets/img/**/*', 'assets/js/**/*'],
        dest: 'docs/build/'
      },
      fonts: {
        expand: true,
        cwd: 'lib/',
        src: ['fonts/**/*'],
        dest: 'docs/build/assets/'
      },
      fontsDist: {
        expand: true,
        cwd: 'lib/',
        src: ['fonts/**/*'],
        dest: 'dist/'
      },
      changelog: {
        src: ['CHANGELOG.md'],
        dest: 'dist/'
      }
    },

    // Copy Javascript to dist and doc
    'concat': {
      options: {
        banner: banner
      },
      dist: {
        files: {
          'dist/js/calcite-web.js': 'lib/js/calcite-web.js'
        }
      },
      doc: {
        files: {
          'docs/source/assets/js/libs/calcite-web.js': 'lib/js/calcite-web.js'
        }
      }
    },

    // Optimize images and icons for dist and doc
    'imagemin': {
      dist: {
        files: [{
          expand: true,
          cwd: 'lib/',
          src: ['img/**/*.{png,jpg,svg,ico,gif}'],
          dest: 'dist/'
        }]
      },
      doc: {
        files: [{
          expand: true,
          cwd: 'lib/',
          src: ['img/**/*.{png,jpg,svg,ico,gif}'],
          dest: 'docs/source/assets/'
        }]
      }
    },

    // Make a zip file of the dist folder
    'compress': {
      main: {
        options: {
          archive: 'calcite-web.zip'
        },
        files: [
          {
            src: ['dist/**', '!dist/__MACOSX'],
            dest: './'
          },
        ]
      }
    },

    // Upload dist folder to s3
    'aws_s3': {
      options: {
        region: 'us-west-1',
        bucket: 'patterns.esri.com',
        endpoint: 'https://s3-us-west-1.amazonaws.com',
        access: 'public-read',
        gzip: true
      },
      production: {
        files: [
          // Manually set content type (plugin was setting incorrectly).
          {expand: true, cwd: 'dist/', src: ['**/*.js'],   dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/javascript'}},
          {expand: true, cwd: 'dist/', src: ['**/*.css'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'text/css'}},
          {expand: true, cwd: 'dist/', src: ['**/*.svg'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'image/svg+xml'}},
          {expand: true, cwd: 'dist/', src: ['**/*.ico'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'image/x-icon'}},
          {expand: true, cwd: 'dist/', src: ['**/*.jpg'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'image/jpg'}},
          {expand: true, cwd: 'dist/', src: ['**/*.map'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/javascript'}},
          {expand: true, cwd: 'dist/', src: ['**/*.eot'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/vnd.ms-fontobject'}},
          {expand: true, cwd: 'dist/', src: ['**/*.woff'], dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/font-woff'}},
          {expand: true, cwd: 'dist/', src: ['**/*.otf'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/font-sfnt'}},
          {expand: true, cwd: 'dist/', src: ['**/*.ttf'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/font-sfnt'}},
          {expand: true, cwd: 'dist/', src: ['**/*.json'], dest: 'files/calcite-web/', params: {ContentType: 'application/javascript'}},
          // Also upload to the 'latest' directory
          {expand: true, cwd: 'dist/', src: ['**/*.js'],   dest: 'files/calcite-web/latest/', params: {ContentType: 'application/javascript'}},
          {expand: true, cwd: 'dist/', src: ['**/*.css'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'text/css'}},
          {expand: true, cwd: 'dist/', src: ['**/*.svg'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'image/svg+xml'}},
          {expand: true, cwd: 'dist/', src: ['**/*.ico'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'image/x-icon'}},
          {expand: true, cwd: 'dist/', src: ['**/*.jpg'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'image/jpg'}},
          {expand: true, cwd: 'dist/', src: ['**/*.map'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'application/javascript'}},
          {expand: true, cwd: 'dist/', src: ['**/*.eot'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'application/vnd.ms-fontobject'}},
          {expand: true, cwd: 'dist/', src: ['**/*.woff'], dest: 'files/calcite-web/latest/', params: {ContentType: 'application/font-woff'}},
          {expand: true, cwd: 'dist/', src: ['**/*.otf'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'application/font-sfnt'}},
          {expand: true, cwd: 'dist/', src: ['**/*.ttf'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'application/font-sfnt'}},
          {expand: true, cwd: 'dist/', src: ['CHANGELOG.md'], dest: 'files/calcite-web/latest/', params: {ContentType: 'text/x-markdown'}}
        ]
      }
    },

    // Ask for AWS ID and Key
    'prompt': {
      aws: {
        options: {
          questions: [
            {
              config: 'aws_s3.options.accessKeyId',
              type: 'input',
              message: 'AWS Access ID:'
            },
            {
              config: 'aws_s3.options.secretAccessKey',
              type: 'input',
              message: 'AWS Secret Access Key:'
            }
          ]
        }
      }
    },

    // bin scripts
    'shell': {
      guid: {
        command: 'bin/guid.js',    // Generate a unique id for a new section
      },
      deploy: {
        command: 'bin/deploy.js',  // Create a JSON record of current documentation
      },
      release: {
        command: 'bin/release.sh'  // Create GitHub release that includes dist
      },
      acetate: {
        command: 'npm run acetate' // build the docs site
      },
      a11y: {
        command: 'npm run a11y' // run accessibility tests
      }
    },

    // Deploy doc site to gh-pages
    'gh-pages': {
      options: {
        base: 'docs/build',
        repo: 'https://github.com/Esri/calcite-web.git'
      },
      src: ['**']
    },

    // Runs tasks concurrently, speeding up Grunt
    'concurrent': {
      prepublish: [
        'scss',
        'uglify',
        'copy',
        'concat:dist',
        'newer:imagemin:dist'
      ]
    }

  });

  // load all grunt modules
  require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

  // ┌─────────────┐
  // │ Grunt tasks │
  // └─────────────┘

  // Build sass
  grunt.registerTask('scss', [
    'sass',
    'cssmin'
  ]);

  grunt.registerTask('buildFont', [
    'webfont',
    'copy:fonts',
    'copy:fontsDist'
  ]);

  // Run a development environment
  grunt.registerTask('dev', [
    'shell:acetate',
    'newer:imagemin:doc',
    'concat:doc',
    'sass:doc',
    'copy:doc',
    'copy:fonts',
    'http-server',
    'watch'
  ]);

  // Test calcite-web.js
  grunt.registerTask('test', [
    'jshint'
  ]);

  // Build a dist folder with all assets
  grunt.registerTask('prepublish', [
    'concurrent:prepublish'
  ]);

  // Upload files to s3
  grunt.registerTask('s3', [
    'prepublish',
    'shell:deploy',
    'prompt:aws',
    'aws_s3'
  ]);

  // Build and deploy doc site to github pages
  grunt.registerTask('deploy', 'Deploy documentation to github pages', function(n) {
    if (grunt.option('message')) {
      grunt.config.set('gh-pages.options.message', grunt.option('message'));
    }
    grunt.task.run([
      'newer:imagemin:doc',
      'concat:doc',
      'sass:doc',
      'copy:doc',
      'copy:fonts',
      'shell:deploy',
      'gh-pages'
    ]);
  });

  // Release a new version of the framework
  grunt.registerTask('release', [
    'prepublish',
    'shell:deploy',
    'compress',
    'shell:release',
    's3'
  ]);

  // Default task starts up a dev environment
  grunt.registerTask('default', ['prepublish', 'dev']);

};
<commit_msg>fix grunt sass<commit_after>// ┌─────────────┐
// │ Gruntfile   │
// └─────────────┘
// Grunt wraps several tasks to ease development
// runs middleman, deploys the site, and tags new releases

// Javascript banner
var banner = '/* <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
                '*  <%= pkg.homepage %>\n' +
                '*  Copyright (c) <%= grunt.template.today("yyyy") %> Environmental Systems Research Institute, Inc.\n' +
                '*  Apache 2.0 License */\n';

var currentVersion = require('./package.json').version;

module.exports = function(grunt) {

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

    // Running a development server
    'http-server': {
      'dev': {
        root: 'docs/build',
        port: 8888,
        host: '0.0.0.0',
        cache: 0,
        showDir : true,
        autoIndex: true,
        ext: 'html',
        runInBackground: true
      }
    },

    // Watch files
    'watch': {
      scripts: {
        files: ['lib/js/calcite-web.js'],
        tasks: [
          'concat:doc',
          'copy:doc',
          'jshint'
        ]
      },
      images: {
        files: ['lib/img/**/*'],
        tasks: [
          'newer:imagemin:doc',
          'copy:doc'
        ]
      },
      libsass: {
        files: ['lib/sass/**/*', 'docs/source/assets/css/**/*'],
        tasks: [
          'sass:doc',
          'copy:doc'
        ]
      },
      docs: {
        files: ['docs/source/**'],
        tasks: [
          'shell:acetate'
        ]
      }
    },

    // Check Javascript for errors
    'jshint': {
      all: ['lib/js/calcite-web.js']
    },

    // Build CSS files from SASS
    'sass': {

      options: {
        includePaths: ['lib/sass/']
      },

      expanded: {
        expand: true,
        src: ['lib/sass/*.scss'],
        dest: 'dist/css/'
      },

      doc: {
        expand: true,
        cwd: 'docs/source/assets/css',
        src: ['**/*.scss'],
        dest: 'docs/build/assets/css',
        ext: '.css'
      },

      homepage: {
        files: {
          'docs/build/assets/css/homepage.css': 'docs/source/assets/css/homepage.scss'
        }
      }
    },

    // Create a webfont full of icons from a folder of svgs
    'webfont': {
      icons: {
        src: 'lib/img/icons/ui/*.svg',
        dest: 'lib/fonts',
        destCss: 'lib/sass/calcite-web/icons',
        options: {
          font: 'calcite-ui',
          template: 'bin/webfont-template.scss',
          relativeFontPath: '#{$font-path}',
          stylesheet: 'scss',
          htmlDemo: false,
          engine: 'fontforge'
        }
      }
    },

    // Create minified version of build css
    'cssmin': {
      target: {
        files: {
          'dist/css/calcite-web.min.css': ['dist/css/calcite-web.css'],
          'dist/css/calcite-web-no-fonts.min.css': ['dist/css/calcite-web-no-fonts.css'],
        }
      }
    },

    // Build minified Javascript file to dist
    'uglify': {
      options: {
        mangle: false,
        banner: banner
      },
      dist: {
        files: {
          'dist/js/calcite-web.min.js': ['lib/js/calcite-web.js']
        }
      }
    },

    // Copy libsass files to dist, doc assets to build
    'copy': {
      libsass: {
        expand: true,
        cwd: 'lib/',
        src: ['sass/**/*'],
        dest: 'dist/'
      },
      doc: {
        expand: true,
        cwd: 'docs/source/',
        src: ['assets/img/**/*', 'assets/js/**/*'],
        dest: 'docs/build/'
      },
      fonts: {
        expand: true,
        cwd: 'lib/',
        src: ['fonts/**/*'],
        dest: 'docs/build/assets/'
      },
      fontsDist: {
        expand: true,
        cwd: 'lib/',
        src: ['fonts/**/*'],
        dest: 'dist/'
      },
      changelog: {
        src: ['CHANGELOG.md'],
        dest: 'dist/'
      }
    },

    // Copy Javascript to dist and doc
    'concat': {
      options: {
        banner: banner
      },
      dist: {
        files: {
          'dist/js/calcite-web.js': 'lib/js/calcite-web.js'
        }
      },
      doc: {
        files: {
          'docs/source/assets/js/libs/calcite-web.js': 'lib/js/calcite-web.js'
        }
      }
    },

    // Optimize images and icons for dist and doc
    'imagemin': {
      dist: {
        files: [{
          expand: true,
          cwd: 'lib/',
          src: ['img/**/*.{png,jpg,svg,ico,gif}'],
          dest: 'dist/'
        }]
      },
      doc: {
        files: [{
          expand: true,
          cwd: 'lib/',
          src: ['img/**/*.{png,jpg,svg,ico,gif}'],
          dest: 'docs/source/assets/'
        }]
      }
    },

    // Make a zip file of the dist folder
    'compress': {
      main: {
        options: {
          archive: 'calcite-web.zip'
        },
        files: [
          {
            src: ['dist/**', '!dist/__MACOSX'],
            dest: './'
          },
        ]
      }
    },

    // Upload dist folder to s3
    'aws_s3': {
      options: {
        region: 'us-west-1',
        bucket: 'patterns.esri.com',
        endpoint: 'https://s3-us-west-1.amazonaws.com',
        access: 'public-read',
        gzip: true
      },
      production: {
        files: [
          // Manually set content type (plugin was setting incorrectly).
          {expand: true, cwd: 'dist/', src: ['**/*.js'],   dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/javascript'}},
          {expand: true, cwd: 'dist/', src: ['**/*.css'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'text/css'}},
          {expand: true, cwd: 'dist/', src: ['**/*.svg'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'image/svg+xml'}},
          {expand: true, cwd: 'dist/', src: ['**/*.ico'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'image/x-icon'}},
          {expand: true, cwd: 'dist/', src: ['**/*.jpg'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'image/jpg'}},
          {expand: true, cwd: 'dist/', src: ['**/*.map'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/javascript'}},
          {expand: true, cwd: 'dist/', src: ['**/*.eot'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/vnd.ms-fontobject'}},
          {expand: true, cwd: 'dist/', src: ['**/*.woff'], dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/font-woff'}},
          {expand: true, cwd: 'dist/', src: ['**/*.otf'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/font-sfnt'}},
          {expand: true, cwd: 'dist/', src: ['**/*.ttf'],  dest: 'files/calcite-web/' + currentVersion + '/', params: {ContentType: 'application/font-sfnt'}},
          {expand: true, cwd: 'dist/', src: ['**/*.json'], dest: 'files/calcite-web/', params: {ContentType: 'application/javascript'}},
          // Also upload to the 'latest' directory
          {expand: true, cwd: 'dist/', src: ['**/*.js'],   dest: 'files/calcite-web/latest/', params: {ContentType: 'application/javascript'}},
          {expand: true, cwd: 'dist/', src: ['**/*.css'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'text/css'}},
          {expand: true, cwd: 'dist/', src: ['**/*.svg'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'image/svg+xml'}},
          {expand: true, cwd: 'dist/', src: ['**/*.ico'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'image/x-icon'}},
          {expand: true, cwd: 'dist/', src: ['**/*.jpg'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'image/jpg'}},
          {expand: true, cwd: 'dist/', src: ['**/*.map'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'application/javascript'}},
          {expand: true, cwd: 'dist/', src: ['**/*.eot'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'application/vnd.ms-fontobject'}},
          {expand: true, cwd: 'dist/', src: ['**/*.woff'], dest: 'files/calcite-web/latest/', params: {ContentType: 'application/font-woff'}},
          {expand: true, cwd: 'dist/', src: ['**/*.otf'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'application/font-sfnt'}},
          {expand: true, cwd: 'dist/', src: ['**/*.ttf'],  dest: 'files/calcite-web/latest/', params: {ContentType: 'application/font-sfnt'}},
          {expand: true, cwd: 'dist/', src: ['CHANGELOG.md'], dest: 'files/calcite-web/latest/', params: {ContentType: 'text/x-markdown'}}
        ]
      }
    },

    // Ask for AWS ID and Key
    'prompt': {
      aws: {
        options: {
          questions: [
            {
              config: 'aws_s3.options.accessKeyId',
              type: 'input',
              message: 'AWS Access ID:'
            },
            {
              config: 'aws_s3.options.secretAccessKey',
              type: 'input',
              message: 'AWS Secret Access Key:'
            }
          ]
        }
      }
    },

    // bin scripts
    'shell': {
      guid: {
        command: 'bin/guid.js',    // Generate a unique id for a new section
      },
      deploy: {
        command: 'bin/deploy.js',  // Create a JSON record of current documentation
      },
      release: {
        command: 'bin/release.sh'  // Create GitHub release that includes dist
      },
      acetate: {
        command: 'npm run acetate' // build the docs site
      },
      a11y: {
        command: 'npm run a11y' // run accessibility tests
      }
    },

    // Deploy doc site to gh-pages
    'gh-pages': {
      options: {
        base: 'docs/build',
        repo: 'https://github.com/Esri/calcite-web.git'
      },
      src: ['**']
    },

    // Runs tasks concurrently, speeding up Grunt
    'concurrent': {
      prepublish: [
        'scss',
        'uglify',
        'copy',
        'concat:dist',
        'newer:imagemin:dist'
      ]
    }

  });

  // load all grunt modules
  require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

  // ┌─────────────┐
  // │ Grunt tasks │
  // └─────────────┘

  // Build sass
  grunt.registerTask('scss', [
    'sass',
    'cssmin'
  ]);

  grunt.registerTask('buildFont', [
    'webfont',
    'copy:fonts',
    'copy:fontsDist'
  ]);

  // Run a development environment
  grunt.registerTask('dev', [
    'shell:acetate',
    'newer:imagemin:doc',
    'concat:doc',
    'sass:doc',
    'copy:doc',
    'copy:fonts',
    'http-server',
    'watch'
  ]);

  // Test calcite-web.js
  grunt.registerTask('test', [
    'jshint'
  ]);

  // Build a dist folder with all assets
  grunt.registerTask('prepublish', [
    'concurrent:prepublish'
  ]);

  // Upload files to s3
  grunt.registerTask('s3', [
    'prepublish',
    'shell:deploy',
    'prompt:aws',
    'aws_s3'
  ]);

  // Build and deploy doc site to github pages
  grunt.registerTask('deploy', 'Deploy documentation to github pages', function(n) {
    if (grunt.option('message')) {
      grunt.config.set('gh-pages.options.message', grunt.option('message'));
    }
    grunt.task.run([
      'newer:imagemin:doc',
      'concat:doc',
      'sass:doc',
      'copy:doc',
      'copy:fonts',
      'shell:deploy',
      'gh-pages'
    ]);
  });

  // Release a new version of the framework
  grunt.registerTask('release', [
    'prepublish',
    'shell:deploy',
    'compress',
    'shell:release',
    's3'
  ]);

  // Default task starts up a dev environment
  grunt.registerTask('default', ['prepublish', 'dev']);

};
<|endoftext|>


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

    // Project configuration.
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        src_files_js: ['js/so_tag.js'],
        src_files_css: ['css/*.css'],
        src_files_other: ['*.json', '*.php', 'LICENSE', 'README.md', '!package.json'],
        dest_dir_dev: 'build/dev/',
        dest_dir_rel: 'build/release/',
        dest_dir_dual: 'build/dual/',
        uglify: {
            main: {
                files: [{
                    expand: true,
                    cwd: '',
                    src: '<%= src_files_js %>',
                    dest: '<%= grunt.option("outpath") %>',
                    ext: '.min.js'
                }]
            },
        },
        jshint: {
            main: {
                src: ['<%= src_files_js %>']
            },
        },
        copy: {
            build_release: {
                src: ['<%= src_files_other %>'],
                dest: '<%= dest_dir_rel %>',
            },
            build_dev: {
                src: ['<%= src_files_js %>', '<%= src_files_css %>', '<%= src_files_other %>'],
                dest: '<%= dest_dir_dev %>',
            },
            build_dual: {
                src: ['<%= src_files_js %>', '<%= src_files_css %>', '<%= src_files_other %>'],
                dest: '<%= dest_dir_dual %>',
            }
        },
        cssmin: {
            main: {
                options : {
                    report: 'gzip'
                },
                files: [
                    {
                        expand: true,
                        cwd: 'css',
                        src: ['*.css', '!*.min.css'],
                        dest: '<%= grunt.option("outpath") %>/css/',
                        ext: '.min.css'
                    }]
            },
        },
        clean: {
            main: {
                src: ['<%= grunt.option("outpath") %>*']
            },
        },
        compress: {
            build_release: {
                options: {
                    mode: 'zip',
                    archive: 'build/SOTag.<%= pkg.version %>.zip'
                },
                cwd: '<%= dest_dir_rel %>',
                src: ['**/*'],
                dest: 'SOTag/',
                expand: true
            },
            build_dev: {
                options: {
                    mode: 'zip',
                    archive: 'build/SOTag.DEV.<%= pkg.version %>.zip'
                },
                cwd: '<%= dest_dir_dev %>',
                src: ['**/*'],
                dest: 'SOTag/',
                expand: true
            },
            build_dual: {
                options: {
                    mode: 'zip',
                    archive: 'build/SOTag.FULL.<%= pkg.version %>.zip'
                },
                cwd: '<%= dest_dir_dual %>',
                src: ['**/*'],
                dest: 'SOTag/',
                expand: true
            }
        },
        csscomb: {
            main: {
                expand: true,
                cwd: 'css/',
                src: ['*.css'],
                dest: 'css/'
            }
        }
    });
    require('time-grunt')(grunt);
    require('load-grunt-tasks')(grunt);

    //custom task:
    grunt.registerTask('set_option', 'Set a global variable.', function (name, val) {
        grunt.option(name, val);
        if (name === 'outpath') {
            grunt.log.writeln("Output folder is now: " + grunt.option('outpath'));
        } else {
            grunt.log.writeln("Global '" + name + "' is now: " + val);
        }
    });

};
<commit_msg>define cache location for grunt-newer<commit_after>module.exports = function (grunt) {
    'use strict';

    // Project configuration.
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        src_files_js: ['js/so_tag.js'],
        src_files_css: ['css/*.css'],
        src_files_other: ['*.json', '*.php', 'LICENSE', 'README.md', '!package.json'],
        dest_dir_dev: 'build/dev/',
        dest_dir_rel: 'build/release/',
        dest_dir_dual: 'build/dual/',
        uglify: {
            main: {
                files: [{
                    expand: true,
                    cwd: '',
                    src: '<%= src_files_js %>',
                    dest: '<%= grunt.option("outpath") %>',
                    ext: '.min.js'
                }]
            },
        },
        jshint: {
            main: {
                src: ['<%= src_files_js %>']
            },
        },
        copy: {
            build_release: {
                src: ['<%= src_files_other %>'],
                dest: '<%= dest_dir_rel %>',
            },
            build_dev: {
                src: ['<%= src_files_js %>', '<%= src_files_css %>', '<%= src_files_other %>'],
                dest: '<%= dest_dir_dev %>',
            },
            build_dual: {
                src: ['<%= src_files_js %>', '<%= src_files_css %>', '<%= src_files_other %>'],
                dest: '<%= dest_dir_dual %>',
            }
        },
        cssmin: {
            main: {
                options : {
                    report: 'gzip'
                },
                files: [
                    {
                        expand: true,
                        cwd: 'css',
                        src: ['*.css', '!*.min.css'],
                        dest: '<%= grunt.option("outpath") %>/css/',
                        ext: '.min.css'
                    }]
            },
        },
        clean: {
            main: {
                src: ['<%= grunt.option("outpath") %>*']
            },
        },
        compress: {
            build_release: {
                options: {
                    mode: 'zip',
                    archive: 'build/SOTag.<%= pkg.version %>.zip'
                },
                cwd: '<%= dest_dir_rel %>',
                src: ['**/*'],
                dest: 'SOTag/',
                expand: true
            },
            build_dev: {
                options: {
                    mode: 'zip',
                    archive: 'build/SOTag.DEV.<%= pkg.version %>.zip'
                },
                cwd: '<%= dest_dir_dev %>',
                src: ['**/*'],
                dest: 'SOTag/',
                expand: true
            },
            build_dual: {
                options: {
                    mode: 'zip',
                    archive: 'build/SOTag.FULL.<%= pkg.version %>.zip'
                },
                cwd: '<%= dest_dir_dual %>',
                src: ['**/*'],
                dest: 'SOTag/',
                expand: true
            }
        },
        csscomb: {
            main: {
                expand: true,
                cwd: 'css/',
                src: ['*.css'],
                dest: 'css/'
            }
        },
        newer: {
            options: {
                cache: 'node_modules/grunt-newer/.cache/<%= grunt.option("outpath") %>'
            }
        }
    });
    require('time-grunt')(grunt);
    require('load-grunt-tasks')(grunt);

    //custom task:
    grunt.registerTask('set_option', 'Set a global variable.', function (name, val) {
        grunt.option(name, val);
        if (name === 'outpath') {
            grunt.log.writeln("Output folder is now: " + grunt.option('outpath'));
        } else {
            grunt.log.writeln("Global '" + name + "' is now: " + val);
        }
    });

};
<|endoftext|>


<commit_before>module.exports = {
  name: 'Sync subsystem',

  sandbox: true,
  init: function(){
    var basis = window.basis.createSandbox();

    var STATE = basis.require('basis.data').STATE;
    var Value = basis.require('basis.data').Value;
    var AbstractData = basis.require('basis.data').AbstractData;
    var DataObject = basis.require('basis.data').Object;
    var resolveValue = basis.require('basis.data').resolveValue;
  },

  test: [
    {
      name: 'should set on init',
      test: function(){
        var fn = function(){};
        var data = new AbstractData({
          syncAction: fn
        });

        assert(data.syncAction === fn);
      }
    },
    {
      name: 'should set via syncAction',
      test: function(){
        var fn = function(){};
        var data = new AbstractData();

        assert(data.syncAction === null);

        data.setSyncAction(fn);
        assert(data.syncAction === fn);

        data.setSyncAction();
        assert(data.syncAction === null);
      }
    },
    {
      name: 'calling syncAction',
      test: [
        {
          name: 'should call syncAction state is undefined and has an active customer',
          test: function(){
            var foo = new DataObject({
              syncAction: function(){
                visit('syncActionCalled');
              }
            });
            var bar = new DataObject({
              active: true,
              delegate: foo
            });

            assert(foo.state == STATE.UNDEFINED);
            assert.visited(['syncActionCalled']);
          }
        },
        {
          name: 'Promise as result',
          test: [
            {
              name: 'resolve treats as READY',
              test: function(done){
                var foo = new DataObject({
                  syncAction: function(){
                    return new Promise(function(resolve){
                      setTimeout(resolve, 5);
                    });
                  }
                });
                var bar = new DataObject({
                  active: true,
                  delegate: foo
                });

                assert(foo.state == STATE.PROCESSING);
                setTimeout(function(){
                  assert(foo.state == STATE.READY);
                  done();
                }, 10);
              }
            },
            {
              name: 'reject treats as ERROR',
              test: function(done){
                var error = new Error('test');
                var foo = new DataObject({
                  syncAction: function(){
                    return new Promise(function(resolve, reject){
                      setTimeout(function(){
                        reject(error);
                      }, 5);
                    });
                  }
                });
                var bar = new DataObject({
                  active: true,
                  delegate: foo
                });

                assert(foo.state == STATE.PROCESSING);
                setTimeout(function(){
                  assert(foo.state == STATE.ERROR);
                  assert(foo.state.data == error);
                  done();
                }, 10);
              }
            }
          ]
        }
      ]
    }
  ]
};
<commit_msg>double sure promise test pass through<commit_after>module.exports = {
  name: 'Sync subsystem',

  sandbox: true,
  init: function(){
    var basis = window.basis.createSandbox();

    var STATE = basis.require('basis.data').STATE;
    var Value = basis.require('basis.data').Value;
    var AbstractData = basis.require('basis.data').AbstractData;
    var DataObject = basis.require('basis.data').Object;
    var resolveValue = basis.require('basis.data').resolveValue;
  },

  test: [
    {
      name: 'should set on init',
      test: function(){
        var fn = function(){};
        var data = new AbstractData({
          syncAction: fn
        });

        assert(data.syncAction === fn);
      }
    },
    {
      name: 'should set via syncAction',
      test: function(){
        var fn = function(){};
        var data = new AbstractData();

        assert(data.syncAction === null);

        data.setSyncAction(fn);
        assert(data.syncAction === fn);

        data.setSyncAction();
        assert(data.syncAction === null);
      }
    },
    {
      name: 'calling syncAction',
      test: [
        {
          name: 'should call syncAction state is undefined and has an active customer',
          test: function(){
            var foo = new DataObject({
              syncAction: function(){
                visit('syncActionCalled');
              }
            });
            var bar = new DataObject({
              active: true,
              delegate: foo
            });

            assert(foo.state == STATE.UNDEFINED);
            assert.visited(['syncActionCalled']);
          }
        },
        {
          name: 'Promise as result',
          test: [
            {
              name: 'resolve treats as READY',
              test: function(done){
                var foo = new DataObject({
                  syncAction: function(){
                    return new Promise(function(resolve){
                      setTimeout(resolve, 5);
                    });
                  }
                });
                var bar = new DataObject({
                  active: true,
                  delegate: foo
                });

                assert(foo.state == STATE.PROCESSING);
                setTimeout(function(){
                  assert(foo.state == STATE.READY);
                  done();
                }, 30);
              }
            },
            {
              name: 'reject treats as ERROR',
              test: function(done){
                var error = new Error('test');
                var foo = new DataObject({
                  syncAction: function(){
                    return new Promise(function(resolve, reject){
                      setTimeout(function(){
                        reject(error);
                      }, 5);
                    });
                  }
                });
                var bar = new DataObject({
                  active: true,
                  delegate: foo
                });

                assert(foo.state == STATE.PROCESSING);
                setTimeout(function(){
                  assert(foo.state == STATE.ERROR);
                  assert(foo.state.data == error);
                  done();
                }, 30);
              }
            }
          ]
        }
      ]
    }
  ]
};
<|endoftext|>