clipbucket/upload/styles/cb_27/theme/js/custom-elements.js
2014-09-22 12:55:49 +00:00

1384 lines
No EOL
47 KiB
JavaScript

// page init
bindReady(function(){
jcf.customForms.replaceAll('.custom-elements');
});
/*
* JavaScript Custom Forms Module
*/
jcf = {
// global options
modules: {},
plugins: {},
baseOptions: {
unselectableClass:'jcf-unselectable',
labelActiveClass:'jcf-label-active',
labelDisabledClass:'jcf-label-disabled',
classPrefix: 'jcf-class-',
hiddenClass:'jcf-hidden',
focusClass:'jcf-focus',
wrapperTag: 'div'
},
// replacer function
customForms: {
setOptions: function(obj) {
for(var p in obj) {
if(obj.hasOwnProperty(p) && typeof obj[p] === 'object') {
jcf.lib.extend(jcf.modules[p].prototype.defaultOptions, obj[p]);
}
}
},
replaceAll: function(context) {
for(var k in jcf.modules) {
var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector, context);
for(var i = 0; i<els.length; i++) {
if(els[i].jcf) {
// refresh form element state
els[i].jcf.refreshState();
} else {
// replace form element
if(!jcf.lib.hasClass(els[i], 'default') && jcf.modules[k].prototype.checkElement(els[i])) {
new jcf.modules[k]({
replaces:els[i]
});
}
}
}
}
},
refreshAll: function(context) {
for(var k in jcf.modules) {
var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector, context);
for(var i = 0; i<els.length; i++) {
if(els[i].jcf) {
// refresh form element state
els[i].jcf.refreshState();
}
}
}
},
refreshElement: function(obj) {
if(obj && obj.jcf) {
obj.jcf.refreshState();
}
},
destroyAll: function() {
for(var k in jcf.modules) {
var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector);
for(var i = 0; i<els.length; i++) {
if(els[i].jcf) {
els[i].jcf.destroy();
}
}
}
}
},
// detect device type
isTouchDevice: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
isWinPhoneDevice: navigator.msPointerEnabled && /MSIE 10.*Touch/.test(navigator.userAgent),
// define base module
setBaseModule: function(obj) {
jcf.customControl = function(opt){
this.options = jcf.lib.extend({}, jcf.baseOptions, this.defaultOptions, opt);
this.init();
};
for(var p in obj) {
jcf.customControl.prototype[p] = obj[p];
}
},
// add module to jcf.modules
addModule: function(obj) {
if(obj.name){
// create new module proto class
jcf.modules[obj.name] = function(){
jcf.modules[obj.name].superclass.constructor.apply(this, arguments);
}
jcf.lib.inherit(jcf.modules[obj.name], jcf.customControl);
for(var p in obj) {
jcf.modules[obj.name].prototype[p] = obj[p]
}
// on create module
jcf.modules[obj.name].prototype.onCreateModule();
// make callback for exciting modules
for(var mod in jcf.modules) {
if(jcf.modules[mod] != jcf.modules[obj.name]) {
jcf.modules[mod].prototype.onModuleAdded(jcf.modules[obj.name]);
}
}
}
},
// add plugin to jcf.plugins
addPlugin: function(obj) {
if(obj && obj.name) {
jcf.plugins[obj.name] = function() {
this.init.apply(this, arguments);
}
for(var p in obj) {
jcf.plugins[obj.name].prototype[p] = obj[p];
}
}
},
// miscellaneous init
init: function(){
if(navigator.pointerEnabled || navigator.msPointerEnabled) {
// use pointer events instead of mouse events
this.eventPress = navigator.pointerEnabled ? 'pointerdown' : 'MSPointerDown';
this.eventMove = navigator.pointerEnabled ? 'pointermove' : 'MSPointerMove';
this.eventRelease = navigator.pointerEnabled ? 'pointerup' : 'MSPointerUp';
} else {
// handle default desktop mouse events
this.eventPress = 'mousedown';
this.eventMove = 'mousemove';
this.eventRelease = 'mouseup';
}
if(this.isTouchDevice) {
// handle touch events also
this.eventPress += ' touchstart';
this.eventMove += ' touchmove';
this.eventRelease += ' touchend';
}
setTimeout(function(){
jcf.lib.domReady(function(){
jcf.initStyles();
});
},1);
return this;
},
initStyles: function() {
// create <style> element and rules
var head = document.getElementsByTagName('head')[0],
style = document.createElement('style'),
rules = document.createTextNode('.'+jcf.baseOptions.unselectableClass+'{'+
'-moz-user-select:none;'+
'-webkit-tap-highlight-color:rgba(255,255,255,0);'+
'-webkit-user-select:none;'+
'user-select:none;'+
'}');
// append style element
style.type = 'text/css';
if(style.styleSheet) {
style.styleSheet.cssText = rules.nodeValue;
} else {
style.appendChild(rules);
}
head.appendChild(style);
}
}.init();
/*
* Custom Form Control prototype
*/
jcf.setBaseModule({
init: function(){
if(this.options.replaces) {
this.realElement = this.options.replaces;
this.realElement.jcf = this;
this.replaceObject();
}
},
defaultOptions: {
// default module options (will be merged with base options)
},
checkElement: function(el){
return true; // additional check for correct form element
},
replaceObject: function(){
this.createWrapper();
this.attachEvents();
this.fixStyles();
this.setupWrapper();
},
createWrapper: function(){
this.fakeElement = jcf.lib.createElement(this.options.wrapperTag);
this.labelFor = jcf.lib.getLabelFor(this.realElement);
jcf.lib.disableTextSelection(this.fakeElement);
jcf.lib.addClass(this.fakeElement, jcf.lib.getAllClasses(this.realElement.className, this.options.classPrefix));
jcf.lib.addClass(this.realElement, jcf.baseOptions.hiddenClass);
},
attachEvents: function(){
jcf.lib.event.add(this.realElement, 'focus', this.onFocusHandler, this);
jcf.lib.event.add(this.realElement, 'blur', this.onBlurHandler, this);
jcf.lib.event.add(this.fakeElement, 'click', this.onFakeClick, this);
jcf.lib.event.add(this.fakeElement, jcf.eventPress, this.onFakePressed, this);
jcf.lib.event.add(this.fakeElement, jcf.eventRelease, this.onFakeReleased, this);
if(this.labelFor) {
this.labelFor.jcf = this;
jcf.lib.event.add(this.labelFor, 'click', this.onFakeClick, this);
jcf.lib.event.add(this.labelFor, jcf.eventPress, this.onFakePressed, this);
jcf.lib.event.add(this.labelFor, jcf.eventRelease, this.onFakeReleased, this);
}
},
fixStyles: function() {
// hide mobile webkit tap effect
if(jcf.isTouchDevice) {
var tapStyle = 'rgba(255,255,255,0)';
this.realElement.style.webkitTapHighlightColor = tapStyle;
this.fakeElement.style.webkitTapHighlightColor = tapStyle;
if(this.labelFor) {
this.labelFor.style.webkitTapHighlightColor = tapStyle;
}
}
},
setupWrapper: function(){
// implement in subclass
},
refreshState: function(){
// implement in subclass
},
destroy: function() {
if(this.fakeElement && this.fakeElement.parentNode) {
this.fakeElement.parentNode.insertBefore(this.realElement, this.fakeElement);
this.fakeElement.parentNode.removeChild(this.fakeElement);
}
jcf.lib.removeClass(this.realElement, jcf.baseOptions.hiddenClass);
this.realElement.jcf = null;
},
onFocus: function(){
// emulated focus event
jcf.lib.addClass(this.fakeElement,this.options.focusClass);
},
onBlur: function(cb){
// emulated blur event
jcf.lib.removeClass(this.fakeElement,this.options.focusClass);
},
onFocusHandler: function() {
// handle focus loses
if(this.focused) return;
this.focused = true;
// handle touch devices also
if(jcf.isTouchDevice) {
if(jcf.focusedInstance && jcf.focusedInstance.realElement != this.realElement) {
jcf.focusedInstance.onBlur();
jcf.focusedInstance.realElement.blur();
}
jcf.focusedInstance = this;
}
this.onFocus.apply(this, arguments);
},
onBlurHandler: function() {
// handle focus loses
if(!this.pressedFlag) {
this.focused = false;
this.onBlur.apply(this, arguments);
}
},
onFakeClick: function(){
if(jcf.isTouchDevice) {
this.onFocus();
} else if(!this.realElement.disabled) {
this.realElement.focus();
}
},
onFakePressed: function(e){
this.pressedFlag = true;
},
onFakeReleased: function(){
this.pressedFlag = false;
},
onCreateModule: function(){
// implement in subclass
},
onModuleAdded: function(module) {
// implement in subclass
},
onControlReady: function() {
// implement in subclass
}
});
/*
* JCF Utility Library
*/
jcf.lib = {
bind: function(func, scope){
return function() {
return func.apply(scope, arguments);
};
},
browser: (function() {
var ua = navigator.userAgent.toLowerCase(), res = {},
match = /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version)?[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+))?/.exec(ua) || [];
res[match[1]] = true;
res.version = match[2] || "0";
res.safariMac = ua.indexOf('mac') != -1 && ua.indexOf('safari') != -1;
return res;
})(),
getOffset: function (obj) {
if (obj.getBoundingClientRect && !jcf.isWinPhoneDevice) {
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
var clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0;
var clientTop = document.documentElement.clientTop || document.body.clientTop || 0;
return {
top:Math.round(obj.getBoundingClientRect().top + scrollTop - clientTop),
left:Math.round(obj.getBoundingClientRect().left + scrollLeft - clientLeft)
};
} else {
var posLeft = 0, posTop = 0;
while (obj.offsetParent) {posLeft += obj.offsetLeft; posTop += obj.offsetTop; obj = obj.offsetParent;}
return {top:posTop,left:posLeft};
}
},
getScrollTop: function() {
return window.pageYOffset || document.documentElement.scrollTop;
},
getScrollLeft: function() {
return window.pageXOffset || document.documentElement.scrollLeft;
},
getWindowWidth: function(){
return document.compatMode=='CSS1Compat' ? document.documentElement.clientWidth : document.body.clientWidth;
},
getWindowHeight: function(){
return document.compatMode=='CSS1Compat' ? document.documentElement.clientHeight : document.body.clientHeight;
},
getStyle: function(el, prop) {
if (document.defaultView && document.defaultView.getComputedStyle) {
return document.defaultView.getComputedStyle(el, null)[prop];
} else if (el.currentStyle) {
return el.currentStyle[prop];
} else {
return el.style[prop];
}
},
getParent: function(obj, selector) {
while(obj.parentNode && obj.parentNode != document.body) {
if(obj.parentNode.tagName.toLowerCase() == selector.toLowerCase()) {
return obj.parentNode;
}
obj = obj.parentNode;
}
return false;
},
isParent: function(parent, child) {
while(child.parentNode) {
if(child.parentNode === parent) {
return true;
}
child = child.parentNode;
}
return false;
},
getLabelFor: function(object) {
var parentLabel = jcf.lib.getParent(object,'label');
if(parentLabel) {
return parentLabel;
} else if(object.id) {
return jcf.lib.queryBySelector('label[for="' + object.id + '"]')[0];
}
},
disableTextSelection: function(el){
if (typeof el.onselectstart !== 'undefined') {
el.onselectstart = function() {return false;};
} else if(window.opera) {
el.setAttribute('unselectable', 'on');
} else {
jcf.lib.addClass(el, jcf.baseOptions.unselectableClass);
}
},
enableTextSelection: function(el) {
if (typeof el.onselectstart !== 'undefined') {
el.onselectstart = null;
} else if(window.opera) {
el.removeAttribute('unselectable');
} else {
jcf.lib.removeClass(el, jcf.baseOptions.unselectableClass);
}
},
queryBySelector: function(selector, scope){
if(typeof scope === 'string') {
var result = [];
var holders = this.getElementsBySelector(scope);
for (var i = 0, contextNodes; i < holders.length; i++) {
contextNodes = Array.prototype.slice.call(this.getElementsBySelector(selector, holders[i]));
result = result.concat(contextNodes);
}
return result;
} else {
return this.getElementsBySelector(selector, scope);
}
},
prevSibling: function(node) {
while(node = node.previousSibling) if(node.nodeType == 1) break;
return node;
},
nextSibling: function(node) {
while(node = node.nextSibling) if(node.nodeType == 1) break;
return node;
},
fireEvent: function(element,event) {
if(element.dispatchEvent){
var evt = document.createEvent('HTMLEvents');
evt.initEvent(event, true, true );
return !element.dispatchEvent(evt);
}else if(document.createEventObject){
var evt = document.createEventObject();
return element.fireEvent('on'+event,evt);
}
},
inherit: function(Child, Parent) {
var F = function() { }
F.prototype = Parent.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
Child.superclass = Parent.prototype
},
extend: function(obj) {
for(var i = 1; i < arguments.length; i++) {
for(var p in arguments[i]) {
if(arguments[i].hasOwnProperty(p)) {
obj[p] = arguments[i][p];
}
}
}
return obj;
},
hasClass: function (obj,cname) {
return (obj.className ? obj.className.match(new RegExp('(\\s|^)'+cname+'(\\s|$)')) : false);
},
addClass: function (obj,cname) {
if (!this.hasClass(obj,cname)) obj.className += (!obj.className.length || obj.className.charAt(obj.className.length - 1) === ' ' ? '' : ' ') + cname;
},
removeClass: function (obj,cname) {
if (this.hasClass(obj,cname)) obj.className=obj.className.replace(new RegExp('(\\s|^)'+cname+'(\\s|$)'),' ').replace(/\s+$/, '');
},
toggleClass: function(obj, cname, condition) {
if(condition) this.addClass(obj, cname); else this.removeClass(obj, cname);
},
createElement: function(tagName, options) {
var el = document.createElement(tagName);
for(var p in options) {
if(options.hasOwnProperty(p)) {
switch (p) {
case 'class': el.className = options[p]; break;
case 'html': el.innerHTML = options[p]; break;
case 'style': this.setStyles(el, options[p]); break;
default: el.setAttribute(p, options[p]);
}
}
}
return el;
},
setStyles: function(el, styles) {
for(var p in styles) {
if(styles.hasOwnProperty(p)) {
switch (p) {
case 'float': el.style.cssFloat = styles[p]; break;
case 'opacity': el.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity='+styles[p]*100+')'; el.style.opacity = styles[p]; break;
default: el.style[p] = (typeof styles[p] === 'undefined' ? 0 : styles[p]) + (typeof styles[p] === 'number' ? 'px' : '');
}
}
}
return el;
},
getInnerWidth: function(el) {
return el.offsetWidth - (parseInt(this.getStyle(el,'paddingLeft')) || 0) - (parseInt(this.getStyle(el,'paddingRight')) || 0);
},
getInnerHeight: function(el) {
return el.offsetHeight - (parseInt(this.getStyle(el,'paddingTop')) || 0) - (parseInt(this.getStyle(el,'paddingBottom')) || 0);
},
getAllClasses: function(cname, prefix, skip) {
if(!skip) skip = '';
if(!prefix) prefix = '';
return cname ? cname.replace(new RegExp('(\\s|^)'+skip+'(\\s|$)'),' ').replace(/[\s]*([\S]+)+[\s]*/gi,prefix+"$1 ") : '';
},
getElementsBySelector: function(selector, scope) {
if(typeof document.querySelectorAll === 'function') {
return (scope || document).querySelectorAll(selector);
}
var selectors = selector.split(',');
var resultList = [];
for(var s = 0; s < selectors.length; s++) {
var currentContext = [scope || document];
var tokens = selectors[s].replace(/^\s+/,'').replace(/\s+$/,'').split(' ');
for (var i = 0; i < tokens.length; i++) {
token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');
if (token.indexOf('#') > -1) {
var bits = token.split('#'), tagName = bits[0], id = bits[1];
var element = document.getElementById(id);
if (tagName && element.nodeName.toLowerCase() != tagName) {
return [];
}
currentContext = [element];
continue;
}
if (token.indexOf('.') > -1) {
var bits = token.split('.'), tagName = bits[0] || '*', className = bits[1], found = [], foundCount = 0;
for (var h = 0; h < currentContext.length; h++) {
var elements;
if (tagName == '*') {
elements = currentContext[h].getElementsByTagName('*');
} else {
elements = currentContext[h].getElementsByTagName(tagName);
}
for (var j = 0; j < elements.length; j++) {
found[foundCount++] = elements[j];
}
}
currentContext = [];
var currentContextIndex = 0;
for (var k = 0; k < found.length; k++) {
if (found[k].className && found[k].className.match(new RegExp('(\\s|^)'+className+'(\\s|$)'))) {
currentContext[currentContextIndex++] = found[k];
}
}
continue;
}
if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^"]*)"?\]$/)) {
var tagName = RegExp.$1 || '*', attrName = RegExp.$2, attrOperator = RegExp.$3, attrValue = RegExp.$4;
if(attrName.toLowerCase() == 'for' && this.browser.msie && this.browser.version < 8) {
attrName = 'htmlFor';
}
var found = [], foundCount = 0;
for (var h = 0; h < currentContext.length; h++) {
var elements;
if (tagName == '*') {
elements = currentContext[h].getElementsByTagName('*');
} else {
elements = currentContext[h].getElementsByTagName(tagName);
}
for (var j = 0; elements[j]; j++) {
found[foundCount++] = elements[j];
}
}
currentContext = [];
var currentContextIndex = 0, checkFunction;
switch (attrOperator) {
case '=': checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue) }; break;
case '~': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('(\\s|^)'+attrValue+'(\\s|$)'))) }; break;
case '|': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))) }; break;
case '^': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0) }; break;
case '$': checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length) }; break;
case '*': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1) }; break;
default : checkFunction = function(e) { return e.getAttribute(attrName) };
}
currentContext = [];
var currentContextIndex = 0;
for (var k = 0; k < found.length; k++) {
if (checkFunction(found[k])) {
currentContext[currentContextIndex++] = found[k];
}
}
continue;
}
tagName = token;
var found = [], foundCount = 0;
for (var h = 0; h < currentContext.length; h++) {
var elements = currentContext[h].getElementsByTagName(tagName);
for (var j = 0; j < elements.length; j++) {
found[foundCount++] = elements[j];
}
}
currentContext = found;
}
resultList = [].concat(resultList,currentContext);
}
return resultList;
},
scrollSize: (function(){
var content, hold, sizeBefore, sizeAfter;
function buildSizer(){
if(hold) removeSizer();
content = document.createElement('div');
hold = document.createElement('div');
hold.style.cssText = 'position:absolute;overflow:hidden;width:100px;height:100px';
hold.appendChild(content);
document.body.appendChild(hold);
}
function removeSizer(){
document.body.removeChild(hold);
hold = null;
}
function calcSize(vertical) {
buildSizer();
content.style.cssText = 'height:'+(vertical ? '100%' : '200px');
sizeBefore = (vertical ? content.offsetHeight : content.offsetWidth);
hold.style.overflow = 'scroll'; content.innerHTML = 1;
sizeAfter = (vertical ? content.offsetHeight : content.offsetWidth);
if(vertical && hold.clientHeight) sizeAfter = hold.clientHeight;
removeSizer();
return sizeBefore - sizeAfter;
}
return {
getWidth:function(){
return calcSize(false);
},
getHeight:function(){
return calcSize(true)
}
}
}()),
domReady: function (handler){
var called = false
function ready() {
if (called) return;
called = true;
handler();
}
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", ready, false);
} else if (document.attachEvent) {
if (document.documentElement.doScroll && window == window.top) {
function tryScroll(){
if (called) return
if (!document.body) return
try {
document.documentElement.doScroll("left")
ready()
} catch(e) {
setTimeout(tryScroll, 0)
}
}
tryScroll()
}
document.attachEvent("onreadystatechange", function(){
if (document.readyState === "complete") {
ready()
}
})
}
if (window.addEventListener) window.addEventListener('load', ready, false)
else if (window.attachEvent) window.attachEvent('onload', ready)
},
event: (function(){
var guid = 0;
function fixEvent(e) {
e = e || window.event;
if (e.isFixed) {
return e;
}
e.isFixed = true;
e.preventDefault = e.preventDefault || function(){this.returnValue = false}
e.stopPropagation = e.stopPropagation || function(){this.cancelBubble = true}
if (!e.target) {
e.target = e.srcElement
}
if (!e.relatedTarget && e.fromElement) {
e.relatedTarget = e.fromElement == e.target ? e.toElement : e.fromElement;
}
if (e.pageX == null && e.clientX != null) {
var html = document.documentElement, body = document.body;
e.pageX = e.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0);
e.pageY = e.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0);
}
if (!e.which && e.button) {
e.which = e.button & 1 ? 1 : (e.button & 2 ? 3 : (e.button & 4 ? 2 : 0));
}
if(e.type === "DOMMouseScroll" || e.type === 'mousewheel') {
e.mWheelDelta = 0;
if (e.wheelDelta) {
e.mWheelDelta = e.wheelDelta/120;
} else if (e.detail) {
e.mWheelDelta = -e.detail/3;
}
}
return e;
}
function commonHandle(event, customScope) {
event = fixEvent(event);
var handlers = this.events[event.type];
for (var g in handlers) {
var handler = handlers[g];
var ret = handler.call(customScope || this, event);
if (ret === false) {
event.preventDefault()
event.stopPropagation()
}
}
}
var publicAPI = {
add: function(elem, type, handler, forcedScope) {
// handle multiple events
if(type.indexOf(' ') > -1) {
var eventList = type.split(' ');
for(var i = 0; i < eventList.length; i++) {
publicAPI.add(elem, eventList[i], handler, forcedScope);
}
return;
}
if (elem.setInterval && (elem != window && !elem.frameElement)) {
elem = window;
}
if (!handler.guid) {
handler.guid = ++guid;
}
if (!elem.events) {
elem.events = {};
elem.handle = function(event) {
return commonHandle.call(elem, event);
}
}
if (!elem.events[type]) {
elem.events[type] = {};
if (elem.addEventListener) elem.addEventListener(type, elem.handle, false);
else if (elem.attachEvent) elem.attachEvent("on" + type, elem.handle);
if(type === 'mousewheel') {
publicAPI.add(elem, 'DOMMouseScroll', handler, forcedScope);
}
}
var fakeHandler = jcf.lib.bind(handler, forcedScope);
fakeHandler.guid = handler.guid;
elem.events[type][handler.guid] = forcedScope ? fakeHandler : handler;
},
remove: function(elem, type, handler) {
// handle multiple events
if(type.indexOf(' ') > -1) {
var eventList = type.split(' ');
for(var i = 0; i < eventList.length; i++) {
publicAPI.remove(elem, eventList[i], handler);
}
return;
}
var handlers = elem.events && elem.events[type];
if (!handlers) return;
delete handlers[handler.guid];
for(var any in handlers) return;
if (elem.removeEventListener) elem.removeEventListener(type, elem.handle, false);
else if (elem.detachEvent) elem.detachEvent("on" + type, elem.handle);
delete elem.events[type];
for (var any in elem.events) return;
try {
delete elem.handle;
delete elem.events;
} catch(e) {
if(elem.removeAttribute) {
elem.removeAttribute("handle");
elem.removeAttribute("events");
}
}
if(type === 'mousewheel') {
publicAPI.remove(elem, 'DOMMouseScroll', handler);
}
}
}
return publicAPI;
}())
}
// custom scrollbars module
jcf.addModule({
name:'customscroll',
selector:'div.scrollable-area',
defaultOptions: {
alwaysPreventWheel: false,
enableMouseWheel: true,
captureFocus: false,
handleNested: true,
alwaysKeepScrollbars: false,
autoDetectWidth: false,
scrollbarOptions: {},
focusClass:'scrollable-focus',
wrapperTag: 'div',
autoDetectWidthClass: 'autodetect-width',
noHorizontalBarClass:'noscroll-horizontal',
noVerticalBarClass:'noscroll-vertical',
innerWrapperClass:'scrollable-inner-wrapper',
outerWrapperClass:'scrollable-area-wrapper',
horizontalClass: 'hscrollable',
verticalClass: 'vscrollable',
bothClass: 'anyscrollable'
},
replaceObject: function(){
this.initStructure();
this.refreshState();
this.addEvents();
},
initStructure: function(){
// set scroll type
this.realElement.jcf = this;
if(jcf.lib.hasClass(this.realElement, this.options.bothClass) ||
jcf.lib.hasClass(this.realElement, this.options.horizontalClass) && jcf.lib.hasClass(this.realElement, this.options.verticalClass)) {
this.scrollType = 'both';
} else if(jcf.lib.hasClass(this.realElement, this.options.horizontalClass)) {
this.scrollType = 'horizontal';
} else {
this.scrollType = 'vertical';
}
// autodetect horizontal width
if(jcf.lib.hasClass(this.realElement,this.options.autoDetectWidthClass)) {
this.options.autoDetectWidth = true;
}
// init dimensions and build structure
this.realElement.style.position = 'relative';
this.realElement.style.overflow = 'hidden';
// build content wrapper and scrollbar(s)
this.buildWrapper();
this.buildScrollbars();
},
buildWrapper: function() {
this.outerWrapper = document.createElement(this.options.wrapperTag);
this.outerWrapper.className = this.options.outerWrapperClass;
this.realElement.parentNode.insertBefore(this.outerWrapper, this.realElement);
this.outerWrapper.appendChild(this.realElement);
// autosize content if single child
if(this.options.autoDetectWidth && (this.scrollType === 'both' || this.scrollType === 'horizontal') && this.realElement.children.length === 1) {
var tmpWidth = 0;
this.realElement.style.width = '99999px';
tmpWidth = this.realElement.children[0].offsetWidth;
this.realElement.style.width = '';
if(tmpWidth) {
this.realElement.children[0].style.width = tmpWidth+'px';
}
}
},
buildScrollbars: function() {
if(this.scrollType === 'horizontal' || this.scrollType === 'both') {
this.hScrollBar = new jcf.plugins.scrollbar(jcf.lib.extend(this.options.scrollbarOptions,{
vertical: false,
spawnClass: this,
holder: this.outerWrapper,
range: this.realElement.scrollWidth - this.realElement.offsetWidth,
size: this.realElement.offsetWidth,
onScroll: jcf.lib.bind(function(v) {
this.realElement.scrollLeft = v;
},this)
}));
}
if(this.scrollType === 'vertical' || this.scrollType === 'both') {
this.vScrollBar = new jcf.plugins.scrollbar(jcf.lib.extend(this.options.scrollbarOptions,{
vertical: true,
spawnClass: this,
holder: this.outerWrapper,
range: this.realElement.scrollHeight - this.realElement.offsetHeight,
size: this.realElement.offsetHeight,
onScroll: jcf.lib.bind(function(v) {
this.realElement.scrollTop = v;
},this)
}));
}
this.outerWrapper.style.width = this.realElement.offsetWidth + 'px';
this.outerWrapper.style.height = this.realElement.offsetHeight + 'px';
this.resizeScrollContent();
},
resizeScrollContent: function() {
var diffWidth = this.realElement.offsetWidth - jcf.lib.getInnerWidth(this.realElement);
var diffHeight = this.realElement.offsetHeight - jcf.lib.getInnerHeight(this.realElement);
this.realElement.style.width = Math.max(0, this.outerWrapper.offsetWidth - diffWidth - (this.vScrollBar ? this.vScrollBar.getScrollBarSize() : 0)) + 'px';
this.realElement.style.height = Math.max(0, this.outerWrapper.offsetHeight - diffHeight - (this.hScrollBar ? this.hScrollBar.getScrollBarSize() : 0)) + 'px';
},
addEvents: function() {
// enable mouse wheel handling
if(!jcf.isTouchDevice && this.options.enableMouseWheel) {
jcf.lib.event.add(this.outerWrapper, 'mousewheel', this.onMouseWheel, this);
}
// add touch scroll on block body
if(jcf.isTouchDevice || navigator.msPointerEnabled || navigator.pointerEnabled) {
this.outerWrapper.style.msTouchAction = 'none';
jcf.lib.event.add(this.realElement, jcf.eventPress, this.onScrollablePress, this);
}
// handle nested scrollbars
if(this.options.handleNested) {
var el = this.realElement, name = this.name;
while(el.parentNode) {
if(el.parentNode.jcf && el.parentNode.jcf.name == name) {
el.parentNode.jcf.refreshState();
}
el = el.parentNode;
}
}
},
isTouchPointerEvent: function(e) {
return (e.type.indexOf('touch') > -1) ||
(navigator.pointerEnabled && e.pointerType === 'touch') ||
(navigator.msPointerEnabled && e.pointerType === e.MSPOINTER_TYPE_TOUCH);
},
onMouseWheel: function(e) {
if(this.scrollType === 'vertical' || this.scrollType === 'both') {
return this.vScrollBar.doScrollWheelStep(e.mWheelDelta) === false ? false : !this.options.alwaysPreventWheel;
} else {
return this.hScrollBar.doScrollWheelStep(e.mWheelDelta) === false ? false : !this.options.alwaysPreventWheel;
}
},
onScrollablePress: function(e) {
if(!this.isTouchPointerEvent(e)) {
return;
}
this.preventFlag = true;
this.origWindowScrollTop = jcf.lib.getScrollTop();
this.origWindowScrollLeft = jcf.lib.getScrollLeft();
this.scrollableOffset = jcf.lib.getOffset(this.realElement);
if(this.hScrollBar) {
this.scrollableTouchX = (e.changedTouches ? e.changedTouches[0] : e).pageX;
this.origValueX = this.hScrollBar.getScrollValue();
}
if(this.vScrollBar) {
this.scrollableTouchY = (e.changedTouches ? e.changedTouches[0] : e).pageY;
this.origValueY = this.vScrollBar.getScrollValue();
}
jcf.lib.event.add(this.realElement, jcf.eventMove, this.onScrollableMove, this);
jcf.lib.event.add(this.realElement, jcf.eventRelease, this.onScrollableRelease, this);
},
onScrollableMove: function(e) {
if(this.vScrollBar) {
var difY = (e.changedTouches ? e.changedTouches[0] : e).pageY - this.scrollableTouchY;
var valY = this.origValueY-difY;
this.vScrollBar.scrollTo(valY);
if(valY < 0 || valY > this.vScrollBar.options.range) {
this.preventFlag = false;
}
}
if(this.hScrollBar) {
var difX = (e.changedTouches ? e.changedTouches[0] : e).pageX - this.scrollableTouchX;
var valX = this.origValueX-difX;
this.hScrollBar.scrollTo(valX);
if(valX < 0 || valX > this.hScrollBar.options.range) {
this.preventFlag = false;
}
}
if(this.preventFlag) {
e.preventDefault();
}
},
onScrollableRelease: function() {
jcf.lib.event.remove(this.realElement, jcf.eventMove, this.onScrollableMove);
jcf.lib.event.remove(this.realElement, jcf.eventRelease, this.onScrollableRelease);
},
refreshState: function() {
if(this.options.alwaysKeepScrollbars) {
if(this.hScrollBar) this.hScrollBar.scrollBar.style.display = 'block';
if(this.vScrollBar) this.vScrollBar.scrollBar.style.display = 'block';
} else {
if(this.hScrollBar) {
if(this.getScrollRange(false)) {
this.hScrollBar.scrollBar.style.display = 'block';
this.resizeScrollContent();
this.hScrollBar.setRange(this.getScrollRange(false));
} else {
this.hScrollBar.scrollBar.style.display = 'none';
this.realElement.style.width = this.outerWrapper.style.width;
}
jcf.lib.toggleClass(this.outerWrapper, this.options.noHorizontalBarClass, this.hScrollBar.options.range === 0);
}
if(this.vScrollBar) {
if(this.getScrollRange(true) > 0) {
this.vScrollBar.scrollBar.style.display = 'block';
this.resizeScrollContent();
this.vScrollBar.setRange(this.getScrollRange(true));
} else {
this.vScrollBar.scrollBar.style.display = 'none';
this.realElement.style.width = this.outerWrapper.style.width;
}
jcf.lib.toggleClass(this.outerWrapper, this.options.noVerticalBarClass, this.vScrollBar.options.range === 0);
}
}
if(this.vScrollBar) {
this.vScrollBar.setRange(this.realElement.scrollHeight - this.realElement.offsetHeight);
this.vScrollBar.setSize(this.realElement.offsetHeight);
this.vScrollBar.scrollTo(this.realElement.scrollTop);
}
if(this.hScrollBar) {
this.hScrollBar.setRange(this.realElement.scrollWidth - this.realElement.offsetWidth);
this.hScrollBar.setSize(this.realElement.offsetWidth);
this.hScrollBar.scrollTo(this.realElement.scrollLeft);
}
},
getScrollRange: function(isVertical) {
if(isVertical) {
return this.realElement.scrollHeight - this.realElement.offsetHeight;
} else {
return this.realElement.scrollWidth - this.realElement.offsetWidth;
}
},
getCurrentRange: function(scrollInstance) {
return this.getScrollRange(scrollInstance.isVertical);
},
onCreateModule: function(){
if(jcf.modules.select) {
this.extendSelect();
}
if(jcf.modules.selectmultiple) {
this.extendSelectMultiple();
}
if(jcf.modules.textarea) {
this.extendTextarea();
}
},
onModuleAdded: function(module){
if(module.prototype.name == 'select') {
this.extendSelect();
}
if(module.prototype.name == 'selectmultiple') {
this.extendSelectMultiple();
}
if(module.prototype.name == 'textarea') {
this.extendTextarea();
}
},
extendSelect: function() {
// add scrollable if needed on control ready
jcf.modules.select.prototype.onControlReady = function(obj){
if(obj.selectList.scrollHeight > obj.selectList.offsetHeight) {
obj.jcfScrollable = new jcf.modules.customscroll({
alwaysPreventWheel: true,
replaces:obj.selectList
});
}
}
// update scroll function
var orig = jcf.modules.select.prototype.scrollToItem;
jcf.modules.select.prototype.scrollToItem = function(){
orig.apply(this);
if(this.jcfScrollable) {
this.jcfScrollable.refreshState();
}
}
},
extendTextarea: function() {
// add scrollable if needed on control ready
jcf.modules.textarea.prototype.onControlReady = function(obj){
obj.jcfScrollable = new jcf.modules.customscroll({
alwaysKeepScrollbars: true,
alwaysPreventWheel: true,
replaces: obj.realElement
});
}
// update scroll function
var orig = jcf.modules.textarea.prototype.refreshState;
jcf.modules.textarea.prototype.refreshState = function(){
orig.apply(this);
if(this.jcfScrollable) {
this.jcfScrollable.refreshState();
}
}
},
extendSelectMultiple: function(){
// add scrollable if needed on control ready
jcf.modules.selectmultiple.prototype.onControlReady = function(obj){
//if(obj.optionsHolder.scrollHeight > obj.optionsHolder.offsetHeight) {
obj.jcfScrollable = new jcf.modules.customscroll({
alwaysPreventWheel: true,
replaces:obj.optionsHolder
});
//}
}
// update scroll function
var orig = jcf.modules.selectmultiple.prototype.scrollToItem;
jcf.modules.selectmultiple.prototype.scrollToItem = function(){
orig.apply(this);
if(this.jcfScrollable) {
this.jcfScrollable.refreshState();
}
}
// update scroll size?
var orig2 = jcf.modules.selectmultiple.prototype.rebuildOptions;
jcf.modules.selectmultiple.prototype.rebuildOptions = function(){
orig2.apply(this);
if(this.jcfScrollable) {
this.jcfScrollable.refreshState();
}
}
}
});
// scrollbar plugin
jcf.addPlugin({
name: 'scrollbar',
defaultOptions: {
size: 0,
range: 0,
moveStep: 6,
moveDistance: 50,
moveInterval: 10,
trackHoldDelay: 900,
holder: null,
vertical: true,
scrollTag: 'div',
onScroll: function(){},
onScrollEnd: function(){},
onScrollStart: function(){},
disabledClass: 'btn-disabled',
VscrollBarClass:'vscrollbar',
VscrollStructure: '<div class="vscroll-up"></div><div class="vscroll-line"><div class="vscroll-slider"><div class="scroll-bar-top"></div><div class="scroll-bar-bottom"></div></div></div></div><div class="vscroll-down"></div>',
VscrollTrack: 'div.vscroll-line',
VscrollBtnDecClass:'div.vscroll-up',
VscrollBtnIncClass:'div.vscroll-down',
VscrollSliderClass:'div.vscroll-slider',
HscrollBarClass:'hscrollbar',
HscrollStructure: '<div class="hscroll-left"></div><div class="hscroll-line"><div class="hscroll-slider"><div class="scroll-bar-left"></div><div class="scroll-bar-right"></div></div></div></div><div class="hscroll-right"></div>',
HscrollTrack: 'div.hscroll-line',
HscrollBtnDecClass:'div.hscroll-left',
HscrollBtnIncClass:'div.hscroll-right',
HscrollSliderClass:'div.hscroll-slider'
},
init: function(userOptions) {
this.setOptions(userOptions);
this.createScrollBar();
this.attachEvents();
this.setSize();
},
setOptions: function(extOptions) {
// merge options
this.options = jcf.lib.extend({}, this.defaultOptions, extOptions);
this.isVertical = this.options.vertical;
this.prefix = this.isVertical ? 'V' : 'H';
this.eventPageOffsetProperty = this.isVertical ? 'pageY' : 'pageX';
this.positionProperty = this.isVertical ? 'top' : 'left';
this.sizeProperty = this.isVertical ? 'height' : 'width';
this.dimenionsProperty = this.isVertical ? 'offsetHeight' : 'offsetWidth';
this.invertedDimenionsProperty = !this.isVertical ? 'offsetHeight' : 'offsetWidth';
// set corresponding classes
for(var p in this.options) {
if(p.indexOf(this.prefix) == 0) {
this.options[p.substr(1)] = this.options[p];
}
}
},
createScrollBar: function() {
// create dimensions
this.scrollBar = document.createElement(this.options.scrollTag);
this.scrollBar.className = this.options.scrollBarClass;
this.scrollBar.innerHTML = this.options.scrollStructure;
// get elements
this.track = jcf.lib.queryBySelector(this.options.scrollTrack,this.scrollBar)[0];
this.btnDec = jcf.lib.queryBySelector(this.options.scrollBtnDecClass,this.scrollBar)[0];
this.btnInc = jcf.lib.queryBySelector(this.options.scrollBtnIncClass,this.scrollBar)[0];
this.slider = jcf.lib.queryBySelector(this.options.scrollSliderClass,this.scrollBar)[0];
this.slider.style.position = 'absolute';
this.track.style.position = 'relative';
},
attachEvents: function() {
// append scrollbar to holder if provided
if(this.options.holder) {
this.options.holder.appendChild(this.scrollBar);
}
// attach listeners for slider and buttons
jcf.lib.event.add(this.slider, jcf.eventPress, this.onSliderPressed, this);
jcf.lib.event.add(this.btnDec, jcf.eventPress, this.onBtnDecPressed, this);
jcf.lib.event.add(this.btnInc, jcf.eventPress, this.onBtnIncPressed, this);
jcf.lib.event.add(this.track, jcf.eventPress, this.onTrackPressed, this);
},
setSize: function(value) {
if(typeof value === 'number') {
this.options.size = value;
}
this.scrollOffset = this.scrollValue = this.sliderOffset = 0;
this.scrollBar.style[this.sizeProperty] = this.options.size + 'px';
this.resizeControls();
this.refreshSlider();
},
setRange: function(r) {
this.options.range = Math.max(r,0);
this.resizeControls();
},
doScrollWheelStep: function(direction) {
// 1 - scroll up, -1 scroll down
this.startScroll();
if((direction < 0 && !this.isEndPosition()) || (direction > 0 && !this.isStartPosition())) {
this.scrollTo(this.getScrollValue()-this.options.moveDistance * direction);
this.moveScroll();
this.endScroll();
return false;
}
},
resizeControls: function() {
// calculate dimensions
this.barSize = this.scrollBar[this.dimenionsProperty];
this.btnDecSize = this.btnDec[this.dimenionsProperty];
this.btnIncSize = this.btnInc[this.dimenionsProperty];
this.trackSize = Math.max(0, this.barSize - this.btnDecSize - this.btnIncSize);
// resize and reposition elements
this.track.style[this.sizeProperty] = this.trackSize + 'px';
this.trackSize = this.track[this.dimenionsProperty];
this.sliderSize = this.getSliderSize();
this.slider.style[this.sizeProperty] = this.sliderSize + 'px';
this.sliderSize = this.slider[this.dimenionsProperty];
},
refreshSlider: function(complete) {
// refresh dimensions
if(complete) {
this.resizeControls();
}
// redraw slider and classes
this.sliderOffset = isNaN(this.sliderOffset) ? 0 : this.sliderOffset;
this.slider.style[this.positionProperty] = this.sliderOffset + 'px';
},
startScroll: function() {
// refresh range if possible
if(this.options.spawnClass && typeof this.options.spawnClass.getCurrentRange === 'function') {
this.setRange(this.options.spawnClass.getCurrentRange(this));
}
this.resizeControls();
this.scrollBarOffset = jcf.lib.getOffset(this.track)[this.positionProperty];
this.options.onScrollStart();
},
moveScroll: function() {
this.options.onScroll(this.scrollValue);
// add disabled classes
jcf.lib.removeClass(this.btnDec, this.options.disabledClass);
jcf.lib.removeClass(this.btnInc, this.options.disabledClass);
if(this.scrollValue === 0) {
jcf.lib.addClass(this.btnDec, this.options.disabledClass);
}
if(this.scrollValue === this.options.range) {
jcf.lib.addClass(this.btnInc, this.options.disabledClass);
}
},
endScroll: function() {
this.options.onScrollEnd();
},
startButtonMoveScroll: function(direction) {
this.startScroll();
clearInterval(this.buttonScrollTimer);
this.buttonScrollTimer = setInterval(jcf.lib.bind(function(){
this.scrollValue += this.options.moveStep * direction
if(this.scrollValue > this.options.range) {
this.scrollValue = this.options.range;
this.endButtonMoveScroll();
} else if(this.scrollValue < 0) {
this.scrollValue = 0;
this.endButtonMoveScroll();
}
this.scrollTo(this.scrollValue);
},this),this.options.moveInterval);
},
endButtonMoveScroll: function() {
clearInterval(this.buttonScrollTimer);
this.endScroll();
},
isStartPosition: function() {
return this.scrollValue === 0;
},
isEndPosition: function() {
return this.scrollValue === this.options.range;
},
getSliderSize: function() {
return Math.round(this.getSliderSizePercent() * this.trackSize / 100);
},
getSliderSizePercent: function() {
return this.options.range === 0 ? 0 : this.barSize * 100 / (this.barSize + this.options.range);
},
getSliderOffsetByScrollValue: function() {
return (this.scrollValue * 100 / this.options.range) * (this.trackSize - this.sliderSize) / 100;
},
getSliderOffsetPercent: function() {
return this.sliderOffset * 100 / (this.trackSize - this.sliderSize);
},
getScrollValueBySliderOffset: function() {
return this.getSliderOffsetPercent() * this.options.range / 100;
},
getScrollBarSize: function() {
return this.scrollBar[this.invertedDimenionsProperty];
},
getScrollValue: function() {
return this.scrollValue || 0;
},
scrollOnePage: function(direction) {
this.scrollTo(this.scrollValue + direction*this.barSize);
},
scrollTo: function(x) {
this.scrollValue = x < 0 ? 0 : x > this.options.range ? this.options.range : x;
this.sliderOffset = this.getSliderOffsetByScrollValue();
this.refreshSlider();
this.moveScroll();
},
onSliderPressed: function(e){
jcf.lib.event.add(document.body, jcf.eventRelease, this.onSliderRelease, this);
jcf.lib.event.add(document.body, jcf.eventMove, this.onSliderMove, this);
jcf.lib.disableTextSelection(this.slider);
// calculate offsets once
this.sliderInnerOffset = (e.changedTouches ? e.changedTouches[0] : e)[this.eventPageOffsetProperty] - jcf.lib.getOffset(this.slider)[this.positionProperty];
this.startScroll();
return false;
},
onSliderRelease: function(){
jcf.lib.event.remove(document.body, jcf.eventRelease, this.onSliderRelease);
jcf.lib.event.remove(document.body, jcf.eventMove, this.onSliderMove);
},
onSliderMove: function(e) {
e.preventDefault();
this.sliderOffset = (e.changedTouches ? e.changedTouches[0] : e)[this.eventPageOffsetProperty] - this.scrollBarOffset - this.sliderInnerOffset;
if(this.sliderOffset < 0) {
this.sliderOffset = 0;
} else if(this.sliderOffset + this.sliderSize > this.trackSize) {
this.sliderOffset = this.trackSize - this.sliderSize;
}
if(this.previousOffset != this.sliderOffset) {
this.previousOffset = this.sliderOffset;
this.scrollTo(this.getScrollValueBySliderOffset());
}
},
onBtnIncPressed: function() {
jcf.lib.event.add(document.body, jcf.eventRelease, this.onBtnIncRelease, this);
jcf.lib.disableTextSelection(this.btnInc);
this.startButtonMoveScroll(1);
return false;
},
onBtnIncRelease: function() {
jcf.lib.event.remove(document.body, jcf.eventRelease, this.onBtnIncRelease);
this.endButtonMoveScroll();
},
onBtnDecPressed: function() {
jcf.lib.event.add(document.body, jcf.eventRelease, this.onBtnDecRelease, this);
jcf.lib.disableTextSelection(this.btnDec);
this.startButtonMoveScroll(-1);
return false;
},
onBtnDecRelease: function() {
jcf.lib.event.remove(document.body, jcf.eventRelease, this.onBtnDecRelease);
this.endButtonMoveScroll();
},
onTrackPressed: function(e) {
var position = e[this.eventPageOffsetProperty] - jcf.lib.getOffset(this.track)[this.positionProperty];
var direction = position < this.sliderOffset ? -1 : position > this.sliderOffset + this.sliderSize ? 1 : 0;
if(direction) {
this.scrollOnePage(direction);
}
}
});
// DOM ready handler
function bindReady(handler){
var called = false;
var ready = function() {
if (called) return;
called = true;
handler();
};
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', ready, false);
} else if (document.attachEvent) {
if (document.documentElement.doScroll && window == window.top) {
var tryScroll = function(){
if (called) return;
if (!document.body) return;
try {
document.documentElement.doScroll('left');
ready();
} catch(e) {
setTimeout(tryScroll, 0);
}
};
tryScroll();
}
document.attachEvent('onreadystatechange', function(){
if (document.readyState === 'complete') {
ready();
}
});
}
if (window.addEventListener) window.addEventListener('load', ready, false);
else if (window.attachEvent) window.attachEvent('onload', ready);
}