Skip to content

Sync with upstream @ 540d753e #375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 111 commits into from
Closed
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
cd86528
Fix typo
wdscxsj Oct 3, 2022
165a3f5
typo "optimzed
joaquinelio Oct 3, 2022
f0fa52f
Fix typo
wdscxsj Oct 4, 2022
88d9b3f
removed -> remove; optimzed -> optimized ;
sagarpanchal Jan 18, 2023
023c0ec
Fixing a minor grammatical typo in the document.
rahulrao0209 Jul 16, 2023
8ab6b39
Add WeakRef and FinalizationRegistry article
WOLFRIEND Nov 4, 2023
5ab1ce2
Merge pull request #3609 from WOLFRIEND/master
iliakan Nov 15, 2023
b7ebc1b
Improve awkward sentence structure
smith558 Nov 24, 2023
75bad83
Improve grammar
smith558 Nov 24, 2023
74a8a19
Fix formatting char
smith558 Nov 27, 2023
d51037a
Fix grammar and add an example
nakhodkin Dec 27, 2023
c66bace
Fix grammar and typos
nakhodkin Dec 31, 2023
bbac8a5
Fix grammar and JavaScript syntax
nakhodkin Jan 2, 2024
1b9a28b
Update article.md
JaFro96 Jan 4, 2024
4ec440f
Update article.md
JaFro96 Jan 4, 2024
d83bfb2
refactor: Updated RFC spec Safe Methods URL in Cookies chapter
alexandermirzoyan Jan 12, 2024
9ec34c6
Replace assignment with equals in Truncate the text task
CJDumbleton Jan 17, 2024
2e0d5fb
Add missing word
qadzek Jan 20, 2024
52e184c
Add missing CSS unit
qadzek Jan 20, 2024
0530c92
fix square brackets
mikayel00 Jan 24, 2024
9270fe5
Merge pull request #3639 from JaFro96/master
smith558 Jan 24, 2024
f0f1006
fix: json
mikayel00 Jan 24, 2024
c98ec82
Merge pull request #3647 from CJDumbleton/CJDumbleton-patch-1
smith558 Jan 24, 2024
9c07c5b
Merge pull request #3649 from qadzek/patch-1
smith558 Jan 24, 2024
1a6edd7
Merge pull request #3656 from mikayel00/fix-brackets
smith558 Jan 26, 2024
9d157d8
Merge pull request #3617 from smith558/patch-3
smith558 Jan 27, 2024
b6c604a
Merge branch 'master' into patch-5
smith558 Jan 27, 2024
4286703
Merge pull request #3644 from alexandermirzoyan/patch-2
smith558 Jan 27, 2024
26ac4c8
Merge pull request #3212 from wdscxsj/patch-2
smith558 Jan 27, 2024
f24e463
Merge pull request #3213 from joaquinelio/patch-15
smith558 Jan 27, 2024
774d0c1
Merge pull request #3326 from sagarpanchal/patch-2
smith558 Jan 27, 2024
aacfc93
Fixed grammar error in regex-groups article.md
eedrxs Jan 27, 2024
e2ac312
Merge pull request #3659 from eedrxs/patch-1
smith558 Jan 27, 2024
ee62307
Update article.md
nepikn Feb 2, 2024
4a20875
Update article.md
smith558 Feb 11, 2024
5ce4b3a
Merge pull request #3664 from nepikn/patch-2
smith558 Feb 11, 2024
daca277
Fix grammar
smith558 Feb 11, 2024
3e92613
Improve options description
smith558 Feb 11, 2024
b6e7472
Update samesite content
smith558 Feb 13, 2024
d134cab
Remove "surely"
smith558 Feb 13, 2024
19e62af
Update article.md
smith558 Feb 13, 2024
a7d351f
change IndexedDb to IndexedDB (#3660)
0xtpsl Feb 13, 2024
ab1db04
Fix grammar and typos (#3628)
nakhodkin Feb 14, 2024
d461a93
Merge pull request #3217 from wdscxsj/patch-3
smith558 Feb 14, 2024
00bdf88
Update LICENSE.md
iliakan Mar 8, 2024
25c9bdf
Update LICENSE.md
iliakan Mar 8, 2024
c13e707
Update LICENSE.md
iliakan Mar 8, 2024
e15f535
Update LICENSE.md
iliakan Mar 8, 2024
2f91d87
Update LICENSE.md
iliakan Mar 8, 2024
ea05aa9
Updated result visualization
Filin3 Mar 31, 2024
04b73bf
Merge pull request #3681 from Filin3/patch-1
smith558 May 5, 2024
acf339c
Merge pull request #3632 from nakhodkin/patch-2
smith558 May 5, 2024
541b7f9
Merge pull request #3636 from nakhodkin/patch-5
smith558 May 5, 2024
0b9bc2f
Merge pull request #3634 from nakhodkin/patch-4
smith558 May 5, 2024
85da6f1
Update article.md
ellie-heidari May 10, 2024
475899e
Update article.md
smith558 May 17, 2024
7e524ba
Add link
smith558 May 17, 2024
42851f4
Update task.md
pvepamb1 May 18, 2024
2092da7
Merge pull request #3694 from pvepamb1/patch-1
smith558 May 18, 2024
f684d39
change example element of multidimensional array
sneeed Jun 8, 2024
c151e11
minor fixes
iliakan Jun 13, 2024
3fd3f98
- `run`
Alexandre887 Jun 23, 2024
d1ffe5d
docs: remove eval polyfill.io
kricsleo Jul 5, 2024
262f91a
Merge pull request #3712 from kricsleo/patch-1
smith558 Jul 8, 2024
815fafa
Merge pull request #3692 from ellie-heidari/patch-1
smith558 Jul 8, 2024
d6e0376
Remove BigInt IE incompatibility part (#3709)
FloffyGarlic Jul 9, 2024
5a0df77
Update article.md
shallow-beach Jul 10, 2024
62827d8
Merge pull request #3715 from shallow-beach/master
smith558 Jul 10, 2024
4104eba
Merge pull request #3704 from Alexandre887/patch-10
smith558 Jul 10, 2024
ca72abb
Merge pull request #3700 from sneeed/patch-2
smith558 Jul 10, 2024
b258d7d
Merge pull request #3495 from rahulrao0209/patch-1
smith558 Jul 10, 2024
6f08958
minor fix to function name written in explanation
tonybishnoi Oct 9, 2024
eedc262
Grammatical fix
nikoandpiko Oct 22, 2024
34a80e7
Merge pull request #3768 from tonybishnoi/patch-1
smith558 Oct 24, 2024
67833c9
Update article.md
zakingslayerv22 Dec 11, 2024
b36823a
better wording
pj-szdm Dec 18, 2024
dc14378
Update article.md
mhi1627 Jan 22, 2025
8b2a2f2
Improve readability
AdityaGirdhar Jan 30, 2025
a2b97b5
Merge pull request #3810 from AdityaGirdhar/patch-1
smith558 Feb 4, 2025
793ad4b
Merge branch 'master' into patch-1
smith558 Feb 4, 2025
e466826
Merge pull request #3772 from nikoandpiko/patch-1
smith558 Feb 4, 2025
a087279
Merge pull request #3804 from mhi1627/patch-1
smith558 Feb 8, 2025
26daef2
Merge pull request #3794 from zakingslayerv22/patch-1
smith558 Feb 8, 2025
6236eb8
Merge pull request #3797 from pj-szdm/patch-1
smith558 Feb 8, 2025
011dd4f
Update article.md
Gleb-Pastushenko Feb 10, 2025
1dce5b7
Merge pull request #3815 from Gleb-Pastushenko/patch-6
smith558 Feb 10, 2025
4b3474b
Fixed missing closing parenthesis in 2-ui/4-forms-control/1-form-elem…
vincent-clipet Mar 10, 2025
3d7abb9
Merge pull request #3822 from vincent-clipet/fix/missing_parenthesis
smith558 Mar 10, 2025
3de63df
promise.all task
iliakan Mar 24, 2025
ef31066
minor fixes
iliakan Mar 24, 2025
de4247b
minor fixes
iliakan Mar 24, 2025
0af25bc
minor fixes
iliakan Mar 24, 2025
d932e52
minor fixes
iliakan Mar 24, 2025
f0d8abb
minor fixes
iliakan Mar 24, 2025
f775835
minor fixes
iliakan Mar 24, 2025
0760c90
minor fixes
iliakan Mar 24, 2025
5dea441
minor fixes
iliakan Mar 24, 2025
035c526
minor fixes
iliakan Mar 24, 2025
4c4598b
Fix for #3826 - Removed errorception.com from the respective md file.
Paramesh-T-S Mar 29, 2025
cde189d
Update Safari settings screenshots
dangerman Apr 2, 2025
78c6c44
Update Safari devtools instructions
dangerman Apr 2, 2025
e88c212
Update Function object, NFE article
rahulrao0209 Apr 6, 2025
a711a1f
Merge pull request #3830 from Paramesh-T-S/Issue_fix_Outdate_-link_in…
iliakan Apr 8, 2025
efaa9aa
sentry.io added as per suggestion - https://github.com/javascript-tut…
Paramesh-T-S Apr 8, 2025
6cc5077
Merge pull request #3835 from Paramesh-T-S/Issue_fix_Outdate_-link_in…
iliakan Apr 9, 2025
81cfee9
Update article.md
rahulrao0209 Apr 13, 2025
e2d8ebe
Merge pull request #3836 from rahulrao0209/patch-3
smith558 Apr 13, 2025
6bbdd0c
Merge pull request #3834 from rahulrao0209/patch-2
smith558 Apr 13, 2025
d3c395c
Merge pull request #3832 from dangerman/update-safari-devtools-screen…
smith558 Apr 13, 2025
540d753
Replace with up to date screenshots
smith558 Apr 13, 2025
0d95d4e
merging all conflicts
iliakan May 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: iliakan
6 changes: 5 additions & 1 deletion 1-js/01-getting-started/4-devtools/article.md
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@

پنل توسعه‌دهندگان به صورت پیش‌فرض روی تب Console باز می‌شود و شما چیزی شبیه به این را باید ببینید :

![chrome](chrome.png)
![chrome](chrome.webp)

ظاهر دقیق این پنل بر اساس نسخه Chrome تغییر می‌کند ولی در نهایت چیزی شبیه به این خواهد بود.

@@ -42,7 +42,11 @@

این مرورگر (فقط در سیستم عامل Mac و نه در Windows و Linux) در اینجا مقداری متفاوت است و ابتدا باید ویژگی Developer menu را فعال کنیم.

<<<<<<< HEAD
به Preferences رفته و به تب Advanced بروید و تیک مربوط به Developer menu را در پایین فعال کنید.
=======
Open Settings and go to the "Advanced" pane. There's a checkbox at the bottom:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
![safari](safari.png)

Binary file removed 1-js/01-getting-started/4-devtools/chrome.png
Binary file not shown.
Binary file added 1-js/01-getting-started/4-devtools/chrome.webp
Binary file not shown.
Binary file added 1-js/01-getting-started/4-devtools/chrome@2.webp
Binary file not shown.
Binary file removed 1-js/01-getting-started/4-devtools/chrome@2x.png
Binary file not shown.
Binary file modified 1-js/01-getting-started/4-devtools/safari.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified 1-js/01-getting-started/4-devtools/safari@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions 1-js/02-first-steps/04-variables/article.md
Original file line number Diff line number Diff line change
@@ -88,16 +88,26 @@ let user = 'John'
*!*var*/!* message = 'سلام';
```

<<<<<<< HEAD
کلیدواژه `var` تقریبا* با* `let` یکسان است. آن هم یک متغیر را تعریف می‌کند، ولی روش کار آن قدیمی است.

تفاوت‌های کوچکی بین این دو وجود دارد که در حال حاضر به آن نمی‌پردازیم. در مبحث <info:var> با جزییات به آن خواهیم پرداخت.
=======
The `var` keyword is *almost* the same as `let`. It also declares a variable but in a slightly different, "old-school" way.

There are subtle differences between `let` and `var`, but they do not matter to us yet. We'll cover them in detail in the chapter <info:var>.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
````
## یک مقایسه‌ی واقعی
برای درک مفهوم متغیر، می‌توانیم آن را یک جعبه برای نگهداری اطلاعات تصور کنیم، که یک نام منحصربه‌فرد روی آن چسبانده‌ایم.
<<<<<<< HEAD
برای نمونه، تصور کنید متغیر "`message`" در جعبه‌ای با برچسب `message` و با مقدار "`سلام!`" داخل آن وجود دارد:
=======
For instance, the variable `message` can be imagined as a box labelled `"message"` with the value `"Hello!"` in it:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
![](variable.svg)
@@ -196,15 +206,24 @@ let my-name; // خط‌های پیوند '-' در نام‌ها ممنوع هس
متغیرهای apple و Apple دو متغیر متفاوت هستند.
```

<<<<<<< HEAD
````smart header="کاراکترهای غیر انگلیسی مجاز هستند ولی توصیه نمی‌شوند"
امکان استفاده از هر زبانی، شامل حروف cyrillic، لوگوگرام‌های چینی و دیگر زبان‌ها وجود دارد، مثلا اینگونه::
=======
````smart header="Non-Latin letters are allowed, but not recommended"
It is possible to use any language, including Cyrillic letters, Chinese logograms and so on, like this:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```js
let имя = '...';
let 我 = '...';
```
<<<<<<< HEAD
از نظر فنی، این‌ها درست کار می‌کنند و مجاز هستند، ولی بر اساس یک قائده بین‌المللی برای نام متغیرها از زبان انگلیسی استفاده می‌شود. حتی اگر اسکریپتی کوچک می‌نویسیم، ممکن است تا مدت طولانی‌ای مورد استفاده و توسعه قرار بگیرد. اشخاصی از سایر کشورها ممکن است نیاز باشد روزی آن اسکریپت را بخوانند.
=======
Technically, there is no error here. Such names are allowed, but there is an international convention to use English in variable names. Even if we're writing a small script, it may have a long life ahead. People from other countries may need to read it sometime.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
````

````warn header="نام‌های رِزِرو شده"
@@ -261,10 +280,18 @@ const myBirthday = '18.04.1982';
myBirthday = '01.01.2001'; // !ارور، نمی‌توان ثابت را دوباره مقداردهی کرد
```
<<<<<<< HEAD
وقتی برنامه‎‌نویس اطمینان دارد که متغیری هیچگاه تغییر نمی‌کند می‌تواند آن را به عنوان const تعریف کند تا برای دیگران نیز این موضوع واضح باشد.
=======
When a programmer is sure that a variable will never change, they can declare it with `const` to guarantee and communicate that fact to everyone.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
<<<<<<< HEAD
### ثابت‌های با حروف بزرگ
=======
There is a widespread practice to use constants as aliases for difficult-to-remember values that are known before execution.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
به صورت یک عُرفِ همه‌گیر، از ثابت‌های با حروف بزرگ به عنوان نام مستعار برای مقادیری که به خاطر سپردن آنها دشوار است، استفاده می‌شود. این دسته از ثابت‌ها اصطلاحا prior to execution (پیش از اجرای برنامه) مقدارشان مشخص است.
@@ -291,15 +318,23 @@ alert(color); // #FF7F00
چه زمانی باید از حروف بزرگ و چه زمانی باید از حروف معمولی برای نام‌گذاری یک constant استفاده کنیم؟ بیایید قضیه را روشن کنیم.
<<<<<<< HEAD
ثابت بودن یک متغیر صرفا بدین معناست که مقدار آن تغییر نخواهد کرد. یک دسته از ثابت‌ها پیش از اجرای برنامه مقدارشان مشخص خواهد بود (مثل هگزادسیمال برای رنگ قرمز) و دسته دیگر در حین اجرای (Run Time) برنامه مقدارشان مشخص می‌شود، اما پس از مقدار دهی اولیه مقدارشان تغییر نمی‌کند.
=======
Being a "constant" just means that a variable's value never changes. But some constants are known before execution (like a hexadecimal value for red) and some constants are *calculated* in run-time, during the execution, but do not change after their initial assignment.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
برای نمونه:
```js
const pageLoadTime = /* زمان بارگیری برای یک صفحه وب */;
```
<<<<<<< HEAD
مقدار `pageLoadTime` پیش از اجرای برنامه مشخص نبوده و به همین دلیل به صورت عادی نوشته شده است. اما همچنان یک ثابت است چراکه زمان اجرای برنامه بعد از مقداردهی دیگر تغییر نخواهد کرد.
=======
The value of `pageLoadTime` is not known before the page load, so it's named normally. But it's still a constant because it doesn't change after the assignment.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
به تعبیری دیگر، ثابت‌های با حروف بزرگ فقط به عنوان نام مستعار برای مقدارهای «hard-coded» استفاده می‌شوند.
@@ -309,18 +344,31 @@ const pageLoadTime = /* زمان بارگیری برای یک صفحه وب */;
نام یک متغیر باید معنی واضح و روشنی داشته باشد که داده درون خود را توصیف می‌کند.
<<<<<<< HEAD
انتخاب نام برای متغیرها یکی از کارهای مهم و پیچیده در برنامه‌نویسی است. یک نگاه سریع به نام متغیرها می‌تواند تفاوت یک برنامه‌نویس تازه‌کار و با تجربه را نشان دهد.
در پروژه‌های واقعی، بجای از صفر نوشتن برنامه‌ها، بیشتر زمان صرف اصلاح و توسعه کدهای موجود می‌شود. وقتی پس از مدتی به کدهای قبلی باز می‌گردیم، بدست آوردن اطلاعات از آن دسته کدهایی که نام‌گذاری‌های خوبی دارند بسیار راحت‌تر است (منظور متغیرهایی است که نام‌های خوبی دارند).
=======
Variable naming is one of the most important and complex skills in programming. A glance at variable names can reveal which code was written by a beginner versus an experienced developer.
In a real project, most of the time is spent modifying and extending an existing code base rather than writing something completely separate from scratch. When we return to some code after doing something else for a while, it's much easier to find information that is well-labelled. Or, in other words, when the variables have good names.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
لطفا پیش از انتخاب نام برای یک متغیر، خوب به آن فکر کنید. ثمره‌ی آن را خواهید دید.
بعضی از دستورالعمل‌های مفید:
<<<<<<< HEAD
- از نام‌هایی که برای انسان قابل فهم است استفاده کنید مانند `userName` یا `shoppingCart`.
- از نام‌های مخفف یا کوتاه استفاده نکنید مانند `a`، `b`، `c`، مگر آنکه واقعا بدانید چه می‌کنید.
- نام‌های کاملا واضح و مختصر انتخاب کنید. نمونه‌هایی از نام‌های بد `data` و `value` هستند. این نام‌ها هیچ‌چیز را توضیح نمی‌دهند. استفاده از این دست نام‌ها فقط زمانی قابل قبول است که محتوای کدی که می‌نویسید به طور استثنا مشخص کند که data یا value به چه چیزی اشاره می‌کند.
- با ذهن خود و تیم کاریتان توافق نظر داشته باشید. اگر به مخاطبان وب‌سایت‌تان user می‌گویید، متغیرهای مرتبط را بجای `currentVisitor` یا `newManInTown` باید `currentUser` یا `newUser` بنامید.
=======
- Use human-readable names like `userName` or `shoppingCart`.
- Stay away from abbreviations or short names like `a`, `b`, and `c`, unless you know what you're doing.
- Make names maximally descriptive and concise. Examples of bad names are `data` and `value`. Such names say nothing. It's only okay to use them if the context of the code makes it exceptionally obvious which data or value the variable is referencing.
- Agree on terms within your team and in your mind. If a site visitor is called a "user" then we should name related variables `currentUser` or `newUser` instead of `currentVisitor` or `newManInTown`.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```smart header="ایجاد یا استفاده مجدد?"
و آخرین نکته: برخی برنامه‌نویسان تنبل بجای تعریف متغیرهای جدید، از متغیرهای موجود دوباره استفاده می‌کنند.
3 changes: 3 additions & 0 deletions 1-js/02-first-steps/05-types/article.md
Original file line number Diff line number Diff line change
@@ -94,13 +94,16 @@ const bigInt = 1234567890123456789012345678901234567890n;

به دلیل اینکه اعداد `BigInt` به ندرت نیاز می‌شوند، ما آنها را اینجا پوشش نمی‌دهیم، اما یک فصل <info:bigint> جدا به آنها اختصاص دادیم. زمانی که به چنین اعداد بزرگی نیاز داشتید آن را بخوانید.

<<<<<<< HEAD

```smart header="مشکلات سازگاری"
در حال حاضر، `BigInt` در Firefox/Chrome/Edge/Safari پشتیبانی می‌شوند، اما در IE خیر.
```

شما می‌توانید [جدول سازگاری BigInt در *MDN*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) را برای اینکه بدانید کدام نسخه از مرورگرها پشتیبانی می‌شوند، بررسی کنید.

=======
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
## String

در جاوااسکریپت یک رشته (string) باید توسط کوتِیشِن‌ها احاطه شود.
2 changes: 1 addition & 1 deletion 1-js/02-first-steps/16-function-expressions/article.md
Original file line number Diff line number Diff line change
@@ -82,7 +82,7 @@ let sayHi = function() { // (1) ایجاد
alert( "سلام" );
};

let func = sayHi;
let func = sayHi; //(2)
// ...
```

14 changes: 14 additions & 0 deletions 1-js/03-code-quality/06-polyfills/article.md
Original file line number Diff line number Diff line change
@@ -7,7 +7,11 @@

پس کاملا طبیعی است که یک موتور فقط بخشی از یک استاندارد را پیاده‌سازی کند.

<<<<<<< HEAD
یک صفحه‌ی خوب برای این که ببینید در حال حاضر چه چیزهایی پشتیبانی می‌شود اینجاست <https://compat-table.github.io/compat-table/es6/> (خیلی بزرگ است، ما چیزهای زیادی برای مطالعه داریم).
=======
A good page to see the current state of support for language features is <https://compat-table.github.io/compat-table/es6/> (it's big, we have a lot to study yet).
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
به عنوان توسعه‌دهنده، ما همیشه دوست داریم از ویژگی‌ها و امکانات جدید استفاده کنیم. هر چه جدیدتر، بهتر!

@@ -71,10 +75,14 @@ if (!Math.trunc) { // اگر چنین تابعی وجود ندارد
جاوااسکریپت یک زبان به‌شدت داینامیک است. اسکریپت‌ها می‌توانند هر تابعی را تغییر دهند یا اضافه کنند. حتی تابع‌های built-in.
<<<<<<< HEAD
دو کتابخانه جالب پلیفیل‌ها:
- [core js](https://github.com/zloirock/core-js) که از چیزهای زیادی پشتیبانی می‌کند و اجازه می‌دهد فقط فیچرهای مورد نیاز خود را اضافه کنید.
- [polyfill.io](http://polyfill.io) سرویسی که یک اسکریپت با پلیفیل‌ها ارائه می‌دهد. بسته به فیچرها و مرورگر کاربر.
=======
One interesting polyfill library is [core-js](https://github.com/zloirock/core-js), which supports a wide range of features and allows you to include only the ones you need.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
## خلاصه
@@ -84,9 +92,15 @@ if (!Math.trunc) { // اگر چنین تابعی وجود ندارد
برای مثال، بعدها که با جاوااسکریپت آشنایی بیشتری پیدا کنید، می‌توانید یک سیستم build کد با [webpack](https://webpack.js.org/) و پلاگین [babel-loader](https://github.com/babel/babel-loader) راه‌اندازی کنید.
<<<<<<< HEAD
منابع خوبی که نشان می‌دهند فیچرهای مختلف در چه حالتی از پشتیبانی قرار دارند:
- <https://compat-table.github.io/compat-table/es6/> - برای جاوااسکریپت.
- <https://caniuse.com/> - برای تابع‌های مربوط به مرورگر.
=======
Good resources that show the current state of support for various features:
- <https://compat-table.github.io/compat-table/es6/> - for pure JavaScript.
- <https://caniuse.com/> - for browser-related functions.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
پانوشت گوگل کروم معمولا نسبت به فیچرهای زبان به‌روزترین است. اگر دموی یک آموزش کار نکرد، آن را امتحان کنید. البته بیشتر دموهای آموزش با هر مرورگر مدرنی کار می‌کنند.
16 changes: 16 additions & 0 deletions 1-js/04-object-basics/04-object-methods/8-chain-calls/task.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,11 @@ importance: 2

# زنجیره‌ای

<<<<<<< HEAD
یک شیء `ladder` وجود دارد که بالا و پایین رفتن را ممکن می‌کند:
=======
There's a `ladder` object that allows you to go up and down:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```js
let ladder = {
@@ -21,7 +25,11 @@ let ladder = {
};
```

<<<<<<< HEAD
حال اگر ما نیاز داشته باشیم که برای چند بار متوالی صدا بزنیم، می‌توانیم اینگونه این کار را انجام دهیم:
=======
Now, if we need to make several calls in sequence, we can do it like this:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```js
ladder.up();
@@ -32,10 +40,18 @@ ladder.down();
ladder.showStep(); // 0
```

<<<<<<< HEAD
کد `up`، `down` و `showStep` را تغییر دهید تا صدازدن‌ها را زنجیره‌ای کنید، مثل این:
=======
Modify the code of `up`, `down`, and `showStep` to make the calls chainable, like this:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```js
ladder.up().up().down().showStep().down().showStep(); // اول 1 را نشان می‌دهد سپس 0 را
```

<<<<<<< HEAD
چنین روشی در بین کتابخانه‌های جاوااسکریپت به طور گسترده استفاده می‌شود.
=======
Such an approach is widely used across JavaScript libraries.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
4 changes: 4 additions & 0 deletions 1-js/04-object-basics/09-object-toprimitive/article.md
Original file line number Diff line number Diff line change
@@ -253,7 +253,11 @@ let obj = {
}
};

<<<<<<< HEAD
alert(obj + 2); // تبدیل به مقدار اصلی یک رشته برگرداند => ادغام، (2 + "2") 22
=======
alert(obj + 2); // "22" ("2" + 2), conversion to primitive returned a string => concatenation
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```

## خلاصه
84 changes: 84 additions & 0 deletions 1-js/05-data-types/02-number/article.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions 1-js/05-data-types/03-string/3-truncate/task.md
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ importance: 5
برای مثال:

```js
truncate("What I'd like to tell on this topic is:", 20) = "What I'd like to te…"
truncate("What I'd like to tell on this topic is:", 20) == "What I'd like to te…"

truncate("Hi everyone!", 20) = "Hi everyone!"
truncate("Hi everyone!", 20) == "Hi everyone!"
```
4 changes: 4 additions & 0 deletions 1-js/05-data-types/04-array/article.md
Original file line number Diff line number Diff line change
@@ -426,7 +426,11 @@ let matrix = [
[7, 8, 9]
];

<<<<<<< HEAD
alert( matrix[1][1] ); // 5 ،المان مرکزی
=======
alert( matrix[0][1] ); // 2, the second value of the first inner array
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```

## متد toString
99 changes: 99 additions & 0 deletions 1-js/05-data-types/05-array-methods/article.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions 1-js/05-data-types/06-iterable/article.md
Original file line number Diff line number Diff line change
@@ -174,7 +174,11 @@ while (true) {

برای مثال، رشته‌ها هم حلقه‌پذیر هستند (`for..of` روی آنها کار می‌کند) و هم شبه آرایه هستند (آنها ایندکس عددی و `length` دارند).

<<<<<<< HEAD
اما یک حلقه‌پذیر ممکن است شبه آرایه نباشد. برعکس آن هم ممکن است یعنی یک شبه آرایه ممکن است حلقه‌پذیر نباشد.
=======
But an iterable may not be array-like. And vice versa an array-like may not be iterable.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
برای مثال، در مثال بالا `range` حلقه‌پذیر است اما شبه آرایه نیست، چون ویژگی‌های ایندکسی و `length` ندارد.

72 changes: 70 additions & 2 deletions 1-js/05-data-types/10-destructuring-assignment/article.md
Original file line number Diff line number Diff line change
@@ -5,18 +5,30 @@
- شیءها به ما این امکان را می‌دهند تا چیزی بسازیم که المان‌های داده را به واسطه کلید ذخیره کند.
- آرایه‌ها به ما امکان جمع‌آوری المان‌های داده را در لیستی مرتب می‌دهند.

<<<<<<< HEAD
اگرچه، زمانی که ما آن‌ها را به تابع می‌دهیم، ممکن است که نیازی به کل یک شیء/آرایه نباشد. شاید تنها قطعه‌های تکی نیاز باشد.

*مقداردهیِ تجزیه‌کننده‌ی ساختار (Destructuring assignment)* یک سینتکس خاص است که به ما امکان می‌دهد تا آرایه‌ها یا شیءها را درون چند متغیر «پخش کنیم» چون بعضی اوقات این موضوع کار را راحت‌تر می‌کند.
=======
However, when we pass these to a function, we may not need all of it. The function might only require certain elements or properties.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
تخریب ساختار همچنین با تابع‌های پیچیده که تعداد زیادی پارامتر، مقدارهای پیش‌فرض و... دارند هم به خوبی کار می‌کند. به زودی آن را خواهیم دید.

<<<<<<< HEAD
## تجزیه ساختار آرایه
=======
Destructuring also works well with complex functions that have a lot of parameters, default values, and so on. Soon we'll see that.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
کد پایین یک مثال از چگونگی تبدیل یک آرایه به چند متغیر است:

```js
<<<<<<< HEAD
// ما یک آرایه شامل نام و نام خانوادگی داریم
=======
// we have an array with a name and surname
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
let arr = ["John", "Smith"]

*!*
@@ -40,10 +52,17 @@ alert(firstName); // John
alert(surname); // Smith
```

<<<<<<< HEAD
همانطور که می‌بینید، سینتکس ساده است. البته چند چیز ویژه در جزییات خود دارد. بیایید برای فهمیدن بهتر آن، مثال‌های بیشتری ببینیم.

````smart header="عبارت «تجزیه‌کننده‌ی ساختار» به معنی «مخرب» نیست."
این سینتکس «مقداردهی تجزیه‌کننده‌ی ساختار» نامیده می‌شود چون با کپی کردن المان‌ها در چند متغیر «ساختار را تغییر می‌دهد». اما خود آرایه تغییر نمی‌کند.
=======
As you can see, the syntax is simple. There are several peculiar details though. Let's see more examples to understand it better.
````smart header="\"Destructuring\" does not mean \"destructive\"."
It's called "destructuring assignment," because it "destructurizes" by copying items into variables. However, the array itself is not modified.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
فقط یک راه کوتاه‌تر برای نوشتن است:
```js
@@ -65,7 +84,11 @@ let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic
alert( title ); // Consul
```
<<<<<<< HEAD
در کد بالا، از المان دوم آرایه گذشتیم، المان سوم به `title` تخصیص داده شد و بقیه المان‌های آرایه هم نادیده گرفته شدند (به دلیل اینکه متغیری برای ذخیره آنها وجود ندارد).
=======
In the code above, the second element of the array is skipped, the third one is assigned to `title`, and the rest of the array items are also skipped (as there are no variables for them).
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
````

````smart header="با هر حلقه‌پذیری در سمت راست کار می‌کند"
@@ -94,18 +117,29 @@ alert(user.surname); // Smith
````

<<<<<<< HEAD
````smart header="حلقه زدن با .entries()"
در فصل قبل ما متد [Object.entries(obj)](mdn:js/Object/entries) را دیدیم.
می‌توانیم آن را با تجزیه‌کننده‌ی ساختار برای حلقه زدن در کلیدها و مقدارهای یک شیء استفاده کنیم:
=======
````smart header="Looping with .entries()"
In the previous chapter, we saw the [Object.entries(obj)](mdn:js/Object/entries) method.
We can use it with destructuring to loop over the keys-and-values of an object:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```js run
let user = {
name: "John",
age: 30
};
<<<<<<< HEAD
// حلقه زدن در کلیدها و مقدارها
=======
// loop over the keys-and-values
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
*!*
for (let [key, value] of Object.entries(user)) {
*/!*
@@ -169,7 +203,11 @@ alert(name2); // Caesar
let [name1, name2, *!*...rest*/!*] = ["Julius", "Caesar", *!*"Consul", "of the Roman Republic"*/!*];
*!*
<<<<<<< HEAD
// آرایه‌ای از المان‌ها است که از المان سوم شروع می‌شود rest
=======
// rest is an array of items, starting from the 3rd one
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
alert(rest.length); // 2
@@ -187,7 +225,11 @@ let [name1, name2, *!*...titles*/!*] = ["Julius", "Caesar", "Consul", "of the Ro

### مقدارهای پیش‌فرض

<<<<<<< HEAD
اگر آرایه از لیست متغیرهای سمت چپ کوتاه‌تر باشد، هیچ اروری ایجاد نمی‌شود. مقدارهای ناموجود undefined در نظر گرفته می‌شوند:
=======
If the array is shorter than the list of variables on the left, there will be no errors. Absent values are considered undefined:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```js run
*!*
@@ -418,7 +460,11 @@ alert( title ); // Menu
## تجزیه ساختار تودرتو
<<<<<<< HEAD
اگر یک شیء یا آرایه، شیء و آرایه‌های تودرتو دیگری را شامل شود، ما می‌توانیم از الگوری پیچیده‌تری در سمت چپ برای استخراج قسمت‌های عمیق‌تر استفاده کنیم.
=======
If an object or an array contains other nested objects and arrays, we can use more complex left-side patterns to extract deeper portions.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
در کد زیر `options` یک شیء دیگری درون ویژگی `size` و یک آرایه درون ویژگی `items` دارد. الگوی سمت چپ مقداردهی ساختار یکسانی برای استخراج مقدار از آنها را دارد:
@@ -449,7 +495,11 @@ alert(item1); // Cake
alert(item2); // Donut
```
<<<<<<< HEAD
تمام ویژگی‌های شیء `options` به جز `extra` که در سمت چپ وجود ندارد، به متغیرهای متناظر خود تخصیص داده شده‌اند:
=======
All properties of `options` object except `extra` which is absent in the left part, are assigned to corresponding variables:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
![](destructuring-complex.svg)
@@ -459,17 +509,27 @@ alert(item2); // Donut
## پارامترهای هوشمند تابع
<<<<<<< HEAD
بعضی اوقات پیش می‌آید که یک تابع پارامترهای زیادی داشته باشد که اکثر آنها الزامی نیستند. خصوصا برای رابط کاربری این اتفاق می‌افتد. یک تابع را تصور کنید که یک منو ایجاد می‌کند. این منو ممکن است دارای طول(width)، ارتفاع(height)، عنوان(title)، لیستی از کالاها و غیره باشد.
این یک راه برای نوشتن چنین تابعی است:
=======
There are times when a function has many parameters, most of which are optional. That's especially true for user interfaces. Imagine a function that creates a menu. It may have a width, a height, a title, an item list and so on.
Here's a bad way to write such a function:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```js
function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
// ...
}
```
<<<<<<< HEAD
در واقعیت، مشکل این است که چگونه ترتیب آرگومان‌ها را به یاد بسپاریم. معمولا محیط‌های کدنویسی (IDE) سعی می‌کنند به ما کمک کنند، مخصوصا اگر کد، مستند خوبی داشته باشد اما باز هم... مشکل دیگری که وجود دارد این است که چگونه یک تابع را زمانی که اکثر پارامترها به صورت پیش‌فرض مشکلی ندارند فراخوانی کنیم.
=======
In real-life, the problem is how to remember the order of arguments. Usually, IDEs try to help us, especially if the code is well-documented, but still... Another problem is how to call a function when most parameters are ok by default.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
مثلا اینگونه؟
@@ -534,7 +594,11 @@ function({
})
```

<<<<<<< HEAD
سپس به صورت پیش‌فرض، برای یک شیء شامل پارامترها، یک متغیر `varName` (اسم متغیر) برای ویژگی `incomingProperty` (ویژگی ورودی)، همراه با `defaultValue` وجود خواهد داشت.
=======
Then, for an object of parameters, there will be a variable `varName` for the property `incomingProperty`, with `defaultValue` by default.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b

لطفا در نظر داشته باشید که چنین تجزیه ساختاری فرض می‌کند که `showMenu()` یک آرگومان دارد. اگر ما تمام مقدارهای پیش‌فرض را بخواهیم، پس باید یک شیء خالی مشخص کنیم:

@@ -561,7 +625,7 @@ showMenu(); // Menu 100 200
- مقداردهی تجزیه‌کننده‌ی ساختار به ما اجازه می‌دهد تا بلافاصله یک شیء یا آرایه را روی بسیاری از متغیرها ترسیم کنیم.
- سینتکس کامل شیء:
```js
let {prop : varName = default, ...rest} = object
let {prop : varName = defaultValue, ...rest} = object
```

این به این معنی است که ویژگی `prop` باید درون متغیر `varName` برود و اگر چنین ویژگی‌ای وجود نداشت، سپس مقدار `default` باید استفاده شود.
@@ -571,9 +635,13 @@ showMenu(); // Menu 100 200
- سینتکس کامل آرایه:

```js
let [item1 = default, item2, ...rest] = array
let [item1 = defaultValue, item2, ...rest] = array
```

<<<<<<< HEAD
المان اول درون `item1` می‎رود؛ المان دوم درون `item2` می‌شود، تمام المان‌های باقی مانده آرایه `rest` را تشکیل می‌دهند.
=======
The first item goes to `item1`; the second goes into `item2`, and all the rest makes the array `rest`.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b

- استخراج داده از آرایه/شیءهای تودرتو هم ممکن است، برای اینکار سمت چپ باید ساختار یکسانی با سمت راست داشته باشد.
2 changes: 1 addition & 1 deletion 1-js/05-data-types/12-json/article.md
Original file line number Diff line number Diff line change
@@ -405,7 +405,7 @@ alert( JSON.stringify(meetup) );

سینتکس آن:
```js
let value = JSON.parse(str, [reviver]);
let value = JSON.parse(str[, reviver]);
```

پارامتر str
4 changes: 4 additions & 0 deletions 1-js/06-advanced-functions/06-function-object/article.md
Original file line number Diff line number Diff line change
@@ -325,7 +325,11 @@ welcome(); // (فراخوانی تودرتو کار می‌کند) Guest ،سل

حالا کار می‌کند چون اسم `"func"` یک تابع محلی است. این اسم از بیرون دریافت نمی‌شود (و آنجا هم قابل رویت نیست). مشخصات زبان تضمین می‌کند که این اسم همیشه به تابع کنونی رجوع می‌کند.

<<<<<<< HEAD
کد بیرونی هنوز هم متغیر `sayHi` یا `welcome` خود را دارد. و `func` یک «اسم درونی تابع» است، جوری که تابع می‌توانند از درون خودش را فراخوانی کند.
=======
The outer code still has its variable `sayHi` or `welcome`. And `func` is an "internal function name", the way for the function to call itself reliably.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```smart header="چنین چیزی برای Function Declaration وجود ندارد"
خصوصیت «اسم درونی» که اینجا توضیح داده شد فقط برای Function Expessionها قابل استفاده است نه برای Function Declarationها. برای Function Declarationها، سینتکسی برای اضاف کردن اسم «درونی» وجود ندارد.
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@

<<<<<<< HEAD
به دلیل اینکه `ask` تابع‌های `loginOk/loginFail` را بدون شیء دریافت می‌کند ارور ایجاد می‌شود.
=======
The error occurs because `askPassword` gets functions `loginOk/loginFail` without the object.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
زمانی که این تابع آن‌ها را فرا می‌خواند، به طور طبیعی آن‌ها `this=undefined` را فرض می‌کنند.

4 changes: 4 additions & 0 deletions 1-js/06-advanced-functions/10-bind/article.md
Original file line number Diff line number Diff line change
@@ -125,7 +125,11 @@ funcUser(); // John
*/!*
```

<<<<<<< HEAD
اینجا `func.bind(user)` به عنوان «یک نوع پیوند زده شده» از `func` با `this=user` شناخته می‌شود.
=======
Here `func.bind(user)` is a "bound variant" of `func`, with fixed `this=user`.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
تمام آرگومان‌ها «بدون تغییر» به تابع اصلی `func` منتقل می‌شوند، برای مثال:

13 changes: 13 additions & 0 deletions 1-js/08-prototypes/04-prototype-methods/article.md
Original file line number Diff line number Diff line change
@@ -14,7 +14,11 @@

اگرچه یک متد خاص برای این کار هم وجود دارد:

<<<<<<< HEAD
- [Object.create(proto, [descriptors])](mdn:js/Object/create) -- یک شیء خالی با تنظیم `proto` داده شده به عنوان `[[Prototype]]` و توصیف‌کننده‌های ویژگی اختیاری ایجاد می‌کند.
=======
- [Object.create(proto[, descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
برای مثال:

@@ -111,7 +115,11 @@ alert(obj[key]); // [object Object], not "یک مقدار"!

در اینجا، اگر کاربر `__proto__` را تایپ کند، انتساب در خط 4 نادیده گرفته می‌شود!

<<<<<<< HEAD
این می‌تواند قطعا برای یک غیر توسعه‌دهنده شوکه کننده باشد اما برای ما بسیار قابل فهم است. ویژگی `__proto__` خاص است: باید یک شیء یا `null` باشد. یک رشته نمی‌تواند به یک پروتوتایپ تبدیل شود. به همین دلیل است که انتساب یک رشته به `__proto__` نادیده گرفته می‌شود.
=======
That could surely be surprising for a non-developer, but pretty understandable for us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype. That's why assigning a string to `__proto__` is ignored.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
اما ما *قصد* اجرای چنین رفتاری را نداشتیم، درست است؟ ما می‌خواهیم جفت‌های کلید/مقدار را ذخیره کنیم، و کلید با نام `"__proto__"` به درستی ذخیره نشده است. پس این یک اشکال است!

@@ -195,8 +203,13 @@ alert(Object.keys(chineseDictionary)); // hello,bye

- برای ایجاد یک شیء با پروتوتایپ تعیین شده، از این‌ها استفاده کنید:

<<<<<<< HEAD
- سینتکس لیترال: `{ __proto__: ...}`، اجازه می‌دهد که چند ویژگی تعیین کنیم
- یا [Object.create(proto, [descriptors])](mdn:js/Object/create)، اجازه می‌دهد که توصیف‌کننده‌های ویژگی را تعیین کنیم.
=======
- literal syntax: `{ __proto__: ... }`, allows to specify multiple properties
- or [Object.create(proto[, descriptors])](mdn:js/Object/create), allows to specify property descriptors.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
- متدهای مدرن برای دریافت/تنظیم پروتوتایپ این‌ها هستند:

4 changes: 4 additions & 0 deletions 1-js/10-error-handling/1-try-catch/article.md
Original file line number Diff line number Diff line change
@@ -632,7 +632,11 @@ window.onerror = function(message, url, line, col, error) {

معمولا نقش کنترل‌کننده گلوبال `window.onerror` این نیست که اجرای اسکریپت را ترمیم کند -- این موضوع در صورتی که ارور برنامه‌نویسی وجود داشته باشد احتمالا غیر ممکن است اما فرستادن پیام ارور به توسعه‌دهندگان ممکن است.

<<<<<<< HEAD
همچنین سرویس‌های وب وجود دارند که رخدادنگاری ارور را برای چنین مواردی فراهم می‌کنند مانند <https://errorception.com> یا <http://www.muscula.com>.
=======
There are also web-services that provide error-logging for such cases, like <https://muscula.com> or <https://www.sentry.io>.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
آن‌ها اینگونه کار می‌کنند:

6 changes: 5 additions & 1 deletion 1-js/11-async/02-promise-basics/article.md
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ let promise = new Promise(function(resolve, reject) {

در اینجا یک نمونه از سازنده Promise و یک تابع اجرا‌کننده ساده با «کد تولید‌کننده» داریم که زمانبر است (از طریق `setTimeout`):

```js run
```js
let promise = new Promise(function(resolve, reject) {
// ساخته می‌شود به طور خودکار اجرا می‌شود Promise این تابع زمانی که

@@ -223,7 +223,11 @@ promise.catch(alert); // .را بعد از 1 ثانیه نشان می‌دهد "
به عنوان مثال، نشانگرهای بارگیری(loading indicators) خود را متوقف می‌کنیم، اتصال‌هایی که دیگر نیاز نیستند یا ببندیم و غیره.
<<<<<<< HEAD
به عنوان یک پایان‌دهنده مهمانی به آن فکر کنید. مهم نیست که مهمانی خوب یا بد بود یا چند دوست در آن حضور داشتند، ما هنوز نیاز داریم (یا حداقل باید) که بعد از مهمانی تمیزکاری انجام دهیم.
=======
Think of it as a party finisher. Irresepective of whether a party was good or bad, how many friends were in it, we still need (or at least should) do a cleanup after it.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
کد ما ممکن است اینگونه بنظر برسد:
113 changes: 113 additions & 0 deletions 1-js/11-async/08-async-await/04-promise-all-failure/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@

The root of the problem is that `Promise.all` immediately rejects when one of its promises rejects, but it do nothing to cancel the other promises.

In our case, the second query fails, so `Promise.all` rejects, and the `try...catch` block catches this error.Meanwhile, other promises are *not affected* - they independently continue their execution. In our case, the third query throws an error of its own after a bit of time. And that error is never caught, we can see it in the console.

The problem is especially dangerous in server-side environments, such as Node.js, when an uncaught error may cause the process to crash.

How to fix it?

An ideal solution would be to cancel all unfinished queries when one of them fails. This way we avoid any potential errors.

However, the bad news is that service calls (such as `database.query`) are often implemented by a 3rd-party library which doesn't support cancellation. Then there's no way to cancel a call.

As an alternative, we can write our own wrapper function around `Promise.all` which adds a custom `then/catch` handler to each promise to track them: results are gathered and, if an error occurs, all subsequent promises are ignored.

```js
function customPromiseAll(promises) {
return new Promise((resolve, reject) => {
const results = [];
let resultsCount = 0;
let hasError = false; // we'll set it to true upon first error

promises.forEach((promise, index) => {
promise
.then(result => {
if (hasError) return; // ignore the promise if already errored
results[index] = result;
resultsCount++;
if (resultsCount === promises.length) {
resolve(results); // when all results are ready - successs
}
})
.catch(error => {
if (hasError) return; // ignore the promise if already errored
hasError = true; // wops, error!
reject(error); // fail with rejection
});
});
});
}
```

This approach has an issue of its own - it's often undesirable to `disconnect()` when queries are still in the process.

It may be important that all queries complete, especially if some of them make important updates.

So we should wait until all promises are settled before going further with the execution and eventually disconnecting.

Here's another implementation. It behaves similar to `Promise.all` - also resolves with the first error, but waits until all promises are settled.

```js
function customPromiseAllWait(promises) {
return new Promise((resolve, reject) => {
const results = new Array(promises.length);
let settledCount = 0;
let firstError = null;

promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(result => {
results[index] = result;
})
.catch(error => {
if (firstError === null) {
firstError = error;
}
})
.finally(() => {
settledCount++;
if (settledCount === promises.length) {
if (firstError !== null) {
reject(firstError);
} else {
resolve(results);
}
}
});
});
});
}
```

Now `await customPromiseAllWait(...)` will stall the execution until all queries are processed.

This is a more reliable approach, as it guarantees a predictable execution flow.

Lastly, if we'd like to process all errors, we can use either use `Promise.allSettled` or write a wrapper around it to gathers all errors in a single [AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError) object and rejects with it.

```js
// wait for all promises to settle
// return results if no errors
// throw AggregateError with all errors if any
function allOrAggregateError(promises) {
return Promise.allSettled(promises).then(results => {
const errors = [];
const values = [];

results.forEach((res, i) => {
if (res.status === 'fulfilled') {
values[i] = res.value;
} else {
errors.push(res.reason);
}
});

if (errors.length > 0) {
throw new AggregateError(errors, 'One or more promises failed');
}

return values;
});
}
```
79 changes: 79 additions & 0 deletions 1-js/11-async/08-async-await/04-promise-all-failure/task.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@

# Dangerous Promise.all

`Promise.all` is a great way to parallelize multiple operations. It's especially useful when we need to make parallel requests to multiple services.

However, there's a hidden danger. We'll see an example in this task and explore how to avoid it.

Let's say we have a connection to a remote service, such as a database.

There're two functions: `connect()` and `disconnect()`.

When connected, we can send requests using `database.query(...)` - an async function which usually returns the result but also may throw an error.

Here's a simple implementation:

```js
let database;

function connect() {
database = {
async query(isOk) {
if (!isOk) throw new Error('Query failed');
}
};
}

function disconnect() {
database = null;
}

// intended usage:
// connect()
// ...
// database.query(true) to emulate a successful call
// database.query(false) to emulate a failed call
// ...
// disconnect()
```

Now here's the problem.

We wrote the code to connect and send 3 queries in parallel (all of them take different time, e.g. 100, 200 and 300ms), then disconnect:

```js
// Helper function to call async function `fn` after `ms` milliseconds
function delay(fn, ms) {
return new Promise((resolve, reject) => {
setTimeout(() => fn().then(resolve, reject), ms);
});
}

async function run() {
connect();

try {
await Promise.all([
// these 3 parallel jobs take different time: 100, 200 and 300 ms
// we use the `delay` helper to achieve this effect
*!*
delay(() => database.query(true), 100),
delay(() => database.query(false), 200),
delay(() => database.query(false), 300)
*/!*
]);
} catch(error) {
console.log('Error handled (or was it?)');
}

disconnect();
}

run();
```

Two of these queries happen to be unsuccessful, but we're smart enough to wrap the `Promise.all` call into a `try..catch` block.

However, this doesn't help! This script actually leads to an uncaught error in console!

Why? How to avoid it?
7 changes: 7 additions & 0 deletions 1-js/13-modules/02-import-export/article.md
Original file line number Diff line number Diff line change
@@ -96,10 +96,17 @@ say.sayBye('John');
1. فهرست کردن صریح مواردی که باید وارد شوند، نام‌های کوتاه‌تری را نشان می‌دهد: `sayHi()‎` به جای `say.sayHi()‎`.
2. لیست واضح از import مرور بهتری از ساختار کد ایجاد می‌کند: چه چیزی استفاده شده و کجا. این پشتیبانی و بازبینی کد را آسان تر می‌کند.

<<<<<<< HEAD
```smart header="از import کردن زیاد نترسید."
ابزارهای مدرن بیلد مانند [webpack](https://webpack.js.org/) و مانند آن، ماژول ها را با هم باندل (ترکیب و فشرده سازی) می‌کنند و بهینه می‌کنند تا سرعت بارگذاری را افزایش دهند. آنها همچنین import های استفاده نشده را حذف می‌کنند.
به عنوان مثال، اگر `import * as library` از یک کتابخانه کد بزرگ import کنیم و سپس تنها از چند تابع آن استفاده کنیم، موارد استفاده نشده [درون بسته بهینه شده نخواهد بود](https://github.com/webpack/webpack/tree/main/examples/harmony-unused#examplejs).
=======
```smart header="Don't be afraid to import too much"
Modern build tools, such as [webpack](https://webpack.js.org/) and others, bundle modules together and optimize them to speedup loading. They also remove unused imports.
For instance, if you `import * as library` from a huge code library, and then use only few methods, then unused ones [will not be included](https://github.com/webpack/webpack/tree/main/examples/harmony-unused#examplejs) into the optimized bundle.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```

## Import "as"‎
483 changes: 483 additions & 0 deletions 1-js/99-js-misc/07-weakref-finalizationregistry/article.md

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.app {
display: flex;
flex-direction: column;
gap: 16px;
}

.start-messages {
width: fit-content;
}

.window {
width: 100%;
border: 2px solid #464154;
overflow: hidden;
}

.window__header {
position: sticky;
padding: 8px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #736e7e;
}

.window__title {
margin: 0;
font-size: 24px;
font-weight: 700;
color: white;
letter-spacing: 1px;
}

.window__button {
padding: 4px;
background: #4f495c;
outline: none;
border: 2px solid #464154;
color: white;
font-size: 16px;
cursor: pointer;
}

.window__body {
height: 250px;
padding: 16px;
overflow: scroll;
background-color: #736e7e33;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html lang="en">

<head>
<meta charset="utf-8">
<link rel="stylesheet" href="index.css">
<title>WeakRef DOM Logger</title>
</head>

<body>

<div class="app">
<button class="start-messages">Start sending messages</button>
<div class="window">
<div class="window__header">
<p class="window__title">Messages:</p>
<button class="window__button">Close</button>
</div>
<div class="window__body">
No messages.
</div>
</div>
</div>


<script type="module" src="index.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const startMessagesBtn = document.querySelector('.start-messages'); // (1)
const closeWindowBtn = document.querySelector('.window__button'); // (2)
const windowElementRef = new WeakRef(document.querySelector(".window__body")); // (3)

startMessagesBtn.addEventListener('click', () => { // (4)
startMessages(windowElementRef);
startMessagesBtn.disabled = true;
});

closeWindowBtn.addEventListener('click', () => document.querySelector(".window__body").remove()); // (5)


const startMessages = (element) => {
const timerId = setInterval(() => { // (6)
if (element.deref()) { // (7)
const payload = document.createElement("p");
payload.textContent = `Message: System status OK: ${new Date().toLocaleTimeString()}`;
element.deref().append(payload);
} else { // (8)
alert("The element has been deleted."); // (9)
clearInterval(timerId);
}
}, 1000);
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
:root {
--mineralGreen: 60, 98, 85;
--viridianGreen: 97, 135, 110;
--swampGreen: 166, 187, 141;
--fallGreen: 234, 231, 177;
--brinkPink: #FA7070;
--silverChalice: 178, 178, 178;
--white: 255, 255, 255;
--black: 0, 0, 0;

--topBarHeight: 64px;
--itemPadding: 32px;
--containerGap: 8px;
}

@keyframes zoom-in {
0% {
transform: scale(1, 1);
}

100% {
transform: scale(1.30, 1.30);
}
}

body, html {
margin: 0;
padding: 0;
}

.app {
min-height: 100vh;
background-color: rgba(var(--viridianGreen), 0.5);
}

.header {
height: var(--topBarHeight);
padding: 0 24px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(var(--mineralGreen), 1);
}

.header-text {
color: white;
}

.container {
display: flex;
gap: 24px;
padding: var(--itemPadding);
}

.item {
width: 50%;
}

.item--scrollable {
overflow-y: scroll;
height: calc(100vh - var(--topBarHeight) - (var(--itemPadding) * 2));
}

.thumbnails-container {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
align-items: center;
}

.thumbnail-item {
width: calc(25% - var(--containerGap));
cursor: pointer;
position: relative;
}

.thumbnail-item:hover {
z-index: 1;
animation: zoom-in 0.1s forwards;
}

.thumbnail-item--selected {
outline: 3px solid rgba(var(--fallGreen), 1);
outline-offset: -3px;
}

.badge {
width: 16px;
height: 16px;
display: flex;
justify-content: center;
align-items: center;
padding: 4px;
position: absolute;
right: 8px;
bottom: 8px;
border-radius: 50%;
border: 2px solid rgba(var(--fallGreen), 1);
background-color: rgba(var(--swampGreen), 1);
}

.check {
display: inline-block;
transform: rotate(45deg);
border-bottom: 2px solid white;
border-right: 2px solid white;
width: 6px;
height: 12px;
}

.img {
width: 100%;
height: 100%;
object-fit: cover;
}

.actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-content: center;
padding: 0 0 16px 0;
gap: 8px;
}

.select {
padding: 16px;
cursor: pointer;
font-weight: 700;
color: rgba(var(--black), 1);
border: 2px solid rgba(var(--swampGreen), 0.5);
background-color: rgba(var(--swampGreen), 1);
}

.select:disabled {
cursor: not-allowed;
background-color: rgba(var(--silverChalice), 1);
color: rgba(var(--black), 0.5);
border: 2px solid rgba(var(--black), 0.25);
}

.btn {
outline: none;
padding: 16px;
cursor: pointer;
font-weight: 700;
color: rgba(var(--black), 1);
border: 2px solid rgba(var(--black), 0.5);
}

.btn--primary {
background-color: rgba(var(--mineralGreen), 1);
}

.btn--primary:hover:not([disabled]) {
background-color: rgba(var(--mineralGreen), 0.85);
}

.btn--secondary {
background-color: rgba(var(--viridianGreen), 0.5);
}

.btn--secondary:hover:not([disabled]) {
background-color: rgba(var(--swampGreen), 0.25);
}

.btn--success {
background-color: rgba(var(--fallGreen), 1);
}

.btn--success:hover:not([disabled]) {
background-color: rgba(var(--fallGreen), 0.85);
}

.btn:disabled {
cursor: not-allowed;
background-color: rgba(var(--silverChalice), 1);
color: rgba(var(--black), 0.5);
border: 2px solid rgba(var(--black), 0.25);
}

.previewContainer {
margin-bottom: 16px;
display: flex;
width: 100%;
height: 40vh;
overflow: scroll;
border: 3px solid rgba(var(--black), 1);
}

.previewContainer--disabled {
background-color: rgba(var(--black), 0.1);
cursor: not-allowed;
}

.canvas {
margin: auto;
display: none;
}

.canvas--ready {
display: block;
}

.spinnerContainer {
display: flex;
gap: 8px;
flex-direction: column;
align-content: center;
align-items: center;
margin: auto;
}

.spinnerContainer--hidden {
display: none;
}

.spinnerText {
margin: 0;
color: rgba(var(--mineralGreen), 1);
}

.spinner {
display: inline-block;
width: 50px;
height: 50px;
margin: auto;
border: 3px solid rgba(var(--mineralGreen), 0.3);
border-radius: 50%;
border-top-color: rgba(var(--mineralGreen), 0.9);
animation: spin 1s ease-in-out infinite;
}

@keyframes spin {
to {
transform: rotate(360deg);
}
}

.loggerContainer {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 8px 8px 8px;
width: 100%;
min-height: 30vh;
max-height: 30vh;
overflow: scroll;
border-left: 3px solid rgba(var(--black), 0.25);
}

.logger-title {
display: flex;
align-items: center;
padding: 8px;
position: sticky;
height: 40px;
min-height: 40px;
top: 0;
left: 0;
background-color: rgba(var(--viridianGreen), 1);
font-size: 24px;
font-weight: 700;
margin: 0;
}

.logger-item {
font-size: 14px;
padding: 8px;
border: 2px solid #5a5a5a;
color: white;
}

.logger--primary {
background-color: #13315a;
}

.logger--success {
background-color: #385a4e;
}

.logger--error {
background-color: #5a1a24;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE HTML>
<html lang="en">

<head>
<meta charset="utf-8">
<link rel="stylesheet" href="index.css">
<title>Photo Library Collage</title>
</head>

<body>

<div class="app">
<header class="header">
<h1 class="header-text">
Photo Library Collage
</h1>
</header>
<div class="container">
<div class="item item--scrollable">
<!--Thumbnails-->
<div class="thumbnails-container"></div>
</div>
<div class="item">
<div>
<div class=actions>
<select class="select"></select>
<button class="btn btn--primary btn-create-collage"> Create collage </button>
<button class="btn btn--secondary btn-start-over"> Start over </button>
<button class="btn btn--success btn-download" onClick={downloadCollage}> Download </button>
</div>
<div class="previewContainer">
<div class="spinnerContainer spinnerContainer--hidden">
<div class="spinner"></div>
<p class="spinnerText"></p>
</div>
<canvas class="canvas"></canvas>
</div>
<div class="loggerContainer">
<p class="logger-title">Logger:</p>
</div>
</div>
</div>
</div>
</div>


<script type="module" src="index.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import {
createImageFile,
loadImage,
weakRefCache,
LAYOUTS,
images,
THUMBNAIL_PARAMS,
stateObj,
} from "./utils.js";

export const state = new Proxy(stateObj, {
set(target, property, value) {
const previousValue = target[property];

target[property] = value;

if (previousValue !== value) {
handleStateChange(target);
}

return true;
},
});

// Elements.
const thumbnailsContainerEl = document.querySelector(".thumbnails-container");
const selectEl = document.querySelector(".select");
const previewContainerEl = document.querySelector(".previewContainer");
const canvasEl = document.querySelector(".canvas");
const createCollageBtn = document.querySelector(".btn-create-collage");
const startOverBtn = document.querySelector(".btn-start-over");
const downloadBtn = document.querySelector(".btn-download");
const spinnerContainerEl = document.querySelector(".spinnerContainer");
const spinnerTextEl = document.querySelector(".spinnerText");
const loggerContainerEl = document.querySelector(".loggerContainer");

// Renders.
// Render thumbnails previews.
images.forEach((img) => {
const thumbnail = document.createElement("div");
thumbnail.classList.add("thumbnail-item");

thumbnail.innerHTML = `
<img src='${img.img}?${THUMBNAIL_PARAMS}' class="img">
`;

thumbnail.addEventListener("click", (e) => handleSelection(e, img));

thumbnailsContainerEl.appendChild(thumbnail);
});
// Render layouts select.
LAYOUTS.forEach((layout) => {
const option = document.createElement("option");
option.value = JSON.stringify(layout);
option.innerHTML = layout.name;
selectEl.appendChild(option);
});

const handleStateChange = (state) => {
if (state.loading) {
selectEl.disabled = true;
createCollageBtn.disabled = true;
startOverBtn.disabled = true;
downloadBtn.disabled = true;
previewContainerEl.classList.add("previewContainer--disabled");
spinnerContainerEl.classList.remove("spinnerContainer--hidden");
spinnerTextEl.innerText = "Loading...";
canvasEl.classList.remove("canvas--ready");
} else if (!state.loading) {
selectEl.disabled = false;
createCollageBtn.disabled = false;
startOverBtn.disabled = false;
downloadBtn.disabled = false;
previewContainerEl.classList.remove("previewContainer--disabled");
spinnerContainerEl.classList.add("spinnerContainer--hidden");
canvasEl.classList.add("canvas--ready");
}

if (!state.selectedImages.size) {
createCollageBtn.disabled = true;
document.querySelectorAll(".badge").forEach((item) => item.remove());
} else if (state.selectedImages.size && !state.loading) {
createCollageBtn.disabled = false;
}

if (!state.collageRendered) {
downloadBtn.disabled = true;
} else if (state.collageRendered) {
downloadBtn.disabled = false;
}
};
handleStateChange(state);

const handleSelection = (e, imgName) => {
const imgEl = e.currentTarget;

imgEl.classList.toggle("thumbnail-item--selected");

if (state.selectedImages.has(imgName)) {
state.selectedImages.delete(imgName);
state.selectedImages = new Set(state.selectedImages);
imgEl.querySelector(".badge")?.remove();
} else {
state.selectedImages = new Set(state.selectedImages.add(imgName));

const badge = document.createElement("div");
badge.classList.add("badge");
badge.innerHTML = `
<div class="check" />
`;
imgEl.prepend(badge);
}
};

// Make a wrapper function.
let getCachedImage;
(async () => {
getCachedImage = await weakRefCache(loadImage);
})();

const calculateGridRows = (blobsLength) =>
Math.ceil(blobsLength / state.currentLayout.columns);

const drawCollage = (images) => {
state.drawing = true;

let context = canvasEl.getContext("2d");

/**
* Calculate canvas dimensions based on the current layout.
* */
context.canvas.width =
state.currentLayout.itemWidth * state.currentLayout.columns;
context.canvas.height =
calculateGridRows(images.length) * state.currentLayout.itemHeight;

let currentRow = 0;
let currentCanvasDx = 0;
let currentCanvasDy = 0;

for (let i = 0; i < images.length; i++) {
/**
* Get current row of the collage.
* */
if (i % state.currentLayout.columns === 0) {
currentRow += 1;
currentCanvasDx = 0;

if (currentRow > 1) {
currentCanvasDy += state.currentLayout.itemHeight;
}
}

context.drawImage(
images[i],
0,
0,
images[i].width,
images[i].height,
currentCanvasDx,
currentCanvasDy,
state.currentLayout.itemWidth,
state.currentLayout.itemHeight,
);

currentCanvasDx += state.currentLayout.itemWidth;
}

state.drawing = false;
state.collageRendered = true;
};

const createCollage = async () => {
state.loading = true;

const images = [];

for (const image of state.selectedImages.values()) {
const blobImage = await getCachedImage(image.img);

const url = URL.createObjectURL(blobImage);
const img = await createImageFile(url);

images.push(img);
URL.revokeObjectURL(url);
}

state.loading = false;

drawCollage(images);
};

/**
* Clear all settled data to start over.
* */
const startOver = () => {
state.selectedImages = new Set();
state.collageRendered = false;
const context = canvasEl.getContext("2d");
context.clearRect(0, 0, canvasEl.width, canvasEl.height);

document
.querySelectorAll(".thumbnail-item--selected")
.forEach((item) => item.classList.remove("thumbnail-item--selected"));

loggerContainerEl.innerHTML = '<p class="logger-title">Logger:</p>';
};

const downloadCollage = () => {
const date = new Date();
const fileName = `Collage-${date.getDay()}-${date.getMonth()}-${date.getFullYear()}.png`;
const img = canvasEl.toDataURL("image/png");
const link = document.createElement("a");
link.download = fileName;
link.href = img;
link.click();
link.remove();
};

const changeLayout = ({ target }) => {
state.currentLayout = JSON.parse(target.value);
};

// Listeners.
selectEl.addEventListener("change", changeLayout);
createCollageBtn.addEventListener("click", createCollage);
startOverBtn.addEventListener("click", startOver);
downloadBtn.addEventListener("click", downloadCollage);
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
const loggerContainerEl = document.querySelector(".loggerContainer");

export const images = [
{
img: "https://images.unsplash.com/photo-1471357674240-e1a485acb3e1",
},
{
img: "https://images.unsplash.com/photo-1589118949245-7d38baf380d6",
},
{
img: "https://images.unsplash.com/photo-1527631746610-bca00a040d60",
},
{
img: "https://images.unsplash.com/photo-1500835556837-99ac94a94552",
},
{
img: "https://images.unsplash.com/photo-1503220317375-aaad61436b1b",
},
{
img: "https://images.unsplash.com/photo-1501785888041-af3ef285b470",
},
{
img: "https://images.unsplash.com/photo-1528543606781-2f6e6857f318",
},
{
img: "https://images.unsplash.com/photo-1523906834658-6e24ef2386f9",
},
{
img: "https://images.unsplash.com/photo-1539635278303-d4002c07eae3",
},
{
img: "https://images.unsplash.com/photo-1533105079780-92b9be482077",
},
{
img: "https://images.unsplash.com/photo-1516483638261-f4dbaf036963",
},
{
img: "https://images.unsplash.com/photo-1502791451862-7bd8c1df43a7",
},
{
img: "https://plus.unsplash.com/premium_photo-1663047367140-91adf819d007",
},
{
img: "https://images.unsplash.com/photo-1506197603052-3cc9c3a201bd",
},
{
img: "https://images.unsplash.com/photo-1517760444937-f6397edcbbcd",
},
{
img: "https://images.unsplash.com/photo-1518684079-3c830dcef090",
},
{
img: "https://images.unsplash.com/photo-1505832018823-50331d70d237",
},
{
img: "https://images.unsplash.com/photo-1524850011238-e3d235c7d4c9",
},
{
img: "https://plus.unsplash.com/premium_photo-1661277758451-b5053309eea1",
},
{
img: "https://images.unsplash.com/photo-1541410965313-d53b3c16ef17",
},
{
img: "https://images.unsplash.com/photo-1528702748617-c64d49f918af",
},
{
img: "https://images.unsplash.com/photo-1502003148287-a82ef80a6abc",
},
{
img: "https://plus.unsplash.com/premium_photo-1661281272544-5204ea3a481a",
},
{
img: "https://images.unsplash.com/photo-1503457574462-bd27054394c1",
},
{
img: "https://images.unsplash.com/photo-1499363536502-87642509e31b",
},
{
img: "https://images.unsplash.com/photo-1551918120-9739cb430c6d",
},
{
img: "https://plus.unsplash.com/premium_photo-1661382219642-43e54f7e81d7",
},
{
img: "https://images.unsplash.com/photo-1497262693247-aa258f96c4f5",
},
{
img: "https://images.unsplash.com/photo-1525254134158-4fd5fdd45793",
},
{
img: "https://plus.unsplash.com/premium_photo-1661274025419-4c54107d5c48",
},
{
img: "https://images.unsplash.com/photo-1553697388-94e804e2f0f6",
},
{
img: "https://images.unsplash.com/photo-1574260031597-bcd9eb192b4f",
},
{
img: "https://images.unsplash.com/photo-1536323760109-ca8c07450053",
},
{
img: "https://images.unsplash.com/photo-1527824404775-dce343118ebc",
},
{
img: "https://images.unsplash.com/photo-1612278675615-7b093b07772d",
},
{
img: "https://images.unsplash.com/photo-1522010675502-c7b3888985f6",
},
{
img: "https://images.unsplash.com/photo-1501555088652-021faa106b9b",
},
{
img: "https://plus.unsplash.com/premium_photo-1669223469435-27e091439169",
},
{
img: "https://images.unsplash.com/photo-1506012787146-f92b2d7d6d96",
},
{
img: "https://images.unsplash.com/photo-1511739001486-6bfe10ce785f",
},
{
img: "https://images.unsplash.com/photo-1553342385-111fd6bc6ab3",
},
{
img: "https://images.unsplash.com/photo-1516546453174-5e1098a4b4af",
},
{
img: "https://images.unsplash.com/photo-1527142879-95b61a0b8226",
},
{
img: "https://images.unsplash.com/photo-1520466809213-7b9a56adcd45",
},
{
img: "https://images.unsplash.com/photo-1516939884455-1445c8652f83",
},
{
img: "https://images.unsplash.com/photo-1545389336-cf090694435e",
},
{
img: "https://plus.unsplash.com/premium_photo-1669223469455-b7b734c838f4",
},
{
img: "https://images.unsplash.com/photo-1454391304352-2bf4678b1a7a",
},
{
img: "https://images.unsplash.com/photo-1433838552652-f9a46b332c40",
},
{
img: "https://images.unsplash.com/photo-1506125840744-167167210587",
},
{
img: "https://images.unsplash.com/photo-1522199873717-bc67b1a5e32b",
},
{
img: "https://images.unsplash.com/photo-1495904786722-d2b5a19a8535",
},
{
img: "https://images.unsplash.com/photo-1614094082869-cd4e4b2905c7",
},
{
img: "https://images.unsplash.com/photo-1474755032398-4b0ed3b2ae5c",
},
{
img: "https://images.unsplash.com/photo-1501554728187-ce583db33af7",
},
{
img: "https://images.unsplash.com/photo-1515859005217-8a1f08870f59",
},
{
img: "https://images.unsplash.com/photo-1531141445733-14c2eb7d4c1f",
},
{
img: "https://images.unsplash.com/photo-1500259783852-0ca9ce8a64dc",
},
{
img: "https://images.unsplash.com/photo-1510662145379-13537db782dc",
},
{
img: "https://images.unsplash.com/photo-1573790387438-4da905039392",
},
{
img: "https://images.unsplash.com/photo-1512757776214-26d36777b513",
},
{
img: "https://images.unsplash.com/photo-1518855706573-84de4022b69b",
},
{
img: "https://images.unsplash.com/photo-1500049242364-5f500807cdd7",
},
{
img: "https://images.unsplash.com/photo-1528759335187-3b683174c86a",
},
];
export const THUMBNAIL_PARAMS = "w=240&h=240&fit=crop&auto=format";

// Console styles.
export const CONSOLE_BASE_STYLES = [
"font-size: 12px",
"padding: 4px",
"border: 2px solid #5a5a5a",
"color: white",
].join(";");
export const CONSOLE_PRIMARY = [
CONSOLE_BASE_STYLES,
"background-color: #13315a",
].join(";");
export const CONSOLE_SUCCESS = [
CONSOLE_BASE_STYLES,
"background-color: #385a4e",
].join(";");
export const CONSOLE_ERROR = [
CONSOLE_BASE_STYLES,
"background-color: #5a1a24",
].join(";");

// Layouts.
export const LAYOUT_4_COLUMNS = {
name: "Layout 4 columns",
columns: 4,
itemWidth: 240,
itemHeight: 240,
};
export const LAYOUT_8_COLUMNS = {
name: "Layout 8 columns",
columns: 8,
itemWidth: 240,
itemHeight: 240,
};
export const LAYOUTS = [LAYOUT_4_COLUMNS, LAYOUT_8_COLUMNS];

export const createImageFile = async (src) =>
new Promise((resolve, reject) => {
const img = new Image();
img.src = src;
img.onload = () => resolve(img);
img.onerror = () => reject(new Error("Failed to construct image."));
});

export const loadImage = async (url) => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(String(response.status));
}

return await response.blob();
} catch (e) {
console.log(`%cFETCHED_FAILED: ${e}`, CONSOLE_ERROR);
}
};

export const weakRefCache = (fetchImg) => {
const imgCache = new Map();
const registry = new FinalizationRegistry(({ imgName, size, type }) => {
const cachedImg = imgCache.get(imgName);
if (cachedImg && !cachedImg.deref()) {
imgCache.delete(imgName);
console.log(
`%cCLEANED_IMAGE: Url: ${imgName}, Size: ${size}, Type: ${type}`,
CONSOLE_ERROR,
);

const logEl = document.createElement("div");
logEl.classList.add("logger-item", "logger--error");
logEl.innerHTML = `CLEANED_IMAGE: Url: ${imgName}, Size: ${size}, Type: ${type}`;
loggerContainerEl.appendChild(logEl);
loggerContainerEl.scrollTop = loggerContainerEl.scrollHeight;
}
});

return async (imgName) => {
const cachedImg = imgCache.get(imgName);

if (cachedImg?.deref() !== undefined) {
console.log(
`%cCACHED_IMAGE: Url: ${imgName}, Size: ${cachedImg.size}, Type: ${cachedImg.type}`,
CONSOLE_SUCCESS,
);

const logEl = document.createElement("div");
logEl.classList.add("logger-item", "logger--success");
logEl.innerHTML = `CACHED_IMAGE: Url: ${imgName}, Size: ${cachedImg.size}, Type: ${cachedImg.type}`;
loggerContainerEl.appendChild(logEl);
loggerContainerEl.scrollTop = loggerContainerEl.scrollHeight;

return cachedImg?.deref();
}

const newImg = await fetchImg(imgName);
console.log(
`%cFETCHED_IMAGE: Url: ${imgName}, Size: ${newImg.size}, Type: ${newImg.type}`,
CONSOLE_PRIMARY,
);

const logEl = document.createElement("div");
logEl.classList.add("logger-item", "logger--primary");
logEl.innerHTML = `FETCHED_IMAGE: Url: ${imgName}, Size: ${newImg.size}, Type: ${newImg.type}`;
loggerContainerEl.appendChild(logEl);
loggerContainerEl.scrollTop = loggerContainerEl.scrollHeight;

imgCache.set(imgName, new WeakRef(newImg));
registry.register(newImg, {
imgName,
size: newImg.size,
type: newImg.type,
});

return newImg;
};
};

export const stateObj = {
loading: false,
drawing: true,
collageRendered: false,
currentLayout: LAYOUTS[0],
selectedImages: new Set(),
};
2 changes: 1 addition & 1 deletion 2-ui/3-event-details/6-pointer-events/article.md
Original file line number Diff line number Diff line change
@@ -126,7 +126,7 @@ Here is the flow of user actions and the corresponding events:
So the issue is that the browser "hijacks" the interaction: `pointercancel` fires in the beginning of the "drag-and-drop" process, and no more `pointermove` events are generated.

```online
Here's the drag'n'drop demo with loggin of pointer events (only `up/down`, `move` and `cancel`) in the `textarea`:
Here's the drag'n'drop demo with logging of pointer events (only `up/down`, `move` and `cancel`) in the `textarea`:
[iframe src="ball" height=240 edit]
```
4 changes: 4 additions & 0 deletions 2-ui/4-forms-controls/1-form-elements/article.md
Original file line number Diff line number Diff line change
@@ -245,7 +245,11 @@ option = new Option(text, value, defaultSelected, selected);
- `defaultSelected` -- ایجاد می‌شود `selected` HTML-attribute باشد `true` اگر
- `selected` -- انتخاب می‌شود option باشد `true` اگر
<<<<<<< HEAD
.انتخاب شده است یا نه option مشخص می‌کند که آیا `selected` در حالی که (آن را بگیریم `option.getAttribute('selected')` که ما می‌توانیم با) می‌کند set را HTML-attribute مقدار `defaultSelected` در این است که `selected` و `defaultSelected` تفاوت بین
=======
The difference between `defaultSelected` and `selected` is that `defaultSelected` sets the HTML-attribute (that we can get using `option.getAttribute('selected')`), while `selected` sets whether the option is selected or not.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
(`false` یا به سادگی حذف شوند، مقدار پیش‌فرض هر دو) .باشند `false` یا `true` در عمل باید معمولا _هر دو_ مقدار
4 changes: 4 additions & 0 deletions 2-ui/4-forms-controls/3-events-change-input/article.md
Original file line number Diff line number Diff line change
@@ -97,7 +97,11 @@

در تمام مرورگرها به جز Firefox ممنوع است که با `dispatchEvent` یک سری "custom" clipboard event ایجاد کنیم. و حتی اگر بخاهیم چنین eventهایی را ارسال کنیم، مضخصات به وضوح بیان می‌کنند که چنین "syntetic" eventهایی نباید به clipboard دسترسی داشته باشند.

<<<<<<< HEAD
حتی اگر کسی تصمیم بگیرد که `event.clipboardData` را در یک event handler ذخیره کند و بعدا به آن دسترسی داشته باشد -- کار نخواهد کرد.
=======
It's forbidden to generate "custom" clipboard events with `dispatchEvent` in all browsers except Firefox. And even if we manage to dispatch such event, the specification clearly states that such "synthetic" events must not provide access to the clipboard.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
برای تکرار، [event.clipboardData](https://www.w3.org/TR/clipboard-apis/#clipboardevent-clipboarddata) تنها در زمینه‌ی user-initiated event handler کار می‌کند.

4 changes: 4 additions & 0 deletions 2-ui/99-ui-misc/02-selection-range/article.md
Original file line number Diff line number Diff line change
@@ -356,7 +356,11 @@ Click buttons to run methods on the selection, "resetExample" to reset it.

```smart header="انتخاب پایان/شروع در مقابل محدوده"
<<<<<<< HEAD
یک تفاوت بسیار مهم بین selection anchor/focus و `Range` start/end
=======
There's an important difference between a selection anchor/focus compared with a `Range` start/end.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
همانطور که می دانیم، اشیاء `Range` همیشه شروع خود را قبل از پایان دارند.
26 changes: 26 additions & 0 deletions 2-ui/99-ui-misc/03-event-loop/article.md
Original file line number Diff line number Diff line change
@@ -17,7 +17,11 @@
- آنها را با قدیمی ترین کار شروع کنید.
2. بخوابید تا زمانی که یک کار ظاهر شود، سپس به 1 بروید.

<<<<<<< HEAD
ین یک رسمی سازی برای چیزی است که هنگام مرور یک صفحه می بینیم. موتور جاوا اسکریپت در اکثر مواقع هیچ کاری انجام نمی دهد، فقط در صورتی اجرا می شود که یک اسکریپت/هندلر/رویداد فعال شود.
=======
That's a formalization of what we see when browsing a page. The JavaScript engine does nothing most of the time, it only runs if a script/handler/event activates.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
نمونه هایی از وظایف:

@@ -30,19 +34,37 @@

ممکن است زمانی اتفاق بیفتد که یک کار در حالی که موتور مشغول است بیاید، سپس در نوبت قرار گیرد.

<<<<<<< HEAD
وظایف یک صف تشکیل می دهند که اصطلاحاً به آن "macrotask queue" (v8 term) می گویند:

![](eventLoop.svg)

به عنوان مثال، در حالی که موتور مشغول اجرای یک `script` است، یک کاربر ممکن است ماوس خود را حرکت دهد و باعث `mousemove` شود، و `setTimeout` ممکن است به دلیل وجود داشته باشد و غیره، این وظایف یک صف تشکیل می دهند، همانطور که در تصویر بالا نشان داده شده است.

وظایف از صف بر اساس "first come – first served" پردازش می شود. وقتی مرورگر موتور با `script` تمام شد، رویداد `mousemove` و سپس `setTimeout`و غیره را کنترل می‌کند.
=======
It may happen that a task comes while the engine is busy, then it's enqueued.

The tasks form a queue, the so-called "macrotask queue" ([v8](https://v8.dev/) term):

![](eventLoop.svg)

For instance, while the engine is busy executing a `script`, a user may move their mouse causing `mousemove`, and `setTimeout` may be due and so on, these tasks form a queue, as illustrated in the picture above.

Tasks from the queue are processed on a "first come – first served" basis. When the engine browser is done with the `script`, it handles `mousemove` event, then `setTimeout` handler, and so on.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
تا اینجا، کاملا ساده، درست است؟

<<<<<<< HEAD
دو جزئیات دیگر:
1. رندر هرگز اتفاق نمی افتد در حالی که موتور یک کار را اجرا می کند. مهم نیست که کار زمان زیادی ببرد. تغییرات در DOM فقط پس از تکمیل کار انجام می شود.
2. اگر یک کار بیش از حد طولانی شود، مرورگر نمی تواند کارهای دیگری مانند پردازش رویدادهای کاربر را انجام دهد. بنابراین پس از مدتی، هشداری مانند "Page Unresponsive" را مطرح می‌کند که نشان می‌دهد کار با کل صفحه از بین می‌رود. این زمانی اتفاق می افتد که محاسبات پیچیده زیادی وجود داشته باشد یا یک خطای برنامه نویسی منجر به یک حلقه بی نهایت شود.
=======
Two more details:
1. Rendering never happens while the engine executes a task. It doesn't matter if the task takes a long time. Changes to the DOM are painted only after the task is complete.
2. If a task takes too long, the browser can't do other tasks, such as processing user events. So after some time, it raises an alert like "Page Unresponsive", suggesting killing the task with the whole page. That happens when there are a lot of complex calculations or a programming error leading to an infinite loop.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
این نظریه بود. حال بیایید ببینیم چگونه می توانیم این دانش را به کار ببریم.

@@ -54,7 +76,11 @@

در حالی که موتور مشغول برجسته‌سازی نحو است، نمی‌تواند سایر کارهای مربوط به DOM، پردازش رویدادهای کاربر و غیره را انجام دهد. حتی ممکن است باعث شود مرورگر برای مدتی "hiccup" یا حتی "hang" کند، که غیرقابل قبول است.

<<<<<<< HEAD
ما می توانیم با تقسیم کار بزرگ به قطعات از مشکلات جلوگیری کنیم. ابتدا 100 خط را برجسته کنید، سپس `setTimeout` (با تاخیر صفر) را برای 100 خط بعدی برنامه ریزی کنید، و به همین ترتیب.
=======
We can avoid problems by splitting the big task into pieces. Highlight the first 100 lines, then schedule `setTimeout` (with zero-delay) for the next 100 lines, and so on.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
برای نشان دادن این رویکرد، به‌خاطر سادگی، به‌جای برجسته‌سازی متن، تابعی را انتخاب می‌کنیم که از `1` تا `1000000000` محاسبه می‌شود.

161 changes: 82 additions & 79 deletions 6-data-storage/01-cookie/article.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion 6-data-storage/03-indexeddb/article.md
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ That power is usually excessive for traditional client-server apps. IndexedDB is

The native interface to IndexedDB, described in the specification <https://www.w3.org/TR/IndexedDB>, is event-based.

We can also use `async/await` with the help of a promise-based wrapper, like <https://github.com/jakearchibald/idb>. That's pretty convenient, but the wrapper is not perfect, it can't replace events for all cases. So we'll start with events, and then, after we gain an understanding of IndexedDb, we'll use the wrapper.
We can also use `async/await` with the help of a promise-based wrapper, like <https://github.com/jakearchibald/idb>. That's pretty convenient, but the wrapper is not perfect, it can't replace events for all cases. So we'll start with events, and then, after we gain an understanding of IndexedDB, we'll use the wrapper.

```smart header="Where's the data?"
Technically, the data is usually stored in the visitor's home directory, along with browser settings, extensions, etc.
4 changes: 4 additions & 0 deletions 7-animation/2-css-animations/article.md
Original file line number Diff line number Diff line change
@@ -463,7 +463,11 @@ boat.onclick = function() {

تفکیک `تبدیل` با `تصادف` معمولاً می‌تواند اکثر نیازهای ما را برطرف کند و انیمیشن‌های روان و خوش‌نظری ارائه کند.

<<<<<<< HEAD
برای مثال، در اینجا با کلیک بر روی عنصر «#boat» کلاسی با `transform: translateX(300)` و `opacity: 0` اضافه می‌شود، بنابراین باعث می‌شود «300px» به سمت راست حرکت کند و ناپدید شود:
=======
For example, here clicking on the `#boat` element adds the class with `transform: translateX(300px)` and `opacity: 0`, thus making it move `300px` to the right and disappear:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
```html run height=260 autorun no-beautify
<img src="https://js.cx/clipart/boat.png" id="boat">
4 changes: 4 additions & 0 deletions 7-animation/3-js-animation/article.md
Original file line number Diff line number Diff line change
@@ -452,4 +452,8 @@ function animate({timing, draw, duration}) {

انیمیشن های جاوا اسکریپت می توانند از هر تابع زمان بندی استفاده کنند. ما نمونه ها و دگرگونی های زیادی را پوشش دادیم تا همه کاره تر شوند. برخلاف CSS، ما در اینجا به منحنی های Bezier محدود نمی شویم.

<<<<<<< HEAD
در مورد `draw` هم همینطور است: ما می توانیم هر چیزی را متحرک کنیم، نه فقط ویژگی های CSS.
=======
The same is true about `draw`: we can animate anything, not just CSS properties.
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
4 changes: 2 additions & 2 deletions 9-regular-expressions/11-regexp-groups/article.md
Original file line number Diff line number Diff line change
@@ -209,9 +209,9 @@ alert(results[0]); // <h1>,h1 (1st tag)
alert(results[1]); // <h2>,h2 (2nd tag)
```

As we can see, the first difference is very important, as demonstrated in the line `(*)`. We can't get the match as `results[0]`, because that object isn't pseudoarray. We can turn it into a real `Array` using `Array.from`. There are more details about pseudoarrays and iterables in the article <info:iterable>.
As we can see, the first difference is very important, as demonstrated in the line `(*)`. We can't get the match as `results[0]`, because that object is a pseudoarray. We can turn it into a real `Array` using `Array.from`. There are more details about pseudoarrays and iterables in the article <info:iterable>.

There's no need in `Array.from` if we're looping over results:
There's no need for `Array.from` if we're looping over results:

```js run
let results = '<h1> <h2>'.matchAll(/<(.*?)>/gi);
6 changes: 6 additions & 0 deletions BACKERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

# Sponsors and Supporters

## Supporters

- Ilya Zelenko
94 changes: 37 additions & 57 deletions LICENSE.md

Large diffs are not rendered by default.