text:
/*
 * @OSF_COPYRIGHT@
 */

/*
 * x86 CPU identification
 *
 */

#ifndef _MACHINE_CPUID_H_
#define _MACHINE_CPUID_H_

#include <sys/appleapiopts.h>

#ifdef __APPLE_API_PRIVATE

#define	CPUID_VID_INTEL		"GenuineIntel"
#define	CPUID_VID_AMD		"AuthenticAMD"

#define CPUID_VMM_ID_VMWARE		"VMwareVMware"
#define CPUID_VMM_ID_PARALLELS	"Parallels\0\0\0"

#define CPUID_STRING_UNKNOWN    "Unknown CPU Typ"

#define _Bit(n)			(1ULL << n)
#define _HBit(n)		(1ULL << ((n)+32))

/*
 * The CPUID_FEATURE_XXX values define 64-bit values
 * returned in %ecx:%edx to a CPUID request with %eax of 1:
 */
#define CPUID_FEATURE_FPU       _Bit(0)   /* Floating point unit on-chip */
#define CPUID_FEATURE_VME       _Bit(1)   /* Virtual Mode Extension */
#define CPUID_FEATURE_DE        _Bit(2)   /* Debugging Extension */
#define CPUID_FEATURE_PSE       _Bit(3)   /* Page Size Extension */
#define CPUID_FEATURE_TSC       _Bit(4)   /* Time Stamp Counter */
#define CPUID_FEATURE_MSR       _Bit(5)   /* Model Specific Registers */
#define CPUID_FEATURE_PAE       _Bit(6)   /* Physical Address Extension */
#define CPUID_FEATURE_MCE       _Bit(7)   /* Machine Check Exception */
#define CPUID_FEATURE_CX8       _Bit(8)   /* CMPXCHG8B */
#define CPUID_FEATURE_APIC      _Bit(9)   /* On-chip APIC */
#define CPUID_FEATURE_SEP       _Bit(11)  /* Fast System Call */
#define CPUID_FEATURE_MTRR      _Bit(12)  /* Memory Type Range Register */
#define CPUID_FEATURE_PGE       _Bit(13)  /* Page Global Enable */
#define CPUID_FEATURE_MCA       _Bit(14)  /* Machine Check Architecture */
#define CPUID_FEATURE_CMOV      _Bit(15)  /* Conditional Move Instruction */
#define CPUID_FEATURE_PAT       _Bit(16)  /* Page Attribute Table */
#define CPUID_FEATURE_PSE36     _Bit(17)  /* 36-bit Page Size Extension */
#define CPUID_FEATURE_PSN       _Bit(18)  /* Processor Serial Number */
#define CPUID_FEATURE_CLFSH     _Bit(19)  /* CLFLUSH Instruction supported */
#define CPUID_FEATURE_DS        _Bit(21)  /* Debug Store */
#define CPUID_FEATURE_ACPI      _Bit(22)  /* Thermal monitor and Clock Ctrl */
#define CPUID_FEATURE_MMX       _Bit(23)  /* MMX supported */
#define CPUID_FEATURE_FXSR      _Bit(24)  /* Fast floating pt save/restore */
#define CPUID_FEATURE_SSE       _Bit(25)  /* Streaming SIMD extensions */
#define CPUID_FEATURE_SSE2      _Bit(26)  /* Streaming SIMD extensions 2 */
#define CPUID_FEATURE_SS        _Bit(27)  /* Self-Snoop */
#define CPUID_FEATURE_HTT       _Bit(28)  /* Hyper-Threading Technology */
#define CPUID_FEATURE_TM        _Bit(29)  /* Thermal Monitor (TM1) */
#define CPUID_FEATURE_PBE       _Bit(31)  /* Pend Break Enable */

#define CPUID_FEATURE_SSE3      _HBit(0)  /* Streaming SIMD extensions 3 */
#define CPUID_FEATURE_PCLMULQDQ _HBit(1)  /* PCLMULQDQ instruction */
#define CPUID_FEATURE_DTES64    _HBit(2)  /* 64-bit DS layout */
#define CPUID_FEATURE_MONITOR   _HBit(3)  /* Monitor/mwait */
#define CPUID_FEATURE_DSCPL     _HBit(4)  /* Debug Store CPL */
#define CPUID_FEATURE_VMX       _HBit(5)  /* VMX */
#define CPUID_FEATURE_SMX       _HBit(6)  /* SMX */
#define CPUID_FEATURE_EST       _HBit(7)  /* Enhanced SpeedsTep (GV3) */
#define CPUID_FEATURE_TM2       _HBit(8)  /* Thermal Monitor 2 */
#define CPUID_FEATURE_SSSE3     _HBit(9)  /* Supplemental SSE3 instructions */
#define CPUID_FEATURE_CID       _HBit(10) /* L1 Context ID */
#define CPUID_FEATURE_SEGLIM64  _HBit(11) /* 64-bit segment limit checking */
#define CPUID_FEATURE_FMA       _HBit(12) /* Fused-Multiply-Add support */
#define CPUID_FEATURE_CX16      _HBit(13) /* CmpXchg16b instruction */
#define CPUID_FEATURE_xTPR      _HBit(14) /* Send Task PRiority msgs */
#define CPUID_FEATURE_PDCM      _HBit(15) /* Perf/Debug Capability MSR */

#define CPUID_FEATURE_PCID      _HBit(17) /* ASID-PCID support */
#define CPUID_FEATURE_DCA       _HBit(18) /* Direct Cache Access */
#define CPUID_FEATURE_SSE4_1    _HBit(19) /* Streaming SIMD extensions 4.1 */
#define CPUID_FEATURE_SSE4_2    _HBit(20) /* Streaming SIMD extensions 4.2 */
#define CPUID_FEATURE_x2APIC    _HBit(21) /* Extended APIC Mode */
#define CPUID_FEATURE_MOVBE     _HBit(22) /* MOVBE instruction */
#define CPUID_FEATURE_POPCNT    _HBit(23) /* POPCNT instruction */
#define CPUID_FEATURE_TSCTMR    _HBit(24) /* TSC deadline timer */
#define CPUID_FEATURE_AES       _HBit(25) /* AES instructions */
#define CPUID_FEATURE_XSAVE     _HBit(26) /* XSAVE instructions */
#define CPUID_FEATURE_OSXSAVE   _HBit(27) /* XGETBV/XSETBV instructions */
#define CPUID_FEATURE_AVX1_0	_HBit(28) /* AVX 1.0 instructions */
#define CPUID_FEATURE_F16C	_HBit(29) /* Float16 convert instructions */
#define CPUID_FEATURE_RDRAND	_HBit(30) /* RDRAND instruction */
#define CPUID_FEATURE_VMM       _HBit(31) /* VMM (Hypervisor) present */

/*
 * Leaf 7, subleaf 0 additional features.
 * Bits returned in %ebx:%ecx to a CPUID request with {%eax,%ecx} of (0x7,0x0}:
 */
#define CPUID_LEAF7_FEATURE_RDWRFSGS _Bit(0)	/* FS/GS base read/write */
#define CPUID_LEAF7_FEATURE_TSCOFF   _Bit(1)	/* TSC thread offset */
#define CPUID_LEAF7_FEATURE_BMI1     _Bit(3)	/* Bit Manipulation Instrs, set 1 */
#define CPUID_LEAF7_FEATURE_HLE      _Bit(4)	/* Hardware Lock Elision*/
#define CPUID_LEAF7_FEATURE_AVX2     _Bit(5)	/* AVX2 Instructions */
#define CPUID_LEAF7_FEATURE_SMEP     _Bit(7)	/* Supervisor Mode Execute Protect */
#define CPUID_LEAF7_FEATURE_BMI2     _Bit(8)	/* Bit Manipulation Instrs, set 2 */
#define CPUID_LEAF7_FEATURE_ERMS     _Bit(9)	/* Enhanced Rep Movsb/Stosb */
#define CPUID_LEAF7_FEATURE_INVPCID  _Bit(10)	/* INVPCID intruction, TDB */
#define CPUID_LEAF7_FEATURE_RTM      _Bit(11)	/* RTM */
#define CPUID_LEAF7_FEATURE_RDSEED   _Bit(18)	/* RDSEED Instruction */
#define CPUID_LEAF7_FEATURE_ADX      _Bit(19)	/* ADX Instructions */
#define CPUID_LEAF7_FEATURE_SMAP     _Bit(20)	/* Supervisor Mode Access Protect */
#define CPUID_LEAF7_FEATURE_SGX      _Bit(2)	/* Software Guard eXtensions */
#define CPUID_LEAF7_FEATURE_PQM      _Bit(12)	/* Platform Qos Monitoring */
#define CPUID_LEAF7_FEATURE_FPU_CSDS _Bit(13)	/* FPU CS/DS deprecation */
#define CPUID_LEAF7_FEATURE_MPX      _Bit(14)	/* Memory Protection eXtensions */
#define CPUID_LEAF7_FEATURE_PQE      _Bit(15)	/* Platform Qos Enforcement */
#define CPUID_LEAF7_FEATURE_CLFSOPT  _Bit(23)	/* CLFSOPT */
#define CPUID_LEAF7_FEATURE_IPT      _Bit(25)	/* Intel Processor Trace */
#define CPUID_LEAF7_FEATURE_SHA      _Bit(29)	/* SHA instructions */

#define CPUID_LEAF7_FEATURE_PREFETCHWT1 _HBit(0)/* Prefetch Write/T1 hint */

/*
 * The CPUID_EXTFEATURE_XXX values define 64-bit values
 * returned in %ecx:%edx to a CPUID request with %eax of 0x80000001:
 */
#define CPUID_EXTFEATURE_SYSCALL   _Bit(11)	/* SYSCALL/sysret */
#define CPUID_EXTFEATURE_XD	   _Bit(20)	/* eXecute Disable */

#define CPUID_EXTFEATURE_1GBPAGE   _Bit(26)	/* 1GB pages */
#define CPUID_EXTFEATURE_RDTSCP	   _Bit(27)	/* RDTSCP */
#define CPUID_EXTFEATURE_EM64T	   _Bit(29)	/* Extended Mem 64 Technology */

#define CPUID_EXTFEATURE_LAHF	   _HBit(0)	/* LAFH/SAHF instructions */
#define CPUID_EXTFEATURE_LZCNT     _HBit(5)	/* LZCNT instruction */
#define CPUID_EXTFEATURE_PREFETCHW _HBit(8)	/* PREFETCHW instruction */

/*
 * The CPUID_EXTFEATURE_XXX values define 64-bit values
 * returned in %ecx:%edx to a CPUID request with %eax of 0x80000007:
 */
#define CPUID_EXTFEATURE_TSCI      _Bit(8)	/* TSC Invariant */

/*
 * CPUID_X86_64_H_FEATURE_SUBSET and CPUID_X86_64_H_LEAF7_FEATURE_SUBSET
 * indicate the bitmask of features that must be present before the system
 * is eligible to run the "x86_64h" "Haswell feature subset" slice.
 */
#define CPUID_X86_64_H_FEATURE_SUBSET ( CPUID_FEATURE_FMA    | \
                                        CPUID_FEATURE_SSE4_2 | \
                                        CPUID_FEATURE_MOVBE  | \
                                        CPUID_FEATURE_POPCNT | \
                                        CPUID_FEATURE_AVX1_0   \
                                      )

#define CPUID_X86_64_H_EXTFEATURE_SUBSET ( CPUID_EXTFEATURE_LZCNT \
                                         )

#define CPUID_X86_64_H_LEAF7_FEATURE_SUBSET ( CPUID_LEAF7_FEATURE_BMI1 | \
                                              CPUID_LEAF7_FEATURE_AVX2 | \
                                              CPUID_LEAF7_FEATURE_BMI2   \
                                            )

#define	CPUID_CACHE_SIZE	16	/* Number of descriptor values */

#define CPUID_MWAIT_EXTENSION	_Bit(0)	/* enumeration of WMAIT extensions */
#define CPUID_MWAIT_BREAK	_Bit(1)	/* interrupts are break events	   */

#define CPUID_MODEL_PENRYN		0x17
#define CPUID_MODEL_NEHALEM		0x1A
#define CPUID_MODEL_FIELDS		0x1E	/* Lynnfield, Clarksfield */
#define CPUID_MODEL_DALES		0x1F	/* Havendale, Auburndale */
#define CPUID_MODEL_NEHALEM_EX		0x2E
#define CPUID_MODEL_DALES_32NM		0x25	/* Clarkdale, Arrandale */
#define CPUID_MODEL_WESTMERE		0x2C	/* Gulftown, Westmere-EP/-WS */
#define CPUID_MODEL_WESTMERE_EX		0x2F
#define CPUID_MODEL_SANDYBRIDGE		0x2A
#define CPUID_MODEL_JAKETOWN		0x2D
#define CPUID_MODEL_IVYBRIDGE		0x3A
#define CPUID_MODEL_IVYBRIDGE_EP	0x3E
#define CPUID_MODEL_CRYSTALWELL		0x46
#define CPUID_MODEL_HASWELL		0x3C
#define CPUID_MODEL_HASWELL_EP		0x3F
#define CPUID_MODEL_HASWELL_ULT		0x45
#define CPUID_MODEL_BROADWELL		0x3D
#define CPUID_MODEL_BROADWELL_ULX	0x3D
#define CPUID_MODEL_BROADWELL_ULT	0x3D
#define CPUID_MODEL_BRYSTALWELL		0x47
#define CPUID_MODEL_SKYLAKE		0x4E
#define CPUID_MODEL_SKYLAKE_ULT		0x4E
#define CPUID_MODEL_SKYLAKE_ULX		0x4E
#define CPUID_MODEL_SKYLAKE_DT		0x5E

#define CPUID_VMM_FAMILY_UNKNOWN	0x0
#define CPUID_VMM_FAMILY_VMWARE		0x1
#define CPUID_VMM_FAMILY_PARALLELS	0x2

#ifndef ASSEMBLER
#include <stdint.h>
#include <mach/mach_types.h>
#include <kern/kern_types.h>
#include <mach/machine.h>

typedef enum { eax, ebx, ecx, edx } cpuid_register_t;
static inline void
cpuid(uint32_t *data)
{
	__asm__ volatile ("cpuid"
		: "=a" (data[eax]),
		  "=b" (data[ebx]),
		  "=c" (data[ecx]),
		  "=d" (data[edx])
		: "a"  (data[eax]),
		  "b"  (data[ebx]),
		  "c"  (data[ecx]),
		  "d"  (data[edx]));
}

static inline void
do_cpuid(uint32_t selector, uint32_t *data)
{
	__asm__ volatile ("cpuid"
		: "=a" (data[0]),
		  "=b" (data[1]),
		  "=c" (data[2]),
		  "=d" (data[3])
		: "a"(selector),
		  "b" (0),
		  "c" (0),
		  "d" (0));
}

/*
 * Cache ID descriptor structure, used to parse CPUID leaf 2.
 * Note: not used in kernel.
 */
typedef enum { Lnone, L1I, L1D, L2U, L3U, LCACHE_MAX } cache_type_t ;
typedef struct {
	unsigned char	value;          /* Descriptor value */
	cache_type_t 	type;           /* Cache type */
	unsigned int 	size;           /* Cache size */
	unsigned int 	linesize;       /* Cache line size */
	const char	*description;   /* Cache description */
} cpuid_cache_desc_t;

#define CACHE_DESC(value,type,size,linesize,text) \
	{ value, type, size, linesize, text }

/* Monitor/mwait Leaf: */
typedef struct {
	uint32_t	linesize_min;
	uint32_t	linesize_max;
	uint32_t	extensions;
	uint32_t	sub_Cstates;
} cpuid_mwait_leaf_t;

/* Thermal and Power Management Leaf: */
typedef struct {
	boolean_t	sensor;
	boolean_t	dynamic_acceleration;
	boolean_t	invariant_APIC_timer;
	boolean_t	core_power_limits;
	boolean_t	fine_grain_clock_mod;
	boolean_t	package_thermal_intr;
	uint32_t	thresholds;
	boolean_t	ACNT_MCNT;
	boolean_t	hardware_feedback;
	boolean_t	energy_policy;
} cpuid_thermal_leaf_t;

/* XSAVE Feature Leaf: */
typedef struct {
	uint32_t	extended_state[4];	/* eax .. edx */
} cpuid_xsave_leaf_t;

/* Architectural Performance Monitoring Leaf: */
typedef struct {
	uint8_t		version;
	uint8_t		number;
	uint8_t		width;
	uint8_t		events_number;
	uint32_t	events;
	uint8_t		fixed_number;
	uint8_t		fixed_width;
} cpuid_arch_perf_leaf_t;

/* The TSC to Core Crystal (RefCLK) Clock Information leaf */
typedef struct {
	uint32_t	numerator;
	uint32_t	denominator;
} cpuid_tsc_leaf_t;

/* Physical CPU info - this is exported out of the kernel (kexts), so be wary of changes */
typedef struct {
	char		cpuid_vendor[16];
	char		cpuid_brand_string[48];
	const char	*cpuid_model_string;

	cpu_type_t	cpuid_type;	/* this is *not* a cpu_type_t in our <mach/machine.h> */
	uint8_t		cpuid_family;
	uint8_t		cpuid_model;
	uint8_t		cpuid_extmodel;
	uint8_t		cpuid_extfamily;
	uint8_t		cpuid_stepping;
	uint64_t	cpuid_features;
	uint64_t	cpuid_extfeatures;
	uint32_t	cpuid_signature;
	uint8_t   	cpuid_brand;
	uint8_t		cpuid_processor_flag;

	uint32_t	cache_size[LCACHE_MAX];
	uint32_t	cache_linesize;

	uint8_t		cache_info[64];    /* list of cache descriptors */

	uint32_t	cpuid_cores_per_package;
	uint32_t	cpuid_logical_per_package;
	uint32_t	cache_sharing[LCACHE_MAX];
	uint32_t	cache_partitions[LCACHE_MAX];

	cpu_type_t	cpuid_cpu_type;			/* <mach/machine.h> */
	cpu_subtype_t	cpuid_cpu_subtype;		/* <mach/machine.h> */

	/* Per-vendor info */
	cpuid_mwait_leaf_t	cpuid_mwait_leaf;
#define cpuid_mwait_linesize_max	cpuid_mwait_leaf.linesize_max
#define cpuid_mwait_linesize_min	cpuid_mwait_leaf.linesize_min
#define cpuid_mwait_extensions		cpuid_mwait_leaf.extensions
#define cpuid_mwait_sub_Cstates		cpuid_mwait_leaf.sub_Cstates
	cpuid_thermal_leaf_t	cpuid_thermal_leaf;
	cpuid_arch_perf_leaf_t	cpuid_arch_perf_leaf;
	uint32_t	unused[4];			/* cpuid_xsave_leaf */

	/* Cache details: */
	uint32_t	cpuid_cache_linesize;
	uint32_t	cpuid_cache_L2_associativity;
	uint32_t	cpuid_cache_size;

	/* Virtual and physical address aize: */
	uint32_t	cpuid_address_bits_physical;
	uint32_t	cpuid_address_bits_virtual;

	uint32_t	cpuid_microcode_version;

	/* Numbers of tlbs per processor [i|d, small|large, level0|level1] */
	uint32_t	cpuid_tlb[2][2][2];
			#define	TLB_INST	0
			#define	TLB_DATA	1
			#define	TLB_SMALL	0
			#define	TLB_LARGE	1
	uint32_t	cpuid_stlb;

	uint32_t	core_count;
	uint32_t	thread_count;

	/* Max leaf ids available from CPUID */
	uint32_t	cpuid_max_basic;
	uint32_t	cpuid_max_ext;

	/* Family-specific info links */
	uint32_t		cpuid_cpufamily;
	cpuid_mwait_leaf_t	*cpuid_mwait_leafp;
	cpuid_thermal_leaf_t	*cpuid_thermal_leafp;
	cpuid_arch_perf_leaf_t	*cpuid_arch_perf_leafp;
	cpuid_xsave_leaf_t	*cpuid_xsave_leafp;
	uint64_t		cpuid_leaf7_features;
	cpuid_tsc_leaf_t	cpuid_tsc_leaf;
	cpuid_xsave_leaf_t	cpuid_xsave_leaf[2];
} i386_cpu_info_t;

#ifdef __cplusplus
extern "C" {
#endif

/*
 * External declarations
 */
extern cpu_type_t	cpuid_cputype(void);
extern cpu_subtype_t	cpuid_cpusubtype(void);
extern void		cpuid_cpu_display(const char *);
extern void		cpuid_feature_display(const char *);
extern void		cpuid_extfeature_display(const char *);
extern char *		cpuid_get_feature_names(uint64_t, char *, unsigned);
extern char *		cpuid_get_extfeature_names(uint64_t, char *, unsigned);
extern char *		cpuid_get_leaf7_feature_names(uint64_t, char *, unsigned);

extern uint64_t		cpuid_features(void);
extern uint64_t		cpuid_extfeatures(void);
extern uint64_t		cpuid_leaf7_features(void);
extern uint32_t		cpuid_family(void);
extern uint32_t		cpuid_cpufamily(void);

extern i386_cpu_info_t	*cpuid_info(void);
extern void		cpuid_set_info(void);

#ifdef __cplusplus
}
#endif

#endif /* ASSEMBLER */

#endif /* __APPLE_API_PRIVATE */
#endif /* _MACHINE_CPUID_H_ */


text:
import tensorflow as tf

CLASS_NUM = 10

def labels2OneHot(labels):
    batch_size = tf.size(labels)
    labels = tf.expand_dims(labels, 1)
    indices = tf.expand_dims(tf.range(0, batch_size, 1), 1)
    concated = tf.concat(1, [indices, labels])
    onehot_labels = tf.sparse_to_dense( concated, tf.pack([batch_size, CLASS_NUM]), 1.0, 0.0)

    return onehot_labels

labels_placeholders = tf.placeholder(tf.int32, shape=(5))
oneHostLabels = labels2OneHot(labels_placeholders)

sess = tf.Session()
feed_dict = { labels_placeholders: [1, 3, 7, 9, 0] }

print(sess.run(oneHostLabels, feed_dict=feed_dict))

redpajama_set_name: RedPajamaGithub


text:
import unittest
from flask import Flask, g, session, url_for
from flask_sqlalchemy import SQLAlchemy
from flask.ext.testing import TestCase

from app import app
from app.users.models import User
from app.journal.models import Journal
from app.users.models import Group
from app.sketchup.models import Scenario

class SketchupOuluUnitTest(TestCase):

    def create_app(self):
        app.config['WTF_CSRF_ENABLED'] = False
        with app.app_context():
            g.user = None
        return app

    def setUp(self):
        with app.app_context():
            g.user = None

    def login(self, email, password):
        input_data = {
            "email": email,
            "password": password
        }
        with app.app_context():
            resp = self.client.post('users/login', data=input_data, follow_redirects=True)

    def logout(self):
        with app.app_context():
            resp = self.client.post('users/logout', follow_redirects=True)

    def login_as_admin(self):
        with app.app_context():
            self.login('df.thangld@hotmail.com', 'abc')

    def login_as_user(self):
        with app.app_context():
            self.login('dreamingfighter@gmail.com', 'abc')

    # 1
    def test_login_success(self):
        input_data = {
            "email": "zhoujunjie.10@gmail.com",
            "password": "Zjj19911031"
        }
        with app.app_context():
            resp = self.client.post('users/login/', data=input_data)
            self.assertIn('user', g)
            self.assertNotEqual(g.user, None)

    # 2
    def test_login_unsuccessful(self):
        input_data = {
            "email": "zhoujunjie.10@gmail.com",
            "password": "Zjj199110311"
        }
        with app.app_context():
            resp = self.client.post('users/login/', data=input_data)
            self.assertNotIn('user', g)

    # 3
    def test_register_successful(self):
        input_data = {
            "name" : "Junjie",
            "email" : "zhoujunjie.10@gmail.com",
            "password" : "1234",
            "confirm" : "1234"
            "read"
        }
        with app.app_context():
            resp = self.client.post('users/register/', data=input_data)
            user = User.query.filter_by(username=input_data['name'], email=input_data['email']).first()
            self.assertNotEqual(user, None)

    # 4
    def test_register_unsuccessful_duplicateusername(self):
        input_data = {
            "name" : "Junjie",
            "email" : "asdasdasd23@qq.com",
            "password" : "1234",
            "confirm" : "1234"
            "read"
        }
        with app.app_context():
            resp = self.client.post('users/register/', data=input_data)
            user = User.query.filter_by(username=input_data['name'], email=input_data['email']).first()
            self.assertEqual(user, None)

    # 5
    def test_register_unsuccessful_duplicateemail(self):
        input_data = {
            "name" : "Hello",
            "email" : "412443823@qq.com",
            "password" : "1234",
            "confirm" : "1234"
            "read"
        }
        with app.app_context():
            resp = self.client.post('users/register/', data=input_data)
            user = User.query.filter_by(username=input_data['name'], email=input_data['email']).first()
            self.assertEqual(user, None)

    # 6
    def test_register_unsuccessful_misspassword(self):
        input_data = {
            "name" : "Hellobaby",
            "email" : "123asdas@qq.com",
        }
        with app.app_context():
            resp = self.client.post('users/register/', data=input_data)
            user = User.query.filter_by(username=input_data['name'], email=input_data['email']).first()
            self.assertEqual(user, None)

    # 7
    def test_login_notfound(self):
        input_data = {"email": "sadasd",
                      "password": "abc"}
        with app.app_context():
            resp = self.client.post('users/login/', data=input_data)
            self.assertNotIn('user', g)

    # 8
    def test_login_wrongpassword(self):
        input_data = {"email": "df.thangld@hotmail.com",
            "password": "abcd"
                    }
        with app.app_context():
            resp = self.client.post('users/login/', data=input_data)
            self.assertNotIn('user', g)

    # 9 user update profile
    def test_upload_profile(self):
        input_data = {
            "address" : "Vietnam",
            "fullname" : "Luu Thang",
            "phone_number" : "12345"
        }
        with app.app_context():
            #to login, use this line of code
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            resp = self.client.post('users/user_profile/', data=input_data)
            user = User.query.filter_by(address=input_data['address']).first()
            self.assertNotEqual(user, None)

    # 10
    def test_send_email(self):
        input_data = {
            "action" : "send",
            "emails"    :  "zhoujunjie.10@gmail.com",
            "title" :   "Test send email",
            "content" : "Hello World from SketchUp Oulu",
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            resp = self.client.post('admin/send_email/', data=input_data)

    # 11
    def test_send_email_normaluser(self):
        input_data = {
            "action" : "send",
            "emails"    :  "zhoujunjie.10@gmail.com",
            "title" :   "Test send email with normal account",
            "content" : "Hello World from Junjie SketchUp Oulu",
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': '412443823@qq.com', 'password': 'Zjj19911031'})
            resp = self.client.post('admin/send_email/', data=input_data)

    # 12
    def test_create_journal_admin(self):
        input_data = {
            "category_id" : "4",
            "is_activate" : "y",
            "title" : "play",
            "content" : "play games",
            "title_fi" : "Mita Kuulu",
            "content_fi" : "Hyva",
            "title_en" : "aaaaaa",
            "content_en" : "bbbbbbbbb",
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('admin/create_journal/', data=input_data)

    # 13 test create journal with normal account
    def test_create_journal_normaluser(self):
        input_data = {
            "category_id" : "4",
            "is_activate" : "y",
            "title" : "play_normal",
            "content" : "play games normal",
            "title_fi" : "Mita Kuulu",
            "content_fi" : "Hyva",
            "title_en" : "aaaaaa",
            "content_en" : "bbbbbbbbb",
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': '412443823@qq.com', 'password': 'Zjj19911031'})
            self.client.post('admin/create_journal/', data=input_data)
            journal = Journal.query.filter_by(content=input_data['content']).first()
            # self.assertEqual(journal, None)

    # 14
    def test_create_journal_blanktitle_admin(self):
        input_data = {
            "category_id" : "4",
            "is_activate" : "y",
            "content" : "play games hahaha",
            "title_fi" : "Mita Kuulu",
            "content_fi" : "Hyva",
            "title_en" : "aaaaaa",
            "content_en" : "bbbbbbbbb",
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('admin/create_journal/', data=input_data)
            journal = Journal.query.filter_by(content=input_data['content']).first()
            self.assertEqual(journal, None)

    # 15
    def test_create_journal_blankcontent_admin(self):
        input_data = {
            "category_id" : "4",
            "is_activate" : "y",
            "title" : "play_normal test blank content",
            "title_fi" : "Mita Kuulu",
            "content_fi" : "Hyva",
            "title_en" : "aaaaaa",
            "content_en" : "bbbbbbbbb",
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('admin/create_journal/', data=input_data)
            journal = Journal.query.filter_by(title=input_data['title']).first()
            self.assertEqual(journal, None)

    # 16 create journal with same title with existed one
    def test_create_journal_sametitle(self):
        input_data = {
            "category_id" : "4",
            "is_activate" : "y",
            "title" : "play_normal",
            "content" : "play games asdaas normal",
            "title_fi" : "Mita Kuulu asdasd",
            "content_fi" : "Hyva asdasd",
            "title_en" : "aaaaaa weq",
            "content_en" : "bbbbbbbbb asdxz",
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('admin/create_journal/', data=input_data)
            journal = Journal.query.filter_by(title=input_data['title'], content=input_data['content']).first()
            # self.assertEqual(journal, None)

    # 17 update user info, doesn't work properly
    def test_update_userprofile_admin(self):
        input_data = {
            "user_id" : "13",
            "fullname": "hahahahahaha",
            "address" : "Wuxi, China",
            "phone_number" : "412443823"
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('admin/update_user_info/', data=input_data)
            user = User.query.filter_by(fullname=input_data['fullname']).first()
            self.assertNotEqual(user, None)

    # 18 Admin update user group, doesn't work properly
    def test_update_usergroup_admin(self):
        input_data = {
            "user_id" : "12",
            "groups_value" : "|2|1|"
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('admin/update_user_info/', data=input_data)
            user = Group.query.filter_by(id=input_data['user_id']).first()
            self.assertNotEqual(user, None)

    # 19 create scenario
    def test_create_scenario(self):
        input_data = {
            "addition_information"
            "name" : "blabalascenario",
            "description" : "this is a testing scenario",
            "is_public" : "1"
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('users/add_scenario/', data=input_data)
            scenario = Scenario.query.filter_by(description=input_data['name']).first()
            self.assertNotEqual(scenario, None)

    # 20 create scenario with blank title
    def test_create_scenario_blanktitle(self):
        input_data = {
            "addition_information"
            "description" : "this is a testing scenario for blank title",
            "is_public" : "1"
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('users/add_scenario/', data=input_data)
            scenario = Scenario.query.filter_by(description=input_data['name']).first()
            self.assertEqual(scenario, None)

    # 21 Ban a user, failed test case
    def test_banuser(self):
        input_data={
            "user_id" : "13",
            "banned" : "1"
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('admin/update_user_info/', data=input_data)

    # 22 Unban a user
    def test_unbanuser(self):
        input_data={
            "user_id" : "13",
            "banned" : "0"
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('admin/update_user_info/', data=input_data)
            self.client.post('users/login/', data={'email': 'zhoujunjie.10@gmail.com', 'password': 'Zjj19911031'})

    # 23 test add comment on scenario
    def test_addcomment_on_scenario(self):
        input_data={
            "comment_type":"scenario",
            "object_id": "mf5L9uDLCOvsDnCEEHg0na197o1ZhPD7ct3TUQZLyKe2bBBisL",
            "content" : "this is supposed to be good"
        }
        with app.app_context():
            self.client.post('users/login/', data={'email': 'df.thangld@hotmail.com', 'password': 'abc'})
            self.client.post('users/add_comment/', data=input_data)

if __name__ == '__main__':
    unittest.main()


text:
{{<layout}}

{{$pageTitle}} You have not accepted the 3 year licence {{/pageTitle}}

{{$head}}
	{{>includes/head}}
	{{>includes/scripts}}
{{/head}}

{{$content}}
{{>includes/buttons/no-back-phase-banner}}

You did not accept the short period medical licence so you should stop driving

What happens next?

A letter explaining the reason for this revocation will be sent to you within the next 10 days. This includes advice on how to reapply in the future.

You need to return your current licence to DVLA Swansea SA99 1ZZ.

In future, if you are able to meet the medical standards for driving, you may be able drive again, but you you will need to reapply for your licence.

Information about how to reapply for your driving licence

			{{>includes/buttons/govuk}}

{{/content}}

{{/layout}}


text:
var env, store, Person, PhoneNumber, Post;
var attr = DS.attr;
var hasMany = DS.hasMany;
var belongsTo = DS.belongsTo;
var run = Ember.run;

module("unit/store/push - DS.Store#push", {
  setup: function() {
    Person = DS.Model.extend({
      firstName: attr('string'),
      lastName: attr('string'),
      phoneNumbers: hasMany('phone-number', { async: false })
    });
    Person.toString = function() {
      return 'Person';
    };

    PhoneNumber = DS.Model.extend({
      number: attr('string'),
      person: belongsTo('person', { async: false })
    });
    PhoneNumber.toString = function() {
      return 'PhoneNumber';
    };

    Post = DS.Model.extend({
      postTitle: attr('string')
    });
    Post.toString = function() {
      return 'Post';
    };

    env = setupStore({
      post: Post,
      person: Person,
      "phone-number": PhoneNumber
    });

    store = env.store;

    env.registry.register('serializer:post', DS.RESTSerializer);
  },

  teardown: function() {
    run(function() {
      store.destroy();
    });
  }
});

test("Calling push with a normalized hash returns a record", function() {
  expect(2);
  env.adapter.shouldBackgroundReloadRecord = () => false;

  run(function() {
    var person = store.push({
      data: {
        type: 'person',
        id: 'wat',
        attributes: {
          firstName: 'Yehuda',
          lastName: 'Katz'
        }
      }
    });
    store.findRecord('person', 'wat').then(function(foundPerson) {
      equal(foundPerson, person, "record returned via load() is the same as the record returned from findRecord()");
      deepEqual(foundPerson.getProperties('id', 'firstName', 'lastName'), {
        id: 'wat',
        firstName: 'Yehuda',
        lastName: 'Katz'
      });
    });
  });
});

test("Supplying a model class for `push` is the same as supplying a string", function () {
  expect(1);
  env.adapter.shouldBackgroundReloadRecord = () => false;

  var Programmer = Person.extend();
  env.registry.register('model:programmer', Programmer);

  run(function() {
    store.push({
      data: {
        type: 'programmer',
        id: 'wat',
        attributes: {
          firstName: 'Yehuda',
          lastName: 'Katz'
        }
      }
    });

    store.findRecord('programmer', 'wat').then(function(foundProgrammer) {
      deepEqual(foundProgrammer.getProperties('id', 'firstName', 'lastName'), {
        id: 'wat',
        firstName: 'Yehuda',
        lastName: 'Katz'
      });
    });
  });
});

test("Calling push triggers `didLoad` even if the record hasn't been requested from the adapter", function() {
  expect(1);

  Person.reopen({
    didLoad: async(function() {
      ok(true, "The didLoad callback was called");
    })
  });

  run(function() {
    store.push({
      data: {
        type: 'person',
        id: 'wat',
        attributes: {
          firstName: 'Yehuda',
          lastName: 'Katz'
        }
      }
    });
  });
});

test("Calling push with partial records updates just those attributes", function() {
  expect(2);
  env.adapter.shouldBackgroundReloadRecord = () => false;

  run(function() {
    store.push({
      data: {
        type: 'person',
        id: 'wat',
        attributes: {
          firstName: 'Yehuda',
          lastName: 'Katz'
        }
      }
    });
    var person = store.peekRecord('person', 'wat');

    store.push({
      data: {
        type: 'person',
        id: 'wat',
        attributes: {
          lastName: "Katz!"
        }
      }
    });

    store.findRecord('person', 'wat').then(function(foundPerson) {
      equal(foundPerson, person, "record returned via load() is the same as the record returned from findRecord()");
      deepEqual(foundPerson.getProperties('id', 'firstName', 'lastName'), {
        id: 'wat',
        firstName: 'Yehuda',
        lastName: "Katz!"
      });
    });
  });
});

test("Calling push on normalize allows partial updates with raw JSON", function () {
  env.registry.register('serializer:person', DS.RESTSerializer);
  var person;

  run(function() {
    person = store.push({
      data: {
        type: 'person',
        id: '1',
        attributes: {
          firstName: 'Robert',
          lastName: 'Jackson'
        }
      }
    });

    store.push(store.normalize('person', {
      id: '1',
      firstName: "Jacquie"
    }));
  });

  equal(person.get('firstName'), "Jacquie", "you can push raw JSON into the store");
  equal(person.get('lastName'), "Jackson", "existing fields are untouched");
});

test("Calling push with a normalized hash containing IDs of related records returns a record", function() {
  expect(1);

  Person.reopen({
    phoneNumbers: hasMany('phone-number', { async: true })
  });

  env.adapter.findRecord = function(store, type, id) {
    if (id === "1") {
      return Ember.RSVP.resolve({
        id: 1,
        number: '5551212',
        person: 'wat'
      });
    }

    if (id === "2") {
      return Ember.RSVP.resolve({
        id: 2,
        number: '5552121',
        person: 'wat'
      });
    }
  };
  var person;

  run(function() {
    person = store.push(store.normalize('person', {
      id: 'wat',
      firstName: 'John',
      lastName: 'Smith',
      phoneNumbers: ["1", "2"]
    }));
    person.get('phoneNumbers').then(function(phoneNumbers) {
      deepEqual(phoneNumbers.map(function(item) {
        return item.getProperties('id', 'number', 'person');
      }), [{
        id: "1",
        number: '5551212',
        person: person
      }, {
        id: "2",
        number: '5552121',
        person: person
      }]);
    });
  });
});

test("Calling pushPayload allows pushing raw JSON", function () {
  run(function() {
    store.pushPayload('post', {
      posts: [{
        id: '1',
        postTitle: "Ember rocks"
      }]
    });
  });

  var post = store.peekRecord('post', 1);

  equal(post.get('postTitle'), "Ember rocks", "you can push raw JSON into the store");

  run(function() {
    store.pushPayload('post', {
      posts: [{
        id: '1',
        postTitle: "Ember rocks (updated)"
      }]
    });
  });

  equal(post.get('postTitle'), "Ember rocks (updated)", "You can update data in the store");
});

test("Calling pushPayload allows pushing singular payload properties", function () {
  run(function() {
    store.pushPayload('post', {
      post: {
        id: '1',
        postTitle: "Ember rocks"
      }
    });
  });

  var post = store.peekRecord('post', 1);

  equal(post.get('postTitle'), "Ember rocks", "you can push raw JSON into the store");

  run(function() {
    store.pushPayload('post', {
      post: {
        id: '1',
        postTitle: "Ember rocks (updated)"
      }
    });
  });

  equal(post.get('postTitle'), "Ember rocks (updated)", "You can update data in the store");
});

test("Calling pushPayload should use the type's serializer for normalizing", function () {
  expect(4);
  env.registry.register('serializer:post', DS.RESTSerializer.extend({
    normalize: function(store, payload) {
      ok(true, "normalized is called on Post serializer");
      return this._super(store, payload);
    }
  }));
  env.registry.register('serializer:person', DS.RESTSerializer.extend({
    normalize: function(store, payload) {
      ok(true, "normalized is called on Person serializer");
      return this._super(store, payload);
    }
  }));

  run(function() {
    store.pushPayload('post', {
      posts: [{
        id: 1,
        postTitle: "Ember rocks"
      }],
      people: [{
        id: 2,
        firstName: "Yehuda"
      }]
    });
  });

  var post = store.peekRecord('post', 1);

  equal(post.get('postTitle'), "Ember rocks", "you can push raw JSON into the store");

  var person = store.peekRecord('person', 2);

  equal(person.get('firstName'), "Yehuda", "you can push raw JSON into the store");
});

test("Calling pushPayload without a type uses application serializer's pushPayload method", function () {
  expect(1);

  env.registry.register('serializer:application', DS.RESTSerializer.extend({
    pushPayload: function(store, payload) {
      ok(true, "pushPayload is called on Application serializer");
      return this._super(store, payload);
    }
  }));

  run(function() {
    store.pushPayload({
      posts: [{ id: '1', postTitle: "Ember rocks" }]
    });
  });
});

test("Calling pushPayload without a type should use a model's serializer when normalizing", function () {
  expect(4);

  env.registry.register('serializer:post', DS.RESTSerializer.extend({
    normalize: function(store, payload) {
      ok(true, "normalized is called on Post serializer");
      return this._super(store, payload);
    }
  }));

  env.registry.register('serializer:application', DS.RESTSerializer.extend({
    normalize: function(store, payload) {
      ok(true, "normalized is called on Application serializer");
      return this._super(store, payload);
    }
  }));

  run(function() {
    store.pushPayload({
      posts: [{
        id: '1',
        postTitle: "Ember rocks"
      }],
      people: [{
        id: '2',
        firstName: 'Yehuda'
      }]
    });
  });

  var post = store.peekRecord('post', 1);

  equal(post.get('postTitle'), "Ember rocks", "you can push raw JSON into the store");

  var person = store.peekRecord('person', 2);

  equal(person.get('firstName'), "Yehuda", "you can push raw JSON into the store");
});

test("Calling pushPayload allows partial updates with raw JSON", function () {
  env.registry.register('serializer:person', DS.RESTSerializer);

  var person;

  run(function() {
    store.pushPayload('person', {
      people: [{
        id: '1',
        firstName: "Robert",
        lastName: "Jackson"
      }]
    });
  });

  person = store.peekRecord('person', 1);

  equal(person.get('firstName'), "Robert", "you can push raw JSON into the store");
  equal(person.get('lastName'), "Jackson", "you can push raw JSON into the store");

  run(function() {
    store.pushPayload('person', {
      people: [{
        id: '1',
        firstName: "Jacquie"
      }]
    });
  });

  equal(person.get('firstName'), "Jacquie", "you can push raw JSON into the store");
  equal(person.get('lastName'), "Jackson", "existing fields are untouched");
});

test('calling push without data argument as an object raises an error', function() {
  var invalidValues = [
    null,
    1,
    'string',
    Ember.Object.create(),
    Ember.Object.extend(),
    true
  ];

  expect(invalidValues.length);

  invalidValues.forEach(function(invalidValue) {
    throws(function() {
      run(function() {
        store.push('person', invalidValue);
      });
    }, /object/);
  });
});

test('Calling push with a link for a non async relationship should warn', function() {
  Person.reopen({
    phoneNumbers: hasMany('phone-number', { async: false })
  });

  warns(function() {
    run(function() {
      store.push(store.normalize('person', {
        id: '1',
        links: {
          phoneNumbers: '/api/people/1/phone-numbers'
        }
      }));
    });
  }, /You have pushed a record of type 'person' with 'phoneNumbers' as a link, but the association is not an async relationship./);
});

test('Calling push with an unknown model name throws an assertion error', function() {

  expectAssertion(function() {
    run(function() {
      store.push({
        data: {
          id: '1',
          type: 'unknown'
        }
      });
    });
  }, /You tried to push data with a type 'unknown' but no model could be found with that name/);
});

test('Calling push with a link containing an object throws an assertion error', function() {
  Person.reopen({
    phoneNumbers: hasMany('phone-number', { async: true })
  });

  expectAssertion(function() {
    run(function() {
      store.push(store.normalize('person', {
        id: '1',
        links: {
          phoneNumbers: {
            href: '/api/people/1/phone-numbers'
          }
        }
      }));
    });
  }, "You have pushed a record of type 'person' with 'phoneNumbers' as a link, but the value of that link is not a string.");
});

test('Calling push with a link containing the value null', function() {
  run(function() {
    store.push(store.normalize('person', {
      id: '1',
      firstName: 'Tan',
      links: {
        phoneNumbers: null
      }
    }));
  });

  var person = store.peekRecord('person', 1);

  equal(person.get('firstName'), "Tan", "you can use links that contain null as a value");
});

test('calling push with hasMany relationship the value must be an array', function() {
  var invalidValues = [
    1,
    'string',
    Ember.Object.create(),
    Ember.Object.extend(),
    true
  ];

  expect(invalidValues.length);

  invalidValues.forEach(function(invalidValue) {
    throws(function() {
      run(function() {
        store.push({
          data: {
            type: 'person',
            id: '1',
            relationships: {
              phoneNumbers: {
                data: invalidValue
              }
            }
          }
        });
      });
    }, /must be an array/);
  });
});

test('calling push with missing or invalid `id` throws assertion error', function() {
  var invalidValues = [
    {},
    { id: null },
    { id: '' }
  ];

  expect(invalidValues.length);

  invalidValues.forEach(function(invalidValue) {
    throws(function() {
      run(function() {
        store.push({
          data: invalidValue
        });
      });
    }, /You must include an 'id'/);
  });
});

test('calling push with belongsTo relationship the value must not be an array', function() {
  throws(function() {
    run(function() {
      store.push({
        data: {
          type: 'phone-number',
          id: '1',
          relationships: {
            person: {
              data: [1]
            }
          }
        }
      });
    });
  }, /must not be an array/);
});

test("Enabling Ember.ENV.DS_WARN_ON_UNKNOWN_KEYS should warn on unknown keys", function() {
  run(function() {
    var originalFlagValue = Ember.ENV.DS_WARN_ON_UNKNOWN_KEYS;
    try {
      Ember.ENV.DS_WARN_ON_UNKNOWN_KEYS = true;
      warns(function() {
        store.push({
          data: {
            type: 'person',
            id: '1',
            attributes: {
              firstName: 'Tomster',
              emailAddress: 'tomster@emberjs.com',
              isMascot: true
            }
          }
        });
      });
    } finally {
      Ember.ENV.DS_WARN_ON_UNKNOWN_KEYS = originalFlagValue;
    }
  });
});

test("Calling push with unknown keys should not warn by default", function() {
  noWarns(function() {
    run(function() {
      store.push({
        data: {
          type: 'person',
          id: '1',
          attributes: {
            firstName: 'Tomster',
            emailAddress: 'tomster@emberjs.com',
            isMascot: true
          }
        }
      });
    });
  }, /The payload for 'person' contains these unknown keys: \[emailAddress,isMascot\]. Make sure they've been defined in your model./);
});

module("unit/store/push - DS.Store#push with JSON-API", {
  setup: function() {
    var Person = DS.Model.extend({
      name: DS.attr('string'),
      cars: DS.hasMany('car', { async: false })
    });

    Person.toString = function() { return "Person"; };

    var Car = DS.Model.extend({
      make: DS.attr('string'),
      model: DS.attr('string'),
      person: DS.belongsTo('person', { async: false })
    });

    Car.toString = function() { return "Car"; };

    env = setupStore({
      adapter: DS.Adapter,
      car: Car,
      person: Person
    });
    store = env.store;

  },

  teardown: function() {
    run(function() {
      store.destroy();
    });
  }
});

test("Should support pushing multiple models into the store", function() {
  expect(2);

  run(function() {
    store.push({
      data: [{
        type: 'person',
        id: 1,
        attributes: {
          name: 'Tom Dale'
        }
      }, {
        type: 'person',
        id: 2,
        attributes: {
          name: "Tomster"
        }
      }]
    });
  });

  var tom = store.peekRecord('person', 1);
  equal(tom.get('name'), 'Tom Dale', 'Tom should be in the store');

  var tomster = store.peekRecord('person', 2);
  equal(tomster.get('name'), 'Tomster', 'Tomster should be in the store');
});

test("Should support pushing included models into the store", function() {
  expect(2);

  run(function() {
    store.push({
      data: [{
        type: 'person',
        id: 1,
        attributes: {
          name: 'Tomster'
        },
        relationships: {
          cars: [{
            data: {
              type: 'person', id: 1
            }
          }]
        }
      }],
      included: [{
        type: 'car',
        id: 1,
        attributes: {
          make: 'Dodge',
          model: 'Neon'
        },
        relationships: {
          person: {
            data: {
              id: 1, type: 'person'
            }
          }
        }
      }]
    });
  });

  var tomster = store.peekRecord('person', 1);
  equal(tomster.get('name'), 'Tomster', 'Tomster should be in the store');

  var car = store.peekRecord('car', 1);
  equal(car.get('model'), 'Neon', 'Tomster\'s car should be in the store');
});


text:
<?php

class Google_Service_PeopleService_ImClient extends Google_Model
{
  public $formattedProtocol;
  public $formattedType;
  protected $metadataType = 'Google_Service_PeopleService_FieldMetadata';
  protected $metadataDataType = '';
  public $protocol;
  public $type;
  public $username;

  public function setFormattedProtocol($formattedProtocol)
  {
    $this->formattedProtocol = $formattedProtocol;
  }
  public function getFormattedProtocol()
  {
    return $this->formattedProtocol;
  }
  public function setFormattedType($formattedType)
  {
    $this->formattedType = $formattedType;
  }
  public function getFormattedType()
  {
    return $this->formattedType;
  }
  public function setMetadata(Google_Service_PeopleService_FieldMetadata $metadata)
  {
    $this->metadata = $metadata;
  }
  public function getMetadata()
  {
    return $this->metadata;
  }
  public function setProtocol($protocol)
  {
    $this->protocol = $protocol;
  }
  public function getProtocol()
  {
    return $this->protocol;
  }
  public function setType($type)
  {
    $this->type = $type;
  }
  public function getType()
  {
    return $this->type;
  }
  public function setUsername($username)
  {
    $this->username = $username;
  }
  public function getUsername()
  {
    return $this->username;
  }
}


text:
@interface LogFile : NSObject { }

@property (readonly)	NSString	*path;
@property (readonly)	NSString	*type;
@property (readonly)	NSString	*pid;
@property (readonly)	NSDate		*date;
@property (readonly)	NSNumber	*size;

+ (LogFile *)logFileFromDictionary:(NSDictionary *)aDictionary;

- (void)open;

@end


text:
/*!
* chartjs-plugin-annotation v1.2.2
* https://www.chartjs.org/chartjs-plugin-annotation/index
 * (c) 2021 chartjs-plugin-annotation Contributors
 * Released under the MIT License
 */
import { Element, defaults, Chart, Animations } from 'chart.js';
import { defined, distanceBetweenPoints, callback, isFinite, valueOrDefault, isObject, toFont, isArray, addRoundedRectPath, toTRBLCorners, toPadding, toRadians, color, drawPoint, isNumber, RAD_PER_DEG, PI as PI$1, clipArea, unclipArea } from 'chart.js/helpers';

const clickHooks = ['click', 'dblclick'];
const moveHooks = ['enter', 'leave'];
const hooks = clickHooks.concat(moveHooks);

function updateListeners(chart, state, options) {
  const annotations = state.annotations || [];
  state.listened = false;
  state.moveListened = false;

  hooks.forEach(hook => {
    if (typeof options[hook] === 'function') {
      state.listened = true;
      state.listeners[hook] = options[hook];
    } else if (defined(state.listeners[hook])) {
      delete state.listeners[hook];
    }
  });
  moveHooks.forEach(hook => {
    if (typeof options[hook] === 'function') {
      state.moveListened = true;
    }
  });

  if (!state.listened || !state.moveListened) {
    annotations.forEach(scope => {
      if (!state.listened) {
        clickHooks.forEach(hook => {
          if (typeof scope[hook] === 'function') {
            state.listened = true;
          }
        });
      }
      if (!state.moveListened) {
        moveHooks.forEach(hook => {
          if (typeof scope[hook] === 'function') {
            state.listened = true;
            state.moveListened = true;
          }
        });
      }
    });
  }
}

function handleEvent(state, event, options) {
  if (state.listened) {
    switch (event.type) {
    case 'mousemove':
    case 'mouseout':
      handleMoveEvents(state, event);
      break;
    case 'click':
      handleClickEvents(state, event, options);
      break;
    }
  }
}

function handleMoveEvents(state, event) {
  if (!state.moveListened) {
    return;
  }

  let element;

  if (event.type === 'mousemove') {
    element = getNearestItem(state.elements, event);
  }

  const previous = state.hovered;
  state.hovered = element;

  dispatchMoveEvents(state, {previous, element}, event);
}

function dispatchMoveEvents(state, elements, event) {
  const {previous, element} = elements;
  if (previous && previous !== element) {
    dispatchEvent(previous.options.leave || state.listeners.leave, previous, event);
  }
  if (element && element !== previous) {
    dispatchEvent(element.options.enter || state.listeners.enter, element, event);
  }
}

function handleClickEvents(state, event, options) {
  const listeners = state.listeners;
  const element = getNearestItem(state.elements, event);
  if (element) {
    const elOpts = element.options;
    const dblclick = elOpts.dblclick || listeners.dblclick;
    const click = elOpts.click || listeners.click;
    if (element.clickTimeout) {
      // 2nd click before timeout, so its a double click
      clearTimeout(element.clickTimeout);
      delete element.clickTimeout;
      dispatchEvent(dblclick, element, event);
    } else if (dblclick) {
      // if there is a dblclick handler, wait for dblClickSpeed ms before deciding its a click
      element.clickTimeout = setTimeout(() => {
        delete element.clickTimeout;
        dispatchEvent(click, element, event);
      }, options.dblClickSpeed);
    } else {
      // no double click handler, just call the click handler directly
      dispatchEvent(click, element, event);
    }
  }
}

function dispatchEvent(handler, element, event) {
  callback(handler, [element.$context, event]);
}

function getNearestItem(elements, position) {
  let minDistance = Number.POSITIVE_INFINITY;

  return elements
    .filter((element) => element.options.display && element.inRange(position.x, position.y))
    .reduce((nearestItems, element) => {
      const center = element.getCenterPoint();
      const distance = distanceBetweenPoints(position, center);

      if (distance < minDistance) {
        nearestItems = [element];
        minDistance = distance;
      } else if (distance === minDistance) {
        // Can have multiple items at the same distance in which case we sort by size
        nearestItems.push(element);
      }

      return nearestItems;
    }, [])
    .sort((a, b) => a._index - b._index)
    .slice(0, 1)[0]; // return only the top item
}

function adjustScaleRange(chart, scale, annotations) {
  const range = getScaleLimits(scale, annotations);
  let changed = changeScaleLimit(scale, range, 'min', 'suggestedMin');
  changed = changeScaleLimit(scale, range, 'max', 'suggestedMax') || changed;
  if (changed && typeof scale.handleTickRangeOptions === 'function') {
    scale.handleTickRangeOptions();
  }
}

function verifyScaleOptions(annotations, scales) {
  for (const annotation of annotations) {
    verifyScaleIDs(annotation, scales);
  }
}

function changeScaleLimit(scale, range, limit, suggestedLimit) {
  if (isFinite(range[limit]) && !scaleLimitDefined(scale.options, limit, suggestedLimit)) {
    const changed = scale[limit] !== range[limit];
    scale[limit] = range[limit];
    return changed;
  }
}

function scaleLimitDefined(scaleOptions, limit, suggestedLimit) {
  return defined(scaleOptions[limit]) || defined(scaleOptions[suggestedLimit]);
}

function verifyScaleIDs(annotation, scales) {
  for (const key of ['scaleID', 'xScaleID', 'yScaleID']) {
    if (annotation[key] && !scales[annotation[key]]) {
      console.warn(`No scale found with id '${annotation[key]}' for annotation '${annotation.id}'`);
    }
  }
}

function getScaleLimits(scale, annotations) {
  const axis = scale.axis;
  const scaleID = scale.id;
  const scaleIDOption = axis + 'ScaleID';
  const limits = {
    min: valueOrDefault(scale.min, Number.NEGATIVE_INFINITY),
    max: valueOrDefault(scale.max, Number.POSITIVE_INFINITY)
  };
  for (const annotation of annotations) {
    if (annotation.scaleID === scaleID) {
      updateLimits(annotation, scale, ['value', 'endValue'], limits);
    } else if (annotation[scaleIDOption] === scaleID) {
      updateLimits(annotation, scale, [axis + 'Min', axis + 'Max', axis + 'Value'], limits);
    }
  }
  return limits;
}

function updateLimits(annotation, scale, props, limits) {
  for (const prop of props) {
    const raw = annotation[prop];
    if (raw) {
      const value = scale.parse(raw);
      limits.min = Math.min(limits.min, value);
      limits.max = Math.max(limits.max, value);
    }
  }
}

const clamp = (x, from, to) => Math.min(to, Math.max(from, x));

function clampAll(obj, from, to) {
  for (const key of Object.keys(obj)) {
    obj[key] = clamp(obj[key], from, to);
  }
  return obj;
}

function inPointRange(point, center, radius) {
  if (!point || !center || radius <= 0) {
    return false;
  }
  return (Math.pow(point.x - center.x, 2) + Math.pow(point.y - center.y, 2)) <= Math.pow(radius, 2);
}

function inBoxRange(mouseX, mouseY, {x, y, width, height}) {
  return mouseX >= x &&
         mouseX <= x + width &&
         mouseY >= y &&
         mouseY <= y + height;
}

function getElementCenterPoint(element, useFinalPosition) {
  const {x, y} = element.getProps(['x', 'y'], useFinalPosition);
  return {x, y};
}

const isPercentString = (s) => typeof s === 'string' && s.endsWith('%');
const toPercent = (s) => clamp(parseFloat(s) / 100, 0, 1);

function getRelativePosition(size, positionOption) {
  if (positionOption === 'start') {
    return 0;
  }
  if (positionOption === 'end') {
    return size;
  }
  if (isPercentString(positionOption)) {
    return toPercent(positionOption) * size;
  }
  return size / 2;
}

function getSize(size, value) {
  if (typeof value === 'number') {
    return value;
  } else if (isPercentString(value)) {
    return toPercent(value) * size;
  }
  return size;
}

function calculateTextAlignment(size, options) {
  const {x, width} = size;
  const textAlign = options.textAlign;
  if (textAlign === 'center') {
    return x + width / 2;
  } else if (textAlign === 'end' || textAlign === 'right') {
    return x + width;
  }
  return x;
}

function readValueToProps(value, props, defValue) {
  const ret = {};
  const objProps = isObject(props);
  const keys = objProps ? Object.keys(props) : props;
  const read = isObject(value)
    ? objProps
      ? prop => valueOrDefault(value[prop], value[props[prop]])
      : prop => value[prop]
    : () => value;

  for (const prop of keys) {
    ret[prop] = valueOrDefault(read(prop), defValue);
  }
  return ret;
}

function toPosition(value) {
  return readValueToProps(value, ['x', 'y'], 'center');
}

function isBoundToPoint(options) {
  return options && (defined(options.xValue) || defined(options.yValue));
}

const widthCache = new Map();

function isImageOrCanvas(content) {
  return content instanceof Image || content instanceof HTMLCanvasElement;
}

/**
 * Apply border options to the canvas context before drawing a box
 * @param {CanvasRenderingContext2D} ctx - chart canvas context
 * @param {Object} options - options with border configuration
 * @returns {boolean} true is the border options have been applied
 */
function setBorderStyle(ctx, options) {
  if (options && options.borderWidth) {
    ctx.lineCap = options.borderCapStyle;
    ctx.setLineDash(options.borderDash);
    ctx.lineDashOffset = options.borderDashOffset;
    ctx.lineJoin = options.borderJoinStyle;
    ctx.lineWidth = options.borderWidth;
    ctx.strokeStyle = options.borderColor;
    return true;
  }
}

/**
 * Measure the label size using the label options.
 * @param {CanvasRenderingContext2D} ctx - chart canvas context
 * @param {Object} options - options to configure the label
 * @returns {{width: number, height: number}} the measured size of the label
 */
function measureLabelSize(ctx, options) {
  const content = options.content;
  if (isImageOrCanvas(content)) {
    return {
      width: getSize(content.width, options.width),
      height: getSize(content.height, options.height)
    };
  }
  const font = toFont(options.font);
  const lines = isArray(content) ? content : [content];
  const mapKey = lines.join() + font.string + (ctx._measureText ? '-spriting' : '');
  if (!widthCache.has(mapKey)) {
    ctx.save();
    ctx.font = font.string;
    const count = lines.length;
    let width = 0;
    for (let i = 0; i < count; i++) {
      const text = lines[i];
      width = Math.max(width, ctx.measureText(text).width);
    }
    ctx.restore();
    const height = count * font.lineHeight;
    widthCache.set(mapKey, {width, height});
  }
  return widthCache.get(mapKey);
}

/**
 * Draw a box with the size and the styling options.
 * @param {CanvasRenderingContext2D} ctx - chart canvas context
 * @param {{x: number, y: number, width: number, height: number}} rect - rect to draw
 * @param {Object} options - options to style the box
 * @returns {undefined}
 */
function drawBox(ctx, rect, options) {
  const {x, y, width, height} = rect;
  ctx.save();
  const stroke = setBorderStyle(ctx, options);
  ctx.fillStyle = options.backgroundColor;
  ctx.beginPath();
  addRoundedRectPath(ctx, {
    x, y, w: width, h: height,
    // TODO: v2 remove support for cornerRadius
    radius: clampAll(toTRBLCorners(valueOrDefault(options.cornerRadius, options.borderRadius)), 0, Math.min(width, height) / 2)
  });
  ctx.closePath();
  ctx.fill();
  if (stroke) {
    ctx.stroke();
  }
  ctx.restore();
}

function drawLabel(ctx, rect, options) {
  const content = options.content;
  if (isImageOrCanvas(content)) {
    ctx.drawImage(content, rect.x, rect.y, rect.width, rect.height);
    return;
  }
  const labels = isArray(content) ? content : [content];
  const font = toFont(options.font);
  const lh = font.lineHeight;
  const x = calculateTextAlignment(rect, options);
  const y = rect.y + (lh / 2);
  ctx.font = font.string;
  ctx.textBaseline = 'middle';
  ctx.textAlign = options.textAlign;
  ctx.fillStyle = options.color;
  labels.forEach((l, i) => ctx.fillText(l, x, y + (i * lh)));
}

function getRectCenterPoint(rect) {
  const {x, y, width, height} = rect;
  return {
    x: x + width / 2,
    y: y + height / 2
  };
}

/**
 * Rotate a `point` relative to `center` point by `angle`
 * @param {{x: number, y: number}} point - the point to rotate
 * @param {{x: number, y: number}} center - center point for rotation
 * @param {number} angle - angle for rotation, in radians
 * @returns {{x: number, y: number}} rotated point
 */
function rotated(point, center, angle) {
  var cos = Math.cos(angle);
  var sin = Math.sin(angle);
  var cx = center.x;
  var cy = center.y;

  return {
    x: cx + cos * (point.x - cx) - sin * (point.y - cy),
    y: cy + sin * (point.x - cx) + cos * (point.y - cy)
  };
}

function scaleValue(scale, value, fallback) {
  value = typeof value === 'number' ? value : scale.parse(value);
  return isFinite(value) ? scale.getPixelForValue(value) : fallback;
}

function getChartDimensionByScale(scale, options) {
  if (scale) {
    const min = scaleValue(scale, options.min, options.start);
    const max = scaleValue(scale, options.max, options.end);
    return {
      start: Math.min(min, max),
      end: Math.max(min, max)
    };
  }
  return {
    start: options.start,
    end: options.end
  };
}

function getChartPoint(chart, options) {
  const {chartArea, scales} = chart;
  const xScale = scales[options.xScaleID];
  const yScale = scales[options.yScaleID];
  let x = chartArea.width / 2;
  let y = chartArea.height / 2;

  if (xScale) {
    x = scaleValue(xScale, options.xValue, x);
  }

  if (yScale) {
    y = scaleValue(yScale, options.yValue, y);
  }
  return {x, y};
}

function getChartRect(chart, options) {
  const xScale = chart.scales[options.xScaleID];
  const yScale = chart.scales[options.yScaleID];
  let {top: y, left: x, bottom: y2, right: x2} = chart.chartArea;

  if (!xScale && !yScale) {
    return {options: {}};
  }

  const xDim = getChartDimensionByScale(xScale, {min: options.xMin, max: options.xMax, start: x, end: x2});
  x = xDim.start;
  x2 = xDim.end;
  const yDim = getChartDimensionByScale(yScale, {min: options.yMin, max: options.yMax, start: y, end: y2});
  y = yDim.start;
  y2 = yDim.end;

  return {
    x,
    y,
    x2,
    y2,
    width: x2 - x,
    height: y2 - y
  };
}

function getChartCircle(chart, options) {
  const point = getChartPoint(chart, options);
  return {
    x: point.x + options.xAdjust,
    y: point.y + options.yAdjust,
    width: options.radius * 2,
    height: options.radius * 2
  };
}

function resolvePointPosition(chart, options) {
  if (!isBoundToPoint(options)) {
    const box = getChartRect(chart, options);
    const point = getRectCenterPoint(box);
    let radius = options.radius;
    if (!radius || isNaN(radius)) {
      radius = Math.min(box.width, box.height) / 2;
      options.radius = radius;
    }
    return {
      x: point.x + options.xAdjust,
      y: point.y + options.yAdjust,
      width: radius * 2,
      height: radius * 2
    };
  }
  return getChartCircle(chart, options);
}

class BoxAnnotation extends Element {
  inRange(mouseX, mouseY, useFinalPosition) {
    return inBoxRange(mouseX, mouseY, this.getProps(['x', 'y', 'width', 'height'], useFinalPosition));
  }

  getCenterPoint(useFinalPosition) {
    return getRectCenterPoint(this.getProps(['x', 'y', 'width', 'height'], useFinalPosition));
  }

  draw(ctx) {
    ctx.save();
    drawBox(ctx, this, this.options);
    ctx.restore();
  }

  drawLabel(ctx) {
    const {x, y, width, height, options} = this;
    const labelOpts = options.label;
    labelOpts.borderWidth = options.borderWidth;
    ctx.save();
    ctx.beginPath();
    ctx.rect(x + labelOpts.borderWidth / 2, y + labelOpts.borderWidth / 2, width - labelOpts.borderWidth, height - labelOpts.borderWidth);
    ctx.clip();
    const position = toPosition(labelOpts.position);
    const padding = toPadding(labelOpts.padding);
    const labelSize = measureLabelSize(ctx, labelOpts);
    const labelRect = {
      x: calculateX(this, labelSize, position, padding),
      y: calculateY(this, labelSize, position, padding),
      width: labelSize.width,
      height: labelSize.height
    };
    drawLabel(ctx, labelRect, labelOpts);
    ctx.restore();
  }

  resolveElementProperties(chart, options) {
    return getChartRect(chart, options);
  }
}

BoxAnnotation.id = 'boxAnnotation';

BoxAnnotation.defaults = {
  adjustScaleRange: true,
  borderCapStyle: 'butt',
  borderDash: [],
  borderDashOffset: 0,
  borderJoinStyle: 'miter',
  borderRadius: 0,
  borderWidth: 1,
  cornerRadius: undefined, // TODO: v2 remove support for cornerRadius
  display: true,
  label: {
    color: 'black',
    content: null,
    drawTime: undefined,
    enabled: false,
    font: {
      family: undefined,
      lineHeight: undefined,
      size: undefined,
      style: undefined,
      weight: 'bold'
    },
    height: undefined,
    padding: 6,
    position: 'center',
    textAlign: 'start',
    xAdjust: 0,
    yAdjust: 0,
    width: undefined
  },
  xMax: undefined,
  xMin: undefined,
  xScaleID: 'x',
  yMax: undefined,
  yMin: undefined,
  yScaleID: 'y'
};

BoxAnnotation.defaultRoutes = {
  borderColor: 'color',
  backgroundColor: 'color'
};

function calculateX(box, labelSize, position, padding) {
  const {x: start, x2: end, width: size, options} = box;
  const {xAdjust: adjust, borderWidth} = options.label;
  return calculatePosition$1({start, end, size}, {
    position: position.x,
    padding: {start: padding.left, end: padding.right},
    adjust, borderWidth,
    size: labelSize.width
  });
}

function calculateY(box, labelSize, position, padding) {
  const {y: start, y2: end, height: size, options} = box;
  const {yAdjust: adjust, borderWidth} = options.label;
  return calculatePosition$1({start, end, size}, {
    position: position.y,
    padding: {start: padding.top, end: padding.bottom},
    adjust, borderWidth,
    size: labelSize.height
  });
}

function calculatePosition$1(boxOpts, labelOpts) {
  const {start, end} = boxOpts;
  const {position, padding: {start: padStart, end: padEnd}, adjust, borderWidth} = labelOpts;
  const availableSize = end - borderWidth - start - padStart - padEnd - labelOpts.size;
  return start + borderWidth / 2 + adjust + padStart + getRelativePosition(availableSize, position);
}

const PI = Math.PI;
const pointInLine = (p1, p2, t) => ({x: p1.x + t * (p2.x - p1.x), y: p1.y + t * (p2.y - p1.y)});
const interpolateX = (y, p1, p2) => pointInLine(p1, p2, Math.abs((y - p1.y) / (p2.y - p1.y))).x;
const interpolateY = (x, p1, p2) => pointInLine(p1, p2, Math.abs((x - p1.x) / (p2.x - p1.x))).y;
const sqr = v => v * v;

function isLineInArea({x, y, x2, y2}, {top, right, bottom, left}) {
  return !(
    (x < left && x2 < left) ||
    (x > right && x2 > right) ||
    (y < top && y2 < top) ||
    (y > bottom && y2 > bottom)
  );
}

function limitPointToArea({x, y}, p2, {top, right, bottom, left}) {
  if (x < left) {
    y = interpolateY(left, {x, y}, p2);
    x = left;
  }
  if (x > right) {
    y = interpolateY(right, {x, y}, p2);
    x = right;
  }
  if (y < top) {
    x = interpolateX(top, {x, y}, p2);
    y = top;
  }
  if (y > bottom) {
    x = interpolateX(bottom, {x, y}, p2);
    y = bottom;
  }
  return {x, y};
}

function limitLineToArea(p1, p2, area) {
  const {x, y} = limitPointToArea(p1, p2, area);
  const {x: x2, y: y2} = limitPointToArea(p2, p1, area);
  return {x, y, x2, y2, width: Math.abs(x2 - x), height: Math.abs(y2 - y)};
}

class LineAnnotation extends Element {
  intersects(x, y, epsilon = 0.001, useFinalPosition) {
    // Adapted from https://stackoverflow.com/a/6853926/25507
    const {x: x1, y: y1, x2, y2} = this.getProps(['x', 'y', 'x2', 'y2'], useFinalPosition);
    const dx = x2 - x1;
    const dy = y2 - y1;
    const lenSq = sqr(dx) + sqr(dy);
    const t = lenSq === 0 ? -1 : ((x - x1) * dx + (y - y1) * dy) / lenSq;
    let xx, yy;
    if (t < 0) {
      xx = x1;
      yy = y1;
    } else if (t > 1) {
      xx = x2;
      yy = y2;
    } else {
      xx = x1 + t * dx;
      yy = y1 + t * dy;
    }
    return (sqr(x - xx) + sqr(y - yy)) < epsilon;
  }

  // TODO: make private in v2
  labelIsVisible(useFinalPosition, chartArea) {
    const labelOpts = this.options.label;
    if (!chartArea || !labelOpts || !labelOpts.enabled) {
      return false;
    }
    return isLineInArea(this.getProps(['x', 'y', 'x2', 'y2'], useFinalPosition), chartArea);
  }

  // TODO: make private in v2
  isOnLabel(mouseX, mouseY, useFinalPosition) {
    if (!this.labelIsVisible(useFinalPosition)) {
      return false;
    }
    const {labelX, labelY, labelWidth, labelHeight, labelRotation} = this.getProps(['labelX', 'labelY', 'labelWidth', 'labelHeight', 'labelRotation'], useFinalPosition);
    const {x, y} = rotated({x: mouseX, y: mouseY}, {x: labelX, y: labelY}, -labelRotation);
    const w2 = labelWidth / 2;
    const h2 = labelHeight / 2;
    return x >= labelX - w2 && x <= labelX + w2 &&
      y >= labelY - h2 && y <= labelY + h2;
  }

  inRange(mouseX, mouseY, useFinalPosition) {
    const epsilon = sqr(this.options.borderWidth / 2);
    return this.intersects(mouseX, mouseY, epsilon, useFinalPosition) || this.isOnLabel(mouseX, mouseY, useFinalPosition);
  }

  getCenterPoint() {
    return {
      x: (this.x2 + this.x) / 2,
      y: (this.y2 + this.y) / 2
    };
  }

  draw(ctx) {
    const {x, y, x2, y2, options} = this;
    ctx.save();

    ctx.lineWidth = options.borderWidth;
    ctx.strokeStyle = options.borderColor;
    ctx.setLineDash(options.borderDash);
    ctx.lineDashOffset = options.borderDashOffset;

    // Draw
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x2, y2);
    ctx.stroke();

    ctx.restore();
  }

  drawLabel(ctx, chartArea) {
    if (!this.labelIsVisible(false, chartArea)) {
      return;
    }
    const {labelX, labelY, labelWidth, labelHeight, labelRotation, labelPadding, labelTextSize, options: {label}} = this;

    ctx.save();
    ctx.translate(labelX, labelY);
    ctx.rotate(labelRotation);

    const boxRect = {
      x: -(labelWidth / 2),
      y: -(labelHeight / 2),
      width: labelWidth,
      height: labelHeight
    };
    drawBox(ctx, boxRect, label);

    const labelTextRect = {
      x: -(labelWidth / 2) + labelPadding.left + label.borderWidth / 2,
      y: -(labelHeight / 2) + labelPadding.top + label.borderWidth / 2,
      width: labelTextSize.width,
      height: labelTextSize.height
    };
    drawLabel(ctx, labelTextRect, label);
    ctx.restore();
  }

  resolveElementProperties(chart, options) {
    const scale = chart.scales[options.scaleID];
    let {top: y, left: x, bottom: y2, right: x2} = chart.chartArea;
    let min, max;

    if (scale) {
      min = scaleValue(scale, options.value, NaN);
      max = scaleValue(scale, options.endValue, min);
      if (scale.isHorizontal()) {
        x = min;
        x2 = max;
      } else {
        y = min;
        y2 = max;
      }
    } else {
      const xScale = chart.scales[options.xScaleID];
      const yScale = chart.scales[options.yScaleID];

      if (xScale) {
        x = scaleValue(xScale, options.xMin, x);
        x2 = scaleValue(xScale, options.xMax, x2);
      }

      if (yScale) {
        y = scaleValue(yScale, options.yMin, y);
        y2 = scaleValue(yScale, options.yMax, y2);
      }
    }
    const inside = isLineInArea({x, y, x2, y2}, chart.chartArea);
    const properties = inside
      ? limitLineToArea({x, y}, {x: x2, y: y2}, chart.chartArea)
      : {x, y, x2, y2, width: Math.abs(x2 - x), height: Math.abs(y2 - y)};

    const label = options.label;
    if (label && label.content) {
      return loadLabelRect(properties, chart, label);
    }
    return properties;
  }
}

LineAnnotation.id = 'lineAnnotation';
LineAnnotation.defaults = {
  adjustScaleRange: true,
  borderDash: [],
  borderDashOffset: 0,
  borderWidth: 2,
  display: true,
  endValue: undefined,
  label: {
    backgroundColor: 'rgba(0,0,0,0.8)',
    borderCapStyle: 'butt',
    borderColor: 'black',
    borderDash: [],
    borderDashOffset: 0,
    borderJoinStyle: 'miter',
    borderRadius: 6,
    borderWidth: 0,
    color: '#fff',
    content: null,
    cornerRadius: undefined, // TODO: v2 remove support for cornerRadius
    drawTime: undefined,
    enabled: false,
    font: {
      family: undefined,
      lineHeight: undefined,
      size: undefined,
      style: undefined,
      weight: 'bold'
    },
    height: undefined,
    padding: 6,
    position: 'center',
    rotation: 0,
    textAlign: 'center',
    width: undefined,
    xAdjust: 0,
    xPadding: undefined, // TODO: v2 remove support for xPadding
    yAdjust: 0,
    yPadding: undefined, // TODO: v2 remove support for yPadding
  },
  scaleID: undefined,
  value: undefined,
  xMax: undefined,
  xMin: undefined,
  xScaleID: 'x',
  yMax: undefined,
  yMin: undefined,
  yScaleID: 'y'
};

LineAnnotation.defaultRoutes = {
  borderColor: 'color'
};

function loadLabelRect(line, chart, options) {
  // TODO: v2 remove support for xPadding and yPadding
  const {padding: lblPadding, xPadding, yPadding, borderWidth} = options;
  const padding = getPadding(lblPadding, xPadding, yPadding);
  const textSize = measureLabelSize(chart.ctx, options);
  const width = textSize.width + padding.width + borderWidth;
  const height = textSize.height + padding.height + borderWidth;
  const labelRect = calculateLabelPosition(line, options, {width, height, padding}, chart.chartArea);
  line.labelX = labelRect.x;
  line.labelY = labelRect.y;
  line.labelWidth = labelRect.width;
  line.labelHeight = labelRect.height;
  line.labelRotation = labelRect.rotation;
  line.labelPadding = padding;
  line.labelTextSize = textSize;
  return line;
}

function calculateAutoRotation(line) {
  const {x, y, x2, y2} = line;
  const rotation = Math.atan2(y2 - y, x2 - x);
  // Flip the rotation if it goes > PI/2 or < -PI/2, so label stays upright
  return rotation > PI / 2 ? rotation - PI : rotation < PI / -2 ? rotation + PI : rotation;
}

// TODO: v2 remove support for xPadding and yPadding
function getPadding(padding, xPadding, yPadding) {
  let tempPadding = padding;
  if (xPadding || yPadding) {
    tempPadding = {x: xPadding || 6, y: yPadding || 6};
  }
  return toPadding(tempPadding);
}

function calculateLabelPosition(line, label, sizes, chartArea) {
  const {width, height, padding} = sizes;
  const {xAdjust, yAdjust} = label;
  const p1 = {x: line.x, y: line.y};
  const p2 = {x: line.x2, y: line.y2};
  const rotation = label.rotation === 'auto' ? calculateAutoRotation(line) : toRadians(label.rotation);
  const size = rotatedSize(width, height, rotation);
  const t = calculateT(line, label, {labelSize: size, padding}, chartArea);
  const pt = pointInLine(p1, p2, t);
  const xCoordinateSizes = {size: size.w, min: chartArea.left, max: chartArea.right, padding: padding.left};
  const yCoordinateSizes = {size: size.h, min: chartArea.top, max: chartArea.bottom, padding: padding.top};

  return {
    x: adjustLabelCoordinate(pt.x, xCoordinateSizes) + xAdjust,
    y: adjustLabelCoordinate(pt.y, yCoordinateSizes) + yAdjust,
    width,
    height,
    rotation
  };
}

function rotatedSize(width, height, rotation) {
  const cos = Math.cos(rotation);
  const sin = Math.sin(rotation);
  return {
    w: Math.abs(width * cos) + Math.abs(height * sin),
    h: Math.abs(width * sin) + Math.abs(height * cos)
  };
}

function calculateT(line, label, sizes, chartArea) {
  let t;
  const space = spaceAround(line, chartArea);
  if (label.position === 'start') {
    t = calculateTAdjust({w: line.x2 - line.x, h: line.y2 - line.y}, sizes, label, space);
  } else if (label.position === 'end') {
    t = 1 - calculateTAdjust({w: line.x - line.x2, h: line.y - line.y2}, sizes, label, space);
  } else {
    t = getRelativePosition(1, label.position);
  }
  return t;
}

function calculateTAdjust(lineSize, sizes, label, space) {
  const {labelSize, padding} = sizes;
  const lineW = lineSize.w * space.dx;
  const lineH = lineSize.h * space.dy;
  const x = (lineW > 0) && ((labelSize.w / 2 + padding.left - space.x) / lineW);
  const y = (lineH > 0) && ((labelSize.h / 2 + padding.top - space.y) / lineH);
  return clamp(Math.max(x, y), 0, 0.25);
}

function spaceAround(line, chartArea) {
  const {x, x2, y, y2} = line;
  const t = Math.min(y, y2) - chartArea.top;
  const l = Math.min(x, x2) - chartArea.left;
  const b = chartArea.bottom - Math.max(y, y2);
  const r = chartArea.right - Math.max(x, x2);
  return {
    x: Math.min(l, r),
    y: Math.min(t, b),
    dx: l <= r ? 1 : -1,
    dy: t <= b ? 1 : -1
  };
}

function adjustLabelCoordinate(coordinate, labelSizes) {
  const {size, min, max, padding} = labelSizes;
  const halfSize = size / 2;
  if (size > max - min) {
    // if it does not fit, display as much as possible
    return (max + min) / 2;
  }
  if (min >= (coordinate - padding - halfSize)) {
    coordinate = min + padding + halfSize;
  }
  if (max <= (coordinate + padding + halfSize)) {
    coordinate = max - padding - halfSize;
  }
  return coordinate;
}

class EllipseAnnotation extends Element {

  inRange(mouseX, mouseY, useFinalPosition) {
    return pointInEllipse({x: mouseX, y: mouseY}, this.getProps(['x', 'y', 'width', 'height'], useFinalPosition), this.options.rotation);
  }

  getCenterPoint(useFinalPosition) {
    return getRectCenterPoint(this.getProps(['x', 'y', 'width', 'height'], useFinalPosition));
  }

  draw(ctx) {
    const {width, height, options} = this;
    const center = this.getCenterPoint();

    ctx.save();

    ctx.translate(center.x, center.y);
    if (options.rotation) {
      ctx.rotate(toRadians(options.rotation));
    }

    ctx.beginPath();

    ctx.lineWidth = options.borderWidth;
    ctx.strokeStyle = options.borderColor;
    ctx.fillStyle = options.backgroundColor;

    ctx.setLineDash(options.borderDash);
    ctx.lineDashOffset = options.borderDashOffset;

    ctx.ellipse(0, 0, height / 2, width / 2, Math.PI / 2, 0, 2 * Math.PI);

    ctx.fill();
    ctx.stroke();

    ctx.restore();
  }

  resolveElementProperties(chart, options) {
    return getChartRect(chart, options);
  }

}

EllipseAnnotation.id = 'ellipseAnnotation';

EllipseAnnotation.defaults = {
  adjustScaleRange: true,
  borderDash: [],
  borderDashOffset: 0,
  borderWidth: 1,
  display: true,
  rotation: 0,
  xMax: undefined,
  xMin: undefined,
  xScaleID: 'x',
  yMax: undefined,
  yMin: undefined,
  yScaleID: 'y'
};

EllipseAnnotation.defaultRoutes = {
  borderColor: 'color',
  backgroundColor: 'color'
};

function pointInEllipse(p, ellipse, rotation) {
  const {width, height} = ellipse;
  const center = ellipse.getCenterPoint(true);
  const xRadius = width / 2;
  const yRadius = height / 2;

  if (xRadius <= 0 || yRadius <= 0) {
    return false;
  }
  // https://stackoverflow.com/questions/7946187/point-and-ellipse-rotated-position-test-algorithm
  const angle = toRadians(rotation || 0);
  const cosAngle = Math.cos(angle);
  const sinAngle = Math.sin(angle);
  const a = Math.pow(cosAngle * (p.x - center.x) + sinAngle * (p.y - center.y), 2);
  const b = Math.pow(sinAngle * (p.x - center.x) - cosAngle * (p.y - center.y), 2);
  return (a / Math.pow(xRadius, 2)) + (b / Math.pow(yRadius, 2)) <= 1;
}

class LabelAnnotation extends Element {

  inRange(mouseX, mouseY, useFinalPosition) {
    return inBoxRange(mouseX, mouseY, this.getProps(['x', 'y', 'width', 'height'], useFinalPosition));
  }

  getCenterPoint(useFinalPosition) {
    return getRectCenterPoint(this.getProps(['x', 'y', 'width', 'height'], useFinalPosition));
  }

  draw(ctx) {
    if (!this.options.content) {
      return;
    }
    const {labelX, labelY, labelWidth, labelHeight, options} = this;
    drawCallout(ctx, this);
    if (this.boxVisible) {
      drawBox(ctx, this, options);
    }
    drawLabel(ctx, {x: labelX, y: labelY, width: labelWidth, height: labelHeight}, options);
  }

  // TODO: make private in v2
  resolveElementProperties(chart, options) {
    const point = !isBoundToPoint(options) ? getRectCenterPoint(getChartRect(chart, options)) : getChartPoint(chart, options);
    const padding = toPadding(options.padding);
    const labelSize = measureLabelSize(chart.ctx, options);
    const boxSize = measureRect(point, labelSize, options, padding);
    const bgColor = color(options.backgroundColor);
    const boxVisible = options.borderWidth > 0 || (bgColor && bgColor.valid && bgColor.rgb.a > 0);

    const properties = {
      boxVisible,
      pointX: point.x,
      pointY: point.y,
      ...boxSize,
      labelX: boxSize.x + padding.left + (options.borderWidth / 2),
      labelY: boxSize.y + padding.top + (options.borderWidth / 2),
      labelWidth: labelSize.width,
      labelHeight: labelSize.height
    };
    properties.calloutPosition = options.callout.enabled && resolveCalloutPosition(properties, options.callout);
    return properties;
  }
}

LabelAnnotation.id = 'labelAnnotation';

LabelAnnotation.defaults = {
  adjustScaleRange: true,
  backgroundColor: 'transparent',
  borderCapStyle: 'butt',
  borderDash: [],
  borderDashOffset: 0,
  borderJoinStyle: 'miter',
  borderRadius: 0,
  borderWidth: 0,
  callout: {
    borderCapStyle: 'butt',
    borderColor: undefined,
    borderDash: [],
    borderDashOffset: 0,
    borderJoinStyle: 'miter',
    borderWidth: 1,
    enabled: false,
    margin: 5,
    position: 'auto',
    side: 5,
    start: '50%',
  },
  color: 'black',
  content: null,
  display: true,
  font: {
    family: undefined,
    lineHeight: undefined,
    size: undefined,
    style: undefined,
    weight: undefined
  },
  height: undefined,
  padding: 6,
  position: 'center',
  textAlign: 'center',
  width: undefined,
  xAdjust: 0,
  xMax: undefined,
  xMin: undefined,
  xScaleID: 'x',
  xValue: undefined,
  yAdjust: 0,
  yMax: undefined,
  yMin: undefined,
  yScaleID: 'y',
  yValue: undefined
};

LabelAnnotation.defaultRoutes = {
  borderColor: 'color',
  backgroundColor: 'color',
};

function measureRect(point, size, options, padding) {
  const width = size.width + padding.width + options.borderWidth;
  const height = size.height + padding.height + options.borderWidth;
  const position = toPosition(options.position);

  return {
    x: calculatePosition(point.x, width, options.xAdjust, position.x),
    y: calculatePosition(point.y, height, options.yAdjust, position.y),
    width,
    height
  };
}

function calculatePosition(start, size, adjust = 0, position) {
  return start - getRelativePosition(size, position) + adjust;
}

function drawCallout(ctx, element) {
  const {pointX, pointY, calloutPosition, options} = element;
  if (!calloutPosition) {
    return;
  }
  const callout = options.callout;
  const {separatorStart, separatorEnd} = getCalloutSeparatorCoord(element, calloutPosition);
  const {sideStart, sideEnd} = getCalloutSideCoord(element, calloutPosition, separatorStart);
  ctx.save();
  ctx.beginPath();
  const stroke = setBorderStyle(ctx, callout);
  if (callout.margin > 0 || options.borderWidth === 0) {
    ctx.moveTo(separatorStart.x, separatorStart.y);
    ctx.lineTo(separatorEnd.x, separatorEnd.y);
  }
  ctx.moveTo(sideStart.x, sideStart.y);
  ctx.lineTo(sideEnd.x, sideEnd.y);
  ctx.lineTo(pointX, pointY);
  if (stroke) {
    ctx.stroke();
  }
  ctx.restore();
}

function getCalloutSeparatorCoord(element, position) {
  const {x, y, width, height} = element;
  const adjust = getCalloutSeparatorAdjust(element, position);
  let separatorStart, separatorEnd;
  if (position === 'left' || position === 'right') {
    separatorStart = {x: x + adjust, y};
    separatorEnd = {x: separatorStart.x, y: separatorStart.y + height};
  } else if (position === 'top' || position === 'bottom') {
    separatorStart = {x, y: y + adjust};
    separatorEnd = {x: separatorStart.x + width, y: separatorStart.y};
  }
  return {separatorStart, separatorEnd};
}

function getCalloutSeparatorAdjust(element, position) {
  const {width, height, options} = element;
  const adjust = options.callout.margin + options.borderWidth / 2;
  if (position === 'right') {
    return width + adjust;
  } else if (position === 'bottom') {
    return height + adjust;
  }
  return -adjust;
}

function getCalloutSideCoord(element, position, separatorStart) {
  const {y, width, height, options} = element;
  const start = options.callout.start;
  const side = getCalloutSideAdjust(position, options.callout);
  let sideStart, sideEnd;
  if (position === 'left' || position === 'right') {
    sideStart = {x: separatorStart.x, y: y + getSize(height, start)};
    sideEnd = {x: sideStart.x + side, y: sideStart.y};
  } else if (position === 'top' || position === 'bottom') {
    sideStart = {x: separatorStart.x + getSize(width, start), y: separatorStart.y};
    sideEnd = {x: sideStart.x, y: sideStart.y + side};
  }
  return {sideStart, sideEnd};
}

function getCalloutSideAdjust(position, options) {
  const side = options.side;
  if (position === 'left' || position === 'top') {
    return -side;
  }
  return side;
}

function resolveCalloutPosition(element, options) {
  const position = options.position;
  if (position === 'left' || position === 'right' || position === 'top' || position === 'bottom') {
    return position;
  }
  return resolveCalloutAutoPosition(element, options);
}

function resolveCalloutAutoPosition(element, options) {
  const {x, y, width, height, pointX, pointY} = element;
  const {margin, side} = options;
  const adjust = margin + side;
  if (pointX < (x - adjust)) {
    return 'left';
  } else if (pointX > (x + width + adjust)) {
    return 'right';
  } else if (pointY < (y + height + adjust)) {
    return 'top';
  } else if (pointY > (y - adjust)) {
    return 'bottom';
  }
}

class PointAnnotation extends Element {

  inRange(mouseX, mouseY, useFinalPosition) {
    const {width} = this.getProps(['width'], useFinalPosition);
    return inPointRange({x: mouseX, y: mouseY}, this.getCenterPoint(useFinalPosition), width / 2 + this.options.borderWidth);
  }

  getCenterPoint(useFinalPosition) {
    return getElementCenterPoint(this, useFinalPosition);
  }

  draw(ctx) {
    const options = this.options;
    ctx.save();
    ctx.fillStyle = options.backgroundColor;
    setBorderStyle(ctx, options);
    drawPoint(ctx, options, this.x, this.y);
    ctx.restore();
  }

  resolveElementProperties(chart, options) {
    return resolvePointPosition(chart, options);
  }
}

PointAnnotation.id = 'pointAnnotation';

PointAnnotation.defaults = {
  adjustScaleRange: true,
  borderDash: [],
  borderDashOffset: 0,
  borderWidth: 1,
  display: true,
  pointStyle: 'circle',
  radius: 10,
  rotation: 0,
  xAdjust: 0,
  xMax: undefined,
  xMin: undefined,
  xScaleID: 'x',
  xValue: undefined,
  yAdjust: 0,
  yMax: undefined,
  yMin: undefined,
  yScaleID: 'y',
  yValue: undefined
};

PointAnnotation.defaultRoutes = {
  borderColor: 'color',
  backgroundColor: 'color'
};

class PolygonAnnotation extends Element {

  inRange(mouseX, mouseY, useFinalPosition) {
    const vertices = getVertices(this.getProps(['x', 'y'], useFinalPosition), this.options);
    return vertices && vertices.length > 0 && pointIsInPolygon(vertices, mouseX, mouseY);
  }

  getCenterPoint(useFinalPosition) {
    return getElementCenterPoint(this, useFinalPosition);
  }

  draw(ctx) {
    const {x, y, options} = this;
    const vertices = getVertices({x, y}, options);
    let vertex = vertices[0];
    ctx.save();
    ctx.beginPath();
    ctx.fillStyle = options.backgroundColor;
    const stroke = setBorderStyle(ctx, options);
    ctx.moveTo(vertex.x, vertex.y);
    for (let i = 1; i < vertices.length; i++) {
      vertex = vertices[i];
      ctx.lineTo(vertex.x, vertex.y);
    }
    ctx.closePath();
    ctx.fill();
    // If no border, don't draw it
    if (stroke) {
      ctx.stroke();
    }
    ctx.restore();
  }

  resolveElementProperties(chart, options) {
    if (isNumber(options.sides) && options.sides >= 1) {
      return resolvePointPosition(chart, options);
    }
    return {options: {}};
  }

}

PolygonAnnotation.id = 'polygonAnnotation';

PolygonAnnotation.defaults = {
  adjustScaleRange: true,
  borderCapStyle: 'butt',
  borderDash: [],
  borderDashOffset: 0,
  borderJoinStyle: 'miter',
  borderWidth: 1,
  display: true,
  radius: 10,
  rotation: 0,
  sides: 3,
  xAdjust: 0,
  xMax: undefined,
  xMin: undefined,
  xScaleID: 'x',
  xValue: undefined,
  yAdjust: 0,
  yMax: undefined,
  yMin: undefined,
  yScaleID: 'y',
  yValue: undefined
};

PolygonAnnotation.defaultRoutes = {
  borderColor: 'color',
  backgroundColor: 'color'
};

function getVertices(point, options) {
  const {sides, radius} = options;
  let angle = (2 * PI$1) / sides;
  let rad = options.rotation * RAD_PER_DEG;
  const vertices = new Array();
  addVertex(vertices, point, rad, radius);
  for (let i = 0; i < sides; i++) {
    rad += angle;
    addVertex(vertices, point, rad, radius);
  }
  return vertices;
}

function addVertex(array, point, rad, radius) {
  array.push({
    x: point.x + Math.sin(rad) * radius,
    y: point.y - Math.cos(rad) * radius
  });
}

function pointIsInPolygon(vertices, x, y) {
  let isInside = false;
  let i = 0;
  let j = vertices.length - 1;
  for (j; i < vertices.length; j = i++) {
    if ((vertices[i].y > y) !== (vertices[j].y > y) &&
         x < (vertices[j].x - vertices[i].x) * (y - vertices[i].y) / (vertices[j].y - vertices[i].y) + vertices[i].x) {
      isInside = !isInside;
    }
  }
  return isInside;
}

const annotationTypes = {
  box: BoxAnnotation,
  ellipse: EllipseAnnotation,
  label: LabelAnnotation,
  line: LineAnnotation,
  point: PointAnnotation,
  polygon: PolygonAnnotation
};

/**
 * Register fallback for annotation elements
 * For example lineAnnotation options would be looked through:
 * - the annotation object (options.plugins.annotation.annotations[id])
 * - element options (options.elements.lineAnnotation)
 * - element defaults (defaults.elements.lineAnnotation)
 * - annotation plugin defaults (defaults.plugins.annotation, this is what we are registering here)
 */
Object.keys(annotationTypes).forEach(key => {
  defaults.describe(`elements.${annotationTypes[key].id}`, {
    _fallback: 'plugins.annotation'
  });
});

var version = "1.2.2";

const chartStates = new Map();
const versionParts = Chart.version.split('.');

var annotation = {
  id: 'annotation',

  version,

  afterRegister() {
    Chart.register(annotationTypes);

    // TODO: Remove this workaround when strictly requiring Chart.js v3.7 or newer
    if (versionParts[0] === '3' && parseInt(versionParts[1], 10) <= 6) {
      // Workaround for https://github.com/chartjs/chartjs-plugin-annotation/issues/572
      Chart.defaults.set('elements.lineAnnotation', {
        callout: {},
        font: {},
        padding: 6
      });
    }
  },

  afterUnregister() {
    Chart.unregister(annotationTypes);
  },

  beforeInit(chart) {
    chartStates.set(chart, {
      annotations: [],
      elements: [],
      visibleElements: [],
      listeners: {},
      listened: false,
      moveListened: false
    });
  },

  beforeUpdate(chart, args, options) {
    const state = chartStates.get(chart);
    const annotations = state.annotations = [];

    let annotationOptions = options.annotations;
    if (isObject(annotationOptions)) {
      Object.keys(annotationOptions).forEach(key => {
        const value = annotationOptions[key];
        if (isObject(value)) {
          value.id = key;
          annotations.push(value);
        }
      });
    } else if (isArray(annotationOptions)) {
      annotations.push(...annotationOptions);
    }
    verifyScaleOptions(annotations, chart.scales);
  },

  afterDataLimits(chart, args) {
    const state = chartStates.get(chart);
    adjustScaleRange(chart, args.scale, state.annotations.filter(a => a.display && a.adjustScaleRange));
  },

  afterUpdate(chart, args, options) {
    const state = chartStates.get(chart);
    updateListeners(chart, state, options);
    updateElements(chart, state, options, args.mode);
    state.visibleElements = state.elements.filter(el => !el.skip && el.options.display);
  },

  beforeDatasetsDraw(chart, _args, options) {
    draw(chart, 'beforeDatasetsDraw', options.clip);
  },

  afterDatasetsDraw(chart, _args, options) {
    draw(chart, 'afterDatasetsDraw', options.clip);
  },

  beforeDraw(chart, _args, options) {
    draw(chart, 'beforeDraw', options.clip);
  },

  afterDraw(chart, _args, options) {
    draw(chart, 'afterDraw', options.clip);
  },

  beforeEvent(chart, args, options) {
    const state = chartStates.get(chart);
    handleEvent(state, args.event, options);
  },

  destroy(chart) {
    chartStates.delete(chart);
  },

  _getState(chart) {
    return chartStates.get(chart);
  },

  defaults: {
    animations: {
      numbers: {
        properties: ['x', 'y', 'x2', 'y2', 'width', 'height', 'pointX', 'pointY', 'labelX', 'labelY', 'labelWidth', 'labelHeight', 'radius'],
        type: 'number'
      },
    },
    clip: true,
    dblClickSpeed: 350, // ms
    drawTime: 'afterDatasetsDraw',
    label: {
      drawTime: null
    }
  },

  descriptors: {
    _indexable: false,
    _scriptable: (prop) => !hooks.includes(prop),
    annotations: {
      _allKeys: false,
      _fallback: (prop, opts) => `elements.${annotationTypes[resolveType(opts.type)].id}`,
    },
  },

  additionalOptionScopes: ['']
};

const directUpdater = {
  update: Object.assign
};

function resolveAnimations(chart, animOpts, mode) {
  if (mode === 'reset' || mode === 'none' || mode === 'resize') {
    return directUpdater;
  }
  return new Animations(chart, animOpts);
}

function resolveType(type = 'line') {
  if (annotationTypes[type]) {
    return type;
  }
  console.warn(`Unknown annotation type: '${type}', defaulting to 'line'`);
  return 'line';
}

function updateElements(chart, state, options, mode) {
  const animations = resolveAnimations(chart, options.animations, mode);

  const annotations = state.annotations;
  const elements = resyncElements(state.elements, annotations);

  for (let i = 0; i < annotations.length; i++) {
    const annotation = annotations[i];
    let el = elements[i];
    const elementClass = annotationTypes[resolveType(annotation.type)];
    if (!el || !(el instanceof elementClass)) {
      el = elements[i] = new elementClass();
    }
    const opts = resolveAnnotationOptions(annotation.setContext(getContext(chart, el, annotation)));
    const properties = el.resolveElementProperties(chart, opts);
    properties.skip = isNaN(properties.x) || isNaN(properties.y);
    properties.options = opts;
    animations.update(el, properties);
  }
}

function resolveAnnotationOptions(resolver) {
  const elementClass = annotationTypes[resolveType(resolver.type)];
  const result = {};
  result.id = resolver.id;
  result.type = resolver.type;
  result.drawTime = resolver.drawTime;
  Object.assign(result, resolveObj(resolver, elementClass.defaults), resolveObj(resolver, elementClass.defaultRoutes));
  for (const hook of hooks) {
    result[hook] = resolver[hook];
  }
  return result;
}

function resolveObj(resolver, defs) {
  const result = {};
  for (const name of Object.keys(defs)) {
    const optDefs = defs[name];
    const value = resolver[name];
    result[name] = isObject(optDefs) ? resolveObj(value, optDefs) : value;
  }
  return result;
}

function getContext(chart, element, annotation) {
  return element.$context || (element.$context = Object.assign(Object.create(chart.getContext()), {
    element,
    id: annotation.id,
    type: 'annotation'
  }));
}

function resyncElements(elements, annotations) {
  const count = annotations.length;
  const start = elements.length;

  if (start < count) {
    const add = count - start;
    elements.splice(start, 0, ...new Array(add));
  } else if (start > count) {
    elements.splice(count, start - count);
  }
  return elements;
}

function draw(chart, caller, clip) {
  const {ctx, chartArea} = chart;
  const state = chartStates.get(chart);

  if (clip) {
    clipArea(ctx, chartArea);
  }
  state.visibleElements.forEach(el => {
    if (el.options.drawTime === caller) {
      el.draw(ctx);
    }
  });
  if (clip) {
    unclipArea(ctx);
  }

  state.visibleElements.forEach(el => {
    if (!('drawLabel' in el)) {
      return;
    }
    const label = el.options.label;
    if (label && label.enabled && label.content && (label.drawTime || el.options.drawTime) === caller) {
      el.drawLabel(ctx, chartArea);
    }
  });
}

export { annotation as default };


text:
#import <Foundation/Foundation.h>
#import "LVHeads.h"

//LVData
@interface LVData : NSObject<LVProtocal, LVClassProtocal>

@property(nonatomic,weak) LuaViewCore* lv_luaviewCore;
@property(nonatomic,assign) LVUserDataInfo* lv_userData;

@property(nonatomic,strong) NSMutableData* data;//真实的数据

-(id) init:(lua_State*) l;
+(int) lvClassDefine:(lua_State *)L globalName:(NSString*) globalName;

+(int) createDataObject:(lua_State *)L  data:(NSData*) data;

@end


text:
package com.openmhealth.ohmage.campaigns.va.ptsd_explorer;

import com.openmhealth.ohmage.core.EventRecord;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class ContentObjectSelectedEvent extends EventRecord {
	public String contentObjectName;
	public String contentObjectDisplayName;
	public String contentObjectId;

	public ContentObjectSelectedEvent() {
		super(8);
	}

	public String ohmageSurveyID() {
	    return "contentObjectSelectedProbe";
	}

	public void toMap(Map<String,Object> into) {
		into.put("contentObjectName",contentObjectName);
		into.put("contentObjectDisplayName",contentObjectDisplayName);
		into.put("contentObjectId",contentObjectId);
	}

	public void addAttributesToOhmageJSON(JSONArray into) {
		try {
			JSONObject obj = new JSONObject();
			obj.put("prompt_id","contentObjectName");
			obj.put("value",(contentObjectName!=null) ? contentObjectName : "NONE");
			into.put(obj);
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		try {
			JSONObject obj = new JSONObject();
			obj.put("prompt_id","contentObjectDisplayName");
			obj.put("value",(contentObjectDisplayName!=null) ? contentObjectDisplayName : "NONE");
			into.put(obj);
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		try {
			JSONObject obj = new JSONObject();
			obj.put("prompt_id","contentObjectId");
			obj.put("value",(contentObjectId!=null) ? contentObjectId : "NONE");
			into.put(obj);
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}