예전부터 음을 만드는 작업을 경이롭게 쳐다만 보다가 이번에 소리를 만드는 것을 이번참에 공부해 볼까 하며 시작하게 되었다. MAX MSP강좌를 보며 소리에 대해 공부해 나가고 있는데 워낙 소리나 음향에 대해 아는게 없다보니 이것저것 찾게 됨.

소리에 대한 기본적인 이해는 이 동영상이 한방에 끝내준다.

https://www.youtube.com/watch?v=cK2-6cgqgYA

그리고 SynthSchool의 동영상이 sine wave와 harmonics에 대해 시각적으로 설명해줘서 도움이 되었다. 특히나 자체 만든 프로그램을 이용해 파형을 보여주는 정성이 굉장함.

https://www.youtube.com/playlist?list=PL0CF041F562C5BE5E

또한 wickiemedia의 컨텐츠도 도움이 되었다.

아래 플레이리스트에는 기본적인 음악이론, waveform, phase, harmonics, envelopse등등이 영상으로 잘 정리되어 있다.

Audio Tutorials – Season 2 : Spectral processors – YouTube

아래 영상도 디지털 음악에 대한 이해에 좋은 컨텐츠

Digital Audio Explained – Samplerate and Bitdepth – YouTube

 

 

php.ini에서 최대 업로드 허용량을 늘려줬는데도 1메가 이상의 이미지 업로드시 HTTP Error가 계속되었는데 알고보니 nginx의 업로드 허용량이 1메가라서 이부분을 수정해줘야 한다.

/etc/nginx/nginx.conf

http괄호 안에 넣어주었다. client_max_body_size 10m;

user  www-data;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
  #tcp_nopush     on;

  keepalive_timeout  65;

  #gzip  on;
  client_max_body_size 10m;
  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;
}

osx에 nginx를 세팅하고 virtual host로 로컬 도메인을 세팅해 주고 워드프레스를 사용할때 아래와 같은 식으로 virtual host를 세팅해주었다.

server {
  listen 80;
  server_name echo.dev;
  root  /Users/ray/Sites/USER/echo/public_html;
  index  index.php index.html index.htm;
  location ~ \.php$ {
    try_files $uri =404;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
  }
    location / {
    index index.php index.html index.htm;
    try_files $uri $uri/ /index.php?$args;
    }
  location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
    access_log off; log_not_found off; expires max;
  }
}

이 때 워드프레스를 http://hostname/wordpress 에 설치하고 Site Address를 http://hostname/ WordPress Address를 http://hostname/wordpress 로 이용할 수 있다. (index.php를 / 에 옮기는 작업이 필요) 이렇게 하면 Permalink Settings에서 Post name을 쓰는 것도 문제가 없다.

문제는 WordPress Address를 / 로 바꾸지 않고 그대로 /wordpress 로 사용할 때 문제가 된다. 이 때 아래와 같이 location을 더해주면 문제가 해결된다.

    location /wordpress/ {
    try_files $uri $uri/ /wordpress/index.php?$args;
    }

최종적인 config 모습.

server {
  listen 80;
  server_name echo.dev;
  root  /Users/ray/Sites/USER/echo/public_html;
  index  index.php index.html index.htm;
  location ~ \.php$ {
    try_files $uri =404;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
  }
    location / {
    index index.php index.html index.htm;
    try_files $uri $uri/ /index.php?$args;
    }
    location /wordpress/ {
    try_files $uri $uri/ /wordpress/index.php?$args;
    }
  location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
    access_log off; log_not_found off; expires max;
  }
}

hosts파일로도 특정 도메인을 로컬로 연결하는게 가능하지만, *.dev를 연결하거나 할때 hosts파일로는 불가능하다 그럴때 dnsmasq를 쓸수가 있는데, 아래 포스트를 참고하면 된다.

Using Dnsmasq for local development on OS X – Passing Curiosity

아주 간단히 설명하면 과정은 다음과 같다.

  1. dnsmasq를 설치.
  2. dnsmasq설정파일에서 address=/dev/127.0.0.1 이런식으로 설정하면 모든 dev로 끝나는 도메인은 127.0.0.1로 연결된다.
  3. osx내부에서 resolve설정을 해준다.

Steven Wittens의 Hackery, Math & Design — Acko.net를 읽다가 JS1k사이트에 가서 2015년 선정작들을 보게 되었다.

JS1k 2015 – 2331 – Do the Wave!

img
2D 파티클의 wave simulation인데 아쉽게도 원본 소스가 없다.

JS1k 2015 – 2340 – Audience is Listening

img
THX 사운드 데모

JS1k 2015 – 2342 – Physics Hype 3D

img
3D physics engine

JS1k 2015 – 2363 – Warp Drive Train

img
좋은 예제다!

JS1k 2015 – 2329 – 1k FM Synthesizer

img
키보드 아랫줄을 누르면 신서사이저처럼 소리가 나는데 잘 이해는 안된다.

JS1k 2015 – 2287 – DragonTrack

img
Felix Woitzel의 작품

JS1k 2015 – 2169 – Pietime

img

JS1k 2015 – 2161 – Romanesco

img

JS1k 2015 – 2163 – en1kma

img
이니그마 머신!

JS1k 2015 – 2177 – Not a fractal

img
우왓

JS1k 2015 – 2198 – Abysm

img

JS1k 2015 – 2231 – Galaxy1K v1.1

img
A WebGL galaxy made out of 70000 particles!

JS1k 2015 – 2241 – Tear the curtain!

img
Tear the curtain

JS1k 2015 – 2252 – Amass v.2

img
멋지다

JS1k 2015 – 2315 – Infinite Star Field

img
오 맘에든다

JS1k 2015 – 2316 – hypeRcube

img
테서렉트!!

JS1k 2015 – 2322 – Mysterious Monorail

img 와! 소스도있네

img

국제갤러리에서 빌 비올라 개인전을 보았다. 그의 작품들을 실제로 미술관에서 보는 것은 처음이었고, 다른 비디오아트 작업들과는 다르게 몰입하게 만드는 힘을 여러 작품들에서 받을 수 있던 좋은 시간이었다.

그중에서도 Mirage시리즈중의 The encounter라는 작품은 사막에서 걸어오는 사람을 찍은 시리즈인데, 아지이를 그렇게까지 자세히 본 적이 없어서인지 10분이 넘는 상영시간과 그 단조로움에도 불구하고 넋을 놓고 신기하게 쳐다본 작품이었다..

img

저 멀리서부터 천천히 걸어오는 사람의 모습은 처음엔 일렁거리는 장면의 한 점이었다가 어느 순간 배경과 사람은 분리가 되는 착각이 든다.

img

어느정도 가까워 진 사람의 모습을 뒤로 한 아지이는 마치 거울과 같은 느낌을 주는데, 아지이임을 알지 못했다면 저기 얕은 물이 고여있는게 아닐까 하는 강한 착각을 끊임없이 불러 일으킬만하다. 과연 어떤 원리일까에 대한 고민이 시작되었다.

img

아지이에 의해 일그러진 영상은 어떤 공간이 아닌 인상파 화가의 캔버스를 보고 있는 느낌을 준다. 사막의 뜨거운 열에 의해 빛이 타오르거나 끓어오르는 듯이 보인다. 위키백과에 아지랑이는 이런 뜻으로 풀이되고 있다.

아지랑이는 햇빛이 강하게 쬐어 지면이 뜨겁게 달구어진 날, 공기가 공중에서 아른아른 움직여 먼 풍경이 지면 근처에서 아른거리며 보이는 대기속의 과학적 현상을 말한다. 주로 이른 봄이나 여름철의 맑은날 햇빛이 강하게 내리 쬘 때, 양지 바른 해안의 모래나 지붕, 도로, 초원등에서 발견할 수 있다.
원인 : 태양열로 인해 지표면 근처의 공기는 뜨거워지면서 팽창한다. 그러면 주위 공기보다 가벼워져 공기덩어리들이 상승하게 되고 그 빈 부분이 아직 가열되지 않은 찬 공기로 채워지게 된다. 빛은 공기의 밀도에 따라 굴절률이 달라지기 때문에 지면에서 급격히 대류하는 공기덩어리 사이를 통과하는 빛은 불규칙하게 굴절한다.
출처 : 아지랑이 – 위키백과, 우리 모두의 백과사전

즉 공기의 밀도가 매우 빠른 속도로 바뀌기 때문에 빛의 굴절 역시 매우 빠른속도로 바뀌고 있는 것이다. 반대로 생각하면 우리가 보는 일반적인 시각이란 안정된 공기의 움직임으로 인해 다행히 일그러짐 없이 보고 있다는 것이다. 그렇다면 거울과 같은 효과는 어떻게 생기는 것일까? 검색해본 바 이것에 대한 설명을 찾을 수가 없어 미술관에서의 추측이 시작되었다.

img

이 사진을 보면 사람은 비교적 선명히 보이지만 배경은 일렁이고 있고, 지평선 아래는 마치 거울처럼 지평선 위를 비치고 있다.

img

A는 하늘 비롯한 지평선 상단의 모습이고, B는 그걸 반사하고 있는 아랫 부분이다. A부분도 약간의 일그러짐이 있는것을 알수 있는데, 즉 배경에서부터 사람 사이의 빛이 공기의 밀도가 바꿈에 따라 굴절되고 있는 것이다. 그렇다면 B부분, 즉 지표면과 가까운 부분은 더 뜨거울 것이고 더 많은 공기의 이동이 이루어져 훨씬 큰 공기의 굴절을 이루고 있어서 B부분의 빛이 카메라로 닿지 못하고 반사되어 A의 빛을 한번 더 반사시켜 우리 눈에 거울과 같은 착시를 불러일으키게 된다.

img

굴절률을 붉은색으로 표현해보았는데, 지평선 위의 공기들은 보다 약한 굴절을 일으키고, 지표면 바로 위는 훨씬 많은 굴절을 일으키게 되는것 같다. 그 굴절은 사막에서 내가 내 발 밑을 못볼 정도까지는 아니고 아주 먼 거리에서 내 위치를 보았을 때 정도의 예각은 반사시킬 정도이지 않을까. 하는 추측으로 마무리 해본다.

공기의 밀도의 변화로 빛을 이정도까지 변화시킬 수 있는 것에 크게 놀란 경험이었다. 이걸 응용하면 공기의 벽을 만들어 빛을 반사시킬 수 있지 않을까? 불가능하려나. 한번 찾아봐야지. 자연의 신비에는 끝이 없지만 인간의 노력도 한계가 없으니 가능할지도 모르겠다.. 미술관다녀온지 이주만에 미루고 미뤄온 포스트 끗.

1. 기본 함수

Iñigo Quilez – fractals, computer graphics, mathematics, demoscene and more의 Box – signed를 민수님의 방법을 사용해서 2차원 함수로 변환하여 분석해보려고 한다.

float sdBox( vec3 p, vec3 b )
{
  vec3 d = abs(p) - b;
  return min(max(d.x,max(d.y,d.z)),0.0) +
         length(max(d,0.0));
}

2차원 함수로 변환했다.

float sdBox2(vec2 p, vec2 b){
    vec2 d = abs(p) - b;
    return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}

2. 순서

이 함수는 다음과 같은 구조를 하고 있다.

1) d를 position으로부터 생성한다.

vec2 d = abs(p) - b;

2) d로부터 계산

min(max(d.x, d.y), 0.0)

3) d로부터 계산

length(max(d, 0.0))

4) 2와 3을 더해 리턴한다. 따라서 이 순서로 진행해보겠다.

3. position 나타내기

일단 기본이 되는 그래프부터 표현해보도록 한다. GLSL Sandbox에서 다음과 같은 코드로 시작한다.

#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 resolution;

void main( void ) {
    vec2 p = gl_FragCoord.xy / resolution.xy;
    p = p * 2.0 - 1.0;
    float color = position.x;
    gl_FragColor = vec4( color, color, color, 1.0 );
}

다음과 같은 결과를 확인할 수 있다.

img

현재 color값에 p.x의 값이 들어간 것을 확인할 수 있다. 즉 좌측 상단부터 (0, 0) 우측 하단까지 (1, 1)의 값을 가지는 것을 알 수 있다. 이는 다음과 같은 그래프로 표현할수 있다.

img

x, y가 갖는 값을 z축에 대입하여 3d그래프로 표현해보았다.

image

4. vec2 d = abs(p) - b; 보기

위의 position값에서 abs(p) 는 절대값이기 때문에 다음과 같이 표현할 수 있다.

image

b는 vec2값이기 때문에 x, y에 둘다 동일한 값을 빼준다고 가정하면 vec2 d = abs(p) - b;는 다음과 같이 표현할수 있다.

image

color = d.x를 glslsandbox에서 아래와 같은 코드로 실행했다.

#ifdef GL_ES
precision mediump float;
#endif

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;

void main( void ) {
    vec2 pos = ( 2.0 * gl_FragCoord.xy - resolution.xy ) / resolution.x;
    float color;
    vec2 d = abs(pos) - 0.5;
    color = d.x;    
    gl_FragColor = vec4( vec3(color), 1.0 );
}

img

그래프를 보면 가운데에 0보다 작은 영역이 어둡게 표현되었음을 알 수 있다.

5. min(max(d.x, d.y), 0.0)

3에서 d.x를 shader에서 표현해보았는데, 이번에는 max(d.x, d.y)를 그래프에 먼저 표현해 보겠다.

image

이전까지는 x에 관한 부분과 y에 관한 부분이 각각 따로 떨어져 있어 color에 대입하려고 해도 d.x나 d.y처럼 각각 따로 할 수 밖에 없었는데, max함수로 마치 두개의 식을 한 식에 표현한 듯한 모양을 얻을수 있다. 또한 이 그래프에서 z축의 단면과 만나는 부분이 사각형을 이루는데 이 부분을 shader에서 표현해 보겠다.

    vec2 d = abs(pos) - 0.5;
    color = max(d.x, d.y);

img

그래프에 나타난 대로 0보다 작은 수는 어둡게 표현되었다.

min(max(d.x, d.y), 0.0)

img

0보다 작은수만 남았기 때문에 0보다 큰수는 지워졌다. 실제로 이 코드를 shader해보면 까만 화면밖에 나오지 않는다. 남은 부분을 볼수 있게 하기 위해 마지막에 -1을 곱해주었다.

    color = min(max(d.x, d.y), 0.0);
    color = -1.0 * color;

img

smoothstep를 적용해 주었다.

color = smoothstep(0.3, 0.31, color);

img

위의 과정을 거쳐 사각형 모양을 얻을 수 있다.

이런 복잡한 주제에 이런 간단한 해결방법이 있을 줄은 몰랐다. 처음에 Mysql sync remote local로 찾았는데, 수많은 복잡한 방법과 복잡한 이야기들. 혹시나 하고 wordpress local remote로 찾았더니 글쎄. 나왔다.

바로바기 : https://github.com/wp-sync-db/wp-sync-db

설치방법

  1. github에서 zip을 받아 양쪽 wordpress에 플러그인으로 설치한다.
  2. 설명에 github-updater를 설치하라고 나오는데, 이 플러그인을 github에서 자동으로 업데이트 시켜주는 기능이라 필요없으면 굳이 설치할 필요 없어 보인다.

사용방법

  1. Remote에서 push선택하면 나오는 코드를 복사
  2. Local에서 pull을 선택하고 아까의 코드를 입력한다.
  3. migrate버튼을 누르면 끝

영상에 잘 나와 있다.

장점

  1. db를 온라인을 통해 옮기는 과정이 쉽고 간단하다.
  2. 자동으로 url주소도 변경해 준다.
  3. 자매품 wp-sync-db-media-files을 쓰면 미디어 파일도 같이 옮겨지는 것 같다.
  4. 공짜다.

매번 스타벅스에 갈 때마다 olleh_starbucks를 잘 쓰고 있습니다만, 개인정보 우려로 매번 가짜입력하는 것도 귀찮아 30분 검색해보고 성공해 만족스런 마음에 공유해봅니다.

  1. automator를 실행합니다.
  2. 신규 – 서비스를 클릭합니다.
  3. 왼쪽창에서 ‘보관’을 클릭, ‘Applescript 실행’을 더블클릭합니다.
  4. ‘서비스가 받는 항목’을 ‘입력 없음’으로, ‘선택 항목 위치’를 ‘모든 응용 프로그램’으로 되있는 것을 확인합니다.
  5. ‘Applescript 실행’의 스크립트 란에 아래 스크립트를 넣습니다. 아이디와 이메일은 바꾸셔도 되는데 너무 짧거나 길면 통과하지 못하는 경우가 있는것 같아 해보면서 조절하시면 될 것 같습니다. 이름에 한글은 왠지 애플스크립트에서 튕기는 것 같아 영어로 했습니다.
  6. 저장하고 이름을 입력합니다. 저는 ‘스타벅스 로그인’으로 했습니다.
  7. 스타벅스로 로그인 화면에서 커서를 이름 입력하는 곳에 둡니다.
  8. 상단의 Chrome를 누르고 서비스를 보면 아까 만든 ‘스타벅스 로그인’이 보입니다. 누르면 입력이 진행됩니다.

img

참고한 문서 어떤 앱에서도 불러낼 수 있는 “단어와 문자 카운터”를 OS X에 추가하자! :: Back to the Mac 블로그

# 스타벅스 wifiap "olleh-starbucks" 로그인 쉽게 하기
#
on run {input, parameters}
tell application "Google Chrome" to activate
tell application "System Events"
keystroke "Kanegi"
delay 0.2
keystroke tab
delay 0.2
keystroke "hongkildong@gmail.com"
delay 0.2
keystroke tab
delay 0.2
keystroke space
delay 0.2
keystroke tab
delay 0.2
keystroke "010"
delay 0.2
keystroke "8888"
delay 0.2
keystroke "9999"
delay 0.2
keystroke space
delay 0.2
keystroke tab
delay 0.2
keystroke space
keystroke return
end tell
return input
end run

or wordpress의 markdown에서 shortcode 쓰기

현재 mytory markdown플러그인를 이용하고 있다. 이를 통해 글을 올리는 프로세스는 다음과 같다.

  1. sublime text로 markdown파일을 dropbox의 public 폴더 내에 저장한다.
  2. dropbox의 public link를 복사한다.
  3. wordpress에 새 글 쓰기를 눌러 mytory markdown이 만든 링크를 넣을 수 있는 곳에 링크 주소를 넣는다.
  4. makrdown이 html로 변환되어 에디터 창에 들어간 것을 확인할 수 있다.

내 글을 내가 직접 관리할 수 있다는 점. 글의 수정시 워드프레스에 반영이 용이하다는 점이 맘에 들어 이 방법으로 계속 포스팅을 하려고 한다.

문제점

이 방법의 몇가지의 문제점이 발생했는데 이는 다음과 같다.

  1. github 스타일의 코드를 넣으면 에러가 난다.
    : markdown변환하는 과정에서 파서가 github favored code 스타일을 인식하지 못해 일어난 에러인데
    http://kmyh.kr/add-prism-js-for-glsl-language/에 해결 방법을 기록했다.
  2. gist를 등록하는 과정에서 script나 shortcode가 인식이 안되는 문제가 생긴다.

해결

어떤 식으로 해결이 가능할지 생각해 보았다. 일단 markdown에 gist가 삽입이 되어야 하는데 script방식은 되지 않고, native gist를 삽입하는 방식은 특정 line을 지정할 수도 없어 소용이 없다. 이를 해결한 Gistpress를 설치해서 gist를 성공적으로 wordpress내에 임베딩하는것은 물론 특정 line까지 지정할 수 있었다. 단지 문제는 이제 시작이었는데, markdown에서는 어떤 shortcode도 문자열을 변환시킨다는 것이다.

markdown에서 shortcode를 소화하는 방법을 아무리 찾아보았지만 관련 질문조차 찾을 수 없었다. 단지 github에서는 {% gist … %} 이런 식으로 지정해서 gist를 임베딩하는 방식을 제공한다는 것 밖에. 한참 고민한 끝에 나도 역시 저런 식으로 마크다운 문법을 만든 다음 markdown을 파싱하는 과정에서 숏코드로 변환하자는 생각을 했다.

결국 이 기능은 mytory shortcode플러그인 안에서 markdown을 파싱하는 과정 직후에 preg_replace를 이용해 만들었다.

입력 예)

{% gist daa33988ada6dafa773d prism-glsl.js 851-873 %}

변환 예)

[gist id="daa33988ada6dafa773d" file="prism-glsl.js" lines="851-873"]

plugins/mytory-markdown/main.php에서 아래 라인을 찾는다.

$post['post_content'] = preg_replace('/<h1>(.*)<\/h1>/', '', $content);

이 부분을 아래와 같이 바꾼다.

$content = preg_replace('/<h1>(.*)<\/h1>/', '', $content);
$post['post_content'] = preg_replace('/<p>{%\sgist\s([\w\d]+)\s([\w\d-.]+)\s(([\d-]+)\s)?(?:%}<\/p>)/', 'https://gist.github.com/$1', $content);

이처럼 markdown에다 넣어주면 숏코드 형태로 변환되어 html로 입력이 된다.

{% gist daa33988ada6dafa773d prism-glsl.js 851-873 %}

워드프레스에 많은 syntexhighligher 플러그인들이 있는데, glsl shading 언어를 지원하는 건 그리 많지 않다. crayon syntax hightlighter가 이를 지원해서 써 왔는데, 썩 맘에 들지 않는 디자인과 시스템 전체에 부하가 걸리는 느낌이 싫어 prism.js를 쓰게 되었다.

Prism

Prism.js는 요즘 많이 사용되는 javascript syntexhighligher인데 깔끔한 디자인과 crayon에 비해 비교적 빠른 로딩속도로 인해 사용하고 싶은 마음은 있었는데, 지난번에도 역시 glsl을 지원하지 않아서 쓰지 못했던 기억이 났다. 이참에 방법을 알아보려고 이리저리 애를 쓰다 기존에 있던 구문강조 들 중 가장 glsl과 비슷한 것이 c-like 인 것을 알아내서, 이를 이용해서 glsl 랭귀지를 만들기로 했다. 워드프레스 플러그인을 설치하고 c문법에서 몇가지 구문을 추가하기로 했다. 방법은 다음과 같다.

문제

  • prism.js에서 “language-glsl” class를 인식하지 못한다.

해결방법

  • 가장 비슷한 language-clike 를 복사하여, glsl의 구문 몇가지를 추가하고 language-glsl을 추가한다.

순서

  1. prism.js를 지원하는 워드프레스 플러그인을 설치한다.
  2. 플러그인에서 참고하는 prism.js를 수정해서 c-like랭귀지 부분을 찾는다.
  3. c-like부분이 없는 경우 prismjs.com에 들어가서 c-like를 추가해서 js와 css를 download받는다.
  4. c-like부분 다음에 아래 소스를 추가해서 ‘language-glsl’구문을 입력받을 수 있게 만든다.

일단 워드프레스 플러그인은 몇가지가 있는데 그중에서도 WordPress › WP Prism Syntax Highlighter « WordPress Plugins 을 사용했는데, 마음에 드는 것을 사용하면 될 것 같다.

플러그인 디렉토리 안에 있는 prism.js파일을 열고 c-like부분을 찾아서 아래 구문을 추가한다.

/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+php+coffeescript+scss+bash+c+markdown&plugins=show-invisibles+show-language */
self = (typeof window !== 'undefined')
? window // if in browser
: (
(typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
? self // if in worker
: {} // if in node js
);
/**
* Prism: Lightweight, robust, elegant syntax highlighting
* MIT license http://www.opensource.org/licenses/mit-license.php/
* @author Lea Verou http://lea.verou.me
*/
var Prism = (function(){
// Private helper vars
var lang = /\blang(?:uage)?-(?!\*)(\w+)\b/i;
var _ = self.Prism = {
util: {
encode: function (tokens) {
if (tokens instanceof Token) {
return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);
} else if (_.util.type(tokens) === 'Array') {
return tokens.map(_.util.encode);
} else {
return tokens.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\u00a0/g, ' ');
}
},
type: function (o) {
return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1];
},
// Deep clone a language definition (e.g. to extend it)
clone: function (o) {
var type = _.util.type(o);
switch (type) {
case 'Object':
var clone = {};
for (var key in o) {
if (o.hasOwnProperty(key)) {
clone[key] = _.util.clone(o[key]);
}
}
return clone;
case 'Array':
return o.map(function(v) { return _.util.clone(v); });
}
return o;
}
},
languages: {
extend: function (id, redef) {
var lang = _.util.clone(_.languages[id]);
for (var key in redef) {
lang[key] = redef[key];
}
return lang;
},
/**
* Insert a token before another token in a language literal
* As this needs to recreate the object (we cannot actually insert before keys in object literals),
* we cannot just provide an object, we need anobject and a key.
* @param inside The key (or language id) of the parent
* @param before The key to insert before. If not provided, the function appends instead.
* @param insert Object with the key/value pairs to insert
* @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted.
*/
insertBefore: function (inside, before, insert, root) {
root = root || _.languages;
var grammar = root[inside];
if (arguments.length == 2) {
insert = arguments[1];
for (var newToken in insert) {
if (insert.hasOwnProperty(newToken)) {
grammar[newToken] = insert[newToken];
}
}
return grammar;
}
var ret = {};
for (var token in grammar) {
if (grammar.hasOwnProperty(token)) {
if (token == before) {
for (var newToken in insert) {
if (insert.hasOwnProperty(newToken)) {
ret[newToken] = insert[newToken];
}
}
}
ret[token] = grammar[token];
}
}
// Update references in other language definitions
_.languages.DFS(_.languages, function(key, value) {
if (value === root[inside] && key != inside) {
this[key] = ret;
}
});
return root[inside] = ret;
},
// Traverse a language definition with Depth First Search
DFS: function(o, callback, type) {
for (var i in o) {
if (o.hasOwnProperty(i)) {
callback.call(o, i, o[i], type || i);
if (_.util.type(o[i]) === 'Object') {
_.languages.DFS(o[i], callback);
}
else if (_.util.type(o[i]) === 'Array') {
_.languages.DFS(o[i], callback, i);
}
}
}
}
},
highlightAll: function(async, callback) {
var elements = document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');
for (var i=0, element; element = elements[i++];) {
_.highlightElement(element, async === true, callback);
}
},
highlightElement: function(element, async, callback) {
// Find language
var language, grammar, parent = element;
while (parent && !lang.test(parent.className)) {
parent = parent.parentNode;
}
if (parent) {
language = (parent.className.match(lang) || [,''])[1];
grammar = _.languages[language];
}
if (!grammar) {
return;
}
// Set language on the element, if not present
element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
// Set language on the parent, for styling
parent = element.parentNode;
if (/pre/i.test(parent.nodeName)) {
parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
}
var code = element.textContent;
if(!code) {
return;
}
code = code.replace(/^(?:\r?\n|\r)/,'');
var env = {
element: element,
language: language,
grammar: grammar,
code: code
};
_.hooks.run('before-highlight', env);
if (async && self.Worker) {
var worker = new Worker(_.filename);
worker.onmessage = function(evt) {
env.highlightedCode = Token.stringify(JSON.parse(evt.data), language);