diff --git a/.babelrc b/.babelrc index 06a818d..6a750ee 100644 --- a/.babelrc +++ b/.babelrc @@ -3,5 +3,5 @@ ["@babel/env", { "modules": false }] - ], + ] } \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..354624a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,34 @@ +# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs + +name: CMS.JS CI Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [12.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + # Install dependencies in a ci mode + - run: npm ci + # Check for issues and compile the base code + - run: npm run compile + # Minify the application (to ensure minification works without error) + - run: npm run minify \ No newline at end of file diff --git a/.gitignore b/.gitignore index b7c1c9f..e66ff3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .DS_Store node_modules/* -release/* +release/*.tgz *.swp npm-debug.log -examples/js/cms.js + diff --git a/README.md b/README.md index c7874cf..5593192 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,10 @@ CMS.js supports two website modes, Github and Server. Host your website on Githu 1. Clone the [starter repo](https://github.com/chrisdiana/cms.js-starter): `git clone https://github.com/chrisdiana/cms.js-starter.git` or download the [latest release here](https://github.com/chrisdiana/cms.js/releases/latest) 2. Configure `js/config.js` to your liking -3. Make sure to set your Github settings in `js/config.js` if using Github mode -4. If using Github mode, create a new branch from your master or working branch called `gh-pages` (Github's default branch for hosting) -5. Visit your site! (which should be located at `https://yourusername.github.io/cms.js-starter`) +3. Configure `.htaccess` to your web URL path if installed in a subdirectory +4. Make sure to set your Github settings in `js/config.js` if using Github mode +5. If using Github mode, create a new branch from your master or working branch called `gh-pages` (Github's default branch for hosting) +6. Visit your site! (which should be located at `https://yourusername.github.io/cms.js-starter`) ## CDN @@ -112,3 +113,19 @@ All forms of contribution are welcome: bug reports, bug fixes, pull requests and ## List of contributors You can find the list of contributors [here](https://github.com/chrisdiana/cms.js/graphs/contributors). + + +## Building + +### Instructions for Windows + +@todo, (I don't have windows, sorry) + +### Instructions for Debian/Ubuntu + +```bash +sudo apt install npm +# from within the cms.js project directory, +NODE_ENV=dev npm install +sudo a2enmod include rewrite +``` \ No newline at end of file diff --git a/dist/cms.es.js b/dist/cms.es.js index d710cd8..036adea 100644 --- a/dist/cms.es.js +++ b/dist/cms.es.js @@ -1,10 +1,9 @@ -/*! @chrisdiana/cmsjs v2.0.1 | MIT (c) 2021 Chris Diana | https://github.com/chrisdiana/cms.js */ +/*! @chrisdiana/cmsjs v2.0.1~cdp1337-20221105 | MIT (c) 2022 Chris Diana | https://github.com/chrisdiana/cms.js */ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; @@ -14,10 +13,12 @@ function _defineProperties(target, props) { Object.defineProperty(target, descriptor.key, descriptor); } } - function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); + Object.defineProperty(Constructor, "prototype", { + writable: false + }); return Constructor; } @@ -42,7 +43,9 @@ var defaults = { debug: false, messageClassName: 'cms-messages', onload: function onload() {}, - onroute: function onroute() {} + onroute: function onroute() {}, + webpath: '/', + titleSearchResults: 'Search Results' }; var messageContainer; @@ -54,12 +57,12 @@ var messages = { LAYOUT_LOAD_ERROR: 'ERROR: Error loading layout. Check the layout file to make sure it exists.', NOT_READY_WARNING: 'WARNING: Not ready to perform action' }; + /** * Creates message container element * @function * @param {string} classname - Container classname. */ - function createMessageContainer(classname) { messageContainer = document.createElement('div'); messageContainer.className = classname; @@ -69,6 +72,7 @@ function createMessageContainer(classname) { messageContainer.style.top = '0px'; document.body.appendChild(messageContainer); } + /** * Handle messages * @function @@ -77,8 +81,6 @@ function createMessageContainer(classname) { * @description * Used for debugging purposes. */ - - function handleMessage(debug, message) { if (debug) messageContainer.innerHTML = message; return message; @@ -94,19 +96,19 @@ function handleMessage(debug, message) { function get(url, callback) { var req = new XMLHttpRequest(); req.open('GET', url, true); - req.onreadystatechange = function () { if (req.readyState === 4) { if (req.status === 200) { - callback(req.response, false); + // Add support for returning the Last-Modified header for lazy timestamps + callback(req.response, false, req.getResponseHeader('Last-Modified')); } else { - callback(req, req.statusText); + callback(req, req.statusText, null); } } }; - req.send(); } + /** * Extend utility function for extending objects. * @function @@ -115,69 +117,48 @@ function get(url, callback) { * @param {function} callback - Callback function after completion. * @returns {object} Extended target object. */ - function extend(target, opts, callback) { var next; - if (typeof opts === 'undefined') { opts = target; } - for (next in opts) { if (Object.prototype.hasOwnProperty.call(opts, next)) { target[next] = opts[next]; } } - if (callback) { callback(); } - return target; } + /** * Utility function for getting a function name. * @function * @param {function} func - The function to get the name * @returns {string} Name of function. */ - function getFunctionName(func) { var ret = func.toString(); ret = ret.substr('function '.length); ret = ret.substr(0, ret.indexOf('(')); return ret; } + /** * Checks if the file URL with file extension is a valid file to load. * @function * @param {string} fileUrl - File URL * @returns {boolean} Is valid. */ - function isValidFile(fileUrl, extension) { if (fileUrl) { var ext = fileUrl.split('.').pop(); return ext === extension.replace('.', '') || ext === 'html' ? true : false; } } -/** - * Get URL paths without parameters. - * @function - * @returns {string} URL Path - */ - -function getPathsWithoutParameters() { - return window.location.hash.split('/').map(function (path) { - if (path.indexOf('?') >= 0) { - path = path.substring(0, path.indexOf('?')); - } - return path; - }).filter(function (path) { - return path !== '#'; - }); -} /** * Get URL parameter by name. * @function @@ -185,45 +166,45 @@ function getPathsWithoutParameters() { * @param {string} url - URL * @returns {string} Parameter value */ - function getParameterByName(name, url) { if (!url) url = window.location.href; name = name.replace(/[[]]/g, '\\$&'); var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), - results = regex.exec(url); + results = regex.exec(url); if (!results) return null; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, ' ')); } + /** * Get Github URL based on configuration. * @function * @param {string} type - Type of file. * @returns {string} GIthub URL */ - function getGithubUrl(type, gh) { var url = [gh.host, 'repos', gh.username, gh.repo, 'contents', type + '?ref=' + gh.branch]; if (gh.prefix) url.splice(5, 0, gh.prefix); return url.join('/'); } + /** * Formats date string to datetime * @param {string} dateString - Date string to convert. * @returns {object} Formatted datetime */ - function getDatetime(dateStr) { var dt = new Date(dateStr); return new Date(dt.getTime() - dt.getTimezoneOffset() * -60000); } + /** * @param {string} filepath - Full file path including file name. * @returns {string} filename */ - function getFilenameFromPath(filepath) { - return filepath.split('\\').pop().split('/').pop(); + //return filepath.split('\\').pop().split('/').pop(); + return filepath.split('\\').pop(); } /** @@ -232,10 +213,10 @@ function getFilenameFromPath(filepath) { * @param {string} text - HTML text to be evaluated. * @returns {string} Rendered template with injected data. */ - function Templater(text) { return new Function('data', 'var output=' + JSON.stringify(text).replace(/<%=(.+?)%>/g, '"+($1)+"').replace(/<%(.+?)%>/g, '";$1\noutput+="') + ';return output;'); } + /** * Load template from URL. * @function @@ -244,13 +225,13 @@ function Templater(text) { * @param {object} data - Data to load into template. * @param {function} callback - Callback function */ - function loadTemplate(url, data, callback) { get(url, function (success, error) { if (error) callback(success, error); callback(Templater(success)(data), error); }); } + /** * Renders the layout into the main container. * @function renderLayout @@ -258,15 +239,16 @@ function loadTemplate(url, data, callback) { * @param {string} layout - Filename of layout. * @param {object} data - Data passed to template. */ - -function renderLayout(layout, config, data) { +function renderLayout(layout, config, data, callback) { config.container.innerHTML = ''; - var url = [config.layoutDirectory, '/', layout, '.html'].join(''); + var url = [config.webpath, '/', config.layoutDirectory, '/', layout, '.html'].join(''); loadTemplate(url, data, function (success, error) { if (error) { handleMessage(messages['LAYOUT_LOAD_ERROR']); + callback(null, error); } else { config.container.innerHTML = success; + callback('rendered', null); } }); } @@ -280,105 +262,119 @@ function renderLayout(layout, config, data) { var Markdown = /*#__PURE__*/function () { function Markdown() { _classCallCheck(this, Markdown); - - this.rules = [// headers - fix link anchor tag regex + this.rules = [ + // headers - fix link anchor tag regex { regex: /(#+)(.*)/g, replacement: function replacement(text, chars, content) { var level = chars.length; return '' + content.trim() + ''; } - }, // image + }, + // image { regex: /!\[([^[]+)\]\(([^)]+)\)/g, replacement: '\'$1\'' - }, // hyperlink + }, + // hyperlink { regex: /\[([^[]+)\]\(([^)]+)\)/g, replacement: '$1' - }, // bold + }, + // bold { regex: /(\*\*|__)(.*?)\1/g, replacement: '$2' - }, // emphasis + }, + // emphasis { regex: /(\*|_)(.*?)\1/g, replacement: '$2' - }, // del + }, + // del { regex: /~~(.*?)~~/g, replacement: '$1' - }, // quote + }, + // quote { regex: /:"(.*?)":/g, replacement: '$1' - }, // block code + }, + // block code { regex: /```[a-z]*\n[\s\S]*?\n```/g, replacement: function replacement(text) { text = text.replace(/```/gm, ''); return '
' + text.trim() + '
'; } - }, // js code + }, + // js code { regex: /&&&[a-z]*\n[\s\S]*?\n&&&/g, replacement: function replacement(text) { text = text.replace(/```/gm, ''); return ''; } - }, // inline code + }, + // inline code { regex: /`(.*?)`/g, replacement: '$1' - }, // ul lists + }, + // ul lists { regex: /\n\*(.*)/g, replacement: function replacement(text, item) { return '\n'; } - }, // ol lists + }, + // ol lists { regex: /\n[0-9]+\.(.*)/g, replacement: function replacement(text, item) { return '\n
    \n\t
  1. ' + item.trim() + '
  2. \n
'; } - }, // blockquotes + }, + // blockquotes { regex: /\n(>|>)(.*)/g, replacement: function replacement(text, tmp, item) { return '\n
' + item.trim() + '
'; } - }, // horizontal rule + }, + // horizontal rule { regex: /\n-{5,}/g, replacement: '\n
' - }, // add paragraphs + }, + // add paragraphs { regex: /\n([^\n]+)\n/g, replacement: function replacement(text, line) { var trimmed = line.trim(); - if (/^<\/?(ul|ol|li|h|p|bl)/i.test(trimmed)) { return '\n' + line + '\n'; } - return '\n

' + trimmed + '

\n'; } - }, // fix extra ul + }, + // fix extra ul { regex: /<\/ul>\s?