├── LICENSE ├── README.md ├── favicon.ico ├── index.html ├── lib ├── cm │ ├── active-line.js │ ├── blackboard.css │ ├── cobalt.css │ ├── codemirror.css │ ├── codemirror.js │ └── python.js ├── jq │ └── jquery.ui.touch-punch.min.js ├── lib.js └── skulpt │ ├── microbit │ ├── __init__.js │ ├── compassarrow.png │ ├── compassbk.jpg │ ├── firmware.hex │ ├── mb.css │ └── mb.png │ ├── music │ └── __init__.js │ ├── os │ └── __init__.js │ ├── py3d │ ├── __init__.js │ └── three.js │ ├── radio │ └── __init__.js │ ├── rpi │ ├── .giosaveVsQYx8 │ ├── __init__.js │ ├── components │ │ ├── cog.png │ │ ├── led.png │ │ └── resistor330.png │ ├── gpiozero.js │ └── raphael.js │ ├── schooldirect │ ├── __init__.js │ └── unicorn.png │ ├── skulpt-stdlib.js │ ├── skulpt.min.js │ ├── speech │ ├── __init__.js │ └── sam.js │ ├── sqlite3 │ └── __init__.js │ ├── tkinter │ └── __init__.js │ └── withcode │ └── __init__.js ├── media ├── console.png ├── play.png ├── recover.png ├── settings.png ├── share.png ├── stop.png ├── tools.png └── tweet.png ├── styles.css ├── thumb.jpg └── toolindex.php /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # createwithcode 2 | ![create.withcode.uk](https://cloud.githubusercontent.com/assets/760604/17930878/50b8f946-6a00-11e6-8a47-55a4054d14d0.png) 3 | 4 | create.withcode.uk lets you write, run, debug and share python code in a web browser. 5 | 6 | ## Features: 7 | * Write python code with syntax highlighting 8 | * Run python code in a web browser 9 | * No user accounts needed 10 | * Completely free 11 | * Includes micro:bit python simulator 12 | * Includes Raspberry Pi GPIO python simulator 13 | 14 | It's designed for students and teachers learning or experimenting with python code. 15 | 16 | This github repro includes most of the source code for create.withcode.uk so that it will run without needing to be hosted on a webserver. It doesn't include the code to be able to save or share python code. 17 | 18 | Try the full version (with ability to save and share) here: [Create.withcode.uk](https://create.withcode.uk) 19 | 20 | Or, try a live copy of this github project (client side only - no save / share) here: [tools.withcode.uk/create](https://tools.withcode.uk/create) 21 | 22 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 28 | 29 | 30 | 31 | 32 | 33 | Create with code 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
61 | To save and share your code please visit create.withcode.uk. Tutorials and resources available at blog.withcode.uk 62 | 63 | 64 |

{withcode.uk}

65 |
66 | 67 | 68 |
69 | 70 | 71 | 72 |
73 | Loading... please wait 74 |
75 | 87 | 93 | 94 | 95 |
96 | 97 | 98 | Click to show/hide tool buttons 99 | Click to run your code 100 | Stop running 101 | Show python output 102 | Customize the screen 103 | Recover your code from a previous session 104 | 105 | 106 | 109 | 110 | 118 | 119 | 171 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /lib/cm/active-line.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | var WRAP_CLASS = "CodeMirror-activeline"; 14 | var BACK_CLASS = "CodeMirror-activeline-background"; 15 | var GUTT_CLASS = "CodeMirror-activeline-gutter"; 16 | 17 | CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { 18 | var prev = old == CodeMirror.Init ? false : old; 19 | if (val == prev) return 20 | if (prev) { 21 | cm.off("beforeSelectionChange", selectionChange); 22 | clearActiveLines(cm); 23 | delete cm.state.activeLines; 24 | } 25 | if (val) { 26 | cm.state.activeLines = []; 27 | updateActiveLines(cm, cm.listSelections()); 28 | cm.on("beforeSelectionChange", selectionChange); 29 | } 30 | }); 31 | 32 | function clearActiveLines(cm) { 33 | for (var i = 0; i < cm.state.activeLines.length; i++) { 34 | cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); 35 | cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); 36 | cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); 37 | } 38 | } 39 | 40 | function sameArray(a, b) { 41 | if (a.length != b.length) return false; 42 | for (var i = 0; i < a.length; i++) 43 | if (a[i] != b[i]) return false; 44 | return true; 45 | } 46 | 47 | function updateActiveLines(cm, ranges) { 48 | var active = []; 49 | for (var i = 0; i < ranges.length; i++) { 50 | var range = ranges[i]; 51 | var option = cm.getOption("styleActiveLine"); 52 | if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) 53 | continue 54 | var line = cm.getLineHandleVisualStart(range.head.line); 55 | if (active[active.length - 1] != line) active.push(line); 56 | } 57 | if (sameArray(cm.state.activeLines, active)) return; 58 | cm.operation(function() { 59 | clearActiveLines(cm); 60 | for (var i = 0; i < active.length; i++) { 61 | cm.addLineClass(active[i], "wrap", WRAP_CLASS); 62 | cm.addLineClass(active[i], "background", BACK_CLASS); 63 | cm.addLineClass(active[i], "gutter", GUTT_CLASS); 64 | } 65 | cm.state.activeLines = active; 66 | }); 67 | } 68 | 69 | function selectionChange(cm, sel) { 70 | updateActiveLines(cm, sel.ranges); 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /lib/cm/blackboard.css: -------------------------------------------------------------------------------- 1 | /* Port of TextMate's Blackboard theme */ 2 | 3 | .cm-s-blackboard.CodeMirror { background: #0C1021; color: #F8F8F8; } 4 | .cm-s-blackboard div.CodeMirror-selected { background: #253B76; } 5 | .cm-s-blackboard .CodeMirror-line::selection, .cm-s-blackboard .CodeMirror-line > span::selection, .cm-s-blackboard .CodeMirror-line > span > span::selection { background: rgba(37, 59, 118, .99); } 6 | .cm-s-blackboard .CodeMirror-line::-moz-selection, .cm-s-blackboard .CodeMirror-line > span::-moz-selection, .cm-s-blackboard .CodeMirror-line > span > span::-moz-selection { background: rgba(37, 59, 118, .99); } 7 | .cm-s-blackboard .CodeMirror-gutters { background: #0C1021; border-right: 0; } 8 | .cm-s-blackboard .CodeMirror-guttermarker { color: #FBDE2D; } 9 | .cm-s-blackboard .CodeMirror-guttermarker-subtle { color: #888; } 10 | .cm-s-blackboard .CodeMirror-linenumber { color: #888; } 11 | .cm-s-blackboard .CodeMirror-cursor { border-left: 1px solid #A7A7A7; } 12 | 13 | .cm-s-blackboard .cm-keyword { color: #FBDE2D; } 14 | .cm-s-blackboard .cm-atom { color: #D8FA3C; } 15 | .cm-s-blackboard .cm-number { color: #D8FA3C; } 16 | .cm-s-blackboard .cm-def { color: #8DA6CE; } 17 | .cm-s-blackboard .cm-variable { color: #FF6400; } 18 | .cm-s-blackboard .cm-operator { color: #FBDE2D; } 19 | .cm-s-blackboard .cm-comment { color: #AEAEAE; } 20 | .cm-s-blackboard .cm-string { color: #61CE3C; } 21 | .cm-s-blackboard .cm-string-2 { color: #61CE3C; } 22 | .cm-s-blackboard .cm-meta { color: #D8FA3C; } 23 | .cm-s-blackboard .cm-builtin { color: #8DA6CE; } 24 | .cm-s-blackboard .cm-tag { color: #8DA6CE; } 25 | .cm-s-blackboard .cm-attribute { color: #8DA6CE; } 26 | .cm-s-blackboard .cm-header { color: #FF6400; } 27 | .cm-s-blackboard .cm-hr { color: #AEAEAE; } 28 | .cm-s-blackboard .cm-link { color: #8DA6CE; } 29 | .cm-s-blackboard .cm-error { background: #9D1E15; color: #F8F8F8; } 30 | 31 | .cm-s-blackboard .CodeMirror-activeline-background { background: #3C3636; } 32 | .cm-s-blackboard .CodeMirror-matchingbracket { outline:1px solid grey;color:white !important; } 33 | -------------------------------------------------------------------------------- /lib/cm/cobalt.css: -------------------------------------------------------------------------------- 1 | .cm-s-cobalt.CodeMirror { background: #002240; color: white; } 2 | .cm-s-cobalt div.CodeMirror-selected { background: #b36539; } 3 | .cm-s-cobalt .CodeMirror-line::selection, .cm-s-cobalt .CodeMirror-line > span::selection, .cm-s-cobalt .CodeMirror-line > span > span::selection { background: rgba(179, 101, 57, .99); } 4 | .cm-s-cobalt .CodeMirror-line::-moz-selection, .cm-s-cobalt .CodeMirror-line > span::-moz-selection, .cm-s-cobalt .CodeMirror-line > span > span::-moz-selection { background: rgba(179, 101, 57, .99); } 5 | .cm-s-cobalt .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } 6 | .cm-s-cobalt .CodeMirror-guttermarker { color: #ffee80; } 7 | .cm-s-cobalt .CodeMirror-guttermarker-subtle { color: #d0d0d0; } 8 | .cm-s-cobalt .CodeMirror-linenumber { color: #d0d0d0; } 9 | .cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white; } 10 | 11 | .cm-s-cobalt span.cm-comment { color: #08f; } 12 | .cm-s-cobalt span.cm-atom { color: #845dc4; } 13 | .cm-s-cobalt span.cm-number, .cm-s-cobalt span.cm-attribute { color: #ff80e1; } 14 | .cm-s-cobalt span.cm-keyword { color: #ffee80; } 15 | .cm-s-cobalt span.cm-string { color: #3ad900; } 16 | .cm-s-cobalt span.cm-meta { color: #ff9d00; } 17 | .cm-s-cobalt span.cm-variable-2, .cm-s-cobalt span.cm-tag { color: #9effff; } 18 | .cm-s-cobalt span.cm-variable-3, .cm-s-cobalt span.cm-def { color: white; } 19 | .cm-s-cobalt span.cm-bracket { color: #d8d8d8; } 20 | .cm-s-cobalt span.cm-builtin, .cm-s-cobalt span.cm-special { color: #ff9e59; } 21 | .cm-s-cobalt span.cm-link { color: #845dc4; } 22 | .cm-s-cobalt span.cm-error { color: #9d1e15; } 23 | 24 | .cm-s-cobalt .CodeMirror-activeline-background { background: #002D57; } 25 | .cm-s-cobalt .CodeMirror-matchingbracket { outline:1px solid grey;color:white !important; } 26 | -------------------------------------------------------------------------------- /lib/cm/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | /*height: 300px;*/ 7 | 8 | color: black; 9 | } 10 | 11 | /* PADDING */ 12 | 13 | .CodeMirror-lines { 14 | padding: 4px 0; /* Vertical padding around content */ 15 | } 16 | .CodeMirror pre { 17 | padding: 0 4px; /* Horizontal padding of content */ 18 | } 19 | 20 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 21 | background-color: white; /* The little square between H and V scrollbars */ 22 | } 23 | 24 | /* GUTTER */ 25 | 26 | .CodeMirror-gutters { 27 | border-right: 1px solid #ddd; 28 | background-color: #f7f7f7; 29 | white-space: nowrap; 30 | } 31 | .CodeMirror-linenumbers {} 32 | .CodeMirror-linenumber { 33 | padding: 0 3px 0 5px; 34 | min-width: 20px; 35 | text-align: right; 36 | color: #999; 37 | white-space: nowrap; 38 | } 39 | 40 | .CodeMirror-guttermarker { color: black; } 41 | .CodeMirror-guttermarker-subtle { color: #999; } 42 | 43 | /* CURSOR */ 44 | 45 | .CodeMirror-cursor { 46 | border-left: 1px solid black; 47 | border-right: none; 48 | width: 0; 49 | } 50 | /* Shown when moving in bi-directional text */ 51 | .CodeMirror div.CodeMirror-secondarycursor { 52 | border-left: 1px solid silver; 53 | } 54 | .cm-fat-cursor .CodeMirror-cursor { 55 | width: auto; 56 | border: 0 !important; 57 | background: #7e7; 58 | } 59 | .cm-fat-cursor div.CodeMirror-cursors { 60 | z-index: 1; 61 | } 62 | 63 | .cm-animate-fat-cursor { 64 | width: auto; 65 | border: 0; 66 | -webkit-animation: blink 1.06s steps(1) infinite; 67 | -moz-animation: blink 1.06s steps(1) infinite; 68 | animation: blink 1.06s steps(1) infinite; 69 | background-color: #7e7; 70 | } 71 | @-moz-keyframes blink { 72 | 0% {} 73 | 50% { background-color: transparent; } 74 | 100% {} 75 | } 76 | @-webkit-keyframes blink { 77 | 0% {} 78 | 50% { background-color: transparent; } 79 | 100% {} 80 | } 81 | @keyframes blink { 82 | 0% {} 83 | 50% { background-color: transparent; } 84 | 100% {} 85 | } 86 | 87 | /* Can style cursor different in overwrite (non-insert) mode */ 88 | .CodeMirror-overwrite .CodeMirror-cursor {} 89 | 90 | .cm-tab { display: inline-block; text-decoration: inherit; } 91 | 92 | .CodeMirror-rulers { 93 | position: absolute; 94 | left: 0; right: 0; top: -50px; bottom: -20px; 95 | overflow: hidden; 96 | } 97 | .CodeMirror-ruler { 98 | border-left: 1px solid #ccc; 99 | top: 0; bottom: 0; 100 | position: absolute; 101 | } 102 | 103 | /* DEFAULT THEME */ 104 | 105 | .cm-s-default .cm-header {color: blue;} 106 | .cm-s-default .cm-quote {color: #090;} 107 | .cm-negative {color: #d44;} 108 | .cm-positive {color: #292;} 109 | .cm-header, .cm-strong {font-weight: bold;} 110 | .cm-em {font-style: italic;} 111 | .cm-link {text-decoration: underline;} 112 | .cm-strikethrough {text-decoration: line-through;} 113 | 114 | .cm-s-default .cm-keyword {color: #708;} 115 | .cm-s-default .cm-atom {color: #219;} 116 | .cm-s-default .cm-number {color: #164;} 117 | .cm-s-default .cm-def {color: #00f;} 118 | .cm-s-default .cm-variable, 119 | .cm-s-default .cm-punctuation, 120 | .cm-s-default .cm-property, 121 | .cm-s-default .cm-operator {} 122 | .cm-s-default .cm-variable-2 {color: #05a;} 123 | .cm-s-default .cm-variable-3 {color: #085;} 124 | .cm-s-default .cm-comment {color: #a50;} 125 | .cm-s-default .cm-string {color: #a11;} 126 | .cm-s-default .cm-string-2 {color: #f50;} 127 | .cm-s-default .cm-meta {color: #555;} 128 | .cm-s-default .cm-qualifier {color: #555;} 129 | .cm-s-default .cm-builtin {color: #30a;} 130 | .cm-s-default .cm-bracket {color: #997;} 131 | .cm-s-default .cm-tag {color: #170;} 132 | .cm-s-default .cm-attribute {color: #00c;} 133 | .cm-s-default .cm-hr {color: #999;} 134 | .cm-s-default .cm-link {color: #00c;} 135 | 136 | .cm-s-default .cm-error {color: #f00;} 137 | .cm-invalidchar {color: #f00;} 138 | 139 | .CodeMirror-composing { border-bottom: 2px solid; } 140 | 141 | /* Default styles for common addons */ 142 | 143 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 144 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 145 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 146 | .CodeMirror-activeline-background {background: #e8f2ff;} 147 | 148 | /* STOP */ 149 | 150 | /* The rest of this file contains styles related to the mechanics of 151 | the editor. You probably shouldn't touch them. */ 152 | 153 | .CodeMirror { 154 | position: absolute; 155 | top:0px; 156 | height: 100%; 157 | width: 100%; 158 | overflow: hidden; 159 | background: white; 160 | } 161 | 162 | .CodeMirror-scroll { 163 | overflow: scroll !important; /* Things will break if this is overridden */ 164 | /* 30px is the magic margin used to hide the element's real scrollbars */ 165 | /* See overflow: hidden in .CodeMirror */ 166 | margin-bottom: -30px; margin-right: -30px; 167 | padding-bottom: 30px; 168 | height: 100%; 169 | outline: none; /* Prevent dragging from highlighting the element */ 170 | position: relative; 171 | } 172 | .CodeMirror-sizer { 173 | position: relative; 174 | border-right: 30px solid transparent; 175 | } 176 | 177 | /* The fake, visible scrollbars. Used to force redraw during scrolling 178 | before actual scrolling happens, thus preventing shaking and 179 | flickering artifacts. */ 180 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 181 | position: absolute; 182 | z-index: 6; 183 | display: none; 184 | } 185 | .CodeMirror-vscrollbar { 186 | right: 0; top: 0; 187 | overflow-x: hidden; 188 | overflow-y: scroll; 189 | } 190 | .CodeMirror-hscrollbar { 191 | bottom: 0; left: 0; 192 | overflow-y: hidden; 193 | overflow-x: scroll; 194 | } 195 | .CodeMirror-scrollbar-filler { 196 | right: 0; bottom: 0; 197 | } 198 | .CodeMirror-gutter-filler { 199 | left: 0; bottom: 0; 200 | } 201 | 202 | .CodeMirror-gutters { 203 | position: absolute; left: 0; top: 0; 204 | min-height: 100%; 205 | z-index: 3; 206 | } 207 | .CodeMirror-gutter { 208 | white-space: normal; 209 | height: 100%; 210 | display: inline-block; 211 | vertical-align: top; 212 | margin-bottom: -30px; 213 | } 214 | .CodeMirror-gutter-wrapper { 215 | position: absolute; 216 | z-index: 4; 217 | background: none !important; 218 | border: none !important; 219 | } 220 | .CodeMirror-gutter-background { 221 | position: absolute; 222 | top: 0; bottom: 0; 223 | z-index: 4; 224 | } 225 | .CodeMirror-gutter-elt { 226 | position: absolute; 227 | cursor: default; 228 | z-index: 4; 229 | } 230 | .CodeMirror-gutter-wrapper { 231 | -webkit-user-select: none; 232 | -moz-user-select: none; 233 | user-select: none; 234 | } 235 | 236 | .CodeMirror-lines { 237 | cursor: text; 238 | min-height: 1px; /* prevents collapsing before first draw */ 239 | } 240 | .CodeMirror pre { 241 | /* Reset some styles that the rest of the page might have set */ 242 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 243 | border-width: 0; 244 | background: transparent; 245 | font-family: inherit; 246 | font-size: inherit; 247 | margin: 0; 248 | white-space: pre; 249 | word-wrap: normal; 250 | line-height: inherit; 251 | color: inherit; 252 | z-index: 2; 253 | position: relative; 254 | overflow: visible; 255 | -webkit-tap-highlight-color: transparent; 256 | -webkit-font-variant-ligatures: contextual; 257 | font-variant-ligatures: contextual; 258 | } 259 | .CodeMirror-wrap pre { 260 | word-wrap: break-word; 261 | white-space: pre-wrap; 262 | word-break: normal; 263 | } 264 | 265 | .CodeMirror-linebackground { 266 | position: absolute; 267 | left: 0; right: 0; top: 0; bottom: 0; 268 | z-index: 0; 269 | } 270 | 271 | .CodeMirror-linewidget { 272 | position: relative; 273 | z-index: 2; 274 | overflow: auto; 275 | } 276 | 277 | .CodeMirror-widget {} 278 | 279 | .CodeMirror-code { 280 | outline: none; 281 | } 282 | 283 | /* Force content-box sizing for the elements where we expect it */ 284 | .CodeMirror-scroll, 285 | .CodeMirror-sizer, 286 | .CodeMirror-gutter, 287 | .CodeMirror-gutters, 288 | .CodeMirror-linenumber { 289 | -moz-box-sizing: content-box; 290 | box-sizing: content-box; 291 | } 292 | 293 | .CodeMirror-measure { 294 | position: absolute; 295 | width: 100%; 296 | height: 0; 297 | overflow: hidden; 298 | visibility: hidden; 299 | } 300 | 301 | .CodeMirror-cursor { 302 | position: absolute; 303 | pointer-events: none; 304 | } 305 | .CodeMirror-measure pre { position: static; } 306 | 307 | div.CodeMirror-cursors { 308 | visibility: hidden; 309 | position: relative; 310 | z-index: 3; 311 | } 312 | div.CodeMirror-dragcursors { 313 | visibility: visible; 314 | } 315 | 316 | .CodeMirror-focused div.CodeMirror-cursors { 317 | visibility: visible; 318 | } 319 | 320 | .CodeMirror-selected { background: #d9d9d9; } 321 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 322 | .CodeMirror-crosshair { cursor: crosshair; } 323 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 324 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 325 | 326 | .cm-searching { 327 | background: #ffa; 328 | background: rgba(255, 255, 0, .4); 329 | } 330 | 331 | /* Used to force a border model for a node */ 332 | .cm-force-border { padding-right: .1px; } 333 | 334 | @media print { 335 | /* Hide the cursor when printing */ 336 | .CodeMirror div.CodeMirror-cursors { 337 | visibility: hidden; 338 | } 339 | } 340 | 341 | /* See issue #2901 */ 342 | .cm-tab-wrap-hack:after { content: ''; } 343 | 344 | /* Help users use markselection to safely style text background */ 345 | span.CodeMirror-selectedtext { background: none; } 346 | -------------------------------------------------------------------------------- /lib/cm/python.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | function wordRegexp(words) { 15 | return new RegExp("^((" + words.join(")|(") + "))\\b"); 16 | } 17 | 18 | var wordOperators = wordRegexp(["and", "or", "not", "is"]); 19 | var commonKeywords = ["as", "assert", "break", "class", "continue", 20 | "def", "del", "elif", "else", "except", "finally", 21 | "for", "from", "global", "if", "import", 22 | "lambda", "pass", "raise", "return", 23 | "try", "while", "with", "yield", "in"]; 24 | var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr", 25 | "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", 26 | "enumerate", "eval", "filter", "float", "format", "frozenset", 27 | "getattr", "globals", "hasattr", "hash", "help", "hex", "id", 28 | "input", "int", "isinstance", "issubclass", "iter", "len", 29 | "list", "locals", "map", "max", "memoryview", "min", "next", 30 | "object", "oct", "open", "ord", "pow", "property", "range", 31 | "repr", "reversed", "round", "set", "setattr", "slice", 32 | "sorted", "staticmethod", "str", "sum", "super", "tuple", 33 | "type", "vars", "zip", "__import__", "NotImplemented", 34 | "Ellipsis", "__debug__"]; 35 | CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins)); 36 | 37 | function top(state) { 38 | return state.scopes[state.scopes.length - 1]; 39 | } 40 | 41 | CodeMirror.defineMode("python", function(conf, parserConf) { 42 | var ERRORCLASS = "error"; 43 | 44 | var singleDelimiters = parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.]/; 45 | var doubleOperators = parserConf.doubleOperators || /^([!<>]==|<>|<<|>>|\/\/|\*\*)/; 46 | var doubleDelimiters = parserConf.doubleDelimiters || /^(\+=|\-=|\*=|%=|\/=|&=|\|=|\^=)/; 47 | var tripleDelimiters = parserConf.tripleDelimiters || /^(\/\/=|>>=|<<=|\*\*=)/; 48 | 49 | var hangingIndent = parserConf.hangingIndent || conf.indentUnit; 50 | 51 | var myKeywords = commonKeywords, myBuiltins = commonBuiltins; 52 | if (parserConf.extra_keywords != undefined) 53 | myKeywords = myKeywords.concat(parserConf.extra_keywords); 54 | 55 | if (parserConf.extra_builtins != undefined) 56 | myBuiltins = myBuiltins.concat(parserConf.extra_builtins); 57 | 58 | var py3 = !(parserConf.version && Number(parserConf.version) < 3) 59 | if (py3) { 60 | // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator 61 | var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/; 62 | var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/; 63 | myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]); 64 | myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]); 65 | var stringPrefixes = new RegExp("^(([rbuf]|(br))?('{3}|\"{3}|['\"]))", "i"); 66 | } else { 67 | var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/; 68 | var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/; 69 | myKeywords = myKeywords.concat(["exec", "print"]); 70 | myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile", 71 | "file", "intern", "long", "raw_input", "reduce", "reload", 72 | "unichr", "unicode", "xrange", "False", "True", "None"]); 73 | var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i"); 74 | } 75 | var keywords = wordRegexp(myKeywords); 76 | var builtins = wordRegexp(myBuiltins); 77 | 78 | // tokenizers 79 | function tokenBase(stream, state) { 80 | if (stream.sol()) state.indent = stream.indentation() 81 | // Handle scope changes 82 | if (stream.sol() && top(state).type == "py") { 83 | var scopeOffset = top(state).offset; 84 | if (stream.eatSpace()) { 85 | var lineOffset = stream.indentation(); 86 | if (lineOffset > scopeOffset) 87 | pushPyScope(state); 88 | else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#") 89 | state.errorToken = true; 90 | return null; 91 | } else { 92 | var style = tokenBaseInner(stream, state); 93 | if (scopeOffset > 0 && dedent(stream, state)) 94 | style += " " + ERRORCLASS; 95 | return style; 96 | } 97 | } 98 | return tokenBaseInner(stream, state); 99 | } 100 | 101 | function tokenBaseInner(stream, state) { 102 | if (stream.eatSpace()) return null; 103 | 104 | var ch = stream.peek(); 105 | 106 | // Handle Comments 107 | if (ch == "#") { 108 | stream.skipToEnd(); 109 | return "comment"; 110 | } 111 | 112 | // Handle Number Literals 113 | if (stream.match(/^[0-9\.]/, false)) { 114 | var floatLiteral = false; 115 | // Floats 116 | if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } 117 | if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } 118 | if (stream.match(/^\.\d+/)) { floatLiteral = true; } 119 | if (floatLiteral) { 120 | // Float literals may be "imaginary" 121 | stream.eat(/J/i); 122 | return "number"; 123 | } 124 | // Integers 125 | var intLiteral = false; 126 | // Hex 127 | if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true; 128 | // Binary 129 | if (stream.match(/^0b[01]+/i)) intLiteral = true; 130 | // Octal 131 | if (stream.match(/^0o[0-7]+/i)) intLiteral = true; 132 | // Decimal 133 | if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { 134 | // Decimal literals may be "imaginary" 135 | stream.eat(/J/i); 136 | // TODO - Can you have imaginary longs? 137 | intLiteral = true; 138 | } 139 | // Zero by itself with no other piece of number. 140 | if (stream.match(/^0(?![\dx])/i)) intLiteral = true; 141 | if (intLiteral) { 142 | // Integer literals may be "long" 143 | stream.eat(/L/i); 144 | return "number"; 145 | } 146 | } 147 | 148 | // Handle Strings 149 | if (stream.match(stringPrefixes)) { 150 | state.tokenize = tokenStringFactory(stream.current()); 151 | return state.tokenize(stream, state); 152 | } 153 | 154 | // Handle operators and Delimiters 155 | if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) 156 | return "punctuation"; 157 | 158 | if (stream.match(doubleOperators) || stream.match(singleOperators)) 159 | return "operator"; 160 | 161 | if (stream.match(singleDelimiters)) 162 | return "punctuation"; 163 | 164 | if (state.lastToken == "." && stream.match(identifiers)) 165 | return "property"; 166 | 167 | if (stream.match(keywords) || stream.match(wordOperators)) 168 | return "keyword"; 169 | 170 | if (stream.match(builtins)) 171 | return "builtin"; 172 | 173 | if (stream.match(/^(self|cls)\b/)) 174 | return "variable-2"; 175 | 176 | if (stream.match(identifiers)) { 177 | if (state.lastToken == "def" || state.lastToken == "class") 178 | return "def"; 179 | return "variable"; 180 | } 181 | 182 | // Handle non-detected items 183 | stream.next(); 184 | return ERRORCLASS; 185 | } 186 | 187 | function tokenStringFactory(delimiter) { 188 | while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0) 189 | delimiter = delimiter.substr(1); 190 | 191 | var singleline = delimiter.length == 1; 192 | var OUTCLASS = "string"; 193 | 194 | function tokenString(stream, state) { 195 | while (!stream.eol()) { 196 | stream.eatWhile(/[^'"\\]/); 197 | if (stream.eat("\\")) { 198 | stream.next(); 199 | if (singleline && stream.eol()) 200 | return OUTCLASS; 201 | } else if (stream.match(delimiter)) { 202 | state.tokenize = tokenBase; 203 | return OUTCLASS; 204 | } else { 205 | stream.eat(/['"]/); 206 | } 207 | } 208 | if (singleline) { 209 | if (parserConf.singleLineStringErrors) 210 | return ERRORCLASS; 211 | else 212 | state.tokenize = tokenBase; 213 | } 214 | return OUTCLASS; 215 | } 216 | tokenString.isString = true; 217 | return tokenString; 218 | } 219 | 220 | function pushPyScope(state) { 221 | while (top(state).type != "py") state.scopes.pop() 222 | state.scopes.push({offset: top(state).offset + conf.indentUnit, 223 | type: "py", 224 | align: null}) 225 | } 226 | 227 | function pushBracketScope(stream, state, type) { 228 | var align = stream.match(/^([\s\[\{\(]|#.*)*$/, false) ? null : stream.column() + 1 229 | state.scopes.push({offset: state.indent + hangingIndent, 230 | type: type, 231 | align: align}) 232 | } 233 | 234 | function dedent(stream, state) { 235 | var indented = stream.indentation(); 236 | while (state.scopes.length > 1 && top(state).offset > indented) { 237 | if (top(state).type != "py") return true; 238 | state.scopes.pop(); 239 | } 240 | return top(state).offset != indented; 241 | } 242 | 243 | function tokenLexer(stream, state) { 244 | if (stream.sol()) state.beginningOfLine = true; 245 | 246 | var style = state.tokenize(stream, state); 247 | var current = stream.current(); 248 | 249 | // Handle decorators 250 | if (state.beginningOfLine && current == "@") 251 | return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS; 252 | 253 | if (/\S/.test(current)) state.beginningOfLine = false; 254 | 255 | if ((style == "variable" || style == "builtin") 256 | && state.lastToken == "meta") 257 | style = "meta"; 258 | 259 | // Handle scope changes. 260 | if (current == "pass" || current == "return") 261 | state.dedent += 1; 262 | 263 | if (current == "lambda") state.lambda = true; 264 | if (current == ":" && !state.lambda && top(state).type == "py") 265 | pushPyScope(state); 266 | 267 | var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1; 268 | if (delimiter_index != -1) 269 | pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1)); 270 | 271 | delimiter_index = "])}".indexOf(current); 272 | if (delimiter_index != -1) { 273 | if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent 274 | else return ERRORCLASS; 275 | } 276 | if (state.dedent > 0 && stream.eol() && top(state).type == "py") { 277 | if (state.scopes.length > 1) state.scopes.pop(); 278 | state.dedent -= 1; 279 | } 280 | 281 | return style; 282 | } 283 | 284 | var external = { 285 | startState: function(basecolumn) { 286 | return { 287 | tokenize: tokenBase, 288 | scopes: [{offset: basecolumn || 0, type: "py", align: null}], 289 | indent: basecolumn || 0, 290 | lastToken: null, 291 | lambda: false, 292 | dedent: 0 293 | }; 294 | }, 295 | 296 | token: function(stream, state) { 297 | var addErr = state.errorToken; 298 | if (addErr) state.errorToken = false; 299 | var style = tokenLexer(stream, state); 300 | 301 | if (style && style != "comment") 302 | state.lastToken = (style == "keyword" || style == "punctuation") ? stream.current() : style; 303 | if (style == "punctuation") style = null; 304 | 305 | if (stream.eol() && state.lambda) 306 | state.lambda = false; 307 | return addErr ? style + " " + ERRORCLASS : style; 308 | }, 309 | 310 | indent: function(state, textAfter) { 311 | if (state.tokenize != tokenBase) 312 | return state.tokenize.isString ? CodeMirror.Pass : 0; 313 | 314 | var scope = top(state), closing = scope.type == textAfter.charAt(0) 315 | if (scope.align != null) 316 | return scope.align - (closing ? 1 : 0) 317 | else 318 | return scope.offset - (closing ? hangingIndent : 0) 319 | }, 320 | 321 | electricInput: /^\s*[\}\]\)]$/, 322 | closeBrackets: {triples: "'\""}, 323 | lineComment: "#", 324 | fold: "indent" 325 | }; 326 | return external; 327 | }); 328 | 329 | CodeMirror.defineMIME("text/x-python", "python"); 330 | 331 | var words = function(str) { return str.split(" "); }; 332 | 333 | CodeMirror.defineMIME("text/x-cython", { 334 | name: "python", 335 | extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+ 336 | "extern gil include nogil property public"+ 337 | "readonly struct union DEF IF ELIF ELSE") 338 | }); 339 | 340 | }); 341 | -------------------------------------------------------------------------------- /lib/jq/jquery.ui.touch-punch.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Touch Punch 0.2.3 3 | * 4 | * Copyright 2011–2014, Dave Furfero 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * 7 | * Depends: 8 | * jquery.ui.widget.js 9 | * jquery.ui.mouse.js 10 | */ 11 | !function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery); -------------------------------------------------------------------------------- /lib/lib.js: -------------------------------------------------------------------------------- 1 | // nasty hacky animated title 2 | var t; 3 | function animateTitle(txt, id) { 4 | var chars = "0123456789ABCDEF"; 5 | var finalChars = txt.split(''); 6 | 7 | var jq = $('#' + id); 8 | 9 | var letterCount = -10; 10 | function onAnimTimeout() { 11 | var randomChars = ""; 12 | if(letterCount >=0 ) { 13 | randomChars = txt.substring(0, letterCount); 14 | } 15 | 16 | randomChars += ''; 17 | for(var i = (letterCount < 0)?0:letterCount; i < txt.length; i++) { 18 | var c = Math.floor(Math.random() * chars.length); 19 | randomChars += chars[c]; 20 | } 21 | randomChars += ''; 22 | 23 | 24 | jq.html(randomChars); 25 | letterCount++; 26 | clearTimeout(t); 27 | if(letterCount <= txt.length) { 28 | t = setTimeout(onAnimTimeout, 50); 29 | } 30 | } 31 | 32 | t = setTimeout(onAnimTimeout, 50); 33 | 34 | } 35 | 36 | // Main PythonIDE object 37 | var PythonIDE = { 38 | updateConsoleSize: function() { 39 | var h = $('#output').height() - $('#headerOut').height(); 40 | if(h < 100) { 41 | h = 100; 42 | } 43 | $('#consoleOut').css({ 44 | 'max-height': h + "px" 45 | }); 46 | }, 47 | // edit a file 48 | // filename: string (name of file to edit) 49 | editFile: function(filename) { 50 | if(PythonIDE.files[PythonIDE.currentFile]) 51 | PythonIDE.files[PythonIDE.currentFile] = PythonIDE.editor.getValue(); 52 | PythonIDE.currentFile = filename; 53 | PythonIDE.editor.setValue(PythonIDE.files[filename]); 54 | PythonIDE.editor.setCursor(0); 55 | PythonIDE.editor.focus(); 56 | 57 | var extension = filename.match(/(\.[^.]+)/); 58 | if(extension.length > 1) 59 | extension = extension[1]; 60 | switch(extension) { 61 | case '.py': 62 | PythonIDE.editor.setOption("mode", "python"); 63 | break; 64 | case '.html': 65 | PythonIDE.editor.setOption("mode", "html"); 66 | break; 67 | case '.js': 68 | PythonIDE.editor.setOption("mode", "javascript"); 69 | break; 70 | case '.sql': 71 | PythonIDE.editor.setOption("mode", "sql"); 72 | break; 73 | default: 74 | PythonIDE.editor.setOption("mode", "python"); 75 | break; 76 | } 77 | 78 | PythonIDE.updateFileTabs(); 79 | }, 80 | 81 | // update the list of files at the top of the screen 82 | updateFileTabs: function() { 83 | var html = ''; 84 | for(var file in PythonIDE.files){ 85 | html += '' 88 | if(file != 'mycode.py'){ 89 | html += 'File settings'; 90 | } 91 | } else { 92 | html += '">'; 93 | } 94 | html += file + ''; 95 | } 96 | html += 'Create new file'; 97 | $('#file_tabs').html(html); 98 | $('.file_tab').click(function(e) { 99 | var fileName = e.currentTarget.textContent; 100 | switch(fileName) { 101 | case "": 102 | fileName = 'newfile.txt'; 103 | if(PythonIDE.files[fileName] === undefined){ 104 | PythonIDE.files[fileName] = ''; 105 | } 106 | PythonIDE.editFile('newfile.txt'); 107 | break; 108 | case PythonIDE.currentFile: 109 | if(fileName != "mycode.py") { 110 | $('#file_settings').dialog("open"); 111 | $('#txt_file_name').val(fileName).focus(); 112 | } 113 | break; 114 | default: 115 | PythonIDE.editFile(e.currentTarget.textContent); 116 | break; 117 | } 118 | 119 | }); 120 | }, 121 | 122 | // file currently being edited 123 | currentFile: 'mycode.py', 124 | 125 | // returns the number of files in the current project 126 | countFiles: function() { 127 | var c = 0; 128 | for(var f in PythonIDE.files) { 129 | c++; 130 | } 131 | return c; 132 | }, 133 | 134 | // stores each of the files in the project 135 | files: {'mycode.py':''}, 136 | 137 | // callback function to allow python (skulpt) to read from a file 138 | readFile: function(filename) { 139 | return PythonIDE.files[filename]; 140 | }, 141 | 142 | // callback function to allow python (skulpt) to write to a file 143 | writeFile: function(filename, contents) { 144 | PythonIDE.files[filename] = contents; 145 | PythonIDE.updateFileTabs(); 146 | }, 147 | 148 | // message to display in the status bar at the bottom of the screen 149 | welcomeMessage: "Press Ctrl+Enter to run", 150 | 151 | // options are stored in browers's localstorage. Get the value of a specified option 152 | getOption: function(optionName, defaultValue) { 153 | if(localStorage && localStorage['OPT_' + optionName]) 154 | return localStorage['OPT_' + optionName] 155 | return defaultValue; 156 | }, 157 | 158 | // set the value of an option and store it in the browser's localstorage 159 | setOption: function(optionName, value) { 160 | localStorage['OPT_' + optionName] = value; 161 | return value; 162 | }, 163 | 164 | // display text in the status bar 165 | showHint: function(msg) { 166 | if(PythonIDE.hideHintTimeout) { 167 | clearTimeout(PythonIDE.hideHintTimeout); 168 | } 169 | PythonIDE.hideHintTimeout = setTimeout(function(){ 170 | delete PythonIDE.hideHintTimeout; 171 | $('#hintBar').fadeOut(); 172 | }, 5000); 173 | $('#hintBar').html(msg).show(); 174 | }, 175 | 176 | // functions and data needed for running theh python code 177 | python: { 178 | outputListeners: [], 179 | 180 | output: function(text, header) { 181 | var id = header == undefined?'consoleOut': 'headerOut'; 182 | var c = document.getElementById(id); 183 | c.innerHTML += text; 184 | 185 | var i = 0; 186 | while(i < PythonIDE.python.outputListeners.length) { 187 | var l = PythonIDE.python.outputListeners[i]; 188 | try { 189 | l(text); 190 | i++; 191 | } catch(e) { 192 | PythonIDE.python.outputListeners.splice(i, 1); 193 | } 194 | } 195 | var c = c.parentNode.parentNode; 196 | c.scrollTop = c.scrollHeight; 197 | 198 | }, 199 | 200 | clear: function() { 201 | var c = document.getElementById('consoleOut'); 202 | c.innerHTML = ''; 203 | var c = c.parentNode.parentNode; 204 | c.scrollTop = c.scrollHeight; 205 | }, 206 | 207 | builtinread: function(x) { 208 | if (Sk.builtinFiles === undefined || Sk.builtinFiles["files"][x] === undefined) 209 | throw "File not found: '" + x + "'"; 210 | return Sk.builtinFiles["files"][x]; 211 | } 212 | }, 213 | 214 | // convenience function that allows modules to run syncronous code asyncrounously. 215 | // For example time.sleep needs to pause the python program but shouldn't make the browser unresponsive 216 | runAsync: function(asyncFunc) { 217 | var p = new Promise(asyncFunc); 218 | var result; 219 | var susp = new Sk.misceval.Suspension(); 220 | susp.resume = function() { 221 | return result; 222 | } 223 | susp.data = { 224 | type: "Sk.promise", 225 | promise: p.then(function(value) { 226 | result = value; 227 | return value; 228 | }, function(err) { 229 | result = ""; 230 | PythonIDE.handleError(err); 231 | return new Promise(function(resolve, reject){ 232 | }); 233 | }) 234 | }; 235 | return susp; 236 | }, 237 | 238 | // used when displaying the contents of global variables that are too large to display on the screeen until clicked on to expand 239 | watchVariables: { 240 | expandHandlers:[] 241 | }, 242 | 243 | // stores all code executed on this browser so the user can recover code from a previous session 244 | vault: localStorage.vault?JSON.parse(localStorage.vault):[], 245 | 246 | // recover code from a previous session in this browser. This is client side only. To save in the cloud, visit create.withcode.uk 247 | recover: function() { 248 | PythonIDE.saveSnapshot(); 249 | var html = '

A copy of your code is saved in your browser every time you run it.

'; 250 | html += '

Number of code backups currently stored in the vault:' + PythonIDE.vault.length + '

'; 251 | html += '
Drag the slider to go back in time and recover your code'; 252 | 253 | html += '

'; 254 | html += '

If more than one person on this computer uses the same login you may wish to clear all of the code stored in the recovery vault.

Be careful: once you\'ve pressed Delete all you can\'t go back.

'; 255 | html += '

'; 256 | 257 | 258 | $('#recover').html(html).dialog("open").parent().css({'opacity':0.8}); 259 | $('#slider_recover').slider({ 260 | min: 0, 261 | max: PythonIDE.vault.length - 1, 262 | value: PythonIDE.vault.length - 1, 263 | slide: function(event, ui) { 264 | var snapshot = PythonIDE.vault[ui.value]; 265 | var d = new Date(snapshot.date); 266 | PythonIDE.files = JSON.parse(snapshot.files); 267 | PythonIDE.currentFile = "mycode.py"; 268 | PythonIDE.editor.setValue(PythonIDE.files[PythonIDE.currentFile]); 269 | PythonIDE.updateFileTabs(); 270 | PythonIDE.editor.setCursor(0); // clear selection 271 | var ds = "" + (d.getMonth() + 1) + "/" + (d.getDate()) + "/" + d.getFullYear(); 272 | var hours = d.getHours() % 12; 273 | if(hours == 0) { 274 | hours = 12; 275 | } 276 | var days = "Sun,Mon,Tue,Wed,Thu,Fri,Sat,Sun".split(","); 277 | var months = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","); 278 | function pad2(s){s = s.toString();return s.length < 2?"0"+s:s;} 279 | $('#recover_time').html('

' + days[d.getDay()] + ' ' + d.getDate() + ' ' + months[d.getMonth()] + ' ' + d.getFullYear() + '

Recovery time:

' + (pad2(hours)) + '' + pad2(d.getMinutes()) + '' + ((d.getHours() >= 12)? "PM" : "AM") + ''); 280 | 281 | } 282 | }); 283 | $('#btn_recover_clear').button().click(function(){ 284 | PythonIDE.vault = []; 285 | PythonIDE.recover(); 286 | }); 287 | $('#btn_recover').button().click(function() { 288 | $('#recover').dialog("close"); 289 | }); 290 | }, 291 | 292 | // save code to the vault (store it in the browser's local storage so it can be recovered at a later date / session) 293 | saveSnapshot: function() { 294 | var snapshot = {date: Date.now(), files: JSON.stringify(PythonIDE.files)}; 295 | while(PythonIDE.vault.length > 50) { 296 | delete PythonIDE.vault[0]; 297 | } 298 | var match = false; 299 | for(i = 0; i < PythonIDE.vault.length; i++) { 300 | if(i == PythonIDE.vault.length - 1 && PythonIDE.vault[i].files == snapshot.files) { 301 | PythonIDE.vault[i].date = snapshot.date; 302 | match = true; 303 | } 304 | } 305 | if(!match) { 306 | PythonIDE.vault.push(snapshot); 307 | } 308 | 309 | localStorage.vault = JSON.stringify(PythonIDE.vault); 310 | }, 311 | 312 | // run the code in the editor 313 | // runMode can be "anim" to step through each line of python code or "normal" to run the whole code as fast as possible 314 | runCode: function(runMode) { 315 | if(PythonIDE.unhandledError) 316 | delete PythonIDE.unhandledError; 317 | 318 | if(PythonIDE.animTimeout && runMode != "anim") { 319 | clearTimeout(PythonIDE.animTimeout); 320 | delete PythonIDE.animTimeout; 321 | return; 322 | } 323 | 324 | if(PythonIDE.continueDebug) { 325 | if(runMode != "normal") { 326 | PythonIDE.continueDebug(); 327 | return; 328 | } 329 | } 330 | 331 | if(runMode === undefined) 332 | runMode = "normal"; 333 | 334 | PythonIDE.runMode = runMode; 335 | PythonIDE.python.outputListeners = []; 336 | 337 | PythonIDE.showHint("Running code..."); 338 | $('#btn_stopRunning').addClass('visibleButton'); 339 | 340 | var code = PythonIDE.files['mycode.py']; 341 | localStorage.lastRunCode = code; 342 | 343 | PythonIDE.saveSnapshot(); 344 | 345 | 346 | var html = ''; 347 | html += '
'; 348 | html += '

Variables:

'; 349 | html += ''; 350 | if(code.indexOf("turtle") > 0) { 351 | html += '
'; 352 | } 353 | html += '
'; 354 | 355 | 356 | 357 | $('#output').html(html); 358 | $('#dlg').dialog("open"); 359 | 360 | $('#btn_stop').button().click(function() { 361 | localStorage.loadAction = "restoreCode"; 362 | window.location = window.location.href.replace('run/', 'python/'); 363 | }); 364 | 365 | if(!PythonIDE.whenFinished) { 366 | $('#btn_hideConsole').button().click(function() { 367 | $('#dlg').dialog("close"); 368 | }); 369 | } else { 370 | $('#btn_hideConsole').hide(); 371 | } 372 | 373 | var handlers = []; 374 | if(runMode != "normal") { 375 | handlers["Sk.debug"] = function(susp) { 376 | // globals 377 | //console.log(susp.child); 378 | var html = '

Global variables:

'; 379 | PythonIDE.watchVariables.expandHandlers = []; 380 | for(var key in susp.child.$gbl) { 381 | var pyVal = susp.child.$gbl[key]; 382 | var val = JSON.stringify(Sk.ffi.remapToJs(pyVal)); 383 | 384 | if(val === undefined) { 385 | val = ""; 386 | } 387 | 388 | if(val && val.length && val.length > 20) { 389 | var eH = {"id":PythonIDE.watchVariables.expandHandlers.length, "fullText": val, "shortText": val.substring(0,17)}; 390 | 391 | PythonIDE.watchVariables.expandHandlers.push(eH); 392 | val = '' + val.substring(0, 17) + ''; 393 | } 394 | 395 | var type = pyVal.skType?pyVal.skType : pyVal.__proto__.tp$name; 396 | if(type == "function") { 397 | continue; 398 | } 399 | if(type == "str") { 400 | type = "string"; 401 | } 402 | if(type === undefined) { 403 | //console.log(pyVal, val, type); 404 | continue; 405 | } 406 | html += ''; 407 | } 408 | html += '
NameData typeValue
' + key + '' + type + '' + val + '
'; 409 | 410 | 411 | 412 | $('#watch').html(html); 413 | 414 | $('span.debug_expand_zone').click(function(e) { 415 | var id = e.currentTarget.id; 416 | var idNum = id.replace("debug_expand_", ""); 417 | $('#' + id).html(PythonIDE.watchVariables.expandHandlers[idNum].fullText); 418 | }); 419 | 420 | var p = new Promise(function(resolve,reject){ 421 | PythonIDE.continueDebug = function() { 422 | return resolve(susp.resume()); 423 | } 424 | 425 | PythonIDE.abortDebug = function() { 426 | delete PythonIDE.abortDebug; 427 | delete PythonIDE.continueDebug; 428 | return reject("Program aborted"); 429 | } 430 | 431 | }); 432 | return p; 433 | } 434 | setTimeout(function() {PythonIDE.runCode(runMode); }, 100); 435 | $('#watch').show(); 436 | } else { 437 | 438 | // if code contains a while loop 439 | if((code.indexOf("while ") > -1) && (code.indexOf("sleep") == -1)) { 440 | console.log("Crash prevention mode enabled: This happens when your code includes an infinite loop without a sleep() function call. Your code will run much more slowly in this mode."); 441 | 442 | var startTime = new Date().getTime(); 443 | var lineCount = 0; 444 | handlers["Sk.debug"] = function(susp) { 445 | lineCount++; 446 | if(new Date().getTime() - startTime > 100) { 447 | if(lineCount < 100) { 448 | return; 449 | } 450 | startTime = new Date().getTime(); 451 | var p = new Promise(function(resolve, reject) { 452 | setTimeout(function() { 453 | PythonIDE.showHint("Limiting speed to avoid crashing the browser: " + (lineCount * 10) + " lines per second"); 454 | lineCount = 0; 455 | return resolve(susp.resume()); 456 | }, 50); 457 | 458 | }); 459 | return p; 460 | } 461 | 462 | 463 | }; 464 | } 465 | } 466 | 467 | 468 | 469 | Sk.misceval.callsimAsync(handlers, function() { 470 | return Sk.importMainWithBody("mycode",false,code,true); 471 | }).then(function(module){ 472 | PythonIDE.showHint('Program finished running'); 473 | if(PythonIDE.continueDebug) 474 | delete PythonIDE.continueDebug; 475 | if(PythonIDE.abortDebug) 476 | delete PythonIDE.abortDebug; 477 | $('#btn_stop').hide(); 478 | $('#btn_stopRunning').removeClass('visibleButton').addClass('hiddenButton'); 479 | if(PythonIDE.whenFinished) { 480 | PythonIDE.whenFinished(); 481 | } 482 | }, PythonIDE.handleError); 483 | 484 | }, 485 | 486 | // display errors caught when the python code runs 487 | handleError:function (err){ 488 | 489 | console.log(err); 490 | 491 | if(!PythonIDE.unhandledError && PythonIDE.continueDebug) { 492 | PythonIDE.unhandledError = err; 493 | return; 494 | } 495 | 496 | var html = '' + err.toString() + ''; 497 | PythonIDE.showHint(html); 498 | PythonIDE.python.output(html); 499 | }, 500 | 501 | // not used in client side code 502 | showShare: function(){ 503 | 504 | if(!PythonIDE.shareMode) 505 | PythonIDE.shareMode = "code"; 506 | 507 | var link = "" + window.location; 508 | var embed = ('https://create.withcode.uk' + window.location.pathname).replace('python/', 'embed/'); 509 | 510 | if(PythonIDE.shareMode == "run") { 511 | link = link.replace('python/', 'run/'); 512 | embed = embed.replace('embed/', 'run/'); 513 | } 514 | $('#share_link_val').val(link); 515 | $('#share_embed_val').val(''); 516 | $('#share_qr_val').html(''); 517 | $('#share_tweet_val').attr({href:'http://twitter.com/home/?status=' + encodeURIComponent("My python code: " + link)}); 518 | $('#share').dialog("open"); 519 | }, 520 | 521 | // not used in client side code 522 | save: function() { 523 | PythonIDE.showHint("Saving..."); 524 | var code = PythonIDE.files['mycode.py']; 525 | if(PythonIDE.countFiles() > 1) { 526 | code = JSON.stringify(PythonIDE.files); 527 | } 528 | $.getJSON('/lib/api.php', { 529 | cmd: 'save', 530 | code: encodeURIComponent(btoa(code)) 531 | }, function(data) { 532 | //console.log(data); 533 | var link = 'https://create.withcode.uk/python/'+data.hash; 534 | window.location=link; 535 | }); 536 | }, 537 | 538 | // event handler that responds when the browser resizes 539 | autoSize: function(e) { 540 | if(e && e.target.localName == "div") 541 | return; 542 | // expand editor to fit height of the screen. 543 | $('.holder').css({height: window.innerHeight - 80}); 544 | $('#dlg,#settings,#login,#share,#file_settings, #recover').dialog({ 545 | width: window.innerWidth * 0.8, 546 | height: window.innerHeight * 0.7 547 | }); 548 | }, 549 | 550 | // initialise the python ide 551 | init: function(style) { 552 | PythonIDE.showHint(PythonIDE.welcomeMessage); 553 | window.onresize = PythonIDE.autoSize; 554 | PythonIDE.updateFileTabs(); 555 | 556 | $('#share_tabs').tabs(); 557 | 558 | animateTitle('create.withcode.uk', 'title_text'); 559 | 560 | PythonIDE.editor = CodeMirror(document.getElementById('editor'), { 561 | value: PythonIDE.files['mycode.py'], 562 | mode: 'python', 563 | lineNumbers: true, 564 | styleActiveLine: true, 565 | inputStyle: "textarea" 566 | }); 567 | PythonIDE.editor.addKeyMap({ 568 | "Tab": function (cm) { 569 | if (cm.somethingSelected()) { 570 | var sel = PythonIDE.editor.getSelection("\n"); 571 | // Indent only if there are multiple lines selected, or if the selection spans a full line 572 | if (sel.length > 0 && (sel.indexOf("\n") > -1 || sel.length === cm.getLine(cm.getCursor().line).length)) { 573 | cm.indentSelection("add"); 574 | return; 575 | } 576 | } 577 | 578 | if (cm.options.indentWithTabs) 579 | cm.execCommand("insertTab"); 580 | else 581 | cm.execCommand("insertSoftTab"); 582 | }, 583 | "Shift-Tab": function (cm) { 584 | cm.indentSelection("subtract"); 585 | } 586 | }); 587 | if(style != "embed" && style != "run") { 588 | PythonIDE.editor.focus(); 589 | } 590 | PythonIDE.editor.on("change", function(e) { 591 | if(PythonIDE.abortDebug) { 592 | PythonIDE.abortDebug(); 593 | } 594 | PythonIDE.files[PythonIDE.currentFile] = PythonIDE.editor.getValue(); 595 | }); 596 | 597 | 598 | 599 | $('#file_settings button').button().click(function(e) { 600 | switch(e.currentTarget.id) { 601 | case 'btn_file_rename': 602 | var newFileName = $('#txt_file_name').val(); 603 | if(!newFileName.match(/^[A-Za-z0-9_.]+$/)){ 604 | PythonIDE.showHint("Invalid file name"); 605 | break; 606 | } 607 | if(PythonIDE.files[newFileName]) { 608 | PythonIDE.showHint('A file with this name already exists'); 609 | break; 610 | } 611 | var fileContents = PythonIDE.files[PythonIDE.currentFile] 612 | delete PythonIDE.files[PythonIDE.currentFile]; 613 | PythonIDE.currentFile = newFileName; 614 | PythonIDE.files[PythonIDE.currentFile] = fileContents; 615 | PythonIDE.updateFileTabs(); 616 | $('#file_settings').dialog("close"); 617 | PythonIDE.editFile(newFileName); 618 | 619 | break; 620 | case 'btn_file_delete': 621 | delete PythonIDE.files[PythonIDE.currentFile]; 622 | PythonIDE.editFile("mycode.py"); 623 | 624 | case 'btn_file_cancel': 625 | $('#file_settings').dialog("close"); 626 | break; 627 | } 628 | //console.log(e.currentTarget.id); 629 | }); 630 | 631 | if(localStorage && !localStorage.options) { 632 | localStorage.options = { 633 | codeSize:12, 634 | outputSize: 12, 635 | outputTransparency: 0, 636 | stepAnimtime: 1000 637 | } 638 | } 639 | 640 | $('#slider_code_size').slider({ 641 | value: PythonIDE.getOption('codeSize', 12), 642 | min: 6, 643 | max: 40, 644 | slide: function(e, ui) { 645 | $('#txt_code_size').val(ui.value + "pt"); 646 | $('#editor').css({'font-size':ui.value + 'pt'}); 647 | PythonIDE.setOption('codeSize', ui.value); 648 | } 649 | }); 650 | $('#txt_code_size').val(PythonIDE.getOption('codeSize', 12) + "pt"); 651 | $('#editor').css({'font-size':PythonIDE.getOption('codeSize', 12) + 'pt'}); 652 | 653 | $('#slider_output_size').slider({ 654 | value: PythonIDE.getOption('outputSize', 12), 655 | min: 6, 656 | max: 40, 657 | slide: function(e, ui) { 658 | $('#txt_output_size').val(ui.value + "pt"); 659 | $('#output').css({'font-size':ui.value + 'pt'}); 660 | PythonIDE.setOption('outputSize', ui.value) 661 | } 662 | }); 663 | $('#txt_output_size').val(PythonIDE.getOption('outputSize', 12) + "pt"); 664 | $('#output').css({'font-size':PythonIDE.getOption('outputSize', 12) + 'pt'}); 665 | 666 | $('#slider_output_transparency').slider({ 667 | value: PythonIDE.getOption('outputTransparency', 0), 668 | min: 0, 669 | max: 100, 670 | slide: function(e, ui) { 671 | $('#txt_output_transparency').val(ui.value + "%"); 672 | $('#dlg').parent().css({'opacity':1 - (ui.value / 100)}); 673 | PythonIDE.setOption('outputTransparency', ui.value); 674 | } 675 | }); 676 | $('#txt_output_transparency').val(PythonIDE.getOption('outputTransparency', 0) + "%"); 677 | 678 | $('#slider_step_anim_time').slider({ 679 | value: PythonIDE.getOption('stepAnimTime', 500), 680 | min: 500, 681 | max: 5000, 682 | step: 500, 683 | slide: function(e, ui) { 684 | $('#txt_step_anim_time').val(ui.value / 1000 + "s"); 685 | PythonIDE.setOption('stepAnimtime', ui.value) 686 | } 687 | }); 688 | $('#txt_step_anim_time').val(PythonIDE.getOption('stepAnimTime', 500) + "ms"); 689 | 690 | window.onerror=function(err) { 691 | var msg = err.toString().replace("Uncaught ", ""); 692 | var html = '' + msg + ''; 693 | PythonIDE.showHint(html); 694 | PythonIDE.python.output(html); 695 | console.log(err); 696 | 697 | return true; 698 | } 699 | 700 | // setup keyboard shortcutts 701 | $(window).keydown(function(e) { 702 | if(e.ctrlKey) { 703 | switch(e.keyCode) { 704 | case 13: // CTRL + ENTER = run / stop 705 | PythonIDE.runCode("normal"); 706 | e.preventDefault(); 707 | break; 708 | 709 | case 83: // CTRL + S = save 710 | PythonIDE.save(); 711 | e.preventDefault(); 712 | break; 713 | 714 | case 190: // CTRL + . = step | CTRL SHIFT + . = anim 715 | if(e.altKey) { 716 | if(PythonIDE.abortDebug) { 717 | PythonIDE.abortDebug(); 718 | } 719 | } else { 720 | if(e.shiftKey) { 721 | PythonIDE.runCode("anim"); 722 | } else { 723 | PythonIDE.runCode("step"); 724 | } 725 | } 726 | e.preventDefault(); 727 | break; 728 | 729 | case 79: // CTRL + O settings 730 | $('#settings').dialog("open"); 731 | e.preventDefault(); 732 | break; 733 | 734 | default: 735 | //console.log("Control + keycode:" + e.keyCode); 736 | break; 737 | } 738 | } 739 | }); 740 | 741 | $('#dlg,#settings,#login,#share,#file_settings, #recover').dialog({ 742 | autoOpen:false, 743 | width: window.innerWidth * 0.8, 744 | height: window.innerHeight * 0.7 745 | }); 746 | 747 | $('#btn_login').button().click(function() { 748 | var username = $('#txt_username').val(); 749 | var password = $('#txt_password').val(); 750 | //console.log(username, password); 751 | }); 752 | 753 | $('#radio_share_mode').buttonset().change(function(e) { 754 | var id = $('#radio_share_mode :radio:checked').attr('id'); 755 | switch(id) { 756 | case 'radio_share_mode_code': 757 | PythonIDE.shareMode = 'code'; 758 | break; 759 | case 'radio_share_mode_run': 760 | PythonIDE.shareMode = 'run'; 761 | break; 762 | } 763 | PythonIDE.showShare(); 764 | }); 765 | 766 | $('#radio_run_mode').buttonset().change(function(e) { 767 | var id = $('#radio_run_mode :radio:checked').attr('id'); 768 | switch(id) { 769 | case 'radio_run_mode_all': 770 | PythonIDE.runMode = "normal"; 771 | break; 772 | case 'radio_run_mode_single': 773 | PythonIDE.runMode = "step"; 774 | break; 775 | case 'radio_run_mode_anim': 776 | PythonIDE.runMode = "anim"; 777 | break; 778 | } 779 | }); 780 | 781 | $('#radio_code_style').buttonset().change(function(e) { 782 | var id = $('#radio_code_style :radio:checked').attr('id'); 783 | switch(id) { 784 | case 'radio_code_style_light': 785 | PythonIDE.editor.setOption("theme", "default"); 786 | $('body').css({'background-color': '#FFF'}); 787 | PythonIDE.setOption('code_style', 'light') 788 | break; 789 | case 'radio_code_style_dark': 790 | PythonIDE.editor.setOption("theme", "blackboard"); 791 | $('body').css({'background-color': '#000'}); 792 | PythonIDE.setOption('code_style', 'dark') 793 | break; 794 | case 'radio_code_style_dusk': 795 | PythonIDE.editor.setOption("theme", "cobalt"); 796 | $('body').css({'background-color': '#002240'}); 797 | PythonIDE.setOption('code_style', 'dusk') 798 | break; 799 | } 800 | }); 801 | $('#radio_code_style_' + PythonIDE.getOption('code_style', 'light')).prop('checked', true).change(); 802 | 803 | $('#radio_output_style').buttonset().change(function(e) { 804 | var id = $('#radio_output_style :radio:checked').attr('id'); 805 | switch(id) { 806 | case 'radio_output_style_light': 807 | $('#dlg').css({'background-color': '#FFF','color':'#000'}); 808 | PythonIDE.setOption('output_style', 'light') 809 | break; 810 | case 'radio_output_style_dark': 811 | $('#dlg').css({'background-color': '#222','color':'#CCC'}); 812 | PythonIDE.setOption('output_style', 'dark') 813 | break; 814 | case 'radio_output_style_dusk': 815 | $('#dlg').css({'background-color': '#002240','color':'#CCC'}); 816 | PythonIDE.setOption('output_style', 'dusk') 817 | break; 818 | } 819 | }); 820 | $('#radio_output_style_' + PythonIDE.getOption('output_style', 'dark')).prop('checked', true).change(); 821 | 822 | $('#userDetails').button().click(function() { 823 | $('#login').dialog("open"); 824 | }); 825 | 826 | 827 | 828 | 829 | if(localStorage.loadAction) { 830 | switch(localStorage.loadAction) { 831 | case 'showShare': 832 | PythonIDE.showShare(); 833 | break; 834 | case 'restoreCode': 835 | PythonIDE.editor.setValue(localStorage.lastRunCode); 836 | break; 837 | } 838 | delete localStorage.loadAction; 839 | PythonIDE.editor.setCursor(0); 840 | PythonIDE.editor.focus(); 841 | } 842 | 843 | (Sk.TurtleGraphics || (Sk.TurtleGraphics = {})).target = 'canvas'; 844 | 845 | Sk.inputfun = function(prompt) { 846 | //return window.prompt(prompt); 847 | var p = new Promise(function(resolve, reject) { 848 | if($('#raw_input_holder').length > 0) { 849 | return; 850 | } 851 | PythonIDE.python.output('
'); 852 | 853 | var btn = $('#raw_input_accept').button().click(function() { 854 | var val = $('#raw_input').val(); 855 | $('#raw_input_holder').remove(); 856 | PythonIDE.python.output(prompt + ' ' + val + "\n"); 857 | resolve(val); 858 | }); 859 | $('#raw_input').focus(); 860 | }); 861 | return p; 862 | } 863 | 864 | Sk.configure({ 865 | breakpoints:function(filename, line_number, offset, s) { 866 | //console.log(line_number, PythonIDE.runMode); 867 | if(PythonIDE.runMode == "anim") { 868 | if(PythonIDE.continueDebug) { 869 | PythonIDE.animTimeout = setTimeout(function() { 870 | PythonIDE.runCode("anim"); 871 | }, $( "#slider_step_anim_time" ).slider( "value" )); 872 | } 873 | } 874 | PythonIDE.editor.setCursor(line_number - 1); 875 | 876 | // check for errors in external libraries 877 | if(PythonIDE.unhandledError) { 878 | throw PythonIDE.unhandledError; 879 | } 880 | return true; 881 | }, 882 | debugging: true, 883 | output: PythonIDE.python.output, 884 | readFile: PythonIDE.readFile, 885 | writeFile: PythonIDE.writeFile, 886 | read: PythonIDE.python.builtinread}); 887 | 888 | // add in additional libraries. 889 | // not all of these are complete but they serve as an example of how you can code your own modules. 890 | Sk.externalLibraries = { 891 | // added as a farewell message to a school direct student 892 | schooldirect: { 893 | path: 'lib/skulpt/schooldirect/__init__.js' 894 | }, 895 | 896 | tkinter: { 897 | path: 'lib/skultp/tkinter/__init__.js' 898 | }, 899 | 900 | os: { 901 | path: 'lib/skulpt/os/__init__.js' 902 | }, 903 | speech: { 904 | path: 'lib/skulpt/speech/__init__.js', 905 | dependencies: ['lib/skulpt/speech/sam.js'] 906 | }, 907 | radio: { 908 | path: 'lib/skulpt/radio/__init__.js' 909 | }, 910 | 911 | 912 | // easy data visualisation functions unique to withcode.uk 913 | withcode: { 914 | path: 'lib/skulpt/withcode/__init__.js' 915 | }, 916 | 917 | // not quite complete implementation of sqlite3 918 | sqlite3: { 919 | path: 'lib/skulpt/sqlite3/__init__.js' 920 | }, 921 | 922 | // microbit simulator 923 | microbit: { 924 | path: 'lib/skulpt/microbit/__init__.js' 925 | }, 926 | 927 | // music module compatible with microbit music module 928 | music: { 929 | path: 'lib/skulpt/music/__init__.js' 930 | }, 931 | 932 | // anyone fancy implementing this?! Imagine the possibilities! 933 | py3d: { 934 | path: 'lib/skulpt/py3d/__init__.js', 935 | dependencies: ['lib/skulpt/py3d/three.js'], 936 | }, 937 | RPi: { 938 | path: 'lib/skulpt/rpi/__init__.js' 939 | }, 940 | "RPi.GPIO": { 941 | path: 'lib/skulpt/rpi/__init__.js' 942 | }, 943 | gpiozero: { 944 | path: 'lib/skulpt/rpi/gpiozero.js', 945 | dependencies: ['lib/skulpt/rpi/raphael.js'] 946 | }, 947 | }; 948 | 949 | // expand editor to fit height of the screen. 950 | $('.holder').css({height: window.innerHeight - 80}); 951 | 952 | $('#footer').css({bottom: 0}); 953 | 954 | $('.toolButton').hover(function(e) { 955 | // mouse over tool button 956 | PythonIDE.showHint($('#' + e.currentTarget.id).attr('alt')); 957 | }, function(e) { 958 | // mouse out tool button 959 | PythonIDE.showHint(PythonIDE.welcomeMessage); 960 | }).click(function(e) { 961 | // tool button click 962 | switch(e.currentTarget.id) { 963 | case 'btn_edit': 964 | window.open(window.location.href.replace('embed', 'python').replace('run', 'python')); 965 | break; 966 | 967 | case 'btn_show_recover': 968 | PythonIDE.recover(); 969 | break; 970 | 971 | case 'btn_stopRunning': 972 | localStorage.loadAction = "restoreCode"; 973 | window.location = window.location.href.replace('run/', 'python/'); 974 | break; 975 | 976 | case 'btn_tools': 977 | toolsVisible = !toolsVisible; 978 | if(toolsVisible) { 979 | $('.toolButton').addClass('visibleButton'); 980 | } else { 981 | $('.toolButton').removeClass('visibleButton'); 982 | } 983 | break; 984 | 985 | case 'btn_show_output': 986 | //$('#btn_group_console').toggleClass('hiddenButtonPanel'); 987 | $('#dlg').dialog("open"); 988 | break; 989 | 990 | case 'btn_show_settings': 991 | $('#settings').dialog("open"); 992 | break; 993 | 994 | case 'btn_show_share': 995 | localStorage.loadAction="showShare"; 996 | PythonIDE.save(); 997 | break; 998 | 999 | case 'btn_run': 1000 | PythonIDE.runCode(PythonIDE.runMode); 1001 | break; 1002 | } 1003 | }); 1004 | if(style == "run") { 1005 | 1006 | $('#editor').hide(); 1007 | var output = $('#output').detach(); 1008 | $('#holder').append(output); 1009 | $('#dlg').remove(); 1010 | PythonIDE.whenFinished = function() { 1011 | var link = window.location.href.replace('run/', 'python/'); 1012 | var html = '

{withcode.uk}

'; 1013 | PythonIDE.python.output(html + '

This python app was written using create.withcode.uk. Click here to edit the python code and create/share your own version or check out blog.withcode.uk for ideas, tips and resources

'); 1014 | $('#btn_run_again').button().click(function() {PythonIDE.runCode()}); 1015 | } 1016 | PythonIDE.runCode(); 1017 | } 1018 | } 1019 | } 1020 | -------------------------------------------------------------------------------- /lib/skulpt/microbit/compassarrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/lib/skulpt/microbit/compassarrow.png -------------------------------------------------------------------------------- /lib/skulpt/microbit/compassbk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/lib/skulpt/microbit/compassbk.jpg -------------------------------------------------------------------------------- /lib/skulpt/microbit/mb.css: -------------------------------------------------------------------------------- 1 | #mb_tabs { 2 | font-size:10pt; 3 | } 4 | #mb_pin_num { 5 | font-size: 0.8em; 6 | } 7 | 8 | #microbit { 9 | width: 300px; 10 | height: 236px; 11 | background-image: url('mb.png'); 12 | position: relative; 13 | transition: all 0.5s; 14 | } 15 | 16 | .mb_btn { 17 | width: 22px; 18 | height: 22px; 19 | border-radius: 11px; 20 | background-color: #333; 21 | transition: all 0.25s; 22 | position: absolute; 23 | opacity: 0.9; 24 | } 25 | 26 | .mb_led { 27 | width: 6px; 28 | height: 6px; 29 | transition: all 0.1s; 30 | position: absolute; 31 | } 32 | 33 | 34 | .mb_info { 35 | font-size: 8pt; 36 | } 37 | 38 | .mb_help_link { 39 | font-size: small; 40 | background-color: #FFF; 41 | border-radius: 10px; 42 | padding: 10px; 43 | color: #000; 44 | transition: all 0.5s; 45 | margin: 5px; 46 | border-style: solid; 47 | border-width: 1px; 48 | border-color: #000; 49 | display:inline-block; 50 | } 51 | 52 | .mb_help_link:hover { 53 | background-color: #F00; 54 | } 55 | .mb_led_brightness_0 { background-color: #000; } 56 | .mb_led_brightness_1 { background-color: #300; } 57 | .mb_led_brightness_2 { background-color: #500; } 58 | .mb_led_brightness_3 { background-color: #700; } 59 | .mb_led_brightness_4 { background-color: #900; } 60 | .mb_led_brightness_5 { background-color: #a00; } 61 | .mb_led_brightness_6 { background-color: #b00; } 62 | .mb_led_brightness_7 { background-color: #c00; } 63 | .mb_led_brightness_8 { background-color: #d00; } 64 | .mb_led_brightness_9 { background-color: #f00; } 65 | .mb_led_row_0 { top: 73px; } 66 | .mb_led_row_1 { top: 96px; } 67 | .mb_led_row_2 { top: 119px; } 68 | .mb_led_row_3 { top: 141px; } 69 | .mb_led_row_4 { top: 164px; } 70 | 71 | .mb_led_col_0 { left: 104px; } 72 | .mb_led_col_1 { left: 126px; } 73 | .mb_led_col_2 { left: 149px; } 74 | .mb_led_col_3 { left: 171px; } 75 | .mb_led_col_4 { left: 195px; } 76 | 77 | #mb_btn_A { 78 | left: 24px; 79 | top: 101px; 80 | } 81 | 82 | #mb_btn_B { 83 | left: 256px; 84 | top: 101px; 85 | } 86 | 87 | .mb_btn:hover { 88 | background-color: #F00; 89 | } 90 | 91 | .mb_slider { 92 | width: 100px; 93 | display: inline-block; 94 | margin: 0px 10px 0px 10px; 95 | } 96 | #mb_com_heading { 97 | width: 100px; 98 | height: 100px; 99 | line-height: 100px; 100 | text-align: center; 101 | vertical-align: center; 102 | background-image: url('compassbk.jpg'); 103 | position: relative; 104 | } 105 | #mb_com_value_heading { 106 | font-size: 2em; 107 | } 108 | #mb_compass_pointer { 109 | position: absolute; 110 | left: 0px; 111 | top: 0px; 112 | width: 100px; 113 | height: 100px; 114 | /*transition: all 0.5s;*/ 115 | background-image: url('compassarrow.png'); 116 | } 117 | .mb_slider_com { 118 | width: 100px; 119 | display: inline-block; 120 | margin: 0px 10px 0px 10px; 121 | } 122 | #mb_gesture_list { 123 | border-style: solid; 124 | border-width: 1px; 125 | border-color: #000; 126 | min-height: 1em; 127 | } 128 | .mb_current_gesture { 129 | color: #F00; 130 | margin; 1em; 131 | } 132 | .mb_active_gesture_down { 133 | transform: rotate(180deg); 134 | } 135 | .mb_active_gesture_left, .mb_active_gesture_right { 136 | -webkit-transform: rotateY(80deg);transform: rotateY(80deg); 137 | } 138 | .mb_active_gesture_facedown { 139 | -webkit-transform: rotateY(180deg);transform: rotateY(180deg); 140 | } 141 | .mb_active_gesture_freefall { 142 | transform: scale(0.2,0.2); 143 | } 144 | @keyframes shake { 145 | 0% {transform: scale(1,1);} 146 | 50% {transform: scale(0.8,0.8);} 147 | 100% {transform: scale(1,1);} 148 | } 149 | .mb_active_gesture_shake { 150 | animation-name: shake; 151 | animation-duration: 0.3s; 152 | animation-iteration-count: infinite; 153 | } 154 | .mb_active_gesture_3g { 155 | animation-name: shake; 156 | animation-duration: 0.5s; 157 | } 158 | .mb_active_gesture_6g { 159 | animation-name: shake; 160 | animation-duration: 0.3s; 161 | } 162 | .mb_active_gesture_8g { 163 | animation-name: shake; 164 | animation-duration: 0.1s; 165 | } -------------------------------------------------------------------------------- /lib/skulpt/microbit/mb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/lib/skulpt/microbit/mb.png -------------------------------------------------------------------------------- /lib/skulpt/music/__init__.js: -------------------------------------------------------------------------------- 1 | var $builtinmodule = function(name) { 2 | var mod = {}; 3 | mod._data = {}; 4 | mod._data.ticks = 4; 5 | mod._data.bpm = 120; 6 | mod._data.duration = 4; 7 | mod._data.octave = 4; 8 | 9 | var set_tempo = function(ticks, bpm) { 10 | if(ticks === undefined) 11 | ticks = new Sk.builtin.nmber(4); 12 | if(bpm === undefined) 13 | bpm = new Sk.builtin.nmber(120); 14 | 15 | mod._data.ticks = ticks.v; 16 | mod._data.bpm = bpm.v; 17 | } 18 | 19 | set_tempo.co_varnames = ['ticks', 'bpm']; 20 | set_tempo.$defaults = [Sk.builtin.nmber(4), Sk.builtin.nmber(120)]; 21 | set_tempo.co_numargs = 2; 22 | mod.set_tempo = new Sk.builtin.func(set_tempo); 23 | 24 | mod.get_tempo = new Sk.builtin.func(function() { 25 | return new Sk.builtin.tuple(Sk.ffi.remapToPy([mod._data.ticks, mod._data.bpm])); 26 | }); 27 | 28 | var play = function(music, pin, wait, loop){ 29 | if(mod._data.stop) { 30 | delete mod._data.stop; 31 | } 32 | if(wait === undefined) 33 | wait = new Sk.builtin.bool(true); 34 | if(loop === undefined) 35 | loop = new Sk.builtin.bool(false); 36 | 37 | var notes = Sk.ffi.remapToJs(music); 38 | 39 | return PythonIDE.runAsync(function(resolve, reject) { 40 | var i = 0; 41 | var timeout = 60000 / mod._data.bpm / mod._data.ticks; 42 | 43 | function playNextNote() { 44 | if(mod._data.stop) { 45 | delete mod._data.stop; 46 | return; 47 | } 48 | if(!mod._data.audioCtx) { 49 | mod._data.audioCtx = new (window.AudioContext || window.webkitAudioContext)(); 50 | } 51 | if(mod._data.osc) { 52 | mod._data.osc.stop(); 53 | delete mod._data.osc; 54 | } 55 | 56 | if(i >= notes.length){ 57 | if(loop.v) { 58 | i = 0; 59 | } else { 60 | if(wait.v) 61 | resolve(); 62 | return; 63 | } 64 | } 65 | var n = notes[i].toLowerCase(); 66 | var d = n.match(/([rcdefgab][b#]?)([0-9]?)(:([0-9]*))?/); 67 | n = {short: n, 68 | note: d[1], 69 | octave: Number.parseInt(d[2]), 70 | ticks: d[4]? Number.parseInt(d[4]) : 1 71 | } 72 | 73 | switch(n.note) { 74 | case 'c': 75 | n.f = 16.352; 76 | break; 77 | case 'c#': 78 | case 'db': 79 | n.f = 17.324; 80 | break; 81 | case 'd': 82 | n.f = 18.354; 83 | break; 84 | case 'd#': 85 | case 'eb': 86 | n.f = 19.445; 87 | break; 88 | case 'e': 89 | n.f = 20.602; 90 | break; 91 | case 'f': 92 | n.f = 21.827; 93 | break; 94 | case 'f#': 95 | case 'gb': 96 | n.f = 23.125; 97 | break; 98 | case 'g': 99 | n.f = 24.500; 100 | break; 101 | case 'g#': 102 | case 'ab': 103 | n.f = 25.957; 104 | break; 105 | case 'a': 106 | n.f = 27.500; 107 | break; 108 | case 'a#': 109 | case 'bb': 110 | n.f = 29.135; 111 | break; 112 | case 'b': 113 | n.f = 30.868; 114 | break; 115 | case 'r': 116 | n.f = 0; 117 | break; 118 | } 119 | if(!n.octave) { 120 | n.octave = mod._data.octave; 121 | } else { 122 | mod._data.octave = n.octave; 123 | } 124 | for(var o = 0; o < n.octave; o++) { 125 | n.f = n.f * 2; 126 | } 127 | n.f = Math.round(n.f); 128 | 129 | if(n.f > 0) { 130 | var osc = mod._data.audioCtx.createOscillator(); 131 | mod._data.osc = osc; 132 | osc.type = 'sine'; 133 | osc.frequency.value = n.f; 134 | osc.connect(mod._data.audioCtx.destination); 135 | osc.start(); 136 | } 137 | i++; 138 | 139 | if(i <= notes.length) { 140 | setTimeout(playNextNote, timeout * n.ticks); 141 | } 142 | } 143 | playNextNote(); 144 | if(!wait.v) { 145 | resolve(); 146 | } 147 | 148 | }); 149 | 150 | } 151 | play.co_varnames = ['music', 'pin', 'wait', 'loop']; 152 | play.$defaults = [undefined, undefined, Sk.builtin.bool(true), Sk.builtin.bool(false)]; 153 | play.co_numargs = 4; 154 | mod.play = new Sk.builtin.func(play); 155 | 156 | var pitch = function(frequency, len, pin, wait){ 157 | if(mod._data.stop) { 158 | delete mod._data.stop; 159 | } 160 | if(len === undefined) 161 | len = new Sk.builtin.nmber(-1); 162 | if(wait === undefined) 163 | wait = new Sk.builtin.bool(true); 164 | 165 | return PythonIDE.runAsync(function(resolve, reject) { 166 | if(!mod._data.audioCtx) { 167 | mod._data.audioCtx = new (window.AudioContext || window.webkitAudioContext)(); 168 | } 169 | if(mod._data.osc) { 170 | mod._data.osc.stop(); 171 | delete mod._data.osc; 172 | } 173 | var osc = mod._data.audioCtx.createOscillator(); 174 | mod._data.osc = osc; 175 | osc.type = 'sine'; 176 | osc.frequency.value = frequency.v; 177 | osc.connect(mod._data.audioCtx.destination); 178 | osc.start(); 179 | if(len.v > 0) { 180 | setTimeout(function() { 181 | osc.stop(); 182 | if(wait.v) { 183 | osc.stop(); 184 | resolve(); 185 | } 186 | }, len.v); 187 | } 188 | if(!wait.v) { 189 | resolve(); 190 | } 191 | }); 192 | 193 | } 194 | pitch.co_varnames = ['frequency', 'len', 'pin', 'wait']; 195 | pitch.$defaults = [undefined, Sk.builtin.nmber(-1), undefined, Sk.builtin.bool(false)]; 196 | pitch.co_numargs = 4; 197 | mod.pitch = new Sk.builtin.func(pitch); 198 | 199 | var stop = function(pin) { 200 | if(mod._data.audioCtx && mod._data.osc) { 201 | mod._data.osc.stop(); 202 | delete mod._data.osc; 203 | mod._data.stop = true; 204 | } 205 | } 206 | stop.co_varnames = ['pin']; 207 | stop.$defaults = [undefined]; 208 | stop.co_numargs = 1; 209 | mod.stop = new Sk.builtin.func(stop); 210 | 211 | 212 | mod.reset = new Sk.builtin.func(function() { 213 | mod._data.ticks = 4; 214 | mod._data.bpm = 120; 215 | mod._data.duration = 4; 216 | mod._data.octave = 4; 217 | }); 218 | 219 | mod.DADADADUM = Sk.ffi.remapToPy(["r4:2", "g", "g", "g", "eb:8", "r:2", "f", "f", "f", "d:8"]); 220 | 221 | mod.ENTERTAINER = Sk.ffi.remapToPy(["d4:1", "d#", "e", "c5:2", "e4:1", "c5:2", "e4:1", "c5:3", "c:1", "d", "d#", "e", "c", "d", "e:2", "b4:1", "d5:2", "c:4"]); 222 | 223 | mod.PRELUDE = Sk.ffi.remapToPy(["c4:1", "e", "g", "c5", "e", "g4", "c5", "e", "c4", "e", 224 | "g", "c5", "e", "g4", "c5", "e", "c4", "d", "g", "d5", "f", 225 | "g4", "d5", "f", "c4", "d", "g", "d5", "f", "g4", "d5", "f", 226 | "b3", "d4", "g", "d5", "f", "g4", "d5", "f", "b3", "d4", "g", 227 | "d5", "f", "g4", "d5", "f", "c4", "e", "g", "c5", "e", "g4", 228 | "c5", "e", "c4", "e", "g", "c5", "e", "g4", "c5", "e"]); 229 | 230 | mod.ODE = Sk.ffi.remapToPy(["e4", "e", "f", "g", "g", "f", "e", "d", "c", "c", "d", "e", 231 | "e:6", "d:2", "d:8", "e:4", "e", "f", "g", 232 | "g", "f", "e", "d", "c", "c", "d", "e", "d:6", 233 | "c:2", "c:8"]); 234 | 235 | mod.NYAN = Sk.ffi.remapToPy(["f#5:2", "g#", "c#:1", "d#:2", 236 | "b4:1", "d5:1", "c#", "b4:2", "b", 237 | "c#5", "d", "d:1", "c#", "b4:1", 238 | "c#5:1", "d#", "f#", "g#", "d#", 239 | "f#", "c#", "d", "b4", "c#5", "b4", 240 | "d#5:2", "f#", "g#:1", "d#", 241 | "f#", "c#", "d#", "b4", "d5", "d#", "d", 242 | "c#", "b4", "c#5", "d:2", "b4:1", "c#5", 243 | "d#", "f#", "c#", "d", "c#", "b4", 244 | "c#5:2", "b4", "c#5", "b4", "f#:1", 245 | "g#", "b:2", "f#:1", "g#", "b", 246 | "c#5", "d#", "b4", "e5", "d#", "e", "f#", 247 | "b4:2", "b", "f#:1", "g#", "b", "f#", 248 | "e5", "d#", "c#", "b4", "f#", "d#", "e", 249 | "f#", "b:2", "f#:1", "g#", "b:2", 250 | "f#:1", "g#", "b", "b", "c#5", "d#", 251 | "b4", "f#", "g#", "f#", "b:2", "b:1", 252 | "a#", "b", "f#", "g#", "b", "e5", "d#", "e", 253 | "f#", "b4:2", "c#5"]); 254 | 255 | mod.RINGTONE = Sk.ffi.remapToPy(["c4:1", "d", "e:2", "g", "d:1", "e", "f:2", 256 | "a", "e:1", "f", "g:2", "b", "c5:4"]); 257 | 258 | mod.FUNK = Sk.ffi.remapToPy(["c2:2", "c", "d#", "c:1", "f:2", "c:1", 259 | "f:2", "f#", "g", "c", "c", "g", "c:1", 260 | "f#:2", "c:1", "f#:2", "f", "d#"]); 261 | 262 | mod.BLUES = Sk.ffi.remapToPy(["c2:2", "e", "g", "a", "a#", "a", "g", "e", 263 | "c2:2", "e", "g", "a", "a#", "a", "g", "e", "f", "a", 264 | "c3", "d", "d#", "d", "c", "a2", "c2:2", "e", "g", 265 | "a", "a#", "a", "g", "e", "g", "b", "d3", "f", "f2", "a", 266 | "c3", "d#", "c2:2", "e", "g", "e", "g", "f", "e", 267 | "d"]); 268 | 269 | mod.BIRTHDAY = Sk.ffi.remapToPy(["c4:3", "c:1", "d:4", "c:4", "f:4", 270 | "e:8", "c:3", "c:1", "d:4", "c:4", 271 | "g:4", "f:8", "c:3", "c:1", "c5:4", "a4:4", 272 | "f:4", "e:4", "d:8", "a#:3", "a#:1", "a:4", 273 | "f:4", "g:4", "f:8"]); 274 | 275 | mod.WEDDING = Sk.ffi.remapToPy(["c4:4", "f:3", "f:1", "f:8", "c:4", 276 | "g:3", "e:1", "f:8", "c:4", "f:3", 277 | "a:1", "c5:4", "a4:3", "f:1", "f:4", 278 | "e:3", "f:1", "g:8"]); 279 | 280 | mod.FUNERAL = Sk.ffi.remapToPy(["c3:4", "c:3", "c:1", "c:4", 281 | "d#:3", "d:1", "d:3", "c:1", 282 | "c:3", "b2:1", "c3:4"]); 283 | 284 | mod.PUNCHLINE = Sk.ffi.remapToPy(["c4:3", "g3:1", "f#", "g", "g#:3", "g", 285 | "r", "b", "c4"]); 286 | 287 | mod.PYTHON = Sk.ffi.remapToPy(["d5:1", "b4", "r", "b", "b", "a#", "b", "g5", "r", 288 | "d", "d", "r", "b4", "c5", "r", "c", "c", "r", "d", 289 | "e:5", "c:1", "a4", "r", "a", "a", "g#", "a", 290 | "f#5", "r", "e", "e", "r", "c", "b4", "r", "b", "b", "r", 291 | "c5", "d:5", "d:1", "b4", "r", "b", "b", "a#", 292 | "b", "b5", "r", "g", "g", "r", "d", "c#", "r", "a", "a", 293 | "r", "a", "a:5", "g:1", "f#:2", "a:1", 294 | "a", "g#", "a", "e:2", "a:1", "a", "g#", 295 | "a", "d", "r", "c#", "d", "r", "c#", "d:2", 296 | "r:3"]); 297 | 298 | mod.BADDY = Sk.ffi.remapToPy(["c3:3", "r", "d:2", "d#", "r", "c", "r", "f#:8"]); 299 | 300 | mod.CHASE = Sk.ffi.remapToPy(["a4:1", "b", "c5", "b4", "a:2", "r", "a:1", "b", "c5", "b4", "a:2", "r", "a:2", "e5", "d#", "e", "f", "e", "d#", "e", "b4:1", "c5", "d", "c", "b4:2", "r", "b:1", "c5", "d", "c", "b4:2", "r", "b:2", "e5", "d#", "e", "f", "e", "d#", "e"]); 301 | 302 | mod.BA_DING = Sk.ffi.remapToPy(["b5:1", "e6:3"]); 303 | 304 | mod.WAWAWAWAA = Sk.ffi.remapToPy(["e3:3", "r:1", "d#:3", "r:1", "d:4", "r:1", "c#:8"]); 305 | 306 | mod.JUMP_UP = Sk.ffi.remapToPy(["c5:1", "d", "e", "f", "g"]); 307 | 308 | mod.JUMP_DOWN = Sk.ffi.remapToPy(["g5:1", "f", "e", "d", "c"]); 309 | 310 | mod.POWER_UP = Sk.ffi.remapToPy(["g4:1", "c5", "e", "g:2", "e:1", "g:3"]); 311 | 312 | mod.POWER_DOWN = Sk.ffi.remapToPy(["g5:1", "d#", "c", "g4:2", "b:1", "c5:3"]); 313 | return mod; 314 | }; -------------------------------------------------------------------------------- /lib/skulpt/os/__init__.js: -------------------------------------------------------------------------------- 1 | var $builtinmodule = function (name) { 2 | var mod = { 3 | 4 | }; 5 | 6 | mod.listdir = new Sk.builtin.func(function() { 7 | var files = []; 8 | for(var key in PythonIDE.files) { 9 | files.push(key); 10 | } 11 | return Sk.ffi.remapToPy(files); 12 | }); 13 | 14 | mod.remove = new Sk.builtin.func(function(filename) { 15 | if(PythonIDE.files[filename.v] === undefined) { 16 | throw new Sk.builtin.Exception("OS Error"); 17 | } 18 | delete PythonIDE.files[filename.v]; 19 | PythonIDE.updateFileTabs(); 20 | }); 21 | 22 | mod.size = new Sk.builtin.func(function() { 23 | if(PythonIDE.files[filename.v] === undefined) { 24 | throw new Sk.builtin.Exception("OS Error"); 25 | } 26 | return Sk.builtin.nmber(PythonIDE.files[filename.v].length); 27 | }); 28 | 29 | mod.uname = new Sk.builtin.func(function() { 30 | throw new Sk.builtin.Exception("Not implemented yet"); 31 | }); 32 | return mod; 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /lib/skulpt/py3d/__init__.js: -------------------------------------------------------------------------------- 1 | var $builtinmodule = function(name) { 2 | var mod = {}; 3 | 4 | var scene, camera, renderer; 5 | var geometry, material, mesh; 6 | 7 | init(); 8 | animate(); 9 | 10 | function init() { 11 | var c = document.getElementById('consoleOut'); 12 | var width = $('#consoleOut').width() - 50; 13 | scene = new THREE.Scene(); 14 | 15 | camera = new THREE.PerspectiveCamera( 75, 16/9, 1, 10000 ); 16 | camera.position.z = 1000; 17 | 18 | geometry = new THREE.BoxGeometry( 200, 200, 200 ); 19 | material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: true } ); 20 | 21 | mesh = new THREE.Mesh( geometry, material ); 22 | scene.add( mesh ); 23 | 24 | renderer = new THREE.WebGLRenderer(); 25 | renderer.setSize( width, width * 9 / 16 ); 26 | 27 | 28 | var c = document.getElementById('consoleOut'); 29 | c.appendChild( renderer.domElement ); 30 | 31 | 32 | } 33 | 34 | function animate() { 35 | 36 | requestAnimationFrame( animate ); 37 | 38 | mesh.rotation.x += 0.01; 39 | mesh.rotation.y += 0.02; 40 | 41 | renderer.render( scene, camera ); 42 | 43 | } 44 | 45 | return mod; 46 | }; -------------------------------------------------------------------------------- /lib/skulpt/radio/__init__.js: -------------------------------------------------------------------------------- 1 | var $builtinmodule = function (name) { 2 | var mod = { 3 | data: { 4 | power: false 5 | } 6 | }; 7 | 8 | radio = mod.data; 9 | 10 | mod.RATE_250KBIT = Sk.builtin.nmber(250); 11 | mod.RATE_1MBIT = Sk.builtin.nmber(1000); 12 | mod.RATE_2MBIT = Sk.builtin.nmber(2000); 13 | 14 | mod.on = new Sk.builtin.func(function() { 15 | mod.data.power = true; 16 | }); 17 | 18 | mod.off = new Sk.builtin.func(function() { 19 | mod.data.power = false; 20 | }); 21 | 22 | var config = function(length, queue, channel, power, address, group, data_rate) { 23 | if(length === undefined) 24 | length = Sk.builtin.nmber(32); 25 | if(queue === undefined) 26 | queue = Sk.builtin.nmber(3); 27 | if(channel === undefined) 28 | channel = Sk.builtin.nmber(7); 29 | if(power === undefined) 30 | power = Sk.builtin.nmber(0); 31 | if(address === undefined) 32 | address = Sk.builtin.str("0x75626974"); 33 | if(group === undefined) 34 | group = Sk.builtin.nmber(0); 35 | if(data_rate === undefined) 36 | data_rate = Sk.builtin.nmber(mod.RATE_1MBIT); 37 | 38 | mod.data.length = length.v; 39 | mod.data.queue = queue.v; 40 | mod.data.channel = channel.v; 41 | mod.data.power = power.v; 42 | mod.data.address = address.v; 43 | mod.data.group = group.v; 44 | mod.data.data_rate = data_rate.v; 45 | mod.data.buffer = []; 46 | delete mod.data.fn_send; 47 | delete mod.data.fn_receive; 48 | }; 49 | config(); 50 | 51 | config.co_varnames = ['length', 'queue', 'channel', 'power', 'address', 'group', 'data_rate']; 52 | config.$defaults = [Sk.builtin.nmber(32), Sk.builtin.nmber(3), Sk.builtin.nmber(7), Sk.builtin.nmber(0), Sk.builtin.str("0x75626974"), Sk.builtin.nmber(0), Sk.builtin.nmber(mod.RATE_1MBIT)]; 53 | config.co_numargs = 7; 54 | mod.config = new Sk.builtin.func(config); 55 | 56 | mod.reset = new Sk.builtin.func(function() { 57 | config(); 58 | }); 59 | 60 | mod.send_bytes = new Sk.builtin.func(function(message) { 61 | if(!mod.data.power) { 62 | throw new Exception("Radio is powered off"); 63 | } 64 | if(mod.data.fn_send) { 65 | mod.data.fn_send(Sk.ffi.remapToJs(message)); 66 | } 67 | }); 68 | 69 | mod.receive_bytes = new Sk.builtin.func(function() { 70 | if(!mod.data.power) { 71 | throw new Sk.builtin.Exception("Radio is powered off"); 72 | } 73 | if(mod.data.buffer.length > 0) { 74 | var data = mod.data.buffer[0]; 75 | mod.data.buffer = mod.data.slice(1); 76 | var bytes = []; 77 | for(var i = 0; i < data.length; i++) { 78 | bytes.push(data.charCodeAt(i)); 79 | } 80 | return Sk.ffi.remapToPy(bytes); 81 | } 82 | return new Sk.builtin.none(); 83 | }); 84 | 85 | mod.receive_bytes_into = new Sk.builtin.func(function(buffer) { 86 | if(!mod.data.power) { 87 | throw new Sk.builtin.Exception("Radio is powered off"); 88 | } 89 | throw new Sk.builtin.Exception("Not implemented yet"); 90 | return new Sk.builtin.none(); 91 | }); 92 | 93 | mod.send = new Sk.builtin.func(function(message) { 94 | if(!mod.data.power) { 95 | throw new Sk.builtin.Exception("Radio is powered off"); 96 | } 97 | if(mod.data.fn_send) { 98 | mod.data.fn_send("\x00\x01\x00" + message.v); 99 | } 100 | }); 101 | 102 | mod.receive = new Sk.builtin.func(function() { 103 | if(!mod.data.power) { 104 | throw new Sk.builtin.Exception("Radio is powered off"); 105 | } 106 | if(mod.data.buffer.length > 0) { 107 | var data = mod.data.buffer[0]; 108 | mod.data.buffer = mod.data.buffer.slice(1); 109 | return Sk.ffi.remapToPy(data.slice(3)); 110 | } 111 | return new Sk.builtin.str("None"); 112 | }); 113 | 114 | 115 | 116 | return mod; 117 | 118 | }; 119 | -------------------------------------------------------------------------------- /lib/skulpt/rpi/.giosaveVsQYx8: -------------------------------------------------------------------------------- 1 | var GPIO = function(name) { 2 | var mod = {}; 3 | console.log("Loaded GPIO module"); 4 | 5 | /*mod.BOARD = Sk.builtin.nmber(10); 6 | mod.MODE_UNKNOWN = Sk.builtin.nmber(-1); 7 | mod.BCM = Sk.builtin.nmber(11); 8 | mod.SERIAL = Sk.builtin.nmber(40); 9 | mod.SPI = Sk.builtin.nmber(41); 10 | mod.I2C = Sk.builtin.nmber(42); 11 | mod.BOARD = Sk.builtin.nmber(10); 12 | mod.PWM = Sk.builtin.nmber(43); 13 | 14 | mod.setmode = Sk.builtin.func(function(self, mode) { 15 | console.log("Not implemented yet"); 16 | }); 17 | 18 | mod.getmode = Sk.builtin.func(function(self, mode) { 19 | console.log("Not implemented yet"); 20 | });*/ 21 | return mod; 22 | } 23 | 24 | var RPi = function(name) { 25 | var mod = {}; 26 | mod.GPIO = new Sk.builtin.module(); 27 | mod.GPIO.$d = new GPIO("RPi.GPIO"); 28 | return mod; 29 | } 30 | 31 | var $builtinmodule = function(name) { 32 | var mod; 33 | switch(Sk.ffi.remapToJs(name) { 34 | case 'RPi': 35 | mod = new RPi(name); 36 | break; 37 | case 'RPi.GPIO': 38 | mod = new GPIO(name); 39 | break; 40 | } 41 | console.log(name, mod); 42 | return mod; 43 | }; 44 | -------------------------------------------------------------------------------- /lib/skulpt/rpi/__init__.js: -------------------------------------------------------------------------------- 1 | var GPIO = function(name) { 2 | var mod = {}; 3 | 4 | mod.BOARD = Sk.builtin.int_(10); 5 | mod.MODE_UNKNOWN = Sk.builtin.int_(-1); 6 | mod.BCM = Sk.builtin.int_(11); 7 | mod.SERIAL = Sk.builtin.int_(40); 8 | mod.SPI = Sk.builtin.int_(41); 9 | mod.I2C = Sk.builtin.int_(42); 10 | mod.BOARD = Sk.builtin.int_(10); 11 | mod.PWM = Sk.builtin.int_(43); 12 | mod.IN = Sk.builtin.int_(1); 13 | mod.OUT = Sk.builtin.int_(0); 14 | mod.HIGH = Sk.builtin.int_(1); 15 | mod.LOW = Sk.builtin.int_(0); 16 | mod.PUD_OFF = Sk.builtin.int_(0); 17 | mod.PUD_DOWN = Sk.builtin.int_(1); 18 | mod.PUD_UP = Sk.builtin.int_(2); 19 | 20 | var internals = { 21 | mode: mod.BOARD, 22 | warnings: Sk.builtin.bool(true), 23 | pins: [], 24 | pinCount: 26, 25 | THRESHOLD: 1.25 26 | }; 27 | 28 | function pin(physical, name, type) { 29 | var p = { 30 | physical: physical, 31 | name: name, 32 | type: type, 33 | voltage: 0.0 34 | } 35 | 36 | switch(type) { 37 | case "power": 38 | p.voltage = name == "Ground"?0.0:parseFloat(name.split(" ")[0].replace("v", ".")); 39 | break; 40 | case "io": 41 | p.bcm = parseInt(name.split(" ")[1]); 42 | p.direction = "input"; 43 | break; 44 | } 45 | 46 | return p; 47 | } 48 | 49 | function addPin(pin) { 50 | internals.pins[pin.physical] = pin; 51 | } 52 | 53 | function setupPins() { 54 | var connections = PythonIDE.files['RPi.txt']; 55 | 56 | var model = 'B+'; 57 | 58 | if(connections) { 59 | var lines = connections.split("\n"); 60 | for(var i = 0; i < lines.length; i++) { 61 | if(lines[i].indexOf('model') > -1) { 62 | var parts = lines[i].split(/\s+/); 63 | 64 | if(parts.length > 1) { 65 | model = parts[1]; 66 | } 67 | } 68 | } 69 | 70 | } 71 | 72 | // determin RPi model 73 | switch(model) { 74 | case 'A': 75 | case 'B': 76 | internals.pinCount = 26; 77 | addPin(pin(1, "3v3 Power", "power")); 78 | addPin(pin(2, "5v Power", "power")); 79 | addPin(pin(3, "BCM 2", "io")); 80 | addPin(pin(4, "5v Power", "power")); 81 | addPin(pin(5, "BCM 3", "io")); 82 | addPin(pin(6, "Ground", "power")); 83 | addPin(pin(7, "BCM 4", "io")); 84 | addPin(pin(8, "BCM 14", "io")); 85 | addPin(pin(9, "Ground", "power")); 86 | addPin(pin(10, "BCM 15", "io")); 87 | 88 | addPin(pin(11, "BCM 17", "io")); 89 | addPin(pin(12, "BCM 18", "io")); 90 | addPin(pin(13, "BCM 27", "io")); 91 | addPin(pin(14, "Ground", "power")); 92 | addPin(pin(15, "BCM 22", "io")); 93 | addPin(pin(16, "BCM 23", "io")); 94 | addPin(pin(17, "3v3 Power", "power")); 95 | addPin(pin(18, "BCM 24", "io")); 96 | addPin(pin(19, "BCM 10", "io")); 97 | addPin(pin(20, "Ground", "power")); 98 | 99 | addPin(pin(21, "BCM 9", "io")); 100 | addPin(pin(22, "BCM 25", "io")); 101 | addPin(pin(23, "BCM 11", "io")); 102 | addPin(pin(24, "BCM 8", "io")); 103 | addPin(pin(25, "Ground", "power")); 104 | addPin(pin(26, "BCM 7", "io")); 105 | break; 106 | case 'A+': 107 | case 'B+': 108 | internals.pinCount = 40; 109 | addPin(pin(1, "3v3 Power", "power")); 110 | addPin(pin(2, "5v Power", "power")); 111 | addPin(pin(3, "BCM 2", "io")); 112 | addPin(pin(4, "5v Power", "power")); 113 | addPin(pin(5, "BCM 3", "io")); 114 | addPin(pin(6, "Ground", "power")); 115 | addPin(pin(7, "BCM 4", "io")); 116 | addPin(pin(8, "BCM 14", "io")); 117 | addPin(pin(9, "Ground", "power")); 118 | addPin(pin(10, "BCM 15", "io")); 119 | 120 | addPin(pin(11, "BCM 17", "io")); 121 | addPin(pin(12, "BCM 18", "io")); 122 | addPin(pin(13, "BCM 27", "io")); 123 | addPin(pin(14, "Ground", "power")); 124 | addPin(pin(15, "BCM 22", "io")); 125 | addPin(pin(16, "BCM 23", "io")); 126 | addPin(pin(17, "3v3 Power", "power")); 127 | addPin(pin(18, "BCM 24", "io")); 128 | addPin(pin(19, "BCM 10", "io")); 129 | addPin(pin(20, "Ground", "power")); 130 | 131 | addPin(pin(21, "BCM 9", "io")); 132 | addPin(pin(22, "BCM 25", "io")); 133 | addPin(pin(23, "BCM 11", "io")); 134 | addPin(pin(24, "BCM 8", "io")); 135 | addPin(pin(25, "Ground", "power")); 136 | addPin(pin(26, "BCM 7", "io")); 137 | addPin(pin(27, "BCM 0", "io")); 138 | addPin(pin(28, "BCM 1", "io")); 139 | addPin(pin(29, "BCM 5", "io")); 140 | addPin(pin(30, "Ground", "power")); 141 | 142 | addPin(pin(31, "BCM 6", "io")); 143 | addPin(pin(32, "BCM 12", "io")); 144 | addPin(pin(33, "BCM 13", "io")); 145 | addPin(pin(34, "Ground", "power")); 146 | addPin(pin(35, "BCM 19", "io")); 147 | addPin(pin(36, "BCM 16", "io")); 148 | addPin(pin(37, "BCM 26", "io")); 149 | addPin(pin(38, "BCM 20", "io")); 150 | addPin(pin(39, "Ground", "power")); 151 | addPin(pin(40, "BCM 21", "io")); 152 | 153 | break; 154 | default: 155 | throw new Sk.builtin.Exception("Unknown RPi model number in RPi.txt (Supported models are currently A, B, A+, B+)"); 156 | } 157 | } 158 | 159 | setupPins(); 160 | 161 | 162 | 163 | function getPin(pinNumber) { 164 | var pin; 165 | if(internals.mode.v == 10) { // board numbers 166 | pin = pinNumber; 167 | } else { // bcm numbers 168 | for(var i = 1; i <=internals.pinCount; i++) { 169 | if(internals.pins[i].bcm && internals.pins[i].bcm == pinNumber) { 170 | pin = internals.pins[i].physical; 171 | break; 172 | } 173 | } 174 | } 175 | return internals.pins[pin]; 176 | } 177 | 178 | 179 | 180 | 181 | mod.input = new Sk.builtin.func(function(channel) { 182 | var pin = getPin(channel.v); 183 | if(pin.type != "io") { 184 | throw new Sk.builtin.Exception("Cannot read input from non io pin " + channel.v + " (" + pin.name + ")"); 185 | } 186 | if(pin.direction != "input") { 187 | throw new Sk.builtin.Exception("Cannot read input from output pin " + channel.v + " (" + pin.name + ")"); 188 | } 189 | return pin.voltage > internals.THRESHOLD? mod.HIGH: mod.LOW; 190 | }); 191 | 192 | function output(channel, state){ 193 | var pin = getPin(channel); 194 | if(pin.type != "io") { 195 | throw new Sk.builtin.Exception("Cannot set output for non io pin " + channel + " (" + pin.name + ")"); 196 | } 197 | if(pin.direction != "output") { 198 | throw new Sk.builtin.Exception("Cannot set output for input pin " + channel + " (" + pin.name + ")"); 199 | } 200 | pin.voltage = state == 1? 3.3 : 0.0; 201 | updateIOs(); 202 | } 203 | 204 | mod.output = new Sk.builtin.func(function(channel, state) { 205 | switch(channel.__proto__.tp$name) { 206 | case 'int': 207 | case 'number': 208 | output(channel.v, state.v); 209 | break; 210 | case 'list': 211 | case 'tuple': 212 | var pins = Sk.ffi.remapToJs(channel); 213 | for(var i = 0; i < pins.length; i++) { 214 | output(pins[i], state.v); 215 | } 216 | 217 | break; 218 | 219 | default: 220 | throw new Sk.builtin.Exception("Don't know how to handle a channel type of " + channel.__proto__.tp$name); 221 | break; 222 | } 223 | }); 224 | 225 | mod.setmode = new Sk.builtin.func(function(mode) { 226 | internals.mode = mode; 227 | }); 228 | 229 | mod.getmode = new Sk.builtin.func(function() { 230 | return internals.mode; 231 | }); 232 | 233 | mod.setwarnings = new Sk.builtin.func(function(warnings) { 234 | internals.warnings = warnings; 235 | }); 236 | 237 | mod.PWM = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 238 | $loc.__init__ = new Sk.builtin.func(function(self, pin, frequency){ 239 | self.pinNumber = pin.v; 240 | self.f = frequency.v; 241 | self.period = 1000.0 / self.f; 242 | 243 | self.pin = getPin(self.pinNumber); 244 | if(self.pin.type != "io") { 245 | throw new Sk.builtin.Exception("Software PWM only works on IO pins"); 246 | } 247 | 248 | if(self.pin.direction != "output") { 249 | throw new Sk.builtin.Exception("You must set to an output before using PWM"); 250 | } 251 | }); 252 | 253 | $loc.start = new Sk.builtin.func(function(self, dutyCycle) { 254 | if(!dutyCycle) 255 | throw new Sk.builtin.Exception("You must specify the duty cycle when starting PWM"); 256 | self.dutyCycle = dutyCycle.v; 257 | 258 | if(dutyCycle < 0 || dutyCycle > 100) { 259 | throw new Sk.builtin.Exception("Duty cycle must be between 0 and 100"); 260 | } 261 | if(typeof PWMNextTimeout !== 'undefined') 262 | clearTimeout(PWMNextTimeout); 263 | 264 | var led = $('#RPi_pin_' + self.pin.physical + ' .out_RPi'); 265 | 266 | var high = function() { 267 | PWMNextTimeout = setTimeout(function() { 268 | self.pin.voltage = 0; 269 | //updateIOs(); 270 | led.removeClass('out_RPi_high').addClass('out_RPi_low'); 271 | low(); 272 | }, self.period * self.dutyCycle / 100) 273 | }; 274 | 275 | var low = function() { 276 | PWMNextTimeout = setTimeout(function() { 277 | self.pin.voltage = 3.3; 278 | //updateIOs(); 279 | led.removeClass('out_RPi_low').addClass('out_RPi_high'); 280 | high(); 281 | }, self.period * (100 - self.dutyCycle) / 100) 282 | }; 283 | 284 | high(); 285 | 286 | }); 287 | 288 | $loc.stop = new Sk.builtin.func(function(self) { 289 | if(PWMNextTimeout) 290 | clearTimeout(PWMNextTimeout); 291 | }); 292 | 293 | $loc.ChangeDutyCycle = new Sk.builtin.func(function(self, dutyCycle) { 294 | self.dutyCycle = dutyCycle.v; 295 | }); 296 | 297 | $loc.ChangeFrequence = new Sk.builtin.func(function(self, frequency) { 298 | self.f = frequency.v; 299 | self.period = 1000.0 / self.f; 300 | }); 301 | 302 | 303 | 304 | }, "RPi.GPIO.PWM", []); 305 | 306 | 307 | var setup = function(channel, direction, initial, pull_up_down) { 308 | function changePin(pinNumber) { 309 | var pin = getPin(pinNumber); 310 | if(pin.type == "io") { 311 | 312 | 313 | if(direction.v == mod.OUT.v) { 314 | pin.direction = "output"; 315 | if(initial == undefined) { 316 | initial = mod.LOW; 317 | } 318 | pin = initial.v == mod.LOW.v? 0.0 : 3.3; 319 | } else { 320 | pin.direction = "input"; 321 | if(pull_up_down && pull_up_down.v == mod.PUD_UP.v) { 322 | pin.voltage = 3.3; 323 | } else { 324 | pin.voltage = 0; 325 | } 326 | 327 | } 328 | } else { 329 | throw new Sk.builtin.Exception("You cannot set pin " + pinNumber + " (" + pin.name + ") to be an input or output"); 330 | } 331 | updateIOs(); 332 | } 333 | 334 | switch(channel.__proto__.tp$name) { 335 | case 'number': 336 | case 'int': 337 | changePin(channel.v); 338 | break; 339 | case 'list': 340 | case 'tuple': 341 | var pins = Sk.ffi.remapToJs(channel); 342 | for(var i = 0; i < pins.length; i++) { 343 | changePin(pins[i]); 344 | } 345 | 346 | 347 | break; 348 | 349 | default: 350 | throw new Sk.builtin.Exception("Don't know how to handle a channel type of " + channel.__proto__.tp$name); 351 | break; 352 | } 353 | } 354 | 355 | setup.co_varnames = ["channel", "direction", "initial", "pull_up_down"]; 356 | setup.$defauls = [Sk.builtin.none, Sk.builtin.none, mod.LOW, Sk.builtin.none]; 357 | setup.co_numargs = 4; 358 | 359 | mod.setup = new Sk.builtin.func(setup); 360 | 361 | var cleanup = function(channel) { 362 | internals.mode = mod.BOARD; 363 | 364 | if(channel == undefined) { 365 | for(var i = 1; i <= internals.pinCount; i++) { 366 | var pin = internals.pins[i]; 367 | if(pin.type == "io") { 368 | setup(Sk.ffi.remapToPy(i), mod.IN); 369 | } 370 | } 371 | } else { 372 | switch(channel.__proto__.tp$name) { 373 | case 'number': 374 | case 'int': 375 | setup(channel, mod.IN); 376 | break; 377 | case 'list': 378 | case 'tuple': 379 | var pins = Sk.ffi.remapToJs(channel); 380 | for(var i = 0; i < pins.length; i++) { 381 | setup(Sk.ffi.remapToPy(pins[i]), mod.IN); 382 | } 383 | break; 384 | } 385 | 386 | } 387 | updateIOs(); 388 | 389 | }; 390 | 391 | cleanup.co_varnames = ["channel"]; 392 | cleanup.$defauls = [Sk.builtin.none]; 393 | cleanup.co_numargs = 1; 394 | mod.cleanup = new Sk.builtin.func(cleanup); 395 | 396 | mod.RPI_INFO = Sk.ffi.remapToPy({'P1_REVISION': 3, 'RAM': '1024M', 'REVISION': 'a01041', 'TYPE': 'Pi2 Model B', 'PROCESSOR': 'BCM2836', 'MANUFACTURER': 'Sony'}); 397 | mod.VERSION = Sk.builtin.str('0.5.3a'); 398 | 399 | var html = '

RPi GPIO connectors:

'; 400 | for(var i = 2; i <= internals.pinCount; i += 2) { 401 | html += ''; 402 | } 403 | html += ''; 404 | for(var i = 1; i <= internals.pinCount; i += 2) { 405 | html += ''; 406 | } 407 | html += '
' + i + '
' + internals.pins[i].name + '
' + i + '
' + internals.pins[i].name + '
'; 408 | $('#RPi').remove(); 409 | PythonIDE.python.output(html, true); 410 | 411 | function updateIOs() { 412 | console.log("update"); 413 | $('.chk_RPi').unbind(); 414 | for(var i = 1; i <= internals.pinCount; i++) { 415 | var pin = internals.pins[i]; 416 | if(pin.type =="io") { 417 | switch(pin.direction) { 418 | case "input": 419 | $('#RPi_pin_' + i).html(' internals.THRESHOLD? 'checked ' : '') + 'id="chk_RPi_' + i + '">'); 420 | break; 421 | case "output": 422 | $('#RPi_pin_' + i).html(' '); 423 | break; 424 | } 425 | 426 | } 427 | } 428 | $('.chk_RPi').change(function(e) { 429 | var id = e.currentTarget.id; 430 | var state = document.getElementById(id).checked; 431 | var pinNumber = id.split('_')[2]; 432 | var pin = internals.pins[pinNumber]; 433 | pin.voltage = state? 3.3 : 0.0; 434 | }); 435 | } 436 | updateIOs(); 437 | 438 | return mod; 439 | } 440 | 441 | var RPi = function(name) { 442 | var mod = {}; 443 | mod.GPIO = new Sk.builtin.module(); 444 | mod.GPIO.$d = new GPIO("RPi.GPIO"); 445 | return mod; 446 | } 447 | 448 | var $builtinmodule = function(name) { 449 | var mod; 450 | switch(Sk.ffi.remapToJs(name.__name__)) { 451 | case 'RPi': 452 | mod = new RPi(name); 453 | break; 454 | case 'RPi.GPIO': 455 | mod = new GPIO(name); 456 | break; 457 | } 458 | return mod; 459 | }; 460 | -------------------------------------------------------------------------------- /lib/skulpt/rpi/components/cog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/lib/skulpt/rpi/components/cog.png -------------------------------------------------------------------------------- /lib/skulpt/rpi/components/led.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/lib/skulpt/rpi/components/led.png -------------------------------------------------------------------------------- /lib/skulpt/rpi/components/resistor330.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/lib/skulpt/rpi/components/resistor330.png -------------------------------------------------------------------------------- /lib/skulpt/rpi/gpiozero.js: -------------------------------------------------------------------------------- 1 | var $builtinmodule = function(name) { 2 | var mod = {}; 3 | var colors = [ 4 | "#3366CC", 5 | "#DC3912", 6 | "#FF9900", 7 | "#109618", 8 | "#990099", 9 | "#3B3EAC", 10 | "#0099C6", 11 | "#DD4477", 12 | "#66AA00", 13 | "#B82E2E", 14 | "#316395", 15 | "#994499", 16 | "#22AA99", 17 | "#AAAA11", 18 | "#6633CC", 19 | "#E67300", 20 | "#8B0707", 21 | "#329262", 22 | "#5574A6", 23 | "#3B3EAC"]; 24 | var wires = 0; 25 | var pinNames = ["", "3v3 Power", 26 | "5v Power", 27 | "BCM 2", 28 | "5v Power", 29 | "BCM 3", 30 | "Ground", 31 | "BCM 4", 32 | "BCM 14", 33 | "Ground", 34 | "BCM 15", 35 | "BCM 17", 36 | "BCM 18", 37 | "BCM 27", 38 | "Ground", 39 | "BCM 22", 40 | "BCM 23", 41 | "3v3 Power", 42 | "BCM 24", 43 | "BCM 10", 44 | "Ground", 45 | "BCM 9", 46 | "BCM 25", 47 | "BCM 11", 48 | "BCM 8", 49 | "Ground", 50 | "BCM 7", 51 | "BCM 0", 52 | "BCM 1", 53 | "BCM 5", 54 | "Ground", 55 | "BCM 6", 56 | "BCM 12", 57 | "BCM 13", 58 | "Ground", 59 | "BCM 19", 60 | "BCM 16", 61 | "BCM 26", 62 | "BCM 20", 63 | "Ground", 64 | "BCM 21"]; 65 | 66 | function getPin(name) { 67 | var pin = { 68 | index: 0, 69 | name: name, 70 | x: 0, 71 | y: 0 72 | 73 | }; 74 | for(var i = 0; i < pinNames.length; i++) { 75 | if(name == pinNames[i]) { 76 | pin.index = i; 77 | pin.physicalNumber = i - 1; 78 | pin.x = 17 + (pin.physicalNumber % 2) * 10; 79 | pin.y = 37 + Math.floor(pin.physicalNumber / 2) * 10; 80 | break; 81 | } 82 | } 83 | return pin; 84 | } 85 | 86 | var html = '
Simulated gpiozero support is under development. Let me know if there\'s a feature you\'d really like to see implemented. The gpiozero module was developed by Ben Nuttall and Dave Jones: read the docs'; 87 | 88 | PythonIDE.python.output(html, true); 89 | var paper = Raphael(document.getElementById("gpiozero"), 600, 350); 90 | PythonIDE.updateConsoleSize(); 91 | var offset = { 92 | x: 50, y: 0 93 | }; 94 | 95 | paper.rect(0, 0, 40, 280, 0).attr({ 96 | fill: '#00935b' 97 | }); 98 | 99 | paper.circle(20, 15, 10).attr({fill: '#ffb60c'}); 100 | paper.circle(20, 15, 5).attr({fill: '#ffffff'}); 101 | paper.circle(20, 250, 10).attr({fill: '#ffb60c'}); 102 | paper.circle(20, 250, 5).attr({fill: '#ffffff'}); 103 | paper.rect(0, 265, 20, 35).attr({fill:'#ccc'}); 104 | 105 | paper.rect(10, 30, 25, 205).attr({ 106 | fill: '#000' 107 | }); 108 | for(var i = 0; i < 20; i++) { 109 | var pin = paper.rect(15, 35 + i*10, 5, 5).attr({ 110 | fill: '#ffb60c' 111 | }); 112 | pin.number = (i*2) + 1; 113 | pin.name = 'Pin ' + pin.number + ' ' + pinNames[pin.number]; 114 | 115 | pin.hover(function() { 116 | PythonIDE.showHint(this.name); 117 | this.attr({stroke:'#fff'}); 118 | this.g = this.glow({color: '#f00', width: 20}); 119 | }, function() { 120 | this.attr({stroke:'unset'}); 121 | this.g.remove(); 122 | }); 123 | 124 | pin = paper.rect(25, 35 + i*10, 5, 5).attr({ 125 | fill: '#ffb60c' 126 | }); 127 | 128 | pin.number = (i*2) + 2; 129 | pin.name = 'Pin ' + pin.number + ' ' + pinNames[pin.number]; 130 | 131 | pin.hover(function() { 132 | PythonIDE.showHint(this.name); 133 | this.attr({stroke:'#fff'}); 134 | this.g = this.glow({color: '#f00', width: 20}); 135 | }, function() { 136 | this.attr({stroke:'unset'}); 137 | this.g.remove(); 138 | }); 139 | } 140 | 141 | 142 | 143 | mod.Device = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 144 | var __init__ = function(self, pin_factory) { 145 | self._closed = false; 146 | self.value = 0; 147 | }; 148 | __init__.co_varnames = ["self", "pin_factory"]; 149 | __init__.$defaults = [Sk.builtin.none.none$]; 150 | 151 | $loc.__init__ = new Sk.builtin.func(__init__); 152 | 153 | $loc.close = new Sk.builtin.func(function(self) { 154 | self._closed = true; 155 | }); 156 | 157 | $loc.closed = new Sk.builtin.func(function(self) { 158 | return new Sk.builtin.bool(self._closed); 159 | }); 160 | 161 | $loc.is_active = new Sk.builtin.func(function(self) { 162 | return new Sk.builtin.bool(self.value); 163 | }); 164 | }, "gpiozero.Device", []); 165 | 166 | mod.CompositeDevice = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 167 | var __init__ = function(kwa, self) { 168 | var args = Array.prototype.slice.call(arguments, 2); 169 | self.devices = args; 170 | for(var i=0; i < kwa.length; i+=2) { 171 | var key = Sk.ffi.remapToJs(kwa[i]); 172 | self.devices[key] = kwa[i+1]; 173 | } 174 | self.closed = false; 175 | }; 176 | __init__['co_kwargs'] = true; 177 | 178 | $loc.__init__ = new Sk.builtin.func(__init__); 179 | 180 | $loc.__getitem__ = new Sk.builtin.func(function(self, key) { 181 | return self.devices[Sk.ffi.remapToJs(key)]; 182 | }); 183 | 184 | $loc.close = new Sk.builtin.func(function(self) { 185 | self.closed = true; 186 | }); 187 | 188 | $loc.closed = new Sk.builtin.func(function(self) { 189 | return Sk.ffi.remapToPy(self.closed); 190 | }); 191 | 192 | $loc.value = new Sk.builtin.func(function(self) { 193 | var values = []; 194 | for(var key in self.devices) { 195 | values.push(self.devices[key].value); 196 | } 197 | return new Sk.builtins['tuple'](values); 198 | }); 199 | }, "gpiozero.CompositeDevice", [mod.Device]); 200 | 201 | function addHover(object, text) { 202 | if(arguments.length == 1) { 203 | for(var key in object) { 204 | addHover(object[key], key); 205 | } 206 | } else { 207 | object.hover(function() { 208 | PythonIDE.showHint(text); 209 | this.g = this.glow({color: '#f00', width: 20}); 210 | }, function() { 211 | this.g.remove(); 212 | }); 213 | } 214 | } 215 | 216 | function addWire(piPin, components, name, description) { 217 | var cPin = components[name]; 218 | var x = offset.x + Math.floor(Math.random() * 25) - 10; 219 | var y1 = offset.y + Math.floor(Math.random() * 50); 220 | var y2 = offset.y + Math.floor(Math.random() * 50); 221 | var wire = components["wire_" + name] = paper.path(["M", piPin.x || piPin.attrs.x, piPin.y || piPin.attrs.y, "C", offset.x, y1, x, y2, cPin.attrs.x+2, cPin.attrs.y+2]).attr({ 222 | stroke: colors[wires++ % colors.length], 223 | "stroke-width": 3 224 | }); 225 | addHover(wire, description); 226 | } 227 | 228 | var spareMotorController = undefined; 229 | 230 | mod.Motor = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 231 | 232 | var __init__ = function(kwa, self, forward, backward) { 233 | self.forward = Sk.ffi.remapToJs(forward); 234 | self.backward = Sk.ffi.remapToJs(backward); 235 | if(spareMotorController) { 236 | self.controller = spareMotorController; 237 | offset.x += 120; 238 | offset.y -= 100; 239 | self.components = { 240 | motorBack: paper.rect(offset.x + 120, offset.y + 40, 60, 20).attr({ 241 | fill: '#aaa' 242 | }), 243 | motorP1: paper.rect(offset.x + 125, offset.y + 60, 5, 10).attr({ 244 | fill: '#888' 245 | }), 246 | motorP2: paper.rect(offset.x + 170, offset.y + 60, 5, 10).attr({ 247 | fill: '#888' 248 | }), 249 | motor: paper.circle(offset.x + 150, offset.y + 30, 30).attr({ 250 | fill:'#ccc' 251 | }), 252 | motorAxis: paper.circle(offset.x + 150, offset.y + 30, 5).attr({ 253 | fill:'#888' 254 | }), 255 | cog: paper.image("lib/skulpt/rpi/components/cog.png", offset.x + 125, offset.y + 5, 50, 50) 256 | }; 257 | 258 | addWire(self.controller.components.pin11, self.components, "motorP1", "Wire to Motor 2"); 259 | addWire(self.controller.components.pin14, self.components, "motorP2", "Wire to Motor 2"); 260 | addWire(self.controller.components.pin16, self.controller.components, "pin9", "Wire to enable Motor 2"); 261 | offset.x -= 50; 262 | addWire(self.controller.components.pin12, self.controller.components, "pin13", "Wire to GND"); 263 | addWire(self.controller.components.pin4, self.controller.components, "pin13", "Wire to GND"); 264 | addWire(getPin("BCM " + self.forward), self.controller.components, "pin10", "Wire from pin " + self.forward + " to M2 forward"); 265 | addWire(getPin("BCM " + self.backward), self.controller.components, "pin15", "Wire from pin " + self.backward + " to M2 backward"); 266 | spareMotorController = undefined; 267 | offset.x -= 70; 268 | offset.y += 100; 269 | } else { 270 | spareMotorController = self; 271 | offset.x += 20; 272 | offset.y += 20; 273 | var gnd = getPin("Ground"); 274 | var power = getPin("5v Power"); 275 | self.components = { 276 | hbridge: paper.rect(offset.x + 30, offset.y, 40, 85).attr({ 277 | fill:'#444' 278 | }), 279 | text: paper.text(offset.x + 50, offset.y+30, "SN54410").attr({ 280 | fill:'#ccc' 281 | }).transform("r90"), 282 | motorBack: paper.rect(offset.x + 120, offset.y + 40, 60, 20).attr({ 283 | fill: '#aaa' 284 | }), 285 | motorP1: paper.rect(offset.x + 125, offset.y + 60, 5, 10).attr({ 286 | fill: '#888' 287 | }), 288 | motorP2: paper.rect(offset.x + 170, offset.y + 60, 5, 10).attr({ 289 | fill: '#888' 290 | }), 291 | motor: paper.circle(offset.x + 150, offset.y + 30, 30).attr({ 292 | fill:'#ccc' 293 | }), 294 | motorAxis: paper.circle(offset.x + 150, offset.y + 30, 5).attr({ 295 | fill:'#888' 296 | }), 297 | cog: paper.image("lib/skulpt/rpi/components/cog.png", offset.x + 125, offset.y + 5, 50, 50) 298 | }; 299 | for(pin = 0; pin < 8; pin++) { 300 | self.components["pin" + (pin+1)] = paper.rect(offset.x+25, offset.y + 5 + (pin * 10), 5, 5).attr({fill: '#ccc'}); 301 | self.components["pin" + (pin+9)] = paper.rect(offset.x+25 + 45, offset.y - 85 + ((16 - pin) * 10), 5, 5).attr({fill: '#ccc'}); 302 | } 303 | addWire(gnd, self.components, "pin4", "Wire to ground"); 304 | 305 | addWire(power, self.components, "pin1", "Wire to enable Motor 1 (+5V)"); 306 | addWire(getPin("BCM " + self.forward), self.components, "pin2", "Wire from pin " + self.forward + " to M1 forward"); 307 | addWire(getPin("BCM " + self.backward), self.components, "pin7", "Wire from pin " + self.backward + " to M1 backward"); 308 | 309 | offset.x += 50; 310 | addWire(self.components.pin4, self.components, "pin5", "Wire to ground"); 311 | offset.y -= 30; 312 | addWire(self.components.pin1, self.components, "pin16", "Wire to +5v"); 313 | offset.y += 30; 314 | addWire(self.components.pin1, self.components, "pin7", "Wire to +5v"); 315 | 316 | addWire(self.components.motorP1, self.components, "pin3", "Motor 1"); 317 | addWire(self.components.motorP2, self.components, "pin6", "Motor 1"); 318 | 319 | addHover({ 320 | "SN54410 HBridge Motor Controller": self.components.hbridge, 321 | "Pin 1 (Motor 1 Enable)": self.components.pin1, 322 | "Pin 2 (Motor 1 Forward)": self.components.pin2, 323 | "Pin 3 (Motor 1)": self.components.pin3, 324 | "Pin 4 (GND)": self.components.pin4, 325 | "Pin 5 (GND)": self.components.pin5, 326 | "Pin 6 (Motor 1)": self.components.pin6, 327 | "Pin 7 (Motor 1 Reverse)": self.components.pin7, 328 | "Pin 8 (Motor Power)": self.components.pin8, 329 | "Pin 9 (Motor 2 Enable)": self.components.pin9, 330 | "Pin 10 (Motor 2 Forward)": self.components.pin10, 331 | "Pin 11 (Motor 2)": self.components.pin11, 332 | "Pin 12 (GND)": self.components.pin12, 333 | "Pin 13 (GND)": self.components.pin13, 334 | "Pin 14 (Motor 2)": self.components.pin14, 335 | "Pin 15 (Motor 2 Reverse)": self.components.pin15, 336 | "Pin 16 (+5v)": self.components.pin16 337 | }); 338 | 339 | offset.x -=70; 340 | offset.y += 100; 341 | } 342 | }; 343 | __init__['co_kwargs'] = true; 344 | function spinCW(c, time) { 345 | var degrees = 0; 346 | var t = c.transform(); 347 | if(t[0]) 348 | degress = Math.floor(t[0][1]) % 360; 349 | c.attr({transform:"R" + degrees}).stop().animate({transform:"R" + (degrees + 360)}, time, function() {spinCW(c, time)}); 350 | } 351 | function spinCCW(c, time) { 352 | var degrees = 0; 353 | var t = c.transform(); 354 | if(t[0]) 355 | degrees = Math.floor(t[0][1]) % 360; 356 | c.attr({transform:"R" + (degrees + 360)}).stop().animate({transform:"R" + degrees}, time, function() {spinCCW(c, time)}); 357 | } 358 | 359 | $loc.forward = new Sk.builtin.func(function(self, speed) { 360 | self.value = Sk.ffi.remapToJs(speed); 361 | if(speed == undefined) self.value = 1; 362 | if(self.value > 1)self.value = 1; 363 | if(self.value < 0)self.value = 0; 364 | if(self.value > 0) { 365 | var time = 5000-((self.value / 1.0) * 4000) 366 | spinCW(self.components.cog, time); 367 | } else { 368 | self.components.cog.stop(); 369 | } 370 | }); 371 | 372 | $loc.backward = new Sk.builtin.func(function(self, speed) { 373 | self.value = 0 - Sk.ffi.remapToJs(speed); 374 | if(speed == undefined) self.value = -1; 375 | if(self.value < -1)self.value = -1; 376 | if(self.value > 0)self.value = 0; 377 | if(self.value < 0) { 378 | var time = 5000-((self.value / -1.0) * 4000) 379 | spinCCW(self.components.cog, time); 380 | } else { 381 | self.components.cog.stop(); 382 | } 383 | }); 384 | 385 | $loc.stop = new Sk.builtin.func(function(self) { 386 | self.components.cog.stop(); 387 | self.value = 0; 388 | }); 389 | 390 | $loc.__init__ = new Sk.builtin.func(__init__); 391 | }, "gpiozero.Motor", [mod.CompositeDevice]); 392 | 393 | mod.GPIODevice = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 394 | var __init__ = function(self, pin, pin_factory) { 395 | Sk.misceval.callsimArray(mod.Device.__init__, [self, pin_factory]); 396 | self.pin = pin; 397 | }; 398 | __init__.co_varnames = ["self", "pin", "pin_factory"]; 399 | __init__.$defaults = [Sk.builtin.none.none$]; 400 | $loc.__init__ = new Sk.builtin.func(__init__); 401 | 402 | $loc.__getattr__ = new Sk.builtin.func(function(self, name) { 403 | name = Sk.ffi.remapToJs(name); 404 | switch(name) { 405 | case 'pin': 406 | return self.pin; 407 | break; 408 | case 'value': 409 | return self.ffi.remapToPy(self.value); 410 | break; 411 | } 412 | }); 413 | }, "gpiozero.GPIODevice", [mod.Device]); 414 | 415 | mod.InputDevice = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 416 | var __init__ = function(self, pin, pull_up, active_state, pin_factory) { 417 | Sk.misceval.callsimArray(mod.GPIODevice.__init__, [self, pin, pin_factory]); 418 | self.pull_up = pull_up; 419 | self.active_state = active_state; 420 | } 421 | __init__.co_varnames = ["self", "pin", "pull_up", "active_state", "pin_factory"]; 422 | __init__.$defaults = [Sk.builtin.bool.false$, Sk.builtin.none.none$, Sk.builtin.none.none$]; 423 | $loc.__init__ = new Sk.builtin.func(__init__); 424 | }, "gpiozero.InputDevice", [mod.GPIODevice]); 425 | 426 | mod.DigitalInputDevice = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 427 | var __init__ = function(self, pin, pull_up, active_state, bounce_time, pin_factory) { 428 | Sk.misceval.callsimArray(mod.InputDevice.__init__, [self, pin, pull_up, active_state, pin_factory]); 429 | self.bounce_time = bounce_time; 430 | } 431 | __init__.co_varnames = ["self", "pin", "pull_up", "active_state", "bounce_time", "pin_factory"]; 432 | __init__.$defaults = [Sk.builtin.bool.false$, Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.none.none$]; 433 | $loc.__init__ = new Sk.builtin.func(__init__); 434 | }, "gpiozero.DigitalInputDevice", [mod.InputDevice]); 435 | 436 | mod.OutputDevice = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 437 | var __init__ = function(self, pin, active_high, initial_value, pin_factory) { 438 | Sk.misceval.callsimArray(mod.GPIODevice.__init__, [self, pin, pin_factory]); 439 | self.active_high = active_high; 440 | self.initial_value = initial_value; 441 | } 442 | __init__.co_varnames = ["self", "pin", "active_high", "initial_value", "pin_factory"]; 443 | __init__.$defaults = [Sk.builtin.bool.true$, Sk.builtin.bool.false$, Sk.builtin.none.none$]; 444 | $loc.__init__ = new Sk.builtin.func(__init__); 445 | }, "gpiozero.OutputDevice", [mod.GPIODevice]); 446 | 447 | mod.DigitalOutputDevice = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 448 | var __init__ = function(self, pin, active_high, initial_value, pin_factory) { 449 | Sk.misceval.callsimArray(mod.OutputDevice.__init__, [self, pin, active_high, initial_value, pin_factory]); 450 | } 451 | __init__.co_varnames = ["self", "pin", "active_high", "initial_value", "pin_factory"]; 452 | __init__.$defaults = [Sk.builtin.bool.true$, Sk.builtin.bool.false$, Sk.builtin.none.none$]; 453 | $loc.__init__ = new Sk.builtin.func(__init__); 454 | 455 | var blink = function(self, on_time, off_time, n, background) { 456 | on_time = Sk.ffi.remapToJs(on_time); 457 | off_time = Sk.ffi.remapToJs(off_time); 458 | n = Sk.ffi.remapToJs(n); 459 | background = Sk.ffi.remapToJs(background); 460 | function nextBlink(resolve) { 461 | if(n > 0 || n == null) { 462 | setTimeout(function() { 463 | Sk.misceval.callsimArray(self.on, [self]); 464 | setTimeout(function() { 465 | Sk.misceval.callsimArray(self.off, [self]); 466 | if(n!==null)n--; 467 | nextBlink(resolve); 468 | }, off_time * 1000); 469 | }, on_time * 1000) 470 | } else { 471 | resolve(); 472 | } 473 | } 474 | if(!background) { 475 | return PythonIDE.runAsync(function(resolve, reject) { 476 | nextBlink(resolve); 477 | }); 478 | } else { 479 | nextBlink(function() { 480 | }); 481 | } 482 | } 483 | blink.co_varnames = ["self", "on_time", "off_time", "n", "background"]; 484 | blink.$defaults = [Sk.ffi.remapToPy(1), Sk.ffi.remapToPy(1), Sk.builtin.none.none$, Sk.builtin.bool.true$]; 485 | $loc.blink = new Sk.builtin.func(blink); 486 | 487 | $loc.off = new Sk.builtin.func(function(self) { 488 | self.value = Sk.ffi.remapToJs(self.active_high)?0:1; 489 | }); 490 | 491 | $loc.on = new Sk.builtin.func(function(self) { 492 | self.value = Sk.ffi.remapToJs(self.active_high)?1:0; 493 | }); 494 | 495 | }, "gpiozero.DigitalOutputDevice", [mod.OutputDevice]); 496 | 497 | mod.Button = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 498 | var __init__ = function(self, pin, pull_up, active_state, bounce_time, hold_time, hold_repeat, pin_factory) { 499 | Sk.misceval.callsimArray(mod.DigitalInputDevice.__init__, [self, pin, pull_up, active_state, bounce_time, pin_factory]); 500 | self.hold_time = hold_time; 501 | self.hold_repeat = hold_repeat; 502 | self.components = { 503 | wire1: paper.rect(offset.x + 20, offset.y + 65, 50, 3).attr({fill:'#989898'}), 504 | wire2: paper.rect(offset.x + 20, offset.y + 80, 50, 3).attr({fill:'#989898'}), 505 | base: paper.rect(offset.x + 30, offset.y + 60, 30, 30, 5).attr({fill:'#ccc'}), 506 | switch: paper.rect(offset.x + 35, offset.y + 65, 20, 20, 5).attr({fill:'#724a02'}).hover(function() { 507 | PythonIDE.showHint("Button"); 508 | this.g = this.glow({color: '#f00', width: 20}); 509 | }, function() { 510 | this.g.remove(); 511 | }).mousedown(function() { 512 | if(self.onpress) { 513 | self.onpress(); 514 | } 515 | self.holdTimeout = setTimeout(function() { 516 | ///TODO 517 | if(self.when_held) { 518 | Sk.misceval.callsimAsync(null, self.when_held, self).then(function() { 519 | }, function(e) { 520 | window.onerror(e); 521 | }); 522 | } 523 | }, Sk.ffi.remapToJs(self.hold_time) * 1000); 524 | self.lastPress = new Date(); 525 | }).mouseup(function() { 526 | if(self.onrelease) { 527 | self.onrelease(); 528 | } 529 | clearTimeout(self.holdTimeout); 530 | delete self.lastPress; 531 | }) 532 | }; 533 | var gnd = getPin("Ground"); 534 | var pin = getPin("BCM " + Sk.ffi.remapToJs(pin)); 535 | self.components.wireToGround = paper.path(["M", gnd.x, gnd.y, "C", offset.x, offset.y, offset.x, offset.y + 50, offset.x + 20, offset.y + 65]).attr({ 536 | stroke: colors[wires++ % colors.length], 537 | "stroke-width": 3 538 | }).hover(function() { 539 | this.g = this.glow({color: '#f00', width: 20}); 540 | PythonIDE.showHint("Wire connected to Ground"); 541 | }, function() { 542 | this.g.remove(); 543 | }); 544 | self.components.wireToPin = paper.path(["M", pin.x, pin.y, "C", offset.x, offset.y, offset.x, offset.y + 80, offset.x + 20, offset.y + 80]).attr({ 545 | stroke: colors[wires++ % colors.length], 546 | "stroke-width": 3 547 | }).hover(function() { 548 | this.g = this.glow({color: '#f00', width: 20}); 549 | PythonIDE.showHint("Wire connected to " + pin.name); 550 | }, function() { 551 | this.g.remove(); 552 | }); 553 | offset.x += 130; 554 | if(offset.x > 500) { 555 | offset.x = 50; 556 | offset.y += 160; 557 | } 558 | } 559 | __init__.co_varnames = ["self", "pin", "pull_up", "active_state", "bounce_time", "hold_time", "hold_repeat", "pin_factory"]; 560 | __init__.$defaults = [Sk.builtin.bool.true$, Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.ffi.remapToPy(1), Sk.builtin.bool.false$, Sk.builtin.none.none$]; 561 | $loc.__init__ = new Sk.builtin.func(__init__); 562 | 563 | var wait_for_press = function(self, timeout) { 564 | timeout = Sk.ffi.remapToJs(timeout); 565 | return PythonIDE.runAsync(function(resolve, reject) { 566 | if(timeout) { 567 | clearTimeout(self.timeout); 568 | self.timeout = setTimeout(resolve, timeout * 1000); 569 | } 570 | self.onpress = function() { 571 | self.onpress = undefined; 572 | resolve(); 573 | } 574 | }); 575 | }; 576 | wait_for_press.co_varnames = ["self", "timeout"]; 577 | wait_for_press.$defaults = [Sk.builtin.none.none$]; 578 | $loc.wait_for_press = new Sk.builtin.func(wait_for_press); 579 | 580 | var wait_for_release = function(self, timeout) { 581 | timeout = Sk.ffi.remapToJs(timeout); 582 | return PythonIDE.runAsync(function(resolve, reject) { 583 | if(timeout) { 584 | clearTimeout(self.timeout); 585 | self.timeout = setTimeout(resolve, timeout * 1000); 586 | } 587 | self.onrelease = function() { 588 | self.onrelease = undefined; 589 | resolve(); 590 | } 591 | }); 592 | }; 593 | wait_for_release.co_varnames = ["self", "timeout"]; 594 | wait_for_release.$defaults = [Sk.builtin.none.none$]; 595 | $loc.wait_for_release = new Sk.builtin.func(wait_for_release); 596 | 597 | $loc.__getattr__ = new Sk.builtin.func(function(self, name) { 598 | name = Sk.ffi.remapToJs(name); 599 | switch(name) { 600 | case 'held_time': 601 | if(self.lastPress) { 602 | var diff = new Date() - self.lastPress; 603 | return Sk.ffi.remapToPy(diff / 1000); 604 | } else { 605 | return Sk.builtin.none.none$; 606 | } 607 | break; 608 | } 609 | }); 610 | }, "gpiozero.Button", [mod.DigitalInputDevice]); 611 | 612 | mod.LED = new Sk.misceval.buildClass(mod, function($gbl, $loc) { 613 | var __init__ = function(self, pin, active_high, initial_value, pin_factory) { 614 | Sk.misceval.callsimArray(mod.DigitalOutputDevice.__init__, [self, pin, active_high, initial_value, pin_factory]); 615 | self.components = { 616 | led: paper.image("lib/skulpt/rpi/components/led.png", offset.x + 80, offset.y, 41, 157).hover(function() { 617 | PythonIDE.showHint("Light Emitting Diode"); 618 | this.g = this.glow({color: '#f00', width: 20}); 619 | }, function() { 620 | this.g.remove(); 621 | }), 622 | shine: paper.rect(offset.x + 90, offset.y + 10, 20, 30, 5).attr({ 623 | fill: 'red', 624 | opacity: 1, 625 | 'stroke-width': 0 626 | }), 627 | resistor: paper.image("lib/skulpt/rpi/components/resistor330.png", offset.x, offset.y + 127, 94, 14).hover(function() { 628 | PythonIDE.showHint("330 Ohm resistor"); 629 | this.g = this.glow({color: '#f00', width: 20}); 630 | }, function() { 631 | this.g.remove(); 632 | }) 633 | }; 634 | var gnd = getPin("Ground"); 635 | var anode = getPin("BCM " + Sk.ffi.remapToJs(pin)); 636 | self.components.wireToResistor = paper.path(["M", gnd.x, gnd.y, "C", offset.x, offset.y, offset.x, offset.y + 50, offset.x, offset.y + 132]).attr({ 637 | stroke: colors[wires++ % colors.length], 638 | "stroke-width": 3 639 | }).hover(function() { 640 | this.g = this.glow({color: '#f00', width: 20}); 641 | PythonIDE.showHint("Wire connected to Ground"); 642 | }, function() { 643 | this.g.remove(); 644 | }); 645 | self.components.wireToAnode = paper.path(["M", anode.x, anode.y, "C", offset.x, offset.y, offset.x, offset.y + 227, offset.x + 112, offset.y + 158]).attr({ 646 | stroke: colors[wires++ % colors.length], 647 | "stroke-width": 3 648 | }).hover(function() { 649 | this.g = this.glow({color: '#f00', width: 20}); 650 | PythonIDE.showHint("Wire connected to " + anode.name); 651 | }, function() { 652 | this.g.remove(); 653 | }); 654 | offset.x += 130; 655 | if(offset.x > 500) { 656 | offset.x = 50; 657 | offset.y += 160; 658 | } 659 | } 660 | __init__.co_varnames = ["self", "pin", "active_high", "initial_value", "pin_factory"]; 661 | __init__.$defaults = [Sk.builtin.bool.true$, Sk.builtin.bool.false$, Sk.builtin.none.none$]; 662 | $loc.__init__ = new Sk.builtin.func(__init__); 663 | 664 | $loc.off = new Sk.builtin.func(function(self) { 665 | self.value = Sk.ffi.remapToJs(self.active_high)?0:1; 666 | var s = self.components.shine.attr({ 667 | opacity: 0.5, 668 | fill: 'black' 669 | }); 670 | if(s.g) s.g.remove(); 671 | }); 672 | 673 | $loc.on = new Sk.builtin.func(function(self) { 674 | self.value = Sk.ffi.remapToJs(self.active_high)?1:0; 675 | var s = self.components.shine.attr({ 676 | opacity: 1, 677 | fill: 'red' 678 | }); 679 | s.g = s.glow({color: '#f00', width: 60}); 680 | self.components.led.toFront(); 681 | }); 682 | }, "gpiozero.LED", [mod.DigitalOutputDevice]); 683 | return mod; 684 | } -------------------------------------------------------------------------------- /lib/skulpt/schooldirect/__init__.js: -------------------------------------------------------------------------------- 1 | var $builtinmodule = function(name) { 2 | var mod = { 3 | messages: ["Congratulations Mr Bradley, on passing your school direct training", 4 | "All the very best for the future", 5 | "You're a fab teacher", 6 | "It's been great to work with you", 7 | "We'll miss you" 8 | ] 9 | }; 10 | var html = ''; 11 | html += 'Congratulations on passing your school direct training'; 12 | PythonIDE.python.output(html); 13 | 14 | setInterval(function() { 15 | var y = Math.round(Math.random() * 10); 16 | var r = Math.round(Math.random() * 10); 17 | $("#unicorn").css({'top': y + "%", 'left': '50%', 'transform':'rotate(' + r + 'deg)'}); 18 | }, 1000); 19 | 20 | $('#unicorn').click(function() { 21 | var i = Math.floor(Math.random() * mod.messages.length); 22 | var msg = mod.messages[i]; 23 | $('#scroller').html(msg); 24 | }); 25 | 26 | 27 | return mod; 28 | }; -------------------------------------------------------------------------------- /lib/skulpt/schooldirect/unicorn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/lib/skulpt/schooldirect/unicorn.png -------------------------------------------------------------------------------- /lib/skulpt/speech/__init__.js: -------------------------------------------------------------------------------- 1 | var $builtinmodule = function (name) { 2 | function PlayWebkit(context, audiobuffer, resolve, reject) 3 | { 4 | var source = context.createBufferSource(); 5 | var soundBuffer = context.createBuffer(1, audiobuffer.length, 22050); 6 | var buffer = soundBuffer.getChannelData(0); 7 | for(var i=0; idownload wav'); 50 | $('#' + id).click(function(e) { 51 | saveAs(mod.dlBlobs[e.currentTarget.id], 'speech.wav'); 52 | }); 53 | if (typeof AudioContext !== "undefined") 54 | { 55 | PlayWebkit(new AudioContext(), audiobuffer, resolve, reject); 56 | } else 57 | if (typeof webkitAudioContext !== "undefined") 58 | { 59 | PlayWebkit(new webkitAudioContext(), audiobuffer, resolve, reject); 60 | } else if (typeof Audio !== "undefined") 61 | { 62 | /*var context = new Audio(); 63 | context.mozSetup(1, 22050); 64 | PlayMozilla(context, audiobuffer, resolve, reject); 65 | */ 66 | //PythonIDE.showHint("Sorry, your browser doesn't support speech"); 67 | 68 | PythonIDE.python.output('

Your browser can\'t say "' + speech + '". Click here to download a wav file instead, or use a better browser if possible.

'); 69 | PythonIDE.showHint("Web browser doesn't support web audio"); 70 | $('.dl_speech').click(function(e) { 71 | saveAs(mod.dlBlobs[e.currentTarget.id], 'speech.wav'); 72 | }); 73 | resolve(); 74 | 75 | } 76 | } 77 | 78 | 79 | // speech module 80 | 81 | 82 | var mod = { 83 | 84 | }; 85 | 86 | var say = function(speech, pitch, speed, mouth, throat) { 87 | return PythonIDE.runAsync(function(resolve, reject) { 88 | // DEFAULTS 89 | if(pitch === undefined) 90 | pitch = Sk.builtin.nmber(64); 91 | if(speed === undefined) 92 | speed = Sk.builtin.nmber(72); 93 | if(mouth === undefined) 94 | mouth = Sk.builtin.nmber(128); 95 | if(throat === undefined) 96 | throat = Sk.builtin.nmber(128); 97 | 98 | _SetPitch(pitch.v); 99 | _SetSpeed(speed.v); 100 | _SetMouth(mouth.v); 101 | _SetThroat(throat.v); 102 | 103 | var input = speech.v; 104 | while (input.length < 256) input += " "; 105 | var ptr = allocate(intArrayFromString(input), 'i8', ALLOC_STACK); 106 | 107 | _TextToPhonemes(ptr); 108 | 109 | 110 | _SetInput(ptr); 111 | _Code39771(); 112 | 113 | var bufferlength = Math.floor(_GetBufferLength()/50); 114 | var bufferptr = _GetBuffer(); 115 | 116 | 117 | audiobuffer = new Float32Array(bufferlength); 118 | 119 | for(var i=0; i= this._buffer.length ){ 306 | rt = new Int16Array(this._buffer.length - this._bufferNeedle); 307 | this._eof = true; 308 | } 309 | else { 310 | rt = new Int16Array(len); 311 | } 312 | 313 | for(var i=0; i> 16; // RIFF size 337 | 338 | intBuffer[4] = 0x4157; // "WA" 339 | intBuffer[5] = 0x4556; // "VE" 340 | 341 | intBuffer[6] = 0x6d66; // "fm" 342 | intBuffer[7] = 0x2074; // "t " 343 | 344 | intBuffer[8] = 0x0010; // fmt chunksize: 16 345 | intBuffer[9] = 0x0000; // 346 | 347 | intBuffer[10] = 0x0001; // format tag : 1 348 | intBuffer[11] = this._channels; // channels: 1 349 | 350 | intBuffer[12] = this._sampleRate & 0x0000ffff; // sample per sec 351 | intBuffer[13] = (this._sampleRate & 0xffff0000) >> 16; // sample per sec 352 | 353 | intBuffer[14] = (2*this._channels*this._sampleRate) & 0x0000ffff; // byte per sec 354 | intBuffer[15] = ((2*this._channels*this._sampleRate) & 0xffff0000) >> 16; // byte per sec 355 | 356 | intBuffer[16] = this._channels * 2; // block align 357 | intBuffer[17] = 0x0010; // bit per sample 358 | 359 | intBuffer[18] = 0x6164; // "da" 360 | intBuffer[19] = 0x6174; // "ta" 361 | intBuffer[20] = (2*buffer.length) & 0x0000ffff; // data size[byte] 362 | intBuffer[21] = ((2*buffer.length) & 0xffff0000) >> 16; // data size[byte] 363 | 364 | for (var i = 0; i < buffer.length; i++) { 365 | tmp = buffer[i]; 366 | if (tmp >= 1) { 367 | intBuffer[i+22] = (1 << 15) - 1; 368 | } 369 | else if (tmp <= -1) { 370 | intBuffer[i+22] = -(1 << 15); 371 | } 372 | else { 373 | intBuffer[i+22] = Math.round(tmp * (1 << 15)); 374 | } 375 | } 376 | 377 | return intBuffer; 378 | }; 379 | 380 | 381 | return mod; 382 | 383 | }; 384 | -------------------------------------------------------------------------------- /lib/skulpt/sqlite3/__init__.js: -------------------------------------------------------------------------------- 1 | var $builtinmodule = function (name) { 2 | var s = { 3 | 4 | }; 5 | 6 | // versions same as from test platform 7 | s.version = new Sk.builtin.str('2.6.0'); 8 | s.version_info = new Sk.builtin.tuple([2,6,0]); 9 | s.sqlite_version = Sk.builtin.str('3.6.21'); 10 | s.sqlite_version_info = Sk.builtin.tuple([3,6,21]); 11 | s.PARSE_DECLTYPES = Sk.ffi.remapToPy(1); 12 | s.PARSE_COLNAMES = Sk.ffi.remapToPy(2); 13 | 14 | 15 | /// TODO: 16 | s.register_converter = new Sk.builtin.func(function(typename, callable) { 17 | throw Sk.builtin.Exception("Not implemented yet"); 18 | }); 19 | 20 | s.register_adapter = new Sk.builtin.func(function(type, callable) { 21 | throw Sk.builtin.Exception("Not implemented yet"); 22 | }); 23 | 24 | s.enable_callback_tracebacks = new Sk.builtin.func(function(flag) { 25 | throw Sk.builtin.Exception("Not implemented yet"); 26 | }); 27 | 28 | /// END TOTO 29 | 30 | 31 | s.complete_statement = new Sk.builtin.func(function(sql) { 32 | // check for semicolon at end 33 | if(sql.v.search(/;\s*$/) < 0) 34 | return false; 35 | 36 | // search for unclosed quotes 37 | var sQ = false; 38 | var dQ = false; 39 | var escapeNext = false; 40 | for(var i = 0; i < sql.v.length; i++) { 41 | if(escapeNext) { 42 | escapeNext = false; 43 | continue; 44 | } 45 | var c = sql.v[i]; 46 | switch(c) { 47 | case '"': 48 | sQ = !sQ; 49 | break; 50 | case "'": 51 | dQ = !dQ; 52 | break; 53 | case "\\": 54 | escapeNext = true; 55 | break; 56 | } 57 | 58 | } 59 | if(sQ) // unmatched single quotes 60 | return false; 61 | 62 | if(dQ) // unmatched double quotes 63 | return false; 64 | return true; 65 | }); 66 | 67 | 68 | 69 | s.Cursor = new Sk.misceval.buildClass (s, function($gbl, $loc) { 70 | $loc.__init__ = new Sk.builtin.func(function(self, connection) { 71 | self.connection = connection; 72 | self.results = []; 73 | self.currentRowID = 0; 74 | }); 75 | 76 | $loc.__iter__ = new Sk.builtin.func(function(self) { 77 | 78 | var allLines = self.results; 79 | 80 | return Sk.builtin.makeGenerator(function () { 81 | if (this.$index >= this.$lines.length) { 82 | return undefined; 83 | } 84 | return this.$lines[this.$index++]; 85 | }, { 86 | $obj : self, 87 | $index: 0, 88 | $lines: allLines 89 | }); 90 | }); 91 | 92 | $loc.close = new Sk.builtin.func(function(self){ 93 | 94 | }); 95 | 96 | 97 | 98 | $loc.execute = new Sk.builtin.func(function(self,sql) { 99 | // run select queries immediately. Queue all others for commit 100 | if(sql.v.toUpperCase().search(/^\s*SELECT/) == 0){ 101 | return PythonIDE.runAsync(function(resolve, reject){ 102 | var pCommit = new Promise(self.connection.commit); 103 | pCommit.then(function(){ 104 | self.connection.db.transaction(function(tx){ 105 | self.results = []; 106 | tx.executeSql(sql.v, [], function(tx, result){ 107 | for(i = 0; i < result.rows.length; i++){ 108 | var record = []; 109 | for(key in result.rows[i]){ 110 | record.push(result.rows[i][key]); 111 | } 112 | self.results.push(Sk.ffi.remapToPy(record)); 113 | } 114 | resolve(self); 115 | }, function(tx, error){ 116 | reject(error); 117 | }); 118 | }); 119 | }).catch(function(e){ 120 | reject(e); 121 | }); 122 | 123 | 124 | 125 | 126 | }); 127 | } else { 128 | self.connection.queue.push(sql.v); 129 | return self; 130 | } 131 | 132 | 133 | 134 | 135 | 136 | }); 137 | }, 'Cursor', []); 138 | 139 | s.Connection = new Sk.misceval.buildClass(s, function($gbl, $loc) { 140 | $loc.__init__ = new Sk.builtin.func(function(self, filename) { 141 | 142 | self.filename = filename; 143 | self.queue = []; 144 | 145 | self.db = openDatabase(filename.v, '1.0', 'sqlite3', 1024); 146 | 147 | self.commit = function(done, fail) { 148 | console.log(self.queue); 149 | self.db.transaction(function(tx) { 150 | var promises = []; 151 | for(var i = 0; i < self.queue.length; i++) { 152 | var p = new Promise(function(resolve, reject){ 153 | console.log(i, "SQL commit:", self.queue[i]) 154 | tx.executeSql(self.queue[i], [], function(tx, results){ 155 | //console.log(results); 156 | resolve(); 157 | }, function(tx, error) { 158 | reject(error); 159 | }); 160 | 161 | }); 162 | promises.push(p); 163 | } 164 | 165 | // create SQL dump 166 | 167 | 168 | Promise.all(promises).then(function(r) { 169 | done(); 170 | //console.log("init completed"); 171 | }).catch(function(error) { 172 | console.log(error); 173 | }); 174 | }); 175 | }; 176 | 177 | self.db.transaction(function(tx) { 178 | 179 | // delete all tables 180 | tx.executeSql("SELECT * FROM sqlite_master WHERE type='table'", [], function(tx, result){ 181 | for(i = 0; i < result.rows.length; i++) { 182 | var tableName = result.rows[i].name; 183 | //console.log(result.rows[i]) 184 | tx.executeSql("DROP TABLE IF EXISTS " + tableName,[], 185 | function(tx, r){ 186 | //console.log("Dropped table: ", tableName) 187 | }, function(tx, error) { 188 | //console.log("Couldn't drop table: ", tableName) 189 | }); 190 | 191 | } 192 | }); 193 | 194 | // load sql from file 195 | var sql; 196 | if(Sk.readFile && (sql = Sk.readFile(filename.v))) { 197 | //console.log("found:", sql); 198 | statements = sql.split(/;\n/); 199 | 200 | var promises = []; 201 | for(var i = 0; i < statements.length; i++) { 202 | var p = new Promise(function(resolve, reject){ 203 | tx.executeSql(statements[i], [], function(tx, results){ 204 | resolve(); 205 | }, function(tx, error) { 206 | reject(error); 207 | }); 208 | 209 | }); 210 | promises.push(p); 211 | } 212 | Promise.all(promises).then(function(r) { 213 | self.loaded = true; 214 | //console.log("init completed"); 215 | }).catch(function(error) { 216 | console.log(error); 217 | }); 218 | 219 | } else { 220 | if(Sk.writeFile) { 221 | Sk.writeFile(filename.v, ""); 222 | self.loaded = true; 223 | } 224 | } 225 | }); 226 | }); 227 | 228 | // default isolation level 229 | $loc.isolation_level = Sk.ffi.remapToPy(""); 230 | 231 | // rolls back any changes since last commit 232 | $loc.rollback = new Sk.builtin.func(function(self) { 233 | self.queue = []; 234 | }); 235 | 236 | $loc.close = new Sk.builtin.func(function(self){ 237 | 238 | }); 239 | 240 | $loc.cursor = new Sk.builtin.func(function(self) { 241 | return Sk.misceval.callsim(s.Cursor, self); 242 | }); 243 | 244 | $loc.commit = new Sk.builtin.func(function(self) { 245 | return PythonIDE.runAsync(self.commit); 246 | 247 | }); 248 | }, 'Connection', []); 249 | 250 | 251 | 252 | s.connect = new Sk.builtin.func(function(filename) { 253 | var connection = Sk.misceval.callsim(s.Connection, filename); 254 | return connection; 255 | }); 256 | 257 | return s; 258 | 259 | }; 260 | -------------------------------------------------------------------------------- /lib/skulpt/tkinter/__init__.js: -------------------------------------------------------------------------------- 1 | var $builtinmodule = function (name) { 2 | // tkinter module 3 | 4 | 5 | var s = { 6 | }; 7 | 8 | 9 | return s; 10 | 11 | }; -------------------------------------------------------------------------------- /lib/skulpt/withcode/__init__.js: -------------------------------------------------------------------------------- 1 | var $builtinmodule = function (name) { 2 | // withcode module 3 | 4 | 5 | var s = { 6 | }; 7 | 8 | 9 | s.Image = new Sk.misceval.buildClass(s, function($gbl, $loc) { 10 | $loc.data = Sk.ffi.remapToPy([]); 11 | var imageData = undefined; 12 | var refreshTimeout = undefined; 13 | 14 | $loc.__init__ = new Sk.builtin.func(function(self, width, height) { 15 | // default param values 16 | if(width === undefined) 17 | width = 200; 18 | else 19 | width = Sk.ffi.remapToJs(width); 20 | 21 | if(height === undefined) 22 | height = 200; 23 | else 24 | height = Sk.ffi.remapToJs(height); 25 | 26 | $loc.id = "withcodeImage_" + Date.now(); 27 | $loc.width = width; 28 | $loc.height = height; 29 | 30 | PythonIDE.python.outputListeners.push(function(e) { 31 | if(refreshTimeout) { 32 | clearTimeout(refreshTimeout); 33 | refreshTimout = undefined; 34 | } 35 | if(imageData) { 36 | setTimeout(function() { 37 | var c = document.getElementById($loc.id); 38 | var ctx = c.getContext("2d"); 39 | ctx.putImageData(imageData, 0, 0); 40 | }, 100); 41 | 42 | } 43 | }); 44 | 45 | }); 46 | 47 | $loc.draw = new Sk.builtin.func(function(self, pyData) { 48 | var data = Sk.ffi.remapToJs(pyData); 49 | var colorModel = "B&W"; 50 | 51 | var c = document.getElementById($loc.id); 52 | if(!c) { 53 | var html = ''; 54 | 55 | PythonIDE.python.output(html); 56 | } 57 | 58 | 59 | 60 | 61 | 62 | var c = document.getElementById($loc.id); 63 | var ctx = c.getContext("2d"); 64 | 65 | var w = c.width; 66 | var h = c.height; 67 | 68 | if(data != undefined && data[0] != undefined && data[0][0] != undefined) { 69 | 70 | var cx = data[0].length; 71 | var cy = data.length; 72 | 73 | var cw = w / cx; 74 | var ch = h / cy; 75 | var x,y; 76 | 77 | // scan through for colour model 78 | if(data[0][0].length && data[0][0].length > 2) { 79 | colorModel = "RGB" 80 | } else { 81 | for(x = 0; x < w; x++) { 82 | if(data[0][x] > 1) { 83 | colorModel = "GS" 84 | break; 85 | } 86 | } 87 | } 88 | 89 | 90 | // fill in blocks 91 | for(y = 0; y < data.length; y++) { 92 | for(x = 0; x < data[y].length; x++) { 93 | switch(colorModel) { 94 | case 'GS': 95 | if(data[y][x] != undefined) { 96 | ctx.fillStyle="rgb(" + data[y][x] + "," + data[y][x] + "," + data[y][x] + ")"; 97 | ctx.fillRect(x * cw, y * ch, cw, ch); 98 | } 99 | break; 100 | case 'B&W': 101 | if(data[y][x]) 102 | ctx.fillRect(x * cw, y * ch, cw, ch); 103 | 104 | 105 | break; 106 | case 'RGB': 107 | if(data[y][x] && data[y][x][0] != undefined && data[y][x][1] != undefined && data[y][x][2] != undefined) { 108 | ctx.fillStyle="rgb(" + data[y][x][0] + "," + data[y][x][1] + "," + data[y][x][2] + ")"; 109 | ctx.fillRect(x * cw, y * ch, cw, ch); 110 | } 111 | 112 | break; 113 | } 114 | 115 | 116 | } 117 | } 118 | 119 | // draw grid 120 | for(x = 0; x < cx; x++) { 121 | ctx.beginPath(); 122 | ctx.moveTo(x * cw, 0); 123 | ctx.lineTo(x * cw, h); 124 | ctx.stroke(); 125 | } 126 | for(y = 0; y < cy; y++) { 127 | ctx.beginPath(); 128 | ctx.moveTo(0, y * ch); 129 | ctx.lineTo(w, y * ch); 130 | ctx.stroke(); 131 | } 132 | imageData = ctx.getImageData(0, 0, w, h); 133 | 134 | } 135 | }); 136 | }, 'Image', []); 137 | 138 | s.clear = new Sk.builtin.func(function() { 139 | PythonIDE.python.clear(); 140 | }); 141 | 142 | 143 | s.Visualiser = new Sk.misceval.buildClass(s, function($gbl, $loc) { 144 | $loc.__init__ = new Sk.builtin.func(function(self) { 145 | 146 | }); 147 | 148 | $loc.map = new Sk.builtin.func(function(self, data) { 149 | var id = "vis_map_" + Date.now(); 150 | var html = '
'; 151 | PythonIDE.python.output(html); 152 | try { 153 | google.charts.load('current', { 'packages': ['map'] }); 154 | } catch(e) { 155 | 156 | } 157 | data = Sk.ffi.remapToJs(data); 158 | google.charts.setOnLoadCallback(function() { 159 | var t = new google.visualization.DataTable(); 160 | t.addColumn('string', 'Location'); 161 | t.addColumn('string', 'Pin'); 162 | 163 | 164 | t.addRows(data); 165 | 166 | var options = { showTip: true }; 167 | var map = new google.visualization.Map(document.getElementById(id)); 168 | map.draw(t, options); 169 | 170 | }); 171 | 172 | }); 173 | 174 | $loc.geochart = new Sk.builtin.func(function(self, data) { 175 | var id = "vis_geo_" + Date.now(); 176 | var html = '
'; 177 | PythonIDE.python.output(html); 178 | try { 179 | google.charts.load('current', { 'packages': ['geochart'] }); 180 | } catch(e) { 181 | 182 | } 183 | data = Sk.ffi.remapToJs(data); 184 | google.charts.setOnLoadCallback(function() { 185 | var t = google.visualization.arrayToDataTable(data); 186 | 187 | var options = {}; 188 | var map = new google.visualization.GeoChart(document.getElementById(id)); 189 | map.draw(t, options); 190 | 191 | }); 192 | 193 | }); 194 | 195 | $loc.pie = new Sk.builtin.func(function(self, data, options) { 196 | var id = "vis_piechart_" + Date.now(); 197 | var html = '
'; 198 | 199 | PythonIDE.python.output(html); 200 | try { 201 | google.charts.load('current', {'packages':['corechart']}); 202 | } catch(e) { 203 | 204 | } 205 | google.charts.setOnLoadCallback(function() { 206 | if(data) { 207 | data = Sk.ffi.remapToJs(data); 208 | } else { 209 | data = {test:1}; 210 | } 211 | if(options) { 212 | options = Sk.ffi.remapToJs(options); 213 | } 214 | else { 215 | options = {'title':'', 'width':400, 'height':300}; 216 | } 217 | 218 | // Create the data table. 219 | var t = new google.visualization.DataTable(); 220 | t.addColumn('string', ''); 221 | t.addColumn('number', ''); 222 | 223 | var rows = []; 224 | for(var key in data) { 225 | rows.push([key, data[key]]); 226 | } 227 | t.addRows(rows); 228 | 229 | 230 | 231 | 232 | // Instantiate and draw our chart, passing in some options. 233 | var chart = new google.visualization.PieChart(document.getElementById(id)); 234 | chart.draw(t, options); 235 | }); 236 | }); 237 | 238 | 239 | $loc.pictogram = new Sk.builtin.func(function(self, data, titles, icons, colours, sizes) { 240 | 241 | var d = Sk.ffi.remapToJs(data); 242 | var t = titles?Sk.ffi.remapToJs(titles):undefined; 243 | var i = icons?Sk.ffi.remapToJs(icons):undefined; 244 | var cl = colours?Sk.ffi.remapToJs(colours):undefined; 245 | var sz = sizes?Sk.ffi.remapToJs(sizes):undefined; 246 | 247 | // show main title 248 | if(t && d && t.length > d.length) 249 | PythonIDE.python.output(t[t.length - 1] + "\n"); 250 | 251 | for(var c = 0; c < d.length; c++) { 252 | var html = ''; 253 | if(t && d && t[c]) 254 | html += t[c] + " "; 255 | 256 | if(d && d[c]) { 257 | if(cl && cl[c]) { 258 | html += ''; 259 | } 260 | if(sz && sz[c]) { 261 | html += ''; 262 | } 263 | for(var j = 0; j < d[c]; j++) { 264 | if(i && i[c]) { 265 | html += ''; 266 | } else { 267 | html += '#'; 268 | } 269 | 270 | } 271 | if(cl && cl[c]) { 272 | html += ''; 273 | } 274 | if(sz && sz[c]) { 275 | html += ''; 276 | } 277 | } 278 | 279 | html += '\n'; 280 | PythonIDE.python.output(html); 281 | } 282 | }); 283 | }, 'Visualiser', []); 284 | 285 | s.Speech = new Sk.misceval.buildClass(s, function($gbl, $loc) { 286 | var voices = []; 287 | 288 | $loc.__init__ = new Sk.builtin.func(function(self) { 289 | 290 | }); 291 | 292 | $loc.getVoices = new Sk.builtin.func(function(self) { 293 | return PythonIDE.runAsync(function(resolve, reject) { 294 | var timeoutCount = 0; 295 | var again = function() { 296 | if(timeoutCount++ > 10){ 297 | reject("Timeout whilst waiting for voices"); 298 | } 299 | voices = window.speechSynthesis.getVoices(); 300 | if(voices.length === 0) { 301 | setTimeout(again, 100); 302 | 303 | } else { 304 | var names = []; 305 | for(i = 0; i < voices.length; i++){ 306 | names.push(voices[i].name); 307 | } 308 | resolve(names); 309 | } 310 | }; 311 | again(); 312 | 313 | 314 | }); 315 | }); 316 | 317 | $loc.setVoice = new Sk.builtin.func(function(self, voiceName) { 318 | $loc.voiceName = voiceName.v; 319 | }); 320 | 321 | $loc.setPitch = new Sk.builtin.func(function(self, pitch) { 322 | $loc.pitch = pitch.v; 323 | }); 324 | 325 | $loc.setRate = new Sk.builtin.func(function(self, rate) { 326 | $loc.rate = rate.v; 327 | }); 328 | 329 | $loc.listen = new Sk.builtin.func(function(self) { 330 | return PythonIDE.runAsync(function(resolve, reject) { 331 | if(!('webkitSpeechRecognition' in window)) { 332 | reject("This browser doesn't support speech recognition: try Safari / Chrome if possible"); 333 | } 334 | var recognition = new webkitSpeechRecognition(); 335 | recognition.continuous = true; 336 | recognition.interimResults = true; 337 | var final_transcript = ''; 338 | 339 | recognition.onstart = function() { 340 | PythonIDE.python.output('listening...'); 341 | $('#btn_speech_stop').button().click(function() { 342 | recognition.stop(); 343 | }); 344 | }; 345 | recognition.onresult = function(event) { 346 | var interim_transcript = ''; 347 | 348 | for (var i = event.resultIndex; i < event.results.length; ++i) { 349 | if (event.results[i].isFinal) { 350 | final_transcript += event.results[i][0].transcript; 351 | } else { 352 | interim_transcript += event.results[i][0].transcript; 353 | } 354 | } 355 | $('#speech_inner').html('' + final_transcript + '' + interim_transcript + ''); 356 | }; 357 | recognition.onerror = function(e) { 358 | reject(e.error); 359 | }; 360 | recognition.onend = function() { 361 | $('#speech_holder').remove(); 362 | PythonIDE.python.output('' + final_transcript + '\n'); 363 | resolve(final_transcript + '\n'); 364 | }; 365 | recognition.start(); 366 | }); 367 | }); 368 | 369 | $loc.say = new Sk.builtin.func(function(self, text) { 370 | return PythonIDE.runAsync(function(resolve, reject) { 371 | /*timeout = setTimeout(function() { 372 | reject("Timeout whilst attempting to say " + text.v); 373 | }, 30000);*/ 374 | if ('speechSynthesis' in window) { 375 | 376 | 377 | var msg = new SpeechSynthesisUtterance(text.v); 378 | msg.onend = function() { 379 | //clearTimeout(timeout); 380 | resolve(); 381 | }; 382 | if($loc.voiceName){ 383 | for(i=0; i < voices.length; i++){ 384 | if(voices[i].name == $loc.voiceName){ 385 | msg.voice = voices[i]; 386 | break; 387 | } 388 | } 389 | 390 | if($loc.rate) { 391 | msg.rate = $loc.rate; 392 | } 393 | 394 | if($loc.pitch) { 395 | msg.pitch = $loc.pitch; 396 | } 397 | } 398 | window.speechSynthesis.speak(msg); 399 | 400 | } else { 401 | //clearTimeout(timeout); 402 | reject("Unfortunately, the speech module isn't supported by your browser yet. Try Google Chrome if possible"); 403 | } 404 | 405 | }); 406 | }); 407 | 408 | 409 | }, "Speech", []); 410 | 411 | return s; 412 | 413 | }; -------------------------------------------------------------------------------- /media/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/media/console.png -------------------------------------------------------------------------------- /media/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/media/play.png -------------------------------------------------------------------------------- /media/recover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/media/recover.png -------------------------------------------------------------------------------- /media/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/media/settings.png -------------------------------------------------------------------------------- /media/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/media/share.png -------------------------------------------------------------------------------- /media/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/media/stop.png -------------------------------------------------------------------------------- /media/tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/media/tools.png -------------------------------------------------------------------------------- /media/tweet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/media/tweet.png -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: monospace; 3 | } 4 | 5 | .brackets { 6 | color: #C8C8C8; 7 | } 8 | 9 | .randomChars { 10 | color: #0A0; 11 | } 12 | 13 | #title { 14 | text-align: center; 15 | background-color: rgba(255,255,255,0.4); 16 | border-radius: 10px; 17 | } 18 | 19 | 20 | .holder { 21 | position:relative; 22 | width:100%; 23 | height: 10px; 24 | } 25 | 26 | #editor { 27 | position: absolute; 28 | top: 0; 29 | right: 0; 30 | bottom: 0; 31 | left: 0; 32 | font-size: 12pt; 33 | } 34 | 35 | #footer { 36 | position: fixed; 37 | bottom: 0px; 38 | height: 70px; 39 | right: 0px; 40 | padding: 20px; 41 | margin: 10px; 42 | z-index: 1000; 43 | background-color: rgba(200,200,200,0.5); 44 | border-radius: 10px; 45 | transition: all 1s; 46 | } 47 | 48 | #footer:hover { 49 | background-color: rgba(0,255,0,0.5); 50 | } 51 | 52 | .toolButton { 53 | width: 65px; 54 | padding: 5px; 55 | transition: all 1s; 56 | } 57 | 58 | .hiddenButton { 59 | width: 0px; 60 | opacity: 0; 61 | padding: 0px; 62 | margin: 0px; 63 | } 64 | 65 | .visibleButton { 66 | width: 65px; 67 | padding: 5px; 68 | opacity: 1; 69 | } 70 | 71 | .toolButton:hover { 72 | transform: rotate(15deg); 73 | width: 75px; 74 | padding: 0px; 75 | } 76 | 77 | #hintBar { 78 | position: fixed; 79 | bottom: 0px; 80 | left: 20px; 81 | right: 20px; 82 | width: 100%; 83 | height: 2em; 84 | background-color: rgba(200,200,200,0.5); 85 | text-align: center; 86 | z-index: 500; 87 | 88 | font-size: 2em; 89 | padding: 3px; 90 | } 91 | #recover{ 92 | width: 80%; 93 | } 94 | .recover_date { 95 | background-color: #000; 96 | margin: 10px; 97 | padding: 10px; 98 | font-size: 2em; 99 | color: #F00; 100 | border-radius: 5px; 101 | } 102 | #recover_time{ 103 | font-family: monospace; 104 | border-radius: 5px; 105 | margin: 10px; 106 | padding: 30px; 107 | float: right; 108 | border-style: solid; 109 | border-color: #000; 110 | border-width: 1px; 111 | background-color: #FFF; 112 | } 113 | 114 | #recover_time_hours { 115 | background-color: #000; 116 | margin: 10px; 117 | padding: 10px; 118 | font-size: 2em; 119 | color: #F00; 120 | border-radius: 5px; 121 | } 122 | 123 | #recover_time_ampm { 124 | background-color: #000; 125 | margin: 10px; 126 | padding: 10px; 127 | color: #F00; 128 | border-radius: 5px; 129 | } 130 | 131 | #recover_time_mins { 132 | background-color: #000; 133 | margin: 10px; 134 | padding: 10px; 135 | font-size: 2em; 136 | color: #F00; 137 | border-radius: 5px; 138 | } 139 | 140 | #dlg { 141 | background-color: #000; 142 | color: #CCC; 143 | width: 80%; 144 | /*font-size: 2em;*/ 145 | } 146 | 147 | button { 148 | margin: 10px; 149 | } 150 | 151 | .console_input { 152 | color:#0A0; 153 | } 154 | 155 | #canvas { 156 | background-color: #FFF; 157 | width: 400px; 158 | height: 400px; 159 | } 160 | 161 | .error { 162 | color: #F00; 163 | padding: 5px; 164 | } 165 | 166 | .buttonPanel { 167 | position: absolute; 168 | top: -70px; 169 | right: 0px; 170 | padding: 10px; 171 | border-radius: 10px; 172 | background-color: rgba(0,0,200,0.5); 173 | 174 | } 175 | 176 | .hiddenButtonPanel { 177 | display: none; 178 | } 179 | 180 | #userDetails { 181 | display: none; 182 | position: absolute; 183 | top: 0px; 184 | right: 0px; 185 | background-color: rgba(200,200,200,0.5); 186 | border-radius: 10px; 187 | padding: 10px; 188 | margin: 10px; 189 | font-family: 'Quicksand', sans-serif; 190 | } 191 | 192 | #watch { 193 | max-width: 50%; 194 | position: absolute; 195 | bottom: 0px; 196 | right: 0px; 197 | background-color: #FFF; 198 | color: #000; 199 | padding:10px; 200 | /*font-size: 0.5em;*/ 201 | z-index: 2000; 202 | border-top-left-radius: 10px; 203 | display: none; 204 | } 205 | 206 | #watch table { 207 | border-collapse: collapse; 208 | table-layout: fixed; 209 | } 210 | 211 | #watch table,th,td { 212 | border: 1px solid black; 213 | word-wrap:break-word; 214 | } 215 | 216 | #watch tr,td { 217 | transition: all 0.5s; 218 | background-color: #FFF; 219 | } 220 | 221 | #watch th { 222 | background-color: #CCC; 223 | } 224 | 225 | #watch tr:hover { 226 | font-size: 2em; 227 | } 228 | #watch td:hover { 229 | background-color: #FF0; 230 | } 231 | a.nounderline{ 232 | color:inherit; 233 | text-decoration: none; 234 | } 235 | 236 | a{ 237 | color: #000; 238 | background-color: #FFF; 239 | } 240 | 241 | pre{ 242 | white-space: pre-wrap; 243 | } 244 | 245 | #file_tabs { 246 | position:absolute; 247 | top: -20px; 248 | 249 | } 250 | 251 | .file_tab { 252 | background-color: #E8E8E8; 253 | border-radius: 10px 10px 0px 0px; 254 | padding: 10px; 255 | height: 20px; 256 | margin: 2px; 257 | } 258 | 259 | .file_tab:hover { 260 | background-color: #FF0; 261 | } 262 | 263 | .file_tab_selected { 264 | font-weight: bold; 265 | background-color: #FF9; 266 | } 267 | .slider { 268 | margin: 20px 50px 20px 50px; 269 | } 270 | 271 | .btn_file_settings{ 272 | width:20px; 273 | height:20px; 274 | position:relative; 275 | margin: -25px 10px -5px 0px; 276 | transition: all 1s; 277 | } 278 | .btn_file_settings:hover { 279 | transform: rotate(180deg); 280 | } 281 | 282 | .debug_expand { 283 | height:1em; 284 | } -------------------------------------------------------------------------------- /thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pddring/createwithcode/4bf1e0bf908b4dc747bf19191c96ce6db9d85566/thumb.jpg -------------------------------------------------------------------------------- /toolindex.php: -------------------------------------------------------------------------------- 1 | Client-side version of create.withcode.uk (no share or save, but works without a web server: available on github) 2 | --------------------------------------------------------------------------------