<commit_before>/*
$File: search_tab.js
$Author: Zheng Li
$Description: this part handles query, filter, scroll function for search tab
*/

var total_rows=0,bookmark,max_entries=20,search_url,skip=0,val;
var Th_priority = ["Th", "Th-232", "232-Th", "Th232", "232Th"];
var U_priority = ["U", "U-238", "238-U", "U238", "238U"];

/*==== click and search for result ====*/
function click_search() {
	var entry = $("#box-search").val();
	val = entry;
	if ( entry == "" || entry == "e.g. all") {
		$("#box-search").focus();
		$("#materials").empty();
		$("#status-line").empty();
		$(".table-header").hide();
		total_rows=0;
		skip=0;
		return false;
	}

	searchResults(entry);

	return false;
};

/*===== decorate the search result ====*/
ButtonFade = function(){
			var parent = $(this).closest('div');
			parent.find('.detail-button').slideToggle(100);

			parent.find('.heading-isotope-name-short').toggle();
		};

function DecorateResult() {
	$("#materials > div").accordion({
		header: "h3",
		navigation: true,
		collapsible:true,
		heightStyle: "content",
		create: function(event) {
			$(event.target).accordion( "activate", false );
		}
	});
	$( "#materials" ).sortable({axis: "y",handle: "h3"});

	/* delete button */
	$(".delete-button").button({
		icons:{primary:"ui-icon-close"},
		text:false
	})
	$(".delete-button" ).click(function(event){
		event.stopPropagation(); // this is
		event.preventDefault(); // the magic
		var parent = $(this).closest('div');
		var head = parent.prev('h3');
		parent.add(head).hide(function(){$(this).remove();});
	});

	/* detail button */
	$(".detail-button").button({
		icons:{primary:"ui-icon-zoomin"},
		text:false
	})
	$(".detail-button" ).unbind();
	$(".detail-button" ).click(function(event){
		event.stopPropagation(); // this is
		event.preventDefault(); // the magic
		var parent = $(this).closest('div');
		parent.find('.hideable').fadeToggle();
		$(".ui-button-icon-primary", this)
			.toggleClass("ui-icon-zoomin ui-icon-zoomout");
	});

	/* export button */
	$(".export-button").button({
		icons:{primary:"ui-icon-arrowthickstop-1-s"},
		text:false
	})
	$(".export-button,.export-option" ).unbind();
	$(".export-button,.export-option" ).mouseenter(function(event){
		var parent = $(this).closest('div');
		parent.find('.export-option').show();
		// $(".ui-button-icon-primary", this)
		// 	.toggleClass("ui-icon-link ui-icon-arrowthickstop-1-s");
	});

	$(".export-button,.export-option" ).mouseleave(function(event){
		var parent = $(this).closest('div');
		parent.find('.export-option').hide();
	});

	$(".export-json").click(function(event){
		var parent = $(this).closest('div');
		url = window.location.protocol + '//' + window.location.host
				+ '/' + dbname+'/'+parent.attr('value');
		window.open(url, '_blank');
	});

	$(".export-xml").click(function(event){
		var parent = $(this).closest('div');
		url = window.location.protocol + '//' + window.location.host
				+ '/' + dbname+'/_design/persephone/_list/exportXML/query-by-id?_id='+parent.attr('value');
		window.open(url, '_blank');
	});

	/* decorate h3 */
	$("h3").unbind('click',ButtonFade);
	$("h3").bind('click', ButtonFade);
};

/* fill the JSON into the output_template.html*/
function FillTemplate(doc){
	var th={ "isotope":"-", "type":"measurement","value":["-"],"unit":"-"},u={ "isotope":"-", "type":"measurement","value":["-"],"unit":"-"}
	var pri_th=100,pri_u=100;
	if ( doc.type == "measurement" ) {
		for(var k in doc.measurement.results){
			var item = doc.measurement.results[k];
			// select Th
			for (var i = 0; i < Th_priority.length; i++) {
				if (item.isotope == Th_priority[i] && i < pri_th){
					th = item;
					pri_th = i;
				}
			};

			// select U
			for (var i = 0; i < U_priority.length; i++) {
				if (item.isotope == U_priority[i] && i < pri_u){
					u = item;
					pri_u = i;
				}
			};
		}

		doc.iso = [th , u];

		var tt = $.tmpl("output_template", doc);
		$("#materials").append(tt);
	}
}

/*==== search for result ====*/
function searchResults(val) {
	// clear the page
	$("#materials").empty();
	$("#status-line").empty();
	// show table header
	$(".table-header").show();

	var n_entries;
	skip=0;
	total_rows=0;

	if ( window.location.host.split(".")[1] == "cloudant" ) {
		search_url = window.location.protocol + '//' + window.location.host
							 + '/' + dbname + '/_design/persephone/_search/assays?q='
							 + val + '&include_docs=true&limit=' + max_entries;
	}

	if ( val == "all" || val == "All" ) {
		search_url = '/' + dbname + '/_design/persephone/_view/query-by-group?limit='+ max_entries +'&include_docs=true';
		// search_url = '/' + dbname + '/_all_docs?limit='+ max_entries +'&include_docs=true&descending=true';
	}

	$.ajax({
		url: search_url,
		dataType: 'json',
		async: false,
		success: function(data) {
			total_rows=data.total_rows;
			$("#status-line").append('Total result: ' + data.total_rows);
			if ( data.total_rows > 0 ) {
				if ( data.total_rows > max_entries ) {
					n_entries = max_entries;
				} else {
					n_entries = data.total_rows;
				};

				if (data.bookmark){
					bookmark = data.bookmark;
				}

				for ( j = 0; j < n_entries; j++ ) {
					var doc = data.rows[j].doc;

					FillTemplate(doc);
				}

				DecorateResult();

				$('#materials').infiniScroll('pollLevel');
			}
		}
	})
};

/*==== order by Th ====*/
function query_by_Th(val) {
	// clear the page
	$("#materials").empty();
	$("#status-line").empty();
	// show table header
	$(".table-header").show();

	$("table-header").css("color" , "#dddddd");
	$(this).css("color", "#e18e94");

	var n_entries;
	skip=0;
	total_rows=0;

	if ( window.location.host.split(".")[1] == "cloudant" ) {
		search_url = window.location.protocol + '//' + window.location.host
							 + '/' + dbname + '/_design/persephone/_search/assays?q='
							 + val + '&include_docs=true&limit=' + max_entries;
	}

	if ( val == "all" || val == "All" ) {
		search_url = '/' + dbname + '/_design/persephone/_view/query-by-Th?limit='+ max_entries +'&include_docs=true';
	}

	$.ajax({
		url: search_url,
		dataType: 'json',
		async: false,
		success: function(data) {
			total_rows=data.total_rows;
			$("#status-line").append('Total result: ' + data.total_rows);
			if ( data.total_rows > 0 ) {
				if ( data.total_rows > max_entries ) {
					n_entries = max_entries;
				} else {
					n_entries = data.total_rows;
				};

				if (data.bookmark){
					bookmark = data.bookmark;
				}

				for ( j = 0; j < n_entries; j++ ) {
					var doc = data.rows[j].doc;

					FillTemplate(doc);
				}

				DecorateResult();

				$('#materials').infiniScroll('pollLevel');
			}
		}
	})
};

/*==== enter box ====*/
function enter_box(event) {

	if (event.keyCode == 13) {

		//$( "#box-search" ).autocomplete("close");
		click_search();
		event.returnValue = false; // for IE
		if (event.preventDefault()) event.preventDefault();

	}

	return false;
}

/*==== email_link ====*/
function email_link(user, dom, linkText) {

 return document.write( "" + linkText + "<\/a>" );

}

/*==== back to top jQuery ====*/
$(document).ready(function(){

	// invoke thfloat() twice; once for "head" and once for "foot"; both attach to window

	// decorate table-header
	$(".table-header").accordion({
			header: "h3",
			icons: { "header": "ui-icon-grip-diagonal-se", "activeHeader": "ui-icon-bullet" },
			collapsible:true,
			disabled: true,
			heightStyle: "content",
		});

	// hide #back-top first
	$("#back-top").hide();
	$("#spinner").hide();

	// fade in #back-top
	$(function () {
		$(window).scroll(function () {
			if ($(this).scrollTop() > 100) {
				$('#back-top').fadeIn();
			} else {
				$('#back-top').fadeOut();
			}
		});

		// scroll body to 0px on click
		$('#back-top a').click(function () {
			$('body,html').animate({
				scrollTop: 0
			}, 800);
			return false;
		});
	});

});

/*==== infinity scroll ====*/
(function( $ ){
	var _checkLevel = function() {
		// if it's low enough, grab latest data
		if (!_levelReached()){
			return methods.pollLevel();
		} else {
			var moreresult = new Boolean(0);
			var url;

			if(skip+max_entries < total_rows){
				moreresult = new Boolean(1);
				skip = skip + max_entries;
				url = search_url + "&skip=" + skip;
			}

			if(val != "all" && val != "All"){
				url = search_url + "&bookmark=" + bookmark;
			}

			if (moreresult == true){
				$('#'+settings.loading_elem).show();
				$.ajax({
					type: "GET",
					url: url,
					dataType: 'json',
					timeout: 3000,
					success: function(data) {
							$('#'+settings.loading_elem).hide();

							if (data.bookmark){
								bookmark = data.bookmark;
							}

							var i;
							for ( i in data.rows ) {
								var doc = data.rows[i].doc;

								FillTemplate(doc);
							}
							DecorateResult();
							methods.pollLevel()
						}
					});
				}	// do not make ajax request if it's the only one left
				// else{
				//	 $('#spinner').hide();
				// }
			}
	};

	var _setLastID = function(elem, lastID){
		elem.data("lastID", lastID);
	};

	var _levelReached = function(){
		// is it low enough to add elements to bottom?
		var pageHeight = Math.max(document.body.scrollHeight ||
			document.body.offsetHeight);
		var viewportHeight = window.innerHeight	||
			document.documentElement.clientHeight	||
			document.body.clientHeight || 0;
		var scrollHeight = window.pageYOffset ||
			document.documentElement.scrollTop	||
			document.body.scrollTop || 0;
		// Trigger for scrolls within 20 pixels from page bottom
		return pageHeight - viewportHeight - scrollHeight < 30;
	};

	/* PUBLIC METHODS */
	var methods = {
		init : function( options ) {
			$('#'+settings.loading_elem).hide();
		},

		pollLevel : function() {
			// checking every so often:
			setTimeout(_checkLevel, 100);
		}
	};

	var settings = {
		 'interval'		 : 100
		,'loading_elem' : 'spinner'
		,'num'					: 5
		,'sort'				 : 'desc'
	};

	$.fn.infiniScroll = function(method) {
		// Method calling logic
		if ( methods[method] ) {
			return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
		} else if ( typeof method === 'object' || ! method ) {
			return methods.init.apply( this, arguments );
		} else {
			$.error( 'Method ' +	method + ' does not exist on jQuery.infiniScroll' );
		}
	}

})( jQuery );

/* Export JSON */
function ExportJSON(JSONid){
    // Start Excel and get Application object.
    var oXL = new ActiveXObject("Excel.Application");
    // Get a new workbook.
    var oWB = oXL.Workbooks.Add();
    var oSheet = oWB.ActiveSheet;
    var hang = objTable.rows.length;

    var lie = objTable.rows(0).cells.length;

    // Add table headers going cell by cell.
    for (var i=0;i<hang;i++)
    {
        for (var j=0;j<lie;j++)
        {
            oSheet.Cells(i+1,j+1).value = objTable.rows(i).cells(j).innerText;
        }

    }
    oXL.Visible = true;
    oXL.UserControl = true;
}
<commit_msg>Optimize Export Assays<commit_after>/*
$File: search_tab.js
$Author: Zheng Li
$Description: this part handles query, filter, scroll function for search tab
*/

var total_rows=0,bookmark,max_entries=20,search_url,skip=0,val;
var Th_priority = ["Th", "Th-232", "232-Th", "Th232", "232Th"];
var U_priority = ["U", "U-238", "238-U", "U238", "238U"];

/*==== click and search for result ====*/
function click_search() {
	var entry = $("#box-search").val();
	val = entry;
	if ( entry == "" || entry == "e.g. all") {
		$("#box-search").focus();
		$("#materials").empty();
		$("#status-line").empty();
		$(".table-header").hide();
		total_rows=0;
		skip=0;
		return false;
	}

	searchResults(entry);

	return false;
};

/*===== decorate the search result ====*/
ButtonFade = function(){
			var parent = $(this).closest('div');
			parent.find('.detail-button').slideToggle(100);

			parent.find('.heading-isotope-name-short').toggle();
		};

function DecorateResult() {
	$("#materials > div").accordion({
		header: "h3",
		navigation: true,
		collapsible:true,
		heightStyle: "content",
		create: function(event) {
			$(event.target).accordion( "activate", false );
		}
	});
	$( "#materials" ).sortable({axis: "y",handle: "h3"});

	/* delete button */
	$(".delete-button").button({
		icons:{primary:"ui-icon-close"},
		text:false
	})
	$(".delete-button" ).click(function(event){
		event.stopPropagation(); // this is
		event.preventDefault(); // the magic
		var parent = $(this).closest('div');
		var head = parent.prev('h3');
		parent.add(head).hide(function(){$(this).remove();});
	});

	/* detail button */
	$(".detail-button").button({
		icons:{primary:"ui-icon-zoomin"},
		text:false
	})
	$(".detail-button" ).unbind();
	$(".detail-button" ).click(function(event){
		event.stopPropagation(); // this is
		event.preventDefault(); // the magic
		var parent = $(this).closest('div');
		parent.find('.hideable').fadeToggle();
		$(".ui-button-icon-primary", this)
			.toggleClass("ui-icon-zoomin ui-icon-zoomout");
	});

	/* export button */
	$(".export-button").button({
		icons:{primary:"ui-icon-arrowthickstop-1-s"},
		text:false
	})

	$(".export-button,.export-option" ).unbind();

	// timeout
	var tt;
	$(".export-button , .export-option" ).mouseover(function(event){
		var parent = $(this).closest('div');
		parent.find('.export-option').fadeIn();
		if(tt){clearTimeout(tt);}
	});

	$(".export-button,.export-option" ).mouseout(function(event){
		var parent = $(this).closest('div');
		tt = setTimeout($.proxy(function() {parent.find('.export-option').fadeOut(); }, this), 200)
	});

	$(".export-json").click(function(event){
		var parent = $(this).closest('div');
		url = window.location.protocol + '//' + window.location.host
				+ '/' + dbname+'/'+parent.attr('value');
		window.open(url, '_blank');
	});

	$(".export-xml").click(function(event){
		var parent = $(this).closest('div');
		url = window.location.protocol + '//' + window.location.host
				+ '/' + dbname+'/_design/persephone/_list/exportXML/query-by-id?_id='+parent.attr('value');
		window.open(url, '_blank');
	});

	/* decorate h3 */
	$("h3").unbind('click',ButtonFade);
	$("h3").bind('click', ButtonFade);
};

/* fill the JSON into the output_template.html*/
function FillTemplate(doc){
	var th={ "isotope":"-", "type":"measurement","value":["-"],"unit":"-"},u={ "isotope":"-", "type":"measurement","value":["-"],"unit":"-"}
	var pri_th=100,pri_u=100;
	if ( doc.type == "measurement" ) {
		for(var k in doc.measurement.results){
			var item = doc.measurement.results[k];
			// select Th
			for (var i = 0; i < Th_priority.length; i++) {
				if (item.isotope == Th_priority[i] && i < pri_th){
					th = item;
					pri_th = i;
				}
			};

			// select U
			for (var i = 0; i < U_priority.length; i++) {
				if (item.isotope == U_priority[i] && i < pri_u){
					u = item;
					pri_u = i;
				}
			};
		}

		doc.iso = [th , u];

		var tt = $.tmpl("output_template", doc);
		$("#materials").append(tt);
	}
}

/*==== search for result ====*/
function searchResults(val) {
	// clear the page
	$("#materials").empty();
	$("#status-line").empty();
	// show table header
	$(".table-header").show();

	var n_entries;
	skip=0;
	total_rows=0;

	if ( window.location.host.split(".")[1] == "cloudant" ) {
		search_url = window.location.protocol + '//' + window.location.host
							 + '/' + dbname + '/_design/persephone/_search/assays?q='
							 + val + '&include_docs=true&limit=' + max_entries;
	}

	if ( val == "all" || val == "All" ) {
		search_url = '/' + dbname + '/_design/persephone/_view/query-by-group?limit='+ max_entries +'&include_docs=true';
		// search_url = '/' + dbname + '/_all_docs?limit='+ max_entries +'&include_docs=true&descending=true';
	}

	$.ajax({
		url: search_url,
		dataType: 'json',
		async: false,
		success: function(data) {
			total_rows=data.total_rows;
			$("#status-line").append('Total result: ' + data.total_rows);
			if ( data.total_rows > 0 ) {
				if ( data.total_rows > max_entries ) {
					n_entries = max_entries;
				} else {
					n_entries = data.total_rows;
				};

				if (data.bookmark){
					bookmark = data.bookmark;
				}

				for ( j = 0; j < n_entries; j++ ) {
					var doc = data.rows[j].doc;

					FillTemplate(doc);
				}

				DecorateResult();

				$('#materials').infiniScroll('pollLevel');
			}
		}
	})
};

/*==== order by Th ====*/
function query_by_Th(val) {
	// clear the page
	$("#materials").empty();
	$("#status-line").empty();
	// show table header
	$(".table-header").show();

	$("table-header").css("color" , "#dddddd");
	$(this).css("color", "#e18e94");

	var n_entries;
	skip=0;
	total_rows=0;

	if ( window.location.host.split(".")[1] == "cloudant" ) {
		search_url = window.location.protocol + '//' + window.location.host
							 + '/' + dbname + '/_design/persephone/_search/assays?q='
							 + val + '&include_docs=true&limit=' + max_entries;
	}

	if ( val == "all" || val == "All" ) {
		search_url = '/' + dbname + '/_design/persephone/_view/query-by-Th?limit='+ max_entries +'&include_docs=true';
	}

	$.ajax({
		url: search_url,
		dataType: 'json',
		async: false,
		success: function(data) {
			total_rows=data.total_rows;
			$("#status-line").append('Total result: ' + data.total_rows);
			if ( data.total_rows > 0 ) {
				if ( data.total_rows > max_entries ) {
					n_entries = max_entries;
				} else {
					n_entries = data.total_rows;
				};

				if (data.bookmark){
					bookmark = data.bookmark;
				}

				for ( j = 0; j < n_entries; j++ ) {
					var doc = data.rows[j].doc;

					FillTemplate(doc);
				}

				DecorateResult();

				$('#materials').infiniScroll('pollLevel');
			}
		}
	})
};

/*==== enter box ====*/
function enter_box(event) {

	if (event.keyCode == 13) {

		//$( "#box-search" ).autocomplete("close");
		click_search();
		event.returnValue = false; // for IE
		if (event.preventDefault()) event.preventDefault();

	}

	return false;
}

/*==== email_link ====*/
function email_link(user, dom, linkText) {

 return document.write( "" + linkText + "<\/a>" );

}

/*==== back to top jQuery ====*/
$(document).ready(function(){

	// invoke thfloat() twice; once for "head" and once for "foot"; both attach to window

	// decorate table-header
	$(".table-header").accordion({
			header: "h3",
			icons: { "header": "ui-icon-grip-diagonal-se", "activeHeader": "ui-icon-bullet" },
			collapsible:true,
			disabled: true,
			heightStyle: "content",
		});

	// hide #back-top first
	$("#back-top").hide();
	$("#spinner").hide();

	// fade in #back-top
	$(function () {
		$(window).scroll(function () {
			if ($(this).scrollTop() > 100) {
				$('#back-top').fadeIn();
			} else {
				$('#back-top').fadeOut();
			}
		});

		// scroll body to 0px on click
		$('#back-top a').click(function () {
			$('body,html').animate({
				scrollTop: 0
			}, 800);
			return false;
		});
	});

});

/*==== infinity scroll ====*/
(function( $ ){
	var _checkLevel = function() {
		// if it's low enough, grab latest data
		if (!_levelReached()){
			return methods.pollLevel();
		} else {
			var moreresult = new Boolean(0);
			var url;

			if(skip+max_entries < total_rows){
				moreresult = new Boolean(1);
				skip = skip + max_entries;
				url = search_url + "&skip=" + skip;
			}

			if(val != "all" && val != "All"){
				url = search_url + "&bookmark=" + bookmark;
			}

			if (moreresult == true){
				$('#'+settings.loading_elem).show();
				$.ajax({
					type: "GET",
					url: url,
					dataType: 'json',
					timeout: 3000,
					success: function(data) {
							$('#'+settings.loading_elem).hide();

							if (data.bookmark){
								bookmark = data.bookmark;
							}

							var i;
							for ( i in data.rows ) {
								var doc = data.rows[i].doc;

								FillTemplate(doc);
							}
							DecorateResult();
							methods.pollLevel()
						}
					});
				}	// do not make ajax request if it's the only one left
				// else{
				//	 $('#spinner').hide();
				// }
			}
	};

	var _setLastID = function(elem, lastID){
		elem.data("lastID", lastID);
	};

	var _levelReached = function(){
		// is it low enough to add elements to bottom?
		var pageHeight = Math.max(document.body.scrollHeight ||
			document.body.offsetHeight);
		var viewportHeight = window.innerHeight	||
			document.documentElement.clientHeight	||
			document.body.clientHeight || 0;
		var scrollHeight = window.pageYOffset ||
			document.documentElement.scrollTop	||
			document.body.scrollTop || 0;
		// Trigger for scrolls within 20 pixels from page bottom
		return pageHeight - viewportHeight - scrollHeight < 30;
	};

	/* PUBLIC METHODS */
	var methods = {
		init : function( options ) {
			$('#'+settings.loading_elem).hide();
		},

		pollLevel : function() {
			// checking every so often:
			setTimeout(_checkLevel, 100);
		}
	};

	var settings = {
		 'interval'		 : 100
		,'loading_elem' : 'spinner'
		,'num'					: 5
		,'sort'				 : 'desc'
	};

	$.fn.infiniScroll = function(method) {
		// Method calling logic
		if ( methods[method] ) {
			return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
		} else if ( typeof method === 'object' || ! method ) {
			return methods.init.apply( this, arguments );
		} else {
			$.error( 'Method ' +	method + ' does not exist on jQuery.infiniScroll' );
		}
	}

})( jQuery );

/* Export JSON */
function ExportJSON(JSONid){
    // Start Excel and get Application object.
    var oXL = new ActiveXObject("Excel.Application");
    // Get a new workbook.
    var oWB = oXL.Workbooks.Add();
    var oSheet = oWB.ActiveSheet;
    var hang = objTable.rows.length;

    var lie = objTable.rows(0).cells.length;

    // Add table headers going cell by cell.
    for (var i=0;i<hang;i++)
    {
        for (var j=0;j<lie;j++)
        {
            oSheet.Cells(i+1,j+1).value = objTable.rows(i).cells(j).innerText;
        }

    }
    oXL.Visible = true;
    oXL.UserControl = true;
}
<|endoftext|>


<commit_before>'use strict';
var LIVERELOAD_PORT = 35729;
var SERVER_PORT = 9000;
var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT});
var mountFolder = function (connect, dir) {
    return connect.static(require('path').resolve(dir));
};

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to match all subfolders:
// 'test/spec/**/*.js'
// templateFramework: 'lodash'

module.exports = function (grunt) {
    // show elapsed time at the end
    require('time-grunt')(grunt);
    // load all grunt tasks
    require('load-grunt-tasks')(grunt);

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'dist'
    };

    grunt.initConfig({
        yeoman: yeomanConfig,
        watch: {
            options: {
                nospawn: true,
                livereload: true
            },
            livereload: {
                options: {
                    livereload: grunt.option('livereloadport') || LIVERELOAD_PORT
                },
                files: [
                    '<%= yeoman.app %>/*.html',
                    '{.tmp,<%= yeoman.app %>}/styles/{,*/}*.css',
                    '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
                    '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}',
                    '<%= yeoman.app %>/scripts/templates/**/*.{ejs,mustache,hbs}',
                    'test/spec/**/*.js'
                ]
            },
            jst: {
                files: [
                    '<%= yeoman.app %>/scripts/templates/**/*.ejs'
                ],
                tasks: ['jst']
            },
            test: {
                files: ['<%= yeoman.app %>/scripts/{,*/}*.js', 'test/spec/**/*.js'],
                tasks: ['test:true']
            },
            less: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.less'],
                tasks: ['less']
            }
        },
        connect: {
            options: {
                port: grunt.option('port') || SERVER_PORT,
                // change this to '0.0.0.0' to access the server from outside
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    middleware: function (connect) {
                        return [
                            lrSnippet,
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },
            test: {
                options: {
                    port: 9001,
                    middleware: function (connect) {
                        return [
                            lrSnippet,
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, 'test'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },
            dist: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, yeomanConfig.dist)
                        ];
                    }
                }
            }
        },
        open: {
            server: {
                path: 'http://localhost:<%= connect.options.port %>'
            },
            test: {
                path: 'http://localhost:<%= connect.test.options.port %>'
            }
        },
        clean: {
            dist: ['.tmp', '<%= yeoman.dist %>/*'],
            server: '.tmp'
        },
        jshint: {
            options: {
                jshintrc: '.jshintrc',
                reporter: require('jshint-stylish')
            },
            all: [
                'Gruntfile.js',
                '<%= yeoman.app %>/scripts/{,*/}*.js',
                '!<%= yeoman.app %>/scripts/vendor/*',
                'test/spec/{,*/}*.js'
            ]
        },
        mocha: {
            all: {
                options: {
                    run: true,
                    urls: ['http://localhost:<%= connect.test.options.port %>/index.html']
                }
            }
        },
        less: {
            dist: {
                files: {
                    '<%= yeoman.app %>/styles/main.css': ['<%= yeoman.app %>/styles/main.less']
                },
                options: {
                    sourceMap: true,
                    sourceMapFilename: '<%= yeoman.app %>/styles/main.css.map',
                    sourceMapBasepath: '<%= yeoman.app %>/',
                    sourceMapRootpath: '/'
                }
            }
        },
        // not enabled since usemin task does concat and uglify
        // check index.html to edit your build targets
        // enable this task if you prefer defining your build targets here
        /*uglify: {
            dist: {}
        },*/
        useminPrepare: {
            html: '<%= yeoman.app %>/index.html',
            options: {
                dest: '<%= yeoman.dist %>'
            }
        },
        usemin: {
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
            options: {
                dirs: ['<%= yeoman.dist %>']
            }
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        cssmin: {
            dist: {
                files: {
                    '<%= yeoman.dist %>/styles/main.css': [
                        '.tmp/styles/{,*/}*.css',
                        '<%= yeoman.app %>/styles/{,*/}*.css'
                    ]
                }
            }
        },
        htmlmin: {
            dist: {
                options: {
                    /*removeCommentsFromCDATA: true,
                    // https://github.com/yeoman/grunt-usemin/issues/44
                    //collapseWhitespace: true,
                    collapseBooleanAttributes: true,
                    removeAttributeQuotes: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true*/
                },
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>',
                    src: '*.html',
                    dest: '<%= yeoman.dist %>'
                }]
            }
        },
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{ico,txt}',
                        'images/{,*/}*.{webp,gif}',
                        'styles/fonts/{,*/}*.*',
                        'fonts/{,*/}*.*',
                    ]
                }, {
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/bower_components/font-awesome/fonts/',
                    dest: '<%= yeoman.app %>/fonts/font-awesome',
                    src: ['*']
                }, {
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/bower_components/bootstrap/dist/fonts/',
                    dest: '<%= yeoman.app %>/fonts/glyphicons',
                    src: ['*']
                }]
            }
        },
        jst: {
            compile: {
                files: {
                    '.tmp/scripts/templates.js': ['<%= yeoman.app %>/scripts/templates/**/*.ejs']
                }
            }
        },
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}',
                        '/styles/fonts/{,*/}*.*',
                        'fonts/{,*/}*.*',
                    ]
                }
            }
        }
    });

    grunt.registerTask('createDefaultTemplate', function () {
        grunt.file.write('.tmp/scripts/templates.js', 'this.JST = this.JST || {};');
    });

    grunt.registerTask('server', function (target) {
        grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
        grunt.task.run(['serve' + (target ? ':' + target : '')]);
    });

    grunt.registerTask('serve', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'open:server', 'connect:dist:keepalive']);
        }

        if (target === 'test') {
            return grunt.task.run([
                'clean:server',
                'createDefaultTemplate',
                'jst',
                'connect:test',
                'open:test',
                'watch'
            ]);
        }

        grunt.task.run([
            'clean:server',
            'less',
            'createDefaultTemplate',
            'jst',
            'connect:livereload',
            'open:server',
            'watch'
        ]);
    });

    grunt.registerTask('test', function (isConnected) {
        isConnected = Boolean(isConnected);
        var testTasks = [
                'clean:server',
                'less',
                'createDefaultTemplate',
                'jst',
                'connect:test',
                'mocha',
            ];

        if(!isConnected) {
            return grunt.task.run(testTasks);
        } else {
            // already connected so not going to connect again, remove the connect:test task
            testTasks.splice(testTasks.indexOf('connect:test'), 1);
            return grunt.task.run(testTasks);
        }
    });

    grunt.registerTask('build', [
        'clean:dist',
        'less',
        'createDefaultTemplate',
        'jst',
        'useminPrepare',
        'imagemin',
        'htmlmin',
        'concat',
        'cssmin',
        'uglify',
        'copy',
        'rev',
        'usemin'
    ]);

    grunt.registerTask('default', [
        'jshint',
        'test',
        'build'
    ]);
};
<commit_msg>Watch all files, recursivelly<commit_after>'use strict';
var LIVERELOAD_PORT = 35729;
var SERVER_PORT = 9000;
var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT});
var mountFolder = function (connect, dir) {
    return connect.static(require('path').resolve(dir));
};

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to match all subfolders:
// 'test/spec/**/*.js'
// templateFramework: 'lodash'

module.exports = function (grunt) {
    // show elapsed time at the end
    require('time-grunt')(grunt);
    // load all grunt tasks
    require('load-grunt-tasks')(grunt);

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'dist'
    };

    grunt.initConfig({
        yeoman: yeomanConfig,
        watch: {
            options: {
                nospawn: true,
                livereload: true
            },
            livereload: {
                options: {
                    livereload: grunt.option('livereloadport') || LIVERELOAD_PORT
                },
                files: [
                    '<%= yeoman.app %>/*.html',
                    '{.tmp,<%= yeoman.app %>}/styles/**/{,*/}*.css',
                    '{.tmp,<%= yeoman.app %>}/scripts/**/{,*/}*.js',
                    '<%= yeoman.app %>/images/**/{,*/}*.{png,jpg,jpeg,gif,webp}',
                    '<%= yeoman.app %>/scripts/templates/**/*.{ejs,mustache,hbs}',
                    'test/spec/**/*.js'
                ]
            },
            jst: {
                files: [
                    '<%= yeoman.app %>/scripts/templates/**/*.ejs'
                ],
                tasks: ['jst']
            },
            test: {
                files: ['<%= yeoman.app %>/scripts/{,*/}*.js', 'test/spec/**/*.js'],
                tasks: ['test:true']
            },
            less: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.less'],
                tasks: ['less']
            }
        },
        connect: {
            options: {
                port: grunt.option('port') || SERVER_PORT,
                // change this to '0.0.0.0' to access the server from outside
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    middleware: function (connect) {
                        return [
                            lrSnippet,
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },
            test: {
                options: {
                    port: 9001,
                    middleware: function (connect) {
                        return [
                            lrSnippet,
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, 'test'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },
            dist: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, yeomanConfig.dist)
                        ];
                    }
                }
            }
        },
        open: {
            server: {
                path: 'http://localhost:<%= connect.options.port %>'
            },
            test: {
                path: 'http://localhost:<%= connect.test.options.port %>'
            }
        },
        clean: {
            dist: ['.tmp', '<%= yeoman.dist %>/*'],
            server: '.tmp'
        },
        jshint: {
            options: {
                jshintrc: '.jshintrc',
                reporter: require('jshint-stylish')
            },
            all: [
                'Gruntfile.js',
                '<%= yeoman.app %>/scripts/{,*/}*.js',
                '!<%= yeoman.app %>/scripts/vendor/*',
                'test/spec/{,*/}*.js'
            ]
        },
        mocha: {
            all: {
                options: {
                    run: true,
                    urls: ['http://localhost:<%= connect.test.options.port %>/index.html']
                }
            }
        },
        less: {
            dist: {
                files: {
                    '<%= yeoman.app %>/styles/main.css': ['<%= yeoman.app %>/styles/main.less']
                },
                options: {
                    sourceMap: true,
                    sourceMapFilename: '<%= yeoman.app %>/styles/main.css.map',
                    sourceMapBasepath: '<%= yeoman.app %>/',
                    sourceMapRootpath: '/'
                }
            }
        },
        // not enabled since usemin task does concat and uglify
        // check index.html to edit your build targets
        // enable this task if you prefer defining your build targets here
        /*uglify: {
            dist: {}
        },*/
        useminPrepare: {
            html: '<%= yeoman.app %>/index.html',
            options: {
                dest: '<%= yeoman.dist %>'
            }
        },
        usemin: {
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
            options: {
                dirs: ['<%= yeoman.dist %>']
            }
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        cssmin: {
            dist: {
                files: {
                    '<%= yeoman.dist %>/styles/main.css': [
                        '.tmp/styles/{,*/}*.css',
                        '<%= yeoman.app %>/styles/{,*/}*.css'
                    ]
                }
            }
        },
        htmlmin: {
            dist: {
                options: {
                    /*removeCommentsFromCDATA: true,
                    // https://github.com/yeoman/grunt-usemin/issues/44
                    //collapseWhitespace: true,
                    collapseBooleanAttributes: true,
                    removeAttributeQuotes: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true*/
                },
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>',
                    src: '*.html',
                    dest: '<%= yeoman.dist %>'
                }]
            }
        },
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{ico,txt}',
                        'images/{,*/}*.{webp,gif}',
                        'styles/fonts/{,*/}*.*',
                        'fonts/{,*/}*.*',
                    ]
                }, {
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/bower_components/font-awesome/fonts/',
                    dest: '<%= yeoman.app %>/fonts/font-awesome',
                    src: ['*']
                }, {
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>/bower_components/bootstrap/dist/fonts/',
                    dest: '<%= yeoman.app %>/fonts/glyphicons',
                    src: ['*']
                }]
            }
        },
        jst: {
            compile: {
                files: {
                    '.tmp/scripts/templates.js': ['<%= yeoman.app %>/scripts/templates/**/*.ejs']
                }
            }
        },
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}',
                        '/styles/fonts/{,*/}*.*',
                        'fonts/{,*/}*.*',
                    ]
                }
            }
        }
    });

    grunt.registerTask('createDefaultTemplate', function () {
        grunt.file.write('.tmp/scripts/templates.js', 'this.JST = this.JST || {};');
    });

    grunt.registerTask('server', function (target) {
        grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
        grunt.task.run(['serve' + (target ? ':' + target : '')]);
    });

    grunt.registerTask('serve', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'open:server', 'connect:dist:keepalive']);
        }

        if (target === 'test') {
            return grunt.task.run([
                'clean:server',
                'createDefaultTemplate',
                'jst',
                'connect:test',
                'open:test',
                'watch'
            ]);
        }

        grunt.task.run([
            'clean:server',
            'less',
            'createDefaultTemplate',
            'jst',
            'connect:livereload',
            'open:server',
            'watch'
        ]);
    });

    grunt.registerTask('test', function (isConnected) {
        isConnected = Boolean(isConnected);
        var testTasks = [
                'clean:server',
                'less',
                'createDefaultTemplate',
                'jst',
                'connect:test',
                'mocha',
            ];

        if(!isConnected) {
            return grunt.task.run(testTasks);
        } else {
            // already connected so not going to connect again, remove the connect:test task
            testTasks.splice(testTasks.indexOf('connect:test'), 1);
            return grunt.task.run(testTasks);
        }
    });

    grunt.registerTask('build', [
        'clean:dist',
        'less',
        'createDefaultTemplate',
        'jst',
        'useminPrepare',
        'imagemin',
        'htmlmin',
        'concat',
        'cssmin',
        'uglify',
        'copy',
        'rev',
        'usemin'
    ]);

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


<commit_before>// GUIDE TO USE
//
// Call AposSnippets.addWidgetType() from your site.js to add this widget type, allowing
// snippets to be inserted into areas on the site.
//
// Call AposSnippets.addWidgetType({ ... }) with different name, label, action and
// defaultLimit options to provide a snippet widget for a different instance type
// that otherwise behaves like the normal snippet widget.
//
// If these options are not enough, you can override methods of apos.widgetTypes[yourName]
// as needed after this call.

AposSnippets.addWidgetType = function(options) {
  options = options || {};
  // _class contains properties common to all instances of the widget
  var _class = {
    name: options.name || 'snippets',
    label: options.label || 'Snippets',
    action: options.action || '/apos-snippet',
    defaultLimit: options.defaultLimit || 1
  };

  apos.widgetTypes[_class.name] = {
    // For the rich content editor's menu
    label: _class.label,

    // Constructor
    editor: function(options) {
      var self = this;
      self._class = _class;

      self.action = self._class.action;
      self.defaultLimit = options.limit || self._class.defaultLimit;
      if (!options.messages) {
        options.messages = {};
      }
      if (!options.messages.missing) {
        options.messages.missing = 'Pick at least one.';
      }

      self.afterCreatingEl = function() {
        if (self.data.limit === undefined) {
          self.data.limit = self.defaultLimit;
        }
        self.$by = self.$el.find('[name="by"]');
        apos.setRadio(self.$by, self.data.by || 'id');
        self.$tags = self.$el.find('[name="tags"]');
        self.$tags.val(apos.tagsToString(self.data.tags));
        self.$limit = self.$el.find('[name="limit"]');
        self.$limit.val(self.data.limit);
        apos.log('self.$limit length is ' + self.$limit.length);
        self.$ids = self.$el.find('[data-ids]');
        apos.log('self.$ids length is ' + self.$ids.length);
        self.populate();
        self.$title = self.$el.find('[name="title"]');
        self.$title.autocomplete({
          minLength: 1,
          source: self.action + '/autocomplete',
          focus: function(event, ui) {
            apos.log('focus');
            self.$title.val(ui.item.title);
            return false;
          },
          select: function(event, ui) {
            apos.log('select');
            self.$title.val('');
            self.add(ui.item);
            return false;
          }
        });

        self.$ids.sortable();

        self.$ids.on('click', '.apos-remove', function() {
          $(this).closest('[data-id]').remove();
          return false;
        });
      };

      self.populate = function() {
        self.$ids.find('[data-id]:not(.apos-template)').remove();
        // We've got just the actual ids, we need the titles also in
        // jquery autocomplete format. The autocomplete route accepts
        // an ids option for this purpose, since it is already set up to
        // transmit results in the needed format
        $.getJSON(options.action + '/autocomplete', { ids: self.data.ids }, function(data) {
          _.each(data, function(info) {
            self.add(info);
          });
        });
      };

      self.add = function(id) {
        var $item = apos.fromTemplate(self.$ids.find('[data-id].apos-template'));
        apos.log($item.length);
        $item.attr('data-snippet-id', id.id);
        $item.find('[data-title]').text(id.value);
        self.$ids.append($item);
        apos.log("appended:");
        apos.log($item[0]);
      };

      self.debrief = function(callback) {
        self.data.by = apos.getRadio(self.$by);
        self.data.tags = apos.tagsToArray(self.$tags.val());
        self.data.limit = parseInt(self.$limit.val(), 10);
        self.data.ids = _.map(self.$ids.find('[data-id]:not(.apos-template)'), function(el) {
          return $(el).data('snippetId');
        });
        self.exists = !!self.data.ids.length;
        return callback();
      };

      self.type = options.type || self._class.name;
      self.css = apos.cssName(self.type);
      options.template = '.apos-' + self.css + '-widget-editor';

      self.prePreview = self.debrief;
      self.preSave = self.debrief;

      // Parent class constructor shared by all widget editors
      apos.widgetEditor.call(self, options);
    }
  };
};

<commit_msg>Fixed the initial repopulate of the selected snippets<commit_after>// GUIDE TO USE
//
// Call AposSnippets.addWidgetType() from your site.js to add this widget type, allowing
// snippets to be inserted into areas on the site.
//
// Call AposSnippets.addWidgetType({ ... }) with different name, label, action and
// defaultLimit options to provide a snippet widget for a different instance type
// that otherwise behaves like the normal snippet widget.
//
// If these options are not enough, you can override methods of apos.widgetTypes[yourName]
// as needed after this call.

AposSnippets.addWidgetType = function(options) {
  options = options || {};
  // _class contains properties common to all instances of the widget
  var _class = {
    name: options.name || 'snippets',
    label: options.label || 'Snippets',
    action: options.action || '/apos-snippet',
    defaultLimit: options.defaultLimit || 1
  };

  apos.widgetTypes[_class.name] = {
    // For the rich content editor's menu
    label: _class.label,

    // Constructor
    editor: function(options) {
      var self = this;
      self._class = _class;

      self.action = self._class.action;
      self.defaultLimit = options.limit || self._class.defaultLimit;
      if (!options.messages) {
        options.messages = {};
      }
      if (!options.messages.missing) {
        options.messages.missing = 'Pick at least one.';
      }

      self.afterCreatingEl = function() {
        if (self.data.limit === undefined) {
          self.data.limit = self.defaultLimit;
        }
        self.$by = self.$el.find('[name="by"]');
        apos.setRadio(self.$by, self.data.by || 'id');
        self.$tags = self.$el.find('[name="tags"]');
        self.$tags.val(apos.tagsToString(self.data.tags));
        self.$limit = self.$el.find('[name="limit"]');
        self.$limit.val(self.data.limit);
        apos.log('self.$limit length is ' + self.$limit.length);
        self.$ids = self.$el.find('[data-ids]');
        apos.log('self.$ids length is ' + self.$ids.length);
        self.populate();
        self.$title = self.$el.find('[name="title"]');
        self.$title.autocomplete({
          minLength: 1,
          source: self.action + '/autocomplete',
          focus: function(event, ui) {
            apos.log('focus');
            self.$title.val(ui.item.title);
            return false;
          },
          select: function(event, ui) {
            apos.log('select');
            self.$title.val('');
            self.add(ui.item);
            return false;
          }
        });

        self.$ids.sortable();

        self.$ids.on('click', '.apos-remove', function() {
          $(this).closest('[data-id]').remove();
          return false;
        });
      };

      self.populate = function() {
        self.$ids.find('[data-id]:not(.apos-template)').remove();
        // We've got just the actual ids, we need the titles also in
        // jquery autocomplete format. The autocomplete route accepts
        // an ids option for this purpose, since it is already set up to
        // transmit results in the needed format
        $.getJSON(self.action + '/autocomplete', { ids: self.data.ids }, function(data) {
          _.each(data, function(info) {
            self.add(info);
          });
        });
      };

      self.add = function(id) {
        var $item = apos.fromTemplate(self.$ids.find('[data-id].apos-template'));
        apos.log($item.length);
        $item.attr('data-snippet-id', id.id);
        $item.find('[data-title]').text(id.value);
        self.$ids.append($item);
        apos.log("appended:");
        apos.log($item[0]);
      };

      self.debrief = function(callback) {
        self.data.by = apos.getRadio(self.$by);
        self.data.tags = apos.tagsToArray(self.$tags.val());
        self.data.limit = parseInt(self.$limit.val(), 10);
        self.data.ids = _.map(self.$ids.find('[data-id]:not(.apos-template)'), function(el) {
          return $(el).data('snippetId');
        });
        self.exists = !!self.data.ids.length;
        return callback();
      };

      self.type = options.type || self._class.name;
      self.css = apos.cssName(self.type);
      options.template = '.apos-' + self.css + '-widget-editor';

      self.prePreview = self.debrief;
      self.preSave = self.debrief;

      // Parent class constructor shared by all widget editors
      apos.widgetEditor.call(self, options);
    }
  };
};

<|endoftext|>


<commit_before>/*globals Node */

import Ember from "ember-metal/core";

/**
@module ember-metal
*/

/**
  Platform specific methods and feature detectors needed by the framework.

  @class platform
  @namespace Ember
  @static
*/
// TODO remove this
var platform = {};

/**
  Identical to `Object.create()`. Implements if not available natively.

  @method create
  @for Ember
*/
var create = Object.create;

// IE8 has Object.create but it couldn't treat property descriptors.
if (create) {
  if (create({a: 1}, {a: {value: 2}}).a !== 2) {
    create = null;
  }
}

// STUB_OBJECT_CREATE allows us to override other libraries that stub
// Object.create different than we would prefer
if (!create || Ember.ENV.STUB_OBJECT_CREATE) {
  var K = function() {};

  create = function(obj, props) {
    K.prototype = obj;
    obj = new K();
    if (props) {
      K.prototype = obj;
      for (var prop in props) {
        K.prototype[prop] = props[prop].value;
      }
      obj = new K();
    }
    K.prototype = null;

    return obj;
  };

  create.isSimulated = true;
}

var defineProperty = Object.defineProperty;
var canRedefineProperties, canDefinePropertyOnDOM;

// Catch IE8 where Object.defineProperty exists but only works on DOM elements
if (defineProperty) {
  try {
    defineProperty({}, 'a',{get:function() {}});
  } catch (e) {
    defineProperty = null;
  }
}

if (defineProperty) {
  // Detects a bug in Android <3.2 where you cannot redefine a property using
  // Object.defineProperty once accessors have already been set.
  canRedefineProperties = (function() {
    var obj = {};

    defineProperty(obj, 'a', {
      configurable: true,
      enumerable: true,
      get: function() { },
      set: function() { }
    });

    defineProperty(obj, 'a', {
      configurable: true,
      enumerable: true,
      writable: true,
      value: true
    });

    return obj.a === true;
  })();

  // This is for Safari 5.0, which supports Object.defineProperty, but not
  // on DOM nodes.
  canDefinePropertyOnDOM = (function() {
    try {
      defineProperty(document.createElement('div'), 'definePropertyOnDOM', {});
      return true;
    } catch(e) { }

    return false;
  })();

  if (!canRedefineProperties) {
    defineProperty = null;
  } else if (!canDefinePropertyOnDOM) {
    defineProperty = function(obj, keyName, desc) {
      var isNode;

      if (typeof Node === "object") {
        isNode = obj instanceof Node;
      } else {
        isNode = typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string";
      }

      if (isNode) {
        // TODO: Should we have a warning here?
        return (obj[keyName] = desc.value);
      } else {
        return Object.defineProperty(obj, keyName, desc);
      }
    };
  }
}

/**
@class platform
@namespace Ember
*/

/**
  Identical to `Object.defineProperty()`. Implements as much functionality
  as possible if not available natively.

  @method defineProperty
  @param {Object} obj The object to modify
  @param {String} keyName property name to modify
  @param {Object} desc descriptor hash
  @return {void}
*/
platform.defineProperty = defineProperty;

/**
  Set to true if the platform supports native getters and setters.

  @property hasPropertyAccessors
  @final
*/
platform.hasPropertyAccessors = true;

if (!platform.defineProperty) {
  platform.hasPropertyAccessors = false;

  platform.defineProperty = function(obj, keyName, desc) {
    if (!desc.get) { obj[keyName] = desc.value; }
  };

  platform.defineProperty.isSimulated = true;
}

if (Ember.ENV.MANDATORY_SETTER && !platform.hasPropertyAccessors) {
  Ember.ENV.MANDATORY_SETTER = false;
}

export {
  create,
  platform
};
<commit_msg>extract es5-sham Object.create(null) shim<commit_after>/*globals Node */

import Ember from "ember-metal/core";

/**
@module ember-metal
*/

/**
  Platform specific methods and feature detectors needed by the framework.

  @class platform
  @namespace Ember
  @static
*/
// TODO remove this
var platform = {};

var defineProperty = Object.defineProperty;
var canRedefineProperties, canDefinePropertyOnDOM;

// Catch IE8 where Object.defineProperty exists but only works on DOM elements
if (defineProperty) {
  try {
    defineProperty({}, 'a',{get:function() {}});
  } catch (e) {
    defineProperty = null;
  }
}

if (defineProperty) {
  // Detects a bug in Android <3.2 where you cannot redefine a property using
  // Object.defineProperty once accessors have already been set.
  canRedefineProperties = (function() {
    var obj = {};

    defineProperty(obj, 'a', {
      configurable: true,
      enumerable: true,
      get: function() { },
      set: function() { }
    });

    defineProperty(obj, 'a', {
      configurable: true,
      enumerable: true,
      writable: true,
      value: true
    });

    return obj.a === true;
  })();

  // This is for Safari 5.0, which supports Object.defineProperty, but not
  // on DOM nodes.
  canDefinePropertyOnDOM = (function() {
    try {
      defineProperty(document.createElement('div'), 'definePropertyOnDOM', {});
      return true;
    } catch(e) { }

    return false;
  })();

  if (!canRedefineProperties) {
    defineProperty = null;
  } else if (!canDefinePropertyOnDOM) {
    defineProperty = function(obj, keyName, desc) {
      var isNode;

      if (typeof Node === "object") {
        isNode = obj instanceof Node;
      } else {
        isNode = typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string";
      }

      if (isNode) {
        // TODO: Should we have a warning here?
        return (obj[keyName] = desc.value);
      } else {
        return Object.defineProperty(obj, keyName, desc);
      }
    };
  }
}

// ES5 15.2.3.7
// http://es5.github.com/#x15.2.3.7
if (!Object.defineProperties) {
  Object.defineProperties = function defineProperties(object, properties) {
    for (var property in properties) {
      if (properties.hasOwnProperty(property) && property !== "__proto__") {
        defineProperty(object, property, properties[property]);
      }
    }
    return object;
  };
}

/**
  Identical to `Object.create()`. Implements if not available natively.

  @method create
  @for Ember
*/
var create;
// ES5 15.2.3.5
// http://es5.github.com/#x15.2.3.5
if (!(Object.create && !Object.create(null).hasOwnProperty)) {
  /* jshint scripturl:true, proto:true */
  // Contributed by Brandon Benvie, October, 2012
  var createEmpty;
  var supportsProto = !({'__proto__':null} instanceof Object);
  // the following produces false positives
  // in Opera Mini => not a reliable check
  // Object.prototype.__proto__ === null
  if (supportsProto || typeof document === 'undefined') {
    createEmpty = function () {
      return { "__proto__": null };
    };
  } else {
    // In old IE __proto__ can't be used to manually set `null`, nor does
    // any other method exist to make an object that inherits from nothing,
    // aside from Object.prototype itself. Instead, create a new global
    // object and *steal* its Object.prototype and strip it bare. This is
    // used as the prototype to create nullary objects.
    createEmpty = function () {
      var iframe = document.createElement('iframe');
      var parent = document.body || document.documentElement;
      iframe.style.display = 'none';
      parent.appendChild(iframe);
      iframe.src = 'javascript:';
      var empty = iframe.contentWindow.Object.prototype;
      parent.removeChild(iframe);
      iframe = null;
      delete empty.constructor;
      delete empty.hasOwnProperty;
      delete empty.propertyIsEnumerable;
      delete empty.isPrototypeOf;
      delete empty.toLocaleString;
      delete empty.toString;
      delete empty.valueOf;
      empty.__proto__ = null;

      function Empty() {}
      Empty.prototype = empty;
      // short-circuit future calls
      createEmpty = function () {
        return new Empty();
      };
      return new Empty();
    };
  }

  create = Object.create = function create(prototype, properties) {

    var object;
    function Type() {}  // An empty constructor.

    if (prototype === null) {
      object = createEmpty();
    } else {
      if (typeof prototype !== "object" && typeof prototype !== "function") {
        // In the native implementation `parent` can be `null`
        // OR *any* `instanceof Object`  (Object|Function|Array|RegExp|etc)
        // Use `typeof` tho, b/c in old IE, DOM elements are not `instanceof Object`
        // like they are in modern browsers. Using `Object.create` on DOM elements
        // is...err...probably inappropriate, but the native version allows for it.
        throw new TypeError("Object prototype may only be an Object or null"); // same msg as Chrome
      }
      Type.prototype = prototype;
      object = new Type();
      // IE has no built-in implementation of `Object.getPrototypeOf`
      // neither `__proto__`, but this manually setting `__proto__` will
      // guarantee that `Object.getPrototypeOf` will work as expected with
      // objects created using `Object.create`
      object.__proto__ = prototype;
    }

    if (properties !== void 0) {
      Object.defineProperties(object, properties);
    }

    return object;
  };
} else {
  create = Object.create;
}

/**
@class platform
@namespace Ember
*/

/**
  Identical to `Object.defineProperty()`. Implements as much functionality
  as possible if not available natively.

  @method defineProperty
  @param {Object} obj The object to modify
  @param {String} keyName property name to modify
  @param {Object} desc descriptor hash
  @return {void}
*/
platform.defineProperty = defineProperty;

/**
  Set to true if the platform supports native getters and setters.

  @property hasPropertyAccessors
  @final
*/
platform.hasPropertyAccessors = true;

if (!platform.defineProperty) {
  platform.hasPropertyAccessors = false;

  platform.defineProperty = function(obj, keyName, desc) {
    if (!desc.get) { obj[keyName] = desc.value; }
  };

  platform.defineProperty.isSimulated = true;
}

if (Ember.ENV.MANDATORY_SETTER && !platform.hasPropertyAccessors) {
  Ember.ENV.MANDATORY_SETTER = false;
}

export {
  create,
  platform
};
<|endoftext|>


<commit_before>function SirpentHex2DGame(game_id, canvas_id) {
  this.canvas = document.getElementById(canvas_id)
  this.context = this.canvas.getContext("2d")

  this.width = this.canvas.width
  this.height = this.canvas.height

  /*this.hexagon_rings = 30
  this.hexagons_across = this.hexagon_rings * 2 + 1

  // The 0.1 discount is to leave a tiny bit of a border.
  this.radius = Math.min(
    this.width / (this.hexagons_across * Math.sqrt(3)),
    this.height / (this.hexagons_across * 1.5 + 0.5)
  ) - 0.1*/

  //this.drawHexagons()

  var grid = false
  var ws = new WebSocket("ws://localhost:8080/worlds/live.json")
  ws.onmessage = function(e) {
    var game_state = JSON.parse(event.data)

    if (!grid) {
      grid = game_state
      this.hexagon_rings = grid.rings
      this.hexagons_across = this.hexagon_rings * 2 + 1
      this.radius = Math.min(
        this.width / (this.hexagons_across * Math.sqrt(3)),
        this.height / (this.hexagons_across * 1.5 + 0.5)
      ) - 0.1
      this.clear()
      this.drawHexagons()
      return
    }

    this.clear()
    this.drawHexagons()
    for (player_id in game_state.plays) {
      var player_state = game_state.plays[player_id]
      this.drawPlayerState(player_state)
    }
    this.drawHexagon(game_state.food, "rgb(200, 0, 0)", "rgb(120, 0, 0)")
  }.bind(this)
  ws.onclose = function(e) {
    console.log("onclose!")
    setTimeout(function() {
      window.location.reload()
    }, 2500)
  }
  //ws.send(data)
}

SirpentHex2DGame.prototype.drawHexagons = function () {
  var cube = {"x": 0, "y": 0, "z": 0}

  for (cube.x = -this.hexagon_rings; cube.x <= this.hexagon_rings; cube.x++) {
    for (cube.y = -this.hexagon_rings; cube.y <= this.hexagon_rings; cube.y++) {
      for (cube.z = -this.hexagon_rings; cube.z <= this.hexagon_rings; cube.z++) {
        if (cube.x + cube.y + cube.z != 0) {
          continue
        }
        this.drawHexagon(cube, "rgb(150,150,150)", null)
      }
    }
  }
}

SirpentHex2DGame.prototype.outlineHexagon = function (x, y) {
  this.context.beginPath()

  var i
  for (i = 0; i < 6; i++) {
    var hc = this.hexCorner({"x": x, "y": y}, this.radius, i)
    if (i == 0) {
      this.context.moveTo(hc.x, hc.y)
    } else {
      this.context.lineTo(hc.x, hc.y)
    }
  }
}

// hex corner, http://www.redblobgames.com/grids/hexagons/#coordinates
SirpentHex2DGame.prototype.hexCorner = function (center, radius, i) {
  var angle_deg = 60 * i   + 30
  var angle_rad = Math.PI / 180 * angle_deg
  var x = center.x + radius * Math.cos(angle_rad)
  var y = center.y + radius * Math.sin(angle_rad)
  return {"x": x, "y": y}
}

SirpentHex2DGame.prototype.drawHexagon = function (hex_vector, strokeColor, fillColor) {
  var canvas_x = this.width / 2 + this.radius * Math.sqrt(3) * (hex_vector.x + hex_vector.z/2)
  var canvas_y = this.height / 2 + this.radius * 1.5 * hex_vector.z

  this.outlineHexagon(canvas_x, canvas_y)

  var tmp

  if (fillColor) {
    tmp = this.context.fillStyle
    this.context.fillStyle = fillColor
    this.context.fill()
    this.context.fillStyle = tmp
  }

  this.context.closePath()

  if (strokeColor) {
    tmp = this.context.strokeStyle
    this.context.strokeStyle = strokeColor
    this.context.stroke()
    this.context.strokeStyle = tmp
  }
}

SirpentHex2DGame.prototype.writeOnHexagon = function (hex_vector, fillColor, text) {
  var canvas_x = this.width / 2 + this.radius * Math.sqrt(3) * (hex_vector.x + hex_vector.z/2)
  var canvas_y = this.height / 2 + this.radius * 1.5 * hex_vector.z

  this.context.textBaseline = "middle"
  this.context.textAlign = "center"
  this.context.fillStyle = fillColor
  this.context.font = "bold 13px Helvetica"
  this.context.fillText(text, canvas_x, canvas_y)
}

SirpentHex2DGame.prototype.drawPlayerState = function (player_state) {
  var i,
      snake = player_state["snake"],
      r = (player_state["alive"]) ? 0 : 255
  for (i = 0; i < snake.length; i++) {
    var segment = snake[i]
    var color = (i == 0) ? "rgb(" + r + ", 120, 0)" : "rgb(" + r + ", 200, 0)"
    this.drawHexagon(segment, "rgb(" + r + ", 120, 0)", color)
    this.writeOnHexagon(segment, "rgb(255, 255, 255)", i)
  }
}

SirpentHex2DGame.prototype.clear = function () {
  this.context.clearRect(0, 0, this.width, this.height)
}
<commit_msg>Started using relative URLs rather than absolute ones in viewer. Makes for easier deploying.<commit_after>function SirpentHex2DGame(game_id, canvas_id) {
  this.canvas = document.getElementById(canvas_id)
  this.context = this.canvas.getContext("2d")

  this.width = this.canvas.width
  this.height = this.canvas.height

  // Assemble a Websocket URL from a relative path on this server.
  // http://stackoverflow.com/a/20161536
  var websocket_url_from_relative = function(s) {
    var l = window.location
    return ((l.protocol === "https:") ? "wss://" : "ws://") + l.host + s
  }

  var grid = false
  var ws = new WebSocket(websocket_url_from_relative("/worlds/live.json"))
  ws.onmessage = function(e) {
    var game_state = JSON.parse(event.data)

    if (!grid) {
      grid = game_state
      this.hexagon_rings = grid.rings
      this.hexagons_across = this.hexagon_rings * 2 + 1
      this.radius = Math.min(
        this.width / (this.hexagons_across * Math.sqrt(3)),
        this.height / (this.hexagons_across * 1.5 + 0.5)
      ) - 0.1
      this.clear()
      this.drawHexagons()
      return
    }

    this.clear()
    this.drawHexagons()
    for (player_id in game_state.plays) {
      var player_state = game_state.plays[player_id]
      this.drawPlayerState(player_state)
    }
    this.drawHexagon(game_state.food, "rgb(200, 0, 0)", "rgb(120, 0, 0)")
  }.bind(this)
  ws.onclose = function(e) {
    console.log("onclose!")
    setTimeout(function() {
      window.location.reload()
    }, 2500)
  }
  //ws.send(data)
}

SirpentHex2DGame.prototype.drawHexagons = function () {
  var cube = {"x": 0, "y": 0, "z": 0}

  for (cube.x = -this.hexagon_rings; cube.x <= this.hexagon_rings; cube.x++) {
    for (cube.y = -this.hexagon_rings; cube.y <= this.hexagon_rings; cube.y++) {
      for (cube.z = -this.hexagon_rings; cube.z <= this.hexagon_rings; cube.z++) {
        if (cube.x + cube.y + cube.z != 0) {
          continue
        }
        this.drawHexagon(cube, "rgb(150,150,150)", null)
      }
    }
  }
}

SirpentHex2DGame.prototype.outlineHexagon = function (x, y) {
  this.context.beginPath()

  var i
  for (i = 0; i < 6; i++) {
    var hc = this.hexCorner({"x": x, "y": y}, this.radius, i)
    if (i == 0) {
      this.context.moveTo(hc.x, hc.y)
    } else {
      this.context.lineTo(hc.x, hc.y)
    }
  }
}

// hex corner, http://www.redblobgames.com/grids/hexagons/#coordinates
SirpentHex2DGame.prototype.hexCorner = function (center, radius, i) {
  var angle_deg = 60 * i   + 30
  var angle_rad = Math.PI / 180 * angle_deg
  var x = center.x + radius * Math.cos(angle_rad)
  var y = center.y + radius * Math.sin(angle_rad)
  return {"x": x, "y": y}
}

SirpentHex2DGame.prototype.drawHexagon = function (hex_vector, strokeColor, fillColor) {
  var canvas_x = this.width / 2 + this.radius * Math.sqrt(3) * (hex_vector.x + hex_vector.z/2)
  var canvas_y = this.height / 2 + this.radius * 1.5 * hex_vector.z

  this.outlineHexagon(canvas_x, canvas_y)

  var tmp

  if (fillColor) {
    tmp = this.context.fillStyle
    this.context.fillStyle = fillColor
    this.context.fill()
    this.context.fillStyle = tmp
  }

  this.context.closePath()

  if (strokeColor) {
    tmp = this.context.strokeStyle
    this.context.strokeStyle = strokeColor
    this.context.stroke()
    this.context.strokeStyle = tmp
  }
}

SirpentHex2DGame.prototype.writeOnHexagon = function (hex_vector, fillColor, text) {
  var canvas_x = this.width / 2 + this.radius * Math.sqrt(3) * (hex_vector.x + hex_vector.z/2)
  var canvas_y = this.height / 2 + this.radius * 1.5 * hex_vector.z

  this.context.textBaseline = "middle"
  this.context.textAlign = "center"
  this.context.fillStyle = fillColor
  this.context.font = "bold 13px Helvetica"
  this.context.fillText(text, canvas_x, canvas_y)
}

SirpentHex2DGame.prototype.drawPlayerState = function (player_state) {
  var i,
      snake = player_state["snake"],
      r = (player_state["alive"]) ? 0 : 255
  for (i = 0; i < snake.length; i++) {
    var segment = snake[i]
    var color = (i == 0) ? "rgb(" + r + ", 120, 0)" : "rgb(" + r + ", 200, 0)"
    this.drawHexagon(segment, "rgb(" + r + ", 120, 0)", color)
    this.writeOnHexagon(segment, "rgb(255, 255, 255)", i)
  }
}

SirpentHex2DGame.prototype.clear = function () {
  this.context.clearRect(0, 0, this.width, this.height)
}
<|endoftext|>


<commit_before>import Events from './events'
import { isFunction, extend, clone } from './util'
import nextTick from './util/next-tick'
import diff from '#/diff'
import patch from '#/patch'
import createElement from '#/create-element'
import { isWidget } from '#/vnode/types'
import { enqueueRender } from './render-queue'

class Component extends Events {
  static extend (properties) {
    let initializing = false
    const base = this
    function beget (obj) {
      let F = function () {}
      F.prototype = obj
      return new F()
    }
    const _super = this.prototype
    initializing = true
    const subPrototype = beget(this.prototype)
    initializing = false
    let props = {}
    for (let prop in properties) {
      if (prop === 'statics') {
        const staticObj = properties[prop]
        for (let staticProp in staticObj) {
          this[staticProp] = staticObj[staticProp]
        }
      } else if (isFunction(_super[prop]) &&
        isFunction(properties[prop]) &&
        this.fnTest.test(properties[prop])) {
        subPrototype[prop] = ((superObj, prop, fn) => {
          return function () {
            this._super = superObj[prop]
            return fn.apply(this, arguments)
          }
        })(_super, prop, properties[prop])
      } else if (!isFunction(properties[prop])) {
        props[prop] = properties[prop]
      } else {
        subPrototype[prop] = properties[prop]
      }
    }

    function T () {
      if (!initializing && isFunction(this.construct)) {
        base.call(this)
        for (let p in props) {
          this[p] = props[p]
        }
        this.construct.apply(this, arguments)
      }
    }
    T.prototype = subPrototype
    T.prototype.constructor = T
    T.extend = Component.extend
    return T
  }

  constructor (props, context) {
    super()
    this._dirty = true
    if (!this.state) {
      this.state = {}
    }
    this.props = props || {}
    this.props = extend(clone(this.constructor.defaultProps || {}), this.props)
    this.context = context || {}
    this.constructor.displayName = this.constructor.name
  }

  setState (state, callback) {
    if (!this.prevState) {
      this.prevState = clone(this.state)
    }
    if (isFunction(state)) {
      state = state(this.state, this.props)
    }
    extend(this.state, state)
    if (callback) {
      (this._renderCallbacks = (this._renderCallbacks || [])).push(callback)
    }
    enqueueRender(this)
  }

  forceUpdate () {
    this.update(true)
  }

  update (isForce) {
    let dom = this.dom
    let props = this.props
    let state = this.state
    let context = this.context
    let prevProps = this.prevProps || props
    let prevState = this.prevState || state
    let prevContext = this.prevContext || context
    let isUpdate = this.dom
    let skip = false
    if (isUpdate) {
      this.props = prevProps
      this.state = prevState
      this.context = prevContext
      if (!isForce && isFunction(this.shouldComponentUpdate)
        && this.shouldComponentUpdate(props, state, context)) {
        skip = true
      } else if (isFunction(this.componentWillUpdate)) {
        this.componentWillUpdate(props, state, context)
      }
      this.props = props
      this.state = state
      this.context = context
    }
    this.prevProps = this.prevState = this.prevContext = null
    this._dirty = false
    if (!skip) {
      if (isFunction(this.getChildContext)) {
        context = extend(extend({}, context), this.getChildContext())
      }
      if (isUpdate && isFunction(this.componentWillReceiveProps)) {
        this.componentWillReceiveProps(props, context)
      }
      this.prevVNode = this.vnode
      this.vnode = this.render()
      this.setupContext(this.vnode, context)
      if (dom) {
        dom._component = null
      }
      if (!this.isServer) {
        this.dom = renderToDom(this)
      }
      if (isUpdate && !isForce) {
        if (isFunction(this.componentDidUpdate)) {
          this.componentDidUpdate()
        }
        this.trigger('updated', this)
      }
      if (isFunction(this.__ref)) {
        this.__ref(this)
      }
      this._prevComponent = null
    }
    if (this._renderCallbacks) {
      while (this._renderCallbacks.length) {
        this._renderCallbacks.pop().call(this)
      }
    }
  }

  setupContext (vnode, context) {
    if (isWidget(vnode)) {
      vnode.component.context = context
    } else {
      let children = vnode.children || []
      children.forEach(child => this.setupContext(child, context))
    }
  }

  mount (parentNode) {
    if (this.constructor.inited && this.constructor.initialHtml) {
      this.update(true)
      if (this.rootNode) {
        this.rootNode.insertAdjacentHTML('beforeend', this.constructor.initialHtml)
        delete this.constructor.inited
        delete this.constructor.initialHtml
      }
    } else {
      if (isFunction(this.componentWillMount)) {
        this.componentWillMount()
      }
      this.update(true)
      if (parentNode && parentNode.appendChild && this.dom) {
        parentNode.appendChild(this.dom)
      }
    }
    let flushMount = () => {
      if (isFunction(this.componentDidMount)) {
        this.componentDidMount()
      }
    }
    if (parentNode) {
      flushMount()
    } else {
      nextTick(flushMount)
    }
  }

  unmount () {
    if (isFunction(this.componentWillUnmount)) {
      this.componentWillUnmount()
    }
    this.prevVNode = this.vnode
    this.vnode = null
    this.dom = renderToDom(this)
    this.prevVNode = null
    let parentNode = this.dom && this.dom.parentNode
    if (parentNode) {
      parentNode.removeChild(this.dom)
    }
    if (isFunction(this.__ref)) {
      this.__ref(null)
    }
    if (this.dom) {
      this.dom.component = null
      this.dom = null
    }
  }
}

function renderToDom (component) {
  let domNode = component.dom ||
    (component._prevComponent && component._prevComponent.dom)
  let lastDomNode = domNode

  if (!domNode) {
    domNode = createElement(component.vnode)
  } else {
    let patches = diff(component.prevVNode, component.vnode)
    domNode._component = component
    domNode = patch(domNode, patches)
  }
  if (domNode) {
    domNode._component = component
  }
  if (lastDomNode && lastDomNode !== domNode) {
    if (lastDomNode._component && lastDomNode._component.dom === lastDomNode) {
      lastDomNode._component.dom = null
    }
    lastDomNode._component = null
  }
  return domNode
}

export default Component<commit_msg>fix:组件prevProps/prevState/prevContext<commit_after>import Events from './events'
import { isFunction, extend, clone } from './util'
import nextTick from './util/next-tick'
import diff from '#/diff'
import patch from '#/patch'
import createElement from '#/create-element'
import { isWidget } from '#/vnode/types'
import { enqueueRender } from './render-queue'

class Component extends Events {
  static extend (properties) {
    let initializing = false
    const base = this
    function beget (obj) {
      let F = function () {}
      F.prototype = obj
      return new F()
    }
    const _super = this.prototype
    initializing = true
    const subPrototype = beget(this.prototype)
    initializing = false
    let props = {}
    for (let prop in properties) {
      if (prop === 'statics') {
        const staticObj = properties[prop]
        for (let staticProp in staticObj) {
          this[staticProp] = staticObj[staticProp]
        }
      } else if (isFunction(_super[prop]) &&
        isFunction(properties[prop]) &&
        this.fnTest.test(properties[prop])) {
        subPrototype[prop] = ((superObj, prop, fn) => {
          return function () {
            this._super = superObj[prop]
            return fn.apply(this, arguments)
          }
        })(_super, prop, properties[prop])
      } else if (!isFunction(properties[prop])) {
        props[prop] = properties[prop]
      } else {
        subPrototype[prop] = properties[prop]
      }
    }

    function T () {
      if (!initializing && isFunction(this.construct)) {
        base.call(this)
        for (let p in props) {
          this[p] = props[p]
        }
        this.construct.apply(this, arguments)
      }
    }
    T.prototype = subPrototype
    T.prototype.constructor = T
    T.extend = Component.extend
    return T
  }

  constructor (props, context) {
    super()
    this._dirty = true
    if (!this.state) {
      this.state = {}
    }
    this.props = props || {}
    this.props = extend(clone(this.constructor.defaultProps || {}), this.props)
    this.context = context || {}
    this.constructor.displayName = this.constructor.name
  }

  setState (state, callback) {
    if (!this.prevState) {
      this.prevState = clone(this.state)
    }
    if (isFunction(state)) {
      state = state(this.state, this.props)
    }
    extend(this.state, state)
    if (callback) {
      (this._renderCallbacks = (this._renderCallbacks || [])).push(callback)
    }
    enqueueRender(this)
  }

  forceUpdate () {
    this.update(true)
  }

  update (isForce) {
    let dom = this.dom
    let props = this.props
    let state = this.state
    let context = this.context
    let prevProps = this.prevProps || props
    let prevState = this.prevState || state
    let prevContext = this.prevContext || context
    let isUpdate = this.dom
    let skip = false
    if (isUpdate) {
      this.props = prevProps
      this.state = prevState
      this.context = prevContext
      if (!isForce && isFunction(this.shouldComponentUpdate)
        && this.shouldComponentUpdate(props, state, context) === false) {
        skip = true
      } else if (isFunction(this.componentWillUpdate)) {
        this.componentWillUpdate(props, state, context)
      }
      this.props = props
      this.state = state
      this.context = context
    }
    this.prevProps = this.prevState = this.prevContext = null
    this._dirty = false
    if (!skip) {
      if (isFunction(this.getChildContext)) {
        context = extend(extend({}, context), this.getChildContext())
      }
      if (isUpdate && isFunction(this.componentWillReceiveProps)) {
        this.componentWillReceiveProps(props, context)
      }
      if (!this.prevProps) {
        this.prevProps = this.props
      }
      if (!this.prevState) {
        this.prevState = this.state
      }
      if (!this.prevContext) {
        this.prevContext = this.context
      }
      this.prevVNode = this.vnode
      this.vnode = this.render()
      this.setupContext(this.vnode, context)
      if (dom) {
        dom._component = null
      }
      if (!this.isServer) {
        this.dom = renderToDom(this)
      }
      if (isUpdate && !isForce) {
        if (isFunction(this.componentDidUpdate)) {
          this.componentDidUpdate()
        }
        this.trigger('updated', this)
      }
      if (isFunction(this.__ref)) {
        this.__ref(this)
      }
      this._prevComponent = null
    }
    if (this._renderCallbacks) {
      while (this._renderCallbacks.length) {
        this._renderCallbacks.pop().call(this)
      }
    }
  }

  setupContext (vnode, context) {
    if (isWidget(vnode)) {
      vnode.component.context = context
    } else {
      let children = vnode.children || []
      children.forEach(child => this.setupContext(child, context))
    }
  }

  mount (parentNode) {
    if (this.constructor.inited && this.constructor.initialHtml) {
      this.update(true)
      if (this.rootNode) {
        this.rootNode.insertAdjacentHTML('beforeend', this.constructor.initialHtml)
        delete this.constructor.inited
        delete this.constructor.initialHtml
      }
    } else {
      if (isFunction(this.componentWillMount)) {
        this.componentWillMount()
      }
      this.update(true)
      if (parentNode && parentNode.appendChild && this.dom) {
        parentNode.appendChild(this.dom)
      }
    }
    let flushMount = () => {
      if (isFunction(this.componentDidMount)) {
        this.componentDidMount()
      }
    }
    if (parentNode) {
      flushMount()
    } else {
      nextTick(flushMount)
    }
  }

  unmount () {
    if (isFunction(this.componentWillUnmount)) {
      this.componentWillUnmount()
    }
    this.prevVNode = this.vnode
    this.vnode = null
    this.dom = renderToDom(this)
    this.prevVNode = null
    let parentNode = this.dom && this.dom.parentNode
    if (parentNode) {
      parentNode.removeChild(this.dom)
    }
    if (isFunction(this.__ref)) {
      this.__ref(null)
    }
    if (this.dom) {
      this.dom.component = null
      this.dom = null
    }
  }
}

function renderToDom (component) {
  let domNode = component.dom ||
    (component._prevComponent && component._prevComponent.dom)
  let lastDomNode = domNode

  if (!domNode) {
    domNode = createElement(component.vnode)
  } else {
    let patches = diff(component.prevVNode, component.vnode)
    domNode._component = component
    domNode = patch(domNode, patches)
  }
  if (domNode) {
    domNode._component = component
  }
  if (lastDomNode && lastDomNode !== domNode) {
    if (lastDomNode._component && lastDomNode._component.dom === lastDomNode) {
      lastDomNode._component.dom = null
    }
    lastDomNode._component = null
  }
  return domNode
}

export default Component<|endoftext|>