<commit_before>Utils = require "../Utils"

{Layer} = require "../Layer"
{Events} = require "../Events"
{LayerStates} = require "../LayerStates"
# Transitions = require "./NavComponentTransitions"

NavComponentLayerScrollKey = "_navComponentWrapped"

Events.TransitionStart = "transitionstart"
Events.TransitionEnd = "transitionend"
Events.Forward = "forward"
Events.Back = "back"

class exports.NavComponent extends Layer

	constructor: (options={}) ->

		options = _.defaults options,
			width: Screen.width
			height: Screen.height
			backgroundColor: "black"

		super options

		@_stack = []
		@_seen = []
		@_current = null

		@background = new Layer
			name: "background"
			parent: @
			size: @size
			backgroundColor: "black"
			visible: false

		@background.onTap => @back()

	@define "isTransitioning",
		get: -> @_isTransitioning

	@define "stack",
		get: -> _.clone(@_stack)

	@define "current",
		get: -> return @_stack[@_stack.length - 1]?.layer

	@define "previous",
		get: -> return @_stack[@_stack.length - 2]?.layer


	##############################################################
	# Transitions

	push: (layer, animate, wrap, transitionFunction) ->

		throw new Error "NavComponent.push expects a layer" unless layer
		return if layer is @current

		# Set the default values

		# If this is the first layer, we skip the animation
		animate ?= if @_stack.length then true else false
		wrap ?= true

		# Make sure the layer is visible
		layer.visible = true
		layer.opacity = 1

		# We want the layer to block events so you can't click on layers
		# that end up behind it without knowing it.
		layer.ignoreEvents = false

		# Wrap the layer and set this as the parent
		wrappedLayer = layer
		wrappedLayer = @_wrapLayer(layer) if wrap
		wrappedLayer.parent = @

		transitionFunction ?= Transitions.push
		transition = @_buildTransition(transitionFunction,
			@_wrappedLayer(@current), wrappedLayer, @background)

		@_runTransition(transition, "forward", animate, @current, layer)
		@_stack.push({layer:layer, transition:transition})

	dialog: (layer, animate) ->
		@push(layer, Transitions.dialog, animate, false)

	modal: (layer, animate) ->
		@modalRight(layer, animate)

	# Modal transitions never get wrapped. If you'd like them to be scrollable
	# you can put them into a ScrollComponent yourself and then insert them.

	modalLeft: (layer, animate) ->
		@push(layer, animate, false, Transitions.modalLeft)

	modalRight: (layer, animate) ->
		@push(layer, animate, false, Transitions.modalRight)
	
	modalTop: (layer, animate) ->
		@push(layer, animate, false, Transitions.modalTop)

	modalBottom: (layer, animate) ->
		@push(layer, animate, false, Transitions.modalBottom)

	back: (animate=true) =>
		return unless @previous
		previous = @_stack.pop()
		@_runTransition(previous?.transition, "back", animate, @current, previous.layer)


	##############################################################
	# Internal methods

	_wrapLayer: (layer) ->

		# Wrap the layer in a ScrollComponent if the size exceeds the size of
		# the NavComponent. Also set the horizontal/vertical scrollin if only
		# one of the sizes exceeds.

		# TODO: what about NavComponent changing size, do we need to account?

		# If we already created a scroll, we can use that one
		if layer[NavComponentLayerScrollKey]
			return layer[NavComponentLayerScrollKey]

		# If the layer size is exactly equal to the size of the NavComponent
		# we can just use it directly.
		if layer.width is @width and layer.height is @height
			return layer

		# If the layer size is smaller then the size of the NavComponent we
		# still need to add a backgound layer so it covers up the background.
		# TODO: Implement this
		if layer.width < @width and layer.height < @height
			return layer

		# If this layer is a ScrollComponent we do not have to add another one
		if layer instanceof ScrollComponent
			return layer

		layer.point = Utils.pointZero()

		scroll = new ScrollComponent
		scroll.name = "scroll"
		scroll.size = layer.size
		# scroll.width = Math.min(layer.width, @width)
		# scroll.height = Math.min(layer.height, @height)
		scroll.backgroundColor = @backgroundColor
		scroll.scrollHorizontal = layer.width > @width
		scroll.scrollVertical = layer.height > @height
		layer.parent = scroll.content

		layer[NavComponentLayerScrollKey] = scroll

		return scroll

	_wrappedLayer: (layer) ->
		# Get the ScrollComponent for a layer if it was wrapped,
		# or just the layer itself if it was not.
		return null unless layer
		return layer[NavComponentLayerScrollKey] or layer

	_runTransition: (transition, direction, animate, from, to) =>

		@_isTransitioning = true
		@emit(Events.TransitionStart, from, to, direction)
		@emit(direction, from, to)

		# Start the transition with a small delay added so it only runs after all
		# js has been processed. It's also important for hints, as they rely on 
		# ignoreEvents to be false at the moment of a click.
		Utils.delay 0, =>
			transition[direction] animate, =>
				@_isTransitioning = false
				@emit(Events.TransitionEnd, from, to, direction)
				@emit(direction, from, to)

	_buildTransition: (template, layerA, layerB, background) ->

		# Get the executed template data by passing in the layers for this transition
		template = template(@, layerA, layerB, background)

		# Buld a new transtition object with empty states
		transition = {}
		transition.states = {}

		seen = @_seen

		# For every layer that exists, add the states form the template
		if layerA and template.layerA
			transition.states.layerA = new LayerStates(layerA)
			transition.states.layerA.add(template.layerA)<commit_msg>Fix dialog, add transtition function<commit_after>Utils = require "../Utils"

{Layer} = require "../Layer"
{Events} = require "../Events"
{LayerStates} = require "../LayerStates"
# Transitions = require "./NavComponentTransitions"

NavComponentLayerScrollKey = "_navComponentWrapped"

Events.TransitionStart = "transitionstart"
Events.TransitionEnd = "transitionend"
Events.Forward = "forward"
Events.Back = "back"

class exports.NavComponent extends Layer

	constructor: (options={}) ->

		options = _.defaults options,
			width: Screen.width
			height: Screen.height
			backgroundColor: "black"

		super options

		@_stack = []
		@_seen = []
		@_current = null

		@background = new Layer
			name: "background"
			parent: @
			size: @size
			backgroundColor: "black"
			visible: false

		@background.onTap => @back()

	@define "isTransitioning",
		get: -> @_isTransitioning

	@define "stack",
		get: -> _.clone(@_stack)

	@define "current",
		get: -> return @_stack[@_stack.length - 1]?.layer

	@define "previous",
		get: -> return @_stack[@_stack.length - 2]?.layer


	##############################################################
	# Transitions

	_transition: (layer, animate, wrap, transitionFunction) ->

		throw new Error "NavComponent.push expects a layer" unless layer
		return if layer is @current

		# Set the default values

		# If this is the first layer, we skip the animation
		animate ?= if @_stack.length then true else false
		wrap ?= true

		# Make sure the layer is visible
		layer.visible = true
		layer.opacity = 1

		# We want the layer to block events so you can't click on layers
		# that end up behind it without knowing it.
		layer.ignoreEvents = false

		# Wrap the layer and set this as the parent
		wrappedLayer = layer
		wrappedLayer = @_wrapLayer(layer) if wrap
		wrappedLayer.parent = @

		transitionFunction ?= Transitions.push
		transition = @_buildTransition(transitionFunction,
			@_wrappedLayer(@current), wrappedLayer, @background)

		@_runTransition(transition, "forward", animate, @current, layer)
		@_stack.push({layer:layer, transition:transition})

	push: (layer, animate) ->
		@_transition(layer, animate, false, Transitions.push)

	dialog: (layer, animate) ->
		@_transition(layer, animate, false, Transitions.dialog)

	modal: (layer, animate) ->
		@modalRight(layer, animate)

	# Modal transitions never get wrapped. If you'd like them to be scrollable
	# you can put them into a ScrollComponent yourself and then insert them.

	modalLeft: (layer, animate) ->
		@_transition(layer, animate, false, Transitions.modalLeft)

	modalRight: (layer, animate) ->
		@_transition(layer, animate, false, Transitions.modalRight)
	
	modalTop: (layer, animate) ->
		@_transition(layer, animate, false, Transitions.modalTop)

	modalBottom: (layer, animate) ->
		@_transition(layer, animate, false, Transitions.modalBottom)

	back: (animate=true) =>
		return unless @previous
		previous = @_stack.pop()
		@_runTransition(previous?.transition, "back", animate, @current, previous.layer)


	##############################################################
	# Internal methods

	_wrapLayer: (layer) ->

		# Wrap the layer in a ScrollComponent if the size exceeds the size of
		# the NavComponent. Also set the horizontal/vertical scrollin if only
		# one of the sizes exceeds.

		# TODO: what about NavComponent changing size, do we need to account?

		# If we already created a scroll, we can use that one
		if layer[NavComponentLayerScrollKey]
			return layer[NavComponentLayerScrollKey]

		# If the layer size is exactly equal to the size of the NavComponent
		# we can just use it directly.
		if layer.width is @width and layer.height is @height
			return layer

		# If the layer size is smaller then the size of the NavComponent we
		# still need to add a backgound layer so it covers up the background.
		# TODO: Implement this
		if layer.width < @width and layer.height < @height
			return layer

		# If this layer is a ScrollComponent we do not have to add another one
		if layer instanceof ScrollComponent
			return layer

		layer.point = Utils.pointZero()

		scroll = new ScrollComponent
		scroll.name = "scroll"
		scroll.size = layer.size
		# scroll.width = Math.min(layer.width, @width)
		# scroll.height = Math.min(layer.height, @height)
		scroll.backgroundColor = @backgroundColor
		scroll.scrollHorizontal = layer.width > @width
		scroll.scrollVertical = layer.height > @height
		layer.parent = scroll.content

		layer[NavComponentLayerScrollKey] = scroll

		return scroll

	_wrappedLayer: (layer) ->
		# Get the ScrollComponent for a layer if it was wrapped,
		# or just the layer itself if it was not.
		return null unless layer
		return layer[NavComponentLayerScrollKey] or layer

	_runTransition: (transition, direction, animate, from, to) =>

		@_isTransitioning = true
		@emit(Events.TransitionStart, from, to, direction)
		@emit(direction, from, to)

		# Start the transition with a small delay added so it only runs after all
		# js has been processed. It's also important for hints, as they rely on 
		# ignoreEvents to be false at the moment of a click.
		Utils.delay 0, =>
			transition[direction] animate, =>
				@_isTransitioning = false
				@emit(Events.TransitionEnd, from, to, direction)
				@emit(direction, from, to)

	_buildTransition: (transitionFunction, layerA, layerB, background) ->

		# Get the executed template data by passing in the layers for this transition
		template = transitionFunction(@, layerA, layerB, background)

		# Buld a new transtition object with empty states
		transition = {}
		transition.states = {}

		seen = @_seen

		# For every layer that exists, add the states form the template
		if layerA and template.layerA
			transition.states.layerA = new LayerStates(layerA)
			transition.states.layerA.add(template.layerA)
{'fraction_non_alphanumeric': 0.11587870313728917, 'fraction_numerical': 0.0006991173643275365, 'mean_word_length': 4.120357941834452, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 48, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5612837', 'n_tokens_mistral': 3649, 'n_tokens_neox': 3201, 'n_words': 1174}