diff --git a/.gitignore b/.gitignore index 4082974f..02e03ab7 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,4 @@ content/turtlebits.js content/welcome.css content/worker.js content/.well-known -.DS_Store +npm-shrinkwrap.json diff --git a/Documentation/Flowcharts/Open XML files with this link to edit.url b/Documentation/Flowcharts/Open XML files with this link to edit.url new file mode 100644 index 00000000..7284392f --- /dev/null +++ b/Documentation/Flowcharts/Open XML files with this link to edit.url @@ -0,0 +1,5 @@ +[{000214A0-0000-0000-C000-000000000046}] +Prop3=19,11 +[InternetShortcut] +IDList= +URL=https://www.draw.io/ diff --git a/Documentation/Flowcharts/Save Function/Pencilcode_Save.png b/Documentation/Flowcharts/Save Function/Pencilcode_Save.png new file mode 100644 index 00000000..4af90ea3 Binary files /dev/null and b/Documentation/Flowcharts/Save Function/Pencilcode_Save.png differ diff --git a/Documentation/Flowcharts/Save Function/Pencilcode_Save.xml b/Documentation/Flowcharts/Save Function/Pencilcode_Save.xml new file mode 100644 index 00000000..75e774f9 --- /dev/null +++ b/Documentation/Flowcharts/Save Function/Pencilcode_Save.xml @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/Documentation/Flowcharts/Signup&Login/Signup&Login.png b/Documentation/Flowcharts/Signup&Login/Signup&Login.png new file mode 100644 index 00000000..f058e958 Binary files /dev/null and b/Documentation/Flowcharts/Signup&Login/Signup&Login.png differ diff --git a/Documentation/Flowcharts/Signup&Login/Signup&Login.xml b/Documentation/Flowcharts/Signup&Login/Signup&Login.xml new file mode 100644 index 00000000..2d2a2992 --- /dev/null +++ b/Documentation/Flowcharts/Signup&Login/Signup&Login.xml @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/Documentation/Flowcharts/runAction/runAction b/Documentation/Flowcharts/runAction/runAction new file mode 100644 index 00000000..8ed145b6 --- /dev/null +++ b/Documentation/Flowcharts/runAction/runAction @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/Documentation/Flowcharts/runAction/runAction.png b/Documentation/Flowcharts/runAction/runAction.png new file mode 100644 index 00000000..eb141c8c Binary files /dev/null and b/Documentation/Flowcharts/runAction/runAction.png differ diff --git a/Documentation/controller.js.txt b/Documentation/controller.js.txt new file mode 100644 index 00000000..9b6b2e42 --- /dev/null +++ b/Documentation/controller.js.txt @@ -0,0 +1,7 @@ +This top section ("down to var model = window.pencilcode.model = {") is loading in all the other files of Pencilcode and getting ready for using them in the rest of the file. + +model is being built as a nulled out object which will later be filled out + +The rest of these functions will be commented inline, anything that should be noted across the entire file will be put in this file. + +view.on is from the view.js file, it sets up event listeners generally as on click functionality \ No newline at end of file diff --git a/Documentation/editor-main.js.txt b/Documentation/editor-main.js.txt new file mode 100644 index 00000000..22f55e6c --- /dev/null +++ b/Documentation/editor-main.js.txt @@ -0,0 +1,7 @@ +'use strict' - + forces strict usage, limits some common code bloopers by throwing exceptions + Prevents use of "unsafe" actions via throwing exceptions + Disables confusing/poorly thought out features of JS + +require('controller'); + Starts the application by including the main file of Pencilcode. \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index d027e2b5..68d26d97 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -346,6 +346,9 @@ module.exports = function(grunt) { // "build", for development, builds code without running tests. grunt.registerTask('build', ['browserify:dist', 'uglify', 'less', 'builddate']); + // "build", for development, builds code without running tests, in debug mode. + grunt.registerTask('build-debug', + ['browserify:dist', 'concat', 'less', 'builddate']); // default target: compile editor code and uglify turtlebits.js, and test it. grunt.registerTask('default', ['build', 'test']); diff --git a/README.md b/README.md index 1c9dd350..31e971fe 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,30 @@ + How To Build and Test PencilCode ================================ [![Build Status](https://travis-ci.org/PencilCode/pencilcode.png?branch=master)](https://travis-ci.org/PencilCode/pencilcode) -First install the prerequisites: git, nodejs, and grunt. Next, be sure you're in your home directory. Then: - -
-git clone https://github.com/PencilCode/pencilcode.git
-cd pencilcode
-npm install
-grunt
-grunt devserver
-
-On Windows Subsystem for Linux (WSL) *only*: - -
-git clone https://github.com/cacticouncil/pencilcode.git
-cd pencilcode
-npm install --no-shrinkwrap
-grunt
-grunt devserver
-
- -Development can be done on Linux, Mac, or Windows. -The prerequisites are a standard node.js development environment -which is very widely used, plus grunt (you'll need to -`npm install -g grunt-cli`). Prerequisites ------------- +There are three prerequisites: git, node.js, and grunt. Instructions are noted by operating system. -First, you need git, which is easy. On Linux, -just `sudo apt-get install git` or `sudo yum install git-core` -if you don't have it. - -Second, you need node.js (which is the `node` and `npm` binaries) -and `grunt` (which is the build tool popular in the node.js community). -The Ubuntu and Debian packages for node.js are pretty old, so don't -just apt-get install the packages. Get and build the latest `node` and -`npm` and `grunt` binaries as follows: +__Linux:__ -(For Linux:) +1. Git - +`sudo apt-get install git` on Debian/Ubuntu +`sudo yum install git-core` on CentOS / Redhat / Fedora +2. Node.js - +Some modern systems have recent enough tools to install node.js via the package manager. Here is how you'd do so on Ubuntu (native or under WSL): +
+sudo apt-get install npm git
+sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10
+sudo npm install -g grunt-cli
+
+If you are not sure, it is recommended you get and build the latest `node`, `npm`, and `grunt` binaries as follows:
 mkdir -p /tmp/nodejs && cd /tmp/nodejs
-wget -N http://nodejs.org/dist/v0.12.7/node-v0.12.7.tar.gz # http://nodejs.org/dist/node-latest.tar.gz
+wget -N http://nodejs.org/dist/v7.4.0/node-v7.4.0.tar.gz # http://nodejs.org/dist/node-latest.tar.gz
 tar xzvf node-*.tar.gz && cd `ls -d node-v*`
 ./configure --prefix=$HOME/local
 make install
@@ -53,19 +34,18 @@ npm install -g grunt-cli
 
Zsh users should change `bashrc` to `zshrc` in the above code. -(For Windows Subsystem for Linux:) +__Mac:__ -
-sudo apt-get install npm git
-sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10
-sudo npm install -g grunt-cli
-
- -(For Mac:) +1. On the Mac, git comes from Apple (you can get it as part of the +[Command line tools for XCode](https://developer.apple.com/downloads/index.action?q=xcode#)), +and if you'd rather not build it, node.js can be installed from +http://nodejs.org/download/. +You will still need to `sudo npm install -g grunt-cli`. +2. You'll need to install node.js as well:
 mkdir -p /tmp/nodejs && cd /tmp/nodejs
-curl http://nodejs.org/dist/v0.12.7/node-v0.12.7.tar.gz > node-latest.tar.gz
+curl http://nodejs.org/dist/v7.4.0/node-v7.4.0.tar.gz > node-latest.tar.gz
 tar xzvf node-*.tar.gz && cd `ls -d node-v*`
 ./configure --prefix=$HOME/local
 make install
@@ -74,23 +54,25 @@ source ~/.profile
 npm install -g grunt-cli
 
-The above drops all the built binaries into `~/local/bin` so you -don't need root. +The above drops all the built binaries into `~/local/bin` so you don't need root. -On the Mac, git comes from Apple (you can get it as part of the -[Command line tools for XCode](https://developer.apple.com/downloads/index.action?q=xcode#)), -and if you'd rather not build it, node.js can be installed from -http://nodejs.org/download/. -You will still need to `sudo npm install -g grunt-cli`. +__Windows:__ + +1. Git can be installed from here: http://git-scm.com/download/win -On Windows, git can be installed from here: -http://git-scm.com/download/win and node.js can be installed -from here: http://nodejs.org/download/. Windows development -is untested, but if you try it, let me know. +2. Node.js v7.x can be installed from here: http://nodejs.org/download/ -Because node.js does not work on cygwin, when I work with node.js -on a Windows box, I just run it with debian under a vbox instance -https://www.virtualbox.org/. + +Building and Running +--------------------- +To clone the repository, build, and run, execute these commands: +
+git clone https://github.com/PencilCode/pencilcode.git
+cd pencilcode
+npm install
+grunt
+grunt devserver
+
How To Experiment with PencilCode diff --git a/Step1_Download Node.js.URL b/Step1_Download Node.js.URL new file mode 100644 index 00000000..5086a018 --- /dev/null +++ b/Step1_Download Node.js.URL @@ -0,0 +1,6 @@ +[InternetShortcut] +URL=https://nodejs.org/en/download/current/ +IDList= +HotKey=0 +IconFile=C:\Users\exlted\AppData\Local\Mozilla\Firefox\Profiles\s32xppaq.default\shortcutCache\+I5_7JpOJ20L+FgHHv8EMg==.ico +IconIndex=0 diff --git a/Step2.txt b/Step2.txt new file mode 100644 index 00000000..459d064a --- /dev/null +++ b/Step2.txt @@ -0,0 +1,2 @@ +Install the Node file you just downloaded then run Step 3 + diff --git a/Step3_Initial_Install.bat b/Step3_Initial_Install.bat new file mode 100644 index 00000000..41f4ccd9 --- /dev/null +++ b/Step3_Initial_Install.bat @@ -0,0 +1 @@ +npm install -g grunt -cli diff --git a/Step4_Setup.bat b/Step4_Setup.bat new file mode 100644 index 00000000..b66c116a --- /dev/null +++ b/Step4_Setup.bat @@ -0,0 +1 @@ +npm install \ No newline at end of file diff --git a/StepFinal_Run_Server.bat b/StepFinal_Run_Server.bat new file mode 100644 index 00000000..70aa704d --- /dev/null +++ b/StepFinal_Run_Server.bat @@ -0,0 +1 @@ +grunt devserver \ No newline at end of file diff --git a/StepTest_Test.bat b/StepTest_Test.bat new file mode 100644 index 00000000..cd325a3a --- /dev/null +++ b/StepTest_Test.bat @@ -0,0 +1 @@ +grunt \ No newline at end of file diff --git a/content/lib/droplet.js b/content/lib/droplet.js index 3263c200..633c5b0c 100644 --- a/content/lib/droplet.js +++ b/content/lib/droplet.js @@ -63400,6 +63400,7 @@ hook('mousedown', 1, function(point, event, state) { this.dumpNodeForDebug(hitTestResult, line); } if (hitTestResult != null) { + this.blockFrom = "editor"; this.clickedBlock = hitTestResult; this.clickedBlockPaletteEntry = null; this.setCursor(this.clickedBlock.start.next); @@ -63973,6 +63974,7 @@ hook('mousedown', 6, function(point, event, state) { entry = ref3[j]; hitTestResult = this.hitTest(palettePoint, entry.block, this.paletteView); if (hitTestResult != null) { + this.blockFrom = "palette"; this.clickedBlock = entry.block; this.clickedPoint = point; this.clickedBlockPaletteEntry = entry; @@ -65880,11 +65882,39 @@ hook('mousedown', 10, function() { }); Editor.prototype.endDrag = function() { + this.draggingBlock.start.next._value; + var WhereDropped = "Dropped into Palette, "; + if (this.lastHighlight == null) { + WhereDropped = "Dropped into Palette, "; + } + else if (this.lastHighlight.type == "document") { + WhereDropped = "Dropped into top of Editor, "; + } + else if (this.lastHighlight.type == "socket") { + WhereDropped = "Dropped into Socket, " + } + else { + WhereDropped = "Dropped into Editor, "; + } + var node = this.draggingBlock.start; + var blockString = "[' " + while(node.next != null){ + if(node.type === "text"){ + blockString += node._value + " "; + } + node = node.next; + } + blockString += "'], " + WhereDropped; + //this.blockFrom = "palette"; + blockString += "Taken from " + this.blockFrom; + console.log(blockString); + $.get('/log/' + "BlockDropped", blockString); if (this.cursorAtSocket()) { this.setCursor(this.cursor, function(x) { return x.type !== 'socketStart'; }); } + var val = this.draggingBlock.start.next._value; this.draggingBlock = null; this.draggingOffset = null; this.lastHighlight = null; diff --git a/content/lib/font-awesome.css b/content/lib/font-awesome.css new file mode 100644 index 00000000..2af3612b --- /dev/null +++ b/content/lib/font-awesome.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/fonts/fontawesome-webfont.eot?v=4.3.0');src:url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/fonts/fontawesome-webfont.eot?#iefix&v=4.3.0') format('embedded-opentype'),url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/fonts/fontawesome-webfont.woff2?v=4.3.0') format('woff2'),url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/fonts/fontawesome-webfont.woff?v=4.3.0') format('woff'),url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/fonts/fontawesome-webfont.ttf?v=4.3.0') format('truetype'),url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/fonts/fontawesome-webfont.svg?v=4.3.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0, 0)}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-genderless:before,.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"} \ No newline at end of file diff --git a/content/lib/pc_py_wrapper.js b/content/lib/pc_py_wrapper.js index e7fea0cf..cbd0d0f5 100644 --- a/content/lib/pc_py_wrapper.js +++ b/content/lib/pc_py_wrapper.js @@ -17,11 +17,13 @@ var $builtinmodule = function (name) { // The doc string cannot be added in code as is usually done in Python, so we set it manually here. mod.__doc__ = "This module provides implemention of the Pencil Code internal functions in Python."; + mod.Window = window; + // Sk.builtin.func creates a Python function out of a JS function mod.addalemon = new Sk.builtin.func(function (data) { // Check arguments: func name, arguments passed, min number, max number Sk.builtin.pyCheckArgs("dumps", arguments, 1, 1); - return data.v + " with a lemon"; // Data comes in as a Python string; we have to use the value (v) in JS + return Sk.ffi.remapToJs(data) + " with a lemon"; }); //MOVE @@ -29,57 +31,41 @@ var $builtinmodule = function (name) { mod.fd = new Sk.builtin.func(function (sprite, distance) { Sk.builtin.pyCheckArgs("fd", arguments, 2, 2); if (sprite/* turtle */ === Sk.builtin.none.none$) { - return fd(distance.v); + return fd(Sk.ffi.remapToJs(distance)); } - return sprite.fd(distance.v); + return sprite.fd(Sk.ffi.remapToJs(distance)); }); mod.bk = new Sk.builtin.func(function (sprite, distance) { Sk.builtin.pyCheckArgs("bk", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return bk(distance.v); - } - return sprite.bk(distance.v); - }); - - mod.rt = new Sk.builtin.func(function (sprite, angle) { - Sk.builtin.pyCheckArgs("rt", arguments, 2, 2); - if (sprite === Sk.builtin.none.none$) { - return rt(angle.v); + return bk(Sk.ffi.remapToJs(distance)); } - return sprite.rt(angle.v); - }); - - mod.lt = new Sk.builtin.func(function (sprite, angle) { - Sk.builtin.pyCheckArgs("lt", arguments, 2, 2); - if (sprite === Sk.builtin.none.none$) { - return lt(angle.v); - } - return sprite.lt(angle.v); + return sprite.bk(Sk.ffi.remapToJs(distance)); }); mod.ra = new Sk.builtin.func(function (sprite, radius, angle) { Sk.builtin.pyCheckArgs("rt", arguments, 2, 3); if (sprite === Sk.builtin.none.none$) { - return rt((radius).v, angle.v); + return rt(Sk.ffi.remapToJs(radius), Sk.ffi.remapToJs(angle)); } - return sprite.rt((radius).v, angle.v); + return sprite.rt(Sk.ffi.remapToJs(radius), Sk.ffi.remapToJs(angle)); }); mod.la = new Sk.builtin.func(function (sprite, radius, angle) { Sk.builtin.pyCheckArgs("lt", arguments, 2, 3); if (sprite === Sk.builtin.none.none$) { - return lt((radius).v, angle.v); + return lt(Sk.ffi.remapToJs(radius), Sk.ffi.remapToJs(angle)); } - return sprite.lt((radius).v, angle.v); + return sprite.lt(Sk.ffi.remapToJs(radius), Sk.ffi.remapToJs(angle)); }); mod.speed = new Sk.builtin.func(function (sprite, value) { Sk.builtin.pyCheckArgs("speed", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return speed(value.v); + return speed(Sk.ffi.remapToJs(value)); } - return sprite.speed(value.v); + return sprite.speed(Sk.ffi.remapToJs(value)); }); mod.home = new Sk.builtin.func(function (sprite) { @@ -93,49 +79,57 @@ var $builtinmodule = function (name) { mod.turnto = new Sk.builtin.func(function (sprite, value) { Sk.builtin.pyCheckArgs("turnto", arguments, 2, 3); if (sprite === Sk.builtin.none.none$) { - return turnto(value.v); + return turnto(Sk.ffi.remapToJs(value)); } - return sprite.turnto(value.v); + return sprite.turnto(Sk.ffi.remapToJs(value)); }); mod.moveto = new Sk.builtin.func(function (sprite, value1, value2) { Sk.builtin.pyCheckArgs("moveto", arguments, 3, 3); if (sprite === Sk.builtin.none.none$) { - return moveto(value1.v, value2.v); + return moveto(Sk.ffi.remapToJs(value1), Sk.ffi.remapToJs(value2)); } - return sprite.moveto(value1.v, value2.v); + return sprite.moveto(Sk.ffi.remapToJs(value1), Sk.ffi.remapToJs(value2)); }); mod.movexy = new Sk.builtin.func(function (sprite, value1, value2) { Sk.builtin.pyCheckArgs("movexy", arguments, 3, 3); if (sprite === Sk.builtin.none.none$) { - return movexy(value1.v, value2.v); + return movexy(Sk.ffi.remapToJs(value1), Sk.ffi.remapToJs(value2)); } - return sprite.movexy(value1.v, value2.v); + return sprite.movexy(Sk.ffi.remapToJs(value1), Sk.ffi.remapToJs(value2)); }); mod.jumpto = new Sk.builtin.func(function (sprite, value1, value2) { Sk.builtin.pyCheckArgs("jumpto", arguments, 3, 3); if (sprite === Sk.builtin.none.none$) { - return jumpto(value1.v, value2.v); + return jumpto(Sk.ffi.remapToJs(value1), Sk.ffi.remapToJs(value2)); } - return sprite.jumpto(value1.v, value2.v); + return sprite.jumpto(Sk.ffi.remapToJs(value1), Sk.ffi.remapToJs(value2)); }); mod.jumpxy = new Sk.builtin.func(function (sprite, value1, value2) { Sk.builtin.pyCheckArgs("jumpxy", arguments, 3, 3); if (sprite === Sk.builtin.none.none$) { - return jumpxy(value1.v, value2.v); + return jumpxy(Sk.ffi.remapToJs(value1), Sk.ffi.remapToJs(value2)); } - return sprite.jumpxy(value1.v, value2.v); + return sprite.jumpxy(Sk.ffi.remapToJs(value1), Sk.ffi.remapToJs(value2)); }); mod.pause = new Sk.builtin.func(function (sprite, value) { Sk.builtin.pyCheckArgs("pause", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return pause(value); + return pause(Sk.ffi.remapToJs(value)); + } + return sprite.pause(Sk.ffi.remapToJs(value)); + }); + + mod.getxy = new Sk.builtin.func(function (sprite) { + Sk.builtin.pyCheckArgs("getxy", arguments, 1, 1); + if (sprite === Sk.builtin.none.none$) { + return new Sk.ffi.remapToPy(getxy()); } - return sprite.pause(value); + return new Sk.ffi.remapToPy(sprite.getxy()); }); //Control @@ -144,56 +138,73 @@ var $builtinmodule = function (name) { mod.button = new Sk.builtin.func(function (sprite, buttonclick, callee) { Sk.builtin.pyCheckArgs("button", arguments, 3, 3); if (sprite === Sk.builtin.none.none$) { - return button(buttonclick.v, function () { Sk.misceval.callsim(callee); } ); + return button(Sk.ffi.remapToJs(buttonclick), function () { Sk.misceval.callsim(callee); } ); } - //return sprite.button(buttonclick.v, function () { Sk.misceval.callsim(callee); } ); + //return sprite.button(Sk.ffi.remapToJs(buttonclick), function () { Sk.misceval.callsim(callee); } ); }); - mod.click = new Sk.builtin.func(function (sprite, fn) { + mod.click = new Sk.builtin.func(function (sprite, func) { Sk.builtin.pyCheckArgs("click", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return click( function () { Sk.misceval.callsim(fn); } ); + return click(function(eventData){ + var data = [eventData.x, eventData.y]; + Sk.misceval.callsim(func); } ); } //return sprite.click( function () { Sk.misceval.callsim(fn); } ); }); - mod.keydown = new Sk.builtin.func(function (sprite, key) { - Sk.builtin.pyCheckArgs("keydown", arguments, 2, 2); + mod.forever = new Sk.builtin.func(function (func) { + Sk.builtin.pyCheckArgs("forever", arguments, 1, 1); + return forever(function(){ Sk.misceval.callsim(func); } ); + }); + + mod.tick = new Sk.builtin.func(function (tps, func) { + Sk.builtin.pyCheckArgs("tick", arguments, 2, 2); + return tick(Sk.ffi.remapToJs(tps), function(){Sk.misceval.callsim(func)}); + }); + + mod.keydown = new Sk.builtin.func(function (sprite, key, func) { + Sk.builtin.pyCheckArgs("keydown", arguments, 3, 3); if (sprite === Sk.builtin.none.none$) { - return keydown(function () { Sk.misceval.callsim(key); }); + return keydown(Sk.ffi.remapToJs(key), function(){Sk.misceval.callsim(func)}); } //return sprite.keydown(function () { Sk.misceval.callsim(key); }); }); - mod.keyup = new Sk.builtin.func(function (sprite, key) { + mod.keyup = new Sk.builtin.func(function (sprite, key, func) { Sk.builtin.pyCheckArgs("keyup", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return keyup(function () { Sk.misceval.callsim(key); }); + return keyup(Sk.ffi.remapToJs(key), function () { Sk.misceval.callsim(func); }); } //return sprite.keyup(function () { Sk.misceval.callsim(key); }); }); + mod.pressed = new Sk.builtin.func(function (key) { + Sk.builtin.pyCheckArgs("pressed", arguments, 1, 1); + return Sk.ffi.remapToPy(pressed(Sk.ffi.remapToJs(key))); + }); + //Sound///////////// mod.play = new Sk.builtin.func(function (sprite, note) { Sk.builtin.pyCheckArgs("play", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return play(note.v); + return play(Sk.ffi.remapToJs(note)); } - return sprite.play(note.v); + return sprite.play(Sk.ffi.remapToJs(note)); }); //mod.tone3 = new Sk.builtin.func(function (a, b, c) { // Sk.builtin.pyCheckArgs("tone", arguments, 3, 3); - // return tone(a.v, b.v, c.v); + // return tone(Sk.ffi.remapToJs(a), Sk.ffi.remapToJs(b), Sk.ffi.remapToJs(c)); //}); mod.tone = new Sk.builtin.func(function (sprite, a, b, c) { Sk.builtin.pyCheckArgs("tone", arguments, 3, 4); if (sprite === Sk.builtin.none.none$) { - return tone(a.v, b.v, c.v); + return tone(Sk.ffi.remapToJs(a), Sk.ffi.remapToJs(b), Sk.ffi.remapToJs(c)); } - return sprite.tone(a.v, b.v, c.v); + return sprite.tone(Sk.ffi.remapToJs(a), Sk.ffi.remapToJs(b), Sk.ffi.remapToJs(c)); }); mod.silence = new Sk.builtin.func(function (sprite) { @@ -207,30 +218,37 @@ var $builtinmodule = function (name) { mod.say = new Sk.builtin.func(function (sprite, a) { Sk.builtin.pyCheckArgs("say", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return say(a.v); + return say(Sk.ffi.remapToJs(a)); } - return sprite.say(a.v); + return sprite.say(Sk.ffi.remapToJs(a)); }); + mod.audioplay = new Sk.builtin.func(function (sprite, url) { + Sk.builtin.pyCheckArgs("audioplay", arguments, 2, 2); + var urlplay = new Audio(Sk.ffi.remapToJs(url)); + + return urlplay.play(); + + }); // TEXT METHODS ///////////////////////////////////////////////////////// mod.write = new Sk.builtin.func(function (message) { Sk.builtin.pyCheckArgs("write", arguments, 1, 1); - return write(message.v); + return write(Sk.ffi.remapToJs(message)); }); mod.debug = new Sk.builtin.func(function (object) { Sk.builtin.pyCheckArgs("debug", arguments, 1, 1); - return debug(object.v); + return debug(Sk.ffi.remapToJs(object)); }); mod.type = new Sk.builtin.func(function (message) { Sk.builtin.pyCheckArgs("type", arguments, 1, 1); - return type(message.v); + return type(Sk.ffi.remapToJs(message)); }); mod.typebox = new Sk.builtin.func(function (color) { Sk.builtin.pyCheckArgs("typebox", arguments, 1, 1); - return typebox(color.v); + return typebox(Sk.ffi.remapToJs(color)); }); mod.typeline = new Sk.builtin.func(function () { @@ -240,7 +258,7 @@ var $builtinmodule = function (name) { mod.label = new Sk.builtin.func(function (name) { Sk.builtin.pyCheckArgs("label", arguments, 1, 1); - return label(name.v); + return label(Sk.ffi.remapToJs(name)); }); /*mod.await = new Sk.builtin.func(function (lambda_expression) { @@ -248,21 +266,38 @@ var $builtinmodule = function (name) { return await(lambda_expression.v); });*/ - mod.read = new Sk.builtin.func(function (prompt) { - Sk.builtin.pyCheckArgs("read", arguments, 1, 1); - return read(prompt.v); + mod.read = new Sk.builtin.func(function (prompt, func) { + Sk.builtin.pyCheckArgs("read", arguments, 2, 2); + if(func == Sk.builtin.none.none$) + { + return Sk.ffi.remapToPy(read(Sk.ffi.remapToJs(prompt))); + } + return Sk.ffi.remapToPy(read(Sk.ffi.remapToJs(prompt), function (w) { Sk.misceval.callsim(func, Sk.ffi.remapToPy(w)); })); }); - mod.readnum = new Sk.builtin.func(function (prompt) { - Sk.builtin.pyCheckArgs("readnum", arguments, 1, 1); - return readnum(prompt.v); + mod.readnum = new Sk.builtin.func(function (prompt, func) { + Sk.builtin.pyCheckArgs("readnum", arguments, 2, 2); + if(func == Sk.builtin.none.none$) + { + return Sk.ffi.remapToPy(readnum(Sk.ffi.remapToJs(prompt))); + } + return Sk.ffi.remapToPy(readnum(Sk.ffi.remapToJs(prompt), function (w) { Sk.misceval.callsim(func, Sk.ffi.remapToPy(w)); })); + }); + + mod.readstr = new Sk.builtin.func(function (prompt, func) { + Sk.builtin.pyCheckArgs("readstr", arguments, 2, 2); + if(func == Sk.builtin.none.none$) + { + return Sk.ffi.remapToPy(readstr(Sk.ffi.remapToJs(prompt))); + } + return Sk.ffi.remapToPy(readstr(Sk.ffi.remapToJs(prompt), function (w) { Sk.misceval.callsim(func, Sk.ffi.remapToPy(w)); })); }); //SPRITES///////////////// mod.Turtle = new Sk.builtin.func(function (value) { Sk.builtin.pyCheckArgs("Turtle", arguments, 1, 1); - return new Turtle(value.v); + return new Turtle(Sk.ffi.remapToJs(value)); }); mod.Sprite = new Sk.builtin.func(function () { @@ -272,7 +307,7 @@ var $builtinmodule = function (name) { // mod.Piano = new Sk.builtin.func(function (keys) { // Sk.builtin.pyCheckArgs("Piano", arguments, 1, 1); -// return new Piano(keys.v); +// return new Piano(Sk.ffi.remapToJs(keys)); // }); mod.Piano = new Sk.builtin.func(function () { Sk.builtin.pyCheckArgs("Piano", arguments, 0, 0); @@ -283,7 +318,25 @@ var $builtinmodule = function (name) { Sk.builtin.pyCheckArgs("Pencil", arguments, 0, 0); return new Pencil(); }); + + mod.copy = new Sk.builtin.func(function () { + Sk.builtin.pyCheckArgs("copy", arguments, 0, 0); + return new copy(); + }); + + mod.table = new Sk.builtin.func(function (rows, columns) { + Sk.builtin.pyCheckArgs("table", arguments, 2, 2); + return new table(Sk.ffi.remapToJs(rows), Sk.ffi.remapToJs(columns)); + }); + mod.cell = new Sk.builtin.func(function (sprite, rows, columns) { + Sk.builtin.pyCheckArgs("cell", arguments, 3, 3); + if(sprite === Sk.builtin.none.none$){ + return cell(Sk.ffi.remapToJs(rows), Sk.ffi.remapToJs(columns)); + } + return new sprite.cell(Sk.ffi.remapToJs(rows), Sk.ffi.remapToJs(columns)); + }); + //ART////// mod.hide = new Sk.builtin.func(function (sprite) { @@ -345,68 +398,122 @@ var $builtinmodule = function (name) { mod.pen = new Sk.builtin.func(function (sprite, color, size) { Sk.builtin.pyCheckArgs("pen", arguments, 3, 3); if (sprite === Sk.builtin.none.none$) { - return pen(color.v, size.v); + return pen(Sk.ffi.remapToJs(color), Sk.ffi.remapToJs(size)); } - return sprite.pen(color.v, size.v); + return sprite.pen(Sk.ffi.remapToJs(color), Sk.ffi.remapToJs(size)); }); mod.dot = new Sk.builtin.func(function (sprite, color, dia) { Sk.builtin.pyCheckArgs("dot", arguments, 3, 3); if (sprite === Sk.builtin.none.none$) { - return dot(color.v, dia.v); + return dot(Sk.ffi.remapToJs(color), Sk.ffi.remapToJs(dia)); } - return sprite.dot(color.v, dia.v); + return sprite.dot(Sk.ffi.remapToJs(color), Sk.ffi.remapToJs(dia)); }); mod.box = new Sk.builtin.func(function (sprite, color, size) { Sk.builtin.pyCheckArgs("box", arguments, 3, 3); if (sprite === Sk.builtin.none.none$) { - return box(color.v, size.v); + return box(Sk.ffi.remapToJs(color), Sk.ffi.remapToJs(size)); } - return sprite.box(color.v, size.v); + return sprite.box(Sk.ffi.remapToJs(color), Sk.ffi.remapToJs(size)); }); mod.fill = new Sk.builtin.func(function (sprite, color) { - Sk.builtin.pyCheckArgs("dot", arguments, 2, 2); + Sk.builtin.pyCheckArgs("fill", arguments, 2, 2); + if (sprite === Sk.builtin.none.none$) { + return fill(Sk.ffi.remapToJs(color)); + } + return sprite.fill(Sk.ffi.remapToJs(color)); + }); + + mod.img = new Sk.builtin.func(function (sprite, pic) { + Sk.builtin.pyCheckArgs("img", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return fill(color.v); + return img(Sk.ffi.remapToJs(pic)); } - return sprite.fill(color.v); + return new sprite.img(Sk.ffi.remapToJs(pic)); }); mod.wear = new Sk.builtin.func(function (sprite, color) { Sk.builtin.pyCheckArgs("wear", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return wear(color.v); + return wear(Sk.ffi.remapToJs(color)); } - return sprite.wear(color.v); + return sprite.wear(Sk.ffi.remapToJs(color)); }); mod.grow = new Sk.builtin.func(function (sprite, size) { Sk.builtin.pyCheckArgs("grow", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return grow(size.v); + return grow(Sk.ffi.remapToJs(size)); } - return sprite.grow(size.v); + return sprite.grow(Sk.ffi.remapToJs(size)); }); mod.drawon = new Sk.builtin.func(function (sprite, canvas) { Sk.builtin.pyCheckArgs("drawon", arguments, 2, 2); if (sprite === Sk.builtin.none.none$) { - return drawon(canvas.v); + return drawon(canvas); } - return sprite.drawon(canvas.v); + return sprite.drawon(canvas); }); - + + mod.arrow = new Sk.builtin.func(function (sprite, color, size) { + Sk.builtin.pyCheckArgs("arrow", arguments, 3, 3); + if (sprite === Sk.builtin.none.none$) { + return arrow(Sk.ffi.remapToJs(color), Sk.ffi.remapToJs(size)); + } + return sprite.arrow(Sk.ffi.remapToJs(color), Sk.ffi.remapToJs(size)); + }); + + mod.shown = new Sk.builtin.func(function (sprite) { + Sk.builtin.pyCheckArgs("shown", arguments, 1, 1); + if (sprite === Sk.builtin.none.none$) { + return Sk.ffi.remapToPy(shown()); + } + return Sk.ffi.remapToPy(shown()); + }); + + mod.hidden = new Sk.builtin.func(function (sprite) { + Sk.builtin.pyCheckArgs("hidden", arguments, 1, 1); + if (sprite === Sk.builtin.none.none$) { + return Sk.ffi.remapToPy(hidden()); + } + return Sk.ffi.remapToPy(hidden()); + }); + + mod.touches = new Sk.builtin.func(function (sprite, obj) { + Sk.builtin.pyCheckArgs("touches", arguments, 2, 2); + if (sprite === Sk.builtin.none.none$) { + return Sk.ffi.remapToPy(touches(Sk.ffi.remapToJs(obj))); + } + return Sk.ffi.remapToPy(sprite.touches(Sk.ffi.remapToJs(obj))); + }); + + mod.inside = new Sk.builtin.func(function (sprite, obj) { + Sk.builtin.pyCheckArgs("inside", arguments, 2, 2); + if(Sk.ffi.remapToJs(obj) == "window"){ + if (sprite === Sk.builtin.none.none$) { + return Sk.ffi.remapToPy(inside(window)); + } + return Sk.ffi.remapToPy(sprite.inside(window)); + } + if (sprite === Sk.builtin.none.none$) { + return Sk.ffi.remapToPy(inside(obj)); + } + return Sk.ffi.remapToPy(sprite.inside(obj)); + }); + //Operators mod.min = new Sk.builtin.func(function (value1, value2) { Sk.builtin.pyCheckArgs("min", arguments, 1, 2); - return min(value1.v, value2.v); + return min(Sk.ffi.remapToJs(value1), Sk.ffi.remapToJs(value2)); }); mod.random = new Sk.builtin.func(function (value) { Sk.builtin.pyCheckArgs("random", arguments, 1, 1); - return random(value.v); + return Sk.ffi.remapToPy(random(Sk.ffi.remapToJs(value))); }); return mod; diff --git a/content/lib/pencilcode.py b/content/lib/pencilcode.py index dc1dfbfc..bea049e2 100644 --- a/content/lib/pencilcode.py +++ b/content/lib/pencilcode.py @@ -1,7 +1,18 @@ import pencilcode_internal +normal = 'normal' +uniform = 'uniform' +position = 'position' +color = 'color' +colour = 'color' +gray = 'gray' +grey = 'gray' +window = "window" + # The SpriteObject class wraps a jQuery-turtle object so it can be used in Python. # This includes Turtle, Sprite, Piano, and Pencil objects. + + class SpriteObject(): def __init__(self, jsSpriteObject): self.jsSpriteObject = jsSpriteObject @@ -16,17 +27,17 @@ def fd(self, distance): def bk(self, distance): pencilcode_internal.bk(self.jsSpriteObject, distance) - def rt(self, angle): - pencilcode_internal.rt(self.jsSpriteObject, angle) + def rt(self, angle, radius=0): + pencilcode_internal.ra(self.jsSpriteObject, angle, radius) - def lt(self, angle): - pencilcode_internal.lt(self.jsSpriteObject, angle) - - def ra(self, radius, angle): - pencilcode_internal.ra(self.jsSpriteObject, radius, angle) - - def la(self, radius, angle): - pencilcode_internal.la(self.jsSpriteObject, radius, angle) + def lt(self, angle, radius=0): + pencilcode_internal.la(self.jsSpriteObject, angle, radius) + + def ra(self, angle, radius=0): + pencilcode_internal.ra(self.jsSpriteObject, angle, radius) + + def la(self, angle, radius=0): + pencilcode_internal.la(self.jsSpriteObject, angle, radius) def speed(self, value): pencilcode_internal.speed(self.jsSpriteObject, value) @@ -37,7 +48,7 @@ def home(self): def turnto(self, value): pencilcode_internal.turnto(self.jsSpriteObject, value) - def moveto(self, x, y): + def moveto(self, x, y = None): pencilcode_internal.moveto(self.jsSpriteObject, x, y) def movexy(self, x, y): @@ -50,7 +61,10 @@ def jumpxy(self, x, y): pencilcode_internal.jumpxy(self.jsSpriteObject, x, y) def pause(self, value): - pencilcode_internal.sleep(self.jsSpriteObject, value) + pencilcode_internal.pause(self.jsSpriteObject, value) + + def getxy(self): + return pencilcode_internal.getxy(self.jsSpriteObject) ###################### ## Control Commands ## @@ -58,7 +72,7 @@ def pause(self, value): def button(self, buttonClick, callee): pass#pencilcode_internal.button(self.jsSpriteObject, buttonClick, callee) - def keydown(self, key): + def keydown(self, func, key = None): pass#pencilcode_internal.keydown(self.jsSpriteObject, key) def click(self, t): @@ -91,15 +105,20 @@ def pd(self): def box(self, a, b): pencilcode_internal.box(self.jsSpriteObject,a,b) - + def box(self, a): + pencilcode_internal.box(self.jsSpriteObject,a,10) + def grow(self, a): pencilcode_internal.grow(self.jsSpriteObject,a) - def pen(self, color, size): - pencilcode_internal.pen(self.jsSpriteObject,color, size) - - def dot(self, color, size): - pencilcode_internal.dot(self.jsSpriteObject,color, size) + def pen(self, color=None, size=None): + pencilcode_internal.pen(self.jsSpriteObject,color, size) + + def dot(self, color=None, size=None): + pencilcode_internal.dot(self.jsSpriteObject,color,size) + + def arrow(self, color, size): + pencilcode_internal.arrow(self.jsSpriteObject, color, size) def fill(self, color): pencilcode_internal.fill(self.jsSpriteObject,color) @@ -107,9 +126,25 @@ def fill(self, color): def wear(self, name): pencilcode_internal.wear(self.jsSpriteObject,name) - def drawon(self, path): - pencilcode_internal.drawon(self.jsSpriteObject,path) - + def drawon(self, canvas): + pencilcode_internal.drawon(self.jsSpriteObject,canvas.jsSpriteObject) + + #def cell(self, rows, columns): + # pencilcode_internal.cell(self.jsSpriteObject, rows, columns) + + def shown(self): + return pencilcode_internal.shown(self.jsSpriteObject) + + def hidden(self): + return pencilcode_internal.hidden(self.jsSpriteObject) + + def touches(self, obj): + return pencilcode_internal.touches(self.jsSpriteObject, obj) + + def inside(self, obj): + + return pencilcode_internal.inside(self.jsSpriteObject, obj if obj == window else obj.jsSpriteObject) + #################### ## Sound Commands ## #################### @@ -126,7 +161,9 @@ def silence(self): def say(self, a): pencilcode_internal.say(self.jsSpriteObject, a) - + def audioplay(self, url): + player = pencilcode_internal.audioplay(self.jsSpriteObject, url) + # These commands act on the default turtle object (which is not wrapped.). ################### @@ -139,18 +176,19 @@ def fd(value): def bk(value): pencilcode_internal.bk(None, value) -def rt(value): - pencilcode_internal.rt(None, value) +def rt(angle, radius = 0): + pencilcode_internal.ra(None, angle, radius) -def lt(value): - pencilcode_internal.lt(None, value) +def lt(angle, radius = 0): + pencilcode_internal.la(None, angle, radius) -# Fix the rest, Stevie! ;)# All Done Jem! :) -def ra(radius, angle): - pencilcode_internal.ra(None, radius, angle) +def ra(angle, radius=0): + pencilcode_internal.ra(None, angle, radius) -def la(radius, angle): - pencilcode_internal.la(None, radius, angle) +def la(angle, radius=0): + pencilcode_internal.la(None, angle, radius) + +# Fix the rest, Stevie! ;)# All Done Jem! :) def speed(value): pencilcode_internal.speed(None, value) @@ -161,7 +199,7 @@ def home(): def turnto(value): pencilcode_internal.turnto(None, value) -def moveto(x, y): +def moveto(x, y = None): pencilcode_internal.moveto(None, x, y) def movexy(x, y): @@ -173,8 +211,11 @@ def jumpto(x, y): def jumpxy(x, y): pencilcode_internal.jumpxy(None, x, y) +def getxy(): + return pencilcode_internal.getxy(None) + def pause(value): - pencilcode_internal.sleep(None, value) + pencilcode_internal.pause(None, value) ################## ## Art Commands ## @@ -204,14 +245,23 @@ def pd(): def box(a, b): pencilcode_internal.box(None,a,b) +def box(a): + pencilcode_internal.box(None,a,10) + def grow(a): pencilcode_internal.grow(None,a) + +def img(pic): + return SpriteObject(pencilcode_internal.img(None, pic)) -def pen(color, size): +def pen(color=None, size=None): pencilcode_internal.pen(None,color, size) -def dot(color, size): - pencilcode_internal.dot(None,color, size) +def dot(color=None, size=None): + pencilcode_internal.dot(None,color,size) + +def arrow(color, size): + pencilcode_internal.arrow(None, color, size) def fill(color): pencilcode_internal.fill(None,color) @@ -219,20 +269,46 @@ def fill(color): def wear(name): pencilcode_internal.wear(None,name) -def drawon(path): - pencilcode_internal.drawon(None,path) +def drawon(canvas): + pencilcode_internal.drawon(None,canvas.jsSpriteObject) + +def shown(): + return pencilcode_internal.shown(None) + +def hidden(): + return pencilcode_internal.hidden(None) + +def touches(obj): + return pencilcode_internal.touches(None, obj) + +def inside(obj): + return pencilcode_internal.inside(None, obj if obj == window else obj.jsSpriteObject) ################### ## Text Commands ## ################### -def write(message): +def write(*args): + x = 0 + message = '' + for arg in args: + message = message + arg + ' ' + x += 1 + if x == 0: + raise AssertionError('Too few arguments passed in to write()') pencilcode_internal.write(message) def debug(object): pencilcode_internal.debug(object) -def type(message): +def type(*args): + x = 0 + message = '' + for arg in args: + message = message + arg + x += 1 + if x == 0: + raise AssertionError('Too few arguments passed in to type()') pencilcode_internal.type(message) def typebox(color): @@ -248,11 +324,14 @@ def await(lamda_exp): # TODO - this might be tricky pass -def read(prompt): - pencilcode_internal.read(prompt) +def read(prompt, func = None): + return pencilcode_internal.read(prompt, func) -def readnum(prompt): - pencilcode_internal.readnum(prompt) +def readnum(prompt, func = None): + return pencilcode_internal.readnum(prompt, func) + +def readstr(prompt, func = None): + return pencilcode_internal.readstr(prompt, func) #################### ## Sound Commands ## @@ -269,7 +348,8 @@ def silence(): def say(a): pencilcode_internal.say(None, a) - +def audioplay(url): + pencilcode_internal.audioplay(None, url) ###################### ## Control Commands ## ###################### @@ -278,12 +358,21 @@ def say(a): def button(buttonClick, callee): pencilcode_internal.button(None, buttonClick, callee) -def keydown(key): - pencilcode_internal.keydown(None, key) +def keydown(func, key = None): + pencilcode_internal.keydown(None, key, func) + +def pressed(key): + return pencilcode_internal.pressed(key) def click(t): pencilcode_internal.click(None, t) +def forever(func): + pencilcode_internal.forever(func) + +def tick(tps, func): + pencilcode_internal.tick(tps, func) + ###################### ## Sprites ## ###################### @@ -298,7 +387,16 @@ def Piano(): def Pencil(): return SpriteObject(pencilcode_internal.Pencil()) - + +def copy(): + return SpriteObject(pencilcode_internal.copy()) + +def table(rows, columns): + return SpriteObject(pencilcode_internal.table(rows, columns)) + +def cell(rows, columns): + return SpriteObject(pencilcode_internal.cell(None, rows, columns)) + ###################### ## Operators ## ###################### @@ -309,3 +407,5 @@ def random(a): def min(a, b = None): return pencilcode_internal.min(a,b) + + diff --git a/content/src/FontLoader.js b/content/src/FontLoader.js index f370611b..b2ff0f29 100644 --- a/content/src/FontLoader.js +++ b/content/src/FontLoader.js @@ -13,8 +13,8 @@ if (isIE) { } } -/** - * FontLoader detects when web fonts specified in the "fontFamiliesArray" array were loaded and rendered. Then it +/**FontLoader detects when web fonts specified in the "fontFamiliesArray" array were loaded and rendered. + *Then it * notifies the specified delegate object via "fontLoaded" and "fontsLoaded" methods when specific or all fonts were * loaded respectively. The use of this functions implies that the insertion of specified web fonts into the * document is done elsewhere. @@ -275,9 +275,7 @@ FontLoader.prototype = { } }; -/** - * Size object - * +/** Instantiates a Size object * @param width * @param height * @constructor @@ -287,8 +285,7 @@ function Size(width, height) { this.height = height; } -/** - * Compares receiver object to passed in size object. +/** Compares receiver object to passed in size object. * * @param otherSize * @returns {boolean} @@ -297,8 +294,8 @@ Size.prototype.isEqual = function(otherSize) { return (this.width === otherSize.width && this.height === otherSize.height); }; -/** - * SizeWatcher observes size of an element and notifies when its size is changed. It doesn't use any timeouts +/** SizeWatcher observes size of an element and notifies when its size is changed. + *It doesn't use any timeouts * to check the element size, when change in size occurs a callback method immediately invoked. * * To watch for element's size changes the element, and other required elements are appended to a container element diff --git a/content/src/advisor.js b/content/src/advisor.js index b5dbb3d5..0790efef 100644 --- a/content/src/advisor.js +++ b/content/src/advisor.js @@ -1,14 +1,24 @@ //////////////////////////////////////////////////////////////////////////r // ERROR MESSAGE ADVISOR SUPPORT /////////////////////////////////////////////////////////////////////////// - +/** Counts how many backslashes are in a line and returns the number found +* @param {string} line +* @param {int} offset +* @return {int} returns the number of backslashes in the line starting offset from the begining of it +*/ function backslashCount(line, offset) { for (var j = offset - 1; j > 0; --j) { if (line.charAt(j) != '//') break; } return offset - j - 1; } - +/** Finds the matching quote for the quote that's passed in, starting at offset +* @param {string} line +* @param {char} quoteChar +* @param {int} offset +* @param {bool} backslashable +* @return {int} returns the last position in line that is the first matching quote. +*/ function findMatchingQuote(line, quoteChar, offset, backslashable) { while (true) { var location = line.indexOf(quoteChar, offset); @@ -21,7 +31,10 @@ function findMatchingQuote(line, quoteChar, offset, backslashable) { } } -// Scan a program, locating +/** Scan a program, locating the first mismatched quote within the lines passed in +* @param {string[]} lines +* @return {int} first line in lines that has a mismached quote +**/ function firstLineWithMismatchedQuote(lines) { var quoteChar = null; var quoteLine = -1; @@ -63,6 +76,12 @@ function firstLineWithMismatchedQuote(lines) { return null; } +/** Takes in an error message and provdes advice on how to fix the error +* @param {string} msg +* @param {int} line +* @param {string} program +* @return {undefined} +**/ function errorAdvice(msg, line, program) { var advice, m, msg, r; var lines = program.split('\n'), index = line - 1, text = ''; diff --git a/content/src/cache.js b/content/src/cache.js index e692a0e1..99769754 100644 --- a/content/src/cache.js +++ b/content/src/cache.js @@ -15,9 +15,7 @@ var model = window.pencilcode.cache = { clear: clearCache }; -/** - * Initialize the cache object to the given - * name only if it's not available already. +/** Initialize the cache object to the given name only if it's not available already. * * @param {string} name * @returns {undefined} @@ -28,8 +26,7 @@ function initializeCache(name) { } } -/** - * Delete the cache object mapped with the given name +/** Delete the cache object mapped with the given name * * @param {string} name * @returns {undefined} @@ -38,16 +35,14 @@ function deleteCache(name) { delete cache[name]; } -/** - * Ceear the complete cache +/** Ceear the complete cache * @returns {undefined} */ function clearCache(){ cache = window.pencilcode.cacheObj = {}; } -/** - * Return the cached value mapped with the given name and key +/** Return the cached value mapped with the given name and key * * @param {string} cacheName * @param {string} key @@ -60,8 +55,7 @@ function get(cacheName, key) { return null; } -/** - * Save the given value mapped with the given name and key +/** Save the given value mapped with the given name and key * * @param {type} cacheName * @param {type} key diff --git a/content/src/codescan.js b/content/src/codescan.js index 1c474962..649ab55b 100644 --- a/content/src/codescan.js +++ b/content/src/codescan.js @@ -4,6 +4,10 @@ // looking for object names and /////////////////////////////////////////////////////////////////////////// +/** Scans multiline quotes and comments and omits those parts of the code in coffeescript code +* @param {string} code +* @returns {string} filtered code +*/ function stripCSMultiline(code) { // Really simple heuristic for coffeescript: // Just scan the multiline quote and comment delimeters, and @@ -46,6 +50,10 @@ function stripCSMultiline(code) { return keep.join(''); } +/** Builds a list of objects in coffeescript code +* @param {string} code +* @returns {string[]} +*/ function coffeeObjects(code) { code = stripCSMultiline(code); var lines = code.split('\n'); @@ -64,6 +72,10 @@ function coffeeObjects(code) { return result; } +/** Builds a list of objects in JavaScript code +* @param {string} code +* @returns {string[]} +*/ function jsObjects(code) { var lines = code.split('\n'); var result = []; @@ -81,6 +93,11 @@ function jsObjects(code) { return result; } +/** Runs coffeeObjects/jsObjects on the passed in code based on the passed in language +* @param {string} language +* @param {string} code +* @returns {undefined} +*/ function scanObjects(language, code) { var objects; if (/coffee/.test(language)) { diff --git a/content/src/controller.js b/content/src/controller.js index 73f5d5dc..7c72675e 100644 --- a/content/src/controller.js +++ b/content/src/controller.js @@ -64,6 +64,7 @@ var model = window.pencilcode.model = { crossFrameContext: getCrossFrameContext() }; +//logs events to the internal event log function logEvent(name, data) { $.get('/log/' + name, data); } @@ -133,11 +134,13 @@ function nosaveowner() { return model.ownername === 'frame'; } +//Determines if the current model can be saved function cansave() { return specialowner() || !model.username || model.tempThumbnail || view.isPaneEditorDirty(paneatpos('left')); } +//Updates the controls at the top of the screen based on what's currently going on function updateTopControls(addHistory) { var m = modelatpos('left'); // Update visible URL and main title name. @@ -280,11 +283,13 @@ function updateTopControls(addHistory) { // Set up some logging event handlers. // +//logs an event when palettes get selected view.on('selectpalette', function(pane, palname) { if (!palname) { palname = 'default'; } logEvent('~selectpalette', {name: palname.replace(/\s/g, '').toLowerCase()}); }); +//logs an event when blocks get picked up view.on('pickblock', function(pane, blockid) { logEvent('~pickblock', { id: blockid }); }); @@ -295,6 +300,8 @@ view.on('pickblock', function(pane, blockid) { // an event handler function // +//view.on is from the view.js file, it sets up event listeners generally as on click functionality + view.on('help', function() { view.flashNotification('Ask a question.' + @@ -325,6 +332,7 @@ view.on('new', function() { directoryname = ''; } window.location.href = '/edit/' + directoryname + untitled; + overwriteProtected = true; }); }); @@ -472,6 +480,7 @@ view.on('changehtmlcss', function(pane) { view.on('run', runAction); +//Runs the code in the editor pane function runAction() { var doc = view.getPaneEditorData(paneatpos('left')); if (!doc) { @@ -646,11 +655,13 @@ guide.on('guideurl', function(guideurl) { readNewUrl.suppress = false; }); +//Creates a hash based on the guideurl passed in function guideHash(guideurl) { return guideurl ? '#guide=' + (/[&#%]/.test(guideurl) ? encodeURIComponent(guideurl) : encodeURI(guideurl)) : ''; } +//Updates the URL that's shown to the user function updateVisibleUrl(baseurl, guideurl, addHistory) { model.guideUrl = guideurl; view.setVisibleUrl(baseurl + guideHash(guideurl), addHistory); @@ -778,6 +789,34 @@ view.on('splitscreen', function() { view.setPreviewMode(!view.getPreviewMode()); }); +var overwriteProtected = true; + +//A function that shows an overwrite dialog. takes in two callback functions on whether "yes" or "no" is selected on the dialog +function showOverwriteDialog(opts, yes, no){ + if(opts === { }){ + var opts = { + close: { }, + validate: function(state) { return this.close; } + }; + } + + opts.content = + '
' + + '
If you continue with this action it will overwrite a pre existing file
' + + '

' + + '' + + ''; + opts.init = function(dialog) { + dialog.find('.ok').on('click', yes); + dialog.find('.cancel').on('click', no); + } + opts.retrieveState = function(){ + return this.done; + } + view.showDialog(opts); +} + +//The "base" save functino which calls other save functions in special cases function saveAction(forceOverwrite, loginPrompt, doneCallback) { if (nosaveowner()) { return; @@ -791,8 +830,9 @@ function saveAction(forceOverwrite, loginPrompt, doneCallback) { return; } var doc = view.getPaneEditorData(paneatpos('left')); - var filename = modelatpos('left').filename; - var thumbnailDataUrl = ''; + var filename = modelatpos('left').filename + view.flashNotification("Saving may take some time, please wait"); + var thumbnailDataUrl = ''; if (!doc) { // There is no editor on the left (or it is misbehaving) - do nothing. console.log("Nothing to save."); @@ -823,9 +863,50 @@ function saveAction(forceOverwrite, loginPrompt, doneCallback) { } // Attempt to save. view.flashNotification('', true); + if(newdata.data === "" || newdata.data === "\n"){ + view.flashNotification("Cannot save empty file"); + return; + } storage.saveFile( model.ownername, filename, newdata, forceOverwrite, model.passkey, false, function(status) { + if(status.overwrite){ + //Let user know continuing with this save will overwrite a file + //Give the the option to overwrite or not, maybe add ability to rename here? + var opts = { + close: { }, + validate: function(state) { return this.close; } + }; + var ok = function(e){ + if(!opts.close.cancel) + { + storage.saveFile(model.ownername, filename, newdata, forceOverwrite, model.passkey, false, function(status) { + if (status.needauth) { + logInAndSave(filename, newdata, forceOverwrite, noteclean, + loginPrompt, doneCallback); + } else { + if (!model.username) { + // If not yet logged in but we have saved (e.g., no password needed), + // then log us in. + model.username = model.ownername; + } + handleSaveStatus(status, filename, noteclean); + if (doneCallback) { + doneCallback(); + } + } + }, false); + opts.close = {cancel: true}; + var x = document.getElementsByClassName("ok"); + x[0].click(); + } + } + var cancel = function(e){ + view.flashNotification('Failed to Save'); + } + showOverwriteDialog(opts, ok, cancel); + return; + } if (status.needauth) { logInAndSave(filename, newdata, forceOverwrite, noteclean, loginPrompt, doneCallback); @@ -840,9 +921,10 @@ function saveAction(forceOverwrite, loginPrompt, doneCallback) { doneCallback(); } } - }); + }, overwriteProtected); // After a successful save, mark the file as clean and update mtime. function noteclean(mtime) { + overwriteProtected = false; view.flashNotification('Saved.'); view.notePaneEditorCleanData(paneatpos('left'), newdata); logCodeEvent('save', filename, newdata.data, @@ -861,8 +943,10 @@ function saveAction(forceOverwrite, loginPrompt, doneCallback) { view.flashThumbnail(thumbnailDataUrl); } } + } +//Creates a key based on the username and password passed in function keyFromPassword(username, p) { if (!p) { return ''; } if (/^[0-9]{3}$/.test(p)) { return p; } @@ -874,6 +958,7 @@ function keyFromPassword(username, p) { return key; } +//Used to check how complex a password is function letterComplexity(s) { var maxcount = 0, uniqcount = 0, dupcount = 0, last = null, count = {}, j, c; for (j = 0; j < s.length; ++j) { @@ -890,6 +975,7 @@ function letterComplexity(s) { return uniqcount && (uniqcount / (maxcount + dupcount)); } +//A more specialized save function which is called when you are not in a user's saved file, or when you choose to switch users from a user's saved file function signUpAndSave(options) { if (!options) { options = {}; } var doc = view.getPaneEditorData(paneatpos('left')); @@ -1089,7 +1175,73 @@ function signUpAndSave(options) { username, rename, $.extend({}, doc), forceOverwrite, key, false, function(status) { - if (status.needauth) { + if (status.overwrite){ + var opts = { + close: { }, + validate: function(state) { return this.close; } + }; + var ok = function(e){ + storage.saveFile(username, rename, $.extend({}, doc), forceOverwrite, key, false, function(status){ + if (status.needauth) { + state.update({ + disable: false, + info: 'Wrong password.' + }); + view.clearPane(paneatpos('right')); + } else if (status.newer) { + state.update({ + disable: false, + info: 'Did not overwrite newer file.' + }); + view.clearPane(paneatpos('right')); + } else if (status.transient) { + state.update({ + disable: false, + info: 'Network down.' + }); + view.clearPane(paneatpos('right')); + } else if (status.error) { + state.update({ + disable: false, + info: status.error + }); + view.clearPane(paneatpos('right')); + } else { + view.notePaneEditorCleanData(paneatpos('left'), doc); + storage.deleteBackup(mp.filename); + storage.deleteBackup(rename); + view.flashNotification('Saved.'); + var hostpath = username + '.' + window.pencilcode.domain + + '/edit/' + rename, + newurl = '//' + hostpath + + '#login=' + username + ':' + (key ? key : ''); + if (model.guideUrl) { + var guideurl = model.guideUrl; + newurl += '&guide=' + (/[&#%]/.test(guideurl) ? + encodeURIComponent(guideurl) : encodeURI(guideurl)); + } + if (location.host + location.pathname == hostpath) { + // If there is no change in URL, be careful to close the dialog. + // We don't do this always, because the 'new user' flow + // will do a 'history.back' when closing the dialog. + state.update({cancel: true}); + } + if (options.nohistory) { + window.location.replace(newurl); + } else { + window.location.href = newurl; + } + } + + }, false); + } + var cancel = function(e){ + view.flashNotification('Failed to Save'); + } + showOverwriteDialog(opts, ok, cancel); + return; + } + if (status.needauth) { state.update({ disable: false, info: 'Wrong password.' @@ -1159,6 +1311,7 @@ function signUpAndSave(options) { }); } +//A more specialized save function that coppies and saves the file off as a new file function saveAs() { if (!model.username) { signUpAndSave(); @@ -1187,6 +1340,49 @@ function saveAs() { model.username, newFilename, doc, true, model.passkey, false, function(m) { + if (m.overwrite){ + var opts = { + close: { }, + validate: function(state) { return this.close; } + }; + + var ok = function(e){ + storage.saveFile(model.username, newFilename, doc, true, model.passkey, false, function(m){ + if (m.needauth) { + signUpAndSave(); + return; + } + if (m.error) { + state.update({info: m.error, disable: true}); + return; + } + if (oldFilename != newFilename) { + storage.deleteBackup(oldFilename); + } + state.update({cancel: true}); + mp.filename = newFilename; + view.noteNewFilename(pp, newFilename); + view.notePaneEditorCleanData(pp, doc); + updateTopControls(false); + view.flashNotification('Saved as ' + newFilename); + view.setPrimaryFocus(); + logCodeEvent('save', newFilename, doc.data, + view.getPaneEditorBlockMode(pp), + view.getPaneEditorLanguage(pp)); + var oldmtime = mp.data.mtime || 0; + if (m.mtime) { + mp.data.mtime = Math.max(m.mtime, oldmtime); + } + + }, false); + } + var cancel = function(e){ + view.flashNotification('Failed to Save'); + } + + showOverwriteDialog(opts, ok, cancel); + return; + } if (m.needauth) { signUpAndSave(); return; @@ -1217,6 +1413,7 @@ function saveAs() { }); } +//A more specialized save function which is called when you are in a user's saved file function logInAndSave(filename, newdata, forceOverwrite, noteclean, loginPrompt, doneCallback) { if (!filename || !newdata || nosaveowner()) { @@ -1235,6 +1432,30 @@ function logInAndSave(filename, newdata, forceOverwrite, model.username, filename, newdata, forceOverwrite, model.passkey, false, function(m) { + if (m.overwrite){ + var opts = { + close: { }, + validate: function(state) { return this.close; } + }; + var ok = function(e){ + storage.saveFile(model.username, filename, newdata, forceOverwrite, model.passkey, false, function(m){ + if (m.needauth) { + state.update({info: 'Wrong password.', disable: false}); + return; + } + state.update({cancel: true}); + handleSaveStatus(m, filename, noteclean); + if (doneCallback) { + doneCallback(); + } + }, false); + } + var cancel = function(e){ + view.flashNotification('Failed to Save'); + } + showOverwriteDialog(opts, ok, cancel); + return; + } if (m.needauth) { state.update({info: 'Wrong password.', disable: false}); return; @@ -1249,6 +1470,7 @@ function logInAndSave(filename, newdata, forceOverwrite, }); } +//Checks to see if a special case needs to happen when saving (like if there is a newer save on the server or you don't have an active connection to the server and will only be backing up to local cookies) function handleSaveStatus(status, filename, noteclean) { if (status.newer) { view.flashNotification('Newer copy on network. ' + @@ -1286,12 +1508,14 @@ function handleSaveStatus(status, filename, noteclean) { } } +//Saves a cookie for the login information function saveLoginCookie() { cookie('login', (model.username || '') + ':' + (model.passkey || ''), { expires: 1, path: '/' }); } var requestedBlockMode = null; +//Changes the editor pane between text and block modes depending on what state they are set to function loadBlockMode() { var result = requestedBlockMode; if (result === null) { @@ -1306,6 +1530,7 @@ function loadBlockMode() { return result; } +//Updates the requested block mode to "on" function saveBlockMode(on) { requestedBlockMode = on; if (model.ownername != 'frame') { @@ -1313,6 +1538,7 @@ function saveBlockMode(on) { } } +//Loads the default metadata into the pane metadata function loadDefaultMeta() { if (model.ownername == 'frame') { return null; @@ -1325,6 +1551,7 @@ function loadDefaultMeta() { return null; } +//Updates the default metadata for loadDefaultMeta() function saveDefaultMeta(meta) { if (model.ownername == 'frame') { return; @@ -1344,6 +1571,7 @@ function saveDefaultMeta(meta) { } } +//Chooses a filename for new files based on what's saved in the directory function chooseNewFilename(dirlist) { if (!dirlist) { return 'unutitled'; } if (dirlist.length === 0) { return 'first';} @@ -1361,6 +1589,7 @@ function chooseNewFilename(dirlist) { view.on('link', handleDirLink); +//Handles links to direct files, loading them from the server and into the needful pane function handleDirLink(pane, linkname) { var base = model.pane[pane].filename; if (base === null) { return; } @@ -1445,6 +1674,7 @@ view.on('done', function() { doneWithFile(modelatpos('left').filename); }); +//Appears to handle the "back" action... somehow... function doneWithFile(filename) { if (!filename || !model.ownername) { if (window.location.href == @@ -1561,6 +1791,7 @@ view.on('rename', function(newname) { view.on('popstate', readNewUrl); +//Used to move a file (when in a user's saved file and logged out) function logInAndMove(filename, newfilename, completeRename) { if (!filename || !newfilename) { return; @@ -1596,6 +1827,7 @@ function logInAndMove(filename, newfilename, completeRename) { }); } +//Notes if a file (at position) is unsaved function noteIfUnsaved(position) { var m = modelatpos(position).data; if (m && m.unsaved) { @@ -1608,6 +1840,8 @@ function noteIfUnsaved(position) { } } +//Switches where the panes are in the model by moving each one a step left +//As there are 3 models, this means that back goes to right, right goes to left and left goes to back function rotateModelLeft(addHistory) { debug.bindframe(null); view.rotateLeft(); @@ -1618,6 +1852,8 @@ function rotateModelLeft(addHistory) { updateTopControls(addHistory); } +//Same as rotateModelLeft, but goes the other direction +//Back goes to left, left goes to right, and right goes to back function rotateModelRight(addHistory) { debug.bindframe(null); view.rotateRight(); @@ -1627,17 +1863,20 @@ function rotateModelRight(addHistory) { updateTopControls(addHistory); } +//Checks if the File passed in (candidate) is within the directory passed in (base) function isFileWithin(base, candidate) { if (base.length && !/\/%/.test(base)) { base += '/'; } return candidate.length > base.length && candidate.indexOf(base) === 0; } +//Checks for all possible cases of "false" that could be being passed around function falsish(s) { return !s || s == '0' || s == 'false' || s == 'none' || s == 'null' || s == 'off' || s == 'no'; } +//Determines what should be going on when a new URL is loaded function readNewUrl(undo) { if (readNewUrl.suppress) { return; @@ -1832,6 +2071,7 @@ function readNewUrl(undo) { loadFileIntoPosition('left', filename, isdir, isdir, afterLoad); } +//Loads data for "left" panel from the network function directNetLoad() { var pos = 'left'; var filename = modelatpos(pos).filename; @@ -1844,10 +2084,12 @@ view.on('netload', directNetLoad); var loadNumber = 0; +//Increments loadNumber function nextLoadNumber() { return ++loadNumber; } +//Clears out a model at the passed in position function cancelAndClearPosition(pos) { debug.bindframe(null); view.clearPane(paneatpos(pos), false); @@ -1859,6 +2101,7 @@ function cancelAndClearPosition(pos) { modelatpos(pos).running = false; } +//Checks to see if we can use instruments on this browser function instrumentCode(code, language) { try { if (language === 'javascript') { @@ -1892,6 +2135,7 @@ function instrumentCode(code, language) { return code; } +//runs the code at the passed in position in a program pane, internal function, called by other functions that are setup as callbacks on other actions function runCodeAtPosition(position, doc, filename, emptyOnly) { var m = modelatpos(position); if (!m.running) { @@ -1921,6 +2165,7 @@ function runCodeAtPosition(position, doc, filename, emptyOnly) { }, 1); } +//Determines whether we are sorting the directory by date or not by default function defaultDirSortingByDate() { if (!specialowner()) return false; try { @@ -1931,6 +2176,7 @@ function defaultDirSortingByDate() { } } +//Sets what method we are by default sorting the directory by function setDefaultDirSortingByDate(f) { try { if (f) { @@ -1942,6 +2188,7 @@ function setDefaultDirSortingByDate(f) { } } +//Creates a new file into the position that's passed in (generally left) function createNewFileIntoPosition(position, filename, text, meta) { var pane = paneatpos(position); var mpp = model.pane[pane]; @@ -1964,7 +2211,7 @@ function createNewFileIntoPosition(position, filename, text, meta) { logCodeEvent('new', filename, text, mode, view.getPaneEditorLanguage(pane)); } - +//Loads a file into the passed in position (generally left) function loadFileIntoPosition(position, filename, isdir, forcenet, cb) { var pane = paneatpos(position); var mpp = model.pane[pane]; @@ -2031,6 +2278,7 @@ function loadFileIntoPosition(position, filename, isdir, forcenet, cb) { } }; +//Gives information needed to sort the items passed in by date function sortByDate(a, b) { if (b.mtime != a.mtime) { return b.mtime - a.mtime; @@ -2038,6 +2286,7 @@ function sortByDate(a, b) { return sortByName(a, b); } +//Gives the information to sort the items passed in by name function sortByName(a, b) { var aName = a.name.toLowerCase(); var bName = b.name.toLowerCase(); @@ -2051,6 +2300,7 @@ function sortByName(a, b) { return 0; } +//Renders the directory in the passed in position function renderDirectory(position) { cache.clear(); var pane = paneatpos(position); @@ -2061,12 +2311,13 @@ function renderDirectory(position) { updateTopControls(false); } - +//Updates the sorting of a directory (generally a rendered directory) function updateSortResults(pane) { view.setPaneLinks(pane,getUpdatedLinksArray(pane)); updateTopControls(false); } +//Updates the items in a directory during a search of said directory function updateSearchResults(pane, search, cb) { search = search ? search.toLowerCase() : ''; var mpp = model.pane[pane]; @@ -2112,6 +2363,7 @@ function updateSearchResults(pane, search, cb) { } } +//Gets an array of links from the pane that's been passed in (generally a directory) function getUpdatedLinksArray(pane) { var mpp = model.pane[pane]; var m = mpp.data; @@ -2186,6 +2438,7 @@ function isEmptyDoc(doc) { (!doc.meta.css || !doc.data.css.trim())))); } +//Shortens a passed in URL function shortenUrl(url, cb) { var reqObj = { dataType: 'json', @@ -2216,6 +2469,7 @@ function shortenUrl(url, cb) { } } +//Adds a cookie into the browser function cookie(key, value, options) { // write if (value !== undefined) { diff --git a/content/src/debug.js b/content/src/debug.js index 104cae53..c36188fb 100644 --- a/content/src/debug.js +++ b/content/src/debug.js @@ -40,11 +40,18 @@ Error.stackTraceLimit = 20; // Remembers the targetWindow, and clears all logged debug records. // Calling bindframe also resets firstSessionId, so that callbacks // having to do with previous sessions are ignored. -function bindframe(w, p) { - if (!targetWindow && !w || targetWindow === w) return; +/** Resets the debugger state +* Remembers the targetWindow and clears all logged debug records. +* Calling bindframe also resets firstSessionId so that callbacks having to do with previous sessions are ignored +* @param {object} Window +* @param {object} Pane +* @returns {undefined} +*/ +function bindframe(Window, Pane) { + if (!targetWindow && !Window || targetWindow === Window) return; // Do not bind to nested subframes - only an immediate child frame. - if (targetWindow && p !== window) return; - targetWindow = w; + if (targetWindow && Pane !== window) return; + targetWindow = Window; cachedParseStack = {}; debugRecordsByDebugId = {}; debugRecordsByLineNo = {}; @@ -153,6 +160,9 @@ var debug = window.ide = { ////////////////////////////////////////////////////////////////////// // STUCK PROGRAM SUPPORT ////////////////////////////////////////////////////////////////////// +/** Detects whether the running program is stuck and throws errors when a program is determined to be stuck +* @returns {undefined} +*/ function detectStuckProgram() { stuckComplexity.lines += 1; if (stuckComplexity.lines % 100 != 1) return; @@ -204,12 +214,30 @@ var showPopupErrorMessage = function (msg) { center.innerHTML = msg; } +/** Reports SeeEval debug message +* @param {Unused} method +* @param {Unused} debugId +* @param {Unused} length +* @param {Unused} coordId +* @param {Unused} elem +* @param {Unused} args +* @returns {undefined} +*/ function reportSeeeval(method, debugId, length, coordId, elem, args){ currentDebugId += 1; record = {seeeval: true}; debugRecordsByDebugId[currentDebugId] = record; } +/** Reports appear debug message +* @param {function} method +* @param {int} debugId +* @param {Unused} length +* @param {int} coordId +* @param {object} elem +* @param {object[]} args +* @returns {undefined} +*/ function reportAppear(method, debugId, length, coordId, elem, args){ stuckComplexity.moves += 1; var recordD = debugRecordsByDebugId[debugId]; @@ -229,6 +257,15 @@ function reportAppear(method, debugId, length, coordId, elem, args){ } } +/** Reports SeeEval debug message +* @param {function} method +* @param {int} debugId +* @param {Unused} length +* @param {int} coordId +* @param {object} elem +* @param {Unused} args +* @returns {undefined} +*/ function reportResolve(method, debugId, length, coordId, elem, args){ var recordD = debugRecordsByDebugId[debugId]; if (recordD) { @@ -248,6 +285,10 @@ function reportResolve(method, debugId, length, coordId, elem, args){ // The error event is triggered when an uncaught exception occurs. // The err object is an exception or an Event object corresponding // to the error. +/** Reports the passed in error +* @param {string/object} err +* @returns {undefined} +*/ function reportError(err) { var line = editorLineNumberForError(err); var m = view.getPaneEditorData(view.paneid('left')); @@ -256,13 +297,20 @@ function reportError(err) { showDebugMessage(advice.message); } -function showDebugMessage(m) { +/** Shows the passed in debug message +* @param {string} message +* @returns {undefined} +*/ +function showDebugMessage(message) { var script = ( - '(' + showPopupErrorMessage.toString() + '(' + JSON.stringify(m) + '));'); + '(' + showPopupErrorMessage.toString() + '(' + JSON.stringify(message) + '));'); view.evalInRunningPane(view.paneid('right'), script, true); } - +/** collects the coordinates of the passed in element +* @param {object} elem +* @returns {undefined} +*/ function collectCoords(elem) { try { // TODO: when the element is not a turtle with the standard @@ -276,6 +324,10 @@ function collectCoords(elem) { } }; +/** Displays the protractor for the passed in record +* @param {object} record +* @returns {undefined} +*/ function displayProtractorForRecord(record) { // TODO: generalize this for turtles that are not in the main field. var origin = targetWindow.jQuery('#field').offset(); @@ -294,6 +346,10 @@ function displayProtractorForRecord(record) { // translate(tx, ty) rotate(rot) scale(sx, sy) rotate(twi) // (with each component optional). // This function quickly parses this form into a canonicalized object. +/** parses the transform from the turtle into a canonicalized object +* @param {string} transform +* @returns {undefined} +*/ function parseTurtleTransform(transform) { if (transform === 'none') { return {tx: 0, ty: 0, rot: 0, sx: 1, sy: 1, twi: 0}; @@ -311,14 +367,20 @@ function parseTurtleTransform(transform) { return {tx:tx, ty:ty, rot:rot, sx:sx, sy:sy, twi:twi}; } -// Highlights the given line number as a line being traced. +/** Highlights the given line number as a line being traced. +* @param {int} line +* @returns {undefined} +*/ function traceLine(line) { view.markPaneEditorLine( view.paneid('left'), line, 'guttermouseable', true); view.markPaneEditorLine(view.paneid('left'), line, 'debugtrace'); } -// Unhighlights the given line number as a line no longer being traced. +/** Unhighlights the given line number as a line no longer being traced. +* @param {int} line +* @returns {undefined} +*/ function untraceLine(line) { view.clearPaneEditorLine(view.paneid('left'), line, 'debugtrace'); } @@ -335,6 +397,10 @@ function untraceLine(line) { // },... // ] // Fields that are unknown are present but with value undefined or null. +/** Parses an Error or ErrorEvent into a specified JSON structure +* @param {string/object} err +* @returns {undefined} +*/ function parsestack(err) { var parsed = [], lines, j, line; // This code currently only works on Chrome. @@ -375,6 +441,10 @@ function parsestack(err) { // Returns the (1-based) line number for an error object, if any; // or returns null if none can be figured out. +/** Returns a 1-based line number for an error object, returns null if not able to be figured out +* @param {object} error +* @returns {int} +*/ function editorLineNumberForError(error) { if (!error || !targetWindow) return null; if (!(error instanceof targetWindow.Error)) { @@ -422,7 +492,6 @@ function editorLineNumberForError(error) { ////////////////////////////////////////////////////////////////////// // PARSE ERROR HIGHLIGHTING ////////////////////////////////////////////////////////////////////// - view.on('parseerror', function(pane, err) { if (err.loc) { // The markPaneEditorLine function uses 1-based line numbering. @@ -439,7 +508,6 @@ view.on('parseerror', function(pane, err) { ////////////////////////////////////////////////////////////////////// // PARSE ERROR HIGHLIGHTING ////////////////////////////////////////////////////////////////////// - view.on('parseerror', function(pane, err) { if (err.loc) { // The markPaneEditorLine function uses 1-based line numbering. @@ -485,6 +553,11 @@ view.on('icehover', function(pane, ev) { } }); +/** Converts a transform into a final coordinate after applying said transform onto the passed in origin +* @param {object} origin +* @param {objecr} astransform +* @returns {object} +*/ function convertCoords(origin, astransform) { if (!origin) { return null; } if (!astransform || !astransform.transform) { return null; } @@ -499,6 +572,10 @@ function convertCoords(origin, astransform) { } var lastRunTime = 0; +/** Takes in a command to determine what the stopButton should do (currently only supports "flash" command) +* @param {string} command +* @returns {bool} +*/ function stopButton(command) { if (command === 'flash') { lastRunTime = +new Date; @@ -522,6 +599,9 @@ function stopButton(command) { return stopButtonShown; } +/** starts the process of polling the main debug window +* @returns {undefined} +*/ function startPollingWindow() { if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; } if (!targetWindow || !targetWindow.jQuery || !targetWindow.jQuery.turtle || @@ -535,6 +615,9 @@ function startPollingWindow() { pollTimer = setTimeout(pollForStop, 100); } +/** stops the process of polling the main debug window +* @returns {undefined} +*/ function stopPollingWindow() { if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; } if (stopButtonShown) { @@ -543,6 +626,9 @@ function stopPollingWindow() { } } +/** Attempts to see if the main debug window is able to be stopped +* @returns {undefined} +*/ function pollForStop() { pollTimer = null; if (!targetWindow || !targetWindow.jQuery || !targetWindow.jQuery.turtle || diff --git a/content/src/draw-protractor.js b/content/src/draw-protractor.js index 182a7652..a76e8b7d 100644 --- a/content/src/draw-protractor.js +++ b/content/src/draw-protractor.js @@ -8,6 +8,10 @@ var $ = require('jquery'), eval(see.scope('drawProtractor')); +/** Turns v from any possible degree entered to 0-360 version of said degree measurement +* @param {numeric} v +* @returns {int} +*/ function to360(v) { while (v < 0) { v = 7200 - Math.abs(v); @@ -15,6 +19,11 @@ function to360(v) { return v % 360; } +/** Renders the protractor "step" onto "canvas" +* @param {object} canvas +* @parm {object} step +* @returns {undefined} +*/ function renderProtractor(canvas, step) { var ctx = canvas[0].getContext('2d'); ctx.resetTransform(); @@ -80,6 +89,16 @@ function renderProtractor(canvas, step) { ctx.restore(); } +/** Draws an arc-ed arrow based on the passed in data onto the canvas +* @param {object} context +* @param {numeric} cx +* @param {numeric} cy +* @param {numeric} radius +* @param {numeric} fromangle +* @param {numeric} toangle +* @param {bool} ccw +* @returns {undefined} +*/ function canvas_arc_arrow(context, cx, cy, radius, fromangle, toangle, ccw) { context.arc(cx, cy, radius, fromangle, toangle, ccw); var headx = cx + radius * Math.cos(toangle), @@ -88,6 +107,14 @@ function canvas_arc_arrow(context, cx, cy, radius, fromangle, toangle, ccw) { headx, heady, toangle + (ccw ? -1 : 1) * Math.PI / 2, 10); } +/** Draws a straight arrow based on the passed in data onto the canvas +* @param {object} context +* @param {numeric} fromx +* @param {numeric} fromy +* @param {numeric} tox +* @param {numeric} toy +* @returns {undefined} +*/ function canvas_straight_arrow(context, fromx, fromy, tox, toy){ var headlen = 10; // length of head in pixels var angle = Math.atan2(toy - fromy, tox - fromx); @@ -96,6 +123,14 @@ function canvas_straight_arrow(context, fromx, fromy, tox, toy){ canvas_arrow_head(context, tox, toy, angle, headlen); } +/** Draws an arrow head onto the canvas based on the passed in data onto the canvas +* @param {object} context +* @param {numeric} x +* @param {numeric} y +* @param {numeric} angle +* @param {numeric} headlen +* @returns {undefined} +*/ function canvas_arrow_head(context, x, y, angle, headlen) { context.moveTo(x - headlen * Math.cos(angle - Math.PI/6), y - headlen * Math.sin(angle - Math.PI/6)); @@ -104,16 +139,32 @@ function canvas_arrow_head(context, x, y, angle, headlen) { y - headlen * Math.sin(angle + Math.PI/6)); } +/** Finds the distance between a and b +* @param {numeric} a +* @param {numeric} b +* @returns {numeric} +*/ function pageDistance(a, b) { var dx = a.pageX - b.pageX, dy = a.pageY - b.pageY; return Math.sqrt(dx * dx + dy * dy); } +/** Turns the passed in direction into radians +* @param {numeric} direction +* @returns {numeric} +*/ function toCanvasRadians(direction) { return (((direction % 360) + 270 + 360) % 360) / 180 * Math.PI; } +/** Draws a tick based on the data passed in onto the canvas +* @param {object} ctx +* @param {numeric} innerRadius +* @param {numeric} outerRadius +* @param {numeric} angleRadians +* @returns {undefined} +*/ function drawTick(ctx, innerRadius, outerRadius, angleRadians) { // We treat angleRadians clockwise starting horizontal, pointing right // (i.e. pi/2 is straight down, pi is to the left, 3pi/2 is straight up). @@ -122,6 +173,16 @@ function drawTick(ctx, innerRadius, outerRadius, angleRadians) { ctx.lineTo(c * outerRadius, s * outerRadius); } +/** calls drawTick multiple times along an area based on the data passed in +* @param {object} ctx +* @param {numeric} startDegrees +* @param {numeric} stepDegrees +* @param {numeric} innerRadius +* @param {numeric} outerRadius +* @param {bool} skipMod +* @param {bool} skipOffset +* @returns {undefined} +*/ function drawTicks(ctx, startDegrees, stepDegrees, innerRadius, outerRadius, skipMod, skipOffset) { @@ -144,6 +205,11 @@ function drawTicks(ctx, startDegrees, stepDegrees, ctx.stroke(); } +/** Draws axes at the passed in radius onto the canvas +* @param {object} ctx +* @param {numeric} radius +* @returns {undefined} +*/ function drawAxes(ctx, radius) { ctx.strokeStyle = 'rgba(255, 128, 128, 0.5)'; ctx.lineWidth = 1; @@ -172,6 +238,14 @@ function drawAxes(ctx, radius) { ctx.stroke(); } +/** Draws the label on the outside of the protractor +* @param {object} ctx +* @param {numeric} radius +* @param {string} label +* @param {numeric} angle +* @param {numeric} zeroAngle +* @returns {undefined} +*/ function drawOuterLabel(ctx, radius, label, angle, zeroAngle) { angle = to360(angle); @@ -200,6 +274,12 @@ function drawOuterLabel(ctx, radius, label, angle, zeroAngle) { ctx.restore(); } +/** Draws the protractor on the canvas +* @param {object} ctx +* @param {numeric} radius +* @param {numeric} zeroAngle +* @returns {int} +*/ function drawProtractor(ctx, radius, zeroAngle) { ctx.save(); diff --git a/content/src/filetype.js b/content/src/filetype.js index f585dd90..5d2fdd0f 100644 --- a/content/src/filetype.js +++ b/content/src/filetype.js @@ -1,5 +1,9 @@ (function(module, define) { +/** Infers the script type based on the filename +* @param {string} filename +* @returns {object} +*/ function inferScriptType(filename) { var mime = mimeForFilename(filename); if (/^text\/x-pencilcode/.test(mime)) { @@ -9,13 +13,16 @@ function inferScriptType(filename) { return mime.replace(/;.*$/, ''); } -// Scans for HTML HEAD content at the top, remembering the positions -// after any start-tags seen and before any legal end-tags. -// Returns { -// pos: { map of tagname -> [index, length] } -// hasbody: true if tag starts the content. -// bodypos: index of the tag or first content text. -// +/** Scans for HTML HEAD content at the top, remembering the positions +* after any start-tags seen and before any legal end-tags. +* Returns { +* pos: { map of tagname -> [index, length] } +* hasbody: true if tag starts the content. +* bodypos: index of the tag or first content text. +* +* @param {string} html +* @returns {object} +*/ function scanHtmlTop(html) { var sofar = html, len, match, seen = {}, endpat, scanned = false, result = { pos: {} }; @@ -65,8 +72,15 @@ function scanHtmlTop(html) { } } -// The job of this function is to take: HTML, CSS, and script content, -// and merge them into one HTML file. (Changed signature to allow generalization of languages) +/** The job of this function is to take: HTML, CSS, and script content, +* and merge them into one HTML file. (Changed signature to allow generalization of languages) +* @param {object} doc +* @param {string} domain +* @param {bool} pragmasOnly +* @param {object} setupScript +* @param {object} instrumeter +* @returns {undefined} +*/ function wrapTurtle(doc, domain, pragmasOnly, setupScript, instrumenter) { // Construct the HTML for running a program. var meta = effectiveMeta(doc); @@ -171,9 +185,9 @@ function wrapTurtle(doc, domain, pragmasOnly, setupScript, instrumenter) { var originalLanguage = null; var seeline = '\n\n'; if (meta.type == "text/x-python") { - maintype = "text/x-python" + maintype = 'text/x-python'; seeline = '# Initialization / clearing goes here\n\n'; - originalLanguage = 'python' + originalLanguage = 'python'; } else { maintype = 'text/coffeescript'; @@ -187,15 +201,21 @@ function wrapTurtle(doc, domain, pragmasOnly, setupScript, instrumenter) { seeline = 'eval(this._start_ide_cs_);\n\n'; originalLanguage = 'coffeescript'; } + seeline = "//# sourceURL=mainscript-virtual.js\n" + seeline; } var instrumented = false; if (instrumenter) { // Instruments the code for debugging, always producing javascript. var newText = instrumenter(text, originalLanguage); if (newText !== false) { - text = newText; - maintype = 'text/javascript'; - instrumented = true; + text = newText; + instrumented = true; + if(originalLanguage == 'python') { + maintype = 'text/x-python'; + } + else { + maintype = 'text/javascript'; + } } } var mainscript = seeline; @@ -219,11 +239,25 @@ function wrapTurtle(doc, domain, pragmasOnly, setupScript, instrumenter) { return result; } +/** Escapes out HTML in the passed in string +* @param {string} s +* @returns {undefined} +*/ function escapeHtml(s) { return ('' + s).replace(/"/g, '"').replace(//g, '>').replace(/\&/g, '&'); } +/** Modifies the passed in data to get ready to preview the data +* @param {object} doc +* @param {string} domain +* @param {string} filename +* @param {string} targetUrl +* @param {bool} pragmasOnly +* @param {object} sScript +* @param {object} instrumenter +* @returns {string} +*/ function modifyForPreview(doc, domain, filename, targetUrl, pragmasOnly, sScript, instrumenter) { var mimeType = mimeForFilename(filename), text = doc.data; @@ -299,9 +333,12 @@ function modifyForPreview(doc, domain, return text; } - +/** Sets up a new mime based on the passed in filename +* @param {string} filename +* @returns {object} +*/ function mimeForFilename(filename) { - var result = filename && filename.indexOf('.') > 0 && { + var result = filename && filename.lastIndexOf('.') > 0 && { 'jpg' : 'image/jpeg', 'jpeg' : 'image/jpeg', 'gif' : 'image/gif', @@ -330,6 +367,10 @@ function mimeForFilename(filename) { return result; } +/** Sets up a new meta based on the input +* @param {object} input +* @returns {object} +*/ function effectiveMeta(input) { var doc; var meta; @@ -348,10 +389,10 @@ function effectiveMeta(input) { if (meta && meta.type && meta.lib) { return meta; } meta = (meta && 'object' == typeof meta) ? JSON.parse(JSON.stringify(meta)) : {}; - if (!meta.type) { - // If there's a doc here, we can try to pull a mimetype from it. - if (doc && doc.mime) { - if (doc.mime.lastIndexOf('text/x-python', 0) === 0) { + //if(doc && doc.mimeType){ + if(doc && doc.mimeType && doc.meta){ + //if (doc.mimeType.lastIndexOf('text/x-python', 0) === 0) { + if (doc.meta.type == 'text/x-python') { meta.type = 'text/x-python'; meta.libs = [{name: 'turtle', src: '//{site}/turtlebits.js'}, {name: 'skulpt.min', src: '//{site}/lib/skulpt.min.js'}, @@ -359,13 +400,9 @@ function effectiveMeta(input) { {name: 'python-script', src: '//{site}/lib/python-script.js'} ]; } - else { - meta.type = 'text/coffeescript'; - } - } - else { + } + if (!meta.type) { meta.type = 'text/coffeescript'; - } } if (!meta.libs) { meta.libs = [ @@ -375,6 +412,10 @@ function effectiveMeta(input) { return meta; } +/** Checks to see if the passed in meta is the default meta +* @param {object} meta +* @returns {bool} +*/ function isDefaultMeta(meta) { if (meta == null) return true; if (JSON.stringify(effectiveMeta(meta)) == diff --git a/content/src/gadget.js b/content/src/gadget.js index aeba799a..786e18e8 100644 --- a/content/src/gadget.js +++ b/content/src/gadget.js @@ -5,6 +5,10 @@ var $ = require('jquery'); +/** Creates a stylesheet entry +* @param {string} item +* @returns {undefined} +*/ function makesheet(item) { return $('
').css({ position: 'fixed', top:0, left:0, @@ -15,6 +19,10 @@ function makesheet(item) { $('body').on('click', '.gadget .topbar button, .gadget .topbar .closer', toggle); +/** Toggles whether the gadget window is shown or not +* @param {unused?} e +* @returns {undefined} +*/ function toggle(e) { var $this = $(this).closest('.gadget'), $window = $(window); @@ -124,6 +132,11 @@ $('body').on('expand', '.gadget', function(e) { } }); +/** Adds a gadget to the gadget window +* @param {int} id +* @param {object} opts +* @returns {undefined} +*/ function addGadget(id, opts) { var result = $( '
\n ``", title: 'Do something when the mouse is clicked'*/ + }, { + block: 'forever(lambda:\n fd(100))', + title: 'Move forward forever' }, { - block: 'def newfunction(x):\n fd(x)\n rt(90)', - title: 'Define a new function' + block: 'tick(60, main)', + title: 'Runs the function the number of times per second passed in, call tick(None, main) to stop running it' + }, { + block: 'def buttonFunction():\n write(\'Button clicked\')', + title: 'Define a new function' }, { - block: 'newfunction(200)', - title: 'Use a custom function' + block: 'button(\'Write\', buttonFunction)', + title: 'Do something when a keyboard key is pressed' + }, { + block: 'keydown(lambda:\n write(\'Key pressed\'))', + title: 'Do something when a keyboard key is pressed' + }, { + block: 'def keydownFunction():\n write(\'Key pressed\')', + title: 'Define a new function' + }, { + block: 'keydown(keydownFunction, \'b\')', + title: 'Do something when a keyboard key is pressed' + }, { + block: 'def clickFunction():\n fd(100)', + title: 'Define a new function' + }, { + block: 'click(clickFunction)', + title: 'Do something when a keyboard key is pressed' + }, { + block: 'click(lambda:\n fd(100))', + title: 'Move to a location when document is clicked' } ]) }, { - name: 'Art', + name: 'Art', color: 'purple', blocks: filterblocks([ { block: 'pen(\'purple\', 10)', title: 'Set pen color and size' + }, { + block: 'pen(\'purple\')', + title: 'Set pen color with a default size' + }, { + block: 'arrow(\'blue\', 200)', + title: 'Creates an arrow for a set color and length' }, { block: 'dot(\'green\', 50)', title: 'Make a dot' }, { + block: 'dot(\'green\')', + title: 'Make a dot with a default size' + }, { block: 'box(\'yellow\', 50)', title: 'Make a square' + }, { + block: 'box(\'yellow\')', + title: 'Make a square with a default size' + }, { + block: 'img (\'/img/bird\')', + title: 'Write an image on the screen' }, { block: 'fill(\'blue\')', title: 'Fill traced shape' @@ -896,7 +989,13 @@ module.exports = { }, { block: 'grow(3)', title: 'Grow the size of the turtle' + }/* , { + block: 'shown()', + title: 'Returns true if turtle is visible' }, { + block: 'hidden()', + title: 'Returns true if turtle is not visible' + } */, { block: 'ht()', title: 'Hide the main turtle' }, { @@ -911,7 +1010,10 @@ module.exports = { }, { block: 'pd()', title: 'Put the pen down' - }, { + }/* , { + block: 'inside(t)', + title: 'True if the turtle is encircled by obj' + } */, { block: 'drawon(s)', title: 'Draw on sprite s' }, { @@ -947,16 +1049,21 @@ module.exports = { }, { block: 'say(\'hello\')', title: 'Speak a word' - } + }, { + block: 'audioplay("url")', + expansion: 'audioplay(\'https://upload.wikimedia.org/wikipedia/commons/1/11/06_-_Vivaldi_Summer_mvt_3_Presto_-_John_Harrison_violin.ogg\')' , + title: 'play an audio file' + } ]) }, { name: 'Operators', color: 'lightgreen', blocks: filterblocks([ { - block: 'x = 0;', + block: 'x = 0', title: 'Set a variable', id: 'assign' + }, { block: 'x += 1', title: 'Increase a variable', @@ -1009,7 +1116,34 @@ module.exports = { }, { block: 'min(_,_)', title: 'Get the smaller on two numbers' - } + }, { + block: 'max(_,_)', + title: 'Get the larger on two numbers' + }, { + block: 'abs(_)', + title: 'Get the absolute value of a number' + + }, { + block: 'round(_)', + title: 'round a number to the nearest integer' + }, { + block: '_._', + title: 'Call a function from inside of an object', + id: 'dot' + }, { + block: 'def newfunction(x):\n fd(x)\n rt(90)', + title: 'Define a new function' + }, { + block: 'newfunction(200)', + title: 'Use a custom function' + }, { + block: 'return _', + title: 'Use inside a newfunction return a value' + }, { + block: 're.findall("pattern", x)', + title: 'find all instances of a pattern in x' + } + ]) }, { name: 'Sprites', @@ -1030,15 +1164,21 @@ module.exports = { }, { block: 'q = Pencil()', title: 'Make an invisible and fast drawing sprite' - }//, { - // block: 'if @touches x:\n ``', - // title: 'Do something only if touching the object x' - // }, { - // block: 'if @inside window:\n ``', - // title: 'Do something only if inside the window' - //} + }, { + block: 'r = copy()', + title: 'Copy turtle' + }, { + block: 'f = table(5, 5)', + title: 'Outputs a table with m rows and n columns' + }, { + block: 'if touches("green"):\n pass', + title: 'Do something only if touching the color' + }, { + block: 'if inside(window):\n pass', + title: 'Do something only if inside the window' + }, ]) - }, { + }, { name: 'Text', color: 'pink', blocks: filterblocks([ @@ -1061,12 +1201,18 @@ module.exports = { block: 'label(\'Spot\')', title: 'Prints label at turtle position' },{ + block: 'input()', + title: 'reads an input from console' + } + + +/* { block: 'read(\'TextToBeRead\')', title: 'Type a new line to the document' },{ block: 'readnum(\'2016\')', title: 'Type a new line to the document' - }//,{ + }*/ //,{ // block: 'while True:\n x=input("Enter number")\n if x.isdigit():\n break', // title: 'Read numbers only' //},{ @@ -1106,7 +1252,7 @@ module.exports = { block: 'def keydownFunction():\n write(\'Key pressed\')', title: 'Define a new function' }, { - block: 'keydown(keydownFunction)', + block: 'keydown(keydownFunction, \'b\')', title: 'Do something when a keyboard key is pressed' }, { block: 'keydown(lambda:\n write(\'Key pressed\'))', @@ -1120,7 +1266,10 @@ module.exports = { }, { block: 'click(lambda:\n fd(100))', title: 'Move to a location when document is clicked' - } + }, { + block: 'forever(lambda:\n fd(100))', + title: 'Move forward forever' + } ]) } ], @@ -1494,26 +1643,27 @@ module.exports = { Pencil: {value: true, color: 'teal'} }, PYTHON_FUNCTIONS: { -// '*.fd': {color: 'purple'} +// '*.fd': {color: 'purple'}, // '?.fd': {color: 'purple'} 'fd': {color: 'lightblue', dropdown: [py_types.distances]}, 'bk': {color: 'lightblue', dropdown: [py_types.distances]}, - 'rt': {color: 'lightblue', dropdown: [py_types.angles]}, - 'lt': {color: 'lightblue', dropdown: [py_types.angles]}, + 'rt': {color: 'lightblue', dropdown: [py_types.angles, py_types.distances]}, + 'lt': {color: 'lightblue', dropdown: [py_types.angles, py_types.distances]}, 'ra': {color: 'lightblue', dropdown: [py_types.angles, py_types.distances]}, 'la': {color: 'lightblue', dropdown: [py_types.angles, py_types.distances]}, 'speed': {color: 'lightblue', drown: [py_types.speeds]}, 'home': {color: 'lightblue'}, + 'pagexy': {color: 'lightblue'}, 'turnto': {color: 'lightblue', dropdown: [py_types.angles]}, 'moveto': {color: 'lightblue', dropdown: [py_types.sdistances, py_types.sdistances]}, 'movexy': {color: 'lightblue', dropdown: [py_types.sdistances, py_types.sdistances]}, 'jumpto': {color: 'lightblue', dropdown: [py_types.sdistances, py_types.sdistances]}, 'jumpxy': {color: 'lightblue', dropdown: [py_types.sdistances, py_types.sdistances]}, - + 'pause': {color: 'lightblue'}, 'pen': {color: 'purple', dropdown: [py_types.colors, py_types.sizes]}, 'dot': {color: 'purple', dropdown: [py_types.colors, py_types.sizes]}, - 'box': {color: 'purple'}, - 'fill': {color: 'purple'}, + 'box': {color: 'purple', dropdown: [py_types.colors, py_types.sizes]}, + 'fill': {color: 'purple', dropdown: [py_types.colors]}, 'wear': {color: 'purple'}, 'grow': {color: 'purple'}, 'st': {color: 'purple'}, @@ -1521,27 +1671,45 @@ module.exports = { 'cs': {color: 'purple'}, 'pu': {color: 'purple'}, 'pd': {color: 'purple'}, + 'img': {color: 'purple'}, 'drawon': {color: 'purple'}, + 'arrow': {color: 'purple', dropdown: [py_types.colors]}, + 'shown': {color: 'purple'}, + 'hidden': {color: 'purple'}, + 'inside': {color: 'lightgreen'}, + 'touches': {color: 'lightgreen'}, + //Sound 'play': {color: 'indigo'}, 'tone': {color: 'indigo'}, 'silence': {color: 'indigo'}, 'say': {color: 'indigo'}, + 'audioplay': {color: 'indigo'}, //Control 'ht': {color: 'purple'}, 'cs': {color: 'purple'}, 'pu': {color: 'purple'}, 'pd': {color: 'purple'}, - 'random': {color:'lightgreen'}, + 'random': {color:'lightgreen', dropdown: [py_types.randarg]}, //opertor 'button': {color: 'orange'}, 'keydown': {color: 'orange'}, 'click': { color: 'orange' }, + 'forever': { color: 'orange'}, + 'tick': {color: 'orange', dropdown: [py_types.tickCount]}, + 'newfunction': {color: 'lightgreen'}, + 'min': {color: 'lightgreen'}, + 'max': {color: 'lightgreen'}, + 'abs': {color: 'lightgreen'}, + 'round': {color: 'lightgreen'}, + 're.findall': {color: 'lightgreen'}, //Sprites 'Sprite' : {color:'teal'}, 'Piano' : {color:'teal'}, 'Pencil' : {color:'teal'}, 'Turtle' : {color:'teal'}, + 'copy' : {color:'teal'}, + 'table': {color: 'teal'}, //Text 'write' : {color: 'pink'}, 'typeline' : {color: 'pink'}, @@ -1551,7 +1719,8 @@ module.exports = { 'label' : {color: 'pink'}, 'read': { color: 'pink' }, 'readnum': { color: 'pink' }, - + 'len': {color:'lightgreen'}, + 'input': {color:'pink'}, /* '?.slide': {color: 'lightblue', dropdown: [sdistances]}, '?.fill': {color: 'purple', dropdown: [colors]}, @@ -1690,17 +1859,18 @@ module.exports = { conditionals: {color: 'orange'}, value: {color: 'lightgreen'}, command: {color: 'lightgreen'}, - errors: {color: '#f00'} + errors: {color: '#f00'}, }, // PYTHON_CATEGORIES: { + // functions: {color: 'lightgreen'}, // returns: {color: 'yellow'}, // comments: {color: 'gray'}, // arithmetic: {color: 'lightgreen'}, // logic: {color: 'lightgreen'}, // containers: {color: 'teal'}, -// assignments: {color: 'lightgreen'}, + assignments: {color: 'lightgreen'}, // loops: {color: 'orange'}, // conditionals: {color: 'orange'}, // value: {color: 'lightgreen'}, @@ -1717,3 +1887,4 @@ module.exports = { center: {category: 'grouping'} } }; + diff --git a/content/src/showturtle.js b/content/src/showturtle.js index dc60abdd..429f8829 100644 --- a/content/src/showturtle.js +++ b/content/src/showturtle.js @@ -1,3 +1,5 @@ +/** Sets up the jquery turtle object for the program window +*/ (function(ww) { var script = document.currentScript || (function() { var scripts = document.getElementsByTagName("script"); diff --git a/content/src/storage.js b/content/src/storage.js index 6b5cf979..59aa02f1 100644 --- a/content/src/storage.js +++ b/content/src/storage.js @@ -8,6 +8,7 @@ var $ = require('jquery'), eval(see.scope('storage')); +//Checks to see if there is a local backup of a file function hasBackup(filename) { try { if (!window.localStorage) return false; @@ -16,14 +17,14 @@ function hasBackup(filename) { return false; } } - +//Checks to see if we are currently able to connect to the save server function isOnline() { // PhantomJS (headless testing) gets this wrong. // https://github.com/ariya/phantomjs/issues/10647 return window.navigator.onLine || null != window.navigator.userAgent.match(/PhantomJS/); } - +//Loads a local backup of a file function loadBackup(filename, annotation) { try { var result = JSON.parse(window.localStorage['backup:' + filename]); @@ -35,7 +36,7 @@ function loadBackup(filename, annotation) { return { error: 'Backup load failed.', down: true }; } } - +//Saves a local backup of a file function saveBackup(filename, msg) { try { if (!window.localStorage) return; @@ -43,14 +44,14 @@ function saveBackup(filename, msg) { window.localStorage['backup:' + filename] = JSON.stringify(msg); } catch(e) { } } - +//Deletes a backup of a file function deleteBackup(filename) { try { if (!window.localStorage) return; delete window.localStorage['backup:' + filename]; } catch(e) { } } - +//Deletes all backups that start with a specified prefix function deleteBackupPrefix(filename) { try { if (!window.localStorage) return; @@ -70,7 +71,7 @@ function deleteBackupPrefix(filename) { } catch(e) { } } - +//Checks if we want to do backups before saving function isBackupPreferred(filename, m, preferUnsaved) { try { if (!window.localStorage) return false; @@ -113,6 +114,7 @@ function networkErrorMessage(domain) { } window.pencilcode.storage = { + //Updates the set of users updateUserSet: function(prefix, set, cb) { prefix = prefix || ''; if (set.hasOwnProperty(prefix)) { @@ -235,7 +237,7 @@ window.pencilcode.storage = { // 4. If the network save succeeded, the file is saved as a backup (without // unsaved set) and success:true, mtime:mtime is the status. saveFile: function(ownername, - filename, data, force, key, backupOnly, callback) { + filename, data, force, key, backupOnly, callback, overwriteProtection = true) { // Always stick the data in the backup immediately, marked as unsaved. var msg = $.extend({}, data), now = +(new Date); if (!/pencil/.test(filetype.mimeForFilename(filename)) || @@ -268,7 +270,90 @@ window.pencilcode.storage = { backupOnly ? {backup:true} : {offline:true}); }, 0); return; } - // Attempt the network save: pack up any metadata, and set up + if(overwriteProtection){ + //If we are protecting the user from overwriting, ask the server for the directory, then check to see + //if the file that we are currently working in's name will overwrite a pre-existing file + this.loadFile(ownername, "", false, function(m){ + if(m.list === null){ + callback(m); + return; + } + for(var i = 0; i < m.list.length; i++){ + if(m.list[i].name === filename){ + callback({ + overwrite: true + }); + return; + } + } + console.log("New File"); + // Attempt the network save: pack up any metadata, and set up + // the conditional argument and the weak authentication key. + var payload = { data: msg.data, meta: JSON.stringify(msg.meta) }; + if (msg.thumbnail) { // Send thumbnail data to server if it exists. + payload.thumbnail = msg.thumbnail; + } + if (msg.mtime && !force) { + payload.conditional = msg.mtime; + } + if (key) { + payload.key = key; + } + var payloadsize = payload.data.length + payload.meta.length; + + // Do the network save. + var domain = (ownername ? ownername + '.' : '') + window.pencilcode.domain; + var crossdomain = (window.location.hostname != domain); + $.ajax({ + url: (ownername ? '//' + domain : '') + + '/save/' + filename, + data: payload, + dataType: 'json', + // Use a GET if crossdomain and the payload is short. Note that + // a longer payload will fail on IE, and anecdotally a crossdomain + // POST (even with CORS) will fail on mobile browsers. + type: (crossdomain && payloadsize < 1024) ? 'GET' : 'POST', + success: function(m) { + var check; + if (m.error) { + // Pass errors on to calback. Backup already done. + console.log('got error ' + m.error); + } else if (m.deleted) { + // On a successful delete, also delete the backup + // (if nothing new has since been entered in backup). + check = loadBackup(filename); + if (check && check.data === msg.data) { + deleteBackup(filename); + } + } else { + // On a successful save, re-commit the backup with the new + // mtime and without the unsaved bit (if nothing new has since + // been entered in the backup). + check = loadBackup(filename); + if (check && check.data === msg.data) { + // Commit backup without 'unsaved' if it is still unchanged. + delete msg.unsaved; + delete msg.needsave; + delete msg.btime; + delete msg.offline; + if (m.mtime) { msg.mtime = m.mtime; } + saveBackup(filename, msg); + } + } + callback && callback(m); + } + }).error(function() { + console.log('got error ' + domain); + callback && callback({ + error: networkErrorMessage(domain), + offline:true + }); + }); + return; + }); + return; + } + // Attempt the network save: pack up any metadata, and set up // the conditional argument and the weak authentication key. var payload = { data: msg.data, meta: JSON.stringify(msg.meta) }; if (msg.thumbnail) { // Send thumbnail data to server if it exists. @@ -331,6 +416,7 @@ window.pencilcode.storage = { }); }); }, + //Moves a file between two locations (Generall to a new filename) moveFile: function(ownername, sourcefile, filename, key, copy, callback) { var payload = { source: ownername + '/' + sourcefile @@ -355,6 +441,7 @@ window.pencilcode.storage = { }); }); }, + //Sets a password for a user setPassKey: function(ownername, key, oldkey, callback) { $.post('//' + ownername + '.' + window.pencilcode.domain + '/save/', $.extend({ mode: 'setkey', data: key}, oldkey ? { key: oldkey } : {}), @@ -368,11 +455,13 @@ window.pencilcode.storage = { }); }); }, + //Deletes all backups from the browser deleteBackup: deleteBackup, deleteAllBackup: function() { deleteBackupPrefix(''); }, // TODO: cache-refreshing crawl. + //Currently does nothing recrawlCache: function() { // Load dirtree URL // Then backup all directories diff --git a/content/src/thumbnail.js b/content/src/thumbnail.js index 1656e08f..98646963 100644 --- a/content/src/thumbnail.js +++ b/content/src/thumbnail.js @@ -6,6 +6,11 @@ var NUM_ATTEMPTS = 3; // Public functions var thumbnail = { + /** Generates a thumbnail based off the passed in iframe and runs the passed in callback + * @param {object} iframe + * @param {function} callback + * @returns {undefined} + */ generateThumbnailDataUrl: function(iframe, callback) { // Get the canvas inside the iframe. var innerDoc = iframe.contentDocument || iframe.contentWindow.document; @@ -83,6 +88,12 @@ var thumbnail = { } // Private functions +/** Gets a URL for the image data +* @param {object} canvas +* @param {int} offsetX +* @param {int} offsetY +* @returns {undefined} +*/ function getImageDataUrl(canvas, offsetX, offsetY) { var w = canvas.width; var h = canvas.height; diff --git a/content/src/view.js b/content/src/view.js index 03c2fc10..a61a7631 100644 --- a/content/src/view.js +++ b/content/src/view.js @@ -13,6 +13,12 @@ var $ = require('jquery'), ZeroClipboard = require('ZeroClipboard'), FontLoader = require('FontLoader'); +/** +* Takes in a string and makes it URL friendly +* +* @param {string} s +* @returns {string} +*/ function htmlEscape(s) { return s.replace(/[<>&"]/g, function(c) { @@ -206,24 +212,44 @@ $(window).on('resize.editor', function() { $('.hpanel').trigger('panelsize'); }); +/** Checks to see if subscribers exist +* @returns {bool} whether there are or are not subscribers +*/ function hasSubscribers() { return state.subscribers.length > 0; } +/** Pushes an event to all subscribers +* @param {function} method +* @param {array[arguments for method]} args +* @param {string} requestid +* @returns {undefined} +*/ function publish(method, args, requestid){ for (var j = 0; j < state.subscribers.length; ++j) { state.subscribers[j](method, args, requestid); } } +/** Finds the paneId at Position +* @param {string} position +* @returns {string} The paneId at position +*/ function paneid(position) { return $('.' + position).find('.pane').attr('id'); } +/** Finds the position of paneId +* @param {string} id +* @returns {string} The position of paneId +*/ function panepos(id) { return $('#' + id).closest('.panebox').attr('class').replace(/\s|panebox/g, ''); } +/** Constructor for panes +* @returns {object} a freshly initialized pane +*/ function initialPaneState() { return { editor: null, // The ace editor instance. @@ -238,6 +264,11 @@ function initialPaneState() { }; } +/** Adds a callback to a tag +* @param {string} tag +* @param {function} callback +* @returns {undefined} +*/ function setOnCallback(tag, cb) { if (state.callbacks[tag] == null) { state.callbacks[tag] = []; @@ -245,6 +276,11 @@ function setOnCallback(tag, cb) { state.callbacks[tag].push(cb); } +/** Fires an event to all callbacks on a tag +* @param {string} tag +* @param {array[arguments to pass to all callbacks]} args +* @returns {undefined} +*/ function fireEvent(tag, args) { if (tag in state.callbacks) { var cbs = state.callbacks[tag].slice(); @@ -259,6 +295,11 @@ function fireEvent(tag, args) { } } +/** Changes the URL in the browser and optionally adds the change to browser history +* @param {string} targetUrl +* @param {bool} addToHistory +* @returns {undefined} +*/ function setVisibleUrl(targetUrl, addToHistory) { var currentDepth = history.state && history.state.depth || 0; var currentUrl = history.state && history.state.current || null; @@ -336,6 +377,10 @@ $('body').on('keydown', function(e) { } }); +/** Pushes keydown events to Editor when it doesn't have focus +* @param {object} keydown_event +* @returns {undefined} +*/ function forwardCommandToEditor(keydown_event) { // Only forward the command if an editor is present and it // does not already have focus. @@ -365,6 +410,11 @@ $('#notification').on('click', 'a', function(e) { } }); +/** flashes a notification to the top of the screen and optionally starts a loading indicator +* @param {string} text +* @param {bool} loading +* @returns {undefined} +*/ function flashNotification(text, loading) { var marker = Math.random(); var hidefunc = function(e) { @@ -393,6 +443,9 @@ function flashNotification(text, loading) { }); } +/** dismisses the visible notification at the top of the screen +* @returns {undefined} +*/ function dismissNotification() { if ($('#notification').is(':visible')) { $('#notification').removeData('marker'); @@ -401,6 +454,10 @@ function dismissNotification() { } } +/** Flashes the specified button +* @param {string} id +* @returns {undefined} +*/ function flashButton(id) { var button = $('#' + id).closest('button'), bg = button.css('backgroundColor'), @@ -416,9 +473,11 @@ function flashButton(id) { /////////////////////////////////////////////////////////////////////////// // FILENAME AND RENAMING /////////////////////////////////////////////////////////////////////////// - -function selectEndOf(contentEditableElement) -{ +/** Selects the end of an element +* @param {object} contentEditableElement +* @returns {undefined} +*/ +function selectEndOf(contentEditableElement) { var range,selection; if (document.createRange) { range = document.createRange(); @@ -435,6 +494,12 @@ function selectEndOf(contentEditableElement) } } +/** Selects characters in node that is range size starting charsFromBegin into the node +* @param {int} range +* @param {object} node +* @param {int} charsFromBegin +* @returns {undefined} +*/ function setRangeStart(range, node, charsFromBegin) { if (node.firstChild) { var count = 0; @@ -454,6 +519,12 @@ function setRangeStart(range, node, charsFromBegin) { } } +/** Selects characters in node that is range size starting charsFromEnd from the end of the node +* @param {int} range +* @param {object} node +* @param {int} charsFromEnd +* @returns {undefined} +*/ function setRangeEnd(range, node, charsFromEnd) { if (node.lastChild) { var count = 0; @@ -473,6 +544,12 @@ function setRangeEnd(range, node, charsFromEnd) { } } +/** Selects a characters in contentEditableElement between beginOffset and endOffset +* @param {object} contentEditableElement +* @param {int} beginOffset +* @param {int} endOffset +* @returns {undefined} +*/ function selectContentsOf(contentEditableElement, beginOffset, endOffset) { var range,selection; if (document.createRange) { @@ -514,9 +591,13 @@ $('#filename').on('keypress keydown keyup input', function(e) { if (text == '') { sel.html(' '); } }); -function fixTypedFilename(enteredtext) { - if (!enteredtext) { return enteredtext; } - return enteredtext.replace(/\s|\xa0|[^\w\/.-]/g, '') +/** Removes characters from the string entered that are not allowed +* @param {string} enteredText +* @returns {string} +*/ +function fixTypedFilename(enteredText) { + if (!enteredText) { return enteredText; } + return enteredText.replace(/\s|\xa0|[^\w\/.-]/g, '') .replace(/^\/*/, '').replace(/\/\/+/g, '/'); } @@ -548,6 +629,10 @@ $('#owner').on('click', function(e) { } }); +/** Updates the parent link of elt based on the filename +* @param {object} elt +* @returns {undefined} +*/ function fixParentLink(elt) { var filename = window.location.pathname; if (filename.indexOf('/') >= 0) { @@ -613,6 +698,10 @@ $('#buttonbar').on('change', 'input[type=checkbox]', function(e) { // buttonlist should be // [{label:, id:, callback:, checkbox:, checked:, disabled:, title:}] +/** Shows the specified buttons +* @param {array} buttonList +* @returns {undefined} +*/ function showButtons(buttonlist) { var bar = $('#buttonbar'); var html = ''; @@ -666,6 +755,11 @@ function showButtons(buttonlist) { $('#buttonbar button.splitmenu li').tooltipster({position: 'left'}); } +/** Globally enables/disables the specified button whether it is shown or not +* @param {string} id +* @param {bool} enable +* @returns {undefined} +*/ function enableButton(id, enable) { if (enable) { var inp = $('#' + id).removeAttr('disabled'); @@ -675,6 +769,9 @@ function enableButton(id, enable) { } // Centers the middle button div using javascript. +/** Centers the middle button +* @returns {undefined} +*/ function centerMiddle() { var m = $('#middle'); // Horizontal center taking into account the button width. @@ -688,6 +785,10 @@ function centerMiddle() { $(window).on('resize.middlebutton', centerMiddle); +/** Determines which middle button to show (including none) +* @param {string} which +* @returns {undefined} +*/ function showMiddleButton(which) { if (which == 'run') { var html, @@ -733,7 +834,10 @@ function showMiddleButton(which) { $('#middle button').tooltipster(); } -// Show thumbnail under the save button. +/** Shows thumbnail from imageDataUrl under the save button +* @param {string} imageDataUrl +* @returns {undefined} +*/ function flashThumbnail(imageDataUrl) { if (!imageDataUrl) { return; } // Destroy the original title tooltip once there is a thumbnail. @@ -754,7 +858,10 @@ function flashThumbnail(imageDataUrl) { /////////////////////////////////////////////////////////////////////////// // SHARE DIALOG /////////////////////////////////////////////////////////////////////////// - +/** Shows the share dialog +* @param {array} opts - an optional list of options that will be passed into the share dialog +* @returns {undefined} +*/ function showShareDialog(opts) { if (!opts) { opts = { }; @@ -883,6 +990,10 @@ function showShareDialog(opts) { showDialog(opts); } +/** Shows a generic dialog defined by opts +* @param {array} opts - a NON-optional array of options that describes the dialog to be shown +* @returns {undefined} +*/ function showDialog(opts) { var overlay = $('#overlay'); if (!opts) { opts = {}; } @@ -995,15 +1106,17 @@ function showDialog(opts) { validate(); } - //////////////////////////////////////////////////////////////////////////// // LOGIN DIALOG /////////////////////////////////////////////////////////////////////////// - +/** Shows the login dialog +* @param {array} opts +* @returns {undefined} +*/ function showLoginDialog(opts) { if (!opts) opts = { }; - + opts.content = '
' + '
Name:
'+ @@ -1087,10 +1200,14 @@ function showLoginDialog(opts) { /////////////////////////////////////////////////////////////////////////// // PANE MANAGEMENT /////////////////////////////////////////////////////////////////////////// - -function setPreviewMode(shown, noanimation) { +/** Sets up a preview in a pane +* @param {bool} shown +* @param {bool} noAnimation +* @returns {undefined} +*/ +function setPreviewMode(shown, noAnimation) { var change = (shown != state.previewMode); - var delay = (noanimation || !change) ? 0 : 400; + var delay = (noAnimation || !change) ? 0 : 400; if (shown) { $('#middle').removeClass('rightedge'); $('.right').animate({left: '50%', width: '50%'}, delay); @@ -1113,10 +1230,17 @@ function setPreviewMode(shown, noanimation) { state.previewMode = shown; } +/** returns a string that looks like this # + name + panebox +* @param {string} name +* @returns {string} +*/ function panelParts(name) { return '#' + name + 'panebox'; } +/** switches the three panes around in a leftwards manner +* @returns {undefined} +*/ function rotateLeft() { var idb = paneid('back'); var idl = paneid('left'); @@ -1134,6 +1258,9 @@ function rotateLeft() { setPrimaryFocus(); } +/** switches the three panes around in a rightwards manner +* @returns {undefined} +*/ function rotateRight() { var idb = paneid('back'); var idl = paneid('left'); @@ -1154,7 +1281,15 @@ function rotateRight() { /////////////////////////////////////////////////////////////////////////// // RUN PREVIEW PANE /////////////////////////////////////////////////////////////////////////// - +/** Sets the passed in pane to run passed in HTML code +* @param {string} pane +* @param {string} html +* @param {string} filename +* @param {string} targetUrl +* @param {string} fullScreenLink +* @param {bool} nocode +* @returns {undefined} +*/ function setPaneRunHtml( pane, html, filename, targetUrl, fullScreenLink, nocode) { clearPane(pane); @@ -1209,6 +1344,12 @@ function setPaneRunHtml( }); } +/** Evals the passed in code in the passed in pane +* @param {string} pane +* @param {string} code +* @param {string} raw +* @returns {undefined} +*/ function evalInRunningPane(pane, code, raw) { var paneState = state.pane[pane]; if (!paneState.running) { return [null, 'error: not running (wrong state)']; } @@ -1232,6 +1373,10 @@ function evalInRunningPane(pane, code, raw) { } } +/** Hides the protractor on the passed in pane +* @param {string} pane +* @returns {undefined} +*/ function hideProtractor(pane) { var paneState = state.pane[pane]; var preview = $('#' + pane + ' .preview'); @@ -1242,13 +1387,18 @@ function hideProtractor(pane) { } } -// step is a record of the following form: -// startCoords: -// pageX:, pageY:, direction:, scale: -// endCoords: -// pageX:, pageY:, direction:, scale: -// command: "fd" -// args: [50] +/** Shows the protractor on the passed in pane +* @param {string} pane +* @param {object} step - defined as followed +* step is a record of the following form: +* startCoords: +* pageX:, pageY:, direction:, scale: +* endCoords: +* pageX:, pageY:, direction:, scale: +* command: "fd" +* args: [50] +* @returns {undefined} +*/ function showProtractor(pane, step) { var paneState = state.pane[pane]; if (!paneState.running) { @@ -1270,6 +1420,11 @@ function showProtractor(pane, step) { labelStep(preview, step); } +/** Labels the step distance on the protractor +* @param {object} preview +* @param {object} step +* @returns {undefined} +*/ function labelStep(preview, step) { if (!step.startCoords) { return; @@ -1348,6 +1503,13 @@ var getScrollbarWidth = function() { return width; }; +/** Sets up the link text in the directory +* @param {string} pane +* @param {string[]} links +* @param {string} filename +* @param {string} ownername +* @returns {undefined} +*/ function setPaneLinkText(pane, links, filename, ownername) { clearPane(pane); var paneState = state.pane[pane]; @@ -1358,6 +1520,11 @@ function setPaneLinkText(pane, links, filename, ownername) { setVisibilityOfSearchTextField(pane); } +/** Sets up the links in the directory +* @param {string} pane +* @param {string[]} links +* @returns {undefined} +*/ function setPaneLinks(pane, links) { var paneState = state.pane[pane]; paneState.links = links; @@ -1376,6 +1543,10 @@ $(window).on('resize.listing', function() { } }); +/** Updates the links on the pane +* @param {string} pane +* @returns {undefined} +*/ function updatePaneLinks(pane) { var j, col, items, width, maxwidth, colcount, colsize, colnum, tightwidth, item, figure, thumbnail, directory, colsdone, list; @@ -1493,6 +1664,10 @@ function updatePaneLinks(pane) { } })(jQuery); +/** Sets whether the search field is visible or not +* @param {string} pane +* @returns {undefined} +*/ function setVisibilityOfSearchTextField(pane) { var directory = $('#'+pane).find('.directory'); var panetitle = directory.parent().parent().find('.panetitle'); @@ -1516,6 +1691,10 @@ function setVisibilityOfSearchTextField(pane) { } } +/** Gets the default thumbnail of the passed in type +* @param {string} type +* @returns {undefined} +*/ function getDefaultThumbnail(type) { var baseUrl = '//' + pencilcode.domain + '/image/'; var mimeToFilename = { @@ -1544,7 +1723,11 @@ function getDefaultThumbnail(type) { /////////////////////////////////////////////////////////////////////////// // ACE EDITOR SUPPORT /////////////////////////////////////////////////////////////////////////// - +/** Clears the passed in pane +* @param {string} pane +* @parame {bool} loading +* @returns {undefined} +*/ function clearPane(pane, loading) { var paneState = state.pane[pane]; if (paneState.dropletEditor && paneState.dropletEditor.destroy) { @@ -1583,6 +1766,10 @@ function clearPane(pane, loading) { $('#' + pane + 'title_text').html(''); } +/** Determines the mode based on the passed in mime type +* @param {string} mimeType +* @returns {undefined} +*/ function modeForMimeType(mimeType) { if (!mimeType) { return 'ace/mode/text'; @@ -1606,6 +1793,10 @@ function modeForMimeType(mimeType) { return 'ace/mode/' + result; } +/** Determines the droplet mode for passed in mime type +* @param {string} mimeType +* @returns {undefined} +*/ function dropletModeForMimeType(mimeType) { if (!mimeType) { return 'ace/mode/text'; @@ -1624,6 +1815,11 @@ function dropletModeForMimeType(mimeType) { return result; } +/** Determines the palette for the passed in pane +* @param {object} paneState +* @param {string} selfname +* @returns {undefined} +*/ function paletteForPane(paneState, selfname) { var mimeType = editorMimeType(paneState).replace(/;.*$/, ''), basePalette = paneState.palette; @@ -1649,6 +1845,10 @@ function paletteForPane(paneState, selfname) { return []; } +/** Determines the droplet options for passed in mime type +* @param {string} mimeType +* @returns {undefined} +*/ function dropletOptionsForMimeType(mimeType) { if (/x-python/.test(mimeType)) { return { @@ -1668,10 +1868,18 @@ function dropletOptionsForMimeType(mimeType) { }; } +/** Determines a uniqueID for the passed in name +* @param {string} name +* @returns {undefined} +*/ function uniqueId(name) { return name + '_' + ('' + Math.random()).substr(2); } +/** Updates the title of the passed in pane +* @param {string} pane +* @returns {string} +*/ function updatePaneTitle(pane) { var paneState = state.pane[pane]; var label = ''; @@ -1747,6 +1955,9 @@ function updatePaneTitle(pane) { setVisibilityOfSearchTextField(pane); } +/** gets the thumbnail to be shown +* @returns {object} the thumbnail to be shown +*/ function getShowThumb() { var showThumb; try { @@ -1763,6 +1974,10 @@ function getShowThumb() { return showThumb; } +/** sets the thumbnail to be shown +* @param {object} showThumb +* @returns {undefined} +*/ function setShowThumb(showThumb) { try { window.localStorage.setItem('showThumb', JSON.stringify(showThumb)); @@ -1771,6 +1986,10 @@ function setShowThumb(showThumb) { } } +/** determines if the thumbnail should be shown or not +* @param {string} path +* @returns {undefined} +*/ function shouldShowThumb(path) { var layers = path.match(/[^\/]+/g); if (!layers) { // Disable on user listing. @@ -1796,6 +2015,11 @@ function shouldShowThumb(path) { } } +/** Sets whether a path should show the thumbnail +* @param {string} path +* @param {bool} shouldShow +* @returns {undefined} +*/ function setShouldShowThumb(path, shouldShow) { var layers = path.match(/[^\/]+/g); if (!layers) { // Do not allow setting on user listing. @@ -1929,6 +2153,10 @@ $('.pane').on('click', '.blocktoggle', function(e) { setPaneEditorBlockMode(pane, true,$(this).attr('droplet-editor')); }); +/** Shows the editor language dialog for the passed in pane +* @param {string} pane +* @returns {undefined} +*/ function showPaneEditorLanguagesDialog(pane) { if (panepos(pane) != 'left') { return; } var paneState = state.pane[pane]; @@ -1957,7 +2185,10 @@ function showPaneEditorLanguagesDialog(pane) { 'Coffeescript
' + '' + + 'Javascript
' + + '' + '
' + '
' + '