<commit_before>package hyperv

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"time"

	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/mcnflag"
	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/docker/machine/libmachine/ssh"
	"github.com/docker/machine/libmachine/state"
)

type Driver struct {
	*drivers.BaseDriver
	boot2DockerURL string
	boot2DockerLoc string
	vSwitch        string
	diskImage      string
	DiskSize       int
	MemSize        int
}

const (
	defaultDiskSize = 20000
	defaultMemory   = 1024
)

func NewDriver(hostName, storePath string) drivers.Driver {
	return &Driver{
		DiskSize: defaultDiskSize,
		MemSize:  defaultMemory,
		BaseDriver: &drivers.BaseDriver{
			MachineName: hostName,
			StorePath:   storePath,
		},
	}
}

// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
	return []mcnflag.Flag{
		mcnflag.StringFlag{
			Name:  "hyperv-boot2docker-url",
			Usage: "Hyper-V URL of the boot2docker image. Defaults to the latest available version.",
		},
		mcnflag.StringFlag{
			Name:  "hyperv-boot2docker-location",
			Usage: "Hyper-V local boot2docker iso. Overrides URL.",
		},
		mcnflag.StringFlag{
			Name:  "hyperv-virtual-switch",
			Usage: "Hyper-V virtual switch name. Defaults to first found.",
		},
		mcnflag.IntFlag{
			Name:  "hyperv-disk-size",
			Usage: "Hyper-V disk size for host in MB.",
			Value: defaultDiskSize,
		},
		mcnflag.IntFlag{
			Name:  "hyperv-memory",
			Usage: "Hyper-V memory size for host in MB.",
			Value: defaultMemory,
		},
	}
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.boot2DockerURL = flags.String("hyperv-boot2docker-url")
	d.boot2DockerLoc = flags.String("hyperv-boot2docker-location")
	d.vSwitch = flags.String("hyperv-virtual-switch")
	d.DiskSize = flags.Int("hyperv-disk-size")
	d.MemSize = flags.Int("hyperv-memory")
	d.SwarmMaster = flags.Bool("swarm-master")
	d.SwarmHost = flags.String("swarm-host")
	d.SwarmDiscovery = flags.String("swarm-discovery")
	d.SSHUser = "docker"
	d.SSHPort = 22
	return nil
}

func (d *Driver) GetSSHHostname() (string, error) {
	return d.GetIP()
}

func (d *Driver) GetSSHUsername() string {
	if d.SSHUser == "" {
		d.SSHUser = "docker"
	}

	return d.SSHUser
}

// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
	return "hyperv"
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}
	if ip == "" {
		return "", nil
	}
	return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, "2376")), nil
}

func (d *Driver) GetState() (state.State, error) {

	command := []string{
		"(",
		"Get-VM",
		"-Name", d.MachineName,
		").state"}
	stdout, err := execute(command)
	if err != nil {
		return state.None, fmt.Errorf("Failed to find the VM status")
	}
	resp := parseStdout(stdout)

	if len(resp) < 1 {
		return state.None, nil
	}
	switch resp[0] {
	case "Running":
		return state.Running, nil
	case "Off":
		return state.Stopped, nil
	}
	return state.None, nil
}

func (d *Driver) Create() error {
	err := hypervAvailable()
	if err != nil {
		return err
	}

	d.setMachineNameIfNotSet()

	b2dutils := mcnutils.NewB2dUtils(d.StorePath)
	if err := b2dutils.CopyIsoToMachineDir(d.boot2DockerURL, d.MachineName); err != nil {
		return err
	}

	log.Infof("Creating SSH key...")

	if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
		return err
	}

	log.Infof("Creating VM...")

	virtualSwitch, err := d.chooseVirtualSwitch()
	if err != nil {
		return err
	}

	err = d.generateDiskImage()
	if err != nil {
		return err
	}

	command := []string{
		"New-VM",
		"-Name", d.MachineName,
		"-Path", fmt.Sprintf("'%s'", d.ResolveStorePath(".")),
		"-MemoryStartupBytes", fmt.Sprintf("%dMB", d.MemSize)}
	_, err = execute(command)
	if err != nil {
		return err
	}

	command = []string{
		"Set-VMDvdDrive",
		"-VMName", d.MachineName,
		"-Path", fmt.Sprintf("'%s'", d.ResolveStorePath("boot2docker.iso"))}
	_, err = execute(command)
	if err != nil {
		return err
	}

	command = []string{
		"Add-VMHardDiskDrive",
		"-VMName", d.MachineName,
		"-Path", fmt.Sprintf("'%s'", d.diskImage)}
	_, err = execute(command)
	if err != nil {
		return err
	}

	command = []string{
		"Connect-VMNetworkAdapter",
		"-VMName", d.MachineName,
		"-SwitchName", fmt.Sprintf("'%s'", virtualSwitch)}
	_, err = execute(command)
	if err != nil {
		return err
	}

	log.Infof("Starting  VM...")
	if err := d.Start(); err != nil {
		return err
	}

	return nil
}

func (d *Driver) chooseVirtualSwitch() (string, error) {
	if d.vSwitch != "" {
		return d.vSwitch, nil
	}
	command := []string{
		"@(Get-VMSwitch).Name"}
	stdout, err := execute(command)
	if err != nil {
		return "", err
	}
	switches := parseStdout(stdout)
	if len(switches) > 0 {
		log.Infof("Using switch %s", switches[0])
		return switches[0], nil
	}
	return "", fmt.Errorf("no vswitch found")
}

func (d *Driver) wait() error {
	log.Infof("Waiting for host to start...")
	for {
		ip, _ := d.GetIP()
		if ip != "" {
			break
		}
		time.Sleep(1 * time.Second)
	}
	return nil
}

func (d *Driver) Start() error {
	command := []string{
		"Start-VM",
		"-Name", d.MachineName}
	_, err := execute(command)
	if err != nil {
		return err
	}

	if err := d.wait(); err != nil {
		return err
	}

	d.IPAddress, err = d.GetIP()
	return err
}

func (d *Driver) Stop() error {
	command := []string{
		"Stop-VM",
		"-Name", d.MachineName}
	_, err := execute(command)
	if err != nil {
		return err
	}
	for {
		s, err := d.GetState()
		if err != nil {
			return err
		}
		if s == state.Running {
			time.Sleep(1 * time.Second)
		} else {
			break
		}
	}
	d.IPAddress = ""
	return nil
}

func (d *Driver) Remove() error {
	s, err := d.GetState()
	if err != nil {
		return err
	}
	if s == state.Running {
		if err := d.Kill(); err != nil {
			return err
		}
	}
	command := []string{
		"Remove-VM",
		"-Name", d.MachineName,
		"-Force"}
	_, err = execute(command)
	return err
}

func (d *Driver) Restart() error {
	err := d.Stop()
	if err != nil {
		return err
	}

	return d.Start()
}

func (d *Driver) Kill() error {
	command := []string{
		"Stop-VM",
		"-Name", d.MachineName,
		"-TurnOff"}
	_, err := execute(command)
	if err != nil {
		return err
	}
	for {
		s, err := d.GetState()
		if err != nil {
			return err
		}
		if s == state.Running {
			time.Sleep(1 * time.Second)
		} else {
			break
		}
	}
	d.IPAddress = ""
	return nil
}

func (d *Driver) setMachineNameIfNotSet() {
	if d.MachineName == "" {
		d.MachineName = fmt.Sprintf("docker-machine-unknown")
	}
}

func (d *Driver) GetIP() (string, error) {
	command := []string{
		"((",
		"Get-VM",
		"-Name", d.MachineName,
		").networkadapters[0]).ipaddresses[0]"}
	stdout, err := execute(command)
	if err != nil {
		return "", err
	}
	resp := parseStdout(stdout)
	if len(resp) < 1 {
		return "", fmt.Errorf("IP not found")
	}
	return resp[0], nil
}

func (d *Driver) publicSSHKeyPath() string {
	return d.GetSSHKeyPath() + ".pub"
}

func (d *Driver) generateDiskImage() error {
	// Create a small fixed vhd, put the tar in,
	// convert to dynamic, then resize

	d.diskImage = d.ResolveStorePath("disk.vhd")
	fixed := d.ResolveStorePath("fixed.vhd")
	log.Infof("Creating VHD")
	command := []string{
		"New-VHD",
		"-Path", fmt.Sprintf("'%s'", fixed),
		"-SizeBytes", "10MB",
		"-Fixed"}
	_, err := execute(command)
	if err != nil {
		return err
	}

	tarBuf, err := d.generateTar()
	if err != nil {
		return err
	}

	file, err := os.OpenFile(fixed, os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	defer file.Close()
	file.Seek(0, os.SEEK_SET)
	_, err = file.Write(tarBuf.Bytes())
	if err != nil {
		return err
	}
	file.Close()

	command = []string{
		"Convert-VHD",
		"-Path", fmt.Sprintf("'%s'", fixed),
		"-DestinationPath", fmt.Sprintf("'%s'", d.diskImage),
		"-VHDType", "Dynamic"}
	_, err = execute(command)
	if err != nil {
		return err
	}
	command = []string{
		"Resize-VHD",
		"-Path", fmt.Sprintf("'%s'", d.diskImage),
		"-SizeBytes", fmt.Sprintf("%dMB", d.DiskSize)}
	_, err = execute(command)
	if err != nil {
		return err
	}

	return err
}

// Make a boot2docker VM disk image.
// See https://github.com/boot2docker/boot2docker/blob/master/rootfs/rootfs/etc/rc.d/automount
func (d *Driver) generateTar() (*bytes.Buffer, error) {
	magicString := "boot2docker, please format-me"

	buf := new(bytes.Buffer)
	tw := tar.NewWriter(buf)

	// magicString first so the automount script knows to format the disk
	file := &tar.Header{Name: magicString, Size: int64(len(magicString))}
	if err := tw.WriteHeader(file); err != nil {
		return nil, err
	}
	if _, err := tw.Write([]byte(magicString)); err != nil {
		return nil, err
	}
	// .ssh/key.pub => authorized_keys
	file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700}
	if err := tw.WriteHeader(file); err != nil {
		return nil, err
	}
	pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
	if err != nil {
		return nil, err
	}
	file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644}
	if err := tw.WriteHeader(file); err != nil {
		return nil, err
	}
	if _, err := tw.Write([]byte(pubKey)); err != nil {
		return nil, err
	}
	file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644}
	if err := tw.WriteHeader(file); err != nil {
		return nil, err
	}
	if _, err := tw.Write([]byte(pubKey)); err != nil {
		return nil, err
	}
	if err := tw.Close(); err != nil {
		return nil, err
	}
	return buf, nil
}
<commit_msg>Clarifying  description of hyperv-disk-size<commit_after>package hyperv

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"time"

	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/mcnflag"
	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/docker/machine/libmachine/ssh"
	"github.com/docker/machine/libmachine/state"
)

type Driver struct {
	*drivers.BaseDriver
	boot2DockerURL string
	boot2DockerLoc string
	vSwitch        string
	diskImage      string
	DiskSize       int
	MemSize        int
}

const (
	defaultDiskSize = 20000
	defaultMemory   = 1024
)

func NewDriver(hostName, storePath string) drivers.Driver {
	return &Driver{
		DiskSize: defaultDiskSize,
		MemSize:  defaultMemory,
		BaseDriver: &drivers.BaseDriver{
			MachineName: hostName,
			StorePath:   storePath,
		},
	}
}

// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
	return []mcnflag.Flag{
		mcnflag.StringFlag{
			Name:  "hyperv-boot2docker-url",
			Usage: "Hyper-V URL of the boot2docker image. Defaults to the latest available version.",
		},
		mcnflag.StringFlag{
			Name:  "hyperv-boot2docker-location",
			Usage: "Hyper-V local boot2docker iso. Overrides URL.",
		},
		mcnflag.StringFlag{
			Name:  "hyperv-virtual-switch",
			Usage: "Hyper-V virtual switch name. Defaults to first found.",
		},
		mcnflag.IntFlag{
			Name:  "hyperv-disk-size",
			Usage: "Hyper-V maximum size of dynamically expanding disk in MB.",
			Value: defaultDiskSize,
		},
		mcnflag.IntFlag{
			Name:  "hyperv-memory",
			Usage: "Hyper-V memory size for host in MB.",
			Value: defaultMemory,
		},
	}
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.boot2DockerURL = flags.String("hyperv-boot2docker-url")
	d.boot2DockerLoc = flags.String("hyperv-boot2docker-location")
	d.vSwitch = flags.String("hyperv-virtual-switch")
	d.DiskSize = flags.Int("hyperv-disk-size")
	d.MemSize = flags.Int("hyperv-memory")
	d.SwarmMaster = flags.Bool("swarm-master")
	d.SwarmHost = flags.String("swarm-host")
	d.SwarmDiscovery = flags.String("swarm-discovery")
	d.SSHUser = "docker"
	d.SSHPort = 22
	return nil
}

func (d *Driver) GetSSHHostname() (string, error) {
	return d.GetIP()
}

func (d *Driver) GetSSHUsername() string {
	if d.SSHUser == "" {
		d.SSHUser = "docker"
	}

	return d.SSHUser
}

// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
	return "hyperv"
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}
	if ip == "" {
		return "", nil
	}
	return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, "2376")), nil
}

func (d *Driver) GetState() (state.State, error) {

	command := []string{
		"(",
		"Get-VM",
		"-Name", d.MachineName,
		").state"}
	stdout, err := execute(command)
	if err != nil {
		return state.None, fmt.Errorf("Failed to find the VM status")
	}
	resp := parseStdout(stdout)

	if len(resp) < 1 {
		return state.None, nil
	}
	switch resp[0] {
	case "Running":
		return state.Running, nil
	case "Off":
		return state.Stopped, nil
	}
	return state.None, nil
}

func (d *Driver) Create() error {
	err := hypervAvailable()
	if err != nil {
		return err
	}

	d.setMachineNameIfNotSet()

	b2dutils := mcnutils.NewB2dUtils(d.StorePath)
	if err := b2dutils.CopyIsoToMachineDir(d.boot2DockerURL, d.MachineName); err != nil {
		return err
	}

	log.Infof("Creating SSH key...")

	if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
		return err
	}

	log.Infof("Creating VM...")

	virtualSwitch, err := d.chooseVirtualSwitch()
	if err != nil {
		return err
	}

	err = d.generateDiskImage()
	if err != nil {
		return err
	}

	command := []string{
		"New-VM",
		"-Name", d.MachineName,
		"-Path", fmt.Sprintf("'%s'", d.ResolveStorePath(".")),
		"-MemoryStartupBytes", fmt.Sprintf("%dMB", d.MemSize)}
	_, err = execute(command)
	if err != nil {
		return err
	}

	command = []string{
		"Set-VMDvdDrive",
		"-VMName", d.MachineName,
		"-Path", fmt.Sprintf("'%s'", d.ResolveStorePath("boot2docker.iso"))}
	_, err = execute(command)
	if err != nil {
		return err
	}

	command = []string{
		"Add-VMHardDiskDrive",
		"-VMName", d.MachineName,
		"-Path", fmt.Sprintf("'%s'", d.diskImage)}
	_, err = execute(command)
	if err != nil {
		return err
	}

	command = []string{
		"Connect-VMNetworkAdapter",
		"-VMName", d.MachineName,
		"-SwitchName", fmt.Sprintf("'%s'", virtualSwitch)}
	_, err = execute(command)
	if err != nil {
		return err
	}

	log.Infof("Starting  VM...")
	if err := d.Start(); err != nil {
		return err
	}

	return nil
}

func (d *Driver) chooseVirtualSwitch() (string, error) {
	if d.vSwitch != "" {
		return d.vSwitch, nil
	}
	command := []string{
		"@(Get-VMSwitch).Name"}
	stdout, err := execute(command)
	if err != nil {
		return "", err
	}
	switches := parseStdout(stdout)
	if len(switches) > 0 {
		log.Infof("Using switch %s", switches[0])
		return switches[0], nil
	}
	return "", fmt.Errorf("no vswitch found")
}

func (d *Driver) wait() error {
	log.Infof("Waiting for host to start...")
	for {
		ip, _ := d.GetIP()
		if ip != "" {
			break
		}
		time.Sleep(1 * time.Second)
	}
	return nil
}

func (d *Driver) Start() error {
	command := []string{
		"Start-VM",
		"-Name", d.MachineName}
	_, err := execute(command)
	if err != nil {
		return err
	}

	if err := d.wait(); err != nil {
		return err
	}

	d.IPAddress, err = d.GetIP()
	return err
}

func (d *Driver) Stop() error {
	command := []string{
		"Stop-VM",
		"-Name", d.MachineName}
	_, err := execute(command)
	if err != nil {
		return err
	}
	for {
		s, err := d.GetState()
		if err != nil {
			return err
		}
		if s == state.Running {
			time.Sleep(1 * time.Second)
		} else {
			break
		}
	}
	d.IPAddress = ""
	return nil
}

func (d *Driver) Remove() error {
	s, err := d.GetState()
	if err != nil {
		return err
	}
	if s == state.Running {
		if err := d.Kill(); err != nil {
			return err
		}
	}
	command := []string{
		"Remove-VM",
		"-Name", d.MachineName,
		"-Force"}
	_, err = execute(command)
	return err
}

func (d *Driver) Restart() error {
	err := d.Stop()
	if err != nil {
		return err
	}

	return d.Start()
}

func (d *Driver) Kill() error {
	command := []string{
		"Stop-VM",
		"-Name", d.MachineName,
		"-TurnOff"}
	_, err := execute(command)
	if err != nil {
		return err
	}
	for {
		s, err := d.GetState()
		if err != nil {
			return err
		}
		if s == state.Running {
			time.Sleep(1 * time.Second)
		} else {
			break
		}
	}
	d.IPAddress = ""
	return nil
}

func (d *Driver) setMachineNameIfNotSet() {
	if d.MachineName == "" {
		d.MachineName = fmt.Sprintf("docker-machine-unknown")
	}
}

func (d *Driver) GetIP() (string, error) {
	command := []string{
		"((",
		"Get-VM",
		"-Name", d.MachineName,
		").networkadapters[0]).ipaddresses[0]"}
	stdout, err := execute(command)
	if err != nil {
		return "", err
	}
	resp := parseStdout(stdout)
	if len(resp) < 1 {
		return "", fmt.Errorf("IP not found")
	}
	return resp[0], nil
}

func (d *Driver) publicSSHKeyPath() string {
	return d.GetSSHKeyPath() + ".pub"
}

func (d *Driver) generateDiskImage() error {
	// Create a small fixed vhd, put the tar in,
	// convert to dynamic, then resize

	d.diskImage = d.ResolveStorePath("disk.vhd")
	fixed := d.ResolveStorePath("fixed.vhd")
	log.Infof("Creating VHD")
	command := []string{
		"New-VHD",
		"-Path", fmt.Sprintf("'%s'", fixed),
		"-SizeBytes", "10MB",
		"-Fixed"}
	_, err := execute(command)
	if err != nil {
		return err
	}

	tarBuf, err := d.generateTar()
	if err != nil {
		return err
	}

	file, err := os.OpenFile(fixed, os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	defer file.Close()
	file.Seek(0, os.SEEK_SET)
	_, err = file.Write(tarBuf.Bytes())
	if err != nil {
		return err
	}
	file.Close()

	command = []string{
		"Convert-VHD",
		"-Path", fmt.Sprintf("'%s'", fixed),
		"-DestinationPath", fmt.Sprintf("'%s'", d.diskImage),
		"-VHDType", "Dynamic"}
	_, err = execute(command)
	if err != nil {
		return err
	}
	command = []string{
		"Resize-VHD",
		"-Path", fmt.Sprintf("'%s'", d.diskImage),
		"-SizeBytes", fmt.Sprintf("%dMB", d.DiskSize)}
	_, err = execute(command)
	if err != nil {
		return err
	}

	return err
}

// Make a boot2docker VM disk image.
// See https://github.com/boot2docker/boot2docker/blob/master/rootfs/rootfs/etc/rc.d/automount
func (d *Driver) generateTar() (*bytes.Buffer, error) {
	magicString := "boot2docker, please format-me"

	buf := new(bytes.Buffer)
	tw := tar.NewWriter(buf)

	// magicString first so the automount script knows to format the disk
	file := &tar.Header{Name: magicString, Size: int64(len(magicString))}
	if err := tw.WriteHeader(file); err != nil {
		return nil, err
	}
	if _, err := tw.Write([]byte(magicString)); err != nil {
		return nil, err
	}
	// .ssh/key.pub => authorized_keys
	file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700}
	if err := tw.WriteHeader(file); err != nil {
		return nil, err
	}
	pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
	if err != nil {
		return nil, err
	}
	file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644}
	if err := tw.WriteHeader(file); err != nil {
		return nil, err
	}
	if _, err := tw.Write([]byte(pubKey)); err != nil {
		return nil, err
	}
	file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644}
	if err := tw.WriteHeader(file); err != nil {
		return nil, err
	}
	if _, err := tw.Write([]byte(pubKey)); err != nil {
		return nil, err
	}
	if err := tw.Close(); err != nil {
		return nil, err
	}
	return buf, nil
}
<|endoftext|>


<commit_before>// Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their corresponding JavaScript syntax.
// The types Object and Any are containers for JavaScript objects and are no real citizens of the Go world, e.g. they do not implement interface{} and you cannot type assert them. The type Any may also contain Go values, but it provides no methods for accessing its content. It is only useful for passing it to functions of this package. The relation between interface{} (Go world), Object (JS world) and Any looks like this:
//
//  +------------------------------------+
//  |                                    |
//  |  +-------------+  +-------------+  |
//  |  |             |  |             |  |
//  |  | interface{} |  |  js.Object  |  |
//  |  |             |  |             |  |
//  |  +-------------+  +-------------+  |
//  |                                    |
//  |               js.Any               |
//  |                                    |
//  +------------------------------------+
//
// Type conversions between Go types and JavaScript types are performed automatically according to the following table:
//
//  | Go type                        | Conversions to interface{} | JavaScript type   |
//  | ------------------------------ | -------------------------- | ----------------- |
//  | bool                           | bool                       | Boolean           |
//  | int?, uint?, float?            | float64                    | Number            |
//  | string                         | string                     | String            |
//  | [?]int8                        | []int8                     | Int8Array         |
//  | [?]int16                       | []int16                    | Int16Array        |
//  | [?]int32, [?]int               | []int                      | Int32Array        |
//  | [?]uint8                       | []uint8                    | Uint8Array        |
//  | [?]uint16                      | []uint16                   | Uint16Array       |
//  | [?]uint32, [?]uint, [?]uintptr | []uint                     | Uint32Array       |
//  | [?]float32                     | []float32                  | Float32Array      |
//  | [?]float64                     | []float64                  | Float64Array      |
//  | all other slices and arrays    | []interface{}              | Array             |
//  | functions                      | func(...js.Any) js.Object  | Function          |
//  | time.Time                      | time.Time                  | Date              |
//  | -                              | *js.DOMNode                | instanceof Node   |
//  | maps, structs                  | map[string]interface{}     | instanceof Object |
//
package js

// Object is a container for a native JavaScript object. Calls to its methods are treated specially by GopherJS and translated directly to their JavaScript syntax. Nil is equal to JavaScript's "null".
// Object can not be used as a map key and it can only be converted to Any, but no other interface type, including interface{}. This means that you can't pass an Object obj to fmt.Println(obj), but using the println(obj) built-in is possible.
type Object interface {

	// Get returns the object's property with the given key.
	Get(key string) Object

	// Set assigns the value to the object's property with the given key.
	Set(key string, value Any)

	// Delete removes the object's property with the given key.
	Delete(key string)

	// Get returns the object's "length" property, converted to int.
	Length() int

	// Index returns the i'th element of an array.
	Index(i int) Object

	// SetIndex sets the i'th element of an array.
	SetIndex(i int, value Any)

	// Call calls the object's method with the given name.
	Call(name string, args ...Any) Object

	// Invoke calls the object itself. This will fail if it is not a function.
	Invoke(args ...Any) Object

	// New creates a new instance of this type object. This will fail if it not a function (constructor).
	New(args ...Any) Object

	// Bool returns the object converted to bool according to JavaScript type conversions.
	Bool() bool

	// String returns the object converted to string according to JavaScript type conversions.
	String() string

	// Int returns the object converted to int according to JavaScript type conversions (parseInt).
	Int() int

	// Int64 returns the object converted to int64 according to JavaScript type conversions (parseInt).
	Int64() int64

	// Uint64 returns the object converted to uint64 according to JavaScript type conversions (parseInt).
	Uint64() uint64

	// Float returns the object converted to float64 according to JavaScript type conversions (parseFloat).
	Float() float64

	// Interface returns the object converted to interface{}. See GopherJS' README for details.
	Interface() interface{}

	// Unsafe returns the object as an uintptr, which can be converted via unsafe.Pointer. Not intended for public use.
	Unsafe() uintptr
}

// Any may contain a JavaScript object or some Go value. Any can not be used as a map key and it can not be converted to some other interface type, including interface{}.
type Any interface{}

// DOMNode is used for encapsulating DOM nodes in a proper Go value. It is not feasible to convert a DOM node into a map[string]interface{}.
type DOMNode struct {
	Object
}

// Error encapsulates JavaScript errors. Those are turned into a Go panic and may be rescued, giving an *Error that holds the JavaScript error object.
type Error struct {
	Object
}

// Error returns the message of the encapsulated JavaScript error object.
func (err *Error) Error() string {
	return "JavaScript error: " + err.Get("message").String()
}

// Stack returns the stack property of the encapsulated JavaScript error object.
func (err *Error) Stack() string {
	return err.Get("stack").String()
}

// Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js). Set this to a mock for testing with pure Go.
var Global Object

// This gives the value of JavaScript's "this" keyword. It can be used when passing Go functions to JavaScript as callbacks. Set this to a mock for testing with pure Go.
var This Object

// Arguments gives the value of JavaScript's "arguments" keyword. It can be used when passing Go functions to JavaScript as callbacks. Set this to a mock for testing with pure Go.
var Arguments []Object

// Module gives the value of the "module" variable set by Node.js. Hint: Set a module export with 'js.Module.Get("exports").Set("exportName", ...)'.
var Module Object

// Undefined gives the JavaScript value "undefined".
var Undefined Object

// Debugger gets compiled to JavaScript's "debugger;" statement.
func Debugger() {}

// InternalObject returns the internal JavaScript object that represents i. Not intended for public use.
func InternalObject(i interface{}) Object {
	return nil
}

// Keys returns the keys of the given JavaScript object.
func Keys(o Object) []string {
	if o == Undefined || o == nil {
		return nil
	}
	a := Global.Get("Object").Call("keys", o)
	s := make([]string, a.Length())
	for i := 0; i < a.Length(); i++ {
		s[i] = a.Index(i).String()
	}
	return s
}

// MakeWrapper creates a JavaScript object which has wrappers for the exported methods of i. This can be used to provide getter and setter methods for Go fields to JavaScript.
func MakeWrapper(i interface{}) Object {
	v := InternalObject(i)
	o := Global.Get("Object").New()
	methods := v.Get("constructor").Get("methods")
	for i := 0; i < methods.Length(); i++ {
		m := methods.Index(i)
		if m.Index(2).String() != "" { // not exported
			continue
		}
		o.Set(m.Index(1).String(), func(args ...Any) Object {
			return v.Call(m.Index(0).String(), args...)
		})
	}
	return o
}

// M is a simple map type. It is intended as a shorthand for JavaScript objects (before conversion).
type M map[string]Any

// S is a simple slice type. It is intended as a shorthand for JavaScript arrays (before conversion).
type S []Any

func init() {
	// avoid dead code elimination
	e := Error{}
	n := DOMNode{}
	_, _ = e, n
}
<commit_msg>documentation for (un)boxing<commit_after>// Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their corresponding JavaScript syntax.
// The types Object and Any are containers for JavaScript objects and are no real citizens of the Go world, e.g. they do not implement interface{} and you cannot type assert them. The type Any may also contain Go values, but it provides no methods for accessing its content. It is only useful for passing it to functions of this package. The relation between interface{} (Go world), Object (JS world) and Any looks like this:
//
//  +------------------------------------+
//  |                                    |
//  |  +-------------+  +-------------+  |
//  |  |             |  |             |  |
//  |  | interface{} |  |  js.Object  |  |
//  |  |             |  |             |  |
//  |  +-------------+  +-------------+  |
//  |                                    |
//  |               js.Any               |
//  |                                    |
//  +------------------------------------+
//
// Type conversions between Go types and JavaScript types are performed automatically according to the following table:
//
//  | Go type                        | Conversions to interface{} | JavaScript type   |
//  | ------------------------------ | -------------------------- | ----------------- |
//  | bool                           | bool                       | Boolean           |
//  | int?, uint?, float?            | float64                    | Number            |
//  | string                         | string                     | String            |
//  | [?]int8                        | []int8                     | Int8Array         |
//  | [?]int16                       | []int16                    | Int16Array        |
//  | [?]int32, [?]int               | []int                      | Int32Array        |
//  | [?]uint8                       | []uint8                    | Uint8Array        |
//  | [?]uint16                      | []uint16                   | Uint16Array       |
//  | [?]uint32, [?]uint, [?]uintptr | []uint                     | Uint32Array       |
//  | [?]float32                     | []float32                  | Float32Array      |
//  | [?]float64                     | []float64                  | Float64Array      |
//  | all other slices and arrays    | []interface{}              | Array             |
//  | functions                      | func(...js.Any) js.Object  | Function          |
//  | time.Time                      | time.Time                  | Date              |
//  | -                              | *js.DOMNode                | instanceof Node   |
//  | maps, structs                  | map[string]interface{}     | instanceof Object |
//
// Additionally, for a struct containing a js.Object field, only the content of the field will be passed to JavaScript and vice versa.
package js

// Object is a container for a native JavaScript object. Calls to its methods are treated specially by GopherJS and translated directly to their JavaScript syntax. Nil is equal to JavaScript's "null".
// Object can not be used as a map key and it can only be converted to Any, but no other interface type, including interface{}. This means that you can't pass an Object obj to fmt.Println(obj), but using the println(obj) built-in is possible.
type Object interface {

	// Get returns the object's property with the given key.
	Get(key string) Object

	// Set assigns the value to the object's property with the given key.
	Set(key string, value Any)

	// Delete removes the object's property with the given key.
	Delete(key string)

	// Get returns the object's "length" property, converted to int.
	Length() int

	// Index returns the i'th element of an array.
	Index(i int) Object

	// SetIndex sets the i'th element of an array.
	SetIndex(i int, value Any)

	// Call calls the object's method with the given name.
	Call(name string, args ...Any) Object

	// Invoke calls the object itself. This will fail if it is not a function.
	Invoke(args ...Any) Object

	// New creates a new instance of this type object. This will fail if it not a function (constructor).
	New(args ...Any) Object

	// Bool returns the object converted to bool according to JavaScript type conversions.
	Bool() bool

	// String returns the object converted to string according to JavaScript type conversions.
	String() string

	// Int returns the object converted to int according to JavaScript type conversions (parseInt).
	Int() int

	// Int64 returns the object converted to int64 according to JavaScript type conversions (parseInt).
	Int64() int64

	// Uint64 returns the object converted to uint64 according to JavaScript type conversions (parseInt).
	Uint64() uint64

	// Float returns the object converted to float64 according to JavaScript type conversions (parseFloat).
	Float() float64

	// Interface returns the object converted to interface{}. See GopherJS' README for details.
	Interface() interface{}

	// Unsafe returns the object as an uintptr, which can be converted via unsafe.Pointer. Not intended for public use.
	Unsafe() uintptr
}

// Any may contain a JavaScript object or some Go value. Any can not be used as a map key and it can not be converted to some other interface type, including interface{}.
type Any interface{}

// DOMNode is used for encapsulating DOM nodes in a proper Go value. It is not feasible to convert a DOM node into a map[string]interface{}.
type DOMNode struct {
	Object
}

// Error encapsulates JavaScript errors. Those are turned into a Go panic and may be rescued, giving an *Error that holds the JavaScript error object.
type Error struct {
	Object
}

// Error returns the message of the encapsulated JavaScript error object.
func (err *Error) Error() string {
	return "JavaScript error: " + err.Get("message").String()
}

// Stack returns the stack property of the encapsulated JavaScript error object.
func (err *Error) Stack() string {
	return err.Get("stack").String()
}

// Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js). Set this to a mock for testing with pure Go.
var Global Object

// This gives the value of JavaScript's "this" keyword. It can be used when passing Go functions to JavaScript as callbacks. Set this to a mock for testing with pure Go.
var This Object

// Arguments gives the value of JavaScript's "arguments" keyword. It can be used when passing Go functions to JavaScript as callbacks. Set this to a mock for testing with pure Go.
var Arguments []Object

// Module gives the value of the "module" variable set by Node.js. Hint: Set a module export with 'js.Module.Get("exports").Set("exportName", ...)'.
var Module Object

// Undefined gives the JavaScript value "undefined".
var Undefined Object

// Debugger gets compiled to JavaScript's "debugger;" statement.
func Debugger() {}

// InternalObject returns the internal JavaScript object that represents i. Not intended for public use.
func InternalObject(i interface{}) Object {
	return nil
}

// Keys returns the keys of the given JavaScript object.
func Keys(o Object) []string {
	if o == Undefined || o == nil {
		return nil
	}
	a := Global.Get("Object").Call("keys", o)
	s := make([]string, a.Length())
	for i := 0; i < a.Length(); i++ {
		s[i] = a.Index(i).String()
	}
	return s
}

// MakeWrapper creates a JavaScript object which has wrappers for the exported methods of i. This can be used to provide getter and setter methods for Go fields to JavaScript.
func MakeWrapper(i interface{}) Object {
	v := InternalObject(i)
	o := Global.Get("Object").New()
	methods := v.Get("constructor").Get("methods")
	for i := 0; i < methods.Length(); i++ {
		m := methods.Index(i)
		if m.Index(2).String() != "" { // not exported
			continue
		}
		o.Set(m.Index(1).String(), func(args ...Any) Object {
			return v.Call(m.Index(0).String(), args...)
		})
	}
	return o
}

// M is a simple map type. It is intended as a shorthand for JavaScript objects (before conversion).
type M map[string]Any

// S is a simple slice type. It is intended as a shorthand for JavaScript arrays (before conversion).
type S []Any

func init() {
	// avoid dead code elimination
	e := Error{}
	n := DOMNode{}
	_, _ = e, n
}
<|endoftext|>


<commit_before>package imgio

import (
	"errors"
	"image/draw"
	"io"
	"sync"
)

type Image struct {
	img draw.Image
	gen PointsSequenceGenerator
	prw PointReadWriter
	mux sync.RWMutex

	pointCursor uint64
	byteCursor  int
}

func NewImage(img draw.Image, gen PointsSequenceGenerator, prw PointReadWriter) *Image {
	return &Image{
		img: img,
		gen: gen,
		prw: prw,
	}
}

// Read implements io.Reader interface
func (i *Image) Read(p []byte) (n int, err error) {
	i.mux.RLock()
	defer i.mux.RUnlock()

	i.gen.Seek(i.pointCursor)

	if !i.gen.Valid() {
		return 0, io.EOF
	}
	if len(p) == 0 {
		return 0, nil
	}

	for {
		if !i.gen.Valid() {
			return n, io.EOF
		}
		if n >= len(p) {
			return
		}

		point := i.gen.Current()
		color := i.img.At(point.X, point.Y)
		buff, nBytesRead := i.prw.Read(i.byteCursor, color, point)

		if len(p)-n > nBytesRead {
			copy(p[n:], buff[:nBytesRead])
			n += nBytesRead
			i.gen.Next()
			i.pointCursor++
			i.byteCursor = 0
		} else {
			end := nBytesRead - len(p) + n
			copy(p[n:], buff[:end])
			n += end
			i.byteCursor = end + 1
			return
		}
	}

	return
}

// Write implements io.Writer interface
func (i *Image) Write(p []byte) (n int, err error) {
	if len(p) == 0 {
		return 0, nil
	}

	i.mux.Lock()
	defer i.mux.Unlock()

	i.gen.Seek(i.pointCursor)

	for {
		if !i.gen.Valid() {
			return n, errors.New("overflow")
		}
		if len(p) == 0 {
			return n, nil
		}

		point := i.gen.Current()
		srcColor := i.img.At(point.X, point.Y)
		color, writtenBytes := i.prw.Write(p, i.byteCursor, srcColor, point)
		i.img.Set(point.X, point.Y, color)
		i.gen.Next()
		i.pointCursor++
		n += writtenBytes
		p = p[writtenBytes:]
		if i.prw.Size(point) > int64(writtenBytes) {
			i.byteCursor = writtenBytes + 1
		}
	}

	return
}

func (i *Image) Seek(offset int64, whence int) (int64, error) {
	return 0, nil
}

func (i *Image) Size() (size int64) {
	i.gen.Rewind()
	for i.gen.Valid() {
		point := i.gen.Current()
		size += i.prw.Size(point)
		i.gen.Next()
	}
	return
}
<commit_msg>Fix read image<commit_after>package imgio

import (
	"errors"
	"image/draw"
	"io"
	"sync"
)

type Image struct {
	img draw.Image
	gen PointsSequenceGenerator
	prw PointReadWriter
	mux sync.RWMutex

	pointCursor uint64
	byteCursor  int
}

func NewImage(img draw.Image, gen PointsSequenceGenerator, prw PointReadWriter) *Image {
	return &Image{
		img: img,
		gen: gen,
		prw: prw,
	}
}

// Read implements io.Reader interface
func (i *Image) Read(p []byte) (n int, err error) {
	i.mux.RLock()
	defer i.mux.RUnlock()

	i.gen.Seek(i.pointCursor)

	if !i.gen.Valid() {
		return 0, io.EOF
	}
	if len(p) == 0 {
		return 0, nil
	}

	for {
		if !i.gen.Valid() {
			return n, io.EOF
		}
		if n >= len(p) {
			return
		}

		point := i.gen.Current()
		color := i.img.At(point.X, point.Y)
		buff, nBytesRead := i.prw.Read(i.byteCursor, color, point)

		if len(p)-n >= nBytesRead {
			copy(p[n:], buff[:nBytesRead])
			n += nBytesRead
			i.gen.Next()
			i.pointCursor++
			i.byteCursor = 0
		} else {
			end := nBytesRead - len(p) + n
			copy(p[n:], buff[:end])
			n += end
			i.byteCursor = end + 1
			return
		}
	}

	return
}

// Write implements io.Writer interface
func (i *Image) Write(p []byte) (n int, err error) {
	if len(p) == 0 {
		return 0, nil
	}

	i.mux.Lock()
	defer i.mux.Unlock()

	i.gen.Seek(i.pointCursor)

	for {
		if !i.gen.Valid() {
			return n, errors.New("overflow")
		}
		if len(p) == 0 {
			return n, nil
		}

		point := i.gen.Current()
		srcColor := i.img.At(point.X, point.Y)
		color, writtenBytes := i.prw.Write(p, i.byteCursor, srcColor, point)
		i.img.Set(point.X, point.Y, color)
		i.gen.Next()
		i.pointCursor++
		n += writtenBytes
		p = p[writtenBytes:]
		if i.prw.Size(point) > int64(writtenBytes) {
			i.byteCursor = writtenBytes + 1
		}
	}

	return
}

func (i *Image) Seek(offset int64, whence int) (int64, error) {
	return 0, nil
}

func (i *Image) Size() (size int64) {
	i.gen.Rewind()
	for i.gen.Valid() {
		point := i.gen.Current()
		size += i.prw.Size(point)
		i.gen.Next()
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"errors"
	"github.com/hajimehoshi/ebiten/internal"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"github.com/hajimehoshi/ebiten/internal/ui"
	"image"
	"image/color"
)

// Image represents an image.
// The pixel format is alpha-premultiplied.
// Image implements image.Image.
type Image struct {
	framebuffer *graphics.Framebuffer
	texture     *graphics.Texture
	pixels      []uint8
}

// Size returns the size of the image.
func (i *Image) Size() (width, height int) {
	return i.framebuffer.Size()
}

// Clear resets the pixels of the image into 0.
func (i *Image) Clear() (err error) {
	return i.Fill(color.Transparent)
}

// Fill fills the image with a solid color.
func (i *Image) Fill(clr color.Color) (err error) {
	i.pixels = nil
	r, g, b, a := internal.RGBA(clr)
	ui.Use(func(c *opengl.Context) {
		// TODO: Change to pass color.Color
		err = i.framebuffer.Fill(c, r, g, b, a)
	})
	return
}

// DrawImage draws the given image on the receiver image.
//
// This method accepts the options.
// The parts of the given image at the parts of the destination.
// After determining parts to draw, this applies the geometry matrix and the color matrix.
//
// Here are the default values:
//     ImageParts: (0, 0) - (source width, source height) to (0, 0) - (source width, source height)
//                 (i.e. the whole source image)
//     GeoM:       Identity matrix
//     ColorM:     Identity matrix (that changes no colors)
//
// Be careful that this method is potentially slow.
// It would be better if you could call this method fewer times.
func (i *Image) DrawImage(image *Image, options *DrawImageOptions) (err error) {
	if i == image {
		return errors.New("Image.DrawImage: image should be different from the receiver")
	}
	i.pixels = nil
	if options == nil {
		options = &DrawImageOptions{}
	}
	parts := options.ImageParts
	if parts == nil {
		// Check options.Parts for backward-compatibility.
		dparts := options.Parts
		if dparts != nil {
			parts = imageParts(dparts)
		} else {
			w, h := image.Size()
			parts = &wholeImage{w, h}
		}
	}
	w, h := image.Size()
	quads := &textureQuads{parts: parts, width: w, height: h}
	ui.Use(func(c *opengl.Context) {
		err = i.framebuffer.DrawTexture(c, image.texture, quads, &options.GeoM, &options.ColorM)
	})
	return
}

// DrawLine draws a line.
func (i *Image) DrawLine(x0, y0, x1, y1 int, clr color.Color) error {
	return i.DrawLines(&line{x0, y0, x1, y1, clr})
}

// DrawLines draws lines.
func (i *Image) DrawLines(lines Lines) (err error) {
	ui.Use(func(c *opengl.Context) {
		err = i.framebuffer.DrawLines(c, lines)
	})
	return
}

// DrawRect draws a rectangle.
func (i *Image) FillRect(x, y, width, height int, clr color.Color) error {
	return i.FillRects(&rect{x, y, width, height, clr})
}

// DrawRects draws rectangles on the image.
func (i *Image) FillRects(rects Rects) (err error) {
	ui.Use(func(c *opengl.Context) {
		err = i.framebuffer.FillRects(c, rects)
	})
	return
}

// Bounds returns the bounds of the image.
func (i *Image) Bounds() image.Rectangle {
	w, h := i.Size()
	return image.Rect(0, 0, w, h)
}

// ColorModel returns the color model of the image.
func (i *Image) ColorModel() color.Model {
	return color.RGBAModel
}

// At returns the color of the image at (x, y).
//
// This method loads pixels from VRAM to system memory if necessary.
func (i *Image) At(x, y int) color.Color {
	if i.pixels == nil {
		ui.Use(func(c *opengl.Context) {
			var err error
			i.pixels, err = i.texture.Pixels(c)
			if err != nil {
				panic(err)
			}
		})
	}
	w, _ := i.Size()
	w = internal.NextPowerOf2Int(w)
	idx := 4*x + 4*y*w
	r, g, b, a := i.pixels[idx], i.pixels[idx+1], i.pixels[idx+2], i.pixels[idx+3]
	return color.RGBA{r, g, b, a}
}

// A DrawImageOptions represents options to render an image on an image.
type DrawImageOptions struct {
	ImageParts ImageParts
	GeoM       GeoM
	ColorM     ColorM

	// Deprecated (as of 1.1.0-alpha): Use ImageParts instead.
	Parts []ImagePart
}
<commit_msg>Bug fix: comment<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"errors"
	"github.com/hajimehoshi/ebiten/internal"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"github.com/hajimehoshi/ebiten/internal/ui"
	"image"
	"image/color"
)

// Image represents an image.
// The pixel format is alpha-premultiplied.
// Image implements image.Image.
type Image struct {
	framebuffer *graphics.Framebuffer
	texture     *graphics.Texture
	pixels      []uint8
}

// Size returns the size of the image.
func (i *Image) Size() (width, height int) {
	return i.framebuffer.Size()
}

// Clear resets the pixels of the image into 0.
func (i *Image) Clear() (err error) {
	return i.Fill(color.Transparent)
}

// Fill fills the image with a solid color.
func (i *Image) Fill(clr color.Color) (err error) {
	i.pixels = nil
	r, g, b, a := internal.RGBA(clr)
	ui.Use(func(c *opengl.Context) {
		// TODO: Change to pass color.Color
		err = i.framebuffer.Fill(c, r, g, b, a)
	})
	return
}

// DrawImage draws the given image on the receiver image.
//
// This method accepts the options.
// The parts of the given image at the parts of the destination.
// After determining parts to draw, this applies the geometry matrix and the color matrix.
//
// Here are the default values:
//     ImageParts: (0, 0) - (source width, source height) to (0, 0) - (source width, source height)
//                 (i.e. the whole source image)
//     GeoM:       Identity matrix
//     ColorM:     Identity matrix (that changes no colors)
//
// Be careful that this method is potentially slow.
// It would be better if you could call this method fewer times.
func (i *Image) DrawImage(image *Image, options *DrawImageOptions) (err error) {
	if i == image {
		return errors.New("Image.DrawImage: image should be different from the receiver")
	}
	i.pixels = nil
	if options == nil {
		options = &DrawImageOptions{}
	}
	parts := options.ImageParts
	if parts == nil {
		// Check options.Parts for backward-compatibility.
		dparts := options.Parts
		if dparts != nil {
			parts = imageParts(dparts)
		} else {
			w, h := image.Size()
			parts = &wholeImage{w, h}
		}
	}
	w, h := image.Size()
	quads := &textureQuads{parts: parts, width: w, height: h}
	ui.Use(func(c *opengl.Context) {
		err = i.framebuffer.DrawTexture(c, image.texture, quads, &options.GeoM, &options.ColorM)
	})
	return
}

// DrawLine draws a line.
func (i *Image) DrawLine(x0, y0, x1, y1 int, clr color.Color) error {
	return i.DrawLines(&line{x0, y0, x1, y1, clr})
}

// DrawLines draws lines.
func (i *Image) DrawLines(lines Lines) (err error) {
	ui.Use(func(c *opengl.Context) {
		err = i.framebuffer.DrawLines(c, lines)
	})
	return
}

// FillRect draws a filled rectangle.
func (i *Image) FillRect(x, y, width, height int, clr color.Color) error {
	return i.FillRects(&rect{x, y, width, height, clr})
}

// FillRects draws filled rectangles on the image.
func (i *Image) FillRects(rects Rects) (err error) {
	ui.Use(func(c *opengl.Context) {
		err = i.framebuffer.FillRects(c, rects)
	})
	return
}

// Bounds returns the bounds of the image.
func (i *Image) Bounds() image.Rectangle {
	w, h := i.Size()
	return image.Rect(0, 0, w, h)
}

// ColorModel returns the color model of the image.
func (i *Image) ColorModel() color.Model {
	return color.RGBAModel
}

// At returns the color of the image at (x, y).
//
// This method loads pixels from VRAM to system memory if necessary.
func (i *Image) At(x, y int) color.Color {
	if i.pixels == nil {
		ui.Use(func(c *opengl.Context) {
			var err error
			i.pixels, err = i.texture.Pixels(c)
			if err != nil {
				panic(err)
			}
		})
	}
	w, _ := i.Size()
	w = internal.NextPowerOf2Int(w)
	idx := 4*x + 4*y*w
	r, g, b, a := i.pixels[idx], i.pixels[idx+1], i.pixels[idx+2], i.pixels[idx+3]
	return color.RGBA{r, g, b, a}
}

// A DrawImageOptions represents options to render an image on an image.
type DrawImageOptions struct {
	ImageParts ImageParts
	GeoM       GeoM
	ColorM     ColorM

	// Deprecated (as of 1.1.0-alpha): Use ImageParts instead.
	Parts []ImagePart
}
<|endoftext|>


<commit_before>// Copyright 2014 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// image.go contains checks against image data.

package ht

import (
	"fmt"

	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"

	"github.com/vdobler/ht/fingerprint"
)

func init() {
	RegisterCheck(Image{})
}

// ----------------------------------------------------------------------------
// Image

// Image checks image format, size and fingerprint. As usual a zero value of
// a field skipps the check of that property.
// Image fingerprinting is done via github.com/vdobler/ht/fingerprint.
// Only one of BMV or ColorHist should be used as there is just one threshold.
//
// Note that you have to register the apropriate image decoder functions
// with package image, e.g. by
//     import _ "image/png"
// if you want to check PNG images.
type Image struct {
	// Format is the format of the image as registered in package image.
	Format string `json:",omitempty"`

	// If > 0 check width or height of image.
	Width, Height int `json:",omitempty"`

	// BMV is the 16 hex digit long Block Mean Value hash of the image.
	BMV string `json:",omitempty"`

	// ColorHist is the 24 hex digit long Color Histogram hash of
	// the image.
	ColorHist string `json:",omitempty"`

	// Threshold is the limit up to which the received image may differ
	// from the given BMV or ColorHist fingerprint.
	Threshold float64 `json:",omitempty"`
}

// Okay implements the Check interface.
func (c Image) Execute(t *Test) error {
	img, format, err := image.Decode(t.Response.Body())
	if err != nil {
		fmt.Printf("Image.Okay resp.BodyReader=%#v\n", t.Response.Body())
		return CantCheck{err}
	}
	// TODO: Do not abort on first failure.
	if c.Format != "" && format != c.Format {
		return fmt.Errorf("Got %s image, want %s", format, c.Format)
	}

	bounds := img.Bounds()
	if c.Width > 0 && c.Width != bounds.Dx() {
		return fmt.Errorf("Got %d px wide image, want %d",
			bounds.Dx(), c.Width)

	}
	if c.Height > 0 && c.Height != bounds.Dy() {
		return fmt.Errorf("Got %d px heigh image, want %d",
			bounds.Dy(), c.Height)

	}

	if c.BMV != "" {
		targetBMV, err := fingerprint.BMVHashFromString(c.BMV)
		if err != nil {
			return CantCheck{fmt.Errorf("bad BMV hash: %s", err)}
		}
		imgBMV := fingerprint.NewBMVHash(img)
		if d := fingerprint.BMVDelta(targetBMV, imgBMV); d > c.Threshold {
			return fmt.Errorf("Got BMV of %s, want %s (delta=%.4f)",
				imgBMV.String(), targetBMV.String(), d)
		}

	}
	if c.ColorHist != "" {
		targetCH, err := fingerprint.ColorHistFromString(c.ColorHist)
		if err != nil {
			return CantCheck{fmt.Errorf("bad ColorHist hash: %s", err)}
		}
		imgCH := fingerprint.NewColorHist(img)
		if d := fingerprint.ColorHistDelta(targetCH, imgCH); d > c.Threshold {
			return fmt.Errorf("Got ColorHist of %s, want %s (delta=%.4f)",
				imgCH.String(), targetCH.String(), d)
		}
	}

	return nil
}

func (_ Image) Prepare() error { return nil }
<commit_msg>Remove wrong docstring.<commit_after>// Copyright 2014 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// image.go contains checks against image data.

package ht

import (
	"fmt"

	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"

	"github.com/vdobler/ht/fingerprint"
)

func init() {
	RegisterCheck(Image{})
}

// ----------------------------------------------------------------------------
// Image

// Image checks image format, size and fingerprint. As usual a zero value of
// a field skipps the check of that property.
// Image fingerprinting is done via github.com/vdobler/ht/fingerprint.
// Only one of BMV or ColorHist should be used as there is just one threshold.
//
// Note that you have to register the apropriate image decoder functions
// with package image, e.g. by
//     import _ "image/png"
// if you want to check PNG images.
type Image struct {
	// Format is the format of the image as registered in package image.
	Format string `json:",omitempty"`

	// If > 0 check width or height of image.
	Width, Height int `json:",omitempty"`

	// BMV is the 16 hex digit long Block Mean Value hash of the image.
	BMV string `json:",omitempty"`

	// ColorHist is the 24 hex digit long Color Histogram hash of
	// the image.
	ColorHist string `json:",omitempty"`

	// Threshold is the limit up to which the received image may differ
	// from the given BMV or ColorHist fingerprint.
	Threshold float64 `json:",omitempty"`
}

func (c Image) Execute(t *Test) error {
	img, format, err := image.Decode(t.Response.Body())
	if err != nil {
		fmt.Printf("Image.Okay resp.BodyReader=%#v\n", t.Response.Body())
		return CantCheck{err}
	}
	// TODO: Do not abort on first failure.
	if c.Format != "" && format != c.Format {
		return fmt.Errorf("Got %s image, want %s", format, c.Format)
	}

	bounds := img.Bounds()
	if c.Width > 0 && c.Width != bounds.Dx() {
		return fmt.Errorf("Got %d px wide image, want %d",
			bounds.Dx(), c.Width)

	}
	if c.Height > 0 && c.Height != bounds.Dy() {
		return fmt.Errorf("Got %d px heigh image, want %d",
			bounds.Dy(), c.Height)

	}

	if c.BMV != "" {
		targetBMV, err := fingerprint.BMVHashFromString(c.BMV)
		if err != nil {
			return CantCheck{fmt.Errorf("bad BMV hash: %s", err)}
		}
		imgBMV := fingerprint.NewBMVHash(img)
		if d := fingerprint.BMVDelta(targetBMV, imgBMV); d > c.Threshold {
			return fmt.Errorf("Got BMV of %s, want %s (delta=%.4f)",
				imgBMV.String(), targetBMV.String(), d)
		}

	}
	if c.ColorHist != "" {
		targetCH, err := fingerprint.ColorHistFromString(c.ColorHist)
		if err != nil {
			return CantCheck{fmt.Errorf("bad ColorHist hash: %s", err)}
		}
		imgCH := fingerprint.NewColorHist(img)
		if d := fingerprint.ColorHistDelta(targetCH, imgCH); d > c.Threshold {
			return fmt.Errorf("Got ColorHist of %s, want %s (delta=%.4f)",
				imgCH.String(), targetCH.String(), d)
		}
	}

	return nil
}

func (_ Image) Prepare() error { return nil }
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"text/template"
)

// Check for errors
func check(err error) {
	if err != nil {
		panic(err)
	}
}

// Page Templates
var (
	uploadTemplate, _ = template.ParseFiles("upload.html")
	errorTemplate, _  = template.ParseFiles("error.html")
)

// MIME Validator
func validateimage(h *multipart.FileHeader) (ext string, err error) {
	mimeArray := h.Header["Content-Type"]
	mime := mimeArray[0]
	if mime == "image/jpeg" {
		ext = ".jpg"
	} else if mime == "image/png" {
		ext = ".png"
	} else {
		err = errors.New("Unsupported filetype uploaded")
	}
	return
}

// Upload Handler
func upload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		// Load the upload page if they aren't posting an image
		uploadTemplate.Execute(w, nil)
		return
	}

	// Get image from POST
	f, h, err := r.FormFile("image")
	check(err)
	defer f.Close()

	// Check MIME and get a file extension
	_, err = validateimage(h)
	check(err)

	// Read and write the uploaded file to disk
	filename := "./img/" + h.Filename
	filebytes, err := ioutil.ReadAll(f)
	check(err)
	err = ioutil.WriteFile(filename, filebytes, 0744)
	check(err)

	// Redirect to the view page
	http.Redirect(w, r, "/view?id="+filename[6:], http.StatusFound)
}

// View Handler
func view(w http.ResponseWriter, r *http.Request) {
	var filename string
	filename = "./img/" + r.FormValue("id")

	if filename[len(filename)-4:] == ".jpg" {
		w.Header().Set("Content-Type", "image/jpeg")
	} else if filename[len(filename)-4:] == ".png" {
		w.Header().Set("Content-Type", "image/png")
	}

	http.ServeFile(w, r, filename)
}

// Error page's variables
type ErrorPage struct {
	Error error
}

// One clean error page
func errorHandler(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if e, ok := recover().(error); ok {
				contents := ErrorPage{Error: e}
				w.WriteHeader(http.StatusInternalServerError)
				errorTemplate.Execute(w, contents)
			}
		}()
		fn(w, r)
	}
}

func main() {
	http.HandleFunc("/", errorHandler(upload))
	http.HandleFunc("/view", errorHandler(view))
	http.ListenAndServe(":3000", nil)
}
<commit_msg>error checking for templates<commit_after>package main

import (
	"errors"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"text/template"
)

// Check for errors
func check(err error) {
	if err != nil {
		panic(err)
	}
}

// Page Templates
var (
	uploadTemplate *template.Template
	errorTemplate  *template.Template
)

// MIME Validator
func validateimage(h *multipart.FileHeader) (ext string, err error) {
	mimeArray := h.Header["Content-Type"]
	mime := mimeArray[0]
	if mime == "image/jpeg" {
		ext = ".jpg"
	} else if mime == "image/png" {
		ext = ".png"
	} else {
		err = errors.New("Unsupported filetype uploaded")
	}
	return
}

// Upload Handler
func upload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		// Load the upload page if they aren't posting an image
		uploadTemplate.Execute(w, nil)
		return
	}

	// Get image from POST
	f, h, err := r.FormFile("image")
	check(err)
	defer f.Close()

	// Check MIME and get a file extension
	_, err = validateimage(h)
	check(err)

	// Read and write the uploaded file to disk
	filename := "./img/" + h.Filename
	filebytes, err := ioutil.ReadAll(f)
	check(err)
	err = ioutil.WriteFile(filename, filebytes, 0744)
	check(err)

	// Redirect to the view page
	http.Redirect(w, r, "/view?id="+filename[6:], http.StatusFound)
}

// View Handler
func view(w http.ResponseWriter, r *http.Request) {
	var filename string
	filename = "./img/" + r.FormValue("id")

	if filename[len(filename)-4:] == ".jpg" {
		w.Header().Set("Content-Type", "image/jpeg")
	} else if filename[len(filename)-4:] == ".png" {
		w.Header().Set("Content-Type", "image/png")
	}

	http.ServeFile(w, r, filename)
}

// Error page's variables
type ErrorPage struct {
	Error error
}

// One clean error page
func errorHandler(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if e, ok := recover().(error); ok {
				contents := ErrorPage{Error: e}
				w.WriteHeader(http.StatusInternalServerError)
				errorTemplate.Execute(w, contents)
			}
		}()
		fn(w, r)
	}
}

func main() {
	var err error

	// Load up templates and check for errors
	uploadTemplate, err = template.ParseFiles("upload.html")
	check(err)
	errorTemplate, err = template.ParseFiles("error.html")
	check(err)

	http.HandleFunc("/", errorHandler(upload))
	http.HandleFunc("/view", errorHandler(view))
	http.ListenAndServe(":3000", nil)
}
<|endoftext|>


<commit_before>package goinsta

import (
	"encoding/json"
	"fmt"
)

// InboxItem is any conversation message.
type InboxItem struct {
	ID            string `json:"item_id"`
	UserID        int64  `json:"user_id"`
	Timestamp     int64  `json:"timestamp"`
	ClientContext string `json:"client_context"`

	// Type there are a few types:
	// text, like, raven_media
	Type string `json:"item_type"`

	// Text is message text.
	Text string `json:"text"`

	// InboxItemLike is the heart that your girlfriend send to you.
	// (or in my case: the heart that my fans sends to me hehe)

	Like string `json:"like"`

	// Media is image or video
	Media struct {
		ID                   string `json:"id"`
		Images               Images `json:"image_versions2"`
		OriginalWidth        int    `json:"original_width"`
		OriginalHeight       int    `json:"original_height"`
		MediaType            int    `json:"media_type"`
		MediaID              int64  `json:"media_id"`
		PlaybackDurationSecs int    `json:"playback_duration_secs"`
		URLExpireAtSecs      int    `json:"url_expire_at_secs"`
		OrganicTrackingToken string `json:"organic_tracking_token"`
	}
}

// Inbox is the direct message inbox.
//
// Inbox contains Conversations. Each conversation has InboxItems.
// InboxItems are the message of the chat.
type Inbox struct {
	inst *Instagram

	Conversations []Conversation `json:"threads"`

	HasNewer            bool  `json:"has_newer"` // TODO
	HasOlder            bool  `json:"has_older"`
	UnseenCount         int   `json:"unseen_count"`
	UnseenCountTs       int64 `json:"unseen_count_ts"`
	BlendedInboxEnabled bool  `json:"blended_inbox_enabled"`
	// this fields are copied from response
	SeqID                int   `json:"seq_id"`
	PendingRequestsTotal int   `json:"pending_requests_total"`
	SnapshotAtMs         int64 `json:"snapshot_at_ms"`
}

type inboxResp struct {
	Inbox                Inbox  `json:"inbox"`
	SeqID                int    `json:"seq_id"`
	PendingRequestsTotal int    `json:"pending_requests_total"`
	SnapshotAtMs         int64  `json:"snapshot_at_ms"`
	Status               string `json:"status"`
}

func newInbox(inst *Instagram) *Inbox {
	return &Inbox{inst: inst}
}

// Sync updates inbox messages.
//
// See example: examples/inbox/sync.go
func (inbox *Inbox) Sync() error {
	// TODO: Next for pagination
	insta := inbox.inst
	body, err := insta.sendRequest(
		&reqOptions{
			Endpoint: urlInbox,
			Query: map[string]string{
				"persistentBadging": "true",
				"use_unified_inbox": "true",
			},
		},
	)
	if err == nil {
		resp := inboxResp{}
		err = json.Unmarshal(body, &resp)
		if err == nil {
			*inbox = resp.Inbox
			inbox.inst = insta
			inbox.SeqID = resp.Inbox.SeqID
			inbox.PendingRequestsTotal = resp.Inbox.PendingRequestsTotal
			inbox.SnapshotAtMs = resp.Inbox.SnapshotAtMs
			for i := range inbox.Conversations {
				inbox.Conversations[i].inst = insta
				inbox.Conversations[i].firstRun = true
			}
		}
	}
	return err
}

type Conversation struct {
	inst     *Instagram
	err      error
	firstRun bool

	ID   string `json:"thread_id"`
	V2ID int64  `json:"thread_v2_id"`
	// Items can be of many types.
	Items                     []InboxItem `json:"items"`
	Title                     string      `json:"thread_title"`
	Users                     []User      `json:"users"`
	LeftUsers                 []User      `json:"left_users"`
	Pending                   bool        `json:"pending"`
	PendingScore              int64       `json:"pending_score"`
	ReshareReceiveCount       int         `json:"reshare_receive_count"`
	ReshareSendCount          int         `json:"reshare_send_count"`
	ViewerID                  int64       `json:"viewer_id"`
	ValuedRequest             bool        `json:"valued_request"`
	LastActivityAt            int64       `json:"last_activity_at"`
	Muted                     bool        `json:"muted"`
	IsPin                     bool        `json:"is_pin"`
	Named                     bool        `json:"named"`
	ThreadType                string      `json:"thread_type"`
	ExpiringMediaSendCount    int         `json:"expiring_media_send_count"`
	ExpiringMediaReceiveCount int         `json:"expiring_media_receive_count"`
	Inviter                   User        `json:"inviter"`
	HasOlder                  bool        `json:"has_older"`
	HasNewer                  bool        `json:"has_newer"`
	LastSeenAt                struct {
		Num7629421016 struct {
			Timestamp string `json:"timestamp"`
			ItemID    string `json:"item_id"`
		} `json:"7629421016"`
	} `json:"last_seen_at"`
	NewestCursor      string `json:"newest_cursor"`
	OldestCursor      string `json:"oldest_cursor"`
	IsSpam            bool   `json:"is_spam"`
	LastPermanentItem Item   `json:"last_permanent_item"`
}

func (c Conversation) Error() error {
	return c.err
}

func (c Conversation) lastItemID() string {
	n := len(c.Items)
	if n == 0 {
		return ""
	}
	return c.Items[n-1].ID
}

// Send sends message in conversation
func (c *Conversation) Send(text string) error {
	insta := c.inst
	data, err := insta.prepareData(
		map[string]interface{}{
			"recipient_users": c.Inviter.ID,
			"action":          "send_item",
			"text":            text,
		},
	)
	body, err := insta.sendRequest(
		&reqOptions{
			Endpoint: urlInboxSend,
			Query:    generateSignature(data),
			IsPost:   true,
		},
	)
	if err == nil {
		// TODO
		_ = body
	}
	return err
}

// Write is like Send but being compatible with io.Writer.
func (c *Conversation) Write(b []byte) (int, error) {
	n := len(b)
	return n, c.Send(b2s(b))
}

// Next loads next set of private messages.
//
// See example: examples/inbox/conversation.go
func (c *Conversation) Next() bool {
	if c.err != nil {
		return false
	}
	if c.firstRun {
		c.firstRun = false
		return true
	}

	insta := c.inst
	body, err := insta.sendRequest(
		&reqOptions{
			Endpoint: fmt.Sprintf(urlInboxThread, c.ID),
			Query: map[string]string{
				"cursor":            c.lastItemID(),
				"direction":         "older", // go to upper
				"use_unified_inbox": "true",
			},
		},
	)
	if err == nil {
		resp := threadResp{}
		err = json.Unmarshal(body, &resp)
		if err == nil {
			*c = resp.Conversation
			c.inst = insta
			if !c.HasOlder {
				c.err = ErrNoMore
			}
			return true
		}
	}
	c.err = err
	return false
}
<commit_msg>Added pagination for Inbox object<commit_after>package goinsta

import (
	"encoding/json"
	"fmt"
)

// InboxItem is any conversation message.
type InboxItem struct {
	ID            string `json:"item_id"`
	UserID        int64  `json:"user_id"`
	Timestamp     int64  `json:"timestamp"`
	ClientContext string `json:"client_context"`

	// Type there are a few types:
	// text, like, raven_media
	Type string `json:"item_type"`

	// Text is message text.
	Text string `json:"text"`

	// InboxItemLike is the heart that your girlfriend send to you.
	// (or in my case: the heart that my fans sends to me hehe)

	Like string `json:"like"`

	// Media is image or video
	Media struct {
		ID                   string `json:"id"`
		Images               Images `json:"image_versions2"`
		OriginalWidth        int    `json:"original_width"`
		OriginalHeight       int    `json:"original_height"`
		MediaType            int    `json:"media_type"`
		MediaID              int64  `json:"media_id"`
		PlaybackDurationSecs int    `json:"playback_duration_secs"`
		URLExpireAtSecs      int    `json:"url_expire_at_secs"`
		OrganicTrackingToken string `json:"organic_tracking_token"`
	}
}

// Inbox is the direct message inbox.
//
// Inbox contains Conversations. Each conversation has InboxItems.
// InboxItems are the message of the chat.
type Inbox struct {
	inst *Instagram
	err  error

	Conversations []Conversation `json:"threads"`

	HasNewer            bool   `json:"has_newer"` // TODO
	HasOlder            bool   `json:"has_older"`
	Cursor              string `json:"oldest_cursor"`
	UnseenCount         int    `json:"unseen_count"`
	UnseenCountTs       int64  `json:"unseen_count_ts"`
	BlendedInboxEnabled bool   `json:"blended_inbox_enabled"`
	// this fields are copied from response
	SeqID                int   `json:"seq_id"`
	PendingRequestsTotal int   `json:"pending_requests_total"`
	SnapshotAtMs         int64 `json:"snapshot_at_ms"`
}

type inboxResp struct {
	Inbox                Inbox  `json:"inbox"`
	SeqID                int    `json:"seq_id"`
	PendingRequestsTotal int    `json:"pending_requests_total"`
	SnapshotAtMs         int64  `json:"snapshot_at_ms"`
	Status               string `json:"status"`
}

func newInbox(inst *Instagram) *Inbox {
	return &Inbox{inst: inst}
}

// Sync updates inbox messages.
//
// See example: examples/inbox/sync.go
func (inbox *Inbox) Sync() error {
	insta := inbox.inst
	body, err := insta.sendRequest(
		&reqOptions{
			Endpoint: urlInbox,
			Query: map[string]string{
				"persistentBadging": "true",
				"use_unified_inbox": "true",
				"cursor":            inbox.Cursor,
			},
		},
	)
	if err == nil {
		resp := inboxResp{}
		err = json.Unmarshal(body, &resp)
		if err == nil {
			*inbox = resp.Inbox
			inbox.inst = insta
			inbox.SeqID = resp.Inbox.SeqID
			inbox.PendingRequestsTotal = resp.Inbox.PendingRequestsTotal
			inbox.SnapshotAtMs = resp.Inbox.SnapshotAtMs
			for i := range inbox.Conversations {
				inbox.Conversations[i].inst = insta
				inbox.Conversations[i].firstRun = true
			}
		}
	}
	return err
}

// Reset sets inbox cursor at the beginning.
func (inbox *Inbox) Reset() {
	inbox.Cursor = ""
}

// Next allows pagination over messages.
//
// See example: examples/inbox/next.go
func (inbox *Inbox) Next() bool {
	if inbox.err != nil {
		return false
	}
	insta := inbox.inst
	body, err := insta.sendRequest(
		&reqOptions{
			Endpoint: urlInbox,
			Query: map[string]string{
				"persistentBadging": "true",
				"use_unified_inbox": "true",
				"cursor":            inbox.Cursor,
			},
		},
	)
	if err == nil {
		resp := inboxResp{}
		err = json.Unmarshal(body, &resp)
		if err == nil {
			*inbox = resp.Inbox
			inbox.inst = insta
			inbox.SeqID = resp.Inbox.SeqID
			inbox.PendingRequestsTotal = resp.Inbox.PendingRequestsTotal
			inbox.SnapshotAtMs = resp.Inbox.SnapshotAtMs
			for i := range inbox.Conversations {
				inbox.Conversations[i].inst = insta
				inbox.Conversations[i].firstRun = true
			}
			if inbox.Cursor == "" || !inbox.HasOlder {
				inbox.err = ErrNoMore
			}
			return true
		}
	}
	inbox.err = err
	return false
}

type Conversation struct {
	inst     *Instagram
	err      error
	firstRun bool

	ID   string `json:"thread_id"`
	V2ID int64  `json:"thread_v2_id"`
	// Items can be of many types.
	Items                     []InboxItem `json:"items"`
	Title                     string      `json:"thread_title"`
	Users                     []User      `json:"users"`
	LeftUsers                 []User      `json:"left_users"`
	Pending                   bool        `json:"pending"`
	PendingScore              int64       `json:"pending_score"`
	ReshareReceiveCount       int         `json:"reshare_receive_count"`
	ReshareSendCount          int         `json:"reshare_send_count"`
	ViewerID                  int64       `json:"viewer_id"`
	ValuedRequest             bool        `json:"valued_request"`
	LastActivityAt            int64       `json:"last_activity_at"`
	Muted                     bool        `json:"muted"`
	IsPin                     bool        `json:"is_pin"`
	Named                     bool        `json:"named"`
	ThreadType                string      `json:"thread_type"`
	ExpiringMediaSendCount    int         `json:"expiring_media_send_count"`
	ExpiringMediaReceiveCount int         `json:"expiring_media_receive_count"`
	Inviter                   User        `json:"inviter"`
	HasOlder                  bool        `json:"has_older"`
	HasNewer                  bool        `json:"has_newer"`
	LastSeenAt                struct {
		Num7629421016 struct {
			Timestamp string `json:"timestamp"`
			ItemID    string `json:"item_id"`
		} `json:"7629421016"`
	} `json:"last_seen_at"`
	NewestCursor      string `json:"newest_cursor"`
	OldestCursor      string `json:"oldest_cursor"`
	IsSpam            bool   `json:"is_spam"`
	LastPermanentItem Item   `json:"last_permanent_item"`
}

func (c Conversation) Error() error {
	return c.err
}

func (c Conversation) lastItemID() string {
	n := len(c.Items)
	if n == 0 {
		return ""
	}
	return c.Items[n-1].ID
}

// Send sends message in conversation
func (c *Conversation) Send(text string) error {
	insta := c.inst
	data, err := insta.prepareData(
		map[string]interface{}{
			"recipient_users": c.Inviter.ID,
			"action":          "send_item",
			"text":            text,
		},
	)
	body, err := insta.sendRequest(
		&reqOptions{
			Endpoint: urlInboxSend,
			Query:    generateSignature(data),
			IsPost:   true,
		},
	)
	if err == nil {
		// TODO
		_ = body
	}
	return err
}

// Write is like Send but being compatible with io.Writer.
func (c *Conversation) Write(b []byte) (int, error) {
	n := len(b)
	return n, c.Send(b2s(b))
}

// Next loads next set of private messages.
//
// See example: examples/inbox/conversation.go
func (c *Conversation) Next() bool {
	if c.err != nil {
		return false
	}
	if c.firstRun {
		c.firstRun = false
		return true
	}

	insta := c.inst
	body, err := insta.sendRequest(
		&reqOptions{
			Endpoint: fmt.Sprintf(urlInboxThread, c.ID),
			Query: map[string]string{
				"cursor":            c.lastItemID(),
				"direction":         "older", // go to upper
				"use_unified_inbox": "true",
			},
		},
	)
	if err == nil {
		resp := threadResp{}
		err = json.Unmarshal(body, &resp)
		if err == nil {
			*c = resp.Conversation
			c.inst = insta
			if !c.HasOlder {
				c.err = ErrNoMore
			}
			return true
		}
	}
	c.err = err
	return false
}
<|endoftext|>


<commit_before>package kazoo

import (
	"encoding/json"
	"errors"
	"fmt"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/samuel/go-zookeeper/zk"
)

var (
	FailedToClaimPartition = errors.New("Failed to claim partition for this consumer instance. Do you have a rogue consumer running?")
)

// ParseConnectionString parses a zookeeper connection string in the form of
// host1:2181,host2:2181/chroot and returns the list of servers, and the chroot.
func ParseConnectionString(zookeeper string) (nodes []string, chroot string) {
	nodesAndChroot := strings.SplitN(zookeeper, "/", 2)
	if len(nodesAndChroot) == 2 {
		chroot = fmt.Sprintf("/%s", nodesAndChroot[1])
	}
	nodes = strings.Split(nodesAndChroot[0], ",")
	return
}

// BuildConnectionString builds a Zookeeper connection string for a list of nodes.
// Returns a string like "zk1:2181,zk2:2181,zk3:2181"
func BuildConnectionString(nodes []string) string {
	return strings.Join(nodes, ",")
}

// ConnectionStringWithChroot builds a Zookeeper connection string for a list
// of nodes and a chroot. The chroot should start with "/".
// Returns a string like "zk1:2181,zk2:2181,zk3:2181/chroot"
func BuildConnectionStringWithChroot(nodes []string, chroot string) string {
	return fmt.Sprintf("%s%s", strings.Join(nodes, ","), chroot)
}

// Kazoo interacts with the Kafka metadata in Zookeeper
type Kazoo struct {
	conn *zk.Conn
	conf *Config
}

// Config holds configuration values f.
type Config struct {
	// The chroot the Kafka installation is registerde under. Defaults to "".
	Chroot string

	// The amount of time the Zookeeper client can be disconnected from the Zookeeper cluster
	// before the cluster will get rid of watches and ephemeral nodes. Defaults to 1 second.
	Timeout time.Duration
}

// NewConfig instantiates a new Config struct with sane defaults.
func NewConfig() *Config {
	return &Config{Timeout: 1 * time.Second}
}

// NewKazoo creates a new connection instance
func NewKazoo(servers []string, conf *Config) (*Kazoo, error) {
	if conf == nil {
		conf = NewConfig()
	}

	conn, _, err := zk.Connect(servers, conf.Timeout)
	if err != nil {
		return nil, err
	}
	return &Kazoo{conn, conf}, nil
}

// NewKazooFromConnectionString creates a new connection instance
// based on a zookeeer connection string that can include a chroot.
func NewKazooFromConnectionString(connectionString string, conf *Config) (*Kazoo, error) {
	if conf == nil {
		conf = NewConfig()
	}

	nodes, chroot := ParseConnectionString(connectionString)
	conf.Chroot = chroot
	return NewKazoo(nodes, conf)
}

// Brokers returns a map of all the brokers that make part of the
// Kafka cluster that is regeistered in Zookeeper.
func (kz *Kazoo) Brokers() (map[int32]string, error) {
	root := fmt.Sprintf("%s/brokers/ids", kz.conf.Chroot)
	children, _, err := kz.conn.Children(root)
	if err != nil {
		return nil, err
	}

	type brokerEntry struct {
		Host string `json:"host"`
		Port int    `json:"port"`
	}

	result := make(map[int32]string)
	for _, child := range children {
		brokerID, err := strconv.ParseInt(child, 10, 32)
		if err != nil {
			return nil, err
		}

		value, _, err := kz.conn.Get(path.Join(root, child))
		if err != nil {
			return nil, err
		}

		var brokerNode brokerEntry
		if err := json.Unmarshal(value, &brokerNode); err != nil {
			return nil, err
		}

		result[int32(brokerID)] = fmt.Sprintf("%s:%d", brokerNode.Host, brokerNode.Port)
	}

	return result, nil
}

// BrokerList returns a slice of broker addresses that can be used to connect to
// the Kafka cluster, e.g. using `sarama.NewAsyncProducer()`.
func (kz *Kazoo) BrokerList() ([]string, error) {
	brokers, err := kz.Brokers()
	if err != nil {
		return nil, err
	}

	result := make([]string, 0, len(brokers))
	for _, broker := range brokers {
		result = append(result, broker)
	}

	return result, nil
}

// Controller returns what broker is currently acting as controller of the Kafka cluster
func (kz *Kazoo) Controller() (int32, error) {
	type controllerEntry struct {
		BrokerID int32 `json:"brokerid"`
	}

	node := fmt.Sprintf("%s/controller", kz.conf.Chroot)
	data, _, err := kz.conn.Get(node)
	if err != nil {
		return -1, err
	}

	var controllerNode controllerEntry
	if err := json.Unmarshal(data, &controllerNode); err != nil {
		return -1, err
	}

	return controllerNode.BrokerID, nil
}

// Close closes the connection with the Zookeeper cluster
func (kz *Kazoo) Close() error {
	kz.conn.Close()
	return nil
}

////////////////////////////////////////////////////////////////////////
// Util methods
////////////////////////////////////////////////////////////////////////

// Exists checks existence of a node
func (kz *Kazoo) exists(node string) (ok bool, err error) {
	ok, _, err = kz.conn.Exists(node)
	return
}

// DeleteAll deletes a node recursively
func (kz *Kazoo) deleteRecursive(node string) (err error) {
	children, stat, err := kz.conn.Children(node)
	if err == zk.ErrNoNode {
		return nil
	} else if err != nil {
		return
	}

	for _, child := range children {
		if err = kz.deleteRecursive(path.Join(node, child)); err != nil {
			return
		}
	}

	return kz.conn.Delete(node, stat.Version)
}

// MkdirAll creates a directory recursively
func (kz *Kazoo) mkdirRecursive(node string) (err error) {
	parent := path.Dir(node)
	if parent != "/" {
		if err = kz.mkdirRecursive(parent); err != nil {
			return
		}
	}

	_, err = kz.conn.Create(node, nil, 0, zk.WorldACL(zk.PermAll))
	if err == zk.ErrNodeExists {
		err = nil
	}
	return
}

// Create stores a new value at node. Fails if already set.
func (kz *Kazoo) create(node string, value []byte, ephemeral bool) (err error) {
	if err = kz.mkdirRecursive(path.Dir(node)); err != nil {
		return
	}

	flags := int32(0)
	if ephemeral {
		flags = zk.FlagEphemeral
	}
	_, err = kz.conn.Create(node, value, flags, zk.WorldACL(zk.PermAll))
	return
}
<commit_msg>Typo fix<commit_after>package kazoo

import (
	"encoding/json"
	"errors"
	"fmt"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/samuel/go-zookeeper/zk"
)

var (
	FailedToClaimPartition = errors.New("Failed to claim partition for this consumer instance. Do you have a rogue consumer running?")
)

// ParseConnectionString parses a zookeeper connection string in the form of
// host1:2181,host2:2181/chroot and returns the list of servers, and the chroot.
func ParseConnectionString(zookeeper string) (nodes []string, chroot string) {
	nodesAndChroot := strings.SplitN(zookeeper, "/", 2)
	if len(nodesAndChroot) == 2 {
		chroot = fmt.Sprintf("/%s", nodesAndChroot[1])
	}
	nodes = strings.Split(nodesAndChroot[0], ",")
	return
}

// BuildConnectionString builds a Zookeeper connection string for a list of nodes.
// Returns a string like "zk1:2181,zk2:2181,zk3:2181"
func BuildConnectionString(nodes []string) string {
	return strings.Join(nodes, ",")
}

// ConnectionStringWithChroot builds a Zookeeper connection string for a list
// of nodes and a chroot. The chroot should start with "/".
// Returns a string like "zk1:2181,zk2:2181,zk3:2181/chroot"
func BuildConnectionStringWithChroot(nodes []string, chroot string) string {
	return fmt.Sprintf("%s%s", strings.Join(nodes, ","), chroot)
}

// Kazoo interacts with the Kafka metadata in Zookeeper
type Kazoo struct {
	conn *zk.Conn
	conf *Config
}

// Config holds configuration values f.
type Config struct {
	// The chroot the Kafka installation is registerde under. Defaults to "".
	Chroot string

	// The amount of time the Zookeeper client can be disconnected from the Zookeeper cluster
	// before the cluster will get rid of watches and ephemeral nodes. Defaults to 1 second.
	Timeout time.Duration
}

// NewConfig instantiates a new Config struct with sane defaults.
func NewConfig() *Config {
	return &Config{Timeout: 1 * time.Second}
}

// NewKazoo creates a new connection instance
func NewKazoo(servers []string, conf *Config) (*Kazoo, error) {
	if conf == nil {
		conf = NewConfig()
	}

	conn, _, err := zk.Connect(servers, conf.Timeout)
	if err != nil {
		return nil, err
	}
	return &Kazoo{conn, conf}, nil
}

// NewKazooFromConnectionString creates a new connection instance
// based on a zookeeer connection string that can include a chroot.
func NewKazooFromConnectionString(connectionString string, conf *Config) (*Kazoo, error) {
	if conf == nil {
		conf = NewConfig()
	}

	nodes, chroot := ParseConnectionString(connectionString)
	conf.Chroot = chroot
	return NewKazoo(nodes, conf)
}

// Brokers returns a map of all the brokers that make part of the
// Kafka cluster that is registered in Zookeeper.
func (kz *Kazoo) Brokers() (map[int32]string, error) {
	root := fmt.Sprintf("%s/brokers/ids", kz.conf.Chroot)
	children, _, err := kz.conn.Children(root)
	if err != nil {
		return nil, err
	}

	type brokerEntry struct {
		Host string `json:"host"`
		Port int    `json:"port"`
	}

	result := make(map[int32]string)
	for _, child := range children {
		brokerID, err := strconv.ParseInt(child, 10, 32)
		if err != nil {
			return nil, err
		}

		value, _, err := kz.conn.Get(path.Join(root, child))
		if err != nil {
			return nil, err
		}

		var brokerNode brokerEntry
		if err := json.Unmarshal(value, &brokerNode); err != nil {
			return nil, err
		}

		result[int32(brokerID)] = fmt.Sprintf("%s:%d", brokerNode.Host, brokerNode.Port)
	}

	return result, nil
}

// BrokerList returns a slice of broker addresses that can be used to connect to
// the Kafka cluster, e.g. using `sarama.NewAsyncProducer()`.
func (kz *Kazoo) BrokerList() ([]string, error) {
	brokers, err := kz.Brokers()
	if err != nil {
		return nil, err
	}

	result := make([]string, 0, len(brokers))
	for _, broker := range brokers {
		result = append(result, broker)
	}

	return result, nil
}

// Controller returns what broker is currently acting as controller of the Kafka cluster
func (kz *Kazoo) Controller() (int32, error) {
	type controllerEntry struct {
		BrokerID int32 `json:"brokerid"`
	}

	node := fmt.Sprintf("%s/controller", kz.conf.Chroot)
	data, _, err := kz.conn.Get(node)
	if err != nil {
		return -1, err
	}

	var controllerNode controllerEntry
	if err := json.Unmarshal(data, &controllerNode); err != nil {
		return -1, err
	}

	return controllerNode.BrokerID, nil
}

// Close closes the connection with the Zookeeper cluster
func (kz *Kazoo) Close() error {
	kz.conn.Close()
	return nil
}

////////////////////////////////////////////////////////////////////////
// Util methods
////////////////////////////////////////////////////////////////////////

// Exists checks existence of a node
func (kz *Kazoo) exists(node string) (ok bool, err error) {
	ok, _, err = kz.conn.Exists(node)
	return
}

// DeleteAll deletes a node recursively
func (kz *Kazoo) deleteRecursive(node string) (err error) {
	children, stat, err := kz.conn.Children(node)
	if err == zk.ErrNoNode {
		return nil
	} else if err != nil {
		return
	}

	for _, child := range children {
		if err = kz.deleteRecursive(path.Join(node, child)); err != nil {
			return
		}
	}

	return kz.conn.Delete(node, stat.Version)
}

// MkdirAll creates a directory recursively
func (kz *Kazoo) mkdirRecursive(node string) (err error) {
	parent := path.Dir(node)
	if parent != "/" {
		if err = kz.mkdirRecursive(parent); err != nil {
			return
		}
	}

	_, err = kz.conn.Create(node, nil, 0, zk.WorldACL(zk.PermAll))
	if err == zk.ErrNodeExists {
		err = nil
	}
	return
}

// Create stores a new value at node. Fails if already set.
func (kz *Kazoo) create(node string, value []byte, ephemeral bool) (err error) {
	if err = kz.mkdirRecursive(path.Dir(node)); err != nil {
		return
	}

	flags := int32(0)
	if ephemeral {
		flags = zk.FlagEphemeral
	}
	_, err = kz.conn.Create(node, value, flags, zk.WorldACL(zk.PermAll))
	return
}
<|endoftext|>


<commit_before>// This is Free Software covered by the terms of the MIT license.
// See LICENSE file for details.
// Copyright 2017 by Intevation GmbH
package main

import (
	"bufio"
	"fmt"
	"io"
	"strconv"
	"strings"
	"unicode"
)

const (
	latexHeader = `\documentclass[]{article}
\usepackage{lmodern}
\usepackage{amssymb,amsmath}
\usepackage{ifxetex,ifluatex}
\usepackage{cancel}
\usepackage{fixltx2e} % provides \textsubscript
\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
  \usepackage[T1]{fontenc}
  \usepackage[utf8]{inputenc}
\else % if luatex or xelatex
  \ifxetex
    \usepackage{mathspec}
  \else
    \usepackage{fontspec}
  \fi
  \defaultfontfeatures{Ligatures=TeX,Scale=MatchLowercase}
\fi
% use upquote if available, for straight quotes in verbatim environments
\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
% use microtype if available
\IfFileExists{microtype.sty}{%
\usepackage[]{microtype}
\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
}{}
\PassOptionsToPackage{hyphens}{url} % url is loaded by hyperref
\usepackage[unicode=true]{hyperref}
\hypersetup{
            pdfborder={0 0 0},
            breaklinks=true}
\urlstyle{same}  % don't use monospace font for urls
\IfFileExists{parskip.sty}{%
\usepackage{parskip}
}{% else
\setlength{\parindent}{0pt}
\setlength{\parskip}{6pt plus 2pt minus 1pt}
}
\setlength{\emergencystretch}{3em}  % prevent overfull lines
\providecommand{\tightlist}{%
  \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
\setcounter{secnumdepth}{0}
% Redefines (sub)paragraphs to behave more like sections
\ifx\paragraph\undefined\else
\let\oldparagraph\paragraph
\renewcommand{\paragraph}[1]{\oldparagraph{#1}\mbox{}}
\fi
\ifx\subparagraph\undefined\else
\let\oldsubparagraph\subparagraph
\renewcommand{\subparagraph}[1]{\oldsubparagraph{#1}\mbox{}}
\fi

% set default figure placement to htbp
\makeatletter
\def\fps@figure{htbp}
\makeatother

\date{}

\begin{document}
`
	latexFooter = `
\end{document}
`
)

// latexReplacer substitutes special LaTex characters with quoted ones
// which can be used in templates.
var latexReplacer = strings.NewReplacer(
	"\n", `\\`,
	`\`, `\textbackslash`,
	`$`, `\$`,
	`_`, `\_`,
	`%`, `\%`,
	`&`, `\&`,
	`#`, `\#`,
	`^`, `\url{^}`,
	`~`, `\url{~}`,
	`"`, `\url{"}`,
	`}`, `\}`,
	`{`, `\{`,
	`]`, `{]}`,
	`[`, `{[}`)

type latex struct {
	out    *bufio.Writer
	labels map[string]struct{}
}

func (l *latex) writeString(txt string) error {
	_, err := l.out.WriteString(txt)
	return err
}

func (l *latex) generateLabel(name string) string {
	name = strings.ToLower(strings.TrimSpace(name))
	name = strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return '-'
		}
		return r
	}, name)
	if _, found := l.labels[name]; !found {
		l.labels[name] = struct{}{}
		return name
	}
	for i := 1; ; i++ {
		n := name + "-" + strconv.Itoa(i)
		if _, found := l.labels[n]; !found {
			l.labels[n] = struct{}{}
			return n
		}
	}
}

func (l *latex) str(txt string) func(*node) error {
	return func(n *node) error { return l.writeString(txt) }
}

func (l *latex) command(cmd string) *visitor {
	return &visitor{l.str(`\` + cmd + `{`), l.str(`}`)}
}

func (l *latex) text(n *node) error {
	return l.writeString(latexReplacer.Replace(n.value.(string)))
}

func (l *latex) link() *visitor {
	return &visitor{
		enter: func(n *node) error {
			l.writeString(`\href{`)
			l.writeString(latexReplacer.Replace(n.value.(string)))
			return l.writeString(`}{`)
		},
		leave: l.str(`}`),
	}
}

func (l *latex) image(n *node) error {
	img := n.value.(*image)
	l.writeString(`\begin{figure}` + "\n")
	l.writeString(`\centering` + "\n")
	l.writeString(`\includegraphics{`)
	l.writeString(latexReplacer.Replace(img.src))
	l.writeString(`}` + "\n" + `\caption{`)
	l.writeString(latexReplacer.Replace(img.alt))
	return l.writeString(`}` + "\n" + `\end{figure}` + "\n")
}

func (l *latex) header(typ string) func(*node) error {
	return func(n *node) error {
		title := n.value.(string)
		_, err := fmt.Fprintf(l.out, "\n\n\\%s{%s}\\label{%s}\n\n",
			typ,
			latexReplacer.Replace(title),
			l.generateLabel(title))
		return err
	}
}

func exportLaTex(doc *document, out io.Writer, standalone bool) error {

	l := latex{
		out:    bufio.NewWriter(out),
		labels: make(map[string]struct{}),
	}

	if standalone {
		l.writeString(latexHeader)
	}

	// TODO: Implement document traversal.
	err := doc.traverse(map[nodeType]*visitor{
		// TODO: orderedListNode
		// TODO: unorderedListNode
		// TODO: listItemNode
		textNode:        &visitor{enter: l.text},
		boldNode:        l.command("textbf"),
		italicsNode:     l.command("textit"),
		underlinedNode:  l.command("underline"),
		strikeNode:      l.command("cancel"),
		superscriptNode: l.command("textsuperscript"),
		subscriptNode:   l.command("textsubscript"),
		// TODO: tableNode
		// TODO: tableRowNode
		// TODO: tableCellNode
		// TODO: tableHeaderRowNode
		// TODO: tableHeaderCellNode
		heading1Node:  &visitor{enter: l.header("section")},
		heading2Node:  &visitor{enter: l.header("subsection")},
		heading3Node:  &visitor{enter: l.header("subsubsection")},
		heading4Node:  &visitor{enter: l.header("paragraph")},
		heading5Node:  &visitor{enter: l.header("subparagraph")},
		heading6Node:  &visitor{enter: l.header("subparagraph")},
		paragraphNode: &visitor{l.str("\n"), l.str("\n")},
		lineBreakNode: &visitor{enter: l.str(`\\` + "\n")},
		// TODO: escapeNode
		// TODO: noWikiNode
		noWikiInlineNode: l.command("texttt"),
		imageNode:        &visitor{enter: l.image},
		linkNode:         l.link(),
		horizontalLineNode: &visitor{
			enter: l.str(
				"\n" + `\begin{center}\rule{0.5\linewidth}{\linethickness}\end{center}` + "\n")},
	})

	if err != nil {
		return err
	}

	if standalone {
		l.writeString(latexFooter)
	}

	return l.out.Flush()
}
<commit_msg>Quote generated label in LaTex exporter, too.<commit_after>// This is Free Software covered by the terms of the MIT license.
// See LICENSE file for details.
// Copyright 2017 by Intevation GmbH
package main

import (
	"bufio"
	"fmt"
	"io"
	"strconv"
	"strings"
	"unicode"
)

const (
	latexHeader = `\documentclass[]{article}
\usepackage{lmodern}
\usepackage{amssymb,amsmath}
\usepackage{ifxetex,ifluatex}
\usepackage{cancel}
\usepackage{fixltx2e} % provides \textsubscript
\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
  \usepackage[T1]{fontenc}
  \usepackage[utf8]{inputenc}
\else % if luatex or xelatex
  \ifxetex
    \usepackage{mathspec}
  \else
    \usepackage{fontspec}
  \fi
  \defaultfontfeatures{Ligatures=TeX,Scale=MatchLowercase}
\fi
% use upquote if available, for straight quotes in verbatim environments
\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
% use microtype if available
\IfFileExists{microtype.sty}{%
\usepackage[]{microtype}
\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
}{}
\PassOptionsToPackage{hyphens}{url} % url is loaded by hyperref
\usepackage[unicode=true]{hyperref}
\hypersetup{
            pdfborder={0 0 0},
            breaklinks=true}
\urlstyle{same}  % don't use monospace font for urls
\IfFileExists{parskip.sty}{%
\usepackage{parskip}
}{% else
\setlength{\parindent}{0pt}
\setlength{\parskip}{6pt plus 2pt minus 1pt}
}
\setlength{\emergencystretch}{3em}  % prevent overfull lines
\providecommand{\tightlist}{%
  \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
\setcounter{secnumdepth}{0}
% Redefines (sub)paragraphs to behave more like sections
\ifx\paragraph\undefined\else
\let\oldparagraph\paragraph
\renewcommand{\paragraph}[1]{\oldparagraph{#1}\mbox{}}
\fi
\ifx\subparagraph\undefined\else
\let\oldsubparagraph\subparagraph
\renewcommand{\subparagraph}[1]{\oldsubparagraph{#1}\mbox{}}
\fi

% set default figure placement to htbp
\makeatletter
\def\fps@figure{htbp}
\makeatother

\date{}

\begin{document}
`
	latexFooter = `
\end{document}
`
)

// latexReplacer substitutes special LaTex characters with quoted ones
// which can be used in templates.
var latexReplacer = strings.NewReplacer(
	"\n", `\\`,
	`\`, `\textbackslash`,
	`$`, `\$`,
	`_`, `\_`,
	`%`, `\%`,
	`&`, `\&`,
	`#`, `\#`,
	`^`, `\url{^}`,
	`~`, `\url{~}`,
	`"`, `\url{"}`,
	`}`, `\}`,
	`{`, `\{`,
	`]`, `{]}`,
	`[`, `{[}`)

type latex struct {
	out    *bufio.Writer
	labels map[string]struct{}
}

func (l *latex) writeString(txt string) error {
	_, err := l.out.WriteString(txt)
	return err
}

func (l *latex) generateLabel(name string) string {
	name = strings.ToLower(strings.TrimSpace(name))
	name = strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return '-'
		}
		return r
	}, name)
	if _, found := l.labels[name]; !found {
		l.labels[name] = struct{}{}
		return name
	}
	for i := 1; ; i++ {
		n := name + "-" + strconv.Itoa(i)
		if _, found := l.labels[n]; !found {
			l.labels[n] = struct{}{}
			return n
		}
	}
}

func (l *latex) str(txt string) func(*node) error {
	return func(n *node) error { return l.writeString(txt) }
}

func (l *latex) command(cmd string) *visitor {
	return &visitor{l.str(`\` + cmd + `{`), l.str(`}`)}
}

func (l *latex) text(n *node) error {
	return l.writeString(latexReplacer.Replace(n.value.(string)))
}

func (l *latex) link() *visitor {
	return &visitor{
		enter: func(n *node) error {
			l.writeString(`\href{`)
			l.writeString(latexReplacer.Replace(n.value.(string)))
			return l.writeString(`}{`)
		},
		leave: l.str(`}`),
	}
}

func (l *latex) image(n *node) error {
	img := n.value.(*image)
	l.writeString(`\begin{figure}` + "\n")
	l.writeString(`\centering` + "\n")
	l.writeString(`\includegraphics{`)
	l.writeString(latexReplacer.Replace(img.src))
	l.writeString(`}` + "\n" + `\caption{`)
	l.writeString(latexReplacer.Replace(img.alt))
	return l.writeString(`}` + "\n" + `\end{figure}` + "\n")
}

func (l *latex) header(typ string) func(*node) error {
	return func(n *node) error {
		title := n.value.(string)
		_, err := fmt.Fprintf(l.out, "\n\n\\%s{%s}\\label{%s}\n\n",
			typ,
			latexReplacer.Replace(title),
			latexReplacer.Replace(l.generateLabel(title)))
		return err
	}
}

func exportLaTex(doc *document, out io.Writer, standalone bool) error {

	l := latex{
		out:    bufio.NewWriter(out),
		labels: make(map[string]struct{}),
	}

	if standalone {
		l.writeString(latexHeader)
	}

	// TODO: Implement document traversal.
	err := doc.traverse(map[nodeType]*visitor{
		// TODO: orderedListNode
		// TODO: unorderedListNode
		// TODO: listItemNode
		textNode:        &visitor{enter: l.text},
		boldNode:        l.command("textbf"),
		italicsNode:     l.command("textit"),
		underlinedNode:  l.command("underline"),
		strikeNode:      l.command("cancel"),
		superscriptNode: l.command("textsuperscript"),
		subscriptNode:   l.command("textsubscript"),
		// TODO: tableNode
		// TODO: tableRowNode
		// TODO: tableCellNode
		// TODO: tableHeaderRowNode
		// TODO: tableHeaderCellNode
		heading1Node:  &visitor{enter: l.header("section")},
		heading2Node:  &visitor{enter: l.header("subsection")},
		heading3Node:  &visitor{enter: l.header("subsubsection")},
		heading4Node:  &visitor{enter: l.header("paragraph")},
		heading5Node:  &visitor{enter: l.header("subparagraph")},
		heading6Node:  &visitor{enter: l.header("subparagraph")},
		paragraphNode: &visitor{l.str("\n"), l.str("\n")},
		lineBreakNode: &visitor{enter: l.str(`\\` + "\n")},
		// TODO: escapeNode
		// TODO: noWikiNode
		noWikiInlineNode: l.command("texttt"),
		imageNode:        &visitor{enter: l.image},
		linkNode:         l.link(),
		horizontalLineNode: &visitor{
			enter: l.str(
				"\n" + `\begin{center}\rule{0.5\linewidth}{\linethickness}\end{center}` + "\n")},
	})

	if err != nil {
		return err
	}

	if standalone {
		l.writeString(latexFooter)
	}

	return l.out.Flush()
}
<|endoftext|>