Compare commits
	
		
			14 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 706111f8ba | |||
| b0a56bec64 | |||
| 5ee8ea9b60 | |||
| 215cef976f | |||
| ceb90c2bfa | |||
| b5223b1053 | |||
| a9cb7a58a3 | |||
| c81ff7a441 | |||
| 9131fb9a42 | |||
| 0909c8e9cc | |||
| 60e2764a1b | |||
| d77b91e27a | |||
|  | 11484146e9 | ||
|  | 7c10a5e993 | 
							
								
								
									
										1
									
								
								.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | {} | ||||||
							
								
								
									
										94
									
								
								DATA.md
									
									
									
									
									
								
							
							
						
						
									
										94
									
								
								DATA.md
									
									
									
									
									
								
							| @ -1,12 +1,10 @@ | |||||||
| Data | # Data | ||||||
| ==== |  | ||||||
| 
 | 
 | ||||||
| Every template gets an object with the exact same structure - whether it's a template or a widget or a page or a post. | Every template gets an object with the exact same structure - whether it's a template or a widget or a page or a post. | ||||||
| 
 | 
 | ||||||
| Here we only document Desirae's default behavior, but there are many objects added for compatibility with Ruhoh that are not documented. | Here we only document Desirae's default behavior, but there are many objects added for compatibility with Ruhoh that are not documented. | ||||||
| 
 | 
 | ||||||
| config.yml vs site.yml | ## config.yml vs site.yml | ||||||
| ------ |  | ||||||
| 
 | 
 | ||||||
| site.yml is for anything that changes the content of the site (navigation, title, analytic and ad ids, default author, etc) | site.yml is for anything that changes the content of the site (navigation, title, analytic and ad ids, default author, etc) | ||||||
| 
 | 
 | ||||||
| @ -16,66 +14,60 @@ config.yml is for anything that doesn't change the site (from where to read dire | |||||||
| desi = {} | desi = {} | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| desi | # desi | ||||||
| ==== |  | ||||||
| 
 | 
 | ||||||
| * `config` - literally `config.yml`, parsed | - `config` - literally `config.yml`, parsed | ||||||
| * `site` - literally `site.yml`, parsed | - `site` - literally `site.yml`, parsed | ||||||
| * `authors` - literally the authors from `authors/*.yml`, parsed | - `authors` - literally the authors from `authors/*.yml`, parsed | ||||||
| * `author` - the primary author of the site | - `author` - the primary author of the site | ||||||
| * `env` - urls and paths for this build (be it production, development, staging, etc) | - `env` - urls and paths for this build (be it production, development, staging, etc) | ||||||
| * `content` - pre-rendered content (i.e. content rendered into the post layout rendered into the default layout) | - `content` - pre-rendered content (i.e. content rendered into the post layout rendered into the default layout) | ||||||
| * `collection` - config related to this collection | - `collection` - config related to this collection | ||||||
| * `entity` - the page, post, article, etc that is the focus of the present template process | - `entity` - the page, post, article, etc that is the focus of the present template process | ||||||
| * `themes` - all themes | - `themes` - all themes | ||||||
| * `theme` - the default theme | - `theme` - the default theme | ||||||
| * `layout` - the selected layout for this theme | - `layout` - the selected layout for this theme | ||||||
| * `satch` - the selected swatch for this theme | - `satch` - the selected swatch for this theme | ||||||
| * `categories` - all categories | - `categories` - all categories | ||||||
| * `tags` - all tags | - `tags` - all tags | ||||||
| * `styles` - ??? goes into the final template in the head | - `styles` - ??? goes into the final template in the head | ||||||
| * `scripts` - ?? that goes into the final template just before the body close | - `scripts` - ?? that goes into the final template just before the body close | ||||||
| 
 | 
 | ||||||
| desi.entity | # desi.entity | ||||||
| =========== |  | ||||||
| 
 | 
 | ||||||
| stuff | stuff | ||||||
| 
 | 
 | ||||||
| * `uuid` | - `uuid` | ||||||
| * `title` | - `title` | ||||||
| * `disqus_url` | - `disqus_url` | ||||||
| * `disqus_identifier` | - `disqus_identifier` | ||||||
| 
 | 
 | ||||||
| more stuff | more stuff | ||||||
| 
 | 
 | ||||||
| * `type` - `post`, `page`, etc | - `type` - `post`, `page`, etc | ||||||
| * `authors` - literally the relevant authors from `authors/*.yml`, parsed | - `authors` - literally the relevant authors from `authors/*.yml`, parsed | ||||||
| * `author` - the primary author of this entity | - `author` - the primary author of this entity | ||||||
| * `theme` - null or a non-default theme | - `theme` - null or a non-default theme | ||||||
| * `layout` - null or a non-default layout for this theme | - `layout` - null or a non-default layout for this theme | ||||||
| * `swatch` - null or a non-default swatch for this theme | - `swatch` - null or a non-default swatch for this theme | ||||||
| * `categories`: []    // *all* categories in all collections | - `categories`: [] // _all_ categories in all collections | ||||||
| * `tags`: []    // *all* categories in all collections | - `tags`: [] // _all_ categories in all collections | ||||||
| * `production_canonical_url` the PRODUCTION canonical_url for this entity | - `production_canonical_url` the PRODUCTION canonical_url for this entity | ||||||
| * `production_url` the PRODUCTION url for this entity | - `production_url` the PRODUCTION url for this entity | ||||||
| * `production_path` the PRODUCTION path for this entity | - `production_path` the PRODUCTION path for this entity | ||||||
| * `url` the full url in the current environment (might be production, development, etc) | - `url` the full url in the current environment (might be production, development, etc) | ||||||
| * `path` the non-host part (i.e. `/compiled_dev/articles/my-first-post.html`) | - `path` the non-host part (i.e. `/compiled_dev/articles/my-first-post.html`) | ||||||
| * `previous` the previous entity in this collection | - `previous` the previous entity in this collection | ||||||
| * `next` the next entitiy in this collection | - `next` the next entitiy in this collection | ||||||
| 
 | 
 | ||||||
| NOTE: Plugins, widgets, etc SHOULD NOT modify config, site, authors, author, or env. | NOTE: Plugins, widgets, etc SHOULD NOT modify config, site, authors, author, or env. | ||||||
| 
 | 
 | ||||||
| desi.posts | # desi.posts | ||||||
| ========== |  | ||||||
| 
 | 
 | ||||||
|       , posts: { collated: desi.collated } |       , posts: { collated: desi.collated } | ||||||
| 
 | 
 | ||||||
| desi.config | # desi.config | ||||||
| =========== |  | ||||||
| 
 | 
 | ||||||
| desi.site | # desi.site | ||||||
| =========== |  | ||||||
| 
 | 
 | ||||||
| desi.env | # desi.env | ||||||
| =========== |  | ||||||
|  | |||||||
							
								
								
									
										84
									
								
								ENTITY.md
									
									
									
									
									
								
							
							
						
						
									
										84
									
								
								ENTITY.md
									
									
									
									
									
								
							| @ -2,61 +2,65 @@ This is what an entity looks like: | |||||||
| 
 | 
 | ||||||
| ```yml | ```yml | ||||||
| # inherited from File Entity | # inherited from File Entity | ||||||
| path              : My Posts/My-Old-Name.html | path: My Posts/My-Old-Name.html | ||||||
| lastModifiedDate  : 2015-07-04T13:56:01Z | lastModifiedDate: 2015-07-04T13:56:01Z | ||||||
| createdDate       : 2015-07-04T13:56:01Z | createdDate: 2015-07-04T13:56:01Z | ||||||
| contents          : '...'           # whatever the file is | contents: "..." # whatever the file is | ||||||
| 
 | 
 | ||||||
| # inherited from Collection Entity | # inherited from Collection Entity | ||||||
| name              : My-Old-Name.html | name: My-Old-Name.html | ||||||
| relativePath      : My Posts | relativePath: My Posts | ||||||
| ext               : .html | ext: .html | ||||||
| collection        : posts | collection: posts | ||||||
| 
 | 
 | ||||||
| # inherited from Content Entity | # inherited from Content Entity | ||||||
| frontmatter       : '---\n...\n---' # frontmatter as a string | frontmatter: '---\n...\n---' # frontmatter as a string | ||||||
| yml               : {}              # frontmatter, parsed | yml: {} # frontmatter, parsed | ||||||
| body              : 'I think ...'   # body, after frontmatter | body: "I think ..." # body, after frontmatter | ||||||
| 
 | 
 | ||||||
| # inherited from Normalized Entity | # inherited from Normalized Entity | ||||||
| title             : My Title        # yml.title | titlize(entity.name) | title: My Title # yml.title | titlize(entity.name) | ||||||
| slug              : my-title        # slugify(title) | slug: my-title # slugify(title) | ||||||
| slug_path         : my-posts        # slugifyPath(relativePath) | slug_path: my-posts # slugifyPath(relativePath) | ||||||
| 
 | 
 | ||||||
| year              : 2014 | year: 2014 | ||||||
| month             : 07 | month: 07 | ||||||
| day               : 04 | day: 04 | ||||||
| hour              : 13 | hour: 13 | ||||||
| twelve_hour       : 1 | twelve_hour: 1 | ||||||
| meridian          : pm | meridian: pm | ||||||
| minute            : 22 | minute: 22 | ||||||
| 
 | 
 | ||||||
| categories        : ['tech'] | categories: ["tech"] | ||||||
| tags              : ['http','url','website'] | tags: | ||||||
|  |   ["http", "url", "website"] | ||||||
| 
 | 
 | ||||||
|                     # includes index.html |   # includes index.html | ||||||
| relative_file     : /posts/foo/index.html | relative_file: | ||||||
|  |   /posts/foo/index.html | ||||||
| 
 | 
 | ||||||
|                     # excludes index.html |   # excludes index.html | ||||||
| relative_href     : /posts/foo/ | relative_href: | ||||||
|  |   /posts/foo/ | ||||||
| 
 | 
 | ||||||
|                     # actual url of this file, even if redirect |   # actual url of this file, even if redirect | ||||||
|                     # excludes index.html |   # excludes index.html | ||||||
| url               : http://dev.example.com/posts/foo/ | url: | ||||||
|  |   http://dev.example.com/posts/foo/ | ||||||
| 
 | 
 | ||||||
|                     # the appropriate url, even in a redirect or duplicate |   # the appropriate url, even in a redirect or duplicate | ||||||
|                     # excludes index.html |   # excludes index.html | ||||||
| canonical_url     : http://dev.example.com/posts/foo/ | canonical_url: | ||||||
|  |   http://dev.example.com/posts/foo/ | ||||||
| 
 | 
 | ||||||
|                     # production url, even in development (for disqus, etc) |   # production url, even in development (for disqus, etc) | ||||||
|                     # excludes index.html |   # excludes index.html | ||||||
| production_url    : http://example.com/posts/foo/ | production_url: http://example.com/posts/foo/ | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Note: The option `env.explicitIndexes` turns on `/index.html`. This option is automatically turned on when Dropbox is the host. | Note: The option `env.explicitIndexes` turns on `/index.html`. This option is automatically turned on when Dropbox is the host. | ||||||
| 
 | 
 | ||||||
| TODO | ## TODO | ||||||
| ---- |  | ||||||
| 
 | 
 | ||||||
| * path relative from / in the browser | - path relative from / in the browser | ||||||
| * path relative from base_path on the file system | - path relative from base_path on the file system | ||||||
|  | |||||||
							
								
								
									
										32
									
								
								GLOSSARY.md
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								GLOSSARY.md
									
									
									
									
									
								
							| @ -1,14 +1,12 @@ | |||||||
| Glossary | # Glossary | ||||||
| ======== |  | ||||||
| 
 | 
 | ||||||
| Canonical URL | ## Canonical URL | ||||||
| -------- |  | ||||||
| base\_url + base\_path + permalink |  | ||||||
| 
 | 
 | ||||||
| Base URL | base_url + base_path + permalink | ||||||
| ---- |  | ||||||
| 
 | 
 | ||||||
| base\_url is the point of ownership | ## Base URL | ||||||
|  | 
 | ||||||
|  | base_url is the point of ownership | ||||||
| 
 | 
 | ||||||
| In most cases that would be https://johndoe.com | In most cases that would be https://johndoe.com | ||||||
| 
 | 
 | ||||||
| @ -16,27 +14,25 @@ In some cases that might be https://school.edu/~/johndoe | |||||||
| 
 | 
 | ||||||
| It does NOT include a trailing / | It does NOT include a trailing / | ||||||
| 
 | 
 | ||||||
| Base Path | ## Base Path | ||||||
| ----- |  | ||||||
| 
 | 
 | ||||||
| base\_path is the blog directory | base_path is the blog directory | ||||||
| 
 | 
 | ||||||
| In most cases that would be / or /blog/ | In most cases that would be / or /blog/ | ||||||
| 
 | 
 | ||||||
| It DOES include BOTH a LEADING and TRAILING slash. | It DOES include BOTH a LEADING and TRAILING slash. | ||||||
| 
 | 
 | ||||||
| In the case of https://school.edu/~/johndoe/weblog, the base\_path would  be /weblog/. | In the case of https://school.edu/~/johndoe/weblog, the base_path would be /weblog/. | ||||||
| 
 | 
 | ||||||
| Permalink | ## Permalink | ||||||
| ------ |  | ||||||
| 
 | 
 | ||||||
| The permalink is the permanent part of the URL, after the path to the blog. | The permalink is the permanent part of the URL, after the path to the blog. | ||||||
| 
 | 
 | ||||||
| For example: | For example: | ||||||
| 
 | 
 | ||||||
|   * http://blog.johndoe.com/articles/first-post.html the permalink is articles/first-post.html | - http://blog.johndoe.com/articles/first-post.html the permalink is articles/first-post.html | ||||||
|   * http://johndoe.com/blog/articles/first-post.html the permalink is still articles/first-post.html | - http://johndoe.com/blog/articles/first-post.html the permalink is still articles/first-post.html | ||||||
|   * http://school.edu/~/johndoe/blog/articles/first-post.html the permalink is yet still articles/first-post.html | - http://school.edu/~/johndoe/blog/articles/first-post.html the permalink is yet still articles/first-post.html | ||||||
| 
 | 
 | ||||||
| The permalink is ALWAYS RELATIVE (no leading slash) | The permalink is ALWAYS RELATIVE (no leading slash) | ||||||
| 
 | 
 | ||||||
| @ -44,5 +40,5 @@ It is designed so that if you ever move your blog from one domain, point of owne | |||||||
| a very simple one-line redirect can be made to your webserver and all of the posts will end up in the right place | a very simple one-line redirect can be made to your webserver and all of the posts will end up in the right place | ||||||
| once again. | once again. | ||||||
| 
 | 
 | ||||||
| base\_url  the | base_url the | ||||||
| permalink refers to | permalink refers to | ||||||
|  | |||||||
							
								
								
									
										206
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										206
									
								
								LICENSE
									
									
									
									
									
								
							| @ -1,202 +1,4 @@ | |||||||
| Apache License | This Source Code Form is subject to the terms of the Mozilla | ||||||
|                            Version 2.0, January 2004 | Public License, v. 2.0. If a copy of the MPL was not distributed | ||||||
|                         http://www.apache.org/licenses/ | with this file, You can obtain one at | ||||||
| 
 | https://mozilla.org/MPL/2.0/. | ||||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |  | ||||||
| 
 |  | ||||||
|    1. Definitions. |  | ||||||
| 
 |  | ||||||
|       "License" shall mean the terms and conditions for use, reproduction, |  | ||||||
|       and distribution as defined by Sections 1 through 9 of this document. |  | ||||||
| 
 |  | ||||||
|       "Licensor" shall mean the copyright owner or entity authorized by |  | ||||||
|       the copyright owner that is granting the License. |  | ||||||
| 
 |  | ||||||
|       "Legal Entity" shall mean the union of the acting entity and all |  | ||||||
|       other entities that control, are controlled by, or are under common |  | ||||||
|       control with that entity. For the purposes of this definition, |  | ||||||
|       "control" means (i) the power, direct or indirect, to cause the |  | ||||||
|       direction or management of such entity, whether by contract or |  | ||||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the |  | ||||||
|       outstanding shares, or (iii) beneficial ownership of such entity. |  | ||||||
| 
 |  | ||||||
|       "You" (or "Your") shall mean an individual or Legal Entity |  | ||||||
|       exercising permissions granted by this License. |  | ||||||
| 
 |  | ||||||
|       "Source" form shall mean the preferred form for making modifications, |  | ||||||
|       including but not limited to software source code, documentation |  | ||||||
|       source, and configuration files. |  | ||||||
| 
 |  | ||||||
|       "Object" form shall mean any form resulting from mechanical |  | ||||||
|       transformation or translation of a Source form, including but |  | ||||||
|       not limited to compiled object code, generated documentation, |  | ||||||
|       and conversions to other media types. |  | ||||||
| 
 |  | ||||||
|       "Work" shall mean the work of authorship, whether in Source or |  | ||||||
|       Object form, made available under the License, as indicated by a |  | ||||||
|       copyright notice that is included in or attached to the work |  | ||||||
|       (an example is provided in the Appendix below). |  | ||||||
| 
 |  | ||||||
|       "Derivative Works" shall mean any work, whether in Source or Object |  | ||||||
|       form, that is based on (or derived from) the Work and for which the |  | ||||||
|       editorial revisions, annotations, elaborations, or other modifications |  | ||||||
|       represent, as a whole, an original work of authorship. For the purposes |  | ||||||
|       of this License, Derivative Works shall not include works that remain |  | ||||||
|       separable from, or merely link (or bind by name) to the interfaces of, |  | ||||||
|       the Work and Derivative Works thereof. |  | ||||||
| 
 |  | ||||||
|       "Contribution" shall mean any work of authorship, including |  | ||||||
|       the original version of the Work and any modifications or additions |  | ||||||
|       to that Work or Derivative Works thereof, that is intentionally |  | ||||||
|       submitted to Licensor for inclusion in the Work by the copyright owner |  | ||||||
|       or by an individual or Legal Entity authorized to submit on behalf of |  | ||||||
|       the copyright owner. For the purposes of this definition, "submitted" |  | ||||||
|       means any form of electronic, verbal, or written communication sent |  | ||||||
|       to the Licensor or its representatives, including but not limited to |  | ||||||
|       communication on electronic mailing lists, source code control systems, |  | ||||||
|       and issue tracking systems that are managed by, or on behalf of, the |  | ||||||
|       Licensor for the purpose of discussing and improving the Work, but |  | ||||||
|       excluding communication that is conspicuously marked or otherwise |  | ||||||
|       designated in writing by the copyright owner as "Not a Contribution." |  | ||||||
| 
 |  | ||||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity |  | ||||||
|       on behalf of whom a Contribution has been received by Licensor and |  | ||||||
|       subsequently incorporated within the Work. |  | ||||||
| 
 |  | ||||||
|    2. Grant of Copyright License. Subject to the terms and conditions of |  | ||||||
|       this License, each Contributor hereby grants to You a perpetual, |  | ||||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable |  | ||||||
|       copyright license to reproduce, prepare Derivative Works of, |  | ||||||
|       publicly display, publicly perform, sublicense, and distribute the |  | ||||||
|       Work and such Derivative Works in Source or Object form. |  | ||||||
| 
 |  | ||||||
|    3. Grant of Patent License. Subject to the terms and conditions of |  | ||||||
|       this License, each Contributor hereby grants to You a perpetual, |  | ||||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable |  | ||||||
|       (except as stated in this section) patent license to make, have made, |  | ||||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, |  | ||||||
|       where such license applies only to those patent claims licensable |  | ||||||
|       by such Contributor that are necessarily infringed by their |  | ||||||
|       Contribution(s) alone or by combination of their Contribution(s) |  | ||||||
|       with the Work to which such Contribution(s) was submitted. If You |  | ||||||
|       institute patent litigation against any entity (including a |  | ||||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work |  | ||||||
|       or a Contribution incorporated within the Work constitutes direct |  | ||||||
|       or contributory patent infringement, then any patent licenses |  | ||||||
|       granted to You under this License for that Work shall terminate |  | ||||||
|       as of the date such litigation is filed. |  | ||||||
| 
 |  | ||||||
|    4. Redistribution. You may reproduce and distribute copies of the |  | ||||||
|       Work or Derivative Works thereof in any medium, with or without |  | ||||||
|       modifications, and in Source or Object form, provided that You |  | ||||||
|       meet the following conditions: |  | ||||||
| 
 |  | ||||||
|       (a) You must give any other recipients of the Work or |  | ||||||
|           Derivative Works a copy of this License; and |  | ||||||
| 
 |  | ||||||
|       (b) You must cause any modified files to carry prominent notices |  | ||||||
|           stating that You changed the files; and |  | ||||||
| 
 |  | ||||||
|       (c) You must retain, in the Source form of any Derivative Works |  | ||||||
|           that You distribute, all copyright, patent, trademark, and |  | ||||||
|           attribution notices from the Source form of the Work, |  | ||||||
|           excluding those notices that do not pertain to any part of |  | ||||||
|           the Derivative Works; and |  | ||||||
| 
 |  | ||||||
|       (d) If the Work includes a "NOTICE" text file as part of its |  | ||||||
|           distribution, then any Derivative Works that You distribute must |  | ||||||
|           include a readable copy of the attribution notices contained |  | ||||||
|           within such NOTICE file, excluding those notices that do not |  | ||||||
|           pertain to any part of the Derivative Works, in at least one |  | ||||||
|           of the following places: within a NOTICE text file distributed |  | ||||||
|           as part of the Derivative Works; within the Source form or |  | ||||||
|           documentation, if provided along with the Derivative Works; or, |  | ||||||
|           within a display generated by the Derivative Works, if and |  | ||||||
|           wherever such third-party notices normally appear. The contents |  | ||||||
|           of the NOTICE file are for informational purposes only and |  | ||||||
|           do not modify the License. You may add Your own attribution |  | ||||||
|           notices within Derivative Works that You distribute, alongside |  | ||||||
|           or as an addendum to the NOTICE text from the Work, provided |  | ||||||
|           that such additional attribution notices cannot be construed |  | ||||||
|           as modifying the License. |  | ||||||
| 
 |  | ||||||
|       You may add Your own copyright statement to Your modifications and |  | ||||||
|       may provide additional or different license terms and conditions |  | ||||||
|       for use, reproduction, or distribution of Your modifications, or |  | ||||||
|       for any such Derivative Works as a whole, provided Your use, |  | ||||||
|       reproduction, and distribution of the Work otherwise complies with |  | ||||||
|       the conditions stated in this License. |  | ||||||
| 
 |  | ||||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, |  | ||||||
|       any Contribution intentionally submitted for inclusion in the Work |  | ||||||
|       by You to the Licensor shall be under the terms and conditions of |  | ||||||
|       this License, without any additional terms or conditions. |  | ||||||
|       Notwithstanding the above, nothing herein shall supersede or modify |  | ||||||
|       the terms of any separate license agreement you may have executed |  | ||||||
|       with Licensor regarding such Contributions. |  | ||||||
| 
 |  | ||||||
|    6. Trademarks. This License does not grant permission to use the trade |  | ||||||
|       names, trademarks, service marks, or product names of the Licensor, |  | ||||||
|       except as required for reasonable and customary use in describing the |  | ||||||
|       origin of the Work and reproducing the content of the NOTICE file. |  | ||||||
| 
 |  | ||||||
|    7. Disclaimer of Warranty. Unless required by applicable law or |  | ||||||
|       agreed to in writing, Licensor provides the Work (and each |  | ||||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, |  | ||||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |  | ||||||
|       implied, including, without limitation, any warranties or conditions |  | ||||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |  | ||||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the |  | ||||||
|       appropriateness of using or redistributing the Work and assume any |  | ||||||
|       risks associated with Your exercise of permissions under this License. |  | ||||||
| 
 |  | ||||||
|    8. Limitation of Liability. In no event and under no legal theory, |  | ||||||
|       whether in tort (including negligence), contract, or otherwise, |  | ||||||
|       unless required by applicable law (such as deliberate and grossly |  | ||||||
|       negligent acts) or agreed to in writing, shall any Contributor be |  | ||||||
|       liable to You for damages, including any direct, indirect, special, |  | ||||||
|       incidental, or consequential damages of any character arising as a |  | ||||||
|       result of this License or out of the use or inability to use the |  | ||||||
|       Work (including but not limited to damages for loss of goodwill, |  | ||||||
|       work stoppage, computer failure or malfunction, or any and all |  | ||||||
|       other commercial damages or losses), even if such Contributor |  | ||||||
|       has been advised of the possibility of such damages. |  | ||||||
| 
 |  | ||||||
|    9. Accepting Warranty or Additional Liability. While redistributing |  | ||||||
|       the Work or Derivative Works thereof, You may choose to offer, |  | ||||||
|       and charge a fee for, acceptance of support, warranty, indemnity, |  | ||||||
|       or other liability obligations and/or rights consistent with this |  | ||||||
|       License. However, in accepting such obligations, You may act only |  | ||||||
|       on Your own behalf and on Your sole responsibility, not on behalf |  | ||||||
|       of any other Contributor, and only if You agree to indemnify, |  | ||||||
|       defend, and hold each Contributor harmless for any liability |  | ||||||
|       incurred by, or claims asserted against, such Contributor by reason |  | ||||||
|       of your accepting any such warranty or additional liability. |  | ||||||
| 
 |  | ||||||
|    END OF TERMS AND CONDITIONS |  | ||||||
| 
 |  | ||||||
|    APPENDIX: How to apply the Apache License to your work. |  | ||||||
| 
 |  | ||||||
|       To apply the Apache License to your work, attach the following |  | ||||||
|       boilerplate notice, with the fields enclosed by brackets "{}" |  | ||||||
|       replaced with your own identifying information. (Don't include |  | ||||||
|       the brackets!)  The text should be enclosed in the appropriate |  | ||||||
|       comment syntax for the file format. We also recommend that a |  | ||||||
|       file or class name and description of purpose be included on the |  | ||||||
|       same "printed page" as the copyright notice for easier |  | ||||||
|       identification within third-party archives. |  | ||||||
| 
 |  | ||||||
|    Copyright {yyyy} {name of copyright owner} |  | ||||||
| 
 |  | ||||||
|    Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|    you may not use this file except in compliance with the License. |  | ||||||
|    You may obtain a copy of the License at |  | ||||||
| 
 |  | ||||||
|        http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
| 
 |  | ||||||
|    Unless required by applicable law or agreed to in writing, software |  | ||||||
|    distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|    See the License for the specific language governing permissions and |  | ||||||
|    limitations under the License. |  | ||||||
| 
 |  | ||||||
|  | |||||||
							
								
								
									
										206
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										206
									
								
								README.md
									
									
									
									
									
								
							| @ -1,5 +1,4 @@ | |||||||
| Did you mean DearDesi? | # Did you mean DearDesi? | ||||||
| ====================== |  | ||||||
| 
 | 
 | ||||||
| If you're looking for [DearDesi](http://dear.desi), the DIY blog platform for normal people | If you're looking for [DearDesi](http://dear.desi), the DIY blog platform for normal people | ||||||
| you should go to <http://dear.desi>. | you should go to <http://dear.desi>. | ||||||
| @ -7,8 +6,7 @@ you should go to <http://dear.desi>. | |||||||
| Desirae (this repo) is the code that programmers to use to make DearDesi better | Desirae (this repo) is the code that programmers to use to make DearDesi better | ||||||
| for everyone and to make Desirae-compatible blog platforms. (it's blog-ception!) | for everyone and to make Desirae-compatible blog platforms. (it's blog-ception!) | ||||||
| 
 | 
 | ||||||
| Desirae (v0.9) | # Desirae (v0.9) | ||||||
| ======= |  | ||||||
| 
 | 
 | ||||||
| Desirae is a static webpage compiler written in JavaScript. | Desirae is a static webpage compiler written in JavaScript. | ||||||
| 
 | 
 | ||||||
| @ -19,18 +17,17 @@ It can also run entirely from the commandline (with io.js / node.js). | |||||||
| 
 | 
 | ||||||
| **Features:** | **Features:** | ||||||
| 
 | 
 | ||||||
|   * `JavaScript` - it's so stable it takes 10 years to get new any features. | - `JavaScript` - it's so stable it takes 10 years to get new any features. | ||||||
|     * Won't break every time you upgrade OS X and reinstall `brew` (*cough* ruby) |   - Won't break every time you upgrade OS X and reinstall `brew` (_cough_ ruby) | ||||||
|   * Decent use of `try { ... } catch(e) ...` and `promise.catch()` | - Decent use of `try { ... } catch(e) ...` and `promise.catch()` | ||||||
|     * the idea is that it shouldn't blow up at the slightest parse error without telling you which page is to blame (*cough* ruhoh *cough* jekyll)... bless me |   - the idea is that it shouldn't blow up at the slightest parse error without telling you which page is to blame (_cough_ ruhoh _cough_ jekyll)... bless me | ||||||
|   * Browser (optional) | - Browser (optional) | ||||||
|     * using your front-end templates to build in your front-end? Imagine that! |   - using your front-end templates to build in your front-end? Imagine that! | ||||||
|   * io.js (node.js) (optional) | - io.js (node.js) (optional) | ||||||
|     * if you'd prefer to go headless, you can. |   - if you'd prefer to go headless, you can. | ||||||
|   * The server is *very* minimal and could easily be implemented in any language (such as ruby or python). | - The server is _very_ minimal and could easily be implemented in any language (such as ruby or python). | ||||||
| 
 | 
 | ||||||
| Installation | # Installation | ||||||
| ============ |  | ||||||
| 
 | 
 | ||||||
| ```bash | ```bash | ||||||
| bower install --save desirae | bower install --save desirae | ||||||
| @ -38,8 +35,7 @@ bower install --save desirae | |||||||
| npm install --save desirae | npm install --save desirae | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Why | # Why | ||||||
| === |  | ||||||
| 
 | 
 | ||||||
| Because I hate ruby. | Because I hate ruby. | ||||||
| 
 | 
 | ||||||
| @ -51,8 +47,7 @@ more (last year I made dozens of gists and 0 blog posts) and I just couldn't get | |||||||
| versions and gems and whatnot... so I gave up and wrote my own (because I needed something | versions and gems and whatnot... so I gave up and wrote my own (because I needed something | ||||||
| compatible with ruhoh). | compatible with ruhoh). | ||||||
| 
 | 
 | ||||||
| Usage Overview | # Usage Overview | ||||||
| ============== |  | ||||||
| 
 | 
 | ||||||
| ### Before we get started | ### Before we get started | ||||||
| 
 | 
 | ||||||
| @ -70,11 +65,10 @@ It's an artifact of this project being born out of the ashes of my | |||||||
| 
 | 
 | ||||||
| ### Getting Started | ### Getting Started | ||||||
| 
 | 
 | ||||||
| First off you need to declare a state object that will be used in every *desirae* action. | First off you need to declare a state object that will be used in every _desirae_ action. | ||||||
| 
 | 
 | ||||||
| ```javascript | ```javascript | ||||||
| var desi = {} | var desi = {}; | ||||||
|   ; |  | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| After that you'll load any plugins you need. | After that you'll load any plugins you need. | ||||||
| @ -156,26 +150,24 @@ Desi.registerRenderer( | |||||||
| ); | ); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| And then you'll initialize Desirae with an *environment*. | And then you'll initialize Desirae with an _environment_. | ||||||
| 
 | 
 | ||||||
| ```javascript | ```javascript | ||||||
| Desirae.init( | Desirae.init(desi, { | ||||||
|   desi |   url: "https://johndoe.exmaple.com/blog", | ||||||
| , { url:                'https://johndoe.exmaple.com/blog' |   base_url: "https://johndoe.exmaple.com", | ||||||
|   , base_url:           'https://johndoe.exmaple.com' |   base_path: "/blog", | ||||||
|   , base_path:          '/blog' |   compiled_path: "compiled_dev", | ||||||
|   , compiled_path:      'compiled_dev' |  | ||||||
| 
 | 
 | ||||||
|                         // default: continue when possible |   // default: continue when possible | ||||||
|   , onError:            function (e) { |   onError: function (e) { | ||||||
|                           return Promise.reject(e); |     return Promise.reject(e); | ||||||
|                         } |   }, | ||||||
| 
 | 
 | ||||||
|                         // io.js / node.js only |   // io.js / node.js only | ||||||
|   , working_path:       './path/to/blog' |   working_path: "./path/to/blog", | ||||||
|   } | }).then(function () { | ||||||
| ).then(function () { |   console.log("Desirae is initialized"); | ||||||
|   console.log('Desirae is initialized'); |  | ||||||
| }); | }); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| @ -198,43 +190,38 @@ Desirae.write(desi, env).then(function () { | |||||||
| }); | }); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| ### Plugins | ### Plugins | ||||||
| 
 | 
 | ||||||
| You need to start every file with a wrapper that is browser and io.js/node.js compatible | You need to start every file with a wrapper that is browser and io.js/node.js compatible | ||||||
| 
 | 
 | ||||||
| ```javascript | ```javascript | ||||||
| /*jshint -W054 */ | /*jshint -W054 */ | ||||||
| ;(function (exports) { | (function (exports) { | ||||||
|   'use strict'; |   "use strict"; | ||||||
|   |  | ||||||
|   var DesiraeMyModule = {} |  | ||||||
|     ; |  | ||||||
| 
 | 
 | ||||||
|  |   var DesiraeMyModule = {}; | ||||||
|   // ... a bunch of code ... |   // ... a bunch of code ... | ||||||
| 
 | 
 | ||||||
|   DesiraeMyModule.doStuff = doStuff; |   DesiraeMyModule.doStuff = doStuff; | ||||||
| 
 | 
 | ||||||
|   exports.DesiraeMyModule = DesiraeMyModule.DesiraeMyModule = DesiraeMyModule; |   exports.DesiraeMyModule = DesiraeMyModule.DesiraeMyModule = DesiraeMyModule; | ||||||
| }('undefined' !== typeof exports && exports || window)); | })(("undefined" !== typeof exports && exports) || window); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Other than that, just be mindful that your code needs to run in both iojs/node and browser environments | Other than that, just be mindful that your code needs to run in both iojs/node and browser environments | ||||||
| so steer away from things that are super iojs/node-ish or super window-ish. | so steer away from things that are super iojs/node-ish or super window-ish. | ||||||
| 
 | 
 | ||||||
| Configuration | # Configuration | ||||||
| ============= |  | ||||||
| 
 | 
 | ||||||
| There are a few configuration files: | There are a few configuration files: | ||||||
| 
 | 
 | ||||||
| * `site.yml` is stuff that might be unique to your site, such as (title, url, adwords id, etc) | - `site.yml` is stuff that might be unique to your site, such as (title, url, adwords id, etc) | ||||||
| * `authors/<<your-handle.yml>>` contains information about you (name, handle, facebook, etc) | - `authors/<<your-handle.yml>>` contains information about you (name, handle, facebook, etc) | ||||||
| * `config.yml` contains directives that describe *how* the blog should be compiled - more technical stuff. | - `config.yml` contains directives that describe _how_ the blog should be compiled - more technical stuff. | ||||||
| 
 | 
 | ||||||
| If any of these files change, the entire site needs to be retemplated. | If any of these files change, the entire site needs to be retemplated. | ||||||
| 
 | 
 | ||||||
| API | # API | ||||||
| === |  | ||||||
| 
 | 
 | ||||||
| I'd like to make the plugin system connect-style API for adding plugins or whatever so that, | I'd like to make the plugin system connect-style API for adding plugins or whatever so that, | ||||||
| for example, so you could have a custom markdown preprocessor (that handles includes, perhaps) | for example, so you could have a custom markdown preprocessor (that handles includes, perhaps) | ||||||
| @ -246,28 +233,26 @@ But here's what I've got so far: | |||||||
| 
 | 
 | ||||||
| ### (html, markdown, jade) | ### (html, markdown, jade) | ||||||
| 
 | 
 | ||||||
| * `Desirae.registerRenderer(ext, fn)` | - `Desirae.registerRenderer(ext, fn)` | ||||||
| 
 | 
 | ||||||
| For example, if you want to add the ability to render from `slim` or `haml` | For example, if you want to add the ability to render from `slim` or `haml` | ||||||
| instead of just `markdown` you could find the appropriate | instead of just `markdown` you could find the appropriate | ||||||
| JavaScript module (or make requests to an API service that renders them for you) and do this | JavaScript module (or make requests to an API service that renders them for you) and do this | ||||||
| 
 | 
 | ||||||
| ```javascript | ```javascript | ||||||
| var slim = exports.slimjs || require('slimjs') | var slim = exports.slimjs || require("slimjs"); | ||||||
|   ; | function render(contentstr /*, desi*/) { | ||||||
| 
 |   return Promise.resolve(slim(contentstr)); | ||||||
| function render(contentstr/*, desi*/) { |  | ||||||
|   return PromiseA.resolve(slim(contentstr)); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Desirae.registerRenderer('.slim', render); | Desirae.registerRenderer(".slim", render); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Data Mapping | ## Data Mapping | ||||||
| 
 | 
 | ||||||
| ### (desirae, ruhoh, etc) | ### (desirae, ruhoh, etc) | ||||||
| 
 | 
 | ||||||
| * `Desirae.registerDataMapper(ext, fn)` | - `Desirae.registerDataMapper(ext, fn)` | ||||||
| 
 | 
 | ||||||
| If you want to use a non-desirae theme that uses attributes in a different than | If you want to use a non-desirae theme that uses attributes in a different than | ||||||
| how Desirae creates the view object internally | how Desirae creates the view object internally | ||||||
| @ -276,17 +261,17 @@ data mapper to accomplish this. | |||||||
| 
 | 
 | ||||||
| Please try not to modify the original object if you can avoid it. | Please try not to modify the original object if you can avoid it. | ||||||
| 
 | 
 | ||||||
| *TODO: maybe pass in a two-level deep shallow copy ?* | _TODO: maybe pass in a two-level deep shallow copy ?_ | ||||||
| 
 | 
 | ||||||
| ```javascript | ```javascript | ||||||
| Desirae.registerDataMapper('ruhoh@3.0', function (view) { | Desirae.registerDataMapper("ruhoh@3.0", function (view) { | ||||||
|   return { |   return { | ||||||
|     page: { |     page: { | ||||||
|       name: view.entity.title |       name: view.entity.title, | ||||||
|     } |     }, | ||||||
|   , author: { |     author: { | ||||||
|       nickname: view.author.twitter |       nickname: view.author.twitter, | ||||||
|     } |     }, | ||||||
|     // ... |     // ... | ||||||
|   }; |   }; | ||||||
| }); | }); | ||||||
| @ -305,54 +290,54 @@ I'd love to work with anyone who is familiar with the Dropbox or similar APIs. | |||||||
| 
 | 
 | ||||||
| I think it would be awesome to support various means of storage. Perhaps github gists too. | I think it would be awesome to support various means of storage. Perhaps github gists too. | ||||||
| 
 | 
 | ||||||
| 
 | # Server | ||||||
| Server |  | ||||||
| ====== |  | ||||||
| 
 | 
 | ||||||
| Obviously there has to be a server with some sort of storage and retrieval mechanism. | Obviously there has to be a server with some sort of storage and retrieval mechanism. | ||||||
| 
 | 
 | ||||||
| I've implemented a very simple node server using the filesystem. | I've implemented a very simple node server using the filesystem. | ||||||
| 
 | 
 | ||||||
| GET /api/fs/walk | ## GET /api/fs/walk | ||||||
| ------------ |  | ||||||
| 
 | 
 | ||||||
| `GET http://local.dear.desi:8080/api/fs/walk?dir=posts&dotfiles=true&extensions=md,markdown,jade,htm,html` | `GET http://local.dear.desi:8080/api/fs/walk?dir=posts&dotfiles=true&extensions=md,markdown,jade,htm,html` | ||||||
| 
 | 
 | ||||||
| * `dir` **must** be supplied. returns a flat list of all files, recursively | - `dir` **must** be supplied. returns a flat list of all files, recursively | ||||||
| * `dotfiles` default to `false`. includes dotfiles when `true`. | - `dotfiles` default to `false`. includes dotfiles when `true`. | ||||||
| * `extensions` defaults to `null`. inclode **only** the supplied extensions when `true`. | - `extensions` defaults to `null`. inclode **only** the supplied extensions when `true`. | ||||||
| 
 | 
 | ||||||
| ```json | ```json | ||||||
| [ | [ | ||||||
|   { "name": "happy-new-year.md" |   { | ||||||
|   , "createdDate": "2015-01-05T18:19:30.000Z" |     "name": "happy-new-year.md", | ||||||
|   , "lastModifiedDate": "2015-01-05T18:19:30.000Z" |     "createdDate": "2015-01-05T18:19:30.000Z", | ||||||
|   , "size": 2121 |     "lastModifiedDate": "2015-01-05T18:19:30.000Z", | ||||||
|   , "relativePath": "posts/2015" |     "size": 2121, | ||||||
|   } |     "relativePath": "posts/2015" | ||||||
|  |   }, | ||||||
| 
 | 
 | ||||||
| , { "name": "tips-for-the-ages.jade" |   { | ||||||
|   , "createdDate": "2014-06-16T18:19:30.000Z" |     "name": "tips-for-the-ages.jade", | ||||||
|   , "lastModifiedDate": "2014-06-16T18:19:30.000Z" |     "createdDate": "2014-06-16T18:19:30.000Z", | ||||||
|   , "size": 389 |     "lastModifiedDate": "2014-06-16T18:19:30.000Z", | ||||||
|   , "relativePath": "posts" |     "size": 389, | ||||||
|   } |     "relativePath": "posts" | ||||||
| , { "name": "my-first-post.html" |   }, | ||||||
|   , "createdDate": "2013-08-01T22:47:37.000Z" |   { | ||||||
|   , "lastModifiedDate": "2013-08-01T22:47:37.000Z" |     "name": "my-first-post.html", | ||||||
|   , "size": 4118 |     "createdDate": "2013-08-01T22:47:37.000Z", | ||||||
|   , "relativePath": "posts/2013" |     "lastModifiedDate": "2013-08-01T22:47:37.000Z", | ||||||
|  |     "size": 4118, | ||||||
|  |     "relativePath": "posts/2013" | ||||||
|   } |   } | ||||||
| ] | ] | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| To retrieve multiple dir listings at once: | To retrieve multiple dir listings at once: | ||||||
| 
 | 
 | ||||||
| * for a few simple dirs without special chars just change `dir` to `dirs` and separate with commas | - for a few simple dirs without special chars just change `dir` to `dirs` and separate with commas | ||||||
| 
 | 
 | ||||||
| `GET http://local.dear.desi:8080/api/fs/walk?dirs=posts/2015,posts/2013&dotfiles=true&extensions=md,markdown,jade,htm,html` | `GET http://local.dear.desi:8080/api/fs/walk?dirs=posts/2015,posts/2013&dotfiles=true&extensions=md,markdown,jade,htm,html` | ||||||
| 
 | 
 | ||||||
| * for many dirs, or dirs with special chars, `POST` an object containing an array of `dirs` with  `&_method=GET` appended to the url. | - for many dirs, or dirs with special chars, `POST` an object containing an array of `dirs` with `&_method=GET` appended to the url. | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| POST http://local.dear.desi:8080/api/fs/walk?dotfiles=true&extensions=md,markdown,jade,htm,html&_method=GET | POST http://local.dear.desi:8080/api/fs/walk?dotfiles=true&extensions=md,markdown,jade,htm,html&_method=GET | ||||||
| @ -367,27 +352,27 @@ POST http://local.dear.desi:8080/api/fs/walk?dotfiles=true&extensions=md,markdow | |||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| GET /api/fs/files | ## GET /api/fs/files | ||||||
| ------------- |  | ||||||
| 
 | 
 | ||||||
| `GET http://local.dear.desi:8080/api/fs/files?path=posts/happy-new-year.md` | `GET http://local.dear.desi:8080/api/fs/files?path=posts/happy-new-year.md` | ||||||
| 
 | 
 | ||||||
| ```json | ```json | ||||||
| { "path": "posts/intro-to-http-with-netcat-node-connect.md" | { | ||||||
| , "createdDate": "2013-08-01T22:47:37.000Z" |   "path": "posts/intro-to-http-with-netcat-node-connect.md", | ||||||
| , "lastModifiedDate": "2013-08-01T22:47:37.000Z" |   "createdDate": "2013-08-01T22:47:37.000Z", | ||||||
| , "contents": "..." |   "lastModifiedDate": "2013-08-01T22:47:37.000Z", | ||||||
| , "sha1": "6eae3a5b062c6d0d79f070c26e6d62486b40cb46" |   "contents": "...", | ||||||
|  |   "sha1": "6eae3a5b062c6d0d79f070c26e6d62486b40cb46" | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| To retrieve multiple files at once: | To retrieve multiple files at once: | ||||||
| 
 | 
 | ||||||
| * for a few simple files without special chars just change `path` to `paths` and separate with commas | - for a few simple files without special chars just change `path` to `paths` and separate with commas | ||||||
| 
 | 
 | ||||||
| `GET http://local.dear.desi:8080/api/fs/files?paths=posts/foo.md,posts/bar.md` | `GET http://local.dear.desi:8080/api/fs/files?paths=posts/foo.md,posts/bar.md` | ||||||
| 
 | 
 | ||||||
| * for many files, or files with special chars, `POST` an object containing an array of `pathss` with  `&_method=GET` appended to the url. | - for many files, or files with special chars, `POST` an object containing an array of `pathss` with `&_method=GET` appended to the url. | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| POST http://local.dear.desi:8080/api/fs/files?dotfiles=true&extensions=md,markdown,jade,htm,html&_method=GET | POST http://local.dear.desi:8080/api/fs/files?dotfiles=true&extensions=md,markdown,jade,htm,html&_method=GET | ||||||
| @ -406,8 +391,7 @@ POST http://local.dear.desi:8080/api/fs/files?dotfiles=true&extensions=md,markdo | |||||||
| ] | ] | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| POST /api/fs/files | ## POST /api/fs/files | ||||||
| ------------------ |  | ||||||
| 
 | 
 | ||||||
| By default this should assume that you intended to write to the compiled directory | By default this should assume that you intended to write to the compiled directory | ||||||
| and return an error if you try to write to any other directory, unless `compiled=false` (not yet implemented). | and return an error if you try to write to any other directory, unless `compiled=false` (not yet implemented). | ||||||
| @ -452,9 +436,15 @@ The response may include errors of all shapes and sizes. | |||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| POST /api/fs/copy | ## POST /api/fs/copy | ||||||
| ------------------ |  | ||||||
| 
 | 
 | ||||||
| ```json | ```json | ||||||
| { files: { "assets/logo.png": "compiled/assets/logo.png" } } | { "files": { "assets/logo.png": "compiled/assets/logo.png" } } | ||||||
| ``` | ``` | ||||||
|  | 
 | ||||||
|  | # License | ||||||
|  | 
 | ||||||
|  | This Source Code Form is subject to the terms of the Mozilla \ | ||||||
|  | Public License, v. 2.0. If a copy of the MPL was not distributed \ | ||||||
|  | with this file, You can obtain one at \ | ||||||
|  | https://mozilla.org/MPL/2.0/. | ||||||
|  | |||||||
							
								
								
									
										44
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								TODO.md
									
									
									
									
									
								
							| @ -4,7 +4,6 @@ show file path | |||||||
| show prod url | show prod url | ||||||
| show dev url | show dev url | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| POST tests | POST tests | ||||||
| create a title and delete it (no error) | create a title and delete it (no error) | ||||||
| change the format. does the permalink change? (yes) | change the format. does the permalink change? (yes) | ||||||
| @ -16,22 +15,17 @@ change the format in the frontmatter permalink. does the format change? (yes) | |||||||
| create a description and delete it (no error) | create a description and delete it (no error) | ||||||
| create a description. does the frontmatter change? (yes) | create a description. does the frontmatter change? (yes) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| protection | protection | ||||||
| Don't allow changing the uuid, original_url, or original_date | Don't allow changing the uuid, original_url, or original_date | ||||||
| 
 | 
 | ||||||
| TODO | ## TODO | ||||||
| --- |  | ||||||
| 
 | 
 | ||||||
| check that no other post uses the same permalink | check that no other post uses the same permalink | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| default data-model 'ruhoh@2.2' | default data-model 'ruhoh@2.2' | ||||||
| other data-model 'desirae@1.0' | other data-model 'desirae@1.0' | ||||||
| 
 | 
 | ||||||
| Widgets | # Widgets | ||||||
| ======= |  | ||||||
| 
 | 
 | ||||||
| All widgets should export an object with a `create(widgetConf, desiState)` function that returns a promise. | All widgets should export an object with a `create(widgetConf, desiState)` function that returns a promise. | ||||||
| 
 | 
 | ||||||
| @ -52,11 +46,10 @@ widgets: | |||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ```javascript | ```javascript | ||||||
| 'use strict'; | "use strict"; | ||||||
| 
 | 
 | ||||||
| module.exports.Foogizmo.create = function (foogizmoConf, desiState) { | module.exports.Foogizmo.create = function (foogizmoConf, desiState) { | ||||||
|   return new Promise(function (resolve) { |   return new Promise(function (resolve) { | ||||||
| 
 |  | ||||||
|     function pager(desiPageState) { |     function pager(desiPageState) { | ||||||
|       // Do processing |       // Do processing | ||||||
| 
 | 
 | ||||||
| @ -68,29 +61,28 @@ module.exports.Foogizmo.create = function (foogizmoConf, desiState) { | |||||||
| 
 | 
 | ||||||
|       desiPostState.fooembedinator = function (fooval) { |       desiPostState.fooembedinator = function (fooval) { | ||||||
|         // figure out what type of link fooval is and return iframe html |         // figure out what type of link fooval is and return iframe html | ||||||
|         return '<iframe src="http://embedinator.com/"' + foovalProcessed + '></iframe>' |         return ( | ||||||
|       } |           '<iframe src="http://embedinator.com/"' + | ||||||
|  |           foovalProcessed + | ||||||
|  |           "></iframe>" | ||||||
|  |         ); | ||||||
|  |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     resolve({ foopager: pager, fooposter: poster }); |     resolve({ foopager: pager, fooposter: poster }); | ||||||
|   }); |   }); | ||||||
| } | }; | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Overlays | ## Overlays | ||||||
| -------- |  | ||||||
| 
 | 
 | ||||||
| For any config a widget uses, it should also check on post.fooconfig and theme.fooconfig to make sure that they don't override the foogizmo.config.fooconfig | For any config a widget uses, it should also check on post.fooconfig and theme.fooconfig to make sure that they don't override the foogizmo.config.fooconfig | ||||||
| 
 | 
 | ||||||
| 
 | # Migrating from Ruhoh | ||||||
| 
 |  | ||||||
| Migrating from Ruhoh |  | ||||||
| ==================== |  | ||||||
| 
 | 
 | ||||||
| There are only a few things in Ruhoh that could only be done in ruby or were otherwise difficult to work around. | There are only a few things in Ruhoh that could only be done in ruby or were otherwise difficult to work around. | ||||||
| 
 | 
 | ||||||
| config.yml | ## config.yml | ||||||
| ---------- |  | ||||||
| 
 | 
 | ||||||
| Instead of having special names for some properties (`_root`) | Instead of having special names for some properties (`_root`) | ||||||
| and `use` sub attributes for others (`twitter` theme, posts directory), | and `use` sub attributes for others (`twitter` theme, posts directory), | ||||||
| @ -117,19 +109,15 @@ widgets [NO CHANGE] | |||||||
| 
 | 
 | ||||||
| All directories are ignored by default. If you want a directory to be interpreted as a collection of pages you need to specify it in the `collections` hash. | All directories are ignored by default. If you want a directory to be interpreted as a collection of pages you need to specify it in the `collections` hash. | ||||||
| 
 | 
 | ||||||
| data.yml | ## data.yml | ||||||
| -------- |  | ||||||
| 
 | 
 | ||||||
| No changes | No changes | ||||||
| 
 | 
 | ||||||
| 
 | ## config.ru | ||||||
| config.ru |  | ||||||
| --------- |  | ||||||
| 
 | 
 | ||||||
| REMOVED (ruby only) | REMOVED (ruby only) | ||||||
| 
 | 
 | ||||||
| themes layout | ## themes layout | ||||||
| ------------- |  | ||||||
| 
 | 
 | ||||||
| TODO | TODO | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								bower.json
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								bower.json
									
									
									
									
									
								
							| @ -1,15 +1,10 @@ | |||||||
| { | { | ||||||
|   "name": "desirae", |   "name": "desirae", | ||||||
|   "version": "0.11.2", |   "version": "0.11.2", | ||||||
|   "authors": [ |   "authors": ["AJ ONeal <awesome@coolaj86.com>"], | ||||||
|     "AJ ONeal <awesome@coolaj86.com>" |  | ||||||
|   ], |  | ||||||
|   "description": "A blogging platform in the browser. Wow!", |   "description": "A blogging platform in the browser. Wow!", | ||||||
|   "main": "desirae.js", |   "main": "desirae.js", | ||||||
|   "moduleType": [ |   "moduleType": ["globals", "node"], | ||||||
|     "globals", |  | ||||||
|     "node" |  | ||||||
|   ], |  | ||||||
|   "keywords": [ |   "keywords": [ | ||||||
|     "desirae", |     "desirae", | ||||||
|     "dear", |     "dear", | ||||||
| @ -26,15 +21,8 @@ | |||||||
|   ], |   ], | ||||||
|   "license": "Apache2", |   "license": "Apache2", | ||||||
|   "homepage": "http://github.com/DearDesi/desirae", |   "homepage": "http://github.com/DearDesi/desirae", | ||||||
|   "ignore": [ |   "ignore": ["**/.*", "node_modules", "bower_components", "test", "tests"], | ||||||
|     "**/.*", |  | ||||||
|     "node_modules", |  | ||||||
|     "bower_components", |  | ||||||
|     "test", |  | ||||||
|     "tests" |  | ||||||
|   ], |  | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "bluebird": "~2.6.2", |  | ||||||
|     "escape-string-regexp": "~1.0.2", |     "escape-string-regexp": "~1.0.2", | ||||||
|     "forEachAsync": "~5.0.5", |     "forEachAsync": "~5.0.5", | ||||||
|     "js-yaml": "~3.2.5", |     "js-yaml": "~3.2.5", | ||||||
|  | |||||||
							
								
								
									
										1159
									
								
								desirae.js
									
									
									
									
									
								
							
							
						
						
									
										1159
									
								
								desirae.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,25 +1,23 @@ | |||||||
| /*jshint -W054 */ | /*jshint -W054 */ | ||||||
| ;(function (exports) { | (function (exports) { | ||||||
|   'use strict'; |   "use strict"; | ||||||
| 
 |  | ||||||
|   var path = exports.path || require('path') |  | ||||||
|     , months |  | ||||||
|     , cores = {} |  | ||||||
|     ; |  | ||||||
| 
 | 
 | ||||||
|  |   var path = exports.path || require("path"), | ||||||
|  |     months, | ||||||
|  |     cores = {}; | ||||||
|   months = { |   months = { | ||||||
|     1: 'January' |     1: "January", | ||||||
|   , 2: 'February' |     2: "February", | ||||||
|   , 3: 'March' |     3: "March", | ||||||
|   , 4: 'April' |     4: "April", | ||||||
|   , 5: 'May' |     5: "May", | ||||||
|   , 6: 'June' |     6: "June", | ||||||
|   , 7: 'July' |     7: "July", | ||||||
|   , 8: 'August' |     8: "August", | ||||||
|   , 9: 'September' |     9: "September", | ||||||
|   , 10: 'October' |     10: "October", | ||||||
|   , 11: 'November' |     11: "November", | ||||||
|   , 12: 'December' |     12: "December", | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   function byDate(a, b) { |   function byDate(a, b) { | ||||||
| @ -67,14 +65,12 @@ | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function collate(entities, env) { |   function collate(entities, env) { | ||||||
|     var yearsArr = [] |     var yearsArr = []; | ||||||
|       ; |  | ||||||
| 
 |  | ||||||
|     entities.forEach(function (f) { |     entities.forEach(function (f) { | ||||||
|       var set |       var set; | ||||||
|         , yindex = 3000 - f.year |       var yindex = 3000 - f.year; | ||||||
|         , mindex = 12 - f.month |       var mindex = 12 - f.month; | ||||||
|         ; |       var monthName = months[parseInt(f.month, 10)]; | ||||||
| 
 | 
 | ||||||
|       f.url = path.join(env.base_path, f.permalink); |       f.url = path.join(env.base_path, f.permalink); | ||||||
| 
 | 
 | ||||||
| @ -84,10 +80,17 @@ | |||||||
|       set = yearsArr[yindex]; |       set = yearsArr[yindex]; | ||||||
| 
 | 
 | ||||||
|       if (!set.months[mindex]) { |       if (!set.months[mindex]) { | ||||||
|         set.months[mindex] = { month: months[parseInt(f.month, 10)], pages: [] }; |         set.months[mindex] = { | ||||||
|  |           month_name: monthName, | ||||||
|  |           month_number: mindex, | ||||||
|  |           month: monthName, | ||||||
|  |           pages: [], | ||||||
|  |         }; | ||||||
|       } |       } | ||||||
|       set = set.months[mindex]; |       set = set.months[mindex]; | ||||||
| 
 | 
 | ||||||
|  |       f.month_name = months[parseInt(f.month, 10)]; | ||||||
|  |       f.month_number = parseInt(f.month, 10); | ||||||
|       set.pages.push(f); |       set.pages.push(f); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
| @ -118,4 +121,4 @@ | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   exports.DesiraeAggregateCore = cores.DesiraeAggregateCore = cores; |   exports.DesiraeAggregateCore = cores.DesiraeAggregateCore = cores; | ||||||
| }('undefined' !== typeof exports && exports || window)); | })(("undefined" !== typeof exports && exports) || window); | ||||||
|  | |||||||
| @ -1,67 +1,66 @@ | |||||||
| /*jshint -W054 */ | /*jshint -W054 */ | ||||||
| ;(function (exports) { | (function (exports) { | ||||||
|   'use strict'; |   "use strict"; | ||||||
| 
 | 
 | ||||||
|   function create(Desi) { |   function create(Desi) { | ||||||
|     // Chrome, Firefox, and even MSIE11+ all support crypto
 |     // Chrome, Firefox, and even MSIE11+ all support crypto
 | ||||||
|     var crypto = window.crypto || window.msCrypto |     var crypto = window.crypto || window.msCrypto, | ||||||
|       , PromiseA = window.Promise |       Promise = window.Promise, | ||||||
|       , algos |       algos; | ||||||
|       ; |  | ||||||
| 
 | 
 | ||||||
|     // convenience mappings for common digest algorithms
 |     // convenience mappings for common digest algorithms
 | ||||||
|     algos = { |     algos = { | ||||||
|       'sha1': 'SHA-1' |       sha1: "SHA-1", | ||||||
|     , 'sha256': 'SHA-256' |       sha256: "SHA-256", | ||||||
|     , 'sha512': 'SHA-512' |       sha512: "SHA-512", | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     // The function to generate a sha1sum is the same as generating any digest
 |     // The function to generate a sha1sum is the same as generating any digest
 | ||||||
|     // but here's a shortcut function anyway
 |     // but here's a shortcut function anyway
 | ||||||
|     function sha1sum(str) { |     function sha1sum(str) { | ||||||
|       return hashsum('sha1', str); |       return hashsum("sha1", str); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // a more general convenience function
 |     // a more general convenience function
 | ||||||
|     function hashsum(hash, str) { |     function hashsum(hash, str) { | ||||||
|         // you have to convert from string to array buffer
 |       // you have to convert from string to array buffer
 | ||||||
|       var ab |       var ab, | ||||||
|         // you have to represent the algorithm as an object
 |         // you have to represent the algorithm as an object
 | ||||||
|         , algo = { name: algos[hash] } |         algo = { name: algos[hash] }; | ||||||
|         ; |       if ("string" === typeof str) { | ||||||
| 
 |  | ||||||
|       if ('string' === typeof str) { |  | ||||||
|         ab = str2ab(str); |         ab = str2ab(str); | ||||||
|       } else { |       } else { | ||||||
|         ab = str; |         ab = str; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       // All crypto digest methods return a promise
 |       // All crypto digest methods return a promise
 | ||||||
|       return crypto.subtle.digest(algo, ab).then(function (digest) { |       return crypto.subtle | ||||||
|         // you have to convert the ArrayBuffer to a DataView and then to a hex String
 |         .digest(algo, ab) | ||||||
|         return ab2hex(digest); |         .then(function (digest) { | ||||||
|       }).catch(function (e) { |           // you have to convert the ArrayBuffer to a DataView and then to a hex String
 | ||||||
|         // if you specify an unsupported digest algorithm or non-ArrayBuffer, you'll get an error
 |           return ab2hex(digest); | ||||||
|         console.error('sha1sum ERROR'); |         }) | ||||||
|         console.error(e); |         .catch(function (e) { | ||||||
|         throw e; |           // if you specify an unsupported digest algorithm or non-ArrayBuffer, you'll get an error
 | ||||||
|       }); |           console.error("sha1sum ERROR"); | ||||||
|  |           console.error(e); | ||||||
|  |           throw e; | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // convert from arraybuffer to hex
 |     // convert from arraybuffer to hex
 | ||||||
|     function ab2hex(ab) { |     function ab2hex(ab) { | ||||||
|       var dv = new DataView(ab) |       var dv = new DataView(ab), | ||||||
|         , i |         i, | ||||||
|         , len |         len, | ||||||
|         , hex = '' |         hex = "", | ||||||
|         , c |         c; | ||||||
|         ; |  | ||||||
| 
 | 
 | ||||||
|       for (i = 0, len = dv.byteLength; i < len; i += 1) { |       for (i = 0, len = dv.byteLength; i < len; i += 1) { | ||||||
|         c = dv.getUint8(i).toString(16); |         c = dv.getUint8(i).toString(16); | ||||||
| 
 | 
 | ||||||
|         if (c.length < 2) { |         if (c.length < 2) { | ||||||
|           c = '0' + c; |           c = "0" + c; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         hex += c; |         hex += c; | ||||||
| @ -72,36 +71,31 @@ | |||||||
| 
 | 
 | ||||||
|     // convert from string to arraybuffer
 |     // convert from string to arraybuffer
 | ||||||
|     function str2ab(stringToEncode, insertBom) { |     function str2ab(stringToEncode, insertBom) { | ||||||
|       stringToEncode = stringToEncode.replace(/\r\n/g,"\n"); |       stringToEncode = stringToEncode.replace(/\r\n/g, "\n"); | ||||||
| 
 | 
 | ||||||
|       var utftext = [] |       var utftext = [], | ||||||
|         , n |         n, | ||||||
|         , c |         c; | ||||||
|         ; |  | ||||||
| 
 | 
 | ||||||
|       if (true === insertBom)  { |       if (true === insertBom) { | ||||||
|         utftext[0] =  0xef; |         utftext[0] = 0xef; | ||||||
|         utftext[1] =  0xbb; |         utftext[1] = 0xbb; | ||||||
|         utftext[2] =  0xbf; |         utftext[2] = 0xbf; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       for (n = 0; n < stringToEncode.length; n += 1) { |       for (n = 0; n < stringToEncode.length; n += 1) { | ||||||
| 
 |  | ||||||
|         c = stringToEncode.charCodeAt(n); |         c = stringToEncode.charCodeAt(n); | ||||||
| 
 | 
 | ||||||
|         if (c < 128) { |         if (c < 128) { | ||||||
|             utftext[utftext.length]= c; |           utftext[utftext.length] = c; | ||||||
|  |         } else if (c > 127 && c < 2048) { | ||||||
|  |           utftext[utftext.length] = (c >> 6) | 192; | ||||||
|  |           utftext[utftext.length] = (c & 63) | 128; | ||||||
|  |         } else { | ||||||
|  |           utftext[utftext.length] = (c >> 12) | 224; | ||||||
|  |           utftext[utftext.length] = ((c >> 6) & 63) | 128; | ||||||
|  |           utftext[utftext.length] = (c & 63) | 128; | ||||||
|         } |         } | ||||||
|         else if((c > 127) && (c < 2048)) { |  | ||||||
|             utftext[utftext.length] = (c >> 6) | 192; |  | ||||||
|             utftext[utftext.length] = (c & 63) | 128; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             utftext[utftext.length] = (c >> 12) | 224; |  | ||||||
|             utftext[utftext.length] = ((c >> 6) & 63) | 128; |  | ||||||
|             utftext[utftext.length] = (c & 63) | 128; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|       } |       } | ||||||
|       return new Uint8Array(utftext).buffer; |       return new Uint8Array(utftext).buffer; | ||||||
|     } |     } | ||||||
| @ -112,26 +106,21 @@ | |||||||
|     //
 |     //
 | ||||||
|     // FSAPI
 |     // FSAPI
 | ||||||
|     //
 |     //
 | ||||||
|     var fsapi |     var fsapi; | ||||||
|       ; |  | ||||||
| 
 | 
 | ||||||
|     function request() { |     function request() {} | ||||||
|     } |     request.get = function (url /*, query*/) { | ||||||
|     request.get = function (url/*, query*/) { |  | ||||||
|       // Return a new promise.
 |       // Return a new promise.
 | ||||||
|       return new PromiseA(function(resolve, reject) { |       return new Promise(function (resolve, reject) { | ||||||
|         // Do the usual XHR stuff
 |         // Do the usual XHR stuff
 | ||||||
|         var req = new XMLHttpRequest() |         var req = new XMLHttpRequest(); | ||||||
|           ; |         req.onload = function () { | ||||||
| 
 |  | ||||||
|         req.onload = function() { |  | ||||||
|           // This is called even on 404 etc
 |           // This is called even on 404 etc
 | ||||||
|           // so check the status
 |           // so check the status
 | ||||||
|           if (200 === req.status) { |           if (200 === req.status) { | ||||||
|             // Resolve the promise with the response text
 |             // Resolve the promise with the response text
 | ||||||
|             resolve(req.response); |             resolve(req.response); | ||||||
|           } |           } else { | ||||||
|           else { |  | ||||||
|             // Otherwise reject with the status text
 |             // Otherwise reject with the status text
 | ||||||
|             // which will hopefully be a meaningful error
 |             // which will hopefully be a meaningful error
 | ||||||
|             reject(Error(req.statusText)); |             reject(Error(req.statusText)); | ||||||
| @ -139,30 +128,27 @@ | |||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         // Handle network errors
 |         // Handle network errors
 | ||||||
|         req.onerror = function() { |         req.onerror = function () { | ||||||
|           reject(Error("Network Error")); |           reject(Error("Network Error")); | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         // Make the request
 |         // Make the request
 | ||||||
|         req.open('GET', url); |         req.open("GET", url); | ||||||
|         req.send(); |         req.send(); | ||||||
|       }); |       }); | ||||||
|     }; |     }; | ||||||
|     request.post = function (url/*, query*/, body) { |     request.post = function (url /*, query*/, body) { | ||||||
|       // Return a new promise.
 |       // Return a new promise.
 | ||||||
|       return new PromiseA(function(resolve, reject) { |       return new Promise(function (resolve, reject) { | ||||||
|         // Do the usual XHR stuff
 |         // Do the usual XHR stuff
 | ||||||
|         var req = new XMLHttpRequest() |         var req = new XMLHttpRequest(); | ||||||
|           ; |         req.onload = function () { | ||||||
| 
 |  | ||||||
|         req.onload = function() { |  | ||||||
|           // This is called even on 404 etc
 |           // This is called even on 404 etc
 | ||||||
|           // so check the status
 |           // so check the status
 | ||||||
|           if (200 === req.status) { |           if (200 === req.status) { | ||||||
|             // Resolve the promise with the response text
 |             // Resolve the promise with the response text
 | ||||||
|             resolve(req.response); |             resolve(req.response); | ||||||
|           } |           } else { | ||||||
|           else { |  | ||||||
|             // Otherwise reject with the status text
 |             // Otherwise reject with the status text
 | ||||||
|             // which will hopefully be a meaningful error
 |             // which will hopefully be a meaningful error
 | ||||||
|             reject(Error(req.statusText)); |             reject(Error(req.statusText)); | ||||||
| @ -170,14 +156,14 @@ | |||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         // Handle network errors
 |         // Handle network errors
 | ||||||
|         req.onerror = function() { |         req.onerror = function () { | ||||||
|           reject(Error("Network Error")); |           reject(Error("Network Error")); | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         req.open('POST', url); |         req.open("POST", url); | ||||||
|         req.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); |         req.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); | ||||||
|         // Make the request
 |         // Make the request
 | ||||||
|         if ('string' !== typeof body) { |         if ("string" !== typeof body) { | ||||||
|           body = JSON.stringify(body); |           body = JSON.stringify(body); | ||||||
|         } |         } | ||||||
|         req.send(body); |         req.send(body); | ||||||
| @ -188,87 +174,93 @@ | |||||||
|     fsapi.getMeta = function (collections, opts) { |     fsapi.getMeta = function (collections, opts) { | ||||||
|       opts = opts || {}; |       opts = opts || {}; | ||||||
| 
 | 
 | ||||||
|       var extensions = '' |       var extensions = "", | ||||||
|         , dotfiles = '' |         dotfiles = "", | ||||||
|         , contents = '' |         contents = "", | ||||||
|         , sha1sum = '' |         sha1sum = ""; | ||||||
|         ; |  | ||||||
| 
 |  | ||||||
|       if (Array.isArray(opts.extensions)) { |       if (Array.isArray(opts.extensions)) { | ||||||
|         extensions = '&extensions=' + opts.extensions.join(','); // md,markdown,jade,htm,html
 |         extensions = "&extensions=" + opts.extensions.join(","); // md,markdown,jade,htm,html
 | ||||||
|       } |       } | ||||||
|       if (opts.dotfiles) { |       if (opts.dotfiles) { | ||||||
|         dotfiles = '&dotfiles=true'; |         dotfiles = "&dotfiles=true"; | ||||||
|       } |       } | ||||||
|       if (opts.contents) { |       if (opts.contents) { | ||||||
|         contents = '&contents=true'; |         contents = "&contents=true"; | ||||||
|       } |       } | ||||||
|       if (false === opts.sha1sum) { |       if (false === opts.sha1sum) { | ||||||
|         sha1sum = '&sha1sum=false'; |         sha1sum = "&sha1sum=false"; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       return request.post('/api/fs/walk?_method=GET' + dotfiles + extensions + contents + sha1sum, { |       return request | ||||||
|         dirs: collections |         .post( | ||||||
|       }).then(function (resp) { |           "/api/fs/walk?_method=GET" + | ||||||
|         return JSON.parse(resp); |             dotfiles + | ||||||
|       }).catch(function (e) { |             extensions + | ||||||
|         throw e; |             contents + | ||||||
|       }); |             sha1sum, | ||||||
|  |           { | ||||||
|  |             dirs: collections, | ||||||
|  |           } | ||||||
|  |         ) | ||||||
|  |         .then(function (resp) { | ||||||
|  |           return JSON.parse(resp); | ||||||
|  |         }) | ||||||
|  |         .catch(function (e) { | ||||||
|  |           throw e; | ||||||
|  |         }); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     fsapi.getContents = function (filepaths) { |     fsapi.getContents = function (filepaths) { | ||||||
|       return request.post('/api/fs/files?_method=GET', { |       return request | ||||||
|         paths: filepaths |         .post("/api/fs/files?_method=GET", { | ||||||
|       }).then(function (resp) { |           paths: filepaths, | ||||||
|         return JSON.parse(resp); |         }) | ||||||
|       }); |         .then(function (resp) { | ||||||
|  |           return JSON.parse(resp); | ||||||
|  |         }); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     fsapi.getCache = function () { |     fsapi.getCache = function () { | ||||||
|       return request.get('/api/fs/static/cache.json').then(function (resp) { |       return request | ||||||
|         return JSON.parse(resp); |         .get("/api/fs/static/cache.json") | ||||||
|       }).catch(function (/*e*/) { |         .then(function (resp) { | ||||||
|         return {}; |           return JSON.parse(resp); | ||||||
|       }).then(function (obj) { |         }) | ||||||
|         return obj; |         .catch(function (/*e*/) { | ||||||
|       }); |           return {}; | ||||||
|  |         }) | ||||||
|  |         .then(function (obj) { | ||||||
|  |           return obj; | ||||||
|  |         }); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     fsapi.copy = function (files) { |     fsapi.copy = function (files) { | ||||||
|       var body = { files: files }; |       var body = { files: files }; | ||||||
|       body = JSON.stringify(body); // this is more or less instant for a few MiB of posts
 |       body = JSON.stringify(body); // this is more or less instant for a few MiB of posts
 | ||||||
|       return request.post('/api/fs/copy', body).then(function (resp) { |       return request.post("/api/fs/copy", body).then(function (resp) { | ||||||
|         var response = JSON.parse(resp) |         var response = JSON.parse(resp); | ||||||
|           ; |  | ||||||
| 
 |  | ||||||
|         // not accurate for utf8/unicode, but close enough
 |         // not accurate for utf8/unicode, but close enough
 | ||||||
|         response.size = body.length; |         response.size = body.length; | ||||||
|         return response; |         return response; | ||||||
|       }); |       }); | ||||||
| 
 |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     fsapi.putFiles = function (files) { |     fsapi.putFiles = function (files) { | ||||||
|       var body = { files: files } |       var body = { files: files }; | ||||||
|         ; |  | ||||||
| 
 |  | ||||||
|       files.forEach(function (file) { |       files.forEach(function (file) { | ||||||
|         if (!file.contents || 'string' === typeof file.contents) { |         if (!file.contents || "string" === typeof file.contents) { | ||||||
|           return; |           return; | ||||||
|         } |         } | ||||||
|         if (/\.json$/i.test(file.path)) { |         if (/\.json$/i.test(file.path)) { | ||||||
|           file.contents = JSON.stringify(file.contents); |           file.contents = JSON.stringify(file.contents); | ||||||
|         } |         } else if (/\.ya?ml$/i.test(file.path)) { | ||||||
|         else if (/\.ya?ml$/i.test(file.path)) { |  | ||||||
|           file.contents = exports.jsyaml.dump(file.contents); |           file.contents = exports.jsyaml.dump(file.contents); | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       body = JSON.stringify(body); // this is more or less instant for a few MiB of posts
 |       body = JSON.stringify(body); // this is more or less instant for a few MiB of posts
 | ||||||
|       return request.post('/api/fs/files', body).then(function (resp) { |       return request.post("/api/fs/files", body).then(function (resp) { | ||||||
|         var response = JSON.parse(resp) |         var response = JSON.parse(resp); | ||||||
|           ; |  | ||||||
| 
 |  | ||||||
|         // not accurate for utf8/unicode, but close enough
 |         // not accurate for utf8/unicode, but close enough
 | ||||||
|         response.size = body.length; |         response.size = body.length; | ||||||
|         return response; |         return response; | ||||||
| @ -281,4 +273,4 @@ | |||||||
|   } else { |   } else { | ||||||
|     exports.create = create; |     exports.create = create; | ||||||
|   } |   } | ||||||
| }('undefined' !== typeof exports && exports || window)); | })(("undefined" !== typeof exports && exports) || window); | ||||||
|  | |||||||
| @ -1,7 +1,9 @@ | |||||||
| 'use strict'; | "use strict"; | ||||||
| 
 | 
 | ||||||
| module.exports.convert = function () { | module.exports.convert = function () { | ||||||
|   console.error("I haven't implemented a ruhoh -> nuhoh converter yet, but it's not very hard to do."); |   console.error( | ||||||
|  |     "I haven't implemented a ruhoh -> nuhoh converter yet, but it's not very hard to do." | ||||||
|  |   ); | ||||||
|   console.error("see https://github.com/coolaj86/nuhoh/tree/master/MIGRATE.md"); |   console.error("see https://github.com/coolaj86/nuhoh/tree/master/MIGRATE.md"); | ||||||
|   throw new Error('Not Implemented.'); |   throw new Error("Not Implemented."); | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /*jshint -W054 */ | /*jshint -W054 */ | ||||||
| ;(function (exports) { | (function (exports) { | ||||||
|   'use strict'; |   "use strict"; | ||||||
| 
 | 
 | ||||||
|   function desiMap(obj) { |   function desiMap(obj) { | ||||||
|     obj.desi = obj; |     obj.desi = obj; | ||||||
| @ -8,4 +8,4 @@ | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   exports.DesiraeDatamapCore = desiMap.DesiraeDatamapCore = desiMap; |   exports.DesiraeDatamapCore = desiMap.DesiraeDatamapCore = desiMap; | ||||||
| }('undefined' !== typeof exports && exports || window)); | })(("undefined" !== typeof exports && exports) || window); | ||||||
|  | |||||||
| @ -1,19 +1,17 @@ | |||||||
| /*jshint -W054 */ | /*jshint -W054 */ | ||||||
| ;(function (exports) { | (function (exports) { | ||||||
|   'use strict'; |   "use strict"; | ||||||
| 
 | 
 | ||||||
|   function create(Desi) { |   function create(Desi) { | ||||||
|     Desi.YAML           = {}; |     Desi.YAML = {}; | ||||||
|     Desi.YAML.parse     = (exports.jsyaml || require('js-yaml')).load; |     Desi.YAML.parse = (exports.jsyaml || require("js-yaml")).load; | ||||||
|     Desi.YAML.stringify = (exports.jsyaml || require('js-yaml')).dump; |     Desi.YAML.stringify = (exports.jsyaml || require("js-yaml")).dump; | ||||||
| 
 | 
 | ||||||
|     function readFrontMatter(text) { |     function readFrontMatter(text) { | ||||||
|       var lines |       var lines, | ||||||
|         , line |         line, | ||||||
|         , padIndent = '' |         padIndent = "", | ||||||
|         , ymllines = [] |         ymllines = []; | ||||||
|         ; |  | ||||||
| 
 |  | ||||||
|       lines = text.split(/\n/); |       lines = text.split(/\n/); | ||||||
|       line = lines.shift(); |       line = lines.shift(); | ||||||
| 
 | 
 | ||||||
| @ -25,14 +23,14 @@ | |||||||
|       // that start without indentation, so
 |       // that start without indentation, so
 | ||||||
|       // we can add it if this is the case
 |       // we can add it if this is the case
 | ||||||
|       if (lines[0] && lines[0].match(/^\S/)) { |       if (lines[0] && lines[0].match(/^\S/)) { | ||||||
|         padIndent = ''; |         padIndent = ""; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       while (true) { |       while (true) { | ||||||
|         line = lines.shift(); |         line = lines.shift(); | ||||||
| 
 | 
 | ||||||
|         // premature end-of-file (unsupported yaml)
 |         // premature end-of-file (unsupported yaml)
 | ||||||
|         if (!line && '' !== line) { |         if (!line && "" !== line) { | ||||||
|           ymllines = []; |           ymllines = []; | ||||||
|           break; |           break; | ||||||
|         } |         } | ||||||
| @ -48,20 +46,17 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|       // XXX can't be sorted because arrays get messed up
 |       // XXX can't be sorted because arrays get messed up
 | ||||||
|       //ymllines.sort();
 |       //ymllines.sort();
 | ||||||
|       if (ymllines) { |       if (ymllines) { | ||||||
|         return '---\n' + ymllines.join('\n'); |         return "---\n" + ymllines.join("\n"); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function separateText(text, fm) { |     function separateText(text, fm) { | ||||||
|       var len |       var len, yml; | ||||||
|         , yml |  | ||||||
|         ; |  | ||||||
| 
 | 
 | ||||||
|       yml = readFrontMatter(fm); |       yml = readFrontMatter(fm); | ||||||
|       // strip frontmatter from text, if any
 |       // strip frontmatter from text, if any
 | ||||||
| @ -72,27 +67,26 @@ | |||||||
|         len = 0; |         len = 0; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       return text.split(/\n/).slice(len).join('\n'); |       return text.split(/\n/).slice(len).join("\n"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function parseText(text) { |     function parseText(text) { | ||||||
|       var fm = readFrontMatter(text) |       var fm = readFrontMatter(text), | ||||||
|         , body = fm && separateText(text, fm) |         body = fm && separateText(text, fm), | ||||||
|         , yml |         yml; | ||||||
|         ; |  | ||||||
| 
 | 
 | ||||||
|       if (fm) { |       if (fm) { | ||||||
|         try { |         try { | ||||||
|           yml = Desi.YAML.parse(fm); |           yml = Desi.YAML.parse(fm); | ||||||
|         } catch(e) { |         } catch (e) { | ||||||
|           //
 |           //
 | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       return { |       return { | ||||||
|         yml: yml |         yml: yml, | ||||||
|       , frontmatter: fm |         frontmatter: fm, | ||||||
|       , body: body |         body: body, | ||||||
|       }; |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -112,4 +106,4 @@ | |||||||
|   } else { |   } else { | ||||||
|     exports.create = create; |     exports.create = create; | ||||||
|   } |   } | ||||||
| }('undefined' !== typeof exports && exports || window)); | })(("undefined" !== typeof exports && exports) || window); | ||||||
|  | |||||||
| @ -1,51 +1,47 @@ | |||||||
| 'use strict'; | "use strict"; | ||||||
| 
 | 
 | ||||||
| var PromiseA = require('bluebird').Promise | var fs = require('fs').promises; | ||||||
|   , fs = PromiseA.promisifyAll(require('fs')) |  | ||||||
|   ; |  | ||||||
| 
 | 
 | ||||||
| function create(Desi, options) { | function create(Desi, options) { | ||||||
|   var fsapi = Desi.fsapi |   var fsapi = Desi.fsapi; | ||||||
|     ; |  | ||||||
| 
 |  | ||||||
|   options.blogdir = options.blogdir || options.working_path; |   options.blogdir = options.blogdir || options.working_path; | ||||||
| 
 | 
 | ||||||
|   fsapi.getMeta = function (dirnames, opts) { |   fsapi.getMeta = function (dirnames, opts) { | ||||||
|     opts = opts || {}; |     opts = opts || {}; | ||||||
| 
 | 
 | ||||||
|     var extensions = '' |     var extensions = "", | ||||||
|       , dotfiles = '' |       dotfiles = "", | ||||||
|       , contents = '' |       contents = "", | ||||||
|       , sha1sum = '' |       sha1sum = ""; | ||||||
|       ; |  | ||||||
| 
 |  | ||||||
|     if (Array.isArray(opts.extensions)) { |     if (Array.isArray(opts.extensions)) { | ||||||
|       extensions = '&extensions=' + opts.extensions.join(','); // md,markdown,jade,htm,html
 |       extensions = "&extensions=" + opts.extensions.join(","); // md,markdown,jade,htm,html
 | ||||||
|     } |     } | ||||||
|     if (opts.dotfiles) { |     if (opts.dotfiles) { | ||||||
|       dotfiles = '&dotfiles=true'; |       dotfiles = "&dotfiles=true"; | ||||||
|     } |     } | ||||||
|     if (opts.contents) { |     if (opts.contents) { | ||||||
|       contents = '&contents=true'; |       contents = "&contents=true"; | ||||||
|     } |     } | ||||||
|     if (false === opts.sha1sum) { |     if (false === opts.sha1sum) { | ||||||
|       sha1sum = '&sha1sum=false'; |       sha1sum = "&sha1sum=false"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return fsapi.walk.walkDirs(options.blogdir, dirnames, opts); |     return fsapi.walk.walkDirs(options.blogdir, dirnames, opts); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   fsapi.getContents = function (filepaths) { |   fsapi.getContents = function (filepaths) { | ||||||
| 
 |  | ||||||
|     return fsapi.getfs(options.blogdir, filepaths); |     return fsapi.getfs(options.blogdir, filepaths); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   fsapi.getCache = function () { |   fsapi.getCache = function () { | ||||||
|     return fs.readFileAsync(options.blogdir, '/cache.json').catch(function (/*e*/) { |     return fs | ||||||
|       return {}; |       .readFile(options.blogdir, "/cache.json") | ||||||
|     }).then(function (obj) { |       .catch(function (/*e*/) { | ||||||
|       return obj; |         return {}; | ||||||
|     }); |       }) | ||||||
|  |       .then(function (obj) { | ||||||
|  |         return obj; | ||||||
|  |       }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   fsapi.copy = function (files) { |   fsapi.copy = function (files) { | ||||||
| @ -55,13 +51,12 @@ function create(Desi, options) { | |||||||
| 
 | 
 | ||||||
|   fsapi.putFiles = function (files, opts) { |   fsapi.putFiles = function (files, opts) { | ||||||
|     files.forEach(function (file) { |     files.forEach(function (file) { | ||||||
|       if (!file.contents || 'string' === typeof file.contents) { |       if (!file.contents || "string" === typeof file.contents) { | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|       if (/\.json$/i.test(file.path)) { |       if (/\.json$/i.test(file.path)) { | ||||||
|         file.contents = JSON.stringify(file.contents); |         file.contents = JSON.stringify(file.contents); | ||||||
|       } |       } else if (/\.ya?ml$/i.test(file.path)) { | ||||||
|       else if (/\.ya?ml$/i.test(file.path)) { |  | ||||||
|         file.contents = Desi.YAML.stringify(file.contents); |         file.contents = Desi.YAML.stringify(file.contents); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|  | |||||||
| @ -1,18 +1,18 @@ | |||||||
| 'use strict'; | "use strict"; | ||||||
| 
 | 
 | ||||||
| var PromiseA      = require('bluebird').Promise | var fs = require("fs").promises; | ||||||
|   , fs            = PromiseA.promisifyAll(require('fs')) | var path = require("path"); | ||||||
|   , forEachAsync  = require('foreachasync').forEachAsync |  | ||||||
|   , path          = require('path') |  | ||||||
|   , walk          = require('walk') |  | ||||||
|   , escapeRegExp  = require('escape-string-regexp') |  | ||||||
|   , safeResolve   = require('../utils').safeResolve |  | ||||||
|   , sha1sum       = function (str) { return require('secret-utils').hashsum('sha1', str); } |  | ||||||
|   , mkdirp        = PromiseA.promisify(require('mkdirp')) |  | ||||||
|   , fsExtra       = PromiseA.promisifyAll(require('fs.extra')) |  | ||||||
|   //, tmpdir        = require('os').tmpdir()
 |  | ||||||
|   ; |  | ||||||
| 
 | 
 | ||||||
|  | var forEachAsync = require("foreachasync").forEachAsync; | ||||||
|  | var walk = require("walk"); | ||||||
|  | var escapeRegExp = require("escape-string-regexp"); | ||||||
|  | var safeResolve = require("../utils").safeResolve; | ||||||
|  | var sha1sum = function (str) { | ||||||
|  |   return require("secret-utils").hashsum("sha1", str); | ||||||
|  | }; | ||||||
|  | var copyAll = require("util").promisify(require("fs.extra").copy); | ||||||
|  | 
 | ||||||
|  | //, tmpdir        = require('os').tmpdir()
 | ||||||
| function strip(prefix, pathname) { | function strip(prefix, pathname) { | ||||||
|   return pathname.substr(prefix.length + 1); |   return pathname.substr(prefix.length + 1); | ||||||
| } | } | ||||||
| @ -23,24 +23,24 @@ function walkDir(parent, sub, opts) { | |||||||
|     opts.sha1sum = true; |     opts.sha1sum = true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   var prefix = path.resolve(parent) |   var prefix = path.resolve(parent); | ||||||
|     , trueRoot = path.resolve(prefix, sub) |   var trueRoot = path.resolve(prefix, sub); | ||||||
|     , files = [] |   var files = []; | ||||||
|     ; |  | ||||||
| 
 |  | ||||||
|   function filter(name) { |   function filter(name) { | ||||||
|     if (!name) { |     if (!name) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!opts.dotfiles && ('.' === name[0])) { |     if (!opts.dotfiles && "." === name[0]) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (opts.extensions && opts.extensions.length) { |     if (opts.extensions && opts.extensions.length) { | ||||||
|       if (!opts.extensions.some(function (ext) { |       if ( | ||||||
|         return new RegExp('\\.' + escapeRegExp(ext) + '$').test(name); |         !opts.extensions.some(function (ext) { | ||||||
|       })) { |           return new RegExp("\\." + escapeRegExp(ext) + "$").test(name); | ||||||
|  |         }) | ||||||
|  |       ) { | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @ -48,11 +48,9 @@ function walkDir(parent, sub, opts) { | |||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return new PromiseA(function (resolve) { |   return new Promise(function (resolve) { | ||||||
|     var walker = walk.walk(trueRoot) |     var walker = walk.walk(trueRoot); | ||||||
|       ; |     walker.on("nodeError", function (filepath, stat, next) { | ||||||
| 
 |  | ||||||
|     walker.on('nodeError', function (filepath, stat, next) { |  | ||||||
|       //stats.forEach(function (stat) {
 |       //stats.forEach(function (stat) {
 | ||||||
|       if (!filter(stat.name)) { |       if (!filter(stat.name)) { | ||||||
|         return; |         return; | ||||||
| @ -60,59 +58,56 @@ function walkDir(parent, sub, opts) { | |||||||
| 
 | 
 | ||||||
|       stat.error.path = path.join(strip(prefix, filepath), stat.name); |       stat.error.path = path.join(strip(prefix, filepath), stat.name); | ||||||
|       files.push({ |       files.push({ | ||||||
|         name: stat.name |         name: stat.name, | ||||||
|       , relativePath: strip(prefix, filepath) |         relativePath: strip(prefix, filepath), | ||||||
|       , path: path.join(strip(prefix, filepath), stat.name) |         path: path.join(strip(prefix, filepath), stat.name), | ||||||
| 
 | 
 | ||||||
|       , type: undefined |         type: undefined, | ||||||
|       , error: stat.error |         error: stat.error, | ||||||
|       }); |       }); | ||||||
|       //});
 |       //});
 | ||||||
| 
 | 
 | ||||||
|       next(); |       next(); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     walker.on('files', function (root, stats, next) { |     walker.on("files", function (root, stats, next) { | ||||||
|       var dirname = strip(prefix, root) |       var dirname = strip(prefix, root); | ||||||
|         ; |  | ||||||
| 
 |  | ||||||
|       function eachFile(stat) { |       function eachFile(stat) { | ||||||
|         var file |         var file; | ||||||
|           ; |  | ||||||
| 
 | 
 | ||||||
|         if (!filter(stat.name)) { |         if (!filter(stat.name)) { | ||||||
|           return PromiseA.resolve(); |           return Promise.resolve(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         file = { |         file = { | ||||||
|           name: stat.name |           name: stat.name, | ||||||
|         , relativePath: dirname |           relativePath: dirname, | ||||||
|         , path: path.join(dirname, stat.name) |           path: path.join(dirname, stat.name), | ||||||
| 
 | 
 | ||||||
|         , createdDate: (stat.birthtime||stat.ctime).toISOString() |           createdDate: (stat.birthtime || stat.ctime).toISOString(), | ||||||
|         , lastModifiedDate: stat.mtime.toISOString() |           lastModifiedDate: stat.mtime.toISOString(), | ||||||
| 
 | 
 | ||||||
|         , size: stat.size |           size: stat.size, | ||||||
|         , type: undefined // TODO include mimetype
 |           type: undefined, // TODO include mimetype
 | ||||||
|         }; |         }; | ||||||
|         files.push(file); |         files.push(file); | ||||||
| 
 | 
 | ||||||
|         if (!(opts.sha1sum || opts.content)) { |         if (!(opts.sha1sum || opts.content)) { | ||||||
|           return PromiseA.resolve(); |           return Promise.resolve(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // TODO stream sha1 (for assets)
 |         // TODO stream sha1 (for assets)
 | ||||||
|         return fs.readFileAsync(path.join(root, stat.name), null).then(function (buffer) { |         return fs | ||||||
|           var contents = buffer.toString('utf8') |           .readFile(path.join(root, stat.name), null) | ||||||
|             ; |           .then(function (buffer) { | ||||||
|  |             var contents = buffer.toString("utf8"); | ||||||
|  |             file.sha1 = sha1sum(contents); | ||||||
|  |             file.type = undefined; | ||||||
| 
 | 
 | ||||||
|           file.sha1 = sha1sum(contents); |             if (opts.contents) { | ||||||
|           file.type = undefined; |               file.contents = contents; | ||||||
| 
 |             } | ||||||
|           if (opts.contents) { |           }); | ||||||
|             file.contents = contents; |  | ||||||
|           } |  | ||||||
|         }); |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (!opts.contents) { |       if (!opts.contents) { | ||||||
| @ -125,7 +120,7 @@ function walkDir(parent, sub, opts) { | |||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     walker.on('end', function () { |     walker.on("end", function () { | ||||||
|       resolve(files); |       resolve(files); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| @ -134,9 +129,7 @@ function walkDir(parent, sub, opts) { | |||||||
| function walkDirs(parent, subs, opts) { | function walkDirs(parent, subs, opts) { | ||||||
|   opts = opts || {}; |   opts = opts || {}; | ||||||
| 
 | 
 | ||||||
|   var collections = {} |   var collections = {}; | ||||||
|     ; |  | ||||||
| 
 |  | ||||||
|   return forEachAsync(subs, function (sub) { |   return forEachAsync(subs, function (sub) { | ||||||
|     return walkDir(parent, sub, opts).then(function (results) { |     return walkDir(parent, sub, opts).then(function (results) { | ||||||
|       collections[sub] = results; |       collections[sub] = results; | ||||||
| @ -146,59 +139,53 @@ function walkDirs(parent, subs, opts) { | |||||||
|   }); |   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| function getfs(blogdir, filepaths) { | function getfs(blogdir, filepaths) { | ||||||
|   var files = [] |   var files = []; | ||||||
|     ; |  | ||||||
| 
 |  | ||||||
|   return forEachAsync(filepaths, function (filepath) { |   return forEachAsync(filepaths, function (filepath) { | ||||||
|     var pathname = safeResolve(blogdir, filepath) |     var pathname = safeResolve(blogdir, filepath); | ||||||
|       ; |     return fs | ||||||
|  |       .lstat(pathname) | ||||||
|  |       .then(function (stat) { | ||||||
|  |         return fs.readFile(pathname, null).then(function (buffer) { | ||||||
|  |           files.push({ | ||||||
|  |             name: path.basename(pathname), | ||||||
|  |             relativePath: path.dirname(filepath), | ||||||
|  |             path: filepath, | ||||||
| 
 | 
 | ||||||
|     return fs.lstatAsync(pathname).then(function (stat) { |             createdDate: (stat.birthtime || stat.ctime).toISOString(), | ||||||
|       return fs.readFileAsync(pathname, null).then(function (buffer) { |             lastModifiedDate: stat.mtime.toISOString(), | ||||||
| 
 | 
 | ||||||
|         files.push({ |             contents: buffer.toString("utf8"), | ||||||
|           name: path.basename(pathname) |             size: buffer.length, | ||||||
|         , relativePath: path.dirname(filepath) |             sha1: sha1sum(buffer), | ||||||
|         , path: filepath |             type: undefined, | ||||||
| 
 |           }); | ||||||
|         , createdDate: (stat.birthtime||stat.ctime).toISOString() |  | ||||||
|         , lastModifiedDate: stat.mtime.toISOString() |  | ||||||
| 
 |  | ||||||
|         , contents: buffer.toString('utf8') |  | ||||||
|         , size: buffer.length |  | ||||||
|         , sha1: sha1sum(buffer) |  | ||||||
|         , type: undefined |  | ||||||
|         }); |         }); | ||||||
|  |       }) | ||||||
|  |       .catch(function (e) { | ||||||
|  |         files.push({ path: filepath, error: e.message }); | ||||||
|       }); |       }); | ||||||
|     }).catch(function (e) { |  | ||||||
|       files.push({ path: filepath, error: e.message }); |  | ||||||
|     }); |  | ||||||
|   }).then(function () { |   }).then(function () { | ||||||
|     return files; |     return files; | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function makeAllDirs(dirpaths) { | function makeAllDirs(dirpaths) { | ||||||
|   var errors = [] |   var errors = []; | ||||||
|     ; |  | ||||||
| 
 |  | ||||||
|   return forEachAsync(dirpaths, function (pathname) { |   return forEachAsync(dirpaths, function (pathname) { | ||||||
|     return mkdirp(pathname).catch(function (e) { |     return fs.mkdir(pathname, { recursive: true }).catch(function (e) { | ||||||
|       // TODO exclude attempting to write files to this dir?
 |       // TODO exclude attempting to write files to this dir?
 | ||||||
|       errors.push({ |       errors.push({ | ||||||
|         type: 'directory' |         type: "directory", | ||||||
| 
 | 
 | ||||||
|       , directory: pathname |         directory: pathname, | ||||||
| 
 | 
 | ||||||
|       , message: e.message |         message: e.message, | ||||||
|       , code: e.code |         code: e.code, | ||||||
|       , errno: e.errno |         errno: e.errno, | ||||||
|       , status: e.status |         status: e.status, | ||||||
|       , syscall: e.syscall |         syscall: e.syscall, | ||||||
|       }); |       }); | ||||||
| 
 |  | ||||||
|     }); |     }); | ||||||
|   }).then(function () { |   }).then(function () { | ||||||
|     return errors; |     return errors; | ||||||
| @ -207,156 +194,162 @@ function makeAllDirs(dirpaths) { | |||||||
| 
 | 
 | ||||||
| function copyfs(blogdir, files) { | function copyfs(blogdir, files) { | ||||||
|   // TODO switch format to { source: ..., dest: ..., opts: ... } ?
 |   // TODO switch format to { source: ..., dest: ..., opts: ... } ?
 | ||||||
|   var results = { errors: [] } |   var results = { errors: [] }, | ||||||
|     , dirpaths = {} |     dirpaths = {}, | ||||||
|     , sources = Object.keys(files) |     sources = Object.keys(files); | ||||||
|     ; |  | ||||||
| 
 |  | ||||||
|   return forEachAsync(sources, function (source) { |   return forEachAsync(sources, function (source) { | ||||||
|     /* |     /* | ||||||
|     var nsource = safeResolve(blogdir, source) |     var nsource = safeResolve(blogdir, source) | ||||||
|       ; |       ; | ||||||
|     */ |     */ | ||||||
| 
 | 
 | ||||||
|     var dest = safeResolve(blogdir, files[source]) |     var dest = safeResolve(blogdir, files[source]), | ||||||
|       , pathname = path.dirname(dest) |       pathname = path.dirname(dest); | ||||||
|       //, filename = path.basename(dest)
 |     //, filename = path.basename(dest)
 | ||||||
|       ; |  | ||||||
| 
 |  | ||||||
|     dirpaths[pathname] = true; |     dirpaths[pathname] = true; | ||||||
| 
 | 
 | ||||||
|     return PromiseA.resolve(); |     return Promise.resolve(); | ||||||
|   }).then(function () { |   }) | ||||||
|     // TODO is it better to do this lazy-like or as a batch?
 |     .then(function () { | ||||||
|     // I figure as batch when there may be hundreds of files,
 |       // TODO is it better to do this lazy-like or as a batch?
 | ||||||
|     // likely within 2 or 3 directories
 |       // I figure as batch when there may be hundreds of files,
 | ||||||
|     return makeAllDirs(Object.keys(dirpaths)).then(function (errors) { |       // likely within 2 or 3 directories
 | ||||||
|       errors.forEach(function (e) { |       return makeAllDirs(Object.keys(dirpaths)).then(function (errors) { | ||||||
|         results.errors.push(e); |         errors.forEach(function (e) { | ||||||
|       }); |           results.errors.push(e); | ||||||
|     }); |  | ||||||
|   }).then(function () { |  | ||||||
|     // TODO allow delete?
 |  | ||||||
|     return forEachAsync(sources, function (source) { |  | ||||||
|       return fsExtra.copyAsync( |  | ||||||
|         safeResolve(blogdir, source) |  | ||||||
|       , safeResolve(blogdir, files[source]) |  | ||||||
|       , { replace: true } |  | ||||||
|       ).catch(function (e) { |  | ||||||
|         results.errors.push({ |  | ||||||
|           type: 'file' |  | ||||||
| 
 |  | ||||||
|         , source: source |  | ||||||
|         , destination: files[source] |  | ||||||
| 
 |  | ||||||
|         , message: e.message |  | ||||||
|         , code: e.code |  | ||||||
|         , errno: e.errno |  | ||||||
|         , status: e.status |  | ||||||
|         , syscall: e.syscall |  | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
|  |     }) | ||||||
|  |     .then(function () { | ||||||
|  |       // TODO allow delete?
 | ||||||
|  |       return forEachAsync(sources, function (source) { | ||||||
|  |         return copyAll( | ||||||
|  |             safeResolve(blogdir, source), | ||||||
|  |             safeResolve(blogdir, files[source]), | ||||||
|  |             { replace: true } | ||||||
|  |           ) | ||||||
|  |           .catch(function (e) { | ||||||
|  |             results.errors.push({ | ||||||
|  |               type: "file", | ||||||
|  | 
 | ||||||
|  |               source: source, | ||||||
|  |               destination: files[source], | ||||||
|  | 
 | ||||||
|  |               message: e.message, | ||||||
|  |               code: e.code, | ||||||
|  |               errno: e.errno, | ||||||
|  |               status: e.status, | ||||||
|  |               syscall: e.syscall, | ||||||
|  |             }); | ||||||
|  |           }); | ||||||
|  |       }); | ||||||
|  |     }) | ||||||
|  |     .catch(function (e) { | ||||||
|  |       results.error = { | ||||||
|  |         message: e.message, | ||||||
|  |         code: e.code, | ||||||
|  |         errno: e.errno, | ||||||
|  |         status: e.status, | ||||||
|  |         syscall: e.syscall, | ||||||
|  |       }; | ||||||
|  |     }) | ||||||
|  |     .then(function () { | ||||||
|  |       return results; | ||||||
|     }); |     }); | ||||||
|   }).catch(function (e) { |  | ||||||
|     results.error = { |  | ||||||
|       message: e.message |  | ||||||
|     , code: e.code |  | ||||||
|     , errno: e.errno |  | ||||||
|     , status: e.status |  | ||||||
|     , syscall: e.syscall |  | ||||||
|     }; |  | ||||||
|   }).then(function () { |  | ||||||
|     return results; |  | ||||||
|   }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function putfs(blogdir, files, options) { | function putfs(blogdir, files, options) { | ||||||
|   options = options || {}; |   options = options || {}; | ||||||
| 
 | 
 | ||||||
|   var putfsResults = { errors: [] } |   var putfsResults = { errors: [] }, | ||||||
|     , dirpaths = {} |     dirpaths = {}; | ||||||
|     ; |  | ||||||
| 
 |  | ||||||
|   return forEachAsync(files, function (file) { |   return forEachAsync(files, function (file) { | ||||||
|     var filepath = safeResolve(blogdir, file.path || path.join(file.relativePath, file.name)) |     var filepath = safeResolve( | ||||||
|       , pathname = path.dirname(filepath) |         blogdir, | ||||||
|       , filename = file.name || path.basename(filepath) |         file.path || path.join(file.relativePath, file.name) | ||||||
|       ; |       ), | ||||||
| 
 |       pathname = path.dirname(filepath), | ||||||
|  |       filename = file.name || path.basename(filepath); | ||||||
|     file.realPath = filepath; |     file.realPath = filepath; | ||||||
|     file.name = filename; |     file.name = filename; | ||||||
| 
 | 
 | ||||||
|     dirpaths[pathname] = true; |     dirpaths[pathname] = true; | ||||||
| 
 | 
 | ||||||
|     return PromiseA.resolve(); |     return Promise.resolve(); | ||||||
|   }).then(function () { |   }) | ||||||
|     // TODO is it better to do this lazy-like or as a batch?
 |     .then(function () { | ||||||
|     // I figure as batch when there may be hundreds of files,
 |       // TODO is it better to do this lazy-like or as a batch?
 | ||||||
|     // likely within 2 or 3 directories
 |       // I figure as batch when there may be hundreds of files,
 | ||||||
|     return forEachAsync(Object.keys(dirpaths), function (pathname) { |       // likely within 2 or 3 directories
 | ||||||
|       return mkdirp(pathname).catch(function (e) { |       return forEachAsync(Object.keys(dirpaths), function (pathname) { | ||||||
|         // TODO exclude attempting to write files to this dir?
 |         return fs.mkdir(pathname, { recursive: true }).catch(function (e) { | ||||||
|         putfsResults.errors.push({ |           // TODO exclude attempting to write files to this dir?
 | ||||||
|           type: 'directory' |           putfsResults.errors.push({ | ||||||
|  |             type: "directory", | ||||||
| 
 | 
 | ||||||
|         , directory: pathname |             directory: pathname, | ||||||
| 
 | 
 | ||||||
|         , message: e.message |             message: e.message, | ||||||
|         , code: e.code |             code: e.code, | ||||||
|         , errno: e.errno |             errno: e.errno, | ||||||
|         , status: e.status |             status: e.status, | ||||||
|         , syscall: e.syscall |             syscall: e.syscall, | ||||||
|         }); |           }); | ||||||
| 
 |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   }).then(function () { |  | ||||||
|     // TODO sort deletes last
 |  | ||||||
|     return forEachAsync(files, function (file) { |  | ||||||
|       // TODO use lastModifiedDate as per client request?
 |  | ||||||
|       // TODO compare sha1 sums for integrity
 |  | ||||||
|       // NOTE existsAsync is backwards
 |  | ||||||
|       return fs.existsAsync(file.realPath).then(function () { |  | ||||||
|         return fs.writeFileAsync(file.realPath, file.contents, 'utf8'); |  | ||||||
|       }).catch(function (/*exists*/) { |  | ||||||
|         if (file.delete || !file.contents) { |  | ||||||
|           return fs.unlinkAsync(file.realPath); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (false === options.replace || false === options.overwrite) { |  | ||||||
|           throw new Error('EEXIST: the file already exists'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return fs.writeFileAsync(file.realPath, file.contents, 'utf8'); |  | ||||||
|       }).catch(function (e) { |  | ||||||
|         putfsResults.errors.push({ |  | ||||||
|           type: 'file' |  | ||||||
| 
 |  | ||||||
|         , file: file.realPath |  | ||||||
|         , delete: !file.contents |  | ||||||
|         , path: file.path |  | ||||||
|         , relativePath: file.relativePath |  | ||||||
|         , name: file.name |  | ||||||
| 
 |  | ||||||
|         , message: e.message |  | ||||||
|         , code: e.code |  | ||||||
|         , errno: e.errno |  | ||||||
|         , status: e.status |  | ||||||
|         , syscall: e.syscall |  | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
|  |     }) | ||||||
|  |     .then(function () { | ||||||
|  |       // TODO sort deletes last
 | ||||||
|  |       return forEachAsync(files, function (file) { | ||||||
|  |         // TODO use lastModifiedDate as per client request?
 | ||||||
|  |         // TODO compare sha1 sums for integrity
 | ||||||
|  |         return fs | ||||||
|  |           .access(file.realPath) | ||||||
|  |           .then(function () { | ||||||
|  |             if (file.delete || !file.contents) { | ||||||
|  |               return fs.unlink(file.realPath); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (false === options.replace || false === options.overwrite) { | ||||||
|  |               throw new Error("EEXIST: the file already exists"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return fs.writeFile(file.realPath, file.contents, "utf8"); | ||||||
|  |           }) | ||||||
|  |           .catch(function () { | ||||||
|  |             return fs.writeFile(file.realPath, file.contents, "utf8"); | ||||||
|  |           }) | ||||||
|  |           .catch(function (e) { | ||||||
|  |             putfsResults.errors.push({ | ||||||
|  |               type: "file", | ||||||
|  | 
 | ||||||
|  |               file: file.realPath, | ||||||
|  |               delete: !file.contents, | ||||||
|  |               path: file.path, | ||||||
|  |               relativePath: file.relativePath, | ||||||
|  |               name: file.name, | ||||||
|  | 
 | ||||||
|  |               message: e.message, | ||||||
|  |               code: e.code, | ||||||
|  |               errno: e.errno, | ||||||
|  |               status: e.status, | ||||||
|  |               syscall: e.syscall, | ||||||
|  |             }); | ||||||
|  |           }); | ||||||
|  |       }); | ||||||
|  |     }) | ||||||
|  |     .catch(function (e) { | ||||||
|  |       putfsResults.error = { | ||||||
|  |         message: e.message, | ||||||
|  |         code: e.code, | ||||||
|  |         errno: e.errno, | ||||||
|  |         status: e.status, | ||||||
|  |         syscall: e.syscall, | ||||||
|  |       }; | ||||||
|  |     }) | ||||||
|  |     .then(function () { | ||||||
|  |       return putfsResults; | ||||||
|     }); |     }); | ||||||
|   }).catch(function (e) { |  | ||||||
|     putfsResults.error = { |  | ||||||
|       message: e.message |  | ||||||
|     , code: e.code |  | ||||||
|     , errno: e.errno |  | ||||||
|     , status: e.status |  | ||||||
|     , syscall: e.syscall |  | ||||||
|     }; |  | ||||||
|   }).then(function () { |  | ||||||
|     return putfsResults; |  | ||||||
|   }); |  | ||||||
| } | } | ||||||
| /* | /* | ||||||
| walkDirs('blog', ['posts'], { contents: false }).then(function (stats) { | walkDirs('blog', ['posts'], { contents: false }).then(function (stats) { | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| 'use strict'; | "use strict"; | ||||||
| 
 | 
 | ||||||
| exports.fsapi = require('./fsapi'); | exports.fsapi = require("./fsapi"); | ||||||
| exports.sha1sum = require('./sha1sum').sha1sum; | exports.sha1sum = require("./sha1sum").sha1sum; | ||||||
| exports.realFsapi = require('./fsapi-real'); | exports.realFsapi = require("./fsapi-real"); | ||||||
|  | |||||||
| @ -1,9 +1,7 @@ | |||||||
| 'use strict'; | "use strict"; | ||||||
| 
 | 
 | ||||||
| var PromiseA = require('bluebird').Promise | var secretutils = require("secret-utils"); | ||||||
|   , secretutils = require('secret-utils') |  | ||||||
|   ; |  | ||||||
| 
 | 
 | ||||||
| module.exports.sha1sum = function (str) { | module.exports.sha1sum = function (str) { | ||||||
|   return PromiseA.resolve( secretutils.hashsum('sha1', str) ); |   return Promise.resolve(secretutils.hashsum("sha1", str)); | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,39 +1,35 @@ | |||||||
| /*jshint -W054 */ | /*jshint -W054 */ | ||||||
| ;(function (exports) { | (function (exports) { | ||||||
|   'use strict'; |   "use strict"; | ||||||
| 
 | 
 | ||||||
|   var PromiseA = exports.Promise || require('bluebird').Promise |   function renderMd(contentstr /*, desi*/) { | ||||||
|     ; |     var markitdown = (exports.markdownit || require("markdown-it"))({ | ||||||
| 
 |       html: true, | ||||||
|   function renderMd(contentstr/*, desi*/) { |       linkify: true, | ||||||
|     var markitdown = (exports.markdownit || require('markdown-it'))({ html: true, linkify: true }) |     }); | ||||||
|       ; |     return Promise.resolve( | ||||||
| 
 |  | ||||||
|     return PromiseA.resolve( |  | ||||||
|       markitdown.render(contentstr) |       markitdown.render(contentstr) | ||||||
|         //.replace('"', '"')
 |       //.replace('"', '"')
 | ||||||
|         //.replace(''', "'")
 |       //.replace(''', "'")
 | ||||||
|         //.replace('/', '/')
 |       //.replace('/', '/')
 | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function renderNoop(contentstr/*, desi*/) { |   function renderNoop(contentstr /*, desi*/) { | ||||||
|     // hmmm... that was easy
 |     // hmmm... that was easy
 | ||||||
|     return PromiseA.resolve(contentstr); |     return Promise.resolve(contentstr); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function renderJade(contentstr, desi, options) { |   function renderJade(contentstr, desi, options) { | ||||||
|     options = options || {}; |     options = options || {}; | ||||||
|     if (!('pretty' in options)) { |     if (!("pretty" in options)) { | ||||||
|       options.pretty = true; |       options.pretty = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var jade = (exports.jade || require('jade')) |     var jade = exports.jade || require("jade"), | ||||||
|       , fn = jade.compile(contentstr, options) |       fn = jade.compile(contentstr, options), | ||||||
|       , html = fn(desi) |       html = fn(desi); | ||||||
|       ; |     return Promise.resolve(html); | ||||||
| 
 |  | ||||||
|     return PromiseA.resolve(html); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   exports.DesiraeRenderMarkdown = renderMd.DesiraeRenderMarkdown = renderMd; |   exports.DesiraeRenderMarkdown = renderMd.DesiraeRenderMarkdown = renderMd; | ||||||
| @ -41,4 +37,4 @@ | |||||||
|   exports.DesiraeRenderCss = renderNoop.DesiraeRenderCss = renderNoop; |   exports.DesiraeRenderCss = renderNoop.DesiraeRenderCss = renderNoop; | ||||||
|   exports.DesiraeRenderJs = renderNoop.DesiraeRenderJs = renderNoop; |   exports.DesiraeRenderJs = renderNoop.DesiraeRenderJs = renderNoop; | ||||||
|   exports.DesiraeRenderJade = renderJade.DesiraeRenderJade = renderJade; |   exports.DesiraeRenderJade = renderJade.DesiraeRenderJade = renderJade; | ||||||
| }('undefined' !== typeof exports && exports || window)); | })(("undefined" !== typeof exports && exports) || window); | ||||||
|  | |||||||
| @ -1,141 +1,151 @@ | |||||||
| /*jshint -W054 */ | /*jshint -W054 */ | ||||||
| ;(function (exports) { | (function (exports) { | ||||||
|   'use strict'; |   "use strict"; | ||||||
| 
 |  | ||||||
|   var cores = {} |  | ||||||
|     , Desi = exports.Desirae || require('desirae').Desirae |  | ||||||
|     , path          = exports.path          || require('path') |  | ||||||
|     ; |  | ||||||
| 
 | 
 | ||||||
|  |   var cores = {}, | ||||||
|  |     Desi = exports.Desirae || require("desirae").Desirae, | ||||||
|  |     path = exports.path || require("path"); | ||||||
|   cores.lint = function (desi, env, collection, entity) { |   cores.lint = function (desi, env, collection, entity) { | ||||||
|     // TODO splice
 |     // TODO splice
 | ||||||
|     //desi.content.collections = desi.content.collections.filter(function (entity) {
 |     //desi.content.collections = desi.content.collections.filter(function (entity) {
 | ||||||
|       // TODO throw for any files that don't have a registered renderer
 |     // TODO throw for any files that don't have a registered renderer
 | ||||||
|       if (!entity.yml) { |     if (!entity.yml) { | ||||||
|         if (!desi.config.empty_frontmatter) { |       if (!desi.config.empty_frontmatter) { | ||||||
|           throw new Error("no frontmatter for " + (entity.path || entity.name) + "." |         throw new Error( | ||||||
|             + "Set `config.yml.empty_frontmatter: include|skip` to ignore this error." |           "no frontmatter for " + | ||||||
|           ); |             (entity.path || entity.name) + | ||||||
|         } |             "." + | ||||||
| 
 |             "Set `config.yml.empty_frontmatter: include|skip` to ignore this error." | ||||||
|         if ('include' === desi.config.empty_frontmatter) { |         ); | ||||||
|           entity.yml = {}; |  | ||||||
|         } |  | ||||||
|         else if ('skip' === desi.config.empty_frontmatter) { |  | ||||||
|           return false; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|           throw new Error('unrecognize option ' + desi.config.empty_frontmatter +  |  | ||||||
|             ' for `config.yml.empty_frontmatter: include|skip`.'); |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (!entity.body || !entity.body.trim()) { |       if ("include" === desi.config.empty_frontmatter) { | ||||||
|         if (!desi.config.empty_body) { |         entity.yml = {}; | ||||||
|           throw new Error('empty content file ' + (entity.path || entity.name) |       } else if ("skip" === desi.config.empty_frontmatter) { | ||||||
|             + '. Set `config.yml.empty_body: include|skip` to ignore this error.' |         return false; | ||||||
|           ); |       } else { | ||||||
|         } |         throw new Error( | ||||||
|  |           "unrecognize option " + | ||||||
|  |             desi.config.empty_frontmatter + | ||||||
|  |             " for `config.yml.empty_frontmatter: include|skip`." | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|         if ('include' === desi.config.empty_body) { |     if (!entity.body || !entity.body.trim()) { | ||||||
|           entity.body = ''; |       if (!desi.config.empty_body) { | ||||||
|         } |         throw new Error( | ||||||
|         else if ('skip' === desi.config.empty_body) { |           "empty content file " + | ||||||
|           return false; |             (entity.path || entity.name) + | ||||||
|         } |             ". Set `config.yml.empty_body: include|skip` to ignore this error." | ||||||
|         else { |         ); | ||||||
|           throw new Error('unrecognize option ' + desi.config.empty_frontmatter +  |  | ||||||
|             ' for `config.yml.empty_body: include|skip`.'); |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       return true; |       if ("include" === desi.config.empty_body) { | ||||||
|  |         entity.body = ""; | ||||||
|  |       } else if ("skip" === desi.config.empty_body) { | ||||||
|  |         return false; | ||||||
|  |       } else { | ||||||
|  |         throw new Error( | ||||||
|  |           "unrecognize option " + | ||||||
|  |             desi.config.empty_frontmatter + | ||||||
|  |             " for `config.yml.empty_body: include|skip`." | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|     //});
 |     //});
 | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   cores.root = function (desi, env, collection, entity) { |   cores.root = function (desi, env, collection, entity) { | ||||||
|     entity.yml = entity.yml || {}; |     entity.yml = entity.yml || {}; | ||||||
| 
 | 
 | ||||||
|     entity.layout = entity.yml.layout || '__page__'; |     entity.layout = entity.yml.layout || "__page__"; | ||||||
| 
 | 
 | ||||||
|     // _root is not subject to the same permalink rules as collections,
 |     // _root is not subject to the same permalink rules as collections,
 | ||||||
|     // so we just go ahead and define that here
 |     // so we just go ahead and define that here
 | ||||||
|     if (/^index\.\w+$/.test(entity.path)) { |     if (/^index\.\w+$/.test(entity.path)) { | ||||||
|       entity.permalink = '/'; |       entity.permalink = "/"; | ||||||
|     } else { |     } else { | ||||||
|       entity.permalink = entity.yml.permalink || entity.path.replace(/\.\w+$/, '/'); |       entity.permalink = | ||||||
|  |         entity.yml.permalink || entity.path.replace(/\.\w+$/, "/"); | ||||||
|       entity.redirects = entity.redirects || []; |       entity.redirects = entity.redirects || []; | ||||||
|       entity.redirects.push(entity.permalink.replace(/\/$/, '/index.html')); |       entity.redirects.push(entity.permalink.replace(/\/$/, "/index.html")); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   cores.normalize = function (desi, env, collection, entity) { |   cores.normalize = function (desi, env, collection, entity) { | ||||||
|     entity.title          = entity.yml.title || Desi.firstCap(entity.name.replace(/\.\w+$/, '')); |     entity.title = | ||||||
|     entity.date           = entity.yml.date; |       entity.yml.title || Desi.firstCap(entity.name.replace(/\.\w+$/, "")); | ||||||
|  |     entity.date = entity.yml.date; | ||||||
| 
 | 
 | ||||||
|     if (!entity.date) { |     if (!entity.date) { | ||||||
|       // TODO tell YAML parser to keep the date a string
 |       // TODO tell YAML parser to keep the date a string
 | ||||||
|       entity.date = new Date(entity.yml.created_at |       entity.date = new Date( | ||||||
|         || entity.yml.time |         entity.yml.created_at || | ||||||
|         || entity.yml.updated_at |           entity.yml.time || | ||||||
|         || entity.createdDate |           entity.yml.updated_at || | ||||||
|         || entity.lastModifiedDate |           entity.createdDate || | ||||||
|  |           entity.lastModifiedDate | ||||||
|       ).toISOString(); |       ).toISOString(); | ||||||
|     } |     } | ||||||
|     if ('object' === typeof entity.date) { |     if ("object" === typeof entity.date) { | ||||||
|       entity.date = entity.date.toISOString(); |       entity.date = entity.date.toISOString(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     entity.updated_at     = entity.yml.updated_at || entity.lastModifiedDate; |     entity.updated_at = entity.yml.updated_at || entity.lastModifiedDate; | ||||||
| 
 | 
 | ||||||
|     entity.published_at   = Desi.fromLocaleDate(entity.date || entity.lastModifiedDate); |     entity.published_at = Desi.fromLocaleDate( | ||||||
|     entity.year           = entity.published_at.year; |       entity.date || entity.lastModifiedDate | ||||||
|     entity.month          = entity.published_at.month; |     ); | ||||||
|     entity.day            = entity.published_at.day; |     entity.year = entity.published_at.year; | ||||||
|     entity.hour           = entity.published_at.hour; |     entity.month = entity.published_at.month; | ||||||
|     entity.twelve_hour    = entity.published_at.twelve_hour; |     entity.day = entity.published_at.day; | ||||||
|     entity.meridian       = entity.published_at.meridian; |     entity.hour = entity.published_at.hour; | ||||||
|     entity.minute         = entity.published_at.minute; |     entity.twelve_hour = entity.published_at.twelve_hour; | ||||||
|  |     entity.meridian = entity.published_at.meridian; | ||||||
|  |     entity.minute = entity.published_at.minute; | ||||||
|     // let's just agree that that's too far
 |     // let's just agree that that's too far
 | ||||||
|     //entity.second = entity.published_at.second;
 |     //entity.second = entity.published_at.second;
 | ||||||
| 
 | 
 | ||||||
|     entity.slug           = Desi.slugify(entity.title); |     entity.slug = Desi.slugify(entity.title); | ||||||
|     entity.slug_path      = Desi.slugifyPath(entity.relativePath); |     entity.slug_path = Desi.slugifyPath(entity.relativePath); | ||||||
|     entity.slugPath       = Desi.slugifyPath(entity.relativePath); |     entity.slugPath = Desi.slugifyPath(entity.relativePath); | ||||||
|     // TODO type checking like below
 |     // TODO type checking like below
 | ||||||
|     entity.redirects      = Array.isArray(entity.yml.redirects) && entity.yml.redirects|| []; |     entity.redirects = | ||||||
|  |       (Array.isArray(entity.yml.redirects) && entity.yml.redirects) || []; | ||||||
| 
 | 
 | ||||||
|     // categories
 |     // categories
 | ||||||
|     if (Array.isArray(entity.yml.categories)) { |     if (Array.isArray(entity.yml.categories)) { | ||||||
|       entity.categories   = entity.yml.categories; |       entity.categories = entity.yml.categories; | ||||||
|     } |     } else if ("string" === typeof entity.yml.categories) { | ||||||
|     else if ('string' === typeof entity.yml.categories) { |       entity.categories = [entity.yml.categories]; | ||||||
|       entity.categories   = [entity.yml.categories]; |     } else if ("string" === typeof entity.yml.category) { | ||||||
|     } |       entity.categories = [entity.yml.category]; | ||||||
|     else if ('string' === typeof entity.yml.category) { |     } else { | ||||||
|       entity.categories   = [entity.yml.category]; |       entity.categories = []; | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|       entity.categories   = []; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // tags
 |     // tags
 | ||||||
|     if (Array.isArray(entity.yml.tags)) { |     if (Array.isArray(entity.yml.tags)) { | ||||||
|       entity.tags         = entity.yml.tags; |       entity.tags = entity.yml.tags; | ||||||
|     } |     } else if ("string" === typeof entity.yml.tags) { | ||||||
|     else if ('string' === typeof entity.yml.tags) { |       entity.tags = [entity.yml.tags]; | ||||||
|       entity.tags         = [entity.yml.tags]; |     } else { | ||||||
|     } |       entity.tags = []; | ||||||
|     else { |  | ||||||
|       entity.tags         = []; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     entity.permalink      = entity.permalink || entity.yml.permalink; |     entity.permalink = entity.permalink || entity.yml.permalink; | ||||||
| 
 | 
 | ||||||
|     if (!entity.permalink) { |     if (!entity.permalink) { | ||||||
|       // try the fallback_permalink first (because we're looking at files that don't have yml)
 |       // try the fallback_permalink first (because we're looking at files that don't have yml)
 | ||||||
|       // then try the normal permalink (because :filename -> :title and whatnot, so it'll work)
 |       // then try the normal permalink (because :filename -> :title and whatnot, so it'll work)
 | ||||||
|       entity.permalink = Desi.permalinkify(desi, collection.fallback_permalink || collection.permalink, entity); |       entity.permalink = Desi.permalinkify( | ||||||
|  |         desi, | ||||||
|  |         collection.fallback_permalink || collection.permalink, | ||||||
|  |         entity | ||||||
|  |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* |     /* | ||||||
| @ -145,38 +155,51 @@ | |||||||
|     */ |     */ | ||||||
| 
 | 
 | ||||||
|     // relative to the site
 |     // relative to the site
 | ||||||
|     entity.relative_file  = path.join(env.base_path, entity.permalink) |     entity.relative_file = path | ||||||
|                               .replace(/\/$/, '/index.html'); |       .join(env.base_path, entity.permalink) | ||||||
|     entity.relative_href  = path.join(env.base_path, entity.permalink) |       .replace(/\/$/, "/index.html"); | ||||||
|                               .replace(/\/index\.html$/, '/'); |     entity.relative_href = path | ||||||
|     entity.relative_link  = entity.relative_href; |       .join(env.base_path, entity.permalink) | ||||||
|  |       .replace(/\/index\.html$/, "/"); | ||||||
|  |     entity.relative_link = entity.relative_href; | ||||||
| 
 | 
 | ||||||
|     entity.url            = env.base_url + path.join(env.base_path, entity.permalink) |     entity.url = | ||||||
|                               .replace(/\/index\.html$/, '/'); |       env.base_url + | ||||||
|     entity.canonical_url  = env.base_url + path.join(env.base_path, entity.permalink) |       path.join(env.base_path, entity.permalink).replace(/\/index\.html$/, "/"); | ||||||
|                               .replace(/\/index\.html$/, '/'); |     entity.canonical_url = | ||||||
|     entity.production_url = desi.site.base_url + path.join(desi.site.base_path, entity.permalink) |       env.base_url + | ||||||
|                               .replace(/\/index\.html$/, '/'); |       path.join(env.base_path, entity.permalink).replace(/\/index\.html$/, "/"); | ||||||
|     entity.relative_url   = path.join(env.base_path, entity.permalink) |     entity.production_url = | ||||||
|                               .replace(/\/index\.html$/, '/'); |       desi.site.base_url + | ||||||
|  |       path | ||||||
|  |         .join(desi.site.base_path, entity.permalink) | ||||||
|  |         .replace(/\/index\.html$/, "/"); | ||||||
|  |     entity.relative_url = path | ||||||
|  |       .join(env.base_path, entity.permalink) | ||||||
|  |       .replace(/\/index\.html$/, "/"); | ||||||
| 
 | 
 | ||||||
|     if (env.explicitIndexes || env.explicitIndices || env.explicit_indexes || env.explicit_indices) { |     if ( | ||||||
|  |       env.explicitIndexes || | ||||||
|  |       env.explicitIndices || | ||||||
|  |       env.explicit_indexes || | ||||||
|  |       env.explicit_indices | ||||||
|  |     ) { | ||||||
|       // NOTE: file_url is NOT replaced
 |       // NOTE: file_url is NOT replaced
 | ||||||
|       ['url', 'canonical_url', 'production_url', 'relative_url'].forEach(function (url) { |       ["url", "canonical_url", "production_url", "relative_url"].forEach( | ||||||
|         entity[url] = entity[url].replace(/\/$/, '/index.html'); |         function (url) { | ||||||
|       }); |           entity[url] = entity[url].replace(/\/$/, "/index.html"); | ||||||
|  |         } | ||||||
|  |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // i.e. bootstrap, hero page, darkly
 |     // i.e. bootstrap, hero page, darkly
 | ||||||
|     entity.theme          = entity.theme || entity.yml.theme; |     entity.theme = entity.theme || entity.yml.theme; | ||||||
|     entity.layout         = entity.layout || entity.yml.layout; |     entity.layout = entity.layout || entity.yml.layout; | ||||||
|     entity.swatch         = entity.swatch || entity.yml.swatch; |     entity.swatch = entity.swatch || entity.yml.swatch; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   cores.disqus = function (desi, env, collection, entity) { |   cores.disqus = function (desi, env, collection, entity) { | ||||||
|     var yml = entity.yml |     var yml = entity.yml; | ||||||
|       ; |  | ||||||
| 
 |  | ||||||
|     if (yml.uuid) { |     if (yml.uuid) { | ||||||
|       entity.disqus_identifier = yml.uuid; |       entity.disqus_identifier = yml.uuid; | ||||||
|     } |     } | ||||||
| @ -184,4 +207,4 @@ | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   exports.DesiraeTransformCore = cores.DesiraeTransformCore = cores; |   exports.DesiraeTransformCore = cores.DesiraeTransformCore = cores; | ||||||
| }('undefined' !== typeof exports && exports || window)); | })(("undefined" !== typeof exports && exports) || window); | ||||||
|  | |||||||
							
								
								
									
										101
									
								
								lib/utils.js
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								lib/utils.js
									
									
									
									
									
								
							| @ -1,9 +1,7 @@ | |||||||
| ;(function (exports) { | (function (exports) { | ||||||
|   'use strict'; |   "use strict"; | ||||||
| 
 |  | ||||||
|   var path  = exports.path  || require('path') |  | ||||||
|     ; |  | ||||||
| 
 | 
 | ||||||
|  |   var path = exports.path || require("path"); | ||||||
|   function escapeRegExp(str) { |   function escapeRegExp(str) { | ||||||
|     return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); |     return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); | ||||||
|   } |   } | ||||||
| @ -11,10 +9,8 @@ | |||||||
|   function safeResolve(basename, target) { |   function safeResolve(basename, target) { | ||||||
|     basename = path.resolve(basename); |     basename = path.resolve(basename); | ||||||
| 
 | 
 | ||||||
|     var targetname = path.resolve(basename, target) |     var targetname = path.resolve(basename, target), | ||||||
|       , re = new RegExp('^' + escapeRegExp(basename) + '(/|$)') |       re = new RegExp("^" + escapeRegExp(basename) + "(/|$)"); | ||||||
|       ; |  | ||||||
| 
 |  | ||||||
|     return re.test(targetname) && targetname; |     return re.test(targetname) && targetname; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -22,29 +18,24 @@ | |||||||
|   exports.escapeRegExp = escapeRegExp; |   exports.escapeRegExp = escapeRegExp; | ||||||
| 
 | 
 | ||||||
|   function create(Desi) { |   function create(Desi) { | ||||||
|     var fsapi = Desi.fsapi || require('./node-adapters').fsapi |     var fsapi = Desi.fsapi || require("./node-adapters").fsapi; | ||||||
|       ; |  | ||||||
| 
 |  | ||||||
|     fsapi.getConfigs = function (confs) { |     fsapi.getConfigs = function (confs) { | ||||||
|       var opts = { extensions: ['yml', 'yaml', 'json'], dotfiles: false, contents: true, sha1sum: true } |       var opts = { | ||||||
|         ; |         extensions: ["yml", "yaml", "json"], | ||||||
| 
 |         dotfiles: false, | ||||||
|  |         contents: true, | ||||||
|  |         sha1sum: true, | ||||||
|  |       }; | ||||||
|       return fsapi.getMeta(confs, opts).then(function (collections) { |       return fsapi.getMeta(confs, opts).then(function (collections) { | ||||||
|         var obj = {} |         var obj = {}; | ||||||
|           ; |  | ||||||
| 
 |  | ||||||
|         Object.keys(collections).forEach(function (key) { |         Object.keys(collections).forEach(function (key) { | ||||||
|           var files = collections[key] |           var files = collections[key], | ||||||
|             , keyname = key.replace(/\.(json|ya?ml|\/)$/i, '') |             keyname = key.replace(/\.(json|ya?ml|\/)$/i, ""); | ||||||
|             ; |  | ||||||
| 
 |  | ||||||
|           obj[keyname] = obj[keyname] || {}; |           obj[keyname] = obj[keyname] || {}; | ||||||
| 
 | 
 | ||||||
|           files.forEach(function (file) { |           files.forEach(function (file) { | ||||||
|             var filename = file.name.replace(/\.(json|ya?ml)$/i, '') |             var filename = file.name.replace(/\.(json|ya?ml)$/i, ""), | ||||||
|               , data = {} |               data = {}; | ||||||
|               ; |  | ||||||
| 
 |  | ||||||
|             if (file.error) { |             if (file.error) { | ||||||
|               console.error(file); |               console.error(file); | ||||||
|               console.error(file.error); |               console.error(file.error); | ||||||
| @ -57,17 +48,16 @@ | |||||||
|                 if ("undefined" === data) { |                 if ("undefined" === data) { | ||||||
|                   data = {}; |                   data = {}; | ||||||
|                 } |                 } | ||||||
|               } catch(e) { |               } catch (e) { | ||||||
|                 data = { error: e }; |                 data = { error: e }; | ||||||
|                 console.error("Could not parse yaml for " + filename); |                 console.error("Could not parse yaml for " + filename); | ||||||
|                 console.error(file); |                 console.error(file); | ||||||
|                 console.error(e); |                 console.error(e); | ||||||
|               } |               } | ||||||
|             } |             } else if (/\.(json)$/i.test(file.name)) { | ||||||
|             else if (/\.(json)$/i.test(file.name)) { |  | ||||||
|               try { |               try { | ||||||
|                 data = JSON.parse(file.contents) || {}; |                 data = JSON.parse(file.contents) || {}; | ||||||
|               } catch(e) { |               } catch (e) { | ||||||
|                 data = { error: e }; |                 data = { error: e }; | ||||||
|                 console.error("Could not parse json for " + filename); |                 console.error("Could not parse json for " + filename); | ||||||
|                 console.error(file); |                 console.error(file); | ||||||
| @ -99,36 +89,34 @@ | |||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     fsapi.getAllPartials = function () { |     fsapi.getAllPartials = function () { | ||||||
|       return fsapi.getConfigs(['partials', 'partials.yml']).then(function (results) { |       return fsapi | ||||||
|         var partials = {} |         .getConfigs(["partials", "partials.yml"]) | ||||||
|           ; |         .then(function (results) { | ||||||
|  |           var partials = {}; | ||||||
|  |           Object.keys(results.partials).forEach(function (key) { | ||||||
|  |             var partial = results.partials[key]; | ||||||
|  |             Object.keys(partial).forEach(function (prop) { | ||||||
|  |               if (partials[prop]) { | ||||||
|  |                 console.warn("partial '" + prop + "' overwritten by " + key); | ||||||
|  |               } | ||||||
| 
 | 
 | ||||||
|         Object.keys(results.partials).forEach(function (key) { |               partials[prop] = partial[prop]; | ||||||
|           var partial = results.partials[key] |             }); | ||||||
|             ; |  | ||||||
| 
 |  | ||||||
|           Object.keys(partial).forEach(function (prop) { |  | ||||||
|             if (partials[prop]) { |  | ||||||
|               console.warn('partial \'' + prop + '\' overwritten by ' + key); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             partials[prop] = partial[prop]; |  | ||||||
|           }); |           }); | ||||||
|         }); |  | ||||||
| 
 | 
 | ||||||
|         return partials; |           return partials; | ||||||
|       }); |         }); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     fsapi.getAllConfigFiles = function () { |     fsapi.getAllConfigFiles = function () { | ||||||
|       return fsapi.getConfigs(['config.yml', 'site.yml', 'authors']).then(function (results) { |       return fsapi | ||||||
|         var authors = results.authors |         .getConfigs(["config.yml", "site.yml", "authors"]) | ||||||
|           , config = results.config.config |         .then(function (results) { | ||||||
|           , site = results.site.site |           var authors = results.authors, | ||||||
|           ; |             config = results.config.config, | ||||||
| 
 |             site = results.site.site; | ||||||
|         return { config: config, authors: authors, site: site }; |           return { config: config, authors: authors, site: site }; | ||||||
|       }); |         }); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     return exports; |     return exports; | ||||||
| @ -136,8 +124,7 @@ | |||||||
| 
 | 
 | ||||||
|   if (exports.Desirae) { |   if (exports.Desirae) { | ||||||
|     create(exports.Desirae); |     create(exports.Desirae); | ||||||
|   } |   } else { | ||||||
|   else { |  | ||||||
|     exports.create = create; |     exports.create = create; | ||||||
|   } |   } | ||||||
| }('undefined' !== typeof exports && exports || window)); | })(("undefined" !== typeof exports && exports) || window); | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| ;(function (exports) { | (function (exports) { | ||||||
|   'use strict'; |   "use strict"; | ||||||
| 
 | 
 | ||||||
|   exports.verifyConfig = function (conf) { |   exports.verifyConfig = function (conf) { | ||||||
|     if (!conf.NuhohSpec) { |     if (!conf.NuhohSpec) { | ||||||
| @ -28,14 +28,18 @@ | |||||||
| 
 | 
 | ||||||
|     if (!Array.isArray(conf.collections)) { |     if (!Array.isArray(conf.collections)) { | ||||||
|       if (conf.posts) { |       if (conf.posts) { | ||||||
|         console.error("Please indent and nest 'posts' under the key 'collection' to continue"); |         console.error( | ||||||
|  |           "Please indent and nest 'posts' under the key 'collection' to continue" | ||||||
|  |         ); | ||||||
|       } |       } | ||||||
|       throw new Error("missing key 'collections'."); |       throw new Error("missing key 'collections'."); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!conf.themes) { |     if (!conf.themes) { | ||||||
|       if (conf.twitter) { |       if (conf.twitter) { | ||||||
|         console.error("Please indent and nest 'twitter' under the key 'themes' to continue"); |         console.error( | ||||||
|  |           "Please indent and nest 'twitter' under the key 'themes' to continue" | ||||||
|  |         ); | ||||||
|       } |       } | ||||||
|       throw new Error("missing key 'themes'"); |       throw new Error("missing key 'themes'"); | ||||||
|     } |     } | ||||||
| @ -55,4 +59,4 @@ | |||||||
|       throw new Error("missing key root"); |       throw new Error("missing key root"); | ||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
| }('undefined' !== typeof exports && exports || window)); | })(("undefined" !== typeof exports && exports) || window); | ||||||
|  | |||||||
							
								
								
									
										175
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,175 @@ | |||||||
|  | { | ||||||
|  |   "name": "desirae", | ||||||
|  |   "version": "0.12.4", | ||||||
|  |   "lockfileVersion": 1, | ||||||
|  |   "requires": true, | ||||||
|  |   "dependencies": { | ||||||
|  |     "argparse": { | ||||||
|  |       "version": "1.0.10", | ||||||
|  |       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", | ||||||
|  |       "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", | ||||||
|  |       "requires": { | ||||||
|  |         "sprintf-js": "~1.0.2" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "crypto-rand": { | ||||||
|  |       "version": "0.0.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/crypto-rand/-/crypto-rand-0.0.2.tgz", | ||||||
|  |       "integrity": "sha1-Hn3CMQLhiRo+6zQPtwrElYCzLt0=" | ||||||
|  |     }, | ||||||
|  |     "escape-string-regexp": { | ||||||
|  |       "version": "1.0.5", | ||||||
|  |       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", | ||||||
|  |       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" | ||||||
|  |     }, | ||||||
|  |     "esprima": { | ||||||
|  |       "version": "4.0.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", | ||||||
|  |       "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" | ||||||
|  |     }, | ||||||
|  |     "foreachasync": { | ||||||
|  |       "version": "5.1.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-5.1.3.tgz", | ||||||
|  |       "integrity": "sha512-q3/l5D2pyZG8aYe9xZQDAnS/KyP7QJakLUNrU/qGkd4bQUTzCUmvVFjagRggoxxjtb1ZrJB+jQeG4ojOLWkztw==" | ||||||
|  |     }, | ||||||
|  |     "fs-extra": { | ||||||
|  |       "version": "0.6.4", | ||||||
|  |       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz", | ||||||
|  |       "integrity": "sha1-9G8MdbeEH40gCzNIzU1pHVoJnRU=", | ||||||
|  |       "requires": { | ||||||
|  |         "jsonfile": "~1.0.1", | ||||||
|  |         "mkdirp": "0.3.x", | ||||||
|  |         "ncp": "~0.4.2", | ||||||
|  |         "rimraf": "~2.2.0" | ||||||
|  |       }, | ||||||
|  |       "dependencies": { | ||||||
|  |         "mkdirp": { | ||||||
|  |           "version": "0.3.5", | ||||||
|  |           "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", | ||||||
|  |           "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "fs.extra": { | ||||||
|  |       "version": "1.3.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/fs.extra/-/fs.extra-1.3.2.tgz", | ||||||
|  |       "integrity": "sha1-3QI/kwE77iRTHxszUUw3sg/ZM0k=", | ||||||
|  |       "requires": { | ||||||
|  |         "fs-extra": "~0.6.1", | ||||||
|  |         "mkdirp": "~0.3.5", | ||||||
|  |         "walk": "^2.3.9" | ||||||
|  |       }, | ||||||
|  |       "dependencies": { | ||||||
|  |         "mkdirp": { | ||||||
|  |           "version": "0.3.5", | ||||||
|  |           "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", | ||||||
|  |           "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "js-yaml": { | ||||||
|  |       "version": "3.14.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", | ||||||
|  |       "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", | ||||||
|  |       "requires": { | ||||||
|  |         "argparse": "^1.0.7", | ||||||
|  |         "esprima": "^4.0.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "jsonfile": { | ||||||
|  |       "version": "1.0.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz", | ||||||
|  |       "integrity": "sha1-6l7+QLg2kLmGZ2FKc5L8YOhCwN0=" | ||||||
|  |     }, | ||||||
|  |     "linkify-it": { | ||||||
|  |       "version": "0.1.5", | ||||||
|  |       "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-0.1.5.tgz", | ||||||
|  |       "integrity": "sha1-OMWD0y+pPtcm2gDHrwAQeL+2uUU=", | ||||||
|  |       "requires": { | ||||||
|  |         "uc.micro": "^1.0.0" | ||||||
|  |       }, | ||||||
|  |       "dependencies": { | ||||||
|  |         "uc.micro": { | ||||||
|  |           "version": "1.0.6", | ||||||
|  |           "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", | ||||||
|  |           "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "markdown-it": { | ||||||
|  |       "version": "3.1.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-3.1.0.tgz", | ||||||
|  |       "integrity": "sha1-IKcejmexKXyWrEfQD3tuaQ1uDDY=", | ||||||
|  |       "requires": { | ||||||
|  |         "argparse": "~ 1.0.0", | ||||||
|  |         "linkify-it": "~ 0.1.2", | ||||||
|  |         "mdurl": "~ 1.0.0", | ||||||
|  |         "uc.micro": "~ 0.1.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "mdurl": { | ||||||
|  |       "version": "1.0.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", | ||||||
|  |       "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" | ||||||
|  |     }, | ||||||
|  |     "mustache": { | ||||||
|  |       "version": "2.3.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz", | ||||||
|  |       "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==" | ||||||
|  |     }, | ||||||
|  |     "ncp": { | ||||||
|  |       "version": "0.4.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", | ||||||
|  |       "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=" | ||||||
|  |     }, | ||||||
|  |     "node-uuid": { | ||||||
|  |       "version": "1.4.8", | ||||||
|  |       "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", | ||||||
|  |       "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" | ||||||
|  |     }, | ||||||
|  |     "rimraf": { | ||||||
|  |       "version": "2.2.8", | ||||||
|  |       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", | ||||||
|  |       "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" | ||||||
|  |     }, | ||||||
|  |     "secret-utils": { | ||||||
|  |       "version": "1.0.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/secret-utils/-/secret-utils-1.0.2.tgz", | ||||||
|  |       "integrity": "sha1-88GhRhCpTH+gMX3RVqj+AJgDb6c=", | ||||||
|  |       "requires": { | ||||||
|  |         "crypto-rand": "0.0.2", | ||||||
|  |         "urlsafe-base64": "0.0.2" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "sprintf-js": { | ||||||
|  |       "version": "1.0.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", | ||||||
|  |       "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" | ||||||
|  |     }, | ||||||
|  |     "uc.micro": { | ||||||
|  |       "version": "0.1.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-0.1.0.tgz", | ||||||
|  |       "integrity": "sha1-7aESHR/blhVO1v3oJHu724MzCMo=" | ||||||
|  |     }, | ||||||
|  |     "urlsafe-base64": { | ||||||
|  |       "version": "0.0.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-0.0.2.tgz", | ||||||
|  |       "integrity": "sha1-+VqmedXqb86RQtY8tXCd4x7r7UI=" | ||||||
|  |     }, | ||||||
|  |     "walk": { | ||||||
|  |       "version": "2.3.14", | ||||||
|  |       "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.14.tgz", | ||||||
|  |       "integrity": "sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg==", | ||||||
|  |       "requires": { | ||||||
|  |         "foreachasync": "^3.0.0" | ||||||
|  |       }, | ||||||
|  |       "dependencies": { | ||||||
|  |         "foreachasync": { | ||||||
|  |           "version": "3.0.0", | ||||||
|  |           "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", | ||||||
|  |           "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY=" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								package.json
									
									
									
									
									
								
							| @ -1,9 +1,10 @@ | |||||||
| { | { | ||||||
|   "name": "desirae", |   "name": "desirae", | ||||||
|   "version": "0.11.3", |   "version": "0.12.4", | ||||||
|   "description": "An in-browser static blog library and static site generator. Similar to Jekyll, Octopress, Nanoc, etc", |   "description": "An in-browser static blog library and static site generator. Similar to Jekyll, Octopress, Nanoc, etc", | ||||||
|   "main": "desirae.js", |   "main": "desirae.js", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|  |     "prettier": "prettier --write './**/*.{js,css,html,json,md,py,xml}'", | ||||||
|     "test": "echo \"Error: no test specified\" && exit 1" |     "test": "echo \"Error: no test specified\" && exit 1" | ||||||
|   }, |   }, | ||||||
|   "repository": { |   "repository": { | ||||||
| @ -22,21 +23,19 @@ | |||||||
|     "octopress", |     "octopress", | ||||||
|     "nanoc" |     "nanoc" | ||||||
|   ], |   ], | ||||||
|   "author": "AJ ONeal", |   "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)", | ||||||
|   "license": "Apache2", |   "license": "MPL-2.0", | ||||||
|   "bugs": { |   "bugs": { | ||||||
|     "url": "https://git.coolaj86.com/coolaj86/desirae.js/issues" |     "url": "https://git.coolaj86.com/coolaj86/desirae.js/issues" | ||||||
|   }, |   }, | ||||||
|   "homepage": "https://git.coolaj86.com/coolaj86/desirae.js", |   "homepage": "https://git.coolaj86.com/coolaj86/desirae.js", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "bluebird": "^2.5.3", |  | ||||||
|     "escape-string-regexp": "^1.0.2", |     "escape-string-regexp": "^1.0.2", | ||||||
|     "foreachasync": "^5.0.5", |     "foreachasync": "^5.0.5", | ||||||
|     "fs.extra": "^1.3.2", |     "fs.extra": "^1.3.2", | ||||||
|     "js-yaml": "^3.2.5", |     "js-yaml": "^3.2.5", | ||||||
|     "markdown-it": "^3.0.2", |     "markdown-it": "^3.0.2", | ||||||
|     "mkdirp": "^0.5.0", |     "mustache": "^2.3.2", | ||||||
|     "mustache": "^1.0.0", |  | ||||||
|     "node-uuid": "^1.4.2", |     "node-uuid": "^1.4.2", | ||||||
|     "secret-utils": "^1.0.2", |     "secret-utils": "^1.0.2", | ||||||
|     "walk": "^2.3.9" |     "walk": "^2.3.9" | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| 'use strict'; | "use strict"; | ||||||
| 
 | 
 | ||||||
| // http://ruhoh.com/docs/2/pages/#toc_41
 | // http://ruhoh.com/docs/2/pages/#toc_41
 | ||||||
| /* | /* | ||||||
| @ -6,32 +6,31 @@ | |||||||
|   Otherwise it should use the the permalink for that collection. |   Otherwise it should use the the permalink for that collection. | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
| var tags | var tags, | ||||||
|   , permalinkTransforms |   permalinkTransforms, | ||||||
|   , cases |   cases, | ||||||
|   , path    = /*exports.path ||*/ require('path') |   path = /*exports.path ||*/ require("path"); | ||||||
|   ; |  | ||||||
|    |  | ||||||
| tags = { | tags = { | ||||||
|   year:           "Year from the page’s filename" |   year: "Year from the page’s filename", | ||||||
| , month:          "Month from the page’s filename" |   month: "Month from the page’s filename", | ||||||
| , day:            "Day from the page’s filename" |   day: "Day from the page’s filename", | ||||||
| , path:           "The page file's path relative to the base of your website." |   path: "The page file's path relative to the base of your website.", | ||||||
| , relative_path:  "The page file's path relative to its name-spaced directory." |   relative_path: "The page file's path relative to its name-spaced directory.", | ||||||
| , filename:       "The page file's filename (path is not included)." |   filename: "The page file's filename (path is not included).", | ||||||
| , categories:     "The specified categories for this page. If more than one category is set, only the first one is used. If no categories exist, the URL omits this parameter." |   categories: | ||||||
| , i_month:        "Month from the page’s filename without leading zeros." |     "The specified categories for this page. If more than one category is set, only the first one is used. If no categories exist, the URL omits this parameter.", | ||||||
| , i_day:          "Day from the page’s filename without leading zeros." |   i_month: "Month from the page’s filename without leading zeros.", | ||||||
| , title:          "The title, as a slug." |   i_day: "Day from the page’s filename without leading zeros.", | ||||||
| , slug:           "alias of title" |   title: "The title, as a slug.", | ||||||
| , name:           "alias of title" |   slug: "alias of title", | ||||||
| , collection:     "i.e. posts/ or essays/ or whatever/" |   name: "alias of title", | ||||||
|  |   collection: "i.e. posts/ or essays/ or whatever/", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| function pad(str, n) { | function pad(str, n) { | ||||||
|   str = str.toString(); |   str = str.toString(); | ||||||
|   if (str.length < n) { |   if (str.length < n) { | ||||||
|     str = '0' + str; |     str = "0" + str; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return str; |   return str; | ||||||
| @ -39,49 +38,51 @@ function pad(str, n) { | |||||||
| 
 | 
 | ||||||
| // https://www.youtube.com/watch?v=1NryFD9_hR0&list=RDOeLUK4a6Ojc&index=2
 | // https://www.youtube.com/watch?v=1NryFD9_hR0&list=RDOeLUK4a6Ojc&index=2
 | ||||||
| cases = { | cases = { | ||||||
|   "/:title.html"                  : "/my-title.html" |   "/:title.html": "/my-title.html", | ||||||
| , ":title/"                       : "/my-title/" |   ":title/": "/my-title/", | ||||||
| , "/:bad/:title/"                 : "/:bad/my-title/" |   "/:bad/:title/": "/:bad/my-title/", | ||||||
| , "/:slug/"                       : "/my-title/" |   "/:slug/": "/my-title/", | ||||||
| , "/:path/:name.html"             : "/posts/fun/my-title.html" |   "/:path/:name.html": "/posts/fun/my-title.html", | ||||||
| , "/:relative_path/:name/"        : "/fun/my-title/" |   "/:relative_path/:name/": "/fun/my-title/", | ||||||
| , "/:year-:month-:day/:name"      : "/2015-07-04/my-title/" |   "/:year-:month-:day/:name": "/2015-07-04/my-title/", | ||||||
| , "/:year/:i_month/:i_day/:name"  : "/2015/7/4/my-title/" |   "/:year/:i_month/:i_day/:name": "/2015/7/4/my-title/", | ||||||
| , "/:filename.html"               : "/my-file-name.html" |   "/:filename.html": "/my-file-name.html", | ||||||
| , "/:filename"                    : "/my-file-name/" |   "/:filename": "/my-file-name/", | ||||||
| , "/:filename/"                   : "/my-file-name/" |   "/:filename/": "/my-file-name/", | ||||||
| , "/:collection/:title/"          : "/posts/my-title/" |   "/:collection/:title/": "/posts/my-title/", | ||||||
| , "/:collection/:filename"        : "/posts/my-file-name/" |   "/:collection/:filename": "/posts/my-file-name/", | ||||||
| , "/:something/:or/:other"        : "/:something/:or/:other/" |   "/:something/:or/:other": "/:something/:or/:other/", | ||||||
| , "/:categories/:title/"          : "/desi/my-title/" |   "/:categories/:title/": "/desi/my-title/", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Object.keys(cases).forEach(function (tpl) { | Object.keys(cases).forEach(function (tpl) { | ||||||
|   var entity |   var entity, tpld; | ||||||
|     , tpld |  | ||||||
|     ; |  | ||||||
| 
 | 
 | ||||||
|   entity = { |   entity = { | ||||||
|     year          : '2015' |     year: "2015", | ||||||
|   , month         : '07' |     month: "07", | ||||||
|   , day           : '04' |     day: "04", | ||||||
|   , title         : "My Title" |     title: "My Title", | ||||||
|   , slug          : "my-title" |     slug: "my-title", | ||||||
|   , name          : "My-File-Name.html" |     name: "My-File-Name.html", | ||||||
|   , relativePath  : "posts/fun" |     relativePath: "posts/fun", | ||||||
|   , path          : "posts/fun/My-File-Name.html" |     path: "posts/fun/My-File-Name.html", | ||||||
|   , collection    : "posts" |     collection: "posts", | ||||||
|   , yml           : { categories: ['desi'] } |     yml: { categories: ["desi"] }, | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   tpld = permalinker(tpl, entity); |   tpld = permalinker(tpl, entity); | ||||||
| 
 | 
 | ||||||
|   if (cases[tpl] !== tpld) { |   if (cases[tpl] !== tpld) { | ||||||
|     console.error('[ERROR]'); |     console.error("[ERROR]"); | ||||||
|     console.error(tpl + ' ' + tpld + ' ' + cases[tpl]); |     console.error(tpl + " " + tpld + " " + cases[tpl]); | ||||||
|     throw new Error( |     throw new Error( | ||||||
|       "Did not template permalink correctly. " |       "Did not template permalink correctly. " + | ||||||
|       + tpl + ' ' + tpld + ' ' + cases[tpl] |         tpl + | ||||||
|  |         " " + | ||||||
|  |         tpld + | ||||||
|  |         " " + | ||||||
|  |         cases[tpl] | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| }); | }); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user