notu 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/notu.mjs +141 -21
- package/dist/notu.umd.js +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/models/Page.d.ts +32 -0
- package/dist/types/notecomponents/NmlParser.d.ts +1 -0
- package/dist/types/services/HttpClient.d.ts +7 -0
- package/dist/types/services/Notu.d.ts +4 -0
- package/dist/types/services/NotuCache.d.ts +2 -0
- package/package.json +1 -1
package/dist/notu.mjs
CHANGED
|
@@ -58,6 +58,19 @@ class Notu {
|
|
|
58
58
|
async customJob(name, data) {
|
|
59
59
|
return await this.client.customJob(name, data);
|
|
60
60
|
}
|
|
61
|
+
async getPages() {
|
|
62
|
+
return (await this.client.getPages()).map((x) => this.cache.pageFromJSON(x));
|
|
63
|
+
}
|
|
64
|
+
async getPage(id) {
|
|
65
|
+
return this.cache.pageFromJSON(
|
|
66
|
+
await this.client.getPage(id)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
async savePage(page) {
|
|
70
|
+
return this.cache.pageFromJSON(
|
|
71
|
+
await this.client.savePage(page)
|
|
72
|
+
);
|
|
73
|
+
}
|
|
61
74
|
}
|
|
62
75
|
class ModelWithState {
|
|
63
76
|
constructor() {
|
|
@@ -307,6 +320,37 @@ class NotuHttpClient {
|
|
|
307
320
|
);
|
|
308
321
|
return this._validateResponseStatus(response), await response.json();
|
|
309
322
|
}
|
|
323
|
+
async getPages() {
|
|
324
|
+
const response = await this._fetch(
|
|
325
|
+
this.url + "/pages",
|
|
326
|
+
{
|
|
327
|
+
method: "GET",
|
|
328
|
+
headers: { Authorization: "Bearer " + this.token }
|
|
329
|
+
}
|
|
330
|
+
);
|
|
331
|
+
return this._validateResponseStatus(response), await response.json();
|
|
332
|
+
}
|
|
333
|
+
async getPage(id) {
|
|
334
|
+
const response = await this._fetch(
|
|
335
|
+
this.url + `/pages/${id}`,
|
|
336
|
+
{
|
|
337
|
+
method: "GET",
|
|
338
|
+
headers: { Authorization: "Bearer " + this.token }
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
return this._validateResponseStatus(response), await response.json();
|
|
342
|
+
}
|
|
343
|
+
async savePage(page) {
|
|
344
|
+
const response = await this._fetch(
|
|
345
|
+
this.url + "/pages",
|
|
346
|
+
{
|
|
347
|
+
method: "POST",
|
|
348
|
+
body: JSON.stringify(page),
|
|
349
|
+
headers: { Authorization: "Bearer " + this.token }
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
return this._validateResponseStatus(response), await response.json();
|
|
353
|
+
}
|
|
310
354
|
}
|
|
311
355
|
class NoteTag extends ModelWithState {
|
|
312
356
|
constructor(tag) {
|
|
@@ -620,6 +664,71 @@ class Note extends ModelWithState {
|
|
|
620
664
|
return !0;
|
|
621
665
|
}
|
|
622
666
|
}
|
|
667
|
+
class Page extends ModelWithState {
|
|
668
|
+
constructor() {
|
|
669
|
+
super(...arguments);
|
|
670
|
+
__publicField(this, "_id", 0);
|
|
671
|
+
__publicField(this, "_name", "");
|
|
672
|
+
__publicField(this, "_order", 0);
|
|
673
|
+
__publicField(this, "_group", null);
|
|
674
|
+
__publicField(this, "_space", null);
|
|
675
|
+
__publicField(this, "_query", null);
|
|
676
|
+
}
|
|
677
|
+
get id() {
|
|
678
|
+
return this._id;
|
|
679
|
+
}
|
|
680
|
+
set id(value) {
|
|
681
|
+
if (!this.isNew)
|
|
682
|
+
throw Error("Cannot change the id of a Page once it has already been created.");
|
|
683
|
+
this._id = value;
|
|
684
|
+
}
|
|
685
|
+
get name() {
|
|
686
|
+
return this._name;
|
|
687
|
+
}
|
|
688
|
+
set name(value) {
|
|
689
|
+
value !== this._name && (this._name = value, this.isClean && this.dirty());
|
|
690
|
+
}
|
|
691
|
+
get order() {
|
|
692
|
+
return this._order;
|
|
693
|
+
}
|
|
694
|
+
set order(value) {
|
|
695
|
+
value !== this._order && (this._order = value, this.isClean && this.dirty());
|
|
696
|
+
}
|
|
697
|
+
get group() {
|
|
698
|
+
return this._group;
|
|
699
|
+
}
|
|
700
|
+
set group(value) {
|
|
701
|
+
value !== this._group && (this._group = value, this.isClean && this.dirty());
|
|
702
|
+
}
|
|
703
|
+
get space() {
|
|
704
|
+
return this._space;
|
|
705
|
+
}
|
|
706
|
+
set space(value) {
|
|
707
|
+
var _a;
|
|
708
|
+
if (value !== this._space) {
|
|
709
|
+
const idChanged = (value == null ? void 0 : value.id) != ((_a = this._space) == null ? void 0 : _a.id);
|
|
710
|
+
this._space = value, this.isClean && idChanged && this.dirty();
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
get query() {
|
|
714
|
+
return this._query;
|
|
715
|
+
}
|
|
716
|
+
set query(value) {
|
|
717
|
+
value !== this._query && (this._query = value, this.isClean && this.dirty());
|
|
718
|
+
}
|
|
719
|
+
toJSON() {
|
|
720
|
+
var _a;
|
|
721
|
+
return {
|
|
722
|
+
state: this.state,
|
|
723
|
+
id: this.id,
|
|
724
|
+
name: this.name,
|
|
725
|
+
order: this.order,
|
|
726
|
+
group: this.group,
|
|
727
|
+
spaceId: (_a = this.space) == null ? void 0 : _a.id,
|
|
728
|
+
query: this.query
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
}
|
|
623
732
|
class SpaceLink extends ModelWithState {
|
|
624
733
|
constructor() {
|
|
625
734
|
super(...arguments);
|
|
@@ -728,6 +837,10 @@ class NotuCache {
|
|
|
728
837
|
}
|
|
729
838
|
return note;
|
|
730
839
|
}
|
|
840
|
+
pageFromJSON(pageData) {
|
|
841
|
+
const page = new Page();
|
|
842
|
+
return page.id = pageData.id, page.name = pageData.name, page.order = pageData.order, page.group = pageData.group, page.space = this.getSpace(pageData.spaceId), page.query = pageData.query, page.state = pageData.state, page;
|
|
843
|
+
}
|
|
731
844
|
getSpaces() {
|
|
732
845
|
return Array.from(this._spaces.values());
|
|
733
846
|
}
|
|
@@ -899,28 +1012,29 @@ function parseNmlTextFragment(wd) {
|
|
|
899
1012
|
wd.addTextUpTo(openStart);
|
|
900
1013
|
}
|
|
901
1014
|
function parseNmlElement(wd) {
|
|
902
|
-
const nameRegex = /<(\/)?\s*(\w[\w\d]*)\s*
|
|
1015
|
+
const nameRegex = /<(\/)?\s*(\w[\w\d]*)\s*(\/?>|\s\w)/y;
|
|
903
1016
|
nameRegex.lastIndex = wd.textIndex;
|
|
904
1017
|
const nameMatch = nameRegex.exec(wd.text);
|
|
905
1018
|
if (!nameMatch || nameMatch.index != wd.textIndex) {
|
|
906
1019
|
wd.resolveStack(), wd.addTextUpTo(wd.textIndex + 1);
|
|
907
1020
|
return;
|
|
908
1021
|
}
|
|
909
|
-
const isClosing = nameMatch[1] != null, newElement = new NmlElement();
|
|
1022
|
+
const isClosing = nameMatch[1] != null, afterTagName = nameMatch[3], newElement = new NmlElement();
|
|
910
1023
|
newElement.tag = nameMatch[2], newElement.startIndex = wd.textIndex;
|
|
911
|
-
let index = wd.textIndex + nameMatch[0].length -
|
|
912
|
-
for (
|
|
913
|
-
const attributeRegex = /(
|
|
1024
|
+
let index = wd.textIndex + nameMatch[0].length - afterTagName.length;
|
|
1025
|
+
for (; ; ) {
|
|
1026
|
+
const attributeRegex = /(\/?>)|\s+(\w[\w\d]*)(\s*(=)\s*\"|\s+\w|\s*\/?>)/y;
|
|
914
1027
|
attributeRegex.lastIndex = index;
|
|
915
1028
|
const attributeMatch = attributeRegex.exec(wd.text);
|
|
916
|
-
if (attributeMatch
|
|
1029
|
+
if (!attributeMatch || attributeMatch.index != index) {
|
|
917
1030
|
wd.resolveStack(), wd.addTextUpTo(index);
|
|
918
1031
|
return;
|
|
919
1032
|
}
|
|
920
|
-
|
|
1033
|
+
const closingBracket = attributeMatch[1], attributeName = attributeMatch[2], afterAttributeName = attributeMatch[3], hasEqualSign = attributeMatch[4] == "=";
|
|
1034
|
+
if (closingBracket == ">") {
|
|
921
1035
|
wd.textIndex = attributeMatch.index + attributeMatch[0].length, isClosing ? newElement.closeText = wd.text.substring(newElement.startIndex, wd.textIndex) : newElement.openText = wd.text.substring(newElement.startIndex, wd.textIndex), wd.addElement(newElement);
|
|
922
1036
|
return;
|
|
923
|
-
} else if (
|
|
1037
|
+
} else if (closingBracket == "/>") {
|
|
924
1038
|
if (isClosing) {
|
|
925
1039
|
wd.resolveStack(), wd.addTextUpTo(attributeMatch.index + attributeMatch[0].length);
|
|
926
1040
|
return;
|
|
@@ -928,26 +1042,31 @@ function parseNmlElement(wd) {
|
|
|
928
1042
|
wd.textIndex = attributeMatch.index + attributeMatch[0].length, newElement.openText = wd.text.substring(newElement.startIndex, attributeMatch.index + attributeMatch[0].length), newElement.isSelfClosing = !0, wd.addElement(newElement);
|
|
929
1043
|
return;
|
|
930
1044
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
wd.resolveStack(), wd.addTextUpTo(index);
|
|
1045
|
+
if (hasEqualSign) {
|
|
1046
|
+
const attributeValueStartIndex = attributeMatch.index + attributeMatch[0].length, attributeValue = parseAttributeValue(wd, attributeMatch.index + attributeMatch[0].length);
|
|
1047
|
+
if (attributeValue == null) {
|
|
1048
|
+
wd.resolveStack(), wd.addTextUpTo(attributeValueStartIndex);
|
|
936
1049
|
return;
|
|
937
1050
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1051
|
+
newElement.attributes[attributeName] = attributeValue.replace(/\/"/g, '"'), index = attributeValueStartIndex + attributeValue.length + 1;
|
|
1052
|
+
} else
|
|
1053
|
+
newElement.attributes[attributeName] = !0, index = attributeMatch.index + attributeMatch[0].length - afterAttributeName.length;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
function parseAttributeValue(wd, startIndex) {
|
|
1057
|
+
let currentIndex = startIndex;
|
|
1058
|
+
for (; ; ) {
|
|
1059
|
+
const nextIndex = wd.text.indexOf('"', currentIndex);
|
|
1060
|
+
if (nextIndex == -1)
|
|
1061
|
+
return null;
|
|
1062
|
+
if (!isSlash(wd.text.charCodeAt(nextIndex - 1)))
|
|
1063
|
+
return wd.text.substring(startIndex, nextIndex);
|
|
1064
|
+
currentIndex = nextIndex + 1;
|
|
943
1065
|
}
|
|
944
1066
|
}
|
|
945
1067
|
function isSlash(charCode) {
|
|
946
1068
|
return charCode == 47;
|
|
947
1069
|
}
|
|
948
|
-
function isSpace(charCode) {
|
|
949
|
-
return charCode == 32;
|
|
950
|
-
}
|
|
951
1070
|
function splitNoteTextIntoComponents(note, notu, componentProcessors, textComponentFactory, paragraphComponentFactory) {
|
|
952
1071
|
const xmlData = parseNml(note.text), components = [];
|
|
953
1072
|
async function save() {
|
|
@@ -1095,6 +1214,7 @@ export {
|
|
|
1095
1214
|
NotuCache,
|
|
1096
1215
|
NotuHttpCacheFetcher,
|
|
1097
1216
|
NotuHttpClient,
|
|
1217
|
+
Page,
|
|
1098
1218
|
ParsedGrouping,
|
|
1099
1219
|
ParsedQuery,
|
|
1100
1220
|
ParsedTag,
|
package/dist/notu.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(global,factory){typeof exports=="object"&&typeof module<"u"?factory(exports):typeof define=="function"&&define.amd?define(["exports"],factory):(global=typeof globalThis<"u"?globalThis:global||self,factory(global.notu={}))})(this,function(exports2){"use strict";var __defProp=Object.defineProperty;var __defNormalProp=(obj,key,value)=>key in obj?__defProp(obj,key,{enumerable:!0,configurable:!0,writable:!0,value}):obj[key]=value;var __publicField=(obj,key,value)=>(__defNormalProp(obj,typeof key!="symbol"?key+"":key,value),value);class Notu{constructor(client,cache){__publicField(this,"_client");__publicField(this,"_cache");this._client=client,this._cache=cache}get client(){return this._client}get cache(){return this._cache}async login(username,password){return await this.client.login(username,password)}async setup(){return await this.client.setup()}getSpaces(){return this.cache.getSpaces()}getSpace(id){return this.cache.getSpace(id)}getSpaceByName(name){return this.cache.getSpaceByName(name)}async saveSpace(space){const spaceData=await this.client.saveSpace(space);return this.cache.spaceSaved(spaceData)}getTags(space=null,includeOtherSpacePublics=!1,includeOtherSpaceCommons=!1){return this.cache.getTags(space,includeOtherSpacePublics,includeOtherSpaceCommons)}getTag(id){return this.cache.getTag(id)}getTagByName(name,space){return this.cache.getTagByName(name,space)}async getNotes(query,spaceId){return(await this.client.getNotes(query,spaceId)).map(n=>this.cache.noteFromJSON(n))}async getNoteCount(query,spaceId){return await this.client.getNoteCount(query,spaceId)}async saveNotes(notes){const tagsBeingDeletedData=notes.filter(x=>!!x.ownTag).filter(x=>x.isDeleted||x.ownTag.isDeleted).map(x=>x.ownTag.toJSON()),notesData=await this.client.saveNotes(notes);for(const noteData of notesData.filter(x=>!!x.ownTag&&!x.ownTag.isDeleted))noteData.ownTag.links=noteData.tags.map(x=>x.tagId),this.cache.tagSaved(noteData.ownTag);for(const tagData of tagsBeingDeletedData)tagData.state="DELETED",this.cache.tagSaved(tagData);return notes=notesData.map(n=>this.cache.noteFromJSON(n)),notes}async customJob(name,data){return await this.client.customJob(name,data)}}class ModelWithState{constructor(){__publicField(this,"state","NEW")}new(){return this.state="NEW",this}clean(){return this.state="CLEAN",this}dirty(){return this.state="DIRTY",this}delete(){return this.state="DELETED",this}get isNew(){return this.state=="NEW"}get isClean(){return this.state=="CLEAN"}get isDirty(){return this.state=="DIRTY"}get isDeleted(){return this.state=="DELETED"}validate(throwError=!1){return!0}}class Space extends ModelWithState{constructor(name=""){super();__publicField(this,"_id",0);__publicField(this,"_name","");__publicField(this,"_internalName","");__publicField(this,"_version","0.0.1");__publicField(this,"_useCommonSpace",!1);__publicField(this,"_settings");__publicField(this,"_links",[]);this._name=name}get id(){return this._id}set id(value){if(!this.isNew)throw Error("Cannot change the id of a Space once it has already been created.");this._id=value}get name(){return this._name}set name(value){value!==this._name&&(this._name=value,this.isClean&&this.dirty())}get internalName(){return this._internalName}set internalName(value){if(!this.isNew&&value!=this.internalName)throw Error("Cannot change the internal name of a space after it has already been saved.");value!=this._internalName&&(this._internalName=value,this.isClean&&this.dirty())}get version(){return this._version}set version(value){value!==this._version&&(this._version=value,this.isClean&&this.dirty())}v(version){return this.version=version,this}get useCommonSpace(){return this._useCommonSpace}set useCommonSpace(value){value!==this._useCommonSpace&&(this._useCommonSpace=value,this.isClean&&this.dirty())}get settings(){return this._settings}set settings(value){this._settings=value,this.isClean&&this.dirty()}withSettings(settings){return this.settings=settings,this}get links(){return this._links.filter(x=>!x.isDeleted)}get linksPendingDeletion(){return this._links.filter(x=>x.isDeleted)}addLink(link){if(link.isDeleted)throw Error("Cannot add a link marked as deleted to a space");if(link.isNew)throw Error("Cannot add a link that hasn't yet been saved to a space");let existing=this._links.find(x=>x.name==link.name);if(existing){if(existing.isDeleted)return existing.dirty(),this;throw Error("The space already contains a link with this name")}return this._links.push(link),this}removeLink(name){const link=this._links.find(x=>x.name==name);return link?(link.isNew?this._links=this._links.filter(x=>x!==link):link.delete(),this):this}duplicate(){const output=new Space;return output.id=this.id,output.name=this.name,output.internalName=this.internalName,output.version=this.version,output.useCommonSpace=this.useCommonSpace,this.settings&&(output._settings=JSON.parse(JSON.stringify(this.settings))),output._links=this.links.map(x=>x.duplicate()),output.state=this.state,output}duplicateAsNew(){const output=new Space;return output.name=this.name,output.internalName=this.internalName,output.version=this.version,output.useCommonSpace=this.useCommonSpace,this.settings&&(output._settings=JSON.parse(JSON.stringify(this.settings))),output._links=this.links.map(x=>x.duplicateAsNew()),output}validate(throwError=!1){let output=null;if(!this.isNew&&this.id<=0&&(output="Space id must be greater than zero if in non-new state."),throwError&&output!=null)throw Error(output);return output==null}toJSON(){return{state:this.state,id:this.id,name:this.name,internalName:this.internalName,version:this.version,useCommonSpace:this.useCommonSpace,settings:this.settings,links:this._links.map(x=>x.toJSON())}}}class NotuHttpClient{constructor(url,fetchMethod=null){__publicField(this,"_url",null);__publicField(this,"_token",null);__publicField(this,"_fetch");__publicField(this,"errorHandler",null);if(!url)throw Error("Endpoint URL must be passed in to NotuClient constructor");url.endsWith("/")&&(url=url.substring(0,url.length-1)),this._url=url,this._fetch=fetchMethod??window.fetch.bind(window)}get url(){return this._url}get token(){return this._token}set token(value){this._token=value}_validateResponseStatus(response){if(response.status>=400&&response.status<600){if(this.errorHandler&&this.errorHandler(response))return;throw Error(response.statusText)}}async login(username,password){const response=await this._fetch(this.url+"/login",{method:"POST",body:JSON.stringify({username,password})});if(this._validateResponseStatus(response),response.body!=null){const result=await response.json();return result&&(this._token=result),result}throw Error("Unknown error occurred on the server")}async setup(){const response=await this._fetch(this.url+"/setup",{method:"POST",headers:{Authorization:"Bearer "+this.token}});this._validateResponseStatus(response),await response.json()}async saveSpace(space){const response=await this._fetch(this.url+"/spaces",{method:"POST",body:JSON.stringify(space),headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}async getNotes(query,space){space&&space instanceof Space&&(space=space.id);const response=await this._fetch(this.url+`/notes?${space?`space=${space}&`:""}query=${encodeURIComponent(query)}`,{method:"GET",headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}async getNoteCount(query,space){space instanceof Space&&(space=space.id);const response=await this._fetch(this.url+`/notes?count=true&space=${space}&query=${encodeURIComponent(query)}`,{method:"GET",headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),(await response.json()).count}async saveNotes(notes){const response=await this._fetch(this.url+"/notes",{method:"POST",body:JSON.stringify(notes),headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}async customJob(name,data){const response=await this._fetch(this.url+"/customJob",{method:"POST",body:JSON.stringify({name,data,clientTimezone:Intl.DateTimeFormat().resolvedOptions().timeZone}),headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}}class NoteTag extends ModelWithState{constructor(tag){super();__publicField(this,"_tag");__publicField(this,"_data");if(!tag)throw Error("Cannot instanciate new NoteTag without a passed in tag.");if(tag.isNew)throw Error("Cannot create a NoteTag object for a tag that hasn't been saved yet.");if(tag.isDeleted)throw Error("Cannot create a NoteTag object for a tag marked as deleted.");this._tag=tag}get tag(){return this._tag}get data(){return this._data}set data(value){this._data=value,this.isClean&&this.dirty()}withData(data){return this.data=data,this}duplicate(){const output=this.duplicateAsNew();return output.state=this.state,output}duplicateAsNew(){const output=new NoteTag(this.tag);return this.data&&(output._data=JSON.parse(JSON.stringify(this.data))),output}validate(throwError=!1){function exit(message){if(throwError&&message!=null)throw Error(message);return message==null}return this.tag?!0:exit("NoteTag must have a tag set.")}toJSON(){return{state:this.state,tagId:this.tag.id,data:this.data}}}class Tag extends ModelWithState{constructor(name=""){super();__publicField(this,"_id",0);__publicField(this,"_space",null);__publicField(this,"_name","");__publicField(this,"_color",null);__publicField(this,"_availability",0);__publicField(this,"_isInternal",!1);__publicField(this,"links",[]);this._name=name}get id(){return this._id}set id(value){if(!this.isNew)throw Error("Cannot change the id of a Tag once it has already been created.");this._id=value}get space(){return this._space}set space(value){var _a;if(value!==this._space){const idChanged=(value==null?void 0:value.id)!=((_a=this._space)==null?void 0:_a.id);this._space=value,this.isClean&&idChanged&&this.dirty()}}in(space){return this.space=space,this}get name(){return this._name}set name(value){value!==this._name&&(this._name=value,this.isClean&&this.dirty())}getFullName(){return`${this.space.name}.${this.name}`}getQualifiedName(contextSpaceId){var _a;return contextSpaceId==((_a=this.space)==null?void 0:_a.id)?this.name:this.getFullName()}getUniqueName(cache){return cache.getTagsByName(this.name).length==1?this.name:this.getFullName()}get color(){return this._color}set color(value){value!==this._color&&(this._color=value,this.isClean&&this.dirty())}get availability(){return this._availability}set availability(value){if(!this.isNew&&value<this.availability)throw Error("Cannot change a tag to private once its already been saved.");value!=this._availability&&(this._availability=value,this.isClean&&this.dirty())}get isPrivate(){return this._availability==0}get isCommon(){return this._availability==1}get isPublic(){return this.availability==2}asPrivate(){return this.availability=0,this}asCommon(){return this.availability=1,this}asPublic(){return this.availability=2,this}get isInternal(){return this._isInternal}set isInternal(value){if(!this.isNew&&value!=this.isInternal)throw Error("Cannot change whether a tag is internal or not once it has already been saved.");value!=this._isInternal&&(this._isInternal=value,this.isClean&&this.dirty())}asInternal(){return this.isInternal=!0,this}linksTo(tag){return!!this.links.find(x=>x==tag)}duplicate(){const output=this.duplicateAsNew();return output.id=this.id,output.state=this.state,output}duplicateAsNew(){const output=new Tag(this.name);return output.color=this.color,output.space=this.space,output.availability=this.availability,output.isInternal=this.isInternal,output.links=this.links.slice(),output}validate(throwError=!1){let output=null;if(!this.isNew&&this.id<=0?output="Tag id must be greater than zero if in non-new state.":!this.name||!/^[a-zA-Z][a-zA-Z0-9 ]*[a-zA-Z0-9]?$/.test(this.name)?output="Tag name is invalid, must only contain letters, numbers, and spaces, starting with a letter":this.color&&!/^#?[A-z0-9]{6}$/.test(this.color)&&(output="Tag color is invalid, must be a 6 character hexadecimal."),throwError&&output!=null)throw Error(output);return output==null}toJSON(){var _a;return{state:this.state,id:this.id,name:this.name,spaceId:(_a=this.space)==null?void 0:_a.id,color:this.color,availability:this.availability,isInternal:this.isInternal,links:this.links.map(x=>x.id)}}}class Note extends ModelWithState{constructor(text,ownTag){super();__publicField(this,"_id",0);__publicField(this,"_date",new Date);__publicField(this,"_text","");__publicField(this,"_space",null);__publicField(this,"_ownTag",null);__publicField(this,"_group");__publicField(this,"_tags",[]);text&&(this.text=text),this._ownTag=ownTag}get id(){return this._id}set id(value){if(!this.isNew)throw Error("Cannot change the id of a Note once it has already been created.");this._id=value,this.ownTag&&this.ownTag.id!=value&&(this.ownTag.id=value)}get date(){return this._date}set date(value){value!==this._date&&(this._date=value,this.isClean&&this.dirty())}at(value){return this.date=value,this}get text(){return this._text}set text(value){value!==this._text&&(this._text=value,this.isClean&&this.dirty())}get space(){return this._space}set space(value){var _a;if(value!==this._space){const idChanged=(value==null?void 0:value.id)!=((_a=this._space)==null?void 0:_a.id);this._space=value,this.isClean&&idChanged&&this.dirty(),this._setOwnTagSpace()}}in(space){return this.space=space,this}get ownTag(){return this._ownTag}setOwnTag(tagName){return this.ownTag==null?(this._ownTag=new Tag(tagName),this.ownTag.id=this.id):this.ownTag.isDeleted&&this.ownTag.dirty(),this.ownTag.name=tagName,this._setOwnTagSpace(),this}removeOwnTag(){return this.ownTag?(this.ownTag.isNew?this._ownTag=null:this.ownTag.delete(),this):this}_setOwnTagSpace(){this.ownTag&&this.space&&(this.ownTag.space=this.space)}get group(){return this._group}set group(value){this._group=value}get tags(){return this._tags.filter(x=>!x.isDeleted)}get tagsPendingDeletion(){return this._tags.filter(x=>x.isDeleted)}addTag(tag){if(tag.isDeleted)throw Error("Cannot add a tag marked as deleted to a note");if(tag.isNew)throw Error("Cannot add a tag that hasn't yet been saved to a note");if(tag.id==this.id)throw Error("Note cannot add its own tag as a linked tag");if(tag.isPrivate&&tag.space.id!=this.space.id)throw Error("Cannot add a private tag from another space");let nt=this._tags.find(x=>x.tag.id==tag.id);return nt?(nt.isDeleted&&nt.dirty(),nt):(nt=new NoteTag(tag),this._tags.push(nt),nt)}removeTag(tag){const nt=this._tags.find(x=>x.tag.id==tag.id);return nt?(nt.isNew?this._tags=this._tags.filter(x=>x!==nt):nt.delete(),this):this}getTag(tag,space=null){return tag instanceof Tag?this.tags.find(x=>x.tag===tag):(space&&space instanceof Space&&(space=space.id),space!=null?this.tags.find(x=>x.tag.name==tag&&x.tag.space.id==space):this.tags.find(x=>x.tag.name==tag&&x.tag.space.id==this.space.id))}getTagData(tag,type){const nt=this.getTag(tag);return nt?new type(nt):null}duplicate(){var _a;const output=new Note(this.text,(_a=this.ownTag)==null?void 0:_a.duplicate()).at(this.date).in(this.space);return output._tags=this.tags.map(x=>x.duplicate()),output.id=this.id,output.state=this.state,output}duplicateAsNew(){const output=new Note(this.text).at(this.date).in(this.space);return output._tags=this.tags.map(x=>x.duplicateAsNew()),output}toJSON(){var _a;return{state:this.state,id:this.id,date:this.date,text:this.text,spaceId:this.space.id,ownTag:(_a=this.ownTag)==null?void 0:_a.toJSON(),tags:this._tags.map(x=>x.toJSON())}}validate(throwError=!1){function exit(message){if(throwError&&message!=null)throw Error(message);return message==null}if(this.space){if(!this.isNew&&this.id<=0)return exit("Note id must be greater than zero if in non-new state.");if(this.ownTag&&this.ownTag.space.id!=this.space.id)return exit("Note cannot belong to a different space than its own tag")}else return exit("Note must belong to a space.");if(this.ownTag&&!this.ownTag.validate(throwError))return!1;for(const nt of this._tags)if(!nt.validate(throwError))return!1;return!0}}class SpaceLink extends ModelWithState{constructor(){super(...arguments);__publicField(this,"_name");__publicField(this,"_toSpace")}get name(){return this._name}set name(value){value!==this._name&&(this._name=value,this.isClean&&this.dirty())}get toSpace(){return this._toSpace}set toSpace(value){var _a;if((value==null?void 0:value.id)!==((_a=this._toSpace)==null?void 0:_a.id)){if(value.isNew)throw Error("Cannot create a link to a space that hasn't been saved yet");if(value.isDeleted)throw Error("Cannot create a link to a space marked as deleted");this._toSpace=value,this.isClean&&this.dirty()}else this._toSpace=value}duplicate(){const output=this.duplicateAsNew();return output.state=this.state,output}duplicateAsNew(){const output=new SpaceLink;return output.name=this.name,output.toSpace=this.toSpace,output}toJSON(){var _a;return{state:this.state,name:this.name,toSpaceId:(_a=this.toSpace)==null?void 0:_a.id}}}class NotuCache{constructor(fetcher){__publicField(this,"_fetcher");__publicField(this,"_spaces",null);__publicField(this,"_tags",null);__publicField(this,"_tagNames",null);if(!fetcher)throw Error("NotuCache constructor must have a fetcher argument supplied.");this._fetcher=fetcher}async populate(){await this._populateSpaces(),await this._populateTags(),this._populateTagNames()}async _populateSpaces(){const spacesData=await this._fetcher.getSpacesData(),allSpaces=new Map;for(const spaceData of spacesData){const space=this.spaceFromJSON(spaceData);allSpaces.set(space.id,space),spaceData.space=space}this._spaces=allSpaces;for(const spaceData of spacesData)this._populateSpaceLinks(spaceData.space,spaceData)}spaceFromJSON(spaceData){const space=new Space(spaceData.name);return space.internalName=spaceData.internalName,space.id=spaceData.id,space.version=spaceData.version,space.useCommonSpace=spaceData.useCommonSpace,space.settings=spaceData.settings,space.state=spaceData.state,this._spaces&&this._populateSpaceLinks(space,spaceData),space}_populateSpaceLinks(space,spaceData){for(const linkData of spaceData.links){const link=new SpaceLink;link.name=linkData.name,link.toSpace=this._spaces.get(linkData.toSpaceId),link.clean(),space.addLink(link)}}async _populateTags(){const tagsData=await this._fetcher.getTagsData(),allTags=new Map;for(const tagData of tagsData){const tag=this.tagFromJSON(tagData);allTags.set(tag.id,tag),tagData.tag=tag}this._tags=allTags;for(const tagData of tagsData)this._populateTagLinks(tagData.tag,tagData)}_populateTagNames(){const result=new Map;for(const tag of this._tags.values())result.has(tag.name)?result.get(tag.name).push(tag):result.set(tag.name,[tag]);this._tagNames=result}tagFromJSON(tagData){const tag=new Tag(tagData.name);return tag.id=tagData.id,tag.space=this._spaces.get(tagData.spaceId),tag.color=tagData.color,tag.availability=tagData.availability,tag.isInternal=tagData.isInternal,tag.state=tagData.state,this._tags&&this._populateTagLinks(tag,tagData),tag}_populateTagLinks(tag,tagData){tag.links=tagData.links.map(x=>this._tags.get(x))}noteFromJSON(noteData){const ownTag=!noteData.ownTag||noteData.ownTag.state=="CLEAN"?this.getTag(noteData.id):this.tagFromJSON(noteData.ownTag),note=new Note(noteData.text,ownTag).at(new Date(noteData.date)).in(this.getSpace(noteData.spaceId));note.id=noteData.id,note.state=noteData.state;for(const ntData of noteData.tags){const nt=note.addTag(this.getTag(ntData.tagId));nt.data=ntData.data,nt.state=ntData.state}return note}getSpaces(){return Array.from(this._spaces.values())}getSpace(id){return this._spaces.get(id)}getSpaceByName(name){for(const space of this._spaces.values())if(space.name==name)return space}spaceSaved(spaceData){const space=this.spaceFromJSON(spaceData);return space.state=="DELETED"?this._spaces.delete(space.id):this._spaces.set(space.id,space),space}getTags(space=null,includeOtherSpacePublics=!1,includeOtherSpaceCommons=!1){if(space==null)return Array.from(this._tags.values());let spaceObj;return typeof space=="number"?spaceObj=this.getSpace(space):spaceObj=space,Array.from(this._tags.values()).filter(x=>{if(x.space.id==spaceObj.id||x.isPublic&&includeOtherSpacePublics||x.isCommon&&includeOtherSpaceCommons)return!0})}getTag(id){return this._tags.get(id)}getTagByName(name,space){space instanceof Space&&(space=space.id);for(const tag of this._tagNames.get(name)??[])if(tag.name==name&&tag.space.id==space)return tag}getTagsByName(name){return this._tagNames.get(name)??[]}tagSaved(tagData){const tag=this.tagFromJSON(tagData);return tag.state=="DELETED"?this._tags.delete(tag.id):this._tags.set(tag.id,tag),this._populateTagNames(),tag}}class NotuHttpCacheFetcher{constructor(url,token,fetchMethod=null){__publicField(this,"_url",null);__publicField(this,"_token",null);__publicField(this,"_fetch");if(!url)throw Error("Endpoint URL must be passed into NotuHttpCacheFetcher constructor");if(!token)throw Error("Security token must be passed into NotuHttpCacheFetcher constructor");url.endsWith("/")&&(url=url.substring(0,url.length-1)),this._url=url,this._token=token,this._fetch=fetchMethod??window.fetch.bind(window)}get url(){return this._url}get token(){return this._token}async getSpacesData(){return await this._getX("/spaces")}async getTagsData(){return await this._getX("/tags")}async _getX(endpoint){return await(await this._fetch(this.url+endpoint,{method:"GET",headers:{Authorization:"Bearer "+this.token}})).json()}}class NmlElement{constructor(){__publicField(this,"tag");__publicField(this,"children",[]);__publicField(this,"attributes",{});__publicField(this,"openText");__publicField(this,"closeText");__publicField(this,"isSelfClosing",!1);__publicField(this,"startIndex")}get length(){var _a;return this.openText.length+(((_a=this.closeText)==null?void 0:_a.length)??0)+this.children.map(x=>x.length).reduce((acc,cur)=>acc+cur,0)}get fullText(){let output=this.openText;for(const child of this.children)typeof child=="string"?output+=child:output+=child.fullText;return this.closeText&&(output+=this.closeText),output}get isComplete(){return!!(this.isSelfClosing||this.openText&&this.closeText)}}class NmlParserWorkingData{constructor(text){__publicField(this,"text");__publicField(this,"textIndex",0);__publicField(this,"results",[]);__publicField(this,"elementStack",[]);this.text=text}addTextUpTo(index){const newText=this.text.substring(this.textIndex,index);if(this.textIndex=index,this.elementStack.length>0){const topElement=this.elementStack[this.elementStack.length-1];if(topElement.children.length>0){const lastItem=topElement.children[topElement.children.length-1];if(typeof lastItem=="string"){topElement.children[topElement.children.length-1]=lastItem+newText;return}}topElement.children.push(newText)}else{if(this.results.length>0){const lastItem=this.results[this.results.length-1];if(typeof lastItem=="string"){this.results[this.results.length-1]=lastItem+newText;return}}this.results.push(newText)}}addElement(element){if(element.isSelfClosing)this.elementStack.length==0?this.results.push(element):this.elementStack[this.elementStack.length-1].children.push(element);else if(!element.closeText)this.elementStack.length>0&&this.elementStack[this.elementStack.length-1].children.push(element),this.elementStack.push(element);else{const topElement=this.elementStack[this.elementStack.length-1];(topElement==null?void 0:topElement.tag)==element.tag?(this.elementStack.pop(),topElement.closeText=element.closeText,this.elementStack.length==0&&this.results.push(topElement)):(this.resolveStack(),this.textIndex=element.startIndex,this.addTextUpTo(element.startIndex+element.closeText.length))}}resolveStack(){if(this.elementStack.length==0)return;const elStack=this.elementStack;this.elementStack=[],this.resolveStackElement(elStack[0])}resolveStackElement(element){if(this.textIndex=element.startIndex,element.isComplete){this.results.push(element),this.textIndex=element.startIndex+element.length;return}this.addTextUpTo(element.startIndex+element.openText.length);for(const child of element.children)typeof child=="string"?this.addTextUpTo(this.textIndex+child.length):this.resolveStackElement(child)}}function parseNml(text){const wd=new NmlParserWorkingData(text);for(;wd.textIndex<text.length;)wd.text[wd.textIndex]=="<"?parseNmlElement(wd):parseNmlTextFragment(wd);return wd.resolveStack(),wd.results}function parseNmlTextFragment(wd){let openStart=wd.text.indexOf("<",wd.textIndex);if(openStart==-1){wd.addTextUpTo(wd.text.length);return}wd.addTextUpTo(openStart)}function parseNmlElement(wd){const nameRegex=/<(\/)?\s*(\w[\w\d]*)\s*[/>\w]/y;nameRegex.lastIndex=wd.textIndex;const nameMatch=nameRegex.exec(wd.text);if(!nameMatch||nameMatch.index!=wd.textIndex){wd.resolveStack(),wd.addTextUpTo(wd.textIndex+1);return}const isClosing=nameMatch[1]!=null,newElement=new NmlElement;newElement.tag=nameMatch[2],newElement.startIndex=wd.textIndex;let index=wd.textIndex+nameMatch[0].length-1;for(isSpace(wd.text.charCodeAt(index-1))&&index--;;){const attributeRegex=/(?:\s+(\w[\w\d]*)\s*=\s*\")?(?:\s*(\/?>))?/y;attributeRegex.lastIndex=index;const attributeMatch=attributeRegex.exec(wd.text);if(attributeMatch[0]==""||attributeMatch.index!=index){wd.resolveStack(),wd.addTextUpTo(index);return}if(attributeMatch[2]==">"){wd.textIndex=attributeMatch.index+attributeMatch[0].length,isClosing?newElement.closeText=wd.text.substring(newElement.startIndex,wd.textIndex):newElement.openText=wd.text.substring(newElement.startIndex,wd.textIndex),wd.addElement(newElement);return}else if(attributeMatch[2]=="/>"){if(isClosing){wd.resolveStack(),wd.addTextUpTo(attributeMatch.index+attributeMatch[0].length);return}wd.textIndex=attributeMatch.index+attributeMatch[0].length,newElement.openText=wd.text.substring(newElement.startIndex,attributeMatch.index+attributeMatch[0].length),newElement.isSelfClosing=!0,wd.addElement(newElement);return}const attributeName=attributeMatch[1];let attributeValueStartIndex=attributeMatch.index+attributeMatch[0].length,attributeValueEndIndex=attributeValueStartIndex;for(;;){if(attributeValueEndIndex=wd.text.indexOf('"',attributeValueStartIndex),attributeValueEndIndex==-1){wd.resolveStack(),wd.addTextUpTo(index);return}if(!isSlash(wd.text.charCodeAt(attributeValueEndIndex-1)))break}const attributeValue=wd.text.substring(attributeValueStartIndex,attributeValueEndIndex);newElement.attributes[attributeName]=attributeValue,index=attributeValueEndIndex+1}}function isSlash(charCode){return charCode==47}function isSpace(charCode){return charCode==32}function splitNoteTextIntoComponents(note,notu,componentProcessors,textComponentFactory,paragraphComponentFactory){const xmlData=parseNml(note.text),components=[];async function save(){note.text=components.map(x=>x.getText()).join(""),await notu.saveNotes([note])}const ungroupedComponents=[];for(const item of xmlData)ungroupedComponents.push(getComponentFromXmlElement(item,componentProcessors,textComponentFactory,note,save));for(let groupStart=0;groupStart<ungroupedComponents.length;groupStart++){const startComp=ungroupedComponents[groupStart];if(!startComp.displaysInline){components.push(startComp);continue}for(let groupEnd=groupStart;groupEnd<=ungroupedComponents.length;groupEnd++){const endComp=ungroupedComponents[groupEnd];if(!endComp||!endComp.displaysInline){const groupedComps=ungroupedComponents.slice(groupStart,groupEnd);components.push(paragraphComponentFactory(groupedComps)),groupStart=groupEnd-1;break}}}return components}function getComponentFromXmlElement(element,componentProcessors,textComponentFactory,note,save){if(typeof element=="string")return textComponentFactory(element);for(const processor of componentProcessors)if(processor.tagName==element.tag)return processor.create(element,note,save,childElement=>getComponentFromXmlElement(childElement,componentProcessors,textComponentFactory,note,save));return textComponentFactory(element.fullText)}class ParsedQuery{constructor(){__publicField(this,"where",null);__publicField(this,"order",null);__publicField(this,"groupings",[]);__publicField(this,"tags",[])}}class ParsedTag{constructor(){__publicField(this,"space",null);__publicField(this,"name",null);__publicField(this,"searchDepths",[]);__publicField(this,"filter",null)}}class ParsedTagFilter{constructor(){__publicField(this,"pattern",null);__publicField(this,"exps",[])}}class ParsedGrouping{constructor(){__publicField(this,"criteria");__publicField(this,"name")}}function parseQuery(query){const output=splitQuery(query);output.where=identifyTags(output.where,output),output.order=identifyTags(output.order,output);for(const grouping of output.groupings)grouping.criteria=identifyTags(grouping.criteria,output);return output}function splitQuery(query){query=" "+query+" ";const output=new ParsedQuery,groupByIndex=query.toUpperCase().indexOf(" GROUP BY ");if(groupByIndex>=0){const groupings=query.substring(groupByIndex+10).trim().split(",");for(const g of groupings){const asIndex=g.toUpperCase().indexOf(" AS "),grouping=new ParsedGrouping;grouping.criteria=g.substring(0,asIndex).trim(),grouping.name=g.substring(asIndex+4).replace(/'/g,"").trim(),output.groupings.push(grouping)}query=query.substring(0,groupByIndex+1)}const orderByIndex=query.toUpperCase().indexOf(" ORDER BY ");return orderByIndex>=0&&(output.order=query.substring(orderByIndex+10).trim(),query=query.substring(0,orderByIndex+1)),output.where=query.trim(),output.where==""&&(output.where=null),output}function identifyTags(query,parsedQuery){const regexes=[/([#@_]+)([\w\d]+\.)?([\w\d]+)/,/([#@_]+)\[([\w\d\s]+\.)?([\w\d\s]+)\]/];for(const regex of regexes)for(;;){const match=regex.exec(query);if(!match)break;let hashPrefix=match[1];const parsedTag=new ParsedTag;parsedTag.space=match[2]?match[2].substring(0,match[2].length-1):null,parsedTag.name=match[3],hashPrefix.startsWith("@")&&(parsedTag.searchDepths.push(0),hashPrefix=hashPrefix.substring(1));for(let i=0;i<hashPrefix.length;i++)hashPrefix[i]=="#"&&parsedTag.searchDepths.push(i+1);const fullMatch=match[0],matchStart=query.indexOf(fullMatch),matchEnd=matchStart+fullMatch.length,tagDataFilter=getTagDataFilterText(query,matchEnd);tagDataFilter&&(query=query.substring(0,matchEnd)+query.substring(matchEnd+tagDataFilter.length+2),processTagDataFilter(parsedTag,tagDataFilter)),query=query.substring(0,matchStart)+`{tag${parsedQuery.tags.length}}`+query.substring(matchEnd),parsedQuery.tags.push(parsedTag)}return query}function getTagDataFilterText(query,tagEndIndex){if(query.charAt(tagEndIndex)!="{")return null;let i=tagEndIndex+1,braceDepth=1;for(;;){if(i>=query.length)throw Error("Invalid query syntax, expected closing '}' symbol.");const char=query.charAt(i);if(char=="{")braceDepth++;else if(char=="}"&&(braceDepth--,braceDepth==0))break;i++}return query.substring(tagEndIndex+1,i)}function processTagDataFilter(parsedTag,filterText){filterText=` ${filterText}`,parsedTag.filter=new ParsedTagFilter,parsedTag.filter.pattern=filterText;const expressionRegex=/[\s\(]\.([\w\d\[\]\.]+)/;for(;;){const match=expressionRegex.exec(parsedTag.filter.pattern);if(!match)break;const expression=match[1];parsedTag.filter.pattern=parsedTag.filter.pattern.replace(`.${expression}`,`{exp${parsedTag.filter.exps.length}}`),parsedTag.filter.exps.push(expression)}parsedTag.filter.pattern=parsedTag.filter.pattern.trim()}exports2.NmlElement=NmlElement,exports2.Note=Note,exports2.NoteTag=NoteTag,exports2.Notu=Notu,exports2.NotuCache=NotuCache,exports2.NotuHttpCacheFetcher=NotuHttpCacheFetcher,exports2.NotuHttpClient=NotuHttpClient,exports2.ParsedGrouping=ParsedGrouping,exports2.ParsedQuery=ParsedQuery,exports2.ParsedTag=ParsedTag,exports2.ParsedTagFilter=ParsedTagFilter,exports2.Space=Space,exports2.SpaceLink=SpaceLink,exports2.Tag=Tag,exports2.parseNml=parseNml,exports2.parseQuery=parseQuery,exports2.splitNoteTextIntoComponents=splitNoteTextIntoComponents,Object.defineProperty(exports2,Symbol.toStringTag,{value:"Module"})});
|
|
1
|
+
(function(global,factory){typeof exports=="object"&&typeof module<"u"?factory(exports):typeof define=="function"&&define.amd?define(["exports"],factory):(global=typeof globalThis<"u"?globalThis:global||self,factory(global.notu={}))})(this,function(exports2){"use strict";var __defProp=Object.defineProperty;var __defNormalProp=(obj,key,value)=>key in obj?__defProp(obj,key,{enumerable:!0,configurable:!0,writable:!0,value}):obj[key]=value;var __publicField=(obj,key,value)=>(__defNormalProp(obj,typeof key!="symbol"?key+"":key,value),value);class Notu{constructor(client,cache){__publicField(this,"_client");__publicField(this,"_cache");this._client=client,this._cache=cache}get client(){return this._client}get cache(){return this._cache}async login(username,password){return await this.client.login(username,password)}async setup(){return await this.client.setup()}getSpaces(){return this.cache.getSpaces()}getSpace(id){return this.cache.getSpace(id)}getSpaceByName(name){return this.cache.getSpaceByName(name)}async saveSpace(space){const spaceData=await this.client.saveSpace(space);return this.cache.spaceSaved(spaceData)}getTags(space=null,includeOtherSpacePublics=!1,includeOtherSpaceCommons=!1){return this.cache.getTags(space,includeOtherSpacePublics,includeOtherSpaceCommons)}getTag(id){return this.cache.getTag(id)}getTagByName(name,space){return this.cache.getTagByName(name,space)}async getNotes(query,spaceId){return(await this.client.getNotes(query,spaceId)).map(n=>this.cache.noteFromJSON(n))}async getNoteCount(query,spaceId){return await this.client.getNoteCount(query,spaceId)}async saveNotes(notes){const tagsBeingDeletedData=notes.filter(x=>!!x.ownTag).filter(x=>x.isDeleted||x.ownTag.isDeleted).map(x=>x.ownTag.toJSON()),notesData=await this.client.saveNotes(notes);for(const noteData of notesData.filter(x=>!!x.ownTag&&!x.ownTag.isDeleted))noteData.ownTag.links=noteData.tags.map(x=>x.tagId),this.cache.tagSaved(noteData.ownTag);for(const tagData of tagsBeingDeletedData)tagData.state="DELETED",this.cache.tagSaved(tagData);return notes=notesData.map(n=>this.cache.noteFromJSON(n)),notes}async customJob(name,data){return await this.client.customJob(name,data)}async getPages(){return(await this.client.getPages()).map(x=>this.cache.pageFromJSON(x))}async getPage(id){return this.cache.pageFromJSON(await this.client.getPage(id))}async savePage(page){return this.cache.pageFromJSON(await this.client.savePage(page))}}class ModelWithState{constructor(){__publicField(this,"state","NEW")}new(){return this.state="NEW",this}clean(){return this.state="CLEAN",this}dirty(){return this.state="DIRTY",this}delete(){return this.state="DELETED",this}get isNew(){return this.state=="NEW"}get isClean(){return this.state=="CLEAN"}get isDirty(){return this.state=="DIRTY"}get isDeleted(){return this.state=="DELETED"}validate(throwError=!1){return!0}}class Space extends ModelWithState{constructor(name=""){super();__publicField(this,"_id",0);__publicField(this,"_name","");__publicField(this,"_internalName","");__publicField(this,"_version","0.0.1");__publicField(this,"_useCommonSpace",!1);__publicField(this,"_settings");__publicField(this,"_links",[]);this._name=name}get id(){return this._id}set id(value){if(!this.isNew)throw Error("Cannot change the id of a Space once it has already been created.");this._id=value}get name(){return this._name}set name(value){value!==this._name&&(this._name=value,this.isClean&&this.dirty())}get internalName(){return this._internalName}set internalName(value){if(!this.isNew&&value!=this.internalName)throw Error("Cannot change the internal name of a space after it has already been saved.");value!=this._internalName&&(this._internalName=value,this.isClean&&this.dirty())}get version(){return this._version}set version(value){value!==this._version&&(this._version=value,this.isClean&&this.dirty())}v(version){return this.version=version,this}get useCommonSpace(){return this._useCommonSpace}set useCommonSpace(value){value!==this._useCommonSpace&&(this._useCommonSpace=value,this.isClean&&this.dirty())}get settings(){return this._settings}set settings(value){this._settings=value,this.isClean&&this.dirty()}withSettings(settings){return this.settings=settings,this}get links(){return this._links.filter(x=>!x.isDeleted)}get linksPendingDeletion(){return this._links.filter(x=>x.isDeleted)}addLink(link){if(link.isDeleted)throw Error("Cannot add a link marked as deleted to a space");if(link.isNew)throw Error("Cannot add a link that hasn't yet been saved to a space");let existing=this._links.find(x=>x.name==link.name);if(existing){if(existing.isDeleted)return existing.dirty(),this;throw Error("The space already contains a link with this name")}return this._links.push(link),this}removeLink(name){const link=this._links.find(x=>x.name==name);return link?(link.isNew?this._links=this._links.filter(x=>x!==link):link.delete(),this):this}duplicate(){const output=new Space;return output.id=this.id,output.name=this.name,output.internalName=this.internalName,output.version=this.version,output.useCommonSpace=this.useCommonSpace,this.settings&&(output._settings=JSON.parse(JSON.stringify(this.settings))),output._links=this.links.map(x=>x.duplicate()),output.state=this.state,output}duplicateAsNew(){const output=new Space;return output.name=this.name,output.internalName=this.internalName,output.version=this.version,output.useCommonSpace=this.useCommonSpace,this.settings&&(output._settings=JSON.parse(JSON.stringify(this.settings))),output._links=this.links.map(x=>x.duplicateAsNew()),output}validate(throwError=!1){let output=null;if(!this.isNew&&this.id<=0&&(output="Space id must be greater than zero if in non-new state."),throwError&&output!=null)throw Error(output);return output==null}toJSON(){return{state:this.state,id:this.id,name:this.name,internalName:this.internalName,version:this.version,useCommonSpace:this.useCommonSpace,settings:this.settings,links:this._links.map(x=>x.toJSON())}}}class NotuHttpClient{constructor(url,fetchMethod=null){__publicField(this,"_url",null);__publicField(this,"_token",null);__publicField(this,"_fetch");__publicField(this,"errorHandler",null);if(!url)throw Error("Endpoint URL must be passed in to NotuClient constructor");url.endsWith("/")&&(url=url.substring(0,url.length-1)),this._url=url,this._fetch=fetchMethod??window.fetch.bind(window)}get url(){return this._url}get token(){return this._token}set token(value){this._token=value}_validateResponseStatus(response){if(response.status>=400&&response.status<600){if(this.errorHandler&&this.errorHandler(response))return;throw Error(response.statusText)}}async login(username,password){const response=await this._fetch(this.url+"/login",{method:"POST",body:JSON.stringify({username,password})});if(this._validateResponseStatus(response),response.body!=null){const result=await response.json();return result&&(this._token=result),result}throw Error("Unknown error occurred on the server")}async setup(){const response=await this._fetch(this.url+"/setup",{method:"POST",headers:{Authorization:"Bearer "+this.token}});this._validateResponseStatus(response),await response.json()}async saveSpace(space){const response=await this._fetch(this.url+"/spaces",{method:"POST",body:JSON.stringify(space),headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}async getNotes(query,space){space&&space instanceof Space&&(space=space.id);const response=await this._fetch(this.url+`/notes?${space?`space=${space}&`:""}query=${encodeURIComponent(query)}`,{method:"GET",headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}async getNoteCount(query,space){space instanceof Space&&(space=space.id);const response=await this._fetch(this.url+`/notes?count=true&space=${space}&query=${encodeURIComponent(query)}`,{method:"GET",headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),(await response.json()).count}async saveNotes(notes){const response=await this._fetch(this.url+"/notes",{method:"POST",body:JSON.stringify(notes),headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}async customJob(name,data){const response=await this._fetch(this.url+"/customJob",{method:"POST",body:JSON.stringify({name,data,clientTimezone:Intl.DateTimeFormat().resolvedOptions().timeZone}),headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}async getPages(){const response=await this._fetch(this.url+"/pages",{method:"GET",headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}async getPage(id){const response=await this._fetch(this.url+`/pages/${id}`,{method:"GET",headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}async savePage(page){const response=await this._fetch(this.url+"/pages",{method:"POST",body:JSON.stringify(page),headers:{Authorization:"Bearer "+this.token}});return this._validateResponseStatus(response),await response.json()}}class NoteTag extends ModelWithState{constructor(tag){super();__publicField(this,"_tag");__publicField(this,"_data");if(!tag)throw Error("Cannot instanciate new NoteTag without a passed in tag.");if(tag.isNew)throw Error("Cannot create a NoteTag object for a tag that hasn't been saved yet.");if(tag.isDeleted)throw Error("Cannot create a NoteTag object for a tag marked as deleted.");this._tag=tag}get tag(){return this._tag}get data(){return this._data}set data(value){this._data=value,this.isClean&&this.dirty()}withData(data){return this.data=data,this}duplicate(){const output=this.duplicateAsNew();return output.state=this.state,output}duplicateAsNew(){const output=new NoteTag(this.tag);return this.data&&(output._data=JSON.parse(JSON.stringify(this.data))),output}validate(throwError=!1){function exit(message){if(throwError&&message!=null)throw Error(message);return message==null}return this.tag?!0:exit("NoteTag must have a tag set.")}toJSON(){return{state:this.state,tagId:this.tag.id,data:this.data}}}class Tag extends ModelWithState{constructor(name=""){super();__publicField(this,"_id",0);__publicField(this,"_space",null);__publicField(this,"_name","");__publicField(this,"_color",null);__publicField(this,"_availability",0);__publicField(this,"_isInternal",!1);__publicField(this,"links",[]);this._name=name}get id(){return this._id}set id(value){if(!this.isNew)throw Error("Cannot change the id of a Tag once it has already been created.");this._id=value}get space(){return this._space}set space(value){var _a;if(value!==this._space){const idChanged=(value==null?void 0:value.id)!=((_a=this._space)==null?void 0:_a.id);this._space=value,this.isClean&&idChanged&&this.dirty()}}in(space){return this.space=space,this}get name(){return this._name}set name(value){value!==this._name&&(this._name=value,this.isClean&&this.dirty())}getFullName(){return`${this.space.name}.${this.name}`}getQualifiedName(contextSpaceId){var _a;return contextSpaceId==((_a=this.space)==null?void 0:_a.id)?this.name:this.getFullName()}getUniqueName(cache){return cache.getTagsByName(this.name).length==1?this.name:this.getFullName()}get color(){return this._color}set color(value){value!==this._color&&(this._color=value,this.isClean&&this.dirty())}get availability(){return this._availability}set availability(value){if(!this.isNew&&value<this.availability)throw Error("Cannot change a tag to private once its already been saved.");value!=this._availability&&(this._availability=value,this.isClean&&this.dirty())}get isPrivate(){return this._availability==0}get isCommon(){return this._availability==1}get isPublic(){return this.availability==2}asPrivate(){return this.availability=0,this}asCommon(){return this.availability=1,this}asPublic(){return this.availability=2,this}get isInternal(){return this._isInternal}set isInternal(value){if(!this.isNew&&value!=this.isInternal)throw Error("Cannot change whether a tag is internal or not once it has already been saved.");value!=this._isInternal&&(this._isInternal=value,this.isClean&&this.dirty())}asInternal(){return this.isInternal=!0,this}linksTo(tag){return!!this.links.find(x=>x==tag)}duplicate(){const output=this.duplicateAsNew();return output.id=this.id,output.state=this.state,output}duplicateAsNew(){const output=new Tag(this.name);return output.color=this.color,output.space=this.space,output.availability=this.availability,output.isInternal=this.isInternal,output.links=this.links.slice(),output}validate(throwError=!1){let output=null;if(!this.isNew&&this.id<=0?output="Tag id must be greater than zero if in non-new state.":!this.name||!/^[a-zA-Z][a-zA-Z0-9 ]*[a-zA-Z0-9]?$/.test(this.name)?output="Tag name is invalid, must only contain letters, numbers, and spaces, starting with a letter":this.color&&!/^#?[A-z0-9]{6}$/.test(this.color)&&(output="Tag color is invalid, must be a 6 character hexadecimal."),throwError&&output!=null)throw Error(output);return output==null}toJSON(){var _a;return{state:this.state,id:this.id,name:this.name,spaceId:(_a=this.space)==null?void 0:_a.id,color:this.color,availability:this.availability,isInternal:this.isInternal,links:this.links.map(x=>x.id)}}}class Note extends ModelWithState{constructor(text,ownTag){super();__publicField(this,"_id",0);__publicField(this,"_date",new Date);__publicField(this,"_text","");__publicField(this,"_space",null);__publicField(this,"_ownTag",null);__publicField(this,"_group");__publicField(this,"_tags",[]);text&&(this.text=text),this._ownTag=ownTag}get id(){return this._id}set id(value){if(!this.isNew)throw Error("Cannot change the id of a Note once it has already been created.");this._id=value,this.ownTag&&this.ownTag.id!=value&&(this.ownTag.id=value)}get date(){return this._date}set date(value){value!==this._date&&(this._date=value,this.isClean&&this.dirty())}at(value){return this.date=value,this}get text(){return this._text}set text(value){value!==this._text&&(this._text=value,this.isClean&&this.dirty())}get space(){return this._space}set space(value){var _a;if(value!==this._space){const idChanged=(value==null?void 0:value.id)!=((_a=this._space)==null?void 0:_a.id);this._space=value,this.isClean&&idChanged&&this.dirty(),this._setOwnTagSpace()}}in(space){return this.space=space,this}get ownTag(){return this._ownTag}setOwnTag(tagName){return this.ownTag==null?(this._ownTag=new Tag(tagName),this.ownTag.id=this.id):this.ownTag.isDeleted&&this.ownTag.dirty(),this.ownTag.name=tagName,this._setOwnTagSpace(),this}removeOwnTag(){return this.ownTag?(this.ownTag.isNew?this._ownTag=null:this.ownTag.delete(),this):this}_setOwnTagSpace(){this.ownTag&&this.space&&(this.ownTag.space=this.space)}get group(){return this._group}set group(value){this._group=value}get tags(){return this._tags.filter(x=>!x.isDeleted)}get tagsPendingDeletion(){return this._tags.filter(x=>x.isDeleted)}addTag(tag){if(tag.isDeleted)throw Error("Cannot add a tag marked as deleted to a note");if(tag.isNew)throw Error("Cannot add a tag that hasn't yet been saved to a note");if(tag.id==this.id)throw Error("Note cannot add its own tag as a linked tag");if(tag.isPrivate&&tag.space.id!=this.space.id)throw Error("Cannot add a private tag from another space");let nt=this._tags.find(x=>x.tag.id==tag.id);return nt?(nt.isDeleted&&nt.dirty(),nt):(nt=new NoteTag(tag),this._tags.push(nt),nt)}removeTag(tag){const nt=this._tags.find(x=>x.tag.id==tag.id);return nt?(nt.isNew?this._tags=this._tags.filter(x=>x!==nt):nt.delete(),this):this}getTag(tag,space=null){return tag instanceof Tag?this.tags.find(x=>x.tag===tag):(space&&space instanceof Space&&(space=space.id),space!=null?this.tags.find(x=>x.tag.name==tag&&x.tag.space.id==space):this.tags.find(x=>x.tag.name==tag&&x.tag.space.id==this.space.id))}getTagData(tag,type){const nt=this.getTag(tag);return nt?new type(nt):null}duplicate(){var _a;const output=new Note(this.text,(_a=this.ownTag)==null?void 0:_a.duplicate()).at(this.date).in(this.space);return output._tags=this.tags.map(x=>x.duplicate()),output.id=this.id,output.state=this.state,output}duplicateAsNew(){const output=new Note(this.text).at(this.date).in(this.space);return output._tags=this.tags.map(x=>x.duplicateAsNew()),output}toJSON(){var _a;return{state:this.state,id:this.id,date:this.date,text:this.text,spaceId:this.space.id,ownTag:(_a=this.ownTag)==null?void 0:_a.toJSON(),tags:this._tags.map(x=>x.toJSON())}}validate(throwError=!1){function exit(message){if(throwError&&message!=null)throw Error(message);return message==null}if(this.space){if(!this.isNew&&this.id<=0)return exit("Note id must be greater than zero if in non-new state.");if(this.ownTag&&this.ownTag.space.id!=this.space.id)return exit("Note cannot belong to a different space than its own tag")}else return exit("Note must belong to a space.");if(this.ownTag&&!this.ownTag.validate(throwError))return!1;for(const nt of this._tags)if(!nt.validate(throwError))return!1;return!0}}class Page extends ModelWithState{constructor(){super(...arguments);__publicField(this,"_id",0);__publicField(this,"_name","");__publicField(this,"_order",0);__publicField(this,"_group",null);__publicField(this,"_space",null);__publicField(this,"_query",null)}get id(){return this._id}set id(value){if(!this.isNew)throw Error("Cannot change the id of a Page once it has already been created.");this._id=value}get name(){return this._name}set name(value){value!==this._name&&(this._name=value,this.isClean&&this.dirty())}get order(){return this._order}set order(value){value!==this._order&&(this._order=value,this.isClean&&this.dirty())}get group(){return this._group}set group(value){value!==this._group&&(this._group=value,this.isClean&&this.dirty())}get space(){return this._space}set space(value){var _a;if(value!==this._space){const idChanged=(value==null?void 0:value.id)!=((_a=this._space)==null?void 0:_a.id);this._space=value,this.isClean&&idChanged&&this.dirty()}}get query(){return this._query}set query(value){value!==this._query&&(this._query=value,this.isClean&&this.dirty())}toJSON(){var _a;return{state:this.state,id:this.id,name:this.name,order:this.order,group:this.group,spaceId:(_a=this.space)==null?void 0:_a.id,query:this.query}}}class SpaceLink extends ModelWithState{constructor(){super(...arguments);__publicField(this,"_name");__publicField(this,"_toSpace")}get name(){return this._name}set name(value){value!==this._name&&(this._name=value,this.isClean&&this.dirty())}get toSpace(){return this._toSpace}set toSpace(value){var _a;if((value==null?void 0:value.id)!==((_a=this._toSpace)==null?void 0:_a.id)){if(value.isNew)throw Error("Cannot create a link to a space that hasn't been saved yet");if(value.isDeleted)throw Error("Cannot create a link to a space marked as deleted");this._toSpace=value,this.isClean&&this.dirty()}else this._toSpace=value}duplicate(){const output=this.duplicateAsNew();return output.state=this.state,output}duplicateAsNew(){const output=new SpaceLink;return output.name=this.name,output.toSpace=this.toSpace,output}toJSON(){var _a;return{state:this.state,name:this.name,toSpaceId:(_a=this.toSpace)==null?void 0:_a.id}}}class NotuCache{constructor(fetcher){__publicField(this,"_fetcher");__publicField(this,"_spaces",null);__publicField(this,"_tags",null);__publicField(this,"_tagNames",null);if(!fetcher)throw Error("NotuCache constructor must have a fetcher argument supplied.");this._fetcher=fetcher}async populate(){await this._populateSpaces(),await this._populateTags(),this._populateTagNames()}async _populateSpaces(){const spacesData=await this._fetcher.getSpacesData(),allSpaces=new Map;for(const spaceData of spacesData){const space=this.spaceFromJSON(spaceData);allSpaces.set(space.id,space),spaceData.space=space}this._spaces=allSpaces;for(const spaceData of spacesData)this._populateSpaceLinks(spaceData.space,spaceData)}spaceFromJSON(spaceData){const space=new Space(spaceData.name);return space.internalName=spaceData.internalName,space.id=spaceData.id,space.version=spaceData.version,space.useCommonSpace=spaceData.useCommonSpace,space.settings=spaceData.settings,space.state=spaceData.state,this._spaces&&this._populateSpaceLinks(space,spaceData),space}_populateSpaceLinks(space,spaceData){for(const linkData of spaceData.links){const link=new SpaceLink;link.name=linkData.name,link.toSpace=this._spaces.get(linkData.toSpaceId),link.clean(),space.addLink(link)}}async _populateTags(){const tagsData=await this._fetcher.getTagsData(),allTags=new Map;for(const tagData of tagsData){const tag=this.tagFromJSON(tagData);allTags.set(tag.id,tag),tagData.tag=tag}this._tags=allTags;for(const tagData of tagsData)this._populateTagLinks(tagData.tag,tagData)}_populateTagNames(){const result=new Map;for(const tag of this._tags.values())result.has(tag.name)?result.get(tag.name).push(tag):result.set(tag.name,[tag]);this._tagNames=result}tagFromJSON(tagData){const tag=new Tag(tagData.name);return tag.id=tagData.id,tag.space=this._spaces.get(tagData.spaceId),tag.color=tagData.color,tag.availability=tagData.availability,tag.isInternal=tagData.isInternal,tag.state=tagData.state,this._tags&&this._populateTagLinks(tag,tagData),tag}_populateTagLinks(tag,tagData){tag.links=tagData.links.map(x=>this._tags.get(x))}noteFromJSON(noteData){const ownTag=!noteData.ownTag||noteData.ownTag.state=="CLEAN"?this.getTag(noteData.id):this.tagFromJSON(noteData.ownTag),note=new Note(noteData.text,ownTag).at(new Date(noteData.date)).in(this.getSpace(noteData.spaceId));note.id=noteData.id,note.state=noteData.state;for(const ntData of noteData.tags){const nt=note.addTag(this.getTag(ntData.tagId));nt.data=ntData.data,nt.state=ntData.state}return note}pageFromJSON(pageData){const page=new Page;return page.id=pageData.id,page.name=pageData.name,page.order=pageData.order,page.group=pageData.group,page.space=this.getSpace(pageData.spaceId),page.query=pageData.query,page.state=pageData.state,page}getSpaces(){return Array.from(this._spaces.values())}getSpace(id){return this._spaces.get(id)}getSpaceByName(name){for(const space of this._spaces.values())if(space.name==name)return space}spaceSaved(spaceData){const space=this.spaceFromJSON(spaceData);return space.state=="DELETED"?this._spaces.delete(space.id):this._spaces.set(space.id,space),space}getTags(space=null,includeOtherSpacePublics=!1,includeOtherSpaceCommons=!1){if(space==null)return Array.from(this._tags.values());let spaceObj;return typeof space=="number"?spaceObj=this.getSpace(space):spaceObj=space,Array.from(this._tags.values()).filter(x=>{if(x.space.id==spaceObj.id||x.isPublic&&includeOtherSpacePublics||x.isCommon&&includeOtherSpaceCommons)return!0})}getTag(id){return this._tags.get(id)}getTagByName(name,space){space instanceof Space&&(space=space.id);for(const tag of this._tagNames.get(name)??[])if(tag.name==name&&tag.space.id==space)return tag}getTagsByName(name){return this._tagNames.get(name)??[]}tagSaved(tagData){const tag=this.tagFromJSON(tagData);return tag.state=="DELETED"?this._tags.delete(tag.id):this._tags.set(tag.id,tag),this._populateTagNames(),tag}}class NotuHttpCacheFetcher{constructor(url,token,fetchMethod=null){__publicField(this,"_url",null);__publicField(this,"_token",null);__publicField(this,"_fetch");if(!url)throw Error("Endpoint URL must be passed into NotuHttpCacheFetcher constructor");if(!token)throw Error("Security token must be passed into NotuHttpCacheFetcher constructor");url.endsWith("/")&&(url=url.substring(0,url.length-1)),this._url=url,this._token=token,this._fetch=fetchMethod??window.fetch.bind(window)}get url(){return this._url}get token(){return this._token}async getSpacesData(){return await this._getX("/spaces")}async getTagsData(){return await this._getX("/tags")}async _getX(endpoint){return await(await this._fetch(this.url+endpoint,{method:"GET",headers:{Authorization:"Bearer "+this.token}})).json()}}class NmlElement{constructor(){__publicField(this,"tag");__publicField(this,"children",[]);__publicField(this,"attributes",{});__publicField(this,"openText");__publicField(this,"closeText");__publicField(this,"isSelfClosing",!1);__publicField(this,"startIndex")}get length(){var _a;return this.openText.length+(((_a=this.closeText)==null?void 0:_a.length)??0)+this.children.map(x=>x.length).reduce((acc,cur)=>acc+cur,0)}get fullText(){let output=this.openText;for(const child of this.children)typeof child=="string"?output+=child:output+=child.fullText;return this.closeText&&(output+=this.closeText),output}get isComplete(){return!!(this.isSelfClosing||this.openText&&this.closeText)}}class NmlParserWorkingData{constructor(text){__publicField(this,"text");__publicField(this,"textIndex",0);__publicField(this,"results",[]);__publicField(this,"elementStack",[]);this.text=text}addTextUpTo(index){const newText=this.text.substring(this.textIndex,index);if(this.textIndex=index,this.elementStack.length>0){const topElement=this.elementStack[this.elementStack.length-1];if(topElement.children.length>0){const lastItem=topElement.children[topElement.children.length-1];if(typeof lastItem=="string"){topElement.children[topElement.children.length-1]=lastItem+newText;return}}topElement.children.push(newText)}else{if(this.results.length>0){const lastItem=this.results[this.results.length-1];if(typeof lastItem=="string"){this.results[this.results.length-1]=lastItem+newText;return}}this.results.push(newText)}}addElement(element){if(element.isSelfClosing)this.elementStack.length==0?this.results.push(element):this.elementStack[this.elementStack.length-1].children.push(element);else if(!element.closeText)this.elementStack.length>0&&this.elementStack[this.elementStack.length-1].children.push(element),this.elementStack.push(element);else{const topElement=this.elementStack[this.elementStack.length-1];(topElement==null?void 0:topElement.tag)==element.tag?(this.elementStack.pop(),topElement.closeText=element.closeText,this.elementStack.length==0&&this.results.push(topElement)):(this.resolveStack(),this.textIndex=element.startIndex,this.addTextUpTo(element.startIndex+element.closeText.length))}}resolveStack(){if(this.elementStack.length==0)return;const elStack=this.elementStack;this.elementStack=[],this.resolveStackElement(elStack[0])}resolveStackElement(element){if(this.textIndex=element.startIndex,element.isComplete){this.results.push(element),this.textIndex=element.startIndex+element.length;return}this.addTextUpTo(element.startIndex+element.openText.length);for(const child of element.children)typeof child=="string"?this.addTextUpTo(this.textIndex+child.length):this.resolveStackElement(child)}}function parseNml(text){const wd=new NmlParserWorkingData(text);for(;wd.textIndex<text.length;)wd.text[wd.textIndex]=="<"?parseNmlElement(wd):parseNmlTextFragment(wd);return wd.resolveStack(),wd.results}function parseNmlTextFragment(wd){let openStart=wd.text.indexOf("<",wd.textIndex);if(openStart==-1){wd.addTextUpTo(wd.text.length);return}wd.addTextUpTo(openStart)}function parseNmlElement(wd){const nameRegex=/<(\/)?\s*(\w[\w\d]*)\s*(\/?>|\s\w)/y;nameRegex.lastIndex=wd.textIndex;const nameMatch=nameRegex.exec(wd.text);if(!nameMatch||nameMatch.index!=wd.textIndex){wd.resolveStack(),wd.addTextUpTo(wd.textIndex+1);return}const isClosing=nameMatch[1]!=null,afterTagName=nameMatch[3],newElement=new NmlElement;newElement.tag=nameMatch[2],newElement.startIndex=wd.textIndex;let index=wd.textIndex+nameMatch[0].length-afterTagName.length;for(;;){const attributeRegex=/(\/?>)|\s+(\w[\w\d]*)(\s*(=)\s*\"|\s+\w|\s*\/?>)/y;attributeRegex.lastIndex=index;const attributeMatch=attributeRegex.exec(wd.text);if(!attributeMatch||attributeMatch.index!=index){wd.resolveStack(),wd.addTextUpTo(index);return}const closingBracket=attributeMatch[1],attributeName=attributeMatch[2],afterAttributeName=attributeMatch[3],hasEqualSign=attributeMatch[4]=="=";if(closingBracket==">"){wd.textIndex=attributeMatch.index+attributeMatch[0].length,isClosing?newElement.closeText=wd.text.substring(newElement.startIndex,wd.textIndex):newElement.openText=wd.text.substring(newElement.startIndex,wd.textIndex),wd.addElement(newElement);return}else if(closingBracket=="/>"){if(isClosing){wd.resolveStack(),wd.addTextUpTo(attributeMatch.index+attributeMatch[0].length);return}wd.textIndex=attributeMatch.index+attributeMatch[0].length,newElement.openText=wd.text.substring(newElement.startIndex,attributeMatch.index+attributeMatch[0].length),newElement.isSelfClosing=!0,wd.addElement(newElement);return}if(hasEqualSign){const attributeValueStartIndex=attributeMatch.index+attributeMatch[0].length,attributeValue=parseAttributeValue(wd,attributeMatch.index+attributeMatch[0].length);if(attributeValue==null){wd.resolveStack(),wd.addTextUpTo(attributeValueStartIndex);return}newElement.attributes[attributeName]=attributeValue.replace(/\/"/g,'"'),index=attributeValueStartIndex+attributeValue.length+1}else newElement.attributes[attributeName]=!0,index=attributeMatch.index+attributeMatch[0].length-afterAttributeName.length}}function parseAttributeValue(wd,startIndex){let currentIndex=startIndex;for(;;){const nextIndex=wd.text.indexOf('"',currentIndex);if(nextIndex==-1)return null;if(!isSlash(wd.text.charCodeAt(nextIndex-1)))return wd.text.substring(startIndex,nextIndex);currentIndex=nextIndex+1}}function isSlash(charCode){return charCode==47}function splitNoteTextIntoComponents(note,notu,componentProcessors,textComponentFactory,paragraphComponentFactory){const xmlData=parseNml(note.text),components=[];async function save(){note.text=components.map(x=>x.getText()).join(""),await notu.saveNotes([note])}const ungroupedComponents=[];for(const item of xmlData)ungroupedComponents.push(getComponentFromXmlElement(item,componentProcessors,textComponentFactory,note,save));for(let groupStart=0;groupStart<ungroupedComponents.length;groupStart++){const startComp=ungroupedComponents[groupStart];if(!startComp.displaysInline){components.push(startComp);continue}for(let groupEnd=groupStart;groupEnd<=ungroupedComponents.length;groupEnd++){const endComp=ungroupedComponents[groupEnd];if(!endComp||!endComp.displaysInline){const groupedComps=ungroupedComponents.slice(groupStart,groupEnd);components.push(paragraphComponentFactory(groupedComps)),groupStart=groupEnd-1;break}}}return components}function getComponentFromXmlElement(element,componentProcessors,textComponentFactory,note,save){if(typeof element=="string")return textComponentFactory(element);for(const processor of componentProcessors)if(processor.tagName==element.tag)return processor.create(element,note,save,childElement=>getComponentFromXmlElement(childElement,componentProcessors,textComponentFactory,note,save));return textComponentFactory(element.fullText)}class ParsedQuery{constructor(){__publicField(this,"where",null);__publicField(this,"order",null);__publicField(this,"groupings",[]);__publicField(this,"tags",[])}}class ParsedTag{constructor(){__publicField(this,"space",null);__publicField(this,"name",null);__publicField(this,"searchDepths",[]);__publicField(this,"filter",null)}}class ParsedTagFilter{constructor(){__publicField(this,"pattern",null);__publicField(this,"exps",[])}}class ParsedGrouping{constructor(){__publicField(this,"criteria");__publicField(this,"name")}}function parseQuery(query){const output=splitQuery(query);output.where=identifyTags(output.where,output),output.order=identifyTags(output.order,output);for(const grouping of output.groupings)grouping.criteria=identifyTags(grouping.criteria,output);return output}function splitQuery(query){query=" "+query+" ";const output=new ParsedQuery,groupByIndex=query.toUpperCase().indexOf(" GROUP BY ");if(groupByIndex>=0){const groupings=query.substring(groupByIndex+10).trim().split(",");for(const g of groupings){const asIndex=g.toUpperCase().indexOf(" AS "),grouping=new ParsedGrouping;grouping.criteria=g.substring(0,asIndex).trim(),grouping.name=g.substring(asIndex+4).replace(/'/g,"").trim(),output.groupings.push(grouping)}query=query.substring(0,groupByIndex+1)}const orderByIndex=query.toUpperCase().indexOf(" ORDER BY ");return orderByIndex>=0&&(output.order=query.substring(orderByIndex+10).trim(),query=query.substring(0,orderByIndex+1)),output.where=query.trim(),output.where==""&&(output.where=null),output}function identifyTags(query,parsedQuery){const regexes=[/([#@_]+)([\w\d]+\.)?([\w\d]+)/,/([#@_]+)\[([\w\d\s]+\.)?([\w\d\s]+)\]/];for(const regex of regexes)for(;;){const match=regex.exec(query);if(!match)break;let hashPrefix=match[1];const parsedTag=new ParsedTag;parsedTag.space=match[2]?match[2].substring(0,match[2].length-1):null,parsedTag.name=match[3],hashPrefix.startsWith("@")&&(parsedTag.searchDepths.push(0),hashPrefix=hashPrefix.substring(1));for(let i=0;i<hashPrefix.length;i++)hashPrefix[i]=="#"&&parsedTag.searchDepths.push(i+1);const fullMatch=match[0],matchStart=query.indexOf(fullMatch),matchEnd=matchStart+fullMatch.length,tagDataFilter=getTagDataFilterText(query,matchEnd);tagDataFilter&&(query=query.substring(0,matchEnd)+query.substring(matchEnd+tagDataFilter.length+2),processTagDataFilter(parsedTag,tagDataFilter)),query=query.substring(0,matchStart)+`{tag${parsedQuery.tags.length}}`+query.substring(matchEnd),parsedQuery.tags.push(parsedTag)}return query}function getTagDataFilterText(query,tagEndIndex){if(query.charAt(tagEndIndex)!="{")return null;let i=tagEndIndex+1,braceDepth=1;for(;;){if(i>=query.length)throw Error("Invalid query syntax, expected closing '}' symbol.");const char=query.charAt(i);if(char=="{")braceDepth++;else if(char=="}"&&(braceDepth--,braceDepth==0))break;i++}return query.substring(tagEndIndex+1,i)}function processTagDataFilter(parsedTag,filterText){filterText=` ${filterText}`,parsedTag.filter=new ParsedTagFilter,parsedTag.filter.pattern=filterText;const expressionRegex=/[\s\(]\.([\w\d\[\]\.]+)/;for(;;){const match=expressionRegex.exec(parsedTag.filter.pattern);if(!match)break;const expression=match[1];parsedTag.filter.pattern=parsedTag.filter.pattern.replace(`.${expression}`,`{exp${parsedTag.filter.exps.length}}`),parsedTag.filter.exps.push(expression)}parsedTag.filter.pattern=parsedTag.filter.pattern.trim()}exports2.NmlElement=NmlElement,exports2.Note=Note,exports2.NoteTag=NoteTag,exports2.Notu=Notu,exports2.NotuCache=NotuCache,exports2.NotuHttpCacheFetcher=NotuHttpCacheFetcher,exports2.NotuHttpClient=NotuHttpClient,exports2.Page=Page,exports2.ParsedGrouping=ParsedGrouping,exports2.ParsedQuery=ParsedQuery,exports2.ParsedTag=ParsedTag,exports2.ParsedTagFilter=ParsedTagFilter,exports2.Space=Space,exports2.SpaceLink=SpaceLink,exports2.Tag=Tag,exports2.parseNml=parseNml,exports2.parseQuery=parseQuery,exports2.splitNoteTextIntoComponents=splitNoteTextIntoComponents,Object.defineProperty(exports2,Symbol.toStringTag,{value:"Module"})});
|
package/dist/types/index.d.ts
CHANGED
|
@@ -6,9 +6,10 @@ import { default as Note } from './models/Note';
|
|
|
6
6
|
import { splitNoteTextIntoComponents } from './notecomponents/NoteComponent';
|
|
7
7
|
import { parseNml, NmlElement } from './notecomponents/NmlParser';
|
|
8
8
|
import { default as NoteTag } from './models/NoteTag';
|
|
9
|
+
import { default as Page } from './models/Page';
|
|
9
10
|
import { default as parseQuery, ParsedQuery, ParsedTag, ParsedTagFilter, ParsedGrouping } from './services/QueryParser';
|
|
10
11
|
import { default as Space } from './models/Space';
|
|
11
12
|
import { default as SpaceLink } from './models/SpaceLink';
|
|
12
13
|
import { default as Tag } from './models/Tag';
|
|
13
14
|
|
|
14
|
-
export { Notu, NotuHttpClient, NotuCache, NotuHttpCacheFetcher, Note, splitNoteTextIntoComponents, parseNml, NmlElement, NoteTag, parseQuery, ParsedQuery, ParsedTag, ParsedTagFilter, ParsedGrouping, Space, SpaceLink, Tag };
|
|
15
|
+
export { Notu, NotuHttpClient, NotuCache, NotuHttpCacheFetcher, Note, splitNoteTextIntoComponents, parseNml, NmlElement, NoteTag, Page, parseQuery, ParsedQuery, ParsedTag, ParsedTagFilter, ParsedGrouping, Space, SpaceLink, Tag };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { default as ModelWithState } from './ModelWithState';
|
|
2
|
+
import { default as Space } from './Space';
|
|
3
|
+
|
|
4
|
+
export default class Page extends ModelWithState<Page> {
|
|
5
|
+
private _id;
|
|
6
|
+
get id(): number;
|
|
7
|
+
set id(value: number);
|
|
8
|
+
private _name;
|
|
9
|
+
get name(): string;
|
|
10
|
+
set name(value: string);
|
|
11
|
+
private _order;
|
|
12
|
+
get order(): number;
|
|
13
|
+
set order(value: number);
|
|
14
|
+
private _group;
|
|
15
|
+
get group(): string;
|
|
16
|
+
set group(value: string);
|
|
17
|
+
private _space;
|
|
18
|
+
get space(): Space;
|
|
19
|
+
set space(value: Space);
|
|
20
|
+
private _query;
|
|
21
|
+
get query(): string;
|
|
22
|
+
set query(value: string);
|
|
23
|
+
toJSON(): {
|
|
24
|
+
state: "NEW" | "CLEAN" | "DIRTY" | "DELETED";
|
|
25
|
+
id: number;
|
|
26
|
+
name: string;
|
|
27
|
+
order: number;
|
|
28
|
+
group: string;
|
|
29
|
+
spaceId: number;
|
|
30
|
+
query: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -23,3 +23,4 @@ export declare class NmlParserWorkingData {
|
|
|
23
23
|
}
|
|
24
24
|
export declare function parseNml(text: string): Array<string | NmlElement>;
|
|
25
25
|
export declare function parseNmlElement(wd: NmlParserWorkingData): void;
|
|
26
|
+
export declare function parseAttributeValue(wd: NmlParserWorkingData, startIndex: number): string | null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as Note } from '../models/Note';
|
|
2
|
+
import { default as Page } from '../models/Page';
|
|
2
3
|
import { default as Space } from '../models/Space';
|
|
3
4
|
|
|
4
5
|
export interface NotuClient {
|
|
@@ -9,6 +10,9 @@ export interface NotuClient {
|
|
|
9
10
|
getNoteCount(query: string, space?: number | Space): Promise<number>;
|
|
10
11
|
saveNotes(notes: Array<Note>): Promise<Array<any>>;
|
|
11
12
|
customJob(name: string, data: any): Promise<any>;
|
|
13
|
+
getPages(): Promise<Array<any>>;
|
|
14
|
+
getPage(id: number): Promise<any>;
|
|
15
|
+
savePage(page: Page): Promise<any>;
|
|
12
16
|
}
|
|
13
17
|
export default class NotuHttpClient implements NotuClient {
|
|
14
18
|
private _url;
|
|
@@ -27,4 +31,7 @@ export default class NotuHttpClient implements NotuClient {
|
|
|
27
31
|
getNoteCount(query: string, space?: number | Space): Promise<number>;
|
|
28
32
|
saveNotes(notes: Array<Note>): Promise<Array<any>>;
|
|
29
33
|
customJob(name: string, data: any): Promise<any>;
|
|
34
|
+
getPages(): Promise<Array<any>>;
|
|
35
|
+
getPage(id: number): Promise<any>;
|
|
36
|
+
savePage(page: Page): Promise<any>;
|
|
30
37
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Note, Space, Tag } from '..';
|
|
2
|
+
import { default as Page } from '../models/Page';
|
|
2
3
|
import { NotuClient } from './HttpClient';
|
|
3
4
|
import { NotuCache } from './NotuCache';
|
|
4
5
|
|
|
@@ -21,4 +22,7 @@ export declare class Notu {
|
|
|
21
22
|
getNoteCount(query: string, spaceId?: number): Promise<number>;
|
|
22
23
|
saveNotes(notes: Array<Note>): Promise<Array<Note>>;
|
|
23
24
|
customJob(name: string, data: any): Promise<any>;
|
|
25
|
+
getPages(): Promise<Array<Page>>;
|
|
26
|
+
getPage(id: number): Promise<Page>;
|
|
27
|
+
savePage(page: Page): Promise<Page>;
|
|
24
28
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as Note } from '../models/Note';
|
|
2
|
+
import { default as Page } from '../models/Page';
|
|
2
3
|
import { default as Space } from '../models/Space';
|
|
3
4
|
import { default as Tag } from '../models/Tag';
|
|
4
5
|
import { NotuCacheFetcher } from './HttpCacheFetcher';
|
|
@@ -18,6 +19,7 @@ export declare class NotuCache {
|
|
|
18
19
|
tagFromJSON(tagData: any): Tag;
|
|
19
20
|
private _populateTagLinks;
|
|
20
21
|
noteFromJSON(noteData: any): Note;
|
|
22
|
+
pageFromJSON(pageData: any): Page;
|
|
21
23
|
getSpaces(): Array<Space>;
|
|
22
24
|
getSpace(id: number): Space;
|
|
23
25
|
getSpaceByName(name: string): Space;
|