/** Suggest from web **/

วันอาทิตย์ที่ 28 กุมภาพันธ์ พ.ศ. 2553

Web เตือนภัยสึนามิ (Tsunami Alert)

นนีได้อ่านข่าวประกาศเตือนภัยสึนามิหลังเกิดแผ่นดินไหวรุนแรงขนาด 8.8 ริกเตอร์ในชิลี ก้อเลยนึกขึ้นมาได้ว่าตั้งใจจะลองเขียน Web Application ด้วย Ruby ไปดึงข้อมูลการเตือนภัยจาก Website ของต่างประเทศ ชื่อ NOAA จาก Website http://www.prh.noaa.gov/ptwc/ ที่เขามีข้อความเตือน update เป็นระยะๆ ตามความเร่งด่วนของเหตุการณ์ อาจถี่ 3-5 นาทีหลังตรวจพบใหม่ๆ หรือครึ่ง - 1 ชั่วโมงหากเหตุการณ์เริ่มสงบ สำหรับเป็นเครื่องเตือนภัยแบบง่ายๆ แค่เปิด web ทิ้งไว้แค่ 1 หน้าจอ หากมีเสียงเตือนภัยขึ้นมาเมื่อไร ค่อยเปิดขึ้นมาดูกัน เหมาะสำหรับเจ้าหน้าที่โรงพยาบาลที่มีการอยู่เวรตลอด 24 ชั่วโมง หรือบุคคลทั่วไปก้อสามารถนำไปใช้ประโยชน์ได้คับ ไม่มีการสงวนลิขสิทธิ์


เลยเขียน Application ต้นแบบที่นำไปใช้งานได้จริง และหากมีข้อเสนอแนะ ยินดีนำไปปรับปรุงเพื่อให้เกิดประโยชน์ในการใช้งานกันต่อไป


หลักการทำงาน

  1. ดึงข้อมูลล่าสุดที่เป็น Text 1 record
  2. ตัดข้อมูลเฉพาะส่วนที่มีความสำคัญ
  3. หา Keyword คำว่า THAI ถ้ามี ให้แจ้งสัญญาณเตือนผ่านลำโพงคอมพิวเตอร์
  4. เพิ่ม link ไปยังแหล่งข้อมูลต้นกำเนิด เพื่อดูแผนที่ และรายละเอียด
  5. เพิ่มข้อมูล การปฏิบัติเมื่อเกิดสึนามิ
  6. เพิ่มปุ่ม ทดสอบ Alert เพื่อดูสิ่งที่จะเกิดขึ้นกรณีที่พบ keyword THAI
  7. refresh web ทุก 15 วินาที
เครื่องมือที่ใช้
  • Ruby
  • Javascript
  • siren.wav
  • Web Server ขอทดลองติดตั้งไว้ที่ ศูนย์เทคโนโลยีสารสนเทศและการสื่อสาร กระทรวงสาธารณสุข
  • URL: http://203.157.240.9/tsunami
การปรับปรุงที่อยากเพิ่มเติมคือ เพิ่ม VDO Broadcast จากเจ้าหน้าที่ผู้เกี่ยวข้องกับการแจ้งเตือนภัย เพื่อความสะดวกรวดเร็วในการแจ้งข่าวว่า ข้อมูลที่ได้รับ มีความเป็นไปได้ในการเกิดสึนามิเพียงใด และจะช่วยให้การแจ้งเตือนภัยทำได้เร็วขึ้น แม้เพียงแค่ 5 นาที อาจหมายถึงการสามารถป้องกันการสูญเสียชีวิตได้ทันท่วงที


ลองใช้งานกันดูนะคร้าบบ














วันจันทร์ที่ 15 กุมภาพันธ์ พ.ศ. 2553

Ex01 Kitty Gallery by Iframe

อ่านทฤษฎีมามากแล้ว บทนี้ขอแทรกแบบฝึกหัดกันหน่อยนะคับ เอาความรู้เรื่อง Iframe จากบทที่ 2 ที่เราเอา Web Google มาใส่ไว้ใน region center กันยังงัยคับ หลักการคือสร้าง iframe ขึ้นมาก่อน เอา iframe มาบรรจุเป็น items ใน region center จากนั้นกดปุ่มด้านซ้ายมือเพื่อเปลียน src url ให้เป็น www.google.co.th

เราจะเอาความรู้มาประยุกต์ใช้ในการทำ image gallery กันดู เอาแบบง่ายๆ แต่แสดงให้เห็นถึงความสามารถของ Ext JS กัน

โจทย์

1 หา image gallery จาก Internet (หาให้เรียบร้อยแล้วคับ เป็นรูป kitty 192 รูป อิอิ)
2 สร้าง iframe เพื่อให้แสดง รูปทีละ 1 รูป
3 สร้างปุ่ม [Prev] [Next] เพื่อแสดงรูปถัดไป รูปก่อนหน้า
4 เอา iframe มาบรรจุใน window container









URL ของ Kitty Gallery คือ hellokittyworld.tripod.com ส่วนชื่อ image 1 – 192 ตั้งไว้เป็นระเบียบ เรียกใช้งานได้ง่ายดีคับ คือ /pics/1.jpg จนถึงรูปสุดท้าย /pics/192.jpg

COPY เอา code ของ iframe บทที่ 2 มา Develop กันดีกว่าคับ

Ext.onReady (function(){
  Ext.BLANK_IMAGE_URL = "/ext/resources/images/default/s.gif";
  Ext.ux.IFrameComponent = Ext.extend(Ext.BoxComponent, {
    onRender : function(ct, position){
      this.el = ct.createChild({tag: 'iframe', id: 'iframe-'+ this.id, frameBorder: 0, src: this.url});
    }
  });
  var mybrowser = new Ext.Panel({
    id: 'mybrowser'
    ,title: 'Welcome to my browser'
    ,closable: true
    ,width: '100%'
    ,height: '100%'
    ,items: [ new Ext.ux.IFrameComponent({
                  id: 'ifx'
                  ,width: '100%'
                  ,height: '100%'
                  ,url: 'about:blank'
               })
    ]
  });
  var banner = new Ext.BoxComponent({
    height:106
    ,style: 'background-image:url(images/banner.png);background-repeat:no-repeat;border:solid 1px #1D6241;'
  });
  var button = new Ext.Button({
    text:  "Google"
    ,width: '75%'
    ,style: 'padding: 20px;'
    ,handler: function() {
      Ext.get('iframe-ifx').dom.src = "http://www.google.com";
    }
  });
  var viewport = new Ext.Viewport({
    layout: "border"
    ,renderTo: Ext.getBody()
    ,items: [{
      region: "north"
      ,height: 106
      ,items: [ banner ]
    },{
      region: "west"
      ,xtype: "panel"
      ,width: 200
      ,bodyCfg: {tag: 'center'}
      ,items: [ button ]
    },{
      region: "center"
      ,items: [ mybrowser ]
    }]
  });
});

ทำการตัดต่อพันธุกรรมในส่วนที่เราไม่ไดใช้ ทิ้งไปก่อนคับ

banner  button viewport ตัดทิ้งได้เลยคับ เพราะเราจะให้ iframe มาบรรจุไว้ใน window กัน ตัดเสร็จ จะเหลือ code ตามข้างล่าง ตั้งชื่อใหม่เป็น ex01-create-gallery.js


Ext.onReady (function(){
  Ext.BLANK_IMAGE_URL = "/ext/resources/images/default/s.gif";
  Ext.ux.IFrameComponent = Ext.extend(Ext.BoxComponent, {
    onRender : function(ct, position){
      this.el = ct.createChild({tag: 'iframe', id: 'iframe-'+ this.id, frameBorder: 0, src: this.url});
    }
  });
  var mybrowser = new Ext.Panel({
    id: 'mybrowser'
    ,title: 'Welcome to my browser'
    ,closable: true
    ,width: '100%'
    ,height: '100%'
    ,items: [ new Ext.ux.IFrameComponent({
                  id: 'ifx'
                  ,width: '100%'
                  ,height: '100%'
                  ,url: 'about:blank'
               })
    ]
  });
});


ต่อไปจะขอสร้าง window เพื่อเอาไว้บรรจุ mybrowser กัน


  var win = new Ext.Window({
    layout: "border"
    ,id: 'idx'
    ,width: 400
    ,height: 300
    ,items: [{
      region: "center"
      ,items: [ mybrowser ]
    }]
  });


เป็น code snippet ที่ใช้สร้างหน้าต่างขนาด 400 x 300 pixels มี items อยู่อันเดียวคือ region center ซึ่งเราเอาไว้บรรจุ iframe อีกทีหนึ่ง

เอา code ใหม่ไปรวมกับของเก่ากันเลยดีกว่า

Ext.onReady (function(){
  Ext.BLANK_IMAGE_URL = "/ext/resources/images/default/s.gif";
  Ext.ux.IFrameComponent = Ext.extend(Ext.BoxComponent, {
    onRender : function(ct, position){
      this.el = ct.createChild({tag: 'iframe', id: 'iframe-'+ this.id, frameBorder: 0, src: this.url});
    }
  });
  var mybrowser = new Ext.Panel({
    id: 'mybrowser'
    ,closable: true
    ,width: '100%'
    ,height: '100%'
    ,items: [ new Ext.ux.IFrameComponent({
                  id: 'ifx'
                  ,width: '100%'
                  ,height: '100%'
                  ,url: 'http://hellokittyworld.tripod.com/pics/1.jpg'
               })
    ]
  });
  var win = new Ext.Window({
    layout: "border"
    ,id: 'idx'
    ,width: 400
    ,height: 300
    ,items: [{
      region: "center"
      ,items: [ mybrowser ]
    }]
  });
  win.show();
});


เปลี่ยน URL ให้ชี้ไปที่ kitty รูปที่ 1 กับสั่งให้แสดง window ออกทางหน้าจอ browser ด้วยคำสั่ง win.show();

ลองเปิด extjs-sandbox.html ที่เราสร้างจากบทที่ 3 (อยู่ที่ C:\extjs-test\extjs-sandbox.html) จากนั้น COPY เอา code ข้างบนของเราไปใส่ในพื้นที่สีเหลือง แล้วกดปุ่ม [execute JS] เพื่อชื่นชมผลงานกันเลยคับ



ต่อไปเราก้อสังเกตุดูว่ายังขาดอะไรอีก เช่น Window เรายังไม่มี title เดวจัดให้เป็น Ex01 Kitty Gallery แระกัน อ่อ ลองเอา title ของ browser ออกก้อดีเหมือนกัน ไม่เกะกะ กับเพิ่มปุ่ม [Next] อันเดียวก่อน เพื่อให้ทำการเปลี่ยน src url ให้เป็น 2.jpg  3.jpg .... จนหมด 192.jpg หมดแล้ว ให้ lock ไว้ตรงนั้นเลย

วิธีเพิ่ม title ให้ window ง่ายมากคับ

,title:  'Ex01 Kitty Gallery '

วิธีเพิ่มปุ่ม [Next] ให้กับ window เรามีให้เลือก 2 option
·        tbar  (Top Toolbar) อยู่ขอบบนของ window
·        bbar (Bottom Toolbar) อยู่ขอบล่างของ window

ขอเลือก tbar นะคับ ส่วนการเพิ่ม button ให้ทำตามนี้ เพิ่มให้ window ตำแหน่งที่เราจะแทรกใน code ก็จะอยู่ใน block ของ window ด้วยนะ


,tbar: [{
  xtype: 'tbbutton'
  ,text: 'Next'
}]

ลองแก้กันตรงๆใน sandbox ของเราแล้วกดปุ่ม [execute JS] ดูกันจะจะเลยคับ



ยังปรับปรุงต่อได้อีกคับ ปุ่ม [Next] มันน่าจะอยู่ชิดขวา จะได้ click สะดวกหน่อย ไม่เกะกะ กับลองเพิ่ม Action ให้กับปุ่มด้วย ให้มันเปลี่ยนรูปไปที่ละ 1 หมายเลข

วิธีทำให้ tbbutton ชิดขวา

ให้เพิ่ม สัญญลักษณ์  '->' ลงไปใน tbar ตรงๆได้เลยคับ ก่อนจะถึง { xtype: …. } คั่นด้วย comma

วิธีเพิ่ม Action ให้กับปุ่ม [Next] หรือ tbbutton


เขาใช้ handler กันคับ ตรงนี้เราต้องวางแผนกันล่วงหน้ากันบ้าง

Action สำคัญจริงๆคือ

Ext.get('iframe-ifx').dom.src = 'http://hellokittyworld.tripod.com/pics/2.jpg';

จากเดิม 1.jpg เพิ่มค่ามาอีก 1 เป็น 2.jpg เพิ่มค่าทีละ 1 ไปเรื่อยๆ สรุปว่า ต้องสร้างตัวแปรมาเก็บหมายเลข ของรูปกันใช่หรือป่าวว เอางั้นตั้งชื่อตัวแปรว่า imgid เพื่อให้สื่อความหมายบ้างเล็กน้อย จากนั้นทำการแยกส่วน src url เพื่อให้จัดการด้วย code ได้ง่ายขึ้น โดยจะขอแบ่งเป็น url กับ imgpath

var  imgid, url, imgpath;

imgid = 1;
url = 'http://hellokittyworld.tripod.com';
imgpath = '/pics/' + imgid + '.jpg';

src ที่เราจะต้องเปลี่ยนกันจริงๆ คือ imgpath ถ้า imgid เปลี่ยน โดยการกดปุ่ม [Next] imgpath ก้อจะได้เปลี่ยนตามไปด้วยงัยคับ

วิธีการเพิ่ม handler ให้กับ tbbutoon ตามนี้เลยคับ

    ,tbar: ['->', {
      xtype: 'tbbutton'
      ,text: 'Next'
      ,handler: function() {
         imgid = imgid+1;
         imgpath = '/pics/' + imgid + '.jpg';
         Ext.get('iframe-ifx').dom.src = url + imgpath;
     }
  }]

ฟังทฤษฎีมาพอสมควรแล้ว ลงมือปฏิบัติเลยคับ


File ex01-kitty-gallery.js

Ext.onReady (function(){
  Ext.BLANK_IMAGE_URL = "/ext/resources/images/default/s.gif";
  Ext.ux.IFrameComponent = Ext.extend(Ext.BoxComponent, {
    onRender : function(ct, position){
      this.el = ct.createChild({tag: 'iframe', id: 'iframe-'+ this.id, frameBorder: 0, src: this.url});
    }
  });
  var url, imgid, imgpath;
  url = 'http://hellokittyworld.tripod.com';
  imgid = 1;
  imgpath = '/pics/' + imgid + '.jpg';
  var mybrowser = new Ext.Panel({
    id: 'mybrowser'
    ,closable: true
    ,width: '100%'
    ,height: '100%'
    ,items: [ new Ext.ux.IFrameComponent({
                  id: 'ifx'
                  ,width: '100%'
                  ,height: '100%'
                  ,url: url + imgpath
               })
    ]
  });
  var win = new Ext.Window({
    layout: "border"
    ,title: 'Ex01 Kitty Gallery'
    ,id: 'idx'
    ,width: 400
    ,height: 300
    ,items: [{
      region: "center"
      ,items: [ mybrowser ]
    }]
    ,tbar: ['->', {
      xtype: 'tbbutton'
      ,text: 'Next'
      ,handler: function() {
         imgid = imgid+1;
         imgpath = '/pics/' + imgid + '.jpg';
         Ext.get('iframe-ifx').dom.src = url + imgpath;
      }
    }]
  });
  win.show();
});

ปัญหาที่จะต้องเจอคือ เมื่อ imgid เป็นรูปสุดท้ายมีค่าเท่ากับ 192 แล้ว user ยังกด [Next] ต่อไปอีก imgid จะเกินรูปที่มีอยู่อะคับ เราต้องป้องกันความผิดพลาดไว้ก่อน คือ หลังจากกดปุ่ม [Next] ค่า imgid เพิ่ม 1 ให้เช็คต่อเลยคับว่า ถ้า imgid > 192 ละก้อ ให้ปรับค่า imgid เป็น 192 lock เอาไว้เลยคับ หรืออีกวิธีคือ disable ปุ่ม [Next] มันซะเลย จะได้ไม่มีโอกาสสร้าง Error ให้กับโปรแกรมของเรา


,handler: function() {
         imgid = imgid+1;
         if (imgid > 192)
           imgid = 192;
         imgpath = '/pics/' + imgid + '.jpg';
         Ext.get('iframe-ifx').dom.src = url + imgpath;
      }


แค่นี้เราก็สามารถป้องกันความผิดพลาดกันได้แล้วคับ

ก่อนจะจบ ขอให้ลองไปเขียนเพิ่มปุ่ม [Prev] เพื่อย้อนภาพถอยหลัง ลดลงที่ละ 1 ดูเองมั่งคับ ปัญหาของการกดปุ่ม [Prev] คือค่า imgid จะลดลงเรื่อยๆจนถึง 1 ถ้าไม่ป้องกันไว้ก่อน ระวังมันจะกลายเปน 0 เกิดความผิดพลาดขึ้นมาได้ เพราะ 0.jpg เขาไม่มีให้นาคับ




จบแบบฝึกหัดที่ 1 แต่เพียงนี้คับ

เฉลยแบบฝึกหัดบทนี้คับ

Ext.onReady (function(){
  Ext.BLANK_IMAGE_URL = "/ext/resources/images/default/s.gif";
  Ext.ux.IFrameComponent = Ext.extend(Ext.BoxComponent, {
    onRender : function(ct, position){
      this.el = ct.createChild({tag: 'iframe', id: 'iframe-'+ this.id, frameBorder: 0, src: this.url});
    }
  });
  var url, imgid, imgpath;
  url = 'http://hellokittyworld.tripod.com';
  imgid = 1;
  imgpath = '/pics/' + imgid + '.jpg';
  var mybrowser = new Ext.Panel({
    id: 'mybrowser'
    ,closable: true
    ,width: '100%'
    ,height: '100%'
    ,items: [ new Ext.ux.IFrameComponent({
                  id: 'ifx'
                  ,width: '100%'
                  ,height: '100%'
                  ,url: url + imgpath
               })
    ]
  });
  var win = new Ext.Window({
    layout: "border"
    ,title: 'Ex01 Kitty Gallery'
    ,id: 'idx'
    ,width: 400
    ,height: 300
    ,items: [{
      region: "center"
      ,items: [ mybrowser ]
    }]
    ,tbar: ['->', {
      xtype: 'tbbutton'
      ,text: 'Prev'
      ,handler: function() {
         imgid = imgid-1;
         if (imgid < 1)
           imgid = 1;
         imgpath = '/pics/' + imgid + '.jpg';
         Ext.get('iframe-ifx').dom.src = url + imgpath;
      }
    },{
      xtype: 'tbbutton'
      ,text: 'Next'
      ,handler: function() {
         imgid = imgid+1;
         if (imgid > 192)
           imgid = 192;
         imgpath = '/pics/' + imgid + '.jpg';
         Ext.get('iframe-ifx').dom.src = url + imgpath;
      }
    }]
  });
  win.show();
});



แถมท้ายอีกนิด แล้วถ้าเราจะ disable ปุ่ม [Next] เสียดีไหมถ้า imgid เท่ากับ 192 พอดี พอ user กดปุ่ม [Prev] ทำให้ค่า imgid ลดลงน้อยกว่า 192 ค่อย enable ปุ่ม [Next] ใหม่

คำตอบ ทำได้คับ ถ้าเรากำหนด id ให้กับปุ่ม Prev และ Next ไว้ก่อน เช่น  id-btn-prev, id-btn-next จากนั้น เราจะสั่ง enable หรือ disable ปุ่ม [Prev] และ [Next] ได้โดย

Ext.getCmp('id-btn-prev').enable();
Ext.getCmp('id-btn-prev').disable();

Ext.getCmp('id-btn-next').enable();
Ext.getCmp('id-btn-next').disable();

Code ใหม่สำหรับปุ่ม [Prev] และ [Next] จะเป็นแบบนี้คับ

เงื่อนไขสำหรับปุ่ม [Next]
imgid = 192 จะถูก disable
เมื่อมีการกดปุ่ม [Next] ทุกครั้ง ให้ไป enable ปุ่ม [Prev] ด้วย

เงื่อนไขสำหรับปุ่ม [Prev]
เมื่อเริ่มโปรแกรม imgid = 1 จะต้อง config ให้ disabled: true ตั้งแต่แรก
เมื่อ imgid < 2 อีกครั้ง ให้ disable ปุ่ม [Prev] ใหม่
เมื่อมีการกดปุ่ม [Prev] ทุกครั้ง ให้ไป enable ปุ่ม [Next] ด้วย

อันนี้คงต้องตั้งสมาธิทบทวนกันสักตั้งว่า ทำไม เงื่อนไขถึงได้จู้จี้ขนาดนี้ ลองไม่ทำตามนี้ดูก้อได้นิคับ แล้วลองแบบอื่นๆดู หลักการของโปรแกรมมันไม่มี 1 วิธีตายตัวหรอกคับ เหมือนอยู่ กทม. จะเดินทางไปเชียงใหม่ ยังมีได้ตั้งหลายวิธี ฉันใดฉันนั้น

Ext.onReady (function(){
  Ext.BLANK_IMAGE_URL = "/ext/resources/images/default/s.gif";
  Ext.ux.IFrameComponent = Ext.extend(Ext.BoxComponent, {
    onRender : function(ct, position){
      this.el = ct.createChild({tag: 'iframe', id: 'iframe-'+ this.id, frameBorder: 0, src: this.url});
    }
  });
  var url, imgid, imgpath;
  url = 'http://hellokittyworld.tripod.com';
  imgid = 1;
  imgpath = '/pics/' + imgid + '.jpg';
  var mybrowser = new Ext.Panel({
    id: 'mybrowser'
    ,closable: true
    ,width: '100%'
    ,height: '100%'
    ,items: [ new Ext.ux.IFrameComponent({
                  id: 'ifx'
                  ,width: '100%'
                  ,height: '100%'
                  ,url: url + imgpath
               })
    ]
  });
  var win = new Ext.Window({
    layout: "border"
    ,title: 'Ex01 Kitty Gallery'
    ,id: 'idx'
    ,width: 400
    ,height: 300
    ,items: [{
      region: "center"
      ,items: [ mybrowser ]
    }]
    ,tbar: ['->', {
      xtype: 'tbbutton'
      ,id: 'id-btn-prev'
      ,text: 'Prev'
      ,disabled: true
      ,handler: function() {
         imgid = imgid-1;
         if (imgid < 2)
           Ext.getCmp('id-btn-prev').disable();
         Ext.getCmp('id-btn-next').enable();
         imgpath = '/pics/' + imgid + '.jpg';
         Ext.get('iframe-ifx').dom.src = url + imgpath;
      }
    },{
      xtype: 'tbbutton'
      ,id: 'id-btn-next'
      ,text: 'Next'
      ,handler: function() {
         imgid = imgid+1;
         if (imgid == 192)
           Ext.getCmp('id-btn-next').disable();
         Ext.getCmp('id-btn-prev').enable();
         imgpath = '/pics/' + imgid + '.jpg';
         Ext.get('iframe-ifx').dom.src = url + imgpath;
      }
    }]
  });
  win.show();
});

หมายเหตุ

Statement ที่ตามหลัง if และไม่มีเครื่องหมาย { } จะมีผลแค่ 1 คำสั่งเท่านั้น หาก block if ต้องการให้ทำหลายคำสั่ง (หลาย statements) ให้ใส่กลุ่มคำสั่งนั้น ภายในเครื่องหมาย { … }

ถ้าต้องการให้ ex01-kitty-gallery.js ถูกเรียกจาก file html ปกติ ก็ให้ย้อนกลับไปดูบทที่ 1 เปลี่ยน

<script type="text/javascript" src="create-hello.js"></script>


จาก file 01-create-hello.html ตรงบรรทัด create-hello.js ให้เป็น ex01-kitty-gallery.js แล้ว save file html เป็นชื่อใหม่ที่ท่านต้องการ เช่น ex01-kitty-gallery.html แล้วใช้ Chrome เปิด file นี้ขึ้นมาแค่นั้นคับ

พอดีบทถัดไปจะเป็นการติดตั้ง VirtualBox และ Fedora Core 9 มีรูปเยอะมาก เลยคิดวิธีการแบบที่นำมาเล่าให้ฟังในแบบฝึกหัดที่ 1 หลังจากทำได้จริงแล้ว เลยนำมาเล่าสู่กันฟังคับ

จบจริงๆละที่นี้


Reference


ขอขอบคุณ Website ที่ใช้นำมาอ้างอิงในแบบฝึกหัดนี้ http://hellokittyworld.tripod.com หลังจากเขียนบทความ พบว่าได้ย้าย URL ไปที่ใหม่แล้วคับ http://becools.net/hellokitty/ จึงขออนุญาตช่วยประกาศให้ทราบโดยทั่วกัน