@mongoosejs/studio 0.2.6 → 0.2.7
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/backend/actions/ChatMessage/executeScript.js +1 -1
- package/backend/actions/Model/createChatMessage.js +54 -0
- package/backend/actions/Model/index.js +2 -0
- package/backend/actions/Model/streamChatMessage.js +58 -0
- package/backend/authorize.js +1 -0
- package/backend/helpers/getModelDescriptions.js +8 -0
- package/frontend/public/app.js +2150 -7
- package/frontend/public/tw.css +23 -0
- package/frontend/src/api.js +60 -0
- package/frontend/src/create-document/create-document.html +36 -1
- package/frontend/src/create-document/create-document.js +51 -1
- package/frontend/src/models/models.html +41 -3
- package/frontend/src/models/models.js +252 -3
- package/package.json +3 -2
package/frontend/public/app.js
CHANGED
|
@@ -704,6 +704,14 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
704
704
|
updateDocument: function updateDocument(params) {
|
|
705
705
|
return client.post('', { action: 'Model.updateDocument', ...params }).then(res => res.data);
|
|
706
706
|
},
|
|
707
|
+
createChatMessage(params) {
|
|
708
|
+
return client.post('', { action: 'Model.createChatMessage', ...params }).then(res => res.data);
|
|
709
|
+
},
|
|
710
|
+
streamChatMessage: async function* streamChatMessage(params) {
|
|
711
|
+
// Don't stream on Next.js or Netlify for now.
|
|
712
|
+
const data = await client.post('', { action: 'Model.createChatMessage', ...params }).then(res => res.data);
|
|
713
|
+
yield { textPart: data.text };
|
|
714
|
+
},
|
|
707
715
|
updateDocuments: function updateDocuments(params) {
|
|
708
716
|
return client.post('', { action: 'Model.updateDocuments', ...params }).then(res => res.data);
|
|
709
717
|
}
|
|
@@ -807,6 +815,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
807
815
|
createChart: function(params) {
|
|
808
816
|
return client.post('/Model/createChart', params).then(res => res.data);
|
|
809
817
|
},
|
|
818
|
+
createChatMessage: function(params) {
|
|
819
|
+
return client.post('/Model/createChatMessage', params).then(res => res.data);
|
|
820
|
+
},
|
|
810
821
|
createDocument: function(params) {
|
|
811
822
|
return client.post('/Model/createDocument', params).then(res => res.data);
|
|
812
823
|
},
|
|
@@ -913,6 +924,55 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
913
924
|
updateDocument: function updateDocument(params) {
|
|
914
925
|
return client.post('/Model/updateDocument', params).then(res => res.data);
|
|
915
926
|
},
|
|
927
|
+
streamChatMessage: async function* streamChatMessage(params) {
|
|
928
|
+
const accessToken = window.localStorage.getItem('_mongooseStudioAccessToken') || null;
|
|
929
|
+
const url = window.MONGOOSE_STUDIO_CONFIG.baseURL + '/Model/streamChatMessage?' + new URLSearchParams(params).toString();
|
|
930
|
+
|
|
931
|
+
const response = await fetch(url, {
|
|
932
|
+
method: 'GET',
|
|
933
|
+
headers: {
|
|
934
|
+
Authorization: `${accessToken}`,
|
|
935
|
+
Accept: 'text/event-stream'
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
if (!response.ok) {
|
|
940
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
const reader = response.body.getReader();
|
|
944
|
+
const decoder = new TextDecoder('utf-8');
|
|
945
|
+
let buffer = '';
|
|
946
|
+
|
|
947
|
+
while (true) {
|
|
948
|
+
const { done, value } = await reader.read();
|
|
949
|
+
if (done) break;
|
|
950
|
+
buffer += decoder.decode(value, { stream: true });
|
|
951
|
+
|
|
952
|
+
let eventEnd;
|
|
953
|
+
while ((eventEnd = buffer.indexOf('\n\n')) !== -1) {
|
|
954
|
+
const eventStr = buffer.slice(0, eventEnd);
|
|
955
|
+
buffer = buffer.slice(eventEnd + 2);
|
|
956
|
+
|
|
957
|
+
// Parse SSE event
|
|
958
|
+
const lines = eventStr.split('\n');
|
|
959
|
+
let data = '';
|
|
960
|
+
for (const line of lines) {
|
|
961
|
+
if (line.startsWith('data:')) {
|
|
962
|
+
data += line.slice(5).trim();
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
if (data) {
|
|
966
|
+
try {
|
|
967
|
+
yield JSON.parse(data);
|
|
968
|
+
} catch (err) {
|
|
969
|
+
// If not JSON, yield as string
|
|
970
|
+
yield data;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
},
|
|
916
976
|
updateDocuments: function updateDocument(params) {
|
|
917
977
|
return client.post('/Model/updateDocuments', params).then(res => res.data);
|
|
918
978
|
}
|
|
@@ -1869,7 +1929,7 @@ module.exports = "";
|
|
|
1869
1929
|
(module) {
|
|
1870
1930
|
|
|
1871
1931
|
"use strict";
|
|
1872
|
-
module.exports = "<div>\n <div class=\"mb-2\">\n <textarea class=\"border border-gray-200 p-2 h-[300px] w-full\" ref=\"codeEditor\"></textarea>\n </div>\n <button @click=\"createDocument()\" class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">Submit</button>\n <div v-if=\"errors.length > 0\" class=\"rounded-md bg-red-50 p-4 mt-1\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">There were {{errors.length}} errors with your submission</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n <ul role=\"list\" class=\"list-disc space-y-1 pl-5\">\n <li v-for=\"error in errors\">\n {{error}}\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
|
|
1932
|
+
module.exports = "<div>\n <div class=\"mb-4\">\n <label class=\"block text-sm font-bold text-gray-900\">AI Mode</label>\n <div class=\"mt-2 flex flex-col gap-2 sm:flex-row sm:items-center\">\n <input\n v-model=\"aiPrompt\"\n type=\"text\"\n placeholder=\"Describe the document you'd like to create...\"\n @keydown.enter.prevent=\"requestAiSuggestion()\"\n class=\"w-full rounded-md border border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-ultramarine-500 focus:outline-none focus:ring-1 focus:ring-ultramarine-500\"\n />\n <button\n @click=\"requestAiSuggestion()\"\n :disabled=\"aiStreaming || !aiPrompt.trim()\"\n class=\"inline-flex items-center justify-center rounded-md bg-ultramarine-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {{ aiStreaming ? 'Generating...' : 'Generate' }}\n </button>\n </div>\n <p class=\"mt-2 text-xs text-gray-500\">Use AI to draft the document. You can accept or reject the suggestion once it finishes.</p>\n <div v-if=\"aiSuggestionReady\" class=\"mt-3 flex flex-wrap gap-2\">\n <button\n @click=\"acceptAiSuggestion()\"\n class=\"rounded-md bg-emerald-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-emerald-500\"\n >\n Accept suggestion\n </button>\n <button\n @click=\"rejectAiSuggestion()\"\n class=\"rounded-md bg-gray-100 px-2.5 py-1.5 text-sm font-semibold text-gray-700 shadow-sm hover:bg-gray-200\"\n >\n Reject suggestion\n </button>\n </div>\n </div>\n <div class=\"mb-2\">\n <label class=\"block text-sm font-bold text-gray-900\">Document to Create</label>\n <textarea class=\"border border-gray-200 p-2 h-[300px] w-full mt-2\" ref=\"codeEditor\"></textarea>\n </div>\n <button @click=\"createDocument()\" class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">Submit</button>\n <div v-if=\"errors.length > 0\" class=\"rounded-md bg-red-50 p-4 mt-1\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">There were {{errors.length}} errors with your submission</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n <ul role=\"list\" class=\"list-disc space-y-1 pl-5\">\n <li v-for=\"error in errors\">\n {{error}}\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
|
|
1873
1933
|
|
|
1874
1934
|
/***/ },
|
|
1875
1935
|
|
|
@@ -1905,10 +1965,60 @@ module.exports = app => app.component('create-document', {
|
|
|
1905
1965
|
return {
|
|
1906
1966
|
documentData: '',
|
|
1907
1967
|
editor: null,
|
|
1908
|
-
errors: []
|
|
1968
|
+
errors: [],
|
|
1969
|
+
aiPrompt: '',
|
|
1970
|
+
aiSuggestion: '',
|
|
1971
|
+
aiOriginalDocument: '',
|
|
1972
|
+
aiStreaming: false,
|
|
1973
|
+
aiSuggestionReady: false
|
|
1909
1974
|
};
|
|
1910
1975
|
},
|
|
1911
1976
|
methods: {
|
|
1977
|
+
async requestAiSuggestion() {
|
|
1978
|
+
if (this.aiStreaming) {
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
const prompt = this.aiPrompt.trim();
|
|
1982
|
+
if (!prompt) {
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
this.aiOriginalDocument = this.editor.getValue();
|
|
1987
|
+
this.aiSuggestion = '';
|
|
1988
|
+
this.aiSuggestionReady = false;
|
|
1989
|
+
this.aiStreaming = true;
|
|
1990
|
+
|
|
1991
|
+
try {
|
|
1992
|
+
for await (const event of api.Model.streamChatMessage({
|
|
1993
|
+
model: this.currentModel,
|
|
1994
|
+
content: prompt,
|
|
1995
|
+
documentData: this.aiOriginalDocument
|
|
1996
|
+
})) {
|
|
1997
|
+
if (event?.textPart) {
|
|
1998
|
+
this.aiSuggestion += event.textPart;
|
|
1999
|
+
this.editor.setValue(this.aiSuggestion);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
this.aiSuggestionReady = true;
|
|
2003
|
+
} catch (err) {
|
|
2004
|
+
this.editor.setValue(this.aiOriginalDocument);
|
|
2005
|
+
this.$toast.error('Failed to generate a document suggestion.');
|
|
2006
|
+
throw err;
|
|
2007
|
+
} finally {
|
|
2008
|
+
this.aiStreaming = false;
|
|
2009
|
+
}
|
|
2010
|
+
},
|
|
2011
|
+
acceptAiSuggestion() {
|
|
2012
|
+
this.aiSuggestionReady = false;
|
|
2013
|
+
this.aiSuggestion = '';
|
|
2014
|
+
this.aiOriginalDocument = '';
|
|
2015
|
+
},
|
|
2016
|
+
rejectAiSuggestion() {
|
|
2017
|
+
this.editor.setValue(this.aiOriginalDocument);
|
|
2018
|
+
this.aiSuggestionReady = false;
|
|
2019
|
+
this.aiSuggestion = '';
|
|
2020
|
+
this.aiOriginalDocument = '';
|
|
2021
|
+
},
|
|
1912
2022
|
async createDocument() {
|
|
1913
2023
|
const data = EJSON.serialize(eval(`(${this.editor.getValue()})`));
|
|
1914
2024
|
try {
|
|
@@ -6781,7 +6891,7 @@ module.exports = ".models {\n position: relative;\n display: flex;\n flex-dir
|
|
|
6781
6891
|
(module) {
|
|
6782
6892
|
|
|
6783
6893
|
"use strict";
|
|
6784
|
-
module.exports = "<div class=\"models flex\" style=\"height: calc(100vh - 55px); height: calc(100dvh - 55px)\">\n <div class=\"fixed top-[65px] cursor-pointer bg-gray-100 rounded-r-md z-10\" @click=\"hideSidebar = false\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"#5f6368\"><path d=\"M360-120v-720h80v720h-80Zm160-160v-400l200 200-200 200Z\"/></svg>\n </div>\n <aside class=\"bg-white border-r overflow-y-auto overflow-x-hidden h-full transition-all duration-300 ease-in-out z-20 w-0 lg:w-48 fixed lg:relative shrink-0\" :class=\"hideSidebar === true ? '!w-0' : hideSidebar === false ? '!w-48' : ''\">\n <div class=\"flex items-center border-b border-gray-100 w-48 overflow-x-hidden\">\n <div class=\"p-1 ml-2 font-bold\">Models</div>\n <button\n @click=\"hideSidebar = true\"\n class=\"ml-auto mr-2 p-2 rounded hover:bg-gray-200 focus:outline-none\"\n aria-label=\"Close sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"currentColor\"><path d=\"M660-320v-320L500-480l160 160ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm120-80v-560H200v560h120Zm80 0h360v-560H400v560Zm-80 0H200h120Z\"/></svg>\n </button>\n </div>\n <nav class=\"flex flex-1 flex-col\">\n <ul role=\"list\" class=\"flex flex-1 flex-col gap-y-7 p-1\">\n <li>\n <ul role=\"list\">\n <li v-for=\"model in models\">\n <router-link\n :to=\"'/model/' + model\"\n class=\"block truncate rounded-md py-2 pr-2 pl-2 text-sm text-gray-700\"\n :class=\"model === currentModel ? 'bg-ultramarine-100 font-bold text-gray-900' : 'hover:bg-ultramarine-50'\">\n {{model}}\n </router-link>\n </li>\n </ul>\n </li>\n </ul>\n <div v-if=\"models.length === 0 && status === 'loaded'\" class=\"p-2 bg-red-100\">\n No models found\n </div>\n </nav>\n </aside>\n <div class=\"documents bg-slate-50\" ref=\"documentsList\">\n <div class=\"relative h-[42px] z-10\">\n <div class=\"documents-menu bg-slate-50\">\n <div class=\"flex flex-row items-center w-full gap-2\">\n <document-search\n ref=\"documentSearch\"\n :value=\"searchText\"\n :schema-paths=\"schemaPaths\"\n @search=\"search\"\n >\n </document-search>\n <div>\n <span v-if=\"numDocuments == null\">Loading ...</span>\n <span v-else-if=\"typeof numDocuments === 'number'\">{{numDocuments === 1 ? numDocuments+ ' document' : numDocuments + ' documents'}}</span>\n </div>\n <button\n @click=\"stagingSelect\"\n type=\"button\"\n :class=\"{ 'bg-gray-500 ring-inset ring-2 ring-gray-300 hover:bg-gray-600': selectMultiple, 'bg-ultramarine-600 hover:bg-ultramarine-500' : !selectMultiple }\"\n class=\"rounded px-2 py-2 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n >\n {{ selectMultiple ? 'Cancel' : 'Select' }}\n </button>\n <button\n v-show=\"selectMultiple\"\n @click=\"shouldShowUpdateMultipleModal=true;\"\n type=\"button\"\n class=\"rounded bg-green-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\"\n >\n Update\n </button>\n <button\n @click=\"shouldShowDeleteMultipleModal=true;\"\n type=\"button\"\n v-show=\"selectMultiple\"\n class=\"rounded bg-red-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500\"\n >\n Delete\n </button>\n <div class=\"relative\" v-show=\"!selectMultiple\" ref=\"actionsMenuContainer\" @keyup.esc.prevent=\"closeActionsMenu\">\n <button\n @click=\"toggleActionsMenu\"\n type=\"button\"\n aria-label=\"More actions\"\n class=\"rounded bg-white px-2 py-2 text-sm font-semibold text-gray-700 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-5 h-5\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Zm0 6a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Zm0 6a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z\" />\n </svg>\n </button>\n <div\n v-if=\"showActionsMenu\"\n class=\"absolute right-0 mt-2 w-48 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-20\"\n >\n <div class=\"py-1\">\n <button\n @click=\"shouldShowExportModal = true; showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Export\n </button>\n <button\n @click=\"shouldShowCreateModal = true; showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Create\n </button>\n <button\n @click=\"openFieldSelection(); showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Projection\n </button>\n <button\n @click=\"openIndexModal\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Indexes\n </button>\n <button\n @click=\"openCollectionInfo\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Collection Info\n </button>\n <button\n @click=\"findOldestDocument\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Find oldest document\n </button>\n </div>\n </div>\n </div>\n <span class=\"isolate inline-flex rounded-md shadow-sm\">\n <button\n @click=\"setOutputType('table')\"\n type=\"button\"\n class=\"relative inline-flex items-center rounded-none rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'table' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/table.svg\">\n </button>\n <button\n @click=\"setOutputType('json')\"\n type=\"button\"\n class=\"relative -ml-px inline-flex items-center rounded-none rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'json' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/json.svg\">\n </button>\n </span>\n </div>\n </div>\n </div>\n <div class=\"documents-container relative\">\n <div v-if=\"error\">\n <div class=\"bg-red-100 border border-red-400 text-red-700 px-4 py-3 relative m-4 rounded-md\" role=\"alert\">\n <span class=\"block font-bold\">Error</span>\n <span class=\"block\">{{ error }}</span>\n </div>\n </div>\n <table v-else-if=\"outputType === 'table'\">\n <thead class=\"bg-slate-50\">\n <th v-for=\"path in filteredPaths\" @click=\"addPathFilter(path.path)\" class=\"cursor-pointer p-3\">\n {{path.path}}\n <span class=\"path-type\">\n ({{(path.instance || 'unknown')}})\n </span>\n <span class=\"sort-arrow\" @click=\"sortDocs(1, path.path)\">{{sortBy[path.path] == 1 ? 'X' : '↑'}}</span>\n <span class=\"sort-arrow\" @click=\"sortDocs(-1, path.path)\">{{sortBy[path.path] == -1 ? 'X' : '↓'}}</span>\n </th>\n </thead>\n <tbody>\n <tr v-for=\"document in documents\" @click=\"handleDocumentClick(document, $event)\" :key=\"document._id\" class=\"bg-white hover:bg-slate-50\">\n <td v-for=\"schemaPath in filteredPaths\" class=\"p-3 cursor-pointer\" :class=\"{ 'bg-blue-200': selectedDocuments.some(x => x._id.toString() === document._id.toString()) }\">\n <component\n :is=\"getComponentForPath(schemaPath)\"\n :value=\"getValueForPath(document, schemaPath.path)\"\n :allude=\"getReferenceModel(schemaPath)\">\n </component>\n </td>\n </tr>\n </tbody>\n </table>\n <div v-else-if=\"outputType === 'json'\" class=\"flex flex-col space-y-2 p-1 mt-1\">\n <div\n v-for=\"document in documents\"\n :key=\"document._id\"\n @click=\"handleDocumentContainerClick(document, $event)\"\n :class=\"[\n 'group relative transition-colors rounded-md border border-slate-100',\n selectedDocuments.some(x => x._id.toString() === document._id.toString()) ? 'bg-blue-200' : 'hover:shadow-sm hover:border-slate-300 bg-white'\n ]\"\n >\n <button\n type=\"button\"\n class=\"absolute top-2 right-2 z-10 inline-flex items-center rounded bg-ultramarine-600 px-2 py-1 text-xs font-semibold text-white shadow-sm transition-opacity duration-150 opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n @click.stop=\"openDocument(document)\"\n >\n Open this Document\n </button>\n <list-json :value=\"filterDocument(document)\" :references=\"referenceMap\">\n </list-json>\n </div>\n </div>\n <div v-if=\"status === 'loading'\" class=\"loader\">\n <img src=\"images/loader.gif\">\n </div>\n </div>\n </div>\n <modal v-if=\"shouldShowExportModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowExportModal = false\">×</div>\n <export-query-results\n :schemaPaths=\"schemaPaths\"\n :search-text=\"searchText\"\n :currentModel=\"currentModel\"\n @done=\"shouldShowExportModal = false\">\n </export-query-results>\n </template>\n </modal>\n <modal v-if=\"shouldShowIndexModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowIndexModal = false\">×</div>\n <div class=\"text-xl font-bold mb-2\">Indexes</div>\n <div v-for=\"index in mongoDBIndexes\" class=\"w-full flex items-center\">\n <div class=\"grow shrink text-left flex justify-between items-center\" v-if=\"index.name != '_id_'\">\n <div>\n <div class=\"font-bold flex items-center gap-2\">\n <div>{{ index.name }}</div>\n <div v-if=\"isTTLIndex(index)\" class=\"rounded-full bg-ultramarine-100 px-2 py-0.5 text-xs font-semibold text-ultramarine-700\">\n TTL: {{ formatTTL(index.expireAfterSeconds) }}\n </div>\n </div>\n <div class=\"text-sm font-mono\">{{ JSON.stringify(index.key) }}</div>\n </div>\n <div>\n <async-button\n type=\"button\"\n @click=\"dropIndex(index.name)\"\n class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600 disabled:bg-gray-400 disabled:cursor-not-allowed\">\n Drop\n </async-button>\n </div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowCollectionInfoModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCollectionInfoModal = false\">×</div>\n <div class=\"text-xl font-bold mb-2\">Collection Info</div>\n <div v-if=\"!collectionInfo\" class=\"text-gray-600\">Loading collection details...</div>\n <div v-else class=\"space-y-3\">\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Documents</div>\n <div class=\"text-gray-900\">{{ formatNumber(collectionInfo.documentCount) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Indexes</div>\n <div class=\"text-gray-900\">{{ formatNumber(collectionInfo.indexCount) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Total Index Size</div>\n <div class=\"text-gray-900\">{{ formatCollectionSize(collectionInfo.totalIndexSize) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Total Storage Size</div>\n <div class=\"text-gray-900\">{{ formatCollectionSize(collectionInfo.size) }}</div>\n </div>\n <div class=\"flex flex-col gap-1\">\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Collation</div>\n <div class=\"text-gray-900\">{{ collectionInfo.hasCollation ? 'Yes' : 'No' }}</div>\n </div>\n <div v-if=\"collectionInfo.hasCollation\" class=\"rounded bg-gray-100 p-3 text-sm text-gray-800 overflow-x-auto\">\n <pre class=\"whitespace-pre-wrap\">{{ JSON.stringify(collectionInfo.collation, null, 2) }}</pre>\n </div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Capped</div>\n <div class=\"text-gray-900\">{{ collectionInfo.capped ? 'Yes' : 'No' }}</div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowFieldModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowFieldModal = false; selectedPaths = [...filteredPaths];\">×</div>\n <div v-for=\"(path, index) in schemaPaths\" :key=\"index\" class=\"w-5 flex items-center\">\n <input class=\"mt-0 h-4 w-4 rounded border-gray-300 text-sky-600 focus:ring-sky-600 accent-sky-600\" type=\"checkbox\" :id=\"'path.path'+index\" @change=\"addOrRemove(path)\" :value=\"path.path\" :checked=\"isSelected(path.path)\" />\n <div class=\"ml-2 text-gray-700 grow shrink text-left\">\n <label :for=\"'path.path' + index\">{{path.path}}</label>\n </div>\n </div>\n <div class=\"mt-4 flex gap-2\">\n <button type=\"button\" @click=\"filterDocuments()\" class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">Filter Selection</button>\n <button type=\"button\" @click=\"selectAll()\" class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">Select All</button>\n <button type=\"button\" @click=\"deselectAll()\" class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">Deselect All</button>\n <button type=\"button\" @click=\"resetDocuments()\" class=\"rounded-md bg-gray-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\" >Cancel</button>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowCreateModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCreateModal = false;\">×</div>\n <create-document :currentModel=\"currentModel\" :paths=\"schemaPaths\" @close=\"closeCreationModal\"></create-document>\n </template>\n </modal>\n <modal v-if=\"shouldShowUpdateMultipleModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowUpdateMultipleModal = false;\">×</div>\n <update-document :currentModel=\"currentModel\" :document=\"selectedDocuments\" :multiple=\"true\" @update=\"updateDocuments\" @close=\"shouldShowUpdateMultipleModal=false;\"></update-document>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteMultipleModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteMultipleModal = false;\">×</div>\n <h2>Are you sure you want to delete {{selectedDocuments.length}} documents?</h2>\n <div>\n <list-json :value=\"selectedDocuments\"></list-json>\n </div>\n <div class=\"flex gap-4\">\n <async-button @click=\"deleteDocuments\" class=\"rounded bg-red-500 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n Confirm\n </async-button>\n <button @click=\"shouldShowDeleteMultipleModal = false;\" class=\"rounded bg-gray-400 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500\">\n Cancel\n </button>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
6894
|
+
module.exports = "<div class=\"models flex\" style=\"height: calc(100vh - 55px); height: calc(100dvh - 55px)\">\n <div class=\"fixed top-[65px] cursor-pointer bg-gray-100 rounded-r-md z-10\" @click=\"hideSidebar = false\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"#5f6368\"><path d=\"M360-120v-720h80v720h-80Zm160-160v-400l200 200-200 200Z\"/></svg>\n </div>\n <aside class=\"bg-white border-r overflow-y-auto overflow-x-hidden h-full transition-all duration-300 ease-in-out z-20 w-0 lg:w-48 fixed lg:relative shrink-0\" :class=\"hideSidebar === true ? '!w-0' : hideSidebar === false ? '!w-48' : ''\">\n <div class=\"flex items-center border-b border-gray-100 w-48 overflow-x-hidden\">\n <div class=\"p-1 ml-2 font-bold\">Models</div>\n <button\n @click=\"hideSidebar = true\"\n class=\"ml-auto mr-2 p-2 rounded hover:bg-gray-200 focus:outline-none\"\n aria-label=\"Close sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"currentColor\"><path d=\"M660-320v-320L500-480l160 160ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm120-80v-560H200v560h120Zm80 0h360v-560H400v560Zm-80 0H200h120Z\"/></svg>\n </button>\n </div>\n <nav class=\"flex flex-1 flex-col\">\n <ul role=\"list\" class=\"flex flex-1 flex-col gap-y-7 p-1\">\n <li>\n <ul role=\"list\">\n <li v-for=\"model in models\">\n <router-link\n :to=\"'/model/' + model\"\n class=\"block truncate rounded-md py-2 pr-2 pl-2 text-sm text-gray-700\"\n :class=\"model === currentModel ? 'bg-ultramarine-100 font-bold text-gray-900' : 'hover:bg-ultramarine-50'\">\n {{model}}\n </router-link>\n </li>\n </ul>\n </li>\n </ul>\n <div v-if=\"models.length === 0 && status === 'loaded'\" class=\"p-2 bg-red-100\">\n No models found\n </div>\n </nav>\n </aside>\n <div class=\"documents bg-slate-50\" ref=\"documentsList\">\n <div class=\"relative h-[42px]\" style=\"z-index: 1000\">\n <div class=\"documents-menu bg-slate-50\">\n <div class=\"flex flex-row items-center w-full gap-2\">\n <document-search\n ref=\"documentSearch\"\n :value=\"searchText\"\n :schema-paths=\"schemaPaths\"\n @search=\"search\"\n >\n </document-search>\n <div>\n <span v-if=\"numDocuments == null\">Loading ...</span>\n <span v-else-if=\"typeof numDocuments === 'number'\">{{documents.length}}/{{numDocuments === 1 ? numDocuments + ' document' : numDocuments + ' documents'}}</span>\n </div>\n <button\n @click=\"stagingSelect\"\n type=\"button\"\n :class=\"{ 'bg-gray-500 ring-inset ring-2 ring-gray-300 hover:bg-gray-600': selectMultiple, 'bg-ultramarine-600 hover:bg-ultramarine-500' : !selectMultiple }\"\n class=\"rounded px-2 py-2 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n >\n {{ selectMultiple ? 'Cancel' : 'Select' }}\n </button>\n <button\n v-show=\"selectMultiple\"\n @click=\"shouldShowUpdateMultipleModal=true;\"\n type=\"button\"\n class=\"rounded bg-green-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\"\n >\n Update\n </button>\n <button\n @click=\"shouldShowDeleteMultipleModal=true;\"\n type=\"button\"\n v-show=\"selectMultiple\"\n class=\"rounded bg-red-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500\"\n >\n Delete\n </button>\n <div class=\"relative\" v-show=\"!selectMultiple\" ref=\"actionsMenuContainer\" @keyup.esc.prevent=\"closeActionsMenu\">\n <button\n @click=\"toggleActionsMenu\"\n type=\"button\"\n aria-label=\"More actions\"\n class=\"rounded bg-white px-2 py-2 text-sm font-semibold text-gray-700 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-5 h-5\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Zm0 6a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Zm0 6a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z\" />\n </svg>\n </button>\n <div\n v-if=\"showActionsMenu\"\n class=\"absolute right-0 mt-2 w-48 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-20\"\n >\n <div class=\"py-1\">\n <button\n @click=\"shouldShowExportModal = true; showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Export\n </button>\n <button\n @click=\"shouldShowCreateModal = true; showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Create\n </button>\n <button\n @click=\"openFieldSelection(); showActionsMenu = false\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Projection\n </button>\n <button\n @click=\"openIndexModal\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Indexes\n </button>\n <button\n @click=\"openCollectionInfo\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Collection Info\n </button>\n <button\n @click=\"findOldestDocument\"\n type=\"button\"\n class=\"block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100\"\n >\n Find oldest document\n </button>\n </div>\n </div>\n </div>\n <span class=\"isolate inline-flex rounded-md shadow-sm\">\n <button\n @click=\"setOutputType('table')\"\n type=\"button\"\n class=\"relative inline-flex items-center rounded-none rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'table' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/table.svg\">\n </button>\n <button\n @click=\"setOutputType('json')\"\n type=\"button\"\n class=\"relative -ml-px inline-flex items-center px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'json' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/json.svg\">\n </button>\n <button\n @click=\"setOutputType('map')\"\n :disabled=\"geoJsonFields.length === 0\"\n type=\"button\"\n :title=\"geoJsonFields.length > 0 ? 'Map view' : 'No GeoJSON fields detected'\"\n class=\"relative -ml-px inline-flex items-center rounded-none rounded-r-md px-2 py-2 ring-1 ring-inset ring-gray-300 focus:z-10\"\n :class=\"[\n geoJsonFields.length === 0 ? 'text-gray-300 cursor-not-allowed bg-gray-100' : 'text-gray-400 hover:bg-gray-50',\n outputType === 'map' ? 'bg-gray-200' : (geoJsonFields.length > 0 ? 'bg-white' : '')\n ]\">\n <svg class=\"h-5 w-5\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 6.75V15m6-6v8.25m.503 3.498 4.875-2.437c.381-.19.622-.58.622-1.006V4.82c0-.836-.88-1.38-1.628-1.006l-3.869 1.934c-.317.159-.69.159-1.006 0L9.503 3.252a1.125 1.125 0 0 0-1.006 0L3.622 5.689C3.24 5.88 3 6.27 3 6.695V19.18c0 .836.88 1.38 1.628 1.006l3.869-1.934c.317-.159.69-.159 1.006 0l4.994 2.497c.317.158.69.158 1.006 0Z\" />\n </svg>\n </button>\n </span>\n </div>\n </div>\n </div>\n <div class=\"documents-container relative\">\n <div v-if=\"error\">\n <div class=\"bg-red-100 border border-red-400 text-red-700 px-4 py-3 relative m-4 rounded-md\" role=\"alert\">\n <span class=\"block font-bold\">Error</span>\n <span class=\"block\">{{ error }}</span>\n </div>\n </div>\n <table v-else-if=\"outputType === 'table'\">\n <thead class=\"bg-slate-50\">\n <th v-for=\"path in filteredPaths\" @click=\"addPathFilter(path.path)\" class=\"cursor-pointer p-3\">\n {{path.path}}\n <span class=\"path-type\">\n ({{(path.instance || 'unknown')}})\n </span>\n <span class=\"sort-arrow\" @click=\"sortDocs(1, path.path)\">{{sortBy[path.path] == 1 ? 'X' : '↑'}}</span>\n <span class=\"sort-arrow\" @click=\"sortDocs(-1, path.path)\">{{sortBy[path.path] == -1 ? 'X' : '↓'}}</span>\n </th>\n </thead>\n <tbody>\n <tr v-for=\"document in documents\" @click=\"handleDocumentClick(document, $event)\" :key=\"document._id\" class=\"bg-white hover:bg-slate-50\">\n <td v-for=\"schemaPath in filteredPaths\" class=\"p-3 cursor-pointer\" :class=\"{ 'bg-blue-200': selectedDocuments.some(x => x._id.toString() === document._id.toString()) }\">\n <component\n :is=\"getComponentForPath(schemaPath)\"\n :value=\"getValueForPath(document, schemaPath.path)\"\n :allude=\"getReferenceModel(schemaPath)\">\n </component>\n </td>\n </tr>\n </tbody>\n </table>\n <div v-else-if=\"outputType === 'json'\" class=\"flex flex-col space-y-2 p-1 mt-1\">\n <div\n v-for=\"document in documents\"\n :key=\"document._id\"\n @click=\"handleDocumentContainerClick(document, $event)\"\n :class=\"[\n 'group relative transition-colors rounded-md border border-slate-100',\n selectedDocuments.some(x => x._id.toString() === document._id.toString()) ? 'bg-blue-200' : 'hover:shadow-sm hover:border-slate-300 bg-white'\n ]\"\n >\n <button\n type=\"button\"\n class=\"absolute top-2 right-2 z-10 inline-flex items-center rounded bg-ultramarine-600 px-2 py-1 text-xs font-semibold text-white shadow-sm transition-opacity duration-150 opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n @click.stop=\"openDocument(document)\"\n >\n Open this Document\n </button>\n <list-json :value=\"filterDocument(document)\" :references=\"referenceMap\">\n </list-json>\n </div>\n </div>\n <div v-else-if=\"outputType === 'map'\" class=\"flex flex-col h-full\">\n <div class=\"p-2 bg-white border-b flex items-center gap-2\">\n <label class=\"text-sm font-medium text-gray-700\">GeoJSON Field:</label>\n <select\n :value=\"selectedGeoField\"\n @change=\"setSelectedGeoField($event.target.value)\"\n class=\"rounded-md border border-gray-300 py-1 px-2 text-sm focus:border-ultramarine-500 focus:ring-ultramarine-500\"\n >\n <option v-for=\"field in geoJsonFields\" :key=\"field.path\" :value=\"field.path\">\n {{ field.label }}\n </option>\n </select>\n <async-button\n @click=\"loadMoreDocuments\"\n :disabled=\"loadedAllDocs\"\n type=\"button\"\n class=\"rounded px-2 py-1 text-xs font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\"\n :class=\"loadedAllDocs ? 'bg-gray-400 cursor-not-allowed' : 'bg-ultramarine-600 hover:bg-ultramarine-500'\"\n >\n Load more\n </async-button>\n </div>\n <div class=\"flex-1 min-h-[400px]\" ref=\"modelsMap\"></div>\n </div>\n <div v-if=\"status === 'loading'\" class=\"loader\">\n <img src=\"images/loader.gif\">\n </div>\n </div>\n </div>\n <modal v-if=\"shouldShowExportModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowExportModal = false\">×</div>\n <export-query-results\n :schemaPaths=\"schemaPaths\"\n :search-text=\"searchText\"\n :currentModel=\"currentModel\"\n @done=\"shouldShowExportModal = false\">\n </export-query-results>\n </template>\n </modal>\n <modal v-if=\"shouldShowIndexModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowIndexModal = false\">×</div>\n <div class=\"text-xl font-bold mb-2\">Indexes</div>\n <div v-for=\"index in mongoDBIndexes\" class=\"w-full flex items-center\">\n <div class=\"grow shrink text-left flex justify-between items-center\" v-if=\"index.name != '_id_'\">\n <div>\n <div class=\"font-bold flex items-center gap-2\">\n <div>{{ index.name }}</div>\n <div v-if=\"isTTLIndex(index)\" class=\"rounded-full bg-ultramarine-100 px-2 py-0.5 text-xs font-semibold text-ultramarine-700\">\n TTL: {{ formatTTL(index.expireAfterSeconds) }}\n </div>\n </div>\n <div class=\"text-sm font-mono\">{{ JSON.stringify(index.key) }}</div>\n </div>\n <div>\n <async-button\n type=\"button\"\n @click=\"dropIndex(index.name)\"\n class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600 disabled:bg-gray-400 disabled:cursor-not-allowed\">\n Drop\n </async-button>\n </div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowCollectionInfoModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCollectionInfoModal = false\">×</div>\n <div class=\"text-xl font-bold mb-2\">Collection Info</div>\n <div v-if=\"!collectionInfo\" class=\"text-gray-600\">Loading collection details...</div>\n <div v-else class=\"space-y-3\">\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Documents</div>\n <div class=\"text-gray-900\">{{ formatNumber(collectionInfo.documentCount) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Indexes</div>\n <div class=\"text-gray-900\">{{ formatNumber(collectionInfo.indexCount) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Total Index Size</div>\n <div class=\"text-gray-900\">{{ formatCollectionSize(collectionInfo.totalIndexSize) }}</div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Total Storage Size</div>\n <div class=\"text-gray-900\">{{ formatCollectionSize(collectionInfo.size) }}</div>\n </div>\n <div class=\"flex flex-col gap-1\">\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Collation</div>\n <div class=\"text-gray-900\">{{ collectionInfo.hasCollation ? 'Yes' : 'No' }}</div>\n </div>\n <div v-if=\"collectionInfo.hasCollation\" class=\"rounded bg-gray-100 p-3 text-sm text-gray-800 overflow-x-auto\">\n <pre class=\"whitespace-pre-wrap\">{{ JSON.stringify(collectionInfo.collation, null, 2) }}</pre>\n </div>\n </div>\n <div class=\"flex justify-between gap-4\">\n <div class=\"font-semibold text-gray-700\">Capped</div>\n <div class=\"text-gray-900\">{{ collectionInfo.capped ? 'Yes' : 'No' }}</div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowFieldModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowFieldModal = false; selectedPaths = [...filteredPaths];\">×</div>\n <div v-for=\"(path, index) in schemaPaths\" :key=\"index\" class=\"w-5 flex items-center\">\n <input class=\"mt-0 h-4 w-4 rounded border-gray-300 text-sky-600 focus:ring-sky-600 accent-sky-600\" type=\"checkbox\" :id=\"'path.path'+index\" @change=\"addOrRemove(path)\" :value=\"path.path\" :checked=\"isSelected(path.path)\" />\n <div class=\"ml-2 text-gray-700 grow shrink text-left\">\n <label :for=\"'path.path' + index\">{{path.path}}</label>\n </div>\n </div>\n <div class=\"mt-4 flex gap-2\">\n <button type=\"button\" @click=\"filterDocuments()\" class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">Filter Selection</button>\n <button type=\"button\" @click=\"selectAll()\" class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">Select All</button>\n <button type=\"button\" @click=\"deselectAll()\" class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">Deselect All</button>\n <button type=\"button\" @click=\"resetDocuments()\" class=\"rounded-md bg-gray-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\" >Cancel</button>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowCreateModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCreateModal = false;\">×</div>\n <create-document :currentModel=\"currentModel\" :paths=\"schemaPaths\" @close=\"closeCreationModal\"></create-document>\n </template>\n </modal>\n <modal v-if=\"shouldShowUpdateMultipleModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowUpdateMultipleModal = false;\">×</div>\n <update-document :currentModel=\"currentModel\" :document=\"selectedDocuments\" :multiple=\"true\" @update=\"updateDocuments\" @close=\"shouldShowUpdateMultipleModal=false;\"></update-document>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteMultipleModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteMultipleModal = false;\">×</div>\n <h2>Are you sure you want to delete {{selectedDocuments.length}} documents?</h2>\n <div>\n <list-json :value=\"selectedDocuments\"></list-json>\n </div>\n <div class=\"flex gap-4\">\n <async-button @click=\"deleteDocuments\" class=\"rounded bg-red-500 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n Confirm\n </async-button>\n <button @click=\"shouldShowDeleteMultipleModal = false;\" class=\"rounded bg-gray-400 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500\">\n Cancel\n </button>\n </div>\n </template>\n </modal>\n</div>\n";
|
|
6785
6895
|
|
|
6786
6896
|
/***/ },
|
|
6787
6897
|
|
|
@@ -6794,15 +6904,19 @@ module.exports = "<div class=\"models flex\" style=\"height: calc(100vh - 55px);
|
|
|
6794
6904
|
"use strict";
|
|
6795
6905
|
|
|
6796
6906
|
|
|
6907
|
+
/* global L */
|
|
6908
|
+
|
|
6797
6909
|
const api = __webpack_require__(/*! ../api */ "./frontend/src/api.js");
|
|
6798
6910
|
const template = __webpack_require__(/*! ./models.html */ "./frontend/src/models/models.html");
|
|
6799
6911
|
const mpath = __webpack_require__(/*! mpath */ "./node_modules/mpath/index.js");
|
|
6912
|
+
const xss = __webpack_require__(/*! xss */ "./node_modules/xss/lib/index.js");
|
|
6800
6913
|
|
|
6801
6914
|
const appendCSS = __webpack_require__(/*! ../appendCSS */ "./frontend/src/appendCSS.js");
|
|
6802
6915
|
appendCSS(__webpack_require__(/*! ./models.css */ "./frontend/src/models/models.css"));
|
|
6803
6916
|
|
|
6804
6917
|
const limit = 20;
|
|
6805
6918
|
const OUTPUT_TYPE_STORAGE_KEY = 'studio:model-output-type';
|
|
6919
|
+
const SELECTED_GEO_FIELD_STORAGE_KEY = 'studio:model-selected-geo-field';
|
|
6806
6920
|
|
|
6807
6921
|
module.exports = app => app.component('models', {
|
|
6808
6922
|
template: template,
|
|
@@ -6836,7 +6950,10 @@ module.exports = app => app.component('models', {
|
|
|
6836
6950
|
query: {},
|
|
6837
6951
|
scrollHeight: 0,
|
|
6838
6952
|
interval: null,
|
|
6839
|
-
outputType: 'table', // json, table
|
|
6953
|
+
outputType: 'table', // json, table, map
|
|
6954
|
+
selectedGeoField: null,
|
|
6955
|
+
mapInstance: null,
|
|
6956
|
+
mapLayer: null,
|
|
6840
6957
|
hideSidebar: null,
|
|
6841
6958
|
lastSelectedIndex: null,
|
|
6842
6959
|
error: null,
|
|
@@ -6846,11 +6963,13 @@ module.exports = app => app.component('models', {
|
|
|
6846
6963
|
created() {
|
|
6847
6964
|
this.currentModel = this.model;
|
|
6848
6965
|
this.loadOutputPreference();
|
|
6966
|
+
this.loadSelectedGeoField();
|
|
6849
6967
|
},
|
|
6850
6968
|
beforeDestroy() {
|
|
6851
6969
|
document.removeEventListener('scroll', this.onScroll, true);
|
|
6852
6970
|
window.removeEventListener('popstate', this.onPopState, true);
|
|
6853
6971
|
document.removeEventListener('click', this.onOutsideActionsMenuClick, true);
|
|
6972
|
+
this.destroyMap();
|
|
6854
6973
|
},
|
|
6855
6974
|
async mounted() {
|
|
6856
6975
|
this.onScroll = () => this.checkIfScrolledToBottom();
|
|
@@ -6882,6 +7001,37 @@ module.exports = app => app.component('models', {
|
|
|
6882
7001
|
|
|
6883
7002
|
await this.initSearchFromUrl();
|
|
6884
7003
|
},
|
|
7004
|
+
watch: {
|
|
7005
|
+
documents: {
|
|
7006
|
+
handler() {
|
|
7007
|
+
if (this.outputType === 'map' && this.mapInstance) {
|
|
7008
|
+
this.$nextTick(() => {
|
|
7009
|
+
this.updateMapFeatures();
|
|
7010
|
+
});
|
|
7011
|
+
}
|
|
7012
|
+
},
|
|
7013
|
+
deep: true
|
|
7014
|
+
},
|
|
7015
|
+
geoJsonFields: {
|
|
7016
|
+
handler(newFields) {
|
|
7017
|
+
// Switch off map view if map is selected but no GeoJSON fields available
|
|
7018
|
+
if (this.outputType === 'map' && newFields.length === 0) {
|
|
7019
|
+
this.setOutputType('json');
|
|
7020
|
+
return;
|
|
7021
|
+
}
|
|
7022
|
+
// Auto-select first field if current selection is not valid
|
|
7023
|
+
if (this.outputType === 'map' && newFields.length > 0) {
|
|
7024
|
+
const isCurrentValid = newFields.some(f => f.path === this.selectedGeoField);
|
|
7025
|
+
if (!isCurrentValid) {
|
|
7026
|
+
this.selectedGeoField = newFields[0].path;
|
|
7027
|
+
this.$nextTick(() => {
|
|
7028
|
+
this.updateMapFeatures();
|
|
7029
|
+
});
|
|
7030
|
+
}
|
|
7031
|
+
}
|
|
7032
|
+
}
|
|
7033
|
+
}
|
|
7034
|
+
},
|
|
6885
7035
|
computed: {
|
|
6886
7036
|
referenceMap() {
|
|
6887
7037
|
const map = {};
|
|
@@ -6891,6 +7041,56 @@ module.exports = app => app.component('models', {
|
|
|
6891
7041
|
}
|
|
6892
7042
|
}
|
|
6893
7043
|
return map;
|
|
7044
|
+
},
|
|
7045
|
+
geoJsonFields() {
|
|
7046
|
+
// Find schema paths that look like GeoJSON fields
|
|
7047
|
+
// GeoJSON fields have nested 'type' and 'coordinates' properties
|
|
7048
|
+
const geoFields = [];
|
|
7049
|
+
const pathsByPrefix = {};
|
|
7050
|
+
|
|
7051
|
+
// Group paths by their parent prefix
|
|
7052
|
+
for (const schemaPath of this.schemaPaths) {
|
|
7053
|
+
const path = schemaPath.path;
|
|
7054
|
+
const parts = path.split('.');
|
|
7055
|
+
if (parts.length >= 2) {
|
|
7056
|
+
const parent = parts.slice(0, -1).join('.');
|
|
7057
|
+
const child = parts[parts.length - 1];
|
|
7058
|
+
if (!pathsByPrefix[parent]) {
|
|
7059
|
+
pathsByPrefix[parent] = {};
|
|
7060
|
+
}
|
|
7061
|
+
pathsByPrefix[parent][child] = schemaPath;
|
|
7062
|
+
}
|
|
7063
|
+
}
|
|
7064
|
+
|
|
7065
|
+
// Check which parents have both 'type' and 'coordinates' children
|
|
7066
|
+
for (const [parent, children] of Object.entries(pathsByPrefix)) {
|
|
7067
|
+
if (children.type && children.coordinates) {
|
|
7068
|
+
geoFields.push({
|
|
7069
|
+
path: parent,
|
|
7070
|
+
label: parent
|
|
7071
|
+
});
|
|
7072
|
+
}
|
|
7073
|
+
}
|
|
7074
|
+
|
|
7075
|
+
// Also check for Embedded/Mixed fields that might contain GeoJSON
|
|
7076
|
+
// by looking at actual document data
|
|
7077
|
+
for (const schemaPath of this.schemaPaths) {
|
|
7078
|
+
if (schemaPath.instance === 'Embedded' || schemaPath.instance === 'Mixed') {
|
|
7079
|
+
// Check if any document has this field with GeoJSON structure
|
|
7080
|
+
const hasGeoJsonData = this.documents.some(doc => {
|
|
7081
|
+
const value = mpath.get(schemaPath.path, doc);
|
|
7082
|
+
return this.isGeoJsonValue(value);
|
|
7083
|
+
});
|
|
7084
|
+
if (hasGeoJsonData && !geoFields.find(f => f.path === schemaPath.path)) {
|
|
7085
|
+
geoFields.push({
|
|
7086
|
+
path: schemaPath.path,
|
|
7087
|
+
label: schemaPath.path
|
|
7088
|
+
});
|
|
7089
|
+
}
|
|
7090
|
+
}
|
|
7091
|
+
}
|
|
7092
|
+
|
|
7093
|
+
return geoFields;
|
|
6894
7094
|
}
|
|
6895
7095
|
},
|
|
6896
7096
|
methods: {
|
|
@@ -6899,18 +7099,170 @@ module.exports = app => app.component('models', {
|
|
|
6899
7099
|
return;
|
|
6900
7100
|
}
|
|
6901
7101
|
const storedPreference = window.localStorage.getItem(OUTPUT_TYPE_STORAGE_KEY);
|
|
6902
|
-
if (storedPreference === 'json' || storedPreference === 'table') {
|
|
7102
|
+
if (storedPreference === 'json' || storedPreference === 'table' || storedPreference === 'map') {
|
|
6903
7103
|
this.outputType = storedPreference;
|
|
6904
7104
|
}
|
|
6905
7105
|
},
|
|
7106
|
+
loadSelectedGeoField() {
|
|
7107
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
7108
|
+
return;
|
|
7109
|
+
}
|
|
7110
|
+
const storedField = window.localStorage.getItem(SELECTED_GEO_FIELD_STORAGE_KEY);
|
|
7111
|
+
if (storedField) {
|
|
7112
|
+
this.selectedGeoField = storedField;
|
|
7113
|
+
}
|
|
7114
|
+
},
|
|
6906
7115
|
setOutputType(type) {
|
|
6907
|
-
if (type !== 'json' && type !== 'table') {
|
|
7116
|
+
if (type !== 'json' && type !== 'table' && type !== 'map') {
|
|
6908
7117
|
return;
|
|
6909
7118
|
}
|
|
6910
7119
|
this.outputType = type;
|
|
6911
7120
|
if (typeof window !== 'undefined' && window.localStorage) {
|
|
6912
7121
|
window.localStorage.setItem(OUTPUT_TYPE_STORAGE_KEY, type);
|
|
6913
7122
|
}
|
|
7123
|
+
if (type === 'map') {
|
|
7124
|
+
this.$nextTick(() => {
|
|
7125
|
+
this.initMap();
|
|
7126
|
+
});
|
|
7127
|
+
} else {
|
|
7128
|
+
this.destroyMap();
|
|
7129
|
+
}
|
|
7130
|
+
},
|
|
7131
|
+
setSelectedGeoField(field) {
|
|
7132
|
+
this.selectedGeoField = field;
|
|
7133
|
+
if (typeof window !== 'undefined' && window.localStorage) {
|
|
7134
|
+
window.localStorage.setItem(SELECTED_GEO_FIELD_STORAGE_KEY, field);
|
|
7135
|
+
}
|
|
7136
|
+
if (this.outputType === 'map') {
|
|
7137
|
+
this.$nextTick(() => {
|
|
7138
|
+
this.updateMapFeatures();
|
|
7139
|
+
});
|
|
7140
|
+
}
|
|
7141
|
+
},
|
|
7142
|
+
isGeoJsonValue(value) {
|
|
7143
|
+
return value != null &&
|
|
7144
|
+
typeof value === 'object' &&
|
|
7145
|
+
!Array.isArray(value) &&
|
|
7146
|
+
Object.prototype.hasOwnProperty.call(value, 'type') &&
|
|
7147
|
+
typeof value.type === 'string' &&
|
|
7148
|
+
Object.prototype.hasOwnProperty.call(value, 'coordinates') &&
|
|
7149
|
+
Array.isArray(value.coordinates);
|
|
7150
|
+
},
|
|
7151
|
+
initMap() {
|
|
7152
|
+
if (typeof L === 'undefined') {
|
|
7153
|
+
console.error('Leaflet (L) is not defined');
|
|
7154
|
+
return;
|
|
7155
|
+
}
|
|
7156
|
+
if (!this.$refs.modelsMap) {
|
|
7157
|
+
return;
|
|
7158
|
+
}
|
|
7159
|
+
if (this.mapInstance) {
|
|
7160
|
+
this.updateMapFeatures();
|
|
7161
|
+
return;
|
|
7162
|
+
}
|
|
7163
|
+
|
|
7164
|
+
const mapElement = this.$refs.modelsMap;
|
|
7165
|
+
mapElement.style.setProperty('height', '100%', 'important');
|
|
7166
|
+
mapElement.style.setProperty('min-height', '400px', 'important');
|
|
7167
|
+
mapElement.style.setProperty('width', '100%', 'important');
|
|
7168
|
+
|
|
7169
|
+
this.mapInstance = L.map(this.$refs.modelsMap).setView([0, 0], 2);
|
|
7170
|
+
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
7171
|
+
attribution: '© OpenStreetMap contributors'
|
|
7172
|
+
}).addTo(this.mapInstance);
|
|
7173
|
+
|
|
7174
|
+
this.$nextTick(() => {
|
|
7175
|
+
if (this.mapInstance) {
|
|
7176
|
+
this.mapInstance.invalidateSize();
|
|
7177
|
+
this.updateMapFeatures();
|
|
7178
|
+
}
|
|
7179
|
+
});
|
|
7180
|
+
},
|
|
7181
|
+
destroyMap() {
|
|
7182
|
+
if (this.mapLayer) {
|
|
7183
|
+
this.mapLayer.remove();
|
|
7184
|
+
this.mapLayer = null;
|
|
7185
|
+
}
|
|
7186
|
+
if (this.mapInstance) {
|
|
7187
|
+
this.mapInstance.remove();
|
|
7188
|
+
this.mapInstance = null;
|
|
7189
|
+
}
|
|
7190
|
+
},
|
|
7191
|
+
updateMapFeatures() {
|
|
7192
|
+
if (!this.mapInstance) {
|
|
7193
|
+
return;
|
|
7194
|
+
}
|
|
7195
|
+
|
|
7196
|
+
// Remove existing layer
|
|
7197
|
+
if (this.mapLayer) {
|
|
7198
|
+
this.mapLayer.remove();
|
|
7199
|
+
this.mapLayer = null;
|
|
7200
|
+
}
|
|
7201
|
+
|
|
7202
|
+
// Auto-select first geoJSON field if none selected
|
|
7203
|
+
if (!this.selectedGeoField && this.geoJsonFields.length > 0) {
|
|
7204
|
+
this.selectedGeoField = this.geoJsonFields[0].path;
|
|
7205
|
+
}
|
|
7206
|
+
|
|
7207
|
+
if (!this.selectedGeoField) {
|
|
7208
|
+
return;
|
|
7209
|
+
}
|
|
7210
|
+
|
|
7211
|
+
// Build GeoJSON FeatureCollection from documents
|
|
7212
|
+
const features = [];
|
|
7213
|
+
for (const doc of this.documents) {
|
|
7214
|
+
const geoValue = mpath.get(this.selectedGeoField, doc);
|
|
7215
|
+
if (this.isGeoJsonValue(geoValue)) {
|
|
7216
|
+
features.push({
|
|
7217
|
+
type: 'Feature',
|
|
7218
|
+
geometry: geoValue,
|
|
7219
|
+
properties: {
|
|
7220
|
+
_id: doc._id,
|
|
7221
|
+
documentId: doc._id
|
|
7222
|
+
}
|
|
7223
|
+
});
|
|
7224
|
+
}
|
|
7225
|
+
}
|
|
7226
|
+
|
|
7227
|
+
if (features.length === 0) {
|
|
7228
|
+
return;
|
|
7229
|
+
}
|
|
7230
|
+
|
|
7231
|
+
const featureCollection = {
|
|
7232
|
+
type: 'FeatureCollection',
|
|
7233
|
+
features: features
|
|
7234
|
+
};
|
|
7235
|
+
|
|
7236
|
+
// Add layer with click handler for popups
|
|
7237
|
+
this.mapLayer = L.geoJSON(featureCollection, {
|
|
7238
|
+
style: {
|
|
7239
|
+
color: '#3388ff',
|
|
7240
|
+
weight: 2,
|
|
7241
|
+
opacity: 0.8,
|
|
7242
|
+
fillOpacity: 0.3
|
|
7243
|
+
},
|
|
7244
|
+
pointToLayer: (feature, latlng) => {
|
|
7245
|
+
return L.marker(latlng);
|
|
7246
|
+
},
|
|
7247
|
+
onEachFeature: (feature, layer) => {
|
|
7248
|
+
const docId = feature.properties._id;
|
|
7249
|
+
const docUrl = `#/model/${this.currentModel}/document/${xss(docId)}`;
|
|
7250
|
+
const popupContent = `
|
|
7251
|
+
<div style="min-width: 150px;">
|
|
7252
|
+
<div style="font-weight: bold; margin-bottom: 8px;">Document</div>
|
|
7253
|
+
<div style="font-family: monospace; font-size: 12px; word-break: break-all; margin-bottom: 8px;">${docId}</div>
|
|
7254
|
+
<a href="${docUrl}" style="color: #3388ff; text-decoration: underline;">Open Document</a>
|
|
7255
|
+
</div>
|
|
7256
|
+
`;
|
|
7257
|
+
layer.bindPopup(popupContent);
|
|
7258
|
+
}
|
|
7259
|
+
}).addTo(this.mapInstance);
|
|
7260
|
+
|
|
7261
|
+
// Fit bounds to show all features
|
|
7262
|
+
const bounds = this.mapLayer.getBounds();
|
|
7263
|
+
if (bounds.isValid()) {
|
|
7264
|
+
this.mapInstance.fitBounds(bounds, { padding: [20, 20], maxZoom: 16 });
|
|
7265
|
+
}
|
|
6914
7266
|
},
|
|
6915
7267
|
buildDocumentFetchParams(options = {}) {
|
|
6916
7268
|
const params = {
|
|
@@ -6964,6 +7316,13 @@ module.exports = app => app.component('models', {
|
|
|
6964
7316
|
this.filteredPaths = this.filteredPaths.filter(x => filter.includes(x.path));
|
|
6965
7317
|
}
|
|
6966
7318
|
this.status = 'loaded';
|
|
7319
|
+
|
|
7320
|
+
// Initialize map if output type is map
|
|
7321
|
+
if (this.outputType === 'map') {
|
|
7322
|
+
this.$nextTick(() => {
|
|
7323
|
+
this.initMap();
|
|
7324
|
+
});
|
|
7325
|
+
}
|
|
6967
7326
|
},
|
|
6968
7327
|
async dropIndex(name) {
|
|
6969
7328
|
const { mongoDBIndexes } = await api.Model.dropIndex({ model: this.currentModel, name });
|
|
@@ -29517,6 +29876,705 @@ var bson = /*#__PURE__*/Object.freeze({
|
|
|
29517
29876
|
//# sourceMappingURL=bson.mjs.map
|
|
29518
29877
|
|
|
29519
29878
|
|
|
29879
|
+
/***/ },
|
|
29880
|
+
|
|
29881
|
+
/***/ "./node_modules/cssfilter/lib/css.js"
|
|
29882
|
+
/*!*******************************************!*\
|
|
29883
|
+
!*** ./node_modules/cssfilter/lib/css.js ***!
|
|
29884
|
+
\*******************************************/
|
|
29885
|
+
(module, __unused_webpack_exports, __webpack_require__) {
|
|
29886
|
+
|
|
29887
|
+
/**
|
|
29888
|
+
* cssfilter
|
|
29889
|
+
*
|
|
29890
|
+
* @author 老雷<leizongmin@gmail.com>
|
|
29891
|
+
*/
|
|
29892
|
+
|
|
29893
|
+
var DEFAULT = __webpack_require__(/*! ./default */ "./node_modules/cssfilter/lib/default.js");
|
|
29894
|
+
var parseStyle = __webpack_require__(/*! ./parser */ "./node_modules/cssfilter/lib/parser.js");
|
|
29895
|
+
var _ = __webpack_require__(/*! ./util */ "./node_modules/cssfilter/lib/util.js");
|
|
29896
|
+
|
|
29897
|
+
|
|
29898
|
+
/**
|
|
29899
|
+
* 返回值是否为空
|
|
29900
|
+
*
|
|
29901
|
+
* @param {Object} obj
|
|
29902
|
+
* @return {Boolean}
|
|
29903
|
+
*/
|
|
29904
|
+
function isNull (obj) {
|
|
29905
|
+
return (obj === undefined || obj === null);
|
|
29906
|
+
}
|
|
29907
|
+
|
|
29908
|
+
/**
|
|
29909
|
+
* 浅拷贝对象
|
|
29910
|
+
*
|
|
29911
|
+
* @param {Object} obj
|
|
29912
|
+
* @return {Object}
|
|
29913
|
+
*/
|
|
29914
|
+
function shallowCopyObject (obj) {
|
|
29915
|
+
var ret = {};
|
|
29916
|
+
for (var i in obj) {
|
|
29917
|
+
ret[i] = obj[i];
|
|
29918
|
+
}
|
|
29919
|
+
return ret;
|
|
29920
|
+
}
|
|
29921
|
+
|
|
29922
|
+
/**
|
|
29923
|
+
* 创建CSS过滤器
|
|
29924
|
+
*
|
|
29925
|
+
* @param {Object} options
|
|
29926
|
+
* - {Object} whiteList
|
|
29927
|
+
* - {Function} onAttr
|
|
29928
|
+
* - {Function} onIgnoreAttr
|
|
29929
|
+
* - {Function} safeAttrValue
|
|
29930
|
+
*/
|
|
29931
|
+
function FilterCSS (options) {
|
|
29932
|
+
options = shallowCopyObject(options || {});
|
|
29933
|
+
options.whiteList = options.whiteList || DEFAULT.whiteList;
|
|
29934
|
+
options.onAttr = options.onAttr || DEFAULT.onAttr;
|
|
29935
|
+
options.onIgnoreAttr = options.onIgnoreAttr || DEFAULT.onIgnoreAttr;
|
|
29936
|
+
options.safeAttrValue = options.safeAttrValue || DEFAULT.safeAttrValue;
|
|
29937
|
+
this.options = options;
|
|
29938
|
+
}
|
|
29939
|
+
|
|
29940
|
+
FilterCSS.prototype.process = function (css) {
|
|
29941
|
+
// 兼容各种奇葩输入
|
|
29942
|
+
css = css || '';
|
|
29943
|
+
css = css.toString();
|
|
29944
|
+
if (!css) return '';
|
|
29945
|
+
|
|
29946
|
+
var me = this;
|
|
29947
|
+
var options = me.options;
|
|
29948
|
+
var whiteList = options.whiteList;
|
|
29949
|
+
var onAttr = options.onAttr;
|
|
29950
|
+
var onIgnoreAttr = options.onIgnoreAttr;
|
|
29951
|
+
var safeAttrValue = options.safeAttrValue;
|
|
29952
|
+
|
|
29953
|
+
var retCSS = parseStyle(css, function (sourcePosition, position, name, value, source) {
|
|
29954
|
+
|
|
29955
|
+
var check = whiteList[name];
|
|
29956
|
+
var isWhite = false;
|
|
29957
|
+
if (check === true) isWhite = check;
|
|
29958
|
+
else if (typeof check === 'function') isWhite = check(value);
|
|
29959
|
+
else if (check instanceof RegExp) isWhite = check.test(value);
|
|
29960
|
+
if (isWhite !== true) isWhite = false;
|
|
29961
|
+
|
|
29962
|
+
// 如果过滤后 value 为空则直接忽略
|
|
29963
|
+
value = safeAttrValue(name, value);
|
|
29964
|
+
if (!value) return;
|
|
29965
|
+
|
|
29966
|
+
var opts = {
|
|
29967
|
+
position: position,
|
|
29968
|
+
sourcePosition: sourcePosition,
|
|
29969
|
+
source: source,
|
|
29970
|
+
isWhite: isWhite
|
|
29971
|
+
};
|
|
29972
|
+
|
|
29973
|
+
if (isWhite) {
|
|
29974
|
+
|
|
29975
|
+
var ret = onAttr(name, value, opts);
|
|
29976
|
+
if (isNull(ret)) {
|
|
29977
|
+
return name + ':' + value;
|
|
29978
|
+
} else {
|
|
29979
|
+
return ret;
|
|
29980
|
+
}
|
|
29981
|
+
|
|
29982
|
+
} else {
|
|
29983
|
+
|
|
29984
|
+
var ret = onIgnoreAttr(name, value, opts);
|
|
29985
|
+
if (!isNull(ret)) {
|
|
29986
|
+
return ret;
|
|
29987
|
+
}
|
|
29988
|
+
|
|
29989
|
+
}
|
|
29990
|
+
});
|
|
29991
|
+
|
|
29992
|
+
return retCSS;
|
|
29993
|
+
};
|
|
29994
|
+
|
|
29995
|
+
|
|
29996
|
+
module.exports = FilterCSS;
|
|
29997
|
+
|
|
29998
|
+
|
|
29999
|
+
/***/ },
|
|
30000
|
+
|
|
30001
|
+
/***/ "./node_modules/cssfilter/lib/default.js"
|
|
30002
|
+
/*!***********************************************!*\
|
|
30003
|
+
!*** ./node_modules/cssfilter/lib/default.js ***!
|
|
30004
|
+
\***********************************************/
|
|
30005
|
+
(__unused_webpack_module, exports) {
|
|
30006
|
+
|
|
30007
|
+
/**
|
|
30008
|
+
* cssfilter
|
|
30009
|
+
*
|
|
30010
|
+
* @author 老雷<leizongmin@gmail.com>
|
|
30011
|
+
*/
|
|
30012
|
+
|
|
30013
|
+
function getDefaultWhiteList () {
|
|
30014
|
+
// 白名单值说明:
|
|
30015
|
+
// true: 允许该属性
|
|
30016
|
+
// Function: function (val) { } 返回true表示允许该属性,其他值均表示不允许
|
|
30017
|
+
// RegExp: regexp.test(val) 返回true表示允许该属性,其他值均表示不允许
|
|
30018
|
+
// 除上面列出的值外均表示不允许
|
|
30019
|
+
var whiteList = {};
|
|
30020
|
+
|
|
30021
|
+
whiteList['align-content'] = false; // default: auto
|
|
30022
|
+
whiteList['align-items'] = false; // default: auto
|
|
30023
|
+
whiteList['align-self'] = false; // default: auto
|
|
30024
|
+
whiteList['alignment-adjust'] = false; // default: auto
|
|
30025
|
+
whiteList['alignment-baseline'] = false; // default: baseline
|
|
30026
|
+
whiteList['all'] = false; // default: depending on individual properties
|
|
30027
|
+
whiteList['anchor-point'] = false; // default: none
|
|
30028
|
+
whiteList['animation'] = false; // default: depending on individual properties
|
|
30029
|
+
whiteList['animation-delay'] = false; // default: 0
|
|
30030
|
+
whiteList['animation-direction'] = false; // default: normal
|
|
30031
|
+
whiteList['animation-duration'] = false; // default: 0
|
|
30032
|
+
whiteList['animation-fill-mode'] = false; // default: none
|
|
30033
|
+
whiteList['animation-iteration-count'] = false; // default: 1
|
|
30034
|
+
whiteList['animation-name'] = false; // default: none
|
|
30035
|
+
whiteList['animation-play-state'] = false; // default: running
|
|
30036
|
+
whiteList['animation-timing-function'] = false; // default: ease
|
|
30037
|
+
whiteList['azimuth'] = false; // default: center
|
|
30038
|
+
whiteList['backface-visibility'] = false; // default: visible
|
|
30039
|
+
whiteList['background'] = true; // default: depending on individual properties
|
|
30040
|
+
whiteList['background-attachment'] = true; // default: scroll
|
|
30041
|
+
whiteList['background-clip'] = true; // default: border-box
|
|
30042
|
+
whiteList['background-color'] = true; // default: transparent
|
|
30043
|
+
whiteList['background-image'] = true; // default: none
|
|
30044
|
+
whiteList['background-origin'] = true; // default: padding-box
|
|
30045
|
+
whiteList['background-position'] = true; // default: 0% 0%
|
|
30046
|
+
whiteList['background-repeat'] = true; // default: repeat
|
|
30047
|
+
whiteList['background-size'] = true; // default: auto
|
|
30048
|
+
whiteList['baseline-shift'] = false; // default: baseline
|
|
30049
|
+
whiteList['binding'] = false; // default: none
|
|
30050
|
+
whiteList['bleed'] = false; // default: 6pt
|
|
30051
|
+
whiteList['bookmark-label'] = false; // default: content()
|
|
30052
|
+
whiteList['bookmark-level'] = false; // default: none
|
|
30053
|
+
whiteList['bookmark-state'] = false; // default: open
|
|
30054
|
+
whiteList['border'] = true; // default: depending on individual properties
|
|
30055
|
+
whiteList['border-bottom'] = true; // default: depending on individual properties
|
|
30056
|
+
whiteList['border-bottom-color'] = true; // default: current color
|
|
30057
|
+
whiteList['border-bottom-left-radius'] = true; // default: 0
|
|
30058
|
+
whiteList['border-bottom-right-radius'] = true; // default: 0
|
|
30059
|
+
whiteList['border-bottom-style'] = true; // default: none
|
|
30060
|
+
whiteList['border-bottom-width'] = true; // default: medium
|
|
30061
|
+
whiteList['border-collapse'] = true; // default: separate
|
|
30062
|
+
whiteList['border-color'] = true; // default: depending on individual properties
|
|
30063
|
+
whiteList['border-image'] = true; // default: none
|
|
30064
|
+
whiteList['border-image-outset'] = true; // default: 0
|
|
30065
|
+
whiteList['border-image-repeat'] = true; // default: stretch
|
|
30066
|
+
whiteList['border-image-slice'] = true; // default: 100%
|
|
30067
|
+
whiteList['border-image-source'] = true; // default: none
|
|
30068
|
+
whiteList['border-image-width'] = true; // default: 1
|
|
30069
|
+
whiteList['border-left'] = true; // default: depending on individual properties
|
|
30070
|
+
whiteList['border-left-color'] = true; // default: current color
|
|
30071
|
+
whiteList['border-left-style'] = true; // default: none
|
|
30072
|
+
whiteList['border-left-width'] = true; // default: medium
|
|
30073
|
+
whiteList['border-radius'] = true; // default: 0
|
|
30074
|
+
whiteList['border-right'] = true; // default: depending on individual properties
|
|
30075
|
+
whiteList['border-right-color'] = true; // default: current color
|
|
30076
|
+
whiteList['border-right-style'] = true; // default: none
|
|
30077
|
+
whiteList['border-right-width'] = true; // default: medium
|
|
30078
|
+
whiteList['border-spacing'] = true; // default: 0
|
|
30079
|
+
whiteList['border-style'] = true; // default: depending on individual properties
|
|
30080
|
+
whiteList['border-top'] = true; // default: depending on individual properties
|
|
30081
|
+
whiteList['border-top-color'] = true; // default: current color
|
|
30082
|
+
whiteList['border-top-left-radius'] = true; // default: 0
|
|
30083
|
+
whiteList['border-top-right-radius'] = true; // default: 0
|
|
30084
|
+
whiteList['border-top-style'] = true; // default: none
|
|
30085
|
+
whiteList['border-top-width'] = true; // default: medium
|
|
30086
|
+
whiteList['border-width'] = true; // default: depending on individual properties
|
|
30087
|
+
whiteList['bottom'] = false; // default: auto
|
|
30088
|
+
whiteList['box-decoration-break'] = true; // default: slice
|
|
30089
|
+
whiteList['box-shadow'] = true; // default: none
|
|
30090
|
+
whiteList['box-sizing'] = true; // default: content-box
|
|
30091
|
+
whiteList['box-snap'] = true; // default: none
|
|
30092
|
+
whiteList['box-suppress'] = true; // default: show
|
|
30093
|
+
whiteList['break-after'] = true; // default: auto
|
|
30094
|
+
whiteList['break-before'] = true; // default: auto
|
|
30095
|
+
whiteList['break-inside'] = true; // default: auto
|
|
30096
|
+
whiteList['caption-side'] = false; // default: top
|
|
30097
|
+
whiteList['chains'] = false; // default: none
|
|
30098
|
+
whiteList['clear'] = true; // default: none
|
|
30099
|
+
whiteList['clip'] = false; // default: auto
|
|
30100
|
+
whiteList['clip-path'] = false; // default: none
|
|
30101
|
+
whiteList['clip-rule'] = false; // default: nonzero
|
|
30102
|
+
whiteList['color'] = true; // default: implementation dependent
|
|
30103
|
+
whiteList['color-interpolation-filters'] = true; // default: auto
|
|
30104
|
+
whiteList['column-count'] = false; // default: auto
|
|
30105
|
+
whiteList['column-fill'] = false; // default: balance
|
|
30106
|
+
whiteList['column-gap'] = false; // default: normal
|
|
30107
|
+
whiteList['column-rule'] = false; // default: depending on individual properties
|
|
30108
|
+
whiteList['column-rule-color'] = false; // default: current color
|
|
30109
|
+
whiteList['column-rule-style'] = false; // default: medium
|
|
30110
|
+
whiteList['column-rule-width'] = false; // default: medium
|
|
30111
|
+
whiteList['column-span'] = false; // default: none
|
|
30112
|
+
whiteList['column-width'] = false; // default: auto
|
|
30113
|
+
whiteList['columns'] = false; // default: depending on individual properties
|
|
30114
|
+
whiteList['contain'] = false; // default: none
|
|
30115
|
+
whiteList['content'] = false; // default: normal
|
|
30116
|
+
whiteList['counter-increment'] = false; // default: none
|
|
30117
|
+
whiteList['counter-reset'] = false; // default: none
|
|
30118
|
+
whiteList['counter-set'] = false; // default: none
|
|
30119
|
+
whiteList['crop'] = false; // default: auto
|
|
30120
|
+
whiteList['cue'] = false; // default: depending on individual properties
|
|
30121
|
+
whiteList['cue-after'] = false; // default: none
|
|
30122
|
+
whiteList['cue-before'] = false; // default: none
|
|
30123
|
+
whiteList['cursor'] = false; // default: auto
|
|
30124
|
+
whiteList['direction'] = false; // default: ltr
|
|
30125
|
+
whiteList['display'] = true; // default: depending on individual properties
|
|
30126
|
+
whiteList['display-inside'] = true; // default: auto
|
|
30127
|
+
whiteList['display-list'] = true; // default: none
|
|
30128
|
+
whiteList['display-outside'] = true; // default: inline-level
|
|
30129
|
+
whiteList['dominant-baseline'] = false; // default: auto
|
|
30130
|
+
whiteList['elevation'] = false; // default: level
|
|
30131
|
+
whiteList['empty-cells'] = false; // default: show
|
|
30132
|
+
whiteList['filter'] = false; // default: none
|
|
30133
|
+
whiteList['flex'] = false; // default: depending on individual properties
|
|
30134
|
+
whiteList['flex-basis'] = false; // default: auto
|
|
30135
|
+
whiteList['flex-direction'] = false; // default: row
|
|
30136
|
+
whiteList['flex-flow'] = false; // default: depending on individual properties
|
|
30137
|
+
whiteList['flex-grow'] = false; // default: 0
|
|
30138
|
+
whiteList['flex-shrink'] = false; // default: 1
|
|
30139
|
+
whiteList['flex-wrap'] = false; // default: nowrap
|
|
30140
|
+
whiteList['float'] = false; // default: none
|
|
30141
|
+
whiteList['float-offset'] = false; // default: 0 0
|
|
30142
|
+
whiteList['flood-color'] = false; // default: black
|
|
30143
|
+
whiteList['flood-opacity'] = false; // default: 1
|
|
30144
|
+
whiteList['flow-from'] = false; // default: none
|
|
30145
|
+
whiteList['flow-into'] = false; // default: none
|
|
30146
|
+
whiteList['font'] = true; // default: depending on individual properties
|
|
30147
|
+
whiteList['font-family'] = true; // default: implementation dependent
|
|
30148
|
+
whiteList['font-feature-settings'] = true; // default: normal
|
|
30149
|
+
whiteList['font-kerning'] = true; // default: auto
|
|
30150
|
+
whiteList['font-language-override'] = true; // default: normal
|
|
30151
|
+
whiteList['font-size'] = true; // default: medium
|
|
30152
|
+
whiteList['font-size-adjust'] = true; // default: none
|
|
30153
|
+
whiteList['font-stretch'] = true; // default: normal
|
|
30154
|
+
whiteList['font-style'] = true; // default: normal
|
|
30155
|
+
whiteList['font-synthesis'] = true; // default: weight style
|
|
30156
|
+
whiteList['font-variant'] = true; // default: normal
|
|
30157
|
+
whiteList['font-variant-alternates'] = true; // default: normal
|
|
30158
|
+
whiteList['font-variant-caps'] = true; // default: normal
|
|
30159
|
+
whiteList['font-variant-east-asian'] = true; // default: normal
|
|
30160
|
+
whiteList['font-variant-ligatures'] = true; // default: normal
|
|
30161
|
+
whiteList['font-variant-numeric'] = true; // default: normal
|
|
30162
|
+
whiteList['font-variant-position'] = true; // default: normal
|
|
30163
|
+
whiteList['font-weight'] = true; // default: normal
|
|
30164
|
+
whiteList['grid'] = false; // default: depending on individual properties
|
|
30165
|
+
whiteList['grid-area'] = false; // default: depending on individual properties
|
|
30166
|
+
whiteList['grid-auto-columns'] = false; // default: auto
|
|
30167
|
+
whiteList['grid-auto-flow'] = false; // default: none
|
|
30168
|
+
whiteList['grid-auto-rows'] = false; // default: auto
|
|
30169
|
+
whiteList['grid-column'] = false; // default: depending on individual properties
|
|
30170
|
+
whiteList['grid-column-end'] = false; // default: auto
|
|
30171
|
+
whiteList['grid-column-start'] = false; // default: auto
|
|
30172
|
+
whiteList['grid-row'] = false; // default: depending on individual properties
|
|
30173
|
+
whiteList['grid-row-end'] = false; // default: auto
|
|
30174
|
+
whiteList['grid-row-start'] = false; // default: auto
|
|
30175
|
+
whiteList['grid-template'] = false; // default: depending on individual properties
|
|
30176
|
+
whiteList['grid-template-areas'] = false; // default: none
|
|
30177
|
+
whiteList['grid-template-columns'] = false; // default: none
|
|
30178
|
+
whiteList['grid-template-rows'] = false; // default: none
|
|
30179
|
+
whiteList['hanging-punctuation'] = false; // default: none
|
|
30180
|
+
whiteList['height'] = true; // default: auto
|
|
30181
|
+
whiteList['hyphens'] = false; // default: manual
|
|
30182
|
+
whiteList['icon'] = false; // default: auto
|
|
30183
|
+
whiteList['image-orientation'] = false; // default: auto
|
|
30184
|
+
whiteList['image-resolution'] = false; // default: normal
|
|
30185
|
+
whiteList['ime-mode'] = false; // default: auto
|
|
30186
|
+
whiteList['initial-letters'] = false; // default: normal
|
|
30187
|
+
whiteList['inline-box-align'] = false; // default: last
|
|
30188
|
+
whiteList['justify-content'] = false; // default: auto
|
|
30189
|
+
whiteList['justify-items'] = false; // default: auto
|
|
30190
|
+
whiteList['justify-self'] = false; // default: auto
|
|
30191
|
+
whiteList['left'] = false; // default: auto
|
|
30192
|
+
whiteList['letter-spacing'] = true; // default: normal
|
|
30193
|
+
whiteList['lighting-color'] = true; // default: white
|
|
30194
|
+
whiteList['line-box-contain'] = false; // default: block inline replaced
|
|
30195
|
+
whiteList['line-break'] = false; // default: auto
|
|
30196
|
+
whiteList['line-grid'] = false; // default: match-parent
|
|
30197
|
+
whiteList['line-height'] = false; // default: normal
|
|
30198
|
+
whiteList['line-snap'] = false; // default: none
|
|
30199
|
+
whiteList['line-stacking'] = false; // default: depending on individual properties
|
|
30200
|
+
whiteList['line-stacking-ruby'] = false; // default: exclude-ruby
|
|
30201
|
+
whiteList['line-stacking-shift'] = false; // default: consider-shifts
|
|
30202
|
+
whiteList['line-stacking-strategy'] = false; // default: inline-line-height
|
|
30203
|
+
whiteList['list-style'] = true; // default: depending on individual properties
|
|
30204
|
+
whiteList['list-style-image'] = true; // default: none
|
|
30205
|
+
whiteList['list-style-position'] = true; // default: outside
|
|
30206
|
+
whiteList['list-style-type'] = true; // default: disc
|
|
30207
|
+
whiteList['margin'] = true; // default: depending on individual properties
|
|
30208
|
+
whiteList['margin-bottom'] = true; // default: 0
|
|
30209
|
+
whiteList['margin-left'] = true; // default: 0
|
|
30210
|
+
whiteList['margin-right'] = true; // default: 0
|
|
30211
|
+
whiteList['margin-top'] = true; // default: 0
|
|
30212
|
+
whiteList['marker-offset'] = false; // default: auto
|
|
30213
|
+
whiteList['marker-side'] = false; // default: list-item
|
|
30214
|
+
whiteList['marks'] = false; // default: none
|
|
30215
|
+
whiteList['mask'] = false; // default: border-box
|
|
30216
|
+
whiteList['mask-box'] = false; // default: see individual properties
|
|
30217
|
+
whiteList['mask-box-outset'] = false; // default: 0
|
|
30218
|
+
whiteList['mask-box-repeat'] = false; // default: stretch
|
|
30219
|
+
whiteList['mask-box-slice'] = false; // default: 0 fill
|
|
30220
|
+
whiteList['mask-box-source'] = false; // default: none
|
|
30221
|
+
whiteList['mask-box-width'] = false; // default: auto
|
|
30222
|
+
whiteList['mask-clip'] = false; // default: border-box
|
|
30223
|
+
whiteList['mask-image'] = false; // default: none
|
|
30224
|
+
whiteList['mask-origin'] = false; // default: border-box
|
|
30225
|
+
whiteList['mask-position'] = false; // default: center
|
|
30226
|
+
whiteList['mask-repeat'] = false; // default: no-repeat
|
|
30227
|
+
whiteList['mask-size'] = false; // default: border-box
|
|
30228
|
+
whiteList['mask-source-type'] = false; // default: auto
|
|
30229
|
+
whiteList['mask-type'] = false; // default: luminance
|
|
30230
|
+
whiteList['max-height'] = true; // default: none
|
|
30231
|
+
whiteList['max-lines'] = false; // default: none
|
|
30232
|
+
whiteList['max-width'] = true; // default: none
|
|
30233
|
+
whiteList['min-height'] = true; // default: 0
|
|
30234
|
+
whiteList['min-width'] = true; // default: 0
|
|
30235
|
+
whiteList['move-to'] = false; // default: normal
|
|
30236
|
+
whiteList['nav-down'] = false; // default: auto
|
|
30237
|
+
whiteList['nav-index'] = false; // default: auto
|
|
30238
|
+
whiteList['nav-left'] = false; // default: auto
|
|
30239
|
+
whiteList['nav-right'] = false; // default: auto
|
|
30240
|
+
whiteList['nav-up'] = false; // default: auto
|
|
30241
|
+
whiteList['object-fit'] = false; // default: fill
|
|
30242
|
+
whiteList['object-position'] = false; // default: 50% 50%
|
|
30243
|
+
whiteList['opacity'] = false; // default: 1
|
|
30244
|
+
whiteList['order'] = false; // default: 0
|
|
30245
|
+
whiteList['orphans'] = false; // default: 2
|
|
30246
|
+
whiteList['outline'] = false; // default: depending on individual properties
|
|
30247
|
+
whiteList['outline-color'] = false; // default: invert
|
|
30248
|
+
whiteList['outline-offset'] = false; // default: 0
|
|
30249
|
+
whiteList['outline-style'] = false; // default: none
|
|
30250
|
+
whiteList['outline-width'] = false; // default: medium
|
|
30251
|
+
whiteList['overflow'] = false; // default: depending on individual properties
|
|
30252
|
+
whiteList['overflow-wrap'] = false; // default: normal
|
|
30253
|
+
whiteList['overflow-x'] = false; // default: visible
|
|
30254
|
+
whiteList['overflow-y'] = false; // default: visible
|
|
30255
|
+
whiteList['padding'] = true; // default: depending on individual properties
|
|
30256
|
+
whiteList['padding-bottom'] = true; // default: 0
|
|
30257
|
+
whiteList['padding-left'] = true; // default: 0
|
|
30258
|
+
whiteList['padding-right'] = true; // default: 0
|
|
30259
|
+
whiteList['padding-top'] = true; // default: 0
|
|
30260
|
+
whiteList['page'] = false; // default: auto
|
|
30261
|
+
whiteList['page-break-after'] = false; // default: auto
|
|
30262
|
+
whiteList['page-break-before'] = false; // default: auto
|
|
30263
|
+
whiteList['page-break-inside'] = false; // default: auto
|
|
30264
|
+
whiteList['page-policy'] = false; // default: start
|
|
30265
|
+
whiteList['pause'] = false; // default: implementation dependent
|
|
30266
|
+
whiteList['pause-after'] = false; // default: implementation dependent
|
|
30267
|
+
whiteList['pause-before'] = false; // default: implementation dependent
|
|
30268
|
+
whiteList['perspective'] = false; // default: none
|
|
30269
|
+
whiteList['perspective-origin'] = false; // default: 50% 50%
|
|
30270
|
+
whiteList['pitch'] = false; // default: medium
|
|
30271
|
+
whiteList['pitch-range'] = false; // default: 50
|
|
30272
|
+
whiteList['play-during'] = false; // default: auto
|
|
30273
|
+
whiteList['position'] = false; // default: static
|
|
30274
|
+
whiteList['presentation-level'] = false; // default: 0
|
|
30275
|
+
whiteList['quotes'] = false; // default: text
|
|
30276
|
+
whiteList['region-fragment'] = false; // default: auto
|
|
30277
|
+
whiteList['resize'] = false; // default: none
|
|
30278
|
+
whiteList['rest'] = false; // default: depending on individual properties
|
|
30279
|
+
whiteList['rest-after'] = false; // default: none
|
|
30280
|
+
whiteList['rest-before'] = false; // default: none
|
|
30281
|
+
whiteList['richness'] = false; // default: 50
|
|
30282
|
+
whiteList['right'] = false; // default: auto
|
|
30283
|
+
whiteList['rotation'] = false; // default: 0
|
|
30284
|
+
whiteList['rotation-point'] = false; // default: 50% 50%
|
|
30285
|
+
whiteList['ruby-align'] = false; // default: auto
|
|
30286
|
+
whiteList['ruby-merge'] = false; // default: separate
|
|
30287
|
+
whiteList['ruby-position'] = false; // default: before
|
|
30288
|
+
whiteList['shape-image-threshold'] = false; // default: 0.0
|
|
30289
|
+
whiteList['shape-outside'] = false; // default: none
|
|
30290
|
+
whiteList['shape-margin'] = false; // default: 0
|
|
30291
|
+
whiteList['size'] = false; // default: auto
|
|
30292
|
+
whiteList['speak'] = false; // default: auto
|
|
30293
|
+
whiteList['speak-as'] = false; // default: normal
|
|
30294
|
+
whiteList['speak-header'] = false; // default: once
|
|
30295
|
+
whiteList['speak-numeral'] = false; // default: continuous
|
|
30296
|
+
whiteList['speak-punctuation'] = false; // default: none
|
|
30297
|
+
whiteList['speech-rate'] = false; // default: medium
|
|
30298
|
+
whiteList['stress'] = false; // default: 50
|
|
30299
|
+
whiteList['string-set'] = false; // default: none
|
|
30300
|
+
whiteList['tab-size'] = false; // default: 8
|
|
30301
|
+
whiteList['table-layout'] = false; // default: auto
|
|
30302
|
+
whiteList['text-align'] = true; // default: start
|
|
30303
|
+
whiteList['text-align-last'] = true; // default: auto
|
|
30304
|
+
whiteList['text-combine-upright'] = true; // default: none
|
|
30305
|
+
whiteList['text-decoration'] = true; // default: none
|
|
30306
|
+
whiteList['text-decoration-color'] = true; // default: currentColor
|
|
30307
|
+
whiteList['text-decoration-line'] = true; // default: none
|
|
30308
|
+
whiteList['text-decoration-skip'] = true; // default: objects
|
|
30309
|
+
whiteList['text-decoration-style'] = true; // default: solid
|
|
30310
|
+
whiteList['text-emphasis'] = true; // default: depending on individual properties
|
|
30311
|
+
whiteList['text-emphasis-color'] = true; // default: currentColor
|
|
30312
|
+
whiteList['text-emphasis-position'] = true; // default: over right
|
|
30313
|
+
whiteList['text-emphasis-style'] = true; // default: none
|
|
30314
|
+
whiteList['text-height'] = true; // default: auto
|
|
30315
|
+
whiteList['text-indent'] = true; // default: 0
|
|
30316
|
+
whiteList['text-justify'] = true; // default: auto
|
|
30317
|
+
whiteList['text-orientation'] = true; // default: mixed
|
|
30318
|
+
whiteList['text-overflow'] = true; // default: clip
|
|
30319
|
+
whiteList['text-shadow'] = true; // default: none
|
|
30320
|
+
whiteList['text-space-collapse'] = true; // default: collapse
|
|
30321
|
+
whiteList['text-transform'] = true; // default: none
|
|
30322
|
+
whiteList['text-underline-position'] = true; // default: auto
|
|
30323
|
+
whiteList['text-wrap'] = true; // default: normal
|
|
30324
|
+
whiteList['top'] = false; // default: auto
|
|
30325
|
+
whiteList['transform'] = false; // default: none
|
|
30326
|
+
whiteList['transform-origin'] = false; // default: 50% 50% 0
|
|
30327
|
+
whiteList['transform-style'] = false; // default: flat
|
|
30328
|
+
whiteList['transition'] = false; // default: depending on individual properties
|
|
30329
|
+
whiteList['transition-delay'] = false; // default: 0s
|
|
30330
|
+
whiteList['transition-duration'] = false; // default: 0s
|
|
30331
|
+
whiteList['transition-property'] = false; // default: all
|
|
30332
|
+
whiteList['transition-timing-function'] = false; // default: ease
|
|
30333
|
+
whiteList['unicode-bidi'] = false; // default: normal
|
|
30334
|
+
whiteList['vertical-align'] = false; // default: baseline
|
|
30335
|
+
whiteList['visibility'] = false; // default: visible
|
|
30336
|
+
whiteList['voice-balance'] = false; // default: center
|
|
30337
|
+
whiteList['voice-duration'] = false; // default: auto
|
|
30338
|
+
whiteList['voice-family'] = false; // default: implementation dependent
|
|
30339
|
+
whiteList['voice-pitch'] = false; // default: medium
|
|
30340
|
+
whiteList['voice-range'] = false; // default: medium
|
|
30341
|
+
whiteList['voice-rate'] = false; // default: normal
|
|
30342
|
+
whiteList['voice-stress'] = false; // default: normal
|
|
30343
|
+
whiteList['voice-volume'] = false; // default: medium
|
|
30344
|
+
whiteList['volume'] = false; // default: medium
|
|
30345
|
+
whiteList['white-space'] = false; // default: normal
|
|
30346
|
+
whiteList['widows'] = false; // default: 2
|
|
30347
|
+
whiteList['width'] = true; // default: auto
|
|
30348
|
+
whiteList['will-change'] = false; // default: auto
|
|
30349
|
+
whiteList['word-break'] = true; // default: normal
|
|
30350
|
+
whiteList['word-spacing'] = true; // default: normal
|
|
30351
|
+
whiteList['word-wrap'] = true; // default: normal
|
|
30352
|
+
whiteList['wrap-flow'] = false; // default: auto
|
|
30353
|
+
whiteList['wrap-through'] = false; // default: wrap
|
|
30354
|
+
whiteList['writing-mode'] = false; // default: horizontal-tb
|
|
30355
|
+
whiteList['z-index'] = false; // default: auto
|
|
30356
|
+
|
|
30357
|
+
return whiteList;
|
|
30358
|
+
}
|
|
30359
|
+
|
|
30360
|
+
|
|
30361
|
+
/**
|
|
30362
|
+
* 匹配到白名单上的一个属性时
|
|
30363
|
+
*
|
|
30364
|
+
* @param {String} name
|
|
30365
|
+
* @param {String} value
|
|
30366
|
+
* @param {Object} options
|
|
30367
|
+
* @return {String}
|
|
30368
|
+
*/
|
|
30369
|
+
function onAttr (name, value, options) {
|
|
30370
|
+
// do nothing
|
|
30371
|
+
}
|
|
30372
|
+
|
|
30373
|
+
/**
|
|
30374
|
+
* 匹配到不在白名单上的一个属性时
|
|
30375
|
+
*
|
|
30376
|
+
* @param {String} name
|
|
30377
|
+
* @param {String} value
|
|
30378
|
+
* @param {Object} options
|
|
30379
|
+
* @return {String}
|
|
30380
|
+
*/
|
|
30381
|
+
function onIgnoreAttr (name, value, options) {
|
|
30382
|
+
// do nothing
|
|
30383
|
+
}
|
|
30384
|
+
|
|
30385
|
+
var REGEXP_URL_JAVASCRIPT = /javascript\s*\:/img;
|
|
30386
|
+
|
|
30387
|
+
/**
|
|
30388
|
+
* 过滤属性值
|
|
30389
|
+
*
|
|
30390
|
+
* @param {String} name
|
|
30391
|
+
* @param {String} value
|
|
30392
|
+
* @return {String}
|
|
30393
|
+
*/
|
|
30394
|
+
function safeAttrValue(name, value) {
|
|
30395
|
+
if (REGEXP_URL_JAVASCRIPT.test(value)) return '';
|
|
30396
|
+
return value;
|
|
30397
|
+
}
|
|
30398
|
+
|
|
30399
|
+
|
|
30400
|
+
exports.whiteList = getDefaultWhiteList();
|
|
30401
|
+
exports.getDefaultWhiteList = getDefaultWhiteList;
|
|
30402
|
+
exports.onAttr = onAttr;
|
|
30403
|
+
exports.onIgnoreAttr = onIgnoreAttr;
|
|
30404
|
+
exports.safeAttrValue = safeAttrValue;
|
|
30405
|
+
|
|
30406
|
+
|
|
30407
|
+
/***/ },
|
|
30408
|
+
|
|
30409
|
+
/***/ "./node_modules/cssfilter/lib/index.js"
|
|
30410
|
+
/*!*********************************************!*\
|
|
30411
|
+
!*** ./node_modules/cssfilter/lib/index.js ***!
|
|
30412
|
+
\*********************************************/
|
|
30413
|
+
(module, exports, __webpack_require__) {
|
|
30414
|
+
|
|
30415
|
+
/**
|
|
30416
|
+
* cssfilter
|
|
30417
|
+
*
|
|
30418
|
+
* @author 老雷<leizongmin@gmail.com>
|
|
30419
|
+
*/
|
|
30420
|
+
|
|
30421
|
+
var DEFAULT = __webpack_require__(/*! ./default */ "./node_modules/cssfilter/lib/default.js");
|
|
30422
|
+
var FilterCSS = __webpack_require__(/*! ./css */ "./node_modules/cssfilter/lib/css.js");
|
|
30423
|
+
|
|
30424
|
+
|
|
30425
|
+
/**
|
|
30426
|
+
* XSS过滤
|
|
30427
|
+
*
|
|
30428
|
+
* @param {String} css 要过滤的CSS代码
|
|
30429
|
+
* @param {Object} options 选项:whiteList, onAttr, onIgnoreAttr
|
|
30430
|
+
* @return {String}
|
|
30431
|
+
*/
|
|
30432
|
+
function filterCSS (html, options) {
|
|
30433
|
+
var xss = new FilterCSS(options);
|
|
30434
|
+
return xss.process(html);
|
|
30435
|
+
}
|
|
30436
|
+
|
|
30437
|
+
|
|
30438
|
+
// 输出
|
|
30439
|
+
exports = module.exports = filterCSS;
|
|
30440
|
+
exports.FilterCSS = FilterCSS;
|
|
30441
|
+
for (var i in DEFAULT) exports[i] = DEFAULT[i];
|
|
30442
|
+
|
|
30443
|
+
// 在浏览器端使用
|
|
30444
|
+
if (typeof window !== 'undefined') {
|
|
30445
|
+
window.filterCSS = module.exports;
|
|
30446
|
+
}
|
|
30447
|
+
|
|
30448
|
+
|
|
30449
|
+
/***/ },
|
|
30450
|
+
|
|
30451
|
+
/***/ "./node_modules/cssfilter/lib/parser.js"
|
|
30452
|
+
/*!**********************************************!*\
|
|
30453
|
+
!*** ./node_modules/cssfilter/lib/parser.js ***!
|
|
30454
|
+
\**********************************************/
|
|
30455
|
+
(module, __unused_webpack_exports, __webpack_require__) {
|
|
30456
|
+
|
|
30457
|
+
/**
|
|
30458
|
+
* cssfilter
|
|
30459
|
+
*
|
|
30460
|
+
* @author 老雷<leizongmin@gmail.com>
|
|
30461
|
+
*/
|
|
30462
|
+
|
|
30463
|
+
var _ = __webpack_require__(/*! ./util */ "./node_modules/cssfilter/lib/util.js");
|
|
30464
|
+
|
|
30465
|
+
|
|
30466
|
+
/**
|
|
30467
|
+
* 解析style
|
|
30468
|
+
*
|
|
30469
|
+
* @param {String} css
|
|
30470
|
+
* @param {Function} onAttr 处理属性的函数
|
|
30471
|
+
* 参数格式: function (sourcePosition, position, name, value, source)
|
|
30472
|
+
* @return {String}
|
|
30473
|
+
*/
|
|
30474
|
+
function parseStyle (css, onAttr) {
|
|
30475
|
+
css = _.trimRight(css);
|
|
30476
|
+
if (css[css.length - 1] !== ';') css += ';';
|
|
30477
|
+
var cssLength = css.length;
|
|
30478
|
+
var isParenthesisOpen = false;
|
|
30479
|
+
var lastPos = 0;
|
|
30480
|
+
var i = 0;
|
|
30481
|
+
var retCSS = '';
|
|
30482
|
+
|
|
30483
|
+
function addNewAttr () {
|
|
30484
|
+
// 如果没有正常的闭合圆括号,则直接忽略当前属性
|
|
30485
|
+
if (!isParenthesisOpen) {
|
|
30486
|
+
var source = _.trim(css.slice(lastPos, i));
|
|
30487
|
+
var j = source.indexOf(':');
|
|
30488
|
+
if (j !== -1) {
|
|
30489
|
+
var name = _.trim(source.slice(0, j));
|
|
30490
|
+
var value = _.trim(source.slice(j + 1));
|
|
30491
|
+
// 必须有属性名称
|
|
30492
|
+
if (name) {
|
|
30493
|
+
var ret = onAttr(lastPos, retCSS.length, name, value, source);
|
|
30494
|
+
if (ret) retCSS += ret + '; ';
|
|
30495
|
+
}
|
|
30496
|
+
}
|
|
30497
|
+
}
|
|
30498
|
+
lastPos = i + 1;
|
|
30499
|
+
}
|
|
30500
|
+
|
|
30501
|
+
for (; i < cssLength; i++) {
|
|
30502
|
+
var c = css[i];
|
|
30503
|
+
if (c === '/' && css[i + 1] === '*') {
|
|
30504
|
+
// 备注开始
|
|
30505
|
+
var j = css.indexOf('*/', i + 2);
|
|
30506
|
+
// 如果没有正常的备注结束,则后面的部分全部跳过
|
|
30507
|
+
if (j === -1) break;
|
|
30508
|
+
// 直接将当前位置调到备注结尾,并且初始化状态
|
|
30509
|
+
i = j + 1;
|
|
30510
|
+
lastPos = i + 1;
|
|
30511
|
+
isParenthesisOpen = false;
|
|
30512
|
+
} else if (c === '(') {
|
|
30513
|
+
isParenthesisOpen = true;
|
|
30514
|
+
} else if (c === ')') {
|
|
30515
|
+
isParenthesisOpen = false;
|
|
30516
|
+
} else if (c === ';') {
|
|
30517
|
+
if (isParenthesisOpen) {
|
|
30518
|
+
// 在圆括号里面,忽略
|
|
30519
|
+
} else {
|
|
30520
|
+
addNewAttr();
|
|
30521
|
+
}
|
|
30522
|
+
} else if (c === '\n') {
|
|
30523
|
+
addNewAttr();
|
|
30524
|
+
}
|
|
30525
|
+
}
|
|
30526
|
+
|
|
30527
|
+
return _.trim(retCSS);
|
|
30528
|
+
}
|
|
30529
|
+
|
|
30530
|
+
module.exports = parseStyle;
|
|
30531
|
+
|
|
30532
|
+
|
|
30533
|
+
/***/ },
|
|
30534
|
+
|
|
30535
|
+
/***/ "./node_modules/cssfilter/lib/util.js"
|
|
30536
|
+
/*!********************************************!*\
|
|
30537
|
+
!*** ./node_modules/cssfilter/lib/util.js ***!
|
|
30538
|
+
\********************************************/
|
|
30539
|
+
(module) {
|
|
30540
|
+
|
|
30541
|
+
module.exports = {
|
|
30542
|
+
indexOf: function (arr, item) {
|
|
30543
|
+
var i, j;
|
|
30544
|
+
if (Array.prototype.indexOf) {
|
|
30545
|
+
return arr.indexOf(item);
|
|
30546
|
+
}
|
|
30547
|
+
for (i = 0, j = arr.length; i < j; i++) {
|
|
30548
|
+
if (arr[i] === item) {
|
|
30549
|
+
return i;
|
|
30550
|
+
}
|
|
30551
|
+
}
|
|
30552
|
+
return -1;
|
|
30553
|
+
},
|
|
30554
|
+
forEach: function (arr, fn, scope) {
|
|
30555
|
+
var i, j;
|
|
30556
|
+
if (Array.prototype.forEach) {
|
|
30557
|
+
return arr.forEach(fn, scope);
|
|
30558
|
+
}
|
|
30559
|
+
for (i = 0, j = arr.length; i < j; i++) {
|
|
30560
|
+
fn.call(scope, arr[i], i, arr);
|
|
30561
|
+
}
|
|
30562
|
+
},
|
|
30563
|
+
trim: function (str) {
|
|
30564
|
+
if (String.prototype.trim) {
|
|
30565
|
+
return str.trim();
|
|
30566
|
+
}
|
|
30567
|
+
return str.replace(/(^\s*)|(\s*$)/g, '');
|
|
30568
|
+
},
|
|
30569
|
+
trimRight: function (str) {
|
|
30570
|
+
if (String.prototype.trimRight) {
|
|
30571
|
+
return str.trimRight();
|
|
30572
|
+
}
|
|
30573
|
+
return str.replace(/(\s*$)/g, '');
|
|
30574
|
+
}
|
|
30575
|
+
};
|
|
30576
|
+
|
|
30577
|
+
|
|
29520
30578
|
/***/ },
|
|
29521
30579
|
|
|
29522
30580
|
/***/ "./node_modules/marked/lib/marked.cjs"
|
|
@@ -33580,6 +34638,1091 @@ const compile = () => {
|
|
|
33580
34638
|
|
|
33581
34639
|
|
|
33582
34640
|
|
|
34641
|
+
/***/ },
|
|
34642
|
+
|
|
34643
|
+
/***/ "./node_modules/xss/lib/default.js"
|
|
34644
|
+
/*!*****************************************!*\
|
|
34645
|
+
!*** ./node_modules/xss/lib/default.js ***!
|
|
34646
|
+
\*****************************************/
|
|
34647
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
34648
|
+
|
|
34649
|
+
/**
|
|
34650
|
+
* default settings
|
|
34651
|
+
*
|
|
34652
|
+
* @author Zongmin Lei<leizongmin@gmail.com>
|
|
34653
|
+
*/
|
|
34654
|
+
|
|
34655
|
+
var FilterCSS = (__webpack_require__(/*! cssfilter */ "./node_modules/cssfilter/lib/index.js").FilterCSS);
|
|
34656
|
+
var getDefaultCSSWhiteList = (__webpack_require__(/*! cssfilter */ "./node_modules/cssfilter/lib/index.js").getDefaultWhiteList);
|
|
34657
|
+
var _ = __webpack_require__(/*! ./util */ "./node_modules/xss/lib/util.js");
|
|
34658
|
+
|
|
34659
|
+
function getDefaultWhiteList() {
|
|
34660
|
+
return {
|
|
34661
|
+
a: ["target", "href", "title"],
|
|
34662
|
+
abbr: ["title"],
|
|
34663
|
+
address: [],
|
|
34664
|
+
area: ["shape", "coords", "href", "alt"],
|
|
34665
|
+
article: [],
|
|
34666
|
+
aside: [],
|
|
34667
|
+
audio: [
|
|
34668
|
+
"autoplay",
|
|
34669
|
+
"controls",
|
|
34670
|
+
"crossorigin",
|
|
34671
|
+
"loop",
|
|
34672
|
+
"muted",
|
|
34673
|
+
"preload",
|
|
34674
|
+
"src",
|
|
34675
|
+
],
|
|
34676
|
+
b: [],
|
|
34677
|
+
bdi: ["dir"],
|
|
34678
|
+
bdo: ["dir"],
|
|
34679
|
+
big: [],
|
|
34680
|
+
blockquote: ["cite"],
|
|
34681
|
+
br: [],
|
|
34682
|
+
caption: [],
|
|
34683
|
+
center: [],
|
|
34684
|
+
cite: [],
|
|
34685
|
+
code: [],
|
|
34686
|
+
col: ["align", "valign", "span", "width"],
|
|
34687
|
+
colgroup: ["align", "valign", "span", "width"],
|
|
34688
|
+
dd: [],
|
|
34689
|
+
del: ["datetime"],
|
|
34690
|
+
details: ["open"],
|
|
34691
|
+
div: [],
|
|
34692
|
+
dl: [],
|
|
34693
|
+
dt: [],
|
|
34694
|
+
em: [],
|
|
34695
|
+
figcaption: [],
|
|
34696
|
+
figure: [],
|
|
34697
|
+
font: ["color", "size", "face"],
|
|
34698
|
+
footer: [],
|
|
34699
|
+
h1: [],
|
|
34700
|
+
h2: [],
|
|
34701
|
+
h3: [],
|
|
34702
|
+
h4: [],
|
|
34703
|
+
h5: [],
|
|
34704
|
+
h6: [],
|
|
34705
|
+
header: [],
|
|
34706
|
+
hr: [],
|
|
34707
|
+
i: [],
|
|
34708
|
+
img: ["src", "alt", "title", "width", "height", "loading"],
|
|
34709
|
+
ins: ["datetime"],
|
|
34710
|
+
kbd: [],
|
|
34711
|
+
li: [],
|
|
34712
|
+
mark: [],
|
|
34713
|
+
nav: [],
|
|
34714
|
+
ol: [],
|
|
34715
|
+
p: [],
|
|
34716
|
+
pre: [],
|
|
34717
|
+
s: [],
|
|
34718
|
+
section: [],
|
|
34719
|
+
small: [],
|
|
34720
|
+
span: [],
|
|
34721
|
+
sub: [],
|
|
34722
|
+
summary: [],
|
|
34723
|
+
sup: [],
|
|
34724
|
+
strong: [],
|
|
34725
|
+
strike: [],
|
|
34726
|
+
table: ["width", "border", "align", "valign"],
|
|
34727
|
+
tbody: ["align", "valign"],
|
|
34728
|
+
td: ["width", "rowspan", "colspan", "align", "valign"],
|
|
34729
|
+
tfoot: ["align", "valign"],
|
|
34730
|
+
th: ["width", "rowspan", "colspan", "align", "valign"],
|
|
34731
|
+
thead: ["align", "valign"],
|
|
34732
|
+
tr: ["rowspan", "align", "valign"],
|
|
34733
|
+
tt: [],
|
|
34734
|
+
u: [],
|
|
34735
|
+
ul: [],
|
|
34736
|
+
video: [
|
|
34737
|
+
"autoplay",
|
|
34738
|
+
"controls",
|
|
34739
|
+
"crossorigin",
|
|
34740
|
+
"loop",
|
|
34741
|
+
"muted",
|
|
34742
|
+
"playsinline",
|
|
34743
|
+
"poster",
|
|
34744
|
+
"preload",
|
|
34745
|
+
"src",
|
|
34746
|
+
"height",
|
|
34747
|
+
"width",
|
|
34748
|
+
],
|
|
34749
|
+
};
|
|
34750
|
+
}
|
|
34751
|
+
|
|
34752
|
+
var defaultCSSFilter = new FilterCSS();
|
|
34753
|
+
|
|
34754
|
+
/**
|
|
34755
|
+
* default onTag function
|
|
34756
|
+
*
|
|
34757
|
+
* @param {String} tag
|
|
34758
|
+
* @param {String} html
|
|
34759
|
+
* @param {Object} options
|
|
34760
|
+
* @return {String}
|
|
34761
|
+
*/
|
|
34762
|
+
function onTag(tag, html, options) {
|
|
34763
|
+
// do nothing
|
|
34764
|
+
}
|
|
34765
|
+
|
|
34766
|
+
/**
|
|
34767
|
+
* default onIgnoreTag function
|
|
34768
|
+
*
|
|
34769
|
+
* @param {String} tag
|
|
34770
|
+
* @param {String} html
|
|
34771
|
+
* @param {Object} options
|
|
34772
|
+
* @return {String}
|
|
34773
|
+
*/
|
|
34774
|
+
function onIgnoreTag(tag, html, options) {
|
|
34775
|
+
// do nothing
|
|
34776
|
+
}
|
|
34777
|
+
|
|
34778
|
+
/**
|
|
34779
|
+
* default onTagAttr function
|
|
34780
|
+
*
|
|
34781
|
+
* @param {String} tag
|
|
34782
|
+
* @param {String} name
|
|
34783
|
+
* @param {String} value
|
|
34784
|
+
* @return {String}
|
|
34785
|
+
*/
|
|
34786
|
+
function onTagAttr(tag, name, value) {
|
|
34787
|
+
// do nothing
|
|
34788
|
+
}
|
|
34789
|
+
|
|
34790
|
+
/**
|
|
34791
|
+
* default onIgnoreTagAttr function
|
|
34792
|
+
*
|
|
34793
|
+
* @param {String} tag
|
|
34794
|
+
* @param {String} name
|
|
34795
|
+
* @param {String} value
|
|
34796
|
+
* @return {String}
|
|
34797
|
+
*/
|
|
34798
|
+
function onIgnoreTagAttr(tag, name, value) {
|
|
34799
|
+
// do nothing
|
|
34800
|
+
}
|
|
34801
|
+
|
|
34802
|
+
/**
|
|
34803
|
+
* default escapeHtml function
|
|
34804
|
+
*
|
|
34805
|
+
* @param {String} html
|
|
34806
|
+
*/
|
|
34807
|
+
function escapeHtml(html) {
|
|
34808
|
+
return html.replace(REGEXP_LT, "<").replace(REGEXP_GT, ">");
|
|
34809
|
+
}
|
|
34810
|
+
|
|
34811
|
+
/**
|
|
34812
|
+
* default safeAttrValue function
|
|
34813
|
+
*
|
|
34814
|
+
* @param {String} tag
|
|
34815
|
+
* @param {String} name
|
|
34816
|
+
* @param {String} value
|
|
34817
|
+
* @param {Object} cssFilter
|
|
34818
|
+
* @return {String}
|
|
34819
|
+
*/
|
|
34820
|
+
function safeAttrValue(tag, name, value, cssFilter) {
|
|
34821
|
+
// unescape attribute value firstly
|
|
34822
|
+
value = friendlyAttrValue(value);
|
|
34823
|
+
|
|
34824
|
+
if (name === "href" || name === "src") {
|
|
34825
|
+
// filter `href` and `src` attribute
|
|
34826
|
+
// only allow the value that starts with `http://` | `https://` | `mailto:` | `/` | `#`
|
|
34827
|
+
value = _.trim(value);
|
|
34828
|
+
if (value === "#") return "#";
|
|
34829
|
+
if (
|
|
34830
|
+
!(
|
|
34831
|
+
value.substr(0, 7) === "http://" ||
|
|
34832
|
+
value.substr(0, 8) === "https://" ||
|
|
34833
|
+
value.substr(0, 7) === "mailto:" ||
|
|
34834
|
+
value.substr(0, 4) === "tel:" ||
|
|
34835
|
+
value.substr(0, 11) === "data:image/" ||
|
|
34836
|
+
value.substr(0, 6) === "ftp://" ||
|
|
34837
|
+
value.substr(0, 2) === "./" ||
|
|
34838
|
+
value.substr(0, 3) === "../" ||
|
|
34839
|
+
value[0] === "#" ||
|
|
34840
|
+
value[0] === "/"
|
|
34841
|
+
)
|
|
34842
|
+
) {
|
|
34843
|
+
return "";
|
|
34844
|
+
}
|
|
34845
|
+
} else if (name === "background") {
|
|
34846
|
+
// filter `background` attribute (maybe no use)
|
|
34847
|
+
// `javascript:`
|
|
34848
|
+
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
|
|
34849
|
+
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
|
|
34850
|
+
return "";
|
|
34851
|
+
}
|
|
34852
|
+
} else if (name === "style") {
|
|
34853
|
+
// `expression()`
|
|
34854
|
+
REGEXP_DEFAULT_ON_TAG_ATTR_7.lastIndex = 0;
|
|
34855
|
+
if (REGEXP_DEFAULT_ON_TAG_ATTR_7.test(value)) {
|
|
34856
|
+
return "";
|
|
34857
|
+
}
|
|
34858
|
+
// `url()`
|
|
34859
|
+
REGEXP_DEFAULT_ON_TAG_ATTR_8.lastIndex = 0;
|
|
34860
|
+
if (REGEXP_DEFAULT_ON_TAG_ATTR_8.test(value)) {
|
|
34861
|
+
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
|
|
34862
|
+
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
|
|
34863
|
+
return "";
|
|
34864
|
+
}
|
|
34865
|
+
}
|
|
34866
|
+
if (cssFilter !== false) {
|
|
34867
|
+
cssFilter = cssFilter || defaultCSSFilter;
|
|
34868
|
+
value = cssFilter.process(value);
|
|
34869
|
+
}
|
|
34870
|
+
}
|
|
34871
|
+
|
|
34872
|
+
// escape `<>"` before returns
|
|
34873
|
+
value = escapeAttrValue(value);
|
|
34874
|
+
return value;
|
|
34875
|
+
}
|
|
34876
|
+
|
|
34877
|
+
// RegExp list
|
|
34878
|
+
var REGEXP_LT = /</g;
|
|
34879
|
+
var REGEXP_GT = />/g;
|
|
34880
|
+
var REGEXP_QUOTE = /"/g;
|
|
34881
|
+
var REGEXP_QUOTE_2 = /"/g;
|
|
34882
|
+
var REGEXP_ATTR_VALUE_1 = /&#([a-zA-Z0-9]*);?/gim;
|
|
34883
|
+
var REGEXP_ATTR_VALUE_COLON = /:?/gim;
|
|
34884
|
+
var REGEXP_ATTR_VALUE_NEWLINE = /&newline;?/gim;
|
|
34885
|
+
// var REGEXP_DEFAULT_ON_TAG_ATTR_3 = /\/\*|\*\//gm;
|
|
34886
|
+
var REGEXP_DEFAULT_ON_TAG_ATTR_4 =
|
|
34887
|
+
/((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a):/gi;
|
|
34888
|
+
// var REGEXP_DEFAULT_ON_TAG_ATTR_5 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/gi;
|
|
34889
|
+
// var REGEXP_DEFAULT_ON_TAG_ATTR_6 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//gi;
|
|
34890
|
+
var REGEXP_DEFAULT_ON_TAG_ATTR_7 =
|
|
34891
|
+
/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/gi;
|
|
34892
|
+
var REGEXP_DEFAULT_ON_TAG_ATTR_8 = /u\s*r\s*l\s*\(.*/gi;
|
|
34893
|
+
|
|
34894
|
+
/**
|
|
34895
|
+
* escape double quote
|
|
34896
|
+
*
|
|
34897
|
+
* @param {String} str
|
|
34898
|
+
* @return {String} str
|
|
34899
|
+
*/
|
|
34900
|
+
function escapeQuote(str) {
|
|
34901
|
+
return str.replace(REGEXP_QUOTE, """);
|
|
34902
|
+
}
|
|
34903
|
+
|
|
34904
|
+
/**
|
|
34905
|
+
* unescape double quote
|
|
34906
|
+
*
|
|
34907
|
+
* @param {String} str
|
|
34908
|
+
* @return {String} str
|
|
34909
|
+
*/
|
|
34910
|
+
function unescapeQuote(str) {
|
|
34911
|
+
return str.replace(REGEXP_QUOTE_2, '"');
|
|
34912
|
+
}
|
|
34913
|
+
|
|
34914
|
+
/**
|
|
34915
|
+
* escape html entities
|
|
34916
|
+
*
|
|
34917
|
+
* @param {String} str
|
|
34918
|
+
* @return {String}
|
|
34919
|
+
*/
|
|
34920
|
+
function escapeHtmlEntities(str) {
|
|
34921
|
+
return str.replace(REGEXP_ATTR_VALUE_1, function replaceUnicode(str, code) {
|
|
34922
|
+
return code[0] === "x" || code[0] === "X"
|
|
34923
|
+
? String.fromCharCode(parseInt(code.substr(1), 16))
|
|
34924
|
+
: String.fromCharCode(parseInt(code, 10));
|
|
34925
|
+
});
|
|
34926
|
+
}
|
|
34927
|
+
|
|
34928
|
+
/**
|
|
34929
|
+
* escape html5 new danger entities
|
|
34930
|
+
*
|
|
34931
|
+
* @param {String} str
|
|
34932
|
+
* @return {String}
|
|
34933
|
+
*/
|
|
34934
|
+
function escapeDangerHtml5Entities(str) {
|
|
34935
|
+
return str
|
|
34936
|
+
.replace(REGEXP_ATTR_VALUE_COLON, ":")
|
|
34937
|
+
.replace(REGEXP_ATTR_VALUE_NEWLINE, " ");
|
|
34938
|
+
}
|
|
34939
|
+
|
|
34940
|
+
/**
|
|
34941
|
+
* clear nonprintable characters
|
|
34942
|
+
*
|
|
34943
|
+
* @param {String} str
|
|
34944
|
+
* @return {String}
|
|
34945
|
+
*/
|
|
34946
|
+
function clearNonPrintableCharacter(str) {
|
|
34947
|
+
var str2 = "";
|
|
34948
|
+
for (var i = 0, len = str.length; i < len; i++) {
|
|
34949
|
+
str2 += str.charCodeAt(i) < 32 ? " " : str.charAt(i);
|
|
34950
|
+
}
|
|
34951
|
+
return _.trim(str2);
|
|
34952
|
+
}
|
|
34953
|
+
|
|
34954
|
+
/**
|
|
34955
|
+
* get friendly attribute value
|
|
34956
|
+
*
|
|
34957
|
+
* @param {String} str
|
|
34958
|
+
* @return {String}
|
|
34959
|
+
*/
|
|
34960
|
+
function friendlyAttrValue(str) {
|
|
34961
|
+
str = unescapeQuote(str);
|
|
34962
|
+
str = escapeHtmlEntities(str);
|
|
34963
|
+
str = escapeDangerHtml5Entities(str);
|
|
34964
|
+
str = clearNonPrintableCharacter(str);
|
|
34965
|
+
return str;
|
|
34966
|
+
}
|
|
34967
|
+
|
|
34968
|
+
/**
|
|
34969
|
+
* unescape attribute value
|
|
34970
|
+
*
|
|
34971
|
+
* @param {String} str
|
|
34972
|
+
* @return {String}
|
|
34973
|
+
*/
|
|
34974
|
+
function escapeAttrValue(str) {
|
|
34975
|
+
str = escapeQuote(str);
|
|
34976
|
+
str = escapeHtml(str);
|
|
34977
|
+
return str;
|
|
34978
|
+
}
|
|
34979
|
+
|
|
34980
|
+
/**
|
|
34981
|
+
* `onIgnoreTag` function for removing all the tags that are not in whitelist
|
|
34982
|
+
*/
|
|
34983
|
+
function onIgnoreTagStripAll() {
|
|
34984
|
+
return "";
|
|
34985
|
+
}
|
|
34986
|
+
|
|
34987
|
+
/**
|
|
34988
|
+
* remove tag body
|
|
34989
|
+
* specify a `tags` list, if the tag is not in the `tags` list then process by the specify function (optional)
|
|
34990
|
+
*
|
|
34991
|
+
* @param {array} tags
|
|
34992
|
+
* @param {function} next
|
|
34993
|
+
*/
|
|
34994
|
+
function StripTagBody(tags, next) {
|
|
34995
|
+
if (typeof next !== "function") {
|
|
34996
|
+
next = function () {};
|
|
34997
|
+
}
|
|
34998
|
+
|
|
34999
|
+
var isRemoveAllTag = !Array.isArray(tags);
|
|
35000
|
+
function isRemoveTag(tag) {
|
|
35001
|
+
if (isRemoveAllTag) return true;
|
|
35002
|
+
return _.indexOf(tags, tag) !== -1;
|
|
35003
|
+
}
|
|
35004
|
+
|
|
35005
|
+
var removeList = [];
|
|
35006
|
+
var posStart = false;
|
|
35007
|
+
|
|
35008
|
+
return {
|
|
35009
|
+
onIgnoreTag: function (tag, html, options) {
|
|
35010
|
+
if (isRemoveTag(tag)) {
|
|
35011
|
+
if (options.isClosing) {
|
|
35012
|
+
var ret = "[/removed]";
|
|
35013
|
+
var end = options.position + ret.length;
|
|
35014
|
+
removeList.push([
|
|
35015
|
+
posStart !== false ? posStart : options.position,
|
|
35016
|
+
end,
|
|
35017
|
+
]);
|
|
35018
|
+
posStart = false;
|
|
35019
|
+
return ret;
|
|
35020
|
+
} else {
|
|
35021
|
+
if (!posStart) {
|
|
35022
|
+
posStart = options.position;
|
|
35023
|
+
}
|
|
35024
|
+
return "[removed]";
|
|
35025
|
+
}
|
|
35026
|
+
} else {
|
|
35027
|
+
return next(tag, html, options);
|
|
35028
|
+
}
|
|
35029
|
+
},
|
|
35030
|
+
remove: function (html) {
|
|
35031
|
+
var rethtml = "";
|
|
35032
|
+
var lastPos = 0;
|
|
35033
|
+
_.forEach(removeList, function (pos) {
|
|
35034
|
+
rethtml += html.slice(lastPos, pos[0]);
|
|
35035
|
+
lastPos = pos[1];
|
|
35036
|
+
});
|
|
35037
|
+
rethtml += html.slice(lastPos);
|
|
35038
|
+
return rethtml;
|
|
35039
|
+
},
|
|
35040
|
+
};
|
|
35041
|
+
}
|
|
35042
|
+
|
|
35043
|
+
/**
|
|
35044
|
+
* remove html comments
|
|
35045
|
+
*
|
|
35046
|
+
* @param {String} html
|
|
35047
|
+
* @return {String}
|
|
35048
|
+
*/
|
|
35049
|
+
function stripCommentTag(html) {
|
|
35050
|
+
var retHtml = "";
|
|
35051
|
+
var lastPos = 0;
|
|
35052
|
+
while (lastPos < html.length) {
|
|
35053
|
+
var i = html.indexOf("<!--", lastPos);
|
|
35054
|
+
if (i === -1) {
|
|
35055
|
+
retHtml += html.slice(lastPos);
|
|
35056
|
+
break;
|
|
35057
|
+
}
|
|
35058
|
+
retHtml += html.slice(lastPos, i);
|
|
35059
|
+
var j = html.indexOf("-->", i);
|
|
35060
|
+
if (j === -1) {
|
|
35061
|
+
break;
|
|
35062
|
+
}
|
|
35063
|
+
lastPos = j + 3;
|
|
35064
|
+
}
|
|
35065
|
+
return retHtml;
|
|
35066
|
+
}
|
|
35067
|
+
|
|
35068
|
+
/**
|
|
35069
|
+
* remove invisible characters
|
|
35070
|
+
*
|
|
35071
|
+
* @param {String} html
|
|
35072
|
+
* @return {String}
|
|
35073
|
+
*/
|
|
35074
|
+
function stripBlankChar(html) {
|
|
35075
|
+
var chars = html.split("");
|
|
35076
|
+
chars = chars.filter(function (char) {
|
|
35077
|
+
var c = char.charCodeAt(0);
|
|
35078
|
+
if (c === 127) return false;
|
|
35079
|
+
if (c <= 31) {
|
|
35080
|
+
if (c === 10 || c === 13) return true;
|
|
35081
|
+
return false;
|
|
35082
|
+
}
|
|
35083
|
+
return true;
|
|
35084
|
+
});
|
|
35085
|
+
return chars.join("");
|
|
35086
|
+
}
|
|
35087
|
+
|
|
35088
|
+
exports.whiteList = getDefaultWhiteList();
|
|
35089
|
+
exports.getDefaultWhiteList = getDefaultWhiteList;
|
|
35090
|
+
exports.onTag = onTag;
|
|
35091
|
+
exports.onIgnoreTag = onIgnoreTag;
|
|
35092
|
+
exports.onTagAttr = onTagAttr;
|
|
35093
|
+
exports.onIgnoreTagAttr = onIgnoreTagAttr;
|
|
35094
|
+
exports.safeAttrValue = safeAttrValue;
|
|
35095
|
+
exports.escapeHtml = escapeHtml;
|
|
35096
|
+
exports.escapeQuote = escapeQuote;
|
|
35097
|
+
exports.unescapeQuote = unescapeQuote;
|
|
35098
|
+
exports.escapeHtmlEntities = escapeHtmlEntities;
|
|
35099
|
+
exports.escapeDangerHtml5Entities = escapeDangerHtml5Entities;
|
|
35100
|
+
exports.clearNonPrintableCharacter = clearNonPrintableCharacter;
|
|
35101
|
+
exports.friendlyAttrValue = friendlyAttrValue;
|
|
35102
|
+
exports.escapeAttrValue = escapeAttrValue;
|
|
35103
|
+
exports.onIgnoreTagStripAll = onIgnoreTagStripAll;
|
|
35104
|
+
exports.StripTagBody = StripTagBody;
|
|
35105
|
+
exports.stripCommentTag = stripCommentTag;
|
|
35106
|
+
exports.stripBlankChar = stripBlankChar;
|
|
35107
|
+
exports.attributeWrapSign = '"';
|
|
35108
|
+
exports.cssFilter = defaultCSSFilter;
|
|
35109
|
+
exports.getDefaultCSSWhiteList = getDefaultCSSWhiteList;
|
|
35110
|
+
|
|
35111
|
+
|
|
35112
|
+
/***/ },
|
|
35113
|
+
|
|
35114
|
+
/***/ "./node_modules/xss/lib/index.js"
|
|
35115
|
+
/*!***************************************!*\
|
|
35116
|
+
!*** ./node_modules/xss/lib/index.js ***!
|
|
35117
|
+
\***************************************/
|
|
35118
|
+
(module, exports, __webpack_require__) {
|
|
35119
|
+
|
|
35120
|
+
/**
|
|
35121
|
+
* xss
|
|
35122
|
+
*
|
|
35123
|
+
* @author Zongmin Lei<leizongmin@gmail.com>
|
|
35124
|
+
*/
|
|
35125
|
+
|
|
35126
|
+
var DEFAULT = __webpack_require__(/*! ./default */ "./node_modules/xss/lib/default.js");
|
|
35127
|
+
var parser = __webpack_require__(/*! ./parser */ "./node_modules/xss/lib/parser.js");
|
|
35128
|
+
var FilterXSS = __webpack_require__(/*! ./xss */ "./node_modules/xss/lib/xss.js");
|
|
35129
|
+
|
|
35130
|
+
/**
|
|
35131
|
+
* filter xss function
|
|
35132
|
+
*
|
|
35133
|
+
* @param {String} html
|
|
35134
|
+
* @param {Object} options { whiteList, onTag, onTagAttr, onIgnoreTag, onIgnoreTagAttr, safeAttrValue, escapeHtml }
|
|
35135
|
+
* @return {String}
|
|
35136
|
+
*/
|
|
35137
|
+
function filterXSS(html, options) {
|
|
35138
|
+
var xss = new FilterXSS(options);
|
|
35139
|
+
return xss.process(html);
|
|
35140
|
+
}
|
|
35141
|
+
|
|
35142
|
+
exports = module.exports = filterXSS;
|
|
35143
|
+
exports.filterXSS = filterXSS;
|
|
35144
|
+
exports.FilterXSS = FilterXSS;
|
|
35145
|
+
|
|
35146
|
+
(function () {
|
|
35147
|
+
for (var i in DEFAULT) {
|
|
35148
|
+
exports[i] = DEFAULT[i];
|
|
35149
|
+
}
|
|
35150
|
+
for (var j in parser) {
|
|
35151
|
+
exports[j] = parser[j];
|
|
35152
|
+
}
|
|
35153
|
+
})();
|
|
35154
|
+
|
|
35155
|
+
// using `xss` on the browser, output `filterXSS` to the globals
|
|
35156
|
+
if (typeof window !== "undefined") {
|
|
35157
|
+
window.filterXSS = module.exports;
|
|
35158
|
+
}
|
|
35159
|
+
|
|
35160
|
+
// using `xss` on the WebWorker, output `filterXSS` to the globals
|
|
35161
|
+
function isWorkerEnv() {
|
|
35162
|
+
return (
|
|
35163
|
+
typeof self !== "undefined" &&
|
|
35164
|
+
typeof DedicatedWorkerGlobalScope !== "undefined" &&
|
|
35165
|
+
self instanceof DedicatedWorkerGlobalScope
|
|
35166
|
+
);
|
|
35167
|
+
}
|
|
35168
|
+
if (isWorkerEnv()) {
|
|
35169
|
+
self.filterXSS = module.exports;
|
|
35170
|
+
}
|
|
35171
|
+
|
|
35172
|
+
|
|
35173
|
+
/***/ },
|
|
35174
|
+
|
|
35175
|
+
/***/ "./node_modules/xss/lib/parser.js"
|
|
35176
|
+
/*!****************************************!*\
|
|
35177
|
+
!*** ./node_modules/xss/lib/parser.js ***!
|
|
35178
|
+
\****************************************/
|
|
35179
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
35180
|
+
|
|
35181
|
+
/**
|
|
35182
|
+
* Simple HTML Parser
|
|
35183
|
+
*
|
|
35184
|
+
* @author Zongmin Lei<leizongmin@gmail.com>
|
|
35185
|
+
*/
|
|
35186
|
+
|
|
35187
|
+
var _ = __webpack_require__(/*! ./util */ "./node_modules/xss/lib/util.js");
|
|
35188
|
+
|
|
35189
|
+
/**
|
|
35190
|
+
* get tag name
|
|
35191
|
+
*
|
|
35192
|
+
* @param {String} html e.g. '<a hef="#">'
|
|
35193
|
+
* @return {String}
|
|
35194
|
+
*/
|
|
35195
|
+
function getTagName(html) {
|
|
35196
|
+
var i = _.spaceIndex(html);
|
|
35197
|
+
var tagName;
|
|
35198
|
+
if (i === -1) {
|
|
35199
|
+
tagName = html.slice(1, -1);
|
|
35200
|
+
} else {
|
|
35201
|
+
tagName = html.slice(1, i + 1);
|
|
35202
|
+
}
|
|
35203
|
+
tagName = _.trim(tagName).toLowerCase();
|
|
35204
|
+
if (tagName.slice(0, 1) === "/") tagName = tagName.slice(1);
|
|
35205
|
+
if (tagName.slice(-1) === "/") tagName = tagName.slice(0, -1);
|
|
35206
|
+
return tagName;
|
|
35207
|
+
}
|
|
35208
|
+
|
|
35209
|
+
/**
|
|
35210
|
+
* is close tag?
|
|
35211
|
+
*
|
|
35212
|
+
* @param {String} html 如:'<a hef="#">'
|
|
35213
|
+
* @return {Boolean}
|
|
35214
|
+
*/
|
|
35215
|
+
function isClosing(html) {
|
|
35216
|
+
return html.slice(0, 2) === "</";
|
|
35217
|
+
}
|
|
35218
|
+
|
|
35219
|
+
/**
|
|
35220
|
+
* parse input html and returns processed html
|
|
35221
|
+
*
|
|
35222
|
+
* @param {String} html
|
|
35223
|
+
* @param {Function} onTag e.g. function (sourcePosition, position, tag, html, isClosing)
|
|
35224
|
+
* @param {Function} escapeHtml
|
|
35225
|
+
* @return {String}
|
|
35226
|
+
*/
|
|
35227
|
+
function parseTag(html, onTag, escapeHtml) {
|
|
35228
|
+
"use strict";
|
|
35229
|
+
|
|
35230
|
+
var rethtml = "";
|
|
35231
|
+
var lastPos = 0;
|
|
35232
|
+
var tagStart = false;
|
|
35233
|
+
var quoteStart = false;
|
|
35234
|
+
var currentPos = 0;
|
|
35235
|
+
var len = html.length;
|
|
35236
|
+
var currentTagName = "";
|
|
35237
|
+
var currentHtml = "";
|
|
35238
|
+
|
|
35239
|
+
chariterator: for (currentPos = 0; currentPos < len; currentPos++) {
|
|
35240
|
+
var c = html.charAt(currentPos);
|
|
35241
|
+
if (tagStart === false) {
|
|
35242
|
+
if (c === "<") {
|
|
35243
|
+
tagStart = currentPos;
|
|
35244
|
+
continue;
|
|
35245
|
+
}
|
|
35246
|
+
} else {
|
|
35247
|
+
if (quoteStart === false) {
|
|
35248
|
+
if (c === "<") {
|
|
35249
|
+
rethtml += escapeHtml(html.slice(lastPos, currentPos));
|
|
35250
|
+
tagStart = currentPos;
|
|
35251
|
+
lastPos = currentPos;
|
|
35252
|
+
continue;
|
|
35253
|
+
}
|
|
35254
|
+
if (c === ">" || currentPos === len - 1) {
|
|
35255
|
+
rethtml += escapeHtml(html.slice(lastPos, tagStart));
|
|
35256
|
+
currentHtml = html.slice(tagStart, currentPos + 1);
|
|
35257
|
+
currentTagName = getTagName(currentHtml);
|
|
35258
|
+
rethtml += onTag(
|
|
35259
|
+
tagStart,
|
|
35260
|
+
rethtml.length,
|
|
35261
|
+
currentTagName,
|
|
35262
|
+
currentHtml,
|
|
35263
|
+
isClosing(currentHtml)
|
|
35264
|
+
);
|
|
35265
|
+
lastPos = currentPos + 1;
|
|
35266
|
+
tagStart = false;
|
|
35267
|
+
continue;
|
|
35268
|
+
}
|
|
35269
|
+
if (c === '"' || c === "'") {
|
|
35270
|
+
var i = 1;
|
|
35271
|
+
var ic = html.charAt(currentPos - i);
|
|
35272
|
+
|
|
35273
|
+
while (ic.trim() === "" || ic === "=") {
|
|
35274
|
+
if (ic === "=") {
|
|
35275
|
+
quoteStart = c;
|
|
35276
|
+
continue chariterator;
|
|
35277
|
+
}
|
|
35278
|
+
ic = html.charAt(currentPos - ++i);
|
|
35279
|
+
}
|
|
35280
|
+
}
|
|
35281
|
+
} else {
|
|
35282
|
+
if (c === quoteStart) {
|
|
35283
|
+
quoteStart = false;
|
|
35284
|
+
continue;
|
|
35285
|
+
}
|
|
35286
|
+
}
|
|
35287
|
+
}
|
|
35288
|
+
}
|
|
35289
|
+
if (lastPos < len) {
|
|
35290
|
+
rethtml += escapeHtml(html.substr(lastPos));
|
|
35291
|
+
}
|
|
35292
|
+
|
|
35293
|
+
return rethtml;
|
|
35294
|
+
}
|
|
35295
|
+
|
|
35296
|
+
var REGEXP_ILLEGAL_ATTR_NAME = /[^a-zA-Z0-9\\_:.-]/gim;
|
|
35297
|
+
|
|
35298
|
+
/**
|
|
35299
|
+
* parse input attributes and returns processed attributes
|
|
35300
|
+
*
|
|
35301
|
+
* @param {String} html e.g. `href="#" target="_blank"`
|
|
35302
|
+
* @param {Function} onAttr e.g. `function (name, value)`
|
|
35303
|
+
* @return {String}
|
|
35304
|
+
*/
|
|
35305
|
+
function parseAttr(html, onAttr) {
|
|
35306
|
+
"use strict";
|
|
35307
|
+
|
|
35308
|
+
var lastPos = 0;
|
|
35309
|
+
var lastMarkPos = 0;
|
|
35310
|
+
var retAttrs = [];
|
|
35311
|
+
var tmpName = false;
|
|
35312
|
+
var len = html.length;
|
|
35313
|
+
|
|
35314
|
+
function addAttr(name, value) {
|
|
35315
|
+
name = _.trim(name);
|
|
35316
|
+
name = name.replace(REGEXP_ILLEGAL_ATTR_NAME, "").toLowerCase();
|
|
35317
|
+
if (name.length < 1) return;
|
|
35318
|
+
var ret = onAttr(name, value || "");
|
|
35319
|
+
if (ret) retAttrs.push(ret);
|
|
35320
|
+
}
|
|
35321
|
+
|
|
35322
|
+
// 逐个分析字符
|
|
35323
|
+
for (var i = 0; i < len; i++) {
|
|
35324
|
+
var c = html.charAt(i);
|
|
35325
|
+
var v, j;
|
|
35326
|
+
if (tmpName === false && c === "=") {
|
|
35327
|
+
tmpName = html.slice(lastPos, i);
|
|
35328
|
+
lastPos = i + 1;
|
|
35329
|
+
lastMarkPos = html.charAt(lastPos) === '"' || html.charAt(lastPos) === "'" ? lastPos : findNextQuotationMark(html, i + 1);
|
|
35330
|
+
continue;
|
|
35331
|
+
}
|
|
35332
|
+
if (tmpName !== false) {
|
|
35333
|
+
if (
|
|
35334
|
+
i === lastMarkPos
|
|
35335
|
+
) {
|
|
35336
|
+
j = html.indexOf(c, i + 1);
|
|
35337
|
+
if (j === -1) {
|
|
35338
|
+
break;
|
|
35339
|
+
} else {
|
|
35340
|
+
v = _.trim(html.slice(lastMarkPos + 1, j));
|
|
35341
|
+
addAttr(tmpName, v);
|
|
35342
|
+
tmpName = false;
|
|
35343
|
+
i = j;
|
|
35344
|
+
lastPos = i + 1;
|
|
35345
|
+
continue;
|
|
35346
|
+
}
|
|
35347
|
+
}
|
|
35348
|
+
}
|
|
35349
|
+
if (/\s|\n|\t/.test(c)) {
|
|
35350
|
+
html = html.replace(/\s|\n|\t/g, " ");
|
|
35351
|
+
if (tmpName === false) {
|
|
35352
|
+
j = findNextEqual(html, i);
|
|
35353
|
+
if (j === -1) {
|
|
35354
|
+
v = _.trim(html.slice(lastPos, i));
|
|
35355
|
+
addAttr(v);
|
|
35356
|
+
tmpName = false;
|
|
35357
|
+
lastPos = i + 1;
|
|
35358
|
+
continue;
|
|
35359
|
+
} else {
|
|
35360
|
+
i = j - 1;
|
|
35361
|
+
continue;
|
|
35362
|
+
}
|
|
35363
|
+
} else {
|
|
35364
|
+
j = findBeforeEqual(html, i - 1);
|
|
35365
|
+
if (j === -1) {
|
|
35366
|
+
v = _.trim(html.slice(lastPos, i));
|
|
35367
|
+
v = stripQuoteWrap(v);
|
|
35368
|
+
addAttr(tmpName, v);
|
|
35369
|
+
tmpName = false;
|
|
35370
|
+
lastPos = i + 1;
|
|
35371
|
+
continue;
|
|
35372
|
+
} else {
|
|
35373
|
+
continue;
|
|
35374
|
+
}
|
|
35375
|
+
}
|
|
35376
|
+
}
|
|
35377
|
+
}
|
|
35378
|
+
|
|
35379
|
+
if (lastPos < html.length) {
|
|
35380
|
+
if (tmpName === false) {
|
|
35381
|
+
addAttr(html.slice(lastPos));
|
|
35382
|
+
} else {
|
|
35383
|
+
addAttr(tmpName, stripQuoteWrap(_.trim(html.slice(lastPos))));
|
|
35384
|
+
}
|
|
35385
|
+
}
|
|
35386
|
+
|
|
35387
|
+
return _.trim(retAttrs.join(" "));
|
|
35388
|
+
}
|
|
35389
|
+
|
|
35390
|
+
function findNextEqual(str, i) {
|
|
35391
|
+
for (; i < str.length; i++) {
|
|
35392
|
+
var c = str[i];
|
|
35393
|
+
if (c === " ") continue;
|
|
35394
|
+
if (c === "=") return i;
|
|
35395
|
+
return -1;
|
|
35396
|
+
}
|
|
35397
|
+
}
|
|
35398
|
+
|
|
35399
|
+
function findNextQuotationMark(str, i) {
|
|
35400
|
+
for (; i < str.length; i++) {
|
|
35401
|
+
var c = str[i];
|
|
35402
|
+
if (c === " ") continue;
|
|
35403
|
+
if (c === "'" || c === '"') return i;
|
|
35404
|
+
return -1;
|
|
35405
|
+
}
|
|
35406
|
+
}
|
|
35407
|
+
|
|
35408
|
+
function findBeforeEqual(str, i) {
|
|
35409
|
+
for (; i > 0; i--) {
|
|
35410
|
+
var c = str[i];
|
|
35411
|
+
if (c === " ") continue;
|
|
35412
|
+
if (c === "=") return i;
|
|
35413
|
+
return -1;
|
|
35414
|
+
}
|
|
35415
|
+
}
|
|
35416
|
+
|
|
35417
|
+
function isQuoteWrapString(text) {
|
|
35418
|
+
if (
|
|
35419
|
+
(text[0] === '"' && text[text.length - 1] === '"') ||
|
|
35420
|
+
(text[0] === "'" && text[text.length - 1] === "'")
|
|
35421
|
+
) {
|
|
35422
|
+
return true;
|
|
35423
|
+
} else {
|
|
35424
|
+
return false;
|
|
35425
|
+
}
|
|
35426
|
+
}
|
|
35427
|
+
|
|
35428
|
+
function stripQuoteWrap(text) {
|
|
35429
|
+
if (isQuoteWrapString(text)) {
|
|
35430
|
+
return text.substr(1, text.length - 2);
|
|
35431
|
+
} else {
|
|
35432
|
+
return text;
|
|
35433
|
+
}
|
|
35434
|
+
}
|
|
35435
|
+
|
|
35436
|
+
exports.parseTag = parseTag;
|
|
35437
|
+
exports.parseAttr = parseAttr;
|
|
35438
|
+
|
|
35439
|
+
|
|
35440
|
+
/***/ },
|
|
35441
|
+
|
|
35442
|
+
/***/ "./node_modules/xss/lib/util.js"
|
|
35443
|
+
/*!**************************************!*\
|
|
35444
|
+
!*** ./node_modules/xss/lib/util.js ***!
|
|
35445
|
+
\**************************************/
|
|
35446
|
+
(module) {
|
|
35447
|
+
|
|
35448
|
+
module.exports = {
|
|
35449
|
+
indexOf: function (arr, item) {
|
|
35450
|
+
var i, j;
|
|
35451
|
+
if (Array.prototype.indexOf) {
|
|
35452
|
+
return arr.indexOf(item);
|
|
35453
|
+
}
|
|
35454
|
+
for (i = 0, j = arr.length; i < j; i++) {
|
|
35455
|
+
if (arr[i] === item) {
|
|
35456
|
+
return i;
|
|
35457
|
+
}
|
|
35458
|
+
}
|
|
35459
|
+
return -1;
|
|
35460
|
+
},
|
|
35461
|
+
forEach: function (arr, fn, scope) {
|
|
35462
|
+
var i, j;
|
|
35463
|
+
if (Array.prototype.forEach) {
|
|
35464
|
+
return arr.forEach(fn, scope);
|
|
35465
|
+
}
|
|
35466
|
+
for (i = 0, j = arr.length; i < j; i++) {
|
|
35467
|
+
fn.call(scope, arr[i], i, arr);
|
|
35468
|
+
}
|
|
35469
|
+
},
|
|
35470
|
+
trim: function (str) {
|
|
35471
|
+
if (String.prototype.trim) {
|
|
35472
|
+
return str.trim();
|
|
35473
|
+
}
|
|
35474
|
+
return str.replace(/(^\s*)|(\s*$)/g, "");
|
|
35475
|
+
},
|
|
35476
|
+
spaceIndex: function (str) {
|
|
35477
|
+
var reg = /\s|\n|\t/;
|
|
35478
|
+
var match = reg.exec(str);
|
|
35479
|
+
return match ? match.index : -1;
|
|
35480
|
+
},
|
|
35481
|
+
};
|
|
35482
|
+
|
|
35483
|
+
|
|
35484
|
+
/***/ },
|
|
35485
|
+
|
|
35486
|
+
/***/ "./node_modules/xss/lib/xss.js"
|
|
35487
|
+
/*!*************************************!*\
|
|
35488
|
+
!*** ./node_modules/xss/lib/xss.js ***!
|
|
35489
|
+
\*************************************/
|
|
35490
|
+
(module, __unused_webpack_exports, __webpack_require__) {
|
|
35491
|
+
|
|
35492
|
+
/**
|
|
35493
|
+
* filter xss
|
|
35494
|
+
*
|
|
35495
|
+
* @author Zongmin Lei<leizongmin@gmail.com>
|
|
35496
|
+
*/
|
|
35497
|
+
|
|
35498
|
+
var FilterCSS = (__webpack_require__(/*! cssfilter */ "./node_modules/cssfilter/lib/index.js").FilterCSS);
|
|
35499
|
+
var DEFAULT = __webpack_require__(/*! ./default */ "./node_modules/xss/lib/default.js");
|
|
35500
|
+
var parser = __webpack_require__(/*! ./parser */ "./node_modules/xss/lib/parser.js");
|
|
35501
|
+
var parseTag = parser.parseTag;
|
|
35502
|
+
var parseAttr = parser.parseAttr;
|
|
35503
|
+
var _ = __webpack_require__(/*! ./util */ "./node_modules/xss/lib/util.js");
|
|
35504
|
+
|
|
35505
|
+
/**
|
|
35506
|
+
* returns `true` if the input value is `undefined` or `null`
|
|
35507
|
+
*
|
|
35508
|
+
* @param {Object} obj
|
|
35509
|
+
* @return {Boolean}
|
|
35510
|
+
*/
|
|
35511
|
+
function isNull(obj) {
|
|
35512
|
+
return obj === undefined || obj === null;
|
|
35513
|
+
}
|
|
35514
|
+
|
|
35515
|
+
/**
|
|
35516
|
+
* get attributes for a tag
|
|
35517
|
+
*
|
|
35518
|
+
* @param {String} html
|
|
35519
|
+
* @return {Object}
|
|
35520
|
+
* - {String} html
|
|
35521
|
+
* - {Boolean} closing
|
|
35522
|
+
*/
|
|
35523
|
+
function getAttrs(html) {
|
|
35524
|
+
var i = _.spaceIndex(html);
|
|
35525
|
+
if (i === -1) {
|
|
35526
|
+
return {
|
|
35527
|
+
html: "",
|
|
35528
|
+
closing: html[html.length - 2] === "/",
|
|
35529
|
+
};
|
|
35530
|
+
}
|
|
35531
|
+
html = _.trim(html.slice(i + 1, -1));
|
|
35532
|
+
var isClosing = html[html.length - 1] === "/";
|
|
35533
|
+
if (isClosing) html = _.trim(html.slice(0, -1));
|
|
35534
|
+
return {
|
|
35535
|
+
html: html,
|
|
35536
|
+
closing: isClosing,
|
|
35537
|
+
};
|
|
35538
|
+
}
|
|
35539
|
+
|
|
35540
|
+
/**
|
|
35541
|
+
* shallow copy
|
|
35542
|
+
*
|
|
35543
|
+
* @param {Object} obj
|
|
35544
|
+
* @return {Object}
|
|
35545
|
+
*/
|
|
35546
|
+
function shallowCopyObject(obj) {
|
|
35547
|
+
var ret = {};
|
|
35548
|
+
for (var i in obj) {
|
|
35549
|
+
ret[i] = obj[i];
|
|
35550
|
+
}
|
|
35551
|
+
return ret;
|
|
35552
|
+
}
|
|
35553
|
+
|
|
35554
|
+
function keysToLowerCase(obj) {
|
|
35555
|
+
var ret = {};
|
|
35556
|
+
for (var i in obj) {
|
|
35557
|
+
if (Array.isArray(obj[i])) {
|
|
35558
|
+
ret[i.toLowerCase()] = obj[i].map(function (item) {
|
|
35559
|
+
return item.toLowerCase();
|
|
35560
|
+
});
|
|
35561
|
+
} else {
|
|
35562
|
+
ret[i.toLowerCase()] = obj[i];
|
|
35563
|
+
}
|
|
35564
|
+
}
|
|
35565
|
+
return ret;
|
|
35566
|
+
}
|
|
35567
|
+
|
|
35568
|
+
/**
|
|
35569
|
+
* FilterXSS class
|
|
35570
|
+
*
|
|
35571
|
+
* @param {Object} options
|
|
35572
|
+
* whiteList (or allowList), onTag, onTagAttr, onIgnoreTag,
|
|
35573
|
+
* onIgnoreTagAttr, safeAttrValue, escapeHtml
|
|
35574
|
+
* stripIgnoreTagBody, allowCommentTag, stripBlankChar
|
|
35575
|
+
* css{whiteList, onAttr, onIgnoreAttr} `css=false` means don't use `cssfilter`
|
|
35576
|
+
*/
|
|
35577
|
+
function FilterXSS(options) {
|
|
35578
|
+
options = shallowCopyObject(options || {});
|
|
35579
|
+
|
|
35580
|
+
if (options.stripIgnoreTag) {
|
|
35581
|
+
if (options.onIgnoreTag) {
|
|
35582
|
+
console.error(
|
|
35583
|
+
'Notes: cannot use these two options "stripIgnoreTag" and "onIgnoreTag" at the same time'
|
|
35584
|
+
);
|
|
35585
|
+
}
|
|
35586
|
+
options.onIgnoreTag = DEFAULT.onIgnoreTagStripAll;
|
|
35587
|
+
}
|
|
35588
|
+
if (options.whiteList || options.allowList) {
|
|
35589
|
+
options.whiteList = keysToLowerCase(options.whiteList || options.allowList);
|
|
35590
|
+
} else {
|
|
35591
|
+
options.whiteList = DEFAULT.whiteList;
|
|
35592
|
+
}
|
|
35593
|
+
|
|
35594
|
+
this.attributeWrapSign = options.singleQuotedAttributeValue === true ? "'" : DEFAULT.attributeWrapSign;
|
|
35595
|
+
|
|
35596
|
+
options.onTag = options.onTag || DEFAULT.onTag;
|
|
35597
|
+
options.onTagAttr = options.onTagAttr || DEFAULT.onTagAttr;
|
|
35598
|
+
options.onIgnoreTag = options.onIgnoreTag || DEFAULT.onIgnoreTag;
|
|
35599
|
+
options.onIgnoreTagAttr = options.onIgnoreTagAttr || DEFAULT.onIgnoreTagAttr;
|
|
35600
|
+
options.safeAttrValue = options.safeAttrValue || DEFAULT.safeAttrValue;
|
|
35601
|
+
options.escapeHtml = options.escapeHtml || DEFAULT.escapeHtml;
|
|
35602
|
+
this.options = options;
|
|
35603
|
+
|
|
35604
|
+
if (options.css === false) {
|
|
35605
|
+
this.cssFilter = false;
|
|
35606
|
+
} else {
|
|
35607
|
+
options.css = options.css || {};
|
|
35608
|
+
this.cssFilter = new FilterCSS(options.css);
|
|
35609
|
+
}
|
|
35610
|
+
}
|
|
35611
|
+
|
|
35612
|
+
/**
|
|
35613
|
+
* start process and returns result
|
|
35614
|
+
*
|
|
35615
|
+
* @param {String} html
|
|
35616
|
+
* @return {String}
|
|
35617
|
+
*/
|
|
35618
|
+
FilterXSS.prototype.process = function (html) {
|
|
35619
|
+
// compatible with the input
|
|
35620
|
+
html = html || "";
|
|
35621
|
+
html = html.toString();
|
|
35622
|
+
if (!html) return "";
|
|
35623
|
+
|
|
35624
|
+
var me = this;
|
|
35625
|
+
var options = me.options;
|
|
35626
|
+
var whiteList = options.whiteList;
|
|
35627
|
+
var onTag = options.onTag;
|
|
35628
|
+
var onIgnoreTag = options.onIgnoreTag;
|
|
35629
|
+
var onTagAttr = options.onTagAttr;
|
|
35630
|
+
var onIgnoreTagAttr = options.onIgnoreTagAttr;
|
|
35631
|
+
var safeAttrValue = options.safeAttrValue;
|
|
35632
|
+
var escapeHtml = options.escapeHtml;
|
|
35633
|
+
var attributeWrapSign = me.attributeWrapSign;
|
|
35634
|
+
var cssFilter = me.cssFilter;
|
|
35635
|
+
|
|
35636
|
+
// remove invisible characters
|
|
35637
|
+
if (options.stripBlankChar) {
|
|
35638
|
+
html = DEFAULT.stripBlankChar(html);
|
|
35639
|
+
}
|
|
35640
|
+
|
|
35641
|
+
// remove html comments
|
|
35642
|
+
if (!options.allowCommentTag) {
|
|
35643
|
+
html = DEFAULT.stripCommentTag(html);
|
|
35644
|
+
}
|
|
35645
|
+
|
|
35646
|
+
// if enable stripIgnoreTagBody
|
|
35647
|
+
var stripIgnoreTagBody = false;
|
|
35648
|
+
if (options.stripIgnoreTagBody) {
|
|
35649
|
+
stripIgnoreTagBody = DEFAULT.StripTagBody(
|
|
35650
|
+
options.stripIgnoreTagBody,
|
|
35651
|
+
onIgnoreTag
|
|
35652
|
+
);
|
|
35653
|
+
onIgnoreTag = stripIgnoreTagBody.onIgnoreTag;
|
|
35654
|
+
}
|
|
35655
|
+
|
|
35656
|
+
var retHtml = parseTag(
|
|
35657
|
+
html,
|
|
35658
|
+
function (sourcePosition, position, tag, html, isClosing) {
|
|
35659
|
+
var info = {
|
|
35660
|
+
sourcePosition: sourcePosition,
|
|
35661
|
+
position: position,
|
|
35662
|
+
isClosing: isClosing,
|
|
35663
|
+
isWhite: Object.prototype.hasOwnProperty.call(whiteList, tag),
|
|
35664
|
+
};
|
|
35665
|
+
|
|
35666
|
+
// call `onTag()`
|
|
35667
|
+
var ret = onTag(tag, html, info);
|
|
35668
|
+
if (!isNull(ret)) return ret;
|
|
35669
|
+
|
|
35670
|
+
if (info.isWhite) {
|
|
35671
|
+
if (info.isClosing) {
|
|
35672
|
+
return "</" + tag + ">";
|
|
35673
|
+
}
|
|
35674
|
+
|
|
35675
|
+
var attrs = getAttrs(html);
|
|
35676
|
+
var whiteAttrList = whiteList[tag];
|
|
35677
|
+
var attrsHtml = parseAttr(attrs.html, function (name, value) {
|
|
35678
|
+
// call `onTagAttr()`
|
|
35679
|
+
var isWhiteAttr = _.indexOf(whiteAttrList, name) !== -1;
|
|
35680
|
+
var ret = onTagAttr(tag, name, value, isWhiteAttr);
|
|
35681
|
+
if (!isNull(ret)) return ret;
|
|
35682
|
+
|
|
35683
|
+
if (isWhiteAttr) {
|
|
35684
|
+
// call `safeAttrValue()`
|
|
35685
|
+
value = safeAttrValue(tag, name, value, cssFilter);
|
|
35686
|
+
if (value) {
|
|
35687
|
+
return name + '=' + attributeWrapSign + value + attributeWrapSign;
|
|
35688
|
+
} else {
|
|
35689
|
+
return name;
|
|
35690
|
+
}
|
|
35691
|
+
} else {
|
|
35692
|
+
// call `onIgnoreTagAttr()`
|
|
35693
|
+
ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
|
|
35694
|
+
if (!isNull(ret)) return ret;
|
|
35695
|
+
return;
|
|
35696
|
+
}
|
|
35697
|
+
});
|
|
35698
|
+
|
|
35699
|
+
// build new tag html
|
|
35700
|
+
html = "<" + tag;
|
|
35701
|
+
if (attrsHtml) html += " " + attrsHtml;
|
|
35702
|
+
if (attrs.closing) html += " /";
|
|
35703
|
+
html += ">";
|
|
35704
|
+
return html;
|
|
35705
|
+
} else {
|
|
35706
|
+
// call `onIgnoreTag()`
|
|
35707
|
+
ret = onIgnoreTag(tag, html, info);
|
|
35708
|
+
if (!isNull(ret)) return ret;
|
|
35709
|
+
return escapeHtml(html);
|
|
35710
|
+
}
|
|
35711
|
+
},
|
|
35712
|
+
escapeHtml
|
|
35713
|
+
);
|
|
35714
|
+
|
|
35715
|
+
// if enable stripIgnoreTagBody
|
|
35716
|
+
if (stripIgnoreTagBody) {
|
|
35717
|
+
retHtml = stripIgnoreTagBody.remove(retHtml);
|
|
35718
|
+
}
|
|
35719
|
+
|
|
35720
|
+
return retHtml;
|
|
35721
|
+
};
|
|
35722
|
+
|
|
35723
|
+
module.exports = FilterXSS;
|
|
35724
|
+
|
|
35725
|
+
|
|
33583
35726
|
/***/ },
|
|
33584
35727
|
|
|
33585
35728
|
/***/ "./package.json"
|
|
@@ -33589,7 +35732,7 @@ const compile = () => {
|
|
|
33589
35732
|
(module) {
|
|
33590
35733
|
|
|
33591
35734
|
"use strict";
|
|
33592
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.2.
|
|
35735
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.2.7","description":"A Mongoose-native MongoDB UI with schema-aware autocomplete, AI-assisted queries, and dashboards that understand your models - not just your data.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.0","marked":"15.0.12","node-inspect-extracted":"3.x","tailwindcss":"3.4.0","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","xss":"^1.0.15"},"peerDependencies":{"mongoose":"7.x || 8.x || ^9.0.0"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js"}}');
|
|
33593
35736
|
|
|
33594
35737
|
/***/ }
|
|
33595
35738
|
|