diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index 944d4879c9..66e6551019 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -422,6 +422,18 @@ class ContentStoreTest(ModuleStoreTestCase): self.assertIn('markdown', problem.metadata, "markdown is missing from metadata") self.assertNotIn('markdown', problem.editable_metadata_fields, "Markdown slipped into the editable metadata fields") + def test_import_metadata_with_attempts_empty_string(self): + import_from_xml(modulestore(), 'common/test/data/', ['simple']) + ms = modulestore('direct') + did_load_item = False + try: + ms.get_item(Location(['i4x', 'edX', 'simple', 'problem', 'ps01-simple', None])) + did_load_item = True + except ItemNotFoundError: + pass + + # make sure we found the item (e.g. it didn't error while loading) + self.assertTrue(did_load_item) def test_metadata_inheritance(self): import_from_xml(modulestore(), 'common/test/data/', ['full']) diff --git a/cms/djangoapps/contentstore/tests/utils.py b/cms/djangoapps/contentstore/tests/utils.py index be028b2836..b6b8cd5023 100644 --- a/cms/djangoapps/contentstore/tests/utils.py +++ b/cms/djangoapps/contentstore/tests/utils.py @@ -1,6 +1,6 @@ import json import copy -from time import time +from uuid import uuid4 from django.test import TestCase from django.conf import settings @@ -20,13 +20,12 @@ class ModuleStoreTestCase(TestCase): def _pre_setup(self): super(ModuleStoreTestCase, self)._pre_setup() - # Use the current seconds since epoch to differentiate + # Use a uuid to differentiate # the mongo collections on jenkins. - sec_since_epoch = '%s' % int(time() * 100) self.orig_MODULESTORE = copy.deepcopy(settings.MODULESTORE) self.test_MODULESTORE = self.orig_MODULESTORE - self.test_MODULESTORE['default']['OPTIONS']['collection'] = 'modulestore_%s' % sec_since_epoch - self.test_MODULESTORE['direct']['OPTIONS']['collection'] = 'modulestore_%s' % sec_since_epoch + self.test_MODULESTORE['default']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex + self.test_MODULESTORE['direct']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex settings.MODULESTORE = self.test_MODULESTORE # Flush and initialize the module store diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 2c69c449ba..8b2d5a6c92 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -352,6 +352,13 @@ class CourseDescriptor(SequenceDescriptor): """ return self.metadata.get('tabs') + @property + def pdf_textbooks(self): + """ + Return the pdf_textbooks config, as a python object, or None if not specified. + """ + return self.metadata.get('pdf_textbooks') + @tabs.setter def tabs(self, value): self.metadata['tabs'] = value diff --git a/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee b/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee index 39c91d8c70..d38036c8de 100644 --- a/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee +++ b/common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee @@ -1,6 +1,21 @@ class @Rubric constructor: () -> + @initialize: (location) -> + $('.rubric').data("location", location) + $('input[class="score-selection"]').change @tracking_callback + + @tracking_callback: (event) -> + target_selection = $(event.target).val() + # chop off the beginning of the name so that we can get the number of the category + category = $(event.target).data("category") + location = $('.rubric').data('location') + # probably want the original problem location as well + + data = {location: location, selection: target_selection, category: category} + Logger.log 'rubric_select', data + + # finds the scores for each rubric category @get_score_list: () => # find the number of categories: @@ -45,6 +60,9 @@ class @CombinedOpenEnded @task_count = @el.data('task-count') @task_number = @el.data('task-number') @accept_file_upload = @el.data('accept-file-upload') + @location = @el.data('location') + # set up handlers for click tracking + Rubric.initialize(@location) @allow_reset = @el.data('allow_reset') @reset_button = @$('.reset-button') @@ -118,6 +136,9 @@ class @CombinedOpenEnded @submit_evaluation_button = $('.submit-evaluation-button') @submit_evaluation_button.click @message_post Collapsible.setCollapsibles(@results_container) + # make sure we still have click tracking + $('.evaluation-response a').click @log_feedback_click + $('input[name="evaluation-score"]').change @log_feedback_selection show_results: (event) => status_item = $(event.target).parent() @@ -155,7 +176,6 @@ class @CombinedOpenEnded @legend_container= $('.legend-container') message_post: (event)=> - Logger.log 'message_post', @answers external_grader_message=$(event.target).parent().parent().parent() evaluation_scoring = $(event.target).parent() @@ -184,6 +204,7 @@ class @CombinedOpenEnded $('section.evaluation').slideToggle() @message_wrapper.html(response.message_html) + $.ajaxWithPrefix("#{@ajax_url}/save_post_assessment", settings) @@ -406,7 +427,7 @@ class @CombinedOpenEnded $.postWithPrefix "#{@ajax_url}/check_for_score", (response) => if response.state == "done" or response.state=="post_assessment" delete window.queuePollerID - location.reload() + @reload() else window.queuePollerID = window.setTimeout(@poll, 10000) @@ -440,7 +461,9 @@ class @CombinedOpenEnded @prompt_container.toggleClass('open') if @question_header.text() == "(Hide)" new_text = "(Show)" + Logger.log 'oe_hide_question', {location: @location} else + Logger.log 'oe_show_question', {location: @location} new_text = "(Hide)" @question_header.text(new_text) @@ -456,4 +479,16 @@ class @CombinedOpenEnded @prompt_container.toggleClass('open') @question_header.text("(Show)") + log_feedback_click: (event) -> + link_text = $(event.target).html() + if link_text == 'See full feedback' + Logger.log 'oe_show_full_feedback', {} + else if link_text == 'Respond to Feedback' + Logger.log 'oe_show_respond_to_feedback', {} + else + generated_event_type = link_text.toLowerCase().replace(" ","_") + Logger.log "oe_" + generated_event_type, {} + log_feedback_selection: (event) -> + target_selection = $(event.target).val() + Logger.log 'oe_feedback_response_selected', {value: target_selection} diff --git a/common/lib/xmodule/xmodule/js/src/peergrading/peer_grading_problem.coffee b/common/lib/xmodule/xmodule/js/src/peergrading/peer_grading_problem.coffee index 63c58e1766..0b38090e43 100644 --- a/common/lib/xmodule/xmodule/js/src/peergrading/peer_grading_problem.coffee +++ b/common/lib/xmodule/xmodule/js/src/peergrading/peer_grading_problem.coffee @@ -426,6 +426,7 @@ class @PeerGradingProblem @submit_button.hide() @action_button.hide() @calibration_feedback_panel.hide() + Rubric.initialize(@location) render_calibration_feedback: (response) => @@ -476,7 +477,9 @@ class @PeerGradingProblem @prompt_container.slideToggle() @prompt_container.toggleClass('open') if @question_header.text() == "(Hide)" + Logger.log 'peer_grading_hide_question', {location: @location} new_text = "(Show)" else + Logger.log 'peer_grading_show_question', {location: @location} new_text = "(Hide)" @question_header.text(new_text) diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py index cc0a957e66..171441c562 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py @@ -330,6 +330,7 @@ class CombinedOpenEndedV1Module(): 'status': self.get_status(False), 'display_name': self.display_name, 'accept_file_upload': self.accept_file_upload, + 'location': self.location, 'legend_list' : LEGEND_LIST, } @@ -814,4 +815,4 @@ class CombinedOpenEndedV1Descriptor(XmlDescriptor, EditingDescriptor): for child in ['task']: add_child(child) - return elt \ No newline at end of file + return elt diff --git a/lms/djangoapps/open_ended_grading/controller_query_service.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/controller_query_service.py similarity index 81% rename from lms/djangoapps/open_ended_grading/controller_query_service.py rename to common/lib/xmodule/xmodule/open_ended_grading_classes/controller_query_service.py index 1b124fc116..1dd5c57ad4 100644 --- a/lms/djangoapps/open_ended_grading/controller_query_service.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/controller_query_service.py @@ -1,8 +1,5 @@ import logging -from xmodule.open_ended_grading_classes.grading_service_module import GradingService - -from xmodule.x_module import ModuleSystem -from mitxmako.shortcuts import render_to_string +from grading_service_module import GradingService log = logging.getLogger(__name__) @@ -11,8 +8,8 @@ class ControllerQueryService(GradingService): """ Interface to staff grading backend. """ - def __init__(self, config): - config['system'] = ModuleSystem(None, None, None, render_to_string, None) + def __init__(self, config, system): + config['system'] = system super(ControllerQueryService, self).__init__(config) self.url = config['url'] + config['grading_controller'] self.login_url = self.url + '/login/' @@ -77,3 +74,16 @@ class ControllerQueryService(GradingService): response = self.post(self.take_action_on_flags_url, params) return response + +def convert_seconds_to_human_readable(seconds): + if seconds < 60: + human_string = "{0} seconds".format(seconds) + elif seconds < 60 * 60: + human_string = "{0} minutes".format(round(seconds/60,1)) + elif seconds < (24*60*60): + human_string = "{0} hours".format(round(seconds/(60*60),1)) + else: + human_string = "{0} days".format(round(seconds/(60*60*24),1)) + + eta_string = "{0}".format(human_string) + return eta_string diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py index 0b546482f4..974d23965f 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py @@ -663,17 +663,21 @@ class OpenEndedModule(openendedchild.OpenEndedChild): Output: Rendered HTML """ #set context variables and render template + eta_string = None if self.state != self.INITIAL: latest = self.latest_answer() previous_answer = latest if latest is not None else self.initial_display post_assessment = self.latest_post_assessment(system) score = self.latest_score() correct = 'correct' if self.is_submission_correct(score) else 'incorrect' + if self.state == self.ASSESSING: + eta_string = self.get_eta() else: post_assessment = "" correct = "" previous_answer = self.initial_display + context = { 'prompt': self.prompt, 'previous_answer': previous_answer, @@ -686,6 +690,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): 'child_type': 'openended', 'correct': correct, 'accept_file_upload': self.accept_file_upload, + 'eta_message' : eta_string, } html = system.render_template('open_ended.html', context) return html diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py index 01ccf4c7ac..50f9534717 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py @@ -23,6 +23,7 @@ from xmodule.xml_module import XmlDescriptor from xmodule.modulestore import Location from capa.util import * from peer_grading_service import PeerGradingService +import controller_query_service from datetime import datetime @@ -106,8 +107,10 @@ class OpenEndedChild(object): # completion (doesn't matter if you self-assessed correct/incorrect). self._max_score = static_data['max_score'] self.peer_gs = PeerGradingService(system.open_ended_grading_interface, system) + self.controller_qs = controller_query_service.ControllerQueryService(system.open_ended_grading_interface,system) self.system = system + self.location_string = location try: self.location_string = self.location_string.url() @@ -354,6 +357,10 @@ class OpenEndedChild(object): if get_data['can_upload_files'] in ['true', '1']: has_file_to_upload = True file = get_data['student_file'][0] + if self.system.track_fuction: + self.system.track_function('open_ended_image_upload', {'filename': file.name}) + else: + log.info("No tracking function found when uploading image.") uploaded_to_s3, image_ok, s3_public_url = self.upload_image_to_s3(file) if uploaded_to_s3: image_tag = self.generate_image_tag_from_url(s3_public_url, file.name) @@ -434,14 +441,14 @@ class OpenEndedChild(object): error_string = ("You need to peer grade {0} more in order to make another submission. " "You have graded {1}, and {2} are required. You have made {3} successful peer grading submissions.") try: - response = self.peer_gs.get_data_for_location(location, student_id) + response = self.peer_gs.get_data_for_location(self.location_string, student_id) count_graded = response['count_graded'] count_required = response['count_required'] student_sub_count = response['student_sub_count'] success = True except: #This is a dev_facing_error - log.error("Could not contact external open ended graders for location {0} and student {1}".format(location,student_id)) + log.error("Could not contact external open ended graders for location {0} and student {1}".format(self.location_string,student_id)) #This is a student_facing_error error_message = "Could not contact the graders. Please notify course staff." return success, allowed_to_submit, error_message @@ -453,3 +460,24 @@ class OpenEndedChild(object): error_message = error_string.format(count_required-count_graded, count_graded, count_required, student_sub_count) return success, allowed_to_submit, error_message + def get_eta(self): + response = self.controller_qs.check_for_eta(self.location_string) + try: + response = json.loads(response) + except: + pass + + success = response['success'] + if isinstance(success, basestring): + success = (success.lower()=="true") + + if success: + eta = controller_query_service.convert_seconds_to_human_readable(response['eta']) + eta_string = "Please check back for your response in at most {0}.".format(eta) + else: + eta_string = "" + + return eta_string + + + diff --git a/common/lib/xmodule/xmodule/xml_module.py b/common/lib/xmodule/xmodule/xml_module.py index 64c3aabbcc..773531c528 100644 --- a/common/lib/xmodule/xmodule/xml_module.py +++ b/common/lib/xmodule/xmodule/xml_module.py @@ -128,8 +128,7 @@ class XmlDescriptor(XModuleDescriptor): 'graded': bool_map, 'hide_progress_tab': bool_map, 'allow_anonymous': bool_map, - 'allow_anonymous_to_peers': bool_map, - 'weight': int_map + 'allow_anonymous_to_peers': bool_map } diff --git a/common/static/css/pdfviewer.css b/common/static/css/pdfviewer.css new file mode 100644 index 0000000000..716db251f0 --- /dev/null +++ b/common/static/css/pdfviewer.css @@ -0,0 +1,760 @@ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +* { + padding: 0; + margin: 0; +} + +html { + height: 100%; +} + +body { + height: 100%; + background-color: #404040; + background-image: url(vendor/pdfjs/images/texture.png); +} + +body, +input, +button, +select { + font: message-box; +} + +.hidden { + display: none; +} +[hidden] { + display: none !important; +} + +#viewerContainer.presentationControls { + cursor: default; +} +*/ + +/* outer/inner center provides horizontal center */ +.outerCenter { + float: right; + position: relative; + right: 50%; +} + +.innerCenter { + float: right; + position: relative; + right: -50%; +} + +#outerContainer { + width: 100%; + height: 100%; +} + +#mainContainer { +/* position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0;*/ + -webkit-transition-duration: 200ms; + -webkit-transition-timing-function: ease; + -moz-transition-duration: 200ms; + -moz-transition-timing-function: ease; + -ms-transition-duration: 200ms; + -ms-transition-timing-function: ease; + -o-transition-duration: 200ms; + -o-transition-timing-function: ease; + transition-duration: 200ms; + transition-timing-function: ease; +} + +#viewerContainer { + overflow: auto; + box-shadow: inset 1px 0 0 hsla(0,0%,100%,.05); +/* position: absolute; + top: 32px; + right: 0; + bottom: 0; + left: 0; */ +} + +.toolbar { +/* position: absolute; */ + left: 0; + right: 0; + height: 32px; + z-index: 9999; + cursor: default; +} + +#toolbarContainer { + width: 100%; +} + + +#toolbarViewer { + position: relative; + height: 32px; + background-image: url(vendor/pdfjs/images/texture.png), + -webkit-linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95)); + background-image: url(vendor/pdfjs/images/texture.png), + -moz-linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95)); + background-image: url(vendor/pdfjs/images/texture.png), + -ms-linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95)); + background-image: url(vendor/pdfjs/images/texture.png), + -o-linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95)); + background-image: url(vendor/pdfjs/images/texture.png), + linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95)); + box-shadow: inset 1px 0 0 hsla(0,0%,100%,.08), + inset 0 1px 1px hsla(0,0%,0%,.15), + inset 0 -1px 0 hsla(0,0%,100%,.05), + 0 1px 0 hsla(0,0%,0%,.15), + 0 1px 1px hsla(0,0%,0%,.1); +} + +#toolbarViewerLeft { + margin-left: -1px; +/* position: absolute; */ + top: 0; + left: 0; +} + +#toolbarViewerRight { +/* position: absolute; */ + top: 0; + right: 0; +} + +#toolbarViewerLeft > *, +#toolbarViewerMiddle > *, +#toolbarViewerRight > * { + float: left; +} + +.splitToolbarButton { + margin: 3px 2px 4px 0; + display: inline-block; +} +.splitToolbarButton > .toolbarButton { + border-radius: 0; + float: left; +} + +.toolbarButton { + border: 0 none; + background-color: rgba(0, 0, 0, 0); + width: 32px; + height: 25px; +} + +.toolbarButton > span { + display: inline-block; + width: 0; + height: 0; + overflow: hidden; +} + +.toolbarButton[disabled] { + opacity: .5; +} + +.toolbarButton.group { + margin-right:0; +} + +.splitToolbarButton.toggled .toolbarButton { + margin: 0; +} + +.splitToolbarButton:hover > .toolbarButton, +.splitToolbarButton:focus > .toolbarButton, +.splitToolbarButton.toggled > .toolbarButton, +.toolbarButton.textButton { + background-color: hsla(0,0%,0%,.12); + background-image: -webkit-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -ms-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -o-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-clip: padding-box; + border: 1px solid hsla(0,0%,0%,.35); + border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42); + box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, + 0 0 1px hsla(0,0%,100%,.15) inset, + 0 1px 0 hsla(0,0%,100%,.05); + -webkit-transition-property: background-color, border-color, box-shadow; + -webkit-transition-duration: 150ms; + -webkit-transition-timing-function: ease; + -moz-transition-property: background-color, border-color, box-shadow; + -moz-transition-duration: 150ms; + -moz-transition-timing-function: ease; + -ms-transition-property: background-color, border-color, box-shadow; + -ms-transition-duration: 150ms; + -ms-transition-timing-function: ease; + -o-transition-property: background-color, border-color, box-shadow; + -o-transition-duration: 150ms; + -o-transition-timing-function: ease; + transition-property: background-color, border-color, box-shadow; + transition-duration: 150ms; + transition-timing-function: ease; + +} +.splitToolbarButton > .toolbarButton:hover, +.splitToolbarButton > .toolbarButton:focus, +.dropdownToolbarButton:hover, +.toolbarButton.textButton:hover, +.toolbarButton.textButton:focus { + background-color: hsla(0,0%,0%,.2); + box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, + 0 0 1px hsla(0,0%,100%,.15) inset, + 0 0 1px hsla(0,0%,0%,.05); + z-index: 199; +} +.splitToolbarButton > .toolbarButton:first-child { + position: relative; + margin: 0; + margin-right: -1px; + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; + border-right-color: transparent; +} +.splitToolbarButton > .toolbarButton:last-child { + position: relative; + margin: 0; + margin-left: -1px; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + border-left-color: transparent; +} +.splitToolbarButtonSeparator { + padding: 8px 0; + width: 1px; + background-color: hsla(0,0%,00%,.5); + z-index: 99; + box-shadow: 0 0 0 1px hsla(0,0%,100%,.08); + display: inline-block; + margin: 5px 0; + float:left; +} +} +.splitToolbarButton:hover > .splitToolbarButtonSeparator, +.splitToolbarButton.toggled > .splitToolbarButtonSeparator { + padding: 12px 0; + margin: 1px 0; + box-shadow: 0 0 0 1px hsla(0,0%,100%,.03); + -webkit-transition-property: padding; + -webkit-transition-duration: 10ms; + -webkit-transition-timing-function: ease; + -moz-transition-property: padding; + -moz-transition-duration: 10ms; + -moz-transition-timing-function: ease; + -ms-transition-property: padding; + -ms-transition-duration: 10ms; + -ms-transition-timing-function: ease; + -o-transition-property: padding; + -o-transition-duration: 10ms; + -o-transition-timing-function: ease; + transition-property: padding; + transition-duration: 10ms; + transition-timing-function: ease; +} + +.toolbarButton, +.dropdownToolbarButton { + min-width: 16px; + padding: 2px 6px 0; + border: 1px solid transparent; + border-radius: 2px; + color: hsl(0,0%,95%); + font-size: 12px; + line-height: 14px; + -webkit-user-select:none; + -moz-user-select:none; + -ms-user-select:none; + /* Opera does not support user-select, use <... unselectable="on"> instead */ + cursor: default; + -webkit-transition-property: background-color, border-color, box-shadow; + -webkit-transition-duration: 150ms; + -webkit-transition-timing-function: ease; + -moz-transition-property: background-color, border-color, box-shadow; + -moz-transition-duration: 150ms; + -moz-transition-timing-function: ease; + -ms-transition-property: background-color, border-color, box-shadow; + -ms-transition-duration: 150ms; + -ms-transition-timing-function: ease; + -o-transition-property: background-color, border-color, box-shadow; + -o-transition-duration: 150ms; + -o-transition-timing-function: ease; + transition-property: background-color, border-color, box-shadow; + transition-duration: 150ms; + transition-timing-function: ease; +} + +.toolbarButton { + margin: 3px 2px 4px 0; +} + +.toolbarButton:hover, +.toolbarButton:focus, +.dropdownToolbarButton { + background-color: hsla(0,0%,0%,.12); + background-image: -webkit-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -ms-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -o-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-clip: padding-box; + border: 1px solid hsla(0,0%,0%,.35); + border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42); + box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, + 0 0 1px hsla(0,0%,100%,.15) inset, + 0 1px 0 hsla(0,0%,100%,.05); +} + +.toolbarButton:hover:active, +.dropdownToolbarButton:hover:active { + background-color: hsla(0,0%,0%,.2); + background-image: -webkit-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -ms-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -o-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + border-color: hsla(0,0%,0%,.35) hsla(0,0%,0%,.4) hsla(0,0%,0%,.45); + box-shadow: 0 1px 1px hsla(0,0%,0%,.1) inset, + 0 0 1px hsla(0,0%,0%,.2) inset, + 0 1px 0 hsla(0,0%,100%,.05); + -webkit-transition-property: background-color, border-color, box-shadow; + -webkit-transition-duration: 10ms; + -webkit-transition-timing-function: linear; + -moz-transition-property: background-color, border-color, box-shadow; + -moz-transition-duration: 10ms; + -moz-transition-timing-function: linear; + -ms-transition-property: background-color, border-color, box-shadow; + -ms-transition-duration: 10ms; + -ms-transition-timing-function: linear; + -o-transition-property: background-color, border-color, box-shadow; + -o-transition-duration: 10ms; + -o-transition-timing-function: linear; + transition-property: background-color, border-color, box-shadow; + transition-duration: 10ms; + transition-timing-function: linear; +} + +.toolbarButton.toggled, +.splitToolbarButton.toggled > .toolbarButton.toggled { + background-color: hsla(0,0%,0%,.3); + background-image: -webkit-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -ms-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -o-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.45) hsla(0,0%,0%,.5); + box-shadow: 0 1px 1px hsla(0,0%,0%,.1) inset, + 0 0 1px hsla(0,0%,0%,.2) inset, + 0 1px 0 hsla(0,0%,100%,.05); + -webkit-transition-property: background-color, border-color, box-shadow; + -webkit-transition-duration: 10ms; + -webkit-transition-timing-function: linear; + -moz-transition-property: background-color, border-color, box-shadow; + -moz-transition-duration: 10ms; + -moz-transition-timing-function: linear; + -ms-transition-property: background-color, border-color, box-shadow; + -ms-transition-duration: 10ms; + -ms-transition-timing-function: linear; + -o-transition-property: background-color, border-color, box-shadow; + -o-transition-duration: 10ms; + -o-transition-timing-function: linear; + transition-property: background-color, border-color, box-shadow; + transition-duration: 10ms; + transition-timing-function: linear; +} + +.toolbarButton.toggled:hover:active, +.splitToolbarButton.toggled > .toolbarButton.toggled:hover:active { + background-color: hsla(0,0%,0%,.4); + border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.5) hsla(0,0%,0%,.55); + box-shadow: 0 1px 1px hsla(0,0%,0%,.2) inset, + 0 0 1px hsla(0,0%,0%,.3) inset, + 0 1px 0 hsla(0,0%,100%,.05); +} + +.dropdownToolbarButton { + max-width: 120px; + padding: 3px 2px 2px; + overflow: hidden; + background: url(vendor/pdfjs/images/toolbarButton-menuArrows.png) no-repeat; + background-position: 95%; +} + +.dropdownToolbarButton > select { + -webkit-appearance: none; + -moz-appearance: none; /* in the future this might matter, see bugzilla bug #649849 */ + min-width: 140px; + font-size: 12px; + color: hsl(0,0%,95%); + margin:0; + padding:0; + border:none; + background: rgba(0,0,0,0); /* Opera does not support 'transparent' background */ +} + +.dropdownToolbarButton > select > option { + background: hsl(0,0%,24%); +} + +#customScaleOption { + display: none; +} + +#pageWidthOption { + border-bottom: 1px rgba(255, 255, 255, .5) solid; +} + +html[dir='ltr'] .splitToolbarButton:first-child, +html[dir='ltr'] .toolbarButton:first-child, +html[dir='rtl'] .splitToolbarButton:last-child, +html[dir='rtl'] .toolbarButton:last-child { + margin-left: 4px; +} +html[dir='ltr'] .splitToolbarButton:last-child, +html[dir='ltr'] .toolbarButton:last-child, +html[dir='rtl'] .splitToolbarButton:first-child, +html[dir='rtl'] .toolbarButton:first-child { + margin-right: 4px; +} + +.toolbarButtonSpacer { + width: 30px; + display: inline-block; + height: 1px; +} + +.toolbarButtonFlexibleSpacer { + -webkit-box-flex: 1; + -moz-box-flex: 1; + min-width: 30px; +} + +.toolbarButton#sidebarToggle::before { + display: inline-block; + content: url(images/toolbarButton-sidebarToggle.png); +} + +html[dir='ltr'] .toolbarButton.findPrevious::before { + display: inline-block; + content: url(images/findbarButton-previous.png); +} + +html[dir='rtl'] .toolbarButton.findPrevious::before { + display: inline-block; + content: url(images/findbarButton-previous-rtl.png); +} + +html[dir='ltr'] .toolbarButton.findNext::before { + display: inline-block; + content: url(images/findbarButton-next.png); +} + +html[dir='rtl'] .toolbarButton.findNext::before { + display: inline-block; + content: url(images/findbarButton-next-rtl.png); +} + +html[dir='ltr'] .toolbarButton.pageUp::before { + display: inline-block; + content: url(images/toolbarButton-pageUp.png); +} + +html[dir='rtl'] .toolbarButton.pageUp::before { + display: inline-block; + content: url(images/toolbarButton-pageUp-rtl.png); +} + +html[dir='ltr'] .toolbarButton.pageDown::before { + display: inline-block; + content: url(images/toolbarButton-pageDown.png); +} + +html[dir='rtl'] .toolbarButton.pageDown::before { + display: inline-block; + content: url(images/toolbarButton-pageDown-rtl.png); +} + +.toolbarButton.zoomOut::before { + display: inline-block; + content: url(images/toolbarButton-zoomOut.png); +} + +.toolbarButton.zoomIn::before { + display: inline-block; + content: url(images/toolbarButton-zoomIn.png); +} + +.toolbarButton.fullscreen::before { + display: inline-block; + content: url(images/toolbarButton-fullscreen.png); +} + +.toolbarButton.print::before { + display: inline-block; + content: url(images/toolbarButton-print.png); +} + +.toolbarButton.openFile::before { + display: inline-block; + content: url(images/toolbarButton-openFile.png); +} + +.toolbarButton.download::before { + display: inline-block; + content: url(images/toolbarButton-download.png); +} + +.toolbarButton.bookmark { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-top: 3px; + padding-top: 4px; +} + +.toolbarButton.bookmark::before { + content: url(images/toolbarButton-bookmark.png); +} + +#viewThumbnail.toolbarButton::before { + display: inline-block; + content: url(images/toolbarButton-viewThumbnail.png); +} + +#viewOutline.toolbarButton::before { + display: inline-block; + content: url(images/toolbarButton-viewOutline.png); +} + +#viewFind.toolbarButton::before { + display: inline-block; + content: url(images/toolbarButton-search.png); +} + + +.toolbarField { + padding: 3px 6px; + margin: 4px 0 4px 0; + border: 1px solid transparent; + border-radius: 2px; + background-color: hsla(0,0%,100%,.09); + background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-clip: padding-box; + border: 1px solid hsla(0,0%,0%,.35); + border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42); + box-shadow: 0 1px 0 hsla(0,0%,0%,.05) inset, + 0 1px 0 hsla(0,0%,100%,.05); + color: hsl(0,0%,95%); + font-size: 12px; + line-height: 14px; + outline-style: none; + -moz-transition-property: background-color, border-color, box-shadow; + -moz-transition-duration: 150ms; + -moz-transition-timing-function: ease; +} + +.toolbarField[type=checkbox] { + display: inline-block; + margin: 8px 0px; +} + +.toolbarField.pageNumber { + min-width: 16px; + text-align: right; + width: 40px; +} + +.toolbarField.pageNumber::-webkit-inner-spin-button, +.toolbarField.pageNumber::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + +.toolbarField:hover { + background-color: hsla(0,0%,100%,.11); + border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.43) hsla(0,0%,0%,.45); +} + +.toolbarField:focus { + background-color: hsla(0,0%,100%,.15); + border-color: hsla(204,100%,65%,.8) hsla(204,100%,65%,.85) hsla(204,100%,65%,.9); +} + +.toolbarLabel { + min-width: 16px; + padding: 3px 6px 3px 2px; + margin: 4px 2px 4px 0; + border: 1px solid transparent; + border-radius: 2px; + color: hsl(0,0%,85%); + font-size: 12px; + line-height: 14px; + text-align: left; + -webkit-user-select:none; + -moz-user-select:none; + cursor: default; +} + +#thumbnailView { + position: absolute; + width: 120px; + top: 0; + bottom: 0; + padding: 10px 40px 0; + overflow: auto; +} + +.thumbnail { + margin-bottom: 15px; + float: left; +} + +.thumbnail:not([data-loaded]) { + border: 1px dashed rgba(255, 255, 255, 0.5); +} + +.thumbnailImage { + -moz-transition-duration: 150ms; + border: 1px solid transparent; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3); + opacity: 0.8; + z-index: 99; +} + +.thumbnailSelectionRing { + border-radius: 2px; + padding: 7px; + -moz-transition-duration: 150ms; +} + +a:focus > .thumbnail > .thumbnailSelectionRing > .thumbnailImage, +.thumbnail:hover > .thumbnailSelectionRing > .thumbnailImage { + opacity: .9; +} + +a:focus > .thumbnail > .thumbnailSelectionRing, +.thumbnail:hover > .thumbnailSelectionRing { + background-color: hsla(0,0%,100%,.15); + background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-clip: padding-box; + box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, + 0 0 1px hsla(0,0%,100%,.2) inset, + 0 0 1px hsla(0,0%,0%,.2); + color: hsla(0,0%,100%,.9); +} + +.thumbnail.selected > .thumbnailSelectionRing > .thumbnailImage { + box-shadow: 0 0 0 1px hsla(0,0%,0%,.5); + opacity: 1; +} + +.thumbnail.selected > .thumbnailSelectionRing { + background-color: hsla(0,0%,100%,.3); + background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-clip: padding-box; + box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, + 0 0 1px hsla(0,0%,100%,.1) inset, + 0 0 1px hsla(0,0%,0%,.2); + color: hsla(0,0%,100%,1); +} + +#outlineView { + position: absolute; + width: 192px; + top: 0; + bottom: 0; + padding: 4px 4px 0; + overflow: auto; + -webkit-user-select:none; + -moz-user-select:none; +} + +html[dir='ltr'] .outlineItem > .outlineItems { + margin-left: 20px; +} + +html[dir='rtl'] .outlineItem > .outlineItems { + margin-right: 20px; +} + +.outlineItem > a { + text-decoration: none; + display: inline-block; + min-width: 95%; + height: auto; + margin-bottom: 1px; + border-radius: 2px; + color: hsla(0,0%,100%,.8); + font-size: 13px; + line-height: 15px; + -moz-user-select:none; + cursor: default; + white-space: normal; +} + +html[dir='ltr'] .outlineItem > a { + padding: 2px 0 5px 10px; +} + +html[dir='rtl'] .outlineItem > a { + padding: 2px 10px 5px 0; +} + +.outlineItem > a:hover { + background-color: hsla(0,0%,100%,.02); + background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-clip: padding-box; + box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, + 0 0 1px hsla(0,0%,100%,.2) inset, + 0 0 1px hsla(0,0%,0%,.2); + color: hsla(0,0%,100%,.9); +} + +.outlineItem.selected { + background-color: hsla(0,0%,100%,.08); + background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-clip: padding-box; + box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, + 0 0 1px hsla(0,0%,100%,.1) inset, + 0 0 1px hsla(0,0%,0%,.2); + color: hsla(0,0%,100%,1); +} + +.noOutline, +.noResults { + font-size: 12px; + color: hsla(0,0%,100%,.8); + font-style: italic; +} + +#findScrollView { + position: absolute; + top: 10px; + bottom: 10px; + left: 10px; + width: 280px; +} + +#sidebarControls { + position:absolute; + width: 180px; + height: 32px; + left: 15px; + bottom: 35px; +} + +canvas { + margin: auto; + display: block; +} + +.page { + direction: ltr; + width: 816px; + height: 1056px; + margin: 10px auto; + position: relative; + overflow: visible; + -webkit-box-shadow: 0px 4px 10px #000; + -moz-box-shadow: 0px 4px 10px #000; + box-shadow: 0px 4px 10px #000; + background-color: white; +} + +.page > a { + display: block; + position: absolute; +} + +.page > a:hover { + opacity: 0.2; + background: #ff0; + -webkit-box-shadow: 0px 2px 10px #ff0; + -moz-box-shadow: 0px 2px 10px #ff0; + box-shadow: 0px 2px 10px #ff0; +} + +.loadingIcon { + position: absolute; + display: block; + left: 0; + top: 0; + right: 0; + bottom: 0; + background: url('images/loading-icon.gif') center no-repeat; +} + +#loadingBox { + position: absolute; + top: 50%; + margin-top: -25px; + left: 0; + right: 0; + text-align: center; + color: #ddd; + font-size: 14px; +} + +#loadingBar { + display: inline-block; + clear: both; + margin: 0px; + margin-top: 5px; + line-height: 0; + border-radius: 2px; + width: 200px; + height: 25px; + + background-color: hsla(0,0%,0%,.3); + background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + background-image: -webkit-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); + border: 1px solid #000; + box-shadow: 0 1px 1px hsla(0,0%,0%,.1) inset, + 0 0 1px hsla(0,0%,0%,.2) inset, + 0 0 1px 1px rgba(255, 255, 255, 0.1); +} + +#loadingBar .progress { + display: inline-block; + float: left; + + background: #666; + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2b2b2), color-stop(100%,#898989)); + background: -webkit-linear-gradient(top, #b2b2b2 0%,#898989 100%); + background: -moz-linear-gradient(top, #b2b2b2 0%,#898989 100%); + background: -ms-linear-gradient(top, #b2b2b2 0%,#898989 100%); + background: -o-linear-gradient(top, #b2b2b2 0%,#898989 100%); + background: linear-gradient(top, #b2b2b2 0%,#898989 100%); + + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; + + width: 0%; + height: 100%; +} + +#loadingBar .progress.full { + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; +} + +#loadingBar .progress.indeterminate { + width: 100%; + height: 25px; + background-image: -moz-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040); + background-image: -webkit-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040); + background-image: -ms-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040); + background-image: -o-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040); + background-size: 75px 25px; + -moz-animation: progressIndeterminate 1s linear infinite; + -webkit-animation: progressIndeterminate 1s linear infinite; +} + +@-moz-keyframes progressIndeterminate { + from { background-position: 0px 0px; } + to { background-position: 75px 0px; } +} + +@-webkit-keyframes progressIndeterminate { + from { background-position: 0px 0px; } + to { background-position: 75px 0px; } +} + +.textLayer { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + color: #000; + font-family: sans-serif; + overflow: hidden; +} + +.textLayer > div { + color: transparent; + position: absolute; + line-height:1.3; + white-space:pre; +} + +.textLayer .highlight { + margin: -1px; + padding: 1px; + + background-color: rgba(180, 0, 170, 0.2); + border-radius: 4px; +} + +.textLayer .highlight.begin { + border-radius: 4px 0px 0px 4px; +} + +.textLayer .highlight.end { + border-radius: 0px 4px 4px 0px; +} + +.textLayer .highlight.middle { + border-radius: 0px; +} + +.textLayer .highlight.selected { + background-color: rgba(0, 100, 0, 0.2); +} + +/* TODO: file FF bug to support ::-moz-selection:window-inactive + so we can override the opaque grey background when the window is inactive; + see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */ +::selection { background:rgba(0,0,255,0.3); } +::-moz-selection { background:rgba(0,0,255,0.3); } + +.annotText > div { + z-index: 200; + position: absolute; + padding: 0.6em; + max-width: 20em; + background-color: #FFFF99; + -webkit-box-shadow: 0px 2px 10px #333; + -moz-box-shadow: 0px 2px 10px #333; + box-shadow: 0px 2px 10px #333; + border-radius: 7px; + -moz-border-radius: 7px; +} + +.annotText > img { + position: absolute; + opacity: 0.6; +} + +.annotText > img:hover { + cursor: pointer; + opacity: 1; +} + +.annotText > div > h1 { + font-size: 1.2em; + border-bottom: 1px solid #000000; + margin: 0px; +} + +#errorWrapper { + background: none repeat scroll 0 0 #FF5555; + color: white; + left: 0; + position: absolute; + right: 0; + top: 32px; + z-index: 1000; + padding: 3px; + font-size: 0.8em; +} + +#errorMessageLeft { + float: left; +} + +#errorMessageRight { + float: right; +} + +#errorMoreInfo { + background-color: #FFFFFF; + color: black; + padding: 3px; + margin: 3px; + width: 98%; +} + +.clearBoth { + clear: both; +} + +.fileInput { + background: white; + color: black; + margin-top: 5px; +} + +#PDFBug { + background: none repeat scroll 0 0 white; + border: 1px solid #666666; + position: fixed; + top: 32px; + right: 0; + bottom: 0; + font-size: 10px; + padding: 0; + width: 300px; +} +#PDFBug .controls { + background:#EEEEEE; + border-bottom: 1px solid #666666; + padding: 3px; +} +#PDFBug .panels { + bottom: 0; + left: 0; + overflow: auto; + position: absolute; + right: 0; + top: 27px; +} +#PDFBug button.active { + font-weight: bold; +} +.debuggerShowText { + background: none repeat scroll 0 0 yellow; + color: blue; + opacity: 0.3; +} +.debuggerHideText:hover { + background: none repeat scroll 0 0 yellow; + opacity: 0.3; +} +#PDFBug .stats { + font-family: courier; + font-size: 10px; + white-space: pre; +} +#PDFBug .stats .title { + font-weight: bold; +} +#PDFBug table { + font-size: 10px; +} + +#viewer.textLayer-visible .textLayer > div, +#viewer.textLayer-hover .textLayer > div:hover { + background-color: white; + color: black; +} + +#viewer.textLayer-shadow .textLayer > div { + background-color: rgba(255,255,255, .6); + color: black; +} + +@page { + margin: 0; +} + +#printContainer { + display: none; +} + +@media print { + /* Rules for browsers that don't support mozPrintCallback. */ + #sidebarContainer, .toolbar, #loadingBox, #errorWrapper, .textLayer { + display: none; + } + + #mainContainer, #viewerContainer, .page, .page canvas { + position: static; + padding: 0; + margin: 0; + } + + .page { + float: left; + display: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + + .page[data-loaded] { + display: block; + } + + /* Rules for browsers that support mozPrintCallback */ + body[data-mozPrintCallback] #outerContainer { + display: none; + } + body[data-mozPrintCallback] #printContainer { + display: block; + } + #printContainer canvas { + position: relative; + top: 0; + left: 0; + } +} + +@media all and (max-width: 950px) { + html[dir='ltr'] #outerContainer.sidebarMoving .outerCenter, + html[dir='ltr'] #outerContainer.sidebarOpen .outerCenter { + float: left; + left: 180px; + } + html[dir='rtl'] #outerContainer.sidebarMoving .outerCenter, + html[dir='rtl'] #outerContainer.sidebarOpen .outerCenter { + float: right; + right: 180px; + } +} + +@media all and (max-width: 770px) { + #sidebarContainer { + top: 33px; + z-index: 100; + } + #sidebarContent { + top: 32px; + background-color: hsla(0,0%,0%,.7); + } + + html[dir='ltr'] #outerContainer.sidebarOpen > #mainContainer { + left: 0px; + } + html[dir='rtl'] #outerContainer.sidebarOpen > #mainContainer { + right: 0px; + } + + html[dir='ltr'] .outerCenter { + float: left; + left: 180px; + } + html[dir='rtl'] .outerCenter { + float: right; + right: 180px; + } +} + +@media all and (max-width: 600px) { + #toolbarViewerRight, #findbar, #viewFind { + display: none; + } +} + +@media all and (max-width: 500px) { + #scaleSelectContainer, #pageNumberLabel { + display: none; + } +} + diff --git a/common/static/js/pdfviewer.js b/common/static/js/pdfviewer.js new file mode 100644 index 0000000000..c25b1b2e4e --- /dev/null +++ b/common/static/js/pdfviewer.js @@ -0,0 +1,312 @@ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Modified (and JQuerified) from PDF-JS sample code (viewer.js) + */ +/* globals: PDFJS as defined in pdf.js. Also assumes that jquery is included. */ + +// +// Disable workers to avoid yet another cross-origin issue (workers need the URL of +// the script to be loaded, and currently do not allow cross-origin scripts) +// +PDFJS.disableWorker = true; + +(function($) { + $.fn.PDFViewer = function(options) { + var pdfViewer = this; + + var pdfDocument = null; + var url = options['url']; + var pageNum = 1; + if (options.pageNum) { + pageNum = options.pageNum; + } + + var viewerElement = document.getElementById('viewer'); + var ANNOT_MIN_SIZE = 10; + var DEFAULT_SCALE_DELTA = 1.1; + var UNKNOWN_SCALE = 0; + var MIN_SCALE = 0.25; + var MAX_SCALE = 4.0; + + var currentScale = UNKNOWN_SCALE; + var currentScaleValue = "0"; + var DEFAULT_SCALE_VALUE = "1"; + + // TESTING: + var destinations = null; + + var setupText = function setupText(textdiv, content, viewport) { + + function getPageNumberFromDest(dest) { + var destPage = 1; + if (dest instanceof Array) { + var destRef = dest[0]; + if (destRef instanceof Object) { + // we would need to look this up in the + // list of all pages that have been loaded, + // but we're trying to not have to load all the pages + // right now. + // destPage = this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R']; + } else { + destPage = (destRef + 1); + } + } + return destPage; + } + + function bindLink(link, dest) { + // get page number from dest: + destPage = getPageNumberFromDest(dest); + link.href = '#page=' + destPage; + link.onclick = function pageViewSetupLinksOnclick() { + if (dest && dest instanceof Array ) + renderPage(destPage); + return false; + }; + } + + function createElementWithStyle(tagName, item, rect) { + if (!rect) { + rect = viewport.convertToViewportRectangle(item.rect); + rect = PDFJS.Util.normalizeRect(rect); + } + var element = document.createElement(tagName); + element.style.left = Math.floor(rect[0]) + 'px'; + element.style.top = Math.floor(rect[1]) + 'px'; + element.style.width = Math.ceil(rect[2] - rect[0]) + 'px'; + element.style.height = Math.ceil(rect[3] - rect[1]) + 'px'; + // BW: my additions here, but should use css: + // TODO: move these to css + element.style.position = 'absolute'; + element.style.cursor = 'auto'; + + return element; + } + + function createTextAnnotation(item) { + var container = document.createElement('section'); + container.className = 'annotText'; + var rect = viewport.convertToViewportRectangle(item.rect); + rect = PDFJS.Util.normalizeRect(rect); + // sanity check because of OOo-generated PDFs + if ((rect[3] - rect[1]) < ANNOT_MIN_SIZE) { + rect[3] = rect[1] + ANNOT_MIN_SIZE; + } + if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) { + rect[2] = rect[0] + (rect[3] - rect[1]); + // make it square + } + var image = createElementWithStyle('img', item, rect); + var iconName = item.name; + } + + + content.getAnnotations().then(function(items) { + for (var i = 0; i < items.length; i++) { + var item = items[i]; + switch (item.type) { + case 'Link': + var link = createElementWithStyle('a', item); + link.href = item.url || ''; + if (!item.url) + bindLink(link, ('dest' in item) ? item.dest : null); + textdiv.appendChild(link); + break; + case 'Text': + var textAnnotation = createTextAnnotation(item); + if (textAnnotation) + textdiv.appendChild(textAnnotation); + break; + } + } + }); + } + + // + // Get page info from document, resize canvas accordingly, and render page + // + renderPage = function(num) { + // don't try to render a page that cannot be rendered + if (num < 1 || num > pdfDocument.numPages) { + return; + } + + // Update logging: + log_event("book", { "type" : "gotopage", "old" : pageNum, "new" : num }); + + parentElement = viewerElement; + while (parentElement.hasChildNodes()) + parentElement.removeChild(parentElement.lastChild); + + // Using promise to fetch the page + pdfDocument.getPage(num).then(function(page) { + var viewport = page.getViewport(currentScale); + + var pageDisplayWidth = viewport.width; + var pageDisplayHeight = viewport.height; + + var pageDivHolder = document.createElement('div'); + pageDivHolder.className = 'pdfpage'; + pageDivHolder.style.width = pageDisplayWidth + 'px'; + pageDivHolder.style.height = pageDisplayHeight + 'px'; + parentElement.appendChild(pageDivHolder); + + // Prepare canvas using PDF page dimensions + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + canvas.width = pageDisplayWidth; + canvas.height = pageDisplayHeight; + pageDivHolder.appendChild(canvas); + + // Render PDF page into canvas context + var renderContext = { + canvasContext : context, + viewport : viewport + }; + page.render(renderContext); + + // Prepare and populate text elements layer + setupText(pageDivHolder, page, viewport); + + }); + pageNum = num; + + // Update page counters + document.getElementById('numPages').textContent = 'of ' + pdfDocument.numPages; + $("#pageNumber").max = pdfDocument.numPages; + $("#pageNumber").val(pageNum); + } + + // Go to previous page + prevPage = function prev_page() { + if (pageNum <= 1) + return; + renderPage(pageNum - 1); + log_event("book", { "type" : "prevpage", "new" : pageNum }); + } + + // Go to next page + nextPage = function next_page() { + if (pageNum >= pdfDocument.numPages) + return; + renderPage(pageNum + 1); + log_event("book", { "type" : "nextpage", "new" : pageNum }); + } + + selectScaleOption = function(value) { + var options = $('#scaleSelect options'); + var predefinedValueFound = false; + for (var i = 0; i < options.length; i++) { + var option = options[i]; + if (option.value != value) { + option.selected = false; + continue; + } + option.selected = true; + predefinedValueFound = true; + } + return predefinedValueFound; + } + + setScale = function pdfViewSetScale(val, resetAutoSettings, noScroll) { + if (val == currentScale) + return; + currentScale = val; + var customScaleOption = $('#customScaleOption')[0]; + customScaleOption.selected = false + var predefinedValueFound = selectScaleOption('' + currentScale); + if (!predefinedValueFound) { + customScaleOption.textContent = Math.round(currentScale * 10000) / 100 + '%'; + customScaleOption.selected = true; + } + $('#zoom_in').disabled = (currentScale === MAX_SCALE); + $('#zoom_out').disabled = (currentScale === MIN_SCALE); + + // Just call renderPage once the scale + // has been changed. If we were saving information about + // the rendering of other pages, we would need + // to reset those as well. + renderPage(pageNum); + }; + + parseScale = function pdfViewParseScale(value, resetAutoSettings, noScroll) { + // we shouldn't be choosing the 'custom' value -- it's only for display. + // Check, just in case. + if ('custom' == value) + return; + + var scale = parseFloat(value); + if (scale) { + currentScaleValue = value; + setScale(scale, true, noScroll); + return; + } + }; + + zoomIn = function pdfViewZoomIn() { + var newScale = (currentScale * DEFAULT_SCALE_DELTA).toFixed(2); + newScale = Math.min(MAX_SCALE, newScale); + parseScale(newScale, true); + }; + + zoomOut = function pdfViewZoomOut() { + var newScale = (currentScale / DEFAULT_SCALE_DELTA).toFixed(2); + newScale = Math.max(MIN_SCALE, newScale); + parseScale(newScale, true); + }; + + // + // Asynchronously download PDF as an ArrayBuffer + // + PDFJS.getDocument(url).then( + function getDocument(_pdfDocument) { + pdfDocument = _pdfDocument; + // display the current page with a default scale value: + parseScale(DEFAULT_SCALE_VALUE); + }, + function getDocumentError(message, exception) { + // placeholder: don't expect errors :) + }, + function getDocumentProgress(progressData) { + // placeholder: not yet ready to display loading progress + }); + + $("#previous").click(function(event) { + prevPage(); + }); + + $("#next").click(function(event) { + nextPage(); + }); + + $('#zoom_in').click(function(event) { + zoomIn(); + }); + $('#zoom_out').click(function(event) { + zoomOut(); + }); + + $('#scaleSelect').change(function(event) { + parseScale(this.value); + }); + + $('#pageNumber').change(function(event) { + var newPageVal = parseInt(this.value); + if (newPageVal) { + renderPage(newPageVal); + } + }); + } +})(jQuery); diff --git a/common/static/js/vendor/pdfjs/compatibility.js b/common/static/js/vendor/pdfjs/compatibility.js new file mode 100644 index 0000000000..04e3a14f77 --- /dev/null +++ b/common/static/js/vendor/pdfjs/compatibility.js @@ -0,0 +1,430 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* globals VBArray */ + +'use strict'; + +// Checking if the typed arrays are supported +(function checkTypedArrayCompatibility() { + if (typeof Uint8Array !== 'undefined') { + // some mobile versions do not support subarray (e.g. safari 5 / iOS) + if (typeof Uint8Array.prototype.subarray === 'undefined') { + Uint8Array.prototype.subarray = function subarray(start, end) { + return new Uint8Array(this.slice(start, end)); + }; + Float32Array.prototype.subarray = function subarray(start, end) { + return new Float32Array(this.slice(start, end)); + }; + } + + // some mobile version might not support Float64Array + if (typeof Float64Array === 'undefined') + window.Float64Array = Float32Array; + + return; + } + + function subarray(start, end) { + return new TypedArray(this.slice(start, end)); + } + + function setArrayOffset(array, offset) { + if (arguments.length < 2) + offset = 0; + for (var i = 0, n = array.length; i < n; ++i, ++offset) + this[offset] = array[i] & 0xFF; + } + + function TypedArray(arg1) { + var result; + if (typeof arg1 === 'number') { + result = []; + for (var i = 0; i < arg1; ++i) + result[i] = 0; + } else + result = arg1.slice(0); + + result.subarray = subarray; + result.buffer = result; + result.byteLength = result.length; + result.set = setArrayOffset; + + if (typeof arg1 === 'object' && arg1.buffer) + result.buffer = arg1.buffer; + + return result; + } + + window.Uint8Array = TypedArray; + + // we don't need support for set, byteLength for 32-bit array + // so we can use the TypedArray as well + window.Uint32Array = TypedArray; + window.Int32Array = TypedArray; + window.Uint16Array = TypedArray; + window.Float32Array = TypedArray; + window.Float64Array = TypedArray; +})(); + +// Object.create() ? +(function checkObjectCreateCompatibility() { + if (typeof Object.create !== 'undefined') + return; + + Object.create = function objectCreate(proto) { + function Constructor() {} + Constructor.prototype = proto; + return new Constructor(); + }; +})(); + +// Object.defineProperty() ? +(function checkObjectDefinePropertyCompatibility() { + if (typeof Object.defineProperty !== 'undefined') { + var definePropertyPossible = true; + try { + // some browsers (e.g. safari) cannot use defineProperty() on DOM objects + // and thus the native version is not sufficient + Object.defineProperty(new Image(), 'id', { value: 'test' }); + // ... another test for android gb browser for non-DOM objects + var Test = function Test() {}; + Test.prototype = { get id() { } }; + Object.defineProperty(new Test(), 'id', + { value: '', configurable: true, enumerable: true, writable: false }); + } catch (e) { + definePropertyPossible = false; + } + if (definePropertyPossible) return; + } + + Object.defineProperty = function objectDefineProperty(obj, name, def) { + delete obj[name]; + if ('get' in def) + obj.__defineGetter__(name, def['get']); + if ('set' in def) + obj.__defineSetter__(name, def['set']); + if ('value' in def) { + obj.__defineSetter__(name, function objectDefinePropertySetter(value) { + this.__defineGetter__(name, function objectDefinePropertyGetter() { + return value; + }); + return value; + }); + obj[name] = def.value; + } + }; +})(); + +// Object.keys() ? +(function checkObjectKeysCompatibility() { + if (typeof Object.keys !== 'undefined') + return; + + Object.keys = function objectKeys(obj) { + var result = []; + for (var i in obj) { + if (obj.hasOwnProperty(i)) + result.push(i); + } + return result; + }; +})(); + +// No readAsArrayBuffer ? +(function checkFileReaderReadAsArrayBuffer() { + if (typeof FileReader === 'undefined') + return; // FileReader is not implemented + var frPrototype = FileReader.prototype; + // Older versions of Firefox might not have readAsArrayBuffer + if ('readAsArrayBuffer' in frPrototype) + return; // readAsArrayBuffer is implemented + Object.defineProperty(frPrototype, 'readAsArrayBuffer', { + value: function fileReaderReadAsArrayBuffer(blob) { + var fileReader = new FileReader(); + var originalReader = this; + fileReader.onload = function fileReaderOnload(evt) { + var data = evt.target.result; + var buffer = new ArrayBuffer(data.length); + var uint8Array = new Uint8Array(buffer); + + for (var i = 0, ii = data.length; i < ii; i++) + uint8Array[i] = data.charCodeAt(i); + + Object.defineProperty(originalReader, 'result', { + value: buffer, + enumerable: true, + writable: false, + configurable: true + }); + + var event = document.createEvent('HTMLEvents'); + event.initEvent('load', false, false); + originalReader.dispatchEvent(event); + }; + fileReader.readAsBinaryString(blob); + } + }); +})(); + +// No XMLHttpRequest.response ? +(function checkXMLHttpRequestResponseCompatibility() { + var xhrPrototype = XMLHttpRequest.prototype; + if (!('overrideMimeType' in xhrPrototype)) { + // IE10 might have response, but not overrideMimeType + Object.defineProperty(xhrPrototype, 'overrideMimeType', { + value: function xmlHttpRequestOverrideMimeType(mimeType) {} + }); + } + if ('response' in xhrPrototype || + 'mozResponseArrayBuffer' in xhrPrototype || + 'mozResponse' in xhrPrototype || + 'responseArrayBuffer' in xhrPrototype) + return; + // IE9 ? + if (typeof VBArray !== 'undefined') { + Object.defineProperty(xhrPrototype, 'response', { + get: function xmlHttpRequestResponseGet() { + return new Uint8Array(new VBArray(this.responseBody).toArray()); + } + }); + return; + } + + // other browsers + function responseTypeSetter() { + // will be only called to set "arraybuffer" + this.overrideMimeType('text/plain; charset=x-user-defined'); + } + if (typeof xhrPrototype.overrideMimeType === 'function') { + Object.defineProperty(xhrPrototype, 'responseType', + { set: responseTypeSetter }); + } + function responseGetter() { + var text = this.responseText; + var i, n = text.length; + var result = new Uint8Array(n); + for (i = 0; i < n; ++i) + result[i] = text.charCodeAt(i) & 0xFF; + return result; + } + Object.defineProperty(xhrPrototype, 'response', { get: responseGetter }); +})(); + +// window.btoa (base64 encode function) ? +(function checkWindowBtoaCompatibility() { + if ('btoa' in window) + return; + + var digits = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + + window.btoa = function windowBtoa(chars) { + var buffer = ''; + var i, n; + for (i = 0, n = chars.length; i < n; i += 3) { + var b1 = chars.charCodeAt(i) & 0xFF; + var b2 = chars.charCodeAt(i + 1) & 0xFF; + var b3 = chars.charCodeAt(i + 2) & 0xFF; + var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4); + var d3 = i + 1 < n ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64; + var d4 = i + 2 < n ? (b3 & 0x3F) : 64; + buffer += (digits.charAt(d1) + digits.charAt(d2) + + digits.charAt(d3) + digits.charAt(d4)); + } + return buffer; + }; +})(); + +// Function.prototype.bind ? +(function checkFunctionPrototypeBindCompatibility() { + if (typeof Function.prototype.bind !== 'undefined') + return; + + Function.prototype.bind = function functionPrototypeBind(obj) { + var fn = this, headArgs = Array.prototype.slice.call(arguments, 1); + var bound = function functionPrototypeBindBound() { + var args = Array.prototype.concat.apply(headArgs, arguments); + return fn.apply(obj, args); + }; + return bound; + }; +})(); + +// IE9/10 text/html data URI +(function checkDataURICompatibility() { + if (!('documentMode' in document) || + document.documentMode !== 9 && document.documentMode !== 10) + return; + // overriding the src property + var originalSrcDescriptor = Object.getOwnPropertyDescriptor( + HTMLIFrameElement.prototype, 'src'); + Object.defineProperty(HTMLIFrameElement.prototype, 'src', { + get: function htmlIFrameElementPrototypeSrcGet() { return this.$src; }, + set: function htmlIFrameElementPrototypeSrcSet(src) { + this.$src = src; + if (src.substr(0, 14) != 'data:text/html') { + originalSrcDescriptor.set.call(this, src); + return; + } + // for text/html, using blank document and then + // document's open, write, and close operations + originalSrcDescriptor.set.call(this, 'about:blank'); + setTimeout((function htmlIFrameElementPrototypeSrcOpenWriteClose() { + var doc = this.contentDocument; + doc.open('text/html'); + doc.write(src.substr(src.indexOf(',') + 1)); + doc.close(); + }).bind(this), 0); + }, + enumerable: true + }); +})(); + +// HTMLElement dataset property +(function checkDatasetProperty() { + var div = document.createElement('div'); + if ('dataset' in div) + return; // dataset property exists + + Object.defineProperty(HTMLElement.prototype, 'dataset', { + get: function() { + if (this._dataset) + return this._dataset; + + var dataset = {}; + for (var j = 0, jj = this.attributes.length; j < jj; j++) { + var attribute = this.attributes[j]; + if (attribute.name.substring(0, 5) != 'data-') + continue; + var key = attribute.name.substring(5).replace(/\-([a-z])/g, + function(all, ch) { return ch.toUpperCase(); }); + dataset[key] = attribute.value; + } + + Object.defineProperty(this, '_dataset', { + value: dataset, + writable: false, + enumerable: false + }); + return dataset; + }, + enumerable: true + }); +})(); + +// HTMLElement classList property +(function checkClassListProperty() { + var div = document.createElement('div'); + if ('classList' in div) + return; // classList property exists + + function changeList(element, itemName, add, remove) { + var s = element.className || ''; + var list = s.split(/\s+/g); + if (list[0] === '') list.shift(); + var index = list.indexOf(itemName); + if (index < 0 && add) + list.push(itemName); + if (index >= 0 && remove) + list.splice(index, 1); + element.className = list.join(' '); + } + + var classListPrototype = { + add: function(name) { + changeList(this.element, name, true, false); + }, + remove: function(name) { + changeList(this.element, name, false, true); + }, + toggle: function(name) { + changeList(this.element, name, true, true); + } + }; + + Object.defineProperty(HTMLElement.prototype, 'classList', { + get: function() { + if (this._classList) + return this._classList; + + var classList = Object.create(classListPrototype, { + element: { + value: this, + writable: false, + enumerable: true + } + }); + Object.defineProperty(this, '_classList', { + value: classList, + writable: false, + enumerable: false + }); + return classList; + }, + enumerable: true + }); +})(); + +// Check console compatability +(function checkConsoleCompatibility() { + if (!('console' in window)) { + window.console = { + log: function() {}, + error: function() {} + }; + } else if (!('bind' in console.log)) { + // native functions in IE9 might not have bind + console.log = (function(fn) { + return function(msg) { return fn(msg); }; + })(console.log); + console.error = (function(fn) { + return function(msg) { return fn(msg); }; + })(console.error); + } +})(); + +// Check onclick compatibility in Opera +(function checkOnClickCompatibility() { + // workaround for reported Opera bug DSK-354448: + // onclick fires on disabled buttons with opaque content + function ignoreIfTargetDisabled(event) { + if (isDisabled(event.target)) { + event.stopPropagation(); + } + } + function isDisabled(node) { + return node.disabled || (node.parentNode && isDisabled(node.parentNode)); + } + if (navigator.userAgent.indexOf('Opera') != -1) { + // use browser detection since we cannot feature-check this bug + document.addEventListener('click', ignoreIfTargetDisabled, true); + } +})(); + +// Checks if navigator.language is supported +(function checkNavigatorLanguage() { + if ('language' in navigator) + return; + Object.defineProperty(navigator, 'language', { + get: function navigatorLanguage() { + var language = navigator.userLanguage || 'en-US'; + return language.substring(0, 2).toLowerCase() + + language.substring(2).toUpperCase(); + }, + enumerable: true + }); +})(); diff --git a/common/static/js/vendor/pdfjs/debugger.js b/common/static/js/vendor/pdfjs/debugger.js new file mode 100644 index 0000000000..c14ad3fdad --- /dev/null +++ b/common/static/js/vendor/pdfjs/debugger.js @@ -0,0 +1,491 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* globals PDFJS */ + +'use strict'; + +var FontInspector = (function FontInspectorClosure() { + var fonts; + var panelWidth = 300; + var active = false; + var fontAttribute = 'data-font-name'; + function removeSelection() { + var divs = document.querySelectorAll('div[' + fontAttribute + ']'); + for (var i = 0, ii = divs.length; i < ii; ++i) { + var div = divs[i]; + div.className = ''; + } + } + function resetSelection() { + var divs = document.querySelectorAll('div[' + fontAttribute + ']'); + for (var i = 0, ii = divs.length; i < ii; ++i) { + var div = divs[i]; + div.className = 'debuggerHideText'; + } + } + function selectFont(fontName, show) { + var divs = document.querySelectorAll('div[' + fontAttribute + '=' + + fontName + ']'); + for (var i = 0, ii = divs.length; i < ii; ++i) { + var div = divs[i]; + div.className = show ? 'debuggerShowText' : 'debuggerHideText'; + } + } + function textLayerClick(e) { + if (!e.target.dataset.fontName || e.target.tagName != 'DIV') + return; + var fontName = e.target.dataset.fontName; + var selects = document.getElementsByTagName('input'); + for (var i = 0; i < selects.length; ++i) { + var select = selects[i]; + if (select.dataset.fontName != fontName) continue; + select.checked = !select.checked; + selectFont(fontName, select.checked); + select.scrollIntoView(); + } + } + return { + // Properties/functions needed by PDFBug. + id: 'FontInspector', + name: 'Font Inspector', + panel: null, + manager: null, + init: function init() { + var panel = this.panel; + panel.setAttribute('style', 'padding: 5px;'); + var tmp = document.createElement('button'); + tmp.addEventListener('click', resetSelection); + tmp.textContent = 'Refresh'; + panel.appendChild(tmp); + + fonts = document.createElement('div'); + panel.appendChild(fonts); + }, + enabled: false, + get active() { + return active; + }, + set active(value) { + active = value; + if (active) { + document.body.addEventListener('click', textLayerClick, true); + resetSelection(); + } else { + document.body.removeEventListener('click', textLayerClick, true); + removeSelection(); + } + }, + // FontInspector specific functions. + fontAdded: function fontAdded(fontObj, url) { + function properties(obj, list) { + var moreInfo = document.createElement('table'); + for (var i = 0; i < list.length; i++) { + var tr = document.createElement('tr'); + var td1 = document.createElement('td'); + td1.textContent = list[i]; + tr.appendChild(td1); + var td2 = document.createElement('td'); + td2.textContent = obj[list[i]].toString(); + tr.appendChild(td2); + moreInfo.appendChild(tr); + } + return moreInfo; + } + var moreInfo = properties(fontObj, ['name', 'type']); + var m = /url\(['"]?([^\)"']+)/.exec(url); + var fontName = fontObj.loadedName; + var font = document.createElement('div'); + var name = document.createElement('span'); + name.textContent = fontName; + var download = document.createElement('a'); + download.href = m[1]; + download.textContent = 'Download'; + var logIt = document.createElement('a'); + logIt.href = ''; + logIt.textContent = 'Log'; + logIt.addEventListener('click', function(event) { + event.preventDefault(); + console.log(fontObj); + }); + var select = document.createElement('input'); + select.setAttribute('type', 'checkbox'); + select.dataset.fontName = fontName; + select.addEventListener('click', (function(select, fontName) { + return (function() { + selectFont(fontName, select.checked); + }); + })(select, fontName)); + font.appendChild(select); + font.appendChild(name); + font.appendChild(document.createTextNode(' ')); + font.appendChild(download); + font.appendChild(document.createTextNode(' ')); + font.appendChild(logIt); + font.appendChild(moreInfo); + fonts.appendChild(font); + // Somewhat of a hack, should probably add a hook for when the text layer + // is done rendering. + setTimeout(function() { + if (this.active) + resetSelection(); + }.bind(this), 2000); + } + }; +})(); + +// Manages all the page steppers. +var StepperManager = (function StepperManagerClosure() { + var steppers = []; + var stepperDiv = null; + var stepperControls = null; + var stepperChooser = null; + var breakPoints = {}; + return { + // Properties/functions needed by PDFBug. + id: 'Stepper', + name: 'Stepper', + panel: null, + manager: null, + init: function init() { + var self = this; + this.panel.setAttribute('style', 'padding: 5px;'); + stepperControls = document.createElement('div'); + stepperChooser = document.createElement('select'); + stepperChooser.addEventListener('change', function(event) { + self.selectStepper(this.value); + }); + stepperControls.appendChild(stepperChooser); + stepperDiv = document.createElement('div'); + this.panel.appendChild(stepperControls); + this.panel.appendChild(stepperDiv); + if (sessionStorage.getItem('pdfjsBreakPoints')) + breakPoints = JSON.parse(sessionStorage.getItem('pdfjsBreakPoints')); + }, + enabled: false, + active: false, + // Stepper specific functions. + create: function create(pageIndex) { + var debug = document.createElement('div'); + debug.id = 'stepper' + pageIndex; + debug.setAttribute('hidden', true); + debug.className = 'stepper'; + stepperDiv.appendChild(debug); + var b = document.createElement('option'); + b.textContent = 'Page ' + (pageIndex + 1); + b.value = pageIndex; + stepperChooser.appendChild(b); + var initBreakPoints = breakPoints[pageIndex] || []; + var stepper = new Stepper(debug, pageIndex, initBreakPoints); + steppers.push(stepper); + if (steppers.length === 1) + this.selectStepper(pageIndex, false); + return stepper; + }, + selectStepper: function selectStepper(pageIndex, selectPanel) { + if (selectPanel) + this.manager.selectPanel(1); + for (var i = 0; i < steppers.length; ++i) { + var stepper = steppers[i]; + if (stepper.pageIndex == pageIndex) + stepper.panel.removeAttribute('hidden'); + else + stepper.panel.setAttribute('hidden', true); + } + var options = stepperChooser.options; + for (var i = 0; i < options.length; ++i) { + var option = options[i]; + option.selected = option.value == pageIndex; + } + }, + saveBreakPoints: function saveBreakPoints(pageIndex, bps) { + breakPoints[pageIndex] = bps; + sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints)); + } + }; +})(); + +// The stepper for each page's IRQueue. +var Stepper = (function StepperClosure() { + function Stepper(panel, pageIndex, initialBreakPoints) { + this.panel = panel; + this.len = 0; + this.breakPoint = 0; + this.nextBreakPoint = null; + this.pageIndex = pageIndex; + this.breakPoints = initialBreakPoints; + this.currentIdx = -1; + } + Stepper.prototype = { + init: function init(IRQueue) { + // Shorter way to create element and optionally set textContent. + function c(tag, textContent) { + var d = document.createElement(tag); + if (textContent) + d.textContent = textContent; + return d; + } + var panel = this.panel; + this.len = IRQueue.fnArray.length; + var content = c('div', 'c=continue, s=step'); + var table = c('table'); + content.appendChild(table); + table.cellSpacing = 0; + var headerRow = c('tr'); + table.appendChild(headerRow); + headerRow.appendChild(c('th', 'Break')); + headerRow.appendChild(c('th', 'Idx')); + headerRow.appendChild(c('th', 'fn')); + headerRow.appendChild(c('th', 'args')); + + var self = this; + for (var i = 0; i < IRQueue.fnArray.length; i++) { + var line = c('tr'); + line.className = 'line'; + line.dataset.idx = i; + table.appendChild(line); + var checked = this.breakPoints.indexOf(i) != -1; + var args = IRQueue.argsArray[i] ? IRQueue.argsArray[i] : []; + + var breakCell = c('td'); + var cbox = c('input'); + cbox.type = 'checkbox'; + cbox.className = 'points'; + cbox.checked = checked; + cbox.onclick = (function(x) { + return function() { + if (this.checked) + self.breakPoints.push(x); + else + self.breakPoints.splice(self.breakPoints.indexOf(x), 1); + StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints); + }; + })(i); + + breakCell.appendChild(cbox); + line.appendChild(breakCell); + line.appendChild(c('td', i.toString())); + line.appendChild(c('td', IRQueue.fnArray[i])); + line.appendChild(c('td', args.join(', '))); + } + panel.appendChild(content); + var self = this; + }, + getNextBreakPoint: function getNextBreakPoint() { + this.breakPoints.sort(function(a, b) { return a - b; }); + for (var i = 0; i < this.breakPoints.length; i++) { + if (this.breakPoints[i] > this.currentIdx) + return this.breakPoints[i]; + } + return null; + }, + breakIt: function breakIt(idx, callback) { + StepperManager.selectStepper(this.pageIndex, true); + var self = this; + var dom = document; + self.currentIdx = idx; + var listener = function(e) { + switch (e.keyCode) { + case 83: // step + dom.removeEventListener('keydown', listener, false); + self.nextBreakPoint = self.currentIdx + 1; + self.goTo(-1); + callback(); + break; + case 67: // continue + dom.removeEventListener('keydown', listener, false); + var breakPoint = self.getNextBreakPoint(); + self.nextBreakPoint = breakPoint; + self.goTo(-1); + callback(); + break; + } + }; + dom.addEventListener('keydown', listener, false); + self.goTo(idx); + }, + goTo: function goTo(idx) { + var allRows = this.panel.getElementsByClassName('line'); + for (var x = 0, xx = allRows.length; x < xx; ++x) { + var row = allRows[x]; + if (row.dataset.idx == idx) { + row.style.backgroundColor = 'rgb(251,250,207)'; + row.scrollIntoView(); + } else { + row.style.backgroundColor = null; + } + } + } + }; + return Stepper; +})(); + +var Stats = (function Stats() { + var stats = []; + function clear(node) { + while (node.hasChildNodes()) + node.removeChild(node.lastChild); + } + function getStatIndex(pageNumber) { + for (var i = 0, ii = stats.length; i < ii; ++i) + if (stats[i].pageNumber === pageNumber) + return i; + return false; + } + return { + // Properties/functions needed by PDFBug. + id: 'Stats', + name: 'Stats', + panel: null, + manager: null, + init: function init() { + this.panel.setAttribute('style', 'padding: 5px;'); + PDFJS.enableStats = true; + }, + enabled: false, + active: false, + // Stats specific functions. + add: function(pageNumber, stat) { + if (!stat) + return; + var statsIndex = getStatIndex(pageNumber); + if (statsIndex !== false) { + var b = stats[statsIndex]; + this.panel.removeChild(b.div); + stats.splice(statsIndex, 1); + } + var wrapper = document.createElement('div'); + wrapper.className = 'stats'; + var title = document.createElement('div'); + title.className = 'title'; + title.textContent = 'Page: ' + pageNumber; + var statsDiv = document.createElement('div'); + statsDiv.textContent = stat.toString(); + wrapper.appendChild(title); + wrapper.appendChild(statsDiv); + stats.push({ pageNumber: pageNumber, div: wrapper }); + stats.sort(function(a, b) { return a.pageNumber - b.pageNumber; }); + clear(this.panel); + for (var i = 0, ii = stats.length; i < ii; ++i) + this.panel.appendChild(stats[i].div); + } + }; +})(); + +// Manages all the debugging tools. +var PDFBug = (function PDFBugClosure() { + var panelWidth = 300; + var buttons = []; + var activePanel = null; + + return { + tools: [ + FontInspector, + StepperManager, + Stats + ], + enable: function(ids) { + var all = false, tools = this.tools; + if (ids.length === 1 && ids[0] === 'all') + all = true; + for (var i = 0; i < tools.length; ++i) { + var tool = tools[i]; + if (all || ids.indexOf(tool.id) !== -1) + tool.enabled = true; + } + if (!all) { + // Sort the tools by the order they are enabled. + tools.sort(function(a, b) { + var indexA = ids.indexOf(a.id); + indexA = indexA < 0 ? tools.length : indexA; + var indexB = ids.indexOf(b.id); + indexB = indexB < 0 ? tools.length : indexB; + return indexA - indexB; + }); + } + }, + init: function init() { + /* + * Basic Layout: + * PDFBug + * Controls + * Panels + * Panel + * Panel + * ... + */ + var ui = document.createElement('div'); + ui.id = 'PDFBug'; + + var controls = document.createElement('div'); + controls.setAttribute('class', 'controls'); + ui.appendChild(controls); + + var panels = document.createElement('div'); + panels.setAttribute('class', 'panels'); + ui.appendChild(panels); + + var container = document.getElementById('viewerContainer'); + container.appendChild(ui); + container.style.right = panelWidth + 'px'; + + // Initialize all the debugging tools. + var tools = this.tools; + var self = this; + for (var i = 0; i < tools.length; ++i) { + var tool = tools[i]; + var panel = document.createElement('div'); + var panelButton = document.createElement('button'); + panelButton.textContent = tool.name; + panelButton.addEventListener('click', (function(selected) { + return function(event) { + event.preventDefault(); + self.selectPanel(selected); + }; + })(i)); + controls.appendChild(panelButton); + panels.appendChild(panel); + tool.panel = panel; + tool.manager = this; + if (tool.enabled) + tool.init(); + else + panel.textContent = tool.name + ' is disabled. To enable add ' + + ' "' + tool.id + '" to the pdfBug parameter ' + + 'and refresh (seperate multiple by commas).'; + buttons.push(panelButton); + } + this.selectPanel(0); + }, + selectPanel: function selectPanel(index) { + if (index === activePanel) + return; + activePanel = index; + var tools = this.tools; + for (var j = 0; j < tools.length; ++j) { + if (j == index) { + buttons[j].setAttribute('class', 'active'); + tools[j].active = true; + tools[j].panel.removeAttribute('hidden'); + } else { + buttons[j].setAttribute('class', ''); + tools[j].active = false; + tools[j].panel.setAttribute('hidden', 'true'); + } + } + } + }; +})(); diff --git a/common/static/js/vendor/pdfjs/l10n.js b/common/static/js/vendor/pdfjs/l10n.js new file mode 100644 index 0000000000..5435631651 --- /dev/null +++ b/common/static/js/vendor/pdfjs/l10n.js @@ -0,0 +1,922 @@ +/** Copyright (c) 2011-2012 Fabien Cazenave, Mozilla. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +/* + Additional modifications for PDF.js project: + - Disables language initialization on page loading; + - Adds fallback argument to the getL10nData; + - Removes consoleLog and simplifies consoleWarn; + - Removes window._ assignment. +*/ +/*jshint browser: true, devel: true, es5: true, globalstrict: true */ +'use strict'; + +document.webL10n = (function(window, document, undefined) { + var gL10nData = {}; + var gTextData = ''; + var gTextProp = 'textContent'; + var gLanguage = ''; + var gMacros = {}; + var gReadyState = 'loading'; + + // read-only setting -- we recommend to load l10n resources synchronously + var gAsyncResourceLoading = true; + + // debug helpers + function consoleWarn(message) { + console.log('[l10n] ' + message); + }; + + /** + * DOM helpers for the so-called "HTML API". + * + * These functions are written for modern browsers. For old versions of IE, + * they're overridden in the 'startup' section at the end of this file. + */ + + function getL10nResourceLinks() { + return document.querySelectorAll('link[type="application/l10n"]'); + } + + function getTranslatableChildren(element) { + return element ? element.querySelectorAll('*[data-l10n-id]') : []; + } + + function getL10nAttributes(element) { + if (!element) + return {}; + + var l10nId = element.getAttribute('data-l10n-id'); + var l10nArgs = element.getAttribute('data-l10n-args'); + var args = {}; + if (l10nArgs) { + try { + args = JSON.parse(l10nArgs); + } catch (e) { + consoleWarn('could not parse arguments for #' + l10nId); + } + } + return { id: l10nId, args: args }; + } + + function fireL10nReadyEvent(lang) { + var evtObject = document.createEvent('Event'); + evtObject.initEvent('localized', false, false); + evtObject.language = lang; + window.dispatchEvent(evtObject); + } + + + /** + * l10n resource parser: + * - reads (async XHR) the l10n resource matching `lang'; + * - imports linked resources (synchronously) when specified; + * - parses the text data (fills `gL10nData' and `gTextData'); + * - triggers success/failure callbacks when done. + * + * @param {string} href + * URL of the l10n resource to parse. + * + * @param {string} lang + * locale (language) to parse. + * + * @param {Function} successCallback + * triggered when the l10n resource has been successully parsed. + * + * @param {Function} failureCallback + * triggered when the an error has occured. + * + * @return {void} + * uses the following global variables: gL10nData, gTextData, gTextProp. + */ + + function parseResource(href, lang, successCallback, failureCallback) { + var baseURL = href.replace(/\/[^\/]*$/, '/'); + + // handle escaped characters (backslashes) in a string + function evalString(text) { + if (text.lastIndexOf('\\') < 0) + return text; + return text.replace(/\\\\/g, '\\') + .replace(/\\n/g, '\n') + .replace(/\\r/g, '\r') + .replace(/\\t/g, '\t') + .replace(/\\b/g, '\b') + .replace(/\\f/g, '\f') + .replace(/\\{/g, '{') + .replace(/\\}/g, '}') + .replace(/\\"/g, '"') + .replace(/\\'/g, "'"); + } + + // parse *.properties text data into an l10n dictionary + function parseProperties(text) { + var dictionary = []; + + // token expressions + var reBlank = /^\s*|\s*$/; + var reComment = /^\s*#|^\s*$/; + var reSection = /^\s*\[(.*)\]\s*$/; + var reImport = /^\s*@import\s+url\((.*)\)\s*$/i; + var reSplit = /^([^=\s]*)\s*=\s*(.+)$/; // TODO: escape EOLs with '\' + + // parse the *.properties file into an associative array + function parseRawLines(rawText, extendedSyntax) { + var entries = rawText.replace(reBlank, '').split(/[\r\n]+/); + var currentLang = '*'; + var genericLang = lang.replace(/-[a-z]+$/i, ''); + var skipLang = false; + var match = ''; + + for (var i = 0; i < entries.length; i++) { + var line = entries[i]; + + // comment or blank line? + if (reComment.test(line)) + continue; + + // the extended syntax supports [lang] sections and @import rules + if (extendedSyntax) { + if (reSection.test(line)) { // section start? + match = reSection.exec(line); + currentLang = match[1]; + skipLang = (currentLang !== '*') && + (currentLang !== lang) && (currentLang !== genericLang); + continue; + } else if (skipLang) { + continue; + } + if (reImport.test(line)) { // @import rule? + match = reImport.exec(line); + loadImport(baseURL + match[1]); // load the resource synchronously + } + } + + // key-value pair + var tmp = line.match(reSplit); + if (tmp && tmp.length == 3) + dictionary[tmp[1]] = evalString(tmp[2]); + } + } + + // import another *.properties file + function loadImport(url) { + loadResource(url, function(content) { + parseRawLines(content, false); // don't allow recursive imports + }, false, false); // load synchronously + } + + // fill the dictionary + parseRawLines(text, true); + return dictionary; + } + + // load the specified resource file + function loadResource(url, onSuccess, onFailure, asynchronous) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, asynchronous); + if (xhr.overrideMimeType) { + xhr.overrideMimeType('text/plain; charset=utf-8'); + } + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + if (xhr.status == 200 || xhr.status === 0) { + if (onSuccess) + onSuccess(xhr.responseText); + } else { + if (onFailure) + onFailure(); + } + } + }; + xhr.send(null); + } + + // load and parse l10n data (warning: global variables are used here) + loadResource(href, function(response) { + gTextData += response; // mostly for debug + + // parse *.properties text data into an l10n dictionary + var data = parseProperties(response); + + // find attribute descriptions, if any + for (var key in data) { + var id, prop, index = key.lastIndexOf('.'); + if (index > 0) { // an attribute has been specified + id = key.substring(0, index); + prop = key.substr(index + 1); + } else { // no attribute: assuming text content by default + id = key; + prop = gTextProp; + } + if (!gL10nData[id]) { + gL10nData[id] = {}; + } + gL10nData[id][prop] = data[key]; + } + + // trigger callback + if (successCallback) + successCallback(); + }, failureCallback, gAsyncResourceLoading); + }; + + // load and parse all resources for the specified locale + function loadLocale(lang, callback) { + clear(); + gLanguage = lang; + + // check all nodes + // and load the resource files + var langLinks = getL10nResourceLinks(); + var langCount = langLinks.length; + if (langCount == 0) { + consoleWarn('no resource to load, early way out'); + fireL10nReadyEvent(lang); + gReadyState = 'complete'; + return; + } + + // start the callback when all resources are loaded + var onResourceLoaded = null; + var gResourceCount = 0; + onResourceLoaded = function() { + gResourceCount++; + if (gResourceCount >= langCount) { + if (callback) // execute the [optional] callback + callback(); + fireL10nReadyEvent(lang); + gReadyState = 'complete'; + } + }; + + // load all resource files + function l10nResourceLink(link) { + var href = link.href; + var type = link.type; + this.load = function(lang, callback) { + var applied = lang; + parseResource(href, lang, callback, function() { + consoleWarn(href + ' not found.'); + applied = ''; + }); + return applied; // return lang if found, an empty string if not found + }; + } + + for (var i = 0; i < langCount; i++) { + var resource = new l10nResourceLink(langLinks[i]); + var rv = resource.load(lang, onResourceLoaded); + if (rv != lang) { // lang not found, used default resource instead + consoleWarn('"' + lang + '" resource not found'); + gLanguage = ''; + } + } + } + + // clear all l10n data + function clear() { + gL10nData = {}; + gTextData = ''; + gLanguage = ''; + // TODO: clear all non predefined macros. + // There's no such macro /yet/ but we're planning to have some... + } + + + /** + * Get rules for plural forms (shared with JetPack), see: + * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html + * https://github.com/mozilla/addon-sdk/blob/master/python-lib/plural-rules-generator.p + * + * @param {string} lang + * locale (language) used. + * + * @return {Function} + * returns a function that gives the plural form name for a given integer: + * var fun = getPluralRules('en'); + * fun(1) -> 'one' + * fun(0) -> 'other' + * fun(1000) -> 'other'. + */ + + function getPluralRules(lang) { + var locales2rules = { + 'af': 3, + 'ak': 4, + 'am': 4, + 'ar': 1, + 'asa': 3, + 'az': 0, + 'be': 11, + 'bem': 3, + 'bez': 3, + 'bg': 3, + 'bh': 4, + 'bm': 0, + 'bn': 3, + 'bo': 0, + 'br': 20, + 'brx': 3, + 'bs': 11, + 'ca': 3, + 'cgg': 3, + 'chr': 3, + 'cs': 12, + 'cy': 17, + 'da': 3, + 'de': 3, + 'dv': 3, + 'dz': 0, + 'ee': 3, + 'el': 3, + 'en': 3, + 'eo': 3, + 'es': 3, + 'et': 3, + 'eu': 3, + 'fa': 0, + 'ff': 5, + 'fi': 3, + 'fil': 4, + 'fo': 3, + 'fr': 5, + 'fur': 3, + 'fy': 3, + 'ga': 8, + 'gd': 24, + 'gl': 3, + 'gsw': 3, + 'gu': 3, + 'guw': 4, + 'gv': 23, + 'ha': 3, + 'haw': 3, + 'he': 2, + 'hi': 4, + 'hr': 11, + 'hu': 0, + 'id': 0, + 'ig': 0, + 'ii': 0, + 'is': 3, + 'it': 3, + 'iu': 7, + 'ja': 0, + 'jmc': 3, + 'jv': 0, + 'ka': 0, + 'kab': 5, + 'kaj': 3, + 'kcg': 3, + 'kde': 0, + 'kea': 0, + 'kk': 3, + 'kl': 3, + 'km': 0, + 'kn': 0, + 'ko': 0, + 'ksb': 3, + 'ksh': 21, + 'ku': 3, + 'kw': 7, + 'lag': 18, + 'lb': 3, + 'lg': 3, + 'ln': 4, + 'lo': 0, + 'lt': 10, + 'lv': 6, + 'mas': 3, + 'mg': 4, + 'mk': 16, + 'ml': 3, + 'mn': 3, + 'mo': 9, + 'mr': 3, + 'ms': 0, + 'mt': 15, + 'my': 0, + 'nah': 3, + 'naq': 7, + 'nb': 3, + 'nd': 3, + 'ne': 3, + 'nl': 3, + 'nn': 3, + 'no': 3, + 'nr': 3, + 'nso': 4, + 'ny': 3, + 'nyn': 3, + 'om': 3, + 'or': 3, + 'pa': 3, + 'pap': 3, + 'pl': 13, + 'ps': 3, + 'pt': 3, + 'rm': 3, + 'ro': 9, + 'rof': 3, + 'ru': 11, + 'rwk': 3, + 'sah': 0, + 'saq': 3, + 'se': 7, + 'seh': 3, + 'ses': 0, + 'sg': 0, + 'sh': 11, + 'shi': 19, + 'sk': 12, + 'sl': 14, + 'sma': 7, + 'smi': 7, + 'smj': 7, + 'smn': 7, + 'sms': 7, + 'sn': 3, + 'so': 3, + 'sq': 3, + 'sr': 11, + 'ss': 3, + 'ssy': 3, + 'st': 3, + 'sv': 3, + 'sw': 3, + 'syr': 3, + 'ta': 3, + 'te': 3, + 'teo': 3, + 'th': 0, + 'ti': 4, + 'tig': 3, + 'tk': 3, + 'tl': 4, + 'tn': 3, + 'to': 0, + 'tr': 0, + 'ts': 3, + 'tzm': 22, + 'uk': 11, + 'ur': 3, + 've': 3, + 'vi': 0, + 'vun': 3, + 'wa': 4, + 'wae': 3, + 'wo': 0, + 'xh': 3, + 'xog': 3, + 'yo': 0, + 'zh': 0, + 'zu': 3 + }; + + // utility functions for plural rules methods + function isIn(n, list) { + return list.indexOf(n) !== -1; + } + function isBetween(n, start, end) { + return start <= n && n <= end; + } + + // list of all plural rules methods: + // map an integer to the plural form name to use + var pluralRules = { + '0': function(n) { + return 'other'; + }, + '1': function(n) { + if ((isBetween((n % 100), 3, 10))) + return 'few'; + if (n === 0) + return 'zero'; + if ((isBetween((n % 100), 11, 99))) + return 'many'; + if (n == 2) + return 'two'; + if (n == 1) + return 'one'; + return 'other'; + }, + '2': function(n) { + if (n !== 0 && (n % 10) === 0) + return 'many'; + if (n == 2) + return 'two'; + if (n == 1) + return 'one'; + return 'other'; + }, + '3': function(n) { + if (n == 1) + return 'one'; + return 'other'; + }, + '4': function(n) { + if ((isBetween(n, 0, 1))) + return 'one'; + return 'other'; + }, + '5': function(n) { + if ((isBetween(n, 0, 2)) && n != 2) + return 'one'; + return 'other'; + }, + '6': function(n) { + if (n === 0) + return 'zero'; + if ((n % 10) == 1 && (n % 100) != 11) + return 'one'; + return 'other'; + }, + '7': function(n) { + if (n == 2) + return 'two'; + if (n == 1) + return 'one'; + return 'other'; + }, + '8': function(n) { + if ((isBetween(n, 3, 6))) + return 'few'; + if ((isBetween(n, 7, 10))) + return 'many'; + if (n == 2) + return 'two'; + if (n == 1) + return 'one'; + return 'other'; + }, + '9': function(n) { + if (n === 0 || n != 1 && (isBetween((n % 100), 1, 19))) + return 'few'; + if (n == 1) + return 'one'; + return 'other'; + }, + '10': function(n) { + if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19))) + return 'few'; + if ((n % 10) == 1 && !(isBetween((n % 100), 11, 19))) + return 'one'; + return 'other'; + }, + '11': function(n) { + if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) + return 'few'; + if ((n % 10) === 0 || + (isBetween((n % 10), 5, 9)) || + (isBetween((n % 100), 11, 14))) + return 'many'; + if ((n % 10) == 1 && (n % 100) != 11) + return 'one'; + return 'other'; + }, + '12': function(n) { + if ((isBetween(n, 2, 4))) + return 'few'; + if (n == 1) + return 'one'; + return 'other'; + }, + '13': function(n) { + if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) + return 'few'; + if (n != 1 && (isBetween((n % 10), 0, 1)) || + (isBetween((n % 10), 5, 9)) || + (isBetween((n % 100), 12, 14))) + return 'many'; + if (n == 1) + return 'one'; + return 'other'; + }, + '14': function(n) { + if ((isBetween((n % 100), 3, 4))) + return 'few'; + if ((n % 100) == 2) + return 'two'; + if ((n % 100) == 1) + return 'one'; + return 'other'; + }, + '15': function(n) { + if (n === 0 || (isBetween((n % 100), 2, 10))) + return 'few'; + if ((isBetween((n % 100), 11, 19))) + return 'many'; + if (n == 1) + return 'one'; + return 'other'; + }, + '16': function(n) { + if ((n % 10) == 1 && n != 11) + return 'one'; + return 'other'; + }, + '17': function(n) { + if (n == 3) + return 'few'; + if (n === 0) + return 'zero'; + if (n == 6) + return 'many'; + if (n == 2) + return 'two'; + if (n == 1) + return 'one'; + return 'other'; + }, + '18': function(n) { + if (n === 0) + return 'zero'; + if ((isBetween(n, 0, 2)) && n !== 0 && n != 2) + return 'one'; + return 'other'; + }, + '19': function(n) { + if ((isBetween(n, 2, 10))) + return 'few'; + if ((isBetween(n, 0, 1))) + return 'one'; + return 'other'; + }, + '20': function(n) { + if ((isBetween((n % 10), 3, 4) || ((n % 10) == 9)) && !( + isBetween((n % 100), 10, 19) || + isBetween((n % 100), 70, 79) || + isBetween((n % 100), 90, 99) + )) + return 'few'; + if ((n % 1000000) === 0 && n !== 0) + return 'many'; + if ((n % 10) == 2 && !isIn((n % 100), [12, 72, 92])) + return 'two'; + if ((n % 10) == 1 && !isIn((n % 100), [11, 71, 91])) + return 'one'; + return 'other'; + }, + '21': function(n) { + if (n === 0) + return 'zero'; + if (n == 1) + return 'one'; + return 'other'; + }, + '22': function(n) { + if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99))) + return 'one'; + return 'other'; + }, + '23': function(n) { + if ((isBetween((n % 10), 1, 2)) || (n % 20) === 0) + return 'one'; + return 'other'; + }, + '24': function(n) { + if ((isBetween(n, 3, 10) || isBetween(n, 13, 19))) + return 'few'; + if (isIn(n, [2, 12])) + return 'two'; + if (isIn(n, [1, 11])) + return 'one'; + return 'other'; + } + }; + + // return a function that gives the plural form name for a given integer + var index = locales2rules[lang.replace(/-.*$/, '')]; + if (!(index in pluralRules)) { + consoleWarn('plural form unknown for [' + lang + ']'); + return function() { return 'other'; }; + } + return pluralRules[index]; + } + + // pre-defined 'plural' macro + gMacros.plural = function(str, param, key, prop) { + var n = parseFloat(param); + if (isNaN(n)) + return str; + + // TODO: support other properties (l20n still doesn't...) + if (prop != gTextProp) + return str; + + // initialize _pluralRules + if (!gMacros._pluralRules) + gMacros._pluralRules = getPluralRules(gLanguage); + var index = '[' + gMacros._pluralRules(n) + ']'; + + // try to find a [zero|one|two] key if it's defined + if (n === 0 && (key + '[zero]') in gL10nData) { + str = gL10nData[key + '[zero]'][prop]; + } else if (n == 1 && (key + '[one]') in gL10nData) { + str = gL10nData[key + '[one]'][prop]; + } else if (n == 2 && (key + '[two]') in gL10nData) { + str = gL10nData[key + '[two]'][prop]; + } else if ((key + index) in gL10nData) { + str = gL10nData[key + index][prop]; + } + + return str; + }; + + + /** + * l10n dictionary functions + */ + + // fetch an l10n object, warn if not found, apply `args' if possible + function getL10nData(key, args, fallback) { + var data = gL10nData[key]; + if (!data) { + consoleWarn('#' + key + ' missing for [' + gLanguage + ']'); + if (!fallback) { + return null; + } + data = fallback; + } + + /** This is where l10n expressions should be processed. + * The plan is to support C-style expressions from the l20n project; + * until then, only two kinds of simple expressions are supported: + * {[ index ]} and {{ arguments }}. + */ + var rv = {}; + for (var prop in data) { + var str = data[prop]; + str = substIndexes(str, args, key, prop); + str = substArguments(str, args); + rv[prop] = str; + } + return rv; + } + + // replace {[macros]} with their values + function substIndexes(str, args, key, prop) { + var reIndex = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)\s*\]\}/; + var reMatch = reIndex.exec(str); + if (!reMatch || !reMatch.length) + return str; + + // an index/macro has been found + // Note: at the moment, only one parameter is supported + var macroName = reMatch[1]; + var paramName = reMatch[2]; + var param; + if (args && paramName in args) { + param = args[paramName]; + } else if (paramName in gL10nData) { + param = gL10nData[paramName]; + } + + // there's no macro parser yet: it has to be defined in gMacros + if (macroName in gMacros) { + var macro = gMacros[macroName]; + str = macro(str, param, key, prop); + } + return str; + } + + // replace {{arguments}} with their values + function substArguments(str, args) { + var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/; + var match = reArgs.exec(str); + while (match) { + if (!match || match.length < 2) + return str; // argument key not found + + var arg = match[1]; + var sub = ''; + if (arg in args) { + sub = args[arg]; + } else if (arg in gL10nData) { + sub = gL10nData[arg][gTextProp]; + } else { + consoleWarn('could not find argument {{' + arg + '}}'); + return str; + } + + str = str.substring(0, match.index) + sub + + str.substr(match.index + match[0].length); + match = reArgs.exec(str); + } + return str; + } + + // translate an HTML element + function translateElement(element) { + var l10n = getL10nAttributes(element); + if (!l10n.id) + return; + + // get the related l10n object + var data = getL10nData(l10n.id, l10n.args); + if (!data) { + consoleWarn('#' + l10n.id + ' missing for [' + gLanguage + ']'); + return; + } + + // translate element (TODO: security checks?) + // for the node content, replace the content of the first child textNode + // and clear other child textNodes + if (data[gTextProp]) { // XXX + if (element.children.length === 0) { + element[gTextProp] = data[gTextProp]; + } else { + var children = element.childNodes, + found = false; + for (var i = 0, l = children.length; i < l; i++) { + if (children[i].nodeType === 3 && + /\S/.test(children[i].textContent)) { // XXX + // using nodeValue seems cross-browser + if (found) { + children[i].nodeValue = ''; + } else { + children[i].nodeValue = data[gTextProp]; + found = true; + } + } + } + if (!found) { + consoleWarn('unexpected error, could not translate element content'); + } + } + delete data[gTextProp]; + } + + for (var k in data) { + element[k] = data[k]; + } + } + + // translate an HTML subtree + function translateFragment(element) { + element = element || document.documentElement; + + // check all translatable children (= w/ a `data-l10n-id' attribute) + var children = getTranslatableChildren(element); + var elementCount = children.length; + for (var i = 0; i < elementCount; i++) { + translateElement(children[i]); + } + + // translate element itself if necessary + translateElement(element); + } + + // cross-browser API (sorry, oldIE doesn't support getters & setters) + return { + // get a localized string + get: function(key, args, fallback) { + var data = getL10nData(key, args, {textContent: fallback}); + if (data) { // XXX double-check this + return 'textContent' in data ? data.textContent : ''; + } + return '{{' + key + '}}'; + }, + + // debug + getData: function() { return gL10nData; }, + getText: function() { return gTextData; }, + + // get|set the document language + getLanguage: function() { return gLanguage; }, + setLanguage: function(lang) { loadLocale(lang, translateFragment); }, + + // get the direction (ltr|rtl) of the current language + getDirection: function() { + // http://www.w3.org/International/questions/qa-scripts + // Arabic, Hebrew, Farsi, Pashto, Urdu + var rtlList = ['ar', 'he', 'fa', 'ps', 'ur']; + return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr'; + }, + + // translate an element or document fragment + translate: translateFragment, + + // this can be used to prevent race conditions + getReadyState: function() { return gReadyState; } + }; + +}) (window, document); diff --git a/common/static/js/vendor/pdfjs/locale/ar/viewer.properties b/common/static/js/vendor/pdfjs/locale/ar/viewer.properties new file mode 100644 index 0000000000..954436ef97 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/ar/viewer.properties @@ -0,0 +1,111 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=Ø§Ù„ØµÙØ­Ø© السابقة +previous_label=السابق +next.title=Ø§Ù„ØµÙØ­Ø© التاليه +next_label=التالي + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=Ø§Ù„ØµÙØ­Ø©: +page_of=من {{pageCount}} + +zoom_out.title=تصغير +zoom_out_label=تصغير +zoom_in.title=تكبير +zoom_in_label=تكبير +zoom.title=التكبير +print.title=طباعة +print_label=طباعة +fullscreen.title=ملء الشاشة +fullscreen_label=ملء الشاشة +open_file.title=ÙØªØ­ المل٠+open_file_label=ÙØªØ­ +download.title=تحميل +download_label=تحميل +bookmark.title=المشهد الحالي (نسخ أو ÙØªØ­ ÙÙŠ Ù†Ø§ÙØ°Ø© جديدة) +bookmark_label=المشهد الحالي + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_slider.title=تبديل الزلاق +toggle_slider_label=تبديل الزلاق +outline.title=إظهار ملخص المستند +outline_label=ملخص المستند +thumbs.title=إظهار الصور المصغرة +thumbs_label=الصور المصغرة +findbar.title=البحث ÙÙŠ المستند +findbar_label=بحث + +# Document outline messages +no_outline=لا يوجد ملخص + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=Ø§Ù„ØµÙØ­Ø© {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=صورة مصغرة من Ø§Ù„ØµÙØ­Ø© {{page}} + +# Context menu +page_rotate_cw.label=تدوير مع عقارب الساعة +page_rotate_ccw.label=تدوير عكس عقارب الساعة + +# Find panel button title and messages +find=بحث +find_terms_not_found=(لا يوجد) + +# Error panel labels +error_more_info=مزيد من المعلومات +error_less_info=معلومات أقل +error_close=إغلاق +# LOCALIZATION NOTE (error_build): "{{build}}" will be replaced by the PDF.JS +# build ID. +error_build=بناء PDF.JS: {{build}} +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=رسالة: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=المكدس: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=الملÙ: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=السطر: {{line}} +rendering_error=حدث خطأ اثناء رسم Ø§Ù„ØµÙØ­Ø©. + +# Predefined zoom values +page_scale_width=عرض Ø§Ù„ØµÙØ­Ø© +page_scale_fit=تناسب Ø§Ù„ØµÙØ­Ø© +page_scale_auto=تقريب تلقائي +page_scale_actual=الحجم الحقيقي + +# Loading indicator messages +loading_error_indicator=خطأ +loading_error=حدث خطأ أثناء تحميل وثيقه الـPDF + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[ملاحظة {{type}}] +request_password=الـPDF محمي بكلمة مرور: + +printing_not_supported=تحذير: الطباعة ليست مدعومة كليًا ÙÙŠ هذا Ø§Ù„Ù…ØªØµÙØ­. diff --git a/common/static/js/vendor/pdfjs/locale/ca/viewer.properties b/common/static/js/vendor/pdfjs/locale/ca/viewer.properties new file mode 100644 index 0000000000..639577e0ba --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/ca/viewer.properties @@ -0,0 +1,127 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=Pàgina anterior +previous_label=Anterior +next.title=Pàgina següent +next_label=Següent + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=Pàgina: +page_of=de {{pageCount}} + +zoom_out.title=Reduir +zoom_out_label=Reduir +zoom_in.title=Ampliar +zoom_in_label=Ampliar +zoom.title=Ampliació +print.title=Imprimir +print_label=Imprimir +fullscreen.title=Pantalla completa +fullscreen_label=Pantalla completa +presentation_mode.title=Canviar a mode de Presentació +presentation_mode_label=Mode de Presentació +open_file.title=Obrir arxiu +open_file_label=Obrir +download.title=Descarregar +download_label=Descarregar +bookmark.title=Vista actual (copiï o obri en una finestra nova) +bookmark_label=Vista actual + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_slider.title=Alternar lliscador +toggle_slider_label=Alternar lliscador +outline.title=Mostrar esquema del document +outline_label=Esquema del document +thumbs.title=Mostrar miniatures +thumbs_label=Miniatures +findbar.title=Cercar en el document +findbar_label=Cercar + +# Document outline messages +no_outline=No hi ha cap esquema disponible + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=Pàgina {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=Miniatura de la pàgina {{page}} + +# Find panel button title and messages +find=Cercar +find_terms_not_found=(No trobat) +# Context menu +first_page.label=Primera pàgina +last_page.label=Darrera pàgina +page_rotate_cw.label=Rotar sentit horari +page_rotate_ccw.label=Rotar sentit anti-horari + +# Find panel button title and messages +find_label=Cerca: +find_previous.title=Trobar ocurrència anterior +find_previous_label=Previ +find_next.title=Trobar ocurrència posterior +find_next_label=Següent +find_highlight=Contrastar tot +find_match_case_label=Majúscules i minúscules +find_wrapped_to_bottom=Part superior assolida, continu a la part inferior +find_wrapped_to_top=Final de pàgina finalitzada, continu a la part superior +find_not_found=Frase no trobada + +# Error panel labels +error_more_info=Més informació +error_less_info=Menys informació +error_close=Tancar +# LOCALIZATION NOTE (error_build): "{{build}}" will be replaced by the PDF.JS +# build ID. +error_build=Compilació de PDF.JS: {{build}} +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=Missatge: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=Pila: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=Arxiu: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=Línia: {{line}} +rendering_error=Ha ocurregut un error mentre es renderitzava la pàgina. + +# Predefined zoom values +page_scale_width=Ample de pàgina +page_scale_fit=Ajustar a la pàgina +page_scale_auto=Ampliació automàtica +page_scale_actual=Tamany real + +# Loading indicator messages +loading_error_indicator=Error +loading_error=Ha ocorregut un error mentres es carregava el PDF. +invalid_file_error=Invàlid o fitxer PDF corrupte. + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[Anotació {{type}}] +request_password=El PDF està protegit amb una contrasenya: + +printing_not_supported=Avís: La impressió no és compatible totalment en aquest navegador. diff --git a/common/static/js/vendor/pdfjs/locale/cs/viewer.properties b/common/static/js/vendor/pdfjs/locale/cs/viewer.properties new file mode 100644 index 0000000000..629480f570 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/cs/viewer.properties @@ -0,0 +1,59 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bookmark.title=Aktuální zobrazení(zkopírovat nebo otevřít v novém oknÄ›) +previous.title=PÅ™edchozí stránka +next.title=Další stránka +print.title=Tisk +download.title=Stáhnout +zoom_out.title=ZmenÅ¡it +zoom_in.title=ZvÄ›tÅ¡it +error_more_info=Více informací +error_less_info=MénÄ› informací +error_close=Zavřít +error_build=PDF.JS Build: {{build}} +error_message=Zpráva:{{message}} +error_stack=Stack:{{stack}} +error_file=Soubor:{{file}} +error_line=Řádek:{{line}} +page_scale_width=Šířka stránky +page_scale_fit=Stránka +page_scale_auto=Automatické pÅ™ibližení +page_scale_actual=SkuteÄná velikost +toggle_slider.title=PÅ™epnout posuvník +thumbs.title=Zobrazit náhledy +outline.title=Zobrazit osnovu dokumentu +loading=NaÄítám... {{percent}}% +loading_error_indicator=Chyba +loading_error=DoÅ¡lo k chybÄ› pÅ™i naÄítání PDF. +rendering_error=DoÅ¡lo k chybÄ› pÅ™i vykreslování stránky. +page_label=Stránka: +page_of=z{{pageCount}} +no_outline=Žádné osnovy k dispozici +open_file.title=Otevřít soubor +text_annotation_type=[{{type}}Anotace] +toggle_slider_label=PÅ™epnout posuvník +thumbs_label=Náhledy +outline_label=PÅ™ehled dokumentu +bookmark_label=Aktuální zobrazení +previous_label=PÅ™edchozí +next_label=Další +print_label=Tisk +download_label=Stáhnout +zoom_out_label=ZmenÅ¡it +zoom_in_label=PÅ™iblížit +zoom.title=ZvÄ›tÅ¡it +thumb_page_title=Stránka{{page}} +thumb_page_canvas=Náhled stránky {{page}} +request_password=PDF je chránÄ›n heslem: diff --git a/common/static/js/vendor/pdfjs/locale/da/viewer.properties b/common/static/js/vendor/pdfjs/locale/da/viewer.properties new file mode 100644 index 0000000000..c710189ace --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/da/viewer.properties @@ -0,0 +1,107 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Værktøjslinje knapper (tooltups og billedtekster) +previous.title=Forrige +previous_label=Forrige +next.title=Næste +next_label=Næste + +# Oversættelsesnote: +# Disse tekststrenge bliver sammensat i formen "Side: X af Y" +# Oversæt ikke "{{pageCount}}", det er en variabel og vil blive erstattet +# med det egentlig antal sider i PDF filen +page_label=Side: +page_of=af {{pageCount}} + +zoom_out.title=Zoom ud +zoom_out_label=Zoom ud +zoom_in.title=Zoom ind +zoom_in_label=Zoom ind +zoom.title=Zoom +print_label=Udskriv +print.title=Udskriv +fullscreen.title=Fuldskærm +fullscreen_label=Fuldskærm +open_file.title=Ã…bn fil +open_file_label=Ã…bn +download.title=Hent +download_label=Hent +bookmark.title=Aktuel visning (kopier eller Ã¥bn i et nyt vindue) +bookmark_label=Aktuel visning + +# Tooltips of alternativ billedtekst til sidepanelet +# (_label strengene er den alternative billedtekst, mens .title +# strengene er tooltips +toggle_slider.title=Skift slider +toggle_slider_label=Skift slider +outline.title=Vis dokumentoversigt +outline_label=Dokumentoversigt +thumbs.title=Vis thumbnails +thumbs_label=Thumbnails +findbar.title=Søg i dokumentet +findbar_label=Søg + +# Dokumentoversigtsbeskeder +no_outline=Ingen dokumentoversigt tilgængelig + +# Thumbnails panelet (tooltips og alt. billedtekst) +# Oversættelsesnote: "{{page}}" vil blive erstattet af det +# egentlige sidetal +thumb_page_title=Side {{page}} +# Oversættelsesnote: "{{page}}" vil blive erstattet af det +# egentlige sidetal +thumb_page_canvas=Thumbnail af side {{page}} + +# Søgepanelet +find=Søg +find_terms_not_found=(Ikke fundet) + +# Fejlpanel +error_more_info=Mere information +error_less_info=Mindre information +error_close=Luk +# Oversættelsesnote: "{{build}}" vil blive erstattet af PDF.JS build nummer +# +error_build=PDF.JS Build: {{build}} +# Oversættelsesnote: "{{message}}" vil blive erstattet af en (engelsk) fejlbesked +# +error_message=Besked: {{message}} +# Oversættelsesnote: "{{stack}}" vil blive erstattet af et stack trace +# +error_stack=Stak: {{stack}} +# Oversættelsesnote: "{{file}}" vil blive erstattet af et filnavn +error_file=Fil: {{file}} +# Oversættelsesnote: "{{line}}" vil blive erstattet af et linjetal +error_line=Linje: {{line}} +rendering_error=Der skete en fejl under gengivelsen af PDF-filen + +# Prædefinerede zoom værdier +page_scale_width=Sidebredde +page_scale_fit=Helside +page_scale_auto=Automatisk zoom +page_scale_actual=Faktisk størrelse + +# Indlæsningsindikator (load ikon) +loading_error_indicator=Fejl +loading_error=Der skete en fejl under indlæsningen af PDF-filen + +# Oversættelsesnote: Dette vil blive brugt som et tooltip +# "{{type}}" vil blive ersattet af en kommentar type fra en liste +# defineret i PDF specifikationen (32000-1:2008 Table 169 – Annotation types). +# Nogle almindelige typer er f.eks.: "Check", "Text", "Comment" og "Note" +text_annotation_type=[{{type}} Kommentar] +request_password=PDF filen er beskyttet med et kodeord: + +printing_not_supported=Advarsel: Denne browser er ikke fuldt understøttet ved udskrift diff --git a/common/static/js/vendor/pdfjs/locale/de/viewer.properties b/common/static/js/vendor/pdfjs/locale/de/viewer.properties new file mode 100644 index 0000000000..0e37c01ff4 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/de/viewer.properties @@ -0,0 +1,123 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=Eine Seite zurück +previous_label=Zurück +next.title=Eine Seite vor +next_label=Vor + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=Seite: +page_of=von {{pageCount}} + +zoom_out.title=Verkleinern +zoom_out_label=Verkleinern +zoom_in.title=Vergrößern +zoom_in_label=Vergrößern +zoom.title=Zoom +print.title=Drucken +print_label=Drucken +presentation_mode.title=Zum Präsentationsmodus wechseln +presentation_mode_label=Bildschirmpräsentation +open_file.title=Datei öffnen +open_file_label=Öffnen +download.title=Herunterladen +download_label=Herunterladen +bookmark.title=Aktuelle Ansicht (Kopieren oder in einem neuen Fenster öffnen) +bookmark_label=Aktuelle Ansicht + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_slider.title=Seitenleiste anzeigen +toggle_slider_label=Seitenleiste +outline.title=Zeige Inhaltsverzeichnis +outline_label=Inhaltsverzeichnis +thumbs.title=Zeige Vorschaubilder +thumbs_label=Vorschaubilder +findbar.title=Im Dokument suchen +findbar_label=Suchen + +# Document outline messages +no_outline=Kein Inhaltsverzeichnis verfügbar + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=Seite {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=Vorschau von Seite {{page}} + +# Context menu +first_page.label=Erste Seite +last_page.label=Letzte Seite +page_rotate_cw.label=Im Uhrzeigersinn drehen +page_rotate_ccw.label=Entgegen dem Uhrzeigersinn drehen + +# Find panel button title and messages +find_label=Suchen: +find_previous.title=Das vorherige Auftreten des Ausdrucks suchen +find_previous_label=Aufwärts +find_next.title=Das nächste Auftreten des Ausdrucks suchen +find_next_label=Abwärts +find_highlight=Hervorheben +find_match_case_label=Groß-/Kleinschreibung +find_reached_top=Der Anfang des Dokuments wurde erreicht, Suche am Ende des Dokuments fortgesetzt +find_reached_bottom=Das Ende des Dokuments wurde erreicht, Suche am Anfang des Dokuments fortgesetzt +find_not_found=Ausdruck nicht gefunden + +# Error panel labels +error_more_info=Mehr Info +error_less_info=Weniger Info +error_close=Schließen +# LOCALIZATION NOTE (error_build): "{{build}}" will be replaced by the PDF.JS +# build ID. +error_build=PDF.JS Build: {{build}} +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=Nachricht: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=Stack: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=Datei: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=Zeile: {{line}} +rendering_error=Das PDF konnte nicht angezeigt werden. + +# Predefined zoom values +page_scale_width=Seitenbreite +page_scale_fit=Ganze Seite +page_scale_auto=Automatisch +page_scale_actual=Originalgröße + +# Loading indicator messages +loading_error_indicator=Fehler +loading_error=Das PDF konnte nicht geladen werden. +invalid_file_error=Ungültige oder beschädigte PDF-Datei. + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[{{type}} Annotation] +request_password=Das PDF ist passwortgeschützt: + +printing_not_supported=Warnung: Drucken wird durch diesen Browser nicht vollständig unterstützt. +web_fonts_disabled=Webfonts sind deaktiviert: Eingebundene PDF-Schriftarten können nicht verwendet werden. diff --git a/common/static/js/vendor/pdfjs/locale/en-US/viewer.properties b/common/static/js/vendor/pdfjs/locale/en-US/viewer.properties new file mode 100644 index 0000000000..e3517cd46a --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/en-US/viewer.properties @@ -0,0 +1,124 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=Previous Page +previous_label=Previous +next.title=Next Page +next_label=Next + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=Page: +page_of=of {{pageCount}} + +zoom_out.title=Zoom Out +zoom_out_label=Zoom Out +zoom_in.title=Zoom In +zoom_in_label=Zoom In +zoom.title=Zoom +print.title=Print +print_label=Print +presentation_mode.title=Switch to Presentation Mode +presentation_mode_label=Presentation Mode +open_file.title=Open File +open_file_label=Open +download.title=Download +download_label=Download +bookmark.title=Current view (copy or open in new window) +bookmark_label=Current View + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_sidebar.title=Toggle Sidebar +toggle_sidebar_label=Toggle Sidebar +outline.title=Show Document Outline +outline_label=Document Outline +thumbs.title=Show Thumbnails +thumbs_label=Thumbnails +findbar.title=Find in Document +findbar_label=Find + +# Document outline messages +no_outline=No Outline Available + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=Page {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=Thumbnail of Page {{page}} + +# Context menu +first_page.label=Go to First Page +last_page.label=Go to Last Page +page_rotate_cw.label=Rotate Clockwise +page_rotate_ccw.label=Rotate Counterclockwise + +# Find panel button title and messages +find_label=Find: +find_previous.title=Find the previous occurrence of the phrase +find_previous_label=Previous +find_next.title=Find the next occurrence of the phrase +find_next_label=Next +find_highlight=Highlight all +find_match_case_label=Match case +find_reached_top=Reached top of document, continued from bottom +find_reached_bottom=Reached end of document, continued from top +find_not_found=Phrase not found + +# Error panel labels +error_more_info=More Information +error_less_info=Less Information +error_close=Close +# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be +# replaced by the PDF.JS version and build ID. +error_version_info=PDF.js v{{version}} (build: {{build}}) +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=Message: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=Stack: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=File: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=Line: {{line}} +rendering_error=An error occurred while rendering the page. + +# Predefined zoom values +page_scale_width=Page Width +page_scale_fit=Page Fit +page_scale_auto=Automatic Zoom +page_scale_actual=Actual Size + +# Loading indicator messages +loading_error_indicator=Error +loading_error=An error occurred while loading the PDF. +invalid_file_error=Invalid or corrupted PDF file. +missing_file_error=Missing PDF file. + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[{{type}} Annotation] +request_password=PDF is protected by a password: + +printing_not_supported=Warning: Printing is not fully supported by this browser. +web_fonts_disabled=Web fonts are disabled: unable to use embedded PDF fonts. diff --git a/common/static/js/vendor/pdfjs/locale/es-MX/viewer.properties b/common/static/js/vendor/pdfjs/locale/es-MX/viewer.properties new file mode 100644 index 0000000000..ebbcaf9b1b --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/es-MX/viewer.properties @@ -0,0 +1,124 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=Página anterior +previous_label=Anterior +next.title=Página siguiente +next_label=Siguiente + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=Página: +page_of=of {{pageCount}} + +zoom_out.title=Reducir +zoom_out_label=Reducir +zoom_in.title=Aumentar +zoom_in_label=Aumentar +zoom.title=Tamaño +print.title=Imprimir +print_label=Imprimir +presentation_mode.title=Cambiar al modo de presentación +presentation_mode_label=Modo de presentación +open_file.title=Abrir archivo +open_file_label=Abrir +download.title=Descargar +download_label=Descargar +bookmark.title=Vista actual (copiar o abrir en una nueva ventana) +bookmark_label=Vista actual + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_sidebar.title=Activar barra lateral +toggle_sidebar_label=Activar barra lateral +outline.title=Mostrar el esquema del documento +outline_label=Esquema del documento +thumbs.title=Mostrar miniaturas +thumbs_label=Miniaturas +findbar.title=Buscar en el documento +findbar_label=Buscar + +# Document outline messages +no_outline=No hay esquema disponible + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=Página {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=Miniatura o página {{page}} + +# Context menu +first_page.label=Ir a la primera página +last_page.label=Ir a la última página +page_rotate_cw.label=Girar hacia la derecha +page_rotate_ccw.label=Girar hacia la izquierda + +# Find panel button title and messages +find_label=Buscar: +find_previous.title=Ir a la anterior frase encontrada +find_previous_label=Anterior +find_next.title=Ir a la siguiente frase encontrada +find_next_label=Siguiente +find_highlight=Marcar todo +find_match_case_label=Coincidir con mayúsculas y minúsculas +find_reached_top=Inicio del documento, se continúa desde el final +find_reached_bottom=Final del documento, se continúa desde el inicio +find_not_found=No se encontró la frase + +# Error panel labels +error_more_info=Más información +error_less_info=Menos información +error_close=Cerrar +# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be +# replaced by the PDF.JS version and build ID. +error_version_info=PDF.js v{{version}} (compilación: {{build}}) +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=Mensaje: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=Pila: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=Archivo: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=Línea: {{line}} +rendering_error=Ocurrió un error al interpretar la página. + +# Predefined zoom values +page_scale_width=Ancho de página +page_scale_fit=Ajustar a la página +page_scale_auto=Ampliación automática +page_scale_actual=Tamaño real + +# Loading indicator messages +loading_error_indicator=Error +loading_error=Ocurrió un error al cargar el PDF. +invalid_file_error=Archivo PDF inválido o corrupto. +missing_file_error=Archivo PDF faltante. + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[Anotación {{type}}] +request_password=El archivo PDF está protegido por contraseña: + +printing_not_supported=Advertencia: la impresión no está completamente soportada en este navegador. +web_fonts_disabled=Las tipografías web están deshabilitadas: no es posible utilizar tipografías PDF incrustadas. diff --git a/common/static/js/vendor/pdfjs/locale/es/viewer.properties b/common/static/js/vendor/pdfjs/locale/es/viewer.properties new file mode 100644 index 0000000000..b4d8055d9f --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/es/viewer.properties @@ -0,0 +1,107 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=Página anterior +previous_label=Anterior +next.title=Página siguiente +next_label=Siguiente + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=Página: +page_of=de {{pageCount}} + +zoom_out.title=Reducir +zoom_out_label=Reducir +zoom_in.title=Ampliar +zoom_in_label=Ampliar +zoom.title=Ampliación +print.title=Imprimir +print_label=Imprimir +fullscreen.title=Pantalla completa +fullscreen_label=Pantalla completa +open_file.title=Abrir archivo +open_file_label=Abrir +download.title=Descargar +download_label=Descargar +bookmark.title=Vista actual (copie o abra en una ventana nueva) +bookmark_label=Vista actual + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_slider.title=Alternar deslizador +toggle_slider_label=Alternar deslizador +outline.title=Mostrar esquema del documento +outline_label=Esquema del documento +thumbs.title=Mostrar miniaturas +thumbs_label=Miniaturas +findbar.title=Buscar en el documento +findbar_label=Buscar + +# Document outline messages +no_outline=No hay un esquema disponible + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=Página {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=Miniatura de la página {{page}} + +# Find panel button title and messages +find=Buscar +find_terms_not_found=(No encontrado) + +# Error panel labels +error_more_info=Más información +error_less_info=Menos información +error_close=Cerrar +# LOCALIZATION NOTE (error_build): "{{build}}" will be replaced by the PDF.JS +# build ID. +error_build=Compilación de PDF.JS: {{build}} +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=Mensaje: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=Pila: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=Archivo: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=Línea: {{line}} +rendering_error=Ocurrió un error mientras se renderizaba la página. + +# Predefined zoom values +page_scale_width=Anchura de página +page_scale_fit=Ajustar a la página +page_scale_auto=Ampliación automática +page_scale_actual=Tamaño real + +# Loading indicator messages +loading_error_indicator=Error +loading_error=Ocurrió un error mientras se cargaba el PDF. + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[Anotación {{type}}] +request_password=El PDF está protegido con una contraseña: + +printing_not_supported=Aviso: La impresión no es compatible totalmente con este navegador. diff --git a/common/static/js/vendor/pdfjs/locale/fi/viewer.properties b/common/static/js/vendor/pdfjs/locale/fi/viewer.properties new file mode 100644 index 0000000000..077b5ea276 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/fi/viewer.properties @@ -0,0 +1,108 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=Edellinen sivu +previous_label=Edellinen +next.title=Seuraava sivu +next_label=Seuraava + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=Sivu: +page_of=/ {{pageCount}} + +zoom_out.title=Suurenna +zoom_out_label=Suurenna +zoom_in.title=Pienennä +zoom_in_label=Pienennä +zoom.title=Sivun suurennus +print.title=Tulosta +print_label=Tulosta +fullscreen.title=Kokoruututila +fullscreen_label=Kokoruututila +open_file.title=Avaa tiedosto +open_file_label=Avaa +download.title=Lataa +download_label=Lataa +bookmark.title=Nykyinen näkymä (kopioi tai avaa uuteen ikkunaan) +bookmark_label=Nykyinen näkymä + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_slider.title=Vaihda vieritysnäkymä +toggle_slider_label=Vaihda vieritysnäkymä +outline.title=Näytä asiakirjan jäsennys +outline_label=Asiakirjan jäsennys +thumbs.title=Näytä esikatselukuvat +thumbs_label=Esikatselukuvat +findbar.title=Etsi asiakirjasta +findbar_label=Etsi + +# Document outline messages +no_outline=Jäsennystä ei ole tarjolla + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=Sivu {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=Sivun {{page}} esikatselukuva + +# Find panel button title and messages +find=Etsi +find_terms_not_found=(Ei löytynyt) + +# Error panel labels +error_more_info=Enemmän tietoa +error_less_info=Vähemmän tietoa +error_close=Sulje +# LOCALIZATION NOTE (error_build): "{{build}}" will be replaced by the PDF.JS +# build ID. +error_build=PDF.JS rakennus: {{build}} +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=Viesti: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=Kutsupino: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=Tiedosto: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=Rivi: {{line}} +rendering_error=Virhe on tapahtunut sivua mallintaessa. + +# Predefined zoom values +page_scale_width=Sivun leveys +page_scale_fit=Sivun sovitus +page_scale_auto=Automaatinen sivun suurennus +page_scale_actual=Todellinen koko + +# Loading indicator messages +loading_error_indicator=Virhe +loading_error=Virhe on tapahtunut PDF:ää ladattaessa. + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[{{type}} Selite] +request_password=PDF on salasanasuojattu: + +printing_not_supported=Varoitus: Tämä selain ei täysin tue tulostusta. + diff --git a/common/static/js/vendor/pdfjs/locale/fr/viewer.properties b/common/static/js/vendor/pdfjs/locale/fr/viewer.properties new file mode 100644 index 0000000000..1c9abfc394 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/fr/viewer.properties @@ -0,0 +1,71 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +previous.title=Page précédente +previous_label=Précédent +next.title=Page suivante +next_label=Suivant +page_label=Page : +page_of=sur {{pageCount}} +zoom_out.title=Zoom arrière +zoom_out_label=Zoom arrière +zoom_in.title=Zoom avant +zoom_in_label=Zoom avant +zoom.title=Zoom +print.title=Imprimer +print_label=Imprimer +presentation_mode.title=Basculer en mode présentation +presentation_mode_label=Mode présentation +open_file.title=Ouvrir le fichier +open_file_label=Ouvrir +download.title=Télécharger +download_label=Télécharger +bookmark.title=Affichage courant (copier ou ouvrir dans une nouvelle fenêtre) +bookmark_label=Affichage actuel +toggle_slider.title=Afficher/masquer le panneau latéral +toggle_slider_label=Afficher/masquer le panneau latéral +outline.title=Afficher les signets +outline_label=Signets du document +thumbs.title=Afficher les vignettes +thumbs_label=Vignettes +findbar.title=Rechercher dans le document +findbar_label=Rechercher +no_outline=Aucun signet disponible +thumb_page_title=Page {{page}} +thumb_page_canvas=Vignette de la page {{page}} +first_page.label=Aller à la première page +last_page.label=Aller à la dernière page +page_rotate_cw.label=Rotation horaire +page_rotate_ccw.label=Rotation anti-horaire + +# Find panel button title and messages +find_label=Rechercher : +find_previous.title=Trouver l'occurrence précédente de la phrase +find_previous_label=Précédent +find_next.title=Trouver la prochaine occurrence de la phrase +find_next_label=Suivant +find_highlight=Tout surligner +find_match_case_label=Respecter la casse +find_wrapped_to_bottom=Bas de la page atteint, poursuite depuis la fin +find_wrapped_to_top=Bas de la page atteint, poursuite au début +find_not_found=Phrase introuvable + +error_more_info=Plus d'informations +error_less_info=Moins d'informations +error_close=Fermer +error_build=Version de PDF.JS : {{build}} +error_message=Message : {{message}} +error_stack=Pile : {{stack}} +error_file=Fichier : {{file}} +error_line=Ligne : {{line}} +rendering_error=Une erreur s'est produite lors de l'affichage de la page. +page_scale_width=Pleine largeur +page_scale_fit=Page entière +page_scale_auto=Zoom automatique +page_scale_actual=Taille réelle +loading_error_indicator=Erreur +loading_error=Une erreur s'est produite lors du chargement du fichier PDF. +text_annotation_type=[Annotation {{type}}] +request_password=Le PDF est protégé par un mot de passe : +printing_not_supported=Attention : l'impression n'est pas totalement prise en charge par ce navigateur. diff --git a/common/static/js/vendor/pdfjs/locale/he/viewer.properties b/common/static/js/vendor/pdfjs/locale/he/viewer.properties new file mode 100644 index 0000000000..6073521747 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/he/viewer.properties @@ -0,0 +1,60 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bookmark.title=דף נוכחי (העתקה ×ו פתיחה בחלון חדש) +previous.title=דף ×§×•×“× +next.title=דף ×”×‘× +print.title=הדפסה +download.title=הורדה +zoom_out.title=התרחקות +zoom_in.title=התקרבות +error_more_info=יותר מידע +error_less_info=פחות מידע +error_close=סגירה +error_build=בניית PDF.JSâ€: {{build}} +error_message=הודעה: {{message}} +error_stack=מחסנית: {{stack}} +error_file=קובץ: {{file}} +error_line=שורה: {{line}} +page_scale_width=רוחב דף +page_scale_fit=גודל דף +page_scale_auto=התקרבות ×וטומטית +page_scale_actual=גודל ×מיתי +toggle_slider.title=מתג החלקה +thumbs.title=הצגת תמונות ממוזערות +outline.title=הצגת מת×ר מסמך +loading=בטעינה... {{percent}}% +loading_error_indicator=שגי××” +loading_error=×ירעה שגי××” בעת טעינת קובץ PDF. +rendering_error=×ירעה שגי××” בעת עיבוד הדף. +page_label=דף: +page_of=מתוך {{pageCount}} +no_outline=×ין מת×ר זמין +open_file.title=פתיחת קובץ +text_annotation_type=[{{type}} Annotation] +toggle_slider_label=מתג החלקה +thumbs_label=תמונות ממוזערות +outline_label=מת×ר מסמך +bookmark_label=תצוגה נוכחית +previous_label=×§×•×“× +next_label=×”×‘× +print_label=הדפסה +download_label=הורדה +zoom_out_label=התרחקות +zoom_in_label=התקרבות +zoom.title=מרחק מתצוגה +thumb_page_title=דף {{page}} +thumb_page_canvas=תמונה ממוזערת של דף {{page}} +request_password=קובץ PDF מוגן בססמה: +open_file_label=פתיחה diff --git a/common/static/js/vendor/pdfjs/locale/it/viewer.properties b/common/static/js/vendor/pdfjs/locale/it/viewer.properties new file mode 100644 index 0000000000..ac7ba369aa --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/it/viewer.properties @@ -0,0 +1,45 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bookmark.title=Visualizzazione corrente (copia o apri in una nuova finestra) +previous.title=Precedente +next.title=Successiva +print.title=Stampa +download.title=Download +zoom_out.title=Riduci Zoom +zoom_in.title=Aumenta Zoom +error_more_info=Più Informazioni +error_less_info=Meno Informazioni +error_close=Chiudi +error_build=PDF.JS Build: {{build}} +error_message=Messaggio: {{message}} +error_stack=Stack: {{stack}} +error_file=File: {{file}} +error_line=Linea: {{line}} +page_scale_width=Adatta alla Larghezza +page_scale_fit=Adatta alla Pagina +page_scale_auto=Zoom Automatico +page_scale_actual=Dimensione Attuale +toggle_slider.title=Visualizza Riquadro Laterale +thumbs.title=Mostra Miniature +outline.title=Mostra Indice Documento +loading=Caricamento... {{percent}}% +loading_error_indicator=Errore +loading_error=È accaduto un errore durante il caricamento del PDF. +rendering_error=È accaduto un errore durante il rendering della pagina. +page_label=Pagina: +page_of=di {{pageCount}} +no_outline=Nessun Indice Disponibile +open_file.title=Apri File +text_annotation_type=[{{type}} Annotazione] diff --git a/common/static/js/vendor/pdfjs/locale/ja/viewer.properties b/common/static/js/vendor/pdfjs/locale/ja/viewer.properties new file mode 100644 index 0000000000..43280355ea --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/ja/viewer.properties @@ -0,0 +1,124 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=å‰ã®ãƒšãƒ¼ã‚¸ +previous_label=å‰ã¸ +next.title=次ã®ãƒšãƒ¼ã‚¸ +next_label=次㸠+ +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=ページ: +page_of=of {{pageCount}} + +zoom_out.title=ç¸®å° +zoom_out_label=ç¸®å° +zoom_in.title=拡大 +zoom_in_label=拡大 +zoom.title=ズーム +print.title=å°åˆ· +print_label=å°åˆ· +presentation_mode.title=プレゼンテーションモードã«åˆ‡ã‚Šæ›¿ãˆã¾ã™ +presentation_mode_label=プレゼンテーションモード +open_file.title=ファイルを開ã +open_file_label=é–‹ã +download.title=ダウンロード +download_label=ダウンロード +bookmark.title=ç¾åœ¨ã®ãƒ“ューをブックマーク +bookmark_label=ç¾åœ¨ã®ãƒ“ューをブックマーク + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_sidebar.title=サイドãƒãƒ¼ã®åˆ‡ã‚Šæ›¿ãˆ +toggle_sidebar_label=サイドãƒãƒ¼ã®åˆ‡ã‚Šæ›¿ãˆ +outline.title=文書ã®ç›®æ¬¡ +outline_label=文書ã®ç›®æ¬¡ +thumbs.title=縮å°ç‰ˆ +thumbs_label=縮å°ç‰ˆ +findbar.title=検索 +findbar_label=検索 + +# Document outline messages +no_outline=利用å¯èƒ½ãªç›®æ¬¡ã¯ã‚りã¾ã›ã‚“ + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title={{page}} ページ +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=ページã®ç¸®å°ç‰ˆ {{page}} + +# Context menu +first_page.label=最åˆã®ãƒšãƒ¼ã‚¸ã¸ç§»å‹• +last_page.label=最後ã®ãƒšãƒ¼ã‚¸ã¸ç§»å‹• +page_rotate_cw.label=å³å›žè»¢ +page_rotate_ccw.label=左回転 + +# Find panel button title and messages +find_label=検索: +find_previous.title=指定文字列ã«ä¸€è‡´ã™ã‚‹ 1 ã¤å‰ã®éƒ¨åˆ†ã‚’検索ã—ã¾ã™ +find_previous_label=å‰ã¸ +find_next.title=指定文字列ã«ä¸€è‡´ã™ã‚‹æ¬¡ã®éƒ¨åˆ†ã‚’検索ã—ã¾ã™ +find_next_label=次㸠+find_highlight=ã™ã¹ã¦å¼·èª¿è¡¨ç¤º +find_match_case_label=大文字/å°æ–‡å­—を区別 +find_reached_top=文書先頭ã¾ã§æ¤œç´¢ã—ãŸã®ã§æœ«å°¾ã«æˆ»ã£ã¦æ¤œç´¢ã—ã¾ã—ãŸã€‚ +find_reached_bottom=文書末尾ã¾ã§æ¤œç´¢ã—ãŸã®ã§å…ˆé ­ã«æˆ»ã£ã¦æ¤œç´¢ã—ã¾ã—ãŸã€‚ +find_not_found=見ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ + +# Error panel labels +error_more_info=詳細情報 +error_less_info=詳細情報ã®éžè¡¨ç¤º +error_close=é–‰ã˜ã‚‹ +# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be +# replaced by the PDF.JS version and build ID. +error_version_info=PDF.js v{{version}} (ビルド: {{build}}) +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=メッセージ: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=スタック: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=ファイル: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=ライン: {{line}} +rendering_error=ページã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—㟠+ +# Predefined zoom values +page_scale_width=å¹…ã«åˆã‚ã›ã‚‹ +page_scale_fit=ページã®ã‚µã‚¤ã‚ºã«åˆã‚ã›ã‚‹ +page_scale_auto=自動ズーム +page_scale_actual=実際ã®ã‚µã‚¤ã‚º + +# Loading indicator messages +loading_error_indicator=エラー +loading_error=PDFã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—㟠+invalid_file_error=無効ã¾ãŸã¯ç ´æã—ãŸPDFファイル +missing_file_error=PDF ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。 + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[{{type}} 注釈] +request_password=PDFã¯ãƒ‘スワードã«ã‚ˆã£ã¦ä¿è­·ã•れã¦ã„ã¾ã™ + +printing_not_supported=警告:ã“ã®ãƒ–ラウザã§ã¯å°åˆ·ãŒå®Œå…¨ã«ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ +web_fonts_disabled=WebフォントãŒç„¡åйã«ãªã£ã¦ã„ã¾ã™: 埋ã‚è¾¼ã¾ã‚ŒãŸPDFã®ãƒ•ォントを使用ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ diff --git a/common/static/js/vendor/pdfjs/locale/locale.properties b/common/static/js/vendor/pdfjs/locale/locale.properties new file mode 100644 index 0000000000..553f3599cb --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/locale.properties @@ -0,0 +1,69 @@ +[ar] +@import url(ar/viewer.properties) + +[ca] +@import url(ca/viewer.properties) + +[cs] +@import url(cs/viewer.properties) + +[da] +@import url(da/viewer.properties) + +[de] +@import url(de/viewer.properties) + +[en-US] +@import url(en-US/viewer.properties) + +[es] +@import url(es/viewer.properties) + +[es-MX] +@import url(es-MX/viewer.properties) + +[fi] +@import url(fi/viewer.properties) + +[fr] +@import url(fr/viewer.properties) + +[he] +@import url(he/viewer.properties) + +[it] +@import url(it/viewer.properties) + +[ja] +@import url(ja/viewer.properties) + +[nl] +@import url(nl/viewer.properties) + +[pl] +@import url(pl/viewer.properties) + +[pt-BR] +@import url(pt-BR/viewer.properties) + +[ro] +@import url(ro/viewer.properties) + +[ru] +@import url(ru/viewer.properties) + +[sr] +@import url(sr/viewer.properties) + +[sv] +@import url(sv/viewer.properties) + +[tr] +@import url(tr/viewer.properties) + +[zh-CN] +@import url(zh-CN/viewer.properties) + +[zh-TW] +@import url(zh-TW/viewer.properties) + diff --git a/common/static/js/vendor/pdfjs/locale/nl/viewer.properties b/common/static/js/vendor/pdfjs/locale/nl/viewer.properties new file mode 100644 index 0000000000..477a51fb23 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/nl/viewer.properties @@ -0,0 +1,124 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=Vorige pagina +previous_label=Vorige +next.title=Volgende pagina +next_label=Volgende + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=Pagina: +page_of=van {{pageCount}} + +zoom_out.title=Uitzoomen +zoom_out_label=Uitzoomen +zoom_in.title=Inzoomen +zoom_in_label=Inzoomen +zoom.title=Zoomen +print.title=Afdrukken +print_label=Afdrukken +presentation_mode.title=Omschakelen naar presentatiemodus +presentation_mode_label=Presentatiemodus +open_file.title=Bestand openen +open_file_label=Openen +download.title=Downloaden +download_label=Downloaden +bookmark.title=Huidige weergave (kopiëren of openen in nieuw venster) +bookmark_label=Huidige weergave + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_sidebar.title=Zijbalk tonen/verbergen +toggle_sidebar_label=Zijbalk tonen/verbergen +outline.title=Documentstructuur tonen +outline_label=Documentstructuur +thumbs.title=Miniaturen tonen +thumbs_label=Miniaturen +findbar.title=Zoeken in document +findbar_label=Zoeken + +# Document outline messages +no_outline=Geen documentstructuur beschikbaar + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=Pagina {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=Miniatuur van pagina {{page}} + +# Context menu +first_page.label=Naar de eerste pagina gaan +last_page.label=Naar de laatste pagina gaan +page_rotate_cw.label=Met de klok mee roteren +page_rotate_ccw.label=Tegen de klok in roteren + +# Find panel button title and messages +find_label=Zoeken: +find_previous.title=Het vorige voorkomen van de tekst zoeken +find_previous_label=Vorige +find_next.title=Het volgende voorkomen van de tekst zoeken +find_next_label=Volgende +find_highlight=Alles markeren +find_match_case_label=Hoofdlettergevoelig +find_reached_top=Bovenkant van de pagina bereikt, doorgegaan vanaf de onderkant +find_reached_bottom=Onderkant van de pagina bereikt, doorgegaan vanaf de bovenkant +find_not_found=Tekst niet gevonden + +# Error panel labels +error_more_info=Meer informatie +error_less_info=Minder informatie +error_close=Sluiten +# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be +# replaced by the PDF.JS version and build ID. +error_version_info=PDF.js versie {{version}} (build {{build}}) +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=Bericht: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=Stack: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=Bestand: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=Regel: {{line}} +rendering_error=Er is een probleem opgetreden bij het renderen van de pagina. + +# Predefined zoom values +page_scale_width=Paginabreed maken +page_scale_fit=Passend maken +page_scale_auto=Automatisch zoomen +page_scale_actual=Werkelijke grootte + +# Loading indicator messages +loading_error_indicator=Fout +loading_error=Er is een fout opgetreden bij het laden van de PDF. +invalid_file_error=Ongeldig of corrupt PDF-bestand. +missing_file_error=Ontbrekend PDF-bestand. + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[{{type}}-aantekening] +request_password=Dit PDF-bestand is beveiligd met een wachtwoord: + +printing_not_supported=Waarschuwing: afdrukken wordt niet volledig ondersteund door deze browser. +web_fonts_disabled=Weblettertypen zijn uitgeschakeld: kan geen ingebakken PDF-lettertypen gebruiken. diff --git a/common/static/js/vendor/pdfjs/locale/pl/viewer.properties b/common/static/js/vendor/pdfjs/locale/pl/viewer.properties new file mode 100644 index 0000000000..d16736911a --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/pl/viewer.properties @@ -0,0 +1,57 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bookmark.title=Aktualny widok (kopiuj lub otwórz w nowym oknie) +previous.title=Poprzednia strona +next.title=NastÄ™pna strona +print.title=Drukuj +download.title=Pobierz +zoom_out.title=Pomniejsz +zoom_in.title=PowiÄ™ksz +error_more_info=WiÄ™cej informacji +error_less_info=Mniej informacji +error_close=Zamknij +error_build=Wersja PDF.JS: {{build}} +error_message=Wiadomość: {{message}} +error_stack=Stos: {{stack}} +error_file=Plik: {{file}} +error_line=Linia: {{line}} +page_scale_width=Szerokość strony +page_scale_fit=CaÅ‚a strona +page_scale_auto=Automatyczne dopasowanie +page_scale_actual=Rzeczywisty rozmiar +toggle_slider.title=Włącz/wyłącz suwak +thumbs.title=WyÅ›wietl miniatury +outline.title=WyÅ›wietl konspekt dokumentu +loading=Wczytywanie... {{percent}}% +loading_error_indicator=Błąd +loading_error=WystÄ…piÅ‚ błąd podczas wczytywania pliku PDF. +invalid_file_error=Błędny lub zepsuty plik PDF. +rendering_error=WystÄ…piÅ‚ błąd podczas wyÅ›wietlania strony. +page_label=Strona: +page_of=z {{pageCount}} +no_outline=Konspekt nie jest dostÄ™pny +open_file.title=Otwórz plik +text_annotation_type=[Komentarz {{type}}] +toggle_slider_label=Przełącz suwak +thumbs_label=Miniatury +outline_label=Konspekt dokumentu +bookmark_label=Aktualny widok +previous_label=Wstecz +next_label=Dalej +print_label=Drukuj +download_label=Pobierz +zoom_out_label=Pomniejsz +zoom_in_label=PowiÄ™ksz +zoom.title=PowiÄ™kszenie diff --git a/common/static/js/vendor/pdfjs/locale/pt-BR/viewer.properties b/common/static/js/vendor/pdfjs/locale/pt-BR/viewer.properties new file mode 100644 index 0000000000..84ab287630 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/pt-BR/viewer.properties @@ -0,0 +1,45 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bookmark.title=Marcar posição atual (bookmark) +previous.title=Página anterior +next.title=Próxima página +print.title=Imprimir +download.title=Baixar arquivo +zoom_out.title=Diminuir Zoom +zoom_in.title=Aumentar Zoom +error_more_info=Mais informações +error_less_info=Menos informações +error_close=Fechar +error_build=PDF.JS Versão: {{build}} +error_message=Mensagem: {{message}} +error_stack=Pilha: {{stack}} +error_file=Arquivo: {{file}} +error_line=Linha: {{line}} +page_scale_width=Largura da página +page_scale_fit=Página inteira +page_scale_auto=Zoom automático +page_scale_actual=Tamanho original +toggle_slider.title=Abrir/fechar aba lateral +thumbs.title=Mostrar miniaturas +outline.title=Mostrar índice +loading=Carregando... {{percent}}% +loading_error_indicator=Erro +loading_error=Um erro ocorreu ao carregar o arquivo. +rendering_error=Um erro ocorreu ao apresentar a página. +page_label=Página: +page_of=de {{pageCount}} +no_outline=Ãndice não disponível +open_file.title=Abrir arquivo +text_annotation_type=[{{type}} Anotações] diff --git a/common/static/js/vendor/pdfjs/locale/ro/viewer.properties b/common/static/js/vendor/pdfjs/locale/ro/viewer.properties new file mode 100644 index 0000000000..af983ab7ff --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/ro/viewer.properties @@ -0,0 +1,56 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bookmark.title=Vederea curentă (copiază sau deschide în fereastră nouă) +previous.title=Pagina precedentă +next.title=Pagina următoare +print.title=TipăreÈ™te +download.title=Descarcă +zoom_out.title=MicÈ™orează +zoom_in.title=MăreÈ™te +error_more_info=Detaliat +error_less_info=Sumarizat +error_close=ÃŽnchide +error_build=PDF.JS Build: {{build}} +error_message=Message: {{message}} +error_stack=Stack: {{stack}} +error_file=File: {{file}} +error_line=Line: {{line}} +page_scale_width=După lățime +page_scale_fit=Toată pagina +page_scale_auto=Mărime automată +page_scale_actual=Mărime originală +toggle_slider.title=Vedere de ansamblu +thumbs.title=Miniaturi +outline.title=Cuprins +loading=ÃŽncărcare... {{percent}}% +loading_error_indicator=Eroare +loading_error=S-a produs o eroare în timpul încărcării documentului. +rendering_error=S-a produs o eroare în timpul procesării paginii. +page_label=Pagina: +page_of=din {{pageCount}} +no_outline=Cuprins indisponibil +open_file.title=Deschide fiÈ™ier +text_annotation_type=[Adnotare {{type}}] +toggle_slider_label=Vedere de ansamblu +thumbs_label=Miniaturi +outline_label=Cuprins +bookmark_label=Vederea curentă +previous_label=ÃŽnapoi +next_label=ÃŽnainte +print_label=TipăreÈ™te +download_label=Descarcă +zoom_out_label=MicÈ™orează +zoom_in_label=MăreÈ™te +zoom.title=Mărime diff --git a/common/static/js/vendor/pdfjs/locale/ru/viewer.properties b/common/static/js/vendor/pdfjs/locale/ru/viewer.properties new file mode 100644 index 0000000000..7f2019b17b --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/ru/viewer.properties @@ -0,0 +1,63 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bookmark.title=СÑылка на текущий вид (Ñкопировать или открыть в новом окне) +previous.title=ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ñтраница +next.title=Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ñтраница +print.title=Печать +download.title=Загрузить +zoom_out.title=Уменьшить +zoom_in.title=Увеличить +error_more_info=Детали +error_less_info=Скрыть детали +error_close=Закрыть +error_build=PDF.JS компилÑциÑ: {{build}} +error_message=Сообщение: {{message}} +error_stack=Стeк: {{stack}} +error_file=Файл: {{file}} +error_line=Строка: {{line}} +page_scale_width=По ширине Ñтраницы +page_scale_fit=Во вÑÑŽ Ñтраницу +page_scale_auto=Ðвто +page_scale_actual=ÐаÑтоÑщий размер +toggle_slider.title=Открыть/закрыть вÑпомогательную панель +thumbs.title=Показать уменьшенные Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ +outline.title=Показать Ñодержание документа +loading=Загрузка... {{percent}}% +loading_error_indicator=Ошибка +loading_error=Произошла ошибка во Ð²Ñ€ÐµÐ¼Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ PDF. +rendering_error=Произошла ошибка во Ð²Ñ€ÐµÐ¼Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñтраницы. +page_label=Страница: +page_of=из {{pageCount}} +no_outline=Содержание не доÑтупно +open_file.title=Открыть файл +text_annotation_type=[ÐÐ½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ {{type}}] +toggle_slider_label=Ð’ÑÐ¿Ð¾Ð¼Ð¾Ð³Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¿Ð°Ð½ÐµÐ»ÑŒ +thumbs_label=Уменьшенные Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ +outline_label=Содержание документа +bookmark_label=Текущий вид +previous_label=ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ +next_label=Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ +print_label=Печать +download_label=Загрузить +zoom_out_label=Уменьшить +zoom_in_label=Увеличить +zoom.title=МаÑштаб +thumb_page_title=Страница {{page}} +thumb_page_canvas=Уменьшенное изображение Ñтраницы {{page}} +request_password=PDF защищён паролем: +fullscreen.title=Полный Ñкран +fullscreen_label=Полный Ñкран +page_rotate_cw.label=Повернуть по чаÑовой Ñтрелке +page_rotate_ccw.label=Повернуть против чаÑовой Ñтрелки diff --git a/common/static/js/vendor/pdfjs/locale/sr/viewer.properties b/common/static/js/vendor/pdfjs/locale/sr/viewer.properties new file mode 100644 index 0000000000..04498a91e7 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/sr/viewer.properties @@ -0,0 +1,56 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bookmark.title=Тренутни приказ (Умножити или отворити у новом прозору) +previous.title=Предходна Ñтрана +next.title=Следећа Ñтрана +print.title=Штампај +download.title=Преузми +zoom_out.title=Умањи +zoom_in.title=Увећај +error_more_info=Више информација +error_less_info=Мање информација +error_close=Затвори +error_build=PDF.JS Build: {{build}} +error_message=Message: {{message}} +error_stack=Stack: {{stack}} +error_file=File: {{file}} +error_line=Line: {{line}} +page_scale_width=Ширина Ñтранице +page_scale_fit=Уклопи +page_scale_auto=Увећај аутоматÑки +page_scale_actual=Стварна величина +toggle_slider.title=Клизач +thumbs.title=Прикажи у Ñличицама +outline.title=Прикажи у линијама +loading=Учитавање... {{percent}}% +loading_error_indicator=Грешка +loading_error=Дошло је до грешке током учитавања ПДФ-а. +rendering_error=Дошло је до грешке приликом приказивања Ñтране. +page_label=Страна: +page_of=од {{pageCount}} +no_outline=Ðема линија +open_file.title=Отвори датотеку +text_annotation_type=[{{type}} Annotation] +toggle_slider_label=Клизач +thumbs_label=Сличице +outline_label=Документи у линијама +bookmark_label=Тренутни приказ +previous_label=Предходна +next_label=Следећа +print_label=Штампај +download_label=Преузми +zoom_out_label=Умањи +zoom_in_label=Увећај +zoom.title=Скала diff --git a/common/static/js/vendor/pdfjs/locale/sv/viewer.properties b/common/static/js/vendor/pdfjs/locale/sv/viewer.properties new file mode 100644 index 0000000000..4d0969dfc0 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/sv/viewer.properties @@ -0,0 +1,124 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=FöregÃ¥ende sida +previous_label=FöregÃ¥ende +next.title=Nästa sida +next_label=Nästa + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=Sida: +page_of=av {{pageCount}} + +zoom_out.title=Zooma ut +zoom_out_label=Zooma ut +zoom_in.title=Zooma in +zoom_in_label=Zooma in +zoom.title=Zooma +print.title=Skriv ut +print_label=Skriv ut +presentation_mode.title=Presentationsläge +presentation_mode_label=Presentationsläge +open_file.title=Öppna fil +open_file_label=Öppna +download.title=Ladda ner +download_label=Ladda ner +bookmark.title=Aktuell vy (kopiera eller öppna i nytt fönster) +bookmark_label=Aktuell vy + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_sidebar.title=Visa/Dölj sidopanel +toggle_sidebar_label=Visa/Dölj sidopanel +outline.title=Visa bokmärken +outline_label=Bokmärken +thumbs.title=Visa sidminiatyrer +thumbs_label=Sidminiatyrer +findbar.title=Sök i dokumentet +findbar_label=Sök + +# Document outline messages +no_outline=Inga bokmärken tillgängliga + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=Sida {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=Miniatyr av sida {{page}} + +# Context menu +first_page.label=GÃ¥ till första sidan +last_page.label=GÃ¥ till sista sidan +page_rotate_cw.label=Rotera medurs +page_rotate_ccw.label=Rotera moturs + +# Find panel button title and messages +find_label=Sök: +find_previous.title=Hitta föregÃ¥ende förekomst av frasen +find_previous_label=FöregÃ¥ende +find_next.title=Hitta nästa förekomst av frasen +find_next_label=Nästa +find_highlight=Markera alla +find_match_case_label=Matcha VERSALER/gemener +find_reached_top=Kommit till början av dokumentet, börjat om +find_reached_bottom=Kommit till slutet av dokumentet, börjat om +find_not_found=Frasen hittades inte + +# Error panel labels +error_more_info=Mer information +error_less_info=Mindre information +error_close=Stäng +# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be +# replaced by the PDF.JS version and build ID. +error_version_info=PDF.js v{{version}} (bygge: {{build}}) +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=Meddelande: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=Stack: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=Fil: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=Rad: {{line}} +rendering_error=Ett fel inträffade när sidan renderades. + +# Predefined zoom values +page_scale_width=Sidbredd +page_scale_fit=Helsida +page_scale_auto=Automatisk zoom +page_scale_actual=Faktisk storlek + +# Loading indicator messages +loading_error_indicator=Fel +loading_error=Ett fel inträffade när PDF-filen laddades. +invalid_file_error=Ogiltig eller korrupt PDF-fil. +missing_file_error=PDF-filen saknas. + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[{{type}}-anteckning] +request_password=PDF-filen är lösenordsskyddad: + +printing_not_supported=Varning: Utskrifter stöds inte fullt ut av denna webbläsare. +web_fonts_disabled=Webbtypsnitt är inaktiverade: Typsnitt inkluderade i PDF-filer kan ej användas. diff --git a/common/static/js/vendor/pdfjs/locale/tr/viewer.properties b/common/static/js/vendor/pdfjs/locale/tr/viewer.properties new file mode 100644 index 0000000000..ffb95f5df9 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/tr/viewer.properties @@ -0,0 +1,124 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=Önceki Sayfa +previous_label=Önceki +next.title=Sonraki Sayfa +next_label=Sonraki + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=Sayfa: +page_of=- {{pageCount}} + +zoom_out.title=UzaklaÅŸ +zoom_out_label=UzaklaÅŸ +zoom_in.title=YakınlaÅŸ +zoom_in_label=YakınlaÅŸ +zoom.title=YakınlaÅŸtır +print.title=Yazdır +print_label=Yazdır +presentation_mode.title=Sunum moduna geçiÅŸ yap +presentation_mode_label=Sunum Modu +open_file.title=Dosya Aç +open_file_label=Aç +download.title=İndir +download_label=İndir +bookmark.title=Mevcut görünüm (kopyala yada yeni sayfada aç) +bookmark_label=Mevcut Görünüm + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_sidebar.title=Yan Menü Aç/Kapa +toggle_sidebar_label=Yan Menü +outline.title=Sayfa kenarlıklarını döster +outline_label=Sayfa Kenarlıkları +thumbs.title=Önizleme resimlerini göster +thumbs_label=Önizleme +findbar.title=Döküman içerisinde bul +findbar_label=Bul + +# Document outline messages +no_outline=Kenarlık Mevcut DeÄŸil + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=Sayfa {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas={{page}} sayfasının ön izlemesi + +# Context menu +first_page.label=İlk Sayfaya Git +last_page.label=Son Sayfaya Git +page_rotate_cw.label=SaÄŸa Çevir +page_rotate_ccw.label=Sola Çevir + +# Find panel button title and messages +find_label=Bul: +find_previous.title=Önceki cümleyi bul +find_previous_label=Önceki +find_next.title=Sonraki cümleyi bul +find_next_label=Sonraki +find_highlight=Hepsini belirt +find_match_case_label=harf eÅŸleme +find_reached_top=Dosyanın en üstüne varıldı. Sonundan devam ediliyor +find_reached_bottom=Dosyanın sonuna varıldı. Başından devam ediliyor +find_not_found=Aramanızla eÅŸleÅŸen sonuç yok + +# Error panel labels +error_more_info=Daha falza bilgi +error_less_info=daha az bilgi +error_close=Kapat +# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be +# replaced by the PDF.JS version and build ID. +error_version_info=PDF.js v{{version}} (build: {{build}}) +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=Mesaj: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=Yığın: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=Dosya: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=Satır: {{line}} +rendering_error=Sayfa oluÅŸturulurken bir hata meydana geldi. + +# Predefined zoom values +page_scale_width=Sayfa GeniÅŸliÄŸi +page_scale_fit=Sayfayı Sığdır +page_scale_auto=Otomatik YakınlaÅŸma +page_scale_actual=Gerçek boyut + +# Loading indicator messages +loading_error_indicator=Hata +loading_error=PDF yüklenirken hata. +invalid_file_error=Geçersiz yada bozuk dosya. +missing_file_error=PDF dosyası bulunamadı. + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[{{type}} Not] +request_password=PDF Åžifre ile korunmakta: + +printing_not_supported=Uyarı: Yazdırma iÅŸlemi bu tarayıcı ile tam desteklenmiyor. +web_fonts_disabled=Web Fontları devre dışı. Web fontlar yüklenemiyor. diff --git a/common/static/js/vendor/pdfjs/locale/zh-CN/viewer.properties b/common/static/js/vendor/pdfjs/locale/zh-CN/viewer.properties new file mode 100644 index 0000000000..80ee751b64 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/zh-CN/viewer.properties @@ -0,0 +1,124 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Main toolbar buttons (tooltips and alt text for images) +previous.title=上一页 +previous_label=å‘上 +next.title=下一页 +next_label=å‘下 + +# LOCALIZATION NOTE (page_label, page_of): +# These strings are concatenated to form the "Page: X of Y" string. +# Do not translate "{{pageCount}}", it will be substituted with a number +# representing the total number of pages. +page_label=页ç : +page_of=/ {{pageCount}} + +zoom_out.title=ç¼©å° +zoom_out_label=ç¼©å° +zoom_in.title=放大 +zoom_in_label=放大 +zoom.title=缩放 +print.title=æ‰“å° +print_label=æ‰“å° +presentation_mode.title=切æ¢è‡³å¹»ç¯æ¨¡å¼ +presentation_mode_label=å¹»ç¯æ¨¡å¼ +open_file.title=打开文件 +open_file_label=打开 +download.title=下载 +download_label=下载 +bookmark.title=当å‰è§†å›¾ï¼ˆå¤åˆ¶æˆ–在新窗å£ä¸­æ‰“开) +bookmark_label=当å‰è§†å›¾ + +# Tooltips and alt text for side panel toolbar buttons +# (the _label strings are alt text for the buttons, the .title strings are +# tooltips) +toggle_sidebar.title=切æ¢ä¾§æ  +toggle_sidebar_label=切æ¢ä¾§æ  +outline.title=显示文档大纲 +outline_label=文档大纲 +thumbs.title=显示缩略图 +thumbs_label=缩略图 +findbar.title=在该文档内查找 +findbar_label=查找 + +# Document outline messages +no_outline=没有å¯ç”¨çš„大纲 + +# Thumbnails panel item (tooltip and alt text for images) +# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page +# number. +thumb_page_title=é¡µç  {{page}} +# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page +# number. +thumb_page_canvas=é¡µé¢ {{page}} 的缩略图 + +# Context menu +first_page.label=转到第一页 +last_page.label=转到结尾页 +page_rotate_cw.label=顺时针旋转 +page_rotate_ccw.label=逆时针旋转 + +# Find panel button title and messages +find_label=查找: +find_previous.title=查找该短语上一次出现的ä½ç½® +find_previous_label=上一个 +find_next.title=查找该短语下一次出现的ä½ç½® +find_next_label=下一个 +find_highlight=全部高亮 +find_match_case_label=区分大å°å†™ +find_reached_top=已查找至文档的开始ä½ç½®ï¼Œå°†ä»Žæ–‡æ¡£æœ«å°¾ç»§ç»­æŸ¥æ‰¾ +find_reached_bottom=已查找至文档的末尾ä½ç½®ï¼Œå°†ä»Žæ–‡æ¡£çš„开始ä½ç½®ç»§ç»­æŸ¥æ‰¾ +find_not_found=找ä¸åˆ° + +# Error panel labels +error_more_info=æ›´å¤šä¿¡æ¯ +error_less_info=ç®€ç•¥ä¿¡æ¯ +error_close=关闭 +# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be +# replaced by the PDF.JS version and build ID. +error_version_info=PDF.js v{{version}} (构建版本: {{build}}) +# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an +# english string describing the error. +error_message=错误信æ¯: {{message}} +# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack +# trace. +error_stack=堆栈: {{stack}} +# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename +error_file=文件: {{file}} +# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number +error_line=行数: {{line}} +rendering_error=æ¸²æŸ“é¡µé¢æ—¶å‡ºé”™ã€‚ + +# Predefined zoom values +page_scale_width=符åˆé¡µå®½ +page_scale_fit=符åˆé¡µé¢ +page_scale_auto=自动缩放 +page_scale_actual=å®žé™…å¤§å° + +# Loading indicator messages +loading_error_indicator=错误 +loading_error=加载 PDF 文件时出错。 +invalid_file_error=PDF 文件无效或已æŸå。 +missing_file_error=缺失 PDF 文件。 + +# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip. +# "{{type}}" will be replaced with an annotation type from a list defined in +# the PDF spec (32000-1:2008 Table 169 – Annotation types). +# Some common types are e.g.: "Check", "Text", "Comment", "Note" +text_annotation_type=[{{type}} 注解] +request_password=该 PDF 文档å—密ç ä¿æŠ¤ï¼š + +printing_not_supported=警告:该æµè§ˆå™¨ä¸èƒ½å®Œå…¨æ”¯æŒæ‰“å°ã€‚ +web_fonts_disabled=Web 页é¢å­—体已被ç¦ç”¨ï¼Œæ— æ³•使用嵌入到 PDF 中的字体。 diff --git a/common/static/js/vendor/pdfjs/locale/zh-TW/viewer.properties b/common/static/js/vendor/pdfjs/locale/zh-TW/viewer.properties new file mode 100644 index 0000000000..8ddd069de7 --- /dev/null +++ b/common/static/js/vendor/pdfjs/locale/zh-TW/viewer.properties @@ -0,0 +1,114 @@ +# Copyright 2012 Mozilla Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ä¸»å·¥å…·åˆ—æŒ‰éµ (工具æç¤ºå’Œåœ–åƒçš„æ›¿ä»£æ–‡å­—) +previous.title=ä¸Šä¸€é  +previous_label=ä¸Šä¸€é  +next.title=ä¸‹ä¸€é  +next_label=ä¸‹ä¸€é  + +# 本地化æç¤º (page_label, page_of): +# é€™äº›å­—ç¬¦ä¸²æœƒé€£æŽ¥æˆ "Page: X of Y" 的表示方å¼ã€‚ +# ä¸è¦ç¿»è­¯ "{{pageCount}}" , å› ç‚ºå®ƒç”¨ä¾†è¡¨ç¤ºç¸½é æ•¸ã€‚ +page_label=第 +page_of=é ï¼Œå…± {{pageCount}} é  + +zoom_out.title=ç¸®å° +zoom_out_label=ç¸®å° +zoom_in.title=放大 +zoom_in_label=放大 +zoom.title=縮放 +print.title=åˆ—å° +print_label=åˆ—å° +presentation_mode.title=切æ›åˆ°ç°¡å ±æ¨¡å¼ +presentation_mode_label=ç°¡å ±æ¨¡å¼ +open_file.title=開啟檔案 +open_file_label=開啟 +download.title=下載 +download_label=下載 +bookmark.title=ç›®å‰æª¢è¦–(複製或在新視窗中開啟) +bookmark_label=ç›®å‰æª¢è¦– + +# å´é‚Šæ¬„å·¥å…·åˆ—æŒ‰éµ (工具æç¤ºå’Œåœ–åƒçš„æ›¿ä»£æ–‡å­—) +# (_label 字符串是按éµçš„æ›¿ä»£æ–‡å­—, .title 字符串是工具æç¤º) +toggle_sidebar.title=切æ›å´é‚Šæ¬„ +toggle_sidebar_label=切æ›å´é‚Šæ¬„ +outline.title=é¡¯ç¤ºæ–‡ä»¶ç¶±è¦ +outline_label=æ–‡ä»¶ç¶±è¦ +thumbs.title=顯示縮圖 +thumbs_label=縮圖 +findbar.title=在文件中æœå°‹ +findbar_label=æœç´¢ + +# 文件綱è¦ç›¸é—œè¨Šæ¯ +no_outline=ç„¡å¯ç”¨çš„ç¶±è¦ + +# ç¸®åœ–é¢æ¿é …ç›® (工具æç¤ºå’Œåœ–åƒçš„æ›¿ä»£æ–‡å­—) +# 本地化æç¤º (thumb_page_title): "{{page}}" æœƒè¢«é æ•¸å–代。 +thumb_page_title=第 {{page}} é  +# 本地化æç¤º (thumb_page_canvas): "{{page}}" æœƒè¢«é æ•¸å–代。 +thumb_page_canvas=第 {{page}} é çš„縮圖 + +# å³éµèœå–® +page_rotate_cw.label=é †æ™‚é‡æ—‹è½‰ +page_rotate_ccw.label=é€†æ™‚é‡æ—‹è½‰ + +# æœå°‹é¢æ¿æŒ‰éµæ–‡å­—åŠè¨Šæ¯ +find_label=æœå°‹ï¼š +find_previous.title=尋找上一個出ç¾çš„詞組 +find_previous_label=上一個 +find_next.title=尋找下一個出ç¾çš„詞組 +find_next_label=下一個 +find_highlight=全部以高亮顯示 +find_match_case_label=å€åˆ†å¤§å°å¯« +find_reached_top=åˆ°é”æ–‡ä»¶é ‚端,由末端繼續æœå°‹ +find_reached_bottom=åˆ°é”æ–‡ä»¶æœ«ç«¯ï¼Œç”±é ‚端繼續æœå°‹ +find_not_found=找ä¸åˆ°è©žçµ„ + +# éŒ¯èª¤é¢æ¿æ¨™ç±¤ +error_more_info=更多資訊 +error_less_info=更少資訊 +error_close=關閉 +# 本地化æç¤º (error_version_info): "{{version}}" and "{{build}}" 會被PDF.JS版本編號åŠçµ„建編號å–代。 +error_version_info=PDF.js v{{version}} (組建: {{build}}) +# 本地化æç¤º (error_message): "{{message}}" 會被英文的錯誤æè¿°å–代。 +error_message=錯誤信æ¯ï¼š{{message}} +# 本地化æç¤º (error_stack): "{{stack}}" 會被錯誤堆疊å–代。 +error_stack=堆疊:{{stack}} +# 本地化æç¤º (error_file): "{{file}}" 會被檔案å稱å–代。 +error_file=檔案:{{file}} +# 本地化æç¤º (error_line): "{{line}}" 會被行數å–代。 +error_line=行數:{{line}} +rendering_error=渲染é é¢æ™‚發生錯誤。 + +# é è¨­çš„縮放值 +page_scale_width=符åˆé å¯¬ +page_scale_fit=符åˆé é¢ +page_scale_auto=自動縮放 +page_scale_actual=å¯¦éš›å¤§å° + +# è¼‰å…¥æŒ‡ç¤ºè¨Šæ¯ +loading_error_indicator=錯誤 +loading_error=載入PDF檔案時發生錯誤。 +invalid_file_error=ç„¡æ•ˆæˆ–å—æçš„PDF檔案。 +missing_file_error=éºå¤±PDF檔案。 + +# å…¶ä»–æ¨™ç±¤å’Œè¨Šæ¯ +# "{{type}}" 用來表示PDFæ ¼å¼è¦ç¯„ (32000-1:2008 Table 169 – Annotation types) 入颿‰€å®šç¾©çš„註解種類。 +# 一些常見的類型有: "Check"〠"Text"〠"Comment"〠"Note" +text_annotation_type=ï¼»{{type}} 註解] +request_password=PDF檔案å—密碼ä¿è­·ï¼š + +printing_not_supported=警告:這個ç€è¦½å™¨ä¸å®Œå…¨æ”¯æ´åˆ—å°ã€‚ +web_fonts_disabled=ç¦æ­¢ä½¿ç”¨ç¶²è·¯å­—型:無法使用嵌入PDF檔案的字型。 diff --git a/common/static/js/vendor/pdfjs/pdf.js.REMOVED.git-id b/common/static/js/vendor/pdfjs/pdf.js.REMOVED.git-id new file mode 100644 index 0000000000..b39c0476bb --- /dev/null +++ b/common/static/js/vendor/pdfjs/pdf.js.REMOVED.git-id @@ -0,0 +1 @@ +34d1996e44f78168a73297217b3a0973c2ae90e1 \ No newline at end of file diff --git a/common/static/js/vendor/pdfjs/viewer.html b/common/static/js/vendor/pdfjs/viewer.html new file mode 100644 index 0000000000..6dfb306f30 --- /dev/null +++ b/common/static/js/vendor/pdfjs/viewer.html @@ -0,0 +1,204 @@ + + + + + + + PDF.js viewer + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+ + +
+
+
+
+
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ +
+ +
+ + + + +
+
+ + + + + + + + + + + + Current View +
+
+
+
+ +
+ +
+ + + +
+
+
+
+
+ + + + + + + + +
+
+
+ +
+
+
+
+ + +
+ +
+
+ + + diff --git a/common/static/js/vendor/pdfjs/viewer.js b/common/static/js/vendor/pdfjs/viewer.js new file mode 100644 index 0000000000..06d6791884 --- /dev/null +++ b/common/static/js/vendor/pdfjs/viewer.js @@ -0,0 +1,3281 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* globals PDFJS, PDFBug, FirefoxCom, Stats */ + +'use strict'; + +var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf'; +var DEFAULT_SCALE = 'auto'; +var DEFAULT_SCALE_DELTA = 1.1; +var UNKNOWN_SCALE = 0; +var CACHE_SIZE = 20; +var CSS_UNITS = 96.0 / 72.0; +var SCROLLBAR_PADDING = 40; +var VERTICAL_PADDING = 5; +var MIN_SCALE = 0.25; +var MAX_SCALE = 4.0; +var IMAGE_DIR = './images/'; +var SETTINGS_MEMORY = 20; +var ANNOT_MIN_SIZE = 10; +var RenderingStates = { + INITIAL: 0, + RUNNING: 1, + PAUSED: 2, + FINISHED: 3 +}; +var FindStates = { + FIND_FOUND: 0, + FIND_NOTFOUND: 1, + FIND_WRAPPED: 2, + FIND_PENDING: 3 +}; + + PDFJS.workerSrc = '/static/js/vendor/pdfjs/pdf.js'; + +var mozL10n = document.mozL10n || document.webL10n; + +function getFileName(url) { + var anchor = url.indexOf('#'); + var query = url.indexOf('?'); + var end = Math.min( + anchor > 0 ? anchor : url.length, + query > 0 ? query : url.length); + return url.substring(url.lastIndexOf('/', end) + 1, end); +} + +function scrollIntoView(element, spot) { + // Assuming offsetParent is available (it's not available when viewer is in + // hidden iframe or object). We have to scroll: if the offsetParent is not set + // producing the error. See also animationStartedClosure. + var parent = element.offsetParent, offsetY = element.offsetTop; + if (!parent) { + console.error('offsetParent is not set -- cannot scroll'); + return; + } + while (parent.clientHeight == parent.scrollHeight) { + offsetY += parent.offsetTop; + parent = parent.offsetParent; + if (!parent) + return; // no need to scroll + } + if (spot) + offsetY += spot.top; + parent.scrollTop = offsetY; +} + +var Cache = function cacheCache(size) { + var data = []; + this.push = function cachePush(view) { + var i = data.indexOf(view); + if (i >= 0) + data.splice(i); + data.push(view); + if (data.length > size) + data.shift().destroy(); + }; +}; + +var ProgressBar = (function ProgressBarClosure() { + + function clamp(v, min, max) { + return Math.min(Math.max(v, min), max); + } + + function ProgressBar(id, opts) { + + // Fetch the sub-elements for later + this.div = document.querySelector(id + ' .progress'); + + // Get options, with sensible defaults + this.height = opts.height || 100; + this.width = opts.width || 100; + this.units = opts.units || '%'; + + // Initialize heights + this.div.style.height = this.height + this.units; + } + + ProgressBar.prototype = { + + updateBar: function ProgressBar_updateBar() { + if (this._indeterminate) { + this.div.classList.add('indeterminate'); + return; + } + + var progressSize = this.width * this._percent / 100; + + if (this._percent > 95) + this.div.classList.add('full'); + else + this.div.classList.remove('full'); + this.div.classList.remove('indeterminate'); + + this.div.style.width = progressSize + this.units; + }, + + get percent() { + return this._percent; + }, + + set percent(val) { + this._indeterminate = isNaN(val); + this._percent = clamp(val, 0, 100); + this.updateBar(); + } + }; + + return ProgressBar; +})(); + + +// Settings Manager - This is a utility for saving settings +// First we see if localStorage is available +// If not, we use FUEL in FF +// Use asyncStorage for B2G +var Settings = (function SettingsClosure() { + var isLocalStorageEnabled = (function localStorageEnabledTest() { + // Feature test as per http://diveintohtml5.info/storage.html + // The additional localStorage call is to get around a FF quirk, see + // bug #495747 in bugzilla + try { + return 'localStorage' in window && window['localStorage'] !== null && + localStorage; + } catch (e) { + return false; + } + })(); + + function Settings(fingerprint) { + this.fingerprint = fingerprint; + this.initializedPromise = new PDFJS.Promise(); + + var resolvePromise = (function settingsResolvePromise(db) { + this.initialize(db || '{}'); + this.initializedPromise.resolve(); + }).bind(this); + + + + if (isLocalStorageEnabled) + resolvePromise(localStorage.getItem('database')); + } + + Settings.prototype = { + initialize: function settingsInitialize(database) { + database = JSON.parse(database); + if (!('files' in database)) + database.files = []; + if (database.files.length >= SETTINGS_MEMORY) + database.files.shift(); + var index; + for (var i = 0, length = database.files.length; i < length; i++) { + var branch = database.files[i]; + if (branch.fingerprint == this.fingerprint) { + index = i; + break; + } + } + if (typeof index != 'number') + index = database.files.push({fingerprint: this.fingerprint}) - 1; + this.file = database.files[index]; + this.database = database; + }, + + set: function settingsSet(name, val) { + if (!this.initializedPromise.isResolved) + return; + + var file = this.file; + file[name] = val; + var database = JSON.stringify(this.database); + + + + if (isLocalStorageEnabled) + localStorage.setItem('database', database); + }, + + get: function settingsGet(name, defaultValue) { + if (!this.initializedPromise.isResolved) + return defaultValue; + + return this.file[name] || defaultValue; + } + }; + + return Settings; +})(); + +var cache = new Cache(CACHE_SIZE); +var currentPageNumber = 1; + +var PDFFindController = { + startedTextExtraction: false, + + extractTextPromises: [], + + // If active, find results will be highlighted. + active: false, + + // Stores the text for each page. + pageContents: [], + + pageMatches: [], + + // Currently selected match. + selected: { + pageIdx: -1, + matchIdx: -1 + }, + + // Where find algorithm currently is in the document. + offset: { + pageIdx: null, + matchIdx: null + }, + + resumePageIdx: null, + + resumeCallback: null, + + state: null, + + dirtyMatch: false, + + findTimeout: null, + + initialize: function() { + var events = [ + 'find', + 'findagain', + 'findhighlightallchange', + 'findcasesensitivitychange' + ]; + + this.handleEvent = this.handleEvent.bind(this); + + for (var i = 0; i < events.length; i++) { + window.addEventListener(events[i], this.handleEvent); + } + }, + + calcFindMatch: function(pageIndex) { + var pageContent = this.pageContents[pageIndex]; + var query = this.state.query; + var caseSensitive = this.state.caseSensitive; + var queryLen = query.length; + + if (queryLen === 0) { + // Do nothing the matches should be wiped out already. + return; + } + + if (!caseSensitive) { + pageContent = pageContent.toLowerCase(); + query = query.toLowerCase(); + } + + var matches = []; + + var matchIdx = -queryLen; + while (true) { + matchIdx = pageContent.indexOf(query, matchIdx + queryLen); + if (matchIdx === -1) { + break; + } + + matches.push(matchIdx); + } + this.pageMatches[pageIndex] = matches; + this.updatePage(pageIndex); + if (this.resumePageIdx === pageIndex) { + var callback = this.resumeCallback; + this.resumePageIdx = null; + this.resumeCallback = null; + callback(); + } + }, + + extractText: function() { + if (this.startedTextExtraction) { + return; + } + this.startedTextExtraction = true; + + this.pageContents = []; + for (var i = 0, ii = PDFView.pdfDocument.numPages; i < ii; i++) { + this.extractTextPromises.push(new PDFJS.Promise()); + } + + var self = this; + function extractPageText(pageIndex) { + PDFView.pages[pageIndex].getTextContent().then( + function textContentResolved(data) { + // Build the find string. + var bidiTexts = data.bidiTexts; + var str = ''; + + for (var i = 0; i < bidiTexts.length; i++) { + str += bidiTexts[i].str; + } + + // Store the pageContent as a string. + self.pageContents.push(str); + + self.extractTextPromises[pageIndex].resolve(pageIndex); + if ((pageIndex + 1) < PDFView.pages.length) + extractPageText(pageIndex + 1); + } + ); + } + extractPageText(0); + return this.extractTextPromise; + }, + + handleEvent: function(e) { + if (this.state === null || e.type !== 'findagain') { + this.dirtyMatch = true; + } + this.state = e.detail; + this.updateUIState(FindStates.FIND_PENDING); + + this.extractText(); + + clearTimeout(this.findTimeout); + if (e.type === 'find') { + // Only trigger the find action after 250ms of silence. + this.findTimeout = setTimeout(this.nextMatch.bind(this), 250); + } else { + this.nextMatch(); + } + }, + + updatePage: function(idx) { + var page = PDFView.pages[idx]; + + if (this.selected.pageIdx === idx) { + // If the page is selected, scroll the page into view, which triggers + // rendering the page, which adds the textLayer. Once the textLayer is + // build, it will scroll onto the selected match. + page.scrollIntoView(); + } + + if (page.textLayer) { + page.textLayer.updateMatches(); + } + }, + + nextMatch: function() { + var pages = PDFView.pages; + var previous = this.state.findPrevious; + var numPages = PDFView.pages.length; + + this.active = true; + + if (this.dirtyMatch) { + // Need to recalculate the matches, reset everything. + this.dirtyMatch = false; + this.selected.pageIdx = this.selected.matchIdx = -1; + this.offset.pageIdx = previous ? numPages - 1 : 0; + this.offset.matchIdx = null; + this.hadMatch = false; + this.resumeCallback = null; + this.resumePageIdx = null; + this.pageMatches = []; + var self = this; + + for (var i = 0; i < numPages; i++) { + // Wipe out any previous highlighted matches. + this.updatePage(i); + + // As soon as the text is extracted start finding the matches. + this.extractTextPromises[i].onData(function(pageIdx) { + // Use a timeout since all the pages may already be extracted and we + // want to start highlighting before finding all the matches. + setTimeout(function() { + self.calcFindMatch(pageIdx); + }); + }); + } + } + + // If there's no query there's no point in searching. + if (this.state.query === '') { + this.updateUIState(FindStates.FIND_FOUND); + return; + } + + // If we're waiting on a page, we return since we can't do anything else. + if (this.resumeCallback) { + return; + } + + var offset = this.offset; + // If there's already a matchIdx that means we are iterating through a + // page's matches. + if (offset.matchIdx !== null) { + var numPageMatches = this.pageMatches[offset.pageIdx].length; + if ((!previous && offset.matchIdx + 1 < numPageMatches) || + (previous && offset.matchIdx > 0)) { + // The simple case, we just have advance the matchIdx to select the next + // match on the page. + this.hadMatch = true; + offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1; + this.updateMatch(true); + return; + } + // We went beyond the current page's matches, so we advance to the next + // page. + this.advanceOffsetPage(previous); + } + // Start searching through the page. + this.nextPageMatch(); + }, + + nextPageMatch: function() { + if (this.resumePageIdx !== null) + console.error('There can only be one pending page.'); + + var matchesReady = function(matches) { + var offset = this.offset; + var numMatches = matches.length; + var previous = this.state.findPrevious; + if (numMatches) { + // There were matches for the page, so initialize the matchIdx. + this.hadMatch = true; + offset.matchIdx = previous ? numMatches - 1 : 0; + this.updateMatch(true); + } else { + // No matches attempt to search the next page. + this.advanceOffsetPage(previous); + if (offset.wrapped) { + offset.matchIdx = null; + if (!this.hadMatch) { + // No point in wrapping there were no matches. + this.updateMatch(false); + return; + } + } + // Search the next page. + this.nextPageMatch(); + } + }.bind(this); + + var pageIdx = this.offset.pageIdx; + var pageMatches = this.pageMatches; + if (!pageMatches[pageIdx]) { + // The matches aren't ready setup a callback so we can be notified, + // when they are ready. + this.resumeCallback = function() { + matchesReady(pageMatches[pageIdx]); + }; + this.resumePageIdx = pageIdx; + return; + } + // The matches are finished already. + matchesReady(pageMatches[pageIdx]); + }, + + advanceOffsetPage: function(previous) { + var offset = this.offset; + var numPages = this.extractTextPromises.length; + offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1; + offset.matchIdx = null; + if (offset.pageIdx >= numPages || offset.pageIdx < 0) { + offset.pageIdx = previous ? numPages - 1 : 0; + offset.wrapped = true; + return; + } + }, + + updateMatch: function(found) { + var state = FindStates.FIND_NOTFOUND; + var wrapped = this.offset.wrapped; + this.offset.wrapped = false; + if (found) { + var previousPage = this.selected.pageIdx; + this.selected.pageIdx = this.offset.pageIdx; + this.selected.matchIdx = this.offset.matchIdx; + state = wrapped ? FindStates.FIND_WRAPPED : FindStates.FIND_FOUND; + // Update the currently selected page to wipe out any selected matches. + if (previousPage !== -1 && previousPage !== this.selected.pageIdx) { + this.updatePage(previousPage); + } + } + this.updateUIState(state, this.state.findPrevious); + if (this.selected.pageIdx !== -1) { + this.updatePage(this.selected.pageIdx, true); + } + }, + + updateUIState: function(state, previous) { + if (PDFView.supportsIntegratedFind) { + FirefoxCom.request('updateFindControlState', + {result: state, findPrevious: previous}); + return; + } + PDFFindBar.updateUIState(state, previous); + } +}; + +var PDFFindBar = { + // TODO: Enable the FindBar *AFTER* the pagesPromise in the load function + // got resolved + + opened: false, + + initialize: function() { + this.bar = document.getElementById('findbar'); + this.toggleButton = document.getElementById('viewFind'); + this.findField = document.getElementById('findInput'); + this.highlightAll = document.getElementById('findHighlightAll'); + this.caseSensitive = document.getElementById('findMatchCase'); + this.findMsg = document.getElementById('findMsg'); + this.findStatusIcon = document.getElementById('findStatusIcon'); + + var self = this; + this.toggleButton.addEventListener('click', function() { + self.toggle(); + }); + + this.findField.addEventListener('input', function() { + self.dispatchEvent(''); + }); + + this.bar.addEventListener('keydown', function(evt) { + switch (evt.keyCode) { + case 13: // Enter + if (evt.target === self.findField) { + self.dispatchEvent('again', evt.shiftKey); + } + break; + case 27: // Escape + self.close(); + break; + } + }); + + document.getElementById('findPrevious').addEventListener('click', + function() { self.dispatchEvent('again', true); } + ); + + document.getElementById('findNext').addEventListener('click', function() { + self.dispatchEvent('again', false); + }); + + this.highlightAll.addEventListener('click', function() { + self.dispatchEvent('highlightallchange'); + }); + + this.caseSensitive.addEventListener('click', function() { + self.dispatchEvent('casesensitivitychange'); + }); + }, + + dispatchEvent: function(aType, aFindPrevious) { + var event = document.createEvent('CustomEvent'); + event.initCustomEvent('find' + aType, true, true, { + query: this.findField.value, + caseSensitive: this.caseSensitive.checked, + highlightAll: this.highlightAll.checked, + findPrevious: aFindPrevious + }); + return window.dispatchEvent(event); + }, + + updateUIState: function(state, previous) { + var notFound = false; + var findMsg = ''; + var status = ''; + + switch (state) { + case FindStates.FIND_FOUND: + break; + + case FindStates.FIND_PENDING: + status = 'pending'; + break; + + case FindStates.FIND_NOTFOUND: + findMsg = mozL10n.get('find_not_found', null, 'Phrase not found'); + notFound = true; + break; + + case FindStates.FIND_WRAPPED: + if (previous) { + findMsg = mozL10n.get('find_reached_top', null, + 'Reached top of document, continued from bottom'); + } else { + findMsg = mozL10n.get('find_reached_bottom', null, + 'Reached end of document, continued from top'); + } + break; + } + + if (notFound) { + this.findField.classList.add('notFound'); + } else { + this.findField.classList.remove('notFound'); + } + + this.findField.setAttribute('data-status', status); + this.findMsg.textContent = findMsg; + }, + + open: function() { + if (this.opened) return; + + this.opened = true; + this.toggleButton.classList.add('toggled'); + this.bar.classList.remove('hidden'); + this.findField.select(); + this.findField.focus(); + }, + + close: function() { + if (!this.opened) return; + + this.opened = false; + this.toggleButton.classList.remove('toggled'); + this.bar.classList.add('hidden'); + + PDFFindController.active = false; + }, + + toggle: function() { + if (this.opened) { + this.close(); + } else { + this.open(); + } + } +}; + +var PDFView = { + pages: [], + thumbnails: [], + currentScale: UNKNOWN_SCALE, + currentScaleValue: null, + initialBookmark: document.location.hash.substring(1), + startedTextExtraction: false, + pageText: [], + container: null, + thumbnailContainer: null, + initialized: false, + fellback: false, + pdfDocument: null, + sidebarOpen: false, + pageViewScroll: null, + thumbnailViewScroll: null, + isFullscreen: false, + previousScale: null, + pageRotation: 0, + mouseScrollTimeStamp: 0, + mouseScrollDelta: 0, + lastScroll: 0, + previousPageNumber: 1, + + // called once when the document is loaded + initialize: function pdfViewInitialize() { + var self = this; + var container = this.container = document.getElementById('viewerContainer'); + this.pageViewScroll = {}; + this.watchScroll(container, this.pageViewScroll, updateViewarea); + + var thumbnailContainer = this.thumbnailContainer = + document.getElementById('thumbnailView'); + this.thumbnailViewScroll = {}; + this.watchScroll(thumbnailContainer, this.thumbnailViewScroll, + this.renderHighestPriority.bind(this)); + + PDFFindBar.initialize(); + PDFFindController.initialize(); + + this.initialized = true; + container.addEventListener('scroll', function() { + self.lastScroll = Date.now(); + }, false); + }, + + // Helper function to keep track whether a div was scrolled up or down and + // then call a callback. + watchScroll: function pdfViewWatchScroll(viewAreaElement, state, callback) { + state.down = true; + state.lastY = viewAreaElement.scrollTop; + viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) { + var currentY = viewAreaElement.scrollTop; + var lastY = state.lastY; + if (currentY > lastY) + state.down = true; + else if (currentY < lastY) + state.down = false; + // else do nothing and use previous value + state.lastY = currentY; + callback(); + }, true); + }, + + setScale: function pdfViewSetScale(val, resetAutoSettings, noScroll) { + if (val == this.currentScale) + return; + + var pages = this.pages; + for (var i = 0; i < pages.length; i++) + pages[i].update(val * CSS_UNITS); + + if (!noScroll && this.currentScale != val) + this.pages[this.page - 1].scrollIntoView(); + this.currentScale = val; + + var event = document.createEvent('UIEvents'); + event.initUIEvent('scalechange', false, false, window, 0); + event.scale = val; + event.resetAutoSettings = resetAutoSettings; + window.dispatchEvent(event); + }, + + parseScale: function pdfViewParseScale(value, resetAutoSettings, noScroll) { + if ('custom' == value) + return; + + var scale = parseFloat(value); + this.currentScaleValue = value; + if (scale) { + this.setScale(scale, true, noScroll); + return; + } + + var container = this.container; + var currentPage = this.pages[this.page - 1]; + if (!currentPage) { + return; + } + + var pageWidthScale = (container.clientWidth - SCROLLBAR_PADDING) / + currentPage.width * currentPage.scale / CSS_UNITS; + var pageHeightScale = (container.clientHeight - VERTICAL_PADDING) / + currentPage.height * currentPage.scale / CSS_UNITS; + switch (value) { + case 'page-actual': + scale = 1; + break; + case 'page-width': + scale = pageWidthScale; + break; + case 'page-height': + scale = pageHeightScale; + break; + case 'page-fit': + scale = Math.min(pageWidthScale, pageHeightScale); + break; + case 'auto': + scale = Math.min(1.0, pageWidthScale); + break; + } + this.setScale(scale, resetAutoSettings, noScroll); + + selectScaleOption(value); + }, + + zoomIn: function pdfViewZoomIn() { + var newScale = (this.currentScale * DEFAULT_SCALE_DELTA).toFixed(2); + newScale = Math.min(MAX_SCALE, newScale); + this.parseScale(newScale, true); + }, + + zoomOut: function pdfViewZoomOut() { + var newScale = (this.currentScale / DEFAULT_SCALE_DELTA).toFixed(2); + newScale = Math.max(MIN_SCALE, newScale); + this.parseScale(newScale, true); + }, + + set page(val) { + var pages = this.pages; + var input = document.getElementById('pageNumber'); + var event = document.createEvent('UIEvents'); + event.initUIEvent('pagechange', false, false, window, 0); + + if (!(0 < val && val <= pages.length)) { + this.previousPageNumber = val; + event.pageNumber = this.page; + window.dispatchEvent(event); + return; + } + + pages[val - 1].updateStats(); + this.previousPageNumber = currentPageNumber; + currentPageNumber = val; + event.pageNumber = val; + window.dispatchEvent(event); + + // checking if the this.page was called from the updateViewarea function: + // avoiding the creation of two "set page" method (internal and public) + if (updateViewarea.inProgress) + return; + + // Avoid scrolling the first page during loading + if (this.loading && val == 1) + return; + + pages[val - 1].scrollIntoView(); + }, + + get page() { + return currentPageNumber; + }, + + get supportsPrinting() { + var canvas = document.createElement('canvas'); + var value = 'mozPrintCallback' in canvas; + // shadow + Object.defineProperty(this, 'supportsPrinting', { value: value, + enumerable: true, + configurable: true, + writable: false }); + return value; + }, + + get supportsFullscreen() { + var doc = document.documentElement; + var support = doc.requestFullscreen || doc.mozRequestFullScreen || + doc.webkitRequestFullScreen; + + // Disable fullscreen button if we're in an iframe + if (!!window.frameElement) + support = false; + + Object.defineProperty(this, 'supportsFullScreen', { value: support, + enumerable: true, + configurable: true, + writable: false }); + return support; + }, + + get supportsIntegratedFind() { + var support = false; + Object.defineProperty(this, 'supportsIntegratedFind', { value: support, + enumerable: true, + configurable: true, + writable: false }); + return support; + }, + + get supportsDocumentFonts() { + var support = true; + Object.defineProperty(this, 'supportsDocumentFonts', { value: support, + enumerable: true, + configurable: true, + writable: false }); + return support; + }, + + get isHorizontalScrollbarEnabled() { + var div = document.getElementById('viewerContainer'); + return div.scrollWidth > div.clientWidth; + }, + + initPassiveLoading: function pdfViewInitPassiveLoading() { + if (!PDFView.loadingBar) { + PDFView.loadingBar = new ProgressBar('#loadingBar', {}); + } + + window.addEventListener('message', function window_message(e) { + var args = e.data; + + if (typeof args !== 'object' || !('pdfjsLoadAction' in args)) + return; + switch (args.pdfjsLoadAction) { + case 'progress': + PDFView.progress(args.loaded / args.total); + break; + case 'complete': + if (!args.data) { + PDFView.error(mozL10n.get('loading_error', null, + 'An error occurred while loading the PDF.'), e); + break; + } + PDFView.open(args.data, 0); + break; + } + }); + FirefoxCom.requestSync('initPassiveLoading', null); + }, + + setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) { + this.url = url; + try { + this.setTitle(decodeURIComponent(getFileName(url)) || url); + } catch (e) { + // decodeURIComponent may throw URIError, + // fall back to using the unprocessed url in that case + this.setTitle(url); + } + }, + + setTitle: function pdfViewSetTitle(title) { + document.title = title; + }, + + open: function pdfViewOpen(url, scale, password) { + var parameters = {password: password}; + if (typeof url === 'string') { // URL + this.setTitleUsingUrl(url); + parameters.url = url; + } else if (url && 'byteLength' in url) { // ArrayBuffer + parameters.data = url; + } + + if (!PDFView.loadingBar) { + PDFView.loadingBar = new ProgressBar('#loadingBar', {}); + } + + this.pdfDocument = null; + var self = this; + self.loading = true; + PDFJS.getDocument(parameters).then( + function getDocumentCallback(pdfDocument) { + self.load(pdfDocument, scale); + self.loading = false; + }, + function getDocumentError(message, exception) { + if (exception && exception.name === 'PasswordException') { + if (exception.code === 'needpassword') { + var promptString = mozL10n.get('request_password', null, + 'PDF is protected by a password:'); + password = prompt(promptString); + if (password && password.length > 0) { + return PDFView.open(url, scale, password); + } + } + } + + var loadingErrorMessage = mozL10n.get('loading_error', null, + 'An error occurred while loading the PDF.'); + + if (exception && exception.name === 'InvalidPDFException') { + // change error message also for other builds + var loadingErrorMessage = mozL10n.get('invalid_file_error', null, + 'Invalid or corrupted PDF file.'); + } + + if (exception && exception.name === 'MissingPDFException') { + // special message for missing PDF's + var loadingErrorMessage = mozL10n.get('missing_file_error', null, + 'Missing PDF file.'); + + } + + var loadingIndicator = document.getElementById('loading'); + loadingIndicator.textContent = mozL10n.get('loading_error_indicator', + null, 'Error'); + var moreInfo = { + message: message + }; + self.error(loadingErrorMessage, moreInfo); + self.loading = false; + }, + function getDocumentProgress(progressData) { + self.progress(progressData.loaded / progressData.total); + } + ); + }, + + download: function pdfViewDownload() { + function noData() { + FirefoxCom.request('download', { originalUrl: url }); + } + var url = this.url.split('#')[0]; + url += '#pdfjs.action=download'; + window.open(url, '_parent'); + }, + + fallback: function pdfViewFallback() { + return; + }, + + navigateTo: function pdfViewNavigateTo(dest) { + if (typeof dest === 'string') + dest = this.destinations[dest]; + if (!(dest instanceof Array)) + return; // invalid destination + // dest array looks like that: + var destRef = dest[0]; + var pageNumber = destRef instanceof Object ? + this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : (destRef + 1); + if (pageNumber > this.pages.length) + pageNumber = this.pages.length; + if (pageNumber) { + this.page = pageNumber; + var currentPage = this.pages[pageNumber - 1]; + currentPage.scrollIntoView(dest); + } + }, + + getDestinationHash: function pdfViewGetDestinationHash(dest) { + if (typeof dest === 'string') + return PDFView.getAnchorUrl('#' + escape(dest)); + if (dest instanceof Array) { + var destRef = dest[0]; // see navigateTo method for dest format + var pageNumber = destRef instanceof Object ? + this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : + (destRef + 1); + if (pageNumber) { + var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber); + var destKind = dest[1]; + if (typeof destKind === 'object' && 'name' in destKind && + destKind.name == 'XYZ') { + var scale = (dest[4] || this.currentScale); + pdfOpenParams += '&zoom=' + (scale * 100); + if (dest[2] || dest[3]) { + pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0); + } + } + return pdfOpenParams; + } + } + return ''; + }, + + /** + * For the firefox extension we prefix the full url on anchor links so they + * don't come up as resource:// urls and so open in new tab/window works. + * @param {String} anchor The anchor hash include the #. + */ + getAnchorUrl: function getAnchorUrl(anchor) { + return anchor; + }, + + /** + * Returns scale factor for the canvas. It makes sense for the HiDPI displays. + * @return {Object} The object with horizontal (sx) and vertical (sy) + scales. The scaled property is set to false if scaling is + not required, true otherwise. + */ + getOutputScale: function pdfViewGetOutputDPI() { + var pixelRatio = 'devicePixelRatio' in window ? window.devicePixelRatio : 1; + return { + sx: pixelRatio, + sy: pixelRatio, + scaled: pixelRatio != 1 + }; + }, + + /** + * Show the error box. + * @param {String} message A message that is human readable. + * @param {Object} moreInfo (optional) Further information about the error + * that is more technical. Should have a 'message' + * and optionally a 'stack' property. + */ + error: function pdfViewError(message, moreInfo) { + var moreInfoText = mozL10n.get('error_version_info', + {version: PDFJS.version || '?', build: PDFJS.build || '?'}, + 'PDF.js v{{version}} (build: {{build}})') + '\n'; + if (moreInfo) { + moreInfoText += + mozL10n.get('error_message', {message: moreInfo.message}, + 'Message: {{message}}'); + if (moreInfo.stack) { + moreInfoText += '\n' + + mozL10n.get('error_stack', {stack: moreInfo.stack}, + 'Stack: {{stack}}'); + } else { + if (moreInfo.filename) { + moreInfoText += '\n' + + mozL10n.get('error_file', {file: moreInfo.filename}, + 'File: {{file}}'); + } + if (moreInfo.lineNumber) { + moreInfoText += '\n' + + mozL10n.get('error_line', {line: moreInfo.lineNumber}, + 'Line: {{line}}'); + } + } + } + + var loadingBox = document.getElementById('loadingBox'); + loadingBox.setAttribute('hidden', 'true'); + + var errorWrapper = document.getElementById('errorWrapper'); + errorWrapper.removeAttribute('hidden'); + + var errorMessage = document.getElementById('errorMessage'); + errorMessage.textContent = message; + + var closeButton = document.getElementById('errorClose'); + closeButton.onclick = function() { + errorWrapper.setAttribute('hidden', 'true'); + }; + + var errorMoreInfo = document.getElementById('errorMoreInfo'); + var moreInfoButton = document.getElementById('errorShowMore'); + var lessInfoButton = document.getElementById('errorShowLess'); + moreInfoButton.onclick = function() { + errorMoreInfo.removeAttribute('hidden'); + moreInfoButton.setAttribute('hidden', 'true'); + lessInfoButton.removeAttribute('hidden'); + }; + lessInfoButton.onclick = function() { + errorMoreInfo.setAttribute('hidden', 'true'); + moreInfoButton.removeAttribute('hidden'); + lessInfoButton.setAttribute('hidden', 'true'); + }; + moreInfoButton.removeAttribute('hidden'); + lessInfoButton.setAttribute('hidden', 'true'); + errorMoreInfo.value = moreInfoText; + + errorMoreInfo.rows = moreInfoText.split('\n').length - 1; + }, + + progress: function pdfViewProgress(level) { + var percent = Math.round(level * 100); + PDFView.loadingBar.percent = percent; + }, + + load: function pdfViewLoad(pdfDocument, scale) { + function bindOnAfterDraw(pageView, thumbnailView) { + // when page is painted, using the image as thumbnail base + pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() { + thumbnailView.setImage(pageView.canvas); + }; + } + + this.pdfDocument = pdfDocument; + + var errorWrapper = document.getElementById('errorWrapper'); + errorWrapper.setAttribute('hidden', 'true'); + + var loadingBox = document.getElementById('loadingBox'); + loadingBox.setAttribute('hidden', 'true'); + var loadingIndicator = document.getElementById('loading'); + loadingIndicator.textContent = ''; + + var thumbsView = document.getElementById('thumbnailView'); + thumbsView.parentNode.scrollTop = 0; + + while (thumbsView.hasChildNodes()) + thumbsView.removeChild(thumbsView.lastChild); + + if ('_loadingInterval' in thumbsView) + clearInterval(thumbsView._loadingInterval); + + var container = document.getElementById('viewer'); + while (container.hasChildNodes()) + container.removeChild(container.lastChild); + + var pagesCount = pdfDocument.numPages; + var id = pdfDocument.fingerprint; + document.getElementById('numPages').textContent = + mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}'); + document.getElementById('pageNumber').max = pagesCount; + + PDFView.documentFingerprint = id; + var store = PDFView.store = new Settings(id); + var storePromise = store.initializedPromise; + + this.pageRotation = 0; + + var pages = this.pages = []; + this.pageText = []; + this.startedTextExtraction = false; + var pagesRefMap = {}; + var thumbnails = this.thumbnails = []; + var pagePromises = []; + for (var i = 1; i <= pagesCount; i++) + pagePromises.push(pdfDocument.getPage(i)); + var self = this; + var pagesPromise = PDFJS.Promise.all(pagePromises); + pagesPromise.then(function(promisedPages) { + for (var i = 1; i <= pagesCount; i++) { + var page = promisedPages[i - 1]; + var pageView = new PageView(container, page, i, scale, + page.stats, self.navigateTo.bind(self)); + var thumbnailView = new ThumbnailView(thumbsView, page, i); + bindOnAfterDraw(pageView, thumbnailView); + + pages.push(pageView); + thumbnails.push(thumbnailView); + var pageRef = page.ref; + pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i; + } + + self.pagesRefMap = pagesRefMap; + }); + + var destinationsPromise = pdfDocument.getDestinations(); + destinationsPromise.then(function(destinations) { + self.destinations = destinations; + }); + + // outline and initial view depends on destinations and pagesRefMap + var promises = [pagesPromise, destinationsPromise, storePromise, + PDFView.animationStartedPromise]; + PDFJS.Promise.all(promises).then(function() { + pdfDocument.getOutline().then(function(outline) { + self.outline = new DocumentOutlineView(outline); + }); + + var storedHash = null; + if (store.get('exists', false)) { + var page = store.get('page', '1'); + var zoom = store.get('zoom', PDFView.currentScale); + var left = store.get('scrollLeft', '0'); + var top = store.get('scrollTop', '0'); + + storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top; + } + + self.setInitialView(storedHash, scale); + }); + + pdfDocument.getMetadata().then(function(data) { + var info = data.info, metadata = data.metadata; + self.documentInfo = info; + self.metadata = metadata; + + // Provides some basic debug information + console.log('PDF ' + pdfDocument.fingerprint + ' [' + + info.PDFFormatVersion + ' ' + (info.Producer || '-') + + ' / ' + (info.Creator || '-') + ']' + + (PDFJS.version ? ' (PDF.js: ' + PDFJS.version + ')' : '')); + + var pdfTitle; + if (metadata) { + if (metadata.has('dc:title')) + pdfTitle = metadata.get('dc:title'); + } + + if (!pdfTitle && info && info['Title']) + pdfTitle = info['Title']; + + if (pdfTitle) + self.setTitle(pdfTitle + ' - ' + document.title); + + if (info.IsAcroFormPresent) { + // AcroForm/XFA was found + PDFView.fallback(); + } + }); + }, + + setInitialView: function pdfViewSetInitialView(storedHash, scale) { + // Reset the current scale, as otherwise the page's scale might not get + // updated if the zoom level stayed the same. + this.currentScale = 0; + this.currentScaleValue = null; + if (this.initialBookmark) { + this.setHash(this.initialBookmark); + this.initialBookmark = null; + } + else if (storedHash) + this.setHash(storedHash); + else if (scale) { + this.parseScale(scale, true); + this.page = 1; + } + + if (PDFView.currentScale === UNKNOWN_SCALE) { + // Scale was not initialized: invalid bookmark or scale was not specified. + // Setting the default one. + this.parseScale(DEFAULT_SCALE, true); + } + }, + + renderHighestPriority: function pdfViewRenderHighestPriority() { + // Pages have a higher priority than thumbnails, so check them first. + var visiblePages = this.getVisiblePages(); + var pageView = this.getHighestPriority(visiblePages, this.pages, + this.pageViewScroll.down); + if (pageView) { + this.renderView(pageView, 'page'); + return; + } + // No pages needed rendering so check thumbnails. + if (this.sidebarOpen) { + var visibleThumbs = this.getVisibleThumbs(); + var thumbView = this.getHighestPriority(visibleThumbs, + this.thumbnails, + this.thumbnailViewScroll.down); + if (thumbView) + this.renderView(thumbView, 'thumbnail'); + } + }, + + getHighestPriority: function pdfViewGetHighestPriority(visible, views, + scrolledDown) { + // The state has changed figure out which page has the highest priority to + // render next (if any). + // Priority: + // 1 visible pages + // 2 if last scrolled down page after the visible pages + // 2 if last scrolled up page before the visible pages + var visibleViews = visible.views; + + var numVisible = visibleViews.length; + if (numVisible === 0) { + return false; + } + for (var i = 0; i < numVisible; ++i) { + var view = visibleViews[i].view; + if (!this.isViewFinished(view)) + return view; + } + + // All the visible views have rendered, try to render next/previous pages. + if (scrolledDown) { + var nextPageIndex = visible.last.id; + // ID's start at 1 so no need to add 1. + if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) + return views[nextPageIndex]; + } else { + var previousPageIndex = visible.first.id - 2; + if (views[previousPageIndex] && + !this.isViewFinished(views[previousPageIndex])) + return views[previousPageIndex]; + } + // Everything that needs to be rendered has been. + return false; + }, + + isViewFinished: function pdfViewNeedsRendering(view) { + return view.renderingState === RenderingStates.FINISHED; + }, + + // Render a page or thumbnail view. This calls the appropriate function based + // on the views state. If the view is already rendered it will return false. + renderView: function pdfViewRender(view, type) { + var state = view.renderingState; + switch (state) { + case RenderingStates.FINISHED: + return false; + case RenderingStates.PAUSED: + PDFView.highestPriorityPage = type + view.id; + view.resume(); + break; + case RenderingStates.RUNNING: + PDFView.highestPriorityPage = type + view.id; + break; + case RenderingStates.INITIAL: + PDFView.highestPriorityPage = type + view.id; + view.draw(this.renderHighestPriority.bind(this)); + break; + } + return true; + }, + + setHash: function pdfViewSetHash(hash) { + if (!hash) + return; + + if (hash.indexOf('=') >= 0) { + var params = PDFView.parseQueryString(hash); + // borrowing syntax from "Parameters for Opening PDF Files" + if ('nameddest' in params) { + PDFView.navigateTo(params.nameddest); + return; + } + if ('page' in params) { + var pageNumber = (params.page | 0) || 1; + if ('zoom' in params) { + var zoomArgs = params.zoom.split(','); // scale,left,top + // building destination array + + // If the zoom value, it has to get divided by 100. If it is a string, + // it should stay as it is. + var zoomArg = zoomArgs[0]; + var zoomArgNumber = parseFloat(zoomArg); + if (zoomArgNumber) + zoomArg = zoomArgNumber / 100; + + var dest = [null, {name: 'XYZ'}, (zoomArgs[1] | 0), + (zoomArgs[2] | 0), zoomArg]; + var currentPage = this.pages[pageNumber - 1]; + currentPage.scrollIntoView(dest); + } else { + this.page = pageNumber; // simple page + } + } + } else if (/^\d+$/.test(hash)) // page number + this.page = hash; + else // named destination + PDFView.navigateTo(unescape(hash)); + }, + + switchSidebarView: function pdfViewSwitchSidebarView(view) { + var thumbsView = document.getElementById('thumbnailView'); + var outlineView = document.getElementById('outlineView'); + + var thumbsButton = document.getElementById('viewThumbnail'); + var outlineButton = document.getElementById('viewOutline'); + + switch (view) { + case 'thumbs': + var wasOutlineViewVisible = thumbsView.classList.contains('hidden'); + + thumbsButton.classList.add('toggled'); + outlineButton.classList.remove('toggled'); + thumbsView.classList.remove('hidden'); + outlineView.classList.add('hidden'); + + PDFView.renderHighestPriority(); + + if (wasOutlineViewVisible) { + // Ensure that the thumbnail of the current page is visible + // when switching from the outline view. + scrollIntoView(document.getElementById('thumbnailContainer' + + this.page)); + } + break; + + case 'outline': + thumbsButton.classList.remove('toggled'); + outlineButton.classList.add('toggled'); + thumbsView.classList.add('hidden'); + outlineView.classList.remove('hidden'); + + if (outlineButton.getAttribute('disabled')) + return; + break; + } + }, + + getVisiblePages: function pdfViewGetVisiblePages() { + return this.getVisibleElements(this.container, + this.pages, true); + }, + + getVisibleThumbs: function pdfViewGetVisibleThumbs() { + return this.getVisibleElements(this.thumbnailContainer, + this.thumbnails); + }, + + // Generic helper to find out what elements are visible within a scroll pane. + getVisibleElements: function pdfViewGetVisibleElements( + scrollEl, views, sortByVisibility) { + var currentHeight = 0, view; + var top = scrollEl.scrollTop; + + for (var i = 1, ii = views.length; i <= ii; ++i) { + view = views[i - 1]; + currentHeight = view.el.offsetTop; + if (currentHeight + view.el.clientHeight > top) + break; + currentHeight += view.el.clientHeight; + } + + var visible = []; + + // Algorithm broken in fullscreen mode + if (this.isFullscreen) { + var currentPage = this.pages[this.page - 1]; + visible.push({ + id: currentPage.id, + view: currentPage + }); + + return { first: currentPage, last: currentPage, views: visible}; + } + + var bottom = top + scrollEl.clientHeight; + var nextHeight, hidden, percent, viewHeight; + for (; i <= ii && currentHeight < bottom; ++i) { + view = views[i - 1]; + viewHeight = view.el.clientHeight; + currentHeight = view.el.offsetTop; + nextHeight = currentHeight + viewHeight; + hidden = Math.max(0, top - currentHeight) + + Math.max(0, nextHeight - bottom); + percent = Math.floor((viewHeight - hidden) * 100.0 / viewHeight); + visible.push({ id: view.id, y: currentHeight, + view: view, percent: percent }); + currentHeight = nextHeight; + } + + var first = visible[0]; + var last = visible[visible.length - 1]; + + if (sortByVisibility) { + visible.sort(function(a, b) { + var pc = a.percent - b.percent; + if (Math.abs(pc) > 0.001) + return -pc; + + return a.id - b.id; // ensure stability + }); + } + + return {first: first, last: last, views: visible}; + }, + + // Helper function to parse query string (e.g. ?param1=value&parm2=...). + parseQueryString: function pdfViewParseQueryString(query) { + var parts = query.split('&'); + var params = {}; + for (var i = 0, ii = parts.length; i < parts.length; ++i) { + var param = parts[i].split('='); + var key = param[0]; + var value = param.length > 1 ? param[1] : null; + params[unescape(key)] = unescape(value); + } + return params; + }, + + beforePrint: function pdfViewSetupBeforePrint() { + if (!this.supportsPrinting) { + var printMessage = mozL10n.get('printing_not_supported', null, + 'Warning: Printing is not fully supported by this browser.'); + this.error(printMessage); + return; + } + var body = document.querySelector('body'); + body.setAttribute('data-mozPrintCallback', true); + for (var i = 0, ii = this.pages.length; i < ii; ++i) { + this.pages[i].beforePrint(); + } + }, + + afterPrint: function pdfViewSetupAfterPrint() { + var div = document.getElementById('printContainer'); + while (div.hasChildNodes()) + div.removeChild(div.lastChild); + }, + + fullscreen: function pdfViewFullscreen() { + var isFullscreen = document.fullscreenElement || document.mozFullScreen || + document.webkitIsFullScreen; + + if (isFullscreen) { + return false; + } + + var wrapper = document.getElementById('viewerContainer'); + if (document.documentElement.requestFullscreen) { + wrapper.requestFullscreen(); + } else if (document.documentElement.mozRequestFullScreen) { + wrapper.mozRequestFullScreen(); + } else if (document.documentElement.webkitRequestFullScreen) { + wrapper.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); + } else { + return false; + } + + this.isFullscreen = true; + var currentPage = this.pages[this.page - 1]; + this.previousScale = this.currentScaleValue; + this.parseScale('page-fit', true); + + // Wait for fullscreen to take effect + setTimeout(function() { + currentPage.scrollIntoView(); + }, 0); + + this.showPresentationControls(); + return true; + }, + + exitFullscreen: function pdfViewExitFullscreen() { + this.isFullscreen = false; + this.parseScale(this.previousScale); + this.page = this.page; + this.clearMouseScrollState(); + this.hidePresentationControls(); + }, + + showPresentationControls: function pdfViewShowPresentationControls() { + var DELAY_BEFORE_HIDING_CONTROLS = 3000; + var wrapper = document.getElementById('viewerContainer'); + if (this.presentationControlsTimeout) { + clearTimeout(this.presentationControlsTimeout); + } else { + wrapper.classList.add('presentationControls'); + } + this.presentationControlsTimeout = setTimeout(function hideControls() { + wrapper.classList.remove('presentationControls'); + delete PDFView.presentationControlsTimeout; + }, DELAY_BEFORE_HIDING_CONTROLS); + }, + + hidePresentationControls: function pdfViewShowPresentationControls() { + if (!this.presentationControlsTimeout) { + return; + } + clearTimeout(this.presentationControlsTimeout); + delete this.presentationControlsTimeout; + + var wrapper = document.getElementById('viewerContainer'); + wrapper.classList.remove('presentationControls'); + }, + + rotatePages: function pdfViewPageRotation(delta) { + + this.pageRotation = (this.pageRotation + 360 + delta) % 360; + + for (var i = 0, l = this.pages.length; i < l; i++) { + var page = this.pages[i]; + page.update(page.scale, this.pageRotation); + } + + for (var i = 0, l = this.thumbnails.length; i < l; i++) { + var thumb = this.thumbnails[i]; + thumb.updateRotation(this.pageRotation); + } + + var currentPage = this.pages[this.page - 1]; + + this.parseScale(this.currentScaleValue, true); + + this.renderHighestPriority(); + + // Wait for fullscreen to take effect + setTimeout(function() { + currentPage.scrollIntoView(); + }, 0); + }, + + /** + * This function flips the page in presentation mode if the user scrolls up + * or down with large enough motion and prevents page flipping too often. + * + * @this {PDFView} + * @param {number} mouseScrollDelta The delta value from the mouse event. + */ + mouseScroll: function pdfViewMouseScroll(mouseScrollDelta) { + var MOUSE_SCROLL_COOLDOWN_TIME = 50; + + var currentTime = (new Date()).getTime(); + var storedTime = this.mouseScrollTimeStamp; + + // In case one page has already been flipped there is a cooldown time + // which has to expire before next page can be scrolled on to. + if (currentTime > storedTime && + currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) + return; + + // In case the user decides to scroll to the opposite direction than before + // clear the accumulated delta. + if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) || + (this.mouseScrollDelta < 0 && mouseScrollDelta > 0)) + this.clearMouseScrollState(); + + this.mouseScrollDelta += mouseScrollDelta; + + var PAGE_FLIP_THRESHOLD = 120; + if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) { + + var PageFlipDirection = { + UP: -1, + DOWN: 1 + }; + + // In fullscreen mode scroll one page at a time. + var pageFlipDirection = (this.mouseScrollDelta > 0) ? + PageFlipDirection.UP : + PageFlipDirection.DOWN; + this.clearMouseScrollState(); + var currentPage = this.page; + + // In case we are already on the first or the last page there is no need + // to do anything. + if ((currentPage == 1 && pageFlipDirection == PageFlipDirection.UP) || + (currentPage == this.pages.length && + pageFlipDirection == PageFlipDirection.DOWN)) + return; + + this.page += pageFlipDirection; + this.mouseScrollTimeStamp = currentTime; + } + }, + + /** + * This function clears the member attributes used with mouse scrolling in + * presentation mode. + * + * @this {PDFView} + */ + clearMouseScrollState: function pdfViewClearMouseScrollState() { + this.mouseScrollTimeStamp = 0; + this.mouseScrollDelta = 0; + } +}; + +var PageView = function pageView(container, pdfPage, id, scale, + stats, navigateTo) { + this.id = id; + this.pdfPage = pdfPage; + + this.rotation = 0; + this.scale = scale || 1.0; + this.viewport = this.pdfPage.getViewport(this.scale, this.pdfPage.rotate); + + this.renderingState = RenderingStates.INITIAL; + this.resume = null; + + this.textContent = null; + this.textLayer = null; + + var anchor = document.createElement('a'); + anchor.name = '' + this.id; + + var div = this.el = document.createElement('div'); + div.id = 'pageContainer' + this.id; + div.className = 'page'; + div.style.width = Math.floor(this.viewport.width) + 'px'; + div.style.height = Math.floor(this.viewport.height) + 'px'; + + container.appendChild(anchor); + container.appendChild(div); + + this.destroy = function pageViewDestroy() { + this.update(); + this.pdfPage.destroy(); + }; + + this.update = function pageViewUpdate(scale, rotation) { + this.renderingState = RenderingStates.INITIAL; + this.resume = null; + + if (typeof rotation !== 'undefined') { + this.rotation = rotation; + } + + this.scale = scale || this.scale; + + var totalRotation = (this.rotation + this.pdfPage.rotate) % 360; + var viewport = this.pdfPage.getViewport(this.scale, totalRotation); + + this.viewport = viewport; + div.style.width = Math.floor(viewport.width) + 'px'; + div.style.height = Math.floor(viewport.height) + 'px'; + + while (div.hasChildNodes()) + div.removeChild(div.lastChild); + div.removeAttribute('data-loaded'); + + delete this.canvas; + + this.loadingIconDiv = document.createElement('div'); + this.loadingIconDiv.className = 'loadingIcon'; + div.appendChild(this.loadingIconDiv); + }; + + Object.defineProperty(this, 'width', { + get: function PageView_getWidth() { + return this.viewport.width; + }, + enumerable: true + }); + + Object.defineProperty(this, 'height', { + get: function PageView_getHeight() { + return this.viewport.height; + }, + enumerable: true + }); + + function setupAnnotations(pdfPage, viewport) { + function bindLink(link, dest) { + link.href = PDFView.getDestinationHash(dest); + link.onclick = function pageViewSetupLinksOnclick() { + if (dest) + PDFView.navigateTo(dest); + return false; + }; + } + function createElementWithStyle(tagName, item, rect) { + if (!rect) { + rect = viewport.convertToViewportRectangle(item.rect); + rect = PDFJS.Util.normalizeRect(rect); + } + var element = document.createElement(tagName); + element.style.left = Math.floor(rect[0]) + 'px'; + element.style.top = Math.floor(rect[1]) + 'px'; + element.style.width = Math.ceil(rect[2] - rect[0]) + 'px'; + element.style.height = Math.ceil(rect[3] - rect[1]) + 'px'; + return element; + } + function createTextAnnotation(item) { + var container = document.createElement('section'); + container.className = 'annotText'; + + var rect = viewport.convertToViewportRectangle(item.rect); + rect = PDFJS.Util.normalizeRect(rect); + // sanity check because of OOo-generated PDFs + if ((rect[3] - rect[1]) < ANNOT_MIN_SIZE) { + rect[3] = rect[1] + ANNOT_MIN_SIZE; + } + if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) { + rect[2] = rect[0] + (rect[3] - rect[1]); // make it square + } + var image = createElementWithStyle('img', item, rect); + var iconName = item.name; + image.src = IMAGE_DIR + 'annotation-' + + iconName.toLowerCase() + '.svg'; + image.alt = mozL10n.get('text_annotation_type', {type: iconName}, + '[{{type}} Annotation]'); + var content = document.createElement('div'); + content.setAttribute('hidden', true); + var title = document.createElement('h1'); + var text = document.createElement('p'); + content.style.left = Math.floor(rect[2]) + 'px'; + content.style.top = Math.floor(rect[1]) + 'px'; + title.textContent = item.title; + + if (!item.content && !item.title) { + content.setAttribute('hidden', true); + } else { + var e = document.createElement('span'); + var lines = item.content.split(/(?:\r\n?|\n)/); + for (var i = 0, ii = lines.length; i < ii; ++i) { + var line = lines[i]; + e.appendChild(document.createTextNode(line)); + if (i < (ii - 1)) + e.appendChild(document.createElement('br')); + } + text.appendChild(e); + image.addEventListener('mouseover', function annotationImageOver() { + content.removeAttribute('hidden'); + }, false); + + image.addEventListener('mouseout', function annotationImageOut() { + content.setAttribute('hidden', true); + }, false); + } + + content.appendChild(title); + content.appendChild(text); + container.appendChild(image); + container.appendChild(content); + + return container; + } + + pdfPage.getAnnotations().then(function(items) { + for (var i = 0; i < items.length; i++) { + var item = items[i]; + switch (item.type) { + case 'Link': + var link = createElementWithStyle('a', item); + link.href = item.url || ''; + if (!item.url) + bindLink(link, ('dest' in item) ? item.dest : null); + div.appendChild(link); + break; + case 'Text': + var textAnnotation = createTextAnnotation(item); + if (textAnnotation) + div.appendChild(textAnnotation); + break; + } + } + }); + } + + this.getPagePoint = function pageViewGetPagePoint(x, y) { + return this.viewport.convertToPdfPoint(x, y); + }; + + this.scrollIntoView = function pageViewScrollIntoView(dest) { + if (!dest) { + scrollIntoView(div); + return; + } + + var x = 0, y = 0; + var width = 0, height = 0, widthScale, heightScale; + var scale = 0; + switch (dest[1].name) { + case 'XYZ': + x = dest[2]; + y = dest[3]; + scale = dest[4]; + break; + case 'Fit': + case 'FitB': + scale = 'page-fit'; + break; + case 'FitH': + case 'FitBH': + y = dest[2]; + scale = 'page-width'; + break; + case 'FitV': + case 'FitBV': + x = dest[2]; + scale = 'page-height'; + break; + case 'FitR': + x = dest[2]; + y = dest[3]; + width = dest[4] - x; + height = dest[5] - y; + widthScale = (this.container.clientWidth - SCROLLBAR_PADDING) / + width / CSS_UNITS; + heightScale = (this.container.clientHeight - SCROLLBAR_PADDING) / + height / CSS_UNITS; + scale = Math.min(widthScale, heightScale); + break; + default: + return; + } + + if (scale && scale !== PDFView.currentScale) + PDFView.parseScale(scale, true, true); + else if (PDFView.currentScale === UNKNOWN_SCALE) + PDFView.parseScale(DEFAULT_SCALE, true, true); + + var boundingRect = [ + this.viewport.convertToViewportPoint(x, y), + this.viewport.convertToViewportPoint(x + width, y + height) + ]; + setTimeout(function pageViewScrollIntoViewRelayout() { + // letting page to re-layout before scrolling + var scale = PDFView.currentScale; + var x = Math.min(boundingRect[0][0], boundingRect[1][0]); + var y = Math.min(boundingRect[0][1], boundingRect[1][1]); + var width = Math.abs(boundingRect[0][0] - boundingRect[1][0]); + var height = Math.abs(boundingRect[0][1] - boundingRect[1][1]); + + scrollIntoView(div, {left: x, top: y, width: width, height: height}); + }, 0); + }; + + this.getTextContent = function pageviewGetTextContent() { + if (!this.textContent) { + this.textContent = this.pdfPage.getTextContent(); + } + return this.textContent; + }; + + this.draw = function pageviewDraw(callback) { + if (this.renderingState !== RenderingStates.INITIAL) { + console.error('Must be in new state before drawing'); + } + + this.renderingState = RenderingStates.RUNNING; + + var canvas = document.createElement('canvas'); + canvas.id = 'page' + this.id; + canvas.mozOpaque = true; + div.appendChild(canvas); + this.canvas = canvas; + + var textLayerDiv = null; + if (!PDFJS.disableTextLayer) { + textLayerDiv = document.createElement('div'); + textLayerDiv.className = 'textLayer'; + div.appendChild(textLayerDiv); + } + var textLayer = this.textLayer = + textLayerDiv ? new TextLayerBuilder(textLayerDiv, this.id - 1) : null; + + var scale = this.scale, viewport = this.viewport; + var outputScale = PDFView.getOutputScale(); + canvas.width = Math.floor(viewport.width) * outputScale.sx; + canvas.height = Math.floor(viewport.height) * outputScale.sy; + + if (outputScale.scaled) { + var cssScale = 'scale(' + (1 / outputScale.sx) + ', ' + + (1 / outputScale.sy) + ')'; + CustomStyle.setProp('transform' , canvas, cssScale); + CustomStyle.setProp('transformOrigin' , canvas, '0% 0%'); + if (textLayerDiv) { + CustomStyle.setProp('transform' , textLayerDiv, cssScale); + CustomStyle.setProp('transformOrigin' , textLayerDiv, '0% 0%'); + } + } + + var ctx = canvas.getContext('2d'); + // TODO(mack): use data attributes to store these + ctx._scaleX = outputScale.sx; + ctx._scaleY = outputScale.sy; + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + if (outputScale.scaled) { + ctx.scale(outputScale.sx, outputScale.sy); + } + + // Rendering area + + var self = this; + var renderingWasReset = false; + function pageViewDrawCallback(error) { + if (renderingWasReset) { + return; + } + + self.renderingState = RenderingStates.FINISHED; + + if (self.loadingIconDiv) { + div.removeChild(self.loadingIconDiv); + delete self.loadingIconDiv; + } + + if (error) { + PDFView.error(mozL10n.get('rendering_error', null, + 'An error occurred while rendering the page.'), error); + } + + self.stats = pdfPage.stats; + self.updateStats(); + if (self.onAfterDraw) + self.onAfterDraw(); + + cache.push(self); + callback(); + } + + var renderContext = { + canvasContext: ctx, + viewport: this.viewport, + textLayer: textLayer, + continueCallback: function pdfViewcContinueCallback(cont) { + if (self.renderingState === RenderingStates.INITIAL) { + // The page update() was called, we just need to abort any rendering. + renderingWasReset = true; + return; + } + + if (PDFView.highestPriorityPage !== 'page' + self.id) { + self.renderingState = RenderingStates.PAUSED; + self.resume = function resumeCallback() { + self.renderingState = RenderingStates.RUNNING; + cont(); + }; + return; + } + cont(); + } + }; + this.pdfPage.render(renderContext).then( + function pdfPageRenderCallback() { + pageViewDrawCallback(null); + }, + function pdfPageRenderError(error) { + pageViewDrawCallback(error); + } + ); + + if (textLayer) { + this.getTextContent().then( + function textContentResolved(textContent) { + textLayer.setTextContent(textContent); + } + ); + } + + setupAnnotations(this.pdfPage, this.viewport); + div.setAttribute('data-loaded', true); + }; + + this.beforePrint = function pageViewBeforePrint() { + var pdfPage = this.pdfPage; + var viewport = pdfPage.getViewport(1); + // Use the same hack we use for high dpi displays for printing to get better + // output until bug 811002 is fixed in FF. + var PRINT_OUTPUT_SCALE = 2; + var canvas = this.canvas = document.createElement('canvas'); + canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE; + canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE; + canvas.style.width = (PRINT_OUTPUT_SCALE * viewport.width) + 'pt'; + canvas.style.height = (PRINT_OUTPUT_SCALE * viewport.height) + 'pt'; + var cssScale = 'scale(' + (1 / PRINT_OUTPUT_SCALE) + ', ' + + (1 / PRINT_OUTPUT_SCALE) + ')'; + CustomStyle.setProp('transform' , canvas, cssScale); + CustomStyle.setProp('transformOrigin' , canvas, '0% 0%'); + + var printContainer = document.getElementById('printContainer'); + printContainer.appendChild(canvas); + + var self = this; + canvas.mozPrintCallback = function(obj) { + var ctx = obj.context; + + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE); + + var renderContext = { + canvasContext: ctx, + viewport: viewport + }; + + pdfPage.render(renderContext).then(function() { + // Tell the printEngine that rendering this canvas/page has finished. + obj.done(); + self.pdfPage.destroy(); + }, function(error) { + console.error(error); + // Tell the printEngine that rendering this canvas/page has failed. + // This will make the print proces stop. + if ('abort' in obj) + obj.abort(); + else + obj.done(); + self.pdfPage.destroy(); + }); + }; + }; + + this.updateStats = function pageViewUpdateStats() { + if (PDFJS.pdfBug && Stats.enabled) { + var stats = this.stats; + Stats.add(this.id, stats); + } + }; +}; + +var ThumbnailView = function thumbnailView(container, pdfPage, id) { + var anchor = document.createElement('a'); + anchor.href = PDFView.getAnchorUrl('#page=' + id); + anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}'); + anchor.onclick = function stopNavigation() { + PDFView.page = id; + return false; + }; + + var rotation = 0; + var totalRotation = (rotation + pdfPage.rotate) % 360; + var viewport = pdfPage.getViewport(1, totalRotation); + var pageWidth = this.width = viewport.width; + var pageHeight = this.height = viewport.height; + var pageRatio = pageWidth / pageHeight; + this.id = id; + + var canvasWidth = 98; + var canvasHeight = canvasWidth / this.width * this.height; + var scaleX = this.scaleX = (canvasWidth / pageWidth); + var scaleY = this.scaleY = (canvasHeight / pageHeight); + + var div = this.el = document.createElement('div'); + div.id = 'thumbnailContainer' + id; + div.className = 'thumbnail'; + + if (id === 1) { + // Highlight the thumbnail of the first page when no page number is + // specified (or exists in cache) when the document is loaded. + div.classList.add('selected'); + } + + var ring = document.createElement('div'); + ring.className = 'thumbnailSelectionRing'; + ring.style.width = canvasWidth + 'px'; + ring.style.height = canvasHeight + 'px'; + + div.appendChild(ring); + anchor.appendChild(div); + container.appendChild(anchor); + + this.hasImage = false; + this.renderingState = RenderingStates.INITIAL; + + this.updateRotation = function(rot) { + + rotation = rot; + totalRotation = (rotation + pdfPage.rotate) % 360; + viewport = pdfPage.getViewport(1, totalRotation); + pageWidth = this.width = viewport.width; + pageHeight = this.height = viewport.height; + pageRatio = pageWidth / pageHeight; + + canvasHeight = canvasWidth / this.width * this.height; + scaleX = this.scaleX = (canvasWidth / pageWidth); + scaleY = this.scaleY = (canvasHeight / pageHeight); + + div.removeAttribute('data-loaded'); + ring.textContent = ''; + ring.style.width = canvasWidth + 'px'; + ring.style.height = canvasHeight + 'px'; + + this.hasImage = false; + this.renderingState = RenderingStates.INITIAL; + this.resume = null; + }; + + function getPageDrawContext() { + var canvas = document.createElement('canvas'); + canvas.id = 'thumbnail' + id; + canvas.mozOpaque = true; + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + canvas.className = 'thumbnailImage'; + canvas.setAttribute('aria-label', mozL10n.get('thumb_page_canvas', + {page: id}, 'Thumbnail of Page {{page}}')); + + div.setAttribute('data-loaded', true); + + ring.appendChild(canvas); + + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, canvasWidth, canvasHeight); + ctx.restore(); + return ctx; + } + + this.drawingRequired = function thumbnailViewDrawingRequired() { + return !this.hasImage; + }; + + this.draw = function thumbnailViewDraw(callback) { + if (this.renderingState !== RenderingStates.INITIAL) { + console.error('Must be in new state before drawing'); + } + + this.renderingState = RenderingStates.RUNNING; + if (this.hasImage) { + callback(); + return; + } + + var self = this; + var ctx = getPageDrawContext(); + var drawViewport = pdfPage.getViewport(scaleX, totalRotation); + var renderContext = { + canvasContext: ctx, + viewport: drawViewport, + continueCallback: function(cont) { + if (PDFView.highestPriorityPage !== 'thumbnail' + self.id) { + self.renderingState = RenderingStates.PAUSED; + self.resume = function() { + self.renderingState = RenderingStates.RUNNING; + cont(); + }; + return; + } + cont(); + } + }; + pdfPage.render(renderContext).then( + function pdfPageRenderCallback() { + self.renderingState = RenderingStates.FINISHED; + callback(); + }, + function pdfPageRenderError(error) { + self.renderingState = RenderingStates.FINISHED; + callback(); + } + ); + this.hasImage = true; + }; + + this.setImage = function thumbnailViewSetImage(img) { + if (this.hasImage || !img) + return; + this.renderingState = RenderingStates.FINISHED; + var ctx = getPageDrawContext(); + ctx.drawImage(img, 0, 0, img.width, img.height, + 0, 0, ctx.canvas.width, ctx.canvas.height); + + this.hasImage = true; + }; +}; + +var DocumentOutlineView = function documentOutlineView(outline) { + var outlineView = document.getElementById('outlineView'); + while (outlineView.firstChild) + outlineView.removeChild(outlineView.firstChild); + + function bindItemLink(domObj, item) { + domObj.href = PDFView.getDestinationHash(item.dest); + domObj.onclick = function documentOutlineViewOnclick(e) { + PDFView.navigateTo(item.dest); + return false; + }; + } + + if (!outline) { + var noOutline = document.createElement('div'); + noOutline.classList.add('noOutline'); + noOutline.textContent = mozL10n.get('no_outline', null, + 'No Outline Available'); + outlineView.appendChild(noOutline); + return; + } + + var queue = [{parent: outlineView, items: outline}]; + while (queue.length > 0) { + var levelData = queue.shift(); + var i, n = levelData.items.length; + for (i = 0; i < n; i++) { + var item = levelData.items[i]; + var div = document.createElement('div'); + div.className = 'outlineItem'; + var a = document.createElement('a'); + bindItemLink(a, item); + a.textContent = item.title; + div.appendChild(a); + + if (item.items.length > 0) { + var itemsDiv = document.createElement('div'); + itemsDiv.className = 'outlineItems'; + div.appendChild(itemsDiv); + queue.push({parent: itemsDiv, items: item.items}); + } + + levelData.parent.appendChild(div); + } + } +}; + +// optimised CSS custom property getter/setter +var CustomStyle = (function CustomStyleClosure() { + + // As noted on: http://www.zachstronaut.com/posts/2009/02/17/ + // animate-css-transforms-firefox-webkit.html + // in some versions of IE9 it is critical that ms appear in this list + // before Moz + var prefixes = ['ms', 'Moz', 'Webkit', 'O']; + var _cache = { }; + + function CustomStyle() { + } + + CustomStyle.getProp = function get(propName, element) { + // check cache only when no element is given + if (arguments.length == 1 && typeof _cache[propName] == 'string') { + return _cache[propName]; + } + + element = element || document.documentElement; + var style = element.style, prefixed, uPropName; + + // test standard property first + if (typeof style[propName] == 'string') { + return (_cache[propName] = propName); + } + + // capitalize + uPropName = propName.charAt(0).toUpperCase() + propName.slice(1); + + // test vendor specific properties + for (var i = 0, l = prefixes.length; i < l; i++) { + prefixed = prefixes[i] + uPropName; + if (typeof style[prefixed] == 'string') { + return (_cache[propName] = prefixed); + } + } + + //if all fails then set to undefined + return (_cache[propName] = 'undefined'); + }; + + CustomStyle.setProp = function set(propName, element, str) { + var prop = this.getProp(propName); + if (prop != 'undefined') + element.style[prop] = str; + }; + + return CustomStyle; +})(); + +var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) { + var textLayerFrag = document.createDocumentFragment(); + + this.textLayerDiv = textLayerDiv; + this.layoutDone = false; + this.divContentDone = false; + this.pageIdx = pageIdx; + this.matches = []; + + this.beginLayout = function textLayerBuilderBeginLayout() { + this.textDivs = []; + this.textLayerQueue = []; + this.renderingDone = false; + }; + + this.endLayout = function textLayerBuilderEndLayout() { + this.layoutDone = true; + this.insertDivContent(); + }; + + this.renderLayer = function textLayerBuilderRenderLayer() { + var self = this; + var textDivs = this.textDivs; + var textLayerDiv = this.textLayerDiv; + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + + // No point in rendering so many divs as it'd make the browser unusable + // even after the divs are rendered + var MAX_TEXT_DIVS_TO_RENDER = 100000; + if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER) + return; + + for (var i = 0, ii = textDivs.length; i < ii; i++) { + var textDiv = textDivs[i]; + textLayerFrag.appendChild(textDiv); + + ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily; + var width = ctx.measureText(textDiv.textContent).width; + + if (width > 0) { + var textScale = textDiv.dataset.canvasWidth / width; + + CustomStyle.setProp('transform' , textDiv, + 'scale(' + textScale + ', 1)'); + CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); + + textLayerDiv.appendChild(textDiv); + } + } + + this.renderingDone = true; + this.updateMatches(); + + textLayerDiv.appendChild(textLayerFrag); + }; + + this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() { + // Schedule renderLayout() if user has been scrolling, otherwise + // run it right away + var RENDER_DELAY = 200; // in ms + var self = this; + if (Date.now() - PDFView.lastScroll > RENDER_DELAY) { + // Render right away + this.renderLayer(); + } else { + // Schedule + if (this.renderTimer) + clearTimeout(this.renderTimer); + this.renderTimer = setTimeout(function() { + self.setupRenderLayoutTimer(); + }, RENDER_DELAY); + } + }; + + this.appendText = function textLayerBuilderAppendText(geom) { + var textDiv = document.createElement('div'); + + // vScale and hScale already contain the scaling to pixel units + var fontHeight = geom.fontSize * geom.vScale; + textDiv.dataset.canvasWidth = geom.canvasWidth * geom.hScale; + textDiv.dataset.fontName = geom.fontName; + + textDiv.style.fontSize = fontHeight + 'px'; + textDiv.style.fontFamily = geom.fontFamily; + textDiv.style.left = geom.x + 'px'; + textDiv.style.top = (geom.y - fontHeight) + 'px'; + + // The content of the div is set in the `setTextContent` function. + + this.textDivs.push(textDiv); + }; + + this.insertDivContent = function textLayerUpdateTextContent() { + // Only set the content of the divs once layout has finished, the content + // for the divs is available and content is not yet set on the divs. + if (!this.layoutDone || this.divContentDone || !this.textContent) + return; + + this.divContentDone = true; + + var textDivs = this.textDivs; + var bidiTexts = this.textContent.bidiTexts; + + for (var i = 0; i < bidiTexts.length; i++) { + var bidiText = bidiTexts[i]; + var textDiv = textDivs[i]; + + textDiv.textContent = bidiText.str; + textDiv.dir = bidiText.ltr ? 'ltr' : 'rtl'; + } + + this.setupRenderLayoutTimer(); + }; + + this.setTextContent = function textLayerBuilderSetTextContent(textContent) { + this.textContent = textContent; + this.insertDivContent(); + }; + + this.convertMatches = function textLayerBuilderConvertMatches(matches) { + var i = 0; + var iIndex = 0; + var bidiTexts = this.textContent.bidiTexts; + var end = bidiTexts.length - 1; + var queryLen = PDFFindController.state.query.length; + + var lastDivIdx = -1; + var pos; + + var ret = []; + + // Loop over all the matches. + for (var m = 0; m < matches.length; m++) { + var matchIdx = matches[m]; + // # Calculate the begin position. + + // Loop over the divIdxs. + while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) { + iIndex += bidiTexts[i].str.length; + i++; + } + + // TODO: Do proper handling here if something goes wrong. + if (i == bidiTexts.length) { + console.error('Could not find matching mapping'); + } + + var match = { + begin: { + divIdx: i, + offset: matchIdx - iIndex + } + }; + + // # Calculate the end position. + matchIdx += queryLen; + + // Somewhat same array as above, but use a > instead of >= to get the end + // position right. + while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) { + iIndex += bidiTexts[i].str.length; + i++; + } + + match.end = { + divIdx: i, + offset: matchIdx - iIndex + }; + ret.push(match); + } + + return ret; + }; + + this.renderMatches = function textLayerBuilder_renderMatches(matches) { + // Early exit if there is nothing to render. + if (matches.length === 0) { + return; + } + + var bidiTexts = this.textContent.bidiTexts; + var textDivs = this.textDivs; + var prevEnd = null; + var isSelectedPage = this.pageIdx === PDFFindController.selected.pageIdx; + var selectedMatchIdx = PDFFindController.selected.matchIdx; + var highlightAll = PDFFindController.state.highlightAll; + + var infty = { + divIdx: -1, + offset: undefined + }; + + function beginText(begin, className) { + var divIdx = begin.divIdx; + var div = textDivs[divIdx]; + div.textContent = ''; + + var content = bidiTexts[divIdx].str.substring(0, begin.offset); + var node = document.createTextNode(content); + if (className) { + var isSelected = isSelectedPage && + divIdx === selectedMatchIdx; + var span = document.createElement('span'); + span.className = className + (isSelected ? ' selected' : ''); + span.appendChild(node); + div.appendChild(span); + return; + } + div.appendChild(node); + } + + function appendText(from, to, className) { + var divIdx = from.divIdx; + var div = textDivs[divIdx]; + + var content = bidiTexts[divIdx].str.substring(from.offset, to.offset); + var node = document.createTextNode(content); + if (className) { + var span = document.createElement('span'); + span.className = className; + span.appendChild(node); + div.appendChild(span); + return; + } + div.appendChild(node); + } + + function highlightDiv(divIdx, className) { + textDivs[divIdx].className = className; + } + + var i0 = selectedMatchIdx, i1 = i0 + 1, i; + + if (highlightAll) { + i0 = 0; + i1 = matches.length; + } else if (!isSelectedPage) { + // Not highlighting all and this isn't the selected page, so do nothing. + return; + } + + for (i = i0; i < i1; i++) { + var match = matches[i]; + var begin = match.begin; + var end = match.end; + + var isSelected = isSelectedPage && i === selectedMatchIdx; + var highlightSuffix = (isSelected ? ' selected' : ''); + if (isSelected) + scrollIntoView(textDivs[begin.divIdx], {top: -50}); + + // Match inside new div. + if (!prevEnd || begin.divIdx !== prevEnd.divIdx) { + // If there was a previous div, then add the text at the end + if (prevEnd !== null) { + appendText(prevEnd, infty); + } + // clears the divs and set the content until the begin point. + beginText(begin); + } else { + appendText(prevEnd, begin); + } + + if (begin.divIdx === end.divIdx) { + appendText(begin, end, 'highlight' + highlightSuffix); + } else { + appendText(begin, infty, 'highlight begin' + highlightSuffix); + for (var n = begin.divIdx + 1; n < end.divIdx; n++) { + highlightDiv(n, 'highlight middle' + highlightSuffix); + } + beginText(end, 'highlight end' + highlightSuffix); + } + prevEnd = end; + } + + if (prevEnd) { + appendText(prevEnd, infty); + } + }; + + this.updateMatches = function textLayerUpdateMatches() { + // Only show matches, once all rendering is done. + if (!this.renderingDone) + return; + + // Clear out all matches. + var matches = this.matches; + var textDivs = this.textDivs; + var bidiTexts = this.textContent.bidiTexts; + var clearedUntilDivIdx = -1; + + // Clear out all current matches. + for (var i = 0; i < matches.length; i++) { + var match = matches[i]; + var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx); + for (var n = begin; n <= match.end.divIdx; n++) { + var div = textDivs[n]; + div.textContent = bidiTexts[n].str; + div.className = ''; + } + clearedUntilDivIdx = match.end.divIdx + 1; + } + + if (!PDFFindController.active) + return; + + // Convert the matches on the page controller into the match format used + // for the textLayer. + this.matches = matches = + this.convertMatches(PDFFindController.pageMatches[this.pageIdx] || []); + + this.renderMatches(this.matches); + }; +}; + +document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) { + PDFView.initialize(); + var params = PDFView.parseQueryString(document.location.search.substring(1)); + + var file = params.file || DEFAULT_URL; + + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { + document.getElementById('openFile').setAttribute('hidden', 'true'); + } else { + document.getElementById('fileInput').value = null; + } + + // Special debugging flags in the hash section of the URL. + var hash = document.location.hash.substring(1); + var hashParams = PDFView.parseQueryString(hash); + + if ('disableWorker' in hashParams) + PDFJS.disableWorker = (hashParams['disableWorker'] === 'true'); + + var locale = navigator.language; + if ('locale' in hashParams) + locale = hashParams['locale']; + mozL10n.setLanguage(locale); + + if ('textLayer' in hashParams) { + switch (hashParams['textLayer']) { + case 'off': + PDFJS.disableTextLayer = true; + break; + case 'visible': + case 'shadow': + case 'hover': + var viewer = document.getElementById('viewer'); + viewer.classList.add('textLayer-' + hashParams['textLayer']); + break; + } + } + + if ('pdfBug' in hashParams) { + PDFJS.pdfBug = true; + var pdfBug = hashParams['pdfBug']; + var enabled = pdfBug.split(','); + PDFBug.enable(enabled); + PDFBug.init(); + } + + if (!PDFView.supportsPrinting) { + document.getElementById('print').classList.add('hidden'); + } + + if (!PDFView.supportsFullscreen) { + document.getElementById('fullscreen').classList.add('hidden'); + } + + if (PDFView.supportsIntegratedFind) { + document.querySelector('#viewFind').classList.add('hidden'); + } + + // Listen for warnings to trigger the fallback UI. Errors should be caught + // and call PDFView.error() so we don't need to listen for those. + PDFJS.LogManager.addLogger({ + warn: function() { + PDFView.fallback(); + } + }); + + var mainContainer = document.getElementById('mainContainer'); + var outerContainer = document.getElementById('outerContainer'); + mainContainer.addEventListener('transitionend', function(e) { + if (e.target == mainContainer) { + var event = document.createEvent('UIEvents'); + event.initUIEvent('resize', false, false, window, 0); + window.dispatchEvent(event); + outerContainer.classList.remove('sidebarMoving'); + } + }, true); + + document.getElementById('sidebarToggle').addEventListener('click', + function() { + this.classList.toggle('toggled'); + outerContainer.classList.add('sidebarMoving'); + outerContainer.classList.toggle('sidebarOpen'); + PDFView.sidebarOpen = outerContainer.classList.contains('sidebarOpen'); + PDFView.renderHighestPriority(); + }); + + document.getElementById('viewThumbnail').addEventListener('click', + function() { + PDFView.switchSidebarView('thumbs'); + }); + + document.getElementById('viewOutline').addEventListener('click', + function() { + PDFView.switchSidebarView('outline'); + }); + + document.getElementById('previous').addEventListener('click', + function() { + PDFView.page--; + }); + + document.getElementById('next').addEventListener('click', + function() { + PDFView.page++; + }); + + document.querySelector('.zoomIn').addEventListener('click', + function() { + PDFView.zoomIn(); + }); + + document.querySelector('.zoomOut').addEventListener('click', + function() { + PDFView.zoomOut(); + }); + + document.getElementById('fullscreen').addEventListener('click', + function() { + PDFView.fullscreen(); + }); + + document.getElementById('openFile').addEventListener('click', + function() { + document.getElementById('fileInput').click(); + }); + + document.getElementById('print').addEventListener('click', + function() { + window.print(); + }); + + document.getElementById('download').addEventListener('click', + function() { + PDFView.download(); + }); + + document.getElementById('pageNumber').addEventListener('click', + function() { + this.select(); + }); + + document.getElementById('pageNumber').addEventListener('change', + function() { + // Handle the user inputting a floating point number. + PDFView.page = (this.value | 0); + + if (this.value !== (this.value | 0).toString()) { + this.value = PDFView.page; + } + }); + + document.getElementById('scaleSelect').addEventListener('change', + function() { + PDFView.parseScale(this.value); + }); + + document.getElementById('first_page').addEventListener('click', + function() { + PDFView.page = 1; + }); + + document.getElementById('last_page').addEventListener('click', + function() { + PDFView.page = PDFView.pdfDocument.numPages; + }); + + document.getElementById('page_rotate_ccw').addEventListener('click', + function() { + PDFView.rotatePages(-90); + }); + + document.getElementById('page_rotate_cw').addEventListener('click', + function() { + PDFView.rotatePages(90); + }); + + + PDFView.open(file, 0); +}, true); + +function updateViewarea() { + + if (!PDFView.initialized) + return; + var visible = PDFView.getVisiblePages(); + var visiblePages = visible.views; + if (visiblePages.length === 0) { + return; + } + + PDFView.renderHighestPriority(); + + var currentId = PDFView.page; + var firstPage = visible.first; + + for (var i = 0, ii = visiblePages.length, stillFullyVisible = false; + i < ii; ++i) { + var page = visiblePages[i]; + + if (page.percent < 100) + break; + + if (page.id === PDFView.page) { + stillFullyVisible = true; + break; + } + } + + if (!stillFullyVisible) { + currentId = visiblePages[0].id; + } + + if (!PDFView.isFullscreen) { + updateViewarea.inProgress = true; // used in "set page" + PDFView.page = currentId; + updateViewarea.inProgress = false; + } + + var currentScale = PDFView.currentScale; + var currentScaleValue = PDFView.currentScaleValue; + var normalizedScaleValue = currentScaleValue == currentScale ? + currentScale * 100 : currentScaleValue; + + var pageNumber = firstPage.id; + var pdfOpenParams = '#page=' + pageNumber; + pdfOpenParams += '&zoom=' + normalizedScaleValue; + var currentPage = PDFView.pages[pageNumber - 1]; + var topLeft = currentPage.getPagePoint(PDFView.container.scrollLeft, + (PDFView.container.scrollTop - firstPage.y)); + pdfOpenParams += ',' + Math.round(topLeft[0]) + ',' + Math.round(topLeft[1]); + + var store = PDFView.store; + store.initializedPromise.then(function() { + store.set('exists', true); + store.set('page', pageNumber); + store.set('zoom', normalizedScaleValue); + store.set('scrollLeft', Math.round(topLeft[0])); + store.set('scrollTop', Math.round(topLeft[1])); + }); + var href = PDFView.getAnchorUrl(pdfOpenParams); + document.getElementById('viewBookmark').href = href; +} + +window.addEventListener('resize', function webViewerResize(evt) { + if (PDFView.initialized && + (document.getElementById('pageWidthOption').selected || + document.getElementById('pageFitOption').selected || + document.getElementById('pageAutoOption').selected)) + PDFView.parseScale(document.getElementById('scaleSelect').value); + updateViewarea(); +}); + +window.addEventListener('hashchange', function webViewerHashchange(evt) { + PDFView.setHash(document.location.hash.substring(1)); +}); + +window.addEventListener('change', function webViewerChange(evt) { + var files = evt.target.files; + if (!files || files.length === 0) + return; + + // Read the local file into a Uint8Array. + var fileReader = new FileReader(); + fileReader.onload = function webViewerChangeFileReaderOnload(evt) { + var buffer = evt.target.result; + var uint8Array = new Uint8Array(buffer); + PDFView.open(uint8Array, 0); + }; + + var file = files[0]; + fileReader.readAsArrayBuffer(file); + PDFView.setTitleUsingUrl(file.name); + + // URL does not reflect proper document location - hiding some icons. + document.getElementById('viewBookmark').setAttribute('hidden', 'true'); + document.getElementById('download').setAttribute('hidden', 'true'); +}, true); + +function selectScaleOption(value) { + var options = document.getElementById('scaleSelect').options; + var predefinedValueFound = false; + for (var i = 0; i < options.length; i++) { + var option = options[i]; + if (option.value != value) { + option.selected = false; + continue; + } + option.selected = true; + predefinedValueFound = true; + } + return predefinedValueFound; +} + +window.addEventListener('localized', function localized(evt) { + document.getElementsByTagName('html')[0].dir = mozL10n.getDirection(); + + // Adjust the width of the zoom box to fit the content. + var container = document.getElementById('scaleSelectContainer'); + var select = document.getElementById('scaleSelect'); + + select.setAttribute('style', 'min-width: inherit;'); + var width = select.clientWidth + 8; + container.setAttribute('style', 'min-width: ' + width + 'px; ' + + 'max-width: ' + width + 'px;'); + select.setAttribute('style', 'min-width: ' + (width + 20) + 'px;'); +}, true); + +window.addEventListener('scalechange', function scalechange(evt) { + var customScaleOption = document.getElementById('customScaleOption'); + customScaleOption.selected = false; + + if (!evt.resetAutoSettings && + (document.getElementById('pageWidthOption').selected || + document.getElementById('pageFitOption').selected || + document.getElementById('pageAutoOption').selected)) { + updateViewarea(); + return; + } + + var predefinedValueFound = selectScaleOption('' + evt.scale); + if (!predefinedValueFound) { + customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%'; + customScaleOption.selected = true; + } + + document.getElementById('zoom_out').disabled = (evt.scale === MIN_SCALE); + document.getElementById('zoom_in').disabled = (evt.scale === MAX_SCALE); + + updateViewarea(); +}, true); + +window.addEventListener('pagechange', function pagechange(evt) { + var page = evt.pageNumber; + if (PDFView.previousPageNumber !== page) { + document.getElementById('pageNumber').value = page; + var selected = document.querySelector('.thumbnail.selected'); + if (selected) + selected.classList.remove('selected'); + var thumbnail = document.getElementById('thumbnailContainer' + page); + thumbnail.classList.add('selected'); + var visibleThumbs = PDFView.getVisibleThumbs(); + var numVisibleThumbs = visibleThumbs.views.length; + // If the thumbnail isn't currently visible scroll it into view. + if (numVisibleThumbs > 0) { + var first = visibleThumbs.first.id; + // Account for only one thumbnail being visible. + var last = numVisibleThumbs > 1 ? + visibleThumbs.last.id : first; + if (page <= first || page >= last) + scrollIntoView(thumbnail); + } + + } + document.getElementById('previous').disabled = (page <= 1); + document.getElementById('next').disabled = (page >= PDFView.pages.length); +}, true); + +// Firefox specific event, so that we can prevent browser from zooming +window.addEventListener('DOMMouseScroll', function(evt) { + if (evt.ctrlKey) { + evt.preventDefault(); + + var ticks = evt.detail; + var direction = (ticks > 0) ? 'zoomOut' : 'zoomIn'; + for (var i = 0, length = Math.abs(ticks); i < length; i++) + PDFView[direction](); + } else if (PDFView.isFullscreen) { + var FIREFOX_DELTA_FACTOR = -40; + PDFView.mouseScroll(evt.detail * FIREFOX_DELTA_FACTOR); + } +}, false); + +window.addEventListener('mousemove', function keydown(evt) { + if (PDFView.isFullscreen) { + PDFView.showPresentationControls(); + } +}, false); + +window.addEventListener('mousedown', function mousedown(evt) { + if (PDFView.isFullscreen && evt.button === 0) { + // Mouse click in fullmode advances a page + evt.preventDefault(); + + PDFView.page++; + } +}, false); + +window.addEventListener('keydown', function keydown(evt) { + var handled = false; + var cmd = (evt.ctrlKey ? 1 : 0) | + (evt.altKey ? 2 : 0) | + (evt.shiftKey ? 4 : 0) | + (evt.metaKey ? 8 : 0); + + // First, handle the key bindings that are independent whether an input + // control is selected or not. + if (cmd == 1 || cmd == 8) { // either CTRL or META key. + switch (evt.keyCode) { + case 70: + if (!PDFView.supportsIntegratedFind) { + PDFFindBar.toggle(); + handled = true; + } + break; + case 61: // FF/Mac '=' + case 107: // FF '+' and '=' + case 187: // Chrome '+' + case 171: // FF with German keyboard + PDFView.zoomIn(); + handled = true; + break; + case 173: // FF/Mac '-' + case 109: // FF '-' + case 189: // Chrome '-' + PDFView.zoomOut(); + handled = true; + break; + case 48: // '0' + case 96: // '0' on Numpad of Swedish keyboard + PDFView.parseScale(DEFAULT_SCALE, true); + handled = true; + break; + } + } + + // CTRL or META with or without SHIFT. + if (cmd == 1 || cmd == 8 || cmd == 5 || cmd == 12) { + switch (evt.keyCode) { + case 71: // g + if (!PDFView.supportsIntegratedFind) { + PDFFindBar.dispatchEvent('again', cmd == 5 || cmd == 12); + handled = true; + } + break; + } + } + + if (handled) { + evt.preventDefault(); + return; + } + + // Some shortcuts should not get handled if a control/input element + // is selected. + var curElement = document.activeElement; + if (curElement && (curElement.tagName == 'INPUT' || + curElement.tagName == 'SELECT')) { + return; + } + var controlsElement = document.getElementById('toolbar'); + while (curElement) { + if (curElement === controlsElement && !PDFView.isFullscreen) + return; // ignoring if the 'toolbar' element is focused + curElement = curElement.parentNode; + } + + if (cmd === 0) { // no control key pressed at all. + switch (evt.keyCode) { + case 38: // up arrow + case 33: // pg up + case 8: // backspace + if (!PDFView.isFullscreen && PDFView.currentScaleValue !== 'page-fit') { + break; + } + /* in fullscreen mode */ + /* falls through */ + case 37: // left arrow + // horizontal scrolling using arrow keys + if (PDFView.isHorizontalScrollbarEnabled) { + break; + } + /* falls through */ + case 75: // 'k' + case 80: // 'p' + PDFView.page--; + handled = true; + break; + case 27: // esc key + if (!PDFView.supportsIntegratedFind && PDFFindBar.opened) { + PDFFindBar.close(); + handled = true; + } + break; + case 40: // down arrow + case 34: // pg down + case 32: // spacebar + if (!PDFView.isFullscreen && PDFView.currentScaleValue !== 'page-fit') { + break; + } + /* falls through */ + case 39: // right arrow + // horizontal scrolling using arrow keys + if (PDFView.isHorizontalScrollbarEnabled) { + break; + } + /* falls through */ + case 74: // 'j' + case 78: // 'n' + PDFView.page++; + handled = true; + break; + + case 36: // home + if (PDFView.isFullscreen) { + PDFView.page = 1; + handled = true; + } + break; + case 35: // end + if (PDFView.isFullscreen) { + PDFView.page = PDFView.pdfDocument.numPages; + handled = true; + } + break; + + case 82: // 'r' + PDFView.rotatePages(90); + break; + } + } + + if (cmd == 4) { // shift-key + switch (evt.keyCode) { + case 82: // 'r' + PDFView.rotatePages(-90); + break; + } + } + + if (handled) { + evt.preventDefault(); + PDFView.clearMouseScrollState(); + } +}); + +window.addEventListener('beforeprint', function beforePrint(evt) { + PDFView.beforePrint(); +}); + +window.addEventListener('afterprint', function afterPrint(evt) { + PDFView.afterPrint(); +}); + +(function fullscreenClosure() { + function fullscreenChange(e) { + var isFullscreen = document.fullscreenElement || document.mozFullScreen || + document.webkitIsFullScreen; + + if (!isFullscreen) { + PDFView.exitFullscreen(); + } + } + + window.addEventListener('fullscreenchange', fullscreenChange, false); + window.addEventListener('mozfullscreenchange', fullscreenChange, false); + window.addEventListener('webkitfullscreenchange', fullscreenChange, false); +})(); + +(function animationStartedClosure() { + // The offsetParent is not set until the pdf.js iframe or object is visible. + // Waiting for first animation. + var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function startAtOnce(callback) { callback(); }; + PDFView.animationStartedPromise = new PDFJS.Promise(); + requestAnimationFrame(function onAnimationFrame() { + PDFView.animationStartedPromise.resolve(); + }); +})(); + + diff --git a/common/test/data/simple/course.xml b/common/test/data/simple/course.xml index 86dc8df45c..660411384f 100644 --- a/common/test/data/simple/course.xml +++ b/common/test/data/simple/course.xml @@ -15,7 +15,7 @@
- +