Guest User

Untitled

a guest
Dec 10th, 2018
93
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.92 KB | None | 0 0
  1. package com.hit.display.ui
  2. {
  3. import flash.display.BitmapData;
  4. import flash.display.DisplayObject;
  5. import flash.display.DisplayObjectContainer;
  6. import flash.display.Loader;
  7. import flash.display.LoaderInfo;
  8. import flash.display.MovieClip;
  9. import flash.display.SimpleButton;
  10. import flash.display.SpreadMethod;
  11. import flash.display.Sprite;
  12. import flash.events.Event;
  13. import flash.events.IOErrorEvent;
  14. import flash.events.MouseEvent;
  15. import flash.net.URLRequest;
  16. import flash.system.ApplicationDomain;
  17. import flash.text.Font;
  18. import flash.text.TextField;
  19. import flash.text.TextFormat;
  20. import flash.utils.getQualifiedClassName;
  21. import flash.utils.Dictionary;
  22.  
  23. import com.greensock.TweenNano;
  24. import com.greensock.easing.Back;
  25. import com.greensock.easing.Bounce;
  26. import com.greensock.easing.Linear;
  27.  
  28. import com.hit.events.DialogEvent;
  29.  
  30. /**
  31. * Dialog
  32. *
  33. * TODO: Change _defaultTween to be a function instead. Have the class use Linear.easeIn as a
  34. * default. The user already needs greensock's library anyways, might as well
  35. * streamline the handling of the tweens.
  36. * TODO: Allow the user to access and modify the TextFields (HeaderText, WindowText and Button)
  37. * TODO?: Allow the user to manually position the window
  38. *
  39. * @author Guyllaume Cardinal
  40. */
  41. public class Dialog extends Sprite
  42. {
  43. // Name constant, used so no constant shares the same name as an other class' constants
  44. private static const NAME:String = "Dialog";
  45.  
  46. // Components URL. Dictates to the class where to fetch the SWF used to create the window
  47. // Will be used by default.
  48. private static const ASSETS_URL:String = "components/DialogAssets/DialogAssets.swf";
  49.  
  50. // Position constants
  51. public static const TOP:String = NAME + "Top";
  52. public static const BOTTOM:String = NAME + "Bottom";
  53. public static const LEFT:String = NAME + "Left";
  54. public static const RIGHT:String = NAME + "Right";
  55. public static const CENTER:String = NAME + "Center";
  56.  
  57. // Function Strings
  58. public static const SHOW_FUNCTION:String = "show";
  59. public static const UPDATE_FUNCTION:String = "update";
  60. public static const HIDE_FUNCTION:String = "hide";
  61.  
  62.  
  63. private var _dialog:Dialog;
  64. // Window graphics
  65. private var _headerGraphic:MovieClip;
  66. private var _windowGraphic:MovieClip;
  67. private var _closeButton:SimpleButton;
  68. private var _overlayGraphic:Sprite;
  69. private var _buttonContainer:Sprite;
  70. // Button classes
  71. private var ButtonUpGraphic:Class;
  72. private var ButtonOverGraphic:Class;
  73. private var ButtonDownGraphic:Class;
  74.  
  75. // Component parameters
  76. private var _parentContainer:DisplayObjectContainer;
  77. private var _window:Sprite;
  78. private var _text:String;
  79. private var _title:String;
  80. // Component texts
  81. private var _headerFormat:TextFormat;
  82. private var _windowFormat:TextFormat;
  83. private var _headerText:TextField;
  84. private var _windowText:TextField;
  85. private var _textFont:Font;
  86. private var _selectable:Boolean;
  87. // Optional parameters
  88. private var _draggable:Boolean;
  89. private var _autoShow:Boolean;
  90. private var _position:String;
  91. private var _titleIcon:DisplayObject;
  92. private var _textIcon:DisplayObject;
  93. private var _defaultTween:Function;
  94. private var _buttons:Dictionary;
  95. private var _padding:int;
  96.  
  97. // Control variables
  98. private var _ready:Boolean;
  99. private var _loading:Boolean;
  100. private var _shown:Boolean;
  101.  
  102.  
  103. /**
  104. *
  105. * @param parent DisplayObjectContainer The DisplayObjectContainer in which to add
  106. * or remove the Dialog
  107. * @param title String The title of the Dialog
  108. * @param text String
  109. * @param options Object
  110. */
  111. public function Dialog(parent:DisplayObjectContainer, title:String, text:String,
  112. options:Object = null ):void
  113. {
  114. _dialog = this as Dialog;
  115.  
  116. // Store the required parameters
  117. _parentContainer = parent;
  118. _text = text;
  119. _title = title;
  120. // Register the optional parameters, setting up the default values if none was passed
  121. registerOptionalParameters(options);
  122.  
  123. // Load the swf with the components to draw the actual window
  124. var assetsLoader:Loader = new Loader();
  125. assetsLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
  126. assetsLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, assetsLoadedhandler);
  127. assetsLoader.load(new URLRequest(ASSETS_URL));
  128. _loading = true;
  129. }
  130.  
  131.  
  132. /**
  133. *
  134. * @param tweenType
  135. */
  136. public function show():void
  137. {
  138. _parentContainer.addChild(_dialog);
  139.  
  140. // Configue and tweeen the overlay and window
  141. TweenNano.from(_overlayGraphic, 0.5, { alpha:0, ease:_defaultTween } );
  142. TweenNano.from(_window, 0.6, { width:0, height:0, ease:_defaultTween } );
  143.  
  144. // Allow the window to be dragged if the option is selected
  145. if (_draggable)
  146. {
  147. _headerGraphic.buttonMode = true;
  148. _headerGraphic.addEventListener(MouseEvent.MOUSE_DOWN, headerMouseDownHandler, false, 0, true);
  149. this.stage.addEventListener(MouseEvent.MOUSE_UP, headerMouseUpHandler, false, 0, true);
  150. }
  151.  
  152. this.dispatchEvent(new DialogEvent(DialogEvent.SHOWN));
  153. _shown = true;
  154. }
  155.  
  156.  
  157. /**
  158. *
  159. * @param event
  160. */
  161. public function hide(event:MouseEvent = null):void
  162. {
  163. if (_draggable)
  164. {
  165. _headerGraphic.removeEventListener(MouseEvent.MOUSE_DOWN, headerMouseDownHandler);
  166. this.stage.removeEventListener(MouseEvent.MOUSE_UP, headerMouseUpHandler);
  167. }
  168.  
  169. TweenNano.to(_overlayGraphic, 0.3, { alpha:0, ease:_defaultTween } );
  170. TweenNano.to(_window, 0.6, { width:0, height:0, alpha: 0, ease:_defaultTween,
  171. onComplete: function() {
  172. _parentContainer.removeChild(_dialog);
  173. } } );
  174.  
  175. this.dispatchEvent(new DialogEvent(DialogEvent.HIDDEN));
  176. _shown = false;
  177. }
  178.  
  179.  
  180. /**
  181. *
  182. */
  183. public function update():void
  184. {
  185. draw();
  186. this.dispatchEvent(new DialogEvent(DialogEvent.UPDATED));
  187. }
  188.  
  189.  
  190. /**
  191. *
  192. * @param event
  193. */
  194. private function assetsLoadedhandler(event:Event):void
  195. {
  196. dispatchEvent(new DialogEvent(DialogEvent.LOADED));
  197. _loading = false;
  198.  
  199. configUI(event.target as LoaderInfo);
  200. draw();
  201.  
  202. dispatchEvent(new DialogEvent(DialogEvent.READY));
  203. _ready = true;
  204.  
  205. if (_autoShow) show();
  206. }
  207.  
  208.  
  209. /**
  210. *
  211. * @param loaderContent
  212. */
  213. private function configUI(loaderContent:LoaderInfo = null):void
  214. {
  215. if (loaderContent)
  216. registerComponents(loaderContent);
  217.  
  218. _window = new Sprite();
  219.  
  220. // Setup the header
  221. _window.addChild(_headerGraphic);
  222.  
  223. // Setup the main window
  224. _window.addChild(_windowGraphic);
  225. _window.addChild(_closeButton);
  226.  
  227. // Adds the icon in the header if one is supplied
  228. if (_titleIcon)
  229. _headerGraphic.addChild(_titleIcon);
  230.  
  231. if (_textIcon)
  232. _windowGraphic.addChild(_textIcon);
  233.  
  234. if (_buttons)
  235. {
  236. _buttonContainer = new Sprite();
  237. _windowGraphic.addChild(_buttonContainer);
  238. }
  239.  
  240. // Textfields
  241. _headerText = new TextField();
  242. _windowText = new TextField();
  243. _headerGraphic.addChild(_headerText);
  244. _windowGraphic.addChild(_windowText);
  245.  
  246. // background overlay
  247. _overlayGraphic = new Sprite();
  248. _overlayGraphic.graphics.beginBitmapFill(new BitmapData(_parentContainer.width,
  249. _parentContainer.height, false,
  250. 0x000000), null, false);
  251. _overlayGraphic.graphics.drawRect(0, 0, _parentContainer.width, _parentContainer.height);
  252. _overlayGraphic.graphics.endFill();
  253. _overlayGraphic.alpha = 0.5;
  254. this.addChildAt(_overlayGraphic, 0);
  255.  
  256. this.addChild(_window);
  257. }
  258.  
  259.  
  260. /**
  261. *
  262. */
  263. private function draw():void
  264. {
  265. // Print the texts
  266. drawTexts();
  267.  
  268. // Header
  269. _headerText.y = _headerGraphic.height / 2 - _headerText.height / 2;
  270. if (_titleIcon)
  271. {
  272. _titleIcon.x = _padding;
  273. _titleIcon.y = _headerText.y + _headerText.height / 2 - _titleIcon.height / 2;
  274. _headerText.x = _titleIcon.x + _titleIcon.width + _padding;
  275. }
  276. else
  277. {
  278. _headerText.x = _padding;
  279. }
  280.  
  281. // Main window
  282. _windowText.y = _padding;
  283. if (_textIcon)
  284. {
  285. _textIcon.x = _padding;
  286. _textIcon.y = _padding;
  287. _windowText.x = _textIcon.x + _textIcon.width + _padding;
  288. // _padding * 3 because we have padding on the left, right and between the text and icon
  289. _windowText.width = _window.width - _textIcon.width - _padding * 3;
  290. }
  291. else
  292. {
  293. _windowText.x = _padding;
  294. _windowText.width = _windowGraphic.width - _padding * 2;
  295. }
  296.  
  297. // Position the elements
  298. _windowGraphic.y = _headerGraphic.y + _headerGraphic.height;
  299. _closeButton.y = _headerGraphic.height / 2 - _closeButton.height / 2;
  300. _closeButton.x = _headerGraphic.width - _closeButton.width - _padding;
  301.  
  302. // Buttons
  303. if (_buttons)
  304. {
  305. var buttonIterator:int = 0;
  306. for (var key:Object in _buttons)
  307. {
  308. var button:Sprite = createButton(_buttons[key]);
  309. button.mouseChildren = false;
  310.  
  311. var buttonFunction:Function;
  312. if (_buttons[key].callback is Function)
  313. buttonFunction = _buttons[key].callback as Function;
  314. else
  315. buttonFunction = this[_buttons[key].callback] as Function;
  316.  
  317. button.addEventListener(MouseEvent.CLICK, buttonFunction);
  318. button.x = (button.width + _padding) * buttonIterator;
  319. _buttonContainer.addChild(button)
  320. buttonIterator++;
  321. }
  322. _buttonContainer.x = _windowGraphic.width - _buttonContainer.width - _padding;
  323. _buttonContainer.y = _windowText.y + _windowText.height + _padding;
  324. }
  325.  
  326. _windowGraphic.getChildAt(0).height = _windowGraphic.height + _padding * 2;
  327.  
  328. // Center the registration point
  329. var halfHeight:int = _window.height / 2;
  330. var halfWidth:int = _window.width / 2;
  331. var i:int = 0;
  332. var n:int = _window.numChildren;
  333. for ( ; i < n; i++ )
  334. {
  335. var obj:DisplayObject = _window.getChildAt(i);
  336. obj.x -= halfWidth;
  337. obj.y -= halfHeight;
  338. }
  339.  
  340. // Set the initial position of the Dialog window
  341. switch (_position)
  342. {
  343. case Dialog.CENTER:
  344. _window.x = _parentContainer.width / 2;
  345. _window.y = _parentContainer.height / 2;
  346. break;
  347.  
  348. case Dialog.LEFT:
  349. _window.x = _window.width / 2;
  350. _window.y = _parentContainer.height / 2;
  351. break;
  352.  
  353. case Dialog.RIGHT:
  354. _window.x = _parentContainer.width - _window.width / 2;
  355. _window.y = _parentContainer.height / 2;
  356. break;
  357.  
  358. case Dialog.TOP:
  359. _window.x = _parentContainer.width / 2;
  360. _window.y = _window.height / 2;
  361. break;
  362.  
  363. case Dialog.BOTTOM:
  364. _window.x = _parentContainer.width / 2;
  365. _window.y = _parentContainer.height - _window.height / 2;
  366. break;
  367.  
  368. default:
  369. _window.x = _parentContainer.width / 2;
  370. _window.y = _parentContainer.height / 2;
  371. }
  372.  
  373. // Add the eventListeners
  374. _closeButton.addEventListener(MouseEvent.CLICK, hide);
  375.  
  376. // Launch an event saying the component is drawn
  377. this.dispatchEvent(new DialogEvent(DialogEvent.DRAWN));
  378. }
  379.  
  380.  
  381. /**
  382. *
  383. */
  384. private function drawTexts():void
  385. {
  386. if (!_windowFormat)
  387. _windowFormat = new TextFormat(_textFont ? _textFont.fontName : "_sans", 14, 0x333333);
  388. if (!_headerFormat)
  389. _headerFormat = new TextFormat(_textFont ? _textFont.fontName : "_sans", 16, 0x333333);
  390.  
  391. // Header Text
  392. if (_textFont) _headerText.embedFonts = true;
  393. _headerText.defaultTextFormat = _headerFormat;
  394. _headerText.autoSize = "left";
  395. _headerText.wordWrap = true;
  396. _headerText.htmlText = _title;
  397. _headerText.selectable = false;
  398.  
  399. // Window Text
  400. if (_textFont) _windowText.embedFonts = true;
  401. _windowText.defaultTextFormat = _windowFormat;
  402. _windowText.autoSize = "left";
  403. _windowText.wordWrap = true;
  404. _windowText.htmlText = _text;
  405. _windowText.selectable = _selectable;
  406. }
  407.  
  408.  
  409. /**
  410. *
  411. * @param params
  412. * @return
  413. */
  414. private function createButton(params:Object):Sprite
  415. {
  416. var button:Sprite = new Sprite;
  417. button.buttonMode = true;
  418. var up:Sprite = new ButtonUpGraphic();
  419. var over:Sprite = new ButtonOverGraphic();
  420. var down:Sprite = new ButtonDownGraphic();
  421.  
  422. var fmt:TextFormat = new TextFormat(_textFont ? _textFont.fontName : "_sans", 16, 0x333333);
  423. fmt.align = "center";
  424. var buttonLabel:TextField = new TextField();
  425. if (_textFont) buttonLabel.embedFonts = true;
  426. buttonLabel.text = params.label
  427. buttonLabel.autoSize = "center";
  428. buttonLabel.mouseEnabled = false;
  429. buttonLabel.x = buttonLabel.y = 0;
  430. buttonLabel.setTextFormat(fmt);
  431.  
  432. buttonLabel.x = up.width / 2 - buttonLabel.width / 2;
  433. buttonLabel.y = up.height / 2 - buttonLabel.height / 2;
  434.  
  435. over.visible = down.visible = false;
  436.  
  437. button.addChild(up);
  438. button.addChild(over);
  439. button.addChild(down);
  440. button.addChild(buttonLabel);
  441.  
  442. button.addEventListener(MouseEvent.MOUSE_DOWN, buttonDownHandler);
  443. button.addEventListener(MouseEvent.MOUSE_OVER, buttonOverHandler);
  444. button.addEventListener(MouseEvent.MOUSE_UP, buttonUpHandler);
  445. button.addEventListener(MouseEvent.MOUSE_OUT, buttonUpHandler);
  446.  
  447. up.name = "up";
  448. over.name = "over";
  449. down.name = "down";
  450. buttonLabel.name = "buttonLabel";
  451.  
  452. return button;
  453. }
  454.  
  455.  
  456. /**
  457. *
  458. * @param loaderContent
  459. */
  460. private function registerComponents(loaderContent:LoaderInfo):void
  461. {
  462. var swfLibrary:ApplicationDomain = loaderContent.applicationDomain;
  463.  
  464. // Window Header
  465. var HeaderGraphic:Class = swfLibrary.getDefinition("HeaderGraphic") as Class;
  466. _headerGraphic = new HeaderGraphic() as MovieClip;
  467.  
  468. // Main Window Background
  469. var WindowGraphic:Class = swfLibrary.getDefinition("WindowGraphic") as Class;
  470. _windowGraphic = new WindowGraphic() as MovieClip;
  471.  
  472. // Close Button
  473. var CloseButton:Class = swfLibrary.getDefinition("CloseButton") as Class;
  474. _closeButton = new CloseButton() as SimpleButton;
  475.  
  476. // Button graphics
  477. ButtonUpGraphic = swfLibrary.getDefinition("ButtonUpGraphic") as Class;
  478. ButtonOverGraphic = swfLibrary.getDefinition("ButtonOverGraphic") as Class;
  479. ButtonDownGraphic = swfLibrary.getDefinition("ButtonDownGraphic") as Class;
  480. }
  481.  
  482.  
  483. /**
  484. *
  485. * @param parameters
  486. */
  487. private function registerOptionalParameters(parameters:Object):void
  488. {
  489.  
  490. if (parameters && parameters.autoShow)
  491. {
  492. if (parameters.autoShow is Boolean)
  493. {
  494. _autoShow = parameters.autoShow;
  495. }
  496. else
  497. {
  498. trace("WARNING: Invalid value (non-Boolean) used in [autoShow], defaulting to false");
  499. _autoShow = false;
  500. }
  501. }
  502. else _autoShow = false;
  503.  
  504.  
  505. if (parameters && parameters.draggable)
  506. {
  507. if (parameters.draggable is Boolean)
  508. {
  509. _draggable = parameters.draggable;
  510. }
  511. else
  512. {
  513. trace("WARNING: Invalid value (non-Boolean) used in [draggable], defaulting to false");
  514. _draggable = false;
  515. }
  516. }
  517. else _draggable = false;
  518.  
  519.  
  520. if (parameters && parameters.position)
  521. {
  522. if (parameters.position == Dialog.CENTER || parameters.position == Dialog.TOP ||
  523. parameters.position == Dialog.BOTTOM || parameters.position == Dialog.LEFT ||
  524. parameters.position == Dialog.RIGHT)
  525. {
  526. _position = parameters.position;
  527. }
  528. else
  529. {
  530. trace("WARNING: Invalid value used in [position], defaulting to Dialog.CENTER");
  531. _position = Dialog.CENTER;
  532. }
  533. }
  534. else _position = Dialog.CENTER;
  535.  
  536.  
  537. if (parameters && parameters.defaultTween)
  538. {
  539. if (parameters.defaultTween is Function)
  540. {
  541. _defaultTween = parameters.defaultTween;
  542. }
  543. else
  544. {
  545. trace("WARNING: Invalid value (not a greensock easing function) used in [defaultTween], defaulting to Linear.easeIn");
  546. _defaultTween = Linear.easeIn;
  547. }
  548. }
  549. else _defaultTween = Linear.easeIn;
  550.  
  551.  
  552. if (parameters && parameters.titleIcon)
  553. {
  554. if (parameters.titleIcon is DisplayObject)
  555. {
  556. _titleIcon = parameters.titleIcon;
  557. }
  558. else
  559. {
  560. trace("WARNING: The [titleIcon] parameter can only accept a DisplayObject. No icon will be used");
  561. _titleIcon = null;
  562. }
  563. }
  564. else _titleIcon = null;
  565.  
  566.  
  567. if (parameters && parameters.textIcon)
  568. {
  569. if (parameters.textIcon is DisplayObject)
  570. {
  571. _textIcon = parameters.textIcon;
  572. }
  573. else
  574. {
  575. trace("WARNING: The [textIcon] parameter can only accept a DisplayObject. No icon will be used");
  576. _textIcon = null;
  577. }
  578. }
  579. else _textIcon = null;
  580.  
  581.  
  582. if (parameters && parameters.padding)
  583. {
  584. if (parameters.padding is int)
  585. {
  586. _padding = parameters.padding;
  587. }
  588. else
  589. {
  590. trace("WARNING: Invalid value (not an int) used in [padding], defaulting to 10");
  591. _padding = 10;
  592. }
  593. }
  594. else _padding = 10;
  595.  
  596.  
  597. if (parameters && parameters.buttons)
  598. {
  599. if (parameters.buttons is Dictionary)
  600. {
  601. _buttons = parameters.buttons;
  602. }
  603. else
  604. {
  605. trace("WARNING: Invalid value (not a Dictionary) used in [buttons], no button will be created");
  606. }
  607. }
  608.  
  609.  
  610. if (parameters && parameters.windowFormat)
  611. {
  612. if (parameters.windowFormat is TextFormat)
  613. {
  614. _windowFormat = parameters.windowFormat;
  615. }
  616. else
  617. {
  618. trace("WARNING: Invalid value (not a TextFormat) used in [windowFormat], reverting to default TextFormat");
  619. }
  620. }
  621.  
  622.  
  623. if (parameters && parameters.headerFormat)
  624. {
  625. if (parameters.headerFormat is TextFormat)
  626. {
  627. _headerFormat = parameters.headerFormat;
  628. }
  629. else
  630. {
  631. trace("WARNING: Invalid value (not a TextFormat) used in [headerFormat], reverting to default TextFormat");
  632. }
  633. }
  634.  
  635.  
  636. if (parameters && parameters.selectable)
  637. {
  638. if (parameters.selectable is Boolean)
  639. {
  640. _selectable = parameters.selectable;
  641. }
  642. else
  643. {
  644. trace("WARNING: Invalid value (non-Boolean) used in [selectable], text will be selectable");
  645. }
  646. }
  647.  
  648.  
  649. if (parameters && parameters.textFont)
  650. {
  651. if (parameters.textFont is Font)
  652. {
  653. _textFont = parameters.textFont;
  654. }
  655. else
  656. {
  657. trace("WARNING: Invalid value (not a Font) used in [textFont], '_sans' will be used");
  658. }
  659. }
  660. }
  661.  
  662.  
  663. /*****************************************
  664. * INTERMEDIATE FUNCTIONS
  665. */
  666.  
  667. /**
  668. *
  669. * @param event
  670. */
  671. private function buttonOverHandler(event:MouseEvent = null):void
  672. {
  673. event.target.getChildByName("up").visible = event.target.getChildByName("down").visible = false;
  674. event.target.getChildByName("over").visible = true;
  675. }
  676.  
  677. /**
  678. *
  679. * @param event
  680. */
  681. private function buttonUpHandler(event:MouseEvent = null):void
  682. {
  683. event.target.getChildByName("over").visible = event.target.getChildByName("down").visible = false;
  684. event.target.getChildByName("up").visible = true;
  685. }
  686.  
  687. /**
  688. *
  689. * @param event
  690. */
  691. private function buttonDownHandler(event:MouseEvent = null):void
  692. {
  693. event.target.getChildByName("up").visible = event.target.getChildByName("over").visible = false;
  694. event.target.getChildByName("down").visible = true;
  695. }
  696.  
  697. /**
  698. *
  699. * @param event
  700. */
  701. private function headerMouseDownHandler(event:MouseEvent):void
  702. {
  703. _window.startDrag();
  704. }
  705.  
  706. /**
  707. *
  708. * @param event
  709. */
  710. private function headerMouseUpHandler(event:MouseEvent):void
  711. {
  712. _window.stopDrag();
  713. }
  714.  
  715. /**
  716. *
  717. * @param event
  718. */
  719. private function ioErrorHandler(event:IOErrorEvent):void
  720. {
  721. trace("ERROR: Could not load the required swf.");
  722. this.dispatchEvent(new DialogEvent(DialogEvent.IO_ERROR));
  723. }
  724.  
  725.  
  726. /*****************************************************************
  727. * GETTERS / SETTERS
  728. ****************************************************************/
  729.  
  730. /**
  731. *
  732. */
  733. public function get ready():Boolean
  734. {
  735. return _ready;
  736. }
  737.  
  738. public function get shown():Boolean
  739. {
  740. return _shown;
  741. }
  742.  
  743. public function get loading():Boolean
  744. {
  745. return _loading;
  746. }
  747.  
  748. }
  749.  
  750. }
Add Comment
Please, Sign In to add comment