├── .gitignore
├── src
├── pacman.png
├── audio
│ ├── die.mp3
│ ├── die.ogg
│ ├── eating.mp3
│ ├── eating.ogg
│ ├── siren.mp3
│ ├── siren.ogg
│ ├── vcs_90.mp3
│ ├── vcs_90.ogg
│ ├── eatghost.mp3
│ ├── eatghost.ogg
│ ├── eatpill.mp3
│ ├── eatpill.ogg
│ ├── extra lives.mp3
│ ├── extra lives.ogg
│ ├── eating.short.mp3
│ ├── eating.short.ogg
│ ├── intermission.mp3
│ ├── intermission.ogg
│ ├── opening_song.mp3
│ └── opening_song.ogg
├── BD_Cartoon_Shout-webfont.ttf
├── index.html
├── modernizr-1.5.min.js
└── pacman.js
├── .vscode
└── extensions.json
├── .gitpod.yml
├── .sasjslint
├── package.json
├── .releaserc
├── sasjs
└── sasjsconfig.json
├── CHANGELOG.md
├── .github
└── workflows
│ └── release.yml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | sasjsbuild/
3 | .env*
4 |
5 | sasjsresults/
6 |
--------------------------------------------------------------------------------
/src/pacman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/pacman.png
--------------------------------------------------------------------------------
/src/audio/die.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/die.mp3
--------------------------------------------------------------------------------
/src/audio/die.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/die.ogg
--------------------------------------------------------------------------------
/src/audio/eating.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eating.mp3
--------------------------------------------------------------------------------
/src/audio/eating.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eating.ogg
--------------------------------------------------------------------------------
/src/audio/siren.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/siren.mp3
--------------------------------------------------------------------------------
/src/audio/siren.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/siren.ogg
--------------------------------------------------------------------------------
/src/audio/vcs_90.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/vcs_90.mp3
--------------------------------------------------------------------------------
/src/audio/vcs_90.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/vcs_90.ogg
--------------------------------------------------------------------------------
/src/audio/eatghost.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eatghost.mp3
--------------------------------------------------------------------------------
/src/audio/eatghost.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eatghost.ogg
--------------------------------------------------------------------------------
/src/audio/eatpill.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eatpill.mp3
--------------------------------------------------------------------------------
/src/audio/eatpill.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eatpill.ogg
--------------------------------------------------------------------------------
/src/audio/extra lives.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/extra lives.mp3
--------------------------------------------------------------------------------
/src/audio/extra lives.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/extra lives.ogg
--------------------------------------------------------------------------------
/src/audio/eating.short.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eating.short.mp3
--------------------------------------------------------------------------------
/src/audio/eating.short.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eating.short.ogg
--------------------------------------------------------------------------------
/src/audio/intermission.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/intermission.mp3
--------------------------------------------------------------------------------
/src/audio/intermission.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/intermission.ogg
--------------------------------------------------------------------------------
/src/audio/opening_song.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/opening_song.mp3
--------------------------------------------------------------------------------
/src/audio/opening_song.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/opening_song.ogg
--------------------------------------------------------------------------------
/src/BD_Cartoon_Shout-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sasjs/pacman/main/src/BD_Cartoon_Shout-webfont.ttf
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "sasjs.sasjs-for-vscode",
4 | "hediet.vscode-drawio"
5 | ]
6 | }
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | tasks:
2 | - init: npm install -g npm
3 | - command: npm i -g @sasjs/cli && npm i
4 |
5 | image:
6 | file: .gitpod.dockerfile
7 | vscode:
8 | extensions:
9 | - sasjs.sasjs-for-vscode
10 | - ritwickdey.liveserver
--------------------------------------------------------------------------------
/.sasjslint:
--------------------------------------------------------------------------------
1 | {
2 | "noEncodedPasswords": true,
3 | "hasDoxygenHeader": true,
4 | "hasMacroNameInMend": false,
5 | "hasMacroParentheses": true,
6 | "indentationMultiple": 2,
7 | "lowerCaseFileNames": true,
8 | "maxLineLength": 80,
9 | "noNestedMacros": true,
10 | "noSpacesInFileNames": true,
11 | "noTabIndentation": true,
12 | "noTrailingSpaces": true
13 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sasjs/pacman",
3 | "repository": "https://github.com/sasjs/pacman",
4 | "version": "1.0.0",
5 | "description": "Pacman for SAS!",
6 | "keywords": [
7 | "SAS",
8 | "SASViya",
9 | "SASjs"
10 | ],
11 | "author": "",
12 | "devDependencies": {
13 | "@sasjs/cli": "^3.15.0",
14 | "@semantic-release/changelog": "^6.0.1",
15 | "@semantic-release/exec": "^6.0.3",
16 | "@semantic-release/git": "^10.0.1",
17 | "@semantic-release/github": "^8.0.4"
18 | }
19 | }
--------------------------------------------------------------------------------
/.releaserc:
--------------------------------------------------------------------------------
1 | {
2 | "branches": [
3 | "main"
4 | ],
5 | "plugins": [
6 | "@semantic-release/commit-analyzer",
7 | "@semantic-release/release-notes-generator",
8 | "@semantic-release/changelog",
9 | [
10 | "@semantic-release/git",
11 | {
12 | "assets": [
13 | "CHANGELOG.md"
14 | ]
15 | }
16 | ],
17 | [
18 | "@semantic-release/github",
19 | {
20 | "assets": [
21 | {
22 | "path": "./artefacts/server.json.zip",
23 | "label": "SASjs Streamed App (Zip File)"
24 | },
25 | {
26 | "path": "./artefacts/sas9.sas",
27 | "label": "SAS 9 EBI Deployment (SAS Program)"
28 | },
29 | {
30 | "path": "./artefacts/viya.sas",
31 | "label": "Viya Deployment (SAS Program)"
32 | }
33 | ]
34 | }
35 | ],
36 | [
37 | "@semantic-release/exec",
38 | {
39 | "publishCmd": "echo 'publish command'"
40 | }
41 | ]
42 | ]
43 | }
--------------------------------------------------------------------------------
/sasjs/sasjsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/sasjs/utils/main/src/types/sasjsconfig-schema.json",
3 | "streamConfig": {
4 | "streamWeb": true,
5 | "streamWebFolder": "web",
6 | "webSourcePath": "src",
7 | "streamServiceName": "Pacman",
8 | "streamLogo": "pacman.png"
9 | },
10 | "defaultTarget": "server",
11 | "targets": [
12 | {
13 | "name": "viya",
14 | "serverUrl": "",
15 | "serverType": "SASVIYA",
16 | "httpsAgentOptions": {
17 | "allowInsecureRequests": false
18 | },
19 | "appLoc": "/Public/app/pacman",
20 | "contextName": "SAS Job Execution compute context"
21 | },
22 | {
23 | "name": "sas9",
24 | "serverUrl": "",
25 | "serverType": "SAS9",
26 | "appLoc": "/Public/app/pacman",
27 | "serverName": "SASApp",
28 | "repositoryName": "Foundation"
29 | },
30 | {
31 | "name": "server",
32 | "serverUrl": "",
33 | "serverType": "SASJS",
34 | "appLoc": "/Public/app/pacman",
35 | "deployConfig": {
36 | "deployServicePack": true
37 | }
38 | }
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.0.4](https://github.com/sasjs/pacman/compare/v1.0.3...v1.0.4) (2022-08-03)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * tweaks to get URLs to work on Viya ([57551a3](https://github.com/sasjs/pacman/commit/57551a316a386678ef5149e3543994392bb5c36e))
7 |
8 | ## [1.0.3](https://github.com/sasjs/pacman/compare/v1.0.2...v1.0.3) (2022-08-03)
9 |
10 |
11 | ### Bug Fixes
12 |
13 | * JS in pacman ([6fb662a](https://github.com/sasjs/pacman/commit/6fb662a3e20093a20087e7760e92c0dedf8ed71d))
14 |
15 | ## [1.0.2](https://github.com/sasjs/pacman/compare/v1.0.1...v1.0.2) (2022-08-02)
16 |
17 |
18 | ### Bug Fixes
19 |
20 | * minor adjustments ([2b0fec4](https://github.com/sasjs/pacman/commit/2b0fec46f5bc40f2d97a794995c76efe396ba73d))
21 |
22 | ## [1.0.1](https://github.com/sasjs/pacman/compare/v1.0.0...v1.0.1) (2022-08-02)
23 |
24 |
25 | ### Bug Fixes
26 |
27 | * link in readme, logo in sasjs/server ([471b193](https://github.com/sasjs/pacman/commit/471b1936fd900e07cae7d2ebeaccbdb82b230ed5))
28 |
29 | # 1.0.0 (2022-08-02)
30 |
31 |
32 | ### Bug Fixes
33 |
34 | * move original files to src folder ([aa91ded](https://github.com/sasjs/pacman/commit/aa91dedda9cc02c926c57d682524ccd2e4d1d3a5))
35 | * semantic release ([984505a](https://github.com/sasjs/pacman/commit/984505a542bc29d46957745391c68e81a81ae392))
36 |
37 |
38 | ### Features
39 |
40 | * remaining assets ([6657f22](https://github.com/sasjs/pacman/commit/6657f22d43a4c09209ba416ae578a7022d78d405))
41 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Push SASjs Deployment Assets
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 |
12 | strategy:
13 | matrix:
14 | node-version: [lts/*]
15 |
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v2
19 |
20 | - name: Use Node.js ${{ matrix.node-version }}
21 | uses: actions/setup-node@v2
22 | with:
23 | node-version: ${{ matrix.node-version }}
24 |
25 | - name: Install dependencies
26 | run: npm install
27 | env:
28 | CI: true
29 |
30 | - name: create SAS 9 EBI build and Add to Release
31 | run: |
32 | npx sasjs cb -t sas9
33 | mkdir ./artefacts
34 | cp ./sasjsbuild/sas9.sas ./artefacts/sas9.sas
35 |
36 | - name: create Viya build and Add to Release
37 | run: |
38 | npx sasjs cb -t viya
39 | cp ./sasjsbuild/viya.sas ./artefacts/viya.sas
40 |
41 | - name: create SASjs build and Add to Release
42 | run: |
43 | npx sasjs cb -t server
44 | cp ./sasjsbuild/server.json.zip ./artefacts/server.json.zip
45 |
46 | - name: Install Semantic Release and plugins
47 | run: |
48 | npm i
49 | npm i -g semantic-release
50 | - name: Release
51 | run: |
52 | GITHUB_TOKEN=${{ secrets.GH_TOKEN }} semantic-release
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pacman for SAS
2 |
3 | Thanks to [Dale Harvey](https://github.com/daleharvey) for this very [permissively licensed](https://github.com/daleharvey/pacman/blob/master/LICENSE) pacman game!
4 |
5 | 
6 |
7 | The aim of this repo is to demonstrate the ease with which apps can be streamed in SAS Viya, EBI, and SASjs Server.
8 |
9 | ## Deployment
10 |
11 | If you just want to deploy the game in your SAS environment, in all cases you need to download the relevant asset from the [releases](https://github.com/sasjs/pacman/releases) page.
12 |
13 | * SAS 9 - Open `sas9.sas` in Enterprise Guide or Studio. Modify line 2 with your preferred appLoc. Execute and open the link at the end of the log.
14 | * Viya - Open `viya.sas` in SAS Studio. Execute and open the link at the end of the log.
15 | * SASjs Server - just import the `server.json.zip` file to AppStream
16 |
17 | ## Build
18 |
19 | If you would like to build the app from source, you first need to [install](https://cli.sasjs.io/installation) the SASjs CLI.
20 |
21 | Then, update the `defaultTarget` attribute of the `sasjs/sasjsconfig.json` according to whether you are running SASjs `server`, `sas9` EBI, or `viya`. You should also update the `serverUrl` and `appLoc` attributes of the relevant target, to determine to which server and logical folder you will deploy.
22 |
23 | Instructions thereafter:
24 |
25 | ```bash
26 | npm i
27 | sasjs auth
28 | sasjs cbd
29 | ```
30 |
31 | You can then click the link at the bottom of the console to launch!
32 |
33 | ## Demo
34 |
35 | This video demonstrates a deployment onto SAS Viya.
36 |
37 | [](http://www.youtube.com/watch?v=uSZw2d_NOHg)
38 |
39 |
40 | If you'd like to publish an app with SASjs, we're happy to help - contact us [here](https://sasapps.io/contact).
41 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | HTML5 Pacman
7 |
8 |
44 |
45 |
46 |
47 |
48 |
49 | shim for font face
50 |
51 | HTML5 Pacman
52 |
53 |
54 | Writeup |
55 | Code on Github
56 |
57 |
58 |
59 |
60 |
61 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/src/modernizr-1.5.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Modernizr JavaScript library 1.5
3 | * http://www.modernizr.com/
4 | *
5 | * Copyright (c) 2009-2010 Faruk Ates - http://farukat.es/
6 | * Dual-licensed under the BSD and MIT licenses.
7 | * http://www.modernizr.com/license/
8 | *
9 | * Featuring major contributions by
10 | * Paul Irish - http://paulirish.com
11 | */
12 | window.Modernizr=function(i,e,I){function C(a,b){for(var c in a)if(m[a[c]]!==I&&(!b||b(a[c],D)))return true}function r(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1);return!!C([a,"Webkit"+c,"Moz"+c,"O"+c,"ms"+c,"Khtml"+c],b)}function P(){j[E]=function(a){for(var b=0,c=a.length;b7)};d.historymanagement=function(){return!!(i.history&&history.pushState)};d.draganddrop=function(){return u("drag")&&u("dragstart")&&u("dragenter")&&u("dragover")&&u("dragleave")&&u("dragend")&&u("drop")};d.websockets=function(){return"WebSocket"in i};d.rgba=function(){m.cssText="background-color:rgba(150,255,150,.5)";return(""+m.backgroundColor).indexOf("rgba")!==-1};d.hsla=function(){m.cssText="background-color:hsla(120,40%,100%,.5)";return(""+
17 | m.backgroundColor).indexOf("rgba")!==-1};d.multiplebgs=function(){m.cssText="background:url(//:),url(//:),red url(//:)";return/(url\s*\(.*?){3}/.test(m.background)};d.backgroundsize=function(){return r("backgroundSize")};d.borderimage=function(){return r("borderImage")};d.borderradius=function(){return r("borderRadius","",function(a){return(""+a).indexOf("orderRadius")!==-1})};d.boxshadow=function(){return r("boxShadow")};d.opacity=function(){var a=y.join("opacity:.5;")+"";m.cssText=a;return(""+m.opacity).indexOf("0.5")!==
18 | -1};d.cssanimations=function(){return r("animationName")};d.csscolumns=function(){return r("columnCount")};d.cssgradients=function(){var a=("background-image:"+y.join("gradient(linear,left top,right bottom,from(#9f9),to(white));background-image:")+y.join("linear-gradient(left top,#9f9, white);background-image:")).slice(0,-17);m.cssText=a;return(""+m.backgroundImage).indexOf("gradient")!==-1};d.cssreflections=function(){return r("boxReflect")};d.csstransforms=function(){return!!C(["transformProperty",
19 | "WebkitTransform","MozTransform","OTransform","msTransform"])};d.csstransforms3d=function(){var a=!!C(["perspectiveProperty","WebkitPerspective","MozPerspective","OPerspective","msPerspective"]);if(a){var b=document.createElement("style"),c=e.createElement("div");b.textContent="@media ("+y.join("transform-3d),(")+"modernizr){#modernizr{height:3px}}";e.getElementsByTagName("head")[0].appendChild(b);c.id="modernizr";s.appendChild(c);a=c.offsetHeight===3;b.parentNode.removeChild(b);c.parentNode.removeChild(c)}return a};
20 | d.csstransitions=function(){return r("transitionProperty")};d.fontface=function(){var a;if(/*@cc_on@if(@_jscript_version>=5)!@end@*/0)a=true;else{var b=e.createElement("style"),c=e.createElement("span"),h,t=false,g=e.body,o,w;b.textContent="@font-face{font-family:testfont;src:url('data:font/ttf;base64,AAEAAAAMAIAAAwBAT1MvMliohmwAAADMAAAAVmNtYXCp5qrBAAABJAAAANhjdnQgACICiAAAAfwAAAAEZ2FzcP//AAMAAAIAAAAACGdseWYv5OZoAAACCAAAANxoZWFk69bnvwAAAuQAAAA2aGhlYQUJAt8AAAMcAAAAJGhtdHgGDgC4AAADQAAAABRsb2NhAIQAwgAAA1QAAAAMbWF4cABVANgAAANgAAAAIG5hbWUgXduAAAADgAAABPVwb3N03NkzmgAACHgAAAA4AAECBAEsAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAACAAMDAAAAAAAAgAACbwAAAAoAAAAAAAAAAFBmRWQAAAAgqS8DM/8zAFwDMwDNAAAABQAAAAAAAAAAAAMAAAADAAAAHAABAAAAAABGAAMAAQAAAK4ABAAqAAAABgAEAAEAAgAuqQD//wAAAC6pAP///9ZXAwAAAAAAAAACAAAABgBoAAAAAAAvAAEAAAAAAAAAAAAAAAAAAAABAAIAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEACoAAAAGAAQAAQACAC6pAP//AAAALqkA////1lcDAAAAAAAAAAIAAAAiAogAAAAB//8AAgACACIAAAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8sgECAO0yMxEhESczESMiARDuzMwCqv1WIgJmAAACAFUAAAIRAc0ADwAfAAATFRQWOwEyNj0BNCYrASIGARQGKwEiJj0BNDY7ATIWFX8aIvAiGhoi8CIaAZIoN/43KCg3/jcoAWD0JB4eJPQkHh7++EY2NkbVRjY2RgAAAAABAEH/+QCdAEEACQAANjQ2MzIWFAYjIkEeEA8fHw8QDxwWFhwWAAAAAQAAAAIAAIuYbWpfDzz1AAsEAAAAAADFn9IuAAAAAMWf0i797/8zA4gDMwAAAAgAAgAAAAAAAAABAAADM/8zAFwDx/3v/98DiAABAAAAAAAAAAAAAAAAAAAABQF2ACIAAAAAAVUAAAJmAFUA3QBBAAAAKgAqACoAWgBuAAEAAAAFAFAABwBUAAQAAgAAAAEAAQAAAEAALgADAAMAAAAQAMYAAQAAAAAAAACLAAAAAQAAAAAAAQAhAIsAAQAAAAAAAgAFAKwAAQAAAAAAAwBDALEAAQAAAAAABAAnAPQAAQAAAAAABQAKARsAAQAAAAAABgAmASUAAQAAAAAADgAaAUsAAwABBAkAAAEWAWUAAwABBAkAAQBCAnsAAwABBAkAAgAKAr0AAwABBAkAAwCGAscAAwABBAkABABOA00AAwABBAkABQAUA5sAAwABBAkABgBMA68AAwABBAkADgA0A/tDb3B5cmlnaHQgMjAwOSBieSBEYW5pZWwgSm9obnNvbi4gIFJlbGVhc2VkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgT3BlbiBGb250IExpY2Vuc2UuIEtheWFoIExpIGdseXBocyBhcmUgcmVsZWFzZWQgdW5kZXIgdGhlIEdQTCB2ZXJzaW9uIDMuYmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhTGlnaHRiYWVjMmE5MmJmZmU1MDMyIC0gc3Vic2V0IG9mIEZvbnRGb3JnZSAyLjAgOiBKdXJhIExpZ2h0IDogMjMtMS0yMDA5YmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhIExpZ2h0VmVyc2lvbiAyIGJhZWMyYTkyYmZmZTUwMzIgLSBzdWJzZXQgb2YgSnVyYUxpZ2h0aHR0cDovL3NjcmlwdHMuc2lsLm9yZy9PRkwAQwBvAHAAeQByAGkAZwBoAHQAIAAyADAAMAA5ACAAYgB5ACAARABhAG4AaQBlAGwAIABKAG8AaABuAHMAbwBuAC4AIAAgAFIAZQBsAGUAYQBzAGUAZAAgAHUAbgBkAGUAcgAgAHQAaABlACAAdABlAHIAbQBzACAAbwBmACAAdABoAGUAIABPAHAAZQBuACAARgBvAG4AdAAgAEwAaQBjAGUAbgBzAGUALgAgAEsAYQB5AGEAaAAgAEwAaQAgAGcAbAB5AHAAaABzACAAYQByAGUAIAByAGUAbABlAGEAcwBlAGQAIAB1AG4AZABlAHIAIAB0AGgAZQAgAEcAUABMACAAdgBlAHIAcwBpAG8AbgAgADMALgBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQBMAGkAZwBoAHQAYgBhAGUAYwAyAGEAOQAyAGIAZgBmAGUANQAwADMAMgAgAC0AIABzAHUAYgBzAGUAdAAgAG8AZgAgAEYAbwBuAHQARgBvAHIAZwBlACAAMgAuADAAIAA6ACAASgB1AHIAYQAgAEwAaQBnAGgAdAAgADoAIAAyADMALQAxAC0AMgAwADAAOQBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQAgAEwAaQBnAGgAdABWAGUAcgBzAGkAbwBuACAAMgAgAGIAYQBlAGMAMgBhADkAMgBiAGYAZgBlADUAMAAzADIAIAAtACAAcwB1AGIAcwBlAHQAIABvAGYAIABKAHUAcgBhAEwAaQBnAGgAdABoAHQAdABwADoALwAvAHMAYwByAGkAcAB0AHMALgBzAGkAbAAuAG8AcgBnAC8ATwBGAEwAAAAAAgAAAAAAAP+BADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAQACAQIAEQt6ZXJva2F5YWhsaQ==')}";
21 | e.getElementsByTagName("head")[0].appendChild(b);c.setAttribute("style","font:99px _,arial,helvetica;position:absolute;visibility:hidden");if(!g){g=s.appendChild(e.createElement("fontface"));t=true}c.innerHTML="........";c.id="fonttest";g.appendChild(c);h=c.offsetWidth*c.offsetHeight;c.style.font="99px testfont,_,arial,helvetica";a=h!==c.offsetWidth*c.offsetHeight;var v=function(){if(g.parentNode){a=j.fontface=h!==c.offsetWidth*c.offsetHeight;s.className=s.className.replace(/(no-)?fontface\b/,"")+
22 | (a?" ":" no-")+"fontface"}};setTimeout(v,75);setTimeout(v,150);addEventListener("load",function(){v();(w=true)&&o&&o(a);setTimeout(function(){t||(g=c);g.parentNode.removeChild(g);b.parentNode.removeChild(b)},50)},false)}j._fontfaceready=function(p){w||a?p(a):(o=p)};return a||h!==c.offsetWidth};d.video=function(){var a=e.createElement("video"),b=!!a.canPlayType;if(b){b=new Boolean(b);b.ogg=a.canPlayType('video/ogg; codecs="theora"');b.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"');b.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"')}return b};
23 | d.audio=function(){var a=e.createElement("audio"),b=!!a.canPlayType;if(b){b=new Boolean(b);b.ogg=a.canPlayType('audio/ogg; codecs="vorbis"');b.mp3=a.canPlayType("audio/mpeg;");b.wav=a.canPlayType('audio/wav; codecs="1"');b.m4a=a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")}return b};d.localStorage=function(){return"localStorage"in i&&i.localStorage!==null};d.sessionStorage=function(){try{return"sessionStorage"in i&&i.sessionStorage!==null}catch(a){return false}};d.webworkers=function(){return!!i.Worker};
24 | d.applicationCache=function(){var a=i.applicationCache;return!!(a&&typeof a.status!="undefined"&&typeof a.update=="function"&&typeof a.swapCache=="function")};d.svg=function(){return!!e.createElementNS&&!!e.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect};d.smil=function(){return!!e.createElementNS&&/SVG/.test(M.call(e.createElementNS("http://www.w3.org/2000/svg","animate")))};d.svgclippaths=function(){return!!e.createElementNS&&/SVG/.test(M.call(e.createElementNS("http://www.w3.org/2000/svg",
25 | "clipPath")))};for(var z in d)if(O(d,z))N.push(((j[z.toLowerCase()]=d[z]())?"":"no-")+z.toLowerCase());j[E]||P();j.addTest=function(a,b){a=a.toLowerCase();if(!j[a]){b=!!b();s.className+=" "+(b?"":"no-")+a;j[a]=b;return j}};m.cssText="";D=n=null;(function(){var a=e.createElement("div");a.innerHTML="";return a.childNodes.length!==1})()&&function(a,b){function c(f,k){if(o[f])o[f].styleSheet.cssText+=k;else{var l=t[G],q=b[A]("style");q.media=f;l.insertBefore(q,l[G]);o[f]=q;c(f,k)}}function h(f,
26 | k){for(var l=new RegExp("\\b("+w+")\\b(?!.*[;}])","gi"),q=function(B){return".iepp_"+B},x=-1;++x\\s*$","i");g.innerHTML=f.outerHTML.replace(/\r|\n/g," ").replace(l,f.currentStyle.display=="block"?"":"
");l=g.childNodes[0];l.className+=" iepp_"+q;l=p[p.length]=[f,l];f.parentNode.replaceChild(l[1],l[0])}h(b.styleSheets,"all")});a.attachEvent("onafterprint",
28 | function(){for(var f=-1,k;++f 10) {
53 | return x1 + (10 - rem);
54 | } else if (rem > 0 && result < 0) {
55 | return x1 - rem;
56 | }
57 | return x1 + x2;
58 | };
59 |
60 | function isVunerable() {
61 | return eatable !== null;
62 | };
63 |
64 | function isDangerous() {
65 | return eaten === null;
66 | };
67 |
68 | function isHidden() {
69 | return eatable === null && eaten !== null;
70 | };
71 |
72 | function getRandomDirection() {
73 | var moves = (direction === LEFT || direction === RIGHT)
74 | ? [UP, DOWN] : [LEFT, RIGHT];
75 | return moves[Math.floor(Math.random() * 2)];
76 | };
77 |
78 | function reset() {
79 | eaten = null;
80 | eatable = null;
81 | position = { "x": 90, "y": 80 };
82 | direction = getRandomDirection();
83 | due = getRandomDirection();
84 | };
85 |
86 | function onWholeSquare(x) {
87 | return x % 10 === 0;
88 | };
89 |
90 | function oppositeDirection(dir) {
91 | return dir === LEFT && RIGHT ||
92 | dir === RIGHT && LEFT ||
93 | dir === UP && DOWN || UP;
94 | };
95 |
96 | function makeEatable() {
97 | direction = oppositeDirection(direction);
98 | eatable = game.getTick();
99 | };
100 |
101 | function eat() {
102 | eatable = null;
103 | eaten = game.getTick();
104 | };
105 |
106 | function pointToCoord(x) {
107 | return Math.round(x / 10);
108 | };
109 |
110 | function nextSquare(x, dir) {
111 | var rem = x % 10;
112 | if (rem === 0) {
113 | return x;
114 | } else if (dir === RIGHT || dir === DOWN) {
115 | return x + (10 - rem);
116 | } else {
117 | return x - rem;
118 | }
119 | };
120 |
121 | function onGridSquare(pos) {
122 | return onWholeSquare(pos.y) && onWholeSquare(pos.x);
123 | };
124 |
125 | function secondsAgo(tick) {
126 | return (game.getTick() - tick) / Pacman.FPS;
127 | };
128 |
129 | function getColour() {
130 | if (eatable) {
131 | if (secondsAgo(eatable) > 5) {
132 | return game.getTick() % 20 > 10 ? "#FFFFFF" : "#0000BB";
133 | } else {
134 | return "#0000BB";
135 | }
136 | } else if (eaten) {
137 | return "#222";
138 | }
139 | return colour;
140 | };
141 |
142 | function draw(ctx) {
143 |
144 | var s = map.blockSize,
145 | top = (position.y / 10) * s,
146 | left = (position.x / 10) * s;
147 |
148 | if (eatable && secondsAgo(eatable) > 8) {
149 | eatable = null;
150 | }
151 |
152 | if (eaten && secondsAgo(eaten) > 3) {
153 | eaten = null;
154 | }
155 |
156 | var tl = left + s;
157 | var base = top + s - 3;
158 | var inc = s / 10;
159 |
160 | var high = game.getTick() % 10 > 5 ? 3 : -3;
161 | var low = game.getTick() % 10 > 5 ? -3 : 3;
162 |
163 | ctx.fillStyle = getColour();
164 | ctx.beginPath();
165 |
166 | ctx.moveTo(left, base);
167 |
168 | ctx.quadraticCurveTo(left, top, left + (s / 2), top);
169 | ctx.quadraticCurveTo(left + s, top, left + s, base);
170 |
171 | // Wavy things at the bottom
172 | ctx.quadraticCurveTo(tl - (inc * 1), base + high, tl - (inc * 2), base);
173 | ctx.quadraticCurveTo(tl - (inc * 3), base + low, tl - (inc * 4), base);
174 | ctx.quadraticCurveTo(tl - (inc * 5), base + high, tl - (inc * 6), base);
175 | ctx.quadraticCurveTo(tl - (inc * 7), base + low, tl - (inc * 8), base);
176 | ctx.quadraticCurveTo(tl - (inc * 9), base + high, tl - (inc * 10), base);
177 |
178 | ctx.closePath();
179 | ctx.fill();
180 |
181 | ctx.beginPath();
182 | ctx.fillStyle = "#FFF";
183 | ctx.arc(left + 6, top + 6, s / 6, 0, 300, false);
184 | ctx.arc((left + s) - 6, top + 6, s / 6, 0, 300, false);
185 | ctx.closePath();
186 | ctx.fill();
187 |
188 | var f = s / 12;
189 | var off = {};
190 | off[RIGHT] = [f, 0];
191 | off[LEFT] = [-f, 0];
192 | off[UP] = [0, -f];
193 | off[DOWN] = [0, f];
194 |
195 | ctx.beginPath();
196 | ctx.fillStyle = "#000";
197 | ctx.arc(left + 6 + off[direction][0], top + 6 + off[direction][1],
198 | s / 15, 0, 300, false);
199 | ctx.arc((left + s) - 6 + off[direction][0], top + 6 + off[direction][1],
200 | s / 15, 0, 300, false);
201 | ctx.closePath();
202 | ctx.fill();
203 |
204 | };
205 |
206 | function pane(pos) {
207 |
208 | if (pos.y === 100 && pos.x >= 190 && direction === RIGHT) {
209 | return { "y": 100, "x": -10 };
210 | }
211 |
212 | if (pos.y === 100 && pos.x <= -10 && direction === LEFT) {
213 | return position = { "y": 100, "x": 190 };
214 | }
215 |
216 | return false;
217 | };
218 |
219 | function move(ctx) {
220 |
221 | var oldPos = position,
222 | onGrid = onGridSquare(position),
223 | npos = null;
224 |
225 | if (due !== direction) {
226 |
227 | npos = getNewCoord(due, position);
228 |
229 | if (onGrid &&
230 | map.isFloorSpace({
231 | "y": pointToCoord(nextSquare(npos.y, due)),
232 | "x": pointToCoord(nextSquare(npos.x, due))
233 | })) {
234 | direction = due;
235 | } else {
236 | npos = null;
237 | }
238 | }
239 |
240 | if (npos === null) {
241 | npos = getNewCoord(direction, position);
242 | }
243 |
244 | if (onGrid &&
245 | map.isWallSpace({
246 | "y": pointToCoord(nextSquare(npos.y, direction)),
247 | "x": pointToCoord(nextSquare(npos.x, direction))
248 | })) {
249 |
250 | due = getRandomDirection();
251 | return move(ctx);
252 | }
253 |
254 | position = npos;
255 |
256 | var tmp = pane(position);
257 | if (tmp) {
258 | position = tmp;
259 | }
260 |
261 | due = getRandomDirection();
262 |
263 | return {
264 | "new": position,
265 | "old": oldPos
266 | };
267 | };
268 |
269 | return {
270 | "eat": eat,
271 | "isVunerable": isVunerable,
272 | "isDangerous": isDangerous,
273 | "makeEatable": makeEatable,
274 | "reset": reset,
275 | "move": move,
276 | "draw": draw
277 | };
278 | };
279 |
280 | Pacman.User = function (game, map) {
281 |
282 | var position = null,
283 | direction = null,
284 | eaten = null,
285 | due = null,
286 | lives = null,
287 | score = 5,
288 | keyMap = {};
289 |
290 | keyMap[KEY.ARROW_LEFT] = LEFT;
291 | keyMap[KEY.ARROW_UP] = UP;
292 | keyMap[KEY.ARROW_RIGHT] = RIGHT;
293 | keyMap[KEY.ARROW_DOWN] = DOWN;
294 |
295 | function addScore(nScore) {
296 | score += nScore;
297 | if (score >= 10000 && score - nScore < 10000) {
298 | lives += 1;
299 | }
300 | };
301 |
302 | function theScore() {
303 | return score;
304 | };
305 |
306 | function loseLife() {
307 | lives -= 1;
308 | };
309 |
310 | function getLives() {
311 | return lives;
312 | };
313 |
314 | function initUser() {
315 | score = 0;
316 | lives = 3;
317 | newLevel();
318 | }
319 |
320 | function newLevel() {
321 | resetPosition();
322 | eaten = 0;
323 | };
324 |
325 | function resetPosition() {
326 | position = { "x": 90, "y": 120 };
327 | direction = LEFT;
328 | due = LEFT;
329 | };
330 |
331 | function reset() {
332 | initUser();
333 | resetPosition();
334 | };
335 |
336 | function keyDown(e) {
337 | if (typeof keyMap[e.keyCode] !== "undefined") {
338 | due = keyMap[e.keyCode];
339 | e.preventDefault();
340 | e.stopPropagation();
341 | return false;
342 | }
343 | return true;
344 | };
345 |
346 | function getNewCoord(dir, current) {
347 | return {
348 | "x": current.x + (dir === LEFT && -2 || dir === RIGHT && 2 || 0),
349 | "y": current.y + (dir === DOWN && 2 || dir === UP && -2 || 0)
350 | };
351 | };
352 |
353 | function onWholeSquare(x) {
354 | return x % 10 === 0;
355 | };
356 |
357 | function pointToCoord(x) {
358 | return Math.round(x / 10);
359 | };
360 |
361 | function nextSquare(x, dir) {
362 | var rem = x % 10;
363 | if (rem === 0) {
364 | return x;
365 | } else if (dir === RIGHT || dir === DOWN) {
366 | return x + (10 - rem);
367 | } else {
368 | return x - rem;
369 | }
370 | };
371 |
372 | function next(pos, dir) {
373 | return {
374 | "y": pointToCoord(nextSquare(pos.y, dir)),
375 | "x": pointToCoord(nextSquare(pos.x, dir)),
376 | };
377 | };
378 |
379 | function onGridSquare(pos) {
380 | return onWholeSquare(pos.y) && onWholeSquare(pos.x);
381 | };
382 |
383 | function isOnSamePlane(due, dir) {
384 | return ((due === LEFT || due === RIGHT) &&
385 | (dir === LEFT || dir === RIGHT)) ||
386 | ((due === UP || due === DOWN) &&
387 | (dir === UP || dir === DOWN));
388 | };
389 |
390 | function move(ctx) {
391 |
392 | var npos = null,
393 | nextWhole = null,
394 | oldPosition = position,
395 | block = null;
396 |
397 | if (due !== direction) {
398 | npos = getNewCoord(due, position);
399 |
400 | if (isOnSamePlane(due, direction) ||
401 | (onGridSquare(position) &&
402 | map.isFloorSpace(next(npos, due)))) {
403 | direction = due;
404 | } else {
405 | npos = null;
406 | }
407 | }
408 |
409 | if (npos === null) {
410 | npos = getNewCoord(direction, position);
411 | }
412 |
413 | if (onGridSquare(position) && map.isWallSpace(next(npos, direction))) {
414 | direction = NONE;
415 | }
416 |
417 | if (direction === NONE) {
418 | return { "new": position, "old": position };
419 | }
420 |
421 | if (npos.y === 100 && npos.x >= 190 && direction === RIGHT) {
422 | npos = { "y": 100, "x": -10 };
423 | }
424 |
425 | if (npos.y === 100 && npos.x <= -12 && direction === LEFT) {
426 | npos = { "y": 100, "x": 190 };
427 | }
428 |
429 | position = npos;
430 | nextWhole = next(position, direction);
431 |
432 | block = map.block(nextWhole);
433 |
434 | if ((isMidSquare(position.y) || isMidSquare(position.x)) &&
435 | block === Pacman.BISCUIT || block === Pacman.PILL) {
436 |
437 | map.setBlock(nextWhole, Pacman.EMPTY);
438 | addScore((block === Pacman.BISCUIT) ? 10 : 50);
439 | eaten += 1;
440 |
441 | if (eaten === 182) {
442 | game.completedLevel();
443 | }
444 |
445 | if (block === Pacman.PILL) {
446 | game.eatenPill();
447 | }
448 | }
449 |
450 | return {
451 | "new": position,
452 | "old": oldPosition
453 | };
454 | };
455 |
456 | function isMidSquare(x) {
457 | var rem = x % 10;
458 | return rem > 3 || rem < 7;
459 | };
460 |
461 | function calcAngle(dir, pos) {
462 | if (dir == RIGHT && (pos.x % 10 < 5)) {
463 | return { "start": 0.25, "end": 1.75, "direction": false };
464 | } else if (dir === DOWN && (pos.y % 10 < 5)) {
465 | return { "start": 0.75, "end": 2.25, "direction": false };
466 | } else if (dir === UP && (pos.y % 10 < 5)) {
467 | return { "start": 1.25, "end": 1.75, "direction": true };
468 | } else if (dir === LEFT && (pos.x % 10 < 5)) {
469 | return { "start": 0.75, "end": 1.25, "direction": true };
470 | }
471 | return { "start": 0, "end": 2, "direction": false };
472 | };
473 |
474 | function drawDead(ctx, amount) {
475 |
476 | var size = map.blockSize,
477 | half = size / 2;
478 |
479 | if (amount >= 1) {
480 | return;
481 | }
482 |
483 | ctx.fillStyle = "#FFFF00";
484 | ctx.beginPath();
485 | ctx.moveTo(((position.x / 10) * size) + half,
486 | ((position.y / 10) * size) + half);
487 |
488 | ctx.arc(((position.x / 10) * size) + half,
489 | ((position.y / 10) * size) + half,
490 | half, 0, Math.PI * 2 * amount, true);
491 |
492 | ctx.fill();
493 | };
494 |
495 | function draw(ctx) {
496 |
497 | var s = map.blockSize,
498 | angle = calcAngle(direction, position);
499 |
500 | ctx.fillStyle = "#FFFF00";
501 |
502 | ctx.beginPath();
503 |
504 | ctx.moveTo(((position.x / 10) * s) + s / 2,
505 | ((position.y / 10) * s) + s / 2);
506 |
507 | ctx.arc(((position.x / 10) * s) + s / 2,
508 | ((position.y / 10) * s) + s / 2,
509 | s / 2, Math.PI * angle.start,
510 | Math.PI * angle.end, angle.direction);
511 |
512 | ctx.fill();
513 | };
514 |
515 | initUser();
516 |
517 | return {
518 | "draw": draw,
519 | "drawDead": drawDead,
520 | "loseLife": loseLife,
521 | "getLives": getLives,
522 | "score": score,
523 | "addScore": addScore,
524 | "theScore": theScore,
525 | "keyDown": keyDown,
526 | "move": move,
527 | "newLevel": newLevel,
528 | "reset": reset,
529 | "resetPosition": resetPosition
530 | };
531 | };
532 |
533 | Pacman.Map = function (size) {
534 |
535 | var height = null,
536 | width = null,
537 | blockSize = size,
538 | pillSize = 0,
539 | map = null;
540 |
541 | function withinBounds(y, x) {
542 | return y >= 0 && y < height && x >= 0 && x < width;
543 | }
544 |
545 | function isWall(pos) {
546 | return withinBounds(pos.y, pos.x) && map[pos.y][pos.x] === Pacman.WALL;
547 | }
548 |
549 | function isFloorSpace(pos) {
550 | if (!withinBounds(pos.y, pos.x)) {
551 | return false;
552 | }
553 | var peice = map[pos.y][pos.x];
554 | return peice === Pacman.EMPTY ||
555 | peice === Pacman.BISCUIT ||
556 | peice === Pacman.PILL;
557 | }
558 |
559 | function drawWall(ctx) {
560 |
561 | var i, j, p, line;
562 |
563 | ctx.strokeStyle = "#0000FF";
564 | ctx.lineWidth = 5;
565 | ctx.lineCap = "round";
566 |
567 | for (i = 0; i < Pacman.WALLS.length; i += 1) {
568 | line = Pacman.WALLS[i];
569 | ctx.beginPath();
570 |
571 | for (j = 0; j < line.length; j += 1) {
572 |
573 | p = line[j];
574 |
575 | if (p.move) {
576 | ctx.moveTo(p.move[0] * blockSize, p.move[1] * blockSize);
577 | } else if (p.line) {
578 | ctx.lineTo(p.line[0] * blockSize, p.line[1] * blockSize);
579 | } else if (p.curve) {
580 | ctx.quadraticCurveTo(p.curve[0] * blockSize,
581 | p.curve[1] * blockSize,
582 | p.curve[2] * blockSize,
583 | p.curve[3] * blockSize);
584 | }
585 | }
586 | ctx.stroke();
587 | }
588 | }
589 |
590 | function reset() {
591 | map = Pacman.MAP.clone();
592 | height = map.length;
593 | width = map[0].length;
594 | };
595 |
596 | function block(pos) {
597 | return map[pos.y][pos.x];
598 | };
599 |
600 | function setBlock(pos, type) {
601 | map[pos.y][pos.x] = type;
602 | };
603 |
604 | function drawPills(ctx) {
605 |
606 | if (++pillSize > 30) {
607 | pillSize = 0;
608 | }
609 |
610 | for (i = 0; i < height; i += 1) {
611 | for (j = 0; j < width; j += 1) {
612 | if (map[i][j] === Pacman.PILL) {
613 | ctx.beginPath();
614 |
615 | ctx.fillStyle = "#000";
616 | ctx.fillRect((j * blockSize), (i * blockSize),
617 | blockSize, blockSize);
618 |
619 | ctx.fillStyle = "#FFF";
620 | ctx.arc((j * blockSize) + blockSize / 2,
621 | (i * blockSize) + blockSize / 2,
622 | Math.abs(5 - (pillSize / 3)),
623 | 0,
624 | Math.PI * 2, false);
625 | ctx.fill();
626 | ctx.closePath();
627 | }
628 | }
629 | }
630 | };
631 |
632 | function draw(ctx) {
633 |
634 | var i, j, size = blockSize;
635 |
636 | ctx.fillStyle = "#000";
637 | ctx.fillRect(0, 0, width * size, height * size);
638 |
639 | drawWall(ctx);
640 |
641 | for (i = 0; i < height; i += 1) {
642 | for (j = 0; j < width; j += 1) {
643 | drawBlock(i, j, ctx);
644 | }
645 | }
646 | };
647 |
648 | function drawBlock(y, x, ctx) {
649 |
650 | var layout = map[y][x];
651 |
652 | if (layout === Pacman.PILL) {
653 | return;
654 | }
655 |
656 | ctx.beginPath();
657 |
658 | if (layout === Pacman.EMPTY || layout === Pacman.BLOCK ||
659 | layout === Pacman.BISCUIT) {
660 |
661 | ctx.fillStyle = "#000";
662 | ctx.fillRect((x * blockSize), (y * blockSize),
663 | blockSize, blockSize);
664 |
665 | if (layout === Pacman.BISCUIT) {
666 | ctx.fillStyle = "#FFF";
667 | ctx.fillRect((x * blockSize) + (blockSize / 2.5),
668 | (y * blockSize) + (blockSize / 2.5),
669 | blockSize / 6, blockSize / 6);
670 | }
671 | }
672 | ctx.closePath();
673 | };
674 |
675 | reset();
676 |
677 | return {
678 | "draw": draw,
679 | "drawBlock": drawBlock,
680 | "drawPills": drawPills,
681 | "block": block,
682 | "setBlock": setBlock,
683 | "reset": reset,
684 | "isWallSpace": isWall,
685 | "isFloorSpace": isFloorSpace,
686 | "height": height,
687 | "width": width,
688 | "blockSize": blockSize
689 | };
690 | };
691 |
692 | Pacman.Audio = function (game) {
693 |
694 | var files = [],
695 | endEvents = [],
696 | progressEvents = [],
697 | playing = [];
698 |
699 | function load(name, path, cb) {
700 |
701 | var f = files[name] = document.createElement("audio");
702 |
703 | progressEvents[name] = function (event) { progress(event, name, cb); };
704 |
705 | f.addEventListener("canplaythrough", progressEvents[name], true);
706 | f.setAttribute("preload", "true");
707 | f.setAttribute("autobuffer", "true");
708 | f.setAttribute("src", path);
709 | f.pause();
710 | };
711 |
712 | function progress(event, name, callback) {
713 | if (event.loaded === event.total && typeof callback === "function") {
714 | callback();
715 | files[name].removeEventListener("canplaythrough",
716 | progressEvents[name], true);
717 | }
718 | };
719 |
720 | function disableSound() {
721 | for (var i = 0; i < playing.length; i++) {
722 | files[playing[i]].pause();
723 | files[playing[i]].currentTime = 0;
724 | }
725 | playing = [];
726 | };
727 |
728 | function ended(name) {
729 |
730 | var i, tmp = [], found = false;
731 |
732 | files[name].removeEventListener("ended", endEvents[name], true);
733 |
734 | for (i = 0; i < playing.length; i++) {
735 | if (!found && playing[i]) {
736 | found = true;
737 | } else {
738 | tmp.push(playing[i]);
739 | }
740 | }
741 | playing = tmp;
742 | };
743 |
744 | function play(name) {
745 | if (!game.soundDisabled()) {
746 | endEvents[name] = function () { ended(name); };
747 | playing.push(name);
748 | files[name].addEventListener("ended", endEvents[name], true);
749 | files[name].play();
750 | }
751 | };
752 |
753 | function pause() {
754 | for (var i = 0; i < playing.length; i++) {
755 | files[playing[i]].pause();
756 | }
757 | };
758 |
759 | function resume() {
760 | for (var i = 0; i < playing.length; i++) {
761 | files[playing[i]].play();
762 | }
763 | };
764 |
765 | return {
766 | "disableSound": disableSound,
767 | "load": load,
768 | "play": play,
769 | "pause": pause,
770 | "resume": resume
771 | };
772 | };
773 |
774 | var PACMAN = (function () {
775 |
776 | var state = WAITING,
777 | audio = null,
778 | ghosts = [],
779 | ghostSpecs = ["#00FFDE", "#FF0000", "#FFB8DE", "#FFB847"],
780 | eatenCount = 0,
781 | level = 0,
782 | tick = 0,
783 | ghostPos, userPos,
784 | stateChanged = true,
785 | timerStart = null,
786 | lastTime = 0,
787 | ctx = null,
788 | timer = null,
789 | map = null,
790 | user = null,
791 | stored = null;
792 |
793 | function getTick() {
794 | return tick;
795 | };
796 |
797 | function drawScore(text, position) {
798 | ctx.fillStyle = "#FFFFFF";
799 | ctx.font = "12px BDCartoonShoutRegular";
800 | ctx.fillText(text,
801 | (position["new"]["x"] / 10) * map.blockSize,
802 | ((position["new"]["y"] + 5) / 10) * map.blockSize);
803 | }
804 |
805 | function dialog(text) {
806 | ctx.fillStyle = "#FFFF00";
807 | ctx.font = "14px BDCartoonShoutRegular";
808 | var width = ctx.measureText(text).width,
809 | x = ((map.width * map.blockSize) - width) / 2;
810 | ctx.fillText(text, x, (map.height * 10) + 8);
811 | }
812 |
813 | function soundDisabled() {
814 | return localStorage["soundDisabled"] === "true";
815 | };
816 |
817 | function startLevel() {
818 | user.resetPosition();
819 | for (var i = 0; i < ghosts.length; i += 1) {
820 | ghosts[i].reset();
821 | }
822 | audio.play("start");
823 | timerStart = tick;
824 | setState(COUNTDOWN);
825 | }
826 |
827 | function startNewGame() {
828 | setState(WAITING);
829 | level = 1;
830 | user.reset();
831 | map.reset();
832 | map.draw(ctx);
833 | startLevel();
834 | }
835 |
836 | function keyDown(e) {
837 | if (e.keyCode === KEY.N) {
838 | startNewGame();
839 | } else if (e.keyCode === KEY.S) {
840 | audio.disableSound();
841 | localStorage["soundDisabled"] = !soundDisabled();
842 | } else if (e.keyCode === KEY.P && state === PAUSE) {
843 | audio.resume();
844 | map.draw(ctx);
845 | setState(stored);
846 | } else if (e.keyCode === KEY.P) {
847 | stored = state;
848 | setState(PAUSE);
849 | audio.pause();
850 | map.draw(ctx);
851 | dialog("Paused");
852 | } else if (state !== PAUSE) {
853 | return user.keyDown(e);
854 | }
855 | return true;
856 | }
857 |
858 | function loseLife() {
859 | setState(WAITING);
860 | user.loseLife();
861 | if (user.getLives() > 0) {
862 | startLevel();
863 | }
864 | }
865 |
866 | function setState(nState) {
867 | state = nState;
868 | stateChanged = true;
869 | };
870 |
871 | function collided(user, ghost) {
872 | return (Math.sqrt(Math.pow(ghost.x - user.x, 2) +
873 | Math.pow(ghost.y - user.y, 2))) < 10;
874 | };
875 |
876 | function drawFooter() {
877 |
878 | var topLeft = (map.height * map.blockSize),
879 | textBase = topLeft + 17;
880 |
881 | ctx.fillStyle = "#000000";
882 | ctx.fillRect(0, topLeft, (map.width * map.blockSize), 30);
883 |
884 | ctx.fillStyle = "#FFFF00";
885 |
886 | for (var i = 0, len = user.getLives(); i < len; i++) {
887 | ctx.fillStyle = "#FFFF00";
888 | ctx.beginPath();
889 | ctx.moveTo(150 + (25 * i) + map.blockSize / 2,
890 | (topLeft + 1) + map.blockSize / 2);
891 |
892 | ctx.arc(150 + (25 * i) + map.blockSize / 2,
893 | (topLeft + 1) + map.blockSize / 2,
894 | map.blockSize / 2, Math.PI * 0.25, Math.PI * 1.75, false);
895 | ctx.fill();
896 | }
897 |
898 | ctx.fillStyle = !soundDisabled() ? "#00FF00" : "#FF0000";
899 | ctx.font = "bold 16px sans-serif";
900 | //ctx.fillText("♪", 10, textBase);
901 | ctx.fillText("s", 10, textBase);
902 |
903 | ctx.fillStyle = "#FFFF00";
904 | ctx.font = "14px BDCartoonShoutRegular";
905 | ctx.fillText("Score: " + user.theScore(), 30, textBase);
906 | ctx.fillText("Level: " + level, 260, textBase);
907 | }
908 |
909 | function redrawBlock(pos) {
910 | map.drawBlock(Math.floor(pos.y / 10), Math.floor(pos.x / 10), ctx);
911 | map.drawBlock(Math.ceil(pos.y / 10), Math.ceil(pos.x / 10), ctx);
912 | }
913 |
914 | function mainDraw() {
915 |
916 | var diff, u, i, len, nScore;
917 |
918 | ghostPos = [];
919 |
920 | for (i = 0, len = ghosts.length; i < len; i += 1) {
921 | ghostPos.push(ghosts[i].move(ctx));
922 | }
923 | u = user.move(ctx);
924 |
925 | for (i = 0, len = ghosts.length; i < len; i += 1) {
926 | redrawBlock(ghostPos[i].old);
927 | }
928 | redrawBlock(u.old);
929 |
930 | for (i = 0, len = ghosts.length; i < len; i += 1) {
931 | ghosts[i].draw(ctx);
932 | }
933 | user.draw(ctx);
934 |
935 | userPos = u["new"];
936 |
937 | for (i = 0, len = ghosts.length; i < len; i += 1) {
938 | if (collided(userPos, ghostPos[i]["new"])) {
939 | if (ghosts[i].isVunerable()) {
940 | audio.play("eatghost");
941 | ghosts[i].eat();
942 | eatenCount += 1;
943 | nScore = eatenCount * 50;
944 | drawScore(nScore, ghostPos[i]);
945 | user.addScore(nScore);
946 | setState(EATEN_PAUSE);
947 | timerStart = tick;
948 | } else if (ghosts[i].isDangerous()) {
949 | audio.play("die");
950 | setState(DYING);
951 | timerStart = tick;
952 | }
953 | }
954 | }
955 | };
956 |
957 | function mainLoop() {
958 |
959 | var diff;
960 |
961 | if (state !== PAUSE) {
962 | ++tick;
963 | }
964 |
965 | map.drawPills(ctx);
966 |
967 | if (state === PLAYING) {
968 | mainDraw();
969 | } else if (state === WAITING && stateChanged) {
970 | stateChanged = false;
971 | map.draw(ctx);
972 | dialog("Press N to start a New game");
973 | } else if (state === EATEN_PAUSE &&
974 | (tick - timerStart) > (Pacman.FPS / 3)) {
975 | map.draw(ctx);
976 | setState(PLAYING);
977 | } else if (state === DYING) {
978 | if (tick - timerStart > (Pacman.FPS * 2)) {
979 | loseLife();
980 | } else {
981 | redrawBlock(userPos);
982 | for (i = 0, len = ghosts.length; i < len; i += 1) {
983 | redrawBlock(ghostPos[i].old);
984 | ghostPos.push(ghosts[i].draw(ctx));
985 | }
986 | user.drawDead(ctx, (tick - timerStart) / (Pacman.FPS * 2));
987 | }
988 | } else if (state === COUNTDOWN) {
989 |
990 | diff = 5 + Math.floor((timerStart - tick) / Pacman.FPS);
991 |
992 | if (diff === 0) {
993 | map.draw(ctx);
994 | setState(PLAYING);
995 | } else {
996 | if (diff !== lastTime) {
997 | lastTime = diff;
998 | map.draw(ctx);
999 | dialog("Starting in: " + diff);
1000 | }
1001 | }
1002 | }
1003 |
1004 | drawFooter();
1005 | }
1006 |
1007 | function eatenPill() {
1008 | audio.play("eatpill");
1009 | timerStart = tick;
1010 | eatenCount = 0;
1011 | for (i = 0; i < ghosts.length; i += 1) {
1012 | ghosts[i].makeEatable(ctx);
1013 | }
1014 | };
1015 |
1016 | function completedLevel() {
1017 | setState(WAITING);
1018 | level += 1;
1019 | map.reset();
1020 | user.newLevel();
1021 | startLevel();
1022 | };
1023 |
1024 | function keyPress(e) {
1025 | if (state !== WAITING && state !== PAUSE) {
1026 | e.preventDefault();
1027 | e.stopPropagation();
1028 | }
1029 | };
1030 |
1031 | function init(wrapper, root) {
1032 |
1033 | var i, len, ghost,
1034 | blockSize = wrapper.offsetWidth / 19,
1035 | canvas = document.createElement("canvas");
1036 |
1037 | canvas.setAttribute("width", (blockSize * 19) + "px");
1038 | canvas.setAttribute("height", (blockSize * 22) + 30 + "px");
1039 |
1040 | wrapper.appendChild(canvas);
1041 |
1042 | ctx = canvas.getContext('2d');
1043 |
1044 | audio = new Pacman.Audio({ "soundDisabled": soundDisabled });
1045 | map = new Pacman.Map(blockSize);
1046 | user = new Pacman.User({
1047 | "completedLevel": completedLevel,
1048 | "eatenPill": eatenPill
1049 | }, map);
1050 |
1051 | for (i = 0, len = ghostSpecs.length; i < len; i += 1) {
1052 | ghost = new Pacman.Ghost({ "getTick": getTick }, map, ghostSpecs[i]);
1053 | ghosts.push(ghost);
1054 | }
1055 |
1056 | map.draw(ctx);
1057 | dialog("Loading ...");
1058 |
1059 | if (Modernizr.audio.ogg) {
1060 | var audio_files = [
1061 | ["start", "audio/opening_song.ogg"],
1062 | ["die", "audio/die.ogg"],
1063 | ["eatghost", "audio/eatghost.ogg"],
1064 | ["eatpill", "audio/eatpill.ogg"],
1065 | ["eating", "audio/eating.short.ogg"],
1066 | ["eating2", "audio/eating.short.ogg"]
1067 | ];
1068 | } else {
1069 | var audio_files = [
1070 | ["start", "audio/opening_song.mp3"],
1071 | ["die", "audio/die.mp3"],
1072 | ["eatghost", "audio/eatghost.mp3"],
1073 | ["eatpill", "audio/eatpill.mp3"],
1074 | ["eating", "audio/eating.short.mp3"],
1075 | ["eating2", "audio/eating.short.mp3"]
1076 | ];
1077 | }
1078 |
1079 | load(audio_files, function () { loaded(); });
1080 | };
1081 |
1082 | function load(arr, callback) {
1083 |
1084 | if (arr.length === 0) {
1085 | callback();
1086 | } else {
1087 | var x = arr.pop();
1088 | audio.load(x[0], x[1], function () { load(arr, callback); });
1089 | }
1090 | };
1091 |
1092 | function loaded() {
1093 |
1094 | dialog("Press N to Start");
1095 |
1096 | document.addEventListener("keydown", keyDown, true);
1097 | document.addEventListener("keypress", keyPress, true);
1098 |
1099 | timer = window.setInterval(mainLoop, 1000 / Pacman.FPS);
1100 | };
1101 |
1102 | return {
1103 | "init": init
1104 | };
1105 |
1106 | }());
1107 |
1108 | /* Human readable keyCode index */
1109 | var KEY = { 'BACKSPACE': 8, 'TAB': 9, 'NUM_PAD_CLEAR': 12, 'ENTER': 13, 'SHIFT': 16, 'CTRL': 17, 'ALT': 18, 'PAUSE': 19, 'CAPS_LOCK': 20, 'ESCAPE': 27, 'SPACEBAR': 32, 'PAGE_UP': 33, 'PAGE_DOWN': 34, 'END': 35, 'HOME': 36, 'ARROW_LEFT': 37, 'ARROW_UP': 38, 'ARROW_RIGHT': 39, 'ARROW_DOWN': 40, 'PRINT_SCREEN': 44, 'INSERT': 45, 'DELETE': 46, 'SEMICOLON': 59, 'WINDOWS_LEFT': 91, 'WINDOWS_RIGHT': 92, 'SELECT': 93, 'NUM_PAD_ASTERISK': 106, 'NUM_PAD_PLUS_SIGN': 107, 'NUM_PAD_HYPHEN-MINUS': 109, 'NUM_PAD_FULL_STOP': 110, 'NUM_PAD_SOLIDUS': 111, 'NUM_LOCK': 144, 'SCROLL_LOCK': 145, 'SEMICOLON': 186, 'EQUALS_SIGN': 187, 'COMMA': 188, 'HYPHEN-MINUS': 189, 'FULL_STOP': 190, 'SOLIDUS': 191, 'GRAVE_ACCENT': 192, 'LEFT_SQUARE_BRACKET': 219, 'REVERSE_SOLIDUS': 220, 'RIGHT_SQUARE_BRACKET': 221, 'APOSTROPHE': 222 };
1110 |
1111 | (function () {
1112 | /* 0 - 9 */
1113 | for (var i = 48; i <= 57; i++) {
1114 | KEY['' + (i - 48)] = i;
1115 | }
1116 | /* A - Z */
1117 | for (i = 65; i <= 90; i++) {
1118 | KEY['' + String.fromCharCode(i)] = i;
1119 | }
1120 | /* NUM_PAD_0 - NUM_PAD_9 */
1121 | for (i = 96; i <= 105; i++) {
1122 | KEY['NUM_PAD_' + (i - 96)] = i;
1123 | }
1124 | /* F1 - F12 */
1125 | for (i = 112; i <= 123; i++) {
1126 | KEY['F' + (i - 112 + 1)] = i;
1127 | }
1128 | })();
1129 |
1130 | Pacman.WALL = 0;
1131 | Pacman.BISCUIT = 1;
1132 | Pacman.EMPTY = 2;
1133 | Pacman.BLOCK = 3;
1134 | Pacman.PILL = 4;
1135 |
1136 | Pacman.MAP = [
1137 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
1138 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
1139 | [0, 4, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 4, 0],
1140 | [0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0],
1141 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
1142 | [0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0],
1143 | [0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
1144 | [0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
1145 | [2, 2, 2, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 2, 2, 2],
1146 | [0, 0, 0, 0, 1, 0, 1, 0, 0, 3, 0, 0, 1, 0, 1, 0, 0, 0, 0],
1147 | [2, 2, 2, 2, 1, 1, 1, 0, 3, 3, 3, 0, 1, 1, 1, 2, 2, 2, 2],
1148 | [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
1149 | [2, 2, 2, 0, 1, 0, 1, 1, 1, 2, 1, 1, 1, 0, 1, 0, 2, 2, 2],
1150 | [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
1151 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
1152 | [0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0],
1153 | [0, 4, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 4, 0],
1154 | [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
1155 | [0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
1156 | [0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0],
1157 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
1158 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
1159 | ];
1160 |
1161 | Pacman.WALLS = [
1162 |
1163 | [{ "move": [0, 9.5] }, { "line": [3, 9.5] },
1164 | { "curve": [3.5, 9.5, 3.5, 9] }, { "line": [3.5, 8] },
1165 | { "curve": [3.5, 7.5, 3, 7.5] }, { "line": [1, 7.5] },
1166 | { "curve": [0.5, 7.5, 0.5, 7] }, { "line": [0.5, 1] },
1167 | { "curve": [0.5, 0.5, 1, 0.5] }, { "line": [9, 0.5] },
1168 | { "curve": [9.5, 0.5, 9.5, 1] }, { "line": [9.5, 3.5] }],
1169 |
1170 | [{ "move": [9.5, 1] },
1171 | { "curve": [9.5, 0.5, 10, 0.5] }, { "line": [18, 0.5] },
1172 | { "curve": [18.5, 0.5, 18.5, 1] }, { "line": [18.5, 7] },
1173 | { "curve": [18.5, 7.5, 18, 7.5] }, { "line": [16, 7.5] },
1174 | { "curve": [15.5, 7.5, 15.5, 8] }, { "line": [15.5, 9] },
1175 | { "curve": [15.5, 9.5, 16, 9.5] }, { "line": [19, 9.5] }],
1176 |
1177 | [{ "move": [2.5, 5.5] }, { "line": [3.5, 5.5] }],
1178 |
1179 | [{ "move": [3, 2.5] },
1180 | { "curve": [3.5, 2.5, 3.5, 3] },
1181 | { "curve": [3.5, 3.5, 3, 3.5] },
1182 | { "curve": [2.5, 3.5, 2.5, 3] },
1183 | { "curve": [2.5, 2.5, 3, 2.5] }],
1184 |
1185 | [{ "move": [15.5, 5.5] }, { "line": [16.5, 5.5] }],
1186 |
1187 | [{ "move": [16, 2.5] }, { "curve": [16.5, 2.5, 16.5, 3] },
1188 | { "curve": [16.5, 3.5, 16, 3.5] }, { "curve": [15.5, 3.5, 15.5, 3] },
1189 | { "curve": [15.5, 2.5, 16, 2.5] }],
1190 |
1191 | [{ "move": [6, 2.5] }, { "line": [7, 2.5] }, { "curve": [7.5, 2.5, 7.5, 3] },
1192 | { "curve": [7.5, 3.5, 7, 3.5] }, { "line": [6, 3.5] },
1193 | { "curve": [5.5, 3.5, 5.5, 3] }, { "curve": [5.5, 2.5, 6, 2.5] }],
1194 |
1195 | [{ "move": [12, 2.5] }, { "line": [13, 2.5] }, { "curve": [13.5, 2.5, 13.5, 3] },
1196 | { "curve": [13.5, 3.5, 13, 3.5] }, { "line": [12, 3.5] },
1197 | { "curve": [11.5, 3.5, 11.5, 3] }, { "curve": [11.5, 2.5, 12, 2.5] }],
1198 |
1199 | [{ "move": [7.5, 5.5] }, { "line": [9, 5.5] }, { "curve": [9.5, 5.5, 9.5, 6] },
1200 | { "line": [9.5, 7.5] }],
1201 | [{ "move": [9.5, 6] }, { "curve": [9.5, 5.5, 10.5, 5.5] },
1202 | { "line": [11.5, 5.5] }],
1203 |
1204 |
1205 | [{ "move": [5.5, 5.5] }, { "line": [5.5, 7] }, { "curve": [5.5, 7.5, 6, 7.5] },
1206 | { "line": [7.5, 7.5] }],
1207 | [{ "move": [6, 7.5] }, { "curve": [5.5, 7.5, 5.5, 8] }, { "line": [5.5, 9.5] }],
1208 |
1209 | [{ "move": [13.5, 5.5] }, { "line": [13.5, 7] },
1210 | { "curve": [13.5, 7.5, 13, 7.5] }, { "line": [11.5, 7.5] }],
1211 | [{ "move": [13, 7.5] }, { "curve": [13.5, 7.5, 13.5, 8] },
1212 | { "line": [13.5, 9.5] }],
1213 |
1214 | [{ "move": [0, 11.5] }, { "line": [3, 11.5] }, { "curve": [3.5, 11.5, 3.5, 12] },
1215 | { "line": [3.5, 13] }, { "curve": [3.5, 13.5, 3, 13.5] }, { "line": [1, 13.5] },
1216 | { "curve": [0.5, 13.5, 0.5, 14] }, { "line": [0.5, 17] },
1217 | { "curve": [0.5, 17.5, 1, 17.5] }, { "line": [1.5, 17.5] }],
1218 | [{ "move": [1, 17.5] }, { "curve": [0.5, 17.5, 0.5, 18] }, { "line": [0.5, 21] },
1219 | { "curve": [0.5, 21.5, 1, 21.5] }, { "line": [18, 21.5] },
1220 | { "curve": [18.5, 21.5, 18.5, 21] }, { "line": [18.5, 18] },
1221 | { "curve": [18.5, 17.5, 18, 17.5] }, { "line": [17.5, 17.5] }],
1222 | [{ "move": [18, 17.5] }, { "curve": [18.5, 17.5, 18.5, 17] },
1223 | { "line": [18.5, 14] }, { "curve": [18.5, 13.5, 18, 13.5] },
1224 | { "line": [16, 13.5] }, { "curve": [15.5, 13.5, 15.5, 13] },
1225 | { "line": [15.5, 12] }, { "curve": [15.5, 11.5, 16, 11.5] },
1226 | { "line": [19, 11.5] }],
1227 |
1228 | [{ "move": [5.5, 11.5] }, { "line": [5.5, 13.5] }],
1229 | [{ "move": [13.5, 11.5] }, { "line": [13.5, 13.5] }],
1230 |
1231 | [{ "move": [2.5, 15.5] }, { "line": [3, 15.5] },
1232 | { "curve": [3.5, 15.5, 3.5, 16] }, { "line": [3.5, 17.5] }],
1233 | [{ "move": [16.5, 15.5] }, { "line": [16, 15.5] },
1234 | { "curve": [15.5, 15.5, 15.5, 16] }, { "line": [15.5, 17.5] }],
1235 |
1236 | [{ "move": [5.5, 15.5] }, { "line": [7.5, 15.5] }],
1237 | [{ "move": [11.5, 15.5] }, { "line": [13.5, 15.5] }],
1238 |
1239 | [{ "move": [2.5, 19.5] }, { "line": [5, 19.5] },
1240 | { "curve": [5.5, 19.5, 5.5, 19] }, { "line": [5.5, 17.5] }],
1241 | [{ "move": [5.5, 19] }, { "curve": [5.5, 19.5, 6, 19.5] },
1242 | { "line": [7.5, 19.5] }],
1243 |
1244 | [{ "move": [11.5, 19.5] }, { "line": [13, 19.5] },
1245 | { "curve": [13.5, 19.5, 13.5, 19] }, { "line": [13.5, 17.5] }],
1246 | [{ "move": [13.5, 19] }, { "curve": [13.5, 19.5, 14, 19.5] },
1247 | { "line": [16.5, 19.5] }],
1248 |
1249 | [{ "move": [7.5, 13.5] }, { "line": [9, 13.5] },
1250 | { "curve": [9.5, 13.5, 9.5, 14] }, { "line": [9.5, 15.5] }],
1251 | [{ "move": [9.5, 14] }, { "curve": [9.5, 13.5, 10, 13.5] },
1252 | { "line": [11.5, 13.5] }],
1253 |
1254 | [{ "move": [7.5, 17.5] }, { "line": [9, 17.5] },
1255 | { "curve": [9.5, 17.5, 9.5, 18] }, { "line": [9.5, 19.5] }],
1256 | [{ "move": [9.5, 18] }, { "curve": [9.5, 17.5, 10, 17.5] },
1257 | { "line": [11.5, 17.5] }],
1258 |
1259 | [{ "move": [8.5, 9.5] }, { "line": [8, 9.5] }, { "curve": [7.5, 9.5, 7.5, 10] },
1260 | { "line": [7.5, 11] }, { "curve": [7.5, 11.5, 8, 11.5] },
1261 | { "line": [11, 11.5] }, { "curve": [11.5, 11.5, 11.5, 11] },
1262 | { "line": [11.5, 10] }, { "curve": [11.5, 9.5, 11, 9.5] },
1263 | { "line": [10.5, 9.5] }]
1264 | ];
1265 |
1266 | Object.prototype.clone = function () {
1267 | var i, newObj = (this instanceof Array) ? [] : {};
1268 | for (i in this) {
1269 | if (i === 'clone') {
1270 | continue;
1271 | }
1272 | if (this[i] && typeof this[i] === "object") {
1273 | newObj[i] = this[i].clone();
1274 | } else {
1275 | newObj[i] = this[i];
1276 | }
1277 | }
1278 | return newObj;
1279 | };
1280 |
--------------------------------------------------------------------------------