<commit_before>/*
 * Code Pulse: A real-time code coverage testing tool. For more information
 * see http://code-pulse.com
 *
 * Copyright (C) 2014 Applied Visions - http://securedecisions.avi.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 PackageWidget.js
  - requires jQuery, D3, Bacon.js
*/

;(function(exports){

	/*
	Returns a "short string" representation of `n`.
	Uses suffixes 'K' (kilo = 1000), 'M' (million), 'B' (billion), or 'T' (trillion) to shorten long numbers.
	*/
	function numToShortString(n){
		n = parseInt(n)
		var s = n.toString()
		var suffixes = ['', 'K', 'M', 'B', 'T']
		var i = 0
		while(s.length > 4 && i <= 4){
			var div = Math.pow(1000, i)
			s = parseInt(n / div) + suffixes[i]
			i++
		}
		return s
	}

	/*
	Makes a user-friendly percentage string based on the given `ratio` (number from 0 to 1)
	For numbers between
	*/
	function percentageString(ratio){
		var perc = ratio * 100
		if(perc < 1 && perc > 0) return "<1%"
		if(perc > 99 && perc < 100) return ">99%"
		return parseInt(perc) + '%'
	}

	/* The package labels get 200px of width by default.
	 * 20 of those pixels are taken up by the expand/collapse icon.
	 * Indentation will take away from that.
	 */
	var packageLabelDefaultWidth = 180

	/*
	 * Creates a function($e, obj) that takes a jQuery element `$e`,
	 * locates the common UI parts within $e (assuming it is a clone
	 * of the PackageWidget's template html), and assigns them to
	 * fields in the `obj`.
	 */
	var assignUiParts = (function(){
		var widgetPartsToFind = [
			'childrenContainer',
			'labelContainer',
			'countContainer',
			'barContainer',
			'collapser',
			'collapserIcon',
			'indent',
			'contentContainer',
			'labelText',
			'instrumentationSelectedToggle',
			'countNumber',
			'barFill',
			'barLabel'
		]

		/*
		 * Each UI part is located in the template HTML with
		 * the id "WidgetPart-partName", where `partName`
		 * corresponds to the string in `widgetPartsToFind`.
		 */
		var infos = widgetPartsToFind.map(function(name){
			var selector = '#WidgetPart-' + name
			return {name:name, selector:selector}
		})
		return function($e, obj){
			infos.forEach(function(info){
				var $f = $e.find(info.selector)
				obj[info.name] = $f
			})
		}
	})()

	function PackageWidget(){

		// ============================================================================
		// Private/Local State
		// ============================================================================
		var e = PackageWidget.template.clone()

		var self = this,
			_fullLabel = '',
			_abbreviatedLabel = '',
			_indentation = 0,
			_methodCount = 0,
			_totalCoverage = 0,
			_displayCompact = false,

			_selected = 0,
			_selectable = true,
			_selectedBus = new Bacon.Bus(),

			_instrumentationSelected = 0,
			_instrumentationSelectable = true,
			_instrumentationSelectedBus = new Bacon.Bus(),

			_collapseChildren = false,
			_childWidgets = []

		// ============================================================================
		// UI Selected Elements
		// ============================================================================

		this.uiParts = {
			'main': e,

			'highlightsD3': d3.select(e[0]).selectAll('.highlight')
		}

		// find the remaining UI parts via the helper function `assignUiParts`
		assignUiParts(e, this.uiParts)

		// ============================================================================
		// Public Functions
		// ============================================================================

		this.fullLabel = function(newLabel){
			if(!arguments.length) return _fullLabel

			_fullLabel = newLabel
			this.uiParts.labelContainer.attr('title', _fullLabel)
			return self
		}

		this.abbreviatedLabel = function(newLabel){
			if(!arguments.length) return _abbreviatedLabel

			_abbreviatedLabel = newLabel
			this.uiParts.labelText.text(_abbreviatedLabel)
			return self
		}

		this.methodCount = function(newMethodCount){
			if(!arguments.length) return _methodCount

			_methodCount = newMethodCount
			updateMethodCounterUI()
			return self
		}

		this.totalCoverage = function(newCount){
			if(!arguments.length) return _totalCoverage

			_totalCoverage = newCount
			updateMethodCounterUI()
			return self
		}

		this.indent = function(newIndent){
			if(!arguments.length) return _indentation

			_indentation = newIndent
			this.uiParts.contentContainer.css('width', (packageLabelDefaultWidth - _indentation) + 'px')
			this.uiParts.indent.css('width', _indentation + 'px')
			_childWidgets.forEach(function(child){
				child.indent(_indentation + 8)
			})
		}

		this.selectable = function(newSel){
			if(!arguments.length) return _selectable

			_selectable = newSel
			return self
		}

		this.instrumentationSelectable = function(newSel){
			if(!arguments.length) return _instrumentationSelectable

			_instrumentationSelectable = newSel

			self.uiParts.instrumentationSelectedToggle
				.toggleClass('disabled', !_instrumentationSelectable)

			return self
		}

		this.displayCompact = function(compactFlag){
			if(!arguments.length) return _displayCompact

			_displayCompact = compactFlag
			this.uiParts.main.toggleClass('compact', _displayCompact)
			return self
		}

		this.flashHighlight = function(){
			self.uiParts.highlightsD3
				.interrupt()
				.style('opacity', .8)
			.transition().duration(5000)
				.style('opacity', 0)

			return self
		}

		this.addDependencyCheckBadge = function(status) {
			if (status.state == 'none') return;

			if (self.uiParts.vulnerableBadge) {
				self.uiParts.vulnerableBadge.remove();
				delete self.uiParts.vulnerableBadge;
			}

			self.badgeClickEnabled = false

			var badge = self.uiParts.depCheckBadge
			if (!badge) {
				badge = PackageWidget.depCheckBadge.clone()
				badge.click(function(e) {
					if (self.badgeClickEnabled) self.vulnerableBadgeClicks.push(e)
					e.stopPropagation()
				})
				self.uiParts.depCheckBadge = badge
				self.uiParts.labelText.before(badge)
			}

			var $badge = $(badge),
				$status = $('#dependencycheck-status', badge),
				$summary = $('#dependencycheck-summary', badge)

			function setStatus(status) {
				$badge.addClass('pending')
				$summary.hide()
				$status.show()
				$status.text(status)
				self.badgeClickEnabled = false
			}

			function setSummary(numVuln) {
				$status.hide()
				$badge.removeClass('pending')
				if (numVuln >= 0) {
					$summary.show()
					$('#dependencycheck-numvuln', $summary).text(numVuln)
					$badge.toggleClass('clean', numVuln == 0)
					self.badgeClickEnabled = numVuln > 0
				} else $badge.hide()
			}

			switch (status.state) {
				case 'queued':
					setStatus('[queued]')
					break

				case 'running':
					setStatus('[scanning]')
					break

				case 'failed':
					setStatus('[failed]')
					break

				case 'unknown':
					setStatus('[unknown]')
					break

				case 'finished':
					setSummary(status.numFlaggedDeps)
					break
			}
		}

		this.addVulnerableBadge = function(isSelf) {
			if (!self.uiParts.depCheckBadge) {
				var badge = self.uiParts.vulnerableBadge
				if (!badge) {
					badge = PackageWidget.vulnBadge.clone()
					badge.click(function(e) {
						self.vulnerableBadgeClicks.push(e)
						e.stopPropagation()
					})
					badge.addClass('bubbled')
					self.uiParts.vulnerableBadge = badge
					self.uiParts.labelText.before(badge)
				}
				if (isSelf) badge.removeClass('bubbled')
			}
		}

		/*
		Get or set the selected state:
			`selected()` returns the state
			`selected(<boolean>)` sets the state
			`selected('toggle')` flips the state
		The setters will trigger a 'selected' event and return a self-reference
		*/
		this.selected = function(newSelect){
			if(!arguments.length) return _selected
			_selected = newSelect

			self.uiParts.main
				.toggleClass('selected', _selected == 1 || _selected == undefined)
				.toggleClass('partial-select', _selected == undefined)

			_selectedBus.push(_selected)

			return self
		}

		this.instrumentationSelected = function(newSelect){
			if(!arguments.length) return _instrumentationSelected
			_instrumentationSelected = newSelect

			self.uiParts.instrumentationSelectedToggle
				.toggleClass('full-select', _instrumentationSelected == 1)
				.toggleClass('partial-select', _instrumentationSelected == undefined)

			_instrumentationSelectedBus.push(_instrumentationSelected)

			return self
		}

		this.collapseChildren = function(arg, animate){
			if(!arguments.length) return _collapseChildren

			if(arg == 'toggle') _collapseChildren = !_collapseChildren
			else _collapseChildren = arg

			updatedCollapserState()

			if(animate){
				self.uiParts.childrenContainer[_collapseChildren ? 'slideUp' : 'slideDown']()
			} else {
				self.uiParts.childrenContainer[_collapseChildren ? 'hide' : 'show']()
			}
			return self
		}

		this.children = {
			'add': function(childWidget){
				_childWidgets.push(childWidget)
				childWidget.indent(_indentation + 8)
				self.uiParts.childrenContainer.append(childWidget.uiParts.main)
				updatedCollapserState()
				return this
			},

			'forEach': function(callback){
				_childWidgets.forEach(callback)
			}
		}

		this.children.__defineGetter__('length', function(){ return _childWidgets.length })

		/*
		Exposes the current `selected` state as a Rx Property
		*/
		this.selectedProp = _selectedBus.toProperty(_selected).skipDuplicates().noLazy()

		this.selectionClicks = new Bacon.Bus()

		this.instrumentationSelectedProp = _instrumentationSelectedBus.toProperty(_instrumentationSelected).noLazy().skipDuplicates()

		this.instrumentationSelectedClicks = new Bacon.Bus()

		this.vulnerableBadgeClicks = new Bacon.Bus()

		// ============================================================================
		// Helper Methods
		// ============================================================================

		function updateMethodCounterUI(){
			var coverageRatio = _methodCount ? (_totalCoverage / _methodCount) : 0,
				fillWidth = parseInt(coverageRatio * 100) + '%',
				coverageText = percentageString(coverageRatio),
				countText = numToShortString(_methodCount)

			self.uiParts.countNumber.text(countText)
			self.uiParts.barFill.css('width', fillWidth)
			self.uiParts.barLabel.text(coverageText)
			self.uiParts.barFill.toggleClass('covered', coverageRatio > 0)

			updateBarLabelPlacement()
		}

		function updateBarLabelPlacement(){
			var barFill = self.uiParts.barFill,
				barPxWidth = barFill.width(),
				barLabelWidth = self.uiParts.barLabel.width()

			barFill.toggleClass('small', barPxWidth - barLabelWidth < 20)
		}

		function updatedCollapserState(){
			self.uiParts.collapserIcon.toggleClass('icon-plus', _collapseChildren)
			self.uiParts.collapserIcon.toggleClass('icon-minus', !_collapseChildren)
			self.uiParts.main.toggleClass('has-children', !!_childWidgets.length)
		}

		// ============================================================================
		// Setup and Initialization
		// ============================================================================

		$(window).resize(updateBarLabelPlacement)
		this.uiParts.contentContainer.click(function(){
			// as long as the widget is 'selectable', toggle it when clicked
			if(_selectable) {
				// self.selected('toggle')
				self.selectionClicks.push(1)
			}
		})
		this.uiParts.instrumentationSelectedToggle.click(function(){
			if(_instrumentationSelectable){
				self.instrumentationSelectedClicks.push(1)
			}
		})
	}

	$(document).ready(function(){
		PackageWidget.template = $('#package-widget-template').attr('id', null).remove()
		PackageWidget.vulnBadge = $('#package-widget-template-has-vuln').attr('id', null).remove()
		PackageWidget.depCheckBadge = $('#package-widget-template-dependency-check').attr('id', null).remove()
	})

	exports.PackageWidget = PackageWidget

})(this)<commit_msg>Add tooltip to dependency check status messages.<commit_after>/*
 * Code Pulse: A real-time code coverage testing tool. For more information
 * see http://code-pulse.com
 *
 * Copyright (C) 2014 Applied Visions - http://securedecisions.avi.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 PackageWidget.js
  - requires jQuery, D3, Bacon.js
*/

;(function(exports){

	/*
	Returns a "short string" representation of `n`.
	Uses suffixes 'K' (kilo = 1000), 'M' (million), 'B' (billion), or 'T' (trillion) to shorten long numbers.
	*/
	function numToShortString(n){
		n = parseInt(n)
		var s = n.toString()
		var suffixes = ['', 'K', 'M', 'B', 'T']
		var i = 0
		while(s.length > 4 && i <= 4){
			var div = Math.pow(1000, i)
			s = parseInt(n / div) + suffixes[i]
			i++
		}
		return s
	}

	/*
	Makes a user-friendly percentage string based on the given `ratio` (number from 0 to 1)
	For numbers between
	*/
	function percentageString(ratio){
		var perc = ratio * 100
		if(perc < 1 && perc > 0) return "<1%"
		if(perc > 99 && perc < 100) return ">99%"
		return parseInt(perc) + '%'
	}

	/* The package labels get 200px of width by default.
	 * 20 of those pixels are taken up by the expand/collapse icon.
	 * Indentation will take away from that.
	 */
	var packageLabelDefaultWidth = 180

	/*
	 * Creates a function($e, obj) that takes a jQuery element `$e`,
	 * locates the common UI parts within $e (assuming it is a clone
	 * of the PackageWidget's template html), and assigns them to
	 * fields in the `obj`.
	 */
	var assignUiParts = (function(){
		var widgetPartsToFind = [
			'childrenContainer',
			'labelContainer',
			'countContainer',
			'barContainer',
			'collapser',
			'collapserIcon',
			'indent',
			'contentContainer',
			'labelText',
			'instrumentationSelectedToggle',
			'countNumber',
			'barFill',
			'barLabel'
		]

		/*
		 * Each UI part is located in the template HTML with
		 * the id "WidgetPart-partName", where `partName`
		 * corresponds to the string in `widgetPartsToFind`.
		 */
		var infos = widgetPartsToFind.map(function(name){
			var selector = '#WidgetPart-' + name
			return {name:name, selector:selector}
		})
		return function($e, obj){
			infos.forEach(function(info){
				var $f = $e.find(info.selector)
				obj[info.name] = $f
			})
		}
	})()

	function PackageWidget(){

		// ============================================================================
		// Private/Local State
		// ============================================================================
		var e = PackageWidget.template.clone()

		var self = this,
			_fullLabel = '',
			_abbreviatedLabel = '',
			_indentation = 0,
			_methodCount = 0,
			_totalCoverage = 0,
			_displayCompact = false,

			_selected = 0,
			_selectable = true,
			_selectedBus = new Bacon.Bus(),

			_instrumentationSelected = 0,
			_instrumentationSelectable = true,
			_instrumentationSelectedBus = new Bacon.Bus(),

			_collapseChildren = false,
			_childWidgets = []

		// ============================================================================
		// UI Selected Elements
		// ============================================================================

		this.uiParts = {
			'main': e,

			'highlightsD3': d3.select(e[0]).selectAll('.highlight')
		}

		// find the remaining UI parts via the helper function `assignUiParts`
		assignUiParts(e, this.uiParts)

		// ============================================================================
		// Public Functions
		// ============================================================================

		this.fullLabel = function(newLabel){
			if(!arguments.length) return _fullLabel

			_fullLabel = newLabel
			this.uiParts.labelContainer.attr('title', _fullLabel)
			return self
		}

		this.abbreviatedLabel = function(newLabel){
			if(!arguments.length) return _abbreviatedLabel

			_abbreviatedLabel = newLabel
			this.uiParts.labelText.text(_abbreviatedLabel)
			return self
		}

		this.methodCount = function(newMethodCount){
			if(!arguments.length) return _methodCount

			_methodCount = newMethodCount
			updateMethodCounterUI()
			return self
		}

		this.totalCoverage = function(newCount){
			if(!arguments.length) return _totalCoverage

			_totalCoverage = newCount
			updateMethodCounterUI()
			return self
		}

		this.indent = function(newIndent){
			if(!arguments.length) return _indentation

			_indentation = newIndent
			this.uiParts.contentContainer.css('width', (packageLabelDefaultWidth - _indentation) + 'px')
			this.uiParts.indent.css('width', _indentation + 'px')
			_childWidgets.forEach(function(child){
				child.indent(_indentation + 8)
			})
		}

		this.selectable = function(newSel){
			if(!arguments.length) return _selectable

			_selectable = newSel
			return self
		}

		this.instrumentationSelectable = function(newSel){
			if(!arguments.length) return _instrumentationSelectable

			_instrumentationSelectable = newSel

			self.uiParts.instrumentationSelectedToggle
				.toggleClass('disabled', !_instrumentationSelectable)

			return self
		}

		this.displayCompact = function(compactFlag){
			if(!arguments.length) return _displayCompact

			_displayCompact = compactFlag
			this.uiParts.main.toggleClass('compact', _displayCompact)
			return self
		}

		this.flashHighlight = function(){
			self.uiParts.highlightsD3
				.interrupt()
				.style('opacity', .8)
			.transition().duration(5000)
				.style('opacity', 0)

			return self
		}

		this.addDependencyCheckBadge = function(status) {
			if (status.state == 'none') return;

			if (self.uiParts.vulnerableBadge) {
				self.uiParts.vulnerableBadge.remove();
				delete self.uiParts.vulnerableBadge;
			}

			self.badgeClickEnabled = false

			var badge = self.uiParts.depCheckBadge
			if (!badge) {
				badge = PackageWidget.depCheckBadge.clone()
				badge.click(function(e) {
					if (self.badgeClickEnabled) self.vulnerableBadgeClicks.push(e)
					e.stopPropagation()
				})
				self.uiParts.depCheckBadge = badge
				self.uiParts.labelText.before(badge)
			}

			var $badge = $(badge),
				$status = $('#dependencycheck-status', badge),
				$summary = $('#dependencycheck-summary', badge)

			function setStatus(status, title) {
				$badge.addClass('pending')
				$summary.hide()
				$status.show()
				$status.attr('title', title);
				$status.text(status)
				self.badgeClickEnabled = false
			}

			function setSummary(numVuln) {
				$status.hide()
				$badge.removeClass('pending')
				if (numVuln >= 0) {
					$summary.show()
					$('#dependencycheck-numvuln', $summary).text(numVuln)
					$badge.toggleClass('clean', numVuln == 0)
					self.badgeClickEnabled = numVuln > 0
				} else $badge.hide()
			}

			switch (status.state) {
				case 'queued':
					setStatus('[queued]', 'This project has been queued for a Dependency Check scan.')
					break

				case 'running':
					setStatus('[scanning]', 'Dependency Check is currently scanning dependencies for known vulnerabilities.')
					break

				case 'failed':
					setStatus('[failed]', 'Dependency Check has failed; please consult the logs for more information.')
					break

				case 'unknown':
					setStatus('[unknown]', 'Dependency Check is in an unknown state; please consult the logs for more information.')
					break

				case 'finished':
					setSummary(status.numFlaggedDeps)
					break
			}
		}

		this.addVulnerableBadge = function(isSelf) {
			if (!self.uiParts.depCheckBadge) {
				var badge = self.uiParts.vulnerableBadge
				if (!badge) {
					badge = PackageWidget.vulnBadge.clone()
					badge.click(function(e) {
						self.vulnerableBadgeClicks.push(e)
						e.stopPropagation()
					})
					badge.addClass('bubbled')
					self.uiParts.vulnerableBadge = badge
					self.uiParts.labelText.before(badge)
				}
				if (isSelf) badge.removeClass('bubbled')
			}
		}

		/*
		Get or set the selected state:
			`selected()` returns the state
			`selected(<boolean>)` sets the state
			`selected('toggle')` flips the state
		The setters will trigger a 'selected' event and return a self-reference
		*/
		this.selected = function(newSelect){
			if(!arguments.length) return _selected
			_selected = newSelect

			self.uiParts.main
				.toggleClass('selected', _selected == 1 || _selected == undefined)
				.toggleClass('partial-select', _selected == undefined)

			_selectedBus.push(_selected)

			return self
		}

		this.instrumentationSelected = function(newSelect){
			if(!arguments.length) return _instrumentationSelected
			_instrumentationSelected = newSelect

			self.uiParts.instrumentationSelectedToggle
				.toggleClass('full-select', _instrumentationSelected == 1)
				.toggleClass('partial-select', _instrumentationSelected == undefined)

			_instrumentationSelectedBus.push(_instrumentationSelected)

			return self
		}

		this.collapseChildren = function(arg, animate){
			if(!arguments.length) return _collapseChildren

			if(arg == 'toggle') _collapseChildren = !_collapseChildren
			else _collapseChildren = arg

			updatedCollapserState()

			if(animate){
				self.uiParts.childrenContainer[_collapseChildren ? 'slideUp' : 'slideDown']()
			} else {
				self.uiParts.childrenContainer[_collapseChildren ? 'hide' : 'show']()
			}
			return self
		}

		this.children = {
			'add': function(childWidget){
				_childWidgets.push(childWidget)
				childWidget.indent(_indentation + 8)
				self.uiParts.childrenContainer.append(childWidget.uiParts.main)
				updatedCollapserState()
				return this
			},

			'forEach': function(callback){
				_childWidgets.forEach(callback)
			}
		}

		this.children.__defineGetter__('length', function(){ return _childWidgets.length })

		/*
		Exposes the current `selected` state as a Rx Property
		*/
		this.selectedProp = _selectedBus.toProperty(_selected).skipDuplicates().noLazy()

		this.selectionClicks = new Bacon.Bus()

		this.instrumentationSelectedProp = _instrumentationSelectedBus.toProperty(_instrumentationSelected).noLazy().skipDuplicates()

		this.instrumentationSelectedClicks = new Bacon.Bus()

		this.vulnerableBadgeClicks = new Bacon.Bus()

		// ============================================================================
		// Helper Methods
		// ============================================================================

		function updateMethodCounterUI(){
			var coverageRatio = _methodCount ? (_totalCoverage / _methodCount) : 0,
				fillWidth = parseInt(coverageRatio * 100) + '%',
				coverageText = percentageString(coverageRatio),
				countText = numToShortString(_methodCount)

			self.uiParts.countNumber.text(countText)
			self.uiParts.barFill.css('width', fillWidth)
			self.uiParts.barLabel.text(coverageText)
			self.uiParts.barFill.toggleClass('covered', coverageRatio > 0)

			updateBarLabelPlacement()
		}

		function updateBarLabelPlacement(){
			var barFill = self.uiParts.barFill,
				barPxWidth = barFill.width(),
				barLabelWidth = self.uiParts.barLabel.width()

			barFill.toggleClass('small', barPxWidth - barLabelWidth < 20)
		}

		function updatedCollapserState(){
			self.uiParts.collapserIcon.toggleClass('icon-plus', _collapseChildren)
			self.uiParts.collapserIcon.toggleClass('icon-minus', !_collapseChildren)
			self.uiParts.main.toggleClass('has-children', !!_childWidgets.length)
		}

		// ============================================================================
		// Setup and Initialization
		// ============================================================================

		$(window).resize(updateBarLabelPlacement)
		this.uiParts.contentContainer.click(function(){
			// as long as the widget is 'selectable', toggle it when clicked
			if(_selectable) {
				// self.selected('toggle')
				self.selectionClicks.push(1)
			}
		})
		this.uiParts.instrumentationSelectedToggle.click(function(){
			if(_instrumentationSelectable){
				self.instrumentationSelectedClicks.push(1)
			}
		})
	}

	$(document).ready(function(){
		PackageWidget.template = $('#package-widget-template').attr('id', null).remove()
		PackageWidget.vulnBadge = $('#package-widget-template-has-vuln').attr('id', null).remove()
		PackageWidget.depCheckBadge = $('#package-widget-template-dependency-check').attr('id', null).remove()
	})

	exports.PackageWidget = PackageWidget

})(this)<|endoftext|>


<commit_before>angular.module( 'gj.Game.Package.Card' ).directive( 'gjGamePackageCard', function(
	$timeout, $injector, gettextCatalog, Screen, Game, Game_Build, Game_PlayModal, Game_Downloader, Device, Environment, Analytics, currencyFilter )
{
	/**
	 * Sort must start at 1 so that we can put their prefered OS as sort 0 later on.
	 */
	var supportInfo = {
		windows: {
			icon: 'windows',
			tooltip: gettextCatalog.getString( 'Windows Support' ),
			sort: 10,
		},
		windows_64: {
			icon: 'windows',
			tooltip: gettextCatalog.getString( 'Windows 64-bit Support' ),
			arch: '64',
			sort: 11,
		},
		mac: {
			icon: 'mac',
			tooltip: gettextCatalog.getString( 'Mac Support' ),
			sort: 20,
		},
		mac_64: {
			icon: 'mac',
			tooltip: gettextCatalog.getString( 'Mac 64-bit Support' ),
			arch: '64',
			sort: 21,
		},
		linux: {
			icon: 'linux',
			tooltip: gettextCatalog.getString( 'Linux Support' ),
			sort: 30,
		},
		linux_64: {
			icon: 'linux',
			tooltip: gettextCatalog.getString( 'Linux 64-bit Support' ),
			arch: '64',
			sort: 30,
		},
		other: {
			icon: 'other-os',
			tooltip: gettextCatalog.getString( 'Downloadable File' ),
			sort: 40,
		},
		html: {
			icon: 'html5',
			tooltip: gettextCatalog.getString( 'Web Playable' ),
			sort: 50,
		},
		flash: {
			icon: 'flash',
			tooltip: gettextCatalog.getString( 'Flash Web Playable' ),
			sort: 51,
		},
		unity: {
			icon: 'unity',
			tooltip: gettextCatalog.getString( 'Unity Web Playable' ),
			sort: 52,
		},
		applet: {
			icon: 'java',
			tooltip: gettextCatalog.getString( 'Java Applet Web Playable' ),
			sort: 53,
		},
		silverlight: {
			icon: 'silverlight',
			tooltip: gettextCatalog.getString( 'Silverlight Web Playable' ),
			sort: 54,
		},
		rom: {
			icon: 'rom',
			tooltip: gettextCatalog.getString( 'ROM' ),
			sort: 60,
		},
	};

	var emulatorInfo = {};
	emulatorInfo[ Game_Build.EMULATOR_GB ] = gettextCatalog.getString( 'Game Boy' );
	emulatorInfo[ Game_Build.EMULATOR_GBC ] = gettextCatalog.getString( 'Game Boy Color' );
	emulatorInfo[ Game_Build.EMULATOR_GBA ] = gettextCatalog.getString( 'Game Boy Advance' );

	function pluckOsSupport( build )
	{
		var support = [];

		// We only include the 64-bit versions if the build doesn't have 32bit and 64bit
		// on the same build. That basically just means it's a universal build.

		if ( build.os_windows ) {
			support.push( 'windows' );
		}

		if ( build.os_windows_64 && !build.os_windows ) {
			support.push( 'windows_64' );
		}

		if ( build.os_mac ) {
			support.push( 'mac' );
		}

		if ( build.os_mac_64 && !build.os_mac ) {
			support.push( 'mac_64' );
		}

		if ( build.os_linux ) {
			support.push( 'linux' );
		}

		if ( build.os_linux_64 && !build.os_linux ) {
			support.push( 'linux_64' );
		}

		if ( build.os_other ) {
			support.push( 'other' );
		}

		return support;
	}

	function checkSupport( support, type )
	{
		return support.indexOf( type ) !== -1;
	}

	return {
		restrict: 'E',
		templateUrl: '/lib/gj-lib-client/components/game/package/card/card.html',
		scope: {},
		bindToController: {
			game: '=game',
			'package': '=gamePackage',
			sellable: '=?sellable',
			releases: '=gameReleases',
			builds: '=gameBuilds',
			key: '@?',
			launchOptions: '=?gameBuildLaunchOptions',
		},
		controllerAs: 'ctrl',
		controller: function( $scope, $attrs, $parse )
		{
			var _this = this;

			$scope.Environment = Environment;
			$scope.Screen = Screen;
			$scope.Game_Build = Game_Build;

			this.showFullDescription = false;
			this.canToggleDescription = undefined;

			this.emulatorInfo = emulatorInfo;
			this.supportInfo = angular.copy( supportInfo );
			this.support = [];

			this.downloadableBuild = null;
			this.browserBuild = null;
			this.extraBuilds = [];

			this.showcasedRelease = null;
			this.showcasedOs = null;
			this.showcasedOsIcon = null;
			this.showcasedBrowserIcon = null;

			this.otherOnly = false;

			// If this game is in their installed games, this will populate.
			this.installedBuild = null;

			this.isOwned = this.sellable && this.sellable.is_owned ? true : false;
			this.isWhatOpen = false;
			this.isPaymentOpen = false;
			this.clickedBuild = null;

			// If there is a key on the package, then we should show it as being "owned".
			if ( this.key ) {
				this.isOwned = true;
			}

			if ( this.sellable && angular.isArray( this.sellable.pricings ) ) {
				this.pricing = this.sellable.pricings[0];
			}

			this.hasPaymentWell = false;
			if ( this.sellable && !this.isOwned && (this.sellable.type == 'pwyw' || this.sellable.type == 'paid') ) {
				this.hasPaymentWell = true;
			}

			// Event to be able to open up the payment form.
			$scope.$on( 'Game_Package_Card.showPaymentOptions', function( event, package )
			{
				// Spoof that we've clicked to "download" the package.
				// This will ensure that the payment well opens with the correct
				// build for "skip paying".
				if ( _this.package.id == package.id ) {
					_this.buildClick( _this.downloadableBuild );
				}
			} );

			if ( this.builds ) {

				var os = Device.os();
				var arch = Device.arch();

				// Indexes by the os/type of the build: [ { type: build } ]
				// While looping we also gather all OS/browser support for this complete package.
				var indexedBuilds = {};
				var otherBuilds = [];
				this.builds.forEach( function( build )
				{
					if ( build.isBrowserBased() ) {
						indexedBuilds[ build.type ] = build;
						this.support.push( build.type );
					}
					else if ( build.type == Game_Build.TYPE_ROM ) {
						indexedBuilds[ build.type ] = build;
						this.support.push( build.type );
						otherBuilds.push( build );
					}
					else if ( build.os_other ) {
						otherBuilds.push( build );
					}
					else {
						var support = pluckOsSupport( build );
						support.forEach( function( _os )
						{
							indexedBuilds[ _os ] = build;
							this.support.push( this.supportInfo[ _os ].icon );
						}, this );
					}
				}, this );

				this.support = _.uniq( this.support );

				// At this point we should have all the OS/browser support, so let's sort it.
				// The sort values are defined above, but we want to push their detected OS at
				// the front. This is because you'd probably want to see [linux] first if you're
				// on a linux machine, before windows, etc.
				// We change the sort for their detected OS to be the first before sorting.
				this.supportInfo[ os ].sort = 0;
				this.support.sort( function( a, b )
				{
					return _this.supportInfo[ a ].sort - _this.supportInfo[ b ].sort;
				} );

				// Now that we have all the builds indexed by the platform they support
				// we need to try to pick one to showcase as the default download. We put
				// their detected OS first so that it tries to pick that one first.
				var checkDownloadables = [ 'windows', 'windows_64', 'mac', 'mac_64', 'linux', 'linux_64' ];

				// This will put the 64 bit version as higher priority.
				if ( arch == '64' ) {
					checkDownloadables.unshift( os );
					checkDownloadables.unshift( os + '_64' );
				}
				// If we don't have a detected arch that is 64, we prioritize the universal version.
				else {
					checkDownloadables.unshift( os + '_64' );
					checkDownloadables.unshift( os );
				}

				checkDownloadables.every( function( _os )
				{
					if ( !indexedBuilds[ _os ] ) {
						return true;
					}
					this.downloadableBuild = indexedBuilds[ _os ];
					this.showcasedOs = _os;
					this.showcasedOsIcon = _this.supportInfo[ _os ].icon;
				}, this );

				// Do the same with browser type. Pick the default browser one to show.
				// We include ROMs in browser play.
				[ 'html', 'flash', 'unity', 'applet', 'silverlight', 'rom' ].every( function( type )
				{
					if ( !indexedBuilds[ type ] ) {
						return true;
					}
					this.browserBuild = indexedBuilds[ type ];
					this.showcasedBrowserIcon = _this.supportInfo[ type ].icon;
				}, this );

				// Pull the showcased release version.
				// It should be the greater one for either the downloadable or browser build chosen.
				if ( this.downloadableBuild ) {
					this.showcasedRelease = this.downloadableBuild._release;
				}

				// Lower sort value is a "newer" version.
				if ( this.browserBuild && (!this.showcasedRelease || this.browserBuild._release.sort < this.showcasedRelease) ) {
					this.showcasedRelease = this.browserBuild._release;
				}

				// Now pull the extra builds (ones that aren't default).
				angular.forEach( indexedBuilds, function( build, type )
				{
					if ( build == _this.downloadableBuild && type == _this.showcasedOs ) {
						return;
					}

					if ( build.type != Game_Build.TYPE_DOWNLOADABLE ) {
						if ( _this.browserBuild && _this.browserBuild.id == build.id ) {
							return;
						}
					}

					_this.extraBuilds.push( {
						type: build.type,
						icon: _this.supportInfo[ type ].icon,
						build: build,
						arch: _this.supportInfo[ type ].arch || null,
						platform: type,
					} );
				} );

				// Add all the "Other" builds onto the end of extra.
				if ( otherBuilds.length ) {
					otherBuilds.forEach( function( build )
					{
						var supportKey = 'other';
						if ( build.type == Game_Build.TYPE_ROM ) {
							supportKey = 'rom';
						}

						_this.extraBuilds.push( {
							type: build.type,
							icon: _this.supportInfo[ supportKey ].icon,
							build: build,
							platform: supportKey,
						} );
					} );
				}

				// Sort extra builds if there are any.
				if ( this.extraBuilds.length ) {
					this.extraBuilds.sort( function( a, b )
					{
						return _this.supportInfo[ a.platform ].sort - _this.supportInfo[ b.platform ].sort;
					} );
				}

				if ( !this.downloadableBuild && !this.browserBuild && otherBuilds.length ) {
					this.otherOnly = true;
					this.showcasedRelease = this.releases[0];
				}
			}

			this.buildClick = function( build, fromExtraSection )
			{
				// For client, if they clicked in the "options" section, then skip showing payment form.
				// Just take them directly to site.
				if ( Environment.isClient && fromExtraSection ) {
					this._doBuildClick( build, fromExtraSection );
				}
				// This will show the payment form if we're supposed to.
				else if ( this.showPayment( build ) ) {
				}
				// Otherwise direct to the build.
				else {
					this._doBuildClick( build, fromExtraSection );
				}
			};

			this._doBuildClick = function( build, fromExtraSection )
			{
				var operation = build.type == Game_Build.TYPE_DOWNLOADABLE ? 'download' : 'play';
				if ( build.type == Game_Build.TYPE_ROM && fromExtraSection ) {
					operation = 'download';
				}

				if ( operation == 'download' ) {
					this._download( build );
				}
				else if ( operation == 'play' ) {
					this._showBrowserModal( build );
				}
			}

			this.showPayment = function( build )
			{
				// If this isn't a free game, then we want to slide the payment open.
				// If it's pay what you want, when the payment is open and they click a build again, just take them to it.
				if ( this.hasPaymentWell ) {
					if ( !this.isPaymentOpen ) {
						this.isPaymentOpen = true;
						this.clickedBuild = build;
						return true;
					}
				}

				return false;
			};

			this.skipPayment = function()
			{
				// When they skip a pwyw payment form, on client we need to start the install.
				// On site we treat it like a normal build click.
				if ( Environment.isClient ) {
					this.startInstall( this.clickedBuild );
				}
				else {
					this.buildClick( this.clickedBuild );
				}
			};

			this._download = function( build )
			{
				Analytics.trackEvent( 'game-package-card', 'download', 'download' );

				Game_Downloader.download( this.game, build, {
					isOwned: this.sellable && this.isOwned,
					key: this.key,
				} );
			};

			this._showBrowserModal = function( build )
			{
				Analytics.trackEvent( 'game-package-card', 'download', 'play' );

				Game_PlayModal.show( this.game, build, {
					isOwned: this.sellable && this.isOwned,
					key: this.key,
				} );
			};

			this.integer = function()
			{
				return Math.floor( this.pricing.amount / 100 );
			};

			this.decimal = function()
			{
				var amount = this.pricing.amount;

				amount %= 100;
				if ( amount < 10 ) {
					amount = amount + '0';
				}
				return '' + amount;
			};

			this.onPackageBought = function()
			{
				this.isWhatOpen = false;
				this.isPaymentOpen = false;
				this.hasPaymentWell = false;
				this.isOwned = true;
			};
		}
	};
} );
<commit_msg>Showing a Growl to show that you've ordered the game.<commit_after>angular.module( 'gj.Game.Package.Card' ).directive( 'gjGamePackageCard', function(
	$timeout, $injector, gettextCatalog, Growls, Screen, Game, Game_Build, Game_PlayModal, Game_Downloader, Device, Environment, Analytics, currencyFilter )
{
	/**
	 * Sort must start at 1 so that we can put their prefered OS as sort 0 later on.
	 */
	var supportInfo = {
		windows: {
			icon: 'windows',
			tooltip: gettextCatalog.getString( 'Windows Support' ),
			sort: 10,
		},
		windows_64: {
			icon: 'windows',
			tooltip: gettextCatalog.getString( 'Windows 64-bit Support' ),
			arch: '64',
			sort: 11,
		},
		mac: {
			icon: 'mac',
			tooltip: gettextCatalog.getString( 'Mac Support' ),
			sort: 20,
		},
		mac_64: {
			icon: 'mac',
			tooltip: gettextCatalog.getString( 'Mac 64-bit Support' ),
			arch: '64',
			sort: 21,
		},
		linux: {
			icon: 'linux',
			tooltip: gettextCatalog.getString( 'Linux Support' ),
			sort: 30,
		},
		linux_64: {
			icon: 'linux',
			tooltip: gettextCatalog.getString( 'Linux 64-bit Support' ),
			arch: '64',
			sort: 30,
		},
		other: {
			icon: 'other-os',
			tooltip: gettextCatalog.getString( 'Downloadable File' ),
			sort: 40,
		},
		html: {
			icon: 'html5',
			tooltip: gettextCatalog.getString( 'Web Playable' ),
			sort: 50,
		},
		flash: {
			icon: 'flash',
			tooltip: gettextCatalog.getString( 'Flash Web Playable' ),
			sort: 51,
		},
		unity: {
			icon: 'unity',
			tooltip: gettextCatalog.getString( 'Unity Web Playable' ),
			sort: 52,
		},
		applet: {
			icon: 'java',
			tooltip: gettextCatalog.getString( 'Java Applet Web Playable' ),
			sort: 53,
		},
		silverlight: {
			icon: 'silverlight',
			tooltip: gettextCatalog.getString( 'Silverlight Web Playable' ),
			sort: 54,
		},
		rom: {
			icon: 'rom',
			tooltip: gettextCatalog.getString( 'ROM' ),
			sort: 60,
		},
	};

	var emulatorInfo = {};
	emulatorInfo[ Game_Build.EMULATOR_GB ] = gettextCatalog.getString( 'Game Boy' );
	emulatorInfo[ Game_Build.EMULATOR_GBC ] = gettextCatalog.getString( 'Game Boy Color' );
	emulatorInfo[ Game_Build.EMULATOR_GBA ] = gettextCatalog.getString( 'Game Boy Advance' );

	function pluckOsSupport( build )
	{
		var support = [];

		// We only include the 64-bit versions if the build doesn't have 32bit and 64bit
		// on the same build. That basically just means it's a universal build.

		if ( build.os_windows ) {
			support.push( 'windows' );
		}

		if ( build.os_windows_64 && !build.os_windows ) {
			support.push( 'windows_64' );
		}

		if ( build.os_mac ) {
			support.push( 'mac' );
		}

		if ( build.os_mac_64 && !build.os_mac ) {
			support.push( 'mac_64' );
		}

		if ( build.os_linux ) {
			support.push( 'linux' );
		}

		if ( build.os_linux_64 && !build.os_linux ) {
			support.push( 'linux_64' );
		}

		if ( build.os_other ) {
			support.push( 'other' );
		}

		return support;
	}

	function checkSupport( support, type )
	{
		return support.indexOf( type ) !== -1;
	}

	return {
		restrict: 'E',
		templateUrl: '/lib/gj-lib-client/components/game/package/card/card.html',
		scope: {},
		bindToController: {
			game: '=game',
			'package': '=gamePackage',
			sellable: '=?sellable',
			releases: '=gameReleases',
			builds: '=gameBuilds',
			key: '@?',
			launchOptions: '=?gameBuildLaunchOptions',
		},
		controllerAs: 'ctrl',
		controller: function( $scope, $attrs, $parse )
		{
			var _this = this;

			$scope.Environment = Environment;
			$scope.Screen = Screen;
			$scope.Game_Build = Game_Build;

			this.showFullDescription = false;
			this.canToggleDescription = undefined;

			this.emulatorInfo = emulatorInfo;
			this.supportInfo = angular.copy( supportInfo );
			this.support = [];

			this.downloadableBuild = null;
			this.browserBuild = null;
			this.extraBuilds = [];

			this.showcasedRelease = null;
			this.showcasedOs = null;
			this.showcasedOsIcon = null;
			this.showcasedBrowserIcon = null;

			this.otherOnly = false;

			// If this game is in their installed games, this will populate.
			this.installedBuild = null;

			this.isOwned = this.sellable && this.sellable.is_owned ? true : false;
			this.isWhatOpen = false;
			this.isPaymentOpen = false;
			this.clickedBuild = null;

			// If there is a key on the package, then we should show it as being "owned".
			if ( this.key ) {
				this.isOwned = true;
			}

			if ( this.sellable && angular.isArray( this.sellable.pricings ) ) {
				this.pricing = this.sellable.pricings[0];
			}

			this.hasPaymentWell = false;
			if ( this.sellable && !this.isOwned && (this.sellable.type == 'pwyw' || this.sellable.type == 'paid') ) {
				this.hasPaymentWell = true;
			}

			// Event to be able to open up the payment form.
			$scope.$on( 'Game_Package_Card.showPaymentOptions', function( event, package )
			{
				// Spoof that we've clicked to "download" the package.
				// This will ensure that the payment well opens with the correct
				// build for "skip paying".
				if ( _this.package.id == package.id ) {
					_this.buildClick( _this.downloadableBuild );
				}
			} );

			if ( this.builds ) {

				var os = Device.os();
				var arch = Device.arch();

				// Indexes by the os/type of the build: [ { type: build } ]
				// While looping we also gather all OS/browser support for this complete package.
				var indexedBuilds = {};
				var otherBuilds = [];
				this.builds.forEach( function( build )
				{
					if ( build.isBrowserBased() ) {
						indexedBuilds[ build.type ] = build;
						this.support.push( build.type );
					}
					else if ( build.type == Game_Build.TYPE_ROM ) {
						indexedBuilds[ build.type ] = build;
						this.support.push( build.type );
						otherBuilds.push( build );
					}
					else if ( build.os_other ) {
						otherBuilds.push( build );
					}
					else {
						var support = pluckOsSupport( build );
						support.forEach( function( _os )
						{
							indexedBuilds[ _os ] = build;
							this.support.push( this.supportInfo[ _os ].icon );
						}, this );
					}
				}, this );

				this.support = _.uniq( this.support );

				// At this point we should have all the OS/browser support, so let's sort it.
				// The sort values are defined above, but we want to push their detected OS at
				// the front. This is because you'd probably want to see [linux] first if you're
				// on a linux machine, before windows, etc.
				// We change the sort for their detected OS to be the first before sorting.
				this.supportInfo[ os ].sort = 0;
				this.support.sort( function( a, b )
				{
					return _this.supportInfo[ a ].sort - _this.supportInfo[ b ].sort;
				} );

				// Now that we have all the builds indexed by the platform they support
				// we need to try to pick one to showcase as the default download. We put
				// their detected OS first so that it tries to pick that one first.
				var checkDownloadables = [ 'windows', 'windows_64', 'mac', 'mac_64', 'linux', 'linux_64' ];

				// This will put the 64 bit version as higher priority.
				if ( arch == '64' ) {
					checkDownloadables.unshift( os );
					checkDownloadables.unshift( os + '_64' );
				}
				// If we don't have a detected arch that is 64, we prioritize the universal version.
				else {
					checkDownloadables.unshift( os + '_64' );
					checkDownloadables.unshift( os );
				}

				checkDownloadables.every( function( _os )
				{
					if ( !indexedBuilds[ _os ] ) {
						return true;
					}
					this.downloadableBuild = indexedBuilds[ _os ];
					this.showcasedOs = _os;
					this.showcasedOsIcon = _this.supportInfo[ _os ].icon;
				}, this );

				// Do the same with browser type. Pick the default browser one to show.
				// We include ROMs in browser play.
				[ 'html', 'flash', 'unity', 'applet', 'silverlight', 'rom' ].every( function( type )
				{
					if ( !indexedBuilds[ type ] ) {
						return true;
					}
					this.browserBuild = indexedBuilds[ type ];
					this.showcasedBrowserIcon = _this.supportInfo[ type ].icon;
				}, this );

				// Pull the showcased release version.
				// It should be the greater one for either the downloadable or browser build chosen.
				if ( this.downloadableBuild ) {
					this.showcasedRelease = this.downloadableBuild._release;
				}

				// Lower sort value is a "newer" version.
				if ( this.browserBuild && (!this.showcasedRelease || this.browserBuild._release.sort < this.showcasedRelease) ) {
					this.showcasedRelease = this.browserBuild._release;
				}

				// Now pull the extra builds (ones that aren't default).
				angular.forEach( indexedBuilds, function( build, type )
				{
					if ( build == _this.downloadableBuild && type == _this.showcasedOs ) {
						return;
					}

					if ( build.type != Game_Build.TYPE_DOWNLOADABLE ) {
						if ( _this.browserBuild && _this.browserBuild.id == build.id ) {
							return;
						}
					}

					_this.extraBuilds.push( {
						type: build.type,
						icon: _this.supportInfo[ type ].icon,
						build: build,
						arch: _this.supportInfo[ type ].arch || null,
						platform: type,
					} );
				} );

				// Add all the "Other" builds onto the end of extra.
				if ( otherBuilds.length ) {
					otherBuilds.forEach( function( build )
					{
						var supportKey = 'other';
						if ( build.type == Game_Build.TYPE_ROM ) {
							supportKey = 'rom';
						}

						_this.extraBuilds.push( {
							type: build.type,
							icon: _this.supportInfo[ supportKey ].icon,
							build: build,
							platform: supportKey,
						} );
					} );
				}

				// Sort extra builds if there are any.
				if ( this.extraBuilds.length ) {
					this.extraBuilds.sort( function( a, b )
					{
						return _this.supportInfo[ a.platform ].sort - _this.supportInfo[ b.platform ].sort;
					} );
				}

				if ( !this.downloadableBuild && !this.browserBuild && otherBuilds.length ) {
					this.otherOnly = true;
					this.showcasedRelease = this.releases[0];
				}
			}

			this.buildClick = function( build, fromExtraSection )
			{
				// For client, if they clicked in the "options" section, then skip showing payment form.
				// Just take them directly to site.
				if ( Environment.isClient && fromExtraSection ) {
					this._doBuildClick( build, fromExtraSection );
				}
				// This will show the payment form if we're supposed to.
				else if ( this.showPayment( build ) ) {
				}
				// Otherwise direct to the build.
				else {
					this._doBuildClick( build, fromExtraSection );
				}
			};

			this._doBuildClick = function( build, fromExtraSection )
			{
				var operation = build.type == Game_Build.TYPE_DOWNLOADABLE ? 'download' : 'play';
				if ( build.type == Game_Build.TYPE_ROM && fromExtraSection ) {
					operation = 'download';
				}

				if ( operation == 'download' ) {
					this._download( build );
				}
				else if ( operation == 'play' ) {
					this._showBrowserModal( build );
				}
			}

			this.showPayment = function( build )
			{
				// If this isn't a free game, then we want to slide the payment open.
				// If it's pay what you want, when the payment is open and they click a build again, just take them to it.
				if ( this.hasPaymentWell ) {
					if ( !this.isPaymentOpen ) {
						this.isPaymentOpen = true;
						this.clickedBuild = build;
						return true;
					}
				}

				return false;
			};

			this.skipPayment = function()
			{
				// When they skip a pwyw payment form, on client we need to start the install.
				// On site we treat it like a normal build click.
				if ( Environment.isClient ) {
					this.startInstall( this.clickedBuild );
				}
				else {
					this.buildClick( this.clickedBuild );
				}
			};

			this._download = function( build )
			{
				Analytics.trackEvent( 'game-package-card', 'download', 'download' );

				Game_Downloader.download( this.game, build, {
					isOwned: this.sellable && this.isOwned,
					key: this.key,
				} );
			};

			this._showBrowserModal = function( build )
			{
				Analytics.trackEvent( 'game-package-card', 'download', 'play' );

				Game_PlayModal.show( this.game, build, {
					isOwned: this.sellable && this.isOwned,
					key: this.key,
				} );
			};

			this.integer = function()
			{
				return Math.floor( this.pricing.amount / 100 );
			};

			this.decimal = function()
			{
				var amount = this.pricing.amount;

				amount %= 100;
				if ( amount < 10 ) {
					amount = amount + '0';
				}
				return '' + amount;
			};

			this.onPackageBought = function()
			{
				this.isWhatOpen = false;
				this.isPaymentOpen = false;
				this.hasPaymentWell = false;
				this.isOwned = true;

				Growls.success( {
					title: gettextCatalog.getString( 'Order Complete' ),
					message: gettextCatalog.getString( 'Warm thanks from both {{ developer }} and the Game Jolt team.', { developer: this.game.developer.display_name } ),
					sticky: true,
				} );
			};
		}
	};
} );
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {blue100} from 'material-ui/styles/colors';
import {DayProgram} from './ProgramComponents'

const fridayPlan = [
  {hour: '', 'groups': []},
  {hour: '20h', 'groups': [false,
                           {name: 'Baila Tocamos (BE) bal', duration: 2, kind: 'bal'},
                           false, false]},
  {hour: '21h', 'groups': [false, true, false, false]},
  {hour: '22h', 'groups': [{name: 'Duo Montanaro-Cavez (BE-FR) concert', duration: 2, kind: 'concert'},
                           false,
                           {name: 'Wouter Vandenabeele et Jeroen Knapen (BE) bal', duration: 2, kind: 'bal'},
                           false]},
  {hour: '23h', 'groups': [true, false, true, false]},
  {hour: '00h', 'groups': [false,
                           {name: 'Les Zéoles (FR) bal', duration: 2, kind: 'bal'},
                           false, false]},
  {hour: '01h', 'groups': [false, true, false, false]},
];

export default class FridayProgram extends React.Component {
  render() {
		return (
      <DayProgram plan={fridayPlan} lang={this.props.lang} />
    );
  }
}
<commit_msg>Update FridayProgram.js<commit_after>import React, { Component } from 'react';
import {blue100} from 'material-ui/styles/colors';
import {DayProgram} from './ProgramComponents'

const fridayPlan = [
  {hour: '', 'groups': []},
  {hour: '20h', 'groups': [false,
  			   {name: 'Baila Tocamos (BE) bal', duration: 4, kind: 'bal'},
  			   false, false]},
  {hour: '20h30', 'groups': [false, true, false, false]},
  {hour: '21h', 'groups': [false, true, false, false]},
  {hour: '21h30', 'groups': [false, true, false, false]},
  {hour: '22h', 'groups': [{name: 'Duo Montanaro-Cavez (BE-FR) concert', duration: 3, kind: 'concert'},
                           true,
                           {name: 'Wouter Vandenabeele et Jeroen Knapen (BE) bal', duration: 4, kind: 'bal'},
                           false]},
  {hour: '22h30', 'groups': [true, false, true, false]},
  {hour: '23h', 'groups': [true, false, true, false]},
  {hour: '23h30', 'groups': [true, false, true, false]},
  {hour: '00h', 'groups': [false,
                           {name: 'Les Zéoles (FR) bal', duration: 4, kind: 'bal'},
                           true, false]},
  {hour: '00h30', 'groups': [false, true, false, false]},
  {hour: '01h', 'groups': [false, true, false, false]},
  {hour: '01h30', 'groups': [false, true, false, false]},
  {hour: '02h', 'groups': [false, true, false, false]},
];

export default class FridayProgram extends React.Component {
  render() {
		return (
      <DayProgram plan={fridayPlan} lang={this.props.lang} />
    );
  }
}
<|endoftext|>


<commit_before>;(function () {

    var addEvent =  function(el, type, fn, capture) {
        if (type === "mousewheel" && document.mozHidden !== undefined) {
            type = "DOMMouseScroll";
        }
        el.addEventListener(type, function(event) {
            var type = event.type;
            if (type == "DOMMouseScroll" || type == "mousewheel") {
                event.delta = event.wheelDelta ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
            }
            fn.call(this, event);
        }, capture || false);
    };

    function addClass(element, className) {

        if (element.classList) {
            element.classList.add(className);
        } else {
            element.className += ' ' + className;
        }

    }

    function removeClass(element, className) {

        if (element.classList) {
            element.classList.remove(className);
        } else {
            element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
        }

    }

    var FullPage = function(selector,option) {
        this.parent = typeof selector === "string" ? document.querySelector(selector) : selector;
        this.parent.style.visibility = "visible";
        var noop = function () { };
        this.beginToPage = option.beginToPage || noop;
        this.leavePage = option.leavePage || noop;
        Transform(this.parent, true);
        this.stepHeight = window.innerHeight;
        var children = this.parent.childNodes,
            len = children.length,
            i = 0;
        this.length = 0;
        this.children = [];
        for (; i < len; i++) {
            var child = children[i];
            if (child.nodeType !== 3) {
                child.style.height = this.stepHeight + "px";
                this.length++;
                this.children.push({child:child,transitionChildren:child.querySelectorAll(".animated")});
            }
        }

        this.duration = 600;

        this._transitionInit();

        var self = this;

        this.alloyTouch = new AlloyTouch({
            touch: this.parent,
            target: this.parent,
            property: "translateY",
            min: (1 - this.length) * this.stepHeight,
            max: 0,
            step: this.stepHeight,
            inertia: false,
            bindSelf : true,
            touchEnd: function (evt, v, index) {

                var step_v = index * this.step * -1;
                var dx = v - step_v;

                if (v < this.min) {
                    this.to(this.min);
                } else if (v > this.max) {
                    this.to(this.max);
                } else if (Math.abs(dx) < 30) {
                    this.to(step_v);

                }else if (dx > 0) {
                   // this.to(step_v + this.step);
                    self.prev();
                } else {
                    self.next();
                   // this.to(step_v - this.step);
                }
                return false;
            },
            animationEnd: function () {
                option.animationEnd.apply(this,arguments);
                self.moving = false;
            }
        });

        this.moving = false;

        addEvent(this.parent,"mousewheel" ,function (evt) {
            if(self.moving) return;
            self.moving = true;
            if(evt.delta>0 ){
                self.prev();
            }else {
                self.next();
            }
        });
    };

    FullPage.prototype = {
        _transitionInit:function(){
            var i = 1 ,
                len = this.children.length;
            for(;i<len;i++){
              this._outPage(i);
            }
            this._toPage(0);
        },
        _toPage:function(index){
            var ttcs = this.children[index].transitionChildren;
            var  i = 0;
            var tLen = ttcs.length;
            if (tLen > 0) {
                for (; i < tLen; i++) {
                    var node = ttcs[i];
                    var delay = parseInt(node.getAttribute("data-delay")) || 0;
                    var showClass = node.getAttribute("data-show");
                    var hideClass = node.getAttribute("data-hide");
                    node.style.visibility = "hidden";
                    setTimeout((function(n,s,h){
                        return function(){
                            n.style.visibility = "visible";
                            s&&addClass(n,s);
                            h&&removeClass(n,h);
                        }
                    })(node,showClass,hideClass),delay);
                }
            }
        },
        _outPage:function(index){
            var ltcs = this.children[index].transitionChildren;
            var lLen = ltcs.length,
                i = 0;
            if (lLen > 0) {
                for (; i < lLen; i++) {
                    var node = ltcs[i];
                    var showClass = node.getAttribute("data-show");
                    var hideClass = node.getAttribute("data-hide");
                    hideClass&&addClass(node,hideClass);
                    showClass&&removeClass(node,showClass);
                }
            }
        },
        _transition: function (leaveIndex, toIndex) {
            this._outPage(leaveIndex);
            this. _toPage(toIndex);
        },
        next: function () {
            var index = this.alloyTouch.currentPage + 1;

            if (index > this.length - 1) {
                index = this.length - 1;
                this.moving = false;
                return;
            }
            this.to(index);
        },
        prev: function () {
            var index = this.alloyTouch.currentPage - 1;

            if (index < 0) {
                index = 0;
                this.moving = false;
                return;
            }
            this.to(index);
        },
        to: function (index) {

            this.leavePage.call(this, this.alloyTouch.currentPage);
            this.beginToPage.call(this, index);
            this._transition(this.alloyTouch.currentPage, index);
            this.alloyTouch.to(-1 * index * this.stepHeight,this.duration);
        }
    };

    AlloyTouch.FullPage = FullPage;
})();<commit_msg>Update alloy_touch.full_page.js<commit_after>;(function () {

    var addEvent =  function(el, type, fn, capture) {
        if (type === "mousewheel" && document.mozHidden !== undefined) {
            type = "DOMMouseScroll";
        }
        el.addEventListener(type, function(event) {
            var type = event.type;
            if (type == "DOMMouseScroll" || type == "mousewheel") {
                event.delta = event.wheelDelta ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
            }

            if (event.delta) {
                fn.call(this, event);
            }
        }, capture || false);
    };

    function addClass(element, className) {

        if (element.classList) {
            element.classList.add(className);
        } else {
            element.className += ' ' + className;
        }

    }

    function removeClass(element, className) {

        if (element.classList) {
            element.classList.remove(className);
        } else {
            element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
        }

    }

    var FullPage = function(selector,option) {
        this.parent = typeof selector === "string" ? document.querySelector(selector) : selector;
        this.parent.style.visibility = "visible";
        var noop = function () { };
        this.beginToPage = option.beginToPage || noop;
        this.leavePage = option.leavePage || noop;
        Transform(this.parent, true);
        this.stepHeight = window.innerHeight;
        var children = this.parent.childNodes,
            len = children.length,
            i = 0;
        this.length = 0;
        this.children = [];
        for (; i < len; i++) {
            var child = children[i];
            if (child.nodeType !== 3) {
                child.style.height = this.stepHeight + "px";
                this.length++;
                this.children.push({child:child,transitionChildren:child.querySelectorAll(".animated")});
            }
        }

        this.duration = 600;

        this._transitionInit();

        var self = this;

        this.alloyTouch = new AlloyTouch({
            touch: this.parent,
            target: this.parent,
            property: "translateY",
            min: (1 - this.length) * this.stepHeight,
            max: 0,
            step: this.stepHeight,
            inertia: false,
            bindSelf : true,
            touchEnd: function (evt, v, index) {

                var step_v = index * this.step * -1;
                var dx = v - step_v;

                if (v < this.min) {
                    this.to(this.min);
                } else if (v > this.max) {
                    this.to(this.max);
                } else if (Math.abs(dx) < 30) {
                    this.to(step_v);

                }else if (dx > 0) {
                   // this.to(step_v + this.step);
                    self.prev();
                } else {
                    self.next();
                   // this.to(step_v - this.step);
                }
                return false;
            },
            animationEnd: function () {
                option.animationEnd.apply(this,arguments);
                self.moving = false;
            }
        });

        this.moving = false;

        addEvent(this.parent,"mousewheel" ,function (evt) {
            if(self.moving) return;
            self.moving = true;
            if(evt.delta>0 ){
                self.prev();
            }else {
                self.next();
            }
        });
    };

    FullPage.prototype = {
        _transitionInit:function(){
            var i = 1 ,
                len = this.children.length;
            for(;i<len;i++){
              this._outPage(i);
            }
            this._toPage(0);
        },
        _toPage:function(index){
            var ttcs = this.children[index].transitionChildren;
            var  i = 0;
            var tLen = ttcs.length;
            if (tLen > 0) {
                for (; i < tLen; i++) {
                    var node = ttcs[i];
                    var delay = parseInt(node.getAttribute("data-delay")) || 0;
                    var showClass = node.getAttribute("data-show");
                    var hideClass = node.getAttribute("data-hide");
                    node.style.visibility = "hidden";
                    setTimeout((function(n,s,h){
                        return function(){
                            n.style.visibility = "visible";
                            s&&addClass(n,s);
                            h&&removeClass(n,h);
                        }
                    })(node,showClass,hideClass),delay);
                }
            }
        },
        _outPage:function(index){
            var ltcs = this.children[index].transitionChildren;
            var lLen = ltcs.length,
                i = 0;
            if (lLen > 0) {
                for (; i < lLen; i++) {
                    var node = ltcs[i];
                    var showClass = node.getAttribute("data-show");
                    var hideClass = node.getAttribute("data-hide");
                    hideClass&&addClass(node,hideClass);
                    showClass&&removeClass(node,showClass);
                }
            }
        },
        _transition: function (leaveIndex, toIndex) {
            this._outPage(leaveIndex);
            this. _toPage(toIndex);
        },
        next: function () {
            var index = this.alloyTouch.currentPage + 1;

            if (index > this.length - 1) {
                index = this.length - 1;
                this.moving = false;
                return;
            }
            this.to(index);
        },
        prev: function () {
            var index = this.alloyTouch.currentPage - 1;

            if (index < 0) {
                index = 0;
                this.moving = false;
                return;
            }
            this.to(index);
        },
        to: function (index) {

            this.leavePage.call(this, this.alloyTouch.currentPage);
            this.beginToPage.call(this, index);
            this._transition(this.alloyTouch.currentPage, index);
            this.alloyTouch.to(-1 * index * this.stepHeight,this.duration);
        }
    };

    AlloyTouch.FullPage = FullPage;
})();
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Grocery = mongoose.model('Grocery'),
	_ = require('lodash');

/**
 * Create a Grocery
 */
exports.create = function(req, res) {
	var grocery = new Grocery(req.body);
	grocery.user = req.user;

	grocery.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(grocery);
		}
	});
};

exports.createAll = function(req, res, next) {
  var importShops = req.body;
  var bulk = Grocery.collection.initializeUnorderedBulkOp();
  importShops.forEach(function(product) {
    if (product)
      bulk.insert(product);
  })
  bulk.execute(function (err,result) {
    if (err) {
      return res.status(400).send({
        message: errorHandler.getErrorMessage(err)
      });
    } else {
      res.jsonp(result);
    }
  });
};

/**
 * Show the current Grocery
 */
exports.read = function(req, res) {
	res.jsonp(req.grocery);
};

/**
 * Update a Grocery
 */
exports.update = function(req, res) {
	var grocery = req.grocery ;

	grocery = _.extend(grocery , req.body);

	grocery.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(grocery);
		}
	});
};

/**
 * Delete an Grocery
 */
exports.delete = function(req, res) {
	var grocery = req.grocery ;

	grocery.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(grocery);
		}
	});
};

/**
 * List of Groceries
 */
exports.list = function(req, res) {
	Grocery.find().sort('-created').populate('user', 'displayName').exec(function(err, groceries) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(groceries);
		}
	});
};

exports.Search = function(req, res) {
  console.log("****")
  console.log(req.params.searchText)
  Grocery.find({'name':new RegExp(req.params.searchText,"i")}).exec(function(err, groceries) {
    if (err) {
      return res.status(400).send({
        message: errorHandler.getErrorMessage(err)
      });
    } else {
      res.jsonp(groceries);
    }
  });
};

/**
 * List of pagination
 */
exports.listPage = function(req, res) {
  if(!req.params.page)
  {
    var page = 1;
  }else{
    var page = req.params.page;
  }
  var per_page = 12;
  Grocery.find({'category':req.params.category}).sort('-created').skip((page-1)*per_page).limit(per_page).exec(function(err, fruits) {
    if (err) {
      return res.status(400).send({
        message: errorHandler.getErrorMessage(err)
      });
    } else {
      res.jsonp(fruits);
    }
  });
};

/**
 * Grocery middleware
 */
exports.groceryByID = function(req, res, next, id) {
	Grocery.findById(id).populate('user', 'displayName').exec(function(err, grocery) {
		if (err) return next(err);
		if (! grocery) return next(new Error('Failed to load Grocery ' + id));
		req.grocery = grocery ;
		next();
	});
};

/**
 * Grocery authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (req.grocery.user.id !== req.user.id) {
		return res.status(403).send('User is not authorized');
	}
	next();
};
<commit_msg>data added<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Grocery = mongoose.model('Grocery'),
	_ = require('lodash');

/**
 * Create a Grocery
 */
exports.create = function(req, res) {
	var grocery = new Grocery(req.body);
	grocery.user = req.user;

	grocery.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(grocery);
		}
	});
};

exports.createAll = function(req, res, next) {
  var importShops = req.body;
  var bulk = Grocery.collection.initializeUnorderedBulkOp();
  importShops.forEach(function(product) {
    if (product)
      bulk.insert(product);
  })
  bulk.execute(function (err,result) {
    if (err) {
      return res.status(400).send({
        message: errorHandler.getErrorMessage(err)
      });
    } else {
      res.jsonp(result);
    }
  });
};

/**
 * Show the current Grocery
 */
exports.read = function(req, res) {
	res.jsonp(req.grocery);
};

/**
 * Update a Grocery
 */
exports.update = function(req, res) {
	var grocery = req.grocery ;

	grocery = _.extend(grocery , req.body);

	grocery.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(grocery);
		}
	});
};

/**
 * Delete an Grocery
 */
exports.delete = function(req, res) {
	var grocery = req.grocery ;

	grocery.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(grocery);
		}
	});
};

/**
 * List of Groceries
 */
exports.list = function(req, res) {
	Grocery.find().sort('-created').populate('user', 'displayName').exec(function(err, groceries) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(groceries);
		}
	});
};

exports.Search = function(req, res) {
  console.log("****")
  console.log(req.params.searchText)
  Grocery.find({'name':new RegExp(req.params.searchText,"i")}).exec(function(err, groceries) {
    if (err) {
      return res.status(400).send({
        message: errorHandler.getErrorMessage(err)
      });
    } else {
      res.jsonp(groceries);
    }
  });
};

/**
 * List of pagination
 */
exports.listPage = function(req, res) {
  console.log("yesy here")
  console.log(req.params.category)
  if(!req.params.page)
  {
    var page = 1;
  }else{
    var page = req.params.page;
  }
  var per_page = 12;
  Grocery.find({'category':req.params.category}).sort( { name: 1 } ).skip((page-1)*per_page).limit(per_page).exec(function(err, fruits) {
    if (err) {
      return res.status(400).send({
        message: errorHandler.getErrorMessage(err)
      });
    } else {
      res.jsonp(fruits);
    }
  });
};

/**
 * Grocery middleware
 */
exports.groceryByID = function(req, res, next, id) {
	Grocery.findById(id).populate('user', 'displayName').exec(function(err, grocery) {
		if (err) return next(err);
		if (! grocery) return next(new Error('Failed to load Grocery ' + id));
		req.grocery = grocery ;
		next();
	});
};

/**
 * Grocery authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (req.grocery.user.id !== req.user.id) {
		return res.status(403).send('User is not authorized');
	}
	next();
};
<|endoftext|>


<commit_before>export default function () {
  console.log('This is main for training JavaScript')
  otherFunc()
  callSubjects()
}

const callSubjects = function () {
  console.log('----- begin subjects -----')
  types()
  operators()
  controlStructure()
  console.log('----- end subjects -----')
}

const otherFunc = function () {
  console.log('other Func!!')
}

export const secondFunction = function () {
  console.log('Second function!!')
}

const types = function () {
  console.log('------ begin type ------')
  // Number
  console.log(0.1 + 0.2)
  const stringNumber = '012'
  console.log(parseInt(stringNumber, 10))
  let count = 2
  console.log(count + '010') // cannot add but can concat.
  // String
  const word = 'hello'
  console.log(word.length)
  console.log(word.charAt(2))
  console.log(word.replace('ll', 'rr'))
  console.log(word.toUpperCase())
  // Boolean
  const falseFromfalse = Boolean(false)
  // const falseFromString = Boolean('false') // This is true
  const falseFrom0 = Boolean(0)
  const falseFromEmpty = Boolean('')
  const falseFromNaN = Boolean(NaN)
  const falseFromNull = Boolean(null)
  const falseFromUndef = Boolean(undefined)
  console.log(falseFromfalse)
  // console.log(falseFromString)
  console.log(falseFrom0)
  console.log(falseFromEmpty)
  console.log(falseFromNaN)
  console.log(falseFromNull)
  console.log(falseFromUndef)
}

const operators = function () {
  console.log('------ begin operators ------')

  let x = 5
  console.log(x)
  x += 5
  console.log(x)
  console.log('hello' + ' world') // hello world
  console.log(3 + 4 + 5)
  console.log('3' + 4 + 5)
  console.log(3 + 4 + '5')
  console.log(123 === '123') // false
  console.log(1 === true) // false
}

const controlStructure = function () {
  console.log('------ controlStructure ------')
}
<commit_msg>Add controll structure.<commit_after>export default function () {
  console.log('This is main for training JavaScript')
  otherFunc()
  callSubjects()
}

const callSubjects = function () {
  console.log('----- begin subjects -----')
  types()
  operators()
  controlStructure()
  objects()
  console.log('----- end subjects -----')
}

const otherFunc = function () {
  console.log('other Func!!')
}

export const secondFunction = function () {
  console.log('Second function!!')
}

const types = function () {
  console.log('------ begin type ------')
  // Number
  console.log(0.1 + 0.2)
  const stringNumber = '012'
  console.log(parseInt(stringNumber, 10))
  let count = 2
  console.log(count + '010') // cannot add but can concat.
  // String
  const word = 'hello'
  console.log(word.length)
  console.log(word.charAt(2))
  console.log(word.replace('ll', 'rr'))
  console.log(word.toUpperCase())
  // Boolean
  const falseFromfalse = Boolean(false)
  // const falseFromString = Boolean('false') // This is true
  const falseFrom0 = Boolean(0)
  const falseFromEmpty = Boolean('')
  const falseFromNaN = Boolean(NaN)
  const falseFromNull = Boolean(null)
  const falseFromUndef = Boolean(undefined)
  console.log(falseFromfalse)
  // console.log(falseFromString)
  console.log(falseFrom0)
  console.log(falseFromEmpty)
  console.log(falseFromNaN)
  console.log(falseFromNull)
  console.log(falseFromUndef)
}

const operators = function () {
  console.log('------ begin operators ------')

  let x = 5
  console.log(x)
  x += 5
  console.log(x)
  console.log('hello' + ' world') // hello world
  console.log(3 + 4 + 5)
  console.log('3' + 4 + 5)
  console.log(3 + 4 + '5')
  console.log(123 === '123') // false
  console.log(1 === true) // false
}

const controlStructure = function () {
  console.log('------ controlStructure ------')

  let name = 'kittens'
  if (name === 'puppies') {
    name += ' woof'
  } else if (name === 'kittens') {
    name += ' meow'
  } else {
    name += '!'
  }
  console.log(name)
  // loop
  const array = ['a', 'b', 'c']
  for (let value of array) {
    console.log(value)
  }
  let animal = {
    hand: 2,
    foot: 2,
    heart: 1
  }
  for (let property in animal) {
    console.log(property)
    console.log(animal[property]) // I can access value of property.
  }
  // short-circuit logic
  let o = {
    name: 'test o'
  }
  let u
  let n = o && o.name
  let n2 = u && u.name
  // let n3 = u.name // This will be TypeError, short-circuit can avoid this error.
  console.log('n is ' + n)
  console.log('n2 is ' + n2)
  // console.log(n3)
  let alreadyCached = 'Cached!'
  let notCached
  console.log(alreadyCached || (alreadyCached = 'Second cache'))
  console.log(notCached || (notCached = 'Second cache'))
  // Ternary operator (三項演算子)
  const age = 18
  console.log((age > 18) ? 'yes' : 'no')
  // Switch
  let action = 'eat'

  switch (action) {
    case 'draw':
      console.log('draw now!')
      break // In ESLint, Required break.
    case 'eat':
      console.log('eat now!')
      break
    default:
      console.log('nothing!')
  }
  switch (action) {
    case 'eat':
    case 'draw': // But, this is ok.
      console.log('eat and draw')
      break
    default:
      console.log('nothing')
  }
  let stringCondition = '4'
  switch (stringCondition) {
    case 4:
      console.log('match!')
      break
    default: // switch use '===' operator.
      console.log('not match!')
  }
}

const objects = function () {
  console.log('------ objects ------')
}
<|endoftext|>


<commit_before>if (process.argv.length < 3) {
    console.log('missing argument.');
    return;
}

var bf = {
    code: {
        // set up an array of values from 8 to 128, by 8s
        base: '++++++++[>+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++>++++++++++++>+++++++++++++>++++++++++++++>+++++++++++++++>++++++++++++++++<<<<<<<<<<<<<<<<-]',
        slot: {
            increment: '>',
            decrement: '<'
        },
        pointer: {
            increment: '+',
            decrement: '-'
        },
        output: '.'
    },
    fuckit: function (text) {
        make_code = function (c) {
            // make the bf code for this character
            var char_code = '';
            // set the slot
            var slot = Math.round(c / 8);
            for (var i = 0; i < slot; i++) {
                char_code += bf.code.slot.increment;
            }
            // set the pointer
            if (c > (slot * 8)) {
                var pointer_diff = c - (slot * 8);
                var pointer = bf.code.pointer.increment;
                var pointer_reversed = bf.code.pointer.decrement;
            } else if (c < (slot * 8)) {
                var pointer_diff = (slot * 8) - c;
                var pointer = bf.code.pointer.decrement;
                var pointer_reversed = bf.code.pointer.increment;
            } else {
                var pointer_diff = 0;
            }
            for (var i = 0; i < pointer_diff; i++) {
                char_code += pointer;
            }
            // output the character
            char_code += bf.code.output;
            // reset the pointer
            for (var i = 0; i < pointer_diff; i++) {
                char_code += pointer_reversed;
            }
            // reset the slot
            for (var i = 0; i < slot; i++) {
                char_code += bf.code.slot.decrement;
            }
            // return our bf code
            return char_code;
        };
        var code = bf.code.base;
        var txt2fck = text; // d.getElementById('txt2fck').value;
        for (var i = 0; i < txt2fck.length; i++) {
            code += make_code(txt2fck.charCodeAt(i));
        }
        code += bf.code.output;
        // d.getElementById('brainf_ckcode').value = code;
        return code;
    }
};

var fs = require('fs');
fs.readFile(process.argv[2], 'utf8', function (err, text) {
    var data = bf.fuckit(text);
fs.writeFile(process.argv[3], data , function (err) {
    console.log(data);
    console.log(err);
});
});
<commit_msg>Fix indent<commit_after>if (process.argv.length < 3) {
    console.log('missing argument.');
    return;
}

var bf = {
    code: {
        // set up an array of values from 8 to 128, by 8s
        base: '++++++++[>+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++>++++++++++++>+++++++++++++>++++++++++++++>+++++++++++++++>++++++++++++++++<<<<<<<<<<<<<<<<-]',
        slot: {
            increment: '>',
            decrement: '<'
        },
        pointer: {
            increment: '+',
            decrement: '-'
        },
        output: '.'
    },
    fuckit: function (text) {
        make_code = function (c) {
            // make the bf code for this character
            var char_code = '';
            // set the slot
            var slot = Math.round(c / 8);
            for (var i = 0; i < slot; i++) {
                char_code += bf.code.slot.increment;
            }
            // set the pointer
            if (c > (slot * 8)) {
                var pointer_diff = c - (slot * 8);
                var pointer = bf.code.pointer.increment;
                var pointer_reversed = bf.code.pointer.decrement;
            } else if (c < (slot * 8)) {
                var pointer_diff = (slot * 8) - c;
                var pointer = bf.code.pointer.decrement;
                var pointer_reversed = bf.code.pointer.increment;
            } else {
                var pointer_diff = 0;
            }
            for (var i = 0; i < pointer_diff; i++) {
                char_code += pointer;
            }
            // output the character
            char_code += bf.code.output;
            // reset the pointer
            for (var i = 0; i < pointer_diff; i++) {
                char_code += pointer_reversed;
            }
            // reset the slot
            for (var i = 0; i < slot; i++) {
                char_code += bf.code.slot.decrement;
            }
            // return our bf code
            return char_code;
        };
        var code = bf.code.base;
        var txt2fck = text; // d.getElementById('txt2fck').value;
        for (var i = 0; i < txt2fck.length; i++) {
            code += make_code(txt2fck.charCodeAt(i));
        }
        code += bf.code.output;
        // d.getElementById('brainf_ckcode').value = code;
        return code;
    }
};

var fs = require('fs');
fs.readFile(process.argv[2], 'utf8', function (err, text) {
  var data = bf.fuckit(text);
  fs.writeFile(process.argv[3], data , function (err) {
    console.log(data);
    console.log(err);
  });
});
<|endoftext|>


<commit_before>/**
 * @fileoverview Build file. Based in large part on functionality found in
 *               eslint's own makefile, with necessary tweaks for building an
 *               eslint plugin instead.
 *
 * @author Casey Visco
 */

"use strict";

/* global cat */
/* global echo */
/* global exec */
/* global exit */
/* global find */
/* global mv */
/* global rm */
/* global target */
/* global test */

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

require("shelljs/make");

var path = require("path");
var semver = require("semver");
var dateformat = require("dateformat");
var nodeCLI = require("shelljs-nodecli");

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

// Tools
var ESLINT = "./node_modules/.bin/eslint ";
var MOCHA = "./node_modules/mocha/bin/_mocha ";

// Files
var MAKEFILE = "./makefile.js";
var JS_FILES = find("lib/").filter(fileType("js")).join(" ");
var TEST_FILES = find("tests/lib/").filter(fileType("js")).join(" ");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Generate a function that matches files with a particular extension.
 *
 * @private
 * @param  {String}   extension - the file extension (i.e. "js")
 * @return {Function} function to pass into a filter method
 */
function fileType(extension) {
    return function (filename) {
        return filename.substring(filename.lastIndexOf(".") + 1) === extension;
    };
}

/**
 * Execute a command and return the output instead of printing it to stdout.
 *
 * @private
 * @param  {String} cmd - command to execute
 * @return {String} result of executed command
 */
function execSilent(cmd) {
    return exec(cmd, { silent: true }).output;
}

/**
 * Push supplied `tag` to supplied `list` only if it is a valid semver. This is
 * a reducer function.
 *
 * @private
 * @param  {Array}  list - array of valid semver tags
 * @param  {String} tag  - tag to push if valid
 * @return {Array}  modified `list`
 */
function validSemverTag(list, tag) {
    if (semver.valid(tag)) {
        list.push(tag);
    }

    return list;
}

function getVersionTags() {
    return execSilent("git tag")
        .trim()
        .split("\n")
        .reduce(validSemverTag, [])
        .sort(semver.compare);
}

//------------------------------------------------------------------------------
// Tasks
//------------------------------------------------------------------------------

target.lint = function () {
    var errors = 0;

    echo("Linting makefile");
    errors += exec(ESLINT + MAKEFILE).code;

    echo("Linting JavaScript files");
    errors += exec(ESLINT + JS_FILES).code;

    echo("Linting JavaScript test files");
    errors += exec(ESLINT + TEST_FILES).code;

    if (errors) {
        exit(1);
    }
};

/**
 * Verify that each rule is defined in index.js with the correct default
 * setting, has documentation and has tests.
 */
target.checkRules = function () {
    echo("Verifying rules");

    var errors = 0;

    var ruleFiles = find("lib/rules/").filter(fileType("js"));
    var configFile = require("./index");
    var readmeText = cat("./README.md");

    function isDefinedInConfig(rule) {
        return configFile.rules.hasOwnProperty(rule);
    }

    function hasDefaultSettingInConfig(rule) {
        var setting = configFile.rulesConfig[rule];

        return typeof setting === "number" ||
               (setting && typeof setting[0] === "number");
    }

    function isOffInConfig(rule) {
        var setting = configFile.rulesConfig[rule];

        return setting === 0 || (setting && setting[0] === 0);
    }

    function isOnInConfig(rule) {
        return !isOffInConfig(rule);
    }

    function isOffInReadme(rule) {
        var line = new RegExp("\\* \\[" + rule + "\\].*").exec(readmeText);

        return (line ? line[0] : "").indexOf("(off by default)") !== -1;
    }

    function isOnInReadme(rule) {
        return !isOffInReadme(rule);
    }

    function hasIdInTitle(docFile, id) {
        var docText = cat(docFile);
        var idInTitleRegExp = new RegExp("^# (.*?) \\(" + id + "\\)");

        return idInTitleRegExp.test(docText);
    }

    ruleFiles.forEach(function (filename) {
        var basename = path.basename(filename, ".js");
        var docFilename = "docs/rules/" + basename + ".md";
        var testFilename = "tests/lib/rules/" + basename + ".js";

        // Verify rule has documentation

        if (!test("-f", docFilename)) {
            console.error("Missing documentation for rule %s", basename);
            errors++;
        } else {
            if (readmeText.indexOf("(" + docFilename + ")") === -1) {
                console.error("Missing link to documentation for rule %s in index", basename);
                errors++;
            }

            // check for proper doc format
            if (!hasIdInTitle(docFilename, basename)) {
                console.error("Missing id in the doc page's title of rule %s", basename);
                errors++;
            }
        }

        // Verify rule has tests

        if (!test("-f", testFilename)) {
            console.error("Missing tests for rule %s", basename);
            errors++;
        }

        // Verify config

        if (!isDefinedInConfig(basename)) {
            console.error("Missing rule entry for %s in index.js", basename);
            errors++;
        }

        if (!hasDefaultSettingInConfig(basename)) {
            console.error("Missing default setting for %s in index.js", basename);
            errors++;
        }

        if (isOffInReadme(basename) && isOnInConfig(basename)) {
            console.error("README states that %s is off by default, but it is enabled in index.js.", basename);
            errors++;
        }

        if (isOffInConfig(basename) && isOnInReadme(basename)) {
            console.error("Missing '(off by default)' for rule %s in README", basename);
            errors++;
        }
    });

    if (errors) {
        exit(1);
    }
};

target.test = function () {
    var errors = 0;

    target.lint();
    target.checkRules();

    echo("Running test suite");

    errors += nodeCLI.exec("istanbul", "cover", MOCHA, "-- -R progress -t 3000 -c", TEST_FILES).code;
    errors += nodeCLI.exec("istanbul", "check-coverage", "--statement 100 --branch 95 --function 100 --lines 100").code;

    if (errors) {
        exit(1);
    }
};

target.changelog = function () {

    // get most recent two tags
    var tags = getVersionTags(),
        rangeTags = tags.slice(tags.length - 2),
        timestamp = dateformat(new Date(), "mmmm d, yyyy"),
        semverRe = /^\* \d\.\d.\d/;

    // output header
    ("### " + rangeTags[1] + " - " + timestamp + "\n").to("CHANGELOG.tmp");

    // get log statements
    var logs = execSilent("git log --pretty=format:\"* %s (%an)\" " + rangeTags.join("..")).split(/\n/g);

    logs = logs.filter(function (line) {
        return line.indexOf("Merge pull request") === -1 &&
               line.indexOf("Merge branch") === -1 &&
               !semverRe.test(line);
    });

    logs.push(""); // to create empty lines
    logs.unshift("");

    // output log statements
    logs.join("\n").toEnd("CHANGELOG.tmp");

    cat("CHANGELOG.tmp", "CHANGELOG.md").to("CHANGELOG.md.tmp");
    rm("CHANGELOG.tmp");
    rm("CHANGELOG.md");
    mv("CHANGELOG.md.tmp", "CHANGELOG.md");
};
<commit_msg>Build: Add release tasks to makefile<commit_after>/**
 * @fileoverview Build file. Based in large part on functionality found in
 *               eslint's own makefile, with necessary tweaks for building an
 *               eslint plugin instead.
 *
 * @author Casey Visco
 */

"use strict";

/* global cat */
/* global echo */
/* global exec */
/* global exit */
/* global find */
/* global mv */
/* global rm */
/* global target */
/* global test */

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

require("shelljs/make");

var path = require("path");
var semver = require("semver");
var dateformat = require("dateformat");
var nodeCLI = require("shelljs-nodecli");

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

// Tools
var ESLINT = "./node_modules/.bin/eslint ";
var MOCHA = "./node_modules/mocha/bin/_mocha ";

// Files
var MAKEFILE = "./makefile.js";
var JS_FILES = find("lib/").filter(fileType("js")).join(" ");
var TEST_FILES = find("tests/lib/").filter(fileType("js")).join(" ");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Generate a function that matches files with a particular extension.
 *
 * @private
 * @param  {String}   extension - the file extension (i.e. "js")
 * @return {Function} function to pass into a filter method
 */
function fileType(extension) {
    return function (filename) {
        return filename.substring(filename.lastIndexOf(".") + 1) === extension;
    };
}

/**
 * Execute a command and return the output instead of printing it to stdout.
 *
 * @private
 * @param  {String} cmd - command to execute
 * @return {String} result of executed command
 */
function execSilent(cmd) {
    return exec(cmd, { silent: true }).output;
}

/**
 * Push supplied `tag` to supplied `list` only if it is a valid semver. This is
 * a reducer function.
 *
 * @private
 * @param  {Array}  list - array of valid semver tags
 * @param  {String} tag  - tag to push if valid
 * @return {Array}  modified `list`
 */
function validSemverTag(list, tag) {
    if (semver.valid(tag)) {
        list.push(tag);
    }

    return list;
}

/**
 * Retrieve a list of semver tags in descending order.
 *
 * @private
 * @return {Array} list of version tags
 */
function getVersionTags() {
    return execSilent("git tag")
        .trim()
        .split("\n")
        .reduce(validSemverTag, [])
        .sort(semver.compare);
}

/**
 * Create a release version, push tags and publish.
 *
 * @private
 * @param  {String} type - type of release to do (patch, minor, major)
 */
function release(type) {
    var newVersion;

    target.test();

    echo("Generating new version");
    newVersion = execSilent("npm version " + type).trim();

    target.changelog();

    // add changelog to commit
    exec("git add CHANGELOG.md");
    exec("git commit --amend --no-edit");

    // replace existing tag
    exec("git tag -f " + newVersion);

    // push all the things
    echo("Publishing to git");
    exec("git push origin master --tags");

    echo("Publishing to npm");
    exec("npm publish");
}

//------------------------------------------------------------------------------
// Tasks
//------------------------------------------------------------------------------

target.lint = function () {
    var errors = 0;

    echo("Linting makefile");
    errors += exec(ESLINT + MAKEFILE).code;

    echo("Linting JavaScript files");
    errors += exec(ESLINT + JS_FILES).code;

    echo("Linting JavaScript test files");
    errors += exec(ESLINT + TEST_FILES).code;

    if (errors) {
        exit(1);
    }
};

/**
 * Verify that each rule is defined in index.js with the correct default
 * setting, has documentation and has tests.
 */
target.checkRules = function () {
    echo("Verifying rules");

    var errors = 0;

    var ruleFiles = find("lib/rules/").filter(fileType("js"));
    var configFile = require("./index");
    var readmeText = cat("./README.md");

    function isDefinedInConfig(rule) {
        return configFile.rules.hasOwnProperty(rule);
    }

    function hasDefaultSettingInConfig(rule) {
        var setting = configFile.rulesConfig[rule];

        return typeof setting === "number" ||
               (setting && typeof setting[0] === "number");
    }

    function isOffInConfig(rule) {
        var setting = configFile.rulesConfig[rule];

        return setting === 0 || (setting && setting[0] === 0);
    }

    function isOnInConfig(rule) {
        return !isOffInConfig(rule);
    }

    function isOffInReadme(rule) {
        var line = new RegExp("\\* \\[" + rule + "\\].*").exec(readmeText);

        return (line ? line[0] : "").indexOf("(off by default)") !== -1;
    }

    function isOnInReadme(rule) {
        return !isOffInReadme(rule);
    }

    function hasIdInTitle(docFile, id) {
        var docText = cat(docFile);
        var idInTitleRegExp = new RegExp("^# (.*?) \\(" + id + "\\)");

        return idInTitleRegExp.test(docText);
    }

    ruleFiles.forEach(function (filename) {
        var basename = path.basename(filename, ".js");
        var docFilename = "docs/rules/" + basename + ".md";
        var testFilename = "tests/lib/rules/" + basename + ".js";

        // Verify rule has documentation

        if (!test("-f", docFilename)) {
            console.error("Missing documentation for rule %s", basename);
            errors++;
        } else {
            if (readmeText.indexOf("(" + docFilename + ")") === -1) {
                console.error("Missing link to documentation for rule %s in index", basename);
                errors++;
            }

            // check for proper doc format
            if (!hasIdInTitle(docFilename, basename)) {
                console.error("Missing id in the doc page's title of rule %s", basename);
                errors++;
            }
        }

        // Verify rule has tests

        if (!test("-f", testFilename)) {
            console.error("Missing tests for rule %s", basename);
            errors++;
        }

        // Verify config

        if (!isDefinedInConfig(basename)) {
            console.error("Missing rule entry for %s in index.js", basename);
            errors++;
        }

        if (!hasDefaultSettingInConfig(basename)) {
            console.error("Missing default setting for %s in index.js", basename);
            errors++;
        }

        if (isOffInReadme(basename) && isOnInConfig(basename)) {
            console.error("README states that %s is off by default, but it is enabled in index.js.", basename);
            errors++;
        }

        if (isOffInConfig(basename) && isOnInReadme(basename)) {
            console.error("Missing '(off by default)' for rule %s in README", basename);
            errors++;
        }
    });

    if (errors) {
        exit(1);
    }
};

target.test = function () {
    var errors = 0;

    target.lint();
    target.checkRules();

    echo("Running test suite");

    errors += nodeCLI.exec("istanbul", "cover", MOCHA, "-- -R progress -t 3000 -c", TEST_FILES).code;
    errors += nodeCLI.exec("istanbul", "check-coverage", "--statement 100 --branch 95 --function 100 --lines 100").code;

    if (errors) {
        exit(1);
    }
};

target.changelog = function () {
    echo("Generating changelog");

    // get most recent two tags
    var tags = getVersionTags(),
        rangeTags = tags.slice(tags.length - 2),
        timestamp = dateformat(new Date(), "mmmm d, yyyy"),
        semverRe = /^\* \d\.\d.\d/;

    // output header
    ("### " + rangeTags[1] + " - " + timestamp + "\n").to("CHANGELOG.tmp");

    // get log statements
    var logs = execSilent("git log --pretty=format:\"* %s (%an)\" " + rangeTags.join("..")).split(/\n/g);

    logs = logs.filter(function (line) {
        return line.indexOf("Merge pull request") === -1 &&
               line.indexOf("Merge branch") === -1 &&
               !semverRe.test(line);
    });

    logs.push(""); // to create empty lines
    logs.unshift("");

    // output log statements
    logs.join("\n").toEnd("CHANGELOG.tmp");

    cat("CHANGELOG.tmp", "CHANGELOG.md").to("CHANGELOG.md.tmp");
    rm("CHANGELOG.tmp");
    rm("CHANGELOG.md");
    mv("CHANGELOG.md.tmp", "CHANGELOG.md");
};

target.patch = function () {
    release("patch");
};

target.minor = function () {
    release("minor");
};

target.major = function () {
    release("major");
};
<|endoftext|>