(function($) { // Utils var gncaUtils = { saveCookie: function(name, value, days) { var expDate = new Date(); expDate.setTime(expDate.getTime() + (days * 24 * 3000 * 1000)); // add 24 hours jQuery.cookie(name, value, { path: '/', domain: window.location.hostname, expires: expDate }); }, removeCookie: function(name) { jQuery.removeCookie(name, { path: '/', domain: window.location.hostname }); }, readCookie: function(name) { return jQuery.cookie(name); } }; window.GNCA_Utils = gncaUtils; })( jQuery ); ; (function($) { var isMobile; var isIOS; var isAndroid; var isSafari; var gncaWatcher = { isIOS: function() { if ( typeof(isIOS) === 'undefined' ) { isIOS = window.navigator.platform && /iP(hone|od|ad)/i.test(window.navigator.platform); } return isIOS; }, isAndroid: function() { if ( typeof(isAndroid) === 'undefined' ) { isAndroid = window.navigator.userAgent && /android/i.test(window.navigator.userAgent); } return isAndroid; }, isMobile: function() { if ( typeof(isMobile) === 'undefined' ) { isMobile = gncaWatcher.isAndroid() || gncaWatcher.isIOS(); } return isMobile; }, isSafari: function() { if ( typeof(isSafari) === 'undefined' ) { isSafari = window.navigator.userAgent && !(/chrome/i.test(window.navigator.userAgent)) && /safari/i.test(window.navigator.userAgent); } return isSafari; }, hasWifi: function() { return gncaWatcher.isAndroid() && navigator.connection && navigator.connection.type === 'wifi'; }, saveData: function() { if ( navigator && navigator.connection ) { // effective connection speed is 3g or lower if ( /\slow-2g|2g|3g/.test( navigator.connection.effectiveType ) ) { return true; } // Save-Data network header is set to true if ( true === navigator.connection.saveData ) { return true; } } return false; } }; window.GNCA_Watcher = gncaWatcher; })( jQuery ); ; if ( 'undefined' === typeof( jQuery.browser ) ) { function isIE() { var style = document.createElement( 'div' ).style; style.cssText = 'position:sticky;position:-webkit-sticky;position:-ms-sticky;'; var isSupported = -1 !== style.position.indexOf( 'sticky' ); return ! isSupported; } var browser = { msie: isIE() }; jQuery.browser = browser; } ; /*! * jQuery Cookie Plugin v1.3.1 * https://github.com/carhartl/jquery-cookie * * Copyright 2013 Klaus Hartl * Released under the MIT license */ (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as anonymous module. define(['jquery'], factory); } else { // Browser globals. factory(jQuery); } }(function ($) { var pluses = /\+/g; function raw(s) { return s; } function decoded(s) { return decodeURIComponent(s.replace(pluses, ' ')); } function converted(s) { if (s.indexOf('"') === 0) { // This is a quoted cookie as according to RFC2068, unescape s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); } try { return config.json ? JSON.parse(s) : s; } catch(er) {} } var config = $.cookie = function (key, value, options) { // write if (value !== undefined) { options = $.extend({}, config.defaults, options); if (typeof options.expires === 'number') { var days = options.expires, t = options.expires = new Date(); t.setDate(t.getDate() + days); } value = config.json ? JSON.stringify(value) : String(value); return (document.cookie = [ config.raw ? key : encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE options.path ? '; path=' + options.path : '', options.domain ? '; domain=' + options.domain : '', options.secure ? '; secure' : '' ].join('')); } // read var decode = config.raw ? raw : decoded; var cookies = document.cookie.split('; '); var result = key ? undefined : {}; for (var i = 0, l = cookies.length; i < l; i++) { var parts = cookies[i].split('='); var name = decode(parts.shift()); var cookie = decode(parts.join('=')); if (key && key === name) { result = converted(cookie); break; } if (!key) { result[name] = converted(cookie); } } return result; }; config.defaults = {}; $.removeCookie = function (key, options) { if ($.cookie(key) !== undefined) { $.cookie(key, '', $.extend(options, { expires: -1 })); return true; } return false; }; })); ; // This file is merely here to set up our global dom library var and ensure the plugins will direct to it if( window.wrap ){ window.jQuery = wrap; } else if( window.jQuery ){ window.wrap = jQuery; }; /*! A fix for the iOS orientationchange zoom bug. Script by @scottjehl, rebound by @wilto. MIT License. */ (function(w){ // This fix addresses an iOS bug, so return early if the UA claims it's something else. var ua = navigator.userAgent; if( !( /iPhone|iPad|iPod/.test( navigator.platform ) && /OS [1-5]_[0-9_]* like Mac OS X/i.test(ua) && ua.indexOf( "AppleWebKit" ) > -1 ) ){ return; } var doc = w.document; if( !doc.querySelector ){ return; } var meta = doc.querySelector( "meta[name=viewport]" ), initialContent = meta && meta.getAttribute( "content" ), disabledZoom = initialContent + ",maximum-scale=1", enabledZoom = initialContent + ",maximum-scale=10", enabled = true, x, y, z, aig; if( !meta ){ return; } function restoreZoom(){ meta.setAttribute( "content", enabledZoom ); enabled = true; } function disableZoom(){ meta.setAttribute( "content", disabledZoom ); enabled = false; } function checkTilt( e ){ aig = e.accelerationIncludingGravity; x = Math.abs( aig.x ); y = Math.abs( aig.y ); z = Math.abs( aig.z ); // If portrait orientation and in one of the danger zones if( (!w.orientation || w.orientation === 180) && ( x > 7 || ( ( z > 6 && y < 8 || z < 8 && y > 6 ) && x > 5 ) ) ){ if( enabled ){ disableZoom(); } } else if( !enabled ){ restoreZoom(); } } w.addEventListener( "orientationchange", restoreZoom, false ); w.addEventListener( "devicemotion", checkTilt, false ); })( this ); ; /* Extend some native JS objects */ if ( !String.trim ) { String.prototype.trim = String.prototype.trim || function trim() { return this.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }; }; /* DEFINE GLOBAL NAMESPACE */ if (typeof window.gNews == "undefined") window.gNews = {}; gNews.Common = function() { // get jQuery var $ = jQuery; // init on dom-ready $(function() { gNews.Common.init(); }); // PUBLIC PROPERTIES & METHODS return { // PROPERTIES isTouch: false, isMsTouch: false, isIOS: false, isAndroid: false, isBlackBerry: false, isWinPhone: false, isIE: false, isTrident: false, isWebkit: false, isSafari: false, isChrome: false, isFirefox: false, isFlash: false, isViewportSet: false, isOrientationChange: false, // to determine whether page is actively being touched or scrolling status: { isTouched: false, isScrolling: false }, // properties used for when the window is being scrolled (iOS only, usually) scrollPos: { current: { left:0, top:0 }, orig: { left:0, top:0 }, delta: { left:0, top:0 } }, scrollCallbacks: [], // array of objects to use to trigger custom jQuery events // format: [ {nodeObj, customEventName}, ... ] // properties used for when the window is resized resizeTimeout: false, resizeCallbacks: [], // array of objects to use to trigger custom jQuery events // format: [ {nodeObj, customEventName}, ... ] // METHODS (PRE DOM-LOAD) preInit: function() { //console.log('gNews.Common.preInit()'); // clear console on escape key hit, local only if (window.location.hostname == 'local.globalnews.ca') { $(document).on('keydown', function(ev) { if (typeof ev !== 'undefined') { keyPress = ev.keyCode; if (keyPress == 27 && console) console.clear(); } }); }; // get ref $html = $('html'); // is device iOS (sometimes Android handles things differently from iOS) this.isIOS = ( navigator.userAgent.match(/(iPad|iPhone|iPod)/gi) ? true : false ); if (this.isIOS ) { $( document.documentElement ).addClass( 'is_ios' ); } // is device Android this.isAndroid = ( navigator.userAgent.match(/Android/gi) ? true : false ); if (this.isAndroid ) { $( document.documentElement ).addClass( 'is_android' ); } // is device a BlackBerry this.isBlackBerry = ( navigator.userAgent.match(/BlackBerry|PlayBook|BB10/gi) ? true : false ); if (this.isBlackBerry ) { $( document.documentElement ).addClass( 'is_blackberry' ); } // is device a Windows Phone this.isWinPhone = ( navigator.userAgent.match(/Windows\sPhone/gi) ? true : false ); if (this.isWinPhone ) { $( document.documentElement ).addClass( 'is_winphone' ); } // handle IE browsers (add version number) this.isIE = ($.browser.msie) ? true : false; this.isTrident = ( navigator.userAgent.match(/Trident/gi) ? true : false ); if ( this.isIE ) { $html.addClass( 'is_ie is_ie'+ this.getIEVersion() ); }; // handle WebKit browsers this.isChrome = ($.browser.chrome) ? true : false; this.isSafari = ($.browser.safari) ? true : false; this.isWebkit = ($.browser.webkit) ? true : false; if (this.isWebkit) { $html.addClass('is_webkit'); if (this.isChrome) { $html.addClass('is_chrome'); } if (this.isSafari) { var versionRegex = /Version\/(\d+)./g; var versionMatch = versionRegex.exec(navigator.userAgent); if ( versionMatch && versionMatch[1] ) { version = parseInt( versionMatch[1], 10 ); // don't bother user agent sniffing for newer safari versions - they don't require the same CSS fixes if ( version < 10 ) { $html.addClass('is_safari'); } } } }; // is device touch capable? this.isTouch = ('ontouchend' in document.documentElement) ? true : false; // is this a touch-capable Microsoft IE browser? this.isMsTouch = ( window.navigator.msMaxTouchPoints>0 && window.navigator.msPointerEnabled ) ? true : false; // does this browser have Flash installed? try { var flashObj = new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); if ( flashObj ) { this.isFlash = true; $html.addClass('flash-enabled'); } else { $html.addClass('no-flash'); }; } catch(e) { if ( navigator.mimeTypes["application/x-shockwave-flash"] != undefined ) { this.isFlash = true; $html.addClass('flash-enabled'); } else { $html.addClass('no-flash'); }; }; // tell other apps whether the page is scrolling or not (this can mess stuff up sometimes), iOS only if (this.isTouch && this.isIOS) { // determine if a user is scrolling $( document ).on( 'touchstart touchmove touchend', $.proxy( this.handleIOSTouch, this ) ); $( window ).on( 'scroll', $.proxy( this.handleIOSScroll, this ) ); }; }, /* POST DOM-LOAD METHODS */ init: function() { // is device touch capable? if ( this.isTouch ) { $( document.documentElement ).addClass( 'touch-enabled' ); } else if (this.isMsTouch) { $( document.documentElement ).addClass( 'ms-touch' ); } else { $( document.documentElement ).addClass( 'no-touch' ); }; // touch screen devices need to keep track of state, sometimes if (this.isTouch) { // determine if orientation change has happened $( window ).on( 'orientationchange', $.proxy( this, 'handleOrientationChange' ) ); } else { // determine if window has changed size $( window ).on( 'resize', function( ev ) { // if we're already doing this, don't bother doing it again if (gNews.Common.resizeTimeout) clearTimeout(gNews.Common.resizeTimeout); // run method on resize gNews.Common.resizeTimeout = window.setTimeout( function() { gNews.Common.handleResize(); }, 200); }); }; }, /* METHODS FOR PAGE RESIZE */ // handle when users resize the page handleResize: function(ev) { for (var i in this.resizeCallbacks) { var callbackNode = this.resizeCallbacks[i].node; var callbackFunction = this.resizeCallbacks[i].callback; callbackNode.trigger( callbackFunction ); }; }, registerResizeCallback: function(node, callback) { //console.log('Common.registerResizeCallback()'); // make sure we don't already have one of these for (var i in this.resizeCallbacks) { if (this.resizeCallbacks[i].node==node && this.resizeCallbacks[i].callback==callback) return; }; // add callback var callbackObj = {}; callbackObj.node = node; callbackObj.callback = callback; this.resizeCallbacks.push( callbackObj ); }, removeResizeCallback: function(node, callback) { //console.log('Common.removeResizeCallback()'); for (var i in this.resizeCallbacks) { if (this.resizeCallbacks[i].node.get(0)==node.get(0) && this.resizeCallbacks[i].callback==callback) { this.resizeCallbacks.splice(i, 1); }; }; }, // do stuff you need to do after an orientation change handleOrientationChange: function(ev) { // at the very least, run the resize event this.handleResize(ev); }, /* METHODS FOR DETERMINING WHETHER PAGE IS ACTIVELY BEING SCROLLED / TOUCHED (iOS only) */ // status isScrolling: function() { // return status return this.status.isScrolling; }, handleIOSTouch: function( ev ) { // figure out what the page is doing this.setPosition( ev.type ); // set flags, where appropriate switch (ev.type) { case 'touchstart': this.status.isTouched = true; this.status.isScrolling = false; break; case 'touchmove': this.status.isScrolling = (this.scrollPos.delta.left!=0 || this.scrollPos.delta.top!=0) ? true : false; break; case 'touchend': this.status.isScrolling = (this.scrollPos.delta.left!=0 || this.scrollPos.delta.top!=0) ? true : false; this.status.isTouched = false; break; }; }, handleIOSScroll: function( ev ) { //console.log('Common.handleIOSScroll()'); // don't bother running if user hasn't scrolled if ( !this.status.isScrolling ) return; // figure out what the page is doing this.setPosition( ev.type ); // clear flags this.status.isTouched = false; this.status.isScrolling = false; // call any scroll callbacks we might have for (var i in this.scrollCallbacks) { var callbackNode = this.scrollCallbacks[i].node; var callbackFunction = this.scrollCallbacks[i].callback; callbackNode.trigger( callbackFunction ); }; }, setPosition: function( type ) { //console.log('Common.setPosition()'); // set current position this.scrollPos.current.left = window.pageXOffset; this.scrollPos.current.top = window.pageYOffset; // reset, or set deltas switch (type) { case 'touchstart': this.scrollPos.orig = {left:window.pageXOffset, top:window.pageYOffset}; this.scrollPos.delta = {left:0, top:0}; break; default: this.scrollPos.delta.left = this.scrollPos.orig.left - this.scrollPos.current.left; this.scrollPos.delta.top = this.scrollPos.orig.top - this.scrollPos.current.top; break; }; }, // note: this only works with iOS registerScrollCallback: function(node, callback) { //console.log('Common.registerScrollCallback()'); // make sure we don't already have one of these for (var i in this.scrollCallbacks) { if (this.scrollCallbacks[i].node==node && this.scrollCallbacks[i].callback==callback) return; }; // add callback var callbackObj = {}; callbackObj.node = node; callbackObj.callback = callback; this.scrollCallbacks.push( callbackObj ); }, removeScrollCallback: function(node, callback) { //console.log('Common.removeScrollCallback()'); for (var i in this.scrollCallbacks) { if (this.scrollCallbacks[i].node==node && this.scrollCallbacks[i].callback==callback) { this.scrollCallbacks.splice(i, 1); }; }; }, /* IE BROWSER DETECTION */ getIEVersion: function() { var version = -1; // Return value assumes failure. if ( this.isIE ) { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) version = parseFloat( RegExp.$1 ); }; return version; }, canPlayMp4: function() { var canPlay = false; var v = document.createElement('video'); if(v.canPlayType && v.canPlayType('video/mp4').replace(/no/, '')) { canPlay = true; } return canPlay; }, iFrameAutoHeight: function( $obj ) { $obj.style.height = $obj.contentWindow.document.body.scrollHeight + 'px'; }, EOF: null }; }(); // init right away gNews.Common.preInit(); /* handles resizing embeds */ gNews.Common.EmbedResizer = function() { // PRIVATE var _embeds = []; var _isInit = false; function _init() { //console.log('********** EmbedResizer._init()'); // double-check to make sure we haven't done this yet if ( _isInit ) return; // add eventListener var $body = $('html body'); $body.on('RecentPostsResizeEvent', function() { _resize(); }); gNews.Common.registerResizeCallback( $body, 'RecentPostsResizeEvent' ); // don't do this more than once _isInit = true; }; function _resize() { //console.log('EmbedResizer._resize()'); // loop through array and resize each embed for (var i=0; i<_embeds.length; i++) { _resizeEmbed( _embeds[i] ); }; }; function _resizeEmbed( $embedObj ) { //console.log('EmbedResizer._resizeEmbed()'); // get current object height var newW = $embedObj.width(); // set width to 100%, and height to ratio of orig height $embedObj.css('height', ( newW * $embedObj.data('origEmbedHeight') / $embedObj.data('origEmbedWidth') ) +1 ); }; // PUBLIC return { // this is how you interact with this object -- simply add embed's you want here... add: function( $embedObj ) { //console.log('EmbedResizer.add()'); // don't add if the embed is already in here if ( $embedObj.data('embedResizerIsInit') ) return; // make sure this is initialized the first time you use it if ( !_isInit ) _init(); // add data to node, to save original height/width var origH = $embedObj.height(); var origW = $embedObj.width(); $embedObj.data('embedResizerIsInit', true); $embedObj.data('origEmbedHeight', origH); $embedObj.data('origEmbedWidth', origW); $embedObj.attr('height', ''); $embedObj.attr('width', ''); $embedObj.css( 'width', '100%'); // set width to 100% now, do this on a timer to give the 100% width change time to take effect window.setTimeout( function() { _resizeEmbed( $embedObj ); }, 10 ); // add embed to array _embeds.push( $embedObj ); }, EOF: null } }(); // Handles Auto search box jQuery(function($){ var $searchInput = $('input.gnca-auto-search-box-wide'); var $searchButton = $searchInput.next( '.gnca-auto-search-box-wide-button' ); $searchInput .focus(function( ){ if( $(this).val() === $(this).attr('data-watermark') ) { $(this).val(''); $(this).addClass('focused'); } }) .blur(function( ){ if( $(this).val() === '' ) { $(this).val($(this).attr('data-watermark')); $(this).removeClass('focused'); } }) .on( 'keyup', function( event ) { if ( 13 === event.keyCode ) { // If user clicks 'Enter' instead of search button, execute the search sortSearchResults( $searchInput ); } }); $searchButton.on( 'click', function() { sortSearchResults( $searchInput ); }); function sortSearchResults( $searchInput ) { var search_term = $searchInput.val().toLowerCase(); var blocks = $searchInput.attr('data-blocks'); var index_elem = $searchInput.attr('data-index'); if( ! blocks ) return; if( search_term === '' || search_term === $searchInput.attr('data-watermark').toLowerCase() ) { $(blocks).css( 'display', '' ); } else { $(blocks).each(function(idx){ var look_for = false; if( index_elem ) { look_for = $(this).children( index_elem ).text().toLowerCase(); } else { look_for = $(this).text().toLowerCase(); } if( look_for.indexOf( search_term ) > -1 ){ $(this).css( 'display', '' ); } else { $(this).css( 'display', 'none' ); } }); } $searchInput.trigger( 'gnca-auto-search-complete' ); } }); ; (function( $ ) { var pluginName = 'gnca_socialShare', selector = 'data-socialshare', // Objects to work on root = false, // jQuery array of all the share elements on the page // data object - associative array with provider name as index countData = {}, // keep track of what data we're in the process of requesting, or already have timers = {}, // we don't want to resize THAT often right in a row // methods methods = { init: function() { //console.log('$().'+pluginName+'.init()'); // leave if we don't have any elements on the page if (root.length < 1) return; // create each block root.each(function( index, obj ) { methods._create( index, obj ); }); }, /* CREATE/DRAW METHODS */ _create: function( index, obj ) { //console.log(' $().'+pluginName+'._create('+ index +')'); // get obj to work on var $obj = $(obj); if ( $obj.attr( selector+ '-init' ) ) return; // get providers var providers = $obj.attr( selector ).split(','); var moreProviders = ($obj.attr( selector+'-more' )) ? $obj.attr( selector+'-more' ).split(',') : []; // leave if we don't have any providers to work with if (providers.length < 1) return; // set default URL/Title for this page if ( !$obj.attr( selector+'-url' ) || $obj.attr( selector+'-url' )===undefined ) $obj.attr( selector+'-url', document.URL ); if ( !$obj.attr( selector+'-title' ) || $obj.attr( selector+'-title' )===undefined ) { // look at openGraph title var $title = $('meta[property="og:title"]'); if ( $title.length > 0 && $title.attr('content') ) { // use openGraph Title $obj.attr( selector+'-title', $title.attr('content') ); } else { // use page title $obj.attr( selector+'-title', document.title ); }; }; // get title/url var key = encodeURIComponent( $obj.attr( selector+'-url' ) ); var title = $obj.attr( selector+'-title' ); // add data-identifier methods._setId( $obj, index ); // add countData object if ( !countData[key] ) countData[key] = {}; // create list var $list = $('