diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..490051876 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: iliakan diff --git a/1-js/01-getting-started/4-devtools/article.md b/1-js/01-getting-started/4-devtools/article.md index d4bb0147e..961d53cd2 100644 --- a/1-js/01-getting-started/4-devtools/article.md +++ b/1-js/01-getting-started/4-devtools/article.md @@ -20,7 +20,7 @@ پنل توسعهدهندگان به صورت پیشفرض روی تب Console باز میشود و شما چیزی شبیه به این را باید ببینید : - + ظاهر دقیق این پنل بر اساس نسخه 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  diff --git a/1-js/01-getting-started/4-devtools/chrome.png b/1-js/01-getting-started/4-devtools/chrome.png deleted file mode 100644 index 4cb3ea2f4..000000000 Binary files a/1-js/01-getting-started/4-devtools/chrome.png and /dev/null differ diff --git a/1-js/01-getting-started/4-devtools/chrome.webp b/1-js/01-getting-started/4-devtools/chrome.webp new file mode 100644 index 000000000..bdf067079 Binary files /dev/null and b/1-js/01-getting-started/4-devtools/chrome.webp differ diff --git a/1-js/01-getting-started/4-devtools/chrome@2.webp b/1-js/01-getting-started/4-devtools/chrome@2.webp new file mode 100644 index 000000000..2aeca5898 Binary files /dev/null and b/1-js/01-getting-started/4-devtools/chrome@2.webp differ diff --git a/1-js/01-getting-started/4-devtools/chrome@2x.png b/1-js/01-getting-started/4-devtools/chrome@2x.png deleted file mode 100644 index b87404a8f..000000000 Binary files a/1-js/01-getting-started/4-devtools/chrome@2x.png and /dev/null differ diff --git a/1-js/01-getting-started/4-devtools/safari.png b/1-js/01-getting-started/4-devtools/safari.png index 64c7a3f6c..4538827eb 100644 Binary files a/1-js/01-getting-started/4-devtools/safari.png and b/1-js/01-getting-started/4-devtools/safari.png differ diff --git a/1-js/01-getting-started/4-devtools/safari@2x.png b/1-js/01-getting-started/4-devtools/safari@2x.png index 27def4d09..1561b2bd9 100644 Binary files a/1-js/01-getting-started/4-devtools/safari@2x.png and b/1-js/01-getting-started/4-devtools/safari@2x.png differ diff --git a/1-js/02-first-steps/04-variables/article.md b/1-js/02-first-steps/04-variables/article.md index 143f2b247..0b5cafad3 100644 --- a/1-js/02-first-steps/04-variables/article.md +++ b/1-js/02-first-steps/04-variables/article.md @@ -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  @@ -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,7 +318,11 @@ 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 برای نمونه: @@ -299,7 +330,11 @@ alert(color); // #FF7F00 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="ایجاد یا استفاده مجدد?" و آخرین نکته: برخی برنامهنویسان تنبل بجای تعریف متغیرهای جدید، از متغیرهای موجود دوباره استفاده میکنند. diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index 46ea20152..8e88b8c3e 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -94,6 +94,7 @@ const bigInt = 1234567890123456789012345678901234567890n; به دلیل اینکه اعداد `BigInt` به ندرت نیاز میشوند، ما آنها را اینجا پوشش نمیدهیم، اما یک فصل <info:bigint> جدا به آنها اختصاص دادیم. زمانی که به چنین اعداد بزرگی نیاز داشتید آن را بخوانید. +<<<<<<< HEAD ```smart header="مشکلات سازگاری" در حال حاضر، `BigInt` در Firefox/Chrome/Edge/Safari پشتیبانی میشوند، اما در IE خیر. @@ -101,6 +102,8 @@ const bigInt = 1234567890123456789012345678901234567890n; شما میتوانید [جدول سازگاری BigInt در *MDN*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) را برای اینکه بدانید کدام نسخه از مرورگرها پشتیبانی میشوند، بررسی کنید. +======= +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ## String در جاوااسکریپت یک رشته (string) باید توسط کوتِیشِنها احاطه شود. diff --git a/1-js/02-first-steps/16-function-expressions/article.md b/1-js/02-first-steps/16-function-expressions/article.md index 994f59549..c4e4a3387 100644 --- a/1-js/02-first-steps/16-function-expressions/article.md +++ b/1-js/02-first-steps/16-function-expressions/article.md @@ -82,7 +82,7 @@ let sayHi = function() { // (1) ایجاد alert( "سلام" ); }; -let func = sayHi; +let func = sayHi; //(2) // ... ``` diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index d4f4d5de6..eeceffb3e 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -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 پانوشت گوگل کروم معمولا نسبت به فیچرهای زبان بهروزترین است. اگر دموی یک آموزش کار نکرد، آن را امتحان کنید. البته بیشتر دموهای آموزش با هر مرورگر مدرنی کار میکنند. diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md index a19760285..c3f0dccdc 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md @@ -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 diff --git a/1-js/04-object-basics/09-object-toprimitive/article.md b/1-js/04-object-basics/09-object-toprimitive/article.md index 086f1c0c7..bec4f12bf 100644 --- a/1-js/04-object-basics/09-object-toprimitive/article.md +++ b/1-js/04-object-basics/09-object-toprimitive/article.md @@ -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 ``` ## خلاصه diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index 74f44f097..5d49d6321 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -4,7 +4,11 @@ 1. اعداد معمولی در جاوااسکریپت با فرمت 64 بیتی [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision) ذخیره میشوند، همچنین با "اعداد اعشاری با دقت یک صدم" هم شناخته میشوند. ما اکثر اوقات از این اعداد استفاده میکنیم، و درباره آنها در این فصل صحبت خواهیم کرد. +<<<<<<< HEAD 2. اعداد BigInt، برای نمایش اعدادی با طول دلخواه استفاده میشوند. آنها بعضی اوقات مورد نیاز هستند، چون اعداد معمولی نمیتوانند از <code>2<sup>53</sup></code> بیشتر یا از <code>-2<sup>53</sup></code> کمتر باشند. چون bigintها در چند حوزه خاص استفاده میشوند، ما به آنها یک فصل خاص <info:bigint> اختصاص میدهیم. +======= +2. BigInt numbers represent integers of arbitrary length. They are sometimes needed because a regular integer number can't safely exceed <code>(2<sup>53</sup>-1)</code> or be less than <code>-(2<sup>53</sup>-1)</code>, as we mentioned earlier in the chapter <info:types>. As bigints are used in a few special areas, we devote them to a special chapter <info:bigint>. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b پس اینجا درباره اعداد معمولی صحبت میکنیم. بیایید دانشمان درباره آنها را گسترش دهیم. @@ -41,7 +45,11 @@ alert( 7.3e9 ); // 7.3 billions (same as 7300000000 or 7_300_000_000) 1.23e6 === 1.23 * 1000000; // e6 یعنی *1000000 ``` +<<<<<<< HEAD حالا بگذارید مقداری خیلی کوچک بنویسیم. مثلا؛ یک میکروثانیه، (یک میلیونیوم ثانیه): +======= +Now let's write something very small. Say, 1 microsecond (one-millionth of a second): +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ```js let mсs = 0.000001; @@ -103,13 +111,23 @@ alert( num.toString(16) ); // ff alert( num.toString(2) ); // 11111111 ``` +<<<<<<< HEAD پایه میتواند از بین `۲` تا `۳۶` تغییر کند. در حالت عادی، `۱۰` است. +======= +The `base` can vary from `2` to `36`. By default, it's `10`. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b حالات معمول استفاده بدین شکلند: +<<<<<<< HEAD - **base=16** برای رنگهای هگزایی، کدگذاری حروف و غیره، ارقام میتوانند به صورت `0..9` یا `A..F` باشند. - **base=2** برای دیباگ کردن عملوندهای بیتی هستند، ارقام میتوانند `0` یا `1` باشند. - **base=36** بزرگترین است، ارقام میتوانند `0..9` یا `A..Z` باشند. کل الفبای لاتین برای نمایش یک عدد مورد استفاده قرار میگیرد. یک حالت خنده دار، اما مفید برای `۳۶` وقتی است که ما نیاز داریم تا یک نشانگر بزرگ عددی را به چیز کوچکتری تبدیل کنیم، به عنوان مثال برای . ساختن لینک های کوتاه شده (short url). میتوان به سادگی اعداد را در پایهی `۳۶` نمایش داد: +======= +- **base=16** is used for hex colors, character encodings etc, digits can be `0..9` or `A..F`. +- **base=2** is mostly for debugging bitwise operations, digits can be `0` or `1`. +- **base=36** is the maximum, digits can be `0..9` or `A..Z`. The whole Latin alphabet is used to represent a number. A funny, but useful case for `36` is when we need to turn a long numeric identifier into something shorter, for example, to make a short url. Can simply represent it in the numeral system with base `36`: +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ```js run alert( 123456..toString(36) ); // 2n9c @@ -118,7 +136,13 @@ alert( num.toString(2) ); // 11111111 ```warn header="دو نقطه برای صدا زدن یک تابع" توجه داشته باشید که دو نقطه در `123456..toString(36)`، غلط املایی نیست. اگر میخواهیم یک تابعی را مستقیما روی عدد صدا بزنیم مثل `toString` در مثال بالا، آنگاه ما نیاز داریم تا بعد از آن دوتا نقطه بگذاریم. +<<<<<<< HEAD اگر ما یک نقطه بگذاریم: `123456.toString(36)`، آنگاه خطایی به وجود میآید، چراکه قواعد نوشتاری جاوااسکریپت بعد از یک نقطه، آن را قسمت اعشاری آن در نظر میگیرد. و اگر یک نقطه بیشتر بگذاریم، جاوااسکریپت فرض میکند قسمت اعشاری خالیست و سپس تابع فراخوانده میشود. +======= +If we placed a single dot: `123456.toString(36)`, then there would be an error, because JavaScript syntax implies the decimal part after the first dot. And if we place one more dot, then JavaScript knows that the decimal part is empty and now uses the method. + +Also could write `(123456).toString(36)`. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b همچنین میتوانیم بنویسیم `(123456).toString(36)`: ``` @@ -136,7 +160,11 @@ alert( num.toString(2) ); // 11111111 : رند بالا: `۳.۱` میشود `۴`, و `-۱.۱` میشود `-۱`. `Math.round` +<<<<<<< HEAD : رند کردن به نزدیک ترین عدد صحیح: `۳.۱` میشود `۳`, `۳.۶` میشود `۴` و `-۱.۱` میشود `-۱`. +======= +: Rounds to the nearest integer: `3.1` becomes `3`, `3.6` becomes `4`. In the middle cases `3.5` rounds up to `4`, and `-3.5` rounds up to `-3`. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b `Math.trunc` (توسط اینترنت اکسپلورر ساپورت نمیشود) : حذف کردن قسمت اعشاری بدون رند کردن: `۳.۱` میشود `۳`, `-۱.۱` میشود `-۱`. @@ -145,10 +173,19 @@ alert( num.toString(2) ); // 11111111 | | `Math.floor` | `Math.ceil` | `Math.round` | `Math.trunc` | |---|---------|--------|---------|---------| +<<<<<<< HEAD |`۳.۱`| `۳` | `۴` | `۳` | `۳` | |`۳.۶`| `۳` | `۴` | `۴` | `۳` | |`-۱.۱`| `-۲` | `-۱` | `-۱` | `-۱` | |`-۱.۶`| `-۲` | `-۱` | `-۲` | `-۱` | +======= +|`3.1`| `3` | `4` | `3` | `3` | +|`3.5`| `3` | `4` | `4` | `3` | +|`3.6`| `3` | `4` | `4` | `3` | +|`-1.1`| `-2` | `-1` | `-1` | `-1` | +|`-1.5`| `-2` | `-1` | `-1` | `-1` | +|`-1.6`| `-2` | `-1` | `-2` | `-1` | +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b این توابع تمام حالات کار با قسمت اعشاری یک عدد را پوشش میدهند. اما چطور میتوانیم عدد را تا رقم `n-ام` بعد از اعشار رند کنیم؟ @@ -187,7 +224,11 @@ alert( num.toString(2) ); // 11111111 alert( num.toFixed(5) ); // "12.34000", صفر اضافه شده تا دقیقا ۵ رقم شود ``` +<<<<<<< HEAD ما میتوانیم آن را با کمک عملگر جمع یگانه یا با فراخوانی `Number()` تبدیل کنیم؛ برای مثال بنویسیم `+num.toFixed(5)`. +======= + We can convert it to a number using the unary plus or a `Number()` call, e.g. write `+num.toFixed(5)`. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ## محاسبات تقریبی @@ -221,7 +262,17 @@ alert( 0.1 + 0.2 ); // 0.30000000000000004 یک عدد در حافظه به شکل دودویی آن ذخیره میشود، مجموعهای از صفرها و یکها. اما کسرهایی مثل `0.1`، `0.2` که در سیستم اعداد اعشاری ساده به نظر میرسند در اصل کسرهای بیپایانی در شکل دودویی خود هستند. +<<<<<<< HEAD مقدار `0.1` چیست؟ مقدار یک که بر ده تقسیم شده، `1/10` یعنی یک دهم. در سیستم اعداد اعشاری چنین اعدادی به سادگی قابل نمایش هستند. آن را با یک سوم `1/3` مقایسه کنید. به کسری بیپایان تبدیل میشود `0.33333(3)`. +======= +```js run +alert(0.1.toString(2)); // 0.0001100110011001100110011001100110011001100110011001101 +alert(0.2.toString(2)); // 0.001100110011001100110011001100110011001100110011001101 +alert((0.1 + 0.2).toString(2)); // 0.0100110011001100110011001100110011001100110011001101 +``` + +What is `0.1`? It is one divided by ten `1/10`, one-tenth. In the decimal numeral system, such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b بنابراین، تقسیم های از توان ده، قطعا در سیستم اعشاری کار میکند اما تقسیمهای بر ۳ اینطور نیست. به علت مشابه، در سیستم اعداد دودویی، تقسیم توانهای ۲ هم قطعا کار میکند اما ۱/۱۰، کسر دودویی بیپایانی میشود. @@ -241,7 +292,11 @@ alert( 0.1.toFixed(20) ); // 0.10000000000000000555 ```smart header="نه تنها جاوااسکریپت" این مشکل در دیگر زبانهای برنامهنویسی بسیاری وجود دارد. +<<<<<<< HEAD PHP، Java، C، Perl، Ruby دقیقا نتیجهی مشابه را میدهند چراکه بر پایهی فرمت عددی یکسانی بنا شدهاند. +======= +PHP, Java, C, Perl, and Ruby give exactly the same result, because they are based on the same numeric format. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ``` آیا ما میتوانیم راهی برای حل این مسأله پیدا کنیم؟ طبعا، قابل اطمینان ترین راه حل این است که نتیجه را با کمک متد [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) رند کنیم: @@ -265,7 +320,11 @@ alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001 ``` +<<<<<<< HEAD پس روش ضرب/تقسیم ارور را کاهش میدهد، اما آن را به طور کامل ازبین نمیبرد. +======= +So, the multiply/divide approach reduces the error, but doesn't remove it totally. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b گاهی اوقات میتوانیم به طور کلی از کسرها فرار کنیم. برای مثال اگر ما با یک فروشگاه سر و کار داریم، میتوانیم قیمتها را به جای دلار به صورت سنت (cent) ذخیره کنیم. اما اگر یک تخفیف 30 درصدی را اعمال کنیم چه؟ در عمل، فرار کردن از کسرها به ندرت ممکن است. زمانی که نیاز شد فقط آنها را رند کنید تا "دمشان" را ببرید. @@ -287,7 +346,11 @@ alert( 9999999999999999 ); // shows 10000000000000000 این به این علت است که علامت با یک بیت مشخص میشود بنابراین هر عدد میتواند مثبت یا منفی باشد، حتی صفر. +<<<<<<< HEAD در اکثر حالات، تفاوت غیرقابل توجه است، چراکه عملگرها برای داشتن رفتار یکسان وفق داده شدهند. +======= +In most cases, the distinction is unnoticeable, because operators are suited to treat them as the same. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ``` ## آزمونها: isFinite و isNaN @@ -336,7 +399,11 @@ alert( isFinite(num) ); ````smart header="`Number.isNaN` و `Number.isFinite`" متدهای [Number.isNaN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) و [Number.isFinite](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) نسخههای «سختگیرتر» از تابعهای `isNaN` و `isFinite` هستند. آنها به طور خودکار آرگومان خود را به عدد تبدیل نمیکنند بلکه در عوض بررسی میکنند که آیا آرگومان به نوع `number` تعلق دارد یا خیر. +<<<<<<< HEAD - `Number.isNaN(value)` اگر آرگومان به نوع `number` تعلق داشته باشد و برابر با `NaN` باشد مقدار `true` را برمیگرداند. در غیر این صورت `false` برگردانده میشود. +======= +- `Number.isNaN(value)` returns `true` if the argument belongs to the `number` type and it is `NaN`. In any other case, it returns `false`. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ```js run alert( Number.isNaN(NaN) ); // true @@ -347,7 +414,11 @@ alert( isFinite(num) ); alert( isNaN("str") ); // true را دریافت میکند پس NaN را به یک عدد تبدیل میکند و از این تبدیل "str" رشته isNaN چون ``` +<<<<<<< HEAD - `Number.isFinite(value)` اگر آرگومان به نوع `number` تعلق داشته باشد و `NaN/Infinity/-Infinity` نباشد مقدار `true` برگردانده میشود. در غیر این صورت `false` را برمیگرداند. +======= +- `Number.isFinite(value)` returns `true` if the argument belongs to the `number` type and it is not `NaN/Infinity/-Infinity`. In any other case, it returns `false`. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ```js run alert( Number.isFinite(123) ); // true @@ -365,8 +436,13 @@ alert( isFinite(num) ); ```smart header="مقایسه کنید با `Object.is`" یک متد درونساخت خاص به نام [Object.is](mdn:js/Object/is) وجود دارد که مقادیر را مثل `===` مقایسه میکند، اما برای دو حالت مرزی قابل اعتمادتر است: +<<<<<<< HEAD ۱. با `NaN` کار میکند: `Object.is(NaN, NaN) === true`، که چیز خوبیست. ۲. مقادیر `0` و `-0` متفاوت هستند: `Object.is(0, -0) === false`، به ندرت اهمیت دارد، اما این مقادیر در اصل متفاوتند. +======= +1. It works with `NaN`: `Object.is(NaN, NaN) === true`, that's a good thing. +2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, technically that's correct because internally the number has a sign bit that may be different even if all other bits are zeroes. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b در تمام حالات دیگر، `Object.is(a, b)` با `a === b` برابراست. @@ -384,7 +460,11 @@ alert( +"100px" ); // NaN تنها استثنا، کاراکتر خالی در شروع یا انتهای رشته حرفی هستند که آنها در تابع در نظر گرفته نمیشوند. +<<<<<<< HEAD اما در دنیای واقعی، ما مقادیر در واحدهای مختلفی داریم، مثل `"100px"` یا `"12pt"` در CSS. همینطور در بسیاری از کشورها، نماد پولی آنها بعد از مقدار عددی ظاهر میشود. مثل `"19€"`، که میخواهیم آن مقدار را از قسمت حرفی جدا کنیم. +======= +But in real life, we often have values in units, like `"100px"` or `"12pt"` in CSS. Also in many countries, the currency symbol goes after the amount, so we have `"19€"` and would like to extract a numeric value out of that. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b به همین علت است که `parseInt` و `parseFloat` استفاده میشوند. @@ -478,4 +558,8 @@ alert( parseInt('2n9c', 36) ); // 123456 توابع ریاضی بیشتر: +<<<<<<< HEAD - [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) شئ را وقتی به آنها نیاز دارید ببینید. کتابخانهی بسیار کوچکیست اما توابع پایهای را پوشش میدهد. +======= +- See the [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object when you need them. The library is very small but can cover basic needs. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b diff --git a/1-js/05-data-types/03-string/3-truncate/task.md b/1-js/05-data-types/03-string/3-truncate/task.md index 9df35c91f..fd60545a2 100644 --- a/1-js/05-data-types/03-string/3-truncate/task.md +++ b/1-js/05-data-types/03-string/3-truncate/task.md @@ -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!" ``` diff --git a/1-js/05-data-types/04-array/article.md b/1-js/05-data-types/04-array/article.md index 04ec5d0b9..2516aa94b 100644 --- a/1-js/05-data-types/04-array/article.md +++ b/1-js/05-data-types/04-array/article.md @@ -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 diff --git a/1-js/05-data-types/05-array-methods/article.md b/1-js/05-data-types/05-array-methods/article.md index 380eef8e7..c80b0bc52 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -1,6 +1,10 @@ # متدهای آرایه +<<<<<<< HEAD آرایهها متدهای زیادی را فراهم میکنند. برای سادهسازی، در این فصل متدها به چند گروه تقسیم شدهاند. +======= +Arrays provide a lot of methods. To make things easier, in this chapter, they are split into groups. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ## اضافه/حذف کردن عضوها @@ -32,11 +36,19 @@ alert( arr.length ); // 3 المان حذف شد، اما آرایه هنوز هم 3 عضو دارد که میتوانیم آن را با `arr.length == 3` ببینیم. +<<<<<<< HEAD این چیز طبیعی است چون `delete obj.key` یک مقدار را با استفاده از `key` حذف میکند. به طور کلی کارش همین است. برای شیءها مناسب است. اما برای آرایهها ما معمولا میخواهیم که بقیه المانها پخش شوند و فضای آزاد شده را اشغال کنند. توقع داریم که الان آرایهای کوتاهتر داشته باشیم. +======= +That's natural, because `delete obj.key` removes a value by the `key`. It's all it does. Fine for objects. But for arrays we usually want the rest of the elements to shift and occupy the freed place. We expect to have a shorter array now. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b بنابراین متدهای خاص باید استفاده شوند. +<<<<<<< HEAD متد [arr.splice](mdn:js/Array/splice) یک شمشیر ارتشی سوئیسی برای آرایهها است. میتواند هر کاری کند: اضافه کند، حذف کند و المانها را جایگزین کند. +======= +The [arr.splice](mdn:js/Array/splice) method is a Swiss army knife for arrays. It can do everything: insert, remove and replace elements. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b سینتکس آن اینگونه است: @@ -62,7 +74,11 @@ alert( arr ); // ["I", "JavaScript"] راحت است، نه؟ از ایندکس `1` به تعداد `1` المان حذف کرد. +<<<<<<< HEAD در مثال بعد ما 3 المان را حذف و آنها را با دو المان جایگزین میکنیم: +======= +In the next example, we remove 3 elements and replace them with the other two: +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ```js run let arr = [*!*"I", "study", "JavaScript",*/!* "right", "now"]; @@ -84,7 +100,11 @@ let removed = arr.splice(0, 2); alert( removed ); // "I", "study" <-- array of removed elements ``` +<<<<<<< HEAD متد `splice` همچنین قادر به اضافه کردن المان بدون هیچ حذفیاتی است. برای این کار باید `deleteCount` را `0` بگذاریم: +======= +The `splice` method is also able to insert the elements without any removals. For that, we need to set `deleteCount` to `0`: +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ```js run let arr = ["I", "study", "JavaScript"]; @@ -114,7 +134,11 @@ alert( arr ); // 1,2,3,4,5 ### متد slice +<<<<<<< HEAD متد [arr.slice](mdn:js/Array/slice) از متد `arr.splice` که از لحاظ ظاهری شبیه به آن است بسیار سادهتر است. +======= +The method [arr.slice](mdn:js/Array/slice) is much simpler than the similar-looking `arr.splice`. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b سینتکس اینگونه است: @@ -124,7 +148,11 @@ arr.slice([start], [end]) این متد یک آرایه جدید که تمام المانها را از ایندکس `start` تا `end` (شامل خود `end` نمیشود) کپی میکند، برمیگرداند. `start` و `end` هر دو میتوانند منفی باشند، که در این صورت موقعیت از انتهای آرایه حساب میشود. +<<<<<<< HEAD این متد شبیه متد رشته `str.slice` است، اما به جای زیر رشته، زیر آرایه ایجاد میکند. +======= +It's similar to a string method `str.slice`, but instead of substrings, it makes subarrays. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b برای مثال: @@ -206,7 +234,11 @@ alert( arr.concat(arrayLike) ); // 1,2,something,else سینتکس اینگونه است: ```js arr.forEach(function(item, index, array) { +<<<<<<< HEAD // ... با المان کاری انجام دهید +======= + // ... do something with an item +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b }); ``` @@ -239,7 +271,11 @@ arr.forEach(function(item, index, array) { - `arr.indexOf(item, from)` -- با شروع از ایندکس `from` به دنبال `item` میگردد و ایندکسی که المان در آن پیدا شد را برمیگرداند، در غیر این صورت `1-`. - `arr.includes(item, from)` -- با شروع از ایندکس `from` به دنبال `item` میگردد، اگر پیدا کند `true` را برمیگرداند. +<<<<<<< HEAD معمولا این متدها تنها با یک آرگومان استفاده میشوند: المانی (`item`) که جستجو برای آن انجام میشود. به طور پیشفرض، جستجو از ابتدا انجام میشود. +======= +Usually, these methods are used with only one argument: the `item` to search. By default, the search is from the beginning. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b برای مثال: @@ -255,7 +291,11 @@ alert( arr.includes(1) ); // true توجه داشته باشید که متدها از مقایسه `===` استفاده میکنند. پس اگر ما به دنبال `false` باشیم، متد دقیقا `false` را پیدا میکند و نه صفر را. +<<<<<<< HEAD اگر ما بخواهیم بررسی کنیم که `item` درون آرایه وجود دارد یا نه و به دنبال ایندکس دقیق نیستیم، پس `arr.includes` ترجیح داده میشود. +======= +If we want to check if `item` exists in the array and don't need the index, then `arr.includes` is preferred. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b متد [arr.lastIndexOf](mdn:js/Array/lastIndexOf) مانند `indexOf` است اما از راست به چپ جستجو میکند. @@ -273,11 +313,20 @@ const arr = [NaN]; alert( arr.indexOf(NaN) ); // -1 (اشتباه است، باید 0 باشد) alert( arr.includes(NaN) );// true (درست است) ``` +<<<<<<< HEAD به این دلیل که `includes` بسیار بعدتر به جاوااسکریپت اضافه شد و از درون از الگوریتمهای مقایسه بروزتری استفاده میکند. +======= +That's because `includes` was added to JavaScript much later and uses the more up-to-date comparison algorithm internally. +```` +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ### متدهای find و findIndex/findLastIndex +<<<<<<< HEAD تصور کنید که یک آرایهای از شیءها داریم. چگونه باید یک شیء با شرطی مشخص را پیدا کنیم؟ +======= +Imagine we have an array of objects. How do we find an object with a specific condition? +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b اینجاست که متد [arr.find(fn)](mdn:js/Array/find) بدرد میخورد. @@ -295,7 +344,11 @@ let result = arr.find(function(item, index, array) { - `index` ایندکس آن است. - `array` خود آرایه است. +<<<<<<< HEAD اگر `true` برگرداند، جستجو متوقف میشود، `item` برگردانده میشود. اگر چیزی پیدا نشود، `undefined` برگردانده میشود. +======= +If it returns `true`, the search is stopped, the `item` is returned. If nothing is found, `undefined` is returned. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b برای مثال، ما یک آرایهای از کاربران داریم، که هر کدام دارای `id` و `name` هستند. بیایید کاربری که `id == 1` داشته باشد را پیدا کنیم: @@ -311,11 +364,19 @@ let user = users.find(item => item.id == 1); alert(user.name); // John ``` +<<<<<<< HEAD در واقعیت، آرایههایی از شیءها چیز متداولی است، پس متد `find` بسیار مفید است. +======= +In real life, arrays of objects are a common thing, so the `find` method is very useful. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b توجه داشته باشید که در مثال بالا ما تابع `item => item.id == 1` را همراه با یک آرگومان برای `find` در نظر گرفتیم. این چیز معمولی است، بقیه آرگومانهای این تابع به ندرت استفاده میشوند. +<<<<<<< HEAD متد [arr.findIndex](mdn:js/Array/findIndex) سینتکس یکسانی دارد اما به جای خود المان ایندکسی که المان در آن پیدا شد را برمیگرداند. اگر چیزی پیدا نشد مقدار `1-` برگردانده میشود. +======= +The [arr.findIndex](mdn:js/Array/findIndex) method has the same syntax but returns the index where the element was found instead of the element itself. The value of `-1` is returned if nothing is found. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b متد [arr.findLastIndex](mdn:js/Array/findLastIndex) مانند `findIndex` است اما مانند `lastIndexOf` از راست به چپ جستجو میکند. @@ -447,11 +508,19 @@ alert(arr); // *!*1, 2, 15*/!* حالا همانطور که انتظار میرفت کار میکند. +<<<<<<< HEAD بیایید کمی عقب بمانیم و ببینیم چه چیزی در حال اتفاق افتادن است. `arr` میتواند آرایهای از هر چیزی باشد نه؟ ممکن است شامل اعداد یا رشتهها یا شیءها یا هرچیز دیگری باشد. ما دستهای از *چیزها* داریم. برای مرتب کردن آن، ما به یک *تابع مرتبکننده* که میداند چگونه المانهای دسته را مقایسه کند، نیاز داریم. ترتیب رشتهای پیشفرض است. +======= +Let's step aside and think about what's happening. The `arr` can be an array of anything, right? It may contain numbers or strings or objects or whatever. We have a set of *some items*. To sort it, we need an *ordering function* that knows how to compare its elements. The default is a string order. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b متد `arr.sort(fn)` یک الگوریتم مرتبسازی کلی را پیادهسازی میکند. ما نیازی نداریم که بدانیم درون آن چه اتفاقی میافتد (اکثر اوقات از یک [مرتبسازی سریع](https://fa.wikipedia.org/wiki/مرتب%E2%80%8Cسازی_سریع) یا [Timsort](https://fa.wikipedia.org/wiki/مرتب%E2%80%8Cسازی_تیم) بهینهشده استفاده میشود). این متد آرایه را طی میکند، المانهای آن را با استفاده از تابع فراهم شده مقایسه میکند و آنها را مرتب میکند، تمام آن چیزی که ما نیاز داریم این است که یک `fn` فراهم کنیم که مقایسه را انجام دهد. +<<<<<<< HEAD راستی، اگر ما بخواهیم بدانیم که کدام المانها مقایسه میشوند -- چیزی ما را از alert کردن آنها متوقف نمیکند: +======= +By the way, if we ever want to know which elements are compared -- nothing prevents us from alerting them: +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ```js run [1, -2, 15, 2, 0, 8].sort(function(a, b) { @@ -523,7 +592,11 @@ alert( arr ); // 5,4,3,2,1 متد [str.split(delim)](mdn:js/String/split) دقیقا همین کار را انجام میدهد. این متد رشته را با استفاده از جداکنندهی داده شده `delim` به یک آرایه تقسیم میکند. +<<<<<<< HEAD در مثال بالا، ما توسط یک کاما که بعد آن space میآید رشته را جدا میکنیم: +======= +In the example below, we split by a comma followed by a space: +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ```js run let names = 'Bilbo, Gandalf, Nazgul'; @@ -590,9 +663,15 @@ let value = arr.reduce(function(accumulator, item, index, array) { - `index` -- موقعیت آن است. - `array` -- آرایه است. +<<<<<<< HEAD همانطور که تابع اعمال میشود، نتیجه فراخوانی قبلی به عنوان آرگومان اول به فراخوانی بعدی منتقل میشود. بنابراین، اولین آرگومان اساسا همان حافظهای است که نتیجه ترکیب شده تمام فراخوانیهای قبلی را ذخیره کرده است. و در پایان تبدیل به نتیجه `reduce` میشود. +======= +As the function is applied, the result of the previous function call is passed to the next one as the first argument. + +So, the first argument is essentially the accumulator that stores the combined result of all previous executions. And at the end, it becomes the result of `reduce`. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b بنظر پیچیده میآید؟ @@ -661,7 +740,11 @@ arr.reduce((sum, current) => sum + current); بنابراین توصیه میشود همیشه مقدار اولیه را تعیین کنید. +<<<<<<< HEAD متد [arr.reduceRight](mdn:js/Array/reduceRight) کار یکسان را انجام میهد، اما از راست به چپ. +======= +The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same but goes from right to left. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b ##متد Array.isArray @@ -687,7 +770,11 @@ alert(Array.isArray([])); // true تقریبا تمام متدهای آرایه که تابعی را صدا میزنند -- مانند `find`، `filter`، `map`، همچنین یک استثنا از `sort`، پارامتر اختیاری اضافی `thisArg` را قبول میکنند. +<<<<<<< HEAD این پارامتر به دلیل اینکه به ندرت استفاده میشود، در قسمتهای بالایی گفته نشد. اما برای کامل بودن ما باید آن را پوشش دهیم. +======= +That parameter is not explained in the sections above, because it's rarely used. But for completeness, we have to cover it. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b سینتکس کامل این متدها در زیر آمده است: @@ -746,11 +833,19 @@ alert(soldiers[1].age); // 23 - `slice(start, end)` -- با ساختن یک آرایه جدید، المانها را از ایندکس `start` تا `end` (شامل نمیشود) در آن کپی میکند. - `concat(...items)` -- یک آرایه جدید را برمیگرداند: تمام عضوهای آرایه کنونی را کپی میکند و `items` را به آن اضافه میکند. اگر هر کدام از `items` آرایه باشد، سپس المانهای آن اضافه میشوند. +<<<<<<< HEAD - برای جستجو در بین المانها: - `indexOf/lastIndexOf(item, pos)` -- با شروع از موقعیت `pos` به دنبال `item` میگردد، ایندکس آن را برمیگرداند و در صورتی که پیدا نشود `1-` را برمیگرداند. - `includes(value)` -- اگر آرایه دارای `value` باشد، مقدار `true` را برمیگرداند در غیر این صورت `false`. - `find/filter(func)` -- المانها را از طریق تابع فیلتر میکند، اولین/تمام مقدارهایی که سبب میشوند تابع `true` برگرداند را برمیگرداند. - `findIndex` مانند `find` است اما به جای مقدار ایندکس را برمیگرداند. +======= +- To search among elements: + - `indexOf/lastIndexOf(item, pos)` -- look for `item` starting from position `pos`, and return the index or `-1` if not found. + - `includes(value)` -- returns `true` if the array has `value`, otherwise `false`. + - `find/filter(func)` -- filter elements through the function, return first/all values that make it return `true`. + - `findIndex` is like `find`, but returns the index instead of a value. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b - برای حلقه زدن در یک آرایه: - `forEach(func)` -- برای تمام المانها تابع `func` را صدا میزند، چیزی را برنمیگرداند. @@ -792,7 +887,11 @@ alert(soldiers[1].age); // 23 برای دیدن لیست کامل، از [راهنما](mdn:js/Array) استفاده کنید. +<<<<<<< HEAD با اولین نگاه ممکن است به نظر برسد که متدهای بسیار زیادی وجود دارد و به حافظه سپردن آنها مشکل است. اما در واقع بسیار آسانتر است. +======= +At first sight, it may seem that there are so many methods, quite difficult to remember. But actually, that's much easier. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b برای داشتن شناخت از آنها به برگه تقلب نگاه بیاندازید. سپس تکلیفهای این فصل را برای تمرین انجام دهید تا نسبت به متدهای آرایه تجربه بدست بیاورید. diff --git a/1-js/05-data-types/06-iterable/article.md b/1-js/05-data-types/06-iterable/article.md index 68d469009..66d0fede4 100644 --- a/1-js/05-data-types/06-iterable/article.md +++ b/1-js/05-data-types/06-iterable/article.md @@ -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` ندارد. diff --git a/1-js/05-data-types/10-destructuring-assignment/article.md b/1-js/05-data-types/10-destructuring-assignment/article.md index a6d89d5ea..6179dc3b0 100644 --- a/1-js/05-data-types/10-destructuring-assignment/article.md +++ b/1-js/05-data-types/10-destructuring-assignment/article.md @@ -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,10 +117,17 @@ 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 = { @@ -105,7 +135,11 @@ let user = { 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  @@ -459,9 +509,15 @@ 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 = []) { @@ -469,7 +525,11 @@ 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 - استخراج داده از آرایه/شیءهای تودرتو هم ممکن است، برای اینکار سمت چپ باید ساختار یکسانی با سمت راست داشته باشد. \ No newline at end of file diff --git a/1-js/05-data-types/12-json/article.md b/1-js/05-data-types/12-json/article.md index 6e6a52bbf..44daf7de1 100644 --- a/1-js/05-data-types/12-json/article.md +++ b/1-js/05-data-types/12-json/article.md @@ -405,7 +405,7 @@ alert( JSON.stringify(meetup) ); سینتکس آن: ```js -let value = JSON.parse(str, [reviver]); +let value = JSON.parse(str[, reviver]); ``` پارامتر str diff --git a/1-js/06-advanced-functions/06-function-object/article.md b/1-js/06-advanced-functions/06-function-object/article.md index f5cd3b0cc..baf0b4bdd 100644 --- a/1-js/06-advanced-functions/06-function-object/article.md +++ b/1-js/06-advanced-functions/06-function-object/article.md @@ -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ها، سینتکسی برای اضاف کردن اسم «درونی» وجود ندارد. diff --git a/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md b/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md index b607c4cfe..785b57b54 100644 --- a/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md +++ b/1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md @@ -1,5 +1,9 @@ +<<<<<<< HEAD به دلیل اینکه `ask` تابعهای `loginOk/loginFail` را بدون شیء دریافت میکند ارور ایجاد میشود. +======= +The error occurs because `askPassword` gets functions `loginOk/loginFail` without the object. +>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b زمانی که این تابع آنها را فرا میخواند، به طور طبیعی آنها `this=undefined` را فرض میکنند. diff --git a/1-js/06-advanced-functions/10-bind/article.md b/1-js/06-advanced-functions/10-bind/article.md index cca4fcfb8..adb45e44f 100644 --- a/1-js/06-advanced-functions/10-bind/article.md +++ b/1-js/06-advanced-functions/10-bind/article.md @@ -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` منتقل میشوند، برای مثال: diff --git a/1-js/08-prototypes/04-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md index 248b88320..fd424aa89 100644 --- a/1-js/08-prototypes/04-prototype-methods/article.md +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -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 - متدهای مدرن برای دریافت/تنظیم پروتوتایپ اینها هستند: diff --git a/1-js/10-error-handling/1-try-catch/article.md b/1-js/10-error-handling/1-try-catch/article.md index 429e0eb31..fabda3374 100644 --- a/1-js/10-error-handling/1-try-catch/article.md +++ b/1-js/10-error-handling/1-try-catch/article.md @@ -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 آنها اینگونه کار میکنند: diff --git a/1-js/11-async/02-promise-basics/article.md b/1-js/11-async/02-promise-basics/article.md index 9ce6efc9f..11b9d0ddd 100644 --- a/1-js/11-async/02-promise-basics/article.md +++ b/1-js/11-async/02-promise-basics/article.md @@ -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 کد ما ممکن است اینگونه بنظر برسد: diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md new file mode 100644 index 000000000..9fda8e000 --- /dev/null +++ b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md @@ -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; + }); +} +``` diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/task.md b/1-js/11-async/08-async-await/04-promise-all-failure/task.md new file mode 100644 index 000000000..74571c43e --- /dev/null +++ b/1-js/11-async/08-async-await/04-promise-all-failure/task.md @@ -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? \ No newline at end of file diff --git a/1-js/13-modules/02-import-export/article.md b/1-js/13-modules/02-import-export/article.md index 1a1ad451c..b1f1f7943 100644 --- a/1-js/13-modules/02-import-export/article.md +++ b/1-js/13-modules/02-import-export/article.md @@ -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" diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/article.md b/1-js/99-js-misc/07-weakref-finalizationregistry/article.md new file mode 100644 index 000000000..777bf703c --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/article.md @@ -0,0 +1,483 @@ + +# WeakRef and FinalizationRegistry + +```warn header="\"Hidden\" features of the language" +This article covers a very narrowly focused topic, that most developers extremely rarely encounter in practice (and may not even be aware of its existence). + +We recommend skipping this chapter if you have just started learning JavaScript. +``` + +Recalling the basic concept of the *reachability principle* from the <info:garbage-collection> chapter, +we can note that the JavaScript engine is guaranteed to keep values in memory that are accessible or in use. + +For example: + + +```js +// the user variable holds a strong reference to the object +let user = { name: "John" }; + +// let's overwrite the value of the user variable +user = null; + +// the reference is lost and the object will be deleted from memory + +``` + +Or a similar, but slightly more complicated code with two strong references: + +```js +// the user variable holds a strong reference to the object +let user = { name: "John" }; + +// copied the strong reference to the object into the admin variable +*!* +let admin = user; +*/!* + +// let's overwrite the value of the user variable +user = null; + +// the object is still reachable through the admin variable +``` +The object `{ name: "John" }` would only be deleted from memory if there were no strong references to it (if we also overwrote the value of the `admin` variable). + +In JavaScript, there is a concept called `WeakRef`, which behaves slightly differently in this case. + + +````smart header="Terms: \"Strong reference\", \"Weak reference\"" +**Strong reference** - is a reference to an object or value, that prevents them from being deleted by the garbage collector. Thereby, keeping the object or value in memory, to which it points. + +This means, that the object or value remains in memory and is not collected by the garbage collector as long, as there are active strong references to it. + +In JavaScript, ordinary references to objects are strong references. For example: + +```js +// the user variable holds a strong reference to this object +let user = { name: "John" }; +``` +**Weak reference** - is a reference to an object or value, that does *not* prevent them from being deleted by the garbage collector. +An object or value can be deleted by the garbage collector if, the only remaining references to them are weak references. +```` + +## WeakRef + + +````warn header="Note of caution" +Before we dive into it, it is worth noting that the correct use of the structures discussed in this article requires very careful thought, and they are best avoided if possible. +```` + +`WeakRef` - is an object, that contains a weak reference to another object, called `target` or `referent`. + +The peculiarity of `WeakRef` is that it does not prevent the garbage collector from deleting its referent-object. In other words, a `WeakRef` object does not keep the `referent` object alive. + +Now let's take the `user` variable as the "referent" and create a weak reference from it to the `admin` variable. +To create a weak reference, you need to use the `WeakRef` constructor, passing in the target object (the object you want a weak reference to). + +In our case — this is the `user` variable: + + +```js +// the user variable holds a strong reference to the object +let user = { name: "John" }; + +// the admin variable holds a weak reference to the object +*!* +let admin = new WeakRef(user); +*/!* + +``` + +The diagram below depicts two types of references: a strong reference using the `user` variable and a weak reference using the `admin` variable: + + + +Then, at some point, we stop using the `user` variable - it gets overwritten, goes out of scope, etc., while keeping the `WeakRef` instance in the `admin` variable: + +```js +// let's overwrite the value of the user variable +user = null; +``` + +A weak reference to an object is not enough to keep it "alive". When the only remaining references to a referent-object are weak references, the garbage collector is free to destroy this object and use its memory for something else. + +However, until the object is actually destroyed, the weak reference may return it, even if there are no more strong references to this object. +That is, our object becomes a kind of "[Schrödinger's cat](https://en.wikipedia.org/wiki/Schr%C3%B6dinger%27s_cat)" – we cannot know for sure whether it's "alive" or "dead": + + + +At this point, to get the object from the `WeakRef` instance, we will use its `deref()` method. + +The `deref()` method returns the referent-object that the `WeakRef` points to, if the object is still in memory. If the object has been deleted by the garbage collector, then the `deref()` method will return `undefined`: + + +```js +let ref = admin.deref(); + +if (ref) { + // the object is still accessible: we can perform any manipulations with it +} else { + // the object has been collected by the garbage collector +} +``` + +## WeakRef use cases + +`WeakRef` is typically used to create caches or [associative arrays](https://en.wikipedia.org/wiki/Associative_array) that store resource-intensive objects. +This allows one to avoid preventing these objects from being collected by the garbage collector solely based on their presence in the cache or associative array. + +One of the primary examples - is a situation when we have numerous binary image objects (for instance, represented as `ArrayBuffer` or `Blob`), and we want to associate a name or path with each image. +Existing data structures are not quite suitable for these purposes: + +- Using `Map` to create associations between names and images, or vice versa, will keep the image objects in memory since they are present in the `Map` as keys or values. +- `WeakMap` is ineligible for this goal either: because the objects represented as `WeakMap` keys use weak references, and are not protected from deletion by the garbage collector. + +But, in this situation, we need a data structure that would use weak references in its values. + +For this purpose, we can use a `Map` collection, whose values are `WeakRef` instances referring to the large objects we need. +Consequently, we will not keep these large and unnecessary objects in memory longer than they should be. + +Otherwise, this is a way to get the image object from the cache if it is still reachable. +If it has been garbage collected, we will re-generate or re-download it again. + +This way, less memory is used in some situations. + +## Example №1: using WeakRef for caching + +Below is a code snippet that demonstrates the technique of using `WeakRef`. + +In short, we use a `Map` with string keys and `WeakRef` objects as their values. +If the `WeakRef` object has not been collected by the garbage collector, we get it from the cache. +Otherwise, we re-download it again and put it in the cache for further possible reuse: + +```js +function fetchImg() { + // abstract function for downloading images... +} + +function weakRefCache(fetchImg) { // (1) + const imgCache = new Map(); // (2) + + return (imgName) => { // (3) + const cachedImg = imgCache.get(imgName); // (4) + + if (cachedImg?.deref()) { // (5) + return cachedImg?.deref(); + } + + const newImg = fetchImg(imgName); // (6) + imgCache.set(imgName, new WeakRef(newImg)); // (7) + + return newImg; + }; +} + +const getCachedImg = weakRefCache(fetchImg); +``` + +Let's delve into the details of what happened here: +1. `weakRefCache` - is a higher-order function that takes another function, `fetchImg`, as an argument. In this example, we can neglect a detailed description of the `fetchImg` function, since it can be any logic for downloading images. +2. `imgCache` - is a cache of images, that stores cached results of the `fetchImg` function, in the form of string keys (image name) and `WeakRef` objects as their values. +3. Return an anonymous function that takes the image name as an argument. This argument will be used as a key for the cached image. +4. Trying to get the cached result from the cache, using the provided key (image name). +5. If the cache contains a value for the specified key, and the `WeakRef` object has not been deleted by the garbage collector, return the cached result. +6. If there is no entry in the cache with the requested key, or `deref()` method returns `undefined` (meaning that the `WeakRef` object has been garbage collected), the `fetchImg` function downloads the image again. +7. Put the downloaded image into the cache as a `WeakRef` object. + +Now we have a `Map` collection, where the keys - are image names as strings, and values - are `WeakRef` objects containing the images themselves. + +This technique helps to avoid allocating a large amount of memory for resource-intensive objects, that nobody uses anymore. +It also saves memory and time in case of reusing cached objects. + +Here is a visual representation of what this code looks like: + + + +But, this implementation has its drawbacks: over time, `Map` will be filled with strings as keys, that point to a `WeakRef`, whose referent-object has already been garbage collected: + + + +One way to handle this problem - is to periodically scavenge the cache and clear out "dead" entries. +Another way - is to use finalizers, which we will explore next. + + +## Example №2: Using WeakRef to track DOM objects + +Another use case for `WeakRef` - is tracking DOM objects. + +Let's imagine a scenario where some third-party code or library interacts with elements on our page as long as they exist in the DOM. +For example, it could be an external utility for monitoring and notifying about the system's state (commonly so-called "logger" – a program that sends informational messages called "logs"). + +Interactive example: + +[codetabs height=420 src="weakref-dom"] + +When the "Start sending messages" button is clicked, in the so-called "logs display window" (an element with the `.window__body` class), messages (logs) start to appear. + +But, as soon as this element is deleted from the DOM, the logger should stop sending messages. +To reproduce the removal of this element, just click the "Close" button in the top right corner. + +In order not to complicate our work, and not to notify third-party code every time our DOM-element is available, and when it is not, it will be enough to create a weak reference to it using `WeakRef`. + +Once the element is removed from the DOM, the logger will notice it and stop sending messages. + +Now let's take a closer look at the source code (*tab `index.js`*): + +1. Get the DOM-element of the "Start sending messages" button. +2. Get the DOM-element of the "Close" button. +3. Get the DOM-element of the logs display window using the `new WeakRef()` constructor. This way, the `windowElementRef` variable holds a weak reference to the DOM-element. +4. Add an event listener on the "Start sending messages" button, responsible for starting the logger when clicked. +5. Add an event listener on the "Close" button, responsible for closing the logs display window when clicked. +6. Use `setInterval` to start displaying a new message every second. +7. If the DOM-element of the logs display window is still accessible and kept in memory, create and send a new message. +8. If the `deref()` method returns `undefined`, it means that the DOM-element has been deleted from memory. In this case, the logger stops displaying messages and clears the timer. +9. `alert`, which will be called, after the DOM-element of the logs display window is deleted from memory (i.e. after clicking the "Close" button). **Note, that deletion from memory may not happen immediately, as it depends only on the internal mechanisms of the garbage collector.** + + We cannot control this process directly from the code. However, despite this, we still have the option to force garbage collection from the browser. + + In Google Chrome, for example, to do this, you need to open the developer tools (`key:Ctrl` + `key:Shift` + `key:J` on Windows/Linux or `key:Option` + `key:⌘` + `key:J` on macOS), go to the "Performance" tab, and click on the bin icon button – "Collect garbage": + +  + + <br> + This functionality is supported in most modern browsers. After the actions are taken, the <code>alert</code> will trigger immediately. + +## FinalizationRegistry + +Now it is time to talk about finalizers. Before we move on, let's clarify the terminology: + +**Cleanup callback (finalizer)** - is a function that is executed, when an object, registered in the `FinalizationRegistry`, is deleted from memory by the garbage collector. + +Its purpose - is to provide the ability to perform additional operations, related to the object, after it has been finally deleted from memory. + +**Registry** (or `FinalizationRegistry`) - is a special object in JavaScript that manages the registration and unregistration of objects and their cleanup callbacks. + +This mechanism allows registering an object to track and associate a cleanup callback with it. +Essentially it is a structure that stores information about registered objects and their cleanup callbacks, and then automatically invokes those callbacks when the objects are deleted from memory. + +To create an instance of the `FinalizationRegistry`, it needs to call its constructor, which takes a single argument - the cleanup callback (finalizer). + +Syntax: + +```js +function cleanupCallback(heldValue) { + // cleanup callback code +} + +const registry = new FinalizationRegistry(cleanupCallback); +``` + +Here: + +- `cleanupCallback` - a cleanup callback that will be automatically called when a registered object is deleted from memory. +- `heldValue` - the value that is passed as an argument to the cleanup callback. If `heldValue` is an object, the registry keeps a strong reference to it. +- `registry` - an instance of `FinalizationRegistry`. + +`FinalizationRegistry` methods: + +- `register(target, heldValue [, unregisterToken])` - used to register objects in the registry. + + `target` - the object being registered for tracking. If the `target` is garbage collected, the cleanup callback will be called with `heldValue` as its argument. + + Optional `unregisterToken` – an unregistration token. It can be passed to unregister an object before the garbage collector deletes it. Typically, the `target` object is used as `unregisterToken`, which is the standard practice. +- `unregister(unregisterToken)` - the `unregister` method is used to unregister an object from the registry. It takes one argument - `unregisterToken` (the unregister token that was obtained when registering the object). + +Now let's move on to a simple example. Let's use the already-known `user` object and create an instance of `FinalizationRegistry`: + +```js +let user = { name: "John" }; + +const registry = new FinalizationRegistry((heldValue) => { + console.log(`${heldValue} has been collected by the garbage collector.`); +}); +``` + +Then, we will register the object, that requires a cleanup callback by calling the `register` method: + +```js +registry.register(user, user.name); +``` + +The registry does not keep a strong reference to the object being registered, as this would defeat its purpose. If the registry kept a strong reference, then the object would never be garbage collected. + +If the object is deleted by the garbage collector, our cleanup callback may be called at some point in the future, with the `heldValue` passed to it: + +```js +// When the user object is deleted by the garbage collector, the following message will be printed in the console: +"John has been collected by the garbage collector." +``` + +There are also situations where, even in implementations that use a cleanup callback, there is a chance that it will not be called. + +For example: +- When the program fully terminates its operation (for example, when closing a tab in a browser). +- When the `FinalizationRegistry` instance itself is no longer reachable to JavaScript code. + If the object that creates the `FinalizationRegistry` instance goes out of scope or is deleted, the cleanup callbacks registered in that registry might also not be invoked. + +## Caching with FinalizationRegistry + +Returning to our *weak* cache example, we can notice the following: +- Even though the values wrapped in the `WeakRef` have been collected by the garbage collector, there is still an issue of "memory leakage" in the form of the remaining keys, whose values have been collected by the garbage collector. + +Here is an improved caching example using `FinalizationRegistry`: + +```js +function fetchImg() { + // abstract function for downloading images... +} + +function weakRefCache(fetchImg) { + const imgCache = new Map(); + + *!* + const registry = new FinalizationRegistry((imgName) => { // (1) + const cachedImg = imgCache.get(imgName); + if (cachedImg && !cachedImg.deref()) imgCache.delete(imgName); + }); + */!* + + return (imgName) => { + const cachedImg = imgCache.get(imgName); + + if (cachedImg?.deref()) { + return cachedImg?.deref(); + } + + const newImg = fetchImg(imgName); + imgCache.set(imgName, new WeakRef(newImg)); + *!* + registry.register(newImg, imgName); // (2) + */!* + + return newImg; + }; +} + +const getCachedImg = weakRefCache(fetchImg); +``` + +1. To manage the cleanup of "dead" cache entries, when the associated `WeakRef` objects are collected by the garbage collector, we create a `FinalizationRegistry` cleanup registry. + + The important point here is, that in the cleanup callback, it should be checked, if the entry was deleted by the garbage collector and not re-added, in order not to delete a "live" entry. +2. Once the new value (image) is downloaded and put into the cache, we register it in the finalizer registry to track the `WeakRef` object. + +This implementation contains only actual or "live" key/value pairs. +In this case, each `WeakRef` object is registered in the `FinalizationRegistry`. +And after the objects are cleaned up by the garbage collector, the cleanup callback will delete all `undefined` values. + +Here is a visual representation of the updated code: + + + +A key aspect of the updated implementation is that finalizers allow parallel processes to be created between the "main" program and cleanup callbacks. +In the context of JavaScript, the "main" program - is our JavaScript-code, that runs and executes in our application or web page. + +Hence, from the moment an object is marked for deletion by the garbage collector, and to the actual execution of the cleanup callback, there may be a certain time gap. +It is important to understand that during this time gap, the main program can make any changes to the object or even bring it back to memory. + +That's why, in the cleanup callback, we must check to see if an entry has been added back to the cache by the main program to avoid deleting "live" entries. +Similarly, when searching for a key in the cache, there is a chance that the value has been deleted by the garbage collector, but the cleanup callback has not been executed yet. + +Such situations require special attention if you are working with `FinalizationRegistry`. + +## Using WeakRef and FinalizationRegistry in practice + +Moving from theory to practice, imagine a real-life scenario, where a user synchronizes their photos on a mobile device with some cloud service +(such as [iCloud](https://en.wikipedia.org/wiki/ICloud) or [Google Photos](https://en.wikipedia.org/wiki/Google_Photos)), +and wants to view them from other devices. In addition to the basic functionality of viewing photos, such services offer a lot of additional features, for example: + +- Photo editing and video effects. +- Creating "memories" and albums. +- Video montage from a series of photos. +- ...and much more. + +Here, as an example, we will use a fairly primitive implementation of such a service. +The main point - is to show a possible scenario of using `WeakRef` and `FinalizationRegistry` together in real life. + +Here is what it looks like: + + + +<br> +On the left side, there is a cloud library of photos (they are displayed as thumbnails). +We can select the images we need and create a collage, by clicking the "Create collage" button on the right side of the page. +Then, the resulting collage can be downloaded as an image. +</br><br> + +To increase page loading speed, it would be reasonable to download and display photo thumbnails in *compressed* quality. +But, to create a collage from selected photos, download and use them in *full-size* quality. + +Below, we can see, that the intrinsic size of the thumbnails is 240x240 pixels. +The size was chosen on purpose to increase loading speed. +Moreover, we do not need full-size photos in preview mode. + + + +<br> +Let's assume, that we need to create a collage of 4 photos: we select them, and then click the "Create collage" button. +At this stage, the already known to us <code>weakRefCache</code> function checks whether the required image is in the cache. +If not, it downloads it from the cloud and puts it in the cache for further use. +This happens for each selected image: +</br><br> + + + +</br> + +Paying attention to the output in the console, you can see, which of the photos were downloaded from the cloud - this is indicated by <span style="background-color:#133159;color:white;font-weight:500">FETCHED_IMAGE</span>. +Since this is the first attempt to create a collage, this means, that at this stage the "weak cache" was still empty, and all the photos were downloaded from the cloud and put in it. + +But, along with the process of downloading images, there is also a process of memory cleanup by the garbage collector. +This means, that the object stored in the cache, which we refer to, using a weak reference, is deleted by the garbage collector. +And our finalizer executes successfully, thereby deleting the key, by which the image was stored in the cache. +<span style="background-color:#901e30;color:white;font-weight:500;">CLEANED_IMAGE</span> notifies us about it: + + + +<br> +Next, we realize that we do not like the resulting collage, and decide to change one of the images and create a new one. +To do this, just deselect the unnecessary image, select another one, and click the "Create collage" button again: +</br><br> + + + +<br> +But this time not all images were downloaded from the network, and one of them was taken from the weak cache: the <span style="background-color:#385950;color:white;font-weight:500;">CACHED_IMAGE</span> message tells us about it. +This means that at the time of collage creation, the garbage collector had not yet deleted our image, and we boldly took it from the cache, +thereby reducing the number of network requests and speeding up the overall time of the collage creation process: +</br><br> + + + +<br> +Let's "play around" a little more, by replacing one of the images again and creating a new collage: +</br><br> + + + +<br> +This time the result is even more impressive. Of the 4 images selected, 3 of them were taken from the weak cache, and only one had to be downloaded from the network. +The reduction in network load was about 75%. Impressive, isn't it? +</br><br> + + + +</br> + +Of course, it is important to remember, that such behavior is not guaranteed, and depends on the specific implementation and operation of the garbage collector. + +Based on this, a completely logical question immediately arises: why do not we use an ordinary cache, where we can manage its entities ourselves, instead of relying on the garbage collector? +That's right, in the vast majority of cases there is no need to use `WeakRef` and `FinalizationRegistry`. + +Here, we simply demonstrated an alternative implementation of similar functionality, using a non-trivial approach with interesting language features. +Still, we cannot rely on this example, if we need a constant and predictable result. + +You can [open this example in the sandbox](sandbox:weakref-finalizationregistry). + +## Summary + +`WeakRef` - designed to create weak references to objects, allowing them to be deleted from memory by the garbage collector if there are no longer strong references to them. +This is beneficial for addressing excessive memory usage and optimizing the utilization of system resources in applications. + +`FinalizationRegistry` - is a tool for registering callbacks, that are executed when objects that are no longer strongly referenced, are destroyed. +This allows releasing resources associated with the object or performing other necessary operations before deleting the object from memory. \ No newline at end of file diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/google-chrome-developer-tools.png b/1-js/99-js-misc/07-weakref-finalizationregistry/google-chrome-developer-tools.png new file mode 100644 index 000000000..021637342 Binary files /dev/null and b/1-js/99-js-misc/07-weakref-finalizationregistry/google-chrome-developer-tools.png differ diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-dom.view/index.css b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-dom.view/index.css new file mode 100644 index 000000000..f6df812d0 --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-dom.view/index.css @@ -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; +} \ No newline at end of file diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-dom.view/index.html b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-dom.view/index.html new file mode 100644 index 000000000..7f93af4c7 --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-dom.view/index.html @@ -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> diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-dom.view/index.js b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-dom.view/index.js new file mode 100644 index 000000000..ea55b4478 --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-dom.view/index.js @@ -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); +}; \ No newline at end of file diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-01.svg b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-01.svg new file mode 100644 index 000000000..2a507dbcd --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-01.svg @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg viewBox="-38.324 -109.673 191.121 281.642" width="191.121px" height="281.642px" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com"> + <defs> + <style bx:fonts="Open Sans">@import url(https://fonts.googleapis.com/css2?family=Open+Sans%3Aital%2Cwght%400%2C300%3B0%2C400%3B0%2C500%3B0%2C600%3B0%2C700%3B0%2C800%3B1%2C300%3B1%2C400%3B1%2C500%3B1%2C600%3B1%2C700%3B1%2C800&display=swap);</style> + </defs> + <g id="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1" transform="matrix(1.6492669582366946, 0, 0, 1.6492669582366946, -81.42222595214844, 62.8305015563965)" style=""> + <g id="memory-user-john-admin.svg" transform="matrix(1, 0, 0, 1, -1.759865, 4.803441)"> + <text id="user" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; font-weight: 600; white-space: pre;" transform="matrix(0.735667, 0, 0, 0.753868, 7.56361, 10.722544)" x="31.917" y="-40.191">user</text> + <path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 39.02 4.206 L 125.8 4.206 L 125.8 48.227 L 39.02 48.227 L 39.02 4.206 Z" style=""/> + <text id="name:-"John"" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 9px; font-weight: 500; white-space: pre;" x="54.086" y="34.58">name: "John"</text> + <text id="Object" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 10.5px; font-weight: 500; white-space: pre;" x="64.679" y="23.487">Object</text> + <path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 36.398 -102.608 L 129.398 -102.608 L 129.398 -76.608 L 36.398 -76.608 L 36.398 -102.608 Z"/> + <text id="<global>" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 10.6px; font-weight: 500; white-space: pre;" x="59.233" y="-86.001"> <global></text> + <path d="M 105.999 1.57 L 113.013 13.895 L 98.985 13.895 L 105.999 1.57 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(1, 0, 0, -1, 0, 0)" bx:shape="triangle 98.985 1.57 14.028 12.325 0.5 0 1@df7d9fa9"/> + <g> + <rect x="105.024" y="-69.436" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-59.02" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-48.877" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + <g transform="matrix(1, 0, 0, 1.024052, -0.03572, 31.45084)" style=""> + <rect x="105.024" y="-69.051" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-59.02" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-48.877" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + <g transform="matrix(1, 0, 0, 1, -9.930843, 12.564639)"> + <path d="M 66.874 14.118 L 73.888 26.443 L 59.86 26.443 L 66.874 14.118 Z" style="fill-rule: nonzero; fill: rgb(192, 99, 52);" transform="matrix(1, 0, 0, -1, 0, 0)" bx:shape="triangle 59.86 14.118 14.028 12.325 0.5 0 1@9d4d6098"/> + <rect x="65.833" y="-81.984" width="2.056" height="55.884" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(192, 99, 52);"/> + </g> + <text id="text-1" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 14px; font-weight: 600; white-space: pre;" transform="matrix(0.735667, 0, 0, 0.753868, 87.03299, 10.790039)" x="31.917" y="-40.191">admin</text> + </g> + </g> +</svg> \ No newline at end of file diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-02.svg b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-02.svg new file mode 100644 index 000000000..6cc199a12 --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-02.svg @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg viewBox="-56.888 -212.944 192.167 294.011" width="178.044px" height="272.403px" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com"> + <defs> + <style bx:fonts="Open Sans">@import url(https://fonts.googleapis.com/css2?family=Open+Sans%3Aital%2Cwght%400%2C300%3B0%2C400%3B0%2C500%3B0%2C600%3B0%2C700%3B0%2C800%3B1%2C300%3B1%2C400%3B1%2C500%3B1%2C600%3B1%2C700%3B1%2C800&display=swap);</style> + </defs> + <g id="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1" transform="matrix(1.708757996559143, 0, 0, 1.708757996559143, -109.71231842041016, -35.19435119628906)" style=""> + <g id="memory-user-john-admin.svg" transform="matrix(1, 0, 0, 1, -1.759865, 4.803441)"> + <path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 39.02 4.206 L 125.8 4.206 L 125.8 48.227 L 39.02 48.227 L 39.02 4.206 Z" style=""/> + <path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 36.398 -102.608 L 129.398 -102.608 L 129.398 -76.608 L 36.398 -76.608 L 36.398 -102.608 Z"/> + <text id="<global>" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 10.8px; font-weight: 500; white-space: pre;" x="57.977" y="-85.926"> <global></text> + <path d="M 105.999 1.57 L 113.013 13.895 L 98.985 13.895 L 105.999 1.57 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(1, 0, 0, -1, 0, 0)" bx:shape="triangle 98.985 1.57 14.028 12.325 0.5 0 1@df7d9fa9"/> + <g style="" transform="matrix(0.7989, 0, 0, 0.7989, 13.076554, 6.083323)"> + <text id="name:-"John"" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 12px; font-weight: 500; white-space: pre;" x="48.212" y="37.89">name: "John"</text> + <text id="Object" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; font-weight: 500; white-space: pre;" x="61.598" y="23.794">Object</text> + </g> + <g> + <rect x="105.024" y="-69.436" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-59.02" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-48.877" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + <g transform="matrix(1, 0, 0, 1.024052, -0.03572, 31.45084)" style=""> + <rect x="105.024" y="-69.051" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-59.02" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-48.877" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + <text id="text-1" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 14px; font-weight: 600; white-space: pre;" transform="matrix(0.735667, 0, 0, 0.753868, 86.426659, 10.790039)" x="31.917" y="-40.191">admin</text> + </g> + </g> + <g transform="matrix(0.529145, 0, 0, 0.529145, -21.841589, -83.135681)" style=""> + <path d="M80.179,13.758c-18.342-18.342-48.08-18.342-66.422,0c-18.342,18.341-18.342,48.08,0,66.421 c18.342,18.342,48.08,18.342,66.422,0C98.521,61.837,98.521,32.099,80.179,13.758z M44.144,83.117 c-4.057,0-7.001-3.071-7.001-7.305c0-4.291,2.987-7.404,7.102-7.404c4.123,0,7.001,3.044,7.001,7.404 C51.246,80.113,48.326,83.117,44.144,83.117z M54.73,44.921c-4.15,4.905-5.796,9.117-5.503,14.088l0.097,2.495 c0.011,0.062,0.017,0.125,0.017,0.188c0,0.58-0.47,1.051-1.05,1.051c-0.004-0.001-0.008-0.001-0.012,0h-7.867 c-0.549,0-1.005-0.423-1.047-0.97l-0.202-2.623c-0.676-6.082,1.508-12.218,6.494-18.202c4.319-5.087,6.816-8.865,6.816-13.145 c0-4.829-3.036-7.536-8.548-7.624c-3.403,0-7.242,1.171-9.534,2.913c-0.264,0.201-0.607,0.264-0.925,0.173 s-0.575-0.327-0.693-0.636l-2.42-6.354c-0.169-0.442-0.02-0.943,0.364-1.224c3.538-2.573,9.441-4.235,15.041-4.235 c12.36,0,17.894,7.975,17.894,15.877C63.652,33.765,59.785,38.919,54.73,44.921z" style="fill: rgb(172, 67, 67);"/> + </g> + <rect x="-52.271" y="-25.92" width="160.339" height="87.431" style="fill: rgba(216, 216, 216, 0); stroke: rgb(172, 67, 67); stroke-width: 3px;"/> +</svg> \ No newline at end of file diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-03.svg b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-03.svg new file mode 100644 index 000000000..949a14f9f --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-03.svg @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg viewBox="-46.534 -212.944 529.701 256.516" width="540.055px" height="256.516px" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com"> + <defs> + <style bx:fonts="Open Sans">@import url(https://fonts.googleapis.com/css2?family=Open+Sans%3Aital%2Cwght%400%2C300%3B0%2C400%3B0%2C500%3B0%2C600%3B0%2C700%3B0%2C800%3B1%2C300%3B1%2C400%3B1%2C500%3B1%2C600%3B1%2C700%3B1%2C800&display=swap);</style> + </defs> + <g id="memory-user-john-admin.svg" transform="matrix(1.7087579965591428, 0, 0, 1.7087579965591428, -443.0394287109375, -47.16891479492199)" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"> + <path id="path-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.068 -87.403 L 333.494 -87.403 L 333.494 -57.762 L 234.068 -57.762 L 234.068 -87.403 Z" style=""/> + <path id="path-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.651 -87.403 L 492.549 -87.403 L 492.549 -57.762 L 336.651 -57.762 L 336.651 -87.403 Z" style=""/> + <path id="path-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.068 -53.711 L 333.494 -53.711 L 333.494 -24.07 L 234.068 -24.07 L 234.068 -53.711 Z" style=""/> + <path id="path-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.651 -53.711 L 492.783 -53.711 L 492.783 -24.07 L 336.651 -24.07 L 336.651 -53.711 Z" style=""/> + <path id="path-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.189 -20.462 L 333.615 -20.462 L 333.615 9.179 L 234.189 9.179 L 234.189 -20.462 Z" style=""/> + <path id="path-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.791 -20.462 L 493.057 -20.462 L 493.057 9.179 L 336.791 9.179 L 336.791 -20.462 Z" style=""/> + <path id="path-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.068 13.088 L 333.494 13.088 L 333.494 43.59 L 234.068 43.59 L 234.068 13.088 Z" style=""/> + <path id="path-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.651 13.088 L 493.054 13.088 L 493.054 43.59 L 336.651 43.59 L 336.651 13.088 Z" style=""/> + <text id="<global>" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 11.5772px; font-weight: 600; line-height: 19.9484px; white-space: pre;" transform="matrix(1.374145, 0, 0, 1.314286, -102.41082, 25.040792)" x="263.861" y="-70.535">key</text> + <text id="text-2" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 11.5772px; font-weight: 600; line-height: 19.9484px; white-space: pre;" transform="matrix(1.374145, 0, 0, 1.314286, 13.577469, 25.203112)" x="263.861" y="-70.535">value</text> + <text id="text-3" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; white-space: pre;" transform="matrix(1.071654, 0, 0, 1.142641, -43.57296, 46.390514)" x="263.861" y="-70.535">image-01.jpg</text> + <text id="text-4" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; white-space: pre;" transform="matrix(1.054877, 0, 0, 1.142641, -39.02475, 80.086914)" x="263.861" y="-70.535">image-02.jpg</text> + <text id="text-5" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; white-space: pre;" transform="matrix(1.054877, 0, 0, 1.142641, -39.468063, 113.637062)" x="263.861" y="-70.535">image-03.jpg</text> + <g transform="matrix(0.000041, -1, 1.521843, 0.000133, 289.017426, 2.750529)" style="transform-origin: 105.999px -35.503px;"> + <path d="M 105.537 7.964 L 110.918 13.895 L 100.155 13.895 L 105.537 7.964 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(1, 0, 0, -1, 0, 0)" bx:shape="triangle 100.155 7.964 10.763 5.931 0.5 0 1@87a4267a"/> + <g> + <rect x="105.024" y="-69.436" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-59.02" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-48.877" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + <g transform="matrix(1, 0, 0, 1.024052, -0.03572, 31.45084)" style=""> + <rect x="105.024" y="-69.051" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-59.02" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-48.877" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + </g> + <text id="text-6" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 9.73113px; font-style: italic; white-space: pre;" transform="matrix(1.038937, 0, 0, 1.142641, 69.710487, 42.749538)" x="263.861" y="-70.535">WeakRef object</text> + <g transform="matrix(0.441364, 0, 0, 0.412845, 420.761139, -59.372555)" style=""> + <g> + <path d=" M 58.15 23.65 L 58.15 20 54.25 20 Q 50.95 20 49.35 22.2 47.75 24.4 47.75 27.7 L 47.05 41.45 Q 46.8 43.25 46.1 44.6 45.45 46.05 44.35 47.05 43.9 47.55 43.2 47.75 42.8 47.95 41.85 48.15 L 41.85 51.6 Q 42.95 51.8 43.2 51.9 43.9 52.1 44.35 52.45 45.45 53.25 46.05 54.55 47.65 58.15 47.65 61.65 L 47.65 72.1 Q 47.65 75.7 49.45 77.85 51.25 80 54.1 80 L 58.15 80 58.15 76.25 55.25 76.25 Q 54.1 76.25 53.35 75.65 52.55 74.95 52.2 74 L 51.65 71.75 51.5 69.45 51.35 59.8 Q 51.35 57.55 50.8 55.8 50.15 53.65 49.65 52.8 48.8 51.5 47.9 50.8 47.05 50.1 46.1 49.75 47 49.3 47.8 48.7 48.95 47.85 49.6 46.75 50.1 45.8 50.75 43.7 51.35 41.85 51.35 39.45 L 51.55 28.05 52 25.9 Q 52.35 24.85 53.15 24.2 53.9 23.65 55.25 23.65 L 58.15 23.65 Z" style="fill: rgb(175, 110, 36);"/> + </g> + </g> + <g transform="matrix(-0.441364, 0, 0, -0.412845, 414.750092, -88.816727)" style="transform-origin: 50px 50px;"> + <g> + <path d="M 14.392 23.65 L 14.392 20 L 10.492 20 C 8.292 20 6.659 20.733 5.592 22.2 C 4.525 23.667 3.992 25.5 3.992 27.7 L 3.292 41.45 C 3.125 42.65 2.809 43.7 2.342 44.6 C 1.909 45.567 1.325 46.383 0.592 47.05 C 0.292 47.383 -0.091 47.617 -0.558 47.75 C -0.825 47.883 -1.275 48.017 -1.908 48.15 L -1.908 51.6 C -1.175 51.733 -0.725 51.833 -0.558 51.9 C -0.091 52.033 0.292 52.217 0.592 52.45 C 1.325 52.983 1.892 53.683 2.292 54.55 C 3.359 56.95 3.892 59.317 3.892 61.65 L 3.892 72.1 C 3.892 74.5 4.492 76.417 5.692 77.85 C 6.892 79.283 8.442 80 10.342 80 L 14.392 80 L 14.392 76.25 L 11.492 76.25 C 10.725 76.25 10.092 76.05 9.592 75.65 C 9.059 75.183 8.675 74.633 8.442 74 L 7.892 71.75 L 7.742 69.45 L 7.592 59.8 C 7.592 58.3 7.409 56.967 7.042 55.8 C 6.609 54.367 6.225 53.367 5.892 52.8 C 5.325 51.933 4.742 51.267 4.142 50.8 C 3.575 50.333 2.975 49.983 2.342 49.75 C 2.942 49.45 3.509 49.1 4.042 48.7 C 4.809 48.133 5.409 47.483 5.842 46.75 C 6.175 46.117 6.559 45.1 6.992 43.7 C 7.392 42.467 7.592 41.05 7.592 39.45 L 7.792 28.05 L 8.242 25.9 C 8.475 25.2 8.859 24.633 9.392 24.2 C 9.892 23.833 10.592 23.65 11.492 23.65 L 14.392 23.65 Z" style="fill: rgb(175, 110, 36);"/> + <path d="M 255.632 27.46 L 268.667 47.91 L 242.597 47.91 L 255.632 27.46 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, 1, 0.000093, 86.522967, -302.258205)" bx:shape="triangle 242.597 27.46 26.07 20.45 0.5 0 1@97f9cc39"/> + <rect x="254.391" y="-239.419" width="4.82" height="17.271" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, 86.522728, -302.258331)"/> + <rect x="254.367" y="-203.504" width="4.82" height="17.271" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, 86.522728, -302.258331)"/> + <rect x="254.367" y="-168.53" width="4.82" height="17.271" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, 86.522728, -302.258331)"/> + <rect x="254.391" y="-243.818" width="4.82" height="17.687" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, -21.921179, -302.35498)"/> + <rect x="254.367" y="-208.398" width="4.82" height="17.687" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, -21.921179, -302.35498)"/> + <rect x="254.367" y="-172.584" width="4.82" height="17.687" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, -21.921179, -302.35498)"/> + <text id="text-7" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 9.73113px; font-style: italic; white-space: pre;" transform="matrix(-2.353923, 0, 0, -2.767724, 945.975708, -228.376572)" x="263.861" y="-70.535">WeakRef object</text> + <path d="M 108.749 -57.365 L 108.749 -61.015 L 104.849 -61.015 C 102.649 -61.015 101.015 -60.281 99.949 -58.815 C 98.882 -57.348 98.349 -55.515 98.349 -53.315 L 97.649 -39.565 C 97.482 -38.365 97.165 -37.315 96.699 -36.415 C 96.265 -35.448 95.682 -34.631 94.949 -33.965 C 94.649 -33.631 94.265 -33.398 93.799 -33.265 C 93.532 -33.131 93.082 -32.998 92.449 -32.865 L 92.449 -29.415 C 93.182 -29.281 93.632 -29.181 93.799 -29.115 C 94.265 -28.981 94.649 -28.798 94.949 -28.565 C 95.682 -28.031 96.249 -27.331 96.649 -26.465 C 97.715 -24.065 98.249 -21.698 98.249 -19.365 L 98.249 -8.915 C 98.249 -6.515 98.849 -4.598 100.049 -3.165 C 101.249 -1.731 102.799 -1.015 104.699 -1.015 L 108.749 -1.015 L 108.749 -4.765 L 105.849 -4.765 C 105.082 -4.765 104.449 -4.965 103.949 -5.365 C 103.415 -5.831 103.032 -6.381 102.799 -7.015 L 102.249 -9.265 L 102.099 -11.565 L 101.949 -21.215 C 101.949 -22.715 101.765 -24.048 101.399 -25.215 C 100.965 -26.648 100.582 -27.648 100.249 -28.215 C 99.682 -29.081 99.099 -29.748 98.499 -30.215 C 97.932 -30.681 97.332 -31.031 96.699 -31.265 C 97.299 -31.565 97.865 -31.915 98.399 -32.315 C 99.165 -32.881 99.765 -33.531 100.199 -34.265 C 100.532 -34.898 100.915 -35.915 101.349 -37.315 C 101.749 -38.548 101.949 -39.965 101.949 -41.565 L 102.149 -52.965 L 102.599 -55.115 C 102.832 -55.815 103.215 -56.381 103.749 -56.815 C 104.249 -57.181 104.949 -57.365 105.849 -57.365 L 108.749 -57.365 Z" style="fill: rgb(175, 110, 36); transform-box: fill-box; transform-origin: 50% 50%;" transform="matrix(-1, 0, 0, -1, -0.000004, -0.000028)"/> + <path d="M 15.325 -57.155 L 15.325 -60.805 L 11.425 -60.805 C 9.225 -60.805 7.591 -60.072 6.525 -58.605 C 5.458 -57.139 4.925 -55.305 4.925 -53.105 L 4.225 -39.355 C 4.058 -38.155 3.741 -37.105 3.275 -36.205 C 2.841 -35.239 2.258 -34.422 1.525 -33.755 C 1.225 -33.422 0.841 -33.189 0.375 -33.055 C 0.108 -32.922 -0.342 -32.789 -0.975 -32.655 L -0.975 -29.205 C -0.242 -29.072 0.208 -28.972 0.375 -28.905 C 0.841 -28.772 1.225 -28.589 1.525 -28.355 C 2.258 -27.822 2.825 -27.122 3.225 -26.255 C 4.291 -23.855 4.825 -21.489 4.825 -19.155 L 4.825 -8.705 C 4.825 -6.305 5.425 -4.389 6.625 -2.955 C 7.825 -1.522 9.375 -0.805 11.275 -0.805 L 15.325 -0.805 L 15.325 -4.555 L 12.425 -4.555 C 11.658 -4.555 11.025 -4.755 10.525 -5.155 C 9.991 -5.622 9.608 -6.172 9.375 -6.805 L 8.825 -9.055 L 8.675 -11.355 L 8.525 -21.005 C 8.525 -22.505 8.341 -23.839 7.975 -25.005 C 7.541 -26.439 7.158 -27.439 6.825 -28.005 C 6.258 -28.872 5.675 -29.539 5.075 -30.005 C 4.508 -30.472 3.908 -30.822 3.275 -31.055 C 3.875 -31.355 4.441 -31.705 4.975 -32.105 C 5.741 -32.672 6.341 -33.322 6.775 -34.055 C 7.108 -34.689 7.491 -35.705 7.925 -37.105 C 8.325 -38.339 8.525 -39.755 8.525 -41.355 L 8.725 -52.755 L 9.175 -54.905 C 9.408 -55.605 9.791 -56.172 10.325 -56.605 C 10.825 -56.972 11.525 -57.155 12.425 -57.155 L 15.325 -57.155 Z" style="fill: rgb(175, 110, 36);"/> + </g> + </g> + </g> + <g transform="matrix(0.3355039954185486, 0, 0, 0.3355039954185486, 67.31304168701172, -387.90008544921875)" style=""> + <g transform="matrix(0.178489, 0, 0, 0.175502, 747.077209, 776.317017)" style=""> + <path d="M 267.184 437.8 C 267.184 466.3 290.384 489.4 318.784 489.4 L 704.984 489.4 C 733.484 489.4 756.584 466.2 756.584 437.8 L 756.584 51.6 C 756.584 23.1 733.384 0 704.984 0 L 318.784 0 C 290.284 0 267.184 23.2 267.184 51.6 C 267.184 51.6 267.184 437.8 267.184 437.8 Z M 704.984 464.9 L 318.784 464.9 C 303.884 464.9 291.684 452.7 291.684 437.8 L 291.684 373.3 L 384.484 280.5 L 463.784 359.8 C 468.584 364.6 476.284 364.6 481.084 359.8 L 624.284 216.6 L 732.084 324.4 L 732.084 437.8 C 732.084 452.7 719.884 464.9 704.984 464.9 Z M 318.784 24.5 L 704.984 24.5 C 719.884 24.5 732.084 36.7 732.084 51.6 L 732.084 289.7 L 632.884 190.6 C 628.084 185.8 620.384 185.8 615.584 190.6 L 472.384 333.8 L 393.084 254.5 C 388.284 249.7 380.584 249.7 375.784 254.5 L 291.684 338.6 L 291.684 51.6 C 291.684 36.7 303.884 24.5 318.784 24.5 Z" style="fill: rgb(175, 110, 36);"/> + <path d="M 418.884 196.1 C 453.284 196.1 481.184 168.1 481.184 133.8 C 481.184 99.5 453.184 71.5 418.884 71.5 C 384.584 71.5 356.584 99.5 356.584 133.8 C 356.584 168.1 384.484 196.1 418.884 196.1 Z M 418.884 96 C 439.784 96 456.684 113 456.684 133.8 C 456.684 154.6 439.684 171.6 418.884 171.6 C 398.084 171.6 381.084 154.6 381.084 133.8 C 381.084 113 397.984 96 418.884 96 Z" style="fill: rgb(175, 110, 36);"/> + </g> + </g> + <path d="M -161.945 -20.71 L -152.75 -5.287 L -171.141 -5.287 L -161.945 -20.71 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -0.9999999999999999, -0.9999999999999999, -0.00008700000762473792, 281.6644897460937, -149.34440612792974)" bx:shape="triangle -171.141 -20.71 18.391 15.423 0.5 0 1@d86e5ee8"/> + <rect x="179.461" y="-180.566" width="3.4" height="13.026" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -0.9999999999999999, 0.9999999999999999, 0.00008700000762473792, 323.0706176757812, 192.94052124023426)"/> + <rect x="179.444" y="-153.479" width="3.4" height="13.026" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -0.9999999999999999, 0.9999999999999999, 0.00008700000762473792, 323.0706176757812, 192.94052124023426)"/> + <rect x="179.444" y="-127.103" width="3.4" height="13.026" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -0.9999999999999999, 0.9999999999999999, 0.00008700000762473792, 323.0706176757812, 192.94052124023426)"/> + <rect x="179.461" y="-183.883" width="3.4" height="13.339" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -0.9999999999999999, 0.9999999999999999, 0.00008700000762473792, 404.85729980468744, 193.00871276855457)"/> + <rect x="179.444" y="-157.171" width="3.4" height="13.339" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -0.9999999999999999, 0.9999999999999999, 0.00008700000762473792, 404.85729980468744, 193.00871276855457)"/> + <rect x="179.444" y="-130.16" width="3.4" height="13.339" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -0.9999999999999999, 0.9999999999999999, 0.00008700000762473792, 404.85729980468744, 193.00871276855457)"/> + <text id="text-8" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 9.73113px; font-style: italic; white-space: pre;" transform="matrix(1.7752919197082517, 0, 0, 1.9524970054626463, -325.1152648925781, 140.82048034667957)" x="263.861" y="-70.535">WeakRef object</text> + <path d="M 318.601 -16.998 L 318.601 -19.573 L 315.66 -19.573 C 314.001 -19.573 312.769 -19.055 311.964 -18.021 C 311.16 -16.986 310.758 -15.693 310.758 -14.141 L 310.23 -4.441 C 310.104 -3.594 309.865 -2.853 309.513 -2.218 C 309.186 -1.536 308.746 -0.96 308.193 -0.49 C 307.967 -0.255 307.678 -0.09 307.326 0.004 C 307.125 0.098 306.786 0.192 306.308 0.286 L 306.308 2.72 C 306.861 2.814 307.2 2.884 307.326 2.931 C 307.678 3.025 307.967 3.155 308.193 3.319 C 308.746 3.696 309.174 4.189 309.475 4.801 C 310.28 6.494 310.682 8.163 310.682 9.81 L 310.682 17.181 C 310.682 18.875 311.135 20.227 312.04 21.238 C 312.945 22.249 314.114 22.755 315.547 22.755 L 318.601 22.755 L 318.601 20.109 L 316.414 20.109 C 315.836 20.109 315.358 19.968 314.981 19.686 C 314.579 19.357 314.29 18.969 314.114 18.522 L 313.699 16.935 L 313.586 15.312 L 313.473 8.504 C 313.473 7.446 313.334 6.506 313.058 5.683 C 312.731 4.671 312.442 3.966 312.191 3.566 C 311.763 2.955 311.323 2.485 310.871 2.155 C 310.443 1.826 309.991 1.579 309.513 1.415 C 309.966 1.203 310.393 0.956 310.795 0.674 C 311.373 0.274 311.826 -0.184 312.153 -0.702 C 312.404 -1.148 312.693 -1.866 313.02 -2.853 C 313.322 -3.723 313.473 -4.723 313.473 -5.852 L 313.623 -13.894 L 313.963 -15.41 C 314.139 -15.904 314.428 -16.304 314.83 -16.61 C 315.207 -16.868 315.735 -16.998 316.414 -16.998 L 318.601 -16.998 Z" style="fill: rgb(175, 110, 36);" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, -1.1368683772161603e-13)"/> + <path d="M 389.058 -17.145 L 389.058 -19.72 L 386.117 -19.72 C 384.458 -19.72 383.226 -19.203 382.421 -18.168 C 381.617 -17.134 381.215 -15.84 381.215 -14.288 L 380.687 -4.588 C 380.561 -3.742 380.322 -3.001 379.97 -2.366 C 379.644 -1.684 379.204 -1.108 378.651 -0.638 C 378.424 -0.403 378.135 -0.238 377.783 -0.144 C 377.582 -0.05 377.243 0.044 376.765 0.138 L 376.765 2.572 C 377.318 2.666 377.658 2.737 377.783 2.784 C 378.135 2.878 378.424 3.007 378.651 3.172 C 379.204 3.548 379.631 4.042 379.933 4.653 C 380.737 6.346 381.139 8.016 381.139 9.662 L 381.139 17.034 C 381.139 18.727 381.592 20.079 382.497 21.09 C 383.402 22.101 384.571 22.607 386.004 22.607 L 389.058 22.607 L 389.058 19.961 L 386.871 19.961 C 386.293 19.961 385.815 19.82 385.438 19.538 C 385.036 19.209 384.747 18.821 384.571 18.374 L 384.156 16.787 L 384.043 15.164 L 383.93 8.357 C 383.93 7.299 383.792 6.358 383.515 5.535 C 383.188 4.524 382.899 3.818 382.648 3.419 C 382.22 2.807 381.78 2.337 381.328 2.008 C 380.901 1.679 380.448 1.432 379.97 1.267 C 380.423 1.055 380.85 0.808 381.253 0.526 C 381.831 0.127 382.283 -0.332 382.61 -0.849 C 382.861 -1.296 383.151 -2.013 383.477 -3.001 C 383.779 -3.871 383.93 -4.87 383.93 -5.999 L 384.081 -14.041 L 384.42 -15.558 C 384.596 -16.052 384.885 -16.452 385.287 -16.757 C 385.664 -17.016 386.192 -17.145 386.871 -17.145 L 389.058 -17.145 Z" style="fill: rgb(175, 110, 36); transform-origin: 382.912px 1.4435px;" transform="matrix(-1, 0, 0, -1, 0, -0.000015020371)"/> + <path d="M 333.236 -45.236 C 333.236 -43.558 334.625 -42.198 336.326 -42.198 L 359.453 -42.198 C 361.16 -42.198 362.543 -43.564 362.543 -45.236 L 362.543 -67.977 C 362.543 -69.655 361.154 -71.015 359.453 -71.015 L 336.326 -71.015 C 334.619 -71.015 333.236 -69.649 333.236 -67.977 C 333.236 -67.977 333.236 -45.236 333.236 -45.236 Z M 359.453 -43.641 L 336.326 -43.641 C 335.434 -43.641 334.703 -44.359 334.703 -45.236 L 334.703 -49.034 L 340.26 -54.499 L 345.009 -49.829 C 345.297 -49.547 345.758 -49.547 346.045 -49.829 L 354.62 -58.261 L 361.076 -51.914 L 361.076 -45.236 C 361.076 -44.359 360.345 -43.641 359.453 -43.641 Z M 336.326 -69.572 L 359.453 -69.572 C 360.345 -69.572 361.076 -68.854 361.076 -67.977 L 361.076 -53.957 L 355.135 -59.792 C 354.848 -60.075 354.387 -60.075 354.099 -59.792 L 345.524 -51.36 L 340.775 -56.029 C 340.488 -56.312 340.027 -56.312 339.739 -56.029 L 334.703 -51.077 L 334.703 -67.977 C 334.703 -68.854 335.434 -69.572 336.326 -69.572 Z" style="fill: rgb(175, 110, 36);" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, -1.1368683772161603e-13)"/> + <path d="M 341.32 -59.468 C 343.38 -59.468 345.051 -61.117 345.051 -63.137 C 345.051 -65.156 343.374 -66.805 341.32 -66.805 C 339.266 -66.805 337.59 -65.156 337.59 -63.137 C 337.59 -61.117 339.26 -59.468 341.32 -59.468 Z M 341.32 -65.362 C 342.572 -65.362 343.584 -64.361 343.584 -63.137 C 343.584 -61.912 342.566 -60.911 341.32 -60.911 C 340.075 -60.911 339.057 -61.912 339.057 -63.137 C 339.057 -64.361 340.069 -65.362 341.32 -65.362 Z" style="fill: rgb(175, 110, 36);" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, -1.1368683772161603e-13)"/> + <path d="M 333.305 13.146 C 333.305 14.824 334.694 16.184 336.395 16.184 L 359.522 16.184 C 361.229 16.184 362.612 14.818 362.612 13.146 L 362.612 -9.594 C 362.612 -11.272 361.223 -12.633 359.522 -12.633 L 336.395 -12.633 C 334.688 -12.633 333.305 -11.267 333.305 -9.594 C 333.305 -9.594 333.305 13.146 333.305 13.146 Z M 359.522 14.742 L 336.395 14.742 C 335.503 14.742 334.772 14.023 334.772 13.146 L 334.772 9.348 L 340.329 3.884 L 345.078 8.553 C 345.366 8.836 345.827 8.836 346.114 8.553 L 354.69 0.121 L 361.145 6.469 L 361.145 13.146 C 361.145 14.023 360.415 14.742 359.522 14.742 Z M 336.395 -11.19 L 359.522 -11.19 C 360.415 -11.19 361.145 -10.472 361.145 -9.594 L 361.145 4.426 L 355.205 -1.41 C 354.917 -1.692 354.456 -1.692 354.169 -1.41 L 345.593 7.022 L 340.844 2.353 C 340.557 2.07 340.096 2.07 339.809 2.353 L 334.772 7.305 L 334.772 -9.594 C 334.772 -10.472 335.503 -11.19 336.395 -11.19 Z" style="fill: rgb(175, 110, 36);" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, -1.1368683772161603e-13)"/> + <path d="M 341.39 -1.086 C 343.45 -1.086 345.12 -2.735 345.12 -4.754 C 345.12 -6.774 343.444 -8.423 341.39 -8.423 C 339.335 -8.423 337.659 -6.774 337.659 -4.754 C 337.659 -2.735 339.329 -1.086 341.39 -1.086 Z M 341.39 -6.98 C 342.641 -6.98 343.653 -5.979 343.653 -4.754 C 343.653 -3.529 342.635 -2.528 341.39 -2.528 C 340.144 -2.528 339.126 -3.529 339.126 -4.754 C 339.126 -5.979 340.138 -6.98 341.39 -6.98 Z" style="fill: rgb(175, 110, 36);" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, -1.1368683772161603e-13)"/> +</svg> \ No newline at end of file diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-04.svg b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-04.svg new file mode 100644 index 000000000..1177d6580 --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-04.svg @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg viewBox="-44.887 -212.944 528.054 256.516" width="540.055px" height="256.516px" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com"> + <defs> + <style bx:fonts="Open Sans">@import url(https://fonts.googleapis.com/css2?family=Open+Sans%3Aital%2Cwght%400%2C300%3B0%2C400%3B0%2C500%3B0%2C600%3B0%2C700%3B0%2C800%3B1%2C300%3B1%2C400%3B1%2C500%3B1%2C600%3B1%2C700%3B1%2C800&display=swap);</style> + </defs> + <g id="memory-user-john-admin.svg" transform="matrix(1.708757996559143, 0, 0, 1.708757996559143, -443.0394287109375, -47.168914794921875)" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"> + <text id="name:-"John"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" style="white-space: pre;" x="19.382" y="-93.28"><tspan x="49.382" y="36.72" style="font-size: 12px; word-spacing: 0px;">name: "John"</tspan></text> + <text id="Object" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" style="white-space: pre;" x="17.598" y="-86.206"><tspan x="61.598" y="23.794" style="font-size: 14px; word-spacing: 0px;">Object</tspan></text> + <path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 40.047 -102 L 133.047 -102 L 133.047 -76 L 40.047 -76 L 40.047 -102 Z"/> + <text id="text-1" style="fill: rgb(172, 67, 67); font-family: PTMono-Regular, 'PT Mono'; font-size: 14px; white-space: pre;" transform="matrix(0.735667, 0, 0, 0.753868, 86.426659, 10.790039)" x="31.917" y="-40.191">admin</text> + <path id="path-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.068 -87.403 L 333.494 -87.403 L 333.494 -57.762 L 234.068 -57.762 L 234.068 -87.403 Z" style=""/> + <path id="path-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.651 -87.403 L 492.549 -87.403 L 492.549 -57.762 L 336.651 -57.762 L 336.651 -87.403 Z" style=""/> + <path id="path-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.068 -53.711 L 333.494 -53.711 L 333.494 -24.07 L 234.068 -24.07 L 234.068 -53.711 Z" style=""/> + <path id="path-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.651 -53.711 L 492.783 -53.711 L 492.783 -24.07 L 336.651 -24.07 L 336.651 -53.711 Z" style=""/> + <path id="path-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.189 -20.462 L 333.615 -20.462 L 333.615 9.179 L 234.189 9.179 L 234.189 -20.462 Z" style=""/> + <path id="path-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.791 -20.462 L 493.057 -20.462 L 493.057 9.179 L 336.791 9.179 L 336.791 -20.462 Z" style=""/> + <path id="path-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.068 13.088 L 333.494 13.088 L 333.494 43.59 L 234.068 43.59 L 234.068 13.088 Z" style=""/> + <path id="path-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.651 13.088 L 493.054 13.088 L 493.054 43.59 L 336.651 43.59 L 336.651 13.088 Z" style=""/> + <text id="<global>" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 11.5772px; font-weight: 600; line-height: 19.9484px; white-space: pre;" transform="matrix(1.374145, 0, 0, 1.314286, -102.41082, 25.040792)" x="263.861" y="-70.535">key</text> + <text id="text-2" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 11.5772px; font-weight: 600; line-height: 19.9484px; white-space: pre;" transform="matrix(1.374145, 0, 0, 1.314286, 13.577469, 25.203112)" x="263.861" y="-70.535">value</text> + <text id="text-3" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; white-space: pre;" transform="matrix(1.071654, 0, 0, 1.142641, -43.57296, 46.390514)" x="263.861" y="-70.535">image-01.jpg</text> + <text id="text-4" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; white-space: pre;" transform="matrix(1.054877, 0, 0, 1.142641, -39.02475, 80.086914)" x="263.861" y="-70.535">image-02.jpg</text> + <text id="text-5" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; white-space: pre;" transform="matrix(1.054877, 0, 0, 1.142641, -39.468063, 113.637062)" x="263.861" y="-70.535">image-03.jpg</text> + <g transform="matrix(0.000041, -1, 1.521843, 0.000133, 289.017426, 2.750529)" style="transform-origin: 105.999px -35.503px;"> + <path d="M 105.537 7.964 L 110.918 13.895 L 100.155 13.895 L 105.537 7.964 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(1, 0, 0, -1, 0, 0)" bx:shape="triangle 100.155 7.964 10.763 5.931 0.5 0 1@87a4267a"/> + <g> + <rect x="105.024" y="-69.436" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-59.02" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-48.877" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + <g transform="matrix(1, 0, 0, 1.024052, -0.03572, 31.45084)" style=""> + <rect x="105.024" y="-69.051" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-59.02" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-48.877" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + </g> + <text id="text-6" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 9.73113px; font-style: italic; white-space: pre;" transform="matrix(1.038937, 0, 0, 1.142641, 69.710487, 42.749538)" x="263.861" y="-70.535">WeakRef object</text> + <g transform="matrix(0.441364, 0, 0, 0.412845, 420.761139, -59.372555)" style=""> + <g> + <path d=" M 58.15 23.65 L 58.15 20 54.25 20 Q 50.95 20 49.35 22.2 47.75 24.4 47.75 27.7 L 47.05 41.45 Q 46.8 43.25 46.1 44.6 45.45 46.05 44.35 47.05 43.9 47.55 43.2 47.75 42.8 47.95 41.85 48.15 L 41.85 51.6 Q 42.95 51.8 43.2 51.9 43.9 52.1 44.35 52.45 45.45 53.25 46.05 54.55 47.65 58.15 47.65 61.65 L 47.65 72.1 Q 47.65 75.7 49.45 77.85 51.25 80 54.1 80 L 58.15 80 58.15 76.25 55.25 76.25 Q 54.1 76.25 53.35 75.65 52.55 74.95 52.2 74 L 51.65 71.75 51.5 69.45 51.35 59.8 Q 51.35 57.55 50.8 55.8 50.15 53.65 49.65 52.8 48.8 51.5 47.9 50.8 47.05 50.1 46.1 49.75 47 49.3 47.8 48.7 48.95 47.85 49.6 46.75 50.1 45.8 50.75 43.7 51.35 41.85 51.35 39.45 L 51.55 28.05 52 25.9 Q 52.35 24.85 53.15 24.2 53.9 23.65 55.25 23.65 L 58.15 23.65 Z" style="fill: rgb(175, 110, 36);"/> + </g> + </g> + <g transform="matrix(-0.441364, 0, 0, -0.412845, 414.750092, -88.816727)" style="transform-origin: 50px 50px;"> + <g> + <path d="M 14.392 23.65 L 14.392 20 L 10.492 20 C 8.292 20 6.659 20.733 5.592 22.2 C 4.525 23.667 3.992 25.5 3.992 27.7 L 3.292 41.45 C 3.125 42.65 2.809 43.7 2.342 44.6 C 1.909 45.567 1.325 46.383 0.592 47.05 C 0.292 47.383 -0.091 47.617 -0.558 47.75 C -0.825 47.883 -1.275 48.017 -1.908 48.15 L -1.908 51.6 C -1.175 51.733 -0.725 51.833 -0.558 51.9 C -0.091 52.033 0.292 52.217 0.592 52.45 C 1.325 52.983 1.892 53.683 2.292 54.55 C 3.359 56.95 3.892 59.317 3.892 61.65 L 3.892 72.1 C 3.892 74.5 4.492 76.417 5.692 77.85 C 6.892 79.283 8.442 80 10.342 80 L 14.392 80 L 14.392 76.25 L 11.492 76.25 C 10.725 76.25 10.092 76.05 9.592 75.65 C 9.059 75.183 8.675 74.633 8.442 74 L 7.892 71.75 L 7.742 69.45 L 7.592 59.8 C 7.592 58.3 7.409 56.967 7.042 55.8 C 6.609 54.367 6.225 53.367 5.892 52.8 C 5.325 51.933 4.742 51.267 4.142 50.8 C 3.575 50.333 2.975 49.983 2.342 49.75 C 2.942 49.45 3.509 49.1 4.042 48.7 C 4.809 48.133 5.409 47.483 5.842 46.75 C 6.175 46.117 6.559 45.1 6.992 43.7 C 7.392 42.467 7.592 41.05 7.592 39.45 L 7.792 28.05 L 8.242 25.9 C 8.475 25.2 8.859 24.633 9.392 24.2 C 9.892 23.833 10.592 23.65 11.492 23.65 L 14.392 23.65 Z" style="fill: rgb(175, 110, 36);"/> + <path d="M 255.632 27.46 L 268.667 47.91 L 242.597 47.91 L 255.632 27.46 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, 1, 0.000093, 86.522967, -302.258205)" bx:shape="triangle 242.597 27.46 26.07 20.45 0.5 0 1@97f9cc39"/> + <rect x="254.391" y="-239.419" width="4.82" height="17.271" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, 86.522728, -302.258331)"/> + <rect x="254.367" y="-203.504" width="4.82" height="17.271" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, 86.522728, -302.258331)"/> + <rect x="254.367" y="-168.53" width="4.82" height="17.271" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, 86.522728, -302.258331)"/> + <rect x="254.391" y="-243.818" width="4.82" height="17.687" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, -21.921179, -302.35498)"/> + <rect x="254.367" y="-208.398" width="4.82" height="17.687" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, -21.921179, -302.35498)"/> + <rect x="254.367" y="-172.584" width="4.82" height="17.687" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, -21.921179, -302.35498)"/> + <text id="text-7" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 9.73113px; font-style: italic; white-space: pre;" transform="matrix(-2.353923, 0, 0, -2.767724, 945.975708, -228.376572)" x="263.861" y="-70.535">WeakRef object</text> + <path d="M 108.749 -57.365 L 108.749 -61.015 L 104.849 -61.015 C 102.649 -61.015 101.015 -60.281 99.949 -58.815 C 98.882 -57.348 98.349 -55.515 98.349 -53.315 L 97.649 -39.565 C 97.482 -38.365 97.165 -37.315 96.699 -36.415 C 96.265 -35.448 95.682 -34.631 94.949 -33.965 C 94.649 -33.631 94.265 -33.398 93.799 -33.265 C 93.532 -33.131 93.082 -32.998 92.449 -32.865 L 92.449 -29.415 C 93.182 -29.281 93.632 -29.181 93.799 -29.115 C 94.265 -28.981 94.649 -28.798 94.949 -28.565 C 95.682 -28.031 96.249 -27.331 96.649 -26.465 C 97.715 -24.065 98.249 -21.698 98.249 -19.365 L 98.249 -8.915 C 98.249 -6.515 98.849 -4.598 100.049 -3.165 C 101.249 -1.731 102.799 -1.015 104.699 -1.015 L 108.749 -1.015 L 108.749 -4.765 L 105.849 -4.765 C 105.082 -4.765 104.449 -4.965 103.949 -5.365 C 103.415 -5.831 103.032 -6.381 102.799 -7.015 L 102.249 -9.265 L 102.099 -11.565 L 101.949 -21.215 C 101.949 -22.715 101.765 -24.048 101.399 -25.215 C 100.965 -26.648 100.582 -27.648 100.249 -28.215 C 99.682 -29.081 99.099 -29.748 98.499 -30.215 C 97.932 -30.681 97.332 -31.031 96.699 -31.265 C 97.299 -31.565 97.865 -31.915 98.399 -32.315 C 99.165 -32.881 99.765 -33.531 100.199 -34.265 C 100.532 -34.898 100.915 -35.915 101.349 -37.315 C 101.749 -38.548 101.949 -39.965 101.949 -41.565 L 102.149 -52.965 L 102.599 -55.115 C 102.832 -55.815 103.215 -56.381 103.749 -56.815 C 104.249 -57.181 104.949 -57.365 105.849 -57.365 L 108.749 -57.365 Z" style="fill: rgb(175, 110, 36); transform-box: fill-box; transform-origin: 50% 50%;" transform="matrix(-1, 0, 0, -1, -0.000004, -0.000028)"/> + <path d="M 15.325 -57.155 L 15.325 -60.805 L 11.425 -60.805 C 9.225 -60.805 7.591 -60.072 6.525 -58.605 C 5.458 -57.139 4.925 -55.305 4.925 -53.105 L 4.225 -39.355 C 4.058 -38.155 3.741 -37.105 3.275 -36.205 C 2.841 -35.239 2.258 -34.422 1.525 -33.755 C 1.225 -33.422 0.841 -33.189 0.375 -33.055 C 0.108 -32.922 -0.342 -32.789 -0.975 -32.655 L -0.975 -29.205 C -0.242 -29.072 0.208 -28.972 0.375 -28.905 C 0.841 -28.772 1.225 -28.589 1.525 -28.355 C 2.258 -27.822 2.825 -27.122 3.225 -26.255 C 4.291 -23.855 4.825 -21.489 4.825 -19.155 L 4.825 -8.705 C 4.825 -6.305 5.425 -4.389 6.625 -2.955 C 7.825 -1.522 9.375 -0.805 11.275 -0.805 L 15.325 -0.805 L 15.325 -4.555 L 12.425 -4.555 C 11.658 -4.555 11.025 -4.755 10.525 -5.155 C 9.991 -5.622 9.608 -6.172 9.375 -6.805 L 8.825 -9.055 L 8.675 -11.355 L 8.525 -21.005 C 8.525 -22.505 8.341 -23.839 7.975 -25.005 C 7.541 -26.439 7.158 -27.439 6.825 -28.005 C 6.258 -28.872 5.675 -29.539 5.075 -30.005 C 4.508 -30.472 3.908 -30.822 3.275 -31.055 C 3.875 -31.355 4.441 -31.705 4.975 -32.105 C 5.741 -32.672 6.341 -33.322 6.775 -34.055 C 7.108 -34.689 7.491 -35.705 7.925 -37.105 C 8.325 -38.339 8.525 -39.755 8.525 -41.355 L 8.725 -52.755 L 9.175 -54.905 C 9.408 -55.605 9.791 -56.172 10.325 -56.605 C 10.825 -56.972 11.525 -57.155 12.425 -57.155 L 15.325 -57.155 Z" style="fill: rgb(175, 110, 36);"/> + </g> + </g> + <text id="text-9" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 4.89804px; font-weight: 700; line-height: 19.9484px; white-space: pre;" transform="matrix(1.374145, 0, 0, 1.314286, 83.283821, 89.215279)" x="263.861" y="-70.535">undefined</text> + <text id="text-10" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 4.89804px; font-weight: 700; line-height: 19.9484px; white-space: pre;" transform="matrix(1.374145, 0, 0, 1.314286, 82.957924, 122.90126)" x="263.861" y="-70.535">undefined</text> + </g> + <g transform="matrix(0.3355039954185486, 0, 0, 0.3355039954185486, 67.31304168701172, -387.90008544921875)" style=""> + <g transform="matrix(0.178489, 0, 0, 0.175502, 747.077209, 776.317017)" style=""> + <path d="M 267.184 437.8 C 267.184 466.3 290.384 489.4 318.784 489.4 L 704.984 489.4 C 733.484 489.4 756.584 466.2 756.584 437.8 L 756.584 51.6 C 756.584 23.1 733.384 0 704.984 0 L 318.784 0 C 290.284 0 267.184 23.2 267.184 51.6 C 267.184 51.6 267.184 437.8 267.184 437.8 Z M 704.984 464.9 L 318.784 464.9 C 303.884 464.9 291.684 452.7 291.684 437.8 L 291.684 373.3 L 384.484 280.5 L 463.784 359.8 C 468.584 364.6 476.284 364.6 481.084 359.8 L 624.284 216.6 L 732.084 324.4 L 732.084 437.8 C 732.084 452.7 719.884 464.9 704.984 464.9 Z M 318.784 24.5 L 704.984 24.5 C 719.884 24.5 732.084 36.7 732.084 51.6 L 732.084 289.7 L 632.884 190.6 C 628.084 185.8 620.384 185.8 615.584 190.6 L 472.384 333.8 L 393.084 254.5 C 388.284 249.7 380.584 249.7 375.784 254.5 L 291.684 338.6 L 291.684 51.6 C 291.684 36.7 303.884 24.5 318.784 24.5 Z" style="fill: rgb(175, 110, 36);"/> + <path d="M 418.884 196.1 C 453.284 196.1 481.184 168.1 481.184 133.8 C 481.184 99.5 453.184 71.5 418.884 71.5 C 384.584 71.5 356.584 99.5 356.584 133.8 C 356.584 168.1 384.484 196.1 418.884 196.1 Z M 418.884 96 C 439.784 96 456.684 113 456.684 133.8 C 456.684 154.6 439.684 171.6 418.884 171.6 C 398.084 171.6 381.084 154.6 381.084 133.8 C 381.084 113 397.984 96 418.884 96 Z" style="fill: rgb(175, 110, 36);"/> + </g> + </g> + <path d="M -161.945 -20.71 L -152.75 -5.287 L -171.141 -5.287 L -161.945 -20.71 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, -1, -0.00008700000762473792, 281.66448974609375, -149.3444061279297)" bx:shape="triangle -171.141 -20.71 18.391 15.423 0.5 0 1@d86e5ee8"/> + <rect x="179.461" y="-180.566" width="3.4" height="13.026" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 323.07061767578125, 192.94052124023438)"/> + <rect x="179.444" y="-153.479" width="3.4" height="13.026" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 323.07061767578125, 192.94052124023438)"/> + <rect x="179.444" y="-127.103" width="3.4" height="13.026" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 323.07061767578125, 192.94052124023438)"/> + <rect x="179.461" y="-183.883" width="3.4" height="13.339" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 404.8572998046875, 193.0087127685547)"/> + <rect x="179.444" y="-157.171" width="3.4" height="13.339" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 404.8572998046875, 193.0087127685547)"/> + <rect x="179.444" y="-130.16" width="3.4" height="13.339" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 404.8572998046875, 193.0087127685547)"/> + <text id="text-8" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 9.73113px; font-style: italic; white-space: pre;" transform="matrix(1.775291919708252, 0, 0, 1.9524970054626465, -325.1152648925781, 140.8204803466797)" x="263.861" y="-70.535">WeakRef object</text> + <path d="M 318.601 -16.998 L 318.601 -19.573 L 315.66 -19.573 C 314.001 -19.573 312.769 -19.055 311.964 -18.021 C 311.16 -16.986 310.758 -15.693 310.758 -14.141 L 310.23 -4.441 C 310.104 -3.594 309.865 -2.853 309.513 -2.218 C 309.186 -1.536 308.746 -0.96 308.193 -0.49 C 307.967 -0.255 307.678 -0.09 307.326 0.004 C 307.125 0.098 306.786 0.192 306.308 0.286 L 306.308 2.72 C 306.861 2.814 307.2 2.884 307.326 2.931 C 307.678 3.025 307.967 3.155 308.193 3.319 C 308.746 3.696 309.174 4.189 309.475 4.801 C 310.28 6.494 310.682 8.163 310.682 9.81 L 310.682 17.181 C 310.682 18.875 311.135 20.227 312.04 21.238 C 312.945 22.249 314.114 22.755 315.547 22.755 L 318.601 22.755 L 318.601 20.109 L 316.414 20.109 C 315.836 20.109 315.358 19.968 314.981 19.686 C 314.579 19.357 314.29 18.969 314.114 18.522 L 313.699 16.935 L 313.586 15.312 L 313.473 8.504 C 313.473 7.446 313.334 6.506 313.058 5.683 C 312.731 4.671 312.442 3.966 312.191 3.566 C 311.763 2.955 311.323 2.485 310.871 2.155 C 310.443 1.826 309.991 1.579 309.513 1.415 C 309.966 1.203 310.393 0.956 310.795 0.674 C 311.373 0.274 311.826 -0.184 312.153 -0.702 C 312.404 -1.148 312.693 -1.866 313.02 -2.853 C 313.322 -3.723 313.473 -4.723 313.473 -5.852 L 313.623 -13.894 L 313.963 -15.41 C 314.139 -15.904 314.428 -16.304 314.83 -16.61 C 315.207 -16.868 315.735 -16.998 316.414 -16.998 L 318.601 -16.998 Z" style="fill: rgb(175, 110, 36);"/> + <path d="M 389.058 -17.145 L 389.058 -19.72 L 386.117 -19.72 C 384.458 -19.72 383.226 -19.203 382.421 -18.168 C 381.617 -17.134 381.215 -15.84 381.215 -14.288 L 380.687 -4.588 C 380.561 -3.742 380.322 -3.001 379.97 -2.366 C 379.644 -1.684 379.204 -1.108 378.651 -0.638 C 378.424 -0.403 378.135 -0.238 377.783 -0.144 C 377.582 -0.05 377.243 0.044 376.765 0.138 L 376.765 2.572 C 377.318 2.666 377.658 2.737 377.783 2.784 C 378.135 2.878 378.424 3.007 378.651 3.172 C 379.204 3.548 379.631 4.042 379.933 4.653 C 380.737 6.346 381.139 8.016 381.139 9.662 L 381.139 17.034 C 381.139 18.727 381.592 20.079 382.497 21.09 C 383.402 22.101 384.571 22.607 386.004 22.607 L 389.058 22.607 L 389.058 19.961 L 386.871 19.961 C 386.293 19.961 385.815 19.82 385.438 19.538 C 385.036 19.209 384.747 18.821 384.571 18.374 L 384.156 16.787 L 384.043 15.164 L 383.93 8.357 C 383.93 7.299 383.792 6.358 383.515 5.535 C 383.188 4.524 382.899 3.818 382.648 3.419 C 382.22 2.807 381.78 2.337 381.328 2.008 C 380.901 1.679 380.448 1.432 379.97 1.267 C 380.423 1.055 380.85 0.808 381.253 0.526 C 381.831 0.127 382.283 -0.332 382.61 -0.849 C 382.861 -1.296 383.151 -2.013 383.477 -3.001 C 383.779 -3.871 383.93 -4.87 383.93 -5.999 L 384.081 -14.041 L 384.42 -15.558 C 384.596 -16.052 384.885 -16.452 385.287 -16.757 C 385.664 -17.016 386.192 -17.145 386.871 -17.145 L 389.058 -17.145 Z" style="fill: rgb(175, 110, 36); transform-origin: 382.912px 1.4435px;" transform="matrix(-1, 0, 0, -1, 0, -0.000015020371)"/> +</svg> \ No newline at end of file diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-05.svg b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-05.svg new file mode 100644 index 000000000..e738f8e7e --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-05.svg @@ -0,0 +1,103 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg viewBox="-56.888 -599965.495 896063.875 604140.645" width="420.466px" height="300.516px" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com"> + <defs> + <style bx:fonts="Open Sans">@import url(https://fonts.googleapis.com/css2?family=Open+Sans%3Aital%2Cwght%400%2C300%3B0%2C400%3B0%2C500%3B0%2C600%3B0%2C700%3B0%2C800%3B1%2C300%3B1%2C400%3B1%2C500%3B1%2C600%3B1%2C700%3B1%2C800&display=swap);</style> + </defs> + <g id="memory-user-john-admin.svg" transform="matrix(3405.68798828125, 0, 0, 3405.68798828125, -789862.25, -296077.03124999994)" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1" style=""> + <path id="path-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.068 -87.403 L 333.494 -87.403 L 333.494 -57.762 L 234.068 -57.762 L 234.068 -87.403 Z" style=""/> + <path id="path-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.651 -87.403 L 492.549 -87.403 L 492.549 -57.762 L 336.651 -57.762 L 336.651 -87.403 Z" style=""/> + <path id="path-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.068 -53.711 L 333.494 -53.711 L 333.494 -24.07 L 234.068 -24.07 L 234.068 -53.711 Z" style=""/> + <path id="path-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.651 -53.711 L 492.783 -53.711 L 492.783 -24.07 L 336.651 -24.07 L 336.651 -53.711 Z" style=""/> + <g transform="matrix(1, 0, 0, 1, 0, 42.135864)"> + <path id="path-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.189 -20.462 L 333.615 -20.462 L 333.615 9.179 L 234.189 9.179 L 234.189 -20.462 Z" style=""/> + <path id="path-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.791 -20.462 L 493.057 -20.462 L 493.057 9.179 L 336.791 9.179 L 336.791 -20.462 Z" style=""/> + <path id="path-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 234.068 13.088 L 333.494 13.088 L 333.494 43.59 L 234.068 43.59 L 234.068 13.088 Z" style=""/> + <path id="path-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M 336.651 13.088 L 493.054 13.088 L 493.054 43.59 L 336.651 43.59 L 336.651 13.088 Z" style=""/> + <text id="text-4" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; white-space: pre;" transform="matrix(1.054877, 0, 0, 1.142641, -39.02475, 80.086914)" x="263.861" y="-70.535">image-02.jpg</text> + <text id="text-5" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; white-space: pre;" transform="matrix(1.054877, 0, 0, 1.142641, -39.468063, 113.637062)" x="263.861" y="-70.535">image-03.jpg</text> + </g> + <text id="<global>" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 11.5772px; font-weight: 600; line-height: 19.9484px; white-space: pre;" transform="matrix(1.374145, 0, 0, 1.314286, -102.41082, 25.040792)" x="263.861" y="-70.535">key</text> + <text id="text-2" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 11.5772px; font-weight: 600; line-height: 19.9484px; white-space: pre;" transform="matrix(1.374145, 0, 0, 1.314286, 13.577469, 25.203112)" x="263.861" y="-70.535">value</text> + <text id="text-3" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 14px; white-space: pre;" transform="matrix(1.071654, 0, 0, 1.142641, -43.57296, 46.390514)" x="263.861" y="-70.535">image-01.jpg</text> + <g transform="matrix(0.000041, -1, 1.521843, 0.000133, 289.017426, 2.750529)" style="transform-origin: 105.999px -35.503px;"> + <path d="M 105.537 7.964 L 110.918 13.895 L 100.155 13.895 L 105.537 7.964 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(1, 0, 0, -1, 0, 0)" bx:shape="triangle 100.155 7.964 10.763 5.931 0.5 0 1@87a4267a"/> + <g> + <rect x="105.024" y="-69.436" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-59.02" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-48.877" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <g transform="matrix(1, 0, 0, 1, 10.348016, -60.719521)"/> + <g transform="matrix(1, 0, 0, 1, 10.356147, 0.41661)"/> + <g transform="matrix(1, 0, 0, 1, 10.161898, 52.045895)"/> + <g transform="matrix(1, 0, 0, 1, -23.594774, -60.719116)"> + <rect x="79.597" y="-80.351" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.587" y="-69.935" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.587" y="-59.792" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.561" y="-50.176" width="1.99" height="5.129" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.551" y="-39.904" width="1.99" height="5.129" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.551" y="-29.517" width="1.99" height="5.129" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + <g transform="matrix(1, 0, 0, 1, -23.586773, 0.417928)"> + <rect x="79.597" y="-80.351" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.587" y="-69.935" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.587" y="-59.792" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.561" y="-50.176" width="1.99" height="5.129" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.551" y="-39.904" width="1.99" height="5.129" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + <g transform="matrix(1, 0, 0, 1, -23.780775, 52.046886)"> + <rect x="79.798" y="-80.298" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.587" y="-69.935" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.587" y="-59.792" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.561" y="-50.176" width="1.99" height="5.129" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.551" y="-39.904" width="1.99" height="5.129" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="79.551" y="-29.517" width="1.99" height="5.129" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + </g> + <g transform="matrix(1, 0, 0, 1.024052, -0.03572, 31.45084)" style=""> + <rect x="105.024" y="-69.051" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-59.02" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + <rect x="105.014" y="-48.877" width="1.99" height="5.009" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);"/> + </g> + </g> + <text id="text-6" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 9.73113px; font-style: italic; white-space: pre;" transform="matrix(1.038937, 0, 0, 1.142641, 69.710487, 42.749538)" x="263.861" y="-70.535">WeakRef object</text> + <g transform="matrix(0.441364, 0, 0, 0.412845, 420.761139, -59.372555)" style=""> + <g> + <path d=" M 58.15 23.65 L 58.15 20 54.25 20 Q 50.95 20 49.35 22.2 47.75 24.4 47.75 27.7 L 47.05 41.45 Q 46.8 43.25 46.1 44.6 45.45 46.05 44.35 47.05 43.9 47.55 43.2 47.75 42.8 47.95 41.85 48.15 L 41.85 51.6 Q 42.95 51.8 43.2 51.9 43.9 52.1 44.35 52.45 45.45 53.25 46.05 54.55 47.65 58.15 47.65 61.65 L 47.65 72.1 Q 47.65 75.7 49.45 77.85 51.25 80 54.1 80 L 58.15 80 58.15 76.25 55.25 76.25 Q 54.1 76.25 53.35 75.65 52.55 74.95 52.2 74 L 51.65 71.75 51.5 69.45 51.35 59.8 Q 51.35 57.55 50.8 55.8 50.15 53.65 49.65 52.8 48.8 51.5 47.9 50.8 47.05 50.1 46.1 49.75 47 49.3 47.8 48.7 48.95 47.85 49.6 46.75 50.1 45.8 50.75 43.7 51.35 41.85 51.35 39.45 L 51.55 28.05 52 25.9 Q 52.35 24.85 53.15 24.2 53.9 23.65 55.25 23.65 L 58.15 23.65 Z" style="fill: rgb(175, 110, 36);"/> + </g> + </g> + <g transform="matrix(-0.441364, 0, 0, -0.412845, 414.750092, -88.816727)" style="transform-origin: 50px 50px;"> + <g> + <path d="M 14.392 23.65 L 14.392 20 L 10.492 20 C 8.292 20 6.659 20.733 5.592 22.2 C 4.525 23.667 3.992 25.5 3.992 27.7 L 3.292 41.45 C 3.125 42.65 2.809 43.7 2.342 44.6 C 1.909 45.567 1.325 46.383 0.592 47.05 C 0.292 47.383 -0.091 47.617 -0.558 47.75 C -0.825 47.883 -1.275 48.017 -1.908 48.15 L -1.908 51.6 C -1.175 51.733 -0.725 51.833 -0.558 51.9 C -0.091 52.033 0.292 52.217 0.592 52.45 C 1.325 52.983 1.892 53.683 2.292 54.55 C 3.359 56.95 3.892 59.317 3.892 61.65 L 3.892 72.1 C 3.892 74.5 4.492 76.417 5.692 77.85 C 6.892 79.283 8.442 80 10.342 80 L 14.392 80 L 14.392 76.25 L 11.492 76.25 C 10.725 76.25 10.092 76.05 9.592 75.65 C 9.059 75.183 8.675 74.633 8.442 74 L 7.892 71.75 L 7.742 69.45 L 7.592 59.8 C 7.592 58.3 7.409 56.967 7.042 55.8 C 6.609 54.367 6.225 53.367 5.892 52.8 C 5.325 51.933 4.742 51.267 4.142 50.8 C 3.575 50.333 2.975 49.983 2.342 49.75 C 2.942 49.45 3.509 49.1 4.042 48.7 C 4.809 48.133 5.409 47.483 5.842 46.75 C 6.175 46.117 6.559 45.1 6.992 43.7 C 7.392 42.467 7.592 41.05 7.592 39.45 L 7.792 28.05 L 8.242 25.9 C 8.475 25.2 8.859 24.633 9.392 24.2 C 9.892 23.833 10.592 23.65 11.492 23.65 L 14.392 23.65 Z" style="fill: rgb(175, 110, 36);"/> + <path d="M 255.632 27.46 L 268.667 47.91 L 242.597 47.91 L 255.632 27.46 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, 1, 0.000093, 86.522967, -404.320629)" bx:shape="triangle 242.597 27.46 26.07 20.45 0.5 0 1@97f9cc39"/> + <rect x="254.391" y="-239.419" width="4.82" height="17.271" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, 86.522728, -404.32132)"/> + <rect x="254.367" y="-203.504" width="4.82" height="17.271" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, 86.522728, -404.32132)"/> + <rect x="254.367" y="-168.53" width="4.82" height="17.271" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, 86.522728, -404.32132)"/> + <rect x="254.391" y="-243.818" width="4.82" height="17.687" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, -21.921179, -404.417969)"/> + <rect x="254.367" y="-208.398" width="4.82" height="17.687" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, -21.921179, -404.417969)"/> + <rect x="254.367" y="-172.584" width="4.82" height="17.687" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(-0.000038, 1, -1, -0.000093, -21.921179, -404.417969)"/> + <text id="text-7" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 9.73113px; font-style: italic; white-space: pre;" transform="matrix(-2.353923, 0, 0, -2.767724, 945.975708, -330.43927)" x="263.861" y="-70.535">WeakRef object</text> + <path d="M 108.749 -159.455 L 108.749 -163.105 L 104.849 -163.105 C 102.649 -163.105 101.015 -162.371 99.949 -160.905 C 98.882 -159.438 98.349 -157.605 98.349 -155.405 L 97.649 -141.655 C 97.482 -140.455 97.165 -139.405 96.699 -138.505 C 96.265 -137.538 95.682 -136.721 94.949 -136.055 C 94.649 -135.721 94.265 -135.488 93.799 -135.355 C 93.532 -135.221 93.082 -135.088 92.449 -134.955 L 92.449 -131.505 C 93.182 -131.371 93.632 -131.271 93.799 -131.205 C 94.265 -131.071 94.649 -130.888 94.949 -130.655 C 95.682 -130.121 96.249 -129.421 96.649 -128.555 C 97.715 -126.155 98.249 -123.788 98.249 -121.455 L 98.249 -111.005 C 98.249 -108.605 98.849 -106.688 100.049 -105.255 C 101.249 -103.821 102.799 -103.105 104.699 -103.105 L 108.749 -103.105 L 108.749 -106.855 L 105.849 -106.855 C 105.082 -106.855 104.449 -107.055 103.949 -107.455 C 103.415 -107.921 103.032 -108.471 102.799 -109.105 L 102.249 -111.355 L 102.099 -113.655 L 101.949 -123.305 C 101.949 -124.805 101.765 -126.138 101.399 -127.305 C 100.965 -128.738 100.582 -129.738 100.249 -130.305 C 99.682 -131.171 99.099 -131.838 98.499 -132.305 C 97.932 -132.771 97.332 -133.121 96.699 -133.355 C 97.299 -133.655 97.865 -134.005 98.399 -134.405 C 99.165 -134.971 99.765 -135.621 100.199 -136.355 C 100.532 -136.988 100.915 -138.005 101.349 -139.405 C 101.749 -140.638 101.949 -142.055 101.949 -143.655 L 102.149 -155.055 L 102.599 -157.205 C 102.832 -157.905 103.215 -158.471 103.749 -158.905 C 104.249 -159.271 104.949 -159.455 105.849 -159.455 L 108.749 -159.455 Z" style="fill: rgb(175, 110, 36); transform-box: fill-box; transform-origin: 50% 50%;" transform="matrix(-1, 0, 0, -1, -0.000041, -0.000044)"/> + <path d="M 15.325 -159.251 L 15.325 -162.901 L 11.425 -162.901 C 9.225 -162.901 7.591 -162.168 6.525 -160.701 C 5.458 -159.235 4.925 -157.401 4.925 -155.201 L 4.225 -141.451 C 4.058 -140.251 3.741 -139.201 3.275 -138.301 C 2.841 -137.335 2.258 -136.518 1.525 -135.851 C 1.225 -135.518 0.841 -135.285 0.375 -135.151 C 0.108 -135.018 -0.342 -134.885 -0.975 -134.751 L -0.975 -131.301 C -0.242 -131.168 0.208 -131.068 0.375 -131.001 C 0.841 -130.868 1.225 -130.685 1.525 -130.451 C 2.258 -129.918 2.825 -129.218 3.225 -128.351 C 4.291 -125.951 4.825 -123.585 4.825 -121.251 L 4.825 -110.801 C 4.825 -108.401 5.425 -106.485 6.625 -105.051 C 7.825 -103.618 9.375 -102.901 11.275 -102.901 L 15.325 -102.901 L 15.325 -106.651 L 12.425 -106.651 C 11.658 -106.651 11.025 -106.851 10.525 -107.251 C 9.991 -107.718 9.608 -108.268 9.375 -108.901 L 8.825 -111.151 L 8.675 -113.451 L 8.525 -123.101 C 8.525 -124.601 8.341 -125.935 7.975 -127.101 C 7.541 -128.535 7.158 -129.535 6.825 -130.101 C 6.258 -130.968 5.675 -131.635 5.075 -132.101 C 4.508 -132.568 3.908 -132.918 3.275 -133.151 C 3.875 -133.451 4.441 -133.801 4.975 -134.201 C 5.741 -134.768 6.341 -135.418 6.775 -136.151 C 7.108 -136.785 7.491 -137.801 7.925 -139.201 C 8.325 -140.435 8.525 -141.851 8.525 -143.451 L 8.725 -154.851 L 9.175 -157.001 C 9.408 -157.701 9.791 -158.268 10.325 -158.701 C 10.825 -159.068 11.525 -159.251 12.425 -159.251 L 15.325 -159.251 Z" style="fill: rgb(175, 110, 36);"/> + </g> + </g> + <text id="text-9" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 4.89804px; font-weight: 700; line-height: 19.9484px; white-space: pre;" transform="matrix(1.374145, 0, 0, 1.314286, 83.283821, 131.351151)" x="263.861" y="-70.535">undefined</text> + <text id="text-10" style="fill: rgb(175, 110, 36); font-family: 'Open Sans'; font-size: 4.89804px; font-weight: 700; line-height: 19.9484px; white-space: pre;" transform="matrix(1.374145, 0, 0, 1.314286, 82.957924, 165.037125)" x="263.861" y="-70.535">undefined</text> + <text id="text-11" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 9.73113px; font-style: italic; white-space: pre;" transform="matrix(1.038937, 0, 0, 1.142641, -18.813889, 90.723404)" x="263.861" y="-70.535">Deleted by FinalizationRegistry cleanup callback</text> + </g> + <g transform="matrix(668.68603515625, 0, 0, 668.68603515625, 227309.90625, -975180.6875)" style=""> + <g transform="matrix(0.178489, 0, 0, 0.175502, 747.077209, 776.317017)" style=""> + <path d="M 267.184 437.8 C 267.184 466.3 290.384 489.4 318.784 489.4 L 704.984 489.4 C 733.484 489.4 756.584 466.2 756.584 437.8 L 756.584 51.6 C 756.584 23.1 733.384 0 704.984 0 L 318.784 0 C 290.284 0 267.184 23.2 267.184 51.6 C 267.184 51.6 267.184 437.8 267.184 437.8 Z M 704.984 464.9 L 318.784 464.9 C 303.884 464.9 291.684 452.7 291.684 437.8 L 291.684 373.3 L 384.484 280.5 L 463.784 359.8 C 468.584 364.6 476.284 364.6 481.084 359.8 L 624.284 216.6 L 732.084 324.4 L 732.084 437.8 C 732.084 452.7 719.884 464.9 704.984 464.9 Z M 318.784 24.5 L 704.984 24.5 C 719.884 24.5 732.084 36.7 732.084 51.6 L 732.084 289.7 L 632.884 190.6 C 628.084 185.8 620.384 185.8 615.584 190.6 L 472.384 333.8 L 393.084 254.5 C 388.284 249.7 380.584 249.7 375.784 254.5 L 291.684 338.6 L 291.684 51.6 C 291.684 36.7 303.884 24.5 318.784 24.5 Z" style="fill: rgb(175, 110, 36);"/> + <path d="M 418.884 196.1 C 453.284 196.1 481.184 168.1 481.184 133.8 C 481.184 99.5 453.184 71.5 418.884 71.5 C 384.584 71.5 356.584 99.5 356.584 133.8 C 356.584 168.1 384.484 196.1 418.884 196.1 Z M 418.884 96 C 439.784 96 456.684 113 456.684 133.8 C 456.684 154.6 439.684 171.6 418.884 171.6 C 398.084 171.6 381.084 154.6 381.084 133.8 C 381.084 113 397.984 96 418.884 96 Z" style="fill: rgb(175, 110, 36);"/> + </g> + </g> + <path d="M 359424.367 41276.336 L 377751.896 72015.889 L 341096.837 72015.889 L 359424.367 41276.336 Z" style="fill-rule: nonzero; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, -1, -0.00008700000762473792, 737054.1250000001, 325983.1250000001)" bx:shape="triangle 341096.837 41276.336 36655.059 30739.553 0.5 0 1@d50babe8"/> + <rect x="357681.08" y="-359881.579" width="6776.286" height="25963.08" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 737054.6250000001, 325981.3125000001)"/> + <rect x="357644.501" y="-305895.331" width="6776.286" height="25963.08" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 737054.6250000001, 325981.3125000001)"/> + <rect x="357644.501" y="-253326.04" width="6776.286" height="25963.08" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 737054.6250000001, 325981.3125000001)"/> + <rect x="357681.08" y="-366492.978" width="6776.286" height="26586.395" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 900061.8125000001, 326117.2187500001)"/> + <rect x="357644.501" y="-313254.567" width="6776.286" height="26586.395" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 900061.8125000001, 326117.2187500001)"/> + <rect x="357644.501" y="-259418.129" width="6776.286" height="26586.395" style="paint-order: stroke; stroke: rgb(53, 81, 138); stroke-width: 0px; fill: rgb(172, 67, 67);" transform="matrix(0.00004100000296602957, -1, 1, 0.00008700000762473792, 900061.8125000001, 326117.2187500001)"/> + <text id="text-8" style="fill: rgb(172, 67, 67); font-family: 'Open Sans'; font-size: 9.73113px; font-style: italic; white-space: pre;" transform="matrix(3538.295166015625, 0, 0, 3891.478759765625, -554830.25, 222101.96875)" x="263.861" y="-70.535">WeakRef object</text> + <path d="M 728146.338 -92443.632 L 728146.338 -97574.712 L 722284.793 -97574.712 C 718978.199 -97574.712 716523.314 -96542.53 714919.193 -94480.927 C 713316.016 -92418.404 712514.341 -89841.53 712514.341 -86748.938 L 711462.052 -67415.4 C 711210.096 -65727.244 710734.892 -64250.108 710033.283 -62985.482 C 709382.304 -61626.233 708504.597 -60478.003 707402.151 -59541.042 C 706951.752 -59072.499 706375.587 -58743.915 705673.655 -58556.036 C 705273.289 -58368.007 704596.909 -58181.644 703645.158 -57993.168 L 703645.158 -53141.856 C 704747.605 -52953.853 705423.812 -52816.007 705673.655 -52721.682 C 706375.587 -52533.653 706951.752 -52275.956 707402.151 -51947.819 C 708504.597 -51196.774 709359.189 -50215.843 709957.252 -48995.062 C 711561.373 -45621.509 712363.794 -42293.888 712363.794 -39012.547 L 712363.794 -24320.941 C 712363.794 -20946.169 713265.063 -18250.364 715069.292 -16236.209 C 716874.118 -14219.916 719201.895 -13211.347 722059.73 -13211.347 L 728146.338 -13211.347 L 728146.338 -18485.095 L 723786.735 -18485.095 C 722635.597 -18485.095 721683.847 -18767.275 720932.205 -19327.881 C 720129.784 -19984.452 719554.812 -20757.843 719201.895 -21648.25 L 718377.253 -24810.511 L 718150.525 -28045.597 L 717925.463 -41615.096 C 717925.463 -43722.656 717648.726 -45597.921 717099.007 -47236.193 C 716447.133 -49254.872 715870.968 -50660.078 715370.511 -51456.161 C 714517.162 -52674.979 713641.12 -53612.089 712740.149 -54268.661 C 711885.582 -54925.978 710986.4 -55416.741 710033.283 -55743.536 C 710935.472 -56166.271 711786.261 -56657.059 712587.937 -57219.753 C 713740.267 -58016.755 714644.122 -58930.278 715294.33 -59961.987 C 715794.787 -60852.37 716371.101 -62282.655 717022.975 -64250.108 C 717625.611 -65984.196 717925.463 -67976.901 717925.463 -70227.453 L 718225.811 -86255.615 L 718902.938 -89277.444 C 719252.823 -90261.705 719829.908 -91059.9 720629.445 -91669.769 C 721380.938 -92183.673 722434.122 -92443.632 723786.735 -92443.632 L 728146.338 -92443.632 Z" style="fill: rgb(175, 110, 36);"/> + <path d="M 868579.948 -92734.348 L 868579.948 -97866.322 L 862717.955 -97866.322 C 859411.336 -97866.322 856954.786 -96837.173 855350.69 -94775.421 C 853747.488 -92713.793 852947.926 -90134.359 852947.926 -87041.916 L 851895.661 -67707.906 C 851643.706 -66022.185 851168.054 -64545.67 850465.228 -63278.459 C 849816.809 -61920.428 848939.872 -60773.094 847838.32 -59836.43 C 847385.039 -59366.993 846807.506 -59039.453 846107.264 -58850.977 C 845705.979 -58662.948 845032.333 -58476.734 844077.55 -58288.705 L 844077.55 -53438.165 C 845180.295 -53250.136 845856.974 -53108.363 846107.264 -53014.659 C 846807.506 -52828.296 847385.039 -52570.897 847838.32 -52242.462 C 848939.872 -51491.715 849788.996 -50508.374 850393.421 -49291.196 C 851994.957 -45916.599 852795.738 -42588.531 852795.738 -39308.084 L 852795.738 -24614.217 C 852795.738 -21238.7 853699.12 -18545.753 855503.051 -16529.485 C 857306.038 -14515.33 859635.504 -13506.885 862491.674 -13506.885 L 868579.948 -13506.885 L 868579.948 -18781.23 L 864220.319 -18781.23 C 863067.542 -18781.23 862114.424 -19061.47 861364.149 -19624.164 C 860562.474 -20278.623 859988.421 -21052.486 859635.504 -21943.639 L 858809.496 -25105.899 L 858584.135 -28341.01 L 858357.407 -41908.844 C 858357.407 -44017.15 858082.336 -45890.452 857532.617 -47532.477 C 856880.42 -49546.483 856302.887 -50953.801 855803.201 -51749.114 C 854950.299 -52969.622 854074.73 -53906.708 853171.77 -54561.937 C 852320.832 -55217.589 851420.01 -55710.191 850465.228 -56038.154 C 851368.61 -56462.579 852219.573 -56952.447 853024.106 -57514.843 C 854174.772 -58310.926 855075.594 -59225.667 855727.194 -60256.63 C 856228.546 -61147.784 856806.351 -62576.08 857456.585 -64545.67 C 858057.531 -66279.609 858357.407 -68270.177 858357.407 -70520.281 L 858660.316 -86548.145 L 859335.33 -89573.007 C 859685.662 -90557.094 860263.045 -91354.096 861063.055 -91962.3 C 861814.548 -92479.061 862866.066 -92734.348 864220.319 -92734.348 L 868579.948 -92734.348 Z" style="fill: rgb(175, 110, 36); transform-origin: 856323px -55686.3px;" transform="matrix(-1, 0, 0, -1, 0.87500000055, -0.046875000444)"/> + <rect x="3810.427" y="-226324.344" width="889802.927" height="226474.243" style="stroke: rgb(0, 0, 0); stroke-opacity: 0; fill: rgb(172, 67, 67); opacity: 0.76;"/> +</svg> \ No newline at end of file diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-01.png b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-01.png new file mode 100644 index 000000000..fc33a023a Binary files /dev/null and b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-01.png differ diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-02.png b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-02.png new file mode 100644 index 000000000..7d8bb01e8 Binary files /dev/null and b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-02.png differ diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-03.gif b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-03.gif new file mode 100644 index 000000000..b81966dda Binary files /dev/null and b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-03.gif differ diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-04.jpg b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-04.jpg new file mode 100644 index 000000000..ba60f1e86 Binary files /dev/null and b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-04.jpg differ diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-05.gif b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-05.gif new file mode 100644 index 000000000..d34bda4d7 Binary files /dev/null and b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-05.gif differ diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-06.jpg b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-06.jpg new file mode 100644 index 000000000..b2655540f Binary files /dev/null and b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-06.jpg differ diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-07.gif b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-07.gif new file mode 100644 index 000000000..51f874518 Binary files /dev/null and b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-07.gif differ diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-08.jpg b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-08.jpg new file mode 100644 index 000000000..5f98aec14 Binary files /dev/null and b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry-demo-08.jpg differ diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/index.css b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/index.css new file mode 100644 index 000000000..e6c9e3960 --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/index.css @@ -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; +} \ No newline at end of file diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/index.html b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/index.html new file mode 100644 index 000000000..7ce52f927 --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/index.html @@ -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> diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/index.js b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/index.js new file mode 100644 index 000000000..983b34d9a --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/index.js @@ -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); diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/utils.js b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/utils.js new file mode 100644 index 000000000..f0140c116 --- /dev/null +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/weakref-finalizationregistry.view/utils.js @@ -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(), +}; diff --git a/2-ui/3-event-details/6-pointer-events/article.md b/2-ui/3-event-details/6-pointer-events/article.md index de4d8e632..ecc144712 100644 --- a/2-ui/3-event-details/6-pointer-events/article.md +++ b/2-ui/3-event-details/6-pointer-events/article.md @@ -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] ``` diff --git a/2-ui/4-forms-controls/1-form-elements/article.md b/2-ui/4-forms-controls/1-form-elements/article.md index 48c7ad1ca..08dffe5a3 100644 --- a/2-ui/4-forms-controls/1-form-elements/article.md +++ b/2-ui/4-forms-controls/1-form-elements/article.md @@ -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` در عمل باید معمولا _هر دو_ مقدار diff --git a/2-ui/4-forms-controls/3-events-change-input/article.md b/2-ui/4-forms-controls/3-events-change-input/article.md index 21298b94c..98f8a073a 100644 --- a/2-ui/4-forms-controls/3-events-change-input/article.md +++ b/2-ui/4-forms-controls/3-events-change-input/article.md @@ -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 کار میکند. diff --git a/2-ui/99-ui-misc/02-selection-range/article.md b/2-ui/99-ui-misc/02-selection-range/article.md index 93ba656a2..878c720f7 100644 --- a/2-ui/99-ui-misc/02-selection-range/article.md +++ b/2-ui/99-ui-misc/02-selection-range/article.md @@ -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` همیشه شروع خود را قبل از پایان دارند. diff --git a/2-ui/99-ui-misc/03-event-loop/article.md b/2-ui/99-ui-misc/03-event-loop/article.md index 48232753d..ce9372cbf 100644 --- a/2-ui/99-ui-misc/03-event-loop/article.md +++ b/2-ui/99-ui-misc/03-event-loop/article.md @@ -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,6 +34,7 @@ ممکن است زمانی اتفاق بیفتد که یک کار در حالی که موتور مشغول است بیاید، سپس در نوبت قرار گیرد. +<<<<<<< HEAD وظایف یک صف تشکیل می دهند که اصطلاحاً به آن "macrotask queue" (v8 term) می گویند:  @@ -37,12 +42,29 @@ به عنوان مثال، در حالی که موتور مشغول اجرای یک `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): + + + +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` محاسبه میشود. diff --git a/6-data-storage/01-cookie/article.md b/6-data-storage/01-cookie/article.md index 01c0e1fee..1b9e93414 100644 --- a/6-data-storage/01-cookie/article.md +++ b/6-data-storage/01-cookie/article.md @@ -2,17 +2,17 @@ Cookies are small strings of data that are stored directly in the browser. They are a part of the HTTP protocol, defined by the [RFC 6265](https://tools.ietf.org/html/rfc6265) specification. -Cookies are usually set by a web-server using the response `Set-Cookie` HTTP-header. Then, the browser automatically adds them to (almost) every request to the same domain using the `Cookie` HTTP-header. +Cookies are usually set by a web server using the response `Set-Cookie` HTTP header. Then, the browser automatically adds them to (almost) every request to the same domain using the `Cookie` HTTP header. One of the most widespread use cases is authentication: -1. Upon sign in, the server uses the `Set-Cookie` HTTP-header in the response to set a cookie with a unique "session identifier". -2. Next time when the request is sent to the same domain, the browser sends the cookie over the net using the `Cookie` HTTP-header. +1. Upon sign-in, the server uses the `Set-Cookie` HTTP header in the response to set a cookie with a unique "session identifier". +2. Next time the request is sent to the same domain, the browser sends the cookie over the net using the `Cookie` HTTP header. 3. So the server knows who made the request. We can also access cookies from the browser, using `document.cookie` property. -There are many tricky things about cookies and their options. In this chapter we'll cover them in detail. +There are many tricky things about cookies and their attributes. In this chapter, we'll cover them in detail. ## Reading from document.cookie @@ -31,17 +31,17 @@ alert( document.cookie ); // cookie1=value1; cookie2=value2;... ``` -The value of `document.cookie` consists of `name=value` pairs, delimited by `; `. Each one is a separate cookie. +The value of `document.cookie` consists of `name=value` pairs, delimited by `; `. Each one is a separate cookie. -To find a particular cookie, we can split `document.cookie` by `; `, and then find the right name. We can use either a regular expression or array functions to do that. +To find a particular cookie, we can split `document.cookie` by `; `, and then find the right name. We can use either a regular expression or array functions to do that. -We leave it as an exercise for the reader. Also, at the end of the chapter you'll find helper functions to manipulate cookies. +We leave it as an exercise for the reader. Also, at the end of the chapter, you'll find helper functions to manipulate cookies. ## Writing to document.cookie We can write to `document.cookie`. But it's not a data property, it's an [accessor (getter/setter)](info:property-accessors). An assignment to it is treated specially. -**A write operation to `document.cookie` updates only cookies mentioned in it, but doesn't touch other cookies.** +**A write operation to `document.cookie` updates only the cookie mentioned in it and doesn't touch other cookies.** For instance, this call sets a cookie with the name `user` and value `John`: @@ -50,12 +50,12 @@ document.cookie = "user=John"; // update only cookie named 'user' alert(document.cookie); // show all cookies ``` -If you run it, then probably you'll see multiple cookies. That's because the `document.cookie=` operation does not overwrite all cookies. It only sets the mentioned cookie `user`. +If you run it, you will likely see multiple cookies. That's because the `document.cookie=` operation does not overwrite all cookies. It only sets the mentioned cookie `user`. Technically, name and value can have any characters. To keep the valid formatting, they should be escaped using a built-in `encodeURIComponent` function: ```js run -// special characters (spaces), need encoding +// special characters (spaces) need encoding let name = "my name"; let value = "John Smith" @@ -67,29 +67,20 @@ alert(document.cookie); // ...; my%20name=John%20Smith ```warn header="Limitations" -There are few limitations: +There are a few limitations: +- You can only set/update a single cookie at a time using `document.cookie`. - The `name=value` pair, after `encodeURIComponent`, should not exceed 4KB. So we can't store anything huge in a cookie. - The total number of cookies per domain is limited to around 20+, the exact limit depends on the browser. ``` -Cookies have several options, many of them are important and should be set. +Cookies have several attributes, many of which are important and should be set. -The options are listed after `key=value`, delimited by `;`, like this: +The attributes are listed after `key=value`, delimited by `;`, like this: ```js run document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT" ``` -## path - -- **`path=/mypath`** - -The url path prefix must be absolute. It makes the cookie accessible for pages under that path. By default, it's the current path. - -If a cookie is set with `path=/admin`, it's visible at pages `/admin` and `/admin/something`, but not at `/home` or `/adminpage`. - -Usually, we should set `path` to the root: `path=/` to make the cookie accessible from all website pages. - ## domain - **`domain=site.com`** @@ -102,7 +93,7 @@ It's a safety restriction, to allow us to store sensitive data in cookies that s By default, a cookie is accessible only at the domain that set it. -Please note, by default a cookie is also not shared to a subdomain as well, such as `forum.site.com`. +Please note, by default, a cookie is not shared with a subdomain, such as `forum.site.com`. ```js // if we set a cookie at site.com website... @@ -114,7 +105,7 @@ alert(document.cookie); // no user ...But this can be changed. If we'd like to allow subdomains like `forum.site.com` to get a cookie set at `site.com`, that's possible. -For that to happen, when setting a cookie at `site.com`, we should explicitly set the `domain` option to the root domain: `domain=site.com`. Then all subdomains will see such cookie. +For that to happen, when setting a cookie at `site.com`, we should explicitly set the `domain` attribute to the root domain: `domain=site.com`. Then all subdomains will see such a cookie. For example: @@ -129,19 +120,31 @@ document.cookie = "user=John; *!*domain=site.com*/!*" alert(document.cookie); // has cookie user=John ``` -For historical reasons, `domain=.site.com` (with a dot before `site.com`) also works the same way, allowing access to the cookie from subdomains. That's an old notation and should be used if we need to support very old browsers. +```warn header="Legacy syntax" +Historically, `domain=.site.com` (with a dot before `site.com`) used to work the same way, allowing access to the cookie from subdomains. Leading dots in domain names are now ignored, but some browsers may decline to set the cookie containing such dots. +``` + +To summarize, the `domain` attribute allows to make a cookie accessible at subdomains. + +## path + +- **`path=/mypath`** + +The URL path prefix must be absolute. It makes the cookie accessible for pages under that path. By default, it's the current path. + +If a cookie is set with `path=/admin`, it's visible on pages `/admin` and `/admin/something`, but not at `/home`, `/home/admin` or `/`. -To summarize, the `domain` option allows to make a cookie accessible at subdomains. +Usually, we should set `path` to the root: `path=/` to make the cookie accessible from all website pages. If this attribute is not set the default is calculated using [this method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#path_default_value). ## expires, max-age -By default, if a cookie doesn't have one of these options, it disappears when the browser is closed. Such cookies are called "session cookies" +By default, if a cookie doesn't have one of these attributes, it disappears when the browser/tab is closed. Such cookies are called "session cookies" -To let cookies survive a browser close, we can set either the `expires` or `max-age` option. +To let cookies survive a browser close, we can set either the `expires` or `max-age` attribute. `max-Age` has precedence if both are set. - **`expires=Tue, 19 Jan 2038 03:14:07 GMT`** -The cookie expiration date defines the time, when the browser will automatically delete it. +The cookie expiration date defines the time when the browser will automatically delete it (according to the browser's time zone). The date must be exactly in this format, in the GMT timezone. We can use `date.toUTCString` to get it. For instance, we can set the cookie to expire in 1 day: @@ -178,7 +181,7 @@ The cookie should be transferred only over HTTPS. That is, cookies are domain-based, they do not distinguish between the protocols. -With this option, if a cookie is set by `https://site.com`, then it doesn't appear when the same site is accessed by HTTP, as `http://site.com`. So if a cookie has sensitive content that should never be sent over unencrypted HTTP, the `secure` flag is the right thing. +With this attribute, if a cookie is set by `https://site.com`, then it doesn't appear when the same site is accessed by HTTP, as `http://site.com`. So if a cookie has sensitive content that should never be sent over unencrypted HTTP, the `secure` flag is the right thing. ```js // assuming we're on https:// now @@ -188,49 +191,49 @@ document.cookie = "user=John; secure"; ## samesite -That's another security attribute `samesite`. It's designed to protect from so-called XSRF (cross-site request forgery) attacks. +This is another security attribute `samesite`. It's designed to protect from so-called XSRF (cross-site request forgery) attacks. To understand how it works and when it's useful, let's take a look at XSRF attacks. ### XSRF attack -Imagine, you are logged into the site `bank.com`. That is: you have an authentication cookie from that site. Your browser sends it to `bank.com` with every request, so that it recognizes you and performs all sensitive financial operations. +Imagine, you are logged into the site `bank.com`. That is: you have an authentication cookie from that site. Your browser sends it to `bank.com` with every request so that it recognizes you and performs all sensitive financial operations. Now, while browsing the web in another window, you accidentally come to another site `evil.com`. That site has JavaScript code that submits a form `<form action="https://bank.com/pay">` to `bank.com` with fields that initiate a transaction to the hacker's account. -The browser sends cookies every time you visit the site `bank.com`, even if the form was submitted from `evil.com`. So the bank recognizes you and actually performs the payment. +The browser sends cookies every time you visit the site `bank.com`, even if the form was submitted from `evil.com`. So the bank recognizes you and performs the payment.  -That's a so-called "Cross-Site Request Forgery" (in short, XSRF) attack. +This is a so-called "Cross-Site Request Forgery" (in short, XSRF) attack. -Real banks are protected from it of course. All forms generated by `bank.com` have a special field, a so-called "XSRF protection token", that an evil page can't generate or extract from a remote page. It can submit a form there, but can't get the data back. The site `bank.com` checks for such token in every form it receives. +Real banks are protected from it of course. All forms generated by `bank.com` have a special field, a so-called "XSRF protection token", that an evil page can't generate or extract from a remote page. It can submit a form there, but can't get the data back. The site `bank.com` checks for such a token in every form it receives. Such a protection takes time to implement though. We need to ensure that every form has the required token field, and we must also check all requests. -### Enter cookie samesite option +### Use cookie samesite attribute -The cookie `samesite` option provides another way to protect from such attacks, that (in theory) should not require "xsrf protection tokens". +The cookie `samesite` attribute provides another way to protect from such attacks, that (in theory) should not require "xsrf protection tokens". It has two possible values: -- **`samesite=strict` (same as `samesite` without value)** +- **`samesite=strict`** A cookie with `samesite=strict` is never sent if the user comes from outside the same site. -In other words, whether a user follows a link from their mail or submits a form from `evil.com`, or does any operation that originates from another domain, the cookie is not sent. +In other words, whether a user follows a link from their email, submits a form from `evil.com`, or does any operation that originates from another domain, the cookie is not sent. -If authentication cookies have the `samesite` option, then a XSRF attack has no chances to succeed, because a submission from `evil.com` comes without cookies. So `bank.com` will not recognize the user and will not proceed with the payment. +If authentication cookies have the `samesite=strict` attribute, then an XSRF attack has no chance of succeeding, because a submission from `evil.com` comes without cookies. So `bank.com` will not recognize the user and will not proceed with the payment. -The protection is quite reliable. Only operations that come from `bank.com` will send the `samesite` cookie, e.g. a form submission from another page at `bank.com`. +The protection is quite reliable. Only operations that come from `bank.com` will send the `samesite=strict` cookie, e.g. a form submission from another page at `bank.com`. Although, there's a small inconvenience. -When a user follows a legitimate link to `bank.com`, like from their own notes, they'll be surprised that `bank.com` does not recognize them. Indeed, `samesite=strict` cookies are not sent in that case. +When a user follows a legitimate link to `bank.com`, like from their notes, they'll be surprised that `bank.com` does not recognize them. Indeed, `samesite=strict` cookies are not sent in that case. -We could work around that by using two cookies: one for "general recognition", only for the purposes of saying: "Hello, John", and the other one for data-changing operations with `samesite=strict`. Then, a person coming from outside of the site will see a welcome, but payments must be initiated from the bank's website, for the second cookie to be sent. +We could work around that by using two cookies: one for "general recognition", only to say: "Hello, John", and the other one for data-changing operations with `samesite=strict`. Then, a person coming from outside of the site will see a welcome, but payments must be initiated from the bank's website, for the second cookie to be sent. -- **`samesite=lax`** +- **`samesite=lax` (same as `samesite` without value)** A more relaxed approach that also protects from XSRF and doesn't break the user experience. @@ -239,40 +242,40 @@ Lax mode, just like `strict`, forbids the browser to send cookies when coming fr A `samesite=lax` cookie is sent if both of these conditions are true: 1. The HTTP method is "safe" (e.g. GET, but not POST). - The full list of safe HTTP methods is in the [RFC7231 specification](https://tools.ietf.org/html/rfc7231). Basically, these are the methods that should be used for reading, but not writing the data. They must not perform any data-changing operations. Following a link is always GET, the safe method. + The full list of safe HTTP methods is in the [RFC7231 specification](https://tools.ietf.org/html/rfc7231#section-4.2.1). These are the methods that should be used for reading, but not writing the data. They must not perform any data-changing operations. Following a link is always GET, the safe method. 2. The operation performs a top-level navigation (changes URL in the browser address bar). - That's usually true, but if the navigation is performed in an `<iframe>`, then it's not top-level. Also, JavaScript methods for network requests do not perform any navigation, hence they don't fit. + This is usually true, but if the navigation is performed in an `<iframe>`, then it is not top-level. Additionally, JavaScript methods for network requests do not perform any navigation. -So, what `samesite=lax` does, is to basically allow the most common "go to URL" operation to have cookies. E.g. opening a website link from notes that satisfy these conditions. +So, what `samesite=lax` does, is to allow the most common "go to URL" operation to have cookies. E.g. opening a website link from notes that satisfy these conditions. But anything more complicated, like a network request from another site or a form submission, loses cookies. If that's fine for you, then adding `samesite=lax` will probably not break the user experience and add protection. -Overall, `samesite` is a great option. +Overall, `samesite` is a great attribute. There's a drawback: -- `samesite` is ignored (not supported) by very old browsers, year 2017 or so. +- `samesite` is ignored (not supported) by very old browsers, the year 2017 or so. **So if we solely rely on `samesite` to provide protection, then old browsers will be vulnerable.** -But we surely can use `samesite` together with other protection measures, like xsrf tokens, to add an additional layer of defence and then, in the future, when old browsers die out, we'll probably be able to drop xsrf tokens. +But we can use `samesite` together with other protection measures, like xsrf tokens, to add a layer of defence and then, in the future, when old browsers die out, we'll probably be able to drop xsrf tokens. ## httpOnly -This option has nothing to do with JavaScript, but we have to mention it for completeness. +This attribute has nothing to do with JavaScript, but we have to mention it for completeness. -The web-server uses the `Set-Cookie` header to set a cookie. Also, it may set the `httpOnly` option. +The web server uses the `Set-Cookie` header to set a cookie. Also, it may set the `httpOnly` attribute. -This option forbids any JavaScript access to the cookie. We can't see such a cookie or manipulate it using `document.cookie`. +This attribute forbids any JavaScript access to the cookie. We can't see such a cookie or manipulate it using `document.cookie`. -That's used as a precaution measure, to protect from certain attacks when a hacker injects his own JavaScript code into a page and waits for a user to visit that page. That shouldn't be possible at all, hackers should not be able to inject their code into our site, but there may be bugs that let them do it. +This is used as a precautionary measure, to protect from certain attacks when a hacker injects his own JavaScript code into a page and waits for a user to visit that page. That shouldn't be possible at all, hackers should not be able to inject their code into our site, but there may be bugs that let them do it. -Normally, if such a thing happens, and a user visits a web-page with hacker's JavaScript code, then that code executes and gains access to `document.cookie` with user cookies containing authentication information. That's bad. +Normally, if such a thing happens, and a user visits a web-page with a hacker's JavaScript code, then that code executes and gains access to `document.cookie` with user cookies containing authentication information. That's bad. But if a cookie is `httpOnly`, then `document.cookie` doesn't see it, so it is protected. @@ -303,30 +306,30 @@ Here `new RegExp` is generated dynamically, to match `; name=<value>`. Please note that a cookie value is encoded, so `getCookie` uses a built-in `decodeURIComponent` function to decode it. -### setCookie(name, value, options) +### setCookie(name, value, attributes) Sets the cookie's `name` to the given `value` with `path=/` by default (can be modified to add other defaults): ```js run -function setCookie(name, value, options = {}) { +function setCookie(name, value, attributes = {}) { - options = { + attributes = { path: '/', // add other defaults here if necessary - ...options + ...attributes }; - if (options.expires instanceof Date) { - options.expires = options.expires.toUTCString(); + if (attributes.expires instanceof Date) { + attributes.expires = attributes.expires.toUTCString(); } let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value); - for (let optionKey in options) { - updatedCookie += "; " + optionKey; - let optionValue = options[optionKey]; - if (optionValue !== true) { - updatedCookie += "=" + optionValue; + for (let attributeKey in attributes) { + updatedCookie += "; " + attributeKey; + let attributeValue = attributes[attributeKey]; + if (attributeValue !== true) { + updatedCookie += "=" + attributeValue; } } @@ -350,7 +353,7 @@ function deleteCookie(name) { ``` ```warn header="Updating or deleting must use same path and domain" -Please note: when we update or delete a cookie, we should use exactly the same path and domain options as when we set it. +Please note: when we update or delete a cookie, we should use exactly the same path and domain attributes as when we set it. ``` Together: [cookie.js](cookie.js). @@ -377,7 +380,7 @@ For instance: Third-party cookies are traditionally used for tracking and ads services, due to their nature. They are bound to the originating domain, so `ads.com` can track the same user between different sites, if they all access it. -Naturally, some people don't like being tracked, so browsers allow to disable such cookies. +Naturally, some people don't like being tracked, so browsers allow them to disable such cookies. Also, some modern browsers employ special policies for such cookies: - Safari does not allow third-party cookies at all. @@ -392,17 +395,17 @@ If a script sets a cookie, then no matter where the script came from -- the cook ## Appendix: GDPR -This topic is not related to JavaScript at all, just something to keep in mind when setting cookies. +This topic is not related to JavaScript at all, it is just something to keep in mind when setting cookies. -There's a legislation in Europe called GDPR, that enforces a set of rules for websites to respect the users' privacy. One of these rules is to require an explicit permission for tracking cookies from the user. +There's a legislation in Europe called GDPR, that enforces a set of rules for websites to respect the users' privacy. One of these rules is to require explicit permission for tracking cookies from the user. Please note, that's only about tracking/identifying/authorizing cookies. So, if we set a cookie that just saves some information, but neither tracks nor identifies the user, then we are free to do it. -But if we are going to set a cookie with an authentication session or a tracking id, then a user must allow that. +But if we are going to set a cookie with an authentication session or a tracking ID, then a user must allow that. -Websites generally have two variants of following GDPR. You must have seen them both already in the web: +Websites generally have two variants of complying with GDPR. You are likely to have seen them both on the web: 1. If a website wants to set tracking cookies only for authenticated users. @@ -410,26 +413,26 @@ Websites generally have two variants of following GDPR. You must have seen them 2. If a website wants to set tracking cookies for everyone. - To do so legally, a website shows a modal "splash screen" for newcomers, and requires them to agree to the cookies. Then the website can set them and let people see the content. That can be disturbing for new visitors though. No one likes to see such "must-click" modal splash screens instead of the content. But GDPR requires an explicit agreement. + To do so legally, a website shows a modal "splash screen" for newcomers and requires them to agree to the cookies. Then the website can set them and let people see the content. That can be disturbing for new visitors though. No one likes to see such "must-click" modal splash screens instead of the content. But GDPR requires an explicit agreement. -GDPR is not only about cookies, it's about other privacy-related issues too, but that's too much beyond our scope. +GDPR is not only about cookies, it is about other privacy-related issues too, but that is beyond our scope. ## Summary `document.cookie` provides access to cookies. -- Write operations modify only cookies mentioned in it. +- Write operations modify only the cookie mentioned in it. - Name/value must be encoded. - One cookie may not exceed 4KB in size. The number of cookies allowed on a domain is around 20+ (varies by browser). -Cookie options: +Cookie attributes: - `path=/`, by default current path, makes the cookie visible only under that path. - `domain=site.com`, by default a cookie is visible on the current domain only. If the domain is set explicitly, the cookie becomes visible on subdomains. -- `expires` or `max-age` sets the cookie expiration time. Without them the cookie dies when the browser is closed. +- `expires` or `max-age` sets the cookie expiration time. Without them, the cookie dies when the browser is closed. - `secure` makes the cookie HTTPS-only. - `samesite` forbids the browser to send the cookie with requests coming from outside the site. This helps to prevent XSRF attacks. Additionally: -- Third-party cookies may be forbidden by the browser, e.g. Safari does that by default. +- The browser may forbid third-party cookies, e.g. Safari does that by default. There is also work in progress to implement this in Chrome. - When setting a tracking cookie for EU citizens, GDPR requires to ask for permission. diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md index 70493e9e1..43344e487 100644 --- a/6-data-storage/03-indexeddb/article.md +++ b/6-data-storage/03-indexeddb/article.md @@ -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. diff --git a/7-animation/2-css-animations/article.md b/7-animation/2-css-animations/article.md index 2a4b79504..6c0d5a53e 100644 --- a/7-animation/2-css-animations/article.md +++ b/7-animation/2-css-animations/article.md @@ -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"> diff --git a/7-animation/3-js-animation/article.md b/7-animation/3-js-animation/article.md index dff6ab27a..82e07ee8d 100644 --- a/7-animation/3-js-animation/article.md +++ b/7-animation/3-js-animation/article.md @@ -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 diff --git a/9-regular-expressions/11-regexp-groups/article.md b/9-regular-expressions/11-regexp-groups/article.md index 796e23f54..8fec60ccc 100644 --- a/9-regular-expressions/11-regexp-groups/article.md +++ b/9-regular-expressions/11-regexp-groups/article.md @@ -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); diff --git a/BACKERS.md b/BACKERS.md new file mode 100644 index 000000000..36b1532bc --- /dev/null +++ b/BACKERS.md @@ -0,0 +1,6 @@ + +# Sponsors and Supporters + +## Supporters + +- Ilya Zelenko diff --git a/LICENSE.md b/LICENSE.md index acfce9082..cbada5307 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -7,7 +7,7 @@ As of now, we license the tutorial to almost everyone for free under the terms o ## License (Short) -The license is basically [CC-BY-NC](https://creativecommons.org/licenses/by-nc/4.0/legalcode), revocable and exclusive. +The license is based on [CC-BY-NC](https://creativecommons.org/licenses/by-nc/4.0/legalcode). It gives the right to: - **Share** – copy and redistribute the tutorial in any medium or material. @@ -15,68 +15,62 @@ It gives the right to: Under the following terms: -- **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. +- **Attribution** — You must give appropriate credit. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. - **NonCommercial** — You may not use the material for commercial purposes. ## License (Legal) -By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this license ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. +By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this license ("License"). To the extent this License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. ### Section 1 – Definitions. -a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. +a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. -b. __Adapter's License__ means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. +b. __Adapter's License__ means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this License. -c. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. +c. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. -f. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License. +f. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this License. -g. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. +g. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. -h. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License. +h. __Licensor__ means the individual(s) or entity(ies) granting rights under this License. -i. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. +i. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. j. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. k. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. -l. __You__ means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. +l. __You__ means the individual or entity exercising the Licensed Rights under this License. Your has a corresponding meaning. ### Section 2 – Scope. a. ___License grant.___ - 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: + 1. Subject to the terms and conditions of this License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive license to exercise the Licensed Rights in the Licensed Material to: A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only. - 2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. + 2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this License does not apply, and You do not need to comply with its terms and conditions. - 3. __Term.__ The term of this Public License is specified in Section 6(a). + 3. __Term.__ The term of this License is specified in Section 6(a). - 4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. + 4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. - 5. __Downstream recipients.__ - - A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. - - B. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. - - 6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). + 5. __No endorsement.__ Nothing in this License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3. b. ___Other rights.___ - 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. + 1. Moral rights, such as the right of integrity, are not licensed under this License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. - 2. Patent and trademark rights are not licensed under this Public License. + 2. Patent and trademark rights are not licensed under this License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. @@ -84,31 +78,15 @@ b. ___Other rights.___ Your exercise of the Licensed Rights is expressly made subject to the following conditions. -a. ___Attribution.___ - - 1. If You Share the Licensed Material (including in modified form), You must: - - A. retain the following if it is supplied by the Licensor with the Licensed Material: - - i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); +1. If You Share the Licensed Material (including in modified form), You must attribute the Licensor by adding: - ii. a copyright notice; + i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); - iii. a notice that refers to this Public License; + ii. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; - iv. a notice that refers to the disclaimer of warranties; +2. You may satisfy the conditions in Section 3(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. - v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; - - B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and - - C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. - - 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. +3. If requested by the Licensor, You must remove any of the information required by Section 3(1) to the extent reasonably practicable. ### Section 4 – Sui Generis Database Rights. @@ -118,21 +96,21 @@ a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and -c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. +c. You must comply with the conditions in Section 3 if You Share all or a substantial portion of the contents of the database. -For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. +For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this License where the Licensed Rights include other Copyright and Similar Rights. ### Section 5 – Disclaimer of Warranties and Limitation of Liability. a. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__ -b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__ +b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__ c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. ### Section 6 – Term and Termination. -a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. +a. This License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this License, then Your rights under this License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: @@ -140,24 +118,26 @@ b. Where Your right to use the Licensed Material has terminated under Section 6( 2. upon express reinstatement by the Licensor. - For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. + For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this License. + +c. Notwithstanding the above, the Licensor reserves the right to terminate this License with respect to You if the Licensor expressly notifies You of the termination. -c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. +d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time. -d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. +e. Sections 1, 5, 6, 7, and 8 survive termination of this License. ### Section 7 – Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. -b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. +b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this License. ### Section 8 – Interpretation. -a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. +a. For the avoidance of doubt, this License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this License. -b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. +b. To the extent possible, if any provision of this License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this License without affecting the enforceability of the remaining terms and conditions. -c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. +c. No term or condition of this License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. -d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. +d. Nothing in this License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.