text:
package ec2

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sort"
	"strconv"
	"time"

	"launchpad.net/goamz/aws"
)

const (
	debug = false

	// legacyAPIVersion is the AWS API version used for all but
	// VPC-related requests.
	legacyAPIVersion = "2011-12-15"

	// AWS API version used for VPC-related calls.
	vpcAPIVersion = "2013-10-15"
)

// The EC2 type encapsulates operations with a specific EC2 region.
type EC2 struct {
	aws.Auth
	aws.Region
	private byte // Reserve the right of using private data.
}

// New creates a new EC2.
func New(auth aws.Auth, region aws.Region) *EC2 {
	return &EC2{auth, region, 0}
}

// ----------------------------------------------------------------------------
// Filtering helper.

// Filter builds filtering parameters to be used in an EC2 query which supports
// filtering.  For example:
//
//     filter := NewFilter()
//     filter.Add("architecture", "i386")
//     filter.Add("launch-index", "0")
//     resp, err := ec2.Instances(nil, filter)
//
type Filter struct {
	m map[string][]string
}

// NewFilter creates a new Filter.
func NewFilter() *Filter {
	return &Filter{make(map[string][]string)}
}

// Add appends a filtering parameter with the given name and value(s).
func (f *Filter) Add(name string, value ...string) {
	f.m[name] = append(f.m[name], value...)
}

func (f *Filter) addParams(params map[string]string) {
	if f != nil {
		a := make([]string, len(f.m))
		i := 0
		for k := range f.m {
			a[i] = k
			i++
		}
		sort.StringSlice(a).Sort()
		for i, k := range a {
			prefix := "Filter." + strconv.Itoa(i+1)
			params[prefix+".Name"] = k
			for j, v := range f.m[k] {
				params[prefix+".Value."+strconv.Itoa(j+1)] = v
			}
		}
	}
}

// ----------------------------------------------------------------------------
// Request dispatching logic.

// Error encapsulates an error returned by EC2.
//
// See http://goo.gl/VZGuC for more details.
type Error struct {
	// HTTP status code (200, 403, ...)
	StatusCode int
	// EC2 error code ("UnsupportedOperation", ...)
	Code string
	// The human-oriented error message
	Message   string
	RequestId string `xml:"RequestID"`
}

func (err *Error) Error() string {
	if err.Code == "" {
		return err.Message
	}

	return fmt.Sprintf("%s (%s)", err.Message, err.Code)
}

// For now a single error inst is being exposed. In the future it may be useful
// to provide access to all of them, but rather than doing it as an array/slice,
// use a *next pointer, so that it's backward compatible and it continues to be
// easy to handle the first error, which is what most people will want.
type xmlErrors struct {
	RequestId string  `xml:"RequestID"`
	Errors    []Error `xml:"Errors>Error"`
}

var timeNow = time.Now

func (ec2 *EC2) query(params map[string]string, resp interface{}) error {
	params["Timestamp"] = timeNow().In(time.UTC).Format(time.RFC3339)
	endpoint, err := url.Parse(ec2.Region.EC2Endpoint)
	if err != nil {
		return err
	}
	if endpoint.Path == "" {
		endpoint.Path = "/"
	}
	sign(ec2.Auth, "GET", endpoint.Path, params, endpoint.Host)
	endpoint.RawQuery = multimap(params).Encode()
	if debug {
		log.Printf("get { %v } -> {\n", endpoint.String())
	}
	r, err := http.Get(endpoint.String())
	if err != nil {
		return err
	}
	defer r.Body.Close()

	if debug {
		dump, _ := httputil.DumpResponse(r, true)
		log.Printf("response:\n")
		log.Printf("%v\n}\n", string(dump))
	}
	if r.StatusCode != 200 {
		return buildError(r)
	}
	err = xml.NewDecoder(r.Body).Decode(resp)
	return err
}

func multimap(p map[string]string) url.Values {
	q := make(url.Values, len(p))
	for k, v := range p {
		q[k] = []string{v}
	}
	return q
}

func buildError(r *http.Response) error {
	errors := xmlErrors{}
	xml.NewDecoder(r.Body).Decode(&errors)
	var err Error
	if len(errors.Errors) > 0 {
		err = errors.Errors[0]
	}
	err.RequestId = errors.RequestId
	err.StatusCode = r.StatusCode
	if err.Message == "" {
		err.Message = r.Status
	}
	return &err
}

func makeParams(action string) map[string]string {
	return makeParamsWithVersion(action, legacyAPIVersion)
}

func makeParamsVPC(action string) map[string]string {
	return makeParamsWithVersion(action, vpcAPIVersion)
}

func makeParamsWithVersion(action, version string) map[string]string {
	params := make(map[string]string)
	params["Action"] = action
	params["Version"] = version
	return params
}

func addParamsList(params map[string]string, label string, ids []string) {
	for i, id := range ids {
		params[label+"."+strconv.Itoa(i+1)] = id
	}
}

// ----------------------------------------------------------------------------
// Instance management functions and types.

// RunNetworkInterface encapsulates options for a single network
// interface, specified when calling RunInstances.
//
// If Id is set, it must match an existing VPC network interface, and
// in this case only a single instance can be launched. If Id is not
// set, a new network interface will be created for each instance.
//
// The following fields are required when creating a new network
// interface (i.e. Id is empty): DeviceIndex, SubnetId, Description
// (only used if set), SecurityGroupIds.
//
// PrivateIPs can be used to add one or more private IP addresses to a
// network interface. Only one of the IP addresses can be set as
// primary. If none are given, EC2 selects a primary IP for each
// created interface from the subnet pool.
//
// When SecondaryPrivateIPCount is non-zero, EC2 allocates that number
// of IP addresses from within the subnet range and sets them as
// secondary IPs. The number of IP addresses that can be assigned to a
// network interface varies by instance type.
type RunNetworkInterface struct {
	Id                      string
	DeviceIndex             int
	SubnetId                string
	Description             string
	PrivateIPs              []PrivateIP
	SecurityGroupIds        []string
	DeleteOnTermination     bool
	SecondaryPrivateIPCount int
}

// The RunInstances type encapsulates options for the respective request in EC2.
//
// See http://goo.gl/Mcm3b for more details.
type RunInstances struct {
	ImageId               string
	MinCount              int
	MaxCount              int
	KeyName               string
	InstanceType          string
	SecurityGroups        []SecurityGroup
	KernelId              string
	RamdiskId             string
	UserData              []byte
	AvailZone             string
	PlacementGroupName    string
	Monitoring            bool
	SubnetId              string
	DisableAPITermination bool
	ShutdownBehavior      string
	PrivateIPAddress      string
	BlockDeviceMappings   []BlockDeviceMapping
	NetworkInterfaces     []RunNetworkInterface
}

// Response to a RunInstances request.
//
// See http://goo.gl/Mcm3b for more details.
type RunInstancesResp struct {
	RequestId      string          `xml:"requestId"`
	ReservationId  string          `xml:"reservationId"`
	OwnerId        string          `xml:"ownerId"`
	SecurityGroups []SecurityGroup `xml:"groupSet>item"`
	Instances      []Instance      `xml:"instancesSet>item"`
}

// Instance encapsulates a running instance in EC2.
//
// See http://goo.gl/OCH8a for more details.
type Instance struct {
	InstanceId         string             `xml:"instanceId"`
	InstanceType       string             `xml:"instanceType"`
	ImageId            string             `xml:"imageId"`
	PrivateDNSName     string             `xml:"privateDnsName"`
	DNSName            string             `xml:"dnsName"`
	IPAddress          string             `xml:"ipAddress"`
	PrivateIPAddress   string             `xml:"privateIpAddress"`
	SubnetId           string             `xml:"subnetId"`
	VPCId              string             `xml:"vpcId"`
	SourceDestCheck    bool               `xml:"sourceDestCheck"`
	KeyName            string             `xml:"keyName"`
	AMILaunchIndex     int                `xml:"amiLaunchIndex"`
	Hypervisor         string             `xml:"hypervisor"`
	VirtType           string             `xml:"virtualizationType"`
	Monitoring         string             `xml:"monitoring>state"`
	AvailZone          string             `xml:"placement>availabilityZone"`
	PlacementGroupName string             `xml:"placement>groupName"`
	State              InstanceState      `xml:"instanceState"`
	Tags               []Tag              `xml:"tagSet>item"`
	SecurityGroups     []SecurityGroup    `xml:"groupSet>item"`
	NetworkInterfaces  []NetworkInterface `xml:"networkInterfaceSet>item"`
}

// RunInstances starts new instances in EC2.
// If options.MinCount and options.MaxCount are both zero, a single instance
// will be started; otherwise if options.MaxCount is zero, options.MinCount
// will be used instead.
//
// See http://goo.gl/Mcm3b for more details.
func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err error) {
	params := prepareRunParams(*options)
	params["ImageId"] = options.ImageId
	params["InstanceType"] = options.InstanceType
	var min, max int
	if options.MinCount == 0 && options.MaxCount == 0 {
		min = 1
		max = 1
	} else if options.MaxCount == 0 {
		min = options.MinCount
		max = min
	} else {
		min = options.MinCount
		max = options.MaxCount
	}
	params["MinCount"] = strconv.Itoa(min)
	params["MaxCount"] = strconv.Itoa(max)
	i, j := 1, 1
	for _, g := range options.SecurityGroups {
		if g.Id != "" {
			params["SecurityGroupId."+strconv.Itoa(i)] = g.Id
			i++
		} else {
			params["SecurityGroup."+strconv.Itoa(j)] = g.Name
			j++
		}
	}
	prepareBlockDevices(params, options.BlockDeviceMappings)
	prepareNetworkInterfaces(params, options.NetworkInterfaces)
	token, err := clientToken()
	if err != nil {
		return nil, err
	}
	params["ClientToken"] = token

	if options.KeyName != "" {
		params["KeyName"] = options.KeyName
	}
	if options.KernelId != "" {
		params["KernelId"] = options.KernelId
	}
	if options.RamdiskId != "" {
		params["RamdiskId"] = options.RamdiskId
	}
	if options.UserData != nil {
		userData := make([]byte, b64.EncodedLen(len(options.UserData)))
		b64.Encode(userData, options.UserData)
		params["UserData"] = string(userData)
	}
	if options.AvailZone != "" {
		params["Placement.AvailabilityZone"] = options.AvailZone
	}
	if options.PlacementGroupName != "" {
		params["Placement.GroupName"] = options.PlacementGroupName
	}
	if options.Monitoring {
		params["Monitoring.Enabled"] = "true"
	}
	if options.SubnetId != "" {
		params["SubnetId"] = options.SubnetId
	}
	if options.DisableAPITermination {
		params["DisableApiTermination"] = "true"
	}
	if options.ShutdownBehavior != "" {
		params["InstanceInitiatedShutdownBehavior"] = options.ShutdownBehavior
	}
	if options.PrivateIPAddress != "" {
		params["PrivateIpAddress"] = options.PrivateIPAddress
	}

	resp = &RunInstancesResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return
}

func prepareRunParams(options RunInstances) map[string]string {
	if options.SubnetId != "" || len(options.NetworkInterfaces) > 0 {
		// When either SubnetId or NetworkInterfaces are specified, we
		// need to use the API version with complete VPC support.
		return makeParamsVPC("RunInstances")
	} else {
		return makeParams("RunInstances")
	}
}

func prepareBlockDevices(params map[string]string, blockDevs []BlockDeviceMapping) {
	for i, b := range blockDevs {
		n := strconv.Itoa(i + 1)
		prefix := "BlockDeviceMapping." + n
		if b.DeviceName != "" {
			params[prefix+".DeviceName"] = b.DeviceName
		}
		if b.VirtualName != "" {
			params[prefix+".VirtualName"] = b.VirtualName
		}
		if b.SnapshotId != "" {
			params[prefix+".Ebs.SnapshotId"] = b.SnapshotId
		}
		if b.VolumeType != "" {
			params[prefix+".Ebs.VolumeType"] = b.VolumeType
		}
		if b.VolumeSize > 0 {
			params[prefix+".Ebs.VolumeSize"] = strconv.FormatInt(b.VolumeSize, 10)
		}
		if b.IOPS > 0 {
			params[prefix+".Ebs.Iops"] = strconv.FormatInt(b.IOPS, 10)
		}
		if b.DeleteOnTermination {
			params[prefix+".Ebs.DeleteOnTermination"] = "true"
		}
	}
}

func prepareNetworkInterfaces(params map[string]string, nics []RunNetworkInterface) {
	for i, ni := range nics {
		// Unlike other lists, NetworkInterface and PrivateIpAddresses
		// should start from 0, not 1, according to the examples
		// requests in the API documentation here http://goo.gl/Mcm3b.
		n := strconv.Itoa(i)
		prefix := "NetworkInterface." + n
		if ni.Id != "" {
			params[prefix+".NetworkInterfaceId"] = ni.Id
		}
		params[prefix+".DeviceIndex"] = strconv.Itoa(ni.DeviceIndex)
		if ni.SubnetId != "" {
			params[prefix+".SubnetId"] = ni.SubnetId
		}
		if ni.Description != "" {
			params[prefix+".Description"] = ni.Description
		}
		for j, gid := range ni.SecurityGroupIds {
			k := strconv.Itoa(j + 1)
			params[prefix+".SecurityGroupId."+k] = gid
		}
		if ni.DeleteOnTermination {
			params[prefix+".DeleteOnTermination"] = "true"
		}
		if ni.SecondaryPrivateIPCount > 0 {
			val := strconv.Itoa(ni.SecondaryPrivateIPCount)
			params[prefix+".SecondaryPrivateIpAddressCount"] = val
		}
		for j, ip := range ni.PrivateIPs {
			k := strconv.Itoa(j)
			subprefix := prefix + ".PrivateIpAddresses." + k
			params[subprefix+".PrivateIpAddress"] = ip.Address
			params[subprefix+".Primary"] = strconv.FormatBool(ip.IsPrimary)
		}
	}
}

func clientToken() (string, error) {
	// Maximum EC2 client token size is 64 bytes.
	// Each byte expands to two when hex encoded.
	buf := make([]byte, 32)
	_, err := rand.Read(buf)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(buf), nil
}

// Response to a TerminateInstances request.
//
// See http://goo.gl/3BKHj for more details.
type TerminateInstancesResp struct {
	RequestId    string                `xml:"requestId"`
	StateChanges []InstanceStateChange `xml:"instancesSet>item"`
}

// InstanceState encapsulates the state of an instance in EC2.
//
// See http://goo.gl/y3ZBq for more details.
type InstanceState struct {
	Code int    `xml:"code"` // Watch out, bits 15-8 have unpublished meaning.
	Name string `xml:"name"`
}

// InstanceStateChange informs of the previous and current states
// for an instance when a state change is requested.
type InstanceStateChange struct {
	InstanceId    string        `xml:"instanceId"`
	CurrentState  InstanceState `xml:"currentState"`
	PreviousState InstanceState `xml:"previousState"`
}

// TerminateInstances requests the termination of instances when the given ids.
//
// See http://goo.gl/3BKHj for more details.
func (ec2 *EC2) TerminateInstances(instIds []string) (resp *TerminateInstancesResp, err error) {
	params := makeParams("TerminateInstances")
	addParamsList(params, "InstanceId", instIds)
	resp = &TerminateInstancesResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return
}

// Response to a DescribeInstances request.
//
// See http://goo.gl/mLbmw for more details.
type InstancesResp struct {
	RequestId    string        `xml:"requestId"`
	Reservations []Reservation `xml:"reservationSet>item"`
}

// Reservation represents details about a reservation in EC2.
//
// See http://goo.gl/0ItPT for more details.
type Reservation struct {
	ReservationId  string          `xml:"reservationId"`
	OwnerId        string          `xml:"ownerId"`
	RequesterId    string          `xml:"requesterId"`
	SecurityGroups []SecurityGroup `xml:"groupSet>item"`
	Instances      []Instance      `xml:"instancesSet>item"`
}

// Instances returns details about instances in EC2.  Both parameters
// are optional, and if provided will limit the instances returned to those
// matching the given instance ids or filtering rules.
//
// See http://goo.gl/4No7c for more details.
func (ec2 *EC2) Instances(instIds []string, filter *Filter) (resp *InstancesResp, err error) {
	params := makeParams("DescribeInstances")
	addParamsList(params, "InstanceId", instIds)
	filter.addParams(params)
	resp = &InstancesResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return
}

// ----------------------------------------------------------------------------
// Image and snapshot management functions and types.

// Response to a DescribeImages request.
//
// See http://goo.gl/hLnyg for more details.
type ImagesResp struct {
	RequestId string  `xml:"requestId"`
	Images    []Image `xml:"imagesSet>item"`
}

// BlockDeviceMapping represents the association of a block device with an image.
//
// See http://goo.gl/wnDBf for more details.
type BlockDeviceMapping struct {
	DeviceName          string `xml:"deviceName"`
	VirtualName         string `xml:"virtualName"`
	SnapshotId          string `xml:"ebs>snapshotId"`
	VolumeType          string `xml:"ebs>volumeType"`
	VolumeSize          int64  `xml:"ebs>volumeSize"` // Size is given in GB
	DeleteOnTermination bool   `xml:"ebs>deleteOnTermination"`

	// The number of I/O operations per second (IOPS) that the volume supports.
	IOPS int64 `xml:"ebs>iops"`
}

// Image represents details about an image.
//
// See http://goo.gl/iSqJG for more details.
type Image struct {
	Id                 string               `xml:"imageId"`
	Name               string               `xml:"name"`
	Description        string               `xml:"description"`
	Type               string               `xml:"imageType"`
	State              string               `xml:"imageState"`
	Location           string               `xml:"imageLocation"`
	Public             bool                 `xml:"isPublic"`
	Architecture       string               `xml:"architecture"`
	Platform           string               `xml:"platform"`
	ProductCodes       []string             `xml:"productCode>item>productCode"`
	KernelId           string               `xml:"kernelId"`
	RamdiskId          string               `xml:"ramdiskId"`
	StateReason        string               `xml:"stateReason"`
	OwnerId            string               `xml:"imageOwnerId"`
	OwnerAlias         string               `xml:"imageOwnerAlias"`
	RootDeviceType     string               `xml:"rootDeviceType"`
	RootDeviceName     string               `xml:"rootDeviceName"`
	VirtualizationType string               `xml:"virtualizationType"`
	Hypervisor         string               `xml:"hypervisor"`
	BlockDevices       []BlockDeviceMapping `xml:"blockDeviceMapping>item"`
}

// Images returns details about available images.
// The ids and filter parameters, if provided, will limit the images returned.
// For example, to get all the private images associated with this account set
// the boolean filter "is-private" to true.
//
// Note: calling this function with nil ids and filter parameters will result in
// a very large number of images being returned.
//
// See http://goo.gl/SRBhW for more details.
func (ec2 *EC2) Images(ids []string, filter *Filter) (resp *ImagesResp, err error) {
	params := makeParams("DescribeImages")
	for i, id := range ids {
		params["ImageId."+strconv.Itoa(i+1)] = id
	}
	filter.addParams(params)

	resp = &ImagesResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return
}

// Response to a CreateSnapshot request.
//
// See http://goo.gl/ttcda for more details.
type CreateSnapshotResp struct {
	RequestId string `xml:"requestId"`
	Snapshot
}

// CreateSnapshot creates a volume snapshot and stores it in S3.
//
// See http://goo.gl/ttcda for more details.
func (ec2 *EC2) CreateSnapshot(volumeId, description string) (resp *CreateSnapshotResp, err error) {
	params := makeParams("CreateSnapshot")
	params["VolumeId"] = volumeId
	params["Description"] = description

	resp = &CreateSnapshotResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return
}

// DeleteSnapshots deletes the volume snapshots with the given ids.
//
// Note: If you make periodic snapshots of a volume, the snapshots are
// incremental so that only the blocks on the device that have changed
// since your last snapshot are incrementally saved in the new snapshot.
// Even though snapshots are saved incrementally, the snapshot deletion
// process is designed so that you need to retain only the most recent
// snapshot in order to restore the volume.
//
// See http://goo.gl/vwU1y for more details.
func (ec2 *EC2) DeleteSnapshots(ids []string) (resp *SimpleResp, err error) {
	params := makeParams("DeleteSnapshot")
	for i, id := range ids {
		params["SnapshotId."+strconv.Itoa(i+1)] = id
	}

	resp = &SimpleResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return
}

// Response to a DescribeSnapshots request.
//
// See http://goo.gl/nClDT for more details.
type SnapshotsResp struct {
	RequestId string     `xml:"requestId"`
	Snapshots []Snapshot `xml:"snapshotSet>item"`
}

// Snapshot represents details about a volume snapshot.
//
// See http://goo.gl/nkovs for more details.
type Snapshot struct {
	Id          string `xml:"snapshotId"`
	VolumeId    string `xml:"volumeId"`
	VolumeSize  string `xml:"volumeSize"`
	Status      string `xml:"status"`
	StartTime   string `xml:"startTime"`
	Description string `xml:"description"`
	Progress    string `xml:"progress"`
	OwnerId     string `xml:"ownerId"`
	OwnerAlias  string `xml:"ownerAlias"`
	Tags        []Tag  `xml:"tagSet>item"`
}

// Snapshots returns details about volume snapshots available to the user.
// The ids and filter parameters, if provided, limit the snapshots returned.
//
// See http://goo.gl/ogJL4 for more details.
func (ec2 *EC2) Snapshots(ids []string, filter *Filter) (resp *SnapshotsResp, err error) {
	params := makeParams("DescribeSnapshots")
	for i, id := range ids {
		params["SnapshotId."+strconv.Itoa(i+1)] = id
	}
	filter.addParams(params)

	resp = &SnapshotsResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return
}

// ----------------------------------------------------------------------------
// Security group management functions and types.

// SimpleResp represents a response to an EC2 request which on success will
// return no other information besides a request id.
type SimpleResp struct {
	XMLName   xml.Name
	RequestId string `xml:"requestId"`
}

// CreateSecurityGroupResp represents a response to a CreateSecurityGroup request.
type CreateSecurityGroupResp struct {
	SecurityGroup
	RequestId string `xml:"requestId"`
}

// CreateSecurityGroup creates a security group with the provided name
// and description.
//
// See http://goo.gl/Eo7Yl for more details.
func (ec2 *EC2) CreateSecurityGroup(name, description string) (resp *CreateSecurityGroupResp, err error) {
	return ec2.CreateSecurityGroupVPC("", name, description)
}

// CreateSecurityGroupVPC creates a security group in EC2, associated
// with the given VPC ID. If vpcId is empty, this call is equivalent
// to CreateSecurityGroup.
//
// See http://goo.gl/Eo7Yl for more details.
func (ec2 *EC2) CreateSecurityGroupVPC(vpcId, name, description string) (resp *CreateSecurityGroupResp, err error) {
	params := makeParamsVPC("CreateSecurityGroup")
	params["GroupName"] = name
	params["GroupDescription"] = description
	if vpcId != "" {
		params["VpcId"] = vpcId
	}

	resp = &CreateSecurityGroupResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	resp.Name = name
	return resp, nil
}

// SecurityGroupsResp represents a response to a DescribeSecurityGroups
// request in EC2.
//
// See http://goo.gl/k12Uy for more details.
type SecurityGroupsResp struct {
	RequestId string              `xml:"requestId"`
	Groups    []SecurityGroupInfo `xml:"securityGroupInfo>item"`
}

// SecurityGroup encapsulates details for a security group in EC2.
//
// See http://goo.gl/CIdyP for more details.
type SecurityGroupInfo struct {
	SecurityGroup
	VPCId       string   `xml:"vpcId"`
	OwnerId     string   `xml:"ownerId"`
	Description string   `xml:"groupDescription"`
	IPPerms     []IPPerm `xml:"ipPermissions>item"`
}

// IPPerm represents an allowance within an EC2 security group.
//
// See http://goo.gl/4oTxv for more details.
type IPPerm struct {
	Protocol     string              `xml:"ipProtocol"`
	FromPort     int                 `xml:"fromPort"`
	ToPort       int                 `xml:"toPort"`
	SourceIPs    []string            `xml:"ipRanges>item>cidrIp"`
	SourceGroups []UserSecurityGroup `xml:"groups>item"`
}

// UserSecurityGroup holds a security group and the owner
// of that group.
type UserSecurityGroup struct {
	Id      string `xml:"groupId"`
	Name    string `xml:"groupName"`
	OwnerId string `xml:"userId"`
}

// SecurityGroup represents an EC2 security group.
// If SecurityGroup is used as a parameter, then one of Id or Name
// may be empty. If both are set, then Id is used.
type SecurityGroup struct {
	Id   string `xml:"groupId"`
	Name string `xml:"groupName"`
}

// SecurityGroupNames is a convenience function that
// returns a slice of security groups with the given names.
func SecurityGroupNames(names ...string) []SecurityGroup {
	g := make([]SecurityGroup, len(names))
	for i, name := range names {
		g[i] = SecurityGroup{Name: name}
	}
	return g
}

// SecurityGroupNames is a convenience function that
// returns a slice of security groups with the given ids.
func SecurityGroupIds(ids ...string) []SecurityGroup {
	g := make([]SecurityGroup, len(ids))
	for i, id := range ids {
		g[i] = SecurityGroup{Id: id}
	}
	return g
}

// SecurityGroups returns details about security groups in EC2.  Both parameters
// are optional, and if provided will limit the security groups returned to those
// matching the given groups or filtering rules.
//
// See http://goo.gl/k12Uy for more details.
func (ec2 *EC2) SecurityGroups(groups []SecurityGroup, filter *Filter) (resp *SecurityGroupsResp, err error) {
	params := makeParams("DescribeSecurityGroups")
	i, j := 1, 1
	for _, g := range groups {
		if g.Id != "" {
			params["GroupId."+strconv.Itoa(i)] = g.Id
			i++
		} else {
			params["GroupName."+strconv.Itoa(j)] = g.Name
			j++
		}
	}
	filter.addParams(params)

	resp = &SecurityGroupsResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// DeleteSecurityGroup removes the given security group in EC2.
//
// See http://goo.gl/QJJDO for more details.
func (ec2 *EC2) DeleteSecurityGroup(group SecurityGroup) (resp *SimpleResp, err error) {
	params := makeParams("DeleteSecurityGroup")
	if group.Id != "" {
		params["GroupId"] = group.Id
	} else {
		params["GroupName"] = group.Name
	}

	resp = &SimpleResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// AuthorizeSecurityGroup creates an allowance for clients matching the provided
// rules to access instances within the given security group.
//
// See http://goo.gl/u2sDJ for more details.
func (ec2 *EC2) AuthorizeSecurityGroup(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) {
	return ec2.authOrRevoke("AuthorizeSecurityGroupIngress", group, perms)
}

// RevokeSecurityGroup revokes permissions from a group.
//
// See http://goo.gl/ZgdxA for more details.
func (ec2 *EC2) RevokeSecurityGroup(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) {
	return ec2.authOrRevoke("RevokeSecurityGroupIngress", group, perms)
}

func (ec2 *EC2) authOrRevoke(op string, group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) {
	params := makeParams(op)
	if group.Id != "" {
		params["GroupId"] = group.Id
	} else {
		params["GroupName"] = group.Name
	}

	for i, perm := range perms {
		prefix := "IpPermissions." + strconv.Itoa(i+1)
		params[prefix+".IpProtocol"] = perm.Protocol
		params[prefix+".FromPort"] = strconv.Itoa(perm.FromPort)
		params[prefix+".ToPort"] = strconv.Itoa(perm.ToPort)
		for j, ip := range perm.SourceIPs {
			params[prefix+".IpRanges."+strconv.Itoa(j+1)+".CidrIp"] = ip
		}
		for j, g := range perm.SourceGroups {
			subprefix := prefix + ".Groups." + strconv.Itoa(j+1)
			if g.OwnerId != "" {
				params[subprefix+".UserId"] = g.OwnerId
			}
			if g.Id != "" {
				params[subprefix+".GroupId"] = g.Id
			} else {
				params[subprefix+".GroupName"] = g.Name
			}
		}
	}

	resp = &SimpleResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// Tag represents key-value metadata used to classify and organize
// EC2 instances.
//
// See http://goo.gl/bncl3 for more details
type Tag struct {
	Key   string `xml:"key"`
	Value string `xml:"value"`
}

// CreateTags adds or overwrites one or more tags for the specified instance ids.
//
// See http://goo.gl/Vmkqc for more details
func (ec2 *EC2) CreateTags(instIds []string, tags []Tag) (resp *SimpleResp, err error) {
	params := makeParams("CreateTags")
	addParamsList(params, "ResourceId", instIds)

	for j, tag := range tags {
		params["Tag."+strconv.Itoa(j+1)+".Key"] = tag.Key
		params["Tag."+strconv.Itoa(j+1)+".Value"] = tag.Value
	}

	resp = &SimpleResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// Response to a StartInstances request.
//
// See http://goo.gl/awKeF for more details.
type StartInstanceResp struct {
	RequestId    string                `xml:"requestId"`
	StateChanges []InstanceStateChange `xml:"instancesSet>item"`
}

// Response to a StopInstances request.
//
// See http://goo.gl/436dJ for more details.
type StopInstanceResp struct {
	RequestId    string                `xml:"requestId"`
	StateChanges []InstanceStateChange `xml:"instancesSet>item"`
}

// StartInstances starts an Amazon EBS-backed AMI that you've previously stopped.
//
// See http://goo.gl/awKeF for more details.
func (ec2 *EC2) StartInstances(ids ...string) (resp *StartInstanceResp, err error) {
	params := makeParams("StartInstances")
	addParamsList(params, "InstanceId", ids)
	resp = &StartInstanceResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// StopInstances requests stopping one or more Amazon EBS-backed instances.
//
// See http://goo.gl/436dJ for more details.
func (ec2 *EC2) StopInstances(ids ...string) (resp *StopInstanceResp, err error) {
	params := makeParams("StopInstances")
	addParamsList(params, "InstanceId", ids)
	resp = &StopInstanceResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// RebootInstance requests a reboot of one or more instances. This operation is asynchronous;
// it only queues a request to reboot the specified instance(s). The operation will succeed
// if the instances are valid and belong to you.
//
// Requests to reboot terminated instances are ignored.
//
// See http://goo.gl/baoUf for more details.
func (ec2 *EC2) RebootInstances(ids ...string) (resp *SimpleResp, err error) {
	params := makeParams("RebootInstances")
	addParamsList(params, "InstanceId", ids)
	resp = &SimpleResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// ----------------------------------------------------------------------------
// Availability zone management functions and types.
// See http://goo.gl/ylxT4R for more details.

// AvailabilityZonesResp represents a response to a DescribeAvailabilityZones
// request in EC2.
type AvailabilityZonesResp struct {
	RequestId string                 `xml:"requestId"`
	Zones     []AvailabilityZoneInfo `xml:"availabilityZoneInfo>item"`
}

// AvailabilityZoneInfo encapsulates details for an availability zone in EC2.
type AvailabilityZoneInfo struct {
	AvailabilityZone
	State      string   `xml:"zoneState"`
	MessageSet []string `xml:"messageSet>item"`
}

// AvailabilityZone represents an EC2 availability zone.
type AvailabilityZone struct {
	Name   string `xml:"zoneName"`
	Region string `xml:"regionName"`
}

// AvailabilityZones returns details about availability zones in EC2.
// The filter parameter is optional, and if provided will limit the
// availability zones returned to those matching the given filtering
// rules.
//
// See http://goo.gl/ylxT4R for more details.
func (ec2 *EC2) AvailabilityZones(filter *Filter) (resp *AvailabilityZonesResp, err error) {
	params := makeParams("DescribeAvailabilityZones")
	filter.addParams(params)
	resp = &AvailabilityZonesResp{}
	err = ec2.query(params, resp)
	if err != nil {
		return nil, err
	}
	return
}

// AccountAttribute holds information about an account attribute.
//
// See http://goo.gl/hBc28j for more details.
type AccountAttribute struct {
	Name   string   `xml:"attributeName"`
	Values []string `xml:"attributeValueSet>item>attributeValue"`
}

// AccountAttributesResp is the response to an AccountAttributes request.
//
// See http://goo.gl/hBc28j for more details.
type AccountAttributesResp struct {
	RequestId  string             `xml:"requestId"`
	Attributes []AccountAttribute `xml:"accountAttributeSet>item"`
}

// AccountAttributes returns the values of one or more account
// attributes.
//
// See http://goo.gl/hBc28j for more details.
func (ec2 *EC2) AccountAttributes(attrNames ...string) (*AccountAttributesResp, error) {
	params := makeParamsVPC("DescribeAccountAttributes")
	for i, attrName := range attrNames {
		params["AttributeName."+strconv.Itoa(i+1)] = attrName
	}

	resp := &AccountAttributesResp{}
	if err := ec2.query(params, resp); err != nil {
		return nil, err
	}
	return resp, nil
}


text:
      Back

    <button class="ui negative button" ng-click="Delete()">

      Delete
    </button>

    <form class="ui form segment">

        <picture-card picture="picture"></picture-card>

        Title
        <input name="title" ng-model="picture.title" type="text">

        URL
        <input name="url" ng-model="picture.url" type="text">

      <button class="ui button" type="submit" ng-click="Update()">Update</button>
    </form>

redpajama_set_name: RedPajamaGithub


text:
<?php
// NEST SNAPPER

// ----------------------------------------------------------------------------

// Includes
require_once 'config.php';
require_once 'src/snapchat.php';

// ----------------------------------------------------------------------------

// Go!

// Log in:
$snapchat = new Snapchat(NEST_SNAPPER_USERNAME, NEST_SNAPPER_PASSWORD);

// Get your feed:
$snaps = $snapchat->getSnaps();

// ----------------------------------------------------------------------------

// Listing

header('Content-type: text/plain');

echo 'Looking for new snaps to forward. ';

foreach($snaps as $snap){

    if($snap->status == 1 && $snap->recipient == NEST_SNAPPER_USERNAME){
        // Download a snap:
        $data = $snapchat->getMedia($snap->id);

        // Mark the snap as viewed:
        $snapchat->markSnapViewed($snap->id);

        // Send
        echo 'Sending snap from ' . $snap->sender . ' to ' . implode(', ', $nesters) . '...';
        $id = $snapchat->upload(
            Snapchat::MEDIA_IMAGE,
            $data
        );
        $snapchat->send($id, $nesters, 8);
    }
}

// ----------------------------------------------------------------------------


text:
require 'spec_helper'
require 'i18n_translation_sync/cli'

describe I18nTranslationSync do
  describe 'invoked without arguments' do
    let(:list) { subject::CLI.start ['list'] }

    it 'returns the commands help' do
      expect{list}.to output("Hello world").to_stdout
    end
  end
end


text:
/* tslint:disable */
import { TestBed, getTestBed, inject, async } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { RateProvider } from './rate';
import { Logger } from '../../providers/logger/logger';
import {
  TranslateModule,
  TranslateService,
  TranslateLoader,
  TranslateFakeLoader
} from '@ngx-translate/core';

describe('RateProvider', () => {
  let injector: TestBed;
  let service: RateProvider;
  let httpMock: HttpTestingController;

  const btcResponse = [{"code":"BTC","name":"Bitcoin","rate":1},{"code":"USD","name":"US Dollar","rate":11535.74},{"code":"BCH","name":"Bitcoin Cash","rate":7.65734}];
  const bchResponse = [{"code":"BTC","name":"Bitcoin","rate":0.130377},{"code":"USD","name":"US Dollar","rate":1503.3},{"code":"BCH","name":"Bitcoin Cash","rate":1}];
  let btcUrl: string = 'https://bitpay.com/api/rates';
  let bchUrl: string = 'https://bitpay.com/api/rates/bch';

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule,
        TranslateModule.forRoot({
          loader: { provide: TranslateLoader, useClass: TranslateFakeLoader }
        })
      ],
      providers: [
        RateProvider,
        Logger
      ]
    });
    injector = getTestBed();
    service = injector.get(RateProvider);
    httpMock = injector.get(HttpTestingController);
  });

  it('should see if rates are available', () => {
    service.updateRatesBtc().then(response => {
      expect(service.isAvailable()).toBe(true);
    });

    httpMock.match(btcUrl)[1].flush(btcResponse);
    httpMock.match(bchUrl)[0].flush(bchResponse);
    httpMock.verify();
  });

  it('should get BTC rates', () => {
    service.updateRatesBtc().then(response => {
      expect(service.isAvailable()).toBe(true);
      expect(service.getRate('BTC')).toEqual(1);
      expect(service.getRate('USD')).toEqual(11535.74);
      expect(service.getRate('BCH')).toEqual(7.65734);
    });

    httpMock.match(btcUrl)[1].flush(btcResponse);
    httpMock.match(bchUrl)[0].flush(bchResponse);
    httpMock.verify();
  });

  it('should get BCH rates', () => {
    service.updateRatesBch().then(response => {
      expect(service.isAvailable()).toBe(true);
      expect(service.getRate('BTC', 'bch')).toEqual(0.130377);
      expect(service.getRate('USD', 'bch')).toEqual(1503.3);
      expect(service.getRate('BCH', 'bch')).toEqual(1);
    });

    httpMock.match(btcUrl)[0].flush(btcResponse);
    httpMock.match(bchUrl)[1].flush(bchResponse);
    httpMock.verify();
  });

  it('should catch an error on when call to update btc rates fails', () => {
    service.getBCH = (): Promise<any> => {
      let prom = new Promise((resolve, reject) => {
        reject('test rejection');
      });
      return prom;
    };

    service.updateRatesBch()
    .catch((err: any) => {
      expect(err).not.toBeNull();
    });
  });

  it('should catch an error on when call to update bch rates fails', () => {
    service.getBTC = (): Promise<any> => {
      let prom = new Promise((resolve, reject) => {
        reject('test rejection');
      });
      return prom;
    };

    service.updateRatesBtc()
    .catch((err: any) => {
      expect(err).not.toBeNull();
    });
  });

  it('should covert BCH satoshis to fiat', () => {
    // before we have rates
    expect(service.toFiat(0.25*1e+8, 'USD', 'bch')).toBeNull();

    // after we have rates
    service.updateRatesBch().then(response => {
      expect(service.isAvailable()).toBe(true);
      expect(service.toFiat(1*1e+8, 'USD', 'bch')).toEqual(1503.3);
      expect(service.toFiat(0.5*1e+8, 'USD', 'bch')).toEqual(751.65);
      expect(service.toFiat(0.25*1e+8, 'USD', 'bch')).toEqual(375.825);
    });

    httpMock.match(btcUrl)[0].flush(btcResponse);
    httpMock.match(bchUrl)[1].flush(bchResponse);
    httpMock.verify();
  });

  it('should covert fiat to BCH satoshis', () => {
    // before we have rates
    expect(service.fromFiat(0.25*1e+8, 'USD', 'bch')).toBeNull();

    // after we have rates
    service.updateRatesBch().then(response => {
      expect(service.isAvailable()).toBe(true);
      expect(service.fromFiat(1503.3, 'USD', 'bch')).toEqual(1*1e+8);
      expect(service.fromFiat(751.65, 'USD', 'bch')).toEqual(0.5*1e+8);
      expect(service.fromFiat(375.825, 'USD', 'bch')).toEqual(0.25*1e+8);
    });

    httpMock.match(btcUrl)[0].flush(btcResponse);
    httpMock.match(bchUrl)[1].flush(bchResponse);
    httpMock.verify();
  });

  it('should covert BTC satoshis to fiat', () => {
    // before we have rates
    expect(service.toFiat(0.25*1e+8, 'USD', 'btc')).toBeNull();

    // after we have rates
    service.updateRatesBtc().then(response => {
      expect(service.isAvailable()).toBe(true);
      expect(service.toFiat(1*1e+8, 'USD', 'btc')).toEqual(11535.74);
      expect(service.toFiat(0.5*1e+8, 'USD', 'btc')).toEqual(5767.87);
      expect(service.toFiat(0.25*1e+8, 'USD', 'btc')).toEqual(2883.935);
    });

    httpMock.match(btcUrl)[1].flush(btcResponse);
    httpMock.match(bchUrl)[0].flush(bchResponse);
    httpMock.verify();
  });

  it('should covert fiat to BTC satoshis', () => {
    // before we have rates
    expect(service.fromFiat(0.25*1e+8, 'USD', 'btc')).toBeNull();

    // after we have rates
    service.updateRatesBtc().then(response => {
      expect(service.isAvailable()).toBe(true);
      expect(service.fromFiat(11535.74, 'USD', 'btc')).toEqual(1*1e+8);
      expect(service.fromFiat(5767.87, 'USD', 'btc')).toEqual(0.5*1e+8);
      expect(service.fromFiat(2883.935, 'USD', 'btc')).toEqual(0.25*1e+8);
    });

    httpMock.match(btcUrl)[1].flush(btcResponse);
    httpMock.match(bchUrl)[0].flush(bchResponse);
    httpMock.verify();
  });

  it('should list alternatives', () => {
    // before we have rates
    expect(service.listAlternatives(false)).toEqual([]);
    expect(service.listAlternatives(true)).toEqual([]);

    // after we have rates
    service.updateRatesBtc().then(response => {
      expect(service.isAvailable()).toBe(true);
      expect(service.listAlternatives(false)).toEqual([
        {name: 'Bitcoin', isoCode: 'BTC'},
        {name: 'US Dollar', isoCode: 'USD'},
        {name: 'Bitcoin Cash', isoCode: 'BCH'}
      ]);
      expect(service.listAlternatives(true)).toEqual([
        {name: 'Bitcoin', isoCode: 'BTC'},
        {name: 'Bitcoin Cash', isoCode: 'BCH'},
        {name: 'US Dollar', isoCode: 'USD'}
      ]);
    });

    httpMock.match(btcUrl)[1].flush(btcResponse);
    httpMock.match(bchUrl)[0].flush(bchResponse);
    httpMock.verify();
  });

  it('should resolve when rates are available', () => {
    // before we have rates
    expect(service.isAvailable()).toBe(false);

    service.whenRatesAvailable().then(response => {
      // after we have rates
      expect(service.isAvailable()).toBe(true);

      // hit the if in whenRatesAvailable
      service.whenRatesAvailable();
    });

    httpMock.match(btcUrl)[1].flush(btcResponse);
    httpMock.match(bchUrl)[0].flush(bchResponse);
    httpMock.verify();
  });
});


text:
app.directive('crudGrid', function () {
    return {
        restrict: 'A',
        replace: false,
        scope: { options: "=" },
        templateUrl: '/Scripts/app/Directives/Templates/crud-grid-directive-template.html',
        controller: ['$scope', '$element', '$attrs', 'crudGridDataFactory', 'notificationFactory', 'crudGridMetadataFactory',
            function ($scope, $element, $attrs, crudGridDataFactory, notificationFactory, crudGridMetadataFactory) {

                //here will be placed default values grid parameters
                var defaultOptions = {
                    gridInitialized : null,
                    visuals: {
                        showEdit: true,
                        showRemove: true,
                        showAdd: true,
                        showPagination: true,
                        showRecordLegend: true,
                        showSearch : false,
                        caption: '',
                    },
                    pageSize: 10,
                    customColumns: [],
                    getParameters: {},
                    routes: {
                        query: '',
                        create: '',
                        update: '',
                        metadata: '',
                        delete: ''
                    },
                    defaultValues: {},
                    rowCallback : null
                };

                $scope.id = $attrs.id || 'crudGrid';
                $scope.baseUrl = $attrs.tableName || $attrs.url;
                $scope.metadataUrl = '';
                $scope.metadata = new Object();
                $scope.objects = [];
                $scope.object = new Object();
                $scope.addMode = false;
                $scope.fields = new Array();
                $scope.loading = true;
                $scope.orderBy = { field: '', asc: true };
                $scope.caption = '';
                $scope.page = 1;
                $scope.pages = 1;
                $scope.backEnabled = false;
                $scope.forwardEnabled = true;
                $scope.searchText = '';

                var loadingFunction = function () { $scope.loading = false; };

                var setNavigationButtons = function() {
                    $scope.getCurrentRowCount();

                    if ($scope.page <= 1) {
                        $scope.backEnabled = false;
                    } else {
                        $scope.backEnabled = true;
                    }

                    if ($scope.page >= $scope.pages) {
                        $scope.forwardEnabled = false;
                    } else {
                        $scope.forwardEnabled = true;
                    }
                };

                var successCallback = function (e) {
                    notificationFactory.success();
                    $scope.getData();
                };

                var errorCallback = function (e) {
                    notificationFactory.error(e.data.ExceptionMessage);
                };

                var dataLoadErrorCallback = function (e) {
                    notificationFactory.error(e.data.MessageDetail);
                    loadingFunction();
                };

                var getRequestParams = function () {
                    var pagingParams = { Size: $scope.options.pageSize, Page: $scope.page, Search: $scope.searchText, SortName: $scope.orderBy.field, SortOrder: $scope.orderBy.asc ? "Asc" : "Desc" };
                    var params = $scope.options.getParameters != null ? $.extend({}, pagingParams, $scope.options.getParameters) : pagingParams;
                    return params;
                };

                $scope.deleteObject = function (object) {
                    crudGridDataFactory($scope.baseUrl + ($scope.options.routes.delete || '')).delete({ id: object[$scope.metadata.Key] }, successCallback, errorCallback);
                };

                $scope.getData = function () {
                        crudGridDataFactory($scope.baseUrl + ($scope.options.routes.query || ''), getRequestParams()).query(function (data) {
                            $scope.objects = data;
                            setNavigationButtons();
                            loadingFunction();
                        }, dataLoadErrorCallback);
                };

                $scope.showActionsColumn = function () {
                    return ($scope.options.visuals.showEdit | $scope.options.visuals.showRemove | $scope.options.visuals.showAdd);
                };

                $scope.showColumn = function (column) {
                    return column.ColumnVisible;
                };

                $scope.goToPage = function (index) {
                    $scope.page = index;
                    $scope.getData();
                };

                $scope.back = function () {
                    if ($scope.page > 1) {
                        $scope.page = $scope.page - 1;
                        $scope.getData();
                    }
                };

                $scope.forward = function () {
                    if ($scope.page < $scope.pages) {
                        $scope.page = $scope.page + 1;
                        $scope.getData();
                    }
                };

                $scope.setOrderBy = function (field) {
                    var asc = $scope.orderBy.field === field ? !$scope.orderBy.asc : true;
                    $scope.orderBy = { field: field, asc: asc };
                    $scope.getData();
                };

                $scope.customColumValue = function (column, rowData) {
                    if (column.IsCustom && column.Callback) {
                        if ({}.toString.call(column.Callback) === '[object Function]') {
                            return '' + column.Callback(rowData) + '';
                        } else {
                            return '' + (column.Callback || '') + '';
                        }
                    } else {
                        if (column.Datatype == 'Boolean') {
                            return (rowData[column.Name] ? ' ' : ' ');
                        }
                        return '' + (rowData[column.Name] || '') + '';
                    }
                };

                $scope.initializeObject = function () {
                    $scope.object = $.extend({}, {}, $scope.options.defaultValues);
                };

                $scope.setMetadataUrl = function () {
                    $scope.metadataUrl = $attrs.tableName != null ?
                       ('Metadata/' + $scope.baseUrl + ($scope.options.routes.metadata || '')) :
                       ($scope.baseUrl + ($scope.options.routes.metadata || ''));
                };

                $scope.getMetadata = function() {
                    crudGridMetadataFactory($scope.metadataUrl, getRequestParams()).metadata(function (data) {
                        $scope.fields = new Array();
                        $scope.metadata = data;
                        $scope.caption = $scope.options.visuals.caption || data.Name;
                        $.each(data.Fields, function (i, field) {
                            field.IsCustom = false;
                            var customField = Enumerable.From($scope.options.customColumns).FirstOrDefault(null, function (x) { return x.Name == field.Name; });
                            if (customField != null) {
                                field.IsCustom = true;
                                var newField = $.extend({}, field, customField);
                                $scope.fields.push(newField);
                            } else {
                                $scope.fields.push(field);
                            }
                        });

                        //Finally fill the other cusotm fields
                        $.each($scope.options.customColumns, function (i, field) {
                            field.IsCustom = true;
                            if (!Enumerable.From($scope.fields).Any(function (x) { return x.Name == field.Name; })) {
                                if (field.ColumnVisible == null) field.ColumnVisible = true;
                                if (field.FieldVisible == null) field.FieldVisible = true;
                                $scope.fields.push(field);
                            }
                        });

                        //Order fields
                            $scope.fields.sort(function (obj1, obj2) {
                                return obj1.Order - obj2.Order;
                            });

                        $scope.orderBy = { field: $scope.fields[0].Name, asc: true };
                        $scope.pages = Math.ceil((Number($scope.metadata.RowCount) / $scope.options.pageSize));
                    });
                };

                $scope.getCurrentRowCount = function() {
                    crudGridMetadataFactory($scope.metadataUrl, getRequestParams()).metadata(function (data) {
                        $scope.metadata.RowCount = data.RowCount;
                        $scope.pages = Math.ceil((Number($scope.metadata.RowCount) / $scope.options.pageSize));
                    });
                };

                init = function () {
                    $scope.options =
                    $scope.options != null ?
                        (($scope.options.constructor == "string".constructor) ?
                            $.extend({}, defaultOptions, $.parseJSON($scope.options)) :
                            $.extend({}, defaultOptions, $scope.options))
                        : defaultOptions;
                    if ($scope.options.routes == null) $scope.options.routes = defaultOptions.routes;
                    $scope.options.visuals = $.extend({}, defaultOptions.visuals, $scope.options.visuals);
                    $scope.initializeObject();
                    $scope.setMetadataUrl();
                    $scope.getMetadata();
                };

                init();

                $scope.$watch("options.getParameters", function () {
                    $scope.getMetadata();
                    $scope.getData();
                });

                $scope.$watch("options.routes.metadata", $scope.setMetadataUrl);
            }]
    };
});


text:
package com.novaordis.gc.parser.linear.cms;

import com.novaordis.gc.model.Timestamp;
import com.novaordis.gc.model.event.cms.CMSConcurrentMark;
import com.novaordis.gc.model.event.cms.CMSConcurrentMarkStart;
import com.novaordis.gc.model.event.cms.CMSConcurrentPreclean;
import com.novaordis.gc.model.event.cms.CMSInitialMark;
import com.novaordis.gc.model.event.GCEvent;
import com.novaordis.gc.parser.CurrentMax;
import com.novaordis.gc.parser.Duration;
import com.novaordis.gc.parser.GCEventParserBase;
import com.novaordis.gc.parser.ParserException;
import com.novaordis.gc.parser.linear.LineUtil;

import java.util.List;

public class CMSParser extends GCEventParserBase
{
    // Constants -------------------------------------------------------------------------------------------------------

    // Static ----------------------------------------------------------------------------------------------------------

    /**
     * This method is invoked on a line after it was established that line contains "CMS-initial-mark".
     */
    public static CMSInitialMark parseCMSInitialMark(Timestamp ts, String line, long lineNumber)
        throws ParserException
    {
        List<String> tokens = LineUtil.toSquareBracketTokens(line, lineNumber);

        String gcInfo = tokens.get(0);

        // sometimes we also get user/sys/real time information in the second token - but not always. We are
        // ignoring it anyway, as the same information is also included in gcInfo

        tokens = LineUtil.toSquareBracketTokens(gcInfo, lineNumber);

        String prefix = tokens.get(0);
        String ogString = tokens.get(1);
        String heapString = tokens.get(2);
        String durationString = tokens.get(3);

        // sanity check - prefix must be 'GC'

        if (!"GC".equals(prefix))
        {
            throw new ParserException("CMS-initial-mark line does not contain\"GC\"", lineNumber);
        }

        // get OG info - throw everything before "CMS-initial-mark:"

        int i = ogString.indexOf("CMS-initial-mark:");

        if (i == -1)
        {
            throw new ParserException("CMS-initial-mark line does not contain\"CMS-initial-mark:\"", lineNumber);
        }

        ogString = ogString.substring(i + "CMS-initial-mark:".length()).trim();

        CurrentMax og = new CurrentMax(ogString, lineNumber);
        CurrentMax heap = new CurrentMax(heapString, lineNumber);
        long duration = Duration.toLongMilliseconds(durationString, lineNumber);

        return new CMSInitialMark(ts, duration, og, heap);
    }

    // Attributes ------------------------------------------------------------------------------------------------------

    // Constructors ----------------------------------------------------------------------------------------------------

    // GCEventParser ---------------------------------------------------------------------------------------------------

    /**
     * Example of recognized line:
     *
     * [CMS-concurrent-mark-start]
     *
     * @see com.novaordis.gc.parser.GCEventParser#parse(com.novaordis.gc.model.Timestamp, String, long, GCEvent)
     */
    @Override
    public GCEvent parse(Timestamp ts, String line, long lineNumber, GCEvent current)
        throws ParserException
    {
        // all processing of the known CMS logging output is done in a try/catch block, so we can cleanly handle parsing
        // error (malformed CMS lines, for example)

        try
        {
            if (line.contains("CMS-initial-mark"))
            {
                // this is the beginning of a CMS cycle
                return parseCMSInitialMark(ts, line, lineNumber);
            }
            else if (line.startsWith("[CMS-concurrent-mark-start"))
            {
                return new CMSConcurrentMarkStart(ts);
            }
            else if (line.contains("CMS-concurrent-preclean"))
            {
                return new CMSConcurrentPreclean(ts);
            }
            else if (line.contains("CMS-concurrent-mark"))
            {
                return new CMSConcurrentMark(ts);
            }
            else
            {
                // we don't recognize this line as CMS logging output
                return null;
            }
        }
        catch(Exception e)
        {
            throw new ParserException(
                    "invalid/unrecognized CMS line: \"" + line + "\"" +
                            (e.getMessage() != null ? " (" + e.getClass().getName() + ": " + e.getMessage() +
                                ") at line " + lineNumber: ""), e, lineNumber);
        }
    }

    // Public ----------------------------------------------------------------------------------------------------------

    // Package protected -----------------------------------------------------------------------------------------------

    // Protected -------------------------------------------------------------------------------------------------------

    // Private ---------------------------------------------------------------------------------------------------------

    // Inner classes ---------------------------------------------------------------------------------------------------
}


text:
class CreateTimeslots < ActiveRecord::Migration
  def change
    create_table :timeslots do |t|
      t.date :date
      t.string :time
      t.references :ca
      t.string :client_name
      t.string :phone_number
      t.string :apt_number
      t.string :current_tenant

      t.timestamps null: false
    end
  end
end


text:
Ext.define('Workflow.view.cls.NorthToolbar', {
	extend: 'Ext.toolbar.Toolbar',

    initComponent: function() {
        Ext.applyIf(this, {
            region: 'north',
            items: [{
            	icon : Ext.CONTEXT_ROOT + '/static/resources/images/disk_multiple_disabled.png',
            	tooltip: 'tooltip',
            	handler: function() {
            		if(Workflow.CONFIG.AUTO_SAVE){
            			this.setIcon(Ext.CONTEXT_ROOT + '/static/resources/images/disk_multiple_disabled.png');
            			Workflow.CONFIG.AUTO_SAVE = false;
            		} else {
            			this.setIcon(Ext.CONTEXT_ROOT + '/static/resources/images/disk_multiple.png');
            			Workflow.CONFIG.AUTO_SAVE = true;
            		}

                }
            }, '-', {
                name : 'cut',
                description : 'description',
                icon: Ext.CONTEXT_ROOT + "/static/resources/images/cut.png"
            }, {
            	name : 'copy',
                description : 'description',
                icon: Ext.CONTEXT_ROOT + "/static/resources/images/page_copy.png"
            }, {
            	name : 'paste',
                description : 'description',
                icon: Ext.CONTEXT_ROOT + "/static/resources/images/page_paste.png"
            }, {
            	name : 'del',
                description : 'description',
                icon: Ext.CONTEXT_ROOT + "/static/resources/images/cross.png"
            }, '-', {
            	name : 'btf',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_move_front.png"
            }, {
            	name : 'btb',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_move_back.png"
            }, {
            	name : 'bf',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_move_forwards.png"
            }, {
            	name : 'bb',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_move_backwards.png"
            }, '-', {
            	name : 'undo',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/arrow_undo.png"
            }, {
            	name : 'redo',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/arrow_redo.png"
            }, '-', {
            	name : 'AddDocker.add',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/vector_add.png",
            }, {
            	name : 'AddDocker.del',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/vector_delete.png"
            }, '-', {
            	name : 'ab',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_align_bottom.png"
            }, {
            	name : 'am',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_align_middle.png"
            }, {
            	name : 'at',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_align_top.png"
            }, {
            	name : 'al',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_align_left.png"
            }, {
            	name : 'ac',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_align_center.png"
            }, {
            	name : 'ar',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_align_right.png"
            }, {
            	name : 'as',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/shape_align_size.png"
            }, '-', {
            	name : 'zoomIn',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/magnifier_zoom_in.png"
            }, {
            	name : 'zoomOut',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/magnifier_zoom_out.png"
            }, {
            	name : 'zoomStandard',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/zoom_standard.png",
            	disabled :true
            }, {
            	name : 'zoomFitToModel',
            	icon: Ext.CONTEXT_ROOT + "/static/resources/images/image.png",
            	disabled :true
            }]
        });
        this.callParent(arguments);
    },

    getItems : function(){
    	var iconRoot = Ext.CONTEXT_ROOT + "/static/resources/images/";

    	var items = [];

    	return items;
    }
});


text:
from django.utils.translation import ugettext_lazy as _
from models import Entry
from django.contrib import admin
from settings import STATIC_URL
from django.contrib.flatpages.models import FlatPage
from django.contrib.flatpages.admin import FlatPageAdmin as FlatPageAdminOld

class EntryAdmin(admin.ModelAdmin):
	def save_model(self, request, obj, form, change):
		if obj.startTime == None:
			obj.startTime = '00:00:00'
		if obj.endTime == None:
			obj.endTime = '23:59:00'
		if obj.startTime == '00:00:00' and obj.endTime == '23:59:00':
			obj.wholeDay = True
		else:
			obj.wholeDay = False
		obj.save()

	def makePublished(self, request, queryset):
		rows_updated = queryset.update(published = True)
		if rows_updated == 1:
			message_bit = _("1 event was")
		else:
			message_bit = _("{0} events were").format(rows_updated)
		self.message_user(request, _("{0} successfully marked as published.").format(message_bit))
	makePublished.short_description = _('Publish selected events')

	def makeDraft(self, request, queryset):
		rows_updated = queryset.update(published = False)
		if rows_updated == 1:
			message_bit = _("1 event was")
		else:
			message_bit = _("{0} events were").format(rows_updated)
		self.message_user(request, _("{0} successfully marked as draft.").format(message_bit))
	makeDraft.short_description = _('Unpublish selected events')

	actions = [makePublished, makeDraft]

	fieldsets = [
		(None,						{'fields': ['name', 'published']}),
		(None,						{'fields': ['description']}),
		(_('Date'),					{'fields': [('startDay', 'endDay'), 'daily', ('startTime', 'endTime')]}),
		(_('Further options'),		{'fields': ['url']}),
	]

	# Automatically fill the url field based on what's given as title.
	prepopulated_fields = {'url': ('name',)}

	search_fields = ['name', 'url', 'description']

	list_display = ('name', 'wholeDay', 'startDay', 'endDay', 'startTime', 'endTime', 'published')
	list_filter = ('published',)

	# WYSIWTF-Editor
	class Media:
		js = [STATIC_URL + 'grappelli/tinymce/jscripts/tiny_mce/tiny_mce.js', STATIC_URL + 'grappelli/tinymce_setup/tinymce_setup.js',]

admin.site.register(Entry, EntryAdmin)

class FlatPageAdmin(FlatPageAdminOld):
	class Media:
		js = [STATIC_URL + 'grappelli/tinymce/jscripts/tiny_mce/tiny_mce.js', STATIC_URL + 'grappelli/tinymce_setup/tinymce_setup.js',]

# We have to unregister it, and then reregister
admin.site.unregister(FlatPage)
admin.site.register(FlatPage, FlatPageAdmin)


text:
package vtk.web.actions.copymove;

import java.net.URLDecoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Required;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import vtk.repository.Path;
import vtk.repository.Repository;
import vtk.repository.Resource;
import vtk.repository.ResourceLockedException;
import vtk.web.RequestContext;

public class CopyBackupController implements Controller {

    private CopyHelper copyHelper;

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Path resourceToCopySrcUri = null;
        try {
            String uri = URLDecoder.decode(request.getParameter("uri"), "UTF-8");
            resourceToCopySrcUri = Path.fromStringWithTrailingSlash(uri);
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            return null;
        }
        if (resourceToCopySrcUri == null) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            return null;
        }

        // Retrieve resource
        RequestContext requestContext = RequestContext.getRequestContext(request);
        String token = requestContext.getSecurityToken();
        Repository repository = requestContext.getRepository();
        Resource resource = repository.retrieve(token, resourceToCopySrcUri, false);

        // If resource is a collection OR parent resource does NOT equals request resource
        if (resource.isCollection() || !requestContext.getResourceURI().equals(resource.getURI().getParent())) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            return null;
        }

        // Copy resource
        Path resourceCopyDestUri = null;
        try {
          resourceCopyDestUri = copyHelper.makeDestUri(resourceToCopySrcUri, repository, token, resource);
          repository.copy(token, null, resourceToCopySrcUri, resourceCopyDestUri, false, true);
        } catch (ResourceLockedException rle) {
          response.setStatus(423);
          return null;
        } catch (Exception e) {
          response.setStatus(HttpServletResponse.SC_FORBIDDEN);
          return null;
        }

        // Return 201 with destination uri in the Location-header
        response.setStatus(HttpServletResponse.SC_CREATED);
        response.setHeader("Location", resourceCopyDestUri.toString());

        return null;
    }

    @Required
    public void setCopyHelper(CopyHelper copyHelper) {
        this.copyHelper = copyHelper;
    }

}


text:
<?xml version="1.0" encoding="utf-8"?>
<resources name="ru" flag="ru" title="Russian">
	<string_add name="_apps"><![CDATA[Приложения]]></string_add>
	<string_add name="_never"><![CDATA[никогда]]></string_add>
	<string_add name="_adm_bp_txt_block_class"><![CDATA[CSS класс]]></string_add>
	<string_add name="_adm_dbd_txt_c_clear_purifier"><![CDATA[Очистить кэш Purifier]]></string_add>
	<string_add name="_adm_dbd_txt_c_clear_opcache"><![CDATA[Очистить Opcache]]></string_add>
	<string_add name="_adm_form_btn_search_forms_sortable_fields_reset"><![CDATA[Сбросить]]></string_add>
	<string_add name="_adm_from_err_search_forms_sortable_fields_reset"><![CDATA[Не могу сбросить форму поиска.]]></string_add>
	<string_add name="_adm_form_txt_field_type_dateselect"><![CDATA[выбор даты]]></string_add>
	<string_add name="_adm_form_txt_search_forms_sortable_fields"><![CDATA[Поля для сортировки]]></string_add>
	<string_add name="_adm_form_txt_search_forms_sortable_fields_caption"><![CDATA[Заголовок]]></string_add>
	<string_add name="_adm_form_txt_search_forms_sortable_fields_direction"><![CDATA[Направление]]></string_add>
	<string_add name="_adm_lmi_cpt_search_sortable_fields"><![CDATA[Поля сортироски поиска]]></string_add>
	<string_add name="_adm_nav_txt_items_collapsed"><![CDATA[Свёрнуто]]></string_add>
	<string_add name="_adm_nav_txt_block_hidden_on_pt"><![CDATA[Скрыть на страницах типа]]></string_add>
	<string_add name="_adm_nav_txt_block_hidden_on_col"><![CDATA[Скрыть в колонках]]></string_add>
	<string_add name="_adm_nav_txt_block_hidden_on_col_thin"><![CDATA[Тонкая]]></string_add>
	<string_add name="_adm_nav_txt_block_hidden_on_col_half"><![CDATA[Половина]]></string_add>
	<string_add name="_adm_nav_txt_block_hidden_on_col_wide"><![CDATA[Широкая]]></string_add>
	<string_add name="_adm_nav_txt_block_hidden_on_col_full"><![CDATA[Полная]]></string_add>
	<string_add name="_adm_stg_cpt_option_permalinks_seo_links"><![CDATA[Включить ссылки оптимизированные для поисковых систем]]></string_add>
	<string_add name="_adm_stg_cpt_option_permalinks_seo_links_redirects"><![CDATA[Включить перенаправление с обычных ссылок на оптимизированные]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_accounts_pruning"><![CDATA[Удалять учетные записи]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_accounts_pruning_interval"><![CDATA[Период удаления учетных записей (в днях)]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_lock_from_unauthenticated"><![CDATA[Заблокировать сайт от неавторизованных пользователей]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_lock_from_unauthenticated_exceptions"><![CDATA[Cписок URI страниц (разделенный запятыми), которые доступны для неавторизованных пользователей]]></string_add>
	<string_add name="_adm_stg_cpt_option_value_sys_account_pruning_no"><![CDATA[Не удалять]]></string_add>
	<string_add name="_adm_stg_cpt_option_value_sys_account_pruning_no_login_delete"><![CDATA[Удалить учетные записи без входа в течение N дней]]></string_add>
	<string_add name="_adm_stg_cpt_option_value_sys_account_pruning_no_login_suspend"><![CDATA[Приостановить учетные записи без входа в систему на N дней]]></string_add>
	<string_add name="_adm_stg_cpt_option_value_sys_account_pruning_no_confirm_delete"><![CDATA[Удалить учетные записи, которые не подтверждали электронную почту в течение N дней]]></string_add>
	<string_add name="_adm_stg_cpt_option_value_sys_account_pruning_no_profile_delete"><![CDATA[Удалить учетную запись, которая не создавала ни одного профиля в течение N дней]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_cf_enable"><![CDATA[Включить контентные фильтры]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_cf_enable_comments"><![CDATA[Включить контентные фильтры для комментариев]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_cf_prohibited"><![CDATA[Запрещенный контент]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_pt_default_visitor"><![CDATA[Тип страницы, используемый по умолчанию для посетителя]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_pt_default_member"><![CDATA[Тип страницы, используемый по умолчанию для пользователя]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_format_input_date"><![CDATA[Формат даты для полей ввода]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_format_input_datetime"><![CDATA[Формат даты и времени для полей ввода]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_format_input_24h"><![CDATA[Использовать 24ч формат времени в каледаре ввода времени]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_csp_frame_ancestors"><![CDATA[Значение заголовка 'Content-Security-Policy: frame-ancestors']]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_notify_to_approve_by_role"><![CDATA[Оповещать участников контекста с определенной ролью о необходимости одобрять контент в контексте]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_quill_insert_as_plain_text"><![CDATA[Quill - вставлять как текст]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_vote_reactions_quick_mode"><![CDATA[Включить режим 'Быстрых реакций' - голосовать при клике, используя реакцию по умолчанию]]></string_add>
	<string_add name="_adm_stg_cpt_option_sys_cmts_enable_auto_approve"><![CDATA[Автоматически активировать комментарий после создания]]></string_add>
	<string_add name="_cmt_txt_note_approve_pending"><![CDATA[Комментарий будет опубликован, как только пройдет проверку модератором.]]></string_add>
	<string_add name="_sys_audit_content_info_popup_title"><![CDATA[Информация]]></string_add>
	<string_add name="_sys_audit_action_account_login"><![CDATA[Вход]]></string_add>
	<string_add name="_sys_audit_action_account_failed_login_attempt"><![CDATA[Неудачная попытка входа]]></string_add>
	<string_add name="_sys_audit_action_account_logout"><![CDATA[Выход]]></string_add>
	<string_add name="_sys_cmts"><![CDATA[комментарии]]></string_add>
	<string_add name="_sys_cmts_administration_grid_column_title_adm_active"><![CDATA[Активные]]></string_add>
	<string_add name="_sys_form_input_caption_author"><![CDATA[Автор]]></string_add>
	<string_add name="_sys_form_acl"><![CDATA[Форма уровней доступа]]></string_add>
	<string_add name="_sys_form_display_acl_set"><![CDATA[Форма уровней доступа: Установить]]></string_add>
	<string_add name="_sys_form_popup_acl_set"><![CDATA[Установить уровень доступа]]></string_add>
	<string_add name="_sys_form_acl_input_sys_level_id"><![CDATA[Уровень]]></string_add>
	<string_add name="_sys_form_acl_input_level_id"><![CDATA[Уровень]]></string_add>
	<string_add name="_sys_form_acl_input_sys_duration"><![CDATA[Продолжительность]]></string_add>
	<string_add name="_sys_form_acl_input_duration"><![CDATA[Продолжительность]]></string_add>
	<string_add name="_sys_form_acl_input_duration_inf"><![CDATA[В днях. Оставьте пустым для неограниченного уровня.]]></string_add>
	<string_add name="_sys_form_acl_input_sys_do_submit"><![CDATA[Выполнить]]></string_add>
	<string_add name="_sys_form_acl_input_do_submit"><![CDATA[Выполнить]]></string_add>
	<string_add name="_sys_form_acl_input_sys_do_cancel"><![CDATA[Отмена]]></string_add>
	<string_add name="_sys_form_acl_input_do_cancel"><![CDATA[Отмена]]></string_add>
	<string_add name="_sys_form_entry_input_sys_cf"><![CDATA[Фильтр контента]]></string_add>
	<string_add name="_sys_form_entry_input_cf"><![CDATA[Выберите аудиторию]]></string_add>
	<string_add name="_sys_form_profile"><![CDATA[Профиль]]></string_add>
	<string_add name="_sys_form_display_profile_cf_set"><![CDATA[Настроить фильтр контента]]></string_add>
	<string_add name="_sys_form_display_profile_cf_manage"><![CDATA[Управлять фильтром контента]]></string_add>
	<string_add name="_sys_form_profile_input_sys_cfw_value"><![CDATA[Фильтр контента]]></string_add>
	<string_add name="_sys_form_profile_input_cfw_value"><![CDATA[Показывать мне контент, отмеченный как]]></string_add>
	<string_add name="_sys_form_profile_input_sys_cfu_items"><![CDATA[Содержимое контентного фильтра]]></string_add>
	<string_add name="_sys_form_profile_input_cfu_items"><![CDATA[Доступные категории контентного фильтра]]></string_add>
	<string_add name="_sys_form_profile_input_sys_cfu_locked"><![CDATA[Заблокировать контентный фильтр]]></string_add>
	<string_add name="_sys_form_profile_input_cfu_locked"><![CDATA[Заблокировать контентный фильтр]]></string_add>
	<string_add name="_sys_form_profile_input_sys_do_submit"><![CDATA[Выполнить]]></string_add>
	<string_add name="_sys_form_profile_input_do_submit"><![CDATA[Выполнить]]></string_add>
	<string_add name="_sys_form_profile_input_sys_do_cancel"><![CDATA[Отмена]]></string_add>
	<string_add name="_sys_form_profile_input_do_cancel"><![CDATA[Отмена]]></string_add>
	<string_add name="_sys_profile_cf_set_successfully_submitted"><![CDATA[Фильтр контента был успешно обновлен]]></string_add>
	<string_add name="_sys_profile_cf_manage_successfully_submitted"><![CDATA[Фильтр контента был успешно обновлен]]></string_add>
	<string_add name="_sys_profile_cf_manage_popup"><![CDATA[Управлять фильтром контента]]></string_add>
	<string_add name="_sys_form_display_ps_gc_members"><![CDATA[Конфиденциальность: Пользовательская группа на основании профилей]]></string_add>
	<string_add name="_sys_form_display_ps_gc_memberships"><![CDATA[Конфиденциальность: Пользовательская группа на основании членств]]></string_add>
	<string_add name="_sys_form_ps_gc_input_caption_system_memberships"><![CDATA[Список членств]]></string_add>
	<string_add name="_sys_form_ps_gc_input_caption_memberships"><![CDATA[Выбрать]]></string_add>
	<string_add name="_sys_form_wiki_input_caption_close"><![CDATA[Закрыть]]></string_add>
	<string_add name="_sys_form_wiki_input_caption_files"><![CDATA[Изображения]]></string_add>
	<string_add name="_sys_menu_item_title_profile_settings_cfilter"><![CDATA[Фильтр контента]]></string_add>
	<string_add name="_sys_menu_item_title_sm_friends_mutual"><![CDATA[Общие друзья: {0}]]></string_add>
	<string_add name="_sys_menu_item_title_system_profile_settings_cfilter"><![CDATA[Фильтр контента]]></string_add>
	<string_add name="_sys_menu_item_title_system_apps"><![CDATA[Приложения]]></string_add>
	<string_add name="_sys_menu_item_title_system_member_avatar"><![CDATA[Аватар пользователя]]></string_add>
	<string_add name="_sys_menu_item_title_system_member_followings"><![CDATA[Отслеживаемые контексты пользователя]]></string_add>
	<string_add name="_sys_menu_item_title_system_member_menu"><![CDATA[Меню пользователя]]></string_add>
	<string_add name="_sys_menu_item_title_system_public_menu"><![CDATA[Публичное меню]]></string_add>
	<string_add name="_sys_menu_item_title_system_sm_friends_mutual"><![CDATA[Общие друзья]]></string_add>
	<string_add name="_sys_menu_set_title_application"><![CDATA[Приложение]]></string_add>
	<string_add name="_sys_menu_set_title_panel"><![CDATA[Панель]]></string_add>
	<string_add name="_sys_menu_template_title_panel"><![CDATA[Панель сайта - Меню]]></string_add>
	<string_add name="_sys_menu_template_title_main_in_panel"><![CDATA[Навигация по сайту - Панель]]></string_add>
	<string_add name="_sys_menu_title_main_in_panel"><![CDATA[Навигация по сайту - Панель]]></string_add>
	<string_add name="_sys_menu_title_application"><![CDATA[Навигация в режиме 'Приложение']]></string_add>
	<string_add name="_sys_menu_title_panel"><![CDATA[Панель сайта]]></string_add>
	<string_add name="_sys_page_block_title_profile_settings_cfilter"><![CDATA[Фильтр контента]]></string_add>
	<string_add name="_sys_page_block_title_sys_author"><![CDATA[Автор]]></string_add>
	<string_add name="_sys_page_block_title_author"><![CDATA[Автор]]></string_add>
	<string_add name="_sys_page_block_title_std_dash_queues"><![CDATA[Очереди]]></string_add>
	<string_add name="_sys_page_block_title_wiki_pages_list"><![CDATA[Список страниц]]></string_add>
	<string_add name="_sys_page_block_title_wiki_page_contents"><![CDATA[Содержание страницы]]></string_add>
	<string_add name="_sys_page_title_profile_settings_cfilter"><![CDATA[Фильтр контента]]></string_add>
	<string_add name="_sys_page_title_system_profile_settings_cfilter"><![CDATA[Фильтр контента]]></string_add>
	<string_add name="_sys_page_title_wiki_pages_list"><![CDATA[Подстраница: Список страниц]]></string_add>
	<string_add name="_sys_page_title_wiki_page_contents"><![CDATA[Подстраница: Содержание страницы]]></string_add>
	<string_add name="_sys_page_type_application"><![CDATA[Приложение]]></string_add>
	<string_add name="_sys_page_type_standard"><![CDATA[Стандартная]]></string_add>
	<string_add name="_sys_page_type_wo_hf"><![CDATA[Без заголовка и основания]]></string_add>
	<string_add name="_sys_pre_lists_content_filter"><![CDATA[Фильтр контента]]></string_add>
	<string_add name="_sys_pre_lists_content_filter_g"><![CDATA[G]]></string_add>
	<string_add name="_sys_pre_lists_content_filter_pg"><![CDATA[PG]]></string_add>
	<string_add name="_sys_pre_lists_content_filter_pg13"><![CDATA[PG-13]]></string_add>
	<string_add name="_sys_pre_lists_content_filter_r"><![CDATA[R]]></string_add>
	<string_add name="_sys_pre_lists_content_filter_x"><![CDATA[X]]></string_add>
	<string_add name="_sys_ps_group_title_memberships_selected"><![CDATA[Избранные уровни членства...]]></string_add>
	<string_add name="_sys_show_less"><![CDATA[Показать меньше]]></string_add>
	<string_add name="_sys_txt_error_profile_is_not_defined"><![CDATA[Профиль не определён]]></string_add>
	<string_add name="_sys_txt_error_profile_update"><![CDATA[Не удалось обновить профиль]]></string_add>
	<string_add name="_sys_txt_switch_profiles"><![CDATA[Переключиться в]]></string_add>
	<string_add name="_sys_txt_share_popup_header"><![CDATA[Поделиться]]></string_add>
	<string_add name="_sys_txt_share_popup_url"><![CDATA[URL]]></string_add>
	<string_add name="_sys_txt_share_popup_copy_to_clipboard_text"><![CDATA[Кликните чтобы скопировать]]></string_add>
	<string_add name="_sys_txt_share_popup_embeded_code"><![CDATA[Код для вставки]]></string_add>
	<string_add name="_sys_txt_search_sort_by"><![CDATA[Сортировать по]]></string_add>
	<string_add name="_sys_txt_search_sort_by_default"><![CDATA[Умолчанию]]></string_add>
	<string_add name="_sys_wiki_copy"><![CDATA[Копировать в буфер обмена]]></string_add>
	<string_add name="_sys_wiki_open_in_editor"><![CDATA[Открыть в редакторе]]></string_add>
	<string_add name="_sys_wiki_external_editor_references_comment"><![CDATA[ВНИМАНИЕ: не меняйте это сообщение и строки ниже]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_bold"><![CDATA[Добавить полужиржирный текст <cmd+b>]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_italic"><![CDATA[Добавить курсив <cmd+i>]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_underline"><![CDATA[Добавить подчернутый текст <cmd+u>]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_clean"><![CDATA[Убрать форматирование]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_list_ordered"><![CDATA[Маркированный список]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_list_bullet"><![CDATA[Нумерованный список]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_indent_1"><![CDATA[Уменьшить отступ]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_indent_2"><![CDATA[Увеличить отступ]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_blockquote"><![CDATA[Добавить цитату]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_direction"><![CDATA[Направление текста]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_script_sub"><![CDATA[Нижний индекс]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_script_super"><![CDATA[Верхний индекс]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_link"><![CDATA[Вставить ссылку]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_image"><![CDATA[Вставить картинку]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_code_block"><![CDATA[Вставить код]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_color"><![CDATA[Цвет текста]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_background"><![CDATA[Цвет фона]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_align"><![CDATA[Выравнивание текста]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_header"><![CDATA[Стиль текста]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_embed"><![CDATA[Вставить ембед]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_embed_popup_header"><![CDATA[Вставить адрес дла ембеда]]></string_add>
	<string_add name="_sys_txt_quill_tooltip_link_popup_header"><![CDATA[Вставить адрес]]></string_add>
	<string_add name="_sys_queue_emails"><![CDATA[Emails]]></string_add>
	<string_add name="_sys_queue_push"><![CDATA[PUSH]]></string_add>
	<string_add name="_sys_queue_transcoding"><![CDATA[Транскодирование видео]]></string_add>
	<string_add name="_sys_queue_storage"><![CDATA[Хранилище файлов]]></string_add>

	<string_upd name="_adm_bp_dsc_page_type_id"><![CDATA[Способ отображения страницы. Значение по умолчанию может быть изменено в Студия -> Дизайнер -> Настройки.]]></string_upd>
	<string_upd name="_adm_nav_dsc_items_icon"><![CDATA[Вы можете использовать здесь иконку для шрифта, SVG код или эмодзи]]></string_upd>
	<string_upd name="_adm_stg_cpt_option_sys_format_date"><![CDATA[Формат даты]]></string_upd>
	<string_upd name="_adm_stg_cpt_option_sys_format_datetime"><![CDATA[Формат даты и времени]]></string_upd>
	<string_upd name="_adm_stg_cpt_option_sys_format_time"><![CDATA[Формат времени]]></string_upd>
	<string_upd name="_sys_acl_action_show_membership_levels_in_privacy_groups"><![CDATA[Использовать уровни членства в полях конфиденциальности]]></string_upd>
	<string_upd name="_sys_acl_action_show_membership_levels_in_privacy_groups_desc"><![CDATA[Разрешить ограничивать доступ по уровню(ям) членства в полях конфиденциальности]]></string_upd>
	<string_upd name="_sys_currency"><![CDATA[{0} {1}]]></string_upd>
	<string_upd name="_sys_et_txt_body_confirmation"><![CDATA[{email_header}

Привет, {name} ({email}).

Пожалуйста, подтвердите ваш email, нажав по ссылке:

{conf_link}

{email_footer}]]></string_upd>
    <string_upd name="_sys_et_txt_body_forgot"><![CDATA[{email_header}

Привет {name} ({email})
,

Пожалуйста, нажмите по ссылке, чтобы сбросить свой пароль:

Сбросить мой пароль.

{email_footer}]]></string_upd>
	<string_upd name="_sys_form_input_labels_placeholder"><![CDATA[Добавить метку]]></string_upd>
	<string_upd name="_sys_form_ps_gc_input_caption_system_do_cancel"><![CDATA[Отмена]]></string_upd>
	<string_upd name="_sys_form_ps_gc_input_caption_do_cancel"><![CDATA[Отмена]]></string_upd>
	<string_upd name="_sys_menu_item_title_system_main_menu"><![CDATA[Главное меню]]></string_upd>
	<string_upd name="_sys_menu_title_main"><![CDATA[Навигация по сайту]]></string_upd>
	<string_upd name="_sys_page_type_default"><![CDATA[Тип по умолчанию]]></string_upd>
	<string_upd name="_sys_pruning_db"><![CDATA[
Очистка базы данных:

{0} удалено просроченных членств

{1} удалено просроченных сессий

{2} удалено просроченных ключей

{3} удалено просроченных токенов хранилища

{4} удалено просроченных транскодированных изображений

{5} удалено/заблокировано аккаунтов

]]></string_upd>

	<string_del name="_adm_stg_cpt_option_sys_install_time"><![CDATA[Дата установки]]></string_del>
	<string_del name="_sys_form_display_ps_gc_manage"><![CDATA[Конфиденциальность: Управлять пользовательской группой]]></string_del>
	<string_del name="_sys_page_type_default_wo_hf"><![CDATA[Без заголовка и основания]]></string_del>
</resources>


text:
namespace Zoolandia.Animals
{
  public class Genus : TaxonomyTerm
  {

  }
}


text:
package org.apache.airavata.gsi.ssh.api.job;

/**
 * Created by IntelliJ IDEA.
 * User: lahirugunathilake
 * Date: 8/22/13
 * Time: 7:19 AM
 * To change this template use File | Settings | File Templates.
 */
public enum JobType {
            SERIAL, SINGLE, MPI, MULTIPLE, CONDOR
}


text:
layout: post
title: Learning from Bezos
author: Dan Temkin
excerpt: What to take away from Jeff Bezos in his Amazon annual shareholders letters.
tags:
- Amazon
- NLP
- Python
- Bezos
- spaCy
excerpt_separator:
type: project
comments: false
---
---
Code used to develop this analysis can be found on my [Github](https://github.com/dtemkin/blog/blob/master/code/bezos-letters/2017-11-29-Bezos-Letters.py)

### Intro and Data Processing
Recently, I came across an editorial on Medium.com entitled "[What I learned from Jeff Bezos after reading every Amazon shareholder letter](https://medium.com/parsa-vc/what-i-learned-from-jeff-bezos-after-reading-every-amazon-shareholder-letter-172d92f38a41)". As the title suggests, the editorial is centered around the author's take-aways after reading the statements made by Jeff Bezos at the annual Amazon shareholders meeting from 1997 to 2016 [link](https://cdn.rawgit.com/dtemkin/blog/0b390703/code/bezos-letters/amzn-shareholder-letters-1997-2016.pdf).

Similarly, I thought it would be interesting to detect the thematic elements of Jeff Bezos' shareholder letters using topic modelling. In order to perform this analysis I opted to use the LDA [Latent Dirchlet Allocation]() topic model with coherence as the primary evaluation measure.

It was a little difficult preparing the data in a format that would be appropriate for the LDA. This was because all of the letters were contained within a single file. However, I was able to use 'To our share' as an effective split point. I couldn't use the full word 'shareholders' because in several of the documents Bezos uses the term 'shareowners'. Nevertheless, this split allowed for each document to be tokenized, stemmed and stripped of stops independently.

By in large, I used the default tokenization provided by spaCy although I did ammend it to handle conjunctions such as "we're", "you'll", etc.

The first step I took after processing the text was constructing a frequency distribution of all the unique words in all the documents.

{% include /static/bezos/bezos-table.html %}

This frequency distribution served two functions. First, was to give me a first look at the subject of the documents also it helped to highlight stopwords that were domain specific.
While, in some instances it is prudent to remove the words in the top/bottom n percent from each document before running the LDA because they can cause the topics modelled to be unusually similar, in this instance given the integral nature of customers to every facet of the company it seemed organic to allow the topics to share customers in many instances.
As such from the frequency distribution it is easy to see that customers along with services and timeliness play a big role in the companys' operations.

### Topic Modelling and Analysis
In performing the LDA it is necessary to decide on the number of topics to return and in order to optimally set this parameter
I used the default Coherence measure used with gensim to evaluate several prospective models each with a different number of topics to return.

#### Coherence Measures

{% include /static/bezos/bezos-coherences.html %}

The assessment of Coherence measures revealed that the optimal model was one with 5 topics.
Then I used pyLDAviz to display the generated topics and below was the result

#### LDA Visualization

{% include /static/bezos/bezos-ldaviz.html %}

#### LDA Topics & Interpretation

{% include /static/bezos/topic-table.html %}

Finally, what I learned from a topic model of Jeff Bezos' shareholder letters from the last 20 years -
- The customer is king and the company's business and continued success depend heavily on customer experience
- Customer service is integral to growth both in terms of buyers and sellers
- Customer service should be the anchor to major company decisions
- The company must remain competitive in the marketplace both by offering competitive pricing and keeping costs down.
- Competitiveness in the marketplace depends on the adaptive offering products and services offered as well as maintaining an aggressive, agile and happy workforce

### Conclusion

Interestingly, what I learned was not far off of those interpreted by the author from the above Medium article. Though his were more specific and detailed we ended up with the same overall sentiments. That being said it would be interesting to expand this study going forward to other company's to see if the same consistency in message is present or if it is unique to Amazon.

Lastly, one point of consideration, the coherence values used in evaluating LDA models with a varying number of topics came back much differently than expected. I expected the values to rise before eventually stabilizing which, as evident in the plot above is not what occurred. I am not sure if this was due to the limited number of documents or the model used but an examination of topics modelled using NMF (Non-Negative Matrix Factorization) might yield more positive results in this regard.


text:
package org.psjava.algo.geometry.convexhull;

import org.psjava.goods.GoodSortingAlgorithm;

public class DivideAndConquerConvexHullTest extends ConvexHullAlgorithmTestBase {

    @Override
    protected ConvexHullAlgorithm getAlgorithm() {
        return DivideAndConquerConvexHull.getInstance(GoodSortingAlgorithm.getInstance());
    }
}


text:
'use strict'

module.exports = function(req, res, next) {

  // User is allowed, proceed to the next policy,
  // or if this is the last policy, the controller
  var user = req.session.user;
  if(user && user.uid === 'admin'){
    return next();
  }

  // User is not allowed
  // (default res.forbidden() behavior can be overridden in `config/403.js`)
  // return res.forbidden('You are not permitted to perform this action.');
  return res.forbidden('对不起,您没有权限执行此操作!');
};


text:
import rangy from 'rangy'

import {contenteditableSpanBug} from './feature-detection'
import * as nodeType from './node-type'
import eventable from './eventable'

/**
 * The Keyboard module defines an event API for key events.
 */

export default class Keyboard {
  constructor (selectionWatcher) {
    eventable(this)
    this.selectionWatcher = selectionWatcher
  }

  dispatchKeyEvent (event, target, notifyCharacterEvent) {
    switch (event.keyCode) {
      case this.key.left:
        return this.notify(target, 'left', event)

      case this.key.right:
        return this.notify(target, 'right', event)

      case this.key.up:
        return this.notify(target, 'up', event)

      case this.key.down:
        return this.notify(target, 'down', event)

      case this.key.tab:
        if (event.shiftKey) return this.notify(target, 'shiftTab', event)
        return this.notify(target, 'tab', event)

      case this.key.esc:
        return this.notify(target, 'esc', event)

      case this.key.backspace:
        this.preventContenteditableBug(target, event)
        return this.notify(target, 'backspace', event)

      case this.key.delete:
        this.preventContenteditableBug(target, event)
        return this.notify(target, 'delete', event)

      case this.key.enter:
        if (event.shiftKey) return this.notify(target, 'shiftEnter', event)
        return this.notify(target, 'enter', event)

      case this.key.ctrl:
      case this.key.shift:
      case this.key.alt:
        return

      // Metakey
      case 224: // Firefox: 224
      case 17: // Opera: 17
      case 91: // Chrome/Safari: 91 (Left)
      case 93: // Chrome/Safari: 93 (Right)
        return

      default:
        // Added here to avoid using fall-through in the switch
        // when b or i are pressed without ctrlKey or metaKey
        if (event.keyCode === this.key.b && (event.ctrlKey || event.metaKey)) {
          return this.notify(target, 'bold', event)
        }
        if (event.keyCode === this.key.i && (event.ctrlKey || event.metaKey)) {
          return this.notify(target, 'italic', event)
        }

        this.preventContenteditableBug(target, event)
        if (!notifyCharacterEvent) return
        // Don't notify character events as long as either the ctrl or
        // meta key are pressed.
        // see: https://github.com/livingdocsIO/editable.js/pull/125
        if (!event.ctrlKey && !event.metaKey) return this.notify(target, 'character', event)
    }
  }

  preventContenteditableBug (target, event) {
    if (!contenteditableSpanBug) return
    if (event.ctrlKey || event.metaKey) return

    // This fixes a strange webkit bug that can be reproduced as follows:
    //
    // 1. A node used within a contenteditable has some style, e.g through the
    //    following CSS:
    //
    //      strong {
    //        color: red
    //      }
    //
    // 2. A selection starts with the first character of a styled node and ends
    //    outside of that node, e.g: "big beautiful" is selected in the following
    //    html:
    //
    //

    //        Hello big beautiful world
    //

    //
    // 3. The user types a letter character to replace "big beautiful", e.g. "x"
    //
    // Result: Webkits adds  and  tags:
    //
    //

    //      Hello
    //
    //        f
    //
    //      world
    //

    //
    // This bug ONLY happens, if the first character of the node is selected and
    // the selection goes further than the node.
    //
    // Solution:
    //
    // Manually remove the element that would be removed anyway before inserting
    // the new letter.
    const rangyInstance = this.selectionWatcher.getFreshRange()
    if (!rangyInstance.isSelection) return

    const nodeToRemove = Keyboard.getNodeToRemove(rangyInstance.range, target)
    if (nodeToRemove) nodeToRemove.remove()
  }

  static getNodeToRemove (selectionRange, target) {
    // This function is only used by preventContenteditableBug. It is exposed on
    // the Keyboard constructor for testing purpose only.

    // Let's make sure we are in the edge-case, in which the bug happens.
    // The selection does not start at the beginning of a node. We have
    // nothing to do.
    if (selectionRange.startOffset !== 0) return

    let startNodeElement = selectionRange.startContainer

    // If the node is a textNode, we select its parent.
    if (startNodeElement.nodeType === nodeType.textNode) startNodeElement = startNodeElement.parentNode

    // The target is the contenteditable element, which we do not want to replace
    if (startNodeElement === target) return

    // We get a range that contains everything within the sartNodeElement to test
    // if the selectionRange is within the startNode, we have nothing to do.
    const startNodeRange = rangy.createRange()
    startNodeRange.setStartBefore(startNodeElement.firstChild)
    startNodeRange.setEndAfter(startNodeElement.lastChild)
    if (startNodeRange.containsRange(selectionRange)) return

    // If the selectionRange.startContainer was a textNode, we have to make sure
    // that its parent's content starts with this node. Content is either a
    // text node or an element. This is done to avoid false positives like the
    // following one:
    // foobar|bazquux|
    if (selectionRange.startContainer.nodeType === nodeType.textNode) {
      const contentNodeTypes = [nodeType.textNode, nodeType.elementNode]
      let firstContentNode = startNodeElement.firstChild

      do {
        if (contentNodeTypes.indexOf(firstContentNode.nodeType) !== -1) break
      } while ((firstContentNode = firstContentNode.nextSibling))

      if (firstContentNode !== selectionRange.startContainer) return
    }

    // Now we know, that we have to return at least the startNodeElement for
    // removal. But it could be, that we also need to remove its parent, e.g.
    // we need to remove  in the following example:
    // |foobarbaz|
    const rangeStartingBeforeCurrentElement = selectionRange.cloneRange()
    rangeStartingBeforeCurrentElement.setStartBefore(startNodeElement)

    return Keyboard.getNodeToRemove(
      rangeStartingBeforeCurrentElement,
      target
    ) || startNodeElement
  }
}

Keyboard.key = Keyboard.prototype.key = {
  left: 37,
  up: 38,
  right: 39,
  down: 40,
  tab: 9,
  esc: 27,
  backspace: 8,
  delete: 46,
  enter: 13,
  shift: 16,
  ctrl: 17,
  alt: 18,
  b: 66,
  i: 73
}