1/** 2* The CPageCache class allows you to store pages in memory. 3* 4* @param {int} maxSize - The maximum number of pages to store in memory. 5* @private 6* @class 7*/ 8function CPageCache(maxSize, batchSize, debug) { 9 var m_queue = []; 10 var m_p1Queue = []; // Priority 1 queue. These can only be bumped by other p1 pages. 11 var m_pages = {}, m_p1Ids = {}; 12 var m_maxSize = maxSize; 13 var m_batchSize = batchSize; 14 var m_maxP1Size = 10; 15 16 if (debug) { 17 window.cpagecache_pages = m_pages; 18 window.cpagecache_queue = m_queue; 19 } 20 21 // @param {Boolean} p1 - Pages the user actually visited are stored longer than preloads. 22 this.add = function(id, data, p1) { 23 if (p1) 24 _addPage(id, m_p1Queue, m_p1Ids, 1, m_maxP1Size); 25 _addPage(id, m_queue, m_pages, data, m_maxSize, m_p1Queue); 26 }; 27 this.remove = function(id) { 28 if (id in m_pages) { 29 m_queue.splice(m_queue.indexOf(id), 1); 30 delete m_pages[id]; 31 32 var p1Idx = m_p1Queue.indexOf(id); 33 if (p1Idx >= 0) { 34 m_queue.splice(p1Idx, 1); 35 delete m_p1Ids[id]; 36 } 37 } 38 }; 39 this.get = function(id) { 40 if (id in m_pages) { 41 // If it's accessed, it goes to the front. 42 _pushToFront(id, m_queue); 43 _pushToFront(id, m_p1Queue); 44 return m_pages[id]; 45 } 46 return null; 47 }; 48 this.has = function(id) { 49 return id in m_pages; 50 }; 51 52 // Load initial cache, based on hrefs in an element 53 this.load = function(elt, history) { 54 var self = this; 55 var ids = {}; 56 $('a', elt).each(function(idx, a) { 57 var href = a.getAttribute('href'); // Use getAttribute because some browsers make href appear to be canonical. 58 if (href && href.indexOf('://') < 0) { 59 var numParams = href.split('=').length; 60 if (href.indexOf('id=') >= 0) 61 numParams--; 62 if (numParams == 1) { 63 var pageinfo = history.getSwitchId(href); 64 if (pageinfo && !m_cache.has(pageinfo.id)) 65 ids[pageinfo.id] = 1; 66 } 67 } 68 }); 69 70 var idsA = []; 71 for (var id in ids) 72 idsA.push(id); 73 74 if (idsA.length > m_maxSize) { 75 // There are so many links that the chances of preloading the right one are basically zero. 76 // TODO: Sort by vertical position and preload near the top of the page? 77 } 78 else if (idsA.length > 0) { 79 if (idsA.length > m_maxSize) 80 idsA.length = m_maxSize; 81 82 // Split pages into at least 4 batches if possible. 83 var batchSize = m_batchSize; 84 if (idsA.length / batchSize < 4) 85 batchSize = Math.ceil(idsA.length / 4); 86 var requests = []; 87 for (var x=0; x<Math.ceil(idsA.length / batchSize); x++) { 88 var sublist = idsA.slice(x*batchSize, (x+1)*batchSize); 89 var params = {partial: 1}; 90 params['do'] = 'fastwiki_preload'; 91 params.fastwiki_preload_pages = sublist.join(','); 92 requests.push(params); 93 } 94 95 function doPost(params) { 96 m_debug && console.log("Preloading " + params.fastwiki_preload_pages); 97 $.post(DOKU_BASE + 'doku.php', params, function(data) { 98 var pages = data.split(JSINFO.fastwiki.preload_head); 99 for (var p=0; p<pages.length; p++) { 100 var line1End = pages[p].indexOf('\n'); 101 var id = pages[p].substr(0, line1End); 102 pages[p] = pages[p].substr(line1End+1); 103 m_debug && console.log("Loaded " + [id, pages[p].length]); 104 // If a bug causes a whole page to be loaded, don't cache it. 105 if (pages[p].indexOf('<body') >= 0) 106 m_debug && console.log("ERROR: Body found!"); 107 else 108 self.add(id, pages[p]); 109 } 110 111 if (requests.length > 0) 112 doPost(requests.shift()); 113 }, 'text'); 114 } 115 116 // Make the first 4 requests. Limit to 4 so as not to monopolize all the browser's sockets (there are 6 in modern browsers). 117 for (var x=0; x<Math.min(4, requests.length); x++) 118 doPost(requests.shift()); 119 } 120 }; 121 122 function _pushToFront(id, queue) { 123 var idx = queue.indexOf(id); 124 if (idx >= 0) { 125 queue.splice(idx, 1); 126 queue.push(id); 127 } 128 } 129 function _addPage(id, queue, hash, data, maxSize, exclude) { 130 if (id in hash) 131 _pushToFront(id, queue); 132 else if (data) { 133 if (queue.length > maxSize) { 134 if (exclude) { 135 for (var x=0; x<queue.length; x++) { 136 if (!exclude[queue[x]]) { 137 delete hash[queue[x]]; 138 queue.splice(x, 1); 139 } 140 } 141 } 142 else 143 delete hash[queue.shift()]; 144 } 145 queue.push(id); 146 } 147 148 if (data) 149 hash[id] = data; 150 } 151} 152