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 @@
+5Vxbk5s2FP41nkkessNNgB/33nTSZKdO2uZRBtkmi5EjxHrdX18JEAYkO941IK+bmczig8Di6DvfuQmP7Ovl8z2Bq8UfOETxyDLC55F9M7IsE4wN9odLNoXEs+1CMCdRWA7aCibRv6gUltfNsyhEaWMgxTim0aopDHCSoIA2ZJAQvG4Om+G4+a0rOEeSYBLAWJb+HYV0UUh9YGzlv6FovhDfbBrlmSkMHucEZ0n5fSPLnuX/itNLKO5Vjk8XMMTrmsi+HdnXBGNaHC2fr1HMdSvUVlx3t+NsNW+CEnrIBVZxwROMs/LRr3GMCRPdoFmURDTCCdPl5chyY3bDqyk75c75kSz5E4Xsug/s/4Rm/IkNuoCU/VnA1Qqx21gGzmjKVpYfFef5IZtdSgI+x2KlEJHv/ZCRVYzK2/8VofXFj1Qe9YXAZC5GXeOEEhzHiCjH3hOEkmq+mLAlVY674pophn1dZMtpAqM4H5gvH90IyFD0zIcv6DJmApMdpuzrH1GhT/smwQkbeTWL4rglSlcwiJI5E4Dtp694xQQfmHbsq/UiomjC5Pyr1szimAw/ITKLc+gsojBkj8KXpL34JR7YWDa9mqgEwz3CS0TJhg15bhpgabcCpuutEZjCthd1AxADYWl48+rOW/CxgxJ/aiy6kk5RyKyy/IgJXeA5TmB8u5Ve5aaG+A2MpvLRc0T/4eILUH76Ls4wWGxqp/jH7+UNfiBKNyUdwYxirujqez/hfE3MfZpOcUaCcvYl5VFI5qgc5RQi/lx7V4OgGNLoqclGx+jWluz8gaCUm2QKnzi+pxmlOJFWYKtfcwcQazqvY5sTnx+gIJAMgZ2Z+sABRjeIrTC6aSK4DllLAVm3A8T6Z4ZYR0aspwuxjuyZ4ih4ZKLbJ/4MlvFuRvCS/f39Z4bI5j2fRJZ7GZjfiHnzmLvjNJ/dPEopIrmHYkrigHB/ZtzLXhUGID5JK8o89IofrggOuMW81AqQGQLkqaxg7Ho2dPuxgvGBVjDuwAqExdWW6jM+M8vwZMsYa+Ny2TS+Izkg6ULhNXXXNFydqtR94YEBNG6bulTuSRqfFG7zMuAR8rv3ctz4kQe4CebjvqwTHtO2XesCL6fZywllNkOu2q2G3nhqdOZWQSsSVDCK2RejyAg/O0YZy/g2bW2U0kvsbe6hj+G1awNd2h1LaM7JYbJCQQTjnB04g7xpfrDtA/nB74IfBkoVO4WkqcgHTW2YNOWM8B4xIEKKqjKHApTHRsMhRP5MCU438NF01hE4jSY4HVcBTqAAp9MFOP3zd14Ctw0sa0sVxUqejfdSqdfpPN/IL70kBG5qA1Y4Smhau/MDF2xNC3jNRFN8vjt0/P7hrr9vODso5ruFRfXgh9kmkGzzhlcD1ihX3M8sIvyILSBjibs37pLBkC65mm7/SalG2lMkpaKqosGFy1npR17soguUozjlt1zwzkJZ88orXW8O1lXDoYS1q4K13xes5TCpJ2e+29uYDUxfdF5pEQhuoNrShWpLpujBiKR/tWprAFlykfYaJsL1iT56SRRFX7bsDKWIMGPtkjdCgPzQUfGGb01tt6OSuDluukMPKHjDU/BG1cw/qqJiD8wSrYKs1T1NWDKeu6/Iviwo3Qim3xFVVj1BAFqLd1wcKXRRs6ZPOIB8yBUMHrMVO8BJvJEwcIqG4jT9q+cp7KSvDqqisn52ybKqrSo2O2hwBHI1cvjuUe8hjUrlvjaVy+2MyzxCz30v38mzJhHNA3UXLjklJNN0lT+rwRih05h9GEqpEm1B0Kp+tN1XzO5K6i67dZMs4FXLWRZL+hSFzWIXV4izaX7mgF0vXahL2sPiDMjBthwa6kjdrZ5Z2D8lFrblAGLwvNK4cEyvrvMPxoWxlTwgErHn4qH/MKuhr71vySX7G4zSKhPKUt6/N9YwqVIkwdpvsKAiUpO93Az64WaxxjI330Hm6cLT4mVHKy2f2+5CW9HVsLU1jWy5zjSJ5sm31WUSckgqmp+naMxOa8f2kMmbLacSQzmxboE5yF6zV7XbHKO5vKKzvat/1h4/Hu/vt5nW3vFHF0rAcHsVNRKbAj/CU2ggtle1hd5WCOO0NniYLlDEMD3ll04/pV2N2/4VnQrH0oVfR05Hv61CSNE3FoVPEO1jV9IwddaWq7ZVW2r78tXOub1dJQDaAK227XRiNvVocoHXn/A8Sm4iGON5D7Ad5tUSCbaH1vK6gK17PGwDvIyCl0C4W5gqkh5XH0zlpCffipzjlP0toDraWyl9bWSgB62OfyBaO4kM5NBLhm8SXvI31EfV+791UO7o5oJ9ynkVA9bUodoIK2Qvy25+mY6A9nvChamUV+3La1rLCszWjQolSDd6TcqiWLO37SkVL0MAbbVWRy4a5OzztsourqWv7ALkavX55dRAQW5AW7tGzOacc2rX1JhTA3mf5cs8ZxPLO9Vxon5R4oVD/WJ70SzDufAM4PuWDayxYwo1/sJNvrQs6e6Y/85p7i9jHl9mBOdGf66C/vS9YKPYUXEm2a1kQIfmC51kt/1Ux1+c8Q62Lwuc1AsUQFEpP69suI1uWxWn9uXTXbkm9n/y6Y6In4716Y4xbvr0QVy6mP6hLl1k6F25dE9GzwDcqPGlT1dRKfS0caMnu/wOevuv90xatK+tTuvKddrPaD3ivzcTMIXSLjedDVSXFRsqRAFP0UXw/X4ckXf8TqrTDakERZxGSCVmUwPun+VvrzFpkmMYlhjuOmEYposL2q/LDNnF9bxzRrKCgjUiWabgMi94k7B1Wj8OoISt0Q1s2cftLxgXod/2Z6Lt2/8A
\ 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
================================
[](https://travis-ci.org/PencilCode/pencilcode)
-First install the prerequisites: git, nodejs, and grunt. Next, be sure you're in your home directory. Then:
-
-
-
-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):
+
-
-(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:
+
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 ' +
+ '' +
'