├── .github └── workflows │ └── publish.yml ├── LICENSE ├── README.md ├── icon.png ├── index.html ├── index.js ├── package.json └── style.css /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Build plugin 2 | 3 | on: 4 | push: 5 | # Sequence of patterns matched against refs/tags 6 | tags: 7 | - '*' # Push events to matching any tag format, i.e. 1.0, 20.15.10 8 | 9 | env: 10 | PLUGIN_NAME: ${{ github.event.repository.name }} 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Use Node.js 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: '16.x' # You might need to adjust this value to your own version 22 | - name: Build 23 | id: build 24 | run: | 25 | mkdir ${{ env.PLUGIN_NAME }} 26 | cp LICENSE README.md package.json index.html index.js style.css icon.png ${{ env.PLUGIN_NAME }} 27 | zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }} 28 | ls 29 | echo "::set-output name=tag_name::$(git tag --sort version:refname | tail -n 1)" 30 | - name: Create Release 31 | uses: ncipollo/release-action@v1 32 | id: create_release 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | VERSION: ${{ github.ref }} 36 | with: 37 | allowUpdates: true 38 | draft: false 39 | prerelease: false 40 | 41 | - name: Upload zip file 42 | id: upload_zip 43 | uses: actions/upload-release-asset@v1 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | with: 47 | upload_url: ${{ steps.create_release.outputs.upload_url }} 48 | asset_path: ./${{ env.PLUGIN_NAME }}.zip 49 | asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip 50 | asset_content_type: application/zip 51 | 52 | - name: Upload package.json 53 | id: upload_metadata 54 | uses: actions/upload-release-asset@v1 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | with: 58 | upload_url: ${{ steps.create_release.outputs.upload_url }} 59 | asset_path: ./package.json 60 | asset_name: package.json 61 | asset_content_type: application/json 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 adxsoft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Logseq Query Builder Plugin 3 | 4 | * builds advanced logseq queries from [Simple Commands](#simple-commands) contained in a logseq code block. 5 | * Choosing **Advanced Query Builder** in the code blocks menu (right click on block's bullet) 6 | * will generate an advanced query in a **new** child block 7 | * You can alter the code block and generate another query which will add another new child block. 8 | * In this way you can experiment with generating multiple advanced queries. 9 | ## Detailed Documentation 10 | 11 | Can be found [here](https://github.com/adxsoft/docs-logseq-query-builder-plugin) 12 | 13 | ### Do not use tabs in commands 14 | *Note. as this code also supports the web site at https://adxsoft.github.io/logseqadvancedquerybuilder/ the use of tabs for indentation of the simple commands is not possible* 15 | So for consistency between the online tool and the plugin you can use any number of leading spaces but the indentations must be consistent. I would suggest either two or four spaces is a good indentation value. 16 | 17 | ## Installation 18 | ### Preparation 19 | * Click the 3 dots in the righthand corner and go to **Settings**. 20 | * Go to **Advanced** and enable **Plug-in system**. 21 | * Restart the application. 22 | * Click 3 dots and go to Plugins (or `Esc t p`). 23 | 24 | ### Install plugin from the Marketplace (recommended) 25 | * Click the `Marketplace` button and then click `Plugins`. 26 | * Find the plugin and click `Install`. 27 | 28 | ### Install plugin manually 29 | * Click the green Code button above and download the zip 30 | * Unzip it into a folder 31 | * Click `Load unpacked plugin`, and select the folder where the plugin code was unzipped 32 | 33 | 34 | ## Technical Information 35 | * Originally the online tool was developed using pyscript and redeveloped in javascript. 36 | * To ensure consistency between the online tool and the plugin the javascript code is shared. 37 | * A variable called **_mode_** is set to _logseq-plugin_, _website_ or _local_. 38 | * All modes use index.js as common code 39 | * **mode _logseq-plugin_** 40 | * will operate as a logseq plugin 41 | * has its own _index.html_ and _package.json_ file 42 | * files are contained in the _plugin-dist_ folder 43 | * **mode _website_** 44 | * will operate as the online tool 45 | * has its own _index.html_ and _package.json_ file 46 | * files are contained in the _website-dist_ folder 47 | * **mode _local_** 48 | * will operate locally 49 | * has its own _index.html_ and _package.json_ file 50 | * files are contained in the main folder 51 | * has a _index.test.js_ file which is used for unit testing with the Jest Testing Library - the index.test.js file is contained in the repository for the online tool at 52 | 53 | 54 | ## Releases 55 | - v0.1 56 | - Original release - Dec 30th 2022 57 | - v0.7 58 | - First release to the logseq marketplace 23rd Feb 2023 59 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adxsoft/logseq-query-builder-plugin/6c06ce596118776478bb118f256d354ece3b1af3/icon.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Logseq Advanced Query Builder 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // logseq-advanced-query-builder v0.1 Dec 23rd 2022 2 | 3 | // set mode of execution at end of this script 4 | // note each mode has its own index.html and package.json files 5 | // index.js is common to each mode and 6 | // copied from main to to dist folders before any deployments 7 | 8 | // 'local' mode for desktop testing with Jest Test framework 9 | // - located in the main folder 10 | // 'logseq-plugin' for operating as a plugin within logseq 11 | // - located in the plugin-dist folder 12 | // 'website' for operating as a online website 13 | // - located in the website-dist folder 14 | 15 | // Deployment 16 | // copy index.js to deploy folder 17 | // WARNING: 18 | // 1. do not copy package.json or index.html to 19 | // deployment folders (plugin-dist and website-dist) 20 | // as the main package.json is configured for jest testing 21 | // 2. do not copy main README.md to 22 | // deployment folders (plugin-dist and docs-dist) 23 | // as they are specific to each deployment domain 24 | 25 | // Dictionary of query lines data 26 | var querylineDict = { 27 | 'common-querylines': { 28 | 'start': 29 | { 30 | 'name': 'start', 31 | 'useincommands': ['common'], 32 | 'segment': 'start', 33 | 'datalog': '#+BEGIN_QUERY', 34 | 'comment': '' 35 | }, 36 | 'open': 37 | { 38 | 'name': 'open', 39 | 'useincommands': ['common'], 40 | 'segment': 'open', 41 | 'datalog': '{', 42 | 'comment': '' 43 | }, 44 | 'title': 45 | { 46 | 'name': 'title', 47 | 'useincommands': ['common'], 48 | 'segment': 'title', 49 | 'datalog': ':title [:b "$$ARG1"]', 50 | 'comment': '' 51 | }, 52 | 'findblocks': 53 | { 54 | 'name': 'findblocks', 55 | 'useincommands': ['common'], 56 | 'segment': 'query', 57 | 'datalog': ':query [:find (pull ?block [*])', 58 | 'comment': 'Get every block into variable ?block' 59 | }, 60 | 'daterange': 61 | { 62 | 'name': 'daterange', 63 | 'useincommands': ['common'], 64 | 'segment': 'in', 65 | 'datalog': ':in $ ?startdate ?enddate', 66 | 'comment': 'give query the ?startdate variable and the ?enddate variable (set by :inputs)' 67 | }, 68 | 'where': 69 | { 70 | 'name': 'where', 71 | 'useincommands': ['common'], 72 | 'segment': 'where', 73 | 'datalog': ':where', 74 | 'comment': 'filter command' 75 | }, 76 | 'tasks_are': 77 | { 78 | 'name': 'tasks_are', 79 | 'useincommands': ['common'], 80 | 'segment': 'filters', 81 | 'datalog': '[(contains? #{"$$ARG1"} ?marker)]', 82 | 'comment': 'Select block if it has one or more tasks (TODO or DONE etc)' 83 | }, 84 | 'not_tasks_are': 85 | { 86 | 'name': 'not_tasks_are', 87 | 'useincommands': ['common'], 88 | 'segment': 'filters', 89 | 'datalog': '(not [(contains? #{"$$ARG1"} ?marker)])', 90 | 'comment': 'Exclude block if it has one or more tasks' 91 | }, 92 | 'pagelinks_are': 93 | { 94 | 'name': 'pagelinks_are', 95 | 'useincommands': ['common'], 96 | 'segment': 'filters', 97 | 'datalog': '[?block :block/path-refs [:block/name "$$ARG1"]]', 98 | 'comment': 'Select block if it has one or more links to other pages' 99 | }, 100 | 'not_pagelinks_are': 101 | { 102 | 'name': 'not_pagelinks_are', 103 | 'useincommands': ['common'], 104 | 'segment': 'filters', 105 | 'datalog': '(not [?block :block/path-refs [:block/name "$$ARG1"]])', 106 | 'comment': 'Exclude block if it has one or more links to other pages' 107 | }, 108 | 'scheduledbetween': 109 | { 110 | 'name': 'scheduledbetween', 111 | 'useincommands': ['common'], 112 | 'segment': 'inputs', 113 | 'datalog': ':inputs [$$ARG1]', 114 | 'comment': 'set the input values for a date range for example could be\n :today :365d-after' 115 | }, 116 | 'journalfrom': 117 | { 118 | 'name': 'journalfrom', 119 | 'useincommands': ['common'], 120 | 'segment': 'filters', 121 | 'datalog': '[(>= ?journaldate ?startdate)]', 122 | 'comment': 'Select if journaldate greater than start date' 123 | }, 124 | 'journalto': 125 | { 126 | 'name': 'journalto', 127 | 'useincommands': ['common'], 128 | 'segment': 'filters', 129 | 'datalog': '[(<= ?journaldate ?enddate)]', 130 | 'comment': 'Select if journalddate less than end date' 131 | }, 132 | 'journalsbetween': 133 | { 134 | 'name': 'journalsbetween', 135 | 'useincommands': ['common'], 136 | 'segment': 'inputs', 137 | 'datalog': ':inputs [$$ARG1]', 138 | 'comment': 'set the input values for a date range for example could be\n :today :365d-after' 139 | }, 140 | 'breadcrumb_show_false': 141 | { 142 | 'name': 'breadcrumb_show_false', 143 | 'useincommands': ['common'], 144 | 'segment': 'options', 145 | 'datalog': ':breadcrumb-show? false', 146 | 'comment': 'Suppress breadcrumbs view' 147 | }, 148 | 'breadcrumb_show_true': 149 | { 150 | 'name': 'breadcrumb_show_true', 151 | 'useincommands': ['common'], 152 | 'segment': 'options', 153 | 'datalog': ':breadcrumb-show? true', 154 | 'comment': 'Show breadcrumbs above each block' 155 | }, 156 | 'collapse_false': 157 | { 158 | 'name': 'collapse_false', 159 | 'useincommands': ['common'], 160 | 'segment': 'options', 161 | 'datalog': ':collapsed? false', 162 | 'comment': 'Toggle collapse or fold' 163 | }, 164 | 'collapse_true': 165 | { 166 | 'name': 'collapse_true', 167 | 'useincommands': ['common'], 168 | 'segment': 'options', 169 | 'datalog': ':collapsed? true', 170 | 'comment': 'Toggle collapse or fold' 171 | }, 172 | 'closefind': 173 | { 174 | 'name': 'closefind', 175 | 'useincommands': ['common'], 176 | 'segment': 'closefind', 177 | 'datalog': ']', 178 | 'comment': '' 179 | }, 180 | 'closequery': 181 | { 182 | 'name': 'closequery', 183 | 'useincommands': ['common'], 184 | 'segment': 'closequery', 185 | 'datalog': '}', 186 | 'comment': '' 187 | }, 188 | 'end': 189 | { 190 | 'name': 'end', 191 | 'useincommands': ['common'], 192 | 'segment': 'end', 193 | 'datalog': '#+END_QUERY', 194 | 'comment': '' 195 | }, 196 | }, 197 | 'blocks-querylines': { 198 | 'blockcontent': 199 | { 200 | 'name': 'blockcontent', 201 | 'useincommands': ['blocks'], 202 | 'segment': 'filters', 203 | 'datalog': '[?block :block/content ?blockcontent]', 204 | 'comment': 'get block content into variable ?blockcontent' 205 | }, 206 | 'page': 207 | { 208 | 'name': 'page', 209 | 'useincommands': ['blocks'], 210 | 'segment': 'filters', 211 | 'datalog': '[?block :block/page ?page]', 212 | 'comment': 'get page (special type of block) into variable ?page (used later)' 213 | }, 214 | 'pagename': 215 | { 216 | 'name': 'pagename', 217 | 'useincommands': ['blocks'], 218 | 'segment': 'filters', 219 | 'datalog': '[?page :block/name ?pagename]', 220 | 'comment': 'get page name (lowercase) from the page block into variable ?pagename' 221 | }, 222 | 'marker': 223 | { 224 | 'name': 'marker', 225 | 'useincommands': ['blocks'], 226 | 'segment': 'filters', 227 | 'datalog': '[?block :block/marker ?marker]', 228 | 'comment': 'get block marker (TODO LATER ETC) into variable ?marker' 229 | }, 230 | 'scheduled': 231 | { 232 | 'name': 'scheduled', 233 | 'useincommands': ['blocks'], 234 | 'segment': 'filters', 235 | 'datalog': '[?block :block/scheduled ?scheduleddate]', 236 | 'comment': 'get scheduled date in the block into variable ?scheduleddate' 237 | }, 238 | 'scheduledfrom': 239 | { 240 | 'name': 'scheduledfrom', 241 | 'useincommands': ['blocks'], 242 | 'segment': 'filters', 243 | 'datalog': '[(>= ?scheduleddate ?startdate)]', 244 | 'comment': 'Select if scheduleddate greater than start date' 245 | }, 246 | 'scheduledto': 247 | { 248 | 'name': 'scheduledto', 249 | 'useincommands': ['blocks'], 250 | 'segment': 'filters', 251 | 'datalog': '[(<= ?scheduleddate ?enddate)]', 252 | 'comment': 'Select if scheduleddate less than end date' 253 | }, 254 | 'deadline': 255 | { 256 | 'name': 'deadline', 257 | 'useincommands': ['blocks'], 258 | 'segment': 'filters', 259 | 'datalog': '[?block :block/deadline ?deadlinedate]', 260 | 'comment': 'get deadline date in the block into variable ?date' 261 | }, 262 | 'deadlinefrom': 263 | { 264 | 'name': 'deadlinefrom', 265 | 'useincommands': ['blocks'], 266 | 'segment': 'filters', 267 | 'datalog': '[(>= ?deadlinedate ?startdate)]', 268 | 'comment': 'Select if deadlinedate greater than start date' 269 | }, 270 | 'deadlineto': 271 | { 272 | 'name': 'deadlineto', 273 | 'useincommands': ['blocks'], 274 | 'segment': 'filters', 275 | 'datalog': '[(<= ?deadlinedate ?enddate)]', 276 | 'comment': 'Select if deadlinedate less than end date' 277 | }, 278 | 'pagename_is': 279 | { 280 | 'name': 'pagename_is', 281 | 'useincommands': ['blocks'], 282 | 'segment': 'filters', 283 | 'datalog': '[?block :block/name "$$ARG1"]', 284 | 'comment': 'Select specific page from the page special block\nPresence of :block/name in a blocks means \nit is a block that has the page attributes' 285 | }, 286 | 'not_pagename_is': 287 | { 288 | 'name': 'not_pagename_is', 289 | 'useincommands': ['blocks'], 290 | 'segment': 'filters', 291 | 'datalog': '(not [?block :block/name "$$ARG1"])', 292 | 'comment': 'Exclude specific page from the page special block' 293 | }, 294 | 'page_is_journal': 295 | { 296 | 'name': 'page_is_journal', 297 | 'useincommands': ['blocks'], 298 | 'segment': 'filters', 299 | 'datalog': '[?page :block/journal? true]', 300 | 'comment': 'Select block if its belongs to a journal page' 301 | }, 302 | 'not_page_is_journal': 303 | { 304 | 'name': 'not_page_is_journal', 305 | 'useincommands': ['blocks'], 306 | 'segment': 'filters', 307 | 'datalog': '[?page :block/journal? false]', 308 | 'comment': 'Exclude block if its belongs to a journal page' 309 | }, 310 | 'journal_date': 311 | { 312 | 'name': 'journal_date', 313 | 'useincommands': ['blocks'], 314 | 'segment': 'filters', 315 | 'datalog': '[?page :block/journal-day ?journaldate]', 316 | 'comment': 'get journal date into variable ?journaldate' 317 | }, 318 | 'block_properties_are': 319 | { 320 | 'name': 'block_properties_are', 321 | 'useincommands': ['blocks'], 322 | 'segment': 'filters', 323 | 'datalog': '(property ?block :$$ARG1 $$ARG2)', 324 | 'comment': 'Select if block has a single property (arg1) with value arg2' 325 | }, 326 | 'not_block_properties_are': 327 | { 328 | 'name': 'not_block_properties_are', 329 | 'useincommands': ['blocks'], 330 | 'segment': 'filters', 331 | 'datalog': '(not (property ?block :$$ARG1 $$ARG2))', 332 | 'comment': 'Exclude if block has a single property (arg1) with value arg2' 333 | }, 334 | 'blocktags_are': 335 | { 336 | 'name': 'blocktags_are', 337 | 'useincommands': ['blocks'], 338 | 'segment': 'filters', 339 | 'datalog': '(page-ref ?block "$$ARG1")', 340 | 'comment': 'Select block if it has a specific tag or page link' 341 | }, 342 | 'not_blocktags_are': 343 | { 344 | 'name': 'not_blocktags_are', 345 | 'useincommands': ['blocks'], 346 | 'segment': 'filters', 347 | 'datalog': '(not (page-ref ?block "$$ARG1"))', 348 | 'comment': 'Exclude block if it has a specific tag or page link' 349 | }, 350 | 'page_properties_are': 351 | { 352 | 'name': 'page_properties_are', 353 | 'useincommands': ['blocks'], 354 | 'segment': 'filters', 355 | 'datalog': '(page-property ?page :$$ARG1 $$ARG2)', 356 | 'comment': 'Select block if the page it belongs to\nhas a page property (arg1) with value arg2' 357 | }, 358 | 'not_page_properties_are': 359 | { 360 | 'name': 'not_page_properties_are', 361 | 'useincommands': ['blocks'], 362 | 'segment': 'filters', 363 | 'datalog': '(not (page-property ?page :$$ARG1 $$ARG2))', 364 | 'comment': 'Exclude block if the page it belongs to\nhas a page property (arg1) with value arg2' 365 | }, 366 | 'pagetags_are': 367 | { 368 | 'name': 'pagetags_are', 369 | 'useincommands': ['blocks'], 370 | 'segment': 'filters', 371 | 'datalog': '(page-tags ?page #{"$$ARG1"})', 372 | 'comment': 'Select block if the page it belongs to\nhas a one or more page tags' 373 | }, 374 | 'not_pagetags_are': 375 | { 376 | 'name': 'not_pagetags_are', 377 | 'useincommands': ['blocks'], 378 | 'segment': 'filters', 379 | 'datalog': '(not (page-tags ?page #{"$$ARG1"}))', 380 | 'comment': 'Exclude block if the page it belongs to\nhas one or more page tags' 381 | }, 382 | 'namespace': 383 | { 384 | 'name': 'namespace', 385 | 'useincommands': ['blocks'], 386 | 'segment': 'filters', 387 | 'datalog': '(namespace ?page "$$ARG1")', 388 | 'comment': 'Select block if the page it belongs to is within namespace arg1' 389 | }, 390 | 'not_namespace': 391 | { 392 | 'name': 'not_namespace_blocks', 393 | 'useincommands': ['blocks'], 394 | 'segment': 'filters', 395 | 'datalog': '(not (namespace ?page "$$ARG1"))', 396 | 'comment': 'Exclude block if the page it belongs to is within namespace arg1' 397 | }, 398 | 'arg_blockcontent_startswith': 399 | { 400 | 'name': 'arg_blockcontent_startswith', 401 | 'useincommands': ['blocks'], 402 | 'segment': 'filters', 403 | 'datalog': '[(clojure.string/starts-with? ?blockcontent "$$ARG1")]', 404 | 'comment': 'Select if block content starts with arg1' 405 | }, 406 | 'arg_blockcontent_endswith': 407 | { 408 | 'name': 'arg_blockcontent_endswith', 409 | 'useincommands': ['blocks'], 410 | 'segment': 'filters', 411 | 'datalog': '[(clojure.string/ends-with? ?blockcontent "$$ARG1")]', 412 | 'comment': 'Select if block content ends with arg1' 413 | }, 414 | 'arg_blockcontent_contains': 415 | { 416 | 'name': 'arg_blockcontent_contains', 417 | 'useincommands': ['blocks'], 418 | 'segment': 'filters', 419 | 'datalog': '[(clojure.string/includes? ?blockcontent "$$ARG1")]', 420 | 'comment': 'Select if block content contains arg1' 421 | }, 422 | 'not_arg_blockcontent_startswith': 423 | { 424 | 'name': 'not_arg_blockcontent_startswith', 425 | 'useincommands': ['blocks'], 426 | 'segment': 'filters', 427 | 'datalog': '(not [(clojure.string/starts-with? ?blockcontent "$$ARG1")])', 428 | 'comment': 'Exclude if block content starts with arg1' 429 | }, 430 | 'not_arg_blockcontent_endswith': 431 | { 432 | 'name': 'not_arg_blockcontent_endswith', 433 | 'useincommands': ['blocks'], 434 | 'segment': 'filters', 435 | 'datalog': '(not [(clojure.string/ends-with? ?blockcontent "$$ARG1")])', 436 | 'comment': 'Exclude if block content ends with arg1' 437 | }, 438 | 'not_arg_blockcontent_contains': 439 | { 440 | 'name': 'not_arg_blockcontent_contains', 441 | 'useincommands': ['blocks'], 442 | 'segment': 'filters', 443 | 'datalog': '(not [(clojure.string/includes? ?blockcontent "$$ARG1")])', 444 | 'comment': 'Exclude if block content contains arg1' 445 | }, 446 | 'scheduledbetween': 447 | { 448 | 'name': 'scheduledbetween', 449 | 'useincommands': ['blocks'], 450 | 'segment': 'inputs', 451 | 'datalog': ':inputs [$$ARG1]', 452 | 'comment': 'set the input values for a date range for example could be\n :today :365d-after' 453 | }, 454 | 'deadlinebetween': 455 | { 456 | 'name': 'deadlinebetween', 457 | 'useincommands': ['blocks'], 458 | 'segment': 'inputs', 459 | 'datalog': ':inputs [$$ARG1]', 460 | 'comment': 'set the input values for a date range for example could be\n :today :365d-after' 461 | }, 462 | }, 463 | 'pages-querylines': { 464 | 'original-pagename': 465 | { 466 | 'name': 'original-pagename', 467 | 'useincommands': ['pages'], 468 | 'segment': 'filters', 469 | 'datalog': '[?block :block/original-name ?originalpagename]', 470 | 'comment': 'get original page name (case sensitive) into variable ?originalpagename', 471 | }, 472 | 'pagename': 473 | { 474 | 'name': 'pagename', 475 | 'useincommands': ['pages'], 476 | 'segment': 'filters', 477 | 'datalog': '[?block :block/name ?pagename]', 478 | 'comment': 'get page name (lowercase) from the special page block into variable ?pagename', 479 | }, 480 | 'pagename_is': 481 | { 482 | 'name': 'pagename_is', 483 | 'useincommands': ['pages'], 484 | 'segment': 'filters', 485 | 'datalog': '[?block :block/name "$$ARG1"]', 486 | 'comment': 'Select specific page using the :block/name\nwhich is only present in special blocks that\nhave the page attributes' 487 | }, 488 | 'not_pagename_is': 489 | { 490 | 'name': 'not_pagename_is', 491 | 'useincommands': ['pages'], 492 | 'segment': 'filters', 493 | 'datalog': '(not [?block :block/name "$$ARG1"])', 494 | 'comment': 'Exclude specific page using the :block/name\nwhich is only present in special blocks that\nhave the page attributes' 495 | }, 496 | 'page_is_journal': 497 | { 498 | 'name': 'page_is_journal', 499 | 'useincommands': ['pages'], 500 | 'segment': 'filters', 501 | 'datalog': '[?block :block/journal? true]', 502 | 'comment': 'Select block if it belonhs to a a journal' 503 | }, 504 | 'not_page_is_journal': 505 | { 506 | 'name': 'not_page_is_journal', 507 | 'useincommands': ['pages'], 508 | 'segment': 'filters', 509 | 'datalog': '[?block :block/journal? false]', 510 | 'comment': 'Exclude block if it belonhs to a a journal' 511 | }, 512 | 'journal_date': 513 | { 514 | 'name': 'journal_date', 515 | 'useincommands': ['pages'], 516 | 'segment': 'filters', 517 | 'datalog': '[?block :block/journal-day ?journaldate]', 518 | 'comment': 'get journal date into variable ?journaldate' 519 | }, 520 | 'page_properties_are': 521 | { 522 | 'name': 'page_properties_are', 523 | 'useincommands': ['pages'], 524 | 'segment': 'filters', 525 | 'datalog': '(page-property ?block :$$ARG1 $$ARG2)', 526 | 'comment': 'Select if block is a special page blockand has a single property (arg1) with value arg2' 527 | }, 528 | 'not_page_properties_are': 529 | { 530 | 'name': 'not_page_properties_are', 531 | 'useincommands': ['pages'], 532 | 'segment': 'filters', 533 | 'datalog': '(not (page-property ?block :$$ARG1 $$ARG2))', 534 | 'comment': 'Exclude if block is a special page blockand has a single property (arg1) with value arg2' 535 | }, 536 | 'block_properties_are': 537 | { 538 | 'name': 'block_properties_are', 539 | 'useincommands': ['pages'], 540 | 'segment': 'filters', 541 | 'datalog': '(page-property ?block :$$ARG1 $$ARG2)', 542 | 'comment': 'Select if block has a single property (arg1) with value arg2' 543 | }, 544 | 'not_block_properties_are': 545 | { 546 | 'name': 'not_block_properties_are', 547 | 'useincommands': ['pages'], 548 | 'segment': 'filters', 549 | 'datalog': '(not (property ?block :$$ARG1 $$ARG2))', 550 | 'comment': 'Exclude if block has a single property (arg1) with value arg2' 551 | }, 552 | 'pagetags_are': 553 | { 554 | 'name': 'pagetags_are', 555 | 'useincommands': ['pages'], 556 | 'segment': 'filters', 557 | 'datalog': '(page-tags ?block #{"$$ARG1"})', 558 | 'comment': 'Select special page block with one or more page tags' 559 | }, 560 | 'not_pagetags_are': 561 | { 562 | 'name': 'not_pagetags_are', 563 | 'useincommands': ['pages'], 564 | 'segment': 'filters', 565 | 'datalog': '(not (page-tags ?block #{"$$ARG1"}))', 566 | 'comment': 'Exclude special page block with one or more page tags' 567 | }, 568 | 'blocktags_are': 569 | { 570 | 'name': 'blocktags_are', 571 | 'useincommands': ['blocks'], 572 | 'segment': 'filters', 573 | 'datalog': '(page-tags ?block #{"$$ARG1"})', 574 | 'comment': 'Select block if the page it belongs to\nhas a specific page tag or page link' 575 | }, 576 | 'not_blocktags_are': 577 | { 578 | 'name': 'not_blocktags_are', 579 | 'useincommands': ['blocks'], 580 | 'segment': 'filters', 581 | 'datalog': '(not (page-tags ?block #{"$$ARG1"}))', 582 | 'comment': 'Exclude block if the page it belongs to\nhas a specific page tag or page link' 583 | }, 584 | 'namespace': 585 | { 586 | 'name': 'namespace_pages', 587 | 'useincommands': ['pages'], 588 | 'segment': 'filters', 589 | 'datalog': '(namespace ?block "$$ARG1")', 590 | 'comment': 'Select if special page block is within namespace arg1' 591 | }, 592 | 'not_namespace': 593 | { 594 | 'name': 'not_namespace_pages', 595 | 'useincommands': ['pages'], 596 | 'segment': 'filters', 597 | 'datalog': '(not (namespace ?block "$$ARG1"))', 598 | 'comment': 'Select if special page block is within namespace arg1' 599 | }, 600 | 'arg_pagename_startswith': 601 | { 602 | 'name': 'arg_pagename_startswith', 603 | 'useincommands': ['pages'], 604 | 'segment': 'filters', 605 | 'datalog': '[(clojure.string/starts-with? ?pagename "$$ARG1")]', 606 | 'comment': 'Select if page title starts with arg1' 607 | }, 608 | 'arg_pagename_endswith': 609 | { 610 | 'name': 'arg_pagename_endswith', 611 | 'useincommands': ['pages'], 612 | 'segment': 'filters', 613 | 'datalog': '[(clojure.string/ends-with? ?pagename "$$ARG1")]', 614 | 'comment': 'Select if page title ends with arg1' 615 | }, 616 | 'arg_pagename_contains': 617 | { 618 | 'name': 'arg_pagename_contains', 619 | 'useincommands': ['common'], 620 | 'segment': 'filters', 621 | 'datalog': '[(clojure.string/includes? ?pagename "$$ARG1")]', 622 | 'comment': 'Select if page title contains arg1' 623 | }, 624 | 'not_arg_pagename_startswith': 625 | { 626 | 'name': 'not_arg_pagename_startswith', 627 | 'useincommands': ['common'], 628 | 'segment': 'filters', 629 | 'datalog': '(not [(clojure.string/starts-with? ?pagename "$$ARG1")])', 630 | 'comment': 'Exclude if page title ends with arg1' 631 | }, 632 | 'not_arg_pagename_endswith': 633 | { 634 | 'name': 'not_arg_pagename_endswith', 635 | 'useincommands': ['pages'], 636 | 'segment': 'filters', 637 | 'datalog': '(not [(clojure.string/ends-with? ?pagename "$$ARG1")])', 638 | 'comment': 'Exclude if page title ends with arg1' 639 | }, 640 | 'not_arg_pagename_contains': 641 | { 642 | 'name': 'not_arg_pagename_contains', 643 | 'useincommands': ['pages'], 644 | 'segment': 'filters', 645 | 'datalog': '(not [(clojure.string/includes? ?pagename "$$ARG1")])', 646 | 'comment': 'Exclude if page title contains arg1' 647 | }, 648 | } 649 | } 650 | 651 | // Dictionary of query builder commands 652 | var commandsDict = { 653 | "blocks": { 654 | "querylines": [ 655 | "findblocks", 656 | "where", 657 | "blockcontent", 658 | "page", 659 | "pagename", 660 | ], 661 | "description": "select logseq blocks by wildcards" 662 | }, 663 | "blockproperties": { 664 | "querylines": [ 665 | ], 666 | "description": "select blocks by property values" 667 | }, 668 | "blocktags": { 669 | "querylines": [ 670 | ], 671 | "description": "select blocks by tag" 672 | }, 673 | "deadline": { 674 | "querylines": [ 675 | "deadline" 676 | ], 677 | "description": "select blocks that have a deadline" 678 | }, 679 | "deadlinebetween": { 680 | "querylines": [ 681 | "deadline", 682 | "deadlinefrom", 683 | "deadlineto", 684 | "daterange" 685 | ], 686 | "description": "select blocks that have a deadline in a date range" 687 | }, 688 | "journalsbetween": { 689 | "querylines": [ 690 | "journal_date", 691 | "journalfrom", 692 | "journalto", 693 | "daterange" 694 | ], 695 | "description": "only select journal pages in a date range" 696 | }, 697 | "journalonly": { 698 | "querylines": [ 699 | "page_is_journal" 700 | ], 701 | "description": "only select journal pages" 702 | }, 703 | "namespace": { 704 | "querylines": [ 705 | ], 706 | "description": "select pages or blocks within a namespace" 707 | }, 708 | "pages": { 709 | "querylines": [ 710 | "findblocks", 711 | "where", 712 | "pagename", 713 | ], 714 | "description": "select pages by wildcards" 715 | }, 716 | "pageproperties": { 717 | "querylines": [ 718 | ], 719 | "description": "select pages by page properties" 720 | }, 721 | "pagetags": { 722 | "querylines": [ 723 | "not_page_is_journal" 724 | ], 725 | "description": "select pages by tag" 726 | }, 727 | "pagelinks": { 728 | "querylines": [ 729 | ], 730 | "description": "select any blocks that has links to pages" 731 | }, 732 | "tasks": { 733 | "querylines": [ 734 | 'marker', 735 | ], 736 | "description": "select blocks that have tasks present" 737 | }, 738 | "scheduled": { 739 | "querylines": [ 740 | "scheduled" 741 | ], 742 | "description": "select blocks that are scheduled" 743 | }, 744 | "scheduledbetween": { 745 | "querylines": [ 746 | "scheduled", 747 | "scheduledfrom", 748 | "scheduledto", 749 | "daterange" 750 | ], 751 | "description": "select blocks that are scheduled in a date range" 752 | }, 753 | "collapse": { 754 | "querylines": [ 755 | "collapse_true" 756 | ], 757 | "description": "collapse found blocks" 758 | }, 759 | "expand": { 760 | "querylines": [ 761 | "collapse_false" 762 | ], 763 | "description": "expand found blocks" 764 | }, 765 | "showbreadcrumb": { 766 | "querylines": [ 767 | "breadcrumb_show_true" 768 | ], 769 | "description": "show breadcrumb for found blocks" 770 | }, 771 | "hidebreadcrumb": { 772 | "querylines": [ 773 | "breadcrumb_show_false" 774 | ], 775 | "description": "hide breadcrumb for found blocks" 776 | }, 777 | } 778 | 779 | // Local tests cases for automated test suite 780 | var QueryTestCases = [ 781 | 782 | // test 1 783 | `title: pages command - select all pages 784 | - pages 785 | - * 786 | #+BEGIN_QUERY 787 | { 788 | :title [:b "pages command - select all pages"] 789 | :query [:find (pull ?block [*]) 790 | :where 791 | [?block :block/name ?pagename] 792 | ] 793 | } 794 | #+END_QUERY 795 | `, 796 | 797 | // test 2 798 | `title: pages command - specific pages 799 | - pages 800 | - testpage001 801 | - testpage002 802 | #+BEGIN_QUERY 803 | { 804 | :title [:b "pages command - specific pages"] 805 | :query [:find (pull ?block [*]) 806 | :where 807 | [?block :block/name ?pagename] 808 | ( or 809 | [?block :block/name "testpage001"] 810 | [?block :block/name "testpage002"] 811 | ) 812 | ] 813 | } 814 | #+END_QUERY 815 | `, 816 | 817 | // test 3 818 | `title: pages command - pages by wildcards 819 | - pages 820 | - testpage00* 821 | #+BEGIN_QUERY 822 | { 823 | :title [:b "pages command - pages by wildcards"] 824 | :query [:find (pull ?block [*]) 825 | :where 826 | [?block :block/name ?pagename] 827 | [(clojure.string/starts-with? ?pagename "testpage00")] 828 | ] 829 | } 830 | #+END_QUERY 831 | `, 832 | 833 | // test 4 834 | `title: pages command - pages by wildcards 835 | - pages 836 | - *002 837 | #+BEGIN_QUERY 838 | { 839 | :title [:b "pages command - pages by wildcards"] 840 | :query [:find (pull ?block [*]) 841 | :where 842 | [?block :block/name ?pagename] 843 | [(clojure.string/ends-with? ?pagename "002")] 844 | ] 845 | } 846 | #+END_QUERY 847 | `, 848 | 849 | // test 5 850 | `title: pages command - pages by wildcards 851 | - pages 852 | - *page00* 853 | #+BEGIN_QUERY 854 | { 855 | :title [:b "pages command - pages by wildcards"] 856 | :query [:find (pull ?block [*]) 857 | :where 858 | [?block :block/name ?pagename] 859 | [(clojure.string/includes? ?pagename "page00")] 860 | ] 861 | } 862 | #+END_QUERY 863 | `, 864 | 865 | // test 6 866 | `title: pages command - ignore pages (including wildcards) 867 | - pages 868 | - not testpage* 869 | - not Queries* 870 | #+BEGIN_QUERY 871 | { 872 | :title [:b "pages command - ignore pages (including wildcards)"] 873 | :query [:find (pull ?block [*]) 874 | :where 875 | [?block :block/name ?pagename] 876 | (not [(clojure.string/starts-with? ?pagename "testpage")]) 877 | (not [(clojure.string/starts-with? ?pagename "Queries")]) 878 | ] 879 | } 880 | #+END_QUERY 881 | `, 882 | 883 | // test 7 884 | `title: blocks command - ignore blocks using wildcards 885 | - blocks 886 | - not And sir dare view* 887 | - not *here leave merit enjoy forth. 888 | - not *roof gutters* 889 | #+BEGIN_QUERY 890 | { 891 | :title [:b "blocks command - ignore blocks using wildcards"] 892 | :query [:find (pull ?block [*]) 893 | :where 894 | [?block :block/content ?blockcontent] 895 | [?block :block/page ?page] 896 | [?page :block/name ?pagename] 897 | (not [(clojure.string/ends-with? ?blockcontent "And sir dare view")]) 898 | (not [(clojure.string/starts-with? ?blockcontent "here leave merit enjoy forth.")]) 899 | (not [(clojure.string/includes? ?blockcontent "roof gutters")]) 900 | ] 901 | } 902 | #+END_QUERY 903 | `, 904 | 905 | // test 8 906 | `title: blocktags - select and exclude block level tags 907 | - blocks 908 | - * 909 | - blocktags 910 | - tagA 911 | - tagD 912 | - not tagB 913 | #+BEGIN_QUERY 914 | { 915 | :title [:b "blocktags - select and exclude block level tags"] 916 | :query [:find (pull ?block [*]) 917 | :where 918 | [?block :block/content ?blockcontent] 919 | [?block :block/page ?page] 920 | [?page :block/name ?pagename] 921 | ( or 922 | (page-ref ?block "taga") 923 | (page-ref ?block "tagd") 924 | ) 925 | (not (page-ref ?block "tagb")) 926 | ] 927 | } 928 | #+END_QUERY 929 | `, 930 | `title: blocktags and pages don't mix 931 | - pages 932 | - testpage00* 933 | - blocktags 934 | - tagA 935 | - not tagB 936 | #+BEGIN_QUERY 937 | 938 | ;; **ERROR: blocktags not valid with pages command use blocks command instead 939 | 940 | { 941 | :title [:b "blocktags and pages don't mix"] 942 | :query [:find (pull ?block [*]) 943 | :where 944 | [?block :block/name ?pagename] 945 | [(clojure.string/starts-with? ?pagename "testpage00")] 946 | ] 947 | } 948 | #+END_QUERY 949 | `, 950 | 951 | // test 10 952 | `title: pagetags - page level tags 953 | - pages 954 | - testpage* 955 | - pagetags 956 | - classA 957 | #+BEGIN_QUERY 958 | { 959 | :title [:b "pagetags - page level tags"] 960 | :query [:find (pull ?block [*]) 961 | :where 962 | [?block :block/name ?pagename] 963 | [(clojure.string/starts-with? ?pagename "testpage")] 964 | [?block :block/journal? false] 965 | (page-tags ?block #{"classa"}) 966 | ] 967 | } 968 | #+END_QUERY 969 | `, 970 | 971 | // test 11 972 | `title: pagetags and pages 973 | - pages 974 | - *dynamics* 975 | - pagetags 976 | - classB 977 | #+BEGIN_QUERY 978 | { 979 | :title [:b "pagetags and pages"] 980 | :query [:find (pull ?block [*]) 981 | :where 982 | [?block :block/name ?pagename] 983 | [(clojure.string/includes? ?pagename "dynamics")] 984 | [?block :block/journal? false] 985 | (page-tags ?block #{"classb"}) 986 | ] 987 | } 988 | #+END_QUERY 989 | `, 990 | 991 | // test 12 992 | `title: select and exclude task types 993 | - tasks 994 | - TODO 995 | - not DOING 996 | #+BEGIN_QUERY 997 | ;; WARNING: Must have 'pages' command or 'blocks' Command 998 | ;; otherwise the query cannot get any information 999 | ;; Inserting a blocks command for you 1000 | 1001 | { 1002 | :title [:b "select and exclude task types"] 1003 | :query [:find (pull ?block [*]) 1004 | :where 1005 | [?block :block/content ?blockcontent] 1006 | [?block :block/page ?page] 1007 | [?page :block/name ?pagename] 1008 | [?block :block/marker ?marker] 1009 | [(contains? #{"TODO"} ?marker)] 1010 | (not [(contains? #{"DOING"} ?marker)]) 1011 | ] 1012 | } 1013 | #+END_QUERY 1014 | `, 1015 | 1016 | `title: select and exclude task types 1017 | - pages 1018 | - testpage00* 1019 | - tasks 1020 | - TODO 1021 | - not DOING 1022 | #+BEGIN_QUERY 1023 | 1024 | ;; **ERROR: tasks not valid with pages command use blocks command instead 1025 | 1026 | { 1027 | :title [:b "select and exclude task types"] 1028 | :query [:find (pull ?block [*]) 1029 | :where 1030 | [?block :block/name ?pagename] 1031 | [(clojure.string/starts-with? ?pagename "testpage00")] 1032 | ] 1033 | } 1034 | #+END_QUERY 1035 | `, 1036 | 1037 | // test 14 1038 | `title: select and exclude pages with page properties 1039 | - pages 1040 | - * 1041 | - pageproperties 1042 | - pagetype, "p-major" 1043 | - pagetype, "p-minor" 1044 | - not pagetype, "p-advanced" 1045 | #+BEGIN_QUERY 1046 | { 1047 | :title [:b "select and exclude pages with page properties"] 1048 | :query [:find (pull ?block [*]) 1049 | :where 1050 | [?block :block/name ?pagename] 1051 | ( or 1052 | (page-property ?block :pagetype "p-major") 1053 | (page-property ?block :pagetype "p-minor") 1054 | ) 1055 | (not (page-property ?block :pagetype "p-advanced")) 1056 | ] 1057 | } 1058 | #+END_QUERY 1059 | `, 1060 | 1061 | // test 15 1062 | `title: select and exclude blocks with block properties 1063 | - blocks 1064 | - * 1065 | - blockproperties 1066 | - category, "b-thriller" 1067 | - category, "b-western" 1068 | - grade, "b-fiction" 1069 | #+BEGIN_QUERY 1070 | { 1071 | :title [:b "select and exclude blocks with block properties"] 1072 | :query [:find (pull ?block [*]) 1073 | :where 1074 | [?block :block/content ?blockcontent] 1075 | [?block :block/page ?page] 1076 | [?page :block/name ?pagename] 1077 | ( or 1078 | (property ?block :category "b-thriller") 1079 | (property ?block :category "b-western") 1080 | (property ?block :grade "b-fiction") 1081 | ) 1082 | ] 1083 | } 1084 | #+END_QUERY 1085 | `, 1086 | 1087 | // test 16 1088 | `title: only search pages in specific namespace 1089 | - pages 1090 | - * 1091 | - namespace 1092 | - physics 1093 | #+BEGIN_QUERY 1094 | { 1095 | :title [:b "only search pages in specific namespace"] 1096 | :query [:find (pull ?block [*]) 1097 | :where 1098 | [?block :block/name ?pagename] 1099 | (namespace ?block "physics") 1100 | ] 1101 | } 1102 | #+END_QUERY 1103 | `, 1104 | 1105 | // test 17 1106 | `title: find block properties in a namespace 1107 | - blocks 1108 | - * 1109 | - namespace 1110 | - tech/python 1111 | - blockproperties 1112 | - grade, "b-fiction" 1113 | #+BEGIN_QUERY 1114 | { 1115 | :title [:b "find block properties in a namespace"] 1116 | :query [:find (pull ?block [*]) 1117 | :where 1118 | [?block :block/content ?blockcontent] 1119 | [?block :block/page ?page] 1120 | [?page :block/name ?pagename] 1121 | (namespace ?page "tech/python") 1122 | (property ?block :grade "b-fiction") 1123 | ] 1124 | } 1125 | #+END_QUERY 1126 | `, 1127 | 1128 | // test 18 1129 | `title: find scheduled blocks in a namespace 1130 | - blocks 1131 | - * 1132 | - namespace 1133 | - physics 1134 | - scheduled 1135 | #+BEGIN_QUERY 1136 | { 1137 | :title [:b "find scheduled blocks in a namespace"] 1138 | :query [:find (pull ?block [*]) 1139 | :where 1140 | [?block :block/content ?blockcontent] 1141 | [?block :block/page ?page] 1142 | [?page :block/name ?pagename] 1143 | (namespace ?page "physics") 1144 | [?block :block/scheduled ?scheduleddate] 1145 | ] 1146 | } 1147 | #+END_QUERY 1148 | `, 1149 | 1150 | // test 19 1151 | `title: scheduled - find scheduled blocks in a date range 1152 | - blocks 1153 | - * 1154 | - scheduledbetween 1155 | - :720d-before :700d-after 1156 | #+BEGIN_QUERY 1157 | { 1158 | :title [:b "scheduled - find scheduled blocks in a date range"] 1159 | :query [:find (pull ?block [*]) 1160 | :in $ ?startdate ?enddate 1161 | :where 1162 | [?block :block/content ?blockcontent] 1163 | [?block :block/page ?page] 1164 | [?page :block/name ?pagename] 1165 | [?block :block/scheduled ?scheduleddate] 1166 | [(>= ?scheduleddate ?startdate)] 1167 | [(<= ?scheduleddate ?enddate)] 1168 | ] 1169 | :inputs [:720d-before :700d-after] 1170 | } 1171 | #+END_QUERY 1172 | `, 1173 | 1174 | // test 20 1175 | `title: find blocks with deadlines 1176 | - blocks 1177 | - * 1178 | - deadline 1179 | #+BEGIN_QUERY 1180 | { 1181 | :title [:b "find blocks with deadlines"] 1182 | :query [:find (pull ?block [*]) 1183 | :where 1184 | [?block :block/content ?blockcontent] 1185 | [?block :block/page ?page] 1186 | [?page :block/name ?pagename] 1187 | [?block :block/deadline ?deadlinedate] 1188 | ] 1189 | } 1190 | #+END_QUERY 1191 | `, 1192 | 1193 | // test 21 1194 | `title: find blocks with deadlines in a date range 1195 | - blocks 1196 | - * 1197 | - deadlinebetween 1198 | - :120d-before :30d-after 1199 | #+BEGIN_QUERY 1200 | { 1201 | :title [:b "find blocks with deadlines in a date range"] 1202 | :query [:find (pull ?block [*]) 1203 | :in $ ?startdate ?enddate 1204 | :where 1205 | [?block :block/content ?blockcontent] 1206 | [?block :block/page ?page] 1207 | [?page :block/name ?pagename] 1208 | [?block :block/deadline ?deadlinedate] 1209 | [(>= ?deadlinedate ?startdate)] 1210 | [(<= ?deadlinedate ?enddate)] 1211 | ] 1212 | :inputs [:120d-before :30d-after] 1213 | } 1214 | #+END_QUERY 1215 | `, 1216 | 1217 | // test 22 1218 | `title: find journals 1219 | - pages 1220 | - * 1221 | - journalonly 1222 | #+BEGIN_QUERY 1223 | { 1224 | :title [:b "find journals"] 1225 | :query [:find (pull ?block [*]) 1226 | :where 1227 | [?block :block/name ?pagename] 1228 | [?block :block/journal? true] 1229 | ] 1230 | } 1231 | #+END_QUERY 1232 | `, 1233 | 1234 | // test 23 1235 | `title: find journal in a date range 1236 | - pages 1237 | - * 1238 | - journalsbetween 1239 | - :today :30d-after 1240 | #+BEGIN_QUERY 1241 | { 1242 | :title [:b "find journal in a date range"] 1243 | :query [:find (pull ?block [*]) 1244 | :in $ ?startdate ?enddate 1245 | :where 1246 | [?block :block/name ?pagename] 1247 | [?block :block/journal-day ?journaldate] 1248 | [(>= ?journaldate ?startdate)] 1249 | [(<= ?journaldate ?enddate)] 1250 | ] 1251 | :inputs [:today :30d-after] 1252 | } 1253 | #+END_QUERY 1254 | `, 1255 | 1256 | // test 24 1257 | `title: find journals between dates 1258 | - blocks 1259 | - * 1260 | - journalsbetween 1261 | - :30d-before :today 1262 | #+BEGIN_QUERY 1263 | { 1264 | :title [:b "find journals between dates"] 1265 | :query [:find (pull ?block [*]) 1266 | :in $ ?startdate ?enddate 1267 | :where 1268 | [?block :block/content ?blockcontent] 1269 | [?block :block/page ?page] 1270 | [?page :block/name ?pagename] 1271 | [?page :block/journal-day ?journaldate] 1272 | [(>= ?journaldate ?startdate)] 1273 | [(<= ?journaldate ?enddate)] 1274 | ] 1275 | :inputs [:30d-before :today] 1276 | } 1277 | #+END_QUERY 1278 | `, 1279 | 1280 | // test 25 1281 | `title: collapse results 1282 | - pages 1283 | - testpage00* 1284 | - collapse 1285 | #+BEGIN_QUERY 1286 | { 1287 | :title [:b "collapse results"] 1288 | :query [:find (pull ?block [*]) 1289 | :where 1290 | [?block :block/name ?pagename] 1291 | [(clojure.string/starts-with? ?pagename "testpage00")] 1292 | ] 1293 | :collapsed? true 1294 | } 1295 | #+END_QUERY 1296 | `, 1297 | 1298 | // test 26 1299 | `title: expand results 1300 | - pages 1301 | - testpage00* 1302 | - expand 1303 | #+BEGIN_QUERY 1304 | { 1305 | :title [:b "expand results"] 1306 | :query [:find (pull ?block [*]) 1307 | :where 1308 | [?block :block/name ?pagename] 1309 | [(clojure.string/starts-with? ?pagename "testpage00")] 1310 | ] 1311 | :collapsed? false 1312 | } 1313 | #+END_QUERY 1314 | `, 1315 | 1316 | // test 27 1317 | `title: show breadcrumbs 1318 | - pages 1319 | - testpage00* 1320 | - showbreadcrumb 1321 | #+BEGIN_QUERY 1322 | { 1323 | :title [:b "show breadcrumbs"] 1324 | :query [:find (pull ?block [*]) 1325 | :where 1326 | [?block :block/name ?pagename] 1327 | [(clojure.string/starts-with? ?pagename "testpage00")] 1328 | ] 1329 | :breadcrumb-show? true 1330 | } 1331 | #+END_QUERY 1332 | `, 1333 | 1334 | // test 28 1335 | `title: hide breadcrumbs 1336 | - pages 1337 | - testpage00* 1338 | - hidebreadcrumb 1339 | #+BEGIN_QUERY 1340 | { 1341 | :title [:b "hide breadcrumbs"] 1342 | :query [:find (pull ?block [*]) 1343 | :where 1344 | [?block :block/name ?pagename] 1345 | [(clojure.string/starts-with? ?pagename "testpage00")] 1346 | ] 1347 | :breadcrumb-show? false 1348 | } 1349 | #+END_QUERY 1350 | `, 1351 | 1352 | // test 29 1353 | `title: find journal in a date range using blocks 1354 | - blocks 1355 | - * 1356 | - journalsbetween 1357 | - :today :30d-after 1358 | #+BEGIN_QUERY 1359 | { 1360 | :title [:b "find journal in a date range using blocks"] 1361 | :query [:find (pull ?block [*]) 1362 | :in $ ?startdate ?enddate 1363 | :where 1364 | [?block :block/content ?blockcontent] 1365 | [?block :block/page ?page] 1366 | [?page :block/name ?pagename] 1367 | [?page :block/journal-day ?journaldate] 1368 | [(>= ?journaldate ?startdate)] 1369 | [(<= ?journaldate ?enddate)] 1370 | ] 1371 | :inputs [:today :30d-after] 1372 | } 1373 | #+END_QUERY 1374 | `, 1375 | // test 30 1376 | `title: All blocks - test access to parent pages tags, journal 1377 | - blocks 1378 | - * 1379 | - tasks 1380 | - TODO 1381 | - pagetags 1382 | - classC 1383 | #+BEGIN_QUERY 1384 | { 1385 | :title [:b "All blocks - test access to parent pages tags, journal"] 1386 | :query [:find (pull ?block [*]) 1387 | :where 1388 | [?block :block/content ?blockcontent] 1389 | [?block :block/page ?page] 1390 | [?page :block/name ?pagename] 1391 | [?block :block/marker ?marker] 1392 | [(contains? #{"TODO"} ?marker)] 1393 | [?page :block/journal? false] 1394 | (page-tags ?page #{"classc"}) 1395 | ] 1396 | } 1397 | #+END_QUERY 1398 | `, 1399 | // test 31 1400 | `title: Pages only - Access page properties 1401 | - pages 1402 | - * 1403 | - pageproperties 1404 | - pagetype, "p-minor" 1405 | #+BEGIN_QUERY 1406 | { 1407 | :title [:b "Pages only - Access page properties"] 1408 | :query [:find (pull ?block [*]) 1409 | :where 1410 | [?block :block/name ?pagename] 1411 | (page-property ?block :pagetype "p-minor") 1412 | ] 1413 | } 1414 | #+END_QUERY 1415 | `, 1416 | // test 32 1417 | `title: Pages only - access multiple property values 1418 | - pages 1419 | - * 1420 | - pageproperties 1421 | - pagetype, "p-minor" 1422 | - pagetype, "p-major" 1423 | #+BEGIN_QUERY 1424 | { 1425 | :title [:b "Pages only - access multiple property values"] 1426 | :query [:find (pull ?block [*]) 1427 | :where 1428 | [?block :block/name ?pagename] 1429 | ( or 1430 | (page-property ?block :pagetype "p-minor") 1431 | (page-property ?block :pagetype "p-major") 1432 | ) 1433 | ] 1434 | } 1435 | #+END_QUERY 1436 | `, 1437 | // test 33 1438 | `title: Page blocks only - pagetags 1439 | - pages 1440 | - * 1441 | - pagetags 1442 | - classC 1443 | #+BEGIN_QUERY 1444 | { 1445 | :title [:b "Page blocks only - pagetags"] 1446 | :query [:find (pull ?block [*]) 1447 | :where 1448 | [?block :block/name ?pagename] 1449 | [?block :block/journal? false] 1450 | (page-tags ?block #{"classc"}) 1451 | ] 1452 | } 1453 | #+END_QUERY 1454 | `, 1455 | // test 34 1456 | `title: Pages only - select all pages 1457 | - pages 1458 | - * 1459 | #+BEGIN_QUERY 1460 | { 1461 | :title [:b "Pages only - select all pages"] 1462 | :query [:find (pull ?block [*]) 1463 | :where 1464 | [?block :block/name ?pagename] 1465 | ] 1466 | } 1467 | #+END_QUERY 1468 | `, 1469 | // test 35 1470 | `title: pages command - specific pages 1471 | - pages 1472 | - testpage001 1473 | - testpage002 1474 | #+BEGIN_QUERY 1475 | { 1476 | :title [:b "pages command - specific pages"] 1477 | :query [:find (pull ?block [*]) 1478 | :where 1479 | [?block :block/name ?pagename] 1480 | ( or 1481 | [?block :block/name "testpage001"] 1482 | [?block :block/name "testpage002"] 1483 | ) 1484 | ] 1485 | } 1486 | #+END_QUERY 1487 | `, 1488 | // test 36 1489 | `title: pages command - pages by wildcards 1490 | - pages 1491 | - testpage00* 1492 | #+BEGIN_QUERY 1493 | { 1494 | :title [:b "pages command - pages by wildcards"] 1495 | :query [:find (pull ?block [*]) 1496 | :where 1497 | [?block :block/name ?pagename] 1498 | [(clojure.string/starts-with? ?pagename "testpage00")] 1499 | ] 1500 | } 1501 | #+END_QUERY 1502 | `, 1503 | // test 37 1504 | `title: pages command - pages by wildcards 1505 | - pages 1506 | - *002 1507 | #+BEGIN_QUERY 1508 | { 1509 | :title [:b "pages command - pages by wildcards"] 1510 | :query [:find (pull ?block [*]) 1511 | :where 1512 | [?block :block/name ?pagename] 1513 | [(clojure.string/ends-with? ?pagename "002")] 1514 | ] 1515 | } 1516 | #+END_QUERY 1517 | `, 1518 | // test 38 1519 | `title: journalsbetween defaulting to blocks retrieval 1520 | - journalsbetween 1521 | - :today :30d-after 1522 | #+BEGIN_QUERY 1523 | ;; WARNING: Must have 'pages' command or 'blocks' Command 1524 | ;; otherwise the query cannot get any information 1525 | ;; Inserting a blocks command for you 1526 | 1527 | { 1528 | :title [:b "journalsbetween defaulting to blocks retrieval"] 1529 | :query [:find (pull ?block [*]) 1530 | :in $ ?startdate ?enddate 1531 | :where 1532 | [?block :block/content ?blockcontent] 1533 | [?block :block/page ?page] 1534 | [?page :block/name ?pagename] 1535 | [?page :block/journal-day ?journaldate] 1536 | [(>= ?journaldate ?startdate)] 1537 | [(<= ?journaldate ?enddate)] 1538 | ] 1539 | :inputs [:today :30d-after] 1540 | } 1541 | #+END_QUERY 1542 | `, 1543 | // test 39 1544 | `title: journalsbetween using pages retrieval 1545 | - pages 1546 | - * 1547 | - journalsbetween 1548 | - :today :30d-after 1549 | #+BEGIN_QUERY 1550 | { 1551 | :title [:b "journalsbetween using pages retrieval"] 1552 | :query [:find (pull ?block [*]) 1553 | :in $ ?startdate ?enddate 1554 | :where 1555 | [?block :block/name ?pagename] 1556 | [?block :block/journal-day ?journaldate] 1557 | [(>= ?journaldate ?startdate)] 1558 | [(<= ?journaldate ?enddate)] 1559 | ] 1560 | :inputs [:today :30d-after] 1561 | } 1562 | #+END_QUERY 1563 | `, 1564 | // test 40 1565 | `title: page property combinations using and and or 1566 | - pages 1567 | - * 1568 | - pageproperties 1569 | - pagecategory, "p-minor" 1570 | - or pagecategory, "p-minimum" 1571 | - and pagetype, "p-type1" 1572 | #+BEGIN_QUERY 1573 | { 1574 | :title [:b "page property combinations using and and or"] 1575 | :query [:find (pull ?block [*]) 1576 | :where 1577 | [?block :block/name ?pagename] 1578 | ( or 1579 | (page-property ?block :pagecategory "p-minor") 1580 | (page-property ?block :pagecategory "p-minimum") 1581 | ) 1582 | (page-property ?block :pagetype "p-type1") 1583 | ] 1584 | } 1585 | #+END_QUERY 1586 | `, 1587 | // test 41 1588 | `title: page tag combinations using and and or 1589 | - pages 1590 | - * 1591 | - pagetags 1592 | - classA 1593 | - or classB 1594 | - and classH 1595 | #+BEGIN_QUERY 1596 | { 1597 | :title [:b "page tag combinations using and and or"] 1598 | :query [:find (pull ?block [*]) 1599 | :where 1600 | [?block :block/name ?pagename] 1601 | [?block :block/journal? false] 1602 | ( or 1603 | (page-tags ?block #{"classa"}) 1604 | (page-tags ?block #{"classb"}) 1605 | ) 1606 | (page-tags ?block #{"classh"}) 1607 | ] 1608 | } 1609 | #+END_QUERY 1610 | `, 1611 | // test 42 1612 | `title: block property combinations using and and or 1613 | - blocks 1614 | - * 1615 | - blockproperties 1616 | - category, "b-fiction" 1617 | - or grade, "b-western" 1618 | - and category, "b-travel" 1619 | #+BEGIN_QUERY 1620 | { 1621 | :title [:b "block property combinations using and and or"] 1622 | :query [:find (pull ?block [*]) 1623 | :where 1624 | [?block :block/content ?blockcontent] 1625 | [?block :block/page ?page] 1626 | [?page :block/name ?pagename] 1627 | ( or 1628 | (property ?block :category "b-fiction") 1629 | (property ?block :grade "b-western") 1630 | ) 1631 | (property ?block :category "b-travel") 1632 | ] 1633 | } 1634 | #+END_QUERY 1635 | `, 1636 | // test 43 1637 | `title: block tag combinations using and and or 1638 | - blocks 1639 | - * 1640 | - blocktags 1641 | - tagA 1642 | - or tagB 1643 | - and tagD 1644 | #+BEGIN_QUERY 1645 | { 1646 | :title [:b "block tag combinations using and and or"] 1647 | :query [:find (pull ?block [*]) 1648 | :where 1649 | [?block :block/content ?blockcontent] 1650 | [?block :block/page ?page] 1651 | [?page :block/name ?pagename] 1652 | ( or 1653 | (page-ref ?block "taga") 1654 | (page-ref ?block "tagb") 1655 | ) 1656 | (page-ref ?block "tagd") 1657 | ] 1658 | } 1659 | #+END_QUERY 1660 | `, 1661 | // test 44 1662 | `title: task and or combintions 1663 | - blocks 1664 | - * 1665 | - tasks 1666 | - TODO 1667 | - and WAITING 1668 | - or LATER 1669 | - not DOING 1670 | #+BEGIN_QUERY 1671 | { 1672 | :title [:b "task and or combintions"] 1673 | :query [:find (pull ?block [*]) 1674 | :where 1675 | [?block :block/content ?blockcontent] 1676 | [?block :block/page ?page] 1677 | [?page :block/name ?pagename] 1678 | [?block :block/marker ?marker] 1679 | ( or 1680 | [(contains? #{"TODO"} ?marker)] 1681 | [(contains? #{"LATER"} ?marker)] 1682 | ) 1683 | [(contains? #{"WAITING"} ?marker)] 1684 | (not [(contains? #{"DOING"} ?marker)]) 1685 | ] 1686 | } 1687 | #+END_QUERY 1688 | `, 1689 | // test 45 1690 | `title: select blocks with links to pages 1691 | - blocks 1692 | - * 1693 | - pagelinks 1694 | - gardening 1695 | - vegetables 1696 | - not turnips 1697 | #+BEGIN_QUERY 1698 | { 1699 | :title [:b "select blocks with links to pages"] 1700 | :query [:find (pull ?block [*]) 1701 | :where 1702 | [?block :block/content ?blockcontent] 1703 | [?block :block/page ?page] 1704 | [?page :block/name ?pagename] 1705 | ( or 1706 | [?block :block/path-refs [:block/name "gardening"]] 1707 | [?block :block/path-refs [:block/name "vegetables"]] 1708 | ) 1709 | (not [?block :block/path-refs [:block/name "turnips"]]) 1710 | ] 1711 | } 1712 | #+END_QUERY 1713 | `, 1714 | // test 46 1715 | `title: select blocks with links to journals 1716 | - blocks 1717 | - * 1718 | - pagelinks 1719 | - Dec 25th, 2022 1720 | - Jan 1st, 2019 1721 | #+BEGIN_QUERY 1722 | { 1723 | :title [:b "select blocks with links to journals"] 1724 | :query [:find (pull ?block [*]) 1725 | :where 1726 | [?block :block/content ?blockcontent] 1727 | [?block :block/page ?page] 1728 | [?page :block/name ?pagename] 1729 | ( or 1730 | [?block :block/path-refs [:block/name "dec 25th, 2022"]] 1731 | [?block :block/path-refs [:block/name "jan 1st, 2019"]] 1732 | ) 1733 | ] 1734 | } 1735 | #+END_QUERY 1736 | ` 1737 | 1738 | ] 1739 | 1740 | function test_queryDBRead(section, key) { 1741 | try { 1742 | read1 = querylineDict[section][key]['name'] 1743 | read2 = querylineDict[section][key]['segment'] 1744 | read3 = querylineDict[section][key]['segment'] 1745 | read4 = querylineDict[section][key]['datalog'] 1746 | read5 = querylineDict[section][key]['comment'] 1747 | // return read1 1748 | return read1 + '\n' + read2 + '\n' + read3 + '\n' + read4 + '\n' + read5 + '\n' 1749 | 1750 | } catch { 1751 | return "Failed" 1752 | } 1753 | } 1754 | 1755 | 1756 | 1757 | 1758 | function add(num1, num2) { 1759 | return num1 + num2 1760 | } 1761 | 1762 | 1763 | // ========= global variables 1764 | 1765 | // ---query structure 1766 | var query_template = { 1767 | "start": [], 1768 | "errors": [], 1769 | "open": [], 1770 | "title": [], 1771 | "query": [], 1772 | "in": [], 1773 | "where": [], 1774 | "filters": [], 1775 | "closefind": [], 1776 | "inputs": [], 1777 | "view": [], 1778 | "options": [], 1779 | "closequery": [], 1780 | "end": [] 1781 | } 1782 | 1783 | var codeblock = false 1784 | var mode = "javascript" 1785 | query = query_template 1786 | querygroup = "pages-querylines" 1787 | querylineDBDict = {} 1788 | showcommandcomments = false 1789 | 1790 | // =========== FUNCTIONS ================ 1791 | 1792 | function initialiseQuery() { 1793 | query = query_template; 1794 | for (key of Object.keys(query)) { 1795 | query[key] = [] 1796 | } 1797 | return query; 1798 | } 1799 | 1800 | function initialisteQueryLineDict() { 1801 | var tempDict; 1802 | tempDict = {}; 1803 | tempDict["pages-querylines"] = querylineDict["pages-querylines"]; 1804 | tempDict["blocks-querylines"] = querylineDict["blocks-querylines"]; 1805 | 1806 | for (const [commonqueryline, value] of Object.entries(querylineDict['common-querylines'])) { 1807 | tempDict["pages-querylines"][commonqueryline] = querylineDict["common-querylines"][commonqueryline]; 1808 | tempDict["blocks-querylines"][commonqueryline] = querylineDict["common-querylines"][commonqueryline]; 1809 | } 1810 | return tempDict; 1811 | } 1812 | 1813 | 1814 | 1815 | function getQueryLineSegment(querylinekey) { 1816 | var errormsg, errortext; 1817 | 1818 | try { 1819 | return ["ok", querylineDBDict[querygroup][querylinekey]["segment"]]; 1820 | } catch (e) { 1821 | errortext = querylinekey + " segment value not found in QueryDB\n"; 1822 | errormsg = ";; **ERROR: " + errortext + "\n"; 1823 | return ["error", errormsg]; 1824 | } 1825 | } 1826 | 1827 | 1828 | function getQueryLine(querylinekey, querysegment) { 1829 | var errormsg; 1830 | 1831 | try { 1832 | if (showcommandcomments === true) { 1833 | var comment = getQueryLineComment(querylinekey) 1834 | if (comment != '') { 1835 | if (!query[querysegment].includes('\n' + comment)) { 1836 | query[querysegment].push("\n" + getQueryLineComment(querylinekey)); 1837 | } 1838 | } 1839 | } 1840 | 1841 | return querylineDBDict[querygroup][querylinekey]["datalog"]; 1842 | } catch (e) { 1843 | errormsg = "\n---- COMMAND ERROR ----\n"; 1844 | errormsg += querylinekey + " not found in QueryDB or invalid usage\n"; 1845 | 1846 | if (querygroup === "pages-querylines") { 1847 | errormsg += querylinekey + " invalid within a 'pages' command query"; 1848 | } 1849 | 1850 | if (querygroup === "blocks-querylines") { 1851 | errormsg += querylinekey + " invalid within a 'blocks' command query"; 1852 | } 1853 | 1854 | errormsg += "\n----------------------\n"; 1855 | return errormsg; 1856 | } 1857 | } 1858 | 1859 | function getQueryLineComment(querylinekey) { 1860 | var comment, separator; 1861 | separator = '' 1862 | try { 1863 | comment = querylineDBDict[querygroup][querylinekey]["comment"]; 1864 | if (comment == '') { 1865 | return '' 1866 | } 1867 | return separator + ";; ---- " + comment; 1868 | } catch (e) { 1869 | return querylinekey + " not found in queryline Dictionary"; 1870 | } 1871 | } 1872 | 1873 | function getCommandQueryLineKeys(command) { 1874 | var commandlist; 1875 | 1876 | if (command === null) { 1877 | return null; 1878 | } 1879 | 1880 | try { 1881 | commandlist = commandsDict[command]["querylines"]; 1882 | } catch (e) { 1883 | return null; 1884 | } 1885 | 1886 | return commandlist; 1887 | } 1888 | 1889 | 1890 | function processCommand(command, commandLinesDict) { 1891 | var commandline, commandvalidity, negativecommandlines, originalcommandlines, positivecommandlines, queryline, querylinekeys, querysegment, querysegmentdata, querysegmentresponse; 1892 | commandvalidity = checkCommandValid(command); 1893 | 1894 | if (commandvalidity[0] === false) { 1895 | query["errors"].push(commandvalidity[1]); 1896 | return; 1897 | } 1898 | 1899 | querylinekeys = getCommandQueryLineKeys(command); 1900 | 1901 | if (querylinekeys === null) { 1902 | return [command + " is not a valid command"]; 1903 | } 1904 | 1905 | for (value of querylinekeys) { 1906 | querysegmentresponse = getQueryLineSegment(value); 1907 | 1908 | querysegmentdata = querysegmentresponse[1]; 1909 | 1910 | if (querysegmentresponse[0] === "error") { 1911 | query["errors"].push(querysegmentdata); 1912 | continue; 1913 | } 1914 | 1915 | querysegment = querysegmentdata; 1916 | queryline = getQueryLine(value, querysegment); 1917 | 1918 | if (query[querysegment].includes(queryline)) { 1919 | continue; 1920 | } 1921 | 1922 | query[querysegment].push(queryline); 1923 | } 1924 | 1925 | // sort the argument lines by positive and negative 1926 | positivecommandlines = []; 1927 | negativecommandlines = []; 1928 | originalcommandlines = commandLinesDict[command]["commandlines"].slice(1); 1929 | 1930 | 1931 | for (commandline of originalcommandlines) { 1932 | if (commandline.trim().substring(2).startsWith("not ") || commandline.trim().substring(2).startsWith("and ")) { 1933 | commandline = commandline.replace("and ", ""); 1934 | negativecommandlines.push(commandline); 1935 | } else { 1936 | if (commandline.trim().substring(2).startsWith("or ")) { 1937 | commandline = commandline.replace("or ", ""); 1938 | positivecommandlines.push(commandline); 1939 | } else { 1940 | positivecommandlines.push(commandline); 1941 | } 1942 | } 1943 | } 1944 | 1945 | if (positivecommandlines.length > 0) { 1946 | processCommandLines("include", command, positivecommandlines); 1947 | } 1948 | 1949 | if (negativecommandlines.length > 0) { 1950 | processCommandLines("exclude", command, negativecommandlines); 1951 | } 1952 | 1953 | return; 1954 | } 1955 | 1956 | function checkCommandValid(command) { 1957 | var commandvalidity, errormessage; 1958 | commandvalidity = true; 1959 | errormessage = ""; 1960 | 1961 | if (querygroup === "pages-querylines") { 1962 | if (command === "blocktags") { 1963 | commandvalidity = false; 1964 | errormessage = "\n;; **ERROR: " + command + " not valid with pages command use blocks command instead\n"; 1965 | } else { 1966 | if (command === "tasks" || command === "pagelinks") { 1967 | commandvalidity = false; 1968 | errormessage = "\n;; **ERROR: " + command + " not valid with pages command use blocks command instead\n"; 1969 | } 1970 | } 1971 | } else { 1972 | if (querygroup === "blocks-querylines") { 1973 | if ([].includes(command)) { 1974 | commandvalidity = false; 1975 | } 1976 | } 1977 | } 1978 | 1979 | return [commandvalidity, errormessage]; 1980 | } 1981 | 1982 | function addQueryLines(command, prefix, querylinekey, arg) { 1983 | var arg1, arg2, args, querysegment, querysegmentdata, querysegmentresponse, updatedqueryline; 1984 | querysegment = getQueryLineSegment(querylinekey); 1985 | querysegmentresponse = getQueryLineSegment(querylinekey)[0]; 1986 | querysegmentdata = getQueryLineSegment(querylinekey)[1]; 1987 | 1988 | if (querysegmentresponse === "error") { 1989 | query["errors"].push(querysegmentdata); 1990 | return; 1991 | } 1992 | 1993 | querysegment = querysegmentdata; 1994 | if (command == 'pagelinks') { 1995 | args = [] 1996 | args.push(arg) 1997 | } else { 1998 | args = arg.split(","); 1999 | } 2000 | 2001 | if (args.length === 1) { 2002 | querylinekey = prefix + querylinekey; 2003 | updatedqueryline = getQueryLine(querylinekey, querysegment).replace("$$ARG1", arg); 2004 | 2005 | if (!query[querysegment].includes(updatedqueryline)) { 2006 | query[querysegment].push(updatedqueryline); 2007 | } 2008 | } else { 2009 | if (args.length === 2) { 2010 | arg1 = args[0].trim(); 2011 | arg2 = args[1].trim(); 2012 | querylinekey = prefix + querylinekey; 2013 | updatedqueryline = getQueryLine(querylinekey, querysegment).replace("$$ARG1", arg1); 2014 | updatedqueryline = updatedqueryline.replace("$$ARG2", arg2); 2015 | 2016 | if (!query[querysegment].includes(updatedqueryline)) { 2017 | query[querysegment].push(updatedqueryline); 2018 | } 2019 | } else { 2020 | query[querysegment].push(command + " => Invalid line => " + arg); 2021 | } 2022 | } 2023 | } 2024 | 2025 | function processCommandLines(action, command, commandlines) { 2026 | var arg, args, firstline, lastline, prefix; 2027 | 2028 | if (commandlines === []) { 2029 | return; 2030 | } 2031 | 2032 | firstline = ""; 2033 | lastline = ""; 2034 | 2035 | if (["pages", "blocks", "blocktags", "pagetags", "pagelinks", "tasks", "pageproperties", "blockproperties", "namespace"].includes(command)) { 2036 | if (commandlines.length > 1) { 2037 | if (action === "include") { 2038 | firstline = "( or "; 2039 | lastline = ")"; 2040 | } 2041 | } 2042 | } 2043 | 2044 | if (firstline !== "") { 2045 | query["filters"].push(firstline); 2046 | } 2047 | 2048 | for (arg of commandlines) { 2049 | arg = arg.trim().substring(2); 2050 | 2051 | if (arg === "") { 2052 | continue; 2053 | } 2054 | 2055 | if (arg === "*") { 2056 | continue; 2057 | } 2058 | 2059 | prefix = ""; 2060 | 2061 | if (arg.startsWith("not ")) { 2062 | prefix = "not_"; 2063 | arg = arg.substring(prefix.length); 2064 | } 2065 | 2066 | if (command === "pages") { 2067 | if (arg[0] !== "*" && arg[arg.length - 1] === "*") { 2068 | addQueryLines(command, prefix, "arg_pagename_startswith", arg.substring(0, arg.length - 1)); 2069 | continue; 2070 | } 2071 | 2072 | if (arg[0] === "*" && arg[arg.length - 1] !== "*") { 2073 | addQueryLines(command, prefix, "arg_pagename_endswith", arg.substring(1)); 2074 | continue; 2075 | } 2076 | 2077 | if (arg[0] === "*" && arg[arg.length - 1] === "*") { 2078 | addQueryLines(command, prefix, "arg_pagename_contains", arg.substring(1, arg.length - 1)); 2079 | continue; 2080 | } 2081 | 2082 | addQueryLines(command, prefix, "pagename_is", arg); 2083 | } 2084 | 2085 | if (command === "blocks") { 2086 | if (arg[0] !== "*" && arg[arg.length - 1] === "*") { 2087 | addQueryLines(command, prefix, "arg_blockcontent_startswith", arg.substring(0, arg.length - 1)); 2088 | continue; 2089 | } 2090 | 2091 | if (arg[0] === "*" && arg[arg.length - 1] !== "*") { 2092 | addQueryLines(command, prefix, "arg_blockcontent_endswith", arg.substring(1)); 2093 | continue; 2094 | } 2095 | 2096 | if (arg[0] === "*" && arg[arg.length - 1] === "*") { 2097 | addQueryLines(command, prefix, "arg_blockcontent_contains", arg.substring(1, arg.length - 1)); 2098 | continue; 2099 | } 2100 | } 2101 | 2102 | if (command === "pagetags") { 2103 | args = arg.split(); 2104 | 2105 | for (arg of args) { 2106 | addQueryLines(command, prefix, "pagetags_are", arg.toLowerCase()); 2107 | } 2108 | } 2109 | 2110 | if (command === "blocktags") { 2111 | args = arg.split(); 2112 | 2113 | for (arg of args) { 2114 | addQueryLines(command, prefix, "blocktags_are", arg.toLowerCase()); 2115 | } 2116 | } 2117 | 2118 | if (command === "pageproperties") { 2119 | addQueryLines(command, prefix, "page_properties_are", arg); 2120 | } 2121 | 2122 | if (command === "blockproperties") { 2123 | addQueryLines(command, prefix, "block_properties_are", arg); 2124 | } 2125 | 2126 | if (command === "pagelinks") { 2127 | args = arg.split(); 2128 | 2129 | for (arg of args) { 2130 | addQueryLines(command, prefix, "pagelinks_are", arg.toLowerCase()); 2131 | } 2132 | } 2133 | if (command === "tasks") { 2134 | addQueryLines(command, prefix, "tasks_are", arg); 2135 | } 2136 | 2137 | if (command === "namespace") { 2138 | addQueryLines(command, prefix, "namespace", arg); 2139 | } 2140 | 2141 | if (command === "scheduled") { 2142 | addQueryLines(command, prefix, "scheduled", arg); 2143 | } 2144 | 2145 | if (command === "scheduledbetween") { 2146 | addQueryLines(command, prefix, "scheduledbetween", arg); 2147 | } 2148 | 2149 | if (command === "deadline") { 2150 | addQueryLines(command, prefix, "deadline", arg); 2151 | } 2152 | 2153 | if (command === "deadlinebetween") { 2154 | addQueryLines(command, prefix, "deadlinebetween", arg); 2155 | } 2156 | 2157 | if (command === "journalonly") { 2158 | addQueryLines(command, prefix, "page_is_journal", arg); 2159 | } 2160 | 2161 | if (command === "journalsbetween") { 2162 | addQueryLines(command, prefix, "journalsbetween", arg); 2163 | } 2164 | 2165 | if (command === "daterange") { 2166 | addQueryLines(command, prefix, "daterange", arg); 2167 | } 2168 | 2169 | if (command === "collapse") { 2170 | addQueryLines(command, prefix, "collapse", arg); 2171 | } 2172 | 2173 | if (command === "collapse") { 2174 | addQueryLines(command, prefix, "expand", arg); 2175 | } 2176 | } 2177 | 2178 | if (lastline !== "") { 2179 | query["filters"].push(lastline); 2180 | } 2181 | 2182 | return; 2183 | } 2184 | 2185 | function insertQueryLineIntoSegment(key) { 2186 | var querysegment, querysegmentdata, querysegmentresponse; 2187 | querysegmentresponse = getQueryLineSegment(key)[0]; 2188 | querysegmentdata = getQueryLineSegment(key)[1]; 2189 | 2190 | if (querysegmentresponse === "error") { 2191 | query["errors"].push(querysegmentdata); 2192 | return; 2193 | } 2194 | 2195 | querysegment = querysegmentdata; 2196 | query[key].push(getQueryLine(key, querysegment)); 2197 | } 2198 | 2199 | function checkUsingPagesorBlocks(commandlines) { 2200 | var blocksfound, commandline, pagesfound 2201 | pagesfound = false; 2202 | blocksfound = false; 2203 | 2204 | for (commandline of commandlines) { 2205 | 2206 | commandline = commandline.trim(); 2207 | 2208 | if (commandline.startsWith("- pages")) { 2209 | pagesfound = true; 2210 | } 2211 | 2212 | if (commandline.startsWith("- blocks")) { 2213 | blocksfound = true; 2214 | } 2215 | } 2216 | 2217 | if (pagesfound === true && blocksfound === false) { 2218 | querygroup = "pages-querylines"; 2219 | return; 2220 | } 2221 | 2222 | if (blocksfound === true && pagesfound === false) { 2223 | querygroup = "blocks-querylines"; 2224 | return; 2225 | } 2226 | 2227 | if (pagesfound === false && blocksfound === false) { 2228 | query["errors"].push(";; WARNING: Must have 'pages' command or 'blocks' Command\n;; otherwise the query cannot get any information\n;; Inserting a blocks command for you\n"); 2229 | insertBlocksCommand(commandlines); 2230 | blocksfound = true; 2231 | querygroup = "blocks-querylines"; 2232 | return; 2233 | } 2234 | 2235 | if (pagesfound && blocksfound) { 2236 | query["errors"].push(";; ERROR: Cannot have 'pages' command and 'blocks' command together in a command list\n\n"); 2237 | return; 2238 | } 2239 | } 2240 | 2241 | function insertBlocksCommand(commandlines) { 2242 | if (commandlines.length > 0) { 2243 | if (commandlines[0].indexOf("title:") > -1) { 2244 | commandlines.splice(1, 0, " - *"); 2245 | commandlines.splice(1, 0, "- blocks"); 2246 | } 2247 | } else { 2248 | commandlines.splice(0, 0, "- blocks\n - *\n"); 2249 | } 2250 | return; 2251 | } 2252 | 2253 | function validCommand(command) { 2254 | try { 2255 | if (commandsDict[command]) { 2256 | return true; 2257 | } else { 2258 | return false 2259 | } 2260 | } catch (e) { 2261 | return false; 2262 | } 2263 | } 2264 | 2265 | function processCommandList(commandlists) { 2266 | var commandLinesDict, commandlines, commandname, currentcommand, fields, query; 2267 | query = initialiseQuery(); 2268 | commandlines = commandlists.split("\n"); 2269 | checkUsingPagesorBlocks(commandlines); 2270 | currentcommand = ""; 2271 | commandLinesDict = {}; 2272 | 2273 | for (line of commandlines) { 2274 | 2275 | if (line === "" || line.startsWith(";;")) { 2276 | continue; 2277 | } 2278 | 2279 | if (line.trim().startsWith("title:")) { 2280 | query["title"].push(getQueryLine("title", "title").replace("$$ARG1", line.split(":")[1].trim())); 2281 | continue; 2282 | } 2283 | 2284 | if (line.trim().startsWith("option:")) { 2285 | var option = line.split(":")[1].trim() 2286 | if (option == "includecomments") { 2287 | setshowcommandcomments(true) 2288 | } else { 2289 | query['errors'].push( 2290 | ";; WARNING: '" + line + "' is not valid option. \n;; Valid options: includecomments") 2291 | } 2292 | continue; 2293 | } 2294 | 2295 | if (line.startsWith("- ")) { 2296 | fields = line.split(" "); 2297 | commandname = fields[1]; 2298 | 2299 | if (validCommand(commandname)) { 2300 | commandLinesDict[commandname] = {}; 2301 | 2302 | if (currentcommand === "" || line !== currentcommand) { 2303 | currentcommand = commandname; 2304 | commandLinesDict[commandname]["commandlines"] = []; 2305 | commandLinesDict[commandname]["commandlines"].push(line); 2306 | continue; 2307 | } 2308 | } else { 2309 | query["errors"].push(";; WARNING: '" + line + "' is not valid command.\n;; Either a mispelt command or no leading dash"); 2310 | // continue; 2311 | } 2312 | } else { 2313 | if (line.startsWith(" ") && line.trim().startsWith("- ")) { 2314 | if (currentcommand === "") { 2315 | query["errors"].push(";; ERROR: '" + line + "' is a command argument but does not have a parent command\n;; Either a command is missing (or invalid) or this should be an argument line"); 2316 | } else { 2317 | if (validCommand(commandname)) { 2318 | commandLinesDict[commandname]["commandlines"].push(line); 2319 | } else { 2320 | query["errors"].push(";; ERROR: '" + line + "' is a command argument but does not have a parent command\n;; Either a command is missing (or invalid) or this should be an argument line"); 2321 | } 2322 | } 2323 | } else { 2324 | if (line.includes("title ")) { 2325 | query["errors"].push(";; WARNING: title line should start with title:"); 2326 | } else { 2327 | if (!line.trim().startsWith("- ") && !(line.indexOf("title:") > -1)) { 2328 | query["errors"].push(";; WARNING: " + line + " has no leading hypen eg '- pages'"); 2329 | } 2330 | } 2331 | } 2332 | } 2333 | } 2334 | 2335 | insertQueryLineIntoSegment("start"); 2336 | insertQueryLineIntoSegment("open"); 2337 | insertQueryLineIntoSegment("where"); 2338 | insertQueryLineIntoSegment("closefind"); 2339 | insertQueryLineIntoSegment("closequery"); 2340 | insertQueryLineIntoSegment("end"); 2341 | 2342 | for (command in commandLinesDict) { 2343 | processCommand(command, commandLinesDict); 2344 | } 2345 | 2346 | query["closefind"] = [getQueryLine("closefind", "closefind")]; 2347 | query["closequery"] = [getQueryLine("closequery", "closequery")]; 2348 | query["end"] = [getQueryLine("end", "end")]; 2349 | return; 2350 | } 2351 | 2352 | 2353 | function constructQuery() { 2354 | var advancedquery; 2355 | advancedquery = ""; 2356 | 2357 | for (const [key, value] of Object.entries(query)) { 2358 | for (let j = 0; j < query[key].length; j++) { 2359 | queryline = query[key][j] 2360 | advancedquery += queryline + "\n"; 2361 | } 2362 | } 2363 | return advancedquery; 2364 | } 2365 | 2366 | function printGeneratedAdvancedQuery(advancedquery) { 2367 | var msg, prefix, suffix; 2368 | 2369 | if (codeblock) { 2370 | prefix = "```clojure\n"; 2371 | suffix = "```"; 2372 | } else { 2373 | prefix = ""; 2374 | suffix = ""; 2375 | } 2376 | 2377 | if (mode === "website") { 2378 | // msg = prefix + advancedquery.replace("\n", "
") + suffix; 2379 | msg = prefix + advancedquery + suffix; 2380 | websitePrintToDiv('advanced_query', msg) 2381 | } else { 2382 | console.log("----------------------------"); 2383 | console.log("Logseq Advanced Query"); 2384 | console.log("----------------------------"); 2385 | console.log(prefix + advancedquery + suffix); 2386 | } 2387 | } 2388 | 2389 | 2390 | // global value functions 2391 | function getquerygroup() { 2392 | return querygroup 2393 | } 2394 | 2395 | function setquerygroup(value) { 2396 | querygroup = value 2397 | } 2398 | 2399 | function getshowcommandcomments() { 2400 | return showcommandcomments 2401 | } 2402 | 2403 | function setshowcommandcomments(value) { 2404 | showcommandcomments = value 2405 | } 2406 | 2407 | function getcodeblock() { 2408 | return codeblock 2409 | } 2410 | 2411 | function setcodeblock(value) { 2412 | codeblock = value 2413 | } 2414 | 2415 | function getquerylineDBDict() { 2416 | return querylineDBDict 2417 | } 2418 | 2419 | function setquerylineDBDict(value) { 2420 | querylineDBDict = value 2421 | } 2422 | 2423 | // Test queryTestDB works 2424 | function test_queryTestDBRead() { 2425 | testcases = QueryTestCases 2426 | return testcases[0]; 2427 | } 2428 | 2429 | function getquerytestcases() { 2430 | return QueryTestCases 2431 | } 2432 | 2433 | function removeLastGeneratedQuery(content) { 2434 | let lines = content.split("\n") 2435 | let newcontent = '' 2436 | for (const line of lines) { 2437 | if (line.startsWith('#+BEGIN_QUERY')) { 2438 | break 2439 | } 2440 | newcontent += line + '\n' 2441 | } 2442 | return newcontent 2443 | } 2444 | 2445 | function showErrors() { 2446 | msg = 'QB: Errors Found - check built query' 2447 | if (query["errors"].length > 0) { 2448 | for (errormsg of query["errors"]) { 2449 | msg += errormsg + '\n' 2450 | } 2451 | return msg 2452 | } else { 2453 | return "QB: Query Built OK" 2454 | } 2455 | } 2456 | 2457 | 2458 | function main() { 2459 | 2460 | logseq.Editor.registerBlockContextMenuItem( 2461 | 'Advanced Query Builder', 2462 | async (e) => { 2463 | const block = await logseq.Editor.getBlock(e.uuid) 2464 | content = block.content 2465 | content = removeLastGeneratedQuery(content) 2466 | commands = content.replaceAll(/```/g, '') 2467 | showcommandcomments = false; 2468 | querygroup = "blocks-querylines"; 2469 | codeblock = false; 2470 | // query = initialiseQuery(); 2471 | // querylineDBDict = initialisteQueryLineDict(); 2472 | // querygroup = "pages-querylines" 2473 | // logseq.App.showMsg( 2474 | // commands, 2475 | // ) 2476 | setquerylineDBDict(initialisteQueryLineDict()) 2477 | console.log("\ncommands\n" + commands); 2478 | console.log("\ncontent\n" + content); 2479 | initialiseQuery() 2480 | processCommandList(commands) 2481 | advancedquery = constructQuery() 2482 | 2483 | // Currently will add a child block with the generated query 2484 | // user can right click on the query and remove it or leave it there 2485 | // IDEA: Maybe always remove the last query so how do I do that 2486 | // remove any children blocks 2487 | // TODO: Returns undefined for the child uuid????? Mayne just let user insert a new child for each query execution 2488 | // if (block.children.length > 0) { 2489 | // for (let child of block.children) { 2490 | // await logseq.Editor.removeBlock(child.uuid); 2491 | // } 2492 | // } 2493 | 2494 | 2495 | // place the advanced query in a child of the current block (that has the commands in it) 2496 | await logseq.Editor.insertBlock(e.uuid, advancedquery, { sibling: false }) 2497 | 2498 | logseq.App.showMsg( 2499 | showErrors() 2500 | ) 2501 | }) 2502 | 2503 | } // end main 2504 | 2505 | // ======== website functions 2506 | 2507 | function websiteInitialise() { 2508 | 2509 | if (mode != "website") { 2510 | return 2511 | } 2512 | 2513 | // connect the generate advanced query button 2514 | generate_query_button = document.getElementById('generate_query_button') 2515 | generate_query_button.addEventListener("click", websiteQueryBuild) 2516 | 2517 | // connect the Clear Commands button 2518 | clear_commands_button = document.getElementById('clear_commands_button') 2519 | clear_commands_button.addEventListener("click", websiteClearCommands) 2520 | 2521 | // connect the Examples button 2522 | examples_options = document.getElementById('command_examples') 2523 | examples_options.addEventListener("input", websiteChooseExample) 2524 | examples_options.value = "" // set to first option 2525 | 2526 | // connect the Command Comments Checkbox 2527 | command_comments_checkbox = document.getElementById( 2528 | 'command_comments_checkbox') 2529 | command_comments_checkbox.addEventListener("click", websiteCommandComments) 2530 | command_comments_checkbox.checked = false 2531 | 2532 | commands_input = document.getElementById('commands_input') 2533 | commands_input.value = '' 2534 | 2535 | // connect the Code Block Output Checkbox 2536 | codeblock_checkbox = document.getElementById( 2537 | 'codeblock_checkbox') 2538 | codeblock_checkbox.addEventListener("click", websiteCodeBlock) 2539 | codeblock_checkbox.checked = false 2540 | 2541 | } 2542 | 2543 | function websiteClearCommands(event) { 2544 | if (mode != "website") { 2545 | return 2546 | } 2547 | 2548 | // # hide copy to clipboard button 2549 | websitePrintToDiv('print_output', 'Clear Commands Button Pressed') 2550 | commands_input = document.getElementById('commands_input') 2551 | commands_input.value = '' 2552 | } 2553 | 2554 | 2555 | function websiteCommandComments(event) { 2556 | if (mode != "website") { 2557 | return 2558 | } 2559 | if (document.getElementById('command_comments_checkbox').checked == true) { 2560 | setshowcommandcomments(true) 2561 | } else { 2562 | setshowcommandcomments(false) 2563 | } 2564 | } 2565 | 2566 | 2567 | function websiteCodeBlock(event) { 2568 | // TODO: Check this works re global codeblock variable 2569 | if (mode != "website") { 2570 | return 2571 | } 2572 | if (document.getElementById('codeblock_checkbox').checked == true) { 2573 | codeblock = true 2574 | } else { 2575 | codeblock = false 2576 | } 2577 | } 2578 | 2579 | 2580 | function websiteChooseExample(event) { 2581 | if (mode != "website") { 2582 | return 2583 | } 2584 | // get selected Example and fill the commands Input Text Area 2585 | examples_options = document.getElementById('command_examples') 2586 | if (examples_options.value != "Choose Example..") { 2587 | advanced_query_text = document.getElementById('advanced_query') 2588 | advanced_query_text.textContent = '' 2589 | websitePrintToDiv('print_output', 2590 | "Example selected .. now press 'Generate Advanced Query' button") 2591 | 2592 | document.getElementById( 2593 | 'commands_input').value = examples_options.value 2594 | // console.log('value is ', examples_options.value) 2595 | } 2596 | } 2597 | 2598 | function websiteAdvancedQueryText(event) { 2599 | if (mode != "website") { 2600 | return 2601 | } 2602 | } 2603 | 2604 | function websitePrintToDiv(divname, text) { 2605 | if (mode != "website") { 2606 | return 2607 | } 2608 | document.getElementById(divname).innerText = text 2609 | } 2610 | 2611 | 2612 | function websiteQueryBuild(event) { 2613 | if (mode != "website") { 2614 | return 2615 | } 2616 | 2617 | // # hide copy to clipboard button 2618 | copy_button = document.getElementById('copy') 2619 | copy_button.setAttribute("hidden", "hidden") 2620 | 2621 | websitePrintToDiv('print_output', 'Processing Commands ..') 2622 | 2623 | commands_input = document.getElementById('commands_input') 2624 | if (!commands_input) { 2625 | websitePrintToDiv('print_output', 'Bug: Element is None') 2626 | return 2627 | } 2628 | processCommandList(commands_input.value) 2629 | advancedquery = constructQuery() 2630 | printGeneratedAdvancedQuery(advancedquery) 2631 | 2632 | // show copy to clipboard button 2633 | var copy_button = document.getElementById('copy') 2634 | hidden = copy_button.getAttribute("hidden") 2635 | copy_button.removeAttribute("hidden") 2636 | 2637 | websitePrintToDiv('print_output', 2638 | "Advanced Query Generated!\n- Tick 'Include Query Comments' if desired\n- Tick 'Copy as code block' if desired\nClick 'Copy Query to Clipboard") 2639 | 2640 | return 2641 | } 2642 | 2643 | 2644 | 2645 | // MAIN ENTRY POINT 2646 | 2647 | // ******************************* 2648 | // LOCAL MODE TESTING WITH JEST TESTING 2649 | // (Comment out WEBSITE MODE section below and PLUGIN MODE section above) 2650 | // (Uncomment this section for local testing with JEST) 2651 | // ******************************* 2652 | // var mode = "local" 2653 | // module.exports = { 2654 | // //querygroup, 2655 | // //showcommandcomments, 2656 | // getquerygroup, 2657 | // setquerygroup, 2658 | // getshowcommandcomments, 2659 | // setshowcommandcomments, 2660 | // getcodeblock, 2661 | // setcodeblock, 2662 | // getquerylineDBDict, 2663 | // setquerylineDBDict, 2664 | // getquerytestcases, 2665 | // add, 2666 | // test_queryDBRead, 2667 | // test_queryTestDBRead, 2668 | // addQueryLines, 2669 | // checkCommandValid, 2670 | // checkUsingPagesorBlocks, 2671 | // constructQuery, 2672 | // getCommandQueryLineKeys, 2673 | // getQueryLine, 2674 | // getQueryLineComment, 2675 | // getQueryLineSegment, 2676 | // initialiseQuery, 2677 | // initialisteQueryLineDict, 2678 | // insertBlocksCommand, 2679 | // insertQueryLineIntoSegment, 2680 | // printGeneratedAdvancedQuery, 2681 | // processCommand, 2682 | // processCommandLines, 2683 | // processCommandList, 2684 | // removeLastGeneratedQuery, 2685 | // validCommand 2686 | // } 2687 | // ******************************* 2688 | 2689 | // ******************************* 2690 | // LOGSEQ WEBSITE MODE 2691 | // (Comment out LOCAL MODE section above and PLUGIN mode section below and uncomment this section) 2692 | // ******************************* 2693 | // mode = "website" 2694 | // ******************************* 2695 | 2696 | 2697 | // ******************************* 2698 | // LOGSEQ PLUGIN MODE 2699 | // (Comment out LOCAL MODE section above and WEBSITE section above and uncomment this section) 2700 | // ******************************* 2701 | mode = "logseq-plugin" 2702 | // ******************************* 2703 | 2704 | // MAIN STARTING POINT 2705 | if (mode == "logseq-plugin") { 2706 | console.log('logseq-advanced-query-builder plugin loaded') 2707 | logseq.ready(main).catch(console.error) 2708 | } 2709 | if (mode == "local") { 2710 | console.log('logseq-advanced-query-builder code running locally') 2711 | querygroup = ""; 2712 | codeblock = false; 2713 | query = initialiseQuery(); 2714 | querylineDBDict = initialisteQueryLineDict(); 2715 | } 2716 | if (mode == "website") { 2717 | console.log('logseq-advanced-query-builder code running in browser') 2718 | querygroup = ""; 2719 | codeblock = false; 2720 | query = initialiseQuery(); 2721 | querylineDBDict = initialisteQueryLineDict(); 2722 | websiteInitialise() 2723 | } 2724 | 2725 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logseq-query-builder-plugin", 3 | "version": "0.4", 4 | "main": "index.html", 5 | "logseq": { 6 | "id": "logseq-query-builder-plugin", 7 | "title": "logseq-query-builder", 8 | "icon": "./icon.png" 9 | } 10 | } -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* theme colors */ 2 | 3 | :root { 4 | 5 | 6 | /* Dark theme */ 7 | --dark-bg: #00384d; 8 | --dark-fg: #b0e1f4c3; 9 | 10 | /* Light theme */ 11 | --light-bg: #d1f0f995; 12 | --light-fg: rgb(59, 44, 44); 13 | 14 | /* Defaults 15 | 16 | For a dark theme use these lines 17 | --current-bg: var(--dark-bg); 18 | --current-fg: var(--dark-fg); 19 | 20 | For a light theme use these lines 21 | --current-bg: var(--light-bg); 22 | --current-fg: var(--light-fg); 23 | 24 | */ 25 | 26 | --current-bg: var(--light-bg); 27 | --current-fg: var(--light-fg); 28 | } 29 | 30 | 31 | /* clear all padding, margins */ 32 | * { 33 | padding: 0; 34 | margin: 0; 35 | box-sizing: border-box; 36 | } 37 | 38 | html { 39 | font-size: 14px; 40 | } 41 | 42 | body { 43 | padding: 1rem; 44 | font-family: 'Roboto', sans-serif; 45 | background-color: var(--current-bg); 46 | color: var(--current-fg); 47 | } 48 | 49 | a { 50 | background-color: var(--current-bg); 51 | color: var(--current-fg); 52 | 53 | } 54 | 55 | 56 | .title { 57 | margin-bottom: 2rem; 58 | font-size: 2rem; 59 | } 60 | 61 | h3 { 62 | font-family: 'Roboto', sans-serif; 63 | font-size: 1.1rem; 64 | color: var(--current-fg); 65 | } 66 | 67 | h5 { 68 | font-family: 'Roboto', sans-serif; 69 | font-size: 0.9rem; 70 | color: var(--current-fg); 71 | } 72 | 73 | 74 | hr { 75 | margin: 1rem 0; 76 | } 77 | 78 | /* Details/Summary Indentation levels */ 79 | 80 | .level1 { 81 | /* list-style: disc; */ 82 | background-color: var(--current-bg); 83 | color: var(--current-fg); 84 | padding: rem; 85 | font-size: 1rem; 86 | cursor: pointer; 87 | } 88 | 89 | .level2 { 90 | /* list-style: disc; */ 91 | background-color: var(--current-bg); 92 | color: var(--current-fg); 93 | padding: rem; 94 | font-size: 1rem; 95 | cursor: pointer; 96 | margin-left: 1rem; 97 | } 98 | 99 | .level3 { 100 | /* list-style: disc; */ 101 | background-color: var(--current-bg); 102 | color: var(--current-fg); 103 | padding: rem; 104 | font-size: 2rem; 105 | cursor: pointer; 106 | margin-left: 1rem; 107 | } 108 | 109 | .details-content { 110 | background-color: var(--current-bg); 111 | color: var(--current-fg); 112 | padding: 1rem; 113 | font-size: 0.8rem; 114 | overflow-wrap: break-word; 115 | cursor: pointer; 116 | } 117 | 118 | button { 119 | font-size: 0.9rem; 120 | padding: 3px 3px; 121 | background-color: rgb(215, 210, 206); 122 | border-radius: 6px; 123 | } 124 | 125 | select { 126 | font-size: 0.8rem; 127 | padding: 1px 1px; 128 | width: 25rem; 129 | background-color: #00384d; 130 | color: white; 131 | text-align: center; 132 | } 133 | 134 | textarea { 135 | color: rgb(7, 7, 0); 136 | width: 25rem; 137 | background-color: rgb(243, 239, 136); 138 | font-size: 0.8rem; 139 | font-weight: bold; 140 | font-family: 'Courier New', Courier, monospace; 141 | padding: 1px 1px; 142 | text-align: left; 143 | } 144 | 145 | .generatedquery { 146 | color: black; 147 | width: 25rem; 148 | background-color: rgb(203, 241, 165); 149 | font-size: 0.8rem; 150 | font-weight: bold; 151 | font-family: 'Courier New', Courier, monospace; 152 | text-align: left; 153 | } 154 | 155 | 156 | 157 | .checkbox { 158 | font-size: 0.8rem; 159 | } 160 | 161 | .statusbar { 162 | font-size: 0.9rem; 163 | padding: 4px 1px; 164 | background-color: var(--current-bg); 165 | color: var(--current-fg); 166 | 167 | text-align: left; 168 | border: 1; 169 | 170 | } 171 | 172 | blockquote { 173 | background-color: var(--current-bg); 174 | color: var(--current-fg); 175 | text-align: left; 176 | } 177 | 178 | table { 179 | background-color: var(--current-bg); 180 | color: var(--current-fg); 181 | border-collapse: collapse; 182 | border: 1px solid var(--current-fg); 183 | } 184 | 185 | th { 186 | padding: 2px; 187 | font-size: 1.8rem; 188 | border: 1px solid var(--current-fg); 189 | } 190 | 191 | td { 192 | padding: 2px; 193 | font-size: 0.8rem; 194 | border: 1px solid var(--current-fg); 195 | } 196 | 197 | /* zebra striping tbody tr:nth-child(odd) { 198 | background-color: var(--current-bg); 199 | color: var(--current-fg); 200 | 201 | } 202 | 203 | tbody tr:nth-child(even) { 204 | background-color: var(--current-bg); 205 | color: var(--current-fg); 206 | } */ 207 | 208 | ul { 209 | list-style-type: disc !important; 210 | padding-left: 1em; 211 | } --------------------------------------------------------------------------------