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