1/* 2 * JavaScript Custom Forms Module 3 */ 4jcf = { 5 // global options 6 modules: {}, 7 plugins: {}, 8 baseOptions: { 9 unselectableClass:'jcf-unselectable', 10 labelActiveClass:'jcf-label-active', 11 labelDisabledClass:'jcf-label-disabled', 12 classPrefix: 'jcf-class-', 13 hiddenClass:'jcf-hidden', 14 focusClass:'jcf-focus', 15 wrapperTag: 'div' 16 }, 17 // replacer function 18 customForms: { 19 setOptions: function(obj) { 20 for(var p in obj) { 21 if(obj.hasOwnProperty(p) && typeof obj[p] === 'object') { 22 jcf.lib.extend(jcf.modules[p].prototype.defaultOptions, obj[p]); 23 } 24 } 25 }, 26 replaceAll: function() { 27 for(var k in jcf.modules) { 28 var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector); 29 for(var i = 0; i<els.length; i++) { 30 if(els[i].jcf) { 31 // refresh form element state 32 els[i].jcf.refreshState(); 33 } else { 34 // replace form element 35 if(!jcf.lib.hasClass(els[i], 'default') && jcf.modules[k].prototype.checkElement(els[i])) { 36 new jcf.modules[k]({ 37 replaces:els[i] 38 }); 39 } 40 } 41 } 42 } 43 }, 44 refreshAll: function() { 45 for(var k in jcf.modules) { 46 var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector); 47 for(var i = 0; i<els.length; i++) { 48 if(els[i].jcf) { 49 // refresh form element state 50 els[i].jcf.refreshState(); 51 } 52 } 53 } 54 }, 55 refreshElement: function(obj) { 56 if(obj && obj.jcf) { 57 obj.jcf.refreshState(); 58 } 59 }, 60 destroyAll: function() { 61 for(var k in jcf.modules) { 62 var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector); 63 for(var i = 0; i<els.length; i++) { 64 if(els[i].jcf) { 65 els[i].jcf.destroy(); 66 } 67 } 68 } 69 } 70 }, 71 // detect device type 72 isTouchDevice: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch, 73 isWinPhoneDevice: navigator.msPointerEnabled && /MSIE 10.*Touch/.test(navigator.userAgent), 74 // define base module 75 setBaseModule: function(obj) { 76 jcf.customControl = function(opt){ 77 this.options = jcf.lib.extend({}, jcf.baseOptions, this.defaultOptions, opt); 78 this.init(); 79 }; 80 for(var p in obj) { 81 jcf.customControl.prototype[p] = obj[p]; 82 } 83 }, 84 // add module to jcf.modules 85 addModule: function(obj) { 86 if(obj.name){ 87 // create new module proto class 88 jcf.modules[obj.name] = function(){ 89 jcf.modules[obj.name].superclass.constructor.apply(this, arguments); 90 }; 91 jcf.lib.inherit(jcf.modules[obj.name], jcf.customControl); 92 for(var p in obj) { 93 jcf.modules[obj.name].prototype[p] = obj[p]; 94 } 95 // on create module 96 jcf.modules[obj.name].prototype.onCreateModule(); 97 // make callback for exciting modules 98 for(var mod in jcf.modules) { 99 if(jcf.modules[mod] != jcf.modules[obj.name]) { 100 jcf.modules[mod].prototype.onModuleAdded(jcf.modules[obj.name]); 101 } 102 } 103 } 104 }, 105 // add plugin to jcf.plugins 106 addPlugin: function(obj) { 107 if(obj && obj.name) { 108 jcf.plugins[obj.name] = function() { 109 this.init.apply(this, arguments); 110 }; 111 for(var p in obj) { 112 jcf.plugins[obj.name].prototype[p] = obj[p]; 113 } 114 } 115 }, 116 // miscellaneous init 117 init: function(){ 118 if(navigator.msPointerEnabled) { 119 this.eventPress = 'MSPointerDown'; 120 this.eventMove = 'MSPointerMove'; 121 this.eventRelease = 'MSPointerUp'; 122 } else { 123 this.eventPress = this.isTouchDevice ? 'touchstart' : 'mousedown'; 124 this.eventMove = this.isTouchDevice ? 'touchmove' : 'mousemove'; 125 this.eventRelease = this.isTouchDevice ? 'touchend' : 'mouseup'; 126 } 127 128 // init jcf styles 129 setTimeout(function(){ 130 jcf.lib.domReady(function(){ 131 jcf.initStyles(); 132 }); 133 },1); 134 return this; 135 }, 136 initStyles: function() { 137 // create <style> element and rules 138 var head = document.getElementsByTagName('head')[0], 139 style = document.createElement('style'), 140 rules = document.createTextNode('.'+jcf.baseOptions.unselectableClass+'{'+ 141 '-moz-user-select:none;'+ 142 '-webkit-tap-highlight-color:rgba(255,255,255,0);'+ 143 '-webkit-user-select:none;'+ 144 'user-select:none;'+ 145 '}'); 146 147 // append style element 148 style.type = 'text/css'; 149 if(style.styleSheet) { 150 style.styleSheet.cssText = rules.nodeValue; 151 } else { 152 style.appendChild(rules); 153 } 154 head.appendChild(style); 155 } 156}.init(); 157 158/* 159 * Custom Form Control prototype 160 */ 161jcf.setBaseModule({ 162 init: function(){ 163 if(this.options.replaces) { 164 this.realElement = this.options.replaces; 165 this.realElement.jcf = this; 166 this.replaceObject(); 167 } 168 }, 169 defaultOptions: { 170 // default module options (will be merged with base options) 171 }, 172 checkElement: function(el){ 173 return true;
173 // additional check for correct form element 174 }, 175 replaceObject: function(){ 176 this.createWrapper(); 177 this.attachEvents(); 178 this.fixStyles(); 179 this.setupWrapper(); 180 }, 181 createWrapper: function(){ 182 this.fakeElement = jcf.lib.createElement(this.options.wrapperTag); 183 this.labelFor = jcf.lib.getLabelFor(this.realElement); 184 jcf.lib.disableTextSelection(this.fakeElement); 185 jcf.lib.addClass(this.fakeElement, jcf.lib.getAllClasses(this.realElement.className, this.options.classPrefix)); 186 jcf.lib.addClass(this.realElement, jcf.baseOptions.hiddenClass); 187 }, 188 attachEvents: function(){ 189 jcf.lib.event.add(this.realElement, 'focus', this.onFocusHandler, this); 190 jcf.lib.event.add(this.realElement, 'blur', this.onBlurHandler, this); 191 jcf.lib.event.add(this.fakeElement, 'click', this.onFakeClick, this); 192 jcf.lib.event.add(this.fakeElement, jcf.eventPress, this.onFakePressed, this); 193 jcf.lib.event.add(this.fakeElement, jcf.eventRelease, this.onFakeReleased, this); 194 195 if(this.labelFor) { 196 this.labelFor.jcf = this; 197 jcf.lib.event.add(this.labelFor, 'click', this.onFakeClick, this); 198 jcf.lib.event.add(this.labelFor, jcf.eventPress, this.onFakePressed, this); 199 jcf.lib.event.add(this.labelFor, jcf.eventRelease, this.onFakeReleased, this); 200 } 201 }, 202 fixStyles: function() { 203 // hide mobile webkit tap effect 204 if(jcf.isTouchDevice) { 205 var tapStyle = 'rgba(255,255,255,0)'; 206 this.realElement.style.webkitTapHighlightColor = tapStyle; 207 this.fakeElement.style.webkitTapHighlightColor = tapStyle; 208 if(this.labelFor) { 209 this.labelFor.style.webkitTapHighlightColor = tapStyle; 210 } 211 } 212 }, 213 setupWrapper: function(){ 214 // implement in subclass 215 }, 216 refreshState: function(){ 217 // implement in subclass 218 }, 219 destroy: function() { 220 if(this.fakeElement && this.fakeElement.parentNode) { 221 this.fakeElement.parentNode.removeChild(this.fakeElement); 222 } 223 jcf.lib.removeClass(this.realElement, jcf.baseOptions.hiddenClass); 224 this.realElement.jcf = null; 225 }, 226 onFocus: function(){ 227 // emulated focus event 228 jcf.lib.addClass(this.fakeElement,this.options.focusClass); 229 }, 230 onBlur: function(cb){ 231 // emulated blur event 232 jcf.lib.removeClass(this.fakeElement,this.options.focusClass); 233 }, 234 onFocusHandler: function() { 235 // handle focus loses 236 if(this.focused) return; 237 this.focused = true; 238 239 // handle touch devices also 240 if(jcf.isTouchDevice) { 241 if(jcf.focusedInstance && jcf.focusedInstance.realElement != this.realElement) { 242 jcf.focusedInstance.onBlur(); 243 jcf.focusedInstance.realElement.blur(); 244 } 245 jcf.focusedInstance = this; 246 } 247 this.onFocus.apply(this, arguments); 248 }, 249 onBlurHandler: function() { 250 // handle focus loses 251 if(!this.pressedFlag) { 252 this.focused = false; 253 this.onBlur.apply(this, arguments); 254 } 255 }, 256 onFakeClick: function(){ 257 if(jcf.isTouchDevice) { 258 this.onFocus(); 259 } else if(!this.realElement.disabled) { 260 this.realElement.focus(); 261 } 262 }, 263 onFakePressed: function(e){ 264 this.pressedFlag = true; 265 }, 266 onFakeReleased: function(){ 267 this.pressedFlag = false; 268 }, 269 onCreateModule: function(){ 270 // implement in subclass 271 }, 272 onModuleAdded: function(module) { 273 // implement in subclass 274 }, 275 onControlReady: function() { 276 // implement in subclass 277 } 278}); 279 280/* 281 * JCF Utility Library 282 */ 283jcf.lib = { 284 bind: function(func, scope){ 285 return function() { 286 return func.apply(scope, arguments); 287 } 288 }, 289 browser: (function() { 290 var ua = navigator.userAgent.toLowerCase(), res = {}, 291 match = /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version)?[ \/]([\w.]+)/.exec(ua) || 292 /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+))?/.exec(ua) || []; 293 res[match[1]] = true; 294 res.version = match[2] || "0"; 295 res.safariMac = ua.indexOf('mac') != -1 && ua.indexOf('safari') != -1; 296 return res; 297 })(), 298 getOffset: function (obj) { 299 if (obj.getBoundingClientRect && !navigator.msPointerEnabled) { 300 var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; 301 var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; 302 var clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0; 303 var clientTop = document.documentElement.clientTop || document.body.clientTop || 0; 304 return { 305 top:Math.round(obj.getBoundingClientRect().top + scrollTop - clientTop), 306 left:Math.round(obj.getBoundingClientRect().left + scrollLeft - clientLeft) 307 } 308 } else { 309 var posLeft = 0, posTop = 0; 310 while (obj.offsetParent) {posLeft += obj.offsetLeft; posTop += obj.offsetTop; obj = obj.offsetParent;}
311 return {top:posTop,left:posLeft}; 312 } 313 }, 314 getScrollTop: function() { 315 return window.pageYOffset || document.documentElement.scrollTop; 316 }, 317 getScrollLeft: function() { 318 return window.pageXOffset || document.documentElement.scrollLeft; 319 }, 320 getWindowWidth: function(){ 321 return document.compatMode=='CSS1Compat' ? document.documentElement.clientWidth : document.body.clientWidth; 322 }, 323 getWindowHeight: function(){ 324 return document.compatMode=='CSS1Compat' ? document.documentElement.clientHeight : document.body.clientHeight; 325 }, 326 getStyle: function(el, prop) { 327 if (document.defaultView && document.defaultView.getComputedStyle) { 328 return document.defaultView.getComputedStyle(el, null)[prop]; 329 } else if (el.currentStyle) { 330 return el.currentStyle[prop]; 331 } else { 332 return el.style[prop]; 333 } 334 }, 335 getParent: function(obj, selector) { 336 while(obj.parentNode && obj.parentNode != document.body) { 337 if(obj.parentNode.tagName.toLowerCase() == selector.toLowerCase()) { 338 return obj.parentNode; 339 } 340 obj = obj.parentNode; 341 } 342 return false; 343 }, 344 isParent: function(child, parent) { 345 while(child.parentNode) { 346 if(child.parentNode === parent) { 347 return true; 348 } 349 child = child.parentNode; 350 } 351 return false; 352 }, 353 getLabelFor: function(object) { 354 var parentLabel = jcf.lib.getParent(object,'label'); 355 if(parentLabel) { 356 return parentLabel; 357 } else if(object.id) { 358 return jcf.lib.queryBySelector('label[for="' + object.id + '"]')[0]; 359 } 360 }, 361 disableTextSelection: function(el){ 362 if (typeof el.onselectstart !== 'undefined') { 363 el.onselectstart = function() {return false}; 364 } else if(window.opera) { 365 el.setAttribute('unselectable', 'on'); 366 } else { 367 jcf.lib.addClass(el, jcf.baseOptions.unselectableClass); 368 } 369 }, 370 enableTextSelection: function(el) { 371 if (typeof el.onselectstart !== 'undefined') { 372 el.onselectstart = null; 373 } else if(window.opera) { 374 el.removeAttribute('unselectable'); 375 } else { 376 jcf.lib.removeClass(el, jcf.baseOptions.unselectableClass); 377 } 378 }, 379 queryBySelector: function(selector, scope){ 380 return this.getElementsBySelector(selector, scope); 381 }, 382 prevSibling: function(node) { 383 while(node = node.previousSibling) if(node.nodeType == 1) break; 384 return node; 385 }, 386 nextSibling: function(node) { 387 while(node = node.nextSibling) if(node.nodeType == 1) break; 388 return node; 389 }, 390 fireEvent: function(element,event) { 391 if(element.dispatchEvent){ 392 var evt = document.createEvent('HTMLEvents'); 393 evt.initEvent(event, true, true ); 394 return !element.dispatchEvent(evt); 395 }else if(document.createEventObject){ 396 var evt = document.createEventObject(); 397 return element.fireEvent('on'+event,evt); 398 } 399 }, 400 isParent: function(p, c) { 401 while(c.parentNode) { 402 if(p == c) { 403 return true; 404 } 405 c = c.parentNode; 406 } 407 return false; 408 }, 409 inherit: function(Child, Parent) { 410 var F = function() { } 411 F.prototype = Parent.prototype 412 Child.prototype = new F() 413 Child.prototype.constructor = Child 414 Child.superclass = Parent.prototype 415 }, 416 extend: function(obj) { 417 for(var i = 1; i < arguments.length; i++) { 418 for(var p in arguments[i]) { 419 if(arguments[i].hasOwnProperty(p)) { 420 obj[p] = arguments[i][p]; 421 } 422 } 423 } 424 return obj; 425 }, 426 hasClass: function (obj,cname) { 427 return (obj.className ? obj.className.match(new RegExp('(\\s|^)'+cname+'(\\s|$)')) : false); 428 }, 429 addClass: function (obj,cname) { 430 if (!this.hasClass(obj,cname)) obj.className += (!obj.className.length || obj.className.charAt(obj.className.length - 1) === ' ' ? '' : ' ') + cname; 431 }, 432 removeClass: function (obj,cname) { 433 if (this.hasClass(obj,cname)) obj.className=obj.className.replace(new RegExp('(\\s|^)'+cname+'(\\s|$)'),' ').replace(/\s+$/, ''); 434 }, 435 toggleClass: function(obj, cname, condition) { 436 if(condition) this.addClass(obj, cname); else this.removeClass(obj, cname); 437 }, 438 createElement: function(tagName, options) { 439 var el = document.createElement(tagName); 440 for(var p in options) { 441 if(options.hasOwnProperty(p)) { 442 switch (p) { 443 case 'class': el.className = options[p]; break; 444 case 'html': el.innerHTML = options[p]; break; 445 case 'style': this.setStyles(el, options[p]); break; 446 default: el.setAttribute(p, options[p]); 447 } 448 } 449 } 450 return el; 451 }, 452 setStyles: function(el, styles) { 453 for(var p in styles) { 454 if(styles.hasOwnProperty(p)) { 455 switch (p) { 456 case 'float': el.style.cssFloat = styles[p]; break; 457 case 'opacity': el.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity='+styles[p]*100+')'; el.style.opacity = styles[p]; break; 458 default: el.style[p] = (typeof styles[p] === 'undefined' ? 0 : styles[p]) + (typeof styles[p] === 'number' ? 'px' : ''); 459 } 460 } 461 } 462 return el; 463 }, 464 getInnerWidth: function(el) { 465 return el.offsetWidth - (parseInt(this.getStyle(el,'paddingLeft')) || 0) - (parseInt(this.getStyle(el,'paddingRight')) || 0); 466 }, 467 getInnerHeight: function(el) { 468 return el.offsetHeight - (parseInt(this.getStyle(el,'paddingTop')) || 0) - (parseInt(this.getStyle(el,'paddingBottom')) || 0); 469 }, 470 getAllClasses: function(cname, prefix, skip) { 471 if(!skip) skip = ''; 472 if(!prefix) prefix = ''; 473 return cname ? cname.replace(new RegExp('(\\s|^)'+skip+'(\\s|$)'),' ').replace(/[\s]*([\S]+)+[\s]*/gi,prefix+"$1 ") : ''; 474 }, 475 getElementsBySelector: function(selector, scope) { 476 if(typeof document.querySelectorAll === 'function') { 477 return (scope || document).querySelectorAll(selector); 478 } 479 var selectors = selector.split(','); 480 var resultList = []; 481 for(var s = 0; s < selectors.length; s++) { 482 var currentContext = [scope || document]; 483 var tokens = selectors[s].replace(/^\s+/,'').replace(/\s+$/,'').split(' '); 484 for (var i = 0; i < tokens.length; i++) { 485 token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,''); 486 if (token.indexOf('#') > -1) { 487 var bits = token.split('#'), tagName = bits[0], id = bits[1]; 488 var element = document.getElementById(id); 489 if (tagName && element.nodeName.toLowerCase() != tagName) { 490 return []; 491 } 492 currentContext = [element]; 493 continue; 494 } 495 if (token.indexOf('.') > -1) { 496 var bits = token.split('.'), tagName = bits[0] || '*', className = bits[1], found = [], foundCount = 0; 497 for (var h = 0; h < currentContext.length; h++) { 498 var elements; 499 if (tagName == '*') { 500 elements = currentContext[h].getElementsByTagName('*'); 501 } else { 502 elements = currentContext[h].getElementsByTagName(tagName); 503 } 504 for (var j = 0; j < elements.length; j++) { 505 found[foundCount++] = elements[j]; 506 } 507 } 508 currentContext = []; 509 var currentContextIndex = 0; 510 for (var k = 0; k < found.length; k++) { 511 if (found[k].className && found[k].className.match(new RegExp('(\\s|^)'+className+'(\\s|$)'))) { 512 currentContext[currentContextIndex++] = found[k]; 513 } 514 } 515 continue; 516 } 517 if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) { 518 var tagName = RegExp.$1 || '*', attrName = RegExp.$2, attrOperator = RegExp.$3, attrValue = RegExp.$4; 519 if(attrName.toLowerCase() == 'for' && this.browser.msie && this.browser.version < 8) { 520 attrName = 'htmlFor'; 521 } 522 var found = [], foundCount = 0; 523 for (var h = 0; h < currentContext.length; h++) { 524 var elements; 525 if (tagName == '*') { 526 elements = currentContext[h].getElementsByTagName('*'); 527 } else { 528 elements = currentContext[h].getElementsByTagName(tagName); 529 } 530 for (var j = 0; elements[j]; j++) { 531 found[foundCount++] = elements[j]; 532 } 533 } 534 currentContext = []; 535 var currentContextIndex = 0, checkFunction; 536 switch (attrOperator) { 537 case '=': checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue) }; break; 538 case '~': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('(\\s|^)'+attrValue+'(\\s|$)'))) }; break; 539 case '|': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))) }; break; 540 case '^': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0) }; break; 541 case '$': checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length) }; break; 542 case '*': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1) }; break; 543 default : checkFunction = function(e) { return e.getAttribute(attrName) }; 544 } 545 currentContext = []; 546 var currentContextIndex = 0; 547 for (var k = 0; k < found.length; k++) { 548 if (checkFunction(found[k])) { 549 currentContext[currentContextIndex++] = found[k]; 550 } 551 } 552 continue; 553 } 554 tagName = token; 555 var found = [], foundCount = 0; 556 for (var h = 0; h < currentContext.length; h++) { 557 var elements = currentContext[h].getElementsByTagName(tagName); 558 for (var j = 0; j < elements.length; j++) { 559 found[foundCount++] = elements[j]; 560 } 561 } 562 currentContext = found; 563 } 564 resultList = [].concat(resultList,currentContext); 565 } 566 return resultList; 567 }, 568 scrollSize: (function(){ 569 var content, hold, sizeBefore, sizeAfter; 570 function buildSizer(){ 571 if(hold) removeSizer(); 572 content = document.createElement('div'); 573 hold = document.createElement('div'); 574 hold.style.cssText = 'position:absolute;overflow:hidden;width:100px;height:100px'; 575 hold.appendChild(content); 576 document.body.appendChild(hold); 577 } 578 function removeSizer(){ 579 document.body.removeChild(hold); 580 hold = null; 581 } 582 function calcSize(vertical) { 583 buildSizer(); 584 content.style.cssText = 'height:'+(vertical ? '100%' : '200px'); 585 sizeBefore = (vertical ? content.offsetHeight : content.offsetWidth);
586 hold.style.overflow = 'scroll'; content.innerHTML = 1; 587 sizeAfter = (vertical ? content.offsetHeight : content.offsetWidth); 588 if(vertical && hold.clientHeight) sizeAfter = hold.clientHeight; 589 removeSizer(); 590 return sizeBefore - sizeAfter; 591 } 592 return { 593 getWidth:function(){ 594 return calcSize(false); 595 }, 596 getHeight:function(){ 597 return calcSize(true) 598 } 599 } 600 }()), 601 domReady: function (handler){ 602 var called = false 603 function ready() { 604 if (called) return; 605 called = true; 606 handler(); 607 } 608 if (document.addEventListener) { 609 document.addEventListener("DOMContentLoaded", ready, false); 610 } else if (document.attachEvent) { 611 if (document.documentElement.doScroll && window == window.top) { 612 function tryScroll(){ 613 if (called) return 614 if (!document.body) return 615 try { 616 document.documentElement.doScroll("left") 617 ready() 618 } catch(e) { 619 setTimeout(tryScroll, 0) 620 } 621 } 622 tryScroll() 623 } 624 document.attachEvent("onreadystatechange", function(){ 625 if (document.readyState === "complete") { 626 ready() 627 } 628 }) 629 } 630 if (window.addEventListener) window.addEventListener('load', ready, false) 631 else if (window.attachEvent) window.attachEvent('onload', ready) 632 }, 633 event: (function(){ 634 var guid = 0; 635 function fixEvent(e) { 636 e = e || window.event; 637 if (e.isFixed) { 638 return e; 639 } 640 e.isFixed = true; 641 e.preventDefault = e.preventDefault || function(){this.returnValue = false} 642 e.stopPropagation = e.stopPropagaton || function(){this.cancelBubble = true} 643 if (!e.target) { 644 e.target = e.srcElement 645 } 646 if (!e.relatedTarget && e.fromElement) { 647 e.relatedTarget = e.fromElement == e.target ? e.toElement : e.fromElement; 648 } 649 if (e.pageX == null && e.clientX != null) { 650 var html = document.documentElement, body = document.body; 651 e.pageX = e.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0); 652 e.pageY = e.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0); 653 } 654 if (!e.which && e.button) { 655 e.which = e.button & 1 ? 1 : (e.button & 2 ? 3 : (e.button & 4 ? 2 : 0)); 656 } 657 if(e.type === "DOMMouseScroll" || e.type === 'mousewheel') { 658 e.mWheelDelta = 0; 659 if (e.wheelDelta) { 660 e.mWheelDelta = e.wheelDelta/120; 661 } else if (e.detail) { 662 e.mWheelDelta = -e.detail/3; 663 } 664 } 665 return e; 666 } 667 function commonHandle(event, customScope) { 668 event = fixEvent(event); 669 var handlers = this.events[event.type]; 670 for (var g in handlers) { 671 var handler = handlers[g]; 672 var ret = handler.call(customScope || this, event); 673 if (ret === false) { 674 event.preventDefault() 675 event.stopPropagation() 676 } 677 } 678 } 679 var publicAPI = { 680 add: function(elem, type, handler, forcedScope) { 681 if (elem.setInterval && (elem != window && !elem.frameElement)) { 682 elem = window; 683 } 684 if (!handler.guid) { 685 handler.guid = ++guid; 686 } 687 if (!elem.events) { 688 elem.events = {}; 689 elem.handle = function(event) { 690 return commonHandle.call(elem, event); 691 } 692 } 693 if (!elem.events[type]) { 694 elem.events[type] = {}; 695 if (elem.addEventListener) elem.addEventListener(type, elem.handle, false); 696 else if (elem.attachEvent) elem.attachEvent("on" + type, elem.handle); 697 if(type === 'mousewheel') { 698 publicAPI.add(elem, 'DOMMouseScroll', handler, forcedScope); 699 } 700 } 701 var fakeHandler = jcf.lib.bind(handler, forcedScope); 702 fakeHandler.guid = handler.guid; 703 elem.events[type][handler.guid] = forcedScope ? fakeHandler : handler; 704 }, 705 remove: function(elem, type, handler) { 706 var handlers = elem.events && elem.events[type]; 707 if (!handlers) return; 708 delete handlers[handler.guid]; 709 for(var any in handlers) return; 710 if (elem.removeEventListener) elem.removeEventListener(type, elem.handle, false); 711 else if (elem.detachEvent) elem.detachEvent("on" + type, elem.handle); 712 delete elem.events[type]; 713 for (var any in elem.events) return; 714 try { 715 delete elem.handle; 716 delete elem.events; 717 } catch(e) { 718 if(elem.removeAttribute) { 719 elem.removeAttribute("handle"); 720 elem.removeAttribute("events"); 721 } 722 } 723 if(type === 'mousewheel') { 724 publicAPI.remove(elem, 'DOMMouseScroll', handler); 725 } 726 } 727 } 728 return publicAPI; 729 }()) 730} 731 732// custom select module 733jcf.addModule({ 734 name:'select', 735 selector:'select', 736 defaultOptions: { 737 useNativeDropOnMobileDevices: true, 738 hideDropOnScroll: true, 739 showNativeDrop: false, 740 handleDropPosition: true, 741 selectDropPosition: 'bottom', // or 'top' 742 wrapperClass:'select-area', 743 focusClass:'select-focus', 744 dropActiveClass:'select-active', 745 selectedClass:'item-selected', 746 currentSelectedClass:'current-selected', 747 disabledClass:'select-disabled', 748 valueSelector:'span.center', 749 optGroupClass:'optgroup', 750 openerSelector:'a.select-opener', 751 selectStructure:'<span class="left"></span><span class="center"></span><a class="select-opener"></a>', 752 wrapperTag: 'span', 753 classPrefix:'select-', 754 dropMaxHeight: 200, 755 dropFlippedClass: 'select-options-flipped', 756 dropHiddenClass:'options-hidden', 757 dropScrollableClass:'options-overflow', 758 dropClass:'select-options', 759 dropClassPrefix:'drop-', 760 dropStructure:'<div class="drop-holder"><div class="drop-list"></div></div>', 761 dropSelector:'div.drop-list' 762 }, 763 checkElement: function(el){ 764 return (!el.size && !el.multiple); 765 }, 766 setupWrapper: function(){ 767 jcf.lib.addClass(this.fakeElement, this.options.wrapperClass); 768 this.realElement.parentNode.insertBefore(this.fakeElement, this.realElement); 769 this.fakeElement.innerHTML = this.options.selectStructure; 770 this.fakeElement.style.width = (this.realElement.offsetWidth > 0 ? this.realElement.offsetWidth + 'px' : 'auto'); 771 772 // show native drop if specified in options 773 if(jcf.baseOptions.useNativeDropOnMobileDevices && (jcf.isTouchDevice || jcf.isWinPhoneDevice)) { 774 this.options.showNativeDrop = true; 775 } 776 if(this.options.showNativeDrop) { 777 this.fakeElement.appendChild(this.realElement); 778 jcf.lib.removeClass(this.realElement, this.options.hiddenClass); 779 jcf.lib.setStyles(this.realElement, { 780 top:0, 781 left:0, 782 margin:0, 783 padding:0, 784 opacity:0, 785 border:'none', 786 position:'absolute', 787 width: jcf.lib.getInnerWidth(this.fakeElement) - 1, 788 height: jcf.lib.getInnerHeight(this.fakeElement) - 1 789 }); 790 jcf.lib.event.add(this.realElement, 'touchstart', function(){ 791 this.realElement.title = ''; 792 }, this) 793 } 794 795 // create select body 796 this.opener = jcf.lib.queryBySelector(this.options.openerSelector, this.fakeElement)[0]; 797 this.valueText = jcf.lib.queryBySelector(this.options.valueSelector, this.fakeElement)[0]; 798 jcf.lib.disableTextSelection(this.valueText); 799 this.opener.jcf = this; 800 801 if(!this.options.showNativeDrop) { 802 this.createDropdown(); 803 this.refreshState(); 804 this.onControlReady(this); 805 this.hideDropdown(true); 806 } else { 807 this.refreshState(); 808 } 809 this.addEvents(); 810 }, 811 addEvents: function(){ 812 if(this.options.showNativeDrop) { 813 jcf.lib.event.add(this.realElement, 'click', this.onChange, this); 814 } else { 815 jcf.lib.event.add(this.fakeElement, 'click', this.toggleDropdown, this); 816 } 817 jcf.lib.event.add(this.realElement, 'change', this.onChange, this); 818 }, 819 onFakeClick: function() { 820 // do nothing (drop toggles by toggleDropdown method) 821 }, 822 onFocus: function(){ 823 jcf.modules[this.name].superclass.onFocus.apply(this, arguments); 824 if(!this.options.showNativeDrop) { 825 // Mac Safari Fix 826 if(jcf.lib.browser.safariMac) { 827 this.realElement.setAttribute('size','2'); 828 } 829 jcf.lib.event.add(this.realElement, 'keydown', this.onKeyDown, this); 830 if(jcf.activeControl && jcf.activeControl != this) { 831 jcf.activeControl.hideDropdown(); 832 jcf.activeControl = this; 833 } 834 } 835 }, 836 onBlur: function(){ 837 if(!this.options.showNativeDrop) { 838 // Mac Safari Fix 839 if(jcf.lib.browser.safariMac) { 840 this.realElement.removeAttribute('size'); 841 } 842 if(!this.isActiveDrop() || !this.isOverDrop()) { 843 jcf.modules[this.name].superclass.onBlur.apply(this); 844 if(jcf.activeControl === this) jcf.activeControl = null; 845 if(!jcf.isTouchDevice) { 846 this.hideDropdown(); 847 } 848 } 849 jcf.lib.event.remove(this.realElement, 'keydown', this.onKeyDown); 850 } else { 851 jcf.modules[this.name].superclass.onBlur.apply(this); 852 } 853 }, 854 onChange: function() { 855 this.refreshState(); 856 }, 857 onKeyDown: function(e){
858 this.dropOpened = true; 859 jcf.tmpFlag = true; 860 setTimeout(function(){jcf.tmpFlag = false},100); 861 var context = this; 862 context.keyboardFix = true; 863 setTimeout(function(){ 864 context.refreshState(); 865 },10); 866 if(e.keyCode == 13) { 867 context.toggleDropdown.apply(context); 868 return false; 869 } 870 }, 871 onResizeWindow: function(e){ 872 if(this.isActiveDrop()) { 873 this.hideDropdown(); 874 } 875 }, 876 onScrollWindow: function(e){ 877 if(this.options.hideDropOnScroll) { 878 this.hideDropdown(); 879 } else if(this.isActiveDrop()) { 880 this.positionDropdown(); 881 } 882 }, 883 onOptionClick: function(e){ 884 var opener = e.target && e.target.tagName && e.target.tagName.toLowerCase() == 'li' ? e.target : jcf.lib.getParent(e.target, 'li'); 885 if(opener) { 886 this.dropOpened = true; 887 this.realElement.selectedIndex = parseInt(opener.getAttribute('rel')); 888 if(jcf.isTouchDevice) { 889 this.onFocus(); 890 } else { 891 this.realElement.focus(); 892 } 893 this.refreshState(); 894 895 this.hideDropdown(); 896 jcf.lib.fireEvent(this.realElement, 'change'); 897 $(e.target).attr('href', $(this.realElement).val()); 898 } 899 return false; 900 }, 901 onClickOutside: function(e){ 902 if(jcf.tmpFlag) { 903 jcf.tmpFlag = false; 904 return; 905 } 906 if(!jcf.lib.isParent(this.fakeElement, e.target) && !jcf.lib.isParent(this.selectDrop, e.target)) { 907 this.hideDropdown(); 908 } 909 }, 910 onDropHover: function(e){ 911 if(!this.keyboardFix) { 912 this.hoverFlag = true; 913 var opener = e.target && e.target.tagName && e.target.tagName.toLowerCase() == 'li' ? e.target : jcf.lib.getParent(e.target, 'li'); 914 if(opener) { 915 this.realElement.selectedIndex = parseInt(opener.getAttribute('rel')); 916 this.refreshSelectedClass(parseInt(opener.getAttribute('rel'))); 917 } 918 } else { 919 this.keyboardFix = false; 920 } 921 }, 922 onDropLeave: function(){ 923 this.hoverFlag = false; 924 }, 925 isActiveDrop: function(){ 926 return !jcf.lib.hasClass(this.selectDrop, this.options.dropHiddenClass); 927 }, 928 isOverDrop: function(){ 929 return this.hoverFlag; 930 }, 931 createDropdown: function(){ 932 // remove old dropdown if exists 933 if(this.selectDrop) { 934 this.selectDrop.parentNode.removeChild(this.selectDrop); 935 } 936 937 // create dropdown holder 938 this.selectDrop = document.createElement('div'); 939 this.selectDrop.className = this.options.dropClass; 940 this.selectDrop.innerHTML = this.options.dropStructure; 941 jcf.lib.setStyles(this.selectDrop, {position:'absolute'}); 942 this.selectList = jcf.lib.queryBySelector(this.options.dropSelector,this.selectDrop)[0]; 943 jcf.lib.addClass(this.selectDrop, this.options.dropHiddenClass); 944 document.body.appendChild(this.selectDrop); 945 this.selectDrop.jcf = this; 946 jcf.lib.event.add(this.selectDrop, 'click', this.onOptionClick, this); 947 jcf.lib.event.add(this.selectDrop, 'mouseover', this.onDropHover, this); 948 jcf.lib.event.add(this.selectDrop, 'mouseout', this.onDropLeave, this); 949 this.buildDropdown(); 950 }, 951 buildDropdown: function() { 952 // build select options / optgroups 953 this.buildDropdownOptions(); 954 955 // position and resize dropdown 956 this.positionDropdown(); 957 958 // cut dropdown if height exceedes 959 this.buildDropdownScroll(); 960 }, 961 buildDropdownOptions: function() { 962 this.resStructure = ''; 963 this.optNum = 0; 964 for(var i = 0; i < this.realElement.children.length; i++) { 965 this.resStructure += this.buildElement(this.realElement.children[i], i) +'\n'; 966 } 967 this.selectList.innerHTML = this.resStructure; 968 }, 969 buildDropdownScroll: function() { 970 if(this.options.dropMaxHeight) { 971 if(this.selectDrop.offsetHeight > this.options.dropMaxHeight) { 972 this.selectList.style.height = this.options.dropMaxHeight+'px'; 973 this.selectList.style.overflow = 'auto'; 974 this.selectList.style.overflowX = 'hidden'; 975 jcf.lib.addClass(this.selectDrop, this.options.dropScrollableClass); 976 } 977 } 978 jcf.lib.addClass(this.selectDrop, jcf.lib.getAllClasses(this.realElement.className, this.options.dropClassPrefix, jcf.baseOptions.hiddenClass)); 979 }, 980 parseOptionTitle: function(optTitle) { 981 return (typeof optTitle === 'string' && /\.(jpg|gif|png|bmp|jpeg)(.*)?$/i.test(optTitle)) ? optTitle : ''; 982 }, 983 buildElement: function(obj, index){ 984 // build option 985 var res = '', optImage; 986 if(obj.tagName.toLowerCase() == 'option') { 987 if(!jcf.lib.prevSibling(obj) || jcf.lib.prevSibling(obj).tagName.toLowerCase() != 'option') { 988 res += '<ul>'; 989 } 990 991 optImage = this.parseOptionTitle(obj.title); 992 res += '<li rel="'+(this.optNum++)+'" class="'+(obj.className? obj.className + ' ' : '')+(index % 2 ? 'option-even ' : '')+'jcfcalc"><a href="#">'+(optImage ? '<img src="'+optImage+'" alt="" />
992' : '')+'<span>' + obj.innerHTML + '</span></a></li>'; 993 if(!jcf.lib.nextSibling(obj) || jcf.lib.nextSibling(obj).tagName.toLowerCase() != 'option') { 994 res += '</ul>'; 995 } 996 return res; 997 } 998 // build option group with options 999 else if(obj.tagName.toLowerCase() == 'optgroup' && obj.label) { 1000 res += '<div class="'+this.options.optGroupClass+'">'; 1001 res += '<strong class="jcfcalc"><em>'+(obj.label)+'</em></strong>'; 1002 for(var i = 0; i < obj.children.length; i++) { 1003 res += this.buildElement(obj.children[i], i); 1004 } 1005 res += '</div>'; 1006 return res; 1007 } 1008 }, 1009 positionDropdown: function(){ 1010 var ofs = jcf.lib.getOffset(this.fakeElement), selectAreaHeight = this.fakeElement.offsetHeight, selectDropHeight = this.selectDrop.offsetHeight; 1011 var fitInTop = ofs.top - selectDropHeight >= jcf.lib.getScrollTop() && jcf.lib.getScrollTop() + jcf.lib.getWindowHeight() < ofs.top + selectAreaHeight + selectDropHeight; 1012 1013 1014 if((this.options.handleDropPosition && fitInTop) || this.options.selectDropPosition === 'top') { 1015 this.selectDrop.style.top = (ofs.top - selectDropHeight)+'px'; 1016 jcf.lib.addClass(this.selectDrop, this.options.dropFlippedClass); 1017 } else { 1018 this.selectDrop.style.top = (ofs.top + selectAreaHeight)+'px'; 1019 jcf.lib.removeClass(this.selectDrop, this.options.dropFlippedClass); 1020 } 1021 this.selectDrop.style.left = ofs.left+'px'; 1022 this.selectDrop.style.width = this.fakeElement.offsetWidth+'px'; 1023 }, 1024 showDropdown: function(){ 1025 document.body.appendChild(this.selectDrop); 1026 jcf.lib.removeClass(this.selectDrop, this.options.dropHiddenClass); 1027 jcf.lib.addClass(this.fakeElement,this.options.dropActiveClass); 1028 this.positionDropdown(); 1029 1030 // highlight current active item 1031 var activeItem = this.getFakeActiveOption(); 1032 this.removeClassFromItems(this.options.currentSelectedClass); 1033 jcf.lib.addClass(activeItem, this.options.currentSelectedClass); 1034 1035 // show current dropdown 1036 jcf.lib.event.add(window, 'resize', this.onResizeWindow, this); 1037 jcf.lib.event.add(window, 'scroll', this.onScrollWindow, this); 1038 jcf.lib.event.add(document, jcf.eventPress, this.onClickOutside, this); 1039 this.positionDropdown(); 1040 }, 1041 hideDropdown: function(partial){ 1042 if(this.selectDrop.parentNode) { 1043 if(this.selectDrop.offsetWidth) { 1044 this.selectDrop.parentNode.removeChild(this.selectDrop); 1045 } 1046 if(partial) { 1047 return; 1048 } 1049 } 1050 if(typeof this.origSelectedIndex === 'number') { 1051 this.realElement.selectedIndex = this.origSelectedIndex; 1052 } 1053 jcf.lib.removeClass(this.fakeElement,this.options.dropActiveClass); 1054 jcf.lib.addClass(this.selectDrop, this.options.dropHiddenClass); 1055 jcf.lib.event.remove(window, 'resize', this.onResizeWindow); 1056 jcf.lib.event.remove(window, 'scroll', this.onScrollWindow); 1057 jcf.lib.event.remove(document.documentElement, jcf.eventPress, this.onClickOutside); 1058 if(jcf.isTouchDevice) { 1059 this.onBlur(); 1060 } 1061 }, 1062 toggleDropdown: function(){ 1063 if(!this.realElement.disabled) { 1064 if(jcf.isTouchDevice) { 1065 this.onFocus(); 1066 } else { 1067 this.realElement.focus(); 1068 } 1069 if(this.isActiveDrop()) { 1070 this.hideDropdown(); 1071 } else { 1072 this.showDropdown(); 1073 } 1074 this.refreshState(); 1075 } 1076 }, 1077 scrollToItem: function(){ 1078 if(this.isActiveDrop()) { 1079 var dropHeight = this.selectList.offsetHeight; 1080 var offsetTop = this.calcOptionOffset(this.getFakeActiveOption()); 1081 var sTop = this.selectList.scrollTop; 1082 var oHeight = this.getFakeActiveOption().offsetHeight; 1083 //offsetTop+=sTop; 1084 1085 if(offsetTop >= sTop + dropHeight) { 1086 this.selectList.scrollTop = offsetTop - dropHeight + oHeight; 1087 } else if(offsetTop < sTop) { 1088 this.selectList.scrollTop = offsetTop; 1089 } 1090 } 1091 }, 1092 getFakeActiveOption: function(c) { 1093 return jcf.lib.queryBySelector('li[rel="'+(typeof c === 'number' ? c : this.realElement.selectedIndex) +'"]',this.selectList)[0]; 1094 }, 1095 calcOptionOffset: function(fake) { 1096 var h = 0;
1097 var els = jcf.lib.queryBySelector('.jcfcalc',this.selectList); 1098 for(var i = 0; i < els.length; i++) { 1099 if(els[i] == fake) break; 1100 h+=els[i].offsetHeight; 1101 } 1102 return h; 1103 }, 1104 childrenHasItem: function(hold,item) { 1105 var items = hold.getElementsByTagName('*'); 1106 for(i = 0; i < items.length; i++) { 1107 if(items[i] == item) return true; 1108 } 1109 return false; 1110 }, 1111 removeClassFromItems: function(className){ 1112 var children = jcf.lib.queryBySelector('li',this.selectList); 1113 for(var i = children.length - 1; i >= 0; i--) { 1114 jcf.lib.removeClass(children[i], className); 1115 } 1116 }, 1117 setSelectedClass: function(c){ 1118 jcf.lib.addClass(this.getFakeActiveOption(c), this.options.selectedClass); 1119 }, 1120 refreshSelectedClass: function(c){ 1121 if(!this.options.showNativeDrop) { 1122 this.removeClassFromItems(this.options.selectedClass); 1123 this.setSelectedClass(c); 1124 } 1125 if(this.realElement.disabled) { 1126 jcf.lib.addClass(this.fakeElement, this.options.disabledClass); 1127 if(this.labelFor) { 1128 jcf.lib.addClass(this.labelFor, this.options.labelDisabledClass); 1129 } 1130 } else { 1131 jcf.lib.removeClass(this.fakeElement, this.options.disabledClass); 1132 if(this.labelFor) { 1133 jcf.lib.removeClass(this.labelFor, this.options.labelDisabledClass); 1134 } 1135 } 1136 }, 1137 refreshSelectedText: function() { 1138 if(!this.dropOpened && this.realElement.title) { 1139 this.valueText.innerHTML = this.realElement.title; 1140 } else { 1141 if(this.realElement.options[this.realElement.selectedIndex].title) { 1142 var optImage = this.parseOptionTitle(this.realElement.options[this.realElement.selectedIndex].title); 1143 this.valueText.innerHTML = (optImage ? '<img src="'+optImage+'" alt="" />' : '') + this.realElement.options[this.realElement.selectedIndex].innerHTML; 1144 } else { 1145 this.valueText.innerHTML = this.realElement.options[this.realElement.selectedIndex].innerHTML; 1146 } 1147 } 1148 }, 1149 refreshState: function(){ 1150 this.origSelectedIndex = this.realElement.selectedIndex; 1151 this.refreshSelectedClass(); 1152 this.refreshSelectedText(); 1153 if(!this.options.showNativeDrop) { 1154 this.positionDropdown(); 1155 if(this.selectDrop.offsetWidth) { 1156 this.scrollToItem(); 1157 } 1158 } 1159 } 1160}); 1161 1162 1163 1164// placeholder class 1165;(function(){ 1166 var placeholderCollection = []; 1167 PlaceholderInput = function() { 1168 this.options = { 1169 element:null, 1170 showUntilTyping:false, 1171 wrapWithElement:false, 1172 getParentByClass:false, 1173 showPasswordBullets:false, 1174 placeholderAttr:'value', 1175 inputFocusClass:'focus', 1176 inputActiveClass:'text-active', 1177 parentFocusClass:'parent-focus', 1178 parentActiveClass:'parent-active', 1179 labelFocusClass:'label-focus', 1180 labelActiveClass:'label-active', 1181 fakeElementClass:'input-placeholder-text' 1182 }; 1183 placeholderCollection.push(this); 1184 this.init.apply(this,arguments); 1185 }; 1186 PlaceholderInput.refreshAllInputs = function(except) { 1187 for(var i = 0; i < placeholderCollection.length; i++) { 1188 if(except !== placeholderCollection[i]) { 1189 placeholderCollection[i].refreshState(); 1190 } 1191 } 1192 }; 1193 PlaceholderInput.replaceByOptions = function(opt) { 1194 var inputs = [].concat( 1195 convertToArray(document.querySelectorAll('input.placeholder-input, textarea.placeholder-input')) 1196 ); 1197 for(var i = 0; i < inputs.length; i++) { 1198 if(inputs[i].className.indexOf(opt.skipClass) < 0) { 1199 var inputType = getInputType(inputs[i]); 1200 var placeholderValue = inputs[i].getAttribute('placeholder'); 1201 if(opt.focusOnly || (opt.clearInputs && (inputType === 'text' || inputType === 'email' || placeholderValue)) || 1202 (opt.clearTextareas && inputType === 'textarea') || 1203 (opt.clearPasswords && inputType === 'password') 1204 ) { 1205 new PlaceholderInput({ 1206 element:inputs[i], 1207 focusOnly: opt.focusOnly, 1208 wrapWithElement:opt.wrapWithElement, 1209 showUntilTyping:opt.showUntilTyping, 1210 getParentByClass:opt.getParentByClass, 1211 showPasswordBullets:opt.showPasswordBullets, 1212 placeholderAttr: placeholderValue ? 'placeholder' : opt.placeholderAttr 1213 }); 1214 } 1215 } 1216 } 1217 }; 1218 PlaceholderInput.prototype = { 1219 init: function(opt) { 1220 this.setOptions(opt); 1221 if(this.element && this.element.PlaceholderInst) { 1222 this.element.PlaceholderInst.refreshClasses(); 1223 } else { 1224 this.element.PlaceholderInst = this; 1225 if(this.elementType !== 'radio' || this.elementType !== 'checkbox' || this.elementType !== 'file') { 1226 this.initElements(); 1227 this.attachEvents(); 1228 this.refreshClasses(); 1229 } 1230 } 1231 }, 1232 setOptions: function(opt) { 1233 for(var p in opt) { 1234 if(opt.hasOwnProperty(p)) { 1235 this.options[p] = opt[p]; 1236 } 1237 } 1238 if(this.options.element) { 1239 this.element = this.options.element; 1240 this.elementType = getInputType(this.element); 1241 if(this.options.focusOnly) { 1242 this.wrapWithElement = false; 1243 } else { 1244 if(this.elementType === 'password' && this.options.showPasswordBullets) { 1245 this.wrapWithElement = false; 1246 } else { 1247 this.wrapWithElement = this.elementType === 'password' || this.options.showUntilTyping ? true : this.options.wrapWithElement; 1248 } 1249 } 1250 this.setPlaceholderValue(this.options.placeholderAttr); 1251 } 1252 }, 1253 setPlaceholderValue: function(attr) { 1254 this.origValue = (attr === 'value' ? this.element.defaultValue : (this.element.getAttribute(attr) || '')); 1255 if(this.options.placeholderAttr !== 'value') { 1256 this.element.removeAttribute(this.options.placeholderAttr); 1257 } 1258 }, 1259 initElements: function() { 1260 // create fake element if needed 1261 if(this.wrapWithElement) { 1262 this.fakeElement = document.createElement('span'); 1263 this.fakeElement.className = this.options.fakeElementClass; 1264 this.fakeElement.innerHTML += this.origValue; 1265 this.fakeElement.style.color = getStyle(this.element, 'color'); 1266 this.fakeElement.style.position = 'absolute'; 1267 this.element.parentNode.insertBefore(this.fakeElement, this.element); 1268 1269 if(this.element.value === this.origValue || !this.element.value) { 1270 this.element.value = ''; 1271 this.togglePlaceholderText(true); 1272 } else { 1273 this.togglePlaceholderText(false); 1274 } 1275 } else if(!this.element.value && this.origValue.length) { 1276 this.element.value = this.origValue; 1277 } 1278 // get input label
1279 if(this.element.id) { 1280 this.labels = document.getElementsByTagName('label'); 1281 for(var i = 0; i < this.labels.length; i++) { 1282 if(this.labels[i].htmlFor === this.element.id) { 1283 this.labelFor = this.labels[i]; 1284 break; 1285 } 1286 } 1287 } 1288 // get parent node (or parentNode by className) 1289 this.elementParent = this.element.parentNode; 1290 if(typeof this.options.getParentByClass === 'string') { 1291 var el = this.element; 1292 while(el.parentNode) { 1293 if(hasClass(el.parentNode, this.options.getParentByClass)) { 1294 this.elementParent = el.parentNode; 1295 break; 1296 } else { 1297 el = el.parentNode; 1298 } 1299 } 1300 } 1301 }, 1302 attachEvents: function() { 1303 this.element.onfocus = bindScope(this.focusHandler, this); 1304 this.element.onblur = bindScope(this.blurHandler, this); 1305 if(this.options.showUntilTyping) { 1306 this.element.onkeydown = bindScope(this.typingHandler, this); 1307 this.element.onpaste = bindScope(this.typingHandler, this); 1308 } 1309 if(this.wrapWithElement) this.fakeElement.onclick = bindScope(this.focusSetter, this); 1310 }, 1311 togglePlaceholderText: function(state) { 1312 if(!this.element.readOnly && !this.options.focusOnly) { 1313 if(this.wrapWithElement) { 1314 this.fakeElement.style.display = state ? '' : 'none'; 1315 } else { 1316 this.element.value = state ? this.origValue : ''; 1317 } 1318 } 1319 }, 1320 focusSetter: function() { 1321 this.element.focus(); 1322 }, 1323 focusHandler: function() { 1324 clearInterval(this.checkerInterval); 1325 this.checkerInterval = setInterval(bindScope(this.intervalHandler,this), 1); 1326 this.focused = true; 1327 if(!this.element.value.length || this.element.value === this.origValue) { 1328 if(!this.options.showUntilTyping) { 1329 this.togglePlaceholderText(false); 1330 } 1331 } 1332 this.refreshClasses(); 1333 }, 1334 blurHandler: function() { 1335 clearInterval(this.checkerInterval); 1336 this.focused = false; 1337 if(!this.element.value.length || this.element.value === this.origValue) { 1338 this.togglePlaceholderText(true); 1339 } 1340 this.refreshClasses(); 1341 PlaceholderInput.refreshAllInputs(this); 1342 }, 1343 typingHandler: function() { 1344 setTimeout(bindScope(function(){ 1345 if(this.element.value.length) { 1346 this.togglePlaceholderText(false); 1347 this.refreshClasses(); 1348 } 1349 },this), 10); 1350 }, 1351 intervalHandler: function() { 1352 if(typeof this.tmpValue === 'undefined') { 1353 this.tmpValue = this.element.value; 1354 } 1355 if(this.tmpValue != this.element.value) { 1356 PlaceholderInput.refreshAllInputs(this); 1357 } 1358 }, 1359 refreshState: function() { 1360 if(this.wrapWithElement) { 1361 if(this.element.value.length && this.element.value !== this.origValue) { 1362 this.togglePlaceholderText(false); 1363 } else if(!this.element.value.length) { 1364 this.togglePlaceholderText(true); 1365 } 1366 } 1367 this.refreshClasses(); 1368 }, 1369 refreshClasses: function() { 1370 this.textActive = this.focused || (this.element.value.length && this.element.value !== this.origValue); 1371 this.setStateClass(this.element, this.options.inputFocusClass,this.focused); 1372 this.setStateClass(this.elementParent, this.options.parentFocusClass,this.focused); 1373 this.setStateClass(this.labelFor, this.options.labelFocusClass,this.focused); 1374 this.setStateClass(this.element, this.options.inputActiveClass, this.textActive); 1375 this.setStateClass(this.elementParent, this.options.parentActiveClass, this.textActive); 1376 this.setStateClass(this.labelFor, this.options.labelActiveClass, this.textActive); 1377 }, 1378 setStateClass: function(el,cls,state) { 1379 if(!el) return; else if(state) addClass(el,cls); else removeClass(el,cls); 1380 } 1381 }; 1382 1383 // utility functions 1384 function convertToArray(collection) { 1385 var arr = []; 1386 for (var i = 0, ref = arr.length = collection.length; i < ref; i++) { 1387 arr[i] = collection[i]; 1388 } 1389 return arr; 1390 } 1391 function getInputType(input) { 1392 return (input.type ? input.type : input.tagName).toLowerCase(); 1393 } 1394 function hasClass(el,cls) { 1395 return el.className ? el.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)')) : false; 1396 } 1397 function addClass(el,cls) { 1398 if (!hasClass(el,cls)) el.className += " "+cls; 1399 } 1400 function removeClass(el,cls) { 1401 if (hasClass(el,cls)) {el.className=el.className.replace(new RegExp('(\\s|^)'+cls+'(\\s|$)'), ' ');} 1402 } 1403 function bindScope(f, scope) { 1404 return function() {return f.apply(scope, arguments);}; 1405 } 1406 function getStyle(el, prop) { 1407 if (document.defaultView && document.defaultView.getComputedStyle) { 1408 return document.defaultView.getComputedStyle(el, null)[prop]; 1409 } else if (el.currentStyle) { 1410 return el.currentStyle[prop]; 1411 } else { 1412 return el.style[prop]; 1413 } 1414 } 1415}()); 1416 1417/* 1418 * Utility module 1419 */ 1420lib = { 1421 hasClass: function(el,cls) { 1422 return el && el.className ? el.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)')) : false; 1423 }, 1424 addClass: function(el,cls) { 1425 if (el && !this.hasClass(el,cls)) el.className += " "+cls; 1426 }, 1427 removeClass: function(el,cls) { 1428 if (el && this.hasClass(el,cls)) {el.className=el.className.replace(new RegExp('(\\s|^)'+cls+'(\\s|$)'), '');} 1429 }, 1430 extend: function(obj) { 1431 for(var i = 1; i < arguments.length; i++) { 1432 for(var p in arguments[i]) { 1433 if(arguments[i].hasOwnProperty(p)) { 1434 obj[p] = arguments[i][p]; 1435 } 1436 } 1437 } 1438 return obj;
1439 }, 1440 each: function(obj, callback) { 1441 var property, len; 1442 if(typeof obj.length === 'number') { 1443 for(property = 0, len = obj.length; property < len; property++) { 1444 if(callback.call(obj[property], property, obj[property]) === false) { 1445 break; 1446 } 1447 } 1448 } else { 1449 for(property in obj) { 1450 if(obj.hasOwnProperty(property)) { 1451 if(callback.call(obj[property], property, obj[property]) === false) { 1452 break; 1453 } 1454 } 1455 } 1456 } 1457 }, 1458 event: (function() { 1459 var fixEvent = function(e) { 1460 e = e || window.event; 1461 if(e.isFixed) return e; else e.isFixed = true; 1462 if(!e.target) e.target = e.srcElement; 1463 e.preventDefault = e.preventDefault || function() {this.returnValue = false;}; 1464 e.stopPropagation = e.stopPropagation || function() {this.cancelBubble = true;}; 1465 return e; 1466 }; 1467 return { 1468 add: function(elem, event, handler) { 1469 if(!elem.events) { 1470 elem.events = {}; 1471 elem.handle = function(e) { 1472 var ret, handlers = elem.events[e.type]; 1473 e = fixEvent(e); 1474 for(var i = 0, len = handlers.length; i < len; i++) { 1475 if(handlers[i]) { 1476 ret = handlers[i].call(elem, e); 1477 if(ret === false) { 1478 e.preventDefault(); 1479 e.stopPropagation(); 1480 } 1481 } 1482 } 1483 }; 1484 } 1485 if(!elem.events[event]) { 1486 elem.events[event] = []; 1487 if(elem.addEventListener) elem.addEventListener(event, elem.handle, false); 1488 else if(elem.attachEvent) elem.attachEvent('on'+event, elem.handle); 1489 } 1490 elem.events[event].push(handler); 1491 }, 1492 remove: function(elem, event, handler) { 1493 var handlers = elem.events[event]; 1494 for(var i = handlers.length - 1; i >= 0; i--) { 1495 if(handlers[i] === handler) { 1496 handlers.splice(i,1); 1497 } 1498 } 1499 if(!handlers.length) { 1500 delete elem.events[event]; 1501 if(elem.removeEventListener) elem.removeEventListener(event, elem.handle, false); 1502 else if(elem.detachEvent) elem.detachEvent('on'+event, elem.handle); 1503 } 1504 } 1505 }; 1506 }()), 1507 queryElementsBySelector: function(selector, scope) { 1508 scope = scope || document; 1509 if(!selector) return []; 1510 if(selector === '>*') return scope.children; 1511 if(typeof document.querySelectorAll === 'function') { 1512 return scope.querySelectorAll(selector); 1513 } 1514 var selectors = selector.split(','); 1515 var resultList = []; 1516 for(var s = 0; s < selectors.length; s++) { 1517 var currentContext = [scope || document]; 1518 var tokens = selectors[s].replace(/^\s+/,'').replace(/\s+$/,'').split(' '); 1519 for (var i = 0; i < tokens.length; i++) { 1520 token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,''); 1521 if (token.indexOf('#') > -1) { 1522 var bits = token.split('#'), tagName = bits[0], id = bits[1]; 1523 var element = document.getElementById(id); 1524 if (element && tagName && element.nodeName.toLowerCase() != tagName) { 1525 return []; 1526 } 1527 currentContext = element ? [element] : []; 1528 continue; 1529 } 1530 if (token.indexOf('.') > -1) { 1531 var bits = token.split('.'), tagName = bits[0] || '*', className = bits[1], found = [], foundCount = 0; 1532 for (var h = 0; h < currentContext.length; h++) { 1533 var elements; 1534 if (tagName == '*') { 1535 elements = currentContext[h].getElementsByTagName('*'); 1536 } else { 1537 elements = currentContext[h].getElementsByTagName(tagName); 1538 } 1539 for (var j = 0; j < elements.length; j++) { 1540 found[foundCount++] = elements[j]; 1541 } 1542 } 1543 currentContext = []; 1544 var currentContextIndex = 0; 1545 for (var k = 0; k < found.length; k++) { 1546 if (found[k].className && found[k].className.match(new RegExp('(\\s|^)'+className+'(\\s|$)'))) { 1547 currentContext[currentContextIndex++] = found[k]; 1548 } 1549 } 1550 continue; 1551 } 1552 if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) { 1553 var tagName = RegExp.$1 || '*', attrName = RegExp.$2, attrOperator = RegExp.$3, attrValue = RegExp.$4; 1554 if(attrName.toLowerCase() == 'for' && this.browser.msie && this.browser.version < 8) { 1555 attrName = 'htmlFor'; 1556 } 1557 var found = [], foundCount = 0; 1558 for (var h = 0; h < currentContext.length; h++) { 1559 var elements; 1560 if (tagName == '*') { 1561 elements = currentContext[h].getElementsByTagName('*'); 1562 } else { 1563 elements = currentContext[h].getElementsByTagName(tagName); 1564 } 1565 for (var j = 0; elements[j]; j++) { 1566 found[foundCount++] = elements[j]; 1567 } 1568 } 1569 currentContext = []; 1570 var currentContextIndex = 0, checkFunction; 1571 switch (attrOperator) { 1572 case '=': checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue) }; break; 1573 case '~': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('(\\s|^)'+attrValue+'(\\s|$)'))) }; break; 1574 case '|': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))) }; break; 1575 case '^': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0) }; break; 1576 case '$': checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length) }; break; 1577 case '*': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1) }; break; 1578 default : checkFunction = function(e) { return e.getAttribute(attrName) }; 1579 } 1580 currentContext = []; 1581 var currentContextIndex = 0; 1582 for (var k = 0; k < found.length; k++) { 1583 if (checkFunction(found[k])) { 1584 currentContext[currentContextIndex++] = found[k]; 1585 } 1586 } 1587 continue; 1588 } 1589 tagName = token; 1590 var found = [], foundCount = 0; 1591 for (var h = 0; h < currentContext.length; h++) { 1592 var elements = currentContext[h].getElementsByTagName(tagName); 1593 for (var j = 0; j < elements.length; j++) { 1594 found[foundCount++] = elements[j]; 1595 } 1596 } 1597 currentContext = found; 1598 } 1599 resultList = [].concat(resultList,currentContext); 1600 } 1601 return resultList; 1602 }, 1603 trim: function (str) { 1604 return str.replace(/^\s+/, '').replace(/\s+$/, ''); 1605 }, 1606 bind: function(f, scope, forceArgs){ 1607 return function() {return f.apply(scope, typeof forceArgs !== 'undefined' ? [forceArgs] : arguments);}; 1608 } 1609}; 1610 1611/* 1612 * jQuery Accordion plugin 1613 */ 1614;(function($){ 1615 $.fn.slideAccordion = function(opt){ 1616 // default options 1617 var options = $.extend({ 1618 addClassBeforeAnimation: false, 1619 activeClass:'active', 1620 opener:'.opener', 1621 slider:'.slide', 1622 animSpeed: 300, 1623 collapsible:true, 1624 event:'click' 1625 },opt); 1626 1627 return this.each(function(){ 1628 // options 1629 var accordion = $(this); 1630 var items = accordion.find(':has('+options.slider+')'); 1631 1632 items.each(function(){ 1633 var item = $(this); 1634 var opener = item.find(options.opener); 1635 var slider = item.find(options.slider); 1636 opener.bind(options.event, function(e){
1637 if(!slider.is(':animated')) { 1638 if(item.hasClass(options.activeClass)) { 1639 if(options.collapsible) { 1640 slider.slideUp(options.animSpeed, function(){ 1641 hideSlide(slider); 1642 item.removeClass(options.activeClass); 1643 }); 1644 } 1645 } else { 1646 // show active 1647 var levelItems = item.siblings('.'+options.activeClass); 1648 var sliderElements = levelItems.find(options.slider); 1649 item.addClass(options.activeClass); 1650 showSlide(slider).hide().slideDown(options.animSpeed); 1651 1652 // collapse others 1653 sliderElements.slideUp(options.animSpeed, function(){ 1654 levelItems.removeClass(options.activeClass); 1655 hideSlide(sliderElements); 1656 }); 1657 } 1658 } 1659 e.preventDefault(); 1660 }); 1661 if(item.hasClass(options.activeClass)) showSlide(slider); else hideSlide(slider); 1662 }); 1663 }); 1664 }; 1665 1666 // accordion slide visibility 1667 var showSlide = function(slide) { 1668 return slide.css({position:'', top: '', left: '', width: '' }); 1669 }; 1670 var hideSlide = function(slide) { 1671 return slide.show().css({position:'absolute', top: -9999, left: -9999, width: slide.width() }); 1672 }; 1673}(jQuery)); 1674 1675/* 1676 * Popups plugin 1677 */ 1678;(function($) { 1679 function ContentPopup(opt) { 1680 this.options = $.extend({ 1681 holder: null, 1682 popup: '.popup', 1683 btnOpen: '.open', 1684 btnClose: '.close', 1685 openClass: 'popup-active', 1686 clickEvent: 'click', 1687 mode: 'click', 1688 hideOnClickLink: true, 1689 hideOnClickOutside: true, 1690 delay: 50 1691 }, opt); 1692 if(this.options.holder) { 1693 this.holder = $(this.options.holder); 1694 this.init(); 1695 } 1696 } 1697 ContentPopup.prototype = { 1698 init: function() { 1699 this.findElements(); 1700 this.attachEvents(); 1701 }, 1702 findElements: function() { 1703 this.popup = this.holder.find(this.options.popup); 1704 this.btnOpen = this.holder.find(this.options.btnOpen); 1705 this.btnClose = this.holder.find(this.options.btnClose); 1706 }, 1707 attachEvents: function() { 1708 // handle popup openers 1709 var self = this; 1710 this.clickMode = isTouchDevice || (self.options.mode === self.options.clickEvent); 1711 1712 if(this.clickMode) { 1713 // handle click mode 1714 this.btnOpen.bind(self.options.clickEvent, function(e) { 1715 if(self.holder.hasClass(self.options.openClass)) { 1716 if(self.options.hideOnClickLink) { 1717 self.hidePopup(); 1718 } 1719 } else { 1720 self.showPopup(); 1721 } 1722 e.preventDefault(); 1723 }); 1724 1725 // prepare outside click handler 1726 this.outsideClickHandler = this.bind(this.outsideClickHandler, this); 1727 } else { 1728 // handle hover mode 1729 var timer, delayedFunc = function(func) { 1730 clearTimeout(timer); 1731 timer = setTimeout(function() { 1732 func.call(self); 1733 }, self.options.delay); 1734 }; 1735 this.btnOpen.bind('mouseover', function() { 1736 delayedFunc(self.showPopup); 1737 }).bind('mouseout', function() { 1738 delayedFunc(self.hidePopup); 1739 }); 1740 this.popup.bind('mouseover', function() { 1741 delayedFunc(self.showPopup); 1742 }).bind('mouseout', function() { 1743 delayedFunc(self.hidePopup); 1744 }); 1745 } 1746 1747 // handle close buttons 1748 this.btnClose.bind(self.options.clickEvent, function(e) { 1749 self.hidePopup(); 1750 e.preventDefault(); 1751 }); 1752 }, 1753 outsideClickHandler: function(e) { 1754 // hide popup if clicked outside 1755 var currentNode = (e.changedTouches ? e.changedTouches[0] : e).target; 1756 if(!$(currentNode).parents().filter(this.holder).length) { 1757 this.hidePopup(); 1758 } 1759 }, 1760 showPopup: function() { 1761 // reveal popup 1762 this.holder.addClass(this.options.openClass); 1763 this.popup.css({display:'block'}); 1764 1765 // outside click handler 1766 if(this.clickMode && this.options.hideOnClickOutside && !this.outsideHandlerActive) { 1767 this.outsideHandlerActive = true; 1768 $(document).bind('click touchstart', this.outsideClickHandler); 1769 } 1770 }, 1771 hidePopup: function() { 1772 // hide popup 1773 this.holder.removeClass(this.options.openClass); 1774 this.popup.css({display:'none'}); 1775 1776 // outside click handler 1777 if(this.clickMode && this.options.hideOnClickOutside && this.outsideHandlerActive) { 1778 this.outsideHandlerActive = false; 1779 $(document).unbind('click touchstart', this.outsideClickHandler); 1780 } 1781 }, 1782 bind: function(f, scope, forceArgs){ 1783 return function() {return f.apply(scope, forceArgs ? [forceArgs] : arguments);}; 1784 } 1785 }; 1786 1787 // detect touch devices 1788 var isTouchDevice = /MSIE 10.*Touch/.test(navigator.userAgent) || ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch; 1789 1790 // jQuery plugin interface 1791 $.fn.contentPopup = function(opt) { 1792 return this.each(function() { 1793 new ContentPopup($.extend(opt, {holder: this})); 1794 }); 1795 }; 1796}(jQuery)); 1797 1798/* 1799 * Convert navigation to select 1800 */ 1801;(function($) { 1802 function NavigationSelect(options) { 1803 this.options = $.extend({ 1804 list: null, 1805 levelIndentHTML: ' • ', 1806 defaultOptionAttr: 'title',
1807 defaultOptionText: '...', 1808 selectClass: 'nav-select', 1809 activeClass: 'nav-active', 1810 defaultOptionClass: 'opt-default', 1811 hasDropClass: 'opt-sublevel', 1812 levelPrefixClass: 'opt-level-', 1813 useDefaultOption: true 1814 }, options); 1815 if(this.options.list) { 1816 this.createSelect(); 1817 this.attachEvents(); 1818 } 1819 } 1820 NavigationSelect.prototype = { 1821 createSelect: function() { 1822 var self = this; 1823 this.startIndex = 0; 1824 this.navigation = $(this.options.list); 1825 this.select = $('<select>').addClass(this.options.selectClass); 1826 this.createDefaultOption(); 1827 this.createList(this.navigation, 0); 1828 this.select.insertBefore(this.navigation); 1829 }, 1830 createDefaultOption: function() { 1831 if(this.options.useDefaultOption) { 1832 var attrText = this.navigation.attr(this.options.defaultOptionAttr); 1833 var defaultOption = $('<option>').addClass(this.options.defaultOptionClass).text(attrText || this.options.defaultOptionText); 1834 this.navigation.removeAttr(this.options.defaultOptionAttr); 1835 this.select.append(defaultOption); 1836 this.startIndex = 1; 1837 } 1838 }, 1839 createList: function(list, level) { 1840 var self = this; 1841 list.children().each(function(index, item) { 1842 var listItem = $(this), 1843 listLink = listItem.find('a').eq(0), 1844 listDrop = listItem.find('ul').eq(0), 1845 hasDrop = listDrop.length > 0; 1846 1847 if(listLink.length) { 1848 self.select.append(self.createOption(listLink, hasDrop, level, listLink.hasClass(self.options.activeClass))); 1849 } 1850 if(hasDrop) { 1851 self.createList(listDrop, level + 1); 1852 } 1853 }); 1854 }, 1855 createOption: function(link, hasDrop, level, selected) { 1856 var optionHTML = this.getLevelIndent(level) + link.html(); 1857 return $('<option>').html(optionHTML) 1858 .addClass(this.options.levelPrefixClass + (level + 1)) 1859 .toggleClass(this.options.hasDropClass, hasDrop) 1860 .val(link.attr('href')).attr('selected', selected ? 'selected' : false); 1861 }, 1862 getLevelIndent: function(level) { 1863 return (new Array(level + 1)).join(this.options.levelIndentHTML); 1864 }, 1865 attachEvents: function() { 1866 // redirect on select change 1867 var self = this; 1868 this.select.change(function() { 1869 if(this.selectedIndex >= self.startIndex) { 1870 location.href = this.value; 1871 } 1872 }); 1873 } 1874 }; 1875 1876 // jquery pluginm interface 1877 $.fn.navigationSelect = function(opt) { 1878 return this.each(function() { 1879 new NavigationSelect($.extend({list: this}, opt)); 1880 }); 1881 }; 1882}(jQuery)); 1883 1884/* 1885 * Responsive Layout helper 1886 */ 1887 1888ResponsiveHelper = (function($){ 1889 // init variables 1890 var handlers = []; 1891 var win = $(window), prevWinWidth; 1892 var scrollBarWidth = 0; 1893 1894 // prepare resize handler 1895 function resizeHandler() { 1896 var winWidth = win.width() + scrollBarWidth; 1897 if(winWidth !== prevWinWidth) { 1898 prevWinWidth = winWidth; 1899 1900 // loop through range groups 1901 $.each(handlers, function(index, rangeObject){ 1902 // disable current active area if needed 1903 $.each(rangeObject.data, function(property, item) { 1904 if((winWidth < item.range[0] || winWidth > item.range[1]) && item.currentActive) { 1905 item.currentActive = false; 1906 if(typeof item.disableCallback === 'function') { 1907 item.disableCallback(); 1908 } 1909 } 1910 }); 1911 1912 // enable areas that match current width 1913 $.each(rangeObject.data, function(property, item) { 1914 if(winWidth >= item.range[0] && winWidth <= item.range[1] && !item.currentActive) { 1915 // make callback 1916 item.currentActive = true; 1917 if(typeof item.enableCallback === 'function') { 1918 item.enableCallback(); 1919 } 1920 } 1921 }); 1922 }); 1923 } 1924 } 1925 win.bind('load', function(){ 1926 if($.browser.mozilla || $.browser.opera) { 1927 scrollBarWidth = window.innerWidth - $('body').width(); 1928 resizeHandler(); 1929 } 1930 win.bind('resize orientationchange', resizeHandler); 1931 }); 1932 1933 // range parser 1934 function parseRange(rangeStr) { 1935 var rangeData = rangeStr.split('..'); 1936 var x1 = parseInt(rangeData[0], 10) || -Infinity; 1937 var x2 = parseInt(rangeData[1], 10) || Infinity; 1938 return [x1, x2].sort(function(a, b){ 1939 return a - b; 1940 }); 1941 } 1942 1943 // export public functions 1944 return { 1945 addRange: function(ranges) { 1946 // parse data and add items to collection 1947 var result = {data:{}}; 1948 $.each(ranges, function(property, data){ 1949 result.data[property] = { 1950 range: parseRange(property), 1951 enableCallback: data.on, 1952 disableCallback: data.off 1953 }; 1954 }); 1955 handlers.push(result); 1956 1957 // call resizeHandler to recalculate all events 1958 resizeHandler(); 1959 } 1960 }; 1961}(jQuery));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.