2013. 11. 27.

HTML 레이어(layer). div 특정 위치에 겹쳐 띄우기. absolute와 relative 값 차이.

  공지사항이나 전달사항을 알리는데 창을 띄워서 팝업창을 사용할 수 있고, 한 화면 내에서 조그맣게 레이어(layer) 형식으로 필요한 정보를 전달 할 수도 있습니다. 허나 요즘은 창을 새로 띄우는 형식의 팝업창은 잘 사용되지 않죠. 브라우저 내의 팝업 차단 기능도 그렇고, 실제로 그다지 관심도 없는데 창이 무자비하게 뜨면 불괘하기도 합니다.

  레이어 라는 단어는 겹쳐서 포갠다는 느낌이 강하게 드네요. 이미지 편집툴을 사용할때도 정말 많이 접하게 되는게 레이어 입니다. HTML에서도 layer 태그가 있었습니다만, HTML5 에서부터 사용이 권장 되지 않습니다. 이런걸 Deprecated 됐다고 하지요.

  div 태그를 이용해서 원하는 위치에 레이어처럼 나타내 보도록 하겠습니다. div 태그의 style 속성 값에서 position, left, top, width, height 이 다섯가지만 있으면 원하는 곳 어디든 레이어를 띄워 줄 수 있습니다.

  position 값으로 다섯가지를 줄 수 있지만, absoluterelative 만 사용해도 무방 합니다.
  left 값은 왼쪽을 기준으로 얼마만큼 떨어져 있는가를 나타내는 값 입니다.
  top은 left 처럼 위에서 얼마나 떨어뜨리는가를 나타냅니다.
  width 값은 div 의 가로 크기, height는 div의 세로 크기를 의미 합니다.

  예제로 확인해 보도록 하겠습니다. 코드 하이라이트를 써서, 소스를 긁어 갈 수 있도록 하려고 했으나... 오늘따라 소스 처리 하는데 뭔가 애로사항이 많네요. 해당 소스 파일을 첨부 하려고 했는데 블로그스팟은 파일 첨부를 할 수 없는건가요; 허허.


  여기서는 position 값 absolute와 relative 두 가지만 눈 여겨 보시면 될듯 합니다. 부모 div와 자식 div가 있고, 자식 div 들의 position 값을 다르게 줘 봤습니다. 아래 결과 사진 캡처를 확인해 보시면 바로 아실 수 있으실꺼라 생각 합니다.



  absolute와 relative 속성 차이가 자식 div의 위치는 변경 시키진 않았지만, 부모 div의 내용물의 위치가 차이가 있네요. 오른쪽의 부모 div 3의 공란의 높이가 자식 div의 높이와 일치해 보입니다. 이제 대충 감이 오네요. position 속성의 값을 relative 로 두게 되면, 자식 div가 부모 div내에서 공간을 차지하게 됩니다. 그려내고자 하는 그림에 맞게 적절하게 써주면 되겠습니다.

  아참, 중요한 설명 하나를 빼먹었습니다. div style 속성 값중 z-index 라는 항목이 있는데요. 이 값은 div들 끼리 겹쳤을때 어떤 div를 위에 보여주기 위해 올리고, 어떤 div를 아래에 보여주기 위해 내리는지를 나타내는 값입니다. 이 값이 큰 div가 가장 위로 올라와서 브라우저에 보이는 div가 됩니다. 예제에서 div1과 3은 z-index 값이1, div2와 4는 z-index 값이 2로 설정 되어 있습니다. div2와 4의 z-index값이 부모 값 보다 크니 부모의 위에 나오게 되는 거겠죠. 작으면 부모 div에 가려져셔 보이지 않게 됩니다.


* 참조 사이트 링크
div tag
http://www.w3schools.com/tags/tag_div.asp
z-index
http://www.w3schools.com/cssref/pr_pos_z-index.asp

2013. 11. 26.

심플한 랜덤 지역별 채팅 어플. 즐거운 채팅 즐챗.


즐거운 채팅 즐챗은 간단하게 사용 할 수 있는 깔끔한 무작위 채팅 어플(앱) 입니다. 기본적으로 전체 지역이 설정 되어 채팅이 되며, 채팅 하고자 하는 지역을 선택하여 무작위 채팅을 시작 할 수도 있습니다.  상대방을 배려하는 대화, 매너있는 대화로 즐거운 대화를 만들어 갑시다.

* 상대방에게 사진을 전송하는 기능은 의도되지 않은 방향으로 사용 될 수 있어 추가할 계획이 없습니다.
* 대화하는 동안 모든 내용은 암호화 되어 처리되며 대화 종료나 앱을 종료 할 때 모두 삭제 처리 됩니다.
* 즐거운 채팅 즐챗은 스마트 기기의 고유값으로 사용자를 구분 합니다.
* 그 외 다른 개인정보는 일절 수집하거나 사용하지 않습니다.


# 구글 플레이 링크
https://play.google.com/store/apps/details?id=com.damnation.KINchat&hl=ko

# 검색 키워드
채팅, 랜덤채팅, 랜덤 채팅, 지역별채팅, 지역별 채팅, 만남, 무작위 채팅, 대화

# 스크린샷



2013. 11. 25.

(안드로이드, 아이폰 등) 스마트폰 모바일 접속 구분 소스 코드 모음.

안드로이드나 아이폰 등 모바일 기기의 브라우저 에서 접속한 사용자에게 PC용 웹페이지가 아닌 모바일 전용 페이지를 보여주는 게 상대적으로 작은 화면의 스마트기기엔 효과적일 것입니다.

모바일 환경에서 접속한 것인지 PC 환경인지 구분해주는 코드들은 잠깐 시간을 투자해 검색해보면 그리 어렵지 않게 구할 수 있습니다. 하지만 웹페이지들은 다양한 프로그래밍 언어로 제작되고,  다양한 언어의 웹페이지들을 운영하고 계신 경우라면 해당 언어에 맞는 코드를 찾는 게 나름 귀찮기도 하고 골치가 아플 때가 있습니다. 언어 별로 모바일과 PC를 구분해주는 코드를 제공해주는 편한 사이트에 어쩌다가 닿게 됐는데 정말 만족스럽더군요.
http://detectmobilebrowsers.com/

메인 페이지를 보면 여러가지 언어들의 아이콘들이 있습니다. 원하는 언어를 클릭해 보시면 텍스트(txt) 파일을 다운로드 할 수 있습니다. 모바일 환경을 판단 해주는 코드인데요. 적당한 곳에 복사해 넣기만 하면 됩니다.

코드를 보시면 마지막 부분에 http://detectmobilebrowser.com/mobile 이렇게 주소가 나와 있는데, 이 주소를 적당하게 자신에 맞게 변경해 주시면 됩니다. 기본적으로 모바일 환경일 때 페이지 이동에 대한 동작만 합니다. 다른 추가적인 작업이 필요하신 분은 필요에 맞게 수정하시면 될 듯 합니다.

자바스크립트의 객체를 반복문(for)으로 필드들 값 출력 해보기.

다른 프로그래밍 언어와 다르게 자바스크립트는 class문이 없는 프로토타입 기반의 언어입니다. 자바나 PHP에서 처럼 객체 정의하고 사용 하는데 익숙한 분들은 아마 저처럼 햇갈려 하시지 않을까 하는 생각이 듭니다. 자바스크립트 문법이 아직 익숙하지 않아 객체 관련해서 코드를 보는데 애를 좀 먹었습니다. 사실 객체니 뭐니 해봤자 변수와 함수들 모아놓은게 객체 일 뿐이니 간단하고 쉽게 생각 합시다.

객체를 생성하는데 여러가지 방법이 있습니다. 아무래도 자세히 알려면 책을 한 권 구입 해야할 것 같네요. 여기선 포스팅 제목처럼 객체 내 필드들의 값을 찍어 보는 방법에 대해서만 알아 보도록 합시다.

var o = {
    r: 'some value',
    t: 'some other value'
};

var o = new Object();
o.r = 'some value';
o.t = 'some other value';

동일한 객체를 만드는 두 가지 방법 입니다. 좀 더 찾아보니 객체 생성하는 방법들 부터 다양한게 머리가 지끈 지끈 하네요... 객체 내부를 돌아다니는 문법은 자바(Java)에서 향상된 for문 이라고 불리는 구문과 왠지 닮아 보입니다.

//Java 향상된 for 문
for( String name : nameArray ){
    System.out.println( name );
}

nameArray[1]을 name에 대입시켜서 반복문이 한 번 돌아가고, nameArray[2]를 name에 대입시켜서 또 반복문이 한 번 돌아가고 이렇게 배열의 끝까지 탐색 합니다. 자바스크립트에서도 이와 비슷한 느낌의 구문이 있네요. 물론 배열을 탐색하는 것과 객체 내 필드들을 탐색 하는 것은 다르긴 하지만, 자바스크립트가 워낙 그런 경계들이 애매모호한 느낌이라... 저만 그렇게 느끼는 건가요!? 아무튼...

for (myKey in myObj){
    alert ("myObj["+myKey +"] = "+myObj[myKey]);
}

myObj 객체 안의 녀석들을 myKey에 하나씩 담아 줍니다. 녀석이라고 표현한 이유는 저기서 받아오는 myObj는 단순한 변수 일수도 있고, 함수 이기도 하고 다른 객체 일 수도 있어서 딱히 무엇이라고 할 수가 없네요. 역시 경계선이 애매모호 합니다.


스택오버플로우 출처.
http://stackoverflow.com/questions/418799/what-does-colon-do-in-javascript

PHP 양력 날짜를 음력 날짜로 변환해주는 함수.

달력에 보면 조그맣게 음력 날짜가 표시되어 있습니다. 우리나라도 아직까지 음력으로 생일이나 기념일을 하시는 분들이 많이 계시죠. 음력에 대한 지식이 전무했던 지라... 전 그저 양력을 음력으로 변환해 주는 함수가 존재하고 양력 날짜 인수만 갖다 넣으면 음력 날짜가 뿅! 하고 나올 줄만 알았습니다. :-(

제가 원했던 것은 20131125 라는 양력 날짜를 넣으면 20131023 이란 음력 날짜를 반환해주는 함수였을 뿐인데... 이게 간단한 문제가 아닌거 같더군요. 양력을 음력으로 바꾸는데는 간단한 수식으로 해결 할 수가 없다고 합니다. 천체의 운행을 기법으로 하는.... 뭐...

이것저것 함수들을 찾아보니 일정 기간의 음력을 데이터베이스 처럼 입력해 놓고 처리하는 방식이고, 특정 년도 이전까지만 유효하게끔 되어 있습니다. 그 만큼 골치아프고 어려운가 봅니다... 게다가 양/음력 날짜를 변환해주는 웹페이지들도 있던데 변환 시 날짜가 아주 엉망으로 나오는 곳도 한 두 군데가 아니더군요.

습관처럼 구글에서 영어로 검색을 한참동안 했는데, 검색을 하면 할 수록 감이 더 안잡히는 불상사가 발생 합니다. 음력이라기에 lunar date 란 검색어로, 양력은 solar date 검색어로 이리저리 찾아봤습니다만... islamic calendar(이슬람 달력)과 chinese calendar(중국 달력) 이 연관되서 나오기 시작하고, Gregorian 날짜와 Julian 날짜 뭐... 하하... 이슬람력이 중국으로 그리고 우리나라로 온 것 같고...

깊은 절망에 빠져들게 만들었던 검색결과의 링크들 입니다.
현재 시간을 Lunar Standard Time 으로 바꿔 줍니다. 46-09-16은 도무지 무슨 날짠지 모르겠네요...
http://random.kakaopor.hu/lunar-date-function-for-php-time-on-the-moon-lst
http://lunarclock.org/convert-to-lunar-standard-time.php

해당 날짜에 맞는 달의 상태를 계산에 줍니다. 보름달인지 반달인지 그런거요. 밑에 것도...
http://jivebay.com/calculating-the-moon-phase/
http://www.phpclasses.org/package/1201-PHP-Calculates-the-phase-of-the-Moon.html

위에서 말한 Gregorian, Julian 녀석들 자바스크립트 라이브러린듯 합니다.
http://stackoverflow.com/questions/12637268/convert-julian-date-to-solar-date-in-javascript
http://jesusnjim.com/common/julian-gregorian.html#tog


영혼 없는 검색질 중 문득 든 생각이 한국어페이지로 검색 하는게 더 나을꺼 같다는 생각이 들더군요. 한국어 웹페이지를 검색하니, 답이 보이기 시작 합니다. 동일한 소스를 여러곳에서 찾았는데 아무리 봐도 해당 블로그는 소스 출처가 아닌 것 같더군요. 긁어서 붙여넣은 모습이 보여서 해당 소스의 출처는 찾질 못했습니다. 혹시 출처를 아시는 분 계시면 알려 주시면 바로 반영 하도록 하겠습니다.

잡소리가 엄청나게 길었네요. 아래 소스를 적당하게 붙여넣으시고 사용하시면 됩니다. 벽에 걸려있는 달력이랑 비교해보니 잘 맞게 나오더라구요. 만족 합니다. 반환되는 객체의 time 필드가 time() 함수를 호출 했을때 반환 값 형식인 timestamp 인듯 합니다. date() 함수로 포멧을 주고 변환해주면 음력 날짜를 간단하게 얻을 수 있습니다.

<?
function sunlunar_data() {
return
"1212122322121-1212121221220-1121121222120-2112132122122-2112112121220-2121211212120-2212321121212-2122121121210-2122121212120-1232122121212-1212121221220-1121123221222-1121121212220-1212112121220-2121231212121-2221211212120-1221212121210-2123221212121-2121212212120-1211212232212-1211212122210-2121121212220-1212132112212-2212112112210-2212211212120-1221412121212-1212122121210-2112212122120-1231212122212-1211212122210-2121123122122-2121121122120-2212112112120-2212231212112-2122121212120-1212122121210-2132122122121-2112121222120-1211212322122-1211211221220-2121121121220-2122132112122-1221212121120-2121221212110-2122321221212-1121212212210-2112121221220-1231211221222-1211211212220-1221123121221-2221121121210-2221212112120-1221241212112-1212212212120-1121212212210-2114121212221-2112112122210-2211211412212-2211211212120-2212121121210-2212214112121-2122122121120-1212122122120-1121412122122-1121121222120-2112112122120-2231211212122-2121211212120-2212121321212-2122121121210-2122121212120-1212142121212-1211221221220-1121121221220-2114112121222-1212112121220-2121211232122-1221211212120-1221212121210-2121223212121-2121212212120-1211212212210-2121321212221-2121121212220-1212112112210-2223211211221-2212211212120-1221212321212-1212122121210-2112212122120-1211232122212-1211212122210-2121121122210-2212312112212-2212112112120-2212121232112-2122121212110-2212122121210-2112124122121-2112121221220-1211211221220-2121321122122-2121121121220-2122112112322-1221212112120-1221221212110-2122123221212-1121212212210-2112121221220-1211231212222-1211211212220-1221121121220-1223212112121-2221212112120-1221221232112-1212212122120-1121212212210-2112132212221-2112112122210-2211211212210-2221321121212-2212121121210-2212212112120-1232212122112-1212122122120-1121212322122-1121121222120-2112112122120-2211231212122-2121211212120-2122121121210-2124212112121-2122121212120-1212121223212-1211212221220-1121121221220-2112132121222-1212112121220-2121211212120-2122321121212-1221212121210-2121221212120-1232121221212-1211212212210-2121123212221-2121121212220-1212112112220-1221231211221-2212211211220-1212212121210-2123212212121-2112122122120-1211212322212-1211212122210-2121121122120-2212114112122-2212112112120-2212121211210-2212232121211-2122122121210-2112122122120-1231212122212-1211211221220-2121121321222-2121121121220-2122112112120-2122141211212-1221221212110-2121221221210-2114121221221";
}

function SolaToLunar($yyyymmdd) {
$getYEAR = substr($yyyymmdd,0,4);
$getMONTH = substr($yyyymmdd,4,2);
$getDAY = substr($yyyymmdd,6,2);

$arrayDATASTR = sunlunar_data();
$arrayDATA = explode("-",$arrayDATASTR);
$arrayLDAYSTR="31-0-31-30-31-30-31-31-30-31-30-31";
$arrayLDAY = explode("-",$arrayLDAYSTR);
$dt = $arrayDATA;



for ($i=0;$i<=168;$i++) {
  $dt[$i] = 0;
  for ($j=0;$j<12;$j++) {
    switch (substr($arrayDATA[$i],$j,1)) {

    case 1:
      $dt[$i] += 29;
      break;

    case 3:
      $dt[$i] += 29;
      break;

    case 2:
      $dt[$i] += 30;
      break;

    case 4:
      $dt[$i] += 30;
      break;
    }
  }

  switch (substr($arrayDATA[$i],12,1)) {

  case 0:
    break;

  case 1:
    $dt[$i] += 29;
    break;

  case 3:
    $dt[$i] += 29;
    break;

  case 2:
    $dt[$i] += 30;
    break;

  case 4:
    $dt[$i] += 30;
    break;
  }
}

$td1 = 1880 * 365 + (int)(1880/4) - (int)(1880/100) + (int)(1880/400) + 30;
$k11 = $getYEAR - 1;
$td2 = $k11 * 365 + (int)($k11/4) - (int)($k11/100) + (int)($k11/400);

if ($getYEAR % 400 == 0 || $getYEAR % 100 != 0 && $getYEAR % 4 == 0) {
  $arrayLDAY[1] = 29;

} else {
  $arrayLDAY[1] = 28;
}

if ($getMONTH > 13) {
  $gf_sol2lun = 0;
}

if ($getDAY > $arrayLDAY[$getMONTH-1]) {
  $gf_sol2lun = 0;
}

for ($i=0;$i<=$getMONTH-2;$i++) {
  $td2 += $arrayLDAY[$i];
}

$td2 += $getDAY;
$td = $td2 - $td1 + 1;
$td0 = $dt[0];

for ($i=0;$i<=168;$i++) {
  if ($td <= $td0) {
    break;
  }
  $td0 += $dt[$i+1];
}

$ryear = $i + 1881;
$td0 -= $dt[$i];
$td -= $td0;

if (substr($arrayDATA[$i], 12, 1) == 0) {
  $jcount = 11;

} else {
  $jcount = 12;
}

$m2 = 0;

for ($j=0;$j<=$jcount;$j++) { // 달수 check, 윤달 > 2 (by harcoon)
  if (substr($arrayDATA[$i],$j,1) <= 2) {
    $m2++;
    $m1 = substr($arrayDATA[$i],$j,1) + 28;
    $gf_yun = 0;
  } else {
    $m1 = substr($arrayDATA[$i],$j,1) + 26;
    $gf_yun = 1;
  }
  if ($td <= $m1) {
    break;
  }
  $td = $td - $m1;
}

$k1=($ryear+6) % 10;
$syuk = $arrayYUK[$k1];
$k2=($ryear+8) % 12;
$sgap = $arrayGAP[$k2];
$sddi = $arrayDDI[$k2];
$gf_sol2lun = 1;

if($m2<10) $m2="0".$m2;
if($sday<10) $td="0".$td;

$Ary[year]=$ryear;
$Ary[month]=$m2;
$Ary[day]=$td;
$Ary[time]=mktime(0,0,0,$Ary[month],$Ary[day],$Ary[year]);

return $Ary;

}

//사용 예
$lunar_date = SolaToLunar($f_date);
echo date("Y-m-d", $lunar_date[time]);
?>

2013. 11. 21.

내가 만든 앱 마켓에 등록하기. 구글 플레이(PLAY) 개발자 등록을 해보자!

구글 플레이 개발자 계정이 하나 더 필요하게 되었네요. 몇 년 전이라 어떻게 등록했는지 절차가 가물 가물 합니다. 등록 화면도 바뀐 것 같고...  그래서 이렇게 포스팅해 봅니다. 등록 하는데 필요한 비용 25달러는 변하지 않았네요. 등록 할 때 한 번만 내면 됩니다.

등록 하고자 하는 구글 계정으로 로그인 후 아래 링크로 이동 하면 다음과 같은 등록 화면을 만날 수 있습니다. 민감한 개인 정보들은 검은색으로 무식하게 덮어 놨습니다.


검은색으로 칠한 부분에 로그인한 정보가 나타 납니다. 왼쪽 아래쪽에 결제 페이지로 이동 하기를 클릭해서 진행 하시면 됩니다. 다른 내용은 없네요. 왼쪽 아래에 개발자 배포 계약 검토 및 동의 부분에서 체크 해주는 것과 25달러 라는것 뿐입니다.

수수료를 결제할 수 있도록 신용카드를 준비하세요. 라고 했는데 체크카드로도 진행 잘 되네요. visa가 붙은 체크카드면 문제가 없나 봅니다.


요구하는거 적어 주면 됩니다. 카드 번호 적어주면 알아서 카드 종류가 오른쪽에 나오네요. 근데 각 카드 번호가 지구에서 고유한 건가요? 입력란 옆에 visa나 다른 종류들이 희미하게 있고 카드 번호를 입력하면 해당되는 종류가 위 사진처럼 나타나는데 어떻게 visa인걸 아는건지 허허. 카드 번호가 16자리니까 큰 숫자긴 하다만...

카드 유효기간 넣어주고 보안코드는 카드 뒤에 4자리, 3자리 숫자 있는거 중에 3자리 숫자. 그리고 청구지 주소를 넣으라고 합니다. 위 캡처 화면은 카드 정보 수정 화면 입니다. 처음 진행할때 캡처 하는걸 깜빡 했거든요. :-D 근데 별반 다른건 없습니다. 거의 동일 합니다.

청구지 주소라고 되있는데, 대한민국 선택하고, 본인 이름 영어로 적고, 우편번호만 적어 주면 됩니다. 000-000 처럼 우편물에 흔히 보이는 그 우편 번호. 주소 적는건 없는데 주소 얘기가 있어서 처음에 좀 당황했습니다. 허허.


지금까지 입력한 것들이 '결재 수단' 으로 만들어 집니다. 카드 여러개를 등록하면 선택할 수 있나 봅니다. 별거 없네요. 구입을 눌러 줍니다.



이제 구체적인 주소를 물어 봅니다. 아래쪽 7가지 입력란에 적당하게 입력해 주시면 됩니다. 주소는 영어로 입력해 줍니다. 위의 두 번째 캡처사진 처럼 도로명주소로 검색하고 간단하게 영문 주소를 확인 할 수 있습니다.

위 두번째 캡처사진의 주소로 예제를 들자면,
첫번째. 서울, 부산, 울산 ... 셀렉트 박스 형식이니 선택해 줍니다.
두번째. Seongdong-gu, Seoul
세번째. 28, Seoulsup 2-gil
네번째. Seoulsup vila 101ho
다섯번째. 이름이 자동으로 들어가 있습니다. 처음 부분에서 입력한 값이네요.
여섯번째. 우편번호도 이름이랑 같이 입력했었는데, 비어있네요. 예제에선 133-923으로.
일곱번째. 전화번호 입니다. 전 그냥 010...에서 맨 앞에 0만 빼고 그대로 적었습니다. 한국은 국가코드가 +82 입니다. 국가 코드까지 포함한다면 +8210... 이 되겠네요.


슬슬 귀찮고 번거롭네요. 요구 하는게 이렇게 많다니... 그리고 다음으로 진행하는 버튼은 아까부터 "구입" 이네요. 괜히 시비 걸어 봅니다.


이제 다 되 가나 봅니다. 결제 처리 중... 문구만 읽고 아래쪽은 읽지 않은 채 5분 동안 멍하게 있었습니다. 창은 사라지지 않더군요. 백그라운드에서 결제가 처리 중이라니, 등록 계속하기 버튼을 눌러 줍니다.


크흐. 드디어 마지막 입니다. 개발자 이름은 어플을 등록했을때 어플 명 아래쪽에 뜨는 그 이름 입니다. 난 내 이름을 걸겠다! 하시면 이름을 쓰셔도 되고, 사업체 명을 쓰셔도 되구요. 이 메일 주소는 현재 로그인한 이메일 주소 그대로 적어 주시면 됩니다.

위에서 전화번호 적을때 국가코드 없이 적었었는데, 여기서는 국가코드 포함해서 +8210******** 형식으로 적어 줬습니다. 보시는 바와 같이 국가코드를 꼭 포함하라고 되어 있네요. 흐음. 이런 저런거 기입 하는걸 좋아 하지 않는 지라 마음이 좋지 않습니다.


등록 완료 버튼을 누르시면 개발자 등록은 정말 이제 끝이 납니다. 노란색으로 알림창이 떠있네요. 최대 48시간 걸린다고 합니다. 제가 토요일날 등록 한 걸로 기억 합니다. 주말이니 주말은 빼고 월요일 부터 계산한다고 하면 화요일쯤 되리라 생각 했지요. 정말 화요일 오후 늦게 등록 되었다는 메일이 왔습니다.

결제가 진행 중이어도, 마켓에 올릴 수만 없을 뿐이고 바로 그 직전까지 모두 작성해 놓을 수 있습니다. 헌데 등록한다고 진을 빼서 아무것도 하기 싫더라구요. 허허. :-D

2013. 11. 20.

HTML 폼(form) 크롬에서 두번 눌러야 제출(submit) 될때.

기존 소스를 좀 건드려야 돼서 수정하고 테스트 하다가, 어이없게 시간을 뺏긴 경우가 있어서 포스팅 하나 해 봅니다. 간단한 form과 자바스크립트 함수 입니다.
<form action="javascript:check_before_submit(this);">
...
<input type="image" ... >
</form>
<script language="JavaScript">
function check_before_submit( f ){
    ...
    f.action = "http://...";
    f.submit(); // or return true;
}
</script>
form안의 image 타입의 input은 submit 버튼 역할을 합니다. 버튼을 누르면 action 속성이 향하는 자바스크립트 함수를 호출 하겠죠. 함수 내에서 action 속성을 변경하고 form의 submit() 함수를 호출 합니다.

IE에서 탈이 없길래 form 부분은 보지도 않았습니다. 크롬에서 테스트 중에 버튼을 처음 눌렀을 때는 아무 반응이 없고, 두 번째 눌렀을 때 반응이 오더군요. 괴상하다 싶어 뒤져보니 소스가 저렇게 돼있었네요.

크롬에서 action 속성이 가리키는 함수에서 action 값을 바꾸면 에러가 발생하는 걸까요. 자세한 건 모르겠지만, 바로 아래 submit() 함수는 실행이 되지 않네요. return 구문으로 바꿔도 동일 합니다.

그래서 처음 눌렀을 때 함수 호출을 통해 action 속성을 바꾸고, 두 번째 클릭했을 때 바로 바뀐 값으로 submit 되는 거라 추측이 되네요. 아래처럼 수정하면 IE나 크롬이나 문제 없이 잘 됩니다. form의 action 속성은 onsubmit으로. 자바스크립트 함수는 true값을 리턴 하는 것으로!
<form onsubmit="return check_before_submit(this);">
...
<input type="image" ... >
</form>
<script language="JavaScript">
function check_before_submit( f ){
    ...
    f.action = "http://...";
    return true;
}
</script>

2013. 11. 18.

JQUERY iframe 또는 팝업창에서 부모 엘리먼트 참조하기.

팝업창이나 한 페이지에서 iframe을 이용해서 다른 페이지를 불러 왔을때 부모의 엘리먼트 값을 참조하거나 바꿔줘야 하는 경우가 있습니다.

팝업창의 경우에 window.opener.함수(매개변수, ...); 와 같이 부모가 가지고 있는 함수를 호출하면서 필요한 인자를 넘겨 부모측에서 처리하게 하면 되고, iframe의 경우엔 window.parent.함수(매개변수,  ...); 처럼 parent를 호출해서 처리하면 됩니다.

뭐 그리 대단한 연산이 있는 것도 아닌데, 자식 창에서 처리해서 부모 창에 반영 되게 하려고 바꾸려던 찰나에 꼭 jquery 문장으로 쓰고 싶다는 생각이 듭니다. 코딩 하다보면 효율이고 나발이고 왠지 싫은게 있고 꼭 이렇게 하고 싶을 때가 생기더라구요. 허허.

JQUERY로 저 문장을 어떻게 바꾸면 좋을까...
$("# ID " ).val( ... ); 이렇게 적어놓고 어떻게 부모창의 엘리먼트에 접근 해야 하나 찾아 봤습니다.
http://stackoverflow.com/questions/726816/how-to-write-this-in-jquery-window-parent-document-getelementbyidparentprice

window.parent.document.getElementById(' ID ').innerHTML 
$('#ID', window.parent.document).html(); //JQUERY :-D

오호라. jquery selector 셀렉터 두번째 인자로 엘리먼트를 찾을 범위(?)를 줄 수 있다는 걸 알았네요. 만족스러운 검색을 마치고 적용해 보았습니다만 말을 듣지 않네요. 또 이리 저리 찾아 보다가...
http://stackoverflow.com/questions/2167455/how-to-access-parent-window-object-using-jquery

window.opener.$("#ID") ...

window객체의 parent는 iframe에서, opener는 팝업창에서 사용 합니다. opener를 parent로 바꿔 봅니다. IE, Chrome에서 잘 돌아 갑니다만... 마음에 들지 않습니다... 처음 찾은 저 코드가 왜 안되는지 마냥 궁금합니다. 그리고 괜히 변태같은 고집이 피어 오르네요. 일단 일은 해야되니 미뤄 놓고 시간 날때 틈틈히 찾아 봤습니다.
http://stackoverflow.com/questions/1133276/how-to-access-parent-document-elements-using-jquery-in-firefox

셀렉터에 파라미터를 2개 사용하는 문장에 대한 질문이고, IE와 Chrome에선 말을 듣지 않으나 firefox에서는 된다고 합니다. 만약 firefox에서 된다면 빌어먹을 브라우저 때문이군! 이렇게 생각하며 접어 버릴 생각으로 firefox를 깔아 봅니다. 그리고 확인해 봅니다! 안됩니다... :-O

http://stackoverflow.com/questions/9031031/jquery-selector-context
좀 더 뒤적거리다가 결국 포기 합니다. 마지막 링크에선 이런 글이 있네요.

so $('span', this) is equivalent to $(this).find('span').

으으음. 셀렉터에서 두번째 파라미터를 줘봤자 내부적으로는 find() 함수를 위와 같이 써서 처리 한답니다. 그래서 $(window.parent.document).find("#ID")... 로 변경해봤으나! 여전히 안됩니다.

* jquery 라이브러리 버전이 문젠가 싶어서 바꿔봤으나 실망만... 아아. 이번 변태같은 고집은 꺾을 수 밖에 없네요... 누가 아신다면 가르쳐 주세요...

PHP 오늘 어제 내일 날짜 구하기 date() mktime() time() 함수.

PHP에서 특정 날짜를 구하는 데는 date()와 mktime() 함수만 알고 있으면 걱정 없습니다. date() 함수는 2가지의 인수를 가집니다. 첫 번째 인수는 시간을 어떻게 출력할 것 인가에 대한 포맷입니다.
년도만 출력할 수 도 있고, 현재 시간만 출력 할 수도 있습니다. date("Y") 는 현재 년도를, date("G")는 현재 시간을(0 - 23시간) 출력해 줍니다.

그럼 기준이 되는 시간은 무엇일까 하는 생각이 듭니다. 두 번째 인수가 기준이 되는 시간 입니다. 두 번째 인수는 넣어도 되고 넣지 않아도 됩니다. 없을 경우 time() 함수의 결과 값이 기본 값으로 들어 갑니다. time()함수는 1970년 1월 1일부터 지금 이 순간 까지 경과한 시간을 초로 반환해 주는 함수 입니다.

date("Ymd") 라고 하면 오늘 날짜를 구할 수 있겠네요. 결과는 20131118 입니다. 두 번째 인수가 안 들어 갔으니 기본 값으로 현재 시간이 들어가는 거구요. 결국 date()는 특정 시간을 어떤 형태로 출력해 주는지 정해 주는 녀석인 것 같습니다.
http://php.net/manual/kr/function.date.php

그럼 어제와 내일 날짜를 구하기 위해선 두 번째 인수를 계산해서 넣어줘야 됩니다. mktime()은 1970년 1월 1일부터 특정 날짜까지 경과한 초를 반환해 줍니다. 인수 없이 mktime()을 호출하면 time()을 호출한 것과 결과가 같습니다.

mktime(시간, 분, 초, 달, 일, 년, is_dst) 의 형식을 가집니다. 마지막 인수는 시간대와 관련된 인수인 듯 합니다 만, PHP5.1 부터 더 이상 사용을 권장하지 않는다고 합니다. mktime() 함수의 인수들은 어떻게 넣느냐. 각 자리마다 구하고 싶은 값들을 넣으면 됩니다. 시간은 0부터 23까지. 분과 초는 0부터 59까지...
http://php.net/manual/en/function.mktime.php

이 함수는 똘똘 합니다. 만약 분을 60이라고 넣으면 알아서 시간 값을 1 올리고 분은 1이 됩니다. 달과 일의 경우도 말일이 28일, 29일, 30일, 31일의 경우가 있는데, 해당 년도와 해당 달을 참고해서 알아서 처리해 줍니다. 구글링을 통해서 찾아 볼 수 있는 PHP관련 날짜 함수는 이런 성질을 이용해서 작성 되어 있습니다.

이제 아래 코드가 한 눈에 들어오리라 생각 합니다.
date("Ymd", mktime(0,0,0, date("m"), date("d")-1, date("Y"))); //어제
date("Ymd", mktime(0,0,0, date("m"), date("d")+1, date("Y"))); //내일
위 코드에서 날짜 포맷 부분을 date("Ymd H:i:s", ... ); 시분초가 나오게 해보면 00:00:00이 나옵니다. mktime()의 1~3 인수를 0으로 줬기 때문인데요. date() 함수를 이용해서 시분초에 해당하는 값을 넣어주면 됩니다.

시분초를 포함한 최종 코드는 아래와 같습니다.
date("Ymd H:i:s", mktime(date("H"), date("i"), date("s"), 
                         date("m"), date("d")-1, date("Y")));
date("Ymd H:i:s", mktime(date("H"), date("i"), date("s"), 
                         date("m"), date("d")+1, date("Y")));

* 특정 날짜에서 전날, 다음날을 구하고 싶다? 날짜 형식은 20131118 이라고 가정. 다른 거 없이 날짜를 쪼개서 적당하게 넣어 주기만 하면 됩니다. 구분자가 포함됐으면 없애버리고 처리하면 되겠죠.
function get_yesterday( $day ){

    $y = substr( $day, 0, 4 );
    $m = substr( $day, 4, 2 );
    $d = substr( $day, 6, 2 );

    return date("Ymd", mktime(0,0,0, $m, $d-1, $y));
}
날짜 구하기. 간단하고 편하네요 :D

2013. 11. 13.

HTML window 객체의 pageYOffset와 outerHeight 값이 undefined.

현재 창의 높이(height)와 현재 얼마만큼 스크롤이 됐는지(offset) 을 구해야 하는데 자꾸 값은 안주고 undefined만 내뱉어서 당황 스럽습니다.

window.pageYOffset은 스크롤바의 offset을 반환 합니다. 크롬에서는 값이 제대로 나오지만 IE8에서는 도통 나오질 않고 undefined만 확인 할 수 있죠. IE10에서는 문제 없이 잘 나옵니다.
https://developer.mozilla.org/en-US/docs/Web/API/Window.scrollY
var y = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
크로스 브라우징을 위한 코드 입니다. pageYOffset의 값이 없을때, 같은 값이 들어있는 다른 이름의 변수들을 참조 하네요. 위의 코드를 사용하면 모든 브라우저에서 원하는 값을 가져올 수 있습니다.

다음 window.outerHeight는 현재 창의 높이 입니다. 스크롤바는 상관없이 눈에 보이는 현재 높이 값 입니다. 창에서 주소표시줄과 같은 높이들도 포함된 값입니다. 이 값도 IE8에서는 undefined만 뱉어 냅니다. 아. 찢어발기고 싶은 IE8. 허접쓰레기 같은 그림판 캡처를 첨부 합니다. window.innerHeight는 html이 표시되는 부분의 높이값만 가집니다.

왼쪽이 크롬, 오른쪽이 IE8 입니다.

http://www.w3schools.com/jsref/obj_window.asp
첫번째 값은 스크롤 offset값.
두번째 값은 창 높이.
세번째부터 다섯번째 값은 첫번째 값과 같은 변수들 입니다.

w3schools 만을 참고해서 하다보면 IE8에서 곤란해지는 일이 종종 발생 합니다. 캡처를 확인해보면 IE8은 완전 반대로 값을 출력해 주네요. 하아.

매번 이렇게 헤매야 할까요. 이런거 신경 안쓰게 해주는 JQUERY로 같은 결과 값을 얻어 보도록 하죠. JQUERY를 사용하면 IE8에서도 문제 없이 값을 가져 올 수 있습니다.
$(window).scrollTop() == window.pageYOffset //그리고 3 - 5번째 값들
$(window).height()    == window.innerHeight
크로스 브라우징을 생각해서 JQUERY를 사용하도록 합시다아. 머리아프지 말고... 헌데 만능은 아니더라고 하더군요. 그저 믿고 있다가 크게 한방 당하신 분이 계시더라는...


* "좋은 것이 빠지고 난 뒤에 남은 허름한 물건"을 뜻하는 '허섭스레기'. 지난 8월 31일에 바뀐 표준어 규정에 따라 '허접쓰레기'도 표준말이 되었다고 합니다.

2013. 11. 12.

다크나이트 라이즈 브루스 웨인의 저택. 노팅엄 월라튼 공원.



영화 다크나이트 라이즈(dark knight rises)에서 배트맨(브루스 웨인) 저택의 촬영지가 궁금하여 구글 지도를 통해 눈으로나마 여행을 떠나보려 합니다. 촬영 장소인 대저택의 명칭은 Wollaton Hall이고 영국에 위치해 있습니다. 아래 지도에서 A 말풍선 위치 입니다.

예전에 영국에 배낭여행겸 갔을때는 런던에만 머물러 있었습니다. 대부분을 걸어서만 다녔고, 지인들과 함께 이동할땐 지하철을 타기도 했었죠. 다리가 아주 아주 아주 아팠던 기억이 나네요. 많이 걸어 보긴 했지만 이번 목적지까지 걷진 못할것 같네요.


세인트 판크라스(st. pancras) 역에서 기차를 타고 이동해야 합니다. 영국 여행할때 워털루 역 근처에 짐을 뿌려놓고 걸어다닌 덕에, 하루에도 몇 번씩 빅벤과 런던아이를 봤던 기억이 나네요. 파리로 이동할때 유로스타를 타고 이동했는데, 워털루 역에서 세인트 판크라스 역까지 걸어 갔었습니다. 지도로 보면 그냥 쭉 가버리면 될 것 같았는데 정말 많이 해맸다는... 한 두시간 정도 걸은거 같네요. 무거운 가방도 메고...



하. 여길 다시 보니 반갑네요. 세인트 판크라스 역까지 걸어가는 길에 역인줄 알고 들어가서 몇 분을 해맸던 곳입니다. 호텔 인가요?


기차표를 예매를 해야겠네요. 뭐든지 미리 예약해야 싸게 먹히는건 진리인듯 합니다. 세인트 판크라스 역에서 노팅엄 역까지 가는 티켓 예매. 지금 글 작성하는 날짜 바로 그 다음 날짜로 해서 캡쳐를 해봤습니다. 그런것 치고는 그리 비싼편은 아니네요. 여기저기 블로그들 보니 미리 예약해서 10파운드로 예매하셨다는 분들도 계시더라구요.


위 링크 타고 들어가셔서 예매 하시면 될 듯 합니다. 갑자기 예전에 런던에서 파리갈때 유로스타 기차표를 어디서 예매했는지 기억이 안나서 당황스럽네요. 인터파크였나... 무튼 저 영어 활자가 눈을 어지럽히는 곳에서 예매 하지 않은건 확실 합니다. 하하.

자! 이제 머릿속으로 예매도 했고 기차도 탑승하고 내려보도록 하죠. 예전에도 그랬고 지금도 그렇고, 여행 준비는 전지전능한 구글 맵으로 합니다. 처음 가보는 곳이니 구글 스트리트 뷰로 눈에 좀 미리 익히는 거죠.


노팅엄 역에 내리면 이런가 봅니다. 한번 봐두면 아무래도 처음 도착 했을때 마음이 편하더라구요. 눈으로 숙소도 잡아보면 좋겠지만, 바로 이동 해보도록 합니다. 목적지인 월라튼 홀(Wollaton Hall)은 공원 안에 있었네요.


저희는 지금 '노팅엄' 하얀 레이블에 있는 겁니다. 아아. 멀고 머네요. 이정도면 걸어가도 될꺼 같은데? 허나 치밀하게 교통편도 알아봅니다.


검은색으로 표시된 부분은 걸어야 하는 부분입니다. A 말풍선이 노팅엄역을 나타내고, B 말풍선이 목적지를 나타냅니다. 파란색으로 표시된 부분은 버스를 타고 이동하는 구간이구요. i4 버스를 이용 하나 봅니다. 노선 번호가 독특 하네요. 구글맵이 알려준 소요시간은 24분 이었습니다. 허나 전 45분 거리를 2시간에 가는 놀라운 능력을 가지고 있죠.


내부 사진도 있어면 좋으련만, 아쉽네요. 아무튼 Sandiacre방면으로 가나 봅니다. 그림에 포함된 노선 정보 라는 사이트를 참고하셔도 될듯 하고(저 주소는 캡쳐하면서 알았네요) 따로 검색한 링크는 요기.
http://www.stapleford-notts.co.uk/trent-barton-i4-bus-timetable.htm

점점 피곤해지고 돈은 점점 줄어 들고 있습니다만, 목적지도 다가오고 있네요! 버스타면 가장 두려운게 어디서 내려야 하는지 모르겠다는것 아닐까요. 전 스마트폰 로밍해가서 지도로 위치 확인하고 내렸었습니다. 허나 어마어마한 속도 때문에 속이 썩어 들어가던 기억이 납니다...

타이밍 좋게, 정확한 곳에 내렸나 봅니다. 내리면 이런 모습이 눈에 보이리라 믿습니다.


발에 힘이 다시 불끈 하고 생기면서 열심히 걸어 들어갑니다. 공원이라 경치도 좋고 걷기도 좋으리라 생각합니다. 이리저리 사진도 찍고 눈알도 굴려가며 이제 목적지에 도착 합니다!


아아. 직접 가서 눈으로 보고 싶네요. 언제 갈진 모르지만 그 날을 위해 이렇게 계획을 짜 봅니다. 다들 즐거운 여행 하세요~


# 영화를 캡쳐해서 첨부하고 싶었으나, 저작권 문제가 무서워 포기 합니다...

* 첫번째 사진 출처.
http://www.gardenvisit.com/garden/wollaton_hall_garden

* 기차편 캡쳐.
http://nationalrail.co.uk/

* 그 외 구글 지도에서 캡쳐.
https://maps.google.com/

2013. 11. 11.

안드로이드 기본 브라우저와 크롬. fieldset 라디오 버튼 onchage 이벤트 차이.

웹페이지를 안드로이드 내장 기본 브라우저와 크롬에서 테스트 하는데, 어느 한쪽에서 이벤트가 발생하지 않는 일이 생겨 좀 찾아봤습니다. 관련 부분은 언제나 그렇듯 간단한 녀석.
<fieldset onchange="myFunc();" >

    <input type="radio" name="RB" id="a" value="value_a"/>
    <label for="a">a</label>

    <input type="radio" name="RB" id="b" value="value_b"  />
    <label for="b">b</label>

</fieldset>
myFunc()의 함수에는 간단하게 alert()으로 현재 선택된 라디오 버튼의 value를 출력해 주는 것이라 가정 합니다.

크롬에서는 라디오 버튼을 조작할때 마다 정상적으로 myFunc()가 호출 됩니다. 그러나 안드로이드 기본 내장 브라우저에서는 도통 응답이 없네요. fieldset 태그를 안드로이드 기본 브라우저에서 100% 지원하지 않는 것 같아서 이런 저런 이벤트 함수를 넣어서 확인해 봤습니다.

disable="disable" 속성을 추가해 봤는데, 크롬에서 정상적으로 disable 비활성화가 됐으나 안드로이드 기본 브라우저는 아무런 반응이 없습니다. 안드로이드 기본 브라우저에서 fieldset 태그를 완전히 배제한 것일까요.

onmouseup()의 경우에는 두 브라우저 모두 반응이 없습니다만, onmousedown()의 경우는 안드로이드 기본 브라우저에서만 이벤트가 발생 합니다. 하하 거참 햇갈리네요.  모바일 웹뷰에서의 터치 이벤트가 HTML의 어떤 이벤트로 전달 되는지 궁금 하네요.

브라우저마다 조금씩 다른건 별 수 없습니다만 신경쓰이는 것도 별 수 없나 봅니다.
http://www.w3schools.com/tags/tag_fieldset.asp
http://www.w3schools.com/tags/ref_eventattributes.asp

결국 fieldset은 별 쓸모 없이 붙어 있게 되었고, 이벤트는 아래와 같이 처리 했습니다. input 태그에서 onchage 속성은 두 브라우저 모두 동일하게 반응 하더군요.
<fieldset>

  <input onchange="myFunc();" type="radio" name="RB" id="a" value="value_a"/>
  <label for="a">a</label>

  <input onchange="myFunc();" type="radio" name="RB" id="b" value="value_b"/>
  <label for="b">b</label>

</fieldset>
http://www.w3schools.com/tags/ev_onchange.asp

* 아, 그리고 위에서 확인했던 이벤트 onmouse*() 함수들은 같은 크롬이라도 모바일과 데스트탑이 결과가 다릅니다. 확인할 엄두가 안나서 포기...

* 안드로이드 4.4(kitkat) 부터는 기본 내장 브라우저가 크롬이 된다고 합니다. 모바일 브라우저 종류도 많은데 한 가지라도 준다고 하니 그건 마음에 드네요.
http://www.android.com/versions/kit-kat-4-4/

2013. 11. 8.

폰갭(phonegap) 웹뷰와 일반 웹뷰 구분하기. 안드로이드 코드에서 웹뷰로 자바스크립트 함수 호출하기.

제목이 두개가 되었네요. 폰갭을 사용한 어플에서의 웹뷰와, 일반 모바일 브라우저 웹뷰를 구분하기 위해 찾아보다가 찾은 방법이 뒷 제목 입니다.

웹사이트에서 안드로이드 기기 카메라를 사용하기 위해 폰갭 플랫폼을 가져와서 안드로이드 앱을 만들었습니다. 구성은 웹 언어로 되어있고 포장은 폰갭과 안드로이드로 한 하이브리드 앱이죠.

허나 해당 웹사이트에서 필요로 했던 기능은, 안드로이드 기기 카메라 제어 단 하나 였습니다. 그 외의 모든 것들은 데스크탑 또는 모바일의 브라우저에서도 다 가능한 것들이구요.

웹사이트 소스에 폰갭( cordova.js ) 라이브러리를 불러 오는 것은 문제가 없습니다만, 해당 라이브러리가 포함 되어 있는 페이지를 데스크탑이나 모바일의 브라우저로 불러오면 페이지 이동이 발생할 때 마다 다이얼로그가 떠서 도무지 뭘 할 수가 없습니다. 안드로이드 어플 단에서 필요로 하는 정보를 가져 오는 것 같은데, 환경이 다르니 그런 거겠죠.

조건절 하나면 해결 될 것 같습니다. 사용한 웹 언어나 자바스크립트에서 지금 실행 환경이 안드로이드 앱상 인지, 일반 웹뷰인지 판단해서 폰갭 라이브러리를 불러 오거나 그러지 않으면 되겠죠.

처음 생각했던 것은, 모바일과 데스크탑의 구분이었습니다.
$mobile = !!(FALSE !== strstr(strtolower($_SERVER['HTTP_USER_AGENT']), 'mobile'));
//모바일 기기에서 접근할때 $mobile > 0 이됩니다.
$mobile변수로 조건문을 만들어 라이브러리를 불러올지 결정했었죠. 이것의 문제는 폰갭 웹뷰나 일반 브라우저 웹뷰나 둘다 mobile 값이기 때문에 구분이 안된다는 것입니다.

그래서 안드로이드 어플단의 자바 코드로 처리를 해줍니다.
안드로이드 4.3, 폰갭 2.9.0 환경 입니다.
public class MainActivity extends DroidGap {

    @Override
public void onCreate(Bundle savedInstanceState) {
  
        super.onCreate(savedInstanceState);     
        super.loadUrl("http:// URL ");
      
         //여기서부터
        class JsObject {
            @JavascriptInterface
            public String toString() { return "injectedObject"; }
         }
         appView.addJavascriptInterface(new JsObject(), "injectedObject");
         //여기까지
    }
}
주석사이의 코드.
http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String)

다른것들은 아무것도 하지 않은 폰갭을 사용한 어플의 가장 간단한 모습입니다. 주석 사이의 코드만 추가해주면 원하는 결과를 가질 수 있습니다.

설명하자면 window 객체( 브라우저 객체, 곧 웹뷰의 객체 )에 사용자 정의 객체를 추가해 주는 겁니다. window 객체에 대해 자세한 내용은 아래 링크 참고.
http://www.w3schools.com/jsref/obj_window.asp

안드로이드 어플을 실행했을 때만 저 사용자 객체가 추가 되므로, 웹페이지 소스에서 저 객체가 있는지 없는지 판단만으로도 문제를 해결 할 수 있습니다.

사용자 객체를 추가했으니 이제 자바스크립트에서 어떻게 사용하는지 알아 봅시다.
<head>
<script type="text/javascript">

if( window.injectedObject.toString() == "injectedObject" ){

      var js = document.createElement("script");
      js.type = "text/javascript";
      js.src = "./cordova.js";
      document.head.appendChild(js);
}
</script>
</head>
동적으로 js 파일 로드 하기.
http://stackoverflow.com/questions/950087/how-to-include-a-javascript-file-in-another-javascript-file

자바스크립트 코드에서 보는 것 처럼, window에 추가된 사용자 객체의 함수를 호출하고 그 반환된 값을 비교하는 조건문 입니다. 안드로이드 어플이 아닌 일반 브라우저에서 실행이 된다면? 저 값은 아무것도 없을테고 폰갭 라이브러리가 로드되지 않는 겁니다.

웹소스로 여러 모바일 OS에 맞는 앱을 만들 수 있다는 편리한 폰갭이지만 성격상 솔직히 그냥 귀찮습니다.

* 안드로이드 코드 단에서  sendJavascript() 함수를 사용한, 비슷해 보이는 방법도 있는듯 합니다. 포스팅된 방법으로 해결이 안되신다면 아래 링크를 한번 참조 해보심이...
http://www.jumpbyte.com/2012/phonegap-native-to-javascript/

* 그 외 참고 사이트.
http://stackoverflow.com/questions/15852499/javascript-calls-from-android-using-phonegap

2013. 11. 1.

HTML STYLE CSS 인쇄시 페이지 기본 여백 설정하기.

이전 포스트에서 크롬과 익스플로러에서 각각 인쇄 미리보기 페이지가 팝업 되게 하는 자바스크립트 소스를 봤었습니다. 각각 띄워놓고 보니 여백이 달라서 다른 페이지 처럼 보입니다.

인쇄가 될때 문서의 기본 여백을 설정해 줘야 겠습니다. 간단 하네요.
인쇄하고자 하는 페이지에서 아래와 같이 style 태그를 추가해 줍니다.
<style type="text/css" media="print">   
    @page{  size:auto; margin : 15mm;  }
</style>
크롬 인쇄 미리보기에서 여백이 '기본값' 일때 저 값이 적용 되네요.
익스플로러에서 여백이 제대로 들어가나 확인해보니 19mm가 되있더군요. 적용이 안되나 싶어서 여백 설정 창에서 확인을 눌러보니 문서 내 여백이 커집니다. 이미 15mm로 적용이 되어 있었던 겁니다.

여백값은 margin 값을 변경해 주면 됩니다.

꿀같은 출처는 역시 스택오버플로우.
http://stackoverflow.com/questions/1960939/disabling-browser-print-options-headers-footers-margins-from-page

자바스크립트 크롬 Chrome과 익스플로러 Explorer에서 인쇄 미리보기 창 띄우기.

크롬에서 인쇄를 하려고 하면 기본으로 미리보기 화면이 나오게 됩니다. 허나 익스플로러에서는 시스템 대화상자를 통한 무심한 창이 팝업 되죠. 크롬과 마찬가지로 익스플로러에서도 인쇄 미리보기가 존재 합니다.
<script type="text/javascript">
    window.print();
</script>
크롬에서는 인쇄 페이지 기본이 미리보기니까 걱정 없고, 익스플로러를 생각해 봅시다.
아래의 자바스크립트 소스로 익스플로러에서 인쇄 미리보기를 팝업 할 수 있습니다.
<script type="text/javascript">
function preview_print(){
   var OLECMDID = 7;
   var PROMPT = 1;
   var WebBrowser = '<OBJECT ID="WebBrowser1" WIDTH=0 HEIGHT=0 CLASSID="CLSID:8856F961-340A-11D0-A96B-00C04FD705A2"></OBJECT>';
   document.body.insertAdjacentHTML('beforeEnd', WebBrowser);
   WebBrowser1.ExecWB( OLECMDID, PROMPT);
}
</script>
다소 괴상해 보이긴 합니다.
insertAdjacentHTML()과 ExecWB()는 아래 링크를 참조.
http://msdn.microsoft.com/en-us/library/ie/ms536452(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/aa752087.aspx

insertAdjacentHTML() 함수는 특정 엘리먼트에 HTML 소스를 집어 넣어 주는 녀석 입니다. 첫번쨰 파라미터에서는 삽입 될 위치를 지정해 주는데 4가지가 있습니다.

<!-- beforebegin --> <p> <!-- afterbegin --> foo <!-- beforeend --> </p> <!-- afterend --> 

시작태그앞, 시작태그뒤, 종료태그앞, 종료태그뒤를 나타내네요.

대게 인쇄 페이지가 팝업으로 뜨고, 자동으로 인쇄 미리 보기 페이지가 나타나게끔 많이 합니다. 크롬은 자체적으로 현재 페이지에서 레이어 같은 걸로 처리 해놨던데... 익스플로러에선 대부분 인쇄하기 위해서 별도의 창을 마련 하더군요.

해당 함수가 있어야 하는 위치가 있습니다.
insertAdjacentHTML() 함수는 페이지 로딩중에는 쓸 수 없다고 합니다. 따라서 우리는 onload 이벤트에서 실행이 되도록 해야 합니다.
<script type="text/javascript">
window.onload = function(){
    preview_print(); //익스플로러
    window.print(); //크롬
};
</script>
저렇게 내버려 둘순 없으니, 브라우저에 맞게 함수를 실행 시켜야 겠네요.
navigator는 브라우저의 정보를 담고 있는 객체로, 이 녀석의 값을 참조해서 구분해 내면 됩니다.
http://www.w3schools.com/jsref/obj_navigator.asp
<script type="text/javascript">
window.onload = function(){
    if( navigator.userAgent.indexOf("MSIE") > 0 ){
        preview_print();
    } else if( navigator.userAgent.indexOf("Chrome") > 0){
        window.print();
    }
};
</script>
이렇게 하면 완성. 헌데 익스플로러 상단의 노란 상태바가 나타나면서 경고 메세지를 보여 주네요. 도구 - 인터넷 옵션 - 보안 - 신뢰할 수 있는 사이트에 해당 사이트를 추가 해주면 더이상 뜨지 않네요.

자동으로 미리보기를 띄우기 위해 스크립트를 사용해서 이런거고, 사용자가 직접 익스플로러 웹페이지에서 마우스 우클릭으로 인쇄 미리보기를 하면 뜨지 않습니다.

경고 문이 뜨면 신뢰할 수 있는 사이트에 추가 하라고 메세지라도 띠워줘야되려나...

PHP에서 인코딩한 문자열을 JAVASCRIPT에서 디코딩 하기.

PHP의 변수를 자바스크립트 함수의 매개변수로 전달 해야 할 경우가 있다. 매개변수로 전달되는 값에 따라 자바스크립트 오류가 발생하기도 한다. 도대체 왜 그런 걸까.

<a href="#" onClick="myFunction('<?php echo $value; ?>')">
    클릭!
</a>

function myFunction( form ){
    //처리.
}

대략 이런 경우가 있다고 해보자. 저 $value값에 따옴표라던가, 어떤 값이 들어가면 제대로 함수가 실행이 안된다. $value값을 <ega>\"ag 라 가정하자. 페이지를 불러와서 해당 링크를 클릭해보면 아무런 반응이 없다. 왜그럴까?

PHP는 서버단에서 실행되는 언어다. 문제 확인을 위해 페이지가 다 로딩 된 후 소스보기로 HTML 소스를 확인해보자.

<a href="#" onClick="myFunction(' <ega>"ag ')">

확인해본 소스는 이렇다. 함수 안의 매개변수에 포함된 " 큰따옴표가 함수를 중간에서 잘라 끝내 버린 격이 돼버린 것.

그래서 필요한 게 인코딩이다. 저런 특수 문자들을 희한한 문자들로 바꿔 준다.
인코딩된 문자열을 받은 myFunction() 함수는 디코딩 함수를 통해 원래 값을 가져오면 된다.
아래는 인코딩과 디코딩을 사용하여 상단 예제를 바꾼 것이다.

<a href="#" onClick="myFunction('<?php echo rawUrlEncode($value); ?>')">

function myFunction( form ){
    form = decodeURIComponent(form) ;
    //처리.
}

PHP에서 rawUrlEncode()는 자바스크립트의 encodeURIComponent()와 같고,
PHP에서 rawUrlDecode()는 자바스크립트의 decodeURIComponent()와 같다.

출처는 역시 스택오버플로우.
http://stackoverflow.com/questions/1105434/php-javascript-url-encoding

2013. 10. 31.

자바스크립트 javascript form onsubmit 양식 제출 되는 문제.

HTML form의 submit 문제는 여러가지 이유에서 발생한다.
form.submit(); return true; 라고 했을 때 두번 submit 되는 문제도 있고, 이것 저것 많다.
이번 포스팅에서는 폼의 onsubmit 함수 안에서 문제가 발생했을 때를 보고자 한다.

아래는 일반적인 onsubmit 속성을 가지고 있는 form과 그 함수이다.

<form onsubmit="return MGY_submit(this);">
    <input type="text" id="value1">
    <input type="image" value="submit">
</form>
function MGY_submit(f){
    var _str = $("#value2").val().substr(0, 4);
    if( _str && _str != "" ){
        return true;
    }
    alert( "빈칸은 안되요" );
    return false;
}

예제 적다 보니 생각난 건데, image input이 form 안에 있을 경우 submit 버튼과 같은 역할을 해버려서 가끔 문제가 생긴다.

아무튼, 버튼을 누르면 form이 submit 하게 되면서 MGY_submit() 함수를 호출한다.
이 함수에서 true 반환하면 정상적으로 submit 하게 되고, false 를 반환하면 submit 되지 않는다. 제자리에 머문다.

MGY_submit() 함수를 보면 문제가 하나 있다. jquery를 이용해 input을 id값으로 참조해 value를 가져오는데, id가 value2 인 input은 form안에 존재하지 않는다. 이런 경우 어떻게 될까?

존재하지 않는 객체를 참조하여 value를 반환하는 함수 val()을 호출 했다. 그리고 그 값에서 일정 부분 추출하는 substr() 함수를 사용했다. 허공에 문자열을 추출하려 했으므로 에러가 나는 시점이다. _str 변수 안에는 값이 들어오지 않았다. 선언과 동시에 초기화가 되는 순간에 에러가 발생했다. 에러 발생 후의 자바스크립트는 더 이상 다음 코드를 실행하지 않는다. if 문은 실행되지 않는다.

그럼 리턴값을 기다리고 있는 form에게 가는 값은 무엇일까? true가 간다.
_str 이 초기화도 안되고, 값도 들어가지 않았다. if문을 통과할 수가 없는데, 심지어 alert()도 반응하지 않고 form은 submit 된다. 왜 그러냐고? 나도 모른다. form의 onsubmit 함수 실행 중에 에러나면 form이 submit 돼버리더라. 라고 기억해 놓으면 언젠가 도움이 된다.

그럼 어떻게 하면 좋을까? try - catch 구문을 사용하자.

function MGY_submit(f){
   try{
      var _str = $("#value2").val().substr(0, 4);
      if( _str && _str != "" ){
          return true;
      }
      else {
          alert( "빈칸은 안돼요" );
          return false;
      }
   } 
   catch ( error ){
      alert( error );
      return false;
   }
}

try 구문 안에서 문법적 에러가 발생하면, catch 부분의 코드가 실행된다. try - catch는 예상치 못한 에러로 프로그램이 비정상적으로 종료되거나 하는 상황을 예방 할 수 있는 아주 좋은 안전 장치다.

javascript try catch error message

캡쳐할때 당시엔 match() 함수를 사용했었다. 에러를 출력해주고 제자리에서 머물게 된다. form은 submit 되지 않는다. form이 잘못 submit 되었을 경우에, DB 등 상황이 곤란해질 우려가 있으므로 신중하게 처리해 주는 편이 좋겠다.

가끔은 아니 자주, 컴퓨터가 말을 좀 했으면 좋겠다.

고양이가 할퀴어서 상처가 나면? 고양이 발톱에 세균이 많을까?


고양이 발톱 할퀸 자국
이정도 상처는 아무것도 아니라는...

손으로 놀아주지 말자 라고 매번 생각하면서도 그게 잘 생각대로 안됩니다. 이번엔 좀 거칠게 당했네요.
고양이에게 긁히면 유독 긁힌 부위가 다른 곳에 긁혔을때 보다 더 붓습니다. 아마 고양이 발톱에 있는 뭔지 모를 세균 때문인 것 같은데... 소독약 발라놓고 이것 저것 좀 찾아 봤습니다.

반려동물에게서 옮을 수 있는 병들로 3가지 정도 글들이 많더군요.
고양이 발톱병( 묘소[조]병 ), 샤가스병, 선페스트!

고양이 발톱병은 공격당하고 며칠이 지나서 두부, 경부, 겨드랑이(!?) 가 붓고... 심하면 곪아 터지기도... 헌데 대부분 건강한 사람이라면 별 문제 없이, 후유증 없이 치유 된답니다. 고양이들은 벼룩한테서 옮는 병이라네요.

샤가스병남미쪽 열대질병이고, 키스벌레라는 '크리아민 노린재'가 옮기는 병이랍니다. 사람, 개, 고양이, 여우, 다람쥐, 쥐 등이 병원균을 보유하고 있을 수 있다네요. 저 무시무시한 벌레는 우리가 잘때 피를 잡수시고 액체 용변을 싸버리는데, 이 안에 기생충 들이 있답니다. 키스벌레라는 별명이 붙은 이유는 이 벌레가 연약한 살 주위를 좋아해서 사람의 경우 주로 입술 근처에서 피를 빨아 먹고 입 주변에 용변을 싸버린다네요... 사람들이 자다가 슥 문지른다던가 하면 입으로 들어가게 되고 그렇게 감염 되버립니다. 남미에선 이 병으로 많은 사람들이 죽어가고 있다는 군요. 허허.

선페스트에서 선은 왜 붙었나 했는데, 선페스트는 페스트의 한 종류였군요. 가장 흔하게 발생하는 페스트의 한 종류랍니다. 주로 페스트 걸린 쥐나 페스트 걸린 사람을 문 벼룩에게서 옮을 수 있답니다.

여기저기 이리저리 사전도 검색해보고 뉴스 기사도 봤습니다. 집 고양이에게서 걸리기 쉽지 않은 병처럼 보이는데 뉴스에서는 마치 당장 반려동물들 내다 버려야 된다는 것 처럼 써놓은 기사들이 많네요. 허허.

한달에 한번은 목 뒤에 진드기 약 바르러 가고, 거의 이틀에 한번은 청소기 돌리고, 하루에 한번은 바닥을 닦는지라... 그다지 와닿지 않는 것들이군요.

열정적인 뜀박질, 장난감 쥐에 날리는 불꽃 싸다구의 세기와, 마구 생산되는 맛동산과 감자들을 지켜보니 아주 건강해 보입니다.

잘 먹고 잘 싸는게 최고라고 배웠습니다... :-D

러시안블루 고양이 라면봉지
긁히고 비명지르니 저리 도망가버렸다는...

2013. 10. 29.

세탁소 옷걸이와 티셔츠로 고양이 텐트를 만들어 보자 :- )



집 구석구석 먼지 많은 곳이나 청소하기 힘든 곳들 여기저기를 막았습니다만 녀석... 뭔가 흥이 떨어져 보입니다. 따로 고양이 집 같은걸 사지 않았던 터라 고민을 하던 차에 괜찮은 것을 발견 했습니다. :-D

안입는 티셔츠와 세탁소 옷걸이 4개로 고양이 텐트를 만들어 봤습니다.
뒤적 뒤적 검색해보니 다른분들께서도 많이 만들어 놓으셨더군요. 흐흐.

만드는 방법은 아주 간단 합니다.
손이 조금 아프긴 했지만... 펜치가 있어야 편하실 겁니다... ㅜ_ㅜ

준비물 입니다.

① 세탁소 옷걸이 4개. 
② 안입는 티셔츠.
③ 무릎 담요 정도. 텐트 안에 넣을꺼에요.
④ 고정용 테이프.
⑤ 그리고 가장 핵심인 미끄러 지지 않는 다부진 손 또는 펜치.


일단 저 옷걸이를 일자로 펴야 합니다...
저처럼 손에 땀이 많으신분들은 꼭 장갑 끼셔야 합니다.
힘주다 미끄러져서 옷걸이 끝 부분에 찔립니다!! 아파요... ㅜ
힘 주면서 이리저리 옷걸이가 움직일텐데 달려드는 고양이가 찔리지 않게도 조심 조심...

펜치가 있으시다면 요리조리 잘 피시면 됩니다.
펜치로 하는게 더 깔끔하고 곧은 일자로 펴실 수 있겠네요.

땀 닦고 힘주고 땀 닦고 힘주고 하다보면 금방 됩니다.
문제는 옆에서 괴롭히는 고양이 녀석이 가장 큰 문제라는...


손으로 피다보니 옷걸이 꼬여있는 부분은 정말 도저히 도무지 저게 한계 더군요.
옷걸이 다 폈으면 끝난 겁니다. 이제 마무리 하시면 됩니다.


두개는 ∩ 이런 모양에 끝만 살짝 꺽어 주시고, 나머지 두개는 반으로 접어주세요.
∩자 하나 반 접은거 하나 해서 한쪽 면을 만듭니다.


이음새 부분은 테이프로 칭칭 감아 주시면 되요. 튼튼하기만 하면 문제 없습니다. 저 테이프 감긴 부분은 텐트 바닥에 깔아줄 담요에 다 덮여서 녀석들이 물어 뜯을 수 가 없어요. :-D


정말 대충 감아 놓은거 같지만 아주 견고 합니다. 테이프를 쭉쭉 땡겨가면서 발랐더니 손도 아프고 테이프도 중간에 끊어지고를 몇 번 반복했습니다. 허허.


드... 드디어 뼈대가 완성 됐습니다. 사진에 '여기' 라고 적어 놓은 부분이 있는데요. 두 면을 이어 붙일려고 하면 붙일 곳이... 마땅치 않더라구요. 지붕 부분 옷걸이 끝부분을 푸셔도 되고 대각선으로 뻗는 옷걸이 끝부분을 접으셔도 상관 없습니다. 해보시면 아실 거에요.

지붕과 바닥에 옷걸이 겹치는 부분은 특히 튼튼하게 테이프로 감아 줍니다. 특히 지붕.
가끔 고양이가 극 흥분 상태에 돌입해서 온 방바닥을 뛰어다닐 때면, 텐트 위로 점프 합니다. 그걸 버터야 해요.

이제 만들어진 뼈대에 티셔츠만 입혀주면 완성!
뒷부분은 이리저리 모아서 집개 등으로 집어 주시면 됩니다. 벽에 확 붙이셔도 될것 같... 그러면 고양이가 빠져 나오겠군요... 집개를 사용 합시다.



포스팅은 지금 하지만 만든건 한 두달 전인거 같네요. 요즘 날씨가 쌀쌀해져서 그런가... 녀석이 텐트를 애용 합니다. 안입는 옷들 서너개 더 집어 넣어서 푹신푹신 따뜻하게 만들어 줬더니 만족 스러운가 봅니다.


저처럼 만들어져 파는것들은 왠지 사기싫고, 사실 돈도 없고 이러시다면, 한번 만들어 보세요.
재.. 재밌답니다. 하하하하하.


2013. 10. 28.

PHP mysql mysqli 데이터베이스 mysqli_fetch_row() mysqli_fetch_assoc() mysqli_fetch_array() 함수 차이.

PHP에서 데이터베이스 쿼리 함수들의 성능에 대한 글을 어쩌다가 봤다.
데이터베이스에서 결과 가져올때 흔히들 쓰는 함수들이다.
정확히 알지도 못하고, 소스에 사용된 부분이 있으면 긁어서 쓰고 또 썼었다.
맨날 긁어서 쓰기만 하니까 기억이 안나서 검색을 좀 해봤다.

array mysql_fetch_row ( resource $result )
array mysql_fetch_assoc ( resource $result )
array mysql_fetch_array ( resource $result [,int $result_type = MYSQL_BOTH ] )

mixed mysqli_fetch_row ( mysqli_result $result )
array mysqli_fetch_assoc ( mysqli_result $result )
mixed mysqli_fetch_array ( mysqli_result $result [,int $resulttype = MYSQLI_BOTH ] )

일단 mysqli_fetch_row().
값을 꺼내오는데 [index] 숫자값을 사용한다.

$connection = new mysqli("localhost", "id", "pw", "database");
$query = " SQL QUERY ";
$result = mysqli_query($connection, $query);
$row = mysqli_fetch_row($result);
$row[0], $row[1] ...

그리고 mysqli_fetch_assoc().
필드명이나 쿼리문에 사용된 alias로 배열을 참조 할 수 있다.

...
$row = mysqli_fetch_assoc($result);
$row["Name"], $row["CountryCode"] ...

그럼 mysqli_fetch_array() 이녀석을 보자.

/* numeric array */
$row = $result->fetch_array(MYSQLI_NUM);
printf ("%s (%s)\n", $row[0], $row[1]);

/* associative array */
$row = $result->fetch_array(MYSQLI_ASSOC);
printf ("%s (%s)\n", $row["Name"], $row["CountryCode"]);

/* associative and numeric array */
$row = mysqli_fetch_array($result, MYSQLI_BOTH);
printf ("%s (%s)\n", $row[0], $row["CountryCode"]);

이녀석은 위에서 본 두가지 배열을 다 만들어서 내놓는다. 두번째 파라미터 기본값으로 MYSQLI_BOTH가 들어간다. 달리 명시해 주지 않으면 두 가지 배열 다 만들어서 돌려 준다.
결국 mysqli_fetch_row() 함수와 mysqli_fetch_assoc() 함수를 동시에 쓰는 거다.
가져오는 데이터베이스 양이 적으면 3가지 중에 뭘 쓰든 별 상관 없다. 편한거 쓰면 된다.

천만개 정도 들어있는 데이터베이스에 쿼리를 날리면 어떻게 될까.
http://www.spearheadsoftwares.com/tutorials/php-performance-benchmarking/50-mysql-fetch-assoc-vs-mysql-fetch-array-vs-mysql-fetch-object

링크를 타고가서 확인해보면 백만개 정도부터 살짝 차이가 나기 시작한다.
mysqli_fetch_object()는 쿼리 결과를 객체로 만들어서 뱉어주는거라 생각 하면 된다.

$obj = mysqli_fetch_object($result);
$obj->Name, $obj->CountryCode) ...

객체로 만들어서 뱉어 주는거라, 그냥 왠지 느릴 것만 같은데 정말 늦다.
물론 이것도 데이터 양이 몇개 안되면 차이 없다.

천만개 정도 데이터 양이 있을때, object()와 assoc()의 성능을 비교해 보면 15초 이상 차이난다.
array()와 assoc()에서는 큰 차이를 보이진 않는다. 4초 정도 되려나. object()와 assoc()에서 15초 이상 차이가 나서 1초가 아주 작아 보인다. 0.1초라도 줄이기 위해 노력 하는데 ... 4초도 정말 큰 시간이다.

스택오버플로우 같은 곳들을 뒤지다 보니 mysqli_fetch_array() 함수를 잊으라고 추천한다.
row()와 assoc()은 따로 비교되지 않았다. row()가 만들어놓은 배열 index와 필드명을 이어 주는게 필요해 보이는데... 성능 차이가 거의 없는걸까. 아무래도 row()가 가장 빠를 것 같다.
아무튼,  mysqli_fetch_row() 또는 mysqli_fetch_assoc()을 사용하자.

* mysql_*() 함수들은 PHP 5.5.0 버전에서 부터 DEPRECATED 되었고, 미래에 함수 자체가 지워질 것이라 한다. 확장격인 mysqliPDO_MySQL을 사용하자.

관련 출처.
http://us2.php.net/mysql_fetch_assoc
http://www.php.net/manual/en/mysqli-result.fetch-assoc.php
http://www.php.net/manual/en/mysqli-result.fetch-array.php
http://stackoverflow.com/questions/11480129/mysql-fetch-row-vs-mysql-fetch-assoc-vs-mysql-fetch-array

2013. 10. 25.

android phonegap(폰갭) 에서 뒤로가기 버튼 제어.

폰갭이 결국 웹뷰를 포장해 놓은거라 그런지, 모바일 기기의 백키를 누르면 뒤로가기가 되버린다.
기기의 back key를 눌렀을때 이벤트를 어떻게 처리 할 수 있을까?

어플 단에서 처리할 수도 있고, 웹 페이지에서 자바 스크립트로 처리할 수도 있다.
간단한 정도로 치면 아무래도 웹 페이지쪽에 한 표 준다.

웹 페이지쪽 부터 알아보자.

<head>
...
function onLoad() {
    document.addEventListener("deviceready", onDeviceReady, false);
}
function onDeviceReady() {
    document.addEventListener("backbutton", onBackKey, false);
}
function onBackKey() {
    navigator.notification.confirm('msg', onBackKeyResult, 'title', 'N, Y');
    //무반응으로 만들려면 위를 주석처리.
}
function onBackKeyResult(index) {
    if(index == 2) {
      navigator.app.exitApp(); 
    }
}
</script>
</head>

<body onload="onLoad()">
...

헤더 header에 집어 넣어야지 모든 페이지에서 작동 하겠지.
onBackKey() 함수의 confirm에서 원하는 텍스트로 갈아준다.
이렇게 하면 기기에서 백키를 눌렀을때 종료 확인 창을 띄울 수 있다.

navigator.notification.confirm() 함수는 리턴 값이 없다. 콜백 함수로 처리 해야 한다.
위에서 콜백 함수는 onBackKeyResult().
콜백 함수는 버튼의 index값을 받는다. 위에서 보면 N이 1, Y가 2다.

이번엔 안드로이드 앱 단에서 처리하는 방법을 찾아보자.
이리저리 검색해봤는데 버전 차인지 뭔지 안되는 소스들도 있더라.

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        confirmAppExit();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}


private void confirmAppExit() {
    ...
}

confirmAppExit() 함수 내용은 Dialog 만들어서 버튼 값에 따라 어플 종료 finish() 시키는 정도의 함수다.
다만, 위 소스는 원하는대로 움직여 주지 않는다.

백키를 한번 더 누르면 홈으로 빠져 나가는 상황, 어플이 종료 되는 상황, 에서만 작동한다. 어떤 놈인지는 모르겠는데 우선순위가 높은 놈이 있는게 분명하다.

이리저리 뒤져보다가 폰갭의 웹뷰 인스턴스 이벤트 리스너를 건드리는 것을 봤다.

appView.setOnKeyListener(new OnKeyListener() {
      @Override
      public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_BACK &&
                 event.getAction() ==  KeyEvent.ACTION_UP)
            {
                 //원한다면 여기에.
                 return true;
            }
            return onKeyDown(keyCode, event);
      }
});

이 코드는 onCreate() 함수 안에 들어간다. 지금 이대로라면 백키를 눌러도 아무 반응이 없다. 종료 확인을 위해 다이얼로그를 띄우려면 주석 자리에 코드를 추가하자.

아무리 봐도, 앱 단에서 처리해서 다시 폰에 집어 넣느니 웹페이지 수정하는 편이 훨씬 나은거 같다. 다만 웹페이지의 <body> 시작 태그가 독립된 헤더에 포함되어 있지 않다면, <body> 태그를 찾아 페이지 마다 추가해야 되는 그런 불상사가 생길 수도 있겠다. 그런 경우라면 어플 단에서 처리 하는게 담배를 한대 덜 필 수 있는 길이다.


스택오버플로우 출처
http://stackoverflow.com/questions/15834629/android-droidgap-disabling-back-button

안드로이드 하이브리드 앱 폰갭(phonegap)에서 쿠키 사용이 안된다.

저번 포스팅에서 폰갭 플랫폼에서 하이브리드 앱을 개발할때 쿠키를 사용하기 위해, 어플 단에서 자바 코드를 추가 하는 방법을 적었다. 헌데, 그렇게 해도 달라지는게 없더라고 글을 마무리 했다. 찾아보고 찾아보고 찾아보니 이유가 있더라.

저번 포스팅의 방식은 원래 작동 해야 하는게 맞다.
안드로이드 2.+ 버전에서 작동 한다고 한다.
허나, 안드로이드 4.+ 버전에선 같은 방식으로 처리해도 쿠키 사용이 되지 않는다.

폰갭이 안드로이드 버전에 맞춰서 따라가질 못하는 건지, 아니면 폰갭의 버그인지, 아니면 안드로이드 자체에 변화가 생긴건지는 자세하게 모르겠다만, 해외 블로그들을 읽을 때마다 모두 다 같은 소리를 한다.

쿠키 말고 localStorage를 써보란다.
http://community.phonegap.com/nitobi/topics/using_cookies_does_not_seem_to_work
http://stackoverflow.com/questions/3709315/phonegap-cookie-based-authentication-php-not-working-webview
http://stackoverflow.com/questions/16463188/cookie-based-authentication-on-phonegap
https://groups.google.com/forum/#!topic/phonegap/brNhSI7oTU4
https://groups.google.com/forum/#!topic/phonegap/wnSPTDRWglU
http://stackoverflow.com/questions/11392364/how-to-store-cookies-values-in-an-android-device-using-phonegap-and-jquery-mobil

쿠키랑 성격이 비슷한 녀석이다. 크로스 브라우징 걱정도 없고, 아주 쓸만한 녀석이다.
http://www.w3schools.com/html/html5_webstorage.asp

사용법 또한 아주 간단한게 마음에 쏙 든다.
위 링크에서 나와있는 사용 예제는 아래와 같다.

<script language="javascript">

localStorage.lastname="Smith";
document.getElementById("result").innerHTML="Last name: " + localStorage.lastname;

</script>

폰갭 API 문서에도 설명이 나와 있다.
http://docs.phonegap.com/en/2.2.0/cordova_storage_storage.md.html#Storage

<script language="javascript">

//값 설정
window.localStorage.setItem("key", "value");
//가져오고
var value = window.localStorage.getItem("key");
//지워주고
window.localStorage.removeItem("key");
window.localStorage.clear();

</script>

데스크탑 환경에서( 폰갭 라이브러리 링크 안했는데 ) 문제 없이 돌아 간다.
그냥 모양새만 다른거고, HTML5 기본으로 제공하는 건가보다.
폰갭에서 따로 W3C Storage를 사용 가능하게 함수를 제공해 주는게 아니고 설명만 적어 놓은 거구나.

아주 쓸만한 녀석이지만, 클라이언트 쪽 데이터라 살짝 귀찮아 진다.
PHP 환경을 생각 해보자.쿠키나 세션이라면 $_COOKIE['...'] 등으로 서버단에서 처리해서 결과를 낼 수 있다만 이 경우라면 ajax등을 이용하여 필요한 처리를 해야한다.

http://stackoverflow.com/questions/3855337/php-localstorage

조만간에 머리가 크게 한번 지끈 거릴 것 같은 슬픈 예감이 든다.


2013. 10. 24.

폰갭(phonegap)에서 쿠키 cookie 사용하기.

웹뷰에서 돌아가는 녀석인데 당연히 쿠키가 사용될 것이라 생각 했다.
이렇게 해도 내가 원하는대로 쿠키는 움직여 주지 않았지만...
해매면서 이것저것 집어 넣어 보고 주무르다 보니, 이 코드 추가 전과 후의 결과가 기억이 안난다.

//변경전
import android.os.Bundle;
import org.apache.cordova.*;

public class App extends DroidGap {
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       super.loadUrl("file:///android_asset/www/index.html");
   }
}

//변경후
import android.os.Bundle;
import android.webkit.CookieManager;
import org.apache.cordova.*;

public class App extends DroidGap {
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
       CookieManager.setAcceptFileSchemeCookies(true);
       super.onCreate(savedInstanceState);
       super.loadUrl("file:///android_asset/www/index.html");
   }
}
http://stackoverflow.com/questions/11083160/how-to-enable-cookies-for-android-phonegap-1-8-0-app

대충.
file:// (안드로이드) 쪽 로컬 쿠키를 사용하려면, 폰갭 프로잭트(어플)에서 로컬 쿠키를 받아들이게 해 줘야 한다는데... 내 경우는 어떻게 되는건지 잘 모르겠다. 난 PHP에서 쿠키 사용할때 이렇게 했다.

set_cookie('param', 'data', time() + 3600), "/";

이게 폰갭을 통해서 안드로이드로 가면 어떤 경로가 될지는 정확히 모르겠는데,
일단 내가 사용하는 경우에는, 저렇게 어플단에서 코드를 추가 하던 안하던 차이가 없다.

솔직히 뭔지 잘 모르겠다만,
폰갭으로 하이브리드 앱 만드는데 쿠키말고 localStorage()를 사용하라는 의견이 지배적이다.

이번 포스팅은 일기가 됐네 :-(

PHP setcookie()로 생성한 쿠키가 cookie 삭제가 안된다.

md5 함수로 막 어지럽히고, base64_encode 함수로 막 괴롭힌 값들로 쿠키를 생성했다.
사실 이건 문제가 안된다.

쿠키를 생성하는 방법 등 기본적인 것들은 링크를 참조 하도록 하자.
http://www.w3schools.com/php/php_cookies.asp
친절한 곳이다.

쿠키 생성하고, 지우는데 사용한 코드다.
헌데 도대체가 지워지지 않는다. 꼬박 이틀을 날렸다.

setcookie('param', 'data', time() + 3600 );
setcookie('param', '',     time() - 3600 );

http://stackoverflow.com/questions/6843822/android-jquery-mobile-cookies-not-stored
http://stackoverflow.com/questions/5681117/cookies-not-being-deleted/7103043#7103043

setcookie()에 네번째 파라미터가 있다.
경로를 지정해 주는 파라미터인데, 이놈을 지정해 주지 않으면 쿠키 삭제가 안될 수 도 있다.
브라우저가 디폴트로 현재 페이지를 어쩌고 저쩌고 ...
그래서 찾아보니 대부분 이렇게 가더라.

setcookie('param', 'data', time() + 3600, '/' );
setcookie('param', '', time() - 3600, '/' );

아참, 그리고 쿠키는 setcookie()로 만들어 주자 마자 바로 사용할 수 없다.

setcookie('param', 'data', time() + 3600, '/' );
echo $_COOKIE[param];

이렇게 해봤자 값은 나오지 않는다. 새로고침 처럼 페이지가 다시 불러와야지 써먹을 수 있다.

setcookie('param', 'data', time() + 3600, '/' );
if( $_COOKIE[param] == "" ){
  echo "<script type='text/javascript'> window.location.reload(); </script>";
} else{
  echo $_COOKIE[param];
}

이렇게 하라는건 아니고, 이렇게 해서 페이지를 다시 불러주면 한방에 찍어 볼 수 있다.

폰갭(PhoneGap) 이란 무엇인가?

하이브리드 앱을 만들때 폰갭과 같은 플랫폼을 사용한다.
치밀하게 알고싶은 사람은 위키디피아로! http://en.wikipedia.org/wiki/PhoneGap

그럼 하이브리드 어플은 무엇인가.

앱 하나를 생각해보자.
얼굴을 찍어서 전생이나 닮은 사람 찾아주는 그런 간단한 것으로. 입력은 사진 한장이다.
사진 한장으로 어떤 알고리즘을 거치든, 결과를 뱉기까지 플랫폼이나 언어가 걸림돌이 되진 않는다.

아이폰이나 안드로이드라면, 카메라로 촬영하고, 촬영한 사진 데이터로 이리저리 구워 삶고 비교하고 또 비교해서 결과 내면 된다.

그럼, 똑같은 기능을 하는 웹사이트가 있다고 해보자. 그리고 아이폰이나 안드로이드의 웹 브라우저로 그 사이트에 접속한 상황이다. 결과 만들어 내는 로직은 웹언어라 해도 문제될 것은 없다. 문제는 입력을 어떻게 주는지다.  카메라를 띄워야 되는데, 모바일 OS들의 카메라를 제어할 수가 없다.

이때 필요한 것이 폰갭과 같은 플랫폼이다. 포장지라고 생각하자.

폰갭을 사용하여 어플을 만드는 과정이나, 그냥 어플을 만드는 과정이나 똑같다.
다만, 폰갭 라이브러리를 어플에 포함시켜서 사용하는 것 뿐이다.

public class MainActivity extends DroidGap {
    @Override
    public void onCreate(Bundle savedInstanceState) {   
        super.onCreate(savedInstanceState);       
        super.loadUrl(" url ");
    }
}

안드로이드 어플을 개발해 본 사람이 있다면 많이 봐왔을 만한 부분이다.
메인엑티비티 인데, Activity를 상속하는게 아니라 저 DroidGap을 상속한다.

그리고 loadUrl()함수로 해당 웹페이지를 가져 오면 된다.
물론 어플 내로 웹페이지 코드 자체를 옮길 수 도 있다. 허나 번거롭다.

그럼 이제 어떻게 되는건가.
카메라를 제어 하고자 하는 웹페이지에서 써주면 된다.

<script type='text/javascript' charset='utf-8' src='cordova.js'></script>
<input type="button" onclick="capturePhoto();" value="사진촬영" />

스크립트 불러와주고, 홈페이지에서 API 봐가면서 맞게 써주면 된다.
밑에 함수들은 웹 페이지에 포함 해준다.

function capturePhoto(){
     navigator.camera.getPicture(
         onPhotoURISuccess,
         onFail,
         { quality:50,
           correctOrientation: true,
           destinationType : Camera.DestinationType.FILE_URI 
         }
     );
}

function onPhotoURISuccess(imageURI){
     //파워하게 처리.
   alert(" 사람의 얼굴로 다시 촬영해 주세요. ");
}

function onFail(message){
     //사진을 촬영하거나, 가져오는데서 실패하면.
     alert(" 카메라가 촬영을 거부 했습니다." );
}

저 navigator.camera 저 녀석들이 폰갭 놈들 인가 보다.
촬영하면 콜백 함수(onPhotoURISuccess, OnFail)가 호출되고 이미지를 리턴해 준다. 이제 위에서 말했던 나이를 맞추든, 닮은 사람을 찾아주든 처리해서 결과를 뱉어 주면 된다.

아이폰에서도 폰갭 라이브러리 불러오고 아이폰 어플 만드는 방식 따라서 웹사이트에 연결해주면 된다. 안드로이드용, 아이폰용 어플을 따로 만들 필요 없이 웹페이지 소스 하나로 안드로이드, 아이폰 어플을 각각 만들어 낼 수 있다는 것이다.


2013. 10. 23.

JQuery ajax 에러 error 코드가 code 0.

form에서 submit할때 호출되는 함수에서 ajax로 뭔가를 처리 하는데 에러가 뜬다.
alert으로 에러 코드를 찍어보니 code 값으로 0을 뱉어 낸다.

아래는 submit 버튼을 누르면 form의 submit_check()함수가 호출되고,
form이 submit되는 간단한 구조의 form이다.

<form name="Frm" method="post" onsubmit="return submit_check(this);">
<input type="submit" value="submit">
function submit_check(){
   jQuery.ajax({
      type:"GET",
      url: target_url,
      success:function(msg){
          //성공!
      },
      error: function(xhr,status,error){
          //에러!
          alert("code:"+xhr.status);
      }
   });

   f.action = submit_url;
   return true;
}

에러 0의 의미가 더 있을진 모르지만 2가지를 알아 봤다.

In my experience, you'll see a status of 0 when either doing cross-site scripting (where access is denied) or requesting a URL that is unreachable (typo, DNS issues, etc).

1. 대충 뭐, 접근 거부 된거나 DNS 문제 등으로 접속 안되는 주소로 연결 했을때.

It's could be possible to get a status code of 0 if you have sent an ajax call and a refresh of the browser was trigger before getting the ajax response. The ajax call will be cancelled and you will getting this status.

2. ajax 호출하고 반환값을 받아와야 되는데, 이 반환값이 도착 하기전에 submit 또는 새로고침 등으로 페이지 이동이 발생해 버리는 경우.

내 문제는 2번이다. 저 위의 form에서 onsubmit 부분은 지워 버리자. ajax가 끝 날때 submit 되야 하는데, 저건 곤란하다. 어찌 됐건 함수가 끝나면 submit 되버리니까.

<input type="button" onclick="submit_check()" value="submit">

버튼 type은 button으로, 이벤트도 달아서 변경해 준다. 그리고 아래는 바뀐 함수.

function submit_check(){
   jQuery.ajax({
      type:"GET",
      url: target_url,
      success:function(msg){
          //성공!
         document.logFrm.action = submit_url;
         document.logFrm.submit();
      },
      error: function(xhr,status,error){
          //에러!
          alert("code:"+xhr.status);
      }
   });
}

이렇게 되면, 버튼을 눌렀을때 함수가 실행되며 ajax 발사!
submit_check() 함수는 실행을 마쳐도 form은 submit 되지 않는다.
ajax가 무사히 반환값을 가지고 돌아오면 그때 form을 submit 시킨다.

꿀같은 출처는 역시 스택오버플로우.
http://stackoverflow.com/questions/2000609/jquery-ajax-status-code-0

HTML textarea value 출력 시 개행 문제.

textarea의 value 값을 데이터베이스에 저장해 놓고, 필요한 곳에 출력해야 한다.
허나, 어떻게 입력을 해도 한줄로 출력된다.

textarea에 입력할때 엔터를 치면 당장 눈에 보이는 개행은 된다.
\n 이 녀석이 들어가는 것. 헌데 그럼 왜 뿌려줄때는 한줄로 나온단 말인가!

브라우저에서 HTML에서 개행은 \n로 되지 않는다. 소스보기를 하면 아마 개행 잘 되있을 것이다.
<br/>이나 다른 개행 태그가 사용 되야 된다.

<form name="cf" onsubmit="return replaceBR();" >
   <textarea id="c"></textarea>
</form>

submit 될때 함수를 호출해서 \n값을 <br/>로 바꿔 주는 방법을 써보자.

function replaceBR(){   
   document.cf.c.value = document.cf.c.value.replace( /\n/gi, '<br//>'); 
   return true; 
}

replace() 함수는 일치하는 문자열을 바꿔 주긴 주는데, 첫 번째 일치하는 녀석만 바꿔준다.
바꾸고 싶은 문자열을 / / 로 감싸고 뒤에 gi를 붙여 주는 것으로 전체를 바꿀 수 있다.

나처럼 "/\n/gi"따옴표로 묶어 놓고, 안된다고 빡치는 시간 낭비 하지 말자.
묶어버리면 그냥 문자열 덩어리일 뿐이다.

아, 함수내에 br태그 슬래시 2개다. 코드 하일라이터에서 개행이 되버려서리...

JQUERY 모바일에서 로딩 애니메이션을 제거 해보자.

특정 엘리먼트가 최상단에 오게 페이지를 스크롤 해야 하는데, JQuery mobile에서 페이지 이동할때 뜨는 'Loading...' 애니메이션 때문인지 스크롤이 안된다.

아래와 같이 jquery mobile 기본 애니메이션을 해제 시킬 수 있다.

<script type="text/javascript" src="/jquery-1.7.1.js"></script>

<script>
$(document).on("mobileinit", function(){
      $.mobile.ajaxEnabled=false;
      $.mobile.loadingMessage = false;
});           
</script>

<script type="text/javascript" src="/jquery.mobile-1.0a3.min.js"></script>

javascript JQuery offset() 반환값이 0이 나와요.

offset()값이 top이나 left나 모두 0으로 나온다.
아래는 특정 엘리먼트를 최상단에 위치하게 페이지 스크롤 해주는 코드다.

var position = $('#time_'+idx).offset();
$('html, body').animate( {scrollTop: position.top-5 }, 500);

phonegap(cordova) + jquery mobile에서 필요해서 갖다 붙였는데 반응하지 않는다.
alert으로 값들을 찍어 봤는데, position.top 값이 0으로 찍힌다.

document.getElementById("target").offsetParent.tagName
$("#target").get(0).offsetParent.tagName

아래 것은 JQuery 방식.
이렇게 특정 엘리먼트를 포함하는 기준 개체가 뭔지 확인 해 볼 수 있다. 부모 개념인가?

내가 이동하고자 하는 특정 엘리멘트를 포함하고 있는 기준 개체란 것의 potision 속성값에 따라 자식 엘리먼트의 좌표값이 바뀔 수 도 있단다.

CSS쪽으론 아는게 없어서 일단  모든 CSS파일을 링크 해제 했다. 형편없게 뭉게져버린 페이지지만 offset()값은 제대로 나온다! 무엇인진 모르겠으나 position에 속성값을 주나보다. 찾아서 좀 수정 해보려 했으나 jquery mobile 기본 CSS라 건드릴 엄두가 안난다. 화면 가득 빽뺵한 소스를 보고있으니 멀미가 난다.

다시 CSS링크 하고 저 기준개체란 녀석을 찍어봤는데 null이 나온다... 여기까지다 못하겠다.
결국 고정값으로 스크롤 되게끔 처리 했지만,  다른 컴포넌트들의 height 값 바뀌면 사정없이 엉뚱한테 스크롤 되버린다.

GET 방식으로 url 주소값을 넘길때 "&" 문제.


url 주소를 GET 방식으로 넘길일이 있겠냐만은... 넘길일이 있더라.

page.php?url=page2.php?param1=1&param2=2

GET 방식으로 url 파라미터에 page2.php?param1=1&param2=2를 보내고 싶었다.
허나 page.php에서 $_GET[url]을 하면 page2.php?param1=1 여기까지만 나온다.
url 파라미터 값 안에 &가 있어서, 한 덩어리가 아닌 다음 파라미터라고 생각하는 것.

즉, page.php에서 받은 $_GET 배열이

$_GET[url] = page2.pgp?param1=1
$_GET[param2] = 2

이렇게 쪼개져 있다는 것이다.

url 주소를 GET 방식으로 넘겨줄려면,
urlencode()함수로 인코딩하여 GET값을 보내주면 된다.

$temp = urlencode(page2.php?param1=1&param2=2);
page.php?url=$temp

HTML form의 input으로 파일(file) 넘겨 받을때 오류 처리 조건문.

PHP 버전이 현재 쓰고 있는 것보다 더 높은 곳으로 서버를 옮겼습니다. type=file인 input을 POST 방식으로 받아 처리하는 페이지가 있는데 서버를 옮긴 이후로 결과가 달라졌더라고요. 일반적인 form과 input 입니다.
 
<form action="upload_file.php" method="post" enctype="multipart/form-data">
      <input type="file" name="file" id="file">
      <input type="submit" name="submit" value="Submit">
</form>

upload_file.php에서 file이 잘 넘어왔는지 확인하는 조건 절이 있는데 이상합니다. extract() 함수를 이용해서 $_POST 변수를 풀어 놓은 상태라 바로 $file 이런 식으로 사용했고요. 위에 보면 input name 속성을 file로 해놨습니다. 가만있자... file 이라는 변수명은 HTML이나 PHP의 예약어라서 사용을 할 수 없는 건가요? form의 name 속성 값을 form 이라고 주면 안됐었던 걸로 갑자기 기억이 나는 듯 마는 듯 합니다.
http://php.net/manual/kr/function.extract.php

if( $file ){...}

같은 소슨데 버전이 달라졌다고 내게 이런 귀찮음을 선물하다니 ㅜ _ㅜ 귀찮습니다.
제대로 쓰려면 어떻게 해야 하나 싶어서 좀 찾아 봤습니다. 아래 링크 참조.
http://www.w3schools.com/php/php_file_upload.asp

권장 사항은 이렇네요.

if ($_FILES["file"]["error"] > 0){...}  

음! 그럼 우리는 이렇게 사용해주면 되겠습니다.

<?php
if ( $_FILES['file']['error'] == 0 ) {
      //에러없을때, 잘 넘어왔을때.
} else {
      //에러.
}
?>

JQuery 엘리먼트 element 스타일 style 변경.

jquery를 써서 html 엘리먼트의 스타일을 변경하고 싶었다.

 $('# ID ').attr("style", "background-color: #f0ffff");

이렇게 하면 배경이 적용 되긴 한다. 하지만 background-color 속성 외에 width, height가 있었다면 사라지고 없다. style 속성에 덮어쓰는 것인가 보다.

이래선 안된다. 기존의 style 속성은 유지하면서 배경만 바꿔야 하는데...

 $('# ID ').css("background", "#f0ffff");

attr 함수가 아닌 css함수로 backround를 줘봤다.
이렇게 하면 기존 설정들은 남아있고 배경색만 변경 된다.

HTML DIV 안의 iframe 높이 height 를 구해보자. 다음에디터 높이를 동적으로 변경!

다음 에디터를 사용 하는데, 작성한 글의 높이에 맞춰서 에디터의 높이도 변경하고 싶었다. 에디터를 로드 하는 부분에서 높이를 지정할 수 있는데, 이 높이 값보다 본문 높이 값이 크면 스크롤바가 생겨서 마음에 영 들지 않는다.

PHP로 계산해서 높이값을 던져 주고 싶었지만, PHP단에서 높이를 측정할 방법을 못 찾았다.

<div id="tx_canvas_wysiwyg_holder" ...>
<iframe id="tx_canvas_wysiwyg" ...></iframe>
</div>

크롬엔 개발자 툴이 있다. F12를 누르면 현재 페이지의 구석 구석을 해집어 볼 수 있다.
tx_canvas_wysiwyg이란 iframe 안에 본문 내용이 몽땅 들어 간다.

Editor.getCanvas().setCanvasSize({ height: '700px' });

위와 같이 높이를 설정해 줄 수 있다. 에디터 안의 본문 높이가 설정 값보다 클 경우 스크롤바가 생겨 버린다. 본문을 모두 불러온 다음 에디터 높이를 변경하려면 스크립트로 처리 하는게 좋을 것 같다.

alert( $("#tx_canvas_wysiwyg').height() );

스크롤바가 생겨 있는데도 본문을 담고 있는 iframe의 높이값을 찍어보면 '700'을 돌려준다.
그럼 어떻게 해야 스크롤바 길이 까지 포함해서 가져올 수 있을까.
iframe이 담고 있는 html body 본문의 height 값을 가져오면 된다.

alert( document.getElementById("tx_canvas_wysiwyg").
          contentWindow.document.body.offsetHeight );

뭔가 길어졌지만, 이렇게 하면 iframe안의 본문 높이를 확인 할 수 있다.
이제 아래와 같이 에디터의 높이 height 값을 변경 해주면 된다.

var editor_height = document.getElementById("tx_canvas_wysiwyg").
                                   contentWindow.document.body.offsetHeight;
$("#tx_canvas_wysiwyg").height( editor_height );

IE8에서는 offsetHeight대신 scrollHeight로 바꾸자. 두개 차이는 뭔지 모르겠다.
그 이상 버전에서는 확인하지 못했다.

2013. 10. 18.

HTML div에 style 속성으로 모서리 둥글게 만들어보자.

div에 border로 테두리 넣을 일이 잘 없었다. 테두리를 어쩌다가 넣었는데 너무 네모난게 마음에 들지 않는다. 이클립스 ctrl + space bar 기능으로 사용 가능한 것들 이리저리 찾아 봤는데 이 style 속성값은 나오지 않더라. 귀찮게 검색을 시키다니 말이야.

<div style="border: 2px solid #ccc;">

뭐 이정도로 div가 하나 있다고 하자. 테두리도 style 속성.
border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px;
border-top-left-radius: 10px;&nbsp;
border-top-right-radius: 10px;

이 속성들만 넣어주면 된다.

<div style="border-bottom-left-radius: 10px; 
               border-bottom-right-radius: 10px; 
               border-top-left-radius: 10px; 
               border-top-right-radius: 10px; 
               border: 2px solid #ccc;">
</div>

10px에서 값이 커지면 둥근 정도도 커진다.

2013. 10. 17.

HTML 새로고침 버튼을 만들어보자.

새로고침은 F5 눌러버리면 그만이라 생각했는데, 만들어 놓아야 할 일이 생기긴 하네. 대충 두 가지 방법으로 새로고침 버튼을 만들어 봤다. 역할은 같고, 둘다 새로고침 버튼이다. 자바스크립트를 썼냐 안썼냐 그 정도 차이 밖에 없다.

echo "<script> var url = '$_SERVER[REQUEST_URI]'; </script>";
<a href="http://www.blogger.com/url">Refresh</a> 
<input onclick="location.replace( url );" type="button" value="Refresh" /> 

두번째에 location.replace를 한번 써봤다.
첫번째의 경우 그냥 페이지 이동인 셈이니까 history가 쌓이겠지.
아마 페이지를 뒤로 이동 하게 된다면 같은 페이지로 제자리 걸음 할 것이야.
라고 생각했는데 아무 문제 없더라. :-)

GET 방식으로 한글값 전달. 근데 이상한 문자로 나오면?

GET 방식으로 url 뒤에 한글 보냈는데, 받는 쪽 PHP에서 확인하니 깨짐 현상이 생긴다.
현재 주므르고 있는 상황은 아래와 같다.

* A.php는 서버단에서 연산하고 자바스크립트 구문을 만들어 출력한다.
* 본문에서는 어떤 이벤트가 발생했을때 동적으로 A.php를 스크립트로 추가 한다.

s = document.createElement('script');
s.src = './A.php?param=뭐임마';

대충 위처럼 본문에서 A.php를 동적으로 삽입한다. 이것도 나름 AJAX 인건가... 잘 모르겠다.  근데 A.php의 PHP 문장에서 저 param값이 '뭐임마'이 아니다. 흔히 인터넷 주소창나 에서 볼 수 있는 %EC&A7%80%EB.... 이렇게 변신해 있다.

A.php에서 값을 echo로 출력해보려고 하면 자꾸 스크립트 오류가 나서 열채게 한다. 왜그런진 모르겠고 화만 치밀었다. 뭔가 인코딩이 된 것 같아서 이것 저것 인코딩/디코딩 PHP 함수를 다 갖다 써 봤다.

A.php 에서 변수 사용할 때 디코팅 함수에 담궈 준다.
$param = URLDecode( $_GET[param] );
이렇게 디코딩 함수를 한번 통과 시켜 주니, 한글이 제대로 나온다.

PHP 문자열 찾기 함수. strpos() 그리고 eregi().

어느샌가 점점 돌대가리가 되어 가는것 같다.
자주 사용 하는 함수 인데, 매번 생각이 안나서 검색을 해야 한다. 여기에 정리 하다 보면 기억에 남으리라 믿는다. 일단, PHP에서 문자열에서 특정 문자가 포함 되었는지 찾아 주는 함수다.
int eregi ( string $pattern , string $string [, array &$regs ] );
예를 들어보자.
echo eregi( 'A', "ABCDEFG");
간단 하다. 파라미터 2개 중에 앞에 찾을려는 문자, 뒤에는 문자열이다. 세번째 파라미터는 패턴 뭐 어쩌고... 모르겠다. 궁금한 사람은 http://php.net/manual/en/function.eregi.php 여기서 보면 된다.

반환값은 몇 번째에 있는가 그런거 상관 없이 있으면 1, 없으면 0을 반환 한다. 세번째 파라미터가 들어가면 반환값이 바뀌는것 같다만 거기까진 알지 말자. PHP닷넷에서 보다보니, 이 함수는 PHP 5.3.0. 버전 이후로는 사용하지 말란다.

mixed strpos ( string $haystack , mixed $needle [, int $offset = 0 ] );
위에 녀석과 파라미터가 반대다. 앞에 문자열이 들어가고 뒤에 찾을 문자가 들어간다. eregi()와 다른점은 이녀석은 찾고자 하는 문자의 위치를 반환 해준다.
예를 들어보자.
echo strpos( "ABCDEFG" , "A" );
이러면 0이 나온다. 첫번째니까. "B"를 찾으면 1이 나오고, G를 찾으면 6이 나오겠지.
그럼 이러면 어떨까?
echo strpos( "ABCDEFG" , "H" );
찾고자 하는 문자가 문자열에 없다. 그럼 이녀석은 int 숫자값이 아닌 boolean 값을 뱉는다. 
찾았을때는 위치 숫자값을, 못찾으면 boolean(false)를 뱉는다. http://php.net/manual/en/function.strpos.php 
참고해보면 이 함수를 사용해서 조건문을 돌릴때면 비교 연산자를 === 또는 !==를 쓰란다.

'=' 이게 3개가 붙어있는거 솔직히 처음 봤다.
 $a === $b  $a와 $b가 같고, 같은 자료형이면 TRUE. (PHP 4에서 추가)
 $a !== $b  $a가 $b와 같지 않거나, 같은 자료형이 아니면 TRUE. (PHP 4에서 추가) http://php.net/manual/kr/language.operators.comparison.php 읽었는데 무슨 말인지 모르겠다.
이해 안되는게 분해서 저 권장사항이 쓸모없음을 증명하고 싶었다.
if( strpos( "ABCDEFG" , "H" ) >=0 )
   echo "포함되네";
어짜피 찾으면 0부터 시작되는 숫자값 줄꺼고, 못찾으면 false니까 뭐 이러면 되겠네!
라고 생각했는데 멍청한 생각이다.
if( -1    ) echo "헐퀴-1";
if(  0    ) echo "헐퀴0";
if(  1    ) echo "헐퀴1";
if( true  ) echo "헐퀴true";
if( false ) echo "헐퀴false";
틈이 나면 이렇게 한번 찍어보라. 0과 false는 헐퀴를 볼 수 없다. 0을 false처럼 써지니까.
while(1) 이런거 많이 해봤잖아.

strpos()는 찾지 못하면 false 을 뱉는다. 찾고자 하는게 가장 첫번째에 있어도 0을 뱉는다.
위의 예제를 보면 결국  false >= 0 이 되는거고 참이다. 항상 참이다.
false는 boolean형이고 0은 int형이지만, if는 신경 안쓴다. 그래서 !== 이나 ===을 쓴다.

권장사항은 이렇다.
if( strpos( "ABCDEFG" , "H" ) !== false )
    echo "헤헿 포함!";
이러면 false !== false 가 되고, 자료형도 달라야되고, 값도 달라야되는데 둘다 같아서 거짓!.
찾을경우 0~n !== false 가 되고, 자로형도 다르고 값도 다르므로 이 되는 것. 

어? 쓰고있는데 햇갈린다.