1/** 2 * Tab Visibility Handling 3 * 4 * Keeps track of when the current document is hidden from view. 5 * 6 */ 7tabVisibilityChange = { 8 9 /* 10 EXAMPLE USAGE: 11 12 tabVisibilityChange.on('change', function(e, tab){ 13 console.log(tab.hidden); 14 }) 15 16 OR: 17 18 tabVisibilityChange.create(document, { 19 change: function(e, tab){ 20 console.log(tab.hidden); 21 }, 22 visible: function(){ 23 console.log('visibubble!'); 24 }, 25 hidden: function(){ 26 console.log('hiddenaways'); 27 } 28 }); 29 30 */ 31 32 window: window, 33 document: document, 34 jQuery: jQuery, 35 36 //global: null, 37 /// exchange the above line for the commented one below 38 /// if you want a global event, 'tabvisibilitychange' to 39 /// be broadcast. Global can be set to any element or object 40 /// but window is obviously the most accessible. 41 global: window, 42 43 ns: 'tvc', 44 nsSeed: 1, 45 shared: null, 46 disabled: false, 47 delegate: null, 48 49 /** 50 * A shared listener is created for each unique document 51 * that tabVisibilityChange is asked to manage. 52 */ 53 sharedListener: { 54 55 eventTarget: null, 56 eventNames: [], 57 lastHidden: null, 58 disabled: false, 59 delegate: null, 60 61 /** 62 * standard create function, expects an instance of tabVisibilityChange 63 * as first argument. 64 */ 65 create: function( tvc, options ){ 66 console.log(tvc); 67 return this.prep.apply(Object.create(this), arguments); 68 }, 69 70 /** 71 * standard prep function, applies unique references to an otherwise 72 * cloned sharedListener. 73 */ 74 prep: function( tvc, options ){ 75 this.document = tvc.document; 76 this.window = tvc.window; 77 this.jQuery = tvc.jQuery; 78 this.global = tvc.global; 79 this.eventTarget = this.document; 80 this.delegate = this.jQuery( {} ); 81 /// create a global object, if requested 82 this.global = (this.global 83 ? this.jQuery(this.global) 84 : null 85 ); 86 /// check for which events to use 87 switch ( true ) { 88 case 'hidden' in this.document: this.eventNames.push('visibilitychange'); break; 89 case 'msHidden' in this.document: this.eventNames.push('msvisibilitychange'); break; 90 case 'mozHidden' in this.document: this.eventNames.push('mozvisibilitychange'); break; 91 case 'webkitHidden' in this.document: this.eventNames.push('webkitvisibilitychange'); break; 92 case 'onfocusin' in this.document: 93 this.eventNames.push('focusin'); 94 this.eventNames.push('focusout'); 95 break; 96 default: 97 this.eventTarget = this.window; 98 this.eventNames.push('pageshow'); 99 this.eventNames.push('pagehide'); 100 break; 101 } 102 /// proxy so that 'this' makes sense in listener() 103 this.listener = this.jQuery.proxy(this.listener, this); 104 /// go go go! 105 this.start(); 106 return this; 107 }, 108 109 110 /** 111 * The key/global listening behaviour 112 */ 113 listener: function( e ){ 114 if ( this.disabled ) { 115 return; 116 } 117 var hidden = null; 118 switch ( e.type ) { 119 case 'visibilitychange': hidden = this.document.hidden; break; 120 case 'mozvisibilitychange': hidden = this.document.mozHidden; break; 121 case 'webkitvisibilitychange': hidden = this.document.webkitHidden; break; 122 case 'msvisibilitychange': hidden = this.document.msHidden; break; 123 case 'focusin': hidden = false; break; 124 case 'focusout': hidden = true; break; 125 case 'pageshow': hidden = false; break; 126 case 'pagehide': hidden = true; break; 127 } 128 if ( hidden !== null && hidden !== this.lastHidden ) { 129 this.global && this.global.triggerHandler('tabvisibilitychange', { hidden: hidden }); 130 this.delegate.triggerHandler( hidden ? 'hidden' : 'visible' ); 131 this.delegate.triggerHandler('change', { hidden: hidden }); 132 this.lastHidden = hidden; 133 } 134 }, 135 136 /** 137 * Shared Listener management API 138 */ 139 start: function(){ 140 /// start listening 141 this.jQuery(this.eventTarget).on(this.eventNames.join(' '), this.listener); 142 }, 143 stop: function(){ 144 /// stop listening 145 this.jQuery(this.eventTarget).off(this.eventNames.join(' '), this.listener); 146 }, 147 148 /** 149 * Shared Listener existence API 150 */ 151 getFor: function( tvc ){ 152 return tvc.document['listeningForTVC']; 153 }, 154 existsFor: function( tvc ){ 155 return tvc.document && 'listeningForTVC' in tvc.document; 156 }, 157 createFor: function( tvc ){ 158 return (tvc.document['listeningForTVC'] = this.create( tvc )); 159 }, 160 161 /** 162 * Shared Listener events API 163 */ 164 on: function( name, callback ){ 165 if ( !callback ) { callback=name; name=null; }; 166 if ( !callback ) return;
167 if ( name && callback && callback.call ) { 168 this.delegate.on(name, callback); 169 } 170 else { 171 this.delegate.on(callback); 172 } 173 }, 174 off: function( name, callback ){ 175 if ( !callback ) { callback=name; name=null; }; 176 if ( !callback ) return; 177 if ( name && callback && callback.call ) { 178 this.delegate.off(name, callback); 179 } 180 else { 181 this.delegate.off(callback); 182 } 183 } 184 185 }, 186 187 /** 188 * Create an individual instance of tabVisibilityChange 189 */ 190 create: function( element, options ){ 191 return this.prep.apply(Object.create(this), arguments); 192 }, 193 194 /** 195 * Prep what makes this instance unique, in terms of references. 196 * 197 * element -- either a window, document or other element that 198 * references the "tab" you wish to watch over 199 * 200 * options -- futher config options 201 * 202 */ 203 prep: function( element, options ){ 204 /// force instance creation so this can't been called statically 205 if ( this === tabVisibilityChange ) { 206 return this.create.apply(this, arguments); 207 } 208 /// if we are a usual instance i.e. .create() has been called. 209 else { 210 this.ns = tabVisibilityChange.ns + (this.nsSeed++); 211 /// create a delegate for managing events 212 this.delegate = this.jQuery( {} ); 213 /// configure this based on element 214 this.configure( element || document ); 215 /// make sure our listener redirect knows that 'this' should be 216 this.sharedToLocalListener = this.jQuery.proxy(this.sharedToLocalListener, this); 217 /// make sure we have a shared listener based on document 218 this.shared = (this.sharedListener.existsFor( this ) 219 ? this.sharedListener.getFor( this ) 220 : this.sharedListener.createFor( this ) 221 ); 222 /// as long as we have a shared listener, we can continue 223 if ( this.shared ) { 224 /// we have a shared to local delegate step to allow for easy .off 225 /// behaviour, without the external user needing to know about 226 /// namespacing of events. Gives a slight overhead, but worth it 227 /// for a cleaner API. 228 this.delegateEar = {}; 229 this.delegateEar['visible' + '.' + this.ns] = this.sharedToLocalListener; 230 this.delegateEar['hidden' + '.' + this.ns] = this.sharedToLocalListener; 231 this.delegateEar['change' + '.' + this.ns] = this.sharedToLocalListener; 232 this.shared.on( this.delegateEar ); 233 /// check if we need our own local listener 234 if ( options ) { 235 /// only bother calling this if we have listeners as options 236 if ( options.change || options.visible || options.hidden ) { 237 /// pass our listener options to the shared instance. 238 this.delegate.on(options); 239 } 240 } 241 } 242 return this; 243 } 244 }, 245 246 /** 247 * Configure other elements based on a root 248 */ 249 configure: function( element ){ 250 if ( element === null ) { 251 element = options; 252 } 253 if ( element ) { 254 /// have we been passed a document object? 255 if ( element.doctype ) { 256 this.document = element; 257 this.window = this.document.defaultView; 258 } 259 /// have we been passed a window object? 260 else if ( element.open && element.location ) { 261 this.window = element; 262 this.document = this.window.document; 263 } 264 /// have we been passed a normal element? 265 else if ( element.nodeName ) { 266 this.document = element.document; 267 this.window = this.document && this.document.defaultView; 268 } 269 else { 270 if ( element.document && element.window ) { 271 this.window = element.window; 272 this.document = element.document; 273 } 274 else if ( element.document ) { 275 this.document = element.document; 276 this.window = this.document.defaultView; 277 } 278 else if ( element.window ) { 279 this.window = element.window; 280 this.document = this.window.document; 281 } 282 } 283 } 284 }, 285 286 /** 287 * Internal function for redirecting events from shared 288 * to the local delegate, and listening to 'disabled'. 289 */ 290 sharedToLocalListener: function(e, tab){ 291 if ( this.disabled ) { 292 return; 293 } 294 this.delegate.triggerHandler(e.type, tab); 295 }, 296
297 /** 298 * TVC Events API 299 */ 300 on: function( name, callback ){ 301 if ( this.delegate ) { 302 this.delegate.on.apply(this.delegate, arguments); 303 } 304 else { 305 return this.on.apply(this.create(document), arguments); 306 } 307 return this; 308 }, 309 off: function( name, callback ){ 310 if ( this.delegate ) { 311 this.delegate.off.apply(this.delegate, arguments); 312 } 313 else { 314 return this.off.apply(this.create(document), arguments); 315 } 316 return this; 317 }, 318 319 /** 320 * TVC Simple managment API, enable(), disable(), start() and stop() 321 */ 322 enable: function(){ 323 this.disabled = false; 324 }, 325 disable: function(){ 326 this.disabled = true; 327 } 328 329};
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.