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// * Browser Detect script 1165// */ 1166// BrowserDetect = (function() { 1167// // script settings 1168// var options = { 1169// osVersion: false, 1170// minorBrowserVersion: false 1171// }; 1172 1173// // browser data 1174// var browserData = { 1175// browsers: { 1176// chrome: uaMatch(/Chrome\/([0-9\.]*)/), 1177// firefox: uaMatch(/Firefox\/([0-9\.]*)/), 1178// safari: uaMatch(/Version\/([0-9\.]*).*Safari/), 1179// opera: uaMatch(/Opera\/.*Version\/([0-9\.]*)/, /Opera\/([0-9\.]*)/), 1180// msie: uaMatch(/MSIE ([0-9\.]*)/) 1181// }, 1182// engines: { 1183// webkit: uaContains('AppleWebKit'), 1184// gecko: uaContains('Gecko'), 1185// presto: uaContains('Presto'), 1186// trident: uaContains('MSIE') 1187// }, 1188// platforms: { 1189// win: uaMatch(/Windows NT ([0-9\.]*)/), 1190// mac: uaMatch(/Mac OS X ([0-9_\.]*)/), 1191// linux: uaContains('X11', 'Linux') 1192// } 1193// }; 1194 1195// // perform detection 1196// var ua = navigator.userAgent; 1197// var detectData = { 1198// platform: detectItem(browserData.platforms), 1199// browser: detectItem(browserData.browsers), 1200// engine: detectItem(browserData.engines) 1201// }; 1202 1203// // private functions 1204// function uaMatch(regExp, altReg) { 1205// return function() { 1206// var result = regExp.exec(ua) || altReg && altReg.exec(ua); 1207// return result && result[1]; 1208// }; 1209// } 1210// function uaContains(word) { 1211// var args = Array.prototype.slice.apply(arguments); 1212// return function() { 1213// for(var i = 0; i < args.length; i++) { 1214// if(ua.indexOf(args[i]) < 0) { 1215// return; 1216// } 1217// } 1218// return true; 1219// }; 1220// } 1221// function detectItem(items) { 1222// var detectedItem = null, itemName, detectValue; 1223// for(itemName in items) { 1224// if(items.hasOwnProperty(itemName)) { 1225// detectValue = items[itemName](); 1226// if(detectValue) { 1227// return { 1228// name: itemName, 1229// value: detectValue 1230// }; 1231// } 1232// } 1233// } 1234// } 1235 1236// // add classes to root element 1237// (function() { 1238// // helper functions 1239// var addClass = function(cls) { 1240// var html = document.documentElement; 1241// html.className += (html.className ? ' ' : '') + cls; 1242// }; 1243// var getVersion = function(ver) { 1244// return ver.replace(/\./g, '_'); 1245// }; 1246 1247// // add classes 1248// if(detectData.platform) { 1249// addClass(detectData.platform.name); 1250// if(options.osVersion) { 1251// addClass(detectData.platform.name + '-' + getVersion(detectData.platform.value)); 1252// } 1253// } 1254// if(detectData.engine) { 1255// addClass(detectData.engine.name); 1256// } 1257// if(detectData.browser) { 1258// addClass(detectData.browser.name); 1259// addClass(detectData.browser.name + '-' + parseInt(detectData.browser.value, 10)); 1260// if(options.minorBrowserVersion) { 1261// addClass(detectData.browser.name + '-' + getVersion(detectData.browser.value)); 1262// } 1263// } 1264// }()); 1265 1266// // export detection information 1267// return detectData; 1268// }()); 1269 1270// /* 1271// * Browser platform detection 1272// */ 1273// PlatformDetect = (function(){ 1274// var detectModules = {}; 1275// return { 1276// options: { 1277// cssPath: 'css/' 1278// }, 1279// addModule: function(obj) { 1280// detectModules[obj.type] = obj;
1281// }, 1282// addRule: function(rule) { 1283// if(this.matchRule(rule)) { 1284// this.applyRule(rule); 1285// return true; 1286// } 1287// }, 1288// matchRule: function(rule) { 1289// return detectModules[rule.type].matchRule(rule); 1290// }, 1291// applyRule: function(rule) { 1292// var head = document.getElementsByTagName('head')[0], fragment, cssText; 1293// if(rule.css) { 1294// cssText = '<link rel="stylesheet" href="' + this.options.cssPath + rule.css + '" />'; 1295// if(head) { 1296// fragment = document.createElement('div'); 1297// fragment.innerHTML = cssText; 1298// head.appendChild(fragment.childNodes[0]); 1299// } else { 1300// document.write(cssText); 1301// } 1302// } 1303 1304// if(rule.meta) { 1305// if(head) { 1306// fragment = document.createElement('div'); 1307// fragment.innerHTML = rule.meta; 1308// head.appendChild(fragment.childNodes[0]); 1309// } else { 1310// document.write(rule.meta); 1311// } 1312// } 1313// }, 1314// matchVersions: function(host, target) { 1315// target = target.toString(); 1316// host = host.toString(); 1317 1318// var majorVersionMatch = parseInt(target, 10) === parseInt(host, 10); 1319// var minorVersionMatch = (host.length > target.length ? host.indexOf(target) : target.indexOf(host)) === 0; 1320 1321// return majorVersionMatch && minorVersionMatch; 1322// } 1323// }; 1324// }()); 1325 1326// // All Mobile detection 1327// PlatformDetect.addModule({ 1328// type: 'allmobile', 1329// uaMatch: function(str) { 1330// if(!this.ua) { 1331// this.ua = navigator.userAgent.toLowerCase(); 1332// } 1333// return this.ua.indexOf(str.toLowerCase()) != -1; 1334// }, 1335// matchRule: function(rule) { 1336// return this.uaMatch('mobi') || this.uaMatch('midp') || this.uaMatch('ppc') || this.uaMatch('webos') || this.uaMatch('android') || this.uaMatch('phone os') || this.uaMatch('touch'); 1337// } 1338// }); 1339 1340// // iPhone detection 1341// PlatformDetect.addModule({ 1342// type: 'iphone', 1343// parseUserAgent: function() { 1344// var match = /(iPhone|iPod).*OS ([0-9_]*) .*/.exec(navigator.userAgent); 1345// if(match) { 1346// return { 1347// retina: window.devicePixelRatio > 1, 1348// version: match[2].replace(/_/g, '.') 1349// }; 1350// } 1351// }, 1352// matchRule: function(rule) { 1353// this.matchData = this.matchData || this.parseUserAgent(); 1354// if(this.matchData) { 1355// var matchVersion = rule.version ? PlatformDetect.matchVersions(this.matchData.version, rule.version) : true; 1356// var matchDevice = rule.deviceType ? (rule.deviceType === 'retina' && this.matchData.retina) || (rule.deviceType === 'noretina' && !this.matchData.retina) : true; 1357// return matchVersion && matchDevice; 1358// } 1359// } 1360// }); 1361 1362// // iPad detection 1363// PlatformDetect.addModule({ 1364// type: 'ipad', 1365// parseUserAgent: function() { 1366// var match = /(iPad).*OS ([0-9_]*) .*/.exec(navigator.userAgent); 1367// if(match) { 1368// return { 1369// retina: window.devicePixelRatio > 1, 1370// version: match[2].replace(/_/g, '.') 1371// }; 1372// } 1373// }, 1374// matchRule: function(rule) { 1375// this.matchData = this.matchData || this.parseUserAgent(); 1376// if(this.matchData) { 1377// var matchVersion = rule.version ? PlatformDetect.matchVersions(this.matchData.version, rule.version) : true; 1378// var matchDevice = rule.deviceType ? (rule.deviceType === 'retina' && this.matchData.retina) || (rule.deviceType === 'noretina' && !this.matchData.retina) : true; 1379// return matchVersion && matchDevice; 1380// } 1381// } 1382// }); 1383 1384// // Android detection 1385// PlatformDetect.addModule({ 1386// type: 'android', 1387// parseUserAgent: function() { 1388// var match = /(Android) ([0-9.]*).*/.exec(navigator.userAgent); 1389// if(match) { 1390// return { 1391// deviceType: navigator.userAgent.indexOf('Mobile') > 0 ? 'mobile' : 'tablet', 1392// version: match[2] 1393// }; 1394// } 1395// }, 1396// matchRule: function(rule) { 1397// this.matchData = this.matchData || this.parseUserAgent(); 1398// if(this.matchData) { 1399// var matchVersion = rule.version ? PlatformDetect.matchVersions(this.matchData.version, rule.version) : true; 1400// var matchDevice = rule.deviceType ? rule.deviceType === this.matchData.deviceType : true; 1401// return matchVersion && matchDevice; 1402// } 1403// } 1404// }); 1405 1406// // Blackberry detection 1407// PlatformDetect.addModule({ 1408// type: 'blackberry', 1409// parseUserAgent: function() { 1410// var match = /(BlackBerry).*Version\/([0-9.]*).*/.exec(navigator.userAgent); 1411// if(match) { 1412// return { 1413// version: match[2] 1414// }; 1415// } 1416// }, 1417// matchRule: function(rule) { 1418// this.matchData = this.matchData || this.parseUserAgent(); 1419// if(this.matchData) { 1420// return rule.version ? PlatformDetect.matchVersions(this.matchData.version, rule.version) : true; 1421// } 1422// } 1423// }); 1424 1425// // Detect rules 1426// PlatformDetect.addRule({type: 'allmobile', css: 'allmobile.css'}); 1427// PlatformDetect.addRule({type: 'iphone', css: 'iphone.css', meta: '<meta name="viewport" content="width=device-width" />'}); 1428// PlatformDetect.addRule({type: 'ipad', css: 'ipad.css', meta: '<meta name="viewport" content="width=device-width" />'}); 1429// PlatformDetect.addRule({type: 'android', version: 4, css: 'android.css'}); 1430// PlatformDetect.addRule({type: 'android', version: 4.2, css: 'android4x.css'}); 1431// PlatformDetect.addRule({type: 'android', version: 2, css: 'android2x.css'}); 1432// PlatformDetect.addRule({type: 'blackberry', css: 'blackberry.css'}); 1433 1434// placeholder class 1435;(function(){ 1436 var placeholderCollection = []; 1437 PlaceholderInput = function() { 1438 this.options = { 1439 element:null, 1440 showUntilTyping:false, 1441 wrapWithElement:false, 1442 getParentByClass:false, 1443 showPasswordBullets:false, 1444 placeholderAttr:'value', 1445 inputFocusClass:'focus', 1446 inputActiveClass:'text-active', 1447 parentFocusClass:'parent-focus', 1448 parentActiveClass:'parent-active', 1449 labelFocusClass:'label-focus', 1450 labelActiveClass:'label-active', 1451 fakeElementClass:'input-placeholder-text' 1452 }; 1453 placeholderCollection.push(this); 1454 this.init.apply(this,arguments); 1455 }; 1456 PlaceholderInput.refreshAllInputs = function(except) { 1457 for(var i = 0; i < placeholderCollection.length; i++) { 1458 if(except !== placeholderCollection[i]) { 1459 placeholderCollection[i].refreshState(); 1460 } 1461 } 1462 }; 1463 PlaceholderInput.replaceByOptions = function(opt) { 1464 var inputs = [].concat( 1465 convertToArray(document.getElementsByTagName('input')), 1466 convertToArray(document.getElementsByTagName('textarea')) 1467 ); 1468 for(var i = 0; i < inputs.length; i++) { 1469 if(inputs[i].className.indexOf(opt.skipClass) < 0) { 1470 var inputType = getInputType(inputs[i]); 1471 var placeholderValue = inputs[i].getAttribute('placeholder'); 1472 if(opt.focusOnly || (opt.clearInputs && (inputType === 'text' || inputType === 'email' || placeholderValue)) || 1473 (opt.clearTextareas && inputType === 'textarea') || 1474 (opt.clearPasswords && inputType === 'password') 1475 ) { 1476 new PlaceholderInput({ 1477 element:inputs[i], 1478 focusOnly: opt.focusOnly, 1479 wrapWithElement:opt.wrapWithElement, 1480 showUntilTyping:opt.showUntilTyping, 1481 getParentByClass:opt.getParentByClass, 1482 showPasswordBullets:opt.showPasswordBullets, 1483 placeholderAttr: placeholderValue ? 'placeholder' : opt.placeholderAttr 1484 });
1485 } 1486 } 1487 } 1488 }; 1489 PlaceholderInput.prototype = { 1490 init: function(opt) { 1491 this.setOptions(opt); 1492 if(this.element && this.element.PlaceholderInst) { 1493 this.element.PlaceholderInst.refreshClasses(); 1494 } else { 1495 this.element.PlaceholderInst = this; 1496 if(this.elementType !== 'radio' || this.elementType !== 'checkbox' || this.elementType !== 'file') { 1497 this.initElements(); 1498 this.attachEvents(); 1499 this.refreshClasses(); 1500 } 1501 } 1502 }, 1503 setOptions: function(opt) { 1504 for(var p in opt) { 1505 if(opt.hasOwnProperty(p)) { 1506 this.options[p] = opt[p]; 1507 } 1508 } 1509 if(this.options.element) { 1510 this.element = this.options.element; 1511 this.elementType = getInputType(this.element); 1512 if(this.options.focusOnly) { 1513 this.wrapWithElement = false; 1514 } else { 1515 if(this.elementType === 'password' && this.options.showPasswordBullets) { 1516 this.wrapWithElement = false; 1517 } else { 1518 this.wrapWithElement = this.elementType === 'password' || this.options.showUntilTyping ? true : this.options.wrapWithElement; 1519 } 1520 } 1521 this.setPlaceholderValue(this.options.placeholderAttr); 1522 } 1523 }, 1524 setPlaceholderValue: function(attr) { 1525 this.origValue = (attr === 'value' ? this.element.defaultValue : (this.element.getAttribute(attr) || '')); 1526 if(this.options.placeholderAttr !== 'value') { 1527 this.element.removeAttribute(this.options.placeholderAttr); 1528 } 1529 }, 1530 initElements: function() { 1531 // create fake element if needed 1532 if(this.wrapWithElement) { 1533 this.fakeElement = document.createElement('span'); 1534 this.fakeElement.className = this.options.fakeElementClass; 1535 this.fakeElement.innerHTML += this.origValue; 1536 this.fakeElement.style.color = getStyle(this.element, 'color'); 1537 this.fakeElement.style.position = 'absolute'; 1538 this.element.parentNode.insertBefore(this.fakeElement, this.element); 1539 1540 if(this.element.value === this.origValue || !this.element.value) { 1541 this.element.value = ''; 1542 this.togglePlaceholderText(true); 1543 } else { 1544 this.togglePlaceholderText(false); 1545 } 1546 } else if(!this.element.value && this.origValue.length) { 1547 this.element.value = this.origValue; 1548 } 1549 // get input label 1550 if(this.element.id) { 1551 this.labels = document.getElementsByTagName('label'); 1552 for(var i = 0; i < this.labels.length; i++) { 1553 if(this.labels[i].htmlFor === this.element.id) { 1554 this.labelFor = this.labels[i]; 1555 break; 1556 } 1557 } 1558 } 1559 // get parent node (or parentNode by className) 1560 this.elementParent = this.element.parentNode; 1561 if(typeof this.options.getParentByClass === 'string') { 1562 var el = this.element; 1563 while(el.parentNode) { 1564 if(hasClass(el.parentNode, this.options.getParentByClass)) { 1565 this.elementParent = el.parentNode; 1566 break; 1567 } else { 1568 el = el.parentNode; 1569 } 1570 } 1571 } 1572 }, 1573 attachEvents: function() { 1574 this.element.onfocus = bindScope(this.focusHandler, this); 1575 this.element.onblur = bindScope(this.blurHandler, this); 1576 if(this.options.showUntilTyping) { 1577 this.element.onkeydown = bindScope(this.typingHandler, this); 1578 this.element.onpaste = bindScope(this.typingHandler, this); 1579 } 1580 if(this.wrapWithElement) this.fakeElement.onclick = bindScope(this.focusSetter, this); 1581 }, 1582 togglePlaceholderText: function(state) { 1583 if(!this.element.readOnly && !this.options.focusOnly) { 1584 if(this.wrapWithElement) { 1585 this.fakeElement.style.display = state ? '' : 'none'; 1586 } else { 1587 this.element.value = state ? this.origValue : ''; 1588 } 1589 } 1590 }, 1591 focusSetter: function() { 1592 this.element.focus(); 1593 }, 1594 focusHandler: function() { 1595 clearInterval(this.checkerInterval); 1596 this.checkerInterval = setInterval(bindScope(this.intervalHandler,this), 1); 1597 this.focused = true; 1598 if(!this.element.value.length || this.element.value === this.origValue) { 1599 if(!this.options.showUntilTyping) { 1600 this.togglePlaceholderText(false); 1601 } 1602 } 1603 this.refreshClasses(); 1604 }, 1605 blurHandler: function() { 1606 clearInterval(this.checkerInterval); 1607 this.focused = false; 1608 if(!this.element.value.length || this.element.value === this.origValue) { 1609 this.togglePlaceholderText(true); 1610 } 1611 this.refreshClasses(); 1612 PlaceholderInput.refreshAllInputs(this); 1613 }, 1614 typingHandler: function() { 1615 setTimeout(bindScope(function(){ 1616 if(this.element.value.length) { 1617 this.togglePlaceholderText(false); 1618 this.refreshClasses(); 1619 } 1620 },this), 10); 1621 }, 1622 intervalHandler: function() { 1623 if(typeof this.tmpValue === 'undefined') { 1624 this.tmpValue = this.element.value; 1625 } 1626 if(this.tmpValue != this.element.value) { 1627 PlaceholderInput.refreshAllInputs(this); 1628 } 1629 }, 1630 refreshState: function() { 1631 if(this.wrapWithElement) { 1632 if(this.element.value.length && this.element.value !== this.origValue) { 1633 this.togglePlaceholderText(false); 1634 } else if(!this.element.value.length) { 1635 this.togglePlaceholderText(true); 1636 } 1637 } 1638 this.refreshClasses(); 1639 }, 1640 refreshClasses: function() { 1641 this.textActive = this.focused || (this.element.value.length && this.element.value !== this.origValue); 1642 this.setStateClass(this.element, this.options.inputFocusClass,this.focused); 1643 this.setStateClass(this.elementParent, this.options.parentFocusClass,this.focused); 1644 this.setStateClass(this.labelFor, this.options.labelFocusClass,this.focused); 1645 this.setStateClass(this.element, this.options.inputActiveClass, this.textActive); 1646 this.setStateClass(this.elementParent, this.options.parentActiveClass, this.textActive); 1647 this.setStateClass(this.labelFor, this.options.labelActiveClass, this.textActive); 1648 }, 1649 setStateClass: function(el,cls,state) { 1650 if(!el) return; else if(state) addClass(el,cls); else removeClass(el,cls); 1651 } 1652 }; 1653 1654 // utility functions 1655 function convertToArray(collection) { 1656 var arr = []; 1657 for (var i = 0, ref = arr.length = collection.length; i < ref; i++) { 1658 arr[i] = collection[i]; 1659 } 1660 return arr; 1661 } 1662 function getInputType(input) { 1663 return (input.type ? input.type : input.tagName).toLowerCase(); 1664 } 1665 function hasClass(el,cls) { 1666 return el.className ? el.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)')) : false; 1667 } 1668 function addClass(el,cls) { 1669 if (!hasClass(el,cls)) el.className += " "+cls; 1670 } 1671 function removeClass(el,cls) { 1672 if (hasClass(el,cls)) {el.className=el.className.replace(new RegExp('(\\s|^)'+cls+'(\\s|$)'), ' ');} 1673 } 1674 function bindScope(f, scope) { 1675 return function() {return f.apply(scope, arguments);}; 1676 } 1677 function getStyle(el, prop) { 1678 if (document.defaultView && document.defaultView.getComputedStyle) { 1679 return document.defaultView.getComputedStyle(el, null)[prop]; 1680 } else if (el.currentStyle) { 1681 return el.currentStyle[prop]; 1682 } else { 1683 return el.style[prop]; 1684 } 1685 } 1686}()); 1687 1688// navigation accesibility module 1689/* 1690function TouchNav(opt) { 1691 this.options = { 1692 hoverClass: 'hover', 1693 menuItems: 'li', 1694 menuOpener: 'a', 1695 menuDrop: 'ul', 1696 navBlock: null 1697 }; 1698 for(var p in opt) { 1699 if(opt.hasOwnProperty(p)) { 1700 this.options[p] = opt[p]; 1701 } 1702 } 1703 this.init(); 1704} 1705TouchNav.isActiveOn = function(elem) { 1706 return elem && elem.touchNavActive; 1707}; 1708TouchNav.prototype = { 1709 init: function() { 1710 if(typeof this.options.navBlock === 'string') { 1711 this.menu = document.getElementById(this.options.navBlock); 1712 } else if(typeof this.options.navBlock === 'object') { 1713 this.menu = this.options.navBlock; 1714 } 1715 if(this.menu) { 1716 this.addEvents(); 1717 } 1718 }, 1719 addEvents: function() { 1720 // attach event handlers 1721 var self = this; 1722 this.menuItems = lib.queryElementsBySelector(this.options.menuItems, this.menu); 1723 1724 for(var i = 0; i < this.menuItems.length; i++) { 1725 (function(i){ 1726 var item = self.menuItems[i], 1727 currentDrop = lib.queryElementsBySelector(self.options.menuDrop, item)[0], 1728 currentOpener = lib.queryElementsBySelector(self.options.menuOpener, item)[0]; 1729 1730 // only for touch input devices 1731 if( (self.isTouchDevice || navigator.msPointerEnabled) && currentDrop && currentOpener) { 1732 lib.event.add(currentOpener, 'click', lib.bind(self.clickHandler, self));
1733 lib.event.add(currentOpener, navigator.msPointerEnabled ? 'MSPointerDown' : 'touchstart', function(e){ 1734 if(navigator.msPointerEnabled && e.pointerType !== e.MSPOINTER_TYPE_TOUCH) return; 1735 self.touchFlag = true; 1736 self.currentItem = item; 1737 self.currentLink = currentOpener; 1738 self.pressHandler.apply(self, arguments); 1739 }); 1740 } 1741 // for desktop computers and touch devices 1742 lib.event.add(item, 'mouseover', function(){ 1743 if(!self.touchFlag) { 1744 self.currentItem = item; 1745 self.mouseoverHandler(); 1746 } 1747 }); 1748 lib.event.add(item, 'mouseout', function(){ 1749 if(!self.touchFlag) { 1750 self.currentItem = item; 1751 self.mouseoutHandler(); 1752 } 1753 }); 1754 item.touchNavActive = true; 1755 })(i); 1756 } 1757 1758 // hide dropdowns when clicking outside navigation 1759 if(this.isTouchDevice || navigator.msPointerEnabled) { 1760 lib.event.add(document, navigator.msPointerEnabled ? 'MSPointerDown' : 'touchstart', lib.bind(this.clickOutsideHandler, this)); 1761 } 1762 }, 1763 mouseoverHandler: function() { 1764 lib.addClass(this.currentItem, this.options.hoverClass); 1765 jQuery(this.currentItem).trigger('itemhover'); 1766 }, 1767 mouseoutHandler: function() { 1768 lib.removeClass(this.currentItem, this.options.hoverClass); 1769 jQuery(this.currentItem).trigger('itemleave'); 1770 }, 1771 hideActiveDropdown: function() { 1772 for(var i = 0; i < this.menuItems.length; i++) { 1773 if(lib.hasClass(this.menuItems[i], this.options.hoverClass)) { 1774 lib.removeClass(this.menuItems[i], this.options.hoverClass); 1775 jQuery(this.menuItems[i]).trigger('itemleave'); 1776 } 1777 } 1778 this.activeParent = null; 1779 }, 1780 pressHandler: function(e) { 1781 // hide previous drop (if active) 1782 if(this.currentItem != this.activeParent && !this.isParent(this.activeParent, this.currentLink)) { 1783 this.hideActiveDropdown(); 1784 } 1785 // handle current drop 1786 this.activeParent = this.currentItem; 1787 if(lib.hasClass(this.currentItem, this.options.hoverClass)) { 1788 this.preventCurrentClick = false; 1789 } else { 1790 e.preventDefault(); 1791 this.preventCurrentClick = true; 1792 lib.addClass(this.currentItem, this.options.hoverClass); 1793 jQuery(this.currentItem).trigger('itemhover'); 1794 } 1795 }, 1796 clickHandler: function(e) { 1797 // prevent first click on link 1798 if(this.preventCurrentClick) { 1799 e.preventDefault(); 1800 } 1801 }, 1802 clickOutsideHandler: function(event) { 1803 if(navigator.msPointerEnabled && event.pointerType !== event.MSPOINTER_TYPE_TOUCH) return; 1804 var e = event.changedTouches ? event.changedTouches[0] : event; 1805 if(this.activeParent && !this.isParent(this.menu, e.target)) { 1806 this.hideActiveDropdown(); 1807 this.touchFlag = false; 1808 } 1809 }, 1810 isParent: function(parent, child) { 1811 while(child.parentNode) { 1812 if(child.parentNode == parent) { 1813 return true; 1814 } 1815 child = child.parentNode; 1816 } 1817 return false; 1818 }, 1819 isTouchDevice: (function() { 1820 try { 1821 return (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) || navigator.userAgent.indexOf('IEMobile') != -1; 1822 } catch (e) { 1823 return false; 1824 } 1825 }()) 1826}; 1827 1828/* 1829 * Utility module 1830 */ 1831lib = { 1832 hasClass: function(el,cls) { 1833 return el && el.className ? el.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)')) : false; 1834 }, 1835 addClass: function(el,cls) { 1836 if (el && !this.hasClass(el,cls)) el.className += " "+cls; 1837 }, 1838 removeClass: function(el,cls) { 1839 if (el && this.hasClass(el,cls)) {el.className=el.className.replace(new RegExp('(\\s|^)'+cls+'(\\s|$)'), '');} 1840 }, 1841 extend: function(obj) { 1842 for(var i = 1; i < arguments.length; i++) { 1843 for(var p in arguments[i]) { 1844 if(arguments[i].hasOwnProperty(p)) { 1845 obj[p] = arguments[i][p]; 1846 } 1847 } 1848 } 1849 return obj; 1850 }, 1851 each: function(obj, callback) { 1852 var property, len; 1853 if(typeof obj.length === 'number') { 1854 for(property = 0, len = obj.length; property < len; property++) { 1855 if(callback.call(obj[property], property, obj[property]) === false) { 1856 break; 1857 } 1858 } 1859 } else { 1860 for(property in obj) { 1861 if(obj.hasOwnProperty(property)) { 1862 if(callback.call(obj[property], property, obj[property]) === false) { 1863 break; 1864 } 1865 } 1866 } 1867 } 1868 }, 1869 event: (function() { 1870 var fixEvent = function(e) { 1871 e = e || window.event; 1872 if(e.isFixed) return e; else e.isFixed = true; 1873 if(!e.target) e.target = e.srcElement; 1874 e.preventDefault = e.preventDefault || function() {this.returnValue = false;}; 1875 e.stopPropagation = e.stopPropagation || function() {this.cancelBubble = true;}; 1876 return e; 1877 }; 1878 return { 1879 add: function(elem, event, handler) { 1880 if(!elem.events) { 1881 elem.events = {}; 1882 elem.handle = function(e) { 1883 var ret, handlers = elem.events[e.type]; 1884 e = fixEvent(e); 1885 for(var i = 0, len = handlers.length; i < len; i++) { 1886 if(handlers[i]) { 1887 ret = handlers[i].call(elem, e); 1888 if(ret === false) { 1889 e.preventDefault(); 1890 e.stopPropagation(); 1891 } 1892 } 1893 } 1894 }; 1895 } 1896 if(!elem.events[event]) { 1897 elem.events[event] = []; 1898 if(elem.addEventListener) elem.addEventListener(event, elem.handle, false); 1899 else if(elem.attachEvent) elem.attachEvent('on'+event, elem.handle); 1900 } 1901 elem.events[event].push(handler); 1902 }, 1903 remove: function(elem, event, handler) { 1904 var handlers = elem.events[event]; 1905 for(var i = handlers.length - 1; i >= 0; i--) { 1906 if(handlers[i] === handler) { 1907 handlers.splice(i,1); 1908 } 1909 } 1910 if(!handlers.length) { 1911 delete elem.events[event]; 1912 if(elem.removeEventListener) elem.removeEventListener(event, elem.handle, false); 1913 else if(elem.detachEvent) elem.detachEvent('on'+event, elem.handle); 1914 } 1915 } 1916 }; 1917 }()), 1918 queryElementsBySelector: function(selector, scope) { 1919 scope = scope || document; 1920 if(!selector) return []; 1921 if(selector === '>*') return scope.children; 1922 if(typeof document.querySelectorAll === 'function') { 1923 return scope.querySelectorAll(selector); 1924 } 1925 var selectors = selector.split(','); 1926 var resultList = []; 1927 for(var s = 0; s < selectors.length; s++) { 1928 var currentContext = [scope || document]; 1929 var tokens = selectors[s].replace(/^\s+/,'').replace(/\s+$/,'').split(' '); 1930 for (var i = 0; i < tokens.length; i++) { 1931 token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,''); 1932 if (token.indexOf('#') > -1) { 1933 var bits = token.split('#'), tagName = bits[0], id = bits[1]; 1934 var element = document.getElementById(id); 1935 if (element && tagName && element.nodeName.toLowerCase() != tagName) { 1936 return []; 1937 } 1938 currentContext = element ? [element] : []; 1939 continue; 1940 } 1941 if (token.indexOf('.') > -1) { 1942 var bits = token.split('.'), tagName = bits[0] || '*', className = bits[1], found = [], foundCount = 0; 1943 for (var h = 0; h < currentContext.length; h++) { 1944 var elements; 1945 if (tagName == '*') { 1946 elements = currentContext[h].getElementsByTagName('*'); 1947 } else { 1948 elements = currentContext[h].getElementsByTagName(tagName); 1949 } 1950 for (var j = 0; j < elements.length; j++) { 1951 found[foundCount++] = elements[j]; 1952 } 1953 } 1954 currentContext = []; 1955 var currentContextIndex = 0; 1956 for (var k = 0; k < found.length; k++) { 1957 if (found[k].className && found[k].className.match(new RegExp('(\\s|^)'+className+'(\\s|$)'))) { 1958 currentContext[currentContextIndex++] = found[k]; 1959 } 1960 } 1961 continue; 1962 } 1963 if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) { 1964 var tagName = RegExp.$1 || '*', attrName = RegExp.$2, attrOperator = RegExp.$3, attrValue = RegExp.$4; 1965 if(attrName.toLowerCase() == 'for' && this.browser.msie && this.browser.version < 8) { 1966 attrName = 'htmlFor'; 1967 } 1968 var found = [], foundCount = 0; 1969 for (var h = 0; h < currentContext.length; h++) { 1970 var elements; 1971 if (tagName == '*') { 1972 elements = currentContext[h].getElementsByTagName('*'); 1973 } else { 1974 elements = currentContext[h].getElementsByTagName(tagName); 1975 } 1976 for (var j = 0; elements[j]; j++) { 1977 found[foundCount++] = elements[j]; 1978 } 1979 } 1980 currentContext = []; 1981 var currentContextIndex = 0, checkFunction; 1982 switch (attrOperator) { 1983 case '=': checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue) }; break; 1984 case '~': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('(\\s|^)'+attrValue+'(\\s|$)'))) }; break; 1985 case '|': checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))) }; break; 1986 case '^': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0) }; break; 1987 case '$': checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length) }; break; 1988 case '*': checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1) }; break; 1989 default : checkFunction = function(e) { return e.getAttribute(attrName) }; 1990 } 1991 currentContext = []; 1992 var currentContextIndex = 0; 1993 for (var k = 0; k < found.length; k++) { 1994 if (checkFunction(found[k])) { 1995 currentContext[currentContextIndex++] = found[k]; 1996 } 1997 } 1998 continue; 1999 } 2000 tagName = token; 2001 var found = [], foundCount = 0; 2002 for (var h = 0; h < currentContext.length; h++) { 2003 var elements = currentContext[h].getElementsByTagName(tagName); 2004 for (var j = 0; j < elements.length; j++) { 2005 found[foundCount++] = elements[j]; 2006 } 2007 } 2008 currentContext = found; 2009 } 2010 resultList = [].concat(resultList,currentContext); 2011 } 2012 return resultList; 2013 }, 2014 trim: function (str) { 2015 return str.replace(/^\s+/, '').replace(/\s+$/, ''); 2016 }, 2017 bind: function(f, scope, forceArgs){ 2018 return function() {return f.apply(scope, typeof forceArgs !== 'undefined' ? [forceArgs] : arguments);}; 2019 } 2020}; 2021 2022/* 2023 * jQuery Accordion plugin 2024 */ 2025;(function($){ 2026 $.fn.slideAccordion = function(opt){ 2027 // default options 2028 var options = $.extend({ 2029 addClassBeforeAnimation: false, 2030 activeClass:'active', 2031 opener:'.opener', 2032 slider:'.slide', 2033 animSpeed: 300, 2034 collapsible:true, 2035 event:'click' 2036 },opt); 2037 2038 return this.each(function(){ 2039 // options 2040 var accordion = $(this); 2041 var items = accordion.find(':has('+options.slider+')'); 2042 2043 items.each(function(){ 2044 var item = $(this); 2045 var opener = item.find(options.opener); 2046 var slider = item.find(options.slider); 2047 opener.bind(options.event, function(e){
2048 if(!slider.is(':animated')) { 2049 if(item.hasClass(options.activeClass)) { 2050 if(options.collapsible) { 2051 slider.slideUp(options.animSpeed, function(){ 2052 hideSlide(slider); 2053 item.removeClass(options.activeClass); 2054 }); 2055 } 2056 } else { 2057 // show active 2058 var levelItems = item.siblings('.'+options.activeClass); 2059 var sliderElements = levelItems.find(options.slider); 2060 item.addClass(options.activeClass); 2061 showSlide(slider).hide().slideDown(options.animSpeed); 2062 2063 // collapse others 2064 sliderElements.slideUp(options.animSpeed, function(){ 2065 levelItems.removeClass(options.activeClass); 2066 hideSlide(sliderElements); 2067 }); 2068 } 2069 } 2070 e.preventDefault(); 2071 }); 2072 if(item.hasClass(options.activeClass)) showSlide(slider); else hideSlide(slider); 2073 }); 2074 }); 2075 }; 2076 2077 // accordion slide visibility 2078 var showSlide = function(slide) { 2079 return slide.css({position:'', top: '', left: '', width: '' }); 2080 }; 2081 var hideSlide = function(slide) { 2082 return slide.show().css({position:'absolute', top: -9999, left: -9999, width: slide.width() }); 2083 }; 2084}(jQuery)); 2085 2086/* 2087 * Popups plugin 2088 */ 2089;(function($) { 2090 function ContentPopup(opt) { 2091 this.options = $.extend({ 2092 holder: null, 2093 popup: '.popup', 2094 btnOpen: '.open', 2095 btnClose: '.close', 2096 openClass: 'popup-active', 2097 clickEvent: 'click', 2098 mode: 'click', 2099 hideOnClickLink: true, 2100 hideOnClickOutside: true, 2101 delay: 50 2102 }, opt); 2103 if(this.options.holder) { 2104 this.holder = $(this.options.holder); 2105 this.init(); 2106 } 2107 } 2108 ContentPopup.prototype = { 2109 init: function() { 2110 this.findElements(); 2111 this.attachEvents(); 2112 }, 2113 findElements: function() { 2114 this.popup = this.holder.find(this.options.popup); 2115 this.btnOpen = this.holder.find(this.options.btnOpen); 2116 this.btnClose = this.holder.find(this.options.btnClose); 2117 }, 2118 attachEvents: function() { 2119 // handle popup openers 2120 var self = this; 2121 this.clickMode = isTouchDevice || (self.options.mode === self.options.clickEvent); 2122 2123 if(this.clickMode) { 2124 // handle click mode 2125 this.btnOpen.bind(self.options.clickEvent, function(e) { 2126 if(self.holder.hasClass(self.options.openClass)) { 2127 if(self.options.hideOnClickLink) { 2128 self.hidePopup(); 2129 } 2130 } else { 2131 self.showPopup(); 2132 } 2133 e.preventDefault(); 2134 }); 2135 2136 // prepare outside click handler 2137 this.outsideClickHandler = this.bind(this.outsideClickHandler, this); 2138 } else { 2139 // handle hover mode 2140 var timer, delayedFunc = function(func) { 2141 clearTimeout(timer); 2142 timer = setTimeout(function() { 2143 func.call(self); 2144 }, self.options.delay); 2145 }; 2146 this.btnOpen.bind('mouseover', function() { 2147 delayedFunc(self.showPopup); 2148 }).bind('mouseout', function() { 2149 delayedFunc(self.hidePopup); 2150 }); 2151 this.popup.bind('mouseover', function() { 2152 delayedFunc(self.showPopup); 2153 }).bind('mouseout', function() { 2154 delayedFunc(self.hidePopup); 2155 }); 2156 } 2157 2158 // handle close buttons 2159 this.btnClose.bind(self.options.clickEvent, function(e) { 2160 self.hidePopup(); 2161 e.preventDefault(); 2162 }); 2163 }, 2164 outsideClickHandler: function(e) { 2165 // hide popup if clicked outside 2166 var currentNode = (e.changedTouches ? e.changedTouches[0] : e).target; 2167 if(!$(currentNode).parents().filter(this.holder).length) { 2168 this.hidePopup(); 2169 } 2170 }, 2171 showPopup: function() { 2172 // reveal popup 2173 this.holder.addClass(this.options.openClass); 2174 this.popup.css({display:'block'}); 2175 2176 // outside click handler 2177 if(this.clickMode && this.options.hideOnClickOutside && !this.outsideHandlerActive) { 2178 this.outsideHandlerActive = true; 2179 $(document).bind('click touchstart', this.outsideClickHandler); 2180 } 2181 }, 2182 hidePopup: function() { 2183 // hide popup 2184 this.holder.removeClass(this.options.openClass); 2185 this.popup.css({display:'none'}); 2186 2187 // outside click handler 2188 if(this.clickMode && this.options.hideOnClickOutside && this.outsideHandlerActive) { 2189 this.outsideHandlerActive = false; 2190 $(document).unbind('click touchstart', this.outsideClickHandler); 2191 } 2192 }, 2193 bind: function(f, scope, forceArgs){ 2194 return function() {return f.apply(scope, forceArgs ? [forceArgs] : arguments);}; 2195 } 2196 }; 2197 2198 // detect touch devices 2199 var isTouchDevice = /MSIE 10.*Touch/.test(navigator.userAgent) || ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch; 2200 2201 // jQuery plugin interface 2202 $.fn.contentPopup = function(opt) { 2203 return this.each(function() { 2204 new ContentPopup($.extend(opt, {holder: this})); 2205 }); 2206 }; 2207}(jQuery)); 2208 2209/* 2210 * Convert navigation to select 2211 */ 2212;(function($) { 2213 function NavigationSelect(options) { 2214 this.options = $.extend({ 2215 list: null, 2216 levelIndentHTML: ' • ', 2217 defaultOptionAttr: 'title',
2218 defaultOptionText: '...', 2219 selectClass: 'nav-select', 2220 activeClass: 'nav-active', 2221 defaultOptionClass: 'opt-default', 2222 hasDropClass: 'opt-sublevel', 2223 levelPrefixClass: 'opt-level-', 2224 useDefaultOption: true 2225 }, options); 2226 if(this.options.list) { 2227 this.createSelect(); 2228 this.attachEvents(); 2229 } 2230 } 2231 NavigationSelect.prototype = { 2232 createSelect: function() { 2233 var self = this; 2234 this.startIndex = 0; 2235 this.navigation = $(this.options.list); 2236 this.select = $('<select>').addClass(this.options.selectClass); 2237 this.createDefaultOption(); 2238 this.createList(this.navigation, 0); 2239 this.select.insertBefore(this.navigation); 2240 }, 2241 createDefaultOption: function() { 2242 if(this.options.useDefaultOption) { 2243 var attrText = this.navigation.attr(this.options.defaultOptionAttr); 2244 var defaultOption = $('<option>').addClass(this.options.defaultOptionClass).text(attrText || this.options.defaultOptionText); 2245 this.navigation.removeAttr(this.options.defaultOptionAttr); 2246 this.select.append(defaultOption); 2247 this.startIndex = 1; 2248 } 2249 }, 2250 createList: function(list, level) { 2251 var self = this; 2252 list.children().each(function(index, item) { 2253 var listItem = $(this), 2254 listLink = listItem.find('a').eq(0), 2255 listDrop = listItem.find('ul').eq(0), 2256 hasDrop = listDrop.length > 0; 2257 2258 if(listLink.length) { 2259 self.select.append(self.createOption(listLink, hasDrop, level, listLink.hasClass(self.options.activeClass))); 2260 } 2261 if(hasDrop) { 2262 self.createList(listDrop, level + 1); 2263 } 2264 }); 2265 }, 2266 createOption: function(link, hasDrop, level, selected) { 2267 var optionHTML = this.getLevelIndent(level) + link.html(); 2268 return $('<option>').html(optionHTML) 2269 .addClass(this.options.levelPrefixClass + (level + 1)) 2270 .toggleClass(this.options.hasDropClass, hasDrop) 2271 .val(link.attr('href')).attr('selected', selected ? 'selected' : false); 2272 }, 2273 getLevelIndent: function(level) { 2274 return (new Array(level + 1)).join(this.options.levelIndentHTML); 2275 }, 2276 attachEvents: function() { 2277 // redirect on select change 2278 var self = this; 2279 this.select.change(function() { 2280 if(this.selectedIndex >= self.startIndex) { 2281 location.href = this.value; 2282 } 2283 }); 2284 } 2285 }; 2286 2287 // jquery pluginm interface 2288 $.fn.navigationSelect = function(opt) { 2289 return this.each(function() { 2290 new NavigationSelect($.extend({list: this}, opt)); 2291 }); 2292 }; 2293}(jQuery)); 2294 2295/* 2296 * Responsive Layout helper 2297 */ 2298 2299ResponsiveHelper = (function($){ 2300 // init variables 2301 var handlers = []; 2302 var win = $(window), prevWinWidth; 2303 var scrollBarWidth = 0; 2304 2305 // prepare resize handler 2306 function resizeHandler() { 2307 var winWidth = win.width() + scrollBarWidth; 2308 if(winWidth !== prevWinWidth) { 2309 prevWinWidth = winWidth; 2310 2311 // loop through range groups 2312 $.each(handlers, function(index, rangeObject){ 2313 // disable current active area if needed 2314 $.each(rangeObject.data, function(property, item) { 2315 if((winWidth < item.range[0] || winWidth > item.range[1]) && item.currentActive) { 2316 item.currentActive = false; 2317 if(typeof item.disableCallback === 'function') { 2318 item.disableCallback(); 2319 } 2320 } 2321 }); 2322 2323 // enable areas that match current width 2324 $.each(rangeObject.data, function(property, item) { 2325 if(winWidth >= item.range[0] && winWidth <= item.range[1] && !item.currentActive) { 2326 // make callback 2327 item.currentActive = true; 2328 if(typeof item.enableCallback === 'function') { 2329 item.enableCallback(); 2330 } 2331 } 2332 }); 2333 }); 2334 } 2335 } 2336 win.bind('load', function(){ 2337 if( cr_browser == 'Opera' || cr_browser == 'Firefox' ) { 2338 scrollBarWidth = window.innerWidth - $('body').width(); 2339 resizeHandler(); 2340 } 2341 win.bind('resize orientationchange', resizeHandler); 2342 }); 2343 2344 // range parser 2345 function parseRange(rangeStr) { 2346 var rangeData = rangeStr.split('..'); 2347 var x1 = parseInt(rangeData[0], 10) || -Infinity; 2348 var x2 = parseInt(rangeData[1], 10) || Infinity; 2349 return [x1, x2].sort(function(a, b){ 2350 return a - b; 2351 }); 2352 } 2353 2354 // export public functions 2355 return { 2356 addRange: function(ranges) { 2357 // parse data and add items to collection 2358 var result = {data:{}}; 2359 $.each(ranges, function(property, data){ 2360 result.data[property] = { 2361 range: parseRange(property), 2362 enableCallback: data.on, 2363 disableCallback: data.off 2364 }; 2365 }); 2366 handlers.push(result); 2367 2368 // call resizeHandler to recalculate all events 2369 resizeHandler(); 2370 } 2371 }; 2372}(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.