1 2 /** 3 * @name CeL 下拉式表單 function 4 * @fileoverview 5 * 本檔案包含了下拉選擇式表單的 functions。 6 * @since 7 */ 8 9 /* 10 TODO: 11 HTML 5 <datalist> Tag 12 date 13 http://plugins.jquery.com/project/timepicker 14 http://digitalbush.com/projects/masked-input-plugin/ 15 理想: 16 http://gs.statcounter.com/ 17 18 19 http://plugins.jquery.com/search/node/Autocomplete+type%3Aproject_project 20 http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/ 21 http://jsgears.com/thread-114-1-1.html 22 23 set focus/blue background-image instead of HTML 5 placeholder text 24 http://dev.w3.org/html5/spec/Overview.html#the-placeholder-attribute 25 The placeholder attribute represents a short hint (a word or short phrase) intended to aid the user with data entry. 26 e.g., background-image: url(http://www.google.com/cse/intl/en/images/google_custom_search_watermark.gif); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(255, 255, 255); background-position: 0% 50%; background-repeat: no-repeat no-repeat; 27 or 28 http://perldoc.perl.org/ 29 usually show a <div>. show <input> only at focus. 30 31 32 http://miketaylr.com/pres/html5/forms2.html 33 http://people.opera.com/brucel/demo/html5-forms-demo.html 34 http://www.erichynds.com/examples/jquery-multiselect/examples.htm 35 http://x.wawooo.com/archives/891 36 37 http://www.google.com.tw/dictionary 38 鍵盤選擇時同時改變值 39 */ 40 41 if (typeof CeL === 'function'){ 42 43 /** 44 * 本 module 之 name(id),<span style="text-decoration:line-through;">不設定時會從呼叫時之 path 取得</span>。 45 * @type String 46 * @constant 47 * @inner 48 * @ignore 49 */ 50 var module_name = 'net.form.select_input'; 51 52 //=================================================== 53 /** 54 * 若欲 include 整個 module 時,需囊括之 code。 55 * @type Function 56 * @param {Function} library_namespace namespace of library 57 * @param load_arguments 呼叫時之 argument(s) 58 * @return 59 * @name CeL.net.form.select_input 60 * @constant 61 * @inner 62 * @ignore 63 */ 64 var code_for_including = function(library_namespace, load_arguments) { 65 66 // requires 67 if (eval(library_namespace.use_function( 68 'net.web.get_node_offset,net.web.parse_URI'))) 69 return; 70 71 library_namespace.include_module_resource('select_input.css',module_name); 72 73 /** 74 * 簡易型 net.web.XML_node @ net.form.select_input 75 * @param tag p.appendChild tag 76 * @param p parent node 77 * @param t text 78 * @param classN className 79 * @inner 80 * @ignore 81 * @return 82 */ 83 var create_DO = function(tag, p, t, classN) { 84 var _e; 85 if (t && (typeof t != 'object' || (t instanceof Object))) 86 t = document.createTextNode(t + ''); 87 if (typeof tag == 'object') { 88 _e = tag; 89 } else if (tag) { 90 _e = document.createElement(tag); 91 if (classN) 92 _e.className = classN; 93 if (t) 94 _e.appendChild(t); 95 } else if (t) 96 _e = t; 97 if (p && _e) 98 p.appendChild(_e); 99 return _e; 100 }; 101 102 103 /** 104 * get scrollbar height 105 * @return 106 * @since 2008/9/3 23:31:21 107 * @inner 108 * @ignore 109 * @see 110 * http://jdsharp.us/jQuery/minute/calculate-scrollbar-width.php 111 * lazy evaluation 112 * http://peter.michaux.ca/articles/lazy-function-definition-pattern 113 */ 114 function scrollbar_width() { 115 var _f = scrollbar_width; 116 if (!_f.w) { 117 var w, p = create_DO('div', document.body), c = create_DO('div', p, ' '), s = p.style; 118 s.width = s.height = '80px'; 119 // 有時沒這行才出得來 120 // c.style.width='100%'; 121 s.overflow = 'hidden'; 122 w = c.offsetWidth; 123 s.overflow = 'scroll'; 124 _f.w = w - c.offsetWidth; 125 // sl('scrollbar_width: '+w+'-'+c.offsetWidth+'='+_f.w); 126 document.body.removeChild(p); 127 } 128 return _f.w; 129 } 130 131 132 /** 133 * scroll 到可以看到 object 134 * TODO: 135 * 考慮可能沒 scrollbar 136 * 包括橫向 137 * @param o object 138 * @param [p] parentNode to scroll 139 * @return 140 * @since 2008/9/3 23:31:29 141 * @inner 142 * @ignore 143 */ 144 function scroll_to_show(o, p) { 145 if (!p) { 146 p = o; 147 while ((p = p.parentNode) && p.offsetHeight == p.scrollHeight) 148 ; 149 } 150 //sl('scroll_to_show: '+p.scrollTop+', '+p.scrollHeight+', '+p.offsetHeight+', '+o.offsetTop); 151 152 var s, a; 153 if (a = o.offsetTop, a < p.scrollTop) 154 s = a; 155 else if (a = o.offsetTop + o.offsetHeight - p.offsetHeight 156 + scrollbar_width(), a > p.scrollTop) 157 if (s = a, a = o.offsetTop, a < s) 158 s = a; 159 160 if (!isNaN(s)) 161 p.scrollTop = s; 162 } 163 164 165 /* 166 167 { 168 title: '', 169 name: '', 170 container: 'id' | obj, 171 list: [] | {} | {group1:{}, group2:[],.. }, 172 default: '' | [], 173 type: 'select' | 'radio' | 'checkbox', 174 } 175 176 return <select> 177 178 179 TODO: 180 複選 <select> 181 <radio> 182 <checkbox> 183 +<label> 184 autocomplete: 假如所有備取 list 都有一樣的 prefix,則自動完成。 185 把後面的用反白自動完成。 186 187 在 list 上下安排三角,onmouseover 即可自動滾動。 188 189 color panel 190 191 http://www.itlearner.com/code/js_ref/choi3.htm 192 selectName.options[i]=new Options("option_value","option_Text", defaultSelected, selected); 193 */ 194 195 /** 196 * container object, list 197 * @param o 198 * @param l 199 * @return 200 * @inner 201 * @ignore 202 */ 203 function menu_creater(o, l) { 204 205 }; 206 207 208 209 // =================================================== 210 211 var 212 213 // class private ----------------------------------- 214 215 216 /* 可紀錄的 set class name,不過對大多數人來說,更常用的是 instance.setClassName 217 218 usage: 219 (item[, obj]) set obj to className item, return real className that setted 220 (0,'prefix') set prefix & 重設(全部重跑) 221 */ 222 setClassName=function(i,o,noRec){ // (0, prefix) or (item, object) 223 var _t=this,s=_.classNameSet; 224 if(!_t.settedClass)_t.settedClass=[]; 225 226 if(!o||typeof o==='object'){ 227 // 設定並回傳 className 228 //sl('setClassName: test '+'class_'+i+': '+('class_'+i in _t?'<em>YES</em>':'none')); 229 s=[ i in s? 230 s[i].charAt(0)==='~'?s.prefix+s[i].slice(1): 231 s[i]: 232 '' 233 ]; 234 if('class_'+i in _t) 235 if(i==='error'||i==='warning')s.unshift(_t['class_'+i]); 236 else s.push(_t['class_'+i]); 237 s=s.join(' '); 238 //sl('setClassName: set '+o+(s?' to ['+s+']':', <em>There is no ['+i+'] in classNameSet or instance set.</em>')); 239 if(o && (o.className=s,!noRec)) 240 _t.settedClass.push(s,o); 241 return s; 242 } 243 244 s.prefix=o; 245 o=_t.settedClass; 246 // 重設(全部重跑) 247 for(var i=0;i<o.length;i++) 248 _f.call(_t,o[0],o[1],1); 249 }, 250 251 funcButton=function(_t,t,f,title){ // add text t, function f to instance _t 252 var _p=pv(_t),o=create_DO('span',_p.listO,'['),b; 253 setClassName.call(_t,'functionB',o); 254 b=create_DO('span',o,t); 255 setClassName.call(_t,'functionT',b); 256 b.title=title,b.onclick=f; 257 create_DO(0,o,']'); 258 return b; 259 }, 260 261 // 簡易型 262 removeAllChild=function(o){ 263 o.innerHTML=''; 264 return o; 265 }, 266 267 // show/hide list 268 showList=function(show){ // ():get, 0:hide, 1:show 269 var _t=this,_p=pv(_t),o=_p.listO,s,c=0; 270 271 if(!o)return; 272 s=o.style; 273 if(show){ 274 c=get_node_offset(_p.inputO); 275 s.top = c.top + c.height + 2 + 'px'; 276 s.left=c.left+'px'; 277 278 s.width=c.width+'px'; 279 s.height=''; // reset 280 c=s.display='block'; 281 if(_t.maxListHeight&&o.offsetHeight>_t.maxListHeight) 282 s.height=_t.maxListHeight+'px'; 283 }else if(typeof show!='undefined') 284 c=s.display='none'; 285 286 if(c) 287 create_DO(0,removeAllChild(_p.arrowO),_.textSet[c!='none'?'hideList':'showList']); 288 else c=s.display; 289 290 return c!='none'; 291 }, 292 293 294 /* 準備選擇清單的某一項 295 TODO: 296 自動完成 297 到最後若可能自動轉到全部 298 → 299 */ 300 cK=0, // control key pressed 301 readyTo=function(e,o){ 302 if(!e)e=event; 303 var _t=this,_p=pv(_t),c,gI=function(o){ 304 return o&&/*(can_use_special_attribute?o.getAttribute("sIndex"):o.sIndex)*/o.sIndex; 305 }; 306 //sl('readyTo: '+e.type+', key: '+(e.keyCode||e.which||e.charCode)+', _p.listA: '+(_p.listA&&_p.listA.length)); 307 308 if(!_p.listA||!_p.listA.length)return; 309 310 if(e.type==='mouseover'||e.type==='mouseout'){ 311 if(_p.readyItem)setClassName.call(_t,'item',_p.readyItem,0); 312 if(e.type==='mouseover')c='item_select',_p.readyItem=o; 313 else if(c='item',_p.readyItem===o)_p.readyItem=0; 314 // 需更改 _p.inputO.onkeyup 以防止重新 list!! 315 }else if(c=e.keyCode||e.which||e.charCode,c==13){ 316 if(_p.readyItem){ 317 //sl('readyTo: 以鍵盤選擇: '+_p.readyItem.innerHTML); 318 cK=c,_p.readyItem.onclick(); // 用 .click() 無效! 319 return false; 320 }else return; 321 // key input 用鍵盤控制上下 ←↑→↓: 37~40 322 }else if(c==38||c==40){ 323 cK=c; 324 o=_p.readyItem; 325 //sl('readyTo: 以鍵盤移至: '+(o&&(o.getAttribute("sIndex")+','+o.sIndex))); 326 if(!o)o=_p.listA[c==40?0:_p.listA.length-1]; 327 else{ 328 //if(!o.getAttribute)throw 1; IE 可用 getAttribute,FF 或許在 appendChild 之後屬性重設?,得用 o.sIndex 329 c=gI(o)+(c==38?-1:1); 330 if(c<0||c>=_p.listA.length)return; 331 332 setClassName.call(_t,'item',o,0); 333 o=_p.listA[c]; 334 } 335 _p.readyItem=o; 336 337 scroll_to_show(o,_p.listO); 338 c='item_select'; 339 }else if(c==35||c==36){ // 35: End, 36: Home 340 cK=c; 341 if(o=_p.readyItem)setClassName.call(_t,'item',o,0); 342 _p.readyItem=o=_p.listA[c==36?0:_p.listA.length-1]; 343 scroll_to_show(o,_p.listO); 344 c='item_select'; 345 }else if(c==33||c==34){ // 33: PageUP, 34: PageDown 346 cK=c; 347 o=_p.readyItem; 348 if(!o)return; 349 setClassName.call(_t,'item',o,0); 350 var i=gI(o),t; 351 if(c==33){ 352 t=_p.listO.scrollTop-1; 353 while(i&&_p.listA[i-1].offsetTop>t)i--; 354 }else{ 355 t=_p.listO.scrollTop+_p.listO.offsetHeight-scrollbar_width(); 356 while(i<_p.listA.length-1&&_p.listA[i+1].offsetTop<t)i++; 357 } 358 //sl('readyTo: Page: '+i+', top: '+t+', scroll: '+_p.listO.scrollTop); 359 if(i==gI(o)) 360 if(c==33){ 361 t-=_p.listO.offsetHeight; 362 if(t<2)i=0; 363 else while(i&&_p.listA[i-1].offsetTop>t)i--; 364 }else{ 365 t+=_p.listO.offsetHeight; 366 while(i<_p.listA.length-1&&_p.listA[i+1].offsetTop<t)i++; 367 } 368 //sl('readyTo: Page: '+i+', top: '+t+', height: '+_p.listO.offsetHeight); 369 _p.readyItem=o=_p.listA[i]; 370 scroll_to_show(o,_p.listO); 371 c='item_select'; 372 }else return; 373 374 setClassName.call(_t,c,o,0); 375 return false; 376 }, 377 378 //can_use_special_attribute, 379 380 // 顯示清單的工具函數 381 setList=function(l,force,limit,f){ 382 var _t=this,_p=pv(_t),i,c=0,k,o; 383 if(isNaN(limit))limit=isNaN(_t.maxList)?_.maxList:_t.maxList||Number.MAX_VALUE; 384 if(!f)f=function(l,i){ 385 var a=_t.onList(l,i),o; 386 if(!a)return; 387 o=create_DO('div',0,a[0]); 388 setClassName.call(_t,'item',o); 389 o.title=a[1]; 390 k=a[2]||a[1]; 391 o.onmouseover=o.onmouseout=function(e){readyTo.call(_t,e,o);}; 392 o.onclick=function(){var v=_t.onSelect(l,i);_t.setValue(v);_t.onInput(v);}; 393 394 // 這邊本來放在下面 for 的地方 395 c++,_p.listO.appendChild(o); 396 //if(!can_use_special_attribute){o.setAttribute("sIndex",1);can_use_special_attribute=o.getAttribute("sIndex")?1:-1;} 397 //if(can_use_special_attribute==1)o.setAttribute("sIndex",_p.listA.length);else o.sIndex=_p.listA.length; 398 o.sIndex=_p.listA.length;//o.setAttribute("sIndex",o.sIndex=_p.listA.length); 399 _p.listA.push(o); 400 401 return o; 402 }; 403 404 //_t.showList(0); 405 406 _p.listO=removeAllChild(_p.listO),_p.listA=[],_p.readyItem=0; 407 if(l instanceof Array){ 408 for(i=0;i<l.length&&c<limit;i++) 409 f(l,i); 410 }else 411 for(i in l) 412 if(c<limit){ 413 f(l,i); 414 }else break; 415 416 //sl('setList: list '+c+' items, key '+k+'=?'+_t.setValue()); 417 if(c==1&&_t.setValue()==k)c=0; // 僅有一個且與 key 相同 418 if(!force&&!c)return; // 無 list 419 420 // add function 421 if(c!=_t.allListCount)funcButton(_t,_.textSet.allBtn,function(){_t.doFunc=1;_t.focus();_t.setList(_t.setAllList(),1,Number.MAX_VALUE);},'顯示全部 '+_t.allListCount+' 列表。'); 422 if(_t.setValue())funcButton(_t,_.textSet.clearBtn,function(){_t.doFunc=2;_t.focus();_t.onInput(_t.setValue(''));},'清除輸入內容,重新列表。'); 423 funcButton(_t,_.textSet.closeBtn,function(){_t.doFunc=3;_t.showList(0);},'close menu \n關閉列表'); 424 425 showList.call(_t,1); 426 return _t.listCount=c; 427 }, 428 429 // return verify 之後的 key(<input>) 值 430 do_verify=function(k){ 431 var _t=this,c=_t.verify(k||_t.setValue()); 432 //sl('do_verify: input status: '+(c==1?'warning':c==2?'error':'OK')); 433 434 if(typeof c==='string')_t.setValue(k=c); // 可以設定 key 值! 435 else setClassName.call(_t,c==1?'warning':c==2?'error':'input',pv(_t).inputO,1); 436 437 return k; 438 }, 439 440 // 簡易設定常用的 onInput 型式 441 searchInList=function(f,o){ // o: 傳入 (list, index, key) 442 var _t=this; 443 if(typeof f==='string'&&(f in _.searchFunctionSet))f=_.searchFunctionSet[f]; 444 445 // 因為允許傳入 list,所以不能在這邊用 _t.setAllList() 判別函數,而得要寫一個泛用的。 446 return _t.onInput=function(k,L,force){ 447 //sl('searchInList, onInput: search ['+k+'] use '+f); 448 449 if(!L)L=_t.setAllList(); 450 k=do_verify.call(_t,k||''); 451 452 var l,i; 453 454 //sl('searchInList: search '+(L instanceof Array?'array':'object')); 455 if(L instanceof Array){ 456 l=[];//new L.constructor(); 457 for(i=0;i<L.length;i++) 458 if(o?f(L,i,k):L[i]&&f(L[i],k))l.push(L[i]); // search value 459 }else{ 460 l={}; 461 for(i in L) 462 if(o?f(L,i,k):i&&f(i,k)||L[i]&&f(L[i],k))l[i]=L[i]; // search key+value 463 } 464 _t.setList(l,force); 465 }; 466 467 }, 468 469 470 /** 471 * 切換 [input] / inputted [span] 472 * @param {Boolean|undefined} to_input 切換至 input or not. default: 切換至 [input] 473 * @return 474 * @private 475 * @inner 476 * @ignore 477 */ 478 triggerToInput = function(to_input) { 479 var _t = this, _p = pv(_t); 480 if (to_input || typeof to_input === 'undefined') { 481 // to <input> 482 _p.inputtedO.style.display = 'none'; 483 if (_t.allListCount) 484 _p.arrowO.style.display = 'inline'; 485 _p.inputO.style.display = 'inline'; 486 return 1; 487 } else { 488 // to inputted <span> 489 _t.showList(0); 490 _t.setInputted(); 491 492 if (to_input = library_namespace.get_style 493 && parseInt(library_namespace.get_style(_p.inputO, 'width'))){ 494 //library_namespace.debug(to_input); 495 // TODO: +16, +10: magic number 496 try { 497 // .get_style(_p.arrowO, 'width') 可能回傳 'auto' @ IE8 498 _p.inputtedO.style.width = (to_input+parseInt(library_namespace.get_style(_p.arrowO, 'width'))+16)+'px'; 499 _p.inputtedO.style.height = (parseInt(library_namespace.get_style(_p.inputO, 'height'))+10)+'px'; 500 } catch (e) { 501 // TODO: handle exception 502 } 503 } 504 505 _p.arrowO.style.display = _p.inputO.style.display = 'none'; 506 if(!_p.inputtedO.innerHTML) 507 _p.inputtedO.innerHTML=' '; 508 509 //_p.inputtedO.style.border = '3px #aaa dotted'; 510 _p.inputtedO.style.display = to_input ? 'inline-block' : 'inline'; 511 } 512 }, 513 514 // TODO: http://blog.xuite.net/sugopili/computerblog/17695447 515 set_source=function(URL){ 516 ; 517 }, 518 519 /* 配置元件 520 521 本函數會配置/增加: 522 <div> .container 523 <input> .inputO 524 <span> .inputtedO 525 <span> .arrowO 526 <div> .listO 527 528 arguments: 529 <input> 會被當作 inputO 主元件 530 <select> 會被當作選項 531 others: container 532 */ 533 layout=function(o){ 534 var _t=this,_p=pv(_t),t; 535 536 if(typeof o!=='object') 537 o=document.getElementById(o); 538 539 if(!o || (o.tagName.toLowerCase() in {hr:1,br:1}))return; // ** 這邊應該檢查 o 是不是 <hr/> 等不能加 child 的! 540 541 //library_namespace.debug(('layout: use <'+o.tagName+(o.id?'#'+o.id:'')+'>: '+o.innerHTML).replace(/</g,'<')); 542 543 // TODO: 這邊應該有一個更完善的刪除策略 544 if(_t.loaded){ 545 t=_p.container; 546 // 不必多做功,已經達到所需配置了。 547 if(t===o.parentNode)return; 548 for(var i=0,e='inputO,inputtedO,arrowO,listO'.split(',');i<e.length;i++) 549 //library_namespace.debug('layout: removeChild '+e[i]), 550 _p[e[i]].parentNode.removeChild(_p[e[i]]);//t.removeChild(_p[e[i]]); 551 if(!t.childNodes.length)t.parentNode.removeChild(t); 552 } 553 554 555 // 依照各種不同的傳入 object 作出應對 556 t=o.tagName.toLowerCase(); 557 558 if(t==='input'){ 559 560 try{ 561 // http://www.w3.org/TR/html5/forms.html#the-pattern-attribute 562 // http://www.whatwg.org/specs/web-apps/current-work/#attr-input-pattern 563 // http://www.w3school.com.cn/html5/html5_input.asp 564 t=o.pattern|| 565 //o.getAttribute&& 566 o.getAttribute('pattern'); 567 // compiled as a JavaScript regular expression with the global, ignoreCase, and multiline flags disabled 568 // somewhat as if it implied a ^(?: at the start of the pattern and a )$ at the end 569 if(t&&(t=new RegExp('^('+t+')?$'))) 570 //library_namespace.debug('set verify pattern of ['+o.id+']: '+t), 571 _t.set_verify(t); 572 }catch(e){ 573 library_namespace.err('error pattern: ['+t+']'); 574 library_namespace.err(e); 575 } 576 577 o.parentNode.insertBefore( 578 t=_p.container=create_DO('span'), 579 _p.inputO=o 580 ); 581 setClassName.call(_t,'container',t); 582 583 if(!o.className)setClassName.call(_t,'input',o); 584 585 t.appendChild(o.parentNode.removeChild(o)); 586 587 o=t; 588 589 }else if(t==='select'){ 590 o.parentNode.insertBefore(t=_p.container=create_DO('span'),o); 591 592 _p.inputO=create_DO('input',t); 593 setClassName.call(_t,'input',_p.inputO); 594 _p.inputO.name=o.name; 595 if(o.selectedIndex>=0)_p.inputO.value=o.options[o.selectedIndex].value; 596 597 var l={},opt=o.options,i=0; 598 for(;i<opt.length;i++) 599 l[opt[i].value||opt[i].innerHTML]=opt[i].innerHTML; 600 601 // list setting 602 _t.setAllList(l); 603 604 o.parentNode.removeChild(removeAllChild(o)); 605 606 o=t; 607 608 }else{ 609 _p.container=o; // 容器 610 if(!o.className)setClassName.call(_t,'container',o); 611 612 _p.inputO=create_DO('input',o); 613 setClassName.call(_t,'input',_p.inputO); 614 615 } 616 617 618 // 補足其他的設定 619 _p.inputO.setAttribute("autocomplete","off"); 620 621 _p.inputtedO=create_DO('span',o); 622 setClassName.call(_t,'inputted',_p.inputtedO); 623 _p.inputtedO.style.display='none'; 624 625 _p.inputtedO.onclick=function(){ 626 _t.clickNow=1; 627 _t.triggerToInput(); 628 _p.inputO.focus(); 629 _t.clickNow=0; 630 }; 631 632 (_p.arrowO=create_DO('span',o,_.textSet.showList)) 633 .title=_.textSet.arrowTitle; 634 setClassName.call(_t,'arrow',_p.arrowO); 635 636 _p.listO=create_DO('div',o); 637 _p.arrowO.onmouseover=_p.listO.onmouseover=function(){_t.clickNow=1;}; 638 _p.arrowO.onmouseout=_p.listO.onmouseout=function(){_t.clickNow=0;}; 639 setClassName.call(_t,'list',_p.listO); 640 _t.showList(0); 641 642 643 // event setting 644 //_p.inputO.onmouseover= 645 _p.inputO.onkeydown=function(e){readyTo.call(_t,e);}; 646 _p.inputO.onmouseup=_p.inputO.onkeyup=_p.inputO.ondragend=function(e){ 647 if(!e)e=event; 648 var c=e.keyCode||e.which||e.charCode; 649 //sl('up: '+e.type+', key: '+c+', _p.listA: '+(_p.listA&&_p.listA.length)); 650 if(cK&&cK==c){cK=0;return false;} 651 // Esc 652 if(c==27){_t.showList(0);return false;} 653 _t.clickNow=1;_t.onInput(_t.setValue()); 654 }; 655 _p.inputO.onmouseout=function(){_t.clickNow=0;}; 656 if(_p.inputO.addEventListener)_p.inputO.addEventListener('dragdrop',_p.inputO.ondragend,false); 657 //if(window.addEventListener)window.addEventListener('click',function(){_t.showList(0);},true); 658 //addListener(0,'click',function(){sl('close..');_t.showList(0);sl('close done.');}) 659 _p.inputO.onblur=function(){ 660 //if(_t.verify(_t.setValue())==2){alert('Wrong input!');return false;} // 這在 Firefox 似乎沒啥效果.. 661 /* 設定這項在按 _p.arrowO 的時候會出問題,所以建議在其他地方自訂。 662 if(_t.setValue() && (_t.setValue() in _t.setAllList())) 663 _t.triggerToInput(0); 664 */ 665 // TODO: 假如以鍵盤離開,應該也 showList(0); 666 //library_namespace.debug('clickNow='+_t.clickNow,1,'_p.inputO.onblur'); 667 if(_t.clickNow)_t.clickNow=0; 668 else _t.showList(0); 669 }; 670 671 // show/hide by press arrow 672 _p.arrowO.onclick=function(){ 673 //sl('arrowO.onclick start'); 674 _t.clickNow=1; 675 if(_t.showList()) 676 // 正在顯示就把他關起來 677 _t.showList(0); 678 else 679 // 沒在顯示就把他開起來: setValue 設定完 list,onInput 模擬 key-down 680 _t.onInput(_t.setValue(),0,1); 681 _t.clickNow=0; 682 //sl('arrowO.onclick end'); 683 }; 684 // ondblclick: double click 685 //_p.inputO.ondblclick=function(){_t.onInput(_p.inputO.value,0,1);}; 686 687 688 _t.loaded=1; // isLoaded 689 690 }, 691 692 // instance constructor --------------------------- 693 instanceL=[], 694 initI=function(o,l,s){ // (HTML object, list: Array or Object) 695 var _t=this,_p; 696 // objects setting 697 if(typeof o!='object') 698 //library_namespace.debug('Use object ['+o+']'), 699 o=document.getElementById(o); 700 701 _p=pv(_t); // also do initial 702 instanceL.push(_t); // for destructor 703 704 if(o) 705 layout.call(this,o); 706 /* 707 else{ 708 //throw new Error(1,'Can not get document object'+(o?' ['+o+']':'')+'!'); 709 return; 710 } 711 */ 712 713 // list setting 714 if(l&&!_t.allListCount)_t.setAllList(l); 715 716 if(_p.arrowO) 717 _p.arrowO.style.display=_t.allListCount?'inline':'none'; // 無 list 的話先不顯示,等有 list 再說。 718 719 // setup default inputted value 720 _t.dInputted=_t.setValue; 721 722 if(s) 723 _t.setSearch(s); 724 //return _t; 725 }; 726 727 728 //=================================================== 729 730 /* 731 732 _=this 733 734 TODO: 735 浮水印 background-image:url(); 736 737 738 HISTORY: 739 2008/7/22 0:38:14 create 740 7/27 22:55:18 verify() 741 8/7 21:18:47 attach() 742 */ 743 /** 744 * 提供有選單的 input 745 * @class form 的 functions 746 * @see 747 * http://dojocampus.org/explorer/#Dijit_Form%20Controls_Filtering%20Select_Basic 748 */ 749 CeL.net.form.select_input 750 =function(){initI.apply(this,arguments);load_arguments&&load_arguments.apply(this,arguments);}, 751 752 // (instance private handle) 不要 instance private 的把這函數刪掉即可。 753 _p='_'+(Math.random()+'').replace(/\./,''), 754 // get private variables (instance[,destroy]), init private variables (instance[,access function list[, instance destructor]]) 755 pv=function(i,d,k){var V,K=_p('k');return arguments.callee.caller===_p('i')?(V=_p(i[K]=_p()),V.O=i,V.L={}):(K in i)&&(V=_p(i[K]))&&i===V.O?d?(_p(i[K],1),delete i[K]):V.L:{};}; 756 757 // (for inherit) 不要 inherit 的把這段刪掉即可。 758 //(_.clone=arguments.callee).toString=function(){return '[class_template]';}; 759 760 761 // class destructor --------------------------- 762 /* 763 please call at last (e.g., window.unload) 764 765 usage: 766 classT=classT.destroy(); 767 or if you has something more to do: 768 classT.destroy()&&classT=null; 769 */ 770 771 _.destroy=function(){for(var i=0;i<instanceL.length;i++)instanceL[i].destroy();_p();}; 772 773 // (instance private handle, continue) 774 eval('_p=(function(){var '+_p+'={a:pv,d:_.destroy,c:0,k:"+pv+'+Math.random()+'",i:initI};return function(i,d){var f=arguments.callee.caller;if(f==='+_p+'.a){if(!d)return i in '+_p+'?'+_p+'[i]:('+_p+'[i='+_p+'.c++]={},i);'+_p+'[i]={};}if(f==='+_p+'.d)'+_p+'={};}})();'); 775 _p.toString=function(){return'';}; 776 777 778 /* 779 // 測試是否可用自訂之屬性 780 var o=document.createElement('div'); 781 o.setAttribute('testA',2); 782 can_use_special_attribute=o.getAttribute('testA'); 783 sl('can_use_special_attribute: '+can_use_special_attribute); 784 */ 785 786 // class public interface --------------------------- 787 788 789 // 預設清單最大顯示數 790 _.maxList=10; 791 792 793 // searchInList 常用到的函數 794 _.searchFunctionSet = { 795 allTheSame : function(i, k) { 796 return (i + '') === k; 797 }, 798 startWith : function(i, k) { 799 return (i + '').slice(0, k.length) === k; 800 }, 801 // 不管大小寫 Whether the case 802 startWithWC : function(i, k) { 803 return (i + '').slice(0, k.length).toLowerCase() === k.toLowerCase(); 804 }, 805 includeKey : function(i, k) { 806 return (i + '').toLowerCase().indexOf(k.toLowerCase()) !== -1; 807 }, 808 includeKeyWC : function(i, k) { 809 return (i + '').toLowerCase().indexOf((k + '').toLowerCase()) !== -1; 810 }, 811 always : function() { 812 return true; 813 } 814 }; 815 816 817 // 預設 className 前有 ~ 的會轉成 prefix 818 _.classNameSet={ 819 prefix:'si_', 820 container:'~container', 821 input:'~input', 822 inputted:'~inputted', 823 arrow:'~arrow', 824 list:'~list', 825 item:'~item', 826 item_select:'~item_select', 827 functionB:'~function', 828 829 functionT:'~functionText', 830 error:'~error', 831 warning:'~warning' 832 }; 833 834 835 // 預設顯示文字 836 _.textSet={ 837 showList:'▼', // 4 way: [▴▸▾◂] 838 hideList:'▲', 839 arrowTitle:'trigger list \n切換顯示查詢列表', 840 841 allBtn:'全部', 842 clearBtn:'清除', 843 closeBtn:'關閉'//'×' 844 }; 845 846 847 // default 欄位驗證 pattern 848 // http://blog.wu-boy.com/2009/06/16/1454/ 849 // TODO: ID, Age, 電話, 地址, 性別, .. 850 _.default_verify_pattern = { 851 '|word' : /^\w*$/, 852 'word' : /^\w+$/, 853 // 整數 854 '|integer' : /^[+-]?\d*$/, 855 'integer' : /^[+-]?\d+$/, 856 // 自然數 857 '|natural' : /^([1-9]\d*)?$/, 858 'natural' : /^[1-9]\d*$/, 859 // 十進位小數 860 '|decimal' : /^\d*(\.\d+)?$/, 861 'decimal' : /^(\d+|\d*\.\d+)$/, 862 // 數字 863 '|digit' : /^\d*$/, 864 'digit' : /^\d$/, 865 866 IPv4 : /^[12]?\d{1,2}\.[12]?\d{1,2}\.[12]?\d{1,2}\.[12]?\d{1,2}$/, 867 868 URI : function(k){return !!parse_URI(k);}, 869 870 // RFC 2822 871 // http://regexlib.com/DisplayPatterns.aspx 872 //'RFC2822' : /^(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/, 873 // http://www.regular-expressions.info/email.html 874 //'email' : /^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+([a-z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b$/i, 875 email : /^[a-z0-9+_~-]+(\.[a-z0-9+_~-]+)*@([a-z\d]([a-z\d-]*[a-z\d])?\.)+([a-z]{2}|com|org|net)\b$/i, 876 877 any : function(k){return k===''?2:0;} 878 }; 879 880 _.prototype={ 881 // 應該盡量把東西放在 class,instance少一點? 882 883 // instance public interface ------------------- 884 885 886 /* click 事件進行中 887 TODO: 888 用更好的方法取代 889 */ 890 clickNow:0, 891 892 // instance 的 <input>,.. 之 className, override _.classNameSet.input,.. 893 //class_input:'~', 894 //class_item:'~', 895 //.. 896 897 //maxList:\d, 898 899 // 預設清單 height (px) 900 maxListHeight:200, 901 902 903 // 設定/取得所有可選的 list 904 setAllList:function(l){ 905 var _t=this,_p=pv(_t),i,c=0,s=_p.arrowO; 906 _t.showList(0); 907 if(typeof l==='object'){ 908 _p.list=l; 909 if(l instanceof Array)c=_t.allListCount=l.length; // 這不準,得用 onList 測試。 910 else{for(i in l)c++;_t.allListCount=c;} 911 //sl('setAllList: Get about '+_t.allListCount+' items.'); 912 if(s) 913 if(s=s.style,!c)s.display='none'; 914 else if(_t.autoShowArrow)s.display=''; 915 } 916 return _p.list; 917 }, 918 // 自動於有 list 時 show arrow,無時 hide 919 autoShowArrow:0, 920 921 // 設定要顯現的 list,會回傳 list,需注意可能被更改! 922 setList:function(l){ // key 923 return setList.apply(this,arguments); 924 }, 925 926 showList:function(show){ 927 return showList.apply(this,arguments); 928 }, 929 930 /* 931 showArrow:function(show){ 932 var a=pv(this).arrowO.style; 933 if(typeof show!='undefined')a.display=show?'':'none'; 934 return a.display; 935 }, 936 */ 937 938 // 每次 input 就會被 call 一次。可用 instance.setSearch('includeKey') 簡易設定 939 onInput:function(k){ // key 940 }, 941 942 // 設定表單文字欄位的欄位驗證 return 1: warning, 2: error ('Suffering from a pattern mismatch'), string: 將輸入改為回傳值, else OK 943 // 另外可設定 onkeypress(){return true/false;} 來對每一次按鍵作 check。但這不能處理 paste。 http://irw.ncut.edu.tw/peterju/jscript.html#skill 944 verify:function(k){ // key 945 }, 946 947 set_verify:function(v){ 948 var m=_.default_verify_pattern; 949 if(library_namespace.is_Object(m)&&(v in m)) 950 v = m[v]; 951 952 if(v instanceof RegExp) 953 this.verify = function(k) { 954 return v.test(k) ? 0 : 2; 955 }; 956 else if(typeof v === 'function') 957 this.verify = v; 958 else if(typeof v === 'string' && (m=v.match(/^(\d*)-(\d*)$/))&&(m[1]||m[2])) 959 this.verify = new Function('k','return isNaN(k)'+(m[1]?'||k<'+m[1]:'')+(m[2]?'||k>'+m[2]:'')+' ? 2 : 0;'); 960 else 961 library_namespace.err('error verify condition of ['+pv(this).inputO.id+']: ['+v+']'); 962 963 return this; 964 }, 965 966 // input: (list, index), return [value, title[, key=title||value]] 967 onList:function(l,i){ 968 return [l[i]||i,l instanceof Array?l[i]:i]; 969 }, 970 971 // input: (list, index), return value to set as input key 972 onSelect:function(l,i){ 973 return l instanceof Array?l[i]:i; 974 }, 975 976 /* searchInList 的減縮版 977 _.searchInList.call(_instance_,'includeKey'); 978 eq 979 _instance_.setSearch('includeKey'); 980 */ 981 setSearch:function(f){ 982 return searchInList.call(this,f); 983 }, 984 985 setClassName:function(n){ 986 var t=this; 987 if(n)t.class_input=t.class_error=t.class_warning=n; 988 else if(typeof n!='undefined'){delete t.class_input;delete t.class_error;delete t.class_warning;} 989 return setClassName.call(this,'input',pv(this).inputO); 990 }, 991 992 993 setProperty:function(p,v){ 994 var i=pv(this).inputO; 995 //sl('setProperty: '+p+'='+i[p]+'→'+v); 996 if(typeof v!='undefined'&&v!=null)i[p]=v; 997 return i[p]; 998 }, 999 1000 // set/get input value 1001 setValue:function(v){ 1002 if(typeof v!=='undefined') 1003 this.triggerToInput(); 1004 //library_namespace.log('setValue: '+this.setProperty('value',v)); 1005 v=this.setProperty('value',v); 1006 //library_namespace.log('setValue: '+v); 1007 // TODO: do not use arguments 1008 if(arguments.callee.caller!==do_verify) 1009 //library_namespace.log('setValue: call do_verify('+v+'), list: ['+this.allListCount+']'+this.setAllList()), 1010 do_verify.call(this,v); 1011 return v; 1012 }, 1013 1014 // set inputted value: 轉換成輸入過的 <span> 時,設定其之值。 1015 setInputted:function(v){ 1016 var _p=pv(this),i=_p.inputO; 1017 if(typeof v==='undefined')v=this.dInputted(); // dInputted: default inputted value, =setValue 1018 create_DO(0,removeAllChild(_p.inputtedO),v); 1019 return v; 1020 }, 1021 1022 setMaxLength:function(l){ 1023 //sl('setMaxLength: set length '+(l>0?l:null)); 1024 return this.setProperty('maxLength',l>0?l:null); 1025 }, 1026 1027 setName:function(n){ 1028 this.setProperty('id',n); 1029 return this.setProperty('name',n); 1030 }, 1031 1032 setTitle:function(t){ 1033 if(t)pv(this).inputtedO.title=t; 1034 return this.setProperty('title',t||null); 1035 }, 1036 1037 // 切換 inputted span/input 1038 triggerToInput:function(){ 1039 return triggerToInput.apply(this,arguments); 1040 }, 1041 1042 1043 /* 1044 for Unobtrusive JavaScript: 為未啟用JavaScript的情況提供替代方案。 1045 接上 <input> 或 <select> 1046 */ 1047 attach:function(o){ // (input or select object) 1048 //sl('attach: '+o); 1049 //o.replaceNode(_p.inputO); 1050 o=layout.call(this,o); 1051 this.setAllList(this.setAllList()); 1052 return o; 1053 }, 1054 1055 1056 // (focus or blur, 不驅動 onfocus/onblur) 1057 focus:function(f){ // ,noE 1058 var i=pv(this).inputO; 1059 /* 1060 sl('focus: '+(f?'focus':'blur')+(noE?' and no event':'')); 1061 if(f||typeof f==='undefined'){ 1062 if(noE)noE=i.onfocus,i.onfocus=null; 1063 i.focus(); 1064 //if(noE)i.onfocus=noE; 1065 }else{ 1066 if(noE)noE=i.onblur,i.onblur=null;else this.showList(0); 1067 i.blur(); 1068 //if(noE)i.onblur=noE; 1069 } 1070 */ 1071 if(f||typeof f==='undefined') 1072 try{ 1073 // @IE5-8 initial: Error @CeL: 2110 [Error] (facility code 10): 控制項不可見、未啟動或無法接受焦點,因此無法將焦點移到控制項上。 1074 // Error @CeL: 2110 [Error] (facility code 10): Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus. 1075 i.focus(); 1076 }catch(e){} 1077 else 1078 this.showList(0), 1079 i.blur(); 1080 }, 1081 1082 1083 // instance destructor --------------------------- 1084 /* 1085 usage: 1086 instance=instance.destroy(); 1087 or if you has something more to do: 1088 instance.destroy()&&instance=null; 1089 */ 1090 destroy : function(){ 1091 pv(this, 1); 1092 } 1093 1094 }; // _.prototype= 1095 1096 1097 // =================================================== 1098 1099 1100 // prevent re-use. 防止再造 1101 //delete _.clone; 1102 1103 _.allow_inherit=true; 1104 1105 return ( 1106 CeL.net.form.select_input 1107 ); 1108 }; 1109 1110 //=================================================== 1111 1112 CeL.setup_module(module_name, code_for_including); 1113 1114 }; 1115