and
to data-width
var fig_width = $.trim($figureDiv.attr('data-width'));
if (!xBookUtils.emptyValue(fig_width) && /^\d+$/.test(fig_width)) {
$figureDiv.attr('data-layout-width', '');
$image.attr('width', fig_width);
$figureDiv.css('width', fig_width + "px");
}
// Otherwise, if resizeDivWidth is requested then attach to each
// figure
else if (resize_div_width === "on") {
$figureDiv.imagesLoaded(function($images, $proper, $broken) {
$proper.each(function() {
var $image = $(this);
var $parent = $image.parent('[data-type="figure"]');
if ($parent.length != 0) {
//safe_log("Adjusting div with for figure " + $parent.attr('id'));
var img_width = $image.width();
// For FW books the image is not always visible at
// this time and thus the width == 0. If this is the
// case then set data-resize-img-div and we'll set the
// width later at the end of show_section_animate()
if (img_width > 0) {
//safe_log("Setting div width to " + img_width + "px");
$parent.css("width", img_width + "px");
}
else {
//safe_log("Setting data-resize-img-div to 1");
$parent.attr('data-resize-img-div', '1');
}
}
});
});
}
}); // end each()
} // end init()
}); // end Figures_manuscript_subtype
/*
Added 12/23/2015 by Bruce.
Adds ability to align table columns by character:
where CH can be a single character or string.
*/
var Tables_manuscript_subtype = Tables.extend({
init: function() {
this._super();
// fix any tables with data-align-char
this._set_data_align_char();
},
// call this function at the end of initialize2
align_columns: function() {
// align tables with align="char"
this._align_table_columns();
},
// set the 'char' attribute for any tables that have data-align-char assigned
_set_data_align_char: function() {
var that = this;
$("[data-type='table'][data-align-char]").each(function() {
var $this = $(this);
var align_char = $this.attr('data-align-char');
if (xBookUtils.emptyValue(align_char)) {
return;
}
xBookUtils.debug("setting align char to " + align_char + " for table #" + $this.attr('id'));
var $rows = $this.children('table').children("tbody").children('tr');
$rows.each(function() {
$.each(this.cells, function(col){
var $td = $(this);
if (!that._has_char_align($td)) {
return;
}
$td.attr("char", align_char);
});
});
});
},
_align_table_columns: function() {
var that = this;
$("#manuscript").find("table").each(function() {
var $table = $(this);
if ($table.parent().attr('data-char-align') === "false") {
xBookUtils.debug("table #" + $table.parent().attr('id') + " data-char-align='false'");
return;
}
var hasCharAlign = []; // stores alignment info for each column
var $rows = $table.children("tbody").children('tr');
// find the columns that have character alignment
$rows.each(function() {
$.each(this.cells, function(col){
var $td = $(this);
if (!that._has_char_align($td)) {
return;
}
// if we already have an align char set for the column we are done
if (hasCharAlign[col] !== undefined &&
hasCharAlign[col].ch !== undefined) {
return;
}
// find char we are aligning on
var align_char = $td.attr('char');
if (align_char === undefined) {
align_char = "."; // default
}
// set align char for this colum
hasCharAlign[col] = {};
hasCharAlign[col].ch = align_char;
xBookUtils.debug("table #" + $table.parent().attr('id') + " column " + col + " is aligned on char " + align_char);
});
});
// If we didn't find any columns with char align then we are done
if (hasCharAlign.length < 1) {
return;
}
// 1. add internal spans
// 2. get width of align char
// 3. for each column, get max width of left and right span
$rows.each(function() {
$.each(this.cells, function(col){
var $td = $(this);
if (!that._has_char_align($td)) {
return;
}
// Search for align char in |
var align_char = hasCharAlign[col].ch;
// escape special chars
align_char = align_char.replace(/([.*])/, "\\$1");
var regexp = new RegExp("(.*)(" + align_char + ")(.*)")
var html = $td.html();
var new_html;
var dmatch = html.match(regexp);
// if | has alignment character
if (dmatch !== null) {
new_html = "" + dmatch[1] + "" + dmatch[2] + "" + dmatch[3] + "";
$td.html(new_html);
if (hasCharAlign[col].hasalignchar !== true) {
hasCharAlign[col].hasalignchar = true;
var $ch_align_span = $td.find('.char-align-middle');
hasCharAlign[col].chwidth = $ch_align_span.width();
}
// get width of align char if needed
if (hasCharAlign[col].charwidth === undefined) {
hasCharAlign[col].charwidth = $td.find('.char-align-middle').width();
}
// get width of left span and set max left width if needed
var left_width = $td.find('.char-align-left').width();
if (hasCharAlign[col].maxleftwidth === undefined) {
hasCharAlign[col].maxleftwidth = left_width;
}
else if (left_width > hasCharAlign[col].maxleftwidth) {
hasCharAlign[col].maxleftwidth = left_width;
}
// get width of right span and set max right width if needed
var right_width = $td.find('.char-align-right').width();
if (hasCharAlign[col].maxrightwidth === undefined) {
hasCharAlign[col].maxrightwidth = right_width;
}
else if (right_width > hasCharAlign[col].maxrightwidth) {
hasCharAlign[col].maxrightwidth = right_width;
}
}
// if | does not have alignment char
else {
new_html = "" + html + "";
$td.html(new_html);
// get width of left span and set max left width if needed
var left_width = $td.find('.char-align-left').width();
if (hasCharAlign[col].maxleftwidth === undefined) {
hasCharAlign[col].maxleftwidth = left_width;
}
else if (left_width > hasCharAlign[col].maxleftwidth) {
hasCharAlign[col].maxleftwidth = left_width;
}
// right width will always be 0
if (hasCharAlign[col].maxrightwidth === undefined) {
hasCharAlign[col].maxrightwidth = 0;
}
}
});
});
// go through cells one more time adding padding where necessary
var font_size;
$rows.each(function() {
$.each(this.cells, function(col){
var $td = $(this);
if (!that._has_char_align($td)) {
return;
}
// find font size if needed
if (font_size === undefined) {
var style = window.getComputedStyle($td[0], null);
font_size = parseFloat(style.fontSize);
}
var $table_align = $td.children('.char-align-table');
var left_width = $table_align.children('.char-align-left').width();
// If left width is less than maxleftwidth then pad to maxleftwidth
if (left_width < hasCharAlign[col].maxleftwidth) {
$table_align.css('padding-left', ((hasCharAlign[col].maxleftwidth - left_width) / font_size) + "em");
}
var right_width = $table_align.children('.char-align-right').width();
var right_padding = 0;
if (right_width < hasCharAlign[col].maxrightwidth) {
right_padding = hasCharAlign[col].maxrightwidth - right_width;
}
var $align_ch = $table_align.children('.char-align-middle');
if (hasCharAlign[col].hasalignchar == true &&
$align_ch.text() != hasCharAlign[col].ch) {
right_padding += hasCharAlign[col].chwidth;
}
if (right_padding > 0) {
$table_align.css('padding-right', (right_padding / font_size) + "em");
}
});
});
}); // end for each table
}, // end align_table_columns
// argument should be a jQuery | object
_has_char_align: function($td) {
// getAttribute is a workaround for IE
var align_value = $td[0].getAttribute("align");
if (align_value !== "char") {
return false;
}
return true;
}
}); // end Tables_manuscript_subtype
/*
Added by Bruce 11/08/2013
Updates Glossary class as follows:
- accesses terms from a central hash table instead of embedded in the page
- hash table is defined in asset/terms.js and/or chapter-specific JS files
- places the pop-up window near the mouse click to avoid scrolling issues in PX
This class works in conjunction with the xBookUtils object (specifically, it uses
the xBookUtils.terms object as the hash table to store the term/definitions pairs).
By default, it will load them from asset/term.js. Using the terms.js file
for both glossary terms and footnotes can create quite a big file. As an alternative,
you can define chapter specific entries (such as footnotes) in the book's
chapter-specific JS files.
To add this functionality to your book, add the following to the player init method
in your book's custom JS file *AFTER* the call to _super:
this.glossary = new Glossary_manuscript_subtype();
Glossary terms should be defined in the xml as usual:
...
where key is the key(term) in the hash table
Footnotes should be defined in the xml as
...
The leading "fn_" on the key denotes it as a footnote
Entries defined in asset/terms.js and the chapter-specific JS files
should be added to the xBookUtils.terms object:
xBookUtils.terms['term'] = "definition";
If you aren't going to use asset/terms.js then you can add {terms: false}
to the context object in order to prevent the browser from trying to load it.
this.glossary = new Glossary_manuscript({terms: false});
*/
var Glossary_manuscript_subtype = Glossary.extend({
// turn this "on" to make glossary terms pop on hover instead of click
hoverTerms: "off",
// specify termrefs that should not be clickable
hoverTermsNoClick: "",
// turn on to use new sticky div
useStickyDiv: "off",
// div used to display gloss/footnotes using hover
hoverDiv: undefined,
// div used to display "sticky" (dragable) gloss/footnotes
stickyDiv: undefined,
// holds timer id for remove_hover
hoverTimer: undefined,
// Called by click handler
// This is the old default version of displaying click terms
show_definition: function(term, Xclick, Yclick) {
var Xcoord = Xclick - 225;
var Ycoord = (Yclick - $(document).scrollTop()) + 25;
// Determine whether we have a glossary term or footnote
var type; // will be set to either "Glossary" or "Footnote"
if (/^fn_/.test(term)) {
type = "Footnote";
}
else {
type = "Glossary";
}
var entry = this.get_entry(term);
// If we didn't find an entry show standard error message
if (entry === undefined) {
Standard_Dialog.open("Can't find entry", {
"title": type,
"modal": false,
"draggable": true,
"buttons": "none",
"position": [Xcoord, Ycoord]
});
safe_log("Glossary.show_definition: Can't find " + type + " entry for '" + term + "'");
return;
}
Standard_Dialog.open(entry, {
"title": type,
"modal": false,
"draggable": true,
"buttons": "none",
"position": [Xcoord, Ycoord]
});
return;
}, // end show_definition
// This is the new way of displaying both click and hover terms
show_term_def: function(term, Xclick, Yclick_top, Yclick_bottom, type) {
//safe_log("show_term_def: x = " + Xclick + ", y_top = " + Yclick_top);
// Save off local copy to increase speed
var h_div;
if (type === "hover") {
h_div = this.hoverDiv;
}
else {
h_div = this.stickyDiv;
}
var entry = this.get_entry(term);
if (entry === undefined) {
entry = "Can't find entry";
safe_log("Glossary.show_hover_definition: Can't find entry for '" + term + "'");
}
// add data-type here so styling is in place before we
// calculate height
h_div.attr("data-type", this.get_prefix(term));
h_div.css({"left": "-999px", "display": "block"});
h_div.children('.content').html(entry);
var Xcoord = 0;
var Ycoord = 0;
// Don't let left side of the div go past left side of page
var p_width = h_div.outerWidth();
if (Xclick - (p_width/2) > 9) {
Xcoord = Xclick - (p_width/2);
}
else {
Xcoord = 10;
}
// Display div above mouse by default
if (type === "keydown") {
h_div.addClass("keydown");
}
else {
h_div.removeClass("keydown");
}
//safe_log("show_term_def: p_height = " + p_height);
// top of viewport
var win_top_px = $(document).scrollTop();
// height of hover div with content
var p_height = h_div.outerHeight();
// top of hover div with content
var p_top_px = Yclick_top - p_height - 10;
// Don't let top of the div go past top of viewport
if (p_top_px > win_top_px) {
Ycoord = p_top_px;
}
else {
Ycoord = Yclick_bottom + 10;
}
h_div.css({"top": Ycoord + "px", "left": Xcoord + "px"});
if (type === "keydown") {
document.getElementById('glossary-sticky-kb-leave').focus();
}
},
get_entry: function(key) {
key = key.replace(/{/g, "<");
key = key.replace(/}/g, ">");
var lc_key = key.toLowerCase();
var entry = xBookUtils.terms[key];
if (entry === undefined) {
entry = xBookUtils.terms[lc_key];
}
if (entry === undefined) {
entry = xBookUtils.terms[lc_key.singularize()];
}
if (entry === undefined) {
entry = xBookUtils.terms[lc_key.pluralize()];
}
if (entry === undefined) {
entry = xBookUtils.terms[key.singularize()];
}
if (entry === undefined) {
entry = xBookUtils.terms[key.pluralize()];
}
// If we found an entry return it
if (entry !== undefined) {
return xBookUtils.prepareMarkup(entry);
}
// We didn't find the entry in the terms array so check for
// the entry in the traditional
$('[data-type="glossaryentry"]').each(function() {
var $this = $(this);
var term = $this.children('[data-type="term"]').html();
if (key === term || lc_key === term) {
entry = $this.children('[data-type="definition"]').html();
return false;
}
}); // end inner each
return entry;
},
get_prefix: function (data_term) {
var term_prefix = "gl"; // default is glossary
// data_term may be undefined because no term attribute was set,
// in this case it is a glossary def
if (data_term === undefined) {
return term_prefix;
}
var pre_match = data_term.match(/^([a-z][a-z])_/);
if (pre_match !== null) {
term_prefix = pre_match[1];
}
return term_prefix;
},
handle_mouse_event: function(element, event) {
if (event === undefined || event === null) {
safe_log("Glossary.handle_mouse_event: empty event");
return;
}
var mouse_type = $(element).data('mouse');
// set mouse type if not specified in html
if (mouse_type !== "click" &&
mouse_type !== "hover" &&
mouse_type !== "noclick") {
// mouse_type is 'click' by default
mouse_type = "click";
// local copy for speed
var hover = this.hoverTerms;
// If hover is turned on globally we need to check if this 'type'
// has click turned off
if (hover === "on" || hover === "all") {
var noclick_array = this.hoverTermsNoClick.split(",");
if ($.inArray(this.get_prefix($(element).data('term')), noclick_array) >= 0) {
mouse_type = "noclick";
}
else {
mouse_type = "hover";
}
}
else if (hover !== "off" && !xBookUtils.emptyValue(hover)) {
var hover_array = hover.split(",");
if ($.inArray(this.get_prefix($(element).data('term')), hover_array) >= 0) {
mouse_type = "hover";
}
}
}
var $termref = $(element);
var term = $termref.data("term");
var offset = $termref.offset();
// bitwise OR will truncate the decimal points
var term_top = offset.top|0;
var term_center = offset.left + (($termref.outerWidth())/2)|0;
var term_bottom = (offset.top + $termref.outerHeight())|0;
// If there is no data-term attribute than use the html in the span
if (xBookUtils.emptyValue(term)) {
term = $termref.html();
if (xBookUtils.emptyValue(term)) {
safe_log("Glossary.handle_mouse_event: can't find term value");
return false;
}
}
if (event.type === "click") {
if (mouse_type !== "noclick") {
this.hoverDiv.css("display", "none");
if (this.useStickyDiv === "on") {
this.show_term_def(term, term_center, term_top, term_bottom, "click");
}
else {
this.show_definition(term, offset.left, offset.top);
}
}
}
else if (event.type === "mouseenter") {
if (mouse_type !== "click") {
clearTimeout(this.hoverTimeout);
this.hoverDiv.data('mouse-active', '1');
this.show_term_def(term, term_center, term_top, term_bottom, "hover");
}
}
else if (event.type === "mouseleave") {
if (mouse_type !== "click") {
this.hoverDiv.data('mouse-active', '0');
this.hoverTimer = setTimeout(this.remove_hover, 1000);
}
}
else if (event.type === "keydown") {
// 13 = Enter key
if (event.keyCode == 13) {
// set last-focus on term
$termref.attr('data-last-focus', '1');
this.show_term_def(term, term_center, term_top, term_bottom, "keydown");
}
}
},
remove_hover: function() {
// Since we are calling this from setTimeout we need to refer to
// the class variables through the player object
clearTimeout(player.glossary.hoverTimeout);
var mouse_active = player.glossary.hoverDiv.data('mouse-active');
if (mouse_active != 1) {
player.glossary.hoverDiv.css("display", "none");
}
},
init: function(context) {
// By default load asset/terms.js but if you don't need it to
// load then you can pass {terms: false} in the context object
if (context !== undefined && context.terms !== undefined &&
context.terms === false) {
// do nothing if context.terms is false
}
else {
var termsFile = xBookUtils.getBaseUrl() + "asset/terms.js";
var body = document.getElementsByTagName("body")[0];
var script = document.createElement('script');
script.setAttribute('type','text/javascript');
script.setAttribute('src', termsFile);
body.appendChild(script);
}
this.hoverTerms = player.cfg_Glossary_hoverTerms || this.hoverTerms;
this.hoverTermsNoClick = player.cfg_Glossary_hoverTermsNoClick || this.hoverTermsNoClick;
this.useStickyDiv = player.cfg_Glossary_useStickyDiv || this.useStickyDiv;
// Remove the click handler to terms added by init() in the parent class
$("span[data-type='termref']").each(function() {
var $this = $(this);
$this.unbind('click');
$this.attr('tabindex', '0');
});
// Local copy for speed
var $man = $('#manuscript');
if ($man.length > 0) {
// Add div for terms
this.hoverDiv = $('');
$man.append(this.hoverDiv);
var that = this;
this.hoverDiv.mouseenter(function() {
clearTimeout(this.hoverTimer);
$(this).data('mouse-active', '1');
});
this.hoverDiv.mouseleave(function() {
$(this).data('mouse-active', '0');
that.hoverTimer = setTimeout(that.remove_hover, 1000);
});
if (this.useStickyDiv === "on") {
this.stickyDiv = $('');
$man.append(this.stickyDiv);
this.stickyDiv.draggable({
cancel: ".close, .content",
handle: ".top-bar",
containment: "document"
});
this.stickyDiv.delegate('#glossary-sticky-close, #glossary-sticky-kb-close','keydown click', function(event) {
if (event.keyCode == 13 || event.type === "click") {
that.stickyDiv.css('display', 'none');
// return focus to last term clicked
var $last_focus = $('[data-last-focus="1"]');
if ($last_focus.length > 0) {
$last_focus.attr('data-last-focus', '0');
$last_focus.focus();
}
}
});
this.stickyDiv.delegate('#glossary-sticky-kb-leave','keydown click', function(event) {
if (event.keyCode == 13 || event.type === "click") {
// return focus to last term clicked
var $last_focus = $('[data-last-focus="1"]');
if ($last_focus.length > 0) {
$last_focus.attr('data-last-focus', '0');
$last_focus.focus();
}
}
});
} // end if useStickyDiv
$man.delegate('[data-type="termref"]', 'mouseenter mouseleave click keydown', function(event) {
player.glossary.handle_mouse_event(this, event);
});
} // end if manuscript
} // end init
}); // end Glossary
/*
Added by Bruce 11/03/2013
Updates create_links to use the xBookUtils.create_links function,
which adds the following:
- honors the target attribute if set in the XML
- keeps any custom data types set in the XML
To use this updated create_links method add the following *AFTER*
the call to this._super in the initialize method for Player_subtype
in your book's custom js file:
this.xrefs = new XRefs_manuscript_subtype();
*/
var XRefs_manuscript_subtype = XRefs.extend({
create_links: function(jq) {
xBookUtils.create_links(jq);
}
});
// insert Lato font definition
// Note: we need to use the fontsquirrel version so that it works in IE
document.write("");
var Activity_manuscript_type = Activity.extend({
// don't show any initialization alerts for ebook pages
ARGA_initialization_alert: function() {
// ... unless a md value says to override that
if (player.md.show_standard_arga_initialization == "true") {
this._super();
} else {
// we just have to call ARGA_initialization_final
player.activity.ARGA_initialization_final();
}
},
// also don't show grade saved message
show_grade_saved_message: function() {
// ... unless a md value says to override that
if (player.md.show_standard_arga_grade_saved_message == "true") {
this._super();
}
}
});
var Player_manuscript_type = Player.extend({
// Global config variables
// set to "on" to automatically remove wrapper s around rawhtml
removeRawHtmlDivs: "off",
// set to "on" to enable 'show answer' button
showAnswer: "off",
// set to "off" to turn off auto wrapping of answers embedded in xml
showAnswerWrapThis: "on",
// set to "on" to automatically move box title (h3) out of inner box
Box_moveTitle: "off",
// block_types (separated by commas)
Box_moveTitleExclude: "",
// disable in-page nav menu
disableInPageNavMenu: "off",
// Add default page number functionality
pageNumbers: "on",
// Prefix string to be shown before page number in print_page_box
PPB_prefix: "Printed Page ",
// 'Continuation' string to be shown after page number if in print_page_box
PPB_continued: "(cont.)",
// Prefix string to be shown before page number in page_starts
pageStart_prefix: "Page ",
// Hide/Show page numbers menu item in print_page_box
PPM_pageNumberToggle: "on",
// 'Go to page' menu item in print_page_box
PPM_pageJump: "on",
// Needed for LC activity links to b-heads
// Holds the 'anchor_id' passed in via the query string
anchorId: undefined,
// If we're in the PX frame, we have to do a number of special things regarding scrolling and such
// so we'll set fne_jq to the focused & engaged frameset if we're there
fne_jq: null,
scrolling_window_jq: null,
rightnav_page_off_not_showing_html: "◌", // dotted open circle
rightnav_page_off_showing_html: "○", // open circle
rightnav_page_on_html: "●", // closed circle
rightnav_navmode_html: "▣",
last_section_showing: 0,
next_unit_item: function() {
try {
if (top.PxPage == null) {
console.log("Not in PX frameset");
} else {
top.$(top.PxPage.switchboard).trigger("fneclickNextNodeTitle");
}
} catch(e) {
console.log("Not in PX frameset (2)");
}
},
switch_to_px_item: function(itemid) {
try {
if (top.PxPage == null) {
console.log("Not in PX frameset");
} else {
PxPage.openContent({id:itemid});
}
} catch(e) {
console.log("Not in PX frameset (3)");
}
},
show_navigation: function() {
// show read on button -- the text here is configurable for each ebook
var html = UI_Elements.get_button_html({
"id": "readon_button"
, "label": this.md.next_page_button
, "fn": 'player.show_section("next")'
, "extra_class": "readon_button"
});
$("#manuscript").append(html);
// activate button
UI_Elements.activate_buttons();
// if there are no subsections, there's no more navigation to show
if (this.sections.length == 1) {
return;
}
var html = " ";
for (var i = 0; i < this.sections.length; ++i) {
var s = this.sections[i];
var entity = this.rightnav_page_off_not_showing_html;
if (i <= this.last_section_showing) {
entity = this.rightnav_page_off_showing_html;
}
html += " " + entity + " "
}
html += " " + this.rightnav_navmode_html + " "
html += " "; // rightnav
$("#manuscript").append(html);
// activate events for rightnav
// hover/click for pages
$(".rightnav_page").hover(function() {
var jq = $(this);
var section_index = jq.attr("section_index") * 1;
var s = player.sections[section_index];
var title = s.title;
if (s.number != null) {
title = s.number + " " + title;
}
var html = " " + title + " "
+ player.rightnav_page_on_html;
$(this).html(html);
}, function() {
var jq = $(this);
var section_index = jq.attr("section_index") * 1;
if (section_index == player.section_currently_showing) {
jq.html(player.rightnav_page_on_html);
} else {
var entity = player.rightnav_page_off_not_showing_html;
if (section_index <= player.last_section_showing) {
entity = player.rightnav_page_off_showing_html;
}
jq.html(entity);
}
}).click(function() {
var jq = $(this);
var section_index = jq.attr("section_index") * 1;
player.show_section(section_index);
});
// nav mode
$("#rightnav_navmode_button").hover(function() {
var jq = $(this);
// add links to show TOC / toggle nav mode
var html = " "
+ " SHOW ALL SECTIONS "
+ " TABLE OF CONTENTS "
+ " "
;
$("#rightnav_navmode_button_inner").before(html);
$("#show_all_div").click(function() {
player.show_all_sections();
});
$("#show_toc_div").click(function() {
player.show_toc();
});
}, function() {
$(".rightnav_navmode_title").remove();
});
$("#rightnav_navmode_button_inner").click(function() {
player.show_all_sections();
});
// position rightnav
player.update_right_nav();
},
show_all_sections: function(force) {
if (this.last_section_showing == this.sections.length - 1) {
return;
}
// add class to all but the last section
for (var i = 0; i < this.sections.length - 1; ++i) {
this.sections[i].jq.addClass("singlepage");
}
// make sure all sections are represented by rightnav_page_off_showing_html
for (var i = 0; i < this.sections.length; ++i) {
// (except the currently showing section)
if (i != this.section_currently_showing) {
$(".rightnav_page").filter("[section_index=" + i + "]").html(player.rightnav_page_off_showing_html);
}
}
// show all sections
$("[data-type=section]").show();
this.last_section_showing = this.sections.length - 1;
this.last_available_section = this.sections.length;
this.switch_readon_button_to_next_unit_item();
// Check if any figure divs need to be resized
this.resize_figure_divs();
// register the last_available_section in ARGA
// brb: This is throwing a reference error: Set_ARGA_Data is not defined
if (typeof(Set_ARGA_Data) == "function") {
Set_ARGA_Data("LASX", this.last_available_section);
}
this.activity.ARGA_submit_data(false);
safe_log("ARGA_submit_data");
// cancel hover menu
$(".rightnav_navmode_title").remove();
},
show_section: function(section_to_show) {
// if this is the last section, go to the next item in the LP instead of the next section
if (section_to_show == "next" && player.section_currently_showing == (player.sections.length - 1)) {
player.next_unit_item();
} else {
this._super(section_to_show);
}
},
/*
This is the old version of show_section_animate. Keeping it around for
reference if needed.
*/
show_section_animate_old: function(section_to_show, force) {
// parent function includes direction as param; we just define it here
// and include the "force" parameter
var direction;
if (section_to_show == this.section_currently_showing && force != "force") {
return;
} else if (section_to_show < this.section_currently_showing) {
direction = "left";
} else {
direction = "right";
}
// make sure modal window is closed -- not needed unless we bring toc_dialog back
$("#toc_dialog").dialog("close");
// if we're not already showing all sections...
if (this.last_section_showing < this.sections.length - 1 || this.sections.length == 1) {
// if we're showing a later section, make sure that
// all sections between the current and the new section are showing
var i = this.section_currently_showing;
if (i == null) {
i = 0;
}
if (section_to_show >= i) {
for (; i <= section_to_show; ++i) {
if (i < section_to_show) {
this.sections[i].jq.addClass("singlepage");
}
this.sections[i].jq.show();
$(".rightnav_page").filter("[section_index=" + i + "]").html(player.rightnav_page_off_showing_html);
}
}
}
// update last_section_showing if necessary
if (this.last_section_showing < section_to_show) {
this.last_section_showing = section_to_show;
}
// scroll to the top of the section we're jumping to
var scroll_top = Math.round(this.sections[section_to_show].jq.offset().top) - 20;
// have to do this after a delay, in case the PX code is resizing the window
setTimeout("player.scrolling_window_jq.scrollTop(" + scroll_top + ")", 100);
this.section_currently_showing = section_to_show;
// update right nav
this.update_right_nav();
// if this is the last section, change the readon button to go to the next item
if (this.section_currently_showing == (this.sections.length - 1)) {
this.switch_readon_button_to_next_unit_item();
}
player.update_last_viewed_section();
}, // end show_section_animate_old
/*
This is the new version of show_section_animate, updated to work with b-head
links in LC activites
Added by Bruce, 04-06-2014.
*/
/*
section_to_show: (integer) number of the section to scroll to
force:
anchor: (string) ID of element to target(auto-scroll to) on page
*/
show_section_animate: function(section_to_show, force, anchor) {
// parent function includes direction as param; we just define it here
// and include the "force" parameter
var direction;
// section_to_show is usually the number of the section div to
// scroll to. If it is something other than a number then assume it is
// an ID on the page and find the section div number that the ID is located
// in. If anchor was not passed in then set it to the ID passed in through
// section_to_show.
if (!(/^\d+$/.test(section_to_show))) {
if (!xBookUtils.emptyValue(anchor)) {
anchor = section_to_show;
}
section_to_show = xBookUtils.getSectionNumForId(section_to_show);
if (section_to_show === undefined) {
section_to_show = 0;
}
}
// At this point, section_to_show should contain an int, let's make sure
// it does :-)
section_to_show = Number(section_to_show);
if (section_to_show == this.section_currently_showing && force != "force") {
return;
}
else if (section_to_show < this.section_currently_showing) {
direction = "left";
}
else {
direction = "right";
}
// make sure modal window is closed -- not needed unless we bring toc_dialog back
$("#toc_dialog").dialog("close");
// if we're not already showing all sections...
if (this.last_section_showing < this.sections.length - 1 || this.sections.length == 1) {
// if we're showing a later section, make sure that
// all sections between the current and the new section are showing
var i = this.section_currently_showing;
if (i == null) {
i = 0;
}
if (section_to_show >= i) {
for (; i <= section_to_show; ++i) {
if (i < section_to_show) {
this.sections[i].jq.addClass("singlepage");
}
this.sections[i].jq.show();
$(".rightnav_page").filter("[section_index=" + i + "]").html(player.rightnav_page_off_showing_html);
}
}
}
// update last_section_showing if necessary
if (this.last_section_showing < section_to_show) {
this.last_section_showing = section_to_show;
}
// scroll to the top of the section we're jumping to
var scroll_top = Math.round(this.sections[section_to_show].jq.offset().top) - 20;
// Get parent iframe wrapping ebook page. The ebook page will have
// a class of 'show-sec-animate' on . So we look for the iframe
// that contains page.
var $parentIFrame = $();
(function() {
var $iframes;
try {
$iframes = $("iframe", window.parent.document);
}
catch(err) {
$iframes = $();
}
$iframes.each(function(index, element) {
$this = $(element);
try {
var $child = $this.contents().find("html");
if ($child.hasClass("show-sec-animate")) {
$parentIFrame = $this;
return false;
}
}
catch (err) { }
});
})();
var $scrollingWindow;
// If we aren't in a frame or we are in the LC dialog page viewer or
// DF wrapper then the iframe does the scrolling as usual so we want
// to set both the scrollingWindow and parentIframe to .
if ($parentIFrame.length == 0 || xBookUtils.inLCViewer() || xBookUtils.inDFWrapper()) {
// this needs to be $(document) or Chrome won't work correctly
$scrollingWindow = $(document);
$parentIFrame = $scrollingWindow;
}
else {
$scrollingWindow = this.scrolling_window_jq;
}
// Now attempt to scroll to the section by checking to make sure the
// page is tall enough to scroll there. Keep trying until the page
// is long enough or we run out of tries.
if (($parentIFrame.length > 0) && ($scrollingWindow.length > 0)) {
var numIntervalTries = 50;
var scrollTo = scroll_top;
var timerID = setInterval(function(){
numIntervalTries--;
// This is a pure hack for right now, we need to do this to force the
// parent iframe to resize itself to the height of the page. Once
// we get an actual javascript function that we can call to force
// the iframe to resize we can use it instead.
if (xBookUtils.inPXFrame()) {
$("body").trigger('click');
}
// If the height of parentIFrame is larger than where we
// want to scroll to then we are safe to scroll there
if (scrollTo < $parentIFrame.height()) {
$scrollingWindow.scrollTop(scrollTo);
clearInterval(timerID);
}
// Safety shutoff valve to make sure we don't get stuck in an
// infinite loop
else if (numIntervalTries < 1) {
// One last try
$scrollingWindow.scrollTop(scrollTo);
clearInterval(timerID);
}
},100);
}
// If we have an anchor then go to it
if (!xBookUtils.emptyValue(anchor)) {
// We only need to scroll if we are targetting something other
// than section_to_show
if (("digfir_section_" + section_to_show) !== anchor) {
var $anchor = $("#" + anchor);
if ($anchor.length > 0) {
safe_log("found anchor, scrolling to ID " + anchor);
$.scrollTo($anchor, 100);
// we need to wait for all images to load since our images
// don't have heights set on them
$('#manuscript').imagesLoaded(function() {
safe_log("scrolling to anchor " + anchor + " after imagesLoaded");
$.scrollTo($anchor, 100);
});
}
}
}
this.section_currently_showing = section_to_show;
// update right nav
this.update_right_nav();
// if this is the last section, change the readon button to go to the next item
if (this.section_currently_showing == (this.sections.length - 1)) {
this.switch_readon_button_to_next_unit_item();
}
player.update_last_viewed_section();
// Set widths on figure divs if needed. If the image wasn't visible during
// imagesLoaded callback then the div will have data-resize-img-div="1".
this.resize_figure_divs();
}, // end show_section_animate
switch_readon_button_to_next_unit_item: function() {
UI_Elements.update_button_label("readon_button", "Next");
// Add class so we can hide this button in the LC dialog page viewer
$('#readon_button').addClass('label-next');
},
update_right_nav: function() {
$(".rightnav_page").removeClass("rightnav_curpage").each(function(){
var jq = $(this);
var section_index = jq.attr("section_index") * 1;
if (section_index <= player.last_section_showing) {
jq.html(player.rightnav_page_off_showing_html);
} else {
jq.html(player.rightnav_page_off_not_showing_html);
}
});
$(".rightnav_page").filter("[section_index=" + this.section_currently_showing + "]").html(player.rightnav_page_on_html).addClass("rightnav_curpage");
// move interface to the top of the page (setting "bottom" didn't work nearly as well)
var w_top = Math.floor(player.scrolling_window_jq.scrollTop()) + 10;
$("#rightnav").css("top", w_top + "px");
},
page_scrolled: function() {
// if we're currently auto-scrolling, return
if (player.currently_auto_scrolling == true) {
return;
}
// find out where the window is scrolled to
var w_top = Math.floor(player.scrolling_window_jq.scrollTop());
// now find out what section we're in
var section_showing = null;
for (var i = 0; i <= player.last_section_showing; ++i) {
var s = player.sections[i];
var s_top = Math.floor(s.jq.offset().top);
if ((s_top - 200) > w_top && section_showing == null) {
section_showing = i;
}
}
if (section_showing == null) {
section_showing = player.last_section_showing;
} else if (section_showing > 0) {
section_showing -= 1;
}
// special case: if we're scrolled to the very top of the page, we *must*
// be in the first section
if (w_top == 0) {
section_showing = 0;
}
// another special case: if we're scrolled to the very bottom of the page,
// we *must* be in the *last* section
if (w_top > ($("#manuscript").height() - player.scrolling_window_jq.height())) {
section_showing = player.last_section_showing;
}
// save the fact that we're on this section
player.section_currently_showing = section_showing;
player.update_right_nav();
player.update_last_viewed_section();
// process iframes for the section, unless all were preloaded
if (player.md.preload_all_iframes != "true") {
player.figures.process_iframes(player.sections[section_showing].jq);
}
// console.log("w_top: " + w_top + "; ms height: " + $("#manuscript").height() + "; w height: " + scrolling_window_jq.height() + "; bot: " + bot);
},
update_last_viewed_section: function() {
// if ARGA is initialized and we need to restore the user to his last position when he returns,
if (this.ARGA_running && player.md.restore_last_viewed_section == "true") {
// if we're viewing a page other than the one currently saved as LVS (last-viewed section)
var lvs = Get_ARGA_Data("LVSX");
if (this.section_currently_showing != null && this.section_currently_showing != lvs) {
// store LVS/LAS to register the last-viewed/last-available page,
// and save ARGA so it gets registered
Set_ARGA_Data("LVSX", this.section_currently_showing);
Set_ARGA_Data("LASX", this.last_available_section);
this.activity.ARGA_submit_data(false);
}
}
},
show_toc: function() {
// cancel hover menu
$(".rightnav_navmode_title").remove();
$("#toc_dialog").remove();
var html = "";
for (var i = 0; i < this.sections.length; ++i) {
var s = this.sections[i];
var extra_style = "";
if (this.md.sequenced_sections == "true" && i > this.last_available_section) {
extra_style = "toc_section_not_available";
}
html += ""
;
}
if (this.md.sequenced_sections == "true") {
html = " Click on a slide to view it. Note: Grey slides are not accessible until preceding slides have been viewed or completed. " + html;
}
html = " " + html + " ";
$("body").append(html);
$("#toc_dialog").dialog({title:"Jump To...", width:450, modal:true, buttons: [ {text:"OK", click: function() {$("#toc_dialog").dialog("close");}}]});
},
// if we stored the last-viewed section in ARGA the standard way, restore
restore_section: function() {
// this should only be called if ARGA is initialized
var lvs = Get_ARGA_Data("LVSX");
var las = Get_ARGA_Data("LASX");
// if we have a last available section, make sure all sections up to that one are showing
if (las != "") {
this.last_available_section = las * 1;
// note that last_available_section can equal this.sections.length; last_section_showing can't
this.last_section_showing = this.last_available_section;
if (this.last_section_showing >= this.sections.length) {
this.last_section_showing = this.sections.length - 1;
}
// show all sections up to las or sections.length (whichever comes first)
for (var i = 0; i <= this.last_available_section && i < this.sections.length; ++i) {
// don't add the "singlepage" class to the last-showing section
if (i < this.last_section_showing) {
this.sections[i].jq.addClass("singlepage");
}
this.sections[i].jq.show();
$(".rightnav_page").filter("[section_index=" + i + "]").html(player.rightnav_page_off_showing_html);
}
// if we're showing all sections, call switch_readon_button_to_next_unit_item
if (this.last_section_showing == this.sections.length - 1) {
this.switch_readon_button_to_next_unit_item();
}
}
// now go to section 0;
// first set LVS now so that we *don't* save to ARGA in update_section_status --
// the last visited section is actually still what it was before.
Set_ARGA_Data("LVSX", "0");
this.show_section(0);
// if we have a previously-stored LVS, we could in theory show it now, but a) we don't know when
// the highlighting/notes code will complete, and b) it might be disconcerting to users
// if (lvs != "") {
// player.show_section(lvs * 1);
// }
},
extract_activity_metadata: function() {
this._super();
this.required_metadata_val("next_page_button", "Read on", true);
this.required_metadata_val("previous_page_button", "Back up", true);
// by default we do not require students to go through slides in order,
// and we restore them to their last-viewed slide when they return.
this.required_metadata_val("sequenced_sections", "false");
this.required_metadata_val("restore_last_viewed_section", "true");
this.required_metadata_val("section_sequence_message", "You must read each page, and complete any questions on the page, in sequence.");
},
show_activity_questions: function(button) {
var jq = $(button).parents().find("[data-block_type=activity_with_questions]");
$(button).hide();
$("[data-block_type=question_sequence]").show();
},
resize_figure_divs: function() {
// If the image wasn't visible during imagesLoaded callback then
// the div will have data-resize-img-div="1".
var figwidthID_count = 0;
//safe_log("resize_figure_divs: Checking for figures that need div resizing...");
var figwidthID = setInterval(function() {
//safe_log("Checking for figures that need div resizing...");
figwidthID_count += 1;
if (figwidthID_count > 20) {
//safe_log("resize_figure_divs: Stopping figwidth timer after 5 seconds");
clearInterval(figwidthID);
}
// find all figures that still need their divs resized
var $figs = $('[data-resize-img-div="1"]');
// If we don't find any then we are done
if ($figs.length < 1) {
//safe_log("resize_figure_divs: no figures found for resizing");
clearInterval(figwidthID);
}
// otherwise, try to resize them
else {
//safe_log("found " + $figs.length + " figures for resizing...");
$figs.each(function() {
var $this = $(this);
var $img = $this.children('img');
var img_width = $img.width();
if (img_width > 0) {
$this.css("width", img_width + "px");
$this.attr('data-resize-img-div', '0');
//safe_log("resize_figure_divs: resized figure div for " + $this.attr('id') + " to " + img_width + "px");
}
});
}
}, 250);
},
initialize_sections: function() {
// Added by Bruce 11/03/2013
// Save original ID on each section to data-sec-id before _super replaces it.
$('[data-type="section"]').each(function() {
var $this = $(this);
$this.attr('data-sec-id', $this.attr('id'));
});
this._super();
// Add class to body if we are viewing page in supp window
(function() {
try {
var $supp_iframe = $('#xBookSuppWinNavPageFrame', window.parent.document);
if ($supp_iframe.length != 0) {
$("body").addClass("xBookSuppWinNavPage");
}
}
catch(err) { }
})();
if (this.removeRawHtmlDivs === "on") {
$('[data-type="rawhtml"]').each(function() {
var $this = $(this);
var raw_content = $this.contents();
$this.replaceWith(raw_content);
});
}
var boxMoveTitle = this.Box_moveTitle;
if (boxMoveTitle === "all") {
var excludes_array = this.Box_moveTitleExclude.split(",");
$('[data-type="box"]').each(function() {
$this = $(this);
var block_type = $this.attr('data-block_type');
if (!xBookUtils.emptyValue(block_type)) {
if ($.inArray(block_type, excludes_array) >= 0) {
return;
}
}
$this.each(xBookUtils.moveBoxTitle);
});
}
else if (boxMoveTitle !== "off") {
var includes_array = this.Box_moveTitle.split(",");
$('[data-type="box"]').each(function() {
$this = $(this);
var block_type = $this.attr('data-block_type');
if (!xBookUtils.emptyValue(block_type)) {
if ($.inArray(block_type, includes_array) >= 0) {
$this.each(xBookUtils.moveBoxTitle);
}
}
});
}
if (this.showAnswer === "on") {
// Add click handler for show answer buttons
$('.show_answer_button').live("click", function() {
$this = $(this);
// Show Answer button must have a target (this should
// always be the case but we'll check anyway)
var target_id = $this.attr('data-target');
if (xBookUtils.emptyValue(target_id)) {
safe_log("showAnswer: no target set on button");
return;
}
// Make sure target(answer) container exists
var $container = $("#" + target_id);
if ($container.length < 1) {
safe_log("showAnswer: target #" + target_id + " does not exist");
return;
}
// Toggle the 'show_answer_button_on' class for both the
// button and the answer container
$this.toggleClass('show_answer_button_on');
$container.toggleClass('show_answer_button_on');
});
// Look for any elements with data-show-answer
var wrap_answer = this.showAnswerWrapThis;
$('[data-show-answer]').each(function() {
var $this = $(this);
// If we've already found an answer for this element we are done
if ($this.attr('data-found-show-answer') === "true") {
return;
}
var answer_id = $this.attr('id');
var type = $this.attr('data-show-answer');
if (type === "this") {
var this_wrap = $this.attr('data-show-answer-wrap');
$this.attr('data-found-show-answer', "true");
// Do we need to wrap this answer in a box?
if ((wrap_answer === "on" && this_wrap !== "false") ||
this_wrap === "true") {
$this.before(" ");
$this.wrap(function() {
return " ";
});
$this.before(" ");
}
else {
$this.before(" ");
$this.addClass('show_answer_container');
}
}
else if (type === "array") {
// Check if answer is stored in xBookUtils.showAnswers array
$this.each(xBookUtils.checkShowAnswer);
}
});
} // end showAnswer === "on"
}, // end initialize_sections
initialize: function(id) {
xBookUtils.debug("digfir_ebook.js version number: " + xBookUtils.DFVersionNumber);
// activity_questions blocks
$("[data-block_type=activity_with_questions]").each(function(index, element) {
var jq = $(element);
jq.find("[data-block_type=question_sequence]").hide();
var html = UI_Elements.get_button_html({"label":"Show Questions", "fn":'player.show_activity_questions(this)', "extra_class":"activity_questions_button"});
jq.append(html);
});
$(".activity_questions_button").button();
// determine if we're in the F&E window
this.fne_jq = null;
this.scrolling_window_jq = $(window);
// If we are viewing page in LC page viewer then the scrolling window
// is $(window)
if (xBookUtils.inLCViewer()) {
$('body').addClass('lc-dialog-viewer');
// While we are here, let's fix the header
(function() {
try {
var $ui_title = $('.ui-dialog-titlebar .ui-dialog-title', window.parent.document);
if ($ui_title.length > 0) {
var title_html = $ui_title.html();
$ui_title.html(title_html.replace(/<(\/)?(sup|sub|em|i|strong|b)>/g,"<$1$2>"));
}
}
catch(err) { }
})();
}
// otherwise check to see if we are in F&E window
else {
try {
this.fne_jq = $("#fne-content",window.parent.document);
if (this.fne_jq.length == 0) {
this.fne_jq = null;
} else {
this.scrolling_window_jq = this.fne_jq;
}
} catch(e) {}
}
// set an event to keep track of where we are
this.disableInPageNavMenu = this.cfg_disableInPageNavMenu || this.disableInPageNavMenu;
if (this.disableInPageNavMenu !== "on") {
this.scrolling_window_jq.scroll(player.page_scrolled);
}
// We need to do this before _super because the original XRefs.create_links
// will wipe out any custom data-attributes we have set on the link spans
// and it won't honor the target set in the XML.
//
// Really, we should change XRefs.create_links do everything that
// xBookUtils.create_links is doing and then it wouldn't be necessary
// to call this here or even extend XRefs in the first place.
xBookUtils.create_links($("body"));
// Needed for LC activity b-head links
$("html").addClass("show-sec-animate");
var anchor_id = xBookUtils.getURLParameter('anchor_id');
if (anchor_id !== undefined) {
this.anchorId = anchor_id;
}
/* brb: 20140711 - temporary fix for inline tags in PX fne-title */
if (xBookUtils.inPXFrame()) {
(function() {
try {
var $fne_title = $('#fne-title', window.parent.document);
if ($fne_title.length > 0) {
var title_html = $fne_title.html();
$fne_title.html(title_html.replace(/<(\/)?(sup|sub|em|i|strong|b)>/g,"<$1$2>"));
}
}
catch(err) { }
})();
}
this._super(id);
// Add height to around |