text:
import re
import jieba
from itertools import chain

#####
# Common Functions
#####
CHARS_TO_IGNORE = [",", "?", "¿", ".", "!", "¡", ";", "；", ":", '""', "%", '"', "�", "ʿ", "·", "჻", "~", "՞",
                   "؟", "،", "।", "॥", "«", "»", "„", "“", "”", "「", "」", "‘", "’", "《", "》", "(", ")", "[", "]",
                   "{", "}", "=", "`", "_", "+", "<", ">", "…", "–", "°", "´", "ʾ", "‹", "›", "©", "®", "—", "→", "。",
                   "、", "﹂", "﹁", "‧", "～", "﹏", "，", "｛", "｝", "（", "）", "［", "］", "【", "】", "‥", "〽",
                   "『", "』", "〝", "〟", "⟨", "⟩", "〜", "：", "！", "？", "♪", "؛", "/", "\\", "º", "−", "^", "ʻ", "ˆ"]

def remove_special_characters(batch):
    batch["sentence"] = re.sub(chars_to_ignore_regex, '', batch["sentence"]).lower() + " "
    return batch

def extract_all_chars(batch):
    all_text = " ".join(batch["sentence"])
    vocab = list(set(all_text))
    return {"vocab": [vocab], "all_text": [all_text]}

#####
# Metric Helper Functions
#####
def tokenize_for_mer(text):
    tokens = list(filter(lambda tok: len(tok.strip()) > 0, jieba.lcut(text)))
    tokens = [[tok] if tok.isascii() else list(tok) for tok in tokens]
    return list(chain(*tokens))

def tokenize_for_cer(text):
    tokens = list(filter(lambda tok: len(tok.strip()) > 0, list(text)))
    return tokens


text: Download the [preprocessed dataset archive](#) to this folder, and unzip the archive

repo_name: HLTCHKUST/CI-AVSR

repo_language: Python

mime_type: text/plain


text:
package main

import (
	"encoding/json"
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/mitchellh/mapstructure"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/openstack"
	"github.com/rackspace/gophercloud/openstack/blockstorage/v2/extensions/volumeactions"
	"github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes"
	"github.com/rackspace/gophercloud/pagination"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/docker/go-plugins-helpers/volume"
)

type Config struct {
	DefaultVolSz   int //Default volume size in GiB
	MountPoint     string
	InitiatorIFace string //iface to use of iSCSI initiator
	HostUUID       string
	SocketGroup    string //Usergroup to use for the plugin socket

	// Cinder credentials
	Endpoint    string
	Username    string
	Password    string
	TenantID    string
	InitiatorIP string
	DomainName  string
	Region	    string
}

type CinderDriver struct {
	Client *gophercloud.ServiceClient
	Mutex  *sync.Mutex
	Conf   *Config
}

type ConnectorInfo struct {
	AccessMode string `mapstructure:"access_mode"`
	AuthUser   string `mapstructure:"auth_username"`
	AuthPass   string `mapstructure:"auth_password"`
	AuthMethod string `mapstructure:"auth_method"`
	TgtDisco   bool   `mapstructure:"target_discovered"`
	TgtIQN     string `mapstructure:"target_iqn"`
	TgtPortal  string `mapstructure:"target_portal"`
	VolumeID   string `mapstructure:"volume_id"`
	TgtLun     int    `mapstructure:"target_lun"`
	Encrypted  bool   `mapstructure:"encrypted"`
}

type ISCSITarget struct {
	Ip        string
	Port      string
	Portal    string
	Iqn       string
	Lun       string
	Device    string
	Discovery string
}

const RootLevelFolder string = "volume_content"

func processConfig(cfg string) (Config, error) {
	var conf Config
	content, err := ioutil.ReadFile(cfg)
	if err != nil {
		log.Fatal("Error reading config file: ", err)
	}
	err = json.Unmarshal(content, &conf)
	if err != nil {
		log.Fatal("Error parsing json config file: ", err)
	}

	if conf.MountPoint == "" {
		conf.MountPoint = "/var/lib/cinder/mount"
	}
	if conf.InitiatorIFace == "" {
		conf.InitiatorIFace = "default"
	}
	if conf.DefaultVolSz == 0 {
		conf.DefaultVolSz = 1
	}
	if conf.HostUUID == "" {
		conf.HostUUID, _ = getRootDiskUUID()
		log.Infof("Set node UUID to: %s", conf.HostUUID)
	}
	conf.InitiatorIP, _ = getIPv4ForIFace(conf.InitiatorIFace)
	log.Infof("Using config file: %s", cfg)
	log.Infof("Set InitiatorIFace to: %s", conf.InitiatorIFace)
	log.Infof("Set node InitiatorIP to: %s", conf.InitiatorIP)
	log.Infof("Set DefaultVolSz to: %d GiB", conf.DefaultVolSz)
	log.Infof("Set Endpoint to: %s", conf.Endpoint)
	log.Infof("Set Username to: %s", conf.Username)
	log.Infof("Set TenantID to: %s", conf.TenantID)
	return conf, nil
}

func New(cfgFile string) CinderDriver {
	conf, err := processConfig(cfgFile)
	isV3 := strings.Contains(conf.Endpoint, "v3")
	if err != nil {
		log.Fatal("Error processing cinder driver config file: ", err)
	}

	_, err = os.Lstat(conf.MountPoint)
	if os.IsNotExist(err) {
		if err := os.MkdirAll(conf.MountPoint, 0755); err != nil {
			log.Fatal("Failed to create Mount directory during driver init: %v", err)
		}
	}

	auth := gophercloud.AuthOptions{
		IdentityEndpoint: conf.Endpoint,
		Username:         conf.Username,
		Password:         conf.Password,
		TenantID:         conf.TenantID,
		AllowReauth:      true,
	}

	if isV3 == true && conf.DomainName == "" {
		log.Warning("V3 endpoint specified, but DomainName not set!")
		log.Warning("Setting to \"Default\", maybe it'll work.")
		auth.DomainName = "Default"
	}

	if conf.DomainName != "" && isV3 == true {
		log.Info("Authorizing to a V3 Endpoint")
		auth.DomainName = conf.DomainName
	}

	// set the default SocketGroup to root, which should work on most Linuxes
	if conf.SocketGroup == "" {
		conf.SocketGroup = "root"
	}

	providerClient, err := openstack.AuthenticatedClient(auth)
	if err != nil {
		log.Fatal("Error initiating gophercloud provider client: ", err)
	}

	// Set the default region to RegionOne, the OpenStack default
	endpointOpts := gophercloud.EndpointOpts{Region: conf.Region}
	if endpointOpts.Region == "" {
		endpointOpts.Region = "RegionOne"
	}

	client, err := openstack.NewBlockStorageV2(providerClient, endpointOpts)
	if err != nil {
		log.Fatal("Error initiating gophercloud cinder client: ", err)
	}

	d := CinderDriver{
		Conf:   &conf,
		Mutex:  &sync.Mutex{},
		Client: client,
	}
	return d
}

func (d CinderDriver) parseOpts(r *volume.CreateRequest) volumes.CreateOpts {
	opts := volumes.CreateOpts{}
	opts.Size = d.Conf.DefaultVolSz
	for k, v := range r.Options {
		log.Debugf("Option: %s = %s", k, v)
		switch k {
		case "size":
			vSize, err := strconv.Atoi(v)
			if err == nil {
				opts.Size = vSize
			}
		case "type":
			if r.Options["type"] != "" {
				opts.VolumeType = v
			}
		}
	}
	// We need to tag these volumes as being created by Docker *somewhere* so
	// we'll do it here.  In the future adding further descriptions is ok but
	// we should try and keep a tag of some sort here if we can
	opts.Description = "Docker volume."
	return opts
}

func (d CinderDriver) getByName(name string) (volumes.Volume, error) {
	log.Debug("getVolByName: `", name, "`")
	opts := volumes.ListOpts{Name: name}
	vols := volumes.List(d.Client, opts)
	var vol volumes.Volume
	err := vols.EachPage(func(page pagination.Page) (bool, error) {
		vList, err := volumes.ExtractVolumes(page)
		if err != nil {
			log.Errorf("Get Volume Error: %s", err)
			return false, err
		}

		for _, v := range vList {
			log.Debugf("querying volume: %+v\n", v)
			if v.Name == name {
				vol = v
				log.Debug("Found Volume ID: ", vol.ID)
				return true, nil
			}
		}
		log.Error("Volume Not Found!")
		return false, errors.New("Volume Not Found")
	})
	if err != nil {
		log.Errorf("Extract Volume Error: %s", err)
		return volumes.Volume{}, err
	}

	return vol, nil
}

func (d CinderDriver) Create(r *volume.CreateRequest) error {
	// TODO(jdg): Right now we have a weird mix for some of our semantics.  We
	// wanted to be able to dynamically create, but create can be called when a
	// volume already exists and is going to be used on another Docker node (ie
	// things like compose); we need to look at reworking things to NOT use
	// names to access Cinder volumes or some way to differentiate a create vs
	// a "use"
	log.Infof("Create volume %s on %s", r.Name, "Cinder")
	d.Mutex.Lock()
	defer d.Mutex.Unlock()

	vol, err := d.getByName(r.Name)
	if err != nil {
		log.Errorf("Error getting existing Volume by Name: (volume %s, error %s)", vol, err.Error())
		return err
	}
	// FIXME(jdg): Keep in mind, NotFound isn't the only error we can get here,
	// we can also receive a "Multiple matches" error if there are duplicate
	// names.

	opts := d.parseOpts(r)
	opts.Name = r.Name
	log.Debugf("Creating with options: %+v", opts)

	_, err = volumes.Create(d.Client, opts).Extract()
	if err != nil {
		log.Errorf("Failed to Create volume: %s\nEncountered error: %s", r.Name, err)
		return err
	}
	path := filepath.Join(d.Conf.MountPoint, r.Name)
	if err := os.Mkdir(path, os.ModeDir); err != nil {
		log.Errorf("Failed to create Mount directory: %v", err)
		return err
	}
	return nil
}

func (d CinderDriver) Remove(r *volume.RemoveRequest) error {
	log.Info("Remove/Delete Volume: ", r.Name)
	vol, err := d.getByName(r.Name)
	log.Debugf("Remove/Delete Volume ID: %s", vol.ID)
	if err != nil {
		log.Errorf("Failed to retrieve volume named: ", r.Name, "during Remove operation", err)
		return err
	}
	errRes := volumes.Delete(d.Client, vol.ID)
	log.Debugf("Response from Delete: %+v\n", errRes)
	if errRes.Err != nil {
		log.Errorf("Failed to Delete volume: %s\nEncountered error: %s", vol, errRes)
		log.Debugf("Error message: %s", errRes.ExtractErr())
		return errRes.ExtractErr()
	}
	path := filepath.Join(d.Conf.MountPoint, r.Name)
	if err := os.Remove(path); err != nil {
		log.Error("Failed to remove Mount directory: %v", err)
		return err
	}
	return nil
}

func (d CinderDriver) Path(r *volume.PathRequest) (*volume.PathResponse, error) {
	log.Info("Retrieve path info for volume: `", r.Name, "`")
	path := filepath.Join(d.Conf.MountPoint, r.Name, RootLevelFolder)
	log.Debug("Path reported as: ", path)
	return &volume.PathResponse{Mountpoint: path}, nil
}

func (d CinderDriver) Mount(r *volume.MountRequest) (*volume.MountResponse, error) {
	d.Mutex.Lock()
	defer d.Mutex.Unlock()

	hostname, _ := os.Hostname()
	log.Infof("Mounting volume %+v on %s", r, hostname)
	vol, err := d.getByName(r.Name)
	if err != nil {
		log.Errorf("Failed to retrieve volume named: ", r.Name, "during Mount operation", err)
		return &volume.MountResponse{}, err
	}
	if vol.ID == "" {
		log.Error("Volume Not Found!")
		err := errors.New("Volume Not Found")
		return &volume.MountResponse{}, err
	}
	if vol.Status == "creating" {
		// NOTE(jdg):  This may be a successive call after a create which from
		// the docker volume api can be quite speedy.  Take a short pause and
		// check the status again before proceeding
		time.Sleep(time.Second * 5)
		vol, err = d.getByName(r.Name)
	}

	if err != nil {
		log.Errorf("Failed to retrieve volume named: ", r.Name, "during Mount operation", err)
		return &volume.MountResponse{}, err
	}

	if vol.Status != "available" {
		log.Debugf("Volume info: %+v\n", vol)
		log.Errorf("Invalid volume status for Mount request, volume is: %s but must be available", vol.Status)
		err := errors.New("Invalid volume status for Mount request")
		return &volume.MountResponse{}, err
	}
	volumeactions.Reserve(d.Client, vol.ID)

	iface := d.Conf.InitiatorIFace
	netDev, _ := net.InterfaceByName(iface)
	IPs, _ := net.InterfaceAddrs()
	log.Debugf("iface: %+v\n Addrs: %+v", netDev, IPs)

	log.Debug("Gather up initiator IQNs...")
	initiator, err := GetInitiatorIqns()
	if err != nil {
		log.Error("Failed to retrieve Initiator name!")
		return &volume.MountResponse{}, err
	}
	// TODO(ebalduf): Change assumption that we have only one Initiator defined
	log.Debugf("Value of IPs is=%+v\n", IPs)
	connectorOpts := volumeactions.ConnectorOpts{
		IP:        d.Conf.InitiatorIP,
		Host:      hostname,
		Initiator: initiator[0],
		Wwpns:     []string{},
		Wwnns:     "",
		Multipath: false,
		Platform:  "x86",
		OSType:    "linux",
	}
	log.Debug("Issue InitializeConnection...")
	response := volumeactions.InitializeConnection(d.Client, vol.ID, &connectorOpts)
	log.Debugf("Response from InitializeConnection: %+v\n", response)
	data := response.Body.(map[string]interface{})["connection_info"].(map[string]interface{})["data"]
	var con ConnectorInfo
	mapstructure.Decode(data, &con)
	path, device, err := attachVolume(&con, "default")
	log.Debug("iSCSI connection done")
	if path == "" || device == "" && err == nil {
		log.Error("Missing path or device, but err not set?")
		log.Debug("Path: ", path, " ,Device: ", device)
		return &volume.MountResponse{}, err

	}
	if err != nil {
		log.Errorf("Failed to perform iscsi attach of volume %s: %v", r.Name, err)
		return &volume.MountResponse{}, err
	}

	if GetFSType(device) == "" {
		//TODO(jdg): Enable selection of *other* fs types
		log.Debugf("Formatting device")
		err := FormatVolume(device, "ext4")
		if err != nil {
			err := errors.New("Failed to format device")
			log.Error(err)
		    return &volume.MountResponse{}, err
		}
	}
	path = filepath.Join(d.Conf.MountPoint, r.Name)
	if mountErr := Mount(device, path); mountErr != nil {
		err := errors.New("Problem mounting docker volume ")
		log.Error(err)
		return &volume.MountResponse{}, err
	}

	// NOTE(jdg): Cinder will barf if you provide both Instance and HostName
	// which is kinda silly... but it is what it is
	attachOpts := volumeactions.AttachOpts{
		MountPoint:   path,
		InstanceUUID: d.Conf.HostUUID,
		HostName:     "",
		Mode:         "rw"}
	log.Debug("Call gophercloud Attach...")
	attRes := volumeactions.Attach(d.Client, vol.ID, &attachOpts)
	log.Debugf("Attach results: %+v", attRes)

	// Create root level folder after mounting.
	pathForDocker := filepath.Join(path, RootLevelFolder)
	os.Mkdir(pathForDocker, os.ModeDir)

	return &volume.MountResponse{Mountpoint: pathForDocker}, nil
}

func (d CinderDriver) Unmount(r *volume.UnmountRequest) error {
	log.Infof("Unmounting volume: %+v", r)
	d.Mutex.Lock()
	defer d.Mutex.Unlock()
	vol, err := d.getByName(r.Name)
	if vol.ID == "" {
		log.Errorf("Request to Unmount failed because volume `%s` could not be found", r.Name)
		err := errors.New("Volume Not Found")
		return err
	}

	if err != nil {
		log.Errorf("Failed to retrieve volume named: `", r.Name, "` during Unmount operation", err)
		return err
	}

	if umountErr := Umount(filepath.Join(d.Conf.MountPoint, r.Name)); umountErr != nil {
		if umountErr.Error() == "Volume is not mounted" {
			log.Warning("Request to unmount volume, but it's not mounted")
			return nil
		} else {
			return umountErr
		}
	}
	// NOTE(jdg): So there's a couple issues with how Docker works here.  If
	// you are trying to attach and it fails, it kindly goes through and does
	// an Unmount to clean up anything that went bad, BUT that creates a
	// problem here.  Say for example you try to attach an in-use volume, we
	// don't want to rip that out from under wherever it's currently being used

	// NOTE(jdg): Don't rely on things like `df --output=source mounpoint`
	// that's no good for error situations.

	tgt, portal := getTgtInfo(vol)
	iscsiDetachVolume(tgt, portal)
	log.Debug("Terminate Connection")
	iface := d.Conf.InitiatorIFace
	netDev, _ := net.InterfaceByName(iface)
	IPs, _ := net.InterfaceAddrs()
	log.Debugf("iface: %+v\n Addrs: %+v", netDev, IPs)
	initiators, err := GetInitiatorIqns()
	if err != nil {
		log.Error("Failed to retrieve Initiator name!")
		return err
	}
	hostname, _ := os.Hostname()
	// TODO(ebalduf): Change assumption that we have only one Initiator defined
	// TODO(jdg): For now we're only supporting linux, but in the future we'll
	// need to get rid of the hard coded Platform/OSType and fix this up for
	// things like say Windows
	log.Debugf("IPs=%+v\n", IPs)
	connectorOpts := volumeactions.ConnectorOpts{
		IP:        d.Conf.InitiatorIP,
		Host:      hostname,
		Initiator: initiators[0],
		Wwpns:     []string{},
		Wwnns:     "",
		Multipath: false,
		Platform:  "x86",
		OSType:    "linux",
	}
	log.Debugf("Unreserve volume: %s", vol.ID)
	volumeactions.Unreserve(d.Client, vol.ID)
	log.Debugf("Terminate connection for volume: %s", vol.ID)
	volumeactions.TerminateConnection(d.Client, vol.ID, &connectorOpts)
	log.Debugf("Detach volume: %s", vol.ID)
	volumeactions.Detach(d.Client, vol.ID)
	return nil
}

func (d CinderDriver) Capabilities() *volume.CapabilitiesResponse {
	return &volume.CapabilitiesResponse{Capabilities: volume.Capability{Scope: "global"}}
}

func (d CinderDriver) Get(r *volume.GetRequest) (*volume.GetResponse, error) {
	log.Info("Get volume: ", r.Name)
	vol, err := d.getByName(r.Name)
	if err != nil {
		log.Errorf("Failed to retrieve volume `%s`: %s", r.Name, err.Error())
		return &volume.GetResponse{}, err
	}
	if vol.ID == "" {
		log.Errorf("Failed to retrieve volume named: ", r.Name, "during Get operation", err)
		err = errors.New("Volume Not Found")
		return &volume.GetResponse{}, err
	}

	// NOTE(jdg): Volume can exist but not necessarily be attached, this just
	// gets the volume object and where it "would" be attached, it may or may
	// not currently be attached, but we don't care here
	path := filepath.Join(d.Conf.MountPoint, r.Name, RootLevelFolder)
	return &volume.GetResponse{Volume: &volume.Volume{Name: r.Name, Mountpoint: path}}, nil
}

func (d CinderDriver) List() (*volume.ListResponse, error) {
	log.Info("List volumes")
	var vols []*volume.Volume
	pager := volumes.List(d.Client, volumes.ListOpts{})
	pager.EachPage(func(page pagination.Page) (bool, error) {
		vlist, _ := volumes.ExtractVolumes(page)
		for _, v := range vlist {
	        path := filepath.Join(d.Conf.MountPoint, v.Name, RootLevelFolder)
			vols = append(vols, &volume.Volume{Name: v.Name, Mountpoint: path})
		}
		return true, nil
	})
	return &volume.ListResponse{Volumes: vols}, nil
}

func iscsiDetachVolume(tgt string, portal string) (err error) {
	target := &ISCSITarget{
		Ip:     portal,
		Portal: portal,
		Iqn:    tgt,
	}
	err = iscsiDisableDelete(target)
	return
}

func attachVolume(c *ConnectorInfo, iface string) (path, device string, err error) {
	log.Debugf("Connector is: %+v", c)
	path = "/dev/disk/by-path/ip-" + c.TgtPortal + "-iscsi-" + c.TgtIQN + "-lun-" + strconv.Itoa(c.TgtLun)

	if iscsiSupported() == false {
		err := errors.New("Unable to attach, open-iscsi tools not found on host")
		log.Error(err)
		return path, device, err
	}

	// Make sure it's not already attached
	if waitForPathToExist(path, 1) {
		log.Debug("Get device file from path: ", path)
		device = strings.TrimSpace(getDeviceFileFromIscsiPath(path))
		return path, device, nil
	}

	err = LoginWithChap(c.TgtIQN, c.TgtPortal, c.AuthUser, c.AuthPass, iface)
	if err != nil {
		log.Error(err)
		return path, device, err
	}
	if waitForPathToExist(path, 5) {
		device = strings.TrimSpace(getDeviceFileFromIscsiPath(path))
		log.Debugf("Attached volume at (path, devfile): %s, %s", path, device)
		return path, device, nil
	}
	return path, device, nil
}

repo_name: j-griffith/cinder-docker-driver

repo_language: Go


text:
package main

import (
	"flag"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/go-plugins-helpers/volume"
	"os"
	"path/filepath"
)

const (
	VERSION = "0.12"
)

var (
	defaultDir = filepath.Join(volume.DefaultDockerRootDirectory, "cinder")
)

func main() {
	showVersion := flag.Bool("version", false, "Display version number of plugin and exit")
	flag.Parse()
	if *showVersion == true {
		fmt.Println("Version: ", VERSION)
		os.Exit(0)
	}

	cfgFile := flag.String("config", "/var/lib/cinder/dockerdriver/config.json", "path to config file")
	debug := flag.Bool("debug", true, "enable debug logging")
	flag.Parse()
	if *debug == true {
		log.SetLevel(log.DebugLevel)
	} else {
		log.SetLevel(log.InfoLevel)
	}
	log.Info("Starting cinder-docker-driver version: ", VERSION)
	d := New(*cfgFile)
	h := volume.NewHandler(d)
	log.Info(h.ServeUnix("cinder", 0))
}


text:
.PHONY: all clean

all: cinder

cinder:
	if [ ! -d ./vendor ]; then dep ensure; fi
	go build -i -o _bin/cdd

clean:
	go clean -r -x
	-rm -rf _bin/*


text:
#!/bin/sh
set -e
#
# This script provides a mechanism for easy installation of the
# cinder-docker-driver, use with curl or wget:
#  'curl -sSl https://raw.githubusercontent.com/j-griffith/cinder-docker-driver/master/install.sh | sh''
# or
#  'wget -qO- https://raw.githubusercontent.com/j-griffith/cinder-docker-driver/master/install.sh | sh'

VERSION=${1:-release}
BIN_NAME=cdd
DRIVER_URL="https://github.com/j-griffith/cinder-docker-driver/releases/download/v0.13/cinder-docker-driver"
SRC_BIN=./_bin/cdd
BIN_DIR="/usr/bin"

do_install() {
mkdir -p /var/lib/cinder/dockerdriver
mkdir -p /var/lib/cinder/mount
rm $BIN_DIR/$BIN_NAME || true
echo "Version is: ${VERSION}"
if [ "${VERSION}" = 'source' ]; then
  cp ./_bin/cdd $BIN_DIR/$BIN_NAME
else
  echo "Installing release from github repo..."
  curl -sSL -o $BIN_DIR/$BIN_NAME $DRIVER_URL
  chmod +x $BIN_DIR/$BIN_NAME
fi

echo "
[Unit]
Description=\"Cinder Docker Plugin daemon\"
Before=docker.service
Requires=cinder-docker-driver.service

[Service]
TimeoutStartSec=0
ExecStart=/usr/bin/cdd &

[Install]
WantedBy=docker.service" >/etc/systemd/system/cinder-docker-driver.service

chmod 644 /etc/systemd/system/cinder-docker-driver.service
systemctl daemon-reload
systemctl enable cinder-docker-driver
}

do_install


text:
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
#   name = "github.com/user/project"
#   version = "1.0.0"
#
# [[constraint]]
#   name = "github.com/user/project2"
#   branch = "dev"
#   source = "github.com/myfork/project2"
#
# [[override]]
#  name = "github.com/x/y"
#  version = "2.4.0"

[[constraint]]
  name = "github.com/Sirupsen/logrus"
  version = "1.0.4"

[[constraint]]
  branch = "master"
  name = "github.com/docker/go-plugins-helpers"

[[constraint]]
  branch = "master"
  name = "github.com/mitchellh/mapstructure"

[[constraint]]
  branch = "master"
  name = "github.com/rackspace/gophercloud"


text:
package main

import (
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes"
	"net"
	"os"
	"os/exec"
	"strings"
	"time"
)

func removeNetmask(fulladdr string) string {
	wordcount := 0
	for i := range fulladdr {
		if fulladdr[i] != '/' {
			wordcount++
		} else {
			break
		}
	}
	return fulladdr[0:wordcount]
}

func GetInitiatorIqns() ([]string, error) {
	var iqns []string
	out, err := exec.Command("sudo", "cat", "/etc/iscsi/initiatorname.iscsi").CombinedOutput()
	log.Debugf("output and err from cat... %s, %s", out, err)
	if err != nil {
		log.Error("Error encountered gathering initiator names: ", err)
		return nil, err
	}
	lines := strings.Split(string(out), "\n")
	for _, l := range lines {
		log.Debugf("Inspect line: %s", l)
		if strings.Contains(l, "InitiatorName=") {
			iqns = append(iqns, strings.Split(l, "=")[1])
		}
	}
	log.Debugf("Found the following iqns: %s", iqns)
	return iqns, nil
}

func waitForPathToExist(fileName string, numTries int) bool {
	log.Info("Waiting for path")
	for i := 0; i < numTries; i++ {
		_, err := os.Stat(fileName)
		if err == nil {
			log.Debug("path found: ", fileName)
			return true
		}
		if err != nil && !os.IsNotExist(err) {
			return false
		}
		time.Sleep(time.Second)
	}
	return false
}

func getDeviceFileFromIscsiPath(iscsiPath string) (devFile string) {
	log.Debug("Begin utils.getDeviceFileFromIscsiPath: ", iscsiPath)
	out, err := exec.Command("sudo", "ls", "-la", iscsiPath).CombinedOutput()
	if err != nil {
		return
	}
	d := strings.Split(string(out), "../../")
	log.Debugf("Found device: %s", d)
	devFile = "/dev/" + d[1]
	devFile = strings.TrimSpace(devFile)
	log.Debug("using base of: ", devFile)
	return
}

func getTgtInfo(v volumes.Volume) (target string, portal string) {
	out, err := exec.Command("sudo", "ls", "-l", "/dev/disk/by-path").CombinedOutput()
	if err != nil {
		log.Error("Failed to list contents of /dev/disk/by-path/: ", err)
		return
	}
	lines := strings.Split(string(out), "\n")
	for _, line := range lines {
		if strings.Contains(line, v.ID) {
			target = strings.Split((strings.Split(line, "-iscsi-")[1]), "-lun-")[0]
			portal = strings.Split((strings.Split(line, " ip-")[1]), "-iscsi-")[0]
		}
	}
	return
}

func getTgtFromMountPoint(mountpoint string) (target string, portal string) {
	log.Infof("Get iSCSI target for path %s", mountpoint)
	out, err := exec.Command("sudo", "df", "--output=source", mountpoint).CombinedOutput()
	if err != nil {
		log.Error("Failed to obtain device info from df cmd: ", err)
		return
	}

	device := "../../" + strings.Split(strings.Fields(string(out))[1], "/")[2]
	log.Debug("Formed the device: ", device)
	out, err = exec.Command("sudo", "ls", "-l", "/dev/disk/by-path").CombinedOutput()
	if err != nil {
		log.Error("Failed to list contents of /dev/disk/by-path/: ", err)
		return
	}
	lines := strings.Split(string(out), "\n")
	for _, line := range lines {
		log.Debugf("check line %s for %s...", line, device)
		if strings.Contains(line, device) {
			target = strings.Split((strings.Split(line, "-iscsi-")[1]), "-lun-")[0]
			portal = strings.Split((strings.Split(line, " ip-")[1]), "-iscsi-")[0]
		}
	}
	return
}

func iscsiSupported() bool {
	_, err := exec.Command("iscsiadm", "-h").CombinedOutput()
	if err != nil {
		log.Debug("iscsiadm tools not found on this host")
		return false
	}
	return true
}

func iscsiDiscovery(portal string) (targets []string, err error) {
	log.Debugf("Begin utils.iscsiDiscovery (portal: %s)", portal)
	out, err := exec.Command("sudo", "iscsiadm", "-m", "discovery", "-t", "sendtargets", "-p", portal).CombinedOutput()
	if err != nil {
		log.Error("Error encountered in sendtargets cmd: ", out)
		return
	}
	targets = strings.Split(string(out), "\n")
	return

}

func iscsiLogin(tgt *ISCSITarget) (err error) {
	log.Debugf("Begin utils.iscsiLogin: %v", tgt)
	_, err = exec.Command("sudo", "iscsiadm", "-m", "node", "-p", tgt.Ip, "-T", tgt.Iqn, "--login").CombinedOutput()
	if err != nil {
		log.Errorf("Received error on login attempt: %v", err)
	}
	return err
}

func iscsiDisableDelete(tgt *ISCSITarget) (err error) {
	log.Debugf("Begin utils.iscsiDisableDelete: %v", tgt)
	_, err = exec.Command("sudo", "iscsiadm", "-m", "node", "-T", tgt.Iqn, "--portal", tgt.Ip, "-u").CombinedOutput()
	if err != nil {
		log.Debugf("Error during iscsi logout: ", err)
		//return
	}
	_, err = exec.Command("sudo", "iscsiadm", "-m", "node", "-o", "delete", "-T", tgt.Iqn).CombinedOutput()
	return
}

func GetFSType(device string) string {
	log.Debugf("Begin utils.GetFSType: %s", device)
	fsType := ""
	out, err := exec.Command("blkid", device).CombinedOutput()
	if err != nil {
		return fsType
	}

	if strings.Contains(string(out), "TYPE=") {
		for _, v := range strings.Split(string(out), " ") {
			if strings.Contains(v, "TYPE=") {
				fsType = strings.Split(v, "=")[1]
				fsType = strings.Replace(fsType, "\"", "", -1)
			}
		}
	}
	return fsType
}

func FormatVolume(device, fsType string) error {
	log.Debugf("Begin utils.FormatVolume: %s, %s", device, fsType)
	cmd := "mkfs.ext4"
	if fsType == "xfs" {
		cmd = "mkfs.xfs"
	}
	log.Debug("Perform ", cmd, " on device: ", device)
	out, err := exec.Command(cmd, "-F", device).CombinedOutput()
	log.Debug("Result of mkfs cmd: ", string(out))
	return err
}

func Mount(device, mountpoint string) error {
	log.Debugf("Begin utils.Mount device: %s on: %s", device, mountpoint)
	out, err := exec.Command("mkdir", mountpoint).CombinedOutput()
	out, err = exec.Command("mount", device, mountpoint).CombinedOutput()
	log.Debug("Response from mount ", device, " at ", mountpoint, ": ", string(out))
	if err != nil {
		log.Error("Error in mount: ", err)
	}
	return err
}

func Umount(mountpoint string) error {
	log.Debugf("Begin utils.Umount: %s", mountpoint)
	out, err := exec.Command("umount", mountpoint).CombinedOutput()
	if err != nil {
		log.Warningf("Unmount call returned error: %s (%s)", err, out)
		if strings.Contains(string(out), "not mounted") {
			log.Debug("Ignore request for unmount on unmounted volume")
			err = errors.New("Volume is not mounted")
		}
	}
	return err
}

func iscsiadmCmd(args []string) error {
	log.Debugf("Being utils.iscsiadmCmd: iscsiadm %+v", args)
	resp, err := exec.Command("iscsiadm", args...).CombinedOutput()
	if err != nil {
		log.Error("Error encountered running iscsiadm ", args, ": ", resp)
		log.Error("Error message: ", err)
	}
	return err
}

func LoginWithChap(tiqn, portal, username, password, iface string) error {
	args := []string{"-m", "node", "-T", tiqn, "-p", portal}
	createArgs := append(args, []string{"--interface", iface, "--op", "new"}...)
	log.Debugf("Create the node entry using args:  %+v", args)

	if _, err := exec.Command("iscsiadm", createArgs...).CombinedOutput(); err != nil {
		log.Errorf("Error running iscsiadm %s", createArgs)
		log.Error(os.Stderr, "Error running iscsiadm node create: ", err)
		return err
	}

	authMethodArgs := append(args, []string{"--op=update", "--name", "node.session.auth.authmethod", "--value=CHAP"}...)
	if out, err := exec.Command("iscsiadm", authMethodArgs...).CombinedOutput(); err != nil {
		log.Error("Error running iscsiadm set authmethod: ", err, "{", out, "}")
		return err
	}

	log.Debug("Update username to: ", username)
	authUserArgs := append(args, []string{"--op=update", "--name", "node.session.auth.username", "--value=" + username}...)
	if _, err := exec.Command("iscsiadm", authUserArgs...).CombinedOutput(); err != nil {
		log.Error(os.Stderr, "Error running iscsiadm set authuser: ", err)
		return err
	}

	log.Debug("Update password to: ", password)
	authPasswordArgs := append(args, []string{"--op=update", "--name", "node.session.auth.password", "--value=" + password}...)
	if _, err := exec.Command("iscsiadm", authPasswordArgs...).CombinedOutput(); err != nil {
		log.Error(os.Stderr, "Error running iscsiadm set authpassword: ", err)
		return err
	}

	loginArgs := append(args, []string{"--login"}...)
	if _, err := exec.Command("iscsiadm", loginArgs...).CombinedOutput(); err != nil {
		log.Error(os.Stderr, "Error running iscsiadm login: ", err, loginArgs)
		return err
	}
	log.Infof("Logged into iSCSI target without error: %+v", loginArgs)
	return nil
}

func getDefaultIFace() (string, error) {
	cmd := "ip route get 8.8.8.8 | head -1 | cut -d ' ' -f5"
	iface, err := exec.Command("sh", "-c", cmd).Output()
	if err != nil {
		log.Errorf("Error detecting default iface: %s", cmd)
		log.Errorf("%s", err)
		return "", err
	} else {
		return string(iface), nil
	}

	return string(iface), nil
}

func getIPv4ForIFace(ifname string) (string, error) {
	interfaces, _ := net.Interfaces()
	if ifname == "default" {
		ifname, _ = getDefaultIFace()
	}

	for _, inter := range interfaces {
		if inter.Name == ifname {
			if addrs, err := inter.Addrs(); err == nil {
				for _, addr := range addrs {
					switch ip := addr.(type) {
					case *net.IPNet:
						if ip.IP.DefaultMask() != nil {
							return ip.IP.String(), nil
						}
					}
				}
			}
		}
	}
	return "", nil
}

func getRootDiskUUID() (string, error) {
	cmd := "mount | grep \" / \"|cut -d' ' -f 1"
	device, err := exec.Command("sh", "-c", cmd).Output()
	if err != nil {
		log.Errorf("Error detecting root disk: %s (%s)", cmd, device)
		return "", err
	}

	cmd = fmt.Sprintf("blkid %s|cut -d' ' -f2", device)
	uuidString, _ := exec.Command("sh", "-c", cmd).Output()
	uuid := strings.Split(string(uuidString), "UUID=\"")[1]
	uuid = strings.Split(string(uuid), " ")[0]
	uuid = strings.Replace(uuid, "\"", "", -1)
	return string(uuid), nil
}


text:
# Cinder Docker Driver

Docker Volume Plugin to enable consumption of OpenStack-Cinder Block Storage
with containers.

## Install

### Build From Source
Obviously you'll need to have a golang dev environment setup, one piece of that
includes the golang dep tool.  You can find install instructions and usage info
here:  https://github.com/golang/dep

For the most part just installing the tool is good enough, the Makefile will
take care of it's usage for you.

```shell
git clone https://github.com/j-griffith/cinder-docker-driver
cd cinder-docker-driver
make clean; make
sudo ./install.sh
```

NOTE the specification of the `source` argument to the install script, this
will result in the install script copying your locally built binary
(```./_bin/cdd```) into the install directory and creating the systemd file.

Omitting the `source` arg (or providing any other argument) will instruct the
installer to pull the latest release from github and installing it instead.

### Just the bits
Use curl to download and run the install script in the package Github repo::

```shell
curl -sSl https://raw.githubusercontent.com/j-griffith/cinder-docker-driver/master/install.sh | sudo sh
```
## Configuration options
Example config.json file:

```json
{
  "Endpoint": "http://172.16.140.145:5000/v2.0",
  "Username": "Fred",
  "Password": "FredsPassWord",
  "TenantID": "979ddb6183834b9993954ca6de518c5a",
  "Region": "RegionOne"
}
```
V3 Endpoints work as well, but we require one additional piece of information
(DomainName).  If you specify a V3 endpoint, and no DomainName in your config,
the driver will attempt to auth with a DomainName = "Default", if your
DomainName is set to something other than this though, you'll need to specify
it in your config file.  Here's an example of a V3 config:

```json
{
  "Endpoint": "http://172.16.140.145/identity/v3",
  "Username": "Fred",
  "Password": "FredsPassWord",
  "TenantID": "979ddb6183834b9993954ca6de518c5a",
  "Region": "RegionOne"
  "DomainName": "MyAuthDomain",
}
```

### Minimal/Required Config
Configuration options are stored in json format in config.json, the minimum required options provide just enough info to connect to our Cinder API and execute commands.  Note that for quick demos and just trying things out you can use the default interface and network for your iSCSI connections, it's highly recommended however you don't do this if you want to do anything that might require some performance.

- Endpoint
- Username
- Password
- TenantID

### Additonal/Optional Config

- DefaultVolSz (1 GiB)
- MountPoint (/var/lib/cinder/mount)
- InitiatorIFace (default)
- HostUUID (root disk UUID)
- InitiatorIP (default interface IP)
- Region (defaults to "RegionOne")

Example config with additional options:

```json
{
  "Endpoint": "http://172.16.140.145:5000/v2.0",
  "Username": "Fred",
  "Password": "FredsPassWord",
  "TenantID": "979ddb6183834b9993954ca6de518c5a",
  "DefaultVolSz": 1,
  "MountPoint": "/mnt/cvols",
  "InitiatorIFace": "/dev/eth4",
  "HostUUID": "219b0670-a214-4281-8424-5bb3be109ddd",
  "InitiatorIP": "192.168.4.201"
}
```
## Start the daemon
If you want to just launch the driver daemon as root (or sudo):

```shell
sudo ./_bin/cdd >> /var/log/cdd.out 2>&1 &'
sudo service docker restart
```

## Using systemd
The install script includes creation of a systemd service file.
If you used the install script you can just add your config file
and use ```service cdd start```.

Otherwise, you can inspect the install.sh script and create/setup
your own systemd service file.

## Try it out
Assuming your credentials were all set correctly and the driver was able to start up without any issues, you should be ready to go.

To get a list of commands offered via the Docker Volume API:

```console
$ docker volume --help

Usage: 	docker volume COMMAND

Manage Docker volumes

Options:
      --help   Print usage

Commands:
  create      Create a volume
  inspect     Display detailed information on one or more volumes
  ls          List volumes
  rm          Remove a volume

Run 'docker volume COMMAND --help' for more information on a command.
```

To create a volume and specify some options like size and Cinder Type:

```console
$ docker volume create -d cinder --name fredsFirstDVol -o size=10 -o type=lvm-1
```

You can also just add the volume arguments to your docker run command and Docker will make the create calls for you.  Keep in mind that if the volume already exists we'll just grab it and try to attach it, if it doesn't, the Docker service will issue a request to the driver to create it.

## What's next
Put the whole thing in a container, and just run it from there.  Almost there,
just trying to figure out some details on how to use iscsid in a container and
have it effect the host.


text:
# Markdown Notes

This is the official repository for [Markdown Notes](http://markdownnotes.com).

## Application overview

Markdown Notes is a note-taking application that uses Markdown and LaTeX. You can see the application on the [Markdown Notes](http://markdownnotes.com) website.

## Architecture overview

Markdown Notes is a Django application with an AngularJS front-end.

**The back-end** consists of a simple Tastypie API to serve the notes and a bunch of simple Django views to handle the front page and user management. File uploads are stored on Amazon S3.

**The front-end** consists of an AngularJS app that binds various libraries together. The front-end code can be found in `/frontend`.

## Setting up the project

1. Install all requirements with pip: `pip install -r requirements.txt`.
2. Install the external libraries with `bower install`
3. Rename `markdown_notes/local_settings.py.template` and update it with your own settings. Some values in `settings.py` need to be set in environment variables.
4. Collect the static files with `python manage.py collectstatic`. When `DEBUG = False`, you will also need to call `python manage.py compress`.
5. Create the database with `python manage.py migrate`.
6. Run the server with `python manage.py runserver`.

### Updating from a previous version

If you have forked this project in the past (before #42), you might need to generate API keys for existing users. If you get unexplained 403 and 404 while logging in with valid user accounts, this is likely the cause of your problem.

To backfill the API keys, enter the following command:

`python manage.py backfill_api_keys`

### Deployment tools

A crude tool for automatic deployments is available. Install `forever` (`npm install -g forever`) and run `forever start webhooks.js` to have the server redeploy every time there is activity on the GitHub repo.

`deploy.sh` will pull code from the git repository, sync the database, collect static files and minimize them before restarting the server. It is used by `webhooks.js`, but can also be called manually.

## Docker

You can also build your own docker image:

1. Rename `markdown_notes/local_settings.py.template` and update it with your own settings. Some values in `settings.py` need to be set in environment variables.
2. Run `docker build -t markdown-notes .`
3. Run `docker -p 8000:80 -d markdown-notes`
4. Open your browser at [localhost:8000](http://localhost:8000)

## Get involved

You can contribute to Markdown Notes by looking at the submitted issues and sending pull requests with your fixes.

repo_name: nicbou/markdown-notes

repo_language: JavaScript


text:
#!/bin/bash

# Get the code
git pull &&

# Install dependencies
pip install -r requirements.txt &&
bower install --allow-root &&

# Migrate database
python manage.py migrate &&

# Collect and compress static files
python manage.py collectstatic --noinput &&
python manage.py compress --force &&

# Restart the server
service gunicorn restart &&

# Send pushover notification
curl -s \
  --form-string "user=u4bYavyUXyk5oUppeR6TA6mnhJn5dM" \
  --form-string "token=anCEaKMXmbDqx8YvEqaY4BCN3qogUn" \
  --form-string "message=Markdown Notes deployment successful" \
  https://api.pushover.net/1/messages.json


text:
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "markdown_notes.settings")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)


text:
// Watches GitHub webhooks sent on port 9001 and calls deploy.sh
// Requires `gith`

// Usage: `node webhook.js`
// Alternative usage: install `forever` from NPM and run `forever start webhook.js`

var gith = require('gith').create( 9001 );
var execFile = require('child_process').execFile;

console.log('Waiting for webhooks...');

gith({
    repo: 'nicbou/markdown-notes'
}).on( 'all', function( payload ) {
    console.log('Webhook received');
    execFile('./deploy.sh', function(error, stdout, stderr) {});
});


text:
Django==1.11.29
Pillow==2.5.1
boto==2.36.0
django-appconf==1.0.1
django-compressor==2.1
django-realtime==1.1
django-storages==1.1.8
django-tastypie==0.13.3
django-toolbelt==0.0.1
gunicorn==18.0
httplib2==0.18.0
mimeparse==0.1.3
oauth2==1.5.211
psycopg2==2.6
python-dateutil==2.1
python-mimeparse==0.1.4
python-openid==2.2.5
six==1.9.0
static3==0.5.1
wsgiref==0.1.2
django-libsass
base36>=0.1.1


text:
FROM python:2-onbuild

RUN apt-get update && apt-get install -y \
  nodejs-legacy \
  npm

RUN npm install -g bower

COPY . /usr/src/app
WORKDIR /usr/src/app

RUN bower install --allow-root
RUN echo "yes" | python manage.py collectstatic
RUN python manage.py migrate

CMD python manage.py runserver 0.0.0.0:80


text:
from django.contrib.auth.models import User
from django.db import models

class News(models.Model):
    """
    Model for one-time only news.
    """
    user = models.ManyToManyField(User, blank=True)
    title = models.CharField(max_length=255)
    content = models.TextField()
    news_date = models.DateTimeField()

def mark_news_read_for_new_user(sender, **kwargs):
    """
    When new User is created, all News which were published will be
    marked for him as 'read', so he doesn't get avalanche of News.

    :param sender:
    :param kwargs:
    :return:
    """
    if kwargs["created"]:
        user_id = kwargs["instance"].id
        ThroughModel = News.user.through
        ThroughModel.objects.bulk_create([ThroughModel(user_id=user_id, news_id=news_id) for news_id in News.objects.values_list('id', flat=True)])

models.signals.post_save.connect(mark_news_read_for_new_user, sender=User)


text:
from django.contrib import admin

from news.models import News

class NewsAdmin(admin.ModelAdmin):
    list_display = (
        'title',
        'news_date',
    )
    exclude = ('user',)

admin.site.register(News, NewsAdmin)


text:
from django.conf.urls import url
from django.http import HttpResponse
from tastypie.authentication import ApiKeyAuthentication
from tastypie.authorization import Authorization
from tastypie.http import HttpForbidden
from tastypie.resources import ModelResource

from news.models import News

class NewsResource(ModelResource):
    """
    Get and update user profile, also serves as login route for retrieving the ApiKey.
    This resource doesn't have any listing route, the root route /user/ is redirected
    to retrieving the authenticated user's data.
    """

    class Meta:

        authentication = ApiKeyAuthentication()
        authorization = Authorization()

        list_allowed_methods = ['get']
        detail_allowed_methods = ['patch']
        always_return_data = True
        include_resource_uri = False

        queryset = News.objects.all()
        fields = ['id', 'title', 'content', 'news_date']

    def prepend_urls(self):
            return [
                url(r"^(?P<resource_name>%s)/(?P<pk>.*?)/read/$" % self._meta.resource_name,
                    self.wrap_view('mark_news_read'),
                    name="api_mark_news_read"),
            ]

    def get_object_list(self, request):
        return super(NewsResource, self).get_object_list(request).exclude(user=request.user)

    def mark_news_read(self, request, **kwargs):
        """
        Special view which enables to override the root route /user/ for accessing the
        data of currently authenticated user and not the listing of all users.

        :param request:
        :param kwargs:
        :return:
        """
        self.method_check(request, allowed=['patch'])
        self.is_authenticated(request)

        user = getattr(request, 'user', None)
        if not user or user.is_anonymous():
            return HttpForbidden()

        News.objects.get(pk=int(kwargs['pk'])).user.add(user)

        return HttpResponse(status=200)


text:
# -*- coding: utf-8 -*-
# Generated by Django 1.9.1 on 2016-10-09 15:58
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models

class Migration(migrations.Migration):

    initial = True

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='News',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('title', models.CharField(max_length=255)),
                ('content', models.TextField()),
                ('news_date', models.DateTimeField()),
                ('user', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)),
            ],
        ),
    ]


text:
# -*- coding: utf-8 -*-
# Generated by Django 1.9.1 on 2016-10-20 19:51
from __future__ import unicode_literals

from django.db import migrations, models
from django.utils import timezone

def create_news(apps, schema_editor):
    News = apps.get_model("news", "News")

    login_news = News(
        title='New login system',
        content="""We implemented a new, improved authentication system. These changes pave the way for native Markdown Notes apps."""
    )
    login_news.news_date = timezone.now()
    login_news.save()

    image_paste_news = News(
        title='Paste images in your notes',
        content="""New feature! You can now paste screenshots and images directly into the editor just as you would in Paint or Photoshop.

This feature is available on Chrome, Firefox and Internet Explorer 11."""
    )
    image_paste_news.news_date = timezone.now()
    image_paste_news.save()

class Migration(migrations.Migration):
    dependencies = [
        ('news', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(create_news)
    ]


text:
from django.db import models
from django.contrib.auth.models import User

def signals_import():
    """ A note on signals.
    The signals need to be imported early on so that they get registered
    by the application. Putting the signals here makes sure of this since
    the models package gets imported on the application startup.
    """
    from tastypie.models import create_api_key

    models.signals.post_save.connect(create_api_key, sender=User)

signals_import()


text:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

UserAdmin.list_display = ('username', 'email', 'first_name', 'last_name', 'is_active', 'date_joined')

admin.site.unregister(User)
admin.site.register(User, UserAdmin)


text:
import json

from tastypie.exceptions import TastypieError
from tastypie.http import HttpBadRequest

class CustomBadRequest(TastypieError):
    """
    This exception is used to interrupt the flow of processing to immediately
    return a custom HttpResponse.
    """

    def __init__(self, code="", message="", field=""):
        self._response = {
            "error": {"code": code or "not_provided",
                      "message": message or "No error message was provided.",
                      "field": field or "not_provided"}}

    @property
    def response(self):
        return HttpBadRequest(
            json.dumps(self._response),
            content_type='application/json')


text:
from django.conf.urls import url
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.models import User
from django.contrib.auth.hashers import make_password

from tastypie.authentication import (
    Authentication, ApiKeyAuthentication,
    MultiAuthentication)
from tastypie.authentication import BasicAuthentication as _BasicAuthentication
from tastypie.authorization import Authorization
from tastypie.resources import ModelResource, Resource
from tastypie.utils import trailing_slash
from tastypie import fields

from .exceptions import CustomBadRequest

class BasicAuthentication(_BasicAuthentication):
    """
    Customized Tastypie BasicAuthentication which returns HTTP 403 code for unauthorized requests.
    For more: https://github.com/django-tastypie/django-tastypie/issues/117
    """

    def _unauthorized(self):
        response = super(BasicAuthentication, self)._unauthorized()
        response.status_code = 403
        return response

class CreateUserResource(ModelResource):
    """
    Resource for creating a new user. This resource is not protected with any
    Authentication or Authorization.
    """

    class Meta:
        allowed_methods = ['post']
        detail_allowed_methods = []
        always_return_data = True
        include_resource_uri = False

        authentication = Authentication()
        authorization = Authorization()

        queryset = User.objects.all().select_related("api_key")
        resource_name = 'create_user'
        fields = ['email', 'username', 'api_key']

    def obj_create(self, bundle, **kwargs):
        """
        Validation of incoming data and checks for email&username uniqueness

        :param bundle:
        :param kwargs:
        :return:
        """
        REQUIRED_USER_FIELDS = ("username", "email", "password")
        for field in REQUIRED_USER_FIELDS:
            if field not in bundle.data:
                raise CustomBadRequest(
                    code="missing_key",
                    message="Must provide {missing_key} when creating a user.".format(missing_key=field),
                    field=field
                )

        try:
            email = bundle.data["email"]
            username = bundle.data["username"]

            if User.objects.filter(email=email):
                raise CustomBadRequest(
                    code="duplicate_exception",
                    message="That email is already used.",
                    field="email")

            if User.objects.filter(username=username):
                raise CustomBadRequest(
                    code="duplicate_exception",
                    message="That username is already used.",
                    field="username")

        except KeyError as missing_key:
            raise CustomBadRequest(
                code="missing_key",
                message="Must provide {missing_key} when creating a user."
                    .format(missing_key=missing_key))
        except User.DoesNotExist:
            pass

        raw_password = bundle.data.pop('password')
        kwargs["password"] = make_password(raw_password)

        return super(CreateUserResource, self).obj_create(bundle, **kwargs)

    def dehydrate(self, bundle):
        bundle.data['api_key'] = bundle.obj.api_key.key

        return bundle

class UserResource(ModelResource):
    """
    Get and update user profile, also serves as login route for retrieving the ApiKey.
    This resource doesn't have any listing route, the root route /user/ is redirected
    to retrieving the authenticated user's data.
    """

    class Meta:
        # For authentication, allow both basic and api key so that the key
        # can be grabbed, if needed.
        authentication = MultiAuthentication(
            BasicAuthentication(),
            ApiKeyAuthentication())
        authorization = Authorization()

        list_allowed_methods = []
        detail_allowed_methods = ['get', 'patch']
        always_return_data = True
        include_resource_uri = False

        queryset = User.objects.all().select_related("api_key")
        fields = ['email', 'username', 'api_key']

    def prepend_urls(self):
        return [
            url(r"^(?P<resource_name>%s)%s$" % (self._meta.resource_name, trailing_slash()),
                self.wrap_view('current_user_redirect'), name="api_current_user_redirect"),
        ]

    def current_user_redirect(self, request, **kwargs):
        """
        Special view which enables to override the root route /user/ for accessing the
        data of currently authenticated user and not the listing of all users.

        :param request:
        :param kwargs:
        :return:
        """
        self.method_check(request, allowed=['get', 'patch'])
        self.is_authenticated(request)

        user = getattr(request, 'user', None)
        if user and not user.is_anonymous():
            return self.dispatch_detail(request, pk=str(request.user.id))

    def update_in_place(self, request, original_bundle, new_data):
        """
        Validation for duplicates of username and email through PATCH requests.

        :param request:
        :param original_bundle:
        :param new_data:
        :return:
        """
        try:
            email = new_data["email"]
            if User.objects.filter(email=email):
                raise CustomBadRequest(
                    code="duplicate_exception",
                    message="That email is already used.",
                    field="email")
        except KeyError:
            pass
        except User.DoesNotExist:
            pass

        try:
            username = new_data["username"]
            if User.objects.filter(username=username):
                raise CustomBadRequest(
                    code="duplicate_exception",
                    message="That username is already used.",
                    field="username")
        except KeyError:
            pass
        except User.DoesNotExist:
            pass

        return super(UserResource, self).update_in_place(request, original_bundle, new_data)

    def authorized_read_list(self, object_list, bundle):
        return object_list.filter(id=bundle.request.user.id).select_related()

    def hydrate(self, bundle):
        """
        When updating user's password, check if the old password is valid.
        :param bundle:
        :return:
        """

        if 'password' in bundle.data or 'old_password' in bundle.data:

            if 'password' not in bundle.data or 'old_password' not in bundle.data:
                raise CustomBadRequest(
                    code="missing_key",
                    message="If you want to change password you have to submit both old and new password!",
                    field="password")

            raw_password = bundle.data.pop('password')
            old_password = bundle.data.pop('old_password')

            if not bundle.obj.check_password(old_password):
                raise CustomBadRequest(
                    code="verification_exception",
                    message="The old password you entered doesn't match your actual old password!",
                    field="oldPassword")

            bundle.obj.set_password(raw_password)

        return bundle

    def dehydrate(self, bundle):
        if bundle.obj.pk == bundle.request.user.pk:
            bundle.data['api_key'] = bundle.obj.api_key.key

        return bundle

class PasswordRecoveryResource(Resource):
    """
    Non-ORM Resource which serves for recovering lost password.
    """
    email = fields.CharField(attribute='email')

    class Meta:
        resource_name = 'password_recovery'
        list_allowed_methods = ['post']
        detail_allowed_methods = []

    def obj_create(self, bundle, **kwargs):
        password_form = PasswordResetForm(data=bundle.data)
        password_form.full_clean()
        password_form.save()


text:
// Generic colors
$colorBackground: #fff;
$colorBackgroundInverted: #303749;
$colorBackGroundInvertedLight: #353c4f;
$colorBackgroundLight: rgba(0, 0, 0, 0.07); // equivalent to #eeeeee;
$colorBorderLight: #ccc;
$colorBorderLightInverted: #485065;
$colorErrorBackground: #d9534f;
$colorHighlight: #00b4f9;
$colorHighlightInverted: #49506a;
$colorInfoBackground: #00b4f9;
$colorSuccess: #0c0;
$colorText: #333;
$colorTextHeavy: #000;
$colorTextHeavyInverted: #fff;
$colorTextInverted: #fff;
$colorTextMuted: #999;
$colorTextMutedInverted: rgba(255,255,255,0.5);
$colorWarningBackground: #f0ad4e;

// Generic sizes
$fontFamilyNormal: Roboto, sans-serif;
$fontSizeMonospace: 13px;
$fontSizeExtraLarge: 40px; // H1
$fontSizeLarge: 25px;
$fontSizeNormal: 14px;
$fontSizeSmall: 10px;
$fontWeightExtraHeavy: 700;
$fontWeightHeavy: 400;
$fontWeightNormal: 300;
$lineHeightNormal: 20px;
$marginExtraLarge: 30px;
$marginExtraSmall: 5px;
$marginNormal: 15px;
$marginSmall: 10px;
$marginLarge: $lineHeightNormal;
$maxPanelWidth: 650px;
$topBarHeight: 30px;


text:
@import 'attributes';

html, body{
    background: #fff;
    height: 100%;
    margin: 0;
    padding: 0;
    width: 100%;
}
body{
    color: #000;
    font: 24px/36px 'Roboto',sans-serif;
    font-weight: 100;
}

#account-buttons{
    background: $colorBackGroundInvertedLight;
    font: #{$fontSizeNormal}/#{$topBarHeight} Roboto, sans-serif;
    position: absolute;
    right: 0;
    top: $marginSmall;

    .separator{
        border-left: 1px solid rgba(255,255,255,0.1);
        height: $topBarHeight;
        margin-bottom: -$marginSmall;
        margin: 0;
    }

    .btn{
        background: $colorBackGroundInvertedLight;
        border-radius: 0;
        border: none;
        font: #{$fontSizeNormal}/#{$topBarHeight} Roboto, sans-serif;
        font-weight: 300;
        padding: 0 $marginNormal;
        text-transform: none;

        &.primary{
            background: $colorHighlight;
        }

        &:hover{
            color:#fff;
        }
    }
}

.section{
    padding: $marginExtraLarge 0;
    text-align:center;

    &.dark{
        background: $colorBackgroundInverted;
        color: #fff!important;

        a{
            color: #fff!important;
        }
    }
    &.header{
        overflow:hidden;
        padding:0;

        img{
            display:block;
            margin:0 auto;
            padding:0;
        }
    }

    >h1{
        font-family:'Roboto';
        font-size:70px;
        font-weight:100;
        line-height:70px;
        margin:0 auto;
        max-width:650px;
        padding:45px 0 20px;
    }
    >h2{
        font-family:'Roboto';
        font-size:50px;
        font-weight:100;
        line-height:55px;
        margin:0 auto;
        max-width:650px;
        padding:10px 0 30px;
    }
    >p{
        font-size: 24px;
        font-weight:200;
        line-height:36px;
        margin:0 auto 30px;
        max-width:650px;
        padding:0;
    }
}

#demo #markdownnotes-wrapper{
    height: 350px;
    margin: -540px auto 0;
    width: 960px;

    .no-demo{
        display:none;
    }
}

.btn{
    background: $colorBackgroundInverted;
    border-radius: 5px;
    border: 1px solid #fff;
    color: #fff;
    display: inline-block;
    font-weight: 100;
    font: 25px/60px Roboto, sans-serif;
    padding: 0 45px;
    text-decoration: none;
    text-transform: uppercase;

    &:hover{
        background: #fff;
        border:1px solid $colorBackgroundInverted;
        color: $colorBackgroundInverted;
        cursor: pointer;
    }

    &.btn-small{
        font-size: $fontSizeNormal;
        line-height: $fontSizeNormal + 14;
        padding: 0 20px;
    }

    &.btn-inverted {
        background-color: $colorBackground;
        color: $colorBackgroundInverted;
        border-color: $colorBackgroundInverted;

        &:hover{
            background-color: $colorBackgroundInverted;
            color: $colorBackground;
            border-color: $colorBackground;
        }
    }
}

.alert{
    font-size: 18px;
    margin: 0 auto 20px;
    max-width: 650px;
    padding: 5px 20px;

    &.alert-error{
        background: $colorErrorBackground;
        color:#fff;
    }
}

/* Forms */
.form-actions{
    margin:30px 0 0;
}
.control-group{
    margin:0 0 10px;

    label{
        display: inline-block;
        font-size: 24px;
        height: 35px;
        text-align: right;
        width: 235px;
        padding-right: 10px;
    }
    input{
        border: 1px solid #fff;
        padding: 10px 6px;
        vertical-align: bottom;
        width: 400px;
    }
    &.error input{
        border-color:#f00;
    }
    .controls{
        display:inline-block;
        height:35px
    }
}

/* Mobile style corrections */
@media (max-width: 767px) {
    #screen, #demo #markdownnotes-wrapper{display:none;}
    html,body{min-width:0;overflow-x:hidden;}
    #account-buttons{position:absolute;top:20px;left:20px;}
    h1,h2,p{text-align:left}
    .header h1, .section.login h1, .section.signup h1{font-size:50px;line-height:55px;padding:60px 0 30px;}
    h1{font-size:35px;line-height:40px;padding:15px 0;}
    p{font-size:18px;line-height:24px}
    .section{padding:20px}
    .section.header{padding:20px 20px 0;}
    .section .btn{font-size:20px;line-height:60px;width:100%;margin-bottom:10px;box-sizing:border-box;}
    .section img{max-width: 100%}

    /* Forms */
    .section.login, .section.signup{text-align:left;padding-top:20px;}
        .control-group{margin:0 0 15px;}
        .control-group+.form-actions>.btn{margin-top:10px;}
        .control-label{width:100%;}
        .controls, .controls input{width:100%;box-sizing:border-box;}

}


text:
@import 'attributes';
@import 'editor';

.app{
    position:absolute;
    top:0;
    left:0;
    width:100%;
    height:100%;
}

#markdownnotes-wrapper{
    font: {
        family: $fontFamilyNormal;
        size: $fontSizeNormal;
        weight: $fontWeightNormal;
    }
    line-height: $lineHeightNormal;
    text-align: left;

    position:relative;
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0;
    overflow: hidden;
    background: $colorBackground;
    color: $colorText;

    header{
        background: $colorBackGroundInvertedLight;
        box-sizing: border-box;
        height: $topBarHeight;
        line-height: 30px;
        position: absolute;
        width: 100%;
        z-index: 100;
        font: {
            family: $fontFamilyNormal;
            weight: $fontWeightNormal;
        }

        #note-title-wrapper{
            overflow:hidden;

            #note-title{
                background:transparent;
                border:none;
                color: $colorTextInverted;
                font: {
                    family: $fontFamilyNormal;
                    size: $fontSizeNormal;
                    weight: $fontWeightNormal;
                }
                line-height: 16px;
                margin: 3px 0 0;
                outline: none;
                padding: 0;
                width: calc(100% - #{$marginNormal});

                &::-webkit-input-placeholder{
                    font: {
                        size: $fontSizeNormal;
                        family: $fontFamilyNormal;
                    }
                    line-height: $lineHeightNormal;
                    color: $colorTextMutedInverted;
                }
            }
            input#note-title:hover, input#note-title:focus{
                    border-bottom: 1px solid rgba(255,255,255,0.5);
                }
            span#note-title{
                display:block;
                max-width: $maxPanelWidth;
                margin:0 auto;
                line-height: $topBarHeight;
            }
        }

        #btn-menu{
            background: {
                color: $colorHighlight;
                image: url('/static/img/icon-menu.gif');
                position: 10px 9px;
                repeat: no-repeat;
                size: 11px;
            }
            color: $colorTextInverted;
            display: inline-block;
            float: left;
            left: -$marginNormal;
            line-height: $topBarHeight;
            margin: 0;
            padding: 0 $marginNormal 0 30px;
            position: relative;
            text-decoration: none;
            text-transform: uppercase;
            width: 45px;

            &.open{
                left:0;
                margin:0 $marginNormal 0 0;
                width: 305px;
            }
        }
    }

    #menu-bar{
        float: right;
        height: $topBarHeight;
        color: $colorTextInverted;

        .separator{
            border-left: 1px solid $colorBorderLightInverted;
            display: inline-block;
            height: $topBarHeight;
            margin: 0 0 -10px;
        }
        a{
            cursor: pointer;
            display: inline-block;
            padding: 0 $marginSmall;
            vertical-align:top;
        }
        .icon{
            font-size:25px;
            vertical-align: middle;
        }
        #btn-mode-hybrid{
            background: $colorHighlightInverted;
        }
    }

    /* Main section */
    #main{
        box-sizing: border-box;
        height: calc(100% - #{$topBarHeight});
        overflow: hidden;
        position: absolute;
        top: $topBarHeight;
        width: 100%;

        #side-menu{
            background: $colorBackGroundInvertedLight;
            box-shadow: 0 0 20px rgba(0,0,0,0.2);
            height: 100%;
            left: -350px;
            margin: 0;
            max-width: 100%;
            overflow: auto;
            padding: 0;
            position: absolute;
            width: 350px;
            z-index: 900;

            &.open{
                left:0!important;
            }

            ul{
                margin:0;
                padding:0;
                color: $colorTextInverted;

                li{
                    list-style:none;
                    width:100%;
                    margin:0;
                    position:relative;
                    display:flex;
                    flex-flow:row wrap;
                    align-items: center;

                    > a {
                        cursor: pointer;
                    }

                    > a, > span{
                        flex:1 1;
                        display:block;
                        width:100%;
                        height:100%;
                        padding:$marginSmall $marginSmall $marginSmall $marginNormal;
                        text-decoration:none;
                        overflow: hidden;
                        text-overflow:ellipsis;
                        white-space:nowrap;
                        color: $colorTextInverted;
                        box-sizing:border-box;

                        &.editable{
                            display:flex;

                            input{
                                background: transparent;
                                color:#fff;
                                border:none;
                                border-bottom: 1px solid rgba(255,255,255,0.5);
                                outline:none;
                                line-height: 20px;
                                font:inherit;
                                vertical-align:top;
                                flex:1 1;

                                &::-webkit-input-placeholder{
                                    color: $colorTextMuted!important;
                                }
                            }
                        }

                        > .icon{ // Main icon
                            font-size:26px;
                            vertical-align:middle;
                            margin-right:$marginSmall;
                            width:23px;
                            margin-top:-3px;
                            display:inline-block;
                            float:left;
                        }
                    }

                    &:hover, &.folder.expanded, &.creating-notebook, &.drag-over{
                        background-color:#49506a!important;

                        &.danger:hover{
                            background-color:rgba(255,0,0,0.7)!important;
                        }

                        .actions{
                            display:block;
                            white-space:nowrap;
                            flex:0 0;
                        }
                    }

                    &.folder{
                        li:hover{
                            background-color:transparent!important;
                        }

                        > .actions{
                            top:8px;
                            display:none;
                        }

                        > ul{
                            width:100%;
                            flex:0 100%;

                            .actions{
                                display:block;
                            }
                        }

                        .separator{
                            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
                        }
                    }

                    &.folder.expanded+.folder.expanded{
                        border-top: 1px solid rgba(255, 255, 255, 0.1);
                    }

                    &.separator{
                        border-bottom: 1px solid $colorBorderLightInverted;
                        height: 0;
                        margin: -1px $marginNormal -1px;
                        width: 320px;
                    }
                    &.separator+.separator{display: none;}

                    .title .untitled{color: $colorTextInverted;}

                    .muted{display:block;
                        color: $colorTextMutedInverted;
                    }

                    .subtitle{display:block;
                        font-size: $fontSizeSmall;
                        text-transform: uppercase;
                        color: $colorTextMutedInverted;
                    }

                    .actions{
                        display:none;
                        padding-right:10px;
                        text-align:right;

                        a{
                            border-radius: 30px;
                            color: $colorTextInverted;
                            cursor: pointer;
                            display:inline-block;
                            font-size:26px;
                            height:30px;
                            line-height: 30px;
                            padding:0!important;
                            text-align: center;
                            width:30px;

                            &:hover{
                                background:#353c4f;
                                color:#ffffff;
                            }
                        }
                    }
                }
            }
        }

        #input,#output{
            width:50%;height:100%;position:absolute;overflow:auto;box-sizing:border-box;
            -webkit-transition: -webkit-transform 300ms, all 300ms;
            -moz-transition: -moz-transform 300ms, all 300ms;
            -ms-transition: -ms-transform 300ms, all 300ms;
            transition: transform 300ms, all 300ms;

            >span{
                color: #999;
                padding: 5px;
                line-height: 25px
            }
        }

        #input{
            padding-bottom: 30px;background:#f6f6f6;

            >#editor{
                max-width: $maxPanelWidth;
                margin:0 auto;
            }
            >#editor, >#editor>form{
                height:100%;
            }
        }

        #output{
            background:#fff;
            left:50%;
            padding:$marginNormal;
            z-index:10;

            // Typographic grid test:
            // background-size: 20px 20px;
            // background-image: repeating-linear-gradient(0deg, #ccc, #ccc 1px, transparent 1px, transparent 40px),repeating-linear-gradient(-90deg, #fff, #fff 1px, transparent 1px, transparent 20px);

            #preview{
                max-width: $maxPanelWidth;
                margin:0 auto;/* Overflow fix */
                padding-bottom: $marginExtraLarge;
                word-wrap: break-word;

                h1, h2, h3, h4, h5{
                    color: $colorTextHeavy;
                }
                h2, h3, h4, h5{
                    font-weight:$fontWeightHeavy;
                }
                h4, h5{
                    line-height: $lineHeightNormal;
                    font-size: $fontSizeNormal;
                    margin: $marginLarge 0 0;
                }
                h1{
                    font-weight: $fontWeightHeavy;
                    font-size: $fontSizeExtraLarge;
                    margin: $marginLarge 0;
                    color: $colorTextHeavy;
                    line-height: $lineHeightNormal * 2;
                }
                h2{
                    font-size: $fontSizeLarge;
                    line-height: $lineHeightNormal * 2;
                    margin: 25px 0 15px;
                }
                h3{
                    font-size: 19px;
                    line-height: $lineHeightNormal;
                    margin: $marginLarge 0;
                }
                h4{
                    text-transform: uppercase;
                }
                ul, ol{
                    margin: 0 0 $marginLarge;
                    padding-left:$marginExtraLarge;
                    .task{
                        list-style:none;position:relative;
                        .checkbox{margin-left:-22px;text-align:center;display:inline-block;font-size:12px;width:12px;height:12px;line-height:12px;color:transparent;border:1px solid #999;position:relative;left:-8px;top:-2px;}
                        &.task-open>.checkbox{}
                        &.task-closed>.checkbox{border-color:$colorSuccess;color:$colorSuccess;}
                    }
                }

                //Content blocks
                code, pre, blockquote, .latex.block{
                    background:$colorBackgroundLight;
                }
                p, pre, blockquote, .latex.block, table{
                    margin: 0 0 20px;
                }
                pre, blockquote{
                    padding: 20px;
                    >*:last-child{
                        margin-bottom:0!important;
                    }
                }
                code, pre{
                    font-family: 'Roboto Mono', monospace;
                    font-size: 12px;
                }
                pre{
                    overflow: auto;
                    >code{
                        background: transparent;
                    }
                }

                br+.latex.block{margin-top:-20px;}
                b, strong{font-weight:$fontWeightExtraHeavy;color:$colorTextHeavy}

                hr{border:0;border-top:1px solid $colorBorderLight;margin:$marginExtraLarge 0;}
                img{max-width: 100%}
                li>ul, li>ol{margin:0;}

                p{
                    font-size: inherit;
                }
                table{
                    border-collapse:collapse;
                    th, td{border:1px solid $colorBorderLight;padding:3px 5px;}
                    th:empty, td:empty{border:none;padding:0;}
                }

                .MathJax{ //Inline
                    display:inline-block;
                    .noError{ // Formulas with errors
                        border:0;
                        color:inherit;
                        font-size:100%;
                        font-family:inherit;
                        font-weight:$fontWeightNormal;
                        border-bottom: 1px dashed #f00;
                    }
                }
                .MathJax_Display{ //Block
                    text-align: left!important;
                    margin:0;
                    >.MathJax{
                        background:none; margin:0;
                        padding: $marginExtraSmall $marginSmall;
                        box-sizing:border-box;
                        border-radius:0;
                    }
                    ~br{display:none;}
                }
            }
        }
    }

    &.input-only{
        #main #input{width:100%;background:#fff;}
        #main #output{left:100%;}
        #btn-mode-input{background:$colorHighlightInverted;}
        #btn-mode-hybrid{background:transparent!important;}
    }
    &.output-only{
        #main #input{left:-50%;background:#fff;}
        #main #output{width:100%;left:0;}
        #btn-mode-output{background:$colorHighlightInverted;}
        #btn-mode-hybrid{background:transparent!important;}
    }

    #MathJax_Message{display:none!important;}

    #side-menu, #btn-menu{/* Menu open transition */-webkit-transition: -webkit-transform 300ms, all 300ms;-moz-transition: -moz-transform 300ms, all 300ms;-ms-transition: -ms-transform 300ms, all 300ms;transition: transform 300ms, all 300ms;}

    /* Messages */
    #messages{
        bottom:0;
        left:0;
        margin:0 auto;
        max-width:100%;
        min-width:400px;
        position:absolute;
        right:0;
        width:50%;
        z-index:1001;

        .message{
            border-radius:3px;
            box-shadow:0 5px 10px -5px rgba(0,0,0,0.1);
            box-sizing:border-box;
            color:#fff;
            margin-bottom:3px;
            padding:3px 5px;
            width:100%;

            &.error{background: $colorErrorBackground}
            &.warning{background: $colorWarningBackground}
            &.info{background: $colorInfoBackground}

            .icon{
                font-size:25px;
                float: right;
                cursor: pointer;
                display: inline-block;
                margin: -3px 0 0;
                padding: 0 2px;
            }
            a{
                color: #fff;
            }
        }
    }

    .modal{
        box-sizing: border-box;
        background: $colorBackGroundInvertedLight;
        color: $colorTextInverted;
        position: absolute;
        z-index: 2000;
        padding: $marginNormal;
        box-shadow: 5px 5px 10px -3px rgba(0,0,0,0.1);

        .close{
            font-size: 25px;
            cursor: pointer;
            position: absolute;
            top: $marginNormal;
            right: $marginNormal;
        }

        .hint{
            font-size: $fontSizeSmall;
            color: $colorTextMutedInverted;
        }

        &.share{
            top: $topBarHeight + $marginSmall;
            right: $marginSmall;
            width: calc(100% - #{$marginSmall * 2});
            max-width: 350px;

            h2{
                margin: 5px 0 10px;
                font-weight: 100;
            }
            .share-url{
                padding: $marginExtraSmall;
                border: 1px solid rgba(255,255,255,0.2);
            }
        }
    }
}

.overlay {
    position: fixed;
    left: 50%;
    top: 50%;
    width: 400px;
    height: 550px;
    margin-left: -200px;
    margin-top: -275px;
    z-index: 120;

    background-color: #ffffff;
    box-shadow: 5px 5px 10px -3px rgba(0, 0, 0, 0.1);

    font-size: $fontSizeNormal;
    font-weight: $fontWeightNormal;
    line-height: $lineHeightNormal;

    &.login{
        height: 375px;
        margin-top: -188px;

        &.with-message{
            height: 415px;
            margin-top: -207px;
        }
    }

    &.passwordRecovery{
        height: 305px;
        margin-top: -152px;

        &.with-message{
            height: 345px;
            margin-top: -172px;
        }
    }

    &.signup{
        height: 435px;
        margin-top: -218px;

        &.with-message{
            height: 475px;
            margin-top: -238px;
        }
    }

    &.accountSettings{
        height: 425px;
        margin-top: -213px;

        &.with-message{
            height: 465px;
            margin-top: -233px;
        }
    }

    h1{
        padding-top: 30px;
        padding-bottom: 20px;
    }

    &.passwordRecovery h1{
        font-size: 55px;
    }

    .dark{
        position: relative;
        padding-left:20px;
        padding-right:20px;
        text-align: left;

        padding-bottom: 42px;
        margin-bottom: 38px;
    }

    .links{
        margin-top: 20px;
        text-align: center;
        padding: 0 56px;

        a{
            text-decoration: none;
            color: $colorText;
            margin-left: 5px;
            margin-right: 5px;

            &:hover{
                text-decoration: underline;
            }
        }
    }

    .close{
        position: absolute;
        right: 10px;
        top: 7px;

        i{
            cursor: pointer;
            font-size: 25px;
        }
    }

    input:not([type="submit"]){
        display: block;
        width: calc(100% - 10px);
        padding: $marginExtraSmall;
        margin-bottom: 10px;

        border: 1px solid rgba(255, 255, 255, 0.2);
        background-color: transparent;
        color: white;

        font-size: $fontSizeNormal;
        font-weight: $fontWeightNormal;
        line-height: $lineHeightNormal;

        &::-moz-placeholder, &::-webkit-input-placeholder, &:-ms-input-placeholder {
            color: $colorTextMutedInverted;
        }

        &:focus{
            outline: none;
        }

        &.errorField{
            border-color: $colorErrorBackground;
            color: $colorErrorBackground;
        }
    }

    .submit-wrapper {
        width: 100%;
        height: 47px;
        position: absolute;
        left: 0;
        bottom: -23.5px;
        margin-top: 10px;
        text-align: center;

        .btn{
            padding: 0 50px;
            font-size: 21px;
            line-height:45px;
        }
    }

    input[type="checkbox"] {
        display: none;
    }

    label{
        cursor: pointer;
    }

    input[type="checkbox"] + label::before {
        content: ' ';
        display: inline-block;
        width: 16px;
        height: 16px;
        margin-right: 10px;

        position: relative;
        top: 4px;

        border: 1px solid $colorTextMutedInverted;
        border-radius: 50%;
        background-color: $colorBackgroundInverted;
    }

    input[type="checkbox"]:checked + label::before {
        background-color: $colorTextMutedInverted;
    }

    .error input:not([type="submit"]){
        border-color: $colorErrorBackground;
        color: $colorErrorBackground;
    }

    .msg{
        padding: 10px;
        font-size: $fontSizeNormal;
        font-weight: $fontWeightNormal;

        &.error{
            background-color: $colorErrorBackground;
            color: white;
        }

        &.success{
            background-color: $colorSuccess;
            color: white;
        }
    }
}
#fade {
    position: fixed;
    left: 0;
    top: 0;
    background-color: black;
    -moz-opacity: 0.5;
    opacity: .50;
    filter: alpha(opacity=50);
    width: 100%;
    height: 100%;
    z-index: 110;
}

#news_container{
    position: fixed;
    top: 45px;
    right: 20px;
    width: 400px;
    z-index: 100;

    article{
        position: relative;
        background-color: $colorInfoBackground;
        margin-bottom: $marginSmall;
        color: #fff;
        padding: $marginNormal;

        h2{
            margin: $marginExtraSmall 0 $marginSmall;
            font-weight: 300;
        }

        .close{
            position: absolute;
            right: $marginNormal;
            top: $marginNormal;
            font-size: 23px;
            color: $colorTextInverted;
        }

        .content{
            white-space: pre-wrap;
        }
    }
}

/* Drag and drop upload overlay */
body.drag-over{
    #markdownnotes-wrapper{
        header, #main{
            filter:blur(2px);-moz-filter:blur(2px);-webkit-filter:blur(2px);-ms-filter:blur(2px);
        }
        #file-drop{
            display:block;
            background: rgba(255,255,255,0.6);
            h1{
                margin-top: 100px;
                color: $colorTextHeavy;
            }
        }
    }
}
#markdownnotes-wrapper #file-drop{
    z-index:1000;
    position:absolute;
    top:0;
    left:0;
    width:100%;
    height:100%;
    display:none;

    h1{
        text-align:center;
        margin-top:50px;
        font-size:$fontSizeExtraLarge;
        font-weight:$fontWeightHeavy;
    }
}

/* Scrollbars */
::-webkit-scrollbar{width:7px;height:7px;}
::-webkit-scrollbar-track{background: rgba(0,0,0,0.1);}
::-webkit-scrollbar-thumb{background: rgba(0,0,0,0.3);}
::-webkit-scrollbar-thumb:hover{background: rgba(0,0,0,0.4);}

#side-menu::-webkit-scrollbar-track{background: rgba(0,0,0,0.1);}
#side-menu::-webkit-scrollbar-thumb{background: rgba(255,255,255,0.2);}
#side-menu::-webkit-scrollbar-thumb:hover{background: rgba(255,255,255,0.4);}

/* Full screen */
:-webkit-full-screen #main{width: 100%;height: 100%;}

/* Print */
@media print{
    *{-webkit-transition:none!important;-moz-transition:none!important;-ms-transition:none!important;}
    #input, #btn-mode-fullscreen, #btn-menu, #side-menu, #menu-bar, #note-title-wrapper, .modal, #markdownnotes-wrapper header{display:none!important;}

    #markdownnotes-wrapper{
        overflow: visible!important;

        #main{
            height:auto;
            top:0;

            #output{
                margin-top:0;
                padding:0 $marginExtraSmall 20px;
                font-size:$fontSizeNormal;

                h1, h2, h3{page-break-after:avoid}
                h1, h2, h3, h4, h5{
                    &:first-child{
                        margin-top: 0;
                    }
                }

                #preview{
                    max-width: none;

                    &h1, &h2, &h3, &h4, &h5{break-after: avoid}

                    & h1:first-child, & h2:first-child, & h3:first-child{margin-top:0;padding-top:0;}

                    .checkbox{
                        border:1px solid $colorTextHeavy!important;

                        .done{
                            color:$colorTextHeavy!important
                        }
                    }
                }
            }

        }

        #main, #main #output{width:100%!important;border:none!important;left:0!important;overflow:visible!important;}
    }
}

/* Mobile style corrections */
@media (max-width: 767px) {
    #markdownnotes-wrapper{
        overflow:visible;
        color:$colorTextHeavy;

        #note-title-wrapper{
            width:100%;border-bottom:1px solid $colorBorderLight;

            #note-title{
                color:$colorTextHeavy!important;
                padding:0 $marginNormal!important;
                margin:0;
                width:100%!important;
                box-sizing:border-box;

                &::-webkit-input-placeholder{
                    color: $colorTextMuted!important;
                }
            }
        }

        #btn-menu.open{width:45px!important;}

        #side-menu{
            left:-100%;
            width:100%;

            .actions{display:block;}
            li a{padding-right:45px!important;}
        }

        #input,#output{
            top: $marginExtraLarge;
            height: calc(100% - #{$marginExtraLarge});
            padding-bottom: $marginExtraLarge;
            width: 100%!important;
            -webkit-transition: none;
            -moz-transition: none;
            -ms-transition: none;
            transition: none;
        }

        #input{
            padding-bottom: 60px;
        }
        #output{
            left:100%;

            #preview{
                padding-bottom: 60px;

                h2,h3,h4,h5{
                    color:$colorTextHeavy;
                }
            }
        }

        /* Disable hybrid mode (defaults to editor) on mobiles */
        #btn-mode-hybrid, #btn-export, #btn-export+.separator{display:none!important}
        #btn-mode-input{color:#fff!important;}
        .output-only{
            #input{left:-100%;}
            #output{left:0!important;}
            #btn-mode-hybrid{color:inherit!important;}
            #btn-mode-output{color:#fff!important;}
            #btn-mode-input{color:inherit!important;}
        }
    }
}


text:
#markdownnotes-wrapper{

    .CodeMirror {
        font-family: 'Roboto Mono', monospace;
        height: auto;
        background: transparent;
    }
    .CodeMirror-scroll {
        overflow-y: hidden!important;
        overflow-x: auto!important;
        height:auto;
        margin-bottom: 0!important;
    }

    /* THE CUSTOM THEME */

    /* Gutters and code folding */
    .CodeMirror-gutters {white-space: nowrap; background:transparent; border:none;}
    .CodeMirror-lines {padding: 4px 0;}
    .CodeMirror pre {padding: 0 4px 0 0;}
    .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {background-color: $colorBackground;}

    .CodeMirror-foldmarker {display: none}
    .CodeMirror-foldgutter {width: $marginNormal;}
    .CodeMirror-foldgutter-open,.CodeMirror-foldgutter-folded {color:#666;cursor: pointer;}
    .CodeMirror-foldgutter-open:after {content: "\25BE";}
    .CodeMirror-foldgutter-folded:after {content: "\25B8";}
    .CodeMirror-gutter-wrapper{text-align:center;line-height:15px;}

    /* Syntax highlighting */
    #editor{
        pre{color:#333;font-size:12px;line-height:20px}
        .cm-variable-1, .cm-variable-2, .cm-variable-3, .cm-keyword{color:inherit;}
        .cm-list {}
        .cm-number {color: #164;}
        .cm-def {color: #00f;}
        .cm-property {color: #000;}
        .cm-operator {color: #000;}
        .cm-comment, .cm-code, .cm-quote{color:#666;}
        .cm-listitem {}
        .cm-meta {color: #555;}
        .cm-error {color: #f00;}
        .cm-qualifier {color: #555;}
        .cm-builtin {color: #30a;}
        .cm-bracket {color: #997;}
        .cm-tag {}
        .cm-attribute {color: #00c;}
        .cm-header {color: #000; font-size: 14px;font-weight:bold}
            .cm-header-1{font-size:24px}
            .cm-header-2{font-size:21px}
            .cm-header-3{font-size:18px}
            .cm-header-4{font-size:15px}
        .cm-formatting-header{font-weight:300}
        .cm-formatting-list {color: #000;font-weight:bold}
            .cm-formatting-list ul{}
        .cm-hr {color: #999;}
        .cm-linktext {}
        .cm-linkhref, span.cm-href, .cm-link{color:#00c; text-decoration:underline;}
        .cm-strong, .cm-emstrong{font-weight: bold;}
        .cm-em, .cm-emstrong{font-style: italic}

        .cm-formatting-task{
            font-weight: bold;

            &.cm-meta{color: $colorBorderLight;} //Checkbox unchecked
            &.cm-property{color: $colorSuccess;} //Checkbox checked
        }

        /* LaTeX and code blocks */
        .cm-delimit-latex, .cm-code-bracket-start, .cm-code-bracket-end{display:inline-block;height:100%;color:#353c4f;background:rgba(0,0,0,0.1);padding-right:3px;padding-left:3px;}
            .cm-latex.cm-bracket{color:#777}
        .cm-latex, .cm-code, .cm-quote, .cm-formatting-code + .cm-comment, .cm-formatting-code{color:#666;background: $colorBackgroundLight;padding:2px 0}
            .cm-code{display:inline-block} //This is only for code blocks. Inline code is .cm-comment
            .cm-latex.cm-tag{color:#11a;}
            .cm-latex:last-child, .cm-code:last-child{padding-right:3px;}
            .cm-code:first-child:last-child{width:100%;box-sizing:border-box;}

        span.cm-invalidchar {color: #f00;}

        div.CodeMirror span.CodeMirror-matchingbracket, div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #000; font-weight:bold}
    }
}


text:
angular.module('notes', ['notes.service', 'notes.utils', 'notes.ui', 'ngRoute', 'ui.codemirror', 'timeRelative']);
angular.module('notes.config', []);
angular.module('notes.service', ['notes.config', 'angularModalService', 'base64']);
angular.module('notes.utils', []);
angular.module('notes.ui', []);


text:
angular.module('notes').config(function($locationProvider, $routeProvider) {
    $locationProvider.html5Mode(false);
    $routeProvider
        .when('/', {
            templateUrl: '/static/js/views/main.html',
            controller: 'NotesCtrl',
            reloadOnSearch: false,
        })
        .when('/:noteId/', {
            templateUrl: '/static/js/views/preview.html',
            controller: 'NotesPreviewCtrl',
            reloadOnSearch: false,
        })
        .otherwise({ redirectTo: '/' });
});


text:
angular.module('notes.utils').factory('debounce', function($timeout, $q) {
    return function(func, wait, immediate) {
        var timeout;
        var deferred = $q.defer();
        return function() {
            var context = this, args = arguments;
            var later = function() {
                timeout = null;
                if(!immediate) {
                    deferred.resolve(func.apply(context, args));
                    deferred = $q.defer();
                }
            };
            var callNow = immediate && !timeout;
            if ( timeout ) {
                $timeout.cancel(timeout);
            }
            timeout = $timeout(later, wait);
            if (callNow) {
                deferred.resolve(func.apply(context,args));
                deferred = $q.defer();
            }
            return deferred.promise;
        };
    };
});


text:
// First, we define our own gfm spinoff
// Then, we multiplex it with LaTeX
CodeMirror.defineMode("gfm-custom", function(config) {
  var codeDepth = 0;
  function blankLine(state) {
    state.code = false;
    return null;
  }
  var gfmOverlay = {
    startState: function() {
      return {
        code: false,
        codeBlock: false,
        ateSpace: false
      };
    },
    copyState: function(s) {
      return {
        code: s.code,
        codeBlock: s.codeBlock,
        ateSpace: s.ateSpace
      };
    },
    token: function(stream, state) {
      // Hack to prevent formatting override inside code blocks (block and inline)
      if (state.codeBlock) {
        if (stream.match(/^```/)) {
          state.codeBlock = false;
          return "code-bracket-end";
        }
        stream.skipToEnd();
        return "code";
      }
      if (stream.sol()) {
        state.code = false;
      }
      if (stream.sol() && stream.match(/^```/)) {
        stream.skipToEnd();
        state.codeBlock = true;
        return "code-bracket-start";
      }

      // If this block is changed, it may need to be updated in Markdown mode
      if (stream.peek() === '`') {
        stream.next();
        var before = stream.pos;
        stream.eatWhile('`');
        var difference = 1 + stream.pos - before;
        if (!state.code) {
          codeDepth = difference;
          state.code = true;
        } else {
          if (difference === codeDepth) { // Must be exact
            state.code = false;
          }
        }
        return null;
      } else if (state.code) {
        stream.next();
        return null;
      }
      // Check if space. If so, links can be formatted later on
      if (stream.eatSpace()) {
        state.ateSpace = true;
        return null;
      }
      if (stream.match(/^((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i) &&
         stream.string.slice(stream.start - 2, stream.start) != "](") {
        return "link";
      }
      stream.next();
      return null;
    },
    blankLine: blankLine
  };
  CodeMirror.defineMIME("gfm-custom", {
    name: "markdown",
    underscoresBreakWords: false,
    taskLists: true,
    highlightFormatting: true,
    strikethrough: true,
    allowAtxHeaderWithoutSpace: true,
  });
  return CodeMirror.overlayMode(CodeMirror.getMode(config, "gfm-custom"), gfmOverlay);
}, "markdown");

CodeMirror.defineMode("gfm-latex", function(config, parserConfig) {
    return CodeMirror.multiplexingMode(
        CodeMirror.getMode(config, "gfm-custom"),
        {
            open: "$$$",
            close: "$$$",
            mode: CodeMirror.getMode(config, "text/x-latex"),
            delimStyle: "delimit-latex delimit-latex-block",
            innerStyle: "latex latex-block",
        },
        {
            open: "$$",
            close: "$$",
            mode: CodeMirror.getMode(config, "text/x-latex"),
            delimStyle: "delimit-latex",
            innerStyle: "latex",
        }
    );
}, 'gfm');


text:
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  var listRE = /^(\s*)(>[> ]*|[*+-]\s(\[[\sx]\])?|(\d+)([.)])(\s\[[\sx]\])?)(\s*)/,
      emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\d+)[.)])(\s\[[\sx]\])?(\s*)$/,
      unorderedListRE = /[*+-]\s/;

  CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
    if (cm.getOption("disableInput")) return CodeMirror.Pass;
    var ranges = cm.listSelections(), replacements = [];
    for (var i = 0; i < ranges.length; i++) {
      var pos = ranges[i].head;
      var eolState = cm.getStateAfter(pos.line);
      var inList = eolState.list !== false;
      var inQuote = eolState.quote !== 0;

      var line = cm.getLine(pos.line), match = listRE.exec(line);
      if (!ranges[i].empty() || (!inList && !inQuote) || !match) {
        cm.execCommand("newlineAndIndent");
        return;
      }
      if (emptyListRE.test(line)) {
        cm.replaceRange("", {
          line: pos.line, ch: 0
        }, {
          line: pos.line, ch: pos.ch + 1
        });
        replacements[i] = "\n";
      } else {
        var indent = match[1], after = match[7];
        var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
          ? match[2].replace('[x]', '[ ]')
          : (parseInt(match[4], 10) + 1) + match[5] + (match[6] || '').replace('[x]', '[ ]');

        replacements[i] = "\n" + indent + bullet + after;
      }
    }

    cm.replaceSelections(replacements);
  };
});