Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Initial commit, MVP

  • Loading branch information...
commit b354d4afa4fccd9e9ac9abf5b8bf25cec3ff7dd3 0 parents
Bryan Woods authored September 17, 2011

Showing 206 changed files with 52,876 additions and 0 deletions. Show diff stats Hide diff stats

  1. 13  bin/degrees
  2. 14  lib/degrees.coffee
  3. 4  node_modules/contextify/.gitignore
  4. 22  node_modules/contextify/LICENSE.txt
  5. 102  node_modules/contextify/README.md
  6. 9  node_modules/contextify/lib/contextify.js
  7. 31  node_modules/contextify/package.json
  8. 275  node_modules/contextify/src/contextify.cc
  9. 432  node_modules/contextify/test/contextify.js
  10. 18  node_modules/contextify/wscript
  11. 9  node_modules/cssom/.idea/CSSOM.iml
  12. 3  node_modules/cssom/.idea/dictionaries/nv.xml
  13. 5  node_modules/cssom/.idea/encodings.xml
  14. 17  node_modules/cssom/.idea/misc.xml
  15. 9  node_modules/cssom/.idea/modules.xml
  16. 82  node_modules/cssom/.idea/projectCodeStyle.xml
  17. 8  node_modules/cssom/.idea/vcs.xml
  18. 467  node_modules/cssom/.idea/workspace.xml
  19. 19  node_modules/cssom/.livereload
  20. 37  node_modules/cssom/Jakefile
  21. 33  node_modules/cssom/README.mdown
  22. 23  node_modules/cssom/Rakefile
  23. 19  node_modules/cssom/docs/.livereload
  24. 3  node_modules/cssom/docs/bar.css
  25. 0  node_modules/cssom/docs/demo.css
  26. 4  node_modules/cssom/docs/foo.css
  27. 170  node_modules/cssom/docs/parse.html
  28. 431  node_modules/cssom/docs/parse2.html
  29. 100  node_modules/cssom/index.html
  30. 34  node_modules/cssom/lib/CSSImportRule.js
  31. 38  node_modules/cssom/lib/CSSMediaRule.js
  32. 3  node_modules/cssom/lib/CSSOM.js
  33. 38  node_modules/cssom/lib/CSSRule.js
  34. 130  node_modules/cssom/lib/CSSStyleDeclaration.js
  35. 187  node_modules/cssom/lib/CSSStyleRule.js
  36. 85  node_modules/cssom/lib/CSSStyleSheet.js
  37. 61  node_modules/cssom/lib/MediaList.js
  38. 15  node_modules/cssom/lib/StyleSheet.js
  39. 69  node_modules/cssom/lib/clone.js
  40. 10  node_modules/cssom/lib/index.js
  41. 195  node_modules/cssom/lib/parse.js
  42. 17  node_modules/cssom/media.html
  43. 30  node_modules/cssom/package.json
  44. 32  node_modules/cssom/plugins/toHTML.js
  45. 22  node_modules/cssom/server/index.html
  46. 21  node_modules/cssom/server/index.js
  47. 21  node_modules/cssom/shorthands.html
  48. 35  node_modules/cssom/test/CSSStyleDeclaration.test.js
  49. 12  node_modules/cssom/test/CSSStyleRule.test.js
  50. 16  node_modules/cssom/test/CSSStyleSheet.test.js
  51. 21  node_modules/cssom/test/MediaList.test.js
  52. 38  node_modules/cssom/test/clone.test.js
  53. 3  node_modules/cssom/test/fixtures/dummy.css
  54. 97  node_modules/cssom/test/helper.js
  55. 42  node_modules/cssom/test/index.html
  56. 346  node_modules/cssom/test/parse.test.js
  57. 189  node_modules/cssom/test/vendor/qunit.css
  58. 1,341  node_modules/cssom/test/vendor/qunit.js
  59. 17  node_modules/htmlparser/.project
  60. 17  node_modules/htmlparser/.project.bak
  61. 6  node_modules/htmlparser/.settings/.jsdtscope
  62. 1  node_modules/htmlparser/.settings/org.eclipse.wst.jsdt.ui.superType.container
  63. 1  node_modules/htmlparser/.settings/org.eclipse.wst.jsdt.ui.superType.name
  64. 108  node_modules/htmlparser/.tmp_runtests.html.51164~
  65. 108  node_modules/htmlparser/.tmp_runtests.min.html.92328~
  66. 33  node_modules/htmlparser/CHANGELOG
  67. 18  node_modules/htmlparser/LICENSE
  68. 186  node_modules/htmlparser/README.md
  69. 35  node_modules/htmlparser/a
  70. 35  node_modules/htmlparser/b
  71. 35  node_modules/htmlparser/c
  72. 482  node_modules/htmlparser/json2.js
  73. 822  node_modules/htmlparser/lib/htmlparser.js
  74. 22  node_modules/htmlparser/lib/htmlparser.min.js
  75. 6  node_modules/htmlparser/lib/node-htmlparser.js
  76. 6  node_modules/htmlparser/lib/node-htmlparser.min.js
  77. BIN  node_modules/htmlparser/libxmljs.node
  78. 54  node_modules/htmlparser/newparser.js
  79. 754  node_modules/htmlparser/node-htmlparser.old.js
  80. 23  node_modules/htmlparser/package.json
  81. 4  node_modules/htmlparser/profile
  82. 53  node_modules/htmlparser/profile.getelement.js
  83. 199  node_modules/htmlparser/profile.getelement.txt
  84. 63  node_modules/htmlparser/profile.js
  85. 301  node_modules/htmlparser/profileresults.txt
  86. 9  node_modules/htmlparser/pulls/node-htmlparser/CHANGELOG
  87. 18  node_modules/htmlparser/pulls/node-htmlparser/LICENSE
  88. 186  node_modules/htmlparser/pulls/node-htmlparser/README.md
  89. 482  node_modules/htmlparser/pulls/node-htmlparser/json2.js
  90. 772  node_modules/htmlparser/pulls/node-htmlparser/lib/node-htmlparser.js
  91. 22  node_modules/htmlparser/pulls/node-htmlparser/lib/node-htmlparser.min.js
  92. 23  node_modules/htmlparser/pulls/node-htmlparser/package.json
  93. 63  node_modules/htmlparser/pulls/node-htmlparser/profile.js
  94. 107  node_modules/htmlparser/pulls/node-htmlparser/runtests.html
  95. 75  node_modules/htmlparser/pulls/node-htmlparser/runtests.js
  96. 107  node_modules/htmlparser/pulls/node-htmlparser/runtests.min.html
  97. 75  node_modules/htmlparser/pulls/node-htmlparser/runtests.min.js
  98. 15  node_modules/htmlparser/pulls/node-htmlparser/snippet.js
  99. 57  node_modules/htmlparser/pulls/node-htmlparser/tests/01-basic.js
  100. 35  node_modules/htmlparser/pulls/node-htmlparser/tests/02-single_tag_1.js
  101. 36  node_modules/htmlparser/pulls/node-htmlparser/tests/03-single_tag_2.js
  102. 52  node_modules/htmlparser/pulls/node-htmlparser/tests/04-unescaped_in_script.js
  103. 44  node_modules/htmlparser/pulls/node-htmlparser/tests/05-tags_in_comment.js
  104. 44  node_modules/htmlparser/pulls/node-htmlparser/tests/06-comment_in_script.js
  105. 45  node_modules/htmlparser/pulls/node-htmlparser/tests/07-unescaped_in_style.js
  106. 45  node_modules/htmlparser/pulls/node-htmlparser/tests/08-extra_spaces_in_tag.js
  107. 45  node_modules/htmlparser/pulls/node-htmlparser/tests/09-unquoted_attrib.js
  108. 39  node_modules/htmlparser/pulls/node-htmlparser/tests/10-singular_attribute.js
  109. 46  node_modules/htmlparser/pulls/node-htmlparser/tests/11-text_outside_tags.js
  110. 37  node_modules/htmlparser/pulls/node-htmlparser/tests/12-text_only.js
  111. 45  node_modules/htmlparser/pulls/node-htmlparser/tests/13-comment_in_text.js
  112. 53  node_modules/htmlparser/pulls/node-htmlparser/tests/14-comment_in_text_in_script.js
  113. 43  node_modules/htmlparser/pulls/node-htmlparser/tests/15-non-verbose.js
  114. 68  node_modules/htmlparser/pulls/node-htmlparser/tests/16-ignore_whitespace.js
  115. 34  node_modules/htmlparser/pulls/node-htmlparser/tests/17-xml_namespace.js
  116. 36  node_modules/htmlparser/pulls/node-htmlparser/tests/18-enforce_empty_tags.js
  117. 38  node_modules/htmlparser/pulls/node-htmlparser/tests/19-ignore_empty_tags.js
  118. 117  node_modules/htmlparser/pulls/node-htmlparser/tests/20-rss.js
  119. 77  node_modules/htmlparser/pulls/node-htmlparser/tests/21-atom.js
  120. 35  node_modules/htmlparser/pulls/node-htmlparser/utils_example.js
  121. 16  node_modules/htmlparser/rssbug.js
  122. 1  node_modules/htmlparser/rssbug.rss
  123. 108  node_modules/htmlparser/runtests.html
  124. 75  node_modules/htmlparser/runtests.js
  125. 108  node_modules/htmlparser/runtests.min.html
  126. 75  node_modules/htmlparser/runtests.min.js
  127. 15  node_modules/htmlparser/snippet.js
  128. 11  node_modules/htmlparser/testdata/.tmp_test.html.2854~
  129. 2,948  node_modules/htmlparser/testdata/.tmp_trackerchecker.html.65425~
  130. 10  node_modules/htmlparser/testdata/.tmp_trackerchecker.html.76922~
  131. 2,948  node_modules/htmlparser/testdata/.tmp_trackerchecker.html.80022~
  132. 2,948  node_modules/htmlparser/testdata/.tmp_trackerchecker2.html.51378~
  133. 2,947  node_modules/htmlparser/testdata/.tmp_trackerchecker2.html.75287~
  134. 3,311  node_modules/htmlparser/testdata/api.html
  135. 3,460  node_modules/htmlparser/testdata/getelement.html
  136. 2,733  node_modules/htmlparser/testdata/trackerchecker.html
  137. 61  node_modules/htmlparser/tests/01-basic.js
  138. 39  node_modules/htmlparser/tests/02-single_tag_1.js
  139. 40  node_modules/htmlparser/tests/03-single_tag_2.js
  140. 56  node_modules/htmlparser/tests/04-unescaped_in_script.js
  141. 48  node_modules/htmlparser/tests/05-tags_in_comment.js
  142. 48  node_modules/htmlparser/tests/06-comment_in_script.js
  143. 49  node_modules/htmlparser/tests/07-unescaped_in_style.js
  144. 49  node_modules/htmlparser/tests/08-extra_spaces_in_tag.js
  145. 49  node_modules/htmlparser/tests/09-unquoted_attrib.js
  146. 43  node_modules/htmlparser/tests/10-singular_attribute.js
  147. 50  node_modules/htmlparser/tests/11-text_outside_tags.js
  148. 41  node_modules/htmlparser/tests/12-text_only.js
  149. 49  node_modules/htmlparser/tests/13-comment_in_text.js
  150. 57  node_modules/htmlparser/tests/14-comment_in_text_in_script.js
  151. 46  node_modules/htmlparser/tests/15-non-verbose.js
  152. 71  node_modules/htmlparser/tests/16-ignore_whitespace.js
  153. 38  node_modules/htmlparser/tests/17-xml_namespace.js
  154. 40  node_modules/htmlparser/tests/18-enforce_empty_tags.js
  155. 41  node_modules/htmlparser/tests/19-ignore_empty_tags.js
  156. 120  node_modules/htmlparser/tests/20-rss.js
  157. 80  node_modules/htmlparser/tests/21-atom.js
  158. 100  node_modules/htmlparser/tests/22-position_data.js
  159. 35  node_modules/htmlparser/utils_example.js
  160. 1,956  node_modules/htmlparser/v8.log
  161. 6  node_modules/jsdom/.gitignore
  162. 22  node_modules/jsdom/LICENSE.txt
  163. 273  node_modules/jsdom/README.md
  164. 23  node_modules/jsdom/benchmark/mark.js
  165. 0  node_modules/jsdom/benchmark/stats.json
  166. 85  node_modules/jsdom/changelog
  167. 37  node_modules/jsdom/example/browser/browser.js
  168. 1,170  node_modules/jsdom/example/ender/ender.js
  169. 10  node_modules/jsdom/example/ender/run.js
  170. 167  node_modules/jsdom/example/jquery/jquery.js
  171. 9  node_modules/jsdom/example/jquery/run.js
  172. 243  node_modules/jsdom/example/node-xml/example.xml
  173. 46  node_modules/jsdom/example/node-xml/run.js
  174. 743  node_modules/jsdom/example/pure/pure.js
  175. 96  node_modules/jsdom/example/pure/run.js
  176. 10  node_modules/jsdom/example/pure/sax-test.js
  177. 535  node_modules/jsdom/example/pure/sax.js
  178. 166  node_modules/jsdom/example/sizzle/run.js
  179. 534  node_modules/jsdom/example/sizzle/sax.js
  180. 1,066  node_modules/jsdom/example/sizzle/sizzle.js
  181. 402  node_modules/jsdom/lib/jsdom.js
  182. 213  node_modules/jsdom/lib/jsdom/browser/domtohtml.js
  183. 145  node_modules/jsdom/lib/jsdom/browser/htmlencoding.js
  184. 178  node_modules/jsdom/lib/jsdom/browser/htmltodom.js
  185. 515  node_modules/jsdom/lib/jsdom/browser/index.js
  186. 1,723  node_modules/jsdom/lib/jsdom/level1/core.js
  187. 650  node_modules/jsdom/lib/jsdom/level2/core.js
  188. 431  node_modules/jsdom/lib/jsdom/level2/events.js
  189. 1,696  node_modules/jsdom/lib/jsdom/level2/html.js
  190. 7  node_modules/jsdom/lib/jsdom/level2/index.js
  191. 13  node_modules/jsdom/lib/jsdom/level2/languages/javascript.js
  192. 262  node_modules/jsdom/lib/jsdom/level2/style.js
  193. 646  node_modules/jsdom/lib/jsdom/level3/core.js
  194. 296  node_modules/jsdom/lib/jsdom/level3/events.js
  195. 9  node_modules/jsdom/lib/jsdom/level3/html.js
  196. 10  node_modules/jsdom/lib/jsdom/level3/index.js
  197. 221  node_modules/jsdom/lib/jsdom/level3/ls.js
  198. 1,859  node_modules/jsdom/lib/jsdom/level3/xpath.js
  199. 28  node_modules/jsdom/lib/jsdom/selectors/index.js
  200. 1,016  node_modules/jsdom/lib/jsdom/selectors/sizzle.js
  201. 138  node_modules/jsdom/package.json
  202. 22  node_modules/jsdom/status.json
  203. 279  node_modules/jsdom/test/DOMTestCase.js
  204. 4  node_modules/jsdom/test/LICENSE.txt
  205. 31  node_modules/jsdom/test/all.js
13  bin/degrees
... ...
@@ -0,0 +1,13 @@
  1
+#!/usr/local/bin/node
  2
+(function() {
  3
+  var WUNDERGROUND_URL, jsdom;
  4
+  jsdom = require('jsdom');
  5
+  WUNDERGROUND_URL = 'http://www.wunderground.com/cgi-bin/findweather/hdfForecast?query=11231';
  6
+  jsdom.env(WUNDERGROUND_URL, ['http://code.jquery.com/jquery-1.5.min.js'], function(errors, window) {
  7
+    var $, high, low;
  8
+    $ = window.jQuery;
  9
+    high = $('.fctHiLow .b').html();
  10
+    low = $('.fctHiLow').first().html().split('|')[1].split('°')[0].trim();
  11
+    return console.log("Today's high: " + high + "\nToday's low: " + low);
  12
+  });
  13
+}).call(this);
14  lib/degrees.coffee
... ...
@@ -0,0 +1,14 @@
  1
+jsdom = require 'jsdom'
  2
+
  3
+BASE_URL = "http://www.wunderground.com/cgi-bin/findweather/hdfForecast?query="
  4
+args = process.argv
  5
+zip  = if args[2] then args[2] else "11231"
  6
+
  7
+jsdom.env BASE_URL + zip,
  8
+  ['http://code.jquery.com/jquery-1.5.min.js'],
  9
+  (errors, window) ->
  10
+    $    = window.jQuery
  11
+    high = $('.fctHiLow .b').html()
  12
+    low  = $('.fctHiLow').first().html().split('|')[1].split('°')[0].trim()
  13
+
  14
+    console.log "Today's high: #{high}\nToday's low: #{low}"
4  node_modules/contextify/.gitignore
... ...
@@ -0,0 +1,4 @@
  1
+.lock-wscript
  2
+build/
  3
+*.swp
  4
+*.swo
22  node_modules/contextify/LICENSE.txt
... ...
@@ -0,0 +1,22 @@
  1
+Copyright (c) 2011 Brian McDaniel
  2
+
  3
+Permission is hereby granted, free of charge, to any person
  4
+obtaining a copy of this software and associated documentation
  5
+files (the "Software"), to deal in the Software without
  6
+restriction, including without limitation the rights to use,
  7
+copy, modify, merge, publish, distribute, sublicense, and/or sell
  8
+copies of the Software, and to permit persons to whom the
  9
+Software is furnished to do so, subject to the following
  10
+conditions:
  11
+
  12
+The above copyright notice and this permission notice shall be
  13
+included in all copies or substantial portions of the Software.
  14
+
  15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  17
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  19
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  20
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  21
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  22
+OTHER DEALINGS IN THE SOFTWARE.
102  node_modules/contextify/README.md
Source Rendered
... ...
@@ -0,0 +1,102 @@
  1
+# Contextify
  2
+
  3
+Turn an object into a V8 execution context.  A contextified object acts as the global 'this' when executing scripts in its context.  Contextify adds 3 methods to the contextified object: run(code, filename), getGlobal(), and dispose().  The main difference between Contextify and Node's vm methods is that Contextify allows asynchronous functions to continue executing in the Contextified object's context.  See vm vs. Contextify below for more discussion.
  4
+
  5
+## Examples
  6
+```javascript
  7
+var Contextify = require('contextify');
  8
+var sandbox = { console : console, prop1 : 'prop1'};
  9
+Contextify(sandbox);
  10
+sandbox.run('console.log(prop1);');
  11
+sandbox.dispose(); // free the resources allocated for the context.
  12
+```
  13
+
  14
+```javascript
  15
+var sandbox = Contextify(); // returns an empty contextified object.
  16
+sandbox.run('var x = 3;');
  17
+console.log(sandbox.x); // prints 3
  18
+sandbox.dispose();
  19
+```
  20
+
  21
+```javascript
  22
+var sandbox = Contextify({setTimeout : setTimeout});
  23
+sandbox.run("setTimeout(function () { x = 3; }, 5);");
  24
+console.log(sandbox.x); // prints undefined
  25
+setTimeout(function () {
  26
+    console.log(sandbox.x); // prints 3
  27
+    sandbox.dispose();
  28
+}, 10);
  29
+```
  30
+## Details
  31
+
  32
+**Contextify([sandbox])**
  33
+
  34
+    sandbox - The object to contextify, which will be modified as described below
  35
+              If no sandbox is specified, an empty object will be allocated and used instead.
  36
+
  37
+    Returns the contextified object.  It doesn't make a copy, so if you already have a reference
  38
+    to the sandbox, you don't need to catch the return value.
  39
+
  40
+A Contextified object has 2 methods added to it:
  41
+
  42
+**run(code, [filename])**
  43
+
  44
+    code - string containing JavaScript to execute
  45
+    filename  - an optional filename for debugging.
  46
+
  47
+    Runs the code in the Contextified object's context.
  48
+
  49
+**getGlobal()**
  50
+
  51
+Returns the actual global object for the V8 context.  The global object is initialized with interceptors (discussed below) which forward accesses on it to the contextified object.  This means the contextified object acts like the global object in most cases.  Sometimes, though, you need to make a reference to the actual global object.
  52
+
  53
+For example:
  54
+
  55
+```javascript
  56
+var window = Contextify({console : console});
  57
+window.window = window;
  58
+window.run("console.log(window === this);");
  59
+// prints false.
  60
+```
  61
+
  62
+```javascript
  63
+var window = Contextify({console : console});
  64
+window.window = window.getGlobal();
  65
+window.run("console.log(window === this);");
  66
+// prints true
  67
+```
  68
+
  69
+The global object returned by getGlobal() can be treated like the contextified sandbox object, except that defining getters/setters will not work on it.  Define getters and setters on the actual sandbox object instead.
  70
+
  71
+**dispose()**
  72
+
  73
+Frees the memory allocated for the underlying V8 context.  If you don't call this when you're done, the V8 context memory will leak, as will the sandbox memory, since the context's global stores a strong reference to the sandbox object.  You can still use your sandbox object after calling dispose(), but it's unsafe to use a global previously returned from getGlobal().  run, getGlobal, and dispose will be removed from the sandbox object.
  74
+
  75
+## Install
  76
+
  77
+    npm install contextify
  78
+
  79
+## require('vm') vs. Contextify
  80
+
  81
+Node's vm functions (runInContext etc) work by copying the values from the sandbox object onto a context's global object, executing the passed in script, then copying the results back.  This means that scripts that create asynchronous functions (using mechanisms like setTimeout) won't have see the results of executing those functions, since the copying in/out only occurs during an explicit call to runInContext and friends.  
  82
+
  83
+Contextify creates a V8 context, and uses interceptors (see: http://code.google.com/apis/v8/embed.html#interceptors) to forward global object accesses to the sandbox object.  This means there is no copying in or out, so asynchronous functions have the expected effect on the sandbox object.  
  84
+
  85
+## Tests
  86
+
  87
+Testing is done with nodeunit.  Run the tests with
  88
+
  89
+    nodeunit test/
  90
+
  91
+Output: 
  92
+
  93
+    OK: 92 assertions (27ms)
  94
+
  95
+
  96
+## Building
  97
+
  98
+    node-waf configure build
  99
+
  100
+## Acknowledgments
  101
+
  102
+Inspiration taken from Assaf's Zombie.js context solution: https://github.com/assaf/zombie
9  node_modules/contextify/lib/contextify.js
... ...
@@ -0,0 +1,9 @@
  1
+try {
  2
+    module.exports = require('../build/Release/contextify').wrap;
  3
+} catch (e) {
  4
+    console.log("Internal Contextify ERROR: Make sure Contextify is built " +
  5
+                "with your current Node version.\nTo rebuild, go to the " +
  6
+                "Contextify root folder and run 'node-waf distclean && " +
  7
+                "node-waf configure build'.");
  8
+    throw e;
  9
+}
31  node_modules/contextify/package.json
... ...
@@ -0,0 +1,31 @@
  1
+{
  2
+  "author": "Brian McDaniel <brianmcd05@gmail.com>",
  3
+  "name": "contextify",
  4
+  "description": "Turn an object into a persistent execution context.",
  5
+  "keywords": [
  6
+    "context",
  7
+    "vm"
  8
+  ],
  9
+  "version": "0.0.5",
  10
+  "repository": {
  11
+    "type" : "git",
  12
+    "url": "https://github.com/brianmcd/contextify.git"
  13
+  },
  14
+  "main": "./lib/contextify.js",
  15
+  "directories": {
  16
+    "lib": "./lib"
  17
+  },
  18
+  "engines": {
  19
+    "node": ">=0.4.0"
  20
+  },
  21
+  "licenses": [
  22
+    {
  23
+        "type" : "MIT",
  24
+        "url" : "http://github.com/brianmcd/contextify/blob/master/LICENSE.txt"
  25
+    }
  26
+  ],
  27
+  "dependencies": {},
  28
+  "devDependencies": {
  29
+    "nodeunit" : ">=0.5.x"
  30
+  }
  31
+}
275  node_modules/contextify/src/contextify.cc
... ...
@@ -0,0 +1,275 @@
  1
+#include "node.h"
  2
+#include <string>
  3
+using namespace v8;
  4
+using namespace node;
  5
+
  6
+struct ContextifyInfo;
  7
+
  8
+static Handle<Value> Wrap(const Arguments& args);
  9
+static Handle<Value> Run(const Arguments& args);
  10
+static Handle<Value> GetGlobal(const Arguments& args);
  11
+static Persistent<Context> CreateContext(ContextifyInfo* info);
  12
+
  13
+// Interceptor functions for global object template.
  14
+static Handle<Value> GlobalPropertyGetter(Local<String> property,
  15
+                                          const AccessorInfo &accessInfo);
  16
+static Handle<Value> GlobalPropertySetter(Local<String> property,
  17
+                                          Local<Value> value,
  18
+                                          const AccessorInfo &accessInfo);
  19
+static Handle<Integer> GlobalPropertyQuery(Local<String> property,
  20
+                                           const AccessorInfo &accessInfo);
  21
+static Handle<Boolean> GlobalPropertyDeleter(Local<String> property,
  22
+                                             const AccessorInfo &accessInfo);
  23
+static Handle<Array> GlobalPropertyEnumerator(const AccessorInfo &accessInfo);
  24
+
  25
+// ContextifyInfo (and the context) will live until dispose() is called on
  26
+// the sandbox or global.
  27
+struct ContextifyInfo {
  28
+    Persistent<Context> context;
  29
+    Persistent<Object> sandbox;
  30
+    Persistent<Object> global;
  31
+
  32
+    void SetContext(Persistent<Context> context) {
  33
+        this->context = context;
  34
+    }
  35
+
  36
+    void SetSandbox(Local<Object> sandbox) {
  37
+        this->sandbox = Persistent<Object>::New(sandbox);
  38
+    }
  39
+    
  40
+    void SetGlobal(Local<Object> global) {
  41
+        this->global = Persistent<Object>::New(global);
  42
+    }
  43
+};
  44
+
  45
+// Dispose is attached to the sandbox as 'dispose'.  It must be called to
  46
+// free the sandbox and context memory.  ContextifyInfo keeps a strong
  47
+// persistent reference to the sandbox, so if this isn't called, the sandbox
  48
+// and the context will both leak.
  49
+static Handle<Value> Dispose(const Arguments& args) {
  50
+    Local<Value> wrapped = args.This()->GetHiddenValue(String::New("info"));
  51
+    void* unwrapped = External::Unwrap(wrapped);
  52
+    if (unwrapped == NULL) {
  53
+        return ThrowException(String::New("Called dispose() twice."));
  54
+    }
  55
+    ContextifyInfo* info = static_cast<ContextifyInfo*>(unwrapped);
  56
+    info->sandbox->SetHiddenValue(String::New("info"), External::Wrap(NULL));
  57
+    info->global->SetHiddenValue(String::New("info"), External::Wrap(NULL));
  58
+    info->context.Dispose();
  59
+    info->context.Clear();
  60
+    info->global.Dispose();
  61
+    info->global.Clear();
  62
+    info->sandbox->Delete(String::NewSymbol("run"));
  63
+    info->sandbox->Delete(String::NewSymbol("getGlobal"));
  64
+    info->sandbox->Delete(String::NewSymbol("dispose"));
  65
+    // Note: this won't actually free the sandbox memory unless references from
  66
+    // the JavaScript side are also dropped.
  67
+    info->sandbox.Dispose();
  68
+    info->sandbox.Clear();
  69
+    delete info;
  70
+    return Undefined();
  71
+}
  72
+
  73
+// We only want to create 1 Function instance for each of these in this
  74
+// context.  Was previously creating a new Function for each Contextified
  75
+// object and causing a memory leak.
  76
+Persistent<Function> runFunc;
  77
+Persistent<Function> getGlobalFunc;
  78
+Persistent<Function> disposeFunc;
  79
+
  80
+// args[0] = the sandbox object
  81
+static Handle<Value> Wrap(const Arguments& args) {
  82
+    HandleScope scope;
  83
+    Local<Object> sandbox;
  84
+    if ((args.Length() > 0) && (args[0]->IsObject())) {
  85
+        sandbox = args[0]->ToObject();
  86
+    } else {
  87
+        sandbox = Object::New();
  88
+    }
  89
+    // info is cleaned up by itself when the sandbox gets GC'd.
  90
+    ContextifyInfo* info = new ContextifyInfo();
  91
+    info->SetSandbox(sandbox);
  92
+    Persistent<Context> context = CreateContext(info);
  93
+    info->SetContext(context);
  94
+    info->SetGlobal(context->Global());
  95
+
  96
+    Local<Value> wrapped = External::Wrap(info);
  97
+    sandbox->SetHiddenValue(String::New("info"), wrapped);
  98
+    info->global->SetHiddenValue(String::New("info"), wrapped);
  99
+
  100
+    if (runFunc.IsEmpty()) {
  101
+        Local<FunctionTemplate> runTmpl = FunctionTemplate::New(Run);
  102
+        runFunc = Persistent<Function>::New(runTmpl->GetFunction());
  103
+    }
  104
+    sandbox->Set(String::NewSymbol("run"), runFunc);
  105
+
  106
+    if (getGlobalFunc.IsEmpty()) {
  107
+        Local<FunctionTemplate> getGlobalTmpl = FunctionTemplate::New(GetGlobal);
  108
+        getGlobalFunc = Persistent<Function>::New(getGlobalTmpl->GetFunction());
  109
+    }
  110
+    sandbox->Set(String::NewSymbol("getGlobal"), getGlobalFunc);
  111
+
  112
+    if (disposeFunc.IsEmpty()) {
  113
+        Local<FunctionTemplate> disposeFuncTmpl = FunctionTemplate::New(Dispose);
  114
+        disposeFunc = Persistent<Function>::New(disposeFuncTmpl->GetFunction());
  115
+    }
  116
+    sandbox->Set(String::NewSymbol("dispose"), disposeFunc);
  117
+
  118
+    return scope.Close(sandbox);
  119
+}
  120
+
  121
+// Create a context whose global object uses the sandbox to lookup and set
  122
+// properties.
  123
+static Persistent<Context> CreateContext(ContextifyInfo* info) {
  124
+    HandleScope scope;
  125
+    // Set up the context's global object.
  126
+    Local<FunctionTemplate> ftmpl = FunctionTemplate::New();
  127
+    ftmpl->SetHiddenPrototype(true);
  128
+    ftmpl->SetClassName(info->sandbox->GetConstructorName());
  129
+    Local<ObjectTemplate> otmpl = ftmpl->InstanceTemplate();
  130
+    otmpl->SetNamedPropertyHandler(GlobalPropertyGetter,
  131
+                                   GlobalPropertySetter,
  132
+                                   GlobalPropertyQuery,
  133
+                                   GlobalPropertyDeleter,
  134
+                                   GlobalPropertyEnumerator);
  135
+    Persistent<Context> context = Context::New(NULL, otmpl);
  136
+
  137
+    // Get rid of the proxy object.
  138
+    context->DetachGlobal();
  139
+    return context;
  140
+}
  141
+
  142
+/*
  143
+ * args[0] = String of code
  144
+ * args[1] = filename
  145
+ */
  146
+static Handle<Value> Run(const Arguments& args) {
  147
+    HandleScope scope;
  148
+    Local<Value> wrapped = args.This()->GetHiddenValue(String::New("info"));
  149
+    void* unwrapped = External::Unwrap(wrapped);
  150
+    if (unwrapped == NULL) {
  151
+        return ThrowException(String::New("Called run() after dispose()."));
  152
+    }
  153
+    ContextifyInfo* info = static_cast<ContextifyInfo*>(unwrapped);
  154
+    Persistent<Context> context = info->context;
  155
+    context->Enter();
  156
+    Local<String> code = args[0]->ToString();
  157
+    TryCatch trycatch;
  158
+    Handle<Script> script;
  159
+    if (args.Length() > 1) {
  160
+        script = Script::Compile(code, args[1]->ToString());
  161
+    } else {
  162
+        script = Script::Compile(code);
  163
+    }
  164
+    if (script.IsEmpty()) {
  165
+      context->Exit();
  166
+      return trycatch.ReThrow();
  167
+    }
  168
+    Handle<Value> result = script->Run();
  169
+    context->Exit();
  170
+    if (result.IsEmpty()) {
  171
+        return trycatch.ReThrow();
  172
+    }
  173
+    return scope.Close(result);
  174
+}
  175
+
  176
+static Handle<Value> GetGlobal(const Arguments& args) {
  177
+    HandleScope scope;
  178
+    Local<Value> wrapped = args.This()->GetHiddenValue(String::New("info"));
  179
+    void* unwrapped = External::Unwrap(wrapped);
  180
+    if (unwrapped == NULL) {
  181
+        return ThrowException(String::New("Called getGlobal() after dispose()."));
  182
+    }
  183
+    ContextifyInfo* info = static_cast<ContextifyInfo*>(unwrapped);
  184
+    return scope.Close(info->global);
  185
+}
  186
+
  187
+static Handle<Value> GlobalPropertyGetter (Local<String> property,
  188
+                                           const AccessorInfo &accessInfo) {
  189
+    HandleScope scope;
  190
+    Local<Value> wrapped = accessInfo.This()->GetHiddenValue(String::New("info"));
  191
+    void* unwrapped = External::Unwrap(wrapped);
  192
+    if (unwrapped == NULL) {
  193
+        return ThrowException(String::New("Called getGlobal() after dispose()."));
  194
+    }
  195
+    ContextifyInfo* info = static_cast<ContextifyInfo*>(unwrapped);
  196
+    Persistent<Object> sandbox = info->sandbox;
  197
+    // First check the sandbox object.
  198
+    Local<Value> rv = sandbox->Get(property);
  199
+    if (rv->IsUndefined()) {
  200
+        // Next, check the global object (things like Object, Array, etc).
  201
+        // This needs to call GetRealNamedProperty or else we'll get stuck in
  202
+        // an infinite loop here.
  203
+        rv = info->global->GetRealNamedProperty(property);
  204
+    }
  205
+    return scope.Close(rv);
  206
+}
  207
+
  208
+// Global variables get set back on the sandbox object.
  209
+static Handle<Value> GlobalPropertySetter (Local<String> property,
  210
+                                           Local<Value> value,
  211
+                                           const AccessorInfo &accessInfo) {
  212
+    HandleScope scope;
  213
+    Local<Value> wrapped = accessInfo.This()->GetHiddenValue(String::New("info"));
  214
+    void* unwrapped = External::Unwrap(wrapped);
  215
+    if (unwrapped == NULL) {
  216
+        return ThrowException(String::New("Tried to set a property on global after dispose()."));
  217
+    }
  218
+    ContextifyInfo* info = static_cast<ContextifyInfo*>(unwrapped);
  219
+    Persistent<Object> sandbox = info->sandbox;
  220
+    bool success = sandbox->Set(property, value);
  221
+    return scope.Close(value);
  222
+}
  223
+
  224
+static Handle<Integer> GlobalPropertyQuery(Local<String> property,
  225
+                                           const AccessorInfo &accessInfo) {
  226
+    HandleScope scope;
  227
+    return scope.Close(Integer::New(None));
  228
+}
  229
+
  230
+static Handle<Boolean> GlobalPropertyDeleter(Local<String> property,
  231
+                                             const AccessorInfo &accessInfo) {
  232
+    HandleScope scope;
  233
+    Local<Value> wrapped = accessInfo.This()->GetHiddenValue(String::New("info"));
  234
+    void* unwrapped = External::Unwrap(wrapped);
  235
+    if (unwrapped == NULL) {
  236
+        ThrowException(String::New("Tried to delete a property on global after dispose()."));
  237
+        return False();
  238
+    }
  239
+    ContextifyInfo* info = static_cast<ContextifyInfo*>(unwrapped);
  240
+    Persistent<Object> sandbox = info->sandbox;
  241
+    bool success = sandbox->Delete(property);
  242
+    if (!success) {
  243
+        Persistent<Context> context = info->context;
  244
+        success = info->global->Delete(property);
  245
+    }
  246
+    return scope.Close(Boolean::New(success));
  247
+}
  248
+
  249
+static Handle<Array> GlobalPropertyEnumerator(const AccessorInfo &accessInfo) {
  250
+    HandleScope scope;
  251
+    Local<Value> wrapped = accessInfo.This()->GetHiddenValue(String::New("info"));
  252
+    void* unwrapped = External::Unwrap(wrapped);
  253
+    if (unwrapped == NULL) {
  254
+        // We can't throw an exception because we have to return an Array,
  255
+        // and ThrowException returns a value. Returning an empty Array is
  256
+        // better than segfaulting.
  257
+        return scope.Close(Array::New());
  258
+    }
  259
+    ContextifyInfo* info = static_cast<ContextifyInfo*>(unwrapped);
  260
+    Persistent<Object> sandbox = info->sandbox;
  261
+    return scope.Close(sandbox->GetPropertyNames());
  262
+}
  263
+
  264
+// Export the C++ Wrap method as 'wrap' on the module.
  265
+static void Init(Handle<Object> target) {
  266
+    HandleScope scope;
  267
+    NODE_SET_METHOD(target, "wrap", Wrap);
  268
+}
  269
+
  270
+extern "C" {
  271
+  static void init(Handle<Object> target) {
  272
+    Init(target);
  273
+  }
  274
+  NODE_MODULE(contextify, init);
  275
+}
432  node_modules/contextify/test/contextify.js
... ...
@@ -0,0 +1,432 @@
  1
+var Contextify = require('../lib/contextify.js');
  2
+
  3
+exports['basic tests'] = {
  4
+    // Creating a context shouldn't fail.
  5
+    'blank context' : function (test) {
  6
+        var ctx = Contextify({});
  7
+        test.notEqual(ctx, null);
  8
+        test.notEqual(ctx, undefined);
  9
+        test.done();
  10
+    },
  11
+
  12
+    // Creating a context with sandbox shouldn't change existing sandbox
  13
+    // properties.
  14
+    'basic context' : function (test) {
  15
+        var sandbox = {
  16
+            prop1 : 'prop1',
  17
+            prop2 : 'prop2'
  18
+        };
  19
+        Contextify(sandbox);
  20
+        test.equal(sandbox.prop1, 'prop1');
  21
+        test.equal(sandbox.prop2, 'prop2');
  22
+        test.done();
  23
+    },
  24
+
  25
+    // Ensure that the correct properties exist on a wrapped sandbox.
  26
+    'test contextified object extra properties' : function (test) {
  27
+        var sandbox = Contextify({});
  28
+        test.notEqual(sandbox.run, undefined);
  29
+        test.notEqual(sandbox.getGlobal, undefined);
  30
+        test.notEqual(sandbox.dispose, undefined);
  31
+        test.done();
  32
+    },
  33
+
  34
+    // Passing undefined should create an empty context.
  35
+    'test undefined sandbox' : function (test) {
  36
+        // Should return an empty object.
  37
+        test.notEqual(Contextify(undefined, undefined), undefined);
  38
+        test.notEqual(Contextify(), undefined);
  39
+        test.done();
  40
+    },
  41
+
  42
+    // Make sure properties that aren't there...aren't there.
  43
+    'test for nonexistent properties' : function (test) {
  44
+        var global = Contextify({}).getGlobal();
  45
+        test.equal(global.test1, undefined);
  46
+        test.done();
  47
+    },
  48
+
  49
+    // Make sure run can be called with a filename parameter.
  50
+    'test run with filename' : function (test) {
  51
+        var sandbox = Contextify();
  52
+        sandbox.run('var x = 3', "test.js");
  53
+        test.equal(sandbox.x, 3);
  54
+        test.done();
  55
+    },
  56
+
  57
+    // Make sure getters/setters on the sandbox object are used.
  58
+    'test accessors on sandbox' : function (test) {
  59
+        var sandbox = {};
  60
+        sandbox.__defineGetter__('test', function () { return 3;});
  61
+        sandbox.__defineSetter__('test2', function (val) { this.x = val;});
  62
+        Contextify(sandbox);
  63
+        var global = sandbox.getGlobal();
  64
+        test.equal(global.test, 3);
  65
+        sandbox.test2 = 5;
  66
+        test.equal(sandbox.x, 5);
  67
+        global.test2 = 7;
  68
+        test.equal(global.x, 7);
  69
+        test.equal(sandbox.x, 7);
  70
+        test.done();
  71
+    },
  72
+
  73
+    // Make sure dispose cleans up the sandbox.
  74
+    'test dispose' : function (test) {
  75
+        var sandbox = Contextify();
  76
+        test.notEqual(sandbox.run, undefined);
  77
+        test.notEqual(sandbox.getGlobal, undefined);
  78
+        test.notEqual(sandbox.dispose, undefined);
  79
+        sandbox.dispose();
  80
+        test.equal(sandbox.run, undefined);
  81
+        test.equal(sandbox.getGlobal, undefined);
  82
+        test.equal(sandbox.dispose, undefined);
  83
+        test.done();
  84
+    }
  85
+};
  86
+
  87
+exports['synchronous script tests'] = {
  88
+    // Synchronous context script execution:
  89
+    // Ensure that global variables are put on the sandbox object.
  90
+    'global variables in scripts should go on sandbox' : function (test) {
  91
+        var sandbox = {
  92
+            prop1 : 'prop1',
  93
+            prop2 : 'prop2'
  94
+        };
  95
+        Contextify(sandbox);
  96
+        sandbox.run('x = 3');
  97
+        test.equal(sandbox.x, 3);
  98
+        test.done();
  99
+    },
  100
+
  101
+    // Synchronous context script execution:
  102
+    // Ensure that sandbox properties can be accessed as global variables.
  103
+    'sandbox properties should be globals' : function (test) {
  104
+        var sandbox = {
  105
+            prop1 : 'prop1',
  106
+            prop2 : 'prop2'
  107
+        };
  108
+        Contextify(sandbox);
  109
+        sandbox.run("test1 = (prop1 == 'prop1');" +
  110
+                    "test2 = (prop2 == 'prop2');");
  111
+        test.ok(sandbox.test1);
  112
+        test.ok(sandbox.test2);
  113
+        test.done();
  114
+    }
  115
+};
  116
+
  117
+exports['asynchronous script tests'] = {
  118
+    // Asynchronous context script execution:
  119
+    // Ensure that global variables are put on the sandbox object.
  120
+    'global variables in scripts should go on sandbox' : function (test) {
  121
+        var sandbox = {
  122
+            setTimeout : setTimeout,
  123
+            prop1 : 'prop1',
  124
+            prop2 : 'prop2'
  125
+        };
  126
+        Contextify(sandbox);
  127
+        sandbox.run('setTimeout(function () {x = 3}, 0);');
  128
+        test.equal(sandbox.x, undefined);
  129
+        setTimeout(function () {
  130
+            test.equal(sandbox.x, 3);
  131
+            test.done();
  132
+        }, 0);
  133
+    },
  134
+
  135
+    // Asynchronous context script execution:
  136
+    // Ensure that sandbox properties can be accessed as global variables.
  137
+    'sandbox properties should be globals' : function (test) {
  138
+        var sandbox = {
  139
+            setTimeout : setTimeout,
  140
+            prop1 : 'prop1',
  141
+            prop2 : 'prop2'
  142
+        };
  143
+        Contextify(sandbox);
  144
+        sandbox.run("setTimeout(function () {" +
  145
+                    "test1 = (prop1 == 'prop1');" +
  146
+                    "test2 = (prop2 == 'prop2');" +
  147
+                    "}, 0)");
  148
+        test.equal(sandbox.test1, undefined);
  149
+        test.equal(sandbox.test2, undefined);
  150
+        setTimeout(function () {
  151
+            test.ok(sandbox.test1);
  152
+            test.ok(sandbox.test2);
  153
+            test.done();
  154
+        }, 0);
  155
+    }
  156
+};
  157
+
  158
+exports['test global'] = {
  159
+    // Make sure getGlobal() works.
  160
+    'basic test' : function (test) {
  161
+        var sandbox = {
  162
+            prop1 : 'prop1',
  163
+            prop2 : 'prop2'
  164
+        };
  165
+        Contextify(sandbox);
  166
+        var global = sandbox.getGlobal();
  167
+        test.notDeepEqual(global, null);
  168
+        test.notDeepEqual(global, undefined);
  169
+        // Make sure global is forwarding properly.
  170
+        test.equal(global.prop1, 'prop1');
  171
+        test.equal(global.prop2, 'prop2');
  172
+        global.prop3 = 'prop3';
  173
+        test.equal(sandbox.prop3, 'prop3');
  174
+        test.done();
  175
+    },
  176
+
  177
+    // Make sure that references to the global are correct.
  178
+    'self references to the global object' : function (test) {
  179
+        var sandbox = Contextify({});
  180
+        var global = sandbox.getGlobal();
  181
+        sandbox.ref1 = global;
  182
+        sandbox.ref2 = {
  183
+            ref2 : global
  184
+        };
  185
+        sandbox.run("test1 = (this == ref1);" +
  186
+                    "test2 = (this == ref2.ref2);");
  187
+        test.ok(sandbox.test1);
  188
+        test.ok(sandbox.test2);
  189
+        test.done();
  190
+    },
  191
+
  192
+    // Make sure the enumerator is enumerating correctly.
  193
+    'test enumerator' : function (test) {
  194
+        var sandbox = {
  195
+            prop1 : 'prop1',
  196
+            prop2 : 'prop2'
  197
+        };
  198
+        var global = Contextify(sandbox).getGlobal();
  199
+        var globalProps = Object.keys(global);
  200
+        test.equal(globalProps.length, 5);
  201
+        test.ok(globalProps.indexOf('prop1') != -1);
  202
+        test.ok(globalProps.indexOf('prop2') != -1);
  203
+        test.ok(globalProps.indexOf('run') != -1);
  204
+        test.ok(globalProps.indexOf('getGlobal') != -1);
  205
+        test.ok(globalProps.indexOf('dispose') != -1);
  206
+        test.done();
  207
+    },
  208
+
  209
+    // Make sure deleter is working.
  210
+    'test deleter' : function (test) {
  211
+        var sandbox = {
  212
+            prop1 : 'prop1',
  213
+            prop2 : 'prop2'
  214
+        };
  215
+        var global = Contextify(sandbox).getGlobal();
  216
+        test.equal(Object.keys(global).length, 5);
  217
+        test.equal(Object.keys(sandbox).length, 5);
  218
+        delete global.prop1;
  219
+        test.equal(Object.keys(global).length, 4);
  220
+        test.equal(Object.keys(sandbox).length, 4);
  221
+        delete global.prop2;
  222
+        test.equal(Object.keys(global).length, 3);
  223
+        test.equal(Object.keys(sandbox).length, 3);
  224
+        delete global.run;
  225
+        test.equal(Object.keys(global).length, 2);
  226
+        test.equal(Object.keys(sandbox).length, 2);
  227
+        delete global.getGlobal;
  228
+        test.equal(Object.keys(global).length, 1);
  229
+        test.equal(Object.keys(sandbox).length, 1);
  230
+        delete global.dispose;
  231
+        test.equal(Object.keys(global).length, 0);
  232
+        test.equal(Object.keys(sandbox).length, 0);
  233
+        test.done();
  234
+    },
  235
+
  236
+    // Make sure the global's class name is the same as the sandbox.
  237
+    'test global class name' : function (test) {
  238
+        function DOMWindow () {};
  239
+        var sandbox = Contextify(new DOMWindow());
  240
+        var global = sandbox.getGlobal();
  241
+        test.equal(sandbox.constructor.name, 'DOMWindow');
  242
+        test.equal(sandbox.constructor.name, global.constructor.name);
  243
+        sandbox.run('thisName = this.constructor.name');
  244
+        test.equal(sandbox.thisName, sandbox.constructor.name);
  245
+        test.done();
  246
+    },
  247
+
  248
+    // Make sure functions in global scope are accessible through global.
  249
+    'test global functions' : function (test) {
  250
+        var sandbox = Contextify();
  251
+        var global = sandbox.getGlobal();
  252
+        sandbox.run("function testing () {}");
  253
+        test.notEqual(global.testing, undefined);
  254
+        test.done();
  255
+    },
  256
+
  257
+    // Make sure global can be a receiver for run().
  258
+    'test global.run()' : function (test) {
  259
+        var global = Contextify().getGlobal();
  260
+        global.run("x = 5");
  261
+        test.equal(global.x, 5);
  262
+        test.done();
  263
+    },
  264
+    
  265
+    // Make sure global can be a receiver for getGlobal().
  266
+    'test global.getGlobal()' : function (test) {
  267
+        var global = Contextify().getGlobal();
  268
+        test.deepEqual(global, global.getGlobal());
  269
+        test.done();
  270
+    },
  271
+
  272
+    //Make sure global can be a receiver for dispose().
  273
+    'test global.dispose()' : function (test) {
  274
+        var sandbox = Contextify();
  275
+        var global = sandbox.getGlobal();
  276
+        test.notEqual(global.run, undefined);
  277
+        test.notEqual(global.getGlobal, undefined);
  278
+        test.notEqual(global.dispose, undefined);
  279
+        global.dispose();
  280
+        // It's not safe to use the global after disposing.
  281
+        test.equal(sandbox.run, undefined);
  282
+        test.equal(sandbox.getGlobal, undefined);
  283
+        test.equal(sandbox.dispose, undefined);
  284
+        test.done();
  285
+    }
  286
+};
  287
+
  288
+
  289
+// Test that multiple contexts don't interfere with each other.
  290
+exports['test multiple contexts'] = function (test) {
  291
+    var sandbox1 = {
  292
+        prop1 : 'prop1',
  293
+        prop2 : 'prop2'
  294
+    };
  295
+    var sandbox2 = {
  296
+        prop1 : 'prop1',
  297
+        prop2 : 'prop2'
  298
+    };
  299
+    var global1 = Contextify(sandbox1).getGlobal();
  300
+    var global2 = Contextify(sandbox2).getGlobal();
  301
+    test.equal(global1.prop1, 'prop1');
  302
+    test.equal(global2.prop1, 'prop1');
  303
+    sandbox1.run('test = 3');
  304
+    sandbox2.run('test = 4');
  305
+    test.equal(sandbox1.test, 3);
  306
+    test.equal(global1.test, 3);
  307
+    test.equal(sandbox2.test, 4);
  308
+    test.equal(global2.test, 4);
  309
+    test.done();
  310
+};
  311
+
  312
+// Test console - segfaults in REPL.
  313
+exports['test console'] = function (test) {
  314
+    var sandbox = {
  315
+        console : console,
  316
+        prop1 : 'prop1'
  317
+    };
  318
+    Contextify(sandbox);
  319
+    test.doesNotThrow(function () {
  320
+        sandbox.run('console.log(prop1);');
  321
+    });
  322
+    test.done();
  323
+};
  324
+
  325
+
  326
+// Make sure exceptions get thrown for invalid scripts.
  327
+exports['test exceptions'] = {
  328
+    'basic test' : function (test) {
  329
+        var sandbox = Contextify();
  330
+        test.throws(function () {
  331
+            sandbox.run('doh');
  332
+        });
  333
+        test.throws(function () {
  334
+            sandbox.run('x = y');
  335
+        });
  336
+        test.throws(function () {
  337
+            sandbox.run('function ( { (( }{);');
  338
+        });
  339
+        test.done();
  340
+    },
  341
+
  342
+    'test double dispose() - sandbox' : function (test) {
  343
+        var sandbox = Contextify();
  344
+        test.doesNotThrow(function () {
  345
+            sandbox.dispose();
  346
+        });
  347
+        test.throws(function () {
  348
+            sandbox.dispose();
  349
+        }, 'Called dispose() twice.');
  350
+        test.done();
  351
+    },
  352
+
  353
+    'test double dispose - global' : function (test) {
  354
+        var sandbox = Contextify();
  355
+        var global = sandbox.getGlobal();
  356
+        test.doesNotThrow(function () {
  357
+            global.dispose();
  358
+        });
  359
+        test.throws(function () {
  360
+            global.dispose();
  361
+        }, 'Called dispose() twice.');
  362
+        test.done();
  363
+    },
  364
+   
  365
+    'test run() after dispose()' : function (test) {
  366
+        var sandbox = Contextify();
  367
+        test.doesNotThrow(function () {
  368
+            sandbox.dispose();
  369
+        });
  370
+        test.throws(function () {
  371
+            sandbox.run('var x = 3');
  372
+        }, 'Called run() after dispose().');
  373
+        test.done();
  374
+    },
  375
+
  376
+    'test getGlobal() after dispose()' : function (test) {
  377
+        var sandbox = Contextify();
  378
+        test.doesNotThrow(function () {
  379
+            sandbox.dispose();
  380
+        });
  381
+        test.throws(function () {
  382
+            var g = sandbox.getGlobal();
  383
+        }, 'Called getGlobal() after dispose().');
  384
+        test.done();
  385
+    },
  386
+
  387
+    'test global property getter after dispose()' : function (test) {
  388
+        var sandbox = Contextify({prop1 : 'test'});
  389
+        var global = sandbox.getGlobal();
  390
+        test.doesNotThrow(function () {
  391
+            sandbox.dispose();
  392
+        });
  393
+        test.throws(function () {
  394
+            var x = global.prop1;
  395
+        }, 'Tried to access global after dispose().');
  396
+        test.done();
  397
+    },
  398
+
  399
+    'test global property setter after dispose()' : function (test) {
  400
+        var sandbox = Contextify();
  401
+        var global = sandbox.getGlobal();
  402
+        test.doesNotThrow(function () {
  403
+            sandbox.dispose();
  404
+        });
  405
+        test.throws(function () {
  406
+            global.x = 3;
  407
+        }, 'Tried to set a property on global after dispose().');
  408
+        test.done();
  409
+    },
  410
+
  411
+    'test global property deleter after dispose()' : function (test) {
  412
+        var sandbox = Contextify({prop1 : 'test'});
  413
+        var global = sandbox.getGlobal();
  414
+        test.doesNotThrow(function () {
  415
+            sandbox.dispose();
  416
+        });
  417
+        test.throws(function () {
  418
+            delete global.prop1;
  419
+        }, 'Tried to delete a property on global after dispose().');
  420
+        test.done();
  421
+
  422
+    }
  423
+};
  424
+
  425
+exports['test global property enumerator after dispose()'] = function (test) {
  426
+    var sandbox = Contextify({prop1 : 'test', prop2 : 'test'});
  427
+    var global = sandbox.getGlobal();
  428
+    sandbox.dispose();
  429
+    var props = Object.keys(global);
  430
+    test.equal(props.length, 0);
  431
+    test.done();
  432
+};
18  node_modules/contextify/wscript
... ...
@@ -0,0 +1,18 @@
  1
+import Options
  2
+import os
  3
+import sys
  4
+
  5
+VERSION = '0.0.5'
  6
+
  7
+def set_options(opt):
  8
+  opt.tool_options("compiler_cxx")
  9
+
  10
+def configure(conf):
  11
+  conf.check_tool("compiler_cxx")
  12
+  conf.check_tool("node_addon")
  13
+  conf.env.set_variant("Release")
  14
+
  15
+def build(bld):
  16
+  obj = bld.new_task_gen("cxx", "shlib", "node_addon")
  17
+  obj.target = "contextify"
  18
+  obj.source = "src/contextify.cc"
9  node_modules/cssom/.idea/CSSOM.iml
... ...
@@ -0,0 +1,9 @@
  1
+<?xml version="1.0" encoding="UTF-8"?>
  2
+<module type="RUBY_MODULE" version="4">
  3
+  <component name="NewModuleRootManager">
  4
+    <content url="file://$MODULE_DIR$" />
  5
+    <orderEntry type="inheritedJdk" />
  6
+    <orderEntry type="sourceFolder" forTests="false" />
  7
+  </component>
  8
+</module>
  9
+
3  node_modules/cssom/.idea/dictionaries/nv.xml
... ...
@@ -0,0 +1,3 @@
  1
+<component name="ProjectDictionaryState">
  2
+  <dictionary name="nv" />
  3
+</component>
5  node_modules/cssom/.idea/encodings.xml
... ...
@@ -0,0 +1,5 @@
  1
+<?xml version="1.0" encoding="UTF-8"?>
  2
+<project version="4">
  3
+  <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" defaultCharsetForPropertiesFiles="UTF-8" />
  4
+</project>
  5
+
17  node_modules/cssom/.idea/misc.xml
... ...
@@ -0,0 +1,17 @@
  1
+<?xml version="1.0" encoding="UTF-8"?>
  2
+<project version="4">
  3
+  <component name="DependencyValidationManager">
  4
+    <option name="SKIP_IMPORT_STATEMENTS" value="false" />
  5
+  </component>
  6
+  <component name="ProjectDetails">
  7
+    <option name="projectName" value="CSSOM" />
  8
+  </component>
  9
+  <component name="ProjectResources">
  10
+    <default-html-doctype>http://www.w3.org/1999/xhtml</default-html-doctype>
  11
+  </component>
  12
+  <component name="ProjectRootManager" version="2" project-jdk-name="Ruby SDK 1.8.7 (/usr/bin/ruby)" project-jdk-type="RUBY_SDK" />
  13
+  <component name="SvnBranchConfigurationManager">
  14
+    <option name="mySupportsUserInfoFilter" value="true" />
  15
+  </component>
  16
+</project>
  17
+
9  node_modules/cssom/.idea/modules.xml
... ...
@@ -0,0 +1,9 @@
  1
+<?xml version="1.0" encoding="UTF-8"?>
  2
+<project version="4">
  3
+  <component name="ProjectModuleManager">
  4
+    <modules>
  5
+      <module fileurl="file://$PROJECT_DIR$/.idea/CSSOM.iml" filepath="$PROJECT_DIR$/.idea/CSSOM.iml" />
  6
+    </modules>
  7
+  </component>
  8
+</project>
  9
+
82  node_modules/cssom/.idea/projectCodeStyle.xml
... ...
@@ -0,0 +1,82 @@
  1
+<?xml version="1.0" encoding="UTF-8"?>
  2
+<project version="4">
  3
+  <component name="CodeStyleSettingsManager">
  4
+    <option name="PER_PROJECT_SETTINGS">
  5
+      <value>
  6
+        <option name="USE_SAME_INDENTS" value="true" />
  7
+        <option name="OTHER_INDENT_OPTIONS">
  8
+          <value>
  9
+            <option name="INDENT_SIZE" value="4" />
  10
+            <option name="CONTINUATION_INDENT_SIZE" value="0" />
  11
+            <option name="TAB_SIZE" value="4" />
  12
+            <option name="USE_TAB_CHARACTER" value="true" />
  13
+            <option name="SMART_TABS" value="false" />
  14
+            <option name="LABEL_INDENT_SIZE" value="0" />
  15
+            <option name="LABEL_INDENT_ABSOLUTE" value="false" />
  16
+            <option name="USE_RELATIVE_INDENTS" value="false" />
  17
+          </value>
  18
+        </option>
  19
+        <option name="LINE_SEPARATOR" value="&#10;" />
  20
+        <option name="XML_ATTRIBUTE_WRAP" value="0" />
  21
+        <option name="XML_TEXT_WRAP" value="0" />
  22
+        <option name="XML_ALIGN_ATTRIBUTES" value="false" />