-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathfaq.html
409 lines (371 loc) · 23.1 KB
/
faq.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
<!DOCTYPE html>
<html>
<head>
<title>FAQ | PharoJS</title>
<meta name="description" content="PharoJS Frequently Asked Questions">
<meta name="twitter:description" content="PharoJS Frequently Asked Questions">
<meta charset="utf-8">
<style>
div.answer{display:none}
li.show>div.answer{display:block}
.openClose>.show{list-style-image: url('img/open.png')}
.openClose>:not(.show){list-style-image: url('img/closed.png')}
.openClose>li>a{font-size:150%;cursor:pointer;}
ul.openClose ul{list-style-image: initial;}
ul.openClose ol{list-style-image: initial;}
</style>
</head>
<body>
<link rel="stylesheet" type="text/css" href="css/pharoJS.css"/>
<div class="pjHeader">
<a href="http://pharojs.org"><img alt="PharoJS Logo" src="img/pharoJsBicolorLogo2016-08-12.png"></a>
<h1>Frequently Asked Questions</h1>
</div>
<h2>General Questions</h2>
<ul class="openClose">
<li><a name="install">How do I install PharoJS?</a>
<div class="answer">
<p>See the <a href="https://github.com/PharoJS/PharoJS">PharoJS GitHub repository</a> for installation instructions in a <a href="https://pharo.org">Pharo image</a>
</p>
</div>
</li>
<li><a name="help">Where can I get help with PharoJS?</a>
<div class="answer">
The best place is with the <a href="https://pharojs.slack.com">PharoJS Slack workspace</a>.
To get an account there, either send a twitter direct-message to <a href="https://twitter.com/pharojs">@pharojs</a> or an email to <a href="email:[email protected]">[email protected]</a>.
</div>
</li>
<li><a name="bugs">Where can I report bugs or issues with PharoJS?</a>
<div class="answer">
We are using <a href="https://github.com/PharoJS/pharojs/issues">GitHub</a>
</div>
</li>
<li><a name="documentation">Where can I find the PharoJS documentation?</a>
<div class="answer">
The PharoJS documentation is <a href="doc.html">available at this linked page</a>.
It includes (but not restricted to) conference papers and slides of talks presenting different facets of PharoJS.
</div>
</li>
<li><a>What versions of Pharo does PharoJS work with?</a>
<div class="answer">
PharoJS is pretty intimately connected to the Pharo compiler, so supporting multiple versions can be tricky.
PharoJS is available on Pharo 7 and Pharo8 right now... it will be available on Pharo9 soon and then all development will be done there.
We will support Pharo7 and Pharo8 as long as feasible. If you need it there and it breaks, let us know and we'll fix it if we reasonably can.
Pharo6 had some stability issues, so we elected to skip Pharo 6.
It also ran on Pharo 5, but it's been a long time since we tested it there. It's also hosted on <a href="http://smalltalkhub.com/">http://smalltalkhub.com/</a> but that will disappear at the end of 2020, so this will no longer work.
</div>
</li>
<!-- <li><a>Is there a PharoJS pre-builtImage?</a>
<div class="answer">
</div>
</li> -->
<li><a>Where can I find the HTML code for App tests (e.g. PjCounterApp and PjMinimalApp)?</a>
<div class="answer">
Upon PharoJS install, the HTML code for app tests are located in a folder along with the image and the change files. This is:
<code>pharo-local/iceberg/PharoJS/PharoJS/HTML</code> It contains subfolders for each example and test.
You can also find it in the PharoJS Git repository, along with Pharo code: <a href="https://github.com/PharoJS/PharoJS">https://github.com/PharoJS/PharoJS</a>.
</div>
</li>
<li><a name="mobile">Does PharoJS support developing mobile apps with Cordova/PhoneGap?</a>
<div class="answer">
Yes. There is a dedicated page that explains <a href="mobileAppDev.html">how to use PharoJS to make mobile apps for Android and iOS</a>.
</div>
</li>
<li><a name="seaside">Does PharoJS Integrates with Seaside?</a>
<div class="answer">
<p>We have an experimental feature. It shows the feasibility of the idea, but the solution is not clean. That is, if you have multiple Seaside components with PharoJS code, you'll end up with redundant JS code. The component will work, but it will waste resources (network bandwidth). This at least 65KB per component.</p>
<p>PharoJS integration with Seaside is defined in a dedicated package <code>PharoJsSeaside</code>. You'll find below instructions on how to install it.</p>
<ul>
<li>Latest version for Pharo 7 and above (GitHub): Evaluate the following code in playground.
<pre>
Metacello new
baseline: 'PharoJsSeaside';
repository: 'github://PharoJS/PharoJS';
load
</pre>
</li>
<li>Old version for Pharo 5 (SmalltalkHub): Evaluate the following in a playground:
<pre>
Gofer it
smalltalkhubUser: 'noury' project: 'PharoJS';
configurationOf: #PharoJsSeaside;
loadBleedingEdge.
</pre>
</li>
</ul>
<p>The code is pretty straight forward. To test it, start Seaside and open
<code>http://localhost:8080/PharoJS/</code>.</p>
</div>
</li>
<li><a name="esug2016demo">Where can I download the ESUG 2016 Demo (Pharo 4 image + Web App + PhoneGap App)?</a>
<div class="answer">
A ready to use Pharo 4 image with the demo from <a href="doc.html#esug2016Talk">the ESUG 2016 talk</a>. The zip file includes the files (e.g. HTML) for the web app and for the PhoneGap mobile application [<a href="https://docs.google.com/uc?id=0B7fN7OYplsLmcG9Cb3pDcWVYS00&export=download" target="_blank">ZIP approx. 85 MB</a>]
</div>
</li>
<li><a name"video">Where can I find a video explanation of PharoJS with examples?</a>
<div class="answer">
<iframe width="560" height="315" src="https://www.youtube.com/embed/XWd2mCQPZUU?rel=0&showinfo=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<div class="answer">
<li><a name="contribute">How do I contribute to PharoJS?</a>
<div class="answer">
<p>
Since PharoJS is on GitHub, essentially what you do is make a fork of PharoJS, load your fork with Iceberg, make and commit your changes, and then create a pull request. Then Noury or Dave will load your pull request, test it, and put it into the PharoJS repository.
</p>
<ol>
<li>You must have a Github account, let's say: <code>YourName</code></li>
<li>On GitHub, clone <a href="https://github.com/PharoJS/PharoJS">PharoJS</a></li>
<li>Then, in a fresh image you evaluate the following
<pre>Metacello new
baseline: 'PharoJS';
repository: 'github://YourName/PharoJS';
load</pre></li>
<li><a href="https://github.com/PharoJS/pharojs/issues">Create an issue</a> if there isn't one already</li>
<li>Create a branch in Iceberg called <code>issue</code><em>nn</em> with the issue number from above</li>
<li>Apply your changes in that branch, commit locally, and push them to your clone on GitHub</li>
<li>In your browser go to <a href="https://github.com/PharoJS/PharoJS">PharoJS on github</a> and click on "new pull request" button. In the drop down, select your pull request + branch</li>
<li>Wait for your contribution to be incorporated (posting on <a href="https://pharojs.slack.com/">the PharoJS Slack</a> would be nice). Thanks!!!</li>
</ol>
</div>
</li>
<li><a name"goodies">Are there any PharoJS goodies (Shirts, Mugs...)?</a>
<div class="answer">
Different PharoJS goodies are available. Please click on the picture below.
<center><a onclick="" href="http://www.zazzle.com/collections/pharojs-119096008034094659" target="_blank"><img src="img/swag.jpg"></a></center>
</div>
</li>
</ul>
<h2>PharoJS code questions</h2>
<ul class="openClose">
<li><a>How can I create a <code>KeyboardEvent</code> or <code>MouseEvent</code>?</a>
<div class="answer">
<pre>
window KeyboardEvent new: #keypress with: { #key -> #Enter. #keyCode -> 13. #which -> 13 } asDictionary
window MouseEvent new: #click
</pre>
</div>
</li>
<li><a>How can I create a DOM object?</a>
<div class="answer">
<pre>
rectangle := document createElement: 'div'.
rectangle id: 'grn'.
document body appendChild: rectangle.
</pre>
</div>
</li>
<li><a>How do I reference a field in a native Javascript object? Call a method?</a>
<div class="answer">
<p>
In most cases, simply treating it as a message send works:
<pre>
jsobj fieldName. " equivalent to jsobj.fieldName "
jsobj fieldName: 0. " equivalent to jsobj.fieldName=0 "
</pre>
You do exactly the same to call a method:
<pre>
jsobj methodName. " equivalent to jsobj.methodName() "
jsobj methodName: 0. " equivalent to jsobj.methodName(0) "
jsobj methodName: 0 foo: 42. " equivalent to jsobj.methodName(0,42) "
jsobj methodName: 0 bar: 42. " equivalent to jsobj.methodName(0,42) "
</pre>
Notice that the message name for multiple parameters is only matched to the first parameter, so the last 2 examples are functionally identical.
</p>
<p>
If the field is a method (function) then simply referencing it will call it, so to get or change the value you must use:
<pre>
jsobj instVarNamed:#count.
jsobj instVarNamed:#count put: 0.
</pre>
You can also use this to force creation of a slot.
</p>
<p>
There is a minor detail about Javascript "classes" such as <code>MouseEvent</code> because they are implemented as special Javascript functions, but usually you don't want to call them, you want to send them a <code>new</code> message.
So if the message name starts with an uppercase letter, it is treated as a value reference, rather than as a function.
This allows the following to work:
<pre>
window MouseEvent new: #click
</pre>
</p>
</div>
</li>
<li><a>Why can't PharoJS do ...?</a>
<div class="answer">
<p>
The premise of PharoJS is that you can develop code in the Pharo IDE with the GUI in the browser, including running tests.
Then when you're finished, you can export the whole thing to Javascript and have it execute the same way (or similar, see the <a href="#differences">question about differences</a>).
</p>
<p>
To achieve this, we communicate across a bridge between the running Pharo image and the running browser (see the <a href="#bridge">question about the bridge</a>).
This means that there are limitations in how processing on the browser side can proceed.
Most paticularly, you can't call-back a block (and wait for the return value) from within code running on the browser.
The closest you can do is call a block, and pass it a continuation block as a parameter.
It could then run on the Pharo side, and call the browser block passing the result.
</p>
<p>
We control the transpilation to Javascript, but we have no control over the Pharo Smalltalk compilation, so we can't add any fundamentally new concept.
</p>
</div>
</li>
<li><a name="bridge">How does the bridge work? What are proxies?</a>
<div class="answer">
<p>
The bridge is used during development to connect the running Pharo image and the running PharoJS code in the browser - but does not exist in production code.
The bridge is implemented over a WebSocket which allows bi-directional asynchronous communication.
</p>
<p>
There are proxy objects on the Pharo side that represent objects on the browser.
Some of there are global proxies, such as <code>document</code>, <code>window</code>, etc. which are implemented as Javascript globals (see the <a href="#globals">question on globals</a>).
When you send a message to a proxy, a DNU intervenes and sends the parameters across the bridge to the browser, which calls the method on the object referred to by the proxy, and returns the result.
<ul>
<li>
If that result is a simple value (number, boolean, string, nil) it is returned across the bridge to the Pharo side which has suspended your code, awaiting the response.
The response is converted to a Smalltalk value, and returned to your code.
</li>
<li>
If that result is not a simple value, a reference to that value is created (so it doesn't get garbage collected by the browser), and a reference is passed back across the bridge.
That reference is converted to a proxy, and that proxy is returned to your code.
</li>
<li>
When all references to that proxy have disappeared on the Pharo side, the browser side is told to remove the holding reference.
</li>
</ul>
</p>
<p>
The other main thing that comes across the bridge are callbacks.
A callback is a reflection of an event occuring on the broswer.
The structure for callbacks is created by sending an <code>addEventListener:block:</code> message to a proxy (see the <a href="#addEventListener">question on this</a>).
In order to faithfully emulate the Javascript execution model, no callback can happen while an existing callback is executing, or while any initial interactionwith the browser is taking place.
</p>
</div>
</li>
<li><a name="addEventListener">Why do I need to use <code>addEventListener:block:</code> to set event handlers?</a>
<div class="answer">
The structure for callbacks is created by sending an <code>addEventListener:block:</code> message to a proxy (typically of a DOM object).
The referenced Smalltalk block must be held as long as there are references to it on the browser.
Unfortunately, there isn't currently a weak data-structure available in browsers that would allow us reliably to clean these up.
So we resort to using reference counting, so you also need to use <code>addEventListener:block:</code> with a <code>nil</code> block to remove the callback.
Not explicitly removing a callback can lead to memory being tied up on the Pharo side until the bridge is closed, but seems like a reasonable trade-off as it only applies during development.
</div>
</li>
<li><a name="differences">What are the differences between normal Smalltalk and PharoJS Smalltalk?</a>
<div class="answer">
We try very hard to make the translation from Smalltalk to Javascript seamless, but there are some differences that are either impossible to handle, or would be hugely inefficient:
<ul>
<li>The numeric stack: in Javascript all numbers are IEEE <code>double</code>s, so there are no big-ints, or fractions.</li>
<li><code>thisContext</code> does not exist, so any method that references it will be uncompilable.</li>
<li><code>become:</code> is unsupported.</li>
<li>Primitives are not supported.</li>
<li>There are no processes.</li>
<li><code>SequenceableCollection>>reverse</code> is defined by ANSI Smalltalk to make a copy, but ECMAScript (aka Javascript) defines it to reverse in place. Instead you should use <code>reversed</code> which is defined in Pharo and PharoJS to return a copy.</li>
<li><code>asArray</code> always returns a copy of the receiver, whereas in Pharo <code>Array>>asArray</code> returns <code>self</code>. <code>Array</code>s and <code>OrderedCollection</code>s are the same thing in PharoJS, so outside of tests, it is probably better to use <code>asOrderedCollection</code>, but this also always returns a copy.</li>
</ul>
</div>
</li>
<li><a name="importMethods">How do I reference methods from Pharo classes (like <code>Number class>>#primesUpTo:</code>?</a>
<div class="answer">
Unfortunately, “<em>automatically importing methods from Pharo classes for classes we have duplicated (like <code>Number</code>) or other classes without importing the whole class</em>” is on our todo list.
In the meantime, add the following to <code>PjNumber</code> class-side:
<pre>jsTranspilationImportMethodsNonstandard
<pharoJsSkip>
^ {
Number class -> #(primesUpTo: primesUpTo:do:).
}</pre>
This says to import those 2 methods from the <code>Number</code> class.
This is the user definable version of <code>jsTranspilationImportMethods</code> (for which <code>PjNumber</code> has quite a large implementation) which allows you to augment the list of methods that it imports.
You should put this method in <code>PjNumber class</code> with a category of <code>*YourPackage</code> so that it will be associated with your package by Monticello or Iceberg.
<p>
We can't import all of e.g. <code>Number</code> or <code>Object</code>, because it would cause considerable code bloat in your application, and because there are methods in some of those classes that cannot be compiled (see the <a href="#differences">question on differences</a>).
</p>
</div>
</li>
<li><a>Can I generate code for NodeJS? Can I use Node modules?</a>
<div class="answer">
<p>
Yes, you certainly can use Node.
Many people would question the decision as they would prefer to provide the application in Pharo directly, with all the advantages that entails.
However, there are situations where deploying as Node is preferred (such as creating an Electron application - before you ask: we don't have that quite figured out yet, but we'd help someone who wants to).
</p>
<p>
Subclass <code>PjNodeApplication</code>.
</p>
<p>
To access modules, define a polyfill similar to <code>PjWebSocketPolyfill</code> (see the <a href="#polyfills">question on polyfills</a>).
</p>
</div>
</li>
<li><a name="polyfills">What are polyfills?</a>
<div class="answer">
<p>
<a href="https://en.wikipedia.org/wiki/Polyfill_(programming)">A polyfill</a> is <a href="https://en.wikipedia.org/wiki/Shim_(computing)"> a code shim</a> that implements a feature on Javascript engines that do not otherwise support the feature.
In PharoJS, polyfills add code at the beginning of the generated Javascript that does whatever is necessary to make a global value/class available in the Javascript engine.
As such, a polyfill is a Javascript global (see the <a href="#globals">question on globals</a>).
If the values aren't predefined and some initialization is required in order for them to be available (see the <a href="#bridge">question on the bridge</a>), then Javascript code needs to be generated to do that initialization.
</p>
<p>
The code for doing this initialization is placed in the methods <code>browserPolyfill:</code>, <code>domPolyfill:</code>, and <code>nodePolyfill:</code>.
A given polyfill may have all three methods, but often they are actually defining globals that are available on other Javascript engines.
For example, <code>PjProcessPolyfill</code> creates a value <code>process</code> for browsers that is comparable to the <code>process</code> value that exists in NodeJS.
Similarly, <code>PjWebSocketPolyfill</code> makes available a websocket in NodeJS that is comparable to the <code>WebSocket</code> code that exists in web servers.
Copying one of these is usually a good starting point for making your own polyfills.
</p>
</div>
</li>
<li><a name="globals">How do I access Javascript global values?</a>
<div class="answer">
<p>
PharoJS uses the Pharo Smalltalk pool dictionary facility to make Javascript globals available to code running in the Pharo image.
In particular groups of such symbols are defined in subclasses of <code>PjJavascriptGlobals</code>.
The appropriate globals are automatically available to subclasses of <code>PjApplication</code>.
If you need them in a class that doesn't subclass that, add a <code>poolDictionaries:</code> parameter to your <code>subclass:instanceVariableNames:classVariableNames:package:</code> (see <code>PjBrowserApplication</code> for an example).
</p>
<p>
If you create your own globals, you should use the trait <code>PjTJavascriptGlobalsInitializer</code>, which defines a class <code>initialize</code> method so that a proxy is available.
The simplest thing to do is to copy the <code>PjUniversalGlobals</code> class which make some common values available as <code>classVariableNames:</code>.
</p>
<p>
If the global you want to use is from some Javascript library or otherwise needs some initialization, you should look at the <a href="#polyfills">question on polyfills</a>.
</div>
</li>
</ul>
<h2>Dealing with errors</h2>
<ul class="openClose">
<li><a>PharoJS tests and playground raise on Safari error: <code>SecurityError (DOM Exception 18): The operation is insecure</code></a>
<div class="answer">
<p>This error prevents running PharoJS tests with Safari as a default browser. This problem also arises when opening a PharoJS playground on a app (e.g. `PjCounterBrowserApp`).</p>
<p>The cause of the bug is that currently, we open HTML files from the disk. This works fine with Firefox and Chrome. But, <a href="https://stackoverflow.com/questions/24997292/localstorage-access-from-local-file">Safari's new security system forbids accessing JS global `localStorage` when the HTML or the JS code is loaded from <code>file://</code></a></p>
<p>Luckily, there is a workaround. First, ensure you have the <a href="https://developer.apple.com/safari/tools/">Safari developer tools installed</a>. Then, in the `Develop` menu check the "Disable local file restrictions" item.</p>
<p>This is needed only during development. Once your app is ready for production, you export the code and make it available on a server. You should not experience the issue.</p>
<p>Don't forget that PharoJS allows building HTML apps. Thus, if you require accessing sensitive resources, you are likely to have to define a <a href="https://content-security-policy.com/">content security policy</a> in your HTML file using the <code>meta</code> tag. You can find some examples in <a href="https://stackoverflow.com/questions/30280370/how-does-content-security-policy-work">the Stack overflow response addressing this question</a></p>
</div>
</li>
<li><a>I can't open an exported app, even when I have the HTML: <code> CORS security error</code></a>
<div class="answer">
<p>On Firefox, open <code>about:config</code> and then enter the setting <code>privacy.file_unique_origin</code>, as <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSRequestNotHttp">explained here</a>.</p>
<p>Similar problem on Chrome <code>URL scheme must be "http" or "https" for CORS request.</code> - See <a href="https://stackoverflow.com/questions/3102819/disable-same-origin-policy-in-chrome">this stackoverflow answer</a></p>
</div>
</li>
<li><a>When I run the tests, browser windows don't get closed.</a>
<div class="answer">
This is a security feature noticed in Chrome and Firefox.
In Firefox, you can open up <code>about:config</code> and set <code>allow_scripts_to_close_windows</code> to true.
<br>
See <a href="https://stackoverflow.com/questions/19761241/window-close-and-self-close-do-not-close-the-window-in-chrome">this StackOverflow question</a> for more details and work-arounds.
</div>
</li>
</ul>
<h2>Other Questions</h2>
<p>
If you have other questions, the best place to ask is the Slack workspace (see the <a href="#help">question on getting help</a>).
As questions are asked there, they get added to this FAQ.
</p>
<!-- -->
<script src="openClose.js" type="text/javascript"></script>
</body>
</html>
<!--
-*- Local Variables:
-*- coding: utf-8
-*- mode: HTML
-*- fill-column: 1000
-*- End:
-->