diff options
| author | Franck Cuny <franck@lumberjaph.net> | 2013-07-25 18:19:52 -0700 |
|---|---|---|
| committer | Franck Cuny <franck@lumberjaph.net> | 2013-07-25 18:19:52 -0700 |
| commit | b42718f7d9aac88a3048c616193f81deff49e401 (patch) | |
| tree | f964fbae3d81cb6af155d612985f4b344267f4b5 /assets/js/main.js | |
| parent | attempt for a new design (diff) | |
| download | lumberjaph-b42718f7d9aac88a3048c616193f81deff49e401.tar.gz | |
First attempt with the new theme.
Diffstat (limited to '')
| -rw-r--r-- | assets/js/main.js | 855 |
1 files changed, 855 insertions, 0 deletions
diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..ced911d --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,855 @@ +/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ +/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ + +window.matchMedia = window.matchMedia || (function( doc, undefined ) { + + "use strict"; + + var bool, + docElem = doc.documentElement, + refNode = docElem.firstElementChild || docElem.firstChild, + // fakeBody required for <FF4 when executed in <head> + fakeBody = doc.createElement( "body" ), + div = doc.createElement( "div" ); + + div.id = "mq-test-1"; + div.style.cssText = "position:absolute;top:-100em"; + fakeBody.style.background = "none"; + fakeBody.appendChild(div); + + return function(q){ + + div.innerHTML = "­<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>"; + + docElem.insertBefore( fakeBody, refNode ); + bool = div.offsetWidth === 42; + docElem.removeChild( fakeBody ); + + return { + matches: bool, + media: q + }; + + }; + +}( document )); + + + + + +/*! Respond.js v1.1.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ +(function( win ){ + + "use strict"; + + //exposed namespace + var respond = {}; + win.respond = respond; + + //define update even in native-mq-supporting browsers, to avoid errors + respond.update = function(){}; + + //expose media query support flag for external use + respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches; + + //if media queries are supported, exit here + if( respond.mediaQueriesSupported ){ + return; + } + + //define vars + var doc = win.document, + docElem = doc.documentElement, + mediastyles = [], + rules = [], + appendedEls = [], + parsedSheets = {}, + resizeThrottle = 30, + head = doc.getElementsByTagName( "head" )[0] || docElem, + base = doc.getElementsByTagName( "base" )[0], + links = head.getElementsByTagName( "link" ), + requestQueue = [], + + //loop stylesheets, send text content to translate + ripCSS = function(){ + + for( var i = 0; i < links.length; i++ ){ + var sheet = links[ i ], + href = sheet.href, + media = sheet.media, + isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; + + //only links plz and prevent re-parsing + if( !!href && isCSS && !parsedSheets[ href ] ){ + // selectivizr exposes css through the rawCssText expando + if (sheet.styleSheet && sheet.styleSheet.rawCssText) { + translate( sheet.styleSheet.rawCssText, href, media ); + parsedSheets[ href ] = true; + } else { + if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) || + href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){ + requestQueue.push( { + href: href, + media: media + } ); + } + } + } + } + makeRequests(); + }, + + //recurse through request queue, get css text + makeRequests = function(){ + if( requestQueue.length ){ + var thisRequest = requestQueue.shift(); + + ajax( thisRequest.href, function( styles ){ + translate( styles, thisRequest.href, thisRequest.media ); + parsedSheets[ thisRequest.href ] = true; + + // by wrapping recursive function call in setTimeout + // we prevent "Stack overflow" error in IE7 + win.setTimeout(function(){ makeRequests(); },0); + } ); + } + }, + + //find media blocks in css text, convert to style blocks + translate = function( styles, href, media ){ + var qs = styles.match( /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ), + ql = qs && qs.length || 0; + + //try to get CSS path + href = href.substring( 0, href.lastIndexOf( "/" ) ); + + var repUrls = function( css ){ + return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" ); + }, + useMedia = !ql && media; + + //if path exists, tack on trailing slash + if( href.length ){ href += "/"; } + + //if no internal queries exist, but media attr does, use that + //note: this currently lacks support for situations where a media attr is specified on a link AND + //its associated stylesheet has internal CSS media queries. + //In those cases, the media attribute will currently be ignored. + if( useMedia ){ + ql = 1; + } + + for( var i = 0; i < ql; i++ ){ + var fullq, thisq, eachq, eql; + + //media attr + if( useMedia ){ + fullq = media; + rules.push( repUrls( styles ) ); + } + //parse for styles + else{ + fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1; + rules.push( RegExp.$2 && repUrls( RegExp.$2 ) ); + } + + eachq = fullq.split( "," ); + eql = eachq.length; + + for( var j = 0; j < eql; j++ ){ + thisq = eachq[ j ]; + mediastyles.push( { + media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all", + rules : rules.length - 1, + hasquery : thisq.indexOf("(") > -1, + minw : thisq.match( /\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ), + maxw : thisq.match( /\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ) + } ); + } + } + + applyMedia(); + }, + + lastCall, + + resizeDefer, + + // returns the value of 1em in pixels + getEmValue = function() { + var ret, + div = doc.createElement('div'), + body = doc.body, + fakeUsed = false; + + div.style.cssText = "position:absolute;font-size:1em;width:1em"; + + if( !body ){ + body = fakeUsed = doc.createElement( "body" ); + body.style.background = "none"; + } + + body.appendChild( div ); + + docElem.insertBefore( body, docElem.firstChild ); + + ret = div.offsetWidth; + + if( fakeUsed ){ + docElem.removeChild( body ); + } + else { + body.removeChild( div ); + } + + //also update eminpx before returning + ret = eminpx = parseFloat(ret); + + return ret; + }, + + //cached container for 1em value, populated the first time it's needed + eminpx, + + //enable/disable styles + applyMedia = function( fromResize ){ + var name = "clientWidth", + docElemProp = docElem[ name ], + currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp, + styleBlocks = {}, + lastLink = links[ links.length-1 ], + now = (new Date()).getTime(); + + //throttle resize calls + if( fromResize && lastCall && now - lastCall < resizeThrottle ){ + win.clearTimeout( resizeDefer ); + resizeDefer = win.setTimeout( applyMedia, resizeThrottle ); + return; + } + else { + lastCall = now; + } + + for( var i in mediastyles ){ + if( mediastyles.hasOwnProperty( i ) ){ + var thisstyle = mediastyles[ i ], + min = thisstyle.minw, + max = thisstyle.maxw, + minnull = min === null, + maxnull = max === null, + em = "em"; + + if( !!min ){ + min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); + } + if( !!max ){ + max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); + } + + // if there's no media query at all (the () part), or min or max is not null, and if either is present, they're true + if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){ + if( !styleBlocks[ thisstyle.media ] ){ + styleBlocks[ thisstyle.media ] = []; + } + styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] ); + } + } + } + + //remove any existing respond style element(s) + for( var j in appendedEls ){ + if( appendedEls.hasOwnProperty( j ) ){ + if( appendedEls[ j ] && appendedEls[ j ].parentNode === head ){ + head.removeChild( appendedEls[ j ] ); + } + } + } + + //inject active styles, grouped by media type + for( var k in styleBlocks ){ + if( styleBlocks.hasOwnProperty( k ) ){ + var ss = doc.createElement( "style" ), + css = styleBlocks[ k ].join( "\n" ); + + ss.type = "text/css"; + ss.media = k; + + //originally, ss was appended to a documentFragment and sheets were appended in bulk. + //this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set, so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one! + head.insertBefore( ss, lastLink.nextSibling ); + + if ( ss.styleSheet ){ + ss.styleSheet.cssText = css; + } + else { + ss.appendChild( doc.createTextNode( css ) ); + } + + //push to appendedEls to track for later removal + appendedEls.push( ss ); + } + } + }, + //tweaked Ajax functions from Quirksmode + ajax = function( url, callback ) { + var req = xmlHttp(); + if (!req){ + return; + } + req.open( "GET", url, true ); + req.onreadystatechange = function () { + if ( req.readyState !== 4 || req.status !== 200 && req.status !== 304 ){ + return; + } + callback( req.responseText ); + }; + if ( req.readyState === 4 ){ + return; + } + req.send( null ); + }, + //define ajax obj + xmlHttp = (function() { + var xmlhttpmethod = false; + try { + xmlhttpmethod = new win.XMLHttpRequest(); + } + catch( e ){ + xmlhttpmethod = new win.ActiveXObject( "Microsoft.XMLHTTP" ); + } + return function(){ + return xmlhttpmethod; + }; + })(); + + //translate CSS + ripCSS(); + + //expose update for re-running respond later on + respond.update = ripCSS; + + //adjust on resize + function callMedia(){ + applyMedia( true ); + } + if( win.addEventListener ){ + win.addEventListener( "resize", callMedia, false ); + } + else if( win.attachEvent ){ + win.attachEvent( "onresize", callMedia ); + } +})(this); + +/*! responsive-nav.js v1.0.14 + * https://github.com/viljamis/responsive-nav.js + * http://responsive-nav.com + * + * Copyright (c) 2013 @viljamis + * Available under the MIT license + */ + +/* jshint strict:false, forin:false, noarg:true, noempty:true, eqeqeq:true, +boss:true, bitwise:true, browser:true, devel:true, indent:2 */ +/* exported responsiveNav */ + +var responsiveNav = (function (window, document) { + + var computed = !!window.getComputedStyle; + + // getComputedStyle polyfill + if (!window.getComputedStyle) { + window.getComputedStyle = function(el) { + this.el = el; + this.getPropertyValue = function(prop) { + var re = /(\-([a-z]){1})/g; + if (prop === "float") { + prop = "styleFloat"; + } + if (re.test(prop)) { + prop = prop.replace(re, function () { + return arguments[2].toUpperCase(); + }); + } + return el.currentStyle[prop] ? el.currentStyle[prop] : null; + }; + return this; + }; + } + + var nav, + opts, + navToggle, + docEl = document.documentElement, + head = document.getElementsByTagName("head")[0], + styleElement = document.createElement("style"), + navOpen = false, + + // fn arg can be an object or a function, thanks to handleEvent + // read more at: http://www.thecssninja.com/javascript/handleevent + addEvent = function (el, evt, fn, bubble) { + if ("addEventListener" in el) { + // BBOS6 doesn't support handleEvent, catch and polyfill + try { + el.addEventListener(evt, fn, bubble); + } catch (e) { + if (typeof fn === "object" && fn.handleEvent) { + el.addEventListener(evt, function (e) { + // Bind fn as this and set first arg as event object + fn.handleEvent.call(fn, e); + }, bubble); + } else { + throw e; + } + } + } else if ("attachEvent" in el) { + // check if the callback is an object and contains handleEvent + if (typeof fn === "object" && fn.handleEvent) { + el.attachEvent("on" + evt, function () { + // Bind fn as this + fn.handleEvent.call(fn); + }); + } else { + el.attachEvent("on" + evt, fn); + } + } + }, + + removeEvent = function (el, evt, fn, bubble) { + if ("removeEventListener" in el) { + try { + el.removeEventListener(evt, fn, bubble); + } catch (e) { + if (typeof fn === "object" && fn.handleEvent) { + el.removeEventListener(evt, function (e) { + fn.handleEvent.call(fn, e); + }, bubble); + } else { + throw e; + } + } + } else if ("detachEvent" in el) { + if (typeof fn === "object" && fn.handleEvent) { + el.detachEvent("on" + evt, function () { + fn.handleEvent.call(fn); + }); + } else { + el.detachEvent("on" + evt, fn); + } + } + }, + + getFirstChild = function (e) { + var firstChild = e.firstChild; + // skip TextNodes + while (firstChild !== null && firstChild.nodeType !== 1) { + firstChild = firstChild.nextSibling; + } + return firstChild; + }, + + setAttributes = function (el, attrs) { + for (var key in attrs) { + el.setAttribute(key, attrs[key]); + } + }, + + addClass = function (el, cls) { + el.className += " " + cls; + el.className = el.className.replace(/(^\s*)|(\s*$)/g,""); + }, + + removeClass = function (el, cls) { + var reg = new RegExp("(\\s|^)" + cls + "(\\s|$)"); + el.className = el.className.replace(reg, " ").replace(/(^\s*)|(\s*$)/g,""); + }, + + ResponsiveNav = function (el, options) { + var i; + + // Default options + this.options = { + animate: true, // Boolean: Use CSS3 transitions, true or false + transition: 400, // Integer: Speed of the transition, in milliseconds + label: "Menu", // String: Label for the navigation toggle + insert: "after", // String: Insert the toggle before or after the navigation + customToggle: "", // Selector: Specify the ID of a custom toggle + openPos: "relative", // String: Position of the opened nav, relative or static + jsClass: "js", // String: 'JS enabled' class which is added to <html> el + init: function(){}, // Function: Init callback + open: function(){}, // Function: Open callback + close: function(){} // Function: Close callback + }; + + // User defined options + for (i in options) { + this.options[i] = options[i]; + } + + // Adds "js" class for <html> + addClass(docEl, this.options.jsClass); + + // Wrapper + this.wrapperEl = el.replace("#", ""); + if (document.getElementById(this.wrapperEl)) { + this.wrapper = document.getElementById(this.wrapperEl); + } else { + // If el doesn't exists, stop here. + throw new Error("The nav element you are trying to select doesn't exist"); + } + + // Inner wrapper + this.wrapper.inner = getFirstChild(this.wrapper); + + // For minification + opts = this.options; + nav = this.wrapper; + + // Init + this._init(this); + }; + + ResponsiveNav.prototype = { + // Public methods + destroy: function () { + this._removeStyles(); + removeClass(nav, "closed"); + removeClass(nav, "opened"); + nav.removeAttribute("style"); + nav.removeAttribute("aria-hidden"); + nav = null; + _instance = null; + + removeEvent(window, "load", this, false); + removeEvent(window, "resize", this, false); + removeEvent(navToggle, "mousedown", this, false); + removeEvent(navToggle, "touchstart", this, false); + removeEvent(navToggle, "touchend", this, false); + removeEvent(navToggle, "keyup", this, false); + removeEvent(navToggle, "click", this, false); + + if (!opts.customToggle) { + navToggle.parentNode.removeChild(navToggle); + } else { + navToggle.removeAttribute("aria-hidden"); + } + }, + + toggle: function () { + if (!navOpen) { + removeClass(nav, "closed"); + addClass(nav, "opened"); + nav.style.position = opts.openPos; + setAttributes(nav, {"aria-hidden": "false"}); + + navOpen = true; + opts.open(); + } else { + removeClass(nav, "opened"); + addClass(nav, "closed"); + setAttributes(nav, {"aria-hidden": "true"}); + + if (opts.animate) { + setTimeout(function () { + nav.style.position = "absolute"; + }, opts.transition + 10); + } else { + nav.style.position = "absolute"; + } + + navOpen = false; + opts.close(); + } + }, + + handleEvent: function (e) { + var evt = e || window.event; + + switch (evt.type) { + case "mousedown": + this._onmousedown(evt); + break; + case "touchstart": + this._ontouchstart(evt); + break; + case "touchend": + this._ontouchend(evt); + break; + case "keyup": + this._onkeyup(evt); + break; + case "click": + this._onclick(evt); + break; + case "load": + this._transitions(evt); + this._resize(evt); + break; + case "resize": + this._resize(evt); + break; + } + }, + + // Private methods + _init: function () { + addClass(nav, "closed"); + this._createToggle(); + + addEvent(window, "load", this, false); + addEvent(window, "resize", this, false); + addEvent(navToggle, "mousedown", this, false); + addEvent(navToggle, "touchstart", this, false); + addEvent(navToggle, "touchend", this, false); + addEvent(navToggle, "keyup", this, false); + addEvent(navToggle, "click", this, false); + }, + + _createStyles: function () { + if (!styleElement.parentNode) { + head.appendChild(styleElement); + } + }, + + _removeStyles: function () { + if (styleElement.parentNode) { + styleElement.parentNode.removeChild(styleElement); + } + }, + + _createToggle: function () { + if (!opts.customToggle) { + var toggle = document.createElement("a"); + toggle.innerHTML = opts.label; + setAttributes(toggle, { + "href": "#", + "id": "nav-toggle" + }); + + if (opts.insert === "after") { + nav.parentNode.insertBefore(toggle, nav.nextSibling); + } else { + nav.parentNode.insertBefore(toggle, nav); + } + + navToggle = document.getElementById("nav-toggle"); + } else { + var toggleEl = opts.customToggle.replace("#", ""); + + if (document.getElementById(toggleEl)) { + navToggle = document.getElementById(toggleEl); + } else { + throw new Error("The custom nav toggle you are trying to select doesn't exist"); + } + } + }, + + _preventDefault: function(e) { + if (e.preventDefault) { + e.preventDefault(); + e.stopPropagation(); + } else { + e.returnValue = false; + } + }, + + _onmousedown: function (e) { + var evt = e || window.event; + // If the user isn't right clicking: + if (!(evt.which === 3 || evt.button === 2)) { + this._preventDefault(e); + this.toggle(e); + } + }, + + _ontouchstart: function (e) { + // Touchstart event fires before + // the mousedown and can wipe it + navToggle.onmousedown = null; + this._preventDefault(e); + this.toggle(e); + }, + + _ontouchend: function () { + // Prevents ghost click from happening on some Android browsers + var that = this; + nav.addEventListener("click", that._preventDefault, true); + setTimeout(function () { + nav.removeEventListener("click", that._preventDefault, true); + }, opts.transition); + }, + + _onkeyup: function (e) { + var evt = e || window.event; + if (evt.keyCode === 13) { + this.toggle(e); + } + }, + + _onclick: function (e) { + // For older browsers (looking at IE) + this._preventDefault(e); + }, + + _transitions: function () { + if (opts.animate) { + var objStyle = nav.style, + transition = "max-height " + opts.transition + "ms"; + + objStyle.WebkitTransition = transition; + objStyle.MozTransition = transition; + objStyle.OTransition = transition; + objStyle.transition = transition; + } + }, + + _calcHeight: function () { + var savedHeight = nav.inner.offsetHeight, + innerStyles = "#" + this.wrapperEl + ".opened{max-height:" + savedHeight + "px}"; + + // Hide from old IE + if (computed) { + styleElement.innerHTML = innerStyles; + innerStyles = ""; + } + }, + + _resize: function () { + if (window.getComputedStyle(navToggle, null).getPropertyValue("display") !== "none") { + setAttributes(navToggle, {"aria-hidden": "false"}); + + // If the navigation is hidden + if (nav.className.match(/(^|\s)closed(\s|$)/)) { + setAttributes(nav, {"aria-hidden": "true"}); + nav.style.position = "absolute"; + } + + this._createStyles(); + this._calcHeight(); + } else { + setAttributes(navToggle, {"aria-hidden": "true"}); + setAttributes(nav, {"aria-hidden": "false"}); + nav.style.position = opts.openPos; + this._removeStyles(); + } + + // Init callback + opts.init(); + } + + }; + + var _instance; + function rn (el, options) { + if (!_instance) { + _instance = new ResponsiveNav(el, options); + } + return _instance; + } + + return rn; +})(window, document); + + +/*global jQuery */ +/*jshint multistr:true browser:true */ +/*! +* FitVids 1.0 +* +* Copyright 2011, Chris Coyier - http://css-tricks.com + Dave Rupert - http://daverupert.com +* Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/ +* Released under the WTFPL license - http://sam.zoy.org/wtfpl/ +* +* Date: Thu Sept 01 18:00:00 2011 -0500 +*/ + +(function( $ ){ + + "use strict"; + + $.fn.fitVids = function( options ) { + var settings = { + customSelector: null + }; + + var div = document.createElement('div'), + ref = document.getElementsByTagName('base')[0] || document.getElementsByTagName('script')[0]; + + div.className = 'fit-vids-style'; + div.innerHTML = '­<style> \ + .fluid-width-video-wrapper { \ + width: 100%; \ + position: relative; \ + padding: 0; \ + } \ + \ + .fluid-width-video-wrapper iframe, \ + .fluid-width-video-wrapper object, \ + .fluid-width-video-wrapper embed { \ + position: absolute; \ + top: 0; \ + left: 0; \ + width: 100%; \ + height: 100%; \ + } \ + </style>'; + + ref.parentNode.insertBefore(div,ref); + + if ( options ) { + $.extend( settings, options ); + } + + return this.each(function(){ + var selectors = [ + "iframe[src*='player.vimeo.com']", + "iframe[src*='www.youtube.com']", + "iframe[src*='www.youtube-nocookie.com']", + "iframe[src*='www.kickstarter.com']", + "object", + "embed" + ]; + + if (settings.customSelector) { + selectors.push(settings.customSelector); + } + + var $allVideos = $(this).find(selectors.join(',')); + + $allVideos.each(function(){ + var $this = $(this); + if (this.tagName.toLowerCase() === 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; } + var height = ( this.tagName.toLowerCase() === 'object' || ($this.attr('height') && !isNaN(parseInt($this.attr('height'), 10))) ) ? parseInt($this.attr('height'), 10) : $this.height(), + width = !isNaN(parseInt($this.attr('width'), 10)) ? parseInt($this.attr('width'), 10) : $this.width(), + aspectRatio = height / width; + if(!$this.attr('id')){ + var videoID = 'fitvid' + Math.floor(Math.random()*999999); + $this.attr('id', videoID); + } + $this.wrap('<div class="fluid-width-video-wrapper"></div>').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+"%"); + $this.removeAttr('height').removeAttr('width'); + }); + }); + }; +})( jQuery ); + +/*! Plugin options and other jQuery stuff */ + +// Responsive Nav +var navigation = responsiveNav("#site-nav", { // Selector: The ID of the wrapper + animate: true, // Boolean: Use CSS3 transitions, true or false + transition: 400, // Integer: Speed of the transition, in milliseconds + label: "<i class='icon-reorder'></i> Menu", // String: Label for the navigation toggle + insert: "before", // String: Insert the toggle before or after the navigation + customToggle: "", // Selector: Specify the ID of a custom toggle + openPos: "relative", // String: Position of the opened nav, relative or static + jsClass: "js", // String: 'JS enabled' class which is added to <html> el + init: function(){}, // Function: Init callback + open: function(){}, // Function: Open callback + close: function(){} // Function: Close callback +}); + +// FitVids options +$(function() { + $("article").fitVids(); +});
\ No newline at end of file |
