├── .gitignore ├── LICENSE ├── README.markdown ├── andro-min.js ├── andro.js ├── index.html ├── package.json ├── resources ├── favicon.png ├── main.css ├── prettify.css └── prettify.js ├── scripts └── minifier.js └── spec ├── andro.spec.js └── eventer.spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2014 Mary Rose Cook and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Andro.js 2 | 3 | * by mary rose cook 4 | * http://maryrosecook.com 5 | * maryrosecook@maryrosecook.com 6 | 7 | Mix behaviours into objects. 8 | 9 | ## What is Andro.js? 10 | 11 | Andro.js takes mixins and applies them, each in its own namespace, to an object, and lets them talk to one another via an event emitter. 12 | 13 | ## Come again? 14 | 15 | Imagine a cube. It can be touched. You want it to make a sound when it goes from not being touched to being touched. You write a little behaviour that emits an event when a first touch occurs. You write another little behaviour that plays a sound when it receives a first touch event. You combine these behaviours on your cube with Andro.js. 16 | 17 | ## Get the code 18 | 19 | * Minified: https://raw.github.com/maryrosecook/androjs/master/andro-min.js 20 | * Single file: https://raw.github.com/maryrosecook/androjs/master/andro.js 21 | * GitHub: https://github.com/maryrosecook/androjs 22 | * npm: `$ npm install androjs` 23 | 24 | ## Get started 25 | 26 | Download the repository. Require the `andro.js` file in your code. Open `index.html` in your browser to see the documentation and an example. 27 | 28 | ## Run the tests 29 | 30 | Install Node.js and npm. 31 | 32 | Install the node dependencies 33 | 34 | $ cd path/to/androjs 35 | $ npm install 36 | 37 | Run the tests 38 | 39 | $ cd path/to/androjs 40 | $ npm test 41 | 42 | ## Licence 43 | 44 | Andro.js is open source, under the MIT licence. 45 | -------------------------------------------------------------------------------- /andro-min.js: -------------------------------------------------------------------------------- 1 | (function(exports){exports.andro={tearDown:function(owner){if(isSetup(owner)){this.eventer(owner).unbindAll();delete owner.andro}},augment:function(owner,behaviourMixin,settings){if(behaviourMixin===undefined){throw"You must pass a behaviour with which to augment the owner."}setupIfNotSetup(owner);var behaviour={};owner.andro.behaviours.push(behaviour);extend(behaviour,behaviourMixin);if(behaviour.setup!==undefined){settings=settings||{};var exports=behaviour.setup(owner,owner.andro.eventer,settings);for(var name in exports){if(owner[name]===undefined){owner[name]=makeFn(exports[name],behaviour)}else{throw"Behaviour export would overwrite existing attribute on owner."}}}},eventer:function(owner){setupIfNotSetup(owner);return owner.andro.eventer}};var setupIfNotSetup=function(owner){if(!isSetup(owner)){owner.andro={};owner.andro.behaviours=[];owner.andro.eventer=new Eventer}};var isSetup=function(owner){return owner.andro!==undefined};var extend=function(target,extensions){for(var property in extensions){if(extensions[property]&&extensions[property].constructor&&extensions[property].constructor===Object){target[property]=extend(target[property]||{},extensions[property])}else{target[property]=extensions[property]}}return target};var makeFn=function(fn,behaviour){return function(){return fn.apply(behaviour,arguments)}};function Eventer(){this.callbacks={}}Eventer.prototype={bind:function(obj,event,callback){this.callbacks[event]=this.callbacks[event]||[];this.callbacks[event].push({obj:obj,callback:callback});return this},unbind:function(obj,event){for(var i=0;i 2 | 3 | 4 | Andro.js 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

Andro.js

13 | 14 |
Mix behaviours into objects
15 | 16 |

What is Andro.js?

17 | 18 |

19 | Andro.js takes mixins and applies them, each in its own namespace, to an object, and lets them talk to one another via an event emitter. 20 |

21 | 22 |

Come again?

23 | Imagine a cube. It can be touched. You want this cube to make a sound when it goes from not being touched to being touched. You write a little behaviour that emits an event when a first touch occurs. You write another little behaviour that plays a sound when it receives a first touch event. You combine these behaviours on your cube with Andro.js. 24 |

25 | 26 |

27 | Or, to put it another way, Andro.js a library for objects that can't quite decide who they are. 28 |

29 | 30 |

Get the code

31 | 39 | 40 |

Example

41 | 42 |

43 | I require the andro.js file. I define the owner object as a constructor called Cube. It has a touch function that, when called, uses the andro.eventer() function to get the eventer to emit a touch event to all behaviours attached to the cube. 44 |

45 | 46 |
 47 | var andro = require('andro').andro;
 48 | 
 49 | function Cube() {
 50 |   this.touch = function(contact) {
 51 |     andro.eventer(this).emit("touch", contact);
 52 |   };
 53 | };
 54 | 
55 | 56 |

57 | I define firstTouchBehaviour. This binds to the touch events emitted by its owner and keeps track of the number of things currently in contact. When the owner goes from being untouched to being touched, firstTouchBehaviour emits a FirstTouch:newlyBeingTouched event. 58 |

59 | 60 |
 61 | var firstTouchBehaviour = {
 62 |   touchCount: 0,
 63 | 
 64 |   setup: function(owner, eventer) {
 65 |     eventer.bind(this, "touch", function(contact) {
 66 |       if(contact === "added") {
 67 |         if(this.touchCount === 0) {
 68 |           eventer.emit("FirstTouch:newlyBeingTouched");
 69 |         }
 70 |         this.touchCount++;
 71 |       } else if(contact === "removed") {
 72 |         this.touchCount--;
 73 |       }
 74 |     });
 75 |   }
 76 | };
 77 | 
78 | 79 |

80 | I define soundBehaviour. This binds to the FirstTouch:newlyBeingTouched event. Each time this event occurs, soundBehaviour makes a noise: "Rarrrrrwwwwwwwwwwwwwwww". 81 |

82 | 83 |
 84 | var soundBehaviour = {
 85 |   setup: function(owner, eventer) {
 86 |     eventer.bind(this, "FirstTouch:newlyBeingTouched", function() {
 87 |       console.log("Rarrrrrwwwwwwwwwwwwwwww");
 88 |     });
 89 |   }
 90 | };
 91 | 
92 | 93 |

94 | I now put everything together. I instantiate cube. I augment it with firstTouchBehaviour and soundBehaviour. I simulate two touches upon the cube. On the first, it roars. On the second, it does not. 95 |

96 | 97 |
 98 | var cube = new Cube();
 99 | andro.augment(cube, firstTouchBehaviour);
100 | andro.augment(cube, soundBehaviour);
101 | 
102 | cube.touch("added"); // rarrrww
103 | cube.touch("added"); // silence
104 | 
105 | 106 |

Why is this cool?

107 | 108 |

109 | Behaviours are completely separate from each other, so they are reusable. You could write explodeBehaviour, combine it with firstTouchBehaviour and have exploding cubes. Or you could write wetBehaviour, combine it with explodey and touchey and have depth charges. 110 |

111 | 112 |

113 | Behaviour mixins are good for writing video games. They help with the concoction of game object logic: no more nightmarish inheritance hierarchies or weird bundles of functions that invent cryptic interfaces for the objects upon which they operate. 114 |

115 | 116 |

117 | See the end of this document for why Andro.js might not be cool. 118 |

119 | 120 |

Reference

121 | 122 |

Add a behaviour to an owner object

123 |

124 | A behaviour is a JavaScript object that has some properties and functions. It can be an empty object, if you like. Here is a behaviour that is not an empty object, but still does absolutely nothing: 125 |

126 | 127 |
128 | var wooBehaviour = {
129 |   setup: function(owner, eventer, settings) {
130 |     this.owner = owner;
131 |   }
132 | };
133 | 
134 |

135 | We add this behaviour to the owner object by calling augment() and passing in ownerObject, wooBehaviour and an optional settings object. This creates a behaviour object, adds it to the ownerObject.behaviours array and writes the properties and functions of the behaviour to it. 136 |

137 | 138 |
139 | var ownerObject = {};
140 | andro.augment(ownerObject, wooBehaviour, {
141 |   followUp: "hoo"
142 | });
143 | 
144 | 145 |

146 | wooBehaviour includes the optional function, setup(), that will automatically be run when we call augment(). When it is run, setup() receives an owner argument that is bound to the owner of the behaviour, and the optional settings object, if one was passed to augment(). 147 |

148 | 149 |

Bind a behaviour to an event

150 | 151 |

152 | We can improve the version of wooBehaviour from the previous section so that, when it receives an event, woo, it goes "woo": 153 |

154 | 155 |
156 | var wooBehaviour = {
157 |   setup: function(owner, eventer, settings) {
158 |     this.owner = owner;
159 |     eventer.bind(this, "woo", function() {
160 |       console.log("Woo.");
161 |     });
162 |   }
163 | };
164 | 
165 | 166 |

167 | The setup() function now calls bind, passing the behaviour, event name and a callback. Now, whenever the event, woo, is emitted, our behaviour will go, "woo". 168 |

169 | 170 |

Emit events from a behaviour

171 | 172 |

173 | We can further improve the wooBehaviour from the previous section. As well as going "woo", it will emit an event, hoo. 174 |

175 | 176 |
177 | var wooBehaviour = {
178 |   setup: function(owner, eventer, settings) {
179 |     this.owner = owner;
180 |     this.eventer = eventer;
181 |     this.followUp = settings.followUp;
182 |     eventer.bind(this, "woo", this.woo);
183 |   },
184 | 
185 |   woo: function() {
186 |     console.log("Woo.");
187 |     this.eventer.emit(this.followUp, "Woo");
188 |   }
189 | };
190 | 
191 | 192 |

193 | We emitted the hoo event that was passed in via settings. We sent along an identifying string, Woo, as data. That way, anyone who binds to the hoo event will know who is doing the hooing. 194 |

195 | 196 |

Unbind a behaviour from an event

197 | 198 |

199 | Let's say we become worried that we are going a bit mental with the wooing. We can alter our behaviour so that, after wooing once, it unbinds from the woo event, thus cutting out any future woos. 200 |

201 | 202 |
203 | woo: function() {
204 |   console.log("Woo.");
205 |   this.eventer.emit(this.followUp, "Woo");
206 |   this.eventer.unbind(this, "woo");
207 | }
208 | 
209 | 210 |

211 | See how on line four it calls unbind on the eventer, passing in the behaviour and the event to unbind from? 212 |

213 | 214 |

Export from a behaviour

215 | 216 |

217 | To gild the lily, we will make wooBehaviour respond to enquiries about whether "woo" has been said. To do this, we add an attribute called hasWooed and alter the woo function to set hasWooed to true. We add a function, getHasWooed(), that returns hasWooed. Finally, we alter setup() so that it returns an object that includes getHasWooed. This function will get written onto the owner object so it can be used by the owner, or by other objects. 218 |

219 | 220 |
221 | var wooBehaviour = {
222 |   hasWooed: false,
223 | 
224 |   setup: function(owner, eventer, settings) {
225 |     this.owner = owner;
226 |     this.eventer = eventer;
227 |     this.followUp = settings.followUp;
228 |     eventer.bind(this, "woo", this.woo);
229 | 
230 |     return {
231 |       "getHasWooed": this.getHasWooed
232 |     };
233 |   },
234 | 
235 |   woo: function() {
236 |     console.log("Woo.");
237 |     this.hasWooed = true;
238 |     eventer.emit(this.followUp, "Woo");
239 |     eventer.unbind(this, "woo");
240 |   },
241 | 
242 |   getHasWooed: function() {
243 |     return this.hasWooed;
244 |   }
245 | };
246 | 
247 | 248 |

Remove Andro augmentation

249 | 250 |

251 | When we grow tired of all the wooing and hooing, we can restore our faithful old object to its pre-Andro state by calling tearDown(). This will remove the andro object from the owner object and unbind all event callbacks. 252 |

253 | 254 |
255 | andro.tearDown(ownerObject);
256 | 
257 | 258 |

Why might this not be cool?

259 | 260 |

261 | Andro.js contains four modes of expression: mixins, exports, settings and event emitters. The library protects you from harming your owner objects: behaviours are name-spaced and you cannot give an export the same name as an existing owner attribute. However, armed with an event emitter and mixins, you can still express yourself into a hell of a mess. Therefore, I humbly offer you some things that seem to be true more often than not. 262 |

263 | 264 |

265 | Behaviours should have one responsbility: flash a light, make a sound, register touches. 266 |

267 | 268 |

269 | Status behaviours are good. That is, behaviours that solely talk about what is going on. Like, "My owner is no longer being touched by anything". 270 |

271 |

272 | Status-consuming behaviours are also good. Like, "Every time I hear that my owner has been touched, I make a sound." 273 |

274 |

275 | Exported functions that allow the interrogation of state are OK. Like, "Here you are, owner. Here is a function that others can use to find out if anything is touching you." 276 |

277 |

278 | Changing the state of the owner, either via an exported function or from within a behaviour: bad. 279 |

280 |

281 | Behaviours that mediate between other behaviours are hard to understand. Like, "Every time I hear my owner has been touched, I emit an event that tells the sound behaviour to pipe up." Use these sparingly and name them well. 282 |

283 | 284 |

Licence

285 | 286 |

287 | The code is open source, under the MIT licence. 288 |

289 | 290 |
291 | 294 | 295 | 296 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "androjs", 3 | "version": "0.3.5", 4 | "scripts": { 5 | "test": "node_modules/jasmine-node/bin/jasmine-node spec/", 6 | "minify": "node scripts/minifier.js", 7 | "prepublish": "node scripts/minifier.js" 8 | }, 9 | "devDependencies": { 10 | "node.packer": "2.0.0", 11 | "jasmine-node": "1.7.1" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /resources/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maryrosecook/androjs/92316c4c99d039c3bb1cabc01e3334ec7c621b4f/resources/favicon.png -------------------------------------------------------------------------------- /resources/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family:"Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif; 3 | font-size:12px; 4 | width: 500px; 5 | margin-left:auto; 6 | margin-right:auto; 7 | text-align: justify; 8 | } 9 | 10 | a { color:black; } 11 | a:visited { color:black; } 12 | 13 | h1 { font-size: 55px; font-weight:normal; margin-top:0; padding:0; display:inline; 14 | letter-spacing: 1px; } 15 | h2 { font-size: 26px; font-weight:normal; margin:0; margin-top:16px; margin-bottom:2px; } 16 | h3 { font-size: 16px; font-weight:normal; } 17 | h4 { font-size: 13px; font-weight:normal; } 18 | 19 | p { line-height:15px; } 20 | 21 | hr { color: #aaa; background-color:#aaa; height:1px; border:0; } 22 | 23 | .footer { width:100px; margin-left: auto; margin-right: auto; } 24 | 25 | .byline { margin-left: 208px; margin-top:-14px; letter-spacing: 1px; } 26 | .strapline { margin-top:-11px; letter-spacing: 0.6px; } 27 | 28 | ol { padding-left: 16px; } 29 | li { padding: 5px 0 5px 0; } 30 | 31 | code { 32 | font-family: 'monaco', 'courier'; 33 | } 34 | 35 | pre { 36 | border-top:1px solid #ddd; 37 | border-bottom:1px solid #ddd; 38 | padding-top:15px; 39 | padding-left:-50px; 40 | margin-bottom:15px; 41 | padding: 1em 0; 42 | font-size:12px; 43 | font-family: 'monaco', 'courier'; 44 | } 45 | -------------------------------------------------------------------------------- /resources/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#090}.kwd{color:#11c}.com{color:#666}.typ{color:#099}.lit{color:#f07}.pun,.opn,.clo{color:#555}.tag{color:#11c}.atn{color:#099}.atv{color:#090}.dec,.var{color:#00b}.fun{color:red}}@media print,projection{.str{color:#090}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /resources/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p