<!doctype html>

  <meta charset="utf-8">

  <title>Ghost</title>

  <meta name="description" content="">
  <meta name="author" content="">

  <meta name="HandheldFriendly" content="True">
  <meta name="MobileOptimized" content="320">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <link rel="stylesheet" href="../core/admin/assets/css/screen.css">

  <link rel="shortcut icon" href="/favicon.ico">
  <link rel="logo" type="image/svg" href="assets/images/logo.svg"/>
  <meta http-equiv="cleartype" content="on">

    Ghost

Dashboard

Blog

Pages

Settings

                    John O'Nolan

 Your Profile

 Help / Support

 Keyboard Shortcuts

 Sign Out

      <hgroup>

Grouped Heading 1

Grouped Heading 2 and linked text too.

      </hgroup>

navigation item #1

navigation item #2

navigation item #3

UI Colours

#aaa9a2 Text

#c0bfb6 Icons

#edece4 Dividers

#242628 BG

#35393b Dividers

#7d878a Text/Icons

#e2edf2 Highlight

Actions

#4090ba Save

#e25440 Delete/Publish

#f59b0d Notifcation

            <hgroup>

An Article Headline

A longer article subheading that explains the article in a sentence

            </hgroup>
            <time datetime="2011-09-06">Tuesday, September 6th 2011</time>

Pellentesque habitant morbi tristique senectus  et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

An aside to illustrate a key point in the article, maybe with a list

Lorem ipsum dolor

Consectetur adipisicing elit

Eiusmod tempor incididunt

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

H1 Header + Link

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

H2 Header + Link

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

H3 Header + Link

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

H4 Header + Link

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

H5 Header + Link

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

H6 Header + Link

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Text-level semantics

            The a element example

            The abbr element and abbr element with title examples

            The b element example

            The cite element example

            The code element example

            The del element example

            The <dfn>dfn element</dfn> and <dfn title="Title text">dfn element with title</dfn> examples

            The em element example

            The img element  example

            The ins element example

            The Ctrl + R kbd example

            The mark element example

            The <q>q element <q>inside</q> a q element</q> example

            The <s>s element</s> example

            The <samp>samp element</samp> example

            The small element example

            The span element example

            The strong element example

            The sub element example

            The sup element example

            The <var>var element</var> example

            The u element example

Blockquote

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Ut a est eget ligula molestie gravida. Curabitur massa.

            Someone famous, in Source Title - The New Yorker, September 2009

Pre

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et me.

<html>
    <head>
    </head>
    <body>
        <div class="main"> <div>
    </body>
</html>

List Types

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.

Ordered Lists

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. This bullet tests content going onto two lines

Un-ordered Lists

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. This bullet tests content going onto two lines

Lists Inside Lists Inside Lists

Morbi in sem quis dui placerat ornare. Pellentesque odio nisi, euismod in, pharetra a, ultricies in, diam. Sed arcu. Cras consequat.

               Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus.

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

                        Aliquam tincidunt mauris eu risus:

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

Aliquam tincidunt mauris eu risus.

Vestibulum auctor dapibus neque.

Vestibulum auctor dapibus neque.

Phasellus ultrices nulla quis nibh. Quisque a lectus. Donec consectetuer ligula vulputate sem tristique cursus. Nam nulla quam, gravida non, commodo a, sodales sit amet, nisi.

Pellentesque fermentum dolor. Aliquam quam lectus, facilisis auctor, ultrices ut, elementum vulputate, nunc.

Definition Lists

Definition Title

Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco.

Lorem ipsum dolor sit amet

Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris commodo consequat.

Embedded content

audio

        <audio controls></audio>
        <audio></audio>

img

figure

Figcaption content

svg

        <svg width="100px" height="100px">
            <circle cx="100" cy="100" r="100" fill="#ff0000" />
        </svg>

video

        <video controls></video>
        <video></video>

Form

        <form>
            <fieldset>
                <legend>Inputs as descendents of labels (form legend)</legend>

                    Text input
                    <input type="text" value="default value">

                    Email input
                    <input type="email">

                    Search input
                    <input type="search">

                    Tel input
                    <input type="tel">

                    URL input
                    <input type="url" placeholder="http://">

                    Password input
                    <input type="password" value="password">

                    File input
                    <input type="file">

                    Radio input
                    <input type="radio" name="rad">

                    Checkbox input
                    <input type="checkbox">

                    <input type="radio" name="rad"> Radio input

                    <input type="checkbox"> Checkbox input

                    Select field
                    <select>
                        <option>Option 01</option>
                        <option>Option 02</option>
                    </select>

                    Textarea
                    <textarea cols="30" rows="5" >Textarea text</textarea>

            </fieldset>

            <fieldset>
                <legend>Clickable inputs and buttons</legend>
                <input type="image" src="http://placekitten.com/90/24" alt="Image (input)">
                <input type="reset" value="Reset (input)">
                <input type="button" value="Button (input)">
                <input type="submit" value="Submit (input)">
                <button type="reset">Reset (button)</button>
                <button type="button">Button (button)</button>
                <button type="submit">Submit (button)</button>
            </fieldset>

            <fieldset id="boxsize">
                <legend>box-sizing tests</legend>

<input type="text" value="text">

<input type="email" value="email">

<input type="search" value="search">

<input type="url" value="http://example.com">

<input type="password" value="password">

<input type="color" value="#000000">

<input type="number" value="5">

<input type="range" value="10">

<input type="date" value="1970-01-01">

<input type="month" value="1970-01">

<input type="week" value="1970-W01">

<input type="time" value="18:23">

<input type="datetime" value="1970-01-01T00:00:00Z">

<input type="datetime-local" value="1970-01-01T00:00">

<input type="radio">

<input type="checkbox">

<select><option>Option 01</option><option>Option 02</option></select>

<textarea cols="30" rows="5" >Textarea text</textarea>

<input type="image" src="http://placekitten.com/90/24" alt="Image (input)">

<input type="reset" value="Reset (input)">

<input type="button" value="Button (input)">

<input type="submit" value="Submit (input)">

<button type="reset">Reset (button)</button>

<button type="button">Button (button)</button>

<button type="submit">Submit (button)</button>

            </fieldset>
        </form>

                Button
                class=""
                Description

                <button type="button" class="button">Default</button>
                button
                Standard boring default button.

                <button type="button" class="button-save">Save This</button>
                button-save
                Stands out, identifies a "save" or "confirm" or "continue" action.

                <button type="button" class="button-add">Add New</button>
                button-new
                Used to indicate an action that will create something new.

                <button type="button" class="button-delete">Delete</button>
                button-delete
                Strong warning - Used to indicate an action that will delete something.

                <button type="button" class="button-alt">Alternative</button>
                button-alt
                Alternate dark gray button, not tied to a semantic action or use.

                <button type="button" class="button-link">Link</button>
                button-link
                Deemphasize a button by making it look like a link, maintaining same behavior.

                        <button type="button" class="button">Split</button>
                        Options

                splitbutton
                Standard boring default button + options.

                        <button type="button" class="button">Split Up</button>
                        Options

                splitbutton up
                The dropdown arrow points up on hover.

                        <button type="button" class="button-save">Split Save</button>
                        Options

                splitbutton-save
                Save button + options.

                        <button type="button" class="button-add">Split Add</button>
                        Options

                splitbutton-add
                Add button + options.

                        <button type="button" class="button-delete">Split Delete</button>
                        Options

                splitbutton-delete
                Delete button + options.

                        <button type="button" class="button-alt">Split Alt</button>
                        Options

                splitbutton-alt
                Alt button + options.

                    Name
                    Phone Number
                    Gender

                    Name
                    Phone Number
                    Gender

                    John Smith
                    555 - 3246591
                    Male

                    Lucy Carter
                    555 - 6327445
                    Female

                    Simon Fletcher
                    555 - 2335213
                    Male

                    Jeremy Edwards
                    555 - 4297642
                    Male

                    Sarah Jones
                    555 - 6678903
                    Female

This page is a Ghost custom template for checking CSS consistency.


keep keep keep keep replace keep keep keep keep keep

// # Temporary Admin UI

/*global window, document, $ */
(function () {
    "use strict";

    // UTILS

    /**
     * Allows to check contents of each element exactly
     * @param obj
     * @param index
     * @param meta
     * @param stack
     * @returns {boolean}
     */
    $.expr[":"].containsExact = function (obj, index, meta, stack) {
        return (obj.textContent || obj.innerText || $(obj).text() || "") === meta[3];
    };

    // Called on Window resize
    $(window).resize(function () {

        var loginContainer = $(".js-login-container"),
            marginTop = Math.floor((loginContainer.parent().height() - loginContainer.height()) / 2) - 15;
        loginContainer.css('margin-top', marginTop).delay(250).fadeIn(750);

    });

    // Allow notifications to be dismissed
    $(document).on('click', '.js-notification .close',  function () {
        $(this).parent().fadeOut(200,  function () { $(this).remove(); });
    });

    $(document).ready(function () {

        // ## Set interactions for all menus
        // This finds all visible '.overlay' elements and hides them upon clicking away from the element itself.
        $("body").on('click', function (event) {
            var $target = $(event.target);
            if (!$target.parents().is(".overlay:visible") && !$target.is(".overlay:visible")) {
                $("body").find(".overlay:visible").fadeOut();
            }
        });

        // LOGIN SCREEN

        $(window).resize();

        // EDITOR / NOTIFICATIONS

        $('.entry-content header, .entry-preview header').on('click', function () {
            $('.entry-content, .entry-preview').removeClass('active');
            $(this).closest('section').addClass('active');
        });

        $('.entry-title .icon-fullscreen').on('click', function (e) {
            e.preventDefault();
            $('body').toggleClass('fullscreen');
        });

        $('.options.up').on('click', function (e) {
            e.stopPropagation();
            $(this).next("ul").fadeToggle(200);
        });

    });

}());


/*
 * These styles control elements specific to the post editor screen
 * used for publishing content with Ghost.
 *
 * Table of Contents:
 *
 * Editor / Preview
 * Post Preview Content
 * Full Screen Mode
 * Publish Bar
 */

/* =============================================================================
   Editor / Preview
   ============================================================================= */

.editor {

	// The main post title
	.entry-title {
		@extend %box;
		height: 35px;
		padding: 10px 15px;
		margin-bottom: 15px;
		position:relative;

		@include breakpoint($mobile) {
			box-shadow: none;
		}

		input {
			border: 0;
			margin: 0;
			padding:0;
			font-size: 2em;
			font-weight: 300;
			width: 100%;
			&:focus {
				outline: 0;
			}
		}

	}

	// The two content panel wrappers, positioned left/right
	.entry-markdown { left:0; border-right:$lightbrown 2px solid; }
	.entry-preview { right:0; border-left:$lightbrown 2px solid; }

	// The visual styles for both panels
	.entry-markdown, .entry-preview {
		@include box-sizing(border-box);
		width: 50%;
		padding: 15px;
		position: absolute;
		bottom:40px; // height of the publish bar
		top:69px; // height of the post title + margin
		background: #fff;
		box-shadow: $shadow;

		@include breakpoint($mobile) {
			box-shadow: none;
		}

		// Convert all content areas to small boxes
		@include breakpoint($netbook) {
			top:109px;
			left:0;
			right:0;
			width:100%;
			border:none;
			z-index:100;
			min-height:380px;
			.markdown, .entry-preview-content {
				height:50px;
				overflow: hidden;
			}
		}

		.floatingheader {

			// Turn headers into tabs which act as links
			// both headers set to grey/inactive colour
			@include breakpoint($netbook) {
			    cursor:pointer;
			    width:50%;
			    border-right:$lightbrown 2px solid;
			    color:#fff;
			    font-weight: normal;
			    background:$brown;
			    position:absolute;
			    top:-40px;
			    left:0;
			    box-shadow: rgba(0,0,0,0.1) 0 -2px 3px inset;

			    a {
			        color:#fff;
			    }
			}

			a {
			    color: $brown;
			}

			.markdown-help {
			    @include icon($i-question, '', lighten($brown, 15%));
			    float:right;

			    &:hover {
			        @include icon($i-question, '', $brown)
			    }
			}

			.entry-word-count {
			    float:right;
			}

		}

		// Give the tab with the .active class the highest z-index
		&.active {
			z-index: 200;
		}

		// Restore the normal height of the .active tab (inactive tab stays small, hidden behind)
		&.active .markdown,
		&.active .entry-preview-content {
			height:auto;
			overflow: auto;
		}

		// Restore the white bg of the currently .active tab, remove hand cursor from currently active tab
		&.active header {
			@include breakpoint($netbook) {
				cursor:auto;
				color: $brown;
				background:#fff;
				box-shadow: none;
				a {
					color: $brown;
				}
			}
		}

		// Hide markdown icon + wordcount when we hit mobile
		@include breakpoint($mobile) {
			.markdown-help,
			.entry-word-count {
				display: none;
			}
		}

	}

	.entry-markdown-content {

		textarea {
			border: 0;
			width: 100%;
			height: 100%;
			max-width: 100%;
			margin: 0;
			padding: 0;
			position:absolute;
			top: 0;
			right:0;
			bottom:0;
			left:0;

			&:focus {
				outline: 0;
			}
		}

		.CodeMirror {
			height: auto;
			position:absolute;
			top:0;
			left:0;
			right:0;
			bottom:0;
			font-family: $font-family-mono;
			font-size:1.1em;
			line-height:1.2em;
			color: lighten($darkgrey, 30%);

			.CodeMirror-focused,
			.CodeMirror-selected {
			    color: $darkgrey;
			    background: lighten($blue, 20%);
			    text-shadow: none;
			}

			::selection {
			    color: $darkgrey;
			    background: lighten($blue, 20%);
			    text-shadow: none;
			}
		}

		.CodeMirror-lines {
			padding: 65px 0 40px 0; /* Vertical padding around content */
			@include breakpoint($netbook) {padding-top: 25px;}
			@include breakpoint($mobile) {padding: 15px 0;}
		}
		.CodeMirror pre {
		 	padding: 0 40px; /* Horizontal padding of content */
		 	@include breakpoint($mobile) {padding: 0 15px;}
		}

		.cm-header {
			color:#000;
			font-size: 1.4em;
			line-height: 1.4em;
		}

		.cm-string,
		.cm-link,
		.cm-comment,
		.cm-quote {color:#000;}

	}

	.entry-preview {
		// Align the tab of entry-preview on the right
		.floatingheader {
			@include breakpoint($netbook) {
				right:0;
				left:auto;
				border-right:none;
				border-left:$lightbrown 2px solid;
			}
		}

		.entry-preview-content {
			position:absolute;
			top:0;
			right:0;
			bottom:0;
			left:0;
			padding: 60px 40px 40px 40px;
			overflow: auto;

			// Tweak padding for smaller screens
			@include breakpoint($netbook) {padding-top: 20px;}
			@include breakpoint($mobile) {padding: 15px;}
		}

	}

	// Special case, when scrolling, add shadows to content headers.
	.scrolling {

		.floatingheader {
			@include breakpoint($netbook) {
	            box-shadow: none;
	        }

	        &::before,
	        &::after {
	        	@include breakpoint($netbook) {display:none;}
	        }
		}
		.CodeMirror-scroll,
		.entry-preview-content {
			@include breakpoint($netbook) {
				box-shadow: 0 5px 5px rgba(0,0,0,0.05) inset;
			}
		}
	}

}//.editor

/* =============================================================================
   Post Preview Content
   ============================================================================= */

// The styles for the actual content inside the preview
// TODO: These should just be defaults, overridden by editor.hbs in theme dir
.entry-preview-content,
.content-preview-content {
	font-size:1.15em;
	line-height: 1.5em;

	a {
		color: $blue;
		text-decoration: underline;
	}
	p {
		text-indent: 0;
		margin: 1.2em 0 1.6em;
		&:first-child {
			margin-top:0;
		}
	}
	h1 {
		font-size: 3em;
	}
	h2 {
		font-size: 2.2em;
	}
	h3 {
		font-size: 1.8em;
	}
	.btn {
		text-decoration: none;
		color: $grey;
	}
	.img-placeholder {
		border: 5px dashed $grey;
		height: 100px;
		position: relative;
		span {
			display: block;
			height: 30px;
			position: absolute;
			margin-top: -15px;
			top: 50%;
			width: 100%;
			text-align: center;
		}
	}

	img {
		width: 100%;
		height: auto;
	}
}

/* =============================================================================
   Full Screen Mode
   ============================================================================= */

body.zen {
	background: lighten($lightbrown, 3%);
	#usermenu {display:none;}
	#global-header, #publish-bar {
		opacity: 0;
		height:0;
		overflow: hidden;
		@include transition(all 0.5s ease-out);
	}

	main {top: 15px;@include transition(all 0.5s ease-out);}
	.entry-markdown, .entry-preview {bottom:0;@include transition(all 0.5s ease-out);}

}

/* =============================================================================
   Publish Bar
   ============================================================================= */

#publish-bar {
	@include box-sizing(border-box);
	height:40px;
	padding: 0;
	color: $midgrey;
	background: darken($darkgrey, 4%);
	position: fixed;
	bottom: 0;
	left:0;
	right:0;
	z-index:900;
	box-shadow: 0 -2px 8px rgba(0,0,0,0.2);

	@include breakpoint($netbook) {font-weight:normal;}

	button {
		min-height: 30px;
		height: 30px;
		line-height: 12px;
		padding: 0 10px;
		margin-top: 5px;
		border-top:rgba(255,255,255,0.4) 1px solid;
	}

	.button-link { border-top: none; }

	.options {
		width:30px;
		min-height: 30px;
		height: 30px;
		margin-top:5px;
		box-shadow: rgba(255,255,255,0.4) 0 1px 0 inset;

		&.up:hover {
            @include icon($i-chevron-down) {
                margin-top:-5px;
                @include transform(rotate(540deg));
                @include transition(transform 0.6s ease);
            };
        }
	}

	.splitbutton-save{
		.button-save{
			@include transition(width 0.25s ease);
		}

		.editor-options{
			@extend %menu;
	    	@extend %menu-right;
	    	bottom: 140%;
	    	right: -3%;

	    	a{
	    		font-size: 14px;
	    	}
		}
	}
}

#entry-categories {
	position: absolute;
	top:0;
	left:0;
	right: 165px;
	bottom:0;
	text-transform: none;
	padding: 10px 0 0 0;
}

.category-label {
	display: block;
	float:left;
	@include icon($i-tag);
	padding:1px 8px 0 8px;
	@include transition;

	&:hover {
		cursor:pointer;
		color: $lightgrey;
	}
}

.category-input {
	display: inline-block;
	color: $lightgrey;
	font-weight: 300;
	background: transparent;
	border: none;

	&:focus {outline:none;}
}

.category {
	@include icon-after($i-x, 8px, $darkgrey) {
		margin-left:4px;
		vertical-align:5%;
		text-shadow: rgba(255,255,255,0.15) 0 1px 0;
		@include transition;
	}
	display: block;
	float: left;
	margin-right: 5px;
	padding: 0 5px;
	color: $lightgrey;
	background: lighten($grey, 15%);
	border-radius: $rounded;
	box-shadow:
		rgba(255,255,255,0.2) 0 1px 0 inset,
		#000 0 1px 3px;

	&:hover {
		cursor: pointer;
		@include icon-after($i-x, 8px, $lightgrey) {text-shadow: none;}

	}
}

.suggestions {
  @extend %menu;
  bottom: 100%;

  li.selected{
    background: $blue;
    box-shadow:
        rgba(255,255,255,0.2) 0 1px 0 inset,
        rgba(0,0,0,0.5) 0 1px 5px;
  }

  mark{
    background: none;
    color: white;
    font-weight: bold;
  }
}

#entry-settings {
	@include icon($i-settings, 1.1em){vertical-align:0;};
	@include box-sizing(border-box);
	display:inline-block;
	padding: 0 10px;
	line-height: 1.8em;
	color: $midgrey;
	@include transition;

	&:hover {
		color: $lightgrey;
	}
}

#entry-settings-menu {
	position: absolute;
    bottom:50px;
    right:-5px;
}

#entry-actions {
	margin-right:6px;
	position: relative;
}

#entry-actions-menu {
    position: absolute;
    bottom:50px;
    right:-5px;
}


keep keep keep keep keep replace keep keep keep

{{!< default}}

Settings

General

Content

General

                <button class="button-save">Save</button>

            {{#with settings}}
            <form id="settings-general">
                <fieldset>

                        Blog Title
                        <input id="blog-title" type="text" value="{{title}}" />

How your blog name appears on the site

                        Blog Logo

Display a logo on your site in place of blog title

                        Blog Icon

The icon for your blog, used in your browser tab and elsewhere

                        Email Address
                        <input id="email-address" type="text" value="{{email}}" />

Address to use for admin notifications

                            <input type="checkbox" value="1" /> Show my email address on my public profile

                        URL Structure
                        <select id="url-structure" name="general[urlstructure]">
                            <option value="post-name">Simple Post Name</option>
                            <option value="date-based">Date Based</option>
                            <option value="number based">Number Based</option>
                            <option value="custom">Custom...</option>
                        </select>

                </fieldset>

                <fieldset>

                        Time Zone
                        <select id="url-structure" name="general[timezone]">
                            <option value="1">Vienna (UTC+1)</option>
                        </select>

                </fieldset>

            {{/with}}
            </form>

Content

                <button class="button-save">Save</button>

            <form id="settings-general">
                <fieldset>

                        Typography
                        <select id="url-structure" name="general[urlstructure]">
                            <option value="post-name">Lato (Light)</option>
                        </select>

Sexy sans-serif font that will make your toes tickle.

                            <input type="checkbox" value="1" /> Load fonts directly from Google

                        Post Options

                            <input type="checkbox" value="1" /> Display Post Meta

Post Author / Date / Views

                            <input type="checkbox" value="1" /> Show Author Box After Post

                            <input type="checkbox" value="1" /> Enable Comments

                </fieldset>

                <fieldset>

                        SEO Title Pattern
                        <input id="seo-title" type="text" value="[Post Name] - [Site Title]" />

The pattern used to display your title tags

                        SEO Description Pattern
                        <input id="seo-description" type="text" value="Auto" />

The pattern used to display your meta descriptions

                        Google+

                            <input type="checkbox" value="1" /> Connect to author profile on Google

                        Home Page Description
                        <textarea id="blog-description"></textarea>

Display a logo on your site in place of blog title

                </fieldset>

            </form>

Users

                <button class="button-add">New User</button>

Invited Users

Some Name

                            Invitation Sent: 7 hours ago

Active Users

                    <form class="user-search">

                            <input class="user-search-input" type="text"/>

                    </form>

Some Name

                            Last Seen: 7 hours ago

                        Admin

Some Name

                            Last Seen: 2 days ago

                        Editor

Appearance

Raw json be here

Active theme: {{json settings.activeTheme}}

Available themes: {{json availableThemes}}

Available plugins: {{json availablePlugins}}


{{!< default}}
{{! TODO: Add "scrolling" class only when one of the panels is scrolled down by 5px or more }}

        <input type="text" id="entry-title"
               placeholder="{{e "editor.entry_title.placeholder" "The Post Title Gets Inserted Up Here"}}"
               value="{{title}}" tabindex="1">

        {{e "editor.headers.markdown.label" "Markdown"}}
        What is Markdown?

        <textarea id="entry-markdown"></textarea>

{{!.entry-markdown}}

        Preview 0 words

            {{!The content gets inserted in here, bitches!}}

{{!.entry-preview}}

            Categories

            <input type="hidden" class="category-holder" id="category-holder">
            <input class="category-input" id="categories" type="text"
                   data-populate-hidden="#category-holder" data-input-behaviour="tag" data-populate=".categories" />

                <button type="button" class="button-save js-post-button"></button>
                Options

Publish Now

Add to Queue

Publish on...

Save Draft


/*globals describe, beforeEach, it*/
var should = require('should'),
    when = require('when'),
    sinon = require('sinon'),
    path = require('path'),
    _ = require('underscore'),
    Ghost = require('../../ghost');

describe("Ghost API", function () {
    var testTemplatePath = 'core/test/ghost/fixtures/',
        ghost;

    beforeEach(function () {
        ghost = new Ghost();
    });

    it("is a singleton", function () {
        var logStub = sinon.stub(console, "log"),
            ghost1 = new Ghost(),
            ghost2 = new Ghost();

        should.strictEqual(ghost1, ghost2);
        logStub.restore();
    });

    it("uses init() to initialize", function (done) {
        var fakeDataProvider = {
                init: function () {
                    return when.resolve();
                }
            },
            dataProviderInitSpy = sinon.spy(fakeDataProvider, "init"),
            oldDataProvider = ghost.dataProvider;

        ghost.dataProvider = fakeDataProvider;

        should.not.exist(ghost.settings());

        ghost.init().then(function () {

            should.exist(ghost.settings());

            dataProviderInitSpy.called.should.equal(true);

            ghost.dataProvider = oldDataProvider;

            done();
        }).then(null, done);

    });

    it("can register filters with specific priority", function () {
        var filterName = 'test',
            filterPriority = 9,
            testFilterHandler = sinon.spy();

        ghost.registerFilter(filterName, filterPriority, testFilterHandler);

        should.exist(ghost.filterCallbacks[filterName]);
        should.exist(ghost.filterCallbacks[filterName][filterPriority]);

        ghost.filterCallbacks[filterName][filterPriority].should.include(testFilterHandler);
    });

    it("can register filters with default priority", function () {
        var filterName = 'test',
            defaultPriority = 5,
            testFilterHandler = sinon.spy();

        ghost.registerFilter(filterName, testFilterHandler);

        should.exist(ghost.filterCallbacks[filterName]);
        should.exist(ghost.filterCallbacks[filterName][defaultPriority]);

        ghost.filterCallbacks[filterName][defaultPriority].should.include(testFilterHandler);
    });

    it("executes filters in priority order", function (done) {
        var filterName = 'testpriority',
            testFilterHandler1 = sinon.spy(),
            testFilterHandler2 = sinon.spy(),
            testFilterHandler3 = sinon.spy();

        ghost.registerFilter(filterName, 0, testFilterHandler1);
        ghost.registerFilter(filterName, 2, testFilterHandler2);
        ghost.registerFilter(filterName, 9, testFilterHandler3);

        ghost.doFilter(filterName, null, function () {

            testFilterHandler1.calledBefore(testFilterHandler2).should.equal(true);
            testFilterHandler2.calledBefore(testFilterHandler3).should.equal(true);

            testFilterHandler3.called.should.equal(true);

            done();
        });
    });

    it("can compile a template", function (done) {
        var template = path.join(process.cwd(), testTemplatePath, 'test.hbs');

        should.exist(ghost.compileTemplate, 'Template Compiler exists');

        ghost.compileTemplate(template).then(function (templateFn) {
            should.exist(templateFn);
            _.isFunction(templateFn).should.equal(true);

            templateFn().should.equal('
HelloWorld
');
            done();
        }).then(null, done);
    });

    it("loads templates for helpers", function (done) {
        var compileSpy = sinon.spy(ghost, 'compileTemplate');

        should.exist(ghost.loadTemplate, 'load template function exists');

        // In order for the test to work, need to replace the path to the template
        ghost.paths = sinon.stub().returns({
            helperTemplates: path.join(process.cwd(), testTemplatePath)
        });

        ghost.loadTemplate('test').then(function (templateFn) {
            // test that compileTemplate was called with the expected path
            compileSpy.calledOnce.should.equal(true);
            compileSpy.calledWith(path.join(process.cwd(), testTemplatePath, 'test.hbs')).should.equal(true);

            should.exist(templateFn);
            _.isFunction(templateFn).should.equal(true);

            templateFn().should.equal('
HelloWorld
');

            compileSpy.restore();

            done();
        }).then(null, done);
    });
});


/*globals describe, beforeEach, it*/
var _ = require("underscore"),
    should = require('should'),
    helpers = require('./helpers'),
    Models = require('../../server/models');

describe('Settings Model', function () {

    var SettingsModel = Models.Settings;

    beforeEach(function (done) {
        helpers.resetData().then(function () {
            done();
        }, done);
    });

    it('can browse', function (done) {
        SettingsModel.browse().then(function (results) {

            should.exist(results);

            results.length.should.be.above(0);

            done();
        }).then(null, done);
    });

    it('can read', function (done) {
        var firstSetting;

        SettingsModel.browse().then(function (results) {

            should.exist(results);

            results.length.should.be.above(0);

            firstSetting = results.models[0];

            return SettingsModel.read(firstSetting.attributes.key);

        }).then(function (found) {

            should.exist(found);

            found.attributes.value.should.equal(firstSetting.attributes.value);

            done();

        }).then(null, done);
    });

    it('can edit single', function (done) {
        var firstSetting;

        SettingsModel.browse().then(function (results) {

            should.exist(results);

            results.length.should.be.above(0);

            firstSetting = results.models[0];

            // The edit method has been modified to take an object of
            // key/value pairs
            firstSetting.set('value', 'new value');

            return SettingsModel.edit(firstSetting);

        }).then(function (edited) {

            should.exist(edited);

            edited.length.should.equal(1);

            edited = edited[0];

            edited.attributes.key.should.equal(firstSetting.attributes.key);
            edited.attributes.value.should.equal('new value');

            done();

        }).then(null, done);
    });

    it('can edit multiple', function (done) {
        var model1,
            model2,
            editedModel;

        SettingsModel.browse().then(function (results) {

            should.exist(results);

            results.length.should.be.above(0);

            model1 = results.models[0];
            model2 = results.models[1];

            // The edit method has been modified to take an object of
            // key/value pairs
            model1.set('value', 'new value1');
            model2.set('value', 'new value2');

            return SettingsModel.edit([model1, model2]);

        }).then(function (edited) {

            should.exist(edited);

            edited.length.should.equal(2);

            editedModel = edited[0];

            editedModel.attributes.key.should.equal(model1.attributes.key);
            editedModel.attributes.value.should.equal('new value1');

            editedModel = edited[1];

            editedModel.attributes.key.should.equal(model2.attributes.key);
            editedModel.attributes.value.should.equal('new value2');

            done();

        }).then(null, done);
    });

    it('can add', function (done) {
        var newSetting = {
            key: 'TestSetting1',
            value: 'Test Content 1'
        };

        SettingsModel.add(newSetting).then(function (createdSetting) {

            should.exist(createdSetting);
            createdSetting.has('uuid').should.equal(true);
            createdSetting.attributes.key.should.equal(newSetting.key, "key is correct");
            createdSetting.attributes.value.should.equal(newSetting.value, "value is correct");
            createdSetting.attributes.type.should.equal("general");

            done();
        }).then(null, done);
    });

    it('can delete', function (done) {
        var firstSettingId;

        SettingsModel.browse().then(function (results) {

            should.exist(results);

            results.length.should.be.above(0);

            firstSettingId = results.models[0].id;

            return SettingsModel.destroy(firstSettingId);

        }).then(function () {

            return SettingsModel.browse();

        }).then(function (newResults) {

            var ids, hasDeletedId;

            ids = _.pluck(newResults.models, "id");

            hasDeletedId = _.any(ids, function (id) {
                return id === firstSettingId;
            });

            hasDeletedId.should.equal(false);

            done();

        }).then(null, done);
    });
});


keep keep keep replace replace replace replace replace replace keep keep keep keep

// Meteor.refresh can be called on the client (if you're in common code) but it
// only has an effect on the server.
Meteor.refresh = function (notification) {
};

if (Meteor.isClient) {
  // By default, try to connect back to the same endpoint as the page
  // was served from.
  var ddpUrl = '/';
  if (typeof __meteor_runtime_config__ !== "undefined") {
    if (__meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL)
      ddpUrl = __meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL;
  }

  var negotiationFailuresKey = "Meteor.DDPVersionNegotiationFailures";

  var onConnected = function () {
    Meteor._localStorage.removeItem(negotiationFailuresKey);
  };

  var exponentialBackoff = function (failures) {
    return Math.pow(failures, 1.5) * 1000;
  };

  var onDDPVersionNegotiationFailure = function (serverRequestedVersion) {
    if (Package.reload) {
      var failures = parseInt(Meteor._localStorage.getItem(negotiationFailuresKey) || "0") + 1;
      Meteor._localStorage.setItem(negotiationFailuresKey, "" + failures);
      Meteor.setTimeout(
        function () {
          Package.reload.Reload._reload();
        },
        exponentialBackoff(failures)
      );
    }
    else {
      Meteor._debug("DDP version negotiation failed; server requested version " + serverRequestedVersion);
    }
  };

  Meteor.connection =
    DDP.connect(ddpUrl, {
      onConnected: onConnected,
      onDDPVersionNegotiationFailure: onDDPVersionNegotiationFailure
    });

  // Proxy the public methods of Meteor.connection so they can
  // be called directly on Meteor.
  _.each(['subscribe', 'methods', 'call', 'apply', 'status', 'reconnect',
          'disconnect'],
         function (name) {
           Meteor[name] = _.bind(Meteor.connection[name], Meteor.connection);
         });
} else {
  // Never set up a default connection on the server. Don't even map
  // subscribe/call/etc onto Meteor.
  Meteor.connection = null;
}

// Meteor.connection used to be called
// Meteor.default_connection. Provide backcompat as a courtesy even
// though it was never documented.
// XXX COMPAT WITH 0.6.4
Meteor.default_connection = Meteor.connection;

// We should transition from Meteor.connect to DDP.connect.
// XXX COMPAT WITH 0.6.4
Meteor.connect = DDP.connect;


/*
    json2.js
    2012-10-08

    Public Domain.

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

    See http://www.JSON.org/js.html

    This code should be minified before deployment.
    See http://javascript.crockford.com/jsmin.html

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    NOT CONTROL.

    This file creates a global JSON object containing two methods: stringify
    and parse.

        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.

            replacer    an optional parameter that determines how object
                        values are stringified for objects. It can be a
                        function or an array of strings.

            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t' or ' '),
                        it contains the characters used to indent at each level.

            This method produces a JSON text from a JavaScript value.

            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method
            will be passed the key associated with the value, and this will be
            bound to the value

            For example, this would serialize Dates as ISO strings.

                Date.prototype.toJSON = function (key) {
                    function f(n) {
                        // Format integers to have at least two digits.
                        return n < 10 ? '0' + n : n;
                    }

                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                };

            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.

            If the replacer parameter is an array of strings, then it will be
            used to select the members to be serialized. It filters the results
            such that only members with keys listed in the replacer array are
            stringified.

            Values that do not have JSON representations, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.

            The optional space parameter produces a stringification of the
            value that is filled with line breaks and indentation to make it
            easier to read.

            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            the indentation will be that many spaces.

            Example:

            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'

            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

            text = JSON.stringify([new Date()], function (key, value) {
                return this[key] instanceof Date ?
                    'Date(' + this[key] + ')' : value;
            });
            // text is '["Date(---current time---)"]'

        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.

            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.

            Example:

            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.

            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });

            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
                var d;
                if (typeof value === 'string' &&
                        value.slice(0, 5) === 'Date(' &&
                        value.slice(-1) === ')') {
                    d = new Date(value.slice(5, -1));
                    if (d) {
                        return d;
                    }
                }
                return value;
            });

    This is a reference implementation. You are free to copy, modify, or
    redistribute.
*/

/*jslint evil: true, regexp: true */

/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/

// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

if (typeof JSON !== 'object') {
    JSON = {};
}

(function () {
    'use strict';

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf())
                ? this.getUTCFullYear()     + '-' +
                    f(this.getUTCMonth() + 1) + '-' +
                    f(this.getUTCDate())      + 'T' +
                    f(this.getUTCHours())     + ':' +
                    f(this.getUTCMinutes())   + ':' +
                    f(this.getUTCSeconds())   + 'Z'
                : null;
        };

        String.prototype.toJSON      =
            Number.prototype.toJSON  =
            Boolean.prototype.toJSON = function (key) {
                return this.valueOf();
            };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;

    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string'
                ? c
                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }

    function str(key, holder) {

// Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

        case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

            if (!value) {
                return 'null';
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === '[object Array]') {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0
                    ? '[]'
                    : gap
                    ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
                    : '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    if (typeof rep[i] === 'string') {
                        k = rep[i];
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0
                ? '{}'
                : gap
                ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
                : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                    typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

            return str('', {'': value});
        };
    }

// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }

// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/
                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function'
                    ? walk({'': j}, '')
                    : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
}());


var onDDPVersionNegotiationFailure = function (serverRequestedVersion) {

var onDDPVersionNegotiationFailure = function (description) {


Package.describe({
  summary: "Common code for OAuth2-based login services",
  internal: true
});

Package.on_use(function (api) {
  api.use('service-configuration', ['client', 'server']);
  api.use('oauth', ['client', 'server']);

  api.add_files('oauth2_server.js', 'server');
});

Package.on_test(function (api) {
  api.use(['tinytest', 'random', 'oauth2', 'oauth', 'service-configuration'],
          'server');
  api.add_files("oauth2_tests.js", 'server');
});


var fs = Npm.require('fs');
var child_process = Npm.require('child_process');
var querystring = Npm.require('querystring');
var urlParser = Npm.require('url');

Spiderable = {};

// list of bot user agents that we want to serve statically, but do
// not obey the _escaped_fragment_ protocol. The page is served
// statically to any client whos user agent matches any of these
// regexps. Users may modify this array.
//
// An original goal with the spiderable package was to avoid doing
// user-agent based tests. But the reality is not enough bots support
// the _escaped_fragment_ protocol, so we need to hardcode a list
// here. I shed a silent tear.
Spiderable.userAgentRegExps = [
    /^facebookexternalhit/i, /^linkedinbot/i, /^twitterbot/i];

// how long to let phantomjs run before we kill it
var REQUEST_TIMEOUT = 15*1000;
// maximum size of result HTML. node's default is 200k which is too
// small for our docs.
var MAX_BUFFER = 5*1024*1024; // 5MB

WebApp.connectHandlers.use(function (req, res, next) {
  if (/\?.*_escaped_fragment_=/.test(req.url) ||
      _.any(Spiderable.userAgentRegExps, function (re) {
        return re.test(req.headers['user-agent']); })) {

    // reassembling url without escaped fragment if exists
    var parsedUrl = urlParser.parse(req.url);
    var parsedQuery = querystring.parse(parsedUrl.query);
    delete parsedQuery['_escaped_fragment_'];
    var newQuery = querystring.stringify(parsedQuery);
    var newPath = parsedUrl.pathname + (newQuery ? ('?' + newQuery) : '');
    var url = "http://" + req.headers.host + newPath;

    // This string is going to be put into a bash script, so it's important
    // that 'url' (which comes from the network) can neither exploit phantomjs
    // or the bash script. JSON stringification should prevent it from
    // exploiting phantomjs, and since the output of JSON.stringify shouldn't
    // be able to contain newlines, it should be unable to exploit bash as
    // well.
    var phantomScript = "var url = " + JSON.stringify(url) + ";" +
          "var page = require('webpage').create();" +
          "page.open(url);" +
          "setInterval(function() {" +
          "  var ready = page.evaluate(function () {" +
          "    if (typeof Meteor !== 'undefined' " +
          "        && typeof(Meteor.status) !== 'undefined' " +
          "        && Meteor.status().connected) {" +
          "      Deps.flush();" +
          "      return DDP._allSubscriptionsReady();" +
          "    }" +
          "    return false;" +
          "  });" +
          "  if (ready) {" +
          "    var out = page.content;" +
          "    out = out.replace(/<script[^>]+>(.|\\n|\\r)*?<\\/script\\s*>/ig, '');" +
          "    out = out.replace('<meta name=\"fragment\" content=\"!\">', '');" +
          "    console.log(out);" +
          "    phantom.exit();" +
          "  }" +
          "}, 100);\n";

    // Run phantomjs.
    //
    // Use '/dev/stdin' to avoid writing to a temporary file. We can't
    // just omit the file, as PhantomJS takes that to mean 'use a
    // REPL' and exits as soon as stdin closes.
    //
    // However, Node 0.8 broke the ability to open /dev/stdin in the
    // subprocess, so we can't just write our string to the process's stdin
    // directly; see https://gist.github.com/3751746 for the gory details. We
    // work around this with a bash heredoc. (We previous used a "cat |"
    // instead, but that meant we couldn't use exec and had to manage several
    // processes.)
    child_process.execFile(
      '/bin/bash',
      ['-c',
       ("exec phantomjs --load-images=no /dev/stdin <<'END'\n" +
        phantomScript + "END\n")],
      {timeout: REQUEST_TIMEOUT, maxBuffer: MAX_BUFFER},
      function (error, stdout, stderr) {
        if (!error && /<html/i.test(stdout)) {
          res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
          res.end(stdout);
        } else {
          // phantomjs failed. Don't send the error, instead send the
          // normal page.
          if (error && error.code === 127)
            Meteor._debug("spiderable: phantomjs not installed. Download and install from http://phantomjs.org/");
          else
            Meteor._debug("spiderable: phantomjs failed:", error, "\nstderr:", stderr);

          next();
        }
      });
  } else {
    next();
  }
});


<template name="commandline">

{{#better_markdown}}

{{#api_section "commandline"}}Command line{{/api_section}}

meteor help

Get help on meteor command line usage. Running `meteor help` by
itself will list the common meteor
commands. Running meteor help command will print
detailed help about the command.

meteor run

Run a meteor development server in the current project. Searches
upward from the current directory for the root directory of a Meteor
project. Whenever you change any of the application's source files, the
changes are automatically detected and applied to the running
application.

You can use the application by pointing your web browser at
localhost:3000. No internet connection is
required.

This is the default command. Simply running `meteor` is the
same as `meteor run`.

To pass additional options to Node.js use the `NODE_OPTIONS` environment variable.
For example: `NODE_OPTIONS='--debug'` or `NODE_OPTIONS='--debug-brk'`

Run `meteor help run` to see the full list of options.

meteor create name

Create a new Meteor project. Makes a subdirectory named *name*
and copies in the template app. You can pass an absolute or relative
path.

meteor deploy site

Deploy the project in your current directory to Meteor's servers.

You can deploy to any available name
under `meteor.com` without any additional
configuration, for example, `myapp.meteor.com`.  If
you deploy to a custom domain, such as `myapp.mydomain.com`,
then you'll need to make sure the DNS for that domain is configured to
point at `origin.meteor.com`.

You can deploy in debug mode by passing `--debug`. This
will leave your source code readable by your favorite in-browser
debugger, just like it is in local development mode.

To delete an application you've deployed, specify
the `--delete` option along with the site.

To add an administrative password to your deployment, include
the `--password` option.  Meteor will prompt
for a password.  Once set, any future `meteor deploy` to
the same domain will require that you provide the password.  The same
password protects access to `meteor mongo`
and `meteor logs`.  You can change the password by
running `meteor deploy --password` again,
which will first prompt for the current password, then for a new
password.

{{#warning}}
Password protection only applies to administrative actions with the
Meteor command.  It does not prevent access to your deployed
website.  Also, this all is a temporary hack until we have
full-featured Meteor accounts.
{{/warning}}

{{#warning}}
If you use a domain name other than `meteor.com`
you must ensure that the name resolves
to `origin.meteor.com`.  If you want a top-level
domain like myapp.com, you'll need a DNS A record, matching the IP
address of origin.meteor.com.
{{/warning}}

You can add information specific to a particular deployment of your application
by using the `--settings` option.  The argument to `--settings` is a file
containing any JSON string.  The object in your settings file will appear on the
server side of your application in [`Meteor.settings`](#meteor_settings).

Settings are persistent.  When you redeploy your app, the old value will be
preserved unless you explicitly pass new settings using the `--settings` option.
To unset `Meteor.settings`, pass an empty settings file.

meteor logs site

Retrieves the server logs for the named Meteor application.

Meteor redirects the output of `console.log()` in your
server code into a logging server.  `meteor logs`
displays those logs.  In client code, the output
of `console.log()` is available in your web browser's
inspector, just like any other client-side JavaScript.

meteor update

Sets the version of Meteor to use with the current project. If a
release is specified with `--release`, set the project to use that
version. Otherwise download and use the latest release of Meteor.

Every project is pinned to a specific release of Meteor. You can temporarily try
using your package with another release by passing the `--release` option to any
command; `meteor update` simply changes the pinned release.

meteor add package

Add packages to your Meteor project. You can add multiple packages
with one command. For a list of the available packages, run `meteor
list`.

meteor remove package

Removes a package previously added to your Meteor project. For a
list of the packages that your application is currently using, run
`meteor list --using`.

meteor list

Without arguments, lists all available Meteor packages. To add one of
these packages to your project, run meteor add package.

With `--using`, list the packages that you have added to your project.

meteor mongo

Open a MongoDB shell on your local development database, so that you
can view or manipulate it directly.

{{#warning}}
For now, you must already have your application running locally
with `meteor run`. This will be easier in the future.
{{/warning}}

meteor reset

Reset the current project to a fresh state. Removes the local
mongo database.

{{#warning}}
This deletes your data! Make sure you do not have any information you
care about in your local mongo database by running `meteor
mongo`. From the mongo shell, use `show collections`
and db.collection.find() to inspect your data.
{{/warning}}

{{#warning}}
For now, you can not run this while a development server is
running. Quit all running meteor applications before running this.
{{/warning}}

meteor bundle

Package the application up for deployment. The output is a tarball
that includes everything necessary to run the
application. See `README` in the tarball for details.

You can use this to host a Meteor application on your own server,
instead of deploying to Meteor's servers.  You will have to deal with
logging, monitoring, backups, load-balancing, etc, all of which we
handle for you if you use `meteor deploy`.

{{/better_markdown}}

</template>