local revision = tonumber(("$Revision: 25450 $"):sub(12, -3))
if revision > Cartographer.revision then
	Cartographer.version = "r" .. revision
	Cartographer.revision = revision
	Cartographer.date = ("$Date: 2007-01-17 11:42:16 -0500 (Wed, 17 Jan 2007) $"):sub(8, 17)
end

local L = AceLibrary("AceLocale-2.2"):new("Cartographer-Notes")

L:RegisterTranslations("enUS", function() return {
	["Notes"] = true,
	["Module which allows you to put notes on the map."] = true,
	["Hint: /note is available to create notes with the command line."] = true,
	
	["White"] = true,
	["Gray"] = true,
	["Red"] = true,
	["Pink"] = true,
	["Yellow"] = true,
	["Orange"] = true,
	["Green"] = true,
	["Lime"] = true,
	["Blue"] = true,
	["Pale blue"] = true,
	["Cyan"] = true,
	["Purple"] = true,
	
	["Unknown"] = true,
	["Custom icon"] = true,
	
	["Create a new note"] = true,
	["Create a new note for %q"] = true,
	["New note"] = true,
	["Delete note"] = true,
	["Edit note"] = true,
	["Send note"] = true,
	["Send to party"] = true,
	["Send to raid"] = true,
	["Send to guild"] = true,
	["Send to player"] = true,
	
	["Note: This may be blocked by Blizzard's spam filter and cause problems. It is recommended to send to your party/raid/guild instead."] = true,
	
	["X position"] = true,
	["Y position"] = true,
	["Title"] = true,
	["Info line 1 (optional)"] = true,
	["Info line 2 (optional)"] = true,
	["Creator (optional)"] = true,
	
	["Ctrl-Right-Click on map to add a note"] = true,
	
	["%s from Guild"] = true,
	["%s from Raid"] = true,
	["%s from Party"] = true,
	
	["Error: %s sent you a note with an unknown zone: %q"] = true,
	["Error: %s sent you a note at %q : %.2f, %.2f with the title %q created by %q, but you already have a note at this location."] = true,
	["%s sent you a note at %q : %.2f, %.2f with the title %q created by %q."] = true,

	["Created by"] = true,
	["Meters away:"] = true,
		
	["Show note creator"] = true,
	["Show the `Created By:' line in notes"] = true,
	
	["Icon size"] = true,
	["Size of the icons on the map"] = true,
	
	["Show minimap icons"] = true,
	["Show icons on the minimap"] = true,
	
	["Minimap icon size"] = true,
	["Size of the icons on the minimap"] = true,
	
	["Max minimap icons per database"] = true,
	["The maximum amount of minimap icons to show per database"] = true,
	
	["Console-note-commands"] = { "/note", "/addnote", "/tloc" },
	["Usage: /note <0-100> <0-100> [title]"] = true,

	["Show chat notifications"] = true,
	["Show notification in the chat window"] = true,
	-- Note cleanup localizations
	["Clear all notes on map"] = true,
	["This will remove all notes on the map. This can't be undone are your sure?"] = true,
	["This will clear all notes on map"] = true,
	
} end)

L:RegisterTranslations("koKR", function() return {
	["Notes"] = "노트",
	["Module which allows you to put notes on the map."] = "지도에 노트를 작성합니다.",
	["Hint: /note is available to create notes with the command line."] = "힌트: 명령어로 신규 노트를 만드는데 '/note'를 이용하세요.",
	
	["White"] = "흰색",
	["Gray"] = "회색",
	["Red"] = "빨강색",
	["Pink"] = "분홍색",
	["Yellow"] = "노랑색",
	["Orange"] = "주황색",
	["Green"] = "녹색",
	["Lime"] = "라임색",
	["Blue"] = "파랑색",
	["Pale blue"] = "연한 파랑색",
	["Cyan"] = "청색",
	["Purple"] = "심홍색",

	["Unknown"] = "알수없음",
	["Custom icon"] = "사용자 아이콘",
	
	["Create a new note"] = "노트 작성",
	["Create a new note for %q"] = "새로운 노트 작성 %q",
	["New note"] = "신규 노트",
	["Delete note"] = "노트 삭제",
	["Edit note"] = "노트 편집",
	["Send note"] = "노트 발송",
	["Send to party"] = "파티에 보냄",
	["Send to raid"] = "공격대에 보냄",
	["Send to guild"] = "길드에 보냄",
	["Send to player"] = "플레이어에게 보냄",
	
	["Note: This may be blocked by Blizzard's spam filter and cause problems. It is recommended to send to your party/raid/guild instead."] = "노트: 이것은 블리자드의 스팸 필터 기능에 의해 문제가 발생할 수 있습니다. 당신의 파티/공격대/길드에 보내는걸 권장합니다.",
	
	["X position"] = "X 위치",
	["Y position"] = "Y 위치",
	["Title"] = "제목",
	["Info line 1 (optional)"] = "정보 라인 1 (선택)",
	["Info line 2 (optional)"] = "정보 라인 2 (선택)",
	["Creator (optional)"] = "신규 (선택)",
	
	["Ctrl-Right-Click on map to add a note"] = "Ctrl-오른쪽-클릭 : 맵 노트 추가",
	
	["%s from Guild"] = "길드로부터 %s 받음",
	["%s from Raid"] = "공격대로부터 %s 받음",
	["%s from Party"] = "파티로부터 %s 받음",
	
	["Error: %s sent you a note with an unknown zone: %q"] = "오류: %s|1이;가; 알려지지 않은 지역 노트를 보냈습니다: %q",
--	["Error: %s sent you a note at %q : %.2f, %.2f with the title %q created by %q, but you already have a note at this location."] = true,
--	["%s sent you a note at %q : %.2f, %.2f with the title %q created by %q."] = true,

	["Created by"] = "작성자",
	["Meters away:"] = "떨어진 거리:",
	
	["Show note creator"] = "작성자 보기",
	["Show the `Created By:' line in notes"] = "노트에 '작성자:' 를 표시합니다.",

	["Icon size"] = "아이콘 크기",
	["Size of the icons on the map"] = "노트의 아이콘 크기를 변경합니다.",
	
	["Show minimap icons"] = "미니맵 아이콘 표시",
	["Show icons on the minimap"] = "미니맵에 아이콘을 표시합니다.",
	
	["Minimap icon size"] = "미니맵 아이콘 크기",
	["Size of the icons on the minimap"] = "미니맵의 아이콘 크기를 변경합니다.",
	
	["Max minimap icons per database"] = "데이터베이스당 최고 미니맵 아이콘",
	["The maximum amount of minimap icons to show per database"] = "데이터베이스당 표시할 최고 미니맵 아이콘",


	
--	["Console-note-commands"] = { "/note", "/addnote", "/tloc" },
--	["Usage: /note <0-100> <0-100> [title]"] = true,

	["Show chat notifications"] = "대화창 통보 표시",
	["Show notification in the chat window"] = "대화창에 통보를 표시하여줍니다.",
	-- Note cleanup localizations
	["Clear all notes on map"] = "모든 노트 메모 지움",
	["This will remove all notes on the map. This can't be undone are your sure?"] = "지도 위의 모든 노트를 제거합니다. 이것은 원상태로 복구할 수 없습니다?",
	["This will clear all notes on map"] = "지도 위의 모든 노트를 지웁니다.",

} end)

L:RegisterTranslations("deDE", function() return {
	["Notes"] = "Notizen",
	["Module which allows you to put notes on the map."] = "Modul zur Erstellung von Notizen auf der Karte.",
	["Hint: /note is available to create notes with the command line."] = "Hinweis: verwende /note zur Erzeugung von Notizen auf der Eingabezeile.",
	
	["White"] = "Weiß",
	["Gray"] = "Grau",
	["Red"] = "Rot",
	["Pink"] = "Pink",
	["Yellow"] = "Gelb",
	["Orange"] = "Orange",
	["Green"] = "Grün",
	["Lime"] = "Kalk",
	["Blue"] = "Blau",
	["Pale blue"] = "Hellblau",
	["Cyan"] = "Türkis",
	["Purple"] = "Violett",
	
	["Unknown"] = "Unbekannt",
	["Custom icon"] = "Benutzerdefiniertes Symbol",
	
	["Create a new note"] = "Neue Notiz erstellen",
	["Create a new note for %q"] = "Erstelle neue Notiz für %q",
	["New note"] = "Neue Notiz",
	["Delete note"] = "Notiz löschen",
	["Edit note"] = "Notiz bearbeiten",
	["Send note"] = "Notiz senden",
	["Send to party"] = "An Gruppe senden",
	["Send to raid"] = "An Schlachtzug senden",
	["Send to guild"] = "An Gilde senden",
	["Send to player"] = "An Spieler senden",
	
	["Note: This may be blocked by Blizzard's spam filter and cause problems. It is recommended to send to your party/raid/guild instead."] = "Hinweis: Dies könnte durch Blizzards Spamfilter blockiert werden und Probleme verursachen. Es ist stattdessen zu empfehlen, nur an Gruppe, Schlachtzug oder Gilde zu senden.",
	
	["X position"] = "X-Position",
	["Y position"] = "Y-Position",
	["Title"] = "Titel",
	["Info line 1 (optional)"] = "Infozeile 1 (optional)",
	["Info line 2 (optional)"] = "Infozeile 2 (optional)",
	["Creator (optional)"] = "Ersteller (optional)",
	
	["Ctrl-Right-Click on map to add a note"] = "Strg-Rechtsklick auf die Karte fügt eine Notiz ein",
	
	["%s from Guild"] = "%s von Gilde",
	["%s from Raid"] = "%s von Schlachtgruppe",
	["%s from Party"] = "%s von Gruppe",
	
	["Error: %s sent you a note with an unknown zone: %q"] = "Fehler: %s sandte Dir eine Notiz mit einer unbekannten Zone: %q",
	["Error: %s sent you a note at %q : %.2f, %.2f with the title %q created by %q, but you already have a note at this location."] = "Fehler: %s sandte Dir eine Notiz an Position %q : %.2f, %.2f mit dem Titel %q erstellt von %q, aber du hast schon eine Notiz an dieser Position.",
	["%s sent you a note at %q : %.2f, %.2f with the title %q created by %q."] = "%s sandte Dir eine Notiz an %q : %.2f, %.2f mit dem Titel %q erstellt von %q.",

	["Created by"] = "Erstellt von",
	["Meters away:"] = "Meter entfernt:",
	
	["Show note creator"] = "Notizersteller anzeigen",
	["Show the `Created By:' line in notes"] = "Zeigt die Zeile 'Erstellt von:' in den Notizen.",
	
	["Icon size"] = "Symbolgröße",
	["Size of the icons on the map"] = "Größe der Symbole auf der Karte",
	
	["Show minimap icons"] = "Zeige Minimapsymbole",
	["Show icons on the minimap"] = "Zeigt die Symbole auf der Minimap an",

	["Minimap icon size"] = "Symbolgröße auf der Minimap",
	["Size of the icons on the minimap"] = "Größe der Symbole auf der Minimap.",
	
  ["Max minimap icons per database"] = "Maximalzahl an Symbolen pro Datenbank",
	["The maximum amount of minimap icons to show per database"] = "Die Maximalzahl an Minimap-Symbolen, die pro Datenbank angezeigt werden.",
	
  ["Console-note-commands"] = { "/note", "/addnote", "/tloc" },
	["Usage: /note <0-100> <0-100> [title]"] = "Benutzung: /note <0-100> <0-100> [titel]",
	
	["Show chat notifications"] = "Zeige Chat Benachrichtigungen",
	["Show notification in the chat window"] = "Zeige Benachrichtigungen im Chatfenster",
	-- Note cleanup localizations
	["Clear all notes on map"] = "Entferne alle Notizen von der Karte",
	["This will remove all notes on the map. This can't be undone are your sure?"] = "Dies wird alle Notizen von der Karte entfernen. Dies kann NICHT rückgängig gemacht werden. Bist du sicher, dass Du dies tun willst?",
	["This will clear all notes on map"] = "Dies wird alle Notizen von der Karte entfernen",
	
} end)

L:RegisterTranslations("frFR", function() return {
	--["Notes"] = true,
	["Module which allows you to put notes on the map."] = "Module permettant l'ajout de notes sur la carte.",
	["Hint: /note is available to create notes with the command line."] = "Astuce : la commande /note est un autre moyen de créer des notes avec la ligne de commande.",

	["White"] = "Blanc",
	["Gray"] = "Gris",
	["Red"] = "Rouge",
	["Pink"] = "Rose",
	["Yellow"] = "Jaune",
	--["Orange"] = true,
	["Green"] = "Vert",
	["Lime"] = "Jaune citron",
	["Blue"] = "Bleu",
	["Pale blue"] = "Bleu pâle",
	--["Cyan"] = true,
	["Purple"] = "Violet",

	["Unknown"] = "Inconnu",
	["Custom icon"] = "Icône perso",

	["Create a new note"] = "Créer une nouvelle note",
	["Create a new note for %q"] = "Créer une nouvelle note pour %q",
	["New note"] = "Nouvelle note",
	["Delete note"] = "Supprimer la note",
	["Edit note"] = "Éditer la note",
	["Send note"] = "Envoyer la note",
	["Send to party"] = "Envoyer au groupe",
	["Send to raid"] = "Envoyer au raid",
	["Send to guild"] = "Envoyer à la guilde",
	["Send to player"] = "Envoyer au joueur",

	["Note: This may be blocked by Blizzard's spam filter and cause problems. It is recommended to send to your party/raid/guild instead."] = "Note : Ceci risque d'être bloqué par le filtre anti-spam de Blizzard et de poser problème. Il est recommandé d'envoyer vers votre groupe/raid/guilde à la place.",

	["X position"] = "Position X",
	["Y position"] = "Position Y",
	["Title"] = "Titre",
	["Info line 1 (optional)"] = "Ligne d'infos 1 (optionnel)",
	["Info line 2 (optional)"] = "Ligne d'infos 2 (optionnel)",
	["Creator (optional)"] = "Créateur (optionnel)",

	["Ctrl-Right-Click on map to add a note"] = "Ctrl-clic-droit sur la carte pour ajouter une note",

	["%s from Guild"] = "%s du canal Guilde",
	["%s from Raid"] = "%s du canal Raid",
	["%s from Party"] = "%s du canal Groupe",

	["Error: %s sent you a note with an unknown zone: %q"] = "Erreur : %s vous a envoyé une note d'une zone inconnue : %q",
	["Error: %s sent you a note at %q : %.2f, %.2f with the title %q created by %q, but you already have a note at this location."] = "Erreur : %s vous a envoyé une note à %q : %.2f, %.2f avec le titre %q et créée par %q, mais vous avez déjà une note à cet endroit.",
	["%s sent you a note at %q : %.2f, %.2f with the title %q created by %q."] = "%s vous a envoyé une note à %q : %.2f, %.2f avec le titre %q et créée par %q.",

	["Created by"] = "Créée par",
	["Meters away:"] = "Distance en mètres :",

	["Show note creator"] = "Afficher le créateur de la note",
	["Show the `Created By:' line in notes"] = "Affiche la ligne 'Créée par :  sur les notes.",

	["Icon size"] = "Taille des icônes",
	["Size of the icons on the map"] = "Détermine la taille des icônes sur la carte.",

	["Show minimap icons"] = "Afficher les icônes de la minicarte",
	["Show icons on the minimap"] = "Affiche les icônes sur la minicarte.",

	["Minimap icon size"] = "Taille des icônes de la minicarte",
	["Size of the icons on the minimap"] = "Détermine la taille des icônes de la minicarte.",

	["Max minimap icons per database"] = "Icônes minicarte max. par base de données",
	["The maximum amount of minimap icons to show per database"] = "Détermine le nombre maximal d'icônes à afficher sur la minicarte par base de données.",

	["Console-note-commands"] = { "/note", "/addnote", "/tloc" },
	["Usage: /note <0-100> <0-100> [title]"] = "Utilisation : /note <0-100> <0-100> [titre]",

	["Show chat notifications"] = "Afficher les notifications",
	["Show notification in the chat window"] = "Affiche les notifications dans la fenêtre de discussion.",

	["Clear all notes on map"] = "Effacer toutes les notes de la carte",
	["This will remove all notes on the map. This can't be undone are your sure?"] = "Ceci supprimera toutes les notes de la carte et est irréversible. Êtes-vous sûr ?",
	["This will clear all notes on map"] = "Efface toutes les notes apposées sur la carte.",
} end)

L:RegisterTranslations("esES", function() return {
	["Notes"] = "Notas",
	["Module which allows you to put notes on the map."] = "M\195\179dulo que te permite poner notas en el mapa",
	["Hint: /note is available to create notes with the command line."] = "Consejo: /note est\195\161 disponible para crear notas con la l\195\173nea de comandos",
	
	["White"] = "Blanco",
	["Gray"] = "Gris",
	["Red"] = "Rojo",
	["Pink"] = "Rosa",
	["Yellow"] = "Amarillo",
	["Orange"] = "Naranja",
	["Green"] = "Verde",
	["Lime"] = "Lima",
	["Blue"] = "Azul",
	["Pale blue"] = "Azul cielo",
	["Cyan"] = "Ci\195\161n",
	["Purple"] = "P\195\186rpura",
	
	["Unknown"] = "Desconocido",
	["Custom icon"] = "Icono personalizado",
	
	["Create a new note"] = "Crear una nueva nota",
	["Create a new note for %q"] = "Crear una nueva nota para %q",
	["New note"] = "Nueva nota",
	["Delete note"] = "Eliminar nota",
	["Edit note"] = "Editar nota",
	["Send note"] = "Enviar nota",
	["Send to party"] = "Enviar al grupo",
	["Send to raid"] = "Enviar a la banda",
	["Send to guild"] = "Enviar a la hermandad",
	["Send to player"] = "Enviar a jugador",
	
	["Note: This may be blocked by Blizzard's spam filter and cause problems. It is recommended to send to your party/raid/guild instead."] = "Nota: Esto puede estar bloqueado por el filtro de spam de Blizzard y causar problemas. Es preferible enviarlo a tu grupo/banda/hermandad",
	
	["X position"] = "Posici\195\179n Horizontal",
	["Y position"] = "Posici\195\179n Vertical",
	["Title"] = "T\195\173tulo",
	["Info line 1 (optional)"] = "L\195\173nea de info 1 (opcional)",
	["Info line 2 (optional)"] = "L\195\173nea de info 2 (opcional)",
	["Creator (optional)"] = "Creador (opcional)",
	
	["Ctrl-Right-Click on map to add a note"] = "Ctrl+ClicDerecho en el mapa para a\195\177adir una nota",
	
	["%s from Guild"] = "%s de la Hermandad",
	["%s from Raid"] = "%s de la Banda",
	["%s from Party"] = "%s del Grupo",
	
	["Error: %s sent you a note with an unknown zone: %q"] = "Error: %s te ha enviado una nota con una zona desconocida: %q",
	["Error: %s sent you a note at %q : %.2f, %.2f with the title %q created by %q, but you already have a note at this location."] = "Error: %s te ha enviado unanota en %q: %.2f, %.2f con el t\195\173tulo %q creada por %q, pero ya tienes otra nota en este lugar.",
	["%s sent you a note at %q : %.2f, %.2f with the title %q created by %q."] = "%s te ha enviado una note en %q : %.2f, %.2f con el t\195\173tulo %q creada por %q.",

	["Created by"] = "Creada por",
	["Meters away:"] = "Distancia en metros:",
	
	["Show note creator"] = "Mostrar el creador de la nota",
	["Show the `Created By:' line in notes"] = "Muestra la l\195\173nea 'Creado por:' en las notas",
	
	["Icon size"] = "Tama\195\177o del icono",
	["Size of the icons on the map"] = "Tama\195\177o de los iconos en el mapa",
	
	["Show minimap icons"] = "Mostrar iconos del minimapa",
	["Show icons on the minimap"] = "Muestra los iconos en el minimapa",
	
	["Minimap icon size"] = "Tama\195\177o de iconos del minimapa",
	["Size of the icons on the minimap"] = "Tama\195\177o de los iconos en el minimapa",
	
	["Max minimap icons per database"] = "M\195\161x. de iconos de minimapa por base de datos",
	["The maximum amount of minimap icons to show per database"] = "La cantidad m\195\161xima de iconos en el minimapa a mostrar por base de datos",
	
	["Console-note-commands"] = { "/note", "/addnote", "/tloc" },
	["Usage: /note <0-100> <0-100> [title]"] = "Uso: /note <0-100> <0-100> [t\195\173tulo]",
	
	["Show chat notifications"] = "Mostrar notificaciones en chat",
	["Show notification in the chat window"] = "Muestra las notificaciones en la ventana de chat",
	-- Note cleanup localizations
	["Clear all notes on map"] = "Borrar todas las notas del mapa",
	["This will remove all notes on the map. This can't be undone are your sure?"] = "Esto eliminar\195\161 todas las notas del mapa. No se podr\195\161 deshacer, \194\191est\195\161s seguro?",
	["This will clear all notes on map"] = "Eliminar\195\161 todas las notas del mapa",

} end)

L:RegisterTranslations("zhTW", function() return {
	["Notes"] = "註記",
	["Module which allows you to put notes on the map."] = "讓你可以在地圖上做註記的模組。",
	["Hint: /note is available to create notes with the command line."] = "提示: 可以使用 /note 指令新增註記。",
	
	["White"] = "白色",
	["Gray"] = "灰色",
	["Red"] = "紅色",
	["Pink"] = "粉紅",
	["Yellow"] = "黃色",
	["Orange"] = "橘色",
	["Green"] = "綠色",
	["Lime"] = "萊姆",
	["Blue"] = "藍色",
	["Pale blue"] = "淡藍",
	["Cyan"] = "靛青",
	["Purple"] = "紫色",
	
	["Unknown"] = "未知",
	["Custom icon"] = "自訂圖示",
	
	["Create a new note"] = "新增註記",
	["Create a new note for %q"] = "為 %q 新增註記",
	["New note"] = "新註記",
	["Delete note"] = "刪除註記",
	["Edit note"] = "編輯註記",
	["Send note"] = "傳送註記",
	["Send to party"] = "傳送到小隊",
	["Send to raid"] = "傳送到團隊",
	["Send to guild"] = "傳送到公會",
	["Send to player"] = "傳送到玩家",
	
	["Note: This may be blocked by Blizzard's spam filter and cause problems. It is recommended to send to your party/raid/guild instead."] = "註: 這有可能被 Blizzard 內建的過濾器擋掉並造成問題。建議您使用傳送到您的小隊/團隊/公會來代替。",
	
	["X position"] = "X位置",
	["Y position"] = "Y位置",
	["Title"] = "標題",
	["Info line 1 (optional)"] = "訊息行1(可選)",
	["Info line 2 (optional)"] = "訊息行2(可選)",
	["Creator (optional)"] = "新增人(可選)",
	
	["Ctrl-Right-Click on map to add a note"] = "Ctrl-右鍵點擊地圖新增註記",
	
	["%s from Guild"] = "來自公會:%s",
	["%s from Raid"] = "來自團隊:%s",
	["%s from Party"] = "來自小隊:%s",
	
	["Error: %s sent you a note with an unknown zone: %q"] = "錯誤: %s傳送給你一個未知地區(%q)的註記",
	["Error: %s sent you a note at %q : %.2f, %.2f with the title %q created by %q, but you already have a note at this location."] = "錯誤: %s在%q : %.2f, %.2f傳送給你一個註記，標題:%q，新增人:%q，但你在同樣位置已經有註記了。",
	["%s sent you a note at %q : %.2f, %.2f with the title %q created by %q."] = "%s在%q : %.2f, %.2f傳送給你一個註記，標題:%q，新增人:%q。",

	["Created by"] = "新增人:",
--	["Meters away:"] = true,
	
	["Show note creator"] = "顯示註記新增人",
	["Show the `Created By:' line in notes"] = "在註記中顯示`新增人:'",
	
	["Icon size"] = "圖示大小",
	["Size of the icons on the map"] = "顯示在地圖上的圖示大小",
	
	["Show minimap icons"] = "顯示小地圖圖示",
	["Show icons on the minimap"] = "在小地圖上顯示圖示",
	
	["Minimap icon size"] = "小地圖圖示大小",
	["Size of the icons on the minimap"] = "小地圖圖示的大小",
	
	--["Max minimap icons per database"] = true,
	--["The maximum amount of minimap icons to show per database"] = true,
	
	--["Console-note-commands"] = { "/note", "/addnote", "/tloc" },
	["Usage: /note <0-100> <0-100> [title]"] = "用法: /note <0-100> <0-100> [標題]",

--	["Show chat notifications"] = true,
--	["Show notification in the chat window"] = true,

} end)

Cartographer_Notes = Cartographer:NewModule("Notes", "AceHook-2.1", "AceEvent-2.0", "AceComm-2.0", "AceConsole-2.0")
local self = Cartographer_Notes
-- Added by Sal Scotto
local setNoteFromComm = false
-- End addition
local Dewdrop = AceLibrary("Dewdrop-2.0")
local Tablet = AceLibrary("Tablet-2.0")
local Tourist = AceLibrary("Tourist-2.0")
local BZ = AceLibrary("Babble-Zone-2.2")

local _G = getfenv(0)
local metric = not LOCALE_enUS

local CURRENT_DB_VERSION = 3

local icons = {}
local function getIconTitle(icon)
	if icons[icon] then
		return icons[icon].text
	elseif icon:find("^Interface\\") then
		return L["Custom icon"]
	else
		return icon
	end
end

local x, y
local function getCurrentPlayerPosition()
	if GetRealZoneText() == Cartographer:GetCurrentLocalizedZoneName() then
		x, y = GetPlayerMapPosition("player")
	end
	return x, y
end

local isInInstance
local currentYardWidth, currentYardHeight
local function yardify(x,y)
	if x then
		return x * currentYardWidth, y * currentYardHeight
	end
end

local forceNextMinimapUpdate = false

local math_floor = math.floor
local function round(num, digits)
	-- banker's rounding
	local mantissa = 10^digits
	local norm = num*mantissa
	norm = norm + 0.5
	local norm_f = math_floor(norm)
	if norm == norm_f and (norm_f % 2) ~= 0 then
		return (norm_f-1)/mantissa
	end
	return norm_f/mantissa
end

local function GetCursorMapLocation(button)
	local x, y = GetCursorPosition()
	local left, top = button:GetLeft(), button:GetTop()
	local width = button:GetWidth()
	local height = button:GetHeight()
	local scale = button:GetEffectiveScale()
	
	return (x/scale - left) / width, (top - y/scale) / height
end

local function getID(x, y)
	return round(x*10000, 0) + round(y*10000, 0)*10001
end

local function getXY(id)
	return (id % 10001)/10000, math_floor(id / 10001)/10000
end

local function getColorID(r, g, b)
	r = round(r*255, 0)
	g = round(g*255, 0)
	b = round(b*255, 0)
	if r > 255 then
		r = 255
	elseif r < 0 then
		r = 0
	end
	if g > 255 then
		g = 255
	elseif g < 0 then
		g = 0
	end
	if b > 255 then
		b = 255
	elseif b < 0 then
		b = 0
	end
	return r*65536 + g*256 + b
end
local whiteColorID = getColorID(1, 1, 1)

local function getRGB(id)
	if not id then
		return 1, 1, 1
	end
	local r = math_floor(id/65536)/255
	local g = (math_floor(id/256) % 256)/255	
	local b = (id % 256)/255
	return r, g, b
end

local upgradeDatabase
do
	local tmp
	function upgradeDatabase(db)
		if next(db) == nil then
			db.version = CURRENT_DB_VERSION
			return
		end
		if (db.version or 1) == 1 then
			local function idConvert(id)
				if type(id) == "number" then
					return (id % 1001)*10 + math.floor(id / 1001)*100010
				end
				return id
			end
			
			if not tmp then
				tmp = {}
			end
			for name, zone in pairs(db) do
				if type(zone) == "table" then
					for id, data in pairs(zone) do
						tmp[idConvert(id)] = data
						zone[id] = nil
					end
					for id, data in pairs(tmp) do
						zone[id] = data
						tmp[id] = nil
					end
				end
			end
			db.version = 2
		end
		if db.version == 2 then
			for name, zone in pairs(db) do
				if type(zone) == "table" then
					for id, data in pairs(zone) do
						if type(data) == "table" then
							local r, g, b = data.titleR or 1, data.titleG or 1, data.titleB or 1
							data.titleR, data.titleB, data.titleG = nil, nil, nil
							local colorID = getColorID(r, g, b)
							if colorID ~= whiteColorID then
								data.titleCol = colorID
							end
							r, g, b = data.infoR or 1, data.infoG or 1, data.infoB or 1
							data.infoR, data.infoB, data.infoG = nil, nil, nil
							colorID = getColorID(r, g, b)
							if colorID ~= whiteColorID then
								data.infoCol = colorID
							end
							r, g, b = data.info2R or 1, data.info2G or 1, data.info2B or 1
							data.info2R, data.info2B, data.info2G = nil, nil, nil
							colorID = getColorID(r, g, b)
							if colorID ~= whiteColorID then
								data.info2Col = colorID
							end
						end
					end
				end
			end
			db.version = 3
		end
	end
end

local function getrawpoi(zone, id, creator)
	if creator then
		local v = self.externalDBs[creator]
		if v and rawget(v, zone) and rawget(v[zone], id) then
			return v[zone][id], creator
		end
		return
	end
	for k,v in pairs(self.externalDBs) do
		if rawget(v, zone) and rawget(v[zone], id) then
			return v[zone][id], k
		end
	end
	if rawget(Cartographer_Notes.db.account.pois, zone) then
		local t = rawget(Cartographer_Notes.db.account.pois[zone], id)
		if t then
			return t, nil
		end
	end
end

local function getpoi(zone, id, creator)
	if creator then
		local v = self.externalDBs[creator]
		if v and v[zone] and v[zone][id] then
			return v[zone][id], creator
		end
		return
	end
	for k,v in pairs(self.externalDBs) do
		if v[zone] and v[zone][id] then
			return v[zone][id], k
		end
	end
	local t = Cartographer_Notes.db.account.pois[zone][id]
	return t, nil
end

function Cartographer_Notes:RegisterIcon(name, data)
	AceLibrary.argCheck(self, name, 2, "string")
	AceLibrary.argCheck(self, data, 3, "table")
	if icons[name] then
		error(("Icon %q already registered"):format(name), 2)
	end
	if type(data.text) ~= "string" then
		error("text in data table must be a string", 2)
	end
	if type(data.path) ~= "string" then
		error("path in data table must be a string", 2)
	end
	if not data.cLeft then
		if data.path:find("^Interface\\Icons\\") then
			data.cLeft = 0.05
			data.cRight = 0.95
			data.cTop = 0.05
			data.cBottom = 0.95
		else
			data.cLeft = 0
			data.cRight = 1
			data.cTop = 0
			data.cBottom = 1
		end
	end
	if not data.alpha then
		data.alpha = 1
	end
	if not data.width then
		data.width = 16
	end
	if not data.height then
		data.height = 16
	end
	icons[name] = data
	self:RefreshMap(false)
end

-- support for icon remapping
local icon_cache = {}

function Cartographer_Notes:GetIconList()
	local list = {}
	for k,v in pairs(icons) do
		local t = next(icon_cache) or {}
		icon_cache[t] = nil
		t.name = v.text
		t.path = v.path
		list[k] = t
	end
	return list
end
function Cartographer_Notes:OverrideIconGraphic(name,newPath)
	AceLibrary.argCheck(self,name,2,"string")
	AceLibrary.argCheck(self,newPath,3,"string")
	
	if not icons[name] then
		error(("Icon %q not registered"):format(name),2)
	end
	local oldPath = icons[name].path
	icons[name].path = newPath
	return oldPath
end
-- Added a way to see if an icon was registered
function Cartographer_Notes:IsIconRegistered(name)
	AceLibrary.argCheck(self,name,2,"string")
	return icons[name] and true
end

function Cartographer_Notes:UnregisterIcon(name)
	AceLibrary.argCheck(self, name, 2, "string")
	if not icons[name] then
		error(("Icon %q not registered"):format(name), 2)
	end
	icons[name] = nil
	self:RefreshMap(false)
end

function Cartographer_Notes:OnInitialize()
	self:SetCommPrefix("Cartographer-Notes")
	self.externalDBs = {}
	self.handlers = {}
	local memoizations = {
		"NOTE", "title", "titleR", "titleG", "titleB", "info", "infoR", "infoG", "infoB", "info2", "info2R", "info2G", "info2B", "creator", "manual", "icon",
		-- icon names
		"Star", "Circle", "Diamond", "Triangle", "Moon", "Square", "Cross", "Skull",
	}
	for zone in BZ:GetIterator() do
		table.insert(memoizations, zone)
	end
	self:RegisterMemoizations(memoizations)
	memoizations = nil
	
	do
		local x = { "Star", "Circle", "Diamond", "Triangle", "Moon", "Square", "Cross", "Skull" }
		
		for i = 1, 8 do
			local t = UnitPopupButtons["RAID_TARGET_" .. i]
			self:RegisterIcon(x[i], {
				text = t.text,
				r = t.color.r,
				g = t.color.g,
				b = t.color.b,
				path = "Interface\\TargetingFrame\\UI-RaidTargetingIcons",
				cLeft = t.tCoordLeft,
				cRight = t.tCoordRight,
				cTop = t.tCoordTop,
				cBottom = t.tCoordBottom,
				showToUser = true,
			})
		end
		
		self:RegisterIcon("Unknown", {
			text = L["Unknown"],
			path = "Interface\\Icons\\INV_Misc_QuestionMark",
		})
	end
	
	self.db = Cartographer:AcquireDBNamespace("Notes")
	Cartographer:RegisterDefaults("Notes", "account", {
		pois = {
			['*'] = { -- zones
				['*'] = {
					creator = "",
					manual = true,
				}
			}
		}
	})
	Cartographer:RegisterDefaults("Notes", "profile", {
		showCreator = true,
		showYardsAway = true,
		iconSize = 1,
		minimapIconSize = 0.65,
		showMinimapIcons = true,
		notesPerDB = 25,
	})
	upgradeDatabase(self.db.account.pois)
	
	Cartographer.options.args.Notes = {
		name = L["Notes"],
		desc = L["Module which allows you to put notes on the map."] .. "\n\n" .. L["Hint: /note is available to create notes with the command line."],
		type = 'group',
		args = {
			showCreator = {
				name = L["Show note creator"],
				desc = L["Show the `Created By:' line in notes"],
				type = 'toggle',
				get = "IsShowingCreator",
				set = "ToggleShowingCreator",
			},
			size = {
				name = L["Icon size"],
				desc = L["Size of the icons on the map"],
				type = 'range',
				min = 0.5,
				max = 2,
				step = 0.05,
				isPercent = true,
				get = "GetIconSize",
				set = "SetIconSize",
			},
			minimapIcons = {
				name = L["Show minimap icons"],
				desc = L["Show icons on the minimap"],
				type = 'toggle',
				get = "IsShowingMinimapIcons",
				set = "ToggleShowingMinimapIcons",
			},
			minimapSize = {
				name = L["Minimap icon size"],
				desc = L["Size of the icons on the minimap"],
				type = 'range',
				min = 0.5,
				max = 2,
				step = 0.05,
				isPercent = true,
				get = "GetMinimapIconSize",
				set = "SetMinimapIconSize",
			},
			minimapNotesPerDB = {
				name = L["Max minimap icons per database"],
				desc = L["The maximum amount of minimap icons to show per database"],
				type = 'range',
				min = 5,
				max = 100,
				step = 5,
				get = "GetMinimapNotesPerDB",
				set = "SetMinimapNotesPerDB",
			},
			notifications = {
				name = L["Show chat notifications"],
				desc = L["Show notification in the chat window"],
				type = 'toggle',
				get = "IsShowingNotifications",
				set = "ToggleShowingNotifications",						  
			},	
			clearnotes	= {
				name = L["Clear all notes on map"],
				desc = L["This will clear all notes on map"],
				type = 'execute',
				confirm = L["This will remove all notes on the map. This can't be undone are your sure?"],
				func = "ClearAllNotes",
				order = -1,
			},				
			toggle = {
				name = AceLibrary("AceLocale-2.2"):new("Cartographer")["Enabled"],
				desc = AceLibrary("AceLocale-2.2"):new("Cartographer")["Suspend/resume this module."],
				type = "toggle",
				order = -1,
				get = function() return Cartographer:IsModuleActive(self) end,
				set = function() Cartographer:ToggleModuleActive(self) end
			},
		},
		handler = self,
	}
end

local pois, minimapPois
local oldWorldMapMagnifyingGlassButtonText
function Cartographer_Notes:OnEnable(first)
	Cartographer:AddToMagnifyingGlass(L["Ctrl-Right-Click on map to add a note"])
	if self.developing then
		Cartographer:AddToMagnifyingGlass(("Alt-Right-Click on map to add a default note to %q"):format(self.developing))
	end
	self:Hook("WorldMapButton_OnClick", true)
	self:Hook("ToggleWorldMap", true)
	self:RegisterEvent("WORLD_MAP_UPDATE", function() self:RefreshMap(false) end)
	self:RegisterEvent("Cartographer_SetCurrentInstance", function() self:RefreshMap(false) end)
	if Cartographer:GetInstanceWorldMapButton() then
		self:HookScript(Cartographer:GetInstanceWorldMapButton(), "OnClick", "InstanceWorldMapButton_OnClick")
	else
		self:RegisterEvent("Cartographer_RegisterInstanceWorldMapButton", function(frame)
			self:HookScript(frame, "OnClick", "InstanceWorldMapButton_OnClick")
		end)
	end
	if WorldMapFrame:IsShown() then
		self:RefreshMap(false)
	end
	
	local func = function()
		self:WorldMapButton_OnClick(arg1, WorldMapButton)
	end
	WorldMapPlayer:SetScript("OnMouseUp", func)
	for i = 1, 4 do
		_G["WorldMapParty" .. i]:SetScript("OnMouseUp", func)
	end
	for i = 1, 40 do
		_G["WorldMapRaid" .. i]:SetScript("OnMouseUp", func)
	end
	WorldMapFlag1:SetScript("OnMouseUp", func)
	WorldMapFlag2:SetScript("OnMouseUp", func)
	WorldMapCorpse:SetScript("OnMouseUp", func)
	WorldMapDeathRelease:SetScript("OnMouseUp", func)
	
	self:RegisterComm(self.commPrefix, "WHISPER")
	self:RegisterComm(self.commPrefix, "GUILD")
	self:RegisterComm(self.commPrefix, "PARTY")
	self:RegisterComm(self.commPrefix, "RAID")
	-- Added the group channel to comm list
	-- when in a BG, sending message to RAID or PARTY are silently dropped BATTLEGROUND should be used
	-- the GROUP distribtuion checks for BG -> then RAID -> then PARTY
	-- message sent to RAID when not in a raid are sent to PARTY
	self:RegisterComm(self.commPrefix, "GROUP")
	
	if first then
		local tmp = {}
		self:RegisterChatCommand(L["Console-note-commands"], function(text)
			text = text:gsub("[%s,]+", " "):trim()
			local x, y, title = (" "):split(text, 3)
			x, y = tonumber(x), tonumber(y)
			if not x or not y then
				self:Print(L["Usage: /note <0-100> <0-100> [title]"])
				return
			end
			x, y = x / 100, y / 100
			
			for k,v in pairs(icons) do
				if v.showToUser then
					table.insert(tmp, k)
				end
			end
			local icon = tmp[math.random(1, #tmp)]
			for k in pairs(tmp) do
				tmp[k] = nil
			end
			if title and title:len() == 0 then
				title = nil
			end
			if title and icons[title] then
				icon = title
				title = nil
			end
			self:SetNote(GetRealZoneText(), x, y, icon, (UnitName("player")),
				'manual', true,
				'title', title)
		end)
	end
	if self.db.profile.showMinimapIcons then
		local f = function()
			self:ScheduleRepeatingEvent("Cartographer-Notes-MinimapIcons", self.UpdateMinimapIcons, 0, self)
			self:RegisterEvent("MINIMAP_ZONE_CHANGED")
			self:RegisterEvent("MINIMAP_UPDATE_ZOOM")
			self:RegisterEvent("ZONE_CHANGED_NEW_AREA")
			self:RegisterEvent("PLAYER_LEAVING_WORLD")
			self:MINIMAP_UPDATE_ZOOM()
			self:MINIMAP_ZONE_CHANGED()
			self:ZONE_CHANGED_NEW_AREA()
		end
		if AceLibrary("AceEvent-2.0"):IsFullyInitialized() then
			f()
		else
			self:RegisterEvent("AceEvent_FullyInitialized", f)
		end
	else
		-- if you enter the gameworld with minimap icons off MINIMAP_ZONE_CHANGED doesnt get called before UpdateMinimapIcons and currentyardwidth not set
		-- causes a nil exception
		self:configureYards()
	end
end

function Cartographer_Notes:OnDisable()
	Cartographer:RemoveFromMagnifyingGlass(L["Ctrl-Right-Click on map to add a note"])
	if self.developing then
		Cartographer:RemoveFromMagnifyingGlass(("Alt-Right-Click on map to add a default note to %q"):format(self.developing))
	end
	self:ClearMap()
	WorldMapPlayer:SetScript("OnMouseUp", nil)
	for i = 1, 4 do
		_G["WorldMapParty" .. i]:SetScript("OnMouseUp", nil)
	end
	for i = 1, 40 do
		_G["WorldMapRaid" .. i]:SetScript("OnMouseUp", nil)
	end
	WorldMapFlag1:SetScript("OnMouseUp", nil)
	WorldMapFlag2:SetScript("OnMouseUp", nil)
	WorldMapCorpse:SetScript("OnMouseUp", nil)
	WorldMapDeathRelease:SetScript("OnMouseUp", nil)
	for id in pairs(minimapPois) do
		minimapPois:del(id)
	end
end

Cartographer_Notes.OnCommReceive = {
	NOTE = function(self, prefix, sender, distribution, zone, x, y, creator, data)
		if type(zone) ~= "string" or type(x) ~= "number" or x < 0 or x > 1 or type(y) ~= "number" or y < 0 or y > 1 or (type(creator) ~= "string" and creator ~= nil) or (type(data) ~= "table" and type(data) ~= "string") or (type(data) == "table" and type(data.icon) ~= "string") then
			-- bad data
			return
		end
		
		if not BZ:HasTranslation(zone) then
			self:Print(L["Error: %s sent you a note with an unknown zone: %q"], fullSender, zone)
			return
		end
		
		local usingDB = self.externalDBs[creator] and creator or nil
		if creator and not usingDB and type(data) == "string" then
			data = {
				icon = data,
				manual = false,
				creator = creator
			}
		end
		
		local fullSender = distribution == "GUILD" and L["%s from Guild"]:format(sender) or distribution == "RAID" and L["%s from Raid"]:format(sender) or distribution == "PARTY" and L["%s from Party"]:format(sender) or sender
		local id = getID(x, y)
		local poi_data, db = getrawpoi(zone, id)
		if poi_data and (db or poi_data.creator or "") ~= (creator or type(data) == "table" and data.creator or "") then
			self:Print(L["Error: %s sent you a note at %q : %.2f, %.2f with the title %q created by %q, but you already have a note at this location."], fullSender, BZ[zone], x*100, y*100, type(data) == "table" and data.title or getIconTitle(type(data) == "string" and data or data.icon) or UNKNOWN, creator or type(data) == "table" and data.creator or UNKNOWN)
			return
		end
		-- turn off the NoteAdd Events 
		setNoteFromComm = true
		if self:SetNote(zone, x, y, type(data) == "string" and data or data.icon, (creator or type(data) == "table" and data.creator or ""), type(data) == "table" and data or nil) then
			-- if changed
			if self.db.profile.showNotifications then
				self:Print(L["%s sent you a note at %q : %.2f, %.2f with the title %q created by %q."], fullSender, BZ[zone], x*100, y*100, type(data) == "table" and data.title or getIconTitle(type(data) == "string" and data or data.icon), creator or type(data) == "table" and data.creator or UNKNOWN)
			end
		end
		-- turn on the note add event
		setNoteFromComm = false
	end
}

function Cartographer_Notes:InstanceWorldMapButton_OnClick(frame, mouseButton)
	if mouseButton ~= "RightButton" or (not IsControlKeyDown() and (not self.developing or not IsAltKeyDown())) then
		return self.hooks[frame].OnClick(frame, mouseButton)
	end
	
	if not Cartographer:GetCurrentEnglishZoneName() then
		return
	end
	
	return self:MapButton_OnClick(mouseButton, frame)
end

function Cartographer_Notes:WorldMapButton_OnClick(mouseButton, button)
	if mouseButton ~= "RightButton" or (not IsControlKeyDown() and (not self.developing or not IsAltKeyDown())) then
		return self.hooks.WorldMapButton_OnClick(mouseButton, button)
	end
	
	if not Cartographer:GetCurrentEnglishZoneName() then
		return
	end
	
	if not button then
		button = this
	end
	
	return self:MapButton_OnClick(mouseButton, button)
end

local lastCursorX, lastCursorY
function Cartographer_Notes:MapButton_OnClick(mouseButton, button)
	if IsControlKeyDown() then
		if not Dewdrop:IsRegistered(WorldMapFrame) then
			local newNoteFunc = function(creator)
				self:OpenNewNoteFrame(lastCursorX, lastCursorY, creator)
				Dewdrop:Close()
			end
			local closeFunc = function()
				Dewdrop:Close()
			end
			Dewdrop:Register(button,
				'children', function()
					Dewdrop:AddLine(
						'text', L["Create a new note"],
						'func', newNoteFunc
					)
					if self.developing then
						Dewdrop:AddLine(
							'text', L["Create a new note for %q"]:format(self.developing),
							'func', newNoteFunc,
							'arg1', self.developing
						)
					end
					Dewdrop:AddLine()
					Dewdrop:AddLine(
						'text', CANCEL,
						'func', closeFunc
					)
				end,
				'dontHook', true,
				'cursorX', true,
				'cursorY', true
			)
		end
		lastCursorX, lastCursorY = GetCursorMapLocation(button)
		if button == WorldMapFrame then
			Dewdrop:Open(button)
		else
			Dewdrop:Open(button, WorldMapFrame)
		end
	else
		local x, y = GetCursorMapLocation(button)
		self:SetNote(Cartographer:GetCurrentEnglishZoneName(), x, y, "Triangle", self.developing)
	end
end

local frame
local function GetNoteDialog()
	if frame then
		return frame
	end
	
	frame = CreateFrame("Frame", "CartographerNotesNewNoteFrame", WorldMapFrame)
	frame:SetPoint("CENTER", WorldMapFrame, "CENTER")
	frame:SetWidth(500)
	frame:SetHeight(330)
	frame:SetFrameLevel(9)
	frame:EnableMouse(true)
	frame:SetMovable(true)
	frame:RegisterForDrag("LeftButton")
	frame:SetScript("OnDragStart", function()
		this:StartMoving()
	end)
	frame:SetScript("OnDragStop", function()
		this:StopMovingOrSizing()
	end)
	frame:SetBackdrop({
		bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
		edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
		tile = true,
		insets = {
			left = 11,
			right = 12,
			top = 12,
			bottom = 11
		},
		'tileSize', 32,
		'edgeSize', 32
	})
	
	local texture = frame:CreateTexture(nil, "ARTWORK")
	texture:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
	texture:SetWidth(256)
	texture:SetHeight(64)
	texture:SetPoint("TOP", frame, "TOP", 0, 12)
	
	local header = frame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
	frame.header = header
	header:SetText(L["New note"])
	header:SetPoint("TOP", texture, "TOP", 0, -14)
	
	local okayButton = CreateFrame("Button", "CartographerNotesNewNoteFrameOkay", frame, "UIPanelButtonTemplate2")
	okayButton:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 20, 20)
	okayButton:SetText(OKAY or 'Okay')
	okayButton:SetWidth(frame:GetWidth()/2 - 25)
	
	local cancelButton = CreateFrame("Button", "CartographerNotesNewNoteFrameCancel", frame, "UIPanelButtonTemplate2")
	cancelButton:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -20, 20)
	cancelButton:SetText(CANCEL or 'Cancel')
	cancelButton:SetWidth(frame:GetWidth()/2 - 25)
	cancelButton:SetScript("OnClick", function()
		frame:Hide()
	end)
	
	local last
	local isGood
	
	local OnEscapePressed = function()
		this:ClearFocus()
	end
	
	local OnTextChanged = function()
		if isGood() then
			okayButton:Enable()
		else
			okayButton:Disable()
		end
	end
	
	local function make(text, colorful)
		local editBox = CreateFrame("EditBox", nil, frame)
		editBox:SetFontObject(ChatFontNormal)
		editBox:SetWidth(colorful and 210 or 240)
		editBox:SetHeight(13)
		if not last then
			editBox:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -25, -35)
		else
			editBox:SetPoint("TOPLEFT", last, "BOTTOMLEFT", 0, -20)
		end
		last = editBox
		editBox:SetAutoFocus(false)
		editBox:SetScript("OnEscapePressed", OnEscapePressed)
		editBox:SetScript("OnTextChanged", OnTextChanged)

		local left = editBox:CreateTexture(nil, "BACKGROUND")
		left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
		left:SetTexCoord(0, 100 / 256, 0, 1)
		left:SetWidth(colorful and 125 or 140)
		left:SetHeight(32)
		left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
		local right = editBox:CreateTexture(nil, "BACKGROUND")
		right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
		right:SetTexCoord(156/256, 1, 0, 1)
		right:SetWidth(colorful and 125 or 140)
		right:SetHeight(32)
		right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
		
		local label = frame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
		label:SetPoint("RIGHT", editBox, "LEFT", -20, 0)
		label:SetPoint("LEFT", frame, "LEFT", 20, 0)
		label:SetPoint("TOP", editBox, "TOP")
		label:SetPoint("BOTTOM", editBox, "BOTTOM")
		label:SetJustifyH("RIGHT")
		label:SetText(text)
		
		if colorful then
			local button = CreateFrame("Button", nil, editBox)
			button:SetPoint("LEFT", editBox, "RIGHT", 20, 0)
			button:SetWidth(16)
			button:SetHeight(16)
			local texture = button:CreateTexture("ARTWORK")
			texture:SetAllPoints(button)
			texture:SetTexture(1, 1, 1)
			local function changeColor(r, g, b)
				texture:SetTexture(r, g, b)
				editBox:SetTextColor(r, g, b)
				Dewdrop:Close()
			end
			function editBox.resetColor(r, g, b)
				if not r then
					r, g, b = 1, 1, 1
				end
				texture:SetTexture(r, g, b)
				editBox:SetTextColor(r, g, b)
			end
			button:SetScript("OnClick", function()
				Dewdrop:Register(button,
					'children', function()
						Dewdrop:AddLine(
							'text', L["White"],
							'textR', 1,
							'textG', 1,
							'textB', 1,
							'func', changeColor,
							'arg1', 1,
							'arg2', 1,
							'arg3', 1
						)
						Dewdrop:AddLine(
							'text', L["Gray"],
							'textR', 0.8,
							'textG', 0.8,
							'textB', 0.8,
							'func', changeColor,
							'arg1', 0.8,
							'arg2', 0.8,
							'arg3', 0.8
						)
						Dewdrop:AddLine(
							'text', L["Pink"],
							'textR', 1,
							'textG', 0.62,
							'textB', 0.59,
							'func', changeColor,
							'arg1', 1,
							'arg2', 0.62,
							'arg3', 0.59
						)
						Dewdrop:AddLine(
							'text', L["Red"],
							'textR', 1,
							'textG', 0.24,
							'textB', 0.17,
							'func', changeColor,
							'arg1', 1,
							'arg2', 0.24,
							'arg3', 0.17
						)
						Dewdrop:AddLine(
							'text', L["Orange"],
							'textR', 0.98,
							'textG', 0.57,
							'textB', 0,
							'func', changeColor,
							'arg1', 0.98,
							'arg2', 0.57,
							'arg3', 0
						)
						Dewdrop:AddLine(
							'text', L["Yellow"],
							'textR', 1,
							'textG', 0.92,
							'textB', 0,
							'func', changeColor,
							'arg1', 1,
							'arg2', 0.92,
							'arg3', 0
						)
						Dewdrop:AddLine(
							'text', L["Green"],
							'textR', 0,
							'textG', 0.7,
							'textB', 0,
							'func', changeColor,
							'arg1', 0,
							'arg2', 0.7,
							'arg3', 0
						)
						Dewdrop:AddLine(
							'text', L["Lime"],
							'textR', 0.04,
							'textG', 0.95,
							'textB', 0,
							'func', changeColor,
							'arg1', 0.04,
							'arg2', 0.95,
							'arg3', 0
						)
						Dewdrop:AddLine(
							'text', L["Cyan"],
							'textR', 0,
							'textG', 1,
							'textB', 1,
							'func', changeColor,
							'arg1', 0,
							'arg2', 1,
							'arg3', 1
						)
						Dewdrop:AddLine(
							'text', L["Blue"],
							'textR', 0,
							'textG', 0.71,
							'textB', 1,
							'func', changeColor,
							'arg1', 0,
							'arg2', 0.71,
							'arg3', 1
						)
						Dewdrop:AddLine(
							'text', L["Pale blue"],
							'textR', 0.7,
							'textG', 0.82,
							'textB', 0.88,
							'func', changeColor,
							'arg1', 0.7,
							'arg2', 0.82,
							'arg3', 0.88
						)
						Dewdrop:AddLine(
							'text', L["Purple"],
							'textR', 0.83,
							'textG', 0.22,
							'textB', 0.9,
							'func', changeColor,
							'arg1', 0.83,
							'arg2', 0.22,
							'arg3', 0.9
						)
					end,
					'dontHook', true,
					'point', "TOPRIGHT",
					'relativePoint', "BOTTOMRIGHT"
				)
				button:SetScript("OnClick", function()
					if Dewdrop:IsOpen(button) then
						Dewdrop:Close()
					else
						Dewdrop:Open(button)
					end
					PlaySound("igMainMenuOptionCheckBoxOn")
				end)
				button:GetScript("OnClick")()
			end)
		end
		
		return editBox
	end
	
	frame.xEditBox = make(L["X position"])
	frame.yEditBox = make(L["Y position"])
	frame.zone = make(ZONE or "Zone")
	frame.zone:SetScript("OnEditFocusGained", function()
		this:ClearFocus()
	end)
	frame.title = make(L["Title"], true)
	frame.info1 = make(L["Info line 1 (optional)"], true)
	frame.info2 = make(L["Info line 2 (optional)"], true)
	frame.creator = make(L["Creator (optional)"])
	
	frame.xEditBox:SetScript("OnTabPressed", function()
		this:ClearFocus()
		frame.yEditBox:SetFocus()
	end)
	frame.yEditBox:SetScript("OnTabPressed", function()
		this:ClearFocus()
		frame.title:SetFocus()
	end)
	frame.zone:SetScript("OnTabPressed", frame.yEditBox:GetScript("OnTabPressed"))
	frame.title:SetScript("OnTabPressed", function()
		this:ClearFocus()
		frame.info1:SetFocus()
	end)
	frame.info1:SetScript("OnTabPressed", function()
		this:ClearFocus()
		frame.info2:SetFocus()
	end)
	frame.info2:SetScript("OnTabPressed", function()
		this:ClearFocus()
		frame.creator:SetFocus()
	end)
	frame.creator:SetScript("OnTabPressed", function()
		this:ClearFocus()
	end)
	function isGood()
		local x = tonumber(frame.xEditBox:GetText())
		if not x then
			return false
		elseif x < 0 or x > 100 then
			return false
		end
		local y = tonumber(frame.yEditBox:GetText())
		if not y then
			return false
		elseif y < 0 or y > 100 then
			return false
		end
		return true
	end
	
	local icon = CreateFrame("Frame", "CartographerNotesNewNoteFrameIcon", frame)
	frame.icon = icon
	icon:SetPoint("TOPLEFT", frame.creator, "BOTTOMLEFT", -20, -10)
	icon:SetWidth(frame:GetWidth()/2 - 25)
	icon:SetHeight(30)
	
	local texture = icon:CreateTexture(nil, "ARTWORK")
	texture:SetTexture("Interface\\Glues\\CharacterCreate\\CharacterCreate-LabelFrame")
	texture:SetWidth(25)
	texture:SetHeight(64)
	texture:SetPoint("TOPLEFT", icon, "TOPLEFT", 0, 17)
	texture:SetTexCoord(0, 0.1953125, 0, 1)
	
	local texture2 = icon:CreateTexture(nil, "ARTWORK")
	texture2:SetTexture("Interface\\Glues\\CharacterCreate\\CharacterCreate-LabelFrame")
	texture2:SetWidth(115)
	texture2:SetHeight(64)
	texture2:SetPoint("LEFT", texture, "RIGHT")
	texture2:SetTexCoord(0.1953125, 0.8046875, 0, 1)
	
	local texture3 = icon:CreateTexture(nil, "ARTWORK")
	texture3:SetTexture("Interface\\Glues\\CharacterCreate\\CharacterCreate-LabelFrame")
	texture3:SetWidth(25)
	texture3:SetHeight(64)
	texture3:SetPoint("LEFT", texture2, "RIGHT")
	texture3:SetTexCoord(0.8046875, 1, 0, 1)
	
	local fontstring = icon:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
	fontstring:SetJustifyH("RIGHT")
	fontstring:SetWidth(0)
	fontstring:SetHeight(10)
	fontstring:SetPoint("RIGHT", texture3, "RIGHT", -43, 2)
	
	local image = icon:CreateTexture(nil, "OVERLAY")
	image:SetTexture("Interface\\TargetingFrame\\UI-RaidTargetingIcons")
	image:SetWidth(16)
	image:SetHeight(16)
	image:SetTexCoord(0, 0.25, 0, 0.25)
	image:SetPoint("LEFT", texture, "LEFT", 20, 0)
	
	local button = CreateFrame("Button", "CartographerNotesNewNoteFrameIconButton", icon)
	button:SetWidth(24)
	button:SetHeight(24)
	button:SetPoint("TOPRIGHT", texture3, "TOPRIGHT", -16, -18)
	button:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up")
	button:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down")
	button:SetDisabledTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Disabled")
	button:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight")
	local iconSelector = function(k)
		icon.value = k
		local t = icons[k]
		if k:find("^Interface\\") then
			if frame.title:GetText() == fontstring:GetText() then
				frame.title:SetText(L["Custom icon"])
			end
			fontstring:SetText(L["Custom icon"])
			fontstring:SetTextColor(1, 1, 1)
			image:SetTexture(k)
			if k:find("^Interface\\Icons\\") then
				image:SetTexCoord(0.05, 0.95, 0.05, 0.95)
			else
				image:SetTexCoord(0, 1, 0, 1)
			end
			Dewdrop:Close()
			return
		end
		if not t then
			t = icons.Unknown
		end
		if frame.title:GetText() == fontstring:GetText() then
			frame.title:SetText(t.text)
		end
		fontstring:SetText(t.text)
		fontstring:SetTextColor(t.r or 1, t.g or 1, t.b or 1)
		image:SetTexture(t.path)
		image:SetTexCoord(t.cLeft or 0, t.cRight or 1, t.cTop or 0, t.cBottom or 1)
		Dewdrop:Close()
	end
	frame.iconSelector = iconSelector
	button:SetScript("OnClick", function()
		local t = {}
		local mysort = function(alpha, bravo)
			if not alpha or not bravo then
				return false
			end
			local alpha_text = icons[alpha].text or alpha
			local bravo_text = icons[bravo].text or alpha
			return alpha_text < bravo_text
		end
		Dewdrop:Register(icon,
			'children', function()
				for k, v in pairs(icons) do
					if v.showToUser then
						table.insert(t, k)
					end
				end
				table.sort(t, mysort)
				for _, k in ipairs(t) do
					local v = icons[k]
					Dewdrop:AddLine(
						'text', v.text or k,
						'textR', v.r or 1,
						'textG', v.g or 1,
						'textB', v.b or 1,
						'icon', v.path,
						'iconCoordLeft', v.cLeft or 0,
						'iconCoordRight', v.cRight or 1,
						'iconCoordTop', v.cTop or 0,
						'iconCoordBottom', v.cBottom or 1,
						'func', iconSelector,
						'arg1', k
					)
				end
				for i = 1, #t do
					t[i] = nil
				end
			end,
			'dontHook', true,
			'point', "TOPLEFT",
			'relativePoint', "BOTTOMLEFT"
		)
		button:SetScript("OnClick", function()
			if Dewdrop:IsOpen(icon) then
				Dewdrop:Close()
			else
				Dewdrop:Open(icon)
			end
			PlaySound("igMainMenuOptionCheckBoxOn")
		end)
		button:GetScript("OnClick")()
	end)
	icon:SetScript("OnHide", function()
		if Dewdrop:IsOpen(icon) then
			Dewdrop:Close()
		end
	end)
	
	okayButton:SetScript("OnClick", function()
		local r,g,b = frame.title:GetTextColor()
		local r2,g2,b2 = frame.info1:GetTextColor()
		local r3,g3,b3 = frame.info2:GetTextColor()
		local x, y = tonumber(frame.xEditBox:GetText())/100, tonumber(frame.yEditBox:GetText())/100
		x,y,r,g,b,r2,g2,b2,r3,g3,b3 = round(x, 3),round(y, 3),round(r, 2),round(g, 2),round(b, 2),round(r2, 2),round(g2, 2),round(b2, 2),round(r3, 2),round(g3, 2),round(b3, 2)
		self:SetNote(frame.zonename, x, y, frame.icon.value, frame.creator:GetText(),
			'title', frame.title:GetText(),
			'titleR', r,
			'titleG', g,
			'titleB', b,
			'info', frame.info1:GetText(),
			'infoR', r2,
			'infoG', g2,
			'infoB', b2,
			'info2', frame.info2:GetText(),
			'info2R', r3,
			'info2G', g3,
			'info2B', b3,
			'manual', true,
			'oldId', frame.id)
		frame:Hide()
	end)
	
	return frame
end

local possibleIcons
function Cartographer_Notes:OpenNewNoteFrame(x, y, creator)
	if not Cartographer:GetCurrentEnglishZoneName() then
		return
	end
	AceLibrary.argCheck(self, x, 2, "number")
	AceLibrary.argCheck(self, y, 3, "number")
	AceLibrary.argCheck(self, creator, 4, "string", "nil")
	if x < 0 or x > 1 then
		error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
	end
	if y < 0 or y > 1 then
		error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
	end
	
	local frame = GetNoteDialog()
	frame.header:SetText(L["New note"])
	frame.id = nil
	frame.xEditBox:SetText(("%.2f"):format(x*100))
	frame.yEditBox:SetText(("%.2f"):format(y*100))
	frame.zone:SetText(Cartographer:GetCurrentLocalizedZoneName())
	frame.title:SetText("")
	frame.title.resetColor()
	frame.info1:SetText("")
	frame.info1.resetColor()
	frame.info2:SetText("")
	frame.info2.resetColor()
	frame.creator:SetText(creator or UnitName("player"))
	frame:Show()
	frame.title:SetFocus()
	if not possibleIcons then
		possibleIcons = {}
	end
	for k in pairs(possibleIcons) do
		possibleIcons[k] = nil
	end
	for k,v in pairs(icons) do
		if v.showToUser then
			table.insert(possibleIcons, k)
		end
	end
	frame.iconSelector(possibleIcons[math.random(1, #possibleIcons)])
	frame.zonename = Cartographer:GetCurrentEnglishZoneName()
end

function Cartographer_Notes:ShowEditDialog(zone, x, y)
	AceLibrary.argCheck(self, zone, 2, "string")
	local id
	if not y then
		id = x
		AceLibrary.argCheck(self, x, 3, "number")
	else
		AceLibrary.argCheck(self, x, 3, "number")
		AceLibrary.argCheck(self, y, 4, "number")
		if x < 0 or x > 1 then
			error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
		end
		if y < 0 or y > 1 then
			error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
		end
		id = getID(x, y)
	end
	
	local data, db = getrawpoi(zone, id)
	if not data then
		return
	end
	local frame = GetNoteDialog()
	frame.header:SetText(L["Edit note"])
	frame.id = id
	local x, y = getXY(id)
	frame.xEditBox:SetText(("%.2f"):format(x*100))
	frame.yEditBox:SetText(("%.2f"):format(y*100))
	frame.zone:SetText(BZ[zone])
	if type(data) == "table" then
		frame.title:SetText(data.title or getIconTitle(data.icon))
		frame.title.resetColor(getRGB(data.titleCol))
		frame.info1:SetText(data.info or "")
		frame.info1.resetColor(getRGB(data.infoCol))
		frame.info2:SetText(data.info2 or "")
		frame.info2.resetColor(getRGB(data.info2Col))
		frame.creator:SetText(db or data.creator or "")
		frame.iconSelector(data.icon)
	else
		frame.title:SetText(getIconTitle(data))
		frame.title.resetColor(1, 1, 1)
		frame.info1:SetText("")
		frame.info1.resetColor(1, 1, 1)
		frame.info2:SetText("")
		frame.info2.resetColor(1, 1, 1)
		frame.creator:SetText(db)
		frame.iconSelector(data)
	end
	frame:Show()
	frame.title:SetFocus()
	frame.zonename = zone
end

do
	local cache = {}
	
	local num_pois = 0
	
	local OnMouseDown, OnMouseUp, OnEnter, OnLeave, OnClick
	
	local dummy
	local function newpoi(minimap)
		local frame = next(cache)
		if frame then
			cache[frame] = nil
			frame:Show()
			frame.minimap = minimap
			if minimap then
				frame:SetPoint("CENTER", Minimap, "CENTER")
			else
				frame:SetPoint("CENTER", WorldMapButton, "CENTER")
			end
			frame:SetAlpha(1)
			return frame
		end
		
		num_pois = num_pois + 1
		
		local frame = CreateFrame("Button", "CartographerNotesPOI" .. num_pois, WorldMapButton)
		frame:EnableMouse(true)
		frame:SetMovable(true)
		frame:SetWidth(16)
		frame:SetHeight(16)
		frame:SetPoint("CENTER", WorldMapButton, "CENTER")
		
		local texture = frame:CreateTexture(nil, "OVERLAY")
		frame.texture = texture
		texture:SetAllPoints(frame)
		texture:SetTexture("Interface\\TargetingFrame\\UI-RaidTargetingIcons")
		texture:SetTexCoord(0, 0.25, 0, 0.25)
		
		frame:SetFrameLevel(frame:GetFrameLevel()+3)
		frame:RegisterForClicks("LeftButtonUp", "RightButtonUp")
		if not OnMouseDown then
			OnMouseDown = function(this, arg1)
				if arg1 == "LeftButton" and this.manual and not this.minimap and IsAltKeyDown() then
					this.isMoving = true
					this:StartMoving()
				end
			end
		end
		frame:SetScript("OnMouseDown", OnMouseDown)
		
		if not OnMouseUp then
			OnMouseUp = function(this, arg1)
				if arg1 == "LeftButton" then
					if this.isMoving then
						this:StopMovingOrSizing()
						this.isMoving = nil
						local x, y = this:GetCenter()
						local parent = this:GetParent()
						local left, top = parent:GetLeft(), parent:GetTop()
						local width = parent:GetWidth()
						local height = parent:GetHeight()
						x = (x - left) / width
						y = (top - y) / height
						
						x, y = round(x, 3), round(y, 3)
						if x > 1 then
							x = 1
						end
						if x < 0 then
							x = 0
						end
						if y > 1 then
							y = 1
						end
						if y < 0 then
							y = 0
						end
						local id = getID(x, y)
						if id ~= this.id then
							pois[id] = pois[this.id]
							pois[this.id] = nil
							local t, db = getpoi(this.zone, this.id)
							forceNextMinimapUpdate = true
							if db then
								local v = self.externalDBs[db]
								v[this.zone][id] = t
								v[this.zone][this.id] = nil
								self:ShowNote(this.zone, id, db)
							else
								self.db.account.pois[this.zone][id] = t
								self.db.account.pois[this.zone][this.id] = nil
								self:ShowNote(this.zone, id, t.creator or "")
							end
						end
					end
				end
			end
		end
		frame:SetScript("OnMouseUp", OnMouseUp)
		
		if not OnEnter then
			local dummy
			local currentFrame
			OnEnter = function(this)
				currentFrame = this
				if not dummy then
					dummy = CreateFrame("Frame")
					Tablet:Register(dummy,
						'children', function()
							if not currentFrame:IsShown() then
								return
							end
							if self.handlers[currentFrame.creator] and type(self.handlers[currentFrame.creator].OnNoteTooltipRequest) == "function" then
								local data = getrawpoi(currentFrame.zone, currentFrame.id, currentFrame.creator)
								if data then
									self.handlers[currentFrame.creator]:OnNoteTooltipRequest(currentFrame.zone, currentFrame.id, data, currentFrame.minimap)
									return
								end
							end
							Tablet:SetTitle(currentFrame.title)
							Tablet:SetTitleColor(getRGB(currentFrame.titleCol))
							
							if currentFrame.info and currentFrame.info ~= "" then
								local r,g,b = getRGB(currentFrame.infoCol)
								Tablet:AddCategory(
									'hideBlankLine', true
								):AddLine(
									'text', currentFrame.info,
									'textR', r,
									'textG', g,
									'textB', b,
									'wrap', true
								)
							end
							if currentFrame.info2 and currentFrame.info2 ~= "" then
								local r,g,b = getRGB(currentFrame.info2Col)
								Tablet:AddCategory(
									'hideBlankLine', true
								):AddLine(
									'text', currentFrame.info2,
									'textR', r,
									'textG', g,
									'textB', b,
									'wrap', true
								)
							end
							if not currentFrame.minimap and self:IsShowingYardsAway() and BZ[currentFrame.zone] == GetRealZoneText() then
								local px, py = yardify(GetPlayerMapPosition("player"))
								local nx, ny = yardify(getXY(currentFrame.id))
								local dx, dy = px-nx, py-ny
								local d = (dx*dx + dy*dy)^0.5
								Tablet:AddCategory(
									'columns', 2,
									'hideBlankLine', true
								):AddLine(
									'text', metric and L["Meters away:"] or "Yards away:",
									'text2', string.format("%.0f", metric and d*0.9144 or d)
								)
							end
							if not currentFrame.minimap and self:IsShowingCreator() and currentFrame.creator and currentFrame.creator ~= "" then
								Tablet:AddCategory(
									'columns', 2,
									'hideBlankLine', true
								):AddLine(
									'text', L["Created by"],
									'text2', currentFrame.creator
								)
							end
						end,
						'point', function(parent)
							local x, y = GetCursorPosition()
							local cx, cy = GetScreenWidth()/2, GetScreenHeight()/2
							if x > cx then
								if y < cy then
									return "BOTTOMRIGHT"
								else
									return "TOPRIGHT"
								end
							else
								if y < cy then
									return "BOTTOMLEFT"
								else
									return "TOPLEFT"
								end
							end
						end,
						'relativePoint', function(parent)
							local x, y = GetCursorPosition()
							local cx, cy = GetScreenWidth()/2, GetScreenHeight()/2
							if x > cx then
								if y < cy then
									return "TOPLEFT"
								else
									return "BOTTOMLEFT"
								end
							else
								if y < cy then
									return "TOPRIGHT"
								else
									return "BOTTOMRIGHT"
								end
							end
						end,
						'dontHook', true
					)
				end
				if not Dewdrop:IsOpen(currentFrame) then
					Tablet:Open(currentFrame, dummy)
				end
			end
		end
		frame:SetScript("OnEnter", OnEnter)
		
		if not OnLeave then
			OnLeave = function(this)
				Tablet:Close()
			end
		end
		frame:SetScript("OnLeave", OnLeave)
		
		if not OnClick then
			local poi
			OnClick = function(this, arg1)
				if this.minimap then
					_G.this = Minimap
					local f = Minimap:GetScript("OnMouseDown")
					if f then
						f(Minimap, arg1)
					end
					f = Minimap:GetScript("OnMouseUp")
					if f then
						f(Minimap, arg1)
					end
					return
				end
				if arg1 == "LeftButton" then
					if self.handlers[this.creator] and type(self.handlers[this.creator].OnNoteClick) == "function" then
						local data = getrawpoi(this.zone, this.id, this.creator)
						if data then
							self.handlers[this.creator]:OnNoteClick(this.zone, this.id, data)
						end
					end
				elseif arg1 == "RightButton" then
					if not dummy then
						dummy = CreateFrame("Frame")
						local function editNoteFunc()
							self:ShowEditDialog(poi.zone, poi.id)
							Dewdrop:Close()
						end
						local function deleteNoteFunc()
							self:DeleteNote(poi.zone, poi.id)
							Dewdrop:Close()
						end
						local function closeFunc()
							Dewdrop:Close()
						end
						local function sendNoteToPartyFunc()
							if getrawpoi(poi.zone, poi.id) then
								self:SendNoteToParty(poi.zone, getXY(poi.id))
							end
						end
						local function sendNoteToRaidFunc()
							if getrawpoi(poi.zone, poi.id) then
								self:SendNoteToRaid(poi.zone, getXY(poi.id))
							end
						end
						local function sendNoteToGuildFunc()
							if getrawpoi(poi.zone, poi.id) then
								self:SendNoteToGuild(poi.zone, getXY(poi.id))
							end
						end
						local function sendNoteToPlayerFunc(player)
							if getrawpoi(poi.zone, poi.id) then
								local x, y = getXY(poi.id)
								self:SendNoteToPlayer(poi.zone, x, y, player)
							end
						end
						local function sendNoteToPlayerValidate(player)
							return player:find("^[A-Za-z][A-Za-z]+$")
						end
						Dewdrop:Register(dummy,
							'children', function(level, value, level2, level3, level4)
								if self.handlers[poi.creator] and type(self.handlers[poi.creator].OnNoteMenuRequest) == "function" then
									local data = getrawpoi(poi.zone, poi.id, poi.creator)
									if data then
										self.handlers[poi.creator]:OnNoteMenuRequest(poi.zone, poi.id, data, level, value, level2, level3, level4)
									end
								else
									if level == 1 then
										if poi.manual then
											Dewdrop:AddLine(
												'text', L["Edit note"],
												'func', editNoteFunc
											)
										end
										Dewdrop:AddLine(
											'text', L["Delete note"],
											'func', deleteNoteFunc
										)
										Dewdrop:AddLine()
										
										Dewdrop:AddLine(
											'text', L["Send note"],
											'hasArrow', true,
											'value', 'send'
										)
										Dewdrop:AddLine()
										
										Dewdrop:AddLine(
											'text', CANCEL,
											'func', closeFunc
										)
									elseif level == 2 then
										if value == 'send' then
											local bit = false
											if GetNumPartyMembers() > 0 then
												Dewdrop:AddLine(
													'text', L["Send to party"],
													'func', sendNoteToPartyFunc
												)
												bit = true
											end
											if GetNumRaidMembers() > 0 then
												Dewdrop:AddLine(
													'text', L["Send to raid"],
													'func', sendNoteToRaidFunc
												)
												bit = true
											end
											if IsInGuild() then
												Dewdrop:AddLine(
													'text', L["Send to guild"],
													'func', sendNoteToGuildFunc
												)
												bit = true
											end
											
											if bit then
												Dewdrop:AddLine()
											end
											
											Dewdrop:AddLine(
												'text', L["Send to player"],
												'tooltipTitle', L["Send to player"],
											--	'tooltipText', L["Note: This may be blocked by Blizzard's spam filter and cause problems. It is recommended to send to your party/raid/guild instead."],
												'hasArrow', true,
												'hasEditBox', true,
												'editBoxText', UnitExists("target") and UnitIsPlayer("target") and UnitIsFriend("player", "target") and UnitName("target") or nil,
												'editBoxFunc', sendNoteToPlayerFunc,
												'editBoxValidateFunc', sendNoteToPlayerValidate
											)
										end
									end
								end
							end,
							'dontHook', true
						)
					end
					poi = this
					if Dewdrop:IsOpen(poi) then
						Dewdrop:Close()
						poi:GetScript("OnEnter")(poi)
					else
						Dewdrop:Open(poi, dummy)
						Tablet:Close()
					end
				end
			end
		end
		frame:SetScript("OnClick", OnClick)
		
		frame:Hide()
		cache[frame] = true
		return newpoi(minimap)
	end
	
	local function delpoi(x)
		x:Hide()
		cache[x] = true
		return nil
	end
	
	local del = function(self, id)
		if id == 'del' then
			return
		end
		local x = rawget(self, id)
		self[id] = nil
		if x then
			delpoi(x)
		end
	end
	
	local __index = function(self, id)
		local frame = newpoi(self == minimapPois)
		self[id] = frame
		return frame
	end
	
	pois = setmetatable({ del = del }, { __index = __index })
	minimapPois = setmetatable({ del = del }, { __index = __index })
end

local cache = {}
function Cartographer_Notes:SetNote(zone, x, y, icon, creator, ...)
	AceLibrary.argCheck(self, zone, 2, "string")
	AceLibrary.argCheck(self, x, 3, "number")
	AceLibrary.argCheck(self, y, 4, "number")
	AceLibrary.argCheck(self, icon, 5, "string")
	AceLibrary.argCheck(self, creator, 6, "string")
	if not BZ:HasTranslation(zone) then
		if BZ:HasReverseTranslation(zone) then
			zone = BZ:GetReverseTranslation(zone)
		else
			error(("Trying to set a note with an unknown zone: %q"):format(zone), 2)
		end
	end
	if x < 0 or x > 1 then
		error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
	end
	if y < 0 or y > 1 then
		error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
	end
	
	local usingDB = self.externalDBs[creator] and creator or nil
	
	local id = getID(x, y)
	
	if usingDB and not self.externalDBs[creator][zone] then
		self.externalDBs[creator][zone] = {}
	end
	local zoneData = usingDB and self.externalDBs[creator][zone] or self.db.account.pois[zone]
	local oldData = rawget(zoneData, id)
	if type(oldData) == "table" then
		local tmp = oldData
		oldData = cache
		for k,v in pairs(tmp) do
			oldData[k] = v
		end
	end
	local k1 = ...
	if not k1 and usingDB then
		zoneData[id] = icon
	else
		local t
		if not zoneData[id] then
			zoneData[id] = {}
		end
		t = zoneData[id]
		for k,v in pairs(t) do
			t[k] = nil
		end
		if type(k1) ~= "table" then
			for i = 1, select('#', ...), 2 do
				local k = select(i, ...)
				if k then
					t[k] = select(i+1, ...)
				else
					break
				end
			end
		else
			for k,v in pairs(k1) do
				t[k] = v
			end
		end
		t.icon = icon
		if not usingDB then
			t.creator = creator
		end
		local oldId = t.oldId
		t.oldId = nil
		if oldId and oldId ~= id then
			local oldicon
			if rawget(zoneData, oldId) then
				oldicon = zoneData[oldId]
				zoneData[oldId] = nil
				if type(oldicon) == "table" then
					oldicon = oldicon.icon
				end
			end
			if zone == Cartographer:GetCurrentEnglishZoneName() then
				pois[id] = pois[oldId]
				pois[oldId] = nil
			end
			if oldicon then
				local oldx, oldy = getXY(oldId)
				self:TriggerEvent("CartographerNotes_NoteDeleted", zone, oldx, oldy, oldicon, usingDB)
			end
		end
		if t.title == getIconTitle(icon) or t.title == "" then
			t.title = nil
		end
		if t.titleR or t.titleG or t.titleB then
			local r, g, b = t.titleR or 1, t.titleG or 1, t.titleB or 1
			t.titleR, t.titleG, t.titleB = nil
			local colorID = getColorID(r, g, b)
			t.titleCol = colorID
		end
		if t.titleCol == whiteColorID then
			t.titleCol = nil
		end
		if t.info == "" then
			t.info = nil
		end
		if t.infoR or t.infoG or t.infoB then
			local r, g, b = t.infoR or 1, t.infoG or 1, t.infoB or 1
			t.infoR, t.infoG, t.infoB = nil
			local colorID = getColorID(r, g, b)
			t.infoCol = colorID
		end
		if t.infoCol == whiteColorID then
			t.infoCol = nil
		end
		if t.info2 == "" then
			t.info2 = nil
		end
		if t.info2R or t.info2G or t.info2B then
			local r, g, b = t.info2R or 1, t.info2G or 1, t.info2B or 1
			t.info2R, t.info2G, t.info2B = nil
			local colorID = getColorID(r, g, b)
			t.info2Col = colorID
		end
		if t.info2Col == whiteColorID then
			t.info2Col = nil
		end
		if usingDB then
			t.creator = nil
			t.manual = nil
		end
		if creator == "" then
			t.creator = nil
		end
		if next(t) == 'icon' and next(t, 'icon') == nil then
			zoneData[id] = t.icon
		end
	end
	local different = false
	local newData = zoneData[id]
	if type(oldData) ~= type(newData) then
		different = true
	elseif type(oldData) ~= "table" then
		different = oldData ~= newData
	else
		for k,v in pairs(oldData) do
			if newData[k] ~= v then
				different = true
				break
			end
		end
		if not different then
			for k,v in pairs(newData) do
				if oldData[k] ~= v then
					different = true
					break
				end
			end
		end
	end
	for k,v in pairs(cache) do
		cache[k] = nil
	end
	if not different then
		return false
	end
	zoneData[id] = zoneData[id] -- to stop from being stored in an __index'd table.
	
	self:TriggerEvent("CartographerNotes_NoteSet", zone, x, y, icon, creator, setNoteFromComm)
	
	forceNextMinimapUpdate = true
	if zone ~= Cartographer:GetCurrentEnglishZoneName() then
		return true
	end
	self:ShowNote(zone, id, usingDB)
	return true
end

function Cartographer_Notes:GetNote(zone, x, y)
	AceLibrary.argCheck(self, zone, 2, "string")
	AceLibrary.argCheck(self, x, 3, "number")
	AceLibrary.argCheck(self, y, 4, "number")
	if not BZ:HasTranslation(zone) then
		if BZ:HasReverseTranslation(zone) then
			zone = BZ:GetReverseTranslation(zone)
		else
			error(("Trying to get a note with an unknown zone: %q"):format(zone), 2)
		end
	end
	if x < 0 or x > 1 then
		error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
	end
	if y < 0 or y > 1 then
		error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
	end
	local id = getID(x, y)
	local data, db = getrawpoi(zone, id)
	if not data then
		return
	end
	
	return zone, x, y, type(data) == "string" and data or data.icon, db, data
end

function Cartographer_Notes:DeleteNote(zone, x, y)
	AceLibrary.argCheck(self, zone, 2, "string")
	AceLibrary.argCheck(self, x, 3, "number")
	AceLibrary.argCheck(self, y, 4, "nil", "number")
	if not BZ:HasTranslation(zone) then
		if BZ:HasReverseTranslation(zone) then
			zone = BZ:GetReverseTranslation(zone)
		else
			error(("Trying to destroy a note with an unknown zone: %q"):format(zone), 2)
		end
	end
	if y then
		if x < 0 or x > 1 then
			error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
		end
		if y < 0 or y > 1 then
			error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
		end
	end
	local id
	if not y then
		id = x
	else
		id = getID(x, y)
	end
	local t, db = getrawpoi(zone, id)
	if not t then
		error(("Cannot destroy note %q at %q if it does not exist"):format(id, zone), 2)
	end
	if db then
		self.externalDBs[db][zone][id] = nil
	else
		self.db.account.pois[zone][id] = nil
	end
	local icon = t
	if type(icon) == "table" then
		icon = icon.icon
	end
	self:TriggerEvent("CartographerNotes_NoteDeleted", zone, x, y, icon, db)
	if zone == Cartographer:GetCurrentEnglishZoneName() then
		pois:del(id)
	end
	forceNextMinimapUpdate = true
end

function Cartographer_Notes:ShowNote(zone, id, creator)
	if zone ~= Cartographer:GetCurrentEnglishZoneName() or not Cartographer:IsActive(self) then
		return
	end
	-- creator overrides
	local note_trans,note_scale
	
	if creator then
		local handler = self.handlers[creator]
		local data = getrawpoi(zone, id, creator)
		if not data then
			return
		end
		-- creator DB scaling and transparency overrides
		if handler and handler.GetNoteTransparency then
			note_trans = handler:GetNoteTransparency(zone,id,data)
			-- ensure transparency withing range
			if note_trans and (note_trans < 0 or note_trans > 1) then note_trans = nil end
		end
		-- per creator scaling overrides
		if handler and handler.GetNoteScaling then
			note_scale = handler:GetNoteScaling(zone,id,data)
			if note_scale and (note_scale < 0.5 or note_scale > 2) then note_scale = nil end
		end
		-- check note hidden or not
		if handler and handler.IsNoteHidden and handler:IsNoteHidden(zone, id, data) then
			return
		end
	end
	local data, db = getpoi(zone, id)
	local poi = pois[id]
	poi.zone = zone
	local icon
	local creator = db or data.creator or ""
	if type(data) == "string" then
		poi.title = getIconTitle(data)
		poi.titleCol = whiteColorID
		poi.info = ""
		poi.infoCol = whiteColorID
		poi.info2 = ""
		poi.infoCol = whiteColorID
		poi.icon = data
		poi.manual = false
		icon = data
	else
		poi.title = data.title or getIconTitle(data.icon)
		poi.titleCol = data.titleCol or whiteColorID
		poi.info = data.info or ""
		poi.infoCol = data.infoCol or whiteColorID
		poi.info2 = data.info2 or ""
		poi.info2Col = data.info2Col or whiteColorID
		poi.icon = data.icon
		poi.manual = data.manual or false
		icon = data.icon
	end
	poi.creator = creator
	poi.id = id
	if self.developing == creator then
		poi.manual = true
	end
	
	local button = zone == Cartographer:GetCurrentInstance() and Cartographer:GetInstanceWorldMapButton() or WorldMapButton
	
	poi:SetParent(button)
	if icon:find("^Interface\\") then
		if note_scale then
			poi:SetWidth(16*self.db.profile.iconSize*note_scale)
			poi:SetHeight(16*self.db.profile.iconSize*note_scale)			
		else
			poi:SetWidth(16*self.db.profile.iconSize)
			poi:SetHeight(16*self.db.profile.iconSize)
		end
		-- Note transparency overrides
		if note_trans then
			poi:SetAlpha(note_trans)
		else
			poi:SetAlpha(1)
		end
		poi.texture:SetTexture(icon)
		if icon:find("^Interface\\Icons\\") then
			poi.texture:SetTexCoord(0.05, 0.95, 0.05, 0.95)
		else
			poi.texture:SetTexCoord(0, 1, 0, 1)
		end
	else
		local t = icons[icon]
		if not t then
			t = icons.Unknown
		end
		if note_scale then
			poi:SetWidth(t.width*self.db.profile.iconSize*note_scale)
			poi:SetHeight(t.height*self.db.profile.iconSize*note_scale)			
		else
			poi:SetWidth(t.width*self.db.profile.iconSize)
			poi:SetHeight(t.height*self.db.profile.iconSize)
		end
		-- Note transparency overrides
		if note_trans then
			poi:SetAlpha(note_trans)
		else
			poi:SetAlpha(t.alpha)
		end
		poi.texture:SetTexture(t.path)
		poi.texture:SetTexCoord(t.cLeft, t.cRight, t.cTop, t.cBottom)
	end
	poi:Show()
	
	poi:ClearAllPoints()
	local x, y = getXY(id)
	poi:SetPoint("CENTER", button, "TOPLEFT", x * button:GetWidth(), -y * button:GetHeight())
end

local lastMap = nil
function Cartographer_Notes:ClearMap()
	for k,v in pairs(pois) do
		pois:del(k)
	end
	lastMap = nil
end

local function RefreshMap(value)
	local zone = Cartographer:GetCurrentEnglishZoneName()
	if not zone or not Cartographer:IsModuleActive(self) then
		self:ClearMap()
		return
	end
	if zone == lastMap and value == false then
		return
	end
	self:ClearMap()
	if rawget(self.db.account.pois, zone) then
		for id in pairs(self.db.account.pois[zone]) do
			self:ShowNote(zone, id, false)
		end
	end
	for k,v in pairs(self.externalDBs) do
		if rawget(v, zone) then
			for id in pairs(v[zone]) do
				self:ShowNote(zone, id, k)
			end
		end
	end
	lastMap = zone
	forceNextMinimapUpdate = true
end

function Cartographer_Notes:RefreshMap(value)
	self:ScheduleEvent("CartographerNotes_RefreshMap", RefreshMap, 0, value)
end

function Cartographer_Notes:ToggleWorldMap()
	self.hooks.ToggleWorldMap()
	if CartographerNotesNewNoteFrame then
		CartographerNotesNewNoteFrame:Hide()
	end
	if not WorldMapFrame:IsShown() then
		self:ClearMap()
	end
end

function Cartographer_Notes:RegisterNotesDatabase(name, db, handler)
	AceLibrary.argCheck(self, name, 2, "string")
	AceLibrary.argCheck(self, db, 3, "table")
	AceLibrary.argCheck(self, handler, 4, "table", "nil")
	if self.externalDBs[name] then
		error("Cannot register a database already registered.", 2)
	end
	upgradeDatabase(db)
	
	self.externalDBs[name] = db
	self.handlers[name] = handler
	
	for zone,zdata in pairs(self.db.account.pois) do
		if type(zdata) == "table" then
			for id,data in pairs(zdata) do
				if data.creator == name then
					zdata[id] = nil
					if not db[zone] then
						db[zone] = {}
					end
					if not rawget(db[zone], id) then
						db[zone][id] = data
						data.creator = nil
						data.manual = nil
						if next(data) == 'icon' and next(data, 'icon') == nil then
							db[zone][id] = data.icon
						end
					end
				end
			end
		end
	end
	
	if WorldMapFrame:IsShown() then
		local zone = Cartographer:GetCurrentEnglishZoneName()
		if rawget(db, zone) then
			for id in pairs(db[zone]) do
				self:ShowNote(zone, id, name)
			end
		end
	end
end

function Cartographer_Notes:UnregisterNotesDatabase(name)
	AceLibrary.argCheck(self, name, 2, "string")
	if not self.externalDBs[name] then
		error("Cannot unregister a database not registered.", 2)
	end
	local t = self.externalDBs[name]
	self.externalDBs[name] = nil
	self.handlers[name] = nil
	local zone = Cartographer:GetCurrentEnglishZoneName()
	if rawget(t, zone) then
		for id in pairs(t[zone]) do
			pois:del(id)
		end
	end
end

function Cartographer_Notes:EnableDevelopment(database)
	if self.developing then
		error("Cannot enable development on two databases.", 2)
	end
	AceLibrary.argCheck(self, database, 2, "string")
	self.developing = database
	
	if Cartographer:IsActive(self) then
		Cartographer:AddToMagnifyingGlass(("Alt-Right-Click on map to add a default note to %q"):format(database))
		
		self:RefreshMap()
	end
end

function Cartographer_Notes:SendNoteToPlayer(zone, x, y, player)
	AceLibrary.argCheck(self, zone, 2, "string")
	AceLibrary.argCheck(self, x, 3, "number")
	AceLibrary.argCheck(self, y, 4, "number")
	AceLibrary.argCheck(self, player, 5, "string")
	if not BZ:HasTranslation(zone) then
		if BZ:HasReverseTranslation(zone) then
			zone = BZ:GetReverseTranslation(zone)
		else
			error(("Trying to set a note with an unknown zone: %q"):format(zone), 2)
		end
	end
	if x < 0 or x > 1 then
		error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
	end
	if y < 0 or y > 1 then
		error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
	end
	if not player:find("^%a%a+$") then
		error(("Argument #5 in the wrong format, got %q"):format(player), 2)
	end
	
	local data, db = getrawpoi(zone, getID(x, y))
	
	if not data then
		AceLibrary.error(self, "Cannot send note %q(%s,%s). Does not exist", zone, x*100, y*100)
	end
	
	self:SendCommMessage("WHISPER", player, "NOTE", zone, x, y, db, data)
	return true
end

local function sendNote(distribution, zone, x, y)
	local data, db = getrawpoi(zone, getID(x, y))
	
	if not data then
		AceLibrary.error("Cannot send note %q(%s,%s). Does not exist", zone, x*100, y*100)
	end
	
	self:SendCommMessage(distribution, "NOTE", zone, x, y, db, data)
	return true
end

function Cartographer_Notes:SendNoteToGuild(zone, x, y)
	AceLibrary.argCheck(self, zone, 2, "string")
	AceLibrary.argCheck(self, x, 3, "number")
	AceLibrary.argCheck(self, y, 4, "number")
	if not BZ:HasTranslation(zone) then
		if BZ:HasReverseTranslation(zone) then
			zone = BZ:GetReverseTranslation(zone)
		else
			error(("Trying to set a note with an unknown zone: %q"):format(zone), 2)
		end
	end
	if x < 0 or x > 1 then
		error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
	end
	if y < 0 or y > 1 then
		error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
	end
	if not IsInGuild() then
		return false
	end
	return sendNote("GUILD", zone, x, y)
end

function Cartographer_Notes:SendNoteToParty(zone, x, y)
	AceLibrary.argCheck(self, zone, 2, "string")
	AceLibrary.argCheck(self, x, 3, "number")
	AceLibrary.argCheck(self, y, 4, "number")
	if not BZ:HasTranslation(zone) then
		if BZ:HasReverseTranslation(zone) then
			zone = BZ:GetReverseTranslation(zone)
		else
			error(("Trying to set a note with an unknown zone: %q"):format(zone), 2)
		end
	end
	if x < 0 or x > 1 then
		error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
	end
	if y < 0 or y > 1 then
		error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
	end
	if GetNumPartyMembers() == 0 then
		return false
	end
	return sendNote("PARTY", zone, x, y)
end

function Cartographer_Notes:SendNoteToRaid(zone, x, y)
	AceLibrary.argCheck(self, zone, 2, "string")
	AceLibrary.argCheck(self, x, 3, "number")
	AceLibrary.argCheck(self, y, 4, "number")
	if not BZ:HasTranslation(zone) then
		if BZ:HasReverseTranslation(zone) then
			zone = BZ:GetReverseTranslation(zone)
		else
			error(("Trying to set a note with an unknown zone: %q"):format(zone), 2)
		end
	end
	if x < 0 or x > 1 then
		error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
	end
	if y < 0 or y > 1 then
		error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
	end
	if GetNumRaidMembers() == 0 then
		return false
	end
	-- changed to group from raid, so in a BG raid notes work, out of a bg they still work
	return sendNote("GROUP", zone, x, y)
end

function Cartographer_Notes:SendNoteToGroup(zone, x, y)
	AceLibrary.argCheck(self, zone, 2, "string")
	AceLibrary.argCheck(self, x, 3, "number")
	AceLibrary.argCheck(self, y, 4, "number")
	if not BZ:HasTranslation(zone) then
		if BZ:HasReverseTranslation(zone) then
			zone = BZ:GetReverseTranslation(zone)
		else
			error(("Trying to set a note with an unknown zone: %q"):format(zone), 2)
		end
	end
	if x < 0 or x > 1 then
		error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
	end
	if y < 0 or y > 1 then
		error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
	end
	if GetNumRaidMembers() > 0 then
		return self:SendNoteToRaid(zone, x, y)
	elseif GetNumPartyMembers() > 0 then
		return self:SendNoteToParty(zone, x, y)
	end
	return false
end

function Cartographer_Notes:IsShowingCreator()
	return self.db.profile.showCreator
end

function Cartographer_Notes:ToggleShowingCreator(value)
	if value == nil then
		value = not self.db.profile.showCreator
	end
	self.db.profile.showCreator = value
end

function Cartographer_Notes:IsShowingYardsAway()
	return self.db.profile.showYardsAway
end

function Cartographer_Notes:ToggleShowingYardsAway(value)
	if value == nil then
		value = not self.db.profile.showYardsAway
	end
	self.db.profile.showYardsAway = value
end

function Cartographer_Notes:IsShowingMinimapIcons()
	return self.db.profile.showMinimapIcons
end

function Cartographer_Notes:ToggleShowingMinimapIcons(value)
	if value == nil then
		value = not self.db.profile.showMinimapIcons
	end
	self.db.profile.showMinimapIcons = value
	if Cartographer:IsModuleActive(self) then
		if not value then
			for id in pairs(minimapPois) do
				minimapPois:del(id)
			end
			self:CancelScheduledEvent("Cartographer-Notes-MinimapIcons")
			self:UnregisterEvent("MINIMAP_ZONE_CHANGED")
			self:UnregisterEvent("MINIMAP_UPDATE_ZOOM")
			self:UnregisterEvent("ZONE_CHANGED_NEW_AREA")
			self:UnregisterEvent("PLAYER_LEAVING_WORLD")
		else
			self:ScheduleRepeatingEvent("Cartographer-Notes-MinimapIcons", self.UpdateMinimapIcons, 0, self)
			self:RegisterEvent("MINIMAP_ZONE_CHANGED")
			self:RegisterEvent("MINIMAP_UPDATE_ZOOM")
			self:RegisterEvent("ZONE_CHANGED_NEW_AREA")
			self:RegisterEvent("PLAYER_LEAVING_WORLD")
			self:MINIMAP_UPDATE_ZOOM()
			self:MINIMAP_ZONE_CHANGED()
			self:ZONE_CHANGED_NEW_AREA()
		end
	end
end

function Cartographer_Notes:GetIconSize()
	return self.db.profile.iconSize
end

function Cartographer_Notes:SetIconSize(value)
	self.db.profile.iconSize = value
	if Cartographer:IsModuleActive(self) then
		self:RefreshMap()
	end
end

function Cartographer_Notes:GetMinimapIconSize()
	return self.db.profile.minimapIconSize
end

function Cartographer_Notes:SetMinimapIconSize(value)
	self.db.profile.minimapIconSize = value
	forceNextMinimapUpdate = true
end

function Cartographer_Notes:GetMinimapNotesPerDB()
	return self.db.profile.notesPerDB
end

function Cartographer_Notes:SetMinimapNotesPerDB(value)
	self.db.profile.notesPerDB = value
	forceNextMinimapUpdate = true
end

function Cartographer_Notes:IsShowingNotifications()
	return self.db.profile.showNotifications
end

function Cartographer_Notes:ToggleShowingNotifications(value)
	if value == nil then
		value = not self.db.profile.showNotifications
	end
	self.db.profile.showNotifications = value	  
end

function Cartographer_Notes:ClearAllNotes()
	local zone = Cartographer:GetCurrentEnglishZoneName()
	local zoneData
	zoneData = rawget(self.db.account.pois, zone)
	-- clear manual notes
	if zoneData and next(zoneData) then
		for i, data in pairs(zoneData) do
			local x, y = getXY(i)
			self:DeleteNote(zone, x, y)
		end
	end
	-- clear other db notes
	for k,v in pairs(self.externalDBs) do
		local zoneData = rawget(v, zone)
		if zoneData and next(zoneData) then
			for i,data in pairs(zoneData) do
				local x, y = getXY(i)
				self:DeleteNote(zone, x, y)
			end
		end
	end
end

local cache = {}
local function iter(t)
	t.id = t.id + 1
	
	local notes = t.notes
	
	local id = notes[t.id]
	if id then
		local data = t.zoneData[id]
		
		local x, y = getXY(id)
		
		return t.zone, x, y, type(data) == "string" and data or data.icon, t.creator, data
	end
	
	cache[t] = true
	for k in pairs(t) do
		t[k] = nil
	end
	cache[notes] = true
	for k in pairs(notes) do
		notes[k] = nil
	end
	return nil
end

local function retNil()
	return nil
end

local current_x, current_y
local current_using_yards
local function my_sort(alpha, bravo)
	if not alpha or not bravo then
		return false
	end
	local a_x, a_y = getXY(alpha)
	local b_x, b_y = getXY(bravo)
	a_x, a_y = a_x - current_x, a_y - current_y
	b_x, b_y = b_x - current_x, b_y - current_y
	if current_using_yards then
		a_y = a_y * 2/3
		b_y = b_y * 2/3
	end
	return a_x*a_x + a_y*a_y < b_x*b_x + b_y*b_y
end

function Cartographer_Notes:IterateNearbyNotes(zone, x, y, radius, creator, max_notes, use_yards)
	AceLibrary.argCheck(self, zone, 2, "string")
	AceLibrary.argCheck(self, x, 3, "number")
	AceLibrary.argCheck(self, y, 4, "number")
	AceLibrary.argCheck(self, radius, 5, "number", "nil")
	AceLibrary.argCheck(self, creator, 6, "string", "nil")
	AceLibrary.argCheck(self, max_notes, 7, "number", "nil")
	AceLibrary.argCheck(self, use_yards, 8, "boolean", "nil")
	
	if not BZ:HasTranslation(zone) then
		if BZ:HasReverseTranslation(zone) then
			zone = BZ:GetReverseTranslation(zone)
		else
			error(("Trying to iterate through notes in an unknown zone: %q"):format(zone), 2)
		end
	end
	if x < 0 or x > 1 then
		error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
	end
	if y < 0 or y > 1 then
		error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
	end
	if not radius then
		radius = 1/0
	end
	if radius < 0 then
		error(("Argument #5 is expected to be [0, inf), got %s"):format(radius), 2)
	end
	if creator and not self.externalDBs[creator] then
		error(("Database %q not registered."):format(creator), 2)
	end
	
	local zoneData
	if creator then
		zoneData = rawget(self.externalDBs[creator], zone)
	else
		zoneData = rawget(self.db.account.pois, zone)
	end
	if not zoneData or not next(zoneData) then
		return retNil
	end
	
	local radius_2 = radius*radius
	
	local notes = next(cache) or {}
	cache[notes] = nil
	local yardWidthMult, yardHeightMult = 1, 1
	if use_yards then
		yardWidthMult, yardHeightMult = Tourist:GetZoneYardSize(BZ[zone])
		if not yardWidthMult then
			yardWidthMult, yardHeightMult = 1000, 1000 * 2/3
		end
	end
	for i, data in pairs(zoneData) do
		local x_p, y_p = getXY(i)
		local x_d = (x_p - x) * yardWidthMult
		local y_d = (y_p - y) * yardHeightMult
		if x_d*x_d + y_d*y_d <= radius_2 then
			table.insert(notes, i)
		end
	end
	
	current_x, current_y = x, y
	current_using_yards = use_yards
	table.sort(notes, my_sort)
	current_x, current_y = nil, nil
	current_using_yards = nil
	
	if max_notes and max_notes > #notes then
		for i = max_notes + 1, #notes do
			notes[i] = nil
		end
	end
	
	local t = next(cache) or {}
	cache[t] = nil
	
	t.zoneData = zoneData
	t.zone = zone
	t.creator = creator
	t.notes = notes
	t.id = 0
	
	return iter, t, nil
end

function Cartographer_Notes:GetNearbyNote(zone, x, y, radius, creator, use_yards)
	AceLibrary.argCheck(self, zone, 2, "string")
	AceLibrary.argCheck(self, x, 3, "number")
	AceLibrary.argCheck(self, y, 4, "number")
	AceLibrary.argCheck(self, radius, 5, "number", "nil")
	AceLibrary.argCheck(self, creator, 6, "string", "nil")
	AceLibrary.argCheck(self, use_yards, 7, "boolean", "nil")
	
	if not BZ:HasTranslation(zone) then
		if BZ:HasReverseTranslation(zone) then
			zone = BZ:GetReverseTranslation(zone)
		else
			error(("Trying to set a note with an unknown zone: %q"):format(zone), 2)
		end
	end
	if x < 0 or x > 1 then
		error(("Argument #3 is expected to be [0, 1], got %s"):format(x), 2)
	end
	if y < 0 or y > 1 then
		error(("Argument #4 is expected to be [0, 1], got %s"):format(y), 2)
	end
	if not radius then
		radius = 1/0
	end
	if radius < 0 then
		error(("Argument #5 is expected to be [0, inf), got %s"):format(radius), 2)
	end
	if creator and not self.externalDBs[creator] then
		error(("Database %q not registered."):format(creator), 2)
	end
	
	local zoneData
	if creator then
		zoneData = rawget(self.externalDBs[creator], zone)
	else
		zoneData = rawget(self.db.account.pois, zone)
	end
	if not zoneData or not next(zoneData) then
		return
	end
	local radius_2 = radius*radius
	
	local close_distance = 1/0
	local close_id
	
	local yardWidthMult, yardHeightMult = 1, 1
	if use_yards then
		yardWidthMult, yardHeightMult = Tourist:GetZoneYardSize(BZ[zone])
		if not yardWidthMult then
			yardWidthMult, yardHeightMult = 1000, 1000 * 2/3
		end
	end
	
	for id, data in pairs(zoneData) do
		local x_p, y_p = getXY(id)
		
		local x_d = (x_p - x) * yardWidthMult
		local y_d = (y_p - y) * yardHeightMult
		
		local d_2 = x_d*x_d + y_d*y_d
		
		if d_2 <= radius_2 and d_2 < close_distance then
			close_distance = d_2
			close_id = id
		end
	end
	if not close_id then
		return
	end
	local x_p, y_p = getXY(close_id)
	local data = zoneData[close_id]
	return zone, x_p, y_p, type(data) == "string" and data or data.icon, creator, data
end

function Cartographer_Notes:OnProfileEnable()
	self:RefreshMap()
end

local MinimapSize = { -- radius of minimap
	indoor = {
		[0] = 150,
		[1] = 120,
		[2] = 90,
		[3] = 60,
		[4] = 40,
		[5] = 25,
	},
	outdoor = {
		[0] = 233 + 1/3,
		[1] = 200,
		[2] = 166 + 2/3,
		[3] = 133 + 1/6,
		[4] = 100,
		[5] = 66 + 2/3,
	},
}

local lastX, lastY = 1/0, 1/0
local lastZoom = -1
local lastTracking = nil
local indoors = 'outdoor'
local TRACK = {
	["Interface\\Icons\\Spell_Nature_Earthquake"] = "Mining",
	["Interface\\Icons\\INV_Misc_Flower_02"] = "Herbalism",
	["Interface\\Icons\\Racial_Dwarf_FindTreasure"] = "Treasure",
}
local FAKE_TRACK = {}
local function CheckToUpdateMinimapIcons(x_, y_, x, y, force)
	if isInInstance then
		return
	end
	if not x then
		return
	end
	local diffX = (lastX - x)
	local diffY = (lastY - y)
	local distance_2 = diffX*diffX + diffY*diffY
	if not force and not forceNextMinimapUpdate and distance_2 < 5^2 then
		return
	end
	forceNextMinimapUpdate = false
	lastX, lastY = x, y
	local radius = MinimapSize[indoors][lastZoom] - 10
	local radius_2 = radius*radius
	
	local zone = GetRealZoneText()
	
	for id, v in pairs(minimapPois) do
		if id ~= 'del' then
			local idX, idY = yardify(getXY(id))
			local diffX, diffY = (x - idX), (y - idY)
			local distance_2 = diffX*diffX + diffY*diffY
			if distance_2 > radius_2 or not getrawpoi(zone, id) then
				minimapPois:del(id)
			end
		end
	end
	
	local db = nil
	local notesPerDB = self.db.profile.notesPerDB
	while true do
		db = next(self.externalDBs, db)
		for zone, x, y, icon, creator, data in Cartographer_Notes:IterateNearbyNotes(zone, x_, y_, radius, db, notesPerDB, true) do
			local id = getID(x, y)
			
			local continue = false
			if creator then
				local handler = self.handlers[creator]
				local data = rawget(self.externalDBs[creator], zone)
				if handler and handler.IsTracking and handler:IsTracking(zone,id,data) then
					FAKE_TRACK[creator] = true
				elseif FAKE_TRACK[creator] then
					FAKE_TRACK[creator] = nil
				end
				if not data then
					continue = true
				else
					data = rawget(data, id)
					if not data then
						continue = true
					elseif handler and handler.IsNoteHidden and handler:IsNoteHidden(zone, id, data) then
						continue = true
					elseif handler and handler.IsMiniNoteHidden and handler:IsMiniNoteHidden(zone,id,data) then
						continue = true
					end
				end
				if continue and rawget(minimapPois, id) then
					minimapPois:del(id)
				end
			end
			if not continue then
				local poi = minimapPois[id]
				poi.zone = zone
				local icon
				local creator = db or data.creator or ""
				if type(data) == "string" then
					poi.title = getIconTitle(data)
					poi.titleCol = whiteColorID
					poi.info = ""
					poi.infoCol = whiteColorID
					poi.info2 = ""
					poi.info2Col = whiteColorID
					poi.icon = data
					poi.manual = false
					icon = data
				else
					poi.title = data.title or getIconTitle(data.icon)
					poi.titleCol = data.titleCol or whiteColorID
					poi.info = data.info or ""
					poi.infoCol = data.infoCol or whiteColorID
					poi.info2 = data.info2 or ""
					poi.info2Col = data.info2Col or whiteColorID
					poi.icon = data.icon
					poi.manual = data.manual or false
					icon = data.icon
				end
				poi.creator = creator
				poi.id = id
				if self.developing == creator then
					poi.manual = true
				end
				if creator and (creator == TRACK[lastTracking] or FAKE_TRACK[creator]) then
					local poiX, poiY = yardify(x, y)
					local diffX = lastX - poiX
					local diffY = lastY - poiY
					local distance_2 = diffX*diffX + diffY*diffY
					if distance_2 <= 100^2 then
						icon = "Interface\\AddOns\\Cartographer\\NotesArtwork\\TrackCircle"
					end
				end
				
				poi:SetParent(Minimap)
				if icon:find("^Interface\\") then
					poi:SetWidth(16*self.db.profile.minimapIconSize)
					poi:SetHeight(16*self.db.profile.minimapIconSize)
					poi:SetAlpha(1)
					poi.texture:SetTexture(icon)
					if icon:find("^Interface\\Icons\\") then
						poi.texture:SetTexCoord(0.05, 0.95, 0.05, 0.95)
					else
						poi.texture:SetTexCoord(0, 1, 0, 1)
					end
				else
					local t = icons[icon]
					if not t then
						t = icons.Unknown
					end
					poi:SetWidth(t.width*self.db.profile.minimapIconSize)
					poi:SetHeight(t.height*self.db.profile.minimapIconSize)
					poi:SetAlpha(t.alpha)
					poi.texture:SetTexture(t.path)
					poi.texture:SetTexCoord(t.cLeft, t.cRight, t.cTop, t.cBottom)
				end
				poi:Show()
			end
		end
		if db == nil then
			break
		end
	end
end

local Minimap = Minimap
local lastX, lastY = 1/0, 1/0
function Cartographer_Notes:UpdateMinimapIcons()
	if isInInstance then
		return
	end
	local x_, y_ = getCurrentPlayerPosition()
	local x, y = yardify(x_, y_)
	if not x then
		return
	end
	local zoom = Minimap:GetZoom()
	local diffZoom = zoom ~= lastZoom
	local tracking = GetTrackingTexture()
	local diffTracking = tracking ~= lastTracking
	if diffZoom or diffTracking or x ~= lastX or y ~= lastY or forceNextMinimapUpdate then
		lastZoom = zoom
		lastTracking = tracking
		local Minimap_Width = Minimap:GetWidth()/2
		lastX, lastY = x, y
		CheckToUpdateMinimapIcons(x_, y_, x, y, diffZoom)
		local radius = MinimapSize[indoors][lastZoom]
		for id, poi in pairs(minimapPois) do
			if id ~= 'del' then
				local px, py = yardify(getXY(id))
				local diffX = (px - x) / radius * Minimap_Width
				local diffY = (py - y) / radius * Minimap_Width
				poi:SetPoint("CENTER", Minimap, "CENTER", diffX, -diffY)
			end
		end
	end
end

function Cartographer_Notes:configureYards()
	forceNextMinimapUpdate = true
	currentYardWidth, currentYardHeight = Tourist:GetZoneYardSize(GetRealZoneText())
	if not currentYardWidth then
		currentYardWidth, currentYardHeight = 1000, 1000 * 2/3
	end
	isInInstance = Tourist:IsInstance(GetRealZoneText())
	if isInInstance then
		for k,v in pairs(minimapPois) do
			minimapPois:del(k)
		end
	end
end

function Cartographer_Notes:MINIMAP_ZONE_CHANGED()
	self:configureYards()
end

function Cartographer_Notes:MINIMAP_UPDATE_ZOOM()
	forceNextMinimapUpdate = true
	local zoom = Minimap:GetZoom()
	if GetCVar("minimapZoom") == GetCVar("minimapInsideZoom") then
		Minimap:SetZoom(zoom < 2 and zoom + 1 or zoom - 1)
	end
	indoors = GetCVar("minimapZoom")+0 == Minimap:GetZoom() and "outdoor" or "indoor"
	Minimap:SetZoom(zoom)
	self:MINIMAP_ZONE_CHANGED()
end

function Cartographer_Notes:PLAYER_LEAVING_WORLD()
	for k,v in pairs(minimapPois) do
		minimapPois:del(k)
	end
	forceNextMinimapUpdate = true
end

function Cartographer_Notes:ZONE_CHANGED_NEW_AREA()
	self:PLAYER_LEAVING_WORLD()
	self:MINIMAP_UPDATE_ZOOM()
	self:MINIMAP_ZONE_CHANGED()
end