This document specifies the version "1" of DDP. It's a rough description of

# DDP Specification

DDP is a protocol between a client and a server that supports two operations:

 * Remote procedure calls by the client to the server.
 * The client subscribing to a set of documents, and the server keeping the
   client informed about the contents of those documents as they change over
   time.

This document specifies the version "pre2" of DDP. It's a rough description of
the protocol and not intended to be entirely definitive.

## General Message Structure:

DDP may use either SockJS or WebSockets as a lower-level message transport. (For
now, you connect via SockJS at the URL `/sockjs` and via WebSockets at the URL
`/websocket`. The latter is likely to change to be the main app URL specifying a
WebSocket subprotocol.)

DDP messages are JSON objects, with some fields specified to be EJSON.  Each one
has a `msg` field that specifies the message type, as well as other fields
depending on message type.

Extra fields may be included by the client and the server; this must not be an
error.  Future minor revisions of DDP might add extra fields without changing
the DDP version; the client must therefore silently ignore unknown fields.
However, the client must not send extra fields other than those documented here
or in future versions, in case these extra fields have meaning to future
servers.  On the server, all field changes must be optional/ignorable for
compatability with older clients; otherwise a new protocol version would be
required.

## Establishing a DDP Connection:

### Messages:

 * `connect` (client -> server)
   - `session`: string (if trying to reconnect to an existing DDP session)
   - `version`: string (the proposed protocol version)
   - `support`: array of strings (protocol versions supported by the client,
      in order of preference)
 * `connected` (server->client)
   - `session`: string (an identifier for the DDP session)
 * `failed` (server->client)
   - `version`: string (a suggested protocol version to connect with)

### Procedure:

The server may send an initial message which is a JSON object lacking
a `msg` key. If so, the client should ignore it. The client does not
have to wait for this message. (The message was once used to help
implement Meteor's hot code reload feature; it is now only included to
force old clients to update).

 * The client sends a `connect` message.
 * If the server is willing to speak the `version` of the protocol specified in
   the `connect` message, it sends back a `connected` message.
 * Otherwise the server sends back a `failed` message with a version of DDP it
   would rather speak, informed by the `connect` message's `support` field, and
   closes the underlying transport.
 * The client is then free to attempt to connect again speaking a different
   version of DDP. It can do that by sending another `connect` message on a new
   connection. The client may optimistically send more messages after the
   `connect` message, assuming that the server will support the proposed
   protocol version. If the server does not support that version, it must ignore
   those additional messages.

The versions in the `support` field of the client's `connect` message
are ordered according to the client's preference, most preferred
first. If, according to this ordering, the `version` proposed by the
client is not the best version that the server supports, the server
must force the client to switch to the better version by sending a
`failed` message.

When a client is connecting to a server for the first time it will
typically set `version` equal to its most preferred version. If
desired, the client can then remember the version that is ultimately
negotiated with the server and begin with that version in future
connections. The client can rely on the server sending a `failed`
message if a better version is possible as a result of the client or
the server having been upgraded.

## Heartbeats

### Messages:

 * `ping`
   - `id`: optional string (identifier used to correlate with response)
 * `pong`
   - `id`: optional string (same as received in the `ping` message)

### Procedure:

At any time after the connection is established either side may send a `ping`
message. The sender may chose to include an `id` field in the `ping`
message. When the other side receives a `ping` it must immediately respond with
a `pong` message. If the received `ping` message includes an `id` field, the
`pong` message must include the same `id` field.

## Managing Data:

### Messages:

 * `sub` (client -> server):
   - `id`: string (an arbitrary client-determined identifier for this subscription)
   - `name`: string (the name of the subscription)
   - `params`: optional array of EJSON items (parameters to the subscription)
 * `unsub` (client -> server):
   - `id`: string (the id passed to 'sub')
 * `nosub` (server -> client):
   - `id`: string (the id passed to 'sub')
   - `error`: optional Error (an error raised by the subscription as it
    concludes, or sub-not-found)
 * `added` (server -> client):
   - `collection`: string (collection name)
   - `id`: string (document ID)
   - `fields`: optional object with EJSON values
 * `changed` (server -> client):
   - `collection`: string (collection name)
   - `id`: string (document ID)
   - `fields`: optional object with EJSON values
   - `cleared`: optional array of strings (field names to delete)
 * `removed` (server -> client):
   - `collection`: string (collection name)
   - `id`: string (document ID)
 * `ready` (server -> client):
   - `subs`: array of strings (ids passed to 'sub' which have sent their
     initial batch of data)
 * `addedBefore` (server -> client):
   - `collection`: string (collection name)
   - `id`: string (document ID)
   - `fields`: optional object with EJSON values
   - `before`: string or null (the document ID to add the document before,
     or null to add at the end)
 * `movedBefore` (server -> client):
   - `collection`: string
   - `id`: string (the document ID)
   - `before`: string or null (the document ID to move the document before, or
     null to move to the end)

### Procedure:

 * The client specifies sets of information it is interested in by sending
   `sub` messages to the server.

 * At any time, but generally informed by the `sub` messages, the server can
   send data messages to the client.  Data consist of `added`, `changed`, and
   `removed` messages.  These messages model a local set of data the client
   should keep track of.

   - An `added` message indicates a document was added to the local set. The ID
     of the document is specified in the `id` field, and the fields of the
     document are specified in the `fields` field.  Minimongo interperets the
     string id field in a special way that transforms it to the _id field of
     Mongo documents.

   - A `changed` message indicates a document in the local set has new values
     for some fields or has had some fields removed. The `id` field is the ID of
     the document that has changed.  The `fields` object, if present, indicates
     fields in the document that should be replaced with new values.  The
     `cleared` field contains an array of fields that are no longer in the
     document.

   - A `removed` message indicates a document was removed from the local
     set. The `id` field is the ID of the document.

 * A collection is either ordered, or not. If a collection is ordered,
   the `added` message is replaced by `addedBefore`, which
   additionally contains the ID of the document after the one being
   added in the `before` field.  If the document is being added at the
   end, `before` is set to null. For a given collection, the server
   should only send `added` messages or `addedBefore` messages, not a
   mixture of both, and should only send `movedBefore` messages for a
   collection with `addedBefore` messages.

   NOTE: The ordered collection DDP messages are not currently used by Meteor.
   They will likely be used by Meteor in the future.

 * The client maintains one set of data per collection.  Each subscription does
   not get its own datastore, but rather overlapping subscriptions cause the
   server to send the union of facts about the one collection's data.  For
   example, if subscription A says document `x` has fields `{foo: 1, bar: 2}`
   and subscription B says document `x` has fields `{foo: 1, baz:3}`, then the
   client will be informed that document `x` has fields `{foo: 1, bar: 2, baz:
   3}`.  If field values from different subscriptions conflict with each other,
   the server should send one of the possible field values.

 * When one or more subscriptions have finished sending their initial batch of
   data, the server will send a `ready` message with their IDs.

## Remote Procedure Calls:

### Messages:

 * `method` (client -> server):
   - `method`: string (method name)
   - `params`: optional array of EJSON items (parameters to the method)
   - `id`: string (an arbitrary client-determined identifier for this method call)
   - `randomSeed`: optional JSON value (an arbitrary client-determined seed
     for pseudo-random generators)
 * `result` (server -> client):
   - `id`: string (the id passed to 'method')
   - `error`: optional Error (an error thrown by the method (or method-not-found)
   - `result`: optional EJSON item (the return value of the method, if any)
 * `updated` (server -> client):
   - `methods`: array of strings (ids passed to 'method', all of whose writes
     have been reflected in data messages)

### Procedure:

 * The client sends a `method` message to the server

 * The server responds with a `result` message to the client, carrying either
   the result of the method call, or an appropriate error.

 * Method calls can affect data that the client is subscribed to.  Once the
   server has finished sending the client all the relevant data messages based
   on this procedure call, the server should send an `updated` message to the
   client with this method's ID.

 * There is no particular required ordering between `result` and `updated`
   messages for a method call.

 * The client may provide a randomSeed JSON value.  If provided, this value is
   used to seed pseudo-random number generation.  By using the same seed with
   the same algorithm, the same pseudo-random values can be generated on the
   client and the server.  In particular, this is used for generating ids for
   newly created documents.  If randomSeed is not provided, then values
   generated on the server and the client will not be identical.

 * Currently randomSeed is expected to be a string, and the algorithm by which
   values are produced from this is not yet documented.  It will likely be
   formally specified in future when we are confident that the complete
   requirements are known, or when a compatible implementation requires this
   to be specified.

## Errors:

Errors appear in `result` and `nosub` messages in an optional error field. An
error is an Object with the following fields:

 * `error`: number
 * `reason`: optional string
 * `details`: optional string

Such an Error is used to represent errors raised by the method or subscription,
as well as an attempt to subscribe to an unknown subscription or call an unknown
method.

Other erroneous messages sent from the client to the server can result in
receiving a top-level `msg: 'error'` message in response. These conditions
include:

 * sending messages which are not valid JSON objects
 * unknown `msg` type
 * other malformed client requests (not including required fields)
 * sending anything other than `connect` as the first message, or sending
   `connect` as a non-initial message

The error message contains the following fields:

 * `reason`: string describing the error
 * `offendingMessage`: if the original message parsed properly, it is included
   here

## Appendix: EJSON

EJSON is a way of embedding more than the built-in JSON types in JSON.  It
supports all types built into JSON as plain JSON, plus the following:

**Dates:**

    {"$date": MILLISECONDS_SINCE_EPOCH}

**Binary data:**

    {"$binary": BASE_64_STRING}

(The base 64 string has `+` and `/` as characters 62 and 63, and has no maximum line length)

**Escaped things** that might otherwise look like EJSON types:

    {"$escape": THING}

For example, here is the JSON value `{$date: 10000}` stored in EJSON:

    {"$escape": {"$date": 10000}}

Note that escaping only causes keys to be literal for one level down; you can
have further EJSON inside.  For example, the following is the key `$date` mapped
to a Date object:

    {"$escape": {"$date": {"$date": 32491}}}

**User-specified types:**

    {"$type": TYPENAME, "$value": VALUE}

Implementations of EJSON should try to preserve key order where they can.  Users
of EJSON should not rely on key order, if possible.

> MongoDB relies on key order.  When using EJSON with MongoDB, the
> implementation of EJSON must preserve key order.

## Appendix 2: randomSeed backward/forward compatibility

randomSeed was added into DDP pre2, but it does not break backward or forward
compatibility.

If the client stub and the server produce documents that are different in any
way, Meteor will reconcile this difference. This may cause 'flicker' in the UI
as the values change on the client to reflect what happened on the server, but
the final result will be correct: the server and the client will agree.

Consistent id generation / randomSeed does not alter the syncing process, and
thus will (at worst) be the same:

* If neither the server nor the client support randomSeed, we will get the
classical/flicker behavior.

* If the client supports randomSeed, but the server does not, the server will
ignore randomSeed, as it ignores any unknown properties in a DDP method call.
Different ids will be generated, but this will be fixed by syncing.

* If the server supports randomSeed, but the client does not, the server will
generate unseeded random values (providing a randomSeed is optional); different
ids will be generated; and again this will be fixed by syncing.

* If both client and server support randomSeed, but different ids are
generated, either because the generation procedure is buggy, or the stub
behaves differently to the server, then syncing will fix this.

* If both client and server support randomSeed, in the normal case the ids
generated will be the same, and syncing will be a no-op.


    {{#each items}}

        {{text}}

    {{/each}}


canonicalizeHtml = function(html) {
  var h = html;
  // kill IE-specific comments inserted by DomRange
  h = h.replace(/<!--IE-->/g, '');
  h = h.replace(/<!---->/g, '');
  // ignore exact text of comments
  h = h.replace(/<!--.*?-->/g, '<!---->');
  // make all tags lowercase
  h = h.replace(/<\/?(\w+)/g, function(m) {
    return m.toLowerCase(); });
  // replace whitespace sequences with spaces
  h = h.replace(/\s+/g, ' ');
  // Trim leading and trailing whitespace
  h = h.replace(/^\s+|\s+$/g, '');
  // remove whitespace before and after tags
  h = h.replace(/\s*(<\/?\w.*?>)\s*/gm, function (m, tag) {
    return tag; });
  // make tag attributes uniform
  h = h.replace(/<(\w+)\s+(.*?)\s*>/g, function(m, tagName, attrs) {
    // Drop expando property used by Sizzle (part of jQuery) which leaks into
    // attributes in IE8. Note that its value always contains spaces.
    attrs = attrs.replace(/sizcache[0-9]+="[^"]*"/g, ' ');
    // Similarly for expando properties used by jQuery to track data.
    attrs = attrs.replace(/jQuery[0-9]+="[0-9]+"/g, ' ');
    // Similarly for expando properties used to DOMBackend to keep
    // track of callbacks to fire when an element is removed
    attrs = attrs.replace(/\$blaze_teardown_callbacks="[^"]*"/g, ' ');
    // And by DOMRange to keep track of the element's DOMRange
    attrs = attrs.replace(/\$blaze_range="[^"]*"/g, ' ');

    attrs = attrs.replace(/\s*=\s*/g, '=');
    attrs = attrs.replace(/^\s+/g, '');
    attrs = attrs.replace(/\s+$/g, '');
    attrs = attrs.replace(/\s+/g, ' ');
    // quote unquoted attribute values, as in `type=checkbox`.  This
    // will do the wrong thing if there's an `=` in an attribute value.
    attrs = attrs.replace(/(\w)=([^'" >/]+)/g, '$1="$2"');

    // for the purpose of splitting attributes in a string like 'a="b"
    // c="d"', assume they are separated by a single space and values
    // are double- or single-quoted, but allow for spaces inside the
    // quotes.  Split on space following quote.
    var attrList = attrs.replace(/(\w)='([^']*)' /g, "$1='$2'\u0000");
    attrList = attrList.replace(/(\w)="([^"]*)" /g, '$1="$2"\u0000');
    attrList = attrList.split("\u0000");
    // put attributes in alphabetical order
    attrList.sort();

    var tagContents = [tagName];

    for(var i=0; i<attrList.length; i++) {
      // If there were no attrs, attrList could be `[""]`,
      // so skip falsy values.
      if (! attrList[i])
        continue;
      var a = attrList[i].split('=');

      // In IE8, attributes whose value is "" appear
      // without the '=' sign altogether.
      if (a.length < 2)
        a.push("");

      var key = a[0];
      // Drop another expando property used by Sizzle.
      if (key === 'sizset')
        continue;
      var value = a[1];

      // make sure the attribute is doubled-quoted
      if (value.charAt(0) === '"') {
        // Do nothing
      } else {
        if (value.charAt(0) !== "'") {
          // attribute is unquoted. should be unreachable because of
          // regex above.
          value = '"' + value + '"';
        } else {
          // attribute is single-quoted. make it double-quoted.
          value = value.replace(/\"/g, """);
        }
        value = value.replace(/["'`]/g, '"');
      }

      // Encode quotes and double quotes in the attribute.
      var attr = value.slice(1, -1);
      attr = attr.replace(/\"/g, """);
      attr = attr.replace(/\'/g, """);
      value = '"' + attr + '"';

      // Ensure that styles do not end with a semicolon.
      if (key === 'style') {
        value = value.replace(/;\"$/, '"');
      }

      tagContents.push(key+'='+value);
    }
    return '<'+tagContents.join(' ')+'>';
  });

  h = h.replace(/</g, "\n<");
  h = h.replace(/<!--[^>]*-->/g, "");

  return h;
};

var fs = require("fs");

o = fs.readFileSync(process.argv[2], {encoding: "utf8"});

o = canonicalizeHtml(o).replace(/(<\w)/g, "\n$1").replace(/(<\/\w*>)/g, "$1\n");
o = o.replace(/<p>\n<\/p>/gm, "");

console.log(o);


behaves differently to the server, then syncing will fix this.

* If both client and server support randomSeed, in the normal case the ids

generated will be the same, and syncing will be a no-op.

```pre2``` added keep-alive (ping & pong messages), and randomSeed.

```1``` should be considered the first official version of DDP.  It is

currently identical to pre2, although non-incompatible changes may be made to


Package.describe({
  summary: "Login service for Github accounts",
  version: "1.0.0"
});

Package.on_use(function(api) {
  api.use('accounts-base', ['client', 'server']);
  // Export Accounts (etc) to packages using this one.
  api.imply('accounts-base', ['client', 'server']);
  api.use('accounts-oauth', ['client', 'server']);
  api.use('github', ['client', 'server']);

  api.add_files('github_login_button.css', 'client');

  api.add_files("github.js");
});


Package.describe({
  summary: "Publish internal app statistics",
  version: '1.0.0'
});

Package.on_use(function (api) {
  api.use(['underscore'], ['client', 'server']);
  api.use(['templating', 'mongo-livedata', 'livedata'], ['client']);

  // Detect whether autopublish is used.
  api.use('autopublish', 'server', {weak: true});

  // Unordered dependency on livedata, since livedata has a (weak) dependency on
  // us.
  api.use('livedata', 'server', {unordered: true});

  api.add_files('facts.html', ['client']);
  api.add_files('facts.js', ['client', 'server']);

  api.export('Facts');
});


Facebook = {};

// Request Facebook credentials for the user
//
// @param options {optional}
// @param credentialRequestCompleteCallback {Function} Callback function to call on
//   completion. Takes one argument, credentialToken on success, or Error on
//   error.
Facebook.requestCredential = function (options, credentialRequestCompleteCallback) {
  // support both (options, callback) and (callback).
  if (!credentialRequestCompleteCallback && typeof options === 'function') {
    credentialRequestCompleteCallback = options;
    options = {};
  }

  var config = ServiceConfiguration.configurations.findOne({service: 'facebook'});
  if (!config) {
    credentialRequestCompleteCallback && credentialRequestCompleteCallback(
      new ServiceConfiguration.ConfigError());
    return;
  }

  var credentialToken = Random.secret();
  var mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(navigator.userAgent);
  var display = mobile ? 'touch' : 'popup';

  var scope = "email";
  if (options && options.requestPermissions)
    scope = options.requestPermissions.join(',');

  var loginUrl =
        'https://www.facebook.com/dialog/oauth?client_id=' + config.appId +
        '&redirect_uri=' + Meteor.absoluteUrl('_oauth/facebook?close') +
        '&display=' + display + '&scope=' + scope + '&state=' + credentialToken;

  OAuth.showPopup(
    loginUrl,
    _.bind(credentialRequestCompleteCallback, null, credentialToken)
  );
};


SUPPORTED_DDP_VERSIONS = [ 'pre2', 'pre1' ];

// All the supported versions (for both the client and server)

// These must be in order of preference; most favored-first

SUPPORTED_DDP_VERSIONS = [ '1', 'pre2', 'pre1' ];


Package.describe({
  summary: "Run a function when the user scrolls past an element",
  version: "1.0.0"
});

Package.on_use(function (api) {
  api.use('jquery');
  api.use('coffeescript');
  api.add_files('waypoints.coffee', 'client');
});


// This app functions with no packages loaded, so it's good for testing releases
// with no packages.  All it does is print "RUNNING" and run forever.
main = function () {
  // Tell the runner we're up.
  console.log("LISTENING");
  // Ensure Node doesn't kill us.
  process.stdin.resume();
  // Ensure boot.js doesn't kill us.
  return 'DAEMON';
};


  <title>movers</title>

  {{> main}}

<template name="main">

Red

Green

Blue

Yellow

</template>


keep keep keep keep keep replace replace replace keep keep keep

/*
 * 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
 * CodeMirror
 */

/* =============================================================================
   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;
			}
		}

	}

    .entry-container {
        position: relative;
        height: -webkit-calc(100% - 70px);
        height: -moz-calc(100% - 70px);
        height: calc(100% - 70px);
    }

	// 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:0px; // 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:40px;
			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;
}

/* =============================================================================
   CodeMirror
   ============================================================================= */

.CodeMirror {
    /* Set height, width, borders, and global font properties here */
    font-family: monospace;
    height: 300px;
}

.CodeMirror-scroll {
    /* Set scrolling behaviour here */
    overflow: auto;
}

/* PADDING */

.CodeMirror-lines {
    padding: 4px 0; /* Vertical padding around content */
}

.CodeMirror pre {
    padding: 0 4px; /* Horizontal padding of content */
}

.CodeMirror-scrollbar-filler {
    background-color: white; /* The little square between H and V scrollbars */
}

/* GUTTER */

.CodeMirror-gutters {
    border-right: 1px solid #ddd;
    background-color: #f7f7f7;
}

/* CURSOR */

.CodeMirror div.CodeMirror-cursor {
    border-left: 1px solid black;
    z-index: 3;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
    border-left: 1px solid silver;
}

.cm-tab { display: inline-block; }

/* DEFAULT THEME */

.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable {color: black;}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-property {color: black;}
.cm-s-default .cm-operator {color: black;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-error {color: #f00;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}

.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}

.cm-invalidchar {color: #f00;}

/* STOP */

/* The rest of this file contains styles related to the mechanics of
   the editor. You probably shouldn't touch them. */

.CodeMirror {
    line-height: 1;
    position: relative;
    overflow: hidden;
    background: white;
    color: black;
}

.CodeMirror-scroll {
    /* 30px is the magic margin used to hide the element's real scrollbars */
    /* See overflow: hidden in .CodeMirror */
    margin-bottom: -30px; margin-right: -30px;
    padding-bottom: 30px; padding-right: 30px;
    height: 100%;
    outline: none; /* Prevent dragging from highlighting the element */
    position: relative;
}
.CodeMirror-sizer {
    position: relative;
}

/* The fake, visible scrollbars. Used to force redraw during scrolling
   before actuall scrolling happens, thus preventing shaking and
   flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
    position: absolute;
    z-index: 6;
    display: none;
}

.CodeMirror-vscrollbar {
    right: 0; top: 0;
    overflow-x: hidden;
    overflow-y: scroll;
}

.CodeMirror-hscrollbar {
    bottom: 0; left: 0;
    overflow-y: hidden;
    overflow-x: scroll;
}

.CodeMirror-scrollbar-filler {
    right: 0; bottom: 0;
    z-index: 6;
}

.CodeMirror-gutters {
    position: absolute; left: 0; top: 0;
    height: 100%;
    padding-bottom: 30px;
    z-index: 3;
}

.CodeMirror-lines {
    cursor: text;
}

.CodeMirror pre {
    /* Reset some styles that the rest of the page might have set */
    -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
    border-width: 0;
    background: transparent;
    font-family: inherit;
    font-size: inherit;
    margin: 0;
    white-space: pre;
    word-wrap: normal;
    line-height: inherit;
    color: inherit;
    z-index: 2;
    position: relative;
    overflow: visible;
}

.CodeMirror-wrap pre {
    word-wrap: break-word;
    white-space: pre-wrap;
    word-break: normal;
}

.CodeMirror-wrap .CodeMirror-scroll {
    overflow-x: hidden;
}

.CodeMirror-measure {
    position: absolute;
    width: 100%; height: 0px;
    overflow: hidden;
    visibility: hidden;
}

.CodeMirror-measure pre { position: static; }

.CodeMirror div.CodeMirror-cursor {
    position: absolute;
    visibility: hidden;
    border-right: none;
    width: 0;
}

.CodeMirror-focused div.CodeMirror-cursor {
    visibility: visible;
}

.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }

/* IE7 hack to prevent it from returning funny offsetTops on the spans */
.CodeMirror span { *vertical-align: text-bottom; }

@media print {
/* Hide the cursor when printing */
    .CodeMirror div.CodeMirror-cursor {
        visibility: hidden;
    }
}


/**
 * http://www.openjs.com/scripts/events/keyboard_shortcuts/
 * Version : 2.01.B
 * By Binny V A
 * License : BSD
 */
shortcut = {
    'all_shortcuts':{},//All the shortcuts are stored in this array
    'add': function(shortcut_combination,callback,opt) {
        //Provide a set of default options
        var default_options = {
            'type':'keydown',
            'propagate':false,
            'disable_in_input':false,
            'target':document,
            'keycode':false
        }
        if(!opt) opt = default_options;
        else {
            for(var dfo in default_options) {
                if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
            }
        }

        var ele = opt.target;
        if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
        var ths = this;
        shortcut_combination = shortcut_combination.toLowerCase();

        //The function to be called at keypress
        var func = function(e) {
            e = e || window.event;

            if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
                var element;
                if(e.target) element=e.target;
                else if(e.srcElement) element=e.srcElement;
                if(element.nodeType==3) element=element.parentNode;

                if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
            }

            //Find Which key is pressed
            if (e.keyCode) code = e.keyCode;
            else if (e.which) code = e.which;
            var character = String.fromCharCode(code).toLowerCase();

            if(code == 188) character=","; //If the user presses , when the type is onkeydown
            if(code == 190) character="."; //If the user presses , when the type is onkeydown

            var keys = shortcut_combination.split("+");
            //Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
            var kp = 0;

            //Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
            var shift_nums = {
                "`":"~",
                "1":"!",
                "2":"@",
                "3":"#",
                "4":"$",
                "5":"%",
                "6":"^",
                "7":"&",
                "8":"*",
                "9":"(",
                "0":")",
                "-":"_",
                "=":"+",
                ";":":",
                "'":"\"",
                ",":"<",
                ".":">",
                "/":"?",
                "\\":"|"
            }
            //Special Keys - and their codes
            var special_keys = {
                'esc':27,
                'escape':27,
                'tab':9,
                'space':32,
                'return':13,
                'enter':13,
                'backspace':8,

                'scrolllock':145,
                'scroll_lock':145,
                'scroll':145,
                'capslock':20,
                'caps_lock':20,
                'caps':20,
                'numlock':144,
                'num_lock':144,
                'num':144,

                'pause':19,
                'break':19,

                'insert':45,
                'home':36,
                'delete':46,
                'end':35,

                'pageup':33,
                'page_up':33,
                'pu':33,

                'pagedown':34,
                'page_down':34,
                'pd':34,

                'left':37,
                'up':38,
                'right':39,
                'down':40,

                'f1':112,
                'f2':113,
                'f3':114,
                'f4':115,
                'f5':116,
                'f6':117,
                'f7':118,
                'f8':119,
                'f9':120,
                'f10':121,
                'f11':122,
                'f12':123
            }

            var modifiers = {
                shift: { wanted:false, pressed:false},
                ctrl : { wanted:false, pressed:false},
                alt  : { wanted:false, pressed:false},
                meta : { wanted:false, pressed:false}   //Meta is Mac specific
            };

            if(e.ctrlKey)   modifiers.ctrl.pressed = true;
            if(e.shiftKey)  modifiers.shift.pressed = true;
            if(e.altKey)    modifiers.alt.pressed = true;
            if(e.metaKey)   modifiers.meta.pressed = true;

            for(var i=0; k=keys[i],i<keys.length; i++) {
                //Modifiers
                if(k == 'ctrl' || k == 'control') {
                    kp++;
                    modifiers.ctrl.wanted = true;

                } else if(k == 'shift') {
                    kp++;
                    modifiers.shift.wanted = true;

                } else if(k == 'alt') {
                    kp++;
                    modifiers.alt.wanted = true;
                } else if(k == 'meta') {
                    kp++;
                    modifiers.meta.wanted = true;
                } else if(k.length > 1) { //If it is a special key
                    if(special_keys[k] == code) kp++;

                } else if(opt['keycode']) {
                    if(opt['keycode'] == code) kp++;

                } else { //The special keys did not match
                    if(character == k) kp++;
                    else {
                        if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
                            character = shift_nums[character];
                            if(character == k) kp++;
                        }
                    }
                }
            }

            if(kp == keys.length &&
                        modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
                        modifiers.shift.pressed == modifiers.shift.wanted &&
                        modifiers.alt.pressed == modifiers.alt.wanted &&
                        modifiers.meta.pressed == modifiers.meta.wanted) {
                callback(e);

                if(!opt['propagate']) { //Stop the event
                    //e.cancelBubble is supported by IE - this will kill the bubbling process.
                    e.cancelBubble = true;
                    e.returnValue = false;

                    //e.stopPropagation works in Firefox.
                    if (e.stopPropagation) {
                        e.stopPropagation();
                        e.preventDefault();
                    }
                    return false;
                }
            }
        }
        this.all_shortcuts[shortcut_combination] = {
            'callback':func,
            'target':ele,
            'event': opt['type']
        };
        //Attach the function with the event
        if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
        else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
        else ele['on'+opt['type']] = func;
    },

    //Remove the shortcut - just specify the shortcut and I will remove the binding
    'remove':function(shortcut_combination) {
        shortcut_combination = shortcut_combination.toLowerCase();
        var binding = this.all_shortcuts[shortcut_combination];
        delete(this.all_shortcuts[shortcut_combination])
        if(!binding) return;
        var type = binding['event'];
        var ele = binding['target'];
        var callback = binding['callback'];

        if(ele.detachEvent) ele.detachEvent('on'+type, callback);
        else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
        else ele['on'+type] = false;
    }
}


/*
 * Breakpoint Sass 1.3.0
 * Last updated: 2012-08-28
 * Copyright: Mason Wendell 2012 - MIT Licensed
 * Source: https://github.com/canarymason/breakpoint
 */

//////////////////////////////
// Default Variables
//////////////////////////////
$breakpoint-default-feature:  'min-width'     !default;
$breakpoint-default-media:    'all'           !default;
$breakpoint-force-media-all:  false           !default;
$breakpoint-default-pair:     'width'         !default;
$breakpoint-to-ems:           false           !default;
$breakpoint-prefixes:         'webkit' 'moz'  !default;
$breakpoint-prefixed-queries: 'device-pixel-ratio' 'min-device-pixel-ratio' 'max-device-pixel-ratio'                      !default;

$breakpoint-no-queries:       false           !default;
$breakpoint-no-query-wrappers: false           !default;

$breakpoint-base-font-size: false;

//////////////////////////////
// Converts the input value to Base EMs
//////////////////////////////
@function breakpoint-to-base-em($value, $base-font-size: false) {
  $value-unit: unit($value);

  // Will convert relative EMs into root EMs.
  @if $base-font-size and type-of($base-font-size) == 'number' and $value-unit == 'em' {
    $base-unit: unit($base-font-size);

    @if $base-unit == 'px' or $base-unit == '%' or $base-unit == 'em' or $base-unit == 'pt' {
      @return base-conversion($value) / base-conversion($base-font-size) * 1em;
    }
    @else {
      @warn '#{$base-font-size} is not set in valid units for font size!';
      @return false;
    }
  }
  @else {
    @return base-conversion($value);
  }
}

@function base-conversion($value) {
  $unit: unit($value);

  @if $unit == 'px' {
    @return $value / 16px * 1em;
  }
  @else if $unit == '%' {
    @return $value / 100% * 1em;
  }
  @else if $unit == 'em' {
    @return $value;
  }
  @else if $unit == 'pt' {
    @return $value / 12pt * 1em;
  }
  @else {
    @return $value;
//    @warn 'Everything is terrible! What have you done?!';
  }
}

//////////////////////////////
// Returns whether the feature can have a min/max pair
//////////////////////////////
@function breakpoint-min-max($feature) {
  @if $feature == 'color' or $feature == 'color-index' or $feature == 'aspect-ratio' or $feature == 'device-height' or $feature == 'device-width' or $feature == 'height' or $feature == 'monochrome' or $feature == 'resolution' or $feature == 'width' or $feature == 'device-pixel-ratio' {
    @return true;
  }
  @else {
    @return false;
  }
}

//////////////////////////////
// Returns whether the feature can have a string value
//////////////////////////////
@function breakpoint-string-value($feature) {
@if $feature == 'orientation' or $feature == 'scan' or $feature == 'color' or $feature == 'resolution' or $feature == 'min-resolution' or $feature == 'max-resolution' {
    @return true;
  }
  @else {
    @return false;
  }
}

//////////////////////////////
// Experimental Media Queries
//////////////////////////////
@function breakpoint-experimental($property, $prefix) {
  @if $property == 'min-device-pixel-ratio' {
    @if $prefix == 'webkit' {
      @return '-#{$prefix}-#{$property}';
    }
    @else if $prefix == 'moz' {
      @return 'min--#{$prefix}-device-pixel-ratio';
    }
    @else {
      @warn '#{$property} is not fully supported in -#{prefix}';
      @return 'ERROR';
    }
  }
  @else if $property == 'max-device-pixel-ratio' {
    @if $prefix == 'webkit' {
      @return '-#{$prefix}-#{$property}';
    }
    @else if $prefix == 'moz' {
      @return 'max--#{$prefix}-device-pixel-ratio';
    }
    @else {
      @warn '#{$property} is not fully supported in -#{prefix}';
      @return 'ERROR';
    }
  }
  @else {
    @return '-#{$prefix}-#{$property}';
  }
}

//////////////////////////////
// Private Breakpoint Variables
//////////////////////////////
$TXkgdmFyaWFibGUhIEdvIGF3YXkh: () !default;

//////////////////////////////
// Breakpoint Get Context
// $feature: Input feature to get it's current MQ context. Returns false if no context
//////////////////////////////
@function breakpoint-get-context($feature) {
  @each $context in $TXkgdmFyaWFibGUhIEdvIGF3YXkh {
    @if $feature == nth($context, 1) {
      @return nth($context, 2);
    }
  }

  @return false;
}

//////////////////////////////
// Private function to set context
//////////////////////////////
@function U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh($feature, $value) {
  @if $value == 'monochrome' {
    $feature: 'monochrome';
  }

  $append: $feature;
  $append: join($append, $value, space);

  $TXkgdmFyaWFibGUhIEdvIGF3YXkh: append($TXkgdmFyaWFibGUhIEdvIGF3YXkh, $append, comma);

  @return true;
}

//////////////////////////////
// Private function to reset context
//////////////////////////////
@mixin TXkgcmVzZXQhIEdvIGF3YXkh {
  $TXkgdmFyaWFibGUhIEdvIGF3YXkh: ();
}

//////////////////////////////
// Breakpoint Mixin
//////////////////////////////
@mixin breakpoint($breakpoint, $media: $breakpoint-default-media, $no-query: false, $base-font-size: $breakpoint-base-font-size) {
  // Query and Media String Defaults
  $query:         false !default;
  $query-holder:  false !default;
  $media-string:  false !default;
  $do-prefix:     false !default;

  $webkit:        false !default;
  $webkit-first:  true !default;
  $moz:           false !default;
  $moz-first:     true !default;
  $o:             false !default;
  $o-first:       true !default;
  $ms:            false !default;
  $ms-first:      true !default;

  // Holder for Count
  $first:         true  !default;

  // Reset Context
  @include TXkgcmVzZXQhIEdvIGF3YXkh;

  // Set Media Context
  $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh('media', $media);

  // Initialize Query String
  @if $media != 'all' or $breakpoint-force-media-all {
    $media-string: "#{$media} ";
  }
  @else {
    $media-string: "";
  }

  // If we have a single query, let's just work with that.
  @if is_breakpoint_list($breakpoint) == false {
    @each $prefix-query in $breakpoint-prefixed-queries {
      @if $do-prefix == false {
        $do-prefix: featureExists($prefix-query, $breakpoint);
      }
    }
    @if $do-prefix {
      @each $prfx in $breakpoint-prefixes {
        @if $prfx == 'webkit' {
          $webkit: breakpoint-switch($breakpoint, $media-string, true, $prfx, $base-font-size: $base-font-size);
        }

        @if $prfx == 'moz' {
          $moz: breakpoint-switch($breakpoint, $media-string, true, $prfx, $base-font-size: $base-font-size);
        }

        @if $prfx == 'o' {
          $o: breakpoint-switch($breakpoint, $media-string, true, $prfx, $base-font-size: $base-font-size);
        }

        @if $prfx == 'ms' {
          $ms: breakpoint-switch($breakpoint, $media-string, true, $prfx, $base-font-size: $base-font-size);
        }
      }
    }
    @else {
      $query: breakpoint-switch($breakpoint, $media-string, true, $base-font-size: $base-font-size);
    }
  }
  @else {
    // See if Prefix Query exists
    @each $prefix-query in $breakpoint-prefixed-queries {
      @if $do-prefix == false {
        $do-prefix: featureExists($prefix-query, $breakpoint);
      }
    }

    @if $do-prefix {
      @each $prfx in $breakpoint-prefixes {
        @each $bkpt in $breakpoint {
          @if $prfx == 'webkit' {
            @if $webkit-first {
              $webkit: breakpoint-switch($bkpt, $media-string, true, $prfx, $base-font-size: $base-font-size);
              $webkit-first: false;
            }
            @else {
              $webkit: join($webkit, breakpoint-switch($bkpt, $media-string, $prefix: $prfx, $base-font-size: $base-font-size));
            }
          }

          @if $prfx == 'moz' {
            @if $moz-first {
              $moz: breakpoint-switch($bkpt, $media-string, true, $prfx, $base-font-size: $base-font-size);
              $moz-first: false;
            }
            @else {
              $moz: join($moz, breakpoint-switch($bkpt, $media-string, $prefix: $prfx, $base-font-size: $base-font-size));
            }
          }

          @if $prfx == 'o' {
            @if $o-first {
              $o: breakpoint-switch($bkpt, $media-string, true, $prfx, $base-font-size: $base-font-size);
              $o-first: false;
            }
            @else {
              $o: join($o, breakpoint-switch($bkpt, $media-string, $prefix: $prfx, $base-font-size: $base-font-size));
            }
          }

          @if $prfx == 'ms' {
            @if $ms-first {
              $ms: breakpoint-switch($bkpt, $media-string, true, $prfx, $base-font-size: $base-font-size);
              $ms-first: false;
            }
            @else {
              $ms: join($ms, breakpoint-switch($bkpt, $media-string, $prefix: $prfx, $base-font-size: $base-font-size));
            }
          }
        }
      }
    }
    @else {
      @each $bkpt in $breakpoint {
        @if $first == true {
          $query: breakpoint-switch($bkpt, $media-string, true, $base-font-size: $base-font-size);
          $first: false;
        }
        @else {
          $query: join($query, breakpoint-switch($bkpt, $media-string, $base-font-size: $base-font-size));
        }
      }
    }
  }

  @if $breakpoint-no-queries {
    @if $no-query {
      $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh('no queries', true);
      @if $breakpoint-no-query-wrappers and type-of($no-query) == string {
        #{$no-query} & {
          @content;
          $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh('no query wrapper', $no-query);
        }
      }
      @else {
        @content;
      }
    }
  }
  @else {
    @if $breakpoint-no-query-wrappers and type-of($no-query) == string {
      #{$no-query} & {
        @content;
        $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh('no query wrapper', $no-query);
      }
    }
    @if $query {
      @media #{$query} {
        @content;
      }
    }
    @else {
      $pf-queries: $webkit, $moz, $o, $ms;
      $pf-query: ();

      @each $pfq in $pf-queries {
        @if $pfq {
          $pf-query: append($pf-query, $pfq, comma);
        }
      }
      @media #{$pf-query} {
        @content;
      }
    }
  }

  @include TXkgcmVzZXQhIEdvIGF3YXkh;
}

@function breakpoint-switch($breakpoint, $media-string, $first: false, $prefix: false, $base-font-size: $breakpoint-base-font-size) {
  // Feature/Value/Length/Query Placehoders:
  $feature:       false !default;
  $min-feature:   "min-#{$breakpoint-default-pair}" !default;
  $max-feature:   "max-#{$breakpoint-default-pair}" !default;
  $value:         false !default;
  $min-value:     false !default;
  $max-value:     false !default;
  $length:        false !default;
  $query:         false !default;

  $length: length($breakpoint);
  // Check to see if there is only one item.
  @if $length == 1 {
    $value: $breakpoint;
    @if type-of($breakpoint) == 'number' {
      $feature: $breakpoint-default-feature;
    }

    // If EM Breakpoints are active, do it!
    @if $breakpoint-to-ems and type-of($value) == 'number' {
      $value: breakpoint-to-base-em($value, $base-font-size);
    }
    // Build the Query
    $query: breakpoint-generate($media-string, $feature, $value, $first);
    // Set Context
    $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh($feature, $value);
  }
  @else if $length == 2 {
    // If both are numbers, we've got a double!
    @if type-of(nth($breakpoint, 1)) == 'number' and type-of(nth($breakpoint, 2)) == 'number' {
      // See which is larger.
      @if nth($breakpoint, 1) > nth($breakpoint, 2) {
        $min-value: nth($breakpoint, 2);
        $max-value: nth($breakpoint, 1);
      }
      @else {
        $min-value: nth($breakpoint, 1);
        $max-value: nth($breakpoint, 2);
      }

      // If EM Breakpoints are active, do it!
      @if $breakpoint-to-ems and type-of($min-value) == 'number' {
        $min-value: breakpoint-to-base-em($min-value, $base-font-size);
      }
      @if $breakpoint-to-ems and type-of($max-value) == 'number' {
        $max-value: breakpoint-to-base-em($max-value, $base-font-size);
      }

      // Min/Max for given
      $query: breakpoint-generate($media-string, $min-feature, $min-value, $first);
      $query: join($query, breakpoint-generate($media-string, $max-feature, $max-value));
      // Set Context
      $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh($min-feature, $min-value);
      $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh($max-feature, $max-value);
    }
    @else if type-of(nth($breakpoint, 1)) == 'string' and type-of(nth($breakpoint, 2)) == 'string' {
      @if breakpoint-string-value(nth($breakpoint, 1)) == true {
        $feature: nth($breakpoint, 1);
        $value: nth($breakpoint, 2);
      }
      @else {
        $feature: nth($breakpoint, 2);
        $value: nth($breakpoint, 1);
      }

      // If EM Breakpoints are active, do it!
      @if $breakpoint-to-ems and type-of($value) == 'number' {
        $value: breakpoint-to-base-em($value, $base-font-size);
      }

      // Build the Query
      $query: breakpoint-generate($media-string, $feature, $value, $first);
      // Set Context
      $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh($feature, $value);
    }
    @else {
      // Because we can have either the first or second option be the feature, we switch on it.
      @if type-of(nth($breakpoint, 1)) == string {
        $feature: nth($breakpoint, 1);
        $value: nth($breakpoint, 2);
      }
      @else if type-of(nth($breakpoint, 2)) == string {
        $feature: nth($breakpoint, 2);
        $value: nth($breakpoint, 1);
      }

      @if $feature == 'device-pixel-ratio' or $feature == 'min-device-pixel-ratio' or $feature == 'max-device-pixel-ratio' {
        $feature: breakpoint-experimental($feature, $prefix);
        //$value: 96 * $value * 1dpi;
        //        @if $feature == 'device-pixel-ratio' {
        //          $feature: 'resolution';
        //        }
        //        @else if $feature == 'min-device-pixel-ratio' {
        //          $feature: 'min-resolution';
        //        }
        //        @else if $feature == 'max-device-pixel-ratio' {
        //          $feature: 'max-resolution';
        //        }
      }

      // If EM Breakpoints are active, do it!
      @if $breakpoint-to-ems and type-of($value) == 'number' {
        $value: breakpoint-to-base-em($value, $base-font-size);
      }

      // Build the Query
      $query: breakpoint-generate($media-string, $feature, $value, $first);
      // Set Context
      $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh($feature, $value);
    }
  }
  @else if $length == 3 {
    @if type-of(nth($breakpoint, 1)) == 'string' {
      $feature: nth($breakpoint, 1);
      // See which is larger.
      @if nth($breakpoint, 2) > nth($breakpoint, 3) {
        $min-value: nth($breakpoint, 3);
        $max-value: nth($breakpoint, 2);
      }
      @else {
        $min-value: nth($breakpoint, 2);
        $max-value: nth($breakpoint, 3);
      }
    }
    @else {
      $feature: nth($breakpoint, 3);
      // See which is larger.
      @if nth($breakpoint, 1) > nth($breakpoint, 2) {
        $min-value: nth($breakpoint, 2);
        $max-value: nth($breakpoint, 1);
      }
      @else {
        $min-value: nth($breakpoint, 1);
        $max-value: nth($breakpoint, 2);
      }
    }

    // If EM Breakpoints are active, do it!
    @if $breakpoint-to-ems and type-of($min-value) == 'number' {
      $min-value: breakpoint-to-base-em($min-value, $base-font-size);
    }
    @if $breakpoint-to-ems and type-of($max-value) == 'number' {
      $max-value: breakpoint-to-base-em($max-value, $base-font-size);
    }

    @if breakpoint-min-max($feature) == true {
      @if $feature == 'device-pixel-ratio' {
        $min-feature: breakpoint-experimental('min-#{$feature}', $prefix);
        $max-feature: breakpoint-experimental('max-#{$feature}', $prefix);
      }
      @else {
        $min-feature: 'min-#{$feature}';
        $max-feature: 'max-#{$feature}';
      }

      // Min/Max for given
      $query: breakpoint-generate($media-string, $min-feature, $min-value, $first);
      $query: join($query, breakpoint-generate($media-string, $max-feature, $max-value));
      // Set Context
      $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh($min-feature, $min-value);
      $context: U2V0IHlvdXIgb3duIGRhbW4gY29udGV4dHMh($max-feature, $max-value);
    }
    @else {
      @warn '#{$feature} cannot have a min/max value!';
    }
  }

  @return $query;
}

@function breakpoint-generate($media, $feature, $value, $first: false) {
  // Media Query string to be returned
  $new-string: "";

  // If it's the first item, it gets special treatment
  @if $first == true {
    // And Statement
    $and: 'and ';
    // If $media is blank (i.e. all), remove and statement
    @if $media == '' {
      $and: '';
    }

    @if $feature != false {
      $new-string: #{$media}unquote("#{$and}(#{$feature}: #{$value})");
    }
    @else {
      $new-string: #{$media}unquote("#{$and}(#{$value})");
    }
  }

  @else {
    @if $feature != false {
      $new-string: unquote("and (#{$feature}: #{$value})");
    }
    @else {
      $new-string: unquote("and (#{$value})");
    }
  }

  @return $new-string;
}


bottom:40px; // height of the publish bar

top:0px; // height of the post title + margin

keep keep keep keep replace keep keep keep keep keep

top:69px; // height of the post title + margin


/*globals Handlebars, moment
*/
(function () {
    "use strict";
    Handlebars.registerHelper('dateFormat', function (context, block) {
        var f = block.hash.format || "MMM Do, YYYY",
            timeago = block.hash.timeago,
            date;
        if (timeago) {
            date = moment(context).fromNow();
        } else {
            date = moment(context).format(f);
        }
        return date;
    });
}());


/**
 * Countable is a script to allow for live paragraph-, word- and character-
 * counting on an HTML element.
 *
 * @author   Sacha Schmid (<https://github.com/RadLikeWhoa>)
 * @version  2.0.0
 * @license  MIT
 * @see      <http://radlikewhoa.github.io/Countable/>
 */

/**
 * Note: For the purpose of this internal documentation, arguments of the type
 * {Nodes} are to be interpreted as either {NodeList} or {Element}.
 */

;(function (global) {
  'use strict'

  /**
   * @private
   *
   * `_liveElements` holds all elements that have the live-counting
   * functionality bound to them.
   *
   * `_event` holds the event to handle the live counting, based on the
   * browser's capabilities.
   */

  var _liveElements = [],
      _event = 'oninput' in document ? 'input' : 'keyup'

  /**
   * `String.trim()` polyfill for non-supporting browsers. This is the
   * recommended polyfill on MDN.
   *
   * @see     <http://goo.gl/uYveB>
   * @see     <http://goo.gl/xjIxJ>
   *
   * @return  {String}  The original string with leading and trailing whitespace
   *                    removed.
   */

  if (!String.prototype.trim) {
    String.prototype.trim = function () {
      return this.replace(/^\s+|\s+$/g, '')
    }
  }

  /**
   * `ucs2decode` function from the punycode.js library.
   *
   * Creates an array containing the decimal code points of each Unicode
   * character in the string. While JavaScript uses UCS-2 internally, this
   * function will convert a pair of surrogate halves (each of which UCS-2
   * exposes as separate characters) into a single code point, matching
   * UTF-16.
   *
   * @see     <http://goo.gl/8M09r>
   * @see     <http://goo.gl/u4UUC>
   *
   * @param   {String}  string   The Unicode input string (UCS-2).
   *
   * @return  {Array}   The new array of code points.
   */

  function _decode (string) {
    var output = [],
        counter = 0,
        length = string.length,
        value, extra

    while (counter < length) {
      value = string.charCodeAt(counter++)

      if ((value & 0xF800) == 0xD800 && counter < length) {

        // High surrogate, and there is a next character.

        extra = string.charCodeAt(counter++)

        if ((extra & 0xFC00) == 0xDC00) {

          // Low surrogate.

          output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000)
        } else {
          output.push(value, extra)
        }
      } else {
        output.push(value)
      }
    }

    return output
  }

  /**
   * `_validateArguments` validates the arguments given to each function call.
   * Errors are logged to the console as warnings, but Countable fails silently.
   *
   * @private
   *
   * @param   {Nodes}     elements  The (collection of) element(s) to
   *                                validate.
   *
   * @param   {Function}  callback  The callback function to validate.
   *
   * @return  {Boolean}   Returns whether all arguments are vaild.
   */

  function _validateArguments (elements, callback) {
    var elementsValid = elements && ((Object.prototype.toString.call(elements) === '[object NodeList]' && elements.length) || (elements.nodeType === 1)),
        callbackValid = callback && typeof callback === 'function'

    if ('console' in window && 'warn' in console) {
      if (!elementsValid) console.warn('Countable: No valid elements were found')
      if (!callbackValid) console.warn('Countable: "' + callback + '" is not a valid callback function')
    }

    return elementsValid && callbackValid
  }

  /**
   * `_extendDefaults` is a function to extend a set of default options with the
   * ones given in the function call. Available options are described below.
   *
   * {Boolean}  hardReturns  Use two returns to seperate a paragraph instead of
   *                         one.
   * {Boolean}  stripTags    Strip HTML tags before counting the values.
   *
   * @private
   *
   * @param   {Object}  options  Countable allows the options described above.
   *                             They can be used in a function call to override
   *                             the default behaviour.
   *
   * @return  {Object}  The new options object.
   */

  function _extendDefaults (options) {
    var defaults = { hardReturns: false, stripTags: false }

    for (var prop in options) {
      if (defaults.hasOwnProperty(prop)) defaults[prop] = options[prop]
    }

    return defaults
  }

  /**
   * `_count` trims an element's value, optionally strips HTML tags and counts
   * paragraphs, words, characters and characters plus spaces.
   *
   * @private
   *
   * @param   {Element}  element  The element whose value is to be counted.
   *
   * @param   {Object}   options  The options to use for the counting.
   *
   * @return  {Object}   The object containing the number of paragraphs,
   *                     words, characters and characters plus spaces.
   */

  function _count (element, options) {
    var original = 'value' in element ? element.value : element.innerText || element.textContent,
        temp, trimmed

    /**
     * The initial implementation to allow for HTML tags stripping was created
     * @craniumslows while the current one was created by @Rob--W.
     *
     * @see <http://goo.gl/Exmlr>
     * @see <http://goo.gl/gFQQh>
     */

    if (options.stripTags) original = original.replace(/<\/?[a-z][^>]*>/gi, '')

    trimmed = original.trim()

    /**
     * Most of the performance improvements are based on the works of @epmatsw.
     *
     * @see <http://goo.gl/SWOLB>
     */

    return {
      paragraphs: trimmed ? (trimmed.match(options.hardReturns ? /\n{2,}/g : /\n+/g) || []).length + 1 : 0,
      words: trimmed ? (trimmed.replace(/['";:,.?¿\-!¡]+/g, '').match(/\S+/g) || []).length : 0,
      characters: trimmed ? _decode(trimmed.replace(/\s/g, '')).length : 0,
      all: _decode(original.replace(/[\n\r]/g, '')).length
    }
  }

  /**
   * `_loop` is a helper function to iterate over a collection, e.g. a NodeList
   * or an Array. The callback receives the current element as the single
   * parameter.
   *
   * @private
   *
   * @param  {Array}     which     The collection to iterate over.
   *
   * @param  {Function}  callback  The callback function to call on each
   *                               iteration.
   */

  function _loop (which, callback) {
    var len = which.length

    if (typeof len !== 'undefined') {
      while (len--) {
        callback(which[len])
      }
    } else {
      callback(which)
    }
  }

  /**
   * This is the main object that will later be exposed to other scripts. It
   * holds all the public methods that can be used to enable the Countable
   * functionality.
   */

  var Countable = {

    /**
     * The `live` method binds the counting handler to all given elements. The
     * event is either `oninput` or `onkeydown`, based on the capabilities of
     * the browser.
     *
     * @param   {Nodes}     elements   All elements that should receive the
     *                                 Countable functionality.
     *
     * @param   {Function}  callback   The callback to fire whenever the
     *                                 element's value changes. The callback is
     *                                 called with the relevant element bound to
     *                                 `this` and the counted values as the
     *                                 single parameter.
     *
     * @param   {Object}    [options]  An object to modify Countable's
     *                                 behaviour. Refer to `_extendDefaults` for
     *                                 a list of available options.
     *
     * @return  {Object}    Returns the Countable object to allow for chaining.
     */

    live: function (elements, callback, options) {
      var ops = _extendDefaults(options),
          bind = function (element) {
            var handler = function () {
                  callback.call(element, _count(element, ops))
                }

            _liveElements.push({ element: element, handler: handler })

            handler()

            if (element.addEventListener) {
              element.addEventListener(_event, handler, false)
            } else if (element.attachEvent) {
              element.attachEvent('on' + _event, handler)
            }
          }

      if (!_validateArguments(elements, callback)) return

      if (elements.length) {
        _loop(elements, bind)
      } else {
        bind(elements)
      }

      return this
    },

    /**
     * The `die` method removes the Countable functionality from all given
     * elements.
     *
     * @param   {Nodes}  elements  All elements whose Countable functionality
     *                             should be unbound.
     *
     * @return  {Object}  Returns the Countable object to allow for chaining.
     */

    die: function (elements) {
      if (!_validateArguments(elements, function () {})) return

      _loop(elements, function (element) {
        var liveElement

        _loop(_liveElements, function (live) {
          if (live.element === element) liveElement = live
        })

        if (!liveElement) return

        if (element.removeEventListener) {
          element.removeEventListener(_event, liveElement.handler, false)
        } else if (element.detachEvent) {
          element.detachEvent('on' + _event, liveElement.handler)
        }

        _liveElements.splice(_liveElements.indexOf(liveElement), 1)
      })

      return this
    },

    /**
     * The `once` method works mostly like the `live` method, but no events are
     * bound, the functionality is only executed once.
     *
     * @param   {Nodes}     elements   All elements that should receive the
     *                                 Countable functionality.
     *
     * @param   {Function}  callback   The callback to fire whenever the
     *                                 element's value changes. The callback is
     *                                 called with the relevant element bound to
     *                                 `this` and the counted values as the
     *                                 single parameter.
     *
     * @param   {Object}    [options]  An object to modify Countable's
     *                                 behaviour. Refer to `_extendDefaults`
     *                                 for a list of available options.
     *
     * @return  {Object}    Returns the Countable object to allow for chaining.
     */

    once: function (elements, callback, options) {
      if (!_validateArguments(elements, callback)) return

      _loop(elements, function (element) {
        callback.call(element, _count(element, _extendDefaults(options)))
      })

      return this
    },

    /**
     * The `enabled` method checks if the live-counting functionality is bound
     * to an element.
     *
     * @param   {Element}  element  A single Element.
     *
     * @return  {Boolean}  A boolean value representing whether Countable
     *                     functionality is bound to the given element.
     */

    enabled: function (element) {
      var isEnabled = false

      if (element && element.nodeType === 1) {
        _loop(_liveElements, function (live) {
          if (live.element === element) isEnabled = true
        })
      }

      return isEnabled
    }

  }

  /**
   * Expose Countable depending on the module system used across the
   * application. (Node / CommonJS, AMD, global)
   */

  if (typeof exports === 'object') {
    module.exports = Countable
  } else if (typeof define === 'function' && define.amd) {
    define(function () { return Countable })
  } else {
    global.Countable = Countable
  }
}(this))