function Scrollbar(mainHolder, holder, content, settings){ this.mainHolder = mainHolder; this.mainHolder.SBobjectModel = this; this.holder = holder; this.barX = null; this.barY = null; this.content = content; this.cont_cur_left = 0; this.cont_cur_top = 0; this.holderProps = {}; this.contentProps = {}; this.interval = null; this.intervalValue = 30; this.contentMoveValue = 5; this.cursor = { X: null, Y: null }; this.settings = (settings) ? settings : Scrollbar.defaultSettings; this.init(); } Scrollbar.prototype.init = function(){ this.initObjects(); //Инициализация встроенных объектов this.getWH(this.holder, this.holderProps); //Получаем высоту и ширину внутреннего холдера this.getWH(this.content, this.contentProps); //Получаем ширину и высоту контента this.createMovement(); this.check(); } Scrollbar.prototype.initObjects = function(){ //Внимание!!! //Данная функция может стать причиной всякого кала!!!!!!!! for(var i in this){ if(this[i] != null && typeof this[i] == "object" && this[i].needsInitialization){ this[i].parentObject = this; } } }; Scrollbar.prototype.getWH = function(elem, obj){ var oDimensions = Style.getDimensions(elem); obj.width = oDimensions.width; obj.height = oDimensions.height; } Scrollbar.prototype.createMovement = function(){ this.MOV = new Movement( this.content, 0, 0, 0, 0, 0 ); } Scrollbar.prototype.check = function(){ if(this.holderProps.width < this.contentProps.width){//Если ширина внутреннего холдера меньше ширины контента if(!this.barX){ this.barX = new this.sBar(this, "X"); }else{ this.barX.remove(); this.barX = new this.sBar(this, "X"); } }else{ if(this.barX){ this.barX.remove(); this.barX = null; } } if(this.holderProps.height < this.contentProps.height){ if(!this.barY){ this.barY = new this.sBar(this, "Y"); }else{ this.barY.remove(); this.barY = new this.sBar(this, "Y"); } }else{ if(this.barY){ this.barY.remove(); this.barY = null; } } }; Scrollbar.prototype.unset = function(){ if(this.barY){ this.barY.remove(); this.barY = null; } if(this.barX){ this.barX.remove(); this.barX = null; } } Scrollbar.prototype.stop = function(){ var objectModel = GetObjectModel(this, "SBobjectModel"); if(objectModel.interval){ window.clearInterval(objectModel.interval); } } /*******************************************/ Scrollbar.prototype.moveContentTo = function(x, y){ if(this.barX){ var movX = Math.round((-x) / this.barX.moveDiff); this.barX.DD.moveBy(movX, 0); } if(this.barY){ var movY = Math.round((-y) / this.barY.moveDiff); this.barY.DD.moveBy(0, movY); } //alert('x:' + movX + ', y:'+ movY); } Scrollbar.prototype.reinit = function(insertedX, insertedY){ var myLeft = this.cont_cur_left; var myWidth = this.contentProps.width; var myTop = this.cont_cur_top; var myHeight = this.contentProps.height; if(this.barX){ this.barX.DD.move(-this.barX.DD.draggedX, 0); } if(this.barY){ this.barY.DD.move(0, -this.barY.DD.draggedY); } this.getWH(this.content, this.contentProps); this.getWH(this.holder, this.holderProps); var diffWidth = this.contentProps.width - myWidth; var diffHeight = this.contentProps.height - myHeight; this.check(); this.cont_cur_left = 0; this.cont_cur_top = 0; if(insertedY < Math.abs(myTop)){ this.moveContentTo(myLeft - diffWidth, myTop - diffHeight); }else{ this.moveContentTo(myLeft, myTop); } } Scrollbar.prototype.insertContent = function(content, contentType, where){ //contentType ['string', 'html', 'DOM'] //where ['beginning', 'end'] var insX, insY if(where == 'beginning'){ insX = -1; insY = -1; }else{ insX = 0; insY = this.contentProps.height; } var cont; var html = false; switch(contentType){ case 'string': cont = document.createTextNode(content); break; case 'DOM': cont = content.cloneNode(true); break; case 'html': cont = content; html = true; }//switch if(html){ var curHTML = this.content.innerHTML; if(where == 'beginning'){ curHTML = cont + curHTML; }else if(where == 'end'){ curHTML += cont; } this.content.innerHTML = curHTML; }else{ if(where == 'beginning'){ this.content.insertBefore(cont, this.content.firstChild); }else if(where == 'end'){ this.content.appendChild(cont) } } this.reinit(insX, insY); } Scrollbar.prototype.removeContent = function(where){ var remX, remY; var node; if(where == 'end'){ node = this.content.childNodes[this.content.childNodes.length - 1]; remY = this.contentProps.height; }else{ node = this.content.firstChild; remY = -1; } if(this.content.firstChild){ this.content.removeChild(node); }else{ alert('Больше нечего удалять'); return; } remX = -1; this.reinit(remX, remY); } Scrollbar.prototype.Debug = { needsInitialization: true, parentObject: {}, out: function(what){ var str = ""; if(typeof what == "object"){ for(var i in what){ str += i; str += " => "; str += what[i]; str += "\n"; }//for }else{ str = what; } var oDIV = document.createElement("DIV"); oDIV.appendChild(document.createTextNode(str)); document.body.appendChild(oDIV); }, outLine: function(what){ var str = what + " "; document.body.appendChild(document.createTextNode(str)); } } Scrollbar.prototype.sBar = function(obj, type){ if(!obj || !type){ // this._error_message(Scrollbar.E._not_enough_arguments) return; } this.name = "Scrollbar" + type; this.type = type; this.parentObject = obj; this.Config = Scrollbar.BarConfig; //В этот объект мы распакуем скроллбар this.Scrollbar = {}; //Здесь будет ссылка на родительский элемент распакованного бара this.Bar = {}; //Здесь будут основные свойства скроллбара this.Props = {}; //А сюда поместим ссылку на холдер, чтобы ближе был this.holder = {}; //Здесь будет ссылка на mainHolder this.mainHolder = {}; //Здесь разместим ссылку на контентбар, шоб поближе this.content = {}; this.contentMS = null; this.contMoved = 0; //здесь будут основные свойства бегунка this.thumbProps = {}; //сюда поместим свойства полосы прокрутки и this.thumbwrapperProps = {}; this.innerThumbProps = {}; //помещаем свойства кнопок this.buttonProps = {}; //сюда поместим имаги, цвета и прочую дребедень this.settings = {}; this.diff = 0; //сие есть разность, которую используем при вычислении размера бегунка this.sumDiff = 0; //сие есть переменная, в которую записывается разница между............. this.stop = { //флаги minus: true, plus: false }; this.moveSpace = 0; //пространство, в котором может двигаться бегунок this.thumbMoveValue = 0; this.start(); }; Scrollbar.prototype.sBar.prototype = { start: function(){ this.initNames(); this.initSettings(); this.initHolders(); this.initScrollbar(); //Записываем необходимые значение в глобальные ассоциативные массивы (массивы глобальные относительно объекта ScrollY) this.getScrollbarProps(); this.getButtonProps(); this.getThumbwrapperProps(); this.getThumbProps(); //Используя функцию объекта Style setElementStyle, //устанавливаем необходимые CSS-стили для элементов скролла //Внимание!! //Некоторые стили были проинициализированы при распаковке!!! (вобще-то, сей дуализм необходимо искоренить :-)) this.appendStyles(); //Устанавливаем значение moveSpace!!! this.moveSpace = this.thumbwrapperProps[this.pri_prop] - this.thumbProps[this.pri_prop]; this.contentMS = this.parentObject.contentProps[this.pri_prop] - this.parentObject.holderProps[this.pri_prop]; this.moveDiff = (this.parentObject.contentProps[this.pri_prop] - this.parentObject.holderProps[this.pri_prop]) / this.moveSpace; this.initThumb(); this.initButtonMinus(); this.initButtonPlus(); if(this.type == "Y" && !window.getComputedStyle){ this.initMouseWheel(); } }, initNames: function(){ var obj = Scrollbar.Names["bar" + this.type.toUpperCase()]; for(var i in obj){ this[i] = obj[i]; } }, initSettings: function(){ this.settings = this.parentObject.settings["scroll" + this.type]; this.Config.scrollbar.children.bMinus.attributes = this.settings.bMinus; this.Config.scrollbar.children.bPlus.attributes = this.settings.bPlus; this.Config.scrollbar.children.thumbWrapper.style["background-color"] = this.settings.barBgColor; if(!this.settings.thumbImg){ this.Config.scrollbar.children.thumbWrapper.children.thumb.style["background-color"] = this.settings.thumbColor; }else{ this.Config.scrollbar.children.thumbWrapper.children.thumb.children = {}; this.Config.scrollbar.children.thumbWrapper.children.thumb.children.imgDiv = {}; this.Config.scrollbar.children.thumbWrapper.children.thumb.children.imgDiv.tag = "DIV"; this.Config.scrollbar.children.thumbWrapper.children.thumb.children.imgDiv.RETURN = true; } }, initHolders: function(){ this.holder = this.parentObject.holder; //помещаем ссылку на главный холдер this.mainHolder = this.parentObject.mainHolder; this.mainHolder.style[this.bar_padding] = this.settings.bMinus[this.sec_prop]+ "px"; this.content = this.parentObject.content; }, initScrollbar: function(){ this.Scrollbar = new Unpacker(this.Config, this.mainHolder); //распаковываем this.Bar = this.Scrollbar.ReturnElements.scrollbar;//присваиваем ссылку на родительский элемент this.Bar.style[this.place] = 0; }, getScrollbarProps: function(){ this.Props[this.sec_prop] = this.Scrollbar.ReturnElements.bMinus[this.sec_prop]; this.Props[this.pri_prop] = this.parentObject.holderProps[this.pri_prop]; this.Props.position = "absolute"; //!Глюк! @Opera =-= если задать свойство bottom, появляется глюк в отображении this.Props[this.place] = 0; /****************************************************************************************/ }, getButtonProps: function(){ this.buttonProps[this.sec_prop] = this.Scrollbar.ReturnElements.bMinus[this.sec_prop]; this.buttonProps[this.pri_prop] = this.Scrollbar.ReturnElements.bMinus[this.pri_prop]; }, getThumbwrapperProps: function(){ this.thumbwrapperProps[this.pri_prop]= this.Props[this.pri_prop] - this.buttonProps[this.pri_prop] * 2 - 2; this.thumbwrapperProps[this.sec_prop] = this.Props[this.sec_prop]; this.thumbwrapperProps[this.place2] = 0; //!!!!!!!!!!! this.thumbwrapperProps[this.pos] = this.buttonProps[this.pri_prop] + 1; }, getThumbProps: function(){ var holderValue = this.parentObject.holderProps[this.pri_prop]; var contentValue = this.parentObject.contentProps[this.pri_prop]; var diffValue = contentValue / holderValue; this.thumbProps.fontSize = 0; if(!this.settings.thumbImg){ this.thumbProps[this.pri_prop] = Math.round(this.thumbwrapperProps[this.pri_prop] / diffValue); this.thumbProps[this.sec_prop] = this.thumbwrapperProps[this.sec_prop] - 2; this.thumbProps[this.place2] = 1; }else{ this.thumbProps[this.pri_prop] = this.settings.thumbImg[this.pri_prop]; this.thumbProps[this.sec_prop] = this.settings.thumbImg[this.sec_prop]; this.thumbProps[this.place2] = 0; this.innerThumbProps[this.sec_prop] = this.thumbProps[this.sec_prop]; this.innerThumbProps[this.pri_prop] = this.thumbProps[this.pri_prop]; this.innerThumbProps.backgroundImage = "url("+this.settings.thumbImg.src+")"; } this.diff = diffValue; }, appendStyles: function(){ Style.setElementStyle(this.Bar, this.Props, null, true); Style.setElementStyle(this.Scrollbar.ReturnElements.thumbWrapper, this.thumbwrapperProps, null, true); Style.setElementStyle(this.Scrollbar.ReturnElements.thumb, this.thumbProps, null, true); Style.setElementStyle(this.Scrollbar.ReturnElements.bPlus, this.pos2, 0, true); if(this.settings.thumbImg){ Style.setElementStyle(this.Scrollbar.ReturnElements.imgDiv, this.innerThumbProps, null, true); } this.Bar.style.visibility = "visible"; }, //Оживляем Скролл!! //Функции, отвечающие за правильную работу Скролла moveBy: function(step){ //window.status = this.mainHolder.id; //Значение step отнимается от текущей позиции бегунка при каждом вызове функции // => чтобы бегунок пошел влево нужно передать положительное значение // => чтобы бегунок пошел вправо, значение step должно быть отрицательным //Флаги: // stop.left - запрещает движение бегунка влево // stop.right - запрещает движение бегунка вправо if(step > 0 && this.stop.plus) //Если бегунок достиг правого края, и step < 0 return; //Выходим... if(step > 0 && this.stop.minus) //Если флаг stop.up установлен, а бегунок пошел вправо this.stop.minus = false; //Сбросить флаг stop.left if(step < 0 && this.stop.plus) //Если бегунок достиг левого края, и step > 0 return; //Выходим... if(step < 0 && this.stop.minus) //Если флаг stop.right установлен, а бегунок пошел вверх this.stop.plus = false; //Сбросить Флаг stop.right var oCont = this.parentObject.content; //Получаем ссылку на контент var oThumb = this.Scrollbar.ReturnElements.thumb; //Получаем ссылку на бегунок //Получаем текущие значение положений контента и бегунка var contPos = parseInt(Style.getElementStyle(oCont, this.pos)); if(isNaN(contPos)){ contPos = 0;//для IE } var thumbPos = parseInt(Style.getElementStyle(oThumb, this.pos)); if(isNaN(thumbPos)){ thumbPos = 0;//для IE } //Прверяем, нужно ли установить флаги для следующего вызова функции if(thumbPos + step > this.moveSpace){// step = Math.round((this.moveSpace - thumbPos)); this.stop.plus = true; }else if(thumbPos + step < 0){ step = -thumbPos; this.stop.minus = true; } var contMove = step * this.moveDiff; var contMoveRounded = Math.round(contMove); var contMoveDiff = contMove - contMoveRounded; this.sumDiff += contMoveDiff; var additionalMove = 0; if(Math.abs(this.sumDiff) >= 1){ additionalMove = (this.sumDiff > 0) ? 1 : -1; this.sumDiff -= additionalMove; } contMove = contMoveRounded + additionalMove; oCont.style[this.pri_prop] = contPos - contMove + "px"; oThumb.style[this.pri_prop] = thumbPos + step + "px"; //oThis.parentObject.Debug.out("Thumb: " + step + ", Cont: "+ contMove); }, moveBy2: function(step){ var contMove = step * this.moveDiff; var contMoveRounded = Math.round(contMove); var contMoveDiff = contMove - contMoveRounded; this.sumDiff += contMoveDiff; var additionalMove = 0; if(Math.abs(this.sumDiff) >= 1){ additionalMove = (this.sumDiff > 0) ? 1 : -1; this.sumDiff -= additionalMove; } contMove = contMoveRounded + additionalMove; //this.parentObject.Debug.outLine('S:' + step + ', C: '+ contMove + '-\\\\-'); this.parentObject.MOV[this.move_step] = contMove; this.parentObject.MOV[this.move_step2] = 0; this.parentObject["cont_cur_" + this.prop] += contMove; this.parentObject.MOV.move.call(this.parentObject.MOV); }, moveContentBy: function(){ }, moveContentTo: function(){ }, //Функции, инициализирующие кнопки initButtonMinus: function(){ var btn = this.Scrollbar.ReturnElements.bMinus; var type = this.type; btn.onmousedown = function(){ var objectModel = GetObjectModel(this, "SBobjectModel"); var _mov_args = [-objectModel["bar" + type].thumbMoveValue, 0]; if(type == "Y"){ _mov_args = _mov_args._invert(); } objectModel.interval = window.setInterval( function(){ objectModel["bar" + type].DD.moveBy.apply(objectModel["bar" + type].DD, _mov_args); }, objectModel.intervalValue ); } btn.onmouseup = this.parentObject.stop; btn.onmouseout = this.parentObject.stop; }, initButtonPlus: function(){ var btn = this.Scrollbar.ReturnElements.bPlus; var type = this.type; btn.onmousedown = function(){ var objectModel = GetObjectModel(this, "SBobjectModel"); var _mov_args = [objectModel["bar" + type].thumbMoveValue, 0]; if(type == "Y"){ _mov_args = _mov_args._invert(); } objectModel.interval = window.setInterval( function(){ objectModel["bar" + type].DD.moveBy.apply(objectModel["bar" + type].DD, _mov_args); }, objectModel.intervalValue ); }; btn.onmouseup = this.parentObject.stop; btn.onmouseout = this.parentObject.stop; }, initThumb: function(){ this.thumbMoveValue = Math.round(this.parentObject.contentMoveValue / this.moveDiff); var ddObj = {}; ddObj["moveSpace" + this.type] = this.moveSpace; ddObj["moveSpace" + this._opposite] = 0; this.DD = new DragDrop( this.Scrollbar.ReturnElements.thumb, this.Scrollbar.ReturnElements.thumb, ddObj ); this.DD.onstart = { func: function(){ this.mainHolder.onmousedown = function(){ return false; } this.mainHolder.onselectstart = function(){ return false; } }, oThis: this.parentObject, args: [] }; this.DD.onstop = { func: function(){ this.mainHolder.onmousedown = null; this.mainHolder.onselectstart = null; }, oThis: this.parentObject, args: [] }; //alert(this.DD.onstart); this.DD.onmove = { func: function(oThis){ oThis.moveBy2.apply(oThis, [-this["delta" + oThis.type]]) }, oThis: this.DD, args: [this] }; }, initMouseWheel: function(){ this.content.onmouseover = function(){ document.onmousewheel = function(e){ var ev = window.event || e; ev.returnValue = false; this.DD.moveBy(0, -ev.wheelDelta / 60); }.bind(this) }.bind(this) this.content.onmouseout = function(){ document.onmousewheel = null; } }, remove: function(){ this.mainHolder.removeChild(this.Bar); this.mainHolder.style[this.bar_padding] = "0"; } } Scrollbar.Names = {} Scrollbar.Names.barX = { pos: "left", pos2: "right", pri_prop: "width", sec_prop: "height", bar_padding: "paddingRight", place: "bottom", place2: "top", move_step: "stepX", move_step2: "stepY", _opposite: "Y" }; Scrollbar.Names.barY = { pos: "top", pos2: "bottom", pri_prop: "height", sec_prop: "width", bar_padding: "paddingBottom", place: "right", place2: "left", move_step: "stepY", move_step2: "stepX", _opposite: "X" }; Scrollbar.defaultSettings = { scrollX: { bMinus: { src: "img/btn-lft.gif", alt: "Scroll Left", width: 11, height: 11 }, bPlus: { src: "img/btn-rt.gif", alt: "Scroll Right", width: 11, height: 11 }, barBgColor: "#333366", thumbColor: "#ceced6", thumbMargin: 1 }, scrollY: { bMinus: { src: "img/btn-up.gif", alt: "Scroll Up", width: 11, height: 11 }, bPlus: { src: "img/btn-dn.gif", alt: "Scroll Down", width: 11, height: 11 }, barBgColor: "#333366", thumbColor: "#ceced6", thumbMargin: 1 } } Scrollbar.BarConfig = { scrollbar: { tag: "DIV", attributes: { className: "" }, style: { visibility: "hidden" }, RETURN: true, children: { bMinus: { tag: "IMG", attributes: {}, style: { position: "absolute" }, RETURN: true }, bPlus: { tag: "IMG", attributes: {}, style: { position: "absolute" }, RETURN: true }, thumbWrapper: { tag: "DIV", style: { position: "absolute" }, RETURN: true, children: { thumb: { tag: "DIV", style: { position: "absolute" }, RETURN: true } } } } } } Scrollbar._credits = function(){ var str = ""; for(var i in this._about){ str += i + ": " + this._about[i] + "\n"; } alert(str); } Scrollbar._about = { Version: "2.0.1", Description: "Better code, easier to work with", Author: "RadukAV", Email: "ohana2005@yandex.ru" };