1/** 2 * WP Content Copy Protection - protection activity counter. 3 * 4 * Counts the actions the protection on this page really blocks and reports 5 * them once, when the visitor leaves or hides the page. Pages where nothing 6 * was blocked send nothing at all. 7 * 8 * Counted (only when the matching layer is active on this page, see 9 * wccp_free_activity_tracker() in activity.php): 10 * s selecting content - drag-selecting, double-clicking or long-pressing text 11 * r right-clicks - anywhere except on images 12 * i image interactions - right-click, drag or long-press on an image 13 * k copy shortcuts - the Ctrl+A/C/X/V/S/U/I combinations the plugin blocks 14 * p print attempts - when a print-protection message is set 15 * 16 * Every type is capped per page view, listeners are passive and never block 17 * or change anything themselves, and nothing personal is sent: only the page 18 * id and the counts. 19 */ 20( function () { 21 'use strict'; 22 23 var cfg = window.wccpActivity; 24 25 if ( ! cfg || ! window.addEventListener || navigator.webdriver ) { 26 return; 27 } 28 29 var flags = cfg.f; 30 var cap = cfg.m || 5; 31 var counts = { s: 0, r: 0, i: 0, k: 0, p: 0 }; 32 var sent = { s: 0, r: 0, i: 0, k: 0, p: 0 }; 33 var visitSent = false; 34 var passive = { passive: true, capture: true }; 35 36 // Mirrors the keyCodes the selection layer blocks with Ctrl held. 37 var KEYS = [ 65, 67, 88, 85, 86, 83, 73 ]; 38 39 function add( key ) { 40 if ( counts[ key ] < cap ) { 41 counts[ key ]++; 42 } 43 } 44 45 // Form fields and editable areas are never blocked, so never counted. 46 function editable( node ) { 47 for ( ; node && node.nodeType === 1; node = node.parentElement ) { 48 if ( /^(INPUT|TEXTAREA|SELECT|OPTION)$/.test( node.nodeName ) || node.isContentEditable ) { 49 return true; 50 } 51 } 52 return false; 53 } 54 55 function image( node ) { 56 return !! node && node.nodeName === 'IMG'; 57 } 58 59 if ( flags.r ) { 60 window.addEventListener( 'contextmenu', function ( e ) { 61 add( image( e.target ) ? 'i' : 'r' ); 62 }, passive ); 63 64 window.addEventListener( 'dragstart', function ( e ) { 65 if ( image( e.target ) ) { 66 add( 'i' ); 67 } 68 }, passive ); 69 } 70 71 if ( flags.s ) { 72 window.addEventListener( 'keydown', function ( e ) { 73 if ( e.ctrlKey && ! e.repeat && KEYS.indexOf( e.keyCode || e.which ) !== -1 && ! editable( e.target ) ) { 74 add( 'k' ); 75 } 76 }, passive ); 77 } 78 79 if ( flags.s || flags.c ) { 80 var down = null; 81 82 window.addEventListener( 'mousedown', function ( e ) { 83 down = null; 84 85 if ( e.button !== 0 || editable( e.target ) || image( e.target ) ) { 86 return; 87 } 88 89 // A double click is a word selection; a triple click only extends it. 90 if ( e.detail === 2 ) { 91 add( 's' ); 92 return; 93 } 94 95 down = { x: e.clientX, y: e.clientY }; 96 }, passive ); 97 98 window.addEventListener( 'mousemove', function ( e ) { 99 if ( down && ( e.buttons & 1 ) && Math.abs( e.clientX - down.x ) + Math.abs( e.clientY - down.y ) > 16 ) { 100 down = null; 101 add( 's' ); 102 } 103 }, passive ); 104 105 window.addEventListener( 'mouseup', function () { 106 down = null; 107 }, passive ); 108 109 // Long press: the touch equivalent of selecting or saving. 110 var press = null; 111 112 function release() { 113 if ( press ) { 114 clearTimeout( press ); 115 press = null; 116 } 117 } 118 119 window.addEventListener( 'touchstart', function ( e ) { 120 release(); 121 122 var target = e.target; 123 124 if ( e.touches.length !== 1 || editable( target ) ) { 125 return; 126 } 127 128 press = setTimeout( function () { 129 press = null; 130 131 if ( image( target ) ) { 132 add( 'i' ); 133 } else if ( ! ( target.closest && target.closest( 'a' ) ) ) { 134 add( 's' ); 135 } 136 }, 650 ); 137 }, passive ); 138 139 window.addEventListener( 'touchmove', release, passive ); 140 window.addEventListener( 'touchend', release, passive ); 141 window.addEventListener( 'touchcancel', release, passive ); 142 } 143 144 if ( flags.p ) { 145 window.addEventListener( 'beforeprint', function () { 146 add( 'p' ); 147 } ); 148 } 149 150 function flush() { 151 var body = 'action=wccp_free_activity&o=' + encodeURIComponent( cfg.o ); 152 var any = false; 153 154 for ( var key in counts ) { 155 var delta = counts[ key ] - sent[ key ]; 156 157 if ( delta > 0 ) { 158 body += '&' + key + '=' + delta; 159 sent[ key ] = counts[ key ]; 160 any = true; 161 } 162 } 163 164 if ( ! any ) { 165 return; 166 } 167 168 if ( ! visitSent ) { 169 body += '&v=1'; 170 visitSent = true; 171 } 172 173 var type = 'application/x-www-form-urlencoded'; 174 175 try {
176 if ( navigator.sendBeacon && navigator.sendBeacon( cfg.u, new Blob( [ body ], { type: type } ) ) ) { 177 return; 178 } 179 } catch ( e ) {} 180 181 if ( window.fetch ) { 182 window.fetch( cfg.u, { method: 'POST', body: body, keepalive: true, credentials: 'same-origin', headers: { 'Content-Type': type } } ).catch( function () {} ); 183 } 184 } 185 186 document.addEventListener( 'visibilitychange', function () { 187 if ( document.visibilityState === 'hidden' ) { 188 flush(); 189 } 190 } ); 191 window.addEventListener( 'pagehide', flush ); 192}() );
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.