<commit_before>var express = require('express');
var router = express.Router();
var url = require('url');
var auth = require('basic-auth');
var textMessage = require('../text_message');

// putting 'controller' code here for now. will probably seperate at some point...
var db = require('../db');
var Phone = db.Phone;
var Prize = db.Prize;
var PhoneGroup = db.PhoneGroup;
var AccessLog = db.AccessLog;
var OutgoingMessage = db.OutgoingMessage;

/////////// PUBLIC API - no auth required
// read prizes
router.get('/prizes', function (req, res) {
  var query = url.parse(req.url, true).query;
  var limit = query.inactive == "1" ? {} : {active: true};
  Prize.find(limit).execAsync().then(function(prizes) {
    res.send(prizes);
  })
});

// update om status - outgoing message
router.post('/om', function (req, res) {
  var jsonReq =  JSON.stringify(req.body);
  var al = new AccessLog({data: jsonReq});
  al.saveAsync()
    .then(function() {
      return OutgoingMessage.findOne({uuid: req.body.ParentMessageUUID}).execAsync();
    }).then(function(om) {
      if (om) {
        om.messageStatus = req.body.Status;
        return om.saveAsync();
      }
    }).then(function(){
      res.send({ok: true});
    })
});

//////////// private api - auth required

router.all('*', function(req, res, next) {
  if (req.get('Host').match(/localhost\:\d+/)) {
    next();
  }
  else {
    var credentials = auth(req);
    if (!credentials || credentials.name !== process.env.BASIC_AUTH_USER || credentials.pass !== process.env.BASIC_AUTH_PASS) {
      res.statusCode = 401;
      res.setHeader('WWW-Authenticate', 'Basic realm="example"');
      res.send('Access denied');
    } else {
      next();
    }
  }
});

// test sms (all verbs...)
router.all('/testsms', function(req, res) {
  textMessage.send(process.env.TEST_NUMBER, 'this is a test - ');
  res.status(201).send({ok: true});
});

// create phone - incoming message
router.post('/im', function (req, res) {
  Phone.handleIncomingMessage(req.body).then(function(p) {
    if(p) {
      res.status(201).send({ok: true});
    }
  });
})

// list access log (al)
router.get('/al', function (req, res) {
  AccessLog.find().execAsync()
    .then(function (als) {
      res.send(als);
    })
});

// list outgoing messages (om)
router.get('/om', function (req, res) {
  OutgoingMessage.find().execAsync()
    .then(function (oms) {
      res.send(oms);
    })
});

// send message to keyword group
router.post('/keyword/:id/send', function (req, res) {
  PhoneGroup.findById(req.params.id).execAsync()
    .then(function(pg) {
      return pg.sendMessage(req.body.message);
    }).then(function() {
      res.send({ok: true});
    })
});

// add phone ids to phonegroup keyword
router.post('/keyword/:id/add_ids', function(req, res) {
  var phoneIdArray = req.body.phoneIds;
  return PhoneGroup.findById(req.params.id).execAsync()
    .then(function(pg) {
      return pg.addPhoneIdsToGroup(phoneIdArray);
    }).then(function() {
      return res.status(201).send({ok: true});
    });
});

// create phonegroup (keyword)
router.post('/keyword', function (req, res) {
  var kw = req.body.keyword;
  var sr = req.body.signupResponse
  var vals = sr ? {keyword: kw, signupResponse: sr} : {keyword: kw};
  var pg = new PhoneGroup(vals).saveAsync()
    .then(function() {
      res.status(201).send({ok: true});
    });
});

// read single phonegroup keyword
router.get('/keyword/:id', function (req, res) {
  PhoneGroup.findById(req.params.id).execAsync()
    .then(function(pg) {
      res.send(pg);
    });
});

// read list of phonegroup keywords
router.get('/keywords', function (req, res) {
  PhoneGroup.find().populate('phones').execAsync()
    .then(function(pgs) {
      res.send(pgs);
    })
})

// update phonegroup keyword
router.put('/keyword/:id', function (req, res) {
  PhoneGroup.findById(req.params.id).execAsync()
    .then(function(pg) {
      pg.keyword = req.body.keyword ? req.body.keyword : pg.keyword;
      pg.signupResponse = req.body.signupResponse ? req.body.signupResponse : pg.signupResponse;
      return pg.saveAsync();
    }).then(function() {
      res.send({ok: true});
    })
});

// increment prize availabilty (actuall dec numClaimed)
router.get('/prize/inc/:id', function (req, res) {
  return Prize.findByIdAndIncrementNumAvail(req.params.id)
    .then(function(p) {
      return res.status(200).send(p);
    });
});

// decrement prize availabilty (actuall increment numClaimed)
router.get('/prize/dec/:id', function (req, res) {
  return Prize.findByIdAndDecrementNumAvail(req.params.id)
    .then(function(p) {
      return res.status(200).send(p);
    });
});

// create prizes
router.post('/prizes', function (req, res) {
  Prize.createNewFromPost(req.body)
    .then(function() {
      res.status(201).send({ok: true});
    });
});

// update prizes
router.put('/prize/:id', function(req, res) {
  if (db.mongoose.Types.ObjectId.isValid(req.params.id)) {
    Prize.findById(req.params.id).execAsync()
      .then(function(prize) {
        if (prize) {
          prize.name = req.body.name ? req.body.name : prize.name;
          prize.numAvailable = req.body.numAvailable ? req.body.numAvailable : prize.numAvailable;
          prize.numClaimed = req.body.numClaimed ? req.body.numClaimed : prize.numClaimed;
          prize.imageUrl = req.body.imageUrl ? req.body.imageUrl : prize.imageUrl;
          prize.active = req.body.active ? req.body.active : prize.active;
          return prize.save();
        }
        else {
          res.status(404).send('not found');
        }
    }).then(function() {
      res.send({ok: true});
    });
  }
  else {
    res.status(404).send('not found');
  }
})

// delete prizes
router.delete('/prize/:id', function (req, res) {
  Prize.findOneAndRemove(req.params.id).execAsync().then(function(p) {
    p ? res.send({ok: true}) : res.status(404).send('not found');
  })
});

// read phone list
router.get('/phones', function (req, res) {
  Phone.find({}).execAsync().then(function(phones) {
    res.send(phones);
  })
});

// delete phone
router.delete('/phones/:id', function (req, res) {
  Phone.findOneAndRemove(req.params.id).execAsync().then(function(p) {
    p ? res.send({ok: true}) : res.status(404).send('not found');
  })
});

router.get('/', function (req, res) {
  res.send({app_name: 'ttapi', ver: 1});
});

module.exports = router;<commit_msg>debug<commit_after>var express = require('express');
var router = express.Router();
var url = require('url');
var auth = require('basic-auth');
var textMessage = require('../text_message');

// putting 'controller' code here for now. will probably seperate at some point...
var db = require('../db');
var Phone = db.Phone;
var Prize = db.Prize;
var PhoneGroup = db.PhoneGroup;
var AccessLog = db.AccessLog;
var OutgoingMessage = db.OutgoingMessage;

/////////// PUBLIC API - no auth required
// read prizes
router.get('/prizes', function (req, res) {
  var query = url.parse(req.url, true).query;
  var limit = query.inactive == "1" ? {} : {active: true};
  Prize.find(limit).execAsync().then(function(prizes) {
    res.send(prizes);
  })
});

// update om status - outgoing message
router.post('/om', function (req, res) {
  var jsonReq =  JSON.stringify(req.body);
  var al = new AccessLog({data: jsonReq});
  al.saveAsync()
    .then(function() {
      return OutgoingMessage.findOne({uuid: req.body.ParentMessageUUID}).execAsync();
    }).then(function(om) {
      if (om) {
        om.messageStatus = req.body.Status;
        return om.saveAsync();
      }
    }).then(function(){
      res.send({ok: true});
    })
});

//////////// private api - auth required

router.all('*', function(req, res, next) {
  if (req.get('Host').match(/localhost\:\d+/)) {
    next();
  }
  else {
    var credentials = auth(req);
    if (!credentials || credentials.name !== process.env.BASIC_AUTH_USER || credentials.pass !== process.env.BASIC_AUTH_PASS) {
      res.statusCode = 401;
      res.setHeader('WWW-Authenticate', 'Basic realm="example"');
      res.send('Access denied');
    } else {
      next();
    }
  }
});

// test sms (all verbs...)
router.all('/testsms', function(req, res) {
  textMessage.send(process.env.TEST_NUMBER, 'this is a test - ');
  res.status(201).send({ok: true});
});

// create phone - incoming message
router.post('/im', function (req, res) {
  Phone.handleIncomingMessage(req.body).then(function(p) {
    if(p) {
      res.status(201).send({ok: true});
    }
  });
})

// list access log (al)
router.get('/al', function (req, res) {
  AccessLog.find().execAsync()
    .then(function (als) {
      res.send(als);
    })
});

// list outgoing messages (om)
router.get('/om', function (req, res) {
  OutgoingMessage.find().execAsync()
    .then(function (oms) {
      res.send(oms);
    })
});

// send message to keyword group
router.post('/keyword/:id/send', function (req, res) {
  PhoneGroup.findById(req.params.id).execAsync()
    .then(function(pg) {
      return pg.sendMessage(req.body.message);
    }).then(function() {
      res.send({ok: true});
    })
});

// add phone ids to phonegroup keyword
router.post('/keyword/:id/add_ids', function(req, res) {
  console.log(req.body);
  var phoneIdArray = req.body.phoneIds;
  return PhoneGroup.findById(req.params.id).execAsync()
    .then(function(pg) {
      return pg.addPhoneIdsToGroup(phoneIdArray);
    }).then(function() {
      return res.status(201).send({ok: true});
    });
});

// create phonegroup (keyword)
router.post('/keyword', function (req, res) {
  var kw = req.body.keyword;
  var sr = req.body.signupResponse
  var vals = sr ? {keyword: kw, signupResponse: sr} : {keyword: kw};
  var pg = new PhoneGroup(vals).saveAsync()
    .then(function() {
      res.status(201).send({ok: true});
    });
});

// read single phonegroup keyword
router.get('/keyword/:id', function (req, res) {
  PhoneGroup.findById(req.params.id).execAsync()
    .then(function(pg) {
      res.send(pg);
    });
});

// read list of phonegroup keywords
router.get('/keywords', function (req, res) {
  PhoneGroup.find().populate('phones').execAsync()
    .then(function(pgs) {
      res.send(pgs);
    })
})

// update phonegroup keyword
router.put('/keyword/:id', function (req, res) {
  PhoneGroup.findById(req.params.id).execAsync()
    .then(function(pg) {
      pg.keyword = req.body.keyword ? req.body.keyword : pg.keyword;
      pg.signupResponse = req.body.signupResponse ? req.body.signupResponse : pg.signupResponse;
      return pg.saveAsync();
    }).then(function() {
      res.send({ok: true});
    })
});

// increment prize availabilty (actuall dec numClaimed)
router.get('/prize/inc/:id', function (req, res) {
  return Prize.findByIdAndIncrementNumAvail(req.params.id)
    .then(function(p) {
      return res.status(200).send(p);
    });
});

// decrement prize availabilty (actuall increment numClaimed)
router.get('/prize/dec/:id', function (req, res) {
  return Prize.findByIdAndDecrementNumAvail(req.params.id)
    .then(function(p) {
      return res.status(200).send(p);
    });
});

// create prizes
router.post('/prizes', function (req, res) {
  Prize.createNewFromPost(req.body)
    .then(function() {
      res.status(201).send({ok: true});
    });
});

// update prizes
router.put('/prize/:id', function(req, res) {
  if (db.mongoose.Types.ObjectId.isValid(req.params.id)) {
    Prize.findById(req.params.id).execAsync()
      .then(function(prize) {
        if (prize) {
          prize.name = req.body.name ? req.body.name : prize.name;
          prize.numAvailable = req.body.numAvailable ? req.body.numAvailable : prize.numAvailable;
          prize.numClaimed = req.body.numClaimed ? req.body.numClaimed : prize.numClaimed;
          prize.imageUrl = req.body.imageUrl ? req.body.imageUrl : prize.imageUrl;
          prize.active = req.body.active ? req.body.active : prize.active;
          return prize.save();
        }
        else {
          res.status(404).send('not found');
        }
    }).then(function() {
      res.send({ok: true});
    });
  }
  else {
    res.status(404).send('not found');
  }
})

// delete prizes
router.delete('/prize/:id', function (req, res) {
  Prize.findOneAndRemove(req.params.id).execAsync().then(function(p) {
    p ? res.send({ok: true}) : res.status(404).send('not found');
  })
});

// read phone list
router.get('/phones', function (req, res) {
  Phone.find({}).execAsync().then(function(phones) {
    res.send(phones);
  })
});

// delete phone
router.delete('/phones/:id', function (req, res) {
  Phone.findOneAndRemove(req.params.id).execAsync().then(function(p) {
    p ? res.send({ok: true}) : res.status(404).send('not found');
  })
});

router.get('/', function (req, res) {
  res.send({app_name: 'ttapi', ver: 1});
});

module.exports = router;<|endoftext|>


<commit_before>/*
 * DiscoJuice
 *  Work is based upon mock up made by the Kantara ULX group.
 *
 * Author: Andreas Åkre Solberg, UNINETT, andreas.solberg@uninett.no
 * Licence undecided. Awaiting alignment with the licence of the origin Kantara mockup.
 */
if (typeof DiscoJuice == "undefined") var DiscoJuice = {};

DiscoJuice.UI = {
	// Reference to the top level DiscoJuice object
	"parent" : DiscoJuice,

	// The current data model
	"control": null,

	// Reference to the
	"popup": null,

	// Entities / items
	"resulthtml": 'Loading data…',

	"show": function() {
		this.control.load();

		this.popup.fadeIn("slow");
		$("div#discojuice_overlay").show(); // fadeIn("fast");
		this.focusSearch();
	},

	"focusSearch": function() {
		$("input.discojuice_search").focus();
	},
	"hide": function() {
		$("div#discojuice_overlay").fadeOut("slow"); //fadeOut("fast");
		this.popup.fadeOut("slow");
	},

	"clearItems": function() {
		this.resulthtml = '';
	},

	// addItem(item, description, {country, flag}, keywordmatch, distance)
	// addItem(current, current.descr || null, countrydef, search, current.distance);
	"addItem": function(item, countrydef, search, distance) {
		var textLink = '';
		var classes = '';
		if (item.weight < -50) classes += 'hothit';

		var iconpath = this.parent.Utils.options.get('discoPath', '') + 'logos/';
		var flagpath = this.parent.Utils.options.get('discoPath', '') + 'flags/';
		var clear = false;

		var debugweight = this.parent.Utils.options.get('debug.weight', false);

		// Add icon element first
		if (item.icon) {
			textLink += '';
			clear = true;
		}

		// Add title
		textLink += '' + item.title + '';

		// Add matched search term
		if (search && search !== true) {
			textLink += '– ' + search + '';
		} else if (item.descr) {
			textLink += '– ' +  item.descr + '';
		}

		if (countrydef || (distance != undefined)) {

			textLink += '';
			if (countrydef) {
				textLink += '';
				if (countrydef.flag) textLink += ' ';
				textLink += countrydef.country + '';
			}

			if (distance != undefined) {
				if (distance < 1) {
					textLink += 'Nearby';
				} else {
					textLink += '' +  Math.round(distance) + ' km' + '';
				}

			}
			textLink += '';
		}

		if (debugweight) {
			textLink += '
';
			var w = 0;
			if (item.weight) {
				w += item.weight;
			}
			if (item.distanceweight) {
				w += item.distanceweight;
			}
			textLink += 'Weight ' + Math.round(100*w)/100 + ' ';

			if (item.weight) {
				textLink += ' (base ' + item.weight + ')   ';
			}
			if (item.distanceweight) {
				textLink += '(dist ' + Math.round(100*item.distanceweight)/100 + ')';
			}

			textLink += '
';
		}

		// Add a clear bar.
		if (clear) {
			textLink += '
';
		}

		console.log(item);

		// Wrap in A element
		textLink = '' +
			textLink + '';

		this.resulthtml += textLink;
	},

	"refreshData": function(showmore, show, listcount) {
		var that = this;

		this.parent.Utils.log('DiscoJuice.UI refreshData()');

		this.popup.find("div.scroller").empty().append(this.resulthtml);
		this.popup.find("div.scroller a").each(function() {
			var overthere = that;	// Overthere is a reference to the UI object
			$(this).click(function(event) {
				event.preventDefault();
				overthere.hide();
				var entityID = unescape($(this).attr('rel'));
				overthere.control.selectProvider(entityID);
			});
		});

		if (showmore) {
			var moreLink = 'Results limited to ' + show + ' entries – show more…';
			this.popup.find("p.discojuice_moreLinkContainer").empty().append(moreLink);
			this.popup.find("p.discojuice_moreLinkContainer a.discojuice_showmore").click(function(e) {
				event.preventDefault();
				that.control.increase();
			});
		} else {
			this.popup.find("p.discojuice_moreLinkContainer").empty();
			if (listcount > 10) {
				var moreLink = '' + listcount + ' entries listed';
				this.popup.find("p.discojuice_moreLinkContainer").append(moreLink);
			}
		}
	},

	"enable": function(control) {
		var imgpath = this.parent.Utils.options.get('discoPath', '') + 'images/';

		var html = 	'
' +
			'
' +
				' ' +
				'
' + this.parent.Utils.options.get('title', 'Title')  +  '
' +
				'
' + this.parent.Utils.options.get('subtitle', 'Subtitle') + '
' +
			'
' +

			'
' +
				'
' +
					'
 Loading list of providers...
' +
				'
' +
				'

' +
			'
' +

			'
' +
				'
<input type="search" class="discojuice_search" results=5 autosave="discojuice" name="searchfield" placeholder="or search for a provider, in example Univerity of Oslo" value="" />
' +
				'
' +
					'Help me, I cannot find my provider' +
//					'
If your institusion is not connected to Foodle, you may either select to login one of the commercial providers such as Facebook or Google, or you may create a new account using any of the Guest providers, such as Feide OpenIdP.
' +
					'
If your institusion is not connected to Foodle, you may create a new account using any of the Guest providers, such as OpenIdP (Guest users).
' +
				'
' +
			'
' +

			'
' +
				'
' +
					'
' +
						'' +
						'Locate me more accurately using HTML5 Geo-Location' +
					'
' +
					'

' +
				'
' +
				'

' +
			'
' +

			'
' +
				'
DiscoJuice © UNINETT
' +
			'
' +

		'
';
		var that = this;

		if (this.parent.Utils.options.get('overlay', true) === true) {
			var overlay = '

';
			$(overlay).appendTo($("body"));
		}

		this.popup = $(html).appendTo($("body"));

		if (this.parent.Utils.options.get('always', false) === true) {
			this.popup.find(".discojuice_close").hide();
			this.show();
		} else {
			// Add a listener to the sign in button.
			$(control).click(function(event) {
				event.preventDefault();
				that.show();
				return false;
			});
		}

		// Add listeners to the close button.
		this.popup.find(".discojuice_close").click(function() {
			that.hide();
		});

 		// Add toogle for what is this text.
		this.popup.find(".discojuice_what").click(function() {
			that.popup.find(".discojuice_whatisthis").toggleClass("show");
		});

		if (this.parent.Utils.options.get('location', false) && navigator.geolocation) {
			var that = this;
			$("#locateme").click(function(event) {
				var imgpath = that.parent.Utils.options.get('discoPath', '') + 'images/';
				event.preventDefault();
				$("div.locatemebefore").hide();
				$("div.locatemeafter").html('
 Getting your location...
');
				that.control.locateMe();
			});
		} else {
			$("dd#locatemediv").hide();
		}

	},

	"setLocationText": function(html) {
		return $("div.locatemeafter").html(html);
	},

	"addContent": function(html) {
		return $(html).appendTo($("body"));
	},
	"addFilter": function(html) {
		return $(html).prependTo(this.popup.find('.filters'));
//		this.popup.find('.filters').append(html).css('border', '1px solid red');
	}
};

<commit_msg>disable debugging<commit_after>/*
 * DiscoJuice
 *  Work is based upon mock up made by the Kantara ULX group.
 *
 * Author: Andreas Åkre Solberg, UNINETT, andreas.solberg@uninett.no
 * Licence undecided. Awaiting alignment with the licence of the origin Kantara mockup.
 */
if (typeof DiscoJuice == "undefined") var DiscoJuice = {};

DiscoJuice.UI = {
	// Reference to the top level DiscoJuice object
	"parent" : DiscoJuice,

	// The current data model
	"control": null,

	// Reference to the
	"popup": null,

	// Entities / items
	"resulthtml": 'Loading data…',

	"show": function() {
		this.control.load();

		this.popup.fadeIn("slow");
		$("div#discojuice_overlay").show(); // fadeIn("fast");
		this.focusSearch();
	},

	"focusSearch": function() {
		$("input.discojuice_search").focus();
	},
	"hide": function() {
		$("div#discojuice_overlay").fadeOut("slow"); //fadeOut("fast");
		this.popup.fadeOut("slow");
	},

	"clearItems": function() {
		this.resulthtml = '';
	},

	// addItem(item, description, {country, flag}, keywordmatch, distance)
	// addItem(current, current.descr || null, countrydef, search, current.distance);
	"addItem": function(item, countrydef, search, distance) {
		var textLink = '';
		var classes = '';
		if (item.weight < -50) classes += 'hothit';

		var iconpath = this.parent.Utils.options.get('discoPath', '') + 'logos/';
		var flagpath = this.parent.Utils.options.get('discoPath', '') + 'flags/';
		var clear = false;

		var debugweight = this.parent.Utils.options.get('debug.weight', false);

		// Add icon element first
		if (item.icon) {
			textLink += '';
			clear = true;
		}

		// Add title
		textLink += '' + item.title + '';

		// Add matched search term
		if (search && search !== true) {
			textLink += '– ' + search + '';
		} else if (item.descr) {
			textLink += '– ' +  item.descr + '';
		}

		if (countrydef || (distance != undefined)) {

			textLink += '';
			if (countrydef) {
				textLink += '';
				if (countrydef.flag) textLink += ' ';
				textLink += countrydef.country + '';
			}

			if (distance != undefined) {
				if (distance < 1) {
					textLink += 'Nearby';
				} else {
					textLink += '' +  Math.round(distance) + ' km' + '';
				}

			}
			textLink += '';
		}

		if (debugweight) {
			textLink += '
';
			var w = 0;
			if (item.weight) {
				w += item.weight;
			}
			if (item.distanceweight) {
				w += item.distanceweight;
			}
			textLink += 'Weight ' + Math.round(100*w)/100 + ' ';

			if (item.weight) {
				textLink += ' (base ' + item.weight + ')   ';
			}
			if (item.distanceweight) {
				textLink += '(dist ' + Math.round(100*item.distanceweight)/100 + ')';
			}

			textLink += '
';
		}

		// Add a clear bar.
		if (clear) {
			textLink += '
';
		}

		// Wrap in A element
		textLink = '' +
			textLink + '';

		this.resulthtml += textLink;
	},

	"refreshData": function(showmore, show, listcount) {
		var that = this;

		this.parent.Utils.log('DiscoJuice.UI refreshData()');

		this.popup.find("div.scroller").empty().append(this.resulthtml);
		this.popup.find("div.scroller a").each(function() {
			var overthere = that;	// Overthere is a reference to the UI object
			$(this).click(function(event) {
				event.preventDefault();
				overthere.hide();
				var entityID = unescape($(this).attr('rel'));
				overthere.control.selectProvider(entityID);
			});
		});

		if (showmore) {
			var moreLink = 'Results limited to ' + show + ' entries – show more…';
			this.popup.find("p.discojuice_moreLinkContainer").empty().append(moreLink);
			this.popup.find("p.discojuice_moreLinkContainer a.discojuice_showmore").click(function(e) {
				event.preventDefault();
				that.control.increase();
			});
		} else {
			this.popup.find("p.discojuice_moreLinkContainer").empty();
			if (listcount > 10) {
				var moreLink = '' + listcount + ' entries listed';
				this.popup.find("p.discojuice_moreLinkContainer").append(moreLink);
			}
		}
	},

	"enable": function(control) {
		var imgpath = this.parent.Utils.options.get('discoPath', '') + 'images/';

		var html = 	'
' +
			'
' +
				' ' +
				'
' + this.parent.Utils.options.get('title', 'Title')  +  '
' +
				'
' + this.parent.Utils.options.get('subtitle', 'Subtitle') + '
' +
			'
' +

			'
' +
				'
' +
					'
 Loading list of providers...
' +
				'
' +
				'

' +
			'
' +

			'
' +
				'
<input type="search" class="discojuice_search" results=5 autosave="discojuice" name="searchfield" placeholder="or search for a provider, in example Univerity of Oslo" value="" />
' +
				'
' +
					'Help me, I cannot find my provider' +
//					'
If your institusion is not connected to Foodle, you may either select to login one of the commercial providers such as Facebook or Google, or you may create a new account using any of the Guest providers, such as Feide OpenIdP.
' +
					'
If your institusion is not connected to Foodle, you may create a new account using any of the Guest providers, such as OpenIdP (Guest users).
' +
				'
' +
			'
' +

			'
' +
				'
' +
					'
' +
						'' +
						'Locate me more accurately using HTML5 Geo-Location' +
					'
' +
					'

' +
				'
' +
				'

' +
			'
' +

			'
' +
				'
DiscoJuice © UNINETT
' +
			'
' +

		'
';
		var that = this;

		if (this.parent.Utils.options.get('overlay', true) === true) {
			var overlay = '

';
			$(overlay).appendTo($("body"));
		}

		this.popup = $(html).appendTo($("body"));

		if (this.parent.Utils.options.get('always', false) === true) {
			this.popup.find(".discojuice_close").hide();
			this.show();
		} else {
			// Add a listener to the sign in button.
			$(control).click(function(event) {
				event.preventDefault();
				that.show();
				return false;
			});
		}

		// Add listeners to the close button.
		this.popup.find(".discojuice_close").click(function() {
			that.hide();
		});

 		// Add toogle for what is this text.
		this.popup.find(".discojuice_what").click(function() {
			that.popup.find(".discojuice_whatisthis").toggleClass("show");
		});

		if (this.parent.Utils.options.get('location', false) && navigator.geolocation) {
			var that = this;
			$("#locateme").click(function(event) {
				var imgpath = that.parent.Utils.options.get('discoPath', '') + 'images/';
				event.preventDefault();
				$("div.locatemebefore").hide();
				$("div.locatemeafter").html('
 Getting your location...
');
				that.control.locateMe();
			});
		} else {
			$("dd#locatemediv").hide();
		}

	},

	"setLocationText": function(html) {
		return $("div.locatemeafter").html(html);
	},

	"addContent": function(html) {
		return $(html).appendTo($("body"));
	},
	"addFilter": function(html) {
		return $(html).prependTo(this.popup.find('.filters'));
//		this.popup.find('.filters').append(html).css('border', '1px solid red');
	}
};

<|endoftext|>


<commit_before>// startsWith to check if a string starts with a particular character sequence
String.prototype.startsWith = function (str) {
	return this.match( "^" + str ) == str;
};

// endsWith to check if a string ends with a particular character sequence
String.prototype.endsWith = function (str) {
	return this.match( str + "$" ) == str;
};

function isWhitespaceOrEmpty( text ) {
	return !/[^\s]/.test( text );
}

// Sets the begindate and enddate filters and sets the globale variables
function setDateFilters(dateStart, dateEnd) {
	beginDate = dateStart;
	endDate = dateEnd;

	// Use the false flag to prevent onChange from firing
	// (hence setting the global variables above)
	dijit.byId("begindate").set("value", dateStart, false);
	dijit.byId("enddate").set("value", dateEnd, false);

	if (beginDate2 !== undefined) {
		toggleSecondDateFilter();
	}
}
<commit_msg>Fixes #136<commit_after>// startsWith to check if a string starts with a particular character sequence
String.prototype.startsWith = function (str) {
	return this.match( "^" + str ) == str;
};

// endsWith to check if a string ends with a particular character sequence
String.prototype.endsWith = function (str) {
	return this.match( str + "$" ) == str;
};

function isWhitespaceOrEmpty( text ) {
	return !/[^\s]/.test( text );
}

// Sets the begindate and enddate filters and sets the globale variables
function setDateFilters(dateStart, dateEnd) {
	beginDate = dateStart;
	endDate = dateEnd;

	// Use the false flag to prevent onChange from firing (hence setting the global variables above)
	// Also, we should temporarily set intermediateChanges to false, otherwise the onChange still fires.
	// See https://bugs.dojotoolkit.org/ticket/17361 for details.
	bDate = dijit.byId("begindate");
	bDate.set("intermediateChanges", false);
	bDate.set("value", dateStart, false);
	bDate.set("intermediateChanges", true);

	eDate = dijit.byId("enddate");
	eDate.set("intermediateChanges", false);
	eDate.set("value", dateEnd, false);
	eDate.set("intermediateChanges", true);

	if (beginDate2 !== undefined) {
		toggleSecondDateFilter();
	}
}
<|endoftext|>


<commit_before>/**
 * @file Provides a cohesive set of utility functions for performing aggregate operations on a collection of resources
 */

import { jsonStringifyEquality, jsonCloneObject } from "/Utilities/JSONUtilities.js"

/**
 * Attempt to store grouped resources if there's more than one resource and the type is defined as well as the order
 * TODO: Investigate Deep Cloning Issues from Object.assign
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
 * @param {Object[]} groupedResources
 * @param {Function} derivedAttrsCallback
 * @param {Object} type
 * @param {Object} order
 * @param {Object[]} storedResources
 */
const attemptToCreateAndStoreAggregateResource = (groupedResources, derivedAttrsCallback, type, order, storedResources) => {
  if(groupedResources.length > 0 && type && order) {
    const derivedAttrs = derivedAttrsCallback(groupedResources, type);
    const aggregateResource = {};
    Object.assign(aggregateResource, jsonCloneObject(type), jsonCloneObject(order), jsonCloneObject(derivedAttrs));
    storedResources.push(aggregateResource);
  }
}

/**
 * This function aggregates adjacent resources of the same type in a post sorted Array
 * based on the strategy provided which is comprised of two concepts: type and order.
 * @see {@link Strategies/AggregateAdjacentResources/PersonStrategy.js}
 * @param {Array<Object>} resources
 * @param {Object} strategy
 * @return {Array<Object>} - transformed collection of resources of which some or all are aggregate resources
 */
export function aggregateAdjacentResources(resources, strategy) {
  // Define function level scoped variables that will be used throughout the iteration process
  let previousResource, groupedResources = [], previousOrder, previousType;
  const transformedResources = [];

  // 1. Sort the resources using the strategy
  const sortedResources = resources.sort((resourceA, resourceB) => {
    const resourceAOrderAttrs = strategy.pluckOrderAttrs(resourceA);
    const resourceBOrderAttrs = strategy.pluckOrderAttrs(resourceB);
    return strategy.orderAttrsComparator(resourceAOrderAttrs, resourceBOrderAttrs);
  });

  // 2. Iterate over the sorted resources array like so
  sortedResources.forEach((resource) => {
    const type = strategy.pluckTypeAttrs(resource);
    const order = strategy.pluckOrderAttrs(resource);

    //2.a Resource is NOT one of the requiredTypes
    if (!strategy.isRequiredType(type)) {
      //2.a.i attempt to store grouped resources
      attemptToCreateAndStoreAggregateResource(groupedResources, strategy.derivedAttrsCallback, previousType, previousOrder, transformedResources);
      //2.a.ii Also store the one we just came across that isn't a required type
      transformedResources.push(jsonCloneObject(resource));
      //2.a.iii Reset the the grouped resources
      groupedResources = [];
    } else {
      // 2.b
      // If there was a previously visited valid resource
      // but the types are not equal then we need store
      // the current resource group and then reset it
      if (previousResource !== undefined && !jsonStringifyEquality(type, previousType)) {
        attemptToCreateAndStoreAggregateResource(groupedResources, strategy.derivedAttrsCallback, previousType, previousOrder, transformedResources);
        groupedResources = []; //reset grouped resources
      }

      // 2.c This is either the first or successive resource we are iterating over that's of a required type
      groupedResources.push(resource);
      previousResource = resource;
    }

    // 2.d Update the previousType and previousOrder based on what has changed with the previousResource
    previousType = previousResource ? strategy.pluckTypeAttrs(previousResource) : undefined;
    previousOrder = previousResource ? strategy.pluckOrderAttrs(previousResource) : undefined;
  });

  // 3. In case we ended without seeing a different type or non-valid type lets store what's left
  attemptToCreateAndStoreAggregateResource(groupedResources, strategy.derivedAttrsCallback, previousType, previousOrder, transformedResources);
  return transformedResources;
}<commit_msg>Updated conditionals to use meaningful boolean variables<commit_after>/**
 * @file Provides a cohesive set of utility functions for performing aggregate operations on a collection of resources
 */

import { jsonStringifyEquality, jsonCloneObject } from "/Utilities/JSONUtilities.js"

/**
 * Attempt to store grouped resources if there's more than one resource and the type is defined as well as the order
 * TODO: Investigate Deep Cloning Issues from Object.assign
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
 * @param {Object[]} groupedResources
 * @param {Function} derivedAttrsCallback
 * @param {Object} type
 * @param {Object} order
 * @param {Object[]} storedResources
 */
const attemptToCreateAndStoreAggregateResource = (groupedResources, derivedAttrsCallback, type, order, storedResources) => {
  const shouldCreateAndStoreResource = groupedResources.length > 0 && type && order;
  if(shouldCreateAndStoreResource) {
    const derivedAttrs = derivedAttrsCallback(groupedResources, type);
    const aggregateResource = {};
    Object.assign(aggregateResource, jsonCloneObject(type), jsonCloneObject(order), jsonCloneObject(derivedAttrs));
    storedResources.push(aggregateResource);
  }
}

/**
 * This function aggregates adjacent resources of the same type in a post sorted Array
 * based on the strategy provided which is comprised of two concepts: type and order.
 * @see {@link Strategies/AggregateAdjacentResources/PersonStrategy.js}
 * @param {Array<Object>} resources
 * @param {Object} strategy
 * @return {Array<Object>} - transformed collection of resources of which some or all are aggregate resources
 */
export function aggregateAdjacentResources(resources, strategy) {
  // Define function level scoped variables that will be used throughout the iteration process
  let previousResource, groupedResources = [], previousOrder, previousType;
  const transformedResources = [];

  // 1. Sort the resources using the strategy
  const sortedResources = resources.sort((resourceA, resourceB) => {
    const resourceAOrderAttrs = strategy.pluckOrderAttrs(resourceA);
    const resourceBOrderAttrs = strategy.pluckOrderAttrs(resourceB);
    return strategy.orderAttrsComparator(resourceAOrderAttrs, resourceBOrderAttrs);
  });

  // 2. Iterate over the sorted resources array like so
  sortedResources.forEach((resource) => {
    const type = strategy.pluckTypeAttrs(resource);
    const order = strategy.pluckOrderAttrs(resource);

    //2.a Resource is NOT one of the requiredTypes
    const resourceIsNotOfARequiredType = !strategy.isRequiredType(type);
    if (resourceIsNotOfARequiredType) {
      //2.a.i attempt to store grouped resources
      attemptToCreateAndStoreAggregateResource(groupedResources, strategy.derivedAttrsCallback, previousType, previousOrder, transformedResources);
      //2.a.ii Also store the one we just came across that isn't of a required type
      transformedResources.push(jsonCloneObject(resource));
      //2.a.iii Reset the the grouped resources
      groupedResources = [];
    } else {
      // 2.b
      // If there was a previously visited valid resource
      // but the types are not equal then we need store
      // the current resource group and then reset it
      const currentResourceTypeDiffersFromPrevious = previousResource !== undefined && !jsonStringifyEquality(type, previousType);
      if (currentResourceTypeDiffersFromPrevious) {
        attemptToCreateAndStoreAggregateResource(groupedResources, strategy.derivedAttrsCallback, previousType, previousOrder, transformedResources);
        groupedResources = []; //reset grouped resources
      }

      // 2.c This is either the first or successive resource we are iterating over that's of the same required type
      groupedResources.push(resource);
      previousResource = resource;
    }

    // 2.d Update the previousType and previousOrder based on what has changed with the previousResource
    previousType = previousResource ? strategy.pluckTypeAttrs(previousResource) : undefined;
    previousOrder = previousResource ? strategy.pluckOrderAttrs(previousResource) : undefined;
  });

  // 3. In case we ended without seeing a different type or non-valid type lets store what's left
  attemptToCreateAndStoreAggregateResource(groupedResources, strategy.derivedAttrsCallback, previousType, previousOrder, transformedResources);
  return transformedResources;
}<|endoftext|>


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

/*
 * This file implements the /api/csv route. This route accepts csv data for
 * meter and readings data.
 */

const crypto = require('crypto');
const express = require('express');
const router = express.Router();
const failure = require('../services/csvPipeline/failure');
const { getConnection } = require('../db');
const { log } = require('../log');
const { verifyCredentials } = require('./authenticator');
const fs = require('fs').promises;
const { mkdirSync } = require('fs');
const multer = require('multer');
const saveCsv = require('../services/csvPipeline/saveCsv');
const uploadMeters = require('../services/csvPipeline/uploadMeters');
const uploadReadings = require('../services/csvPipeline/uploadReadings');
const zlib = require('zlib');

/** Middleware validation */
const { validateMetersCsvUploadParams, validateReadingsCsvUploadParams } = require('../services/csvPipeline/validateCsvUploadParams');
const { CSVPipelineError } = require('../services/csvPipeline/CustomErrors');
const { isTokenAuthorized, isUserAuthorized } = require('../util/userRoles');

const { CSV: csvRole } = require('../models/User').role;

// Set router to use multer
router.use(function (req, res, next) {
	// Config object so that multer stores the uploaded file to disk rather than to memory.
	const storage = multer.diskStorage({
		destination: function () {
			const path = `${__dirname}/../tmp/uploads/csvPipeline`;
			mkdirSync(path, { recursive: true }); // creates directory if not exists
			return path;
		}(),
		filename: function (r, file, cb) {
			// Save file with original name and date time to prevent accidental overwrites.
			const modifiedFilename = `${(new Date(Date.now()).toISOString())}-${crypto.randomBytes(2).toString('hex')}-${file.originalname}`;
			cb(null, modifiedFilename);
		}
	})

	// Multer Config
	let upload = multer({
		storage: storage,
		// We will use this filter to handle user authentication. If the user is doing a curl request
		// we need the supplied password param to precede the file when uploading so that
		// multer will have stored 'password' in req.body by the time this filter is called on a file.
		// If the user is uploading via the webapp, we expect the request to have a token.
		fileFilter: async function (request, file, cb) {
			// Multer only executes the file filter when a file is being processed. If no file is
			// uploaded, then the authentication checks below are not called. This is okay, because
			// if no file is uploaded the subsequent middleware will end the request.
			try {
				const token = request.headers.token || request.body.token || request.query.token;
				if (token) {
					// If a token is found, then we will check authentication and validation via the token.
					(await isTokenAuthorized(token, csvRole)) ? cb(null, true) : cb(new Error('Invalid token'));
				} else {
					// If no token is found, then the request is mostly like a curl request. We require an
					// email and password to be supplied for curl requests.
					const { email, password } = request.body;
					const verifiedUser = await verifyCredentials(email, password, true);
					if (verifiedUser) {
						isUserAuthorized(verifiedUser, csvRole) ? cb(null, true) : cb(new Error('Invalid credentials'));
					} else {
						cb(new Error('Invalid credentials'));
					}
				}
			} catch (error) {
				cb(error);
			}
		}
	})

	// Set multer to look for a file in the csvfile field.
	upload = upload.single('csvfile');

	upload(req, res, function (err) {
		if (err) {
			failure(req, res, err);
			return;
		}
		next();
	})
});

// This middleware ensures that one CSV file has been uploaded. If no file is uploaded, the request ends. Otherwise, it proceeds.
// We need this extra middleware because multer does not provide an option to guard against the case where no file is uploaded.
router.use(function (req, res, next) {
	if (!req.file) {
		failure(req, res, new CSVPipelineError('No csv file was uploaded. A csv file must be submitted via the csvfile parameter.'));
	} else {
		next();
	}
});

router.post('/meters', validateMetersCsvUploadParams, async (req, res) => {
	const uploadedFilepath = req.file.path;
	let csvFilepath;
	try {
		let fileBuffer = await fs.readFile(uploadedFilepath);
		// Unzip uploaded file and save file to disk if the user
		// has indicated that the file is (g)zipped.
		if (req.body.gzip === 'true') {
			fileBuffer = zlib.gunzipSync(fileBuffer);
			// We expect this directory to have been created by this stage of the pipeline.
			const dir = `${__dirname}/../tmp/uploads/csvPipeline`;
			csvFilepath = await saveCsv(fileBuffer, 'meters', dir);
			log.info(`The file ${csvFilepath} was created to upload meters csv data`);
		} else {
			csvFilepath = uploadedFilepath;
		}

		const conn = getConnection();
		await uploadMeters(req, res, csvFilepath, conn);
	} catch (error) {
		failure(req, res, error);
	}

	// Clean up files
	fs.unlink(uploadedFilepath) // Delete the uploaded file.
		.then(() => log.info(`Successfully deleted the uploaded file ${uploadedFilepath}.`))
		.catch(err => {
			log.error(`Failed to remove the file ${uploadedFilepath}.`, err);
		});

	if (csvFilepath) {
		// Delete the unzipped csv file if it exists.
		fs.unlink(csvFilepath)
			.then(() => log.info(`Successfully deleted the csv file ${csvFilepath}.`))
			.catch(err => {
				log.error(`Failed to remove the file ${csvFilepath}.`, err);
			});
	}
});

router.post('/readings', validateReadingsCsvUploadParams, async (req, res) => {
	const uploadedFilepath = req.file.path;
	let csvFilepath;
	try {
		let fileBuffer = await fs.readFile(uploadedFilepath);
		// Unzip uploaded file and save file to disk if the user
		// has indicated that the file is (g)zipped.
		if (req.body.gzip === 'true') {
			fileBuffer = zlib.gunzipSync(fileBuffer);
			// We expect this directory to have been created by this stage of the pipeline.
			const dir = `${__dirname}/../tmp/uploads/csvPipeline`;
			csvFilepath = await saveCsv(fileBuffer, 'meters', dir);
			log.info(`The file ${csvFilepath} was created to upload meters csv data`);
		} else {
			csvFilepath = uploadedFilepath;
		}

		log.info(`The file ${csvFilepath} was created to upload readings csv data`);
		const conn = getConnection();
		await uploadReadings(req, res, csvFilepath, conn);
		if (req.body.refreshReadings) {
			// Call the refreshReadingViews script
			require('../services/refreshReadingViews');
		}
	} catch (error) {
		failure(req, res, error);
	}

	// Clean up files
	fs.unlink(uploadedFilepath) // Delete the uploaded file.
		.then(() => log.info(`Successfully deleted the uploaded file ${uploadedFilepath}.`))
		.catch(err => {
			log.error(`Failed to remove the file ${uploadedFilepath}.`, err);
		});

	if (csvFilepath) {
		// Delete the unzipped csv file if it exists.
		fs.unlink(csvFilepath)
			.then(() => log.info(`Successfully deleted the csv file ${csvFilepath}.`))
			.catch(err => {
				log.error(`Failed to remove the file ${csvFilepath}.`, err);
			});
	}
});

module.exports = router;<commit_msg>Fix redudant file removal error<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * This file implements the /api/csv route. This route accepts csv data for
 * meter and readings data.
 */

const crypto = require('crypto');
const express = require('express');
const router = express.Router();
const failure = require('../services/csvPipeline/failure');
const { getConnection } = require('../db');
const { log } = require('../log');
const { verifyCredentials } = require('./authenticator');
const fs = require('fs').promises;
const { mkdirSync } = require('fs');
const multer = require('multer');
const saveCsv = require('../services/csvPipeline/saveCsv');
const uploadMeters = require('../services/csvPipeline/uploadMeters');
const uploadReadings = require('../services/csvPipeline/uploadReadings');
const zlib = require('zlib');

/** Middleware validation */
const { validateMetersCsvUploadParams, validateReadingsCsvUploadParams } = require('../services/csvPipeline/validateCsvUploadParams');
const { CSVPipelineError } = require('../services/csvPipeline/CustomErrors');
const { isTokenAuthorized, isUserAuthorized } = require('../util/userRoles');

const { CSV: csvRole } = require('../models/User').role;

// Set router to use multer
router.use(function (req, res, next) {
	// Config object so that multer stores the uploaded file to disk rather than to memory.
	const storage = multer.diskStorage({
		destination: function () {
			const path = `${__dirname}/../tmp/uploads/csvPipeline`;
			mkdirSync(path, { recursive: true }); // creates directory if not exists
			return path;
		}(),
		filename: function (r, file, cb) {
			// Save file with original name and date time to prevent accidental overwrites.
			const modifiedFilename = `${(new Date(Date.now()).toISOString())}-${crypto.randomBytes(2).toString('hex')}-${file.originalname}`;
			cb(null, modifiedFilename);
		}
	})

	// Multer Config
	let upload = multer({
		storage: storage,
		// We will use this filter to handle user authentication. If the user is doing a curl request
		// we need the supplied password param to precede the file when uploading so that
		// multer will have stored 'password' in req.body by the time this filter is called on a file.
		// If the user is uploading via the webapp, we expect the request to have a token.
		fileFilter: async function (request, file, cb) {
			// Multer only executes the file filter when a file is being processed. If no file is
			// uploaded, then the authentication checks below are not called. This is okay, because
			// if no file is uploaded the subsequent middleware will end the request.
			try {
				const token = request.headers.token || request.body.token || request.query.token;
				if (token) {
					// If a token is found, then we will check authentication and validation via the token.
					(await isTokenAuthorized(token, csvRole)) ? cb(null, true) : cb(new Error('Invalid token'));
				} else {
					// If no token is found, then the request is mostly like a curl request. We require an
					// email and password to be supplied for curl requests.
					const { email, password } = request.body;
					const verifiedUser = await verifyCredentials(email, password, true);
					if (verifiedUser) {
						isUserAuthorized(verifiedUser, csvRole) ? cb(null, true) : cb(new Error('Invalid credentials'));
					} else {
						cb(new Error('Invalid credentials'));
					}
				}
			} catch (error) {
				cb(error);
			}
		}
	})

	// Set multer to look for a file in the csvfile field.
	upload = upload.single('csvfile');

	upload(req, res, function (err) {
		if (err) {
			failure(req, res, err);
			return;
		}
		next();
	})
});

// This middleware ensures that one CSV file has been uploaded. If no file is uploaded, the request ends. Otherwise, it proceeds.
// We need this extra middleware because multer does not provide an option to guard against the case where no file is uploaded.
router.use(function (req, res, next) {
	if (!req.file) {
		failure(req, res, new CSVPipelineError('No csv file was uploaded. A csv file must be submitted via the csvfile parameter.'));
	} else {
		next();
	}
});

router.post('/meters', validateMetersCsvUploadParams, async (req, res) => {
	const isGzip = req.body.gzip === 'true';
	const uploadedFilepath = req.file.path;
	let csvFilepath;
	try {
		let fileBuffer = await fs.readFile(uploadedFilepath);
		// Unzip uploaded file and save file to disk if the user
		// has indicated that the file is (g)zipped.
		if (isGzip) {
			fileBuffer = zlib.gunzipSync(fileBuffer);
			// We expect this directory to have been created by this stage of the pipeline.
			const dir = `${__dirname}/../tmp/uploads/csvPipeline`;
			csvFilepath = await saveCsv(fileBuffer, 'meters', dir);
			log.info(`The file ${csvFilepath} was created to upload meters csv data`);
		} else {
			csvFilepath = uploadedFilepath;
		}

		const conn = getConnection();
		await uploadMeters(req, res, csvFilepath, conn);
	} catch (error) {
		failure(req, res, error);
	}

	// Clean up files
	fs.unlink(uploadedFilepath) // Delete the uploaded file.
		.then(() => log.info(`Successfully deleted the uploaded file ${uploadedFilepath}.`))
		.catch(err => {
			log.error(`Failed to remove the file ${uploadedFilepath}.`, err);
		});

	// If user has indicated that the file is (g)zipped, then we also have to remove the unzipped file.
	if (isGzip) {
		// Delete the unzipped csv file if it exists.
		fs.unlink(csvFilepath)
			.then(() => log.info(`Successfully deleted the unzipped csv file ${csvFilepath}.`))
			.catch(err => {
				log.error(`Failed to remove the file ${csvFilepath}.`, err);
			});
	}
});

router.post('/readings', validateReadingsCsvUploadParams, async (req, res) => {
	const isGzip = req.body.gzip === 'true';
	const uploadedFilepath = req.file.path;
	let csvFilepath;
	try {
		let fileBuffer = await fs.readFile(uploadedFilepath);
		// Unzip uploaded file and save file to disk if the user
		// has indicated that the file is (g)zipped.
		if (isGzip) {
			fileBuffer = zlib.gunzipSync(fileBuffer);
			// We expect this directory to have been created by this stage of the pipeline.
			const dir = `${__dirname}/../tmp/uploads/csvPipeline`;
			csvFilepath = await saveCsv(fileBuffer, 'meters', dir);
			log.info(`The file ${csvFilepath} was created to upload meters csv data`);
		} else {
			csvFilepath = uploadedFilepath;
		}

		log.info(`The file ${csvFilepath} was created to upload readings csv data`);
		const conn = getConnection();
		await uploadReadings(req, res, csvFilepath, conn);
		if (req.body.refreshReadings) {
			// Call the refreshReadingViews script
			require('../services/refreshReadingViews');
		}
	} catch (error) {
		failure(req, res, error);
	}

	// Clean up files
	fs.unlink(uploadedFilepath) // Delete the uploaded file.
		.then(() => log.info(`Successfully deleted the uploaded file ${uploadedFilepath}.`))
		.catch(err => {
			log.error(`Failed to remove the file ${uploadedFilepath}.`, err);
		});

	// If user has indicated that the file is (g)zipped, then we also have to remove the unzipped file.
	if (isGzip) {
		// Delete the unzipped csv file if it exists.
		fs.unlink(csvFilepath)
			.then(() => log.info(`Successfully deleted the unzipped csv file ${csvFilepath}.`))
			.catch(err => {
				log.error(`Failed to remove the file ${csvFilepath}.`, err);
			});
	}
});

module.exports = router;<|endoftext|>


<commit_before>/*
 * DiscoJuice
 *  Work is based upon mock up made by the Kantara ULX group.
 *
 * Author: Andreas Åkre Solberg, UNINETT, andreas.solberg@uninett.no
 * Licence undecided. Awaiting alignment with the licence of the origin Kantara mockup.
 */
if (typeof DiscoJuice == "undefined") var DiscoJuice = {};

DiscoJuice.UI = {
	// Reference to the top level DiscoJuice object
	"parent" : DiscoJuice,

	// The current data model
	"control": null,

	// Reference to the
	"popup": null,

	// Entities / items
	"resulthtml": 'Loading data…',

	"show": function() {
		this.control.load();

		this.popup.fadeIn("slow");
		$("div#discojuice_overlay").show(); // fadeIn("fast");
		this.focusSearch();
	},

	"focusSearch": function() {
		$("input.discojuice_search").focus();
	},

	"hide": function() {
		$("div#discojuice_overlay").fadeOut("slow"); //fadeOut("fast");
		this.popup.fadeOut("slow");
	},

	"clearItems": function() {
		this.resulthtml = '';
	},

	// addItem(item, description, {country, flag}, keywordmatch, distance)
	// addItem(current, current.descr || null, countrydef, search, current.distance);
	"addItem": function(item, countrydef, search, distance, quickentry) {
		var textLink = '';
		var classes = '';
//		if (item.weight < -50) classes += 'hothit';

		var iconpath = this.parent.Utils.options.get('discoPath', '') + 'logos/';
		var flagpath = this.parent.Utils.options.get('discoPath', '') + 'flags/';
		var clear = false;

		var debugweight = this.parent.Utils.options.get('debug.weight', false);

		// Add icon element first
		if (item.icon) {
			textLink += '';
			clear = true;
		}

		if (quickentry) {
//			textLink += '↵';
			textLink += '↵';
		}

		// Add title
		textLink += '' + item.title + '';

		// Add matched search term
		if (search && search !== true) {
			textLink += '– ' + search + '';
		} else if (item.descr) {
			textLink += '– ' +  item.descr + '';
		}

		if (countrydef || (distance != undefined)) {

			textLink += '';
			if (countrydef) {
				textLink += '';
				if (countrydef.flag) textLink += ' ';
				textLink += countrydef.country + '';
			}

			if (distance != undefined) {
				if (distance < 1) {
					textLink += 'Nearby';
				} else {
					textLink += '' +  Math.round(distance) + ' km' + '';
				}

			}
			textLink += '';
		}

		if (debugweight) {
			textLink += '
';

			if (item.subID) {
				textLink += '<input value="' + item.subID + '" />';
			}

			var w = 0;
			if (item.weight) {
				w += item.weight;
			}
			if (item.distanceweight) {
				w += item.distanceweight;
			}
			textLink += 'Weight ' + Math.round(100*w)/100 + ' ';

			if (item.weight) {
				textLink += ' (base ' + item.weight + ')   ';
			}
			if (item.distanceweight) {
				textLink += '(dist ' + Math.round(100*item.distanceweight)/100 + ')';
			}

			textLink += '
';
		}

		// Add a clear bar.
		if (clear) {
			textLink += '
';
		}

		var relID = item.entityID;
		if (item.subID) {
			relID += '#' + item.subID;
		}

		// Wrap in A element
		textLink = '' +
			textLink + '';

		this.resulthtml += textLink;
	},

	"refreshData": function(showmore, show, listcount) {
		var that = this;

		this.parent.Utils.log('DiscoJuice.UI refreshData()');

		this.popup.find("div.scroller").empty().append(this.resulthtml);
		this.popup.find("div.scroller a").each(function() {
			var overthere = that;	// Overthere is a reference to the UI object
			$(this).click(function(event) {
				event.preventDefault();
				overthere.hide();

				// The "rel" attribute is containing: 'entityid#subid'
				// THe following code, decodes that.
				var relID = unescape($(this).attr('rel'));
				var entityID = relID;
				var subID = undefined;
				if (relID.match(/^.*#.+?$/)) {
					var matched = /^(.*)#(.+?)$/.exec(relID);
					entityID = matched[1];
					subID = matched[2];
				}
				overthere.control.selectProvider(entityID, subID);
			});
		});

		if (showmore) {
			var moreLink = 'Results limited to ' + show + ' entries – show more…';
			this.popup.find("p.discojuice_moreLinkContainer").empty().append(moreLink);
			this.popup.find("p.discojuice_moreLinkContainer a.discojuice_showmore").click(function(event) {
				event.preventDefault();
				that.control.increase();
			});
		} else {
			this.popup.find("p.discojuice_moreLinkContainer").empty();
			if (listcount > 10) {
				var moreLink = '' + listcount + ' entries listed';
				this.popup.find("p.discojuice_moreLinkContainer").append(moreLink);
			}
		}
	},

	"enable": function(control) {
		var imgpath = this.parent.Utils.options.get('discoPath', '') + 'images/';

		var textSearch = this.parent.Utils.options.get('textSearch', 'or search for a provider, in example Univerity of Oslo');
		var textHelp = this.parent.Utils.options.get('textHelp', 'Help me, I cannot find my provider');
		var textHelpMore = this.parent.Utils.options.get('textHelpMore', 'If your institusion is not connected to Foodle, you may create a new account using any of the Guest providers, such as OpenIdP (Guest users).');

		var html = 	'
' +
			'
' +
				' ' +
				'
' + this.parent.Utils.options.get('title', 'Title')  +  '
' +
				'
' + this.parent.Utils.options.get('subtitle', 'Subtitle') + '
' +
			'
' +

			'
' +
				'
' +
					'
 Loading list of providers...
' +
				'
' +
				'

' +
			'
' +

			'
' +
				'
<input type="search" class="discojuice_search" results=5 autosave="discojuice" name="searchfield" placeholder="' + textSearch + '" value="" />
' +
				'
' +
					'' + textHelp + '' +
					'
' + textHelpMore + '
' +
				'
' +
			'
' +

			'
' +
				'
' +
					'
' +
						'' +
						'Locate me more accurately using HTML5 Geo-Location' +
					'
' +
					'

' +
				'
' +
				'

' +
			'
' +

			'
' +
				'<button id="discojuiceextesion_listener" />' +
			'
' +

			'
' +
				'
DiscoJuice © UNINETT
' +
			'
' +

		'
';
		var that = this;

		if (this.parent.Utils.options.get('overlay', true) === true) {
			var overlay = '

';
			$(overlay).appendTo($("body"));
		}

		this.popup = $(html).appendTo($("body"));

		if (this.parent.Utils.options.get('always', false) === true) {
			this.popup.find(".discojuice_close").hide();
			this.show();
		} else {
			// Add a listener to the sign in button.
			$(control).click(function(event) {
				event.preventDefault();
				that.show();
				return false;
			});
		}

		this.popup.find("#discojuiceextesion_listener").click(function() {
			that.control.discojuiceextension();
		});

		// Add listeners to the close button.
		this.popup.find(".discojuice_close").click(function() {
			that.hide();
		});

 		// Add toogle for what is this text.
		this.popup.find(".discojuice_what").click(function() {
			that.popup.find(".discojuice_whatisthis").toggleClass("show");
		});

		if (this.parent.Utils.options.get('location', false) && navigator.geolocation) {
			var that = this;
			$("a#locateme").click(function(event) {
				that.parent.Utils.log('Locate me. Detected click event.');
				var imgpath = that.parent.Utils.options.get('discoPath', '') + 'images/';
				event.preventDefault();
 				event.stopPropagation();
				$("div.locatemebefore").hide();
				$("div.locatemeafter").html('
 Getting your location...
');
				that.control.locateMe();
			});
		} else {
			$("dd#locatemediv").hide();
		}

	},

	"setLocationText": function(html) {
		return $("div.locatemeafter").html(html);
	},

	"addContent": function(html) {
		return $(html).appendTo($("body"));
	},
	"addFilter": function(html) {
		return $(html).prependTo(this.popup.find('.filters'));
//		this.popup.find('.filters').append(html).css('border', '1px solid red');
	}
};

<commit_msg>Fix text for geo location. Add subtitle optional<commit_after>/*
 * DiscoJuice
 *  Work is based upon mock up made by the Kantara ULX group.
 *
 * Author: Andreas Åkre Solberg, UNINETT, andreas.solberg@uninett.no
 * Licence undecided. Awaiting alignment with the licence of the origin Kantara mockup.
 */
if (typeof DiscoJuice == "undefined") var DiscoJuice = {};

DiscoJuice.UI = {
	// Reference to the top level DiscoJuice object
	"parent" : DiscoJuice,

	// The current data model
	"control": null,

	// Reference to the
	"popup": null,

	// Entities / items
	"resulthtml": 'Loading data…',

	"show": function() {
		this.control.load();

		this.popup.fadeIn("slow");
		$("div#discojuice_overlay").show(); // fadeIn("fast");
		this.focusSearch();
	},

	"focusSearch": function() {
		$("input.discojuice_search").focus();
	},

	"hide": function() {
		$("div#discojuice_overlay").fadeOut("slow"); //fadeOut("fast");
		this.popup.fadeOut("slow");
	},

	"clearItems": function() {
		this.resulthtml = '';
	},

	// addItem(item, description, {country, flag}, keywordmatch, distance)
	// addItem(current, current.descr || null, countrydef, search, current.distance);
	"addItem": function(item, countrydef, search, distance, quickentry) {
		var textLink = '';
		var classes = '';
//		if (item.weight < -50) classes += 'hothit';

		var iconpath = this.parent.Utils.options.get('discoPath', '') + 'logos/';
		var flagpath = this.parent.Utils.options.get('discoPath', '') + 'flags/';
		var clear = false;

		var debugweight = this.parent.Utils.options.get('debug.weight', false);

		// Add icon element first
		if (item.icon) {
			textLink += '';
			clear = true;
		}

		if (quickentry) {
//			textLink += '↵';
			textLink += '↵';
		}

		// Add title
		textLink += '' + item.title + '';

		// Add matched search term
		if (search && search !== true) {
			textLink += '– ' + search + '';
		} else if (item.descr) {
			textLink += '– ' +  item.descr + '';
		}

		if (countrydef || (distance != undefined)) {

			textLink += '';
			if (countrydef) {
				textLink += '';
				if (countrydef.flag) textLink += ' ';
				textLink += countrydef.country + '';
			}

			if (distance != undefined) {
				if (distance < 1) {
					textLink += 'Nearby';
				} else {
					textLink += '' +  Math.round(distance) + ' km' + '';
				}

			}
			textLink += '';
		}

		if (debugweight) {
			textLink += '
';

			if (item.subID) {
				textLink += '<input value="' + item.subID + '" />';
			}

			var w = 0;
			if (item.weight) {
				w += item.weight;
			}
			if (item.distanceweight) {
				w += item.distanceweight;
			}
			textLink += 'Weight ' + Math.round(100*w)/100 + ' ';

			if (item.weight) {
				textLink += ' (base ' + item.weight + ')   ';
			}
			if (item.distanceweight) {
				textLink += '(dist ' + Math.round(100*item.distanceweight)/100 + ')';
			}

			textLink += '
';
		}

		// Add a clear bar.
		if (clear) {
			textLink += '
';
		}

		var relID = item.entityID;
		if (item.subID) {
			relID += '#' + item.subID;
		}

		// Wrap in A element
		textLink = '' +
			textLink + '';

		this.resulthtml += textLink;
	},

	"refreshData": function(showmore, show, listcount) {
		var that = this;

		this.parent.Utils.log('DiscoJuice.UI refreshData()');

		this.popup.find("div.scroller").empty().append(this.resulthtml);
		this.popup.find("div.scroller a").each(function() {
			var overthere = that;	// Overthere is a reference to the UI object
			$(this).click(function(event) {
				event.preventDefault();
				overthere.hide();

				// The "rel" attribute is containing: 'entityid#subid'
				// THe following code, decodes that.
				var relID = unescape($(this).attr('rel'));
				var entityID = relID;
				var subID = undefined;
				if (relID.match(/^.*#.+?$/)) {
					var matched = /^(.*)#(.+?)$/.exec(relID);
					entityID = matched[1];
					subID = matched[2];
				}
				overthere.control.selectProvider(entityID, subID);
			});
		});

		if (showmore) {
			var moreLink = 'Results limited to ' + show + ' entries – show more…';
			this.popup.find("p.discojuice_moreLinkContainer").empty().append(moreLink);
			this.popup.find("p.discojuice_moreLinkContainer a.discojuice_showmore").click(function(event) {
				event.preventDefault();
				that.control.increase();
			});
		} else {
			this.popup.find("p.discojuice_moreLinkContainer").empty();
			if (listcount > 10) {
				var moreLink = '' + listcount + ' entries listed';
				this.popup.find("p.discojuice_moreLinkContainer").append(moreLink);
			}
		}
	},

	"enable": function(control) {
		var imgpath = this.parent.Utils.options.get('discoPath', '') + 'images/';

		var textSearch = this.parent.Utils.options.get('textSearch', 'or search for a provider, in example Univerity of Oslo');
		var textHelp = this.parent.Utils.options.get('textHelp', 'Help me, I cannot find my provider');
		var textHelpMore = this.parent.Utils.options.get('textHelpMore', 'If your institusion is not connected to Foodle, you may create a new account using any of the Guest providers, such as OpenIdP (Guest users).');

		var subtitleText = this.parent.Utils.options.get('subtitle', null);
		var subtitleHTML = (subtitleText !== null ? '
' + subtitleText + '
' : '');

		var html = 	'
' +
			'
' +
				' ' +
				'
' + this.parent.Utils.options.get('title', 'Title')  +  '
' +
				subtitleHTML +
			'
' +

			'
' +
				'
' +
					'
 Loading list of providers...
' +
				'
' +
				'

' +
			'
' +

			'
' +
				'
<input type="search" class="discojuice_search" results=5 autosave="discojuice" name="searchfield" placeholder="' + textSearch + '" value="" />
' +
				'
' +
					'' + textHelp + '' +
					'
' + textHelpMore + '
' +
				'
' +
			'
' +

			'
' +
				'
' +
					'
' +
						'' +
						'Locate me and show nearby providers' +
					'
' +
					'

' +
				'
' +
				'

' +
			'
' +

			'
' +
				'<button id="discojuiceextesion_listener" />' +
			'
' +

			'
' +
				'
DiscoJuice © UNINETT
' +
			'
' +

		'
';
		var that = this;

		if (this.parent.Utils.options.get('overlay', true) === true) {
			var overlay = '

';
			$(overlay).appendTo($("body"));
		}

		this.popup = $(html).appendTo($("body"));

		if (this.parent.Utils.options.get('always', false) === true) {
			this.popup.find(".discojuice_close").hide();
			this.show();
		} else {
			// Add a listener to the sign in button.
			$(control).click(function(event) {
				event.preventDefault();
				that.show();
				return false;
			});
		}

		this.popup.find("#discojuiceextesion_listener").click(function() {
			that.control.discojuiceextension();
		});

		// Add listeners to the close button.
		this.popup.find(".discojuice_close").click(function() {
			that.hide();
		});

 		// Add toogle for what is this text.
		this.popup.find(".discojuice_what").click(function() {
			that.popup.find(".discojuice_whatisthis").toggleClass("show");
		});

		if (this.parent.Utils.options.get('location', false) && navigator.geolocation) {
			var that = this;
			$("a#locateme").click(function(event) {
				that.parent.Utils.log('Locate me. Detected click event.');
				var imgpath = that.parent.Utils.options.get('discoPath', '') + 'images/';
				event.preventDefault();
 				event.stopPropagation();
				$("div.locatemebefore").hide();
				$("div.locatemeafter").html('
 Getting your location...
');
				that.control.locateMe();
			});
		} else {
			$("dd#locatemediv").hide();
		}

	},

	"setLocationText": function(html) {
		return $("div.locatemeafter").html(html);
	},

	"addContent": function(html) {
		return $(html).appendTo($("body"));
	},
	"addFilter": function(html) {
		return $(html).prependTo(this.popup.find('.filters'));
//		this.popup.find('.filters').append(html).css('border', '1px solid red');
	}
};

<|endoftext|>


<commit_before>/**
 * @file MaterialDatePickerTextField component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import TextField from '../TextField';
import MaterialFieldSeparator from '../_MaterialFieldSeparator';
import Theme from '../Theme';

import Util from '../_vendors/Util';

class MaterialDatePickerTextField extends Component {

    static Type = TextField.Type;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: '',
            isFocus: false,
            isHover: false
        };

        this.triggerFocusHandler = ::this.triggerFocusHandler;
        this.triggerBlurHandler = ::this.triggerBlurHandler;
        this.triggerChangeHandler = ::this.triggerChangeHandler;
        this.triggerMouseOverHandler = ::this.triggerMouseOverHandler;
        this.triggerMouseOutHandler = ::this.triggerMouseOutHandler;

    }

    triggerFocusHandler(...args) {
        this.setState({
            isFocus: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(...args);
        });
    }

    triggerBlurHandler(...args) {
        const {popupVisible} = this.props;
        this.setState({
            isFocus: popupVisible
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(...args);
        });
    }

    triggerChangeHandler(value) {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    }

    triggerMouseOverHandler(...args) {
        this.setState({
            isHover: true
        }, () => {
            const {onMouseOver} = this.props;
            onMouseOver && onMouseOver(...args);
        });
    }

    triggerMouseOutHandler(...args) {
        this.setState({
            isHover: false
        }, () => {
            const {onMouseOut} = this.props;
            onMouseOut && onMouseOut(...args);
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value || nextProps.popupVisible !== this.props.popupVisible) {
            this.setState({
                value: nextProps.value,
                isFocus: nextProps.popupVisible
            });
        }
    }

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    render() {

        const {
                className, style, theme, label, isLabelAnimate, popupVisible,
                ...restProps
            } = this.props,
            {isFocus, isHover, value} = this.state,

            fieldClassName = classNames('material-date-picker-text-field', {
                animated: isLabelAnimate,
                'has-label': label,
                focused: isFocus,
                'has-value': value,
                [className]: className
            });

        return (

                {
                    label ?

                            {label}

                        :
                        null
                }

                <TextField {...restProps}
                           theme={theme}
                           value={value}
                           onFocus={this.triggerFocusHandler}
                           onBlur={this.triggerBlurHandler}
                           onMouseOver={this.triggerMouseOverHandler}
                           onMouseOut={this.triggerMouseOutHandler}
                           onChange={this.triggerChangeHandler}/>

                <MaterialFieldSeparator theme={theme}
                                        isHover={isHover}
                                        isFocus={isFocus}/>

        );

    }
};

MaterialDatePickerTextField.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The TextField theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * Specifies the type of input to display such as "password" or "text".
     */
    type: PropTypes.oneOf(Util.enumerateValue(TextField.Type)),

    /**
     * The name of the text field.
     */
    name: PropTypes.string,

    /**
     * The label of the text field.
     */
    label: PropTypes.any,

    /**
     * The animate of the text field.
     */
    isLabelAnimate: PropTypes.bool,

    /**
     * The placeholder of the text field.
     */
    placeholder: PropTypes.string,

    /**
     * The value of the text field.Type can be string or number.
     */
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Use this property to display an icon.
     */
    iconCls: PropTypes.string,

    /**
     * Disables the textField if set to true.
     */
    disabled: PropTypes.bool,

    /**
     * If true,the textField will can't editable.
     */
    readOnly: PropTypes.bool,

    /**
     * If true,the textField will autoFocus.
     */
    autoFocus: PropTypes.bool,

    /**
     * The message of the textField.
     */
    infoMsg: PropTypes.string,

    /**
     * If true,clearButton will display when the textField is not empty.
     */
    clearButtonVisible: PropTypes.bool,

    /**
     * Use this property to display an icon.
     */
    rightIconCls: PropTypes.string,

    /**
     * If true,passwordButton will display.
     */
    passwordButtonVisible: PropTypes.bool,

    /**
     * If true,the textField will always Focus.
     */
    popupVisible: PropTypes.bool,

    // valid
    /**
     * If true,the textField must be required.
     */
    required: PropTypes.bool,

    /**
     * The maximum length of textField to enter.
     */
    maxLength: PropTypes.number,

    /**
     * Maximum number of textField that can be entered.
     */
    max: PropTypes.number,

    /**
     * Minimum number of textField that can be entered.
     */
    min: PropTypes.number,

    /**
     * The matching rule of textField.
     */
    pattern: PropTypes.object,

    /**
     * The invalid message of textField.
     */
    patternInvalidMsg: PropTypes.string,

    /**
     *
     */
    autoComplete: PropTypes.string,

    /**
     *
     */
    autoCorrect: PropTypes.string,

    /**
     *
     */
    autoCapitalize: PropTypes.string,

    /**
     *
     */
    spellCheck: PropTypes.string,

    /**
     * Callback function fired when the textField is changed.
     */
    onChange: PropTypes.func,

    /**
     * Callback function fired when the press enter.
     */
    onPressEnter: PropTypes.func,

    /**
     * Callback function fired when textField valid.
     */
    onValid: PropTypes.func,

    /**
     * Callback function fired when textField invalid.
     */
    onInvalid: PropTypes.func,

    /**
     * Callback function fired when textField focus.
     */
    onFocus: PropTypes.func,

    /**
     * Callback function fired when textField blur.
     */
    onBlur: PropTypes.func,

    /**
     * Callback function fired when textField clear.
     */
    onClear: PropTypes.func,

    /**
     * Callback function fired when password visible.
     */
    onPasswordVisible: PropTypes.func,

    /**
     * Callback function fired when password invisible.
     */
    onPasswordInvisible: PropTypes.func,

    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

MaterialDatePickerTextField.defaultProps = {

    className: '',
    style: null,
    theme: Theme.DEFAULT,

    type: 'text',
    name: '',
    label: '',
    isLabelAnimate: true,
    placeholder: '',
    value: '',
    iconCls: '',
    disabled: false,
    readOnly: false,
    autoFocus: false,
    popupVisible: false,
    infoMsg: '',

    clearButtonVisible: true,
    passwordButtonVisible: true,

    // valid
    required: false,
    patternInvalidMsg: '',
    preventInvalidInput: false,

    autoComplete: 'off',
    autoCorrect: 'off',
    autoCapitalize: 'off',
    spellCheck: 'false',

    fieldMsgVisible: true

};

export default MaterialDatePickerTextField;<commit_msg>update MaterialDatePickerTextField<commit_after>/**
 * @file MaterialDatePickerTextField component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import TextField from '../TextField';
import MaterialFieldSeparator from '../_MaterialFieldSeparator';
import Theme from '../Theme';

import Util from '../_vendors/Util';

class MaterialDatePickerTextField extends Component {

    static Type = TextField.Type;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: '',
            isFocus: false,
            isHover: false
        };

        this.triggerFocusHandler = ::this.triggerFocusHandler;
        this.triggerBlurHandler = ::this.triggerBlurHandler;
        this.triggerChangeHandler = ::this.triggerChangeHandler;
        this.triggerMouseOverHandler = ::this.triggerMouseOverHandler;
        this.triggerMouseOutHandler = ::this.triggerMouseOutHandler;

    }

    triggerFocusHandler(...args) {
        this.setState({
            isFocus: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(...args);
        });
    }

    triggerBlurHandler(...args) {
        const {popupVisible} = this.props;
        this.setState({
            isFocus: popupVisible
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(...args);
        });
    }

    triggerChangeHandler(value) {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    }

    triggerMouseOverHandler(...args) {
        this.setState({
            isHover: true
        }, () => {
            const {onMouseOver} = this.props;
            onMouseOver && onMouseOver(...args);
        });
    }

    triggerMouseOutHandler(...args) {
        this.setState({
            isHover: false
        }, () => {
            const {onMouseOut} = this.props;
            onMouseOut && onMouseOut(...args);
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value || nextProps.popupVisible !== this.props.popupVisible) {
            this.setState({
                value: nextProps.value,
                isFocus: nextProps.popupVisible
            });
        }
    }

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    render() {

        const {
                className, style, theme, label, isLabelAnimate, popupVisible,
                ...restProps
            } = this.props,
            {isFocus, isHover, value} = this.state,

            fieldClassName = classNames('material-date-picker-text-field', {
                animated: isLabelAnimate,
                'has-label': label,
                focused: isFocus,
                'has-value': value,
                [className]: className
            });

        return (

                {
                    label ?

                            {label}

                        :
                        null
                }

                <TextField {...restProps}
                           theme={theme}
                           value={value}
                           onFocus={this.triggerFocusHandler}
                           onBlur={this.triggerBlurHandler}
                           onMouseOver={this.triggerMouseOverHandler}
                           onMouseOut={this.triggerMouseOutHandler}
                           onChange={this.triggerChangeHandler}/>

                <MaterialFieldSeparator theme={theme}
                                        isHover={isHover}
                                        isFocus={isFocus}/>

        );

    }
};

MaterialDatePickerTextField.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The TextField theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * Specifies the type of input to display such as "password" or "text".
     */
    type: PropTypes.oneOf(Util.enumerateValue(TextField.Type)),

    /**
     * The name of the text field.
     */
    name: PropTypes.string,

    /**
     * The label of the text field.
     */
    label: PropTypes.any,

    /**
     * The animate of the text field.
     */
    isLabelAnimate: PropTypes.bool,

    /**
     * The placeholder of the text field.
     */
    placeholder: PropTypes.string,

    /**
     * The value of the text field.Type can be string or number.
     */
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Use this property to display an icon.
     */
    iconCls: PropTypes.string,

    /**
     * Disables the textField if set to true.
     */
    disabled: PropTypes.bool,

    /**
     * If true,the textField will can't editable.
     */
    readOnly: PropTypes.bool,

    /**
     * If true,the textField will autoFocus.
     */
    autoFocus: PropTypes.bool,

    /**
     * The message of the textField.
     */
    infoMsg: PropTypes.string,

    /**
     * If true,clearButton will display when the textField is not empty.
     */
    clearButtonVisible: PropTypes.bool,

    /**
     * Use this property to display an icon.
     */
    rightIconCls: PropTypes.string,

    /**
     * If true,passwordButton will display.
     */
    passwordButtonVisible: PropTypes.bool,

    /**
     * If true,the textField will always Focus.
     */
    popupVisible: PropTypes.bool,

    // valid
    /**
     * If true,the textField must be required.
     */
    required: PropTypes.bool,

    /**
     * The maximum length of textField to enter.
     */
    maxLength: PropTypes.number,

    /**
     * Maximum number of textField that can be entered.
     */
    max: PropTypes.number,

    /**
     * Minimum number of textField that can be entered.
     */
    min: PropTypes.number,

    /**
     * The matching rule of textField.
     */
    pattern: PropTypes.object,

    /**
     * The invalid message of textField.
     */
    patternInvalidMsg: PropTypes.string,

    /**
     *
     */
    autoComplete: PropTypes.string,

    /**
     *
     */
    autoCorrect: PropTypes.string,

    /**
     *
     */
    autoCapitalize: PropTypes.string,

    /**
     *
     */
    spellCheck: PropTypes.string,

    /**
     * Callback function fired when the textField is changed.
     */
    onChange: PropTypes.func,

    /**
     * Callback function fired when the press enter.
     */
    onPressEnter: PropTypes.func,

    /**
     * Callback function fired when textField valid.
     */
    onValid: PropTypes.func,

    /**
     * Callback function fired when textField invalid.
     */
    onInvalid: PropTypes.func,

    /**
     * Callback function fired when textField focus.
     */
    onFocus: PropTypes.func,

    /**
     * Callback function fired when textField blur.
     */
    onBlur: PropTypes.func,

    /**
     * Callback function fired when textField clear.
     */
    onClear: PropTypes.func,

    /**
     * Callback function fired when password visible.
     */
    onPasswordVisible: PropTypes.func,

    /**
     * Callback function fired when password invisible.
     */
    onPasswordInvisible: PropTypes.func,

    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

MaterialDatePickerTextField.defaultProps = {

    className: '',
    style: null,
    theme: Theme.DEFAULT,

    type: 'text',
    name: '',
    label: '',
    isLabelAnimate: true,
    placeholder: '',
    value: '',
    iconCls: '',
    disabled: false,
    readOnly: false,
    autoFocus: false,
    popupVisible: false,
    infoMsg: '',

    clearButtonVisible: true,
    passwordButtonVisible: true,

    // valid
    required: false,
    patternInvalidMsg: '',

    autoComplete: 'off',
    autoCorrect: 'off',
    autoCapitalize: 'off',
    spellCheck: 'false',

    fieldMsgVisible: true

};

export default MaterialDatePickerTextField;<|endoftext|>


<commit_before>var ko = require('knockout')
var _ = require('lodash')
var ajax = require('basic-ajax')
var Endpoints = require('../endpoint-builder')
var getUrlParameter = require('../get-url-parameter')
var cookies = require('../cookies')
var OpeningTime = require('./OpeningTime')
var Address = require('./Address')
var BaseViewModel = require('./BaseViewModel')
var adminUrls = require('../admin-urls')

function Service (data) {
  var self = this
  self.serviceProviderId = data.serviceProviderId
  self.id = ko.observable(data.key)

  self.name = data.name
  self.info = ko.observable(data.info)
  self.locationDescription = ko.observable(data.locationDescription)

  var tags = data.tags !== null ? data.tags.join(', ') : ''

  self.tags = ko.observable(tags)
  self.openingTimes = ko.observableArray(_.map(data.openingTimes, function (ot) {
    return new OpeningTime(ot)
  }))
  self.address = new Address(data.address)

  self.savedName = ko.observable(data.name)
  self.savedInfo = ko.observable(data.info)
  self.savedTags = ko.observable(tags)
  self.savedOpeningTimes = ko.observableArray(_.map(data.openingTimes, function (ot) {
    return new OpeningTime(ot)
  }))
  self.savedAddress = new Address(data.address)

  self.isEditing = ko.observable(false)
  self.message = ko.observable()
  self.endpoints = new Endpoints()
  self.listeners = ko.observableArray()

  self.editServiceUrl = adminUrls.serviceProviderServicesEdit + '?providerId=' + data.serviceProviderId + '&serviceId=' + self.id()

  self.edit = function () {
    self.isEditing(true)
  }

  self.cancelEdit = function () {
    self.restoreFields()
  }

  self.restoreFields = function () {
    self.isEditing(false)
    self.info(self.savedInfo())
    self.tags(self.savedTags())

    var restoredOpeningTimes = _.map(self.savedOpeningTimes(), function (ot) {
      return new OpeningTime({
        'day': ot.day(),
        'startTime': ot.startTime(),
        'endTime': ot.endTime()
      })
    })
    self.openingTimes(restoredOpeningTimes)

    self.address.cancel()
  }

  self.newOpeningTime = function () {
    var openingTimes = self.openingTimes()
    openingTimes.push(new OpeningTime({
      'day': '',
      'startTime': '',
      'endTime': ''
    }))
    self.openingTimes(openingTimes)
  }

  self.removeOpeningTime = function (openingTimeToRemove) {
    var o = function (o) {
      return o.day() !== openingTimeToRemove.day() ||
             o.startTime() !== openingTimeToRemove.startTime() ||
             o.endTime() !== openingTimeToRemove.endTime()
    }
    var remaining = self.openingTimes().filter(o => notToBeRemoved(o))

    self.openingTimes(remaining)
  }

  self.save = function () {
    var endpoint = self.endpointBuilder.serviceProviders(self.serviceProviderId).services(self.id()).build()
    var tags = []
    if (self.tags().length > 0) tags = _.map(self.tags().split(','), function (t) { return t.trim() })

    var model = JSON.stringify({
      'Info': self.info(),
      'LocationDescription': self.locationDescription(),
      'Tags': tags,
      'OpeningTimes': _.map(self.openingTimes(), function (openingTime) {
        return {
          'StartTime': openingTime.startTime(),
          'EndTime': openingTime.endTime(),
          'Day': openingTime.day()
        }
      }),
      'Address': {
        'Street1': self.address.street1(),
        'Street2': self.address.street2(),
        'Street3': self.address.street3(),
        'Street4': self.address.street4(),
        'City': self.address.city(),
        'Postcode': self.address.postcode()
      }
    })

    ajax.put(endpoint,
      self.headers(cookies.get('session-token')),
      model
    ).then(function (result) {
      self.isEditing(false)
      self.listeners().forEach(l => l.serviceSaved(self))
    }, function (error) {
      self.handleError(error)
    })
  }

  self.deleteService = function () {
    var endpoint = self.endpointBuilder.serviceProviders(getUrlParameter.parameter('key')).services(self.id()).build()
    ajax.delete(endpoint, self.headers(cookies.get('session-token')), JSON.stringify({}))
    .then(function (result) {
      _.forEach(self.listeners(), function (listener) {
        listener.deleteService(self)
      })
    }, function (error) {
      self.handleError(error)
    })
  }

  self.addListener = function (listener) {
    self.listeners().push(listener)
  }
}

Service.prototype = new BaseViewModel()

module.exports = Service
<commit_msg>removing lodash<commit_after>var ko = require('knockout')
var _ = require('lodash')
var ajax = require('basic-ajax')
var Endpoints = require('../endpoint-builder')
var getUrlParameter = require('../get-url-parameter')
var cookies = require('../cookies')
var OpeningTime = require('./OpeningTime')
var Address = require('./Address')
var BaseViewModel = require('./BaseViewModel')
var adminUrls = require('../admin-urls')

function Service (data) {
  var self = this
  self.serviceProviderId = data.serviceProviderId
  self.id = ko.observable(data.key)

  self.name = data.name
  self.info = ko.observable(data.info)
  self.locationDescription = ko.observable(data.locationDescription)

  var tags = data.tags !== null ? data.tags.join(', ') : ''

  self.tags = ko.observable(tags)
  self.openingTimes = ko.observableArray(_.map(data.openingTimes, function (ot) {
    return new OpeningTime(ot)
  }))
  self.address = new Address(data.address)

  self.savedName = ko.observable(data.name)
  self.savedInfo = ko.observable(data.info)
  self.savedTags = ko.observable(tags)
  self.savedOpeningTimes = ko.observableArray(_.map(data.openingTimes, function (ot) {
    return new OpeningTime(ot)
  }))
  self.savedAddress = new Address(data.address)

  self.isEditing = ko.observable(false)
  self.message = ko.observable()
  self.endpoints = new Endpoints()
  self.listeners = ko.observableArray()

  self.editServiceUrl = adminUrls.serviceProviderServicesEdit + '?providerId=' + data.serviceProviderId + '&serviceId=' + self.id()

  self.edit = function () {
    self.isEditing(true)
  }

  self.cancelEdit = function () {
    self.restoreFields()
  }

  self.restoreFields = function () {
    self.isEditing(false)
    self.info(self.savedInfo())
    self.tags(self.savedTags())

    var restoredOpeningTimes = _.map(self.savedOpeningTimes(), function (ot) {
      return new OpeningTime({
        'day': ot.day(),
        'startTime': ot.startTime(),
        'endTime': ot.endTime()
      })
    })
    self.openingTimes(restoredOpeningTimes)

    self.address.cancel()
  }

  self.newOpeningTime = function () {
    var openingTimes = self.openingTimes()
    openingTimes.push(new OpeningTime({
      'day': '',
      'startTime': '',
      'endTime': ''
    }))
    self.openingTimes(openingTimes)
  }

  self.removeOpeningTime = function (openingTimeToRemove) {
    var notToBeRemoved = function (o) {
      return o.day() !== openingTimeToRemove.day() ||
             o.startTime() !== openingTimeToRemove.startTime() ||
             o.endTime() !== openingTimeToRemove.endTime()
    }
    var remaining = self.openingTimes().filter(o => notToBeRemoved(o))

    self.openingTimes(remaining)
  }

  self.save = function () {
    var endpoint = self.endpointBuilder.serviceProviders(self.serviceProviderId).services(self.id()).build()
    var tags = []
    if (self.tags().length > 0) tags = _.map(self.tags().split(','), function (t) { return t.trim() })

    var model = JSON.stringify({
      'Info': self.info(),
      'LocationDescription': self.locationDescription(),
      'Tags': tags,
      'OpeningTimes': _.map(self.openingTimes(), function (openingTime) {
        return {
          'StartTime': openingTime.startTime(),
          'EndTime': openingTime.endTime(),
          'Day': openingTime.day()
        }
      }),
      'Address': {
        'Street1': self.address.street1(),
        'Street2': self.address.street2(),
        'Street3': self.address.street3(),
        'Street4': self.address.street4(),
        'City': self.address.city(),
        'Postcode': self.address.postcode()
      }
    })

    ajax.put(endpoint,
      self.headers(cookies.get('session-token')),
      model
    ).then(function (result) {
      self.isEditing(false)
      self.listeners().forEach(l => l.serviceSaved(self))
    }, function (error) {
      self.handleError(error)
    })
  }

  self.deleteService = function () {
    var endpoint = self.endpointBuilder.serviceProviders(getUrlParameter.parameter('key')).services(self.id()).build()
    ajax.delete(endpoint, self.headers(cookies.get('session-token')), JSON.stringify({}))
    .then(function (result) {
      _.forEach(self.listeners(), function (listener) {
        listener.deleteService(self)
      })
    }, function (error) {
      self.handleError(error)
    })
  }

  self.addListener = function (listener) {
    self.listeners().push(listener)
  }
}

Service.prototype = new BaseViewModel()

module.exports = Service
<|endoftext|>


<commit_before>// ==UserScript==
// @name           Stop gif animations on escape
// @namespace      http://github.com/johan/
// @description    Implements the "stop gif animations on hitting the escape key" feature that all browsers except Safari and Google Chrome have had since forever. Now also for Google Chrome!
// ==/UserScript==

document.addEventListener('keydown', freeze_gifs_on_escape, true);

function freeze_gifs_on_escape(e) {
  if (e.keyCode == 27 && !e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
    [].slice.apply(document.images).filter(is_gif_image).map(freeze_gif);
  }
}

function is_gif_image(i) {
  return /^(?!data:).*\.gif/i.test(i.src);
}

function freeze_gif(i) {
  var c = document.createElement('canvas');
  var w = c.width = i.width;
  var h = c.height = i.height;
  c.getContext('2d').drawImage(i, 0, 0, w, h);
  try {
    i.src = c.toDataURL("image/gif"); // if possible, retain all css aspects
  } catch(e) { // cross-domain -- mimic original with all its tag attributes
    for (var j = 0, a; a = i.attributes[j]; i++)
      i.setAttribute(a.name, a.value);
    i.parentNode.replaceChild(c, i);
  }
}
<commit_msg>Oops; fixed typo.<commit_after>// ==UserScript==
// @name           Stop gif animations on escape
// @namespace      http://github.com/johan/
// @description    Implements the "stop gif animations on hitting the escape key" feature that all browsers except Safari and Google Chrome have had since forever. Now also for Google Chrome!
// ==/UserScript==

document.addEventListener('keydown', freeze_gifs_on_escape, true);

function freeze_gifs_on_escape(e) {
  if (e.keyCode == 27 && !e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
    [].slice.apply(document.images).filter(is_gif_image).map(freeze_gif);
  }
}

function is_gif_image(i) {
  return /^(?!data:).*\.gif/i.test(i.src);
}

function freeze_gif(i) {
  var c = document.createElement('canvas');
  var w = c.width = i.width;
  var h = c.height = i.height;
  c.getContext('2d').drawImage(i, 0, 0, w, h);
  try {
    i.src = c.toDataURL("image/gif"); // if possible, retain all css aspects
  } catch(e) { // cross-domain -- mimic original with all its tag attributes
    for (var j = 0, a; a = i.attributes[j]; j++)
      i.setAttribute(a.name, a.value);
    i.parentNode.replaceChild(c, i);
  }
}
<|endoftext|>