├── .bowerrc ├── .gitignore ├── LICENSE ├── NOTICE ├── README.rdoc ├── Rakefile ├── VERSION ├── bower.json ├── config ├── assets.yml └── templates_only.yml ├── dist ├── print-datauri.css ├── print.css ├── templates.js ├── viewer-datauri.css ├── viewer.css └── viewer.js ├── public ├── assets │ ├── print-datauri.css │ ├── print.css │ ├── templates.js │ ├── viewer-datauri.css │ ├── viewer.css │ └── viewer.js ├── images │ └── DV │ │ ├── browsers │ │ ├── chrome.gif │ │ ├── firefox.gif │ │ └── safari.gif │ │ └── embed │ │ ├── bar.png │ │ ├── bar_small.png │ │ ├── bullet4x4.gif │ │ ├── close.png │ │ ├── close_tab.png │ │ ├── controlsArrows.png │ │ ├── dc_inline_logo.png │ │ ├── dc_mini_logo.png │ │ ├── draft_dot.png │ │ ├── expanders.png │ │ ├── fullscreen.png │ │ ├── headerbg.png │ │ ├── highlight.gif │ │ ├── leftArrow.png │ │ ├── link.png │ │ ├── pagesbg.jpg │ │ ├── rightArrow.png │ │ ├── search_icon.png │ │ ├── shadow-white.gif │ │ ├── shadow.png │ │ ├── slider.png │ │ ├── spinner.gif │ │ ├── tabbg.png │ │ ├── tabs-small.png │ │ └── tabs.png ├── javascripts │ └── DV │ │ ├── controllers │ │ ├── api.js │ │ └── document_viewer.js │ │ ├── elements │ │ └── elements.js │ │ ├── events │ │ ├── ViewAnnotation.js │ │ ├── ViewDocument.js │ │ ├── ViewSearch.js │ │ ├── ViewText.js │ │ ├── ViewThumbnails.js │ │ ├── events.js │ │ ├── history.js │ │ └── navigation.js │ │ ├── helpers │ │ ├── annotations.js │ │ ├── construction.js │ │ ├── editor.js │ │ ├── helpers.js │ │ ├── navigation.js │ │ └── search.js │ │ ├── lib │ │ ├── annotation.js │ │ ├── drag_reporter.js │ │ ├── elements.js │ │ ├── history.js │ │ ├── initializer.js │ │ ├── page.js │ │ ├── page_set.js │ │ └── thumbnails.js │ │ ├── models │ │ ├── annotation.js │ │ ├── chapter.js │ │ ├── document.js │ │ └── page.js │ │ ├── schema │ │ └── schema.js │ │ ├── states │ │ └── states.js │ │ ├── vendor │ │ ├── jquery-migrate-1.4.1.min.js │ │ ├── jquery.acceptInput.js │ │ ├── jquery.placeholder.js │ │ └── underscore.js │ │ └── views │ │ ├── annotation.jst │ │ ├── annotationNav.jst │ │ ├── chapterNav.jst │ │ ├── descriptionContainer.jst │ │ ├── footer.jst │ │ ├── fullscreenControl.jst │ │ ├── header.jst │ │ ├── navControls.jst │ │ ├── navigationExpander.jst │ │ ├── pageAnnotation.jst │ │ ├── pages.jst │ │ ├── thumbnails.jst │ │ ├── unsupported.jst │ │ └── viewer.jst └── stylesheets │ └── DV │ ├── components │ ├── annotations.css │ ├── minimode.css │ ├── pages.css │ ├── reset.css │ ├── structure.css │ ├── ui-footer.css │ ├── ui-header.css │ ├── ui-menu.css │ ├── ui-navigation.css │ ├── ui-search.css │ ├── ui-text.css │ ├── ui-zoom.css │ ├── ui.css │ ├── unsupported.css │ ├── view-annotations.css │ ├── view-document.css │ ├── view-search.css │ ├── view-text.css │ └── view-thumbnails.css │ ├── print.css │ └── themes │ └── plain.css ├── viewer-debug.html ├── viewer-embed-debug.html ├── viewer-iframe.html ├── viewer-mini-debug.html ├── viewer-oembed.html ├── viewer-responsive.html └── viewer.html /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/javascripts/DV/vendor" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | log/* 2 | *.pid 3 | *~ 4 | .#* 5 | .DS_Store 6 | raw 7 | .tm_properties 8 | /public/javascripts/DV/vendor/underscore 9 | /public/javascripts/DV/vendor/jquery-ui 10 | /public/javascripts/DV/vendor/jquery 11 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | NYT Document Viewer 2 | Copyright (c) 2010 The New York Times co. 3 | 4 | 5 | The NYT Document Viewer includes the following software: 6 | --------------------------------------------------------------------------- 7 | jQuery 8 | Copyright (c) 2010 John Resig, http://jquery.com/ 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining 11 | a copy of this software and associated documentation files (the 12 | "Software"), to deal in the Software without restriction, including 13 | without limitation the rights to use, copy, modify, merge, publish, 14 | distribute, sublicense, and/or sell copies of the Software, and to 15 | permit persons to whom the Software is furnished to do so, subject to 16 | the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be 19 | included in all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 25 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 26 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | 29 | --------------------------------------------------------------------------- 30 | jQuery UI 31 | Copyright (c) 2010 Paul Bakaus, http://jqueryui.com/ 32 | 33 | This software consists of voluntary contributions made by many 34 | individuals (AUTHORS.txt, http://jqueryui.com/about) For exact 35 | contribution history, see the revision history and logs, available 36 | at http://jquery-ui.googlecode.com/svn/ 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining 39 | a copy of this software and associated documentation files (the 40 | "Software"), to deal in the Software without restriction, including 41 | without limitation the rights to use, copy, modify, merge, publish, 42 | distribute, sublicense, and/or sell copies of the Software, and to 43 | permit persons to whom the Software is furnished to do so, subject to 44 | the following conditions: 45 | 46 | The above copyright notice and this permission notice shall be 47 | included in all copies or substantial portions of the Software. 48 | 49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 50 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 51 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 52 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 53 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 54 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 55 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 56 | 57 | --------------------------------------------------------------------------- 58 | Underscore 59 | Copyright (c) 2010 Jeremy Ashkenas, DocumentCloud 60 | 61 | Permission is hereby granted, free of charge, to any person 62 | obtaining a copy of this software and associated documentation 63 | files (the "Software"), to deal in the Software without 64 | restriction, including without limitation the rights to use, 65 | copy, modify, merge, publish, distribute, sublicense, and/or sell 66 | copies of the Software, and to permit persons to whom the 67 | Software is furnished to do so, subject to the following 68 | conditions: 69 | 70 | The above copyright notice and this permission notice shall be 71 | included in all copies or substantial portions of the Software. 72 | 73 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 74 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 75 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 76 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 77 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 78 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 79 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 80 | OTHER DEALINGS IN THE SOFTWARE. 81 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | ____ _ ___ 2 | / __ \____ ____| | / (_)__ _ _____ _____ 3 | / / / / __ \/ ___/ | / / / _ \ | /| / / _ \/ ___/ 4 | / /_/ / /_/ / /__ | |/ / / __/ |/ |/ / __/ / 5 | /_____/\____/\___/ |___/_/\___/|__/|__/\___/_/ 6 | 7 | 8 | == Newspapers and News Agencies 9 | If you're thinking of implementing this project, consider signing up for a DocumentCloud account instead -- we handle the document processing and indexing for search, and give you an easily embeddable version of this viewer. 10 | 11 | http://www.documentcloud.org/contact 12 | 13 | If you're not part of a news agency, read on for the technical details... 14 | 15 | == Browser Support 16 | All core functionality is supported as far back as IE7. However, users with modern browsers (Firefox 3.5, Safari 4, Chrome) will notice a much improved view. 17 | 18 | == Usage 19 | 20 | Take a peek at the viewer.html template to see how to load in a Document. All you really need to do is point to a Document Viewer JSON file and specify the target element to embed the viewer: 21 | 22 | DV.load(JSON_FILE, { container: element_selector }); 23 | 24 | *JSON_FILE*: Either an inline json object, or the path to a remote json object - https://www.documentcloud.org/documents/282753-lefler-thesis.json 25 | 26 | * *container*: The element to contain the Document Viewer - '#DV-container' 27 | 28 | 29 | == Data Formats: Documents 30 | All data should be formatted as JSON. When provided a cross domain url, DV will attempt a JSONP request. 31 | Examples of this in use can be found here: 32 | https://www.documentcloud.org/documents/282753-lefler-thesis.json 33 | 34 | { 35 | title : DOCUMENT_TITLE, 36 | description : DOCUMENT_DESCRIPTION, 37 | id : DOCUMENT_ID, 38 | pages : TOTAL_PAGES, 39 | 40 | annotations : 41 | [ 42 | { 43 | title : ANNOTATION_TITLE, 44 | page : PAGE_INDEX, 45 | content : ANNOTATION_DESCRIPTION 46 | location : { image: "x1, y1, x2, y2" }, 47 | } 48 | ], 49 | 50 | sections : 51 | [ 52 | { title : CHAPTER_TITLE, pages: "1-10" }, 53 | { title : CHAPTER_TITLE, pages: "11-20" } 54 | ], 55 | 56 | resources : 57 | { 58 | page : { text: TEXT_URL_FORMAT, image: IMAGE_URL_FORMAT }, 59 | related_story : LINK_URL, 60 | pdf : ORIGINAL_PDF_URL, 61 | search : SEARCH_UTILITY_URL 62 | } 63 | } 64 | 65 | == Image Sizes and URLs 66 | Images should be created to match the three sizes required by the viewer: 67 | 68 | * *large* : 1000px wide 69 | * *normal* : 700px wide 70 | * *thumbnail* : 180px wide 71 | 72 | The document JSON data should have an image template string in the `resources.page.image` section. The template should include `{page}` and `{size}`. 73 | 74 | http://s3.documentcloud.org/documents/19864/pages/goldman-sachs-internal-emails-p{page}-{size}.gif 75 | 76 | == Data Formats: Search 77 | All data should be formatted as JSON. When provided a cross domain url, the Document Viewer will attempt a JSONP request. 78 | 79 | { 80 | matches : NUMBER_OF_PAGES_WITH_MATCHES, 81 | results : [PAGE_NUMBER,PAGE_NUMBER,PAGE_NUMBER], 82 | query : SEARCH_QUERY 83 | } 84 | 85 | 86 | * *matches* : The number of pages with matches. 87 | * *results* : An array of page numbers with matches. NOT zero based. 88 | * *query* : Your search query. 89 | 90 | 91 | == Requirements 92 | === Jammit 93 | 94 | We're using the excellent Jammit asset compression library from the DocumentCloud project to compress assets and render JS templates. Anytime you make a change to a template, you'll need to run jammit command to render out the views. 95 | 96 | jammit 97 | 98 | 99 | Details on installing and using the Jammit library can be found at http://documentcloud.github.com/jammit/ 100 | 101 | 102 | == Styling the Viewer 103 | The template provided comes in two forms: 104 | 105 | ===viewer.html 106 | This is the production facing view that uses the latest compressed assets and views. Compressed assets in this repository are only updated each time we do a point release. 107 | 108 | ===viewer-debug.html 109 | Should only be used for development and debugging purposes. You'll need to keep this up to date should add or remove new assets. 110 | 111 | All view templates can be found in javascripts/DV/views 112 | To see your changes you'll need to be sure to run the jammit command to re-render your views. 113 | 114 | == Copyright 115 | Copyright © 2010 The New York Times. See LICENSE for details. 116 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | namespace :build do 2 | 3 | desc "Rebuild just the templates, no CSS or JS" 4 | task :templates do 5 | `jammit -c config/templates_only.yml` 6 | end 7 | 8 | end 9 | 10 | desc "Build the viewer" 11 | task :build do 12 | `jammit -c config/assets.yml` 13 | end -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.1 -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "documentcloud-document-viewer", 3 | "description": "DocumentCloud document viewer based on NYTimes document viewer", 4 | "version": "0.1.0", 5 | "authors": [ 6 | "Jeremy Ashkenas ", 7 | "Alan McLean ", 8 | "Ted Han ", 9 | "Justin Reese " 10 | ], 11 | "keywords": [ 12 | "documentcloud", 13 | "embedded", 14 | "3rdparty" 15 | ], 16 | "license": "MIT", 17 | "moduleType": [ 18 | "globals" 19 | ], 20 | "ignore": [ 21 | "**/.*", 22 | "node_modules", 23 | "public/javascripts/DV/vendor" 24 | ], 25 | "dependencies": { 26 | "jquery": "~1.12.0", 27 | "jquery-ui": "~1.12.0", 28 | "underscore": "~1.8.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /config/assets.yml: -------------------------------------------------------------------------------- 1 | template_function: DV._.template 2 | embed_assets: datauri 3 | gzip_assets: off 4 | 5 | javascripts: 6 | viewer: 7 | - public/javascripts/DV/vendor/jquery/dist/jquery.js 8 | - public/javascripts/DV/vendor/jquery-migrate-1.4.1.min.js 9 | - public/javascripts/DV/vendor/jquery-ui/ui/version.js 10 | - public/javascripts/DV/vendor/jquery-ui/ui/widget.js 11 | - public/javascripts/DV/vendor/jquery-ui/ui/position.js 12 | - public/javascripts/DV/vendor/jquery-ui/ui/data.js 13 | - public/javascripts/DV/vendor/jquery-ui/ui/disable-selection.js 14 | - public/javascripts/DV/vendor/jquery-ui/ui/focusable.js 15 | - public/javascripts/DV/vendor/jquery-ui/ui/form-reset-mixin.js 16 | - public/javascripts/DV/vendor/jquery-ui/ui/keycode.js 17 | - public/javascripts/DV/vendor/jquery-ui/ui/labels.js 18 | - public/javascripts/DV/vendor/jquery-ui/ui/scroll-parent.js 19 | - public/javascripts/DV/vendor/jquery-ui/ui/tabbable.js 20 | - public/javascripts/DV/vendor/jquery-ui/ui/unique-id.js 21 | - public/javascripts/DV/vendor/jquery-ui/ui/widgets/mouse.js 22 | - public/javascripts/DV/vendor/jquery-ui/ui/widgets/slider.js 23 | - public/javascripts/DV/vendor/jquery-ui/ui/effect.js 24 | - public/javascripts/DV/vendor/jquery-ui/ui/effects/effect-blind.js 25 | - public/javascripts/DV/vendor/underscore/underscore.js 26 | - public/javascripts/DV/vendor/jquery.acceptInput.js 27 | - public/javascripts/DV/vendor/jquery.placeholder.js 28 | - public/javascripts/DV/lib/initializer.js 29 | - public/javascripts/DV/lib/*.js 30 | - public/javascripts/DV/schema/*.js 31 | - public/javascripts/DV/elements/*.js 32 | - public/javascripts/DV/models/*.js 33 | - public/javascripts/DV/events/events.js 34 | - public/javascripts/DV/events/View*.js 35 | - public/javascripts/DV/events/*.js 36 | - public/javascripts/DV/helpers/helpers.js 37 | - public/javascripts/DV/helpers/*.js 38 | - public/javascripts/DV/states/*.js 39 | - public/javascripts/DV/controllers/*.js 40 | templates: 41 | - public/javascripts/DV/views/*.jst 42 | 43 | stylesheets: 44 | viewer: 45 | - public/stylesheets/DV/components/reset.css 46 | - public/stylesheets/DV/components/structure.css 47 | - public/stylesheets/DV/components/annotations.css 48 | - public/stylesheets/DV/components/pages.css 49 | - public/stylesheets/DV/components/ui.css 50 | - public/stylesheets/DV/components/ui-*.css 51 | - public/stylesheets/DV/components/view-*.css 52 | - public/stylesheets/DV/components/unsupported.css 53 | - public/stylesheets/DV/components/minimode.css 54 | - public/stylesheets/DV/themes/plain.css 55 | print: 56 | - public/stylesheets/DV/print.css 57 | -------------------------------------------------------------------------------- /config/templates_only.yml: -------------------------------------------------------------------------------- 1 | template_function: DV._.template 2 | gzip_assets: off 3 | 4 | javascripts: 5 | templates: 6 | - public/javascripts/DV/views/*.jst 7 | -------------------------------------------------------------------------------- /dist/print-datauri.css: -------------------------------------------------------------------------------- 1 | .DV-docViewerWrapper{display:none}.DV-printMessage{display:absolute;background:white;top:0;left:0;right:0;bottom:0;padding:50px;font-size:16px} -------------------------------------------------------------------------------- /dist/print.css: -------------------------------------------------------------------------------- 1 | .DV-docViewerWrapper{display:none}.DV-printMessage{display:absolute;background:white;top:0;left:0;right:0;bottom:0;padding:50px;font-size:16px} -------------------------------------------------------------------------------- /public/assets/print-datauri.css: -------------------------------------------------------------------------------- 1 | .DV-docViewerWrapper{display:none}.DV-printMessage{display:absolute;background:white;top:0;left:0;right:0;bottom:0;padding:50px;font-size:16px} -------------------------------------------------------------------------------- /public/assets/print.css: -------------------------------------------------------------------------------- 1 | .DV-docViewerWrapper{display:none}.DV-printMessage{display:absolute;background:white;top:0;left:0;right:0;bottom:0;padding:50px;font-size:16px} -------------------------------------------------------------------------------- /public/images/DV/browsers/chrome.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/browsers/chrome.gif -------------------------------------------------------------------------------- /public/images/DV/browsers/firefox.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/browsers/firefox.gif -------------------------------------------------------------------------------- /public/images/DV/browsers/safari.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/browsers/safari.gif -------------------------------------------------------------------------------- /public/images/DV/embed/bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/bar.png -------------------------------------------------------------------------------- /public/images/DV/embed/bar_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/bar_small.png -------------------------------------------------------------------------------- /public/images/DV/embed/bullet4x4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/bullet4x4.gif -------------------------------------------------------------------------------- /public/images/DV/embed/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/close.png -------------------------------------------------------------------------------- /public/images/DV/embed/close_tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/close_tab.png -------------------------------------------------------------------------------- /public/images/DV/embed/controlsArrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/controlsArrows.png -------------------------------------------------------------------------------- /public/images/DV/embed/dc_inline_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/dc_inline_logo.png -------------------------------------------------------------------------------- /public/images/DV/embed/dc_mini_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/dc_mini_logo.png -------------------------------------------------------------------------------- /public/images/DV/embed/draft_dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/draft_dot.png -------------------------------------------------------------------------------- /public/images/DV/embed/expanders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/expanders.png -------------------------------------------------------------------------------- /public/images/DV/embed/fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/fullscreen.png -------------------------------------------------------------------------------- /public/images/DV/embed/headerbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/headerbg.png -------------------------------------------------------------------------------- /public/images/DV/embed/highlight.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/highlight.gif -------------------------------------------------------------------------------- /public/images/DV/embed/leftArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/leftArrow.png -------------------------------------------------------------------------------- /public/images/DV/embed/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/link.png -------------------------------------------------------------------------------- /public/images/DV/embed/pagesbg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/pagesbg.jpg -------------------------------------------------------------------------------- /public/images/DV/embed/rightArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/rightArrow.png -------------------------------------------------------------------------------- /public/images/DV/embed/search_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/search_icon.png -------------------------------------------------------------------------------- /public/images/DV/embed/shadow-white.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/shadow-white.gif -------------------------------------------------------------------------------- /public/images/DV/embed/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/shadow.png -------------------------------------------------------------------------------- /public/images/DV/embed/slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/slider.png -------------------------------------------------------------------------------- /public/images/DV/embed/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/spinner.gif -------------------------------------------------------------------------------- /public/images/DV/embed/tabbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/tabbg.png -------------------------------------------------------------------------------- /public/images/DV/embed/tabs-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/tabs-small.png -------------------------------------------------------------------------------- /public/images/DV/embed/tabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/documentcloud/document-viewer/4843cb1850e901d779ba5c03d0ec192781d38094/public/images/DV/embed/tabs.png -------------------------------------------------------------------------------- /public/javascripts/DV/controllers/document_viewer.js: -------------------------------------------------------------------------------- 1 | DV.DocumentViewer = function(options) { 2 | this.options = options; 3 | this.window = window; 4 | this.$ = this.jQuery; 5 | this.schema = new DV.Schema(); 6 | this.api = new DV.Api(this); 7 | this.history = new DV.History(this); 8 | 9 | // Build the data models 10 | this.models = this.schema.models; 11 | this.events = DV._.extend({}, DV.Schema.events); 12 | this.helpers = DV._.extend({}, DV.Schema.helpers); 13 | this.states = DV._.extend({}, DV.Schema.states); 14 | 15 | // state values 16 | this.isFocus = true; 17 | this.openEditor = null; 18 | this.confirmStateChange = null; 19 | this.activeElement = null; 20 | this.observers = []; 21 | this.windowDimensions = {}; 22 | this.scrollPosition = null; 23 | this.checkTimer = {}; 24 | this.busy = false; 25 | this.annotationToLoadId = null; 26 | this.dragReporter = null; 27 | this.compiled = {}; 28 | this.tracker = {}; 29 | this.visible = true; 30 | 31 | this.onStateChangeCallbacks = []; 32 | 33 | this.events = DV._.extend(this.events, { 34 | viewer : this, 35 | states : this.states, 36 | elements : this.elements, 37 | helpers : this.helpers, 38 | models : this.models, 39 | // this allows us to bind events to call the method corresponding to the current state 40 | compile : function(){ 41 | var a = this.viewer; 42 | var methodName = arguments[0]; 43 | return function(){ 44 | if(!a.events[a.state][methodName]){ 45 | a.events[methodName].apply(a.events,arguments); 46 | }else{ 47 | a.events[a.state][methodName].apply(a.events,arguments); 48 | } 49 | }; 50 | } 51 | }); 52 | 53 | this.helpers = DV._.extend(this.helpers, { 54 | viewer : this, 55 | states : this.states, 56 | elements : this.elements, 57 | events : this.events, 58 | models : this.models 59 | }); 60 | 61 | this.states = DV._.extend(this.states, { 62 | viewer : this, 63 | helpers : this.helpers, 64 | elements : this.elements, 65 | events : this.events, 66 | models : this.models 67 | }); 68 | }; 69 | 70 | DV.DocumentViewer.prototype.loadModels = function() { 71 | this.models.chapters = new DV.model.Chapters(this); 72 | this.models.document = new DV.model.Document(this); 73 | this.models.pages = new DV.model.Pages(this); 74 | this.models.annotations = new DV.model.Annotations(this); 75 | this.models.removedPages = {}; 76 | }; 77 | 78 | // Transition to a given state ... unless we're already in it. 79 | DV.DocumentViewer.prototype.open = function(state) { 80 | if (this.state == state) return; 81 | var continuation = DV._.bind(function() { 82 | this.state = state; 83 | this.states[state].apply(this, arguments); 84 | this.slapIE(); 85 | this.notifyChangedState(); 86 | return true; 87 | }, this); 88 | this.confirmStateChange ? this.confirmStateChange(continuation) : continuation(); 89 | }; 90 | 91 | DV.DocumentViewer.prototype.slapIE = function() { 92 | DV.jQuery(this.options.container).css({zoom: 0.99}).css({zoom: 1}); 93 | }; 94 | 95 | DV.DocumentViewer.prototype.notifyChangedState = function() { 96 | DV._.each(this.onStateChangeCallbacks, function(c) { c(); }); 97 | }; 98 | 99 | // Record a hit on this document viewer. 100 | DV.DocumentViewer.prototype.recordHit = function(hitUrl) { 101 | var url = this.helpers.getSourceUrl(); 102 | if (url.match(/^file:/)) return false; 103 | var slug = this.api.getId(); 104 | var id = parseInt(slug, 10); 105 | var key = encodeURIComponent('document:' + id + ':' + url); 106 | if (DV._.isUndefined(DV.viewers) || DV._.isUndefined(DV.viewers[slug]) || DV._.isUndefined(DV.viewers[slug].elements) || DV._.isUndefined(DV.viewers[slug].elements.viewer)) { 107 | var selector = '.DV-docViewer'; 108 | } else { 109 | var selector = DV.viewers[slug].elements.viewer; 110 | } 111 | var $viewer = DV.jQuery(selector); 112 | $viewer.append(''); 113 | }; 114 | 115 | // jQuery object, scoped to this viewer's container. 116 | DV.DocumentViewer.prototype.jQuery = function(selector, context) { 117 | context = context || this.options.container; 118 | return DV.jQuery.call(DV.jQuery, selector, context); 119 | }; 120 | 121 | // The origin function, kicking off the entire documentViewer render. 122 | // Since https://github.com/documentcloud/documentcloud/pull/418, we intend the 123 | // public `DV.load` to be a queuing function (defined in the loader) which only 124 | // fires this function once it's available. 125 | DV.immediatelyLoadDocument = function(documentRep, options) { 126 | options = options || {}; 127 | var id = documentRep.id || documentRep.match(/([^\/]+)(\.js|\.json)$/)[1]; 128 | if ('showSidebar' in options) options.sidebar = options.showSidebar; 129 | var defaults = { 130 | container : document.body, 131 | zoom : 'auto', 132 | sidebar : true 133 | }; 134 | options = DV._.extend({}, defaults, options); 135 | options.sidebarVisible = options.sidebar; 136 | options.fixedSize = !options.responsive && (!!(options.width || options.height)); 137 | var viewer = new DV.DocumentViewer(options); 138 | DV.viewers[id] = viewer; 139 | // Once we have the JSON representation in-hand, finish loading the viewer. 140 | var continueLoad = DV.loadJSON = function(json) { 141 | var viewer = DV.viewers[json.id]; 142 | viewer.schema.importCanonicalDocument(json); 143 | viewer.loadModels(); 144 | DV.jQuery(function() { 145 | viewer.visible = DV.jQuery(viewer.options.container).is(':visible'); 146 | viewer.open('InitialLoad'); 147 | if (options.afterLoad) options.afterLoad(viewer); 148 | if (DV.afterLoad) DV.afterLoad(viewer); 149 | if (DV.recordHit) viewer.recordHit(DV.recordHit); 150 | }); 151 | }; 152 | 153 | // If we've been passed the JSON directly, we can go ahead, 154 | // otherwise make a JSONP request to fetch it. 155 | var jsonLoad = function() { 156 | if (DV._.isString(documentRep)) { 157 | if (documentRep.match(/\.js$/)) { 158 | //DV.jQuery.getScript(documentRep); 159 | var cacheRequest = (options.cache == undefined) ? true : options.cache; 160 | DV.jQuery.ajax({ cache: cacheRequest, url: documentRep, dataType: "script" }); 161 | } else { 162 | var crossDomain = viewer.helpers.isCrossDomain(documentRep); 163 | if (crossDomain) documentRep = documentRep + '?callback=?'; 164 | DV.jQuery.getJSON(documentRep, continueLoad); 165 | } 166 | } else { 167 | continueLoad(documentRep); 168 | } 169 | }; 170 | 171 | // If we're being asked the fetch the templates, load them remotely before 172 | // continuing. 173 | if (options.templates) { 174 | DV.jQuery.getScript(options.templates, jsonLoad); 175 | } else { 176 | jsonLoad(); 177 | } 178 | 179 | return viewer; 180 | }; 181 | 182 | // For backwards-compatibility with the old loader (and for use by people who 183 | // don't use the loader), alias `DV.load` when undefined OR when defined by the 184 | // old oEmbed loader (https://github.com/documentcloud/documentcloud/issues/420) 185 | if (DV._.isUndefined(DV.load) || !DV._.isUndefined(DV._documentsWaitingForAppLoad)) { 186 | DV.load = DV.immediatelyLoadDocument; 187 | }; 188 | 189 | // If the document viewer has been loaded dynamically, allow the external 190 | // script to specify the onLoad behavior. 191 | if (DV.onload) DV._.defer(DV.onload); 192 | 193 | -------------------------------------------------------------------------------- /public/javascripts/DV/elements/elements.js: -------------------------------------------------------------------------------- 1 | // We cache DOM references to improve speed and reduce DOM queries 2 | DV.Schema.elements = 3 | [ 4 | { name: 'browserDocument', query: document }, 5 | { name: 'browserWindow', query: window }, 6 | { name: 'header', query: 'div.DV-header'}, 7 | { name: 'viewer', query: 'div.DV-docViewer'}, 8 | { name: 'window', query: 'div.DV-pages'}, 9 | { name: 'sets', query: 'div.DV-set'}, 10 | { name: 'pages', query: 'div.DV-page'}, 11 | { name: 'metas', query: 'div.DV-pageMeta'}, 12 | { name: 'bar', query: 'div.DV-bar'}, 13 | { name: 'currentPage', query: 'span.DV-currentPage'}, 14 | { name: 'well', query: 'div.DV-well'}, 15 | { name: 'collection', query: 'div.DV-pageCollection'}, 16 | { name: 'annotations', query: 'div.DV-allAnnotations'}, 17 | { name: 'navigation', query: 'div.DV-navigation' }, 18 | { name: 'chaptersContainer', query: 'div.DV-chaptersContainer' }, 19 | { name: 'searchInput', query: 'input.DV-searchInput' }, 20 | { name: 'textCurrentPage', query: 'span.DV-textCurrentPage' }, 21 | { name: 'coverPages', query: 'div.DV-cover' }, 22 | { name: 'fullscreen', query: 'div.DV-fullscreen' } 23 | ]; -------------------------------------------------------------------------------- /public/javascripts/DV/events/ViewAnnotation.js: -------------------------------------------------------------------------------- 1 | DV.Schema.events.ViewAnnotation = { 2 | next: function(e){ 3 | var viewer = this.viewer; 4 | var activeAnnotationId = viewer.activeAnnotationId; 5 | var annotationsModel = this.models.annotations; 6 | var nextAnnotation = (activeAnnotationId === null) ? 7 | annotationsModel.getFirstAnnotation() : annotationsModel.getNextAnnotation(activeAnnotationId); 8 | 9 | if (!nextAnnotation){ 10 | return false; 11 | } 12 | 13 | viewer.pageSet.showAnnotation(nextAnnotation); 14 | this.helpers.setAnnotationPosition(nextAnnotation.position); 15 | 16 | 17 | }, 18 | previous: function(e){ 19 | var viewer = this.viewer; 20 | var activeAnnotationId = viewer.activeAnnotationId; 21 | var annotationsModel = this.models.annotations; 22 | 23 | var previousAnnotation = (!activeAnnotationId) ? 24 | annotationsModel.getFirstAnnotation() : annotationsModel.getPreviousAnnotation(activeAnnotationId); 25 | if (!previousAnnotation){ 26 | return false; 27 | } 28 | 29 | viewer.pageSet.showAnnotation(previousAnnotation); 30 | this.helpers.setAnnotationPosition(previousAnnotation.position); 31 | 32 | 33 | }, 34 | search: function(e){ 35 | e.preventDefault(); 36 | this.viewer.open('ViewSearch'); 37 | 38 | return false; 39 | } 40 | }; -------------------------------------------------------------------------------- /public/javascripts/DV/events/ViewDocument.js: -------------------------------------------------------------------------------- 1 | DV.Schema.events.ViewDocument = { 2 | next: function(){ 3 | var nextPage = this.models.document.nextPage(); 4 | this.helpers.jump(nextPage); 5 | 6 | // this.viewer.history.save('document/p'+(nextPage+1)); 7 | }, 8 | previous: function(e){ 9 | var previousPage = this.models.document.previousPage(); 10 | this.helpers.jump(previousPage); 11 | 12 | // this.viewer.history.save('document/p'+(previousPage+1)); 13 | }, 14 | search: function(e){ 15 | e.preventDefault(); 16 | 17 | this.viewer.open('ViewSearch'); 18 | return false; 19 | } 20 | } -------------------------------------------------------------------------------- /public/javascripts/DV/events/ViewSearch.js: -------------------------------------------------------------------------------- 1 | DV.Schema.events.ViewSearch = { 2 | next: function(e){ 3 | var nextPage = this.models.document.nextPage(); 4 | this.loadText(nextPage); 5 | 6 | this.viewer.open('ViewText'); 7 | }, 8 | previous: function(e){ 9 | var previousPage = this.models.document.previousPage(); 10 | this.loadText(previousPage); 11 | 12 | this.viewer.open('ViewText'); 13 | }, 14 | search: function(e){ 15 | e.preventDefault(); 16 | this.helpers.getSearchResponse(this.elements.searchInput.val()); 17 | 18 | return false; 19 | } 20 | }; -------------------------------------------------------------------------------- /public/javascripts/DV/events/ViewText.js: -------------------------------------------------------------------------------- 1 | DV.Schema.events.ViewText = { 2 | next: function(e){ 3 | var nextPage = this.models.document.nextPage(); 4 | this.loadText(nextPage); 5 | }, 6 | previous: function(e){ 7 | var previousPage = this.models.document.previousPage(); 8 | this.loadText(previousPage); 9 | }, 10 | search: function(e){ 11 | e.preventDefault(); 12 | this.viewer.open('ViewSearch'); 13 | 14 | return false; 15 | } 16 | }; -------------------------------------------------------------------------------- /public/javascripts/DV/events/ViewThumbnails.js: -------------------------------------------------------------------------------- 1 | DV.Schema.events.ViewThumbnails = { 2 | next: function(){ 3 | var nextPage = this.models.document.nextPage(); 4 | this.helpers.jump(nextPage); 5 | }, 6 | previous: function(e){ 7 | var previousPage = this.models.document.previousPage(); 8 | this.helpers.jump(previousPage); 9 | }, 10 | search: function(e){ 11 | e.preventDefault(); 12 | 13 | this.viewer.open('ViewSearch'); 14 | return false; 15 | } 16 | }; -------------------------------------------------------------------------------- /public/javascripts/DV/events/events.js: -------------------------------------------------------------------------------- 1 | // This manages events for different states activated through DV interface actions like clicks, mouseovers, etc. 2 | DV.Schema.events = { 3 | // Change zoom level and causes a reflow and redraw of pages. 4 | zoom: function(level){ 5 | if (!this.viewer.visible) return; 6 | var viewer = this.viewer; 7 | var continuation = function() { 8 | viewer.pageSet.zoom({ zoomLevel: level }); 9 | var ranges = viewer.models.document.ZOOM_RANGES; 10 | viewer.dragReporter.sensitivity = ranges[ranges.length-1] == level ? 1.5 : 1; 11 | viewer.notifyChangedState(); 12 | return true; 13 | }; 14 | viewer.confirmStateChange ? viewer.confirmStateChange(continuation) : continuation(); 15 | }, 16 | 17 | // Draw (or redraw) the visible pages on the screen. 18 | drawPages: function() { 19 | if (this.viewer.state != 'ViewDocument' || !this.viewer.visible) return; 20 | var doc = this.models.document; 21 | var win = this.elements.window[0]; 22 | var offsets = doc.baseHeightsPortionOffsets; 23 | var scrollPos = this.viewer.scrollPosition = win.scrollTop; 24 | var midpoint = scrollPos + (this.viewer.$(win).height() / 3); 25 | var currentPage = DV._.sortedIndex(offsets, scrollPos); 26 | var middlePage = DV._.sortedIndex(offsets, midpoint); 27 | if (offsets[currentPage] == scrollPos) currentPage++ && middlePage++; 28 | var pageIds = this.helpers.sortPages(middlePage - 1); 29 | var total = doc.totalPages; 30 | if (doc.currentPage() != currentPage) doc.setPageIndex(currentPage - 1); 31 | this.drawPageAt(pageIds, middlePage - 1); 32 | }, 33 | 34 | // Draw the page at the given index. 35 | drawPageAt : function(pageIds, index) { 36 | var first = index == 0; 37 | var last = index == this.models.document.totalPages - 1; 38 | if (first) index += 1; 39 | var pages = [ 40 | { label: pageIds[0], index: index - 1 }, 41 | { label: pageIds[1], index: index }, 42 | { label: pageIds[2], index: index + 1 } 43 | ]; 44 | if (last) pages.pop(); 45 | pages[first ? 0 : pages.length - 1].currentPage = true; 46 | this.viewer.pageSet.draw(pages); 47 | }, 48 | 49 | checkVisibility: function() { 50 | if (this.elements.viewer.is(':visible') && !this.viewer.visible) { 51 | this.viewer.visible = true; 52 | this.viewer.api.redraw(true) 53 | } 54 | }, 55 | 56 | check: function(){ 57 | var viewer = this.viewer; 58 | if(viewer.busy === false){ 59 | viewer.busy = true; 60 | for(var i = 0; i < this.viewer.observers.length; i++){ 61 | this[viewer.observers[i]].call(this); 62 | } 63 | viewer.busy = false; 64 | } 65 | }, 66 | 67 | loadText: function(pageIndex,afterLoad){ 68 | 69 | pageIndex = (!pageIndex) ? this.models.document.currentIndex() : parseInt(pageIndex,10); 70 | this._previousTextIndex = pageIndex; 71 | 72 | var me = this; 73 | 74 | var processText = function(raw_text) { 75 | var text = DV._.escape(raw_text); 76 | 77 | var pageNumber = parseInt(pageIndex,10)+1; 78 | me.viewer.$('.DV-textContents').replaceWith('
' + text + '
'); 79 | me.elements.currentPage.text(pageNumber); 80 | me.elements.textCurrentPage.text('p. '+(pageNumber)); 81 | me.models.document.setPageIndex(pageIndex); 82 | me.helpers.setActiveChapter(me.models.chapters.getChapterId(pageIndex)); 83 | 84 | if (me.viewer.openEditor == 'editText' && 85 | !(pageNumber in me.models.document.originalPageText)) { 86 | me.models.document.originalPageText[pageNumber] = text; 87 | } 88 | if (me.viewer.openEditor == 'editText') { 89 | me.viewer.$('.DV-textContents').attr('contentEditable', true).addClass('DV-editing'); 90 | } 91 | 92 | if(afterLoad) afterLoad.call(me.helpers); 93 | }; 94 | 95 | if (me.viewer.schema.text[pageIndex]) { 96 | return processText(me.viewer.schema.text[pageIndex]); 97 | } 98 | 99 | var handleResponse = DV.jQuery.proxy(function(response) { 100 | processText(me.viewer.schema.text[pageIndex] = response); 101 | }, this); 102 | 103 | this.viewer.$('.DV-textContents').text(''); 104 | 105 | var textURI = me.viewer.schema.document.resources.page.text.replace('{page}', pageIndex + 1); 106 | var crossDomain = this.helpers.isCrossDomain(textURI); 107 | if (crossDomain) textURI += '?callback=?'; 108 | DV.jQuery[crossDomain ? 'getJSON' : 'get'](textURI, {}, handleResponse); 109 | }, 110 | 111 | resetTracker: function(){ 112 | this.viewer.activeAnnotation = null; 113 | this.trackAnnotation.combined = null; 114 | this.trackAnnotation.h = null; 115 | }, 116 | trackAnnotation: function(){ 117 | var viewer = this.viewer; 118 | var helpers = this.helpers; 119 | var scrollPosition = this.elements.window[0].scrollTop; 120 | 121 | if(viewer.activeAnnotation){ 122 | var annotation = viewer.activeAnnotation; 123 | var trackAnnotation = this.trackAnnotation; 124 | 125 | 126 | if(trackAnnotation.id != annotation.id){ 127 | trackAnnotation.id = annotation.id; 128 | helpers.setActiveAnnotationLimits(annotation); 129 | } 130 | if(!viewer.activeAnnotation.annotationEl.hasClass('DV-editing') && 131 | (scrollPosition > (trackAnnotation.h) || scrollPosition < trackAnnotation.combined)) { 132 | annotation.hide(true); 133 | viewer.pageSet.setActiveAnnotation(null); 134 | viewer.activeAnnotation = null; 135 | trackAnnotation.h = null; 136 | trackAnnotation.id = null; 137 | trackAnnotation.combined = null; 138 | } 139 | }else{ 140 | viewer.pageSet.setActiveAnnotation(null); 141 | viewer.activeAnnotation = null; 142 | trackAnnotation.h = null; 143 | trackAnnotation.id = null; 144 | trackAnnotation.combined = null; 145 | helpers.removeObserver('trackAnnotation'); 146 | } 147 | } 148 | }; -------------------------------------------------------------------------------- /public/javascripts/DV/events/history.js: -------------------------------------------------------------------------------- 1 | DV._.extend(DV.Schema.events, { 2 | 3 | // #document/p[pageID] 4 | handleHashChangeViewDocumentPage: function(page){ 5 | var pageIndex = parseInt(page,10) - 1; 6 | if(this.viewer.state === 'ViewDocument'){ 7 | this.viewer.pageSet.cleanUp(); 8 | this.helpers.jump(pageIndex); 9 | }else{ 10 | this.models.document.setPageIndex(pageIndex); 11 | this.viewer.open('ViewDocument'); 12 | } 13 | }, 14 | 15 | // #p[pageID] 16 | handleHashChangeLegacyViewDocumentPage: function(page){ 17 | var pageIndex = parseInt(page,10) - 1; 18 | this.handleHashChangeViewDocumentPage(page); 19 | }, 20 | 21 | // #document/p[pageID]/a[annotationID] 22 | handleHashChangeViewDocumentAnnotation: function(page,annotation){ 23 | var pageIndex = parseInt(page,10) - 1; 24 | var annotation = parseInt(annotation,10); 25 | 26 | if(this.viewer.state === 'ViewDocument'){ 27 | this.viewer.pageSet.showAnnotation(this.viewer.models.annotations.byId[annotation]); 28 | }else{ 29 | this.models.document.setPageIndex(pageIndex); 30 | this.viewer.pageSet.setActiveAnnotation(annotation); 31 | this.viewer.openingAnnotationFromHash = true; 32 | this.viewer.open('ViewDocument'); 33 | } 34 | }, 35 | 36 | // #annotation/a[annotationID] 37 | handleHashChangeViewAnnotationAnnotation: function(annotation){ 38 | var annotation = parseInt(annotation,10); 39 | var viewer = this.viewer; 40 | 41 | if(viewer.state === 'ViewAnnotation'){ 42 | viewer.pageSet.showAnnotation(this.viewer.models.annotations.byId[annotation]); 43 | }else{ 44 | viewer.activeAnnotationId = annotation; 45 | this.viewer.open('ViewAnnotation'); 46 | } 47 | }, 48 | 49 | // Default route if all else fails 50 | handleHashChangeDefault: function(){ 51 | this.viewer.pageSet.cleanUp(); 52 | this.models.document.setPageIndex(0); 53 | 54 | if(this.viewer.state === 'ViewDocument'){ 55 | this.helpers.jump(0); 56 | // this.viewer.history.save('document/p1'); 57 | }else{ 58 | this.viewer.open('ViewDocument'); 59 | } 60 | }, 61 | 62 | // #text/p[pageID] 63 | handleHashChangeViewText: function(page){ 64 | var pageIndex = parseInt(page,10) - 1; 65 | if(this.viewer.state === 'ViewText'){ 66 | this.events.loadText(pageIndex); 67 | }else{ 68 | this.models.document.setPageIndex(pageIndex); 69 | this.viewer.open('ViewText'); 70 | } 71 | }, 72 | 73 | handleHashChangeViewPages: function() { 74 | if (this.viewer.state == 'ViewThumbnails') return; 75 | this.viewer.open('ViewThumbnails'); 76 | }, 77 | 78 | // #search/[searchString] 79 | handleHashChangeViewSearchRequest: function(page,query){ 80 | var pageIndex = parseInt(page,10) - 1; 81 | this.elements.searchInput.val(decodeURIComponent(query)); 82 | 83 | if(this.viewer.state !== 'ViewSearch'){ 84 | this.models.document.setPageIndex(pageIndex); 85 | } 86 | this.viewer.open('ViewSearch'); 87 | }, 88 | 89 | // #entity/p[pageID]/[searchString]/[offset]:[length] 90 | handleHashChangeViewEntity: function(page, name, offset, length) { 91 | page = parseInt(page,10) - 1; 92 | name = decodeURIComponent(name); 93 | this.elements.searchInput.val(name); 94 | this.models.document.setPageIndex(page); 95 | this.states.ViewEntity(name, parseInt(offset, 10), parseInt(length, 10)); 96 | } 97 | }); 98 | -------------------------------------------------------------------------------- /public/javascripts/DV/events/navigation.js: -------------------------------------------------------------------------------- 1 | DV._.extend(DV.Schema.events, { 2 | handleNavigation: function(e){ 3 | var el = this.viewer.$(e.target); 4 | var triggerEl = el.closest('.DV-trigger'); 5 | var noteEl = el.closest('.DV-annotationMarker'); 6 | var chapterEl = el.closest('.DV-chapter'); 7 | if (!triggerEl.length) return; 8 | 9 | if (el.hasClass('DV-expander')) { 10 | return chapterEl.toggleClass('DV-collapsed'); 11 | 12 | } else if (noteEl.length) { 13 | var aid = noteEl[0].id.replace('DV-annotationMarker-',''); 14 | var annotation = this.models.annotations.getAnnotation(aid); 15 | var pageNumber = parseInt(annotation.index,10)+1; 16 | 17 | if(this.viewer.state === 'ViewText'){ 18 | this.loadText(annotation.index); 19 | 20 | // this.viewer.history.save('text/p'+pageNumber); 21 | } else{ 22 | if (this.viewer.state === 'ViewThumbnails') { 23 | this.viewer.open('ViewDocument'); 24 | } 25 | this.viewer.pageSet.showAnnotation(annotation); 26 | } 27 | 28 | } else if (chapterEl.length) { 29 | // its a header, take it to the page 30 | chapterEl.removeClass('DV-collapsed'); 31 | var cid = parseInt(chapterEl[0].id.replace('DV-chapter-',''), 10); 32 | var chapterIndex = parseInt(this.models.chapters.getChapterPosition(cid),10); 33 | var pageNumber = parseInt(chapterIndex,10)+1; 34 | 35 | if(this.viewer.state === 'ViewText'){ 36 | this.loadText(chapterIndex); 37 | // this.viewer.history.save('text/p'+pageNumber); 38 | } else if(this.viewer.state === 'ViewDocument' || 39 | this.viewer.state === 'ViewThumbnails'){ 40 | this.helpers.jump(chapterIndex); 41 | // this.viewer.history.save('document/p'+pageNumber); 42 | if (this.viewer.state === 'ViewThumbnails') { 43 | this.viewer.open('ViewDocument'); 44 | } 45 | } else{ 46 | return false; 47 | } 48 | 49 | } else{ 50 | return false; 51 | } 52 | } 53 | }); -------------------------------------------------------------------------------- /public/javascripts/DV/helpers/annotations.js: -------------------------------------------------------------------------------- 1 | DV._.extend(DV.Schema.helpers, { 2 | getAnnotationModel : function(annoEl) { 3 | var annoId = parseInt(annoEl.attr('rel').match(/\d+/), 10); 4 | return this.models.annotations.getAnnotation(annoId); 5 | }, 6 | // Return the annotation Object that connects with the element in the DOM 7 | getAnnotationObject: function(annotation){ 8 | 9 | var annotation = this.viewer.$(annotation); 10 | var annotationId = annotation.attr('id').replace(/DV\-annotation\-|DV\-listAnnotation\-/,''); 11 | var pageId = annotation.closest('div.DV-set').attr('data-id'); 12 | 13 | for(var i = 0; (annotationObject = this.viewer.pageSet.pages[pageId].annotations[i]); i++){ 14 | if(annotationObject.id == annotationId){ 15 | // cleanup 16 | annotation = null; 17 | return annotationObject; 18 | } 19 | } 20 | 21 | return false; 22 | 23 | }, 24 | // Set of bridges to access annotation methods 25 | // Toggle 26 | annotationBridgeToggle: function(e){ 27 | e.preventDefault(); 28 | var annotationObject = this.getAnnotationObject(this.viewer.$(e.target).closest(this.annotationClassName)); 29 | annotationObject.toggle(); 30 | }, 31 | // Show annotation 32 | annotationBridgeShow: function(e){ 33 | e.preventDefault(); 34 | var annotationObject = this.getAnnotationObject(this.viewer.$(e.target).closest(this.annotationClassName)); 35 | annotationObject.show(); 36 | }, 37 | // Hide annotation 38 | annotationBridgeHide: function(e){ 39 | e.preventDefault(); 40 | var annotationObject = this.getAnnotationObject(this.viewer.$(e.target).closest(this.annotationClassName)); 41 | annotationObject.hide(true); 42 | }, 43 | // Jump to the next annotation 44 | annotationBridgeNext: function(e){ 45 | e.preventDefault(); 46 | var annotationObject = this.getAnnotationObject(this.viewer.$(e.target).closest(this.annotationClassName)); 47 | annotationObject.next(); 48 | }, 49 | // Jump to the previous annotation 50 | annotationBridgePrevious: function(e){ 51 | e.preventDefault(); 52 | var annotationObject = this.getAnnotationObject(this.viewer.$(e.target).closest(this.annotationClassName)); 53 | annotationObject.previous(); 54 | }, 55 | // Update currentpage text to indicate current annotation 56 | setAnnotationPosition: function(_position){ 57 | this.elements.currentPage.text(_position); 58 | }, 59 | // Update active annotation limits 60 | setActiveAnnotationLimits: function(annotation){ 61 | var annotation = (annotation) ? annotation : this.viewer.activeAnnotation; 62 | 63 | if(!annotation || annotation == null){ 64 | return; 65 | } 66 | 67 | var elements = this.elements; 68 | var aPage = annotation.page; 69 | var aEl = annotation.annotationEl; 70 | var aPosTop = annotation.position.top * this.models.pages.zoomFactor(); 71 | var _trackAnnotation = this.events.trackAnnotation; 72 | 73 | if(annotation.type === 'page'){ 74 | _trackAnnotation.h = aEl.outerHeight()+aPage.getOffset(); 75 | _trackAnnotation.combined = (aPage.getOffset()) - elements.window.height(); 76 | }else{ 77 | _trackAnnotation.h = aEl.height()+aPosTop-20+aPage.getOffset()+aPage.getPageNoteHeight(); 78 | _trackAnnotation.combined = (aPosTop-20+aPage.getOffset()+aPage.getPageNoteHeight()) - elements.window.height(); 79 | } 80 | 81 | } 82 | }); -------------------------------------------------------------------------------- /public/javascripts/DV/helpers/editor.js: -------------------------------------------------------------------------------- 1 | DV._.extend(DV.Schema.helpers,{ 2 | showAnnotationEdit : function(e) { 3 | var annoEl = this.viewer.$(e.target).closest(this.annotationClassName); 4 | var area = this.viewer.$('.DV-annotationTextArea', annoEl); 5 | annoEl.addClass('DV-editing'); 6 | area.focus(); 7 | }, 8 | cancelAnnotationEdit : function(e) { 9 | var annoEl = this.viewer.$(e.target).closest(this.annotationClassName); 10 | var anno = this.getAnnotationModel(annoEl); 11 | this.viewer.$('.DV-annotationTitleInput', annoEl).val(anno.title); 12 | this.viewer.$('.DV-annotationTextArea', annoEl).val(anno.text); 13 | if (anno.unsaved) { 14 | this.models.annotations.removeAnnotation(anno); 15 | } else { 16 | annoEl.removeClass('DV-editing'); 17 | } 18 | }, 19 | saveAnnotation : function(e, option) { 20 | var target = this.viewer.$(e.target); 21 | var annoEl = target.closest(this.annotationClassName); 22 | var anno = this.getAnnotationModel(annoEl); 23 | if (!anno) return; 24 | anno.title = this.viewer.$('.DV-annotationTitleInput', annoEl).val(); 25 | anno.text = this.viewer.$('.DV-annotationTextArea', annoEl).val(); 26 | anno.owns_note = anno.unsaved ? true : anno.owns_note; 27 | if (anno.owns_note) { 28 | anno.author = anno.author || dc.account.name; 29 | anno.author_organization = anno.author_organization || (dc.account.isReal && dc.account.organization.name); 30 | } 31 | if (target.hasClass('DV-saveAnnotationDraft')) anno.access = 'exclusive'; 32 | else if (annoEl.hasClass('DV-accessExclusive')) anno.access = 'public'; 33 | if (option == 'onlyIfText' && 34 | (!anno.title || anno.title == 'Untitled Note') && 35 | !anno.text && 36 | !anno.server_id) { 37 | return this.models.annotations.removeAnnotation(anno); 38 | } 39 | annoEl.removeClass('DV-editing'); 40 | this.models.annotations.fireSaveCallbacks(anno); 41 | this.viewer.api.redraw(true); 42 | if (this.viewer.activeAnnotation) this.viewer.pageSet.showAnnotation(anno); 43 | }, 44 | deleteAnnotation : function(e) { 45 | var annoEl = this.viewer.$(e.target).closest(this.annotationClassName); 46 | var anno = this.getAnnotationModel(annoEl); 47 | this.models.annotations.removeAnnotation(anno); 48 | this.models.annotations.fireDeleteCallbacks(anno); 49 | } 50 | }); -------------------------------------------------------------------------------- /public/javascripts/DV/helpers/navigation.js: -------------------------------------------------------------------------------- 1 | DV._.extend(DV.Schema.helpers, { 2 | resetNavigationState: function(){ 3 | var elements = this.elements; 4 | if (elements.chaptersContainer.length) elements.chaptersContainer[0].id = ''; 5 | if (elements.navigation.length) elements.navigation[0].id = ''; 6 | }, 7 | setActiveChapter: function(chapterId){ 8 | if (chapterId) this.elements.chaptersContainer.attr('id','DV-selectedChapter-'+chapterId); 9 | }, 10 | setActiveAnnotationInNav: function(annotationId){ 11 | if(annotationId != null){ 12 | this.elements.navigation.attr('id','DV-selectedAnnotation-'+annotationId); 13 | }else{ 14 | this.elements.navigation.attr('id',''); 15 | } 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /public/javascripts/DV/helpers/search.js: -------------------------------------------------------------------------------- 1 | DV._.extend(DV.Schema.helpers, { 2 | getSearchResponse: function(query){ 3 | var handleResponse = DV.jQuery.proxy(function(response){ 4 | this.viewer.searchResponse = response; 5 | var hasResults = (response.results.length > 0) ? true : false; 6 | 7 | var text = hasResults ? 'of '+response.results.length + ' ' : ' '; 8 | this.viewer.$('span.DV-totalSearchResult').text(text); 9 | this.viewer.$('span.DV-searchQuery').text(response.query); 10 | if (hasResults) { 11 | // this.viewer.history.save('search/p'+response.results[0]+'/'+response.query); 12 | var currentPage = this.viewer.models.document.currentPage(); 13 | var page = (DV._.include(response.results, currentPage)) ? currentPage : response.results[0]; 14 | this.events.loadText(page - 1, this.highlightSearchResponses); 15 | } else { 16 | this.highlightSearchResponses(); 17 | } 18 | }, this); 19 | 20 | var failResponse = function() { 21 | this.viewer.$('.DV-currentSearchResult').text('Search is not available at this time'); 22 | this.viewer.$('span.DV-searchQuery').text(query); 23 | this.viewer.$('.DV-searchResults').addClass('DV-noResults'); 24 | }; 25 | 26 | var searchURI = this.viewer.schema.document.resources.search.replace('{query}', encodeURIComponent(query)); 27 | if (this.viewer.helpers.isCrossDomain(searchURI)) searchURI += '&callback=?'; 28 | DV.jQuery.ajax({url : searchURI, dataType : 'json', success : handleResponse, error : failResponse}); 29 | }, 30 | acceptInputCallBack: function(){ 31 | var pageIndex = this.elements.currentPage.first().text(); 32 | // sanitize input 33 | 34 | pageIndex = (pageIndex === '') ? 0 : parseInt(pageIndex, 10) - 1; 35 | pageIndex = (pageIndex < 0) ? 0 : pageIndex; 36 | pageIndex = (pageIndex+1 > this.models.document.totalPages) ? this.models.document.totalPages-1 : pageIndex; 37 | var pageNumber = pageIndex+1; 38 | 39 | this.elements.currentPage.text(pageNumber); 40 | this.viewer.$('.DV-pageNumberContainer input').val(pageNumber); 41 | 42 | if(this.viewer.state === 'ViewDocument' || 43 | this.viewer.state === 'ViewThumbnails'){ 44 | // this.viewer.history.save('document/p'+pageNumber); 45 | this.jump(pageIndex); 46 | }else if(this.viewer.state === 'ViewText'){ 47 | // this.viewer.history.save('text/p'+pageNumber); 48 | this.events.loadText(pageIndex); 49 | } 50 | 51 | }, 52 | highlightSearchResponses: function(){ 53 | var viewer = this.viewer; 54 | var response = viewer.searchResponse; 55 | 56 | if(!response) return false; 57 | 58 | var results = response.results; 59 | var currentResultEl = this.viewer.$('.DV-currentSearchResult'); 60 | 61 | if (results.length == 0){ 62 | currentResultEl.text('No Results'); 63 | this.viewer.$('.DV-searchResults').addClass('DV-noResults'); 64 | }else{ 65 | this.viewer.$('.DV-searchResults').removeClass('DV-noResults'); 66 | } 67 | for(var i = 0; i < response.results.length; i++){ 68 | if(this.models.document.currentPage() === response.results[i]){ 69 | currentResultEl.text('Page ' + (i+1) + ' '); 70 | break; 71 | } 72 | } 73 | 74 | // Replaces spaces in query with `\s+` to match newlines in textContent, 75 | // escape regex char contents (like "()"), and only match on word boundaries. 76 | var boundary = '(\\b|\\B)'; 77 | var query = boundary + '('+response.query.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&").replace(/\s+/g, '\\s+')+')' + boundary; 78 | var textContent = this.viewer.$('.DV-textContents'); 79 | var currentPageText = textContent.text(); 80 | var pattern = new RegExp(query,"ig"); 81 | var replacement = currentPageText.replace(pattern,'$1$2$3'); 82 | 83 | textContent.html(replacement); 84 | 85 | var highlightIndex = (viewer.toHighLight) ? viewer.toHighLight : 0; 86 | this.highlightMatch(highlightIndex); 87 | 88 | // cleanup 89 | currentResultEl = null; 90 | textContent = null; 91 | 92 | }, 93 | // Highlight a single instance of an entity on the page. Make sure to 94 | // convert into proper UTF8 before trying to get the entity length, and 95 | // then back into UTF16 again. 96 | highlightEntity: function(offset, length) { 97 | this.viewer.$('.DV-searchResults').addClass('DV-noResults'); 98 | var textContent = this.viewer.$('.DV-textContents'); 99 | var text = textContent.text(); 100 | var pre = text.substr(0, offset); 101 | var entity = text.substr(offset, length); 102 | var post = text.substr(offset + length); 103 | text = [pre, '', entity, '', post].join(''); 104 | textContent.html(text); 105 | this.highlightMatch(0); 106 | }, 107 | 108 | highlightMatch: function(index){ 109 | var highlightsOnThisPage = this.viewer.$('.DV-textContents span.DV-searchMatch'); 110 | if (highlightsOnThisPage.length == 0) return false; 111 | var currentPageIndex = this.getCurrentSearchPageIndex(); 112 | var toHighLight = this.viewer.toHighLight; 113 | 114 | if(toHighLight){ 115 | if(toHighLight !== false){ 116 | if(toHighLight === 'last'){ 117 | index = highlightsOnThisPage.length - 1; 118 | }else if(toHighLight === 'first'){ 119 | index = 0; 120 | }else{ 121 | index = toHighLight; 122 | } 123 | } 124 | toHighLight = false; 125 | this.viewer.toHighLight = null; 126 | } 127 | var searchResponse = this.viewer.searchResponse; 128 | if (searchResponse) { 129 | if(index === (highlightsOnThisPage.length)){ 130 | 131 | if(searchResponse.results.length === currentPageIndex+1){ 132 | return; 133 | } 134 | 135 | this.events.loadText(searchResponse.results[currentPageIndex + 1] - 1,this.highlightSearchResponses); 136 | 137 | return; 138 | }else if(index === -1){ 139 | if(currentPageIndex-1 < 0){ 140 | return false; 141 | } 142 | this.viewer.toHighLight = 'last'; 143 | this.events.loadText(String(searchResponse.results[currentPageIndex - 1] - 1),this.highlightSearchResponses); 144 | 145 | return; 146 | } 147 | highlightsOnThisPage.removeClass('DV-highlightedMatch'); 148 | } 149 | 150 | var match = this.viewer.$('.DV-textContents span.DV-searchMatch:eq('+index+')'); 151 | match.addClass('DV-highlightedMatch'); 152 | 153 | this.elements.window[0].scrollTop = match.position().top - 50; 154 | if (searchResponse) searchResponse.highlighted = index; 155 | 156 | // cleanup 157 | highlightsOnThisPage = null; 158 | match = null; 159 | }, 160 | getCurrentSearchPageIndex: function(){ 161 | var searchResponse = this.viewer.searchResponse; 162 | if(!searchResponse) { 163 | return false; 164 | } 165 | var docModel = this.models.document; 166 | for(var i = 0,len = searchResponse.results.length; i 0){ 34 | for(var i = 0,len = this.page.annotations.length;i 0) ? 'right' : 'left'; 75 | var directionY = (deltaY > 0) ? 'down' : 'up'; 76 | this.pageY = e.pageY; 77 | this.pageX = e.pageX; 78 | if (deltaY === 0 && deltaX === 0) return; 79 | this.dispatcher({ event: e, deltaX: deltaX, deltaY: deltaY, directionX: directionX, directionY: directionY }); 80 | }; 81 | -------------------------------------------------------------------------------- /public/javascripts/DV/lib/elements.js: -------------------------------------------------------------------------------- 1 | DV.Elements = function(viewer){ 2 | this._viewer = viewer; 3 | var elements = DV.Schema.elements; 4 | for (var i=0, elemCount=elements.length; i < elemCount; i++) { 5 | this.getElement(elements[i]); 6 | } 7 | }; 8 | 9 | // Get and store an element reference 10 | DV.Elements.prototype.getElement = function(elementQuery,force){ 11 | this[elementQuery.name] = this._viewer.$(elementQuery.query); 12 | }; 13 | -------------------------------------------------------------------------------- /public/javascripts/DV/lib/history.js: -------------------------------------------------------------------------------- 1 | // Handles JavaScript history management and callbacks. To use, register a 2 | // regexp that matches the history hash with its corresponding callback: 3 | // 4 | // dc.history.register(/^#search/, controller.runSearch); 5 | // 6 | // And then you can save arbitrary history fragments. 7 | // 8 | // dc.history.save('search/freedom/p3'); 9 | // 10 | // Initialize history with an empty set of handlers. 11 | // Bind to the HTML5 'onhashchange' callback, if it exists. Otherwise, 12 | // start polling the window location. 13 | DV.History = function(viewer) { 14 | this.viewer = viewer; 15 | 16 | // Ensure we don't accidentally bind to history twice. 17 | DV.History.count++; 18 | 19 | // The interval at which the window location is polled. 20 | this.URL_CHECK_INTERVAL = 500; 21 | 22 | // We need to use an iFrame to save history if we're in an old version of IE. 23 | this.USE_IFRAME = DV.jQuery.browser.msie && DV.jQuery.browser.version < 8; 24 | 25 | // The ordered list of history handlers matchers and callbacks. 26 | this.handlers = []; 27 | this.defaultCallback = null; 28 | 29 | // The current recorded window.location.hash. 30 | this.hash = window.location.hash; 31 | 32 | DV._.bindAll(this, 'checkURL'); 33 | if (DV.History.count > 1) return; 34 | 35 | // Wait until the window loads. 36 | DV.jQuery(DV._.bind(function() { 37 | if (this.USE_IFRAME) this.iframe = DV.jQuery(' 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /viewer-mini-debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document Viewer (Mini Debug) 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 | 97 | 98 |



99 | 100 |
101 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /viewer-oembed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document Title 7 | 8 | 9 | 10 |
11 | 12 | 13 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /viewer-responsive.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document Viewer (Responsive Debug) 9 | 10 | 11 | 12 | 13 | 14 | 15 | 50 | 51 | 52 | 53 |
54 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /viewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document Viewer (Debug) 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 |
24 | 33 | 34 | 35 | 36 | --------------------------------------------------------------------------------