Security update to Drupal 8.4.6
[yaffs-website] / node_modules / phridge / README.md
1 phridge
2 =======
3 **A bridge between [node](http://nodejs.org/) and [PhantomJS](http://phantomjs.org/).**
4
5 [![](https://img.shields.io/npm/v/phridge.svg)](https://www.npmjs.com/package/phridge)
6 [![](https://img.shields.io/npm/dm/phridge.svg)](https://www.npmjs.com/package/phridge)
7 [![Dependency Status](https://david-dm.org/peerigon/phridge.svg)](https://david-dm.org/peerigon/phridge)
8 [![Build Status](https://travis-ci.org/peerigon/phridge.svg?branch=master)](https://travis-ci.org/peerigon/phridge)
9 [![Coverage Status](https://img.shields.io/coveralls/peerigon/phridge.svg)](https://Coveralls.io/r/peerigon/phridge?branch=master)
10
11 Working with PhantomJS in node is a bit cumbersome since you need to spawn a new PhantomJS process for every single task. However, spawning a new process is quite expensive and thus can slow down your application significantly.
12
13 phridge provides an api to easily
14
15 - spawn new PhantomJS processes
16 - run functions with arguments inside PhantomJS
17 - return results from PhantomJS to node
18 - manage long-running PhantomJS instances
19
20 Unlike other node-PhantomJS bridges phridge provides a way to run code directly inside PhantomJS instead of turning every call and assignment into an async operation.
21
22 phridge uses PhantomJS' stdin and stdout for [inter-process communication](http://en.wikipedia.org/wiki/Inter-process_communication). It stringifies the given function, passes it to PhantomJS via stdin, executes it in the PhantomJS environment and passes back the results via stdout. Thus you can write your PhantomJS scripts inside your node modules in a clean and synchronous way.
23
24 Instead of ...
25
26 ```javascript
27 phantom.addCookie("cookie_name", "cookie_value", "localhost", function () {
28     phantom.createPage(function (page) {
29         page.set("customHeaders.Referer", "http://google.com", function () {
30             page.set(
31                 "settings.userAgent",
32                 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5)",
33                 function () {
34                     page.open("http://localhost:9901/cookie", function (status) {
35                         page.evaluate(function (selector) {
36                             return document.querySelector(selector).innerText;
37                         }, function (text) {
38                             console.log("The element contains the following text: "+ text)
39                         }, "h1");
40                     });
41                 }
42             );
43         });
44     });
45 });
46 ```
47
48 ... you can write ...
49
50 ```javascript
51 // node
52 phantom.run("h1", function (selector, resolve) {
53     // this code runs inside PhantomJS
54
55     phantom.addCookie("cookie_name", "cookie_value", "localhost");
56
57     var page = webpage.create();
58     page.customHeaders = {
59         Referer: "http://google.com"
60     };
61     page.settings = {
62         userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5)"
63     };
64     page.open("http://www.google.com", function () {
65         var text = page.evaluate(function (selector) {
66             return document.querySelector(selector).innerText;
67         }, selector);
68
69         // resolve the promise and pass 'text' back to node 
70         resolve(text);
71     });
72 }).then(function (text) {
73     // inside node again
74     console.log("The element contains the following text: " + text);
75 });
76 ```
77
78 Please note that the `phantom`-object provided by phridge is completely different to the `phantom`-object inside PhantomJS. So is the `page`-object. [Check out the api](#api-phantom) for further information.
79
80 <br />
81
82 Installation
83 ------------------------------------------------------------------------
84
85 `npm install phridge`
86
87 <br />
88
89 Examples
90 ------------------------------------------------------------------------
91
92 ### Spawn a new PhantomJS process
93
94 ```javascript
95 phridge.spawn({
96     proxyAuth: "john:1234",
97     loadImages: false,
98     // passing CLI-style options does also work
99     "--remote-debugger-port": 8888
100 }).then(function (phantom) {
101     // phantom is now a reference to a specific PhantomJS process
102 });
103 ```
104
105 `phridge.spawn()` takes an object which will be passed as config to PhantomJS. Check out [their documentation](http://phantomjs.org/api/command-line.html) for a detailed overview of options. CLI-style options are added as they are, so be sure to escape the space character.
106
107 *Please note: There are [known issues](https://github.com/peerigon/phridge/issues/31) of PhantomJS that some config options are only supported in CLI-style.*
108
109 ### Run any function inside PhantomJS
110
111 ```javascript
112 phantom.run(function () {
113     console.log("Hi from PhantomJS");
114 });
115 ```
116
117 phridge stringifies the given function, sends it to PhantomJS and evals it again. Hence you can't use scope variables:
118
119 ```javascript
120 var someVar = "hi";
121
122 phantom.run(function () {
123     console.log(someVar); // throws a ReferenceError
124 });
125 ```
126
127 ### Passing arguments
128
129 You can also pass arguments to the PhantomJS process:
130
131 ```javascript
132 phantom.run("hi", 2, {}, function (string, number, object) {
133     console.log(string, number, object); // 'hi', 2, [object Object]
134 });
135 ```
136
137 Arguments are stringified by `JSON.stringify()`, so be sure to use JSON-valid objects.
138
139 ### Returning results
140
141 The given function can run sync and async. However, the `run()` method itself will always run async as it needs to wait for the process to respond.
142
143 **Sync**
144
145 ```javascript
146 phantom.run(function () {
147     return Math.PI;
148 }).then(function (pi) {
149     console.log(pi === Math.PI); // true
150 });
151 ```
152
153 **Async**
154
155 ```javascript
156 phantom.run(function (resolve) {
157     setTimeout(function () {
158         resolve("after 500 ms");
159     }, 500);
160 }).then(function (msg) {
161     console.log(msg); // 'after 500 ms'
162 });
163 ```
164
165 Results are also stringified by `JSON.stringify()`, so returning application objects with functions won't work.
166
167 ```javascript
168 phantom.run(function () {
169     ...
170     // doesn't work because page is not a JSON-valid object
171     return page;
172 });
173 ```
174
175 ### Returning errors
176
177 Errors can be returned by using the `throw` keyword or by calling the `reject` function. Both ways will reject the promise returned by `run()`.
178
179 **Sync**
180
181 ```javascript
182 phantom.run(function () {
183     throw new Error("An unknown error occured");
184 }).catch(function (err) {
185     console.log(err); // 'An unknown error occured'
186 });
187 ```
188
189 **Async**
190
191 ```javascript
192 phantom.run(function (resolve, reject) {
193     setTimeout(function () {
194         reject(new Error("An unknown error occured"));
195     }, 500);
196 }).catch(function (err) {
197     console.log(err); // 'An unknown error occured'
198 });
199 ```
200
201 ### Async methods with arguments
202
203 `resolve` and `reject` are just appended to the regular arguments:
204
205 ```javascript
206 phantom.run(1, 2, 3, function (one, two, three, resolve, reject) {
207
208 });
209 ```
210
211 ### Persisting states inside PhantomJS
212
213 Since the function passed to `phantom.run()` can't declare variables in the global scope, it is impossible to maintain state in PhantomJS. That's why `phantom.run()` calls all functions on the same context object. Thus you can easily store state variables.
214
215 ```javascript
216 phantom.run(function () {
217     this.message = "Hello from the first call";
218 }).then(function () {
219     phantom.run(function () {
220         console.log(this.message); // 'Hello from the first call'
221     });
222 });
223 ```
224
225 For further convenience all PhantomJS modules are already available in the global scope.
226
227 ```javascript
228 phantom.run(function () {
229     console.log(webpage);           // [object Object]
230     console.log(system);            // [object Object]
231     console.log(fs);                // [object Object]
232     console.log(webserver);         // [object Object]
233     console.log(child_process);     // [object Object]
234 });
235 ```
236
237 ### Working in a page context
238
239 Most of the time its more useful to work in a specific webpage context. This is done by creating a Page via `phantom.createPage()` which calls internally `require("webpage").create()`. The returned page wrapper will then execute all functions bound to a PhantomJS [webpage instance](http://phantomjs.org/api/webpage/). 
240
241 ```javascript
242 var page = phantom.createPage();
243
244 page.run(function (resolve, reject) {
245     // `this` is now a webpage instance
246     this.open("http://example.com", function (status) {
247         if (status !== "success") {
248             return reject(new Error("Cannot load " + this.url));
249         }
250         resolve();
251     });
252 });
253 ```
254
255 And for the busy ones: You can just call `phantom.openPage(url)` which is basically the same as above:
256
257 ```javascript
258 phantom.openPage("http://example.com").then(function (page) {
259     console.log("Example loaded");
260 });
261 ``` 
262
263 ### Cleaning up
264
265 If you don't need a particular page anymore, just call:
266
267 ```javascript
268 page.dispose().then(function () {
269     console.log("page disposed");
270 });
271 ```
272
273 This will clean up all page references inside PhantomJS.
274
275 If you don't need the whole process anymore call
276
277 ```javascript
278 phantom.dispose().then(function () {
279     console.log("process terminated");
280 });
281 ```
282
283 which will terminate the process cleanly by calling `phantom.exit(0)` internally. You don't need to dispose all pages manuallly when you call `phantom.dispose()`.
284
285 However, calling
286
287 ```javascript
288 phridge.disposeAll().then(function () {
289     console.log("All processes created by phridge.spawn() have been terminated");
290 });
291 ```
292
293 will terminate all processes.
294
295 **I strongly recommend to call** `phridge.disposeAll()` **when the node process exits as this is the only way to ensure that all child processes terminate as well.** Since `disposeAll()` is async it is not safe to call it on `process.on("exit")`. It is better to call it on `SIGINT`, `SIGTERM` and within your regular exit flow.
296
297 <br />
298
299 API
300 ----
301
302 ### phridge
303
304 #### .spawn(config?): Promise → Phantom
305
306 Spawns a new PhantomJS process with the given config. [Read the PhantomJS documentation](http://phantomjs.org/api/command-line.html) for all available config options. Use camelCase style for option names. The promise will be fulfilled with an instance of `Phantom`.
307
308 #### .disposeAll(): Promise
309
310 Terminates all PhantomJS processes that have been spawned. The promise will be fulfilled when all child processes emitted an `exit`-event.
311
312 #### .config.stdout: Stream = process.stdout
313
314 Destination stream where PhantomJS' [clean stdout](#phantom-childprocess-cleanstdout) will be piped to. Set it `null` if you don't want it. Changing the value does not affect processes that have already been spawned.
315
316 #### .config.stderr: Stream = process.stderr
317
318 Destination stream where PhantomJS' stderr will be piped to. Set it `null` if you don't want it. Changing the value does not affect processes that have already been spawned.
319
320 ----
321
322 ### <a name="api-phantom"></a>Phantom.prototype
323
324 #### .childProcess: ChildProcess
325
326 A reference to the [ChildProcess](http://nodejs.org/api/child_process.html#child_process_class_childprocess)-instance.
327
328 #### <a name="phantom-childprocess-cleanstdout"></a> .childProcess.cleanStdout: ReadableStream
329
330 phridge extends the [ChildProcess](http://nodejs.org/api/child_process.html#child_process_class_childprocess)-instance by a new stream called `cleanStdout`. This stream is piped to `process.stdout` by default. It provides all data not dedicated to phridge. Streaming data is considered to be dedicated to phridge when the new line is preceded by the classifier string `"message to node: "`.
331
332 #### <a name="phantom-run"></a>.run(args..., fn): Promise → *
333
334 Stringifies `fn`, sends it to PhantomJS and executes it there again. `args...` are stringified using `JSON.stringify()` and passed to `fn` again. `fn` may simply `return` a result or `throw` an error or call `resolve()` or `reject()` respectively if it is asynchronous. phridge compares `fn.length` with the given number of arguments to determine whether `fn` is sync or async. The returned promise will be resolved with the result or rejected with the error.
335
336 #### .createPage(): Page
337
338 Creates a wrapper to execute code in the context of a specific [PhantomJS webpage](http://phantomjs.org/api/webpage/).
339
340 #### .openPage(url): Promise → Page
341
342 Calls `phantom.createPage()`, then `page.open(url, cb)` inside PhantomJS and resolves when `cb` is called. If the returned `status` is not `"success"` the promise will be rejected.
343
344 #### .dispose(): Promise
345
346 Calls `phantom.exit(0)` inside PhantomJS and resolves when the child process emits an `exit`-event.
347
348 ### Events
349
350 #### unexpectedExit
351
352 Will be emitted when PhantomJS exited without a call to `phantom.dispose()` or one of its std streams emitted an `error` event. This event may be fired on some OS when the process group receives a `SIGINT` or `SIGTERM` (see [#35](https://github.com/peerigon/phridge/pull/35)).
353
354 When an `unexpectedExit` event is encountered, the `phantom` instance will be unusable and therefore automatically disposed. Usually you don't need to listen for this event.
355
356 ---
357
358 ### Page.prototype
359
360 #### .phantom: Phantom
361
362 A reference to the parent [`Phantom`](#api-phantom) instance.
363
364 #### .run(args..., fn): Promise → *
365
366 Calls `fn` on the context of a PhantomJS page object. See [`phantom.run()`](#phantom-run) for further information.
367
368 #### .dispose(): Promise
369
370 Cleans up this page instance by calling `page.close()`
371
372 <br />
373
374 Contributing
375 ------------------------------------------------------------------------
376
377 From opening a bug report to creating a pull request: **every contribution is appreciated and welcome**. If you're planing to implement a new feature or change the api please create an issue first. This way we can ensure that your precious work is not in vain.
378
379 All pull requests should have 100% test coverage (with notable exceptions) and need to pass all tests.
380
381 - Call `npm test` to run the unit tests
382 - Call `npm run coverage` to check the test coverage (using [istanbul](https://github.com/gotwarlost/istanbul))  
383
384 <br />
385
386 License
387 ------------------------------------------------------------------------
388
389 Unlicense