Canvas.st 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038
  1. Smalltalk current createPackage: 'Canvas' properties: #{}!
  2. Object subclass: #HTMLCanvas
  3. instanceVariableNames: 'root'
  4. package: 'Canvas'!
  5. !HTMLCanvas methodsFor: 'accessing'!
  6. root
  7. ^root
  8. !
  9. root: aTagBrush
  10. root := aTagBrush
  11. !
  12. snippet: anElement
  13. "Adds clone of anElement, finds [data-snippet=""*""] subelement
  14. and returns TagBrush as if that subelement was just added.
  15. Rarely needed to use directly, use `html foo` dynamically installed method
  16. for a snippet named foo."
  17. | clone caret |
  18. clone := anElement asJQuery clone.
  19. self with: (TagBrush fromJQuery: clone canvas: self).
  20. caret := clone find: '[data-snippet="*"]'.
  21. caret toArray isEmpty ifTrue: [ caret := clone ].
  22. ^TagBrush fromJQuery: (caret removeAttr: 'data-snippet') canvas: self
  23. ! !
  24. !HTMLCanvas methodsFor: 'adding'!
  25. with: anObject
  26. ^self root with: anObject
  27. ! !
  28. !HTMLCanvas methodsFor: 'initialization'!
  29. initialize
  30. super initialize.
  31. root ifNil: [root := TagBrush fromString: 'div' canvas: self]
  32. !
  33. initializeFromJQuery: aJQuery
  34. root := TagBrush fromJQuery: aJQuery canvas: self
  35. ! !
  36. !HTMLCanvas methodsFor: 'tags'!
  37. a
  38. ^self tag: 'a'
  39. !
  40. abbr
  41. ^self tag: 'abbr'
  42. !
  43. address
  44. ^self tag: 'address'
  45. !
  46. area
  47. ^self tag: 'area'
  48. !
  49. article
  50. ^self tag: 'article'
  51. !
  52. aside
  53. ^self tag: 'aside'
  54. !
  55. audio
  56. ^self tag: 'audio'
  57. !
  58. base
  59. ^self tag: 'base'
  60. !
  61. blockquote
  62. ^self tag: 'blockquote'
  63. !
  64. body
  65. ^self tag: 'body'
  66. !
  67. br
  68. ^self tag: 'br'
  69. !
  70. button
  71. ^self tag: 'button'
  72. !
  73. canvas
  74. ^self tag: 'canvas'
  75. !
  76. caption
  77. ^self tag: 'caption'
  78. !
  79. cite
  80. ^self tag: 'cite'
  81. !
  82. code
  83. ^self tag: 'code'
  84. !
  85. col
  86. ^self tag: 'col'
  87. !
  88. colgroup
  89. ^self tag: 'colgroup'
  90. !
  91. command
  92. ^self tag: 'command'
  93. !
  94. datalist
  95. ^self tag: 'datalist'
  96. !
  97. dd
  98. ^self tag: 'dd'
  99. !
  100. del
  101. ^self tag: 'del'
  102. !
  103. details
  104. ^self tag: 'details'
  105. !
  106. div
  107. ^self tag: 'div'
  108. !
  109. div: aBlock
  110. ^self div with: aBlock
  111. !
  112. dl
  113. ^self tag: 'dl'
  114. !
  115. dt
  116. ^self tag: 'dt'
  117. !
  118. em
  119. ^self tag: 'em'
  120. !
  121. embed
  122. ^self tag: 'embed'
  123. !
  124. fieldset
  125. ^self tag: 'fieldset'
  126. !
  127. figcaption
  128. ^self tag: 'figcaption'
  129. !
  130. figure
  131. ^self tag: 'figure'
  132. !
  133. footer
  134. ^self tag: 'footer'
  135. !
  136. form
  137. ^self tag: 'form'
  138. !
  139. h1
  140. ^self tag: 'h1'
  141. !
  142. h1: anObject
  143. ^self h1 with: anObject
  144. !
  145. h2
  146. ^self tag: 'h2'
  147. !
  148. h2: anObject
  149. ^ self h2 with: anObject
  150. !
  151. h3
  152. ^self tag: 'h3'
  153. !
  154. h3: anObject
  155. ^self h3 with: anObject
  156. !
  157. h4
  158. ^self tag: 'h4'
  159. !
  160. h4: anObject
  161. ^self h4 with: anObject
  162. !
  163. h5
  164. ^self tag: 'h5'
  165. !
  166. h5: anObject
  167. ^self h5 with: anObject
  168. !
  169. h6
  170. ^self tag: 'h6'
  171. !
  172. h6: anObject
  173. ^self h6 with: anObject
  174. !
  175. head
  176. ^self tag: 'head'
  177. !
  178. header
  179. ^self tag: 'header'
  180. !
  181. hgroup
  182. ^self tag: 'hgroup'
  183. !
  184. hr
  185. ^self tag: 'hr'
  186. !
  187. html
  188. ^self tag: 'html'
  189. !
  190. iframe
  191. ^self tag: 'iframe'
  192. !
  193. iframe: aString
  194. ^self iframe src: aString
  195. !
  196. img
  197. ^self tag: 'img'
  198. !
  199. img: aString
  200. ^self img src: aString
  201. !
  202. input
  203. ^self tag: 'input'
  204. !
  205. label
  206. ^self tag: 'label'
  207. !
  208. legend
  209. ^self tag: 'legend'
  210. !
  211. li
  212. ^self tag: 'li'
  213. !
  214. li: anObject
  215. ^self li with: anObject
  216. !
  217. link
  218. ^self tag: 'link'
  219. !
  220. map
  221. ^self tag: 'map'
  222. !
  223. mark
  224. ^self tag: 'mark'
  225. !
  226. menu
  227. ^self tag: 'menu'
  228. !
  229. meta
  230. ^self tag: 'meta'
  231. !
  232. nav
  233. ^self tag: 'nav'
  234. !
  235. newTag: aString
  236. ^TagBrush fromString: aString canvas: self
  237. !
  238. noscript
  239. ^self tag: 'noscript'
  240. !
  241. object
  242. ^self tag: 'object'
  243. !
  244. ol
  245. ^self tag: 'ol'
  246. !
  247. ol: anObject
  248. ^self ol with: anObject
  249. !
  250. optgroup
  251. ^self tag: 'optgroup'
  252. !
  253. option
  254. ^self tag: 'option'
  255. !
  256. output
  257. ^self tag: 'output'
  258. !
  259. p
  260. ^self tag: 'p'
  261. !
  262. p: anObject
  263. ^self p with: anObject
  264. !
  265. param
  266. ^self tag: 'param'
  267. !
  268. pre
  269. ^self tag: 'pre'
  270. !
  271. progress
  272. ^self tag: 'progress'
  273. !
  274. script
  275. ^self tag: 'script'
  276. !
  277. section
  278. ^self tag: 'section'
  279. !
  280. select
  281. ^self tag: 'select'
  282. !
  283. small
  284. ^self tag: 'small'
  285. !
  286. source
  287. ^self tag: 'source'
  288. !
  289. span
  290. ^self tag: 'span'
  291. !
  292. span: anObject
  293. ^self span with: anObject
  294. !
  295. strong
  296. ^self tag: 'strong'
  297. !
  298. strong: anObject
  299. ^self strong with: anObject
  300. !
  301. style
  302. ^ root addBrush: (StyleTag canvas: self)
  303. !
  304. style: aString
  305. ^ self style with: aString; yourself
  306. !
  307. sub
  308. ^self tag: 'sub'
  309. !
  310. summary
  311. ^self tag: 'summary'
  312. !
  313. sup
  314. ^self tag: 'sup'
  315. !
  316. table
  317. ^self tag: 'table'
  318. !
  319. tag: aString
  320. ^root addBrush: (self newTag: aString)
  321. !
  322. tbody
  323. ^self tag: 'tbody'
  324. !
  325. td
  326. ^self tag: 'td'
  327. !
  328. textarea
  329. ^self tag: 'textarea'
  330. !
  331. tfoot
  332. ^self tag: 'tfoot'
  333. !
  334. th
  335. ^self tag: 'th'
  336. !
  337. thead
  338. ^self tag: 'thead'
  339. !
  340. time
  341. ^self tag: 'time'
  342. !
  343. title
  344. ^self tag: 'title'
  345. !
  346. tr
  347. ^self tag: 'tr'
  348. !
  349. ul
  350. ^self tag: 'ul'
  351. !
  352. ul: anObject
  353. ^self ul with: anObject
  354. !
  355. video
  356. ^self tag: 'video'
  357. ! !
  358. !HTMLCanvas class methodsFor: 'instance creation'!
  359. browserVersion
  360. ^(jQuery at: #browser) version
  361. !
  362. isMSIE
  363. ^((jQuery at: #browser) at: #msie) notNil
  364. !
  365. isMozilla
  366. ^((jQuery at: #browser) at: #mozilla) notNil
  367. !
  368. isOpera
  369. ^((jQuery at: #browser) at: #opera) notNil
  370. !
  371. isWebkit
  372. ^((jQuery at: #browser) at: #webkit) notNil
  373. !
  374. onJQuery: aJQuery
  375. ^self basicNew
  376. initializeFromJQuery: aJQuery;
  377. initialize;
  378. yourself
  379. ! !
  380. Object subclass: #HTMLSnippet
  381. instanceVariableNames: 'snippets'
  382. package: 'Canvas'!
  383. !HTMLSnippet commentStamp!
  384. HTMLSnippet instance is the registry of html snippets.
  385. HTMLSnippet current is the public singleton instance.
  386. At the beginning, it scans the document for any html elements
  387. with 'data-snippet="foo"' attribute and takes them off the document,
  388. remembering them in the store under the specified name.
  389. It also install method #foo into HTMLCanvas dynamically.
  390. Every html snippet should mark a 'caret', a place where contents
  391. can be inserted, by 'data-snippet="*"' (a special name for caret).
  392. For example:
  393. <li data-snippet='menuelement' class='...'><a data-snippet='*'></a></li>
  394. defines a list element with a link inside; the link itself is marked as a caret.
  395. You can later issue
  396. html menuelement href: '/foo'; with: 'A foo'
  397. to insert the whole snippet and directly manipulate the caret, so it renders:
  398. <li class='...'><a href='/foo'>A foo</a></li>
  399. For a self-careting tags (not very useful, but you do not need to fill class etc.
  400. you can use
  401. <div class='lots of classes' attr1='one' attr2='two' data-snippet='*bar'></div>
  402. and in code later do:
  403. html bar with: [ xxx ]
  404. to render
  405. <div class='lots of classes' attr1='one' attr2='two'>...added by xxx...</div>!
  406. !HTMLSnippet methodsFor: 'accessing'!
  407. snippetAt: aString
  408. ^ self snippets at: aString
  409. !
  410. snippets
  411. ^snippets ifNil: [ snippets := #{} ]
  412. ! !
  413. !HTMLSnippet methodsFor: 'initialization'!
  414. initializeFromJQuery: aJQuery
  415. "Finds and takes out all snippets out of aJQuery.
  416. Installs it into self."
  417. (self snippetsFromJQuery: aJQuery) do: [ :each |
  418. self installSnippetFromJQuery: each asJQuery ]
  419. ! !
  420. !HTMLSnippet methodsFor: 'method generation'!
  421. snippetAt: aString compile: anElement
  422. "Method generation for the snippet.
  423. The selector is aString, the method block uses anElement"
  424. ClassBuilder new
  425. installMethod: ([ :htmlReceiver | htmlReceiver snippet: anElement ]
  426. currySelf asCompiledMethod: aString)
  427. forClass: HTMLCanvas
  428. category: '**snippets'
  429. ! !
  430. !HTMLSnippet methodsFor: 'private'!
  431. snippetsFromJQuery: aJQuery
  432. ^ (aJQuery find: '[data-snippet]') toArray
  433. ! !
  434. !HTMLSnippet methodsFor: 'snippet installation'!
  435. installSnippetFromJQuery: element
  436. | name |
  437. name := element attr: 'data-snippet'.
  438. name = '*' ifFalse: [
  439. ('^\*' asRegexp test: name)
  440. ifTrue: [
  441. name := name allButFirst.
  442. element attr: 'data-snippet' put: '*' ]
  443. ifFalse: [
  444. element removeAttr: 'data-snippet' ].
  445. self snippetAt: name install: (element detach get: 0) ]
  446. !
  447. snippetAt: aString install: anElement
  448. self snippets at: aString put: anElement.
  449. self snippetAt: aString compile: anElement
  450. ! !
  451. HTMLSnippet class instanceVariableNames: 'current'!
  452. !HTMLSnippet class methodsFor: 'initialization'!
  453. ensureCurrent
  454. current ifNil: [
  455. current := super new
  456. initializeFromJQuery: document asJQuery;
  457. yourself ]
  458. !
  459. initialize
  460. super initialize.
  461. self ensureCurrent
  462. ! !
  463. !HTMLSnippet class methodsFor: 'instance creation'!
  464. current
  465. ^ current
  466. !
  467. new
  468. self shouldNotImplement
  469. ! !
  470. Object subclass: #TagBrush
  471. instanceVariableNames: 'canvas element'
  472. package: 'Canvas'!
  473. !TagBrush methodsFor: 'accessing'!
  474. element
  475. ^element
  476. ! !
  477. !TagBrush methodsFor: 'adding'!
  478. addBrush: aTagBrush
  479. self appendChild: aTagBrush element.
  480. ^aTagBrush
  481. !
  482. append: anObject
  483. anObject appendToBrush: self
  484. !
  485. appendBlock: aBlock
  486. | root |
  487. root := canvas root.
  488. canvas root: self.
  489. aBlock value: canvas.
  490. canvas root: root
  491. !
  492. appendChild: anElement
  493. "In IE7 and IE8 appendChild fails on several node types. So we need to check"
  494. <var element=self['@element'];
  495. if (null == element.canHaveChildren || element.canHaveChildren) {
  496. element.appendChild(anElement);
  497. } else {
  498. element.text = String(element.text) + anElement.innerHTML;
  499. } >
  500. !
  501. appendString: aString
  502. self appendChild: (self createTextNodeFor: aString)
  503. !
  504. appendToBrush: aTagBrush
  505. aTagBrush addBrush: self
  506. !
  507. contents: anObject
  508. self
  509. empty;
  510. append: anObject
  511. !
  512. empty
  513. self asJQuery empty
  514. !
  515. with: anObject
  516. self append: anObject
  517. ! !
  518. !TagBrush methodsFor: 'attributes'!
  519. accesskey: aString
  520. self at: 'accesskey' put: aString
  521. !
  522. action: aString
  523. self at: 'action' put: aString
  524. !
  525. align: aString
  526. self at: 'align' put: aString
  527. !
  528. alt: aString
  529. self at: 'alt' put: aString
  530. !
  531. at: aString put: aValue
  532. <self['@element'].setAttribute(aString, aValue)>
  533. !
  534. class: aString
  535. <self['@element'].className = aString>
  536. !
  537. cols: aString
  538. self at: 'cols' put: aString
  539. !
  540. contenteditable: aString
  541. self at: 'contenteditable' put: aString
  542. !
  543. contextmenu: aString
  544. self at: 'contextmenu' put: aString
  545. !
  546. draggable: aString
  547. self at: 'draggable' put: aString
  548. !
  549. for: aString
  550. self at: 'for' put: aString
  551. !
  552. height: aString
  553. self at: 'height' put: aString
  554. !
  555. hidden
  556. self at: 'hidden' put: 'hidden'
  557. !
  558. href: aString
  559. self at: 'href' put: aString
  560. !
  561. id: aString
  562. self at: 'id' put: aString
  563. !
  564. media: aString
  565. self at: 'media' put: aString
  566. !
  567. method: aString
  568. self at: 'method' put: aString
  569. !
  570. name: aString
  571. self at: 'name' put: aString
  572. !
  573. placeholder: aString
  574. self at: 'placeholder' put: aString
  575. !
  576. rel: aString
  577. self at: 'rel' put: aString
  578. !
  579. removeAt: aString
  580. <self['@element'].removeAttribute(aString)>
  581. !
  582. rows: aString
  583. self at: 'rows' put: aString
  584. !
  585. src: aString
  586. self at: 'src' put: aString
  587. !
  588. style: aString
  589. self at: 'style' put: aString
  590. !
  591. tabindex: aNumber
  592. self at: 'tabindex' put: aNumber
  593. !
  594. target: aString
  595. self at: 'target' put: aString
  596. !
  597. title: aString
  598. self at: 'title' put: aString
  599. !
  600. type: aString
  601. self at: 'type' put: aString
  602. !
  603. valign: aString
  604. self at: 'valign' put: aString
  605. !
  606. value: aString
  607. self at: 'value' put: aString
  608. !
  609. width: aString
  610. self at: 'width' put: aString
  611. ! !
  612. !TagBrush methodsFor: 'converting'!
  613. asJQuery
  614. ^window jQuery: self element
  615. ! !
  616. !TagBrush methodsFor: 'events'!
  617. onBlur: aBlock
  618. self asJQuery bind: 'blur' do: aBlock
  619. !
  620. onChange: aBlock
  621. self asJQuery bind: 'change' do: aBlock
  622. !
  623. onClick: aBlock
  624. self asJQuery bind: 'click' do: aBlock
  625. !
  626. onDblClick: aBlock
  627. self asJQuery bind: 'dblclick' do: aBlock
  628. !
  629. onFocus: aBlock
  630. self asJQuery bind: 'focus' do: aBlock
  631. !
  632. onFocusIn: aBlock
  633. self asJQuery bind: 'focusin' do: aBlock
  634. !
  635. onFocusOut: aBlock
  636. self asJQuery bind: 'focusout' do: aBlock
  637. !
  638. onHover: aBlock
  639. self asJQuery bind: 'hover' do: aBlock
  640. !
  641. onKeyDown: aBlock
  642. self asJQuery bind: 'keydown' do: aBlock
  643. !
  644. onKeyPress: aBlock
  645. self asJQuery bind: 'keypress' do: aBlock
  646. !
  647. onKeyUp: aBlock
  648. self asJQuery bind: 'keyup' do: aBlock
  649. !
  650. onMouseDown: aBlock
  651. self asJQuery bind: 'mousedown' do: aBlock
  652. !
  653. onMouseEnter: aBlock
  654. self asJQuery bind: 'mouseenter' do: aBlock
  655. !
  656. onMouseLeave: aBlock
  657. self asJQuery bind: 'mouseleave' do: aBlock
  658. !
  659. onMouseMove: aBlock
  660. self asJQuery bind: 'mousemove' do: aBlock
  661. !
  662. onMouseOut: aBlock
  663. self asJQuery bind: 'mouseout' do: aBlock
  664. !
  665. onMouseOver: aBlock
  666. self asJQuery bind: 'mouseover' do: aBlock
  667. !
  668. onMouseUp: aBlock
  669. self asJQuery bind: 'mouseup' do: aBlock
  670. !
  671. onSelect: aBlock
  672. self asJQuery bind: 'select' do: aBlock
  673. !
  674. onSubmit: aBlock
  675. self asJQuery bind: 'submit' do: aBlock
  676. !
  677. onUnload: aBlock
  678. self asJQuery bind: 'unload' do: aBlock
  679. ! !
  680. !TagBrush methodsFor: 'initialization'!
  681. initializeFromJQuery: aJQuery canvas: aCanvas
  682. element := aJQuery get: 0.
  683. canvas := aCanvas
  684. !
  685. initializeFromString: aString canvas: aCanvas
  686. element := self createElementFor: aString.
  687. canvas := aCanvas
  688. ! !
  689. !TagBrush methodsFor: 'private'!
  690. createElementFor: aString
  691. <return document.createElement(String(aString))>
  692. !
  693. createTextNodeFor: aString
  694. <return document.createTextNode(String(aString))>
  695. ! !
  696. !TagBrush class methodsFor: 'instance creation'!
  697. fromJQuery: aJQuery canvas: aCanvas
  698. ^self new
  699. initializeFromJQuery: aJQuery canvas: aCanvas;
  700. yourself
  701. !
  702. fromString: aString canvas: aCanvas
  703. ^self new
  704. initializeFromString: aString canvas: aCanvas;
  705. yourself
  706. ! !
  707. TagBrush subclass: #StyleTag
  708. instanceVariableNames: 'canvas element'
  709. package: 'Canvas'!
  710. !StyleTag commentStamp!
  711. I'm a <style> tag use to inline CSS or load a stylesheet.
  712. For inlining handle IE compatibility problems.!
  713. !StyleTag methodsFor: 'adding'!
  714. with: aString
  715. HTMLCanvas isMSIE
  716. ifTrue: [self element styleSheet cssText: aString ]
  717. ifFalse: [super with: aString ].
  718. ! !
  719. !StyleTag class methodsFor: 'instance creation'!
  720. canvas: aCanvas
  721. ^self new
  722. initializeFromString: 'style' canvas: aCanvas;
  723. yourself
  724. ! !
  725. Object subclass: #Widget
  726. instanceVariableNames: ''
  727. package: 'Canvas'!
  728. !Widget methodsFor: 'adding'!
  729. appendToBrush: aTagBrush
  730. self appendToJQuery: aTagBrush asJQuery
  731. !
  732. appendToJQuery: aJQuery
  733. self renderOn: (HTMLCanvas onJQuery: aJQuery)
  734. ! !
  735. !Widget methodsFor: 'rendering'!
  736. renderOn: html
  737. self
  738. ! !
  739. !Object methodsFor: '*Canvas'!
  740. appendToBrush: aTagBrush
  741. aTagBrush append: self asString
  742. !
  743. appendToJQuery: aJQuery
  744. aJQuery append: self asString
  745. ! !
  746. !BlockClosure methodsFor: '*Canvas'!
  747. appendToBrush: aTagBrush
  748. aTagBrush appendBlock: self
  749. !
  750. appendToJQuery: aJQuery
  751. self value: (HTMLCanvas onJQuery: aJQuery)
  752. ! !
  753. !CharacterArray methodsFor: '*Canvas'!
  754. asSnippet
  755. ^ HTMLSnippet current snippetAt: self asString
  756. ! !
  757. !String methodsFor: '*Canvas'!
  758. appendToBrush: aTagBrush
  759. aTagBrush appendString: self
  760. !
  761. appendToJQuery: aJQuery
  762. aJQuery append: self
  763. !
  764. asJQuery
  765. <return jQuery(String(self))>
  766. ! !
  767. !JSObjectProxy methodsFor: '*Canvas'!
  768. asJQuery
  769. <return jQuery(self['@jsObject'])>
  770. ! !