\n {!showRichTextButtons && (\n
\n \n \n )}\n
\n
\n {showRichTextButtons && (\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n )}\n {showMarkersButtons &&\n this.props.isNotCompleteForStudent() &&\n !showRichTextButtons && (\n
\n
\n \n
\n \n \n \n this.props.startDrawingMode(\n '#ff8f02'\n )\n }\n />\n \n this.props.startDrawingMode(\n '#000'\n )\n }\n />\n \n this.props.startDrawingMode(\n '#2A3DB9'\n )\n }\n />\n \n \n
\n )}\n
\n {this.props.isNotCompleteForStudent() && !showRichTextButtons && (\n
\n )}\n {this.props.isNotCompleteForStudent() && !showRichTextButtons && (\n
\n )}\n {showAdminSaveButtons && (\n
\n )}\n {showStudentSaveButtons &&\n this.props.isNotCompleteForStudent() &&\n !showRichTextButtons && (\n
\n )}\n {showStudentSaveButtons && !showRichTextButtons && !isIOS && (\n
\n )}\n {this.props.isNotCompleteForStudent() && !showRichTextButtons && (\n
\n )}\n
\n );\n }\n}\n\nProjectToolbar.propTypes = {};\n\nexport default ProjectToolbar;\n","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAARCAYAAAACCvahAAAAiklEQVQ4je3TUQ3CQBAE0NcGA7VwFmoBC1jAQrGABSRgoRqQABYqYfk5kuuF9Ai/MMkke5udnfvYgYT4gtedNS545PqYF8MZS64nDJn2edMUEV7EXLikoj/gjrmvXJuIiCXPJqXbJ84l+w2TJv7iHxZ3XTdiLFqHt+rqJFvxXIWnjuSC08ZPb+XjCV2cbIyiaLRxAAAAAElFTkSuQmCC\"","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNTRmMGUzYi02NjM1LTZkNDAtODc4My1hOWMxMWRlMGM1YjAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RTIwNUZCMzE1NzgyMTFFN0JBQTQ4NEYzOUM4RTIyN0IiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RTIwNUZCMzA1NzgyMTFFN0JBQTQ4NEYzOUM4RTIyN0IiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MTU0ZjBlM2ItNjYzNS02ZDQwLTg3ODMtYTljMTFkZTBjNWIwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE1NGYwZTNiLTY2MzUtNmQ0MC04NzgzLWE5YzExZGUwYzViMCIv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\"","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNTRmMGUzYi02NjM1LTZkNDAtODc4My1hOWMxMWRlMGM1YjAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OEM4MUQ5OTk1NzgyMTFFN0I1OTdGQjM1RDY2NTQyNzciIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OEM4MUQ5OTg1NzgyMTFFN0I1OTdGQjM1RDY2NTQyNzciIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MTU0ZjBlM2ItNjYzNS02ZDQwLTg3ODMtYTljMTFkZTBjNWIwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE1NGYwZTNiLTY2MzUtNmQ0MC04NzgzLWE5YzExZGUwYzViMCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PhYL/QgAAAz9SURBVHja7Fx7UJNXFj8ghAhEYkUUeYSHKFgeCtgKRZCOj11xtftH1XX6GGexO+7adUanpTudjgsq5WFVbLtsd9tqW1uLFVstuqLiqAVEUQwPeSkgCAryVJTwCMme83VxAAPfI/lIaPc3c0dJbm7y/XLOPb9z7vliptVqYTSYmZmBKSMxMXGORCL51srKas4EBF2PWq3u7evrq8Lx8dtvv/2xkHXZeHnCz3gmEMmbJ5PJ8om4keb09vZ2qlSqrUjkv/9P4DCkpKTck0ql07nM7erqKkUyl8bExDQYkkBzGKdITk5O4koewdraeg5aay2+Lt6Qn2NcWiC6rguScRs9l7cBdHd3w927d9Pxv5twncZfpAt/8MEHQf39/esxGHiam5s3Y5DI2LJly+GB5/fs2XODLEpIYKivrwcMLvTnAxxbcXyORGp/EQQmJSV9WFdX9+dbt26ZI3lDnpsyZQooFAqVvb19np2dXaSQ9dvb25kxDOdxbEASb41rArdv334tJycncDhxuuDv7w+zZ8/mtT4GEGhoaBiJmMc43kASvxmXBOLGvvHChQv/oP2JK0i5REREMJbJBbjvAYf1Y5DEpHEXhWtqaj7iQx4B90g4d+4cZGdng0ajGXXuw4cPgeP6iShzVo+rKBwXF1d48eJFf33XCQ0NBScnJ51E37lzh5XkQaDg4pWQkNBs8ha4e/fuSKVS6W+ItXJzc+Hs2bMDEfYJWlpa+JBHsMOxhetkoxLY2NR0prOz02DrUYT94YcfAKP4z5Hh8WNmCMDLXCdaGIu82Ni4Yq3ZhAkB84LgZkU5Xugjg619/fp1QDkEqCHB1dVVyBIeJm2BO3fG+/b29fvS/21tZTA3MBjc3D2YCzYUUGjDqVOnICMjQ9vR0cHbmE2aQFV3j9JSIhkSqJxdFBAY9BzYyeV6r09Wd/z48QH5Ynb06FHGKnnshYdMlsDYuO3pEywsdZafpBMngp//PPCa5Q0WFsJ2F9KHN2/eHEIWifP8/HzaH7X3799nW6IVx99NUsbs3btX2tjUrJJOtGad24eZQ9WtSoyizbzew9PTEw4fPjzq9fj6+kJwcDBYWlrqmvI6iukvTVJIY5SsVCoL4H5TI+tccnHvOb7g86wfSKysOK3v4ODwxHVHKygUFxdDenq6lgoLw5BF5PG5pjEjcNeuXa8UFBS4qFGnVVaUQUmxklN2MGWKPQQFPw/THZ1Y5/LIOGiu2cmTJ+Hy5cua/7m7Csef+F7XmBFYW1t7QKVSPfm7AzVbwdUr0FB/hzXvpH1tptcs8A8IhInWut3fy8sLcnJyeH+uwsJC86ysrD7MWHah9VXxff2Y7IE7duzIO3/+/PMjPU9ShgKHja0t61parQbu1NUyY+Czy2QyKCkpYbIOoQgMDLwfFhbmGBsbqzGpYgKma8H4LT8/2pxHjzpBef0q3K6pYpUaZmYojhXuMC9oPsgmTWIem4jRWx/yCLi9OFRXV//H5Fz43r172TqKmDo39/o7dVBw7Qp0dLDPt7a2gYC5QejWc+H06dMG+ayZmZlL33nnnVCTITAhIeFrpVJpxec13bhPlhQp4WZlObAVV8lae3t6YOHCcJg6daren5cKEeXl5Z+aBIGo+exLS0vXcd1LhqOp8R4GmcvQ0jyy8NX0q6G1tRWkUikEBQVDAFqjZFCGIzCP9t62bZu10Qlsbm6upCqwPqAyfHnZDSi9UQw9aGlDn+uB2ts1Qx5zdHRkrFFXXZArSN6gWlhkVAKTk5P/hpvyZEOt19bawkiee3cHzsS10NOt0unilF34+fnD/PnPMQUFIcAva6bRCETXtcBcdOfwwqa+6Ed3pdSuCDOZHjrbbWhgEeBT4IUXwsDNzZ23FMNtoM5oBHZ0dJSiHBDtHMDS0gIOfXOQKZqySR4S4N7e3hASEgKT/id52DBt2jQ1Wm6GUQhEzfcSbsJeYkZ22hcfPHiABN6E3Nwc4FLrmzTJDkkMhVmzZsMofUgMIiIiDqCYVhuFwLq6uiMCS+icMHPmTMCMZpAAf4S5bB6UlZWySh5yYw8PD8atRzoK9fX1bZ8+fTqvfNhgBMbHx2dhOjVBLPJsbGzgwoULOgU45tmQnf0TRn7WWh8TWCjA+Pr6DSlnkfwJDQ1dM5DKjWkujIFjTl5e3g1906nRMGPGDPjxxx9Z55GU8fGZw0kPkhRCrQqNjY0QFRV19uDBg0sGfzFcYJBDJdR7+WKSp1Ao4Pvvv+eaOjJ5sbe3D6selEisYO7cedDW1qb29PRcJeSz6e3CSUlJqZiuWYtFHpX2qdLCJ6MhCVVcXAT5+VeosZJ1vpubYhe6bpeQz6eXC6PrygsLC9vp5F8s0Mb/3XffCX49U0uc6YUkuem8Fo2mvzUtLc1e194qugWi6ZeLSR5GRNYSPbsA74eKinK4dCmXqVgPtdRe2jPD9FlfMIHUUXX16tVpYpFH1oL59FM5sB45LkNiRUUFQyrBzk5+BL2o3CgEUkcViVoxNd+VK1cMuia5ZU1NNeTkZINK1dX52WefvqzvmoIIjIuLK66srBStkiOXy5muAhGtW+vj4xNhiLV4k4Dp2mKMur5ipmvU4iGgHYMzli1blvb+++9fNwqB9fX1J4dvxoYEHYyfOXNGtPXR8jqdnZ1fNdiXzWcyfmvHi4qKLMW6ODocwoxGNPJIU4aFhb3Kp1hgMAIxWrkWFxf/TkzXpXMNaksTC0uWLPkJhf8xg243XCc2NTWVcGjMEQwXFxc4duyYmOv3enl5rTT4fs0xXUsqKCiQiXVxlC1wKZDqg8jIyLfQdQ0emVhTuZSUFCm6bldtba1oVWa2jip9ER4eXonWzevGEoNVYx4/fly+cOFCs40bNzJlcaoGV1VVMR1O165dAy43xrDtexkZGaKRN3nyZI2fn99vxNoeWC2wrKyMRKfO56giTJIjPT2d9kjBkZfu9RALa9eu/TA1NfWvQrIWgxDY19entrS0nMCWsJ84cQL279/PkMonXUtLSxONvMDAwOasrCwHoWmfQYIIpj2sjSoUBFauXAkHDhyABQsWcHpj6qjCixONvJ9L9/NfApFhzuGbqOe6mJ2dHeXJsH79ek4XKGYVGzXfsYSEhFxTIPAGz0Qd1q1bB2+++eaIc9zd3YG6Q8UC6r0uhUKxGsYAXAgUVFMil16zZs1Tj9NhD91yIKamjIiIiEbN12sqBJ4Vuji5Mmm8waDTNZJBYuHFF1/MT05OPgRjBFYCUWaUagX2qJE1bNiw4cnfdEqmb4l+NDg6OqpRci2HMQSnVA5TLME3sqGUoH4TZm+k8xNDNx0NxuLFi7eh67aYIoGNQt+AiKMGH3JlMfe+0NDQ2/v27YuHMQang3X04AoKbvposra2Nnj33XfB2dmZuZeD2jEoHaT7fAff/iAEKJ+0AQEBvyUxb6oEKvGfFULegLIUsr6RtCGRSU3iX3zxBSiVSkEXsXTp0s/j4+PLwQgw50jgOaFvQASGh4eP+Dz1N5PkoXz6k08+YQ7A+cDPz68dg8cbYCRwLajmCFmcgjcf98QgwAjs1157jdN80pQhISEv8+2oGtNqzEA7hFqtVqEskfJZnPpShB6M0362devWUaP2ihUrTn/11VfLxCDG4K0duCAveUB1Qn26CqKiopjqDrn4COlgt4eHx+/ByOBMIEqZar7Wpy+ot3nPnj06vWLRokV/EdpRZRQC0QKLuM6lfW+g/0RfYISF6OjoIY9FRkYW7t69+3MwAfAhkFPZmIjj+ytEbNi8eTM888wzzP8dHBz6MV2LAhMBZwIzM8/m0sEPm2UZwnV1CfHVq1cPROqEHTt2NJgKgbwaLNesWauVy+2Y6Eg3OA8HBQ0xCCRcvHgRUlNT72ZmZjqNBTGiNFiiG2moFLVp0yZG9A6OsnSmq29KxhLEuoKDg03GdQURaGsr6x4g68iRI8zmXlBQ8CRwCL0zk407HHmUbezcuVNpagTy6tKXSifS2aX7wN90e0BMTAymYi/B8uXL0ULlhvpc9Nst1CBIdf9MtPxWMFGY8yNQek1XLrs35VNYELIEjh4VfEBOpktrx+GgYz0HJO0VHN+YMnm8CbSwsNw//DG53AGqq2uhpaUNojdshrV/iIb6ek73CVOfCvVzvI5jOhIVjGMbjsu018I4Ae/bHFatWqUduB/O2dkFDn6d8VR7h42NNbz33lsQ/cdXhv+gWCGluTjoxx0uIVH9pkqMaL+hGh29QXP7do0ZEdPSqsIgUjLiayPCQ9Vffpl6QiazpYOQU0jY3fFiWaLd6iWTyag8InF1dYfTZ57uqPL0cOvx9vYqdHJyPDR5svxfCoVrF/yCwZtAa2vbdnv7qdMOfftzwKDmoICAZxvd3VyzpjrYf7QvJTGvquo6/FrAm0CJRFKu0Vjahy8MLXR2npGG0uWfiYmxD/Muwa8S/xVgAM2WGZumYW4vAAAAAElFTkSuQmCC\"","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNTRmMGUzYi02NjM1LTZkNDAtODc4My1hOWMxMWRlMGM1YjAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QjlENDY1NDM1NzgyMTFFNzkxMTZFMUYxRjM2RTBERDEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QjlENDY1NDI1NzgyMTFFNzkxMTZFMUYxRjM2RTBERDEiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MTU0ZjBlM2ItNjYzNS02ZDQwLTg3ODMtYTljMTFkZTBjNWIwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE1NGYwZTNiLTY2MzUtNmQ0MC04NzgzLWE5YzExZGUwYzViMCIv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\"","import React from 'react';\nimport FabricUI from './FabricUI';\nimport { round } from 'lodash';\n\nclass BLM extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n pageContainerWidth: 0,\n pageContainerHeight: 0,\n marginLeft: 0,\n pageClassName: '',\n pageWidth: 0,\n pageHeight: 0\n };\n this.resizeProjectPage = this.resizeProjectPage.bind(this);\n }\n componentDidMount() {\n console.log('Project (BLM) component mounted');\n this.resizeProjectPage();\n window.addEventListener('resize', this.resizeProjectPage);\n }\n componentWillUnmount() {\n window.removeEventListener('resize', this.resizeProjectPage);\n console.log('unmounting BLM');\n }\n componentDidUpdate(prevProps) {\n let shouldResize = false;\n if (\n prevProps.leftPageContainerWidth !==\n this.props.leftPageContainerWidth ||\n prevProps.leftPageContainerHeight !==\n this.props.leftPageContainerHeight\n ) {\n shouldResize = true;\n // console.log('leftPageContainer changed in Project');\n }\n if (\n prevProps.blm.Title !== this.props.blm.Title ||\n prevProps.blmMode !== this.props.blmMode\n ) {\n shouldResize = true;\n // console.log('blm changed in project');\n }\n if (prevProps.pagesVisible !== this.props.pagesVisible) {\n shouldResize = true;\n // console.log('pagesVisible changed in project')\n }\n if (shouldResize) {\n this.resizeProjectPage();\n }\n }\n resizeProjectPage() {\n const topToolbarToggle = 40; // and close button\n const bottomToolbarHeight = 120;\n const widthOfArrow = 60;\n const widthOfProjectToolbar = 50;\n let maxContentHeight = 0;\n let maxContentWidth = 0;\n let scalePercent = 1;\n let pageContainerWidth = 0;\n let pageContainerHeight = 0;\n let marginLeft = 0;\n\n const pageWidth = this.props.blm.bounds[0][0];\n const pageHeight = this.props.blm.bounds[0][1];\n\n // if Project is displaying next to a book page, then subtract the page width and the two arrow buttson from the available space\n if (this.props.pagesVisible === 1) {\n maxContentHeight = window.innerHeight - topToolbarToggle;\n maxContentWidth = window.innerWidth - widthOfProjectToolbar - 80; // plus some extra whitespace\n } else {\n maxContentHeight =\n window.innerHeight - topToolbarToggle - bottomToolbarHeight;\n maxContentWidth =\n window.innerWidth -\n this.props.leftPageContainerWidth -\n widthOfArrow * 2 -\n widthOfProjectToolbar;\n marginLeft = this.props.leftPageContainerWidth + 50;\n }\n const heightDiff = pageHeight - maxContentHeight; // difference between the height of the project and the available hight we can display it in\n let widthDiff = pageWidth - maxContentWidth; // difference between the width of the project and the available width we have to display it in\n\n let pageClassName = this.getProjectClassName();\n // if we are lacking mostly height, then scale down by height\n if (heightDiff < widthDiff) {\n // TODO we might be able to increase the available width to display the wide BLM by adding margin top for the close button\n // limit by width\n scalePercent = round(maxContentWidth / pageWidth, 3);\n pageContainerWidth = pageWidth * scalePercent;\n pageContainerHeight = pageHeight * scalePercent;\n // console.log('resizing blm by width', maxContentHeight, maxContentWidth, pageContainerWidth, pageContainerHeight)\n } else {\n // limit by height\n scalePercent = round(maxContentHeight / pageHeight, 3);\n pageContainerWidth = pageWidth * scalePercent;\n pageContainerHeight = pageHeight * scalePercent;\n // console.log('resizing blm by height', maxContentHeight, maxContentWidth, pageContainerWidth, pageContainerHeight)\n }\n this.setState({\n pageContainerWidth,\n pageContainerHeight,\n pageClassName,\n marginLeft,\n pageWidth,\n pageHeight\n });\n this.props.updateProjectScalePercent(scalePercent);\n }\n\n getProjectClassName() {\n let pageClassName = 'blm ';\n if (this.props.pagesVisible === 1) {\n pageClassName += 'single-page';\n }\n return pageClassName;\n }\n\n render() {\n /*\n * size and position self\n * if single page: 'left page-left single-page'\n * if 2 page: 'right page-right blm-page'\n */\n const pagesVisible = this.props.pagesVisible;\n let projectClassName = '';\n if (pagesVisible === 1) {\n projectClassName = 'page left page-left single-page';\n } else {\n projectClassName = 'page right page-right blm-page';\n }\n if (!this.props.blmHtml) {\n return null;\n }\n const pageStyle = {\n width: `${this.state.pageContainerWidth}px`,\n height: `${this.state.pageContainerHeight}px`,\n marginLeft: `${this.state.marginLeft}px`,\n position: 'absolute'\n }; // the width and height will change as we zoom\n const jpedalStyle = {\n width: `${this.state.pageWidth}px`,\n height: `${this.state.pageHeight}px`,\n position: 'relative',\n display: 'block',\n transform: `translateY(0px) translateX(0px) scale(${this.props.projectScalePercent})`,\n transformOrigin: 'top left'\n }; // the scale will change as we zoom.\n return (\n \n
\n {this.getNotesHTML()}\n
this.bookPageTapped(e, pageNumber)}\n onMouseDown={(e) =>\n this.props.pagesOnMouseDown(e, pageNumber)\n }\n onMouseMove={this.props.pagesOnMouseMove}\n onMouseUp={(e) =>\n this.props.pagesOnMouseUp(e, pageNumber)\n }\n onTouchStart={(e) =>\n this.props.pagesOnMouseDown(e, pageNumber)\n }\n onDragStart={(e) => {\n e.preventDefault();\n }}\n />\n
\n
\n );\n }\n}\n\nBookPage.propTypes = {\n pagesOnMouseDown: PropTypes.func.isRequired,\n pagesOnMouseUp: PropTypes.func.isRequired,\n pagesOnMouseMove: PropTypes.func.isRequired,\n pagesVisible: PropTypes.number.isRequired,\n blmMode: PropTypes.bool.isRequired,\n user: PropTypes.object.isRequired,\n highlighterRight: PropTypes.object,\n highlighterLeft: PropTypes.object,\n wordTapped: PropTypes.func.isRequired,\n location: PropTypes.object,\n page: PropTypes.object.isRequired,\n pagesContainerHeight: PropTypes.number.isRequired,\n pagesContainerWidth: PropTypes.number.isRequired,\n updateLeftPageContainer: PropTypes.func.isRequired,\n leftPageContainerWidth: PropTypes.number.isRequired,\n leftPageContainerHeight: PropTypes.number.isRequired,\n updateBookScalePercent: PropTypes.func.isRequired\n};\n\nconst mapStateToProps = (state, ownProps) => {\n return {\n bookToolbar: state.bookToolbar,\n bookView: state.bookView,\n loading: state.ajaxCallsInProgress > 0,\n book: state.book\n };\n};\nexport default connect(mapStateToProps, {\n saveBookItem,\n setActiveNote,\n deleteBookItem,\n startPointing\n})(withRouter(BookPage));\n","/*\n * Pages component is responsible for containing the book and/or project pages.\n * it is also responsible for gestures and changing pages\n * It centers the pages currently only horrizontally ( in the future we would like it to center vertically as well) It positions the left and right arrow buttons.\n * it does this by adding up the width and height of the elements it contains, then centers itself\n * try simply adding up the width of the elements inside and then setting the pages container width.\n * calculate the width of each page by multiplying their bounds by the scalePercent of that page.\n */\n\nimport * as React from 'react';\n\nimport BLM from '../blm/BLM';\nimport BookPage from './BookPage';\nimport { Button } from 'react-bootstrap';\nimport PropTypes from 'prop-types';\nimport { StyledButton } from '../../constants/styledComponents';\nimport UserAPI from '../../api/userAPI';\nimport constants from '../../constants/constants';\nimport icon_arrowleft from '../../images/icon_arrowleft.png';\nimport icon_arrowright from '../../images/icon_arrowright.png';\nimport { viewerModes } from '../../constants/viewerModes';\nimport {map} from 'lodash';\nclass Pages extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n transX: '',\n transY: '',\n scaleX: '',\n scaleY: '',\n pagesContainerWidth: 0,\n pagesContainerHeight: 0,\n leftMarginForArrow: 0,\n dragging: false\n };\n this.handleSwipe = this.handleSwipe.bind(this);\n this.resizePagesContainer = this.resizePagesContainer.bind(this);\n this.pagesOnMouseDown = this.pagesOnMouseDown.bind(this);\n this.pagesOnMouseMove = this.pagesOnMouseMove.bind(this);\n this.pagesOnMouseUp = this.pagesOnMouseUp.bind(this);\n this.shouldShowProject = this.shouldShowProject.bind(this);\n\n this.lastClientX = 0;\n this.lastClientY = 0;\n this.theme = constants.themeProvider.activeTheme;\n }\n\n componentDidMount() {\n // window.addEventListener('resize', this.resizePagesContainer);\n // this.resizePagesContainer();\n }\n\n componentWillUnmount() {\n // window.removeEventListener('resize', this.resizePagesContainer);\n }\n\n /*\n * Step 5 in loading pages container\n *\n */\n componentDidUpdate(prevProps) {\n let shouldResize = false;\n if (\n prevProps.leftPageContainerWidth !==\n this.props.leftPageContainerWidth ||\n prevProps.leftPageContainerHeight !==\n this.props.leftPageContainerHeight\n ) {\n shouldResize = true;\n }\n if (prevProps.pagesVisible !== this.props.pagesVisible) {\n shouldResize = true;\n }\n // we must check for BLM mode changes here because the size and scale do not change when closing a BLM on a wide screen\n if (\n prevProps.blm.Title !== this.props.blm.Title ||\n prevProps.blmMode !== this.props.blmMode\n ) {\n shouldResize = true;\n }\n // if (prevProps.book.ID !== this.props.book.ID || prevProps.book.CurrentPage !== this.props.book.CurrentPage) {\n // this.resizePagesContainer();\n // }\n // if (prevProps.bookScalePercent !== this.props.bookScalePercent){\n // this.resizePagesContainer();\n // }\n // when the project is the right page, we need to resize after it has\n if (prevProps.projectScalePercent !== this.props.projectScalePercent) {\n shouldResize = true;\n }\n if (prevProps.book.bookIsReady !== this.props.book.bookIsReady) {\n shouldResize = true;\n }\n if (prevProps.blm.projectIsReady !== this.props.blm.projectIsReady) {\n shouldResize = true;\n }\n if (shouldResize) {\n this.resizePagesContainer();\n }\n }\n\n /*\n * resize the pages container\n *\n */\n resizePagesContainer() {\n // console.log('resizing pages container')\n // const maxContentHeight = window.innerHeight - 120; // height of window - toolbar and top toggle\n let leftW = 0;\n let leftH = 0;\n // let rightW = this.props.book.bounds[rIndex][0];\n // let rightH = this.props.book.bounds[rIndex][1];\n let rightW = 0;\n let rightH = 0;\n const pagesVisible = this.props.pagesVisible;\n let leftScalePercent = this.props.bookScalePercent; // TODO might rename bookScalePercent to leftPageScalePercent. We are Assuming that the pages of the book are all the same size.\n let rightScalePercent = 0;\n let totalWidthOfPages = 0;\n let tallestPageHeight = 0;\n let widthOfArrow = 50;\n let widthOfProjectToolbar = 50;\n let leftMarginForArrow = 0;\n let gapBetweenPages = 5;\n\n // if blmMode then use the bounds of the BLM instead.\n if (this.props.blmMode && this.props.blm.projectIsReady) {\n if (pagesVisible === 1) {\n leftScalePercent = this.props.projectScalePercent;\n leftW = this.props.blm.bounds[0][0] * leftScalePercent;\n leftH = this.props.blm.bounds[0][1] * leftScalePercent;\n totalWidthOfPages = leftW + widthOfProjectToolbar;\n } else {\n leftW = this.props.leftPageContainerWidth;\n leftH = this.props.leftPageContainerHeight;\n // TODO before determining the rightScalePercent we need to determin if we are limiting by height or width.\n // maybe we calculate both the scalePercent for height and width - then use the greater scaling (resulting in smaller page size)\n rightScalePercent = this.props.projectScalePercent;\n // rightScalePercentWidth = maxContentWidth / rightW;\n rightW = this.props.blm.bounds[0][0] * rightScalePercent;\n rightH = this.props.blm.bounds[0][1] * rightScalePercent;\n totalWidthOfPages =\n leftW + rightW + widthOfArrow + gapBetweenPages;\n leftMarginForArrow = leftW;\n // if the Project is wider than it is tall, switch to single page view automatically\n if (leftW > leftH) {\n this.props.automaticUpdatePagesVisible(1);\n }\n }\n } else if (this.props.book.bookIsReady) {\n if (pagesVisible === 1) {\n leftW = this.props.leftPageContainerWidth;\n leftH = this.props.leftPageContainerHeight;\n totalWidthOfPages = leftW;\n leftMarginForArrow = leftW;\n } else {\n // set both left and right to the leftPageContainer because it is easier and we can assume that both pages of the book are the same size.\n leftW = this.props.leftPageContainerWidth;\n leftH = this.props.leftPageContainerHeight;\n rightW = this.props.leftPageContainerWidth;\n rightH = this.props.leftPageContainerHeight;\n totalWidthOfPages = leftW + rightW + gapBetweenPages;\n leftMarginForArrow = leftW + rightW + gapBetweenPages;\n // if the book page is wider than it is tall, switch to single page view automatically\n // if (leftW > leftH){\n // this.props.automaticUpdatePagesVisible(1);\n // }\n }\n }\n\n tallestPageHeight = leftH > rightH ? leftH : rightH;\n\n this.setState({\n pagesContainerHeight: tallestPageHeight,\n pagesContainerWidth: totalWidthOfPages,\n leftMarginForArrow\n });\n }\n\n handleSwipe(direction) {\n this.props.resetActiveTimeout();\n console.log(`you swiped ${direction}`);\n switch (direction) {\n case 'top':\n break;\n case 'bottom':\n break;\n case 'left':\n this.props.nextPage();\n break;\n case 'right':\n this.props.prevPage();\n break;\n default:\n break;\n }\n }\n\n getBookPage(page, pageID) {\n return (\n
\n );\n }\n\n /*\n * do Not show the book if we are in BLMMode and only 1 page visible\n * wait to show the book until prop.pages is defined\n */\n shouldShowBook() {\n let showBook = false;\n if (this.state.pagesContainerWidth === 0) {\n showBook = false;\n }\n if (!this.props.book.bookIsReady) {\n return false;\n }\n\n // wait for the highlighters to be ready - this might block the book page from loading completely\n // if (!this.props.highlighterLeft || !this.props.highlighterRight){\n // return false;\n // }\n if (this.props.blmMode && this.props.pagesVisible === 1) {\n showBook = false;\n } else if (!!this.props.book.cachedPages) {\n showBook = true;\n } else {\n showBook = false;\n }\n return showBook;\n }\n shouldShowProject() {\n let showProject = false;\n if (\n this.props.blmMode &&\n this.props.blm.bounds &&\n this.state.pagesContainerWidth &&\n this.props.blm.projectIsReady\n ) {\n showProject = true;\n }\n return showProject;\n }\n\n /*\n * shouldShowCLoseButton\n */\n shouldShowCloseButton = () => {\n const { RoleID } = this.props.user;\n if (\n // is a student\n UserAPI.isStudent(RoleID) === true ||\n // is generic and not resource mode\n (UserAPI.isGeneric(RoleID) === true &&\n this.props.viewerMode !== viewerModes.MODE_RESOURCE) ||\n // is demo\n UserAPI.isDemo(RoleID) === true\n ) {\n return true;\n } else {\n return false;\n }\n };\n\n /*\n * respond to mouse down\n * if we are not already dragging and the pointer tool is active, then drag scroll\n */\n pagesOnMouseDown(e, pageNumber) {\n e.persist();\n if (!this.state.dragging && this.props.bookToolbar.pointing) {\n this.setState({ dragging: true });\n this.lastClientX = e.clientX || e.touches[0].clientX;\n this.lastClientY = e.clientY || e.touches[0].clientY;\n const tempX = this.lastClientX;\n const tempY = this.lastClientY;\n\n // if they don't move much, then they are highlighting rather than dragging\n setTimeout(() => {\n const moveX = parseInt(tempX - this.lastClientX, 10);\n const moveY = parseInt(tempY - this.lastClientY, 10);\n if (moveX < 30 || moveY < 30) {\n this.setState({ dragging: false });\n }\n }, 200);\n }\n this.props.onMouseDown(e, pageNumber);\n }\n pagesOnMouseMove(e) {\n e.persist();\n if (this.state.dragging) {\n this.pagesContainer.scrollLeft -=\n -this.lastClientX + (this.lastClientX = e.clientX);\n this.pagesContainer.scrollTop -=\n -this.lastClientY + (this.lastClientY = e.clientY);\n this.mouseMove++;\n }\n this.props.onMouseMove(e);\n }\n\n pagesOnMouseUp(e, pageNumber) {\n if (this.state.dragging) {\n this.setState({ dragging: false });\n }\n\n e.persist();\n this.props.onMouseUp(e, pageNumber);\n }\n\n render() {\n let bookReadyClass = this.shouldShowBook() ? 'book-ready' : '';\n let classes = `pages ${this.props.bookToolbar.pagesClassName} ${bookReadyClass}`;\n let showBookArrows = 'block';\n // TODO canvasClassForBLM is defined in Pages as well as BLM - pick one!\n let canvasClassForBlm, bookArrowLeftStyle, bookArrowRightStyle;\n if (this.props.pagesVisible === 1 && this.props.blmMode) {\n showBookArrows = 'none';\n canvasClassForBlm = 'page single-page blm-canvas-container';\n } else if (this.props.pagesVisible === 2 && this.props.blmMode) {\n canvasClassForBlm = 'page blm-page blm-canvas-container';\n }\n if (this.props.blmMode) {\n classes += ' blm-mode';\n }\n if (this.props.pagesVisible === 1) {\n classes += ' one-visible';\n } else {\n classes += ' two-visible';\n }\n bookArrowLeftStyle = { display: `${showBookArrows}` };\n bookArrowRightStyle = {\n display: `${showBookArrows}`,\n marginLeft: `${this.state.leftMarginForArrow}px`\n };\n const pagesContainerStyle = {\n width: `${this.state.pagesContainerWidth}px`,\n height: `${this.state.pagesContainerHeight}px`\n };\n return (\n
\n {this.shouldShowCloseButton() && (\n
this.props.closeBookView()}\n >\n \n \n )}\n
\n this.props.updatePagesVisible(1)}\n >\n \n \n this.props.updatePagesVisible(2)}\n >\n \n \n
\n
\n
\n
\n
\n
\n
\n
\n\n {this.shouldShowBook() &&\n map(this.props.book.cachedPages, (page, pageID) => {\n return this.getBookPage(page, pageID);\n })}\n\n {this.shouldShowProject() && (\n
\n )}\n
\n
\n );\n }\n}\n\nPages.propTypes = {\n onMouseDown: PropTypes.func.isRequired,\n onMouseUp: PropTypes.func.isRequired,\n onMouseMove: PropTypes.func.isRequired,\n nextPage: PropTypes.func.isRequired,\n prevPage: PropTypes.func.isRequired,\n pagesVisible: PropTypes.number.isRequired,\n blmMode: PropTypes.bool.isRequired,\n user: PropTypes.object.isRequired,\n book: PropTypes.object.isRequired,\n highlighterRight: PropTypes.object,\n highlighterLeft: PropTypes.object,\n wordTapped: PropTypes.func.isRequired,\n location: PropTypes.object,\n updateLeftPageContainer: PropTypes.func.isRequired,\n leftPageContainerWidth: PropTypes.number.isRequired,\n leftPageContainerHeight: PropTypes.number.isRequired,\n bookScalePercent: PropTypes.number.isRequired,\n updateBookScalePercent: PropTypes.func.isRequired,\n updateProjectScalePercent: PropTypes.func.isRequired,\n projectScalePercent: PropTypes.number,\n automaticUpdatePagesVisible: PropTypes.func.isRequired,\n bookToolbar: PropTypes.object.isRequired,\n resetActiveTimeout: PropTypes.func.isRequired,\n viewerMode: PropTypes.string.isRequired\n};\n\nexport default React.forwardRef((props, ref) => {\n return
;\n });;\n","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD0AAABlCAYAAAD+v+tzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjZEMDQ4NEZGRjU0QzExRTY5RUM2RUQ4MzQ0ODNGODQwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjZEMDQ4NTAwRjU0QzExRTY5RUM2RUQ4MzQ0ODNGODQwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NkQwNDg0RkRGNTRDMTFFNjlFQzZFRDgzNDQ4M0Y4NDAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NkQwNDg0RkVGNTRDMTFFNjlFQzZFRDgzNDQ4M0Y4NDAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7G0d7LAAAF50lEQVR42uSc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\"","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD0AAABlCAYAAAD+v+tzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjYzRUU5MTk3RjU0QzExRTY5NjZERjZEQUY4QUIxMzVCIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjYzRUU5MTk4RjU0QzExRTY5NjZERjZEQUY4QUIxMzVCIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NjNFRTkxOTVGNTRDMTFFNjk2NkRGNkRBRjhBQjEzNUIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NjNFRTkxOTZGNTRDMTFFNjk2NkRGNkRBRjhBQjEzNUIiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6Y0xUQAAAF1UlEQVR42uSc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\"","export default __webpack_public_path__ + \"static/media/move_icon.de0c46bd.png\";","import * as React from 'react';\n\nimport {\n Button,\n Col,\n Dropdown,\n FormControl,\n FormGroup,\n Row\n} from 'react-bootstrap';\nimport { StyledButton, StyledDiv } from '../../constants/styledComponents';\n\nimport CommonModal from '../common/CommonModal';\nimport FontAwesome from 'react-fontawesome';\nimport constants from '../../constants/constants';\nimport icon_eraser from '../../images/icon_eraser.png';\nimport icon_hl_green from '../../images/icon_hl_green.png';\nimport icon_hl_orange from '../../images/icon_hl_orange.png';\nimport icon_hl_teal from '../../images/icon_hl_teal.png';\nimport icon_hl_yellow from '../../images/icon_hl_yellow.png';\nimport icon_pen from '../../images/icon_pen.png';\nimport icon_pointer from '../../images/icon_pointer.png';\nimport move_icon from '../../images/move_icon.png';\nimport { Ibook, IbookToolbar, Iuser, IviewerSettings } from '../../models';\nimport { viewerModes } from '../../constants/viewerModes';\n\ninterface Iprops {\n lastPage: ()=>void;\n firstPage: ()=>void;\n nextPage: ()=>void;\n prevPage: ()=>void;\n user: Iuser;\n book: Ibook;\n startHighlighter: (e: any, color: string)=>void;\n startEraser: ()=>void;\n startPointer: ()=>void;\n startNotes: ()=>void;\n openDrawer: ()=>void;\n viewerSettings: IviewerSettings;\n currentPage: number;\n blmMode: boolean;\n pagesVisible: number;\n bookToolbar: IbookToolbar;\n decreaseBookZoom: ()=>void;\n increaseBookZoom: ()=>void;\n resetActiveTimeout: ()=>void;\n viewerMode?: string;\n goToPage: (pageNumber: number, callback?: ()=>void, shouldCheckLeftPageEven?: boolean)=>void;\n bookIsZooming: boolean;\n}\ninterface Istate {\n showGoToPageModal: boolean;\n goToPage: string;\n showBookMarkupTools: boolean;\n}\nconst theme = constants.themeProvider.activeTheme;\n\nclass Toolbar extends React.Component
{\n private startedHighlighting = false;\n\n constructor(props: Iprops) {\n super(props);\n\n this.state = {\n showGoToPageModal: false,\n goToPage: '',\n showBookMarkupTools: this.shouldShowMarkupTools()\n \n };\n this.startedHighlighting = false;\n }\n/**\n * @return {boolean}\n */\n shouldShowMarkupTools = (): boolean => {\n const hasCorrectViewerMode = this.props.viewerMode !==\n viewerModes.MODE_TEACHER_VIEW_STUDENT_BOOK &&\n this.props.viewerMode !==\n viewerModes.MODE_TEACHER_CLASS_NOTES &&\n this.props.viewerMode !==\n viewerModes.MODE_TEACHER_GROUP_NOTES &&\n this.props.viewerMode !==\n viewerModes.MODE_TEACHER_STUDENT_BLM &&\n this.props.viewerMode !== viewerModes.MODE_RESOURCE;\n\n const {IsEPub} = this.props.book;\n return hasCorrectViewerMode && IsEPub === false;\n }\n\n startHL = (e: any, color: string) => {\n // console.log('starting hl', e.type)\n /*\n * stopPropegation rather than preventDefault because preventDefault on iOS leaves the selected text highlighted\n * and we do not want the text to remain highlighted. Then you can't see the color of the highlight.\n * Unfortunately only dong stopPropagation does not prevent this from being clicked twice... so we have a timeout.\n * after further testing apparently we do not need to preventDefault here... Will keep this around for a bit.\n */\n // if (this.startedHighlighting) return;\n // this.startedHighlighting = true;\n // setTimeout(() => {\n // this.startedHighlighting = false;\n // }, 300)\n\n // if (e.type === 'touchend'){\n // e.stopPropagation();\n // }\n // e.preventDefault();\n\n this.props.startHighlighter(e, color);\n };\n closeGoToPageModal = () => {\n this.setState({ showGoToPageModal: false });\n };\n openGoToPageModal = (e: any) => {\n this.props.resetActiveTimeout();\n e.preventDefault();\n this.setState({ showGoToPageModal: true });\n };\n\n submitGoToPage = () => {\n this.props.resetActiveTimeout();\n this.setState(\n {\n showGoToPageModal: false\n },\n () => {\n this.props.goToPage(parseInt(this.state.goToPage, 10));\n this.setState({ goToPage: '' });\n }\n );\n };\n toggleColors = (isOpen: boolean, e: any) => {\n this.props.resetActiveTimeout();\n if (e) {\n e.stopPropagation();\n e.preventDefault();\n }\n };\n\n handleChange = (e: any) => {\n this.setState({\n goToPage: e.target.value\n });\n };\n\n inputForm = () => {\n return (\n \n \n \n \n \n \n
\n );\n };\n\n render() {\n let icon_hl = icon_hl_yellow;\n let toolbarStyle, pointerIcon;\n if (this.props.bookToolbar.highlightColor === 'hl-green') {\n icon_hl = icon_hl_green;\n } else if (this.props.bookToolbar.highlightColor === 'hl-teal') {\n icon_hl = icon_hl_teal;\n }\n if (this.props.bookToolbar.highlightColor === 'hl-orange') {\n icon_hl = icon_hl_orange;\n }\n if (this.props.viewerSettings.showToolbar) {\n toolbarStyle = 'toolbar row';\n } else {\n toolbarStyle = 'toolbar row d-none';\n }\n if (this.props.bookIsZooming) {\n pointerIcon = move_icon;\n } else {\n pointerIcon = icon_pointer;\n }\n const bookToolbar = this.props.bookToolbar;\n const pointerClassName = bookToolbar.pointing\n ? 'item hl-pointer active'\n : 'item hl-pointer';\n const highlighterClassName =\n bookToolbar.highlighting &&\n bookToolbar.highlightColor !== 'hl-strikethrough' &&\n bookToolbar.highlightColor !== 'hl-underline'\n ? `item-child btn btn-light dropdown-toggle hl-highlight active`\n : 'item-child btn btn-light dropdown-toggle hl-highlight';\n const strikeClassName =\n bookToolbar.highlighting &&\n bookToolbar.highlightColor === 'hl-strikethrough'\n ? 'item icon-btn hl-strike active'\n : 'item icon-btn hl-strike';\n const underlineClassName =\n bookToolbar.highlighting &&\n bookToolbar.highlightColor === 'hl-underline'\n ? 'item icon-btn hl-under active'\n : 'item icon-btn hl-under';\n const eraseClassName = bookToolbar.erasing\n ? 'item hl-erase active'\n : 'item hl-erase';\n let notesClassName = bookToolbar.allowNotes\n ? 'item hl-notes active'\n : 'item hl-notes';\n if (this.props.book.IsEPub){\n notesClassName = 'hide'\n }\n // hide the bottom toolbar if in single page Project mode\n if (this.props.blmMode && this.props.pagesVisible === 1) {\n return null;\n }\n return (\n \n \n
\n {this.state.showBookMarkupTools && (\n
\n \n \n \n \n \n \n )}\n {/* notes button stays outside the span above for teachers to add notes */}\n
\n
\n
\n {/*\n \n
\n \n \n \n \n \n \n \n */}\n \n {\n this.props.resetActiveTimeout();\n this.props.decreaseBookZoom();\n }}\n >\n \n \n {\n this.props.resetActiveTimeout();\n this.props.increaseBookZoom();\n }}\n >\n \n \n
\n \n \n \n \n \n \n
\n this.closeGoToPageModal()}\n submit={this.submitGoToPage}\n />\n \n );\n }\n}\n\nexport default Toolbar;\n","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAALEwAACxMBAJqcGAAACcVJREFUeJzdnHlsHcUdxz+zz36nkxBqIGliNy5BIFLVDRjHLeUId6ClUpAooAbR/lF6CakSrVpRUFQh9Y8i1CKUgnoo4OBQG1JIi3AIJCSEBjcoJwYSx3bsGN/n87vf253+YTl1XPt5Z3fWBx/JkrU7853xVzO7v/nNrGGBIyVG/Ay1uS6yVg9mpoM9sotVc92vBUH0JMWpDrplL3Lij9WDle5gpxxmqdd9MLxuwCuGTlIeLKIt4OeSyfeEQPj9fNtK0ZPs4Ekpvfs7hVfCXjLSzP2RMNU+Hz475XM5BjIZNkVW8abuviw4A2Ot/C4c5lfCQc+TCRrMHHcvWk2vrv4sqCkca+WFSMSZeQChMOssyen2Q9ynq08LZgTG29gVDnGbG41cDrq7QEqQsNfw8XDJWprcaC6IEZg4w0G35gEMDY6ZByBgvTQ51v4hj8pae8/SqZjXI1BKjORZDoeClLvVSiRgoH/a2x9YJg+sWkerqu68NVA24k8VcTwY4nK3WpY1NnVNM2+xEeDe0greUtGel1NYNuJPLeKkDvMAhodnNA9gCfCvs4fYoKI97ww8Z15Qz3IsnYZ4zHbxQil4TkV/XhnY20hRajGndJkn5diLQ5FSlcIFyvIe0dtI0eIlnJ5qaeaU0Shks2p1BGpB9rwYgV6Yl8tCNKpeT8JRlfJzbqAX5gEMToj5FNmlUnhODfTKvHhs7OXhBMPgHZXyc/YM7OwkvNjSb55pjoUtDhlYsZbjKhXmxEDZiD+VozEQ1GsejJlnWY6rHxACpYk/61NYd5w3kVQKEnFXEkrPP5hlA700z2HMdx6mTz3hOmsGemkewMjIWLrKBR+VreWMaqVZMdBr87LZsaDZDRJ2OKnnuYFemwcwOOBewxC86qie+6anR0qM1GKOeWlebBQyGdcyLSVXq4Uv43hmoJQYqXZOBANc4VUbpjn27NOAo+kLHhl4zrwQV3qhP87QoKuY7xxOpy94ZGCyjfe9Ni+ZgGRSi9TZFVfR4LSydgMTZzgYClOlW3ci0oKhIW1ydaqrj4loNTB+ht1emwcwPGIrRW8LAbVu6mtbC8fbqA+HuEWX3nRkMmNvXk20lFQ4n76gaQTGW3k1HOJ2HVozoSPmG0dAnVsN1wbGWtkejrDRrY4dog5S9PkwJX93q+HKwFgrf41E9J0zyUcuB1E9MR8AEk6uuoYjbnUcPwPjrWwJR/iB2w7YZch5in5K3L48xnE0AkdbeDIU5sc6OmCHRHws16cTE2p06CiPwFgLj4YjPOb0iJkqlt6YDwABR8oq+FSHltIIHG3mJ+EIv58t8wCGh/Qs1yYiNY0+UDBw9DTfjUR4djbNS6cg7i5FPxVSFLBdl5itKRxr4ZJgiG3CmL3TXFKO7e1qR7C/5Gt8pkvO1gg0DF72+WZ3By8adZ2inxKd0xdsGljo51qdjc6EjhT9dNJGmld0CtoysKCAQp2NzoTumO8cgjdLvoHWB4MtA7MZenQ2mo+Yi2MZMyHhJd2atgxMp3jANA0vxsR5mCaMOD+WMRNRK8pO3aK2DFy0mj27/1352nuHb8SyHB9onxEvYr5xJNSWrUfzekYhDtxwzwcbD524Mvpc3c842610iNMWqeTYSXqvMKT+6QuKK5G2s8GNfUMX8bfXfsgb791NOhPQ0gkp9S/XJtG+soL9XggrzceGhoOtlZVVmwr9/gs7+1Zy9ORVLF08xEVL+1x1YmRYf7JgIgL+tGQFb3uhrZyNSabTj4//HkssonbXA7xc/z2isSWOOpDJwKi+FP2UWAbVXmk7Wpr9+rEnYoFAIDLxmr8wzU2Vu6n8ygcIYf+F3dOt5WRBPg6XVnC1V+KO8oGpdOrdydcy2QD173+Lv/zjR/QMLLOlM6rnWEZ+JNu8lHdkoJnJbZnuXmfvSp5/5ae83XA72dz0CxiPY75xLFPqy7xMhePsyhObf2sZhi9v/aWLB7nrup1cWvL/X5T292k7WZCPXaUV3OFlA443lTKZzIzjZyh6IdveeIgd79xLIvm/R2ZC37GMvAjh3ctjHMcGmmZu+o9HJ3GiqZxnX/45Rz+9GssSDHsb840TLyzgNa8bcb6taUmlLe5kOsTr727kpX/eQu/AIsfN2kUIdiwrR38+exKODZQQVK1TfEEn9XuLefqFO9nbsAbT8u58p2XxomfiE3D8FwghlHKEUkoaPx5BSkEu56P+QDnPVN9Be1ex0y7ko6O0gj1eCE/GsYFGQeEKlfKDA/0Mj5yfaunuv4AtNbfx+p4KUhmtOdtqIfAor3M+jsKYzZs3F5gYGd8MYcw4mUyGMy2nkXnSzEuKEnzn5g9Zs7rDSZfOwzC5fOU6TrkWstOWk0pDQ9Hv2zUPoKe7M695ACOxMC++fj3VO69jJBZ20i0ApOTgbJkHDs/GFAYDv7RbdmRkmITC5u5HTSU0tS1jw3XHqCpvUlpXA2CwVa2CO5Sn8COPPHLZ0i9cfErY2GE3TZPW5iZMh8dJS5f3c89t/2FZse01XyoLyy6tQOM5rvwoT+GCwuBTdswD6O3pdmweQHtXMX+s3kD9gXJyOVupyx2zaR44MDAQCNxgp1wiHieqIVtgWYK9DWt4+oU7Od2eN8sjLclTrhtURH0E+gsXz1RGSklPd6ezHk3DwPAi/lx3E7X1VSSSU2wlCP6g48CkKuovEYlkhmfnQH8fGeeJvkEhaLAsmg2DDillQggxKqUMB/3m8r7BJetqd1VdcXPVR8UlywZCAAieL2nmF04bdIOygelUqi0ciZRNez+dZjDPP6mahk4pqRZC1mzfvv0EzPzdxuN7KTgLXzIt4mWVdKs2qAtlA3PZ1A2ZjO8Tvz8Ymep+T9fMMd8EBkA8GQgUPrd161albSWxnhzQrFLHCxytRBreWv3iroN3bbLExeddHx4apKe7y67M7lwu+2BdXd2cjR4dKBt4/L3SpcLKfQaE9h3+Jn3RryKEIJfL0drchGXjaIEQ1GSz2Qfr6uo0fW80dyi/hYWZ2wSEAG646gBfv3IHVi5Kb3eXLfOAt4uKih76PJgHDkbgiX3LG0BUTrxmSYMjHy/n3YYy0pm8j9VTQlBVU1MzOznpWUDJwGP7Likz8LVMdz+e9LP/0CqOfrIcKSdLy898Pt+N27ZtO+2op/MUpSksMG7Ndz8SyrDh+lM8fN8hvlw6+DEQB7Igan0+3zWfN/NAcQQe3//FGiG530bRXr/Fmt9suTYKUFdX5/X2+ZyhFAcKyWU2imWBTZev7+zX8DHkvEcxkBZymkWCBbQIwRFh8sya9Z0HNPRtQaBmoJTVCNYCCeAUiPcN5J5kJriv4taWWU0jzRf+C2hDy7M0m7eTAAAAAElFTkSuQmCC\"","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MjU2QTczNkYxQkQ1MTFFNzhEQ0E4NTM4MDY1RjQ2MjMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjU2QTczNkUxQkQ1MTFFNzhEQ0E4NTM4MDY1RjQ2MjMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RTZEMDE2OEUxOEFFMTFFNzlCM0ZDNzE0NzI1Rjk2RTQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RTZEMDE2OEYxOEFFMTFFNzlCM0ZDNzE0NzI1Rjk2RTQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4Z7xmGAAASrUlEQVR42uSd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\"","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzAzODk2OEExQkQ1MTFFNzhCNjdFMUJCQUJBRTJEM0UiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MzAzODk2ODkxQkQ1MTFFNzhCNjdFMUJCQUJBRTJEM0UiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QkIwQTQ3NEYxOEFFMTFFN0FFRDVFRkQ4NzZCM0Y5NjYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QkIwQTQ3NTAxOEFFMTFFN0FFRDVFRkQ4NzZCM0Y5NjYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5zgoaNAAAQSElEQVR42uxc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\"","import rangy from 'rangy/lib/rangy-core';\nimport 'rangy/lib/rangy-textrange';\nimport 'rangy/lib/rangy-serializer';\nimport 'rangy/lib/rangy-selectionsaverestore';\nimport 'rangy/lib/rangy-highlighter';\nimport 'rangy/lib/rangy-classapplier';\n\nconst testForSimilarMarkup = (element, highlightColor) => {\n if (\n element.className.includes(highlightColor) &&\n element.textContent !== ' '\n ) {\n // alert('found similar')\n return true;\n } else {\n return false;\n }\n};\n\n// search through all the possible matches, then merge them\nconst findSimilarMarkups = (target, whichSide, pid, highlightColor) => {\n let range = rangy.createRange();\n let currentRange = rangy.createRange();\n let willMergeMarkups = false;\n currentRange.selectNodeContents(target);\n range.selectNode(target); // start with the initial selection of the word\n findPreviousMarkups(target); // increase the range before this markup?\n findNextMarkups(target); // increase the selection range after this markup?\n if (willMergeMarkups) {\n range.select();\n }\n\n function findPreviousMarkups(element) {\n let didFindElement = false; // keep track of if we found one\n let foundElement;\n\n // find elements in non-audio books\n try {\n if (\n testForSimilarMarkup(\n element.previousElementSibling,\n highlightColor\n )\n ) {\n didFindElement = true;\n foundElement = element.previousElementSibling;\n }\n } catch (error) {}\n // find elements that are on different lines\n try {\n if (\n !foundElement &&\n element.parentNode.children.length === 1 &&\n element.parentNode.className.includes('t') &&\n testForSimilarMarkup(\n element.parentNode.previousElementSibling.children[0],\n highlightColor\n )\n ) {\n didFindElement = true;\n foundElement =\n element.parentNode.previousElementSibling.children[0];\n }\n } catch (error) {}\n // find elements in audio books - they are buried in an additional span occassionaly\n try {\n if (\n !foundElement &&\n element.parentNode.children.length === 1 &&\n element.parentNode.className.includes('t') &&\n testForSimilarMarkup(\n element.parentNode.previousElementSibling.children[0]\n .children[0],\n highlightColor\n )\n ) {\n didFindElement = true;\n foundElement =\n element.parentNode.previousElementSibling.children[0]\n .children[0];\n }\n } catch (error) {}\n\n // if we found something, then take that something and see if there is another similar markup before that\n if (didFindElement) {\n willMergeMarkups = true;\n range.setStart(foundElement);\n\n findPreviousMarkups(foundElement);\n }\n }\n\n function findNextMarkups(element) {\n let didFindElement = false; // keep track of if we found one\n let foundElement;\n try {\n if (\n testForSimilarMarkup(element.nextElementSibling, highlightColor)\n ) {\n didFindElement = true;\n foundElement = element.nextElementSibling;\n }\n } catch (error) {}\n\n try {\n if (\n !foundElement &&\n element.parentNode.children.length === 1 &&\n element.parentNode.className.includes('t') &&\n testForSimilarMarkup(\n element.parentNode.nextElementSibling.children[0],\n highlightColor\n )\n ) {\n didFindElement = true;\n foundElement =\n element.parentNode.nextElementSibling.children[0];\n }\n } catch (error) {}\n try {\n if (\n !foundElement &&\n element.parentNode.children.length === 1 &&\n element.parentNode.className.includes('t') &&\n testForSimilarMarkup(\n element.parentNode.nextElementSibling.children[0]\n .children[0],\n highlightColor\n )\n ) {\n didFindElement = true;\n foundElement =\n element.parentNode.nextElementSibling.children[0]\n .children[0];\n }\n } catch (error) {}\n // if we found something, then take that something and see if there is another similar markup AFTER that\n if (didFindElement) {\n willMergeMarkups = true;\n range.setEnd(\n foundElement.firstChild,\n foundElement.firstChild.length\n );\n\n findNextMarkups(foundElement);\n }\n }\n};\n\nexport default findSimilarMarkups;\n","/*\n * Eventually the BookView component will only be responsible for\n */\n\nimport 'rangy/lib/rangy-textrange';\nimport 'rangy/lib/rangy-serializer';\nimport 'rangy/lib/rangy-selectionsaverestore';\nimport 'rangy/lib/rangy-highlighter';\nimport 'rangy/lib/rangy-classapplier';\n\n// import 'react-jplayer/src/less/skins/sleek.less';\n// Styles Play/Pause/Mute etc when icons () are used for them\n// import 'react-jplayer/src/less/controls/iconControls.less';\n\nimport { viewerModes } from '../../constants/viewerModes';\nimport EpubView from './EpubView';\nimport React, { Component } from 'react';\nimport {\n automaticUpdatePagesVisible,\n decreaseBookZoom,\n increaseBookZoom,\n resetBookView,\n setActiveNote,\n updateBookScalePercent,\n updateLeftPageContainer,\n updatePagesVisible,\n updatePendingItem,\n updateProjectScalePercent\n} from '../../actions/bookViewActions';\nimport {\n cacheBookPage,\n deleteBookItem,\n downloadSpeech,\n getBookByID,\n getBookItems,\n getBookItemsByStudentID,\n getBookPage,\n getBookProperties,\n searchBookBagBooks,\n getSpeechMarks,\n getTeacherBookItems,\n nextPage,\n prevPage,\n resetCachedBookPages,\n saveBookItem,\n setBookReady,\n updateBookStatus,\n updateCurrentPage,\n} from '../../actions/bookActions';\nimport {\n emptyBLMs,\n getAssignedBLMs,\n getBLMByID,\n getBLMItems,\n getBLMProperties,\n getBLMStatus,\n getStudentsBLMItems,\n getTeachersComment,\n getTemplateItems,\n setProjectReady\n} from '../../actions/blmActions';\nimport { hashHistory, withRouter } from 'react-router';\nimport {\n manualAjaxEnd,\n manualAjaxStart\n} from '../../actions/ajaxStatusActions';\nimport {\n startErasing,\n startHighlighting,\n startMoving,\n startNotes,\n startPointing,\n startStriking,\n startUnderlining\n} from '../../actions/bookToolbarActions';\n\nimport BlmAPI from '../../api/blmAPI';\nimport CommonModal from '../common/CommonModal';\nimport Drawer from './Drawer';\nimport { HeaderContainer } from '../header/HeaderContainer';\nimport Loading from '../common/Loading';\nimport Pages from './Pages';\nimport Toolbar from './Toolbar';\nimport UserAPI from '../../api/userAPI';\nimport config from '../../api/config';\nimport { connect } from 'react-redux';\nimport constants from '../../constants/constants';\nimport { debounce } from 'lodash';\nimport findSimilarMarkups from '../../utilities/MarkupMergerUtility';\nimport rangy from 'rangy/lib/rangy-core';\nimport { removeQuery } from '../../vendor/utils-router';\nimport { toastr } from 'react-redux-toastr';\nimport { userLogout } from '../../actions/userActions';\nimport { requireSignIn } from '../../routes';\nimport $ from 'jquery';\nimport {\n BOOK_FIRST_PAGE,\n BOOK_LAST_PAGE,\n BOOK_NEXT_PAGE,\n BOOK_PREV_PAGE\n} from './events';\nimport { viewerDrawerTypeEnum } from '../../models-enums';\n\nwindow.rangy = rangy;\n\nclass BookView extends Component {\n highlights;\n activeTimeout;\n activeWarningTimeout;\n\n constructor(props) {\n super(props);\n this.state = {\n highlighterLeft: null,\n highlighterRight: null,\n EPubUrl: '',\n blmHtml: {},\n viewerSettings: {\n showDrawer: false,\n showDrawerHighlightsButton: true,\n showDrawerNotesButton: true,\n drawerType: viewerDrawerTypeEnum.notes,\n highlightingMouseDown: false,\n header: { visible: false, class: 'fa fa-angle-double-down' },\n showToolbar: true,\n showHeader: true,\n highlightingPageNumber: 1,\n loadedProjectAssignmentID: '' // keep track of what Project Assignment the blmData belongs too so we know when to re-load it\n },\n showSaveProjectConfirm: false,\n saveProjectSubmit: () => {\n console.error('saveProjectModal function not replaced');\n }\n };\n this.pagesRef = React.createRef();\n\n this.superDebouncedMarkupUpdate = debounce(this.updateMarkups, 8000);\n this.debouncedMarkupUpdate = debounce(this.updateMarkups, 1000);\n\n // lets store moseMove on the BookView class because we are changing it very quickly as the mouse moves.\n this.mouseMove = 0;\n\n // let speechMarkRaw = constants.testSpeechMarkRaw;\n // this.speechMarksData = speechMarkRaw.split(/\\n/);\n this.highlightedSpeechMarks = [];\n this.speechMarkIndex = 0;\n }\n\n componentDidMount() {\n console.info('bookview mounted');\n if (requireSignIn(this.props.user, this.props.location)) {\n return;\n }\n\n // initialize all rangy stuff\n rangy.init();\n this.setState({\n highlighterRight: this.createHighlighter(),\n highlighterLeft: this.createHighlighter()\n });\n // open the header for testing\n // this.toggleHeader();\n\n /*\n * Initial Viewer Settings\n */\n let initialViewerSettings = {};\n\n // should we show the header?\n initialViewerSettings.showHeader = UserAPI.canAccessBook(\n this.props.user.RoleID\n );\n\n // load list of BLMs and Books?\n if (UserAPI.isStudent(this.props.user.RoleID)) {\n // this.initBooks(); // TODO fix the action in here... it expects a lot of params\n this.initBLMs();\n } else {\n // load blm if you are not a student\n if (this.props.blmMode) {\n if (\n this.props.viewerMode ===\n viewerModes.MODE_TEACHER_STUDENT_BLM\n ) {\n this.loadBLM(\n this.props.projectAssignmentID,\n this.props.blmID\n ).then(() => {\n this.props.setProjectReady(true);\n $('#pages').fadeTo('fast', 1.0);\n // if blmID and No bookID then we will only display a single page blm\n if (!this.props.bookID) {\n this.props.updatePagesVisible(1);\n }\n });\n } else {\n this.loadBLM('', this.props.blmID).then(() => {\n this.props.setProjectReady(true);\n $('#pages').fadeTo('fast', 1.0);\n // if blmID and No bookID then we will only display a single page blm\n if (!this.props.bookID) {\n this.props.updatePagesVisible(1);\n }\n });\n }\n }\n if (\n this.props.viewerMode ===\n viewerModes.MODE_TEACHER_CLASS_NOTES ||\n this.props.viewerMode === viewerModes.MODE_TEACHER_GROUP_NOTES\n ) {\n initialViewerSettings.showDrawerHighlightsButton = false;\n }\n }\n\n initialViewerSettings.hideDrawer = this.shouldHideDrawer();\n\n this.updateViewerSettings(initialViewerSettings, () => {\n console.log('updated initial viewer settings');\n\n // Load Book?\n if (this.props.bookID && this.props.bookID.length) {\n if (this.props.book.ID !== this.props.bookID) {\n this.props.resetCachedBookPages();\n this.loadBook(this.props.bookID).then(() => {\n setTimeout(() => {\n this.props.setBookReady(true);\n // check if the left page is supposed to be even\n this.goToPage(this.props.currentPage, false, false);\n }, 100);\n $('#pages').fadeTo('fast', 1.0);\n });\n return;\n }\n this.loadBook(this.props.bookID).then(() => {\n setTimeout(() => {\n this.props.setBookReady(true);\n // check if the left page is supposed to be even\n this.goToPage(this.props.currentPage, false, false);\n }, 100);\n $('#pages').fadeTo('fast', 1.0);\n });\n }\n });\n\n // listen for active user\n // document.addEventListener(\n // 'visibilitychange',\n // this.handleVisibilityChange,\n // false\n // );\n // this.resetActiveTimeout();\n this.setupEventHandlers();\n }\n\n componentDidUpdate(prevProps) {\n if (prevProps.bookIsZooming !== this.props.bookIsZooming) {\n if (this.props.bookIsZooming) {\n this.props.startMoving();\n } else {\n this.props.startPointing(); // this is a little strange to zoom to normal and then it switches to pointer, but cant think of simple solution\n }\n }\n // if the pages visible changes we need to check if the left page is supposed to be even\n // unless the book or the blm is not ready yet\n if (\n prevProps.pagesVisible !== this.props.pagesVisible &&\n (this.props.blm.projectIsReady || this.props.book.bookIsReady)\n ) {\n this.goToPage(this.props.book.currentPage);\n }\n\n // TODO can we improve this flow by grouping the things to do when a book or blm loads here?\n // if (prevProps.blm.projectIsReady !== this.props.blm.projectIsReady && this.props.blm.projectIsReady){\n // $(\"#pages\").fadeTo(\"fast\", 1.0);\n // }\n // if (prevProps.book.bookIsReady !== this.props.book.bookIsReady && this.props.book.bookIsReady){\n // $(\"#pages\").fadeTo(\"fast\", 1.0);\n // }\n\n if (prevProps.book.ID && !this.props.book.ID) {\n console.log(\n 'book has been reset in bookView, redirecting to bookbag'\n );\n this.goToBag();\n }\n }\n\n componentWillUnmount() {\n setTimeout(() => {\n this.removeAllHighlights();\n }, 100);\n if (this.props.blms.length > 0) {\n this.props.emptyBLMs();\n }\n this.superDebouncedMarkupUpdate.cancel();\n this.debouncedMarkupUpdate.cancel();\n // document.removeEventListener(\n // 'visibilitychange',\n // this.handleVisibilityChange\n // );\n // if (this.activeTimeout) {\n // clearTimeout(this.activeTimeout);\n // }\n // if (this.activeWarningTimeout) {\n // clearTimeout(this.activeWarningTimeout);\n // }\n // if (this.handleTimeoutCloseBook) {\n // clearTimeout(this.handleTimeoutCloseBook);\n // }\n this.removeEventHandlers();\n }\n\n setupEventHandlers() {\n // listen for renewedAzureToken\n document.addEventListener(\n 'renewedAzureToken',\n this.handleRenewedAzureToken\n );\n document.addEventListener('keyup', this.handleKeyPress, false);\n }\n\n removeEventHandlers() {\n document.removeEventListener(\n 'renewedAzureToken',\n this.handleRenewedAzureToken\n );\n document.removeEventListener('keyup', this.handleKeyPress, false);\n }\n\n handleKeyPress = ({ key }) => {\n key && key === 'ArrowRight' && this.nextPage();\n key && key === 'ArrowLeft' && this.prevPage();\n };\n\n /*\n * shouldHideDrawer\n * don't show the left drawer in resource mode or when viewing an ePub\n */\n shouldHideDrawer = () => {\n return (\n this.props.viewerMode === viewerModes.MODE_RESOURCE ||\n this.props.book.IsEPub === true\n );\n };\n\n handleRenewedAzureToken = () => {\n console.log('handling renewed Azure token');\n\n // if( config.System === 'web' && this.props.isOnline){\n // this.props.resetCachedBookPages();\n // this.getBookHTMLPages(this.props.book, this.props.currentPage, true);\n // } else if (config.System !== 'web' && this.props.isOnline){\n\n if (this.props.isOnline) {\n // if on a device we need to re-download the entire book\n this.props.resetCachedBookPages();\n this.loadBook(this.props.bookID).then(() => {\n setTimeout(() => {\n this.props.setBookReady(true);\n // check if the left page is supposed to be even\n this.goToPage(this.props.currentPage);\n }, 100);\n $('#pages').fadeTo('fast', 1.0);\n });\n } else {\n toastr.error(\n `No internet connection.`,\n `Error Loading Book`,\n constants.toastrErrorOptions\n );\n }\n };\n\n // handleVisibilityChange = () => {\n // if (document.hidden) {\n // this.activeTimeout = setTimeout(\n // this.handleTimeoutWarning,\n // constants.closeBookTimeout\n // );\n // console.log('view hidden');\n // } else {\n // console.log('view visible');\n // clearTimeout(this.activeTimeout);\n // clearTimeout(this.activeWarningTimeout);\n // }\n // };\n\n // handleTimeoutWarning = () => {\n // toastr.warning(\n // `Still there?`,\n // `We will be closing the book in 30 seconds due to inactivity`,\n // {\n // ...constants.toastrWarningOptions,\n // id: 'countdownWarning',\n // timeOut: constants.closeBookWarning,\n // onCloseButtonClick: this.resetActiveTimeout\n // }\n // );\n // this.activeWarningTimeout = setTimeout(\n // this.handleTimeoutCloseBook,\n // constants.closeBookWarning\n // );\n // clearTimeout(this.activeTimeout);\n // };\n\n // handleTimeoutCloseBook = () => {\n // clearTimeout(this.activeWarningTimeout);\n // this.props.updateBookStatus(\n // this.props.book.ID,\n // this.props.book.currentPage,\n // this.props.book.pagecount,\n // true,\n // this.props.blmMode\n // );\n // this.closeBookView();\n // toastr.warning(\n // '',\n // \"We have closed the book due to inactivity. Don't worry, we put in a bookmark for ya.\",\n // { ...constants.toastrWarningOptions, timeOut: 0 }\n // );\n // };\n\n resetActiveTimeout = () => {\n console.log('resetting countdown timeout');\n // clearTimeout(this.activeTimeout);\n // clearTimeout(this.activeWarningTimeout);\n // this.activeTimeout = setTimeout(\n // this.handleTimeoutWarning,\n // constants.closeBookTimeout\n // );\n // // toastr.remove('countdownWarning') // not sure why this did not work.\n // toastr.removeByType('warning');\n };\n\n /*\n * function to call from child components to update viewerSettings\n * newViewerSettings = some subset of the viewer settings\n * object that you want to update.\n * callback - setState is asynchronus, so I am allowing an optional callback\n * in case we need to do something AFTER the update completes\n */\n updateViewerSettings = (newViewerSettings, callback) => {\n // let now = new Date().getTime();\n // console.error('UPDATING VIEWER SETTINGS: old, new', now ,this.state.viewerSettings, newViewerSettings );\n\n let viewerSettings = Object.assign(\n {},\n this.state.viewerSettings,\n newViewerSettings\n );\n return this.setState({ viewerSettings }, callback);\n };\n\n /*\n * Gets an array of books\n */\n initBooks = () => {\n if (this.props.books.length === 0) {\n // check if we are online\n if (!this.props.isOnline) {\n // if we do not have books and we are offline, we need to show an error\n console.warn('unable to load bookview while offline');\n toastr.error(\n `No internet connection.`,\n `Error Loading Book`,\n constants.toastrErrorOptions\n );\n return;\n }\n this.props.searchBookBagBooks();\n }\n };\n\n initBLMs = () => {\n console.log('initializing BLMs');\n if (!this.props.isOnline) {\n console.log('offline, skipping getAssigndBlms');\n return;\n }\n this.props\n .getAssignedBLMs(this.props.bookID, this.props.user)\n .then((blms) => {\n // Load BLM?\n if (this.props.projectAssignmentID && this.props.blmMode) {\n this.loadBLM(\n this.props.projectAssignmentID,\n this.props.blmID\n );\n\n // if blmID and No bookID then we will only display a single page blm\n // commented out for now as a student never views a blm by itself\n // if (!this.props.bookID) {\n // initialViewerSettings.pagesVisible = 1;\n // }\n }\n })\n .catch((error) => {\n console.error('Error searching for blm', error);\n if (this.props.isOnline) {\n toastr.error(\n 'Error searching for blm',\n `Error`,\n constants.toastrErrorOptions\n );\n }\n });\n };\n\n /*\n ***************************************** RANGY HIGHLIGHTER ***************************************************\n /*\n\n /*\n * create Rangy highlighter objects for the various markup types and colors\n */\n createHighlighter = () => {\n var newHL = rangy.createHighlighter(this.pagesRef.current, 'TextRange');\n newHL.addClassApplier(rangy.createClassApplier('hl-yellow'));\n newHL.addClassApplier(rangy.createClassApplier('hl-teal'));\n newHL.addClassApplier(rangy.createClassApplier('hl-green'));\n newHL.addClassApplier(rangy.createClassApplier('hl-orange'));\n newHL.addClassApplier(rangy.createClassApplier('hl-underline'));\n newHL.addClassApplier(rangy.createClassApplier('hl-strikethrough'));\n return newHL;\n };\n\n removeAllHighlights = () => {\n this.state.highlighterLeft.removeAllHighlights();\n this.state.highlighterRight.removeAllHighlights();\n };\n\n /*\n * pass in which side then make sure all selections are safe before saving them\n * we have to have two highlighters because this saves all the highlights for the highlighter.\n * this function is performance intensive and blocking\n */\n serializePageMarkups = (side) => {\n let self = this;\n let serializedText = this.state[`highlighter${side}`].serialize({\n serializeHighlightText: true\n });\n /* if we find a \" or a -webkit-transform throw an error */\n if (\n serializedText.indexOf('\"') !== -1 &&\n serializedText.indexOf('-webkit-transform') !== -1\n ) {\n console.error('unable to serialize markup', serializedText);\n alert('Error selecting text, please try again');\n self.state[`highlighter${side}`].unhighlightSelection();\n throw new Error('unable to serialize text');\n } else {\n return serializedText;\n }\n };\n\n /*\n ****************************************************** BOTTOM TOOLBAR *********************************************\n */\n startNotes = (e) => {\n this.resetActiveTimeout();\n if (e) {\n e.preventDefault();\n }\n if (\n !this.props.isOnline &&\n !UserAPI.isStudent(this.props.user.RoleID)\n ) {\n // if we do not have books and we are offline, we need to show an error\n console.warn('unable to create note while offline');\n toastr.error(\n `No internet connection.`,\n `Error Saving Note`,\n constants.toastrErrorOptions\n );\n return;\n }\n this.props.startNotes(true);\n };\n startHighlighter = (e, hlClass) => {\n this.resetActiveTimeout();\n // if this is a touch then we highlight what is selected then turn off the highlight mode.\n // as of 6/2018 we are no longer doing touch highlight mode\n // if (e.type === 'touchend'){\n // this.touchHighlight(e, hlClass);\n // } else {\n this.clickHighlight(e, hlClass);\n // }\n };\n\n // this function is being depricated\n touchHighlight = (e, hlClass) => {\n this.resetActiveTimeout();\n console.log('touch highlight');\n e.preventDefault();\n\n // make sure they selected some text first\n if (window.getSelection().type === 'None') {\n toastr.warning(\n `When using touch, please select text first.`,\n ``,\n constants.toastrWarningOptions\n );\n return;\n }\n this.doTouchHighlight(hlClass);\n };\n\n clickHighlight = (e, hlClass) => {\n this.resetActiveTimeout();\n console.log('started click highlighter ' + hlClass);\n this.props.startHighlighting(true, hlClass);\n };\n\n startEraser = () => {\n this.resetActiveTimeout();\n console.log('started eraser');\n this.props.startErasing(true);\n };\n\n startPointer = () => {\n this.resetActiveTimeout();\n console.log('started pointer');\n this.props.startPointing();\n };\n\n /*\n * Book Navigation (first, last, next, previous)\n */\n firstPage = (e) => {\n this.resetActiveTimeout();\n if (e) e.preventDefault();\n this.goToPage(1);\n document.dispatchEvent(new Event(BOOK_FIRST_PAGE));\n };\n\n lastPage = (e) => {\n this.resetActiveTimeout();\n if (e) e.preventDefault();\n this.goToPage(this.props.book.totalPage);\n document.dispatchEvent(new Event(BOOK_LAST_PAGE));\n // if (this.props.currentPage !== this.props.book.pagecount) {\n // const pi = this.props.book.pagecount - this.props.pagesVisible + 1;\n // this.goToPage(pi);\n // }\n };\n\n nextPage = (e) => {\n this.resetActiveTimeout();\n if (e) e.preventDefault();\n \n let pagesToChange = 1;\n \n if (!this.props.blmMode && this.props.pagesVisible === 2) {\n pagesToChange = 2;\n }\n \n if (this.props.currentPage >= this.props.totalPage) {\n return;\n }\n this.goToPage(this.props.currentPage + pagesToChange);\n document.dispatchEvent(new Event(BOOK_NEXT_PAGE));\n };\n\n prevPage = (e) => {\n this.resetActiveTimeout();\n if (e) e.preventDefault();\n let pagesToChange = 1;\n\n if (!this.props.blmMode && this.props.pagesVisible === 2) {\n pagesToChange = 2;\n }\n\n if (this.props.currentPage <= 0) {\n return;\n }\n this.goToPage(this.props.currentPage - pagesToChange);\n document.dispatchEvent(new Event(BOOK_PREV_PAGE));\n };\n\n /*\n ************************************************* CLICK SWIPE AND DRAG LISTENERS *****************************************\n */\n\n // TODO create a get relative position util function. accepts pageW, pageH, layerX, layerY, offsetX, offsetY, scaleX, scaleY then returns offsetX and offsetY\n\n /*\n * respond to page taps.\n */\n pagesTapped = (e, pageNumber) => {\n this.resetActiveTimeout();\n // console.log('page tapped: ', e.type);\n if (\n e.target.className.includes('tappable') ||\n e.target.parentNode.className.includes('tappable')\n ) {\n this.wordTapped(e, pageNumber);\n }\n\n // on mobile Safari we use the e.originalEvent.layerX (with react e.nativeEvent.layerX) from the touch event, but on Chrome, touch event's don't have this.\n // we don't need it on desktop browsers because they will trigger a click event right after the touch event.\n if (e.type === 'touchend' && !e.nativeEvent.layerX) {\n return;\n }\n };\n\n updateMarkups = (whichSide, pid) => {\n this.updateHighlights(this.serializePageMarkups(whichSide), pid);\n rangy.getSelection().removeAllRanges(); // this only slightly helps to be debounced and we call it unnecessarily when erasing\n this.props.updatePendingItem(false);\n };\n\n /*\n * respond to word taps\n */\n wordTapped = (e, pageNumber) => {\n this.resetActiveTimeout();\n // console.log('word tapped', e.type);\n // if (config.System === 'mobile'){\n // this.props.manualAjaxStart();\n // }\n // console.log($(e.currentTarget).text(), e);\n // console.log(this.state.highlighter);\n // console.log($('.pages .left').attr('id'));\n let erasing = this.props.bookToolbar.erasing;\n const pid = `page${pageNumber}`;\n const whichSide =\n this.props.book.currentPage === pageNumber ? 'Left' : 'Right';\n\n if (this.props.bookToolbar.highlighting) {\n /*\n * Before we added user-select: all to the .t class we had to manually highlight the\n * characters before applying the highlights with the code below:\n */\n // let sel = rangy.getSelection();\n // sel.selectCharacters(e.target, 0, e.target.textContent.length);\n\n /*\n * find similar markups and merge them in order to help iOS users tap to highlight\n */\n findSimilarMarkups(\n e.target,\n whichSide,\n pid,\n this.props.bookToolbar.highlightColor\n );\n\n // console.log('word tapped, highlighting selection', e.target.textContent.length, sel);\n this.state[`highlighter${whichSide}`].highlightSelection(\n this.props.bookToolbar.highlightColor,\n {\n containerElementId: pid\n }\n );\n\n // this is blocking rendering the view, so we wait for view to show highlight before serializing\n // on iOS we need a much longer delay\n this.props.updatePendingItem(true);\n // if (config.System === 'mobile'){\n // this.superDebouncedMarkupUpdate(whichSide, pid);\n // } else {\n this.debouncedMarkupUpdate(whichSide, pid);\n // }\n } else if (this.props.bookToolbar.erasing) {\n /*\n * since a word was tapped, we must manually\n * highlight the characters before applying highlights. only if it is not Mobile\n * we do not support highlighting a single word on mobile. when on mobile we are erasing the entire selection.\n */\n // var sel = rangy.getSelection();\n // sel.selectCharacters(e.target, 0, e.target.textContent.length);\n this.state[`highlighter${whichSide}`].unhighlightSelection();\n\n // this is blocking rendering the view, so we wait for view to show highlight before serializing\n // on iOS we need a much longer delay\n this.props.updatePendingItem(true);\n // if (config.System === 'mobile'){\n // this.superDebouncedMarkupUpdate(whichSide, pid);\n // } else {\n this.debouncedMarkupUpdate(whichSide, pid);\n // }\n\n // turn off erasing\n erasing = false;\n }\n this.updateViewerSettings({ highlighting: false }, () => {\n // if it was a touch event and we just erased something then automatically turn off erasing\n // commented out while we are only doing single tap markups\n if (e.type === 'touchend' && !erasing) {\n // this.startPointer();\n }\n });\n this.props.manualAjaxEnd();\n };\n\n /*\n * respond to mouse down\n */\n onMouseDown = (e, pageNumber) => {\n this.updateViewerSettings({\n highlightingMouseDown: true,\n highlightingPageNumber: pageNumber\n });\n this.mouseMove = 0;\n };\n\n /*\n * respond to mouse up\n */\n onMouseUp = (e, pageNumber) => {\n const pid = `page${pageNumber}`;\n const whichSide =\n this.props.book.currentPage === pageNumber ? 'Left' : 'Right';\n var sel = rangy.getSelection();\n if (\n this.props.bookToolbar.highlighting &&\n this.state.viewerSettings.highlightingMouseDown &&\n this.mouseMove > 1\n ) {\n this.state[`highlighter${whichSide}`].highlightSelection(\n this.props.bookToolbar.highlightColor,\n {\n containerElementId: pid\n }\n );\n this.updateHighlights(this.serializePageMarkups(whichSide), pid);\n // remove the selection from view\n sel.removeAllRanges();\n } else if (\n this.props.bookToolbar.erasing &&\n this.state.viewerSettings.highlightingMouseDown &&\n this.mouseMove > 5\n ) {\n this.state[`highlighter${whichSide}`].unhighlightSelection();\n this.updateHighlights(this.serializePageMarkups(whichSide), pid);\n }\n this.updateViewerSettings({ highlightingMouseDown: false });\n this.mouseMove = 0;\n };\n\n onMouseMove = (e) => {\n this.mouseMove++;\n };\n\n /*\n * doTouchHighlight highlights the text after a touch devices has highlighted a piece of text and then chosen the highlight color\n */\n doTouchHighlight = (highlightClass) => {\n // get the current selection\n var sel = rangy.getSelection();\n const pid = `page${this.state.viewerSettings.highlightingPageNumber}`;\n const whichSide =\n this.props.book.currentPage ===\n this.state.viewerSettings.highlightingPageNumber\n ? 'Left'\n : 'Right';\n this.state[`highlighter${whichSide}`].highlightSelection(\n highlightClass,\n {\n containerElementId: pid\n }\n );\n this.updateHighlights(this.serializePageMarkups(whichSide), pid);\n // remove the selection from view\n sel.removeAllRanges();\n this.startPointer();\n\n // workaround the iOS bug that does not remove the highlight automatically\n $('.pages .left').click();\n };\n /*\n * save the markups to the server by sending all the highlights and the page id (pid) example: \"page13\"\n */\n updateHighlights = (highlight, pid) => {\n const newHighlights = Object.assign(\n {},\n this.props.book.highlights.Content,\n { [pid]: highlight }\n );\n const bookData = Object.assign(\n {},\n {\n Type: 2,\n Content: newHighlights,\n bookID: this.props.book.ID,\n LastDownloadedFromServer:\n this.props.book.highlights.LastDownloadedFromServer\n }\n );\n this.props\n .saveBookItem(\n bookData,\n this.props.user,\n this.props.viewerMode,\n this.props.book.ID\n )\n .then((bi) => {\n console.log('saved book item');\n })\n .catch((error) => {\n console.error('Error saving book item', error);\n toastr.error(\n 'Unable to save markup. Please try again or contact support.',\n `Error Saving`,\n constants.toastrErrorOptions\n );\n });\n };\n\n /*\n ************************************************* DRAWER *******************************************\n */\n goToNote = (note) => {\n this.resetActiveTimeout();\n const noteID = note.ID || note.TempID;\n this.goToPage(note.Page, () => {\n // give the page time to fade in\n setTimeout(() => {\n this.props.setActiveNote(noteID);\n }, 200);\n });\n };\n\n openDrawer = (drawerType = viewerDrawerTypeEnum.notes) => {\n this.resetActiveTimeout();\n const showDrawer = !this.state.viewerSettings.showDrawer;\n if (drawerType !== this.state.viewerSettings.drawerType) {\n this.updateViewerSettings({\n showDrawer: true,\n drawerType: drawerType\n });\n } else {\n this.updateViewerSettings({\n showDrawer: showDrawer,\n drawerType: drawerType\n });\n }\n };\n closeDrawer = () => {\n this.resetActiveTimeout();\n //close the drawer only if it is open this way we do not call updateViewerSettings too often\n if (this.state.viewerSettings.showDrawer) {\n this.updateViewerSettings({ showDrawer: false });\n }\n };\n\n /*\n ****************************************************** HEADER *************************************************\n */\n\n toggleHeader = () => {\n this.resetActiveTimeout();\n const self = this;\n if (this.state.viewerSettings.header.visible) {\n // TODO refactor to CSS animations\n $('.page-header').animate({ top: '-60px' }, 500);\n $('.toggle').animate({ top: '-25px' }, 500);\n self.updateViewerSettings({\n header: { visible: false, class: 'fa fa-angle-double-down' }\n });\n } else {\n $('.page-header').animate({ top: '0px' }, 500);\n $('.toggle').animate({ top: '35px' }, 500);\n self.updateViewerSettings({\n header: { visible: true, class: 'fa fa-angle-double-up' }\n });\n }\n };\n\n /*\n * toggle BLM mode\n */\n\n toggleBLMMode = (blmMode = false) => {\n this.resetActiveTimeout();\n if (this.props.itemSavePending && blmMode === false) {\n this.setState({\n showSaveProjectConfirm: true,\n saveProjectSubmit: () => {\n removeQuery('blmID');\n removeQuery('projectAssignmentID');\n this.props.updatePendingItem(false);\n }\n });\n } else {\n this.props.updatePendingItem(false);\n if (blmMode === false) {\n removeQuery('blmID');\n removeQuery('projectAssignmentID');\n }\n }\n };\n\n closeBookView = () => {\n // students or .... are able to go to the bookbag, everyone else is logged out\n if (this.props.itemSavePending) {\n this.setState({\n showSaveProjectConfirm: true,\n saveProjectSubmit: () => {\n this.props.updatePendingItem(false);\n this.closeBookView();\n this.setState({ showSaveProjectConfirm: false });\n }\n });\n return;\n }\n this.props.resetBookView();\n this.props.startPointing();\n this.props.setBookReady(false);\n this.props.setProjectReady(false);\n $('#pages').fadeTo('fast', 0.0);\n\n if (\n UserAPI.isStudent(this.props.user.RoleID) ||\n UserAPI.isGeneric(this.props.user.RoleID)\n ) {\n this.goToBag();\n } else {\n this.props.userLogout();\n hashHistory.replace('/');\n }\n };\n\n goToBag = () => {\n // TODO @jfbloom22 why do we empty blms?\n if (!this.props.itemSavePending) {\n this.props.emptyBLMs();\n }\n // let the API know user closed the book by passing -1 in as the page index\n this.props\n .updateBookStatus(\n this.props.book.ID,\n -1,\n this.props.book.pagecount,\n false,\n this.props.blmMode\n )\n .then((resp) => {\n console.log('updated book status - close book');\n })\n .catch((err) => {\n console.error('Error updating book status - close book', err);\n // I don't think we want to show an error if this does not work\n // toastr.error(`Please try again or contact support`, `Error Changing Page`, {\n // closeButton: true,\n // showAnimation: 'animated fadeInDown'\n // });\n });\n\n hashHistory.push({\n pathname: '/bag',\n query: this.props.location.query\n });\n };\n /*\n *\n * update the URL, close the drawer, change to the most recent book page\n * TODO going to need to do this for BLM, they will grab a BLM id\n */\n changeBook = (book) => {\n this.resetActiveTimeout();\n if (this.props.itemSavePending) {\n this.setState({\n showSaveProjectConfirm: true,\n saveProjectSubmit: () => {\n this.changeBook(book);\n this.setState({ showSaveProjectConfirm: false });\n this.props.updatePendingItem(false);\n }\n });\n return;\n }\n if (book.IsExternal) {\n // open the book in the Lerner viewer, not in the DiBS viewer\n const externalURL = `${config.API.Main}/book/openexternalviewer?bookID=${book.ID}`;\n var win = window.open(externalURL, '_blank');\n win.focus();\n return;\n }\n this.props.setProjectReady(false);\n this.props.setBookReady(false);\n this.setState({ pages: {} }, () => {\n const query = { ...this.props.location.query, bookID: book.ID };\n hashHistory.replace({\n pathname: `/viewer`,\n query: query\n });\n this.closeDrawer();\n this.loadBook(book.ID).then(() => {\n this.toggleBLMMode(false);\n setTimeout(() => {\n this.props.setBookReady(true);\n }, 100);\n $('#pages').fadeTo('fast', 1.0);\n this.initBLMs();\n });\n });\n };\n\n changeBlm = (projectAssignmentID, blmID) => {\n this.resetActiveTimeout();\n this.closeDrawer();\n this.props.setProjectReady(false);\n this.loadBLM(projectAssignmentID, blmID).then(() => {\n this.props.setProjectReady(true);\n }); // TODO this will get removed when we add some listeners in did receive props\n };\n\n /*\n **************************************************** GENERAL VIEWER FUNCTIONS *******************************************\n */\n\n fadeOut = (duration, cb) => {\n $('#pages').fadeTo(duration, 0.01, cb);\n // TODO verify that we do not need to handle fading out and in better.\n setTimeout(() => {\n $('#pages').fadeTo('fast', 1.0);\n }, 300);\n };\n /*\n * to support images that span two pages, we make sure we are displaying even or odd pages on the left\n * if left page is supposed to be even, if viewing two pages, if not BLM mode, if pi is odd then subtract one\n * if left page is supposed to be odd, and pi is not odd, then subtract one\n */\n checkLeftPageEven = (pi) => {\n function isOdd(num) {\n return num % 2;\n }\n if (\n this.props.book.LeftPageEven &&\n this.props.pagesVisible === 2 &&\n !this.props.blmMode &&\n isOdd(pi)\n ) {\n return pi - 1;\n } else if (\n !this.props.book.LeftPageEven &&\n this.props.pagesVisible === 2 &&\n !this.props.blmMode &&\n !isOdd(pi)\n ) {\n return pi - 1;\n } else {\n return pi;\n }\n };\n\n checkIfPageExists = (pi) => {\n // if the user tries to view a page and has a letter in the search, it will reject the search\n let re = /^\\d+$/;\n if (!re.test(pi)) {\n return false;\n }\n // if it exceeds the page count\n // if it is 0 and we are viewing a single page or in blm mode\n // if it is -1\n if (\n pi > this.props.book.pagecount ||\n (pi <= 0 &&\n (this.props.pagesVisible === 1 || this.props.blmMode)) ||\n pi < 0\n ) {\n return false;\n }\n return true;\n };\n\n /*\n * go to a specific page\n */\n goToPage = (pi, cb, shouldCheckLeftPageEven = true) => {\n this.resetActiveTimeout();\n // only check the left page even if it is not the the initial load of the book\n if (shouldCheckLeftPageEven) {\n pi = this.checkLeftPageEven(pi); // make sure the correct page is displaying on the left\n }\n\n if (!this.checkIfPageExists(pi)) {\n if (!pi || (pi && pi === 0 && this.props.pagesVisible === 1)) {\n pi = 1;\n } else {\n toastr.error(\n `Page does not exist`,\n `The page you are looking for does not exist.`,\n constants.toastrErrorOptions\n );\n return;\n }\n }\n\n if (pi === this.props.currentPage)\n if (!!cb) {\n return cb();\n } else {\n return;\n }\n // close any notes that happen to be open\n this.props.setActiveNote('');\n if (!!this.state.highlighterRight) {\n this.state.highlighterRight.removeAllHighlights();\n }\n if (!!this.state.highlighterLeft) {\n this.state.highlighterLeft.removeAllHighlights();\n }\n const self = this;\n self.fadeOut(200, function () {\n // update the current page number that we are on\n self.props.updateCurrentPage(pi, self.props.book.ID);\n self.props\n .updateBookStatus(\n self.props.book.ID,\n pi,\n self.props.book.pagecount,\n false,\n self.props.blmMode\n )\n .catch((err) => {\n console.warn('Error updating book status', err);\n });\n // sometimes we go to page 0 in order to display an even page on the left side, don't try to get the html\n if (pi !== 0) {\n self.getBookHTMLPages(self.props.book, pi, true);\n } else if (\n pi === 0 &&\n self.props.pagesVisible === 2 &&\n !self.props.blmMode\n ) {\n self.getBookHTMLPages(self.props.book, 1, true);\n }\n\n if (!!cb) {\n window.setTimeout(() => {\n return cb();\n }, 750);\n }\n });\n };\n\n /*\n * Load the selected book\n * step 1 of 5 in loading a book page\n */\n loadBook = (bookID) => {\n $('#pages').fadeTo('fast', 0.0);\n return this.props\n .getBookByID(bookID, this.props.user)\n .then((book) => {\n console.log(\n `opening book: ISBN: ${book.ISBN}, Title: ${book.Title}, ID: ${book.ID}`\n );\n if (book.IsEPub){\n const httpPrefix = config.Debug ? 'http://' : 'https://';\n this.setState({EPubUrl: `${httpPrefix}${window.location.host}${book.EBookPath}/${book.ISBN}`})\n return Promise.resolve(true);\n }\n return this.getBookParts(book);\n })\n .catch((error) => {\n console.error('error loading book', error);\n let message = `We encountered an error loading the book.`;\n if (error.message) {\n message = error.message;\n }\n this.closeBookView();\n if (error.status === 403) {\n this.handleInvalidSession();\n return;\n } else {\n toastr.error(\n `Error`,\n message,\n constants.toastrErrorOptions\n );\n }\n });\n };\n\n /*\n * step 2 of 5 get the book\n * a book has 3 parts, the properties.json file, HTML pages, and book items\n */\n getBookParts = (book) => {\n let data = {};\n // start step 3\n return this.props\n .getBookProperties(book, this.props.user.AzureToken) // get book properties.json (part 1 of 3)\n .then((bookObj) => {\n // bookReducer UPDATE_BOOK_STATUS_SUCCESS deals with this logic, don't return this since we don't care if it fails\n this.props\n .updateBookStatus(\n bookObj.ID,\n 0,\n bookObj.pagecount,\n false,\n this.props.blmMode\n )\n .then((resp) => {})\n .catch((err) => {\n console.error('Error updating book status', err);\n // do not display an error if updating the status fails\n });\n // we are returning the book obj along with the properties\n // start step 4a\n this.getBookHTMLPages(bookObj, this.props.currentPage, true); // get book html pages (part 2 of 3)\n\n // start step 4b - get student book items if user is a teacher viewing student book\n // MODE: teacher token to view a students items and pull up teacher items with that book\n // action: getTeacherBookItems shows the teacher the items they have made\n // teachers will not have downloaded books as they load in with a token (requiring internet support)\n if (\n this.props.viewerMode ===\n viewerModes.MODE_TEACHER_VIEW_STUDENT_BOOK\n ) {\n Object.assign(data, {\n book: book.ID,\n classID: this.props.location.query.classID,\n groupID: this.props.location.query.groupID,\n studentID: this.props.location.query.studentID\n });\n let promises = [];\n promises.push(\n this.props.getBookItemsByStudentID(\n book.ID,\n this.props.location.query.studentID,\n this.props.user\n )\n );\n promises.push(\n this.props.getTeacherBookItems(\n data,\n this.props.user,\n book.ID\n )\n );\n return Promise.all(promises);\n\n // MODE: teacher token to view a group or class and pull up their teacher items with that class or group\n } else if (\n this.props.viewerMode ===\n viewerModes.MODE_TEACHER_GROUP_NOTES ||\n this.props.viewerMode ===\n viewerModes.MODE_TEACHER_CLASS_NOTES\n ) {\n Object.assign(data, {\n book: book.ID,\n classID: this.props.location.query.classID,\n groupID: this.props.location.query.groupID\n });\n return this.props.getTeacherBookItems(\n data,\n this.props.user,\n book.ID\n );\n\n // MODE: teacher has a token to view a students blm, also grab items for the student\n } else if (\n this.props.viewerMode ===\n viewerModes.MODE_TEACHER_STUDENT_BLM\n ) {\n Object.assign(data, {\n book: book.ID,\n classID: this.props.location.query.classID,\n groupID: this.props.location.query.groupID,\n StudentID: this.props.location.query.studentID\n });\n let promises = [];\n promises.push(\n this.props.getBookItemsByStudentID(\n book.ID,\n this.props.location.query.studentID,\n this.props.user\n )\n );\n promises.push(\n this.props.getTeacherBookItems(\n data,\n this.props.user,\n book.ID\n )\n );\n return Promise.all(promises);\n } else if (UserAPI.isStudent(this.props.user.RoleID)) {\n // TODO 2/16/18 determine this is online\n // start step 4c - get the book items and set them in the redux store\n // action: getBookItems shows the student the items they have made in the book\n return this.props\n .getBookItems(\n book.ID,\n this.props.user,\n this.props.downloadedBooks\n )\n .then(() => {\n // if offline and there are no highlights and no notes - then show an error. TODO add support for doing markups in multiple books while offline\n // if (!this.props.isOnline && !this.props.book.highlights.Content && this.state.notes.length === 0 ){\n // this.updateViewerSettings({showToolbar: false});\n // throw {message: 'Please connect to the internet and re-open this book before doing any markups.'};\n // }\n }); // get book items (part 3 of 3)\n // TODO 2/16/18 create another if statment determining if the user is a student and we are offline -->\n // Load from downloaded books in redux where we have book items\n } else if (UserAPI.isGeneric(this.props.user.RoleID)) {\n // console.log('not getting book items when a generic user.')\n return Promise.resolve(true);\n } else if (this.props.viewerMode === viewerModes.MODE_GENERIC) {\n // console.log('not getting book items when in generic mode.')\n return Promise.resolve(true);\n } else if (\n this.props.viewerMode === viewerModes.MODE_RESOURCE\n ) {\n console.log('resource mode');\n return Promise.resolve(true);\n } else {\n return Promise.reject({\n message: `not getting book items for this user: ${this.props.user.LoginID}`\n });\n }\n });\n };\n\n /*\n * step 4a grab the HTML assets for the page passed in (left page) plus the nextpage (right page)\n * then wait 1.5 seconds and cache the next 2 pages, wait 2 seconds and cache the previous 2 pages\n *\n */\n getBookHTMLPages = (book, pi, cache) => {\n if (book.IsEPub) {\n return;\n }\n const lIndex = pi; // left page index\n const lpageKey = `page${lIndex}`; // left page key\n const rIndex = lIndex + 1; // right page index we manually add 1 to get the right pi\n const rpageKey = `page${rIndex}`; // right page key\n const pageIndexToCache = lIndex + this.props.pagesVisible;\n const previousPageIndexToCache = lIndex - this.props.pagesVisible;\n const pageIndexToCacheTwo = lIndex + this.props.pagesVisible * 2;\n\n // if left page does not already exist in state, then grab it from the server\n if (!this.props.book.cachedPages[lpageKey]) {\n this.getBookHTMLPage(lpageKey, book, lIndex);\n }\n // if right page does not already exist in state and the right page index is less then or equal to the total pages in the book\n // then grab the book page for the right side\n // TODO how does this work when we are in single page view?\n if (\n !this.props.book.cachedPages[rpageKey] &&\n rIndex <= book.pagecount\n ) {\n this.getBookHTMLPage(rpageKey, book, rIndex);\n }\n\n /*\n * should we cache and is the page index less than the total pages in the book\n * if yes and yes then cache them\n */\n if (cache && lIndex < book.pagecount - 1) {\n setTimeout(() => {\n this.getBookHTMLPages(this.props.book, pageIndexToCache, false);\n }, 1500);\n }\n\n /*\n * should we cache and is the page index more than 2\n * this caches the previous two pages\n */\n if (cache && lIndex > 2) {\n setTimeout(() => {\n this.getBookHTMLPages(\n this.props.book,\n previousPageIndexToCache,\n false\n );\n }, 2500);\n }\n\n /*\n * should we cache and is the page index less than 4 pages ahead of the current index and less than the total pages in the book\n * this caches up to 4 pages ahead of the current viewer page index\n */\n if (\n cache &&\n lIndex - this.props.currentPage < 4 &&\n pageIndexToCacheTwo <= book.pagecount - 1\n ) {\n setTimeout(() => {\n this.getBookHTMLPages(\n this.props.book,\n pageIndexToCacheTwo,\n false\n );\n }, 2000);\n }\n };\n\n /*\n * Get the assets for a single book HTML page and store them in cachedPages\n */\n getBookHTMLPage = (pageKey, bookObj, index) => {\n if (index === 0) {\n this.props.cacheBookPage({\n pageKey,\n pageHTML: '
',\n pageNumber: 0,\n width: bookObj.bounds[0][0],\n height: bookObj.bounds[0][1]\n });\n return;\n }\n // since the book pages are so temporary we do not store them in Redux\n return this.props\n .getSpeechMarks(bookObj, index, this.props.user.AzureToken, pageKey)\n .then(() => {\n return this.props\n .getBookPage(\n this.props.book,\n index,\n this.props.user.AzureToken,\n pageKey\n )\n .then((pageHTML) => {\n // console.info('retrieved book page', pageData);\n if (!pageHTML) {\n console.error('failed to process page', pageHTML);\n throw new Error({\n message: 'failed to process page'\n });\n }\n this.props.cacheBookPage({\n pageKey,\n pageHTML,\n pageNumber: index,\n width: bookObj.bounds[index - 1][0],\n height: bookObj.bounds[index - 1][1]\n });\n });\n })\n .catch((error) => {\n // check if we are online\n if (!this.props.isOnline) {\n // if we do not have books and we are offline, we need to show an error\n console.warn('unable to load page while offline');\n toastr.error(\n `No internet connection.`,\n `Error Loading Book Page`,\n constants.toastrErrorOptions\n );\n return;\n }\n console.error('Error trying to get book page', error);\n if (error.status === 403) {\n this.handleInvalidSession();\n } else {\n toastr.error(\n `We encountered an error getting the book page.`,\n `Error Getting Page`,\n constants.toastrErrorOptions\n );\n }\n });\n };\n\n loadBLM = (projectAssignmentID, blmID) => {\n // if the projectAssignmentID is empty, the current object is the blmID or vice versa\n // For example: blmCreator does not have a projectAssignmentID, only a blmID\n // A student does not use a blmID only a projectAssignmentID\n let currentObjectID;\n if (this.props.itemSavePending) {\n this.setState({\n showSaveProjectConfirm: true,\n saveProjectSubmit: () => {\n this.loadBLM(projectAssignmentID, blmID);\n removeQuery('blmID');\n this.setState({ showSaveProjectConfirm: false });\n this.props.updatePendingItem(false);\n }\n });\n return Promise.reject({ message: 'not saved' });\n }\n\n const query = {\n ...this.props.location.query,\n blmID,\n projectAssignmentID\n };\n hashHistory.replace({\n pathname: `/viewer`,\n query: query\n });\n\n // projectAssignmentID is empty here for a creator\n if (projectAssignmentID === '') {\n currentObjectID = blmID;\n } else {\n currentObjectID = projectAssignmentID;\n }\n return this.getBLMObject(currentObjectID)\n .then((blm) => {\n // need to not fade out if we have already loaded the blm\n if (\n this.props.projectAssignmentID &&\n this.props.blm.projectAssignmentID !==\n blm.projectAssignmentID\n ) {\n this.fadeOut(600, () => {});\n return this.getBLMParts(blm);\n } else {\n return this.getBLMParts(blm);\n }\n })\n .catch((err) => {\n console.error('unable to load BLM', err);\n toastr.error(\n ``,\n `Error Loading BLM`,\n constants.toastrErrorOptions\n );\n throw err;\n });\n };\n\n getBLMObject = (projectAssignmentID) => {\n let blm = this.props.blm;\n\n // if the book is already in redux as the active book (this happens after token login and page refresh)\n // For a student, we check to see if projectAssignmentID equals the paID on the blm\n // For an admin making a blm, we do not have a projectAssignmentID, so we pass a blmID\n // For a teacher, we do not have a projectAssignmentID on the blm but we have it in props\n if (this.props.viewerMode === viewerModes.MODE_TEACHER_STUDENT_BLM) {\n if (this.props.projectAssignmentID === projectAssignmentID) {\n return Promise.resolve(blm);\n }\n } else {\n if (\n blm.projectAssignmentID === projectAssignmentID ||\n blm.ID === projectAssignmentID\n ) {\n return Promise.resolve(blm);\n }\n }\n\n blm = this.props.blms.filter((b) => {\n return b.projectAssignmentID === projectAssignmentID;\n })[0];\n if (blm) {\n return Promise.resolve(blm);\n } else {\n // console.error('unable to find the project in the array of projects');\n // return reject(false);\n // TODO enable downloaded BLMs ????\n // if we are on mobile or chrome then try to find it in the array of downloaded blms\n // if (config.System === 'chrome' || config.System === 'mobile'){\n // blm = this.props.downloadedBlms.filter((b) => { return b.ID === blmID })[0];\n // if (blm){\n // console.log('have blm in downloadedBooks');\n // resolve(blm);\n // }\n }\n if (this.props.isOnline) {\n // we are online so grab it from the API\n return this.props\n .getBLMByID(this.props.blmID, this.props.user)\n .then((result) => {\n // this.loadBook(bookSaved);\n // book = result;\n return result;\n });\n } else {\n return Promise.reject(false);\n }\n };\n\n getBLMParts = (blm) => {\n return this.props\n .getBLMProperties(blm, this.props.user.AzureToken) // step 1, get blm properties\n .then((blmObj) => {\n if (!this.props.blmMode) {\n this.toggleBLMMode(true);\n }\n this.getBlmHtml(blmObj); // step 2, get blm html\n\n if (UserAPI.isStudent(this.props.user.RoleID)) {\n return this.props\n .getBLMStatus(blm, this.props.user)\n .then(() => {\n return this.props.getTeachersComment(\n blm.projectAssignmentID,\n blm.TeacherID,\n this.props.user\n );\n })\n .then(() => {\n return this.props.getBLMItems(\n blmObj.ID,\n blm.projectAssignmentID,\n this.props.user\n );\n })\n .then(() => {\n this.updateViewerSettings({\n loadedProjectAssignmentID:\n blm.projectAssignmentID\n });\n });\n }\n\n // MODE: teacher has token to view a students blm, this call grabs the students blm items\n if (\n this.props.viewerMode ===\n viewerModes.MODE_TEACHER_STUDENT_BLM\n ) {\n return this.props\n .getStudentsBLMItems(\n blm.ID,\n this.props.projectAssignmentID,\n this.props.location.query.studentID,\n this.props.user\n )\n .then(() => {\n this.updateViewerSettings({\n loadedProjectAssignmentID:\n blm.projectAssignmentID\n });\n });\n\n // MODE: teacher has token to create a blm template. They find any items they have previously made with the template\n } else if (\n this.props.viewerMode ===\n viewerModes.MODE_ADMIN_CREATE_TEMPLATE\n ) {\n return this.props\n .getTemplateItems(blmObj.ID, this.props.user)\n .then(() => {\n this.updateViewerSettings({\n loadedProjectAssignmentID:\n blm.projectAssignmentID\n });\n });\n }\n })\n .catch((error) => {\n console.error('Error getting BLM', error);\n if (error.status === 403) {\n this.handleInvalidSession();\n } else {\n toastr.error(\n `We encountered an error loading the BLM.`,\n `Error Loading BLM`,\n constants.toastrErrorOptions\n );\n }\n });\n };\n handleInvalidSession = () => {\n toastr.warning(\n `Trying to fix an issue with the book.`,\n `Please wait a moment`,\n constants.toastrWarningOptions\n );\n };\n\n getBlmHtml = (blm) => {\n // console.log(blm, 'BLM getting html');\n // passing in blm, 1 (because there is always only 1 page of the BLM, and the azure token)\n BlmAPI.getBlmPageRemote(blm, 1, this.props.user.AzureToken)\n .then((blmPage) => {\n this.setState({ blmHtml: blmPage });\n })\n .catch((error) => {\n // check if we are online\n if (!this.props.isOnline) {\n // if we do not have books and we are offline, we need to show an error\n console.warn('unable to load BLM while offline');\n toastr.error(\n `No internet connection.`,\n `Error Loading BLM`,\n constants.toastrErrorOptions\n );\n return;\n }\n console.error('Error trying to get BLM', error);\n if (error.status === 403) {\n this.handleInvalidSession();\n } else {\n toastr.error(\n `We encountered an error getting the BLM.`,\n `Error Getting BLM`,\n constants.toastrErrorOptions\n );\n }\n });\n };\n\n render() {\n if (requireSignIn(this.props.user, this.props.location)) {\n return null;\n }\n let books;\n if (this.props.isOnline) {\n books = this.props.books;\n } else {\n books = this.props.downloadedBooks;\n }\n return (\n \n {this.props.book.IsEPub && this.state.EPubUrl && (\n
\n \n console.log(epubcifi)\n }\n tocChanged={(toc) => console.log(toc)}\n closeBookView={this.closeBookView}\n // audioChanged={this.props.updateBookAudioUrl}\n />\n
\n )}\n {this.props.book.IsEPub === false && (\n
\n )}\n
\n {this.state.viewerSettings.showHeader && (\n \n )}\n\n \n \n
\n
\n
{\n this.setState({ showSaveProjectConfirm: false });\n }}\n submit={this.state.saveProjectSubmit}\n />\n \n );\n }\n}\n\nconst mapStateToProps = (state, ownProps) => {\n return {\n user: state.user,\n highlights: state.highlights,\n book: state.book,\n books: state.books,\n completedBLMs: state.completedBLMs,\n blms: state.blms,\n blm: state.blm,\n blmProperties: state.blmProperties,\n isOnline: state.offlineQueue.isOnline && navigator.onLine,\n loading: state.ajaxCallsInProgress > 0,\n downloadedBooks: state.downloadedBooks,\n currentPage: state.book.currentPage,\n totalPage: state.book.pagecount,\n leftPageContainerWidth: state.bookView.leftPageContainerWidth,\n leftPageContainerHeight: state.bookView.leftPageContainerHeight,\n bookScalePercent: state.bookView.bookScalePercent,\n projectScalePercent: state.bookView.projectScalePercent,\n blmMode: !!(\n ownProps.location.query.blmID &&\n ownProps.location.query.blmID.length > 0\n ),\n pagesVisible: state.bookView.pagesVisible,\n bookID: ownProps.location.query.bookID,\n viewerMode: ownProps.location.query.viewerMode,\n projectAssignmentID: ownProps.location.query.projectAssignmentID,\n blmID: ownProps.location.query.blmID,\n bookToolbar: state.bookToolbar,\n bookIsZooming: state.bookView.bookManualZoomLevel !== 1,\n itemSavePending: state.bookView.itemSavePending\n };\n};\n\nexport default connect(mapStateToProps, {\n userLogout,\n getBookProperties,\n getBookItems,\n getBookItemsByStudentID,\n getTeacherBookItems,\n getBookPage,\n getSpeechMarks,\n searchBookBagBooks,\n saveBookItem,\n deleteBookItem,\n updateBookStatus,\n getAssignedBLMs,\n getBLMProperties,\n getBLMStatus,\n getTeachersComment,\n emptyBLMs,\n getBLMItems,\n getStudentsBLMItems,\n getTemplateItems,\n getBookByID,\n getBLMByID,\n manualAjaxStart,\n manualAjaxEnd,\n updateCurrentPage,\n nextPage,\n prevPage,\n cacheBookPage,\n updateLeftPageContainer,\n updateBookScalePercent,\n updateProjectScalePercent,\n updatePagesVisible,\n resetBookView,\n automaticUpdatePagesVisible,\n setBookReady,\n setProjectReady,\n startNotes,\n startPointing,\n startHighlighting,\n startErasing,\n startUnderlining,\n startStriking,\n setActiveNote,\n increaseBookZoom,\n decreaseBookZoom,\n startMoving,\n downloadSpeech,\n updatePendingItem,\n resetCachedBookPages,\n})(withRouter(BookView));\n","import * as React from 'react';\nimport { FormGroup, FormControl, Row, Col } from 'react-bootstrap';\n\nclass UserProfileForm extends React.Component {\n render() {\n return (\n \n \n {this.props.forms.map((form) => {\n return (\n \n \n \n );\n })}\n \n \n Need to update your password?\n \n
\n \n Enter the new one twice above, otherwise leave it\n blank.\n \n
\n \n
\n );\n }\n}\n\nexport default UserProfileForm;\n","import * as types from '../actionTypes';\n\nexport const toggleUserProfileModal = () => ({\n type: types.TOGGLE_USER_PROFILE_MODAL\n});\n\nexport const toggleLoggoutConfirmModal = () => ({\n type: types.TOGGLE_LOGGOUT_CONFIRM_MODAL\n});\n","import React from 'react';\nimport { Col, FormControl, FormGroup, Row, FormLabel } from 'react-bootstrap';\nimport { connect } from 'react-redux';\nimport { addClassCode, toggleClassCodeModal } from '../../actions/userActions';\nimport CommonModal from '../common/CommonModal';\n\n\ninterface Iprops {\n show: boolean;\n submit: typeof addClassCode;\n cancel: ()=> void;\n addCode?: string;\n}\n\ninterface Istate {\n code: string;\n}\n\nclass ClassCodeModal extends React.Component {\n constructor(props: Iprops) {\n super(props);\n this.state = {\n code: this.props.addCode || ''\n }\n }\n render() {\n return (\n \n
{e.preventDefault(); this.props.submit(this.state.code)}}\n >\n \n \n \n \n \n Class Code or Group\n \n this.setState({code: e.currentTarget.value})}\n />\n \n \n
\n
\n\n \n
\n );\n }\n}\n\n\nfunction mapStateToProps(state: any, ownProps: any) {\n return {\n loading: state.ajaxCallsInProgress > 0,\n show: state.dashboard.showClassCodeModal\n };\n}\n\nexport default connect(mapStateToProps, {\n submit: addClassCode,\n cancel: toggleClassCodeModal\n})(ClassCodeModal);\n","import * as React from 'react';\nimport { connect } from 'react-redux';\nimport { toastr } from 'react-redux-toastr';\nimport { hashHistory } from 'react-router';\nimport UserProfileForm from '../common/UserProfileForm';\nimport CommonModal from '../common/CommonModal';\nimport { Nav, Navbar, NavItem } from 'react-bootstrap';\n\nimport {\n userUpdateProfile,\n userUpdatePW,\n userLogout,\n toggleClassCodeModal\n} from '../../actions/userActions';\nimport {\n toggleUserProfileModal,\n toggleLoggoutConfirmModal\n} from '../../actions/dashboard/dashboardActions';\n\nimport constants from '../../constants/constants';\nimport UserAPI from '../../api/userAPI';\nimport { StyledNavbar } from '../../constants/styledComponents';\nimport ClassCodeModal from './ClassCodeModal';\n\nconst FontAwesome = require('react-fontawesome');\n\nclass BookBagHeader extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n firstName: this.props.user.First,\n lastName: this.props.user.Last,\n password: '',\n confirmPassword: '',\n loginID: this.props.user.LoginID,\n logoutWarning:\n 'All of your downloaded books will be removed and you will have to redownload them. Are you sure you want to continue?'\n };\n this.theme = constants.themeProvider.activeTheme;\n this.logo = this.theme.logo;\n }\n\n componentDidMount() {\n this.setState({ logoutWarning: '' });\n }\n\n closeLoggoutConfirmModal = () => {\n this.setState({ showLoggoutConfirmModal: false });\n };\n\n handleLoggout = () => {\n this.props.userLogout();\n hashHistory.push('/login');\n };\n\n handleSelect = (e) => {\n switch (e) {\n case '1':\n this.props.toggleUserProfileModal();\n break;\n case '2':\n this.signOut();\n break;\n case '3':\n this.props.toggleClassCodeModal();\n break;\n default:\n }\n };\n\n handleChange = (e) => {\n this.setState({ [e.target.name]: e.target.value });\n };\n\n signOut = () => {\n // prevent user from logging out if there is no internet connection\n if (!this.props.isOnline) {\n toastr.error(\n `You must be connected to the internet before logging out.`,\n `Warning`,\n constants.toastrWarningOptions\n );\n return;\n }\n this.props.toggleLoggoutConfirmModal();\n };\n\n handleSubmit = () => {\n if (!this.props.isOnline) {\n toastr.error(\n `You must be connected to the internet before updating your profile or changing your password.`,\n `No Internet Connection`,\n {\n closeButton: true,\n showAnimation: 'animated fadeInDown'\n }\n );\n return;\n }\n if (\n this.state.firstName !== '' &&\n this.state.lastName !== '' &&\n this.state.loginID !== ''\n ) {\n const firstName = this.state.firstName;\n const lastName = this.state.lastName;\n const loginID = this.state.loginID;\n this.props.toggleUserProfileModal();\n\n this.props\n .userUpdateProfile(\n this.props.user,\n firstName,\n lastName,\n loginID\n )\n .then(() => {\n toastr.success(\n `Updated profile info.`,\n `Success`,\n constants.toastrSuccessOptions\n );\n })\n .catch((error) => {\n console.error('Error updating profile', error);\n toastr.error(\n `Unable to update profile. ${error.statusText}`,\n `Error`,\n constants.toastrErrorOptions\n );\n });\n }\n\n if (\n this.state.password === this.state.confirmPassword &&\n this.state.password !== ''\n ) {\n const password = this.state.password;\n this.props\n .userUpdatePW(this.props.user, password)\n .then(() => {\n toastr.success(\n `Updated password.`,\n `Success`,\n constants.toastrSuccessOptions\n );\n })\n .catch((error) => {\n console.error('Error updating password', error);\n toastr.error(\n `Unable to update password. ${error.statusText}`,\n `Error`,\n constants.toastrErrorOptions\n );\n });\n this.setState({ password: '', confirmPassword: '' });\n this.props.toggleUserProfileModal();\n }\n };\n buildUserProfileForm = () => {\n return (\n \n );\n };\n\n render() {\n return (\n \n
\n {/* */}\n \n
\n \n {/* */}\n \n \n \n \n\n
\n\n
\n
\n
\n );\n }\n}\n\nfunction mapStateToProps(state, ownProps) {\n return {\n user: state.user,\n showUserProfileModal: state.dashboard.showUserProfileModal,\n showLoggoutConfirmModal: state.dashboard.showLoggoutConfirmModal\n };\n}\n\nexport default connect(mapStateToProps, {\n userUpdateProfile,\n userUpdatePW,\n userLogout,\n toggleUserProfileModal,\n toggleLoggoutConfirmModal,\n toggleClassCodeModal\n})(BookBagHeader);\n","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOQAAAEjCAYAAAAiza01AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNFQjRFNzcyMjQ4MzExRTdCQjNERDhCODU5NDJDMkJFIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNFQjRFNzczMjQ4MzExRTdCQjNERDhCODU5NDJDMkJFIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6M0VCNEU3NzAyNDgzMTFFN0JCM0REOEI4NTk0MkMyQkUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6M0VCNEU3NzEyNDgzMTFFN0JCM0REOEI4NTk0MkMyQkUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6pSFKnAAAO5klEQVR42uzd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\"","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOQAAAEjCAYAAAAiza01AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjM2NDRFOTE0MjQ4MzExRTc5Q0I1ODVFMUUyQTY2Q0Q4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjM2NDRFOTE1MjQ4MzExRTc5Q0I1ODVFMUUyQTY2Q0Q4Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MzY0NEU5MTIyNDgzMTFFNzlDQjU4NUUxRTJBNjZDRDgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzY0NEU5MTMyNDgzMTFFNzlDQjU4NUUxRTJBNjZDRDgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6b15M9AAANXUlEQVR42uzd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\"","import * as types from './actionTypes';\n\nexport const saveAdvancedSearchFilter = (filters) => ({\n type: types.SAVE_ADVANCED_SEARCH_FILTERS,\n filters\n});\n\nexport const clearAdvancedSearchFilter = () => ({\n type: types.CLEAR_ADVANCED_SEARCH_FILTERS\n});\n","export default \"data:image/png;base64,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\"","import React from 'react';\nimport { connect } from 'react-redux';\nimport config from '../../api/config';\nimport {\n saveAdvancedSearchFilter,\n clearAdvancedSearchFilter\n} from '../../actions/bookbagFiltersActions';\nimport { Col, Row, FormGroup, FormControl, Form } from 'react-bootstrap';\nimport PropTypes from 'prop-types';\nimport debounce from 'throttle-debounce/debounce';\nimport CommonModal from '../common/CommonModal';\nimport { MultiSelect } from 'react-selectize';\nimport { reject } from 'lodash';\nimport UserAPI from '../../api/userAPI';\nimport Select from 'react-select';\n\nclass AdvancedSearchModal extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n showAdvancedSearch: false,\n advancedSearch: ''\n };\n this.debouncedAdvancedSearch = debounce(800, this.doAdvancedSearch);\n }\n\n clearAdvancedSearch = () => {\n this.props.clearAdvancedSearchFilter();\n this.props.cancel();\n };\n handleReadingLevelChange = (selectedReadingLevelTypeOption) => {\n this.props.saveAdvancedSearchFilter({ selectedReadingLevelTypeOption });\n };\n\n advancedSearchForm() {\n return (\n \n
\n \n \n {this.state.loading && (\n \n )}\n
\n \n
\n
\n \n \n {\n this.props.saveAdvancedSearchFilter({\n tags: value\n });\n }}\n closeOnSelect={true}\n onSearchChange={(advancedSearch) => {\n if (\n this.state.advancedSearch ===\n advancedSearch\n )\n return;\n this.setState({ advancedSearch });\n\n if (advancedSearch.length > 0) {\n this.setState({ loading: true });\n if (!!this.req) {\n this.req.abort();\n }\n this.handleAdvancedSearchChange(\n advancedSearch\n );\n }\n }}\n uid={(item) => {\n return item.TagID;\n }}\n filterOptions={(options, search) => {\n return options;\n }}\n renderOption={(item) => {\n return (\n \n
\n \n {item.name}\n \n \n {'@' +\n item.Name +\n ' (' +\n item.TagCount +\n ')'}\n \n
\n
\n );\n }}\n renderValue={(item) => {\n return (\n \n {\n let rejectedSearch = reject(\n this.props\n .bookbagFilters,\n (search) => {\n return (\n search.TagID ===\n item.TagID\n );\n }\n );\n this.props.saveAdvancedSearchFilter(\n rejectedSearch\n );\n }}\n >\n x\n \n\n \n {item.name}\n \n \n {'@' +\n item.Name +\n ' (' +\n item.TagCount +\n ')'}\n \n
\n );\n }}\n renderNoResultsFound={(value, search) => {\n return (\n \n {typeof this.req === 'undefined' &&\n this.state.advancedSearch.length ===\n 0\n ? 'type a few characters to kick off remote search'\n : 'No results found'}\n
\n );\n }}\n />\n \n \n
\n\n
\n \n \n \n \n \n \n \n \n this.props.saveAdvancedSearchFilter({\n readingMin: e.target.value\n })\n }\n />\n \n \n \n \n \n this.props.saveAdvancedSearchFilter({\n readingMax: e.target.value\n })\n }\n />\n \n \n
\n
\n \n \n \n this.props.saveAdvancedSearchFilter({\n spanishOnly: e.target.checked\n })\n }\n label=\"Spanish Only\"\n />\n \n \n
\n
\n );\n }\n\n doAdvancedSearch(value) {\n UserAPI.getAdvancedSearch(value, this.props.user)\n .then((advancedSearchResults) => {\n this.setState({\n advancedSearchResults: advancedSearchResults,\n loading: false\n });\n })\n .catch((error) => {\n this.setState({ loading: false });\n console.error(error, 'Error with advanced search');\n });\n }\n\n handleAdvancedSearchChange(value) {\n this.debouncedAdvancedSearch(value);\n }\n render() {\n return (\n \n \n
\n );\n }\n}\n\nAdvancedSearchModal.propTypes = {\n show: PropTypes.bool.isRequired,\n cancel: PropTypes.func.isRequired\n};\n\nfunction mapStateToProps(state, ownProps) {\n return {\n user: state.user,\n bookbagFilters: state.bookbagFilters,\n loading: state.ajaxCallsInProgress > 0,\n isOnline: state.offlineQueue.isOnline && navigator.onLine\n };\n}\n\nexport default connect(mapStateToProps, {\n saveAdvancedSearchFilter,\n clearAdvancedSearchFilter\n})(AdvancedSearchModal);\n","import * as React from 'react';\nimport allSquare from '../../images/NandF_Square.png';\nimport backpackImg from '../../images/backpack.png';\nimport bookshelfImg from '../../images/bookshelf.png';\nimport languageSquare from '../../images/Language_Square.png';\nimport mathSquare from '../../images/Math_Square.png';\nimport nonFictionSquare from '../../images/Nonfiction_Square.png';\nimport AdvancedSearchModal from './AdvancedSearchModal';\nimport scienceSquare from '../../images/Science_Square.png';\nimport socialStudiesSquare from '../../images/SocialStudies_Square.png';\nimport fictionSquare from '../../images/Fiction_Square.png';\nimport { FormGroup, Row, Col, FormControl, Button } from 'react-bootstrap';\nimport { StyledButton } from '../../constants/styledComponents';\nimport { addQuery, removeQuery } from '../../vendor/utils-router';\nconst FontAwesome = require('react-fontawesome');\n\nimport {\n checkForUpdatedBookFiles,\n getBookItemsList,\n initialDashboardQuery,\n searchBookBagBooks\n} from '../../actions/bookActions';\nimport { clearAdvancedSearchFilter } from '../../actions/bookbagFiltersActions';\n\ninterface Iprops {\n query: typeof initialDashboardQuery;\n isOnline: boolean;\n bookbagFilters: {\n tags: any;\n spanishOnly: any;\n selectedReadingLevelTypeOption: any;\n readingMax: any;\n readingMin: any;\n };\n showSearchBar: boolean;\n theme: any;\n books: Array;\n searchBookBagBooks: typeof searchBookBagBooks;\n isFiltersActive: boolean;\n user: any;\n downloadedBooks: Array;\n checkForUpdatedBookFiles: typeof checkForUpdatedBookFiles;\n manualAjaxEnd: () => void;\n handleInvalidSession: () => void;\n clearAdvancedSearchFilter: typeof clearAdvancedSearchFilter;\n loading: boolean;\n isStudent: boolean;\n isGeneric: boolean;\n getBookItemsList: typeof getBookItemsList;\n}\n\ninterface Istate {\n showAdvancedSearch: boolean;\n}\n\nclass BookBagSearch extends React.Component {\n constructor(props: Iprops) {\n super(props);\n this.state = {\n showAdvancedSearch: false\n };\n }\n componentDidMount() {\n this.initializeComponent();\n }\n componentDidUpdate(prevProps: Iprops) {\n if (prevProps.isOnline !== this.props.isOnline && this.props.isOnline) {\n setTimeout(() => {\n this.updateBookItems();\n }, 300);\n }\n }\n /*\n * initial setup of the component\n * call getBookListItems to get the latest book items for DOWNLOADED books only\n * this.props.downloadedBooks, loop through array to get all bookIDs\n * Action: save the book contents to downloaded books\n * Potential Issue: Being offline, saving a book item, going online and going straight to book bag:\n * Solution: check to see if there are any ajaxCallsInProgress\n */\n initializeComponent = () => {\n if (!this.props.query.searchMode) {\n addQuery({ searchMode: 'local' });\n }\n this.updateBookItems();\n\n if (this.props.isGeneric) {\n addQuery({ searchMode: 'remote' });\n setTimeout(() => {\n this.props.searchBookBagBooks(\n this.props.isFiltersActive,\n this.props.query\n );\n }, 100);\n } else if (this.props.isStudent) {\n // if local then always load books. If remote the only load books if there is a search\n if (\n !this.props.query.searchMode ||\n this.props.query.searchMode === 'local'\n ) {\n this.props.searchBookBagBooks(\n this.props.isFiltersActive,\n this.props.query\n );\n } else if (\n this.props.query.searchMode === 'remote' &&\n this.props.query.search.length > 0 &&\n this.props.books.length === 0\n ) {\n this.props.searchBookBagBooks(\n this.props.isFiltersActive,\n this.props.query\n );\n }\n if (this.props.isOnline) {\n console.log('****** we are online');\n } else {\n console.log('****** we are offline');\n }\n } else {\n // if you are not a student or a Generic user, they will be sent to the login screen via the auth check in the render function.\n }\n // TODO load all assigned blms and save this to redux\n // TODO start with including it into props\n };\n /*\n * If we are on a device and downloaded books, check to see if there are updated Book Items or updated Book files\n */\n updateBookItems = () => {\n console.log('about to update book items');\n if (this.props.isOnline && this.props.downloadedBooks.length > 0) {\n // Make call to grab all book items if ajax calls are not currently in progress\n if (!this.props.loading) {\n this.props.getBookItemsList(\n this.props.downloadedBooks,\n this.props.user\n );\n } else {\n //wait 5 seconds then try again\n setTimeout(() => {\n this.updateBookItems();\n }, 5000);\n }\n }\n };\n\n isActiveAdvancedSearch = () => {\n const {\n tags,\n spanishOnly,\n selectedReadingLevelTypeOption,\n readingMax,\n readingMin\n } = this.props.bookbagFilters;\n if (\n (tags && tags.length) ||\n spanishOnly ||\n (selectedReadingLevelTypeOption &&\n selectedReadingLevelTypeOption.value !== 'grl') ||\n readingMax ||\n readingMin\n ) {\n return true;\n } else {\n return false;\n }\n };\n\n /*\n * Switch to Remote Books\n */\n switchToRemote = () => {\n // this.setState({ searchMode: 'remote' }, () => {\n // // only load remote books if there is a search paramater\n // // do we want to search if there is a tag, type, or reading level selected??? Lets stick with search for now\n // // if (this.state.search.length > 0 || this.state.tag !== 'all-tags' || this.state.type !== 'all' || this.state.readingLeve.length > 0){\n // });\n addQuery({ searchMode: 'remote' });\n removeQuery('activePage');\n };\n\n /*\n * Switch to Local Books\n */\n switchToLocal = () => {\n addQuery({ searchMode: 'local' });\n removeQuery('activePage');\n };\n\n searchForBooks = (event: React.ChangeEvent) => {\n if (event) {\n addQuery({ search: event.target.value });\n }\n };\n\n searchForReadingLevel = (event: React.ChangeEvent) => {\n if (event) {\n addQuery({ grl: event.target.value });\n }\n };\n\n handleTagChange = (val: string) => {\n addQuery({ tag: val });\n };\n\n handleTypeChange = (val: string) => {\n addQuery({ type: val });\n };\n\n showAdvancedSearch = () => {\n this.setState({ showAdvancedSearch: true });\n };\n\n closeAdvancedSearch = () => {\n this.setState({ showAdvancedSearch: false });\n };\n clearAdvancedSearchForm = () => {\n removeQuery('search', 'type', 'tag', 'grl');\n addQuery({ activePage: 1 });\n this.props.clearAdvancedSearchFilter();\n };\n\n handleSubmit = (\n event: React.FormEvent,\n activePage: number\n ) => {\n if (event) {\n event.preventDefault();\n }\n console.log(`initiating a bookbag search: ${this.props.query.search}`);\n addQuery({ activePage });\n this.props.searchBookBagBooks(this.props.isFiltersActive, {\n ...this.props.query,\n activePage\n });\n };\n\n render() {\n let backpack = '';\n let bookshelf = '';\n let bookbagSearchMode = 'bookbag-searchmode';\n if (this.props.isGeneric) {\n bookbagSearchMode = 'd-none';\n }\n if (\n !this.props.query.searchMode ||\n this.props.query.searchMode === 'local'\n ) {\n backpack += 'backpack';\n bookshelf += '';\n } else {\n backpack += '';\n bookshelf += 'bookshelf';\n }\n\n let advancedSearchButton = this.isActiveAdvancedSearch()\n ? 'advanced-search-active'\n : 'advanced-search';\n // tags\n let languageBC =\n this.props.query.tag === 'languageArts' ? 'language-bc' : '';\n let socialStudiesBC =\n this.props.query.tag === 'socialStudies' ? 'social-science-bc' : '';\n let mathBC = this.props.query.tag === 'math' ? 'math-bc' : '';\n let scienceSearchBC =\n this.props.query.tag === 'science' ? 'science-bc' : '';\n let allTags = this.props.query.tag === 'allTags' ? 'all-tags' : '';\n // types\n let allBC = this.props.query.type === 'All' ? 'all-bc' : '';\n let fictionBC = this.props.query.type === 'Fiction' ? 'fiction-bc' : '';\n let nonFictionBC =\n this.props.query.type === 'Non-Fiction' ? 'non-fiction-bc' : '';\n\n return (\n \n {this.props.showSearchBar && this.props.isOnline && (\n \n )}\n \n );\n }\n}\n\nexport default BookBagSearch;\n","export default \"data:image/png;base64,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\"","export default \"data:image/png;base64,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\"","export default \"data:image/png;base64,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\"","export default \"data:image/png;base64,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\"","export default \"data:image/png;base64,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\"","export default \"data:image/png;base64,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\"","export default \"data:image/png;base64,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\"","export default \"data:image/png;base64,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\"","import { connect } from 'react-redux';\nimport { manualAjaxEnd } from '../../actions/ajaxStatusActions';\nimport {\n checkForUpdatedBookFiles,\n getBookItemsList,\n initialDashboardQuery,\n searchBookBagBooks\n} from '../../actions/bookActions';\nimport { clearAdvancedSearchFilter } from '../../actions/bookbagFiltersActions';\nimport UserAPI from '../../api/userAPI';\nimport BookBagSearch from './BookBagSearch';\n\ninterface Iprops {\n query: typeof initialDashboardQuery;\n theme: any;\n isFiltersActive: boolean;\n handleInvalidSession: () => void;\n}\n\nconst mapStateToProps = (state: any, ownProps: Iprops) => {\n const showSearchBar =\n UserAPI.isStudent(state.user.RoleID) ||\n UserAPI.isGeneric(state.user.RoleID);\n return {\n loading: state.ajaxCallsInProgress > 0,\n books: state.books,\n downloadedBooks: state.downloadedBooks,\n isGeneric: UserAPI.isGeneric(state.user.RoleID),\n isStudent: UserAPI.isStudent(state.user.RoleID),\n user: state.user,\n isOnline: state.offlineQueue.isOnline && navigator.onLine,\n bookbagFilters: state.bookbagFilters,\n showSearchBar\n };\n};\n\nexport const BookBagSearchContainer = connect(mapStateToProps, {\n getBookItemsList,\n checkForUpdatedBookFiles,\n clearAdvancedSearchFilter,\n searchBookBagBooks: searchBookBagBooks,\n manualAjaxEnd: manualAjaxEnd\n})(BookBagSearch);\n","import { Col, Container, Row } from 'react-bootstrap';\nimport { StyledButton, StyledDiv } from '../../constants/styledComponents';\nimport {\n addBook,\n searchBookBagBooks,\n initialDashboardQuery,\n removeBook,\n downloadBook,\n deleteDownloadedBook\n} from '../../actions/bookActions';\nimport { addQuery, removeQuery } from '../../vendor/utils-router';\nimport { manualAjaxStart } from '../../actions/ajaxStatusActions';\n\nimport BookBagHeader from './BookBagHeader';\nimport Loading from '../common/Loading';\nimport React from 'react';\n\nimport config from '../../api/config';\nimport { connect } from 'react-redux';\nimport constants from '../../constants/constants';\nimport defaultPhoto from '../../images/default.jpg';\nimport downloadedImage from '../../images/downloaded.png';\nimport { forEach } from 'lodash';\nimport { hashHistory } from 'react-router';\nimport { indexOf } from 'lodash';\n\nimport readBook from '../../images/read.png';\nimport { requireSignIn } from '../../routes';\n\nimport { sortBy } from 'lodash';\nimport { toastr } from 'react-redux-toastr';\nimport { toggleUserProfileModal } from '../../actions/dashboard/dashboardActions';\nimport unReadBook from '../../images/unread.png';\nimport { toggleClassCodeModal, userLogout } from '../../actions/userActions';\nimport UltimatePagination from 'react-ultimate-pagination-bootstrap-4';\nimport { BookBagSearchContainer } from './BookBagSearchContainer';\nimport UserAPI from '../../api/userAPI';\n\nlet bookState = unReadBook;\n\nclass BookBag extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n prevSelected: [],\n selectedTypes: [],\n selectedBookID: false,\n loading: false,\n emptySearchMessage:\n \"Please enter your search criteria above and press 'Go'\"\n };\n\n this.openingBook = false;\n this.theme = constants.themeProvider.activeTheme;\n }\n\n componentDidMount() {\n console.log('bookbag is mounted');\n // this.handleTypeChange(this.props.query.tag); // TODO what is this????\n // this.handleTypeChange(this.props.query.type);\n // remove the BLM ID and projectAssignmentID from query so that we do not stay in BLM mode when switching books\n removeQuery('blmID');\n removeQuery('projectAssignmentID');\n this.checkAddCode();\n this.checkCanAccessBookBag();\n }\n\n componentDidUpdate(prevProps) {\n if (prevProps.books !== this.props.books) {\n // set the appropriate empty search message. If this is the local bag and they have no search criteria, then they have no books in their bag\n let emptySearchMessage = '';\n if (this.props.books.length <= 0 && !this.filtersActive()) {\n emptySearchMessage = `You currently do not have any books in your book bag.`;\n }\n this.setState({ emptySearchMessage });\n }\n if (prevProps.query !== this.props.query) {\n this.setState({ prevSelected: [] });\n }\n }\n\n /*\n * only students, generic, and demo can access the bookbag. otherwise log them out\n */\n checkCanAccessBookBag = () => {\n if (this.props.user.SchoolID && (this.props.isStudent || this.props.isDemo || this.props.isGeneric) === false){\n toastr.warning('Warning', 'User does not have access to the bookbag. Logging out...', constants.toastrWarningOptions);\n setTimeout(() => {\n this.props.userLogout();\n hashHistory.replace('/');\n }, 5000);\n }\n\n\n }\n\n checkAddCode = () =>{\n console.log('checking addcode')\n if (this.props.addCode){\n setTimeout(() => {\n this.props.toggleClassCodeModal(true);\n }, 1000);\n }\n }\n\n handleBookClick = (e, book) => {\n if (e) {\n e.preventDefault();\n }\n if (this.state.selectedBookID === book.ID) {\n this.openBook(book);\n } else {\n this.setState({ selectedBookID: book.ID });\n }\n };\n\n openBook = (book) => {\n // prevent double click on open book\n // If we are offline:\n if (this.openingBook) return;\n\n this.openingBook = true;\n setTimeout(() => {\n this.openingBook = false;\n }, 300);\n\n if (book.IsExternal) {\n // open the book in the Lerner viewer, not in the DiBS viewer\n const externalURL = `${config.API.Main}/book/openexternalviewer?bookID=${book.ID}`;\n var win = window.open(externalURL, '_blank');\n win.focus();\n } else {\n // addQuery({bookID: book.ID});\n const location = Object.assign(\n {},\n hashHistory.getCurrentLocation()\n );\n Object.assign(location.query, { bookID: book.ID });\n hashHistory.push({\n pathname: `/viewer`,\n query: location.query\n });\n }\n };\n\n handleSubmit = (e, activePage) => {\n if (e) {\n e.preventDefault();\n }\n console.log(`initiating a bookbag search: ${this.props.query.search}`);\n addQuery({ activePage });\n this.props.searchBookBagBooks(this.filtersActive(), {\n ...this.props.query,\n activePage\n });\n };\n\n // are any book bag filters active? returns a boolean\n filtersActive = () => {\n if (\n this.props.query.search.length === 0 &&\n this.props.query.type.length === 0 &&\n this.props.query.tag.length === 0 &&\n this.props.query.grl.length === 0 &&\n this.props.bookbagFilters.length === 0\n ) {\n return false;\n } else {\n return true;\n }\n };\n\n handleInvalidSession = () => {\n toastr.error(\n `Please login again.`,\n `Session Expired`,\n constants.toastrErrorOptions\n );\n setTimeout(() => {\n this.props.userLogout();\n hashHistory.replace('/');\n }, 3000);\n };\n\n displayBookReadingLevel = (book) => {\n let grl, atos, lex;\n let hideBookReadingLevel = 'book-reading-level';\n if (\n book.GuidedReadingLevel === '' ||\n book.GuidedReadingLevel === null\n ) {\n grl = 'grl-hide';\n } else if (book.ATOS === '' || book.ATOS === null) {\n atos = 'atos-hide';\n } else if (book.LEX === '' || book.LEX === null) {\n lex = 'lex-hide';\n }\n\n if (!book.GuidedReadingLevel && !book.ATOS && !book.LEX) {\n hideBookReadingLevel = '';\n return;\n }\n\n return (\n \n {book.ATOS !== null && book.ATOS !== '' && (\n A: {book.ATOS}, \n )}\n {book.LEX !== null && book.LEX !== '' && (\n L: {book.LEX}, \n )}\n {book.GuidedReadingLevel !== null &&\n book.GuidedReadingLevel !== '' && (\n \n GRL: {book.GuidedReadingLevel}\n \n )}\n
\n );\n };\n\n displayBookImage = (book) => {\n let img = !!book.OfficialImage ? book.OfficialImage : defaultPhoto;\n const bookTitle = book.Title;\n let downloadedIcon;\n forEach(this.props.downloadedBooks, (dlBook) => {\n if (book.ID === dlBook.ID) {\n downloadedIcon = downloadedImage;\n }\n });\n\n let photo = {\n backgroundImage: `url(${img})`,\n backgroundPosition: 'center center',\n backgroundSize: 'contain',\n backgroundRepeat: 'no-repeat'\n };\n if (!!book.OfficialImage) {\n return (\n \n
{bookTitle}
\n
this.handleBookClick(e, book)}\n >\n

\n {book.IsExternal && (\n
\n \n
\n )}\n {this.displayBookReadingLevel(book)}\n
\n
\n );\n } else {\n return (\n this.handleBookClick(e, book)}\n >\n

\n {book.IsExternal && (\n
\n \n
\n )}\n
{bookTitle}
\n {this.displayBookReadingLevel(book)}\n
\n );\n }\n };\n\n render() {\n if (requireSignIn(this.props.user, this.props.location)) {\n return null;\n }\n let books = !!this.props.books\n ? this.props.books.filter((book) => {\n return book;\n })\n : [];\n const downloadedBooks = this.props.downloadedBooks;\n let filteredBooks = this.props.isOnline\n ? sortBy(books, [\n function (o) {\n return o.Title;\n }\n ])\n : sortBy(downloadedBooks, [\n function (o) {\n return o.Title;\n }\n ]);\n\n return (\n \n \n \n\n {!this.props.isOnline && (\n \n \n You are offline\n \n
\n )}\n \n {filteredBooks.length <= 0 && (\n \n \n {this.state.emptySearchMessage}\n
\n \n )}\n {filteredBooks.map((book) => {\n let selected = 'd-none';\n let glow = 'book-cover book-shadow';\n let bookHeight = '';\n\n if (this.state.selectedBookID === book.ID) {\n selected = 'visible animated fadeInDown';\n bookHeight = ' pulse raise-book';\n glow = 'book-cover glow';\n if (book.CurrentPage !== null && book.CurrentPage !== 1) {\n bookState = readBook;\n }\n } else if (\n indexOf(this.state.prevSelected, book.ID) !== -1\n ) {\n bookHeight = ' ';\n }\n if (book.CurrentPage !== null && book.CurrentPage !== 1) {\n bookState = readBook;\n } else {\n bookState = unReadBook;\n }\n const isDownloaded = this.props.downloadedBooks.find(\n (downloadedBook) => downloadedBook.ID === book.ID\n )\n ? true\n : false;\n return (\n \n \n
\n {this.displayBookImage(book)}\n

\n
\n
\n this.openBook(book)}\n >\n READ\n \n {book.isInBookBag === true &&\n isDownloaded === false && (\n \n this.props\n .downloadBook(book)\n .then(() => {\n toastr.success('Succcess', 'Book downloaded for viewing offline.', constants.toastrSuccessOptions)\n })\n }\n >\n \n DOWNLOAD\n \n \n )}\n {book.isInBookBag === true &&\n isDownloaded === true && (\n \n this.props.deleteDownloadedBook(\n book.ISBN\n )\n }\n >\n \n REMOVE DOWNLOAD\n \n \n )}\n\n {book.isInBookBag === false && (\n \n this.props.addBook(book)\n }\n >\n \n ADD TO BOOK BAG\n \n \n )}\n {book.IsStudentAdded === true &&\n book.isInBookBag === true && (\n \n this.props.removeBook(\n book.ID, book.ISBN\n )\n }\n >\n \n REMOVE FROM BAG\n \n \n )}\n
\n
\n \n );\n })}\n
\n {this.props.books.length > 0 && (\n \n \n \n \n this.handleSubmit('', pageNumber)\n }\n />\n \n \n
\n )}\n \n \n );\n }\n}\n\nfunction mapStateToProps(state, ownProps) {\n const addCode = ownProps.location.query.addCode || ownProps.location.query.addcode || ownProps.location.query.AddCode;\n return {\n completedBLMs: state.completedBLMs,\n user: state.user,\n book: state.book,\n books: state.books,\n bookbagFilters: state.bookbagFilters,\n loading: state.ajaxCallsInProgress > 0,\n isOnline: state.offlineQueue.isOnline && navigator.onLine,\n downloadedBooks: state.downloadedBooks,\n pageResults: state.dashboardPageResults,\n query: { ...initialDashboardQuery, ...ownProps.location.query },\n addCode,\n isGeneric: UserAPI.isGeneric(state.user.RoleID),\n isStudent: UserAPI.isStudent(state.user.RoleID),\n isDemo: UserAPI.isDemo(state.user.RoleID)\n };\n}\n\nexport default connect(mapStateToProps, {\n addBook,\n removeBook,\n searchBookBagBooks,\n userLogout,\n manualAjaxStart,\n toggleUserProfileModal,\n downloadBook,\n deleteDownloadedBook,\n toggleClassCodeModal\n})(BookBag);\n","export default \"data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMqaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0MiA3OS4xNjA5MjQsIDIwMTcvMDcvMTMtMDE6MDY6MzkgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NEMwMzFGOTFCQjQxMTFFN0I4OEI5RDQ3NkJDREEyODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NEMwMzFGOTJCQjQxMTFFN0I4OEI5RDQ3NkJDREEyODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0QzAzMUY4RkJCNDExMUU3Qjg4QjlENDc2QkNEQTI4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0QzAzMUY5MEJCNDExMUU3Qjg4QjlENDc2QkNEQTI4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/uAA5BZG9iZQBkwAAAAAH/2wCEAAYEBAQFBAYFBQYJBgUGCQsIBgYI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P6kqf95b/qIRG/hgvILr3Y5D7rhHWRN/5Un4WItbz4gamjsW3VDYaFohhqZ2QxRNPhDAC8j1ZsqJGJ8MJI05fCPEVTP6pCoJV/EBuVFVzRNq6fLHI5o/s8fgHEIuK5udwqblcKm4VRDqmrkdNM5oDQXvOY4AeHEorGQEBAQEBAQEBB6jkkie2SNxY9pxa9pIII8wQgkLtf6lqmUVLeayW72ujkbL93VUjzFJlOIbIQQ9w9ZQxe2k/iK0bWRxUt1ppLJK0BjSB3aYAcBg5gDmj1s4elGcWdR3Cx363PdST09yoJ2lkmRzZY3NcOLXAY+I8iiOVNffbNF6zqrTYLtIbdSTGppKdkpkjgfKwtcxzDizOwOLfDHBGogaKIOg9j9c6QsuhJKG63ano6t1TM8QyuIdlc1oB4DzwRmxz675x9aNPiDoLcLXOkLhs9HaKO7U89ybTUTDSscS/NHkzjDD8XAokjzuhrjSNz2mhtVBdYKm4tZRA0zHEvxjy5+GHlghFbbNanpNO68o6yumbT0EzJKeqmeSGta9pLScPptCFb34gNaWrUd/t0Noq2VlvoaYnvRklvemf749jWNQjffD7rHSthsd2gvVzhoZZ6ljo2SuILmiPAkcPShW6fYfhsle6R1bSlzyXOJqphxJxP4yJ6q3VFl0CKi9z2mvDYoJnNtkMcmdjmNjaW/OBc7O4u8+GCKgqKICAgICAgICAgICDKt91ultm79urJ6KbDDu08j4nYejMwgoMZznPcXvJc5xJc4nEknxJKD4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIPuR/SeSBkf0nkgZH9J5IGR/SeSBkf0nkgZH9J5IGR/SeSBkf0nkgZH9J5IGR/SeSBkf0nkgZH9J5IGR/SeSBkf0nkgZH9J5IGR/SeSBkf0nkgZH9J5IGR/SeSBkf0nkgZH9J5IGR/SeSBkf0nkgZH9J5IGR/SeSBkf0nkg/9k=\"","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADcAAAAuCAYAAACMAoEVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjlGMzQyRjE5MUY5QzExRTdBRDNGOUNDNTU1NTlENzcxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjlGMzQyRjFBMUY5QzExRTdBRDNGOUNDNTU1NTlENzcxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OUYzNDJGMTcxRjlDMTFFN0FEM0Y5Q0M1NTU1OUQ3NzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OUYzNDJGMTgxRjlDMTFFN0FEM0Y5Q0M1NTU1OUQ3NzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4QtWyDAAAHI0lEQVR42tSZ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\"","import { Container, Nav, NavItem, Navbar } from 'react-bootstrap';\nimport { ToastContainer, ToastMessage } from 'react-toastr';\nimport {\n manualAjaxEnd,\n manualAjaxStart\n} from '../../actions/ajaxStatusActions';\nimport {\n userLogout,\n userUpdatePW,\n userUpdateProfile\n} from '../../actions/userActions';\n\nimport FabricUI from './FabricUI';\nimport FontAwesome from 'react-fontawesome';\nimport React from 'react';\nimport { StyledNavbar } from '../../constants/styledComponents';\nimport { connect } from 'react-redux';\nimport constants from '../../constants/constants';\nimport { hashHistory } from 'react-router';\nimport logo from '../../images/logo.png';\nimport { requireSignIn } from '../../routes';\n\nconst ToastMessageFactory = React.createFactory(ToastMessage.animation);\n\nclass BLMView extends React.Component {\n toastCont;\n\n constructor(props) {\n super(props);\n this.state = {};\n\n this.handleSelect = this.handleSelect.bind(this);\n this.theme = constants.themeProvider.activeTheme;\n }\n\n signOut() {\n this.props.userLogout();\n hashHistory.push('/login');\n }\n\n handleSelect(e) {\n switch (e) {\n case 2:\n this.signOut();\n break;\n default:\n }\n }\n\n render() {\n if (requireSignIn(this.props.user, this.props.location)) {\n return null;\n }\n\n return (\n \n \n \n \n
\n \n \n \n \n \n \n \n {\n // \n }\n {\n this.toastCont = toast;\n }}\n className=\"toast-top-right\"\n />\n \n );\n }\n}\n\n/*\n \n
*/\n\nfunction mapStateToProps(state, ownProps) {\n return {\n user: state.user,\n book: state.book,\n books: state.books,\n loading: state.ajaxCallsInProgress > 0\n };\n}\n\nexport default connect(mapStateToProps, {\n userUpdateProfile,\n userUpdatePW,\n userLogout,\n manualAjaxStart,\n manualAjaxEnd\n})(BLMView);\n","import React, { Component } from 'react';\nimport {\n manualAjaxEnd,\n manualAjaxStart\n} from '../../actions/ajaxStatusActions';\nimport { tokenLogin, userLogout } from '../../actions/userActions';\n\nimport { Container } from 'react-bootstrap';\nimport Loading from '../common/Loading';\nimport { Navbar } from 'react-bootstrap';\nimport { StyledNavbar } from '../../constants/styledComponents';\nimport { connect } from 'react-redux';\nimport constants from '../../constants/constants';\nimport { getBLMByID } from '../../actions/blmActions';\nimport { getBookByID } from '../../actions/bookActions';\nimport { hashHistory } from 'react-router';\nimport logo from '../../images/logo.png';\nimport { toastr } from 'react-redux-toastr';\nimport { updateViewerMode } from '../../actions/modeActions';\nimport { viewerModes } from '../../constants/viewerModes';\n\n/*\n *** IMPORTANT ***\n - Tokens are only applicable for web deployments\n - (will not be used for iOS and Android)\n *** IMPORTANT ***\n*/\n\nclass Token extends Component {\n constructor(props) {\n super(props);\n\n this.state = {\n showLoading: true\n };\n\n this.theme = constants.themeProvider.activeTheme;\n }\n\n componentDidMount() {\n console.log('opening book with token login');\n // return;\n let params = this.props.location.query;\n if (!params || !params.token) {\n // fail\n this.setState({ showLoading: false });\n toastr.error(\n `Error`,\n `Missing token/bookID, unable to load book`,\n constants.toastrWarningOptions\n );\n } else {\n // console.log(params);\n this.doLogin(params);\n }\n }\n\n doLogin = (params) => {\n this.props\n .userLogout()\n .then(() => {\n return this.props.tokenLogin(params.token);\n })\n .then((user) => {\n console.log('***USER', user.LoginID);\n if (user.APIKey === null) {\n toastr.error(\n `Error`,\n 'Unable to load book. Please contact support. User missing API Key.',\n constants.toastrWarningOptions\n );\n return;\n }\n // we need to account for multiple possibilities now...\n // if we have a bookID only, we should just load up the book\n if (params.viewerMode === viewerModes.MODE_GENERIC) {\n return this.props\n .getBookByID(params.bookID, user)\n .then((bookSaved) => {\n this.setState({ loading: false });\n // hashHistory.push('/viewer/' + params.bookID);\n hashHistory.push({\n pathname: `/viewer`,\n query: this.props.location.query\n });\n });\n // TODO: error handling here?\n }\n // if we have a bookID and a studentID, we should load the book\n // and load the student's highlights\n else if (\n params.viewerMode ===\n viewerModes.MODE_TEACHER_VIEW_STUDENT_BOOK\n ) {\n console.log(\n 'TOKEN: LOADING TEACHER VIEW STUDENT HIHGLIGHTS'\n );\n return this.props\n .getBookByID(params.bookID, user)\n .then((bookSaved) => {\n this.setState({ loading: false });\n hashHistory.push({\n pathname: `/viewer`,\n query: this.props.location.query\n });\n });\n // TODO: error handling here?\n } else if (\n params.viewerMode === viewerModes.MODE_TEACHER_CLASS_NOTES\n ) {\n console.log('TOKEN: LOADING TEACHER VIEW CLASS NOTES');\n return this.props\n .getBookByID(params.bookID, user)\n .then((bookSaved) => {\n this.setState({ loading: false });\n hashHistory.push({\n pathname: `/viewer`,\n query: this.props.location.query\n });\n });\n // TODO: error handling here?\n } else if (\n params.viewerMode === viewerModes.MODE_TEACHER_GROUP_NOTES\n ) {\n console.log('TOKEN: LOADING TEACHER VIEW Group NOTES');\n return this.props\n .getBookByID(params.bookID, user)\n .then((bookSaved) => {\n this.setState({ loading: false });\n hashHistory.push({\n pathname: `/viewer`,\n query: this.props.location.query\n });\n });\n // TODO: error handling here?\n }\n // if we have a blmID, we should load up the blm in admin\n // create template mode\n else if (\n params.viewerMode === viewerModes.MODE_ADMIN_CREATE_TEMPLATE\n ) {\n // TODO: load admin create mode\n return this.props\n .getBLMByID(params.blmID, user)\n .then((blmSaved) => {\n this.setState({ loading: false });\n hashHistory.push({\n pathname: `/viewer`,\n query: this.props.location.query\n });\n });\n } else if (\n params.viewerMode === viewerModes.MODE_TEACHER_STUDENT_BLM\n ) {\n return this.props\n .getBLMByID(params.blmID, user)\n .then((blmSaved) => {\n this.setState({ loading: false });\n // hashHistory.push(`/viewer/${blmSaved.BookID}/${params.blmID}`);\n hashHistory.push({\n pathname: `/viewer`,\n query: this.props.location.query\n });\n });\n } else if (params.viewerMode === viewerModes.MODE_RESOURCE) {\n return this.props\n .getBookByID(params.bookID, user)\n .then((bookSaved) => {\n this.setState({ loading: false });\n // hashHistory.push('/viewer/' + params.bookID);\n hashHistory.push({\n pathname: `/viewer`,\n query: this.props.location.query\n });\n });\n } else if (params.viewerMode === viewerModes.MODE_STUDENT) {\n hashHistory.push({\n pathname: `/bag?searchMode=local&tag=allTags`,\n query: ''\n });\n }\n })\n .catch((error) => {\n this.setState({ showLoading: false });\n console.error(\n `error logging in or loading book with token`,\n error\n );\n let message = `Unable to login or load book with token`;\n if (error === undefined) {\n message = `The provided login token is invalid.`;\n }\n toastr.error(`Error`, message, constants.toastrWarningOptions);\n });\n };\n\n render() {\n return (\n \n \n {' '}\n \n \n
\n \n \n \n \n \n
\n \n );\n }\n}\n\nconst mapStateToProps = (state, ownProps) => {\n return {\n user: state.user,\n loading: state.ajaxCallsInProgress > 0\n };\n};\n\nexport default connect(mapStateToProps, {\n userLogout,\n tokenLogin,\n getBookByID,\n getBLMByID,\n updateViewerMode,\n manualAjaxStart,\n manualAjaxEnd\n})(Token);\n","import * as types from './actionTypes';\n\nexport function updateViewerMode(viewerMode) {\n return function (dispatch) {\n dispatch({ type: types.UPDATE_MODE_SUCCESS, viewerMode });\n };\n}\n","import * as React from 'react';\nimport { Route, IndexRoute } from 'react-router';\nimport App from './App';\nimport LoginForm from './components/auth/LoginForm';\nimport RegisterForm from './components/auth/RegisterForm';\nimport BookView from './components/bookview/BookView';\nimport BookBag from './components/bookbag/BookBag';\nimport BLMView from './components/blm/BLMView';\nimport Token from './components/auth/Token';\nimport { hashHistory } from 'react-router';\n\nexport const requireSignIn = (user, location) => {\n const redirect = `${location.pathname}${location.search}`;\n if (\n !user ||\n (user && !user.APIKey) ||\n (user && user.APIKey && user.APIKey.length === 0)\n ) {\n console.info('sign in required, redirecting user', redirect, user);\n hashHistory.push(`/?redirect=${redirect}`);\n return true;\n }\n return false;\n};\n\nexport default (\n \n \n \n \n \n {/* // All params for the BookBag are GET params... managed within the component */}\n \n \n {/* // All params for the Token component are GET params... managed within the component */}\n \n \n);\n","import config from '../api/config';\n\n/*\n * load state localStorage\n */\nexport const loadState = (key) => {\n var statePromise = new Promise((resolve, reject) => {\n try {\n const serializedState = localStorage.getItem(`state-${key}`);\n if (serializedState === null) {\n resolve(undefined);\n } else {\n resolve(JSON.parse(serializedState));\n }\n } catch (error) {\n console.error('Error loading state', error);\n resolve(undefined);\n }\n });\n return statePromise;\n};\n\n/*\n * Save state\n */\nexport const saveState = (key, state) => {\n try {\n const serializedState = JSON.stringify(state);\n localStorage.setItem(`state-${key}`, serializedState);\n } catch (error) {\n // ignore write error\n console.error('Error saving state', error);\n }\n};\n","import config from './config';\nimport { loadState } from '../store/localStorage';\n\nexport const CL_DEBUG = 1;\nexport const CL_VERBOSE = 2;\nexport const CL_INFO = 3;\nexport const CL_WARN = 4;\nexport const CL_ERROR = 5;\nexport const CL_CRITICAL = 6;\n\nexport class Logger {\n static log(severity, ...args) {\n return loadState('dibs').then((state) => {\n let text = { ...args };\n if (severity === CL_ERROR || severity === CL_CRITICAL) {\n text = { ...args, reduxStore: state };\n }\n var obj = {\n privateKey: config.Coralogix.key,\n applicationName: 'DiBS.' + config.Environment,\n subsystemName: config.Coralogix.subsystemName,\n computerName:\n state && state.user\n ? `${state.user.First} ${state.user.Last}`\n : 'not logged in',\n logEntries: [\n {\n timestamp: new Date().getTime(),\n severity: severity,\n text: text,\n category:\n state && state.user\n ? state.user.APIKey\n : 'no api key'\n }\n ]\n };\n\n return this.sendToCoralogix(obj);\n });\n }\n\n static sendToCoralogix(obj) {\n if (process.env.NODE_ENV !== 'production') {\n return true;\n }\n return fetch('https://api.coralogix.com/api/v1/logs', {\n method: 'POST',\n headers: new Headers({ 'content-type': 'application/json' }),\n body: JSON.stringify(obj)\n })\n .then(function (response) {\n return response.text();\n })\n .then(function (data) {\n return data;\n })\n .catch(function (ex) {\n // if logging fails... what can ya do?\n throw ex;\n });\n }\n}\n","import { map, pickBy, find } from 'lodash';\nimport * as types from '../actions/actionTypes';\nimport initialState from './initialState';\n\nconst normalizeBook = (rawBook) => {\n return {\n ...initialState.book,\n ...pickBy(rawBook, (property) => property !== null)\n };\n};\n\n/*\n * Book Reducer\n */\nexport function bookReducer(state = initialState.book, action) {\n switch (action.type) {\n case types.FETCH_BOOK_SUCCESS:\n return normalizeBook(action.book);\n case types.FETCH_BOOK_FAILED: {\n return initialState.book;\n }\n case types.LOAD_BOOKS_SUCCESS:{\n const foundBook = find(action.books, {ID: state.ID})\n if (foundBook){\n return {...state, ...normalizeBook(foundBook)}\n }\n return state;\n }\n\n case types.LOAD_BOOK_SUCCESS:\n return Object.assign({}, state, action.book);\n\n case types.FETCH_BOOKITEMS_STUDENT_SUCCESS:\n // when connecting back online, dispatch actions might have been triggered for a different book\n if (state.ID !== action.bookID) {\n return state;\n }\n return Object.assign({}, state, {\n studentBookmarks: action.bookmarks,\n studentHighlights: action.highlights,\n studentNotes: action.notes\n });\n\n case types.FETCH_BOOKITEMS_SUCCESS:\n // when connecting back online, dispatch actions might have been triggered for a different book\n if (state.ID !== action.bookID) {\n return state;\n }\n return Object.assign({}, state, {\n bookmarks: action.bookmarks,\n highlights: action.highlights,\n notes: action.notes\n });\n\n case types.SAVE_BOOKITEM_BOOKMARK_SUCCESS:\n // when connecting back online, dispatch actions might have been triggered for a different book\n if (state.ID !== action.bookID) {\n return state;\n }\n let bookmarks = [];\n if (!state.bookmarks) {\n Object.assign({}, state, {\n bookmarks: bookmarks\n });\n }\n bookmarks = [\n ...state.bookmarks.filter(\n (bookmark) => bookmark.ID !== action.bookmark.ID\n ),\n Object.assign({}, action.bookmark)\n ];\n return Object.assign({}, state, {\n bookmarks: bookmarks\n });\n\n case types.SAVE_BOOKITEM_NOTE_SUCCESS:\n // when connecting back online, dispatch actions might have been triggered for a different book\n if (state.ID !== action.bookID) {\n return state;\n }\n let notes = [];\n if (!state.notes) {\n Object.assign({}, state, {\n notes: notes\n });\n }\n\n if (action.note.ID === undefined) {\n // this is for non-students who only save these notes locally - thus they never get a real ID\n notes = [\n ...state.notes.filter(\n (note) => note.TempID !== action.note.TempID\n ),\n Object.assign({}, action.note)\n ];\n } else {\n notes = [\n ...state.notes.filter((note) => note.ID !== action.note.ID),\n Object.assign({}, action.note)\n ];\n }\n notes.sort((a, b) => {\n return a.Page > b.Page;\n });\n return Object.assign({}, state, {\n notes: notes\n });\n\n case types.SAVE_BOOKITEM_HIGHLIGHT_SUCCESS:\n // when connecting back online, dispatch actions might have been triggered for a different book\n if (state.ID !== action.bookID) {\n return state;\n }\n return Object.assign({}, state, {\n highlights: action.highlights\n });\n\n case types.DELETE_BOOKITEM_BOOKMARK_SUCCESS:\n // when connecting back online, dispatch actions might have been triggered for a different book\n if (state.ID !== action.bookID) {\n return state;\n }\n return Object.assign({}, state, {\n bookmarks: state.bookmarks.filter(\n (bookmark) => bookmark.ID !== action.bookmark.ID\n )\n });\n\n case types.DELETE_BOOKITEM_NOTE_SUCCESS:\n // when connecting back online, dispatch actions might have been triggered for a different book\n if (state.ID !== action.bookID) {\n return state;\n }\n\n return Object.assign({}, state, {\n notes: state.notes.filter((note) => note.ID !== action.note.ID)\n });\n // TODO do we need to support teachers deleting student notes?\n // case types.TEACHER_DELETE_BOOKITEM_NOTE_SUCCESS:\n // if (state.ID !== action.bookID){\n // return state;\n // }\n // return Object.assign({}, state, {\n // 'studentNotes': state.studentNotes.filter((note) => note.ID !== action.note.ID)\n // });\n\n // case types.UPDATE_BOOKSTATUS_SUCCESS:\n // if (state.ID !== action.bookID) {\n // return state;\n // } else {\n // // we send 0 when initially opening the book and -1 when closing it. Don't actually set this to the current page\n // if (action.pageIndex === 0 || action.pageIndex === -1){\n // action.pageIndex = state.currentPage ? state.currentPage : 0;\n // }\n // const book = Object.assign({}, state, { currentPage: action.pageIndex });\n // return Object.assign({}, state, book);\n // }\n\n case types.PURGE_TEMP_ID_NOTES:\n // when connecting back online, dispatch actions might have been triggered for a different book\n if (state.ID !== action.bookID) {\n return state;\n }\n return Object.assign({}, state, {\n notes: state.notes.filter((note) => !note.TempID)\n });\n\n case types.CACHE_BOOK_PAGE:\n // if (!state.cachedPages[action.page.pageKey].pageKey){\n const newCachedPage = Object.assign(\n {},\n state.cachedPages[action.page.pageKey],\n { ...action.page, pageReady: true }\n );\n return Object.assign({}, state, {\n cachedPages: {\n ...state.cachedPages,\n [action.page.pageKey]: newCachedPage\n }\n });\n // } else {\n // return state;\n // }\n case types.RESET_CACHED_BOOK_PAGES:\n return { ...state, cachedPages: {} };\n case types.NEXT_PAGE:\n return { ...state, currentPage: state.currentPage + 1 };\n case types.PREV_PAGE:\n return { ...state, currentPage: state.currentPage - 1 };\n case types.UPDATE_CURRENT_PAGE:\n return { ...state, currentPage: action.currentPage };\n case types.SET_BOOK_READY:\n return { ...state, bookIsReady: action.ready };\n case types.TOTAL_PAGE:\n return { ...state, pagecount: action.totalPage };\n case types.GET_SPEECH_MARKS:\n if (!state.cachedPages[action.pageKey]) {\n const newPage = Object.assign(\n {},\n { speechMarks: action.parsedMarks, pageReady: false }\n );\n const newCachedPages = {\n ...state.cachedPages,\n [action.pageKey]: newPage\n };\n return Object.assign({}, state, {\n cachedPages: newCachedPages\n });\n } else {\n return state;\n }\n case types.USER_LOGOUT_SUCCESS:\n return initialState.book;\n case types.RESET_BOOK:\n return initialState.book;\n\n default:\n return state;\n }\n}\n/*\n * Books Reducer\n */\nexport function booksReducer(state = initialState.books, action) {\n switch (action.type) {\n case types.LOAD_BOOKS_SUCCESS:\n return map(action.books, normalizeBook);\n case types.ADD_BOOK_TO_BOOKBAG_SUCCESS:\n // return [...state, normalizeBook(action.book)]; // don't do anything because the user will switch back and hit search again\n case types.REMOVE_BOOK_TO_BOOKBAG_SUCCESS:\n return state.filter((book) => book.ID !== action.bookID);\n case types.UPDATE_CURRENT_PAGE:\n let newBook = state.filter((b) => {\n return b.ID === action.bookID;\n })[0];\n if (!newBook) {\n return state;\n }\n return [\n ...state.filter((b) => {\n return b.ID !== action.bookID;\n }),\n Object.assign({}, newBook, { currentPage: action.currentPage })\n ];\n\n case types.USER_LOGOUT_SUCCESS:\n return initialState.books;\n\n default:\n return state;\n }\n}\n\n/*\n * Downloaded Books Reducer\n */\nexport function downloadedBooksReducer(\n state = initialState.downloadedBooks,\n action\n) {\n switch (action.type) {\n case types.DOWNLOADED_BOOK_SUCCESS:\n // filter the array for this book already on the array, it it is not there then add it\n console.error('why is it downloading')\n return [\n ...state.filter((db) => {\n return db.ID !== action.book.ID;\n }),\n Object.assign({}, normalizeBook(action.book))\n ];\n case types.LOAD_BOOKS_SUCCESS:{\n const newBooks = map(action.books, normalizeBook);\n return map(state, (book)=>{\n const foundNewBook = find(newBooks, {ID: book.ID});\n if (foundNewBook){\n return {...book, ...foundNewBook}\n } else {\n return book;\n }\n })\n }\n case types.DELETE_DOWNLOADED_BOOK: {\n return [...state.filter((db) => db.ISBN !== action.payload)];\n }\n\n case types.FETCH_BOOKITEMS_SUCCESS:\n let downloadedBook = state.filter((b) => {\n return b.ID === action.bookID;\n })[0];\n if (!downloadedBook) {\n return state;\n }\n downloadedBook = Object.assign({}, downloadedBook, {\n bookmarks: action.bookmarks,\n highlights: action.highlights,\n notes: action.notes\n });\n if (!downloadedBook.ID) {\n console.error(\n 'Error: undefined book ID in saving bookitem highlight.'\n );\n return state;\n }\n return [\n ...state.filter((b) => {\n return b.ID !== action.bookID;\n }),\n downloadedBook\n ];\n\n case types.SAVE_BOOKITEM_HIGHLIGHT_SUCCESS:\n let book = state.filter((b) => {\n return b.ID === action.bookID;\n })[0];\n if (!book) {\n return state;\n }\n book = Object.assign({}, book, { highlights: action.highlights });\n if (!book.ID) {\n console.error(\n 'Error: undefined book ID in saving bookitem highlight.'\n );\n return state;\n }\n\n // check to see if state has any length, if not return\n\n return [\n ...state.filter((b) => {\n return b.ID !== action.bookID;\n }),\n book\n ];\n\n case types.SAVE_BOOKITEM_NOTE_SUCCESS:\n let bookNote = state.filter((b) => {\n return b.ID === action.bookID;\n })[0];\n // console.log('book note', bookNote, action.bookID, state)\n if (!bookNote || (bookNote && !bookNote.notes)) {\n return state;\n }\n let notes = [];\n if (action.note.ID === undefined) {\n // this is for non-students who only save these notes locally - thus they never get a real ID\n notes = [\n ...bookNote.notes.filter(\n (note) => note.TempID !== action.note.TempID\n ),\n Object.assign({}, action.note)\n ];\n } else {\n notes = [\n ...bookNote.notes.filter(\n (note) => note.ID !== action.note.ID\n ),\n Object.assign({}, action.note)\n ];\n }\n\n notes.sort((a, b) => {\n return a.Page > b.Page;\n });\n bookNote = Object.assign({}, bookNote, { notes: notes });\n\n return [\n ...state.filter((b) => {\n return b.ID !== action.bookID;\n }),\n bookNote\n ];\n\n case types.UPDATE_CURRENT_PAGE:\n let newBook = state.filter((b) => {\n return b.ID === action.bookID;\n })[0];\n if (!newBook) {\n return state;\n }\n return [\n ...state.filter((b) => {\n return b.ID !== action.bookID;\n }),\n Object.assign({}, newBook, { currentPage: action.currentPage })\n ];\n\n case types.DELETE_BOOKITEM_BOOKMARK_SUCCESS:\n // dlBookBookmark is a downloaded book object and in this use case, we are deleting a bookmark\n let dlBookBookmark = state.filter((b) => {\n return b.ID === action.bookID;\n })[0];\n if (!dlBookBookmark) {\n return state;\n }\n Object.assign({}, dlBookBookmark, {\n bookmarks: dlBookBookmark.bookmarks.filter(\n (bookmark) => bookmark.ID !== action.bookmark.ID\n )\n });\n if (!dlBookBookmark.ID) {\n console.error(\n 'Error: undefined book ID in saving bookitem highlight.'\n );\n return state;\n }\n // if something unexpected happens return state\n return state;\n\n case types.DELETE_BOOKITEM_NOTE_SUCCESS:\n // dlBookNote is a downloaded book object and in this use case, we are deleting the note\n let dlBookNote = state.filter((b) => {\n return b.ID === action.bookID;\n })[0];\n if (!dlBookNote) {\n return state;\n }\n\n if (action.note.ID === undefined) {\n // this is for non-students who only save these notes locally - thus they never get a real ID\n notes = [\n ...dlBookNote.notes.filter(\n (note) => note.TempID !== action.note.TempID\n ),\n Object.assign({}, action.note)\n ];\n } else {\n notes = [\n ...dlBookNote.notes.filter(\n (note) => note.ID !== action.note.ID\n ),\n Object.assign({}, action.note)\n ];\n }\n if (!dlBookNote.ID) {\n console.error(\n 'Error: undefined book ID in saving bookitem highlight.'\n );\n return state;\n }\n // if something unexpected happens, return state\n return state;\n\n case types.PURGE_TEMP_ID_NOTES:\n // dlPurgeTempNotes is a downloaded book object and in this use case, we are purging any temp ID's\n let dlPurgeTempNotes = state.filter((b) => {\n return b.ID === action.bookID;\n })[0];\n if (!dlPurgeTempNotes) {\n return state;\n }\n dlPurgeTempNotes = Object.assign({}, dlPurgeTempNotes, {\n notes: dlPurgeTempNotes.notes.filter((note) => !note.TempID)\n });\n\n return [\n ...state.filter((b) => {\n return b.ID !== action.bookID;\n }),\n dlPurgeTempNotes\n ];\n\n case types.DELETE_BOOKITEM_HIGHLIGHT_SUCCESS:\n // dlBookHighlight is a downloaded book object and in this use case, we are deleting the highlight\n let dlBookHighlight = state.filter((b) => {\n return b.ID === action.bookID;\n })[0];\n if (!dlBookHighlight) {\n return state;\n }\n return Object.assign({}, dlBookHighlight, {\n highlights: dlBookHighlight.highlights\n });\n\n case types.USER_LOGOUT_SUCCESS:\n return initialState.downloadedBooks;\n\n default:\n return state;\n }\n}\n\nexport function dashboardPageResultsReducer(state = 1, action) {\n switch (action.type) {\n case types.DASHBOARD_PAGE_RESULTS:\n if (action.pageResults && action.pageResults > 0) {\n return action.pageResults;\n }\n return state;\n case types.USER_LOGOUT_SUCCESS:\n return 1;\n default:\n return state;\n }\n}\n","import * as types from '../actions/actionTypes';\nimport initialState from './initialState';\n\nfunction actionTypeEndsInSuccess(type) {\n return type.substring(type.length - 8) === '_SUCCESS';\n}\n\nfunction actionTypeEndsInFailed(type) {\n return type.substring(type.length - 7) === '_FAILED';\n}\n\nexport default function ajaxStatusReducer(\n state = initialState.ajaxCallsInProgress,\n action\n) {\n if (action.type === types.BEGIN_AJAX_CALL) {\n return state + 1;\n } else if (\n state >= 1 &&\n (actionTypeEndsInSuccess(action.type) ||\n actionTypeEndsInFailed(action.type))\n ) {\n return state - 1;\n } else if (state >= 1 && action.type === types.END_AJAX_CALL) {\n return state - 1;\n }\n\n return state;\n}\n","import { reducer as offlineQueue } from 'redux-queue-offline';\nimport { combineReducers } from 'redux';\nimport { routerReducer as routing } from 'react-router-redux';\nimport { reducer as toastr, ToastrState } from 'react-redux-toastr';\nimport user from './userReducer';\nimport { bookbagFiltersReducer as bookbagFilters } from './bookbagFiltersReducer';\nimport { dashboard } from './dashboardReducer';\n\nimport { reducer as jPlayers } from 'react-jplayer';\nimport {\n blmReducer as blm,\n blmsReducer as blms,\n completedBLMsReducer as completedBLMs\n} from './blmReducer';\nimport {\n bookReducer as book,\n booksReducer as books,\n downloadedBooksReducer as downloadedBooks,\n dashboardPageResultsReducer as dashboardPageResults\n} from './bookReducer';\nimport bookView from './bookViewReducer';\nimport { projectToolbar } from './projectToolbarReducer';\nimport bookToolbar from './bookToolbarReducer';\nimport ajaxCallsInProgress from './ajaxStatusReducer';\nimport viewerMode from './modeReducer';\nimport readium from './readiumReducer';\n\nconst rootReducer = combineReducers({\n appVersion: (state = {}) => state,\n offlineQueue,\n toastr,\n user,\n blms,\n completedBLMs,\n blm,\n book,\n books,\n downloadedBooks,\n routing,\n viewerMode,\n bookbagFilters,\n dashboard,\n ajaxCallsInProgress,\n jPlayers,\n projectToolbar,\n bookView,\n bookToolbar,\n dashboardPageResults,\n readium,\n});\n\nexport type IinitialState = ReturnType &\n { toastr: ToastrState };\n\nexport default rootReducer;\n","/*\n * Reducer for the user and user account\n */\n\nimport * as types from '../actions/actionTypes';\nimport initialState from './initialState';\n\nexport default function userReducer(state = initialState.user, action) {\n switch (action.type) {\n case types.USER_LOGIN_SUCCESS:\n case types.USER_UPDATE_SUCCESS:\n case types.USER_UPDATE_PASSWORD_SUCCESS:\n const lastCheckSession = Date.now();\n return {...state, ...action.user, lastCheckSession}\n case types.USER_LOGOUT_SUCCESS:\n return initialState.user;\n\n default:\n return state;\n }\n}\n","import * as types from '../actions/actionTypes';\nimport initialState from './initialState';\n\nexport function blmsReducer(state = initialState.blms, action) {\n switch (action.type) {\n case types.LOAD_BLMS_SUCCESS:\n return action.blms;\n\n default:\n return state;\n }\n}\n\nexport function completedBLMsReducer(\n state = initialState.completedBLMs,\n action\n) {\n switch (action.type) {\n case types.GET_COMPLETED_BLMS_SUCCESS:\n return action.completedBLMs;\n\n default:\n return state;\n }\n}\n\nexport function blmReducer(state = initialState.blm, action) {\n switch (action.type) {\n case types.FETCH_BLM_SUCCESS:\n return action.blm;\n\n case types.GET_BLM_ITEMS_SUCCESS:\n return Object.assign({}, state, {\n blmData: action.blmData,\n fabricItems: action.fabricItems,\n originalBLMTemplate: action.originalBLMTemplate\n });\n\n case types.SAVE_TEACHERS_COMMENT_SUCCESS:\n return Object.assign({}, state, {\n teachersComment: action.teachersComment\n });\n\n case types.GET_TEACHERS_BLM_COMMENT_SUCCESS:\n return Object.assign({}, state, {\n teachersComment: action.teachersComment\n });\n\n case types.GET_BLM_STATUS_SUCCESS:\n return Object.assign({}, state, {\n isComplete: action.isComplete\n });\n\n case types.SAVE_BLMITEM_FABRIC_SUCCESS:\n return Object.assign({}, state, {\n blmData: action.blmData\n });\n\n case types.UPDATE_STUDENT_ITEM_SUCCESS:\n return Object.assign({}, state, {\n fabricItemsStudent: action.fabricItemsStudent\n });\n case types.UPDATE_TEMPLATE_ITEM_SUCCESS:\n return Object.assign({}, state, {\n fabricItemsTemplate: action.fabricItemsTemplate\n });\n\n case types.UPDATE_BLM_STATUS_SUCCESS:\n return Object.assign({}, state, {\n isComplete: action.isComplete\n });\n\n case types.REMOVE_BLM_DATA_SUCCESS:\n return action.blm;\n case types.SET_PROJECT_READY:\n return { ...state, projectIsReady: action.ready };\n\n case types.USER_LOGOUT_SUCCESS:\n return initialState.blms;\n\n default:\n return state;\n }\n}\n","import * as types from '../actions/actionTypes';\nimport initialState from './initialState';\n\nexport default function modeReducer(state = initialState.viewerMode, action) {\n switch (action.type) {\n case types.UPDATE_MODE_SUCCESS:\n return action.viewerMode;\n\n case types.USER_LOGOUT_SUCCESS:\n return initialState.viewerMode;\n\n default:\n return state;\n }\n}\n","import * as types from '../actions/actionTypes';\nimport initialState from './initialState';\n\nexport function bookbagFiltersReducer(\n state = initialState.bookbagFilters,\n action\n) {\n switch (action.type) {\n case types.SAVE_ADVANCED_SEARCH_FILTERS:\n return { ...state, ...action.filters };\n // return Array.from(new Set(action.filters.tags));\n case types.CLEAR_ADVANCED_SEARCH_FILTERS:\n return initialState.bookbagFilters;\n case types.USER_LOGOUT_SUCCESS:\n return initialState.bookbagFilters;\n\n default:\n return state;\n }\n}\n","/*\n * This reducer contains all the visual state of the dashboard\n */\n\nimport * as types from '../actions/actionTypes';\nimport initialState from './initialState';\n\nexport function dashboard(state = initialState.dashboard, action) {\n switch (action.type) {\n case types.TOGGLE_USER_PROFILE_MODAL:\n return {\n ...state,\n showUserProfileModal: !state.showUserProfileModal\n };\n\n case types.TOGGLE_LOGGOUT_CONFIRM_MODAL:\n return {\n ...state,\n showLoggoutConfirmModal: !state.showLoggoutConfirmModal\n };\n case types.TOGGLE_CLASS_CODE_MODAL:\n let showClassCodeModal = !state.showClassCodeModal;\n if (action.show && typeof action.show === 'boolean'){\n showClassCodeModal = action.show;\n }\n return {\n ...state,\n showClassCodeModal\n };\n case types.USER_LOGOUT_SUCCESS:\n return initialState.dashboard;\n\n default:\n return state;\n }\n}\n","import * as types from '../actions/actionTypes';\nimport initialState from './initialState';\n\nexport function projectToolbar(state = initialState.projectToolbar, action) {\n switch (action.type) {\n case types.TOOLBAR_ERASER_ON:\n return Object.assign({}, state, action.projectToolbar);\n case types.TOOLBAR_POINTER_ON:\n return Object.assign({}, state, action.projectToolbar);\n case types.TOOLBAR_DRAWING_MODE_ON:\n return Object.assign({}, state, action.projectToolbar);\n case types.TOOLBAR_ADD_TEXT_ON:\n return Object.assign({}, state, action.projectToolbar);\n case types.SHOW_RICH_TEXT_BUTTONS:\n return Object.assign({}, state, action.projectToolbar);\n default:\n return state;\n }\n}\n","/*\n * This reducer contains all the visual state of the viewer\n */\n\nimport * as types from '../actions/actionTypes';\nimport initialState from './initialState';\nimport constants from '../constants/constants';\n\nexport default function bookViewReducer(state = initialState.bookView, action) {\n // dispatch all redux events as document events - probably a bad idea\n document.dispatchEvent(new CustomEvent(action.type, {detail: action}))\n switch (action.type) {\n case types.USER_LOGOUT_SUCCESS:\n return initialState.bookView;\n case types.RESET_VIEW:\n return initialState.bookView;\n case types.UPDATE_BOOK_SCALE_PERCENT:\n return { ...state, bookScalePercent: action.scale };\n case types.UPDATE_PROJECT_SCALE_PERCENT:\n return { ...state, projectScalePercent: action.scale };\n case types.UPDATE_LEFT_PAGE_CONTAINER:\n return {\n ...state,\n leftPageContainerWidth: action.width,\n leftPageContainerHeight: action.height\n };\n case types.UPDATE_PAGES_VISIBLE:\n return { ...state, pagesVisible: action.pagesVisible };\n case types.AUTOMATIC_UPDATE_PAGES_VISIBLE:\n if (state.automaticUpdatedPagesVisible) {\n return state;\n }\n return {\n ...state,\n pagesVisible: action.pagesVisible,\n automaticUpdatedPagesVisible: true\n };\n\n case types.BOOK_TOOLBAR_UPDATE_MODE:\n return { ...state, bookToolbar: action.bookToolbar };\n case types.SET_ACTIVE_NOTE:\n return { ...state, activeNoteID: action.noteID };\n case types.INCREASE_BOOK_ZOOM:\n const newZoomLevelIndex = state.bookManualZoomIndex + 1;\n const newZoomLevel = constants.bookZoomLevels[newZoomLevelIndex];\n if (newZoomLevel === undefined) {\n return state;\n }\n if (\n newZoomLevelIndex === initialState.bookView.bookManualZoomIndex\n ) {\n return {\n ...state,\n bookManualZooming: false,\n bookManualZoomIndex: newZoomLevelIndex,\n bookManualZoomLevel: newZoomLevel / 100\n };\n } else {\n return {\n ...state,\n bookManualZooming: true,\n bookManualZoomIndex: newZoomLevelIndex,\n bookManualZoomLevel: newZoomLevel / 100\n };\n }\n case types.DECREASE_BOOK_ZOOM:\n const newZoomLevelIndexD = state.bookManualZoomIndex - 1;\n const newZoomLevelD = constants.bookZoomLevels[newZoomLevelIndexD];\n if (newZoomLevelD === undefined) {\n return state;\n }\n if (\n newZoomLevelIndexD === initialState.bookView.bookManualZoomIndex\n ) {\n return {\n ...state,\n bookManualZooming: false,\n bookManualZoomIndex: newZoomLevelIndexD,\n bookManualZoomLevel: newZoomLevelD / 100\n };\n } else {\n return {\n ...state,\n bookManualZooming: true,\n bookManualZoomIndex: newZoomLevelIndexD,\n bookManualZoomLevel: newZoomLevelD / 100\n };\n }\n case types.UPDATE_PENDING_ITEM:\n return { ...state, itemSavePending: action.status };\n\n // case types.BOOK_UPDATE_AUDIO:\n // return { ...state, audioUrl: action.audioUrl}\n\n default:\n return state;\n }\n}\n","/*\n * This reducer contains all the state for managing the book toolbar\n */\n\nimport * as types from '../actions/actionTypes';\nimport initialState from './initialState';\n\nexport default function bookToolbar(state = initialState.bookToolbar, action) {\n switch (action.type) {\n case types.USER_LOGOUT_SUCCESS:\n return initialState.bookToolbar;\n case types.BOOK_TOOLBAR_POINTING:\n return initialState.bookToolbar;\n case types.BOOK_TOOLBAR_NOTES:\n return {\n ...initialState.bookToolbar,\n allowNotes: action.allowNotes,\n pointing: false,\n pagesClassName: 'note-mode'\n };\n case types.BOOK_TOOLBAR_HIGHLIGHTING:\n return {\n ...initialState.bookToolbar,\n highlighting: action.highlighting,\n highlightColor: action.highlightColor,\n pointing: false,\n pagesClassName: `hl-mode-${action.highlightColor}`\n };\n case types.BOOK_TOOLBAR_ERASING:\n return {\n ...initialState.bookToolbar,\n erasing: action.erasing,\n pointing: false,\n pagesClassName: 'eraser-mode'\n };\n case types.BOOK_TOOLBAR_UNDERLINING:\n return {\n ...initialState.bookToolbar,\n underlining: action.underlining,\n pointing: false,\n pagesClassName: 'hl-underline'\n };\n case types.BOOK_TOOLBAR_STRIKING:\n return {\n ...initialState.bookToolbar,\n striking: action.striking,\n pointing: false,\n pagesClassName: 'hl-strikethrough'\n };\n case types.BOOK_TOOLBAR_UPDATE_MODE:\n return { ...state, bookToolbar: action.bookToolbar };\n case types.BOOK_TOOLBAR_MOVING:\n return {\n ...initialState.bookToolbar,\n moving: true,\n pointing: true,\n pagesClassName: 'move-mode'\n };\n default:\n return state;\n }\n}\n","import { READIUM_INITIALIZED } from '../actions/actionTypes';\nimport initialState from './initialState';\n\nexport default function readiumReducer(state = initialState.readium, action) {\n switch (action.type) {\n case READIUM_INITIALIZED:\n return { ...state, readiumClass: action.readiumClass };\n default:\n return state;\n }\n}\n","import { IinitialState } from '../reducers/index';\nimport initialState from '../reducers/initialState';\n\n/*\n * Migration 1 runs when upgrading from 0 to 1\n */\n\nconst typedMigrations = {\n 162: (state: IinitialState): IinitialState => {\n console.log('running migration 162', state.appVersion);\n try {\n const serializedState = localStorage.getItem(`state-dibs`);\n if (serializedState !== null) {\n console.log(\"migrating previous state\")\n localStorage.removeItem('state-dibs');\n return {\n ...JSON.parse(serializedState),\n ...state\n };\n }\n } catch {\n console.log('no previous state');\n }\n const { downloadedBooks } = initialState;\n return {\n ...state,\n downloadedBooks\n };\n }\n} as { [key: number]: (state: IinitialState) => IinitialState };\n\nconst untypedMigrations = typedMigrations as any;\nexport const migrations = untypedMigrations;\n","import { middleware as offlineQueueMiddleware } from 'redux-queue-offline';\nimport { createStore, applyMiddleware, compose } from 'redux';\nimport rootReducer from '../reducers';\nimport thunk from 'redux-thunk';\nimport * as createMigration from 'redux-persist-migrate';\nimport initialState from '../reducers/initialState';\nimport {persistStore, autoRehydrate} from 'redux-persist';\n\nimport {\n saveBookItem,\n updateBookStatus,\n deleteBookItem\n} from '../actions/bookActions';\nimport { saveBLMItem } from '../actions/blmActions';\nimport { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';\nimport BookFS from '../api/bookFS';\nimport { migrations } from './migrations';\n\nexport const persistConfig = {\n // keyPrefix: 'dibs',\n debounce: 300,\n storage: BookFS.fs,\n blacklist: ['showEditProfileModal', 'ajaxCallsInProgress', 'toastr']\n // transforms: [createTransform(encode, decode)]\n};\n// see https://github.com/diegoddox/react-redux-toastr/issues/249 about blacklisting toastr\nconst migration = createMigration.default(migrations, 'appVersion');\n\nconst offlineActionCreators = {\n saveBookItem: saveBookItem,\n updateBookStatus: updateBookStatus,\n deleteBookItem: deleteBookItem,\n saveBLMItem: saveBLMItem\n};\n\n\nexport const configureStore = (persistCallback) => {\n if (process.env.NODE_ENV !== 'production') {\n // if (false){ // comment in for testing localling without redux dev tools\n\n const store = createStore(\n rootReducer,\n initialState,\n composeWithDevTools(\n applyMiddleware(\n offlineQueueMiddleware(\n 'offlineQueue',\n ['payload.promise'],\n offlineActionCreators\n ),\n thunk\n ),\n compose(autoRehydrate(), migration) // , require('redux-immutable-state-invariant').default()\n )\n );\n const persistor = persistStore(store, persistConfig, persistCallback);\n return {store, persistor};\n } else {\n const store = createStore(\n rootReducer,\n initialState,\n compose(applyMiddleware(\n offlineQueueMiddleware(\n 'offlineQueue',\n ['payload.promise'],\n offlineActionCreators\n ),\n thunk,\n ),\n autoRehydrate(), migration)\n );\n const persistor = persistStore(store, persistConfig, persistCallback);\n return {store, persistor};\n }\n}\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://cra.link/PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://cra.link/PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then((registration) => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://cra.link/PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch((error) => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' }\n })\n .then((response) => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null &&\n contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then((registration) => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready\n .then((registration) => {\n registration.unregister();\n })\n .catch((error) => {\n console.error(error.message);\n });\n }\n}\n","const reportWebVitals = (onPerfEntry) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(\n ({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n }\n );\n }\n};\n\nexport default reportWebVitals;\n","import { READIUM_INITIALIZED } from './actionTypes';\n\nexport const readiumInitialized = (readiumClass) => {\n return { type: READIUM_INITIALIZED, readiumClass };\n};\n","/* eslint-disable */\nimport * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport constants from './constants/constants';\n\nimport { NetworkListener } from './utilities/NetworkListener';\nimport { Provider } from 'react-redux';\nimport { Router, hashHistory } from 'react-router';\nimport routes from './routes';\nimport { Logger, CL_WARN, CL_ERROR } from './api/logger';\nimport config from './api/config';\nimport ReduxToastr from 'react-redux-toastr';\nimport {configureStore} from './store/configureStore';\n\nimport 'whatwg-fetch';\n// import 'bootstrap/dist/css/bootstrap.min.css';\nimport 'font-awesome/css/font-awesome.min.css';\nimport './App.scss';\nimport 'bootstrap/dist/js/bootstrap.min.js';\nimport './vendor/animate.min.css';\nimport './vendor/toastr.min.css';\nimport './vendor/reactReduxToaster.min.css';\nimport './vendor/fonts.css';\nimport * as serviceWorkerRegistration from './serviceWorkerRegistration';\nimport reportWebVitals from './reportWebVitals';\nimport { readiumInitialized } from './actions/readiumActions';\n\n// print version\nconsole.log(\n `***** Version: ${config.Version} **** Environment: ${config.Environment}`\n);\n\nconst NetworkListenerProvider = NetworkListener(Provider);\n\nconstants.themeProvider\n.initTheme()\n\nclass IndexClass extends React.Component {\n constructor(props) {\n super(props);\n this.state = { isRehydrated: false };\n const {store, persistor} = configureStore(() => {\n this.setState({ isRehydrated: true });\n });\n this.store = store;\n this.persistor = persistor;\n this.listenToReadiumInit();\n }\n render() {\n if (!this.state.isRehydrated) {\n return null;\n }\n return (\n // \n \n \n \n \n
\n \n // \n );\n }\n\n listenToReadiumInit() {\n if (window.readiumClass) {\n this.store.dispatch(readiumInitialized(window.readiumClass));\n return;\n }\n const handler = (ev) => {\n if (ev.data === 'readium-initialized') {\n window.removeEventListener('message', handler);\n this.store.dispatch(readiumInitialized(window.readiumClass));\n }\n };\n window.addEventListener('message', handler);\n }\n}\n\nReactDOM.render(\n ,\n document.getElementById('root')\n);\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://cra.link/PWA\nserviceWorkerRegistration.register({\n onUpdate: (registration) => {\n document.dispatchEvent(new Event('newVersionAvailable'));\n if (registration && registration.waiting) {\n registration.waiting.postMessage({ type: 'SKIP_WAITING' });\n }\n }\n});\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n\nwindow.console = (function (origConsole) {\n if (!window.console || !origConsole) origConsole = {};\n var isDebug = false,\n logArray = {\n logs: [],\n errors: [],\n warns: [],\n infos: []\n };\n return {\n log: function () {\n logArray.logs.push(arguments);\n // Logger.log(CL_DEBUG, ...arguments);\n isDebug &&\n origConsole.log &&\n origConsole.log.apply(origConsole, arguments);\n },\n warn: function () {\n logArray.warns.push(arguments);\n Logger.log(CL_WARN, ...arguments);\n isDebug &&\n origConsole.warn &&\n origConsole.warn.apply(origConsole, arguments);\n },\n error: function () {\n logArray.errors.push(arguments);\n Logger.log(CL_ERROR, ...arguments);\n isDebug &&\n origConsole.error &&\n origConsole.error.apply(origConsole, arguments);\n },\n info: function (v) {\n logArray.infos.push(arguments);\n // Logger.log(CL_INFO, ...arguments);\n isDebug &&\n origConsole.info &&\n origConsole.info.apply(origConsole, arguments);\n },\n debug: function (bool) {\n isDebug = bool;\n },\n logArray: function () {\n return logArray;\n }\n };\n})(window.console);\nwindow.console.debug(true);\n","import * as React from 'react';\nimport { ONLINE, OFFLINE } from 'redux-queue-offline';\n\nexport const NetworkListener = (Provider) => {\n return class NetworkListenerComponent extends React.Component {\n defaultProps = { store: {} };\n componentDidMount() {\n if (window && window.addEventListener) {\n window.addEventListener('online', this._onlineListener);\n window.addEventListener('offline', this._offlineListener);\n }\n }\n componentWillUnmount() {\n if (window && window.removeEventListener) {\n window.removeEventListener('online', this._onlineListener);\n window.removeEventListener('offline', this._offlineListener);\n }\n }\n render() {\n return React.createElement(Provider, this.props);\n }\n _onlineListener = () => {\n this.props.store.dispatch({\n type: ONLINE\n });\n };\n _offlineListener = () => {\n this.props.store.dispatch({\n type: OFFLINE\n });\n };\n };\n};\n"],"sourceRoot":""}