This email address is being protected from spambots. You need JavaScript enabled to view it. > Apachine License Version 2.0 */ (function(){function b(a,b,e,f){var c=(a.className||"").split(/\s+/g);""===c[0]&&c.shift();var d=c.indexOf(b);0>d&&e&&c.push(b);0<=d&&f&&c.splice(d,1);a.className=c.join(" ");return 0<=d}if(!("classList"in document.createElement("div"))){var e={add:function(a){b(this.element,a,!0,!1)},contains:function(a){return b(this.element,a,!1,!1)},remove:function(a){b(this.element,a,!1,!0)},toggle:function(a){b(this.element,a,!0,!0)}};Object.defineProperty(HTMLElement.prototype,"classList",{get:function(){if(this._classList)return this._classList; var a=Object.create(e,{element:{value:this,writable:!1,enumerable:!0}});Object.defineProperty(this,"_classList",{value:a,writable:!1,enumerable:!1});return a},enumerable:!0})}})(); This email address is being protected from spambots. You need JavaScript enabled to view it. > and other contributors * https://github.com/coolwanglu/pdf2htmlEX/blob/master/share/LICENSE */ /* * Attention: * This files is to be optimized by closure-compiler, * so pay attention to the forms of property names: * * string/bracket form is safe, won't be optimized: * var obj={ 'a':'b' }; obj['a'] = 'b'; * name/dot form will be optimized, the name is likely to be modified: * var obj={ a:'b' }; obj.a = 'b'; * * Either form can be used for internal objects, * but must be consistent for each one respectively. * * string/bracket form must be used for external objects * e.g. DEFAULT_CONFIG, object stored in page-data * property names are part of the `protocol` in these cases. * */ 'use strict'; var pdf2htmlEX = window['pdf2htmlEX'] = window['pdf2htmlEX'] || {}; /** * @const * @struct */ var CSS_CLASS_NAMES = { page_frame : 'pf', page_content_box : 'pc', page_data : 'pi', background_image : 'bi', link : 'l', __dummy__ : 'no comma' }; /** * configurations of Viewer * @const * @dict */ var DEFAULT_CONFIG = { // id of the element to put the pages in 'container_id' : 'page-container', // id of the element for sidebar (to open and close) 'sidebar_id' : 'sidebar', // id of the element for outline 'outline_id' : 'outline', // class for the loading indicator 'loading_indicator_cls' : 'loading-indicator', // How many page shall we preload that are below the last visible page 'preload_pages' : 3, // how many ms should we wait before actually rendering the pages and after a scroll event 'render_timeout' : 100, // zoom ratio step for each zoom in/out event 'scale_step' : 0.9, // register global key handler 'key_handler' : true, // register hashchange handler 'hashchange_handler' : true, '__dummy__' : 'no comma' }; /** @const */ var EPS = 1e-6; /************************************/ /* utility function */ /** * @param{Array.} ctm */ function invert(ctm) { var det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; return [ ctm[3] / det ,-ctm[1] / det ,-ctm[2] / det ,ctm[0] / det ,(ctm[2] * ctm[5] - ctm[3] * ctm[4]) / det ,(ctm[1] * ctm[4] - ctm[0] * ctm[5]) / det ]; }; /** * @param{Array.} ctm * @param{Array.} pos */ function transform(ctm, pos) { return [ctm[0] * pos[0] + ctm[2] * pos[1] + ctm[4] ,ctm[1] * pos[0] + ctm[3] * pos[1] + ctm[5]]; }; /** * @param{Element} ele */ function get_page_number(ele) { return parseInt(ele.getAttribute('data-page-no'), 16); }; /** * @param{NodeList} eles */ function disable_dragstart(eles) { for (var i = 0, l = eles.length; i 0 ? config : {})); this.pages_loading = []; this.init_before_loading_content(); var self = this; document.addEventListener('DOMContentLoaded', function(){ self.init_after_loading_content(); }, false); }; Viewer.prototype = { scale : 1, /* * index of the active page (the one with largest visible area) * which estimates the page currently being viewed */ cur_page_idx : 0, /* * index of the first visible page * used when determining current view */ first_page_idx : 0, init_before_loading_content : function() { /* hide all pages before loading, will reveal only visible ones later */ this.pre_hide_pages(); }, init_after_loading_content : function() { this.sidebar = document.getElementById(this.config['sidebar_id']); this.outline = document.getElementById(this.config['outline_id']); this.container = document.getElementById(this.config['container_id']); this.loading_indicator = document.getElementsByClassName(this.config['loading_indicator_cls'])[0]; { // Open the outline if nonempty var empty = true; var nodes = this.outline.childNodes; for (var i = 0, l = nodes.length; i promise ? */ load_page : function(idx, pages_to_preload, callback) { var pages = this.pages; if (idx >= pages.length) return; // Page does not exist var cur_page = pages[idx]; if (cur_page.loaded) return; // Page is loaded if (this.pages_loading[idx]) return; // Page is already loading var cur_page_ele = cur_page.page; var url = cur_page_ele.getAttribute('data-page-url'); if (url) { this.pages_loading[idx] = true; // set semaphore // add a copy of the loading indicator var new_loading_indicator = this.loading_indicator.cloneNode(); new_loading_indicator.classList.add('active'); cur_page_ele.appendChild(new_loading_indicator); // load data { var self = this; var _idx = idx; var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onreadystatechange = function(){ if (xhr.readyState != 4) return; if (xhr.status === 200) { // find the page element in the data var div = document.createElement('div'); div.innerHTML = xhr.responseText; var new_page = null; var nodes = div.childNodes; for (var i = 0, l = nodes.length; i 0) { var self = this; setTimeout(function() { self.load_page(idx+1, pages_to_preload); },0); } }, /* * Hide all pages that have no 'opened' class * The 'opened' class will be added to visible pages by JavaScript * We cannot add this in the default CSS because JavaScript may be disabled */ pre_hide_pages : function() { /* pages might have not been loaded yet, so add a CSS rule */ var s = '@media screen{.'+CSS_CLASS_NAMES.page_content_box+'{display:none;}}'; var n = document.createElement('style'); if (n.styleSheet) { n.styleSheet.cssText = s; } else { n.appendChild(document.createTextNode(s)); } document.head.appendChild(n); }, /* * show visible pages and hide invisible pages */ render : function () { var container = this.container; /* * show the pages that are 'nearly' visible -- it's right above or below the container * * all the y values are in the all-page element's coordinate system */ var container_min_y = container.scrollTop; var container_height = container.clientHeight; var container_max_y = container_min_y + container_height; var visible_min_y = container_min_y - container_height; var visible_max_y = container_max_y + container_height; var cur_page_fully_visible = false; var cur_page_idx = this.cur_page_idx; var max_visible_page_idx = cur_page_idx; var max_visible_ratio = 0.0; var pl = this.pages; for (var i = 0, l = pl.length; i = visible_min_y)) { // cur_page is 'nearly' visible, show it or load it if (cur_page.loaded) { cur_page.show(); } else { this.load_page(i); } } else { cur_page.hide(); } } }, /* * update cur_page_idx and first_page_idx * normally called upon scrolling */ update_page_idx: function () { var pages = this.pages; var pages_len = pages.length; // there is no chance that cur_page_idx or first_page_idx is modified if (pages_len < 2) return; var container = this.container; var container_min_y = container.scrollTop; var container_max_y = container_min_y + container.clientHeight; // binary search for the first page // whose bottom border is below the top border of the container var first_idx = -1; var last_idx = pages_len; var rest_len = last_idx - first_idx; // TODO: use current first_page_idx as a hint? while(rest_len > 1) { var idx = first_idx + Math.floor(rest_len / 2); var cur_page_ele = pages[idx].page; if (cur_page_ele.offsetTop + cur_page_ele.clientTop + cur_page_ele.clientHeight >= container_min_y) { last_idx = idx; } else { first_idx = idx; } rest_len = last_idx - first_idx; } /* * with malformed settings it is possible that no page is visible, e.g. * - the container is to thin, which lies in the margin between two pages * - all pages are completely above or below the container * but we just assume that they won't happen. */ this.first_page_idx = last_idx; // find the page with largest visible area var cur_page_idx = this.cur_page_idx; var max_visible_page_idx = cur_page_idx; var max_visible_ratio = 0.0; for(var i = last_idx; i container_max_y) break; // check the visible fraction of the page var page_visible_ratio = ( Math.min(container_max_y, page_max_y) - Math.max(container_min_y, page_min_y) ) / page_height; // stay with the current page if it is still fully visible if ((i === cur_page_idx) && (Math.abs(page_visible_ratio - 1.0) <= EPS)) { max_visible_page_idx = cur_page_idx; break; } if (page_visible_ratio > max_visible_ratio) { max_visible_ratio = page_visible_ratio; max_visible_page_idx = i; } } this.cur_page_idx = max_visible_page_idx; }, /** * @param{boolean} renew renew the existing schedule instead of using the old one */ schedule_render : function(renew) { if (this.render_timer !== undefined) { if (!renew) return; clearTimeout(this.render_timer); } var self = this; this.render_timer = setTimeout(function () { /* * render() may trigger load_page(), which may in turn trigger another render() * so delete render_timer first */ delete self.render_timer; self.render(); }, this.config['render_timeout']); }, /* * Handling key events, zooming, scrolling etc. */ register_key_handler: function () { /* * When user try to zoom in/out using ctrl + +/- or mouse wheel * handle this and prevent the default behaviours * * Code credit to PDF.js */ var self = this; // Firefox specific event, so that we can prevent browser from zooming window.addEventListener('DOMMouseScroll', function(e) { if (e.ctrlKey) { e.preventDefault(); var container = self.container; var rect = container.getBoundingClientRect(); var fixed_point = [e.clientX - rect['left'] - container.clientLeft ,e.clientY - rect['top'] - container.clientTop]; self.rescale(Math.pow(self.config['scale_step'], e.detail), true, fixed_point); } }, false); window.addEventListener('keydown', function(e) { var handled = false; /* var cmd = (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0) | (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0) ; */ var with_ctrl = e.ctrlKey || e.metaKey; var with_alt = e.altKey; switch (e.keyCode) { case 61: // FF/Mac '=' case 107: // FF '+' and '=' case 187: // Chrome '+' if (with_ctrl){ self.rescale(1.0 / self.config['scale_step'], true); handled = true; } break; case 173: // FF/Mac '-' case 109: // FF '-' case 189: // Chrome '-' if (with_ctrl){ self.rescale(self.config['scale_step'], true); handled = true; } break; case 48: // '0' if (with_ctrl){ self.rescale(0, false); handled = true; } break; case 33: // Page UP: if (with_alt) { // alt-pageup -> scroll one page up self.scroll_to(self.cur_page_idx - 1); } else { // pageup -> scroll one screen up self.container.scrollTop -= self.container.clientHeight; } handled = true; break; case 34: // Page DOWN if (with_alt) { // alt-pagedown -> scroll one page down self.scroll_to(self.cur_page_idx + 1); } else { // pagedown -> scroll one screen down self.container.scrollTop += self.container.clientHeight; } handled = true; break; case 35: // End self.container.scrollTop = self.container.scrollHeight; handled = true; break; case 36: // Home self.container.scrollTop = 0; handled = true; break; } if (handled) { e.preventDefault(); return; } }, false); }, /** * @param{number} ratio * @param{boolean} is_relative * @param{Array.=} fixed_point preserve the position (relative to the top-left corner of the viewer) after rescaling */ rescale : function (ratio, is_relative, fixed_point) { var old_scale = this.scale; var new_scale = old_scale; // set new scale if (ratio === 0) { new_scale = 1; is_relative = false; } else if (is_relative) new_scale *= ratio; else new_scale = ratio; this.scale = new_scale; if (!fixed_point) fixed_point = [0,0]; // translate fixed_point to the coordinate system of all pages var container = this.container; fixed_point[0] += container.scrollLeft; fixed_point[1] += container.scrollTop; // find the visible page that contains the fixed point // if the fixed point lies between two pages (including their borders), it's contained in the first one var pl = this.pages; var pl_len = pl.length; for (var i = this.first_page_idx; i = fixed_point[1]) break; } var fixed_point_page_idx = i - 1; // determine the new scroll position // each-value consists of two parts, one inside the page, which is affected by rescaling, // the other is outside, (e.g. borders and margins), which is not affected // if the fixed_point is above the first page, use the first page as the reference if (fixed_point_page_idx < 0) fixed_point_page_idx = 0; var fp_p = pl[fixed_point_page_idx].page; var fp_p_width = fp_p.clientWidth; var fp_p_height = fp_p.clientHeight; var fp_x_ref = fp_p.offsetLeft + fp_p.clientLeft; var fp_x_inside = fixed_point[0] - fp_x_ref; if (fp_x_inside < 0) fp_x_inside = 0; else if (fp_x_inside > fp_p_width) fp_x_inside = fp_p_width; var fp_y_ref = fp_p.offsetTop + fp_p.clientTop; var fp_y_inside = fixed_point[1] - fp_y_ref; if (fp_y_inside < 0) fp_y_inside = 0; else if (fp_y_inside > fp_p_height) fp_y_inside = fp_p_height; // Rescale pages for (var i = 0; i =} pos [x,y] where (0,0) is the top-left corner */ scroll_to : function(page_idx, pos) { var pl = this.pages; if ((page_idx < 0) || (page_idx >= pl.length)) return; var target_page = pl[page_idx]; var cur_target_pos = target_page.view_position(); if (pos === undefined) pos = [0,0]; var container = this.container; container.scrollLeft += pos[0] - cur_target_pos[0]; container.scrollTop += pos[1] - cur_target_pos[1]; } }; // export pdf2htmlEX.Viewer pdf2htmlEX['Viewer'] = Viewer;
Office of the Secretary of Defense
Fiscal Year (FY) 2020 President’s Budget
Volume II - Defense-Wide Data Book
env30-91
Environmental Restoration
Program
(This page intentionally left blank)
Office of the Secretary of Defense
Fiscal Year (FY) 2020 President’s Budget
Volume II - Defense-Wide Data Book
ENV-30 Exhibit, Environmental Restoration Program
OUSD(C)-93
Office of the Secretary of Defense
Fiscal Year (FY) 2020 President’s Budget
Volume II - Defense-Wide Data Book
ENV-30 Exhibit, Environmental Restoration Program
OUSD(C)-94
Office of the Secretary of Defense
Fiscal Year (FY) 2020 President’s Budget
Volume II - Defense-Wide Data Book
ENV-30 Exhibit, Environmental Restoration Program
OUSD(C)-95
Office of the Secretary of Defense
Fiscal Year (FY) 2020 President’s Budget
Volume II - Defense-Wide Data Book
ENV-30 Exhibit, Environmental Restoration Program
OUSD(C)-96