diff --git a/.eslintrc b/.eslintrc index d52103feb..6e83375f1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -14,8 +14,8 @@ "eqeqeq": [2, "allow-null"], "global-strict": [0, "never"], "guard-for-in": 2, - "indent": [2, 2, {"SwitchCase": 1, "VariableDeclarator": 1}], - "lines-around-comment": [2, { + "indent": [1, 2, {"SwitchCase": 1, "VariableDeclarator": 1}], + "lines-around-comment": [1, { "beforeBlockComment": true, "beforeLineComment": true, "allowBlockStart": true, diff --git a/.gitignore b/.gitignore index a1c22e42c..74ab03195 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules/ -npm-debug.log .DS_Store latest-change.txt patternlab.json diff --git a/.travis.yml b/.travis.yml index 611cfd7d6..90d48e964 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: node_js node_js: - - stable - - 4.0 - - 0.12 - - 0.11 + - node + - 6 + - 5 + - 4 before_install: - phantomjs --version @@ -16,3 +16,12 @@ branches: only: - master - dev + - dev-2.0-core + +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/a14f537f16b0756d9470 + on_success: always + on_failure: always + on_start: never diff --git a/Gruntfile.js b/Gruntfile.js index 2fbe7d31f..2d74d0d19 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,168 +1,41 @@ module.exports = function (grunt) { - var path = require('path'); - - function paths() { - return require('./patternlab-config.json').paths; - } - - // Project configuration. + /****************************** + * Project configuration. + * Should only be needed if you are developing against core, running tests, linting and want to run tests or increment package numbers + *****************************/ grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), concat: { options: { stripBanners: true, - banner: '/* \n * <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy") %> \n * \n * <%= pkg.author %>, and the web community.\n * Licensed under the <%= pkg.license %> license. \n * \n * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. \n *\n */\n\n', + banner: '/* \n * <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy") %> \n * \n * <%= pkg.author.name %>, <%= pkg.contributors[0].name %>, and the web community.\n * Licensed under the <%= pkg.license %> license. \n * \n * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. \n *\n */\n\n', }, patternlab: { src: './core/lib/patternlab.js', dest: './core/lib/patternlab.js' - }, - object_factory: { - src: './core/lib/object_factory.js', - dest: './core/lib/object_factory.js' - }, - lineage: { - src: './core/lib/lineage_hunter.js', - dest: './core/lib/lineage_hunter.js' - }, - media_hunter: { - src: './core/lib/media_hunter.js', - dest: './core/lib/media_hunter.js' - }, - patternlab_grunt: { - src: './core/lib/patternlab_grunt.js', - dest: './core/lib/patternlab_grunt.js' - }, - patternlab_gulp: { - src: './core/lib/patternlab_gulp.js', - dest: './core/lib/patternlab_gulp.js' - }, - parameter_hunter: { - src: './core/lib/parameter_hunter.js', - dest: './core/lib/parameter_hunter.js' - }, - pattern_exporter: { - src: './core/lib/pattern_exporter.js', - dest: './core/lib/pattern_exporter.js' - }, - pattern_assembler: { - src: './core/lib/pattern_assembler.js', - dest: './core/lib/pattern_assembler.js' - }, - pseudopattern_hunter: { - src: './core/lib/pseudopattern_hunter.js', - dest: './core/lib/pseudopattern_hunter.js' - }, - list_item_hunter: { - src: './core/lib/list_item_hunter.js', - dest: './core/lib/list_item_hunter.js' - }, - style_modifier_hunter: { - src: './core/lib/style_modifier_hunter.js', - dest: './core/lib/style_modifier_hunter.js' - } - }, - copy: { - main: { - files: [ - { expand: true, cwd: path.resolve(paths().source.js), src: '*.js', dest: path.resolve(paths().public.js) }, - { expand: true, cwd: path.resolve(paths().source.css), src: '*.css', dest: path.resolve(paths().public.css) }, - { expand: true, cwd: path.resolve(paths().source.images), src: ['**/*.png', '**/*.jpg', '**/*.gif', '**/*.jpeg'], dest: path.resolve(paths().public.images) }, - { expand: true, cwd: path.resolve(paths().source.fonts), src: '*', dest: path.resolve(paths().public.fonts) }, - { expand: true, cwd: path.resolve(paths().source.data), src: 'annotations.js', dest: path.resolve(paths().public.data) } - ] - }, - styleguide: { - files: [ - { expand: true, cwd: path.resolve(paths().source.styleguide), src: ['*.*', '**/*.*'], dest: path.resolve(paths().public.styleguide) } - ] - } - }, - watch: { - all: { - files: [ - path.resolve(paths().source.css + '**/*.css'), - path.resolve(paths().source.styleguide + 'css/*.css'), - path.resolve(paths().source.patterns + '**/*.mustache'), - path.resolve(paths().source.patterns + '**/*.json'), - path.resolve(paths().source.fonts + '/*'), - path.resolve(paths().source.images + '/*'), - path.resolve(paths().source.data + '*.json'), - path.resolve(paths().source.js + '/*.js') - ], - tasks: ['default', 'bsReload:css'] } }, nodeunit: { all: ['test/*_tests.js'] }, - browserSync: { - dev: { - options: { - server: path.resolve(paths().public.root), - watchTask: true, - watchOptions: { - ignoreInitial: true, - ignored: '*.html' - }, - snippetOptions: { - // Ignore all HTML files within the templates folder - blacklist: ['/index.html', '/', '/?*'] - }, - plugins: [ - { - module: 'bs-html-injector', - options: { - files: [path.resolve(paths().public.root + '/index.html'), path.resolve(paths().public.styleguide + '/styleguide.html')] - } - } - ], - notify: { - styles: [ - 'display: none', - 'padding: 15px', - 'font-family: sans-serif', - 'position: fixed', - 'font-size: 1em', - 'z-index: 9999', - 'bottom: 0px', - 'right: 0px', - 'border-top-left-radius: 5px', - 'background-color: #1B2032', - 'opacity: 0.4', - 'margin: 0', - 'color: white', - 'text-align: center' - ] - } - } - } - }, eslint: { options: { configFile: './.eslintrc' }, target: ['./core/lib/*'] - }, - bsReload: { - css: path.resolve(paths().public.root + '**/*.css') } }); // load all grunt tasks - require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); - - //load the patternlab task - grunt.task.loadTasks('./core/lib/'); - - grunt.registerTask('default', ['patternlab', 'copy:main', 'copy:styleguide']); + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-eslint'); + grunt.loadNpmTasks('grunt-contrib-nodeunit'); //travis CI task - grunt.registerTask('travis', ['nodeunit', 'eslint', 'patternlab']); - - grunt.registerTask('serve', ['patternlab', 'copy:main', 'copy:styleguide', 'browserSync', 'watch:all']); + grunt.registerTask('travis', ['nodeunit', 'eslint']); + //to be run prior to releasing a version grunt.registerTask('build', ['nodeunit', 'eslint', 'concat']); }; diff --git a/README.md b/README.md index 307a5bd06..9e56f6fb3 100644 --- a/README.md +++ b/README.md @@ -1,425 +1,44 @@ -[![Build Status](https://travis-ci.org/pattern-lab/patternlab-node.png?branch=master)](https://travis-ci.org/pattern-lab/patternlab-node) +[![Build Status](https://travis-ci.org/pattern-lab/patternlab-node.png?branch=master)](https://travis-ci.org/pattern-lab/patternlab-node) [![Join the chat at Gitter](https://badges.gitter.im/pattern-lab/node.svg)](https://gitter.im/pattern-lab/node) -## About the Node Version of Pattern Lab +# Pattern Lab Node Core -The Node version of [Pattern Lab](http://patternlab.io/) is, at its core, a static site generator. It combines platform-agnostic assets, like the Mustache-based patterns, the JavaScript-based viewer, and the self-contained webserver, with a Node-based "builder" that transforms and dynamically builds the Pattern Lab site. +This repository contains the core functionality for Pattern Lab Node. Pattern Lab Core is designed to be included as a dependency within [Node Editions](https://github.com/pattern-lab?utf8=%E2%9C%93&query=edition-node). +If this looks **REALLY DIFFERENT** from what you expected, check out the [ChangeLog](https://github.com/pattern-lab/patternlab-node/wiki/ChangeLog). -This repository contains the vanilla builder logic, grunt and gulp configurations, and some sample template/css/data to illustrate the power and flexibility of the tool. +* [Pattern Lab/Node: Gulp Edition](https://github.com/pattern-lab/edition-node-gulp) contains info how to get started within a Gulp task running environment. +* [Pattern Lab/Node: Grunt Node Edition](https://github.com/pattern-lab/edition-node-grunt) contains info how to get started within a Grunt task running environment. -###### Core Team +## Core Team * [@bmuenzenmeyer](https://github.com/bmuenzenmeyer) - Lead Maintainer * [@geoffp](https://github.com/geoffp) - Core Contributor -### Prerequisites +## Upgrading -Make sure Node and npm are installed. A great guide can be found here: [https://docs.npmjs.com/getting-started/installing-node](https://docs.npmjs.com/getting-started/installing-node) +If you find yourself here and are looking to ugpgrade, check out how to upgrade from version to version of Pattern Lab Node here: [https://github.com/pattern-lab/patternlab-node/wiki/Upgrading](https://github.com/pattern-lab/patternlab-node/wiki/Upgrading) -### Download +## Command Line Interface -* Download the [latest release of patternlab-node](https://github.com/pattern-lab/patternlab-node/releases/latest) from Github -* Via npm, run `npm install patternlab-node` (Note this will auto install the grunt version currently. see below) -* **NOTE** Node version 4.X and 5.X have tentative support, citing [a lot of Windows issues](https://github.com/nodejs/node-gyp/issues/629), including [mine](https://github.com/pattern-lab/patternlab-node/issues/162). Upgrade node at your own risk until otherwise stated. I've tried to catalog some issues and troubleshooting steps on the [wiki](https://github.com/pattern-lab/patternlab-node/wiki/Windows-Issues). +The [command line interface](https://github.com/pattern-lab/patternlab-node/wiki/Command-Line-Interface) is documented in the wiki, and already implemented for you within [Node Editions](https://github.com/pattern-lab?utf8=%E2%9C%93&query=edition-node). -### Troubleshooting Installs +## Contributing -Make sure you are running your terminal/command line session as administrator. This could mean `sudo`, or opening the window with a right-click option. +If you'd like to contribute to Pattern Lab Node, please do so! There is always a lot of ground to cover and something for your wheelhouse. -### Choose Your Adventure! Now Vanilla, Grunt & Gulp +No pull request is too small. Check out any [up for grabs issues](https://github.com/pattern-lab/patternlab-node/labels/up%20for%20grabs) as a good way to get your feet wet, or add some more unit tests. -This repository ships with two `package.json` files, a `Gruntfile.js`, and a `gulpfile.js`. The default is grunt currently. The core builder is not dependent on either. +## Guidelines +1. Please keep your pull requests concise and limited to **ONE** substantive change at a time. This makes reviewing and testing so much easier. +2. _ALWAYS_ submit pull requests against the [dev branch](https://github.com/pattern-lab/patternlab-node/tree/dev). If this does not occur, I will first, try to redirect you gently, second, port over your contribution manually if time allows, and/or third, close your pull request. If you have a major feature to stabilize over time, talk to @bmuenzenmeyer about making a dedicated `feature-branch` +3. If you can, add some unit tests using the existing patterns in the `./test` directory +4. To help hack on core from an edition, read [this wiki page](https://github.com/pattern-lab/patternlab-node/wiki/Running-an-Edition-Against-Local-Core) -### Getting Started - Grunt +## Coding style +Two files combine within the project to define and maintain our coding style. -To run patternlab-node using grunt, do the following in the directory you downloaded and extracted the zipped release: +* The `.editorconfig` controls spaces / tabs within supported editors. Check out their [site](http://editorconfig.org/). +* The `.eslintrc` defines our javascript standards. Some editors will evaluate this real-time - otherwise it's run using `grunt|gulp build` -1. Run `npm install` from the command line -2. Optionally, delete `package.gulp.json`, `gulpfile.js`, and `core/lib/patternlab_gulp.js` files if you are certain you don't need it. -* Not deleting `core/lib/patternlab_gulp.js` may cause a harmless error when running grunt. Delete it. -3. Run `grunt` or `grunt serve` from the command line +## Gitter -This creates all patterns, the styleguide, and the pattern lab site. It's strongly recommended to run `grunt serve` to have BrowserSync spin up and serve the files to you. - -### Getting Started - Gulp - -To run patternlab-node using gulp, you need to swap out the default grunt configuration. Do the following in the directory you downloaded and extracted the zipped release: - -1. Rename `package.json` to `package.grunt.json` or delete it if you don't intend on going back -2. Rename `package.gulp.json` to `package.json` -3. Run `npm install` from the command line -4. Run `gulp` or `gulp serve` from the command line - -This creates all patterns, the styleguide, and the pattern lab site. It's strongly recommended to run `gulp serve` to have BrowserSync spin up and serve the files to you. - -### There and Back Again, or Switching Between Grunt and Gulp - -It's not expected to toggle between the two build systems, but for those migrating between the two configs, here's some general guidelines: - -* Make sure your `package.json` files are correct per the Getting Started sections. -* Run `npm cache clear` before installation -* Delete the contents of `./node_modules` if you want a cleaner installation. -* Regarding speed, Gulp is faster. BrowserSync takes a bit longer than the old static server to spin up, but its capabilities far outweigh the startup cost. - -### Upgrading - -You can find instructions on how to upgrade from version to version of Pattern Lab Node here: [https://github.com/pattern-lab/patternlab-node/wiki/Upgrading](https://github.com/pattern-lab/patternlab-node/wiki/Upgrading) - -### Command Line Interface - -The following are grunt/gulp task arguments you may execute: - -##### `patternlab` -With no arguments, patternlab runs the full builder, compiling patterns, and constructing the front-end site. - -##### `patternlab:only_patterns` -Compile the patterns only, outputting to ./public/patterns - -##### `patternlab:v` -Retrieve the version of patternlab-node you have installed - -##### `patternlab:help` -Get more information about patternlab-node, pattern lab in general, and where to report issues. - -### Further Configuration - -##### Watching Changes -To have patternlab-node watch for changes to either a mustache template, data, or stylesheets, run `grunt|gulp watch` or `grunt|gulp serve`. The `Gruntfile|Gulpfile` governs what is watched. It should be easy to add scss or whatever preprocessor you fancy. - -##### Configurable Paths -Pattern Lab Node ships with a particular source and public workflow intended to separate the code you work on with the code generated for consumption elsewhere. If you wish to change any paths, you may do so within `patternlab-config.json`. The contents are here: - -``` -"paths" : { - "source" : { - "root": "./source/", - "patterns" : "./source/_patterns/", - "data" : "./source/_data/", - "styleguide" : "./core/styleguide/", - "patternlabFiles" : "./source/_patternlab-files/", - "js" : "./source/js", - "images" : "./source/images", - "fonts" : "./source/fonts", - "css" : "./source/css/" - }, - "public" : { - "root" : "./public/", - "patterns" : "./public/patterns/", - "data" : "./public/data/", - "styleguide" : "./public/styleguide/", - "js" : "./public/js", - "images" : "./public/images", - "fonts" : "./public/fonts", - "css" : "./public/css" - } -} -``` - -Note the intentional repitition of the nested structure, made this way for maximum flexibility. Relative paths are default but absolute paths should work too. You may also use these paths within Grunt or Gulp files by referring to the paths() object. - -##### Nav Bar Controls -If you don't have a need for some of the nav-bar tools in the Pattern Lab frontend, you can turn them off in `patternlab-config.json`. - -The current selection is as follows. - -``` -"ishControlsVisible": { - "s": true, - "m": true, - "l": true, - "full": true, - "random": true, - "disco": true, - "hay": true, - "mqs": false, - "find": false, - "views-all": true, - "views-annotations": true, - "views-code": true, - "views-new": true, - "tools-all": true, - "tools-follow": false, - "tools-reload": false, - "tools-shortcuts": false, - "tools-docs": true -} -``` -##### Pattern States -You can set the state of a pattern by including its key in the `patternStates` object in `patternlab-config.json`, along with a style defined inside `patternStateCascade`. The out of the box styles are in progress (orange), in review (yellow), and complete (green). -``` -"patternStates": { - "atoms-colors" : "complete", - "molecules-primary-nav" : "inreview", - "organisms-header" : "inprogress" -} -``` - -Note that patterns inherit the lowest common denominator pattern state of their lineage. -Consider: -``` -"patternStates": { - "molecules-single-comment" : "complete", - "organisms-sticky-comment" : "inreview", - "templates-article" : "complete" -} -``` -In this case, two things are of note: - -* templates-article will display inreview since it inherits `organisms-sticky-comment` -* pages-article will not display any pattern state, as it does not define one - -The `patternStateCascade` array is important in that the order is hierarchical. -The default is below: - -``` -"patternStateCascade": ["inprogress", "inreview", "complete"], -``` - -which correspond to classes defined inside `./core/styleguide/css/styleguide.css` - -``` -/* pattern states */ -.inprogress:before { - color: #FF4136 !important; -} - -.inreview:before { - color: #FFCC00 !important; -} - -.complete:before { - color: #2ECC40 !important; -} -``` - -##### Pattern Export -`patternlab-config.json` also has two properties that work together to export completed patterns for use in a production environment. Provide an array of keys and an output directory. Pattern Lab doesn't ship with any pattern export keys, but the default directory is `"./pattern_exports/"` created inside the install directory. - -``` -"patternExportKeys": ["molecules-primary-nav", "organisms-header", "organisms-header"], -"patternExportDirectory": "./pattern_exports/" -``` - -Coupled with exported css (much easier to extract with existing tools like [grunt-contrib-copy](https://github.com/gruntjs/grunt-contrib-copy)), pattern export can help to maintain the relevancy of the design system by directly placing partials in a directory of your choosing. - -##### cacheBust -`patternlab-config.json` has this flag to instruct Pattern Lab to append a unique query string to Javascript and CSS assets throughout the frontend. - -``` -"cacheBust": true -``` - -Default: true - -##### defaultPattter -`patternlab-config.json` has an entry that allows you to specifiy a specific pattern upon launch of the main site. It works even without BrowserSync running. Set it like this: - -``` -"defaultPattern": "pages-homepage", -``` -Default: "all" -If running with BrowserSync, you may also set [this BrowserSync options](https://www.browsersync.io/docs/options/#option-startPath) to achieve the same result via your Gruntfile or Gulpfile. - -``` -startPath: '/?p=pages-homepage', -``` - -##### baseurl - -If your instance of Pattern Lab lives in a subdirectory of your server, for instance on github pages (ex: yourusername.github.io/patterns-demo/), then add the baseurl here. The baseurl is everything after the hostname - ie: `patterns-demo` - -``` -"baseurl" : "/patterns-demo" -``` - -Default: blank - -##### excluding patterns - -If you'd like to exclude an individual pattern you can do so by prepending the filename with an underscore, like: `_filename.mustache` - -You can also exclude complete directories by prepending the directory name with an underscore, like: `/_experiment/...` - -##### Style Guide Excludes - -Exclude whole pattern types from the "All patterns" styleguide by adding entries to `patternlab-config.json`. This is quite useful to make speedier. Pattern Lab Node ships with the following: - -``` -"styleGuideExcludes": [ - "templates", - "pages" -] -``` - - -##### Debug Mode -`patternlab.json` is a file created for debugging purposes. Set `debug` to true in `.patternlab-config.json` to see all the secrets. - -##### Server & BrowserSync -Running `grunt serve` or `gulp serve` will compile the Pattern Lab frontend and host it by default on http://localhost:3000 via [BrowserSync](http://www.browsersync.io/docs/). After it starts, templates, `data.json`, and scss/css changes in your source code will be automatically injected into the page. - -You'll notice that if you have this open across different browsers, we do our best to keep the frontend in sync, but there is a known issue with synced navigation using the main menu. - -### Roadmap - - -A roadmap exists for Pattern Lab Node. Check it out [here](https://github.com/pattern-lab/patternlab-node/issues/134). The Node version of Pattern Lab is maintained by [@bmuenzenmeyer](https://twitter.com/bmuenzenmeyer) and contributors. Pull requests welcome, but please take a moment to read the [guidelines](https://github.com/pattern-lab/patternlab-node/blob/master/CONTRIBUTING.md). - -Dave Olsen has also published the [specification](https://github.com/pattern-lab/the-spec/blob/draft/SPEC.md) for Pattern Lab ports. Development will be oriented toward compliance with this as the spec and the port mature together. Post v1 work will focus on other pattern engines and a plugin architecture. - -### Advanced Pattern Library Features - -##### Pattern Parameters -Pattern parameters are a simple mechanism for replacing Mustache variables via attributes on a pattern partial tag rather than having to use a pattern-specific json file. They are especially useful when you want to supply distinct values for Mustache variables in a specific pattern partial instance that may be included multiple times in a molecule, template, or page. - -The basic syntax is this: - -``` -{{> molecules-single-comment(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }} -``` - -The attributes listed in the pattern parameters should match Mustache variable names in your pattern. The values listed for each attribute will replace the Mustache variables. Again, pattern parameters are a simple find and replace of Mustache variables with the supplied values. - -Pattern parameters **do not** currently support the following: - -* sub-lists (e.g. iteration of a section), -* and the use of long strings of text can be unwieldy -* nested properties within the parameter data, such as `{{> molecules-single-comment(foo.bar: 'baz') }}` - -You can read the full documentation on pattern parameters here: [Using Pattern Parameters](http://patternlab.io/docs/pattern-parameters.html) - -##### Pattern Style Modifiers -Style Modifiers allow you to create a base pattern that you can easily modify by adding a class name to the pattern partial. Read more about them [here](http://patternlab.io/docs/pattern-stylemodifier.html), including support with pattern parameters. Below is the gist. - -The basic syntax is this: - -``` -{{> atoms-message:error }} -``` - -This works by using a reserved mustache variable of sorts called {{ styleModifier }} applied to the atoms-message mustache file itself: - -``` -
{{ message }}
-``` - -Once rendered, it looks like this: - -``` -
-
-
-``` - -You may also specify multiple classes using a pipe character (|). - -``` -{{> atoms-message:error|is-on }} -``` - - - -##### Pseudo-Patterns -Pseudo-patterns are meant to give developers the ability to build multiple and unique **rendered** patterns off of one base pattern and its mark-up while giving them control over the data that is injected into the base pattern. This feature is especially useful when developing template- and page-style patterns. - -Pseudo-patterns are, essentially, the pattern-specific JSON files that would accompany a pattern. Rather than require a Mustache pattern, though, pseudo-patterns are hinted so a developer can reference a shared pattern. The basic syntax: - -`patternName~pseudoPatternName.json` - -The tilde, `~`, and JSON extension denotes that this is a pseudo-pattern. `patternName` is the parent pattern that will be used when rendering the pseudo-pattern. `patternName` and `pseudoPatternName` are combined when adding the pseudo-pattern to the navigation. - -The JSON file itself works exactly like the [pattern-specific JSON file](http://patternlab.io/docs/data-pattern-specific.html). It has the added benefit that the pseudo-pattern will also import any values from the parent pattern's pattern-specific JSON file. Here is an example (which ships with the package) where we want to show an emergency notification on our homepage template. Our `03-templates/` directory looks like this: - -``` -00-homepage.mustache -01-blog.mustache -02-article.mustache -``` - -Our `00-homepage.mustache` template might look like this: - -``` -
- {{# emergency }} -
Oh Noes! Emergency!
- {{/ emergency }} - { ...a bunch of other content... } -
-``` - -If our `_data.json` file doesn't give a value for `emergency` that section will never show up when `00-homepage.mustache` is rendered. - -We want to show both the regular and emergency states of the homepage but we don't want to duplicate the entire `00-homepage.mustache` template. That would be a maintenance nightmare. So let's add our pseudo-pattern: - -``` -00-homepage.mustache -00-homepage~emergency.json -01-blog.mustache -02-article.mustache -``` - -In our pseudo-pattern, `00-homepage~emergency.json`, we add our `emergency` attribute: - -``` -{ - "emergency": true -} -``` - -Now when we generate our site we'll have our homepage template rendered twice. Once as the regular template and once as a pseudo-pattern showing the emergency section. Note that the pseudo-pattern will show up in our navigation as `Homepage Emergency`. - -##### Pattern Linking -You can build patterns that link to one another to help simulate using a real website. This is especially useful when working with the Pages and Templates pattern types. The basic format is: - -`{{ link.pattern-name }}` - -For example, if you wanted to add a link to the `home page` template from your `blog` template you could write the following: - -`Home` - -This would compile to: - -`Home` - -As you can see, it's a much easier way of linking patterns to one another. - -=== - -## Working with Patterns - -(The following documentation is built for the PHP version of Pattern Lab, but most applies to the node version too. If you find omissions or mistakes please open an issue.) - -Patterns are the core element of Pattern Lab. Understanding how they work is the key to getting the most out of the system. Patterns use [Mustache](http://mustache.github.io/) so please read [Mustache's docs](http://mustache.github.io/mustache.5.html) as well. - -* [How Patterns Are Organized](http://patternlab.io/docs/pattern-organization.html) -* [Adding New Patterns](http://patternlab.io/docs/pattern-add-new.html) -* [Reorganizing Patterns](http://patternlab.io/docs/pattern-reorganizing.html) -* [Including One Pattern Within Another via Partials](http://patternlab.io/docs/pattern-including.html) -* [Managing Assets for a Pattern: JavaScript, images, CSS, etc.](http://patternlab.io/docs/pattern-managing-assets.html) -* [Modifying the Pattern Header and Footer](http://patternlab.io/docs/pattern-header-footer.html) -* [Using Pattern Parameters](http://patternlab.io/docs/pattern-parameters.html) -* [Using Pattern State](http://patternlab.io/docs/pattern-states.html) -* ["Hiding" Patterns in the Navigation](http://patternlab.io/docs/pattern-hiding.html) -* [Adding Annotations](http://patternlab.io/docs/pattern-adding-annotations.html) -* [Viewing Patterns on a Mobile Device](http://patternlab.io/docs/pattern-mobile-view.html) - -## Creating & Working With Dynamic Data for a Pattern - -The Node version of Pattern Lab utilizes Mustache as the template language for patterns. In addition to allowing for the [inclusion of one pattern within another](http://patternlab.io/docs/pattern-including.html) it also gives pattern developers the ability to include variables. This means that attributes like image sources can be centralized in one file for easy modification across one or more patterns. The Node version of Pattern Lab uses a JSON file, `source/_data/data.json`, to centralize many of these attributes. - -* [Introduction to JSON & Mustache Variables](http://patternlab.io/docs/data-json-mustache.html) -* [Overriding the Central `data.json` Values with Pattern-specific Values](http://patternlab.io/docs/data-pattern-specific.html) -* [Linking to Patterns with Pattern Lab's Default `link` Variable](http://patternlab.io/docs/data-link-variable.html) -* [Creating Lists with Pattern Lab's Default `listItems` Variable](http://patternlab.io/docs/data-listitems.html) - -## Using Pattern Lab's Advanced Features - -By default, the Pattern Lab assets can be manually generated and the Pattern Lab site manually refreshed but who wants to waste time doing that? Here are some ways that Pattern Lab can make your development workflow a little smoother: - -* [Watching for Changes and Auto-Regenerating Patterns](http://patternlab.io/docs/advanced-auto-regenerate.html) - Node version coming soon -* [Auto-Reloading the Browser Window When Changes Are Made](http://patternlab.io/docs/advanced-reload-browser.html) - Node version coming soon -* [Multi-browser & Multi-device Testing with Page Follow](http://patternlab.io/docs/advanced-page-follow.html) -* [Keyboard Shortcuts](http://patternlab.io/docs/advanced-keyboard-shortcuts.html) -* [Special Pattern Lab-specific Query String Variables ](http://patternlab.io/docs/pattern-linking.html) -* [Preventing the Cleaning of public/](http://patternlab.io/docs/advanced-clean-public.html) - Node version coming soon -* [Modifying the Pattern Lab Nav](http://patternlab.io/docs/advanced-pattern-lab-nav.html) - Node version coming soon -* [Editing the config.ini Options](http://patternlab.io/docs/advanced-config-options.html) - Node version coming soon +The Pattern Lab Node team uses [our gitter.im channel, pattern-lab/node](https://gitter.im/pattern-lab/node) to keep in sync, share updates, and talk shop. Please stop by to say hello or as a first place to turn if stuck. Other channels in the Pattern Lab organization can be found on gitter too. diff --git a/core/lib/annotation_exporter.js b/core/lib/annotation_exporter.js new file mode 100644 index 000000000..0000e2bbd --- /dev/null +++ b/core/lib/annotation_exporter.js @@ -0,0 +1,117 @@ +"use strict"; + +var annotations_exporter = function (pl) { + var path = require('path'), + fs = require('fs-extra'), + JSON5 = require('json5'), + _ = require('lodash'), + md = require('markdown-it')(), + paths = pl.config.paths; + + /* + Returns the array of comments that used to be wrapped in raw JS. + */ + function parseAnnotationsJS() { + //attempt to read the file + try { + var oldAnnotations = fs.readFileSync(path.resolve(paths.source.annotations, 'annotations.js'), 'utf8'); + } catch (ex) { + if (pl.config.debug) { + console.log('annotations.js file missing from ' + paths.source.annotations + '. This may be expected.'); + } + return []; + } + + //parse as JSON by removing the old wrapping js syntax. comments and the trailing semi-colon + oldAnnotations = oldAnnotations.replace('var comments = ', ''); + try { + var oldAnnotationsJSON = JSON5.parse(oldAnnotations.trim().slice(0, -1)); + } catch (ex) { + console.log('There was an error parsing JSON for ' + paths.source.annotations + 'annotations.js'); + console.log(ex); + return []; + } + return oldAnnotationsJSON.comments; + } + + /* + Converts the annotations.md file yaml list into an array of annotations + */ + function parseAnnotationsMD() { + var annotations = []; + + //attempt to read the file + var annotationsMD = ''; + try { + annotationsMD = fs.readFileSync(path.resolve(paths.source.annotations, 'annotations.md'), 'utf8'); + } catch (ex) { + if (pl.config.debug) { + console.log('annotations.md file missing from ' + paths.source.annotations + '. This may be expected.'); + } + return []; + } + + //take the annotation snippets and split them on our custom delimiter + var annotationsYAML = annotationsMD.split('~*~'); + for (var i = 0; i < annotationsYAML.length; i++) { + var annotation = {}; + + //for each annotation process the yaml frontmatter and markdown + var annotationSnippet = annotationsYAML[i]; + var annotationsRE = /---\r?\n{1}([\s\S]*)---\r?\n{1}([\s\S]*)+/gm; + var chunks = annotationsRE.exec(annotationSnippet); + if (chunks && chunks[1] && chunks[2]) { + + //convert each yaml frontmatter key into an object key + var frontmatter = chunks[1]; + var frontmatterLines = frontmatter.split(/\n/gm); + for (var j = 0; j < frontmatterLines.length; j++) { + var frontmatterLine = frontmatterLines[j]; + if (frontmatterLine.length > 0) { + var frontmatterLineChunks = frontmatterLine.split(':'); //test this + var frontmatterKey = frontmatterLineChunks[0].toLowerCase().trim(); + var frontmatterValueString = frontmatterLineChunks[1].trim(); + var frontmatterValue = frontmatterValueString.substring(1, frontmatterValueString.length - 1); + if (frontmatterKey === 'el' || frontmatterKey === 'selector') { + annotation.el = frontmatterValue; + } + if (frontmatterKey === 'title') { + annotation.title = frontmatterValue; + } + } + } + + //set the comment to the parsed markdown + var annotationMarkdown = chunks[2]; + annotation.comment = md.render(annotationMarkdown); + + annotations.push(annotation); + } else { + console.log('annotations.md file not formatted as expected. Error parsing frontmatter and markdown out of ' + annotationSnippet); + } + } + return annotations; + } + + function gatherAnnotations() { + var annotationsJS = parseAnnotationsJS(); + var annotationsMD = parseAnnotationsMD(); + var mergedAnnotations = _.unionBy(annotationsJS, annotationsMD, 'el'); + return mergedAnnotations; + } + + return { + gather: function () { + return gatherAnnotations(); + }, + gatherJS: function () { + return parseAnnotationsJS(); + }, + gatherMD: function () { + return parseAnnotationsMD(); + } + }; + +}; + +module.exports = annotations_exporter; diff --git a/core/lib/lineage_hunter.js b/core/lib/lineage_hunter.js index a030d05dc..2c5fb0112 100644 --- a/core/lib/lineage_hunter.js +++ b/core/lib/lineage_hunter.js @@ -1,13 +1,3 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - "use strict"; var lineage_hunter = function () { @@ -19,36 +9,19 @@ var lineage_hunter = function () { var pattern_assembler = new pa(); //find the {{> template-name }} within patterns - var matches = pattern_assembler.find_pattern_partials(pattern); + var matches = pattern.findPartials(); if (matches !== null) { matches.forEach(function (match) { - //strip out the template cruft - var foundPatternKey = match - .replace("{{> ", "") - .replace(" }}", "") - .replace("{{>", "") - .replace("}}", ""); - - // remove any potential pattern parameters. this and the above are - // rather brutish but I didn't want to do a regex at the time - if (foundPatternKey.indexOf('(') > 0) { - foundPatternKey = foundPatternKey.substring(0, foundPatternKey.indexOf('(')); - } - - //remove any potential stylemodifiers. - foundPatternKey = foundPatternKey.split(':')[0]; - //get the ancestorPattern - var ancestorPattern = pattern_assembler.get_pattern_by_key(foundPatternKey, patternlab); - - if (ancestorPattern && pattern.lineageIndex.indexOf(ancestorPattern.key) === -1) { + var ancestorPattern = pattern_assembler.findPartial(pattern.findPartial(match), patternlab); + if (ancestorPattern && pattern.lineageIndex.indexOf(ancestorPattern.patternPartial) === -1) { //add it since it didnt exist - pattern.lineageIndex.push(ancestorPattern.key); + pattern.lineageIndex.push(ancestorPattern.patternPartial); //create the more complex patternLineage object too var l = { - "lineagePattern": ancestorPattern.key, + "lineagePattern": ancestorPattern.patternPartial, "lineagePath": "../../patterns/" + ancestorPattern.patternLink }; if (ancestorPattern.patternState) { @@ -58,12 +31,12 @@ var lineage_hunter = function () { pattern.lineage.push(l); //also, add the lineageR entry if it doesn't exist - if (ancestorPattern.lineageRIndex.indexOf(pattern.key) === -1) { - ancestorPattern.lineageRIndex.push(pattern.key); + if (ancestorPattern.lineageRIndex.indexOf(pattern.patternPartial) === -1) { + ancestorPattern.lineageRIndex.push(pattern.patternPartial); //create the more complex patternLineage object in reverse var lr = { - "lineagePattern": pattern.key, + "lineagePattern": pattern.patternPartial, "lineagePath": "../../patterns/" + pattern.patternLink }; if (pattern.patternState) { @@ -81,14 +54,14 @@ var lineage_hunter = function () { // if the request came from the past, apply target pattern state to current pattern lineage if (direction === 'fromPast') { for (var i = 0; i < pattern.lineageIndex.length; i++) { - if (pattern.lineageIndex[i] === targetPattern.key) { + if (pattern.lineageIndex[i] === targetPattern.patternPartial) { pattern.lineage[i].lineageState = targetPattern.patternState; } } } else { //the request came from the future, apply target pattern state to current pattern reverse lineage for (var i = 0; i < pattern.lineageRIndex.length; i++) { - if (pattern.lineageRIndex[i] === targetPattern.key) { + if (pattern.lineageRIndex[i] === targetPattern.patternPartial) { pattern.lineageR[i].lineageState = targetPattern.patternState; } } @@ -110,7 +83,7 @@ var lineage_hunter = function () { //find all lineage - patterns being consumed by this one for (var h = 0; h < pattern.lineageIndex.length; h++) { - var lineagePattern = pattern_assembler.get_pattern_by_key(pattern.lineageIndex[h], patternlab); + var lineagePattern = pattern_assembler.findPartial(pattern.lineageIndex[h], patternlab); setPatternState('fromFuture', lineagePattern, pattern); } } @@ -120,7 +93,7 @@ var lineage_hunter = function () { //find all reverse lineage - that is, patterns consuming this one for (var j = 0; j < pattern.lineageRIndex.length; j++) { - var lineageRPattern = pattern_assembler.get_pattern_by_key(pattern.lineageRIndex[j], patternlab); + var lineageRPattern = pattern_assembler.findPartial(pattern.lineageRIndex[j], patternlab); //only set patternState if pattern.patternState "is less than" the lineageRPattern.patternstate //or if lineageRPattern.patternstate (the consuming pattern) does not have a state @@ -129,7 +102,7 @@ var lineage_hunter = function () { < patternlab.config.patternStateCascade.indexOf(lineageRPattern.patternState))) { if (patternlab.config.debug) { - console.log('Found a lower common denominator pattern state: ' + pattern.patternState + ' on ' + pattern.key + '. Setting reverse lineage pattern ' + lineageRPattern.key + ' from ' + (lineageRPattern.patternState === '' ? '<>' : lineageRPattern.patternState)); + console.log('Found a lower common denominator pattern state: ' + pattern.patternState + ' on ' + pattern.patternPartial + '. Setting reverse lineage pattern ' + lineageRPattern.patternPartial + ' from ' + (lineageRPattern.patternState === '' ? '<>' : lineageRPattern.patternState)); } lineageRPattern.patternState = pattern.patternState; @@ -153,7 +126,6 @@ var lineage_hunter = function () { cascadePatternStates(patternlab); } }; - }; module.exports = lineage_hunter; diff --git a/core/lib/list_item_hunter.js b/core/lib/list_item_hunter.js index e13549143..35c077f5d 100644 --- a/core/lib/list_item_hunter.js +++ b/core/lib/list_item_hunter.js @@ -1,13 +1,3 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - "use strict"; var list_item_hunter = function () { @@ -16,18 +6,22 @@ var list_item_hunter = function () { JSON5 = require('json5'), pa = require('./pattern_assembler'), smh = require('./style_modifier_hunter'), - pattern_assembler = new pa(), + plutils = require('./utilities'), + Pattern = require('./object_factory').Pattern; + + var pattern_assembler = new pa(), style_modifier_hunter = new smh(), - items = [ 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty']; + items = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty']; function processListItemPartials(pattern, patternlab) { //find any listitem blocks - var matches = pattern_assembler.find_list_items(pattern, patternlab); + var matches = pattern.findListItems(); + if (matches !== null) { matches.forEach(function (liMatch) { if (patternlab.config.debug) { - console.log('found listItem of size ' + liMatch + ' inside ' + pattern.key); + console.log('found listItem of size ' + liMatch + ' inside ' + pattern.patternPartial); } //find the boundaries of the block @@ -38,9 +32,10 @@ var list_item_hunter = function () { //build arrays that repeat the block, however large we need to var repeatedBlockTemplate = []; var repeatedBlockHtml = ''; - var i; // for loops - - for (i = 0; i < items.indexOf(loopNumberString); i++) { + for (var i = 0; i < items.indexOf(loopNumberString); i++) { + if (patternlab.config.debug) { + console.log('list item(s) in pattern', pattern.patternPartial, 'adding', patternBlock, 'to repeatedBlockTemplate'); + } repeatedBlockTemplate.push(patternBlock); } @@ -49,13 +44,16 @@ var list_item_hunter = function () { try { listData = JSON5.parse(JSON5.stringify(patternlab.listitems)); } catch (err) { - console.log('There was an error parsing JSON for ' + pattern.abspath); + console.log('There was an error parsing JSON for ' + pattern.relPath); console.log(err); } - listData = pattern_assembler.merge_data(listData, pattern.listitems); + + listData = plutils.mergeData(listData, pattern.listitems); + listData = pattern_assembler.parse_data_links_specific(patternlab, listData, 'listitems.json + any pattern listitems.json'); //iterate over each copied block, rendering its contents along with pattenlab.listitems[i] - for (i = 0; i < repeatedBlockTemplate.length; i++) { + for (var i = 0; i < repeatedBlockTemplate.length; i++) { + var thisBlockTemplate = repeatedBlockTemplate[i]; var thisBlockHTML = ""; @@ -67,32 +65,37 @@ var list_item_hunter = function () { globalData = JSON5.parse(JSON5.stringify(patternlab.data)); localData = JSON5.parse(JSON5.stringify(pattern.jsonFileData)); } catch (err) { - console.log('There was an error parsing JSON for ' + pattern.abspath); + console.log('There was an error parsing JSON for ' + pattern.relPath); console.log(err); } - var allData = pattern_assembler.merge_data(globalData, localData); - allData = pattern_assembler.merge_data(allData, itemData !== undefined ? itemData[i] : {}); //itemData could be undefined if the listblock contains no partial, just markup + var allData = plutils.mergeData(globalData, localData); + allData = plutils.mergeData(allData, itemData !== undefined ? itemData[i] : {}); //itemData could be undefined if the listblock contains no partial, just markup allData.link = extend({}, patternlab.data.link); //check for partials within the repeated block - var foundPartials = pattern_assembler.find_pattern_partials({ 'template' : thisBlockTemplate }); + var foundPartials = Pattern.createEmpty({'template': thisBlockTemplate}).findPartials(); if (foundPartials && foundPartials.length > 0) { + for (var j = 0; j < foundPartials.length; j++) { + //get the partial var partialName = foundPartials[j].match(/([\w\-\.\/~]+)/g)[0]; - var partialPattern = pattern_assembler.get_pattern_by_key(partialName, patternlab); + var partialPattern = pattern_assembler.findPartial(partialName, patternlab); //create a copy of the partial so as to not pollute it after the get_pattern_by_key call. var cleanPartialPattern; try { cleanPartialPattern = JSON5.parse(JSON5.stringify(partialPattern)); } catch (err) { - console.log('There was an error parsing JSON for ' + pattern.abspath); + console.log('There was an error parsing JSON for ' + pattern.relPath); console.log(err); } + //if we retrieved a pattern we should make sure that its extendedTemplate is reset. looks to fix #356 + cleanPartialPattern.extendedTemplate = cleanPartialPattern.template; + //if partial has style modifier data, replace the styleModifier value if (foundPartials[j].indexOf(':') > -1) { style_modifier_hunter.consume_style_modifier(cleanPartialPattern, foundPartials[j], patternlab); @@ -104,6 +107,7 @@ var list_item_hunter = function () { //render with data thisBlockHTML = pattern_assembler.renderPattern(thisBlockTemplate, allData, patternlab.partials); + } else { //just render with mergedData thisBlockHTML = pattern_assembler.renderPattern(thisBlockTemplate, allData, patternlab.partials); @@ -118,7 +122,7 @@ var list_item_hunter = function () { pattern.extendedTemplate = pattern.extendedTemplate.replace(repeatingBlock, repeatedBlockHtml); //update the extendedTemplate in the partials object in case this pattern is consumed later - patternlab.partials[pattern.key] = pattern.extendedTemplate; + patternlab.partials[pattern.patternPartial] = pattern.extendedTemplate; }); } @@ -129,7 +133,6 @@ var list_item_hunter = function () { processListItemPartials(pattern, patternlab); } }; - }; module.exports = list_item_hunter; diff --git a/core/lib/media_hunter.js b/core/lib/media_hunter.js index fca2a5626..8954df13e 100644 --- a/core/lib/media_hunter.js +++ b/core/lib/media_hunter.js @@ -1,13 +1,3 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - "use strict"; var diveSync = require('diveSync'), diff --git a/core/lib/object_factory.js b/core/lib/object_factory.js index b3c0fb708..1a55c5760 100644 --- a/core/lib/object_factory.js +++ b/core/lib/object_factory.js @@ -1,70 +1,152 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - "use strict"; -var oPattern = function (abspath, subdir, filename, data) { - this.fileName = filename.substring(0, filename.indexOf('.')); - this.abspath = abspath; - this.subdir = subdir; - this.name = subdir.replace(/[\/\\]/g, '-') + '-' + this.fileName; //this is the unique name with the subDir +var patternEngines = require('./pattern_engines'); +var path = require('path'); +var extend = require('util')._extend; + +// Pattern properties + +var Pattern = function (relPath, data) { + // We expect relPath to be the path of the pattern template, relative to the + // root of the pattern tree. Parse out the path parts and save the useful ones. + var pathObj = path.parse(relPath); + this.relPath = relPath; // '00-atoms/00-global/00-colors.mustache' + this.fileName = pathObj.name; // '00-colors' + this.subdir = pathObj.dir; // '00-atoms/00-global' + this.fileExtension = pathObj.ext; // '.mustache' + + // this is the unique name, subDir + fileName (sans extension) + this.name = this.subdir.replace(/[\/\\]/g, '-') + '-' + this.fileName.replace('~', '-'); // '00-atoms-00-global-00-colors' + + // the JSON used to render values in the pattern this.jsonFileData = data || {}; - this.patternName = this.fileName.replace(/^\d*\-/, ''); - this.patternDisplayName = this.patternName.split('-').reduce(function (val, working) { + + // strip leading "00-" from the file name and flip tildes to dashes + this.patternBaseName = this.fileName.replace(/^\d*\-/, '').replace('~', '-'); // 'colors' + + // Fancy name. No idea how this works. 'Colors' + this.patternName = this.patternBaseName.split('-').reduce(function (val, working) { return val.charAt(0).toUpperCase() + val.slice(1) + ' ' + working.charAt(0).toUpperCase() + working.slice(1); }, '').trim(); //this is the display name for the ui. strip numeric + hyphen prefixes - this.patternLink = this.name + '/' + this.name + '.html'; + + // calculated path from the root of the public directory to the generated html + // file for this pattern + this.patternLink = this.name + '/' + this.name + '.html'; // '00-atoms-00-global-00-colors/00-atoms-00-global-00-colors.html' + + // the top-level pattern group this pattern belongs to. 'atoms' this.patternGroup = this.name.substring(this.name.indexOf('-') + 1, this.name.indexOf('-', 4) + 1 - this.name.indexOf('-') + 1); - this.patternSubGroup = subdir.substring(subdir.indexOf('/') + 4); - this.flatPatternPath = subdir.replace(/[\/\\]/g, '-'); - this.key = this.patternGroup + '-' + this.patternName; + + // the sub-group this pattern belongs to. + this.patternSubGroup = path.basename(this.subdir).replace(/^\d*-/, ''); // 'global' + + // Not sure what this is used for. + this.flatPatternPath = this.subdir.replace(/[\/\\]/g, '-'); // '00-atoms-00-global' + + // The canonical "key" by which this pattern is known. This is the callable + // name of the pattern. UPDATE: this.key is now known as this.patternPartial + this.patternPartial = this.patternGroup + '-' + this.patternBaseName; + this.template = ''; - this.patternPartial = ''; + this.patternPartialCode = ''; this.lineage = []; this.lineageIndex = []; this.lineageR = []; this.lineageRIndex = []; + this.isPseudoPattern = false; + this.engine = patternEngines.getEngineForPattern(this); }; -var oBucket = function (name) { - this.bucketNameLC = name; - this.bucketNameUC = name.split('-').reduce(function (val, working) { +// Pattern methods + +Pattern.prototype = { + + // render method on oPatterns; this acts as a proxy for the PatternEngine's + // render function + render: function (data, partials) { + return this.engine.renderPattern(this, data || this.jsonFileData, partials); + }, + + registerPartial: function () { + if (typeof this.engine.registerPartial === 'function') { + this.engine.registerPartial(this); + } + }, + + // the finders all delegate to the PatternEngine, which also encapsulates all + // appropriate regexes + findPartials: function () { + return this.engine.findPartials(this); + }, + + findPartialsWithStyleModifiers: function () { + return this.engine.findPartialsWithStyleModifiers(this); + }, + + findPartialsWithPatternParameters: function () { + return this.engine.findPartialsWithPatternParameters(this); + }, + + findListItems: function () { + return this.engine.findListItems(this); + }, + + findPartial: function (partialString) { + return this.engine.findPartial(partialString); + } +}; + +// Pattern static methods + +// factory: creates an empty Pattern for miscellaneous internal use, such as +// by list_item_hunter +Pattern.createEmpty = function (customProps) { + var pattern = new Pattern('', null); + return extend(pattern, customProps); +}; + +// factory: creates an Pattern object on-demand from a hash; the hash accepts +// parameters that replace the positional parameters that the Pattern +// constructor takes. +Pattern.create = function (relPath, data, customProps) { + var newPattern = new Pattern(relPath || '', data || null); + return extend(newPattern, customProps); +}; + + +var oPatternType = function (name) { + this.patternTypeLC = name; + this.patternTypeUC = name.split('-').reduce(function (val, working) { return val.charAt(0).toUpperCase() + val.slice(1) + ' ' + working.charAt(0).toUpperCase() + working.slice(1); }, '').trim(); - this.navItems = []; - this.navItemsIndex = []; + this.patternTypeItems = []; + this.patternTypeItemsIndex = []; this.patternItems = []; this.patternItemsIndex = []; }; -var oNavItem = function (name) { - this.sectionNameLC = name; - this.sectionNameUC = name.split('-').reduce(function (val, working) { + +var oPatternSubType = function (name) { + this.patternSubtypeLC = name; + this.patternSubtypeUC = name.split('-').reduce(function (val, working) { return val.charAt(0).toUpperCase() + val.slice(1) + ' ' + working.charAt(0).toUpperCase() + working.slice(1); }, '').trim(); - this.navSubItems = []; - this.navSubItemsIndex = []; + this.patternSubtypeItems = []; + this.patternSubtypeItemsIndex = []; }; -var oNavSubItem = function (name) { + +var oPatternSubTypeItem = function (name) { this.patternPath = ''; - this.patternPartial = ''; + this.patternPartialCode = ''; this.patternName = name.split(' ').reduce(function (val, working) { return val.charAt(0).toUpperCase() + val.slice(1) + ' ' + working.charAt(0).toUpperCase() + working.slice(1); }, '').trim(); }; + module.exports = { - oPattern: oPattern, - oBucket: oBucket, - oNavItem: oNavItem, - oNavSubItem: oNavSubItem + Pattern: Pattern, + oPatternType: oPatternType, + oPatternSubType: oPatternSubType, + oPatternSubTypeItem: oPatternSubTypeItem }; - diff --git a/core/lib/parameter_hunter.js b/core/lib/parameter_hunter.js index 0f5b6fdbe..0ae1ebf2f 100644 --- a/core/lib/parameter_hunter.js +++ b/core/lib/parameter_hunter.js @@ -1,13 +1,3 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - "use strict"; var parameter_hunter = function () { @@ -16,8 +6,9 @@ var parameter_hunter = function () { JSON5 = require('json5'), pa = require('./pattern_assembler'), smh = require('./style_modifier_hunter'), - pattern_assembler = new pa(), - style_modifier_hunter = new smh(); + plutils = require('./utilities'), + style_modifier_hunter = new smh(), + pattern_assembler = new pa(); /** * This function is really to accommodate the lax JSON-like syntax allowed by @@ -249,7 +240,7 @@ var parameter_hunter = function () { pattern.parameteredPartials.forEach(function (pMatch) { //find the partial's name and retrieve it var partialName = pMatch.match(/([\w\-\.\/~]+)/g)[0]; - var partialPattern = pattern_assembler.get_pattern_by_key(partialName, patternlab); + var partialPattern = pattern_assembler.findPartial(partialName, patternlab); //if we retrieved a pattern we should make sure that its extendedTemplate is reset. looks to fix #190 partialPattern.extendedTemplate = partialPattern.template; @@ -273,12 +264,12 @@ var parameter_hunter = function () { globalData = JSON5.parse(JSON5.stringify(patternlab.data)); localData = JSON5.parse(JSON5.stringify(pattern.jsonFileData || {})); } catch (err) { - console.log('There was an error parsing JSON for ' + pattern.abspath); + console.log('There was an error parsing JSON for ' + pattern.relPath); console.log(err); } - var allData = pattern_assembler.merge_data(globalData, localData); - allData = pattern_assembler.merge_data(allData, paramData); + var allData = plutils.mergeData(globalData, localData); + allData = plutils.mergeData(allData, paramData); //if partial has style modifier data, replace the styleModifier value if (pattern.stylePartials && pattern.stylePartials.length > 0) { @@ -294,7 +285,7 @@ var parameter_hunter = function () { pattern.extendedTemplate = pattern.extendedTemplate.replace(pMatch, renderedPartial); //update the extendedTemplate in the partials object in case this pattern is consumed later - patternlab.partials[pattern.key] = pattern.extendedTemplate; + patternlab.partials[pattern.patternPartial] = pattern.extendedTemplate; }); } } diff --git a/core/lib/pattern_assembler.js b/core/lib/pattern_assembler.js index 8ab0e9c0a..829cc0bc8 100644 --- a/core/lib/pattern_assembler.js +++ b/core/lib/pattern_assembler.js @@ -1,86 +1,45 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - "use strict"; var pattern_assembler = function () { + var path = require('path'), + fs = require('fs-extra'), + Pattern = require('./object_factory').Pattern, + pph = require('./pseudopattern_hunter'), + md = require('markdown-it')(), + plutils = require('./utilities'), + patternEngines = require('./pattern_engines'); - // returns any patterns that match {{> value:mod }} or {{> value:mod(foo:"bar") }} within the pattern - function findPartialsWithStyleModifiers(pattern) { - var matches = pattern.template.match(/{{>([ ])?([\w\-\.\/~]+)(?!\()(\:[A-Za-z0-9-_|]+)+(?:(| )\(.*)?([ ])?}}/g); - return matches; - } - // returns any patterns that match {{> value(foo:"bar") }} or {{> value:mod(foo:"bar") }} within the pattern - function findPartialsWithPatternParameters(pattern) { - var matches = pattern.template.match(/{{>([ ])?([\w\-\.\/~]+)(?:\:[A-Za-z0-9-_|]+)?(?:(| )\(.*)+([ ])?}}/g); - return matches; - } - - //find and return any {{> template-name* }} within pattern - function findPartials(pattern) { - var matches = pattern.template.match(/{{>([ ])?([\w\-\.\/~]+)(?:\:[A-Za-z0-9-_|]+)?(?:(| )\(.*)?([ ])?}}/g); - return matches; - } + // HELPER FUNCTIONS - function findListItems(pattern) { - var matches = pattern.template.match(/({{#( )?)(list(I|i)tems.)(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)( )?}}/g); - return matches; - } - - function setState(pattern, patternlab) { - if (patternlab.config.patternStates && patternlab.config.patternStates[pattern.key]) { - pattern.patternState = patternlab.config.patternStates[pattern.key]; - } else { - pattern.patternState = ""; - } - } - - function addPattern(pattern, patternlab) { - //add the link to the global object - patternlab.data.link[pattern.patternGroup + '-' + pattern.patternName] = '/patterns/' + pattern.patternLink; - - //only push to array if the array doesn't contain this pattern - var isNew = true, i; - for (i = 0; i < patternlab.patterns.length; i++) { - //so we need the identifier to be unique, which patterns[i].abspath is - if (pattern.abspath === patternlab.patterns[i].abspath) { - //if abspath already exists, overwrite that element - patternlab.patterns[i] = pattern; - patternlab.partials[pattern.key] = pattern.extendedTemplate || pattern.template; - isNew = false; - break; + function getPartial(partialName, patternlab) { + //look for exact partial matches + for (var i = 0; i < patternlab.patterns.length; i++) { + if (patternlab.patterns[i].patternPartial === partialName) { + return patternlab.patterns[i]; } } - //if the pattern is new, just push to the array - if (isNew) { - patternlab.patterns.push(pattern); - patternlab.partials[pattern.key] = pattern.extendedTemplate || pattern.template; + //else look by verbose syntax + for (var i = 0; i < patternlab.patterns.length; i++) { + switch (partialName) { + case patternlab.patterns[i].subdir + '/' + patternlab.patterns[i].fileName: + case patternlab.patterns[i].subdir + '/' + patternlab.patterns[i].fileName + '.mustache': + return patternlab.patterns[i]; + } } - } - function renderPattern(template, data, partials) { - var mustache = require('mustache'); + //return the fuzzy match if all else fails + for (var i = 0; i < patternlab.patterns.length; i++) { + var partialParts = partialName.split('-'), + partialType = partialParts[0], + partialNameEnd = partialParts.slice(1).join('-'); - if (partials) { - return mustache.render(template, data, partials); - } else { - return mustache.render(template, data); + if (patternlab.patterns[i].patternPartial.split('-')[0] === partialType && patternlab.patterns[i].patternPartial.indexOf(partialNameEnd) > -1) { + return patternlab.patterns[i]; + } } - } - - //http://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array-in-javascript - function shuffle(o) { - for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); //eslint-disable-line curly - return o; + throw 'Could not find pattern with partial ' + partialName; } function buildListItems(container) { @@ -91,81 +50,102 @@ var pattern_assembler = function () { list.push(container.listitems[item]); } } - container.listItemArray = shuffle(list); + container.listItemArray = plutils.shuffle(list); for (var i = 1; i <= container.listItemArray.length; i++) { var tempItems = []; if (i === 1) { tempItems.push(container.listItemArray[0]); - container.listitems['' + i] = tempItems; + container.listitems['' + i ] = tempItems; } else { for (var c = 1; c <= i; c++) { tempItems.push(container.listItemArray[c - 1]); - container.listitems['' + i] = tempItems; + container.listitems['' + i ] = tempItems; } } } } - function getpatternbykey(key, patternlab) { - var i; // for the for loops - - //look for exact key matches - for (i = 0; i < patternlab.patterns.length; i++) { - if (patternlab.patterns[i].key === key) { - return patternlab.patterns[i]; - } + function setState(pattern, patternlab) { + if (patternlab.config.patternStates && patternlab.config.patternStates[pattern.patternPartial]) { + pattern.patternState = patternlab.config.patternStates[pattern.patternPartial]; + } else { + pattern.patternState = ""; } + } - //else look by verbose syntax - for (i = 0; i < patternlab.patterns.length; i++) { - switch (key) { - case patternlab.patterns[i].subdir + '/' + patternlab.patterns[i].fileName: - case patternlab.patterns[i].subdir + '/' + patternlab.patterns[i].fileName + '.mustache': - return patternlab.patterns[i]; + function addPattern(pattern, patternlab) { + + //add the link to the global object + patternlab.data.link[pattern.patternGroup + '-' + pattern.patternBaseName] = '/patterns/' + pattern.patternLink; + + //only push to array if the array doesn't contain this pattern + var isNew = true; + for (var i = 0; i < patternlab.patterns.length; i++) { + //so we need the identifier to be unique, which patterns[i].relPath is + if (pattern.relPath === patternlab.patterns[i].relPath) { + //if relPath already exists, overwrite that element + patternlab.patterns[i] = pattern; + patternlab.partials[pattern.patternPartial] = pattern.extendedTemplate || pattern.template; + isNew = false; + break; } } - //return the fuzzy match if all else fails - for (i = 0; i < patternlab.patterns.length; i++) { - var keyParts = key.split('-'), - keyType = keyParts[0], - keyName = keyParts.slice(1).join('-'); + // if the pattern is new, we must register it with various data structures! + if (isNew) { - if (patternlab.patterns[i].key.split('-')[0] === keyType && patternlab.patterns[i].key.indexOf(keyName) > -1) { - return patternlab.patterns[i]; + if (patternlab.config.debug) { + console.log('found new pattern ' + pattern.patternPartial); } + + // do global registration + patternlab.patterns.push(pattern); + patternlab.partials[pattern.patternPartial] = pattern.extendedTemplate || pattern.template; + + // do plugin-specific registration + pattern.registerPartial(); } - throw 'Could not find pattern with key ' + key; } - function processPatternIterative(file, patternlab) { - var fs = require('fs-extra'), - of = require('./object_factory'), - path = require('path'); + // Render a pattern on request. Long-term, this should probably go away. + function renderPattern(pattern, data, partials) { + // if we've been passed a full Pattern, it knows what kind of template it + // is, and how to render itself, so we just call its render method + if (pattern instanceof Pattern) { + return pattern.render(data, partials); + } else { + // otherwise, assume it's a plain mustache template string, and we + // therefore just need to create a dummpy pattern to be able to render + // it + var dummyPattern = Pattern.createEmpty({extendedTemplate: pattern}); + return patternEngines.mustache.renderPattern(dummyPattern, data, partials); + } + } + + function processPatternIterative(relPath, patternlab) { + + var pseudopattern_hunter = new pph(); //extract some information - var subdir = path.dirname(path.relative(patternlab.config.paths.source.patterns, file)).replace('\\', '/'); - var filename = path.basename(file); + var filename = path.basename(relPath); var ext = path.extname(filename); + var patternsPath = patternlab.config.paths.source.patterns; - //ignore dotfiles, underscored files, and non-variant .json files - if (filename.charAt(0) === '.' || (ext === '.json' && filename.indexOf('~') === -1)) { - return; - } + // skip non-pattern files + if (!patternEngines.isPatternFile(filename, patternlab)) { return null; } //make a new Pattern Object - var currentPattern = new of.oPattern(file, subdir, filename); + var currentPattern = new Pattern(relPath); - //if file is named in the syntax for variants, no need to process further - //processPatternRecursive() will run find_pseudopatterns() and look at each pattern for a variant - if (ext === '.json' && filename.indexOf('~') > -1) { - return; + //if file is named in the syntax for variants + if (patternEngines.isPseudoPatternJSON(filename)) { + return currentPattern; } - //can ignore all non-mustache files at this point - if (ext !== '.mustache') { - return; + //can ignore all non-supported files at this point + if (patternEngines.isFileExtensionSupported(ext) === false) { + return currentPattern; } //see if this file has a state @@ -173,122 +153,114 @@ var pattern_assembler = function () { //look for a json file for this template try { - var jsonFilename = path.resolve(patternlab.config.paths.source.patterns, currentPattern.subdir, currentPattern.fileName + ".json"); - currentPattern.jsonFileData = fs.readJSONSync(jsonFilename); - if (patternlab.config.debug) { - console.log('found pattern-specific data.json for ' + currentPattern.key); + var jsonFilename = path.resolve(patternsPath, currentPattern.subdir, currentPattern.fileName + ".json"); + try { + var jsonFilenameStats = fs.statSync(jsonFilename); + } catch (err) { + //not a file + } + if (jsonFilenameStats && jsonFilenameStats.isFile()) { + currentPattern.jsonFileData = fs.readJSONSync(jsonFilename); + if (patternlab.config.debug) { + console.log('processPatternIterative: found pattern-specific data.json for ' + currentPattern.patternPartial); + } } } - catch (error) { - // do nothing + catch (err) { + console.log('There was an error parsing sibling JSON for ' + currentPattern.relPath); + console.log(err); } //look for a listitems.json file for this template try { - var listJsonFileName = path.resolve(patternlab.config.paths.source.patterns, currentPattern.subdir, currentPattern.fileName + ".listitems.json"); - currentPattern.listitems = fs.readJSONSync(listJsonFileName); - buildListItems(currentPattern); - if (patternlab.config.debug) { - console.log('found pattern-specific listitems.json for ' + currentPattern.key); + var listJsonFileName = path.resolve(patternsPath, currentPattern.subdir, currentPattern.fileName + ".listitems.json"); + try { + var listJsonFileStats = fs.statSync(listJsonFileName); + } catch (err) { + //not a file + } + if (listJsonFileStats && listJsonFileStats.isFile()) { + currentPattern.listitems = fs.readJSONSync(listJsonFileName); + buildListItems(currentPattern); + if (patternlab.config.debug) { + console.log('found pattern-specific listitems.json for ' + currentPattern.patternPartial); + } } } catch (err) { + console.log('There was an error parsing sibling listitem JSON for ' + currentPattern.relPath); + console.log(err); + } + + //look for a markdown file for this template + try { + var markdownFileName = path.resolve(patternlab.config.paths.source.patterns, currentPattern.subdir, currentPattern.fileName + ".md"); + var markdownFileContents = fs.readFileSync(markdownFileName, 'utf8'); + currentPattern.patternDescExists = true; + currentPattern.patternDesc = md.render(markdownFileContents); + if (patternlab.config.debug) { + console.log('found pattern-specific markdown-documentation.md for ' + currentPattern.patternPartial); + } + } + catch (e) { // do nothing } //add the raw template to memory - currentPattern.template = fs.readFileSync(file, 'utf8'); + currentPattern.template = fs.readFileSync(path.resolve(patternsPath, relPath), 'utf8'); //find any stylemodifiers that may be in the current pattern - currentPattern.stylePartials = findPartialsWithStyleModifiers(currentPattern); + currentPattern.stylePartials = currentPattern.findPartialsWithStyleModifiers(); //find any pattern parameters that may be in the current pattern - currentPattern.parameteredPartials = findPartialsWithPatternParameters(currentPattern); + currentPattern.parameteredPartials = currentPattern.findPartialsWithPatternParameters(); //add currentPattern to patternlab.patterns array addPattern(currentPattern, patternlab); + + //look for a pseudo pattern by checking if there is a file containing same name, with ~ in it, ending in .json + pseudopattern_hunter.find_pseudopatterns(currentPattern, patternlab); + + return currentPattern; } function processPatternRecursive(file, patternlab) { var lh = require('./lineage_hunter'), - ph = require('./parameter_hunter'), - pph = require('./pseudopattern_hunter'), - lih = require('./list_item_hunter'), - smh = require('./style_modifier_hunter'); + lih = require('./list_item_hunter'); - var parameter_hunter = new ph(), - lineage_hunter = new lh(), - list_item_hunter = new lih(), - style_modifier_hunter = new smh(), - pseudopattern_hunter = new pph(); + var lineage_hunter = new lh(), + list_item_hunter = new lih(); - //find current pattern in patternlab object using var file as a key - var currentPattern, - i; + //find current pattern in patternlab object using var file as a partial + var currentPattern, i; for (i = 0; i < patternlab.patterns.length; i++) { - if (patternlab.patterns[i].abspath === file) { + if (patternlab.patterns[i].relPath === file) { currentPattern = patternlab.patterns[i]; - break; } } //return if processing an ignored file - if (typeof currentPattern === 'undefined') { - return; - } + if (typeof currentPattern === 'undefined') { return; } currentPattern.extendedTemplate = currentPattern.template; //find how many partials there may be for the given pattern - var foundPatternPartials = findPartials(currentPattern); + var foundPatternPartials = currentPattern.findPartials(currentPattern); - if (foundPatternPartials !== null && foundPatternPartials.length > 0) { + //find any listItem blocks that within the pattern, even if there are no partials + list_item_hunter.process_list_item_partials(currentPattern, patternlab); - if (patternlab.config.debug) { - console.log('found partials for ' + currentPattern.key); - } + // expand any partials present in this pattern; that is, drill down into + // the template and replace their calls in this template with rendered + // results + if (currentPattern.engine.expandPartials && (foundPatternPartials !== null && foundPatternPartials.length > 0)) { + // eslint-disable-next-line + expandPartials(foundPatternPartials, list_item_hunter, patternlab, currentPattern); - //find any listItem blocks - list_item_hunter.process_list_item_partials(currentPattern, patternlab); - - //determine if the template contains any pattern parameters. if so they must be immediately consumed - parameter_hunter.find_parameters(currentPattern, patternlab); - - //do something with the regular old partials - for (i = 0; i < foundPatternPartials.length; i++) { - var partialKey = foundPatternPartials[i].replace(/{{>([ ])?([\w\-\.\/~]+)(:[A-z0-9-_|]+)?(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)?([ ])?}}/g, '$2'); - - var partialPath; - - //identify which pattern this partial corresponds tou - for (var j = 0; j < patternlab.patterns.length; j++) { - if (patternlab.patterns[j].key === partialKey || - patternlab.patterns[j].abspath.indexOf(partialKey) > -1) { - partialPath = patternlab.patterns[j].abspath; - } - } - - //recurse through nested partials to fill out this extended template. - processPatternRecursive(partialPath, patternlab); - - //complete assembly of extended template - var partialPattern = getpatternbykey(partialKey, patternlab); - - //if partial has style modifier data, replace the styleModifier value - if (currentPattern.stylePartials && currentPattern.stylePartials.length > 0) { - style_modifier_hunter.consume_style_modifier(partialPattern, foundPatternPartials[i], patternlab); - } - - currentPattern.extendedTemplate = currentPattern.extendedTemplate.replace(foundPatternPartials[i], partialPattern.extendedTemplate); - - //update the extendedTemplate in the partials object in case this pattern is consumed later - patternlab.partials[currentPattern.key] = currentPattern.extendedTemplate; - } - - } else { - //find any listItem blocks that within the pattern, even if there are no partials - list_item_hunter.process_list_item_partials(currentPattern, patternlab); + // update the extendedTemplate in the partials object in case this + // pattern is consumed later + patternlab.partials[currentPattern.patternPartial] = currentPattern.extendedTemplate; } //find pattern lineage @@ -296,39 +268,50 @@ var pattern_assembler = function () { //add to patternlab object so we can look these up later. addPattern(currentPattern, patternlab); - - //look for a pseudo pattern by checking if there is a file containing same name, with ~ in it, ending in .json - pseudopattern_hunter.find_pseudopatterns(currentPattern, patternlab); } - function mergeData(obj1, obj2) { - if (typeof obj2 === 'undefined') { - obj2 = {}; //eslint-disable-line no-param-reassign - } + function expandPartials(foundPatternPartials, list_item_hunter, patternlab, currentPattern) { + var smh = require('./style_modifier_hunter'), + ph = require('./parameter_hunter'); - for (var p in obj1) { //eslint-disable-line guard-for-in - try { - // Only recurse if obj1[p] is an object. - if (obj1[p].constructor === Object) { - // Requires 2 objects as params; create obj2[p] if undefined. - if (typeof obj2[p] === 'undefined') { - obj2[p] = {}; - } - obj2[p] = mergeData(obj1[p], obj2[p]); + var style_modifier_hunter = new smh(), + parameter_hunter = new ph(); - // Pop when recursion meets a non-object. If obj1[p] is a non-object, - // only copy to undefined obj2[p]. This way, obj2 maintains priority. - } else if (typeof obj2[p] === 'undefined') { - obj2[p] = obj1[p]; - } - } catch (e) { - // Property in destination object not set; create it and set its value. - if (typeof obj2[p] === 'undefined') { - obj2[p] = obj1[p]; + if (patternlab.config.debug) { + console.log('found partials for ' + currentPattern.patternPartial); + } + + // determine if the template contains any pattern parameters. if so they + // must be immediately consumed + parameter_hunter.find_parameters(currentPattern, patternlab); + + //do something with the regular old partials + for (var i = 0; i < foundPatternPartials.length; i++) { + var partial = currentPattern.findPartial(foundPatternPartials[i]); + var partialPath; + + //identify which pattern this partial corresponds to + for (var j = 0; j < patternlab.patterns.length; j++) { + if (patternlab.patterns[j].patternPartial === partial || + patternlab.patterns[j].relPath.indexOf(partial) > -1) + { + partialPath = patternlab.patterns[j].relPath; } } + + //recurse through nested partials to fill out this extended template. + processPatternRecursive(partialPath, patternlab); + + //complete assembly of extended template + var partialPattern = getPartial(partial, patternlab); + + //if partial has style modifier data, replace the styleModifier value + if (currentPattern.stylePartials && currentPattern.stylePartials.length > 0) { + style_modifier_hunter.consume_style_modifier(partialPattern, foundPatternPartials[i], patternlab); + } + + currentPattern.extendedTemplate = currentPattern.extendedTemplate.replace(foundPatternPartials[i], partialPattern.extendedTemplate); } - return obj2; } function parseDataLinksHelper(patternlab, obj, key) { @@ -371,22 +354,22 @@ var pattern_assembler = function () { //loop through all patterns for (var i = 0; i < patternlab.patterns.length; i++) { - patternlab.patterns[i].jsonFileData = parseDataLinksHelper(patternlab, patternlab.patterns[i].jsonFileData, patternlab.patterns[i].key); + patternlab.patterns[i].jsonFileData = parseDataLinksHelper(patternlab, patternlab.patterns[i].jsonFileData, patternlab.patterns[i].partial); } } return { find_pattern_partials: function (pattern) { - return findPartials(pattern); + return pattern.findPartials(); }, find_pattern_partials_with_style_modifiers: function (pattern) { - return findPartialsWithStyleModifiers(pattern); + return pattern.findPartialsWithStyleModifiers(); }, find_pattern_partials_with_parameters: function (pattern) { - return findPartialsWithPatternParameters(pattern); + return pattern.findPartialsWithPatternParameters(); }, find_list_items: function (pattern) { - return findListItems(pattern); + return pattern.findListItems(); }, setPatternState: function (pattern, patternlab) { setState(pattern, patternlab); @@ -398,27 +381,25 @@ var pattern_assembler = function () { return renderPattern(template, data, partials); }, process_pattern_iterative: function (file, patternlab) { - processPatternIterative(file, patternlab); + return processPatternIterative(file, patternlab); }, process_pattern_recursive: function (file, patternlab, additionalData) { processPatternRecursive(file, patternlab, additionalData); }, - get_pattern_by_key: function (key, patternlab) { - return getpatternbykey(key, patternlab); - }, - merge_data: function (existingData, newData) { - return mergeData(existingData, newData); + findPartial: function (partial, patternlab) { + return getPartial(partial, patternlab); }, combine_listItems: function (patternlab) { buildListItems(patternlab); }, parse_data_links: function (patternlab) { parseDataLinks(patternlab); + }, + parse_data_links_specific: function (patternlab, data, label) { + return parseDataLinksHelper(patternlab, data, label) } }; }; module.exports = pattern_assembler; - - diff --git a/core/lib/pattern_engines.js b/core/lib/pattern_engines.js new file mode 100644 index 000000000..acfe5f9db --- /dev/null +++ b/core/lib/pattern_engines.js @@ -0,0 +1,197 @@ +/* + * patternlab-node - v0.10.1 - 2015 + * + * Geoffrey Pursell, Brian Muenzenmeyer, and the web community. + * Licensed under the MIT license. + * + * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. + * + */ + +'use strict'; + +var path = require('path'); +var diveSync = require('diveSync'); +var engineMatcher = /^patternengine-node-(.*)$/; +var enginesDirectories = [ + { + displayName: 'the core', + path: path.resolve(__dirname, '..', '..', 'node_modules') + }, + { + displayName: 'the edition or test directory', + path: path.join(process.cwd(), 'node_modules') + } +]; +var PatternEngines; // the main export object +var engineNameForExtension; // generated mapping of extension to engine name + + +// free "private" functions, for internal setup only + +// given a path: return the engine name if the path points to a valid engine +// module directory, or false if it doesn't +function isEngineModule(filePath) { + var baseName = path.basename(filePath); + var engineMatch = baseName.match(engineMatcher); + + if (engineMatch) { return engineMatch[1]; } + return false; +} + +function findEngineModulesInDirectory(dir) { + var foundEngines = []; + + diveSync(dir, { + recursive: false, + directories: true + }, function (err, filePath) { + if (err) { throw err; } + var foundEngineName = isEngineModule(filePath); + if (foundEngineName) { + foundEngines.push({ + name: foundEngineName, + modulePath: filePath + }); + } + }); + + return foundEngines; +} + +// Try to load engines! We scan for engines at each path specified above. This +// function is kind of a big deal. +function loadAllEngines(enginesObject) { + console.log('\nLoading engines...'); + + enginesDirectories.forEach(function (engineDirectory) { + var enginesInThisDir = findEngineModulesInDirectory(engineDirectory.path); + console.log("...scanning for engines in", engineDirectory.displayName + "..."); + + // find all engine-named things in this directory and try to load them, + // unless it's already been loaded. + enginesInThisDir.forEach(function (engineDiscovery) { + var errorMessage; + var successMessage = "good to go"; + + try { + // give it a try! load 'er up. But not if we already have, of course. + if (enginesObject[engineDiscovery.name]) { + throw new Error("already loaded, skipping."); + } + enginesObject[engineDiscovery.name] = require(engineDiscovery.modulePath); + } catch (err) { + errorMessage = err.message; + } finally { + // report on the status of the engine, one way or another! + console.log('-', engineDiscovery.name, 'engine:', errorMessage ? errorMessage : successMessage); + } + }); + }); + + // Complain if for some reason we haven't loaded any engines. + if (Object.keys(enginesObject).length === 0) { + throw new Error('No engines loaded! Something is seriously wrong.'); + } + console.log('...done loading engines.\n'); +} + + +// produce a mapping between file extension and engine name for each of the +// loaded engines +function createFileExtensionToEngineNameMap(enginesObject) { + var mapping = {}; + + Object.keys(enginesObject).forEach(function (engineName) { + var extensionForEngine = enginesObject[engineName].engineFileExtension; + mapping[extensionForEngine] = engineName; + }); + + return mapping; +} + + +// +// PatternEngines: the main export of this module +// +// It's an Object/hash of all loaded pattern engines, empty at first. My +// intention here is to make this return an object that can be used to obtain +// any loaded PatternEngine by addressing them like this: +// +// var PatternEngines = require('./pattern_engines/pattern_engines'); +// var Mustache = PatternEngines['mustache']; +// +// Object.create lets us create an object with a specified prototype. We want +// this here because we would like the object's "own properties" to include +// only the engine names so we can easily iterate over them; all the handy +// methods and properites below should therefore be on its prototype. + +PatternEngines = Object.create({ + getEngineNameForPattern: function (pattern) { + // avoid circular dependency by putting this in here. TODO: is this slow? + var of = require('./object_factory'); + + if (pattern instanceof of.Pattern && typeof pattern.fileExtension === 'string' && pattern.fileExtension) { + return engineNameForExtension[pattern.fileExtension]; + } + + // otherwise, assume it's a plain mustache template string and act + // accordingly + return 'mustache'; + }, + + getEngineForPattern: function (pattern) { + if (pattern.isPseudoPattern) { + return this.getEngineForPattern(pattern.basePattern); + } else { + var engineName = this.getEngineNameForPattern(pattern); + return this[engineName]; + } + }, + + getSupportedFileExtensions: function () { + var engineNames = Object.keys(PatternEngines); + return engineNames.map(function (engineName) { + return PatternEngines[engineName].engineFileExtension; + }); + }, + + isFileExtensionSupported: function (fileExtension) { + var supportedExtensions = PatternEngines.getSupportedFileExtensions(); + return (supportedExtensions.lastIndexOf(fileExtension) !== -1); + }, + + // given a filename, return a boolean: whether or not the filename indicates + // that the file is pseudopattern JSON + isPseudoPatternJSON: function (filename) { + var extension = path.extname(filename); + return (extension === '.json' && filename.indexOf('~') > -1); + }, + + // takes a filename string, not a full path; a basename (plus extension) + // ignore _underscored patterns, dotfiles, and anything not recognized by a + // loaded pattern engine. Pseudo-pattern .json files ARE considered to be + // pattern files! + isPatternFile: function (filename) { + // skip hidden patterns/files without a second thought + var extension = path.extname(filename); + if (filename.charAt(0) === '.' || + (extension === '.json' && !PatternEngines.isPseudoPatternJSON(filename))) { + return false; + } + + // not a hidden pattern, let's dig deeper + var supportedPatternFileExtensions = PatternEngines.getSupportedFileExtensions(); + return (supportedPatternFileExtensions.lastIndexOf(extension) !== -1 || + PatternEngines.isPseudoPatternJSON(filename)); + } +}); + + +// load up the engines we found +loadAllEngines(PatternEngines); + +// mapping of file extensions to engine names, for lookup use +engineNameForExtension = createFileExtensionToEngineNameMap(PatternEngines); + +module.exports = PatternEngines; diff --git a/core/lib/pattern_exporter.js b/core/lib/pattern_exporter.js index 5ff05e09c..c53768420 100644 --- a/core/lib/pattern_exporter.js +++ b/core/lib/pattern_exporter.js @@ -1,13 +1,3 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - "use strict"; var fs = require('fs-extra'); @@ -16,14 +6,14 @@ var pattern_exporter = function () { function exportPatterns(patternlab) { //read the config export options - var exportKeys = patternlab.config.patternExportKeys; + var exportPartials = patternlab.config.patternExportPatternPartials; //find the chosen patterns to export - for (var i = 0; i < exportKeys.length; i++) { + for (var i = 0; i < exportPartials.length; i++) { for (var j = 0; j < patternlab.patterns.length; j++) { - if (exportKeys[i] === patternlab.patterns[j].key) { + if (exportPartials[i] === patternlab.patterns[j].patternPartial) { //write matches to the desired location - fs.outputFileSync(patternlab.config.patternExportDirectory + patternlab.patterns[j].key + '.html', patternlab.patterns[j].patternPartial); + fs.outputFileSync(patternlab.config.patternExportDirectory + patternlab.patterns[j].patternPartial + '.html', patternlab.patterns[j].patternPartialCode); } } } diff --git a/core/lib/patternlab.js b/core/lib/patternlab.js index 7bd7ebcbc..45835b5e2 100644 --- a/core/lib/patternlab.js +++ b/core/lib/patternlab.js @@ -1,58 +1,163 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. +/* + * patternlab-node - v2.0.0 - 2016 + * + * Brian Muenzenmeyer, Geoff Pursell, and the web community. + * Licensed under the MIT license. + * + * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. * */ +"use strict"; + +var diveSync = require('diveSync'), + path = require('path'); + +// GTP: these two diveSync pattern processors factored out so they can be reused +// from unit tests to reduce code dupe! + +function processAllPatternsIterative(pattern_assembler, patterns_dir, patternlab) { + diveSync( + patterns_dir, + { + filter: function (thisPath, dir) { + if (dir) { + var remainingPath = thisPath.replace(patterns_dir, ''); + var isValidPath = remainingPath.indexOf('/_') === -1; + return isValidPath; + } + return true; + } + }, + function (err, file) { + //log any errors + if (err) { + console.log(err); + return; + } + pattern_assembler.process_pattern_iterative(path.relative(patterns_dir, file), patternlab); + } + ); +} + +function processAllPatternsRecursive(pattern_assembler, patterns_dir, patternlab) { + diveSync( + patterns_dir, + { + filter: function (thisPath, dir) { + if (dir) { + var remainingPath = thisPath.replace(patterns_dir, ''); + var isValidPath = remainingPath.indexOf('/_') === -1; + return isValidPath; + } + return true; + } + }, + function (err, file) { + //log any errors + if (err) { + console.log(err); + return; + } + pattern_assembler.process_pattern_recursive(path.relative(patterns_dir, file), patternlab); + } + ); +} + var patternlab_engine = function (config) { 'use strict'; - var path = require('path'), - JSON5 = require('json5'), + var JSON5 = require('json5'), fs = require('fs-extra'), - diveSync = require('diveSync'), - of = require('./object_factory'), pa = require('./pattern_assembler'), - mh = require('./media_hunter'), pe = require('./pattern_exporter'), lh = require('./lineage_hunter'), + buildFrontEnd = require('./ui_builder'), he = require('html-entities').AllHtmlEntities, + plutils = require('./utilities'), + sm = require('./starterkit_manager'), patternlab = {}; - patternlab.package = fs.readJSONSync('./package.json'); + patternlab.package = fs.readJSONSync(path.resolve(__dirname, '../../package.json')); patternlab.config = config || fs.readJSONSync(path.resolve(__dirname, '../../patternlab-config.json')); var paths = patternlab.config.paths; - function getVersion() { console.log(patternlab.package.version); } + function help() { - console.log('Patternlab Node Help'); + + console.log(''); + + console.log('|=======================================|'); + plutils.logGreen(' Pattern Lab Node Help v' + patternlab.package.version); + console.log('|=======================================|'); + + console.log(''); + console.log('Command Line Interface - usually consumed by an edition'); + console.log(''); + + plutils.logGreen(' patternlab:build'); + console.log(' > Compiles the patterns and frontend, outputting to config.paths.public'); + console.log(''); + + plutils.logGreen(' patternlab:patternsonly'); + console.log(' > Compiles the patterns only, outputting to config.paths.public'); + console.log(''); + + plutils.logGreen(' patternlab:version'); + console.log(' > Return the version of patternlab-node you have installed'); + console.log(''); + + plutils.logGreen(' patternlab:help'); + console.log(' > Get more information about patternlab-node, pattern lab in general, and where to report issues.'); + console.log(''); + + plutils.logGreen(' patternlab:liststarterkits'); + console.log(' > Returns a url with the list of available starterkits hosted on the Pattern Lab organization Github account'); + console.log(''); + + plutils.logGreen(' patternlab:loadstarterkit'); + console.log(' > Load a starterkit into config.paths.source/*'); + console.log(' > NOTE: Overwrites existing content, and only cleans out existing directory if --clean=true argument is passed.'); + console.log(' > NOTE: In most cases, `npm install starterkit-name` will precede this call.'); + console.log(' > arguments:'); + console.log(' -- kit '); + console.log(' > the name of the starter kit to load'); + console.log(' -- clean '); + console.log(' > removes all files from config.paths.source/ prior to load'); + console.log(' > example (gulp):'); + console.log(' `gulp patternlab:loadstarterkit --kit=starterkit-mustache-demo`'); + console.log(''); + console.log('==============================='); - console.log('Command Line Arguments'); - console.log('patternlab:only_patterns'); - console.log(' > Compiles the patterns only, outputting to config.patterns.public'); - console.log('patternlab:v'); - console.log(' > Retrieve the version of patternlab-node you have installed'); - console.log('patternlab:help'); - console.log(' > Get more information about patternlab-node, pattern lab in general, and where to report issues.'); + console.log(''); + console.log('Visit http://patternlab.io/ for more info about Pattern Lab'); + console.log('Visit https://github.com/pattern-lab/patternlab-node/issues to open an issue.'); + console.log('Visit https://github.com/pattern-lab/patternlab-node/wiki to view the changelog, roadmap, and other info.'); + console.log(''); console.log('==============================='); - console.log('Visit http://patternlab.io/docs/index.html for general help on pattern-lab'); - console.log('Visit https://github.com/pattern-lab/patternlab-node/issues to open a bug.'); } function printDebug() { + // A replacer function to pass to stringify below; this is here to prevent + // the debug output from blowing up into a massive fireball of circular + // references. This happens specifically with the Handlebars engine. Remove + // if you like 180MB log files. + function propertyStringReplacer(key, value) { + if (key === 'engine' && value.engineName) { + return '{' + value.engineName + ' engine object}'; + } + return value; + } + //debug file can be written by setting flag on patternlab-config.json if (patternlab.config.debug) { console.log('writing patternlab debug file to ./patternlab.json'); - fs.outputFileSync('./patternlab.json', JSON.stringify(patternlab, null, 3)); + fs.outputFileSync('./patternlab.json', JSON.stringify(patternlab, propertyStringReplacer, 3)); } } @@ -67,12 +172,40 @@ var patternlab_engine = function (config) { } } + function listStarterkits() { + var starterkit_manager = new sm(patternlab); + return starterkit_manager.list_starterkits(); + } + + function loadStarterKit(starterkitName, clean) { + var starterkit_manager = new sm(patternlab); + starterkit_manager.load_starterkit(starterkitName, clean); + } + function buildPatterns(deletePatternDir) { - patternlab.data = fs.readJSONSync(path.resolve(paths.source.data, 'data.json')); - patternlab.listitems = fs.readJSONSync(path.resolve(paths.source.data, 'listitems.json')); - patternlab.header = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/pattern-header-footer/header.html'), 'utf8'); - patternlab.footerPattern = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/pattern-header-footer/footer-pattern.html'), 'utf8'); - patternlab.footer = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/pattern-header-footer/footer.html'), 'utf8'); + try { + patternlab.data = fs.readJSONSync(path.resolve(paths.source.data, 'data.json')); + } catch (ex) { + plutils.logRed('missing or malformed' + paths.source.data + 'data.json Pattern Lab may not work without this file.'); + patternlab.data = {}; + } + try { + patternlab.listitems = fs.readJSONSync(path.resolve(paths.source.data, 'listitems.json')); + } catch (ex) { + plutils.logRed('missing or malformed' + paths.source.data + 'listitems.json Pattern Lab may not work without this file.'); + patternlab.listitems = {}; + } + try { + patternlab.header = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'partials', 'general-header.mustache'), 'utf8'); + patternlab.footer = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'partials', 'general-footer.mustache'), 'utf8'); + patternlab.patternSection = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'partials', 'patternSection.mustache'), 'utf8'); + patternlab.patternSectionSubType = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'partials', 'patternSectionSubtype.mustache'), 'utf8'); + patternlab.viewAll = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'viewall.mustache'), 'utf8'); + } catch (ex) { + console.log(ex); + plutils.logRed('\nERROR: missing an essential file from ' + paths.source.patternlabFiles + '. Pattern Lab won\'t work without this file.\n'); + process.exit(1); + } patternlab.patterns = []; patternlab.partials = {}; patternlab.data.link = {}; @@ -87,69 +220,30 @@ var patternlab_engine = function (config) { pattern_assembler.combine_listItems(patternlab); - //diveSync once to perform iterative populating of patternlab object - diveSync( - patterns_dir, - { - filter: function (filePath, dir) { - if (dir) { - var remainingPath = filePath.replace(patterns_dir, ''); - var isValidPath = remainingPath.indexOf('/_') === -1; - return isValidPath; - } - return true; - } - }, - function (err, file) { - //log any errors - if (err) { - console.log(err); - return; - } - pattern_assembler.process_pattern_iterative(path.resolve(file), patternlab); - } - ); + // diveSync once to perform iterative populating of patternlab object + processAllPatternsIterative(pattern_assembler, patterns_dir, patternlab); //diveSync again to recursively include partials, filling out the //extendedTemplate property of the patternlab.patterns elements - diveSync( - patterns_dir, - { - filter: function (filePath, dir) { - if (dir) { - var remainingPath = filePath.replace(patterns_dir, ''); - var isValidPath = remainingPath.indexOf('/_') === -1; - return isValidPath; - } - return true; - } - }, - function (err, file) { - //log any errors - if (err) { - console.log(err); - return; - } - pattern_assembler.process_pattern_recursive(path.resolve(file), patternlab); - }); + processAllPatternsRecursive(pattern_assembler, patterns_dir, patternlab); //set user defined head and foot if they exist try { - patternlab.userHead = pattern_assembler.get_pattern_by_key('atoms-head', patternlab); + patternlab.userHead = fs.readFileSync(path.resolve(paths.source.meta, '_00-head.mustache'), 'utf8'); } catch (ex) { if (patternlab.config.debug) { console.log(ex); - console.log('Could not find optional user-defined header, atoms-head pattern. It was likely deleted.'); + console.log('Could not find optional user-defined header, usually found at ./source/_meta/_00-head.mustache. It was likely deleted.'); } } try { - patternlab.userFoot = pattern_assembler.get_pattern_by_key('atoms-foot', patternlab); + patternlab.userFoot = fs.readFileSync(path.resolve(paths.source.meta, '_01-foot.mustache'), 'utf8'); } catch (ex) { if (patternlab.config.debug) { console.log(ex); - console.log('Could not find optional user-defined footer, atoms-foot pattern. It was likely deleted.'); + console.log('Could not find optional user-defined footer, usually found at ./source/_meta/_01-foot.mustache. It was likely deleted.'); } } @@ -162,451 +256,138 @@ var patternlab_engine = function (config) { //delete the contents of config.patterns.public before writing if (deletePatternDir) { + fs.removeSync(paths.public.patterns); fs.emptyDirSync(paths.public.patterns); } //set pattern-specific header if necessary var head; if (patternlab.userHead) { - head = patternlab.userHead.extendedTemplate.replace('{% pattern-lab-head %}', patternlab.header); + head = patternlab.userHead.replace('{% pattern-lab-head %}', patternlab.header); } else { head = patternlab.header; } + //set the pattern-specific header by compiling the general-header with data, and then adding it to the meta header + patternlab.data.patternLabHead = pattern_assembler.renderPattern(patternlab.header, { + cacheBuster: patternlab.cacheBuster + }); + //render all patterns last, so lineageR works patternlab.patterns.forEach(function (pattern) { pattern.header = head; - //json stringify lineage and lineageR - var lineageArray = []; - for (var i = 0; i < pattern.lineage.length; i++) { - lineageArray.push(JSON5.stringify(pattern.lineage[i])); - } - pattern.lineage = lineageArray; - - var lineageRArray = []; - for (var i = 0; i < pattern.lineageR.length; i++) { - lineageRArray.push(JSON5.stringify(pattern.lineageR[i])); - } - pattern.lineageR = lineageRArray; + //todo move this into lineage_hunter + pattern.patternLineages = pattern.lineage; + pattern.patternLineageExists = pattern.lineage.length > 0; + pattern.patternLineagesR = pattern.lineageR; + pattern.patternLineageRExists = pattern.lineageR.length > 0; + pattern.patternLineageEExists = pattern.patternLineageExists || pattern.patternLineageRExists; //render the pattern, but first consolidate any data we may have var allData; try { allData = JSON5.parse(JSON5.stringify(patternlab.data)); } catch (err) { - console.log('There was an error parsing JSON for ' + pattern.abspath); + console.log('There was an error parsing JSON for ' + pattern.relPath); console.log(err); } - allData = pattern_assembler.merge_data(allData, pattern.jsonFileData); - - //also add the cachebuster value. slight chance this could collide with a user that has defined cacheBuster as a value - allData.cacheBuster = patternlab.cacheBuster; - pattern.cacheBuster = patternlab.cacheBuster; + allData = plutils.mergeData(allData, pattern.jsonFileData); - //render the pattern-specific header - var headHtml = pattern_assembler.renderPattern(pattern.header, allData); + //var headHTML = pattern_assembler.renderPattern(patternlab.userHead, allData); + var headHTML = pattern_assembler.renderPattern(pattern.header, allData); //render the extendedTemplate with all data - pattern.patternPartial = pattern_assembler.renderPattern(pattern.extendedTemplate, allData); - - //set the pattern-specific footer if necessary - if (patternlab.userFoot) { - var userFooter = patternlab.userFoot.extendedTemplate.replace('{% pattern-lab-foot %}', patternlab.footerPattern + patternlab.footer); - pattern.footer = pattern_assembler.renderPattern(userFooter, pattern); - } else { - pattern.footer = pattern_assembler.renderPattern(patternlab.footerPattern, pattern); - } + pattern.patternPartialCode = pattern_assembler.renderPattern(pattern, allData); + + //todo see if this is still needed + pattern.patternPartialCodeE = entity_encoder.encode(pattern.patternPartialCode); + + // stringify this data for individual pattern rendering and use on the styleguide + // see if patternData really needs these other duped values + pattern.patternData = JSON.stringify({ + cssEnabled: false, + patternLineageExists: pattern.patternLineageExists, + patternLineages: pattern.patternLineages, + lineage: pattern.patternLineages, + patternLineageRExists: pattern.patternLineageRExists, + patternLineagesR: pattern.patternLineagesR, + lineageR: pattern.patternLineagesR, + patternLineageEExists: pattern.patternLineageExists || pattern.patternLineageRExists, + patternDesc: pattern.patternDescExists ? pattern.patternDesc : '', + patternBreadcrumb: + pattern.patternGroup === pattern.patternSubGroup ? + { + patternType: pattern.patternGroup + } : { + patternType: pattern.patternGroup, + patternSubtype: pattern.patternSubGroup + }, + patternExtension: pattern.fileExtension, + patternName: pattern.patternName, + patternPartial: pattern.patternPartial, + patternState: pattern.patternState, + extraOutput: {} + }); + + //set the pattern-specific footer by compiling the general-footer with data, and then adding it to the meta footer + var footerPartial = pattern_assembler.renderPattern(patternlab.footer, { + isPattern: true, + patternData: pattern.patternData, + cacheBuster: patternlab.cacheBuster + }); + + var footerHTML = pattern_assembler.renderPattern(patternlab.userFoot, { + patternLabFoot : footerPartial + }); //write the compiled template to the public patterns directory - fs.outputFileSync(paths.public.patterns + pattern.patternLink, headHtml + pattern.patternPartial + pattern.footer); + var patternPage = headHTML + pattern.patternPartialCode + footerHTML; + fs.outputFileSync(paths.public.patterns + pattern.patternLink, patternPage); //write the mustache file too - fs.outputFileSync(paths.public.patterns + pattern.patternLink.replace('.html', '.mustache'), entity_encoder.encode(pattern.template)); + fs.outputFileSync(paths.public.patterns + pattern.patternLink.replace('.html', pattern.fileExtension), pattern.template); //write the encoded version too - fs.outputFileSync(paths.public.patterns + pattern.patternLink.replace('.html', '.escaped.html'), entity_encoder.encode(pattern.patternPartial)); + fs.outputFileSync(paths.public.patterns + pattern.patternLink.replace('.html', '.markup-only.html'), pattern.patternPartialCode); }); //export patterns if necessary pattern_exporter.export_patterns(patternlab); } - function addToPatternPaths(bucketName, pattern) { - //this is messy, could use a refactor. - patternlab.patternPaths[bucketName][pattern.patternName] = pattern.subdir.replace(/\\/g, '/') + "/" + pattern.fileName; - } - - //todo: refactor this as a method on the pattern object itself once we merge dev with pattern-engines branch - function isPatternExcluded(pattern) { - // returns whether or not the first character of the pattern filename is an underscore, or excluded - return pattern.fileName.charAt(0) === '_'; - } - - function buildFrontEnd() { - var pattern_assembler = new pa(), - media_hunter = new mh(), - styleGuideExcludes = patternlab.config.styleGuideExcludes, - styleguidePatterns = [], - i; // for loops - - patternlab.buckets = []; - patternlab.bucketIndex = []; - patternlab.patternPaths = {}; - patternlab.viewAllPaths = {}; - - //sort all patterns explicitly. - patternlab.patterns = patternlab.patterns.sort(function (a, b) { - if (a.name > b.name) { return 1; } - if (a.name < b.name) { return -1; } - - // a must be equal to b - return 0; - }); - - //find mediaQueries - media_hunter.find_media_queries('./source/css', patternlab); - - // check if patterns are excluded, if not add them to styleguidePatterns - if (styleGuideExcludes && styleGuideExcludes.length) { - for (i = 0; i < patternlab.patterns.length; i++) { - - // skip underscore-prefixed files - if (isPatternExcluded(patternlab.patterns[i])) { - if (patternlab.config.debug) { - console.log('Omitting ' + patternlab.patterns[i].key + " from styleguide pattern exclusion."); - } - continue; - } - - var key = patternlab.patterns[i].key; - var typeKey = key.substring(0, key.indexOf('-')); - var isExcluded = (styleGuideExcludes.indexOf(typeKey) > -1); - if (!isExcluded) { - styleguidePatterns.push(patternlab.patterns[i]); - } - } - } else { - for (i = 0; i < patternlab.patterns.length; i++) { - // skip underscore-prefixed files - if (isPatternExcluded(patternlab.patterns[i])) { - if (patternlab.config.debug) { - console.log('Omitting ' + patternlab.patterns[i].key + " from styleguide pattern exclusion."); - } - continue; - } - styleguidePatterns.push(patternlab.patterns[i]); - } - } - - //also add the cachebuster value. slight chance this could collide with a user that has defined cacheBuster as a value - patternlab.data.cacheBuster = patternlab.cacheBuster; - - //get the main page head and foot - var mainPageHead = patternlab.userHead.extendedTemplate.replace('{% pattern-lab-head %}', patternlab.header); - var mainPageHeadHtml = pattern_assembler.renderPattern(mainPageHead, patternlab.data); - var mainPageFoot = patternlab.userFoot.extendedTemplate.replace('{% pattern-lab-foot %}', patternlab.footer); - var mainPageFootHtml = pattern_assembler.renderPattern(mainPageFoot, patternlab.data); - - //build the styleguide - var styleguideTemplate = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/styleguide.mustache'), 'utf8'), - styleguideHtml = pattern_assembler.renderPattern(styleguideTemplate, {partials: styleguidePatterns, cacheBuster: patternlab.cacheBuster}); - - fs.outputFileSync(path.resolve(paths.public.styleguide, 'html/styleguide.html'), mainPageHeadHtml + styleguideHtml + mainPageFootHtml); - - //build the viewall pages - var prevSubdir = '', - prevGroup = ''; - - for (i = 0; i < patternlab.patterns.length; i++) { - // skip underscore-prefixed files - if (isPatternExcluded(patternlab.patterns[i])) { - if (patternlab.config.debug) { - console.log('Omitting ' + patternlab.patterns[i].key + " from view all rendering."); - } - continue; - } - - var pattern = patternlab.patterns[i]; - - //create the view all for the section - // check if the current section is different from the previous one - if (pattern.patternGroup !== prevGroup) { - prevGroup = pattern.patternGroup; - - var viewAllPatterns = [], - patternPartial = "viewall-" + pattern.patternGroup, - j; - - for (j = 0; j < patternlab.patterns.length; j++) { - if (patternlab.patterns[j].patternGroup === pattern.patternGroup) { - //again, skip any sibling patterns to the current one that may have underscores - if (isPatternExcluded(patternlab.patterns[j])) { - if (patternlab.config.debug) { - console.log('Omitting ' + patternlab.patterns[j].key + " from view all sibling rendering."); - } - continue; - } - - viewAllPatterns.push(patternlab.patterns[j]); - } - } - - var viewAllTemplate = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/viewall.mustache'), 'utf8'); - var viewAllHtml = pattern_assembler.renderPattern(viewAllTemplate, {partials: viewAllPatterns, patternPartial: patternPartial, cacheBuster: patternlab.cacheBuster }); - fs.outputFileSync(paths.public.patterns + pattern.subdir.slice(0, pattern.subdir.indexOf(pattern.patternGroup) + pattern.patternGroup.length) + '/index.html', mainPageHead + viewAllHtml + mainPageFoot); - } - - // create the view all for the subsection - // check if the current sub section is different from the previous one - if (pattern.subdir !== prevSubdir) { - prevSubdir = pattern.subdir; - - var viewAllPatterns = [], - patternPartial = "viewall-" + pattern.patternGroup + "-" + pattern.patternSubGroup, - j; - - for (j = 0; j < patternlab.patterns.length; j++) { - if (patternlab.patterns[j].subdir === pattern.subdir) { - //again, skip any sibling patterns to the current one that may have underscores - if (isPatternExcluded(patternlab.patterns[j])) { - if (patternlab.config.debug) { - console.log('Omitting ' + patternlab.patterns[j].key + " from view all sibling rendering."); - } - continue; - } - viewAllPatterns.push(patternlab.patterns[j]); - } - } - - var viewAllTemplate = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/viewall.mustache'), 'utf8'); - var viewAllHtml = pattern_assembler.renderPattern(viewAllTemplate, {partials: viewAllPatterns, patternPartial: patternPartial, cacheBuster: patternlab.cacheBuster}); - fs.outputFileSync(paths.public.patterns + pattern.flatPatternPath + '/index.html', mainPageHeadHtml + viewAllHtml + mainPageFootHtml); - } - } - - //build the patternlab website - var patternlabSiteTemplate = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/index.mustache'), 'utf8'); - - //loop through all patterns.to build the navigation - //todo: refactor this someday - for (i = 0; i < patternlab.patterns.length; i++) { - - var pattern = patternlab.patterns[i]; - var bucketName = pattern.name.replace(/\\/g, '-').split('-')[1]; - - //check if the bucket already exists - var bucketIndex = patternlab.bucketIndex.indexOf(bucketName); - if (bucketIndex === -1) { - - // skip underscore-prefixed files. don't create a bucket on account of an underscored pattern - if (isPatternExcluded(pattern)) { - continue; - } - - //add the bucket - var bucket = new of.oBucket(bucketName); - - //add patternPath and viewAllPath - patternlab.patternPaths[bucketName] = {}; - patternlab.viewAllPaths[bucketName] = {}; - - //get the navItem - var navItemName = pattern.subdir.split('/').pop(); - navItemName = navItemName.replace(/(\d).(-)/g, ''); - - //get the navSubItem - var navSubItemName = pattern.patternName.replace(/-/g, ' '); - - //test whether the pattern struture is flat or not - usually due to a template or page - var flatPatternItem = false; - if (navItemName === bucketName) { - flatPatternItem = true; - } - - //assume the navItem does not exist. - var navItem = new of.oNavItem(navItemName); - - //assume the navSubItem does not exist. - var navSubItem = new of.oNavSubItem(navSubItemName); - navSubItem.patternPath = pattern.patternLink; - navSubItem.patternPartial = bucketName + "-" + pattern.patternName; //add the hyphenated name - - //add the patternState if it exists - if (pattern.patternState) { - navSubItem.patternState = pattern.patternState; - } - - //if it is flat - we should not add the pattern to patternPaths - if (flatPatternItem) { - bucket.patternItems.push(navSubItem); - - //add to patternPaths - addToPatternPaths(bucketName, pattern); - - } else { - bucket.navItems.push(navItem); - bucket.navItemsIndex.push(navItemName); - navItem.navSubItems.push(navSubItem); - navItem.navSubItemsIndex.push(navSubItemName); - - //add to patternPaths - addToPatternPaths(bucketName, pattern); - - //add the navViewAllItem - var navViewAllItem = new of.oNavSubItem("View All"); - navViewAllItem.patternPath = pattern.subdir.slice(0, pattern.subdir.indexOf(pattern.patternGroup) + pattern.patternGroup.length) + "/index.html"; - navViewAllItem.patternPartial = "viewall-" + pattern.patternGroup; - - bucket.patternItems.push(navViewAllItem); - patternlab.viewAllPaths[bucketName].viewall = pattern.subdir.slice(0, pattern.subdir.indexOf(pattern.patternGroup) + pattern.patternGroup.length); - } - - //add the bucket. - patternlab.buckets.push(bucket); - patternlab.bucketIndex.push(bucketName); - - //done - - } else { - //find the bucket - var bucket = patternlab.buckets[bucketIndex]; - - //get the navItem - //if there is one or more slashes in the subdir, get everything after - //the last slash. if no slash, get the whole subdir string and strip - //any numeric + hyphen prefix - var navItemName = pattern.subdir.split('/').pop().replace(/^\d*\-/, ''); - - //get the navSubItem - var navSubItemName = pattern.patternName.replace(/-/g, ' '); - - //assume the navSubItem does not exist. - var navSubItem = new of.oNavSubItem(navSubItemName); - navSubItem.patternPath = pattern.patternLink; - navSubItem.patternPartial = bucketName + "-" + pattern.patternName; //add the hyphenated name - - //add the patternState if it exists - if (pattern.patternState) { - navSubItem.patternState = pattern.patternState; - } - - //test whether the pattern struture is flat or not - usually due to a template or page - var flatPatternItem = false; - if (navItemName === bucketName) { - flatPatternItem = true; - } - - //if it is flat - we should not add the pattern to patternPaths - if (flatPatternItem) { - - // skip underscore-prefixed files - if (isPatternExcluded(pattern)) { - continue; - } - - //add the navItem to patternItems - bucket.patternItems.push(navSubItem); - - //add to patternPaths - addToPatternPaths(bucketName, pattern); - - } else { - - // only do this if pattern is included - if (!isPatternExcluded(pattern)) { - //check to see if navItem exists - var navItemIndex = bucket.navItemsIndex.indexOf(navItemName); - if (navItemIndex === -1) { - var navItem = new of.oNavItem(navItemName); - - //add the navItem and navSubItem - navItem.navSubItems.push(navSubItem); - navItem.navSubItemsIndex.push(navSubItemName); - bucket.navItems.push(navItem); - bucket.navItemsIndex.push(navItemName); - - } else { - //add the navSubItem - var navItem = bucket.navItems[navItemIndex]; - navItem.navSubItems.push(navSubItem); - navItem.navSubItemsIndex.push(navSubItemName); - } - } - - //check if we are moving to a new sub section in the next loop - if (!patternlab.patterns[i + 1] || pattern.patternSubGroup !== patternlab.patterns[i + 1].patternSubGroup) { - - //add the navViewAllSubItem - var navViewAllSubItem = new of.oNavSubItem(""); - navViewAllSubItem.patternName = "View All"; - navViewAllSubItem.patternPath = pattern.flatPatternPath + "/index.html"; - navViewAllSubItem.patternPartial = "viewall-" + pattern.patternGroup + "-" + pattern.patternSubGroup; - - navItem.navSubItems.push(navViewAllSubItem); - navItem.navSubItemsIndex.push("View All"); - } - - // just add to patternPaths - addToPatternPaths(bucketName, pattern); - } - - } - - patternlab.viewAllPaths[bucketName][pattern.patternSubGroup] = pattern.flatPatternPath; - - } - - //the patternlab site requires a lot of partials to be rendered. - //patternNav - var patternNavTemplate = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/partials/patternNav.mustache'), 'utf8'); - var patternNavPartialHtml = pattern_assembler.renderPattern(patternNavTemplate, patternlab); - - //ishControls - var ishControlsTemplate = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/partials/ishControls.mustache'), 'utf8'); - patternlab.config.mqs = patternlab.mediaQueries; - var ishControlsPartialHtml = pattern_assembler.renderPattern(ishControlsTemplate, patternlab.config); - - //patternPaths - var patternPathsTemplate = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/partials/patternPaths.mustache'), 'utf8'); - var patternPathsPartialHtml = pattern_assembler.renderPattern(patternPathsTemplate, {'patternPaths': JSON5.stringify(patternlab.patternPaths)}); - - //viewAllPaths - var viewAllPathsTemplate = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'templates/partials/viewAllPaths.mustache'), 'utf8'); - var viewAllPathsPartialHtml = pattern_assembler.renderPattern(viewAllPathsTemplate, {'viewallpaths': JSON5.stringify(patternlab.viewAllPaths)}); - - //render the patternlab template, with all partials - var patternlabSiteHtml = pattern_assembler.renderPattern(patternlabSiteTemplate, { - defaultPattern: patternlab.config.defaultPattern || 'all', - cacheBuster: patternlab.cacheBuster - }, { - 'ishControls': ishControlsPartialHtml, - 'patternNav': patternNavPartialHtml, - 'patternPaths': patternPathsPartialHtml, - 'viewAllPaths': viewAllPathsPartialHtml - }); - fs.outputFileSync(path.resolve(paths.public.root, 'index.html'), patternlabSiteHtml); - } - return { version: function () { return getVersion(); }, - build: function (deletePatternDir) { + build: function (callback, deletePatternDir) { buildPatterns(deletePatternDir); - buildFrontEnd(); + buildFrontEnd(patternlab); printDebug(); + callback(); }, help: function () { help(); }, - build_patterns_only: function (deletePatternDir) { + patternsonly: function (callback, deletePatternDir) { buildPatterns(deletePatternDir); printDebug(); + callback(); + }, + liststarterkits: function () { + return listStarterkits(); + }, + loadstarterkit: function (starterkitName, clean) { + loadStarterKit(starterkitName, clean); } }; - }; +// export these free functions so they're available without calling the exported +// function, for use in reducing code dupe in unit tests. At least, until we +// have a better way to do this +patternlab_engine.process_all_patterns_iterative = processAllPatternsIterative; +patternlab_engine.process_all_patterns_recursive = processAllPatternsRecursive; + module.exports = patternlab_engine; diff --git a/core/lib/patternlab_grunt.js b/core/lib/patternlab_grunt.js deleted file mode 100644 index 31967435a..000000000 --- a/core/lib/patternlab_grunt.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - -var patternlab_engine = require('./patternlab.js'); - -module.exports = function (grunt) { - grunt.registerTask('patternlab', 'create design systems with atomic design', function (arg) { - var patternlab = patternlab_engine(); - - if (arguments.length === 0) { - patternlab.build(true); - } - - if (arg && arg === 'v') { - patternlab.version(); - } - - if (arg && arg === "only_patterns") { - patternlab.build_patterns_only(true); - } - - if (arg && arg === "help") { - patternlab.help(); - } - - if (arg && (arg !== "v" && arg !== "only_patterns" && arg !== "help")) { - patternlab.help(); - } - }); - -}; diff --git a/core/lib/patternlab_gulp.js b/core/lib/patternlab_gulp.js deleted file mode 100644 index 75340380f..000000000 --- a/core/lib/patternlab_gulp.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - -var patternlab_engine = require('./patternlab.js'); - -module.exports = function (gulp) { - - gulp.task('patternlab', ['clean'], function (cb) { - var patternlab = patternlab_engine(); - patternlab.build(false); - cb(); - }); - - gulp.task('patternlab:version', function () { - var patternlab = patternlab_engine(); - patternlab.version(); - }); - - gulp.task('patternlab:only_patterns', ['clean'], function () { - var patternlab = patternlab_engine(); - patternlab.build_patterns_only(false); - }); - - gulp.task('patternlab:help', function () { - var patternlab = patternlab_engine(); - patternlab.help(); - }); -}; diff --git a/core/lib/pseudopattern_hunter.js b/core/lib/pseudopattern_hunter.js index d4d1cafe8..47e4b7e57 100644 --- a/core/lib/pseudopattern_hunter.js +++ b/core/lib/pseudopattern_hunter.js @@ -1,13 +1,3 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - "use strict"; var pseudopattern_hunter = function () { @@ -17,14 +7,17 @@ var pseudopattern_hunter = function () { fs = require('fs-extra'), pa = require('./pattern_assembler'), lh = require('./lineage_hunter'), - of = require('./object_factory'), + Pattern = require('./object_factory').Pattern, + plutils = require('./utilities'), path = require('path'); + var pattern_assembler = new pa(); var lineage_hunter = new lh(); var paths = patternlab.config.paths; - //look for a pseudo pattern by checking if there is a file containing same name, with ~ in it, ending in .json + //look for a pseudo pattern by checking if there is a file containing same + //name, with ~ in it, ending in .json var needle = currentPattern.subdir + '/' + currentPattern.fileName + '~*.json'; var pseudoPatterns = glob.sync(needle, { cwd: paths.source.patterns, @@ -35,27 +28,37 @@ var pseudopattern_hunter = function () { if (pseudoPatterns.length > 0) { for (var i = 0; i < pseudoPatterns.length; i++) { if (patternlab.config.debug) { - console.log('found pseudoPattern variant of ' + currentPattern.key); + console.log('found pseudoPattern variant of ' + currentPattern.patternPartial); } //we want to do everything we normally would here, except instead read the pseudoPattern data - var variantFileData = fs.readJSONSync(path.resolve(paths.source.patterns, pseudoPatterns[i])); - + try { + var variantFileData = fs.readJSONSync(path.resolve(paths.source.patterns, pseudoPatterns[i])); + } catch (err) { + console.log('There was an error parsing pseudopattern JSON for ' + currentPattern.relPath); + console.log(err); + } + //extend any existing data with variant data - variantFileData = pattern_assembler.merge_data(currentPattern.jsonFileData, variantFileData); + variantFileData = plutils.mergeData(currentPattern.jsonFileData, variantFileData); var variantName = pseudoPatterns[i].substring(pseudoPatterns[i].indexOf('~') + 1).split('.')[0]; - var variantFilePath = path.resolve(paths.source.patterns, currentPattern.subdir, currentPattern.fileName + '~' + variantName + '.json'); - var variantFileName = currentPattern.fileName + '-' + variantName + '.'; - var patternVariant = new of.oPattern(variantFilePath, currentPattern.subdir, variantFileName, variantFileData); + var variantFilePath = path.join(currentPattern.subdir, currentPattern.fileName + '~' + variantName + '.json'); + var patternVariant = Pattern.create(variantFilePath, variantFileData, { + //use the same template as the non-variant + template: currentPattern.template, + fileExtension: currentPattern.fileExtension, + extendedTemplate: currentPattern.extendedTemplate, + isPseudoPattern: true, + basePattern: currentPattern, + + // use the same template engine as the non-variant + engine: currentPattern.engine + }); //see if this file has a state pattern_assembler.setPatternState(patternVariant, patternlab); - //use the same template as the non-variant - patternVariant.template = currentPattern.template; - patternVariant.extendedTemplate = currentPattern.extendedTemplate; - //find pattern lineage lineage_hunter.find_lineage(patternVariant, patternlab); diff --git a/core/lib/starterkit_manager.js b/core/lib/starterkit_manager.js new file mode 100644 index 000000000..e14503d45 --- /dev/null +++ b/core/lib/starterkit_manager.js @@ -0,0 +1,66 @@ +"use strict"; + +var starterkit_manager = function (pl) { + var path = require('path'), + fs = require('fs-extra'), + util = require('./utilities'), + paths = pl.config.paths; + + function loadStarterKit(starterkitName, clean) { + try { + var kitPath = path.resolve( + path.join(process.cwd(), 'node_modules', starterkitName, pl.config.starterkitSubDir) + ); + console.log('Attempting to load starterkit from', kitPath); + try { + var kitDirStats = fs.statSync(kitPath); + } catch (ex) { + util.logRed(starterkitName + ' not found, please use npm to install it first.'); + util.logRed(starterkitName + ' not loaded.'); + return; + } + var kitPathDirExists = kitDirStats.isDirectory(); + if (kitPathDirExists) { + + if (clean) { + console.log('Deleting contents of', paths.source.root, 'prior to starterkit load.'); + util.emptyDirectory(paths.source.root); + } else { + console.log('Overwriting contents of', paths.source.root, 'during starterkit load.'); + } + + fs.copy(kitPath, paths.source.root, function (ex) { + if (ex) { + console.error(ex); + } + util.logGreen('starterkit ' + starterkitName + ' loaded successfully.'); + }); + } + } catch (ex) { + console.log(ex); + } + } + + function listStarterkits() { + console.log('https://github.com/search?utf8=%E2%9C%93&q=starterkit+in%3Aname%2C+user%3Apattern-lab&type=Repositories&ref=searchresults'); + } + + function packStarterkit() { + + } + + return { + load_starterkit: function (starterkitName, clean) { + loadStarterKit(starterkitName, clean); + }, + list_starterkits: function () { + listStarterkits(); + }, + pack_starterkit: function () { + packStarterkit(); + } + }; + +}; + +module.exports = starterkit_manager; diff --git a/core/lib/style_modifier_hunter.js b/core/lib/style_modifier_hunter.js index fb18063c9..8c0b7413f 100644 --- a/core/lib/style_modifier_hunter.js +++ b/core/lib/style_modifier_hunter.js @@ -1,13 +1,3 @@ -/* - * patternlab-node - v1.3.0 - 2016 - * - * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - "use strict"; var style_modifier_hunter = function () { @@ -21,14 +11,14 @@ var style_modifier_hunter = function () { styleModifier = styleModifier.replace(/\|/g, ' '); if (patternlab.config.debug) { - console.log('found partial styleModifier within pattern ' + pattern.key); + console.log('found partial styleModifier within pattern ' + pattern.patternPartial); } //replace the stylemodifier placeholder with the class name pattern.extendedTemplate = pattern.extendedTemplate.replace(/{{[ ]?styleModifier[ ]?}}/i, styleModifier); //update the extendedTemplate in the partials object in case this pattern is consumed later - patternlab.partials[pattern.key] = pattern.extendedTemplate; + patternlab.partials[pattern.patternPartial] = pattern.extendedTemplate; } } diff --git a/core/lib/ui_builder.js b/core/lib/ui_builder.js new file mode 100644 index 000000000..f14e160a3 --- /dev/null +++ b/core/lib/ui_builder.js @@ -0,0 +1,508 @@ +"use strict"; + +var path = require('path'); +var fs = require('fs-extra'); +var of = require('./object_factory'); +var pa = require('./pattern_assembler'); +var pattern_assembler = new pa(); +var eol = require('os').EOL; + +// PRIVATE FUNCTIONS + +function addToPatternPaths(patternlab, patternTypeName, pattern) { + //this is messy, could use a refactor. + if (!patternlab.patternPaths[patternTypeName]) { + patternlab.patternPaths[patternTypeName] = {}; + } + patternlab.patternPaths[patternTypeName][pattern.patternBaseName] = pattern.subdir.replace(/\\/g, '/') + "/" + pattern.fileName.replace('~', '-'); +} + +//todo: refactor this as a method on the pattern object itself once we merge dev with pattern-engines branch +function isPatternExcluded(pattern) { + // returns whether or not the first character of the pattern filename is an underscore, or excluded + return pattern.fileName.charAt(0) === '_'; +} + +// Returns the array of patterns to be rendered in the styleguide view and +// linked to in the pattern navigation. Checks if patterns are excluded. +function assembleStyleguidePatterns(patternlab) { + var styleguideExcludes = patternlab.config.styleGuideExcludes; + var styleguidePatterns = []; + + if (styleguideExcludes && styleguideExcludes.length) { + for (var i = 0; i < patternlab.patterns.length; i++) { + + var pattern = patternlab.patterns[i]; + + // skip underscore-prefixed files + if (isPatternExcluded(pattern)) { + if (patternlab.config.debug) { + console.log('Omitting ' + pattern.patternPartial + " from styleguide pattern exclusion."); + } + continue; + } + + //this is meant to be a homepage that is not present anywhere else + if (pattern.patternPartial === patternlab.config.defaultPattern) { + if (patternlab.config.debug) { + console.log('omitting ' + pattern.patternPartial + ' from styleguide patterns because it is defined as a defaultPattern'); + } + continue; + } + + var partial = pattern.patternPartial; + var partialType = partial.substring(0, partial.indexOf('-')); + var isExcluded = (styleguideExcludes.indexOf(partialType) > -1); + if (!isExcluded) { + styleguidePatterns.push(pattern); + } + } + } else { + for (i = 0; i < patternlab.patterns.length; i++) { + var pattern = patternlab.patterns[i]; + + // skip underscore-prefixed files + if (isPatternExcluded(pattern)) { + if (patternlab.config.debug) { + console.log('Omitting ' + pattern.patternPartial + " from styleguide pattern exclusion."); + } + continue; + } + + //this is meant to be a homepage that is not present anywhere else + if (pattern.patternPartial === patternlab.config.defaultPattern) { + if (patternlab.config.debug) { + console.log('omitting ' + pattern.patternPartial + ' from styleguide patterns because it is defined as a defaultPattern'); + } + continue; + } + + styleguidePatterns.push(pattern); + } + } + + return styleguidePatterns; +} + +function buildNavigation(patternlab) { + for (var i = 0; i < patternlab.patterns.length; i++) { + + var pattern = patternlab.patterns[i]; + + //todo: check if this is already available + var patternTypeName = pattern.name.replace(/\\/g, '-').split('-')[1]; + + //exclude any named defaultPattern from the navigation. + //this is meant to be a homepage that is not navigable + if (pattern.patternPartial === patternlab.config.defaultPattern) { + if (patternlab.config.debug) { + console.log('omitting ' + pattern.patternPartial + ' from navigation because it is defined as a defaultPattern'); + } + + //add to patternPaths before continuing + addToPatternPaths(patternlab, patternTypeName, pattern); + + continue; + } + + // skip underscore-prefixed files. don't create a patternType on account of an underscored pattern + if (isPatternExcluded(pattern)) { + continue; + } + + var patternSubTypeName; + var patternSubTypeItemName; + var flatPatternItem; + var patternSubType; + var patternSubTypeItem; + var viewAllPatternSubTypeItem; + + //get the patternSubType. + //if there is one or more slashes in the subdir, get everything after + //the last slash. if no slash, get the whole subdir string and strip + //any numeric + hyphen prefix + patternSubTypeName = pattern.subdir.split(path.sep).pop().replace(/^\d*\-/, ''); + + //get the patternSubTypeItem + patternSubTypeItemName = pattern.patternName.replace(/-/g, ' '); + + //assume the patternSubTypeItem does not exist. + patternSubTypeItem = new of.oPatternSubTypeItem(patternSubTypeItemName); + patternSubTypeItem.patternPath = pattern.patternLink; + patternSubTypeItem.patternPartial = pattern.patternPartial; + + //check if the patternType already exists + var patternTypeIndex = patternlab.patternTypeIndex.indexOf(patternTypeName); + if (patternTypeIndex === -1) { + //add the patternType + var patternType = new of.oPatternType(patternTypeName); + + //add patternPath and viewAllPath + patternlab.patternPaths[patternTypeName] = patternlab.patternPaths[patternTypeName] || {}; + patternlab.viewAllPaths[patternTypeName] = {}; + + //test whether the pattern structure is flat or not - usually due to a template or page + flatPatternItem = patternSubTypeName === patternTypeName; + + //assume the patternSubType does not exist. + patternSubType = new of.oPatternSubType(patternSubTypeName); + + //add the patternState if it exists + if (pattern.patternState) { + patternSubTypeItem.patternState = pattern.patternState; + } + + //if it is flat - we should not add the pattern to patternPaths + if (flatPatternItem) { + + patternType.patternItems.push(patternSubTypeItem); + + //add to patternPaths + addToPatternPaths(patternlab, patternTypeName, pattern); + + } else { + + patternType.patternTypeItems.push(patternSubType); + patternType.patternTypeItemsIndex.push(patternSubTypeName); + patternSubType.patternSubtypeItems.push(patternSubTypeItem); + patternSubType.patternSubtypeItemsIndex.push(patternSubTypeItemName); + + //add to patternPaths + addToPatternPaths(patternlab, patternTypeName, pattern); + + //add the view all PatternSubTypeItem + viewAllPatternSubTypeItem = new of.oPatternSubTypeItem("View All"); + viewAllPatternSubTypeItem.patternPath = pattern.subdir.slice(0, pattern.subdir.indexOf(pattern.patternGroup) + pattern.patternGroup.length) + "/index.html"; + viewAllPatternSubTypeItem.patternPartial = "viewall-" + pattern.patternGroup; + + patternType.patternItems.push(viewAllPatternSubTypeItem); + patternlab.viewAllPaths[patternTypeName].viewall = pattern.subdir.slice(0, pattern.subdir.indexOf(pattern.patternGroup) + pattern.patternGroup.length); + + } + + //add the patternType. + patternlab.patternTypes.push(patternType); + patternlab.patternTypeIndex.push(patternTypeName); + + //done + + } else { + //find the patternType + patternType = patternlab.patternTypes[patternTypeIndex]; + + //add the patternState if it exists + if (pattern.patternState) { + patternSubTypeItem.patternState = pattern.patternState; + } + + //test whether the pattern structure is flat or not - usually due to a template or page + flatPatternItem = patternSubTypeName === patternTypeName; + + //if it is flat - we should not add the pattern to patternPaths + if (flatPatternItem) { + //add the patternSubType to patternItems + patternType.patternItems.push(patternSubTypeItem); + + //add to patternPaths + addToPatternPaths(patternlab, patternTypeName, pattern); + + } else { + + // only do this if pattern is included + //check to see if patternSubType exists + var patternTypeItemsIndex = patternType.patternTypeItemsIndex.indexOf(patternSubTypeName); + if (patternTypeItemsIndex === -1) { + patternSubType = new of.oPatternSubType(patternSubTypeName); + + //add the patternSubType and patternSubTypeItem + patternSubType.patternSubtypeItems.push(patternSubTypeItem); + patternSubType.patternSubtypeItemsIndex.push(patternSubTypeItemName); + patternType.patternTypeItems.push(patternSubType); + patternType.patternTypeItemsIndex.push(patternSubTypeName); + + } else { + //add the patternSubTypeItem + patternSubType = patternType.patternTypeItems[patternTypeItemsIndex]; + patternSubType.patternSubtypeItems.push(patternSubTypeItem); + patternSubType.patternSubtypeItemsIndex.push(patternSubTypeItemName); + } + + //check if we are moving to a new subgroup in the next loop + if (!patternlab.patterns[i + 1] || pattern.patternSubGroup !== patternlab.patterns[i + 1].patternSubGroup) { + + //add the viewall SubTypeItem + var viewAllPatternSubTypeItem = new of.oPatternSubTypeItem("View All"); + viewAllPatternSubTypeItem.patternPath = pattern.flatPatternPath + "/index.html"; + viewAllPatternSubTypeItem.patternPartial = "viewall-" + pattern.patternGroup + "-" + pattern.patternSubGroup; + + patternSubType.patternSubtypeItems.push(viewAllPatternSubTypeItem); + patternSubType.patternSubtypeItemsIndex.push("View All"); + } + + // just add to patternPaths + addToPatternPaths(patternlab, patternTypeName, pattern); + } + } + + patternlab.viewAllPaths[patternTypeName][pattern.patternSubGroup] = pattern.flatPatternPath; + } + return patternTypeIndex; +} + +function buildFooterHTML(patternlab, patternPartial) { + //set the pattern-specific footer by compiling the general-footer with data, and then adding it to the meta footer + var footerPartial = pattern_assembler.renderPattern(patternlab.footer, { + patternData: JSON.stringify({ + patternPartial: patternPartial, + }), + cacheBuster: patternlab.cacheBuster + }); + var footerHTML = pattern_assembler.renderPattern(patternlab.userFoot, { + patternLabFoot : footerPartial + }); + return footerHTML; +} + +function buildViewAllHTML(patternlab, patterns, patternPartial) { + var viewAllHTML = pattern_assembler.renderPattern(patternlab.viewAll, + { + partials: patterns, + patternPartial: patternPartial, + cacheBuster: patternlab.cacheBuster + }, { + patternSection: patternlab.patternSection, + patternSectionSubType: patternlab.patternSectionSubType + }); + return viewAllHTML; +} + +function buildViewAllPages(mainPageHeadHtml, patternlab) { + var paths = patternlab.config.paths; + var prevSubdir = ''; + var prevGroup = ''; + var i; + + for (i = 0; i < patternlab.patterns.length; i++) { + + var pattern = patternlab.patterns[i]; + + // skip underscore-prefixed files + if (isPatternExcluded(pattern)) { + if (patternlab.config.debug) { + console.log('Omitting ' + pattern.patternPartial + " from view all rendering."); + } + continue; + } + + //this is meant to be a homepage that is not present anywhere else + if (pattern.patternPartial === patternlab.config.defaultPattern) { + if (patternlab.config.debug) { + console.log('Omitting ' + pattern.patternPartial + ' from view all rendering because it is defined as a defaultPattern'); + } + continue; + } + + //create the view all for the section + // check if the current section is different from the previous one + if (pattern.patternGroup !== prevGroup) { + prevGroup = pattern.patternGroup; + + var viewAllPatterns = []; + var patternPartial = "viewall-" + pattern.patternGroup; + var j; + + for (j = 0; j < patternlab.patterns.length; j++) { + if (patternlab.patterns[j].patternGroup === pattern.patternGroup) { + //again, skip any sibling patterns to the current one that may have underscores + if (isPatternExcluded(patternlab.patterns[j])) { + if (patternlab.config.debug) { + console.log('Omitting ' + patternlab.patterns[j].patternPartial + " from view all sibling rendering."); + } + continue; + } + + //this is meant to be a homepage that is not present anywhere else + if (patternlab.patterns[j].patternPartial === patternlab.config.defaultPattern) { + if (patternlab.config.debug) { + console.log('Omitting ' + pattern.patternPartial + ' from view all sibling rendering because it is defined as a defaultPattern'); + } + continue; + } + + viewAllPatterns.push(patternlab.patterns[j]); + } + } + + //render the footer needed for the viewall template + var footerHTML = buildFooterHTML(patternlab, patternPartial); + + //render the viewall template + var viewAllHTML = buildViewAllHTML(patternlab, viewAllPatterns, patternPartial); + + fs.outputFileSync(paths.public.patterns + pattern.subdir.slice(0, pattern.subdir.indexOf(pattern.patternGroup) + pattern.patternGroup.length) + '/index.html', mainPageHeadHtml + viewAllHTML + footerHTML); + } + + //create the view all for the subsection + // check if the current sub section is different from the previous one + if (pattern.subdir !== prevSubdir) { + prevSubdir = pattern.subdir; + + viewAllPatterns = []; + patternPartial = "viewall-" + pattern.patternGroup + "-" + pattern.patternSubGroup; + + for (j = 0; j < patternlab.patterns.length; j++) { + if (patternlab.patterns[j].subdir === pattern.subdir) { + //again, skip any sibling patterns to the current one that may have underscores + if (isPatternExcluded(patternlab.patterns[j])) { + if (patternlab.config.debug) { + console.log('Omitting ' + patternlab.patterns[j].patternPartial + " from view all sibling rendering."); + } + continue; + } + + //this is meant to be a homepage that is not present anywhere else + if (patternlab.patterns[j].patternPartial === patternlab.config.defaultPattern) { + if (patternlab.config.debug) { + console.log('Omitting ' + pattern.patternPartial + ' from view all sibling rendering because it is defined as a defaultPattern'); + } + continue; + } + + viewAllPatterns.push(patternlab.patterns[j]); + } + } + + //render the footer needed for the viewall template + var footerHTML = buildFooterHTML(patternlab, patternPartial); + + //render the viewall template + var viewAllHTML = buildViewAllHTML(patternlab, viewAllPatterns, patternPartial); + + fs.outputFileSync(paths.public.patterns + pattern.flatPatternPath + '/index.html', mainPageHeadHtml + viewAllHTML + footerHTML); + } + } +} + +function sortPatterns(patternsArray) { + return patternsArray.sort(function (a, b) { + if (a.name > b.name) { + return 1; + } + if (a.name < b.name) { + return -1; + } + + // a must be equal to b + return 0; + }); +} + + +// MAIN BUILDER FUNCTION + +function buildFrontEnd(patternlab) { + var mh = require('./media_hunter'); + var media_hunter = new mh(); + var ae = require('./annotation_exporter'); + var annotation_exporter = new ae(patternlab); + var styleguidePatterns = []; + var paths = patternlab.config.paths; + + patternlab.patternTypes = []; + patternlab.patternTypeIndex = []; + patternlab.patternPaths = {}; + patternlab.viewAllPaths = {}; + + //sort all patterns explicitly. + patternlab.patterns = sortPatterns(patternlab.patterns); + + //find mediaQueries + media_hunter.find_media_queries(path.resolve(paths.source.css), patternlab); + + // check if patterns are excluded, if not add them to styleguidePatterns + styleguidePatterns = assembleStyleguidePatterns(patternlab); + + //set the pattern-specific header by compiling the general-header with data, and then adding it to the meta header + var headerPartial = pattern_assembler.renderPattern(patternlab.header, { + cacheBuster: patternlab.cacheBuster + }); + var headerHTML = pattern_assembler.renderPattern(patternlab.userHead, { + patternLabHead : headerPartial + }); + + //set the pattern-specific footer by compiling the general-footer with data, and then adding it to the meta footer + var footerPartial = pattern_assembler.renderPattern(patternlab.footer, { + patternData: '{}', + cacheBuster: patternlab.cacheBuster + }); + var footerHTML = pattern_assembler.renderPattern(patternlab.userFoot, { + patternLabFoot : footerPartial + }); + + //build the styleguide + var styleguideTemplate = fs.readFileSync(path.resolve(paths.source.patternlabFiles, 'viewall.mustache'), 'utf8'); + var styleguideHtml = pattern_assembler.renderPattern(styleguideTemplate, + { + partials: styleguidePatterns, + cacheBuster: patternlab.cacheBuster + }, { + patternSection: patternlab.patternSection, + patternSectionSubType: patternlab.patternSectionSubType + }); + + fs.outputFileSync(path.resolve(paths.public.styleguide, 'html/styleguide.html'), headerHTML + styleguideHtml + footerHTML); + + //build the viewall pages + buildViewAllPages(headerHTML, patternlab); + + //build the patternlab website + buildNavigation(patternlab); + + //move the index file from its asset location into public root + var patternlabSiteHtml; + try { + patternlabSiteHtml = fs.readFileSync(path.resolve(paths.source.styleguide, 'index.html'), 'utf8'); + } catch (error) { + console.log(error); + console.log("\nERROR: Could not load one or more styleguidekit assets from", paths.source.styleguide, '\n'); + process.exit(1); + } + fs.outputFileSync(path.resolve(paths.public.root, 'index.html'), patternlabSiteHtml); + + //write out the data + var output = ''; + + //config + output += 'var config = ' + JSON.stringify(patternlab.config) + ';\n'; + + //ishControls + output += 'var ishControls = {"ishControlsHide":' + JSON.stringify(patternlab.config.ishControlsHide) + '};' + eol; + + //todo add media queries to this + //navItems + output += 'var navItems = {"patternTypes": ' + JSON.stringify(patternlab.patternTypes) + '};' + eol; + + //patternPaths + output += 'var patternPaths = ' + JSON.stringify(patternlab.patternPaths) + ';' + eol; + + //viewAllPaths + output += 'var viewAllPaths = ' + JSON.stringify(patternlab.viewAllPaths) + ';' + eol; + + //plugins someday + output += 'var plugins = [];' + eol; + + //smaller config elements + output += 'var defaultShowPatternInfo = ' + (patternlab.config.defaultShowPatternInfo ? patternlab.config.defaultShowPatternInfo : 'false') + ';' + eol; + output += 'var defaultPattern = "' + (patternlab.config.defaultPattern ? patternlab.config.defaultPattern : 'all') + '";' + eol; + + //write all ouytput to patternlab-data + fs.outputFileSync(path.resolve(paths.public.data, 'patternlab-data.js'), output); + + //annotations + var annotationsJSON = annotation_exporter.gather(); + var annotations = 'var comments = { "comments" : ' + JSON.stringify(annotationsJSON) + '};'; + fs.outputFileSync(path.resolve(paths.public.annotations, 'annotations.js'), annotations); + +} + +module.exports = buildFrontEnd; diff --git a/core/lib/utilities.js b/core/lib/utilities.js new file mode 100644 index 000000000..4765e874b --- /dev/null +++ b/core/lib/utilities.js @@ -0,0 +1,89 @@ +"use strict"; + +var fs = require('fs-extra'), + path = require('path'); + +var util = { + // http://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array-in-javascript + shuffle: function (o) { + /*eslint-disable curly*/ + for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); + return o; + }, + + logGreen: function (message) { + console.log('\x1b[32m', message, '\x1b[0m'); + }, + + logRed: function (message) { + console.log('\x1b[41m', message, '\x1b[0m'); + }, + + /** + * Recursively merge properties of two objects. + * + * @param {Object} obj1 If obj1 has properties obj2 doesn't, add to obj2. + * @param {Object} obj2 This object's properties have priority over obj1. + * @returns {Object} obj2 + */ + mergeData: function (obj1, obj2) { + /*eslint-disable no-param-reassign, guard-for-in*/ + if (typeof obj2 === 'undefined') { + obj2 = {}; + } + for (var p in obj1) { + try { + // Only recurse if obj1[p] is an object. + if (obj1[p].constructor === Object) { + // Requires 2 objects as params; create obj2[p] if undefined. + if (typeof obj2[p] === 'undefined') { + obj2[p] = {}; + } + obj2[p] = util.mergeData(obj1[p], obj2[p]); + + // Pop when recursion meets a non-object. If obj1[p] is a non-object, + // only copy to undefined obj2[p]. This way, obj2 maintains priority. + } else if (typeof obj2[p] === 'undefined') { + obj2[p] = obj1[p]; + } + } catch (e) { + // Property in destination object not set; create it and set its value. + if (typeof obj2[p] === 'undefined') { + obj2[p] = obj1[p]; + } + } + } + return obj2; + }, + + isObjectEmpty: function (obj) { + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { return false; } + } + return true; + }, + + // recursively delete the contents of directory + // adapted from https://gist.github.com/tkihira/2367067 + emptyDirectory: function (dir, cleanDir) { + var list = fs.readdirSync(dir); + for (var i = 0; i < list.length; i++) { + var filename = path.join(dir, list[i]); + var stat = fs.statSync(filename); + + if (filename === "." || filename === "..") { + // pass these files + } else if (stat.isDirectory()) { + this.emptyDirectory(filename); + } else { + // rm fiilename + fs.unlinkSync(filename); + } + } + if (cleanDir) { + fs.rmdirSync(dir); + } + } +}; + +module.exports = util; diff --git a/core/styleguide/css/static.css b/core/styleguide/css/static.css deleted file mode 100644 index e23eaf41e..000000000 --- a/core/styleguide/css/static.css +++ /dev/null @@ -1,459 +0,0 @@ -@charset "UTF-8"; -/* -colors -red: $orange; rgb(229,24,55) -gray: #808080; -*/ -/************Reset**************/ -* { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - -html, body, div, object, iframe, fieldset { - margin: 0; - padding: 0; - border: 0; -} - -ol, ul { - list-style: none; - margin: 0; - padding: 0; -} - -table { - border-collapse: collapse; - border-spacing: 0; -} - -header, footer, nav, section, article, hgroup, figure { - display: block; -} - -legend { - display: none; -} - -/************End Reset**************/ -/************Global**************/ -body { - background: #fff; - color: #000; - font: 100%/1.4 "HelveticaNeue", "Helvetica", "Arial", sans-serif; - padding: 0; - -webkit-text-size-adjust: 100%; - border-top: 20px solid #000; - border-bottom: 20px solid #000; -} - -a { - color: #808080; - text-decoration: none; -} - -a:hover, a:focus { - color: #bededf; -} - -p { - margin: 0 0 1em; -} - -img, object, video { - max-width: 100%; - border: 0; -} - -a img { - border: 0; - outline: 0; -} - -h1 { - font-size: 3em; - line-height: 1; - letter-spacing: -0.02em; - margin-bottom: 0.2em; -} - -h2 { - font-size: 2em; - line-height: 1.1; - margin-bottom: 0.2em; -} - -h3 { - font-weight: normal; - line-height: 1.1; - padding-bottom: 0.4em; - border-bottom: 1px solid #ccc; -} - -h1 a, h2 a, h3 a { - display: block; - color: #000; -} - -h1 a:hover, h2 a:hover, h3 a:hover { - color: #bededf; -} - -blockquote { - border-left: 0.5em solid #ddd; - padding-left: 1em; - margin-left: 1em; -} - -small { - color: #bededf; -} - -input[type=search] { - -webkit-appearance: none; - border-radius: 0; -} - -::-webkit-input-placeholder { - color: #808080; -} - -:-moz-placeholder { - color: #808080; -} - -/************End Global**************/ -/************Classes**************/ -.inactive { - color: #ddd; -} - -/************End Classes**************/ -/************Structure**************/ -.container { - max-width: 60em; - margin: 0 auto; - padding: 0 1em; - overflow: hidden; -} - -[role=main] { - padding-bottom: 1em; -} - -/*Footer*/ -[role=contentinfo] { - color: #fff; - background: #000; - margin: 0 -1em; - position: relative; - z-index: 2; -} -[role=contentinfo] > div { - max-width: 60em; - padding: 0 1em; - margin: 0 auto; - overflow: hidden; -} - -/*End Footer*/ -/*Grid*/ -.grid { - margin: 0 -1em; - overflow: hidden; -} - -.grid:target { - -webkit-animation: fadeout 5s 1 ease-out; - -moz-animation: fadeout 5s 1 ease-out; - -o-animation: fadeout 5s 1 ease-out; - animation: fadeout 5s 1 ease-out; -} - -.grid > h2 { - margin-left: 0.45em; -} - -.grid > section { - padding: 1em 1em 0.5em; -} - -.grid > section:target { - -webkit-animation: fadeout 5s 1 ease-out; - -moz-animation: fadeout 5s 1 ease-out; - -o-animation: fadeout 5s 1 ease-out; - animation: fadeout 5s 1 ease-out; -} - -.grid ul { - overflow: hidden; -} - -.grid ul li { - margin-bottom: 0.3em; -} - -.featured:after { - content: "*"; - color: #bededf; -} - -/*Fluid*/ -.fluid { - display: block; - margin: 0 auto; - max-width: 40em; -} - -/*Homepage*/ -.home h1 { - margin-bottom: 0.2em; -} - -.intro { - font-size: 1.8em; - line-height: 1.2; - margin: 0 auto; -} - -.intro a:hover, .intro a:focus { - color: #000; - border-bottom-color: #000; -} - -.ani { - position: relative; - height: 15em; - margin: 1em 0 0; - width: 100%; - z-index: 0; -} - -.ani div { - width: 100%; -} - -.ani div b { - display: block; - position: absolute; - top: 5%; - right: 5%; - bottom: 5%; - left: 5%; - background: rgba(229, 24, 55, 0.22); -} - -/*Patterns*/ -.mod { - padding: 1em; -} - -.pattern { - background: #f7f7f7; - border-bottom: 1px solid #808080; - margin-bottom: 1em; - overflow: hidden; -} - -.pattern-description h1 { - font-size: 3.4em; - margin-bottom: 0.5em; -} - -.pattern-description { - max-width: 40em; - margin: 0 auto; -} - -.pattern-description ul, .pattern-description ol { - margin-bottom: 2em; -} - -.pattern-description li { - margin-bottom: 1em; -} - -/*Blog*/ -/*Blog Header*/ -.blog .container { - max-width: 62em; -} - -.blog header[role=banner] { - overflow: hidden; - margin-bottom: 2em; - padding: 2em 0 1em; - border-bottom: 1px solid #000; -} - -.blog-logo { - font-weight: normal; - font-size: 1.2em; - margin: 0 0 1em; -} - -.blog-logo img { - width: 3.3em; -} - -.blog-logo a { - color: #000; -} - -.search-form { - width: 100%; - margin-bottom: 1em; -} - -.search-field { - width: 100%; - padding: 0.5em 0; - border: 0; - border-bottom: 1px solid #808080; - outline: none; -} - -.search-field:focus { - background: #bededf; - color: #fff; -} - -.search-field:focus::-webkit-input-placeholder { - color: #fff; -} - -.search-field:focus :-moz-placeholder { - color: #fff; -} - -.blog .nav { - clear: both; -} - -.blog .nav a { - display: block; - font-weight: bold; - color: #000; -} - -.blog .nav a:hover { - color: #bededf; -} - -/*Posts*/ -.posts ol > li { - padding-bottom: 1em; - border-bottom: 1px solid #ccc; - margin-bottom: 1em; - overflow: hidden; -} - -.posts h2 { - font-size: 1.4em; - margin: 0.28em 0 0.1em; - font-weight: normal; -} - -.posts h2 a { - color: #000; -} - -.posts h2 a:hover, .posts h2 a:focus { - color: #bededf; -} - -.permalink { - display: block; - font-size: 0.8em; - margin-bottom: 1.2em; -} - -.post-body a { - border-bottom: 1px solid #ccc; -} - -.posts blockquote { - margin: 0 0 1em; - color: #666; - border-left: 0.25em solid #ccc; - padding-left: 1em; -} - -.tags { - float: left; -} - -.tags li { - display: inline-block; - font-size: 0.8em; - margin-right: 0.5em; -} - -.posts ol > li .tags a, .permalink { - color: #ccc; - -webkit-transition: color 0.3s ease-out; - -moz-transition: color 0.3s ease-out; - -ms-transition: color 0.3s ease-out; - -o-transition: color 0.3s ease-out; - transition: color 0.3s ease-out; -} - -.posts ol > li:hover .tags a, .posts ol > li:hover .permalink { - color: #808080; -} - -.blog-nav { - text-align: center; - overflow: hidden; - padding: 1em 0; -} - -.posts .blog-nav a { - border: 0; -} - -.nav-next { - float: right; -} - -.nav-prev { - float: left; -} - -/* Sidebar */ -.sidebar { - font-size: 0.8em; - padding-bottom: 1.4em; -} - -.sidebar div { - margin-bottom: 2em; -} - -.sidebar h3 { - font-weight: bold; - font-size: 0.9em; - line-height: 1; - border-bottom: 1px solid #000; -} - -.sidebar a { - color: #808080; -} - -.sidebar a:hover, .sidebar a:focus { - color: #bededf; -} - -.top { - clear: both; - display: block; - padding: 1em 0; -} - -.top:before { - content: 'Γû▓'; -} - -/*# sourceMappingURL=static.css.map */ diff --git a/core/styleguide/css/styleguide-specific.css b/core/styleguide/css/styleguide-specific.css deleted file mode 100644 index 3be158e1a..000000000 --- a/core/styleguide/css/styleguide-specific.css +++ /dev/null @@ -1,101 +0,0 @@ -/*------------------------------------*\ - $PATTERN LAB-SPECIFIC STYLES -\*------------------------------------*/ -/** - * This stylesheet is for styles you want to include only when the interface is being viewed within Pattern Lab. - * This is helpful for displaying demo styles for grids, animations, color swatches, etc - * It's also helpful for overriding context-specific styles like fixed or absolutely positioned elements - * These styles will not be your production CSS. - */ -/* Style Guide Interface Colors */ -/* Typography */ -/* Defaults */ -/* Dimensions */ -/* Breakpoints */ -.demo { - overflow: hidden; - margin-bottom: 1rem; -} - -.demo .gi, .demo .demo-block { - background: #ddd; - color: #808080; - text-align: center; - margin-bottom: 0.5em; - padding: 1em !important; -} -.demo .gi:nth-of-type(2n), .demo .demo-block:nth-of-type(2n) { - color: #ddd; - background: #808080; -} -.demo .gi .gi, .demo .demo-block .gi { - background: rgba(0, 0, 0, 0.1); - color: #ddd; -} -.demo .gi .gi:nth-of-type(2n), .demo .demo-block .gi:nth-of-type(2n) { - background: rgba(0, 0, 0, 0.3); -} - -.demo-animate { - background: #ddd; - padding: 1em; - margin-bottom: 1em; - text-align: center; -} - -.animate-move { - position: relative; -} -.animate-move .demo-shape { - position: absolute; - top: 0; - left: 0; - bottom: 0; - width: 20px; - background: #808080; -} -.animate-move:hover > .demo-shape { - left: 100%; - margin-left: -20px; -} - -.sg-colors { - overflow: hidden; -} -.sg-colors li { - overflow: hidden; - border: 1px solid #ddd; - padding: 0.3em; - margin: 0 0.2em 0.2em 0; -} -@media all and (min-width: 30em) { - .sg-colors li { - float: left; - width: 5em; - } -} - -.sg-swatch { - display: block; - height: 1.5em; - width: 50%; -} -@media all and (max-width: 30em) { - .sg-swatch { - float: left; - margin-right: 0.3em; - } -} -@media all and (min-width: 30em) { - .sg-swatch { - width: 100%; - height: 4em; - margin-bottom: 0.2em; - } -} - -.sg-label { - line-height: 1; -} - -/*# sourceMappingURL=styleguide-specific.css.map */ diff --git a/core/styleguide/css/styleguide.css b/core/styleguide/css/styleguide.css deleted file mode 100644 index c7c2b48ae..000000000 --- a/core/styleguide/css/styleguide.css +++ /dev/null @@ -1,843 +0,0 @@ -/*------------------------------------*\ - $PATTERN LAB STYLES -\*------------------------------------*/ -/** - * NOTE: These styles are specific to Pattern Lab and should not be modified. - * Edit all project styles in /source/css/ - * - * Second note: Any important declarations are to prevent brand styles from overriding style guide - */ -/*------------------------------------*\ - $CONTENTS -\*------------------------------------*/ -/** - * STYLE GUIDE VARIABLES------------------Declarations of Sass variables - * -----Font - * -----Colors - * -----Typography - * -----Defaults - * -----Breakpoints - * MIXINS---------------------------------Sass mixins - * GLOBAL ELEMENTS------------------------Establish global styles - * -----Main - * -----Headings - * -----Text-related elements (p, blockquote, lists) - * -----Defaults - * -----Breakpoints - * STYLE GUIDE INTERFACE------------------CSS for the Pattern Lab Container. - * -----Header - * -----Navigation - * -----Controls - * -----Main Container - * -----Viewport - * -----Section Headers - * -----Code View - * -----Icon Fonts - */ -/*------------------------------------*\ - $PATTERN LAB VARIABLES -\*------------------------------------*/ -/*Fonts*/ -/* Style Guide Interface Colors */ -/* Typography */ -/* Defaults */ -/* Dimensions */ -/* Breakpoints */ -/*------------------------------------*\ - $PATTERN LAB MIXINS -\*------------------------------------*/ -/*------------------------------------*\ - $PATTERN LAB INTERFACE -\*------------------------------------*/ -#patternlab-html, #patternlab-body { - margin: 0; - padding: 0; - background: #ddd; - -webkit-text-size-adjust: 100%; -} - -.sg-nav-wrapper { - overflow: hidden; - background: #ddd; -} - -.is-vishidden { - position: absolute !important; - overflow: hidden; - width: 1px; - height: 1px; - padding: 0; - border: 0; - clip: rect(1px, 1px, 1px, 1px); -} - -.sg-cf, .sg-pattern { - /**zoom: 1;*/ -} -.sg-cf:before, .sg-pattern:before, .sg-cf:after, .sg-pattern:after { - content: " "; - display: table; -} -.sg-cf:after, .sg-pattern:after { - clear: both; -} - -/*------------------------------------*\ - $PATTERN LAB HEADER -\*------------------------------------*/ -/* Header */ -.sg-header { - background: #222; - color: #fff; - font-family: "HelveticaNeue", "Helvetica", "Arial", sans-serif; - text-transform: uppercase; - position: fixed; - top: 0; - left: 0; - z-index: 2; - width: 100%; -} -.sg-header * { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} -.sg-header ul, .sg-header ol { - padding: 0; - margin: 0; -} -.sg-header li { - list-style: none; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); -} -.sg-header a { - font-size: 70%; - color: #808080; - text-decoration: none; - display: block; - line-height: 1; - padding: 1em; - -webkit-transition: background 0.15s ease-out; - -moz-transition: background 0.15s ease-out; - -ms-transition: background 0.15s ease-out; - -o-transition: background 0.15s ease-out; - transition: background 0.15s ease-out; - -webkit-transition: color 0.15s ease-out; - -moz-transition: color 0.15s ease-out; - -ms-transition: color 0.15s ease-out; - -o-transition: color 0.15s ease-out; - transition: color 0.15s ease-out; -} -.sg-header a:hover, .sg-header a:focus, .sg-header a.active { - color: #fff; - background: rgba(255, 255, 255, 0.05); -} -.sg-header ol ol ol a { - padding-left: 2em; - text-transform: none; -} - -/* Navigation */ -.sg-header .sg-nav-toggle { - display: inline-block; - padding: 0.9em 1em; - border-bottom: 1px solid transparent; - position: relative; - text-transform: uppercase; - z-index: 2; -} -.sg-header .sg-nav-toggle span { - display: inline-block; - padding-right: 0.2em; -} -@media all and (min-width: 48em) { - .sg-header .sg-nav-toggle { - display: none; - } -} - -@media all and (max-width: 48em) { - .sg-nav-container { - overflow: hidden; - max-height: 0; - -webkit-transition: max-height 0.1s ease-out; - -moz-transition: max-height 0.1s ease-out; - -ms-transition: max-height 0.1s ease-out; - -o-transition: max-height 0.1s ease-out; - transition: max-height 0.1s ease-out; - } - .sg-nav-container.active { - max-height: 50em; - } -} - -.sg-nav { - z-index: 1; - margin: 0; - padding: 0; - list-style: none; -} -.sg-nav > li { - cursor: pointer; -} -@media all and (min-width: 48em) { - .sg-nav > li { - border-bottom: 0; - border-right: 1px solid rgba(255, 255, 255, 0.05); - float: left; - position: relative; - } - .sg-nav > li > ol { - position: absolute; - top: 2em; - left: 0; - } -} - -/* Accordion */ -.sg-acc-handle:after { - content: ' +'; - float: right; - font-size: 70%; -} -@media all and (min-width: 48em) { - .sg-acc-handle:after { - float: none; - } -} -.sg-acc-handle.active { - color: #fff; - background: rgba(255, 255, 255, 0.05); -} -.sg-acc-handle.active:after { - content: ' -'; -} -.sg-acc-handle.sg-icon:after { - content: ""; -} - -.sg-header .sg-icon { - width: auto; - font-size: 1rem; - padding: 0.5rem 1rem; -} - -.sg-acc-panel { - overflow: hidden; - max-height: 0; - min-width: 10em; - -webkit-transition: max-height 0.1s ease-out; - -moz-transition: max-height 0.1s ease-out; - -ms-transition: max-height 0.1s ease-out; - -o-transition: max-height 0.1s ease-out; - transition: max-height 0.1s ease-out; -} -.sg-acc-panel li { - background: #222; -} -.sg-acc-panel.active { - max-height: 120em; - overflow: auto; -} -.sg-acc-panel.sg-right { - position: absolute; - left: auto; - right: 0; -} -.sg-acc-panel.sg-left { - position: absolute; - left: auto; -} -.sg-acc-panel [class^="sg-icon-"]:before { - display: inline-block; - margin-right: 0.4em; -} - -/* Controls (sizing, view mode) */ -.sg-controls { - border: 0; - position: absolute; - top: 0; - right: 0; - z-index: 2; -} - -.sg-control-trigger { - border-bottom: 1px solid rgba(255, 255, 255, 0.05); -} -@media all and (min-width: 48em) { - .sg-control-trigger { - border: 0; - } -} -@media all and (min-width: 72em) { - .sg-control-trigger { - float: left; - width: 6em; - } -} - -.sg-control > li { - float: left; -} -@media all and (min-width: 48em) { - .sg-control > li { - border-bottom: 0; - border-left: 1px solid rgba(255, 255, 255, 0.05); - } -} -.sg-control .sg-input { - padding: 0.1em; - -webkit-transition: all 0.2s ease-out; - -moz-transition: all 0.2s ease-out; - -ms-transition: all 0.2s ease-out; - -o-transition: all 0.2s ease-out; - transition: all 0.2s ease-out; -} -.sg-control .sg-input:active, .sg-control .sg-input:focus { - outline: 0; - background: #999; - color: #000; -} - -.sg-current-size { - font-size: 70%; - color: #808080; - padding: 0.85em 0.7em; -} -.sg-current-size:hover .sg-input { - background: #999; - color: #000; -} -@media all and (min-width: 72em) { - .sg-current-size { - float: left; - } -} - -.sg-size { - width: 135px; -} -@media all and (min-width: 48em) { - .sg-size { - width: auto; - } -} - -@media all and (min-width: 72em) { - .sg-size-options { - float: left; - position: static; - max-height: none; - max-width: none; - } - .sg-size-options > li { - float: left; - border: 0; - border-left: 1px solid rgba(255, 255, 255, 0.05); - } -} - -#sg-size-mq { - display: none; -} -@media all and (min-width: 72em) { - #sg-size-mq { - display: block; - } -} - -#sg-form { - margin: 0; - border: 0; - padding: 0; -} - -.sg-input { - margin: -2px 0 0 0; - padding: 0; - border: 0; - background-color: #222; - color: gray; - width: 25px; - text-align: right; -} -@media all and (min-width: 48em) { - .sg-input { - width: 35px; - } -} - -.sg-input-active { - background-color: #fff; - color: #000; -} - -.sg-view { - position: relative; -} -.sg-view > ul { - position: absolute; - top: 2em; - left: 0; -} - -.sg-checkbox:before { - display: inline-block; - margin-right: 0.4em; -} - -/* basic styling */ -.sg-pattern-state:before { - margin-right: 4px; - content: "\2022"; - display: inline-block; - margin-bottom: -4px; - font-size: 18px; - vertical-align: bottom; -} - -/* nav styling */ -.sg-nav .sg-pattern-state:before { - margin-top: -4px; - margin-bottom: 0; - margin-left: -4px; - height: 20px; - display: block; - float: left; -} - -.sg-sub-nav .sg-pattern-state:before { - margin-left: -11px; - margin-right: 4px; -} - -/* call out for pattern's pattern state */ -span.sg-pattern-state { - color: #999; -} - -span.sg-pattern-state:before { - margin-bottom: -3px; - margin-left: 4px; -} - -/* pattern states */ -.inprogress:before { - color: #FF4136 !important; -} - -.inreview:before { - color: #FFCC00 !important; -} - -.complete:before { - color: #2ECC40 !important; -} - -/*------------------------------------*\ - $PATTERN LAB VIEWPORT -\*------------------------------------*/ -/* Viewport */ -#sg-vp-wrap { - text-align: center; - width: 100%; - position: fixed; - top: 2em; - bottom: 0; - left: 0; - right: 0; - z-index: 0; -} -#sg-vp-wrap.wrap-animate { - -webkit-transition: left 0.3s ease-out; - -moz-transition: left 0.3s ease-out; - -ms-transition: left 0.3s ease-out; - -o-transition: left 0.3s ease-out; - transition: left 0.3s ease-out; -} - -#sg-viewport { - position: absolute; - height: 100%; - width: 100%; - border: 0; - padding: 0; - margin: 0; - top: 0; - bottom: 0; - left: 0; - right: 0; - background-color: white; -} -#sg-viewport.hay-mode { - -webkit-transition: all 40s linear; - -moz-transition: all 40s linear; - -ms-transition: all 40s linear; - -o-transition: all 40s linear; - transition: all 40s linear; -} - -.no-resize #sg-cover, .no-resize #sg-rightpull-container { - display: none; -} -.no-resize #sg-viewport { - overflow: hidden !important; -} - -#sg-cover { - width: 100%; - height: 100%; - display: none; - position: absolute; - z-index: 20; - cursor: col-resize; -} - -#sg-gen-container { - height: 100%; - position: relative; - text-align: center; - margin: 0 auto; - -webkit-overflow-scrolling: touch; - overflow-y: auto; - overflow-x: hidden; -} -#sg-gen-container.hay-mode { - -webkit-transition: all 40s linear; - -moz-transition: all 40s linear; - -ms-transition: all 40s linear; - -o-transition: all 40s linear; - transition: all 40s linear; -} - -#sg-rightpull-container { - width: 14px; - float: right; - margin: 0; - height: 100%; - cursor: col-resize; -} - -#sg-rightpull { - margin: 0; - width: 100%; - height: 100%; - background: #999; - -webkit-transition: background 0.2s ease-out; - -moz-transition: background 0.2s ease-out; - -ms-transition: background 0.2s ease-out; - -o-transition: background 0.2s ease-out; - transition: background 0.2s ease-out; -} -#sg-rightpull:hover { - background: #666; -} -#sg-rightpull:active { - cursor: col-resize; - background: #444; -} - -.vp-animate { - -webkit-transition: width 0.8s ease-out; - -moz-transition: width 0.8s ease-out; - -ms-transition: width 0.8s ease-out; - -o-transition: width 0.8s ease-out; - transition: width 0.8s ease-out; -} - -/*------------------------------------*\ - $PATTERN LAB CONTENT -\*------------------------------------*/ -/* Section Pattern */ -.sg-pattern { - margin-bottom: 2em; - position: relative; -} - -/* Section Head */ -.sg-pattern-head { - margin: 2em 0; - font-family: "HelveticaNeue", "Helvetica", "Arial", sans-serif; - font-size: 70%; - font-weight: normal; - padding: 1em 0; - border-bottom: 1px solid #808080; -} -.sg-pattern-head a { - display: block; - color: #808080; - text-decoration: none; - cursor: pointer; -} -.sg-pattern-head a:hover { - color: #222; -} - -.sg-view-container { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - font-family: "HelveticaNeue", "Helvetica", "Arial", sans-serif; - line-height: 1.4; - font-size: 90%; - background: #222; - color: #808080; - position: fixed; - top: auto; - padding: 1em; - bottom: 0; - left: 0; - z-index: 2; - width: 100%; - height: 50%; - overflow-y: auto; - overflow-x: hidden; -} -.sg-view-container a { - color: #999; -} -.sg-view-container pre { - padding: 0 1em; -} -.sg-view-container.anim-ready { - -webkit-transition: bottom 0.3s ease-out; - -moz-transition: bottom 0.3s ease-out; - -webkit-transition: bottom 0.3s ease-out; - -ms-transition: bottom 0.3s ease-out; - -o-transition: bottom 0.3s ease-out; - transition: bottom 0.3s ease-out; -} - -.sg-view-close { - width: 100%; - margin-bottom: -10px; -} - -.sg-view-close-btn { - color: #fff; - text-transform: uppercase; - text-decoration: none; - text-align: right; - display: block; -} - -.has-annotation { - cursor: help !important; - box-shadow: 0 0 10px #808080; -} -.has-annotation a, .has-annotation input { - cursor: help !important; -} -.has-annotation:hover { - box-shadow: 0 0 10px #222; -} -.has-annotation.active { - box-shadow: inset 0 0 20px #808080; -} - -.annotation-tip { - display: block; - position: absolute; - margin-top: -10px !important; - margin-left: -10px !important; - width: 25px !important; - height: 25px !important; - border-radius: 13px !important; - text-align: center !important; - background: #444 !important; - color: #fff !important; - font-weight: bold !important; - font-size: 16px !important; - z-index: 100; -} - -#sg-comments-container { - max-width: 60em; - margin: 0 auto; -} - -.sg-comment-container { - padding-bottom: 2em; - margin-bottom: 1em; - border-bottom: 1px solid rgba(255, 255, 255, 0.25); -} -.sg-comment-container p:last-child { - margin-bottom: 0; -} -.sg-comment-container h2 { - margin-bottom: 0.25em; -} - -.sg-code, .sg-annotations { - clear: both; - background: #ddd; - color: #222; - padding: 1em 0.5em; - margin: 1em 0; -} -.sg-code a, .sg-annotations a { - text-decoration: underline; -} - -.sg-code pre { - white-space: -moz-pre-line; - white-space: -pre-line; - white-space: -o-pre-line; - word-wrap: break-word; - white-space: pre-line; - border: 1px solid rgba(0, 0, 0, 0.1); - padding: 0.5em; -} - -.sg-code-contains { - margin-bottom: 1rem; - font-size: 85%; - color: #808080; -} -.sg-code-contains code { - padding: 0.2em; - background: rgba(0, 0, 0, 0.3); - color: #999; - position: relative; - top: -2px; -} - -.sg-code-head { - color: #808080; - font-size: 1em; -} - -#sg-code-markup { - padding-top: 10px; -} - -#sg-code-tabs { - list-style: none; - margin: 0; - padding: 0; -} - -#sg-code-tabs li { - float: left; - background-color: #333; - font-size: 1.3em; - font-weight: bold; - padding: 5px 15px; - border-top: 2px solid #666; - margin-right: 2px; - cursor: pointer; -} - -.sg-code-title-active { - color: #bbb; - background-color: #272822 !important; -} - -div.clear { - clear: both; -} - -.sg-code-patternname { - color: #aaa; -} - -#sg-code-loader { - display: none; - position: absolute; - left: 45%; - top: 20%; - width: 150px; - padding: 10px; - text-align: center; - border-radius: 10px; - background-color: #000; - z-index: 100; -} - -.spinner { - height: 30px; - width: 30px; - margin-left: auto; - margin-right: auto; - background-position: center center; - background-repeat: no-repeat; - background: url("../images/spinner.gif"); - border-radius: 50%; - opacity: .7; -} - -/* Pattern Lab icon fonts */ -@font-face { - font-family: 'icomoon'; - src: url("../fonts/icomoon.eot?srsv7g"); - src: url("../fonts/icomoon.eot?#iefixsrsv7g") format("embedded-opentype"), url("../fonts/icomoon.woff?srsv7g") format("woff"), url("../fonts/icomoon.ttf?srsv7g") format("truetype"), url("../fonts/icomoon.svg?srsv7g#icomoon") format("svg"); - font-weight: normal; - font-style: normal; -} -.sg-icon-search, .sg-icon-cog, .sg-icon-minus, .sg-icon-plus, .sg-icon-menu, .sg-icon-radio-checked, .sg-checkbox.active, .sg-icon-radio-unchecked, .sg-checkbox, .sg-icon-file, .sg-icon-link, .sg-icon-keyboard, .sg-icon-qrcode, .sg-icon-eye, .sg-checkbox { - font-family: 'icomoon'; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - /* Better Font Rendering =========== */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.sg-icon-search:before { - content: "\e600"; - font-size: 85%; -} - -.sg-icon-cog:before { - content: "\e601"; -} - -.sg-icon-minus:before { - content: "\e602"; -} - -.sg-icon-plus:before { - content: "\e603"; -} - -.sg-icon-menu:before { - content: "\e604"; -} - -.sg-icon-radio-checked:before, .sg-checkbox.active:before, .sg-checkbox.active:before { - content: "\e605"; -} - -.sg-icon-radio-unchecked:before, .sg-checkbox:before, .sg-checkbox:before { - content: "\e606"; -} - -.sg-icon-file:before { - content: "\e607"; -} - -.sg-icon-link:before { - content: "\e608"; -} - -.sg-icon-keyboard:before { - content: "\e609"; -} - -.sg-icon-qrcode:before { - content: "\e60a"; -} - -.sg-icon-eye:before { - content: "\e60b"; -} - -/******************************************************************/ -/* End Pattern Lab Interface code */ - -/*# sourceMappingURL=styleguide.css.map */ diff --git a/core/styleguide/css/vendor/prism.css b/core/styleguide/css/vendor/prism.css deleted file mode 100644 index 0b78bd0c4..000000000 --- a/core/styleguide/css/vendor/prism.css +++ /dev/null @@ -1,112 +0,0 @@ -/** - * okaidia theme for JavaScript, CSS and HTML - * Loosely based on Monokai textmate theme by http://www.monokai.nl/ - * @author ocodia - */ - -code[class*="language-"], -pre[class*="language-"] { - color: #f8f8f2; - text-shadow: 0 1px rgba(0,0,0,0.3); - font-family: Consolas, Monaco, 'Andale Mono', monospace; - direction: ltr; - text-align: left; - white-space: pre; - word-spacing: normal; - - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; -} - -/* Code blocks */ -pre[class*="language-"] { - padding: 1em; - margin: .5em 0; - overflow: auto; - border-radius: 0.3em; -} - -:not(pre) > code[class*="language-"], -pre[class*="language-"] { - background: #272822; -} - -/* Inline code */ -:not(pre) > code[class*="language-"] { - padding: .1em; - border-radius: .3em; -} - -.token.comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: slategray; -} - -.token.punctuation { - color: #f8f8f2; -} - -.namespace { - opacity: .7; -} - -.token.property, -.token.tag, -.token.constant, -.token.symbol { - color: #f92672; -} - -.token.boolean, -.token.number{ - color: #ae81ff; -} - -.token.selector, -.token.attr-name, -.token.string, -.token.builtin { - color: #a6e22e; -} - - -.token.operator, -.token.entity, -.token.url, -.language-css .token.string, -.style .token.string, -.token.variable { - color: #f8f8f2; -} - -.token.atrule, -.token.attr-value -{ - color: #e6db74; -} - - -.token.keyword{ -color: #66d9ef; -} - -.token.regex, -.token.important { - color: #fd971f; -} - -.token.important { - font-weight: bold; -} - -.token.entity { - cursor: help; -} diff --git a/core/styleguide/css/vendor/typeahead.css b/core/styleguide/css/vendor/typeahead.css deleted file mode 100644 index a4f940169..000000000 --- a/core/styleguide/css/vendor/typeahead.css +++ /dev/null @@ -1,66 +0,0 @@ -#sg-find { - color: #000; - text-transform: lowercase; -} - -.show-overflow { - overflow: visible; -} - -.typeahead, -.tt-query, -.tt-hint { - width: 220px; - height: 30px; - padding: 8px 12px; - font-size: 14px; - line-height: 16px; - border: 2px solid #ccc; - outline: none; -} - -.typeahead { - background-color: #fff; -} - -.typeahead:focus { - border: 2px solid #999; -} - -.tt-query { - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} - -.tt-hint { - color: #999 -} - -.tt-dropdown-menu { - width: 422px; - padding: 8px 0; - background-color: #fff; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.2); - text-align: left; - -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2); - -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2); - box-shadow: 0 5px 10px rgba(0,0,0,.2); -} - -.tt-suggestion { - padding: 3px 13px; - font-size: 14px; - line-height: 16px; -} - -.tt-suggestion.tt-cursor { - color: #fff; - background-color: #0097cf; - -} - -.tt-suggestion p { - margin: 0; -} \ No newline at end of file diff --git a/core/styleguide/fonts/icomoon.eot b/core/styleguide/fonts/icomoon.eot deleted file mode 100644 index a44826918..000000000 Binary files a/core/styleguide/fonts/icomoon.eot and /dev/null differ diff --git a/core/styleguide/fonts/icomoon.svg b/core/styleguide/fonts/icomoon.svg deleted file mode 100644 index d69b0abd4..000000000 --- a/core/styleguide/fonts/icomoon.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - -Generated by IcoMoon - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/styleguide/fonts/icomoon.ttf b/core/styleguide/fonts/icomoon.ttf deleted file mode 100644 index b4fc4ac20..000000000 Binary files a/core/styleguide/fonts/icomoon.ttf and /dev/null differ diff --git a/core/styleguide/fonts/icomoon.woff b/core/styleguide/fonts/icomoon.woff deleted file mode 100644 index ca0c4cffb..000000000 Binary files a/core/styleguide/fonts/icomoon.woff and /dev/null differ diff --git a/core/styleguide/html/README b/core/styleguide/html/README deleted file mode 100644 index 5b0c53819..000000000 --- a/core/styleguide/html/README +++ /dev/null @@ -1 +0,0 @@ -will hold the auto-generated styleguide.html \ No newline at end of file diff --git a/core/styleguide/images/spinner.gif b/core/styleguide/images/spinner.gif deleted file mode 100644 index a0aa8c8df..000000000 Binary files a/core/styleguide/images/spinner.gif and /dev/null differ diff --git a/core/styleguide/js/annotations-pattern.js b/core/styleguide/js/annotations-pattern.js deleted file mode 100644 index 3a30b25a7..000000000 --- a/core/styleguide/js/annotations-pattern.js +++ /dev/null @@ -1,308 +0,0 @@ -/*! - * Annotations Support for Patterns - v0.3 - * - * Copyright (c) 2013-2014 Dave Olsen, http://dmolsen.com - * Licensed under the MIT license - * - */ - -var annotationsPattern = { - - commentsOverlayActive: false, - commentsOverlay: false, - commentsEmbeddedActive: false, - commentsEmbedded: false, - commentsGathered: { "commentOverlay": "on", "comments": { } }, - trackedElements: [ ], - targetOrigin: (window.location.protocol == "file:") ? "*" : window.location.protocol+"//"+window.location.host, - - /** - * record which annotations are related to this pattern so they can be sent to the viewer when called - */ - gatherComments: function() { - - // make sure this only added when we're on a pattern specific view - if (document.getElementById("sg-patterns") === null) { - - var count = 0; - - for (comment in comments.comments) { - var item = comments.comments[comment]; - var els = document.querySelectorAll(item.el); - if (els.length > 0) { - - count++; - item.displaynumber = count; - - for (var i = 0; i < els.length; ++i) { - els[i].onclick = (function(item) { - return function(e) { - - if (annotationsPattern.commentsOverlayActive) { - - e.preventDefault(); - e.stopPropagation(); - - // if an element was clicked on while the overlay was already on swap it - var obj = JSON.stringify({ "displaynumber": item.displaynumber, "el": item.el, "title": item.title, "comment": item.comment }); - parent.postMessage(obj,annotationsPattern.targetOrigin); - - } - - } - })(item); - } - } - - - } - - } else { - - var obj = JSON.stringify({ "commentOverlay": "off" }); - parent.postMessage(obj,annotationsPattern.targetOrigin); - - } - - }, - - /** - * embed a comment by building the sg-annotations div (if necessary) and building an sg-annotation div - * @param {Object} element to check the parent node of - * @param {String} the title of the comment - * @param {String} the comment HTML - */ - embedComments: function (el,title,comment) { - - // build the annotation div and add the content to it - var annotationDiv = document.createElement("div"); - annotationDiv.classList.add("sg-annotation"); - - var h3 = document.createElement("h3"); - var p = document.createElement("p"); - h3.innerHTML = title; - p.innerHTML = comment; - - annotationDiv.appendChild(h3); - annotationDiv.appendChild(p); - - // find the parent element to attach things to - var parentEl = annotationsPattern.findParent(el); - - // see if a child with the class annotations exists - var els = parentEl.getElementsByClassName("sg-annotations"); - if (els.length > 0) { - els[0].appendChild(annotationDiv); - } else { - var annotationsDiv = document.createElement("div"); - annotationsDiv.classList.add("sg-annotations"); - annotationsDiv.appendChild(annotationDiv); - parentEl.appendChild(annotationsDiv); - } - - }, - - /** - * recursively find the parent of an element to see if it contains the sg-pattern class - * @param {Object} element to check the parent node of - */ - findParent: function(el) { - - var parentEl; - - if (el.classList.contains("sg-pattern")) { - return el; - } else if (el.parentNode.classList.contains("sg-pattern")) { - return el.parentNode; - } else { - parentEl = annotationsPattern.findParent(el.parentNode); - } - - return parentEl; - - }, - - /** - * toggle the annotation feature on/off - * based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage - * @param {Object} event info - */ - receiveIframeMessage: function(event) { - - var data = (typeof event.data !== "string") ? event.data : JSON.parse(event.data); - - // does the origin sending the message match the current host? if not dev/null the request - if ((window.location.protocol != "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) { - return; - } - - if ((data.resize !== undefined) && (annotationsPattern.commentsOverlayActive)) { - - for (var i = 0; i < annotationsPattern.trackedElements.length; ++i) { - var el = annotationsPattern.trackedElements[i]; - if (window.getComputedStyle(el.element,null).getPropertyValue("max-height") == "0px") { - el.element.firstChild.style.display = "none"; - var obj = JSON.stringify({"annotationState": false, "displayNumber": el.displayNumber }); - parent.postMessage(obj,annotationsPattern.targetOrigin); - } else { - el.element.firstChild.style.display = "block"; - var obj = JSON.stringify({"annotationState": true, "displayNumber": el.displayNumber }); - parent.postMessage(obj,annotationsPattern.targetOrigin); - } - } - - } else if (data.commentToggle !== undefined) { - - var i, els, item, displayNum; - - // if this is an overlay make sure it's active for the onclick event - annotationsPattern.commentsOverlayActive = false; - annotationsPattern.commentsEmbeddedActive = false; - - // see which flag to toggle based on if this is a styleguide or view-all page - if ((data.commentToggle === "on") && (document.getElementById("sg-patterns") !== null)) { - annotationsPattern.commentsEmbeddedActive = true; - } else if (data.commentToggle === "on") { - annotationsPattern.commentsOverlayActive = true; - } - - // if comments overlay is turned off make sure to remove the has-annotation class and pointer - if (!annotationsPattern.commentsOverlayActive) { - els = document.querySelectorAll(".has-annotation"); - for (i = 0; i < els.length; i++) { - els[i].classList.remove("has-annotation"); - } - els = document.querySelectorAll(".annotation-tip"); - for (i = 0; i < els.length; i++) { - els[i].style.display = "none"; - } - } - - // if comments embedding is turned off make sure to hide the annotations div - if (!annotationsPattern.commentsEmbeddedActive) { - els = document.getElementsByClassName("sg-annotations"); - for (i = 0; i < els.length; i++) { - els[i].style.display = "none"; - } - } - - // if comments overlay is turned on add the has-annotation class and pointer - if (annotationsPattern.commentsOverlayActive) { - - var count = 0; - - for (i = 0; i < comments.comments.length; i++) { - item = comments.comments[i]; - els = document.querySelectorAll(item.el); - - var state = true; - - if (els.length) { - - count++; - - //Loop through all items with annotations - for (k = 0; k < els.length; k++) { - - els[k].classList.add("has-annotation"); - - var span = document.createElement("span"); - span.innerHTML = count; - span.classList.add("annotation-tip"); - - if (window.getComputedStyle(els[k],null).getPropertyValue("max-height") == "0px") { - span.style.display = "none"; - state = false; - } - - annotationsPattern.trackedElements.push({ "itemel": item.el, "element": els[k], "displayNumber": count, "state": state }); - - els[k].insertBefore(span,els[k].firstChild); - - } - - } - - } - - // count elements so it can be used when displaying the results in the viewer - var count = 0; - - // iterate over the comments in annotations.js - for(i = 0; i < comments.comments.length; i++) { - - var state = true; - - var item = comments.comments[i]; - var els = document.querySelectorAll(item.el); - - // if an element is found in the given pattern add it to the overall object so it can be passed when the overlay is turned on - if (els.length > 0) { - count++; - for (k = 0; k < els.length; k++) { - if (window.getComputedStyle(els[k],null).getPropertyValue("max-height") == "0px") { - state = false; - } - } - annotationsPattern.commentsGathered.comments[count] = { "el": item.el, "title": item.title, "comment": item.comment, "number": count, "state": state }; - } - - } - - // send the list of annotations for the page back to the parent - var obj = JSON.stringify(annotationsPattern.commentsGathered); - parent.postMessage(obj,annotationsPattern.targetOrigin); - - } else if (annotationsPattern.commentsEmbeddedActive && !annotationsPattern.commentsEmbedded) { - - // if comment embedding is turned on and comments haven't been embedded yet do it - for (i = 0; i < comments.comments.length; i++) { - item = comments.comments[i]; - els = document.querySelectorAll(item.el); - if (els.length > 0) { - annotationsPattern.embedComments(els[0],item.title,item.comment); //Embed the comment - } - annotationsPattern.commentsEmbedded = true; - } - - } else if (annotationsPattern.commentsEmbeddedActive && annotationsPattern.commentsEmbedded) { - - // if comment embedding is turned on and comments have been embedded simply display them - els = document.getElementsByClassName("sg-annotations"); - for (i = 0; i < els.length; ++i) { - els[i].style.display = "block"; - } - - } - - } - - } - -}; - -// add the onclick handlers to the elements that have an annotations -annotationsPattern.gatherComments(); -window.addEventListener("message", annotationsPattern.receiveIframeMessage, false); - -// before unloading the iframe make sure any active overlay is turned off/closed -window.onbeforeunload = function() { - var obj = JSON.stringify({ "commentOverlay": "off" }); - parent.postMessage(obj,annotationsPattern.targetOrigin); -}; - -// tell the parent iframe that keys were pressed - -// toggle the annotations panel -jwerty.key('ctrl+shift+a', function (e) { - var obj = JSON.stringify({ "keyPress": "ctrl+shift+a" }); - parent.postMessage(obj,codePattern.targetOrigin); - return false; -}); - -// close the annotations panel if using escape -jwerty.key('esc', function (e) { - var obj = JSON.stringify({ "keyPress": "esc" }); - parent.postMessage(obj,codePattern.targetOrigin); - return false; -}); \ No newline at end of file diff --git a/core/styleguide/js/annotations-viewer.js b/core/styleguide/js/annotations-viewer.js deleted file mode 100644 index 79bfd2093..000000000 --- a/core/styleguide/js/annotations-viewer.js +++ /dev/null @@ -1,289 +0,0 @@ -/*! - * Annotations Support for the Viewer - v0.3 - * - * Copyright (c) 2013 Brad Frost, http://bradfrostweb.com & Dave Olsen, http://dmolsen.com - * Licensed under the MIT license - * - */ - -var annotationsViewer = { - - // set-up default sections - commentsActive: false, - commentsViewAllActive: false, - targetOrigin: (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host, - moveToOnInit: 0, - - /** - * add the onclick handler to the annotations link in the main nav - */ - onReady: function() { - - // not sure this is used anymore... - $('body').addClass('comments-ready'); - - $(window).resize(function() { - if(!annotationsViewer.commentsActive) { - annotationsViewer.slideComment($('#sg-annotation-container').outerHeight()); - } - }); - - $('#sg-t-annotations').click(function(e) { - - e.preventDefault(); - - // remove the class from the "eye" nav item - $('#sg-t-toggle').removeClass('active'); - - // turn the annotations section on and off - annotationsViewer.toggleComments(); - - }); - - // initialize the annotations viewer - annotationsViewer.commentContainerInit(); - - // load the query strings in case code view has to show by default - var queryStringVars = urlHandler.getRequestVars(); - if ((queryStringVars.view !== undefined) && ((queryStringVars.view === "annotations") || (queryStringVars.view === "a"))) { - annotationsViewer.openComments(); - if (queryStringVars.number !== undefined) { - annotationsViewer.moveToOnInit = queryStringVars.number; - } - } - - }, - - /** - * decide on if the annotations panel should be open or closed - */ - toggleComments: function() { - - if (!annotationsViewer.commentsActive) { - annotationsViewer.openComments(); - } else { - annotationsViewer.closeComments(); - } - - }, - - /** - * open the annotations panel - */ - openComments: function() { - - // make sure the code view overlay is off before showing the annotations view - $('#sg-t-code').removeClass('active'); - codeViewer.codeActive = false; - var obj = JSON.stringify({ "codeToggle": "off" }); - document.getElementById('sg-viewport').contentWindow.postMessage(obj,annotationsViewer.targetOrigin); - codeViewer.slideCode(999); - - // tell the iframe annotation view has been turned on - var obj = JSON.stringify({ "commentToggle": "on" }); - document.getElementById('sg-viewport').contentWindow.postMessage(obj,annotationsViewer.targetOrigin); - - // note that it's turned on in the viewer - annotationsViewer.commentsActive = true; - $('#sg-t-annotations').addClass('active'); - }, - - /** - * close the annotations panel - */ - closeComments: function() { - annotationsViewer.commentsActive = false; - var obj = JSON.stringify({ "commentToggle": "off" }); - document.getElementById('sg-viewport').contentWindow.postMessage(obj,annotationsViewer.targetOrigin); - annotationsViewer.slideComment($('#sg-annotation-container').outerHeight()); - $('#sg-t-annotations').removeClass('active'); - }, - - /** - * add the basic mark-up and events for the annotations container - */ - commentContainerInit: function() { - - // the bulk of this template is in core/templates/index.mustache - if (document.getElementById("sg-annotation-container") === null) { - $('
').html($("#annotations-template").html()).appendTo('body').css('bottom',-$(document).outerHeight()); - setTimeout(function(){ $('#sg-annotation-container').addClass('anim-ready'); },50); //Add animation class once container is positioned out of frame - } - - // make sure the close button handles the click - $('body').delegate('#sg-annotation-close-btn','click',function() { - annotationsViewer.commentsActive = false; - $('#sg-t-annotations').removeClass('active'); - annotationsViewer.slideComment($('#sg-annotation-container').outerHeight()); - var obj = JSON.stringify({ "commentToggle": "off" }); - document.getElementById('sg-viewport').contentWindow.postMessage(obj,annotationsViewer.targetOrigin); - return false; - }); - - }, - - /** - * slides the panel - */ - slideComment: function(pos) { - $('#sg-annotation-container').css('bottom',-pos); - }, - - /** - * moves to a particular item in the viewer - */ - moveTo: function(number) { - if (document.getElementById("annotation-"+number) !== undefined) { - var top = document.getElementById("annotation-"+number).offsetTop; - $('#sg-annotation-container').animate({scrollTop: top - 10}, 600); - } - }, - - /** - * when turning on or switching between patterns with annotations view on make sure we get - * the annotations from from the pattern via post message - */ - updateComments: function(comments) { - - var commentsContainer = document.getElementById("sg-comments-container"); - - // clear out the comments container - if (commentsContainer.innerHTML !== "") { - commentsContainer.innerHTML = ""; - } - - // see how many comments this pattern might have. if more than zero write them out. if not alert the user to the fact their aren't any - var count = Object.keys(comments).length; - if (count > 0) { - - for (i = 1; i <= count; i++) { - - var displayNum = comments[i].number; - - var span = document.createElement("span"); - span.id = "annotation-state-" + displayNum; - span.style.fontSize = "0.8em"; - span.style.color = "#666"; - if (comments[i].state === false) { - span.innerHTML = " hidden"; - } - - var h2 = document.createElement("h2"); - h2.innerHTML = displayNum + ". " + comments[i].title; - h2.appendChild(span); - - var div = document.createElement("div"); - div.innerHTML = comments[i].comment; - - var commentDiv = document.createElement("div"); - commentDiv.classList.add("sg-comment-container"); - commentDiv.id = "annotation-" + displayNum; - commentDiv.appendChild(h2); - commentDiv.appendChild(div); - - commentsContainer.appendChild(commentDiv); - - } - - } else { - - var h2 = document.createElement("h2"); - h2.innerHTML = "No Annotations"; - - var div = document.createElement("div"); - div.innerHTML = "There are no annotations for this pattern."; - - var commentDiv = document.createElement("div"); - commentDiv.classList.add("sg-comment-container"); - commentDiv.appendChild(h2); - commentDiv.appendChild(div); - - commentsContainer.appendChild(commentDiv); - - } - - // slide the comment section into view - annotationsViewer.slideComment(0); - - if (annotationsViewer.moveToOnInit != "0") { - annotationsViewer.moveTo(annotationsViewer.moveToOnInit); - annotationsViewer.moveToOnInit = "0"; - } - - }, - - /** - * toggle the comment pop-up based on a user clicking on the pattern - * based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage - * @param {Object} event info - */ - receiveIframeMessage: function(event) { - - var data = (typeof event.data !== "string") ? event.data : JSON.parse(event.data); - - // does the origin sending the message match the current host? if not dev/null the request - if ((window.location.protocol !== "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) { - return; - } - - if (data.commentOverlay !== undefined) { - if (data.commentOverlay === "on") { - annotationsViewer.updateComments(data.comments); - } else { - annotationsViewer.slideComment($('#sg-annotation-container').outerHeight()); - } - } else if (data.annotationState !== undefined) { - document.getElementById("annotation-state-"+data.displayNumber).innerHTML = (data.annotationState == true) ? "" : " hidden"; - } else if (data.displaynumber !== undefined) { - annotationsViewer.moveTo(data.displaynumber); - } else if (data.keyPress !== undefined) { - if (data.keyPress == 'ctrl+shift+a') { - annotationsViewer.toggleComments(); - return false; - } else if (data.keyPress == 'esc') { - if (annotationsViewer.commentsActive) { - annotationsViewer.closeComments(); - return false; - } - } - } else if (data.patternpartial !== undefined) { - if (annotationsViewer.commentsViewAllActive && (data.patternpartial.indexOf("viewall-") != -1)) { - var obj = JSON.stringify({ "commentToggle": "on" }); - document.getElementById('sg-viewport').contentWindow.postMessage(obj,annotationsViewer.targetOrigin); - } - } - - } - -}; - -$(document).ready(function() { annotationsViewer.onReady(); }); -window.addEventListener("message", annotationsViewer.receiveIframeMessage, false); - -// make sure if a new pattern or view-all is loaded that comments are turned on as appropriate -$('#sg-viewport').load(function() { - if (annotationsViewer.commentsActive) { - var obj = JSON.stringify({ "commentToggle": "on" }); - document.getElementById('sg-viewport').contentWindow.postMessage(obj,annotationsViewer.targetOrigin); - } -}); - -// no idea why this has to be outside. there's something funky going on with the JS pattern -$('#sg-view li a').click(function() { - $(this).parent().parent().removeClass('active'); - $(this).parent().parent().parent().parent().removeClass('active'); -}); - -// toggle the annotations panel -jwerty.key('ctrl+shift+a', function (e) { - annotationsViewer.toggleComments(); - return false; -}); - -// close the annotations panel if using escape -jwerty.key('esc', function (e) { - if (annotationsViewer.commentsActive) { - annotationsViewer.closeComments(); - return false; - } -}); \ No newline at end of file diff --git a/core/styleguide/js/code-pattern.js b/core/styleguide/js/code-pattern.js deleted file mode 100644 index f8a949519..000000000 --- a/core/styleguide/js/code-pattern.js +++ /dev/null @@ -1,118 +0,0 @@ -/*! - * Code View Support for Patterns - v0.3 - * - * Copyright (c) 2013-2014 Dave Olsen, http://dmolsen.com - * Licensed under the MIT license - * - */ - -var codePattern = { - - codeOverlayActive: false, - codeEmbeddedActive: false, - targetOrigin: (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host, - - /** - * toggle the annotation feature on/off - * based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage - * @param {Object} event info - */ - receiveIframeMessage: function(event) { - - var data = (typeof event.data !== "string") ? event.data : JSON.parse(event.data); - - // does the origin sending the message match the current host? if not dev/null the request - if ((window.location.protocol != "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) { - return; - } - - if (data.codeToggle !== undefined) { - - var els, i; - - // if this is an overlay make sure it's active for the onclick event - codePattern.codeOverlayActive = false; - codePattern.codeEmbeddedActive = false; - - // see which flag to toggle based on if this is a styleguide or view-all page - if ((data.codeToggle == "on") && (document.getElementById("sg-patterns") !== null)) { - codePattern.codeEmbeddedActive = true; - } else if (data.codeToggle == "on") { - codePattern.codeOverlayActive = true; - } - - // if comments embedding is turned off make sure to hide the annotations div - if (!codePattern.codeEmbeddedActive && (document.getElementById("sg-patterns") !== null)) { - els = document.getElementsByClassName("sg-code"); - for (i = 0; i < els.length; i++) { - els[i].style.display = "none"; - } - } - - // if comments overlay is turned on add the has-comment class and pointer - if (codePattern.codeOverlayActive) { - - var obj = JSON.stringify({ "codeOverlay": "on", "lineage": lineage, "lineageR": lineageR, "patternPartial": patternPartial, "patternState": patternState, "cssEnabled": cssEnabled }); - parent.postMessage(obj,codePattern.targetOrigin); - - } else if (codePattern.codeEmbeddedActive) { - - // if code embedding is turned on simply display them - els = document.getElementsByClassName("sg-code"); - for (i = 0; i < els.length; ++i) { - els[i].style.display = "block"; - } - - } - - } - - } - -}; - -// add the onclick handlers to the elements that have an annotations -window.addEventListener("message", codePattern.receiveIframeMessage, false); - -// before unloading the iframe make sure any active overlay is turned off/closed -window.onbeforeunload = function() { - var obj = JSON.stringify({ "codeOverlay": "off" }); - parent.postMessage(obj,codePattern.targetOrigin); -}; - -// tell the parent iframe that keys were pressed - -// toggle the code panel -jwerty.key('ctrl+shift+c', function (e) { - var obj = JSON.stringify({ "keyPress": "ctrl+shift+c" }); - parent.postMessage(obj,codePattern.targetOrigin); - return false; -}); - -// when the code panel is open hijack cmd+a so that it only selects the code view -jwerty.key('cmd+a/ctrl+a', function (e) { - var obj = JSON.stringify({ "keyPress": "cmd+a" }); - parent.postMessage(obj,codePattern.targetOrigin); - return false; -}); - -// open the mustache panel -jwerty.key('ctrl+shift+u', function (e) { - var obj = JSON.stringify({ "keyPress": "ctrl+shift+u" }); - parent.postMessage(obj,codePattern.targetOrigin); - return false; -}); - -// open the html panel -jwerty.key('ctrl+shift+h', function (e) { - var obj = JSON.stringify({ "keyPress": "ctrl+shift+h" }); - parent.postMessage(obj,codePattern.targetOrigin); - return false; -}); - -// close the code panel if using escape -jwerty.key('esc', function (e) { - var obj = JSON.stringify({ "keyPress": "esc" }); - parent.postMessage(obj,codePattern.targetOrigin); - return false; -}); \ No newline at end of file diff --git a/core/styleguide/js/code-viewer.js b/core/styleguide/js/code-viewer.js deleted file mode 100644 index ede15941f..000000000 --- a/core/styleguide/js/code-viewer.js +++ /dev/null @@ -1,441 +0,0 @@ -/*! - * Code View Support for the Viewer - v0.1 - * - * Copyright (c) 2013 Brad Frost, http://bradfrostweb.com & Dave Olsen, http://dmolsen.com - * Licensed under the MIT license - * - */ - -var codeViewer = { - - // set up some defaults - codeActive: false, - tabActive: "e", - encoded: "", - mustache: "", - css: "", - ids: { "e": "#sg-code-title-html", "m": "#sg-code-title-mustache", "c": "#sg-code-title-css" }, - targetOrigin: (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host, - copyOnInit: false, - - /** - * add the onclick handler to the code link in the main nav - */ - onReady: function() { - - // not sure this is needed anymore... - $('body').addClass('code-ready'); - - $(window).resize(function() { - if(!codeViewer.codeActive) { - codeViewer.slideCode($('#sg-code-container').outerHeight()); - } - }); - - // add the onclick handler - $('#sg-t-code').click(function(e) { - - e.preventDefault(); - - // remove the class from the "eye" nav item - $('#sg-t-toggle').removeClass('active'); - - // if the code link in the main nav is active close the panel. otherwise open - if ($(this).hasClass('active')) { - codeViewer.closeCode(); - } else { - codeViewer.openCode(); - } - - }); - - // initialize the code viewer - codeViewer.codeContainerInit(); - - // load the query strings in case code view has to show by default - var queryStringVars = urlHandler.getRequestVars(); - if ((queryStringVars.view !== undefined) && ((queryStringVars.view === "code") || (queryStringVars.view === "c"))) { - codeViewer.copyOnInit = ((queryStringVars.copy !== undefined) && (queryStringVars.copy === "true")) ? true : false; - codeViewer.openCode(); - } - - }, - - /** - * decide on if the code panel should be open or closed - */ - toggleCode: function() { - - if (!codeViewer.codeActive) { - codeViewer.openCode(); - } else { - codeViewer.closeCode(); - } - - }, - - /** - * after clicking the code view link open the panel - */ - openCode: function() { - - // make sure the annotations overlay is off before showing code view - $('#sg-t-annotations').removeClass('active'); - annotationsViewer.commentsActive = false; - var obj = JSON.stringify({ "commentToggle": "off" }); - document.getElementById('sg-viewport').contentWindow.postMessage(obj,codeViewer.targetOrigin); - annotationsViewer.slideComment(999); - - // tell the iframe code view has been turned on - var obj = JSON.stringify({ "codeToggle": "on" }); - document.getElementById('sg-viewport').contentWindow.postMessage(obj,codeViewer.targetOrigin); - - // note it's turned on in the viewer - codeViewer.codeActive = true; - $('#sg-t-code').addClass('active'); - - }, - - /** - * after clicking the code view link close the panel - */ - closeCode: function() { - var obj = JSON.stringify({ "codeToggle": "off" }); - document.getElementById('sg-viewport').contentWindow.postMessage(obj,codeViewer.targetOrigin); - codeViewer.codeActive = false; - codeViewer.slideCode($('#sg-code-container').outerHeight()); - $('#sg-t-code').removeClass('active'); - }, - - /** - * add the basic mark-up and events for the code container - */ - codeContainerInit: function() { - - // the bulk of this template is in core/templates/index.mustache - if (document.getElementById("sg-code-container") === null) { - $('
').html($("#code-template").html()).appendTo('body').css('bottom',-$(document).outerHeight()); - setTimeout(function(){ $('#sg-code-container').addClass('anim-ready'); },50); //Add animation class once container is positioned out of frame - } - - // make sure the close button handles the click - $('body').delegate('#sg-code-close-btn','click',function() { - codeViewer.closeCode(); - return false; - }); - - // make sure the click events are handled on the HTML tab - $(codeViewer.ids["e"]).click(function() { - codeViewer.swapCode("e"); - }); - - // make sure the click events are handled on the Mustache tab - $(codeViewer.ids["m"]).click(function() { - codeViewer.swapCode("m"); - }); - - // make sure the click events are handled on the CSS tab - $(codeViewer.ids["c"]).click(function() { - codeViewer.swapCode("c"); - }); - - }, - - /** - * depending on what tab is clicked this swaps out the code container. makes sure prism highlight is added. - */ - swapCode: function(type) { - - codeViewer.clearSelection(); - var fill = ""; - var className = (type == "c") ? "css" : "markup"; - $("#sg-code-fill").removeClass().addClass("language-"+className); - if (type == "m") { - fill = codeViewer.mustache; - } else if (type == "e") { - fill = codeViewer.encoded; - } else if (type == "c") { - fill = codeViewer.css; - } - $("#sg-code-fill").html(fill).text(); - codeViewer.tabActive = type; - Prism.highlightElement(document.getElementById("sg-code-fill")); - $('.sg-code-title-active').removeClass('sg-code-title-active'); - $(codeViewer.ids[type]).toggleClass("sg-code-title-active"); - }, - - /** - * select the code where using cmd+a/ctrl+a - */ - selectCode: function() { - if (codeViewer.codeActive) { - selection = window.getSelection(); - range = document.createRange(); - range.selectNodeContents(document.getElementById("sg-code-fill")); - selection.removeAllRanges(); - selection.addRange(range); - } - }, - - /** - * clear any selection of code when swapping tabs or opening a new pattern - */ - clearSelection: function() { - if (codeViewer.codeActive) { - if (window.getSelection().empty) { - window.getSelection().empty(); - } else if (window.getSelection().removeAllRanges) { - window.getSelection().removeAllRanges(); - } - } - }, - - /** - * slides the panel - */ - slideCode: function(pos) { - $('#sg-code-container').css('bottom',-pos); - }, - - /** - * once the AJAX request for the encoded mark-up is finished this runs. - * if the encoded tab is the current active tab it adds the content to the default code container - */ - saveEncoded: function() { - codeViewer.encoded = this.responseText; - if (codeViewer.tabActive == "e") { - codeViewer.activateDefaultTab("e",this.responseText); - } - }, - - /** - * once the AJAX request for the mustache mark-up is finished this runs. - * if the mustache tab is the current active tab it adds the content to the default code container - */ - saveMustache: function() { - codeViewer.mustache = this.responseText; - if (codeViewer.tabActive == "m") { - codeViewer.activateDefaultTab("m",this.responseText); - } - }, - - /** - * once the AJAX request for the css mark-up is finished this runs. if this function is running then css has been enabled - * if the css tab is the current active tab it adds the content to the default code container - */ - saveCSS: function() { - $('#sg-code-title-css').css("display","block"); - codeViewer.css = this.responseText; - if (codeViewer.tabActive == "c") { - codeViewer.activateDefaultTab("c",this.responseText); - } - }, - - /** - * when loading the code view make sure the active tab is highlighted and filled in appropriately - */ - activateDefaultTab: function(type,code) { - var typeName = ""; - var className = (type == "c") ? "css" : "markup"; - if (type == "m") { - typeName = "mustache"; - } else if (type == "e") { - typeName = "html"; - } else if (type == "c") { - typeName = "css"; - } - $('.sg-code-title-active').removeClass('sg-code-title-active'); - $('#sg-code-title-'+typeName).addClass('sg-code-title-active'); - $("#sg-code-fill").removeClass().addClass("language-"+className); - $("#sg-code-fill").html(code).text(); - Prism.highlightElement(document.getElementById("sg-code-fill")); - if (codeViewer.copyOnInit) { - codeViewer.selectCode(); - codeViewer.copyOnInit = false; - } - }, - - /** - * when turning on or switching between patterns with code view on make sure we get - * the code from from the pattern via post message - */ - updateCode: function(lineage,lineageR,patternPartial,patternState,cssEnabled) { - - // clear any selections that might have been made - codeViewer.clearSelection(); - - // draw lineage - if (lineage.length !== 0) { - var lineageList = ""; - $("#sg-code-lineage").css("display","block"); - for (var i = 0; i < lineage.length; i++) { - lineageList += (i === 0) ? "" : ", "; - var cssClass = (lineage[i].lineageState != undefined) ? "sg-pattern-state "+lineage[i].lineageState : ""; - lineageList += ""+lineage[i].lineagePattern+""; - } - $("#sg-code-lineage-fill").html(lineageList); - } else { - $("#sg-code-lineage").css("display","none"); - } - - // draw reverse lineage - if (lineageR.length !== 0) { - var lineageRList = ""; - $("#sg-code-lineager").css("display","block"); - for (var i = 0; i < lineageR.length; i++) { - lineageRList += (i === 0) ? "" : ", "; - var cssClass = (lineageR[i].lineageState != undefined) ? "sg-pattern-state "+lineageR[i].lineageState : ""; - lineageRList += ""+lineageR[i].lineagePattern+""; - } - $("#sg-code-lineager-fill").html(lineageRList); - } else { - $("#sg-code-lineager").css("display","none"); - } - - // when clicking on a lineage item change the iframe source - $('#sg-code-lineage-fill a, #sg-code-lineager-fill a').on("click", function(e){ - e.preventDefault(); - $("#sg-code-loader").css("display","block"); - var obj = JSON.stringify({ "path": urlHandler.getFileName($(this).attr("data-patternpartial")) }); - document.getElementById("sg-viewport").contentWindow.postMessage(JSON.parse(obj),codeViewer.targetOrigin); - }); - - // show pattern state - if (patternState != "") { - $("#sg-code-patternstate").css("display","block"); - var patternStateItem = ""+patternState+""; - $("#sg-code-patternstate-fill").html(patternStateItem); - } else { - $("#sg-code-patternstate").css("display","none"); - } - - // fill in the name of the pattern - $('#sg-code-lineage-patternname, #sg-code-lineager-patternname, #sg-code-patternstate-patternname').html(patternPartial); - - // get the file name of the pattern so we can get the various editions of the code that can show in code view - var fileName = urlHandler.getFileName(patternPartial); - - // request the encoded markup version of the pattern - var e = new XMLHttpRequest(); - e.onload = this.saveEncoded; - e.open("GET", fileName.replace(/\.html/,".escaped.html") + "?" + (new Date()).getTime(), true); - e.send(); - - // request the mustache markup version of the pattern - var m = new XMLHttpRequest(); - m.onload = this.saveMustache; - m.open("GET", fileName.replace(/\.html/,".mustache") + "?" + (new Date()).getTime(), true); - m.send(); - - // if css is enabled request the css for the pattern - if (cssEnabled) { - var c = new XMLHttpRequest(); - c.onload = this.saveCSS; - c.open("GET", fileName.replace(/\.html/,".css") + "?" + (new Date()).getTime(), true); - c.send(); - } - - // move the code into view - codeViewer.slideCode(0); - - $("#sg-code-loader").css("display","none"); - - }, - - /** - * toggle the comment pop-up based on a user clicking on the pattern - * based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage - * @param {Object} event info - */ - receiveIframeMessage: function(event) { - - var data = (typeof event.data !== "string") ? event.data : JSON.parse(event.data); - - // does the origin sending the message match the current host? if not dev/null the request - if ((window.location.protocol !== "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) { - return; - } - - // switch based on stuff related to the postmessage - if (data.codeOverlay !== undefined) { - if (data.codeOverlay === "on") { - codeViewer.updateCode(data.lineage,data.lineageR,data.patternPartial,data.patternState,data.cssEnabled); - } else { - codeViewer.slideCode($('#sg-code-container').outerHeight()); - } - } else if (data.keyPress !== undefined) { - if (data.keyPress == 'ctrl+shift+c') { - codeViewer.toggleCode(); - return false; - } else if (data.keyPress == 'cmd+a') { - codeViewer.selectCode(); - return false; - } else if (data.keyPress == 'ctrl+shift+u') { - if (codeViewer.codeActive) { - codeViewer.swapCode("m"); - return false; - } - } else if (data.keyPress == 'ctrl+shift+y') { - if (codeViewer.codeActive) { - codeViewer.swapCode("e"); - return false; - } - } else if (data.keyPress == 'esc') { - if (codeViewer.codeActive) { - codeViewer.closeCode(); - return false; - } - } - } - - } - -}; - -// when the document is ready make the codeViewer ready -$(document).ready(function() { codeViewer.onReady(); }); -window.addEventListener("message", codeViewer.receiveIframeMessage, false); - -// make sure if a new pattern or view-all is loaded that comments are turned on as appropriate -$('#sg-viewport').load(function() { - if (codeViewer.codeActive) { - var obj = JSON.stringify({ "codeToggle": "on" }); - document.getElementById('sg-viewport').contentWindow.postMessage(obj,codeViewer.targetOrigin); - } -}); - -// toggle the code panel -jwerty.key('ctrl+shift+c', function (e) { - codeViewer.toggleCode(); - return false; -}); - -// when the code panel is open hijack cmd+a so that it only selects the code view -jwerty.key('cmd+a/ctrl+a', function (e) { - codeViewer.selectCode(); - return false; -}); - -// open the mustache panel -jwerty.key('ctrl+shift+u', function (e) { - if (codeViewer.codeActive) { - codeViewer.swapCode("m"); - return false; - } -}); - -// open the html panel -jwerty.key('ctrl+shift+y', function (e) { - if (codeViewer.codeActive) { - codeViewer.swapCode("e"); - return false; - } -}); - -// close the code panel if using escape -jwerty.key('esc', function (e) { - if (codeViewer.codeActive) { - codeViewer.closeCode(); - return false; - } -}); \ No newline at end of file diff --git a/core/styleguide/js/data-saver.js b/core/styleguide/js/data-saver.js deleted file mode 100644 index e354e1e79..000000000 --- a/core/styleguide/js/data-saver.js +++ /dev/null @@ -1,169 +0,0 @@ -/*! - * jQuery Cookie Plugin v1.3 - * https://github.com/carhartl/jquery-cookie - * - * Copyright 2011, Klaus Hartl - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://www.opensource.org/licenses/mit-license.php - * http://www.opensource.org/licenses/GPL-2.0 - */ -(function ($, document, undefined) { - - var pluses = /\+/g; - - function raw(s) { - return s; - } - - function decoded(s) { - return decodeURIComponent(s.replace(pluses, ' ')); - } - - var config = $.cookie = function (key, value, options) { - - // write - if (value !== undefined) { - options = $.extend({}, config.defaults, options); - - if (value === null) { - options.expires = -1; - } - - if (typeof options.expires === 'number') { - var days = options.expires, t = options.expires = new Date(); - t.setDate(t.getDate() + days); - } - - value = config.json ? JSON.stringify(value) : String(value); - - return (document.cookie = [ - encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), - options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE - options.path ? '; path=' + options.path : '', - options.domain ? '; domain=' + options.domain : '', - options.secure ? '; secure' : '' - ].join('')); - } - - // read - var decode = config.raw ? raw : decoded; - var cookies = document.cookie.split('; '); - for (var i = 0, l = cookies.length; i < l; i++) { - var parts = cookies[i].split('='); - if (decode(parts.shift()) === key) { - var cookie = decode(parts.join('=')); - return config.json ? JSON.parse(cookie) : cookie; - } - } - - return null; - }; - - config.defaults = {}; - - $.removeCookie = function (key, options) { - if ($.cookie(key) !== null) { - $.cookie(key, null, options); - return true; - } - return false; - }; - -})(jQuery, document); - -/*! - * Data Saver - v0.1 - * - * Copyright (c) 2013 Dave Olsen, http://dmolsen.com - * Licensed under the MIT license - */ - -var DataSaver = { - - // the name of the cookie to store the data in - cookieName: "patternlab", - - /** - * Add a given value to the cookie - * @param {String} the name of the key - * @param {String} the value - */ - addValue: function (name,val) { - var cookieVal = $.cookie(this.cookieName); - if ((cookieVal == null) || (cookieVal == "")) { - cookieVal = name+"~"+val; - } else { - cookieVal = cookieVal+"|"+name+"~"+val; - } - $.cookie(this.cookieName,cookieVal); - }, - - /** - * Update a value found in the cookie. If the key doesn't exist add the value - * @param {String} the name of the key - * @param {String} the value - */ - updateValue: function (name,val) { - if (this.findValue(name)) { - var updateCookieVals = ""; - var cookieVals = $.cookie(this.cookieName).split("|"); - for (var i = 0; i < cookieVals.length; i++) { - var fieldVals = cookieVals[i].split("~"); - if (fieldVals[0] == name) { - fieldVals[1] = val; - } - if (i > 0) { - updateCookieVals += "|"+fieldVals[0]+"~"+fieldVals[1]; - } else { - updateCookieVals += fieldVals[0]+"~"+fieldVals[1]; - } - } - $.cookie(this.cookieName,updateCookieVals); - } else { - this.addValue(name,val); - } - }, - - /** - * Remove the given key - * @param {String} the name of the key - */ - removeValue: function (name) { - var updateCookieVals = ""; - var cookieVals = $.cookie(this.cookieName).split("|"); - var k = 0; - for (var i = 0; i < cookieVals.length; i++) { - var fieldVals = cookieVals[i].split("~"); - if (fieldVals[0] != name) { - if (k == 0) { - updateCookieVals += fieldVals[0]+"~"+fieldVals[1]; - } else { - updateCookieVals += "|"+fieldVals[0]+"~"+fieldVals[1]; - } - k++; - } - } - $.cookie(this.cookieName,updateCookieVals); - }, - - /** - * Find the value using the given key - * @param {String} the name of the key - * - * @return {String} the value of the key or false if the value isn't found - */ - findValue: function (name) { - if ($.cookie(this.cookieName)) { - var cookieVals = $.cookie(this.cookieName).split("|"); - var k = 0; - for (var i = 0; i < cookieVals.length; i++) { - var fieldVals = cookieVals[i].split("~"); - if (fieldVals[0] == name) { - return fieldVals[1]; - } - } - } - return false; - } - -}; diff --git a/core/styleguide/js/pattern-finder.js b/core/styleguide/js/pattern-finder.js deleted file mode 100644 index f2290db12..000000000 --- a/core/styleguide/js/pattern-finder.js +++ /dev/null @@ -1,122 +0,0 @@ -/*! - * Pattern Finder - * - * Copyright (c) 2014 Dave Olsen, http://dmolsen.com - * Licensed under the MIT license - * - */ - -var patternFinder = { - - data: [], - active: false, - - init: function() { - - for (var patternType in patternPaths) { - if (patternPaths.hasOwnProperty(patternType)) { - for (var pattern in patternPaths[patternType]) { - var obj = {}; - obj.patternPartial = patternType+"-"+pattern; - obj.patternPath = patternPaths[patternType][pattern]; - this.data.push(obj); - } - } - } - - // instantiate the bloodhound suggestion engine - var patterns = new Bloodhound({ - datumTokenizer: function(d) { return Bloodhound.tokenizers.nonword(d.patternPartial); }, - queryTokenizer: Bloodhound.tokenizers.nonword, - limit: 10, - local: this.data - }); - - // initialize the bloodhound suggestion engine - patterns.initialize(); - - $('#sg-find .typeahead').typeahead({ highlight: true }, { - displayKey: 'patternPartial', - source: patterns.ttAdapter() - }).on('typeahead:selected', patternFinder.onAutocompleted).on('typeahead:autocompleted', patternFinder.onSelected); - - }, - - passPath: function(item) { - // update the iframe via the history api handler - patternFinder.closeFinder(); - var obj = JSON.stringify({ "path": urlHandler.getFileName(item.patternPartial) }); - document.getElementById("sg-viewport").contentWindow.postMessage(JSON.parse(obj), urlHandler.targetOrigin); - }, - - onSelected: function(e,item) { - patternFinder.passPath(item); - }, - - onAutocompleted: function(e,item) { - patternFinder.passPath(item); - }, - - toggleFinder: function() { - if (!patternFinder.active) { - patternFinder.openFinder(); - } else { - patternFinder.closeFinder(); - } - }, - - openFinder: function() { - patternFinder.active = true; - $('#sg-find .typeahead').val(""); - $("#sg-find").addClass('show-overflow'); - $('#sg-find .typeahead').focus(); - }, - - closeFinder: function() { - patternFinder.active = false; - $("#sg-find").removeClass('show-overflow'); - $('.sg-acc-handle, .sg-acc-panel').removeClass('active'); - $('#sg-find .typeahead').val(""); - }, - - receiveIframeMessage: function(event) { - - var data = (typeof event.data !== "string") ? event.data : JSON.parse(event.data); - - // does the origin sending the message match the current host? if not dev/null the request - if ((window.location.protocol !== "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) { - return; - } - - if (data.keyPress !== undefined) { - if (data.keyPress == 'ctrl+shift+f') { - patternFinder.toggleFinder(); - return false; - } - } - - } - -} - -patternFinder.init(); - -window.addEventListener("message", patternFinder.receiveIframeMessage, false); - -$('#sg-find .typeahead').focus(function() { - if (!patternFinder.active) { - patternFinder.openFinder(); - } -}); - -$('#sg-find .typeahead').blur(function() { - patternFinder.closeFinder(); -}); - -// jwerty stuff -// toggle the annotations panel -jwerty.key('ctrl+shift+f', function (e) { - $('.sg-find .sg-acc-handle, .sg-find .sg-acc-panel').addClass('active'); - patternFinder.toggleFinder(); - return false; -}); \ No newline at end of file diff --git a/core/styleguide/js/postmessage.js b/core/styleguide/js/postmessage.js deleted file mode 100644 index 76e482d3f..000000000 --- a/core/styleguide/js/postmessage.js +++ /dev/null @@ -1,127 +0,0 @@ -/*! - * Basic postMessage Support - v0.1 - * - * Copyright (c) 2013-2014 Dave Olsen, http://dmolsen.com - * Licensed under the MIT license - * - * Handles the postMessage stuff in the pattern, view-all, and style guide templates. - * - */ - -// alert the iframe parent that the pattern has loaded assuming this view was loaded in an iframe -if (self != top) { - - // handle the options that could be sent to the parent window - // - all get path - // - pattern & view all get a pattern partial, styleguide gets all - // - pattern shares lineage - var path = window.location.toString(); - var parts = path.split("?"); - var options = { "path": parts[0] }; - options.patternpartial = (patternPartial !== "") ? patternPartial : "all"; - if (lineage !== "") { - options.lineage = lineage; - } - - var targetOrigin = (window.location.protocol == "file:") ? "*" : window.location.protocol+"//"+window.location.host; - parent.postMessage(options, targetOrigin); - - // find all links and add an onclick handler for replacing the iframe address so the history works - var aTags = document.getElementsByTagName('a'); - for (var i = 0; i < aTags.length; i++) { - aTags[i].onclick = function(e) { - e.preventDefault(); - var href = this.getAttribute("href"); - if (href != "#") { - window.location.replace(href); - } - }; - } - - // bind the keyboard shortcuts for various viewport resizings + pattern search - var keys = [ "s", "m", "l", "d", "h", "f" ]; - for (var i = 0; i < keys.length; i++) { - jwerty.key('ctrl+shift+'+keys[i], function (k,t) { - return function(e) { - parent.postMessage({ "keyPress": "ctrl+shift+"+k },t); - return false; - } - }(keys[i],targetOrigin)); - } - - // bind the keyboard shortcuts for mqs - var i = 0; - while (i < 10) { - jwerty.key('ctrl+shift+'+i, function (k,t) { - return function(e) { - var targetOrigin = (window.location.protocol == "file:") ? "*" : window.location.protocol+"//"+window.location.host; - parent.postMessage({ "keyPress": "ctrl+shift+"+k },t); - return false; - } - }(i,targetOrigin)); - i++; - } - -} - -// if there are clicks on the iframe make sure the nav in the iframe parent closes -var body = document.getElementsByTagName('body'); -body[0].onclick = function() { - var targetOrigin = (window.location.protocol == "file:") ? "*" : window.location.protocol+"//"+window.location.host; - parent.postMessage({ "bodyclick": "bodyclick" },targetOrigin); -}; - -// watch the iframe source so that it can be sent back to everyone else. -function receiveIframeMessage(event) { - - var path; - var data = (typeof event.data !== "string") ? event.data : JSON.parse(event.data); - - // does the origin sending the message match the current host? if not dev/null the request - if ((window.location.protocol != "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) { - return; - } - - // see if it got a path to replace - if (data.path !== undefined) { - - if (patternPartial !== "") { - - if(baseurl){ - // create main site url - var siteurl = window.location.protocol+"//"+window.location.host; - // add a trailing slash if it doesn't exist - siteurl = siteurl.replace(/\/?$/, '/'); - // remove a begining slash for baseurl if it exists - baseurl = baseurl.replace(/^\//, ''); - // connect baseurl to siteurl - siteurl = siteurl + baseurl; - // add a trailing slash if it doesn't exist - siteurl = siteurl.replace(/\/?$/, '/'); - // build our final path - path = siteurl+data.path+'?'+Date.now(); - } else { - // handle patterns and the view all page - var re = /patterns\/(.*)$/; - path = window.location.protocol+"//"+window.location.host+window.location.pathname.replace(re,'')+data.path+'?'+Date.now(); - } - - window.location.replace(path); - - } else { - - // handle the style guide - path = window.location.protocol+"//"+window.location.host+window.location.pathname.replace("styleguide\/html\/styleguide.html","")+data.path+'?'+Date.now(); - window.location.replace(path); - - } - - } else if (data.reload !== undefined) { - - // reload the location if there was a message to do so - window.location.reload(); - - } - -} -window.addEventListener("message", receiveIframeMessage, false); \ No newline at end of file diff --git a/core/styleguide/js/styleguide.js b/core/styleguide/js/styleguide.js deleted file mode 100644 index ce83f10d5..000000000 --- a/core/styleguide/js/styleguide.js +++ /dev/null @@ -1,602 +0,0 @@ -(function(w){ - - var sw = document.body.clientWidth, //Viewport Width - sh = $(document).height(), //Viewport Height - minViewportWidth = 240, //Minimum Size for Viewport - maxViewportWidth = 2600, //Maxiumum Size for Viewport - viewportResizeHandleWidth = 14, //Width of the viewport drag-to-resize handle - $sgViewport = $('#sg-viewport'), //Viewport element - $sizePx = $('.sg-size-px'), //Px size input element in toolbar - $sizeEms = $('.sg-size-em'), //Em size input element in toolbar - $bodySize = parseInt($('body').css('font-size')), //Body size of the document - $headerHeight = $('.sg-header').height(), - discoID = false, - discoMode = false, - fullMode = true, - hayMode = false; - - $(w).resize(function(){ //Update dimensions on resize - sw = document.body.clientWidth; - sh = $(document).height(); - - setAccordionHeight(); - - if(fullMode === true) { - sizeiframe(sw, false); - }; - - }); - - /* Pattern Lab accordion dropdown */ - // Accordion dropdown - $('.sg-acc-handle').on("click", function(e){ - e.preventDefault(); - - var $this = $(this), - $panel = $this.next('.sg-acc-panel'), - subnav = $this.parent().parent().hasClass('sg-acc-panel'); - - //Close other panels if link isn't a subnavigation item - if (!subnav) { - $('.sg-acc-handle').not($this).removeClass('active'); - $('.sg-acc-panel').not($panel).removeClass('active'); - } - - //Activate selected panel - $this.toggleClass('active'); - $panel.toggleClass('active'); - setAccordionHeight(); - }); - - //Accordion Height - function setAccordionHeight() { - var $activeAccordion = $('.sg-acc-panel.active').first(), - accordionHeight = $activeAccordion.height(), - availableHeight = sh-$headerHeight; //Screen height minus the height of the header - - $activeAccordion.height(availableHeight); //Set height of accordion to the available height - } - - $('.sg-nav-toggle').on("click", function(e){ - e.preventDefault(); - $('.sg-nav-container').toggleClass('active'); - }); - - //"View (containing clean, code, raw, etc options) Trigger - $('#sg-t-toggle').on("click", function(e){ - e.preventDefault(); - $(this).parents('ul').toggleClass('active'); - }); - - //Size Trigger - $('#sg-size-toggle').on("click", function(e){ - e.preventDefault(); - $(this).parents('ul').toggleClass('active'); - }); - - //Phase View Events - $('.sg-size[data-size]').on("click", function(e){ - e.preventDefault(); - killDisco(); - killHay(); - fullMode = false; - - var val = $(this).attr('data-size'); - - if (val.indexOf('px') > -1) { - $bodySize = 1; - } - - val = val.replace(/[^\d.-]/g,'') - sizeiframe(Math.floor(val*$bodySize)); - }); - - //Size View Events - - // handle small button - function goSmall() { - killDisco(); - killHay(); - fullMode = false; - sizeiframe(getRandom(minViewportWidth,500)); - } - - $('#sg-size-s').on("click", function(e){ - e.preventDefault(); - goSmall(); - }); - - jwerty.key('ctrl+shift+s', function(e) { - goSmall(); - return false; - }); - - // handle medium button - function goMedium() { - killDisco(); - killHay(); - fullMode = false; - sizeiframe(getRandom(500,800)); - } - - $('#sg-size-m').on("click", function(e){ - e.preventDefault(); - goMedium(); - }); - - jwerty.key('ctrl+shift+m', function(e) { - goMedium(); - return false; - }); - - // handle large button - function goLarge() { - killDisco(); - killHay(); - fullMode = false; - sizeiframe(getRandom(800,1200)); - } - - $('#sg-size-l').on("click", function(e){ - e.preventDefault(); - goLarge(); - }); - - jwerty.key('ctrl+shift+l', function(e) { - goLarge(); - return false; - }); - - //Click Full Width Button - $('#sg-size-full').on("click", function(e){ //Resets - e.preventDefault(); - killDisco(); - killHay(); - fullMode = true; - sizeiframe(sw); - }); - - //Click Random Size Button - $('#sg-size-random').on("click", function(e){ - e.preventDefault(); - killDisco(); - killHay(); - sizeiframe(getRandom(minViewportWidth,sw)); - }); - - //Click for Disco Mode, which resizes the viewport randomly - $('#sg-size-disco').on("click", function(e){ - e.preventDefault(); - killHay(); - fullMode = false; - - if (discoMode) { - killDisco(); - - } else { - startDisco(); - } - }); - - /* Disco Mode */ - function disco() { - sizeiframe(getRandom(minViewportWidth,sw)); - } - - function killDisco() { - discoMode = false; - clearInterval(discoID); - discoID = false; - } - - function startDisco() { - killHay(); - discoMode = true; - discoID = setInterval(disco, 800); - } - - jwerty.key('ctrl+shift+d', function(e) { - if (!discoMode) { - startDisco(); - } else { - killDisco(); - } - return false; - }); - - //Stephen Hay Mode - "Start with the small screen first, then expand until it looks like shit. Time for a breakpoint!" - $('#sg-size-hay').on("click", function(e){ - e.preventDefault(); - killDisco(); - if (hayMode) { - killHay(); - } else { - startHay(); - } - }); - - //Stop Hay! Mode - function killHay() { - var currentWidth = $sgViewport.width(); - hayMode = false; - $sgViewport.removeClass('hay-mode'); - $('#sg-gen-container').removeClass('hay-mode'); - sizeiframe(Math.floor(currentWidth)); - } - - // start Hay! mode - function startHay() { - killDisco(); - hayMode = true; - $('#sg-gen-container').removeClass("vp-animate").width(minViewportWidth+viewportResizeHandleWidth); - $sgViewport.removeClass("vp-animate").width(minViewportWidth); - - var timeoutID = window.setTimeout(function(){ - $('#sg-gen-container').addClass('hay-mode').width(maxViewportWidth+viewportResizeHandleWidth); - $sgViewport.addClass('hay-mode').width(maxViewportWidth); - - setInterval(function(){ var vpSize = $sgViewport.width(); updateSizeReading(vpSize); },100); - }, 200); - } - - // start hay from a keyboard shortcut - jwerty.key('ctrl+shift+h', function(e) { - if (hayMode) { - killHay(); - } else { - startHay(); - } - return false; - }); - - //Pixel input - $sizePx.on('keydown', function(e){ - var val = Math.floor($(this).val()); - - if(e.keyCode === 38) { //If the up arrow key is hit - val++; - sizeiframe(val,false); - } else if(e.keyCode === 40) { //If the down arrow key is hit - val--; - sizeiframe(val,false); - } else if(e.keyCode === 13) { //If the Enter key is hit - e.preventDefault(); - sizeiframe(val); //Size Iframe to value of text box - $(this).blur(); - } - }); - - $sizePx.on('keyup', function(){ - var val = Math.floor($(this).val()); - updateSizeReading(val,'px','updateEmInput'); - }); - - //Em input - $sizeEms.on('keydown', function(e){ - var val = parseFloat($(this).val()); - - if(e.keyCode === 38) { //If the up arrow key is hit - val++; - sizeiframe(Math.floor(val*$bodySize),false); - } else if(e.keyCode === 40) { //If the down arrow key is hit - val--; - sizeiframe(Math.floor(val*$bodySize),false); - } else if(e.keyCode === 13) { //If the Enter key is hit - e.preventDefault(); - sizeiframe(Math.floor(val*$bodySize)); //Size Iframe to value of text box - } - }); - - $sizeEms.on('keyup', function(){ - var val = parseFloat($(this).val()); - updateSizeReading(val,'em','updatePxInput'); - }); - - // set 0 to 320px as a default - jwerty.key('ctrl+shift+0', function(e) { - e.preventDefault(); - sizeiframe(320,true); - return false; - }); - - // handle the MQ click - var mqs = []; - $('#sg-mq a').each(function(i) { - - mqs.push($(this).html()); - - // bind the click - $(this).on("click", function(i,k) { - return function(e) { - e.preventDefault(); - var val = $(k).html(); - var type = (val.indexOf("px") !== -1) ? "px" : "em"; - val = val.replace(type,""); - var width = (type === "px") ? val*1 : val*$bodySize; - sizeiframe(width,true); - } - }(i,this)); - - // bind the keyboard shortcut. can't use cmd on a mac because 3 & 4 are for screenshots - jwerty.key('ctrl+shift+'+(i+1), function (k) { - return function(e) { - var val = $(k).html(); - var type = (val.indexOf("px") !== -1) ? "px" : "em"; - val = val.replace(type,""); - var width = (type === "px") ? val*1 : val*$bodySize; - sizeiframe(width,true); - return false; - } - }(this)); - - }); - - //Resize the viewport - //'size' is the target size of the viewport - //'animate' is a boolean for switching the CSS animation on or off. 'animate' is true by default, but can be set to false for things like nudging and dragging - function sizeiframe(size,animate) { - var theSize; - - if(size>maxViewportWidth) { //If the entered size is larger than the max allowed viewport size, cap value at max vp size - theSize = maxViewportWidth; - } else if(size