added iOS source code
[wl-app.git] / iOS / Pods / FolioReaderKit / Source / Resources / Bridge.js
diff --git a/iOS/Pods/FolioReaderKit/Source/Resources/Bridge.js b/iOS/Pods/FolioReaderKit/Source/Resources/Bridge.js
new file mode 100755 (executable)
index 0000000..41baa45
--- /dev/null
@@ -0,0 +1,639 @@
+//
+//  Bridge.js
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 06/05/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+var thisHighlight;
+var audioMarkClass;
+var wordsPerMinute = 180;
+
+document.addEventListener("DOMContentLoaded", function(event) {
+//    var lnk = document.getElementsByClassName("lnk");
+//    for (var i=0; i<lnk.length; i++) {
+//        lnk[i].setAttribute("onclick","return callVerseURL(this);");
+//    }
+});
+
+// Generate a GUID
+function guid() {
+    function s4() {
+        return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
+    }
+    var guid = s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
+    return guid.toUpperCase();
+}
+
+// Get All HTML
+function getHTML() {
+    return document.documentElement.outerHTML;
+}
+
+// Class manipulation
+function hasClass(ele,cls) {
+  return !!ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
+}
+
+function addClass(ele,cls) {
+  if (!hasClass(ele,cls)) ele.className += " "+cls;
+}
+
+function removeClass(ele,cls) {
+  if (hasClass(ele,cls)) {
+    var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
+    ele.className=ele.className.replace(reg,' ');
+  }
+}
+
+// Font name class
+function setFontName(cls) {
+    var elm = document.documentElement;
+    removeClass(elm, "andada");
+    removeClass(elm, "lato");
+    removeClass(elm, "lora");
+    removeClass(elm, "raleway");
+    addClass(elm, cls);
+}
+
+// Toggle night mode
+function nightMode(enable) {
+    var elm = document.documentElement;
+    if(enable) {
+        addClass(elm, "nightMode");
+    } else {
+        removeClass(elm, "nightMode");
+    }
+}
+
+// Set font size
+function setFontSize(cls) {
+    var elm = document.documentElement;
+    removeClass(elm, "textSizeOne");
+    removeClass(elm, "textSizeTwo");
+    removeClass(elm, "textSizeThree");
+    removeClass(elm, "textSizeFour");
+    removeClass(elm, "textSizeFive");
+    addClass(elm, cls);
+}
+
+// Set font size
+function setMarginSize(cls) {
+    var elm = document.documentElement;
+    removeClass(elm, "marginSizeOne");
+    removeClass(elm, "marginSizeTwo");
+    removeClass(elm, "marginSizeThree");
+    removeClass(elm, "marginSizeFour");
+    removeClass(elm, "marginSizeFive");
+    addClass(elm, cls);
+}
+
+// Set font size
+function setInterlineSize(cls) {
+    var elm = document.documentElement;
+    removeClass(elm, "interlineSizeOne");
+    removeClass(elm, "interlineSizeTwo");
+    removeClass(elm, "interlineSizeThree");
+    removeClass(elm, "interlineSizeFour");
+    removeClass(elm, "interlineSizeFive");
+    addClass(elm, cls);
+}
+
+/*
+ *     Native bridge Highlight text
+ */
+function highlightString(style) {
+    var range = window.getSelection().getRangeAt(0);
+    var startOffset = range.startOffset;
+    var endOffset = range.endOffset;
+    var selectionContents = range.extractContents();
+    var elm = document.createElement("highlight");
+    var id = guid();
+    
+    elm.appendChild(selectionContents);
+    elm.setAttribute("id", id);
+    elm.setAttribute("onclick","callHighlightURL(this);");
+    elm.setAttribute("class", style);
+    
+    range.insertNode(elm);
+    thisHighlight = elm;
+    
+    var params = [];
+    params.push({id: id, rect: getRectForSelectedText(elm), startOffset: startOffset.toString(), endOffset: endOffset.toString()});
+    
+    return JSON.stringify(params);
+}
+
+// Menu colors
+function setHighlightStyle(style) {
+    thisHighlight.className = style;
+    return thisHighlight.id;
+}
+
+function removeThisHighlight() {
+    thisHighlight.outerHTML = thisHighlight.innerHTML;
+    return thisHighlight.id;
+}
+
+function removeHighlightById(elmId) {
+    var elm = document.getElementById(elmId);
+    elm.outerHTML = elm.innerHTML;
+    return elm.id;
+}
+
+function getHighlightContent() {
+    return thisHighlight.textContent
+}
+
+function getBodyText() {
+    return document.body.innerText;
+}
+
+// Method that returns only selected text plain
+var getSelectedText = function() {
+    return window.getSelection().toString();
+}
+
+// Method that gets the Rect of current selected text
+// and returns in a JSON format
+var getRectForSelectedText = function(elm) {
+    if (typeof elm === "undefined") elm = window.getSelection().getRangeAt(0);
+    
+    var rect = elm.getBoundingClientRect();
+    return "{{" + rect.left + "," + rect.top + "}, {" + rect.width + "," + rect.height + "}}";
+}
+
+// Method that call that a hightlight was clicked
+// with URL scheme and rect informations
+var callHighlightURL = function(elm) {
+       event.stopPropagation();
+       var URLBase = "highlight://";
+    var currentHighlightRect = getRectForSelectedText(elm);
+    thisHighlight = elm;
+    
+    window.location = URLBase + encodeURIComponent(currentHighlightRect);
+}
+
+// Reading time
+function getReadingTime() {
+    var text = document.body.innerText;
+    var totalWords = text.trim().split(/\s+/g).length;
+    var wordsPerSecond = wordsPerMinute / 60; //define words per second based on words per minute
+    var totalReadingTimeSeconds = totalWords / wordsPerSecond; //define total reading time in seconds
+    var readingTimeMinutes = Math.round(totalReadingTimeSeconds / 60);
+
+    return readingTimeMinutes;
+}
+
+/**
+ Get Vertical or Horizontal paged #anchor offset
+ */
+var getAnchorOffset = function(target, horizontal) {
+    var elem = document.getElementById(target);
+    
+    if (!elem) {
+        elem = document.getElementsByName(target)[0];
+    }
+    
+    if (horizontal) {
+        return window.innerWidth * Math.floor(elem.offsetTop / window.innerHeight);
+    }
+    
+    return elem.offsetTop;
+}
+
+function findElementWithID(node) {
+    if( !node || node.tagName == "BODY")
+        return null
+    else if( node.id )
+        return node
+    else
+        return findElementWithID(node)
+}
+
+function findElementWithIDInView() {
+
+    if(audioMarkClass) {
+        // attempt to find an existing "audio mark"
+        var el = document.querySelector("."+audioMarkClass)
+
+        // if that existing audio mark exists and is in view, use it
+        if( el && el.offsetTop > document.body.scrollTop && el.offsetTop < (window.innerHeight + document.body.scrollTop))
+            return el
+    }
+
+    // @NOTE: is `span` too limiting?
+    var els = document.querySelectorAll("span[id]")
+
+    for(indx in els) {
+        var element = els[indx];
+
+        // Horizontal scroll
+        if (document.body.scrollTop == 0) {
+            var elLeft = window.innerWidth * Math.floor(element.offsetTop / window.innerHeight);
+            // document.body.scrollLeft = elLeft;
+
+            if (elLeft == document.body.scrollLeft) {
+                return element;
+            }
+
+        // Vertical
+        } else if(element.offsetTop > document.body.scrollTop) {
+            return element;
+        }
+    }
+
+    return null
+}
+
+
+/**
+ Play Audio - called by native UIMenuController when a user selects a bit of text and presses "play"
+ */
+function playAudio() {
+    var sel = getSelection();
+    var node = null;
+
+    // user selected text? start playing from the selected node
+    if (sel.toString() != "") {
+        node = sel.anchorNode ? findElementWithID(sel.anchorNode.parentNode) : null;
+
+    // find the first ID'd element that is within view (it will
+    } else {
+        node = findElementWithIDInView()
+    }
+
+    playAudioFragmentID(node ? node.id : null)
+}
+
+
+/**
+ Play Audio Fragment ID - tells page controller to begin playing audio from the following ID
+ */
+function playAudioFragmentID(fragmentID) {
+    var URLBase = "play-audio://";
+    window.location = URLBase + (fragmentID?encodeURIComponent(fragmentID):"")
+}
+
+/**
+ Go To Element - scrolls the webview to the requested element
+ */
+function goToEl(el) {
+    var top = document.body.scrollTop;
+    var elTop = el.offsetTop - 20;
+    var bottom = window.innerHeight + document.body.scrollTop;
+    var elBottom = el.offsetHeight + el.offsetTop + 60
+
+    if(elBottom > bottom || elTop < top) {
+        document.body.scrollTop = el.offsetTop - 20
+    }
+    
+    /* Set scroll left in case horz scroll is activated.
+    
+        The following works because el.offsetTop accounts for each page turned
+        as if the document was scrolling vertical. We then divide by the window
+        height to figure out what page the element should appear on and set scroll left
+        to scroll to that page.
+    */
+    if( document.body.scrollTop == 0 ){
+        var elLeft = window.innerWidth * Math.floor(el.offsetTop / window.innerHeight);
+        document.body.scrollLeft = elLeft;
+    }
+
+    return el;
+}
+
+/**
+ Remove All Classes - removes the given class from all elements in the DOM
+ */
+function removeAllClasses(className) {
+    var els = document.body.getElementsByClassName(className)
+    if( els.length > 0 )
+    for( i = 0; i <= els.length; i++) {
+        els[i].classList.remove(className);
+    }
+}
+
+/**
+ Audio Mark ID - marks an element with an ID with the given class and scrolls to it
+ */
+function audioMarkID(className, id) {
+    if (audioMarkClass)
+        removeAllClasses(audioMarkClass);
+
+    audioMarkClass = className
+    var el = document.getElementById(id);
+
+    goToEl(el);
+    el.classList.add(className)
+}
+
+function setMediaOverlayStyle(style){
+    document.documentElement.classList.remove("mediaOverlayStyle0", "mediaOverlayStyle1", "mediaOverlayStyle2")
+    document.documentElement.classList.add(style)
+}
+
+function setMediaOverlayStyleColors(color, colorHighlight) {
+    var stylesheet = document.styleSheets[document.styleSheets.length-1];
+    stylesheet.insertRule(".mediaOverlayStyle0 span.epub-media-overlay-playing { background: "+colorHighlight+" !important }")
+    stylesheet.insertRule(".mediaOverlayStyle1 span.epub-media-overlay-playing { border-color: "+color+" !important }")
+    stylesheet.insertRule(".mediaOverlayStyle2 span.epub-media-overlay-playing { color: "+color+" !important }")
+}
+
+var currentIndex = -1;
+
+
+function findSentenceWithIDInView(els) {
+    // @NOTE: is `span` too limiting?
+    for(indx in els) {
+        var element = els[indx];
+
+        // Horizontal scroll
+        if (document.body.scrollTop == 0) {
+            var elLeft = window.innerWidth * Math.floor(element.offsetTop / window.innerHeight);
+            // document.body.scrollLeft = elLeft;
+
+            if (elLeft == document.body.scrollLeft) {
+                currentIndex = indx;
+                return element;
+            }
+
+        // Vertical
+        } else if(element.offsetTop > document.body.scrollTop) {
+            currentIndex = indx;
+            return element;
+        }
+    }
+    
+    return null
+}
+
+function findNextSentenceInArray(els) {
+    if(currentIndex >= 0) {
+        currentIndex ++;
+        return els[currentIndex];
+    }
+    
+    return null
+}
+
+function resetCurrentSentenceIndex() {
+    currentIndex = -1;
+}
+
+function getSentenceWithIndex(className) {
+    var sentence;
+    var sel = getSelection();
+    var node = null;
+    var elements = document.querySelectorAll("span.sentence");
+
+    // Check for a selected text, if found start reading from it
+    if (sel.toString() != "") {
+        console.log(sel.anchorNode.parentNode);
+        node = sel.anchorNode.parentNode;
+
+        if (node.className == "sentence") {
+            sentence = node
+
+            for(var i = 0, len = elements.length; i < len; i++) {
+                if (elements[i] === sentence) {
+                    currentIndex = i;
+                    break;
+                }
+            }
+        } else {
+            sentence = findSentenceWithIDInView(elements);
+        }
+    } else if (currentIndex < 0) {
+        sentence = findSentenceWithIDInView(elements);
+    } else {
+        sentence = findNextSentenceInArray(elements);
+    }
+
+    var text = sentence.innerText || sentence.textContent;
+    
+    goToEl(sentence);
+    
+    if (audioMarkClass){
+        removeAllClasses(audioMarkClass);
+    }
+    
+    audioMarkClass = className;
+    sentence.classList.add(className)
+    return text;
+}
+
+function wrappingSentencesWithinPTags(){
+    currentIndex = -1;
+    "use strict";
+    
+    var rxOpen = new RegExp("<[^\\/].+?>"),
+    rxClose = new RegExp("<\\/.+?>"),
+    rxSupStart = new RegExp("^<sup\\b[^>]*>"),
+    rxSupEnd = new RegExp("<\/sup>"),
+    sentenceEnd = [],
+    rxIndex;
+    
+    sentenceEnd.push(new RegExp("[^\\d][\\.!\\?]+"));
+    sentenceEnd.push(new RegExp("(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*?$)"));
+    sentenceEnd.push(new RegExp("(?![^\\(]*?\\))"));
+    sentenceEnd.push(new RegExp("(?![^\\[]*?\\])"));
+    sentenceEnd.push(new RegExp("(?![^\\{]*?\\})"));
+    sentenceEnd.push(new RegExp("(?![^\\|]*?\\|)"));
+    sentenceEnd.push(new RegExp("(?![^\\\\]*?\\\\)"));
+    //sentenceEnd.push(new RegExp("(?![^\\/.]*\\/)")); // all could be a problem, but this one is problematic
+    
+    rxIndex = new RegExp(sentenceEnd.reduce(function (previousValue, currentValue) {
+                                            return previousValue + currentValue.source;
+                                            }, ""));
+    
+    function indexSentenceEnd(html) {
+        var index = html.search(rxIndex);
+        
+        if (index !== -1) {
+            index += html.match(rxIndex)[0].length - 1;
+        }
+        
+        return index;
+    }
+
+    function pushSpan(array, className, string, classNameOpt) {
+        if (!string.match('[a-zA-Z0-9]+')) {
+            array.push(string);
+        } else {
+            array.push('<span class="' + className + '">' + string + '</span>');
+        }
+    }
+    
+    function addSupToPrevious(html, array) {
+        var sup = html.search(rxSupStart),
+        end = 0,
+        last;
+        
+        if (sup !== -1) {
+            end = html.search(rxSupEnd);
+            if (end !== -1) {
+                last = array.pop();
+                end = end + 6;
+                array.push(last.slice(0, -7) + html.slice(0, end) + last.slice(-7));
+            }
+        }
+        
+        return html.slice(end);
+    }
+    
+    function paragraphIsSentence(html, array) {
+        var index = indexSentenceEnd(html);
+        
+        if (index === -1 || index === html.length) {
+            pushSpan(array, "sentence", html, "paragraphIsSentence");
+            html = "";
+        }
+        
+        return html;
+    }
+    
+    function paragraphNoMarkup(html, array) {
+        var open = html.search(rxOpen),
+        index = 0;
+        
+        if (open === -1) {
+            index = indexSentenceEnd(html);
+            if (index === -1) {
+                index = html.length;
+            }
+            
+            pushSpan(array, "sentence", html.slice(0, index += 1), "paragraphNoMarkup");
+        }
+        
+        return html.slice(index);
+    }
+    
+    function sentenceUncontained(html, array) {
+        var open = html.search(rxOpen),
+        index = 0,
+        close;
+        
+        if (open !== -1) {
+            index = indexSentenceEnd(html);
+            if (index === -1) {
+                index = html.length;
+            }
+            
+            close = html.search(rxClose);
+            if (index < open || index > close) {
+                pushSpan(array, "sentence", html.slice(0, index += 1), "sentenceUncontained");
+            } else {
+                index = 0;
+            }
+        }
+        
+        return html.slice(index);
+    }
+    
+    function sentenceContained(html, array) {
+        var open = html.search(rxOpen),
+        index = 0,
+        close,
+        count;
+        
+        if (open !== -1) {
+            index = indexSentenceEnd(html);
+            if (index === -1) {
+                index = html.length;
+            }
+            
+            close = html.search(rxClose);
+            if (index > open && index < close) {
+                count = html.match(rxClose)[0].length;
+                pushSpan(array, "sentence", html.slice(0, close + count), "sentenceContained");
+                index = close + count;
+            } else {
+                index = 0;
+            }
+        }
+        
+        return html.slice(index);
+    }
+    
+    function anythingElse(html, array) {
+        pushSpan(array, "sentence", html, "anythingElse");
+        
+        return "";
+    }
+    
+    function guessSenetences() {
+        var paragraphs = document.getElementsByTagName("p");
+
+        Array.prototype.forEach.call(paragraphs, function (paragraph) {
+            var html = paragraph.innerHTML,
+                length = html.length,
+                array = [],
+                safety = 100;
+
+            while (length && safety) {
+                html = addSupToPrevious(html, array);
+                if (html.length === length) {
+                    if (html.length === length) {
+                        html = paragraphIsSentence(html, array);
+                        if (html.length === length) {
+                            html = paragraphNoMarkup(html, array);
+                            if (html.length === length) {
+                                html = sentenceUncontained(html, array);
+                                if (html.length === length) {
+                                    html = sentenceContained(html, array);
+                                    if (html.length === length) {
+                                        html = anythingElse(html, array);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                length = html.length;
+                safety -= 1;
+            }
+
+            paragraph.innerHTML = array.join("");
+        });
+    }
+    
+    guessSenetences();
+}
+
+// Class based onClick listener
+
+function addClassBasedOnClickListener(schemeName, querySelector, attributeName, selectAll) {
+       if (selectAll) {
+               // Get all elements with the given query selector
+               var elements = document.querySelectorAll(querySelector);
+               for (elementIndex = 0; elementIndex < elements.length; elementIndex++) {
+                       var element = elements[elementIndex];
+                       addClassBasedOnClickListenerToElement(element, schemeName, attributeName);
+               }
+       } else {
+               // Get the first element with the given query selector
+               var element = document.querySelector(querySelector);
+               addClassBasedOnClickListenerToElement(element, schemeName, attributeName);
+       }
+}
+
+function addClassBasedOnClickListenerToElement(element, schemeName, attributeName) {
+       // Get the content from the given attribute name
+       var attributeContent = element.getAttribute(attributeName);
+       // Add the on click logic
+       element.setAttribute("onclick", "onClassBasedListenerClick(\"" + schemeName + "\", \"" + encodeURIComponent(attributeContent) + "\");");
+}
+
+var onClassBasedListenerClick = function(schemeName, attributeContent) {
+       // Prevent the browser from performing the default on click behavior
+       event.preventDefault();
+       // Don't pass the click event to other elemtents
+       event.stopPropagation();
+       // Create parameters containing the click position inside the web view.
+       var positionParameterString = "/clientX=" + event.clientX + "&clientY=" + event.clientY;
+       // Set the custom link URL to the event
+       window.location = schemeName + "://" + attributeContent + positionParameterString;
+}