codeplay-vscroll.js
var Vscroll = function(options,dataList){ if(!options){ alert("Required options"); return; } this.options = options; //this.id = options.id; this.originalData = dataList; //실데이터 this.ReLoad(dataList,options.viewH,options.lineH); this.viewport = options.viewport; this.content = options.content; this.Events.onStart && this.Events.onStart(options,this); //$$$ viewport와 content를 받음. this.viewport.scroll((function(obj){ //jquery return function(){obj.Scroll();} })(this)); this.Init(); }; Vscroll.prototype = { Init : function(){ this.viewport.css({height:this.viewH,overflow:"auto"}); //jquery this.content.css({height:this.h,position:"relative",overflow:"hidden"}); //jquery this.Scroll(); }, ReLoad : function(list, vh, lh){ this.dataList = list; //실데이터 this.viewH = (vh != null)?vh:this.viewH; // 보이는 부분 높이 this.lineH = (lh != null)?lh:this.lineH; // 행 높이 this.lh = Math.ceil(this.viewH / this.lineH); //라인 갯수. this.tot = list.length; this.vHeight = this.tot * this.lineH ; // 가상 높이 (+를 해서 아래 공백을 넣을수 있음.) this.h = this.tot * this.lineH; // 실제 스크롤 가능 높이 this.pageH = this.h / this.lineH; // 페이지 높이 this.pageCnt = Math.ceil(this.vHeight / this.pageH); // 페이지 개수 this.overlaid = (this.vHeight - this.h) / (this.pageCnt - 1); // "넘어가는" 계수 this.page = 0; // 현재 페이지 this.offset=0; // 현재 페이지 위치 this.prevScrollTop = 0; this.rows = {}; // 캐시된 행 노드 }, // vo에 해당하는 이름으로 검색어를 넣어주면 됩니다. {name:"해당값"} 검색시 사용 Filter : function(searchVo,isNotLowerCase){ this._RemoveAllRows(); var filterData = this._findData(searchVo,false,isNotLowerCase); this.ReLoad(filterData); //재설정 this.Init(); }, // selected value를 가져옴 {name:"해당값"} FindData : function(searchVo){ if(!searchVo) return []; return this._findData(searchVo,true,true); //1 true는 equal검색. 2 true는 lowercase검색안하기. }, FindFilteredData : function(searchVo){ if(!searchVo) return []; return this._findFilteredData(searchVo,true); //true는 equal검색. //필터 결과 // var resultList = this.originalData.filter(function(item){ // for(var i in searchVo){ // if(item[i] == searchVo[i]){ // return true; // } // } // }); }, // vo에 해당하는 이름으로 검색어를 넣어주면 됩니다. {selected:false} ChangeAllData : function(changeVo){ if(!changeVo) return; //원본 값을 수정. this.originalData.filter(function(item,i){ for(var v in changeVo){ item[v] = changeVo[v]; //값을 모두 넣는다. } }); //복사본 값을 수정. this.dataList.filter(function(item,i){ for(var v in changeVo){ item[v] = changeVo[v]; //값을 모두 넣는다. } }); //다시그리기 this._ReDraw(); }, // 필터 적용된것만 수정. (dataList를 바꾸되.. original의 동일 데이터를 함께 바꾼다.) {selected:false} ChangeAllFilteredData : function(changeVo){ if(!changeVo) return; var originalList = this.originalData; //복사본 값을 수정. this.dataList.filter(function(item,i){ for(var v in changeVo){ item[v] = changeVo[v]; //값을 모두 넣는다. //_idx가 없으면 원본 그대로임. originalList[((item._idx != null ) ? item._idx : i)][v] = changeVo[v]; //원본도 수정한다. // filtered본에는 _idx로 original의 index를갖고 있음. } }); //다시그리기 this._ReDraw(); }, // vo에 해당하는 이름으로 검색어를 넣어주면 됩니다. {key:{name:한글},val:{selected:true}} ChangeData : function(changeVo){ var keyVo = changeVo.key; var valVo = changeVo.val; if(!keyVo) return; if(!valVo) return; //원본 값을 수정. this.originalData.filter(function(item,i){ for(var k in keyVo){ //{name:한글} if(!keyVo[k]) break; if(item[k] == keyVo[k]){ //1개라도 일치하면 for(var v in valVo){ //{selected:true} item[v] = valVo[v]; //값을 모두 넣는다. } break; //for나가기 } } }); //복사본 값을 수정. this.dataList.filter(function(item,i){ for(var k in keyVo){ if(!keyVo[k]) break;//{name:한글} if(item[k] == keyVo[k]){ //1개라도 일치하면 for(var v in valVo){ //{selected:true} item[v] = valVo[v]; //값을 모두 넣는다. } break; //for나가기 } } }); //다시그리기 //this._ReDraw(); }, // 필터 결과 중에서 검색하기. _findFilteredData : function(searchVo,isEquals){ for(var i in searchVo) if(searchVo[i] == '') delete searchVo[i]; //공백 지우기. //필터 결과 var resultList = this.dataList.filter(function(item){ var failcnt = 0; for(var i in searchVo){ if(!item[i]){ failcnt++; continue; } if(isEquals){ if(item[i] != searchVo[i]){ failcnt++; } }else{ if(item[i].indexOf(searchVo[i]) == -1){ failcnt++; } } } if(failcnt == 0) return true; }); return resultList; }, //해당 조회 조건으로 찾는다. {selected:false} _findData : function(searchVo,isEquals,isNotLowerCase){ for(var i in searchVo) if(searchVo[i] == '') delete searchVo[i]; //공백 지우기. var filterData = []; if(searchVo == null ){ filterData = this.originalData; }else{ for (var k = 0; k < this.originalData.length; k++) { var item = this.originalData[k]; var failcnt = 0; for(var i in searchVo){ if(!item[i]){ failcnt++; continue; } //소문자 통일 여부. var oriVal = item[i]; var tarVal = searchVo[i]; if(!isNotLowerCase){ //소문자로 통일. oriVal = (""+item[i]).toLowerCase(); tarVal = (""+searchVo[i]).toLowerCase(); } if(isEquals){ if(oriVal != tarVal){ failcnt++; } }else{ if(oriVal.indexOf(tarVal) == -1){ failcnt++; } } } if(failcnt == 0){ item._idx = k; //original의 index; filterData.push(item); } } } return filterData; }, _JumpScroll : function(){ //순간이동 스크롤을 빨리하면 일루 빠짐. var scrollTop = this.viewport.scrollTop(); this.page = Math.floor(scrollTop * ((this.vHeight-this.viewH) / (this.h-this.viewH)) * (1/this.pageH)); this.offset = Math.round(this.page * this.overlaid); this.prevScrollTop = scrollTop; this._RemoveAllRows(); }, _NearScroll : function(){ var scrollTop = this.viewport.scrollTop(); // 다음 페이지 if (scrollTop + this.offset > (this.page+1)*this.pageH) { this.page++; this.offset = Math.round(this.page * this.overlaid); this.viewport.scrollTop(this.prevScrollTop = scrollTop - this.overlaid); this._RemoveAllRows(); } // 이전 페이지 else if (scrollTop + this.offset < this.page*this.pageH) { this.page--; this.offset = Math.round(this.page * this.overlaid); this.viewport.scrollTop(this.prevScrollTop = scrollTop + this.overlaid); this._RemoveAllRows(); } else{ this.prevScrollTop = scrollTop; } }, _RemoveAllRows : function() { for (var i in this.rows) { this.rows[i].remove(); //객체 지우기. delete this.rows[i]; } }, _RenderViewport : function() { // 보이는 부분 + 버퍼 를 계산한다. var y = this.viewport.scrollTop() + this.offset, buffer = this.viewH, //*0.6, top = Math.floor((y-buffer)/this.lineH), bottom = Math.ceil((y+this.viewH+buffer)/this.lineH); //부드럽게 보일려면 *0.5를 높여준다. top = Math.max(0,top); //상단 bottom = Math.min(this.tot,bottom); //하단 // 더이상 보이지 않는 부분을 제거한다. for (var i in this.rows) { if (i < top || i > bottom) { this.rows[i].remove(); //jquery // this.Events.onRemoveRow && this.Events.onRemoveRow(this.rows[i],i); delete this.rows[i]; } } // 새 행을 넣는다. for (var i=top; i < bottom; i++) { if (!this.rows[i]){ var css = { position: "absolute", top: i*this.lineH - this.offset, height: this.lineH, index : i }; this.rows[i] = this.Events.onDraw(css,this.dataList[i],this.options); //만든거. //여기서 에러 } } //다그리고 호출. try{ this.Events.onDrawAfter(this.options,this); }catch(e){} }, //화면만 갱신 _ReDraw : function(){ // this.rows = {}; this._RemoveAllRows(); this._RenderViewport(); }, //스크롤 동작 이벤트 Scroll : function(){ var scrollTop = this.viewport.scrollTop(); if (Math.abs(scrollTop-this.prevScrollTop) > this.viewH){ //jump this._JumpScroll(); }else { this._NearScroll(); } this.Events.onScroll && this.Events.onScroll(this); //$$$외부 호출. this._RenderViewport(); } }; //multi select Vscroll.prototype.Events = { onStart : function(options,obj){ //미리 체크 항목 넣기. if(!options.selectedItems) return; obj.originalData.filter(function(item,i){ for(var i in options.selectedItems){ if(item.CODE == options.selectedItems[i]){ item.selected = true; break; } } }); }, onScroll : function(obj){}, //페이지 표시 가능. onDraw : function(css, row, options){ return $("") .css(css) .text("row " + row.CODE) .appendTo(options.content); }, onDrawAfter : function(options,obj){ }, onRemoveRow :function(rowObj){} }; var MultiSelect = { init : function(){ var dataList = []; for(var i = 0 ; i < 50000 ; i++){ dataList[i] = {name :"한글"+i ,code:"testCode"+i }; } this.vscrolling(dataList); }, vscrolling : function(dataList){ Vscroll.prototype.Events.onDraw = function(css, row, opts){ return $("") .css(css) .text("row " + row.name) .appendTo(opts.content); }; this.viewport1 = new Vscroll({viewport : $("#viewport") ,content : $("#content"),viewH:500,lineH:30},dataList); }, //검색어 입력 changeFilter : function(obj,objname){ if(obj == "") this[objname].Filter(null); else this[objname].Filter({name:obj.value}); } }; $(function() { MultiSelect.init(); });