<commit_before>	function setHardwareOptions(id,default_boot){
        default_boot = typeof default_boot !== 'undefined' ? default_boot : 'hd' ;
			// id is the main div id containing hardware.html
			$(id+" #hardware-memory").find('option').remove();
			$(id+" #hardware-vcpus").find('option').remove();
			$(id+" #hardware-interfaces").find('option').remove();
			$(id+" #hardware-graphics").find('option').remove();
            $(id+" #hardware-videos").find('option').remove();
			$(id+" #hardware-boot_order").find('option').remove();
			$(id+" #hardware-qos_id").find('option').remove();

			api.ajax_async('/api/v3/user/hardware_allowed','GET','').done(function(hardware) {
				if(hardware.nets.length == 1){
					$(id+" #hardware-interfaces").attr("disabled",true);
				}else{
					$(id+" #hardware-interfaces").attr("disabled",false);
				}
				$.each(hardware.nets,function(key, value)
				{
					$(id+" #hardware-interfaces").append('<option value=' + value.id + '>' + value.name + ' - ' + value.description + '</option>');
				});

				if(hardware.graphics.length == 1){
					$(id+" #hardware-graphics").attr("disabled",true);
				}else{
					$(id+" #hardware-graphics").attr("disabled",false);
				}
				$.each(hardware.graphics,function(key, value)
				{
					$(id+" #hardware-graphics").append('<option value=' + value.id + '>' + value.name + '</option>');
				});

				if(hardware.videos.length == 1){
					$(id+" #hardware-videos").attr("disabled",true);
				}else{
					$(id+" #hardware-videos").attr("disabled",false);
				}
				$.each(hardware.videos,function(key, value)
				{
					$(id+" #hardware-videos").append('<option value=' + value.id + '>' + value.name + '</option>');
				});

				if(hardware.boots.length == 1){
					$(id+" #hardware-boot_order").attr("disabled",true);
				}else{
					$(id+" #hardware-boot_order").attr("disabled",false);
				}
				$.each(hardware.boots,function(key, value)
				{
					$(id+" #hardware-boot_order").append('<option value=' + value.id + '>' + value.name + '</option>');
				});
                $(id+' #hardware-boot_order option[value="'+default_boot+'"]').prop("selected",true);

				if(hardware.qos_id.length <= 1 || !('qos_id' in hardware)){
					$(id+" #hardware-qos_id").attr("disabled",true);
				}else{
					$(id+" #hardware-qos_id").attr("disabled",false);
				}
				if(hardware.qos_id.length == 0){
					$(id+" #hardware-qos_id").append('<option value="unlimited">Unlimited</option>');
				}else{
					$.each(hardware.qos_id,function(key, value)
					{
						$(id+" #hardware-qos_id").append('<option value=' + value.id + '>' + value.name + '</option>');
					});
				}

				if(hardware.quota == false){
					hardware.quota={'memory':128, 'vcpus':128, 'desktops_disk_size':500}
				}

				for (var i = 0.5; i <= hardware.quota.memory; i += 0.5) {
					$(id+" #hardware-memory").append('<option value='+i+'>' + i +'</option>');
				}

				for (var i = 1; i <= hardware.quota.vcpus; i += 1) {
					$(id+" #hardware-vcpus").append('<option value='+i+'>' + i +' vCPU</option>');
				}

				if($(id+" #disk_size").length != 0) {
					$(id+" #disk_size").find('option').remove();
					if(hardware.quota.desktops_disk_size <= 10){
						for (var i = 1; i <= hardware.quota.desktops_disk_size; i += 1) {
							$(id+" #disk_size").append('<option value='+i+'>' + i +' GB</option>');
						}
					}else{
						$(id+" #disk_size").append('<option value=1>1 GB</option>');
						$(id+" #disk_size").append('<option value=5>5 GB</option>');
						for (var i = 10; i <= hardware.quota.desktops_disk_size; i += 5) {
							$(id+" #disk_size").append('<option value='+i+'>' + i +' GB</option>');
						}
					}
				}

			});
	}

	function setHardwareDomainIdDefaults(div_id,domain_id){
		api.ajax('/api/v3/domain/hardware/'+domain_id,'GET','').done(function(domain) {
			setHardwareDomainDefaults(div_id,domain)
		})
	}

	function setHardwareDomainDefaults(div_id,domain){
		// id is the domain id
		$(div_id+' #hardware-interfaces option:selected').prop("selected", false);
		$(div_id+' #hardware-graphics option:selected').prop("selected", false);
		$(div_id+' #hardware-videos option:selected').prop("selected", false);
		$(div_id+' #hardware-boot_order option:selected').prop("selected", false);

		$.each(domain.hardware.interfaces, function(k,value){
			$(div_id+' #hardware-interfaces option[value="'+value+'"]').prop("selected",true);
		})
		$(div_id+' #hardware-graphics option[value="'+domain.hardware.graphics[0].type+'"]').prop("selected",true);
		$(div_id+' #hardware-videos option[value="'+domain.hardware.videos[0]+'"]').prop("selected",true);
		$(div_id+' #hardware-diskbus option[value="'+domain.hardware.disks[0].bus+'"]').prop("selected",true);

		// Need to talk with engine and change this
		if(domain.hardware.boot_order[0]=='hd'){domain.hardware.boot_order[0]='disk'}
		if(domain.hardware.boot_order[0]=='cdrom'){domain.hardware.boot_order[0]='iso'}
		if(domain.hardware.boot_order[0]=='network'){domain.hardware.boot_order[0]='pxe'}
		$(div_id+' #hardware-boot_order option[value="'+domain.hardware.boot_order[0]+'"]').prop("selected",true);

		if(domain.hardware.memory > $(div_id+' #hardware-memory option:last-child').val()){
			$(div_id+' #hardware-memory option:last-child').prop("selected",true);
		}else{
			if($(div_id+' #hardware-memory option[value="'+domain.hardware.memory+'"]').length > 0){
				$(div_id+' #hardware-memory option[value="'+domain.hardware.memory+'"]').prop("selected",true);
			}else{
				$(div_id+' #hardware-memory option:first').prop("selected",true);
			}
		}

		if(domain.hardware.vcpus > $(div_id+' #hardware-vcpus option:last-child').val()){
			$(div_id+' #hardware-vcpus option:last-child').prop("selected",true);
		}else{
			if($(div_id+' #hardware-vcpus option[value="'+domain.hardware.vcpus+'"]').length > 0){
				$(div_id+' #hardware-vcpus option[value="'+domain.hardware.vcpus+'"]').prop("selected",true);
			}else{
				$(div_id+' #hardware-vcpus option:first').prop("selected",true);
			}
		}

		if('qos_id' in domain.hardware.disks[0]){
			if(domain.hardware.disks[0]['qos_id']==false){
				qos_id='unlimited'
			}else{
				qos_id=domain.hardware.disks[0]['qos_id']
			}
		}else{
			qos_id='unlimited'
		}

		$(div_id+' #hardware-qos_id option[value="'+qos_id+'"]').prop("selected",true);
	}

function populate_tree_template(id){
    $(":ui-fancytree").fancytree("destroy")
	$(".template_tree").fancytree({

		extensions: ["table"],
		table: {
			indentation: 20,      // indent 20px per node level
			nodeColumnIdx: 2      // render the node title into the 2nd column
		},
		source: {
			url: "/api/v3/admin/desktops/tree_list/" + id,
			cache: false
		},
		lazyLoad: function(event, data) {
			data.result = $.ajax({
				url: "/api/v3/admin/desktops/tree_list/" + id,
				dataType: "json"
			});
		},
		selectMode: 3,
		renderColumns: function(event, data) {
			var node = data.node,
			$tdList = $(node.tr).find(">td");
			$tdList.eq(1).text(node.getIndexHier());
			// (index #2 is rendered by fancytree)
			$tdList.eq(3).text(node.data.user);
			$tdList.eq(4).text(node.data.kind);
			$tdList.eq(5).text(node.data.category);
			$tdList.eq(6).text(node.data.group);
		}
	});

}

	function setHardwareDomainDefaults_viewer(div_id,data){
		data['hardware']=data['create_dict']['hardware']
		$(div_id+" #vcpu").html(data.hardware.vcpus+' CPU(s)');
		$(div_id+" #ram").html((data.hardware.memory/1048576).toFixed(2)+'GB');
		$(div_id+" #gpu").html(data.create_dict.reservables ? data.create_dict.reservables.vgpus : '-');
		$(div_id+" #net").html(data.hardware.interfaces.join(' '));
	    	$(div_id+" #graphics").html(data.hardware.graphics);
		$(div_id+" #video").html(data.hardware.videos);
		$(div_id+" #boot").html(data.hardware['boot_order']);
		if(data['forced_hyp']){
			$(div_id+" #forced_hyp").html(data['forced_hyp']);
			$(div_id+" #forced_hyp").closest("tr").show();
		}else{
			$(div_id+" #forced_hyp").closest("tr").hide(); //.closest("tr").remove();
		}
		// console.log(data)
		// console.log('Disabled template tree. The endpoint is filtering managers and admins. It is that needed?')
		if (data.kind == 'desktop') {
			populate_tree_template(data.create_dict.origin ? data.create_dict.origin : data.id);
		}
	}<commit_msg>fix(webapp): remove showing hyphen when not gpu in desktop<commit_after>	function setHardwareOptions(id,default_boot){
        default_boot = typeof default_boot !== 'undefined' ? default_boot : 'hd' ;
			// id is the main div id containing hardware.html
			$(id+" #hardware-memory").find('option').remove();
			$(id+" #hardware-vcpus").find('option').remove();
			$(id+" #hardware-interfaces").find('option').remove();
			$(id+" #hardware-graphics").find('option').remove();
            $(id+" #hardware-videos").find('option').remove();
			$(id+" #hardware-boot_order").find('option').remove();
			$(id+" #hardware-qos_id").find('option').remove();

			api.ajax_async('/api/v3/user/hardware_allowed','GET','').done(function(hardware) {
				if(hardware.nets.length == 1){
					$(id+" #hardware-interfaces").attr("disabled",true);
				}else{
					$(id+" #hardware-interfaces").attr("disabled",false);
				}
				$.each(hardware.nets,function(key, value)
				{
					$(id+" #hardware-interfaces").append('<option value=' + value.id + '>' + value.name + ' - ' + value.description + '</option>');
				});

				if(hardware.graphics.length == 1){
					$(id+" #hardware-graphics").attr("disabled",true);
				}else{
					$(id+" #hardware-graphics").attr("disabled",false);
				}
				$.each(hardware.graphics,function(key, value)
				{
					$(id+" #hardware-graphics").append('<option value=' + value.id + '>' + value.name + '</option>');
				});

				if(hardware.videos.length == 1){
					$(id+" #hardware-videos").attr("disabled",true);
				}else{
					$(id+" #hardware-videos").attr("disabled",false);
				}
				$.each(hardware.videos,function(key, value)
				{
					$(id+" #hardware-videos").append('<option value=' + value.id + '>' + value.name + '</option>');
				});

				if(hardware.boots.length == 1){
					$(id+" #hardware-boot_order").attr("disabled",true);
				}else{
					$(id+" #hardware-boot_order").attr("disabled",false);
				}
				$.each(hardware.boots,function(key, value)
				{
					$(id+" #hardware-boot_order").append('<option value=' + value.id + '>' + value.name + '</option>');
				});
                $(id+' #hardware-boot_order option[value="'+default_boot+'"]').prop("selected",true);

				if(hardware.qos_id.length <= 1 || !('qos_id' in hardware)){
					$(id+" #hardware-qos_id").attr("disabled",true);
				}else{
					$(id+" #hardware-qos_id").attr("disabled",false);
				}
				if(hardware.qos_id.length == 0){
					$(id+" #hardware-qos_id").append('<option value="unlimited">Unlimited</option>');
				}else{
					$.each(hardware.qos_id,function(key, value)
					{
						$(id+" #hardware-qos_id").append('<option value=' + value.id + '>' + value.name + '</option>');
					});
				}

				if(hardware.quota == false){
					hardware.quota={'memory':128, 'vcpus':128, 'desktops_disk_size':500}
				}

				for (var i = 0.5; i <= hardware.quota.memory; i += 0.5) {
					$(id+" #hardware-memory").append('<option value='+i+'>' + i +'</option>');
				}

				for (var i = 1; i <= hardware.quota.vcpus; i += 1) {
					$(id+" #hardware-vcpus").append('<option value='+i+'>' + i +' vCPU</option>');
				}

				if($(id+" #disk_size").length != 0) {
					$(id+" #disk_size").find('option').remove();
					if(hardware.quota.desktops_disk_size <= 10){
						for (var i = 1; i <= hardware.quota.desktops_disk_size; i += 1) {
							$(id+" #disk_size").append('<option value='+i+'>' + i +' GB</option>');
						}
					}else{
						$(id+" #disk_size").append('<option value=1>1 GB</option>');
						$(id+" #disk_size").append('<option value=5>5 GB</option>');
						for (var i = 10; i <= hardware.quota.desktops_disk_size; i += 5) {
							$(id+" #disk_size").append('<option value='+i+'>' + i +' GB</option>');
						}
					}
				}

			});
	}

	function setHardwareDomainIdDefaults(div_id,domain_id){
		api.ajax('/api/v3/domain/hardware/'+domain_id,'GET','').done(function(domain) {
			setHardwareDomainDefaults(div_id,domain)
		})
	}

	function setHardwareDomainDefaults(div_id,domain){
		// id is the domain id
		$(div_id+' #hardware-interfaces option:selected').prop("selected", false);
		$(div_id+' #hardware-graphics option:selected').prop("selected", false);
		$(div_id+' #hardware-videos option:selected').prop("selected", false);
		$(div_id+' #hardware-boot_order option:selected').prop("selected", false);

		$.each(domain.hardware.interfaces, function(k,value){
			$(div_id+' #hardware-interfaces option[value="'+value+'"]').prop("selected",true);
		})
		$(div_id+' #hardware-graphics option[value="'+domain.hardware.graphics[0].type+'"]').prop("selected",true);
		$(div_id+' #hardware-videos option[value="'+domain.hardware.videos[0]+'"]').prop("selected",true);
		$(div_id+' #hardware-diskbus option[value="'+domain.hardware.disks[0].bus+'"]').prop("selected",true);

		// Need to talk with engine and change this
		if(domain.hardware.boot_order[0]=='hd'){domain.hardware.boot_order[0]='disk'}
		if(domain.hardware.boot_order[0]=='cdrom'){domain.hardware.boot_order[0]='iso'}
		if(domain.hardware.boot_order[0]=='network'){domain.hardware.boot_order[0]='pxe'}
		$(div_id+' #hardware-boot_order option[value="'+domain.hardware.boot_order[0]+'"]').prop("selected",true);

		if(domain.hardware.memory > $(div_id+' #hardware-memory option:last-child').val()){
			$(div_id+' #hardware-memory option:last-child').prop("selected",true);
		}else{
			if($(div_id+' #hardware-memory option[value="'+domain.hardware.memory+'"]').length > 0){
				$(div_id+' #hardware-memory option[value="'+domain.hardware.memory+'"]').prop("selected",true);
			}else{
				$(div_id+' #hardware-memory option:first').prop("selected",true);
			}
		}

		if(domain.hardware.vcpus > $(div_id+' #hardware-vcpus option:last-child').val()){
			$(div_id+' #hardware-vcpus option:last-child').prop("selected",true);
		}else{
			if($(div_id+' #hardware-vcpus option[value="'+domain.hardware.vcpus+'"]').length > 0){
				$(div_id+' #hardware-vcpus option[value="'+domain.hardware.vcpus+'"]').prop("selected",true);
			}else{
				$(div_id+' #hardware-vcpus option:first').prop("selected",true);
			}
		}

		if('qos_id' in domain.hardware.disks[0]){
			if(domain.hardware.disks[0]['qos_id']==false){
				qos_id='unlimited'
			}else{
				qos_id=domain.hardware.disks[0]['qos_id']
			}
		}else{
			qos_id='unlimited'
		}

		$(div_id+' #hardware-qos_id option[value="'+qos_id+'"]').prop("selected",true);
	}

function populate_tree_template(id){
    $(":ui-fancytree").fancytree("destroy")
	$(".template_tree").fancytree({

		extensions: ["table"],
		table: {
			indentation: 20,      // indent 20px per node level
			nodeColumnIdx: 2      // render the node title into the 2nd column
		},
		source: {
			url: "/api/v3/admin/desktops/tree_list/" + id,
			cache: false
		},
		lazyLoad: function(event, data) {
			data.result = $.ajax({
				url: "/api/v3/admin/desktops/tree_list/" + id,
				dataType: "json"
			});
		},
		selectMode: 3,
		renderColumns: function(event, data) {
			var node = data.node,
			$tdList = $(node.tr).find(">td");
			$tdList.eq(1).text(node.getIndexHier());
			// (index #2 is rendered by fancytree)
			$tdList.eq(3).text(node.data.user);
			$tdList.eq(4).text(node.data.kind);
			$tdList.eq(5).text(node.data.category);
			$tdList.eq(6).text(node.data.group);
		}
	});

}

	function setHardwareDomainDefaults_viewer(div_id,data){
		data['hardware']=data['create_dict']['hardware']
		$(div_id+" #vcpu").html(data.hardware.vcpus+' CPU(s)');
		$(div_id+" #ram").html((data.hardware.memory/1048576).toFixed(2)+'GB');
		if(data.create_dict.reservables){
			$(div_id+" #gpu").html(data.create_dict.reservables.vgpus);
			$(div_id+" #gpu").closest("tr").show();
		}else{
			$(div_id+" #gpu").closest("tr").hide();
		}
		$(div_id+" #net").html(data.hardware.interfaces.join(' '));
	    $(div_id+" #graphics").html(data.hardware.graphics);
		$(div_id+" #video").html(data.hardware.videos);
		$(div_id+" #boot").html(data.hardware['boot_order']);
		if(data['forced_hyp']){
			$(div_id+" #forced_hyp").html(data['forced_hyp']);
			$(div_id+" #forced_hyp").closest("tr").show();
		}else{
			$(div_id+" #forced_hyp").closest("tr").hide(); //.closest("tr").remove();
		}
		// console.log(data)
		// console.log('Disabled template tree. The endpoint is filtering managers and admins. It is that needed?')
		if (data.kind == 'desktop') {
			populate_tree_template(data.create_dict.origin ? data.create_dict.origin : data.id);
		}
	}<|endoftext|>


<commit_before>/**
 * Created by Matt on 3/26/2016.
 */
var encryption = require('../authentication/encryption'),
    db = require('../database/db');

var loginLocation = 'login';
var logOutLocation = 'logout';

var Session = {
    new:function(req, res){
        //req.session.publicKey = encryption.servePublicKey();
        res.render(loginLocation, { title: 'Login',message:'',pubKey:encryption.servePublicKey(), username: req.session.user.username, isAdmin: req.session.user.admin});
    },

    create:function(req,res){
        //console.log('in create');
        //console.log(req.body);
        var encrypted = req.body.encrypted;
        //console.log('encrypted = ',encrypted);
        var decrypted = encryption.asymDecrypt(encrypted);
        //console.log(decrypted);

        db.get("SELECT * from Users WHERE username = ?", decrypted.username, function(err,user){
            if(err || !user)
                return res.render(loginLocation,{title: 'Login', invalid: true, message:"Username/Password not found. Please try again.", pubKey:encryption.servePublicKey(), username: req.session.user.username, isAdmin: req.session.user.admin});
            var digest = encryption.hash(decrypted.password, user.salt);
            if(user.passwordDigest !== digest)
                return res.render(loginLocation,{title: 'Login', invalid: true, message:"Username/Password not found. Please try again.", pubKey:encryption.servePublicKey(), username: req.session.user.username, isAdmin: req.session.user.admin});
            req.session.reset();
            req.session.user = user;
            console.log(user.username, " has logged in.");
            return res.render('index', {title: "Home Page", username: req.session.user.username, isAdmin: req.session.user.admin});
        });
    },

    destroy: function(req, res){
        req.session.reset();
        req.session.user = {username: "Guest", isAdmin: false};
        res.render('login', { success: true, message: "You have logged out!", pubKey:encryption.servePublicKey(), username: req.session.user.username, isAdmin: req.session.user.admin})
    },

    loadUser: function(req, res, next){
        console.log("HERE", req.session.user);
        if(req.session && req.session.user && req.session.user.username != "Guest"){
            db.get("SELECT * from Users WHERE username = ?", req.session.user.username, function(err,user){
                if(err){
                    //return res.render("error", { error: err});
                    return res.sendStatus(500);
                }

                req.user = user;
                req.session.user = user;
                console.log("Loading user: ",user);
                return next();
            });
        }
        else{
            req.user = {username:"Guest", isAdmin: false};
            req.session.user = {username:"Guest", isAdmin: false};
            next();
        }
    }
};

module.exports = exports = Session;<commit_msg>Made some changes to make login usernames not case-sensitive<commit_after>/**
 * Created by Matt on 3/26/2016.
 */
var encryption = require('../authentication/encryption'),
    db = require('../database/db');

var loginLocation = 'login';
var logOutLocation = 'logout';

var Session = {
    new:function(req, res){
        //req.session.publicKey = encryption.servePublicKey();
        res.render(loginLocation, { title: 'Login',message:'',pubKey:encryption.servePublicKey(), username: req.session.user.username, isAdmin: req.session.user.admin});
    },

    create:function(req,res){
        //console.log('in create');
        //console.log(req.body);
        var encrypted = req.body.encrypted;
        //console.log('encrypted = ',encrypted);
        var decrypted = encryption.asymDecrypt(encrypted);
        //console.log(decrypted);

        db.get("SELECT * from Users WHERE upper(username) = ?", decrypted.username.toUpperCase(), function(err,user){
            if(err || !user)
                return res.render(loginLocation,{title: 'Login', invalid: true, message:"Username/Password not found. Please try again.", pubKey:encryption.servePublicKey(), username: req.session.user.username, isAdmin: req.session.user.admin});
            var digest = encryption.hash(decrypted.password, user.salt);
            if(user.passwordDigest !== digest)
                return res.render(loginLocation,{title: 'Login', invalid: true, message:"Username/Password not found. Please try again.", pubKey:encryption.servePublicKey(), username: req.session.user.username, isAdmin: req.session.user.admin});
            req.session.reset();
            req.session.user = user;
            console.log(user.username, " has logged in.");
            return res.render('index', {title: "Home Page", username: req.session.user.username, isAdmin: req.session.user.admin});
        });
    },

    destroy: function(req, res){
        req.session.reset();
        req.session.user = {username: "Guest", isAdmin: false};
        res.render('login', { success: true, message: "You have logged out!", pubKey:encryption.servePublicKey(), username: req.session.user.username, isAdmin: req.session.user.admin})
    },

    loadUser: function(req, res, next){
        console.log("HERE", req.session.user);
        if(req.session && req.session.user && req.session.user.username != "Guest"){
            db.get("SELECT * from Users WHERE username = ?", req.session.user.username, function(err,user){
                if(err){
                    //return res.render("error", { error: err});
                    return res.sendStatus(500);
                }

                req.user = user;
                req.session.user = user;
                console.log("Loading user: ",user);
                return next();
            });
        }
        else{
            req.user = {username:"Guest", isAdmin: false};
            req.session.user = {username:"Guest", isAdmin: false};
            next();
        }
    }
};

module.exports = exports = Session;<|endoftext|>


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

CLASS({
  package: 'foam.ui.md',
  name: 'UpdateDetailView',
  extends: 'foam.ui.md.BaseDetailView',

  requires: [
    'foam.ui.PopupChoiceView',
    'foam.ui.md.Toolbar',
    'foam.ui.md.ToolbarAction'
  ],

  imports: [
    'dao',
    'stack',
    'controllerMode'
  ],
  exports: [
    'toolbar as mdToolbar'
  ],

  properties: [
    {
      name: 'title',
      dynamicValue: function() {
        return (this.data && this.data.id ? 'Edit' : 'New') +
            (this.model ? ' ' + this.model.label : '');
      }
    },
    {
      name: 'rawData',
      documentation: 'The uncloned original input data.',
      postSet: function(old, nu) {
        if ( old ) old.removeListener(this.rawUpdate);
        if ( nu ) nu.addListener(this.rawUpdate);
      }
    },
    {
      name: 'originalData',
      documentation: 'A clone of the input data, for comparison with edits.'
    },
    {
      name: 'data',
      preSet: function(_, v) {
        if ( ! v ) return undefined;
        this.rawData = v;
        return v.deepClone();
      },
      postSet: function(_, data) {
        if ( ! data ) return;
        this.originalData = data.deepClone();
        if ( data && data.model_ ) this.model = data.model_;
        var self = this;
        data.addPropertyListener(null, function(o, topic) {
          var prop = o.model_.getProperty(topic[1]);
          if ( prop.transient ) return;
          self.version++;
          self.rawData = '';
        });
      }
    },
    {
      name: 'immutable',
      documentation: 'Set to true if the view should never switch to ' +
          'X-and-check mode, even when there are changes.',
      defaultValue: false
    },
    {
      name: 'liveEdit',
      documentation: "Propagate edits immediately, and don't show the save button",
      defaultValue: false
    },
    {
      name: 'exitOnSave',
      documentation: "If true, saving will also exit the view.",
      defaultValue: false
    },
    {
      // Version of the data which changes whenever any property of the data is updated.
      // Used to help trigger isEnabled / isAvailable in Actions.
      model_: 'IntProperty',
      name: 'version'
    },
    {
      model_: 'BooleanProperty',
      name: 'showModelActions',
      defaultValue: true
    },
    {
      model_: 'BooleanProperty',
      name: 'outstandingChanges',
      hidden: true,
      dynamicValue: function() {
        this.version; this.immutable;
        return ! this.immutable && ! this.originalData.equals(this.data);
      },
      postSet: function(old,nu) {
        if ( nu && this.liveEdit ) {
          this.commit_();
        }
      }
    },
    {
      type: 'foam.ui.md.Toolbar',
      name: 'toolbar',
      lazyFactory: function() {
        return this.Toolbar.create({
          data$: this.data$,
          title$: this.title$
        }, this.Y);
      },
      postSet: function(old, nu) {
        if ( old === nu ) return;
        if ( old ) {
          old.removeLeftActions(this.leftActions_);
          old.removeRightActions(this.rightActions_);
        }
        if ( nu ) {
          nu.addLeftActions(this.leftActions_);
          nu.addRightActions(this.rightActions_);
        }
      }
    },
    {
      model_: 'ViewFactoryProperty',
      name: 'innerView',
      defaultValue: 'foam.ui.md.DetailView'
    },
    {
      name: 'className',
      defaultValue: 'md-update-detail-view'
    },
    {
      model_: 'ArrayProperty',
      name: 'leftActions_',
      lazyFactory: function() {
        var myModel = this.model_;
        return [
          this.ToolbarAction.create({
            data: this,
            action: myModel.getAction('back')
          }, this.Y),
          this.ToolbarAction.create({
            data: this,
            action: myModel.getAction('reset')
          }, this.Y)
        ];
      },
      postSet: function(old, nu) {
        var toolbar = this.toolbar;
        if ( old === nu || ! toolbar ) return;
        if ( old ) toolbar.removeLeftActions(old);
        if ( nu ) toolbar.addLeftActions(nu);
      }
    },
    {
      model_: 'ArrayProperty',
      name: 'rightActions_',
      lazyFactory: function() {
        return [
          this.ToolbarAction.create({
            data: this,
            action: this.model_.getAction('save')
          }),
          this.ToolbarAction.create({
            data: this,
            action: this.model_.getAction('delete')
          })
        ];
      },
      postSet: function(old, nu) {
        var toolbar = this.toolbar;
        if ( old === nu || ! toolbar ) return;
        if ( old ) toolbar.removeRightActions(old);
        if ( nu ) toolbar.addRightActions(nu);
      }
    }
  ],

  actions: [
    {
      name: 'back',
      help:  'Go back',
      priority: 0,
      order: 0.0,
      iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAPUlEQVQ4y2NgGLbgf8P/BtKU////+78WacpDSFMeSlPlYaQo/0OacjyAcg1wJ4WTGmHDS4sWaVrqhm/mBQAoLpX9t+4i2wAAAABJRU5ErkJggg==',
      ligature: 'arrow_back',
      isAvailable: function() {
        return this.liveEdit || ! this.outstandingChanges;
      },
      code: function() { this.stack.popView(); }
    },
    {
      name: 'save',
      help:  'Save updates',
      priority: 0,
      order: 0,
      iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAUElEQVQ4jWNgGAW4wH9d0pRH///zv4E05f+J1jB0lP9n+b/0fzgJpv8PBUr++R9BgmP+N4C1RJLgdqiWKBK8CtVCUsiAtBCvHKqFFOUjAwAATIhwjZSqeJcAAAAASUVORK5CYII=',
      ligature: 'check',
      isAvailable: function() { return  ! this.liveEdit && this.outstandingChanges; },
      code: function() {
        this.commit_();
      }
    },
    {
      name: 'delete',
      iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAOklEQVQ4y2NgGPzgv8L/B/9h4MF/BXxK8QDqaCDH/aSaP6phVAMuDa+wqn+BW4P//5eYyv/7DvI8DwBDJ5LB6mdU8gAAAABJRU5ErkJggg==',
      priority: 0,
      order: 0,
      isAvailable: function() {
        return this.controllerMode && this.controllerMode == 'update';
      },
      code: function() {
        this.dao.remove(this.data, {
          remove: function() {
            this.stack.popView();
          }.bind(this),
          error: function() {
            // TODO:
          }
        });
      }
    },
    {
      name: 'reset',
      priority: 0,
      order: 1.0,
      help: 'Reset form (cancel update)',
      iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAdklEQVQ4y+WTuQ3AIBAEaQKK8NN/BEUArmccgGyj43MMIZo5TqtFqbUPJxYtbg2OvS44IJQKhguwdUETSiXjXr77KhGICRjihWKm8Dw3KXP4Z5VZ/Lfw7B5kyD1cy5C7uAx5iJcht6vhRTUi4OrC0Szftvi/vAFNdbZ2Dp661QAAAABJRU5ErkJggg==',
      ligature: 'close',
      isAvailable: function() { return  ! this.liveEdit && this.outstandingChanges; },
      code: function() { this.data = this.originalData.deepClone(); }
    }
  ],

  methods: [
    function commit_() {
      var self = this;
      var obj  = this.data;

      this.dao.put(obj, {
        put: function() {
          self.originalData = obj.deepClone();

          if (self.exitOnSave && ! self.liveEdit) {
            self.stack.popView();
          }
        },
        error: function() {
          console.error('Error saving', arguments);
        }
      });
    }
  ],

  listeners: [
    {
      name: 'rawUpdate',
      code: function() {
        // If this listener fires, the raw data updated and the user hasn't
        // changed anything.
        this.data = this.rawData;
      }
    }
  ],

  templates: [
    function CSS() {/*
      .md-update-detail-view {
        display: flex;
        flex-direction: column;
        height: 100%;
        width: 100%;
      }
      .md-update-detail-view-body {
        overflow-x: hidden;
        overflow-y: auto;
      }
    */},
    function toHTML() {/*
      <div id="<%= this.id %>" <%= this.cssClassAttr() %>>
        %%toolbar

          <%= this.innerView({ data$: this.data$ }, this.Y) %>

    */}
  ]
});
<commit_msg>foam.ui.md.UpdateDetailView: Add extra class according to data model.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.ui.md',
  name: 'UpdateDetailView',
  extends: 'foam.ui.md.BaseDetailView',

  requires: [
    'foam.ui.PopupChoiceView',
    'foam.ui.md.Toolbar',
    'foam.ui.md.ToolbarAction'
  ],

  imports: [
    'dao',
    'stack',
    'controllerMode'
  ],
  exports: [
    'toolbar as mdToolbar'
  ],

  properties: [
    {
      name: 'title',
      dynamicValue: function() {
        return (this.data && this.data.id ? 'Edit' : 'New') +
            (this.model ? ' ' + this.model.label : '');
      }
    },
    {
      name: 'rawData',
      documentation: 'The uncloned original input data.',
      postSet: function(old, nu) {
        if ( old ) old.removeListener(this.rawUpdate);
        if ( nu ) nu.addListener(this.rawUpdate);
        // HACK(markdittmer): For envelope-based detail views.
        // TODO(markdittmer): Remove this once foam.browser has U2 views that
        // can do something like this on a case-by-case basis.
        if ( nu.data && nu.data.model_ ) {
          var modelClassName = nu.data.model_.id.replace(/[.]/g, '-');
          if ( this.$ ) {
            var className = this.$.className;
            className = className.replace(new RegExp(modelClassName, 'g'), '');
            className = className + ' ' + modelClassName;
            this.$.className = className;
          } else {
            this.extraClassName = modelClassName;
          }
        }
      }
    },
    {
      name: 'originalData',
      documentation: 'A clone of the input data, for comparison with edits.'
    },
    {
      name: 'data',
      preSet: function(_, v) {
        if ( ! v ) return undefined;
        this.rawData = v;
        return v.deepClone();
      },
      postSet: function(_, data) {
        if ( ! data ) return;
        this.originalData = data.deepClone();
        if ( data && data.model_ ) this.model = data.model_;
        var self = this;
        data.addPropertyListener(null, function(o, topic) {
          var prop = o.model_.getProperty(topic[1]);
          if ( prop.transient ) return;
          self.version++;
          self.rawData = '';
        });
      }
    },
    {
      name: 'immutable',
      documentation: 'Set to true if the view should never switch to ' +
          'X-and-check mode, even when there are changes.',
      defaultValue: false
    },
    {
      name: 'liveEdit',
      documentation: "Propagate edits immediately, and don't show the save button",
      defaultValue: false
    },
    {
      name: 'exitOnSave',
      documentation: "If true, saving will also exit the view.",
      defaultValue: false
    },
    {
      // Version of the data which changes whenever any property of the data is updated.
      // Used to help trigger isEnabled / isAvailable in Actions.
      model_: 'IntProperty',
      name: 'version'
    },
    {
      model_: 'BooleanProperty',
      name: 'showModelActions',
      defaultValue: true
    },
    {
      model_: 'BooleanProperty',
      name: 'outstandingChanges',
      hidden: true,
      dynamicValue: function() {
        this.version; this.immutable;
        return ! this.immutable && ! this.originalData.equals(this.data);
      },
      postSet: function(old,nu) {
        if ( nu && this.liveEdit ) {
          this.commit_();
        }
      }
    },
    {
      type: 'foam.ui.md.Toolbar',
      name: 'toolbar',
      lazyFactory: function() {
        return this.Toolbar.create({
          data$: this.data$,
          title$: this.title$
        }, this.Y);
      },
      postSet: function(old, nu) {
        if ( old === nu ) return;
        if ( old ) {
          old.removeLeftActions(this.leftActions_);
          old.removeRightActions(this.rightActions_);
        }
        if ( nu ) {
          nu.addLeftActions(this.leftActions_);
          nu.addRightActions(this.rightActions_);
        }
      }
    },
    {
      model_: 'ViewFactoryProperty',
      name: 'innerView',
      defaultValue: 'foam.ui.md.DetailView'
    },
    {
      name: 'className',
      defaultValue: 'md-update-detail-view'
    },
    {
      model_: 'ArrayProperty',
      name: 'leftActions_',
      lazyFactory: function() {
        var myModel = this.model_;
        return [
          this.ToolbarAction.create({
            data: this,
            action: myModel.getAction('back')
          }, this.Y),
          this.ToolbarAction.create({
            data: this,
            action: myModel.getAction('reset')
          }, this.Y)
        ];
      },
      postSet: function(old, nu) {
        var toolbar = this.toolbar;
        if ( old === nu || ! toolbar ) return;
        if ( old ) toolbar.removeLeftActions(old);
        if ( nu ) toolbar.addLeftActions(nu);
      }
    },
    {
      model_: 'ArrayProperty',
      name: 'rightActions_',
      lazyFactory: function() {
        return [
          this.ToolbarAction.create({
            data: this,
            action: this.model_.getAction('save')
          }),
          this.ToolbarAction.create({
            data: this,
            action: this.model_.getAction('delete')
          })
        ];
      },
      postSet: function(old, nu) {
        var toolbar = this.toolbar;
        if ( old === nu || ! toolbar ) return;
        if ( old ) toolbar.removeRightActions(old);
        if ( nu ) toolbar.addRightActions(nu);
      }
    }
  ],

  actions: [
    {
      name: 'back',
      help:  'Go back',
      priority: 0,
      order: 0.0,
      iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAPUlEQVQ4y2NgGLbgf8P/BtKU////+78WacpDSFMeSlPlYaQo/0OacjyAcg1wJ4WTGmHDS4sWaVrqhm/mBQAoLpX9t+4i2wAAAABJRU5ErkJggg==',
      ligature: 'arrow_back',
      isAvailable: function() {
        return this.liveEdit || ! this.outstandingChanges;
      },
      code: function() { this.stack.popView(); }
    },
    {
      name: 'save',
      help:  'Save updates',
      priority: 0,
      order: 0,
      iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAUElEQVQ4jWNgGAW4wH9d0pRH///zv4E05f+J1jB0lP9n+b/0fzgJpv8PBUr++R9BgmP+N4C1RJLgdqiWKBK8CtVCUsiAtBCvHKqFFOUjAwAATIhwjZSqeJcAAAAASUVORK5CYII=',
      ligature: 'check',
      isAvailable: function() { return  ! this.liveEdit && this.outstandingChanges; },
      code: function() {
        this.commit_();
      }
    },
    {
      name: 'delete',
      iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAOklEQVQ4y2NgGPzgv8L/B/9h4MF/BXxK8QDqaCDH/aSaP6phVAMuDa+wqn+BW4P//5eYyv/7DvI8DwBDJ5LB6mdU8gAAAABJRU5ErkJggg==',
      priority: 0,
      order: 0,
      isAvailable: function() {
        return this.controllerMode && this.controllerMode == 'update';
      },
      code: function() {
        this.dao.remove(this.data, {
          remove: function() {
            this.stack.popView();
          }.bind(this),
          error: function() {
            // TODO:
          }
        });
      }
    },
    {
      name: 'reset',
      priority: 0,
      order: 1.0,
      help: 'Reset form (cancel update)',
      iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAdklEQVQ4y+WTuQ3AIBAEaQKK8NN/BEUArmccgGyj43MMIZo5TqtFqbUPJxYtbg2OvS44IJQKhguwdUETSiXjXr77KhGICRjihWKm8Dw3KXP4Z5VZ/Lfw7B5kyD1cy5C7uAx5iJcht6vhRTUi4OrC0Szftvi/vAFNdbZ2Dp661QAAAABJRU5ErkJggg==',
      ligature: 'close',
      isAvailable: function() { return  ! this.liveEdit && this.outstandingChanges; },
      code: function() { this.data = this.originalData.deepClone(); }
    }
  ],

  methods: [
    function commit_() {
      var self = this;
      var obj  = this.data;

      this.dao.put(obj, {
        put: function() {
          self.originalData = obj.deepClone();

          if (self.exitOnSave && ! self.liveEdit) {
            self.stack.popView();
          }
        },
        error: function() {
          console.error('Error saving', arguments);
        }
      });
    }
  ],

  listeners: [
    {
      name: 'rawUpdate',
      code: function() {
        // If this listener fires, the raw data updated and the user hasn't
        // changed anything.
        this.data = this.rawData;
      }
    }
  ],

  templates: [
    function CSS() {/*
      .md-update-detail-view {
        display: flex;
        flex-direction: column;
        height: 100%;
        width: 100%;
      }
      .md-update-detail-view-body {
        overflow-x: hidden;
        overflow-y: auto;
      }
    */},
    function toHTML() {/*
      <div id="<%= this.id %>" <%= this.cssClassAttr() %>>
        %%toolbar

          <%= this.innerView({ data$: this.data$ }, this.Y) %>

    */}
  ]
});
<|endoftext|>


<commit_before>const childProcess = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');

const chai = require('chai');
chai.use(require('chai-diff'));
const expect = chai.expect;

const testCases = [
  {
    binInputParameters: ['-a', 'spec/artifacts/empty.handlebars'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.amd.js'
  },
  {
    binInputParameters: [
      '-a',
      '-f',
      'TEST_OUTPUT',
      'spec/artifacts/empty.handlebars'
    ],
    outputLocation: 'TEST_OUTPUT',
    expectedOutputSpec: './spec/expected/empty.amd.js'
  },
  {
    binInputParameters: [
      '-a',
      '-n',
      'CustomNamespace.templates',
      'spec/artifacts/empty.handlebars'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.amd.namespace.js'
  },
  {
    binInputParameters: [
      '-a',
      '--namespace',
      'CustomNamespace.templates',
      'spec/artifacts/empty.handlebars'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.amd.namespace.js'
  },
  {
    binInputParameters: ['-a', '-s', 'spec/artifacts/empty.handlebars'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.amd.simple.js'
  },
  {
    binInputParameters: ['-a', '-m', 'spec/artifacts/empty.handlebars'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.amd.min.js'
  },
  {
    binInputParameters: [
      'spec/artifacts/known.helpers.handlebars',
      '-a',
      '-k',
      'someHelper',
      '-k',
      'anotherHelper',
      '-o'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/non.empty.amd.known.helper.js'
  },
  {
    binInputParameters: ['--help'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/help.menu.txt'
  },
  {
    binInputParameters: ['-v'],
    outputLocation: 'stdout',
    expectedOutput: require('../package.json').version
  },
  {
    binInputParameters: [
      '-a',
      '-e',
      'hbs',
      './spec/artifacts/non.default.extension.hbs'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/non.default.extension.amd.js'
  },
  {
    binInputParameters: [
      '-a',
      '-p',
      './spec/artifacts/partial.template.handlebars'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/partial.template.js'
  },
  {
    binInputParameters: ['spec/artifacts/empty.handlebars', '-c'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.common.js'
  },
  {
    binInputParameters: [
      'spec/artifacts/empty.handlebars',
      'spec/artifacts/empty.handlebars',
      '-a',
      '-n',
      'someNameSpace'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/namespace.amd.js'
  },
  {
    binInputParameters: [
      'spec/artifacts/empty.handlebars',
      '-h',
      'some-path/',
      '-a'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/handlebar.path.amd.js'
  },
  {
    binInputParameters: [
      'spec/artifacts/partial.template.handlebars',
      '-r',
      'spec',
      '-a'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.root.amd.js'
  },
  {
    binInputParameters: [
      '-i',
      '
1
',
      '-i',
      '
2
',
      '-N',
      'firstTemplate',
      '-N',
      'secondTemplate',
      '-a'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.name.amd.js'
  },
  {
    binInputParameters: [
      '-i',
      '
1
',
      '-a',
      '-m',
      '-N',
      'test',
      '--map',
      './spec/artifacts/source.map.amd.txt'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/source.map.amd.js'
  },
  {
    binInputParameters: [
      '-i',
      '
1
',
      '-a',
      '-m',
      '-N',
      'test',
      '--map',
      './spec/artifacts/source.map.amd.txt'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/source.map.amd.js'
  },
  {
    binInputParameters: ['./spec/artifacts/bom.handlebars', '-b', '-a'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/bom.amd.js'
  }
];

module.exports = function(grunt) {
  grunt.registerTask('test:bin', function() {
    testCases.forEach(
      ({
        binInputParameters,
        outputLocation,
        expectedOutputSpec,
        expectedOutput
      }) => {
        const stdout = executeBinHandlebars(...binInputParameters);

        if (!expectedOutput && expectedOutputSpec) {
          expectedOutput = fs.readFileSync(expectedOutputSpec, 'utf-8');
        }

        const useStdout = outputLocation === 'stdout';
        const normalizedOutput = normalizeCrlf(
          useStdout ? stdout : fs.readFileSync(outputLocation, 'utf-8')
        );
        const normalizedExpectedOutput = normalizeCrlf(expectedOutput);

        if (!useStdout) {
          fs.unlinkSync(outputLocation);
        }

        expect(normalizedOutput).not.to.be.differentFrom(
          normalizedExpectedOutput,
          {
            relaxedSpace: true
          }
        );
      }
    );
  });
};

// helper functions

function executeBinHandlebars(...args) {
  if (os.platform() === 'win32') {
    // On Windows, the executable handlebars.js file cannot be run directly
    const nodeJs = process.argv[0];
    return execFilesSyncUtf8(nodeJs, ['./bin/handlebars'].concat(args));
  }
  return execFilesSyncUtf8('./bin/handlebars', args);
}

function execFilesSyncUtf8(command, args) {
  const env = process.env;
  env.PATH = addPathToNodeJs(env.PATH);
  return childProcess.execFileSync(command, args, { encoding: 'utf-8', env });
}

function addPathToNodeJs(pathEnvironment) {
  return path.dirname(process.argv0) + path.delimiter + pathEnvironment;
}

function normalizeCrlf(string) {
  if (typeof string === 'string') {
    return string.replace(/\r\n/g, '\n');
  }
  return string;
}
<commit_msg>tasks/test-bin.js: Delete duplicate test<commit_after>const childProcess = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');

const chai = require('chai');
chai.use(require('chai-diff'));
const expect = chai.expect;

const testCases = [
  {
    binInputParameters: ['-a', 'spec/artifacts/empty.handlebars'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.amd.js'
  },
  {
    binInputParameters: [
      '-a',
      '-f',
      'TEST_OUTPUT',
      'spec/artifacts/empty.handlebars'
    ],
    outputLocation: 'TEST_OUTPUT',
    expectedOutputSpec: './spec/expected/empty.amd.js'
  },
  {
    binInputParameters: [
      '-a',
      '-n',
      'CustomNamespace.templates',
      'spec/artifacts/empty.handlebars'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.amd.namespace.js'
  },
  {
    binInputParameters: [
      '-a',
      '--namespace',
      'CustomNamespace.templates',
      'spec/artifacts/empty.handlebars'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.amd.namespace.js'
  },
  {
    binInputParameters: ['-a', '-s', 'spec/artifacts/empty.handlebars'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.amd.simple.js'
  },
  {
    binInputParameters: ['-a', '-m', 'spec/artifacts/empty.handlebars'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.amd.min.js'
  },
  {
    binInputParameters: [
      'spec/artifacts/known.helpers.handlebars',
      '-a',
      '-k',
      'someHelper',
      '-k',
      'anotherHelper',
      '-o'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/non.empty.amd.known.helper.js'
  },
  {
    binInputParameters: ['--help'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/help.menu.txt'
  },
  {
    binInputParameters: ['-v'],
    outputLocation: 'stdout',
    expectedOutput: require('../package.json').version
  },
  {
    binInputParameters: [
      '-a',
      '-e',
      'hbs',
      './spec/artifacts/non.default.extension.hbs'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/non.default.extension.amd.js'
  },
  {
    binInputParameters: [
      '-a',
      '-p',
      './spec/artifacts/partial.template.handlebars'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/partial.template.js'
  },
  {
    binInputParameters: ['spec/artifacts/empty.handlebars', '-c'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.common.js'
  },
  {
    binInputParameters: [
      'spec/artifacts/empty.handlebars',
      'spec/artifacts/empty.handlebars',
      '-a',
      '-n',
      'someNameSpace'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/namespace.amd.js'
  },
  {
    binInputParameters: [
      'spec/artifacts/empty.handlebars',
      '-h',
      'some-path/',
      '-a'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/handlebar.path.amd.js'
  },
  {
    binInputParameters: [
      'spec/artifacts/partial.template.handlebars',
      '-r',
      'spec',
      '-a'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.root.amd.js'
  },
  {
    binInputParameters: [
      '-i',
      '
1
',
      '-i',
      '
2
',
      '-N',
      'firstTemplate',
      '-N',
      'secondTemplate',
      '-a'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/empty.name.amd.js'
  },
  {
    binInputParameters: [
      '-i',
      '
1
',
      '-a',
      '-m',
      '-N',
      'test',
      '--map',
      './spec/artifacts/source.map.amd.txt'
    ],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/source.map.amd.js'
  },
  {
    binInputParameters: ['./spec/artifacts/bom.handlebars', '-b', '-a'],
    outputLocation: 'stdout',
    expectedOutputSpec: './spec/expected/bom.amd.js'
  }
];

module.exports = function(grunt) {
  grunt.registerTask('test:bin', function() {
    testCases.forEach(
      ({
        binInputParameters,
        outputLocation,
        expectedOutputSpec,
        expectedOutput
      }) => {
        const stdout = executeBinHandlebars(...binInputParameters);

        if (!expectedOutput && expectedOutputSpec) {
          expectedOutput = fs.readFileSync(expectedOutputSpec, 'utf-8');
        }

        const useStdout = outputLocation === 'stdout';
        const normalizedOutput = normalizeCrlf(
          useStdout ? stdout : fs.readFileSync(outputLocation, 'utf-8')
        );
        const normalizedExpectedOutput = normalizeCrlf(expectedOutput);

        if (!useStdout) {
          fs.unlinkSync(outputLocation);
        }

        expect(normalizedOutput).not.to.be.differentFrom(
          normalizedExpectedOutput,
          {
            relaxedSpace: true
          }
        );
      }
    );
  });
};

// helper functions

function executeBinHandlebars(...args) {
  if (os.platform() === 'win32') {
    // On Windows, the executable handlebars.js file cannot be run directly
    const nodeJs = process.argv[0];
    return execFilesSyncUtf8(nodeJs, ['./bin/handlebars'].concat(args));
  }
  return execFilesSyncUtf8('./bin/handlebars', args);
}

function execFilesSyncUtf8(command, args) {
  const env = process.env;
  env.PATH = addPathToNodeJs(env.PATH);
  return childProcess.execFileSync(command, args, { encoding: 'utf-8', env });
}

function addPathToNodeJs(pathEnvironment) {
  return path.dirname(process.argv0) + path.delimiter + pathEnvironment;
}

function normalizeCrlf(string) {
  if (typeof string === 'string') {
    return string.replace(/\r\n/g, '\n');
  }
  return string;
}
<|endoftext|>


<commit_before>// Copyright 2015-2018, University of Colorado Boulder

/**
 * Slider, with support for horizontal and vertical orientations. By default, the slider is constructed in the
 * horizontal orientation, then adjusted if the vertical orientation was specified.
 *
 * Note: This type was originally named HSlider, renamed in https://github.com/phetsims/sun/issues/380.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var AccessibleSlider = require( 'SUN/accessibility/AccessibleSlider' );
  var BooleanIO = require( 'TANDEM/types/BooleanIO' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var FocusHighlightFromNode = require( 'SCENERY/accessibility/FocusHighlightFromNode' );
  var inherit = require( 'PHET_CORE/inherit' );
  var InstanceRegistry = require( 'PHET_CORE/documentation/InstanceRegistry' );
  var LinearFunction = require( 'DOT/LinearFunction' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Property = require( 'AXON/Property' );
  var PropertyIO = require( 'AXON/PropertyIO' );
  var Range = require( 'DOT/Range' );
  var RangeIO = require( 'DOT/RangeIO' );
  var Shape = require( 'KITE/Shape' );
  var SimpleDragHandler = require( 'SCENERY/input/SimpleDragHandler' );
  var SliderIO = require( 'SUN/SliderIO' );
  var SliderThumb = require( 'SUN/SliderThumb' );
  var SliderTrack = require( 'SUN/SliderTrack' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Util = require( 'DOT/Util' );

  // constants
  var VERTICAL_ROTATION = -Math.PI / 2;

  /**
   * @param {Property.<number>} valueProperty
   * @param {Range} range
   * @param {Object} [options]
   * @constructor
   */
  function Slider( valueProperty, range, options ) {

    var self = this;

    options = _.extend( {

      orientation: 'horizontal', // 'horizontal'|'vertical'

      // track
      trackSize: new Dimension2( 100, 5 ),
      trackFillEnabled: 'white',
      trackFillDisabled: 'gray',
      trackStroke: 'black',
      trackLineWidth: 1,
      trackCornerRadius: 0,

      // {Node} optional thumb, replaces the default.
      // Client is responsible for highlighting, disabling and pointer areas.
      // The thumb is positioned based on its center and hence can have its origin anywhere
      thumbNode: null,

      // Options for the default thumb, ignored if thumbNode is set
      thumbSize: new Dimension2( 22, 45 ),
      thumbFillEnabled: 'rgb(50,145,184)',
      thumbFillHighlighted: 'rgb(71,207,255)',
      thumbFillDisabled: '#F0F0F0',
      thumbStroke: 'black',
      thumbLineWidth: 1,
      thumbYOffset: 0, // center of the thumb is vertically offset by this amount from the center of the track
      thumbCenterLineStroke: 'white',
      thumbTouchAreaXDilation: 11,
      thumbTouchAreaYDilation: 11,
      thumbMouseAreaXDilation: 0,
      thumbMouseAreaYDilation: 0,

      // ticks
      tickLabelSpacing: 6,
      majorTickLength: 25,
      majorTickStroke: 'black',
      majorTickLineWidth: 1,
      minorTickLength: 10,
      minorTickStroke: 'black',
      minorTickLineWidth: 1,

      // other
      cursor: 'pointer',
      startDrag: function() {}, // called when a drag sequence starts
      endDrag: function() {}, // called when a drag sequence ends
      constrainValue: function( value ) { return value; }, // called before valueProperty is set

      enabledProperty: null, // see below
      enabledRangeProperty: null, // see below

      // phet-io
      tandem: Tandem.required,
      phetioType: SliderIO
    }, options );

    assert && assert( range instanceof Range, 'range must be of type Range:' + range );
    assert && assert( options.orientation === 'horizontal' || options.orientation === 'vertical',
      'invalid orientation: ' + options.orientation );
    this.orientation = options.orientation; // @private

    Node.call( this );

    var ownsEnabledProperty = !options.enabledProperty;
    var ownsEnabledRangeProperty = !options.enabledRangeProperty;

    // phet-io, Assign default options that need tandems.
    options.enabledProperty = options.enabledProperty || new Property( true, {
      tandem: options.tandem.createTandem( 'enabledProperty' ),
      phetioType: PropertyIO( BooleanIO )
    } );

    // controls the portion of the slider that is enabled
    options.enabledRangeProperty = options.enabledRangeProperty || new Property( range, {
      tandem: options.tandem.createTandem( 'enabledRangeProperty' ),
      phetioType: PropertyIO( RangeIO )
    } );

    // @public
    this.enabledProperty = options.enabledProperty;
    this.enabledRangeProperty = options.enabledRangeProperty;

    // @private options needed by prototype functions that add ticks
    this.tickOptions = _.pick( options, 'tickLabelSpacing',
      'majorTickLength', 'majorTickStroke', 'majorTickLineWidth',
      'minorTickLength', 'minorTickStroke', 'minorTickLineWidth' );

    var sliderParts = [];

    // @private ticks are added to these parents, so they are behind the knob
    this.majorTicksParent = new Node();
    this.minorTicksParent = new Node();
    sliderParts.push( this.majorTicksParent );
    sliderParts.push( this.minorTicksParent );

    // @private mapping between value and track position
    this.valueToPosition = new LinearFunction( range.min, range.max, 0, options.trackSize.width, true /* clamp */ );

    // @private track
    this.track = new SliderTrack( valueProperty, this.valueToPosition, {

      // propagate options that are specific to SliderTrack
      size: options.trackSize,
      fillEnabled: options.trackFillEnabled,
      fillDisabled: options.trackFillDisabled,
      stroke: options.trackStroke,
      lineWidth: options.trackLineWidth,
      cornerRadius: options.trackCornerRadius,
      enabledProperty: options.enabledProperty,
      startDrag: options.startDrag,
      endDrag: options.endDrag,
      constrainValue: options.constrainValue,

      // phet-io
      tandem: options.tandem.createTandem( 'track' )
    } );
    this.track.centerX = this.valueToPosition( ( range.max + range.min ) / 2 );

    // The thumb of the slider
    var thumb = options.thumbNode || new SliderThumb( this.enabledProperty, {

      // propagate options that are specific to SliderThumb
      size: options.thumbSize,
      fillEnabled: options.thumbFillEnabled,
      fillHighlighted: options.thumbFillHighlighted,
      fillDisabled: options.thumbFillDisabled,
      stroke: options.thumbStroke,
      lineWidth: options.thumbLineWidth,
      centerLineStroke: options.thumbCenterLineStroke,
      tandem: options.tandem.createTandem( 'thumb' )
    } );

    // Dilate the local bounds horizontally so that it extends beyond where the thumb can reach.  This prevents layout
    // asymmetry when the slider thumb is off the edges of the track.  See https://github.com/phetsims/sun/issues/282
    this.track.localBounds = this.track.localBounds.dilatedX( thumb.width / 2 );

    // Add the track
    sliderParts.push( this.track );

    // do this outside of options hash, so that it applied to both default and custom thumbs
    thumb.centerY = this.track.centerY + options.thumbYOffset;
    sliderParts.push( thumb );

    // Wrap all of the slider parts in a Node, and set the orientation of that Node.
    // This allows us to still decorate the Slider with additional children.
    // See https://github.com/phetsims/sun/issues/406
    var sliderPartsNode = new Node( { children: sliderParts } );
    if ( options.orientation === 'vertical' ) {
      sliderPartsNode.rotation = VERTICAL_ROTATION;
    }
    this.addChild( sliderPartsNode );

    // touchArea for the default thumb. If a custom thumb is provided, the client is responsible for its touchArea.
    if ( !options.thumbNode && ( options.thumbTouchAreaXDilation || options.thumbTouchAreaYDilation ) ) {
      thumb.touchArea = thumb.localBounds.dilatedXY( options.thumbTouchAreaXDilation, options.thumbTouchAreaYDilation );
    }

    // mouseArea for the default thumb. If a custom thumb is provided, the client is responsible for its mouseArea.
    if ( !options.thumbNode && ( options.thumbMouseAreaXDilation || options.thumbMouseAreaYDilation ) ) {
      thumb.mouseArea = thumb.localBounds.dilatedXY( options.thumbMouseAreaXDilation, options.thumbMouseAreaYDilation );
    }

    // update value when thumb is dragged
    var clickXOffset = 0; // x-offset between initial click and thumb's origin
    var thumbInputListener = new SimpleDragHandler( {

      tandem: options.tandem.createTandem( 'thumbInputListener' ),

      allowTouchSnag: true,

      start: function( event, trail ) {
        if ( self.enabledProperty.get() ) {
          options.startDrag();
          var transform = trail.subtrailTo( sliderPartsNode ).getTransform();

          // Determine the offset relative to the center of the thumb
          clickXOffset = transform.inversePosition2( event.pointer.point ).x - thumb.centerX;
        }
      },

      drag: function( event, trail ) {
        if ( self.enabledProperty.get() ) {
          var transform = trail.subtrailTo( sliderPartsNode ).getTransform(); // we only want the transform to our parent
          var x = transform.inversePosition2( event.pointer.point ).x - clickXOffset;
          var newValue = self.valueToPosition.inverse( x );

          valueProperty.set( options.constrainValue( newValue ) );
        }
      },

      end: function() {
        if ( self.enabledProperty.get() ) {
          options.endDrag();
        }
      }
    } );
    thumb.addInputListener( thumbInputListener );

    // @public (read-only) {Boolean} - flag that indicates whether the thumb is currently being dragged
    this.thumbDragging = false;
    var thumbDraggingListener = thumbInputListener.isDraggingProperty.linkAttribute( this, 'thumbDragging' );

    // enable/disable
    var enabledObserver = function( enabled ) {
      self.cursor = self.enabledProperty.get() ? options.cursor : 'default';
      if ( !enabled ) {
        if ( thumbInputListener.dragging ) { thumbInputListener.endDrag(); }
      }
      self.pickable = enabled;
    };
    this.enabledProperty.link( enabledObserver ); // must be unlinked in disposeSlider

    // a11y - custom focus highlight that surrounds and moves with the thumb
    this.focusHighlight = new FocusHighlightFromNode( thumb );

    // update thumb location when value changes
    var valueObserver = function( value ) {
      thumb.centerX = self.valueToPosition( value );
    };
    valueProperty.link( valueObserver ); // must be unlinked in disposeSlider

    // when the enabled range changes, the value to position linear function must change as well
    var enabledRangeObserver = function( enabledRange ) {

      assert && assert( enabledRange.min >= range.min && enabledRange.max <= range.max, 'enabledRange is out of range' );

      var initialValueToPosition = new LinearFunction( range.min, range.max, 0, options.trackSize.width, true /* clamp */ );
      var min = initialValueToPosition( enabledRange.min );
      var max = initialValueToPosition( enabledRange.max );

      // update the geometry of the enabled track
      self.track.updateEnabledTrackWidth( min, max );

      // update the function that maps value to position for the track and the slider
      self.valueToPosition = new LinearFunction( enabledRange.min, enabledRange.max, min, max, true /* clamp */ );
      self.track.valueToPosition = self.valueToPosition;

      // clamp the value to the enabled range if it changes
      valueProperty.set( Util.clamp( valueProperty.value, enabledRange.min, enabledRange.max ) );
    };
    this.enabledRangeProperty.link( enabledRangeObserver ); // needs to be unlinked in dispose function

    this.mutate( options );

    // @private Called by dispose
    this.disposeSlider = function() {
      thumb.dispose && thumb.dispose(); // in case a custom thumb is provided via options.thumbNode that doesn't implement dispose
      self.track.dispose();
      self.disposeAccessibleSlider(); // dispose accessibility
      valueProperty.unlink( valueObserver );
      ownsEnabledRangeProperty && self.enabledRangeProperty.dispose();
      ownsEnabledProperty && self.enabledProperty.dispose();
      thumbInputListener.isDraggingProperty.unlinkAttribute( thumbDraggingListener );
      thumbInputListener.dispose();
    };

    // mix accessible slider functionality into Slider
    this.initializeAccessibleSlider( valueProperty, this.enabledRangeProperty, this.enabledProperty,
      _.extend( {}, options, {
        ariaOrientation: options.orientation,
        roundToStepSize: true
      } ) );

    // support for binder documentation, stripped out in builds and only runs when ?binder is specified
    assert && phet.chipper.queryParameters.binder && InstanceRegistry.registerDataURL( 'sun', 'Slider', this );
  }

  sun.register( 'Slider', Slider );

  inherit( Node, Slider, {

    // @public - ensures that this object is eligible for GC
    dispose: function() {
      this.disposeSlider();
      Node.prototype.dispose.call( this );
    },

    /**
     * Adds a major tick mark.
     * @param {number} value
     * @param {Node} [label] optional
     * @public
     */
    addMajorTick: function( value, label ) {
      this.addTick( this.majorTicksParent, value, label,
        this.tickOptions.majorTickLength, this.tickOptions.majorTickStroke, this.tickOptions.majorTickLineWidth );
    },

    /**
     * Adds a minor tick mark.
     * @param {number} value
     * @param {Node} [label] optional
     * @public
     */
    addMinorTick: function( value, label ) {
      this.addTick( this.minorTicksParent, value, label,
        this.tickOptions.minorTickLength, this.tickOptions.minorTickStroke, this.tickOptions.minorTickLineWidth );
    },

    /*
     * Adds a tick mark above the track.
     * @param {Node} parent
     * @param {number} value
     * @param {Node} [label] optional
     * @param {number} length
     * @param {number} stroke
     * @param {number} lineWidth
     * @private
     */
    addTick: function( parent, value, label, length, stroke, lineWidth ) {
      var labelX = this.valueToPosition( value );

      // ticks
      var tick = new Path( new Shape()
          .moveTo( labelX, this.track.top )
          .lineTo( labelX, this.track.top - length ),
        { stroke: stroke, lineWidth: lineWidth } );
      parent.addChild( tick );

      // label
      if ( label ) {

        // For a vertical slider, rotate labels opposite the rotation of the slider, so that they appear as expected.
        if ( this.orientation === 'vertical' ) {
          label.rotation = -VERTICAL_ROTATION;
        }
        parent.addChild( label );
        label.centerX = tick.centerX;
        label.bottom = tick.top - this.tickOptions.tickLabelSpacing;
        label.pickable = false;
      }
    },

    // @public
    setEnabled: function( enabled ) { this.enabledProperty.value = enabled; },
    set enabled( value ) { this.setEnabled( value ); },

    // @public
    getEnabled: function() { return this.enabledProperty.value; },
    get enabled() { return this.getEnabled(); },

    // @public
    setEnabledRange: function( enabledRange ) { this.enabledRangeProperty.value = enabledRange; },
    set enabledRange( range ) { this.setEnabledRange( range ); },

    // @public
    getEnabledRange: function() { return this.enabledRangeProperty.value; },
    get enabledRange() { return this.getEnabledRange(); },

    // @public - Sets visibility of major ticks.
    setMajorTicksVisible: function( visible ) {
      this.majorTicksParent.visible = visible;
    },
    set majorTicksVisible( value ) { this.setMajorTicksVisible( value ); },

    // @public - Gets visibility of major ticks.
    getMajorTicksVisible: function() {
      return this.majorTicksParent.visible;
    },
    get majorTicksVisible() { return this.getMajorTicksVisible(); },

    // @public - Sets visibility of minor ticks.
    setMinorTicksVisible: function( visible ) {
      this.minorTicksParent.visible = visible;
    },
    set minorTicksVisible( value ) { this.setMinorTicksVisible( value ); },

    // @public - Gets visibility of minor ticks.
    getMinorTicksVisible: function() {
      return this.minorTicksParent.visible;
    },
    get minorTicksVisible() { return this.getMinorTicksVisible(); }
  } );

  // mix accessibility into Slider
  AccessibleSlider.mixInto( Slider );

  return Slider;
} );<commit_msg>Factored out noop and identity, see https://github.com/phetsims/tandem/issues/71<commit_after>// Copyright 2015-2018, University of Colorado Boulder

/**
 * Slider, with support for horizontal and vertical orientations. By default, the slider is constructed in the
 * horizontal orientation, then adjusted if the vertical orientation was specified.
 *
 * Note: This type was originally named HSlider, renamed in https://github.com/phetsims/sun/issues/380.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var AccessibleSlider = require( 'SUN/accessibility/AccessibleSlider' );
  var BooleanIO = require( 'TANDEM/types/BooleanIO' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var FocusHighlightFromNode = require( 'SCENERY/accessibility/FocusHighlightFromNode' );
  var inherit = require( 'PHET_CORE/inherit' );
  var InstanceRegistry = require( 'PHET_CORE/documentation/InstanceRegistry' );
  var LinearFunction = require( 'DOT/LinearFunction' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Property = require( 'AXON/Property' );
  var PropertyIO = require( 'AXON/PropertyIO' );
  var Range = require( 'DOT/Range' );
  var RangeIO = require( 'DOT/RangeIO' );
  var Shape = require( 'KITE/Shape' );
  var SimpleDragHandler = require( 'SCENERY/input/SimpleDragHandler' );
  var SliderIO = require( 'SUN/SliderIO' );
  var SliderThumb = require( 'SUN/SliderThumb' );
  var SliderTrack = require( 'SUN/SliderTrack' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Util = require( 'DOT/Util' );

  // constants
  var VERTICAL_ROTATION = -Math.PI / 2;

  /**
   * @param {Property.<number>} valueProperty
   * @param {Range} range
   * @param {Object} [options]
   * @constructor
   */
  function Slider( valueProperty, range, options ) {

    var self = this;

    options = _.extend( {

      orientation: 'horizontal', // 'horizontal'|'vertical'

      // track
      trackSize: new Dimension2( 100, 5 ),
      trackFillEnabled: 'white',
      trackFillDisabled: 'gray',
      trackStroke: 'black',
      trackLineWidth: 1,
      trackCornerRadius: 0,

      // {Node} optional thumb, replaces the default.
      // Client is responsible for highlighting, disabling and pointer areas.
      // The thumb is positioned based on its center and hence can have its origin anywhere
      thumbNode: null,

      // Options for the default thumb, ignored if thumbNode is set
      thumbSize: new Dimension2( 22, 45 ),
      thumbFillEnabled: 'rgb(50,145,184)',
      thumbFillHighlighted: 'rgb(71,207,255)',
      thumbFillDisabled: '#F0F0F0',
      thumbStroke: 'black',
      thumbLineWidth: 1,
      thumbYOffset: 0, // center of the thumb is vertically offset by this amount from the center of the track
      thumbCenterLineStroke: 'white',
      thumbTouchAreaXDilation: 11,
      thumbTouchAreaYDilation: 11,
      thumbMouseAreaXDilation: 0,
      thumbMouseAreaYDilation: 0,

      // ticks
      tickLabelSpacing: 6,
      majorTickLength: 25,
      majorTickStroke: 'black',
      majorTickLineWidth: 1,
      minorTickLength: 10,
      minorTickStroke: 'black',
      minorTickLineWidth: 1,

      // other
      cursor: 'pointer',
      startDrag: _.noop, // called when a drag sequence starts
      endDrag: _.noop, // called when a drag sequence ends
      constrainValue: _.identity, // called before valueProperty is set

      enabledProperty: null, // see below
      enabledRangeProperty: null, // see below

      // phet-io
      tandem: Tandem.required,
      phetioType: SliderIO
    }, options );

    assert && assert( range instanceof Range, 'range must be of type Range:' + range );
    assert && assert( options.orientation === 'horizontal' || options.orientation === 'vertical',
      'invalid orientation: ' + options.orientation );
    this.orientation = options.orientation; // @private

    Node.call( this );

    var ownsEnabledProperty = !options.enabledProperty;
    var ownsEnabledRangeProperty = !options.enabledRangeProperty;

    // phet-io, Assign default options that need tandems.
    options.enabledProperty = options.enabledProperty || new Property( true, {
      tandem: options.tandem.createTandem( 'enabledProperty' ),
      phetioType: PropertyIO( BooleanIO )
    } );

    // controls the portion of the slider that is enabled
    options.enabledRangeProperty = options.enabledRangeProperty || new Property( range, {
      tandem: options.tandem.createTandem( 'enabledRangeProperty' ),
      phetioType: PropertyIO( RangeIO )
    } );

    // @public
    this.enabledProperty = options.enabledProperty;
    this.enabledRangeProperty = options.enabledRangeProperty;

    // @private options needed by prototype functions that add ticks
    this.tickOptions = _.pick( options, 'tickLabelSpacing',
      'majorTickLength', 'majorTickStroke', 'majorTickLineWidth',
      'minorTickLength', 'minorTickStroke', 'minorTickLineWidth' );

    var sliderParts = [];

    // @private ticks are added to these parents, so they are behind the knob
    this.majorTicksParent = new Node();
    this.minorTicksParent = new Node();
    sliderParts.push( this.majorTicksParent );
    sliderParts.push( this.minorTicksParent );

    // @private mapping between value and track position
    this.valueToPosition = new LinearFunction( range.min, range.max, 0, options.trackSize.width, true /* clamp */ );

    // @private track
    this.track = new SliderTrack( valueProperty, this.valueToPosition, {

      // propagate options that are specific to SliderTrack
      size: options.trackSize,
      fillEnabled: options.trackFillEnabled,
      fillDisabled: options.trackFillDisabled,
      stroke: options.trackStroke,
      lineWidth: options.trackLineWidth,
      cornerRadius: options.trackCornerRadius,
      enabledProperty: options.enabledProperty,
      startDrag: options.startDrag,
      endDrag: options.endDrag,
      constrainValue: options.constrainValue,

      // phet-io
      tandem: options.tandem.createTandem( 'track' )
    } );
    this.track.centerX = this.valueToPosition( ( range.max + range.min ) / 2 );

    // The thumb of the slider
    var thumb = options.thumbNode || new SliderThumb( this.enabledProperty, {

      // propagate options that are specific to SliderThumb
      size: options.thumbSize,
      fillEnabled: options.thumbFillEnabled,
      fillHighlighted: options.thumbFillHighlighted,
      fillDisabled: options.thumbFillDisabled,
      stroke: options.thumbStroke,
      lineWidth: options.thumbLineWidth,
      centerLineStroke: options.thumbCenterLineStroke,
      tandem: options.tandem.createTandem( 'thumb' )
    } );

    // Dilate the local bounds horizontally so that it extends beyond where the thumb can reach.  This prevents layout
    // asymmetry when the slider thumb is off the edges of the track.  See https://github.com/phetsims/sun/issues/282
    this.track.localBounds = this.track.localBounds.dilatedX( thumb.width / 2 );

    // Add the track
    sliderParts.push( this.track );

    // do this outside of options hash, so that it applied to both default and custom thumbs
    thumb.centerY = this.track.centerY + options.thumbYOffset;
    sliderParts.push( thumb );

    // Wrap all of the slider parts in a Node, and set the orientation of that Node.
    // This allows us to still decorate the Slider with additional children.
    // See https://github.com/phetsims/sun/issues/406
    var sliderPartsNode = new Node( { children: sliderParts } );
    if ( options.orientation === 'vertical' ) {
      sliderPartsNode.rotation = VERTICAL_ROTATION;
    }
    this.addChild( sliderPartsNode );

    // touchArea for the default thumb. If a custom thumb is provided, the client is responsible for its touchArea.
    if ( !options.thumbNode && ( options.thumbTouchAreaXDilation || options.thumbTouchAreaYDilation ) ) {
      thumb.touchArea = thumb.localBounds.dilatedXY( options.thumbTouchAreaXDilation, options.thumbTouchAreaYDilation );
    }

    // mouseArea for the default thumb. If a custom thumb is provided, the client is responsible for its mouseArea.
    if ( !options.thumbNode && ( options.thumbMouseAreaXDilation || options.thumbMouseAreaYDilation ) ) {
      thumb.mouseArea = thumb.localBounds.dilatedXY( options.thumbMouseAreaXDilation, options.thumbMouseAreaYDilation );
    }

    // update value when thumb is dragged
    var clickXOffset = 0; // x-offset between initial click and thumb's origin
    var thumbInputListener = new SimpleDragHandler( {

      tandem: options.tandem.createTandem( 'thumbInputListener' ),

      allowTouchSnag: true,

      start: function( event, trail ) {
        if ( self.enabledProperty.get() ) {
          options.startDrag();
          var transform = trail.subtrailTo( sliderPartsNode ).getTransform();

          // Determine the offset relative to the center of the thumb
          clickXOffset = transform.inversePosition2( event.pointer.point ).x - thumb.centerX;
        }
      },

      drag: function( event, trail ) {
        if ( self.enabledProperty.get() ) {
          var transform = trail.subtrailTo( sliderPartsNode ).getTransform(); // we only want the transform to our parent
          var x = transform.inversePosition2( event.pointer.point ).x - clickXOffset;
          var newValue = self.valueToPosition.inverse( x );

          valueProperty.set( options.constrainValue( newValue ) );
        }
      },

      end: function() {
        if ( self.enabledProperty.get() ) {
          options.endDrag();
        }
      }
    } );
    thumb.addInputListener( thumbInputListener );

    // @public (read-only) {Boolean} - flag that indicates whether the thumb is currently being dragged
    this.thumbDragging = false;
    var thumbDraggingListener = thumbInputListener.isDraggingProperty.linkAttribute( this, 'thumbDragging' );

    // enable/disable
    var enabledObserver = function( enabled ) {
      self.cursor = self.enabledProperty.get() ? options.cursor : 'default';
      if ( !enabled ) {
        if ( thumbInputListener.dragging ) { thumbInputListener.endDrag(); }
      }
      self.pickable = enabled;
    };
    this.enabledProperty.link( enabledObserver ); // must be unlinked in disposeSlider

    // a11y - custom focus highlight that surrounds and moves with the thumb
    this.focusHighlight = new FocusHighlightFromNode( thumb );

    // update thumb location when value changes
    var valueObserver = function( value ) {
      thumb.centerX = self.valueToPosition( value );
    };
    valueProperty.link( valueObserver ); // must be unlinked in disposeSlider

    // when the enabled range changes, the value to position linear function must change as well
    var enabledRangeObserver = function( enabledRange ) {

      assert && assert( enabledRange.min >= range.min && enabledRange.max <= range.max, 'enabledRange is out of range' );

      var initialValueToPosition = new LinearFunction( range.min, range.max, 0, options.trackSize.width, true /* clamp */ );
      var min = initialValueToPosition( enabledRange.min );
      var max = initialValueToPosition( enabledRange.max );

      // update the geometry of the enabled track
      self.track.updateEnabledTrackWidth( min, max );

      // update the function that maps value to position for the track and the slider
      self.valueToPosition = new LinearFunction( enabledRange.min, enabledRange.max, min, max, true /* clamp */ );
      self.track.valueToPosition = self.valueToPosition;

      // clamp the value to the enabled range if it changes
      valueProperty.set( Util.clamp( valueProperty.value, enabledRange.min, enabledRange.max ) );
    };
    this.enabledRangeProperty.link( enabledRangeObserver ); // needs to be unlinked in dispose function

    this.mutate( options );

    // @private Called by dispose
    this.disposeSlider = function() {
      thumb.dispose && thumb.dispose(); // in case a custom thumb is provided via options.thumbNode that doesn't implement dispose
      self.track.dispose();
      self.disposeAccessibleSlider(); // dispose accessibility
      valueProperty.unlink( valueObserver );
      ownsEnabledRangeProperty && self.enabledRangeProperty.dispose();
      ownsEnabledProperty && self.enabledProperty.dispose();
      thumbInputListener.isDraggingProperty.unlinkAttribute( thumbDraggingListener );
      thumbInputListener.dispose();
    };

    // mix accessible slider functionality into Slider
    this.initializeAccessibleSlider( valueProperty, this.enabledRangeProperty, this.enabledProperty,
      _.extend( {}, options, {
        ariaOrientation: options.orientation,
        roundToStepSize: true
      } ) );

    // support for binder documentation, stripped out in builds and only runs when ?binder is specified
    assert && phet.chipper.queryParameters.binder && InstanceRegistry.registerDataURL( 'sun', 'Slider', this );
  }

  sun.register( 'Slider', Slider );

  inherit( Node, Slider, {

    // @public - ensures that this object is eligible for GC
    dispose: function() {
      this.disposeSlider();
      Node.prototype.dispose.call( this );
    },

    /**
     * Adds a major tick mark.
     * @param {number} value
     * @param {Node} [label] optional
     * @public
     */
    addMajorTick: function( value, label ) {
      this.addTick( this.majorTicksParent, value, label,
        this.tickOptions.majorTickLength, this.tickOptions.majorTickStroke, this.tickOptions.majorTickLineWidth );
    },

    /**
     * Adds a minor tick mark.
     * @param {number} value
     * @param {Node} [label] optional
     * @public
     */
    addMinorTick: function( value, label ) {
      this.addTick( this.minorTicksParent, value, label,
        this.tickOptions.minorTickLength, this.tickOptions.minorTickStroke, this.tickOptions.minorTickLineWidth );
    },

    /*
     * Adds a tick mark above the track.
     * @param {Node} parent
     * @param {number} value
     * @param {Node} [label] optional
     * @param {number} length
     * @param {number} stroke
     * @param {number} lineWidth
     * @private
     */
    addTick: function( parent, value, label, length, stroke, lineWidth ) {
      var labelX = this.valueToPosition( value );

      // ticks
      var tick = new Path( new Shape()
          .moveTo( labelX, this.track.top )
          .lineTo( labelX, this.track.top - length ),
        { stroke: stroke, lineWidth: lineWidth } );
      parent.addChild( tick );

      // label
      if ( label ) {

        // For a vertical slider, rotate labels opposite the rotation of the slider, so that they appear as expected.
        if ( this.orientation === 'vertical' ) {
          label.rotation = -VERTICAL_ROTATION;
        }
        parent.addChild( label );
        label.centerX = tick.centerX;
        label.bottom = tick.top - this.tickOptions.tickLabelSpacing;
        label.pickable = false;
      }
    },

    // @public
    setEnabled: function( enabled ) { this.enabledProperty.value = enabled; },
    set enabled( value ) { this.setEnabled( value ); },

    // @public
    getEnabled: function() { return this.enabledProperty.value; },
    get enabled() { return this.getEnabled(); },

    // @public
    setEnabledRange: function( enabledRange ) { this.enabledRangeProperty.value = enabledRange; },
    set enabledRange( range ) { this.setEnabledRange( range ); },

    // @public
    getEnabledRange: function() { return this.enabledRangeProperty.value; },
    get enabledRange() { return this.getEnabledRange(); },

    // @public - Sets visibility of major ticks.
    setMajorTicksVisible: function( visible ) {
      this.majorTicksParent.visible = visible;
    },
    set majorTicksVisible( value ) { this.setMajorTicksVisible( value ); },

    // @public - Gets visibility of major ticks.
    getMajorTicksVisible: function() {
      return this.majorTicksParent.visible;
    },
    get majorTicksVisible() { return this.getMajorTicksVisible(); },

    // @public - Sets visibility of minor ticks.
    setMinorTicksVisible: function( visible ) {
      this.minorTicksParent.visible = visible;
    },
    set minorTicksVisible( value ) { this.setMinorTicksVisible( value ); },

    // @public - Gets visibility of minor ticks.
    getMinorTicksVisible: function() {
      return this.minorTicksParent.visible;
    },
    get minorTicksVisible() { return this.getMinorTicksVisible(); }
  } );

  // mix accessibility into Slider
  AccessibleSlider.mixInto( Slider );

  return Slider;
} );<|endoftext|>


<commit_before>//  Phaser.Sound

module.exports = {

    Dynamic: require('./dynamic'),

    BaseSoundManager: require('./BaseSoundManager'),
    WebAudioSoundManager: require('./WebAudioSoundManager'),

    BaseSound: require('./BaseSound')

};
<commit_msg>updated references in sound folder index.js<commit_after>//  Phaser.Sound

module.exports = {

    Dynamic: require('./dynamic'),

    SoundManagerCreator: require('./SoundManagerCreator'),

    BaseSound: require('./BaseSound'),
    BaseSoundManager: require('./BaseSoundManager'),

    WebAudioSound: require('./webaudio/WebAudioSound'),
    WebAudioSoundManager: require('./webaudio/WebAudioSoundManager')

};
<|endoftext|>


<commit_before>'use strict';

/**
 * This initializes the ElasticSearch index.
 *
 * @param {Object} state The state of which we are about to initialize.
 */

const es = require('../../lib/services/elasticsearch');
const config = require('../../../shared/config');

module.exports = (state) => {
  // Save the index in the context
  state.context.index = config.es.assetIndex; //TODO: replace all through with both indexes -- or remove use altogether seeing as index is from config
  console.log('Initializing the Elastic Search index: ' + state.context.index);

  return Promise.all([
    es.indices.exists({index: config.es.assetIndex}),
    es.indices.exists({index: config.es.seriesIndex}),
  ])
    .then(([{body: assetIndexExists}, {body: seriesIndexExists}]) => {
      const indicesToCreate = [];
      const assetMapping = getAssetMapping();
      if(!assetIndexExists) {
        indicesToCreate.push({
          index: config.es.assetIndex,
          mappings: assetMapping,
        });
      }

      if(!seriesIndexExists) {
        indicesToCreate.push({
          index: config.es.seriesIndex,
          mappings: getSeriesMapping(assetMapping),
        });
      }

      if(indicesToCreate.length < 1) {
        return state;
      }

      return Promise.all(indicesToCreate.map((def) => createIndex(def)))
        .then(() => {
          console.log('Indices created:', indicesToCreate);
          return state;
        });
    });

  //TODO: move createIndex and getAssetMapping top level

  function createIndex({index, mappings}) {
    return es.indices.create({
      index,
      body: {
        settings: {
          max_result_window: 100000 // So sitemaps can access all assets
        },
        mappings,
      },
    })
      .catch((error) => {
        console.error('Error while creating index', index);
        console.dir(mappings, {depth: 10});
        console.dir(error, {depth:10});
        throw error;
      });
  }

  function getAssetMapping() {
    let fields = config.types.asset.mapping || {};
    fields.id = {
      type: 'long',
      fields: {
        raw: {
          type: 'text',
          index: false,
        },
      },
    };

    fields.series_ids = {
      type: 'keyword',
    };

    const fieldDefinitions = [
      ...config.types.asset.fields,

      // Generated fields that are not indexed from cumulus
      {short: 'height_cm', type: 'float'},
      {short: 'width_cm', type: 'float'},
      {short: 'height_px', type: 'float'},
      {short: 'width_px', type: 'float'},
    ];

    // Get all fields that needs a raw value included in the index
    fieldDefinitions
      .filter((field) => field.includeRaw)
      .forEach((field) => {
        fields[field.short] = {
          type: 'text',
          fields: {
            raw: {
              type: 'text',
              index: false,
            },
          },
        };
      });

    // Derive mappings from the asset field types
    // First the fields with date types
    fieldDefinitions
      .filter((field) => field.type === 'date')
      .forEach((field) => {
        fields[field.short] = {
          type: 'object',
          properties: {
            timestamp: {type: 'date'},
          },
        };
      });

    // Enumurations should not have their displaystring tokenized
    fieldDefinitions
      .filter((field) => field.type === 'enum')
      .forEach((field) => {
        fields[field.short] = {
          type: 'object',
          properties: {
            displaystring: {
              type: 'text',
              index: false,
            }
          }
        };
      });

    // Other fields with type
    fieldDefinitions
      .filter(({includeRaw, type}) => type && !includeRaw && !['date','enum'].includes(type))
      .forEach(({short, type}) => fields[short] = {type});

    return {properties: fields};
  }

  function getSeriesMapping(assetMapping) {
    return {
      properties: {
        url: {
          type: 'keyword'
        },
        title: {
          type: 'text',
        },
        description: {type: 'text'},
        tags: {type: 'text'},
        assets: {type: 'keyword'},
        previewAssets: assetMapping,
        dateFrom: {
          type: 'object',
          properties: {
            timestamp: {
              type: 'date'
            }
          }
        },
        dateTo: {
          type: 'object',
          properties: {
            timestamp: {
              type: 'date'
            }
          }
        }
      }
    };
  }
};
<commit_msg>Raw fields and displaystrings and tags should all be keywords instead of text!<commit_after>'use strict';

/**
 * This initializes the ElasticSearch index.
 *
 * @param {Object} state The state of which we are about to initialize.
 */

const es = require('../../lib/services/elasticsearch');
const config = require('../../../shared/config');

module.exports = (state) => {
  // Save the index in the context
  state.context.index = config.es.assetIndex; //TODO: replace all through with both indexes -- or remove use altogether seeing as index is from config
  console.log('Initializing the Elastic Search index: ' + state.context.index);

  return Promise.all([
    es.indices.exists({index: config.es.assetIndex}),
    es.indices.exists({index: config.es.seriesIndex}),
  ])
    .then(([{body: assetIndexExists}, {body: seriesIndexExists}]) => {
      const indicesToCreate = [];
      const assetMapping = getAssetMapping();
      if(!assetIndexExists) {
        indicesToCreate.push({
          index: config.es.assetIndex,
          mappings: assetMapping,
        });
      }

      if(!seriesIndexExists) {
        indicesToCreate.push({
          index: config.es.seriesIndex,
          mappings: getSeriesMapping(assetMapping),
        });
      }

      if(indicesToCreate.length < 1) {
        return state;
      }

      return Promise.all(indicesToCreate.map((def) => createIndex(def)))
        .then(() => {
          console.log('Indices created:', indicesToCreate);
          return state;
        });
    });

  //TODO: move createIndex and getAssetMapping top level

  function createIndex({index, mappings}) {
    return es.indices.create({
      index,
      body: {
        settings: {
          max_result_window: 100000 // So sitemaps can access all assets
        },
        mappings,
      },
    })
      .catch((error) => {
        console.error('Error while creating index', index);
        console.dir(mappings, {depth: 10});
        console.dir(error, {depth:10});
        throw error;
      });
  }

  function getAssetMapping() {
    let fields = config.types.asset.mapping || {};
    fields.id = {
      type: 'long',
      fields: {
        raw: {
          type: 'text',
          index: false,
        },
      },
    };

    fields.series_ids = {
      type: 'keyword',
    };

    const fieldDefinitions = [
      ...config.types.asset.fields,

      // Generated fields that are not indexed from cumulus
      {short: 'height_cm', type: 'float'},
      {short: 'width_cm', type: 'float'},
      {short: 'height_px', type: 'float'},
      {short: 'width_px', type: 'float'},
    ];

    // Get all fields that needs a raw value included in the index
    fieldDefinitions
      .filter((field) => field.includeRaw)
      .forEach((field) => {
        fields[field.short] = {
          type: 'text',
          fields: {
            raw: {
              type: 'keyword',
              index: true,
            },
          },
        };
      });

    // Derive mappings from the asset field types
    // First the fields with date types
    fieldDefinitions
      .filter((field) => field.type === 'date')
      .forEach((field) => {
        fields[field.short] = {
          type: 'object',
          properties: {
            timestamp: {type: 'date'},
          },
        };
      });

    // Enumurations should not have their displaystring tokenized
    fieldDefinitions
      .filter((field) => field.type === 'enum')
      .forEach((field) => {
        fields[field.short] = {
          type: 'object',
          properties: {
            displaystring: {
              type: 'keyword',
              index: true,
            }
          }
        };
      });

    // Other fields with type
    fieldDefinitions
      .filter(({includeRaw, type}) => type && !includeRaw && !['date','enum'].includes(type))
      .forEach(({short, type}) => fields[short] = {type});

    return {properties: fields};
  }

  function getSeriesMapping(assetMapping) {
    return {
      properties: {
        url: {
          type: 'keyword'
        },
        title: {
          type: 'text',
        },
        description: {type: 'text'},
        tags: {type: 'keyword'},
        assets: {type: 'keyword'},
        previewAssets: assetMapping,
        dateFrom: {
          type: 'object',
          properties: {
            timestamp: {
              type: 'date'
            }
          }
        },
        dateTo: {
          type: 'object',
          properties: {
            timestamp: {
              type: 'date'
            }
          }
        }
      }
    };
  }
};
<|endoftext|>


<commit_before>'use strict';

const express = require('express');
const router = express.Router();

const HttpStatus = require('http-status-codes');
const DatabaseQuery = require('../models/database-query');

router.get('/:id', (req, res, next) => {

    const url = req.params.id;
    const databaseQuery = new DatabaseQuery();
    databaseQuery.insertUrl(url);

    res.status(HttpStatus.OK).send({  });
});

module.exports = router;<commit_msg>Use DatabaseQuery in shorten route. Partial implementation<commit_after>'use strict';

const express = require('express');
const router = express.Router();

const IdToKeyConverter = require('../utils/id-to-key-converter');

const HttpStatus = require('http-status-codes');
const DatabaseQuery = require('../models/database-query');

router.get('/:id', (req, res, next) => {

    const url = req.params.id;
    const databaseQuery = new DatabaseQuery();

    databaseQuery.insertOriginalUrl(url);
    databaseQuery
        .queryId(url)
        .then((id) => {
            const key = IdToKeyConverter.convertIdToKey(id);

            databaseQuery.updateKey(id, key);

            res.status(HttpStatus.OK).send({
                id: id,
                key: key
            });
        })
        .catch((err) => {
            res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ error_message: err });
        });
});

module.exports = router;<|endoftext|>