Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | 1x 1x 1x 1x 1x 16x 16x 16x 16x 1x 1x 1x 1x 1x 1x 1x 1x 16x 16x 16x 16x 16x 16x 16x 1x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 1x | ( function () { // Copied from mediawiki.requestIdleCallback var requestIdleCallbackInternal = function ( callback ) { setTimeout( function () { var start = ve.now(); callback( { didTimeout: false, timeRemaining: function () { // Hard code a target maximum busy time of 50 milliseconds return Math.max( 0, 50 - ( ve.now() - start ) ); } } ); }, 1 ); }; // eslint-disable-next-line compat/compat var requestIdleCallback = window.requestIdleCallback ? // Bind because it throws TypeError if context is not window // eslint-disable-next-line compat/compat window.requestIdleCallback.bind( window ) : requestIdleCallbackInternal; var EXPIRY_PREFIX = '_EXPIRY_'; /** * Implementation of ve.init.SafeStorage * * Duplicate of mediawiki.storage. * * @class ve.init.sa.SafeStorage * @extends ve.init.SafeStorage * * @constructor * @param {Storage|undefined} store The Storage instance to wrap around */ ve.init.sa.SafeStorage = function ( store ) { this.store = store; // Purge expired items once per page session var storage = this; setTimeout( function () { storage.clearExpired(); }, 2000 ); }; /** * @inheritdoc */ ve.init.sa.SafeStorage.prototype.get = function ( key ) { if ( this.isExpired( key ) ) { return null; } try { return this.store.getItem( key ); } catch ( e ) {} return false; }; /** * @inheritdoc */ ve.init.sa.SafeStorage.prototype.set = function ( key, value, expiry ) { if ( key.slice( 0, EXPIRY_PREFIX.length ) === EXPIRY_PREFIX ) { throw new Error( 'Key can\'t have a prefix of ' + EXPIRY_PREFIX ); } try { this.store.setItem( key, value ); this.setExpires( key, expiry ); return true; } catch ( e ) {} return false; }; /** * @inheritdoc */ ve.init.sa.SafeStorage.prototype.remove = function ( key ) { try { this.store.removeItem( key ); this.setExpires( key ); return true; } catch ( e ) {} return false; }; /** * @inheritdoc */ ve.init.sa.SafeStorage.prototype.getObject = function ( key ) { var json = this.get( key ); if ( json === false ) { return false; } try { return JSON.parse( json ); } catch ( e ) {} return null; }; /** * @inheritdoc */ ve.init.sa.SafeStorage.prototype.setObject = function ( key, value, expiry ) { var json; try { json = JSON.stringify( value ); return this.set( key, json, expiry ); } catch ( e ) {} return false; }; /** * @inheritdoc */ ve.init.sa.SafeStorage.prototype.setExpires = function ( key, expiry ) { if ( expiry ) { try { this.store.setItem( EXPIRY_PREFIX + key, Math.floor( Date.now() / 1000 ) + expiry ); } catch ( e ) {} } else { try { this.store.removeItem( EXPIRY_PREFIX + key ); } catch ( e ) {} } }; // Minimum amount of time (in milliseconds) for an iteration involving localStorage access. var MIN_WORK_TIME = 3; /** * @inheritdoc */ ve.init.sa.SafeStorage.prototype.clearExpired = function () { var storage = this; return this.getExpiryKeys().then( function ( keys ) { return $.Deferred( function ( d ) { requestIdleCallback( function iterate( deadline ) { while ( keys[ 0 ] !== undefined && deadline.timeRemaining() > MIN_WORK_TIME ) { var key = keys.shift(); if ( storage.isExpired( key ) ) { storage.remove( key ); } } Iif ( keys[ 0 ] !== undefined ) { // Ran out of time with keys still to remove, continue later requestIdleCallback( iterate ); } else { return d.resolve(); } } ); } ); } ); }; /** * @inheritdoc */ ve.init.sa.SafeStorage.prototype.getExpiryKeys = function () { var store = this.store; return $.Deferred( function ( d ) { requestIdleCallback( function ( deadline ) { var prefixLength = EXPIRY_PREFIX.length; var keys = []; var length = 0; try { length = store.length; } catch ( e ) {} // Optimization: If time runs out, degrade to checking fewer keys. // We will get another chance during a future page view. Iterate forward // so that older keys are checked first and increase likelihood of recovering // from key exhaustion. // // We don't expect to have more keys than we can handle in 50ms long-task window. // But, we might still run out of time when other tasks run before this, // or when the device receives UI events (especially on low-end devices). for ( var i = 0; ( i < length && deadline.timeRemaining() > MIN_WORK_TIME ); i++ ) { var key = null; try { key = store.key( i ); } catch ( e ) {} if ( key !== null && key.slice( 0, prefixLength ) === EXPIRY_PREFIX ) { keys.push( key.slice( prefixLength ) ); } } d.resolve( keys ); } ); } ).promise(); }; /** * @inheritdoc */ ve.init.sa.SafeStorage.prototype.isExpired = function ( key ) { var expiry; try { expiry = this.store.getItem( EXPIRY_PREFIX + key ); } catch ( e ) { return false; } return !!expiry && expiry < Math.floor( Date.now() / 1000 ); }; }() ); |