1(function ($) { 2 3 4 5 ///////////////////// 6 // to do 7 ///////////////////// 8 // rewrite this whole thing from scratch! 9 // change classes to data-attr? Had to be done for hideClass to allow targeting of SVG. Do the same for the rest? 10 // background color issue when hideBackgroundImages is true (double-check this is the case) 11 // further testing on other sites 12 // omits? requires styles to be inherited :/ 13 // if cookie value cannot be applied (if options are later removed) 14 // improve sizing array to add default options before cookie value is applied 15 // run function on initialize? 16 // improve array implementation of fonts, color and sizes? (split differently?) 17 // set options on click if no cookies exist? for better site performance if accessibility not used by visitor 18 // show alt tags over images / something like this? 19 20 $.fn.ocular = function (options) { 21 22 ///////////////////// 23 // defaults 24 ///////////////////// 25 $.fn.ocular.defaults = { 26 baseClass: "ocular", 27 borderClass: false, 28 borderWidth: '2', 29 colours: [ 30 ["#000000", "#FFFFFF"], 31 ["#000000", "#FFFF00"], 32 ["#FFFF00", "#0000FF"] 33 ], 34 coloursDisplay: 'button', 35 coloursTitle: "Colours", 36 customTargets: false, 37 fonts: [ 38 ["Georgia", ""], 39 ["Tahoma", ""], 40 ["Verdana", ""] 41 ], 42 fontsTitle: "Font family", 43 hideClass: false, 44 hideBackgroundImages: false, 45 imageToggle: false, 46 invertClass: false, 47 omitClass: false, 48 onChange: function( intOption ) { 49 }, 50 sizes: [ 51 ["Medium", "110%"], 52 ["Large", "120%"], 53 ["X Large", "130%"] 54 ], 55 sizesDisplay: 'button', 56 sizesTitle: "Font size" 57 } 58 59 60 61
62 ///////////////////// 63 // vars 64 ///////////////////// 65 var ocular = this, 66 opts = $.extend({}, $.fn.ocular.defaults, options), 67 $interface = this, 68 arrCookieCheck = ["colours", "fonts", "sizes"], 69 $html = $('html'), 70 strBaseClass = opts.baseClass, 71 strRootTarget = opts.customTargets, 72 strBorderClass = opts.borderClass, 73 strHideClass = opts.hideClass, 74 strInvertClass = opts.invertClass, 75 strOmitClass = opts.omitClass, 76 strBgImgs = opts.hideBackgroundImages, 77 arrBorderCheck = ['top', 'right', 'bottom', 'left'], 78 strTheme = "."+strBaseClass+"-theme-", 79 strColor = "."+strBaseClass+"-col", 80 strBg = "."+strBaseClass+"-bg", 81 strBgStyle = "background-color", 82 strBorder = "."+strBaseClass+"-border", 83 strInvert = "."+strBaseClass+"-invert", 84 strImgsOff = strBaseClass+'-imgsoff', 85 strBordercolor = "border-color", 86 strImportant = "!important;", 87 strOmits = "head,head *,script,img,style,.fa,." + strBaseClass + ",." + strBaseClass + " *", 88 cookieVal = 0, 89 selected = [], 90 strStyles = "", 91 blnCookies = true 92 93 if (typeof Cookies != "undefined"){ 94 blnCookies = Cookies.allowed() 95 } 96 97
98 ///////////////////// 99 // add relevant classes to the DOM 100 ///////////////////// 101 // add baseClass to target element 102 $interface.addClass( strBaseClass ) 103 104 // omit class 105 strOmitClass && (strOmits = strOmits + "," + strOmitClass + "," + strOmitClass + " *") 106 107 // elements to search through and apply classes to 108 $strAppendTargets = $('*').not( strOmits ) 109 110 // apply custom border classes 111 if (strBorderClass){ 112 splitCSV( strBorderClass, "-bordercustom") 113 } 114 115 // add to cookie check 116 if (opts.imageToggle){ 117 arrCookieCheck.push( "imageToggle" ) 118 } 119 120 // apply custom invert classes 121 if (strInvertClass){ 122 splitCSV( strInvertClass, "-invert") 123 // look for inverts inside inverts 124 $(strInvert).each(function(){ 125 var thisInvertedChild = $(this).find(strInvert) 126 if ( thisInvertedChild.length ){ 127 thisInvertedChild.addClass(strBaseClass+'-invert-child') 128 } 129 }); 130 } 131 132 // apply class to elements that should become hidden 133 if (strHideClass){ 134 splitCSV( strHideClass, "-hide") 135 } 136 137 138 // adjust CSS background property to override background images 139 if (strBgImgs){ 140 strBgStyle = "background" 141 } 142 143 // apply custom target classes (for adding the root class names to) 144 if (strRootTarget){ 145 // adjust the target elements to search 146 var customRootTarget = strRootTarget + "||" 147 customRootTarget = customRootTarget.replace(/,/g, " *,") 148 customRootTarget = customRootTarget.replace("||", " *") 149 customRootTarget = strRootTarget + "," + customRootTarget 150 // redefine now 151 $strAppendTargets = $(customRootTarget).not( strOmits ) 152 splitCSV( strRootTarget, "-root") 153 strRootTarget = $(strRootTarget) 154 } else { 155 // else add root classes to HTML 156 strRootTarget = $html 157 strRootTarget.addClass(strBaseClass + "-root") 158 } 159 160 // check for items with background colours and borders and set class names to them 161 $strAppendTargets.each(function(){ 162 //console.log( $this.prop('nodeName').toLowerCase() + " >> (classes = '" + $this.attr('class') + "' )"); 163 var $this = $(this), 164 thisBgImg = $this.css("background-image") 165 166 // bg color check 167 var stBgColor = $this.css("background-color") 168 if ( (stBgColor !== "rgba(0, 0, 0, 0)") && (stBgColor !== "transparent") ) { 169 $this.addClass(strBaseClass + "-bg") 170 } 171 // support for: 172 // linear-gradient 173 // radial-gradient 174 // repeating-linear-gradient 175 // repeating-radial-gradient 176 if ( (thisBgImg.search("gradient") == 7) ){ 177 $this.addClass(strBaseClass + "-gradient") 178 } 179 // bg img check 180 if (strBgImgs){ 181 if ( (thisBgImg !== "none") ){ 182 $this.addClass(strBaseClass + "-bg") 183 } 184 } 185 // border check 186 for (i = 0; i < arrBorderCheck.length; ++i) { 187 // check through each border edges 188 var strBorder = $this.css('border-' + arrBorderCheck[i] + '-width') 189 // if a width is defined, then this element should be targeted 190 if (strBorder !== "0px"){ 191 $this.addClass(strBaseClass + "-border") 192 // end loop as this element doesn't require further checking 193 break 194 } 195 } 196 // border image 197 var strBordImg = $this.css("border-image-source") 198 if ( (strBordImg !== undefined) && (strBordImg !== "none") ) { 199 $this.addClass(strBaseClass + "-bordercustom") 200 } 201 202 203 // also add color and font class to defined targets 204 $this.addClass(strBaseClass + "-col") 205 if (!$this.hasClass('fa')){ 206 $this.addClass(strBaseClass + "-font") 207 } 208 }); 209 210 211 212 213 // run through each cookie option 214 $.each(arrCookieCheck, function (i) { 215 var strThis = this.toString() 216 cookieVal = getCookie(this) 217 strThis == "imageToggle" ? ((cookieVal == "" || !blnCookie
217s) && (cookieVal = "On")) : ((cookieVal == "" || !blnCookies) && (cookieVal = "0")) 218 selected[this] = cookieVal 219 blnCookies && runCustom( strThis, cookieVal, true ) 220 }); 221 buildInterface( selected ) 222 $html.append("<style class='ocular:themes'>"+strStyles+"</style>") 223 224 225 226 227 228 ///////////////////// 229 // functions 230 ///////////////////// 231 // get cookie data 232 function getCookie( name ) { 233 var value = "; " + document.cookie, parts = value.split("; " + name + "=") 234 return parts.pop().split(";").shift() 235 } 236 // set cookie needs a name and an index value 237 function setCookie( cname, cvalue ) { 238 if (blnCookies){ 239 var d = new Date(), expires 240 d.setTime(d.getTime() + (360 * 1000 * 24 * 365 * 10)) 241 document.cookie = cname+"="+cvalue+";path=/;expires=" + d.toUTCString() 242 } 243 } 244 // split comma-separated elements and add relevant classes to them 245 function splitCSV( strInput, strOutputSuffix ){ 246 strInput = strInput 247 var csvSplit = strInput.split(",") 248 for (i = 0; i < csvSplit.length; ++i) { 249 strInput = csvSplit[i] 250 // use data attr to allow target of SVGs 251 $(strInput).addClass(strBaseClass + strOutputSuffix).attr('data-'+strBaseClass+strOutputSuffix, true) 252 } 253 } 254 // loadCustomFont 255 function loadCustomFont( strFontSrc ){ 256 if (!$("link[href='" + strFontSrc + "']").length){ 257 $("<link href='" + strFontSrc + "' rel='stylesheet'>").appendTo("head"); 258 } 259 } 260 // custom functions for each cookie option 261 function runCustom( name, index, boolCookieLoad ) { 262 if (name !== "imageToggle"){ 263 (index == "") && (index = 0) 264 index = parseInt(index) 265 } 266 267 var custom = { 268 colours: function () { 269 switchClasses( [["-theme-", index]] ) 270 }, 271 fonts: function () { 272 switchClasses( [["-font-", index]] ) 273 // load custom font on cookie check 274 275 if (boolCookieLoad){ 276 if ( (index != 0) || (index != "") ){ 277 index = index-1 278 } 279 } 280 281 if ( opts.fonts[ index ][1] ){ 282 loadCustomFont( opts.fonts[ index ][1] ) 283 } 284 }, 285 imageToggle: function () { 286 $('.img_toggle').text( index ).data('state', index) 287 index == "Off" ? strRootTarget.addClass(strImgsOff) : strRootTarget.removeClass(strImgsOff) 288 }, 289 sizes: function () { 290 function applySizes( intVal ){ 291 if (intVal == "100%"){ 292 intVal = "" 293 } 294 if (intVal !== undefined){ 295 strRootTarget.css( 'font-size', intVal ) 296 } 297 } 298 // differnet value needed on cookie check because sizes array does not have the 100% option added /* NEEDS IMPROVING */ 299 if (boolCookieLoad){ 300 index != 0 ? applySizes( opts.sizes[ index-1 ][1] ) : applySizes( '100%' ) 301 } else { 302 applySizes( opts.sizes[ index ][1] ) 303 } 304 } 305 }; 306 return custom[name]() 307 } 308 // remove existing classes from root elements, then add new ones if necessary 309 function switchClasses( removeArr ){ 310 var arrClass = strBaseClass + removeArr[0][0] 311 // remove matching class 312 strRootTarget.removeClass (function (i, css) { 313 // ^ = beginning 314 // | = alternation (basically an OR) 315 // \s = matches whitespace 316 // \S = match anything but a whitespace character 317 // /g = match all occurrences 318 // toString fixes issues with SVG 319 return (css.toString().match ( new RegExp( "(^|\\s)" + arrClass + "\\S", "g") ) || []).join(' ') 320 }); 321 var i = removeArr[0][1] 322 // if (i !== 0){
323 strRootTarget.addClass(arrClass + i) 324 // } 325 } 326 // generate <style> tags and append to HTML 327 function appendStyles( str ){ 328 strStyles+=str 329 } 330 // build the interface and add 'selected' available cookie data 331 function buildInterface( selected ){ 332 333 // run through each option 334 $.each( opts, function( optsName, optsList ){ 335 336 var optionType = "button", 337 strBtnText = "A", 338 strOptionTitle, 339 compiledOptions, 340 strOptsName = optsName.toString() 341 342 343 // if it's an option with multiple choices (size, colours, fonts) 344 if ( $.isArray(optsList) ){ 345 346 compiledOptions = $("<div class='" + strBaseClass + "__item--" + strOptsName + "'>") 347 var strOptionTitle = strOptsName 348 349 switch ( strOptsName ) { 350 case "colours": 351 strBtnText = "C" 352 strOptionTitle = opts.coloursTitle 353 optionType = opts.coloursDisplay 354 $.each( optsList, function (i) { // add theme names 355 this.unshift( "Theme " + (i+1) ) 356 }); 357 // add default item to beginning of array ( 100% for sizing ) 358 optsList.unshift(["Default", "inherit", "inherit"]) 359 break; 360 case "fonts": 361 optionType = "select" 362 strOptionTitle = opts.fontsTitle 363 // duplicate array items to match format of size and color options 364 $.each( optsList, function () { 365 this.push( this[0] ) 366 }); 367 // add default item to beginning of array ( 100% for sizing ) 368 optsList.unshift(["Default", "", ""]) 369 break; 370 case "sizes": 371 strOptionTitle = opts.sizesTitle 372 optionType = opts.sizesDisplay 373 // duplicate array items to match format of size and color options 374 $.each( optsList, function () { 375 this.push( this[1] ) 376 }); 377 // add default item to beginning of array ( 100% for sizing ) 378 optsList.unshift(["Default", "100%", "100%"]) 379 break; 380 default: 381 } 382 383 // if <select> is chosen, output that element and its functionality 384 if ( optionType === "select" ) { 385 $('<select>').on("change", function () { 386 var $this = $(this) 387 // apply selected across site and update cookie 388 applySelected( optsName, $this, $this.find(':selected').index(), true ) 389 // run onChange custom function 390 opts.onChange( $this.find(':selected').index() ) 391 }).appendTo( compiledOptions ) 392 } else { 393 $('<div>').appendTo( compiledOptions ) 394 } 395 // prepend the option name to the interface 396 compiledOptions.prepend('<h3>' + strOptionTitle + '</h3>') 397 398 399 // run through settings for each option 400 $.each( optsList, function (index) { 401 402 var optName = optsList[index][0], 403 optVal1 = optsList[index][1], 404 optVal2 = optsList[index][2], 405 selectedClass = "" 406 407 408 if (selected[strOptsName] == index) { 409 selectedClass = "selected" 410 } 411 412 switch ( strOptsName ) { 413 case "colours": 414 if (index !== 0){ 415 416 // 417 // MUST BE A BETTER WAY OF DOING THIS!! 418 // 419 420 /* 421 .oc-theme-1.oc-col, 422 .oc-theme-1 .oc-col, 423 .oc-theme-1 svg, 424 .oc-theme-1 .oc-col.oc-invert.oc-invert-child { 425 fill: #1 !important; 426 color: #1 !important; 427 }*/ 428 appendStyles(strTheme+index+" "+strColor+", "+strTheme+index+" svg, "+strTheme+index+" "+strColor+strInvert+strInvert+"-child{fill: "+optVal2+strImportant+"color:"+optVal2+" "+strImportant+"}") 429 430 /*.oc-theme-1 a { 431 text-decoration: underline; 432 }*/ 433 appendStyles(strTheme+index+strColor+" a{ text-decoration: underline; }") 434 435 /* 436 .oc-theme-1.oc-bg, 437 .oc-theme-1 .oc-bg, 438 .oc-theme-1 .oc-bg.oc-invert.oc-invert-child { 439 background-color: #2 !important; 440 }*/ 441 appendStyles(strTheme+index+strBg+", "+strTheme+index+" "+strBg+", "+strTheme+index+" "+strBg+strInvert+strInvert+"-child {"+strBgStyle+":"+optVal1+" "+strImportant+"}") 442 443 /* 444 .oc-theme-1.oc-border, 445 .oc-theme-1 .oc-border { 446 border-color: #1 !important; 447 }*/ 448 appendStyles(strTheme+index+strBorder+","+strTheme+index+" "+strBorder+"{"+strBordercolor+": "+optVal2+" "+strImportant+"}") 449 450 /* 451 .oc-theme-1 .oc-bordercustom { 452 border: 2px solid #1 !important; 453 }*/ 454 appendStyles(strTheme+index+" "+strBorder+"custom{border:"+opts.borderWidth+"px solid "+optVal2+" "+strImportant+"}") 455 456 /* 457 .oc-theme-1 .oc-invert, 458 .oc-theme-1.oc-invert.oc-bg, 459 .oc-theme-1 .oc-invert.oc-bg, 460 .oc-theme-1 .oc-invert .oc-bg, 461 .oc-theme-1.oc-invert .oc-bg, 462 .oc-theme-1 a:hover { 463 background-color: #1 !important; 464 }*/ 465 appendStyles(strTheme+index+" "+strInvert+","+strTheme+index+strInvert+strBg+","+strTheme+index+" "+strInvert+strBg+","+strTheme+index+" "+strInvert+" "+strBg+","+strTheme+index+strInvert+" "+strBg+", "+strTheme+index+" a:hover {"+strBgStyle+": "+optVal2+" "+strImportant+"}") 466 467 /* 468 .oc-theme-1 .oc-gradient { 469 background-image: none !important; 470 } 471 */ 472 appendStyles(strTheme+index+" ."+strBaseClass+"-gradient { background-image: none "+strImportant+"}") 473 474 /* 475 .oc-theme-1.oc-invert.oc-col, 476 .oc-theme-1 .oc-invert.oc-col, 477 .oc-theme-1 .oc-invert .oc-col, 478 .oc-theme-1.oc-invert .oc-col, 479 .oc-theme-1 .oc-invert svg, 480 .oc-theme-1 a:hover, 481 .oc-theme-1 a:hover * { 482 fill: #2 !important; 483 color: #2 !important; 484 }*/ 485 appendStyles(strTheme+index+strInvert+strColor+","+strTheme+index+" "+strInvert+strColor+","+strTheme+index+" "+strInvert+" "+strColor+","+strTheme+index+strInvert+" "+strColor+","+strTheme+index+" "+strInvert+" svg, "+strTheme+index+" a:hover, "+strTheme+index+" a:hover * { fill:"+optVal1+" "+strImportant+"color:"+optVal1+" "+strImportant+"}") 486 487 /* 488 .oc-theme-1 .oc-invert.oc-border, 489 .oc-theme-1 .oc-invert .oc-border, 490 .oc-theme-1 .oc-invert.oc-bordercustom, 491 .oc-theme-1 .oc-invert .oc-bordercustom { 492 border-color: #2 !important; 493 }*/ 494 appendStyles(strTheme+index+" "+strInvert+strBorder+", "+strTheme+index+" "+strInvert+" "+strBorder+", "+strTheme+index+" "+strInvert+"."+strBaseClass+"-bordercustom "+strTheme+index+" "+strInvert+" ."+strBaseClass+"-bordercustom {"+strBordercolor+": "+optVal1+" "+strImportant+"}") 495 496 /*.oc-theme-1 .oc-invert a:hover { 497 background-color: #2 !important; 498 }*/ 499 appendStyles(strTheme+index+" ."+strBaseClass+"-invert a:hover," + strTheme+index+" ."+strBaseClass+"-invert a:hover * {"+"color:"+optVal2+" "+strImportant+"}"+strTheme+index+" ."+strBaseClass+"-invert a:hover {"+strBgStyle+":"+optVal1+" "+strImportant+"}"); 500 /* 501 .oc-theme-1 .oc-hide { 502 display: none !important; 503 }*/ 504 appendStyles(strTheme+index+" *[data-ocular-hide='true'] {display:none "+strImportant+"}") 505 506 /* 507 .oc-theme-1 *:before, 508 .oc-theme-1 *:after 509 */ 510 appendStyles(strTheme+index+" *:not('.fa'):before,"+strTheme+index+" *:after{visibility:hidden "+strImportant+"}") 511 512 } 513 break; 514 case "fonts": 515 if (index !== 0){ 516 /* 517 .oc-font-1, 518 .oc-font-1 .oc-font 519 */ 520 appendStyles("."+strBaseClass+"-font-"+index+",."+strBaseClass+"-font-"+index+" ."+strBaseClass+"-font{font-family:"+optName+";}") 521 break; 522 } 523 524 default: 525 } 526 527 if ( optionType === "select" ) { 528 $('<option '+selectedClass+' style="font-family:'+optVal1+';">'+optName+'</option>').appendTo( compiledOptions.find('select') ) 529 } else { 530 $('<button class="'+selectedClass+'"><span style="background:'+optVal1+';color:'+optVal2+';font-size:'+optVal2+';color:'+optVal2+'">'+strBtnText+'</span></button>').on("click", function () { 531 // apply selected across site and update cookie 532 applySelected( optsName, $(this), index, true )
533 // run onChange custom function 534 opts.onChange( index ) 535 }).appendTo( compiledOptions.find('div') ) 536 } 537 538 }); 539 } 540 541 // add resulting option elements to target element 542 $interface.append( compiledOptions ) 543 544 // set images button 545 if ( opts.imageToggle && strOptsName == "imageToggle") { 546 547 appendStyles('.'+strImgsOff+' img{visibility:hidden '+strImportant+'}.'+strImgsOff+' .'+strBaseClass+'-bg{background-image:none '+strImportant+'}') 548 var strSelected; 549 selected['imageToggle'] == "Off" ? strSelected='selected' : strSelected=''; 550 $('<button class="'+strSelected+'">Images <span class="img_toggle" data-state="'+selected['imageToggle']+'">'+selected['imageToggle']+'</span></button>').on('click', function(){ 551 552 var $this = $(this).children('span'), 553 intStatus = $this.data('state'); 554 intStatus == "Off" ? intStatus = "On" : intStatus = "Off" 555 556 // apply selected across site and update cookie 557 applySelected( 'imageToggle', $this, intStatus, false ) 558 559 }).prependTo( $interface ) 560 561 } 562 563 }); 564 565 566 // apply selected across site and update cookie 567 function applySelected( optsName, el, index, boolClassSwitch ){ 568 (optsName !== "imageToggle") && (index = parseInt(index)) 569 570 // save the cookie 571 setCookie(optsName, index) 572 // changes classes if applicable 573 if (boolClassSwitch){ 574 el.addClass("selected").siblings().removeClass("selected") 575 } else { 576 el.parent().toggleClass("selected") 577 } 578 // runs stuff depending on the options selected 579 runCustom( optsName, index, false ) 580 } 581 582 583 } /* buildInterface END */ 584 585 586 587 return ocular.easterEgg = function (){ 588 var strEgg = $("."+strBaseClass+"_overlay") 589 if ( strEgg.length ){ 590 strEgg.remove() 591 } else { 592 $html.append("<div class='"+strBaseClass+"_overlay' style='position:fixed;top:0;left:0;right:0;bottom:0;opacity:.5;background:blue url(https://media.giphy.com/media/piNKuaEbyKhig/giphy.gif) no-repeat center/cover;background-blend-mode:screen;pointer-events:none;'>") 593 } 594 } 595 596 597 598 }; 599 600}(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.