├── .github ├── dependabot.yml └── workflows │ └── nodejs.yml ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── bin └── pd-fileutils ├── browser.js ├── demos ├── css │ ├── randomDrone.less │ └── reset.css ├── img │ ├── play.png │ ├── play.svg │ ├── reload.png │ ├── reload.svg │ ├── stop.png │ └── stop.svg ├── js │ ├── d3.v3.min.js │ ├── jquery.knob.js │ ├── jquery.min.js │ ├── less-1.3.3.min.js │ ├── pd-fileutils-latest.js │ ├── underscore-min.js │ └── webpd-latest.js └── randomDrone.html ├── dist └── pd-fileutils-latest.js ├── index.js ├── lib ├── Patch.js ├── pd-rendering.js ├── svg-default-style.css └── svg-rendering.js ├── package-lock.json ├── package.json ├── test ├── Patch-test.js ├── parsing-test.js ├── patches │ ├── arrays.pd │ ├── embedded-in-a-page.hbs │ ├── graphs.pd │ ├── node-elems.pd │ ├── object-size-pd-vanilla.pd │ ├── simple.pd │ ├── special-chars.pd │ ├── subpatches.pd │ ├── tables.pd │ └── tall-and-wide-patch.pd ├── pd-rendering-test.js ├── rendered │ └── .gitignore └── svg-rendering-test.js └── webpack.config.js /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | 13 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: pd-fileutils 2 | 3 | on: 4 | push: 5 | branches: [ master, develop ] 6 | pull_request: 7 | branches: [ develop ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [12.x, 14.x, 15.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v2 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - name: Reconfigure git to use HTTP authentication 25 | run: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ 26 | - name: Install dependencies 27 | run: npm ci 28 | - run: npm run build --if-present 29 | - run: npm test 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 8 4 | - 10 5 | before_script: 6 | - npm install -g grunt-cli 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2015 Sébastien Piquemal 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **⚠️⚠️ DEPRECATED ⚠️⚠️** 2 | 3 | *The parser has moved there :* [https://github.com/sebpiq/WebPd_pd-parser](https://github.com/sebpiq/WebPd_pd-parser) 4 | It should be in pretty good shape, is developped actively, and should work out of the box. 5 | 6 | *The renderer has moved there :* [https://github.com/sebpiq/WebPd_pd-renderer](https://github.com/sebpiq/WebPd_pd-renderer) 7 | I haven't touched its code in a long while because I have been focusing on [WebPd](https://github.com/sebpiq/WebPd), and the renderer is not needed for it. 8 | It should be easy enough to pick back up though, although that might require a bit of work. 9 | 10 | Contributions are welcome for both libraries above. 11 | 12 | 13 | Pure Data file utilities 14 | ========================== 15 | 16 | [![Build Status](https://github.com/sebpiq/pd-fileutils/actions/workflows/nodejs.yml/badge.svg)](https://github.com/sebpiq/pd-fileutils/actions) 17 | 18 | This library is a set of tools for handling pure data files. 19 | 20 | `pd-fileutils` allows you to parse Pd files to a JavaScript object which is easy to modify. Of course, you can also create a patch from scratch. A patch can then be rendered to `pd` format, or to `SVG` if you want an image of it. 21 | 22 | Demos 23 | ====== 24 | - [Random drone generator](http://sebpiq.github.com/pd-fileutils/demos/randomDrone.html) : generate random droning patches (you know ... robot sounds), listen to them online, and download the pd file if you like it. 25 | 26 | 27 | Usage in the browser 28 | ====================== 29 | 30 | First download the latest (or latest stable) browser build from [`dist/`](https://github.com/sebpiq/pd-fileutils/tree/master/dist) and include it in your page : 31 | 32 | ```html 33 | 34 | ``` 35 | 36 | Then you can use `pd-fileutils` : 37 | 38 | ```html 39 |
40 | 45 | ``` 46 | 47 | Usage on node.js 48 | ================== 49 | 50 | Installation 51 | ------------- 52 | 53 | Obviously, you will need [node.js](http://nodejs.org/). 54 | 55 | Installation is easier with the node package manager [npm](https://npmjs.org/) : 56 | 57 | ``` 58 | npm install pd-fileutils 59 | ``` 60 | 61 | To install the command-line tool globally, you might want to run `npm` with the `-g` option. Note that in this case you might need admin rights : 62 | 63 | ``` 64 | npm install -g pd-fileutils 65 | ``` 66 | 67 | 68 | Command-line tool 69 | ------------------ 70 | 71 | At the moment, the only thing you can do is render `.pd` files to `.svg`, for example : 72 | 73 | ``` 74 | pd-fileutils myPatch.pd > myPatch.svg 75 | ``` 76 | 77 | 78 | API documentation 79 | ------------------- 80 | 81 | ### Patch objects 82 | 83 | `pd-fileutils` deals with JavaScript objects representing patches. 84 | 85 | #### Specification 86 | 87 | Patch object : 88 | 89 | ``` 90 | { 91 | nodes: [, ..., ], 92 | connections: [, ..., ], 93 | args: [, ..., ], 94 | layout: {: }, 95 | } 96 | ``` 97 | 98 | Where `` is with format : 99 | 100 | ``` 101 | { 102 | id: , 103 | proto: , 104 | args: [, ..., ], 105 | layout: {: }, 106 | data: [, ..., ], 107 | subpatch: 108 | } 109 | ``` 110 | 111 | - `layout` : a map containing all the layout properties of the object/patch. 112 | - `args` : an array of the creation arguments of the object/patch. Those can be only strings or numbers. 113 | - `data` : *[only for tables]* a list of numbers. 114 | - `subpatch` : *[only for subpatches]* contains the whole subpatch's graph. 115 | 116 | 117 | And `` : 118 | 119 | ``` 120 | { 121 | source: { 122 | id: , 123 | port: 124 | }, 125 | sink: { 126 | id: , 127 | port: 128 | } 129 | } 130 | ``` 131 | 132 | 133 | ### parse(pdFile) 134 | 135 | Parses the string `pdFile` to a patch object. Example usage on `Node.js` : 136 | 137 | ```javascript 138 | var pdfu = require('pd-fileutils') 139 | , fs = require('fs') 140 | , patchStr, patch 141 | 142 | // Read the file 143 | patchStr = fs.readFileSync('./simple.pd').toString() 144 | 145 | // Parse the read file 146 | patch = pdfu.parse(patchStr) 147 | ``` 148 | 149 | ### renderPd(patch) 150 | 151 | Renders the `patch` object to a string in the Pd file format. 152 | 153 | 154 | ### renderSvg(patch) 155 | 156 | Renders the `patch` object to a string in SVG format 157 | 158 | 159 | Contributions 160 | =============== 161 | 162 | Are most welcome. There is still a lot of work to do there. Fork the project, make your changes and send me a pull request. 163 | 164 | 165 | Running the tests 166 | ------------------ 167 | 168 | Running tests require `mocha`. Run with : 169 | 170 | `npm test` 171 | 172 | 173 | Building from source to dist/ 174 | ------------------------------- 175 | 176 | `npm run build` 177 | 178 | 179 | History 180 | ======== 181 | 182 | 0.4 183 | ------ 184 | 185 | - update all dependencies + move to webpack 186 | 187 | 188 | 0.3.4 189 | ------ 190 | 191 | - parsing handles infos after the comma on a object definition line 192 | 193 | 194 | 0.3 195 | ---- 196 | 197 | - parsing separates layout from model data 198 | - SVG rendering of all controls 199 | - basic pd rendering 200 | 201 | 0.2 202 | ---- 203 | 204 | - SVG rendering + command-line tool 205 | - parsing supports most of the format 206 | 207 | 0.1 208 | ---- 209 | 210 | - basic parsing 211 | 212 | -------------------------------------------------------------------------------- /bin/pd-fileutils: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var pdFileUtils = require('../index') 4 | , args = process.argv.slice(2) 5 | , fs = require('fs') 6 | , path = require('path') 7 | 8 | if (!args[0]) console.error('usage : pd-fileutils > ') 9 | else { 10 | var infile = path.resolve(process.cwd(), args[0]) 11 | fs.readFile(infile, function (err, data) { 12 | if (err) throw err; 13 | var patch = pdFileUtils.parse(data.toString()) 14 | process.stdout.write(pdFileUtils.renderSvg(patch)) 15 | }) 16 | } 17 | 18 | 19 | -------------------------------------------------------------------------------- /browser.js: -------------------------------------------------------------------------------- 1 | var svgRendering = require('./lib/svg-rendering') 2 | 3 | // Set default css style for rendering 4 | svgRendering.defaults.style = require('./lib/svg-default-style.css') 5 | 6 | exports.parse = require('pd-fileutils.parser').parse 7 | exports.renderSvg = svgRendering.render 8 | exports.renderPd = require('./lib/pd-rendering').render 9 | exports.Patch = require('./lib/Patch') 10 | window.pdfu = exports 11 | -------------------------------------------------------------------------------- /demos/css/randomDrone.less: -------------------------------------------------------------------------------- 1 | /* http://colorschemedesigner.com/#3H51Ew0w0w0w0 */ 2 | 3 | html, body { 4 | font-size: 20px; 5 | height: 100%; 6 | width: 100%; 7 | overflow: hidden; 8 | color: #052C6E; 9 | } 10 | 11 | p { 12 | margin-bottom: 1em; 13 | line-height: 1.4em; 14 | } 15 | 16 | h2 { 17 | font-size: 150%; 18 | font-weight: bold; 19 | margin-bottom: 1em; 20 | } 21 | 22 | h1 { 23 | text-transform: uppercase; 24 | font-size: 180%; 25 | margin-bottom: 1em; 26 | } 27 | 28 | b { 29 | font-weight: bold; 30 | } 31 | 32 | i { 33 | font-style: italic; 34 | } 35 | 36 | ol { 37 | list-style: decimal; 38 | } 39 | 40 | #title { 41 | display: inline-block; 42 | margin-right: 0.1em; 43 | } 44 | 45 | #nav { 46 | font-family: 'Paytone One', sans-serif; 47 | height: 100%; 48 | min-width: 35%; 49 | position: absolute; 50 | top: 0; 51 | right: 0; 52 | padding-right: 1em; 53 | padding-top: 0.2em; 54 | font-size: 200%; 55 | 56 | li { 57 | text-align: right; 58 | margin-bottom: 1em; 59 | cursor: pointer; 60 | color: #052C6E; 61 | } 62 | 63 | } 64 | 65 | 66 | #tabs { 67 | font-family: 'Tauri', sans-serif; 68 | 69 | .tab { 70 | font-size: 110%; 71 | color: lighten(#6A92D4, 50%); 72 | display: none; 73 | position: absolute; 74 | top: 0; 75 | left: 0; 76 | width: 100%; 77 | height: 100%; 78 | background-color: rgba(0, 0, 0, 0.9); 79 | &.active {display: block;} 80 | 81 | .innerTab { 82 | padding: 5%; 83 | width: 90%; 84 | height: 90%; 85 | overflow: auto; 86 | } 87 | 88 | a, a:visited, a:hover { 89 | text-decoration: none; 90 | color: #6A92D4; 91 | } 92 | 93 | a.closeTab { 94 | font-family: 'Paytone One', sans-serif; 95 | position: absolute; 96 | top: 1%; 97 | right: 1%; 98 | cursor: pointer; 99 | font-size: 140%; 100 | } 101 | } 102 | 103 | #pd { 104 | .innerTab { 105 | height: 80%; 106 | textarea { 107 | width: 50%; 108 | height: 100%; 109 | display: block; 110 | } 111 | } 112 | } 113 | 114 | #configuration { 115 | .paramContainer { 116 | display: inline-block; 117 | min-width: 12em; 118 | text-align: center; 119 | } 120 | #genConfig { 121 | margin-bottom: 3em; 122 | } 123 | } 124 | 125 | #about { 126 | #copyright { 127 | font-size: 80%; 128 | } 129 | } 130 | } 131 | 132 | #controls { 133 | display: inline-block; 134 | position: relative; 135 | bottom: 0.2em; 136 | 137 | button { 138 | font-family: 'Paytone One', sans-serif; 139 | background: transparent no-repeat url('../img/reload.png') center center; 140 | background-size: auto 100%; 141 | border: none; 142 | width: 100px; 143 | height: 100px; 144 | cursor: pointer; 145 | display: inline-block; 146 | 147 | span { 148 | border-radius: 1em; 149 | background-color: rgba(255, 255, 255, 0.8); 150 | padding: 0.1em 0.2em 0.3em 0.1em; 151 | color: #052C6E; 152 | display: block; 153 | width: 100%; 154 | visibility: hidden; 155 | font-size: 120%; 156 | } 157 | } 158 | 159 | button:hover { 160 | span { 161 | visibility: visible; 162 | } 163 | } 164 | 165 | #getRandomPatch { 166 | 167 | } 168 | 169 | #startSound { 170 | background-image: url('../img/play.png'); 171 | span { 172 | position: relative; 173 | right: 1em; 174 | } 175 | } 176 | 177 | #stopSound { 178 | background-image: url('../img/stop.png'); 179 | display: none; 180 | background-size: auto 90%; 181 | } 182 | 183 | } 184 | 185 | #social { 186 | position: absolute; 187 | bottom: 1em; 188 | right: 0; 189 | #twitter, #github { 190 | float: right; 191 | } 192 | } 193 | 194 | #svg svg { 195 | position: relative; 196 | } -------------------------------------------------------------------------------- /demos/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | -------------------------------------------------------------------------------- /demos/img/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebpiq/pd-fileutils/020998dddb7420bffff07be3a3215f3909a4e0fa/demos/img/play.png -------------------------------------------------------------------------------- /demos/img/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 54 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /demos/img/reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebpiq/pd-fileutils/020998dddb7420bffff07be3a3215f3909a4e0fa/demos/img/reload.png -------------------------------------------------------------------------------- /demos/img/reload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 54 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /demos/img/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebpiq/pd-fileutils/020998dddb7420bffff07be3a3215f3909a4e0fa/demos/img/stop.png -------------------------------------------------------------------------------- /demos/img/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 40 | 41 | 43 | image/svg+xml 44 | 46 | 47 | 48 | 49 | 50 | 54 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /demos/js/jquery.knob.js: -------------------------------------------------------------------------------- 1 | /*!jQuery Knob*/ 2 | /** 3 | * Downward compatible, touchable dial 4 | * 5 | * Version: 1.2.0 (15/07/2012) 6 | * Requires: jQuery v1.7+ 7 | * 8 | * Copyright (c) 2012 Anthony Terrien 9 | * Under MIT and GPL licenses: 10 | * http://www.opensource.org/licenses/mit-license.php 11 | * http://www.gnu.org/licenses/gpl.html 12 | * 13 | * Thanks to vor, eskimoblood, spiffistan, FabrizioC 14 | */ 15 | (function($) { 16 | 17 | /** 18 | * Kontrol library 19 | */ 20 | "use strict"; 21 | 22 | /** 23 | * Definition of globals and core 24 | */ 25 | var k = {}, // kontrol 26 | max = Math.max, 27 | min = Math.min; 28 | 29 | k.c = {}; 30 | k.c.d = $(document); 31 | k.c.t = function (e) { 32 | return e.originalEvent.touches.length - 1; 33 | }; 34 | 35 | /** 36 | * Kontrol Object 37 | * 38 | * Definition of an abstract UI control 39 | * 40 | * Each concrete component must call this one. 41 | * 42 | * k.o.call(this); 43 | * 44 | */ 45 | k.o = function () { 46 | var s = this; 47 | 48 | this.o = null; // array of options 49 | this.$ = null; // jQuery wrapped element 50 | this.i = null; // mixed HTMLInputElement or array of HTMLInputElement 51 | this.g = null; // 2D graphics context for 'pre-rendering' 52 | this.v = null; // value ; mixed array or integer 53 | this.cv = null; // change value ; not commited value 54 | this.x = 0; // canvas x position 55 | this.y = 0; // canvas y position 56 | this.$c = null; // jQuery canvas element 57 | this.c = null; // rendered canvas context 58 | this.t = 0; // touches index 59 | this.isInit = false; 60 | this.fgColor = null; // main color 61 | this.pColor = null; // previous color 62 | this.dH = null; // draw hook 63 | this.cH = null; // change hook 64 | this.eH = null; // cancel hook 65 | this.rH = null; // release hook 66 | 67 | this.run = function () { 68 | var cf = function (e, conf) { 69 | var k; 70 | for (k in conf) { 71 | s.o[k] = conf[k]; 72 | } 73 | s.init(); 74 | s._configure() 75 | ._draw(); 76 | }; 77 | 78 | if(this.$.data('kontroled')) return; 79 | this.$.data('kontroled', true); 80 | 81 | this.extend(); 82 | this.o = $.extend( 83 | { 84 | // Config 85 | min : this.$.data('min') || 0, 86 | max : this.$.data('max') || 100, 87 | stopper : true, 88 | readOnly : this.$.data('readonly'), 89 | 90 | // UI 91 | cursor : (this.$.data('cursor') === true && 30) 92 | || this.$.data('cursor') 93 | || 0, 94 | thickness : this.$.data('thickness') || 0.35, 95 | lineCap : this.$.data('linecap') || 'butt', 96 | width : this.$.data('width') || 200, 97 | height : this.$.data('height') || 200, 98 | displayInput : this.$.data('displayinput') == null || this.$.data('displayinput'), 99 | displayPrevious : this.$.data('displayprevious'), 100 | fgColor : this.$.data('fgcolor') || '#87CEEB', 101 | inputColor: this.$.data('inputcolor') || this.$.data('fgcolor') || '#87CEEB', 102 | inline : false, 103 | step : this.$.data('step') || 1, 104 | 105 | // Hooks 106 | draw : null, // function () {} 107 | change : null, // function (value) {} 108 | cancel : null, // function () {} 109 | release : null // function (value) {} 110 | }, this.o 111 | ); 112 | 113 | // routing value 114 | if(this.$.is('fieldset')) { 115 | 116 | // fieldset = array of integer 117 | this.v = {}; 118 | this.i = this.$.find('input') 119 | this.i.each(function(k) { 120 | var $this = $(this); 121 | s.i[k] = $this; 122 | s.v[k] = $this.val(); 123 | 124 | $this.bind( 125 | 'change' 126 | , function () { 127 | var val = {}; 128 | val[k] = $this.val(); 129 | s.val(val); 130 | } 131 | ); 132 | }); 133 | this.$.find('legend').remove(); 134 | 135 | } else { 136 | // input = integer 137 | this.i = this.$; 138 | this.v = this.$.val(); 139 | (this.v == '') && (this.v = this.o.min); 140 | 141 | this.$.bind( 142 | 'change' 143 | , function () { 144 | s.val(s._validate(s.$.val())); 145 | } 146 | ); 147 | } 148 | 149 | (!this.o.displayInput) && this.$.hide(); 150 | 151 | this.$c = $(''); 154 | this.c = this.$c[0].getContext("2d"); 155 | 156 | this.$ 157 | .wrap($('
')) 160 | .before(this.$c); 161 | 162 | if (this.v instanceof Object) { 163 | this.cv = {}; 164 | this.copy(this.v, this.cv); 165 | } else { 166 | this.cv = this.v; 167 | } 168 | 169 | this.$ 170 | .bind("configure", cf) 171 | .parent() 172 | .bind("configure", cf); 173 | 174 | this._listen() 175 | ._configure() 176 | ._xy() 177 | .init(); 178 | 179 | this.isInit = true; 180 | 181 | this._draw(); 182 | 183 | return this; 184 | }; 185 | 186 | this._draw = function () { 187 | 188 | // canvas pre-rendering 189 | var d = true, 190 | c = document.createElement('canvas'); 191 | 192 | c.width = s.o.width; 193 | c.height = s.o.height; 194 | s.g = c.getContext('2d'); 195 | 196 | s.clear(); 197 | 198 | s.dH 199 | && (d = s.dH()); 200 | 201 | (d !== false) && s.draw(); 202 | 203 | s.c.drawImage(c, 0, 0); 204 | c = null; 205 | }; 206 | 207 | this._touch = function (e) { 208 | 209 | var touchMove = function (e) { 210 | 211 | var v = s.xy2val( 212 | e.originalEvent.touches[s.t].pageX, 213 | e.originalEvent.touches[s.t].pageY 214 | ); 215 | 216 | if (v == s.cv) return; 217 | 218 | if ( 219 | s.cH 220 | && (s.cH(v) === false) 221 | ) return; 222 | 223 | 224 | s.change(s._validate(v)); 225 | s._draw(); 226 | }; 227 | 228 | // get touches index 229 | this.t = k.c.t(e); 230 | 231 | // First touch 232 | touchMove(e); 233 | 234 | // Touch events listeners 235 | k.c.d 236 | .bind("touchmove.k", touchMove) 237 | .bind( 238 | "touchend.k" 239 | , function () { 240 | k.c.d.unbind('touchmove.k touchend.k'); 241 | 242 | if ( 243 | s.rH 244 | && (s.rH(s.cv) === false) 245 | ) return; 246 | 247 | s.val(s.cv); 248 | } 249 | ); 250 | 251 | return this; 252 | }; 253 | 254 | this._mouse = function (e) { 255 | 256 | var mouseMove = function (e) { 257 | var v = s.xy2val(e.pageX, e.pageY); 258 | if (v == s.cv) return; 259 | 260 | if ( 261 | s.cH 262 | && (s.cH(v) === false) 263 | ) return; 264 | 265 | s.change(s._validate(v)); 266 | s._draw(); 267 | }; 268 | 269 | // First click 270 | mouseMove(e); 271 | 272 | // Mouse events listeners 273 | k.c.d 274 | .bind("mousemove.k", mouseMove) 275 | .bind( 276 | // Escape key cancel current change 277 | "keyup.k" 278 | , function (e) { 279 | if (e.keyCode === 27) { 280 | k.c.d.unbind("mouseup.k mousemove.k keyup.k"); 281 | 282 | if ( 283 | s.eH 284 | && (s.eH() === false) 285 | ) return; 286 | 287 | s.cancel(); 288 | } 289 | } 290 | ) 291 | .bind( 292 | "mouseup.k" 293 | , function (e) { 294 | k.c.d.unbind('mousemove.k mouseup.k keyup.k'); 295 | 296 | if ( 297 | s.rH 298 | && (s.rH(s.cv) === false) 299 | ) return; 300 | 301 | s.val(s.cv); 302 | } 303 | ); 304 | 305 | return this; 306 | }; 307 | 308 | this._xy = function () { 309 | var o = this.$c.offset(); 310 | this.x = o.left; 311 | this.y = o.top; 312 | return this; 313 | }; 314 | 315 | this._listen = function () { 316 | 317 | if (!this.o.readOnly) { 318 | this.$c 319 | .bind( 320 | "mousedown" 321 | , function (e) { 322 | e.preventDefault(); 323 | s._xy()._mouse(e); 324 | } 325 | ) 326 | .bind( 327 | "touchstart" 328 | , function (e) { 329 | e.preventDefault(); 330 | s._xy()._touch(e); 331 | } 332 | ); 333 | this.listen(); 334 | } else { 335 | this.$.attr('readonly', 'readonly'); 336 | } 337 | 338 | return this; 339 | }; 340 | 341 | this._configure = function () { 342 | 343 | // Hooks 344 | if (this.o.draw) this.dH = this.o.draw; 345 | if (this.o.change) this.cH = this.o.change; 346 | if (this.o.cancel) this.eH = this.o.cancel; 347 | if (this.o.release) this.rH = this.o.release; 348 | 349 | if (this.o.displayPrevious) { 350 | this.pColor = this.h2rgba(this.o.fgColor, "0.4"); 351 | this.fgColor = this.h2rgba(this.o.fgColor, "0.6"); 352 | } else { 353 | this.fgColor = this.o.fgColor; 354 | } 355 | 356 | return this; 357 | }; 358 | 359 | this._clear = function () { 360 | this.$c[0].width = this.$c[0].width; 361 | }; 362 | 363 | this._validate = function(v) { 364 | return (~~ (((v < 0) ? -0.5 : 0.5) + (v/this.o.step))) * this.o.step; 365 | }; 366 | 367 | // Abstract methods 368 | this.listen = function () {}; // on start, one time 369 | this.extend = function () {}; // each time configure triggered 370 | this.init = function () {}; // each time configure triggered 371 | this.change = function (v) {}; // on change 372 | this.val = function (v) {}; // on release 373 | this.xy2val = function (x, y) {}; // 374 | this.draw = function () {}; // on change / on release 375 | this.clear = function () { this._clear(); }; 376 | 377 | // Utils 378 | this.h2rgba = function (h, a) { 379 | var rgb; 380 | h = h.substring(1,7) 381 | rgb = [parseInt(h.substring(0,2),16) 382 | ,parseInt(h.substring(2,4),16) 383 | ,parseInt(h.substring(4,6),16)]; 384 | return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + a + ")"; 385 | }; 386 | 387 | this.copy = function (f, t) { 388 | for (var i in f) { t[i] = f[i]; } 389 | }; 390 | }; 391 | 392 | 393 | /** 394 | * k.Dial 395 | */ 396 | k.Dial = function () { 397 | k.o.call(this); 398 | 399 | this.startAngle = null; 400 | this.xy = null; 401 | this.radius = null; 402 | this.lineWidth = null; 403 | this.cursorExt = null; 404 | this.w2 = null; 405 | this.PI2 = 2*Math.PI; 406 | 407 | this.extend = function () { 408 | this.o = $.extend( 409 | { 410 | bgColor : this.$.data('bgcolor') || '#EEEEEE', 411 | angleOffset : this.$.data('angleoffset') || 0, 412 | angleArc : this.$.data('anglearc') || 360, 413 | inline : true 414 | }, this.o 415 | ); 416 | }; 417 | 418 | this.val = function (v) { 419 | if (null != v) { 420 | this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v; 421 | this.v = this.cv; 422 | this.$.val(this.v); 423 | this._draw(); 424 | } else { 425 | return this.v; 426 | } 427 | }; 428 | 429 | this.xy2val = function (x, y) { 430 | var a, ret; 431 | 432 | a = Math.atan2( 433 | x - (this.x + this.w2) 434 | , - (y - this.y - this.w2) 435 | ) - this.angleOffset; 436 | 437 | if(this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) { 438 | // if isset angleArc option, set to min if .5 under min 439 | a = 0; 440 | } else if (a < 0) { 441 | a += this.PI2; 442 | } 443 | 444 | ret = ~~ (0.5 + (a * (this.o.max - this.o.min) / this.angleArc)) 445 | + this.o.min; 446 | 447 | this.o.stopper 448 | && (ret = max(min(ret, this.o.max), this.o.min)); 449 | 450 | return ret; 451 | }; 452 | 453 | this.listen = function () { 454 | // bind MouseWheel 455 | var s = this, 456 | mw = function (e) { 457 | e.preventDefault(); 458 | var ori = e.originalEvent 459 | ,deltaX = ori.detail || ori.wheelDeltaX 460 | ,deltaY = ori.detail || ori.wheelDeltaY 461 | ,v = parseInt(s.$.val()) + (deltaX>0 || deltaY>0 ? s.o.step : deltaX<0 || deltaY<0 ? -s.o.step : 0); 462 | 463 | if ( 464 | s.cH 465 | && (s.cH(v) === false) 466 | ) return; 467 | 468 | s.val(v); 469 | } 470 | , kval, to, m = 1, kv = {37:-s.o.step, 38:s.o.step, 39:s.o.step, 40:-s.o.step}; 471 | 472 | this.$ 473 | .bind( 474 | "keydown" 475 | ,function (e) { 476 | var kc = e.keyCode; 477 | 478 | // numpad support 479 | if(kc >= 96 && kc <= 105) { 480 | kc = e.keyCode = kc - 48; 481 | } 482 | 483 | kval = parseInt(String.fromCharCode(kc)); 484 | 485 | if (isNaN(kval)) { 486 | 487 | (kc !== 13) // enter 488 | && (kc !== 8) // bs 489 | && (kc !== 9) // tab 490 | && (kc !== 189) // - 491 | && e.preventDefault(); 492 | 493 | // arrows 494 | if ($.inArray(kc,[37,38,39,40]) > -1) { 495 | e.preventDefault(); 496 | 497 | var v = parseInt(s.$.val()) + kv[kc] * m; 498 | 499 | s.o.stopper 500 | && (v = max(min(v, s.o.max), s.o.min)); 501 | 502 | s.change(v); 503 | s._draw(); 504 | 505 | // long time keydown speed-up 506 | to = window.setTimeout( 507 | function () { m*=2; } 508 | ,30 509 | ); 510 | } 511 | } 512 | } 513 | ) 514 | .bind( 515 | "keyup" 516 | ,function (e) { 517 | if (isNaN(kval)) { 518 | if (to) { 519 | window.clearTimeout(to); 520 | to = null; 521 | m = 1; 522 | s.val(s.$.val()); 523 | } 524 | } else { 525 | // kval postcond 526 | (s.$.val() > s.o.max && s.$.val(s.o.max)) 527 | || (s.$.val() < s.o.min && s.$.val(s.o.min)); 528 | } 529 | 530 | } 531 | ); 532 | 533 | this.$c.bind("mousewheel DOMMouseScroll", mw); 534 | this.$.bind("mousewheel DOMMouseScroll", mw) 535 | }; 536 | 537 | this.init = function () { 538 | 539 | if ( 540 | this.v < this.o.min 541 | || this.v > this.o.max 542 | ) this.v = this.o.min; 543 | 544 | this.$.val(this.v); 545 | this.w2 = this.o.width / 2; 546 | this.cursorExt = this.o.cursor / 100; 547 | this.xy = this.w2; 548 | this.lineWidth = this.xy * this.o.thickness; 549 | this.lineCap = this.o.lineCap; 550 | this.radius = this.xy - this.lineWidth / 2; 551 | 552 | this.o.angleOffset 553 | && (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset); 554 | 555 | this.o.angleArc 556 | && (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc); 557 | 558 | // deg to rad 559 | this.angleOffset = this.o.angleOffset * Math.PI / 180; 560 | this.angleArc = this.o.angleArc * Math.PI / 180; 561 | 562 | // compute start and end angles 563 | this.startAngle = 1.5 * Math.PI + this.angleOffset; 564 | this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc; 565 | 566 | var s = max( 567 | String(Math.abs(this.o.max)).length 568 | , String(Math.abs(this.o.min)).length 569 | , 2 570 | ) + 2; 571 | 572 | this.o.displayInput 573 | && this.i.css({ 574 | 'width' : ((this.o.width / 2 + 4) >> 0) + 'px' 575 | ,'height' : ((this.o.width / 3) >> 0) + 'px' 576 | ,'position' : 'absolute' 577 | ,'vertical-align' : 'middle' 578 | ,'margin-top' : ((this.o.width / 3) >> 0) + 'px' 579 | ,'margin-left' : '-' + ((this.o.width * 3 / 4 + 2) >> 0) + 'px' 580 | ,'border' : 0 581 | ,'background' : 'none' 582 | ,'font' : 'bold ' + ((this.o.width / s) >> 0) + 'px Arial' 583 | ,'text-align' : 'center' 584 | ,'color' : this.o.inputColor || this.o.fgColor 585 | ,'padding' : '0px' 586 | ,'-webkit-appearance': 'none' 587 | }) 588 | || this.i.css({ 589 | 'width' : '0px' 590 | ,'visibility' : 'hidden' 591 | }); 592 | }; 593 | 594 | this.change = function (v) { 595 | this.cv = v; 596 | this.$.val(v); 597 | }; 598 | 599 | this.angle = function (v) { 600 | return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min); 601 | }; 602 | 603 | this.draw = function () { 604 | 605 | var c = this.g, // context 606 | a = this.angle(this.cv) // Angle 607 | , sat = this.startAngle // Start angle 608 | , eat = sat + a // End angle 609 | , sa, ea // Previous angles 610 | , r = 1; 611 | 612 | c.lineWidth = this.lineWidth; 613 | 614 | c.lineCap = this.lineCap; 615 | 616 | this.o.cursor 617 | && (sat = eat - this.cursorExt) 618 | && (eat = eat + this.cursorExt); 619 | 620 | c.beginPath(); 621 | c.strokeStyle = this.o.bgColor; 622 | c.arc(this.xy, this.xy, this.radius, this.endAngle, this.startAngle, true); 623 | c.stroke(); 624 | 625 | if (this.o.displayPrevious) { 626 | ea = this.startAngle + this.angle(this.v); 627 | sa = this.startAngle; 628 | this.o.cursor 629 | && (sa = ea - this.cursorExt) 630 | && (ea = ea + this.cursorExt); 631 | 632 | c.beginPath(); 633 | c.strokeStyle = this.pColor; 634 | c.arc(this.xy, this.xy, this.radius, sa, ea, false); 635 | c.stroke(); 636 | r = (this.cv == this.v); 637 | } 638 | 639 | c.beginPath(); 640 | c.strokeStyle = r ? this.o.fgColor : this.fgColor ; 641 | c.arc(this.xy, this.xy, this.radius, sat, eat, false); 642 | c.stroke(); 643 | }; 644 | 645 | this.cancel = function () { 646 | this.val(this.v); 647 | }; 648 | }; 649 | 650 | $.fn.dial = $.fn.knob = function (o) { 651 | return this.each( 652 | function () { 653 | var d = new k.Dial(); 654 | d.o = o; 655 | d.$ = $(this); 656 | d.run(); 657 | } 658 | ).parent(); 659 | }; 660 | 661 | })(jQuery); -------------------------------------------------------------------------------- /demos/js/less-1.3.3.min.js: -------------------------------------------------------------------------------- 1 | // 2 | // LESS - Leaner CSS v1.3.3 3 | // http://lesscss.org 4 | // 5 | // Copyright (c) 2009-2013, Alexis Sellier 6 | // Licensed under the Apache 2.0 License. 7 | // 8 | (function(e,t){function n(t){return e.less[t.split("/")[1]]}function f(){r.env==="development"?(r.optimization=0,r.watchTimer=setInterval(function(){r.watchMode&&g(function(e,t,n,r,i){t&&S(t.toCSS(),r,i.lastModified)})},r.poll)):r.optimization=3}function m(){var e=document.getElementsByTagName("style");for(var t=0;t0&&(s.splice(o-1,2),o-=2)}return i.hostPart=r[1],i.directories=s,i.path=r[1]+s.join("/"),i.fileUrl=i.path+(r[4]||""),i.url=i.fileUrl+(r[5]||""),i}function w(t,n,i,s){var o=t.contents||{},u=t.files||{},a=b(t.href,e.location.href),f=a.url,c=l&&l.getItem(f),h=l&&l.getItem(f+":timestamp"),p={css:c,timestamp:h},d;r.relativeUrls?r.rootpath?t.entryPath?d=b(r.rootpath+y(a.path,t.entryPath)).path:d=r.rootpath:d=a.path:r.rootpath?d=r.rootpath:t.entryPath?d=t.entryPath:d=a.path,x(f,t.type,function(e,l){v+=e.replace(/@import .+?;/ig,"");if(!i&&p&&l&&(new Date(l)).valueOf()===(new Date(p.timestamp)).valueOf())S(p.css,t),n(null,null,e,t,{local:!0,remaining:s},f);else try{o[f]=e,(new r.Parser({optimization:r.optimization,paths:[a.path],entryPath:t.entryPath||a.path,mime:t.type,filename:f,rootpath:d,relativeUrls:t.relativeUrls,contents:o,files:u,dumpLineNumbers:r.dumpLineNumbers})).parse(e,function(r,i){if(r)return k(r,f);try{n(r,i,e,t,{local:!1,lastModified:l,remaining:s},f),N(document.getElementById("less-error-message:"+E(f)))}catch(r){k(r,f)}})}catch(c){k(c,f)}},function(e,t){throw new Error("Couldn't load "+t+" ("+e+")")})}function E(e){return e.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function S(e,t,n){var r,i=t.href||"",s="less:"+(t.title||E(i));if((r=document.getElementById(s))===null){r=document.createElement("style"),r.type="text/css",t.media&&(r.media=t.media),r.id=s;var o=t&&t.nextSibling||null;(o||document.getElementsByTagName("head")[0]).parentNode.insertBefore(r,o)}if(r.styleSheet)try{r.styleSheet.cssText=e}catch(u){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(e){r.childNodes.length>0?r.firstChild.nodeValue!==e.nodeValue&&r.replaceChild(e,r.firstChild):r.appendChild(e)})(document.createTextNode(e));if(n&&l){C("saving "+i+" to cache.");try{l.setItem(i,e),l.setItem(i+":timestamp",n)}catch(u){C("failed to save")}}}function x(e,t,n,i){function a(t,n,r){t.status>=200&&t.status<300?n(t.responseText,t.getResponseHeader("Last-Modified")):typeof r=="function"&&r(t.status,e)}var s=T(),u=o?r.fileAsync:r.async;typeof s.overrideMimeType=="function"&&s.overrideMimeType("text/css"),s.open("GET",e,u),s.setRequestHeader("Accept",t||"text/x-less, text/css; q=0.9, */*; q=0.5"),s.send(null),o&&!r.fileAsync?s.status===0||s.status>=200&&s.status<300?n(s.responseText):i(s.status,e):u?s.onreadystatechange=function(){s.readyState==4&&a(s,n,i)}:a(s,n,i)}function T(){if(e.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(t){return C("browser doesn't support AJAX."),null}}function N(e){return e&&e.parentNode.removeChild(e)}function C(e){r.env=="development"&&typeof console!="undefined"&&console.log("less: "+e)}function k(e,t){var n="less-error-message:"+E(t),i='
  • {content}
  • ',s=document.createElement("div"),o,u,a=[],f=e.filename||t,l=f.match(/([^\/]+(\?.*)?)$/)[1];s.id=n,s.className="less-error-message",u="

    "+(e.message||"There is an error in your .less file")+"

    "+'

    in '+l+" ";var c=function(e,t,n){e.extract[t]&&a.push(i.replace(/\{line\}/,parseInt(e.line)+(t-1)).replace(/\{class\}/,n).replace(/\{content\}/,e.extract[t]))};e.stack?u+="
    "+e.stack.split("\n").slice(1).join("
    "):e.extract&&(c(e,0,""),c(e,1,"line"),c(e,2,""),u+="on line "+e.line+", column "+(e.column+1)+":

    "+"
      "+a.join("")+"
    "),s.innerHTML=u,S([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),s.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),r.env=="development"&&(o=setInterval(function(){document.body&&(document.getElementById(n)?document.body.replaceChild(s,document.getElementById(n)):document.body.insertBefore(s,document.body.firstChild),clearInterval(o))},10))}Array.isArray||(Array.isArray=function(e){return Object.prototype.toString.call(e)==="[object Array]"||e instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(e,t){var n=this.length>>>0;for(var r=0;r>>0,n=new Array(t),r=arguments[1];for(var i=0;i>>0,n=0;if(t===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var r=arguments[1];else do{if(n in this){r=this[n++];break}if(++n>=t)throw new TypeError}while(!0);for(;n=t)return-1;n<0&&(n+=t);for(;nh&&(c[u]=c[u].slice(o-h),h=o)}function w(e){var t=e.charCodeAt(0);return t===32||t===10||t===9}function E(e){var t,n,r,i,a;if(e instanceof Function)return e.call(p.parsers);if(typeof e=="string")t=s.charAt(o)===e?e:null,r=1,b();else{b();if(!(t=e.exec(c[u])))return null;r=t[0].length}if(t)return S(r),typeof t=="string"?t:t.length===1?t[0]:t}function S(e){var t=o,n=u,r=o+c[u].length,i=o+=e;while(o=0&&t.charAt(n)!=="\n";n--)r++;return{line:typeof e=="number"?(t.slice(0,e).match(/\n/g)||"").length:null,column:r}}function L(e){return r.mode==="browser"||r.mode==="rhino"?e.filename:n("path").resolve(e.filename)}function A(e,t,n){return{lineNumber:k(e,t).line+1,fileName:L(n)}}function O(e,t){var n=C(e,t),r=k(e.index,n),i=r.line,s=r.column,o=n.split("\n");this.type=e.type||"Syntax",this.message=e.message,this.filename=e.filename||t.filename,this.index=e.index,this.line=typeof i=="number"?i+1:null,this.callLine=e.call&&k(e.call,n).line+1,this.callExtract=o[k(e.call,n).line],this.stack=e.stack,this.column=s,this.extract=[o[i-1],o[i],o[i+1]]}var s,o,u,a,f,l,c,h,p,d=this,t=t||{};t.contents||(t.contents={}),t.rootpath=t.rootpath||"",t.files||(t.files={});var v=function(){},m=this.imports={paths:t.paths||[],queue:[],files:t.files,contents:t.contents,mime:t.mime,error:null,push:function(e,n){var i=this;this.queue.push(e),r.Parser.importer(e,this.paths,function(t,r,s){i.queue.splice(i.queue.indexOf(e),1);var o=s in i.files;i.files[s]=r,t&&!i.error&&(i.error=t),n(t,r,o),i.queue.length===0&&v(i.error)},t)}};return this.env=t=t||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,p={imports:m,parse:function(e,a){var f,d,m,g,y,b,w=[],S,x=null;o=u=h=l=0,s=e.replace(/\r\n/g,"\n"),s=s.replace(/^\uFEFF/,""),c=function(e){var n=0,r=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,i=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,o=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,u=0,a,f=e[0],l;for(var c=0,h,p;c0?"missing closing `}`":"missing opening `{`",filename:t.filename},t)),e.map(function(e){return e.join("")})}([[]]);if(x)return a(x,t);try{f=new i.Ruleset([],E(this.parsers.primary)),f.root=!0}catch(T){return a(new O(T,t))}f.toCSS=function(e){var s,o,u;return function(s,o){var u=[],a;s=s||{},typeof o=="object"&&!Array.isArray(o)&&(o=Object.keys(o).map(function(e){var t=o[e];return t instanceof i.Value||(t instanceof i.Expression||(t=new i.Expression([t])),t=new i.Value([t])),new i.Rule("@"+e,t,!1,0)}),u=[new i.Ruleset(null,o)]);try{var f=e.call(this,{frames:u}).toCSS([],{compress:s.compress||!1,dumpLineNumbers:t.dumpLineNumbers})}catch(l){throw new O(l,t)}if(a=p.imports.error)throw a instanceof O?a:new O(a,t);return s.yuicompress&&r.mode==="node"?n("ycssmin").cssmin(f):s.compress?f.replace(/(\s)+/g,"$1"):f}}(f.eval);if(o=0&&s.charAt(N)!=="\n";N--)C++;x={type:"Parse",message:"Syntax Error on line "+y,index:o,filename:t.filename,line:y,column:C,extract:[b[y-2],b[y-1],b[y]]}}this.imports.queue.length>0?v=function(e){e=x||e,e?a(e):a(null,f)}:a(x,f)},parsers:{primary:function(){var e,t=[];while((e=E(this.mixin.definition)||E(this.rule)||E(this.ruleset)||E(this.mixin.call)||E(this.comment)||E(this.directive))||E(/^[\s\n]+/)||E(/^;+/))e&&t.push(e);return t},comment:function(){var e;if(s.charAt(o)!=="/")return;if(s.charAt(o+1)==="/")return new i.Comment(E(/^\/\/.*/),!0);if(e=E(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new i.Comment(e)},entities:{quoted:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=='"'&&s.charAt(t)!=="'")return;n&&E("~");if(e=E(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new i.Quoted(e[0],e[1]||e[2],n)},keyword:function(){var e;if(e=E(/^[_A-Za-z-][_A-Za-z0-9-]*/))return i.colors.hasOwnProperty(e)?new i.Color(i.colors[e].slice(1)):new i.Keyword(e)},call:function(){var e,n,r,s,a=o;if(!(e=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(c[u])))return;e=e[1],n=e.toLowerCase();if(n==="url")return null;o+=e.length;if(n==="alpha"){s=E(this.alpha);if(typeof s!="undefined")return s}E("("),r=E(this.entities.arguments);if(!E(")"))return;if(e)return new i.Call(e,r,a,t.filename)},arguments:function(){var e=[],t;while(t=E(this.entities.assignment)||E(this.expression)){e.push(t);if(!E(","))break}return e},literal:function(){return E(this.entities.ratio)||E(this.entities.dimension)||E(this.entities.color)||E(this.entities.quoted)||E(this.entities.unicodeDescriptor)},assignment:function(){var e,t;if((e=E(/^\w+(?=\s?=)/i))&&E("=")&&(t=E(this.entity)))return new i.Assignment(e,t)},url:function(){var e;if(s.charAt(o)!=="u"||!E(/^url\(/))return;return e=E(this.entities.quoted)||E(this.entities.variable)||E(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",x(")"),new i.URL(e.value!=null||e instanceof i.Variable?e:new i.Anonymous(e),t.rootpath)},variable:function(){var e,n=o;if(s.charAt(o)==="@"&&(e=E(/^@@?[\w-]+/)))return new i.Variable(e,n,t.filename)},variableCurly:function(){var e,n,r=o;if(s.charAt(o)==="@"&&(n=E(/^@\{([\w-]+)\}/)))return new i.Variable("@"+n[1],r,t.filename)},color:function(){var e;if(s.charAt(o)==="#"&&(e=E(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/)))return new i.Color(e[1])},dimension:function(){var e,t=s.charCodeAt(o);if(t>57||t<43||t===47||t==44)return;if(e=E(/^([+-]?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/))return new i.Dimension(e[1],e[2])},ratio:function(){var e,t=s.charCodeAt(o);if(t>57||t<48)return;if(e=E(/^(\d+\/\d+)/))return new i.Ratio(e[1])},unicodeDescriptor:function(){var e;if(e=E(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))return new i.UnicodeDescriptor(e[0])},javascript:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=="`")return;n&&E("~");if(e=E(/^`([^`]*)`/))return new i.JavaScript(e[1],o,n)}},variable:function(){var e;if(s.charAt(o)==="@"&&(e=E(/^(@[\w-]+)\s*:/)))return e[1]},shorthand:function(){var e,t;if(!N(/^[@\w.%-]+\/[@\w.-]+/))return;g();if((e=E(this.entity))&&E("/")&&(t=E(this.entity)))return new i.Shorthand(e,t);y()},mixin:{call:function(){var e=[],n,r,u=[],a=[],f,l,c,h,p,d,v,m=o,b=s.charAt(o),w,S,C=!1;if(b!=="."&&b!=="#")return;g();while(n=E(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/))e.push(new i.Element(r,n,o)),r=E(">");if(E("(")){p=[];while(c=E(this.expression)){h=null,S=c;if(c.value.length==1){var k=c.value[0];k instanceof i.Variable&&E(":")&&(p.length>0&&(d&&T("Cannot mix ; and , as delimiter types"),v=!0),S=x(this.expression),h=w=k.name)}p.push(S),a.push({name:h,value:S});if(E(","))continue;if(E(";")||d)v&&T("Cannot mix ; and , as delimiter types"),d=!0,p.length>1&&(S=new i.Value(p)),u.push({name:w,value:S}),w=null,p=[],v=!1}x(")")}f=d?u:a,E(this.important)&&(C=!0);if(e.length>0&&(E(";")||N("}")))return new i.mixin.Call(e,f,m,t.filename,C);y()},definition:function(){var e,t=[],n,r,u,a,f,c=!1;if(s.charAt(o)!=="."&&s.charAt(o)!=="#"||N(/^[^{]*\}/))return;g();if(n=E(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){e=n[1];do{E(this.comment);if(s.charAt(o)==="."&&E(/^\.{3}/)){c=!0,t.push({variadic:!0});break}if(!(u=E(this.entities.variable)||E(this.entities.literal)||E(this.entities.keyword)))break;if(u instanceof i.Variable)if(E(":"))a=x(this.expression,"expected expression"),t.push({name:u.name,value:a});else{if(E(/^\.{3}/)){t.push({name:u.name,variadic:!0}),c=!0;break}t.push({name:u.name})}else t.push({value:u})}while(E(",")||E(";"));E(")")||(l=o,y()),E(this.comment),E(/^when/)&&(f=x(this.conditions,"expected condition")),r=E(this.block);if(r)return new i.mixin.Definition(e,t,r,f,c);y()}}},entity:function(){return E(this.entities.literal)||E(this.entities.variable)||E(this.entities.url)||E(this.entities.call)||E(this.entities.keyword)||E(this.entities.javascript)||E(this.comment)},end:function(){return E(";")||N("}")},alpha:function(){var e;if(!E(/^\(opacity=/i))return;if(e=E(/^\d+/)||E(this.entities.variable))return x(")"),new i.Alpha(e)},element:function(){var e,t,n,r;n=E(this.combinator),e=E(/^(?:\d+\.\d+|\d+)%/)||E(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||E("*")||E("&")||E(this.attribute)||E(/^\([^()@]+\)/)||E(/^[\.#](?=@)/)||E(this.entities.variableCurly),e||E("(")&&(r=E(this.entities.variableCurly)||E(this.entities.variable)||E(this.selector))&&E(")")&&(e=new i.Paren(r));if(e)return new i.Element(n,e,o)},combinator:function(){var e,t=s.charAt(o);if(t===">"||t==="+"||t==="~"||t==="|"){o++;while(s.charAt(o).match(/\s/))o++;return new i.Combinator(t)}return s.charAt(o-1).match(/\s/)?new i.Combinator(" "):new i.Combinator(null)},selector:function(){var e,t,n=[],r,u;if(E("("))return e=E(this.entity),E(")")?new i.Selector([new i.Element("",e,o)]):null;while(t=E(this.element)){r=s.charAt(o),n.push(t);if(r==="{"||r==="}"||r===";"||r===","||r===")")break}if(n.length>0)return new i.Selector(n)},attribute:function(){var e="",t,n,r;if(!E("["))return;if(t=E(/^(?:[_A-Za-z0-9-]|\\.)+/)||E(this.entities.quoted))(r=E(/^[|~*$^]?=/))&&(n=E(this.entities.quoted)||E(/^[\w-]+/))?e=[t,r,n.toCSS?n.toCSS():n].join(""):e=t;if(!E("]"))return;if(e)return"["+e+"]"},block:function(){var e;if(E("{")&&(e=E(this.primary))&&E("}"))return e},ruleset:function(){var e=[],n,r,u,a;g(),t.dumpLineNumbers&&(a=A(o,s,t));while(n=E(this.selector)){e.push(n),E(this.comment);if(!E(","))break;E(this.comment)}if(e.length>0&&(r=E(this.block))){var f=new i.Ruleset(e,r,t.strictImports);return t.dumpLineNumbers&&(f.debugInfo=a),f}l=o,y()},rule:function(){var e,t,n=s.charAt(o),r,a;g();if(n==="."||n==="#"||n==="&")return;if(e=E(this.variable)||E(this.property)){e.charAt(0)!="@"&&(a=/^([^@+\/'"*`(;{}-]*);/.exec(c[u]))?(o+=a[0].length-1,t=new i.Anonymous(a[1])):e==="font"?t=E(this.font):t=E(this.value),r=E(this.important);if(t&&E(this.end))return new i.Rule(e,t,r,f);l=o,y()}},"import":function(){var e,n,r=o;g();var s=E(/^@import(?:-(once))?\s+/);if(s&&(e=E(this.entities.quoted)||E(this.entities.url))){n=E(this.mediaFeatures);if(E(";"))return new i.Import(e,m,n,s[1]==="once",r,t.rootpath)}y()},mediaFeature:function(){var e,t,n=[];do if(e=E(this.entities.keyword))n.push(e);else if(E("(")){t=E(this.property),e=E(this.entity);if(!E(")"))return null;if(t&&e)n.push(new i.Paren(new i.Rule(t,e,null,o,!0)));else{if(!e)return null;n.push(new i.Paren(e))}}while(e);if(n.length>0)return new i.Expression(n)},mediaFeatures:function(){var e,t=[];do if(e=E(this.mediaFeature)){t.push(e);if(!E(","))break}else if(e=E(this.entities.variable)){t.push(e);if(!E(","))break}while(e);return t.length>0?t:null},media:function(){var e,n,r,u;t.dumpLineNumbers&&(u=A(o,s,t));if(E(/^@media/)){e=E(this.mediaFeatures);if(n=E(this.block))return r=new i.Media(n,e),t.dumpLineNumbers&&(r.debugInfo=u),r}},directive:function(){var e,n,r,u,a,f,l,c,h,p;if(s.charAt(o)!=="@")return;if(n=E(this["import"])||E(this.media))return n;g(),e=E(/^@[a-z-]+/);if(!e)return;l=e,e.charAt(1)=="-"&&e.indexOf("-",2)>0&&(l="@"+e.slice(e.indexOf("-",2)+1));switch(l){case"@font-face":c=!0;break;case"@viewport":case"@top-left":case"@top-left-corner":case"@top-center":case"@top-right":case"@top-right-corner":case"@bottom-left":case"@bottom-left-corner":case"@bottom-center":case"@bottom-right":case"@bottom-right-corner":case"@left-top":case"@left-middle":case"@left-bottom":case"@right-top":case"@right-middle":case"@right-bottom":c=!0;break;case"@page":case"@document":case"@supports":case"@keyframes":c=!0,h=!0;break;case"@namespace":p=!0}h&&(e+=" "+(E(/^[^{]+/)||"").trim());if(c){if(r=E(this.block))return new i.Directive(e,r)}else if((n=p?E(this.expression):E(this.entity))&&E(";")){var d=new i.Directive(e,n);return t.dumpLineNumbers&&(d.debugInfo=A(o,s,t)),d}y()},font:function(){var e=[],t=[],n,r,s,o;while(o=E(this.shorthand)||E(this.entity))t.push(o);e.push(new i.Expression(t));if(E(","))while(o=E(this.expression)){e.push(o);if(!E(","))break}return new i.Value(e)},value:function(){var e,t=[],n;while(e=E(this.expression)){t.push(e);if(!E(","))break}if(t.length>0)return new i.Value(t)},important:function(){if(s.charAt(o)==="!")return E(/^! *important/)},sub:function(){var e;if(E("(")&&(e=E(this.expression))&&E(")"))return e},multiplication:function(){var e,t,n,r;if(e=E(this.operand)){while(!N(/^\/[*\/]/)&&(n=E("/")||E("*"))&&(t=E(this.operand)))r=new i.Operation(n,[r||e,t]);return r||e}},addition:function(){var e,t,n,r;if(e=E(this.multiplication)){while((n=E(/^[-+]\s+/)||!w(s.charAt(o-1))&&(E("+")||E("-")))&&(t=E(this.multiplication)))r=new i.Operation(n,[r||e,t]);return r||e}},conditions:function(){var e,t,n=o,r;if(e=E(this.condition)){while(E(",")&&(t=E(this.condition)))r=new i.Condition("or",r||e,t,n);return r||e}},condition:function(){var e,t,n,r,s=o,u=!1;E(/^not/)&&(u=!0),x("(");if(e=E(this.addition)||E(this.entities.keyword)||E(this.entities.quoted))return(r=E(/^(?:>=|=<|[<=>])/))?(t=E(this.addition)||E(this.entities.keyword)||E(this.entities.quoted))?n=new i.Condition(r,e,t,s,u):T("expected expression"):n=new i.Condition("=",e,new i.Keyword("true"),s,u),x(")"),E(/^and/)?new i.Condition("and",n,E(this.condition)):n},operand:function(){var e,t=s.charAt(o+1);s.charAt(o)==="-"&&(t==="@"||t==="(")&&(e=E("-"));var n=E(this.sub)||E(this.entities.dimension)||E(this.entities.color)||E(this.entities.variable)||E(this.entities.call);return e?new i.Operation("*",[new i.Dimension(-1),n]):n},expression:function(){var e,t,n=[],r;while(e=E(this.addition)||E(this.entity))n.push(e);if(n.length>0)return new i.Expression(n)},property:function(){var e;if(e=E(/^(\*?-?[_a-z0-9-]+)\s*:/))return e[1]}}}};if(r.mode==="browser"||r.mode==="rhino")r.Parser.importer=function(e,t,n,r){!/^([a-z-]+:)?\//.test(e)&&t.length>0&&(e=t[0]+e),w({href:e,title:e,type:r.mime,contents:r.contents,files:r.files,rootpath:r.rootpath,entryPath:r.entryPath,relativeUrls:r.relativeUrls},function(e,i,s,o,u,a){e&&typeof r.errback=="function"?r.errback.call(null,a,t,n,r):n.call(null,e,i,a)},!0)};(function(e){function t(t){return e.functions.hsla(t.h,t.s,t.l,t.a)}function n(t,n){return t instanceof e.Dimension&&t.unit=="%"?parseFloat(t.value*n/100):r(t)}function r(t){if(t instanceof e.Dimension)return parseFloat(t.unit=="%"?t.value/100:t.value);if(typeof t=="number")return t;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function i(e){return Math.min(1,Math.max(0,e))}e.functions={rgb:function(e,t,n){return this.rgba(e,t,n,1)},rgba:function(t,i,s,o){var u=[t,i,s].map(function(e){return n(e,256)});return o=r(o),new e.Color(u,o)},hsl:function(e,t,n){return this.hsla(e,t,n,1)},hsla:function(e,t,n,i){function u(e){return e=e<0?e+1:e>1?e-1:e,e*6<1?o+(s-o)*e*6:e*2<1?s:e*3<2?o+(s-o)*(2/3-e)*6:o}e=r(e)%360/360,t=r(t),n=r(n),i=r(i);var s=n<=.5?n*(t+1):n+t-n*t,o=n*2-s;return this.rgba(u(e+1/3)*255,u(e)*255,u(e-1/3)*255,i)},hsv:function(e,t,n){return this.hsva(e,t,n,1)},hsva:function(e,t,n,i){e=r(e)%360/360*360,t=r(t),n=r(n),i=r(i);var s,o;s=Math.floor(e/60%6),o=e/60-s;var u=[n,n*(1-t),n*(1-o*t),n*(1-(1-o)*t)],a=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(u[a[s][0]]*255,u[a[s][1]]*255,u[a[s][2]]*255,i)},hue:function(t){return new e.Dimension(Math.round(t.toHSL().h))},saturation:function(t){return new e.Dimension(Math.round(t.toHSL().s*100),"%")},lightness:function(t){return new e.Dimension(Math.round(t.toHSL().l*100),"%")},red:function(t){return new e.Dimension(t.rgb[0])},green:function(t){return new e.Dimension(t.rgb[1])},blue:function(t){return new e.Dimension(t.rgb[2])},alpha:function(t){return new e.Dimension(t.toHSL().a)},luma:function(t){return new e.Dimension(Math.round((.2126*(t.rgb[0]/255)+.7152*(t.rgb[1]/255)+.0722*(t.rgb[2]/255))*t.alpha*100),"%")},saturate:function(e,n){var r=e.toHSL();return r.s+=n.value/100,r.s=i(r.s),t(r)},desaturate:function(e,n){var r=e.toHSL();return r.s-=n.value/100,r.s=i(r.s),t(r)},lighten:function(e,n){var r=e.toHSL();return r.l+=n.value/100,r.l=i(r.l),t(r)},darken:function(e,n){var r=e.toHSL();return r.l-=n.value/100,r.l=i(r.l),t(r)},fadein:function(e,n){var r=e.toHSL();return r.a+=n.value/100,r.a=i(r.a),t(r)},fadeout:function(e,n){var r=e.toHSL();return r.a-=n.value/100,r.a=i(r.a),t(r)},fade:function(e,n){var r=e.toHSL();return r.a=n.value/100,r.a=i(r.a),t(r)},spin:function(e,n){var r=e.toHSL(),i=(r.h+n.value)%360;return r.h=i<0?360+i:i,t(r)},mix:function(t,n,r){r||(r=new e.Dimension(50));var i=r.value/100,s=i*2-1,o=t.toHSL().a-n.toHSL().a,u=((s*o==-1?s:(s+o)/(1+s*o))+1)/2,a=1-u,f=[t.rgb[0]*u+n.rgb[0]*a,t.rgb[1]*u+n.rgb[1]*a,t.rgb[2]*u+n.rgb[2]*a],l=t.alpha*i+n.alpha*(1-i);return new e.Color(f,l)},greyscale:function(t){return this.desaturate(t,new e.Dimension(100))},contrast:function(e,t,n,r){return e.rgb?(typeof n=="undefined"&&(n=this.rgba(255,255,255,1)),typeof t=="undefined"&&(t=this.rgba(0,0,0,1)),typeof r=="undefined"?r=.43:r=r.value,(.2126*(e.rgb[0]/255)+.7152*(e.rgb[1]/255)+.0722*(e.rgb[2]/255))*e.alpha255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},operate:function(t,n){var r=[];n instanceof e.Color||(n=n.toColor());for(var i=0;i<3;i++)r[i]=e.operate(t,this.rgb[i],n.rgb[i]);return new e.Color(r,this.alpha+n.alpha)},toHSL:function(){var e=this.rgb[0]/255,t=this.rgb[1]/255,n=this.rgb[2]/255,r=this.alpha,i=Math.max(e,t,n),s=Math.min(e,t,n),o,u,a=(i+s)/2,f=i-s;if(i===s)o=u=0;else{u=a>.5?f/(2-i-s):f/(i+s);switch(i){case e:o=(t-n)/f+(t255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},compare:function(e){return e.rgb?e.rgb[0]===this.rgb[0]&&e.rgb[1]===this.rgb[1]&&e.rgb[2]===this.rgb[2]&&e.alpha===this.alpha?0:-1:-1}}}(n("../tree")),function(e){e.Comment=function(e,t){this.value=e,this.silent=!!t},e.Comment.prototype={toCSS:function(e){return e.compress?"":this.value},eval:function(){return this}}}(n("../tree")),function(e){e.Condition=function(e,t,n,r,i){this.op=e.trim(),this.lvalue=t,this.rvalue=n,this.index=r,this.negate=i},e.Condition.prototype.eval=function(e){var t=this.lvalue.eval(e),n=this.rvalue.eval(e),r=this.index,i,i=function(e){switch(e){case"and":return t&&n;case"or":return t||n;default:if(t.compare)i=t.compare(n);else{if(!n.compare)throw{type:"Type",message:"Unable to perform comparison",index:r};i=n.compare(t)}switch(i){case-1:return e==="<"||e==="=<";case 0:return e==="="||e===">="||e==="=<";case 1:return e===">"||e===">="}}}(this.op);return this.negate?!i:i}}(n("../tree")),function(e){e.Dimension=function(e,t){this.value=parseFloat(e),this.unit=t||null},e.Dimension.prototype={eval:function(){return this},toColor:function(){return new e.Color([this.value,this.value,this.value])},toCSS:function(){var e=this.value+this.unit;return e},operate:function(t,n){return new e.Dimension(e.operate(t,this.value,n.value),this.unit||n.unit)},compare:function(t){return t instanceof e.Dimension?t.value>this.value?-1:t.value":e.compress?">":" > ","|":e.compress?"|":" | "}[this.value]}}(n("../tree")),function(e){e.Expression=function(e){this.value=e},e.Expression.prototype={eval:function(t){return this.value.length>1?new e.Expression(this.value.map(function(e){return e.eval(t)})):this.value.length===1?this.value[0].eval(t):this},toCSS:function(e){return this.value.map(function(t){return t.toCSS?t.toCSS(e):""}).join(" ")}}}(n("../tree")),function(e){e.Import=function(t,n,r,i,s,o){var u=this;this.once=i,this.index=s,this._path=t,this.features=r&&new e.Value(r),this.rootpath=o,t instanceof e.Quoted?this.path=/(\.[a-z]*$)|([\?;].*)$/.test(t.value)?t.value:t.value+".less":this.path=t.value.value||t.value,this.css=/css([\?;].*)?$/.test(this.path),this.css||n.push(this.path,function(t,n,r){t&&(t.index=s),r&&u.once&&(u.skip=r),u.root=n||new e.Ruleset([],[])})},e.Import.prototype={toCSS:function(e){var t=this.features?" "+this.features.toCSS(e):"";return this.css?(typeof this._path.value=="string"&&!/^(?:[a-z-]+:|\/)/.test(this._path.value)&&(this._path.value=this.rootpath+this._path.value),"@import "+this._path.toCSS()+t+";\n"):""},eval:function(t){var n,r=this.features&&this.features.eval(t);return this.skip?[]:this.css?this:(n=new e.Ruleset([],this.root.rules.slice(0)),n.evalImports(t),this.features?new e.Media(n.rules,this.features.value):n.rules)}}}(n("../tree")),function(e){e.JavaScript=function(e,t,n){this.escaped=n,this.expression=e,this.index=t},e.JavaScript.prototype={eval:function(t){var n,r=this,i={},s=this.expression.replace(/@\{([\w-]+)\}/g,function(n,i){return e.jsify((new e.Variable("@"+i,r.index)).eval(t))});try{s=new Function("return ("+s+")")}catch(o){throw{message:"JavaScript evaluation error: `"+s+"`",index:this.index}}for(var u in t.frames[0].variables())i[u.slice(1)]={value:t.frames[0].variables()[u].value,toJS:function(){return this.value.eval(t).toCSS()}};try{n=s.call(i)}catch(o){throw{message:"JavaScript evaluation error: '"+o.name+": "+o.message+"'",index:this.index}}return typeof n=="string"?new e.Quoted('"'+n+'"',n,this.escaped,this.index):Array.isArray(n)?new e.Anonymous(n.join(", ")):new e.Anonymous(n)}}}(n("../tree")),function(e){e.Keyword=function(e){this.value=e},e.Keyword.prototype={eval:function(){return this},toCSS:function(){return this.value},compare:function(t){return t instanceof e.Keyword?t.value===this.value?0:1:-1}},e.True=new e.Keyword("true"),e.False=new e.Keyword("false")}(n("../tree")),function(e){e.Media=function(t,n){var r=this.emptySelectors();this.features=new e.Value(n),this.ruleset=new e.Ruleset(r,t),this.ruleset.allowImports=!0},e.Media.prototype={toCSS:function(e,t){var n=this.features.toCSS(t);return this.ruleset.root=e.length===0||e[0].multiMedia,"@media "+n+(t.compress?"{":" {\n ")+this.ruleset.toCSS(e,t).trim().replace(/\n/g,"\n ")+(t.compress?"}":"\n}\n")},eval:function(t){t.mediaBlocks||(t.mediaBlocks=[],t.mediaPath=[]);var n=new e.Media([],[]);return this.debugInfo&&(this.ruleset.debugInfo=this.debugInfo,n.debugInfo=this.debugInfo),n.features=this.features.eval(t),t.mediaPath.push(n),t.mediaBlocks.push(n),t.frames.unshift(this.ruleset),n.ruleset=this.ruleset.eval(t),t.frames.shift(),t.mediaPath.pop(),t.mediaPath.length===0?n.evalTop(t):n.evalNested(t)},variable:function(t){return e.Ruleset.prototype.variable.call(this.ruleset,t)},find:function(){return e.Ruleset.prototype.find.apply(this.ruleset,arguments)},rulesets:function(){return e.Ruleset.prototype.rulesets.apply(this.ruleset)},emptySelectors:function(){var t=new e.Element("","&",0);return[new e.Selector([t])]},evalTop:function(t){var n=this;if(t.mediaBlocks.length>1){var r=this.emptySelectors();n=new e.Ruleset(r,t.mediaBlocks),n.multiMedia=!0}return delete t.mediaBlocks,delete t.mediaPath,n},evalNested:function(t){var n,r,i=t.mediaPath.concat([this]);for(n=0;n0;n--)t.splice(n,0,new e.Anonymous("and"));return new e.Expression(t)})),new e.Ruleset([],[])},permute:function(e){if(e.length===0)return[];if(e.length===1)return e[0];var t=[],n=this.permute(e.slice(1));for(var r=0;r0){c=!0;for(a=0;athis.params.length)return!1;if(this.required>0&&n>this.params.length)return!1}r=Math.min(n,this.arity);for(var s=0;si.selectors[o].elements.length?Array.prototype.push.apply(r,i.find(new e.Selector(t.elements.slice(1)),n)):r.push(i);break}}),this._lookups[o]=r)},toCSS:function(t,n){var r=[],i=[],s=[],o=[],u=[],a,f,l;this.root||this.joinSelectors(u,t,this.selectors);for(var c=0;c0){f=e.debugInfo(n,this),a=u.map(function(e){return e.map(function(e){return e.toCSS(n)}).join("").trim()}).join(n.compress?",":",\n");for(var c=i.length-1;c>=0;c--)s.indexOf(i[c])===-1&&s.unshift(i[c]);i=s,r.push(f+a+(n.compress?"{":" {\n ")+i.join(n.compress?"":"\n ")+(n.compress?"}":"\n}\n"))}return r.push(o),r.join("")+(n.compress?"\n":"")},joinSelectors:function(e,t,n){for(var r=0;r0)for(i=0;i0&&this.mergeElementsOnToSelectors(g,a);for(s=0;s0&&(l[0].elements=l[0].elements.slice(0),l[0].elements.push(new e.Element(f.combinator,"",0))),y.push(l);else for(o=0;o0?(h=l.slice(0),m=h.pop(),d=new e.Selector(m.elements.slice(0)),v=!1):d=new e.Selector([]),c.length>1&&(p=p.concat(c.slice(1))),c.length>0&&(v=!1,d.elements.push(new e.Element(f.combinator,c[0].elements[0].value,0)),d.elements=d.elements.concat(c[0].elements.slice(1))),v||h.push(d),h=h.concat(p),y.push(h)}a=y,g=[]}}g.length>0&&this.mergeElementsOnToSelectors(g,a);for(i=0;i0?i[i.length-1]=new e.Selector(i[i.length-1].elements.concat(t)):i.push(new e.Selector(t))}}}(n("../tree")),function(e){e.Selector=function(e){this.elements=e},e.Selector.prototype.match=function(e){var t=this.elements,n=t.length,r,i,s,o;r=e.elements.slice(e.elements.length&&e.elements[0].value==="&"?1:0),i=r.length,s=Math.min(n,i);if(i===0||n1?"["+e.value.map(function(e){return e.toCSS(!1)}).join(", ")+"]":e.toCSS(!1)}}(n("./tree"));var o=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);r.env=r.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||o?"development":"production"),r.async=r.async||!1,r.fileAsync=r.fileAsync||!1,r.poll=r.poll||(o?1e3:1500);if(r.functions)for(var u in r.functions)r.tree.functions[u]=r.functions[u];var a=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);a&&(r.dumpLineNumbers=a[1]),r.watch=function(){return r.watchMode||(r.env="development",f()),this.watchMode=!0},r.unwatch=function(){return clearInterval(r.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&r.watch();var l=null;if(r.env!="development")try{l=typeof e.localStorage=="undefined"?null:e.localStorage}catch(c){}var h=document.getElementsByTagName("link"),p=/^text\/(x-)?less$/;r.sheets=[];for(var d=0;du;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); -------------------------------------------------------------------------------- /demos/randomDrone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Random drone - [pd-fileutils] & [WebPd] demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
    24 | 25 | 51 | 52 |
    53 | 54 |
    55 |
    56 | 57 |
    58 |

    Sound Generators

    59 |
    60 | 61 |
    Amount of [osc~]
    62 |
    63 |
    64 | 65 |
    Amount of [phasor~]
    66 |
    67 |
    68 | 69 |
    Amount of [noise~]
    70 |
    71 |
    72 | 73 |
    Amount of [*~]
    74 |
    75 |
    76 | 77 |
    Amount of [+~]
    78 |
    79 |
    80 | 81 |
    82 |

    Modulation with LFOs

    83 |
    84 | 85 |
    Total amount
    86 |
    87 |
    88 | 89 |
    Frequency modulation
    90 |
    91 |
    92 | 93 |
    94 |
    95 | 96 |
    97 |
    98 | Copy paste the following text, and save it in a .pd file 99 | 100 |
    101 |
    102 | 103 |
    104 |
    105 |

    This is a random drone generator. You can generate a random drone sound, then download the Pure Data patch by clicking on "Download PD patch".

    106 |

    This is a demo of the pd-fileutils and the WebPd libraries. pd-fileutils is used to generate the random patch, and WebPd allows to load the patch and play it in the browser without plugin. 107 |

    108 | 111 |
    112 |
    113 | 114 |
    115 | 116 | 372 | 373 | 385 | 386 | 387 | 388 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var JSDOM = require('jsdom').JSDOM 2 | , fs = require('fs') 3 | , path = require('path') 4 | , svgRendering = require('./lib/svg-rendering') 5 | 6 | // Set default css style for rendering 7 | svgRendering.defaults.style = fs.readFileSync( 8 | path.join(__dirname, 'lib', 'svg-default-style.css') 9 | ).toString() 10 | 11 | // Set global document for d3 12 | global.document = (new JSDOM('...')).window.document 13 | 14 | exports.parse = require('pd-fileutils.parser').parse 15 | exports.renderSvg = svgRendering.render 16 | exports.renderPd = require('./lib/pd-rendering').render 17 | exports.Patch = require('./lib/Patch') -------------------------------------------------------------------------------- /lib/Patch.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Sébastien Piquemal 3 | * 4 | * BSD Simplified License. 5 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. 7 | * 8 | * See https://github.com/sebpiq/pd-fileutils for documentation 9 | * 10 | */ 11 | 12 | var _ = require('underscore') 13 | 14 | var Patch = module.exports = function(obj) { _.extend(this, obj) } 15 | 16 | 17 | _.extend(Patch.prototype, { 18 | 19 | getNode: function(id) { 20 | return _.find(this.nodes, function(node) { return node.id === id }) || null 21 | }, 22 | 23 | guessPortlets: function() { 24 | var self = this 25 | _.each(this.nodes, function(node) { 26 | node.outlets = _.reduce(self.connections, function(memo, conn) { 27 | if (conn.source.id === node.id) { 28 | return Math.max(memo, conn.source.port) 29 | } else return memo 30 | }, -1) + 1 31 | node.inlets = _.reduce(self.connections, function(memo, conn) { 32 | if (conn.sink.id === node.id) { 33 | return Math.max(memo, conn.sink.port) 34 | } else return memo 35 | }, -1) + 1 36 | }) 37 | }, 38 | 39 | getSinks: function(node) { 40 | var conns = _.filter(this.connections, function(conn) { return conn.source.id === node.id }) 41 | , sinkIds = _.uniq(_.map(conns, function(conn) { return conn.sink.id })) 42 | , self = this 43 | return _.map(sinkIds, function(sinkId) { return self.getNode(sinkId) }) 44 | }, 45 | 46 | getSources: function(node) { 47 | var conns = _.filter(this.connections, function(conn) { return conn.sink.id === node.id }) 48 | , sourceIds = _.uniq(_.map(conns, function(conn) { return conn.source.id })) 49 | , self = this 50 | return _.map(sourceIds, function(sourceId) { return self.getNode(sourceId) }) 51 | }, 52 | 53 | addNode: function(node) { 54 | if (node.id === undefined) node.id = this.nextId() 55 | else if (this.getNode(node.id) !== null) return 56 | this.nodes.push(node) 57 | }, 58 | 59 | nextId: function() { 60 | if (this.nodes.length) { 61 | return Math.max.apply(Math, _.pluck(this.nodes, 'id')) + 1 62 | } else return 0 63 | } 64 | 65 | }) 66 | -------------------------------------------------------------------------------- /lib/pd-rendering.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Sébastien Piquemal 3 | * 4 | * BSD Simplified License. 5 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. 7 | * 8 | * See https://github.com/sebpiq/pd-fileutils for documentation 9 | * 10 | */ 11 | 12 | var mustache = require('mustache') 13 | , _ = require('underscore') 14 | 15 | exports.render = function(patch) { 16 | 17 | // Render the graph canvas 18 | var rendered = '' 19 | , layout = _.clone(patch.layout || {}) 20 | _.defaults(layout, {x: 0, y: 0, width: 500, height: 500}) 21 | rendered += mustache.render(canvasTpl, {args: patch.args, layout: layout}) + ';\n' 22 | 23 | // Render all nodes 24 | _.forEach(patch.nodes.sort(function(n1, n2){return n1.id - n2.id}), function(node) { 25 | var layout = _.clone(node.layout || {}) 26 | _.defaults(layout, {x: 0, y: 0}) 27 | rendered += mustache.render(objTpl, {args: node.args, layout: layout, proto: node.proto}) + ';\n' 28 | }) 29 | 30 | // Render all connections 31 | _.forEach(patch.connections, function(conn) { 32 | rendered += mustache.render(connectTpl, conn) + ';\n' 33 | }) 34 | 35 | return rendered 36 | } 37 | 38 | var canvasTpl = '#N canvas {{{layout.x}}} {{{layout.y}}} {{{layout.width}}} {{{layout.height}}} {{{args.0}}}{{#layout.openOnLoad}} {{{.}}}{{/layout.openOnLoad}}' 39 | , connectTpl = '#X connect {{{source.id}}} {{{source.port}}} {{{sink.id}}} {{{sink.port}}}' 40 | 41 | var floatAtomTpl = '#X floatatom {{{layout.x}}} {{{layout.y}}} {{{layout.width}}} {{{args.0}}} {{{args.1}}} {{{layout.labelPos}}} {{{layout.label}}} {{{args.2}}} {{{args.3}}}' 42 | , symbolAtomTpl = '#X symbolatom {{{layout.x}}} {{{layout.y}}} {{{layout.width}}} {{{args.0}}} {{{args.1}}} {{{layout.labelPos}}} {{{layout.label}}} {{{args.2}}} {{{args.3}}}' 43 | , bngTpl = '#X obj {{{layout.x}}} {{{layout.y}}} bng {{{layout.size}}} {{{layout.hold}}} {{{layout.interrupt}}} {{{args.0}}} {{{args.1}}} {{{args.2}}} {{{layout.label}}} {{{layout.labelX}}} {{{layout.labelY}}} {{{layout.labelFont}}} {{{layout.labelFontSize}}} {{{layout.bgColor}}} {{{layout.fgColor}}} {{{layout.labelColor}}}' 44 | , nbxTpl = '#X obj {{{layout.x}}} {{{layout.y}}} nbx {{{layout.size}}} {{{layout.height}}} {{{args.0}}} {{{args.1}}} {{{layout.log}}} {{{args.2}}} {{{args.3}}} {{{args.4}}} {{{layout.label}}} {{{layout.labelX}}} {{{layout.labelY}}} {{{layout.labelFont}}} {{{layout.labelFontSize}}} {{{layout.bgColor}}} {{{layout.fgColor}}} {{{layout.labelColor}}} {{{layout.logHeight}}}' 45 | , vslTpl = '#X obj {{{layout.x}}} {{{layout.y}}} vsl {{{layout.width}}} {{{layout.height}}} {{{args.0}}} {{{args.1}}} {{{layout.log}}} {{{args.2}}} {{{args.3}}} {{{args.4}}} {{{layout.label}}} {{{layout.labelX}}} {{{layout.labelY}}} {{{layout.labelFont}}} {{{layout.labelFontSize}}} {{{layout.bgColor}}} {{{layout.fgColor}}} {{{layout.labelColor}}} {{{args.5}}} {{{layout.steadyOnClick}}}' 46 | , hslTpl = '#X obj {{{layout.x}}} {{{layout.y}}} hsl {{{layout.width}}} {{{layout.height}}} {{{args.0}}} {{{args.1}}} {{{layout.log}}} {{{args.2}}} {{{args.3}}} {{{args.4}}} {{{layout.label}}} {{{layout.labelX}}} {{{layout.labelY}}} {{{layout.labelFont}}} {{{layout.labelFontSize}}} {{{layout.bgColor}}} {{{layout.fgColor}}} {{{layout.labelColor}}} {{{args.5}}} {{{layout.steadyOnClick}}}' 47 | , vradioTpl = '#X obj {{{layout.x}}} {{{layout.y}}} vradio {{{layout.size}}} {{{args.0}}} {{{args.1}}} {{{args.2}}} {{{args.3}}} {{{args.4}}} {{{layout.label}}} {{{layout.labelX}}} {{{layout.labelY}}} {{{layout.labelFont}}} {{{layout.labelFontSize}}} {{{layout.bgColor}}} {{{layout.fgColor}}} {{{layout.labelColor}}} {{{args.5}}}' 48 | , hradioTpl = '#X obj {{{layout.x}}} {{{layout.y}}} hradio {{{layout.size}}} {{{args.0}}} {{{args.1}}} {{{args.2}}} {{{args.3}}} {{{args.4}}} {{{layout.label}}} {{{layout.labelX}}} {{{layout.labelY}}} {{{layout.labelFont}}} {{{layout.labelFontSize}}} {{{layout.bgColor}}} {{{layout.fgColor}}} {{{layout.labelColor}}} {{{args.5}}}' 49 | , vuTpl = '#X obj {{{layout.x}}} {{{layout.y}}} vu {{{layout.width}}} {{{layout.height}}} {{{args.0}}} {{{layout.label}}} {{{layout.labelX}}} {{{layout.labelY}}} {{{layout.labelFont}}} {{{layout.labelFontSize}}} {{{layout.bgColor}}} {{{layout.labelColor}}} {{{layout.log}}} {{{args.1}}}' 50 | , cnvTpl = '#X obj {{{layout.x}}} {{{layout.y}}} cnv {{{layout.size}}} {{{layout.width}}} {{{layout.height}}} {{{args.0}}} {{{args.1}}} {{{layout.label}}} {{{layout.labelX}}} {{{layout.labelY}}} {{{layout.labelFont}}} {{{layout.labelFontSize}}} {{{layout.bgColor}}} {{{layout.labelColor}}} {{{args.2}}}' 51 | , objTpl = '#X obj {{{layout.x}}} {{{layout.y}}} {{{proto}}}{{#args}} {{.}}{{/args}}' 52 | -------------------------------------------------------------------------------- /lib/svg-default-style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'source_code_proregular'; 3 | src: url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff'); 4 | font-weight: normal; 5 | font-style: normal; 6 | } 7 | 8 | text { 9 | font-size: 14px; 10 | font-family: 'source_code_proregular', monospace; 11 | } -------------------------------------------------------------------------------- /lib/svg-rendering.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Sébastien Piquemal 3 | * 4 | * BSD Simplified License. 5 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL 6 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. 7 | * 8 | * See https://github.com/sebpiq/pd-fileutils for documentation 9 | * 10 | */ 11 | 12 | var _ = require('underscore') 13 | , d3 = Object.assign({}, require('d3-selection'), require('d3-shape')) 14 | , Patch = require('./Patch') 15 | 16 | exports.defaults = { 17 | portletWidth: 5, 18 | portletHeight: 3.5, 19 | objMinWidth: 25, 20 | objMinHeight: 20, 21 | ratio: 1.2, 22 | padding: 10, 23 | glyphWidth: 8, 24 | glyphHeight: 9, 25 | textPadding: 6, 26 | svgFile: true, 27 | style: null, 28 | } 29 | 30 | exports.render = function(patch, opts) { 31 | opts = opts || {} 32 | _.defaults(opts, exports.defaults) 33 | 34 | d3.select('svg').remove() 35 | var svgContainer = d3.select('body').append('div') 36 | , svg = svgContainer.append('svg') 37 | .attr('xmlns', 'http://www.w3.org/2000/svg') 38 | .attr('version', '1.1') 39 | , root = svg.append('g') 40 | , connections, nodes 41 | 42 | if (opts.style) { 43 | svg.append('style').text(opts.style) 44 | } 45 | 46 | // Creating all renderers 47 | patch = new Patch(patch) 48 | patch.guessPortlets() 49 | patch.nodes = _.map(patch.nodes, function(node) { 50 | var proto = node.proto 51 | if (proto === 'msg') return new MsgRenderer(node, opts) 52 | else if (proto === 'text') return new TextRenderer(node, opts) 53 | else if (proto === 'floatatom') return new FloatAtomRenderer(node, opts) 54 | else if (proto === 'symbolatom') return new SymbolAtomRenderer(node, opts) 55 | else if (proto === 'bng') return new BngRenderer(node, opts) 56 | else if (proto === 'tgl') return new TglRenderer(node, opts) 57 | else if (proto === 'nbx') return new NbxRenderer(node, opts) 58 | else if (proto === 'hsl') return new HslRenderer(node, opts) 59 | else if (proto === 'vsl') return new VslRenderer(node, opts) 60 | else if (proto === 'hradio') return new HRadioRenderer(node, opts) 61 | else if (proto === 'vradio') return new VRadioRenderer(node, opts) 62 | else if (proto === 'vu') return new VuRenderer(node, opts) 63 | else return new ObjectRenderer(node, opts) 64 | }) 65 | 66 | // Render the nodes 67 | nodes = root.selectAll('g.node') 68 | .data(patch.nodes) 69 | .enter() 70 | .append('g') 71 | .attr('transform', function(renderer) { 72 | return 'translate(' + renderer.getX() + ' ' + renderer.getY() + ')' 73 | }) 74 | .attr('class', 'node') 75 | .attr('id', function(node) { return node.id }) 76 | .each(function(renderer, i) { renderer.render(d3.select(this)) }) 77 | 78 | // Render the connections 79 | connections = root.selectAll('line.connection') 80 | .data(patch.connections) 81 | .enter() 82 | .append('line') 83 | .attr('class', 'connection') 84 | .attr('style', 'stroke:black;stroke-width:2px;') 85 | .each(function(conn) { 86 | var sourceRenderer = patch.getNode(conn.source.id) 87 | , sinkRenderer = patch.getNode(conn.sink.id) 88 | 89 | d3.select(this) 90 | .attr('x1', function(conn) { 91 | return sourceRenderer.getOutletX(conn.source.port) + opts.portletWidth/2 92 | }) 93 | .attr('y1', function(conn) { 94 | return sourceRenderer.getOutletY(conn.source.port) + opts.portletHeight 95 | }) 96 | .attr('x2', function(conn) { 97 | return sinkRenderer.getInletX(conn.sink.port) + opts.portletWidth/2 98 | }) 99 | .attr('y2', function(conn) { 100 | return sinkRenderer.getInletY(conn.sink.port) 101 | }) 102 | }) 103 | 104 | // Calculate width / height of the SVG 105 | var allX1 = [], allY1 = [], allX2 = [], allY2 = [] 106 | , topLeft = {}, bottomRight = {} 107 | _.forEach(patch.nodes, function(n) { 108 | allX1.push(n.getX()) 109 | allY1.push(n.getY()) 110 | allX2.push(n.getX() + n.getW()) 111 | allY2.push(n.getY() + n.getH()) 112 | }) 113 | topLeft.x = _.min(allX1) 114 | topLeft.y = _.min(allY1) 115 | bottomRight.x = _.max(allX2) 116 | bottomRight.y = _.max(allY2) 117 | svg.attr('width', bottomRight.x - topLeft.x + opts.padding * 2) 118 | svg.attr('height', bottomRight.y - topLeft.y + opts.padding * 2) 119 | root.attr('transform', 'translate(' 120 | + (-topLeft.x + opts.padding) + ' ' 121 | + (-topLeft.y + opts.padding) + ')' 122 | ) 123 | 124 | // Finally rendering to a string 125 | var rendered = svgContainer.node().innerHTML 126 | if (opts.svgFile) { 127 | rendered = '' + rendered 129 | } else { 130 | svgContainer.remove() 131 | } 132 | return rendered 133 | 134 | } 135 | 136 | //==================== Node renderers ====================// 137 | 138 | // Simple helper to memoize some method calls 139 | var memoized = function(obj, methodName) { 140 | var originalMethod = obj[methodName] 141 | , cache = undefined 142 | 143 | if (originalMethod.length > 0) 144 | throw new Error('This memoization is valid only for methods with 0 arguments') 145 | 146 | obj[methodName] = function() { 147 | cache = originalMethod.apply(obj, arguments) 148 | obj[methodName] = function() { return cache } 149 | return cache 150 | } 151 | } 152 | 153 | var NodeRenderer = function(node, opts) { 154 | this.opts = opts 155 | this.node = node 156 | this.id = node.id 157 | memoized(this, 'getX') 158 | memoized(this, 'getY') 159 | } 160 | 161 | _.extend(NodeRenderer.prototype, { 162 | 163 | // Returns node X in the canvas 164 | getX: function() { return this.node.layout.x * this.opts.ratio }, 165 | 166 | // Returns node Y in the canvas 167 | getY: function() { return this.node.layout.y * this.opts.ratio }, 168 | 169 | // Returns outlet's absolute X in the canvas 170 | getOutletX: function(outlet) { 171 | return this.getOutletRelX(outlet) + this.getX() 172 | }, 173 | 174 | // Returns intlet's absolute X in the canvas 175 | getInletX: function(inlet) { 176 | return this.getInletRelX(inlet) + this.getX() 177 | }, 178 | 179 | // Returns outlet's Y in the canvas 180 | getOutletY: function(outlet) { 181 | return this.getOutletRelY(outlet) + this.getY() 182 | }, 183 | 184 | // Returns inlet's Y in the canvas 185 | getInletY: function(inlet) { 186 | return this.getInletRelY(inlet) + this.getY() 187 | }, 188 | 189 | // ---- Methods to implement ---- // 190 | // Do the actual rendering in svg group `g`. 191 | render: function(g) { throw new Error('Implement me') }, 192 | 193 | // Returns the width of the bounding box of the node 194 | getW: function() { throw new Error('Implement me') }, 195 | 196 | // Returns the height of the bounding box of the node 197 | getH: function() { throw new Error('Implement me') }, 198 | 199 | // Returns outlet X relatively to the node 200 | getOutletRelX: function(outlet) { throw new Error('Implement me') }, 201 | 202 | // Returns inlet X relatively to the node 203 | getInletRelX: function(inlet) { throw new Error('Implement me') }, 204 | 205 | // Returns outlet Y relatively to the node 206 | getOutletRelY: function(outlet) { throw new Error('Implement me') }, 207 | 208 | // Returns inlet Y relatively to the node 209 | getInletRelY: function(inlet) { throw new Error('Implement me') }, 210 | 211 | }) 212 | 213 | 214 | var ObjectRenderer = function() { 215 | NodeRenderer.prototype.constructor.apply(this, arguments) 216 | memoized(this, 'getW') 217 | memoized(this, 'getH') 218 | } 219 | 220 | _.extend(ObjectRenderer.prototype, NodeRenderer.prototype, { 221 | 222 | render: function(g) { 223 | this.renderBox(g) 224 | this.renderText(g) 225 | this.renderOutlets(g) 226 | this.renderInlets(g) 227 | }, 228 | 229 | renderBox: function(g) { 230 | g.append('rect') 231 | .attr('class', 'box') 232 | .attr('width', this.getW()) 233 | .attr('height', this.getH()) 234 | .attr('style', 'stroke:black;fill:white;') 235 | }, 236 | 237 | renderText: function(g) { 238 | g.append('text') 239 | .attr('class', 'proto') 240 | .text(this.getText()) 241 | .attr('dy', this.getTextY()) 242 | .attr('dx', this.opts.textPadding) 243 | }, 244 | 245 | renderInlets: function(g) { this._genericRenderPortlets('inlet', g) }, 246 | renderOutlets: function(g) { this._genericRenderPortlets('outlet', g) }, 247 | _genericRenderPortlets: function(portletType, g) { 248 | var portletTypeCap = portletType.substr(0, 1).toUpperCase() + portletType.substr(1) 249 | , self = this 250 | g.selectAll('rect.' + portletType) 251 | .data(_.range(this.node[portletType+'s'])) 252 | .enter() 253 | .append('rect') 254 | .classed(portletType, true) 255 | .classed('portlet', true) 256 | .attr('width', this.opts.portletWidth) 257 | .attr('height', this.opts.portletHeight) 258 | .attr('x', function(i) { return self['get' + portletTypeCap + 'RelX'](i) }) 259 | .attr('y', function(i) { return self['get' + portletTypeCap + 'RelY'](i) }) 260 | }, 261 | 262 | // Returns object height 263 | getH: function() { return this.opts.objMinHeight }, 264 | 265 | // Returns object width 266 | getW: function() { 267 | var maxPortlet = Math.max(this.node.inlets, this.node.outlets) 268 | , textLength = this.getText().length * this.opts.glyphWidth + this.opts.textPadding * 2 269 | return Math.max((maxPortlet-1) * this.opts.objMinWidth, this.opts.objMinWidth, textLength) 270 | }, 271 | 272 | // Returns text to display on the object 273 | getText: function() { return this.node.proto + ' ' + this.node.args.join(' ') }, 274 | 275 | // Returns text Y relatively to the object 276 | getTextY: function() { return this.getH()/2 + this.opts.glyphHeight/2 }, 277 | 278 | // ---- Implement virtual methods ---- // 279 | getOutletRelX: function(outlet) { 280 | return this._genericPortletRelX('outlets', outlet) 281 | }, 282 | 283 | getInletRelX: function(inlet) { 284 | return this._genericPortletRelX('inlets', inlet) 285 | }, 286 | 287 | getOutletRelY: function(outlet) { 288 | return this.getH() - this.opts.portletHeight 289 | }, 290 | 291 | getInletRelY: function(inlet) { return 0 }, 292 | 293 | _genericPortletRelX: function(inOrOutlets, portlet) { 294 | var width = this.getW() 295 | , n = this.node[inOrOutlets] 296 | if (portlet === 0) return 0; 297 | else if (portlet === n-1) return width - this.opts.portletWidth 298 | else { 299 | // Space between portlets 300 | var a = (width - n*this.opts.portletWidth) / (n-1) 301 | return portlet * (this.opts.portletWidth + a) 302 | } 303 | } 304 | 305 | }) 306 | 307 | 308 | var MsgRenderer = function() { 309 | ObjectRenderer.prototype.constructor.apply(this, arguments) 310 | } 311 | 312 | _.extend(MsgRenderer.prototype, ObjectRenderer.prototype, { 313 | 314 | renderBox: function(g) { 315 | 316 | var r = this.getH() * 0.75 317 | , teta = Math.asin(this.getH() / (2 * r)) 318 | , arcPath = d3.arc()({ 319 | innerRadius: r, 320 | outerRadius: r, 321 | startAngle: -Math.PI / 2 - teta, 322 | endAngle: -Math.PI / 2 + teta 323 | }) 324 | , linePath = d3.line()([ 325 | [this.getW(), 0], [0, 0], 326 | [0, this.getH()], [this.getW(), this.getH()] 327 | ]) 328 | 329 | g.append('svg:path') 330 | .attr('d', linePath) 331 | .attr('style', 'stroke:black;fill:white;') 332 | 333 | g.append('svg:path') 334 | .attr('d', arcPath) 335 | .attr('transform', 'translate(' + (this.getW() + r * Math.cos(teta)) + ' ' + this.getH()/2 + ')') 336 | .attr('style', 'stroke:black;fill:white;') 337 | }, 338 | 339 | getText: function() { return this.node.args.join(' ') } 340 | 341 | }) 342 | 343 | 344 | var AtomBoxRenderer = function() { 345 | ObjectRenderer.prototype.constructor.apply(this, arguments) 346 | } 347 | 348 | _.extend(AtomBoxRenderer.prototype, ObjectRenderer.prototype, { 349 | 350 | renderBox: function(g) { 351 | 352 | var r = this.getH() * 0.4 353 | , arcPath = d3.arc()({ 354 | innerRadius: r, 355 | outerRadius: r, 356 | startAngle: 0, 357 | endAngle: Math.PI / 2 358 | }) 359 | , linePath = d3.line()([ 360 | [this.getW() - r, 0], [0, 0], [0, this.getH()], 361 | [this.getW(), this.getH()], [this.getW(), r] 362 | ]) 363 | 364 | g.append('svg:path') 365 | .attr('d', linePath) 366 | .attr('style', 'stroke:black;fill:white;') 367 | 368 | g.append('svg:path') 369 | .attr('d', arcPath) 370 | .attr('transform', 'translate(' + (this.getW() - r) + ' ' + r + ')') 371 | .attr('style', 'stroke:black;fill:white;') 372 | } 373 | 374 | }) 375 | 376 | var FloatAtomRenderer = function() { 377 | AtomBoxRenderer.prototype.constructor.apply(this, arguments) 378 | } 379 | 380 | _.extend(FloatAtomRenderer.prototype, AtomBoxRenderer.prototype, { 381 | 382 | getText: function() { return '0' } 383 | 384 | }) 385 | 386 | 387 | var SymbolAtomRenderer = function() { 388 | AtomBoxRenderer.prototype.constructor.apply(this, arguments) 389 | } 390 | 391 | _.extend(SymbolAtomRenderer.prototype, AtomBoxRenderer.prototype, { 392 | 393 | getText: function() { return 'symbol' } 394 | 395 | }) 396 | 397 | 398 | 399 | var BngRenderer = function() { 400 | ObjectRenderer.prototype.constructor.apply(this, arguments) 401 | } 402 | 403 | _.extend(BngRenderer.prototype, ObjectRenderer.prototype, { 404 | 405 | render: function(g) { 406 | g.append('rect') 407 | .attr('class', 'box') 408 | .attr('width', this.getW()) 409 | .attr('height', this.getH()) 410 | .attr('style', 'stroke:black;fill:white;') 411 | 412 | g.append('circle') 413 | .attr('cx', this.getW()/2) 414 | .attr('cy', this.getH()/2) 415 | .attr('r', this.getW()/3) 416 | .attr('style', 'stroke:black;fill:white;') 417 | 418 | this.renderOutlets(g) 419 | this.renderInlets(g) 420 | }, 421 | 422 | getW: function() { return 20 }, 423 | getH: function() { return 20 } 424 | 425 | }) 426 | 427 | 428 | var TglRenderer = function() { 429 | ObjectRenderer.prototype.constructor.apply(this, arguments) 430 | } 431 | 432 | _.extend(TglRenderer.prototype, ObjectRenderer.prototype, { 433 | 434 | render: function(g) { 435 | var crossPath = d3.symbol() 436 | .size(this.getW() * this.getH() / 3.5) 437 | .type(d3.symbolCross)([1]) 438 | 439 | g.append('rect') 440 | .attr('class', 'box') 441 | .attr('width', this.getW()) 442 | .attr('height', this.getH()) 443 | .attr('style', 'stroke:black;fill:white;') 444 | 445 | g.append('svg:path') 446 | .attr('d', crossPath) 447 | .attr('transform', 'rotate(' + 45 + ' ' + this.getW()/2 + ' ' + this.getH()/2 448 | + ') translate(' + this.getW()/2 + ' ' + this.getH()/2 + ')') 449 | .attr('style', 'stroke:black;fill:white;') 450 | 451 | this.renderOutlets(g) 452 | this.renderInlets(g) 453 | }, 454 | 455 | getW: function() { return 20 }, 456 | getH: function() { return 20 } 457 | 458 | }) 459 | 460 | 461 | var NbxRenderer = function() { 462 | AtomBoxRenderer.prototype.constructor.apply(this, arguments) 463 | } 464 | 465 | _.extend(NbxRenderer.prototype, AtomBoxRenderer.prototype, { 466 | 467 | renderBox: function(g) { 468 | AtomBoxRenderer.prototype.renderBox.apply(this, arguments) 469 | var trianglePath = d3.line()([ [0, 0], [this.getW()/6, this.getH()/2], [0, this.getH()] ]) 470 | g.append('svg:path') 471 | .attr('d', trianglePath) 472 | .attr('style', 'stroke:black;fill:white;') 473 | }, 474 | 475 | getText: function() { return '0' } 476 | 477 | }) 478 | 479 | 480 | var HslRenderer = function() { 481 | ObjectRenderer.prototype.constructor.apply(this, arguments) 482 | } 483 | 484 | _.extend(HslRenderer.prototype, ObjectRenderer.prototype, { 485 | 486 | renderBox: function(g) { 487 | ObjectRenderer.prototype.renderBox.apply(this, arguments) 488 | var cursorPath = d3.line()([ [5, 0], [10, 0], 489 | [10, this.getH()], [5, this.getH()], [5, 0] ]) 490 | g.append('svg:path') 491 | .attr('d', cursorPath) 492 | .attr('style', 'stroke:black;fill:black;') 493 | }, 494 | 495 | getText: function() { return '' }, 496 | getW: function() { return 200 }, 497 | getH: function() { return 20 } 498 | 499 | }) 500 | 501 | 502 | var VslRenderer = function() { 503 | ObjectRenderer.prototype.constructor.apply(this, arguments) 504 | } 505 | 506 | _.extend(VslRenderer.prototype, ObjectRenderer.prototype, { 507 | 508 | renderBox: function(g) { 509 | ObjectRenderer.prototype.renderBox.apply(this, arguments) 510 | var cursorPath = d3.line()([ [0, 5], [0, 10], 511 | [this.getW(), 10], [this.getW(), 5], [0, 5] ]) 512 | g.append('svg:path') 513 | .attr('d', cursorPath) 514 | .attr('style', 'stroke:black;fill:black;') 515 | }, 516 | 517 | getText: function() { return '' }, 518 | getW: function() { return 20 }, 519 | getH: function() { return 200 } 520 | 521 | }) 522 | 523 | 524 | var HRadioRenderer = function() { 525 | ObjectRenderer.prototype.constructor.apply(this, arguments) 526 | } 527 | 528 | _.extend(HRadioRenderer.prototype, ObjectRenderer.prototype, { 529 | 530 | renderBox: function(g) { 531 | var nBoxes = this.getNBoxes(), i 532 | , enabledSize = this.getBoxSize() / 1.5 533 | for (i = 0; i < nBoxes; i++) { 534 | g.append('rect') 535 | .attr('width', this.getBoxSize()) 536 | .attr('height', this.getBoxSize()) 537 | .attr('transform', 'translate(' + i * this.getBoxSize() + ' ' + 0 + ')') 538 | .attr('style', 'stroke:black;fill:white;') 539 | } 540 | g.append('rect') 541 | .attr('width', enabledSize) 542 | .attr('height', enabledSize) 543 | .attr('transform', 'translate(' + (this.getBoxSize() - enabledSize) / 2 544 | + ' ' + (this.getBoxSize() - enabledSize) / 2 + ')') 545 | .attr('style', 'stroke:black;fill:black;') 546 | }, 547 | 548 | getW: function() { return this.getBoxSize() * this.getNBoxes() }, 549 | getH: function() { return this.getBoxSize() }, 550 | getBoxSize: function() { return 20 }, 551 | getNBoxes: function() { return this.node.args[2] }, 552 | getText: function() { return '' } 553 | 554 | }) 555 | 556 | 557 | var VRadioRenderer = function() { 558 | ObjectRenderer.prototype.constructor.apply(this, arguments) 559 | } 560 | 561 | _.extend(VRadioRenderer.prototype, ObjectRenderer.prototype, { 562 | 563 | renderBox: function(g) { 564 | var nBoxes = this.getNBoxes(), i 565 | , enabledSize = this.getBoxSize() / 1.5 566 | for (i = 0; i < nBoxes; i++) { 567 | g.append('rect') 568 | .attr('width', this.getBoxSize()) 569 | .attr('height', this.getBoxSize()) 570 | .attr('transform', 'translate(' + 0 + ' ' + i * this.getBoxSize() + ')') 571 | .attr('style', 'stroke:black;fill:white;') 572 | } 573 | g.append('rect') 574 | .attr('width', enabledSize) 575 | .attr('height', enabledSize) 576 | .attr('transform', 'translate(' + (this.getBoxSize() - enabledSize) / 2 577 | + ' ' + (this.getBoxSize() - enabledSize) / 2 + ')') 578 | .attr('style', 'stroke:black;fill:black;') 579 | }, 580 | 581 | getW: function() { return this.getBoxSize() }, 582 | getH: function() { return this.getBoxSize() * this.getNBoxes() }, 583 | getBoxSize: function() { return 20 }, 584 | getNBoxes: function() { return this.node.args[2] }, 585 | getText: function() { return '' } 586 | 587 | }) 588 | 589 | 590 | var VuRenderer = function() { 591 | ObjectRenderer.prototype.constructor.apply(this, arguments) 592 | } 593 | 594 | _.extend(VuRenderer.prototype, ObjectRenderer.prototype, { 595 | 596 | renderBox: function(g) { 597 | g.append('rect') 598 | .attr('class', 'box') 599 | .attr('width', this.getW()) 600 | .attr('height', this.getH()) 601 | .attr('style', 'stroke:black;fill:grey;') 602 | }, 603 | 604 | getText: function() { return '' }, 605 | getW: function() { return 20 }, 606 | getH: function() { return 200 } 607 | 608 | }) 609 | 610 | 611 | var TextRenderer = function() { 612 | NodeRenderer.prototype.constructor.apply(this, arguments) 613 | } 614 | 615 | _.extend(TextRenderer.prototype, NodeRenderer.prototype, { 616 | 617 | render: function(g) { 618 | g.append('text') 619 | .attr('class', 'comment') 620 | .text(this.node.args[0]) 621 | .attr('dy', this.getH()/2 + this.opts.glyphHeight/2) 622 | }, 623 | getW: function() { return this.node.args[0].length * this.opts.glyphWidth + this.opts.textPadding * 2 }, 624 | getH: function() { return this.opts.objMinHeight } 625 | 626 | }) 627 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pd-fileutils", 3 | "version": "0.4.0", 4 | "global": true, 5 | "description": "A set of utilities for Pure Data files : parser, image generator.", 6 | "main": "index.js", 7 | "directories": { 8 | "test": "test" 9 | }, 10 | "dependencies": { 11 | "ansi-regex": "^6.0.1", 12 | "d3-selection": "^2.0.0", 13 | "d3-shape": "^2.1.0", 14 | "jsdom": "18.1.x", 15 | "mustache": "4.2.x", 16 | "npm-test": "^0.0.1", 17 | "pd-fileutils.parser": "sebpiq/pd-fileutils.parser", 18 | "underscore": "1.13.x" 19 | }, 20 | "devDependencies": { 21 | "css-loader": "6.5.x", 22 | "mocha": "9.1.x", 23 | "to-string-loader": "^1.1.5", 24 | "webpack": "5.64.x", 25 | "webpack-cli": "4.9.x" 26 | }, 27 | "scripts": { 28 | "test": "mocha --recursive test/", 29 | "build": "webpack-cli --mode production" 30 | }, 31 | "bin": { 32 | "pd-fileutils": "./bin/pd-fileutils" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git@github.com:sebpiq/pd-fileutils.git" 37 | }, 38 | "keywords": [ 39 | "pd", 40 | "puredata" 41 | ], 42 | "author": { 43 | "name": "Sébastien Piquemal", 44 | "email": "sebpiq@gmail.com", 45 | "url": "http://funktion.fm/" 46 | }, 47 | "license": "BSD-2-Clause" 48 | } 49 | -------------------------------------------------------------------------------- /test/Patch-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | , Patch = require('../lib/Patch') 3 | 4 | describe('Patch', function() { 5 | 6 | describe('#getNode', function() { 7 | 8 | it('should return the right object if existing', function() { 9 | var patch = new Patch({ 10 | nodes: [ {id: 78, attr1: 90}, {id: 56, attr1: 88}, {id: 2, attr1: 5} ] 11 | }) 12 | assert.deepEqual(patch.getNode(56), {id: 56, attr1: 88}) 13 | assert.deepEqual(patch.getNode(2), {id: 2, attr1: 5}) 14 | }) 15 | 16 | it('should return null if object unknown', function() { 17 | var patch = new Patch({ 18 | nodes: [ {id: 78, attr1: 90}, {id: 56, attr1: 88}, {id: 2, attr1: 5} ], 19 | }) 20 | assert.equal(patch.getNode(57), null) 21 | }) 22 | 23 | }) 24 | 25 | describe('#guessPortlets', function() { 26 | 27 | it('should guess the portlets right', function() { 28 | var patch = new Patch({ 29 | nodes: [ {id: 78, attr1: 90}, {id: 56, attr1: 88}, {id: 2, attr1: 5}, {id: 32, attr1: 9} ], 30 | connections: [ 31 | {source: {id: 78, port: 2}, sink: {id: 56, port: 1}}, 32 | {source: {id: 78, port: 1}, sink: {id: 2, port: 0}} 33 | ] 34 | }) 35 | patch.guessPortlets() 36 | assert.deepEqual(patch.nodes, [ 37 | {id: 78, attr1: 90, inlets: 0, outlets: 3}, 38 | {id: 56, attr1: 88, inlets: 2, outlets: 0}, 39 | {id: 2, attr1: 5, inlets: 1, outlets: 0}, 40 | {id: 32, attr1: 9, inlets: 0, outlets: 0} 41 | ]) 42 | }) 43 | 44 | }) 45 | 46 | describe('#getSinks/getSources', function() { 47 | 48 | it('should get the sinks rightly', function() { 49 | var patch = new Patch({ 50 | nodes: [ {id: 78, attr1: 90}, {id: 56, attr1: 88}, {id: 2, attr1: 5}, {id: 32, attr1: 9} ], 51 | connections: [ 52 | {source: {id: 78, port: 2}, sink: {id: 56, port: 1}}, 53 | {source: {id: 78, port: 2}, sink: {id: 56, port: 0}}, 54 | {source: {id: 78, port: 1}, sink: {id: 2, port: 0}}, 55 | {source: {id: 32, port: 0}, sink: {id: 78, port: 1}} 56 | ] 57 | }) 58 | 59 | var sinks = patch.getSinks({id: 78}) 60 | assert.equal(sinks.length, 2) 61 | assert.deepEqual(sinks[0], {id: 56, attr1: 88}) 62 | assert.deepEqual(sinks[1], {id: 2, attr1: 5}) 63 | 64 | var sinks = patch.getSinks({id: 2}) 65 | assert.equal(sinks.length, 0) 66 | }) 67 | 68 | it('should get the sources rightly', function() { 69 | var patch = new Patch({ 70 | nodes: [ {id: 78, attr1: 90}, {id: 56, attr1: 88}, {id: 2, attr1: 5}, {id: 32, attr1: 9} ], 71 | connections: [ 72 | {source: {id: 78, port: 2}, sink: {id: 56, port: 1}}, 73 | {source: {id: 56, port: 1}, sink: {id: 2, port: 0}}, 74 | {source: {id: 56, port: 0}, sink: {id: 2, port: 1}}, 75 | {source: {id: 32, port: 0}, sink: {id: 2, port: 1}} 76 | ] 77 | }) 78 | 79 | var sources = patch.getSources({id: 2}) 80 | assert.equal(sources.length, 2) 81 | assert.deepEqual(sources[0], {id: 56, attr1: 88}) 82 | assert.deepEqual(sources[1], {id: 32, attr1: 9}) 83 | 84 | var sources = patch.getSources({id: 78}) 85 | assert.equal(sources.length, 0) 86 | }) 87 | 88 | }) 89 | 90 | describe('#nextId', function() { 91 | 92 | it('should pick the next free id', function() { 93 | var patch = new Patch({ 94 | nodes: [ {id: 78, attr1: 90}, {id: 56, attr1: 88}, {id: 2, attr1: 5}, {id: 32, attr1: 9} ], 95 | }) 96 | assert.equal(patch.nextId(), 79) 97 | 98 | var patch = new Patch({ 99 | nodes: [ {id: 56, attr1: 88}, {id: 2, attr1: 5}, {id: 32, attr1: 9} ], 100 | }) 101 | assert.equal(patch.nextId(), 57) 102 | }) 103 | 104 | it('should pick 0 if there\'s no node', function() { 105 | var patch = new Patch({ nodes: [] }) 106 | assert.equal(patch.nextId(), 0) 107 | }) 108 | 109 | }) 110 | 111 | describe('#addNode', function() { 112 | 113 | it('should assign an id if the node doesn\'t have one', function() { 114 | var patch = new Patch({ 115 | nodes: [ {id: 0, attr1: 90}, {id: 1, attr1: 88}, {id: 2, attr1: 5} ], 116 | }) 117 | patch.addNode({attr1: 99}) 118 | assert.deepEqual(patch.nodes, [ {id: 0, attr1: 90}, {id: 1, attr1: 88}, {id: 2, attr1: 5}, {id: 3, attr1: 99} ]) 119 | }) 120 | 121 | it('should insert the node as is, if it has an id', function() { 122 | var patch = new Patch({ 123 | nodes: [ {id: 0, attr1: 90}, {id: 1, attr1: 88}, {id: 2, attr1: 5} ], 124 | }) 125 | patch.addNode({attr1: 99, id: 43}) 126 | assert.deepEqual(patch.nodes, [ {id: 0, attr1: 90}, {id: 1, attr1: 88}, {id: 2, attr1: 5}, {id: 43, attr1: 99} ]) 127 | }) 128 | 129 | it('should do nothing if the node is already in there', function() { 130 | var patch = new Patch({ 131 | nodes: [ {id: 0, attr1: 90}, {id: 1, attr1: 88}, {id: 2, attr1: 5} ], 132 | }) 133 | patch.addNode({attr1: 99, id: 2}) 134 | assert.deepEqual(patch.nodes, [ {id: 0, attr1: 90}, {id: 1, attr1: 88}, {id: 2, attr1: 5} ]) 135 | }) 136 | 137 | }) 138 | 139 | }) 140 | -------------------------------------------------------------------------------- /test/parsing-test.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | , fs = require('fs') 3 | , assert = require('assert') 4 | , parsing = require('pd-fileutils.parser') 5 | 6 | // round a number to a given number of decimal places 7 | var round = function(num, dec) { 8 | dec = dec || 4 9 | var f = Math.pow(10, dec) 10 | return Math.round(num * f) / f 11 | } 12 | 13 | // apply round to all elements of an array 14 | var roundArray = function(array, dec) { 15 | var roundedArray = [] 16 | for (var i=0; i 2 | 3 | 4 | 14 | 15 | 16 | {{{images}}} 17 | 18 | -------------------------------------------------------------------------------- /test/patches/graphs.pd: -------------------------------------------------------------------------------- 1 | #N canvas 49 82 450 300 10; 2 | #N canvas 0 0 450 300 (subpatch) 0; 3 | #X coords 0 1 100 -1 200 140 1; 4 | #X restore 100 20 graph; 5 | -------------------------------------------------------------------------------- /test/patches/node-elems.pd: -------------------------------------------------------------------------------- 1 | #N canvas 361 203 838 432 10; 2 | #X floatatom 73 84 5 0 0 0 - - -; 3 | #X msg 73 43 89; 4 | #X obj 142 42 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 5 | -1; 6 | #X obj 144 85 tgl 15 1 tglSendBla tglRcvBla empty 17 7 0 4 -262144 7 | -1 -262144 10 10; 8 | #X obj 180 42 nbx 5 14 -1e+37 1e+37 0 1 empty empty empty 0 -8 0 10 9 | -262144 -1 -1 56789 256; 10 | #X obj 242 86 hsl 128 15 0 1270 0 1 empty empty empty -2 -8 0 10 -262144 11 | -1 -1 5800 1; 12 | #X obj 249 137 vradio 15 1 0 8 empty empty empty 0 -8 0 10 -262144 13 | -1 -1 0; 14 | #X obj 89 141 vu 15 120 empty empty -1 -8 0 10 -66577 -1 1 0; 15 | #X obj 317 154 cnv 15 100 60 empty empty empty 20 12 0 14 -233017 -66577 16 | 0; 17 | #X symbolatom 255 38 10 0 0 0 - - -; 18 | #X obj 458 62 vsl 15 128 0 12700 0 1 empty empty empty 0 -9 0 10 -262144 19 | -1 -1 9500 1; 20 | #X obj 69 311 hradio 15 1 0 8 empty empty empty 0 -8 0 10 -262144 -1 21 | -1 0; 22 | #X text 205 308 < this comment should be aligned to the hradio; 23 | #X connect 1 0 0 0; 24 | #X connect 2 0 0 0; 25 | #X connect 6 0 4 0; 26 | -------------------------------------------------------------------------------- /test/patches/object-size-pd-vanilla.pd: -------------------------------------------------------------------------------- 1 | #N canvas 123 189 450 300 10; 2 | #X floatatom 80 38 30 0 0 0 - - -, f 30; 3 | #X obj 80 71 pack f f, f 40; 4 | -------------------------------------------------------------------------------- /test/patches/simple.pd: -------------------------------------------------------------------------------- 1 | #N canvas 778 17 450 300 10; 2 | #X obj 14 13 loadbang; 3 | #X obj 14 34 print bla; 4 | #X connect 0 0 1 0; 5 | -------------------------------------------------------------------------------- /test/patches/special-chars.pd: -------------------------------------------------------------------------------- 1 | #N canvas 49 82 450 300 10; 2 | #X obj 17 15 expr if ($f1 < 0 \, $f1 \, 0) \; $f2; 3 | -------------------------------------------------------------------------------- /test/patches/subpatches.pd: -------------------------------------------------------------------------------- 1 | #N canvas 340 223 450 300 10; 2 | #X obj 78 81 osc~; 3 | #N canvas 447 260 450 300 mySubpatch 1; 4 | #X obj 46 39 inlet~; 5 | #X obj 47 83 delwrite~ myDel; 6 | #X obj 47 126 delread~ myDel; 7 | #X obj 48 165 outlet~; 8 | #N canvas 842 260 450 300 subSubPatch 1; 9 | #X obj 67 67 outlet~; 10 | #X obj 66 32 phasor~ -440; 11 | #X connect 1 0 0 0; 12 | #X restore 183 83 pd subSubPatch; 13 | #X connect 0 0 1 0; 14 | #X connect 2 0 3 0; 15 | #X restore 79 117 pd subPatch; 16 | #X obj 80 175 dac~; 17 | #X connect 0 0 1 0; 18 | #X connect 1 0 2 0; 19 | #X connect 1 0 2 1; 20 | -------------------------------------------------------------------------------- /test/patches/tables.pd: -------------------------------------------------------------------------------- 1 | #N canvas 114 400 450 300 10; 2 | #X obj 290 57 table myTable 35; 3 | #X obj 40 250 osc~ 440; 4 | -------------------------------------------------------------------------------- /test/patches/tall-and-wide-patch.pd: -------------------------------------------------------------------------------- 1 | #N canvas 49 54 1317 714 10; 2 | #X obj 1377 752 print; 3 | #X msg -120 -119 1; 4 | #X connect 1 0 0 0; 5 | -------------------------------------------------------------------------------- /test/pd-rendering-test.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | , fs = require('fs') 3 | , assert = require('assert') 4 | , parsing = require('pd-fileutils.parser') 5 | , pdRendering = require('../lib/pd-rendering') 6 | , NEWLINE_REGEX = /\r?\n/; 7 | 8 | describe('pd-rendering', function() { 9 | 10 | describe('#render', function() { 11 | 12 | it('should succeed parsing/rendering all test patches identically', function() { 13 | var patchFile = fs.readFileSync(path.join(__dirname, 'patches', 'simple.pd')).toString() 14 | , simplePatch = parsing.parse(patchFile) 15 | assert.deepEqual( 16 | pdRendering.render(simplePatch).split(NEWLINE_REGEX), 17 | patchFile.split(NEWLINE_REGEX) 18 | ); 19 | }) 20 | 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /test/rendered/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /test/svg-rendering-test.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | , fs = require('fs') 3 | , JSDOM = require('jsdom').JSDOM 4 | , mustache = require('mustache') 5 | , parsing = require('pd-fileutils.parser') 6 | , svg = require('../lib/svg-rendering') 7 | 8 | // Set global document for d3 9 | global.document = (new JSDOM('...')).window.document 10 | 11 | describe('svg-rendering', function() { 12 | 13 | describe('#render', function() { 14 | 15 | it('should succeed rendering a patch', function() { 16 | svg.render({ 17 | nodes: [ 18 | {id: 0, proto: 'loadbang', args: [], layout: {x: 14, y: 13}}, 19 | {id: 1, proto: 'print', args: ['bla'], layout: {x: 14, y: 34}}, 20 | ], 21 | connections: [ 22 | { source: {id: 0, port: 0}, sink: {id: 1, port: 0} } 23 | ] 24 | }) 25 | }) 26 | 27 | it('should succeed rendering all test patches', function() { 28 | // Render svgs 29 | var svgFilenames = [] 30 | fs.readdirSync(path.join(__dirname, 'patches')) 31 | .filter(function(filename) { return filename.slice(-3) === '.pd' }) 32 | .forEach(function(filename) { 33 | var patchStr = fs.readFileSync(path.join(__dirname, 'patches', filename)).toString() 34 | , patch = parsing.parse(patchStr) 35 | , svgFilename = filename.slice(0, -3) + '.svg' 36 | svgFilenames.push(svgFilename) 37 | fs.writeFileSync(path.join(__dirname, 'rendered', svgFilename), svg.render(patch)) 38 | }) 39 | 40 | // Put them all in a single HTML page 41 | var template = fs.readFileSync(path.join(__dirname, 'patches', 'embedded-in-a-page.hbs')).toString() 42 | , images = '' 43 | , htmlFilename = path.join(__dirname, 'rendered', 'embedded-in-a-page.html') 44 | svgFilenames.forEach(function(filename) { images += '' }) 45 | fs.writeFileSync(htmlFilename, mustache.render(template, { images: images })) 46 | }) 47 | 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './browser.js', 5 | output: { 6 | path: path.resolve(__dirname, 'dist'), 7 | filename: 'pd-fileutils-latest.js' 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.css$/i, 13 | use: ['to-string-loader', 'css-loader'], 14 | }, 15 | ] 16 | } 17 | } --------------------------------------------------------------------------------