text:
#include "vixen.h"

namespace Vixen {

VX_IMPLEMENT_CLASSID(Scene, SharedObj, VX_Scene);

int						Scene::DeviceDepth = 32;
int						Scene::NumTexUnits = 1;
bool					Scene::SupportDDS = false;
const TCHAR*			Scene::RenderOptions = NULL;
Scene::TLS				Scene::t_State;

void Scene::InitThreadGlobals()
{
	TLS* g = GetTLS();
	g->ThreadType = -1;
}

static const TCHAR* opnames[] =
{	TEXT("UNUSED"), TEXT("SetModels"), TEXT("SetCamera"),
	TEXT("SetLight"), TEXT("SetEngines"), TEXT("SetBackColor"), TEXT("SetAmbient"),
	TEXT("ShowAll"), TEXT("SetAutoAdjust"), TEXT("SetOptions"),
	TEXT("SetViewport"), TEXT("SetPostProcess")
};

const TCHAR** Scene::DoNames = opnames;

void Scene::Shutdown()
{
#ifndef VX_NOTHREAD
	if (s_Threads)
	{
		VX_ASSERT(s_Threads->NumThreads == 0);
		delete s_Threads;
		s_Threads = NULL;
	}
#endif
	DataLayout::FreeLayouts();
}

bool Scene::InitRender(Vixen::Window window)
{
	Renderer* gs = m_Renderer;
	if (gs && gs->Init(this, window, RenderOptions))
	{
		SetViewport();
		return true;
	}
	return false;
}

/*!
 * @fn SceneStats* Scene::GetStats() const
 *
 * Returns the statistics gathered about the currently
 * displayed scene for the last frame in SceneStats
 * structure. This structure may be device or platform
 * dependent and varies across Vixen ports.
 *
 * @return scene statistics or NULL if none available
 *
 * @see StatusLog FrameCounter
 */
SceneStats* Scene::GetStats() const
{
	return (SceneStats*) &m_Stats;
}

/*!
 * @fn Scene::Scene(Renderer* render)
 * @param renderer	-> renderer used to display the scene
 *
 * Initialize scene with default camera and light.
 * The scene will have no scene graph or simulation tree
 * and will not be attached to the display.
 */
Scene::Scene(Renderer* renderer) : SharedObj()
{
	m_pDevInfo = MakeDevInfo();
	m_Time = 0.0f;
	m_TimeInc = 0.0f;
	m_Changed = -1;
	m_Camera = new Camera();
	SetBackColor(Col4(0.2f, 0.5f, 0.8f, 0.0f));
	m_AutoAdjust = true;
	m_IsMultiframe = false;
	m_Options = CLEARALL;
	m_Renderer = NULL;
	m_ChangePending = false;
	m_Changed = 0;
	m_Child = m_Parent = NULL;
	m_Renderer = renderer;
#ifdef VX_NOTHREAD
	DoExit = false;
#endif
};

Scene::Scene(Scene& src) : SharedObj(src)
{
	m_pDevInfo = MakeDevInfo();
	m_Renderer = NULL;
	m_IsMultiframe = src.m_IsMultiframe;
	m_Changed = 0;
	m_Child = m_Parent = NULL;
	Copy(&src);
}

void Scene::Exit()
{
	Renderer*	gs = GetRenderer();

	Empty();
	if (m_Prop)
	{
		m_Prop->Empty();
		m_Prop = NULL;
	}
	m_Camera = (Camera*) NULL;
	if (gs)
		gs->Exit();
	m_Renderer = NULL;
}

/****
 *
 * class Scene override for SharedObj::Copy
 * Initialize scene with default values for parameters.
 *
 ****/
bool Scene::Copy(const SharedObj* src_obj)
{
	const Scene* src = (const Scene*) src_obj;
	float eyesep = m_Camera->GetEyeSep();

	VX_ASSERT(src->IsClass(VX_Scene));
	Suspend();
	Empty();
	Notify(SCENE_CameraChanged | SCENE_WindowChanged | SCENE_RootChanged | SCENE_EnvChanged);
	m_Camera = src->m_Camera;
	if (eyesep > 0)
	{
		m_Camera->SetEyeSep(eyesep);
		m_Camera->SetChanged(true);
	}
	Core::InterlockSet(&m_Options, src->m_Options);
	SetModels(src->m_Models);
	SetEngines(src->m_Engines);
	SetBackColor(src->m_BackColor);
	if (!src-> m_PostProcess.IsNull())
		SetPostProcess(src->m_PostProcess);
	SetAmbient(src->m_Ambient);
	SetTimeInc(src->m_TimeInc);
	if (src->m_Viewport.Width() && src->m_Viewport.Height())
		SetViewport(src->m_Viewport);
	m_AutoAdjust = src->m_AutoAdjust;
	Resume();
	return true;
}

/****
 *
 * class Scene override for SharedObj::Print
 *
 ****/
DebugOut& Scene::Print(DebugOut& dbg, int opts) const
{
	if ((opts & PRINT_Attributes) == 0)
		return SharedObj::Print(dbg, opts);
	SharedObj::Print(dbg, opts & ~PRINT_Trailer);
	endl(dbg << "<attr name='Camera'>");
	m_Camera->Print(dbg, opts | PRINT_Trailer);
	endl(dbg << "</attr>");
	if (!m_Models.IsNull())
	{
		endl(dbg << "<attr name='Models'>");
		m_Models->Print(dbg, opts | PRINT_Trailer);
		endl(dbg << "</attr>");
	}
	if (!m_Engines.IsNull())
	{
		endl(dbg << "<attr name='Engines'>");
		m_Engines->Print(dbg, opts | PRINT_Trailer);
		endl(dbg << "</attr>");
	}
	if (!m_PostProcess.IsNull())
	{
		endl(dbg << "<attr name='PostProcess'>");
		m_PostProcess->Print(dbg, opts | PRINT_Trailer);
		endl(dbg << "</attr>");
	}

	endl(dbg << "\t<attr name='AutoAdjust'>" << m_AutoAdjust << "</AutoAdjust>");
	endl(dbg << "\t<attr name='Ambient'>" << ((SharedObj*) m_Ambient) << "</Ambient>");
	endl(dbg << "\t<attr name='BackColor'>" << m_BackColor << "</BackColor>");
	return SharedObj::Print(dbg, opts & PRINT_Trailer);
}

void Scene::Empty()
{
	Scene* child = GetChild();
	Renderer*	gs = GetRenderer();

	if (child)
	{
		child->m_Parent = NULL;
		child->Exit();
		m_Child = (Scene*) NULL;
	}
	if (gs)
		gs->Empty();
	m_Models = (const Model*) NULL;
	m_Engines = (const Engine*) NULL;
}

/*!
 * @fn void Scene::ShowAll()
 *
 * Orients the camera and moves it along the Z axis so that all of the
 * objects in the scene are visible. Camera movement is based on the
 * scene's bounding volume.
 *
 * @see Model::GetBound Camera Camera::SetViewVol
 */
void Scene::ShowAll()
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_ShowAll) << this;
	VX_STREAM_END( )

	Box3			bound;
	const Model*	group = m_Models;

	if (group)
	{
		group->GetBound(&bound, Model::WORLD);
		ZoomToBounds(bound);
	}
}

/*!
 * @fn const Shader* Scene::InstallShader(const Shader*)
 *
 * Installs the given shader in a global dictionary so a single
 * copy of the named shader is shared. The shader returned
 * is the global copy, which may not be the same copy as the
 * input shader. The name of the shader, as set by SharedObj::SetName,
 * is the global unique key. Shaders with the same name will be mapped
 * to the same global copy.
 *
 * @returns -> copy to global shader
 *
 * @see Renderer::InstallShader Appearance::SetPixelShader Appearance::SetVertexShader
 */
const Shader* Scene::InstallShader(const Shader* shader)
{
	Renderer* render  = GetRenderer();

	if (render)
		return render->InstallShader(shader);
	return NULL;
}

void Scene::ZoomToModel(Model *pMod)
{
	Box3	bound;

	pMod->GetBound(&bound, Model::WORLD);
	ZoomToBounds(bound);
}

void Scene::ZoomToBounds(Box3& bound)
{
	Vec3 ctr, loc;
	float fFOV = m_Camera->GetFOV() / 2.0f;
	float fWidth = bound.Width();
	float fHeight = bound.Height();
	float fDepth = bound.Depth();
	float dist = (fWidth > fHeight) ? fWidth : fHeight;

	ctr = bound.Center();					/* center of bounding volume */
	m_Camera->Reset();
	if (fWidth == 0.0f)
	{
		// Probably a light or empty group, not a shape
		loc.Set(ctr.x, ctr.y, ctr.z);	/* position of camera */
		m_Camera->Move(loc);
		return;
	}
	if (m_Camera->GetType() == Camera::ORTHOGRAPHIC)
		dist += fDepth / 2.0f;
	else
	{
		// find distance of camera from object
		dist *= tanf(fFOV) / 2.0f;
		dist = fDepth / 2.0f + dist;
	}
	if (dist < fDepth)
		dist = fDepth;
	loc.Set(ctr.x, ctr.y, ctr.z + dist);	// position of camera
	m_Camera->Move(loc);

	// make sure that the scene fits within the hither and yon planes
	if (m_Camera->GetHither() > dist)
		m_Camera->SetHither(dist);
	if (m_Camera->GetYon() < fDepth + dist)
		m_Camera->SetYon(fDepth + dist);
}

bool Scene::SetDepthBuffer(Bitmap* bmap)
{
	m_DepthBuffer = bmap;
	bmap->SetChanged(true);
	Notify(SCENE_FBChanged);
	return true;
}

bool Scene::SetColorBuffer(Bitmap* bmap)
{
	m_ColorBuffer = bmap;
	bmap->SetChanged(true);
	Notify(SCENE_FBChanged);
	return true;
}

/*!
 *@fn bool Scene::GetBound(Sphere* bound) const
 * @param bound	where to store bounding sphere
 *
 * Computes the closest fitting sphere in world coordinates which bounds
 * the entire scene graph.
 *
 * @return \b true if bounding sphere is not empty, else \b false
 *
 * @see Model::GetBound
 */
bool Scene::GetBound(Sphere* bound) const
{
	Sphere		s;
	const Model*	group = m_Models;
	bool			rc = false;

	bound->Empty();
	if (group && group->GetBound(&s, Model::WORLD))
	{
		bound->Extend(s);
		rc = true;
	}
	return rc;
}

/*!
 * @fn void Scene::SetTime()
 *
 * Establishes the time for the next frame to start.
 * The time is set from system time or from the time
 * increment if it is non-zero. If an argument is provided, the time
 * is set to that, overriding any other established time. This routine
 * should not be called at the user level. It is used internally for
 * sychronizing simulation across multiple processors.
 *
 * @see Scene::SetTimeInc World3D::GetTime Scene::GetTime
 */
void Scene::SetTime()
{
	if (m_TimeInc == 0.0f)				// animate in real time?
		m_Time = World3D::Get()->GetTime();
	else
		m_Time += m_TimeInc;			// no, frame based animation
	VX_TRACE2(Engine::Debug, ("Scene::SetTime(%0.3f)\n", m_Time));
}

/*!
 * @fn void Scene::OnFrame(int framenum)
 * Subclasses can override this routine to intercept control
 * once per frame. This routine is called right before
 * the scene display threads start a frame and holds off
 * display until it returns. You should not do time-consuming
 * operations here but you may safely update the hierarchy here.
 *
 * The base implementation computes the start time of the next
 * frame and processes updates from remote machines, applying
 * the necessary changes to the local hierarchy.
 * @note If you override this routine, you \b must
 * call the base implementation!
 *
 * @return starting time of frame (in seconds)
 *
 * @see Scene::DoFrame Scene::SetTime Messenger::Load Scene::GetTime
 */
float Scene::OnFrame(int framenum)
{
	Ref<Messenger> mess(GetMessenger());
	SetTime();
	if (IsSet(SharedObj::DOEVENTS))
	{
		FrameEvent* e = new FrameEvent(this);

		e->Frame = framenum;
		e->Log();		// generate frame event
	}
	mess->Load();		// process scene graph updates
	return m_Time;
}

/*!
 * @fn void Scene::SetOptions(int v)
 *
 * Defines scene display options. You can selectively
 * clear the display, the Z buffer or both. You can also enable
 * state sorting which will sort primitives based on appearance
 * before sending them to the display. This uses more memory
 * but is a significant performance enhancement for applications
 * which reuse textures a lot within a scene.
 *
 * @code
 *	CLEARCOLOR		flag will clear the display.
 *	CLEARDEPTH		flag will clear the Z buffer.
 *	CLEARALL		clears both (combination of both flags)
 *	STATESORT		enables state sorting
 *	FULLSCREEN		enables full screen operation
 *	REPAINT			suspends scene threads, suppressing continuous screen refresh,
 *					repaints a single frame when Repaint is called
 * @endcode
 *
 * @see Scene::EnableOptions Scene::DisableOptions Scene::Repaint
 */
void Scene::SetOptions(int v)
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_SetOptions) << this << int32(v);
	VX_STREAM_END( )

 	m_Options = v;

	Renderer* gs = GetRenderer();
	if (gs)
		gs->SetOptions((v & GeoSorter::NoStateSort) | gs->GetOptions());
}

/*!
 * @fn void Scene::EnableOptions(int opts)
 * @param opts	options to enable
 *
 * Enables the given scene display options. You can selectively
 * clear the display, the Z buffer or both. You can also enable
 * state sorting which will sort primitives based on appearance
 * before sending them to the display.
 * @code
 *	SCENE_ClearColor	flag will clear the display.
 *	SCENE_ClearDepth	flag will clear the Z buffer.
 *	SCENE_ClearAll		clears both (combination of both flags)
 *	SCENE_StateSort		enables state sorting
 *	SCENE_FullScreen	enables full screen operation
 *	SCENE_Repaint		suspends scene threads, suppressing continuous screen refresh,
 *						repaints a single frame when Repaint is called
 * @endcode
 *
 * @see Scene::SetOptions Scene::DisableOptions
 */
void Scene::EnableOptions(int opts)
{
	SetOptions(GetOptions() | opts);
}

/*!
 * @fn void Scene::DisableOptions(int opts)
 * @param opts	options to enable
 *
 * Disables the given scene display options.
 * @code
 *	SCENE_ClearColor	clear the frame buffer
 *	SCENE_ClearDepth	clear the Z buffer
 *	SCENE_ClearAll		clears both (default)
 *	SCENE_StateSort		enables state sorting (default)
 *	SCENE_FullScreen	enables full screen operation
 *	SCENE_Repaint		suspends scene threads, suppressing continuous screen refresh,
 *						repaints a single frame when Repaint is called
 * @endcode
 *
 * @see Scene::SetOptions Scene::EnableOptions
 */
void Scene::DisableOptions(int opts)
{
	SetOptions(GetOptions() & ~opts);
}

/*!
 * @fn void Scene::SetAutoAdjust(bool v)
 *
 * Determines whether or not the camera aspect ratio
 * is automatically adjusted as the scene viewport changes.
 *
 * @see Camera Camera::SetAspect Scene::SetViewport
 */
void Scene::SetAutoAdjust(bool v)
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_SetAutoAdjust) << this << int32(v);
	VX_STREAM_END( )

	m_AutoAdjust = v;
}

/*!
 * @fn void Scene::SetAmbient(const Col4& c)
 *
 * Establishes color and amount of ambient light in the scene.
 *
 * @see Light::SetAmbient Material::SetAmbient
 */
void Scene::SetAmbient(const Col4& c)
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_SetAmbientColor) << this << c;
	VX_STREAM_END( )

	Light*	l = GetAmbient();
	bool	hasambient = true;

	if ((c.r == 0) && (c.g == 0) && (c.b == 0))
		hasambient = false;
	if (l == NULL)
	{
		if (!hasambient)
			return;
		m_Ambient = new AmbientLight();
		m_Ambient->SetName(TEXT("scene.ambientlight"));
		m_Ambient->SetColor(c);
	}
	else if (!hasambient)
	{
		m_Ambient->SetActive(false);
		return;
	}
	else
	{
		m_Ambient->SetActive(true);
		m_Ambient->SetColor(c);
	}
}

void Scene::SetAmbient(Light* ambient)
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_SetAmbient) << this << ambient;
	VX_STREAM_END( )

	m_Ambient = ambient;
}

Light* Scene::GetAmbient() const
{
	Renderer*	render = GetRenderer();

	if (!m_Ambient.IsNull())
		return m_Ambient;
	if (render == NULL)
		return NULL;

	LightList::Iter	iter(render->GetLights());
	Light*			l;

	bool			enabled;
	while (l = iter.Next(enabled))
		if (l->ClassID() == VX_AmbientLight)
		{
			m_Ambient = l;
			return l;
		}
	return NULL;
}

/*!
 * @fn void Scene::SetEngines(const Engine* e)
 *
 * The set of engines used to compute the state of
 * the dynamic hierarchy over time. Like the Models
 * to display, the engines to compute are kept in a
 * hierarchy. Engines are executed before traversal
 * and culling. On multi-processor systems, engines
 * may be executed in a separate thread.
 *
 * @see Scene::DoSimulation Engine Engine::Compute
 */
void Scene::SetEngines(const Engine* e)
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_SetEngines) << this << e;
	VX_STREAM_END( )

	m_Engines = e;
}

/*!
 * @fn void Scene::SetCamera(const Camera* v)
 *
 * The camera used to view the scene. By default,
 * the scene has a camera which is not in the hierarchy.
 * You can change the camera to be part of the hierarchy
 * in which case its movement over time affects the
 * viewpoint from which the scene is displayed. The
 * default light for the scene tracks the camera.
 *
 * @see Scene::DoDisplay Camera
 */
void Scene::SetCamera(const Camera* v)
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_SetCamera) << this << v;
	VX_STREAM_END( )

	if (v == NULL)
		v = new Camera();

	bool bNewCamera = (v != m_Camera);
	m_Camera = v;
	if (bNewCamera)
	{
		v->SetChanged(true);
		Notify(SCENE_CameraChanged);
	}
}

/*!
 * @fn void Scene::SetPostProcess(Appearance* post)
 * @param post	Appearance used to post-process the scene after 3D rendering.
 *				The shader is run on all of the pixels in the framebuffer
 *				at the end of the frame.
 *
 * Designates the shader to be used for pixel post processing.
 * The name of the Shader associated with this appearance and it's entry point
 * must be called "PostProcessor". If not supplied, no post-processing is done.
 *
 * @see Scene::DoDisplay Shader
 */
void Scene::SetPostProcess(Appearance* appear)
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_SetPostProcess) << this << appear;
	VX_STREAM_END( )

	m_PostProcess = appear;
	Notify(SCENE_ShaderChanged);
}

/*!
 * @fn void Scene::SetBackColor(const Col4& c)
 *
 * If screen clearning is enabled, the background color is displayed in
 * the scene viewport before any meshes are rendered.
 *
 * @see Col4 Scene::SetOptions Scene::SetAmbient
 */
void Scene::SetBackColor(const Col4& c)
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_SetBackColor) << this << c;
	VX_STREAM_END( )

	m_BackColor = c;
}

/*!
 * @fn void Scene::SetTimeInc(float timeinc)
 * @param timeinc	time increment (in seconds) to advance each frame
 *					zero (default) advances in real time
 *
 * The time increment of a scene controls the speed of the animations
 * in the simulation tree. Each frame, the simulation engines are evaluated
 * with the current time. If the time increment is zero (the default case),
 * the current time will be the system time at the start of the frame.
 * If the time increment is non-zero, the current time is incremented by
 * that amount each frame, regardless of the system time. This is useful
 * when debugging time-based simulation engines.
 *
 * @see Scene::GetTime World3D::GetTime Engine::Compute
 */
void Scene::SetTimeInc(float timeinc)
{
	if (m_TimeInc == timeinc)
		return;
	if (m_TimeInc != 0.0f)			// establish credible starting time
	{								// for application (NOW)
		World3D*	w = World3D::Get();
		w->StartTime = w->GetTime();
	}
	m_TimeInc = timeinc;
}

/*!
 * @fn void Scene::SetModels(const Model* m)
 * @param m	root of scene graph to use. If NULL, nothing is displayed.
 *
 * Sets the hierarchy this scene should display.
 *
 * @see Scene::SetModels Scene::DoDisplay Model::Display
 */
void Scene::SetModels(const Model* m)
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_SetModels) << this << m;
	VX_STREAM_END( )

	m_Models = m;
}

/*!
 * @fn void Scene::DoFrame()
 *
 * Performs display traversal, simulation and rendering for the current frame.
 * @code
 *	1.	Process pending scene graph updates from remote
 *		clients from the messenger. The hierarchy is locked
 *		for update during this operation.
 *	2.	Compute the frame start time
 *	4.	Calculate bounding spheres for scene graph
 *	5.	Traverse scene graph and cull
 *	6.	Render geometry
 *	3.	Evaluate simulation engines
 * @endcode
 *
 * @see Model::Display Scene::DoDisplay Scene::DoRender Scene::DoSimulation Messenger::Load
 */
void Scene::DoFrame()
{
//
// compute start time of frame and process remote updates
//
	TLS*		g = GetTLS();
	Renderer*	r = GetRenderer();
	float		save_start = OnFrame(g->Frame);
	Scene*		scene = this;

	if (IsExit())						// got exit request?
		return;							// don't do next frame
//
//	REMOTE THREADS CAN UPDATE THE SCENE GRAPH HERE
//
	r->Flip(g->Frame);					// flip buffers
	AllowUpdate();						// allow update by other threads
	GetMessenger()->t_NoLog = true;		// no logging during traversal
 	Core::InterlockSet(&m_Stats.TotalVerts, 0);
	Core::InterlockSet(&m_Stats.CulledVerts, 0);
	Core::InterlockSet(&m_Stats.TotalModels, 0);
	Core::InterlockSet(&m_Stats.CulledModels, 0);
//
// do display traversal for this scene and all the child scenes
// which use its display context (procedural texture generation scenes)
//
	DoDisplay();						// do traversal, culling
	if (GetChild())						// force viewport change if multiple scenes
		Notify(SCENE_CameraChanged);
	DoRender();							// do rendering
//
// done rendering, enable logging and do simulation
//
	GetMessenger()->t_NoLog = false;	// enable logging again
	DoSimulation();						// run simulation (hierarchy locked here)
	GetMessenger()->Flush();			// distribute scene graph updates
	m_Stats.StartTime = save_start;
	++(g->Frame);
	m_Stats.EndTime = World3D::Get()->GetTime();
}

/*!
 * @fn void Scene::End()
 *
 * Called after rendering for a frame is complete.
 *
 * @see Scene::Begin Scene::DoRender
 */
void Scene::End()
{
	Renderer* r = GetRenderer();

	if (r)
		r->End(GetTLS()->Frame);
	m_Changed = 0;
}

/*!
 * @fn void Scene::DoSimulation()
 *
 * Traverses the simulation tree and calls Engine::Eval
 * for each active engine. This routine locks the entire hierarchy
 * and does not permit foreign threads to update the objects
 * while it is executing.
 *
 * @see Scene::GetTime Scene::SetTimeInc Scene::DoDisplay Scene::DoFrame Engine::Compute Engine::Eval
 */
void Scene::DoSimulation()
{
	Sphere	sphere;

	if (!m_Engines.IsNull())			// simulation engines?
		m_Engines->Compute(m_Time);		// run simulation
	if (!m_Models.IsNull())				// compute dynamic bounds
		m_Models->GetBound(&sphere);
}

/*!
 * @fn void Scene::DoRender()
 *
 * Render accumulated graphics primitives. Primitives are
 * accumulated into Renderer objects associated with
 * the scene as the scene graph is traversed each frame.
 * These non-culled primitives are sorted by appearance
 * and rendered after traversal by this routine.
 *
 * @see Scene::DoDisplay Scene::DoFrame Renderer Scene::Begin Scene::End
 */
void Scene::DoRender()
{
	Renderer*	r = GetRenderer();
	int			frame = GetTLS()->Frame;
	Scene*		child = GetChild();

	if (IsExit())
		return;
	if (child && child->IsActive())
	{
		child->Notify(SCENE_WindowChanged);
		child->DoRender();				// do rendering
	}
	m_Stats.PrimsRendered = m_Stats.RenderStateChanges = 0;
	Begin();
	if (r)
		r->Render(frame, GeoSorter::All);
	End();
}

/*!
 * @fn void Scene::Begin()
 *
 * Called internally from the rendering thread before
 * rendering each frame. Establishes initial conditions for
 * transformation matrix, lights and camera projection.
 *
 * @see Scene::DoRender Scene::End Scene::DoFrame
 */
void Scene::Begin()
{
	Scene::TLS*	tls = Scene::GetTLS();
	Renderer*	r = GetRenderer();

	if (r == NULL)
		return;
	if (m_Changed & SCENE_WindowChanged)
	{
		const Box2& vp = GetViewport();
		r->SetViewport(vp.min.x, vp.min.y, vp.max.x, vp.max.y);
	}
	if (m_Camera->HasChanged())
		m_Changed |= SCENE_CameraChanged;
	if (m_Changed & (SCENE_CameraChanged | SCENE_WindowChanged))
	{
		if (m_AutoAdjust)					// preserve aspect ratio of view volume?
			m_Camera->SetAspect(m_Viewport.Width() / m_Viewport.Height());
	}
	r->Begin(m_Changed, tls->Frame);
}

/*!
 * @fn void Scene::DoDisplay()
 *
 * Traverses the scene graph, does view volume culling and accumulates
 * meshes which should be rendered. This routine does not access the
 * graphics device directly.
 *
 * @see Model::Display Scene::DoFrame GeoSorter
 */
void Scene::DoDisplay()
{
	Renderer*	r = GetRenderer();
	TLS*		g = GetTLS();
	Camera*		cam = GetCamera();
	Scene*		child = GetChild();

	if (IsExit())
		return;
	if (child)
	{
		if (child->GetChanged() & SCENE_KillMe)
			RemoveChild();// link out the scene to delete
		else if (child->IsActive())
			child->DoDisplay();
	}
	InitCamera();
	if (r)
	{
		if (m_Changed & SCENE_RootChanged)
			r->Empty();
		r->Reset();
	}
	m_WorldMatrix.Identity();				// initialize view matrix
	if (!m_Ambient.IsNull())
		m_Ambient->Display(this);
	if (!cam->IsChild())					// not in the hierarchy
	{
		cam->Display(this);					// in case it has children
		m_WorldMatrix.Identity();			// initialize view matrix
	}
	m_WorldMatrix.SetChanged(false);
	if (!m_Models.IsNull())					// display dynamic models
	{
		m_WorldMatrix.Identity();			// initialize view matrix
		m_WorldMatrix.SetChanged(false);	// reset initial matrix
		m_Models->CalcStats();				// recalc vertex, face counts if necessary
		m_Models->Display(this);			// visit dynamic models
	}
	m_WorldMatrix.Identity();				// initialize view matrix
}

/*!
 * @fn void Scene::InitCamera()
 *
 * Called internally before scene traversal at the beginning
 * of each frame. Establishes initial conditions for
 * transformation matrix and camera parameters.
 *
 * @see Scene::Begin Scene::DoDisplay
 */
void Scene::InitCamera()
{
	Camera*		cam = m_Camera;

	if (m_Changed & SCENE_WindowChanged)
	{
		SetViewport();						// update viewport based on window size
		if (m_AutoAdjust)					// preserve aspect ratio of view volume?
			cam->SetAspect(m_Viewport.Width() / m_Viewport.Height());
	}
/*
 * Calculate the camera view matrix. The scene manager allows the
 * camera to be positioned anywhere. The display device supports a
 * fixed camera positioned at the origin looking down the Z axis.
 * The view matrix transforms the scene objects into this camera space.
 */
	if (cam->HasChanged())					// camera changed?
	{
		Notify(SCENE_CameraChanged);		// alert other threads
		cam->CalcViewPlanes();				// recalculate camera clip planes
		cam->SetChanged(false);
	}
	m_Camera->SetViewTrans(this);			// calculate new camera matrix and save it
}

/*!
 * @fn void	Scene::SetViewport(float left, float top, float right, float bottom)
 * @param left	X coordinate of upper left corner of screen viewport
 * @param top	Y coordinate of upper left corner of screen viewport
 * @param right	X coordinate of lower right corner of screen viewport
 * @param bottom Y coordinate of lower right corner of screen viewport
 *
 * Sets the screen viewport to which the 3D viewport maps.
 * You need to call this function whenever the window
 * origin or size changes to notify the scene manager to change the
 * 3D screen viewport the current scene is using.
 * You need to call SetViewport when you
 * handle window move and resize events.
 *
 * @see Scene::Begin Camera::SetViewVol
 */
void	Scene::SetViewport(float l, float t, float r, float b)
{
	m_Viewport.Set(l, r, t, b);
	Notify(SCENE_WindowChanged);
}

/*!
 * @fn void	Scene::SetViewport(const Box2& box)
 * @param box	2D box defining the upper left and lower right
 *				corners of the screen viewport
 *
 * Sets the screen viewport to which the 3D viewport maps.
 * You need to call this function whenever the window
 * origin or size changes to notify the scene manager to change the
 * 3D screen viewport the current scene is using.
 * You need to call SetViewport when you
 * handle window move and resize events.
 *
 * @see Scene::Begin Camera::SetViewVol
 */
void Scene::SetViewport(const Box2& b)
{
	VX_STREAM_BEGIN(s)
		*s << OP(VX_Scene, SCENE_SetViewport) << this << b.min.x << b.min.y << b.max.x << b.max.y;
	VX_STREAM_END( )

	SetViewport(b.min.x, b.min.y, b.max.x, b.max.y);
}

/****
 *
 * class Scene override for SharedObj::Save
 *
 ****/
int Scene::Save(Messenger& s, int opts) const
{
	const Model*		mod;
	const Camera*		cam;
	const Engine*		eng;
	const Appearance*	app;
	int32				h = SharedObj::Save(s, opts);

	if (h < 0)
		return h;
	if (h > 0)
	{
		s << OP(VX_Scene, SCENE_SetBackColor) << h << GetBackColor();
		s << OP(VX_Scene, SCENE_SetAmbient) << h << GetAmbient();
		s << OP(VX_Scene, SCENE_SetAutoAdjust) << h << int32(IsAutoAdjust());
	}
	cam = GetCamera();
	if (cam && (cam->Save(s, opts) >= 0) && h)
		s << OP(VX_Scene, SCENE_SetCamera) << h << cam;
	mod = GetModels();
	if (mod && (mod->Save(s, opts) >= 0) && h)
		s << OP(VX_Scene, SCENE_SetModels) << h << mod;
	eng = GetEngines();
	if (eng && (eng->Save(s, opts) >= 0) && h)
		s << OP(VX_Scene, SCENE_SetEngines) << h << eng;
	app = GetPostProcess();
	if (app && (app->Save(s, opts) >= 0) && h)
		s << OP(VX_Scene, SCENE_SetPostProcess) << h << app;
	return h;
}

/****
 *
 * class Scene override for SharedObj::Do
 *		SCENE_SetModels	<Model*>
 *		SCENE_SetCamera		<Camera*>
 *		SCENE_SetEngines	<Engine*>
 *		SCENE_SetBackColor	<Col4>
 *		SCENE_SetAutoAdjust <int32>
 *		SCENE_SetOptions	<int32>
 *		SCENE_SetFog		<Fog*>
 *
 ****/
bool Scene::Do(Messenger& s, int op)
{
	SharedObj*		obj;
	const Model*	mod;
	const Camera*	cam;
	const Engine*	eng;
	Col4			c;
	Box2			b;
	int32			n;
	Opcode			o = Opcode(op);

	switch (op)
	   {
		case SCENE_SetAutoAdjust:
		s >> n;
		SetAutoAdjust(n != 0);
		return true;

		case SCENE_SetOptions:
		s >> n;
		SetOptions(n);
		return true;

		case SCENE_SetBackColor:
		s >> c;
		SetBackColor(c);
		return true;

		case SCENE_SetAmbient:
		s >> c;
		SetAmbient(c);
		return true;

		case SCENE_SetViewport:
		s >> b.min.x >> b.min.y >> b.max.x >> b.max.y;
		SetViewport(b);
		return true;

		case SCENE_SetModels:
		s >> obj;
		VX_ASSERT((obj == NULL) || obj->IsClass(VX_Model));
		mod = (const Model*) obj;
		SetModels(mod);
		break;

		case SCENE_SetCamera:
		s >> obj;
		VX_ASSERT(obj->IsClass(VX_Camera));
		cam = (const Camera*) obj;
		SetCamera(cam);
		break;

		case SCENE_SetEngines:
		s >> obj;
		VX_ASSERT((obj == NULL) || obj->IsClass(VX_Engine));
		eng = (const Engine*) obj;
		SetEngines(eng);
		break;

		default:
		return SharedObj::Do(s, op);
	   }
#ifdef _DEBUG
	if (s.Debug)
		endl(vixen_debug << ClassName() << "::"
					   << Scene::DoNames[op - SCENE_UNUSED0]
					   << " " << this);
#endif
	return true;
}

} // end Vixen


text:
const  int kNumberOfColumns =2;//有几列

static int MaxPage = 1;

@interface EKViewController ()<EKStreamViewDelegate,EGORefreshTableHeaderDelegate>
{
    NSMutableArray *randomHeights;
    int page;
    EGORefreshTableHeaderView *_refreshHeaderView;
    BOOL _reloading;
}

@property ( nonatomic) IBOutlet EKStreamView *stream;
@property (nonatomic, retain) NSArray *images;

@end
@implementation EKViewController
@synthesize stream;

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    self.view.backgroundColor =[UIColor colorWithRed:231.0f/255.0f green:236.0f/255.0f blue:240.0f/255.0f alpha:1.0f];
    randomHeights = [[NSMutableArray alloc] initWithCapacity:100];
    for (int i = 0; i < 1000; i++) {
//        CGFloat h = arc4random() % 200 + 50.f;
//        [randomHeights addObject:@(h)];
         [randomHeights addObject:[NSString stringWithFormat:@"%d.jpg", i % 10 + 1]];

    }
    stream.scrollsToTop = YES;
    //填充距离
    stream.cellPadding = 5.0f;
    stream.columnPadding = 5.0f;

    //下拉刷新
    if (_refreshHeaderView == nil) {

		EGORefreshTableHeaderView *view = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - stream.bounds.size.height, self.view.frame.size.width, stream.bounds.size.height)];
		view.delegate = self;
		[stream addSubview:view];
		_refreshHeaderView = view;
        ;

	}

	//  update the last update date
	[_refreshHeaderView refreshLastUpdatedDate];

}

- (void)viewDidUnload
{
    [self setStream:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    // set backcolor

}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (NSInteger)numberOfCellsInStreamView:(EKStreamView *)streamView
{
    return [randomHeights count];
}

- (NSInteger)numberOfColumnsInStreamView:(EKStreamView *)streamView
{
    return kNumberOfColumns;
}

- (UIView *)streamView:(EKStreamView *)streamView cellAtIndex:(NSInteger)index
{
    static NSString *CellID1 = @"MyCell1";
    static NSString *CellID2 = @"MyCell2";

    BOOL redCell = index % kNumberOfColumns == 0;
    NSString *CellID =  redCell ? CellID2 : CellID1;

    EKStreamCell *cell;

    cell = (EKStreamCell *)[streamView dequeueReusableCellWithIdentifier:CellID];
     CGFloat destWidth = (streamView.bounds.size.width - (kNumberOfColumns + 1) * streamView.columnPadding) / kNumberOfColumns;
    if (cell == nil) {

        cell = [[EKStreamCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100) infoViewHeight:kViewInfoHeight] ;
        cell.reuseIdentifier = CellID;
//        if (redCell) cell.label.textColor = [UIColor redColor];
    }

//    cell.label.text = [NSString stringWithFormat:@"%d",index];
    UIImage* image =[self imageAtIndexPath:index];
    cell.mainImageView.image = image;

    cell.mainLabel.text =[NSString stringWithFormat:@"%d瀑布流例子",index];
//    cell.mainLabel.center = CGPointMake(streamView.bounds.size.width*0.5,streamView.bounds.size.height*0.5);

//    cell.topImageView.frame =CGRectMake(0, 0,  cell.topImageView.frame.size.width, image.size.height);

    return cell;
}

- (CGFloat)streamView:(EKStreamView *)streamView heightForCellAtIndex:(NSInteger)index
{

    CGFloat destWidth = (streamView.bounds.size.width - (kNumberOfColumns + 1) * streamView.columnPadding) / kNumberOfColumns;
    float height =[self imageAtIndexPath:index].size.height*destWidth/[self imageAtIndexPath:index].size.width+kViewInfoHeight;
//    NSLog(@"打印高度===%f",height);
    return height;
}
////顶部栏
- (UIView *)headerForStreamView:(EKStreamView *)streamView
{
    UIImage* topImage =[UIImage imageNamed:@"banler.jpg"];
//     CGFloat destWidth = self.view.frame.size.width - stream.columnPadding * 2;
    CGFloat destWidth = streamView.frame.size.width;
    float height =topImage.size.height*destWidth/topImage.size.width;
//    float height =190.0f;
    EKStreamTopCell *header = [[EKStreamTopCell alloc] initWithFrame:CGRectMake(0, 0,  destWidth , height)];

//    header.label.text = @"This is the header";
    header.mainImageView.image =topImage;
    NSLog(@"headerForColumnPadding===%f",self.view.frame.size.width);
    return header;
}

//底部栏
//- (UIView *)footerForStreamView:(EKStreamView *)streamView
//{
//    if (page <= MaxPage) {
//        MyCell *footer = [[MyCell alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width - stream.columnPadding * 2, 60)];
//        footer.label.text = @"This is the footer";
//
//         NSLog(@"footerForStreamView===%f",self.view.frame.size.width);
//
//        return footer;
//    } else {
//        return nil;
//    }
//}

//- (void)scrollViewDidScroll:(UIScrollView *)scrollView
//{
//    if (ABS(scrollView.contentSize.height - scrollView.frame.size.height - scrollView.contentOffset.y) < 3
//        && page <= MaxPage) {
//        for (int i = 0; i < 100; i++) {
//            CGFloat h = arc4random() % 200 + 50.f;
//            [randomHeights addObject:[NSNumber numberWithFloat:h]];
//        }
//
//        page++;
//
//        [stream reloadData];
//    }
//}
//

#pragma mark- images

- (UIImage *)imageAtIndexPath:(NSUInteger)index {
    return [UIImage imageNamed:[randomHeights objectAtIndex:index]];
}

#pragma mark -
#pragma mark UIScrollViewDelegate Methods
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    [_refreshHeaderView egoRefreshScrollViewDidScroll:scrollView];
    //    if (ABS(scrollView.contentSize.height - scrollView.frame.size.height - scrollView.contentOffset.y) < 3
    //        && page <= MaxPage) {
    //        for (int i = 0; i < 100; i++) {
    //            CGFloat h = arc4random() % 200 + 50.f;
    //            [randomHeights addObject:[NSNumber numberWithFloat:h]];
    //        }
    //
    //        page++;
    //
    //        [stream reloadData];
    //    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{

    [_refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView];

}
#pragma mark -
#pragma mark EGORefreshTableHeaderDelegate Methods

- (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView*)view{

	[self performSelector:@selector(doneLoadingTableViewData) withObject:nil afterDelay:3.0];

}

- (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView*)view{

	return _reloading; // should return if data source model is reloading

}

- (NSDate*)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView*)view{

	return [NSDate date]; // should return date data source was last changed

}

#pragma mark -
#pragma mark Data Source Loading / Reloading Methods

- (void)reloadTableViewDataSource{

	//  should be calling your tableviews data source model to reload
	//  put here just for demo
	_reloading = YES;

}

- (void)doneLoadingTableViewData{

	//  model should call this when its done loading
	_reloading = NO;
	[_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:stream];

}
@end

redpajama_set_name: RedPajamaGithub


text:
/*!
 * Angular Material Design
 * https://github.com/angular/material
 * @license MIT
 * v0.10.1-master-8c71d0c
 */
goog.provide('ng.material.components.datepicker');
goog.require('ng.material.components.icon');
goog.require('ng.material.components.virtualRepeat');
goog.require('ng.material.core');
(function() {
  'use strict';

  /**
   * @ngdoc module
   * @name material.components.datepicker
   * @description Datepicker
   */
  angular.module('material.components.datepicker', [
    'material.core',
    'material.components.icon',
    'material.components.virtualRepeat'
  ]).directive('mdCalendar', calendarDirective);

  // POST RELEASE
  // TODO(jelbourn): Mac Cmd + left / right == Home / End
  // TODO(jelbourn): Clicking on the month label opens the month-picker.
  // TODO(jelbourn): Minimum and maximum date
  // TODO(jelbourn): Refactor month element creation to use cloneNode (performance).
  // TODO(jelbourn): Define virtual scrolling constants (compactness) users can override.
  // TODO(jelbourn): Animated month transition on ng-model change (virtual-repeat)
  // TODO(jelbourn): Scroll snapping (virtual repeat)
  // TODO(jelbourn): Remove superfluous row from short months (virtual-repeat)
  // TODO(jelbourn): Month headers stick to top when scrolling.
  // TODO(jelbourn): Previous month opacity is lowered when partially scrolled out of view.
  // TODO(jelbourn): Support md-calendar standalone on a page (as a tabstop w/ aria-live
  //     announcement and key handling).
  // Read-only calendar (not just date-picker).

  /**
   * Height of one calendar month tbody. This must be made known to the virtual-repeat and is
   * subsequently used for scrolling to specific months.
   */
  var TBODY_HEIGHT = 265;

  function calendarDirective() {
    return {
      template:
          '

' +
          '
' +
            '<md-virtual-repeat-container class="md-calendar-scroll-container">' +
              '
' +
                '

' +
              '
' +
            '</md-virtual-repeat-container>' +
          '
',
      scope: {},
      require: ['ngModel', 'mdCalendar'],
      controller: CalendarCtrl,
      controllerAs: 'ctrl',
      bindToController: true,
      link: function(scope, element, attrs, controllers) {
        var ngModelCtrl = controllers[0];
        var mdCalendarCtrl = controllers[1];
        mdCalendarCtrl.configureNgModel(ngModelCtrl);
      }
    };
  }

  /** Class applied to the selected date cell/. */
  var SELECTED_DATE_CLASS = 'md-calendar-selected-date';

  /** Class applied to the focused date cell/. */
  var FOCUSED_DATE_CLASS = 'md-focus';

  /** Next identifier for calendar instance. */
  var nextUniqueId = 0;

  /** The first renderable date in the virtual-scrolling calendar (for all instances). */
  var firstRenderableDate = null;

  /**
   * Controller for the mdCalendar component.
   * ngInject @constructor
   */
  function CalendarCtrl($element, $attrs, $scope, $animate, $q, $mdConstant,
      $mdTheming, $$mdDateUtil, $mdDateLocale, $mdInkRipple, $mdUtil) {
    $mdTheming($element);
    /**
     * Dummy array-like object for virtual-repeat to iterate over. The length is the total
     * number of months that can be viewed. This is shorter than ideal because of (potential)
     * Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=1181658.
     */
    this.items = {length: 2000};

    /** @final {!angular.$animate} */
    this.$animate = $animate;

    /** @final {!angular.$q} */
    this.$q = $q;

    /** @final */
    this.$mdInkRipple = $mdInkRipple;

    /** @final */
    this.$mdUtil = $mdUtil;

    /** @final */
    this.keyCode = $mdConstant.KEY_CODE;

    /** @final */
    this.dateUtil = $$mdDateUtil;

    /** @final */
    this.dateLocale = $mdDateLocale;

    /** @final {!angular.JQLite} */
    this.$element = $element;

    /** @final {!angular.Scope} */
    this.$scope = $scope;

    /** @final {HTMLElement} */
    this.calendarElement = $element[0].querySelector('.md-calendar');

    /** @final {HTMLElement} */
    this.calendarScroller = $element[0].querySelector('.md-virtual-repeat-scroller');

    /** @final {Date} */
    this.today = this.dateUtil.createDateAtMidnight();

    // Set the first renderable date once for all calendar instances.
    firstRenderableDate =
        firstRenderableDate || this.dateUtil.incrementMonths(this.today, -this.items.length / 2);

    /** @final {number} Unique ID for this calendar instance. */
    this.id = nextUniqueId++;

    /** @type {!angular.NgModelController} */
    this.ngModelCtrl = null;

    /**
     * The selected date. Keep track of this separately from the ng-model value so that we
     * can know, when the ng-model value changes, what the previous value was before its updated
     * in the component's UI.
     *
     * @type {Date}
     */
    this.selectedDate = null;

    /**
     * The date that is currently focused or showing in the calendar. This will initially be set
     * to the ng-model value if set, otherwise to today. It will be updated as the user navigates
     * to other months. The cell corresponding to the displayDate does not necesarily always have
     * focus in the document (such as for cases when the user is scrolling the calendar).
     * @type {Date}
     */
    this.displayDate = null;

    /**
     * The date that has or should have focus.
     * @type {Date}
     */
    this.focusDate = null;

    /** @type {boolean} */
    this.isInitialized = false;

    /** @type {boolean} */
    this.isMonthTransitionInProgress = false;

    // Unless the user specifies so, the calendar should not be a tab stop.
    // This is necessary because ngAria might add a tabindex to anything with an ng-model
    // (based on whether or not the user has turned that particular feature on/off).
    if (!$attrs['tabindex']) {
      $element.attr('tabindex', '-1');
    }

    var self = this;

    /**
     * Handles a click event on a date cell.
     * Created here so that every cell can use the same function instance.
     * @this {HTMLTableCellElement} The cell that was clicked.
     */
    this.cellClickHandler = function() {
      var cellElement = this;
      if (this.hasAttribute('data-timestamp')) {
        $scope.$apply(function() {
          var timestamp = Number(cellElement.getAttribute('data-timestamp'));
          self.setNgModelValue(self.dateUtil.createDateAtMidnight(timestamp));
        });
      }
    };

    this.attachCalendarEventListeners();
  }
  CalendarCtrl.$inject = ["$element", "$attrs", "$scope", "$animate", "$q", "$mdConstant", "$mdTheming", "$$mdDateUtil", "$mdDateLocale", "$mdInkRipple", "$mdUtil"];

  /*** Initialization ***/

  /**
   * Sets up the controller's reference to ngModelController.
   * @param {!angular.NgModelController} ngModelCtrl
   */
  CalendarCtrl.prototype.configureNgModel = function(ngModelCtrl) {
    this.ngModelCtrl = ngModelCtrl;

    var self = this;
    ngModelCtrl.$render = function() {
      self.changeSelectedDate(self.ngModelCtrl.$viewValue);
    };
  };

  /**
   * Initialize the calendar by building the months that are initially visible.
   * Initialization should occur after the ngModel value is known.
   */
  CalendarCtrl.prototype.buildInitialCalendarDisplay = function() {
    this.buildWeekHeader();
    this.hideVerticalScrollbar();

    this.displayDate = this.selectedDate || this.today;
    this.isInitialized = true;
  };

  /**
   * Hides the vertical scrollbar on the calendar scroller by setting the width on the
   * calendar scroller and the `overflow: hidden` wrapper around the scroller, and then setting
   * a padding-right on the scroller equal to the width of the browser's scrollbar.
   *
   * This will cause a reflow.
   */
  CalendarCtrl.prototype.hideVerticalScrollbar = function() {
    var element = this.$element[0];

    var scrollMask = element.querySelector('.md-calendar-scroll-mask');
    var scroller = this.calendarScroller;

    var headerWidth = element.querySelector('.md-calendar-day-header').clientWidth;
    var scrollbarWidth = scroller.offsetWidth - scroller.clientWidth;

    scrollMask.style.width = headerWidth + 'px';
    scroller.style.width = (headerWidth + scrollbarWidth) + 'px';
    scroller.style.paddingRight = scrollbarWidth + 'px';
  };

  /** Attach event listeners for the calendar. */
  CalendarCtrl.prototype.attachCalendarEventListeners = function() {
    // Keyboard interaction.
    this.$element.on('keydown', angular.bind(this, this.handleKeyEvent));
  };

  /*** User input handling ***/

  /**
   * Handles a key event in the calendar with the appropriate action. The action will either
   * be to select the focused date or to navigate to focus a new date.
   * @param {KeyboardEvent} event
   */
  CalendarCtrl.prototype.handleKeyEvent = function(event) {
    var self = this;
    this.$scope.$apply(function() {
      // Capture escape and emit back up so that a wrapping component
      // (such as a date-picker) can decide to close.
      if (event.which == self.keyCode.ESCAPE || event.which == self.keyCode.TAB) {
        self.$scope.$emit('md-calendar-close');

        if (event.which == self.keyCode.TAB) {
          event.preventDefault();
        }

        return;
      }

      // Remaining key events fall into two categories: selection and navigation.
      // Start by checking if this is a selection event.
      if (event.which === self.keyCode.ENTER) {
        self.setNgModelValue(self.displayDate);
        event.preventDefault();
        return;
      }

      // Selection isn't occuring, so the key event is either navigation or nothing.
      var date = self.getFocusDateFromKeyEvent(event);
      if (date) {
        event.preventDefault();
        event.stopPropagation();

        // Since this is a keyboard interaction, actually give the newly focused date keyboard
        // focus after the been brought into view.
        self.changeDisplayDate(date).then(function () {
          self.focus(date);
        });
      }
    });
  };

  /**
   * Gets the date to focus as the result of a key event.
   * @param {KeyboardEvent} event
   * @returns {Date} Date to navigate to, or null if the key does not match a calendar shortcut.
   */
  CalendarCtrl.prototype.getFocusDateFromKeyEvent = function(event) {
    var dateUtil = this.dateUtil;
    var keyCode = this.keyCode;

    switch (event.which) {
      case keyCode.RIGHT_ARROW: return dateUtil.incrementDays(this.displayDate, 1);
      case keyCode.LEFT_ARROW: return dateUtil.incrementDays(this.displayDate, -1);
      case keyCode.DOWN_ARROW:
        return event.metaKey ?
          dateUtil.incrementMonths(this.displayDate, 1) :
          dateUtil.incrementDays(this.displayDate, 7);
      case keyCode.UP_ARROW:
        return event.metaKey ?
          dateUtil.incrementMonths(this.displayDate, -1) :
          dateUtil.incrementDays(this.displayDate, -7);
      case keyCode.PAGE_DOWN: return dateUtil.incrementMonths(this.displayDate, 1);
      case keyCode.PAGE_UP: return dateUtil.incrementMonths(this.displayDate, -1);
      case keyCode.HOME: return dateUtil.getFirstDateOfMonth(this.displayDate);
      case keyCode.END: return dateUtil.getLastDateOfMonth(this.displayDate);
      default: return null;
    }
  };

  /**
   * Gets the "index" of the currently selected date as it would be in the virtual-repeat.
   * @returns {number}
   */
  CalendarCtrl.prototype.getSelectedMonthIndex = function() {
    return this.dateUtil.getMonthDistance(firstRenderableDate, this.selectedDate || this.today);
  };

  /**
   * Scrolls to the month of the given date.
   * @param {Date} date
   */
  CalendarCtrl.prototype.scrollToMonth = function(date) {
    if (!this.dateUtil.isValidDate(date)) {
      return;
    }

    var monthDistance = this.dateUtil.getMonthDistance(firstRenderableDate, date);
    this.calendarScroller.scrollTop = monthDistance * TBODY_HEIGHT;
  };

  /**
   * Sets the ng-model value for the calendar and emits a change event.
   * @param {Date} date
   */
  CalendarCtrl.prototype.setNgModelValue = function(date) {
    this.$scope.$emit('md-calendar-change', date);
    this.ngModelCtrl.$setViewValue(date);
    this.ngModelCtrl.$render();
  };

  /**
   * Focus the cell corresponding to the given date.
   * @param {Date=} opt_date
   */
  CalendarCtrl.prototype.focus = function(opt_date) {
    var date = opt_date || this.selectedDate;

    var previousFocus = this.calendarElement.querySelector('.md-focus');
    if (previousFocus) {
      previousFocus.classList.remove(FOCUSED_DATE_CLASS);
    }

    var cellId = this.getDateId(date);
    var cell = document.getElementById(cellId);
    if (cell) {
      cell.classList.add(FOCUSED_DATE_CLASS);
      cell.focus();
    } else {
      this.focusDate = date;
    }
  };

  /*** Updating the displayed / selected date ***/

  /**
   * Change the selected date in the calendar (ngModel value has already been changed).
   * @param {Date} date
   */
  CalendarCtrl.prototype.changeSelectedDate = function(date) {
    var self = this;
    var previousSelectedDate = this.selectedDate;
    this.selectedDate = date;
    this.changeDisplayDate(date).then(function() {

      // Remove the selected class from the previously selected date, if any.
      if (previousSelectedDate) {
        var prevDateCell =
            document.getElementById(self.getDateId(previousSelectedDate));
        if (prevDateCell) {
          prevDateCell.classList.remove(SELECTED_DATE_CLASS);
          prevDateCell.setAttribute('aria-selected', 'false');
        }
      }

      // Apply the select class to the new selected date if it is set.
      if (date) {
        var dateCell = document.getElementById(self.getDateId(date));
        if (dateCell) {
          dateCell.classList.add(SELECTED_DATE_CLASS);
          dateCell.setAttribute('aria-selected', 'true');
        }
      }
    });
  };

  /**
   * Change the date that is being shown in the calendar. If the given date is in a different
   * month, the displayed month will be transitioned.
   * @param {Date} date
   */
  CalendarCtrl.prototype.changeDisplayDate = function(date) {
    // Initialization is deferred until this function is called because we want to reflect
    // the starting value of ngModel.
    if (!this.isInitialized) {
      this.buildInitialCalendarDisplay();
      return this.$q.when();
    }

    // If trying to show an invalid date or a transition is in progress, do nothing.
    if (!this.dateUtil.isValidDate(date) || this.isMonthTransitionInProgress) {
      return this.$q.when();
    }

    this.isMonthTransitionInProgress = true;
    var animationPromise = this.animateDateChange(date);

    this.displayDate = date;

    var self = this;
    animationPromise.then(function() {
      self.isMonthTransitionInProgress = false;
    });

    return animationPromise;
  };

  /**
   * Animates the transition from the calendar's current month to the given month.
   * @param {Date} date
   * @returns {angular.$q.Promise} The animation promise.
   */
  CalendarCtrl.prototype.animateDateChange = function(date) {
    this.scrollToMonth(date);
    return this.$q.when();
  };

  /*** Constructing the calendar table ***/

  /**
   * Builds and appends a day-of-the-week header to the calendar.
   * This should only need to be called once during initialization.
   */
  CalendarCtrl.prototype.buildWeekHeader = function() {
    var row = document.createElement('tr');
    for (var i = 0; i < 7; i++) {
      var th = document.createElement('th');
      th.textContent = this.dateLocale.shortDays[i];
      row.appendChild(th);
    }

    this.$element.find('thead').append(row);
  };

    /**
   * Gets an identifier for a date unique to the calendar instance for internal
   * purposes. Not to be displayed.
   * @param {Date} date
   * @returns {string}
   */
  CalendarCtrl.prototype.getDateId = function(date) {
    return [
      'md',
      this.id,
      date.getFullYear(),
      date.getMonth(),
      date.getDate()
    ].join('-');
  };
})();

(function() {
  'use strict';

  angular.module('material.components.datepicker')
      .directive('mdCalendarMonth', mdCalendarMonthDirective);

  /**
   * Private directive consumed by md-calendar. Having this directive lets the calender use
   * md-virtual-repeat and also cleanly separates the month DOM construction functions from
   * the rest of the calendar controller logic.
   */
  function mdCalendarMonthDirective() {
    return {
      require: ['^^mdCalendar', 'mdCalendarMonth'],
      scope: {offset: '=mdMonthOffset'},
      controller: CalendarMonthCtrl,
      controllerAs: 'mdMonthCtrl',
      bindToController: true,
      link: function(scope, element, attrs, controllers) {
        var calendarCtrl = controllers[0];
        var monthCtrl = controllers[1];

        monthCtrl.calendarCtrl = calendarCtrl;
        monthCtrl.generateContent();

        // The virtual-repeat re-uses the same DOM elements, so there are only a limited number
        // of repeated items that are linked, and then those elements have their bindings updataed.
        // Since the months are not generated by bindings, we simply regenerate the entire thing
        // when the binding (offset) changes.
        scope.$watch(function() { return monthCtrl.offset }, function(offset, oldOffset) {
          if (offset != oldOffset) {
            monthCtrl.generateContent();
          }
        });
      }
    };
  }

  /** Class applied to the cell for today. */
  var TODAY_CLASS = 'md-calendar-date-today';

  /** Class applied to the selected date cell/. */
  var SELECTED_DATE_CLASS = 'md-calendar-selected-date';

  /** Class applied to the focused date cell/. */
  var FOCUSED_DATE_CLASS = 'md-focus';

  /**
   * Controller for a single calendar month.
   * ngInject @constructor
   */
  function CalendarMonthCtrl($element, $$mdDateUtil, $mdDateLocale) {
    this.dateUtil = $$mdDateUtil;
    this.dateLocale = $mdDateLocale;
    this.$element = $element;
    this.calendarCtrl = null;

    /**
     * Number of months from the start of the month "items"
     * that the currently rendered month occurs.
     * @type {number}
     */
    this.offset = 0;

    /**
     * Date cell to focus after appending the month to the document.
     * @type {HTMLElement}
     */
    this.focusAfterAppend = null;
  }
  CalendarMonthCtrl.$inject = ["$element", "$$mdDateUtil", "$mdDateLocale"];

  /** Generate and append the content for this month to the directive element. */
  CalendarMonthCtrl.prototype.generateContent = function() {
    var calendarCtrl = this.calendarCtrl;
    var offset = (-calendarCtrl.items.length / 2) + this.offset;
    var date = this.dateUtil.incrementMonths(calendarCtrl.today, offset);

    this.$element.empty();
    this.$element.append(this.buildCalendarForMonth(date));

    if (this.focusAfterAppend) {
      this.focusAfterAppend.classList.add(FOCUSED_DATE_CLASS);
      this.focusAfterAppend.focus();
      this.focusAfterAppend = null;
    }
  };

  /**
   * Creates a single cell to contain a date in the calendar with all appropriate
   * attributes and classes added. If a date is given, the cell content will be set
   * based on the date.
   * @param {Date=} opt_date
   * @returns {HTMLElement}
   */
  CalendarMonthCtrl.prototype.buildDateCell = function(opt_date) {
    var calendarCtrl = this.calendarCtrl;

    // TODO(jelbourn): cloneNode is likely a faster way of doing this.
    var cell = document.createElement('td');
    cell.tabIndex = -1;
    cell.classList.add('md-calendar-date');
    cell.setAttribute('role', 'gridcell');

    if (opt_date) {
      // Add a indicator for select, hover, and focus states.
      var selectionIndicator = document.createElement('span');
      cell.appendChild(selectionIndicator);
      selectionIndicator.classList.add('md-calendar-date-selection-indicator');
      selectionIndicator.textContent = this.dateLocale.dates[opt_date.getDate()];

      cell.setAttribute('tabindex', '-1');
      cell.setAttribute('aria-label', this.dateLocale.longDateFormatter(opt_date));
      cell.id = calendarCtrl.getDateId(opt_date);
      cell.addEventListener('click', calendarCtrl.cellClickHandler);

      // Use `data-timestamp` attribute because IE10 does not support the `dataset` property.
      cell.setAttribute('data-timestamp', opt_date.getTime());

      // TODO(jelourn): Doing these comparisons for class addition during generation might be slow.
      // It may be better to finish the construction and then query the node and add the class.
      if (this.dateUtil.isSameDay(opt_date, calendarCtrl.today)) {
        cell.classList.add(TODAY_CLASS);
      }

      if (this.dateUtil.isValidDate(calendarCtrl.selectedDate) &&
          this.dateUtil.isSameDay(opt_date, calendarCtrl.selectedDate)) {
        cell.classList.add(SELECTED_DATE_CLASS);
        cell.setAttribute('aria-selected', 'true');
      }

      if (calendarCtrl.focusDate && this.dateUtil.isSameDay(opt_date, calendarCtrl.focusDate)) {
        this.focusAfterAppend = cell;
      }
    }

    return cell;
  };

  /**
   * Builds a `tr` element for the calendar grid.
   * @param rowNumber The week number within the month.
   * @returns {HTMLElement}
   */
  CalendarMonthCtrl.prototype.buildDateRow = function(rowNumber) {
    var row = document.createElement('tr');
    row.setAttribute('role', 'row');

    // Because of an NVDA bug (with Firefox), the row needs an aria-label in order
    // to prevent the entire row being read aloud when the user moves between rows.
    // See http://community.nvda-project.org/ticket/4643.
    row.setAttribute('aria-label', this.dateLocale.weekNumberFormatter(rowNumber));

    return row;
  };

  /**
   * Builds the
 content for the given date's month.
   * @param {Date=} opt_dateInMonth
   * @returns {DocumentFragment} A document fragment containing the
 elements.
   */
  CalendarMonthCtrl.prototype.buildCalendarForMonth = function(opt_dateInMonth) {
    var date = this.dateUtil.isValidDate(opt_dateInMonth) ? opt_dateInMonth : new Date();

    var firstDayOfMonth = this.dateUtil.getFirstDateOfMonth(date);
    var firstDayOfTheWeek = firstDayOfMonth.getDay();
    var numberOfDaysInMonth = this.dateUtil.getNumberOfDaysInMonth(date);

    // Store rows for the month in a document fragment so that we can append them all at once.
    var monthBody = document.createDocumentFragment();

    var rowNumber = 1;
    var row = this.buildDateRow(rowNumber);
    monthBody.appendChild(row);

    // Add a label for the month. If the month starts on a Sun/Mon/Tues, the month label
    // goes on a row above the first of the month. Otherwise, the month label takes up the first
    // two cells of the first row.
    var blankCellOffset = 0;
    var monthLabelCell = document.createElement('td');
    monthLabelCell.classList.add('md-calendar-month-label');
    if (firstDayOfTheWeek <= 2) {
      monthLabelCell.setAttribute('colspan', '7');

      var monthLabelRow = this.buildDateRow();
      monthLabelRow.appendChild(monthLabelCell);
      monthBody.insertBefore(monthLabelRow, row);
    } else {
      blankCellOffset = 2;
      monthLabelCell.setAttribute('colspan', '2');
      row.appendChild(monthLabelCell);
    }

    monthLabelCell.textContent = this.dateLocale.monthHeaderFormatter(date);

    // Add a blank cell for each day of the week that occurs before the first of the month.
    // For example, if the first day of the month is a Tuesday, add blank cells for Sun and Mon.
    // The blankCellOffset is needed in cases where the first N cells are used by the month label.
    for (var i = blankCellOffset; i < firstDayOfTheWeek; i++) {
      row.appendChild(this.buildDateCell());
    }

    // Add a cell for each day of the month, keeping track of the day of the week so that
    // we know when to start a new row.
    var dayOfWeek = firstDayOfTheWeek;
    var iterationDate = firstDayOfMonth;
    for (var d = 1; d <= numberOfDaysInMonth; d++) {
      // If we've reached the end of the week, start a new row.
      if (dayOfWeek === 7) {
        dayOfWeek = 0;
        rowNumber++;
        row = this.buildDateRow(rowNumber);
        monthBody.appendChild(row);
      }

      iterationDate.setDate(d);
      var cell = this.buildDateCell(iterationDate);
      row.appendChild(cell);

      dayOfWeek++;
    }

    // Ensure that the last row of the month has 7 cells.
    while (row.childNodes.length < 7) {
      row.appendChild(this.buildDateCell());
    }

    // Ensure that all months have 6 rows. This is necessary for now because the virtual-repeat
    // requires that all items have exactly the same height.
    while (monthBody.childNodes.length < 6) {
      var whitespaceRow = this.buildDateRow();
      for (var i = 0; i < 7; i++) {
        whitespaceRow.appendChild(this.buildDateCell());
      }
      monthBody.appendChild(whitespaceRow);
    }

    return monthBody;
  };

})();

(function() {
  'use strict';

  /**
   * @ngdoc service
   * @name $mdDateLocaleProvider
   * @module material.components.datepicker
   *
   * @description
   * The `$mdDateLocaleProvider` is the provider that creates the `$mdDateLocale` service.
   * This provider that allows the user to specify messages, formatters, and parsers for date
   * internationalization. The `$mdDateLocale` service itself is consumed by Angular Material
   * components that that deal with dates.
   *
   * @property {(Array<string>)=} months Array of month names (in order).
   * @property {(Array<string>)=} shortMonths Array of abbreviated month names.
   * @property {(Array<string>)=} days Array of the days of the week (in order).
   * @property {(Array<string>)=} shortDays Array of abbreviated dayes of the week.
   * @property {(Array<string>)=} dates Array of dates of the month. Only necessary for locales
   *     using a numeral system other than [1, 2, 3...].
   * @property {(function(string): Date)=} parseDate Function to parse a date object from a string.
   * @property {(function(Date): string)=} formatDate Function to format a date object to a string.
   * @property {(function(Date): string)=} monthHeaderFormatter Function that returns the label for
   *     a month given a date.
   * @property {(function(number): string)=} weekNumberFormatter Function that returns a label for
   *     a week given the week number.
   * @property {(string)=} msgCalendar Translation of the label "Calendar" for the current locale.
   * @property {(string)=} msgOpenCalendar Translation of the button label "Open calendar" for the
   *     current locale.
   *
   * @usage
   * <hljs lang="js">
   *   myAppModule.config(function($mdDateLocaleProvider) {
   *
   *     // Example of a French localization.
   *     $mdDateLocaleProvider.months = ['janvier', 'février', 'mars', ...];
   *     $mdDateLocaleProvider.shortMonths = ['janv', 'févr', 'mars', ...];
   *     $mdDateLocaleProvider.days = ['dimanche', 'lundi', 'mardi', ...];
   *     $mdDateLocaleProvider.shortDays = ['Di', 'Lu', 'Ma', ...];
   *
   *     // Optional.
   *     $mdDateLocaleProvider.dates = [1, 2, 3, 4, 5, 6, ...];
   *
   *     // Example uses moment.js to parse and format dates.
   *     $mdDateLocaleProvider.parseDate = function(dateString) {
   *       return moment(dateString).toDate();
   *     };
   *
   *     $mdDateLocaleProvider.formatDate = function(date) {
   *       return moment(date).format('L');
   *     };
   *
   *     $mdDateLocaleProvider.monthHeaderFormatter = function(date) {
   *       myShortMonths[date.getMonth()] + ' ' + date.getFullYear();
   *     };
   *
   *     // In addition to date display, date components also need localized messages
   *     // for aria-labels for screen-reader users.
   *
   *     $mdDateLocaleProvider.weekNumberFormatter = function(weekNumber) {
   *       return 'Semaine ' + weekNumber;
   *     };
   *
   *     $mdDateLocaleProvider.msgCalendar = 'Calendrier';
   *     $mdDateLocaleProvider.msgOpenCalendar = 'Ouvrir le calendrier';
   *
   * });
   * </hljs>
   *
   */

  angular.module('material.components.datepicker').config(["$provide", function($provide) {
    // TODO(jelbourn): Assert provided values are correctly formatted. Need assertions.

    /** @constructor */
    function DateLocaleProvider() {
      /** Array of full month names. E.g., ['January', 'Febuary', ...] */
      this.months = null;

      /** Array of abbreviated month names. E.g., ['Jan', 'Feb', ...] */
      this.shortMonths = null;

      /** Array of full day of the week names. E.g., ['Monday', 'Tuesday', ...] */
      this.days = null;

      /** Array of abbreviated dat of the week names. E.g., ['M', 'T', ...] */
      this.shortDays = null;

      /** Array of dates of a month (1 - 31). Characters might be different in some locales. */
      this.dates = null;

      /**
       * Function that converts the date portion of a Date to a string.
       * @type {(function(Date): string)}
       */
      this.formatDate = null;

      /**
       * Function that converts a date string to a Date object (the date portion)
       * @type {function(string): Date}
       */
      this.parseDate = null;

      /**
       * Function that formats a Date into a month header string.
       * @type {function(Date): string}
       */
      this.monthHeaderFormatter = null;

      /**
       * Function that formats a week number into a label for the week.
       * @type {function(number): string}
       */
      this.weekNumberFormatter = null;

      /**
       * Function that formats a date into a long aria-label that is read
       * when the focused date changes.
       * @type {function(Date): string}
       */
      this.longDateFormatter = null;

      /**
       * ARIA label for the calendar "dialog" used in the datepicker.
       * @type {string}
       */
      this.msgCalendar = '';

      /**
       * ARIA label for the datepicker's "Open calendar" buttons.
       * @type {string}
       */
      this.msgOpenCalendar = '';
    }

    /**
     * Factory function that returns an instance of the dateLocale service.
     * ngInject
     * @param $locale
     * @returns {DateLocale}
     */
    DateLocaleProvider.prototype.$get = function($locale) {
      /**
       * Default date-to-string formatting function.
       * @param {!Date} date
       * @returns {string}
       */
      function defaultFormatDate(date) {
        return date.toLocaleDateString();
      }

      /**
       * Default string-to-date parsing function.
       * @param {string} dateString
       * @returns {!Date}
       */
      function defaultParseDate(dateString) {
        return new Date(dateString);
      }

      /**
       * Default function to determine whether a string makes sense to be
       * parsed to a Date object.
       *
       * This is very permissive and is just a basic sanity check to ensure that
       * things like single integers aren't able to be parsed into dates.
       * @param {string} dateString
       * @returns {boolean}
       */
      function defaultIsDateComplete(dateString) {
        dateString = dateString.trim();

        // Looks for three chunks of content (either numbers or text) separated
        // by delimiters.
        var re = /^(([a-zA-Z]{3,}|[0-9]{1,4})([ \.,]+|[\/\-])){2}([a-zA-Z]{3,}|[0-9]{1,4})$/;
        return re.test(dateString);
      }

      /**
       * Default date-to-string formatter to get a month header.
       * @param {!Date} date
       * @returns {string}
       */
      function defaultMonthHeaderFormatter(date) {
        return service.shortMonths[date.getMonth()] + ' ' + date.getFullYear();
      }

      /**
       * Default week number formatter.
       * @param number
       * @returns {string}
       */
      function defaultWeekNumberFormatter(number) {
        return 'Week ' + number;
      }

      /**
       * Default formatter for date cell aria-labels.
       * @param {!Date} date
       * @returns {string}
       */
      function defaultLongDateFormatter(date) {
        // Example: 'Thursday June 18 2015'
        return [
          service.days[date.getDay()],
          service.months[date.getMonth()],
          service.dates[date.getDate()],
          date.getFullYear()
        ].join(' ');
      }

      // The default "short" day strings are the first character of each day,
      // e.g., "Monday" => "M".
      var defaultShortDays = $locale.DATETIME_FORMATS.DAY.map(function(day) {
        return day[0];
      });

      // The default dates are simply the numbers 1 through 31.
      var defaultDates = Array(32);
      for (var i = 1; i <= 31; i++) {
        defaultDates[i] = i;
      }

      // Default ARIA messages are in English (US).
      var defaultMsgCalendar = 'Calendar';
      var defaultMsgOpenCalendar = 'Open calendar';

      var service = {
        months: this.months || $locale.DATETIME_FORMATS.MONTH,
        shortMonths: this.shortMonths || $locale.DATETIME_FORMATS.SHORTMONTH,
        days: this.days || $locale.DATETIME_FORMATS.DAY,
        shortDays: this.shortDays || defaultShortDays,
        dates: this.dates || defaultDates,
        formatDate: this.formatDate || defaultFormatDate,
        parseDate: this.parseDate || defaultParseDate,
        isDateComplete: this.isDateComplete || defaultIsDateComplete,
        monthHeaderFormatter: this.monthHeaderFormatter || defaultMonthHeaderFormatter,
        weekNumberFormatter: this.weekNumberFormatter || defaultWeekNumberFormatter,
        longDateFormatter: this.longDateFormatter || defaultLongDateFormatter,
        msgCalendar: this.msgCalendar || defaultMsgCalendar,
        msgOpenCalendar: this.msgOpenCalendar || defaultMsgOpenCalendar
      };

      return service;
    };
    DateLocaleProvider.prototype.$get.$inject = ["$locale"];

    $provide.provider('$mdDateLocale', new DateLocaleProvider());
  }]);
})();

(function() {
  'use strict';

  // POST RELEASE
  // TODO(jelbourn): Demo that uses moment.js
  // TODO(jelbourn): make sure this plays well with validation and ngMessages.
  // TODO(jelbourn): calendar pane doesn't open up outside of visible viewport.
  // TODO(jelbourn): forward more attributes to the internal input (required, autofocus, etc.)
  // TODO(jelbourn): something better for mobile (calendar panel takes up entire screen?)
  // TODO(jelbourn): input behavior (masking? auto-complete?)
  // TODO(jelbourn): UTC mode
  // TODO(jelbourn): RTL

  angular.module('material.components.datepicker')
      .directive('mdDatepicker', datePickerDirective);

  /**
   * @ngdoc directive
   * @name mdDatepicker
   * @module material.components.datepicker
   *
   * @param {Date} ng-model The component's model. Expects a JavaScript Date object.
   * @param {boolean=} disabled Whether the datepicker is disabled.
   *
   * @description
   * `<md-datepicker>` is a component used to select a single date.
   * For information on how to configure internationalization for the date picker,
   * see `$mdDateLocaleProvider`.
   *
   * @usage
   * <hljs lang="html">
   *   <md-datepicker ng-model="birthday"></md-datepicker>
   * </hljs>
   *
   */
  function datePickerDirective() {
    return {
      template:
          // Buttons are not in the tab order because users can open the calendar via keyboard
          // interaction on the text input, and multiple tab stops for one component (picker)
          // may be confusing.
          '<md-button class="md-datepicker-button md-icon-button" type="button" ' +
              'tabindex="-1" aria-hidden="true" ' +
              'ng-click="ctrl.openCalendarPane($event)">' +
            '<md-icon class="md-datepicker-calendar-icon" md-svg-icon="md-calendar"></md-icon>' +
          '</md-button>' +
          '
' +
            '<input class="md-datepicker-input" aria-haspopup="true" ' +
                'ng-focus="ctrl.setFocused(true)" ng-blur="ctrl.setFocused(false)">' +
            '<md-button md-no-ink class="md-datepicker-triangle-button md-icon-button" ' +
                'ng-click="ctrl.openCalendarPane($event)" ' +
                'aria-label="{{::ctrl.dateLocale.msgOpenCalendar}}">' +
              '

' +
            '</md-button>' +
          '
' +

          // This pane will be detached from here and re-attached to the document body.
          '
' +
            '

' +
            '
' +
              '<md-calendar role="dialog" aria-label="{{::ctrl.dateLocale.msgCalendar}}" ' +
                  'ng-model="ctrl.date" ng-if="ctrl.isCalendarOpen"></md-calendar>' +
            '
' +
          '
',
      require: ['ngModel', 'mdDatepicker'],
      scope: {
        placeholder: '@mdPlaceholder'
      },
      controller: DatePickerCtrl,
      controllerAs: 'ctrl',
      bindToController: true,
      link: function(scope, element, attr, controllers) {
        var ngModelCtrl = controllers[0];
        var mdDatePickerCtrl = controllers[1];

        mdDatePickerCtrl.configureNgModel(ngModelCtrl);
      }
    };
  }

  /** Additional offset for the input's `size` attribute, which is updated based on its content. */
  var EXTRA_INPUT_SIZE = 3;

  /** Class applied to the container if the date is invalid. */
  var INVALID_CLASS = 'md-datepicker-invalid';

  /** Default time in ms to debounce input event by. */
  var DEFAULT_DEBOUNCE_INTERVAL = 500;

  /**
   * Controller for md-datepicker.
   *
   * ngInject @constructor
   */
  function DatePickerCtrl($scope, $element, $attrs, $compile, $timeout, $mdConstant, $mdTheming,
      $mdUtil, $mdDateLocale, $$mdDateUtil, $$rAF) {
    /** @final */
    this.$compile = $compile;

    /** @final */
    this.$timeout = $timeout;

    /** @final */
    this.dateLocale = $mdDateLocale;

    /** @final */
    this.dateUtil = $$mdDateUtil;

    /** @final */
    this.$mdConstant = $mdConstant;

    /* @final */
    this.$mdUtil = $mdUtil;

    /** @final */
    this.$$rAF = $$rAF;

    /** @type {!angular.NgModelController} */
    this.ngModelCtrl = null;

    /** @type {HTMLInputElement} */
    this.inputElement = $element[0].querySelector('input');

    /** @type {HTMLElement} */
    this.inputContainer = $element[0].querySelector('.md-datepicker-input-container');

    /** @type {HTMLElement} Floating calendar pane. */
    this.calendarPane = $element[0].querySelector('.md-datepicker-calendar-pane');

    /** @type {HTMLElement} Calendar icon button. */
    this.calendarButton = $element[0].querySelector('.md-datepicker-button');

    /** @final {!angular.JQLite} */
    this.$element = $element;

    /** @final {!angular.Attributes} */
    this.$attrs = $attrs;

    /** @final {!angular.Scope} */
    this.$scope = $scope;

    /** @type {Date} */
    this.date = null;

    /** @type {boolean} */
    this.isFocused = false;

    /** @type {boolean} */
    this.isDisabled;
    this.setDisabled($element[0].disabled || angular.isString($attrs['disabled']));

    /** @type {boolean} Whether the date-picker's calendar pane is open. */
    this.isCalendarOpen = false;

    /**
     * Element from which the calendar pane was opened. Keep track of this so that we can return
     * focus to it when the pane is closed.
     * @type {HTMLElement}
     */
    this.calendarPaneOpenedFrom = null;

    this.calendarPane.id = 'md-date-pane' + $mdUtil.nextUid();

    $mdTheming($element);

    /** Pre-bound click handler is saved so that the event listener can be removed. */
    this.bodyClickHandler = angular.bind(this, this.handleBodyClick);

    // Unless the user specifies so, the datepicker should not be a tab stop.
    // This is necessary because ngAria might add a tabindex to anything with an ng-model
    // (based on whether or not the user has turned that particular feature on/off).
    if (!$attrs['tabindex']) {
      $element.attr('tabindex', '-1');
    }

    this.installPropertyInterceptors();
    this.attachChangeListeners();
    this.attachInterationListeners();

    var self = this;
    $scope.$on('$destroy', function() {
      self.detachCalendarPane();
    });
  }
  DatePickerCtrl.$inject = ["$scope", "$element", "$attrs", "$compile", "$timeout", "$mdConstant", "$mdTheming", "$mdUtil", "$mdDateLocale", "$$mdDateUtil", "$$rAF"];

  /**
   * Sets up the controller's reference to ngModelController.
   * @param {!angular.NgModelController} ngModelCtrl
   */
  DatePickerCtrl.prototype.configureNgModel = function(ngModelCtrl) {
    this.ngModelCtrl = ngModelCtrl;

    var self = this;
    ngModelCtrl.$render = function() {
      self.date = self.ngModelCtrl.$viewValue;
      self.inputElement.value = self.dateLocale.formatDate(self.date);
      self.resizeInputElement();
    };
  };

  /**
   * Attach event listeners for both the text input and the md-calendar.
   * Events are used instead of ng-model so that updates don't infinitely update the other
   * on a change. This should also be more performant than using a $watch.
   */
  DatePickerCtrl.prototype.attachChangeListeners = function() {
    var self = this;

    self.$scope.$on('md-calendar-change', function(event, date) {
      self.ngModelCtrl.$setViewValue(date);
      self.inputElement.value = self.dateLocale.formatDate(date);
      self.closeCalendarPane();
    });

    self.inputElement.addEventListener('input', angular.bind(self, self.resizeInputElement));
    // TODO(chenmike): Add ability for users to specify this interval.
    self.inputElement.addEventListener('input', self.$mdUtil.debounce(self.handleInputEvent,
        DEFAULT_DEBOUNCE_INTERVAL, self));
  };

  /** Attach event listeners for user interaction. */
  DatePickerCtrl.prototype.attachInterationListeners = function() {
    var self = this;
    var $scope = this.$scope;
    var keyCodes = this.$mdConstant.KEY_CODE;

    // Add event listener through angular so that we can triggerHandler in unit tests.
    angular.element(self.inputElement).on('keydown', function(event) {
      if (event.altKey && event.keyCode == keyCodes.DOWN_ARROW) {
        self.openCalendarPane(event);
        $scope.$digest();
      }
    });

    $scope.$on('md-calendar-close', function() {
      self.closeCalendarPane();
    });
  };

  /**
   * Capture properties set to the date-picker and imperitively handle internal changes.
   * This is done to avoid setting up additional $watches.
   */
  DatePickerCtrl.prototype.installPropertyInterceptors = function() {
    var self = this;

    if (this.$attrs['ngDisabled']) {
      // The expression is to be evaluated against the directive element's scope and not
      // the directive's isolate scope.
      this.$element.scope().$watch(this.$attrs['ngDisabled'], function(isDisabled) {
        self.setDisabled(isDisabled);
      });
    }

    Object.defineProperty(this, 'placeholder', {
      get: function() { return self.inputElement.placeholder; },
      set: function(value) { self.inputElement.placeholder = value; }
    });
  };

  /**
   * Sets whether the date-picker is disabled.
   * @param {boolean} isDisabled
   */
  DatePickerCtrl.prototype.setDisabled = function(isDisabled) {
    this.isDisabled = isDisabled;
    this.inputElement.disabled = isDisabled;
    this.calendarButton.disabled = isDisabled;
  };

  /**
   * Resizes the input element based on the size of its content.
   */
  DatePickerCtrl.prototype.resizeInputElement = function() {
    this.inputElement.size = this.inputElement.value.length + EXTRA_INPUT_SIZE;
  };

  /**
   * Sets the model value if the user input is a valid date.
   * Adds an invalid class to the input element if not.
   */
  DatePickerCtrl.prototype.handleInputEvent = function() {
    var inputString = this.inputElement.value;
    var parsedDate = this.dateLocale.parseDate(inputString);

    if (this.dateUtil.isValidDate(parsedDate) && this.dateLocale.isDateComplete(inputString)) {
      this.ngModelCtrl.$setViewValue(parsedDate);
      this.date = parsedDate;
      this.inputContainer.classList.remove(INVALID_CLASS);
      this.$scope.$apply();
    } else {
      // If there's an input string, it's an invalid date.
      this.inputContainer.classList.toggle(INVALID_CLASS, inputString);
    }
  };

  /** Position and attach the floating calendar to the document. */
  DatePickerCtrl.prototype.attachCalendarPane = function() {
    var calendarPane = this.calendarPane;
    this.$element.addClass('md-datepicker-open');

    var elementRect = this.inputContainer.getBoundingClientRect();
    var bodyRect = document.body.getBoundingClientRect();

    calendarPane.style.left = (elementRect.left - bodyRect.left) + 'px';
    calendarPane.style.top = (elementRect.top - bodyRect.top) + 'px';
    document.body.appendChild(this.calendarPane);

    // Add CSS class after one frame to trigger open animation.
    this.$$rAF(function() {
      calendarPane.classList.add('md-pane-open');
    });
  };

  /** Detach the floating calendar pane from the document. */
  DatePickerCtrl.prototype.detachCalendarPane = function() {
    this.$element.removeClass('md-datepicker-open');
    this.calendarPane.classList.remove('md-pane-open');

    // Use native DOM removal because we do not want any of the angular state of this element
    // to be disposed.
    this.calendarPane.parentNode.removeChild(this.calendarPane);
  };

  /**
   * Open the floating calendar pane.
   * @param {Event} event
   */
  DatePickerCtrl.prototype.openCalendarPane = function(event) {
    if (!this.isCalendarOpen && !this.isDisabled) {
      this.isCalendarOpen = true;
      this.calendarPaneOpenedFrom = event.target;
      this.attachCalendarPane();
      this.focusCalendar();

      // Because the calendar pane is attached directly to the body, it is possible that the
      // rest of the component (input, etc) is in a different scrolling container, such as
      // an md-content. This means that, if the container is scrolled, the pane would remain
      // stationary. To remedy this, we disable scrolling while the calendar pane is open, which
      // also matches the native behavior for things like `<select>` on Mac and Windows.
      this.$mdUtil.disableScrollAround(this.calendarPane);

      // Attach click listener inside of a timeout because, if this open call was triggered by a
      // click, we don't want it to be immediately propogated up to the body and handled.
      var self = this;
      this.$mdUtil.nextTick(function() {
        document.body.addEventListener('click', self.bodyClickHandler);
      }, false);
    }
  };

  /** Close the floating calendar pane. */
  DatePickerCtrl.prototype.closeCalendarPane = function() {
    this.isCalendarOpen = false;
    this.detachCalendarPane();
    this.calendarPaneOpenedFrom.focus();
    this.calendarPaneOpenedFrom = null;
    this.$mdUtil.enableScrolling();

    document.body.removeEventListener('click', this.bodyClickHandler);
  };

  /** Gets the controller instance for the calendar in the floating pane. */
  DatePickerCtrl.prototype.getCalendarCtrl = function() {
    return angular.element(this.calendarPane.querySelector('md-calendar')).controller('mdCalendar');
  };

  /** Focus the calendar in the floating pane. */
  DatePickerCtrl.prototype.focusCalendar = function() {
    // Use a timeout in order to allow the calendar to be rendered, as it is gated behind an ng-if.
    var self = this;
    this.$mdUtil.nextTick(function() {
      self.getCalendarCtrl().focus();
    }, false);
  };

  /**
   * Sets whether the input is currently focused.
   * @param {boolean} isFocused
   */
  DatePickerCtrl.prototype.setFocused = function(isFocused) {
    this.isFocused = isFocused;
  };

  /**
   * Handles a click on the document body when the floating calendar pane is open.
   * Closes the floating calendar pane if the click is not inside of it.
   * @param {MouseEvent} event
   */
  DatePickerCtrl.prototype.handleBodyClick = function(event) {
    if (this.isCalendarOpen) {
      // TODO(jelbourn): way want to also include the md-datepicker itself in this check.
      var isInCalendar = this.$mdUtil.getClosest(event.target, 'md-calendar');
      if (!isInCalendar) {
        this.closeCalendarPane();
      }

      this.$scope.$digest();
    }
  };
})();

(function() {
  'use strict';

  /**
   * Utility for performing date calculations to facilitate operation of the calendar and
   * datepicker.
   */
  angular.module('material.components.datepicker').factory('$$mdDateUtil', function() {
    return {
      getFirstDateOfMonth: getFirstDateOfMonth,
      getNumberOfDaysInMonth: getNumberOfDaysInMonth,
      getDateInNextMonth: getDateInNextMonth,
      getDateInPreviousMonth: getDateInPreviousMonth,
      isInNextMonth: isInNextMonth,
      isInPreviousMonth: isInPreviousMonth,
      getDateMidpoint: getDateMidpoint,
      isSameMonthAndYear: isSameMonthAndYear,
      getWeekOfMonth: getWeekOfMonth,
      incrementDays: incrementDays,
      incrementMonths: incrementMonths,
      getLastDateOfMonth: getLastDateOfMonth,
      isSameDay: isSameDay,
      getMonthDistance: getMonthDistance,
      isValidDate: isValidDate,
      createDateAtMidnight: createDateAtMidnight
    };

    /**
     * Gets the first day of the month for the given date's month.
     * @param {Date} date
     * @returns {Date}
     */
    function getFirstDateOfMonth(date) {
      return new Date(date.getFullYear(), date.getMonth(), 1);
    }

    /**
     * Gets the number of days in the month for the given date's month.
     * @param date
     * @returns {number}
     */
    function getNumberOfDaysInMonth(date) {
      return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
    }

    /**
     * Get an arbitrary date in the month after the given date's month.
     * @param date
     * @returns {Date}
     */
    function getDateInNextMonth(date) {
      return new Date(date.getFullYear(), date.getMonth() + 1, 1);
    }

    /**
     * Get an arbitrary date in the month before the given date's month.
     * @param date
     * @returns {Date}
     */
    function getDateInPreviousMonth(date) {
      return new Date(date.getFullYear(), date.getMonth() - 1, 1);
    }

    /**
     * Gets whether two dates have the same month and year.
     * @param {Date} d1
     * @param {Date} d2
     * @returns {boolean}
     */
    function isSameMonthAndYear(d1, d2) {
      return d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth();
    }

    /**
     * Gets whether two dates are the same day (not not necesarily the same time).
     * @param {Date} d1
     * @param {Date} d2
     * @returns {boolean}
     */
    function isSameDay(d1, d2) {
      return d1.getDate() == d2.getDate() && isSameMonthAndYear(d1, d2);
    }

    /**
     * Gets whether a date is in the month immediately after some date.
     * @param {Date} startDate The date from which to compare.
     * @param {Date} endDate The date to check.
     * @returns {boolean}
     */
    function isInNextMonth(startDate, endDate) {
      var nextMonth = getDateInNextMonth(startDate);
      return isSameMonthAndYear(nextMonth, endDate);
    }

    /**
     * Gets whether a date is in the month immediately before some date.
     * @param {Date} startDate The date from which to compare.
     * @param {Date} endDate The date to check.
     * @returns {boolean}
     */
    function isInPreviousMonth(startDate, endDate) {
      var previousMonth = getDateInPreviousMonth(startDate);
      return isSameMonthAndYear(endDate, previousMonth);
    }

    /**
     * Gets the midpoint between two dates.
     * @param {Date} d1
     * @param {Date} d2
     * @returns {Date}
     */
    function getDateMidpoint(d1, d2) {
      return createDateAtMidnight((d1.getTime() + d2.getTime()) / 2);
    }

    /**
     * Gets the week of the month that a given date occurs in.
     * @param {Date} date
     * @returns {number} Index of the week of the month (zero-based).
     */
    function getWeekOfMonth(date) {
      var firstDayOfMonth = getFirstDateOfMonth(date);
      return Math.floor((firstDayOfMonth.getDay() + date.getDate() - 1) / 7);
    }

    /**
     * Gets a new date incremented by the given number of days. Number of days can be negative.
     * @param {Date} date
     * @param {number} numberOfDays
     * @returns {Date}
     */
    function incrementDays(date, numberOfDays) {
      return new Date(date.getFullYear(), date.getMonth(), date.getDate() + numberOfDays);
    }

    /**
     * Gets a new date incremented by the given number of months. Number of months can be negative.
     * If the date of the given month does not match the target month, the date will be set to the
     * last day of the month.
     * @param {Date} date
     * @param {number} numberOfMonths
     * @returns {Date}
     */
    function incrementMonths(date, numberOfMonths) {
      // If the same date in the target month does not actually exist, the Date object will
      // automatically advance *another* month by the number of missing days.
      // For example, if you try to go from Jan. 30 to Feb. 30, you'll end up on March 2.
      // So, we check if the month overflowed and go to the last day of the target month instead.
      var dateInTargetMonth = new Date(date.getFullYear(), date.getMonth() + numberOfMonths, 1);
      var numberOfDaysInMonth = getNumberOfDaysInMonth(dateInTargetMonth);
      if (numberOfDaysInMonth < date.getDate()) {
        dateInTargetMonth.setDate(numberOfDaysInMonth);
      } else {
        dateInTargetMonth.setDate(date.getDate());
      }

      return dateInTargetMonth;
    }

    /**
     * Get the integer distance between two months. This *only* considers the month and year
     * portion of the Date instances.
     *
     * @param {Date} start
     * @param {Date} end
     * @returns {number} Number of months between `start` and `end`. If `end` is before `start`
     *     chronologically, this number will be negative.
     */
    function getMonthDistance(start, end) {
      return (12 * (end.getFullYear() - start.getFullYear())) + (end.getMonth() - start.getMonth());
    }

    /**
     * Gets the last day of the month for the given date.
     * @param {Date} date
     * @returns {Date}
     */
    function getLastDateOfMonth(date) {
      return new Date(date.getFullYear(), date.getMonth(), getNumberOfDaysInMonth(date));
    }

    /**
     * Checks whether a date is valid.
     * @param {Date} date
     * @return {boolean} Whether the date is a valid Date.
     */
    function isValidDate(date) {
      return date !== null && date.getTime && !isNaN(date.getTime());
    }

    /**
     * Creates a date with the time set to midnight.
     * Drop-in replacement for two forms of the Date constructor:
     * 1. No argument for Date representing now.
     * 2. Single-argument value representing number of seconds since Unix Epoch.
     * @param {number=} opt_value
     * @return {Date} New date with time set to midnight.
     */
    function createDateAtMidnight(opt_value) {
      var date;
      if (angular.isUndefined(opt_value)) {
        date = new Date();
      } else {
        date = new Date(opt_value);
      }
      date.setHours(0, 0, 0, 0);
      return date;
    }
  });
})();

ng.material.components.datepicker = angular.module("material.components.datepicker");


text:
package org.apache.nifi.flow.resource;

import java.io.File;

/**
 * This strategy allows to replace the already acquired external resource if the available is newer, based on the modification time.
 *
 * This strategy assumes that the external source maintains the modification time in a proper manner and the local files are
 * not modified by other parties.
 */
final class ReplaceWithNewerResolutionStrategy implements ExternalResourceConflictResolutionStrategy {

    @Override
    public boolean shouldBeFetched(final File targetDirectory, final ExternalResourceDescriptor available) {
        final File file = new File(targetDirectory, available.getLocation());
        return !file.exists() || file.lastModified() < available.getLastModified();
    }
}


text:
<readable><title>2798651021_2566f2a47e</title><content>
A guy sitting on the side of the road playing an accordion .
A man is playing the accordion in an empty park .
A man sits by a driveway in front of a white building playing an accordion .
A man with an accordion sitting alongside a path .
Older man playing accordion on a street .
</content></readable>


text:
;
var handled_errors = {
    'Code 100': '[Deleted Video]'
  , 'Code 150': '[Blocked Video: Contains copyrighted content]'
}

var fs   = require('fs')
  , ytdl = require('ytdl-core')
  , join = require('path').join
  , argv = require('minimist')(process.argv.slice(2))

var list_id  = argv._[0] || 'PLpRjkOHBe_TgmznCle__jWDhoV4aFgCjw'
  , protocol = 'https://'
  , host  = 'youtube.com'
  , path  = '/playlist?list='
  , url   = protocol + host + path + list_id
  , parse = require('./lib/parse-playlist')
  , cwd   = process.cwd()

function decode_title(title) {
  title = title
    .replace(/-/g, '_')
    .replace(/[^a-zA-Z0-9 _]/g, '')
    .replace(/\s+/g, '-')
    .replace(/-+_-+/g, '_')

  return title
}

console.log('\nPlease wait for all downloads to complete..')
console.log('(can take several minutes, depending on connection speed)\n')

parse(url, function(err, id) {
  if (err) console.error(err.message, err.stack)

  var title = id.substr(9) +'.mp3'
  var target = protocol + host + id
  console.log('url: '+ target)

  ytdl(target, { filter: 'audioonly' })
    .on('info', function(data) {
      title = decode_title(data.title)
      console.log(data.video_id +' >> '+ title)
      fs.rename(join(cwd, data.video_id) +'.mp3', join(cwd, title) +'.mp3', function(err, data) {
        if (err) console.error(err.message, err.stack)
      })
    })
    .on('error', function(err) {
      if (! handled_errors[err.message.substr(0, 8)]) {
        console.error(err.message, err.stack)
        process.exit(1)
      } else {
        var k = err.message.substr(0, 8)
        var v = handled_errors[k]
        console.log('Warning: '+ k +' '+ v)
      }
    })
    .pipe(fs.createWriteStream(join(cwd, title)))
})


text:
#include <ieeefp.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_errno.h>

/* This is a copy of fp-netbsd.c, modified for openbsd by Toby White
   --- Brian Gough */

int
gsl_ieee_set_mode (int precision, int rounding, int exception_mask)
{
  fp_except mode = 0;
  fp_rnd    rnd  = 0;

  switch (precision)
    {
    case GSL_IEEE_SINGLE_PRECISION:
      GSL_ERROR ("OpenBSD only supports default precision rounding",
                 GSL_EUNSUP);
      break;
    case GSL_IEEE_DOUBLE_PRECISION:
      GSL_ERROR ("OpenBSD only supports default precision rounding",
                 GSL_EUNSUP);
      break;
    case GSL_IEEE_EXTENDED_PRECISION:
      GSL_ERROR ("OpenBSD only supports default precision rounding",
                 GSL_EUNSUP);
      break;
    }

  switch (rounding)
    {
    case GSL_IEEE_ROUND_TO_NEAREST:
      rnd = FP_RN;
      fpsetround (rnd);
      break;
    case GSL_IEEE_ROUND_DOWN:
      rnd = FP_RM;
      fpsetround (rnd);
      break;
    case GSL_IEEE_ROUND_UP:
      rnd = FP_RP;
      fpsetround (rnd);
      break;
    case GSL_IEEE_ROUND_TO_ZERO:
      rnd = FP_RZ;
      fpsetround (rnd);
      break;
    default:
      rnd = FP_RN;
      fpsetround (rnd);
    }

/* Turn on all available exceptions apart from 'inexact'.
   Denormalized operand exception not available on all platforms. */

  mode = FP_X_INV | FP_X_DZ | FP_X_OFL | FP_X_UFL;
#ifdef FP_X_DNML
  mode = mode | FP_X_DNML;
#endif

  if (exception_mask & GSL_IEEE_MASK_INVALID)
    mode &= ~ FP_X_INV;

  if (exception_mask & GSL_IEEE_MASK_DENORMALIZED)
    {
#ifdef FP_X_DNML
      mode &= ~ FP_X_DNML;
#endif
    }
  else
    {
#ifndef FP_X_DNML
      GSL_ERROR ("OpenBSD does not support the denormalized operand exception on this platform. "
                 "Use 'mask-denormalized' to work around this.",
                 GSL_EUNSUP);
#endif
    }

  if (exception_mask & GSL_IEEE_MASK_DIVISION_BY_ZERO)
    mode &= ~ FP_X_DZ;

  if (exception_mask & GSL_IEEE_MASK_OVERFLOW)
    mode &= ~ FP_X_OFL;

  if (exception_mask & GSL_IEEE_MASK_UNDERFLOW)
    mode &=  ~ FP_X_UFL;

  if (exception_mask & GSL_IEEE_TRAP_INEXACT)
    {
      mode |= FP_X_IMP;
    }
  else
    {
      mode &= ~ FP_X_IMP;
    }

  fpsetmask (mode);

  return GSL_SUCCESS;

}


text:
@(scenario: models.Scenario)

@main("OrganiCity Scenarios") {

				Scenario @scenario.title was successfully created.

}


text:
package org.gradle.launcher.bootstrap;

import org.gradle.api.internal.ClassPathRegistry;
import org.gradle.api.internal.DefaultClassPathProvider;
import org.gradle.api.internal.DefaultClassPathRegistry;
import org.gradle.api.internal.classpath.DefaultModuleRegistry;
import org.gradle.internal.classloader.ClassLoaderFactory;
import org.gradle.internal.classloader.ClassLoaderUtils;
import org.gradle.internal.classloader.DefaultClassLoaderFactory;
import org.gradle.internal.classloader.VisitableURLClassLoader;
import org.gradle.internal.classpath.ClassPath;
import org.gradle.internal.installation.CurrentGradleInstallation;

import java.lang.reflect.Method;

public class ProcessBootstrap {
    /**
     * Sets up the ClassLoader structure for the given class, creates an instance and invokes {@link EntryPoint#run(String[])} on it.
     */
    public void run(String mainClassName, String[] args) {
        try {
            runNoExit(mainClassName, args);
            System.exit(0);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.exit(1);
        }
    }

    private void runNoExit(String mainClassName, String[] args) throws Exception {
        ClassPathRegistry classPathRegistry = new DefaultClassPathRegistry(new DefaultClassPathProvider(new DefaultModuleRegistry(CurrentGradleInstallation.get())));
        ClassLoaderFactory classLoaderFactory = new DefaultClassLoaderFactory();
        ClassPath antClasspath = classPathRegistry.getClassPath("ANT");
        ClassPath runtimeClasspath = classPathRegistry.getClassPath("GRADLE_RUNTIME");
        ClassLoader antClassLoader = classLoaderFactory.createIsolatedClassLoader(antClasspath);
        ClassLoader runtimeClassLoader = new VisitableURLClassLoader(antClassLoader, runtimeClasspath);

        ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(runtimeClassLoader);

        try {
            Class<?> mainClass = runtimeClassLoader.loadClass(mainClassName);
            Object entryPoint = mainClass.newInstance();
            Method mainMethod = mainClass.getMethod("run", String[].class);
            mainMethod.invoke(entryPoint, new Object[]{args});
        } finally {
            Thread.currentThread().setContextClassLoader(oldClassLoader);

            ClassLoaderUtils.tryClose(runtimeClassLoader);
            ClassLoaderUtils.tryClose(antClassLoader);
        }
    }
}