๐ŸŒŽ
web.dev
  • ๐ŸŒweb.dev
    • ๐Ÿšงtodo
    • ๐Ÿงชlab
    • ๐Ÿšธlegend
    • ๐Ÿ—’๏ธtemplate
    • ๐ŸŽนshortcuts
    • ๐Ÿ“—reference
      • ๐Ÿ“˜JS spec
      • ๐Ÿ“™javascript.info
      • ๐Ÿ“™Eloquent JavaScript
      • ๐Ÿ“™JavaScript: The Definitive Guide
      • ๐Ÿ“—book
        • ๐Ÿ“—You Don't Know JS: series (v.1)
        • ๐Ÿ“—You Don't Know JS: series (v.2)
          • ๐Ÿ“—YDKJS: Scope & Closures (v.2)
        • ๐Ÿ“—Functional-Light JavaScript
      • ๐Ÿ”ฐwebsite
        • ๐Ÿ”ฐfreeCodeCamp
    • โค๏ธfav
  • โš™๏ธtools
    • ๐Ÿ”ฐtooltip
    • ๐Ÿ”ฐsyntax highlighting
  • ๐Ÿ”ฐHTML
    • ๐Ÿ”ฐelement
      • ๐ŸŽ›๏ธ<select>
      • <form>
      • ๐Ÿท๏ธ<hr>
      • <figure>
      • <input>
        • type="range"
        • type="radio"
    • block vs. inline
    • Semantic Elements
  • ๐Ÿ”ฐCSS
    • โญbox model
      • height
      • border
      • outline
      • ๐Ÿ”ธmargin
        • layout with margin
        • default margin
        • ๐Ÿ”ธnegative margin
        • margin collapsing
      • ๐Ÿ’Šbox-sizing
    • ๐Ÿ”ฐlayout
      • ๐Ÿ”นposition
        • ๐Ÿ”ธpositioned ancestor
        • ๐Ÿ”ธnearest positioned ancestor
      • ๐Ÿ”นtop
      • ๐Ÿ”นdisplay
        • ๐Ÿ”ธblock
          • inset
        • ๐Ÿ”ธinline
        • ๐Ÿ”ธflex
          • ๐ŸšงFlexbox
            • flex-wrap
            • justify-content
            • align-content
            • flex
            • container properties
          • ๐Ÿ”ธflex-direction
          • ๐Ÿ”ธalign-items
        • ๐Ÿ”ธinline-block
        • ๐Ÿ”ธinline-flex
        • ๐Ÿ”ธnone
      • ๐Ÿ”ธformatting context
      • Layout System
        • Lorem
        • Spacer
        • VStack
        • HStack
        • Box
        • VCenter/HCenter
        • Dark Theme
      • โœจexamples
        • Email Form
      • ๐Ÿ”ฐCSS grid
      • vertical-align
      • float
      • vertical centering
        • center with "transform"
        • center with "flex"
      • Block Formatting Context
    • ๐Ÿ”ฐvalues
      • ๐Ÿ”ฐunits
        • ๐Ÿ”ฐ็›ธๅฐๅ–ฎไฝ
          • ๐Ÿ”ธpercentage (๏ผ…)
          • ๐Ÿ”ธrem
          • ๐Ÿ”ธem
      • ๐Ÿ”ฐcolors
        • HSL
      • ๐Ÿ”ฐgradients
        • radial-gradient()
    • ๐Ÿ”ฐvariables
    • ๐Ÿ”ฐfunctions
      • ๐Ÿ”นcalc()
    • ๐Ÿ”ฐselectors
      • select direct children by JS
      • basic selectors
      • attribute selectors
      • ๐Ÿ”ฐcombinators
      • pseudo-class
      • ๐Ÿ”ธpseudo-element
      • custom selectors
    • ๐Ÿ”ฐproperties
      • get/set CSS props
      • Logical Properties
      • overflow
    • ๐Ÿ’กTips
    • CSS import
    • Responsive Design
    • CSS Rules
      • CSS reset
    • Design
      • ๐Ÿ”ฐCSS shadows
    • โœจexamples
      • styling <details>
      • undefined tag
      • modal dialog
      • nav bar
      • glowing buttons
    • Cascade
      • Order
      • Specificity
      • Inheritance
    • Stacking Context
    • Font
      • Font Awesome
      • font-family
    • Text
    • List
  • ๐Ÿ’JS
    • ๐Ÿ’กtips
      • fill 2D array
    • ๐Ÿงštechnique
    • โญfeature
      • โญ๏ธ ES5
      • โญ๏ธ ES6 (2015)
      • โญ๏ธ ES2017
      • โญ๏ธ ES2016
      • โญ๏ธ ES2018
      • โญ๏ธ ES2020
    • ๐Ÿ”ฐconcept
      • ๐Ÿ”ฐexecution context
        • ๐Ÿ“˜this
          • โ—"this" determined on call siteโ—๏ธ
        • ๐Ÿ”ฐrunning execution context
        • ๐Ÿ”ฐlexical environment
          • ๐Ÿ”ฐenvironment record
          • ๐Ÿ”ธ[[Environment]]
          • โ—lexical environment optimizaton
        • ๐Ÿ”ฐcall stack
      • ๐Ÿ”ฐcontext
        • ๐Ÿ”ฐglobal context
        • ๐Ÿ”ฐclass body context
      • ๐Ÿ”ฐenvironment
        • โš™๏ธJS engine
          • ๐Ÿ”ฐmode
            • ๐Ÿ›ก๏ธstrict mode
              • ๐Ÿ›ก๏ธeval has its own scope in strict mode
              • โœ…always use strict mode
            • โ—sloppy mode
              • โ—accidental global variable in sloppy modeโ—๏ธ
              • โ—modify current scope at runtimeโ—๏ธ
            • โš–๏ธstrict vs. sloppy
          • ๐Ÿ”ฐv8
        • ๐Ÿ”ฐNode.js
      • ๐Ÿ”ฐassociated array
      • ๐Ÿ”ฐparser
    • ๐Ÿ”ฐcompilation
      • ๐Ÿ”ฐlexing
      • ๐Ÿ”ฐparsing
      • ๐Ÿ”ฐcode generation
      • ๐Ÿ”ฐJS is compiled
      • ๐Ÿ”ฐcompile-time
      • ๐Ÿ”ฐruntime
    • ๐Ÿ”ฐgrammar
      • ๐Ÿšฉdirective
        • ๐Ÿ“œ'use strict'
      • *๏ธtoken
        • ๐Ÿ—๏ธkeyword
          • ๐Ÿ”reserved word
            • โ—reserved word as property name is allowedโ—๏ธ
          • ๐Ÿ”‘contextual keyword
        • ๐Ÿ”ฃpunctuator
          • *๏ธhash (#)
          • ๐Ÿ”ฃsemicolon (;)
            • ๐Ÿ”ฐautomatic semicolon insertion
            • ๐Ÿ”ฐstatements that must end with ";"
          • ๐Ÿ”ฃdot (.)
          • ๐Ÿ”ฃcomma (,)
          • ๐Ÿ”ฃcolon (:)
          • ๐Ÿ”ฃplus (+)
          • ๐Ÿ”ฃequal (=)
          • ๐Ÿ”ฃ3 dots (...)
          • ๐Ÿ”ฃbrackets []
          • ๐Ÿ”ฃbraces {}
          • ๐Ÿ”ฃparentheses ()
          • ๐Ÿ”ฃquestion dot (?.)
          • ๐Ÿ”ฃquestion (?)
        • ๐Ÿ†”identifier
          • ๐Ÿ†”special identifier
          • โš–๏ธidentifier vs. string
          • โš–๏ธidentifier vs. keyword
          • โ—undeclared identifier
          • ๐Ÿ”ฐstatic binding
          • ๐Ÿ”ฐdynamic binding
        • โœก๏ธliteral
          • ๐Ÿ”ฐtrailing comma
      • ๐Ÿ”ฐcomment
      • ๐Ÿˆฏdeclaration
        • โ—redeclaration
        • ๐Ÿˆฏlexical declaration
      • ๐Ÿ“œstatement
        • ๐Ÿ“ฆexpression
          • ๐Ÿ“ฆprimary expression
          • ๐Ÿ“ฆinvocation expression๏ผšf()
            • ๐Ÿ“ฆmethod invocation
          • ๐Ÿ“ฆleft-hand side expression
        • ๐Ÿ”loop
          • ๐Ÿ”while
          • ๐Ÿ”do...while
          • ๐Ÿ”for loops
            • ๐Ÿ”for
              • ๐Ÿ”ฐ"for-init" scope
              • ๐Ÿ”ฐ"for-init" block
            • ๐Ÿ”for-in
            • ๐Ÿ”for-of
              • โ—arrays are iterated "live"โ—๏ธ
              • ๐Ÿ”ฐfor-of with objects
              • ๐Ÿ”ฐfor-of with strings
              • โš–๏ธfor-of vs. forEach
            • ๐Ÿ”for-await
            • โš–๏ธfor-in vs. for-of vs. in
            • ๐Ÿ“˜arr.forEach()
        • ๐Ÿ”€control flow
          • โคด๏ธjump
            • โคด๏ธreturn
            • โคด๏ธcontinue
            • โคด๏ธbreak
            • โš–๏ธcontinue vs. break
          • ๐Ÿ”€branch
            • ๐Ÿ”€if
            • ๐Ÿ”€switch
              • ๐Ÿ”€case clause
        • ๐Ÿ“œother statement
          • ๐Ÿ“œblock
            • ๐Ÿ”ฐblock scope
          • ๐Ÿ“œlabel
          • ๐Ÿ›‘with
          • ๐Ÿ“œempty statement
        • โ›”statement expectedโ—๏ธ
      • ๐Ÿšงoperator
        • ๐Ÿšงtable of operators
        • ๐Ÿ“–terms for operator
          • ๐Ÿ”ฐarity
          • ๐Ÿ”ฐassociativity
          • ๐Ÿ”ฐlvalue
          • ๐Ÿ”ฐdividend
          • ๐Ÿ”ฐdivisor
          • ๐Ÿ”ฐnon-numeric operand
          • ๐Ÿ”ฐshort-circuiting
        • ๐Ÿ”ดassignment
          • โญdestructuring assignment
            • ๐Ÿ”ฐdestructuring array
            • ๐Ÿ”ฐdestructuring iterable
            • ๐Ÿ”ฐobject destructuring
            • ๐Ÿ”ฐnested destructuring
              • ๐ŸŒŸnested default values
            • ๐Ÿ”ฐdestructuring arguments
          • โž•assignment (=)
        • ๐Ÿ”ดarithmetic operator
          • โญ•unary arithmetic operator
            • ๐Ÿšงunary plus (+)
          • โญ•binary arithmetic operator
            • โž•add/concate (+)
            • โž•division (/)
            • โž•remainder (%)
              • ๐Ÿ’พmodulo(a, b)
              • ๐Ÿ’พmod(a, b)
              • โš–๏ธremainder vs. modulo vs. mod
            • โž•exponentiation (**)
          • โญ•bitwise operator
            • ๐ŸŒŸ2's complement
            • ๐Ÿ’พbitview()
            • โž•bitwise not (~)
              • ๐Ÿ”ฐdouble tilde (~~)
              • โš–๏ธ(~~) vs. Math.trunc()
        • ๐Ÿ”ดrelational operator
          • ๐Ÿ”ฐequality & inequality operator
            • โž•sloppy equality (==)
            • โž•strict equality (===)
          • ๐Ÿ”ฐcomparison operator
          • โž•in
          • โž•instanceof
        • ๐Ÿšงlogical operator
          • โž•nullish coalescing (??)
          • โญ•unary logical operator
          • โญ•binary logical operator
        • โญ•unary operator
          • โž•void
        • โญ•binary operator
          • โž•comma operator (,)
        • โญ•ternary operator
          • โž•conditional operator (?:)
        • ๐Ÿ”ฐspread operator (...)
        • โž•rest operator (...)
      • ๐Ÿ”ฐstatement vs. declaration
    • ๐Ÿ”ฐscope
      • ๐Ÿ”ฐlexical scope
      • ๐Ÿ”ฐstatic scope
      • ๐Ÿ”ฐdynamic scope
      • โš–๏ธstatic vs. dynamic scoping
      • ๐Ÿ”ฐscope chain
      • โš–๏ธlexical environment vs. scope
      • ๐Ÿ”ฐtop-level scope
      • ๐ŸŒŸglobal scope
        • ๐Ÿ“˜global object
          • ๐Ÿท๏ธglobalThis
          • ๐Ÿ„window
            • ๐Ÿ”ธwindow.name
            • ๐Ÿ”ธwindow.navigator
          • ๐Ÿ„global
          • ๐Ÿ”ธeval
          • ๐Ÿ”ธglobal object property
        • ๐Ÿ’พisCurrentlyInGlobalScope()
        • ๐Ÿ”ฐglobal variable
          • ๐Ÿ”ฐglobal let
            • โ—global let shadows global object property
          • ๐Ÿ”ฐglobal var
        • ๐Ÿ”ธDOM element with "id"
      • ๐Ÿ”ฐmodule scope
      • ๐Ÿ”ฐfunction scope
      • ๐Ÿ”ฐfunction name scope
      • ๐Ÿ”ฐparameter scope
      • ๐Ÿ”ฐhoisting
        • ๐Ÿ”ฐfunction hoisting
          • โ—function hoisting firstโ—๏ธ
        • ๐Ÿ”ฐvariable hoisting
          • ๐Ÿ”ฐvar hoisting
            • โ—function expression not hoisted
          • ๐Ÿ”ฐlet/const/class hoisting
      • โš–๏ธcontext vs. scope
    • ๐Ÿ’value
      • ๐Ÿ”ฐtype
        • ๐Ÿ’falsy
        • ๐Ÿ“–terms for type
          • ๐Ÿ”ฐstatically typed
          • ๐Ÿ”ฐdynamically typed
        • ๐Ÿ”ฐtype conversion
        • ๐Ÿ”ฐtype name
          • โž•typeof
          • ๐Ÿ’พbaseTypeName()
          • ๐Ÿ’พtypeName()
          • ๐Ÿ’พcustom type name
        • ๐Ÿ”ฐnested type
        • ๐Ÿ‘”type functions
          • ๐Ÿ’พfunctionDefinition()
          • ๐Ÿ’พisObject()
          • ๐Ÿ’พisPrimitive()
          • ๐Ÿ’พisFunction()
          • ๐Ÿ’พisClass()
      • ๐Ÿฆ primitive
        • ๐Ÿ’nullish
        • ๐Ÿ”ธundefined
        • ๐Ÿ”null
        • ๐Ÿ”ขNumber
          • ๐Ÿ”ฐfloating-point
          • ๐Ÿ”ฐintegers
          • ๐Ÿ”ดspecial number
            • ๐Ÿ”ขInfinity
            • ๐Ÿ”ขNaN
            • ๐Ÿ”ขNumber.EPSILON
            • ๐Ÿ”ขNumber.MAX_VALUE
            • ๐Ÿ”ขNumber.MAX_SAFE_INTEGER
          • ๐Ÿ‘”Number+ext
            • ๐Ÿ’พn.toHex()
            • ๐Ÿ’พn.isEqual()
            • ๐Ÿ’พisNumber()
          • ๐Ÿ‘”Random
            • randomInt()
            • randomFloat()
          • ๐Ÿ‘”Quaternion
        • ๐Ÿ”ขBigInt
        • ๐Ÿฆ Boolean
        • ๐Ÿ”คString
          • ๐Ÿ”ฐraw string
          • ๐Ÿ”ฐescape sequence
          • template literal
            • tagged template
            • tag function
          • ๐Ÿ”ฐString methods
            • str.map()
            • ๐Ÿ’พstr.capitalize()
            • str.countLetters()
            • ๐Ÿ’พstr.htmlToElement()
            • str.kebabToCamelCase()
            • ๐Ÿ“˜str.match()
              • โœจsplit into course names
            • ๐Ÿ“˜str.matchAll()
              • โœจmatchAll() using named groups
              • โœจwords in sentence
            • str.random()
            • str.removeWhitespaces()
            • ๐Ÿ“˜str.replace()
              • ๐Ÿ”ฐreplacer function
              • โœจminus one
            • ๐Ÿ’พstr.replaceWhitespaces()
            • str.reverse()
            • str.shuffle()
            • ๐Ÿ’พstr.slice2()
            • ๐Ÿ’พstr.splice()
            • str.escapeHTML()
            • str.toDatasetPropName()
            • ๐Ÿ’พstr.words()
            • str.wordCounts()
            • String.fromCharCodes()
          • ๐Ÿ‘”String extension
            • ๐Ÿ’พstr.isKeyword
            • ๐Ÿ’พstr.isReservedWord
            • ๐Ÿ’พstr.isPunctuator
          • ๐Ÿ”ฐUnicode
            • ๐Ÿ”ฐcode point
            • ๐Ÿ”ฐgrapheme cluster
            • ๐Ÿ”ฐcode plane
            • ๐Ÿ”ฐcharacter encoding
              • ๐Ÿ”ฐUTF-16
                • ๐Ÿ”ฐcode unit
                • ๐Ÿ”ฐsurrogate pair
                • ๐Ÿ”ฐscalar
        • ๐Ÿ”บSymbol
          • ๐Ÿ”บSymbol.iterator
        • ๐Ÿ”ฐprimitive wrapper
      • ๐Ÿ„object
        • ๐Ÿ”ธproperty
          • ๐Ÿšฉattribute
            • ๐Ÿ„property descriptor
            • ๐Ÿ’พownPropertyFlags(obj)
          • ๐Ÿ”ธgetter/setter
          • ๐Ÿ”ธproperty name
            • โญcomputed property name
          • โญshorthand property
          • ๐Ÿ”ธinternal property
          • ๐Ÿ”ฐproperty access
            • ๐Ÿ“ฆproperty access expression
            • โž•dot notation (.)
              • โš–๏ธdot notation vs. decimal point
            • โž•bracket notation []
            • โž•optional chaining (?., ?.[])
              • โ“is Optional Chaining Supported?
              • ๐Ÿ’พobj.prop(path)
            • โž•optional invocation ?.()
              • is Optional Chaining Supported?
              • obj.prop(path)
            • ๐ŸŒŸchaining rules
              • โœจjane.boyfriend?.money.more
          • ๐Ÿ”ฐproperty creation/deletion
            • ๐Ÿ“˜Object.defineProperty()
            • โž•delete
              • โ—unqualified identifier for delete
          • ๐Ÿ”ฐproperty testing
          • ๐Ÿ”ฐproperty enumeration
        • ๐Ÿ”ฐcreating objects
          • โœก๏ธobject literal
            • โ—object literal is not a blockโ—๏ธ
          • โž•new
            • ๐Ÿ”ธnew.target
            • โš–๏ธcallable vs. constructable
          • โ“‚๏ธObject.create()
            • โœจ"pure" object
            • โœจguard against accidental modifications
        • ๐Ÿ”ฐextending objects
          • ๐Ÿ“˜Object.assign()
            • โ›”๏ธ Object.assign causing TypeError
            • โ—๏ธObject.assign copies with getter/setter
          • inheritance
            • ๐Ÿ”ธ__proto__
            • class A vs. class A extends Object
          • mixin
            • ๐Ÿ’พmergeWithoutOverride()
            • ๐Ÿ’พ.assignDescriptors()
            • mixin inheritance
            • โœจmixin: HandleEvents
          • ๐Ÿ’พ add default properties
          • extending built-in classes
        • ๐Ÿ”ฐconverting objects
          • ๐Ÿ”ฐserializing objects
          • ๐ŸŒŸobject -> primitive conversion
            • ๐Ÿ”˜prefer-string
            • ๐Ÿ”˜prefer-number
            • ๐Ÿ”˜no preference
          • โ“‚๏ธ.toString()
          • โ“‚๏ธ.valueOf()
          • ๐Ÿ’พvalueToString()
        • โ“‚๏ธmethod
          • ๐Ÿ’ [[HomeObject]]
          • ๐Ÿ”ฐpolymorphism
        • ๐Ÿ”ธprototype
          • ๐Ÿ”ฐprototype chain
            • ๐ŸšงprintPrototypeChain()
      • ๐Ÿ„function
        • ๐Ÿ”ฐcreating functions
        • ๐Ÿˆฏfunction declaration
          • โ—function in block (FiB)
          • โ—function redeclaration
          • ๐Ÿ“˜function declaration instantiation
        • ๐Ÿ”ฐfunction expression
          • โ—read-only identifier
          • ๐Ÿ”ฐnamed function expression
          • ๐Ÿ”ฐas variable
          • ๐Ÿ”ฐIIFE
        • ๐Ÿ”ฐarrow function
          • โ—arrow function as methodโ—๏ธ
          • ๐Ÿ”ฐarrow function as argument
          • โ—arrow function as class fieldโ—๏ธ
          • โ—arrow function returning object literal needs (...)โ—๏ธ
          • ๐Ÿ”ฐarrow function expression
        • ๐Ÿ”ฐclosure
          • โ—scopes matter with closures
          • ๐Ÿ’กprivate property by closure
          • โœจclosure examples
            • โœจclosure: to close over or not
            • โœจclosure: manage grades
            • โœจclosure as object
        • ๐ŸŒฟsorts of function
          • ๐Ÿ”ฐcallback
          • ๐Ÿ”ฐhigher-order function
            • ๐Ÿ’พnot(f)
            • ๐Ÿ’พpipe(f, g, ...)
            • ๐Ÿ’พpartial(f, a, b ...)
            • ๐Ÿงšdecorator
              • ๐Ÿ’พsaveCallHistory(f)
              • ๐Ÿ’พdelay(f, s)
              • ๐Ÿ’พbenchmark(f)
              • ๐Ÿ’พdebounce(f, s)
              • ๐Ÿ’พthrottle(f, s)
              • โš–๏ธdebounce vs. throttle
              • ๐Ÿงšmemoization
                • ๐Ÿงšmemoize by decorator
                  • ๐Ÿ’พmemoize(f)
                • ๐Ÿงšmemoize by closure
          • ๐Ÿ”ฐpredicate
          • ๐Ÿ”ฐnested function
          • ๐Ÿ”ฐrecursive function
            • โœจfindSolution()
            • ๐Ÿ’พdeepEqual()
        • ๐Ÿ”ฐfunction boundary
        • ๐Ÿ”ฐfunction overloading
        • ๐Ÿ”ฐfunction forwarding
        • ๐Ÿ”ธreturn value
        • ๐Ÿ”ธfunction name
          • ๐Ÿ”ฐanonymous function
        • โš–๏ธparameter vs. argument
        • ๐Ÿ”ธargument
          • โ—arguments are passed by valueโ—๏ธ
        • ๐Ÿ”ธparameter
          • ๐Ÿ”ธdefault parameter
          • ๐Ÿ”ธrest parameters
            • ๐Ÿ”ธrest parameters as object
          • ๐Ÿ”ธdestructured parameter
          • โš–๏ธsimple vs. non-simple parameter
          • โ—deplicate parameters
        • ๐Ÿ”ธprototype
        • ๐Ÿ”นconstructor
      • ๐Ÿ„class
        • ๐Ÿ”ฐinitialization
          • ๐Ÿ”นstatic block
        • ๐Ÿ”ธmember
          • ๐Ÿ”ธclass field
          • ๐Ÿ”ธprivate member
          • ๐Ÿ”ธstatic member
            • ๐Ÿ”ฐinit static property
          • private/protected members
          • ๐Ÿ”ธgetter/setter
          • ๐Ÿ”ธmethod
          • ๐Ÿ”ธbound method
        • ๐Ÿ”ฐcreating class
          • ๐Ÿ”ฐdelegation
        • ๐Ÿ”ฐinheritance
          • ๐Ÿ“˜super
          • derived class init
          • override constructor
          • override methods
          • override class fields
        • โœจexamples
          • โœจComplex
          • โœจTypedMap
          • โœจBag
      • ๐Ÿ“˜built-in objects
        • ๐Ÿ“˜Object
          • ๐Ÿ”ธObject.prototype
          • ๐Ÿ’พObject extension
            • ๐Ÿ’พobj.mergeWith()
        • ๐Ÿ“˜Function
        • ๐Ÿ“˜Array
          • ๐Ÿ”ธelement
            • ๐Ÿ”ธelement index
              • โ—element index is stringโ—๏ธ
          • ๐Ÿ”ธarray length
          • ๐Ÿ”ธarray object property
          • ๐Ÿ„sparse array
            • ๐Ÿ”ฐhow array methods deal with "holes" ?
          • ๐Ÿ”ฐarray-like
            • ๐Ÿ‘”Object+arrayLike
          • ๐Ÿ”ฐcreating arrays
            • โœก๏ธarray literal
              • ๐Ÿ”ฐundefined element
          • ๐Ÿ”ฐaccessing elements
          • ๐Ÿ”ฐiterating elements
          • ๐Ÿ“˜array methods
            • ๐Ÿ“˜arr.entries
            • ๐Ÿ“˜arr.filter()
            • ๐Ÿ“˜arr.flatMap()
            • ๐Ÿ“˜arr.reduce()
              • early break reduce()
            • ๐Ÿ“˜arr.slice()
            • ๐Ÿ“˜arr.splice()
          • ๐Ÿ‘”Array extension
            • ๐Ÿ’พarr.containsSubarray()
            • ๐Ÿ’พarr.copy()
            • ๐Ÿ’พarr.isEmpty
            • ๐Ÿ’พarr.last
            • ๐Ÿ’พarr.none()
            • ๐Ÿ’พarr.reversed()
            • ๐Ÿ’พarr.shuffle()
            • ๐Ÿ’พarr.randomElement
            • ๐Ÿ’พarr.removeDuplicates()
            • ๐Ÿ’พarr.uniqueMerge()
            • ๐Ÿ’พarr.removeUndefined()
            • ๐Ÿ’พarr.removeValue()
            • ๐Ÿ’พarr.sum(), .average()
            • ๐Ÿ’พarr.topN()
            • ๐Ÿ’พarr.rank()
            • ๐Ÿ’พarr.padEndSpaces()
            • ๐Ÿ’พarr.max()
            • ๐Ÿ’พarr.isMatrix
            • ๐Ÿ’พarr.indexDictionaryForValues()
            • Array extensions (archive)
          • โœจarray examples
            • ๐Ÿ’พobjects to HTML table
          • array & matrix methods
          • static methods
            • ๐Ÿ“Array(n) vs. Array(n).fill()
            • Array.from()
            • ๐ŸšงArray.list(n)
          • matrix methods
            • mat.matrixMap()
            • mat.transpose()
            • mat.maxElementInEachColumn()
          • Matrix methods
            • ๐ŸŒ€ Array.matrixFill()
        • ๐Ÿ“˜Set
          • ๐Ÿ‘”Set extension
            • ๐Ÿ’พset.isEqualTo()
        • ๐Ÿ“˜Map
          • Don't use object properties
        • ๐Ÿ“˜RegExp
          • ๐Ÿšฉregex flag
            • ๐Ÿšฉflag /u
            • ๐Ÿšฉflag /d
          • ๐Ÿ”ธlastIndex
          • ๐Ÿ”ฐcreating regex
          • ๐Ÿ”ฐusing regex
          • ๐Ÿ”ฐpattern
            • ๐Ÿ”ฐspecial characters
            • ๐Ÿ”ฐanchor
            • ๐Ÿ”ฐrepeat
              • ๐Ÿ”ฐgreedy vs. lazy
            • ๐Ÿ”ฐcharacter set
            • ๐Ÿ”ฐalternation (or)
            • ๐Ÿ”ฐgroup
              • ๐Ÿ”ฐcapturing group
                • ๐Ÿ”ฐnamed group
            • ๐Ÿ”ฐlookaround
            • ๐Ÿ”ฐinline modifier
            • ๐Ÿ”ฐreplace pattern
            • ๐Ÿ˜€emoji
          • โœจregex examples
            • โœจparse .ini file
            • โœจquick brown fox
            • โœจJS-style numbers
        • ๐Ÿ“˜Proxy
          • ๐Ÿ”นhandler method
          • ๐Ÿ“˜Reflect
          • โœจProxy examples
            • โœจidentity (proxy)
            • โœจread-only proxy
            • โœจobserve()
        • ๐Ÿ“˜Date
    • ๐Ÿ”ฐvariable
      • ๐Ÿˆฏvariable declaration
        • ๐Ÿ“œvar
          • โœ…stop using varโ—๏ธ
          • โ—var is a statementโ—๏ธ
          • โ—var has no block scopeโ—๏ธ
          • โ—accessing var before declaration gets undefinedโ—๏ธ
          • โ—global var / function is global object propertyโ—๏ธ
          • โ—var redeclaration applied even in strict modeโ—๏ธ
          • โ—var in block can't shadow outer letโ—๏ธ
          • โ—var can shadow parameter even in strict modeโ—๏ธ
        • ๐Ÿˆฏconst
          • โ›”const requires initialization
          • โ›”const can't reassign
          • โ›”no const for classic "for"
        • ๐Ÿˆฏlet
          • โœ…let redeclaration not allowed even in sloppy modeโ—๏ธ
          • โœ…let can't shadow parameter even in sloppy modeโ—๏ธ
        • ๐Ÿ”ฐinitial value
      • โ—variable redeclaration
        • โ—var redeclaration
        • โ—let redeclaration
      • ๐Ÿ”ดaccessing variables
        • โ›”temporal dead zone
          • โ›”TDZ
          • โ›”typeof let/const/class in TDZ gets an errorโ—๏ธ
        • โ›”uninitialized variable
        • ๐Ÿ”ฐLHS reference
        • ๐Ÿ”ฐRHS reference
        • โš–๏ธLHS vs. RHS reference
      • โ—variable shadowing
      • โ—can't delete variable/functionโ—๏ธ
    • ๐Ÿ”ฐmodule
      • โญES module
        • ๐Ÿˆฏimport
          • โœจimport ES module
          • dynamic import()
        • ๐Ÿˆฏexport
          • ๐Ÿ“˜default export
          • ๐Ÿ“˜named export
      • ๐Ÿ”ฐCommonJS
        • ๐Ÿ“œpackage.json
      • โš–๏ธES vs CommonJS
      • ๐Ÿ”ฐimport.meta
      • ๐Ÿ”ฐpackage
      • ๐Ÿ”ฐbundler
      • ๐Ÿ”ฐminifier
      • ๐Ÿ”ฐbrowser-specific features
      • ๐Ÿ”ฐmodule pattern
    • ๐Ÿ”ฐiteration
      • ๐Ÿ‘”Iterable+ext
        • ๐Ÿ’พrange()
        • ๐Ÿ’พobj.isIterable
      • ๐Ÿ”ฐiterable
        • ๐Ÿ”ธmake-iterator method
        • โ—only-iterate-once iterable
        • ๐Ÿ”ฐmake iterables
        • โœจiterable examples
          • โœจClosedRange
          • โœจSequence
      • ๐Ÿ”ฐiterator
        • ๐Ÿ”ธnext()
        • ๐Ÿ”ธreturn()
        • โ—iterators only iterate onceโ—๏ธ
        • ๐Ÿ”ฐiterable iterator
        • ๐Ÿ”ฐinfinite iterator
        • ๐Ÿ”ฐmake iterator iterable
        • ๐Ÿ’พIteratorPrototype
        • ๐Ÿ’พIterator
      • ๐Ÿ”ฐiteration result
      • ๐Ÿ“˜Generator
        • ๐Ÿ”ฐgenerator function
          • ๐Ÿ“˜function*
          • ๐Ÿ“ฆyield
          • ๐Ÿ“˜yield*
          • ๐Ÿ”ฐcomposition
          • ๐Ÿ”ฐgenerator function as ...
        • โœจgenerator examples
          • ๐Ÿ’พ*list()
          • ๐Ÿ’พ*integers()
          • ๐Ÿ’พ*closedRange()
          • ๐Ÿ’พ*fibonacci()
          • ๐Ÿ’พ*zip()
          • ๐Ÿ’พ*sequence()
          • ๐Ÿ’พ*interleave()
    • ๐Ÿ”ฐasync code
      • ๐Ÿ”ฐthread
      • ๐Ÿ”ฐevent loop
      • ๐Ÿ„Promise
        • ๐Ÿ‘”custom promises
          • ๐Ÿ’พgetJSON()
          • ๐Ÿ’พPromise.wait()
          • ๐Ÿ’พfutureValue()
        • ๐Ÿ”ฐusing Promises
        • ๐Ÿ”ฐerror handling
          • โญfinally()
          • ๐Ÿ”ฐrecoverable errors
        • ๐Ÿ”ฐchaining Promises
          • ๐Ÿ’พPromise.inSeries()
        • ๐Ÿ”ฐPromises in parallel
      • ๐Ÿ†”async
        • ๐Ÿ”ฐasync function
        • ๐Ÿ”ฐasync method
        • ๐Ÿ”ฐasync arrow function
        • ๐Ÿ”ฐasync IIFE
        • ๐Ÿ’พmeasureTime()
      • โž•await
        • ๐Ÿ”ฐawait promises
          • ๐Ÿ”ฐawait sequentially
          • ๐Ÿ”ฐawait in parallel
        • ๐Ÿ”ฐawait "thenable"
        • ๐Ÿ”ฐerror handling
    • ๐Ÿ”ฐdebugging
      • ๐Ÿ”ฐtesting
      • ๐Ÿ’Šerror handling
        • โคด๏ธthrow
        • ๐Ÿ”ฐrethrow
        • โคด๏ธtry-catch-finally
      • ๐Ÿ› ๏ธdevtools
        • ๐Ÿ“œdebugger
        • ๐Ÿ”ฐshow built-in shadow DOM
    • โ›”Error
      • ๐Ÿ”ดcompile-time error
        • โ›”๏ธ early errors
      • ๐Ÿ”ดruntime error
      • โ›”๏ธ SyntaxError
        • โ›”duplicate parameter not allowed in strict modeโ—๏ธ
        • โ›”unexpected token "xxx"โ—๏ธ
        • โ›”Named export 'xxx' not foundโ—๏ธ
        • โ›”unexpected numberโ—๏ธ
        • โ›”... parenthesis must be used to disambiguate operator precedenceโ—๏ธ
        • โ›”identifier 'xxx' has already been declaredโ—๏ธ
        • โ›”missing initializer in const declarationโ—๏ธ
        • โ›”lexical declaration cannot appear in a single-statement contextโ—๏ธ
        • โ›”delete of an unqualified identifier in strict modeโ—๏ธ
        • โ›”octal literals not allowed in strict modeโ—๏ธ
      • โ›”๏ธ ReferenceError
        • โ›”cannot access 'xxx' before initializationโ—๏ธ
        • โ›”'xxx' is not definedโ—๏ธ
      • โ›”๏ธ TypeError
        • โ›”'xxx' is not a functionโ—๏ธ
        • โ›”assignment to constant variableโ—๏ธ
        • โ›”cannot read properties of nullishโ—๏ธ
        • โ›”cannot convert BigInt to Numberโ—๏ธ
        • โ›”cannot convert Symbol to Numberโ—๏ธ
        • โ›”cannot assign to read only property 'prototype' of function 'xxx'โ—๏ธ
    • ๐Ÿ›๏ธLibraries
      • ๐Ÿ›๏ธJSXGraph
      • ๐Ÿ›๏ธgrapheme-splitter
    • ๐Ÿ› ๏ธtools
      • transpilers
    • โœจexamples
      • Code Wars
        • Next Bigger Number
        • Changing Money
        • Game of Life
      • Exercises
        • flattenObject()
        • Pascal's Triangle
        • Permuations
        • Spiral Matrix
    • ๐Ÿ’ผprojects
      • ๐Ÿ’ผDictionary App
      • ๐Ÿ’ผLanguage: Egg
      • Calculator โค๏ธ
      • Synth Keyboard โค๏ธ
      • Form Validation
      • Password Generator
  • ๐Ÿ”ฐweb component
    • ๐Ÿ”ธcustom element
      • lifecycle methods
      • getter/setter for attributes
      • Element Types
      • Element Info
      • Observed Attributes
      • Element Upgrades
      • Unknown Elements vs Undefined Custom Elements
      • Element-defined Content
    • ๐Ÿ”ธshadow DOM
      • light, shadow, flattened DOM
      • shadow root vs. host
      • shadow DOM styles
        • Use CSS Variables
        • CSS reset in Shadow DOM
      • shadow DOM events
        • retargeting
        • Active Element
        • Events that Cross Shadow DOM Boundary
        • Bubbling in Shadow DOM
      • shadow DOM slots
    • ๐Ÿ”ธ<template>
      • ๐Ÿ”ธ<slot>
        • ๐Ÿ”ธnamed slot
        • ๐Ÿ”ธfallback content
        • ๐Ÿ”ฐslotted node styles
        • ๐Ÿ”ธslotted nodes
        • ๐Ÿ”น`slotchange` event
        • ๐Ÿ”ฐSlot API
    • ๐Ÿ”ธlight DOM
    • ๐Ÿ”ฐimplementing components
    • ๐Ÿ”ฐusing components
      • ๐Ÿ’กhide components until defined
    • Built-in Components
      • <details>
    • โœจweb component examples
      • โœจ<search-box>
      • <my-log> โค๏ธ
      • <element-details>
      • <custom-dialog>
      • <user-card>
      • <expanding-list>
      • <dom-hierarchy> โญ๏ธ
      • <time-formatted>
      • <live-timer>
      • <error-message>
      • <popup-info>
      • <custom-menu>
  • ๐ŸŒbrowser
    • ๐Ÿ“˜web API
      • ๐Ÿ“˜clearTimeout()
      • ๐Ÿ“˜setTimeout()
      • ๐Ÿ„URL
      • ๐Ÿ„navigator
      • ๐Ÿ”ฐweb worker
        • ๐Ÿ„self
    • ๐Ÿ”ฐconcepts
      • ๐Ÿ”ฐcoordinates
    • ๐Ÿ”ฐEvent
      • ๐Ÿ”ธevent.type
      • ๐Ÿ”ธevent.currentTarget
      • ๐Ÿ”ธevent.target
        • โœจbuttons A, B, C
      • ๐Ÿ”ฐevent dispatching
      • ๐Ÿ”ฐevent propagation
        • ๐Ÿ”ฐbubbling phase
        • ๐Ÿ”ฐcapturing phase
        • ๐Ÿ”ฐstop propagation
          • โœจbutton in clickable paragraph
      • ๐Ÿ”ฐevent handler
        • invocation context
        • โŒreturn value
        • ๐Ÿ”ฐ"this" in event handler
        • ๐Ÿ”ฐregister handler
          • ๐Ÿ”ฐhandler options
          • capturing handler
          • ๐Ÿ„object handler
        • ๐Ÿ”ฐremove handler
        • โ—window-reflecting body element event handlers
      • ๐Ÿ”ฐdefault action
        • โœจhide or not to hide?
        • โœจlink going nowhere
      • ๐ŸŒฟevent types
        • ๐Ÿ”ฐkey event
          • โœจpress shift + space
        • ๐Ÿ”ฐinput event
        • ๐Ÿ”ฐmouse event
          • ๐Ÿ”ธ.buttons
          • ๐Ÿ”ธ.button
          • ๐Ÿ”ฐ"click"
            • โœจdraw dots
            • โœจsliding menu
            • โœจclosing button [x]
            • โœจclick to slide (carousel)
            • โœจclick to move ball
          • ๐Ÿ”ฐ"dblclick"
          • ๐Ÿ”ฐ"mousemove"
            • โœจdrag the bar
          • ๐Ÿ”ฐdrag event
          • ๐Ÿ‘”MouseEvent+ext
          • โœจmouse event examples
            • โœจmouse event coords
        • ๐Ÿ”ฐtouch event
          • ๐Ÿ”ธ.touches
          • ๐Ÿ”ธ.targetTouches
          • ๐Ÿ‘”TouchList+ext
        • ๐Ÿ”ฐscroll event
          • โœจscroll me
          • โœจscroll progress
          • โœจscroll progress on body
        • state-change
        • CSS events
      • client-side JavaScript timeline
      • custom events
        • event.isTrusted
      • "change" event
    • ๐Ÿ”ฐDOM
      • ๐Ÿ”ฐDOM hierarchy
      • ๐Ÿ”ฐquerying elements
        • ๐Ÿ’พ$(), $all()
      • ๐Ÿ”ฐtraversing DOM
      • ๐Ÿ”ฐcreate/insert/delete nodes
        • โœจtable of contents
      • ๐ŸŒฟDOM types
        • ๐Ÿ“˜Node
          • ๐Ÿ”ฐ.tagName vs .nodeName
          • ๐Ÿ‘”Node+ext
            • ๐Ÿ’พnode.isInPage
            • ๐Ÿšงnode.traverse()
            • ๐Ÿ’พnode.$(), .$all()
            • ๐Ÿšงnode.appendTag()
            • ๐Ÿ’พnewElement()
        • ๐Ÿ“˜Element
          • ๐Ÿ”ธelement content
          • ๐Ÿ”น.insertAdjacentHTML()
          • ๐Ÿ”ธattribute
            • ๐Ÿ”ธid
            • ๐Ÿ”ฐdata attributes
            • โš–๏ธattributes vs. properties
          • ๐Ÿ”ฐbox models
          • ๐Ÿ‘”Element+boxes
            • ๐Ÿ”ธ.scrollBox
              • ๐Ÿ”ฐscrollbar width
            • ๐Ÿ”ธ.paddingBox
            • ๐Ÿ”ธ.borderBox
              • ๐Ÿ”ธ.offsetParent
            • ๐Ÿ”ธ.boundingBox
              • โœจelem.showNote()
          • ๐Ÿ‘”Element+ext
            • ๐Ÿ’พelem.isInside()
            • ๐Ÿ’พelem.isHeading
            • ๐Ÿ’พelem.wrappedWith()
            • ๐Ÿ’พelem.wrappedWithHTML()
            • ๐Ÿšงelem.attr()
            • ๐Ÿšงelem.styleProp()
            • ๐Ÿšงelem.showDataAttr()
            • ๐Ÿ’พelem.position()
        • ๐Ÿ„HTMLElement
          • ๐Ÿ’พhtmlElem.isHidden
        • ๐Ÿ“˜Document
          • ๐Ÿ”ธdocument size
          • ๐Ÿ”ธmethods
            • ๐Ÿ“˜doc.elementFromPoint()
        • ๐Ÿ“˜Window
          • ๐Ÿ”น.requestAnimationFrame()
          • ๐Ÿ”ฐviewport
          • ๐Ÿ”ฐwindow scrolling
            • ๐Ÿ”ฐscroll smoothly
            • ๐Ÿ”ฐscroll lock
        • ๐Ÿ„DocumentFragment
        • ๐Ÿ„NodeList
          • NodeList vs. Array
        • CSSStyleDeclaration
      • โœจDOM examples
      • ๐Ÿ‘”custom methods
        • ๐Ÿšงprop()
        • ๐Ÿšงtag()
    • ๐Ÿ”ฐSVG
      • ๐Ÿ’พSVGElement()
      • Attributes
        • fill
        • stroke-dasharray
        • transform
      • ๐Ÿ”ฐgradients
        • ๐Ÿ“˜<linearGradient>
        • ๐Ÿ“˜<radialGradient>
      • Patterns
      • ๐Ÿ”ฐSVG shapes
        • <rect>
        • <line>
        • <polygon>
        • <polyline>
        • <ellipse>
        • <path>
      • โœจSVG examples
        • SVG as background image
        • 3 circles
        • โœจSVG Clock
        • โœจpie chart
      • namespace
      • tools
    • ๐Ÿ”ณ<canvas>
      • ๐Ÿ‘”Canvas+ext
        • ๐Ÿ’พdrawOnCanvas2D()
        • ๐Ÿ’พctx.point()
        • ๐Ÿ’พctx.polyline()
        • ๐Ÿ’พctx.roundedRect()
      • ๐Ÿ”ฐrectangle
      • ๐Ÿ”ฐpath
        • ๐Ÿ”ฐnonzero winding rule
        • ๐Ÿ“˜arcTo()
        • โœจcurves
      • ๐Ÿ”ฐtext
      • ๐Ÿ”ฐimage
        • โœจcanvas snapshot
      • ๐Ÿ”ฐgraphics state
        • ๐Ÿ”ฐfill / stroke styles
          • ๐Ÿ”ฐcolors
            • โœจcolor wheel
          • ๐Ÿ”ฐpatterns
          • ๐Ÿ”ฐgradients
            • ๐Ÿ’พctx.gradient()
        • ๐Ÿ”ฐshadows
        • ๐Ÿ”ฐcompositing
        • ๐Ÿ”ฐtransformation
          • โœจKoch snowflake
        • ๐Ÿ”ฐclipping
      • โœจcanvas examples
        • โœจsquare & circle
        • โœจopen/closed subpaths
        • โœจfractal
        • โœจregular polygons
    • ๐Ÿ”ฐstorage
    • ๐Ÿ”ฐanimation
      • โœจanimation examples
        • โœจmoving cat
        • โœจcat in hat
        • โœจcat behind hat
  • ๐Ÿ”ฐReact
    • ๅ•Ÿๅ‹•
    • Hello Reactโ—๏ธ
    • React Projects
      • FullStackOpen
        • Part 1
  • Server
    • HTTP status code
  • ๐Ÿ”–้™„้Œ„
    • ๐Ÿ‘”custom
      • ๐Ÿ‘”element
        • ๐Ÿ‘”tabs control
        • ๐Ÿ‘”left tabs
      • ๐Ÿ‘”custom css
        • ๐Ÿ‘”Every Layout
      • ๐Ÿ‘”custom classes
        • ๐Ÿ‘”Vector
        • ๐Ÿ‘”Rect
        • ๐Ÿ‘”Size
        • ๐Ÿ’พTableMaker
        • List
        • Point
        • Turtle
        • Matrix
      • ๐Ÿ‘”custom functions
        • randomElement()
        • randomColor()
        • randomPassword()
        • clone(obj)
        • functionName()
      • helper functions
    • ๐Ÿงฉthree.js
      • ๐Ÿ‘”Director
      • ๐Ÿ”ฐrenderer
        • ๐Ÿ”นset canvas size
      • โœจexamples
        • โœจstartup
      • ๐Ÿ”ฐprimitives
      • ๐Ÿ”ฐscene graph
    • ๐Ÿ…ฐ๏ธGoogle Apps Script
      • ๐Ÿ„app
        • ๐Ÿ”นhelpers
        • ๐Ÿ”ธapp.init()
        • ๐Ÿ”ธmembers
          • ๐Ÿ”ธapp.color
          • ๐Ÿ’พapp.dataRangeValuesFromSheet()
          • ๐Ÿ’พapp.sheetByName()
          • ๐Ÿ’พapp.valueOfNamedRange()
          • ๐Ÿ’พapp.deleteSheetByName()
          • ๐Ÿšงapp.cellValueAt()
          • โญapp.makeTable()
          • ๐Ÿšงapp.mergedCellValue()
          • ๐Ÿšงapp.parseFields()
          • ๐Ÿšงapp.fetch()
          • ๐Ÿšงapp.resizeColumns()
          • ๐Ÿšงapp.setBorder()
          • ๐Ÿšงapp.writeAwardList()
        • ๐Ÿ”ณprototypes
          • ๐Ÿ”ณapp.sheet.prototype
            • ๐Ÿ’พ sheet.delete()
            • ๐Ÿ’พ sheet.rangeByRect()
            • ๐Ÿ’พ sheet.setValues()
            • ๐Ÿ’พsheet.appendConditionalFormatRule()
            • ๐ŸšงSheetMethods
              • sheet.values()
          • ๐Ÿ”ณapp.range.prototype
            • range.alignCenter()
            • range.cellValue()
            • range.contains()
            • ๐Ÿšงrange.isEqualTo()
        • ๐ŸŸฅapp menu
      • ๐Ÿ„RawData
      • ๐ŸŒฟClasses
        • ๐Ÿ„Sheet
        • ๐Ÿ„Range
          • ๐Ÿ”ฐA1 notation
            • ๐Ÿ’พcolumnName()
            • ๐Ÿ’พA1Notation()
            • ๐Ÿ’พA1NotationToRowColumn()
          • ๐Ÿ”ฐborder
          • ๐Ÿ“˜range.setNumberFormat()
          • range.setRichTextValue()
        • ๐Ÿ„ConditionalFormatRule
        • ๐Ÿ„RichTextValueBuilder
        • ๐Ÿ„Protection
        • ๐Ÿ„Ui
      • ๐Ÿ‘”custom objects
        • ๐Ÿ‘”RangeRect
          • ๐Ÿ’พrect.cell(i, j)
          • ๐Ÿ’พRangeRect.byData()
        • ๐ŸšงBorderStyle
        • ๐ŸšงSheetFields, SheetField
      • ๐Ÿ’ผprojects
        • ๐Ÿ’ผไธ่ƒฝ่ฃœ่€ƒๅๅ–ฎ
        • ๐Ÿ’ผๅ„็ญๅนณๅ‡ๅŠๅ‰ไธ‰ๅ
        • ๐Ÿ’ผๅœ‹ไธญๆˆ็ธพไธ€่ฆฝ่กจ
        • ๐Ÿ’ผ้ซ˜ไธญ็Žๅญธ้‡‘ๅๅ†Š
      • ๐Ÿ’กtips
        • ๐Ÿ’ก custom prototypes
        • ๐Ÿ’ก refresh cells
        • ๐Ÿ’ก ๆขไปถๆ ผๅผ๏ผš่‡ช่จ‚ๅ…ฌๅผ
        • ่™•็†ๅญ—ไธฒ
        • ้กฏ็คบไธญๆ–‡ๆ˜ŸๆœŸๅนพ
        • ๅœจๅ„ฒๅญ˜ๆ ผไฝฟ็”จ้™ฃๅˆ—
        • Count non-blank cells
        • ๐Ÿ’ก ๅฟซ้€Ÿ่ชฟๆ•ดๆฌ„ไฝๅฏฌๅบฆ
      • โ“questions
      • ๐Ÿ“ŠGoogle Sheet
      • ๐Ÿ”ธGAS โŸฉ commands
      • โœจGAS โŸฉ examples
        • examples
        • โœจsplit data into tabs
          • migrate sheets into files
        • โœจevent: onEdit
      • ๐Ÿ”ฐGAS โŸฉ Events
        • onEdit
      • Dialogs & Sidebars
    • ๐Ÿ“–JSDoc
      • return value
      • destructured parameter
    • ๐Ÿ“ฆdata structure
      • ๐Ÿ“ฆQueue
      • ๐Ÿ“ฆStack
      • ๐Ÿ“ฆWaitingList
      • ๐Ÿ“ฆGraph
        • ๐Ÿ”น.findPath()
        • ๐Ÿ”น.findShortestPath()
        • ๐Ÿค–breadth-first search
        • ๐Ÿค–depth-first search
        • โœจGraph examples
          • โœจMail Robot
    • ๐Ÿ”ฐalgorithm
      • ๐Ÿ”ฐrecursion
        • โœจrecursion count
      • ๐Ÿ”ฐdynamic programming
      • Sort
        • sort strings
        • sort by multiple keys
        • bubble sort
      • โœจproblems
        • โœจfind couple
        • โœจknapsack problem
        • โœจlongest path
    • ๐Ÿ”ฐparadigm
      • ๐Ÿ”ฐTest Driven Development (TDD)
      • ๐Ÿ”ฐFunctional Programming (FP)
    • ๐Ÿ”ฐTypeScript
      • Setup
      • โญ๏ธ Cheat sheet
      • Type
        • type annotation
        • type predicate
        • Primitives
        • Narrowing
        • Tuples
        • Generics
          • Array<T>
          • Generic Functions
        • Object
          • Object Types
          • Object Properties
        • Function
          • optional/default parameters
          • return type
          • function type expression
          • callable objects with properties
          • function overloads
        • Union
          • discriminated union
      • TS Operators
    • ๐Ÿ’Žresource
      • โœ๏ธeditor
        • โœ๏ธVSCode
          • emmet
          • shortcuts
        • โœ๏ธreplit.com
          • ๐Ÿ”ฐimport .js file
      • ๐Ÿ”งtools
        • Vectornator
          • Tutorials
      • ๐Ÿ“šbooks
Powered by GitBook
On this page

Was this helpful?

  1. browser
  2. <canvas>

Canvas+ext

Previous<canvas>NextdrawOnCanvas2D()

Last updated 2 years ago

Was this helpful?

โŸฉ โŸฉ +ext

extend and .

custom

  • replit โŸฉ -> require Rect, Vector, Number+ext, Iterable+ext

// (Canvas+ext v1.1)
// 2023.01.22 - 21:29 (/) .drawPath() -> 1. opts destructuring, 2. line dash
//                    (/) .moveToPoint() -> remove `newPath` param
//                    (/) fix .drawArcTo(), .drawPolylineTo()
//                    (+) .drawPolyline(), .drawQuadraticCurve(), .drawCubicBezierCurve()

// 2023.01.21 - 09:02 (/) .drawPolygon() - fix start angle
// 2023.01.21 - 08:14 (+) .drawPolygon()
// 2023.01.20 - 23:36 (โ€ข) first version
// -----------------------------------------------------------------

const {PI} = Math;

// โญ import
import { deg } from './Number+ext.js';        // ๐Ÿ‘” Number+ext
import { vec, polar } from './Vector.js';     // ๐Ÿ‘” Vector
import { range } from './Iterable+ext.js';    // ๐Ÿ‘” Iterable+ext
// -----------------------------------------------------------------


// โญ HTMLCanvasElement + extensions
// -----------------------------------------------------------------
// ๐Ÿ”น .draw2D()
//
Object.defineProperties(HTMLCanvasElement.prototype, {
    // ๐Ÿ”น .draw2D()
    draw2D: {
        value: function(draw){
            draw(this.getContext("2d"));
        },
    },
});

// โญ CanvasRenderingContext2D + extensions
// -----------------------------------------------------------------
// โญ .drawPath()         // general method
// ๐Ÿ”น .drawCircle()
// ๐Ÿ”น .drawWedge()
// ๐Ÿ”น .drawEllipse()
// -----------------------------------------------------------------
// ๐Ÿ”น .moveToPoint()
// -----------------------------------------------------------------
// ๐Ÿ”น .drawArc()
// ๐Ÿ”น .drawArcTo()        // for rounded corners
// ๐Ÿ”น .drawPolyline()
// ๐Ÿ”น .drawPolylineTo()
// ๐Ÿ”น .drawQuadraticCurve()
// ๐Ÿ”น .drawQuadraticCurveTo()
// ๐Ÿ”น .drawCubicBezierCurve()
// ๐Ÿ”น .drawCubicBezierCurveTo()
// -----------------------------------------------------------------
// ๐Ÿ”น .drawRect()
// ๐Ÿ”น .drawPolygon()
//
Object.defineProperties(CanvasRenderingContext2D.prototype, {

    // ๐Ÿ”น .moveToPoint()
    moveToPoint: {
        value: function(p, {
            // newPath = false,        // begin new subpath ? (default = false)
        }={}) {
            // if (newPath) this.beginPath();
            this.moveTo(...p.coords);        // ๐Ÿ‘” Vector
        }
    },

    // โญ .drawPath()
    drawPath: {
        value: function(draw, {
            // path
            newPath = true,        // begin new subpath ?
            closePath = true,      // close subpath ?
            // fill/stroke
            fill = true,
            stroke = true,
            fillStyle,
            strokeStyle,
            // line dash
            dash,
        }={}){
            
            // new subpath ?
            if (newPath) this.beginPath();    // start new path if necessary
            
            // draw path
            draw();

            // close path ?
            if (closePath) this.closePath();
            
            // fill ?
            if (fill) {
                if (fillStyle) this.fillStyle = fillStyle;
                this.fill();
            };
            
            // stroke ?
            if (stroke) {
                if (strokeStyle) this.strokeStyle = strokeStyle;
                if (dash) this.setLineDash(dash);
                this.stroke();
            };
            
        },
    },

    // --------------------------
    //     arc/circle related
    // --------------------------
    
    // ๐Ÿ”น .drawArc()
    drawArc: {
        value: function(center, radius, {
            // path: arc related
            start = 0,            // start angle
            end = 2 * PI,         // end angle
            clockwise = true,
            // path: general
            ...opts
        }={}){
            this.drawPath(() => {
                this.arc(...center.coords, radius, start, end, !clockwise);
            }, { ...opts });
        },
    },

    // ๐Ÿ”น .drawArcTo()
    // - for rounded corners
    drawArcTo: {
        value: function(control, target, radius, {
            // arc specific
            start,                 // start point
            // path: general
            ...opts
        }={}){
            // arc specific
            //      โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ default โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
            opts = {newPath: false, closePath:false, ...opts};
            // if (start) { opts.newPath = true }    // new subpath if has start point
            // draw arc to ...
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start);
                // connect arc
                this.arcTo(...control.coords, ...target.coords, radius);
            }, { ...opts });
        },
    },

    // ๐Ÿ”น .drawCircle()
    drawCircle: {
        value: function(center, radius, {
            // path: general
            newPath = true,       // start new path?
            closePath = true,
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.arc(...center.coords, radius, 0, 2*PI);
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawEllipse()
    drawEllipse: {
        value: function(center, radiusX, radiusY, {
            // path: ellipse related
            start = 0,
            end = 2 * PI,
            rotation = 0,
            clockwise = true,
            // path: general
            newPath = true,
            closePath = true,
            // fill/stroke
            fill = true,
            stroke = true,
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.ellipse(
                    ...center.coords, radiusX, radiusY, 
                    rotation, start, end, !clockwise
                );
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawWedge()
    drawWedge: {
        value: function(center, radius, {
            // path: arc related
            start = 0,            // start angle
            end = 90 * deg,       // end angle    // ๐Ÿ‘” Number+ext
            clockwise = true,
            // path: general
            ...opts
        }={}){
            // wedge specific parameters
            opts = {
                ...opts, 
                newPath: true, 
                closePath: true,    // add line back to center
            };
            // draw wedge
            this.drawPath(() => {
                this.moveTo(...center.coords);           // center
                // โญ arc() adds a line from `center` to arc start.
                this.arc(...center.coords, radius, start, end, !clockwise);
            }, { ...opts });
        },
    },

    // --------------------------
    //     bezier curve
    // --------------------------

    // ๐Ÿ”น .drawQuadraticCurveTo()
    drawQuadraticCurveTo: {
        value: function(control, target, {
            // bezier curve specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path?
            closePath = false,
            // fill/stroke
            fill = false,          // fill?
            stroke = false,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect bezier curve
                this.quadraticCurveTo(...control.coords, ...target.coords);
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawQuadraticCurve()
    drawQuadraticCurve: {
        value: function(p1, ctrl, p2, {
            ...opts
        }={}){
            // curve specific
            //      โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ default โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
            opts = {newPath: true, fill: false, closePath: false, ...opts};
            this.drawPath(() => {
                // move to start point
                this.moveToPoint(p1);
                // connect bezier curve
                this.quadraticCurveTo(...ctrl.coords, ...p2.coords);
            }, { ...opts });
        },
    },

    // ๐Ÿ”น .drawCubicBezierCurveTo()
    drawCubicBezierCurveTo: {
        value: function(control1, control2, target, {
            // bezier curve specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path ?    (default: false)
            closePath = false,     // close subpath ?     (default: false)
            // fill/stroke
            fill = false,          // fill ?              (default: false)
            stroke = false,        // stroke ?            (default: false)
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect bezier curve
                this.bezierCurveTo(
                    ...control1.coords, ...control2.coords, 
                    ...target.coords,
                );
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawCubicBezierCurve()
    drawCubicBezierCurve: {
        value: function(p1, c1, c2, p2, {
            ...opts
        }={}){
            // curve specific
            //      โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ default โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
            opts = {newPath: true, fill: false, closePath: false, ...opts};
            this.drawPath(() => {
                // move to start point 
                this.moveToPoint(p1);
                // connect bezier curve
                this.bezierCurveTo(
                    ...c1.coords, ...c2.coords, 
                    ...p2.coords,
                );
            }, { ...opts });
        },
    },

    // --------------------------
    //     polygon
    // --------------------------

    // ๐Ÿ”น .drawRect()
    drawRect: {
        value: function(rect, {...opts}={}){
            this.drawPath(() => {
                this.rect(...rect.coords);        // ๐Ÿ‘” Rect
            }, { ...opts });
        },
    },

    // ๐Ÿ”น .drawPolygon()
    drawPolygon: {
        value: function(n, {
            // polygon specific
            center = vec(100, 100),   // ๐Ÿ‘” Vector
            radius = 50,
            rotate = 0,               // rotation
            clockwise = true,         // path direction
            // path: general
            ...opts
        }={}){
            this.drawPath(() => {
                
                // dฮธ = ยฑ 2ฯ€ / n
                const dt = 2 * Math.PI / n * (clockwise ? 1 : -1);
                // start angle
                const a0 = -90 * deg + rotate + (n.isEven ? dt/2 : 0);

                // vertices
                const points = range(0, n-1)
                    .map(i => center.plus(polar(radius, a0 + dt * i)))
                    .array;
                
                // draw polyline
                this.drawPolylineTo(points.slice(1), {
                    start: points[0],
                });
                
            }, { ...opts });
        },
    },

    // ๐Ÿ”น .drawPolyline()
    drawPolyline: {
        value: function(points, {
            ...opts
        }={}){
            // polyline specific
            //      โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ default โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
            opts = {fill: false, closePath: false, ...opts};
            this.drawPath(() => {
                this.drawPolylineTo(points.slice(1), {
                    start: points[0],
                });
            }, { ...opts });
            
        },
    },
    
    // ๐Ÿ”น .drawPolylineTo()
    drawPolylineTo: {
        value: function(points, {
            // polyline specific
            start,                 // start point
            newPath = false,       // start new path?
            closePath = false,
            // path: general
            ...opts
        }={}){
            // polyline specific
            if (start) newPath = true;    // new subpath if has start point
            //      โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ default โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
            opts = {fill: false, closePath: false, ...opts, newPath};
            // draw polyline to ...
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start);
                // connect arc
                points.forEach(p => {
                    this.lineTo(...p.coords);
                });
            }, { ...opts });
        },
    },
    
});

// โญ export
// -----------------------------------------------------------------
export {};        // ES module export
  • regular polygons

  • (this example)

const { log } = console
const { sqrt } = Math;

// โญ๏ธ import
import { $ } from './js/ext/Node_ext.js';        // ๐Ÿ‘” Node + ext
import { deg } from './js/ext/Number+ext.js';    // ๐Ÿ‘” Number+ext
import { size } from './js/ext/Size.js';         // ๐Ÿ‘” Size
import { rect } from './js/ext/Rect.js';         // ๐Ÿ‘” Rect
import { vec, polar, linearCombination as lc } from './js/ext/Vector.js'; // ๐Ÿ‘” Vector
import {  } from './js/ext/Element+boxes.js';    // ๐Ÿ‘” Element + boxes
import {  } from './js/ext/Canvas+ext.js';       // ๐Ÿ‘” Canvas + ext
import {  } from './js/ext/MouseEvent+ext.js';   // ๐Ÿ‘” MouseEvent + ext
import { range } from './js/ext/Iterable+ext.js';   // ๐Ÿ‘” Iterable + ext
// --------------------------------------------------------------------

// elements
const canvas = $('#canvas');

// canvas
const {width: W, height: H} = canvas.paddingBox;
const ROWS = 3, COLS = 3;

// layout
const dx = 20, dy = 20;             // padding
const w = 120, h = 120;    // box size

const STEPX = w + dx;                // translation
const STEPY = h + dy;                // translation

// expected canvas size
const SIZE = size(COLS*STEPX + dx, ROWS*STEPY + dy);  
log(`${ROWS} rows x ${COLS} cols: needs canvas size ${SIZE}`);

const u = vec(STEPX, 0);    // vectors for translation
const v = vec(0, STEPY);    // vectors for translation

const INSET = 10;

// styles
const HUE = 30;

// first rect
const p0 = vec(dx, dy);    // origin of first rect
const diag = vec(w, h);    // diagonal vector
const rect0 = rect(p0, diag);

const p1 = p0.plus(STEPX, 0);

// rects
const rects = range(0,2).map(i => 
    range(0,2)
        .map(j => rect0.translate(lc([j,i], [u,v])))  // ๐Ÿ‘” Rect (v1.1)
        .array
).array;

// draw rects
canvas.draw2D(ctx => {

    // ---------
    //   inset
    // ---------
    
    // positive inset
    range(0, 4).forEach(i => {                // ๐Ÿ‘” Iterable + ext
        ctx.drawRect(rects[0][0].inset(INSET * i), {
            fillStyle: `hsl(${HUE * i ** 1.3} 90% 50%)`
        });
    })

    ctx.drawRect(rects[0][1], {dash: [5, 5]});

    // โญ๏ธ negative inset
    ctx.drawRect(rects[0][1].inset(-10), {fillStyle: `#eee6`});

    // -----------
    //   polygon
    // -----------

    ctx.drawPolygon(4, {
        center: rects[0][2].center, 
        radius: w / sqrt(2),
    });
});

// โญ draw paths
canvas.draw2D(ctx => {            // ๐Ÿ‘” Node+ext, ๐Ÿ‘” Canvas+ext

    const c21 = rect0.translate(v).center;           // center (๐Ÿ‘” Vector)
    const R = h/2 - 10;
    const r = 35;
    const d = 2*R + dx;                // traslation offset
    // const u = vec(d, 0);               // translation vector

    ctx.lineWidth = 4;                 // stroke width

    // โญ 1. polygon
    // --------------------------------------
    ctx.drawPolygon(6, {
        center: rects[1][0].center,
        radius: R+10, 
        fillStyle: 'yellow',
        dash: [],
    });

    // โญ circle
    // --------------------------------------
    ctx.drawCircle(rects[1][0].center, R-10, { fillStyle: 'red' });
    
    // โญ 2. ellipse 
    // --------------------------------------
    ctx.drawEllipse(rects[1][1].center, R, r, {
        rotation: 30*deg, start: 90*deg,           // ๐Ÿ‘” Number+ext
        fillStyle: "orange",
    });

    // โญ 3. wedge 1 (bigger)
    // --------------------------------------
    ctx.drawWedge(rects[1][2].center, R, {
        end: 300*deg, fillStyle: 'yellow'
    });

    // โญ wedge 2 (smaller)
    const c23_1 = rects[1][2].center.plus(polar(dx, -29 * deg));
    ctx.drawWedge(c23_1, R, {start: -60*deg, end: 0, fillStyle: 'orange'});

    // โญ 4. partial rounded sqaure
    // --------------------------------------
    const rect31 = rects[2][0].inset(10);
    const p0 = rect31.top;                  // top middle
    const [p3, p2, p1, p4] = rect31.points; // BL, BR, TR, TL
    
    // begin in top middle.
    ctx.drawArcTo(p1, p2, 20, {start: p0, newPath: true});
    ctx.drawArcTo(p2, p3, 20);
    
    ctx.drawArcTo(p3, p4, 20, {
        closePath: true, 
        stroke: true, fill: true, 
        fillStyle: 'hsl(120 90% 50% / 70%)',    // green
    });

    // โญ 5. quadratic Bezier curve (1 control point)
    // --------------------------------------
    const p21 = p3.plus(u);
    const p22 = p21.plus(lc([0.3, -0.7],[u,v])); // control point
    const p23 = p21.plus(w, 0);
    const offset = vec(5,5);        // control point size, ๐Ÿ‘” Vector

    ctx.drawQuadraticCurve(p21, p22, p23, {
        fill: true, 
        fillStyle: 'hsl(225 90% 50% / 70%)',    // blue
    }); 
    
    // โญ 6. cubic Bezier curve (2 control points)
    // --------------------------------------
    const p31 = p21.plus(d, -R);
    const p34 = p31.plus(2*R, 0);
    const p32 = p31.plus(30, -R);    // control point
    const p33 = p34.plus(-30, R);    // control point

    ctx.drawCubicBezierCurve(p31, p32, p33, p34, {
        fill: true, 
        fillStyle: 'hsl(270 90% 50% / 70%)',    // tranparent green
    });
    
    // ------------
    //   polyline
    // ------------

    const q1 = rects[0][2].bottomLeft.plus(10, -10), 
        q2 = rects[0][2].top.plus(0, 10), 
        q3 = rects[0][2].bottomRight.plus(-10, -10);
    
    ctx.lineWidth = 1;
    ctx.setLineDash([3, 6]);         // โญ dash line
    
    ctx.drawPolyline([p21, p22, p23]);   
    ctx.drawPolyline([q1, q2, q3]);
    ctx.drawPolyline([p31, p32, p33, p34]);

    // โญ return to solid lines
    ctx.setLineDash([]);        
    
    // draw control points
    [p21, p22, p23, p31, p32, p33, p34].forEach(p => {
        ctx.drawRect(rect(p, offset, { center: true }), { 
            fillStyle: 'tomato',
        });   
    });
    
});
  • drawOnCanvas2D()

  • ctx.polyline()

  • ctx.point()

  • ctx.gradient()

  • regular polygons - add subpath

History

0: (โ€ข) first version
1: (+) .drawPolygon()
// 2023.01.20 - 23:36 
// -----------------------------------------------------------------

const {PI} = Math;

// โญ import
import { deg } from './Number+ext.js';        // ๐Ÿ‘” Number+ext
// -----------------------------------------------------------------


// โญ HTMLCanvasElement + extensions
// -----------------------------------------------------------------
// ๐Ÿ”น .draw2D()
//
Object.defineProperties(HTMLCanvasElement.prototype, {
    // ๐Ÿ”น .draw2D()
    draw2D: {
        value: function(draw){
            draw(this.getContext("2d"));
        },
    },
});

// โญ CanvasRenderingContext2D + extensions
// -----------------------------------------------------------------
// ๐Ÿ”น .moveToPoint()
// -----------------------------------------------------------------
// โญ .drawPath()         // general method
// ๐Ÿ”น .drawArc()
// ๐Ÿ”น .drawCircle()
// ๐Ÿ”น .drawWedge()
// ๐Ÿ”น .drawEllipse()
// -----------------------------------------------------------------
// ๐Ÿ”น .drawArcTo()        // for rounded corners
// ๐Ÿ”น .drawPolylineTo()
// ๐Ÿ”น .drawQuadraticCurveTo()
// ๐Ÿ”น .drawCubicBezierCurveTo()
// -----------------------------------------------------------------
// ๐Ÿ”น .drawRect()
//
Object.defineProperties(CanvasRenderingContext2D.prototype, {

    // ๐Ÿ”น .moveToPoint()
    moveToPoint: {
        value: function(p, {
            newPath=true,        // begin new subpath ? (default = true)
        }={}) {
            if (newPath) this.beginPath();
            this.moveTo(...p.coords);        // ๐Ÿ‘” Vector
        }
    },

    // โญ .drawPath()
    drawPath: {
        value: function(draw, {
            // path
            newPath = true,        // begin new subpath ?
            closePath = true,      // close subpath ?
            // fill/stroke
            fill = true,
            stroke = true,
            fillStyle,
            strokeStyle,
        }={}){
            
            // new subpath ?
            if (newPath) this.beginPath();    // start new path if necessary
            
            // draw path
            draw();

            // close path ?
            if (closePath) this.closePath();
            
            // fill ?
            if (fill) {
                if (fillStyle) this.fillStyle = fillStyle;
                this.fill();
            };
            
            // stroke ?
            if (stroke) {
                if (strokeStyle) this.strokeStyle = strokeStyle;
                this.stroke()
            };
            
        },
    },

    // --------------------------
    //     arc/circle related
    // --------------------------
    
    // ๐Ÿ”น .drawArc()
    drawArc: {
        value: function(center, radius, {
            // path: arc related
            start = 0,            // start angle
            end = 2 * PI,         // end angle
            clockwise = true,
            // path: general
            newPath = true,       // start new path?
            closePath = true,
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.arc(...center.coords, radius, start, end, !clockwise);
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawArcTo()
    // - for rounded corners
    drawArcTo: {
        value: function(control, target, radius, {
            // arc specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path?
            closePath = false,
            // fill/stroke
            fill = false,          // fill?
            stroke = false,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect arc
                this.arcTo(...control.coords, ...target.coords, radius);
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawCircle()
    drawCircle: {
        value: function(center, radius, {
            // path: general
            newPath = true,       // start new path?
            closePath = true,
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.arc(...center.coords, radius, 0, 2*PI);
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawEllipse()
    drawEllipse: {
        value: function(center, radiusX, radiusY, {
            // path: ellipse related
            start = 0,
            end = 2 * PI,
            rotation = 0,
            clockwise = true,
            // path: general
            newPath = true,
            closePath = true,
            // fill/stroke
            fill = true,
            stroke = true,
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.ellipse(
                    ...center.coords, radiusX, radiusY, 
                    rotation, start, end, !clockwise
                );
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawWedge()
    drawWedge: {
        value: function(center, radius, {
            // path: arc related
            start = 0,            // start angle
            end = 90 * deg,       // end angle    // ๐Ÿ‘” Number+ext
            clockwise = true,
            // path: general
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.moveTo(...center.coords);           // center
                // โญ arc() adds a line from `center` to arc start.
                this.arc(...center.coords, radius, start, end, !clockwise);
                // this.closePath();                        // add line back to center
            }, { newPath: true, closePath: true, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // --------------------------
    //     bezier curve
    // --------------------------

    // ๐Ÿ”น .drawQuadraticCurveTo()
    drawQuadraticCurveTo: {
        value: function(control, target, {
            // bezier curve specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path?
            closePath = false,
            // fill/stroke
            fill = false,          // fill?
            stroke = false,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect bezier curve
                this.quadraticCurveTo(...control.coords, ...target.coords);
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawCubicBezierCurveTo()
    drawCubicBezierCurveTo: {
        value: function(control1, control2, target, {
            // bezier curve specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path ?    (default: false)
            closePath = false,     // close subpath ?     (default: false)
            // fill/stroke
            fill = false,          // fill ?              (default: false)
            stroke = false,        // stroke ?            (default: false)
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect bezier curve
                this.bezierCurveTo(
                    ...control1.coords, ...control2.coords, 
                    ...target.coords,
                );
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // --------------------------
    //     polygon
    // --------------------------

    // ๐Ÿ”น .drawRect()
    drawRect: {
        value: function(rect, {
            // path: general
            newPath = true,       // start new path?
            closePath = true,
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.rect(...rect.coords);        // ๐Ÿ‘” Rect
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawPolylineTo()
    drawPolylineTo: {
        value: function(points, {
            // polyline specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path?
            closePath = false,
            // fill/stroke
            fill = false,          // fill?
            stroke = false,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect arc
                points.forEach(p => {
                    this.lineTo(...p.coords);
                });
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },
    
});

// โญ export
// -----------------------------------------------------------------
export {};        // ES module export
// 2023.01.21 - 08:14 (+) .drawPolygon()
// 2023.01.20 - 23:36 (โ€ข) first version
// -----------------------------------------------------------------

const {PI} = Math;

// โญ import
import { deg } from './Number+ext.js';        // ๐Ÿ‘” Number+ext
import { vec, polar } from './Vector.js';     // ๐Ÿ‘” Vector
import { range } from './Iterable+ext.js';    // ๐Ÿ‘” Iterable+ext
// -----------------------------------------------------------------


// โญ HTMLCanvasElement + extensions
// -----------------------------------------------------------------
// ๐Ÿ”น .draw2D()
//
Object.defineProperties(HTMLCanvasElement.prototype, {
    // ๐Ÿ”น .draw2D()
    draw2D: {
        value: function(draw){
            draw(this.getContext("2d"));
        },
    },
});

// โญ CanvasRenderingContext2D + extensions
// -----------------------------------------------------------------
// ๐Ÿ”น .moveToPoint()
// -----------------------------------------------------------------
// โญ .drawPath()         // general method
// ๐Ÿ”น .drawArc()
// ๐Ÿ”น .drawCircle()
// ๐Ÿ”น .drawWedge()
// ๐Ÿ”น .drawEllipse()
// -----------------------------------------------------------------
// ๐Ÿ”น .drawArcTo()        // for rounded corners
// ๐Ÿ”น .drawPolylineTo()
// ๐Ÿ”น .drawQuadraticCurveTo()
// ๐Ÿ”น .drawCubicBezierCurveTo()
// -----------------------------------------------------------------
// ๐Ÿ”น .drawRect()
// ๐Ÿ”น .drawPolygon()
//
Object.defineProperties(CanvasRenderingContext2D.prototype, {

    // ๐Ÿ”น .moveToPoint()
    moveToPoint: {
        value: function(p, {
            newPath=true,        // begin new subpath ? (default = true)
        }={}) {
            if (newPath) this.beginPath();
            this.moveTo(...p.coords);        // ๐Ÿ‘” Vector
        }
    },

    // โญ .drawPath()
    drawPath: {
        value: function(draw, {
            // path
            newPath = true,        // begin new subpath ?
            closePath = true,      // close subpath ?
            // fill/stroke
            fill = true,
            stroke = true,
            fillStyle,
            strokeStyle,
        }={}){
            
            // new subpath ?
            if (newPath) this.beginPath();    // start new path if necessary
            
            // draw path
            draw();

            // close path ?
            if (closePath) this.closePath();
            
            // fill ?
            if (fill) {
                if (fillStyle) this.fillStyle = fillStyle;
                this.fill();
            };
            
            // stroke ?
            if (stroke) {
                if (strokeStyle) this.strokeStyle = strokeStyle;
                this.stroke()
            };
            
        },
    },

    // --------------------------
    //     arc/circle related
    // --------------------------
    
    // ๐Ÿ”น .drawArc()
    drawArc: {
        value: function(center, radius, {
            // path: arc related
            start = 0,            // start angle
            end = 2 * PI,         // end angle
            clockwise = true,
            // path: general
            newPath = true,       // start new path?
            closePath = true,
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.arc(...center.coords, radius, start, end, !clockwise);
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawArcTo()
    // - for rounded corners
    drawArcTo: {
        value: function(control, target, radius, {
            // arc specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path?
            closePath = false,
            // fill/stroke
            fill = false,          // fill?
            stroke = false,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect arc
                this.arcTo(...control.coords, ...target.coords, radius);
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawCircle()
    drawCircle: {
        value: function(center, radius, {
            // path: general
            newPath = true,       // start new path?
            closePath = true,
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.arc(...center.coords, radius, 0, 2*PI);
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawEllipse()
    drawEllipse: {
        value: function(center, radiusX, radiusY, {
            // path: ellipse related
            start = 0,
            end = 2 * PI,
            rotation = 0,
            clockwise = true,
            // path: general
            newPath = true,
            closePath = true,
            // fill/stroke
            fill = true,
            stroke = true,
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.ellipse(
                    ...center.coords, radiusX, radiusY, 
                    rotation, start, end, !clockwise
                );
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawWedge()
    drawWedge: {
        value: function(center, radius, {
            // path: arc related
            start = 0,            // start angle
            end = 90 * deg,       // end angle    // ๐Ÿ‘” Number+ext
            clockwise = true,
            // path: general
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.moveTo(...center.coords);           // center
                // โญ arc() adds a line from `center` to arc start.
                this.arc(...center.coords, radius, start, end, !clockwise);
                // this.closePath();                        // add line back to center
            }, { newPath: true, closePath: true, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // --------------------------
    //     bezier curve
    // --------------------------

    // ๐Ÿ”น .drawQuadraticCurveTo()
    drawQuadraticCurveTo: {
        value: function(control, target, {
            // bezier curve specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path?
            closePath = false,
            // fill/stroke
            fill = false,          // fill?
            stroke = false,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect bezier curve
                this.quadraticCurveTo(...control.coords, ...target.coords);
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawCubicBezierCurveTo()
    drawCubicBezierCurveTo: {
        value: function(control1, control2, target, {
            // bezier curve specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path ?    (default: false)
            closePath = false,     // close subpath ?     (default: false)
            // fill/stroke
            fill = false,          // fill ?              (default: false)
            stroke = false,        // stroke ?            (default: false)
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect bezier curve
                this.bezierCurveTo(
                    ...control1.coords, ...control2.coords, 
                    ...target.coords,
                );
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // --------------------------
    //     polygon
    // --------------------------

    // ๐Ÿ”น .drawRect()
    drawRect: {
        value: function(rect, {
            // path: general
            newPath = true,       // start new path?
            closePath = true,
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.rect(...rect.coords);        // ๐Ÿ‘” Rect
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawPolygon()
    drawPolygon: {
        value: function(n, {
            // polygon specific
            center = vec(100, 100),   // ๐Ÿ‘” Vector
            radius = 50,
            rotate = 0,               // rotation
            clockwise = true,         // path direction
            // path: general
            newPath = true,           // start new path?
            closePath = true,
            // fill/stroke
            fill = true,              // fill?
            stroke = true,            // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                
                // dฮธ = ยฑ 2ฯ€ / n
                const dt = 2 * Math.PI / n * (clockwise ? 1 : -1);   
                const a0 = -90 * deg + rotate;        // start angle

                // vertices
                const points = range(0, n-1)
                    .map(i => center.plus(polar(radius, a0 + dt * i)))
                    .array;
                
                // draw polyline
                this.drawPolylineTo(points.slice(1), {
                    start: points[0],
                });
                
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawPolylineTo()
    drawPolylineTo: {
        value: function(points, {
            // polyline specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path?
            closePath = false,
            // fill/stroke
            fill = false,          // fill?
            stroke = false,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect arc
                points.forEach(p => {
                    this.lineTo(...p.coords);
                });
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },
    
});

// โญ export
// -----------------------------------------------------------------
export {};        // ES module export
// 2023.01.21 - 09:02 (/) .drawPolygon() - fix start angle
// 2023.01.21 - 08:14 (+) .drawPolygon()
// 2023.01.20 - 23:36 (โ€ข) first version
// -----------------------------------------------------------------

const {PI} = Math;

// โญ import
import { deg } from './Number+ext.js';        // ๐Ÿ‘” Number+ext
import { vec, polar } from './Vector.js';     // ๐Ÿ‘” Vector
import { range } from './Iterable+ext.js';    // ๐Ÿ‘” Iterable+ext
// -----------------------------------------------------------------


// โญ HTMLCanvasElement + extensions
// -----------------------------------------------------------------
// ๐Ÿ”น .draw2D()
//
Object.defineProperties(HTMLCanvasElement.prototype, {
    // ๐Ÿ”น .draw2D()
    draw2D: {
        value: function(draw){
            draw(this.getContext("2d"));
        },
    },
});

// โญ CanvasRenderingContext2D + extensions
// -----------------------------------------------------------------
// ๐Ÿ”น .moveToPoint()
// -----------------------------------------------------------------
// โญ .drawPath()         // general method
// ๐Ÿ”น .drawArc()
// ๐Ÿ”น .drawCircle()
// ๐Ÿ”น .drawWedge()
// ๐Ÿ”น .drawEllipse()
// -----------------------------------------------------------------
// ๐Ÿ”น .drawArcTo()        // for rounded corners
// ๐Ÿ”น .drawPolylineTo()
// ๐Ÿ”น .drawQuadraticCurveTo()
// ๐Ÿ”น .drawCubicBezierCurveTo()
// -----------------------------------------------------------------
// ๐Ÿ”น .drawRect()
// ๐Ÿ”น .drawPolygon()
//
Object.defineProperties(CanvasRenderingContext2D.prototype, {

    // ๐Ÿ”น .moveToPoint()
    moveToPoint: {
        value: function(p, {
            newPath=true,        // begin new subpath ? (default = true)
        }={}) {
            if (newPath) this.beginPath();
            this.moveTo(...p.coords);        // ๐Ÿ‘” Vector
        }
    },

    // โญ .drawPath()
    drawPath: {
        value: function(draw, {
            // path
            newPath = true,        // begin new subpath ?
            closePath = true,      // close subpath ?
            // fill/stroke
            fill = true,
            stroke = true,
            fillStyle,
            strokeStyle,
        }={}){
            
            // new subpath ?
            if (newPath) this.beginPath();    // start new path if necessary
            
            // draw path
            draw();

            // close path ?
            if (closePath) this.closePath();
            
            // fill ?
            if (fill) {
                if (fillStyle) this.fillStyle = fillStyle;
                this.fill();
            };
            
            // stroke ?
            if (stroke) {
                if (strokeStyle) this.strokeStyle = strokeStyle;
                this.stroke()
            };
            
        },
    },

    // --------------------------
    //     arc/circle related
    // --------------------------
    
    // ๐Ÿ”น .drawArc()
    drawArc: {
        value: function(center, radius, {
            // path: arc related
            start = 0,            // start angle
            end = 2 * PI,         // end angle
            clockwise = true,
            // path: general
            newPath = true,       // start new path?
            closePath = true,
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.arc(...center.coords, radius, start, end, !clockwise);
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawArcTo()
    // - for rounded corners
    drawArcTo: {
        value: function(control, target, radius, {
            // arc specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path?
            closePath = false,
            // fill/stroke
            fill = false,          // fill?
            stroke = false,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect arc
                this.arcTo(...control.coords, ...target.coords, radius);
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawCircle()
    drawCircle: {
        value: function(center, radius, {
            // path: general
            newPath = true,       // start new path?
            closePath = true,
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.arc(...center.coords, radius, 0, 2*PI);
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawEllipse()
    drawEllipse: {
        value: function(center, radiusX, radiusY, {
            // path: ellipse related
            start = 0,
            end = 2 * PI,
            rotation = 0,
            clockwise = true,
            // path: general
            newPath = true,
            closePath = true,
            // fill/stroke
            fill = true,
            stroke = true,
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.ellipse(
                    ...center.coords, radiusX, radiusY, 
                    rotation, start, end, !clockwise
                );
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawWedge()
    drawWedge: {
        value: function(center, radius, {
            // path: arc related
            start = 0,            // start angle
            end = 90 * deg,       // end angle    // ๐Ÿ‘” Number+ext
            clockwise = true,
            // path: general
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.moveTo(...center.coords);           // center
                // โญ arc() adds a line from `center` to arc start.
                this.arc(...center.coords, radius, start, end, !clockwise);
                // this.closePath();                        // add line back to center
            }, { newPath: true, closePath: true, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // --------------------------
    //     bezier curve
    // --------------------------

    // ๐Ÿ”น .drawQuadraticCurveTo()
    drawQuadraticCurveTo: {
        value: function(control, target, {
            // bezier curve specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path?
            closePath = false,
            // fill/stroke
            fill = false,          // fill?
            stroke = false,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect bezier curve
                this.quadraticCurveTo(...control.coords, ...target.coords);
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawCubicBezierCurveTo()
    drawCubicBezierCurveTo: {
        value: function(control1, control2, target, {
            // bezier curve specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path ?    (default: false)
            closePath = false,     // close subpath ?     (default: false)
            // fill/stroke
            fill = false,          // fill ?              (default: false)
            stroke = false,        // stroke ?            (default: false)
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect bezier curve
                this.bezierCurveTo(
                    ...control1.coords, ...control2.coords, 
                    ...target.coords,
                );
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // --------------------------
    //     polygon
    // --------------------------

    // ๐Ÿ”น .drawRect()
    drawRect: {
        value: function(rect, {
            // path: general
            newPath = true,       // start new path?
            closePath = true,
            // fill/stroke
            fill = true,          // fill?
            stroke = true,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                this.rect(...rect.coords);        // ๐Ÿ‘” Rect
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawPolygon()
    drawPolygon: {
        value: function(n, {
            // polygon specific
            center = vec(100, 100),   // ๐Ÿ‘” Vector
            radius = 50,
            rotate = 0,               // rotation
            clockwise = true,         // path direction
            // path: general
            newPath = true,           // start new path?
            closePath = true,
            // fill/stroke
            fill = true,              // fill?
            stroke = true,            // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                
                // dฮธ = ยฑ 2ฯ€ / n
                const dt = 2 * Math.PI / n * (clockwise ? 1 : -1);
                // start angle
                const a0 = -90 * deg + rotate + (n.isEven ? dt/2 : 0);

                // vertices
                const points = range(0, n-1)
                    .map(i => center.plus(polar(radius, a0 + dt * i)))
                    .array;
                
                // draw polyline
                this.drawPolylineTo(points.slice(1), {
                    start: points[0],
                });
                
            }, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },

    // ๐Ÿ”น .drawPolylineTo()
    drawPolylineTo: {
        value: function(points, {
            // polyline specific
            start,                 // start point
            // path: general
            newPath = false,       // start new path?
            closePath = false,
            // fill/stroke
            fill = false,          // fill?
            stroke = false,        // stroke?
            fillStyle,
            strokeStyle,
        }={}){
            this.drawPath(() => {
                // move to start point if necessary
                if (start) this.moveToPoint(start, {newPath});
                // connect arc
                points.forEach(p => {
                    this.lineTo(...p.coords);
                });
            }, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
        },
    },
    
});

// โญ export
// -----------------------------------------------------------------
export {};        // ES module export

replit โŸฉ , -> require Rect, Vector, Number+ext, Iterable+ext

๐ŸŒ
๐Ÿ”ณ
๐Ÿ‘”
Canvas+ext
๐Ÿ‘‰
browser
canvas
CanvasRenderingContext2D
HTMLCanvasElement
Canvas+ext (v1.1)