<commit_before> /*!
 * jQuery HiGoogleFonts Library v0.1.0
 * http://asadiqbal.com/
 *
 * Uses select2 jquery library for creating Picker
 *
 * Copyright Hiwaas and other contributors
 * Released under the Apache license 2.0
 * https://github.com/saadqbal/HiGoogleFonts/blob/master/LICENSE
 *
 * Date: 2016-Feb-20
 */

 (function ( $ ) {
		$.fn.higooglefonts = function(options) {

			//console.log(this);

				var settings = $.extend({
					selectedCallback: function(params) { },
					loadedCallback: function(params) { }
				}, options );

				var fontMap   =   fontMap || [];

				fonts.forEach(function(font,index){

					fontMap.push({
						id           :   font,
						text         :   font,
						itemId     :   index
					});

				});

				var y=0;
				this.select2({
					placeholder:"Select Font Family",
					data: fontMap,
					theme: "classic",
					triggerChange: true,
					allowClear: true,
					minimumResultsForSearch: Infinity,
					templateResult: function (result) {
						var state = $('
'+result.text+'
');
						y	+=29;
						return state;
					}
				});

				this.on("select2:open", function (e) {
					y=0;
				});
				this.on("select2:close", function (e) {
					y=0;
				});
				this.on("select2:select", function (e) {
					var font_family		=	e.params.data.text;

					if (typeof settings.selectedCallback == 'function') { // make sure the callback is a function

						settings.selectedCallback.call(this, e.params.data.text); // brings the scope to the callback
					}

					WebFont.load({
						google: {
							families: [font_family]
						},
						fontactive: function(familyName, fvd) {

							if (typeof settings.loadedCallback == 'function') { // make sure the callback is a function

								settings.loadedCallback.call(this, familyName); // brings the scope to the callback
							}

						}
					});

				});

		  }; /// End of function

		  var fonts = [
					"ABeeZee","Abel","Abril Fatface","Aclonica","Acme","Actor","Adamina","Advent Pro","Aguafina Script","Akronim","Aladin","Aldrich","Alef","Alegreya","Alegreya SC","Alegreya Sans","Alegreya Sans SC","Alex Brush","Alfa Slab One","Alice","Alike","Alike Angular","Allan","Allerta","Allerta Stencil","Allura","Almendra","Almendra Display","Almendra SC","Amarante","Amaranth","Amatic SC","Amethysta","Amiri","Amita","Anaheim","Andada","Andika","Angkor","Annie Use Your Telescope","Anonymous Pro","Antic","Antic Didone","Antic Slab","Anton","Arapey","Arbutus","Arbutus Slab","Architects Daughter","Archivo Black","Archivo Narrow","Arimo","Arizonia","Armata","Artifika","Arvo","Arya","Asap","Asar","Asset","Astloch","Asul","Atomic Age","Aubrey","Audiowide","Autour One","Average","Average Sans","Averia Gruesa Libre","Averia Libre","Averia Sans Libre","Averia Serif Libre","Bad Script","Balthazar","Bangers","Basic","Battambang","Baumans","Bayon","Belgrano","Belleza","BenchNine","Bentham","Berkshire Swash","Bevan","Bigelow Rules","Bigshot One","Bilbo","Bilbo Swash Caps","Biryani","Bitter","Black Ops One","Bokor","Bonbon","Boogaloo","Bowlby One","Bowlby One SC","Brawler","Bree Serif","Bubblegum Sans","Bubbler One","Buda","Buenard","Butcherman","Butterfly Kids","Cabin","Cabin Condensed","Cabin Sketch","Caesar Dressing","Cagliostro","Calligraffitti","Cambay","Cambo","Candal","Cantarell","Cantata One","Cantora One","Capriola","Cardo","Carme","Carrois Gothic","Carrois Gothic SC","Carter One","Catamaran","Caudex","Caveat","Caveat Brush","Cedarville Cursive","Ceviche One","Changa One","Chango","Chau Philomene One","Chela One","Chelsea Market","Chenla","Cherry Cream Soda","Cherry Swash","Chewy","Chicle","Chivo","Chonburi","Cinzel","Cinzel Decorative","Clicker Script","Coda","Coda Caption","Codystar","Combo","Comfortaa","Coming Soon","Concert One","Condiment","Content","Contrail One","Convergence","Cookie","Copse","Corben","Courgette","Cousine","Coustard","Covered By Your Grace","Crafty Girls","Creepster","Crete Round","Crimson Text","Croissant One","Crushed","Cuprum","Cutive","Cutive Mono","Damion","Dancing Script","Dangrek","Dawning of a New Day","Days One","Dekko","Delius","Delius Swash Caps","Delius Unicase","Della Respira","Denk One","Devonshire","Dhurjati","Didact Gothic","Diplomata","Diplomata SC","Domine","Donegal One","Doppio One","Dorsa","Dosis","Dr Sugiyama","Droid Sans","Droid Sans Mono","Droid Serif","Duru Sans","Dynalight","EB Garamond","Eagle Lake","Eater","Economica","Eczar","Ek Mukta","Electrolize","Elsie","Elsie Swash Caps","Emblema One","Emilys Candy","Engagement","Englebert","Enriqueta","Erica One","Esteban","Euphoria Script","Ewert","Exo","Exo 2","Expletus Sans","Fanwood Text","Fascinate","Fascinate Inline","Faster One","Fasthand","Fauna One","Federant","Federo","Felipa","Fenix","Finger Paint","Fira Mono","Fira Sans","Fjalla One","Fjord One","Flamenco","Flavors","Fondamento","Fontdiner Swanky","Forum","Francois One","Freckle Face","Fredericka the Great","Fredoka One","Freehand","Fresca","Frijole","Fruktur","Fugaz One","GFS Didot","GFS Neohellenic","Gabriela","Gafata","Galdeano","Galindo","Gentium Basic","Gentium Book Basic","Geo","Geostar","Geostar Fill","Germania One","Gidugu","Gilda Display","Give You Glory","Glass Antiqua","Glegoo","Gloria Hallelujah","Goblin One","Gochi Hand","Gorditas","Goudy Bookletter 1911","Graduate","Grand Hotel","Gravitas One","Great Vibes","Griffy","Gruppo","Gudea","Gurajada","Habibi","Halant","Hammersmith One","Hanalei","Hanalei Fill","Handlee","Hanuman","Happy Monkey","Headland One","Henny Penny","Herr Von Muellerhoff","Hind","Hind Siliguri","Hind Vadodara","Holtwood One SC","Homemade Apple","Homenaje","IM Fell DW Pica","IM Fell DW Pica SC","IM Fell Double Pica","IM Fell Double Pica SC","IM Fell English","IM Fell English SC","IM Fell French Canon","IM Fell French Canon SC","IM Fell Great Primer","IM Fell Great Primer SC","Iceberg","Iceland","Imprima","Inconsolata","Inder","Indie Flower","Inika","Inknut Antiqua","Irish Grover","Istok Web","Italiana","Italianno","Itim","Jacques Francois","Jacques Francois Shadow","Jaldi","Jim Nightshade","Jockey One","Jolly Lodger","Josefin Sans","Josefin Slab","Joti One","Judson","Julee","Julius Sans One",
					"Junge","Jura","Just Another Hand","Just Me Again Down Here","Kadwa","Kalam","Kameron","Kanit","Kantumruy","Karla","Karma","Kaushan Script","Kavoon","Kdam Thmor","Keania One","Kelly Slab","Kenia","Khand","Khmer","Khula","Kite One","Knewave","Kotta One","Koulen","Kranky","Kreon","Kristi","Krona One","Kurale","La Belle Aurore","Laila","Lakki Reddy","Lancelot","Lateef","Lato","League Script","Leckerli One","Ledger","Lekton","Lemon","Libre Baskerville","Life Savers","Lilita One","Lily Script One","Limelight","Linden Hill","Lobster","Lobster Two","Londrina Outline","Londrina Shadow","Londrina Sketch","Londrina Solid","Lora","Love Ya Like A Sister","Loved by the King","Lovers Quarrel","Luckiest Guy","Lusitana","Lustria","Macondo","Macondo Swash Caps","Magra","Maiden Orange","Mako","Mallanna","Mandali","Marcellus","Marcellus SC","Marck Script","Margarine","Marko One","Marmelad","Martel","Martel Sans","Marvel","Mate","Mate SC","Maven Pro","McLaren","Meddon","MedievalSharp","Medula One","Megrim","Meie Script","Merienda","Merienda One","Merriweather","Merriweather Sans","Metal","Metal Mania","Metamorphous","Metrophobic","Michroma","Milonga","Miltonian","Miltonian Tattoo","Miniver","Miss Fajardose","Modak","Modern Antiqua","Molengo","Molle","Monda","Monofett","Monoton","Monsieur La Doulaise","Montaga","Montez","Montserrat","Montserrat Alternates","Montserrat Subrayada","Moul","Moulpali","Mountains of Christmas","Mouse Memoirs","Mr Bedfort","Mr Dafoe","Mr De Haviland","Mrs Saint Delafield","Mrs Sheppards","Muli","Mystery Quest","NTR","Neucha","Neuton","New Rocker","News Cycle","Niconne","Nixie One","Nobile","Nokora","Norican","Nosifer","Nothing You Could Do","Noticia Text","Noto Sans","Noto Serif","Nova Cut","Nova Flat","Nova Mono","Nova Oval","Nova Round","Nova Script","Nova Slim","Nova Square","Numans","Nunito","Odor Mean Chey","Offside","Old Standard TT","Oldenburg","Oleo Script","Oleo Script Swash Caps","Open Sans","Open Sans Condensed","Oranienbaum","Orbitron","Oregano","Orienta","Original Surfer","Oswald","Over the Rainbow","Overlock","Overlock SC","Ovo","Oxygen","Oxygen Mono","PT Mono","PT Sans","PT Sans Caption","PT Sans Narrow","PT Serif","PT Serif Caption","Pacifico","Palanquin","Palanquin Dark","Paprika","Parisienne","Passero One","Passion One","Pathway Gothic One","Patrick Hand","Patrick Hand SC","Patua One","Paytone One","Peddana","Peralta","Permanent Marker","Petit Formal Script","Petrona","Philosopher","Piedra","Pinyon Script","Pirata One","Plaster","Play","Playball","Playfair Display","Playfair Display SC","Podkova","Poiret One","Poller One","Poly","Pompiere","Pontano Sans","Poppins","Port Lligat Sans","Port Lligat Slab","Pragati Narrow","Prata","Preahvihear","Press Start 2P","Princess Sofia","Prociono","Prosto One","Puritan","Purple Purse","Quando","Quantico","Quattrocento","Quattrocento Sans","Questrial","Quicksand","Quintessential","Qwigley","Racing Sans One","Radley","Rajdhani","Raleway","Raleway Dots","Ramabhadra","Ramaraja","Rambla","Rammetto One","Ranchers","Rancho","Ranga","Rationale","Ravi Prakash","Redressed","Reenie Beanie","Revalia","Rhodium Libre","Ribeye","Ribeye Marrow","Righteous","Risque","Roboto","Roboto Condensed","Roboto Mono","Roboto Slab","Rochester","Rock Salt","Rokkitt","Romanesco","Ropa Sans","Rosario","Rosarivo","Rouge Script","Rozha One","Rubik","Rubik Mono One","Rubik One","Ruda","Rufina","Ruge Boogie","Ruluko","Rum Raisin","Ruslan Display","Russo One","Ruthie","Rye","Sacramento","Sahitya","Sail","Salsa","Sanchez","Sancreek","Sansita One","Sarala","Sarina","Sarpanch","Satisfy","Scada","Scheherazade","Schoolbell","Seaweed Script","Sevillana","Seymour One","Shadows Into Light","Shadows Into Light Two","Shanti","Share","Share Tech","Share Tech Mono","Shojumaru","Short Stack","Siemreap","Sigmar One","Signika","Signika Negative","Simonetta","Sintony","Sirin Stencil","Six Caps","Skranji","Slabo 13px","Slabo 27px","Slackey","Smokum","Smythe","Sniglet","Snippet","Snowburst One","Sofadi One","Sofia","Sonsie One","Sorts Mill Goudy","Source Code Pro","Source Sans Pro","Source Serif Pro","Special Elite","Spicy Rice","Spinnaker","Spirax","Squada One","Sree Krushnadevaraya","Stalemate","Stalinist One","Stardos Stencil","Stint Ultra Condensed","Stint Ultra Expanded","Stoke","Strait","Sue Ellen Francisco","Sumana","Sunshiney","Supermercado One","Sura","Suranna","Suravaram","Suwannaphum","Swanky and Moo Moo","Syncopate","Tangerine","Taprom","Tauri","Teko","Telex","Tenali Ramakrishna","Tenor Sans","Text Me One","The Girl Next Door","Tienne","Tillana","Timmana","Tinos","Titan One","Titillium Web","Trade Winds","Trocchi","Trochut","Trykker","Tulpen One","Ubuntu","Ubuntu Condensed","Ubuntu Mono","Ultra","Uncial Antiqua","Underdog","Unica One","UnifrakturCook","UnifrakturMaguntia","Unkempt","Unlock","Unna","VT323","Vampiro One","Varela","Varela Round","Vast Shadow","Vesper Libre","Vibur","Vidaloka","Viga","Voces","Volkhov","Vollkorn","Voltaire","Waiting for the Sunrise","Wallpoet","Walter Turncoat","Warnes","Wellfleet","Wendy One","Wire One","Work Sans","Yanone Kaffeesatz","Yantramanav","Yellowtail","Yeseva One","Yesteryear","Zeyada"
				];

		}( jQuery ));
<commit_msg>Set an initial empty value on load<commit_after> /*!
 * jQuery HiGoogleFonts Library v0.1.0
 * http://asadiqbal.com/
 *
 * Uses select2 jquery library for creating Picker
 *
 * Copyright Hiwaas and other contributors
 * Released under the Apache license 2.0
 * https://github.com/saadqbal/HiGoogleFonts/blob/master/LICENSE
 *
 * Date: 2016-Feb-20
 */

 (function ( $ ) {
		$.fn.higooglefonts = function(options) {

			//console.log(this);

				var settings = $.extend({
					selectedCallback: function(params) { },
					loadedCallback: function(params) { }
				}, options );

				var fontMap   =   fontMap || [];

				fonts.forEach(function(font,index){

					fontMap.push({
						id           :   font,
						text         :   font,
						itemId     :   index
					});

				});

				var y=0;
				this.select2({
					placeholder:"Select Font Family",
					data: fontMap,
					theme: "classic",
					triggerChange: true,
					allowClear: true,
					minimumResultsForSearch: Infinity,
					templateResult: function (result) {
						var state = $('
'+result.text+'
');
						y	+=29;
						return state;
					}
				});

				this.on("select2:open", function (e) {
					y=0;
				});
				this.on("select2:close", function (e) {
					y=0;
				});
				this.on("select2:select", function (e) {
					var font_family		=	e.params.data.text;

					if (typeof settings.selectedCallback == 'function') { // make sure the callback is a function

						settings.selectedCallback.call(this, e.params.data.text); // brings the scope to the callback
					}

					WebFont.load({
						google: {
							families: [font_family]
						},
						fontactive: function(familyName, fvd) {

							if (typeof settings.loadedCallback == 'function') { // make sure the callback is a function

								settings.loadedCallback.call(this, familyName); // brings the scope to the callback
							}

						}
					});

				});

				// Set empty initial value
				this.val('');
				this.trigger('change');

		  }; /// End of function

		  var fonts = [
					"ABeeZee","Abel","Abril Fatface","Aclonica","Acme","Actor","Adamina","Advent Pro","Aguafina Script","Akronim","Aladin","Aldrich","Alef","Alegreya","Alegreya SC","Alegreya Sans","Alegreya Sans SC","Alex Brush","Alfa Slab One","Alice","Alike","Alike Angular","Allan","Allerta","Allerta Stencil","Allura","Almendra","Almendra Display","Almendra SC","Amarante","Amaranth","Amatic SC","Amethysta","Amiri","Amita","Anaheim","Andada","Andika","Angkor","Annie Use Your Telescope","Anonymous Pro","Antic","Antic Didone","Antic Slab","Anton","Arapey","Arbutus","Arbutus Slab","Architects Daughter","Archivo Black","Archivo Narrow","Arimo","Arizonia","Armata","Artifika","Arvo","Arya","Asap","Asar","Asset","Astloch","Asul","Atomic Age","Aubrey","Audiowide","Autour One","Average","Average Sans","Averia Gruesa Libre","Averia Libre","Averia Sans Libre","Averia Serif Libre","Bad Script","Balthazar","Bangers","Basic","Battambang","Baumans","Bayon","Belgrano","Belleza","BenchNine","Bentham","Berkshire Swash","Bevan","Bigelow Rules","Bigshot One","Bilbo","Bilbo Swash Caps","Biryani","Bitter","Black Ops One","Bokor","Bonbon","Boogaloo","Bowlby One","Bowlby One SC","Brawler","Bree Serif","Bubblegum Sans","Bubbler One","Buda","Buenard","Butcherman","Butterfly Kids","Cabin","Cabin Condensed","Cabin Sketch","Caesar Dressing","Cagliostro","Calligraffitti","Cambay","Cambo","Candal","Cantarell","Cantata One","Cantora One","Capriola","Cardo","Carme","Carrois Gothic","Carrois Gothic SC","Carter One","Catamaran","Caudex","Caveat","Caveat Brush","Cedarville Cursive","Ceviche One","Changa One","Chango","Chau Philomene One","Chela One","Chelsea Market","Chenla","Cherry Cream Soda","Cherry Swash","Chewy","Chicle","Chivo","Chonburi","Cinzel","Cinzel Decorative","Clicker Script","Coda","Coda Caption","Codystar","Combo","Comfortaa","Coming Soon","Concert One","Condiment","Content","Contrail One","Convergence","Cookie","Copse","Corben","Courgette","Cousine","Coustard","Covered By Your Grace","Crafty Girls","Creepster","Crete Round","Crimson Text","Croissant One","Crushed","Cuprum","Cutive","Cutive Mono","Damion","Dancing Script","Dangrek","Dawning of a New Day","Days One","Dekko","Delius","Delius Swash Caps","Delius Unicase","Della Respira","Denk One","Devonshire","Dhurjati","Didact Gothic","Diplomata","Diplomata SC","Domine","Donegal One","Doppio One","Dorsa","Dosis","Dr Sugiyama","Droid Sans","Droid Sans Mono","Droid Serif","Duru Sans","Dynalight","EB Garamond","Eagle Lake","Eater","Economica","Eczar","Ek Mukta","Electrolize","Elsie","Elsie Swash Caps","Emblema One","Emilys Candy","Engagement","Englebert","Enriqueta","Erica One","Esteban","Euphoria Script","Ewert","Exo","Exo 2","Expletus Sans","Fanwood Text","Fascinate","Fascinate Inline","Faster One","Fasthand","Fauna One","Federant","Federo","Felipa","Fenix","Finger Paint","Fira Mono","Fira Sans","Fjalla One","Fjord One","Flamenco","Flavors","Fondamento","Fontdiner Swanky","Forum","Francois One","Freckle Face","Fredericka the Great","Fredoka One","Freehand","Fresca","Frijole","Fruktur","Fugaz One","GFS Didot","GFS Neohellenic","Gabriela","Gafata","Galdeano","Galindo","Gentium Basic","Gentium Book Basic","Geo","Geostar","Geostar Fill","Germania One","Gidugu","Gilda Display","Give You Glory","Glass Antiqua","Glegoo","Gloria Hallelujah","Goblin One","Gochi Hand","Gorditas","Goudy Bookletter 1911","Graduate","Grand Hotel","Gravitas One","Great Vibes","Griffy","Gruppo","Gudea","Gurajada","Habibi","Halant","Hammersmith One","Hanalei","Hanalei Fill","Handlee","Hanuman","Happy Monkey","Headland One","Henny Penny","Herr Von Muellerhoff","Hind","Hind Siliguri","Hind Vadodara","Holtwood One SC","Homemade Apple","Homenaje","IM Fell DW Pica","IM Fell DW Pica SC","IM Fell Double Pica","IM Fell Double Pica SC","IM Fell English","IM Fell English SC","IM Fell French Canon","IM Fell French Canon SC","IM Fell Great Primer","IM Fell Great Primer SC","Iceberg","Iceland","Imprima","Inconsolata","Inder","Indie Flower","Inika","Inknut Antiqua","Irish Grover","Istok Web","Italiana","Italianno","Itim","Jacques Francois","Jacques Francois Shadow","Jaldi","Jim Nightshade","Jockey One","Jolly Lodger","Josefin Sans","Josefin Slab","Joti One","Judson","Julee","Julius Sans One",
					"Junge","Jura","Just Another Hand","Just Me Again Down Here","Kadwa","Kalam","Kameron","Kanit","Kantumruy","Karla","Karma","Kaushan Script","Kavoon","Kdam Thmor","Keania One","Kelly Slab","Kenia","Khand","Khmer","Khula","Kite One","Knewave","Kotta One","Koulen","Kranky","Kreon","Kristi","Krona One","Kurale","La Belle Aurore","Laila","Lakki Reddy","Lancelot","Lateef","Lato","League Script","Leckerli One","Ledger","Lekton","Lemon","Libre Baskerville","Life Savers","Lilita One","Lily Script One","Limelight","Linden Hill","Lobster","Lobster Two","Londrina Outline","Londrina Shadow","Londrina Sketch","Londrina Solid","Lora","Love Ya Like A Sister","Loved by the King","Lovers Quarrel","Luckiest Guy","Lusitana","Lustria","Macondo","Macondo Swash Caps","Magra","Maiden Orange","Mako","Mallanna","Mandali","Marcellus","Marcellus SC","Marck Script","Margarine","Marko One","Marmelad","Martel","Martel Sans","Marvel","Mate","Mate SC","Maven Pro","McLaren","Meddon","MedievalSharp","Medula One","Megrim","Meie Script","Merienda","Merienda One","Merriweather","Merriweather Sans","Metal","Metal Mania","Metamorphous","Metrophobic","Michroma","Milonga","Miltonian","Miltonian Tattoo","Miniver","Miss Fajardose","Modak","Modern Antiqua","Molengo","Molle","Monda","Monofett","Monoton","Monsieur La Doulaise","Montaga","Montez","Montserrat","Montserrat Alternates","Montserrat Subrayada","Moul","Moulpali","Mountains of Christmas","Mouse Memoirs","Mr Bedfort","Mr Dafoe","Mr De Haviland","Mrs Saint Delafield","Mrs Sheppards","Muli","Mystery Quest","NTR","Neucha","Neuton","New Rocker","News Cycle","Niconne","Nixie One","Nobile","Nokora","Norican","Nosifer","Nothing You Could Do","Noticia Text","Noto Sans","Noto Serif","Nova Cut","Nova Flat","Nova Mono","Nova Oval","Nova Round","Nova Script","Nova Slim","Nova Square","Numans","Nunito","Odor Mean Chey","Offside","Old Standard TT","Oldenburg","Oleo Script","Oleo Script Swash Caps","Open Sans","Open Sans Condensed","Oranienbaum","Orbitron","Oregano","Orienta","Original Surfer","Oswald","Over the Rainbow","Overlock","Overlock SC","Ovo","Oxygen","Oxygen Mono","PT Mono","PT Sans","PT Sans Caption","PT Sans Narrow","PT Serif","PT Serif Caption","Pacifico","Palanquin","Palanquin Dark","Paprika","Parisienne","Passero One","Passion One","Pathway Gothic One","Patrick Hand","Patrick Hand SC","Patua One","Paytone One","Peddana","Peralta","Permanent Marker","Petit Formal Script","Petrona","Philosopher","Piedra","Pinyon Script","Pirata One","Plaster","Play","Playball","Playfair Display","Playfair Display SC","Podkova","Poiret One","Poller One","Poly","Pompiere","Pontano Sans","Poppins","Port Lligat Sans","Port Lligat Slab","Pragati Narrow","Prata","Preahvihear","Press Start 2P","Princess Sofia","Prociono","Prosto One","Puritan","Purple Purse","Quando","Quantico","Quattrocento","Quattrocento Sans","Questrial","Quicksand","Quintessential","Qwigley","Racing Sans One","Radley","Rajdhani","Raleway","Raleway Dots","Ramabhadra","Ramaraja","Rambla","Rammetto One","Ranchers","Rancho","Ranga","Rationale","Ravi Prakash","Redressed","Reenie Beanie","Revalia","Rhodium Libre","Ribeye","Ribeye Marrow","Righteous","Risque","Roboto","Roboto Condensed","Roboto Mono","Roboto Slab","Rochester","Rock Salt","Rokkitt","Romanesco","Ropa Sans","Rosario","Rosarivo","Rouge Script","Rozha One","Rubik","Rubik Mono One","Rubik One","Ruda","Rufina","Ruge Boogie","Ruluko","Rum Raisin","Ruslan Display","Russo One","Ruthie","Rye","Sacramento","Sahitya","Sail","Salsa","Sanchez","Sancreek","Sansita One","Sarala","Sarina","Sarpanch","Satisfy","Scada","Scheherazade","Schoolbell","Seaweed Script","Sevillana","Seymour One","Shadows Into Light","Shadows Into Light Two","Shanti","Share","Share Tech","Share Tech Mono","Shojumaru","Short Stack","Siemreap","Sigmar One","Signika","Signika Negative","Simonetta","Sintony","Sirin Stencil","Six Caps","Skranji","Slabo 13px","Slabo 27px","Slackey","Smokum","Smythe","Sniglet","Snippet","Snowburst One","Sofadi One","Sofia","Sonsie One","Sorts Mill Goudy","Source Code Pro","Source Sans Pro","Source Serif Pro","Special Elite","Spicy Rice","Spinnaker","Spirax","Squada One","Sree Krushnadevaraya","Stalemate","Stalinist One","Stardos Stencil","Stint Ultra Condensed","Stint Ultra Expanded","Stoke","Strait","Sue Ellen Francisco","Sumana","Sunshiney","Supermercado One","Sura","Suranna","Suravaram","Suwannaphum","Swanky and Moo Moo","Syncopate","Tangerine","Taprom","Tauri","Teko","Telex","Tenali Ramakrishna","Tenor Sans","Text Me One","The Girl Next Door","Tienne","Tillana","Timmana","Tinos","Titan One","Titillium Web","Trade Winds","Trocchi","Trochut","Trykker","Tulpen One","Ubuntu","Ubuntu Condensed","Ubuntu Mono","Ultra","Uncial Antiqua","Underdog","Unica One","UnifrakturCook","UnifrakturMaguntia","Unkempt","Unlock","Unna","VT323","Vampiro One","Varela","Varela Round","Vast Shadow","Vesper Libre","Vibur","Vidaloka","Viga","Voces","Volkhov","Vollkorn","Voltaire","Waiting for the Sunrise","Wallpoet","Walter Turncoat","Warnes","Wellfleet","Wendy One","Wire One","Work Sans","Yanone Kaffeesatz","Yantramanav","Yellowtail","Yeseva One","Yesteryear","Zeyada"
				];

		}( jQuery ));
<|endoftext|>


<commit_before>var pause = undefined;
var play = undefined;
var video = undefined;
var canvas = undefined;
var tapesound = undefined;
var staticsound = undefined;

function endstatic()
{
	document.body.style.backgroundImage = "none";
	document.body.style.backgroundColor = "black";

	staticsound.pause();
}

function videoloop()
{
		if (video && !video.paused && !video.ended) {
				context.drawImage(video, 0, 0, canvas.width, canvas.height);
				setTimeout(videoloop, 1000 / 30);
		}
}

function startvid()
{
	canvas.style.display = "inline";
	video.play();
}

function playoff()
{
	play.style.display = "none";
}

function startsound()
{
	tapesound.play();
}

function onplay(e)
{
	pause.style.display = "none";
	play.style.display = "inline";
	var Sky = document.getElementById("Sky");
   	Sky.classList.add("wakethefup");
   	var TopHouse = document.getElementById("TopHouse");
   	TopHouse.classList.add("wakethefup");
	TopHouse.classList.add("hmmyesthisfloorismade");
	setTimeout(playoff, 7000);
	setTimeout(startvid, 5000);
	setTimeout(endstatic, 2000);
	setTimeout(startsound, 1000);
}

function reset()
{
	pause.style.display = "inline";
	play.style.display = "none";
	canvas.style.display = "none";

	video.currentTime = 0;
	video.pause();

	tapesound.currentTime = 0;
	tapesound.pause();

 	staticsound.play();

	var string = "/img/tiled static.gif";
	document.body.style.backgroundImage = "url('" + string + "')";
	document.body.style.backgroundColor = "white";
}

window.onload = function()
{
	pause = document.getElementById("pause");
	play = document.getElementById("play");
	console.log("load successed");
	pause.onclick = onplay;

	tapesound = document.createElement("audio");
	tapesound.src = "/tapefeed.mp3";
	tapesound.pause();

	staticsound = document.createElement("audio");
	staticsound.src = "https://omaniamnotgoodwith.computer/audio/OurMoreMuffledHouse.mp3";
	staticsound.loop = true;
	staticsound.play();

	video = document.createElement("video");
	video.src = "https://oman.imjake.me/OurHouse.mp4";
	video.pause();
	video.addEventListener('play',function(){
		videoloop();
	},false);
	video.addEventListener('ended',function(){
		reset();
	},false);

	canvas = document.getElementById("canvas");
	context = canvas.getContext("2d");

//	canvas.width = canvas.clientWidth;
//	canvas.height = canvas.clientHeight;

	context.clearRect(0, 0, canvas.width, canvas.height);
}
<commit_msg>Update house.js<commit_after>var pause = undefined;
var play = undefined;
var video = undefined;
var canvas = undefined;
var tapesound = undefined;
var staticsound = undefined;

function endstatic()
{
	document.body.style.backgroundImage = "none";
	document.body.style.backgroundColor = "black";

	staticsound.pause();
}

function videoloop()
{
		if (video && !video.paused && !video.ended) {
				context.drawImage(video, 0, 0, canvas.width, canvas.height);
				setTimeout(videoloop, 1000 / 30);
		}
}

function startvid()
{
	canvas.style.display = "inline";
	video.play();
}

function playoff()
{
	play.style.display = "none";
}

function startsound()
{
	tapesound.play();
}

function onplay(e)
{
	pause.style.display = "none";
	play.style.display = "inline";
	var Sky = document.getElementById("Sky");
   	Sky.classList.add("wakethefup");
   	var TopHouse = document.getElementById("TopHouse");
   	TopHouse.classList.add("wakethefup");
	TopHouse.classList.add("hmmyesthisfloorismade");
	setTimeout(playoff, 7000);
	setTimeout(startvid, 5000);
	setTimeout(endstatic, 2000);
	setTimeout(startsound, 1000);
}

function reset()
{
	pause.style.display = "inline";
	play.style.display = "none";
	canvas.style.display = "none";

	video.currentTime = 0;
	video.pause();

	tapesound.currentTime = 0;
	tapesound.pause();

 	staticsound.play();

	var string = "/img/tiled static.gif";
	document.body.style.backgroundImage = "url('" + string + "')";
	document.body.style.backgroundColor = "white";
}

window.onload = function()
{
	pause = document.getElementById("pause");
	play = document.getElementById("play");
	console.log("load successed");
	pause.onclick = onplay;

	tapesound = document.createElement("audio");
	tapesound.src = "/tapefeed.mp3";
	tapesound.pause();

	staticsound = document.createElement("audio");
	staticsound.src = "https://omaniamnotgoodwith.computer/audio/OurMoreMuffledHouse.mp3";
	staticsound.loop = true;
	staticsound.play();

	video = document.createElement("video");
	video.src = "https://oman.imjake.me/OurHouse.mp4";
	video.pause();
	video.addEventListener('play',function(){
		videoloop();
	},false);
	video.addEventListener('ended',function(){
		reset();
	},false);

	canvas = document.getElementById("canvas");
	context = canvas.getContext("2d");

	canvas.width = window.innerWidth;
	canvas.height = window.innerWidth;

	context.clearRect(0, 0, canvas.width, canvas.height);
}
<|endoftext|>


<commit_before>// Copyright 2016-2021, University of Colorado Boulder

/**
 * Spinner for numbers.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Andrea Lin (PhET Interactive Simulations)
 */

import merge from '../../phet-core/js/merge.js';
import NumberDisplay from '../../scenery-phet/js/NumberDisplay.js';
import Node from '../../scenery/js/nodes/Node.js';
import SceneryConstants from '../../scenery/js/SceneryConstants.js';
import Tandem from '../../tandem/js/Tandem.js';
import AccessibleNumberSpinner from './accessibility/AccessibleNumberSpinner.js';
import ArrowButton from './buttons/ArrowButton.js';
import sun from './sun.js';

// possible values for options.arrowsPosition
const ARROWS_POSITION_VALUES = [
  'leftRight', // arrow buttons on left and right of value
  'topBottom', // arrow buttons on top and bottom of value
  'bothRight', // both arrow buttons to the right of the value
  'bothBottom' // both arrow buttons below the value
];

class NumberSpinner extends Node {

  /**
   * @param {Property.<number>} numberProperty value, must be an integer
   * @param {Property.<Range>} rangeProperty - Dynamic range of values, min and max must be integers
   * @param {Object} [options]
   * @mixes AccessibleNumberSpinner
   */
  constructor( numberProperty, rangeProperty, options ) {

    assert && assert( rangeProperty.value.contains( numberProperty.get() ),
      `value ${numberProperty.get()} is out of range ${rangeProperty.value.toString()}` );

    options = merge( {

      // {string} where to place the arrow buttons, see ARROWS_POSITION_VALUES
      arrowsPosition: 'bothRight',

      // {number|null} By default, arrows are scaled to fit dimensions of value background. This is an additional scale factor.
      arrowsScale: null,
      arrowButtonFill: 'white',
      arrowButtonStroke: 'black',
      arrowButtonLineWidth: 1,

      // {function(number):number|null} function called when the increment button is pressed.
      // Parameter is the current value, returns the new value. Defaults to adding options.deltaValue.
      incrementFunction: null,

      // {function(number):number|null} function called when the decrement button is pressed.
      // Parameter is the current value, returns the new value. Defaults to subtracting options.deltaValue.
      decrementFunction: null,

      // may be ignored if incrementFunction and decrementFunction are provided
      deltaValue: 1,

      // {number} - opt into Node's disabled opacity when enabled:false
      disabledOpacity: SceneryConstants.DISABLED_OPACITY,
      xSpacing: 5,
      ySpacing: 3,

      // NumberDisplay options
      numberDisplayOptions: {
        cornerRadius: 5,
        backgroundStroke: 'black'
      },

      // arrow button pointer areas
      touchAreaXDilation: 0,
      touchAreaYDilation: 0,
      mouseAreaXDilation: 0,
      mouseAreaYDilation: 0,

      // PhET-iO
      tandem: Tandem.REQUIRED,
      phetioEnabledPropertyInstrumented: true // opt into default PhET-iO instrumented enabledProperty
    }, options );

    // validate options
    assert && assert( _.includes( ARROWS_POSITION_VALUES, options.arrowsPosition ), `invalid arrowsPosition: ${options.arrowsPosition}` );
    assert && assert( options.numberDisplayOptions.tandem === undefined, 'NumberSpinner sets tandem for NumberDisplay' );

    // Defaults for incrementFunction and decrementFunction
    if ( !options.incrementFunction ) {
      options.incrementFunction = value => value + options.deltaValue;
    }
    if ( !options.decrementFunction ) {
      options.decrementFunction = value => value - options.deltaValue;
    }

    const numberDisplay = new NumberDisplay( numberProperty, rangeProperty.value, merge( {},
      options.numberDisplayOptions, {
        tandem: options.tandem.createTandem( 'numberDisplay' )
      } ) );

    // buttons
    const arrowButtonOptions = {
      baseColor: options.arrowButtonFill,
      stroke: options.arrowButtonStroke,
      lineWidth: options.arrowButtonLineWidth,
      focusable: false,

      // as requested in https://github.com/phetsims/sun/issues/575
      enabledPropertyOptions: {
        phetioReadOnly: true,
        phetioFeatured: false
      }
    };

    // increment button
    const incrementDirection = ( options.arrowsPosition === 'topBottom' || options.arrowsPosition === 'bothRight' ) ? 'up' : 'right';
    const incrementButton = new ArrowButton( incrementDirection,
      () => incrementButton.enabled && numberProperty.set( options.incrementFunction( numberProperty.get() ) ),
      merge( {
        tandem: options.tandem.createTandem( 'incrementButton' )
      }, arrowButtonOptions ) );

    // decrement button
    const decrementDirection = ( options.arrowsPosition === 'topBottom' || options.arrowsPosition === 'bothRight' ) ? 'down' : 'left';
    const decrementButton = new ArrowButton( decrementDirection,
      () => decrementButton.enabled && numberProperty.set( options.decrementFunction( numberProperty.get() ) ),
      merge( {
        tandem: options.tandem.createTandem( 'decrementButton' )
      }, arrowButtonOptions ) );

    // arrow button scaling
    let arrowsScale;
    if ( !arrowsScale ) {
      if ( options.arrowsPosition === 'leftRight' ) {
        arrowsScale = numberDisplay.height / incrementButton.height;
      }
      else if ( options.arrowsPosition === 'topBottom' ) {
        arrowsScale = numberDisplay.width / incrementButton.width;
      }
      else if ( options.arrowsPosition === 'bothRight' ) {
        arrowsScale = ( ( numberDisplay.height / 2 ) - ( options.ySpacing / 2 ) ) / incrementButton.height;
      }
      else { // 'bothBottom'
        arrowsScale = ( ( numberDisplay.width / 2 ) - ( options.xSpacing / 2 ) ) / incrementButton.width;
      }
    }
    if ( options.arrowsScale ) {
      arrowsScale = arrowsScale * options.arrowsScale;
    }
    incrementButton.setScaleMagnitude( arrowsScale );
    decrementButton.setScaleMagnitude( arrowsScale );

    // Because range may change via rangeProperty, the size of numberDisplay may change, in which case layout will need
    // to be revised. See https://github.com/phetsims/sun/issues/709
    const updateLayout = () => {
      console.log( 'NumberSpinner.updateLayout' );
      if ( options.arrowsPosition === 'leftRight' ) {
        incrementButton.left = numberDisplay.right + options.xSpacing;
        decrementButton.right = numberDisplay.left - options.xSpacing;
        incrementButton.centerY = decrementButton.centerY = numberDisplay.centerY;
      }
      else if ( options.arrowsPosition === 'topBottom' ) {
        incrementButton.centerX = decrementButton.centerX = numberDisplay.centerX;
        incrementButton.bottom = numberDisplay.top - options.ySpacing;
        decrementButton.top = numberDisplay.bottom + options.ySpacing;
      }
      else if ( options.arrowsPosition === 'bothRight' ) {
        incrementButton.left = decrementButton.left = numberDisplay.right + options.xSpacing;
        incrementButton.bottom = numberDisplay.centerY - ( options.ySpacing / 2 );
        decrementButton.top = numberDisplay.centerY + ( options.ySpacing / 2 );
      }
      else { // 'bothBottom'
        incrementButton.left = numberDisplay.centerX + ( options.xSpacing / 2 );
        decrementButton.right = numberDisplay.centerX - ( options.xSpacing / 2 );
        incrementButton.top = decrementButton.top = numberDisplay.bottom + options.ySpacing;
      }
    };
    numberDisplay.boundsProperty.link( () => updateLayout() );

    // touch areas
    if ( options.touchAreaXDilation || options.touchAreaYDilation ) {

      incrementButton.touchArea = incrementButton.localBounds.dilatedXY( options.touchAreaXDilation, options.touchAreaYDilation );
      decrementButton.touchArea = decrementButton.localBounds.dilatedXY( options.touchAreaXDilation, options.touchAreaYDilation );

      // shift touch areas for these options, to prevent overlap
      if ( options.arrowsPosition === 'bothRight' ) {
        incrementButton.touchArea = incrementButton.touchArea.shiftedY( -options.touchAreaYDilation );
        decrementButton.touchArea = decrementButton.touchArea.shiftedY( options.touchAreaYDilation );
      }
      else if ( options.arrowsPosition === 'bothBottom' ) {
        incrementButton.touchArea = incrementButton.touchArea.shiftedX( options.touchAreaXDilation );
        decrementButton.touchArea = decrementButton.touchArea.shiftedX( -options.touchAreaXDilation );
      }
    }

    // mouse areas
    if ( options.mouseAreaXDilation || options.mouseAreaYDilation ) {

      incrementButton.mouseArea = incrementButton.localBounds.dilatedXY( options.mouseAreaXDilation, options.mouseAreaYDilation );
      decrementButton.mouseArea = decrementButton.localBounds.dilatedXY( options.mouseAreaXDilation, options.mouseAreaYDilation );

      // shift touch areas for these options, to prevent overlap
      if ( options.arrowsPosition === 'bothRight' ) {
        incrementButton.mouseArea = incrementButton.mouseArea.shiftedY( -options.mouseAreaYDilation );
        decrementButton.mouseArea = decrementButton.mouseArea.shiftedY( options.mouseAreaYDilation );
      }
      else if ( options.arrowsPosition === 'bothBottom' ) {
        incrementButton.mouseArea = incrementButton.mouseArea.shiftedX( options.mouseAreaXDilation );
        decrementButton.mouseArea = decrementButton.mouseArea.shiftedX( -options.mouseAreaXDilation );
      }
    }

    assert && assert( !options.children, 'decoration not supported' );
    options.children = [ numberDisplay, incrementButton, decrementButton ];

    super( options );

    // enable/disable arrow buttons
    const updateEnabled = () => {
      incrementButton.enabled = ( options.incrementFunction( numberProperty.value ) <= rangeProperty.value.max );
      decrementButton.enabled = ( options.decrementFunction( numberProperty.value ) >= rangeProperty.value.min );
    };

    // synchronize with number value
    const numberPropertyObserver = value => {
      assert && assert( rangeProperty.value.contains( value ), `value out of range: ${value}` );
      updateEnabled();
    };
    numberProperty.link( numberPropertyObserver ); // must be unlinked in dispose

    // Dynamic range changes, see https://github.com/phetsims/scenery-phet/issues/305
    const rangeObserver = range => {
      // If our value is outside our new range, adjust it to be within the range.
      numberProperty.value = range.constrainValue( numberProperty.value );

      // Range changes may change whether the buttons are enabled
      updateEnabled();
    };
    rangeProperty.link( rangeObserver );

    // pdom - NumberSpinner uses AccessibleValueHandler for accessibility, but it was decided that keyboardStep
    // and shiftKeyboardStep should have the same behavior as the NumberSpinner ArrowButtons AND the ArrowButtons
    // should look depressed when interacting with those keys. To accomplish this we actually press the ArrowButtons
    // in response to input with those keys. keyboardStep and shiftKeyboardStep are set to zero so the value isn't
    // modified again by AccessibleValueHandler.
    assert && assert( !options.keyboardStep, 'NumberSpinner sets keyboardStep, it will be the same as deltaValue' );
    assert && assert( !options.shiftKeyboardStep, 'NumberSpinner sets shiftKeyboardStep, it will be the same as deltaValue' );
    options.keyboardStep = 0;
    options.shiftKeyboardStep = 0;
    this.initializeAccessibleNumberSpinner( numberProperty, rangeProperty, this.enabledProperty, options );

    // pdom - click arrow buttons on press of arrow keys so that the Property value changes by deltaValue
    // and the buttons look depressed
    const increasedListener = function( isDown ) { isDown && incrementButton.pdomClick(); };
    const decreasedListener = function( isDown ) { isDown && decrementButton.pdomClick(); };
    this.incrementDownEmitter.addListener( increasedListener );
    this.decrementDownEmitter.addListener( decreasedListener );

    // Create a link to associated Property, so it's easier to find in Studio.
    this.addLinkedElement( numberProperty, {
      tandem: options.tandem.createTandem( 'property' )
    } );

    // @private
    this.disposeNumberSpinner = () => {

      // dispose a11y features
      this.incrementDownEmitter.removeListener( increasedListener );
      this.decrementDownEmitter.removeListener( decreasedListener );

      numberProperty.unlink( numberPropertyObserver );
      rangeProperty.unlink( rangeObserver );
    };
  }

  // @public Ensures that this node is eligible for GC.
  dispose() {
    this.disposeNumberSpinner();
    this.disposeAccessibleNumberSpinner();
    super.dispose();
  }
}

AccessibleNumberSpinner.mixInto( NumberSpinner );

sun.register( 'NumberSpinner', NumberSpinner );
export default NumberSpinner;<commit_msg>delete console.log, https://github.com/phetsims/sun/issues/709, https://github.com/phetsims/natural-selection/issues/299<commit_after>// Copyright 2016-2021, University of Colorado Boulder

/**
 * Spinner for numbers.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Andrea Lin (PhET Interactive Simulations)
 */

import merge from '../../phet-core/js/merge.js';
import NumberDisplay from '../../scenery-phet/js/NumberDisplay.js';
import Node from '../../scenery/js/nodes/Node.js';
import SceneryConstants from '../../scenery/js/SceneryConstants.js';
import Tandem from '../../tandem/js/Tandem.js';
import AccessibleNumberSpinner from './accessibility/AccessibleNumberSpinner.js';
import ArrowButton from './buttons/ArrowButton.js';
import sun from './sun.js';

// possible values for options.arrowsPosition
const ARROWS_POSITION_VALUES = [
  'leftRight', // arrow buttons on left and right of value
  'topBottom', // arrow buttons on top and bottom of value
  'bothRight', // both arrow buttons to the right of the value
  'bothBottom' // both arrow buttons below the value
];

class NumberSpinner extends Node {

  /**
   * @param {Property.<number>} numberProperty value, must be an integer
   * @param {Property.<Range>} rangeProperty - Dynamic range of values, min and max must be integers
   * @param {Object} [options]
   * @mixes AccessibleNumberSpinner
   */
  constructor( numberProperty, rangeProperty, options ) {

    assert && assert( rangeProperty.value.contains( numberProperty.get() ),
      `value ${numberProperty.get()} is out of range ${rangeProperty.value.toString()}` );

    options = merge( {

      // {string} where to place the arrow buttons, see ARROWS_POSITION_VALUES
      arrowsPosition: 'bothRight',

      // {number|null} By default, arrows are scaled to fit dimensions of value background. This is an additional scale factor.
      arrowsScale: null,
      arrowButtonFill: 'white',
      arrowButtonStroke: 'black',
      arrowButtonLineWidth: 1,

      // {function(number):number|null} function called when the increment button is pressed.
      // Parameter is the current value, returns the new value. Defaults to adding options.deltaValue.
      incrementFunction: null,

      // {function(number):number|null} function called when the decrement button is pressed.
      // Parameter is the current value, returns the new value. Defaults to subtracting options.deltaValue.
      decrementFunction: null,

      // may be ignored if incrementFunction and decrementFunction are provided
      deltaValue: 1,

      // {number} - opt into Node's disabled opacity when enabled:false
      disabledOpacity: SceneryConstants.DISABLED_OPACITY,
      xSpacing: 5,
      ySpacing: 3,

      // NumberDisplay options
      numberDisplayOptions: {
        cornerRadius: 5,
        backgroundStroke: 'black'
      },

      // arrow button pointer areas
      touchAreaXDilation: 0,
      touchAreaYDilation: 0,
      mouseAreaXDilation: 0,
      mouseAreaYDilation: 0,

      // PhET-iO
      tandem: Tandem.REQUIRED,
      phetioEnabledPropertyInstrumented: true // opt into default PhET-iO instrumented enabledProperty
    }, options );

    // validate options
    assert && assert( _.includes( ARROWS_POSITION_VALUES, options.arrowsPosition ), `invalid arrowsPosition: ${options.arrowsPosition}` );
    assert && assert( options.numberDisplayOptions.tandem === undefined, 'NumberSpinner sets tandem for NumberDisplay' );

    // Defaults for incrementFunction and decrementFunction
    if ( !options.incrementFunction ) {
      options.incrementFunction = value => value + options.deltaValue;
    }
    if ( !options.decrementFunction ) {
      options.decrementFunction = value => value - options.deltaValue;
    }

    const numberDisplay = new NumberDisplay( numberProperty, rangeProperty.value, merge( {},
      options.numberDisplayOptions, {
        tandem: options.tandem.createTandem( 'numberDisplay' )
      } ) );

    // buttons
    const arrowButtonOptions = {
      baseColor: options.arrowButtonFill,
      stroke: options.arrowButtonStroke,
      lineWidth: options.arrowButtonLineWidth,
      focusable: false,

      // as requested in https://github.com/phetsims/sun/issues/575
      enabledPropertyOptions: {
        phetioReadOnly: true,
        phetioFeatured: false
      }
    };

    // increment button
    const incrementDirection = ( options.arrowsPosition === 'topBottom' || options.arrowsPosition === 'bothRight' ) ? 'up' : 'right';
    const incrementButton = new ArrowButton( incrementDirection,
      () => incrementButton.enabled && numberProperty.set( options.incrementFunction( numberProperty.get() ) ),
      merge( {
        tandem: options.tandem.createTandem( 'incrementButton' )
      }, arrowButtonOptions ) );

    // decrement button
    const decrementDirection = ( options.arrowsPosition === 'topBottom' || options.arrowsPosition === 'bothRight' ) ? 'down' : 'left';
    const decrementButton = new ArrowButton( decrementDirection,
      () => decrementButton.enabled && numberProperty.set( options.decrementFunction( numberProperty.get() ) ),
      merge( {
        tandem: options.tandem.createTandem( 'decrementButton' )
      }, arrowButtonOptions ) );

    // arrow button scaling
    let arrowsScale;
    if ( !arrowsScale ) {
      if ( options.arrowsPosition === 'leftRight' ) {
        arrowsScale = numberDisplay.height / incrementButton.height;
      }
      else if ( options.arrowsPosition === 'topBottom' ) {
        arrowsScale = numberDisplay.width / incrementButton.width;
      }
      else if ( options.arrowsPosition === 'bothRight' ) {
        arrowsScale = ( ( numberDisplay.height / 2 ) - ( options.ySpacing / 2 ) ) / incrementButton.height;
      }
      else { // 'bothBottom'
        arrowsScale = ( ( numberDisplay.width / 2 ) - ( options.xSpacing / 2 ) ) / incrementButton.width;
      }
    }
    if ( options.arrowsScale ) {
      arrowsScale = arrowsScale * options.arrowsScale;
    }
    incrementButton.setScaleMagnitude( arrowsScale );
    decrementButton.setScaleMagnitude( arrowsScale );

    // Because range may change via rangeProperty, the size of numberDisplay may change, in which case layout will need
    // to be revised. See https://github.com/phetsims/sun/issues/709
    const updateLayout = () => {
      if ( options.arrowsPosition === 'leftRight' ) {
        incrementButton.left = numberDisplay.right + options.xSpacing;
        decrementButton.right = numberDisplay.left - options.xSpacing;
        incrementButton.centerY = decrementButton.centerY = numberDisplay.centerY;
      }
      else if ( options.arrowsPosition === 'topBottom' ) {
        incrementButton.centerX = decrementButton.centerX = numberDisplay.centerX;
        incrementButton.bottom = numberDisplay.top - options.ySpacing;
        decrementButton.top = numberDisplay.bottom + options.ySpacing;
      }
      else if ( options.arrowsPosition === 'bothRight' ) {
        incrementButton.left = decrementButton.left = numberDisplay.right + options.xSpacing;
        incrementButton.bottom = numberDisplay.centerY - ( options.ySpacing / 2 );
        decrementButton.top = numberDisplay.centerY + ( options.ySpacing / 2 );
      }
      else { // 'bothBottom'
        incrementButton.left = numberDisplay.centerX + ( options.xSpacing / 2 );
        decrementButton.right = numberDisplay.centerX - ( options.xSpacing / 2 );
        incrementButton.top = decrementButton.top = numberDisplay.bottom + options.ySpacing;
      }
    };
    numberDisplay.boundsProperty.link( () => updateLayout() );

    // touch areas
    if ( options.touchAreaXDilation || options.touchAreaYDilation ) {

      incrementButton.touchArea = incrementButton.localBounds.dilatedXY( options.touchAreaXDilation, options.touchAreaYDilation );
      decrementButton.touchArea = decrementButton.localBounds.dilatedXY( options.touchAreaXDilation, options.touchAreaYDilation );

      // shift touch areas for these options, to prevent overlap
      if ( options.arrowsPosition === 'bothRight' ) {
        incrementButton.touchArea = incrementButton.touchArea.shiftedY( -options.touchAreaYDilation );
        decrementButton.touchArea = decrementButton.touchArea.shiftedY( options.touchAreaYDilation );
      }
      else if ( options.arrowsPosition === 'bothBottom' ) {
        incrementButton.touchArea = incrementButton.touchArea.shiftedX( options.touchAreaXDilation );
        decrementButton.touchArea = decrementButton.touchArea.shiftedX( -options.touchAreaXDilation );
      }
    }

    // mouse areas
    if ( options.mouseAreaXDilation || options.mouseAreaYDilation ) {

      incrementButton.mouseArea = incrementButton.localBounds.dilatedXY( options.mouseAreaXDilation, options.mouseAreaYDilation );
      decrementButton.mouseArea = decrementButton.localBounds.dilatedXY( options.mouseAreaXDilation, options.mouseAreaYDilation );

      // shift touch areas for these options, to prevent overlap
      if ( options.arrowsPosition === 'bothRight' ) {
        incrementButton.mouseArea = incrementButton.mouseArea.shiftedY( -options.mouseAreaYDilation );
        decrementButton.mouseArea = decrementButton.mouseArea.shiftedY( options.mouseAreaYDilation );
      }
      else if ( options.arrowsPosition === 'bothBottom' ) {
        incrementButton.mouseArea = incrementButton.mouseArea.shiftedX( options.mouseAreaXDilation );
        decrementButton.mouseArea = decrementButton.mouseArea.shiftedX( -options.mouseAreaXDilation );
      }
    }

    assert && assert( !options.children, 'decoration not supported' );
    options.children = [ numberDisplay, incrementButton, decrementButton ];

    super( options );

    // enable/disable arrow buttons
    const updateEnabled = () => {
      incrementButton.enabled = ( options.incrementFunction( numberProperty.value ) <= rangeProperty.value.max );
      decrementButton.enabled = ( options.decrementFunction( numberProperty.value ) >= rangeProperty.value.min );
    };

    // synchronize with number value
    const numberPropertyObserver = value => {
      assert && assert( rangeProperty.value.contains( value ), `value out of range: ${value}` );
      updateEnabled();
    };
    numberProperty.link( numberPropertyObserver ); // must be unlinked in dispose

    // Dynamic range changes, see https://github.com/phetsims/scenery-phet/issues/305
    const rangeObserver = range => {
      // If our value is outside our new range, adjust it to be within the range.
      numberProperty.value = range.constrainValue( numberProperty.value );

      // Range changes may change whether the buttons are enabled
      updateEnabled();
    };
    rangeProperty.link( rangeObserver );

    // pdom - NumberSpinner uses AccessibleValueHandler for accessibility, but it was decided that keyboardStep
    // and shiftKeyboardStep should have the same behavior as the NumberSpinner ArrowButtons AND the ArrowButtons
    // should look depressed when interacting with those keys. To accomplish this we actually press the ArrowButtons
    // in response to input with those keys. keyboardStep and shiftKeyboardStep are set to zero so the value isn't
    // modified again by AccessibleValueHandler.
    assert && assert( !options.keyboardStep, 'NumberSpinner sets keyboardStep, it will be the same as deltaValue' );
    assert && assert( !options.shiftKeyboardStep, 'NumberSpinner sets shiftKeyboardStep, it will be the same as deltaValue' );
    options.keyboardStep = 0;
    options.shiftKeyboardStep = 0;
    this.initializeAccessibleNumberSpinner( numberProperty, rangeProperty, this.enabledProperty, options );

    // pdom - click arrow buttons on press of arrow keys so that the Property value changes by deltaValue
    // and the buttons look depressed
    const increasedListener = function( isDown ) { isDown && incrementButton.pdomClick(); };
    const decreasedListener = function( isDown ) { isDown && decrementButton.pdomClick(); };
    this.incrementDownEmitter.addListener( increasedListener );
    this.decrementDownEmitter.addListener( decreasedListener );

    // Create a link to associated Property, so it's easier to find in Studio.
    this.addLinkedElement( numberProperty, {
      tandem: options.tandem.createTandem( 'property' )
    } );

    // @private
    this.disposeNumberSpinner = () => {

      // dispose a11y features
      this.incrementDownEmitter.removeListener( increasedListener );
      this.decrementDownEmitter.removeListener( decreasedListener );

      numberProperty.unlink( numberPropertyObserver );
      rangeProperty.unlink( rangeObserver );
    };
  }

  // @public Ensures that this node is eligible for GC.
  dispose() {
    this.disposeNumberSpinner();
    this.disposeAccessibleNumberSpinner();
    super.dispose();
  }
}

AccessibleNumberSpinner.mixInto( NumberSpinner );

sun.register( 'NumberSpinner', NumberSpinner );
export default NumberSpinner;<|endoftext|>


<commit_before>const settings = require('electron-settings')

let authList = []

function load() {
  authList = settings.getSync('authList', [])
}

function save() {
  settings.setSync('authList', authList)
}

function getAll() {
  if (!authList) {
    load()
  }
  return authList
}

function findByUUID(uuid) {
  return getAll().find(auth => auth.uuid == uuid)
}

function removeByUUID(uuid) {
  let list = getAll()
  authList = list.filter(auth => auth.uuid != uuid)
  save()
  return authList
}

function add(auth) {
  let list = getAll()
  list.push(auth)
  authList = list
  save()
}

module.exports = {
  getAll: getAll,
  findByUUID: findByUUID,
  removeByUUID: removeByUUID,
  add: add
}
<commit_msg>authList 초기화 되지 않는 버그 수정<commit_after>const settings = require('electron-settings')

let initialized = false
let authList = []

function load() {
  authList = settings.getSync('authList', [])
  console.log("load authList", authList)
}

function save() {
  settings.setSync('authList', authList)
}

function getAll() {
  if (!initialized) {
    load()
    initialized = true
  }
  return authList
}

function findByUUID(uuid) {
  return getAll().find(auth => auth.uuid == uuid)
}

function removeByUUID(uuid) {
  let list = getAll()
  authList = list.filter(auth => auth.uuid != uuid)
  save()
  return authList
}

function add(auth) {
  let list = getAll()
  list.push(auth)
  authList = list
  save()
}

module.exports = {
  getAll: getAll,
  findByUUID: findByUUID,
  removeByUUID: removeByUUID,
  add: add
}
<|endoftext|>


<commit_before>
'use strict';

var util = require('util');

var templateLoader = require('../util/template-loader');
var dot = require('dot');
dot.templateSettings.strip = false;

var id = require('../util/id');
var QueryBuilder = require('../filter/query-builder');
var Validator = require('./validator');
var limits = require('./limits');

var TYPE = require('./type');
module.exports.TYPE = TYPE;
var GEOMETRY = require('./geometry');
module.exports.GEOMETRY = GEOMETRY;

var STATUS = {
    // transitional status
    PENDING: 'pending',
    WAITING: 'waiting',
    RUNNING: 'running',

    // final status
    FAILED: 'failed',
    READY: 'ready'
};

module.exports.STATUS = STATUS;

var NODE_RESERVED_KEYWORDS = {
    type: 1,
    typeSignature: 1,
    params: 1,
    status: 1,
    errorMessage: 1,
    filters: 1,
    owner: 1,
    schema: 1,
    columns: 1,
    cacheQuery: 1,
    cacheQueryTimeout: 1,
    queuedWork: 1,
    lazy: 1,
    updatedAt: 1,
    validateAndBuild: 1,
    json: 1,
    attributesForId: 1,
    version: 1,
    nodes: 1,
    inputNodes: 1,
    attrs: 1,
    tags: 1,
    setStatusFromInputNodes: 1,
    affectedTables: 1
};

function Node() {
    // args will look like ['owner', {param1: '', param2: ''}, {param1: '', param3: ''}, ...]
    var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));
    this.owner = args.shift();

    this.params = args.reduce(function(reducedParams, arg) {
        Object.keys(arg).forEach(function(paramName) {
            reducedParams[paramName] = arg[paramName];
        });
        return reducedParams;
    }, {});

    this.status = STATUS.PENDING;
    this.errorMessage = null;
    this.queuedWork = false;
    this.cacheQueryTimeout = 0;

    this.filters = this.params.filters || {};

    this.schema = {};
    this.columns = [];

    this.nodes = {};
    this.inputNodes = [];

    this.updatedAt = null;
    this.affectedTables = [];

    this.attrs = {};

    this.json = {};
    this.attributesForId = [];

    this.typeSignature = null;

    this.version = 0;
}

Node.prototype.id = function(skipFilters) {
    var attrsToSkip = {};
    if (skipFilters || false) {
        attrsToSkip.filters = true;
    }

    var json = Object.keys(this.json).reduce(function(json, paramName) {
        if (!attrsToSkip.hasOwnProperty(paramName)) {
            json[paramName] = this.json[paramName];
        }
        return json;
    }.bind(this), {});

    return id({
        // Internal version.
        // Bump this for backwards incompatible changes.
        __version__: 1,
        owner: this.owner,
        json: json,
        version: this.version,
        attrs: this.attributesForId.map(function(attr) {
            return this.hasOwnProperty(attr) ? this[attr] : attr;
        }.bind(this))
    });
};

Node.prototype.cachedNodeId = function() {
    return this.shouldCacheQuery() ? this.id(true) : this.id();
};

Node.prototype.setAttributeToModifyId = function(attr) {
    this.attributesForId.push(attr);
};

Node.prototype.getQuery = function(applyFilters) {
    var query = this.sql();
    if (this.shouldCacheQuery()) {
        query = 'select * from ' + this.getTargetTable();
    }
    if (applyFilters === false) {
        applyFilters = Object.keys(this.filters).reduce(function(skipAllFilters, filterName) {
            skipAllFilters[filterName] = false;
            return skipAllFilters;
        }, {});
    }
    return QueryBuilder.getSql(query, this.filters, applyFilters, this.schema);
};

Node.prototype.getColumns = function(skipTheGeoms) {
    skipTheGeoms = skipTheGeoms || false;
    return (skipTheGeoms) ? skipColumns(this.columns) : this.columns;
};

Node.prototype.getFilters = function() {
    return this.filters;
};

Node.prototype.getOwner = function() {
    return this.owner;
};

Node.prototype.getType = function() {
    return this.type;
};

Node.prototype.getTypeSignature = function() {
    if (this.typeSignature === null) {
        this.typeSignature = id(this.getType()).substring(0, 10);
    }
    return this.typeSignature;
};

var SKIP_COLUMNS = {
    'the_geom': true,
    'the_geom_webmercator': true
};

function skipColumns(columnNames) {
    return columnNames
        .filter(function(columnName) { return !SKIP_COLUMNS[columnName]; });
}

Node.prototype.setColumns = function(columns) {
    this.schema = columns.reduce(function(schema, column) {
        schema[column.name] = column.type;
        return schema;
    }, {});
    this.setColumnsNames(columns.map(function(column) {
        return column.name;
    }));
};

Node.prototype.setColumnsNames = function(columnNames) {
    this.columns = columnNames;
};

Node.prototype.getStatus = function() {
    return this.status;
};

var VALID_STATUS = Object.keys(STATUS).reduce(function(valid, key) {
    valid[STATUS[key]] = true;
    return valid;
}, {});

Node.prototype.setStatus = function(status) {
    if (!VALID_STATUS.hasOwnProperty(status)) {
        return false;
    }
    this.status = status;
};

Node.prototype.getErrorMessage = function() {
    return this.errorMessage;
};

Node.prototype.setErrorMessage = function(errorMessage) {
    this.errorMessage = errorMessage;
};

Node.prototype.getUpdatedAt = function() {
    return this.updatedAt;
};

Node.prototype.setUpdatedAt = function(updatedAt) {
    this.updatedAt = (updatedAt === null) ? null : new Date(updatedAt);
};

Node.prototype.getAffectedTables = function() {
    return this.affectedTables;
};

Node.prototype.addAffectedTables = function(affectedTables) {
    var tables = affectedTables;
    if (!Array.isArray(tables))  {
        tables = [affectedTables];
    }
    this.affectedTables = this.affectedTables.concat(tables);
};

Node.prototype.getLastUpdatedAtFromInputNodes = function() {
    if (this.getInputNodes().length === 0) {
        return this.getUpdatedAt();
    }

    return this.getInputNodes().reduce(function(lastUpdatedAt, inputNode) {
        var inputNodeUpdatedAt = inputNode.getLastUpdatedAtFromInputNodes();
        if (lastUpdatedAt === null) {
            lastUpdatedAt = inputNodeUpdatedAt;
        }

        if (lastUpdatedAt !== null &&
            inputNodeUpdatedAt !== null &&
            inputNodeUpdatedAt.getTime() > lastUpdatedAt.getTime()) {

            lastUpdatedAt = inputNodeUpdatedAt;
        }

        return lastUpdatedAt;
    }, null);
};

Node.prototype.isOutdated = function() {
    var lastUpdatedAtFromInputNodes = this.getLastUpdatedAtFromInputNodes();
    var updatedAt = this.getUpdatedAt();
    return updatedAt === null || lastUpdatedAtFromInputNodes === null ||
        (lastUpdatedAtFromInputNodes.getTime() > updatedAt.getTime());
};

function shouldBeInWaitingStatus(inputNodeStatus) {
    return inputNodeStatus === STATUS.PENDING || inputNodeStatus === STATUS.RUNNING;
}
Node.prototype.setStatusFromInputNodes = function() {
    var shouldCacheNode = this.shouldCacheQuery();
    this.status = this.getInputNodes().reduce(function(currentNodeStatus, node) {
        if (currentNodeStatus === STATUS.FAILED || currentNodeStatus === STATUS.WAITING) {
            return currentNodeStatus;
        }

        var inputNodeStatus = node.getStatus();

        if (inputNodeStatus === STATUS.FAILED) {
            return STATUS.FAILED;
        }

        if (shouldBeInWaitingStatus(inputNodeStatus)) {
            return STATUS.WAITING;
        }

        return shouldCacheNode ? STATUS.PENDING : STATUS.READY;
    }, STATUS.PENDING);
};

Node.prototype.getInputNodes = function() {
    return this.inputNodes;
};

Node.prototype.getAllInputNodes = function(filterFn) {
    filterFn = filterFn || function() { return true; };

    function getAllInputNodes(node, nodeList) {
        node.getInputNodes().forEach(function(inputNode) {
            nodeList.push(inputNode);
            getAllInputNodes(inputNode, nodeList);
        });

        return nodeList;
    }

    return getAllInputNodes(this, []).filter(filterFn);
};

Node.prototype.shouldCacheQuery = function() {
    return this.cacheQuery;
};

Node.prototype.setQueuedWork = function(queuedWork) {
    this.queuedWork = queuedWork;
};

Node.prototype.didQueueWork = function() {
    return this.queuedWork;
};

Node.prototype.getCacheQueryTimeout = function() {
    return this.cacheQueryTimeout;
};

Node.prototype.setCacheQueryTimeout = function(cacheQueryTimeout) {
    this.cacheQueryTimeout = cacheQueryTimeout;
};

Node.prototype.getTags = function() {
    return this.tags;
};

Node.prototype.ignoreParamForId = function(paramName) {
    delete this.json[paramName];
};

Node.prototype.getTargetTable = function() {
    // Keep name at 60 chars
    // Ref.: https://www.postgresql.org/docs/9.5/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
    return 'analysis_' + this.getTypeSignature() + '_' + this.cachedNodeId();
};

Node.prototype.toJSON = function() {
    return this.json;
};

Node.prototype.toDot = function() {
    return {
        type: this.type,
        nodes: this.nodes,
        attrs: this.attrs
    };
};

module.exports.PARAM = {
    NODE: function() {
        var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));
        return new Validator.Node(args);
    },
    NUMBER: function() {
        return new Validator.Generic(TYPE.NUMBER, Number.isFinite);
    },
    STRING: function() {
        return new Validator.Generic(TYPE.STRING, function (str) {
            return typeof str === 'string';
        });
    },
    BOOLEAN: function() {
        return new Validator.Generic(TYPE.BOOLEAN, function (bool) {
            return typeof bool === 'boolean';
        });
    },
    ENUM: function() {
        var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));
        return new Validator.Enum(args);
    },
    ARRAY: function (validator) {
        if (validator) {
            return new Validator.Generic(TYPE.ARRAY, function(arr) {
                return Array.isArray(arr) && arr.every(validator.isValid);
            }, false, TYPE.ARRAY + '<' + validator.type + '>');
        }
        return new Validator.Generic(TYPE.ARRAY, Array.isArray);
    },
    NULLABLE: function (validator, defaultValue) {
        defaultValue = typeof defaultValue !== 'undefined' ? defaultValue : validator.getDefaultValue();
        return new Validator.Generic(validator.type, function(param) {
            return (param === null || typeof param === 'undefined') || validator.isValid(param);
        }, true, validator.expects, defaultValue, validator.toJSON.bind(validator));
    }
};

module.exports.getChildNodesNames = function(NodeClass) {
    return Object.keys(NodeClass.PARAMS).reduce(function(childNodesNames, paramName) {
        if (NodeClass.PARAMS[paramName].type === TYPE.NODE) {
            childNodesNames.push(paramName);
        }
        return childNodesNames;
    }, []);
};

module.exports.getSqlTemplateFn = function(templateName) {
    return templateLoader.getTemplateFn(__dirname + '/nodes/sql/' + templateName + '.sql');
};

module.exports.template = function(tmpl, c, def) {
    return dot.template(tmpl, c, def);
};

module.exports.create = function createNode(type, expectedParams, options) {
    Object.keys(expectedParams).forEach(function(expectedParamName) {
        if (NODE_RESERVED_KEYWORDS.hasOwnProperty(expectedParamName)) {
            throw new Error('Invalid param name "' + expectedParamName + '". It is a reserved keyword.');
        }
    });

    options = options || {};
    options.cache = options.cache || false;
    options.lazy = options.lazy || false;
    if (!Number.isFinite(options.version)) {
        options.version = 0;
    }
    function BaseNode() {
        Node.apply(this, arguments);
        this.type = type;
        this.cacheQuery = options.cache || false;
        this.lazy = options.lazy || false;
        this.tags = options.tags || [];
        if (options.version > 0) {
            this.version = options.version;
        }
        this.validateAndBuild();
        // `beforeCreate` allows to change/modify the node before it's actually created/returned
        // if it throws an error it will fail to create the node.
        if (options.beforeCreate) {
            options.beforeCreate(this);
        }
    }
    util.inherits(BaseNode, Node);
    BaseNode.prototype.validateAndBuild = function() {
        this.json.type = this.type;
        Object.keys(expectedParams).forEach(function(expectedParamName) {
            var validator = expectedParams[expectedParamName];
            var param = validate(validator, this.params, expectedParamName);
            var value = (param === null || typeof param === 'undefined') ? validator.getDefaultValue() : param;

            this[expectedParamName] = value;
            if (validator.type === TYPE.NODE) {
                var parentNode = param;
                this.nodes[expectedParamName] = parentNode;
                this.inputNodes.push(parentNode);
                var skipFilters = this.lazy;
                this.json[expectedParamName] = parentNode.id(skipFilters);

                if (this.lazy) {
                    this.filters = this.filters || {};
                    var parentNodeFilters = parentNode.getFilters();
                    Object.keys(parentNodeFilters).forEach(function(filterName) {
                        this.filters['__p__' + filterName] = parentNodeFilters[filterName];
                    }.bind(this));
                }
            } else {
                this.attrs[expectedParamName] = value;
                this.json[expectedParamName] = value;
            }
        }.bind(this));
        if (Object.keys(this.filters).length > 0) {
            this.json.filters = this.filters;
        }

        this.setStatusFromInputNodes();
    };
    BaseNode.TYPE = type;
    BaseNode.PARAMS = expectedParams;
    BaseNode.OPTIONS = {
        version: options.version,
        cache: options.cache,
        lazy: options.lazy
    };
    return BaseNode;
};

function validate(validator, params, expectedParamName) {
    if (!validator.nullable && !params.hasOwnProperty(expectedParamName)) {
        throw new Error('Missing required param "' + expectedParamName + '"');
    }

    var param = params[expectedParamName];

    if (!validator.isValid(param)) {
        throw new Error(
            'Invalid type for param "' + expectedParamName + '",' +
            ' expects "' + validator.expects + '" type,' +
            ' got `' + JSON.stringify(param) + '`'
        );
    }

    return param;
}

Node.prototype.checkLimits = function(context, callback) {
    // the default is to limit number of output rows if a maximumNumberOfRows is
    // present in the configuration (not by default)
    var defaultLimit = null;
    var limit = context.getLimit(this.getType(), 'maximumNumberOfRows', defaultLimit, 'too many result rows');
    limits.limitNumberOfRows(this, context, limit, callback);
};
<commit_msg>Not check for array, concat will take care of it<commit_after>
'use strict';

var util = require('util');

var templateLoader = require('../util/template-loader');
var dot = require('dot');
dot.templateSettings.strip = false;

var id = require('../util/id');
var QueryBuilder = require('../filter/query-builder');
var Validator = require('./validator');
var limits = require('./limits');

var TYPE = require('./type');
module.exports.TYPE = TYPE;
var GEOMETRY = require('./geometry');
module.exports.GEOMETRY = GEOMETRY;

var STATUS = {
    // transitional status
    PENDING: 'pending',
    WAITING: 'waiting',
    RUNNING: 'running',

    // final status
    FAILED: 'failed',
    READY: 'ready'
};

module.exports.STATUS = STATUS;

var NODE_RESERVED_KEYWORDS = {
    type: 1,
    typeSignature: 1,
    params: 1,
    status: 1,
    errorMessage: 1,
    filters: 1,
    owner: 1,
    schema: 1,
    columns: 1,
    cacheQuery: 1,
    cacheQueryTimeout: 1,
    queuedWork: 1,
    lazy: 1,
    updatedAt: 1,
    validateAndBuild: 1,
    json: 1,
    attributesForId: 1,
    version: 1,
    nodes: 1,
    inputNodes: 1,
    attrs: 1,
    tags: 1,
    setStatusFromInputNodes: 1,
    affectedTables: 1
};

function Node() {
    // args will look like ['owner', {param1: '', param2: ''}, {param1: '', param3: ''}, ...]
    var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));
    this.owner = args.shift();

    this.params = args.reduce(function(reducedParams, arg) {
        Object.keys(arg).forEach(function(paramName) {
            reducedParams[paramName] = arg[paramName];
        });
        return reducedParams;
    }, {});

    this.status = STATUS.PENDING;
    this.errorMessage = null;
    this.queuedWork = false;
    this.cacheQueryTimeout = 0;

    this.filters = this.params.filters || {};

    this.schema = {};
    this.columns = [];

    this.nodes = {};
    this.inputNodes = [];

    this.updatedAt = null;
    this.affectedTables = [];

    this.attrs = {};

    this.json = {};
    this.attributesForId = [];

    this.typeSignature = null;

    this.version = 0;
}

Node.prototype.id = function(skipFilters) {
    var attrsToSkip = {};
    if (skipFilters || false) {
        attrsToSkip.filters = true;
    }

    var json = Object.keys(this.json).reduce(function(json, paramName) {
        if (!attrsToSkip.hasOwnProperty(paramName)) {
            json[paramName] = this.json[paramName];
        }
        return json;
    }.bind(this), {});

    return id({
        // Internal version.
        // Bump this for backwards incompatible changes.
        __version__: 1,
        owner: this.owner,
        json: json,
        version: this.version,
        attrs: this.attributesForId.map(function(attr) {
            return this.hasOwnProperty(attr) ? this[attr] : attr;
        }.bind(this))
    });
};

Node.prototype.cachedNodeId = function() {
    return this.shouldCacheQuery() ? this.id(true) : this.id();
};

Node.prototype.setAttributeToModifyId = function(attr) {
    this.attributesForId.push(attr);
};

Node.prototype.getQuery = function(applyFilters) {
    var query = this.sql();
    if (this.shouldCacheQuery()) {
        query = 'select * from ' + this.getTargetTable();
    }
    if (applyFilters === false) {
        applyFilters = Object.keys(this.filters).reduce(function(skipAllFilters, filterName) {
            skipAllFilters[filterName] = false;
            return skipAllFilters;
        }, {});
    }
    return QueryBuilder.getSql(query, this.filters, applyFilters, this.schema);
};

Node.prototype.getColumns = function(skipTheGeoms) {
    skipTheGeoms = skipTheGeoms || false;
    return (skipTheGeoms) ? skipColumns(this.columns) : this.columns;
};

Node.prototype.getFilters = function() {
    return this.filters;
};

Node.prototype.getOwner = function() {
    return this.owner;
};

Node.prototype.getType = function() {
    return this.type;
};

Node.prototype.getTypeSignature = function() {
    if (this.typeSignature === null) {
        this.typeSignature = id(this.getType()).substring(0, 10);
    }
    return this.typeSignature;
};

var SKIP_COLUMNS = {
    'the_geom': true,
    'the_geom_webmercator': true
};

function skipColumns(columnNames) {
    return columnNames
        .filter(function(columnName) { return !SKIP_COLUMNS[columnName]; });
}

Node.prototype.setColumns = function(columns) {
    this.schema = columns.reduce(function(schema, column) {
        schema[column.name] = column.type;
        return schema;
    }, {});
    this.setColumnsNames(columns.map(function(column) {
        return column.name;
    }));
};

Node.prototype.setColumnsNames = function(columnNames) {
    this.columns = columnNames;
};

Node.prototype.getStatus = function() {
    return this.status;
};

var VALID_STATUS = Object.keys(STATUS).reduce(function(valid, key) {
    valid[STATUS[key]] = true;
    return valid;
}, {});

Node.prototype.setStatus = function(status) {
    if (!VALID_STATUS.hasOwnProperty(status)) {
        return false;
    }
    this.status = status;
};

Node.prototype.getErrorMessage = function() {
    return this.errorMessage;
};

Node.prototype.setErrorMessage = function(errorMessage) {
    this.errorMessage = errorMessage;
};

Node.prototype.getUpdatedAt = function() {
    return this.updatedAt;
};

Node.prototype.setUpdatedAt = function(updatedAt) {
    this.updatedAt = (updatedAt === null) ? null : new Date(updatedAt);
};

Node.prototype.getAffectedTables = function() {
    return this.affectedTables;
};

Node.prototype.addAffectedTables = function(affectedTables) {
    var tables = affectedTables;
    this.affectedTables = this.affectedTables.concat(tables);
};

Node.prototype.getLastUpdatedAtFromInputNodes = function() {
    if (this.getInputNodes().length === 0) {
        return this.getUpdatedAt();
    }

    return this.getInputNodes().reduce(function(lastUpdatedAt, inputNode) {
        var inputNodeUpdatedAt = inputNode.getLastUpdatedAtFromInputNodes();
        if (lastUpdatedAt === null) {
            lastUpdatedAt = inputNodeUpdatedAt;
        }

        if (lastUpdatedAt !== null &&
            inputNodeUpdatedAt !== null &&
            inputNodeUpdatedAt.getTime() > lastUpdatedAt.getTime()) {

            lastUpdatedAt = inputNodeUpdatedAt;
        }

        return lastUpdatedAt;
    }, null);
};

Node.prototype.isOutdated = function() {
    var lastUpdatedAtFromInputNodes = this.getLastUpdatedAtFromInputNodes();
    var updatedAt = this.getUpdatedAt();
    return updatedAt === null || lastUpdatedAtFromInputNodes === null ||
        (lastUpdatedAtFromInputNodes.getTime() > updatedAt.getTime());
};

function shouldBeInWaitingStatus(inputNodeStatus) {
    return inputNodeStatus === STATUS.PENDING || inputNodeStatus === STATUS.RUNNING;
}
Node.prototype.setStatusFromInputNodes = function() {
    var shouldCacheNode = this.shouldCacheQuery();
    this.status = this.getInputNodes().reduce(function(currentNodeStatus, node) {
        if (currentNodeStatus === STATUS.FAILED || currentNodeStatus === STATUS.WAITING) {
            return currentNodeStatus;
        }

        var inputNodeStatus = node.getStatus();

        if (inputNodeStatus === STATUS.FAILED) {
            return STATUS.FAILED;
        }

        if (shouldBeInWaitingStatus(inputNodeStatus)) {
            return STATUS.WAITING;
        }

        return shouldCacheNode ? STATUS.PENDING : STATUS.READY;
    }, STATUS.PENDING);
};

Node.prototype.getInputNodes = function() {
    return this.inputNodes;
};

Node.prototype.getAllInputNodes = function(filterFn) {
    filterFn = filterFn || function() { return true; };

    function getAllInputNodes(node, nodeList) {
        node.getInputNodes().forEach(function(inputNode) {
            nodeList.push(inputNode);
            getAllInputNodes(inputNode, nodeList);
        });

        return nodeList;
    }

    return getAllInputNodes(this, []).filter(filterFn);
};

Node.prototype.shouldCacheQuery = function() {
    return this.cacheQuery;
};

Node.prototype.setQueuedWork = function(queuedWork) {
    this.queuedWork = queuedWork;
};

Node.prototype.didQueueWork = function() {
    return this.queuedWork;
};

Node.prototype.getCacheQueryTimeout = function() {
    return this.cacheQueryTimeout;
};

Node.prototype.setCacheQueryTimeout = function(cacheQueryTimeout) {
    this.cacheQueryTimeout = cacheQueryTimeout;
};

Node.prototype.getTags = function() {
    return this.tags;
};

Node.prototype.ignoreParamForId = function(paramName) {
    delete this.json[paramName];
};

Node.prototype.getTargetTable = function() {
    // Keep name at 60 chars
    // Ref.: https://www.postgresql.org/docs/9.5/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
    return 'analysis_' + this.getTypeSignature() + '_' + this.cachedNodeId();
};

Node.prototype.toJSON = function() {
    return this.json;
};

Node.prototype.toDot = function() {
    return {
        type: this.type,
        nodes: this.nodes,
        attrs: this.attrs
    };
};

module.exports.PARAM = {
    NODE: function() {
        var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));
        return new Validator.Node(args);
    },
    NUMBER: function() {
        return new Validator.Generic(TYPE.NUMBER, Number.isFinite);
    },
    STRING: function() {
        return new Validator.Generic(TYPE.STRING, function (str) {
            return typeof str === 'string';
        });
    },
    BOOLEAN: function() {
        return new Validator.Generic(TYPE.BOOLEAN, function (bool) {
            return typeof bool === 'boolean';
        });
    },
    ENUM: function() {
        var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));
        return new Validator.Enum(args);
    },
    ARRAY: function (validator) {
        if (validator) {
            return new Validator.Generic(TYPE.ARRAY, function(arr) {
                return Array.isArray(arr) && arr.every(validator.isValid);
            }, false, TYPE.ARRAY + '<' + validator.type + '>');
        }
        return new Validator.Generic(TYPE.ARRAY, Array.isArray);
    },
    NULLABLE: function (validator, defaultValue) {
        defaultValue = typeof defaultValue !== 'undefined' ? defaultValue : validator.getDefaultValue();
        return new Validator.Generic(validator.type, function(param) {
            return (param === null || typeof param === 'undefined') || validator.isValid(param);
        }, true, validator.expects, defaultValue, validator.toJSON.bind(validator));
    }
};

module.exports.getChildNodesNames = function(NodeClass) {
    return Object.keys(NodeClass.PARAMS).reduce(function(childNodesNames, paramName) {
        if (NodeClass.PARAMS[paramName].type === TYPE.NODE) {
            childNodesNames.push(paramName);
        }
        return childNodesNames;
    }, []);
};

module.exports.getSqlTemplateFn = function(templateName) {
    return templateLoader.getTemplateFn(__dirname + '/nodes/sql/' + templateName + '.sql');
};

module.exports.template = function(tmpl, c, def) {
    return dot.template(tmpl, c, def);
};

module.exports.create = function createNode(type, expectedParams, options) {
    Object.keys(expectedParams).forEach(function(expectedParamName) {
        if (NODE_RESERVED_KEYWORDS.hasOwnProperty(expectedParamName)) {
            throw new Error('Invalid param name "' + expectedParamName + '". It is a reserved keyword.');
        }
    });

    options = options || {};
    options.cache = options.cache || false;
    options.lazy = options.lazy || false;
    if (!Number.isFinite(options.version)) {
        options.version = 0;
    }
    function BaseNode() {
        Node.apply(this, arguments);
        this.type = type;
        this.cacheQuery = options.cache || false;
        this.lazy = options.lazy || false;
        this.tags = options.tags || [];
        if (options.version > 0) {
            this.version = options.version;
        }
        this.validateAndBuild();
        // `beforeCreate` allows to change/modify the node before it's actually created/returned
        // if it throws an error it will fail to create the node.
        if (options.beforeCreate) {
            options.beforeCreate(this);
        }
    }
    util.inherits(BaseNode, Node);
    BaseNode.prototype.validateAndBuild = function() {
        this.json.type = this.type;
        Object.keys(expectedParams).forEach(function(expectedParamName) {
            var validator = expectedParams[expectedParamName];
            var param = validate(validator, this.params, expectedParamName);
            var value = (param === null || typeof param === 'undefined') ? validator.getDefaultValue() : param;

            this[expectedParamName] = value;
            if (validator.type === TYPE.NODE) {
                var parentNode = param;
                this.nodes[expectedParamName] = parentNode;
                this.inputNodes.push(parentNode);
                var skipFilters = this.lazy;
                this.json[expectedParamName] = parentNode.id(skipFilters);

                if (this.lazy) {
                    this.filters = this.filters || {};
                    var parentNodeFilters = parentNode.getFilters();
                    Object.keys(parentNodeFilters).forEach(function(filterName) {
                        this.filters['__p__' + filterName] = parentNodeFilters[filterName];
                    }.bind(this));
                }
            } else {
                this.attrs[expectedParamName] = value;
                this.json[expectedParamName] = value;
            }
        }.bind(this));
        if (Object.keys(this.filters).length > 0) {
            this.json.filters = this.filters;
        }

        this.setStatusFromInputNodes();
    };
    BaseNode.TYPE = type;
    BaseNode.PARAMS = expectedParams;
    BaseNode.OPTIONS = {
        version: options.version,
        cache: options.cache,
        lazy: options.lazy
    };
    return BaseNode;
};

function validate(validator, params, expectedParamName) {
    if (!validator.nullable && !params.hasOwnProperty(expectedParamName)) {
        throw new Error('Missing required param "' + expectedParamName + '"');
    }

    var param = params[expectedParamName];

    if (!validator.isValid(param)) {
        throw new Error(
            'Invalid type for param "' + expectedParamName + '",' +
            ' expects "' + validator.expects + '" type,' +
            ' got `' + JSON.stringify(param) + '`'
        );
    }

    return param;
}

Node.prototype.checkLimits = function(context, callback) {
    // the default is to limit number of output rows if a maximumNumberOfRows is
    // present in the configuration (not by default)
    var defaultLimit = null;
    var limit = context.getLimit(this.getType(), 'maximumNumberOfRows', defaultLimit, 'too many result rows');
    limits.limitNumberOfRows(this, context, limit, callback);
};
<|endoftext|>


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

//TODO Further generalization of this component is in progress, see https://github.com/phetsims/sun/issues/85
/**
 * Spinner for numbers.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var ArrowButton = require( 'SUN/buttons/ArrowButton' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var sun = require( 'SUN/sun' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );

  /**
   * @param {Property.<number>} numberProperty value, must be an integer
   * @param {Range} range range of values, min and max must be integers
   * @param {Object} [options]
   * @constructor
   */
  function NumberSpinner( numberProperty, range, options ) {

    assert && assert( range.contains( numberProperty.get() ), 'value ' + numberProperty.get() + ' is out of range ' + range.toString() );

    options = _.extend( {

      // {string} where to place the arrow buttons, 'leftRight'|'topBottom'|'bothRight'|'bothBottom'
      arrowsPosition: 'bothRight',

      // {number|null} arrows are by default scaled to fit dimensions of value background. This scales relative to that scale.
      arrowsScale: null,

      decimalPlaces: 0,
      font: new PhetFont( 28 ),
      xMargin: 5,
      yMargin: 3,
      xSpacing: 5,
      ySpacing: 3,
      cornerRadius: 5,
      touchXDilated: 20,
      touchYDilated: 10
    }, options );

    var valueOptions = {
      font: options.font,
      fill: 'black'
    };

    // compute max width
    var maxWidth = Math.max(
      new Text( Util.toFixed( range.max, options.decimalPlaces ), valueOptions ).width,
      new Text( Util.toFixed( range.min, options.decimalPlaces ), valueOptions ).width
    );

    // number
    var numberNode = new Text( numberProperty.get(), valueOptions );
    var backgroundNode = new Rectangle( 0, 0, maxWidth + ( 2 * options.xMargin ), numberNode.height + ( 2 * options.yMargin ),
      options.cornerRadius, options.cornerRadius, {
        fill: 'white',
        stroke: 'black',
        lineWidth: 0.5
      } );
    numberNode.center = backgroundNode.center;
    var valueParent = new Node( { children: [ backgroundNode, numberNode ] } );

    // buttons
    var incrementDirection = ( options.arrowsPosition === 'topBottom' || options.arrowsPosition === 'bothRight' ) ? 'up' : 'right';
    var incrementButton = new ArrowButton( incrementDirection, function() { numberProperty.set( numberProperty.get() + 1 ); } );
    var decrementDirection = ( options.arrowsPosition === 'topBottom' || options.arrowsPosition === 'bothRight' ) ? 'down' : 'left';
    var decrementButton = new ArrowButton( decrementDirection, function() { numberProperty.set( numberProperty.get() - 1 ); } );

    // pointer area
    incrementButton.touchArea = incrementButton.localBounds
      .dilatedXY( options.touchXDilated, options.touchYDilated )
      .shiftedY( -options.touchYDilated );
    decrementButton.touchArea = decrementButton.localBounds
      .dilatedXY( options.touchXDilated, options.touchYDilated )
      .shiftedY( options.touchYDilated );

    // arrow button scaling
    var arrowsScale;
    if ( !arrowsScale ) {
      if ( options.arrowsPosition === 'leftRight' ) {
        arrowsScale = valueParent.height / incrementButton.height;
      }
      else if ( options.arrowsPosition === 'topBottom' ) {
        arrowsScale = valueParent.width / incrementButton.width;
      }
      else if ( options.arrowsPosition === 'bothRight' ) {
        arrowsScale = ( ( valueParent.height / 2 ) - ( options.ySpacing / 2 ) ) / incrementButton.height;
      }
      else if ( options.arrowsPosition === 'bothBottom' ) {
        arrowsScale = ( ( valueParent.width / 2 ) - ( options.xSpacing / 2 ) ) / incrementButton.width;
      }
      else {
        throw new Error( 'invalid options.arrowsPosition: ' + options.arrowsPosition );
      }
    }
    if ( options.arrowsScale ) {
      arrowsScale = arrowsScale * options.arrowsScale;
    }
    incrementButton.setScaleMagnitude( arrowsScale );
    decrementButton.setScaleMagnitude( arrowsScale );

    // layout
    if ( options.arrowsPosition === 'leftRight' ) {
      incrementButton.left = valueParent.right + options.xSpacing;
      decrementButton.right = valueParent.left - options.xSpacing;
      incrementButton.centerY = decrementButton.centerY = valueParent.centerY;
    }
    else if ( options.arrowsPosition === 'topBottom' ) {
      incrementButton.centerX = decrementButton.centerX = valueParent.centerX;
      incrementButton.bottom = valueParent.top - options.ySpacing;
      decrementButton.top = valueParent.bottom + options.ySpacing;
    }
    else if ( options.arrowsPosition === 'bothRight' ) {
      incrementButton.left = decrementButton.left = valueParent.right + options.xSpacing;
      incrementButton.bottom = valueParent.centerY - ( options.ySpacing / 2 );
      decrementButton.top = valueParent.centerY + ( options.ySpacing / 2 );
    }
    else if ( options.arrowsPosition === 'bothBottom' ) {
      incrementButton.left = valueParent.centerX + ( options.xSpacing / 2 );
      decrementButton.right = valueParent.centerX - ( options.xSpacing / 2 );
      incrementButton.top = decrementButton.top = valueParent.bottom + options.ySpacing;
    }
    else {
      throw new Error( 'invalid options.arrowsPosition: ' + options.arrowsPosition );
    }

    assert && assert( !this.children, 'decoration not supported' );
    options.children = [ valueParent, incrementButton, decrementButton ];

    Node.call( this, options );

    // @private When the value changes ...
    this.numberPropertyObserver = function( value ) {
      assert && assert( range.contains( value ) );

      // update the number and center it
      numberNode.text = Util.toFixed( value, options.decimalPlaces );
      numberNode.center = backgroundNode.center;

      // enable/disable arrow buttons
      incrementButton.enabled = ( value < range.max );
      decrementButton.enabled = ( value > range.min );
    };
    this.numberProperty = numberProperty; // @private
    this.numberProperty.link( this.numberPropertyObserver ); // must be unlinked in dispose
  }

  sun.register( 'NumberSpinner', NumberSpinner );

  return inherit( Node, NumberSpinner, {

    // @public Ensures that this node is eligible for GC.
    dispose: function() {
      this.numberProperty.unlink( this.numberPropertyObserver );
    }
  } );
} );
<commit_msg>add AL as co-author, https://github.com/phetsims/sun/issues/85<commit_after>// Copyright 2014-2015, University of Colorado Boulder

//TODO Further generalization of this component is in progress, see https://github.com/phetsims/sun/issues/85
/**
 * Spinner for numbers.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author Andrea Lin
 */
define( function( require ) {
  'use strict';

  // modules
  var ArrowButton = require( 'SUN/buttons/ArrowButton' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var sun = require( 'SUN/sun' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );

  /**
   * @param {Property.<number>} numberProperty value, must be an integer
   * @param {Range} range range of values, min and max must be integers
   * @param {Object} [options]
   * @constructor
   */
  function NumberSpinner( numberProperty, range, options ) {

    assert && assert( range.contains( numberProperty.get() ), 'value ' + numberProperty.get() + ' is out of range ' + range.toString() );

    options = _.extend( {

      // {string} where to place the arrow buttons, 'leftRight'|'topBottom'|'bothRight'|'bothBottom'
      arrowsPosition: 'bothRight',

      // {number|null} arrows are by default scaled to fit dimensions of value background. This scales relative to that scale.
      arrowsScale: null,

      decimalPlaces: 0,
      font: new PhetFont( 28 ),
      xMargin: 5,
      yMargin: 3,
      xSpacing: 5,
      ySpacing: 3,
      cornerRadius: 5,
      touchXDilated: 20,
      touchYDilated: 10
    }, options );

    var valueOptions = {
      font: options.font,
      fill: 'black'
    };

    // compute max width
    var maxWidth = Math.max(
      new Text( Util.toFixed( range.max, options.decimalPlaces ), valueOptions ).width,
      new Text( Util.toFixed( range.min, options.decimalPlaces ), valueOptions ).width
    );

    // number
    var numberNode = new Text( numberProperty.get(), valueOptions );
    var backgroundNode = new Rectangle( 0, 0, maxWidth + ( 2 * options.xMargin ), numberNode.height + ( 2 * options.yMargin ),
      options.cornerRadius, options.cornerRadius, {
        fill: 'white',
        stroke: 'black',
        lineWidth: 0.5
      } );
    numberNode.center = backgroundNode.center;
    var valueParent = new Node( { children: [ backgroundNode, numberNode ] } );

    // buttons
    var incrementDirection = ( options.arrowsPosition === 'topBottom' || options.arrowsPosition === 'bothRight' ) ? 'up' : 'right';
    var incrementButton = new ArrowButton( incrementDirection, function() { numberProperty.set( numberProperty.get() + 1 ); } );
    var decrementDirection = ( options.arrowsPosition === 'topBottom' || options.arrowsPosition === 'bothRight' ) ? 'down' : 'left';
    var decrementButton = new ArrowButton( decrementDirection, function() { numberProperty.set( numberProperty.get() - 1 ); } );

    // pointer area
    incrementButton.touchArea = incrementButton.localBounds
      .dilatedXY( options.touchXDilated, options.touchYDilated )
      .shiftedY( -options.touchYDilated );
    decrementButton.touchArea = decrementButton.localBounds
      .dilatedXY( options.touchXDilated, options.touchYDilated )
      .shiftedY( options.touchYDilated );

    // arrow button scaling
    var arrowsScale;
    if ( !arrowsScale ) {
      if ( options.arrowsPosition === 'leftRight' ) {
        arrowsScale = valueParent.height / incrementButton.height;
      }
      else if ( options.arrowsPosition === 'topBottom' ) {
        arrowsScale = valueParent.width / incrementButton.width;
      }
      else if ( options.arrowsPosition === 'bothRight' ) {
        arrowsScale = ( ( valueParent.height / 2 ) - ( options.ySpacing / 2 ) ) / incrementButton.height;
      }
      else if ( options.arrowsPosition === 'bothBottom' ) {
        arrowsScale = ( ( valueParent.width / 2 ) - ( options.xSpacing / 2 ) ) / incrementButton.width;
      }
      else {
        throw new Error( 'invalid options.arrowsPosition: ' + options.arrowsPosition );
      }
    }
    if ( options.arrowsScale ) {
      arrowsScale = arrowsScale * options.arrowsScale;
    }
    incrementButton.setScaleMagnitude( arrowsScale );
    decrementButton.setScaleMagnitude( arrowsScale );

    // layout
    if ( options.arrowsPosition === 'leftRight' ) {
      incrementButton.left = valueParent.right + options.xSpacing;
      decrementButton.right = valueParent.left - options.xSpacing;
      incrementButton.centerY = decrementButton.centerY = valueParent.centerY;
    }
    else if ( options.arrowsPosition === 'topBottom' ) {
      incrementButton.centerX = decrementButton.centerX = valueParent.centerX;
      incrementButton.bottom = valueParent.top - options.ySpacing;
      decrementButton.top = valueParent.bottom + options.ySpacing;
    }
    else if ( options.arrowsPosition === 'bothRight' ) {
      incrementButton.left = decrementButton.left = valueParent.right + options.xSpacing;
      incrementButton.bottom = valueParent.centerY - ( options.ySpacing / 2 );
      decrementButton.top = valueParent.centerY + ( options.ySpacing / 2 );
    }
    else if ( options.arrowsPosition === 'bothBottom' ) {
      incrementButton.left = valueParent.centerX + ( options.xSpacing / 2 );
      decrementButton.right = valueParent.centerX - ( options.xSpacing / 2 );
      incrementButton.top = decrementButton.top = valueParent.bottom + options.ySpacing;
    }
    else {
      throw new Error( 'invalid options.arrowsPosition: ' + options.arrowsPosition );
    }

    assert && assert( !this.children, 'decoration not supported' );
    options.children = [ valueParent, incrementButton, decrementButton ];

    Node.call( this, options );

    // @private When the value changes ...
    this.numberPropertyObserver = function( value ) {
      assert && assert( range.contains( value ) );

      // update the number and center it
      numberNode.text = Util.toFixed( value, options.decimalPlaces );
      numberNode.center = backgroundNode.center;

      // enable/disable arrow buttons
      incrementButton.enabled = ( value < range.max );
      decrementButton.enabled = ( value > range.min );
    };
    this.numberProperty = numberProperty; // @private
    this.numberProperty.link( this.numberPropertyObserver ); // must be unlinked in dispose
  }

  sun.register( 'NumberSpinner', NumberSpinner );

  return inherit( Node, NumberSpinner, {

    // @public Ensures that this node is eligible for GC.
    dispose: function() {
      this.numberProperty.unlink( this.numberPropertyObserver );
    }
  } );
} );
<|endoftext|>