본문 바로가기
개발/dart

6. Defined a Custom Element

by 허허 그림 2014. 3. 2.
728x90

* 전체 링크

1.get started

2. Connect Dart & HTML

3. Add Elements to the DOM

4, Remove DOM Elements

- 5. Install Shared Packages

6. Define a Custom Element

7. Use Future-Based APIS

8. Use Streams for Data

9. Fetch Data Dynamically

10. Get Input from a Form

11. Use Indexed DB

12. Write Command-line Apps


Define a Custom Element
사용자 요소 정의 하기

Create a custom HTML element using Polymer
Polymer을 사용해서 사용자 HTML 요소 만들기

A custom element is an HTML element you can define yourself, encapsulating appearance and/or behavior within semantically meaningful HTML.

사용자 요소라는 것은 의미상 의기가 있는 HTML로써 외관이나 동작을 사용자가 직접 캡슐화 할 수 있는 HTML 요소를 말합니다.

Version Note: The code sample and the content of this tutorial are compatible with polymer.dart 0.9.

버전 정보: 이번 튜토리얼에 사용되는 샘플코드와 내용은 polymer.dart 0.9 버전과 호환됩니다.


Custom elements are one feature of Polymer, a new type of library for the web based on Web Components. Polymer.dart is the Dart implementation of Polymer. (Note: Polymer supersedes Web UI.)

사용자 요소는 웹 컴포넌트에 기초한 웹라이브러리의 새로운 타입인  Polymer  의 하나의 특징입니다.Polymer.dart는 Polymer를 구현한 Dart 입니다.(Polymer는 Web UI를 대체합니다)



An example
예제

In the example running below, the LemonChiffon area outlined in black is a custom element implemented using Polymer.

아래에 실행가능한 예제에서(예제 원문 주소: https://www.dartlang.org/docs/tutorials/polymer-intro/)  검정색선으로 둘러싸인 LemonChiffon 영역은 Polymer 사용해서 구현된 사용자 요소입니다.

Try it! Start and stop the stopwatch. Reset the stopwatch to 00:00 using the Reset button.

스탑와치를 시작하고 중지해보세요. Reset 버튼을 사용해서 스탑와치를 00:00으로 세팅하세요.

To place this custom element on an HTML page, import the file with the custom element definition and use the name of the element as an HTML tag:

HTML 페이지에 이 사용자 요소를 붙이기 위해서 사용자 요소가 정의된 파일을 가지고 오고 HTML 태그로 요소의 이름을 사용합니다.

<link rel="import" href="tute_stopwatch.html">
...
<tute-stopwatch></tute-stopwatch>

The counting text, the three buttons along with their actions, and the style are all contained within the custom element. The definition of the custom element encapsulates and hides the implementation details, which as the user of the element, you care nothing about.

카운팅 텍스트, 액션이 다른 3개의 버튼, 그리고 스타일모두는 사용자 요소에 포함된 것들입니다.사용자 요소의 정의는 캡슐화 되어 있고 당신이 신경 쓸 필요가 없는 세부정보 구현은 숨깁니다.

When you use developer tools to inspect the element, you see just the custom element’s begin and end tags.

당신이 요소를 조사하기 위해 개발자 도구를 사용한다면, 사용자 요소의 시작과 끝 태그를 볼 수 있습니다.

With custom elements, you can easily create new kinds of elements that have semantically meaningful tags and that are easy to share, reuse, and read.

사용자 요소를 사용하면, 의미있고 중요한 새로운 태그를 쉽게 만들수 있습니다. 그리고 쉽게 공유하고 재사용하고 읽을 수 있습니다.

Overview of the example files
샘플 파일 둘러보기.

Three main source files implement the Stopwatch example:

Stopwatch 예제 구현을 위한 3개의 파일

index.html

The primary HTML file for the app. Includes the Polymer bootstrap script and instantiates the custom element.

앱을 위한 기본 HTML 파일. Polymer 부트스트랩 스크립트를 포함하고 있고 사용자 요소를 인스턴스화 합니다.

tute_stopwatch.html

The HTML code that defines the custom element.

사용자 요소를 정의하는 HTML 코드

tute_stopwatch.dart

The Dart class that implements the custom element.

사용자 요소를 구현하는 Dart 클래스

The following diagram shows the structure of the example app and its use of custom elements.

아래의 그림은 샘플 앱의 구조와 사용자 요소의 사용법을 보여줍니다.

Installing Polymer.dart
Polymer.dart 구현하기.

To use the features provided by Polymer.dart, you need to install the Polymer package. If you are unfamiliar with installing packages, refer to Install Shared Packages, which describes the process in detail.

Polymer.dat에서 제공하는 기능을 사용하려면, Polymer 패키지를 먼저 설치해야 합니다. 당신이 패키지 설치에 익숙하지 않다면 자세한 과정이 설명되어져 있는 install Shared Packages를 참고하세요.

In brief, to install the Polymer package:

요약해서, Polymer 패키지 설치하기.

  • In the application’s pubspec.yaml file, add the package to the list of dependencies by adding the package name, polymer, to the list. YAML is whitespace-sensitive, so take care to indent the package name as shown:

  • 어플리케이션의 puspec.yaml 파일에서 종속성 리스트에 패키지 이름인 polymer를 추가하세요. YAML 은 공백문자에 민감하기 때문에 아래의 그림에 보여지는 것 처럼 조심해서 패키지 이름을 적으세요.

  • Run pub get, which recursively installs the polymer.dart package and all the packages that it depends on. If you are using Dart Editor, when you save pubspec.yaml the editor automatically runs pub get for you. If you are using command line tools, you can run it with the command pub get.

  • pub get을 실행하세요. 그것은 재귀적으로 polyer.dart 패키지와 그것에 종속하는 모든  패키지를 설치합니다. 만약 Dart 에디터를 사용한다면 pubspec.yaml을 저장할때 자동적으로 pub get이 실행이 됩니다. 만약 컨맨드 라인 툴을 사용한다면 컨맨드 라인데 pub get 명령을 사용해서 실행할 수 있습니다.

Including Polymer.dart in your application
어플리케이션에 Polymer.dart 포함하기

To use Polymer.dart features such as custom elements, you need to include Polymer in both the HTML side and the Dart side of your app.

사용자 요소 같은 Polymer.dart의 기능을 사용하기 위해서는 , 앱의 HTML 쪽과 Dart 쪽 둘다에 Polymer을 포함시켜야 합니다.

  • In the primary HTML file for your app, export package:polymer/init.dart within a <script> tag in the <head> section. This script contains the main() function for the app and initializes Polymer.

  • 앱의 기본 HTML 파일 안에, <head>  태그 사이에 <script> 태그를 사용해서  package:polymer/init.dart 내보내세요. 이 스크립트는 앱의 man() 함수를 포함하고 있고 Polymer를 초기화 합니다.

  • In the primary HTML file for your app, include the packages/browser/dart.js bootstrap script in the <head> section.

  • 앱의 기본 HTML 에서, <head> 섹션 사이에 부트스트랩 스크립트인 packages/browser/dart.js 를 포함시키세요.

  • In your Dart code, import the Polymer library:

  • Dart 코드 안에는 Polymer 라이브러리를 추가하세요.

Instantiating a custom element
사용자 요소 인스턴스화 하기

To create an instance of a custom element, use the name of the custom element just as you would any normal HTML tag. In this example, the tag name is tute-stopwatch.

사용자 태그의 인스턴스를 만들기 위해서, 일반적인 HTML 태그처럼  사용자 요소의 이름을 사용하세요.이번 예제에서는 사용되는 태그 이름은 tute-stopwatch 입니다.


Using best practices, the custom element definition is in a separate file. Use link [rel="import"]to import the HTML definition file as shown.

가장 좋은 방법을 사용하는 것은, 사용자 요소 정의는 분리된 파일에 두는 것입니다. 그림에서 보는 것 처럼 HTML 정의 가져오기 위해서 link [rel=”import”] 를 사용하세요.

Defining a custom element
사용자 요소 정의 하기

The definition for the <tute-stopwatch> element is in tute_stopwatch.html. A custom element definition should be in its own source file so that it can be included by other files. An HTML file that contains the definition for a custom element does not need <html>, <head>, or <body> tags.

<tute-stopwatch> 요소의 정의는 tute_stopwatch html 파일안에 있스니다. 사용자 요소 정의는 그것이 다른 파일에 포함될 수 있도록 자기 자신의 소스 파일에 있어야 합니다. 사용자 요소가 정의되어 있는 HTML 파일은 <html>, <head>, <head> 같은 태그들이 필요하지 않습니다.

To define a custom element, use the <polymer-element> tag and provide a name.

사용자 요소를 정의 하기 위해서는, <polymer-element> 태그를 사용하고 이름을 제공합니다.

<polymer-element name="tute-stopwatch">
 ...
</polymer-element>

A custom element name must have at least one hyphen (-). We advise using an identifiable prefix to avoid naming conflicts with elements shared by others and to help identify the project from which the element originates. For example, for tutorial custom elements, we use the prefix tute.

사용자 요소의 이름은 반드시 적어도 하나의 하이튼(-)이 있어야 합니다. 우리는 다른 사람에 의해 공유되어지는 요소와의 충돌되는 이름을 피하기 위해 식별 가능한 접두어를 사용할 것을 권고합니다. 그리고 그 사용자 정의 요소가 쓰이는 프로젝트를 식별하기 위해 식별 가능한 접두어를 사용할 것을 권고합니다. 예를 들어, 사용자 요소 튜토리얼에서는 tute 라는 접두어를 사용합니다.

Within the <polymer-element> tag, you can provide a template (appearance) and a script (behavior). UI widgets, like our Stopwatch example, typically have both a template and a script, but neither is required. A custom element with a script and no template is purely functional. A custom element with a template and no script is purely visual.

<polymer-lement> 태그 내에서, 당신은 템플릿(외관) 과 스크립트(동작)을 제공할 수 있습니다. Stopwatch 예제서처럼 UI 위젯은 일반적으로 템플릿과 스크립트를 둘 다 가지고 있지만 2개가 꼭 필요한 것은 아니다. 템플릿은 없고 스크립트만 있는 사용자 요소는 순수하게 함수입니다. 반대로 스크립트는 없고 템플릿만 있는 사용자 요소는 순수하게 시각적인것만 담당 합니다.

<polymer-element name="tute-stopwatch">
 
<template>
   ...
 
</template>
 
<script type="application/dart" src="tute_stopwatch.dart">
 
</script>
</polymer-element>

<template>

Describes the custom element's structure—its user interface. The template comprises any valid HTML code within the <template> tag. When the custom element is instantiated, the instance is created from the template. The template can include CSS styles within a <style> tag.

<template>은 사용자 인터페이스인 사용자 요소의 구조를 묘사합니다. 템플릿은 <template> 태그안에 검증된 HTML 코드로 구성됩니다. 사용자 요소가 인스턴스화 될때, 그 인스턴스는 템플릿에서 만들어집니다. 템플릿은 <style> 태글 사용해서 CSS 스타일도 포함할 수 있습니다.

<script>

Specifies a Dart script. For custom elements, the Dart script is a Dart class that implements the behavior of the element. The class typically overrides some life-cycle methods and provides event handlers that join the UI with its programmatic behavior. In this example, the script is in tute_stopwatch.dart.

<script>는 Dart 스크립트를 명시합니다.사용자 요소를 위해서, Dart 스크립트는 요소의 동작을 구현하는 Dart 클래스입니다. 이 클래스는 전통적으로 몇 몇의 생명 주기 메소드를 override하고 UI에 프로그램적인 동작을 입히는  이벤트 핸들러를 제공합니다.예를 들면, tute_stopwatch.dart가 있습니다.

Providing a template for the custom element
사용자 요소 템플릿 제공하기.

Here’s the template code for the tute-stopwatch element:

tute_stopwatch 요소를 위한 템플릿 코드가 있습니다.

The tute-stopwatch template uses a <style> tag, which is optional. These styles are scoped; they affect only the appearance of the custom element and the elements it contains. More about scoped CSS in Styling a custom element.

tute_stopwatch 템플릿은  <style> 태그를 사용합니다. <style> 태그는 옵션입니다(사용해도 되고 안해도 되는.) . 이 스타일들은 소코프 범위입니다. 즉 스코프 범위란, 사용자 요소의 모양과 그것을 포함하고 있는 요소의 모양에만 영향이 미칩니다. 범위지정(소코프) CSS에 좀 더 알고 싶다면 Styling a custom element. 을 보세요.

The rest of the code within the <template> tag is normal HTML, with two exceptions:

<template> 태그안에 있는 나머지 코드들은 일바적인 HTML 입니다. 아래의 2개를 제외하고는 말이죠.

{{counter}}

Uses a Polymer syntax to bind Dart data to the HTML page. The double curly braces are commonly known as a “double mustache”.

HTML 페이지에 Dart 데이터를 바인딩하는 Polymer 문법입니다.  일반적으로 이중 중괄호는 이중 수염(?)으로 알려져있습니다.

on-click

Uses Polymer declarative event mapping, which allows you to set up event handlers for a UI element. on-click sets up an event handler for mouse clicks. Polymer has mappings for other event types, such as on-input for changes to text fields.

Polymer의 선언적(?) 이벤트 맵핑입니다.  이것은 당신이 UI 요소에 이벤트 핸들러를 설치할 수 있다는 것입니다. on-click 은 마우스 클릭에 대한 이벤트 핸들러를 설치합니다. Polymer은 텍스트 필드가 바뀌었을때 발생하는 on-input 과 같은 다른 타입의 이벤트와 매핑할수 있습니다.


Let’s take a look at the structure of the Dart code before we get into the details of data binding, event handlers, and scoped CSS.

데이타 바인딩, 이벤트 핸들러 그리고 범위지정 CSS에 대해서 자세히 알아보기 전에, Dart 코드의 구조를 한 번 살펴봅시다.

Providing a script for the custom element
사용자 요소에 script(동작) 입히기.

On the Dart side, a class implements the behavior of the custom element. You associate the Dart class with the custom element using the @CustomTag annotation and the name of the custom element.

Dart 측면에서 보면, 클래스는 사용자 요소의 작동을 구현합니다. 당신은 @CustomTag 주석과 사용자 요소의 이름을 사용하여 사용자 요소와 Dart 클래스를 연결합니다.

This diagram gives an overview of the TuteStopwatch class:

이 그림은 TuteStopwatch 클래스의 개요를 보여줍니다.

Any Dart class that backs a Polymer element must subclass PolymerElement.

Polymer 요소를 사용하는 Dart 클래스는 반드시 PolymerElement 의 하위 클래스여야 합니다.

반드시 상속 받아야 합니다.

The class can respond to life-cycle milestones by overriding life-cycle methods. For example, the TuteStopwatch class overrides the enteredView() method—which is called when the element is inserted into the DOM—to initialize the app.

그 클래스는 생명주기 메소드를 오버라이드함으로써 생명 주기 단계에서 응답할 수 있습니다. 예를 들어, TuteStopwatch 클래스는 앱을 초기화 하기 위해 enteredView() 메소드를 오버라이드합니다. enteredView()은 요소가 DOM에 삽입되어질때 호출됩니다.

The start() method is an event handler for the Start button. The event handler is declaratively connected to the button. Refer to Setting up event handlers declaratively to see how.

start() 메소드는  Start 버튼의 이벤트 핸들러입니다.이 이벤트 핸들러는 버튼과 연결되어 집니다. 어떻게 되는지 궁금하다면 Setting up event handlers declaratively 을 참고하세요.

Overriding life-cycle methods
생명주기 메소드 오버라디딩하기.

A custom element has four life-cycle methods that it can override:


created()

Called when an instance of a custom element is created.

사용자 요소가 인스턴스가 만들어질때 호출된다.

enteredView()

Called when an instance of a custom element is inserted into the DOM.

사용자 요소의 인스턴스가 DOM안에 삽입될때 호출된다.

leftView()

Called when an instance of a custom element is removed from the DOM.

사용자 요소의 인스턴스가 DOM 에서 제거될때 호출된다.

attributeChanged()

Called when an attribute, such as class, of an instance of the custom element is added, changed, or removed.

class 같은 사용자 요소의 인스턴스의 속성이 추가되거나 바뀌거나, 삭제될때 호출된다.


You can override any of these life-cycle methods. The overriding method must call the super class method first.

이런 생명주기 메소드들은 오버라이딩 할 수 있습니다. 오버라이딩 메소드는 맨 처음에 부모 클래스의 메소드를 반드시 호출해야 합니다.

The Stopwatch app overrides the enteredView() method because it needs a reference to each of the three buttons so that it can enable and disable them. When a tute-stopwatch custom element is inserted into the DOM the buttons have been created, so the references to them will be available when the enteredView() method is called.

Stopwatch 앱은 enteredView() 메소드를 오버라이딩 합니다. 왜냐하면, 3개의 버튼을 enable와 disable을 하기 위해서 3개 버튼의 참조값이 필요하기 때문입니다.

void enteredView() {
 
super.enteredView();
 startButton
= $['startButton'];
 stopButton
= $['stopButton'];
 resetButton
= $['resetButton'];
     
 stopButton
.disabled = true;
 resetButton
.disabled = true;
}

The code uses automatic node finding, a Polymer feature, to get a reference to each button. Every node in a custom element that is tagged with an id attribute can be referenced by its ID using the syntax: $['ID'].

이 코드는 각 버튼의 참조값을 가져오기 위해서 , Polymer의 기능인, 자동 노드 찾기 기능을 사용합니다. id 속성을 가지고 있는 사용자 요소 태그는 ID를 통해서 참조할 수 있습니다. 문법은 $[‘ID’] 를 사용합니다.

Using data binding
데이터 바인딩 사용하기.

In the HTML definition of a custom element, use double curly brackets to embed Dart data into the webpage. In your Dart code, use the @observable annotation to mark the embedded data. Here, the data is a string called counter.

사용자 요소의 HTML 정의 부분에서, 웹 페이지와 Dart 데이터를 결합시키기 위해서 이중 중괄호를 사용하세요. 당신의 Dart 코드에서, 결합된 데이터를 표시하기 위해서 @observable  주석을 사용하세요. 이 예제에는 counter이라고 하는 스트링 변수입니다.

The tute-stopwatch element uses a periodic Timer to fire an event every second. When the Timer fires, it calls the updateTimer() method, which modifies the counter string. Polymer takes care of updating the HTML page with the new string.

tute-stopwatch 요소는 매 초마다 실행되는 타머입니다. 타이머가 실행되면 counter 문자열을 수정하는  updateTimer() 메소드를 호출합니다. Polymer 은 그 새로운 counter 문자열을 HTML 페이지에 업데이트합니다.

This type of binding is called one-way data binding because the data can change only on the Dart side. Polymer also supports two-way data binding. In two-way data binding, when data changes on the HTML side—for example with an input element—the value in the Dart code changes to match. For more information about two-way binding, plus examples of using it with a variety of HTML5 widgets, check out the Forms tutorial section Two-way data binding using Polymer.

이런 타입의 바인딩은 데이터가 Dart쪽에서만 변경할 수 있기 때문에 단방향 바인딩이라고 불립니다. Polymer은 양방향 데이터 바인딩도 지원합니다. 양방향 데이터 바인딩은, 데이터가 HTML 쪽에서도 변경이 일어날때- 예를 들면 input 요소- Dart 코드에 있는 값도 변경이 됩니다. 수많은 HTML5 위젯과 함께 양방향 바인딩에 대해서 좀 더 많은 정보를  얻기를 원한다면,  Two-way data binding using Polymer 섹션을 둘러보세요.

You can use expressions within the double curly brackets. Polymer expressions provide the default syntax. Examples of allowable expressions include:

이중 괄호를 사용해서 표현할 수 있습니다. Polymer expressions는 기본 문법을 제공합니다.  허용되는 표현식의 예는 아래와 같습니다.

{{myObject.aProperty}}

Property access.

{{!empty}}

Operators, like the logical not operator.

{{myList[3]}}

List indexing.

{{myFilter()}}

Data filtering.


Setting up event handlers declaratively
이벤트 핸들러 설치하기.

This example has three buttons, each with an event handler that is written in Dart, but attached to the button declaratively from HTML.

이 예제는 Dart 에서 작성된 각각의 이벤트 핸들러를 가지고 있고 HTML 페이지에 형식적으로 붙어 있는 3개의 버튼이 있습니다.

In HTML, use the on-click attribute to attach a mouse click handler to an HTML element. The value of the attribute must be the name of a method in the class that implements the custom element. When the user clicks the button, the specified method is called with three parameters:

HTML에서 HTML 요소에 마우스 클릭 이벤트 핸들러를 붙이기 위해  on-click  속성을 사용하세요. on-click  속성의 값은 반드시 사용자 요소를 구현하는 클래스의 메소드 이름이어야 합니다. 사용자가 버튼을 클릭할때, 지정된 메소드는 3개의 파라미터를 가지고 호출됩니다.

  • An Event that contains information about the event, such as its type and when it occurred.

  • 이벤트의 타입과 발생 시점 같은 이벤트에 대한 정보를 가지고 있는 Event 객체

  • The detail object can provide additional, event-specific information.

  • detail 객체는 추가적이고 좀 특수한 이벤트 정보를 제공합니다.

  • The Node that fired the event—the Start button in this case.

  • 이벤트가 발생된 노드. 이 예제에서는 Start 버튼이 됩니다.

You can attach event handlers for other kinds of events. For example, you can use on-input to handle events for input text elements when the text changes.

당신은 다른 종류의 이벤트 핸들러를 붙일수 있습니다. 예를 들면, 텍스트가 변경될때, input 텍스트 요소에 이벤트를 처리하기 위해 on-input 을 사용할 수 있습니다.

Refer to Declarative event mapping for further details.

좀 더 자세한 걸 원한다면 Declarative event mapping 보세요.

Styling a custom element
사용자 요소에 스타일 입히기.

You can optionally include CSS styles for your custom element that apply only to the contents of the custom element.

필요에 따라서 사용자 요소의 내용에만 적용될 사용자 요소에 CSS 스타일을 포함시킬 수 있습니다.


The @host rule allows you to target and style an element internally, from within its definition. The:scope pseudo-class refers to the custom element itself. The only selectors that work within @host are those contained in the host element itself. So you don’t need to worry about naming conflicts on the page. Any CSS selectors within the template need to be unique only within the template.

@host 규칙은, 내부에 정의된 대로,  당신이 내부적으로 어떤 요소를 타겟으로 지정하고 스타일을 입힐 수 있습니다. (내 생각: 아마도 @host 라는 것이 현재 @host가 속해 있는 html 파일에만 적용된다는 뜻이 아닐까...아니면 댓글 부탁합니다. ). :scope  가상 클래스는 사용자 요소 그 자체를 참조합니다. 호스트 내에서 작동하는 유일한 셀렉터는 호스트 요소 그 자체를 포함하는 것들입니다. 이 페이지에서는 이름이 충돌하는 것에 대해서는 걱정할 필요가 없습니다. 템플릿 내에서 어떤 CSS선택자라도 템플릿 안에서는만 유일하면 됩니다.

For further details about styling custom elements, refer to A Guide to Styling Elements

사용자 요소에 대해서 좀 더 자세한 정보를 알고 싶으면, A Guide to Styling Elements를 참고하세요.

Other resources
다른 자원들

Use these other resources to learn more about Polymer:

Polymer에 대해서 좀 더 배우고 싶으면 다음의 자료들을 이용하세요.

  • The Polymer.dart homepage provides information specific to the Dart port of the Polymer project.

  • Polymer.dart  홈페이지는 Polymer 프로젝트를 Dart로 포팅에 대한 특정 정보를 제공합니다.

  • The Polymer project website polymer-project.org contains information about the Polymer project as a whole.

  • Polymer 프로젝트 웹 사이트  polymer-project.org 은 Polymer 프로젝트의 전체 정보를 볼 수 있습니다.

What next?
다음은?

Two-way data binding with Polymer in the tutorial about forms shows how to use two-way data binding with various types of input elements such as text fields, color pickers, and so on.

폼에 대한 튜토리얼인 "Polymer를 사용한 양방향 데이터 바인딩"은 텍스트 필드, 컬러 픽스 등과 같은 많은 input 요소에 어떻게 양방향 데이터를 사용하는지 알아 볼 것입니다.

Check out these other tutorial examples that use Polymer:

Polymer을 사용하는 다음 튜토리얼 예제를 한 번 보세요.

  • its_all_about_you

  • slambook

  • count_down

The next tutorial, Fetch Data Dynamically, shows you how to fetch data and use JSON to encode and decode that data.

다음 튜토리얼에서는,동적으로 데이터 가져오기, 어떻게 데이터를 가지고 오고 이 데이터를 어떻게 JSON으로 인코딩하고 디코딩하는지를 알아 볼 것 입니다.


300x250

'개발 > dart' 카테고리의 다른 글

8. Use Streams for Data  (0) 2014.03.02
7. Use Future-Based APIs  (0) 2014.03.02
5. Install Shared Packages  (0) 2014.03.02
4. Remove DOM Elements  (0) 2014.03.02
3. Add Elements to the DOM  (0) 2014.03.02

댓글