README updates (and some code inspection fixes)
This commit is contained in:
parent
efab8c7147
commit
383de1cd8a
12 changed files with 262 additions and 820 deletions
243
README.md
243
README.md
|
|
@ -50,70 +50,114 @@ and the original machine are available in the [C1Pjs Documentation](/docs/c1pjs/
|
|||
|
||||
<!--BEGIN:EXCLUDE-->
|
||||
|
||||
Updating PCjs
|
||||
===
|
||||
|
||||
Developing
|
||||
Installing PCjs
|
||||
---
|
||||
Let's say you just finished developing, testing and deploying version 1.12.1, and you want to start developing some
|
||||
new features for version 1.13.0. Here are the recommended steps:
|
||||
|
||||
1. Create and switch to a new Git development branch (eg, "node_dev")
|
||||
2. Change the version number in [package.json](/package.json) (eg, to "1.13.0")
|
||||
3. Run "grunt" (the default task will build fresh "1.13.0" copies of everything under "/versions")
|
||||
4. Run "grunt promote" to bump the version in all the site's machine XML files to match (ie, to "1.13.0")
|
||||
|
||||
You may not want to do the last step until you're ready to start testing the new version. In fact, you may want to
|
||||
manually edit the version in only a few machine configuration files for testing, leaving the rest set to the previous
|
||||
version to make comparison runs easier, and then run "grunt promote" when the new version is much closer to being
|
||||
released.
|
||||
The following instructions were written for OS X users. However, users of other operating systems should have
|
||||
no problem following along.
|
||||
|
||||
Commit the development branch to GitHub as often as desired. I probably tend to "over-commit", checking in lots of
|
||||
intermediate changes before I finally decide that the new version is "good enough". But having too much delta history is
|
||||
probably better than too little. Note that the initial "node_dev" commit may not get pushed to GituHub without first
|
||||
running:
|
||||
Open Terminal and `cd` to a directory where you'd like to install *pcjs*, and run the following commands:
|
||||
|
||||
git push --set-upstream origin node_dev
|
||||
git clone git@github.com:jeffpar/pcjs.git pcjs
|
||||
cd pcjs
|
||||
npm install
|
||||
node server.js
|
||||
|
||||
One downside to intermediate commits to GitHub is that compiled versions of the sources (such as those in
|
||||
[/docs/pcjs/demos/](/docs/pcjs/demos/)) may keep changing, and those particular commits are pretty useless.
|
||||
To temporarily ignore changes to those files:
|
||||
Now open a web browser and go to `http://localhost:8088/`. You're done!
|
||||
|
||||
cd docs/pcjs/demos
|
||||
git update-index --assume-unchanged components.xsl pc.js pc-dbg.js samples.zip
|
||||
It's assumed that the OS X Developer Tools (which include Git) have already been installed, as well as
|
||||
Node and NPM. Node (which now includes NPM) should be downloaded from [nodejs.org](http://nodejs.org/download/).
|
||||
|
||||
The current version of Node ([0.10.32](http://nodejs.org/dist/v0.10.32/node-v0.10.32.pkg) at the time of this
|
||||
writing) should work fine, but version [0.10.26](http://nodejs.org/dist/v0.10.26/node-v0.10.26.pkg)
|
||||
is what's been used to develop and test PCjs so far.
|
||||
|
||||
Also, [server.js](server.js) was originally written using [Express](http://expressjs.com/) v3.x. Since then,
|
||||
Express v4.x has been released, but `npm install` will make sure that v3.x is installed locally.
|
||||
|
||||
The plan is to eventually move development to a newer version of Node, and migrate the PCjs server to a newer
|
||||
version of Express; there's no desire to remain stuck in the past (well, ignoring the fact that PCjs is the
|
||||
quintessential "stuck in the past" project), but there's also no urgency to update.
|
||||
|
||||
### Building PCjs
|
||||
|
||||
Unlike a typical project, where you have to *build* or *configure* or *make* something, PCjs is "ready to run".
|
||||
That's because both the compiled and uncompiled versions of PCjs are checked into the project, making deployment
|
||||
to a web server easy.
|
||||
|
||||
However, in order to build and test PCjs modifications, you'll want to use [Grunt](http://gruntjs.com/) and the
|
||||
Grunt tasks defined by [Gruntfile.js](Gruntfile.js).
|
||||
|
||||
Although Grunt was installed locally when you ran `npm install`, you'll also want to install the command-line
|
||||
interface to Grunt; you can install that locally as well, but it's recommended that you install it globally, with
|
||||
the "-g" option:
|
||||
|
||||
sudo npm install grunt-cli -g
|
||||
|
||||
Now you can run `grunt` anywhere within the PCjs project to build an updated version. If no command-line arguments
|
||||
are specified, `grunt` runs the "default" task defined by [Gruntfile.js](Gruntfile.js); that task runs Google's
|
||||
[Closure Compiler](https://developers.google.com/closure/compiler/) if any of the target files (eg, pc.js, pc-dbg.js,
|
||||
etc) are out-of date.
|
||||
|
||||
To ensure identical compilation results for everyone, a copy of the Closure Compiler has been checked into the
|
||||
[bin](bin/) folder. This version of Closure Compiler, in turn, requires Java v7.x or later. Use the following
|
||||
commands to confirm that everything is working properly:
|
||||
|
||||
java -version
|
||||
|
||||
To see a list of all files that are marked as "assume-unchanged", use this command:
|
||||
|
||||
git ls-files -v | grep '^[[:lower:]]'
|
||||
which should report a version >= 1.7.x; eg:
|
||||
|
||||
The same issue would exist for all new files created under "/versions", but fortunately, those files are ignored until
|
||||
you explicitly add them to Git, which you'll want to do just prior to deployment:
|
||||
java version "1.7.0_67"
|
||||
Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
|
||||
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)
|
||||
|
||||
git add versions/pcjs/1.13.0/*
|
||||
git add versions/c1pjs/1.13.0/*
|
||||
|
||||
which is when you'll also want to do undo the "assume-unchanged" operation above:
|
||||
Then run:
|
||||
|
||||
cd docs/pcjs/demos
|
||||
git update-index --no-assume-unchanged components.xsl pc.js pc-dbg.js samples.zip
|
||||
java -jar bin/compiler.jar --version
|
||||
|
||||
Testing
|
||||
which should report:
|
||||
|
||||
Closure Compiler (http://code.google.com/closure/compiler)
|
||||
Version: v20140407
|
||||
Built on: 2014/04/07 14:04
|
||||
|
||||
If you don't have Java installed, it's recommended that you install the JDK (*not* the JRE), because the JRE may not
|
||||
update your command-line tools properly. Note that Java is used *only* by the Closure Compiler; none of the PCjs
|
||||
client or server components use Java.
|
||||
|
||||
Newer versions of the Closure Compiler should work as well, and at some point, a newer version will be checked into the
|
||||
project.
|
||||
|
||||
Using PCjs
|
||||
---
|
||||
PCjs can now be run from the command-line mode using Node, making it possible to script the application,
|
||||
|
||||
### From The Browser
|
||||
|
||||
The PCjs web server is little more than a file/directory browser for the PCjs project, plus a collection of APIs.
|
||||
|
||||
If a URL corresponds to a PCjs project folder and no "index.html" exists in that folder, the server loads an HTML
|
||||
template ([common.html](my_modules/shared/templates/common.html)) and generates an "index.html" for that folder.
|
||||
|
||||
The contents of the "index.html" will vary depending on the contents of the folder; for example, if the folder
|
||||
contains a README.md, then that file is converted to HTML and embedded in the "index.html". Similarly, if the folder
|
||||
contains a machine XML file, that is embedded as well.
|
||||
|
||||
### From The Command-Line
|
||||
|
||||
The PCjs client app can also be run from the command-line mode using Node, making it possible to script the application,
|
||||
run a series of automated tests, etc:
|
||||
|
||||
cd ./my_modules/pcjs-client/bin
|
||||
node pcjs
|
||||
|
||||
The [pcjs](/my_modules/pcjs-client/bin/pcjs) script in the *bin* directory loads all the PCjs browser scripts listed
|
||||
in [package.json](/package.json), and then it starts a Node REPL ("read-eval-print loop"). The REPL handles a few
|
||||
special commands (eg, "load", "quit") and passes anything else to the PCjs Debugger component. If no Debugger component
|
||||
has been created yet, or if the Debugger didn't recognize the command, then it's passed on to *eval()*, like a good
|
||||
little REPL.
|
||||
The [pcjs](my_modules/pcjs-client/bin/pcjs) script in [/my_modules/pcjs-client/bin](my_modules/pcjs-client/bin) loads
|
||||
all the PCjs browser scripts listed in [package.json](/package.json), and then it starts a Node REPL ("read-eval-print loop").
|
||||
The REPL handles a few special commands (eg, "load", "quit") and passes anything else to the PCjs Debugger component.
|
||||
If no Debugger component has been created yet, or if the Debugger didn't recognize the command, then it's passed on to *eval()*,
|
||||
like a good little REPL.
|
||||
|
||||
Use the "load" command to load a JSON machine configuration file. A sample [machine.json](/my_modules/pcjs-client/bin/machine.json)
|
||||
is provided in the *bin* directory, which is a "JSON-ified" version of the [machine.xml](/configs/pc/machines/5150/mda/64kb/machine.xml)
|
||||
displayed on the [pcjs.org](http://www.pcjs.org/) home page.
|
||||
Use the "load" command to load a JSON machine configuration file. A sample [machine.json](my_modules/pcjs-client/bin/machine.json)
|
||||
is provided in the *bin* directory, which is a "JSON-ified" version of the [machine.xml](configs/pc/machines/5150/mda/64kb/machine.xml)
|
||||
displayed on the [pcjs.org](/) home page.
|
||||
|
||||
The command-line loader creates all the JSON-defined machine components in the same order that the browser creates
|
||||
XML-defined components. You can also issue the "load" command directly from the command-line:
|
||||
|
|
@ -139,65 +183,72 @@ supports *only* JSON machine configuration files.
|
|||
I haven't decided whether I'll add support for JSON configuration files to the client, or add some XML-to-JSON conversion
|
||||
to the server, or both.
|
||||
|
||||
Deploying
|
||||
Debugging PCjs
|
||||
---
|
||||
I like to use the AWS Elastic Beanstalk web interface to create a new "development" environment ("pcjs-dev") for testing:
|
||||
|
||||
### Server Components
|
||||
|
||||
To help test/debug changes to PCjs server components (eg, [DiskDump](my_modules/diskdump/), [HTMLOut](my_modules/htmlout/)),
|
||||
you can start the server with some additional options; eg:
|
||||
|
||||
node server.js --logging --console --debug
|
||||
|
||||
The `--logging` option will create a [node.log](/logs/node.log) that records all the HTTP requests, `--debug`
|
||||
will generate additional debug-only messages (which will also be logged if `--logging` is enabled), and `--console`
|
||||
will replicate any messages to your console as well.
|
||||
|
||||
A complete list of command-line options can be found in [server.js](server.js).
|
||||
|
||||
### Client Components
|
||||
|
||||
A special parameter ("gort") can be appended to the URL to request uncompiled client source files, making problems
|
||||
much easier to debug:
|
||||
|
||||
http://localhost:8088/?gort=debug
|
||||
|
||||
However, the "gort=debug" parameter is unnecessary if the server was started with `--debug`; the server always
|
||||
serves uncompiled files when running in "debug" mode.
|
||||
|
||||
Conversely, if the server is running "debug" mode but you want to test a compiled version of PCjs, use:
|
||||
|
||||
http://localhost:8088/?gort=release
|
||||
|
||||
and the server will serve compiled JavaScript files, regardless whether the server is running in "debug" or "release"
|
||||
mode.
|
||||
|
||||
Updating PCjs
|
||||
---
|
||||
|
||||
### Developing
|
||||
|
||||
To start developing features for a new version of PCjs, here are the recommended steps:
|
||||
|
||||
1. Save the current production environment ("pcjs-env") as configuration "pcjs-config"
|
||||
2. Create a new environment ("pcjs-dev") using the "pcjs-config" configuration
|
||||
3. Wait for the new environment ("pcjs-dev") to start (ie, for its health to become "Green")
|
||||
4. From Terminal, go to the `~/Sites/pcjs` folder and run `eb branch` followed by `git aws.push`
|
||||
1. Change the version number in [package.json](/package.json)
|
||||
2. Run the "grunt promote" task to bump the version in all machine XML files
|
||||
3. Make changes
|
||||
4. Run "grunt" to build new versions of the apps (eg, "/versions/pcjs/1.xx/pc.js")
|
||||
|
||||
The `eb branch` command will display:
|
||||
However, you may want to skip step #2 until you're ready to start testing the new version. Depending on the nature
|
||||
of your changes, it may be better to manually edit the version number in only a few machine XML files for testing,
|
||||
leaving the rest of the XML files pointing to the previous version. Run "grunt promote" when the new version is much
|
||||
closer to being released.
|
||||
|
||||
The current branch is "node_dev".
|
||||
Enter an AWS Elastic Beanstalk environment name (auto-generated value is "pcjs-nodedev-env"): pcjs-dev
|
||||
Do you want to copy the settings from environment "pcjs-env" for the new branch? [y/n]: y
|
||||
|
||||
after which we're ready for `git aws.push` to the new "pcjs-dev" environment.
|
||||
### Testing
|
||||
|
||||
See the "[Develop, Test, and Deploy](http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_nodejs.sdlc.html)"
|
||||
AWS documentation for more details.
|
||||
In the course of testing PCjs, there may be stale "index.html" files that prevent you from seeing application
|
||||
updates, changes to README.md files, etc. So, before running Node, you may want to "touch" the default HTML template:
|
||||
|
||||
On a local machine, the process is similar. If this is a "virgin" machine, you must first install Node and NPM;
|
||||
see my notes on "[Installing Node (and NPM)](/my_modules/#installing-node-and-npm)" for more details.
|
||||
|
||||
Then install and run the PCjs web server files:
|
||||
|
||||
[iMac:~/Sites] git clone git@github.com:jeffpar/jsmachines.git pcjs
|
||||
[iMac:~/Sites] cd pcjs
|
||||
[iMac:~/Sites/pcjs] git checkout node_dev
|
||||
[iMac:~/Sites/pcjs] npm install --production
|
||||
[iMac:~/Sites/pcjs] export PORT=8086 (this is optional)
|
||||
[iMac:~/Sites/pcjs] node server.js
|
||||
|
||||
For a machine that is simply out-of-date, you can do something like this:
|
||||
|
||||
[iMac:~/Sites/pcjs] git pull
|
||||
[iMac:~/Sites/pcjs] npm update --production
|
||||
[iMac:~/Sites/pcjs] export PORT=8086 (this is optional)
|
||||
[iMac:~/Sites/pcjs] node server.js
|
||||
|
||||
In the second scenario, there may be stale "index.html" files that prevent you from seeing the latest versions
|
||||
of everything, so before running Node, you may want to do this first:
|
||||
|
||||
[iMac:~/Sites/pcjs] touch my_modules/shared/templates/common.html
|
||||
touch my_modules/shared/templates/common.html
|
||||
|
||||
The [HTMLOut](/my_modules/htmlout/) module compares the timestamp of that template file to the timestamp of any
|
||||
"index.html" and will regenerate the latter if it's out-of-date. There's a TODO to expand that check to include
|
||||
the timestamp of any local README.md file, but there are many other factors that contribute to stale "index.html"
|
||||
files, so the safest thing to do is touch the [common.html](/my_modules/shared/templates/common.html) template,
|
||||
or delete all existing "index.html" files -- either by hand, or by using:
|
||||
"index.html" and will regenerate the latter if it's out-of-date.
|
||||
|
||||
[iMac:~/Sites/pcjs] grunt clean
|
||||
|
||||
However, if you included "--production" in your NPM install/update commands, you won't have the necessary grunt
|
||||
task files. You may not even have grunt itself installed, unless you've previously run:
|
||||
There's a TODO to expand that check to include the timestamp of any local README.md file, but there are many other
|
||||
factors that can contribute to stale "index.html" files, so usually the safest thing to do is "touch" the
|
||||
[common.html](/my_modules/shared/templates/common.html) template, or delete all existing "index.html" files, either
|
||||
manually or with the Grunt "clean" task:
|
||||
|
||||
[iMac:~/Sites/pcjs] sudo npm install grunt-cli -g
|
||||
|
||||
Stale "index.html" files may be a non-issue on AWS, because it appears to create a completely new directory
|
||||
structure when it rebuilds the environment following a `git aws.push` -- but I'm not sure that's always true.
|
||||
grunt clean
|
||||
|
||||
<!--END:EXCLUDE-->
|
||||
|
||||
|
|
|
|||
|
|
@ -1,649 +1,7 @@
|
|||
Private Node Modules
|
||||
===
|
||||
|
||||
**my_modules** contains all the *private* **JSMachines** Node modules. It is the counterpart to
|
||||
**my_modules** contains all the *private* **PCjs** Node modules. It is the counterpart to
|
||||
**node_modules**, where all the *public* Node modules are installed. Only private Node modules are checked
|
||||
into GitHub; if we later decide to publish any or all of these on [npmjs.org](http://npmjs.org), then they will
|
||||
into GitHub; if any of these are later published on [npmjs.org](http://npmjs.org), then they will
|
||||
be moved to **node_modules** and removed from this folder.
|
||||
|
||||
The rest of this file documents some of the early steps involved in porting the **JSMachines** project,
|
||||
which required a web server running PHP, to the new **PCjs** web server running Node. That includes setting up
|
||||
my first Node web server and then extending its functionality with these private Node modules.
|
||||
|
||||
For more details on the private Node modules stored here, refer to the README.md for each module (which, alas,
|
||||
may be nothing more than placeholders at this point, but I have good intentions).
|
||||
|
||||
Creating My First Node Project
|
||||
===
|
||||
|
||||
Plan A: Fork jsmachines.net -> pcjs.org
|
||||
---
|
||||
The original **JSMachines** project assumed a web server running PHP
|
||||
(eg, [http://jsmachines.net](http://jsmachines.net)). However, to improve client-server integration, reduce
|
||||
the number of languages/frameworks used, and implement some new features, such as socket-based disk I/O interfaces,
|
||||
I decided to create a fork, **PCjs**, and port all the server-side PHP to JavaScript, running on a Node web server.
|
||||
|
||||
Unfortunately, when I tried to fork my private **JSMachines** project on GitHub, I discovered that you're
|
||||
not allowed to [fork](https://help.github.com/articles/fork-a-repo) your own projects. Adding to the confusion: GitHub
|
||||
displays the "Fork" button on my project's home page, and even lets me click it, but then quietly does nothing.
|
||||
|
||||
The recommended work-around (as per [this post](http://bitdrift.com/post/4534738938/fork-your-own-project-on-github))
|
||||
seemed to be:
|
||||
|
||||
- Create new project on GitHub
|
||||
- Clone old project with new name
|
||||
- Replace new project's "origin URL" with URL for new GitHub project
|
||||
- Add original project as an "upstream source"
|
||||
- Push new project to GitHub
|
||||
|
||||
Plan B: Branch jsmachines -> node_dev
|
||||
---
|
||||
The above seemed a bit clunky, so I decided to branch instead of fork. This new development branch was called
|
||||
**node_pcjs**, but I've since branched to **node_dev**. I should probably remove the original **node_pcjs** branch,
|
||||
but for now, it's still there.
|
||||
|
||||
Then I pushed the **node_dev** branch to the remote, as per [GitHub](https://help.github.com/articles/pushing-to-a-remote):
|
||||
|
||||
[~/Sites/jsmachines] git push origin node_dev
|
||||
Total 0 (delta 0), reused 0 (delta 0)
|
||||
To git@github.com:jeffpar/jsmachines.git
|
||||
* [new branch] node_dev -> node_dev
|
||||
[~/Sites/jsmachines]
|
||||
|
||||
That seemed to work fine, so I then created a new folder and cloned the project, as per
|
||||
[GitHub](https://help.github.com/articles/fetching-a-remote):
|
||||
|
||||
[~/Sites] git clone git@github.com:jeffpar/jsmachines.git pcjs
|
||||
Cloning into 'pcjs'...
|
||||
remote: Reusing existing pack: 2725, done.
|
||||
remote: Total 2725 (delta 0), reused 0 (delta 0)
|
||||
Receiving objects: 100% (2725/2725), 59.48 MiB | 2.67 MiB/s, done.
|
||||
Resolving deltas: 100% (1543/1543), done.
|
||||
Checking connectivity... done
|
||||
|
||||
Then I checked-out the new branch:
|
||||
|
||||
[~/Sites] cd pcjs
|
||||
[~/Sites/pcjs] git checkout node_dev
|
||||
Branch node_dev set up to track remote branch node_dev from origin.
|
||||
Switched to a new branch 'node_dev'
|
||||
|
||||
If I have to make any changes to the "master" branch (ie, the original jsmachines project) in the interim,
|
||||
then after I've pushed those changes to GitHub, I can pull them into the **node_dev** branch like so:
|
||||
|
||||
[~/Sites/pcjs] git pull origin master
|
||||
|
||||
Installing Node (and NPM)
|
||||
---
|
||||
I downloaded the latest Node installation package for my MacBook from [nodejs.org](http://nodejs.org/),
|
||||
which provided the usual `node` and `npm` commands. Here are the versions they reported:
|
||||
|
||||
[~/Sites/pcjs] node -v
|
||||
v0.10.26
|
||||
[~/Sites/pcjs] npm -v
|
||||
1.4.3
|
||||
|
||||
I also installed a Node helper on my MacBook called `n` that makes it easy to keep Node up-to-date, per this tip on
|
||||
[stackoverflow](http://stackoverflow.com/questions/8191459/how-to-update-node-js-npm-and-all-other-dependencies):
|
||||
|
||||
1) Clear NPM's cache:
|
||||
|
||||
[~/Sites/pcjs] sudo npm cache clean -f
|
||||
|
||||
2) Install a little helper called 'n':
|
||||
|
||||
[~/Sites/pcjs] sudo npm install -g n
|
||||
|
||||
3) Install latest stable NodeJS version:
|
||||
|
||||
[~/Sites/pcjs] sudo n stable
|
||||
|
||||
Alternatively pick a specific version and install like so:
|
||||
|
||||
[~/Sites/pcjs] sudo n 0.8.20
|
||||
|
||||
I'm not sure I like the generic name for this command ("n"); what would have been wrong with "node-update"
|
||||
or "update-node"? Oh well.
|
||||
|
||||
Next, I wanted to make sure `npm` was up-to-date. According to
|
||||
[FAQ on npmjs.org](https://npmjs.org/doc/faq.html#How-do-I-update-npm), that's as easy as:
|
||||
|
||||
[~/Sites/pcjs] sudo npm update npm -g
|
||||
|
||||
although occasionally `npm` won't be able to update itself, in which case the fallback is:
|
||||
|
||||
[~/Sites/pcjs] curl https://npmjs.org/install.sh | sh
|
||||
|
||||
So, I made sure `npm` itself, along with `n` and all my other globally installed packages, were up-to-date,
|
||||
and then I used `n` to make sure I was running the latest stable version of Node:
|
||||
|
||||
[~/Sites/pcjs] sudo npm update -g
|
||||
[~/Sites/pcjs] sudo n stable
|
||||
[~/Sites/pcjs] node -v
|
||||
v0.10.26
|
||||
|
||||
I've also seen suggestions to run this command before the preceding commands, but I don't know how often this is
|
||||
recommended:
|
||||
|
||||
[~/Sites/pcjs] sudo npm cache clean -f
|
||||
|
||||
Next, I ran `npm init`:
|
||||
|
||||
[~/Sites/pcjs] npm adduser
|
||||
Username: jeffpar
|
||||
Password:
|
||||
Email: (this IS public) jeff@pcjs.org
|
||||
npm http PUT http://registry.npmjs.org/-/user/org.couchdb.user:jeffpar
|
||||
npm http 201 http://registry.npmjs.org/-/user/org.couchdb.user:jeffpar
|
||||
|
||||
[~/Sites/pcjs] npm init
|
||||
This utility will walk you through creating a package.json file.
|
||||
It only covers the most common items, and tries to guess sane defaults.
|
||||
|
||||
See `npm help json` for definitive documentation on these fields
|
||||
and exactly what they do.
|
||||
|
||||
Use `npm install <pkg> --save` afterwards to install a package and
|
||||
save it as a dependency in the package.json file.
|
||||
|
||||
Press ^C at any time to quit.
|
||||
name: (pcjs)
|
||||
version: (0.0.0) 0.1.0
|
||||
description: Node-enabled version of PCjs
|
||||
entry point: (server.js)
|
||||
test command:
|
||||
git repository: (git://github.com/jeffpar/jsmachines.git)
|
||||
keywords: pcjs,ibm pc,emulator
|
||||
author: Jeff Parsons <Jeff@pcjs.org>
|
||||
license: (ISC)
|
||||
About to write to /Users/Jeff/Sites/pcjs/package.json:
|
||||
|
||||
{
|
||||
"name": "pcjs",
|
||||
"version": "0.1.0",
|
||||
"description": "Node-enabled version of PCjs",
|
||||
"main": "server.js",
|
||||
"directories": {
|
||||
"doc": "docs",
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/jeffpar/jsmachines.git"
|
||||
},
|
||||
"keywords": [
|
||||
"pcjs",
|
||||
"ibm",
|
||||
"pc",
|
||||
"emulator"
|
||||
],
|
||||
"author": "Jeff Parsons <Jeff@pcjs.org>",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jeffpar/jsmachines/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jeffpar/jsmachines"
|
||||
}
|
||||
|
||||
Is this ok? (yes)
|
||||
|
||||
Next, I installed `express` using `npm install --save express`:
|
||||
|
||||
[~/Sites/pcjs] npm install --save express
|
||||
npm http GET http://registry.npmjs.org/express
|
||||
...
|
||||
express@3.4.8 node_modules/express
|
||||
├── methods@0.1.0
|
||||
├── merge-descriptors@0.0.1
|
||||
├── range-parser@0.0.4
|
||||
├── cookie-signature@1.0.1
|
||||
├── fresh@0.2.0
|
||||
├── debug@0.7.4
|
||||
├── buffer-crc32@0.2.1
|
||||
├── cookie@0.1.0
|
||||
├── mkdirp@0.3.5
|
||||
├── commander@1.3.2 (keypress@0.1.0)
|
||||
├── send@0.1.4 (mime@1.2.11)
|
||||
└── connect@2.12.0 (uid2@0.0.3, pause@0.0.1, qs@0.6.6, bytes@0.2.1, raw-body@1.1.2, batch@0.5.0, negotiator@0.3.0, multiparty@2.2.0)
|
||||
|
||||
This added the following lines to my "package.json" file:
|
||||
|
||||
"dependencies": {
|
||||
"express": "~3.4.8"
|
||||
}
|
||||
|
||||
Then I created a test "server.js" in the root of the project:
|
||||
|
||||
var http = require("http");
|
||||
var express = require("express");
|
||||
var app = express();
|
||||
app.get("/", function(req, res) {
|
||||
res.end("Testing");
|
||||
});
|
||||
http.createServer(app).listen(3000);
|
||||
|
||||
and ran `node server.js` and verified that all was working as expected.
|
||||
|
||||
Creating My First Node Module
|
||||
---
|
||||
The first module I decided to write was a port of the diskconv.php script I'd written to convert disk images to/from
|
||||
JSON. I decided to call this new module **DiskDump**, since it will output disk images in a variety of formats, although
|
||||
initially only as JSON.
|
||||
|
||||
[~/Sites/pcjs/my_modules] mkdir diskdump
|
||||
[~/Sites/pcjs/my_modules] cd diskdump
|
||||
[~/Sites/pcjs/my_modules/diskdump] npm init
|
||||
This utility will walk you through creating a package.json file.
|
||||
It only covers the most common items, and tries to guess sane defaults.
|
||||
|
||||
See `npm help json` for definitive documentation on these fields
|
||||
and exactly what they do.
|
||||
|
||||
Use `npm install <pkg> --save` afterwards to install a package and
|
||||
save it as a dependency in the package.json file.
|
||||
|
||||
Press ^C at any time to quit.
|
||||
name: (diskdump)
|
||||
version: (0.0.0) 0.1.0
|
||||
description: Convert disk images to/from JSON
|
||||
entry point: (index.js)
|
||||
test command:
|
||||
git repository:
|
||||
keywords:
|
||||
author: Jeff Parsons <Jeff@pcjs.org>
|
||||
license: (ISC)
|
||||
About to write to /Users/Jeff/Sites/pcjs/my_modules/diskdump/package.json:
|
||||
|
||||
{
|
||||
"name": "diskdump",
|
||||
"version": "0.1.0",
|
||||
"description": "Convert disk images to/from JSON",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Jeff Parsons <Jeff@pcjs.org>",
|
||||
"license": "ISC"
|
||||
}
|
||||
|
||||
Is this ok? (yes)
|
||||
|
||||
Next, I followed the steps outlined [here](http://www.anupshinde.com/posts/how-to-create-nodejs-npm-package/) to
|
||||
create a package skeleton with a CLI (command-line interface).
|
||||
|
||||
Filtering Web Server Requests
|
||||
---
|
||||
The next module I created was [HTMLOut](/my_modules/htmlout/), which provides a filter() function for the Express module,
|
||||
taking an early crack at the server's HTTP requests.
|
||||
|
||||
If the request is for a directory, **HTMLOut** builds an "index.html" from a common HTML template file, and then saves
|
||||
the file in that directory, so that future requests can be served by the Express static() function. The HTML template
|
||||
file can specify a variety of replacement tokens, including a reference to a "README.md" file in the same directory,
|
||||
which **HTMLOut** will load and pass to the private **MarkOut** Node module for conversion from Markdown to HTML.
|
||||
|
||||
Similarly, if the URL is an API request, **HTMLOut** will direct the request to the appropriate private Node module
|
||||
(eg, **DiskDump** or **FileDump**).
|
||||
|
||||
What's The Right Way To Incorporate Node Module Fixes?
|
||||
---
|
||||
My current version of Express was 3.4.8, and looking at its dependencies in package.json, it wanted "fresh" v0.2.0.
|
||||
|
||||
Unfortunately, there seemed to be a problem with Safari and that version of "fresh", occasionally resulting in
|
||||
blank pages, as discussed [here](http://stackoverflow.com/questions/18811286/nodejs-express-cache-and-304-status-code):
|
||||
|
||||
Safari sends Cache-Control: max-age=0 on reload. Express (or more specifically, Express's dependency, node-fresh)
|
||||
considers the cache stale when Cache-Control: no-cache headers are received, but it doesn't do the same for
|
||||
Cache-Control: max-age=0.
|
||||
|
||||
Further investigation revealed that [node-fresh](https://github.com/visionmedia/node-fresh) v0.2.1 added a fix for
|
||||
that problem. But what was the right (or best) way to incorporate that fix into my local Express installation?
|
||||
|
||||
I decided to hand-edit the "fresh" dependency in Express' package.json and then run "npm update" in the Express folder.
|
||||
That pulled in the newer "fresh" module. But I noticed that other modules that Express depends on include their OWN
|
||||
version of "fresh", which were also locked at v0.2.0. Even after creating an "npm-shrinkwrap.json" and hand-editing
|
||||
it, "npm update" refused to upgrade them from v0.2.0. I could edit the package.json files in each of the Express
|
||||
"connect" and "send" folders, run "npm update" in those folders, and force v0.2.1 that way, but as soon as I ran another
|
||||
"npm update" in the root, those folders reverted to v0.2.0 again.
|
||||
|
||||
However, this all turned out to be moot. The "Cache-Control: max-age=0" work-around added to "fresh" v0.2.1 was later
|
||||
backed-out in v0.2.2. And I discovered that the "blank page" problem went away at the same time I added trailing-slash
|
||||
redirects (see below), so there was likely a connection. The underlying issue with Safari is discussed in more detail
|
||||
[here](https://github.com/visionmedia/node-fresh/issues/8); it seems likely the problem wasn't related to "fresh" after all.
|
||||
|
||||
Problems With Directory URLs and Slashes
|
||||
---
|
||||
I discovered that I needed to install "express-slash", enable Express "strict routing", and make HTMLOut.filter()
|
||||
pass on directory requests that didn't include a trailing slash, so that the Node web server could call slash() and
|
||||
trigger a trailing-slash redirect. This is apparently what apache's "mod_dir" module does automatically, and why
|
||||
I never had a problem with apache. Without trailing slashes on directories, client-side requests for files relative to
|
||||
those "slash-less" directories can fail. More details on this issue can be found in [htmlout.js](htmlout/lib/htmlout.js).
|
||||
|
||||
npm install express-slash --save
|
||||
|
||||
Moving from Make to Grunt
|
||||
---
|
||||
Step 1:
|
||||
|
||||
sudo npm install grunt-cli -g
|
||||
|
||||
Step 2:
|
||||
|
||||
npm install grunt --save-dev
|
||||
|
||||
which automatically adds the following to my package.json:
|
||||
|
||||
"devDependencies": {
|
||||
"grunt": "^0.4.3"
|
||||
}
|
||||
|
||||
Step 3: Create a basic Gruntfile.js:
|
||||
|
||||
module.exports = function(grunt) {
|
||||
grunt.initConfig({
|
||||
});
|
||||
grunt.registerTask("default", []);
|
||||
};
|
||||
|
||||
Step 4: Install some grunt modules; eg:
|
||||
|
||||
npm install grunt-contrib-uglify --save-dev
|
||||
|
||||
Step 5: Add a task (eg, uglify):
|
||||
|
||||
module.exports = function(grunt) {
|
||||
grunt.initConfig({
|
||||
uglify: {
|
||||
dist: {
|
||||
src: "dist/myfile.js",
|
||||
dest: "dist/myfile.min.js"
|
||||
}
|
||||
}
|
||||
});
|
||||
grunt.loadNpmTasks("grunt-contrib-uglify");
|
||||
grunt.registerTask("default", ["uglify"]);
|
||||
};
|
||||
|
||||
However, while all of the above is a very nice example, I'm not really interested in uglify.
|
||||
|
||||
I use Google's Closure Compiler, so I needed to do this instead:
|
||||
|
||||
npm install grunt-closure-tools --save-dev
|
||||
|
||||
There was also an NPM module called "grunt-closure-compiler", but it hadn't been updated as recently, and from
|
||||
the examples it provided, it wasn't clear that it included support for all the Closure Compiler options, like
|
||||
"output_wrapper", so I went with "grunt-closure-tools", even though it also contains support for other tools that
|
||||
I'm not currently using.
|
||||
|
||||
So now my package.json includes:
|
||||
|
||||
"devDependencies": {
|
||||
"grunt": "^0.4.3",
|
||||
"grunt-closure-tools": "^0.9.4"
|
||||
}
|
||||
|
||||
Optional Node Modules
|
||||
===
|
||||
|
||||
replace
|
||||
---
|
||||
I use the Node "replace" module to perform project-wide search-and-replace operations on files; eg:
|
||||
|
||||
node node_modules/replace/bin/replace.js '(<machine.*class)="c1pjs"' '$1="c1p"' . -r --include=*.xml --preview
|
||||
node node_modules/replace/bin/replace.js '(<machine.*class)="pcjs"' '$1="pc"' . -r --include=*.xml --preview
|
||||
node node_modules/replace/bin/replace.js '(href="/versions/c1pjs)/[^/]*/(.*)' '$1/1.12.1/$2' . -r --include=*.xml --preview
|
||||
node node_modules/replace/bin/replace.js '(href="/versions/pcjs)/[^/]*/(.*)' '$1/1.12.1/$2' . -r --include=*.xml --preview
|
||||
|
||||
Most people probably install modules like this globally, but I'd rather just define a command-line alias
|
||||
("replace" => "node node_modules/replace/bin/replace.js") instead of cluttering my system-level directories.
|
||||
|
||||
Testing with Azure
|
||||
===
|
||||
> NOTE: I've since abandoned Azure in favor of AWS Elastic Beanstalk, because Amazon's service has a simpler UI,
|
||||
provides more control over the virtual machine environment (you can even log into the machine via SSH), and
|
||||
the logging services for Node on AWS are more reliable. I also wrote a short [blog post](/blog/2014/03/30/)
|
||||
on some of my frustrations with Azure.
|
||||
|
||||
I subscribed to Azure (formerly Windows Azure, now [Microsoft Azure](http://azure.microsoft.com/)), and started
|
||||
watching this YouTube video "[Lightning nodejs dev in Windows Azure](http://www.youtube.com/watch?v=WbtV1bX_m2I)"
|
||||
by Glenn Block ([@gblock](http://twitter.com/gblock)). I was glad to see that he used a Mac, because a number of
|
||||
other Azure videos on Youtube were made using Windows.
|
||||
|
||||
The first recommendation was to install the "azure-cli" command-line tools:
|
||||
|
||||
sudo npm install azure-cli -g
|
||||
|
||||
To display available commands:
|
||||
|
||||
azure
|
||||
|
||||
To connect the command-line tools to my Azure account (after setting my default browser to Chrome, since I used
|
||||
Chrome to set up my Azure account and browse the Azure portal):
|
||||
|
||||
azure account download
|
||||
|
||||
To import my acount info:
|
||||
|
||||
azure account import ~/Downloads/Pay-As-You-Go-3-30-2014-credentials.publishsettings
|
||||
|
||||
To create a site, the video recommends:
|
||||
|
||||
azure site create pcjs --git
|
||||
|
||||
However, I'm holding off until I understand how to link a new Azure site deployment with an existing GitHub repository,
|
||||
as discussed [here](http://www.windowsazure.com/en-us/documentation/articles/web-sites-publish-source-control/),
|
||||
because the above command appears intended for new project folders, which are then associated with a remote Azure-hosted
|
||||
Git repository -- not what I need or want.
|
||||
|
||||
The following command would show the remote Azure repository:
|
||||
|
||||
git remote -v
|
||||
|
||||
In my case, that command shows:
|
||||
|
||||
origin git@github.com:jeffpar/jsmachines.git (fetch)
|
||||
origin git@github.com:jeffpar/jsmachines.git (push)
|
||||
|
||||
The following command would then be used to push updates to Azure:
|
||||
|
||||
git push azure master
|
||||
|
||||
Although in my case, I'm guessing it would be:
|
||||
|
||||
git push origin master
|
||||
|
||||
Or, rather:
|
||||
|
||||
git push origin node_dev
|
||||
|
||||
Since my local "pcjs" project is based on the **node_dev** branch.
|
||||
|
||||
If I was using an Azure-hosted Git repository, I could then run:
|
||||
|
||||
azure site browse
|
||||
|
||||
because the `git push azure master` command would have triggered a website update.
|
||||
|
||||
The video goes on to show how I can specify the version Node I want Azure to use, by adding the
|
||||
following to my "package.json":
|
||||
|
||||
"engines": {
|
||||
"node": "0.8.x"
|
||||
}
|
||||
|
||||
This command:
|
||||
|
||||
azure site deployment list
|
||||
|
||||
will list all the deployments so far, any one of which can be redeployed like so:
|
||||
|
||||
azure site deployment redeploy xxx
|
||||
|
||||
The video finally gets that what I *really* care about, which is deploying via GitHub. This command
|
||||
lists some of the commands we care about:
|
||||
|
||||
azure site create --help
|
||||
|
||||
To create a site from a repository on GitHub:
|
||||
|
||||
azure site create pcjs --github
|
||||
|
||||
JavaScript Coding Conventions
|
||||
===
|
||||
|
||||
This is not intended to be an exhaustive list. It just lists a few things that came to mind, and I'll
|
||||
probably update it over time.
|
||||
|
||||
Tabs
|
||||
---
|
||||
I've configured my IDE (PhpStorm) to NEVER use tab characters in .js files (spaces only) and to
|
||||
ALWAYS use tab characters in almost every other type of text file. This is largely because when a web
|
||||
browser displays a JavaScript file (either in the main window or in the Developer Tools window), tabs
|
||||
usually screw up the formatting, which I find annoying when I'm debugging. XML files, on the other hand,
|
||||
are usually reformatted by the browser, so in those cases, I opt for smaller files and use real tabs.
|
||||
|
||||
Moreover, most of the JavaScript delivered by a production server will have been pre-compiled by Google's
|
||||
Closure Compiler, which completely eliminates all non-essential whitespace, so this is really a development
|
||||
preference, with little to no impact on production files.
|
||||
|
||||
Regardless of the choice of tab character however, I almost always use 4-column tab stops, except
|
||||
in legacy .asm files, where 8-column tab stops were the norm. I know that 2-column tab stops have
|
||||
recently become "all the rage," especially in Node projects; NPM, for example, will rewrite package.json
|
||||
files, replacing all the 4-column tabs with 2-column tabs, and I don't fight that trend -- I just ignore it.
|
||||
|
||||
JSDoc
|
||||
---
|
||||
I've taken a great deal of care to "[JSDoc](http://usejsdoc.org/)-ify" nearly all my JavaScript code, not
|
||||
because I want to be able to generate documentation (although that's something to think about), but because
|
||||
it's the only way to tell both Google's Closure Compiler and my IDE (PhpStorm) exactly what data
|
||||
types are passed into/out of every function. The goals are to minimize the number of "code inspection"
|
||||
warnings in the IDE and produce warning-free compilations.
|
||||
|
||||
In order to use the Closure Compiler's ADVANCED_OPTIMIZATIONS option and get maximum performance
|
||||
(and maximum "minification", sometimes called "uglification"), every function and its parameters needs to
|
||||
be fully typed; otherwise, the Compiler will generate WAY too many warnings/errors -- at least, that was the
|
||||
case when I first started using it a couple of years ago.
|
||||
|
||||
So, I've adopted a zero-tolerance policy for warnings: no check-ins allowed if the Closure Compiler generates
|
||||
even a SINGLE warning.
|
||||
|
||||
I'm not sure the [JSDoc](http://usejsdoc.org/) folks and the
|
||||
[Closure Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler) are totally in sync on
|
||||
everything. And then there's [PhpStorm](http://www.jetbrains.com/phpstorm/webhelp/creating-jsdoc-comments.html),
|
||||
whose code inspections occasionally fail; sometimes a bogus code inspection warning can be fixed with some
|
||||
additional JSDoc @name or @class annotations, but not always.
|
||||
|
||||
In any case, the subset of variable and function type declarations I use works pretty well across the board;
|
||||
code inspection warnings are allowed in the IDE, as long as they are clearly erroneous (or clearly innocuous).
|
||||
|
||||
Braces, Parentheses, etc.
|
||||
---
|
||||
Most of my opening braces appear at the end of the line containing the associated "if", "while", "for", "switch",
|
||||
"function" etc, preceded by a single space. And most of my opening parentheses are also preceded by a single space,
|
||||
except when following "function" or a function name, in which case there is NO space. This is just an historical
|
||||
preference, dating back to my BASIC and C programming days; it's not that those preferences matter anymore, it's just
|
||||
that I see no reason to change them.
|
||||
|
||||
I'm not obsessed with these conventions. Sometimes, for example, I'll write a bunch of code where the opening
|
||||
brace of every function body begins underneath the "function" keyword, because the extra whitespace can make the
|
||||
file a bit more readable. It may sometimes depend on my mood, but I do try to be consistent within a given file
|
||||
at least.
|
||||
|
||||
Variable Names
|
||||
---
|
||||
I still tend to follow Charles Simonyi's "[Hungarian](http://en.wikipedia.org/wiki/Hungarian_notation)" naming
|
||||
conventions -- or rather, a naming convention loosely inspired by Hungarian. I know lots of people sneer at
|
||||
those conventions and think they're useless, and all I can say is, they're wrong: they are not useless to ME.
|
||||
I admit they may be useless to anyone who has a phenomenal memory and can remember that an obscure variable named
|
||||
"foo" was initialized with a string or a number, or who has an IDE that can answer that question with the press
|
||||
of a key (or two), but for me, with my non-phenomenal memory and lazy fingers, I prefer being able to simply
|
||||
look at a variable to immediately know what *type* of data it contains, if nothing else.
|
||||
|
||||
I rarely name a string or numeric variable something vague like "foo." At worst, I would name it "sFoo" if it
|
||||
was a string or "iFoo" if it was a number (or possibly "nFoo" or "cFoo" if it represented a total of Foos or a
|
||||
counter of Foos). And if a string or numeric variable has a very short-term use, I often just call it "s" or "i"
|
||||
(or "n").
|
||||
|
||||
As I mention [below](./#quotation-marks), I still tend to distinguish single characters from strings too,
|
||||
which means I may sometimes prefix character variables with "ch" and character counters with "cch".
|
||||
|
||||
Of course, these letter prefixes like "s" and "n" are irrelevant if you already give your variables meaningful
|
||||
names like "nameOfPerson" or "numberOfPeople". And that's fine -- I do that sometimes, too. But in general,
|
||||
I still prefer variable names like "sPerson" and "nPeople".
|
||||
|
||||
I don't try to come up with special prefixes for Objects. If there's a Person object, for example, I'll probably
|
||||
use colloquial names like "personHere" or "personThere". I am stricter about Arrays though: I prefix array variables
|
||||
with "a", arrays of strings and numbers with "as" and "ai" (or "an"), and arrays of arrays with "aa". As for Arrays
|
||||
of anything else, I usually don't bother with anything more than an "a" prefix.
|
||||
|
||||
Quotation Marks
|
||||
---
|
||||
Coming from a long C background, I prefer to use double-quotes around multi-character strings and single quotes
|
||||
around single-character strings. While the reasons for doing so are largely historical and currently irrelevant,
|
||||
characters are STILL the building blocks of strings, and even the JavaScript String class contains methods that
|
||||
deal with individual characters (eg, charCodeAt() and fromCharCode()). So for any code that knowingly deals with
|
||||
single characters, I like to reinforce that distinction with single quotes.
|
||||
|
||||
Also, to emphasize that an object property name isn't really a string, I tend to use single quotes around property
|
||||
names as well. I realize that the JSON standard insists on property names with double-quotes, but functions like
|
||||
JSON.stringify() take care of that for us -- besides, I find that aspect of the JSON standard to be rather annoying
|
||||
(not to mention JSON's disapproval of comments).
|
||||
|
||||
Obviously, it's possible to set and get object properties without any quotes at all, as in:
|
||||
|
||||
obj.prop = true;
|
||||
|
||||
instead of:
|
||||
|
||||
obj['prop'] = true;
|
||||
|
||||
but that works only if the property name conforms to the same syntax that variable names use. Also, the "dot"
|
||||
syntax can create problems for code compiled with Google's Closure Compiler (using ADVANCED_OPTIMIZATIONS),
|
||||
because it likes to rename "dot" property names to smaller "minified" property names. Which means if you
|
||||
need to export your objects as JSON later, or if you ever need to access a property using an externally defined
|
||||
string, you must avoid the "dot" syntax.
|
||||
|
||||
However, if an object and all its properties are purely internal, there's usually no reason not to use it:
|
||||
|
||||
obj = {foo: "old", modified: false};
|
||||
obj.foo = "new";
|
||||
obj.modified = true;
|
||||
|
||||
I break my own quoting rules slightly when dealing with strings that *contain* double-quotes, since it's more readable
|
||||
to put double-quotes inside single-quoted strings than to "escape" every double-quote with a backslash.
|
||||
|
||||
For code that was originally written in PHP and later ported to JavaScript, there was a tendency in the original
|
||||
code to always use double-quotes around strings and "escape" double-quotes regardless, and that tendency may linger
|
||||
in code I didn't feel like rewriting much, but the tendency was due more to idiosyncrasies of PHP than any convention
|
||||
of mine; for example:
|
||||
|
||||
- single-quoted PHP strings may not include any escaped characters (except for single-quote and backslash)
|
||||
- single-quoted PHP strings cannot resolve references to string variables (eg, "the value of foo is {$foo}")
|
||||
|
||||
Because of PHP's restrictions on single-quoted strings, I tended to avoid them. However, in JavaScript, the
|
||||
first restriction isn't true, and the second isn't even a feature of the language, so they have no bearing on the use
|
||||
of single-quoted strings in JavaScript.
|
||||
|
||||
Arrays
|
||||
---
|
||||
In [this article](http://stackoverflow.com/questions/8668174/indexof-method-in-an-object-array), it's asserted,
|
||||
without disagreement, that searching arrays of objects for a particular object is best performed with code that looks
|
||||
like this:
|
||||
|
||||
pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');
|
||||
|
||||
I disagree. Depending on how often that function is called, creating a new array by iterating over every element
|
||||
of the original array, and doing that every time you want to search for an array object with a property (eg, `hello`)
|
||||
equal to some value (eg, 'stevie'), is a bad idea; I don't care how fast your JavaScript engine is or how fantastic
|
||||
your garbage collector is.
|
||||
|
||||
My preferred approach is to create parallel arrays: one array for each property that you might want to search on, and
|
||||
another array of objects that contain all the other object properties. Then searching doesn't involve creating new
|
||||
arrays:
|
||||
|
||||
pos = myArrayOfHello.indexOf('stevie');
|
||||
|
||||
Yes, it's not quite as *clean* as one, unified array of objects, and yes, it would be nicer if the JavaScript Array
|
||||
class had better search methods, but I think this is the right trade-off given the current reality.
|
||||
|
|
@ -72,8 +72,8 @@ var fCache = true;
|
|||
var fConsole = false;
|
||||
|
||||
/*
|
||||
* fServerDebug controls debug console messages; it is false by default and can be enabled using the setOptions()
|
||||
* 'debug' property (or from the command-line interface using "--debug").
|
||||
* fServerDebug controls server-related debug features; it is false by default and can be enabled using the
|
||||
* setOptions() 'debug' property (or from the command-line interface using "--debug").
|
||||
*/
|
||||
var fServerDebug = false;
|
||||
|
||||
|
|
@ -262,8 +262,11 @@ function HTMLOut(sDir, sFile, fRebuild, req, done)
|
|||
* of fDebug to simply never cache, instead of always rebuilding the cache.
|
||||
*
|
||||
* if (this.fDebug) this.fRebuild = true;
|
||||
*
|
||||
* Note that a production server should not need the GORT_REBUILD command, so we accept
|
||||
* it only if fServerDebug is true.
|
||||
*/
|
||||
if (net.hasParm(net.GORT_COMMAND, net.GORT_REBUILD, req)) {
|
||||
if (fServerDebug && net.hasParm(net.GORT_COMMAND, net.GORT_REBUILD, req)) {
|
||||
req.query[net.GORT_COMMAND] = undefined;
|
||||
this.fRebuild = true;
|
||||
}
|
||||
|
|
@ -887,9 +890,15 @@ HTMLOut.prototype.getVersion = function(sToken, sIndent, aParms)
|
|||
{
|
||||
/*
|
||||
* Use the same test that processMachines() uses for setting fCompiled: if we're not using compiled code,
|
||||
* then we should be using "current" CSS and template files (as opposed to version-specific template files) as well.
|
||||
* then we should be using "current" CSS and template files (as opposed to version-specific template files).
|
||||
*
|
||||
* NOTE: I used to create a symlink in each app's "versions" directory (eg, /versions/pcjs/current ->
|
||||
* ../../my_modules/shared/templates), so that when fDebug was true, I could simply insert "current" in
|
||||
* place of a version number. However, that symlink didn't get added to the repository, and I'm not sure
|
||||
* all operating systems would deal with it properly even if was added, so now I'm treating the "version"
|
||||
* token as the equivalent of a symlink here.
|
||||
*/
|
||||
this.aTokens[sToken] = this.fDebug? "current" : pkg.version;
|
||||
this.aTokens[sToken] = this.fDebug? "../../my_modules/shared/templates" : pkg.version;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1432,6 +1441,8 @@ HTMLOut.prototype.getManifestXML = function(sToken, sIndent, aParms)
|
|||
case "default":
|
||||
sDefault = matchParams[2];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sLabel) {
|
||||
|
|
|
|||
|
|
@ -386,6 +386,7 @@ MarkOut.prototype.convertMD = function(sIndent)
|
|||
*/
|
||||
var aMatch;
|
||||
var re = /(^|\n)( ? ?)([\*+-]|[0-9]+\.)([^\n]*\n)([ \t]+[^\n]*\n|\n)+([ \t]+[^\n]+)/g;
|
||||
//noinspection UnnecessaryLocalVariableJS
|
||||
var sMDOrig = sMD;
|
||||
while ((aMatch = re.exec(sMDOrig))) {
|
||||
var sReplace = aMatch[0].replace(/\n\n/g, "\n\t\n");
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ Structure
|
|||
All the code for PCjs is contained in the following JavaScript files, which roughly divide the
|
||||
functionality into major PC components, aka "devices". However, not every file implements a device,
|
||||
and "component" is an overloaded term, since *[Component](/docs/pcjs/component/)* is also the name of
|
||||
the shared base class used for most PCjs objects (see [component.js](../../shared/lib/component.js)).
|
||||
the shared base class used for most PCjs devices (see [component.js](../../shared/lib/component.js)).
|
||||
So it's best to refer to these files generically as "modules", and more specifically as "device modules"
|
||||
whenever they implement a specific device (or set of devices, in the case of [*Chipset*](/docs/pcjs/chipset/)).
|
||||
|
||||
Examples of non-device modules include UI modules like [panel.js](panel.js) and [debugger.js](debugger.js),
|
||||
and sub-modules like [x86code.js](x86code.js), [x86mode.js](x86mode.js) and [x86help.js](x86help.js)
|
||||
and sub-modules like [x86opxx.js](x86opxx.js), [x86mods.js](x86mods.js) and [x86help.js](x86help.js)
|
||||
that separate the CPU functionality of [x86.js](x86.js) into more manageable pieces.
|
||||
|
||||
These modules should always be loaded or compiled in the order listed by the *pcJSFiles* property in
|
||||
|
|
@ -32,11 +32,14 @@ At the time of this writing, the order is:
|
|||
* [pcjs-client/bus.js](bus.js)
|
||||
* [pcjs-client/mem.js](mem.js)
|
||||
* [pcjs-client/cpu.js](cpu.js)
|
||||
* [pcjs-client/x86defs.js](x86defs.js)
|
||||
* [pcjs-client/x86help.js](x86help.js)
|
||||
* [pcjs-client/x86code.js](x86code.js)
|
||||
* [pcjs-client/x86mode.js](x86mode.js)
|
||||
* [pcjs-client/x86.js](x86.js)
|
||||
* [pcjs-client/x86seg.js](x86seg.js)
|
||||
* [pcjs-client/x86cpu.js](x86cpu.js)
|
||||
* [pcjs-client/x86grps.js](x86grps.js)
|
||||
* [pcjs-client/x86help.js](x86help.js)
|
||||
* [pcjs-client/x86mods.js](x86mods.js)
|
||||
* [pcjs-client/x86op0f.js](x86op0f.js)
|
||||
* [pcjs-client/x86opxx.js](x86opxx.js)
|
||||
* [pcjs-client/chipset.js](chipset.js)
|
||||
* [pcjs-client/rom.js](rom.js)
|
||||
* [pcjs-client/ram.js](ram.js)
|
||||
|
|
@ -55,16 +58,10 @@ At the time of this writing, the order is:
|
|||
Some of the modules *can* be reordered or even omitted (eg, [debugger.js](debugger.js) or
|
||||
[embed.js](../../shared/lib/embed.js)), but you should observe the following:
|
||||
|
||||
* [component.js](../../shared/lib/component.js) should be listed before any module that extends [*Component*](/docs/pcjs/component/)
|
||||
* [component.js](../../shared/lib/component.js) must be listed before any module that extends [*Component*](/docs/pcjs/component/)
|
||||
* [panel.js](panel.js) should be loaded early to initialize the Control Panel (if any) as soon as possible
|
||||
* [computer.js](computer.js) should be the last device module, as it supervises and notifies all the other device modules
|
||||
|
||||
To minimize ordering requirements, the init() handlers and constructors of all modules should avoid
|
||||
referencing other modules. Device modules should define an initBus() notification handler, which the
|
||||
[*Computer*](/docs/pcjs/computer/) will call after it has created/initialized the *Bus* object.
|
||||
|
||||
What's Next
|
||||
---
|
||||
EGA support. I've added the infrastructure for EGA I/O operations, but there's no "meat" yet. All the EGA
|
||||
code is in [video.js](video.js), alongside the CGA and MDA support, but EGA functionality is pretty isolated; you have
|
||||
to set the "model" attribute of the video component to "ega" to enable it.
|
||||
|
|
|
|||
|
|
@ -1713,12 +1713,14 @@ ChipSet.prototype.toggleSwitch = function(control)
|
|||
var asParts = sID.split("-");
|
||||
var b = (0x1 << (parseInt(asParts[1], 10) - 1));
|
||||
switch (asParts[0]) {
|
||||
case "sw1":
|
||||
this.sw1Init = (this.sw1Init & ~b) | (f? 0 : b);
|
||||
break;
|
||||
case "sw2":
|
||||
this.sw2Init = (this.sw2Init & ~b) | (f? 0 : b);
|
||||
break;
|
||||
case "sw1":
|
||||
this.sw1Init = (this.sw1Init & ~b) | (f? 0 : b);
|
||||
break;
|
||||
case "sw2":
|
||||
this.sw2Init = (this.sw2Init & ~b) | (f? 0 : b);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.updateSwitchDesc();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -72,34 +72,37 @@ var PREFETCH = false;
|
|||
/**
|
||||
* @define {boolean}
|
||||
*
|
||||
* FASTDISABLE turns on memory-function switching to dynamically disable
|
||||
* memory accesses whenever the CPU wants to disable spurious memory reads
|
||||
* (or stale memory writes, which are worse). If FASTDISABLE is false, then
|
||||
* the code falls back to setting/testing internal OP_NOREAD and OP_NOWRITE
|
||||
* CPU opFlags as needed.
|
||||
* FASTDISABLE turns on memory-function switching to dynamically disable memory accesses whenever the CPU wants
|
||||
* to disable spurious memory reads (which are mostly harmless) or stale memory writes (which tend to be destructive
|
||||
* and are NOT mostly harmless).
|
||||
*
|
||||
* At the moment, it seems that FASTDISABLE is a bit slower than relying on
|
||||
* the OP_NOREAD/OP_NOWRITE flags, so it's turned off.
|
||||
* If FASTDISABLE is false, then the code falls back to setting/testing internal OP_NOREAD and OP_NOWRITE opFlags
|
||||
* as needed.
|
||||
*
|
||||
* At the moment, it seems that FASTDISABLE is a bit slower than relying on the OP_NOREAD/OP_NOWRITE flags, so it's
|
||||
* turned off; apparently, I was a bit too optimistic calling it "FAST". But your mileage may vary, depending on the
|
||||
* browser and its version.
|
||||
*
|
||||
* See the Bus component for details.
|
||||
* See the X86CPU component for details, since it is the CPU, not the underlying Bus or Memory components, that needs
|
||||
* to be able to do this.
|
||||
*/
|
||||
var FASTDISABLE = false;
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
*
|
||||
* FATARRAYS is a Closure Compiler compile-time option that allocates 1 number per byte
|
||||
* for Memory blocks; wasteful, but slightly faster.
|
||||
* FATARRAYS is a Closure Compiler compile-time option that allocates 1 number per byte for Memory blocks;
|
||||
* wasteful, but potentially slightly faster.
|
||||
*
|
||||
* See the Memory component for details.
|
||||
*/
|
||||
var FATARRAYS = false;
|
||||
|
||||
/**
|
||||
* TYPEDARRAYS enables use of typed arrays for Memory blocks. This used to be a compile-time
|
||||
* option, but since I've added memory access functions for typed arrays (see Memory.afnTArray),
|
||||
* I can turn the support on dynamically now. Originally, I didn't see much of a speed increase
|
||||
* over the original (non-typed) implementation, but that will probably change over time.
|
||||
* TYPEDARRAYS enables use of typed arrays for Memory blocks. This used to be a compile-time * option, but since I've
|
||||
* added memory access functions for typed arrays (see Memory.afnTArray), I can turn the support on dynamically now.
|
||||
* Originally, I didn't see much of a speed increase over the original (non-typed) implementation, but that will probably
|
||||
* change over time.
|
||||
*
|
||||
* See the Memory component for details.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -439,20 +439,22 @@ Mouse.prototype.clickMouse = function(iButton, fDown) {
|
|||
if (this.isActive()) {
|
||||
var sDiag;
|
||||
switch (iButton) {
|
||||
case 0:
|
||||
if (this.fButton1 != fDown) {
|
||||
this.fButton1 = fDown;
|
||||
sDiag = DEBUGGER ? ("mouse button1 " + (fDown ? "dn" : "up")) : null;
|
||||
this.sendPacket(sDiag);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (this.fButton2 != fDown) {
|
||||
this.fButton2 = fDown;
|
||||
sDiag = DEBUGGER ? ("mouse button2 " + (fDown ? "dn" : "up")) : null;
|
||||
this.sendPacket(sDiag);
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
if (this.fButton1 != fDown) {
|
||||
this.fButton1 = fDown;
|
||||
sDiag = DEBUGGER ? ("mouse button1 " + (fDown ? "dn" : "up")) : null;
|
||||
this.sendPacket(sDiag);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (this.fButton2 != fDown) {
|
||||
this.fButton2 = fDown;
|
||||
sDiag = DEBUGGER ? ("mouse button2 " + (fDown ? "dn" : "up")) : null;
|
||||
this.sendPacket(sDiag);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -316,6 +316,9 @@ SerialPort.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, cont
|
|||
serial.sendRBR([charCode]);
|
||||
};
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3158,6 +3158,8 @@ Video.prototype.getAccess = function()
|
|||
case Card.GRC.DATAROT.XOR:
|
||||
nWriteAccess = Card.ACCESS.WRITE.MODE0XOR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
card.nDataRotate = regDataRotate & Card.GRC.DATAROT.COUNT;
|
||||
}
|
||||
|
|
@ -3463,6 +3465,8 @@ Video.prototype.checkMode = function(fForce)
|
|||
card.sizeBuffer = cbBufferText;
|
||||
nMode = (this.nMonitorType == ChipSet.MONITOR.MONO? Video.MODES.CGA_80X25_BW : Video.MODES.CGA_80X25);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
var fSEQDotClock = (card.aSEQRegs[Card.SEQ.CLK.INDX] & Card.SEQ.CLK.DOTCLOCK);
|
||||
|
|
@ -4532,6 +4536,8 @@ Video.prototype.outGRCData = function(port, bOut, addrFrom)
|
|||
case Card.GRC.BITMASK.INDX:
|
||||
this.cardEGA.nBitMapMask = bOut | (bOut << 8) | (bOut << 16) | (bOut << 24);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -344,6 +344,8 @@ var X86Help = {
|
|||
case 0x7: // BH
|
||||
this.regBX = (this.regBX & 0xff) | (dst << 8);
|
||||
break;
|
||||
default:
|
||||
break; // there IS no other case, but JavaScript inspections don't know that
|
||||
}
|
||||
this.nStepCycles -= this.nOpCyclesXchgRR;
|
||||
} else {
|
||||
|
|
@ -390,6 +392,8 @@ var X86Help = {
|
|||
case 0x7: // DI
|
||||
this.regDI = dst;
|
||||
break;
|
||||
default:
|
||||
break; // there IS no other case, but JavaScript inspections don't know that
|
||||
}
|
||||
this.nStepCycles -= this.nOpCyclesXchgRR;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1807,43 +1807,47 @@ var X86OpXX = {
|
|||
case 0x7: // this form of MOV to DS is undocumented on 8086/8088/80186/80188, invalid on 80286 and up
|
||||
temp = this.regDI;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
X86Mods.aOpModsRegWord[bModRM].call(this, X86Help.opHelpMOV);
|
||||
switch (reg) {
|
||||
case 0x0:
|
||||
this.setES(this.regAX);
|
||||
this.regAX = temp;
|
||||
break;
|
||||
case 0x1:
|
||||
this.setCS(this.regCX);
|
||||
this.regCX = temp;
|
||||
break;
|
||||
case 0x2:
|
||||
this.setSS(this.regDX);
|
||||
this.regDX = temp;
|
||||
break;
|
||||
case 0x3:
|
||||
this.setDS(this.regBX);
|
||||
this.regBX = temp;
|
||||
break;
|
||||
case 0x4:
|
||||
this.setES(this.regSP);
|
||||
this.regSP = temp;
|
||||
break;
|
||||
case 0x5:
|
||||
this.setCS(this.regBP);
|
||||
this.regBP = temp;
|
||||
break;
|
||||
case 0x6:
|
||||
this.setSS(this.regSI);
|
||||
this.regSI = temp;
|
||||
break;
|
||||
case 0x7:
|
||||
this.setDS(this.regDI);
|
||||
this.regDI = temp;
|
||||
break;
|
||||
case 0x0:
|
||||
this.setES(this.regAX);
|
||||
this.regAX = temp;
|
||||
break;
|
||||
case 0x1:
|
||||
this.setCS(this.regCX);
|
||||
this.regCX = temp;
|
||||
break;
|
||||
case 0x2:
|
||||
this.setSS(this.regDX);
|
||||
this.regDX = temp;
|
||||
break;
|
||||
case 0x3:
|
||||
this.setDS(this.regBX);
|
||||
this.regBX = temp;
|
||||
break;
|
||||
case 0x4:
|
||||
this.setES(this.regSP);
|
||||
this.regSP = temp;
|
||||
break;
|
||||
case 0x5:
|
||||
this.setCS(this.regBP);
|
||||
this.regBP = temp;
|
||||
break;
|
||||
case 0x6:
|
||||
this.setSS(this.regSI);
|
||||
this.regSI = temp;
|
||||
break;
|
||||
case 0x7:
|
||||
this.setDS(this.regDI);
|
||||
this.regDI = temp;
|
||||
break;
|
||||
default:
|
||||
break; // there IS no other case, but JavaScript inspections don't know that
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue