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

frappe.provide("frappe.ui.form");

frappe.ui.form.Review = class Review {
	constructor({parent, frm}) {
		this.parent = parent;
		this.frm = frm;
		this.fetch_energy_points()
			.then(() => {
				this.make_review_container();
				this.add_review_button();
				this.update_reviewers();
			});
	}
	fetch_energy_points() {
		return frappe.xcall('frappe.social.doctype.energy_point_log.energy_point_log.get_energy_points', {
			user: frappe.session.user
		}).then(data => {
			this.points = data;
		});
	}
	make_review_container() {
		this.$wrapper = this.parent.append(`

${__('Reviews')}

		`);
		this.review_list_wrapper = this.$wrapper.find('.review-list');
	}
	add_review_button() {

		this.review_list_wrapper.append(`

		`);

		const review_button = this.review_list_wrapper.find('.btn-add-review');

		if (!this.points.review_points) {
			review_button.click(false);
			review_button.popover({
				trigger: 'hover',
				content: () => {
					return `

						${__('You do not have enough review points')}

`;
				},
				html: true
			});
		} else {
			review_button.click(() => this.show_review_dialog());
		}
	}
	get_involved_users() {
		const user_fields = this.frm.meta.fields
			.filter(d => d.fieldtype === 'Link' && d.options === 'User')
			.map(d => d.fieldname);

		user_fields.push('owner');
		let involved_users = user_fields.map(field => this.frm.doc[field]);

		const docinfo = this.frm.get_docinfo();

		involved_users = involved_users.concat(
			docinfo.communications.map(d => d.sender && d.delivery_status==='sent'),
			docinfo.comments.map(d => d.owner),
			docinfo.versions.map(d => d.owner),
			docinfo.assignments.map(d => d.owner)
		);

		return involved_users
			.uniqBy(u => u)
			.filter(user => !['Administrator', frappe.session.user].includes(user))
			.filter(Boolean);
	}
	show_review_dialog() {
		const user_options = this.get_involved_users();
		const doc_owner = this.frm.doc.owner;
		const review_dialog = new frappe.ui.Dialog({
			'title': __('Add Review'),
			'fields': [{
				fieldname: 'to_user',
				fieldtype: 'Autocomplete',
				label: __('To User'),
				options: user_options,
				default: user_options.includes(doc_owner) ? doc_owner : user_options[0],
				description: __('Only users involved in the document are listed')
			}, {
				fieldname: 'review_type',
				fieldtype: 'Select',
				label: __('Action'),
				options: [{
					'label': __('Appreciate'),
					'value': 'Appreciation'
				}, {
					'label': __('Criticize'),
					'value': 'Criticism'
				}],
				default: 'Appreciation'
			}, {
				fieldname: 'points',
				fieldtype: 'Int',
				label: __('Points'),
				reqd: 1,
				description: __(`Currently you have ${this.points.review_points} review points`)
			}, {
				fieldtype: 'Small Text',
				fieldname: 'reason',
				reqd: 1,
				label: __('Reason')
			}],
			primary_action: (values) => {
				if (values.points > this.points.review_points) {
					return frappe.msgprint(__('You do not have enough points'));
				}
				frappe.xcall('frappe.social.doctype.energy_point_log.energy_point_log.review', {
					doc: {
						doctype: this.frm.doc.doctype,
						name: this.frm.doc.name,
					},
					to_user: values.to_user,
					points: values.points,
					review_type: values.review_type,
					reason: values.reason
				}).then(review => {
					review_dialog.hide();
					review_dialog.clear();
					this.frm.get_docinfo().energy_point_logs.unshift(review);
					this.frm.timeline.refresh();
					this.update_reviewers();
				});
			},
			primary_action_label: __('Submit')
		});
		review_dialog.show();
	}
	update_reviewers() {
		const review_logs = this.frm.get_docinfo().energy_point_logs
			.filter(log => ['Appreciation', 'Criticism'].includes(log.type));

		this.review_list_wrapper.find('.review-pill').remove();
		review_logs.forEach(log => {
			let review_pill = $(`

					${frappe.avatar(log.owner)}
					${frappe.energy_points.get_points(log.points)}

			`);
			this.review_list_wrapper.prepend(review_pill);
			this.setup_detail_popover(review_pill, log);
		});
	}
	setup_detail_popover(el, data) {
		let subject = '';
		let fullname = frappe.user.full_name(data.user);
		let timestamp = `<span class="text-muted">${frappe.datetime.comment_when(data.creation)}</span>`;
		let message_parts = [Math.abs(data.points), fullname, timestamp];
		if (data.type === 'Appreciation') {
			if (data.points == 1) {
				subject = __('{0} appreciation point for {1} {2}', message_parts);
			} else {
				subject = __('{0} appreciation points for {1} {2}', message_parts);
			}
		} else {
			if (data.points == -1) {
				subject = __('{0} criticism point for {1} {2}', message_parts);
			} else {
				subject = __('{0} criticism points for {1} {2}', message_parts);
			}
		}
		el.popover({
			animation: true,
			trigger: 'hover',
			delay: 500,
			placement: 'top',
			template:`

			`,
			content: () => {
				return `

							${subject}

${data.reason}

				`;
			},
			html: true,
			container: 'body'
		});
		return el;
	}
};
<commit_msg>fix: Disable submit button while doing network request - to avoid duplicate entries<commit_after>// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide("frappe.ui.form");

frappe.ui.form.Review = class Review {
	constructor({parent, frm}) {
		this.parent = parent;
		this.frm = frm;
		this.fetch_energy_points()
			.then(() => {
				this.make_review_container();
				this.add_review_button();
				this.update_reviewers();
			});
	}
	fetch_energy_points() {
		return frappe.xcall('frappe.social.doctype.energy_point_log.energy_point_log.get_energy_points', {
			user: frappe.session.user
		}).then(data => {
			this.points = data;
		});
	}
	make_review_container() {
		this.$wrapper = this.parent.append(`

${__('Reviews')}

		`);
		this.review_list_wrapper = this.$wrapper.find('.review-list');
	}
	add_review_button() {

		this.review_list_wrapper.append(`

		`);

		const review_button = this.review_list_wrapper.find('.btn-add-review');

		if (!this.points.review_points) {
			review_button.click(false);
			review_button.popover({
				trigger: 'hover',
				content: () => {
					return `

						${__('You do not have enough review points')}

`;
				},
				html: true
			});
		} else {
			review_button.click(() => this.show_review_dialog());
		}
	}
	get_involved_users() {
		const user_fields = this.frm.meta.fields
			.filter(d => d.fieldtype === 'Link' && d.options === 'User')
			.map(d => d.fieldname);

		user_fields.push('owner');
		let involved_users = user_fields.map(field => this.frm.doc[field]);

		const docinfo = this.frm.get_docinfo();

		involved_users = involved_users.concat(
			docinfo.communications.map(d => d.sender && d.delivery_status==='sent'),
			docinfo.comments.map(d => d.owner),
			docinfo.versions.map(d => d.owner),
			docinfo.assignments.map(d => d.owner)
		);

		return involved_users
			.uniqBy(u => u)
			.filter(user => !['Administrator', frappe.session.user].includes(user))
			.filter(Boolean);
	}
	show_review_dialog() {
		const user_options = this.get_involved_users();
		const doc_owner = this.frm.doc.owner;
		const review_dialog = new frappe.ui.Dialog({
			'title': __('Add Review'),
			'fields': [{
				fieldname: 'to_user',
				fieldtype: 'Autocomplete',
				label: __('To User'),
				options: user_options,
				default: user_options.includes(doc_owner) ? doc_owner : user_options[0],
				description: __('Only users involved in the document are listed')
			}, {
				fieldname: 'review_type',
				fieldtype: 'Select',
				label: __('Action'),
				options: [{
					'label': __('Appreciate'),
					'value': 'Appreciation'
				}, {
					'label': __('Criticize'),
					'value': 'Criticism'
				}],
				default: 'Appreciation'
			}, {
				fieldname: 'points',
				fieldtype: 'Int',
				label: __('Points'),
				reqd: 1,
				description: __(`Currently you have ${this.points.review_points} review points`)
			}, {
				fieldtype: 'Small Text',
				fieldname: 'reason',
				reqd: 1,
				label: __('Reason')
			}],
			primary_action: (values) => {
				review_dialog.disable_primary_action();
				if (values.points > this.points.review_points) {
					return frappe.msgprint(__('You do not have enough points'));
				}
				frappe.xcall('frappe.social.doctype.energy_point_log.energy_point_log.review', {
					doc: {
						doctype: this.frm.doc.doctype,
						name: this.frm.doc.name,
					},
					to_user: values.to_user,
					points: values.points,
					review_type: values.review_type,
					reason: values.reason
				}).then(review => {
					review_dialog.hide();
					review_dialog.clear();
					this.frm.get_docinfo().energy_point_logs.unshift(review);
					this.frm.timeline.refresh();
					this.update_reviewers();
				}).finally(() => {
					review_dialog.enable_primary_action();
				});
			},
			primary_action_label: __('Submit')
		});
		review_dialog.show();
	}
	update_reviewers() {
		const review_logs = this.frm.get_docinfo().energy_point_logs
			.filter(log => ['Appreciation', 'Criticism'].includes(log.type));

		this.review_list_wrapper.find('.review-pill').remove();
		review_logs.forEach(log => {
			let review_pill = $(`

					${frappe.avatar(log.owner)}
					${frappe.energy_points.get_points(log.points)}

			`);
			this.review_list_wrapper.prepend(review_pill);
			this.setup_detail_popover(review_pill, log);
		});
	}
	setup_detail_popover(el, data) {
		let subject = '';
		let fullname = frappe.user.full_name(data.user);
		let timestamp = `<span class="text-muted">${frappe.datetime.comment_when(data.creation)}</span>`;
		let message_parts = [Math.abs(data.points), fullname, timestamp];
		if (data.type === 'Appreciation') {
			if (data.points == 1) {
				subject = __('{0} appreciation point for {1} {2}', message_parts);
			} else {
				subject = __('{0} appreciation points for {1} {2}', message_parts);
			}
		} else {
			if (data.points == -1) {
				subject = __('{0} criticism point for {1} {2}', message_parts);
			} else {
				subject = __('{0} criticism points for {1} {2}', message_parts);
			}
		}
		el.popover({
			animation: true,
			trigger: 'hover',
			delay: 500,
			placement: 'top',
			template:`

			`,
			content: () => {
				return `

							${subject}

${data.reason}

				`;
			},
			html: true,
			container: 'body'
		});
		return el;
	}
};
<|endoftext|>


<commit_before>(function () {
  'use strict';

  /* global AmCharts */

  angular.module('status').directive('chart', [
    'OpenTimeData', 'showGlobalError', 'TimeUtils',
    function (OpenTimeData, showGlobalError, TimeUtils) {

      function makeChart(chartData, chartElement) {
        var chart;
        // the last item to hover on (performance optimization for the balloon)
        var lastHoverItem = null;
        var ballonTextCache = '';

        function onRollOver(event) {
          var balloon = event.chart.balloon;
          var item = event.item;
          if (lastHoverItem !== event.index) {
            // data update is needed
            lastHoverItem = event.index;
            balloon.setPosition(event.item.x, event.item.y);

            var total = TimeUtils.getHourFormatBase60(item.values.value);
            ballonTextCache =
              'Geöffnet: ' + TimeUtils.formatAsTime(TimeUtils.getHourFormatBase60(item.values.open)) +
                '\nGeschlossen: ' + TimeUtils.formatAsTime(TimeUtils.getHourFormatBase60(item.values.close)) +
                '\nInsgesamt offen: \n' + TimeUtils.formatAsDuration(total);
          }
          balloon.showBalloon(ballonTextCache);
        }

        function onRollOut(event) {
        }

        function zoomChart() {
          // different zoom methods can be used - zoomToIndexes, zoomToDates,
          // zoomToCategoryValues
          chart.zoomToIndexes(chartData.length - 40, chartData.length - 1);
        }

        chart = new AmCharts.AmSerialChart();
        chart.pathToImages = 'img/amstats/';
        chart.autoMarginOffset = 3;
        chart.marginRight = 15;
        chart.zoomOutButton = {
          backgroundColor: '#000000',
          backgroundAlpha: 0.15
        };
        chart.dataProvider = chartData;
        chart.categoryField = 'date';

        // the following two lines makes chart 3D
        // chart.depth3D = 10;
        // chart.angle = 40;

        // data updated event will be fired when chart is displayed,
        // also when data will be updated. We'll use it to set some
        // initial zoom
        chart.addListener('dataUpdated', zoomChart);

        // AXES
        // Category
        var categoryAxis = chart.categoryAxis;
        categoryAxis.parseDates = true; // in order char to understand dates, we
        // should set parseDates to true
        categoryAxis.minPeriod = 'DD'; // as we have data with minute interval, we
        // have to set 'mm' here.
        categoryAxis.gridAlpha = 0.07;
        categoryAxis.showLastLabel = false;
        categoryAxis.axisColor = '#DADADA';

        // Value
        var valueAxis = new AmCharts.ValueAxis();
        valueAxis.gridAlpha = 0.07;
        valueAxis.maximum = 24;
        valueAxis.title = 'MainFrame open time (hours)';
        chart.addValueAxis(valueAxis);

        // GRAPH
        var graph = new AmCharts.AmGraph();
        // graph.type = 'column';
        graph.type = 'candlestick';
        graph.title = 'red line';

        graph.openField = 'open';
        graph.highField = 'open';
        graph.lowField = 'close';
        graph.closeField = 'close';

        graph.valueField = 'duration';
        graph.lineAlpha = 1;
        graph.lineColor = '#d1cf2a';
        graph.fillAlphas = 0.3; // setting fillAlphas to > 0 value makes it area
        // graph
        graph.showBalloonAt = 'open';
        graph.showBalloon = false;
        chart.addGraph(graph);
        chart.addListener('rollOverGraphItem', onRollOver);
        chart.addListener('rollOutGraphItem', onRollOut);

        // CURSOR
        var chartCursor = new AmCharts.ChartCursor();
        chartCursor.cursorPosition = 'mouse';
        chartCursor.categoryBalloonDateFormat = 'DD MMMM YYYY';
        chart.addChartCursor(chartCursor);

        // SCROLLBAR
        var chartScrollbar = new AmCharts.ChartScrollbar();

        chart.addChartScrollbar(chartScrollbar);

        // WRITE
        chart.write(chartElement[0]);
      }

      return {
        restrict: 'EA',
        link: function (scope, element, attrs) {
          OpenTimeData.get().then(
            function (chartData) {
              makeChart(chartData, element);
            });

        }
      };
    }
  ]);
})();<commit_msg>fixing image path ref<commit_after>(function () {
  'use strict';

  /* global AmCharts */

  angular.module('status').directive('chart', [
    'OpenTimeData', 'showGlobalError', 'TimeUtils',
    function (OpenTimeData, showGlobalError, TimeUtils) {

      function makeChart(chartData, chartElement) {
        var chart;
        // the last item to hover on (performance optimization for the balloon)
        var lastHoverItem = null;
        var ballonTextCache = '';

        function onRollOver(event) {
          var balloon = event.chart.balloon;
          var item = event.item;
          if (lastHoverItem !== event.index) {
            // data update is needed
            lastHoverItem = event.index;
            balloon.setPosition(event.item.x, event.item.y);

            var total = TimeUtils.getHourFormatBase60(item.values.value);
            ballonTextCache =
              'Geöffnet: ' + TimeUtils.formatAsTime(TimeUtils.getHourFormatBase60(item.values.open)) +
                '\nGeschlossen: ' + TimeUtils.formatAsTime(TimeUtils.getHourFormatBase60(item.values.close)) +
                '\nInsgesamt offen: \n' + TimeUtils.formatAsDuration(total);
          }
          balloon.showBalloon(ballonTextCache);
        }

        function onRollOut(event) {
        }

        function zoomChart() {
          // different zoom methods can be used - zoomToIndexes, zoomToDates,
          // zoomToCategoryValues
          chart.zoomToIndexes(chartData.length - 40, chartData.length - 1);
        }

        chart = new AmCharts.AmSerialChart();
        chart.pathToImages = 'images/amstats/';
        chart.autoMarginOffset = 3;
        chart.marginRight = 15;
        chart.zoomOutButton = {
          backgroundColor: '#000000',
          backgroundAlpha: 0.15
        };
        chart.dataProvider = chartData;
        chart.categoryField = 'date';

        // the following two lines makes chart 3D
        // chart.depth3D = 10;
        // chart.angle = 40;

        // data updated event will be fired when chart is displayed,
        // also when data will be updated. We'll use it to set some
        // initial zoom
        chart.addListener('dataUpdated', zoomChart);

        // AXES
        // Category
        var categoryAxis = chart.categoryAxis;
        categoryAxis.parseDates = true; // in order char to understand dates, we
        // should set parseDates to true
        categoryAxis.minPeriod = 'DD'; // as we have data with minute interval, we
        // have to set 'mm' here.
        categoryAxis.gridAlpha = 0.07;
        categoryAxis.showLastLabel = false;
        categoryAxis.axisColor = '#DADADA';

        // Value
        var valueAxis = new AmCharts.ValueAxis();
        valueAxis.gridAlpha = 0.07;
        valueAxis.maximum = 24;
        valueAxis.title = 'MainFrame open time (hours)';
        chart.addValueAxis(valueAxis);

        // GRAPH
        var graph = new AmCharts.AmGraph();
        // graph.type = 'column';
        graph.type = 'candlestick';
        graph.title = 'red line';

        graph.openField = 'open';
        graph.highField = 'open';
        graph.lowField = 'close';
        graph.closeField = 'close';

        graph.valueField = 'duration';
        graph.lineAlpha = 1;
        graph.lineColor = '#d1cf2a';
        graph.fillAlphas = 0.3; // setting fillAlphas to > 0 value makes it area
        // graph
        graph.showBalloonAt = 'open';
        graph.showBalloon = false;
        chart.addGraph(graph);
        chart.addListener('rollOverGraphItem', onRollOver);
        chart.addListener('rollOutGraphItem', onRollOut);

        // CURSOR
        var chartCursor = new AmCharts.ChartCursor();
        chartCursor.cursorPosition = 'mouse';
        chartCursor.categoryBalloonDateFormat = 'DD MMMM YYYY';
        chart.addChartCursor(chartCursor);

        // SCROLLBAR
        var chartScrollbar = new AmCharts.ChartScrollbar();

        chart.addChartScrollbar(chartScrollbar);

        // WRITE
        chart.write(chartElement[0]);
      }

      return {
        restrict: 'EA',
        link: function (scope, element, attrs) {
          OpenTimeData.get().then(
            function (chartData) {
              makeChart(chartData, element);
            });

        }
      };
    }
  ]);
})();<|endoftext|>


<commit_before>/**
 *
 * check of identifying code
 *
 *
 *
 * 20150623
 */
 (function($, undefined){
 	//生产类
 	var Pub = function(el, op){
 		this.$el = $(el);
 		this.captchaInput = this.$el.find('[node-type="LoanCaptchaInput"]');
 		this.sendBtn = this.$el.find('[node-type="LoanSendBtn"]');
 		this.confirmBtn = this.$el.find('[node-type="LoanConfirmBtn"]');
 		this.phone = $("#phone").val();
 		this.idCode = $("#idCode").val();
 		this._addEvents();
 	}

 	Pub.prototype = {
 		//初始化
 		_init : function(){

 		},
 		//所有事件绑定
 		_addEvents : function(){
 			var sthis = this;
 			/*验证码输入判断*/
 			this.captchaInput.on('keyup', function(){
 				var idCode_var = this.value;
 				this.value = sthis._fnCaptcha(idCode_var);
 			})
 			/*获取验证码按钮*/
 			this.sendBtn.on('click', function(){
 				var times = 59,
 					send = $(this);
 				sthis._fnObtain(times,send);
 			})
 			/*确定按钮*/
 			this.confirmBtn.on('click', function(){
 				var confirmBtn = $(this);
 				sthis._fnSubmit(confirmBtn);
 			})

 		},
 		/*验证码处理*/
 		_fnCaptcha : function(idCode_var){
 			idCode_var = idCode_var.replace(/\D/g,'');
			var idCode_len = idCode_var.length;
			if(idCode_len==0){
				this.confirmBtn.addClass("disabled");
			}else if(idCode_len==6){
				this.confirmBtn.removeClass("disabled");
			}
			return idCode_var;
 		},
 		_fnObtain : function(times,send){
 			var timer = null;
 			if(send.hasClass("disabled")) return false;
 			send.addClass('disabled');
 			timer = setInterval(function(){
 				send.html("重新获取("+times+")");
 				if(times < 0){
 					send.html("获取验证码");
 					send.removeClass('disabled');
 					times = 59;
 					clearInterval(timer);
 				}
 				times--;
 			},1000);

 			TOOLS.ajax({
 				url:"./index.php?r=loan/sms",
 				data:{mobile:this.phone,code:1},
 				type:"get",
 				dataType:"json",
 				fnSuccess:function(data){
 				},
 				fnError:function(){}
 			});
 		},
 		_fnSubmit : function(confirmBtn){
 			if(confirmBtn.hasClass("disabled")) return false;
 			confirmBtn.addClass('disabled');
 			TOOLS.ajax({
 				url:"./index.php?r=loan/sms",
 				data:{mobile:this.phone,code:this.idCode,type:1},
 				type:"get",
 				dataType:"json",
 				fnSuccess:function(data){
 					console.log(data);
 					if(data.isSuccess == "0"){
 						confirmBtn.removeClass('disabled');
 						MessageBox.alert({type:"common",txt:CS.ERRORMSG["CAPTCHAERROR"]});
 					}else if(data.code == "1"){
 						window.location="./index.php?r=loan/success";
 					}
 				},
 				fnError:function(){}
 			});
 		}
 	}

 	$.fn.PUB = function(options){
 		return this.each(function(){
 			var $this = $(this) ,
 				data = $this.data('PUB');

 			if(!data){
 				$this.data('PUB', (data = new Pub(this, options)))
 			}

 			if(typeof options == 'string') data[options](setData);
 		});
 	}
 })(window.jQuery||window.Zepto)

<commit_msg>UPDATE<commit_after>/**
 *
 * check of identifying code
 *
 *
 *
 * 20150623
 */
 (function($, undefined){
 	//生产类
 	var Pub = function(el, op){
 		this.$el = $(el);
 		this.captchaInput = this.$el.find('[node-type="LoanCaptchaInput"]');
 		this.sendBtn = this.$el.find('[node-type="LoanSendBtn"]');
 		this.confirmBtn = this.$el.find('[node-type="LoanConfirmBtn"]');
 		this.phone = $("#phone").html();
 		this.idCode = $("#idCode").val();
 		this._addEvents();
 	}

 	Pub.prototype = {
 		//初始化
 		_init : function(){

 		},
 		//所有事件绑定
 		_addEvents : function(){
 			var sthis = this;
 			/*验证码输入判断*/
 			this.captchaInput.on('keyup', function(){
 				var idCode_var = this.value;
 				this.value = sthis._fnCaptcha(idCode_var);
 			})
 			/*获取验证码按钮*/
 			this.sendBtn.on('click', function(){
 				var times = 59,
 					send = $(this);
 				sthis._fnObtain(times,send);
 			})
 			/*确定按钮*/
 			this.confirmBtn.on('click', function(){
 				var confirmBtn = $(this);
 				sthis._fnSubmit(confirmBtn);
 			})

 		},
 		/*验证码处理*/
 		_fnCaptcha : function(idCode_var){
 			idCode_var = idCode_var.replace(/\D/g,'');
			var idCode_len = idCode_var.length;
			if(idCode_len==0){
				this.confirmBtn.addClass("disabled");
			}else if(idCode_len==6){
				this.confirmBtn.removeClass("disabled");
			}
			return idCode_var;
 		},
 		_fnObtain : function(times,send){
 			var timer = null;
 			if(send.hasClass("disabled")) return false;
 			send.addClass('disabled');
 			timer = setInterval(function(){
 				send.html("重新获取("+times+")");
 				if(times < 0){
 					send.html("获取验证码");
 					send.removeClass('disabled');
 					times = 59;
 					clearInterval(timer);
 				}
 				times--;
 			},1000);

 			TOOLS.ajax({
 				url:"./index.php?r=loan/sms",
 				data:{mobile:this.phone,code:1},
 				type:"get",
 				dataType:"json",
 				fnSuccess:function(data){
 				},
 				fnError:function(){}
 			});
 		},
 		_fnSubmit : function(confirmBtn){
 			if(confirmBtn.hasClass("disabled")) return false;
 			confirmBtn.addClass('disabled');
 			TOOLS.ajax({
 				url:"./index.php?r=loan/sms",
 				data:{mobile:this.phone,code:this.idCode,type:1},
 				type:"get",
 				dataType:"json",
 				fnSuccess:function(data){
 					console.log(data);
 					if(data.isSuccess == "0"){
 						confirmBtn.removeClass('disabled');
 						MessageBox.alert({type:"common",txt:CS.ERRORMSG["CAPTCHAERROR"]});
 					}else if(data.code == "1"){
 						window.location="./index.php?r=loan/success";
 					}
 				},
 				fnError:function(){}
 			});
 		}
 	}

 	$.fn.PUB = function(options){
 		return this.each(function(){
 			var $this = $(this) ,
 				data = $this.data('PUB');

 			if(!data){
 				$this.data('PUB', (data = new Pub(this, options)))
 			}

 			if(typeof options == 'string') data[options](setData);
 		});
 	}
 })(window.jQuery||window.Zepto)

<|endoftext|>


<commit_before>var _ = require('lodash');
var Promise = require('bluebird');
var logger = require('../Logger');
var dbUtil = require('./DbUtil');

var GET_RECIPIENTS = ' select id,firstname,lastname,city,verified,picture from receipient r';
var GET_RECIPIENTSBYID='select * from receipient r,family f   ';
var INSERT_RECIPIENT='INSERT INTO receipient (firstname, lastname, email, contactnumber, address_line1, address_line2, city, state, pincode, verified,moneyrequired,singleparent,marks,picture) values ' +
    '(:firstname, :lastname, :email, :contactnumber, :address_line1, :address_line2, :city, :state, :pincode, :verified,:moneyrequired,:singleparent,:marks,:picture)';
var PREFIXES = {
    receipient: "r"
};

module.exports = function(dbcp) {
    return {
        getRecipients: _.partial(getAllRecipients, dbcp),
        getRecipientById: _.partial(getRecipientByID,dbcp),
        createReceipient: _.partial(createReceipient,dbcp)
    };
};

function createReceipient(dbcp, options) {

    var orderReqId = 0;
    logger.info("receipient info -------> ", options);

    return new Promise(function(resolve, reject) {
        dbUtil.executeQuery(dbcp, INSERT_RECIPIENT, options)
            .then(function(results) {
                res.json(results);
            })

            .catch(function(err) {
                reject(err);
            });
    });
}

/**
 * Gets All Recipients
 */
function getAllRecipients(dbcp, options) {
    return new Promise(function(resolve, reject) {
        dbUtil.executeQuery(dbcp, getRecipientsQuery(options), options)
            .then(function(rows) {
                resolve( _.pluck(rows, PREFIXES.receipient));
            })
            .catch(function(err) {
                reject(err);
            });
    });
}

function getRecipientByID(dbcp, options) {
    return new Promise(function(resolve, reject) {
        dbUtil.executeQuery(dbcp, getRecipientByIdQuery(options), options)
            .then(function(rows) {
                resolve( _.pluck(rows, PREFIXES.receipient));
            })
            .catch(function(err) {
                reject(err);
            });
    });
}

function getRecipientsQuery(options) {

    var query = GET_RECIPIENTS;
console.log(options+' is options');
    if (!_.isUndefined(options.singleparent||options.moneyrequired||options.city||options.marks)) {
        query += ' where ';
    }
    if (!_.isUndefined(options.singleparent)) {
        query += " r.singleparent = :singleparent";

    }
    if (!_.isUndefined(options.moneyrequired)) {
        query += " and r.moneyrequired <= :moneyrequired";
    }
    if (!_.isUndefined(options.city)) {
        query += " and r.city = :city";
    }
    if (!_.isUndefined(options.marks)) {
        query += " and r.marks > :marks";
    }
    return query;
}

function getRecipientByIdQuery(options){
    var query = GET_RECIPIENTSBYID;

    if (!_.isUndefined(options.id)) {
        query += "where  r.id = :id";
        query += " and r.id = f.receipientid";
    }
    return query;
}
function mapOrderRows(rows, prefixes) {
    var finalResult = _.pluck(rows, prefixes.receipient);
    console.log(JSON.stringify(finalResult));
    return finalResult;

}

<commit_msg>Added reg<commit_after>var _ = require('lodash');
var Promise = require('bluebird');
var logger = require('../Logger');
var dbUtil = require('./DbUtil');

var GET_RECIPIENTS = ' select id,firstname,lastname,city,verified,picture from receipient r';
var GET_RECIPIENTSBYID='select * from receipient r,family f   ';
var INSERT_RECIPIENT='INSERT INTO receipient (firstname, lastname, email, contactnumber, address_line1, address_line2, city, state, pincode, verified,moneyrequired,singleparent,marks,picture) values ' +
    '(:firstname, :lastname, :email, :contactnumber, :address_line1, :address_line2, :city, :state, :pincode, :verified,:moneyrequired,:singleparent,:marks,:picture)';
var PREFIXES = {
    receipient: "r"
};

module.exports = function(dbcp) {
    return {
        getRecipients: _.partial(getAllRecipients, dbcp),
        getRecipientById: _.partial(getRecipientByID,dbcp),
        createReceipient: _.partial(createReceipient,dbcp)
    };
};

function createReceipient(dbcp, options) {

    var orderReqId = 0;
    logger.info("receipient info -------> ", options);

    return new Promise(function(resolve, reject) {
        dbUtil.executeQuery(dbcp, INSERT_RECIPIENT, options)
            .then(function(results) {
                res.json(results);
            })

            .catch(function(err) {
                reject(err);
            });
    });
}

/**
 * Gets All Recipients
 */
function getAllRecipients(dbcp, options) {
    return new Promise(function(resolve, reject) {
        dbUtil.executeQuery(dbcp, getRecipientsQuery(options), options)
            .then(function(rows) {
                resolve( _.pluck(rows, PREFIXES.receipient));
            })
            .catch(function(err) {
                reject(err);
            });
    });
}

function getRecipientByID(dbcp, options) {
    return new Promise(function(resolve, reject) {
        dbUtil.executeQuery(dbcp, getRecipientByIdQuery(options), options)
            .then(function(rows) {
                resolve( _.pluck(rows, PREFIXES.receipient));
            })
            .catch(function(err) {
                reject(err);
            });
    });
}

function getRecipientsQuery(options) {

    var query = GET_RECIPIENTS;
console.log(options+' is options');
    if (!_.isUndefined(options.singleparent||options.moneyrequired||options.city||options.marks)) {
        query += ' where ';
    }
    if (!_.isUndefined(options.singleparent)) {
        query += " r.singleparent = :singleparent";

    }
    if (!_.isUndefined(options.moneyrequired)) {
        query += " and r.moneyrequired <= :moneyrequired";
    }
    if (!_.isUndefined(options.city)) {
        query += " and r.city = :city";
    }
    if (!_.isUndefined(options.marks)) {
        query += " and r.marks > :marks";
    }
    return query;
}

function getRecipientByIdQuery(options){
    var query = GET_RECIPIENTSBYID;

    if (!_.isUndefined(options.id)) {
        query += "where  r.id = :id";
        query += " and r.id = f.receipientid";
    }
    return query;
}
function mapOrderRows(rows, prefixes) {
    var finalResult = _.pluck(rows, prefixes.receipient);
    console.log(JSON.stringify(finalResult));
    return finalResult;

}

    <|endoftext|>


<commit_before>var path = require('path');
var webpack = require('webpack');

var SRC = "./src";
var DEST = "./build";

module.exports = {
  entry: {
    app: [
       path.join(process.cwd(), SRC, '/js/main.js')
    ],
  },
  output: {
    path: path.join(process.cwd(), DEST,'/assets/js'),
    publicPath: '/assets/js/',
    filename: 'main.min.js'
  },
  plugins: [
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin()
  ],
  resolve: {
    extensions: ['', '.js', '.jsx', ]
  },
  debug: false,
  module: {
    loaders: [{
      test: /\.jsx?$/,
      exclude: /node_modules/,
      include: [path.join(process.cwd(), SRC, '/js')],
      loaders: ['react-hot', 'babel?cacheDirectory'],
    }],
  }
};<commit_msg>changed production config<commit_after>var path = require('path');
var webpack = require('webpack');

var SRC = "./src";
var DEST = "./build";

module.exports = {
  entry: {
    app: [
       path.join(process.cwd(), SRC, '/js/main.js')
    ],
  },
  output: {
    path: path.join(process.cwd(), DEST,'/assets/js'),
    publicPath: '/assets/js/',
    filename: 'main.min.js'
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin()
  ],
  resolve: {
    extensions: ['', '.js', '.jsx', ]
  },
  debug: false,
  module: {
    loaders: [{
      test: /\.jsx?$/,
      exclude: /node_modules/,
      include: [path.join(process.cwd(), SRC, '/js')],
      loaders: ['react-hot', 'babel?cacheDirectory'],
    }],
  }
};<|endoftext|>


<commit_before>require('mocha')

var chai = require('chai')
var nock = require('nock')

var assert = chai.assert

var email = 'fake@fakey.nope' // process.env.EMAIL,
var pass = 'secret' // process.env.PASS,
var kidid = '1234567890123456' // process.env.KIDID

var useNock = true
// nock.recorder.rec()
describe('dailyconnect API tests', function () {
  var Api = require('../')
  var api = new Api(email, pass)

  it('No error on successful login (302)', function (done) {
    if (useNock) {
      nock('https://www.dailyconnect.com:443')
        .post('/Cmd?cmd=UserAuth', 'email=fake%40fakey.nope&pass=secret')
        .reply(302, '', {
          expires: 'Thu, 01 Jan 1970 00:00:00 GMT',
          'set-cookie': ['seacloud1=1/uid.123;Path=/'],
          location: 'https://www.dailyconnect.com/home',
          date: 'Fri, 24 Oct 2014 06:31:18 GMT',
          'content-type': 'text/html',
          server: 'Google Frontend',
          'content-length': '0'
        })
    }

    api.login(function (err) {
      if (err) {
        assert.fail(err, true)
      }
      done()
    })
  })

  it('getUserInfo returns user info', function (done) {
    if (useNock) {
      nock('https://www.dailyconnect.com:443')
        .post('/CmdW', 'cmd=UserInfoW')
        .reply(200, {
          'Name': 'John Doe',
          'Ut': 1,
          'Email': 'fake@fakey.nope',
          'created': '5/12/2014',
          'myKids': [{
            'Name': 'Baby Doe',
            'Created': 140512,
            'Contacts': [{
              'phone': '8005551212',
              'name': 'Some name-  home'
            }],
            'PhotoInd': 2,
            'Id': 1234567890123456,
            'Parents': [{
              'Name': 'John Doe',
              'Ut': 1,
              'Email': 'fake@fakey.nope',
              'Id': 6543210987654321
            }, {
              'Name': 'Jane Doe',
              'Ut': 1,
              'Email': 'alsofake@fakey.nope',
              'Id': 1231231231231231
            }, {
              'Name': 'Great Preschool Room 1',
              'Ut': 10,
              'Email': 'fakeschool@fakey.nope',
              'Id': 9879879879879879,
              'cc': [6767676767676767]
            }, {
              'Name': 'Great Preschool-SomeCity',
              'Type': 2,
              'ccId': 6767676767676767,
              'Id': 6767676767676767
            }],
            'Boy': true,
            'Allergies': '',
            'Photo': 5656565656565656,
            'BDay': '10/26/2011'
          }],
          'nws': true,
          'Id': 6543210987654321
        }, {
          expires: 'Thu, 01 Jan 1970 00:00:00 GMT',
          'set-cookie': ['seacloud1=1/uid.123;Path=/'],
          'content-type': 'text/plain; charset=utf-8',
          vary: 'Accept-Encoding',
          date: 'Fri, 24 Oct 2014 06:31:18 GMT',
          server: 'Google Frontend',
          'cache-control': 'private',
          'transfer-encoding': 'chunked'
        })
    }

    api.getUserInfo(function (err, user) {
      if (err) {
        assert.fail(err, true)
        done()
      }
      assert.isObject(user, 'user is not an object')
      assert.equal(user.Email, api.email)
      done()
    })
  })

  it('getKidSummaryByDay returns kid summary', function (done) {
    if (useNock) {
      nock('https://www.dailyconnect.com:443')
        .post('/CmdW', 'cmd=KidGetSummary&Kid=1234567890123456&pdt=141022')
        .reply(200, {
          'summary': {
            'totalSleepDuration': 130,
            'timeOfLastSleeping': '10/22/2014 14:53',
            'kidId': 1234567890123456,
            'timeOfLastFood': '10/22/2014 11:33',
            'nrOfSleep': 1,
            'longuestSleepDuration': 130,
            'day': 141022,
            'timeOfLastDropOff': '10/22/2014 7:45',
            'isSleeping': false
          }
        }, {
          expires: 'Thu, 01 Jan 1970 00:00:00 GMT',
          'set-cookie': ['seacloud1=1/uid.123;Path=/'],
          'content-type': 'text/plain; charset=utf-8',
          vary: 'Accept-Encoding',
          date: 'Fri, 24 Oct 2014 07:00:40 GMT',
          server: 'Google Frontend',
          'cache-control': 'private',
          'transfer-encoding': 'chunked'
        })
    }

    api.getKidSummaryByDay(kidid, new Date(2014, 9, 22), function (err, kidinfo) {
      if (err) {
        assert.fail(err, true)
        done()
      }
      assert.isObject(kidinfo, 'kidinfo is not an object')
      assert.equal(kidid, kidinfo.summary.kidId)
      done()
    })
  })

  it('getKidStatusByDay returns kid daily status', function (done) {
    if (useNock) {
      nock('https://www.dailyconnect.com:443')
        .post('/CmdListW', 'cmd=StatusList&Kid=1234567890123456&pdt=141022&fmt=long')
        .reply(200, {
          'summary': {
            'totalSleepDuration': 130,
            'timeOfLastSleeping': '10/22/2014 14:53',
            'kidId': 1234567890123456,
            'timeOfLastFood': '10/22/2014 11:33',
            'nrOfSleep': 1,
            'longuestSleepDuration': 130,
            'day': 141022,
            'timeOfLastDropOff': '10/22/2014 7:45',
            'isSleeping': false
          },
          'staff': [],
          'users': [{
            'Name': 'Great Preschool Room 1',
            'Ut': 10,
            'Email': 'fakepreschoo@fakey.nope',
            'Id': 9879879879879879,
            'cc': [6767676767676767]
          }],
          'ts': 166481228455,
          'kidId': 1234567890123456,
          'pdt': 141022,
          'list': [{
            'By': 9879879879879879,
            'Kid': 1234567890123456,
            'Ptm': 1010,
            'e': '10/22/2014 09:45',
            'Cat': 101,
            'Txt': 'Baby Doe  is dropped off',
            'ms': 166464259139,
            'lId': 145293057051,
            'Utm': 945,
            'isst': 1,
            'Id': 1509600413,
            'Pdt': 141022
          }, {
            'By': 9879879879879879,
            'Kid': 1234567890123456,
            'Ptm': 1010,
            'e': '10/22/2014 10:01',
            'Cat': 700,
            'Txt': 'Baby Doe  is on the playground ',
            'ms': 166464248036,
            'lId': 145293043669,
            'Utm': 1001,
            'isst': 1,
            'Id': 1496860453,
            'Pdt': 141022
          }, {
            'By': 9879879879879879,
            'Ptm': 1253,
            'e': '10/22/2014 10:50',
            'ms': 166474032684,
            'n': 'Apple bobbing- the kids used tongs to bob for apples. This activity worked on hand-eye coordination and fine motor skills.',
            'lId': 145302826419,
            'isst': 1,
            'Pdt': 141022,
            'Kid': 1234567890123456,
            'Cat': 700,
            'Txt': 'Baby Doe  is working on curriculum ',
            'Utm': 1050,
            'Id': 1466741737
          }, {
            'By': 9879879879879879,
            'Ptm': 1242,
            'e': '10/22/2014 11:33',
            'ms': 166473351097,
            'n': 'Mashed potatoes, chicken gravy, steamed veggies, pears, milk',
            'lId': 145302146106,
            'isst': 1,
            'Pdt': 141022,
            'Kid': 1234567890123456,
            'Cat': 200,
            'Txt': 'Baby Doe  ate Lunch',
            'Utm': 1133,
            'Id': 1472060549
          }, {
            'By': 9879879879879879,
            'Kid': 1234567890123456,
            'Ptm': 1243,
            'e': '10/22/2014 12:43',
            'Cat': 501,
            'Txt': 'Baby Doe  starts sleeping',
            'ms': 166473418212,
            'lId': 145302215272,
            'Utm': 1243,
            'isst': 1,
            'Id': 1515291700,
            'Pdt': 141022
          }, {
            'By': 9879879879879879,
            'Ptm': 1412,
            'e': '10/22/2014 14:12',
            'ms': 166478753386,
            'lId': 145307545600,
            'isst': 1,
            'Pdt': 141022,
            'Photo': 1508541205,
            'Kid': 1234567890123456,
            'Cat': 1000,
            'Txt': 'Photo',
            'Utm': 1412,
            'Id': 1508541205
          }, {
            'By': 9879879879879879,
            'Kid': 1234567890123456,
            'Ptm': 1431,
            'e': '10/22/2014 14:31',
            'Cat': 1100,
            'Txt': 'Please bring more diapers by next Wednesday. ',
            'ms': 166479897971,
            'lId': 145308694731,
            'Utm': 1431,
            'isst': 1,
            'Id': 1466362335,
            'Pdt': 141022
          }, {
            'By': 9879879879879879,
            'Ptm': 1453,
            'd': 130,
            'e': '10/22/2014 14:53',
            'ms': 166481228455,
            'lId': 145310026415,
            'isst': 1,
            'Pdt': 141022,
            'Kid': 1234567890123456,
            'Cat': 502,
            'Txt': 'Baby Doe  napped (2h10m)',
            'Utm': 1243,
            'Id': 1488191935
          }],
          'deleted': [],
          'all': true
        }, {
          expires: 'Thu, 01 Jan 1970 00:00:00 GMT',
          'set-cookie': ['seacloud1=1/uid.123;Path=/'],
          'content-type': 'text/plain; charset=utf-8',
          vary: 'Accept-Encoding',
          date: 'Fri, 24 Oct 2014 07:00:41 GMT',
          server: 'Google Frontend',
          'cache-control': 'private',
          'transfer-encoding': 'chunked'
        })
    }

    api.getKidStatusByDay(kidid, new Date(2014, 9, 22), function (err, status) {
      if (err) {
        assert.fail(err, true)
        done()
      }
      assert.isObject(status, 'status is not an object')
      assert.equal(kidid, status.kidId)
      assert.equal(true, status.all)
      done()
    })
  })

  it('getPhoto returns buffer with type property', function (done) {
    if (useNock) {
      nock('https://www.dailyconnect.com:443')
        .get('/GetCmd?cmd=PhotoGet&id=12345&thumb=0')
        .reply(200, '89504e470d0a1a0a0000000trucated-for-sanity', {
          'content-type': 'image/jpg',
          'content-disposition': 'inline; filename=\"Photo.jpg\"',
          date: 'Fri, 24 Oct 2014 07:17:59 GMT',
          server: 'Google Frontend',
          'content-length': '4415'
        })
    }
    api.getPhoto(12345, function (err, photo) {
      if (err) {
        console.log(err)
        assert.fail(err, true)
        done()
      }
      assert.equal(photo.type, 'image/jpg')
      done()
    })
  })
})
<commit_msg>add globals to test-index.js<commit_after>/* global describe, it */
require('mocha')

var chai = require('chai')
var nock = require('nock')

var assert = chai.assert

var email = 'fake@fakey.nope' // process.env.EMAIL,
var pass = 'secret' // process.env.PASS,
var kidid = '1234567890123456' // process.env.KIDID

var useNock = true
// nock.recorder.rec()
describe('dailyconnect API tests', function () {
  var Api = require('../')
  var api = new Api(email, pass)

  it('No error on successful login (302)', function (done) {
    if (useNock) {
      nock('https://www.dailyconnect.com:443')
        .post('/Cmd?cmd=UserAuth', 'email=fake%40fakey.nope&pass=secret')
        .reply(302, '', {
          expires: 'Thu, 01 Jan 1970 00:00:00 GMT',
          'set-cookie': ['seacloud1=1/uid.123;Path=/'],
          location: 'https://www.dailyconnect.com/home',
          date: 'Fri, 24 Oct 2014 06:31:18 GMT',
          'content-type': 'text/html',
          server: 'Google Frontend',
          'content-length': '0'
        })
    }

    api.login(function (err) {
      if (err) {
        assert.fail(err, true)
      }
      done()
    })
  })

  it('getUserInfo returns user info', function (done) {
    if (useNock) {
      nock('https://www.dailyconnect.com:443')
        .post('/CmdW', 'cmd=UserInfoW')
        .reply(200, {
          'Name': 'John Doe',
          'Ut': 1,
          'Email': 'fake@fakey.nope',
          'created': '5/12/2014',
          'myKids': [{
            'Name': 'Baby Doe',
            'Created': 140512,
            'Contacts': [{
              'phone': '8005551212',
              'name': 'Some name-  home'
            }],
            'PhotoInd': 2,
            'Id': 1234567890123456,
            'Parents': [{
              'Name': 'John Doe',
              'Ut': 1,
              'Email': 'fake@fakey.nope',
              'Id': 6543210987654321
            }, {
              'Name': 'Jane Doe',
              'Ut': 1,
              'Email': 'alsofake@fakey.nope',
              'Id': 1231231231231231
            }, {
              'Name': 'Great Preschool Room 1',
              'Ut': 10,
              'Email': 'fakeschool@fakey.nope',
              'Id': 9879879879879879,
              'cc': [6767676767676767]
            }, {
              'Name': 'Great Preschool-SomeCity',
              'Type': 2,
              'ccId': 6767676767676767,
              'Id': 6767676767676767
            }],
            'Boy': true,
            'Allergies': '',
            'Photo': 5656565656565656,
            'BDay': '10/26/2011'
          }],
          'nws': true,
          'Id': 6543210987654321
        }, {
          expires: 'Thu, 01 Jan 1970 00:00:00 GMT',
          'set-cookie': ['seacloud1=1/uid.123;Path=/'],
          'content-type': 'text/plain; charset=utf-8',
          vary: 'Accept-Encoding',
          date: 'Fri, 24 Oct 2014 06:31:18 GMT',
          server: 'Google Frontend',
          'cache-control': 'private',
          'transfer-encoding': 'chunked'
        })
    }

    api.getUserInfo(function (err, user) {
      if (err) {
        assert.fail(err, true)
        done()
      }
      assert.isObject(user, 'user is not an object')
      assert.equal(user.Email, api.email)
      done()
    })
  })

  it('getKidSummaryByDay returns kid summary', function (done) {
    if (useNock) {
      nock('https://www.dailyconnect.com:443')
        .post('/CmdW', 'cmd=KidGetSummary&Kid=1234567890123456&pdt=141022')
        .reply(200, {
          'summary': {
            'totalSleepDuration': 130,
            'timeOfLastSleeping': '10/22/2014 14:53',
            'kidId': 1234567890123456,
            'timeOfLastFood': '10/22/2014 11:33',
            'nrOfSleep': 1,
            'longuestSleepDuration': 130,
            'day': 141022,
            'timeOfLastDropOff': '10/22/2014 7:45',
            'isSleeping': false
          }
        }, {
          expires: 'Thu, 01 Jan 1970 00:00:00 GMT',
          'set-cookie': ['seacloud1=1/uid.123;Path=/'],
          'content-type': 'text/plain; charset=utf-8',
          vary: 'Accept-Encoding',
          date: 'Fri, 24 Oct 2014 07:00:40 GMT',
          server: 'Google Frontend',
          'cache-control': 'private',
          'transfer-encoding': 'chunked'
        })
    }

    api.getKidSummaryByDay(kidid, new Date(2014, 9, 22), function (err, kidinfo) {
      if (err) {
        assert.fail(err, true)
        done()
      }
      assert.isObject(kidinfo, 'kidinfo is not an object')
      assert.equal(kidid, kidinfo.summary.kidId)
      done()
    })
  })

  it('getKidStatusByDay returns kid daily status', function (done) {
    if (useNock) {
      nock('https://www.dailyconnect.com:443')
        .post('/CmdListW', 'cmd=StatusList&Kid=1234567890123456&pdt=141022&fmt=long')
        .reply(200, {
          'summary': {
            'totalSleepDuration': 130,
            'timeOfLastSleeping': '10/22/2014 14:53',
            'kidId': 1234567890123456,
            'timeOfLastFood': '10/22/2014 11:33',
            'nrOfSleep': 1,
            'longuestSleepDuration': 130,
            'day': 141022,
            'timeOfLastDropOff': '10/22/2014 7:45',
            'isSleeping': false
          },
          'staff': [],
          'users': [{
            'Name': 'Great Preschool Room 1',
            'Ut': 10,
            'Email': 'fakepreschoo@fakey.nope',
            'Id': 9879879879879879,
            'cc': [6767676767676767]
          }],
          'ts': 166481228455,
          'kidId': 1234567890123456,
          'pdt': 141022,
          'list': [{
            'By': 9879879879879879,
            'Kid': 1234567890123456,
            'Ptm': 1010,
            'e': '10/22/2014 09:45',
            'Cat': 101,
            'Txt': 'Baby Doe  is dropped off',
            'ms': 166464259139,
            'lId': 145293057051,
            'Utm': 945,
            'isst': 1,
            'Id': 1509600413,
            'Pdt': 141022
          }, {
            'By': 9879879879879879,
            'Kid': 1234567890123456,
            'Ptm': 1010,
            'e': '10/22/2014 10:01',
            'Cat': 700,
            'Txt': 'Baby Doe  is on the playground ',
            'ms': 166464248036,
            'lId': 145293043669,
            'Utm': 1001,
            'isst': 1,
            'Id': 1496860453,
            'Pdt': 141022
          }, {
            'By': 9879879879879879,
            'Ptm': 1253,
            'e': '10/22/2014 10:50',
            'ms': 166474032684,
            'n': 'Apple bobbing- the kids used tongs to bob for apples. This activity worked on hand-eye coordination and fine motor skills.',
            'lId': 145302826419,
            'isst': 1,
            'Pdt': 141022,
            'Kid': 1234567890123456,
            'Cat': 700,
            'Txt': 'Baby Doe  is working on curriculum ',
            'Utm': 1050,
            'Id': 1466741737
          }, {
            'By': 9879879879879879,
            'Ptm': 1242,
            'e': '10/22/2014 11:33',
            'ms': 166473351097,
            'n': 'Mashed potatoes, chicken gravy, steamed veggies, pears, milk',
            'lId': 145302146106,
            'isst': 1,
            'Pdt': 141022,
            'Kid': 1234567890123456,
            'Cat': 200,
            'Txt': 'Baby Doe  ate Lunch',
            'Utm': 1133,
            'Id': 1472060549
          }, {
            'By': 9879879879879879,
            'Kid': 1234567890123456,
            'Ptm': 1243,
            'e': '10/22/2014 12:43',
            'Cat': 501,
            'Txt': 'Baby Doe  starts sleeping',
            'ms': 166473418212,
            'lId': 145302215272,
            'Utm': 1243,
            'isst': 1,
            'Id': 1515291700,
            'Pdt': 141022
          }, {
            'By': 9879879879879879,
            'Ptm': 1412,
            'e': '10/22/2014 14:12',
            'ms': 166478753386,
            'lId': 145307545600,
            'isst': 1,
            'Pdt': 141022,
            'Photo': 1508541205,
            'Kid': 1234567890123456,
            'Cat': 1000,
            'Txt': 'Photo',
            'Utm': 1412,
            'Id': 1508541205
          }, {
            'By': 9879879879879879,
            'Kid': 1234567890123456,
            'Ptm': 1431,
            'e': '10/22/2014 14:31',
            'Cat': 1100,
            'Txt': 'Please bring more diapers by next Wednesday. ',
            'ms': 166479897971,
            'lId': 145308694731,
            'Utm': 1431,
            'isst': 1,
            'Id': 1466362335,
            'Pdt': 141022
          }, {
            'By': 9879879879879879,
            'Ptm': 1453,
            'd': 130,
            'e': '10/22/2014 14:53',
            'ms': 166481228455,
            'lId': 145310026415,
            'isst': 1,
            'Pdt': 141022,
            'Kid': 1234567890123456,
            'Cat': 502,
            'Txt': 'Baby Doe  napped (2h10m)',
            'Utm': 1243,
            'Id': 1488191935
          }],
          'deleted': [],
          'all': true
        }, {
          expires: 'Thu, 01 Jan 1970 00:00:00 GMT',
          'set-cookie': ['seacloud1=1/uid.123;Path=/'],
          'content-type': 'text/plain; charset=utf-8',
          vary: 'Accept-Encoding',
          date: 'Fri, 24 Oct 2014 07:00:41 GMT',
          server: 'Google Frontend',
          'cache-control': 'private',
          'transfer-encoding': 'chunked'
        })
    }

    api.getKidStatusByDay(kidid, new Date(2014, 9, 22), function (err, status) {
      if (err) {
        assert.fail(err, true)
        done()
      }
      assert.isObject(status, 'status is not an object')
      assert.equal(kidid, status.kidId)
      assert.equal(true, status.all)
      done()
    })
  })

  it('getPhoto returns buffer with type property', function (done) {
    if (useNock) {
      nock('https://www.dailyconnect.com:443')
        .get('/GetCmd?cmd=PhotoGet&id=12345&thumb=0')
        .reply(200, '89504e470d0a1a0a0000000trucated-for-sanity', {
          'content-type': 'image/jpg',
          'content-disposition': 'inline; filename=\"Photo.jpg\"',
          date: 'Fri, 24 Oct 2014 07:17:59 GMT',
          server: 'Google Frontend',
          'content-length': '4415'
        })
    }
    api.getPhoto(12345, function (err, photo) {
      if (err) {
        console.log(err)
        assert.fail(err, true)
        done()
      }
      assert.equal(photo.type, 'image/jpg')
      done()
    })
  })
})
<|endoftext|>


<commit_before>var should 		= require( 'should' );
var _ 			= require( 'underscore' );

var testConfig  = require( './config' );
var rongSDK 	= require( '../index' );

var groupIDs 	= _.keys( testConfig.group.groupIdNamePairs );
var groupNames 	= _.values( testConfig.group.groupIdNamePairs );

describe( 'Group Test', function() {
	before( function( done ) {
		// Init the SDK before testing.
		rongSDK.init( testConfig.appKey, testConfig.appSecret );
		done();
	} );

	after( function( done ) {
		done();
	} );

	describe( 'Sync Group', function() {
		it( 'Synchornize group: should return OK', function( done ) {
			rongSDK.group.sync( testConfig.group.userId, testConfig.group.groupIdNamePairs, function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );
	} );

	describe( 'Quit Group', function() {
		it( 'Quit group: should return OK', function( done ) {
			rongSDK.group.quit( testConfig.group.userId, groupIDs[0], function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );
	} );

	describe( 'Create/Join Group', function() {
		it( 'Create/Join group with a single user: should return OK', function( done ) {
			rongSDK.group.create( testConfig.group.userId, 'MyGroupID', 'MyGroupName' , function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );

		it( 'Create/Join group with multiple users: should return OK', function( done ) {
			rongSDK.group.join( [ testConfig.message.fromUserId, testConfig.message.toUserId ], 'MyGroupID', 'MyGroupName' , function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );
	} );

	describe( 'Dissmiss Group', function() {
		it( 'Dismiss group(' + groupNames[0] + '): should return OK', function( done ) {
			rongSDK.group.quit( testConfig.group.userId, groupIDs[0], function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );

		it( 'Dismiss group(' + groupNames[1] + '): should return OK', function( done ) {
			rongSDK.group.quit( testConfig.group.userId, groupIDs[1], function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );

		it( 'Dismiss group(MyGroupName): should return OK', function( done ) {
			rongSDK.group.quit( testConfig.group.userId, 'MyGroupID', function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );

	} );

} );<commit_msg>Add the testing on refresh group API. Fix the wrongg ref in dismiss group API<commit_after>var should 		= require( 'should' );
var _ 			= require( 'underscore' );

var testConfig  = require( './config' );
var rongSDK 	= require( '../index' );

var groupIDs 	= _.keys( testConfig.group.groupIdNamePairs );
var groupNames 	= _.values( testConfig.group.groupIdNamePairs );

describe( 'Group Test', function() {
	before( function( done ) {
		// Init the SDK before testing.
		rongSDK.init( testConfig.appKey, testConfig.appSecret );
		done();
	} );

	after( function( done ) {
		done();
	} );

	describe( 'Sync Group', function() {
		it( 'Synchornize group: should return OK', function( done ) {
			rongSDK.group.sync( testConfig.group.userId, testConfig.group.groupIdNamePairs, function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );
	} );

	describe( 'Quit Group', function() {
		it( 'Quit group: should return OK', function( done ) {
			rongSDK.group.quit( testConfig.group.userId, groupIDs[0], function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );
	} );

	describe( 'Create/Join Group', function() {
		it( 'Create/Join group with a single user: should return OK', function( done ) {
			rongSDK.group.create( testConfig.group.userId, 'MyGroupID', 'MyGroupName' , function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );

		it( 'Create/Join group with multiple users: should return OK', function( done ) {
			rongSDK.group.join( [ testConfig.message.fromUserId, testConfig.message.toUserId ], 'MyGroupID', 'MyGroupName' , function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );
	} );

	describe( 'Dissmiss Group', function() {
		it( 'Dismiss group(' + groupNames[0] + '): should return OK', function( done ) {
			rongSDK.group.dismiss( testConfig.group.userId, groupIDs[0], function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );

		it( 'Dismiss group(' + groupNames[1] + '): should return OK', function( done ) {
			rongSDK.group.dismiss( testConfig.group.userId, groupIDs[1], function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );

		it( 'Dismiss group(MyGroupName): should return OK', function( done ) {
			rongSDK.group.dismiss( testConfig.group.userId, 'MyGroupID', function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );

	} );

	describe( 'Refresh Group', function() {
    var newGroupName = groupNames[0];
		it( 'Refresh group(' + groupNames[0] + ') to be ' + newGroupName + ': should return OK', function( done ) {
			rongSDK.group.refresh( groupIDs[0], newGroupName, function( err, resultText ) {
				should.not.exists( err );
				var result = JSON.parse( resultText );
				result.code.should.equal( 200 );
				done();
			} );
		} );
  });

} );
<|endoftext|>


<commit_before>/**
 * The grapefruit math library, used to abstract commonly used math operations
 *
 * @class math
 * @extends Object
 * @namespace gf
 */
var math = module.exports = {
    DEG_TO_RAD: Math.PI / 180,
    RAD_TO_DEG: 180 / Math.PI,
    SEED: Math.random(),
    /**
     * Alias some native functions for great justice (or incase we want to override)
     *
     */
    /**
     * Applys a Floor operation to a value, currently uses native Math.floor
     * since it deals with all edge cases
     *
     * @method floor
     * @param num {Number} The number to floor
     * @return {Number} The floored value
     */
    floor: Math.floor,
    /**
     * Applys a Ceiling operation to a value, currently uses native Math.ceil
     * since it deals with all edge cases
     *
     * @method ceil
     * @param num {Number} The number to ceil
     * @return {Number} The ceiling value
     */
    ceil: Math.ceil,
    /**
     * Generates a random number between 0 and 1, NON DETERMINISTIC
     *
     * @method random
     * @return {Number} The random value
     */
    random: Math.random,
    /**
     * Returns the absolute value of a number, currently uses native Math.abs
     * since it is more performant than tricks you can use.
     * see:
     *      http://jsperf.com/math-abs-vs-bitwise/7
     *      http://jsperf.com/abs-value
     *      http://jsperf.com/math-abs-vs-bitwise/3
     *
     * @method abs
     * @param num {Number} The number to get the absolute value for
     * @return {Number} The absolute value
     */
    abs: Math.abs,
    /**
     * Returns the square root of a number, currently uses native Math.sqrt
     *
     * @method sqrt
     * @param num {Number} The number to get the sqrt of
     * @return {Number} The sqrt value
     */
    sqrt: Math.sqrt,
    /**
     * Returns the min of the values passed
     *
     * @method min
     * @param num* {Number...}
     * @return {Number} The min value
     */
    min: Math.min,
    /**
     * Returns the max of the values passed
     *
     * @method max
     * @param num* {Number...}
     * @return {Number} The max value
     */
    max: Math.max,
    /**
     * Quickly rounds a number. This is about twice as fast as Math.round()
     *
     * @method round
     * @param num {Number} The number to round
     * @return {Number} The rounded value
     */
    round: function(n) {
        return ~~(n + (n > 0 ? 0.5 : -0.5));
    },
    /**
     * Clamps a number between two values.
     *
     * @method clamp
     * @param num {Number} The number to clamp
     * @param min {Number} The minimum value the number is allowed to be
     * @param max {Number} The maximum value the number is allowed to be
     * @return {Number} The clamped value
     */
    clamp: function(n, min, max) {
        return Math.max(min, Math.min(max, n));
    },
    /**
     * Truncates the decimal from a number
     *
     * @method truncate
     * @param num {Number} The number to truncate
     * @return {Number} The truncated value
     */
    truncate: function(n) {
        return (n > 0) ? Math.floor(n) : Math.ceil(n);
    },
    /**
     * Snaps a number to a grid value.
     * For example, if you have a grid with gaps the size of 10 horizontally, and
     * a position of 11, it would snap to 10; a position of 18 would snap to 20
     *
     * @method snap
     * @param num {Number} The number to snap
     * @param gap {Number} The gap size of the grid (the tile size)
     * @param [offset=0] {Number} The starting offset of a grid slice (aka tile)
     * @return {Number} The snapped value
     */
    snap: function(n, gap, offset) {
        if(gap === 0) return n;

        offset = offset || 0;

        n -= offset;
        n = gap * Math.round(n / gap);

        return offset + n;
    },
    /**
     * Snaps a number to a grid value, using floor.
     * For example, if you have a grid with gaps the size of 10 horizontally, and
     * a position of 11, it would snap to 10; a position of 18 would also snap to 10
     *
     * @method snapFloor
     * @param num {Number} The number to snap
     * @param gap {Number} The gap size of the grid (the tile size)
     * @param [offset=0] {Number} The starting offset of a grid slice (aka tile)
     * @return {Number} The snapped value
     */
    snapFloor: function(n, gap, offset) {
        if(gap === 0) return n;

        offset = offset || 0;

        n -= offset;
        n = gap * Math.floor(n / gap);

        return offset + n;
    },
    /**
     * Snaps a number to a grid value, using ceiling.
     * For example, if you have a grid with gaps the size of 10 horizontally, and
     * a position of 11, it would snap to 20; a position of 18 would also snap to 20
     *
     * @method snapCeil
     * @param num {Number} The number to snap
     * @param gap {Number} The gap size of the grid (the tile size)
     * @param [offset=0] {Number} The starting offset of a grid slice (aka tile)
     * @return {Number} The snapped value
     */
    snapCeil: function(n, gap, offset) {
        if(gap === 0) return n;

        offset = offset || 0;

        n -= offset;
        n = gap * Math.ceil(n / gap);

        return offset + n;
    },
    /**
     * Convert radians to degrees
     *
     * @method radiansToDegrees
     * @param angle {Number} The angle in radians to convert
     * @return {Number} The angle in degrees
     */
    radiansToDegrees: function(angle) {
        return angle * math.RAD_TO_DEG;
    },
    /**
     * Convert radians to degrees
     *
     * @method degreesToRadians
     * @param angle {Number} The angle in degrees to convert
     * @return {Number} The angle in radians
     */
    degreesToRadians: function(angle) {
        return angle * math.DEG_TO_RAD;
    },
    /**
     * Calculates the angle between two points
     *
     * @method angleBetween
     * @param pos1 {Vector|Point} The first position
     * @param pos2 {Vector|Point} The second position
     * @return {Number} The angle in radians
     */
    angleBetween: function(pos1, pos2) {
        return Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x);
    },
    /**
     * Returns a random boolean based on the provided chance. The chance represents the
     * percentage chance of returning: true.
     *
     * @method randomBool
     * @param chance {Number} The % chance of getting true (0 - 100), defaults to 50%
     * @return {Boolean}
     */
    randomBool: function(chance) {
        if(chance === undefined)
            chance = 50;

        //no chance of true
        if(chance <= 0)
            return false;

        //must always be true
        if(chance >= 100)
            return true;

        //if roll is less than change, return true
        return (Math.random() * 100 < chance);
    },
    /**
     * Returns a random int between min and max.
     *
     * @method randomInt
     * @param min {Number} The minimun number that the result can be
     * @param max {Number} The maximun number that the result can be
     * @return {Number}
     */
    randomInt: function(min, max) {
        if(min === max)
            return min;

        return Math.floor(Math.random() * (max - min + 1) + min);
    },
    /**
     * Returns a random real number between min and max.
     *
     * @method randomReal
     * @param min {Number} The minimun number that the result can be
     * @param max {Number} The maximun number that the result can be
     * @return {Number}
     */
    randomReal: function(min, max) {
        if(min === max)
            return min;

        return math.random() * (max - min + 1) + min;
    },
    /**
     * Returns a random sign based on the provided chance. The chance represents the
     * percentage chance of returning 1 (positive).
     *
     * @method randomSign
     * @param chance {Number} The % chance of getting positive (0 - 100), defaults to 50%
     * @return {Number} either 1 or -1
     */
    randomSign: function(chance) {
        return math.randomBool(chance) ? 1 : -1;
    },
    /**
     * Returns a random string based on a random value between 0 and 1, multiplied
     * by the current date. Ex: "1158014093337", "86371874178", etc
     *
     * @method randomString
     * @return {String} A random string
     */
    randomString: function() {
        return Math.floor(Date.now() * Math.random()).toString();
    },
    /**
     * Generates a random RFC4122 compliant (v4) UUID
     *
     * @method randomUuid
     * @return {String} A random guid
     */
    randomUuid: function() {
        //collect some random bytes
        var buf = math.randomBytes(math.__uuidBytes);

        // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
        buf[6] = (buf[6] & 0x0f) | 0x40;
        buf[8] = (buf[8] & 0x3f) | 0x80;

        var i = 0,
            bth = math.__byteToHex;

        //convert bytes to string
        return bth[buf[i++]] + bth[buf[i++]] +
                bth[buf[i++]] + bth[buf[i++]] + '-' +
                bth[buf[i++]] + bth[buf[i++]] + '-' +
                bth[buf[i++]] + bth[buf[i++]] + '-' +
                bth[buf[i++]] + bth[buf[i++]] + '-' +
                bth[buf[i++]] + bth[buf[i++]] +
                bth[buf[i++]] + bth[buf[i++]] +
                bth[buf[i++]] + bth[buf[i++]];
    },
    __uuidBytes: new Uint8Array(16),
    __byteToHex: (function() {
        var bth = [],
            htb = {};
        for (var i = 0; i < 256; i++) {
            bth[i] = (i + 0x100).toString(16).substr(1);
            htb[bth[i]] = i;
        }

        return bth;
    })(),
    /**
     * Fills a Typed Array with random bytes. If you do not pass an output param, then a default
     * Uint8Array(16) is created and returned for you.
     *
     * @method randomBytes
     * @param [output] {TypedArray} The output array for the random data, if none specified a new Uint8Array(16) is created
     */
    randomBytes: (function() {
        if(crypto && crypto.getRandomValues) {
            // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
            // Moderately fast, high quality.
            return function(ary) {
                ary = ary || new Uint8Array(16);
                crypto.getRandomValues(ary);
                return ary;
            };
        } else {
            // Math.random()-based (RNG)
            // It's fast, but is of unspecified quality.
            return function(ary) {
                ary = ary || new Uint8Array(16);

                //get a view into the buffer that we *know* is Uint8
                var buf = ary.buffer,
                    len = buf.byteLength,
                    view = new Uint8Array(buf);

                //fill the array one random byte at a time
                for(var i = 0, r; i < len; ++i) {
                    //we only need a new random when we have pulled all the bytes out of it
                    if((i & 0x03) === 0) {
                        r = Math.random() * 0x100000000;
                    }

                    view[i] = r >>> ((i & 0x03) << 3) & 0xff;
                }

                //return the original view which now has the data we put into the buffer
                return ary;
            };
        }
    })(),
    /**
     * Returns a random element of an array.
     *
     * @method randomElement
     * @param array {Array} The array to choose from
     * @param start {Number} The index of the first element to include, defaults to 0
     * @param end {Number} The index of the last element to include, defaults to array.length - 1
     * @return {Number} either 1 or -1
     */
    randomElement: function(array, start, end) {
        //ensure we have an array, and there are elements to check
        if(!array || !array.length)
            return null;

        //special case for 1 element
        if(array.length === 1)
            return array[0];

        //default for start
        if(!start || start < 0)
            start = start || 0;

        //default for end
        if(!end || end < 0)
            end = array.length - 1;

        return array[math.randomInt(start, end)];
    }
};
<commit_msg>using wrong math there<commit_after>/**
 * The grapefruit math library, used to abstract commonly used math operations
 *
 * @class math
 * @extends Object
 * @namespace gf
 */
var math = module.exports = {
    DEG_TO_RAD: Math.PI / 180,
    RAD_TO_DEG: 180 / Math.PI,
    SEED: Math.random(),
    /**
     * Alias some native functions for great justice (or incase we want to override)
     *
     */
    /**
     * Applys a Floor operation to a value, currently uses native Math.floor
     * since it deals with all edge cases
     *
     * @method floor
     * @param num {Number} The number to floor
     * @return {Number} The floored value
     */
    floor: Math.floor,
    /**
     * Applys a Ceiling operation to a value, currently uses native Math.ceil
     * since it deals with all edge cases
     *
     * @method ceil
     * @param num {Number} The number to ceil
     * @return {Number} The ceiling value
     */
    ceil: Math.ceil,
    /**
     * Generates a random number between 0 and 1, NON DETERMINISTIC
     *
     * @method random
     * @return {Number} The random value
     */
    random: Math.random,
    /**
     * Returns the absolute value of a number, currently uses native Math.abs
     * since it is more performant than tricks you can use.
     * see:
     *      http://jsperf.com/math-abs-vs-bitwise/7
     *      http://jsperf.com/abs-value
     *      http://jsperf.com/math-abs-vs-bitwise/3
     *
     * @method abs
     * @param num {Number} The number to get the absolute value for
     * @return {Number} The absolute value
     */
    abs: Math.abs,
    /**
     * Returns the square root of a number, currently uses native Math.sqrt
     *
     * @method sqrt
     * @param num {Number} The number to get the sqrt of
     * @return {Number} The sqrt value
     */
    sqrt: Math.sqrt,
    /**
     * Returns the min of the values passed
     *
     * @method min
     * @param num* {Number...}
     * @return {Number} The min value
     */
    min: Math.min,
    /**
     * Returns the max of the values passed
     *
     * @method max
     * @param num* {Number...}
     * @return {Number} The max value
     */
    max: Math.max,
    /**
     * Quickly rounds a number. This is about twice as fast as Math.round()
     *
     * @method round
     * @param num {Number} The number to round
     * @return {Number} The rounded value
     */
    round: function(n) {
        return ~~(n + (n > 0 ? 0.5 : -0.5));
    },
    /**
     * Clamps a number between two values.
     *
     * @method clamp
     * @param num {Number} The number to clamp
     * @param min {Number} The minimum value the number is allowed to be
     * @param max {Number} The maximum value the number is allowed to be
     * @return {Number} The clamped value
     */
    clamp: function(n, min, max) {
        return Math.max(min, Math.min(max, n));
    },
    /**
     * Truncates the decimal from a number
     *
     * @method truncate
     * @param num {Number} The number to truncate
     * @return {Number} The truncated value
     */
    truncate: function(n) {
        return (n > 0) ? Math.floor(n) : Math.ceil(n);
    },
    /**
     * Snaps a number to a grid value.
     * For example, if you have a grid with gaps the size of 10 horizontally, and
     * a position of 11, it would snap to 10; a position of 18 would snap to 20
     *
     * @method snap
     * @param num {Number} The number to snap
     * @param gap {Number} The gap size of the grid (the tile size)
     * @param [offset=0] {Number} The starting offset of a grid slice (aka tile)
     * @return {Number} The snapped value
     */
    snap: function(n, gap, offset) {
        if(gap === 0) return n;

        offset = offset || 0;

        n -= offset;
        n = gap * Math.round(n / gap);

        return offset + n;
    },
    /**
     * Snaps a number to a grid value, using floor.
     * For example, if you have a grid with gaps the size of 10 horizontally, and
     * a position of 11, it would snap to 10; a position of 18 would also snap to 10
     *
     * @method snapFloor
     * @param num {Number} The number to snap
     * @param gap {Number} The gap size of the grid (the tile size)
     * @param [offset=0] {Number} The starting offset of a grid slice (aka tile)
     * @return {Number} The snapped value
     */
    snapFloor: function(n, gap, offset) {
        if(gap === 0) return n;

        offset = offset || 0;

        n -= offset;
        n = gap * Math.floor(n / gap);

        return offset + n;
    },
    /**
     * Snaps a number to a grid value, using ceiling.
     * For example, if you have a grid with gaps the size of 10 horizontally, and
     * a position of 11, it would snap to 20; a position of 18 would also snap to 20
     *
     * @method snapCeil
     * @param num {Number} The number to snap
     * @param gap {Number} The gap size of the grid (the tile size)
     * @param [offset=0] {Number} The starting offset of a grid slice (aka tile)
     * @return {Number} The snapped value
     */
    snapCeil: function(n, gap, offset) {
        if(gap === 0) return n;

        offset = offset || 0;

        n -= offset;
        n = gap * Math.ceil(n / gap);

        return offset + n;
    },
    /**
     * Convert radians to degrees
     *
     * @method radiansToDegrees
     * @param angle {Number} The angle in radians to convert
     * @return {Number} The angle in degrees
     */
    radiansToDegrees: function(angle) {
        return angle * math.RAD_TO_DEG;
    },
    /**
     * Convert radians to degrees
     *
     * @method degreesToRadians
     * @param angle {Number} The angle in degrees to convert
     * @return {Number} The angle in radians
     */
    degreesToRadians: function(angle) {
        return angle * math.DEG_TO_RAD;
    },
    /**
     * Calculates the angle between two points
     *
     * @method angleBetween
     * @param pos1 {Vector|Point} The first position
     * @param pos2 {Vector|Point} The second position
     * @return {Number} The angle in radians
     */
    angleBetween: function(pos1, pos2) {
        return Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x);
    },
    /**
     * Returns a random boolean based on the provided chance. The chance represents the
     * percentage chance of returning: true.
     *
     * @method randomBool
     * @param chance {Number} The % chance of getting true (0 - 100), defaults to 50%
     * @return {Boolean}
     */
    randomBool: function(chance) {
        if(chance === undefined)
            chance = 50;

        //no chance of true
        if(chance <= 0)
            return false;

        //must always be true
        if(chance >= 100)
            return true;

        //if roll is less than change, return true
        return (Math.random() * 100 < chance);
    },
    /**
     * Returns a random int between min and max.
     *
     * @method randomInt
     * @param min {Number} The minimun number that the result can be
     * @param max {Number} The maximun number that the result can be
     * @return {Number}
     */
    randomInt: function(min, max) {
        if(min === max)
            return min;

        return Math.floor(Math.random() * (max - min + 1) + min);
    },
    /**
     * Returns a random real number between min and max.
     *
     * @method randomReal
     * @param min {Number} The minimun number that the result can be
     * @param max {Number} The maximun number that the result can be
     * @return {Number}
     */
    randomReal: function(min, max) {
        if(min === max)
            return min;

        return math.random() * (max - min + 1) + min;
    },
    /**
     * Returns a random sign based on the provided chance. The chance represents the
     * percentage chance of returning 1 (positive).
     *
     * @method randomSign
     * @param chance {Number} The % chance of getting positive (0 - 100), defaults to 50%
     * @return {Number} either 1 or -1
     */
    randomSign: function(chance) {
        return math.randomBool(chance) ? 1 : -1;
    },
    /**
     * Returns a random string based on a random value between 0 and 1, multiplied
     * by the current date. Ex: "1158014093337", "86371874178", etc
     *
     * @method randomString
     * @return {String} A random string
     */
    randomString: function() {
        return Math.floor(Date.now() * Math.random()).toString();
    },
    /**
     * Generates a random RFC4122 compliant (v4) UUID
     *
     * @method randomUuid
     * @return {String} A random guid
     */
    randomUuid: function() {
        //collect some random bytes
        var buf = math.randomBytes(math.__uuidBytes);

        // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
        buf[6] = (buf[6] & 0x0f) | 0x40;
        buf[8] = (buf[8] & 0x3f) | 0x80;

        var i = 0,
            bth = math.__byteToHex;

        //convert bytes to string
        return bth[buf[i++]] + bth[buf[i++]] +
                bth[buf[i++]] + bth[buf[i++]] + '-' +
                bth[buf[i++]] + bth[buf[i++]] + '-' +
                bth[buf[i++]] + bth[buf[i++]] + '-' +
                bth[buf[i++]] + bth[buf[i++]] + '-' +
                bth[buf[i++]] + bth[buf[i++]] +
                bth[buf[i++]] + bth[buf[i++]] +
                bth[buf[i++]] + bth[buf[i++]];
    },
    __uuidBytes: new Uint8Array(16),
    __byteToHex: (function() {
        var bth = [],
            htb = {};
        for (var i = 0; i < 256; i++) {
            bth[i] = (i + 0x100).toString(16).substr(1);
            htb[bth[i]] = i;
        }

        return bth;
    })(),
    /**
     * Fills a Typed Array with random bytes. If you do not pass an output param, then a default
     * Uint8Array(16) is created and returned for you.
     *
     * @method randomBytes
     * @param [output] {TypedArray} The output array for the random data, if none specified a new Uint8Array(16) is created
     */
    randomBytes: (function() {
        if(crypto && crypto.getRandomValues) {
            // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
            // Moderately fast, high quality.
            return function(ary) {
                ary = ary || new Uint8Array(16);
                crypto.getRandomValues(ary);
                return ary;
            };
        } else {
            // Math.random()-based (RNG)
            // It's fast, but is of unspecified quality.
            return function(ary) {
                ary = ary || new Uint8Array(16);

                //get a view into the buffer that we *know* is Uint8
                var buf = ary.buffer,
                    len = buf.byteLength,
                    view = new Uint8Array(buf);

                //fill the array one random byte at a time
                for(var i = 0, r; i < len; ++i) {
                    //we only need a new random when we have pulled all the bytes out of it
                    if((i & 0x03) === 0) {
                        r = math.random() * 0x100000000;
                    }

                    //pull the next byte out of the random number
                    view[i] = r >>> ((i & 0x03) << 3) & 0xff;
                }

                //return the original view which now has the data we put into the buffer
                return ary;
            };
        }
    })(),
    /**
     * Returns a random element of an array.
     *
     * @method randomElement
     * @param array {Array} The array to choose from
     * @param start {Number} The index of the first element to include, defaults to 0
     * @param end {Number} The index of the last element to include, defaults to array.length - 1
     * @return {Number} either 1 or -1
     */
    randomElement: function(array, start, end) {
        //ensure we have an array, and there are elements to check
        if(!array || !array.length)
            return null;

        //special case for 1 element
        if(array.length === 1)
            return array[0];

        //default for start
        if(!start || start < 0)
            start = start || 0;

        //default for end
        if(!end || end < 0)
            end = array.length - 1;

        return array[math.randomInt(start, end)];
    }
};
<|endoftext|>


<commit_before>// I want to avoid adding a Cache instance variable.
// I need to create the temporary object to hold poitners
// to both the cache and the tile which we are waiting for
// the image to load.  The callback only gives a single reference.
function LoadTileCallback(tile,cache) {
    this.Tile = tile;
    this.Cache = cache;
}

// Which cache????????
LoadTileCallback.prototype.HandleLoadedTexture = function () {
    this.Tile.HandleLoadedTexture(this.Cache);
}

// If we cannot load a tile, we need to inform the cache so it can start
// loading another tile.
LoadTileCallback.prototype.HandleErrorTexture = function () {
    LoadQueueError(this.Tile);
}

function GetLoadTextureFunction (callback) {
    return function () {callback.HandleLoadedTexture();}
}
function GetErrorTextureFunction (callback) {
    return function () {callback.HandleErrorTexture();}
}

// Three stages to loading a tile:
// 1: Create a tile object.
// 2: Initialize the texture.
// 3: onload is called indicating the image has been loaded.
function Tile(x, y, z, level, name, cache) {
  // This should be implicit.
  //this is just for debugging
  //this.Id = x + (y<<level)
  //
  this.Cache = cache;
  this.X = x;
  this.Y = y;
  this.Level = level;
  this.Children = [];
  this.Parent = null;
  this.LoadState = 0;
  this.Name = name;
  this.Texture = null;
  this.TimeStamp = TIME_STAMP;
  this.BranchTimeStamp = TIME_STAMP;

  this.Matrix = mat4.create();
  mat4.identity(this.Matrix);
  this.Matrix[14] = z * cache.RootSpacing[2] -(0.1 * this.Level);

  // Default path is to place shared geometry with the matrix.
  if ( ! cache.Warp) {
    // TODO: We should have a simple version of warp that creates this matrix for us.
    // Use shared buffers and place them with the matrix transformation.
    var xScale = cache.TileDimensions[0] * cache.RootSpacing[0] / (1 << this.Level);
    var yScale = cache.TileDimensions[1] * cache.RootSpacing[1] / (1 << this.Level);
    this.Matrix[0] = xScale;
    this.Matrix[5] = yScale;
    this.Matrix[12] = this.X * xScale;
    this.Matrix[13] = this.Y * yScale;
    this.Matrix[15] = 1.0;

    // These tiles share the same buffers.  Do not crop when there is no warp.
    this.VertexPositionBuffer = tileVertexPositionBuffer;
    this.VertexTextureCoordBuffer = tileVertexTextureCoordBuffer;
    this.CellBuffer = tileCellBuffer;
  } else {
    // Warp model.
    this.CreateWarpBuffer(cache.Warp);
  }

  ++NUMBER_OF_TILES;
};

Tile.prototype.destructor=function()
{
  --NUMBER_OF_TILES;
  if (this.Texture) {
    GL.deleteTexture(this.Texture);
  }
  this.Texture = null;
  delete this.Matrix;
  this.Matrix = null;
  if (this.Image) {
    delete this.Image;
    this.Image = 0;
  }
  for (var i = 0; i < 4; ++i) {
    if (this.Children[i] != null) {
        this.Children[i].destructor();
        this.Children[i] = null;
    }
  }
}

Tile.prototype.CreateWarpBuffer = function (warp) {
  // Compute the tile bounds.
  var tileDimensions = this.Cache.TileDimensions;
  var rootSpacing = this.Cache.RootSpacing;
  var p = (1 << this.Level);
  var size = [rootSpacing[0]*tileDimensions[0]/p, rootSpacing[1]*tileDimensions[1]/p];
  var bds = [size[0]*this.X, size[0]*(this.X+1),
             size[1]*this.Y, size[1]*(this.Y+1),
             this.Level, this.Level];

  // Tile geometry buffers.
  var vertexPositionData = [];
  var tCoordsData = [];
  var cellData = [];

  warp.CreateMeshFromBounds(bds, vertexPositionData, tCoordsData, cellData);

  this.VertexTextureCoordBuffer = GL.createBuffer();
  GL.bindBuffer(GL.ARRAY_BUFFER, this.VertexTextureCoordBuffer);
  GL.bufferData(GL.ARRAY_BUFFER, new Float32Array(tCoordsData), GL.STATIC_DRAW);
  this.VertexTextureCoordBuffer.itemSize = 2;
  this.VertexTextureCoordBuffer.numItems = tCoordsData.length / 2;

  this.VertexPositionBuffer = GL.createBuffer();
  GL.bindBuffer(GL.ARRAY_BUFFER, this.VertexPositionBuffer);
  GL.bufferData(GL.ARRAY_BUFFER, new Float32Array(vertexPositionData), GL.STATIC_DRAW);
  this.VertexPositionBuffer.itemSize = 3;
  this.VertexPositionBuffer.numItems = vertexPositionData.length / 3;

  this.CellBuffer = GL.createBuffer();
  GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, this.CellBuffer);
  GL.bufferData(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(cellData), GL.STATIC_DRAW);
  this.CellBuffer.itemSize = 1;
  this.CellBuffer.numItems = cellData.length;
}

// This starts the loading of the tile.
// Loading is asynchronous, so the tile will not
// immediately change its state.
Tile.prototype.StartLoad = function (cache) {
  if (this.Texture != null) {
    return;
  }

  var imageSrc = cache.GetSource() + this.Name + ".jpg";

  this.Texture = GL.createTexture();
  // Reusing the image caused problems.
  //if (this.Image == null) {
    this.Image = new Image();
    var callback = new LoadTileCallback(this, cache);
    this.Image.onload = GetLoadTextureFunction(callback);
    this.Image.onerror = GetErrorTextureFunction(callback);
  //}
  // This starts the loading.
  this.Image.src = imageSrc;
};

Tile.prototype.Draw = function (program) {
  // Load state 0 is: Not loaded and not scheduled to be loaded yet.
  // Load state 1 is: not loaded but in the load queue.
  if ( this.LoadState != 3) {
    // The tile is not available.
    // render the lower resolution tile as a place holder.
    if (this.Parent) {
        this.Parent.Draw(program);
    }
    // Keep rendering until all nodes are available.
    eventuallyRender();
    return;
  }

  // These are the same for every tile.
  // Vertex points (shifted by tiles matrix)
  GL.bindBuffer(GL.ARRAY_BUFFER, this.VertexPositionBuffer);
  // Needed for outline ??? For some reason, DrawOutline did not work
  // without this call first.
  GL.vertexAttribPointer(imageProgram.vertexPositionAttribute,
                        this.VertexPositionBuffer.itemSize,
                        GL.FLOAT, false, 0, 0);     // Texture coordinates
  GL.bindBuffer(GL.ARRAY_BUFFER, this.VertexTextureCoordBuffer);
  GL.vertexAttribPointer(imageProgram.textureCoordAttribute,
                        this.VertexTextureCoordBuffer.itemSize,
                        GL.FLOAT, false, 0, 0);
  // Cell Connectivity
  GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, this.CellBuffer);

    // Texture
  GL.activeTexture(GL.TEXTURE0);
  GL.bindTexture(GL.TEXTURE_2D, this.Texture);

  GL.uniform1i(program.samplerUniform, 0);
  // Matrix that tranforms the vertex p
  GL.uniformMatrix4fv(program.mvMatrixUniform, false, this.Matrix);

  GL.drawElements(GL.TRIANGLES, this.CellBuffer.numItems, GL.UNSIGNED_SHORT, 0);
}

Tile.prototype.HandleLoadedTexture = function (cache) {
    var texture = this.Texture;
    //alert(tile);
    GL.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, true);
    GL.bindTexture(GL.TEXTURE_2D, texture);
    GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, this.Image);
    GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR);
    GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR);
    GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
    GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
    GL.bindTexture(GL.TEXTURE_2D, null);
    // There is an issue: Tiles call this method when their image gets loaded.
    // How does the tile know which cache it belongs too.
    LoadQueueLoaded(this);
}

<commit_msg>Delay creating texture map until tile is drawn.<commit_after>// I want to avoid adding a Cache instance variable.
// I need to create the temporary object to hold poitners
// to both the cache and the tile which we are waiting for
// the image to load.  The callback only gives a single reference.
function LoadTileCallback(tile,cache) {
    this.Tile = tile;
    this.Cache = cache;
}

// Cache is now saved in tile ivar.
LoadTileCallback.prototype.HandleLoadedImage = function () {
  LoadQueueLoaded(this.Tile);
}

// If we cannot load a tile, we need to inform the cache so it can start
// loading another tile.
LoadTileCallback.prototype.HandleErrorImage = function () {
    LoadQueueError(this.Tile);
}

function GetLoadImageFunction (callback) {
    return function () {callback.HandleLoadedImage();}
}
function GetErrorImageFunction (callback) {
    return function () {callback.HandleErrorImage();}
}

// Three stages to loading a tile: (texture map is created when the tile is rendered.
// 1: Create a tile object.
// 2: Initialize the texture.
// 3: onload is called indicating the image has been loaded.
function Tile(x, y, z, level, name, cache) {
  // This should be implicit.
  //this is just for debugging
  //this.Id = x + (y<<level)
  //
  this.Cache = cache;
  this.X = x;
  this.Y = y;
  this.Level = level;
  this.Children = [];
  this.Parent = null;
  this.LoadState = 0;
  this.Name = name;
  this.Texture = null;
  this.TimeStamp = TIME_STAMP;
  this.BranchTimeStamp = TIME_STAMP;

  this.Matrix = mat4.create();
  mat4.identity(this.Matrix);
  this.Matrix[14] = z * cache.RootSpacing[2] -(0.1 * this.Level);

  // Default path is to place shared geometry with the matrix.
  if ( ! cache.Warp) {
    // TODO: We should have a simple version of warp that creates this matrix for us.
    // Use shared buffers and place them with the matrix transformation.
    var xScale = cache.TileDimensions[0] * cache.RootSpacing[0] / (1 << this.Level);
    var yScale = cache.TileDimensions[1] * cache.RootSpacing[1] / (1 << this.Level);
    this.Matrix[0] = xScale;
    this.Matrix[5] = yScale;
    this.Matrix[12] = this.X * xScale;
    this.Matrix[13] = this.Y * yScale;
    this.Matrix[15] = 1.0;

    // These tiles share the same buffers.  Do not crop when there is no warp.
    this.VertexPositionBuffer = tileVertexPositionBuffer;
    this.VertexTextureCoordBuffer = tileVertexTextureCoordBuffer;
    this.CellBuffer = tileCellBuffer;
  } else {
    // Warp model.
    this.CreateWarpBuffer(cache.Warp);
  }

  ++NUMBER_OF_TILES;
};

Tile.prototype.destructor=function()
{
  --NUMBER_OF_TILES;
  if (this.Texture) {
    GL.deleteTexture(this.Texture);
  }
  this.Texture = null;
  delete this.Matrix;
  this.Matrix = null;
  if (this.Image) {
    delete this.Image;
    this.Image = 0;
  }
  for (var i = 0; i < 4; ++i) {
    if (this.Children[i] != null) {
        this.Children[i].destructor();
        this.Children[i] = null;
    }
  }
}

Tile.prototype.CreateWarpBuffer = function (warp) {
  // Compute the tile bounds.
  var tileDimensions = this.Cache.TileDimensions;
  var rootSpacing = this.Cache.RootSpacing;
  var p = (1 << this.Level);
  var size = [rootSpacing[0]*tileDimensions[0]/p, rootSpacing[1]*tileDimensions[1]/p];
  var bds = [size[0]*this.X, size[0]*(this.X+1),
             size[1]*this.Y, size[1]*(this.Y+1),
             this.Level, this.Level];

  // Tile geometry buffers.
  var vertexPositionData = [];
  var tCoordsData = [];
  var cellData = [];

  warp.CreateMeshFromBounds(bds, vertexPositionData, tCoordsData, cellData);

  this.VertexTextureCoordBuffer = GL.createBuffer();
  GL.bindBuffer(GL.ARRAY_BUFFER, this.VertexTextureCoordBuffer);
  GL.bufferData(GL.ARRAY_BUFFER, new Float32Array(tCoordsData), GL.STATIC_DRAW);
  this.VertexTextureCoordBuffer.itemSize = 2;
  this.VertexTextureCoordBuffer.numItems = tCoordsData.length / 2;

  this.VertexPositionBuffer = GL.createBuffer();
  GL.bindBuffer(GL.ARRAY_BUFFER, this.VertexPositionBuffer);
  GL.bufferData(GL.ARRAY_BUFFER, new Float32Array(vertexPositionData), GL.STATIC_DRAW);
  this.VertexPositionBuffer.itemSize = 3;
  this.VertexPositionBuffer.numItems = vertexPositionData.length / 3;

  this.CellBuffer = GL.createBuffer();
  GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, this.CellBuffer);
  GL.bufferData(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(cellData), GL.STATIC_DRAW);
  this.CellBuffer.itemSize = 1;
  this.CellBuffer.numItems = cellData.length;
}

// This starts the loading of the tile.
// Loading is asynchronous, so the tile will not
// immediately change its state.
Tile.prototype.StartLoad = function (cache) {
  if (this.LoadState >= 2) {
    return;
  }

  var imageSrc = cache.GetSource() + this.Name + ".jpg";

  // Reusing the image caused problems.
  //if (this.Image == null) {
    this.Image = new Image();
    var callback = new LoadTileCallback(this, cache);
    this.Image.onload = GetLoadImageFunction(callback);
    this.Image.onerror = GetErrorImageFunction(callback);
  //}
  // This starts the loading.
  this.Image.src = imageSrc;
};

Tile.prototype.Draw = function (program) {

  // Load state 0 is: Not loaded and not scheduled to be loaded yet.
  // Load state 1 is: not loaded but in the load queue.
  if ( this.LoadState != 3) {
    // The tile is not available.
    // render the lower resolution tile as a place holder.
    if (this.Parent) {
        this.Parent.Draw(program);
    }
    // Keep rendering until all nodes are available.
    eventuallyRender();
    return;
  }

  if (this.Texture == null) {
    this.CreateTexture();
  }

  // These are the same for every tile.
  // Vertex points (shifted by tiles matrix)
  GL.bindBuffer(GL.ARRAY_BUFFER, this.VertexPositionBuffer);
  // Needed for outline ??? For some reason, DrawOutline did not work
  // without this call first.
  GL.vertexAttribPointer(imageProgram.vertexPositionAttribute,
                        this.VertexPositionBuffer.itemSize,
                        GL.FLOAT, false, 0, 0);     // Texture coordinates
  GL.bindBuffer(GL.ARRAY_BUFFER, this.VertexTextureCoordBuffer);
  GL.vertexAttribPointer(imageProgram.textureCoordAttribute,
                        this.VertexTextureCoordBuffer.itemSize,
                        GL.FLOAT, false, 0, 0);
  // Cell Connectivity
  GL.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, this.CellBuffer);

    // Texture
  GL.activeTexture(GL.TEXTURE0);
  GL.bindTexture(GL.TEXTURE_2D, this.Texture);

  GL.uniform1i(program.samplerUniform, 0);
  // Matrix that tranforms the vertex p
  GL.uniformMatrix4fv(program.mvMatrixUniform, false, this.Matrix);

  GL.drawElements(GL.TRIANGLES, this.CellBuffer.numItems, GL.UNSIGNED_SHORT, 0);
}

Tile.prototype.CreateTexture = function () {
  if (this.Texture != null) { return;}

  this.Texture = GL.createTexture();
  var texture = this.Texture;
  //alert(tile);
  GL.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, true);
  GL.bindTexture(GL.TEXTURE_2D, texture);
  GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, this.Image);
  GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR);
  GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR);
  GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
  GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
  GL.bindTexture(GL.TEXTURE_2D, null);
}

<|endoftext|>