1 2/********* 3* ../app/js/core/namespace.js 4**********/ 5// replacement IE console 6if (typeof console == 'undefined') { window.console = {log:function() {}}; } 7 8// everything goes here 9window.sofia = {}; 10 11// version 12sofia.version = '3.8.5'; 13 14// allows items to register themselves 15sofia.plugins = []; 16sofia.windowTypes = []; 17sofia.menuComponents = []; 18sofia.initMethods = []; 19 20sofia.globals = {}; 21 22// language 23sofia.resources = {}; 24 25 26if (window.location.protocol === 'file:') { 27 sofia.protocol = 'https:'; 28} else { 29 sofia.protocol = ''; 30} 31 32 33 34/********* 35* ../app/js/core/config-default.js 36**********/ 37sofia.config = { 38 39 // Change this to clear all user settings 40 settingsPrefix: '20140307', 41 42 // Enables the use of online sources (Google Maps, FCBH, Jesus Film, etc.) 43 enableOnlineSources: true, 44 45 // first load 46 windows: [ 47 {type: 'bible', data: {textid: 'ENGNAS', fragmentid: 'JN1_1'}}, 48 {type: 'bible', data: {textid: 'grc_tisch', fragmentid: 'JN1_1'}}, 49 {type: 'search', data: {textid: 'ENGNAS', searchtext:'truth love'}} 50 ], 51 52 // URL to content 53 // (1) Leave blank to use local content folder. 54 // (2) Enter URL (http://www.biblesite.com/) for CORS enabled sites 55 // (3) Enter URL (http://www.biblesite.com/) and update 56 baseContentUrl: '', 57 58 // (1) Leave blank for local files or for CORS enabled CDN 59 // (2) Enter path of script that will convert all files to JSONP (e.g., api.php) 60 baseContentApiPath: '', 61 62 // if your are creating a CDN for your content (see above) and want to send a key 63 baseContentApiKey: '', 64 65 // file name of texts lists 66 textsIndexPath: 'texts.json', 67 68 // URL to about page 69 aboutPagePath: 'about.html', 70 71 // (1) Leave blank for JSON search
72 // (2) Enter path of script that will return JSON data 73 serverSearchPath: '', 74 75 // texts shown before the "MORE" button ("eng-NASB1995", "eng-kjv", "eng_net") 76 topTexts: [], 77 78 // new window 79 newBibleWindowVersion: 'eng-NASB1995', 80 81 // new bible verse 82 newWindowFragmentid: 'JN1_1', 83 84 // new commentary window 85 newCommentaryWindowTextId: 'comm_eng_wesley', 86 87 // language for top 88 pinnedLanguage: 'English', 89 90 // language(s) for top 91 pinnedLanguages: ['English', 'Spanish'], 92 93 // Override the browser and user's choice for UI language 94 defaultLanguage: '', 95 96 // URL to custom CSS 97 customCssUrl: '', 98 99 // Faith Comes by Hearing 100 fcbhKey: '', 101 102 // any texts you want to ignore from FCBH 103 fcbhTextExclusions: [''], 104 105 // true: live parse all versions 106 // false: loads texts_fcbh.json 107 fcbhLoadVersions: false, 108 109 // jesus film media 110 jfmKey: '' 111}; 112 113sofia.customConfigs = { 114 "dbs": { 115 customCssUrl: 'dbs.css' 116 } 117}; 118 119 120 121/********* 122* ../app/js/core/windowmanager.js 123**********/ 124 125var WindowManager = function(node, app) { 126 127 var windows = []; 128 129 function addWindow(className, data) { 130 131 var id = 'win' + (new Date()).getTime().toString(); 132 133 if (className == 'TextWindow') { 134 className = 'BibleWindow'; 135 } 136 137 // test for classname 138 if (typeof window[className] == 'undefined') 139 return; 140 141 // create window and add to array 142 var win = new Window(id, node, className, data, ext); 143 windows.push(win); 144 145 var tabWidth = win.tab.outerWidth(); 146 147 //win.tab.css({right: '0px'}); 148 149 150 //size(); 151 setTimeout(function() { 152 app.resize(); 153 },10); 154 } 155 function removeWindow(id) { 156 157 // find window 158 var windowsToClose = windows.filter(function(win) { 159 return win.id == id; 160 }), 161 windowToClose = (windowsToClose.length == 1) ? windowsToClose[0] : null; 162 163 if (windowToClose == null) { 164 console.log('ERROR', "Can't find window", id); 165 return; 166 } 167 168 // remove from array 169 windows = windows.filter(function(win) { 170 return win.id != id; 171 }); 172 173 PlaceKeeper.storePlace(); 174 175 // remove from DOM, run delete functions 176 windowToClose.close(); 177 windowToClose = null; 178 179 // resize and reset 180 //size(); 181 PlaceKeeper.restorePlace(); 182 183 /* 184 var tabWidth = windows[0].tab.outerWidth() - 10; 185 for (var i=0,il=windows.length; i<il; i++) { 186 windows[i].tab.css({right: ((il-i) * tabWidth) +'px'}); 187 } 188 */ 189 190 windows[0].tab.addClass('active'); 191 windows[0].node.addClass('active'); 192 193 setTimeout(function() { 194 app.resize(); 195 },10); 196 197 198 // trigger save 199 ext.trigger('settingschange', {type: 'settingschange', target: this, data: null}); 200 } 201 202 function size(width, height) { 203 // set container size 204 if (width && height) { 205 node.width(width) 206 .height(height); 207 } else { 208 width = node.width(); 209 height = node.height(); 210 } 211 212 //console.log('manage resize', windows.length); 213 214 var sizeThreshold = 560; 215 216 if (width < sizeThreshold) { 217 $('body').addClass('compact-ui'); 218 } else { 219 $('body').removeClass('compact-ui'); 220 } 221 222 223 if (windows.length > 0) { 224 225 if (width < sizeThreshold) { 226 227 //$('body').addClass('compact-ui'); 228 229 230 var tabWidth = windows[0].tab.outerWidth()-10; 231 232 // resize all windows to the same 233 for (var i=0, il=windows.length; i<il; i++) { 234 windows[i].size(width, height); 235 windows[i].tab.css({right: ((il-i-1) * tabWidth) +'px'}); 236 } 237 238 } else { 239 240 //$('body').removeClass('compact-ui'); 241 242 // all windows to a percent of the width 243 244 var windowWidth = Math.floor(width/windows.length), 245 firstMarginLeft = parseInt(windows[0].node.css('margin-left'), 10), 246 firstMarginRight =parseInt(windows[0].node.css('margin-right'), 10); 247 248 249 if (!isNaN(firstMarginLeft)) { 250 windowWidth = windowWidth - firstMarginLeft; 251 } 252 if (!isNaN(firstMarginRight)) { 253 windowWidth = windowWidth - firstMarginRight; 254 } 255 256 for (var i=0, il=windows.length; i<il; i++) { 257 windows[i].size(windowWidth, height); 258 } 259 } 260 } 261 } 262 263 function getSettings() { 264 var settingsForAllWindows = []; 265 266 for (var i=0, il=windows.length; i<il; i++) { 267 settingsForAllWindows.push({ 268 'windowType': windows[i].className, 269 'data': windows[i].getData() 270 }); 271 } 272 273 return settingsForAllWindows; 274 } 275 276 var ext = { 277 add: addWindow, 278 remove: removeWindow, 279 size: size,
280 getSettings: getSettings, 281 getWindows: function() { return windows; } 282 }; 283 284 ext = $.extend(true, ext, EventEmitter); 285 286 return ext; 287}; 288 289var Window = function(id, parentNode, className, data, manager) { 290 291 var ext = {}, 292 node = $('<div class="window ' + className + ' active"></div>') 293 .appendTo(parentNode), 294 closeBtn = $('<div class="close-container"><span class="close-button"></span></div>') 295 .appendTo(node) 296 .find('.close-button') 297 .on('click', function() { 298 console.log(id, 'remove', manager); 299 300 301 manager.remove(id); 302 }), 303 tab = $('<div class="window-tab ' + className + ' active">' + 304 '<div class="window-tab-inner">' + 305 '<span class="window-tab-label ' + className + '-tab">' + className + '</span>' + 306 '</div>' + 307 '</div>') 308 .appendTo( $('body') ); 309 310 // make sure this one is selected 311 node.siblings('.window').removeClass('active'); 312 tab.siblings('.window-tab').removeClass('active'); 313 314 ext.id = id; 315 ext.className = className; 316 ext.node = node; 317 ext.tab = tab; 318 319 var controller = new window[className](id, ext, data); 320 ext.controller = controller; 321 322 // send settings up to the manager, up to the app 323 controller.on('settingschange', function(e) { 324 ext.trigger('settingschange', e); // {type: e.type, target: this, data: e.data}); 325 }); 326 controller.on('globalmessage', function(e) { 327 e.id = id; 328 ext.trigger('globalmessage', e); // {type: e.type, target: this, data: e.data}); 329 }); 330 331 332 // send focus/blur events down to controller 333 node.on('mouseenter touchstart', function(e) { 334 controller.trigger('focus', {}); 335 node 336 .addClass('focused') 337 .siblings() 338 .removeClass('focused') 339 .trigger('windowblur'); 340 }); 341 node.on('mouseleave windowblur', function(e) { 342 node 343 .removeClass('focused') 344 345 controller.trigger('blur', {}); 346 }); 347 348 tab.on('click', function(e) { 349 // dectivate all other tabs and windows 350 $('.window, .window-tab').removeClass('active'); 351 352 tab.addClass('active'); 353 node.addClass('active'); 354 }); 355 356 357 function size(width, height) { 358 node.outerWidth(width) 359 .outerHeight(height); 360 361 controller.size(width, height); 362 } 363 364 function quit() { 365 controller.quit(); 366 } 367 368 function close() { 369 370 if (typeof controller.close != 'undefined') { 371 controller.close(); 372 } 373 controller = null; 374 375 ext.clearListeners(); 376 377 tab.remove(); 378 node.remove(); 379 } 380 381 382 ext.size = size; 383 ext.quit = quit; 384 ext.getData = function() { 385 return controller.getData(); 386 }; 387 ext.close = close; 388 389 ext = $.extend(true, ext, EventEmitter); 390 391 // receive from App, send down to controller 392 ext.on('message', function(e) { 393 controller.trigger('message', e); 394 395 if (e.data.labelTab) { 396 tab.find('span').html( e.data.labelTab ); 397 } 398 }); 399 400 401 // when a window reports a settings change 402 ext.on('settingschange', function(e) { 403 404 // pass up to root 405 manager.trigger('settingschange', e); 406 }); 407 408 ext.on('globalmessage', sofia.app.handleGlobalMessage); 409 410 return ext; 411}; 412 413 414 415/********* 416* ../app/js/core/windowapp.js 417**********/ 418 419var App = function() { 420 // create nodes 421 var win = $(window), 422 body = $(document.body),
423 container = $('<div class="windows-container"></div>').appendTo(body), 424 header = $('<div class="windows-header"></div>').appendTo(container), 425 main = $('<div class="windows-main"></div>').appendTo(container), 426 footer = $('<div class="windows-footer"></div>').appendTo(container), 427 settingsKey = 'app-windows', 428 ext = {}, 429 windowManager = null, 430 mainMenu = null; 431 432 function init() { 433 // create objects 434 mainMenu = new MainMenu(header); 435 windowManager = new WindowManager(main, ext); 436 437 ext.windowManager = windowManager; 438 439 // combine nodes and objects 440 win.on('resize', resize); 441 win.on('orientationchange', resize); 442 resize(); 443 444 445 // if not fullscreen and is touch screen 446 //if (Detection.hasTouch) { 447 try { 448 window.top.scrollTo(0, 1); 449 } catch (ex) { 450 451 } 452 //} 453 454 455 var settings = getWindowSettings(), 456 windowWidth = win.width(); 457 //console.log('settings',settings, settings.length); 458 459 /* 460 if (windowWidth < 768 && settings.windows.length >= 3) { 461 settings.windows = settings.windows.slice(0,2); 462 } 463 if (windowWidth < 460 && settings.windows.length >= 2) { 464 settings.windows = settings.windows.slice(0,1); 465 } 466 */ 467 468 // create windows 469 for (var i=0, il=settings.windows.length; i<il; i++) { 470 var setting = settings.windows[i], 471 windowClassName = setting.windowType; 472 473 // try to find ClassName by the parameter 474 if (!windowClassName) { 475 var param = setting.type, 476 filteredWindowTypes = sofia.windowTypes.filter(function(wt) { 477 return wt.param == param; 478 }); 479 480 if (filteredWindowTypes.length == 1) { 481 windowClassName = filteredWindowTypes[0].className; 482 } 483 } 484 485 //console.log('create window', setting, windowClassName); 486 windowManager.add(windowClassName, setting.data); 487 } 488 // make sure first is active 489 $('.window, .window-tab').removeClass('active'); 490 $('.window:first, .window-tab:first').addClass('active'); 491 492 // get first window 493 var bibleWindows = settings.windows.filter(function(s) { return s.windowType == 'BibleWindow'; }), 494 firstBibleWindow = bibleWindows.length > 0 ? bibleWindows[0] : null, 495 firstFragmentid = firstBibleWindow != null && firstBibleWindow.data != null ? firstBibleWindow.data.fragmentid : null; 496 497 if (firstFragmentid != null) { 498 TextNavigation.firstState( firstFragmentid ); 499 } 500 501 var settingsTimer = new Timer(storeSettings, 1000); 502 503 windowManager.on('settingschange', function(e) { 504 505 // title to show active window's position 506 if (e.data && e.data.label && e.data.hasFocus) { 507 document.title = e.data.labelLong; 508 } 509 510 settingsTimer.start(); 511 512 }); 513 514 var plugins = [] 515 ext.plugins = plugins; 516 // run plugins 517 for (var x in sofia.plugins) { 518 plugin = new window[ sofia.plugins[x] ](this); 519 plugins.push(plugin); 520 521 if (plugin.on) { 522 523 plugin.on('globalmessage', handleGlobalMessage); 524 } 525 } 526 } 527 528 function resize() { 529 //console.log('app resize'); 530 PlaceKeeper.storePlace(); 531 532 if (windowManager && windowManager.getWindows().length == 1) { 533 body.addClass('one-window') 534 } else { 535 body.removeClass('one-window') 536 } 537 538 // get window size 539 var width = win.width(), 540 height = win.height(), 541 542 // calculate size 543 areaHeight = height - header.outerHeight() + footer.outerHeight(), 544 areaWidth = width - parseInt(main.css('margin-left'), 10) - parseInt(main.css('margin-right'), 10); 545 546 /* 547 if (width < 460) { 548 //header.hide(); 549 areaHeight = height; 550 } else { 551 header.show(); 552 } 553 */ 554 555 // set height 556 main.height(areaHeight); 557 main.width(areaWidth); 558 559 // pass new size down to area 560 ext.windowManager.size(areaWidth, areaHeight); 561 562 PlaceKeeper.restorePlace(); 563 } 564 ext.resize = resize; 565 566 function getWindowSettings() { 567 568 // (1) get default settings 569 var settings = { 570 windows: sofia.config.windows 571 }; 572 573 // (2) replace defaults with user settings 574 settings = AppSettings.getValue(settingsKey, settings); 575 576 // (3) overwrite with QueryString when present 577 var queryData = stringUtility.parseQuerystring(); 578 if (queryData["w1"]) { 579 580 var tempSettings = []; 581 582 for (var i=1; i<=4; i++) { 583 var winTypeName = queryData["w" + i.toString()], 584 winTypeInfo = sofia.windowTypes.filter(function(winType) { return winType.param == winTypeName; })[0], 585 winTypeParamKeys = typeof winTypeInfo != 'undefined' ? Object.keys(winTypeInfo.paramKeys) : [], 586 setting = { 587 type: winTypeName, 588 data: {} 589 }; 590 591 if (typeof winTypeName != 'undefined') { 592 // go though all querystring values, and anything that ends with '1' goes with this data 593 for (var q in queryData) { 594 var key = q.substring(0, q.length-1), 595 number = q.substring(q.length-1), 596 value = queryData[q]; 597 598 if (key != 'w' && number == i.toString()) { 599 600 // convert short querystring key to longer one if needed 601 var longParamKey = winTypeParamKeys.filter(function(lpk) { 602 console.log(key, lpk, winTypeInfo.paramKeys[lpk]); 603 return key == winTypeInfo.paramKeys[lpk] || key == lpk; 604 })[0]; 605 606 setting.data[longParamKey] = value; 607 } 608 } 609 610 tempSettings.push(setting); 611 } 612 } 613 614 // overwrite settings 615 if (tempSettings.length > 0) { 616 settings.windows = tempSettings; 617 } 618 619 } 620 return settings; 621 } 622 623 function storeSettings() { 624 // get settings from all windows
625 var windowSettings = ext.windowManager.getSettings(), 626 // later we'll need these 627 headerSettings = {}, 628 settings = { 629 windows: windowSettings 630 }; 631 632 //console.log('storeSettings', settings); 633 634 // store 635 AppSettings.setValue(settingsKey, settings ); 636 } 637 638 function handleGlobalMessage(e) { 639 // give to other windows 640 var windows = ext.windowManager.getWindows(); 641 642 for (var i=0, il=windows.length; i<il; i++) { 643 var w = windows[i]; 644 645 if (w.id != e.id) { 646 // pass message down 647 w.trigger('message', e); 648 } 649 } 650 651 // plugins 652 if (ext.plugins) { 653 for (var i=0, il=ext.plugins.length; i<il; i++) { 654 var p = sofia.app.plugins[i]; 655 656 if (p.trigger) { 657 // pass message down 658 p.trigger('message', e); 659 } 660 } 661 } 662 663 } 664 665 ext.init = init; 666 ext.handleGlobalMessage = handleGlobalMessage; 667 668 // internationalization: i18n 669 670 // if this is the first visit, we need a way to set the language 671 var lngSetting = '', 672 i18nCookieValue = AppSettings.getCookieValue('i18next'); 673 674 if (i18nCookieValue == '' || i18nCookieValue == null && sofia.config.defaultLanguage != '') { 675 lngSetting = sofia.config.defaultLanguage; 676 } 677 678 i18n.init({fallbackLng: 'en', lng: lngSetting, resStore: sofia.resources}); 679 680 setTimeout(function() { 681 682 var lang = i18n.lng(), 683 langSelector = $('#config-language'); 684 685 langSelector.val( lang ); 686 687 if (lang != langSelector.val() ) { 688 langSelector.val( lang.split('-')[0] ); 689 } 690 691 if (langSelector[0] && langSelector[0].localizeLanguages) { 692 langSelector[0].localizeLanguages(); 693 } 694 695 }, 50); 696 697 return ext; 698}; 699 700 701 702/********* 703* ../app/js/core/startup.js 704**********/ 705 706$(function() { 707 // hide initial text area 708 $('#startup').hide(); 709 710 // test for local file support 711 if (window.location.protocol === 'file:') { 712 713 $.ajax({ 714 dataType: 'text', 715 716 url: 'about.html', 717 success: function() { 718 init(); 719 }, 720 error: function(e) { 721 var modal = new MovableWindow( Math.min(500, $(window).width()) ,250, 'Local Files Error'), 722 errorMessage = '', 723 ua = navigator.userAgent.toLowerCase(); 724 //modal.size(500, 200).center(); 725 726 if (ua.indexOf('chrome') > -1) { 727 if (ua.indexOf('mac os') > -1) { 728 errorMessage = 729 '<p>Mac, Terminal</p>' + 730 '<code>/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --allow-file-access-from-files</code>'; 731 } else 732 if (ua.indexOf('windows') > -1) { 733 errorMessage = 734 '<p>Mac, Terminal</p>' + 735 '<code>/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --allow-file-access-from-files</code>'; 736 } 737 } else { 738 errorMessage = 739 '<p>Unknown error loading files (cannot load about.html)' + e + '</p>'; 740 } 741 742 modal.body.css({background: '#000', color: '#fff' }).html( 743 '<div style="padding: 20px;">' + 744 errorMessage + 745 '</div>' 746 ); 747 modal.show().center(); 748 } 749 }); 750 751 } else { 752 init(); 753 } 754 755 function init() { 756 // load config 757 var params = stringUtility.parseQuerystring(), 758 custom = params["custom"]; 759 760 if (typeof custom != 'undefined' && custom != '') { 761 var customizations = sofia.customConfigs[custom]; 762 763 if (typeof customizations != 'undefined' && customizations != null) { 764 sofia.config = $.extend(sofia.config, customizations); 765 } 766 } 767 768 // load css 769 if (typeof sofia.config.customCssUrl != 'undefined' && sofia.config.customCssUrl != '') { 770 $('<link href="' + sofia.config.customCssUrl + '" rel="stylesheet" />').appendTo( $('head') ); 771 } 772 773 var isiOSApp = (navigator.userAgent.toLowerCase().indexOf('ipad') > -1 || navigator.userAgent.toLowerCase().indexOf('iphone') > -1) && window.location.protocol === 'file:'; 774 775 if (window.navigator.standalone === true || isiOSApp) { 776 $('body').addClass('app-mobile-fullscreen'); 777 } 778 779 // run inits 780 for (var i=0, il=sofia.initMethods.length; i<il; i++) { 781 sofia.initMethods[i](); 782 } 783 784 // create app 785 sofia.app = new App(); 786 sofia.app.init(); 787 788 $('.i18n').i18n(); 789 } 790}); 791 792 793 794/********* 795* ../app/js/lib/xregexp.js 796**********/ 797/*! 798 * XRegExp v2.0.0 799 * (c) 2007-2012 Steven Levithan <http://xregexp.com/> 800 * MIT License 801 */ 802 803/** 804 * XRegExp provides augmented, extensible JavaScript regular expressions. You get new syntax, 805 * flags, and methods beyond what browsers support natively. XRegExp is also a regex utility belt 806 * with tools to make your client-side grepping simpler and more powerful, while freeing you from 807 * worrying about pesky cross-browser inconsistencies and the dubious `lastIndex` property. See 808 * XRegExp's documentation (http://xregexp.com/) for more details. 809 * @module xregexp 810 * @requires N/A 811 */ 812var XRegExp; 813 814// Avoid running twice; that would reset tokens and could break references to native globals 815XRegExp = XRegExp || (function (undef) { 816 "use strict"; 817 818/*-------------------------------------- 819 * Private variables 820 *------------------------------------*/ 821 822 var self, 823 addToken, 824 add, 825 826// Optional features; can be installed and uninstalled 827 features = { 828 natives: false, 829 extensibility: false 830 }, 831 832// Store native methods to use and restore ("native" is an ES3 reserved keyword) 833 nativ = { 834 exec: RegExp.prototype.exec, 835 test: RegExp.prototype.test, 836 match: String.prototype.match, 837 replace: String.prototype.replace, 838 split: String.prototype.split 839 }, 840 841// Storage for fixed/extended native methods 842 fixed = {}, 843 844// Storage for cached regexes 845 cache = {}, 846 847// Storage for addon tokens 848 tokens = [], 849 850// Token scopes 851 defaultScope = "default", 852 classScope = "class", 853 854// Regexes that match native regex syntax 855 nativeTokens = { 856 // Any native multicharacter token in default scope (includes octals, excludes character classes) 857 "default": /^(?:\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??)/, 858 // Any native multicharacter token in character class scope (includes octals) 859 "class": /^(?:\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S]))/ 860 }, 861 862// Any backreference in replacement strings 863 replacementToken = /\$(?:{([\w$]+)}|(\d\d?|[\s\S]))/g, 864 865// Any character with a later instance in the string 866 duplicateFlags = /([\s\S])(?=[\s\S]*\1)/g, 867 868// Any greedy/lazy quantifier 869 quantifier = /^(?:[?*+]|{\d+(?:,\d*)?})\??/, 870
871// Check for correct `exec` handling of nonparticipating capturing groups 872 compliantExecNpcg = nativ.exec.call(/()??/, "")[1] === undef, 873 874// Check for flag y support (Firefox 3+) 875 hasNativeY = RegExp.prototype.sticky !== undef, 876 877// Used to kill infinite recursion during XRegExp construction 878 isInsideConstructor = false, 879 880// Storage for known flags, including addon flags 881 registeredFlags = "gim" + (hasNativeY ? "y" : ""); 882 883/*-------------------------------------- 884 * Private helper functions 885 *------------------------------------*/ 886 887/** 888 * Attaches XRegExp.prototype properties and named capture supporting data to a regex object. 889 * @private 890 * @param {RegExp} regex Regex to augment. 891 * @param {Array} captureNames Array with capture names, or null. 892 * @param {Boolean} [isNative] Whether the regex was created by `RegExp` rather than `XRegExp`. 893 * @returns {RegExp} Augmented regex. 894 */ 895 function augment(regex, captureNames, isNative) { 896 var p; 897 // Can't auto-inherit these since the XRegExp constructor returns a nonprimitive value 898 for (p in self.prototype) { 899 if (self.prototype.hasOwnProperty(p)) { 900 regex[p] = self.prototype[p]; 901 } 902 } 903 regex.xregexp = {captureNames: captureNames, isNative: !!isNative}; 904 return regex; 905 } 906 907/** 908 * Returns native `RegExp` flags used by a regex object. 909 * @private 910 * @param {RegExp} regex Regex to check. 911 * @returns {String} Native flags in use. 912 */ 913 function getNativeFlags(regex) { 914 //return nativ.exec.call(/\/([a-z]*)$/i, String(regex))[1]; 915 return (regex.global ? "g" : "") + 916 (regex.ignoreCase ? "i" : "") + 917 (regex.multiline ? "m" : "") + 918 (regex.extended ? "x" : "") + // Proposed for ES6, included in AS3 919 (regex.sticky ? "y" : ""); // Proposed for ES6, included in Firefox 3+ 920 } 921 922/** 923 * Copies a regex object while preserving special properties for named capture and augmenting with 924 * `XRegExp.prototype` methods. The copy has a fresh `lastIndex` property (set to zero). Allows 925 * adding and removing flags while copying the regex. 926 * @private 927 * @param {RegExp} regex Regex to copy. 928 * @param {String} [addFlags] Flags to be added while copying the regex. 929 * @param {String} [removeFlags] Flags to be removed while copying the regex. 930 * @returns {RegExp} Copy of the provided regex, possibly with modified flags. 931 */ 932 function copy(regex, addFlags, removeFlags) { 933 if (!self.isRegExp(regex)) { 934 throw new TypeError("type RegExp expected"); 935 } 936 var flags = nativ.replace.call(getNativeFlags(regex) + (addFlags || ""), duplicateFlags, ""); 937 if (removeFlags) { 938 // Would need to escape `removeFlags` if this was public 939 flags = nativ.replace.call(flags, new RegExp("[" + removeFlags + "]+", "g"), ""); 940 } 941 if (regex.xregexp && !regex.xregexp.isNative) { 942 // Compiling the current (rather than precompilation) source preserves the effects of nonnative source flags 943 regex = augment(self(regex.source, flags), 944 regex.xregexp.captureNames ? regex.xregexp.captureNames.slice(0) : null); 945 } else { 946 // Augment with `XRegExp.prototype` methods, but use native `RegExp` (avoid searching for special tokens) 947 regex = augment(new RegExp(regex.source, flags), null, true); 948 } 949 return regex; 950 } 951 952/* 953 * Returns the last index at which a given value can be found in an array, or `-1` if it's not 954 * present. The array is searched backwards. 955 * @private 956 * @param {Array} array Array to search. 957 * @param {*} value Value to locate in the array. 958 * @returns {Number} Last zero-based index at which the item is found, or -1. 959 */ 960 function lastIndexOf(array, value) { 961 var i = array.length; 962 if (Array.prototype.lastIndexOf) { 963 return array.lastIndexOf(value); // Use the native method if available 964 } 965 while (i--) { 966 if (array[i] === value) { 967 return i; 968 } 969 } 970 return -1; 971 } 972 973/** 974 * Determines whether an object is of the specified type. 975 * @private 976 * @param {*} value Object to check. 977 * @param {String} type Type to check for, in lowercase. 978 * @returns {Boolean} Whether the object matches the type. 979 */ 980 function isType(value, type) { 981 return Object.prototype.toString.call(value).toLowerCase() === "[object " + type + "]"; 982 } 983 984/** 985 * Prepares an options object from the given value. 986 * @private 987 * @param {String|Object} value Value to convert to an options object. 988 * @returns {Object} Options object. 989 */ 990 function prepareOptions(value) { 991 value = value || {}; 992 if (value === "all" || value.all) { 993 value = {natives: true, extensibility: true}; 994 } else if (isType(value, "string")) { 995 value = self.forEach(value, /[^\s,]+/, function (m) { 996 this[m] = true; 997 }, {}); 998 } 999 return value; 1000 } 1001 1002/** 1003 * Runs built-in/custom tokens in reverse insertion order, until a match is found. 1004 * @private 1005 * @param {String} pattern Original pattern from which an XRegExp object is being built.
1006 * @param {Number} pos Position to search for tokens within `pattern`. 1007 * @param {Number} scope Current regex scope. 1008 * @param {Object} context Context object assigned to token handler functions. 1009 * @returns {Object} Object with properties `output` (the substitution string returned by the 1010 * successful token handler) and `match` (the token's match array), or null. 1011 */ 1012 function runTokens(pattern, pos, scope, context) { 1013 var i = tokens.length, 1014 result = null, 1015 match, 1016 t; 1017 // Protect against constructing XRegExps within token handler and trigger functions 1018 isInsideConstructor = true; 1019 // Must reset `isInsideConstructor`, even if a `trigger` or `handler` throws 1020 try { 1021 while (i--) { // Run in reverse order 1022 t = tokens[i]; 1023 if ((t.scope === "all" || t.scope === scope) && (!t.trigger || t.trigger.call(context))) { 1024 t.pattern.lastIndex = pos; 1025 match = fixed.exec.call(t.pattern, pattern); // Fixed `exec` here allows use of named backreferences, etc. 1026 if (match && match.index === pos) { 1027 result = { 1028 output: t.handler.call(context, match, scope), 1029 match: match 1030 }; 1031 break; 1032 } 1033 } 1034 } 1035 } catch (err) { 1036 throw err; 1037 } finally { 1038 isInsideConstructor = false; 1039 } 1040 return result; 1041 } 1042 1043/** 1044 * Enables or disables XRegExp syntax and flag extensibility. 1045 * @private 1046 * @param {Boolean} on `true` to enable; `false` to disable. 1047 */ 1048 function setExtensibility(on) { 1049 self.addToken = addToken[on ? "on" : "off"]; 1050 features.extensibility = on; 1051 } 1052 1053/** 1054 * Enables or disables native method overrides. 1055 * @private 1056 * @param {Boolean} on `true` to enable; `false` to disable. 1057 */ 1058 function setNatives(on) { 1059 RegExp.prototype.exec = (on ? fixed : nativ).exec; 1060 RegExp.prototype.test = (on ? fixed : nativ).test; 1061 String.prototype.match = (on ? fixed : nativ).match; 1062 String.prototype.replace = (on ? fixed : nativ).replace; 1063 String.prototype.split = (on ? fixed : nativ).split; 1064 features.natives = on; 1065 } 1066 1067/*-------------------------------------- 1068 * Constructor 1069 *------------------------------------*/ 1070 1071/** 1072 * Creates an extended regular expression object for matching text with a pattern. Differs from a 1073 * native regular expression in that additional syntax and flags are supported. The returned object 1074 * is in fact a native `RegExp` and works with all native methods. 1075 * @class XRegExp 1076 * @constructor 1077 * @param {String|RegExp} pattern Regex pattern string, or an existing `RegExp` object to copy. 1078 * @param {String} [flags] Any combination of flags: 1079 * <li>`g` - global 1080 * <li>`i` - ignore case 1081 * <li>`m` - multiline anchors 1082 * <li>`n` - explicit capture 1083 * <li>`s` - dot matches all (aka singleline) 1084 * <li>`x` - free-spacing and line comments (aka extended) 1085 * <li>`y` - sticky (Firefox 3+ only) 1086 * Flags cannot be provided when constructing one `RegExp` from another. 1087 * @returns {RegExp} Extended regular expression object. 1088 * @example 1089 * 1090 * // With named capture and flag x 1091 * date = XRegExp('(?<year> [0-9]{4}) -? # year \n\ 1092 * (?<month> [0-9]{2}) -? # month \n\ 1093 * (?<day> [0-9]{2}) # day ', 'x'); 1094 * 1095 * // Passing a regex object to copy it. The copy maintains special properties for named capture, 1096 * // is augmented with `XRegExp.prototype` methods, and has a fresh `lastIndex` property (set to 1097 * // zero). Native regexes are not recompiled using XRegExp syntax. 1098 * XRegExp(/regex/); 1099 */ 1100 self = function (pattern, flags) { 1101 if (self.isRegExp(pattern)) { 1102 if (flags !== undef) { 1103 throw new TypeError("can't supply flags when constructing one RegExp from another"); 1104 } 1105 return copy(pattern); 1106 } 1107 // Tokens become part of the regex construction process, so protect against infinite recursion 1108 // when an XRegExp is constructed within a token handler function 1109 if (isInsideConstructor) { 1110 throw new Error("can't call the XRegExp constructor within token definition functions"); 1111 } 1112 1113 var output = [], 1114 scope = defaultScope, 1115 tokenContext = { 1116 hasNamedCapture: false, 1117 captureNames: [], 1118 hasFlag: function (flag) { 1119 return flags.indexOf(flag) > -1; 1120 } 1121 }, 1122 pos = 0, 1123 tokenResult, 1124 match, 1125 chr; 1126 pattern = pattern === undef ? "" : String(pattern); 1127 flags = flags === undef ? "" : String(flags); 1128 1129 if (nativ.match.call(flags, duplicateFlags)) { // Don't use test/exec because they would update lastIndex 1130 throw new SyntaxError("invalid duplicate regular expression flag"); 1131 }
1132 // Strip/apply leading mode modifier with any combination of flags except g or y: (?imnsx) 1133 pattern = nativ.replace.call(pattern, /^\(\?([\w$]+)\)/, function ($0, $1) { 1134 if (nativ.test.call(/[gy]/, $1)) { 1135 throw new SyntaxError("can't use flag g or y in mode modifier"); 1136 } 1137 flags = nativ.replace.call(flags + $1, duplicateFlags, ""); 1138 return ""; 1139 }); 1140 self.forEach(flags, /[\s\S]/, function (m) { 1141 if (registeredFlags.indexOf(m[0]) < 0) { 1142 throw new SyntaxError("invalid regular expression flag " + m[0]); 1143 } 1144 }); 1145 1146 while (pos < pattern.length) { 1147 // Check for custom tokens at the current position 1148 tokenResult = runTokens(pattern, pos, scope, tokenContext); 1149 if (tokenResult) { 1150 output.push(tokenResult.output); 1151 pos += (tokenResult.match[0].length || 1); 1152 } else { 1153 // Check for native tokens (except character classes) at the current position 1154 match = nativ.exec.call(nativeTokens[scope], pattern.slice(pos)); 1155 if (match) { 1156 output.push(match[0]); 1157 pos += match[0].length; 1158 } else { 1159 chr = pattern.charAt(pos); 1160 if (chr === "[") { 1161 scope = classScope; 1162 } else if (chr === "]") { 1163 scope = defaultScope; 1164 } 1165 // Advance position by one character 1166 output.push(chr); 1167 ++pos; 1168 } 1169 } 1170 } 1171 1172 return augment(new RegExp(output.join(""), nativ.replace.call(flags, /[^gimy]+/g, "")), 1173 tokenContext.hasNamedCapture ? tokenContext.captureNames : null); 1174 }; 1175 1176/*-------------------------------------- 1177 * Public methods/properties 1178 *------------------------------------*/ 1179 1180// Installed and uninstalled states for `XRegExp.addToken` 1181 addToken = { 1182 on: function (regex, handler, options) { 1183 options = options || {}; 1184 if (regex) { 1185 tokens.push({ 1186 pattern: copy(regex, "g" + (hasNativeY ? "y" : "")), 1187 handler: handler, 1188 scope: options.scope || defaultScope, 1189 trigger: options.trigger || null 1190 }); 1191 } 1192 // Providing `customFlags` with null `regex` and `handler` allows adding flags that do 1193 // nothing, but don't throw an error 1194 if (options.customFlags) { 1195 registeredFlags = nativ.replace.call(registeredFlags + options.customFlags, duplicateFlags, ""); 1196 } 1197 }, 1198 off: function () { 1199 throw new Error("extensibility must be installed before using addToken"); 1200 } 1201 }; 1202 1203/** 1204 * Extends or changes XRegExp syntax and allows custom flags. This is used internally and can be 1205 * used to create XRegExp addons. `XRegExp.install('extensibility')` must be run before calling 1206 * this function, or an error is thrown. If more than one token can match the same string, the last 1207 * added wins. 1208 * @memberOf XRegExp 1209 * @param {RegExp} regex Regex object that matches the new token. 1210 * @param {Function} handler Function that returns a new pattern string (using native regex syntax) 1211 * to replace the matched token within all future XRegExp regexes. Has access to persistent 1212 * properties of the regex being built, through `this`. Invoked with two arguments: 1213 * <li>The match array, with named backreference properties. 1214 * <li>The regex scope where the match was found. 1215 * @param {Object} [options] Options object with optional properties: 1216 * <li>`scope` {String} Scopes where the token applies: 'default', 'class', or 'all'. 1217 * <li>`trigger` {Function} Function that returns `true` when the token should be applied; e.g., 1218 * if a flag is set. If `false` is returned, the matched string can be matched by other tokens. 1219 * Has access to persistent properties of the regex being built, through `this` (including 1220 * function `this.hasFlag`). 1221 * <li>`customFlags` {String} Nonnative flags used by the token's handler or trigger functions.
1222 * Prevents XRegExp from throwing an invalid flag error when the specified flags are used. 1223 * @example 1224 * 1225 * // Basic usage: Adds \a for ALERT character 1226 * XRegExp.addToken( 1227 * /\\a/, 1228 * function () {return '\\x07';}, 1229 * {scope: 'all'} 1230 * ); 1231 * XRegExp('\\a[\\a-\\n]+').test('\x07\n\x07'); // -> true 1232 */ 1233 self.addToken = addToken.off; 1234 1235/** 1236 * Caches and returns the result of calling `XRegExp(pattern, flags)`. On any subsequent call with 1237 * the same pattern and flag combination, the cached copy is returned. 1238 * @memberOf XRegExp 1239 * @param {String} pattern Regex pattern string. 1240 * @param {String} [flags] Any combination of XRegExp flags. 1241 * @returns {RegExp} Cached XRegExp object. 1242 * @example 1243 * 1244 * while (match = XRegExp.cache('.', 'gs').exec(str)) { 1245 * // The regex is compiled once only 1246 * } 1247 */ 1248 self.cache = function (pattern, flags) { 1249 var key = pattern + "/" + (flags || ""); 1250 return cache[key] || (cache[key] = self(pattern, flags)); 1251 }; 1252 1253/** 1254 * Escapes any regular expression metacharacters, for use when matching literal strings. The result 1255 * can safely be used at any point within a regex that uses any flags. 1256 * @memberOf XRegExp 1257 * @param {String} str String to escape. 1258 * @returns {String} String with regex metacharacters escaped. 1259 * @example 1260 * 1261 * XRegExp.escape('Escaped? <.>'); 1262 * // -> 'Escaped\?\ <\.>' 1263 */ 1264 self.escape = function (str) { 1265 return nativ.replace.call(str, /[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 1266 }; 1267 1268/** 1269 * Executes a regex search in a specified string. Returns a match array or `null`. If the provided 1270 * regex uses named capture, named backreference properties are included on the match array. 1271 * Optional `pos` and `sticky` arguments specify the search start position, and whether the match 1272 * must start at the specified position only. The `lastIndex` property of the provided regex is not 1273 * used, but is updated for compatibility. Also fixes browser bugs compared to the native 1274 * `RegExp.prototype.exec` and can be used reliably cross-browser. 1275 * @memberOf XRegExp 1276 * @param {String} str String to search. 1277 * @param {RegExp} regex Regex to search with. 1278 * @param {Number} [pos=0] Zero-based index at which to start the search. 1279 * @param {Boolean|String} [sticky=false] Whether the match must start at the specified position 1280 * only. The string `'sticky'` is accepted as an alternative to `true`. 1281 * @returns {Array} Match array with named backreference properties, or null. 1282 * @example 1283 * 1284 * // Basic use, with named backreference 1285 * var match = XRegExp.exec('U+2620', XRegExp('U\\+(?<hex>[0-9A-F]{4})')); 1286 * match.hex; // -> '2620' 1287 * 1288 * // With pos and sticky, in a loop 1289 * var pos = 2, result = [], match; 1290 * while (match = XRegExp.exec('<1><2><3><4>5<6>', /<(\d)>/, pos, 'sticky')) { 1291 * result.push(match[1]); 1292 * pos = match.index + match[0].length; 1293 * } 1294 * // result -> ['2', '3', '4'] 1295 */ 1296 self.exec = function (str, regex, pos, sticky) { 1297 var r2 = copy(regex, "g" + (sticky && hasNativeY ? "y" : ""), (sticky === false ? "y" : "")), 1298 match; 1299 r2.lastIndex = pos = pos || 0; 1300 match = fixed.exec.call(r2, str); // Fixed `exec` required for `lastIndex` fix, etc. 1301 if (sticky && match && match.index !== pos) { 1302 match = null; 1303 } 1304 if (regex.global) { 1305 regex.lastIndex = match ? r2.lastIndex : 0; 1306 } 1307 return match; 1308 }; 1309 1310/** 1311 * Executes a provided function once per regex match. 1312 * @memberOf XRegExp 1313 * @param {String} str String to search. 1314 * @param {RegExp} regex Regex to search with. 1315 * @param {Function} callback Function to execute for each match. Invoked with four arguments: 1316 * <li>The match array, with named backreference properties. 1317 * <li>The zero-based match index. 1318 * <li>The string being traversed. 1319 * <li>The regex object being used to traverse the string. 1320 * @param {*} [context] Object to use as `this` when executing `callback`. 1321 * @returns {*} Provided `context` object. 1322 * @example 1323 * 1324 * // Extracts every other digit from a string
1325 * XRegExp.forEach('1a2345', /\d/, function (match, i) { 1326 * if (i % 2) this.push(+match[0]); 1327 * }, []); 1328 * // -> [2, 4] 1329 */ 1330 self.forEach = function (str, regex, callback, context) { 1331 var pos = 0, 1332 i = -1, 1333 match; 1334 while ((match = self.exec(str, regex, pos))) { 1335 callback.call(context, match, ++i, str, regex); 1336 pos = match.index + (match[0].length || 1); 1337 } 1338 return context; 1339 }; 1340 1341/** 1342 * Copies a regex object and adds flag `g`. The copy maintains special properties for named 1343 * capture, is augmented with `XRegExp.prototype` methods, and has a fresh `lastIndex` property 1344 * (set to zero). Native regexes are not recompiled using XRegExp syntax. 1345 * @memberOf XRegExp 1346 * @param {RegExp} regex Regex to globalize. 1347 * @returns {RegExp} Copy of the provided regex with flag `g` added. 1348 * @example 1349 * 1350 * var globalCopy = XRegExp.globalize(/regex/); 1351 * globalCopy.global; // -> true 1352 */ 1353 self.globalize = function (regex) { 1354 return copy(regex, "g"); 1355 }; 1356 1357/** 1358 * Installs optional features according to the specified options. 1359 * @memberOf XRegExp 1360 * @param {Object|String} options Options object or string. 1361 * @example 1362 * 1363 * // With an options object 1364 * XRegExp.install({ 1365 * // Overrides native regex methods with fixed/extended versions that support named 1366 * // backreferences and fix numerous cross-browser bugs 1367 * natives: true, 1368 * 1369 * // Enables extensibility of XRegExp syntax and flags 1370 * extensibility: true 1371 * }); 1372 * 1373 * // With an options string 1374 * XRegExp.install('natives extensibility'); 1375 * 1376 * // Using a shortcut to install all optional features 1377 * XRegExp.install('all'); 1378 */ 1379 self.install = function (options) { 1380 options = prepareOptions(options); 1381 if (!features.natives && options.natives) { 1382 setNatives(true); 1383 } 1384 if (!features.extensibility && options.extensibility) { 1385 setExtensibility(true); 1386 } 1387 }; 1388 1389/** 1390 * Checks whether an individual optional feature is installed. 1391 * @memberOf XRegExp 1392 * @param {String} feature Name of the feature to check. One of: 1393 * <li>`natives` 1394 * <li>`extensibility` 1395 * @returns {Boolean} Whether the feature is installed. 1396 * @example 1397 * 1398 * XRegExp.isInstalled('natives'); 1399 */ 1400 self.isInstalled = function (feature) { 1401 return !!(features[feature]); 1402 }; 1403 1404/** 1405 * Returns `true` if an object is a regex; `false` if it isn't. This works correctly for regexes 1406 * created in another frame, when `instanceof` and `constructor` checks would fail. 1407 * @memberOf XRegExp 1408 * @param {*} value Object to check. 1409 * @returns {Boolean} Whether the object is a `RegExp` object. 1410 * @example 1411 * 1412 * XRegExp.isRegExp('string'); // -> false 1413 * XRegExp.isRegExp(/regex/i); // -> true 1414 * XRegExp.isRegExp(RegExp('^', 'm')); // -> true 1415 * XRegExp.isRegExp(XRegExp('(?s).')); // -> true 1416 */ 1417 self.isRegExp = function (value) { 1418 return isType(value, "regexp"); 1419 }; 1420 1421/** 1422 * Retrieves the matches from searching a string using a chain of regexes that successively search 1423 * within previous matches. The provided `chain` array can contain regexes and objects with `regex` 1424 * and `backref` properties. When a backreference is specified, the named or numbered backreference 1425 * is passed forward to the next regex or returned. 1426 * @memberOf XRegExp 1427 * @param {String} str String to search. 1428 * @param {Array} chain Regexes that each search for matches within preceding results. 1429 * @returns {Array} Matches by the last regex in the chain, or an empty array. 1430 * @example 1431 * 1432 * // Basic usage; matches numbers within <b> tags 1433 * XRegExp.matchChain('1 <b>2</b> 3 <b>4 a 56</b>', [ 1434 * XRegExp('(?is)<b>.*?</b>'), 1435 * /\d+/ 1436 * ]); 1437 * // -> ['2', '4', '56'] 1438 * 1439 * // Passing forward and returning specific backreferences 1440 * html = '<a href="http://xregexp.com/api/">XRegExp</a>\ 1441 * <a href="http://www.google.com/">Google</a>'; 1442 * XRegExp.matchChain(html, [ 1443 * {regex: /<a href="([^"]+)">/i, backref: 1}, 1444 * {regex: XRegExp('(?i)^https?://(?<domain>[^/?#]+)'), backref: 'domain'} 1445 * ]); 1446 * // -> ['xregexp.com', 'www.google.com'] 1447 */ 1448 self.matchChain = function (str, chain) { 1449 return (function recurseChain(values, level) {
1450 var item = chain[level].regex ? chain[level] : {regex: chain[level]}, 1451 matches = [], 1452 addMatch = function (match) { 1453 matches.push(item.backref ? (match[item.backref] || "") : match[0]); 1454 }, 1455 i; 1456 for (i = 0; i < values.length; ++i) { 1457 self.forEach(values[i], item.regex, addMatch); 1458 } 1459 return ((level === chain.length - 1) || !matches.length) ? 1460 matches : 1461 recurseChain(matches, level + 1); 1462 }([str], 0)); 1463 }; 1464 1465/** 1466 * Returns a new string with one or all matches of a pattern replaced. The pattern can be a string 1467 * or regex, and the replacement can be a string or a function to be called for each match. To 1468 * perform a global search and replace, use the optional `scope` argument or include flag `g` if 1469 * using a regex. Replacement strings can use `${n}` for named and numbered backreferences. 1470 * Replacement functions can use named backreferences via `arguments[0].name`. Also fixes browser 1471 * bugs compared to the native `String.prototype.replace` and can be used reliably cross-browser. 1472 * @memberOf XRegExp 1473 * @param {String} str String to search. 1474 * @param {RegExp|String} search Search pattern to be replaced. 1475 * @param {String|Function} replacement Replacement string or a function invoked to create it. 1476 * Replacement strings can include special replacement syntax: 1477 * <li>$$ - Inserts a literal '$'. 1478 * <li>$&, $0 - Inserts the matched substring. 1479 * <li>$` - Inserts the string that precedes the matched substring (left context). 1480 * <li>$' - Inserts the string that follows the matched substring (right context). 1481 * <li>$n, $nn - Where n/nn are digits referencing an existent capturing group, inserts 1482 * backreference n/nn. 1483 * <li>${n} - Where n is a name or any number of digits that reference an existent capturing 1484 * group, inserts backreference n. 1485 * Replacement functions are invoked with three or more arguments: 1486 * <li>The matched substring (corresponds to $& above). Named backreferences are accessible as 1487 * properties of this first argument. 1488 * <li>0..n arguments, one for each backreference (corresponding to $1, $2, etc. above). 1489 * <li>The zero-based index of the match within the total search string. 1490 * <li>The total string being searched. 1491 * @param {String} [scope='one'] Use 'one' to replace the first match only, or 'all'. If not 1492 * explicitly specified and using a regex with flag `g`, `scope` is 'all'. 1493 * @returns {String} New string with one or all matches replaced. 1494 * @example 1495 * 1496 * // Regex search, using named backreferences in replacement string 1497 * var name = XRegExp('(?<first>\\w+) (?<last>\\w+)'); 1498 * XRegExp.replace('John Smith', name, '${last}, ${first}'); 1499 * // -> 'Smith, John' 1500 * 1501 * // Regex search, using named backreferences in replacement function 1502 * XRegExp.replace('John Smith', name, function (match) { 1503 * return match.last + ', ' + match.first; 1504 * }); 1505 * // -> 'Smith, John' 1506 * 1507 * // Global string search/replacement 1508 * XRegExp.replace('RegExp builds RegExps', 'RegExp', 'XRegExp', 'all'); 1509 * // -> 'XRegExp builds XRegExps' 1510 */ 1511 self.replace = function (str, search, replacement, scope) { 1512 var isRegex = self.isRegExp(search), 1513 search2 = search, 1514 result; 1515 if (isRegex) { 1516 if (scope === undef && search.global) { 1517 scope = "all"; // Follow flag g when `scope` isn't explicit 1518 } 1519 // Note that since a copy is used, `search`'s `lastIndex` isn't updated *during* replacement iterations 1520 search2 = copy(search, scope === "all" ? "g" : "", scope === "all" ? "" : "g"); 1521 } else if (scope === "all") { 1522 search2 = new RegExp(self.escape(String(search)), "g"); 1523 } 1524 result = fixed.replace.call(String(str), search2, replacement); // Fixed `replace` required for named backreferences, etc. 1525 if (isRegex && search.global) { 1526 search.lastIndex = 0; // Fixes IE, Safari bug (last tested IE 9, Safari 5.1) 1527 } 1528 return result; 1529 }; 1530 1531/**
1532 * Splits a string into an array of strings using a regex or string separator. Matches of the 1533 * separator are not included in the result array. However, if `separator` is a regex that contains 1534 * capturing groups, backreferences are spliced into the result each time `separator` is matched. 1535 * Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably 1536 * cross-browser. 1537 * @memberOf XRegExp 1538 * @param {String} str String to split. 1539 * @param {RegExp|String} separator Regex or string to use for separating the string. 1540 * @param {Number} [limit] Maximum number of items to include in the result array. 1541 * @returns {Array} Array of substrings. 1542 * @example 1543 * 1544 * // Basic use 1545 * XRegExp.split('a b c', ' '); 1546 * // -> ['a', 'b', 'c'] 1547 * 1548 * // With limit 1549 * XRegExp.split('a b c', ' ', 2); 1550 * // -> ['a', 'b'] 1551 * 1552 * // Backreferences in result array 1553 * XRegExp.split('..word1..', /([a-z]+)(\d+)/i); 1554 * // -> ['..', 'word', '1', '..'] 1555 */ 1556 self.split = function (str, separator, limit) { 1557 return fixed.split.call(str, separator, limit); 1558 }; 1559 1560/** 1561 * Executes a regex search in a specified string. Returns `true` or `false`. Optional `pos` and 1562 * `sticky` arguments specify the search start position, and whether the match must start at the 1563 * specified position only. The `lastIndex` property of the provided regex is not used, but is 1564 * updated for compatibility. Also fixes browser bugs compared to the native 1565 * `RegExp.prototype.test` and can be used reliably cross-browser. 1566 * @memberOf XRegExp 1567 * @param {String} str String to search. 1568 * @param {RegExp} regex Regex to search with. 1569 * @param {Number} [pos=0] Zero-based index at which to start the search. 1570 * @param {Boolean|String} [sticky=false] Whether the match must start at the specified position 1571 * only. The string `'sticky'` is accepted as an alternative to `true`. 1572 * @returns {Boolean} Whether the regex matched the provided value. 1573 * @example 1574 * 1575 * // Basic use 1576 * XRegExp.test('abc', /c/); // -> true 1577 * 1578 * // With pos and sticky 1579 * XRegExp.test('abc', /c/, 0, 'sticky'); // -> false 1580 */ 1581 self.test = function (str, regex, pos, sticky) { 1582 // Do this the easy way :-) 1583 return !!self.exec(str, regex, pos, sticky); 1584 }; 1585 1586/** 1587 * Uninstalls optional features according to the specified options. 1588 * @memberOf XRegExp 1589 * @param {Object|String} options Options object or string. 1590 * @example 1591 * 1592 * // With an options object 1593 * XRegExp.uninstall({ 1594 * // Restores native regex methods 1595 * natives: true, 1596 * 1597 * // Disables additional syntax and flag extensions 1598 * extensibility: true 1599 * }); 1600 * 1601 * // With an options string 1602 * XRegExp.uninstall('natives extensibility'); 1603 * 1604 * // Using a shortcut to uninstall all optional features 1605 * XRegExp.uninstall('all'); 1606 */ 1607 self.uninstall = function (options) { 1608 options = prepareOptions(options); 1609 if (features.natives && options.natives) { 1610 setNatives(false); 1611 } 1612 if (features.extensibility && options.extensibility) { 1613 setExtensibility(false); 1614 } 1615 }; 1616 1617/** 1618 * Returns an XRegExp object that is the union of the given patterns. Patterns can be provided as 1619 * regex objects or strings. Metacharacters are escaped in patterns provided as strings. 1620 * Backreferences in provided regex objects are automatically renumbered to work correctly. Native 1621 * flags used by provided regexes are ignored in favor of the `flags` argument. 1622 * @memberOf XRegExp 1623 * @param {Array} patterns Regexes and strings to combine. 1624 * @param {String} [flags] Any combination of XRegExp flags. 1625 * @returns {RegExp} Union of the provided regexes and strings. 1626 * @example 1627 * 1628 * XRegExp.union(['a+b*c', /(dogs)\1/, /(cats)\1/], 'i'); 1629 * // -> /a\+b\*c|(dogs)\1|(cats)\2/i 1630 * 1631 * XRegExp.union([XRegExp('(?<pet>dogs)\\k<pet>'), XRegExp('(?<pet>cats)\\k<pet>')]); 1632 * // -> XRegExp('(?<pet>dogs)\\k<pet>|(?<pet>cats)\\k<pet>') 1633 */ 1634 self.union = function (patterns, flags) { 1635 var parts = /(\()(?!\?)|\\([1-9]\d*)|\\[\s\S]|\[(?:[^\\\]]|\\[\s\S])*]/g, 1636 numCaptures = 0, 1637 numPriorCaptures, 1638 captureNames, 1639 rewrite = function (match, paren, backref) { 1640 var name = captureNames[numCaptures - numPriorCaptures]; 1641 if (paren) { // Capturing group 1642 ++numCaptures; 1643 if (name) { // If the current capture has a name 1644 return "(?<" + name + ">"; 1645 } 1646 } else if (backref) { // Backreference 1647 return "\\" + (+backref + numPriorCaptures); 1648 } 1649 return match; 1650 }, 1651 output = [], 1652 pattern, 1653 i; 1654 if (!(isType(patterns, "array") && patterns.length)) { 1655 throw new TypeError("patterns must be a nonempty array"); 1656 } 1657 for (i = 0; i < patterns.length; ++i) { 1658 pattern = patterns[i]; 1659 if (self.isRegExp(pattern)) { 1660 numPriorCaptures = numCaptures; 1661 captureNames = (pattern.xregexp && pattern.xregexp.captureNames) || []; 1662 // Rewrite backreferences. Passing to XRegExp dies on octals and ensures patterns 1663 // are independently valid; helps keep this simple. Named captures are put back 1664 output.push(self(pattern.source).source.replace(parts, rewrite)); 1665 } else { 1666 output.push(self.escape(pattern)); 1667 } 1668 } 1669 return self(output.join("|"), flags); 1670 }; 1671 1672/** 1673 * The XRegExp version number. 1674 * @static 1675 * @memberOf XRegExp
1676 * @type String 1677 */ 1678 self.version = "2.0.0"; 1679 1680/*-------------------------------------- 1681 * Fixed/extended native methods 1682 *------------------------------------*/ 1683 1684/** 1685 * Adds named capture support (with backreferences returned as `result.name`), and fixes browser 1686 * bugs in the native `RegExp.prototype.exec`. Calling `XRegExp.install('natives')` uses this to 1687 * override the native method. Use via `XRegExp.exec` without overriding natives. 1688 * @private 1689 * @param {String} str String to search. 1690 * @returns {Array} Match array with named backreference properties, or null. 1691 */ 1692 fixed.exec = function (str) { 1693 var match, name, r2, origLastIndex, i; 1694 if (!this.global) { 1695 origLastIndex = this.lastIndex; 1696 } 1697 match = nativ.exec.apply(this, arguments); 1698 if (match) { 1699 // Fix browsers whose `exec` methods don't consistently return `undefined` for 1700 // nonparticipating capturing groups 1701 if (!compliantExecNpcg && match.length > 1 && lastIndexOf(match, "") > -1) { 1702 r2 = new RegExp(this.source, nativ.replace.call(getNativeFlags(this), "g", "")); 1703 // Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed 1704 // matching due to characters outside the match 1705 nativ.replace.call(String(str).slice(match.index), r2, function () { 1706 var i; 1707 for (i = 1; i < arguments.length - 2; ++i) { 1708 if (arguments[i] === undef) { 1709 match[i] = undef; 1710 } 1711 } 1712 }); 1713 } 1714 // Attach named capture properties 1715 if (this.xregexp && this.xregexp.captureNames) { 1716 for (i = 1; i < match.length; ++i) { 1717 name = this.xregexp.captureNames[i - 1]; 1718 if (name) { 1719 match[name] = match[i]; 1720 } 1721 } 1722 } 1723 // Fix browsers that increment `lastIndex` after zero-length matches 1724 if (this.global && !match[0].length && (this.lastIndex > match.index)) { 1725 this.lastIndex = match.index; 1726 } 1727 } 1728 if (!this.global) { 1729 this.lastIndex = origLastIndex; // Fixes IE, Opera bug (last tested IE 9, Opera 11.6) 1730 } 1731 return match; 1732 }; 1733 1734/** 1735 * Fixes browser bugs in the native `RegExp.prototype.test`. Calling `XRegExp.install('natives')` 1736 * uses this to override the native method. 1737 * @private 1738 * @param {String} str String to search. 1739 * @returns {Boolean} Whether the regex matched the provided value. 1740 */ 1741 fixed.test = function (str) { 1742 // Do this the easy way :-) 1743 return !!fixed.exec.call(this, str); 1744 }; 1745 1746/** 1747 * Adds named capture support (with backreferences returned as `result.name`), and fixes browser 1748 * bugs in the native `String.prototype.match`. Calling `XRegExp.install('natives')` uses this to 1749 * override the native method. 1750 * @private 1751 * @param {RegExp} regex Regex to search with. 1752 * @returns {Array} If `regex` uses flag g, an array of match strings or null. Without flag g, the 1753 * result of calling `regex.exec(this)`. 1754 */ 1755 fixed.match = function (regex) { 1756 if (!self.isRegExp(regex)) { 1757 regex = new RegExp(regex); // Use native `RegExp` 1758 } else if (regex.global) { 1759 var result = nativ.match.apply(this, arguments); 1760 regex.lastIndex = 0; // Fixes IE bug 1761 return result; 1762 } 1763 return fixed.exec.call(regex, this); 1764 }; 1765 1766/** 1767 * Adds support for `${n}` tokens for named and numbered backreferences in replacement text, and 1768 * provides named backreferences to replacement functions as `arguments[0].name`. Also fixes 1769 * browser bugs in replacement text syntax when performing a replacement using a nonregex search 1770 * value, and the value of a replacement regex's `lastIndex` property during replacement iterations 1771 * and upon completion. Note that this doesn't support SpiderMonkey's proprietary third (`flags`) 1772 * argument. Calling `XRegExp.install('natives')` uses this to override the native method. Use via 1773 * `XRegExp.replace` without overriding natives. 1774 * @private 1775 * @param {RegExp|String} search Search pattern to be replaced. 1776 * @param {String|Function} replacement Replacement string or a function invoked to create it. 1777 * @returns {String} New string with one or all matches replaced. 1778 */ 1779 fixed.replace = function (search, replacement) { 1780 var isRegex = self.isRegExp(search), captureNames, result, str, origLastIndex; 1781 if (isRegex) { 1782 if (search.xregexp) { 1783 captureNames = search.xregexp.captureNames; 1784 } 1785 if (!search.global) { 1786 origLastIndex = search.lastIndex; 1787 } 1788 } else { 1789 search += ""; 1790 } 1791 if (isType(replacement, "function")) { 1792 result = nativ.replace.call(String(this), search, function () { 1793 var args = arguments, i; 1794 if (captureNames) { 1795 // Change the `arguments[0]` string primitive to a `String` object that can store properties 1796 args[0] = new String(args[0]); 1797 // Store named backreferences on the first argument 1798 for (i = 0; i < captureNames.length; ++i) { 1799 if (captureNames[i]) { 1800 args[0][captureNames[i]] = args[i + 1];
1801 } 1802 } 1803 } 1804 // Update `lastIndex` before calling `replacement`. 1805 // Fixes IE, Chrome, Firefox, Safari bug (last tested IE 9, Chrome 17, Firefox 11, Safari 5.1) 1806 if (isRegex && search.global) { 1807 search.lastIndex = args[args.length - 2] + args[0].length; 1808 } 1809 return replacement.apply(null, args); 1810 }); 1811 } else { 1812 str = String(this); // Ensure `args[args.length - 1]` will be a string when given nonstring `this` 1813 result = nativ.replace.call(str, search, function () { 1814 var args = arguments; // Keep this function's `arguments` available through closure 1815 return nativ.replace.call(String(replacement), replacementToken, function ($0, $1, $2) { 1816 var n; 1817 // Named or numbered backreference with curly brackets 1818 if ($1) { 1819 /* XRegExp behavior for `${n}`: 1820 * 1. Backreference to numbered capture, where `n` is 1+ digits. `0`, `00`, etc. is the entire match. 1821 * 2. Backreference to named capture `n`, if it exists and is not a number overridden by numbered capture. 1822 * 3. Otherwise, it's an error. 1823 */ 1824 n = +$1; // Type-convert; drop leading zeros 1825 if (n <= args.length - 3) { 1826 return args[n] || ""; 1827 } 1828 n = captureNames ? lastIndexOf(captureNames, $1) : -1; 1829 if (n < 0) { 1830 throw new SyntaxError("backreference to undefined group " + $0); 1831 } 1832 return args[n + 1] || ""; 1833 } 1834 // Else, special variable or numbered backreference (without curly brackets) 1835 if ($2 === "$") return "$"; 1836 if ($2 === "&" || +$2 === 0) return args[0]; // $&, $0 (not followed by 1-9), $00 1837 if ($2 === "`") return args[args.length - 1].slice(0, args[args.length - 2]); 1838 if ($2 === "'") return args[args.length - 1].slice(args[args.length - 2] + args[0].length); 1839 // Else, numbered backreference (without curly brackets) 1840 $2 = +$2; // Type-convert; drop leading zero 1841 /* XRegExp behavior: 1842 * - Backreferences without curly brackets end after 1 or 2 digits. Use `${..}` for more digits. 1843 * - `$1` is an error if there are no capturing groups. 1844 * - `$10` is an error if there are less than 10 capturing groups. Use `${1}0` instead. 1845 * - `$01` is equivalent to `$1` if a capturing group exists, otherwise it's an error. 1846 * - `$0` (not followed by 1-9), `$00`, and `$&` are the entire match. 1847 * Native behavior, for comparison: 1848 * - Backreferences end after 1 or 2 digits. Cannot use backreference to capturing group 100+. 1849 * - `$1` is a literal `$1` if there are no capturing groups. 1850 * - `$10` is `$1` followed by a literal `0` if there are less than 10 capturing groups. 1851 * - `$01` is equivalent to `$1` if a capturing group exists, otherwise it's a literal `$01`. 1852 * - `$0` is a literal `$0`. `$&` is the entire match. 1853 */ 1854 if (!isNaN($2)) { 1855 if ($2 > args.length - 3) { 1856 throw new SyntaxError("backreference to undefined group " + $0); 1857 } 1858 return args[$2] || ""; 1859 } 1860 throw new SyntaxError("invalid token " + $0); 1861 }); 1862 }); 1863 } 1864 if (isRegex) { 1865 if (search.global) { 1866 search.lastIndex = 0; // Fixes IE, Safari bug (last tested IE 9, Safari 5.1) 1867 } else { 1868 search.lastIndex = origLastIndex; // Fixes IE, Opera bug (last tested IE 9, Opera 11.6) 1869 } 1870 } 1871 return result; 1872 }; 1873 1874/** 1875 * Fixes browser bugs in the native `String.prototype.split`. Calling `XRegExp.install('natives')` 1876 * uses this to override the native method. Use via `XRegExp.split` without overriding natives. 1877 * @private 1878 * @param {RegExp|String} separator Regex or string to use for separating the string. 1879 * @param {Number} [limit] Maximum number of items to include in the result array. 1880 * @returns {Array} Array of substrings. 1881 */ 1882 fixed.split = function (separator, limit) { 1883 if (!self.isRegExp(separator)) { 1884 return nativ.split.apply(this, arguments); // use faster native method 1885 } 1886 var str = String(this), 1887 origLastIndex = separator.lastIndex, 1888 output = [], 1889 lastLastIndex = 0, 1890 lastLength; 1891 /* Values for `limit`, per the spec: 1892 * If undefined: pow(2,32) - 1 1893 * If 0, Infinity, or NaN: 0 1894 * If positive number: limit = floor(limit); if (limit >= pow(2,32)) limit -= pow(2,32); 1895 * If negative number: pow(2,32) - floor(abs(limit)) 1896 * If other: Type-convert, then use the above rules 1897 */ 1898 limit = (limit === undef ? -1 : limit) >>> 0; 1899 self.forEach(str, separator, function (match) { 1900 if ((match.index + match[0].length) > lastLastIndex) { // != `if (match[0].length)` 1901 output.push(str.slice(lastLastIndex, match.index)); 1902 if (match.length > 1 && match.index < str.length) { 1903 Array.prototype.push.apply(output, match.slice(1)); 1904 } 1905 lastLength = match[0].length; 1906 lastLastIndex = match.index + lastLength; 1907 } 1908 }); 1909 if (lastLastIndex === str.length) { 1910 if (!nativ.test.call(separator, "") || lastLength) { 1911 output.push(""); 1912 } 1913 } else { 1914 output.push(str.slice(lastLastIndex)); 1915 } 1916 separator.lastIndex = origLastIndex; 1917 return output.length > limit ? output.slice(0, limit) : output; 1918 }; 1919 1920/*-------------------------------------- 1921 * Built-in tokens 1922 *------------------------------------*/ 1923 1924// Shortcut 1925 add = addToken.on; 1926 1927/* Letter identity escapes that natively match literal characters: \p, \P, etc. 1928 * Should be SyntaxErrors but are allowed in web reality. XRegExp makes them errors for cross- 1929 * browser consistency and to reserve their syntax, but lets them be superseded by XRegExp addons. 1930 */ 1931 add(/\\([ABCE-RTUVXYZaeg-mopqyz]|c(?![A-Za-z])|u(?![\dA-Fa-f]{4})|x(?![\dA-Fa-f]{2}))/, 1932 function (match, scope) { 1933 // \B is allowed in default scope only 1934 if (match[1] === "B" && scope === defaultScope) { 1935 return match[0]; 1936 } 1937 throw new SyntaxError("invalid escape " + match[0]); 1938 }, 1939 {scope: "all"}); 1940 1941/* Empty character class: [] or [^] 1942 * Fixes a critical cross-browser syntax inconsistency. Unless this is standardized (per the spec), 1943 * regex syntax can't be accurately parsed because character class endings can't be determined. 1944 */ 1945 add(/\[(\^?)]/, 1946 function (match) { 1947 // For cross-browser compatibility with ES3, convert [] to \b\B and [^] to [\s\S]. 1948 // (?!) should work like \b\B, but is unreliable in Firefox 1949 return match[1] ? "[\\s\\S]" : "\\b\\B"; 1950 }); 1951 1952/* Comment pattern: (?# ) 1953 * Inline comments are an alternative to the line comments allowed in free-spacing mode (flag x). 1954 */ 1955 add(/(?:\(\?#[^)]*\))+/, 1956 function (match) { 1957 // Keep tokens separated unless the following token is a quantifier 1958 return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)"; 1959 }); 1960 1961/* Named backreference: \k<name> 1962 * Backreference names can use the characters A-Z, a-z, 0-9, _, and $ only. 1963 */ 1964 add(/\\k<([\w$]+)>/, 1965 function (match) { 1966 var index = isNaN(match[1]) ? (lastIndexOf(this.captureNames, match[1]) + 1) : +match[1], 1967 endIndex = match.index + match[0].length; 1968 if (!index || index > this.captureNames.length) { 1969 throw new SyntaxError("backreference to undefined group " + match[0]); 1970 } 1971 // Keep backreferences separate from subsequent literal numbers 1972 return "\\" + index + ( 1973 endIndex === match.input.length || isNaN(match.input.charAt(endIndex)) ? "" : "(?:)" 1974 ); 1975 }); 1976 1977/* Whitespace and line comments, in free-spacing mode (aka extended mode, flag x) only. 1978 */ 1979 add(/(?:\s+|#.*)+/, 1980 function (match) { 1981 // Keep tokens separated unless the following token is a quantifier 1982 return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)"; 1983 }, 1984 { 1985 trigger: function () { 1986 return this.hasFlag("x"); 1987 }, 1988 customFlags: "x" 1989 }); 1990 1991/* Dot, in dotall mode (aka singleline mode, flag s) only. 1992 */ 1993 add(/\./, 1994 function () { 1995 return "[\\s\\S]"; 1996 }, 1997 { 1998 trigger: function () { 1999 return this.hasFlag("s"); 2000 }, 2001 customFlags: "s" 2002 }); 2003 2004/* Named capturing group; match the opening delimiter only: (?<name> 2005 * Capture names can use the characters A-Z, a-z, 0-9, _, and $ only. Names can't be integers. 2006 * Supports Python-style (?P<name> as an alternate syntax to avoid issues in recent Opera (which 2007 * natively supports the Python-style syntax). Otherwise, XRegExp might treat numbered 2008 * backreferences to Python-style named capture as octals. 2009 */ 2010 add(/\(\?P?<([\w$]+)>/, 2011 function (match) { 2012 if (!isNaN(match[1])) { 2013 // Avoid incorrect lookups, since named backreferences are added to match arrays 2014 throw new SyntaxError("can't use integer as capture name " + match[0]); 2015 } 2016 this.captureNames.push(match[1]); 2017 this.hasNamedCapture = true; 2018 return "("; 2019 }); 2020 2021/* Numbered backreference or octal, plus any following digits: \0, \11, etc. 2022 * Octals except \0 not followed by 0-9 and backreferences to unopened capture groups throw an 2023 * error. Other matches are returned unaltered. IE <= 8 doesn't support backreferences greater than 2024 * \99 in regex syntax. 2025 */ 2026 add(/\\(\d+)/, 2027 function (match, scope) { 2028 if (!(scope === defaultScope && /^[1-9]/.test(match[1]) && +match[1] <= this.captureNames.length) && 2029 match[1] !== "0") { 2030 throw new SyntaxError("can't use octal escape or backreference to undefined group " + match[0]); 2031 } 2032 return match[0]; 2033 }, 2034 {scope: "all"}); 2035 2036/* Capturing group; match the opening parenthesis only. 2037 * Required for support of named capturing groups. Also adds explicit capture mode (flag n). 2038 */ 2039 add(/\((?!\?)/, 2040 function () { 2041 if (this.hasFlag("n")) { 2042 return "(?:"; 2043 } 2044 this.captureNames.push(null); 2045 return "("; 2046 }, 2047 {customFlags: "n"}); 2048 2049/*-------------------------------------- 2050 * Expose XRegExp 2051 *------------------------------------*/ 2052 2053// For CommonJS enviroments 2054 if (typeof exports !== "undefined") { 2055 exports.XRegExp = self; 2056 } 2057 2058 return self; 2059 2060}()); 2061 2062 2063 2064/********* 2065* ../app/js/lib/clipboard.js 2066**********/ 2067/*! 2068 * clipboard.js v1.5.12 2069 * https://zenorocha.github.io/clipboard.js 2070 * 2071 * Licensed MIT © Zeno Rocha 2072 */ 2073(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 2074var matches = require('matches-selector') 2075 2076module.exports = function (element, selector, checkYoSelf) { 2077 var parent = checkYoSelf ? element : element.parentNode 2078 2079 while (parent && parent !== document) { 2080 if (matches(parent, selector)) return parent; 2081 parent = parent.parentNode 2082 } 2083} 2084 2085},{"matches-selector":5}],2:[function(require,module,exports){ 2086var closest = require('closest'); 2087 2088/** 2089 * Delegates event to a selector. 2090 * 2091 * @param {Element} element 2092 * @param {String} selector 2093 * @param {String} type 2094 * @param {Function} callback 2095 * @param {Boolean} useCapture 2096 * @return {Object} 2097 */ 2098function delegate(element, selector, type, callback, useCapture) { 2099 var listenerFn = listener.apply(this, arguments); 2100 2101 element.addEventListener(type, listenerFn, useCapture); 2102 2103 return { 2104 destroy: function() { 2105 element.removeEventListener(type, listenerFn, useCapture); 2106 } 2107 } 2108} 2109 2110/** 2111 * Finds closest match and invokes callback. 2112 * 2113 * @param {Element} element 2114 * @param {String} selector 2115 * @param {String} type 2116 * @param {Function} callback 2117 * @return {Function} 2118 */ 2119function listener(element, selector, type, callback) { 2120 return function(e) { 2121 e.delegateTarget = closest(e.target, selector, true); 2122 2123 if (e.delegateTarget) { 2124 callback.call(element, e); 2125 } 2126 } 2127} 2128 2129module.exports = delegate; 2130 2131},{"closest":1}],3:[function(require,module,exports){ 2132/** 2133 * Check if argument is a HTML element. 2134 * 2135 * @param {Object} value 2136 * @return {Boolean} 2137 */ 2138exports.node = function(value) { 2139 return value !== undefined 2140 && value instanceof HTMLElement 2141 && value.nodeType === 1; 2142}; 2143 2144/** 2145 * Check if argument is a list of HTML elements. 2146 * 2147 * @param {Object} value 2148 * @return {Boolean} 2149 */ 2150exports.nodeList = function(value) { 2151 var type = Object.prototype.toString.call(value); 2152 2153 return value !== undefined 2154 && (type === '[object NodeList]' || type === '[object HTMLCollection]') 2155 && ('length' in value) 2156 && (value.length === 0 || exports.node(value[0])); 2157}; 2158 2159/** 2160 * Check if argument is a string. 2161 * 2162 * @param {Object} value 2163 * @return {Boolean} 2164 */ 2165exports.string = function(value) { 2166 return typeof value === 'string' 2167 || value instanceof String; 2168}; 2169 2170/** 2171 * Check if argument is a function. 2172 * 2173 * @param {Object} value 2174 * @return {Boolean} 2175 */ 2176exports.fn = function(value) { 2177 var type = Object.prototype.toString.call(value); 2178 2179 return type === '[object Function]'; 2180}; 2181 2182},{}],4:[function(require,module,exports){ 2183var is = require('./is'); 2184var delegate = require('delegate'); 2185 2186/** 2187 * Validates all params and calls the right 2188 * listener function based on its target type. 2189 * 2190 * @param {String|HTMLElement|HTMLCollection|NodeList} target 2191 * @param {String} type 2192 * @param {Function} callback 2193 * @return {Object} 2194 */ 2195function listen(target, type, callback) { 2196 if (!target && !type && !callback) { 2197 throw new Error('Missing required arguments'); 2198 } 2199 2200 if (!is.string(type)) { 2201 throw new TypeError('Second argument must be a String'); 2202 } 2203 2204 if (!is.fn(callback)) { 2205 throw new TypeError('Third argument must be a Function'); 2206 } 2207 2208 if (is.node(target)) { 2209 return listenNode(target, type,
2209 callback); 2210 } 2211 else if (is.nodeList(target)) { 2212 return listenNodeList(target, type, callback); 2213 } 2214 else if (is.string(target)) { 2215 return listenSelector(target, type, callback); 2216 } 2217 else { 2218 throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList'); 2219 } 2220} 2221 2222/** 2223 * Adds an event listener to a HTML element 2224 * and returns a remove listener function. 2225 * 2226 * @param {HTMLElement} node 2227 * @param {String} type 2228 * @param {Function} callback 2229 * @return {Object} 2230 */ 2231function listenNode(node, type, callback) { 2232 node.addEventListener(type, callback); 2233 2234 return { 2235 destroy: function() { 2236 node.removeEventListener(type, callback); 2237 } 2238 } 2239} 2240 2241/** 2242 * Add an event listener to a list of HTML elements 2243 * and returns a remove listener function. 2244 * 2245 * @param {NodeList|HTMLCollection} nodeList 2246 * @param {String} type 2247 * @param {Function} callback 2248 * @return {Object} 2249 */ 2250function listenNodeList(nodeList, type, callback) { 2251 Array.prototype.forEach.call(nodeList, function(node) { 2252 node.addEventListener(type, callback); 2253 }); 2254 2255 return { 2256 destroy: function() { 2257 Array.prototype.forEach.call(nodeList, function(node) { 2258 node.removeEventListener(type, callback); 2259 }); 2260 } 2261 } 2262} 2263 2264/** 2265 * Add an event listener to a selector 2266 * and returns a remove listener function. 2267 * 2268 * @param {String} selector 2269 * @param {String} type 2270 * @param {Function} callback 2271 * @return {Object} 2272 */ 2273function listenSelector(selector, type, callback) { 2274 return delegate(document.body, selector, type, callback); 2275} 2276 2277module.exports = listen; 2278 2279},{"./is":3,"delegate":2}],5:[function(require,module,exports){ 2280 2281/** 2282 * Element prototype. 2283 */ 2284 2285var proto = Element.prototype; 2286 2287/** 2288 * Vendor function. 2289 */ 2290 2291var vendor = proto.matchesSelector 2292 || proto.webkitMatchesSelector 2293 || proto.mozMatchesSelector 2294 || proto.msMatchesSelector 2295 || proto.oMatchesSelector; 2296 2297/** 2298 * Expose `match()`. 2299 */ 2300 2301module.exports = match; 2302 2303/** 2304 * Match `el` to `selector`. 2305 * 2306 * @param {Element} el 2307 * @param {String} selector 2308 * @return {Boolean} 2309 * @api public 2310 */ 2311 2312function match(el, selector) { 2313 if (vendor) return vendor.call(el, selector); 2314 var nodes = el.parentNode.querySelectorAll(selector); 2315 for (var i = 0; i < nodes.length; ++i) { 2316 if (nodes[i] == el) return true; 2317 } 2318 return false; 2319} 2320},{}],6:[function(require,module,exports){ 2321function select(element) { 2322 var selectedText; 2323 2324 if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { 2325 element.focus(); 2326 element.setSelectionRange(0, element.value.length); 2327 2328 selectedText = element.value; 2329 } 2330 else { 2331 if (element.hasAttribute('contenteditable')) { 2332 element.focus(); 2333 } 2334 2335 var selection = window.getSelection(); 2336 var range = document.createRange(); 2337 2338 range.selectNodeContents(element); 2339 selection.removeAllRanges(); 2340 selection.addRange(range); 2341 2342 selectedText = selection.toString(); 2343 } 2344 2345 return selectedText; 2346} 2347 2348module.exports = select; 2349 2350},{}],7:[function(require,module,exports){ 2351function E () { 2352 // Keep this empty so it's easier to inherit from 2353 // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) 2354} 2355 2356E.prototype = { 2357 on: function (name, callback, ctx) { 2358 var e = this.e || (this.e = {}); 2359 2360 (e[name] || (e[name] = [])).push({ 2361 fn: callback, 2362 ctx: ctx 2363 }); 2364 2365 return this; 2366 }, 2367 2368 once: function (name, callback, ctx) { 2369 var self = this; 2370 function listener () { 2371 self.off(name, listener); 2372 callback.apply(ctx, arguments); 2373 }; 2374 2375 listener._ = callback 2376 return this.on(name, listener, ctx); 2377 }, 2378 2379 emit: function (name) { 2380 var data = [].slice.call(arguments, 1); 2381 var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); 2382 var i = 0; 2383 var len = evtArr.length; 2384 2385 for (i; i < len; i++) { 2386 evtArr[i].fn.apply(evtArr[i].ctx, data); 2387 } 2388 2389 return this; 2390 }, 2391 2392 off: function (name, callback) { 2393 var e = this.e || (this.e = {}); 2394 var evts = e[name]; 2395 var liveEvents = []; 2396 2397 if (evts && callback) { 2398 for (var i = 0, len = evts.length; i < len; i++) { 2399 if (evts[i].fn !== callback && evts[i].fn._ !== callback) 2400 liveEvents.push(evts[i]); 2401 } 2402 } 2403 2404 // Remove event from queue to prevent memory leak 2405 // Suggested by https://github.com/lazd 2406 // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 2407 2408 (liveEvents.length) 2409 ? e[name] = liveEvents 2410 : delete e[name]; 2411 2412 return this; 2413 } 2414}; 2415 2416module.exports = E; 2417 2418},{}],8:[function(require,module,exports){ 2419(function (global, factory) { 2420 if (typeof define === "function" && define.amd) { 2421 define(['module', 'select'], factory); 2422 } else if (typeof exports !== "undefined") { 2423 factory(module, require('select')); 2424 } else { 2425 var mod = { 2426 exports: {} 2427 }; 2428 factory(mod, global.select); 2429 global.clipboardAction = mod.exports; 2430 } 2431})(this, function (module, _select) { 2432 'use strict'; 2433 2434 var _select2 = _interopRequireDefault(_select); 2435 2436 function _interopRequireDefault(obj) { 2437 return obj && obj.__esModule ? obj : { 2438 default: obj 2439 }; 2440 } 2441 2442 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 2443 return typeof obj; 2444 } : function (obj) { 2445 return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; 2446 }; 2447 2448 function _classCallCheck(instance, Constructor) { 2449 if (!(instance instanceof Constructor)) { 2450 throw new TypeError("Cannot call a class as a function"); 2451 } 2452 } 2453 2454 var _createClass = function () { 2455 function defineProperties(target, props) { 2456 for (var i = 0; i < props.length; i++) { 2457 var descriptor = props[i]; 2458 descriptor.enumerable = descriptor.enumerable || false; 2459 descriptor.configurable = true; 2460 if ("value" in descriptor) descriptor.writable = true; 2461 Object.defineProperty(target, descriptor.key, descriptor); 2462 } 2463 } 2464 2465 return function (Constructor, protoProps, staticProps) { 2466 if (protoProps) defineProperties(Constructor.prototype, protoProps); 2467 if (staticProps) defineProperties(Constructor, staticProps); 2468 return Constructor; 2469 }; 2470 }(); 2471 2472 var ClipboardAction = function () { 2473 /** 2474 * @param {Object} options 2475 */ 2476 2477 function ClipboardAction(options) { 2478 _classCallCheck(this, ClipboardAction); 2479 2480 this.resolveOptions(options); 2481 this.initSelection(); 2482 } 2483 2484 /** 2485 * Defines base properties passed from constructor. 2486 * @param {Object} options 2487 */ 2488 2489 2490 ClipboardAction.prototype.resolveOptions = function resolveOptions() { 2491 var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; 2492 2493 this.action = options.action; 2494 this.emitter = options.emitter; 2495 this.target = options.target; 2496 this.text = options.text; 2497 this.trigger = options.trigger; 2498 2499 this.selectedText = ''; 2500 }; 2501 2502 ClipboardAction.prototype.initSelection = function initSelection() { 2503 if (this.text) { 2504 this.selectFake(); 2505 } else if (this.target) { 2506 this.selectTarget(); 2507 } 2508 }; 2509 2510 ClipboardAction.prototype.selectFake = function selectFake() { 2511 var _this = this; 2512 2513 var isRTL = document.documentElement.getAttribute('dir') == 'rtl'; 2514 2515 this.removeFake(); 2516 2517 this.fakeHandlerCallback = function () { 2518 return _this.removeFake(); 2519 }; 2520 this.fakeHandler = document.body.addEventListener('click', this.fakeHandlerCallback) || true; 2521 2522 this.fakeElem = document.createElement('textarea'); 2523 // Prevent zooming on iOS 2524 this.fakeElem.style.fontSize = '12pt'; 2525 // Reset box model 2526 this.fakeElem.style.border = '0'; 2527 this.fakeElem.style.padding = '0'; 2528 this.fakeElem.style.margin = '0'; 2529 // Move element out of screen horizontally 2530 this.fakeElem.style.position = 'absolute'; 2531 this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px'; 2532 // Move element to the same position vertically 2533 this.fakeElem.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px'; 2534 this.fakeElem.setAttribute('readonly', ''); 2535 this.fakeElem.value = this.text; 2536 2537 document.body.appendChild(this.fakeElem); 2538 2539 this.selectedText = (0, _select2.default)(this.fakeElem); 2540 this.copyText(); 2541 }; 2542 2543 ClipboardAction.prototype.removeFake = function removeFake() { 2544 if (this.fakeHandler) { 2545 document.body.removeEventListener('click', this.fakeHandlerCallback); 2546 this.fakeHandler = null; 2547 this.fakeHandlerCallback = null; 2548 } 2549 2550 if (this.fakeElem) { 2551 document.body.removeChild(this.fakeElem); 2552 this.fakeElem = null; 2553 } 2554 }; 2555 2556 ClipboardAction.prototype.selectTarget = function selectTarget() { 2557 this.selectedText = (0, _select2.default)(this.target); 2558 this.copyText(); 2559 }; 2560 2561 ClipboardAction.prototype.copyText = function copyText() { 2562 var succeeded = undefined; 2563 2564 try { 2565 succeeded = document.execCommand(this.action); 2566 } catch (err) { 2567 succeeded = false; 2568 } 2569 2570 this.handleResult(succeeded); 2571 }; 2572 2573 ClipboardAction.prototype.handleResult = function handleResult(succeeded) { 2574 if (succeeded) { 2575 this.emitter.emit('success', { 2576 action: this.action, 2577 text: this.selectedText, 2578 trigger: this.trigger, 2579 clearSelection: this.clearSelection.bind(this) 2580 }); 2581 } else { 2582 this.emitter.emit('error', { 2583 action: this.action, 2584 trigger: this.trigger, 2585 clearSelection: this.clearSelection.bind(this) 2586 }); 2587 } 2588 }; 2589 2590 ClipboardAction.prototype.clearSelection = function clearSelection() { 2591 if (this.target) { 2592 this.target.blur(); 2593 } 2594 2595 window.getSelection().removeAllRanges(); 2596 }; 2597 2598 ClipboardAction.prototype.destroy = function destroy() { 2599 this.removeFake(); 2600 }; 2601 2602 _createClass(ClipboardAction, [{ 2603 key: 'action', 2604 set: function set() { 2605 var action = arguments.length <= 0 || arguments[0] === undefined ? 'copy' : arguments[0]; 2606 2607 this._action = action; 2608 2609 if (this._action !== 'copy' && this._action !== 'cut') { 2610 throw new Error('Invalid "action" value, use either "copy" or "cut"'); 2611 } 2612 }, 2613 get: function get() { 2614 return this._action; 2615 } 2616 }, { 2617 key: 'target', 2618 set: function set(target) { 2619 if (target !== undefined) { 2620 if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) { 2621 if (this.action === 'copy' && target.hasAttribute('disabled')) { 2622 throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'); 2623 } 2624 2625 if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) { 2626 throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'); 2627 } 2628 2629 this._target = target; 2630 } else { 2631 throw new Error('Invalid "target" value, use a valid Element'); 2632 } 2633 } 2634 }, 2635 get: function get() { 2636 return this._target; 2637 } 2638 }]); 2639 2640 return ClipboardAction; 2641 }(); 2642 2643 module.exports = ClipboardAction; 2644}); 2645 2646},{"select":6}],9:[function(require,module,exports){ 2647(function (global, factory) { 2648 if (typeof define === "function" && define.amd) { 2649 define(['module', './clipboard-action', 'tiny-emitter', 'good-l
2649istener'], factory); 2650 } else if (typeof exports !== "undefined") { 2651 factory(module, require('./clipboard-action'), require('tiny-emitter'), require('good-listener')); 2652 } else { 2653 var mod = { 2654 exports: {} 2655 }; 2656 factory(mod, global.clipboardAction, global.tinyEmitter, global.goodListener); 2657 global.clipboard = mod.exports; 2658 } 2659})(this, function (module, _clipboardAction, _tinyEmitter, _goodListener) { 2660 'use strict'; 2661 2662 var _clipboardAction2 = _interopRequireDefault(_clipboardAction); 2663 2664 var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter); 2665 2666 var _goodListener2 = _interopRequireDefault(_goodListener); 2667 2668 function _interopRequireDefault(obj) { 2669 return obj && obj.__esModule ? obj : { 2670 default: obj 2671 }; 2672 } 2673 2674 function _classCallCheck(instance, Constructor) { 2675 if (!(instance instanceof Constructor)) { 2676 throw new TypeError("Cannot call a class as a function"); 2677 } 2678 } 2679 2680 function _possibleConstructorReturn(self, call) { 2681 if (!self) { 2682 throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 2683 } 2684 2685 return call && (typeof call === "object" || typeof call === "function") ? call : self; 2686 } 2687 2688 function _inherits(subClass, superClass) { 2689 if (typeof superClass !== "function" && superClass !== null) { 2690 throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); 2691 } 2692 2693 subClass.prototype = Object.create(superClass && superClass.prototype, { 2694 constructor: { 2695 value: subClass, 2696 enumerable: false, 2697 writable: true, 2698 configurable: true 2699 } 2700 }); 2701 if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 2702 } 2703 2704 var Clipboard = function (_Emitter) { 2705 _inherits(Clipboard, _Emitter); 2706 2707 /** 2708 * @param {String|HTMLElement|HTMLCollection|NodeList} trigger 2709 * @param {Object} options 2710 */ 2711 2712 function Clipboard(trigger, options) { 2713 _classCallCheck(this, Clipboard); 2714 2715 var _this = _possibleConstructorReturn(this, _Emitter.call(this)); 2716 2717 _this.resolveOptions(options); 2718 _this.listenClick(trigger); 2719 return _this; 2720 } 2721 2722 /** 2723 * Defines if attributes would be resolved using internal setter functions 2724 * or custom functions that were passed in the constructor. 2725 * @param {Object} options 2726 */ 2727 2728 2729 Clipboard.prototype.resolveOptions = function resolveOptions() { 2730 var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; 2731 2732 this.action = typeof options.action === 'function' ? options.action : this.defaultAction; 2733 this.target = typeof options.target === 'function' ? options.target : this.defaultTarget; 2734 this.text = typeof options.text === 'function' ? options.text : this.defaultText; 2735 }; 2736 2737 Clipboard.prototype.listenClick = function listenClick(trigger) { 2738 var _this2 = this; 2739 2740 this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) { 2741 return _this2.onClick(e); 2742 }); 2743 }; 2744 2745 Clipboard.prototype.onClick = function onClick(e) { 2746 var trigger = e.delegateTarget || e.currentTarget; 2747 2748 if (this.clipboardAction) { 2749 this.clipboardAction = null; 2750 } 2751 2752 this.clipboardAction = new _clipboardAction2.default({ 2753 action: this.action(trigger), 2754 target: this.target(trigger), 2755 text: this.text(trigger), 2756 trigger: trigger, 2757 emitter: this 2758 }); 2759 }; 2760 2761 Clipboard.prototype.defaultAction = function defaultAction(trigger) { 2762 return getAttributeValue('action', trigger); 2763 }; 2764 2765 Clipboard.prototype.defaultTarget = function defaultTarget(trigger) { 2766 var selector = getAttributeValue('target', trigger); 2767 2768 if (selector) { 2769 return document.querySelector(selector); 2770 } 2771 }; 2772 2773 Clipboard.prototype.defaultText = function defaultText(trigger) { 2774 return getAttributeValue('text', trigger); 2775 }; 2776 2777 Clipboard.prototype.destroy = function destroy() { 2778 this.listener.destroy(); 2779 2780 if (this.clipboardAction) { 2781 this.clipboardAction.destroy(); 2782 this.clipboardAction = null; 2783 } 2784 }; 2785 2786 return Clipboard; 2787 }(_tinyEmitter2.default); 2788 2789 /** 2790 * Helper function to retrieve attribute value. 2791 * @param {String} suffix 2792 * @param {Element} element 2793 */ 2794 function getAttributeValue(suffix, element) { 2795 var attribute = 'data-clipboard-' + suffix; 2796 2797 if (!element.hasAttribute(attribute)) { 2798 return; 2799 } 2800 2801 return element.getAttribute(attribute); 2802 } 2803 2804 module.exports = Clipboard; 2805}); 2806 2807},{"./clipboard-action":8,"good-listener":4,"tiny-emitter":7}]},{},[9])(9) 2808}); 2809 2810 2811/********* 2812* ../app/js/lib/i18next-1.7.2.js 2813**********/ 2814// i18next, v1.7.2
2815// Copyright (c)2014 Jan Mühlemann (jamuhl). 2816// Distributed under MIT license 2817// http://i18next.com 2818(function() { 2819 2820 // add indexOf to non ECMA-262 standard compliant browsers 2821 if (!Array.prototype.indexOf) { 2822 Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { 2823 "use strict"; 2824 if (this == null) { 2825 throw new TypeError(); 2826 } 2827 var t = Object(this); 2828 var len = t.length >>> 0; 2829 if (len === 0) { 2830 return -1; 2831 } 2832 var n = 0; 2833 if (arguments.length > 0) { 2834 n = Number(arguments[1]); 2835 if (n != n) { // shortcut for verifying if it's NaN 2836 n = 0; 2837 } else if (n != 0 && n != Infinity && n != -Infinity) { 2838 n = (n > 0 || -1) * Math.floor(Math.abs(n)); 2839 } 2840 } 2841 if (n >= len) { 2842 return -1; 2843 } 2844 var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); 2845 for (; k < len; k++) { 2846 if (k in t && t[k] === searchElement) { 2847 return k; 2848 } 2849 } 2850 return -1; 2851 } 2852 } 2853 2854 // add lastIndexOf to non ECMA-262 standard compliant browsers 2855 if (!Array.prototype.lastIndexOf) { 2856 Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) { 2857 "use strict"; 2858 if (this == null) { 2859 throw new TypeError(); 2860 } 2861 var t = Object(this); 2862 var len = t.length >>> 0; 2863 if (len === 0) { 2864 return -1; 2865 } 2866 var n = len; 2867 if (arguments.length > 1) { 2868 n = Number(arguments[1]); 2869 if (n != n) { 2870 n = 0; 2871 } else if (n != 0 && n != (1 / 0) && n != -(1 / 0)) { 2872 n = (n > 0 || -1) * Math.floor(Math.abs(n)); 2873 } 2874 } 2875 var k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n); 2876 for (; k >= 0; k--) { 2877 if (k in t && t[k] === searchElement) { 2878 return k; 2879 } 2880 } 2881 return -1; 2882 }; 2883 } 2884 2885 var root = this 2886 , $ = root.jQuery || root.Zepto 2887 , i18n = {} 2888 , resStore = {} 2889 , currentLng 2890 , replacementCounter = 0 2891 , languages = [] 2892 , initialized = false; 2893 2894 2895 // Export the i18next object for **CommonJS**. 2896 // If we're not in CommonJS, add `i18n` to the 2897 // global object or to jquery. 2898 if (typeof module !== 'undefined' && module.exports) { 2899 module.exports = i18n; 2900 } else { 2901 if ($) { 2902 $.i18n = $.i18n || i18n; 2903 } 2904 2905 root.i18n = root.i18n || i18n; 2906 } 2907 // defaults 2908 var o = { 2909 lng: undefined, 2910 load: 'all', 2911 preload: [], 2912 lowerCaseLng: false, 2913 returnObjectTrees: false, 2914 fallbackLng: 'dev', 2915 fallbackNS: [], 2916 detectLngQS: 'setLng', 2917 ns: 'translation', 2918 fallbackOnNull: true, 2919 fallbackOnEmpty: false, 2920 fallbackToDefaultNS: false, 2921 nsseparator: ':', 2922 keyseparator: '.', 2923 selectorAttr: 'data-i18n', 2924 debug: false, 2925 2926 resGetPath: 'locales/__lng__/__ns__.json', 2927 resPostPath: 'locales/add/__lng__/__ns__', 2928 2929 getAsync: true, 2930 postAsync: true, 2931 2932 resStore: undefined, 2933 useLocalStorage: false, 2934 localStorageExpirationTime: 7*24*60*60*1000, 2935 2936 dynamicLoad: false, 2937 sendMissing: false, 2938 sendMissingTo: 'fallback', // current | all 2939 sendType: 'POST', 2940 2941 interpolationPrefix: '__', 2942 interpolationSuffix: '__', 2943 reusePrefix: '$t(', 2944 reuseSuffix: ')', 2945 pluralSuffix: '_plural', 2946 pluralNotFound: ['plural_not_found', Math.random()].join(''), 2947 contextNotFound: ['context_not_found', Math.random()].join(''), 2948 escapeInterpolation: false, 2949 2950 setJqueryExt: true, 2951 defaultValueFromContent: true, 2952 useDataAttrOptions: false, 2953 cookieExpirationTime: undefined, 2954 useCookie: true, 2955 cookieName: 'i18next', 2956 cookieDomain: undefined, 2957 2958 objectTreeKeyHandler: undefined, 2959 postProcess: undefined, 2960 parseMissingKey: undefined, 2961 2962 shortcutFunction: 'sprintf' // or: defaultValue 2963 }; 2964 function _extend(target, source) { 2965 if (!source || typeof source === 'function') { 2966 return target; 2967 } 2968 2969 for (var attr in source) { target[attr] = source[attr]; } 2970 return target; 2971 } 2972 2973 function _each(object, callback, args) { 2974 var name, i = 0, 2975 length = object.length, 2976 isObj = length === undefined || typeof object === "function"; 2977 2978 if (args) { 2979 if (isObj) { 2980 for (name in object) { 2981 if (callback.apply(object[name], args) === false) { 2982 break; 2983 } 2984 } 2985 } else { 2986 for ( ; i < length; ) { 2987 if (callback.apply(object[i++], args) === false) { 2988 break; 2989 } 2990 } 2991 } 2992 2993 // A special, fast, case for the most common use of each 2994 } else { 2995 if (isObj) { 2996 for (name in object) { 2997 if (callback.call(object[name], name, object[name]) === false) { 2998 break; 2999 } 3000 } 3001 } else { 3002 for ( ; i < length; ) { 3003 if (callback.call(object[i], i, object[i++]) === false) { 3004 break; 3005 } 3006 } 3007 } 3008 } 3009 3010 return object; 3011 } 3012 3013 var _entityMap = { 3014 "&": "&", 3015 "<": "<", 3016 ">": ">", 3017 '"': '"', 3018 "'": ''', 3019 "/": '/' 3020 }; 3021 3022 function _escape(data) { 3023 if (typeof data === 'string') { 3024 return data.replace(/[&<>
3024"'\/]/g, function (s) { 3025 return _entityMap[s]; 3026 }); 3027 }else{ 3028 return data; 3029 } 3030 } 3031 3032 function _ajax(options) { 3033 3034 // v0.5.0 of https://github.com/goloroden/http.js 3035 var getXhr = function (callback) { 3036 // Use the native XHR object if the browser supports it. 3037 if (window.XMLHttpRequest) { 3038 return callback(null, new XMLHttpRequest()); 3039 } else if (window.ActiveXObject) { 3040 // In Internet Explorer check for ActiveX versions of the XHR object. 3041 try { 3042 return callback(null, new ActiveXObject("Msxml2.XMLHTTP")); 3043 } catch (e) { 3044 return callback(null, new ActiveXObject("Microsoft.XMLHTTP")); 3045 } 3046 } 3047 3048 // If no XHR support was found, throw an error. 3049 return callback(new Error()); 3050 }; 3051 3052 var encodeUsingUrlEncoding = function (data) { 3053 if(typeof data === 'string') { 3054 return data; 3055 } 3056 3057 var result = []; 3058 for(var dataItem in data) { 3059 if(data.hasOwnProperty(dataItem)) { 3060 result.push(encodeURIComponent(dataItem) + '=' + encodeURIComponent(data[dataItem])); 3061 } 3062 } 3063 3064 return result.join('&'); 3065 }; 3066 3067 var utf8 = function (text) { 3068 text = text.replace(/\r\n/g, '\n'); 3069 var result = ''; 3070 3071 for(var i = 0; i < text.length; i++) { 3072 var c = text.charCodeAt(i); 3073 3074 if(c < 128) { 3075 result += String.fromCharCode(c); 3076 } else if((c > 127) && (c < 2048)) { 3077 result += String.fromCharCode((c >> 6) | 192); 3078 result += String.fromCharCode((c & 63) | 128); 3079 } else { 3080 result += String.fromCharCode((c >> 12) | 224); 3081 result += String.fromCharCode(((c >> 6) & 63) | 128); 3082 result += String.fromCharCode((c & 63) | 128); 3083 } 3084 } 3085 3086 return result; 3087 }; 3088 3089 var base64 = function (text) { 3090 var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 3091 3092 text = utf8(text); 3093 var result = '', 3094 chr1, chr2, chr3, 3095 enc1, enc2, enc3, enc4, 3096 i = 0; 3097 3098 do { 3099 chr1 = text.charCodeAt(i++); 3100 chr2 = text.charCodeAt(i++); 3101 chr3 = text.charCodeAt(i++); 3102 3103 enc1 = chr1 >> 2; 3104 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 3105 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 3106 enc4 = chr3 & 63; 3107 3108 if(isNaN(chr2)) { 3109 enc3 = enc4 = 64; 3110 } else if(isNaN(chr3)) { 3111 enc4 = 64; 3112 } 3113 3114 result += 3115 keyStr.charAt(enc1) + 3116 keyStr.charAt(enc2) + 3117 keyStr.charAt(enc3) + 3118 keyStr.charAt(enc4); 3119 chr1 = chr2 = chr3 = ''; 3120 enc1 = enc2 = enc3 = enc4 = ''; 3121 } while(i < text.length); 3122 3123 return result; 3124 }; 3125 3126 var mergeHeaders = function () { 3127 // Use the first header object as base. 3128 var result = arguments[0]; 3129 3130 // Iterate through the remaining header objects and add them. 3131 for(var i = 1; i < arguments.length; i++) { 3132 var currentHeaders = arguments[i]; 3133 for(var header in currentHeaders) { 3134 if(currentHeaders.hasOwnProperty(header)) { 3135 result[header] = currentHeaders[header]; 3136 } 3137 } 3138 } 3139 3140 // Return the merged headers. 3141 return result; 3142 }; 3143 3144 var ajax = function (method, url, options, callback) { 3145 // Adjust parameters. 3146 if(typeof options === 'function') { 3147 callback = options; 3148 options = {}; 3149 } 3150 3151 // Set default parameter values. 3152 options.cache = options.cache || false; 3153 options.data = options.data || {}; 3154 options.headers = options.headers || {}; 3155 options.jsonp = options.jsonp || false; 3156 options.async = options.async === undefined ? true : options.async; 3157 3158 // Merge the various header objects. 3159 var headers = mergeHeaders({ 3160 'accept': '*/*', 3161 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8' 3162 }, ajax.headers, options.headers); 3163 3164 // Encode the data according to the content-type. 3165 var payload; 3166 if (headers['content-type'] === 'application/json') { 3167 payload = JSON.stringify(options.data); 3168 } else { 3169 payload = encodeUsingUrlEncoding(options.data); 3170 } 3171 3172 // Specially prepare GET requests: Setup the query string, handle caching and make a JSONP call 3173 // if neccessary. 3174 if(method === 'GET') { 3175 // Setup the query string. 3176 var queryString = []; 3177 if(payload) { 3178 queryString.push(payload); 3179 payload = null; 3180 } 3181 3182 // Handle caching. 3183 if(!options.cache) { 3184 queryString.push('_=' + (new Date()).getTime()); 3185 } 3186 3187 // If neccessary prepare the query string for a JSONP call. 3188 if(options.jsonp) { 3189 queryString.push('callback=' + options.jsonp); 3190 queryString.push('jsonp=' + options.jsonp); 3191 } 3192 3193 // Merge the query string and attach it to the url. 3194 queryString = queryString.join('&'); 3195 if (queryString.length > 1) { 3196 if (url.indexOf('?') > -1) { 3197 url += '&' + queryString; 3198 } else { 3199 url += '?' + queryString; 3200 } 3201 } 3202 3203 // Make a JSONP call if neccessary. 3204 if(options.jsonp) { 3205 var head = document.getElementsByTagName('head')[0]; 3206 var script = document.createElement('script'); 3207 script.type = 'text/javascript'; 3208 script.src = url; 3209 head.appendChild(script); 3210 return; 3211 } 3212 } 3213 3214 // Since we got here, it is no JSONP request, so make a normal XHR request. 3215 getXhr(function (err, xhr) { 3216 if(err) return callback(err); 3217 3218 // Open the request. 3219 xhr.open(method, url, options.async); 3220 3221 // Set the request headers. 3222 for(var header in headers) { 3223 if(headers.hasOwnProperty(header)) { 3224 xhr.setRequestHeader(header, headers[header]); 3225 } 3226 } 3227 3228 // Handle the request events. 3229 xhr.onreadystatechange = function () { 3230 if(xhr.readyState === 4) { 3231 var data = xhr.responseText || ''; 3232 3233 // If no callback is given, return. 3234 if(!callback) { 3235 return; 3236 } 3237 3238 // Return an object that provides access to the data as text and JSON. 3239 callback(xhr.status, { 3240 text: function () { 3241 return data; 3242 }, 3243 3244 json: function () { 3245 return JSON.parse(data); 3246 } 3247 }); 3248 } 3249 }; 3250 3251 // Actually send the XHR request. 3252 xhr.send(payload); 3253 }); 3254 }; 3255 3256 // Define the external interface. 3257 var http = { 3258 authBasic: function (username, password) { 3259 ajax.headers['Authorization'] = 'Basic ' + base64(username + ':' + password); 3260 }, 3261 3262 connect: function (url, options, callback) { 3263 return ajax('CONNECT', url, options, callback); 3264 }, 3265 3266 del: function (url, options, callback) { 3267 return ajax('DELETE', url, options, callback); 3268 }, 3269 3270 get: function (url, options, callback) { 3271 return ajax('GET', url, options, callback); 3272 }, 3273 3274 head: function (url, options, callback) { 3275 return ajax('HEAD', url, options, callback); 3276 }, 3277 3278 headers: function (headers) { 3279 ajax.headers = headers || {}; 3280 }, 3281 3282 isAllowed: function (url, verb, callback) { 3283 this.options(url, function (status, data) { 3284 callback(data.text().indexOf(verb) !== -1); 3285 }); 3286 }, 3287 3288 options: function (url, options, callback) { 3289 return ajax('OPTIONS', url, options, callback); 3290 }, 3291 3292 patch: function (url, options, callback) { 3293 return ajax('PATCH', url, options, callback); 3294 }, 3295 3296 post: function (url, options, callback) { 3297 return ajax('POST', url, options, callback); 3298 }, 3299 3300 put: function (url, options, callback) { 3301 return ajax('PUT', url, options, callback); 3302 }, 3303 3304 trace: function (url, options, callback) { 3305 return ajax('TRACE', url, options, callback); 3306 } 3307 }; 3308 3309 3310 var methode = options.type ? options.type.toLowerCase() : 'get'; 3311 3312 http[methode](options.url, options, function (status, data) { 3313 if (status === 200) { 3314 options.success(data.json(), status, null); 3315 } else { 3316 options.error(data.text(), status, null); 3317 } 3318 }); 3319 } 3320 3321 var _cookie = { 3322 create: function(name,value,minutes,domain) { 3323 var expires; 3324 if (minutes) { 3325 var date = new Date(); 3326 date.setTime(date.getTime()+(minutes*60*1000)); 3327 expires = "; expires="+date.toGMTString(); 3328 } 3329 else expires = ""; 3330 domain = (domain)? "domain="+domain+";" : ""; 3331 document.cookie = name+"="+value+expires+";"+domain+"path=/"; 3332 }, 3333 3334 read: function(name) { 3335 var nameEQ = name + "="; 3336 var ca = document.cookie.split(';'); 3337 for(var i=0;i < ca.length;i++) { 3338 var c = ca[i];
3339 while (c.charAt(0)==' ') c = c.substring(1,c.length); 3340 if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length,c.length); 3341 } 3342 return null; 3343 }, 3344 3345 remove: function(name) { 3346 this.create(name,"",-1); 3347 } 3348 }; 3349 3350 var cookie_noop = { 3351 create: function(name,value,minutes,domain) {}, 3352 read: function(name) { return null; }, 3353 remove: function(name) {} 3354 }; 3355 3356 3357 3358 // move dependent functions to a container so that 3359 // they can be overriden easier in no jquery environment (node.js) 3360 var f = { 3361 extend: $ ? $.extend : _extend, 3362 each: $ ? $.each : _each, 3363 ajax: $ ? $.ajax : (typeof document !== 'undefined' ? _ajax : function() {}), 3364 cookie: typeof document !== 'undefined' ? _cookie : cookie_noop, 3365 detectLanguage: detectLanguage, 3366 escape: _escape, 3367 log: function(str) { 3368 if (o.debug && typeof console !== "undefined") console.log(str); 3369 }, 3370 toLanguages: function(lng) { 3371 var languages = []; 3372 if (typeof lng === 'string' && lng.indexOf('-') > -1) { 3373 var parts = lng.split('-'); 3374 3375 lng = o.lowerCaseLng ? 3376 parts[0].toLowerCase() + '-' + parts[1].toLowerCase() : 3377 parts[0].toLowerCase() + '-' + parts[1].toUpperCase(); 3378 3379 if (o.load !== 'unspecific') languages.push(lng); 3380 if (o.load !== 'current') languages.push(parts[0]); 3381 } else { 3382 languages.push(lng); 3383 } 3384 3385 if (languages.indexOf(o.fallbackLng) === -1 && o.fallbackLng) languages.push(o.fallbackLng); 3386 3387 return languages; 3388 }, 3389 regexEscape: function(str) { 3390 return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); 3391 } 3392 }; 3393 function init(options, cb) { 3394 3395 if (typeof options === 'function') { 3396 cb = options; 3397 options = {}; 3398 } 3399 options = options || {}; 3400 3401 // override defaults with passed in options 3402 f.extend(o, options); 3403 delete o.fixLng; /* passed in each time */ 3404 3405 // create namespace object if namespace is passed in as string 3406 if (typeof o.ns == 'string') { 3407 o.ns = { namespaces: [o.ns], defaultNs: o.ns}; 3408 } 3409 3410 // fallback namespaces 3411 if (typeof o.fallbackNS == 'string') { 3412 o.fallbackNS = [o.fallbackNS]; 3413 } 3414 3415 // escape prefix/suffix 3416 o.interpolationPrefixEscaped = f.regexEscape(o.interpolationPrefix); 3417 o.interpolationSuffixEscaped = f.regexEscape(o.interpolationSuffix); 3418 3419 if (!o.lng) o.lng = f.detectLanguage(); 3420 if (o.lng) { 3421 // set cookie with lng set (as detectLanguage will set cookie on need) 3422 if (o.useCookie) f.cookie.create(o.cookieName, o.lng, o.cookieExpirationTime, o.cookieDomain); 3423 } else { 3424 o.lng = o.fallbackLng; 3425 if (o.useCookie) f.cookie.remove(o.cookieName); 3426 } 3427 3428 languages = f.toLanguages(o.lng); 3429 currentLng = languages[0]; 3430 f.log('currentLng set to: ' + currentLng); 3431 3432 var lngTranslate = translate; 3433 if (options.fixLng) { 3434 lngTranslate = function(key, options) { 3435 options = options || {}; 3436 options.lng = options.lng || lngTranslate.lng; 3437 return translate(key, options); 3438 }; 3439 lngTranslate.lng = currentLng; 3440 } 3441 3442 pluralExtensions.setCurrentLng(currentLng); 3443 3444 // add JQuery extensions 3445 if ($ && o.setJqueryExt) addJqueryFunct(); 3446 3447 // jQuery deferred 3448 var deferred; 3449 if ($ && $.Deferred) { 3450 deferred = $.Deferred(); 3451 } 3452 3453 // return immidiatly if res are passed in 3454 if (o.resStore) { 3455 resStore = o.resStore; 3456 initialized = true; 3457 if (cb) cb(lngTranslate); 3458 if (deferred) deferred.resolve(lngTranslate); 3459 if (deferred) return deferred.promise(); 3460 return; 3461 } 3462 3463 // languages to load 3464 var lngsToLoad = f.toLanguages(o.lng); 3465 if (typeof o.preload === 'string') o.preload = [o.preload]; 3466 for (var i = 0, l = o.preload.length; i < l; i++) { 3467 var pres = f.toLanguages(o.preload[i]); 3468 for (var y = 0, len = pres.length; y < len; y++) { 3469 if (lngsToLoad.indexOf(pres[y]) < 0) { 3470 lngsToLoad.push(pres[y]); 3471 } 3472 } 3473 } 3474 3475 // else load them 3476 i18n.sync.load(lngsToLoad, o, function(err, store) { 3477 resStore = store; 3478 initialized = true; 3479 3480 if (cb) cb(lngTranslate); 3481 if (deferred) deferred.resolve(lngTranslate); 3482 }); 3483 3484 if (deferred) return deferred.promise(); 3485 } 3486 function preload(lngs, cb) { 3487 if (typeof lngs === 'string') lngs = [lngs]; 3488 for (var i = 0, l = lngs.length; i < l; i++) { 3489 if (o.preload.indexOf(lngs[i]) < 0) { 3490 o.preload.push(lngs[i]); 3491 } 3492 } 3493 return init(cb); 3494 } 3495 3496 function addResourceBundle(lng, ns, resources) { 3497 if (typeof ns !== 'string') { 3498 resources = ns; 3499 ns = o.ns.defaultNs; 3500 } else if (o.ns.namespaces.indexOf(ns) < 0) { 3501 o.ns.namespaces.push(ns); 3502 } 3503 3504 resStore[lng] = resStore[lng] || {}; 3505 resStore[lng][ns] = resStore[lng][ns] || {}; 3506 3507 f.extend(resStore[lng][ns], resources); 3508 } 3509 3510 function removeResourceBundle(lng, ns) { 3511 if (typeof ns !== 'string') { 3512 ns = o.ns.defaultNs; 3513 } 3514 3515 resStore[lng] = resStore[lng] || {}; 3516 resStore[lng][ns] = {}; 3517 } 3518 3519 function setDefaultNamespace(ns) { 3520 o.ns.defaultNs = ns; 3521 } 3522 3523 function loadNamespace(namespace, cb) { 3524 loadNamespaces([namespace], cb); 3525 } 3526 3527 function loadNamespaces(namespaces, cb) { 3528 var opts = { 3529 dynamicLoad: o.dynamicLoad, 3530 resGetPath: o.resGetPath, 3531 getAsync: o.getAsync, 3532 customLoad: o.customLoad, 3533 ns: { namespaces: namespaces, defaultNs: ''} /* new namespaces to load */ 3534 }; 3535 3536 // languages to load 3537 var lngsToLoad = f.toLanguages(o.lng); 3538 if (typeof o.preload === 'string') o.preload = [o.preload]; 3539 for (var i = 0, l = o.preload.length; i < l; i++) { 3540 var pres = f.toLanguages(o.preload[i]); 3541 for (var y = 0, len = pres.length; y < len; y++) { 3542 if (lngsToLoad.indexOf(pres[y]) < 0) { 3543 lngsToLoad.push(pres[y]); 3544 } 3545 } 3546 } 3547 3548 // check if we have to load 3549 var lngNeedLoad = []; 3550 for (var a = 0, lenA = lngsToLoad.length; a < lenA; a++) { 3551 var needLoad = false; 3552 var resSet = resStore[lngsToLoad[a]]; 3553 if (resSet) { 3554 for (var b = 0, lenB = namespaces.length; b < lenB; b++) { 3555 if (!resSet[namespaces[b]]) needLoad = true; 3556 } 3557 } else { 3558 needLoad = true; 3559 } 3560 3561 if (needLoad) lngNeedLoad.push(lngsToLoad[a]); 3562 } 3563 3564 if (lngNeedLoad.length) { 3565 i18n.sync._fetch(lngNeedLoad, opts, function(err, store) { 3566 var todo = namespaces.length * lngNeedLoad.length; 3567 3568 // load each file individual 3569 f.each(namespaces, function(nsIndex, nsValue) { 3570 3571 // append namespace to namespace array 3572 if (o.ns.namespaces.indexOf(nsValue) < 0) { 3573 o.ns.namespaces.push(nsValue); 3574 } 3575 3576 f.each(lngNeedLoad, function(lngIndex, lngValue) { 3577 resStore[lngValue] = resStore[lngValue] || {}; 3578 resStore[lngValue][nsValue] = store[lngValue][nsValue]; 3579 3580 todo--; // wait for all done befor callback 3581 if (todo === 0 && cb) { 3582 if (o.useLocalStorage) i18n.sync._storeLocal(resStore); 3583 cb(); 3584 } 3585 }); 3586 }); 3587 }); 3588 } else { 3589 if (cb) cb(); 3590 } 3591 } 3592 3593 function setLng(lng, options, cb) { 3594 if (typeof options === 'function') { 3595 cb = options; 3596 options = {}; 3597 } else if (!options) { 3598 options = {}; 3599 } 3600 3601 options.lng = lng; 3602 return init(options, cb); 3603 } 3604 3605 function lng() { 3606 return currentLng; 3607 } 3608 function addJqueryFunct() { 3609 // $.t shortcut 3610 $.t = $.t || translate; 3611 3612 function parse(ele, key, options) { 3613 if (key.length === 0) return; 3614 3615 var attr = 'text'; 3616 3617 if (key.indexOf('[') === 0) { 3618 var parts = key.split(']'); 3619 key = parts[1]; 3620 attr = parts[0].substr(1, parts[0].length-1); 3621 } 3622 3623 if (key.indexOf(';') === key.length-1) { 3624 key = key.substr(0, key.length-2); 3625 } 3626 3627 var optionsToUse; 3628 if (attr === 'html') { 3629 optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options; 3630 ele.html($.t(key, optionsToUse)); 3631 } else if (attr === 'text') { 3632 optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.text() }, options) : options; 3633 ele.text($.t(key, optionsToUse)); 3634 } else if (attr === 'prepend') { 3635 optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options; 3636 ele.prepend($.t(key, optionsToUse)); 3637 } else if (attr === 'append') { 3638 optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options; 3639 ele.append($.t(key, optionsToUse)); 3640 } else { 3641 optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.attr(attr) }, options) : options; 3642 ele.attr(attr, $.t(key, optionsToUse)); 3643 } 3644 } 3645 3646 function localize(ele, options) { 3647 var key = ele.attr(o.selectorAttr); 3648 if (!key && typeof key !== 'undefined' && key !== false) key = ele.text() || ele.val(); 3649 if (!key) return; 3650 3651 var target = ele 3652 , targetSelector = ele.data("i18n-target");
3653 if (targetSelector) { 3654 target = ele.find(targetSelector) || ele; 3655 } 3656 3657 if (!options && o.useDataAttrOptions === true) { 3658 options = ele.data("i18n-options"); 3659 } 3660 options = options || {}; 3661 3662 if (key.indexOf(';') >= 0) { 3663 var keys = key.split(';'); 3664 3665 $.each(keys, function(m, k) { 3666 if (k !== '') parse(target, k, options); 3667 }); 3668 3669 } else { 3670 parse(target, key, options); 3671 } 3672 3673 if (o.useDataAttrOptions === true) ele.data("i18n-options", options); 3674 } 3675 3676 // fn 3677 $.fn.i18n = function (options) { 3678 return this.each(function() { 3679 // localize element itself 3680 localize($(this), options); 3681 3682 // localize childs 3683 var elements = $(this).find('[' + o.selectorAttr + ']'); 3684 elements.each(function() { 3685 localize($(this), options); 3686 }); 3687 }); 3688 }; 3689 } 3690 function applyReplacement(str, replacementHash, nestedKey, options) { 3691 if (!str) return str; 3692 3693 options = options || replacementHash;
3693 // first call uses replacement hash combined with options 3694 if (str.indexOf(options.interpolationPrefix || o.interpolationPrefix) < 0) return str; 3695 3696 var prefix = options.interpolationPrefix ? f.regexEscape(options.interpolationPrefix) : o.interpolationPrefixEscaped 3697 , suffix = options.interpolationSuffix ? f.regexEscape(options.interpolationSuffix) : o.interpolationSuffixEscaped 3698 , unEscapingSuffix = 'HTML'+suffix; 3699 3700 f.each(replacementHash, function(key, value) { 3701 var nextKey = nestedKey ? nestedKey + o.keyseparator + key : key; 3702 if (typeof value === 'object' && value !== null) { 3703 str = applyReplacement(str, value, nextKey, options); 3704 } else { 3705 if (options.escapeInterpolation || o.escapeInterpolation) { 3706 str = str.replace(new RegExp([prefix, nextKey, unEscapingSuffix].join(''), 'g'), value); 3707 str = str.replace(new RegExp([prefix, nextKey, suffix].join(''), 'g'), f.escape(value)); 3708 } else { 3709 str = str.replace(new RegExp([prefix, nextKey, suffix].join(''), 'g'), value); 3710 } 3711 // str = options.escapeInterpolation; 3712 } 3713 }); 3714 return str; 3715 } 3716 3717 // append it to functions 3718 f.applyReplacement = applyReplacement; 3719 3720 function applyReuse(translated, options) { 3721 var comma = ','; 3722 var options_open = '{'; 3723 var options_close = '}'; 3724 3725 var opts = f.extend({}, options); 3726 delete opts.postProcess; 3727 3728 while (translated.indexOf(o.reusePrefix) != -1) { 3729 replacementCounter++; 3730 if (replacementCounter > o.maxRecursion) { break; } // safety net for too much recursion 3731 var index_of_opening = translated.lastIndexOf(o.reusePrefix); 3732 var index_of_end_of_closing = translated.indexOf(o.reuseSuffix, index_of_opening) + o.reuseSuffix.length; 3733 var token = translated.substring(index_of_opening, index_of_end_of_closing); 3734 var token_without_symbols = token.replace(o.reusePrefix, '').replace(o.reuseSuffix, ''); 3735 3736 3737 if (token_without_symbols.indexOf(comma) != -1) { 3738 var index_of_token_end_of_closing = token_without_symbols.indexOf(comma); 3739 if (token_without_symbols.indexOf(options_open, index_of_token_end_of_closing) != -1 && token_without_symbols.indexOf(options_close, index_of_token_end_of_closing) != -1) { 3740 var index_of_opts_opening = token_without_symbols.indexOf(options_open, index_of_token_end_of_closing); 3741 var index_of_opts_end_of_closing = token_without_symbols.indexOf(options_close, index_of_opts_opening) + options_close.length; 3742 try { 3743 opts = f.extend(opts, JSON.parse(token_without_symbols.substring(index_of_opts_opening, index_of_opts_end_of_closing))); 3744 token_without_symbols = token_without_symbols.substring(0, index_of_token_end_of_closing); 3745 } catch (e) { 3746 } 3747 } 3748 } 3749 3750 var translated_token = _translate(token_without_symbols, opts); 3751 translated = translated.replace(token, translated_token); 3752 } 3753 return translated; 3754 } 3755 3756 function hasContext(options) { 3757 return (options.context && (typeof options.context == 'string' || typeof options.context == 'number')); 3758 } 3759 3760 function needsPlural(options) { 3761 return (options.count !== undefined && typeof options.count != 'string' && options.count !== 1); 3762 } 3763 3764 function exists(key, options) { 3765 options = options || {}; 3766 3767 var notFound = _getDefaultValue(key, options) 3768 , found = _find(key, options); 3769 3770 return found !== undefined || found === notFound; 3771 } 3772 3773 function translate(key, options) { 3774 options = options || {}; 3775 3776 if (!initialized) { 3777 f.log('i18next not finished initialization. you might have called t function before loading resources finished.') 3778 return options.defaultValue || ''; 3779 }; 3780 replacementCounter = 0; 3781 return _translate.apply(null, arguments); 3782 } 3783 3784 function _getDefaultValue(key, options) { 3785 return (options.defaultValue !== undefined) ? options.defaultValue : key; 3786 } 3787 3788 function _injectSprintfProcessor() { 3789 3790 var values = []; 3791 3792 // mh: build array from second argument onwards 3793 for (var i = 1; i < arguments.length; i++) { 3794 values.push(arguments[i]); 3795 } 3796 3797 return { 3798 postProcess: 'sprintf', 3799 sprintf: values 3800 }; 3801 } 3802 3803 function _translate(potentialKeys, options) { 3804 if (typeof options == 'string') { 3805 if (o.shortcutFunction === 'sprintf') { 3806 // mh: gettext like sprintf syntax found, automatically create sprintf processor 3807 options = _injectSprintfProcessor.apply(null, arguments); 3808 } else if (o.shortcutFunction === 'defaultValue') { 3809 options = { 3810 defaultValue: options 3811 } 3812 } 3813 } else { 3814 options = options || {}; 3815 } 3816 3817 if (potentialKeys === undefined || potentialKeys === null) return ''; 3818 3819 if (typeof potentialKeys == 'string') { 3820 potentialKeys = [potentialKeys]; 3821 } 3822 3823 var key = potentialKeys[0]; 3824 3825 if (potentialKeys.length > 1) { 3826 for (var i = 0; i < potentialKeys.length; i++) { 3827 key = potentialKeys[i]; 3828 if (exists(key)) { 3829 break; 3830 } 3831 } 3832 } 3833 3834 var notFound = _getDefaultValue(key, options) 3835 , found = _find(key, options) 3836 , lngs = options.lng ? f.toLanguages(options.lng) : languages 3837 , ns = options.ns || o.ns.defaultNs 3838 , parts; 3839 3840 // split ns and key 3841 if (key.indexOf(o.nsseparator) > -1) { 3842 parts = key.split(o.nsseparator); 3843 ns = parts[0]; 3844 key = parts[1]; 3845 } 3846 3847 if (found === undefined && o.sendMissing) { 3848 if (options.lng) { 3849 sync.postMissing(lngs[0], ns, key, notFound, lngs); 3850 } else { 3851 sync.postMissing(o.lng, ns, key, notFound, lngs); 3852 } 3853 } 3854 3855 var postProcessor = options.postProcess || o.postProcess; 3856 if (found !== undefined && postProcessor) { 3857 if (postProcessors[postProcessor]) { 3858 found = postProcessors[postProcessor](found, key, options); 3859 } 3860 } 3861 3862 // process notFound if function exists 3863 var splitNotFound = notFound; 3864 if (notFound.indexOf(o.nsseparator) > -1) { 3865 parts = notFound.split(o.nsseparator); 3866 splitNotFound = parts[1]; 3867 } 3868 if (splitNotFound === key && o.parseMissingKey) { 3869 notFound = o.parseMissingKey(notFound); 3870 } 3871 3872 if (found === undefined) { 3873 notFound = applyReplacement(notFound, options); 3874 notFound = applyReuse(notFound, options); 3875 3876 if (postProcessor && postProcessors[postProcessor]) { 3877 var val = _getDefaultValue(key, options); 3878 found = postProcessors[postProcessor](val, key, options); 3879 } 3880 } 3881 3882 return (found !== undefined) ? found : notFound; 3883 } 3884 3885 function _find(key, options) { 3886 options = options || {}; 3887 3888 var optionWithoutCount, translated 3889 , notFound = _getDefaultValue(key, options) 3890 , lngs = languages;
3891 3892 if (!resStore) { return notFound; } // no resStore to translate from 3893 3894 if (options.lng) { 3895 lngs = f.toLanguages(options.lng); 3896 3897 if (!resStore[lngs[0]]) { 3898 var oldAsync = o.getAsync; 3899 o.getAsync = false; 3900 3901 i18n.sync.load(lngs, o, function(err, store) { 3902 f.extend(resStore, store); 3903 o.getAsync = oldAsync; 3904 }); 3905 } 3906 } 3907 3908 var ns = options.ns || o.ns.defaultNs; 3909 if (key.indexOf(o.nsseparator) > -1) { 3910 var parts = key.split(o.nsseparator); 3911 ns = parts[0]; 3912 key = parts[1]; 3913 } 3914 3915 if (hasContext(options)) { 3916 optionWithoutCount = f.extend({}, options); 3917 delete optionWithoutCount.context; 3918 optionWithoutCount.defaultValue = o.contextNotFound; 3919 3920 var contextKey = ns + o.nsseparator + key + '_' + options.context; 3921 3922 translated = translate(contextKey, optionWithoutCount); 3923 if (translated != o.contextNotFound) { 3924 return applyReplacement(translated, { context: options.context }); // apply replacement for context only 3925 } // else continue translation with original/nonContext key 3926 } 3927 3928 if (needsPlural(options)) { 3929 optionWithoutCount = f.extend({}, options); 3930 delete optionWithoutCount.count; 3931 optionWithoutCount.defaultValue = o.pluralNotFound; 3932 3933 var pluralKey = ns + o.nsseparator + key + o.pluralSuffix; 3934 var pluralExtension = pluralExtensions.get(lngs[0], options.count); 3935 if (pluralExtension >= 0) { 3936 pluralKey = pluralKey + '_' + pluralExtension; 3937 } else if (pluralExtension === 1) { 3938 pluralKey = ns + o.nsseparator + key; // singular 3939 } 3940 3941 translated = translate(pluralKey, optionWithoutCount); 3942 if (translated != o.pluralNotFound) { 3943 return applyReplacement(translated, { 3944 count: options.count, 3945 interpolationPrefix: options.interpolationPrefix, 3946 interpolationSuffix: options.interpolationSuffix 3947 }); // apply replacement for count only 3948 } // else continue translation with original/singular key 3949 } 3950 3951 var found; 3952 var keys = key.split(o.keyseparator); 3953 for (var i = 0, len = lngs.length; i < len; i++ ) { 3954 if (found !== undefined) break; 3955 3956 var l = lngs[i]; 3957 3958 var x = 0; 3959 var value = resStore[l] && resStore[l][ns]; 3960 while (keys[x]) { 3961 value = value && value[keys[x]]; 3962 x++; 3963 } 3964 if (value !== undefined) { 3965 var valueType = Object.prototype.toString.apply(value); 3966 if (typeof value === 'string') { 3967 value = applyReplacement(value, options); 3968 value = applyReuse(value, options); 3969 } else if (valueType === '[object Array]' && !o.returnObjectTrees && !options.returnObjectTrees) { 3970 value = value.join('\n'); 3971 value = applyReplacement(value, options); 3972 value = applyReuse(value, options); 3973 } else if (value === null && o.fallbackOnNull === true) { 3974 value = undefined; 3975 } else if (value !== null) { 3976 if (!o.returnObjectTrees && !options.returnObjectTrees) { 3977 if (o.objectTreeKeyHandler && typeof o.objectTreeKeyHandler == 'function') { 3978 value = o.objectTreeKeyHandler(key, value, l, ns, options); 3979 } else { 3980 value = 'key \'' + ns + ':' + key + ' (' + l + ')\' ' + 3981 'returned an object instead of string.'; 3982 f.log(value); 3983 } 3984 } else if (valueType !== '[object Number]' && valueType !== '[object Function]' && valueType !== '[object RegExp]') { 3985 var copy = (valueType === '[object Array]') ? [] : {}; // apply child translation on a copy 3986 f.each(value, function(m) { 3987 copy[m] = _translate(ns + o.nsseparator + key + o.keyseparator + m, options); 3988 }); 3989 value = copy; 3990 } 3991 } 3992 3993 if (typeof value === 'string' && value.trim() === '' && o.fallbackOnEmpty === true) 3994 value = undefined; 3995 3996 found = value; 3997 } 3998 } 3999 4000 if (found === undefined && !options.isFallbackLookup && (
4000o.fallbackToDefaultNS === true || (o.fallbackNS && o.fallbackNS.length > 0))) { 4001 // set flag for fallback lookup - avoid recursion 4002 options.isFallbackLookup = true; 4003 4004 if (o.fallbackNS.length) { 4005 4006 for (var y = 0, lenY = o.fallbackNS.length; y < lenY; y++) { 4007 found = _find(o.fallbackNS[y] + o.nsseparator + key, options); 4008 4009 if (found) { 4010 /* compare value without namespace */ 4011 var foundValue = found.indexOf(o.nsseparator) > -1 ? found.split(o.nsseparator)[1] : found 4012 , notFoundValue = notFound.indexOf(o.nsseparator) > -1 ? notFound.split(o.nsseparator)[1] : notFound; 4013 4014 if (foundValue !== notFoundValue) break; 4015 } 4016 } 4017 } else { 4018 found = _find(key, options); // fallback to default NS 4019 } 4020 } 4021 4022 return found; 4023 } 4024 function detectLanguage() { 4025 var detectedLng; 4026 4027 // get from qs 4028 var qsParm = []; 4029 if (typeof window !== 'undefined') { 4030 (function() { 4031 var query = window.location.search.substring(1); 4032 var parms = query.split('&'); 4033 for (var i=0; i<parms.length; i++) { 4034 var pos = parms[i].indexOf('='); 4035 if (pos > 0) { 4036 var key = parms[i].substring(0,pos); 4037 var val = parms[i].substring(pos+1); 4038 qsParm[key] = val; 4039 } 4040 } 4041 })(); 4042 if (qsParm[o.detectLngQS]) { 4043 detectedLng = qsParm[o.detectLngQS]; 4044 } 4045 } 4046 4047 // get from cookie 4048 if (!detectedLng && typeof document !== 'undefined' && o.useCookie ) { 4049 var c = f.cookie.read(o.cookieName); 4050 if (c) detectedLng = c; 4051 } 4052 4053 // get from navigator 4054 if (!detectedLng && typeof navigator !== 'undefined') { 4055 detectedLng = (navigator.language) ? navigator.language : navigator.userLanguage; 4056 } 4057 4058 return detectedLng; 4059 } 4060 var sync = { 4061 4062 load: function(lngs, options, cb) { 4063 if (options.useLocalStorage) { 4064 sync._loadLocal(lngs, options, function(err, store) { 4065 var missingLngs = []; 4066 for (var i = 0, len = lngs.length; i < len; i++) { 4067 if (!store[lngs[i]]) missingLngs.push(lngs[i]); 4068 } 4069 4070 if (missingLngs.length > 0) { 4071 sync._fetch(missingLngs, options, function(err, fetched) { 4072 f.extend(store, fetched); 4073 sync._storeLocal(fetched); 4074 4075 cb(null, store); 4076 }); 4077 } else { 4078 cb(null, store); 4079 } 4080 }); 4081 } else { 4082 sync._fetch(lngs, options, function(err, store){ 4083 cb(null, store); 4084 }); 4085 } 4086 }, 4087 4088 _loadLocal: function(lngs, options, cb) { 4089 var store = {} 4090 , nowMS = new Date().getTime(); 4091 4092 if(window.localStorage) { 4093 4094 var todo = lngs.length; 4095 4096 f.each(lngs, function(key, lng) { 4097 var local = window.localStorage.getItem('res_' + lng); 4098 4099 if (local) { 4100 local = JSON.parse(local); 4101 4102 if (local.i18nStamp && local.i18nStamp + options.localStorageExpirationTime > nowMS) { 4103 store[lng] = local; 4104 } 4105 } 4106 4107 todo--; // wait for all done befor callback 4108 if (todo === 0) cb(null, store); 4109 }); 4110 } 4111 }, 4112 4113 _storeLocal: function(store) { 4114 if(window.localStorage) { 4115 for (var m in store) { 4116 store[m].i18nStamp = new Date().getTime(); 4117 window.localStorage.setItem('res_' + m, JSON.stringify(store[m])); 4118 } 4119 } 4120 return; 4121 }, 4122 4123 _fetch: function(lngs, options, cb) { 4124 var ns = options.ns 4125 , store = {}; 4126 4127 if (!options.dynamicLoad) { 4128 var todo = ns.namespaces.length * lngs.length 4129 , errors; 4130 4131 // load each file individual 4132 f.each(ns.namespaces, function(nsIndex, nsValue) { 4133 f.each(lngs, function(lngIndex, lngValue) { 4134 4135 // Call this once our translation has returned. 4136 var loadComplete = function(err, data) { 4137 if (err) { 4138 errors = errors || []; 4139 errors.push(err); 4140 } 4141 store[lngValue] = store[lngValue] || {}; 4142 store[lngValue][nsValue] = data; 4143 4144 todo--; // wait for all done befor callback 4145 if (todo === 0) cb(errors, store); 4146 }; 4147 4148 if(typeof options.customLoad == 'function'){ 4149 // Use the specified custom callback. 4150 options.customLoad(lngValue, nsValue, options, loadComplete); 4151 } else { 4152 //~ // Use our inbuilt sync. 4153 sync._fetchOne(lngValue, nsValue, options, loadComplete); 4154 } 4155 }); 4156 }); 4157 } else { 4158 // Call this once our translation has returned. 4159 var loadComplete = function(err, data) { 4160 cb(null, data); 4161 }; 4162 4163 if(typeof options.customLoad == 'function'){ 4164 // Use the specified custom callback. 4165 options.customLoad(lngs, ns.namespaces, options, loadComplete); 4166 } else { 4167 var url = applyReplacement(options.resGetPath, { lng: lngs.join('+'), ns: ns.namespaces.join('+') });
4168 // load all needed stuff once 4169 f.ajax({ 4170 url: url, 4171 success: function(data, status, xhr) { 4172 f.log('loaded: ' + url); 4173 loadComplete(null, data); 4174 }, 4175 error : function(xhr, status, error) { 4176 f.log('failed loading: ' + url); 4177 loadComplete('failed loading resource.json error: ' + error); 4178 }, 4179 dataType: "json", 4180 async : options.getAsync 4181 }); 4182 } 4183 } 4184 }, 4185 4186 _fetchOne: function(lng, ns, options, done) { 4187 var url = applyReplacement(options.resGetPath, { lng: lng, ns: ns }); 4188 f.ajax({ 4189 url: url, 4190 success: function(data, status, xhr) { 4191 f.log('loaded: ' + url); 4192 done(null, data); 4193 }, 4194 error : function(xhr, status, error) { 4195 if (error.status == 200) { 4196 // file loaded but invalid json, stop waste time ! 4197 f.log('There is a typo in: ' + url); 4198 } else if (error.status == 404) { 4199 f.log('Does not exist: ' + url); 4200 } else { 4201 f.log(error.status + ' when loading ' + url); 4202 } 4203 4204 done(error, {}); 4205 }, 4206 dataType: "json", 4207 async : options.getAsync 4208 }); 4209 }, 4210 4211 postMissing: function(lng, ns, key, defaultValue, lngs) { 4212 var payload = {}; 4213 payload[key] = defaultValue; 4214 4215 var urls = []; 4216 4217 if (o.sendMissingTo === 'fallback' && o.fallbackLng !== false) { 4218 urls.push({lng: o.fallbackLng, url: applyReplacement(o.resPostPath, { lng: o.fallbackLng, ns: ns })}); 4219 } else if (o.sendMissingTo === 'current' || (o.sendMissingTo === 'fallback' && o.fallbackLng === false) ) { 4220 urls.push({lng: lng, url: applyReplacement(o.resPostPath, { lng: lng, ns: ns })}); 4221 } else if (o.sendMissingTo === 'all') { 4222 for (var i = 0, l = lngs.length; i < l; i++) { 4223 urls.push({lng: lngs[i], url: applyReplacement(o.resPostPath, { lng: lngs[i], ns: ns })}); 4224 } 4225 } 4226 4227 for (var y = 0, len = urls.length; y < len; y++) { 4228 var item = urls[y]; 4229 f.ajax({ 4230 url: item.url, 4231 type: o.sendType, 4232 data: payload, 4233 success: function(data, status, xhr) { 4234 f.log('posted missing key \'' + key + '\' to: ' + item.url); 4235 4236 // add key to resStore 4237 var keys = key.split('.'); 4238 var x = 0; 4239 var value = resStore[item.lng][ns]; 4240 while (keys[x]) { 4241 if (x === keys.length - 1) { 4242 value = value[keys[x]] = defaultValue; 4243 } else { 4244 value = value[keys[x]] = value[keys[x]] || {}; 4245 } 4246 x++; 4247 } 4248 }, 4249 error : function(xhr, status, error) { 4250 f.log('failed posting missing key \'' + key + '\' to: ' + item.url); 4251 }, 4252 dataType: "json", 4253 async : o.postAsync 4254 }); 4255 } 4256 } 4257 }; 4258 // definition http://translate.sourceforge.net/wiki/l10n/pluralforms 4259 var pluralExtensions = { 4260 4261 rules: { 4262 "ach": { 4263 "name": "Acholi", 4264 "numbers": [ 4265 1, 4266 2 4267 ], 4268 "plurals": function(n) { return Number(n > 1); } 4269 }, 4270 "af": { 4271 "name": "Afrikaans", 4272 "numbers": [ 4273 1, 4274 2 4275 ], 4276 "plurals": function(n) { return Number(n != 1); } 4277 }, 4278 "ak": { 4279 "name": "Akan", 4280 "numbers": [ 4281 1, 4282 2 4283 ], 4284 "plurals": function(n) { return Number(n > 1); } 4285 }, 4286 "am": { 4287 "name": "Amharic", 4288 "numbers": [ 4289 1, 4290 2 4291 ], 4292 "plurals": function(n) { return Number(n > 1); } 4293 }, 4294 "an": { 4295 "name": "Aragonese", 4296 "numbers": [ 4297 1, 4298 2 4299 ], 4300 "plurals": function(n) { return Number(n != 1); } 4301 }, 4302 "ar": { 4303 "name": "Arabic", 4304 "numbers": [ 4305 0, 4306 1, 4307 2, 4308 3, 4309 11, 4310 100 4311 ], 4312 "plurals": function(n) { return Number(n===0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5); } 4313 }, 4314 "arn": { 4315 "name": "Mapudungun", 4316 "numbers": [ 4317 1, 4318 2 4319 ], 4320 "plurals": function(n) { return Number(n > 1); } 4321 }, 4322 "ast": { 4323 "name": "Asturian", 4324 "numbers": [ 4325 1, 4326 2 4327 ], 4328 "plurals": function(n) { return Number(n != 1); } 4329 }, 4330 "ay": { 4331 "name": "Aymar\u00e1", 4332 "numbers": [ 4333 1 4334 ], 4335 "plurals": function(n) { return 0; } 4336 }, 4337 "az": { 4338 "name": "Azerbaijani", 4339 "numbers": [ 4340 1, 4341 2 4342 ], 4343 "plurals": function(n) { return Number(n != 1); } 4344 }, 4345 "be": { 4346 "name": "Belarusian", 4347 "numbers": [ 4348 1, 4349 2, 4350 5 4351 ], 4352 "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } 4353 }, 4354 "bg": { 4355 "name": "Bulgarian", 4356 "numbers": [ 4357 1, 4358 2 4359 ], 4360 "plurals": function(n) { return Number(n != 1); } 4361 }, 4362 "bn": { 4363 "name": "Bengali", 4364 "numbers": [ 4365 1, 4366 2 4367 ], 4368 "plurals": function(n) { return Number(n != 1); } 4369 }, 4370 "bo": { 4371 "name": "Tibetan", 4372 "numbers": [ 4373 1 4374 ], 4375 "plurals": function(n) { return 0; } 4376 }, 4377 "br": { 4378 "name": "Breton", 4379 "numbers": [ 4380 1, 4381 2 4382 ], 4383 "plurals": function(n) { return Number(n > 1); } 4384 }, 4385 "bs": { 4386 "name": "Bosnian", 4387 "numbers": [ 4388 1, 4389 2, 4390 5 4391 ], 4392 "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } 4393 }, 4394 "ca": { 4395 "name": "Catalan", 4396 "numbers": [ 4397 1, 4398 2 4399 ], 4400 "plurals": function(n) { return Number(n != 1); } 4401 }, 4402 "cgg": { 4403 "name": "Chiga", 4404 "numbers": [ 4405 1 4406 ], 4407 "plurals": function(n) { return 0; } 4408 }, 4409 "cs": { 4410 "name": "Czech", 4411 "numbers": [ 4412 1, 4413 2, 4414 5 4415 ], 4416 "plurals": function(n) { return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2); } 4417 }, 4418 "csb": { 4419 "name": "Kashubian", 4420 "numbers": [ 4421 1, 4422 2, 4423 5 4424 ], 4425 "plurals": function(n) { return Number(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } 4426 }, 4427 "cy": { 4428 "name": "Welsh", 4429 "numbers": [ 4430 1, 4431 2, 4432 3, 4433 8 4434 ], 4435 "plurals": function(n) { return Number((n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3); } 4436 }, 4437 "da": { 4438 "name": "Danish", 4439 "numbers": [ 4440 1, 4441 2 4442 ], 4443 "plurals": function(n) { return Number(n != 1); } 4444 }, 4445 "de": { 4446 "name": "German", 4447 "numbers": [ 4448 1, 4449 2 4450 ], 4451 "plurals": function(n) { return Number(n != 1); } 4452 }, 4453 "dz": { 4454 "name": "Dzongkha", 4455 "numbers": [ 4456 1 4457 ], 4458 "plurals": function(n) { return 0; } 4459 }, 4460 "el": { 4461 "name": "Greek", 4462 "numbers": [ 4463 1, 4464 2 4465 ], 4466 "plurals": function(n) { return Number(n != 1); } 4467 }, 4468 "en": { 4469 "name": "English", 4470 "numbers": [ 4471 1, 4472 2 4473 ], 4474 "plurals": function(n) { return Number(n != 1); } 4475 }, 4476 "eo": { 4477 "name": "Esperanto", 4478 "numbers": [ 4479 1, 4480 2 4481 ], 4482 "plurals": function(n) { return Number(n != 1); } 4483 }, 4484 "es": { 4485 "name": "Spanish", 4486 "numbers": [ 4487 1, 4488 2 4489 ], 4490 "plurals": function(n) { return Number(n != 1); } 4491 }, 4492 "es_ar": { 4493 "name": "Argentinean Spanish", 4494 "numbers": [ 4495 1, 4496 2 4497 ], 4498 "plurals": function(n) { return Number(n != 1); } 4499 }, 4500 "et": { 4501 "name": "Estonian", 4502 "numbers": [ 4503 1, 4504 2 4505 ], 4506 "plurals": function(n) { return Number(n != 1); } 4507 }, 4508 "eu": { 4509 "name": "Basque", 4510 "numbers": [ 4511 1, 4512 2 4513 ], 4514 "plurals": function(n) { return Number(n != 1); } 4515 }, 4516 "fa": { 4517 "name": "Persian", 4518 "numbers": [ 4519 1 4520 ], 4521 "plurals": function(n) { return 0; } 4522 }, 4523 "fi": { 4524 "name": "Finnish", 4525 "numbers": [ 4526 1, 4527 2 4528 ], 4529 "plurals": function(n) { return Number(n != 1); } 4530 }, 4531 "fil": { 4532 "name": "Filipino", 4533 "numbers": [ 4534 1, 4535 2 4536 ], 4537 "plurals": function(n) { return Number(n > 1); } 4538 }, 4539 "fo": { 4540 "name": "Faroese", 4541 "numbers": [ 4542 1, 4543 2 4544 ], 4545 "plurals": function(n) { return Number(n != 1); } 4546 }, 4547 "fr": { 4548 "name": "French", 4549 "numbers": [ 4550 1, 4551 2 4552 ], 4553 "plurals": function(n) { return Number(n > 1); } 4554 }, 4555 "fur": { 4556 "name": "Friulian", 4557 "numbers": [ 4558 1, 4559 2 4560 ], 4561 "plurals": function(n) { return Number(n != 1); } 4562 }, 4563 "fy": { 4564 "name": "Frisian", 4565 "numbers": [ 4566 1, 4567 2 4568 ], 4569 "plurals": function(n) { return Number(n != 1); } 4570 }, 4571 "ga": { 4572 "name": "Irish", 4573 "numbers": [ 4574 1, 4575 2, 4576 3, 4577 7, 4578 11 4579 ], 4580 "plurals": function(n) { return Number(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4) ;} 4581 }, 4582 "gd": { 4583 "name": "Scottish Gaelic", 4584 "numbers": [ 4585 1, 4586 2, 4587 3, 4588 20 4589 ], 4590 "plurals": function(n) { return Number((n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3); } 4591 }, 4592 "gl": { 4593 "name": "Galician", 4594 "numbers": [ 4595 1, 4596 2 4597 ], 4598 "plurals": function(n) { return Number(n != 1); } 4599 }, 4600 "gu": { 4601 "name": "Gujarati", 4602 "numbers": [ 4603 1, 4604 2 4605 ], 4606 "plurals": function(n) { return Number(n != 1); } 4607 }, 4608 "gun": { 4609 "name": "Gun", 4610 "numbers": [ 4611 1, 4612 2 4613 ], 4614 "plurals": function(n) { return Number(n > 1); } 4615 }, 4616 "ha": { 4617 "name": "Hausa", 4618 "numbers": [ 4619 1, 4620 2 4621 ], 4622 "plurals": function(n) { return Number(n != 1); } 4623 }, 4624 "he": { 4625 "name": "Hebrew", 4626 "numbers": [ 4627 1, 4628 2 4629 ], 4630 "plurals": function(n) { return Number(n != 1); } 4631 }, 4632 "hi": { 4633 "name": "Hindi", 4634 "numbers": [ 4635 1, 4636 2 4637 ], 4638 "plurals": function(n) { return Number(n != 1); } 4639 }, 4640 "hr": { 4641 "name": "Croatian", 4642 "numbers": [ 4643 1, 4644 2, 4645 5 4646 ], 4647 "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } 4648 }, 4649 "hu": { 4650 "name": "Hungarian", 4651 "numbers": [ 4652 1, 4653 2 4654 ], 4655 "plurals": function(n) { return Number(n != 1); } 4656 }, 4657 "hy": { 4658 "name": "Armenian", 4659 "numbers": [ 4660 1, 4661 2 4662 ], 4663 "plurals": function(n) { return Number(n != 1); } 4664 }, 4665
4665 "ia": { 4666 "name": "Interlingua", 4667 "numbers": [ 4668 1, 4669 2 4670 ], 4671 "plurals": function(n) { return Number(n != 1); } 4672 }, 4673 "id": { 4674 "name": "Indonesian", 4675 "numbers": [ 4676 1 4677 ], 4678 "plurals": function(n) { return 0; } 4679 }, 4680 "is": { 4681 "name": "Icelandic", 4682 "numbers": [ 4683 1, 4684 2 4685 ], 4686 "plurals": function(n) { return Number(n%10!=1 || n%100==11); } 4687 }, 4688 "it": { 4689 "name": "Italian", 4690 "numbers": [ 4691 1, 4692 2 4693 ], 4694 "plurals": function(n) { return Number(n != 1); } 4695 }, 4696 "ja": { 4697 "name": "Japanese", 4698 "numbers": [ 4699 1 4700 ], 4701 "plurals": function(n) { return 0; } 4702 }, 4703 "jbo": { 4704 "name": "Lojban", 4705 "numbers": [ 4706 1 4707 ], 4708 "plurals": function(n) { return 0; } 4709 }, 4710 "jv": { 4711 "name": "Javanese", 4712 "numbers": [ 4713 0, 4714 1 4715 ], 4716 "plurals": function(n) { return Number(n !== 0); } 4717 }, 4718 "ka": { 4719 "name": "Georgian", 4720 "numbers": [ 4721 1 4722 ], 4723 "plurals": function(n) { return 0; } 4724 }, 4725 "kk": { 4726 "name": "Kazakh", 4727 "numbers": [ 4728 1 4729 ], 4730 "plurals": function(n) { return 0; } 4731 }, 4732 "km": { 4733 "name": "Khmer", 4734 "numbers": [ 4735 1 4736 ], 4737 "plurals": function(n) { return 0; } 4738 }, 4739 "kn": { 4740 "name": "Kannada", 4741 "numbers": [ 4742 1, 4743 2 4744 ], 4745 "plurals": function(n) { return Number(n != 1); } 4746 }, 4747 "ko": { 4748 "name": "Korean", 4749 "numbers": [ 4750 1 4751 ], 4752 "plurals": function(n) { return 0; } 4753 }, 4754 "ku": { 4755 "name": "Kurdish", 4756 "numbers": [ 4757 1, 4758 2 4759 ], 4760 "plurals": function(n) { return Number(n != 1); } 4761 }, 4762 "kw": { 4763 "name": "Cornish", 4764 "numbers": [ 4765 1, 4766 2, 4767 3, 4768 4 4769 ], 4770 "plurals": function(n) { return Number((n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : 3); } 4771 }, 4772 "ky": { 4773 "name": "Kyrgyz", 4774 "numbers": [ 4775 1 4776 ], 4777 "plurals": function(n) { return 0; } 4778 }, 4779 "lb": { 4780 "name": "Letzeburgesch", 4781 "numbers": [ 4782 1, 4783 2 4784 ], 4785 "plurals": function(n) { return Number(n != 1); } 4786 }, 4787 "ln": { 4788 "name": "Lingala", 4789 "numbers": [ 4790 1, 4791 2 4792 ], 4793 "plurals": function(n) { return Number(n > 1); } 4794 }, 4795 "lo": { 4796 "name": "Lao", 4797 "numbers": [ 4798 1 4799 ], 4800 "plurals": function(n) { return 0; } 4801 }, 4802 "lt": { 4803 "name": "Lithuanian", 4804 "numbers": [ 4805 1, 4806 2, 4807 10 4808 ], 4809 "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2); } 4810 }, 4811 "lv": { 4812 "name": "Latvian", 4813 "numbers": [ 4814 0, 4815 1, 4816 2 4817 ], 4818 "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n !== 0 ? 1 : 2); } 4819 }, 4820 "mai": { 4821 "name": "Maithili", 4822 "numbers": [ 4823 1, 4824 2 4825 ], 4826 "plurals": function(n) { return Number(n != 1); } 4827 }, 4828 "mfe": { 4829 "name": "Mauritian Creole", 4830 "numbers": [ 4831 1, 4832 2 4833 ], 4834 "plurals": function(n) { return Number(n > 1); } 4835 }, 4836 "mg": { 4837 "name": "Malagasy", 4838 "numbers": [ 4839 1, 4840 2 4841 ], 4842 "plurals": function(n) { return Number(n > 1); } 4843 }, 4844 "mi": { 4845 "name": "Maori", 4846 "numbers": [ 4847 1, 4848 2 4849 ], 4850 "plurals": function(n) { return Number(n > 1); } 4851 }, 4852 "mk": { 4853 "name": "Macedonian", 4854 "numbers": [ 4855 1, 4856 2 4857 ], 4858 "plurals": function(n) { return Number(n==1 || n%10==1 ? 0 : 1); } 4859 }, 4860 "ml": { 4861 "name": "Malayalam", 4862 "numbers": [ 4863 1, 4864 2 4865 ], 4866 "plurals": function(n) { return Number(n != 1); } 4867 }, 4868 "mn": { 4869 "name": "Mongolian", 4870 "numbers": [ 4871 1, 4872 2 4873 ], 4874 "plurals": function(n) { return Number(n != 1); } 4875 }, 4876 "mnk": { 4877 "name": "Mandinka", 4878 "numbers": [ 4879 0, 4880 1, 4881 2 4882 ], 4883 "plurals": function(n) { return Number(0 ? 0 : n==1 ? 1 : 2); } 4884 }, 4885 "mr": { 4886 "name": "Marathi", 4887 "numbers": [ 4888 1, 4889 2 4890 ], 4891 "plurals": function(n) { return Number(n != 1); } 4892 }, 4893 "ms": { 4894 "name": "Malay", 4895 "numbers": [ 4896 1 4897 ], 4898 "plurals": function(n) { return 0; } 4899 }, 4900 "mt": { 4901 "name": "Maltese", 4902 "numbers": [ 4903 1, 4904 2, 4905 11, 4906 20 4907 ], 4908 "plurals": function(n) { return Number(n==1 ? 0 : n===0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3); } 4909 }, 4910 "nah": { 4911 "name": "Nahuatl", 4912 "numbers": [ 4913 1, 4914 2 4915 ], 4916 "plurals": function(n) { return Number(n != 1); } 4917 }, 4918 "nap": { 4919 "name": "Neapolitan", 4920 "numbers": [ 4921 1, 4922 2 4923 ], 4924 "plurals": function(n) { return Number(n != 1); } 4925 }, 4926 "nb": {
4927 "name": "Norwegian Bokmal", 4928 "numbers": [ 4929 1, 4930 2 4931 ], 4932 "plurals": function(n) { return Number(n != 1); } 4933 }, 4934 "ne": { 4935 "name": "Nepali", 4936 "numbers": [ 4937 1, 4938 2 4939 ], 4940 "plurals": function(n) { return Number(n != 1); } 4941 }, 4942 "nl": { 4943 "name": "Dutch", 4944 "numbers": [ 4945 1, 4946 2 4947 ], 4948 "plurals": function(n) { return Number(n != 1); } 4949 }, 4950 "nn": { 4951 "name": "Norwegian Nynorsk", 4952 "numbers": [ 4953 1, 4954 2 4955 ], 4956 "plurals": function(n) { return Number(n != 1); } 4957 }, 4958 "no": { 4959 "name": "Norwegian", 4960 "numbers": [ 4961 1, 4962 2 4963 ], 4964 "plurals": function(n) { return Number(n != 1); } 4965 }, 4966 "nso": { 4967 "name": "Northern Sotho", 4968 "numbers": [ 4969 1, 4970 2 4971 ], 4972 "plurals": function(n) { return Number(n != 1); } 4973 }, 4974 "oc": { 4975 "name": "Occitan", 4976 "numbers": [ 4977 1, 4978 2 4979 ], 4980 "plurals": function(n) { return Number(n > 1); } 4981 }, 4982 "or": { 4983 "name": "Oriya", 4984 "numbers": [ 4985 2, 4986 1 4987 ], 4988 "plurals": function(n) { return Number(n != 1); } 4989 }, 4990 "pa": { 4991 "name": "Punjabi", 4992 "numbers": [ 4993 1, 4994 2 4995 ], 4996 "plurals": function(n) { return Number(n != 1); } 4997 }, 4998 "pap": { 4999 "name": "Papiamento", 5000 "numbers": [ 5001 1, 5002 2 5003 ], 5004 "plurals": function(n) { return Number(n != 1); } 5005 }, 5006 "pl": { 5007 "name": "Polish", 5008 "numbers": [ 5009 1, 5010 2, 5011 5 5012 ], 5013 "plurals": function(n) { return Number(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } 5014 }, 5015 "pms": { 5016 "name": "Piemontese", 5017 "numbers": [ 5018 1, 5019 2 5020 ], 5021 "plurals": function(n) { return Number(n != 1); } 5022 }, 5023 "ps": { 5024 "name": "Pashto", 5025 "numbers": [ 5026 1, 5027 2 5028 ], 5029 "plurals": function(n) { return Number(n != 1); } 5030 }, 5031 "pt": { 5032 "name": "Portuguese", 5033 "numbers": [ 5034 1, 5035 2 5036 ], 5037 "plurals": function(n) { return Number(n != 1); } 5038 }, 5039 "pt_br": { 5040 "name": "Brazilian Portuguese", 5041 "numbers": [ 5042 1, 5043 2 5044 ], 5045 "plurals": function(n) { return Number(n != 1); } 5046 }, 5047 "rm": { 5048 "name": "Romansh", 5049 "numbers": [ 5050 1, 5051 2 5052 ], 5053 "plurals": function(n) { return Number(n != 1); } 5054 }, 5055 "ro": { 5056 "name": "Romanian", 5057 "numbers": [ 5058 1, 5059 2, 5060 20 5061 ], 5062 "plurals": function(n) { return Number(n==1 ? 0 : (n===0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2); } 5063 }, 5064 "ru": { 5065 "name": "Russian", 5066 "numbers": [ 5067 1, 5068 2, 5069 5 5070 ], 5071 "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } 5072 }, 5073 "sah": { 5074 "name": "Yakut", 5075 "numbers": [ 5076 1 5077 ], 5078 "plurals": function(n) { return 0; } 5079 }, 5080 "sco": { 5081 "name": "Scots", 5082 "numbers": [ 5083 1, 5084 2 5085 ], 5086 "plurals": function(n) { return Number(n != 1); } 5087 }, 5088 "se": { 5089 "name": "Northern Sami", 5090 "numbers": [ 5091 1, 5092 2 5093 ], 5094 "plurals": function(n) { return Number(n != 1); } 5095 }, 5096 "si": { 5097 "name": "Sinhala", 5098 "numbers": [ 5099 1, 5100 2 5101 ], 5102 "plurals": function(n) { return Number(n != 1); } 5103 }, 5104 "sk": { 5105 "name": "Slovak", 5106 "numbers": [ 5107 1, 5108 2, 5109 5 5110 ], 5111 "plurals": function(n) { return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2); } 5112 }, 5113 "sl": { 5114 "name": "Slovenian", 5115 "numbers": [ 5116 5, 5117 1, 5118 2, 5119 3 5120 ], 5121 "plurals": function(n) { return Number(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0); } 5122 }, 5123 "so": { 5124 "name": "Somali", 5125 "numbers": [ 5126 1, 5127 2 5128 ], 5129 "plurals": function(n) { return Number(n != 1); } 5130 }, 5131 "son": { 5132 "name": "Songhay", 5133 "numbers": [ 5134 1, 5135 2 5136 ], 5137 "plurals": function(n) { return Number(n != 1); } 5138 }, 5139 "sq": { 5140 "name": "Albanian", 5141 "numbers": [ 5142 1, 5143 2 5144 ], 5145 "plurals": function(n) { return Number(n != 1); } 5146 }, 5147 "sr": { 5148 "name": "Serbian", 5149 "numbers": [ 5150 1, 5151 2, 5152 5 5153 ], 5154 "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } 5155 }, 5156 "su": { 5157 "name": "Sundanese", 5158 "numbers": [ 5159 1 5160 ], 5161 "plurals": function(n) { return 0; } 5162 }, 5163 "sv": { 5164 "name": "Swedish", 5165 "numbers": [ 5166 1, 5167 2 5168 ], 5169 "plurals": function(n) { return Number(n != 1); } 5170 }, 5171 "sw": { 5172 "name": "Swahili", 5173 "numbers": [ 5174 1, 5175 2 5176 ], 5177 "plurals": function(n) { return Number(n != 1); } 5178 }, 5179 "ta": { 5180 "name": "Tamil", 5181 "numbers": [ 5182 1, 5183 2 5184 ], 5185 "plurals": function(n) { return Number(n != 1); } 5186 }, 5187 "te": { 5188 "name": "Telugu", 5189 "numbers": [ 5190 1, 5191 2 5192 ], 5193 "plurals": function(n) { return Number(n != 1); } 5194 }, 5195 "tg": { 5196 "name": "Tajik", 5197 "numbers": [ 5198 1, 5199 2 5200 ], 5201 "plurals": function(n) { return Number(n > 1); } 5202 }, 5203 "th": { 5204 "name": "Thai", 5205 "numbers": [ 5206 1 5207 ], 5208 "plurals": function(n) { return 0; } 5209 }, 5210 "ti": { 5211 "name": "Tigrinya", 5212 "numbers": [ 5213 1, 5214 2 5215 ], 5216 "plurals": function(n) { return Number(n > 1); } 5217 }, 5218 "tk": { 5219 "name": "Turkmen", 5220 "numbers": [ 5221 1, 5222 2 5223 ], 5224 "plurals": function(n) { return Number(n != 1); } 5225 }, 5226 "tr": { 5227 "name": "Turkish", 5228 "numbers": [ 5229 1, 5230 2 5231 ], 5232 "plurals": function(n) { return Number(n > 1); } 5233 }, 5234 "tt": { 5235 "name": "Tatar", 5236 "numbers": [ 5237 1 5238 ], 5239 "plurals": function(n) { return 0; } 5240 }, 5241 "ug": { 5242 "name": "Uyghur", 5243 "numbers": [ 5244 1 5245 ], 5246 "plurals": function(n) { return 0; } 5247 }, 5248 "uk": { 5249 "name": "Ukrainian", 5250 "numbers": [ 5251 1, 5252 2, 5253 5 5254 ], 5255 "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } 5256 }, 5257 "ur": { 5258 "name": "Urdu", 5259 "numbers": [ 5260 1, 5261 2 5262 ], 5263 "plurals": function(n) { return Number(n != 1); } 5264 }, 5265 "uz": { 5266 "name": "Uzbek", 5267 "numbers": [ 5268 1, 5269 2 5270 ], 5271 "plurals": function(n) { return Number(n > 1); } 5272 }, 5273 "vi": { 5274 "name": "Vietnamese", 5275 "numbers": [ 5276 1 5277 ], 5278 "plurals": function(n) { return 0; } 5279 }, 5280 "wa": { 5281 "name": "Walloon", 5282 "numbers": [ 5283 1, 5284 2 5285 ], 5286 "plurals": function(n) { return Number(n > 1); } 5287 }, 5288 "wo": { 5289 "name": "Wolof", 5290 "numbers": [ 5291 1 5292 ], 5293 "plurals": function(n) { return 0; } 5294 }, 5295 "yo": { 5296 "name": "Yoruba", 5297 "numbers": [ 5298 1, 5299 2 5300 ], 5301 "plurals": function(n) { return Number(n != 1); } 5302 }, 5303 "zh": { 5304 "name": "Chinese", 5305 "numbers": [ 5306 1 5307 ], 5308 "plurals": function(n) { return 0; } 5309 } 5310 }, 5311 5312 // for demonstration only sl and ar is added but you can add your own pluralExtensions 5313 addRule: function(lng, obj) { 5314 pluralExtensions.rules[lng] = obj;
5315 }, 5316 5317 setCurrentLng: function(lng) { 5318 if (!pluralExtensions.currentRule || pluralExtensions.currentRule.lng !== lng) { 5319 var parts = lng.split('-'); 5320 5321 pluralExtensions.currentRule = { 5322 lng: lng, 5323 rule: pluralExtensions.rules[parts[0]] 5324 }; 5325 } 5326 }, 5327 5328 get: function(lng, count) { 5329 var parts = lng.split('-'); 5330 5331 function getResult(l, c) { 5332 var ext; 5333 if (pluralExtensions.currentRule && pluralExtensions.currentRule.lng === lng) { 5334 ext = pluralExtensions.currentRule.rule; 5335 } else { 5336 ext = pluralExtensions.rules[l]; 5337 } 5338 if (ext) { 5339 var i = ext.plurals(c); 5340 var number = ext.numbers[i]; 5341 if (ext.numbers.length === 2 && ext.numbers[0] === 1) { 5342 if (number === 2) { 5343 number = -1; // regular plural 5344 } else if (number === 1) { 5345 number = 1; // singular 5346 } 5347 }//console.log(count + '-' + number); 5348 return number; 5349 } else { 5350 return c === 1 ? '1' : '-1'; 5351 } 5352 } 5353 5354 return getResult(parts[0], count); 5355 } 5356 5357 }; 5358 var postProcessors = {}; 5359 var addPostProcessor = function(name, fc) { 5360 postProcessors[name] = fc; 5361 }; 5362 // sprintf support 5363 var sprintf = (function() { 5364 function get_type(variable) { 5365 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase(); 5366 } 5367 function str_repeat(input, multiplier) { 5368 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */} 5369 return output.join(''); 5370 } 5371 5372 var str_format = function() { 5373 if (!str_format.cache.hasOwnProperty(arguments[0])) { 5374 str_format.cache[arguments[0]] = str_format.parse(arguments[0]); 5375 } 5376 return str_format.format.call(null, str_format.cache[arguments[0]], arguments); 5377 }; 5378 5379 str_format.format = function(parse_tree, argv) { 5380 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length; 5381 for (i = 0; i < tree_length; i++) { 5382 node_type = get_type(parse_tree[i]); 5383 if (node_type === 'string') { 5384 output.push(parse_tree[i]); 5385 } 5386 else if (node_type === 'array') { 5387 match = parse_tree[i]; // convenience purposes only 5388 if (match[2]) { // keyword argument 5389 arg = argv[cursor]; 5390 for (k = 0; k < match[2].length; k++) { 5391 if (!arg.hasOwnProperty(match[2][k])) { 5392 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k])); 5393 } 5394 arg = arg[match[2][k]]; 5395 } 5396 } 5397 else if (match[1]) { // positional argument (explicit) 5398 arg = argv[match[1]]; 5399 } 5400 else { // positional argument (implicit) 5401 arg = argv[cursor++]; 5402 } 5403 5404 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) { 5405 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg))); 5406 } 5407 switch (match[8]) { 5408 case 'b': arg = arg.toString(2); break; 5409 case 'c': arg = String.fromCharCode(arg); break; 5410 case 'd': arg = parseInt(arg, 10); break; 5411 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break; 5412 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break; 5413 case 'o': arg = arg.toString(8); break; 5414 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break; 5415 case 'u': arg = Math.abs(arg); break; 5416 case 'x': arg = arg.toString(16); break; 5417 case 'X': arg = arg.toString(16).toUpperCase(); break; 5418 } 5419 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg); 5420 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' '; 5421 pad_length = match[6] - String(arg).length; 5422 pad = match[6] ? str_repeat(pad_character, pad_length) : ''; 5423 output.push(match[5] ? arg + pad : pad + arg); 5424 } 5425 } 5426 return output.join(''); 5427 }; 5428 5429 str_format.cache = {}; 5430 5431 str_format.parse = function(fmt) { 5432 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0; 5433 while (_fmt) { 5434 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) { 5435 parse_tree.push(match[0]); 5436 } 5437 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) { 5438 parse_tree.push('%'); 5439 } 5440 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) { 5441 if (match[2]) { 5442 arg_names |= 1; 5443 var field_list = [], replacement_field = match[2], field_match = []; 5444 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) { 5445 field_list.push(field_match[1]); 5446 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') { 5447 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) { 5448 field_list.push(field_match[1]); 5449 } 5450 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) { 5451 field_list.push(field_match[1]); 5452 } 5453 else { 5454 throw('[sprintf] huh?'); 5455 } 5456 } 5457 } 5458 else { 5459 throw('[sprintf] huh?'); 5460 } 5461 match[2] = field_list; 5462 } 5463 else { 5464 arg_names |= 2; 5465 } 5466 if (arg_names === 3) { 5467 throw('[sprintf] mixing positional and named placeholders is not (yet) supported'); 5468 } 5469 parse_tree.push(match); 5470 } 5471 else { 5472 throw('[sprintf] huh?'); 5473 } 5474 _fmt = _fmt.substring(match[0].length); 5475 } 5476 return parse_tree; 5477 }; 5478 5479 return str_format; 5480 })(); 5481 5482 var vsprintf = function(fmt, argv) { 5483 argv.unshift(fmt); 5484 return sprintf.apply(null, argv); 5485 }; 5486 5487 addPostProcessor("sprintf", function(val, key, opts) { 5488 if (!opts.sprintf) return val; 5489 5490 if (Object.prototype.toString.apply(opts.sprintf) === '[object Array]') { 5491 return vsprintf(val, opts.sprintf); 5492 } else if (typeof opts.sprintf === 'object') { 5493 return sprintf(val, opts.sprintf); 5494 } 5495 5496 return val; 5497 });
5498 // public api interface 5499 i18n.init = init; 5500 i18n.setLng = setLng; 5501 i18n.preload = preload; 5502 i18n.addResourceBundle = addResourceBundle; 5503 i18n.removeResourceBundle = removeResourceBundle; 5504 i18n.loadNamespace = loadNamespace; 5505 i18n.loadNamespaces = loadNamespaces; 5506 i18n.setDefaultNamespace = setDefaultNamespace; 5507 i18n.t = translate; 5508 i18n.translate = translate; 5509 i18n.exists = exists; 5510 i18n.detectLanguage = f.detectLanguage; 5511 i18n.pluralExtensions = pluralExtensions; 5512 i18n.sync = sync; 5513 i18n.functions = f; 5514 i18n.lng = lng; 5515 i18n.addPostProcessor = addPostProcessor; 5516 i18n.options = o; 5517 5518})(); 5519 5520 5521 5522/********* 5523* ../app/js/lib/jsdiff.js 5524**********/ 5525(function() {/* 5526 * Javascript Diff Algorithm 5527 * By John Resig (http://ejohn.org/) 5528 * Modified by Chu Alan "sprite" 5529 * 5530 * Released under the MIT license. 5531 * 5532 * More Info: 5533 * http://ejohn.org/projects/javascript-diff-algorithm/ 5534 */ 5535 5536function escape(s) { 5537 var n = s; 5538 n = n.replace(/&/g, "&"); 5539 n = n.replace(/</g, "<"); 5540 n = n.replace(/>/g, ">"); 5541 n = n.replace(/"/g, """); 5542 5543 return n; 5544} 5545 5546function diffString( o, n ) { 5547 o = o.replace(/\s+$/, ''); 5548 n = n.replace(/\s+$/, ''); 5549 5550 var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) ); 5551 var str = ""; 5552 5553 var oSpace = o.match(/\s+/g); 5554 if (oSpace == null) { 5555 oSpace = ["\n"]; 5556 } else { 5557 oSpace.push("\n"); 5558 } 5559 var nSpace = n.match(/\s+/g); 5560 if (nSpace == null) { 5561 nSpace = ["\n"]; 5562 } else { 5563 nSpace.push("\n"); 5564 } 5565 5566 if (out.n.length == 0) { 5567 for (var i = 0; i < out.o.length; i++) { 5568 str += '<del>' + escape(out.o[i]) + oSpace[i] + "</del>"; 5569 } 5570 } else { 5571 if (out.n[0].text == null) { 5572 for (n = 0; n < out.o.length && out.o[n].text == null; n++) { 5573 str += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>"; 5574 } 5575 } 5576 5577 for ( var i = 0; i < out.n.length; i++ ) { 5578 if (out.n[i].text == null) { 5579 str += '<ins>' + escape(out.n[i]) + nSpace[i] + "</ins>"; 5580 } else { 5581 var pre = ""; 5582 5583 for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { 5584 pre += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>"; 5585 } 5586 str += " " + out.n[i].text + nSpace[i] + pre; 5587 } 5588 } 5589 } 5590 5591 return str; 5592} 5593 5594function randomColor() { 5595 return "rgb(" + (Math.random() * 100) + "%, " + 5596 (Math.random() * 100) + "%, " + 5597 (Math.random() * 100) + "%)"; 5598} 5599function diffString2( o, n ) { 5600 o = o.replace(/\s+$/, ''); 5601 n = n.replace(/\s+$/, ''); 5602 5603 var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) ); 5604 5605 var oSpace = o.match(/\s+/g); 5606 if (oSpace == null) { 5607 oSpace = ["\n"]; 5608 } else { 5609 oSpace.push("\n"); 5610 } 5611 var nSpace = n.match(/\s+/g); 5612 if (nSpace == null) { 5613 nSpace = ["\n"]; 5614 } else { 5615 nSpace.push("\n"); 5616 } 5617 5618 var os = ""; 5619 var colors = new Array(); 5620 for (var i = 0; i < out.o.length; i++) { 5621 colors[i] = randomColor(); 5622 5623 if (out.o[i].text != null) { 5624 os += '<span style="background-color: ' +colors[i]+ '">' + 5625 escape(out.o[i].text) + oSpace[i] + "</span>"; 5626 } else { 5627 os += "<del>" + escape(out.o[i]) + oSpace[i] + "</del>"; 5628 } 5629 } 5630 5631 var ns = ""; 5632 for (var i = 0; i < out.n.length; i++) { 5633 if (out.n[i].text != null) { 5634 ns += '<span style="background-color: ' +colors[out.n[i].row]+ '">' + 5635 escape(out.n[i].text) + nSpace[i] + "</span>"; 5636 } else { 5637 ns += "<ins>" + escape(out.n[i]) + nSpace[i] + "</ins>"; 5638 } 5639 } 5640 5641 return { o : os , n : ns }; 5642} 5643 5644function diff( o, n ) { 5645 var ns = new Object(); 5646 var os = new Object(); 5647 5648 for ( var i = 0; i < n.length; i++ ) { 5649 if ( ns[ n[i] ] == null ) 5650 ns[ n[i] ] = { rows: new Array(), o: null }; 5651 ns[ n[i] ].rows.push( i ); 5652 } 5653 5654 for ( var i = 0; i < o.length; i++ ) { 5655 if ( os[ o[i] ] == null ) 5656 os[ o[i] ] = { rows: new Array(), n: null }; 5657 os[ o[i] ].rows.push( i ); 5658 } 5659 5660 for ( var i in ns ) { 5661 if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) { 5662 n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] }; 5663 o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] }; 5664 } 5665 } 5666 5667 for ( var i = 0; i < n.length - 1; i++ ) { 5668 if ( n[i].text != null && n[i+1].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && 5669 n[i+1] == o[ n[i].row + 1 ] ) { 5670 n[i+1] = { text: n[i+1], row: n[i].row + 1 }; 5671 o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 }; 5672 } 5673 } 5674 5675 for ( var i = n.length - 1; i > 0; i-- ) { 5676 if ( n[i].text != null && n[i-1].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && 5677 n[i-1] == o[ n[i].row - 1 ] ) { 5678 n[i-1] = { text: n[i-1], row: n[i].row - 1 }; 5679 o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 }; 5680 } 5681 } 5682 5683 return { o: o, n: n }; 5684} 5685 5686window.diffString = diffString; 5687 5688})(); 5689 5690 5691/********* 5692* ../app/js/resources/en.js 5693**********/ 5694sofia.resources['en'] = { 5695 "translation": { 5696 "name": "English", 5697 "menu": { 5698 "labels": { 5699 "about": "About", 5700 "addwindow": "Add Window", 5701 "settings": "Settings", 5702 "feedback": "Feedback", 5703 "options": "Options" 5704 }, 5705 "feedback": { 5706 "name": "Name", 5707 "email": "Email", 5708 "comments": "Your Comments", 5709 "feature": "Feature Suggestion", 5710 "bug": "Bug Report", 5711 "other": "Other", 5712 "send": "Send", 5713 "thankyou": "Thank you for your feedback" 5714 }, 5715 "search": { 5716 "placeholder": "Search", 5717 "suggestversion": "Open <strong>{0}</strong> in new window", 5718 "suggestreference": "Go to <strong>{0}</strong> in {1}", 5719 "suggestsearch": "Search \"<strong>{0}</strong>\" in {1}" 5720 }, 5721 "config": { 5722 "font": "Style", 5723 "settings": "Settings", 5724 "tools": "Tools" 5725 }, 5726 "reset": "restore", 5727 "themes": { 5728 "default": "Normal", 5729 "sepia": "Sepia", 5730 "dark": "Low Light" 5731 } 5732 }, 5733 "plugins": { 5734 "visualfilters": { 5735 "button": "Visual Filters", 5736 "title": "Visual Filters", 5737 "newfilter": "New Filter", 5738 "strongsnumber": "Strong's #", 5739 "morphology": "Morphology", 5740 "style": "Style" 5741 }, 5742 "eng2p": { 5743 "button": "English 2nd Person Plural", 5744 "title": "English Second Person Plural", 5745 "description": "Modern English does not distinguish between <em>
5745you singular</em> and <em>you plural</em>, so this plugin uses regional English equivalents to represent the Hebrew and Greek forms." 5746 5747 }, 5748 "lemmapopup": { 5749 "findalloccurrences": "Find all occurrences (approximately __count__)" 5750 } 5751 }, 5752 "windows": { 5753 "deafbible": { 5754 "label": "Deaf Bible" 5755 }, 5756 "videobible": { 5757 "label": "Video Bible" 5758 }, 5759 "bible": { 5760 "label": "Bible", 5761 "recentlyused": "Recently Used", 5762 "filter": "Filter...", 5763 "ot": "Old Testament", 5764 "nt": "New Testament", 5765 "dc": "Deuterocanonical Books", 5766 "more": "More", 5767 "less": "Less", 5768 "default": "Featured", 5769 "languages": "Language", 5770 "countries": "Country", 5771 "lemma":"Greek/Hebrew Popups", 5772 "audio":"Audio available" 5773 }, 5774 "commentary": { 5775 "label": "Commentary" 5776 }, 5777 "map": { 5778 "label": "Map", 5779 "placeholder": "Search..." 5780 }, 5781 "search": { 5782 "label": "Search", 5783 "placeholder": "Search", 5784 "button": "Search", 5785 "results": "Results", 5786 "verses": "verses", 5787 "options": "Search Options" 5788 }, 5789 "parallel": { 5790 "label": "Parallels", 5791 "loading": "Loading...", 5792 "showall": "Show All", 5793 "hideall": "Hide All" 5794 }, 5795 "media": { 5796 "label": "Media" 5797 }, 5798 "notes": { 5799 "label": "Notes" 5800 }, 5801 "audio": { 5802 "label":"Audio", 5803 "options": "Audio Options", 5804 "synctext": "Sync Text (beta)", 5805 "autoplay": "Autoplay Next", 5806 "drama": "Dramatized", 5807 "nondrama": "Spoken" 5808 }, 5809 "comparison": { 5810 "label": "Comparison" 5811 }, 5812 "stats": { 5813 "label": "Statistics", 5814 "frequentwords":"Frequent Words" 5815 } 5816 }, 5817 "names": { 5818 "en": "English", 5819 "fr": "anglais", 5820 "es": "Inglés", 5821 "de": "Englisch", 5822 "ru": "английÑкий", 5823 "hi": "ठà¤à¤à¥à¤°à¥à¤à¥", 5824 "ar": "Ø§ÙØ¥ÙجÙÙØ²ÙØ©", 5825 "af": "Engels", 5826 "bn": "à¦à¦à¦°à§à¦à¦¿", 5827 "id": "Bahasa Inggris", 5828 "sw": "Kiingereza", 5829 "tr": "ingilizce", 5830 "ja": "è±èª", 5831 "zh-TW": "è±èª", 5832 "zh-CN": "è±è¯" 5833 } 5834 } 5835} 5836 5837 5838 5839/********* 5840* ../app/js/resources/fr.js 5841**********/ 5842sofia.resources['fr'] = { 5843 "translation": { 5844 "name": "Français", 5845 "menu": { 5846 "search": { 5847 "placeholder": "Recherche" 5848 }, 5849 "config": { 5850 "font": "Style", 5851 "settings": "Paramètres", 5852 "tools": "Outils" 5853 }, 5854 "reset": "remettre", 5855 "themes": { 5856 "default": "Normal", 5857 "sepia": "Sépia", 5858 "dark": "Low Light" 5859 }, 5860 "labels": { 5861 "about": "Sur", 5862 "addwindow": "Ajouter fenêtre", 5863 "settings": "Paramètres", 5864 "feedback": "Réaction", 5865 "options": "Options" 5866 }, 5867 "feedback": { 5868 "name": "Nom", 5869 "email": "Email", 5870 "comments": "Vos commentaires", 5871 "feature": "Caractéristique Suggestion", 5872 "bug": "Rapport de Bug", 5873 "other": "Autre", 5874 "send": "Envoyer", 5875 "thankyou": "Merci pour vos commentaires" 5876 } 5877 }, 5878 "plugins": { 5879 "visualfilters": { 5880 "button": "Filtres visuels", 5881 "title": "Filtres visuels", 5882 "newfilter": "Nouveau filtre", 5883 "strongsnumber": "De Strong #", 5884 "morphology": "Morphologie", 5885 "style": "Style" 5886 }, 5887 "eng2p": { 5888 "button": "Anglais 2ème personne pluriel", 5889 "title": "Anglais Deuxième personne pluriel" 5890 }, 5891 "lemmapopup": { 5892 "findalloccurrences": "Trouver toutes les occurrences (environ __count__)" 5893 } 5894 }, 5895 "windows": { 5896 "bible": { 5897 "label": "Bible", 5898 "filter": "Filtrer ...", 5899 "ot": "Ancien Testament", 5900 "nt": "Le Nouveau Testament", 5901 "dc": "Deutérocanoniques", 5902 "more": "Plus", 5903 "less": "Moins", 5904 "recentlyused": "Récemment utilisés", 5905 "languages": "Langues", 5906 "countries": "Pays" 5907 }, 5908 "commentary": { 5909 "label": "Commentaire" 5910 }, 5911 "map": { 5912 "label": "Cartes", 5913 "placeholder": "Recherche ..." 5914 }, 5915 "search": { 5916 "label": "Recherche", 5917 "placeholder": "Recherche", 5918 "button": "Recherche", 5919 "results": "Résultats", 5920 "verses": "versets", 5921 "options": "options de recherche" 5922 }, 5923 "media": { 5924 "label": "Médias" 5925 }, 5926 "notes": { 5927 "label": "Remarques" 5928 }, 5929 "audio": { 5930 "options": "Options audio", 5931 "synctext": "Synchronisation du texte (beta)", 5932 "autoplay": "Autoplay Suivant", 5933 "drama": "Drame", 5934 "nondrama": "Non Drame" 5935 }, 5936 "parallel": { 5937 "label": "Parallels", 5938 "loading": "Chargement ...", 5939 "showall": "Tout afficher", 5940 "hideall": "Masquer tout" 5941 }, 5942 "comparison": { 5943 "label": "Comparaison" 5944 } 5945 }, 5946 "names": { 5947 "en": "French" 5948 } 5949 } 5950} 5951 5952 5953 5954/********* 5955* ../app/js/resources/de.js 5956**********/ 5957sofia.resources['de'] = { 5958 "translation": { 5959 "name": "Deutsch", 5960 "menu": { 5961 "search": { 5962 "placeholder": "Suche" 5963 }, 5964 "config": {
5965 "font": "Schriftart", 5966 "settings": "Einstellungen", 5967 "tools": "Werkzeuge" 5968 }, 5969 "reset": "zurücksetzen", 5970 "labels": { 5971 "about": "Ãber", 5972 "addwindow": "Neues Fenster", 5973 "settings": "Einstellungen", 5974 "feedback": "Rückmeldung", 5975 "options": "Optionen" 5976 }, 5977 "feedback": { 5978 "name": "Name", 5979 "email": "E-Mail", 5980 "comments": "Ihre Kommentare", 5981 "feature": "Feature-Vorschlag", 5982 "bug": "Fehlerhinweis", 5983 "other": "Sonstiges", 5984 "send": "Absenden", 5985 "thankyou": "Vielen Dank für Ihre Rückmeldung" 5986 }, 5987 "themes": { 5988 "default": "Normal", 5989 "sepia": "Sepia", 5990 "dark": "Wenig Licht" 5991 } 5992 }, 5993 "plugins": { 5994 "visualfilters": { 5995 "button": "Visuelle Filter", 5996 "title": "Visuelle Filter", 5997 "newfilter": "Neuer Filter", 5998 "strongsnumber": "Strong #", 5999 "morphology": "Morphologie", 6000 "style": "Stil" 6001 }, 6002 "eng2p": { 6003 "button": "2. Person Plural Englisch", 6004 "title": "Englisch Zweite Person Plural" 6005 }, 6006 "lemmapopup": { 6007 "findalloccurrences": "Alle Vorkommen (etwa __count__)" 6008 } 6009 }, 6010 "windows": { 6011 "deafbible": { 6012 "label": "Bibel für Taube" 6013 }, 6014 "videobible": { 6015 "label": "Video-Bibel" 6016 }, 6017 "bible": { 6018 "label": "Bibel", 6019 "filter": "Filter ...", 6020 "ot": "Altes Testament", 6021 "nt": "Neues Testament", 6022 "dc": "Deuterokanonische Bücher", 6023 "more": "Mehr", 6024 "less": "Weniger", 6025 "recentlyused": "Zuletzt verwendet", 6026 "languages": "Sprachen", 6027 "countries": "Länder", 6028 "lemma":"Griechisch/Hebräisch-Popups", 6029 "audio":"Audio verfügbar" 6030 }, 6031 "commentary": { 6032 "label": "Kommentar" 6033 }, 6034 "map": { 6035 "label": "Karten", 6036 "placeholder": "Suche ..." 6037 }, 6038 "search": { 6039 "label": "Suche", 6040 "placeholder": "Suche", 6041 "button": "Suche", 6042 "results": "Ergebnisse", 6043 "verses": "Verse", 6044 "options": "Suchoptionen" 6045 }, 6046 "media": { 6047 "label": "Medien" 6048 }, 6049 "notes": { 6050 "label": "Notizen" 6051 }, 6052 "audio": { 6053 "options": "Audio-Optionen", 6054 "synctext": "Text synchron halten (beta)", 6055 "autoplay": "Automatisch nächstes spielen", 6056 "drama": "Dramatisiert", 6057 "nondrama": "Vorgelesen" 6058 }, 6059 "parallel": { 6060 "label": "Parallelstellen", 6061 "loading": "Wird geladen ...", 6062 "showall": "Alle anzeigen", 6063 "hideall": "Alle ausblenden" 6064 }, 6065 "comparison": { 6066 "label": "Vergleich" 6067 }, 6068 "stats": { 6069 "label": "Statistiken", 6070 "frequentwords":"Häufige Wörter" 6071 } 6072 }, 6073 "names": { 6074 "en": "German" 6075 } 6076 } 6077} 6078 6079 6080 6081/********* 6082* ../app/js/resources/es.js 6083**********/ 6084sofia.resources['es'] = { 6085 "translation": { 6086 "name": "español", 6087 "menu": { 6088 "search": { 6089 "placeholder": "Búsqueda" 6090 }, 6091 "config": { 6092 "font": "Fuente", 6093 "settings": "Configuración", 6094 "tools": "Instrumentos" 6095 }, 6096 "reset": "reajustar", 6097 "labels": { 6098 "about": "Acerca de", 6099 "addwindow": "Añadir Ventana", 6100 "settings": "Ajustes", 6101 "feedback": "Realimentación", 6102 "options": "Opciones" 6103 }, 6104 "feedback": { 6105 "name": "Nombre", 6106 "email": "Correo electrónico", 6107 "comments": "Sus comentarios", 6108 "feature": "CaracterÃstica Sugerencia", 6109 "bug": "Informar de un error", 6110 "other": "Otro", 6111 "send": "Enviar", 6112 "thankyou": "Gracias por su colaboración" 6113 }, 6114 "themes": { 6115 "default": "Normal", 6116 "sepia": "Sepia", 6117 "dark": "Luz baja" 6118 } 6119 }, 6120 "plugins": { 6121 "visualfilters": { 6122 "button": "Visual Filtros", 6123 "title": "Visual Filtros", 6124 "newfilter": "Nuevo filtro", 6125 "strongsnumber": "Strong #", 6126 "morphology": "MorfologÃa", 6127 "style": "Estilo" 6128 }, 6129 "eng2p": { 6130 "button": "Inglés segunda persona del plural", 6131 "title": "Inglés Segunda Persona del Plural" 6132 }, 6133 "lemmapopup": { 6134 "findalloccurrences": "Buscar todas las ocurrencias (aproximadamente __count__)" 6135 } 6136 }, 6137 "windows": { 6138 "bible": { 6139 "label": "Biblia", 6140 "filter": "Filtro ...", 6141 "ot": "Antiguo Testamento", 6142 "nt": "Nuevo Testamento", 6143 "dc": "Libros Deuterocanónicos", 6144 "more": "Más", 6145 "less": "Menos", 6146 "recentlyused": "Usados âârecientemente", 6147 "languages": "Idiomas", 6148 "countries": "PaÃses" 6149 }, 6150 "commentary": { 6151 "label": "Comentario" 6152 }, 6153 "map": { 6154 "label": "Mapas", 6155 "placeholder": "Buscar ..." 6156 }, 6157 "search": { 6158 "label": "Búsqueda", 6159 "placeholder": "Búsqueda", 6160 "button": "Búsqueda", 6161 "results": "Resultados",
6162 "verses": "versos", 6163 "options": "Opciones de Búsqueda" 6164 }, 6165 "media": { 6166 "label": "Medios de comunicación" 6167 }, 6168 "notes": { 6169 "label": "Notas" 6170 }, 6171 "audio": { 6172 "options": "Opciones de audio", 6173 "synctext": "Sincronizar texto (beta)", 6174 "autoplay": "Reproducción automática Siguiente", 6175 "drama": "Drama", 6176 "nondrama": "No drama" 6177 }, 6178 "parallel": { 6179 "label": "Paralelas", 6180 "loading": "Cargando ...", 6181 "showall": "Mostrar todo", 6182 "hideall": "Ocultar todo" 6183 }, 6184 "comparison": { 6185 "label": "Comparación" 6186 } 6187 }, 6188 "names": { 6189 "en": "Spanish" 6190 } 6191 } 6192} 6193 6194 6195 6196/********* 6197* ../app/js/resources/ru.js 6198**********/ 6199sofia.resources['ru'] = { 6200 "translation": { 6201 "name": "ÑÑÑÑкий", 6202 "menu": { 6203 "search": { 6204 "placeholder": "ÐоиÑк" 6205 }, 6206 "config": { 6207 "font": "ШÑиÑÑ", 6208 "settings": "ÐаÑÑÑойки", 6209 "tools": "ÐнÑÑÑÑменÑаÑий" 6210 }, 6211 "reset": "ÑбÑоÑ", 6212 "themes": { 6213 "default": "ÐоÑмалÑнÑй", 6214 "sepia": "СепиÑ", 6215 "dark": "ÐÐ¸Ð·ÐºÐ°Ñ Ð¾ÑвеÑенноÑÑÑ" 6216 }, 6217 "labels": { 6218 "about": "Ð", 6219 "addwindow": "ÐобавиÑÑ Ð¾ÐºÐ½Ð¾", 6220 "settings": "ÐаÑÑÑойки", 6221 "feedback": "ÐбÑаÑÐ½Ð°Ñ ÑвÑзÑ", 6222 "options": "ÐпÑии" 6223 }, 6224 "feedback": { 6225 "name": "ÐмÑ", 6226 "email": "Email", 6227 "comments": "ÐаÑи комменÑаÑии", 6228 "feature": "ÐÑобенноÑÑÑ ÐÑедложение", 6229 "bug": "СообÑение об оÑибке", 6230 "other": "ÐÑÑгой", 6231 "send": "ÐоÑлаÑÑ", 6232 "thankyou": "СпаÑибо за ÐÐ°Ñ Ð¾ÑзÑв" 6233 } 6234 }, 6235 "plugins": { 6236 "visualfilters": { 6237 "button": "ÐизÑалÑнÑе ФилÑÑÑÑ", 6238 "title": "ÐизÑалÑнÑе ФилÑÑÑÑ", 6239 "newfilter": "ÐовÑй ÑилÑÑÑ", 6240 "strongsnumber": "# СÑÑонга", 6241 "morphology": "ÐоÑÑологиÑ", 6242 "style": "СÑилÑ" 6243 }, 6244 "eng2p": { 6245 "button": "ÐнглийÑкий 2-го лиÑа множеÑÑвенного ÑиÑла", 6246 "title": "ÐнглийÑкий ÐÑоÑое ÐиÑо ÐножеÑÑвенное" 6247 }, 6248 "lemmapopup": { 6249 "findalloccurrences": "ÐайÑи вÑе Ð²Ñ Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ (пÑимеÑно __count__)" 6250 } 6251 }, 6252 "windows": { 6253 "bible": { 6254 "label": "ÐиблиÑ", 6255 "filter": "ФилÑÑÑ ...", 6256 "ot": "ÐеÑÑ Ð¸Ð¹ ÐавеÑ", 6257 "nt": "ÐовÑй ÐавеÑ", 6258 "dc": "ÐÑоÑоканониÑеÑкие книги", 6259 "more": "Ðолее", 6260 "less": "ÐенÑÑе", 6261 "recentlyused": "Ðедавно иÑполÑзованнÑе", 6262 "languages": "ЯзÑки", 6263 "countries": "СÑÑанÑ" 6264 }, 6265 "commentary": { 6266 "label": "ÐомменÑаÑий" 6267 }, 6268 "map": { 6269 "label": "ÐаÑÑÑ", 6270 "placeholder": "ÐоиÑк ..." 6271 }, 6272 "search": { 6273 "label": "ÐоиÑк", 6274 "placeholder": "ÐоиÑк", 6275 "button": "ÐоиÑк", 6276 "results": "РезÑлÑÑаÑÑ", 6277 "verses": "виÑÑи", 6278 "options": "ÐаÑамеÑÑÑ Ð¿Ð¾Ð¸Ñка" 6279 }, 6280 "media": { 6281 "label": "СÑедÑÑва маÑÑовой инÑоÑмаÑии" 6282 }, 6283 "notes": { 6284 "label": "ÐÑимеÑаниÑ" 6285 }, 6286 "audio": { 6287 "options": "Audio Options", 6288 "synctext": "Ð¡Ð¸Ð½Ñ ÑонизаÑÐ¸Ñ ÑекÑÑ (беÑа-веÑÑиÑ)", 6289 "autoplay": "ÐвÑозапÑÑк СледÑÑÑаÑ", 6290 "drama": "ÐÑама", 6291 "nondrama": "ÐомеÑа Ð´Ð»Ñ ÐÑама" 6292 }, 6293 "parallel": { 6294 "label": "Parallels", 6295 "loading": "Loading ...", 6296 "showall": "ÐоказаÑÑ Ð²Ñе", 6297 "hideall": "СкÑÑÑÑ Ð²Ñе" 6298 }, 6299 "comparison": { 6300 "label": "СÑавнение" 6301 } 6302 }, 6303 "names": { 6304 "en": "Russian" 6305 } 6306 } 6307} 6308 6309 6310 6311/********* 6312* ../app/js/resources/hi.js 6313**********/ 6314sofia.resources['hi'] = { 6315 "translation": { 6316 "name": "हिनà¥à¤¦à¥", 6317 "menu": { 6318 "search": { 6319 "placeholder": "à¤à¥à¤" 6320 }, 6321 "config": { 6322 "font": "फ़à¥à¤¨à¥à¤", 6323 "settings": "सà¥à¤à¤¿à¤à¤", 6324 "tools": "à¤à¤ªà¤à¤°à¤£" 6325 }, 6326 "tools": { 6327 "visualfilters": "विà¤à¥à¤ ल फ़िलà¥à¤à¤°", 6328 "eng2p": "ठà¤à¤à¥à¤°à¥à¤à¥ 2 वà¥à¤¯à¤à¥à¤¤à¤¿ बहà¥à¤µà¤à¤¨" 6329 }, 6330 "reset": "रà¥à¤¸à¥à¤", 6331 "themes": { 6332 "default": "साधारण", 6333 "sepia": "à¤à¤ पà¥à¤°à¤à¤¾à¤° à¤à¥ मà¤à¤²à¥", 6334 "dark": "à¤à¤® रà¥à¤¶à¤¨à¥" 6335 }, 6336 "labels": { 6337 "about": "तà¤à¤°à¥à¤¬à¤¨", 6338 "addwindow": "à¤à¤¿à¤¡à¤¼à¤à¥ à¤à¥à¤¡à¤¼à¥à¤", 6339 "settings": "सà¥à¤à¤¿à¤à¤à¥à¤¸", 6340 "feedback": "पà¥à¤°à¤¤à¤¿à¤à¥à¤°à¤¿à¤¯à¤¾", 6341 "options": "विà¤à¤²à¥à¤ª" 6342 }, 6343 "feedback": { 6344 "name": "नाम", 6345 "email": "à¤à¤®à¥à¤²", 6346 "comments": "à¤à¤ªà¤à¥ à¤à¤¿à¤ªà¥à¤ªà¤£à¥", 6347 "feature": "सà¥à¤µà¤¿à¤§à¤¾ सà¥à¤à¤¾à¤µ",
6348 "bug": "बठरिपà¥à¤°à¥à¤", 6349 "other": "ठनà¥à¤¯", 6350 "send": "à¤à¥à¤à¥", 6351 "thankyou": "à¤à¤ªà¤à¥ पà¥à¤°à¤¤à¤¿à¤à¥à¤°à¤¿à¤¯à¤¾ à¤à¥ लिठधनà¥à¤¯à¤µà¤¾à¤¦" 6352 } 6353 }, 6354 "plugins": { 6355 "visualfilters": { 6356 "button": "विà¤à¥à¤ ल फ़िलà¥à¤à¤°", 6357 "title": "विà¤à¥à¤ ल फ़िलà¥à¤à¤°", 6358 "newfilter": "नया फ़िलà¥à¤à¤°", 6359 "strongsnumber": "मà¤à¤¬à¥à¤¤ #", 6360 "morphology": "à¤à¤à¥à¤¤à¤¿ विà¤à¥à¤à¤¾à¤¨", 6361 "style": "शà¥à¤²à¥" 6362 }, 6363 "eng2p": { 6364 "button": "ठà¤à¤à¥à¤°à¥à¤à¥ 2 वà¥à¤¯à¤à¥à¤¤à¤¿ बहà¥à¤µà¤à¤¨", 6365 "title": "ठà¤à¤à¥à¤°à¥à¤à¥ 2 वà¥à¤¯à¤à¥à¤¤à¤¿ बहà¥à¤µà¤à¤¨" 6366 }, 6367 "lemmapopup": { 6368 "findalloccurrences": "सà¤à¥ à¤à¤à¤¨à¤¾à¤à¤ à¤à¤¾ पता (लà¤à¤à¤ __count__)" 6369 } 6370 }, 6371 "windows": { 6372 "bible": { 6373 "label": "बाà¤à¤¬à¤²", 6374 "filter": "तà¤...", 6375 "ot": "à¤à¤²à¥à¤¡ à¤à¥à¤¸à¥à¤à¤¾à¤®à¥à¤à¤", 6376 "nt": "नà¥à¤¯à¥ à¤à¥à¤¸à¥à¤à¤¾à¤®à¥à¤à¤", 6377 "dc": "Deuterocanonical Books", 6378 "more": "ठधिà¤", 6379 "less": "à¤à¤®", 6380 "recentlyused": "हाल हॠमà¥à¤ पà¥à¤°à¤¯à¥à¤à¥à¤¤", 6381 "languages": "à¤à¤¾à¤·à¤¾à¤à¤", 6382 "countries": "दà¥à¤¶" 6383 }, 6384 "commentary": { 6385 "label": "à¤à¥à¤à¤¾" 6386 }, 6387 "map": { 6388 "label": "मानà¤à¤¿à¤¤à¥à¤°", 6389 "placeholder": "à¤à¥à¤..." 6390 }, 6391 "search": { 6392 "label": "à¤à¥à¤", 6393 "placeholder": "à¤à¥à¤", 6394 "button": "à¤à¥à¤", 6395 "results": "परिणाम", 6396 "verses": "à¤à¤à¤¦", 6397 "options": "à¤à¥à¤ विà¤à¤²à¥à¤ª" 6398 }, 6399 "media": { 6400 "label": "मà¥à¤¡à¤¿à¤¯à¤¾" 6401 }, 6402 "notes": { 6403 "label": "नà¥à¤à¥à¤¸" 6404 }, 6405 "audio": { 6406 "options": "à¤à¤¡à¤¿à¤¯à¥ विà¤à¤²à¥à¤ª", 6407 "synctext": "सिà¤à¤ पाठ( बà¥à¤à¤¾ )", 6408 "autoplay": "à¤à¤à¥à¤ªà¥à¤²à¥ ठà¤à¤²à¤¾", 6409 "drama": "डà¥à¤°à¤¾à¤®à¤¾", 6410 "nondrama": "à¤à¥à¤° डà¥à¤°à¤¾à¤®à¤¾" 6411 }, 6412 "parallel": { 6413 "label": "समानताà¤à¤", 6414 "loading": "लà¥à¤¡à¤¿à¤à¤ ...", 6415 "showall": "सब दिà¤à¤¾à¤à¤", 6416 "hideall": "सà¤à¥ à¤à¥à¤ªà¤¾à¤à¤" 6417 }, 6418 "comparison": { 6419 "label": "तà¥à¤²à¤¨à¤¾" 6420 } 6421 }, 6422 "names": { 6423 "en": "Hindi" 6424 } 6425 } 6426} 6427 6428 6429 6430/********* 6431* ../app/js/resources/ar.js 6432**********/ 6433sofia.resources['ar'] = { 6434 "translation": { 6435 "name": "Ø§ÙØ¹Ø±Ø¨ÙØ©", 6436 "menu": { 6437 "search": { 6438 "placeholder": "Ø¨ØØ«" 6439 }, 6440 "config": { 6441 "font": "Ø§ÙØ®Ø·", 6442 "settings": "إعدادات", 6443 "tools": "Ø£Ø¯ÙØ§Øª" 6444 }, 6445 "reset": "إعادة ضبط", 6446 "themes": { 6447 "default": "Ø§ÙØªØ±Ø§Ø¶Ù", 6448 "sepia": "بÙ٠داÙÙ", 6449 "dark": "Ø®ÙÙÙÙØ© ØºØ§Ù ÙØ©" 6450 }, 6451 "labels": { 6452 "about": "ØÙÙ", 6453 "addwindow": "Ø¥Ø¶Ø§ÙØ© اÙÙØ§Ùذة", 6454 "settings": "Ø§ÙØ¥Ø¹Ø¯Ø§Ø¯Ø§Øª", 6455 "feedback": "Ø±Ø¯ÙØ¯ اÙÙØ¹Ù", 6456 "options": "Ø®ÙØ§Ø±Ø§Øª" 6457 }, 6458 "feedback": { 6459 "name": "اس٠", 6460 "email": "Ø§ÙØ¨Ø±Ùد Ø§ÙØ¥ÙÙØªØ±ÙÙÙ", 6461 "comments": "ØªØØ¯ÙثاتÙ", 6462 "feature": "Ù ÙØ²Ø© Ø§ÙØªØ±Ø§Ø", 6463 "bug": "ØªÙØ±Ùر Ø¹ÙØ©", 6464 "other": "آخر", 6465 "send": "إرساÙ", 6466 "thankyou": "Ø´ÙØ±Ø§ ÙÙ ÙØ§ØØ¸Ø§ØªÙ" 6467 } 6468 }, 6469 "plugins": { 6470 "visualfilters": { 6471 "button": "Ù Ø±Ø´ØØ§Øª Ø¨ØµØ±ÙØ©", 6472 "title": "Ù Ø±Ø´ØØ§Øª Ø¨ØµØ±ÙØ©", 6473 "newfilter": "Ù Ø±Ø´Ø Ø¬Ø¯ÙØ¯", 6474 "strongsnumber": "رÙÙ ÙÙØ±Ø³ سترÙÙØ¬", 6475 "morphology": "ÙØºÙÙØ§Øª", 6476 "style": "أسÙÙØ¨" 6477 }, 6478 "eng2p": { 6479 "button": "استخدا٠ÙÙÙ Ø© Ø£ÙØª ÙÙØ¬Ù ع ÙÙ Ø§ÙØ¥ÙجÙÙØ²ÙØ©", 6480 "title": "استخدا٠ÙÙÙ Ø© Ø£ÙØª ÙÙØ¬Ù ع ÙÙ Ø§ÙØ¥ÙجÙÙØ²ÙØ©" 6481 }, 6482 "lemmapopup": { 6483 "findalloccurrences": "Ø¨ØØ« ÙÙ Ø§ÙØØ§ÙØ§Øª" 6484 } 6485 }, 6486 "windows": { 6487 "bible": { 6488 "label": "اÙÙØªØ§Ø¨ اÙÙ ÙØ¯Ø³", 6489 "filter": "ØªØ±Ø´ÙØ اÙÙØ§Ø¦Ù Ø©", 6490 "ot": "Ø§ÙØ¹Ùد اÙÙØ¯ÙÙ ", 6491 "nt": "Ø§ÙØ¹Ùد Ø§ÙØ¬Ø¯Ùد", 6492 "dc": "Ø§ÙØ£Ø³Ùار اÙÙØ§ÙÙÙÙØ©", 6493 "more": "Ø£ÙØ«Ø±", 6494 "less": "Ø£ÙÙ", 6495 "recentlyused": "اÙ٠ستخد٠ة ٠ؤخرا", 6496 "languages": "اÙÙØºØ§Øª", 6497 "countries": "Ø§ÙØ¯ÙÙ" 6498 }, 6499 "commentary": { 6500 "label": "ØªÙØ§Ø³Ùر" 6501 }, 6502 "map": { 6503 "label": "خرائط", 6504 "placeholder": "Ø¨ØØ«" 6505 }, 6506 "search": { 6507 "label": "Ø¨ØØ«", 6508 "placeholder": "Ø¨ØØ«", 6509 "button": "Ø¨ØØ«", 6510 "results": "اÙÙØªØ§Ø¦Ø¬", 6511 "verses": "Ø§ÙØ¢Ùات", 6512 "options": "Ø®ÙØ§Ø±Ø§Øª Ø£ÙØ«Ø± ÙÙØ¨ØØ«" 6513 }, 6514 "media": { 6515 "label": "Ø³Ù Ø¹ÙØ§Øª ÙØ¨ØµØ±Ùات" 6516 }, 6517 "notes": { 6518 "label": "Ù ÙØ§ØØ¸Ø§Øª" 6519 }, 6520 "audio": { 6521 "options": "Ø®ÙØ§Ø±Ø§Øª Ø§ÙØµÙت", 6522 "synctext": "ØªØØ±Ù٠اÙÙØµ ٠ع Ø§ÙØµÙت", 6523 "autoplay": "تشغÙÙ Ø§ÙØ£ØµØØ§Ø Ø§ÙØªØ§Ù٠تÙÙØ§Ø¦ÙاÙ", 6524 "drama": "ÙØ±Ø§Ø¡Ø© Ø¯Ø±Ø§Ù ÙØ©", 6525 "nondrama": "ÙØ±Ø§Ø¡Ø© ØºÙØ± Ø¯Ø±Ø§Ù ÙØ©" 6526 }, 6527 "parallel": { 6528 "label": "اÙÙ ØªÙØ§Ø²Ùات", 6529 "loading": "تØÙ ÙÙ ...", 6530 "showall": "عرض Ø¬Ù ÙØ¹", 6531 "hideall": "Ø¥Ø®ÙØ§Ø¡ اÙÙÙ" 6532 }, 6533 "comparison": { 6534 "label": "Ù ÙØ§Ø±ÙØ©" 6535 } 6536 }, 6537 "names": { 6538 "en": "Arabic" 6539 } 6540 } 6541} 6542 6543 6544 6545/********* 6546* ../app/js/resources/af.js 6547**********/ 6548sofia.resources['af'] = { 6549 "translation": { 6550 "name": "Afrikaans", 6551 "menu": { 6552 "search": { 6553 "placeholder": "Soek" 6554 }, 6555 "config": { 6556 "font": "Skrif", 6557 "settings": "Instellings", 6558 "tools": "Gereedskap" 6559 }, 6560 "reset": "herstel", 6561 "themes": { 6562 "default": "Normale", 6563 "sepia": "Sepia", 6564 "dark": "Lae lig" 6565 }, 6566 "labels": { 6567 "about": "Oor", 6568 "addwindow": "Voeg Venster", 6569 "settings": "Instellings", 6570 "feedback": "Terugvoer", 6571 "options": "Opsies" 6572 }, 6573 "feedback": { 6574 "name": "Naam", 6575 "email": "E-pos", 6576 "comments": "Jou kommentaar", 6577 "feature": "Funksie Voorstelle", 6578 "bug": "Bug Rapporteer", 6579 "other": "Ander", 6580 "send": "Stuur", 6581 "thankyou": "Dankie vir jou terugvoer" 6582 } 6583 }, 6584 "plugins": { 6585 "visualfilters": { 6586 "button": "Visuele Filters", 6587 "title": "Visuele Filters", 6588 "newfilter": "Nuwe weergawe", 6589 "strongsnumber": "Strong's #", 6590 "morphology": "Morfologie", 6591 "style": "Style" 6592 }, 6593 "eng2p": { 6594 "button": "Engels 2de persoon meervoud", 6595 "title": "Engels Tweede persoon meervoud" 6596 }, 6597 "lemmapopup": { 6598 "findalloccurrences": "Vind alle gebeurtenisse (ongeveer __count__)" 6599 } 6600 }, 6601 "windows": { 6602 "bible": { 6603 "label": "Bybel", 6604 "filter": "Filter ...", 6605 "ot": "Ou Testament", 6606 "nt": "Nuwe Testament", 6607 "dc": "Deuterokanonieke Boeke", 6608 "more": "Meer", 6609 "less": "Minder", 6610 "recentlyused": "Onlangs gebruik", 6611 "languages": "Tale", 6612 "countries": "Lande" 6613 }, 6614 "commentary": { 6615 "label": "Kommentaar" 6616 }, 6617 "map": { 6618 "label": "Kaarte", 6619 "placeholder": "Soek ..." 6620 }, 6621 "search": { 6622 "label": "Soek", 6623 "placeholder": "Soek", 6624 "button": "Soek", 6625 "results": "Resultate",
6626 "verses": "verse", 6627 "options": "Soek opsies" 6628 }, 6629 "media": { 6630 "label": "Media" 6631 }, 6632 "notes": { 6633 "label": "Notes" 6634 }, 6635 "audio": { 6636 "options": "Audio Options", 6637 "synctext": "Sinkroniseer teks (beta)", 6638 "autoplay": "Vakansie speel Volgende", 6639 "drama": "Drama", 6640 "nondrama": "Nie-Drama" 6641 }, 6642 "parallel": { 6643 "label": "Parallelle", 6644 "loading": "Laai ...", 6645 "showall": "Wys Alle", 6646 "hideall": "Verberg Alle" 6647 }, 6648 "comparison": { 6649 "label": "Vergelyking" 6650 } 6651 }, 6652 "names": { 6653 "en": "Afrikaans" 6654 } 6655 } 6656} 6657 6658 6659 6660/********* 6661* ../app/js/resources/bn.js 6662**********/ 6663sofia.resources['bn'] = { 6664 "translation": { 6665 "name": "বাà¦à¦²à¦¾", 6666 "menu": { 6667 "search": { 6668 "placeholder": "ঠনà§à¦¸à¦¨à§à¦§à¦¾à¦¨" 6669 }, 6670 "config": { 6671 "font": "ফনà§à¦", 6672 "settings": "সà§à¦à¦¿à¦à¦¸", 6673 "tools": "সরà¦à§à¦à¦¾à¦®" 6674 }, 6675 "reset": "রিসà§à¦ à¦à¦°à§à¦¨", 6676 "themes": { 6677 "default": "সà§à¦¬à¦¾à¦à¦¾à¦¬à¦¿à¦", 6678 "sepia": "à¦à¦¬à¦¿ à¦à¦à¦à¦¾à¦° à¦à¦à¦§à¦°à¦¨à§à¦° à¦à¦¾à¦²à¦à§ - বাদামৠরà¦", 6679 "dark": "à¦à¦® à¦à¦²à§" 6680 }, 6681 "labels": { 6682 "about": "পà§à¦°à¦¾à¦¯à¦¼", 6683 "addwindow": "যà§à¦ à¦à¦à¦¨à§à¦¡à§", 6684 "settings": "সà§à¦à¦¿à¦à¦¸", 6685 "feedback": "পà§à¦°à¦¤à¦¿à¦à§à¦°à¦¿à¦¯à¦¼à¦¾", 6686 "options": "বিà¦à¦²à§à¦ª" 6687 }, 6688 "feedback": { 6689 "name": "নাম", 6690 "email": "à¦à¦®à§à¦²", 6691 "comments": "à¦à¦ªà¦¨à¦¾à¦° মনà§à¦¤à¦¬à§à¦¯", 6692 "feature": "বà§à¦¶à¦¿à¦·à§à¦à§à¦¯ পরামরà§à¦¶", 6693 "bug": "বাঠরিপà§à¦°à§à¦", 6694 "other": "ঠনà§à¦¯à¦¾à¦¨à§à¦¯", 6695 "send": "পাঠান", 6696 "thankyou": "à¦à¦ªà¦¨à¦¾à¦° পà§à¦°à¦¤à¦¿à¦à§à¦°à¦¿à¦¯à¦¼à¦¾à¦° à¦à¦¨à§à¦¯ ধনà§à¦¯à¦¬à¦¾à¦¦" 6697 } 6698 }, 6699 "plugins": { 6700 "visualfilters": { 6701 "button": "à¦à¦¿à¦¸à§à¦¯à¦¼à¦¾à¦² ফিলà§à¦à¦¾à¦°", 6702 "title": "à¦à¦¿à¦¸à§à¦¯à¦¼à¦¾à¦² ফিলà§à¦à¦¾à¦°", 6703 "newfilter": "নতà§à¦¨ ফিলà§à¦à¦¾à¦°", 6704 "strongsnumber": "à¦à¦° সà§à¦à§à¦°à¦ #", 6705 "morphology": "ঠà¦à§à¦à¦¸à¦à¦¸à§à¦¥à¦¾à¦¨à¦¬à¦¿à¦¦à§à¦¯à¦¾", 6706 "style": "শà§à¦²à§" 6707 }, 6708 "eng2p": { 6709 "button": "à¦à¦à¦°à§à¦à¦¿ 2nd বà§à¦¯à¦à§à¦¤à¦¿ বহà§à¦¬à¦à¦¨", 6710 "title": "à¦à¦à¦°à§à¦à¦¿ দà§à¦¬à¦¿à¦¤à§à¦¯à¦¼ বà§à¦¯à¦à§à¦¤à¦¿ বহà§à¦¬à¦à¦¨" 6711 }, 6712 "lemmapopup": { 6713 "findalloccurrences": "সব à¦à¦à¦¨à¦¾à¦° à¦à§à¦à¦à§à¦¨ (à¦à¦¨à§à¦®à¦¾à¦¨à¦¿à¦ __count__)" 6714 } 6715 }, 6716 "windows": { 6717 "bible": { 6718 "label": "বাà¦à¦¬à§à¦²", 6719 "filter": "ফিলà§à¦à¦¾à¦° ...", 6720 "ot": "à¦à¦²à§à¦¡ à¦à§à¦¸à§à¦à¦¾à¦®à§à¦¨à§à¦", 6721 "nt": "নববিধান", 6722 "dc": "Deuterocanonical বà¦", 6723 "more": "ঠধিà¦", 6724 "less": "à¦à¦®", 6725 "recentlyused": "সমà§à¦ªà§à¦°à¦¤à¦¿ বà§à¦¯à¦¬à¦¹à§à¦¤", 6726 "languages": "à¦à¦¾à¦·à¦¾", 6727 "countries": "দà§à¦¶ সমà§à¦¹" 6728 }, 6729 "commentary": { 6730 "label": "à¦à¦¾à¦·à§à¦¯" 6731 }, 6732 "map": { 6733 "label": "মানà¦à¦¿à¦¤à§à¦°", 6734 "placeholder": "ঠনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ ..." 6735 }, 6736 "search": { 6737 "label": "ঠনà§à¦¸à¦¨à§à¦§à¦¾à¦¨", 6738 "placeholder": "ঠনà§à¦¸à¦¨à§à¦§à¦¾à¦¨", 6739 "button": "ঠনà§à¦¸à¦¨à§à¦§à¦¾à¦¨", 6740 "results": "ফলাফল", 6741 "verses": "à¦à¦¯à¦¼à¦¾à¦¤", 6742 "options": "ঠনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ বিà¦à¦²à§à¦ª" 6743 }, 6744 "media": { 6745 "label": "মিডিয়া" 6746 }, 6747 "notes": { 6748 "label": "নà§à¦" 6749 }, 6750 "audio": { 6751 "options": "ঠডিঠবিà¦à¦²à§à¦ª", 6752 "synctext": "সিà¦à§à¦ à¦à§à¦à§à¦¸à¦ (বিà¦à¦¾)", 6753 "autoplay": "ঠà¦à§à¦ªà§à¦²à§ পরবরà§à¦¤à§", 6754 "drama": "নাà¦à¦", 6755 "nondrama": "ঠনাà¦à¦" 6756 }, 6757 "parallel": { 6758 "label": "সমানà§à¦¤à¦°à¦¾à¦²", 6759 "loading": "লà§à¦¡ হà¦à§à¦à§ ...", 6760 "showall": "সমসà§à¦¤ দà§à¦à¦¾à¦¨", 6761 "hideall": "সমসà§à¦¤ লà§à¦à¦¾à¦¨" 6762 }, 6763 "comparison": { 6764 "label": "তà§à¦²à¦¨à¦¾" 6765 } 6766 }, 6767 "names": { 6768 "en": "Bengali" 6769 } 6770 } 6771} 6772 6773 6774 6775/********* 6776* ../app/js/resources/id.js 6777**********/ 6778sofia.resources['id'] = { 6779 "translation": { 6780 "name": "Bahasa Indonesia", 6781 "menu": { 6782 "search": { 6783 "placeholder": "Pencarian" 6784 }, 6785 "config": { 6786 "font": "Fon", 6787 "settings": "Pengaturan", 6788 "tools": "Alat" 6789 }, 6790 "reset": "ulang", 6791 "themes": { 6792 "default": "Normal", 6793 "sepia": "Warna coklat tua", 6794 "dark": "Cahaya Rendah" 6795 }, 6796 "labels": { 6797 "about": "Tentang", 6798 "addwindow": "Tambahkan Jendela", 6799 "settings": "Pengaturan", 6800 "feedback": "Umpan Balik", 6801 "options": "Pilihan" 6802 }, 6803 "feedback": { 6804 "name": "Nama", 6805 "email": "Email", 6806 "comments": "Komentar Anda", 6807 "feature": "Fitur Saran", 6808 "bug": "Bug Report", 6809 "other": "Yang Lainnya", 6810 "send": "Mengirim", 6811 "thankyou": "Terima kasih atas tanggapan Anda" 6812 } 6813 }, 6814 "plugins": { 6815 "visualfilters": { 6816 "button": "Visual Filter", 6817 "title": "Visual Filter", 6818 "newfilter": "Filter baru", 6819 "strongsnumber": "Strong #", 6820 "morphology": "Morfologi", 6821 "style": "Gaya" 6822 }, 6823 "eng2p": { 6824 "button": "English 2 Orang Plural", 6825 "title": "English Kedua Orang Plural" 6826 }, 6827 "lemmapopup": { 6828 "findalloccurrences": "Cari semua kejadian (sekitar __count__)" 6829 } 6830 }, 6831 "windows": { 6832 "bible": { 6833 "label": "Alkitab", 6834 "filter": "Filter ...", 6835 "ot": "Perjanjian Lama", 6836 "nt": "Perjanjian Baru", 6837 "dc": "Deuterokanonika Books", 6838 "more": "Lebih", 6839 "less": "Kurang", 6840 "recentlyused": "Baru Digunakan", 6841 "languages": "Bahasa", 6842 "countries": "Negara" 6843 }, 6844 "commentary": { 6845 "label": "Komentar" 6846 }, 6847 "map": { 6848 "label": "Peta", 6849 "placeholder": "Cari ..." 6850 }, 6851 "search": { 6852 "label": "Pencarian", 6853 "placeholder": "Pencarian", 6854 "button": "Pencarian", 6855 "results": "Hasil", 6856 "verses": "ayat", 6857 "options": "Opsi pencarian" 6858 }, 6859 "media": { 6860 "label": "Media" 6861 }, 6862 "notes": { 6863 "label": "Catatan" 6864 }, 6865 "audio": { 6866 "options": "Audio Pilihan", 6867 "synctext": "Sync Text (beta)", 6868 "autoplay": "Autoplay Berikutnya", 6869 "drama": "Drama", 6870 "nondrama": "Non-Drama" 6871 }, 6872 "parallel": { 6873 "label": "Parallels", 6874 "loading": "Loading ...", 6875 "showall": "Tampilkan Semua", 6876 "hideall": "Sembunyikan Semua" 6877 }, 6878 "comparison": { 6879 "label": "Perbandingan" 6880 } 6881 }, 6882 "names": { 6883 "en": "Indonesian" 6884 } 6885 } 6886} 6887 6888 6889 6890/********* 6891* ../app/js/resources/sw.js 6892**********/ 6893sofia.resources['sw'] = { 6894 "translation": {
6895 "name": "Kiswahili", 6896 "menu": { 6897 "search": { 6898 "placeholder": "Search" 6899 }, 6900 "config": { 6901 "font": "Font", 6902 "settings": "Mazingira", 6903 "tools": "Vyombo vya" 6904 }, 6905 "reset": "upya", 6906 "themes": { 6907 "default": "Kawaida", 6908 "sepia": "Sepia", 6909 "dark": "Low Mwanga" 6910 }, 6911 "labels": { 6912 "about": "Kuhusu", 6913 "addwindow": "Kuongeza Window", 6914 "settings": "Mipangilio", 6915 "feedback": "Maoni", 6916 "options": "Options" 6917 }, 6918 "feedback": { 6919 "name": "Jina", 6920 "email": "Email", 6921 "comments": "Maoni yako", 6922 "feature": "Feature Maoni", 6923 "bug": "Bug Ripoti", 6924 "other": "Nyingine", 6925 "send": "Kutuma", 6926 "thankyou": "Asante sana kwa maoni yako" 6927 } 6928 }, 6929 "plugins": { 6930 "visualfilters": { 6931 "button": "Visual Filters", 6932 "title": "Visual Filters", 6933 "newfilter": "New Filter", 6934 "strongsnumber": "Nguvu ya #", 6935 "morphology": "Maumbile", 6936 "style": "Mtindo" 6937 }, 6938 "eng2p": { 6939 "button": "English 2 Mtu vyama vingi", 6940 "title": "Kiingereza Pili Mtu vyama vingi" 6941 }, 6942 "lemmapopup": { 6943 "findalloccurrences": "Kupata matukio yote (takriban __count__)" 6944 } 6945 }, 6946 "windows": { 6947 "bible": { 6948 "label": "Biblia", 6949 "filter": "Filter ...", 6950 "ot": "Agano la Kale", 6951 "nt": "New Testament", 6952 "dc": "Deuterocanonical vitabu", 6953 "more": "Zaidi", 6954 "less": "Chini ya", 6955 "recentlyused": "Kutumika hivi karibuni", 6956 "languages": "Lugha", 6957 "countries": "Nchi" 6958 }, 6959 "commentary": { 6960 "label": "Ufafanuzi" 6961 }, 6962 "map": { 6963 "label": "Ramani", 6964 "placeholder": "Search ..." 6965 }, 6966 "search": { 6967 "label": "Search", 6968 "placeholder": "Search", 6969 "button": "Search", 6970 "results": "Matokeo", 6971 "verses": "mistari", 6972 "options": "Search Options" 6973 }, 6974 "media": { 6975 "label": "Vyombo vya habari" 6976 }, 6977 "notes": { 6978 "label": "Maelezo" 6979 }, 6980 "audio": { 6981 "options": "Chaguzi Audio", 6982 "synctext": "Sync Nakala (beta)", 6983 "autoplay": "Autoplay Ifwatayo", 6984 "drama": "Mchezo wa kuigiza", 6985 "nondrama": "Mashirika yasiyo ya Drama" 6986 }, 6987 "parallel": { 6988 "label": "Sambamba", 6989 "loading": "Loading ...", 6990 "showall": "Kuonyesha wote", 6991 "hideall": "Ficha All" 6992 }, 6993 "comparison": { 6994 "label": "Kulinganisha" 6995 } 6996 }, 6997 "names": { 6998 "en": "Swahili" 6999 } 7000 } 7001} 7002 7003 7004 7005/********* 7006* ../app/js/resources/tr.js 7007**********/ 7008sofia.resources['tr'] = { 7009 "translation": { 7010 "name": "Türkçe", 7011 "menu": { 7012 "search": { 7013 "placeholder": "Arama" 7014 }, 7015 "config": { 7016 "font": "Stil", 7017 "settings": "Ayarlar", 7018 "tools": "Araçlar" 7019 }, 7020 "reset": "sıfırla", 7021 "themes": { 7022 "default": "Açık", 7023 "sepia": "Sepya", 7024 "dark": "Koyu" 7025 }, 7026 "labels": { 7027 "about": "Hakkında", 7028 "addwindow": "Pencere ekle", 7029 "settings": "Ayarlar", 7030 "feedback": "Geribesleme", 7031 "options": "Seçenekler" 7032 }, 7033 "feedback": { 7034 "name": "Isim", 7035 "email": "E-posta", 7036 "comments": "Yorumlarınız", 7037 "feature": "Ãzellik Ãneri", 7038 "bug": "Hata Raporu", 7039 "other": "DiÄer", 7040 "send": "Göndermek", 7041 "thankyou": "GörüÅleriniz için teÅekkür ederiz" 7042 } 7043 }, 7044 "plugins": { 7045 "visualfilters": { 7046 "button": "Görsel Filtreler", 7047 "title": "Görsel Filtreler", 7048 "newfilter": "Yeni Filtre", 7049 "strongsnumber": "Strong #", 7050 "morphology": "Biçimbilgisi", 7051 "style": "Stil" 7052 }, 7053 "eng2p": { 7054 "button": "İngilizce 2. KiÅi ÃoÄul", 7055 "title": "İngilizce İkinci KiÅi ÃoÄul" 7056 }, 7057 "lemmapopup": { 7058 "findalloccurrences": "Tüm örneÄi bul (yaklaÅık __count__)" 7059 } 7060 }, 7061 "windows": { 7062 "bible": { 7063 "label": "Katsal Kitap", 7064 "filter": "Filtre ...", 7065 "ot": "Eski AntlaÅma", 7066 "nt": "Yeni AntlaÅma", 7067 "dc": "Apokrif", 7068 "more": "Daha fazla", 7069 "less": "Daha az", 7070 "recentlyused": "Son Kullanılan", 7071 "languages": "Diller", 7072 "countries": "Ãlkeler" 7073 }, 7074 "commentary": { 7075 "label": "Açıklamalar" 7076 }, 7077 "map": { 7078 "label": "Haritalar", 7079 "placeholder": "Arama..." 7080 }, 7081 "search": { 7082 "label": "Arama", 7083 "placeholder": "Arama", 7084 "button": "Arama", 7085 "results": "Sonuçlar", 7086 "verses": "ayetler", 7087 "options": "Arama Seçenekleri" 7088 }, 7089 "parallel": { 7090 "label": "Paralellik", 7091 "loading": "Yükleniyor...", 7092 "showall": "Hepsi Göster", 7093 "hideall": "Hepsi Gizle" 7094 }, 7095 "media": { 7096 "label": "Medya" 7097 }, 7098 "notes": { 7099 "label": "Notlar" 7100 }, 7101 "audio": { 7102 "options": "Ses Seçenekleri", 7103 "synctext": "Sütün EÅleÅme (beta)", 7104 "autoplay": "Otomatik Yürüt", 7105 "drama": "Dramatize EdilmiÅ", 7106 "nondrama": "Tekil KonuÅma" 7107 }, 7108 "comparison": { 7109 "label": "KarÅılaÅtırma" 7110 } 7111 }, 7112 "names": { 7113 "en": "Turkish", 7114 "de": "Türkisch", 7115 "zh-TW": "è±èª", 7116 "zh-CN": "è±è¯", 7117 "es": "Turco", 7118 "fr": "turc" 7119 } 7120 } 7121} 7122 7123 7124 7125/********* 7126* ../app/js/resources/ja.js 7127**********/ 7128sofia.resources['ja'] = { 7129 "translation": { 7130 "name": "æ¥æ¬èª", 7131 "menu": { 7132 "search": { 7133 "placeholder": "æ¤ç´¢" 7134 }, 7135 "config": { 7136 "font": "ãã©ã³ã", 7137 "settings": "[è¨å®]", 7138 "tools": "ãã¼ã«" 7139 }, 7140 "reset": "ãªã»ãã", 7141 "themes": { 7142 "default": "é常ã®", 7143 "sepia": "ã»ãã¢", 7144 "dark": "å¾®å " 7145 }, 7146 "labels": { 7147 "about": "ç´", 7148 "addwindow": "ã¦ã£ã³ãã¦ã追å ", 7149 "settings": "[è¨å®]", 7150 "feedback": "ãã£ã¼ãããã¯", 7151 "options": "ãªãã·ã§ã³" 7152 }, 7153 "feedback": { 7154 "name": "åå", 7155 "email": "ã¡ã¼ã«", 7156 "comments": "ããªãã®ã³ã¡ã³ã", 7157 "feature": "ãã£ã¼ãã£ã¼ææ¡", 7158 "bug": "ãã°ã¬ãã¼ã", 7159 "other": "ãã®ä»", 7160 "send": "éä¿¡ãã", 7161 "thankyou": "ãååãããã¨ããããã¾ãã" 7162 } 7163 }, 7164 "plugins": { 7165 "visualfilters": { 7166 "button": "è¦è¦çãªãã£ã«ã¿", 7167 "title": "è¦è¦çãªãã£ã«ã¿", 7168 "newfilter": "æ°ãããã£ã«ã¿", 7169 "strongsnumber": "ã®å¼·ãï¼", 7170 "morphology": "å½¢æ å¦", 7171 "style": "ã¹ã¿ã¤ã«" 7172 }, 7173 "eng2p": { 7174 "button": "è±èªç¬¬äºäººã¯è¤æ°", 7175 "title": "è±èªäºäººç®ã¯è¤æ°" 7176 }, 7177 "lemmapopup": { 7178 "findalloccurrences": "ãã¹ã¦æ¤ç´¢ï¼ç´__count__ï¼" 7179 } 7180 }, 7181 "windows": { 7182 "bible": { 7183 "label": "èæ¸", 7184 "filter": "ãã£ã«ã¿...", 7185 "ot": "æ§ç´èæ¸", 7186 "nt": "æ°ç´èæ¸", 7187 "dc": "ç¬¬äºæ£å ¸", 7188 "more": "ãã£ã¨", 7189 "less": "ã¬ã¹", 7190 "recentlyused": "æè¿ä½¿ç¨ããã", 7191 "languages": "è¨èª", 7192 "countries": "å½" 7193 }, 7194 "commentary": { 7195 "label": "解説" 7196 }, 7197 "map": { 7198 "label": "å°å³", 7199 "placeholder": "æ¤ç´¢..." 7200 }, 7201 "search": { 7202 "label": "æ¤ç´¢", 7203 "placeholder": "æ¤ç´¢", 7204 "button": "æ¤ç´¢", 7205 "results": "çµæ", 7206 "verses": "è©©", 7207 "options": "æ¤ç´¢ãªãã·ã§ã³" 7208 }, 7209 "media": { 7210 "label": "ã¡ãã£ã¢" 7211 }, 7212 "notes": { 7213 "label": "注é" 7214 }, 7215 "audio": { 7216 "options": "ãªã¼ãã£ãªãªãã·ã§ã³", 7217 "synctext": "åæããã¹ãï¼ãã¼ã¿çï¼", 7218 "autoplay": "èªååçæ¬¡", 7219 "drama": "ãã©ã", 7220 "nondrama": "éãã©ã" 7221 }, 7222 "parallel": { 7223 "label": "Parallelsã®", 7224 "loading": "èªã¿è¾¼ãã§ãã¾ã...", 7225 "showall": "ãã¹ã¦è¡¨ç¤º", 7226 "hideall": "ãã¹ã¦é表示ã«ãã" 7227 }, 7228 "comparison": { 7229 "label": "æ¯è¼" 7230 } 7231 }, 7232 "names": { 7233 "en": "Japanese" 7234 } 7235 } 7236} 7237 7238 7239 7240/********* 7241* ../app/js/resources/zh-CN.js 7242**********/ 7243sofia.resources['zh-CN'] = { 7244 "translation": { 7245 "name": "䏿", 7246 "menu": { 7247 "search": { 7248 "placeholder": "æç´¢" 7249 }, 7250 "config": { 7251 "font": "åä½", 7252 "settings": "设置", 7253 "tools": "å·¥å ·" 7254 }, 7255 "reset": "å¤ä½" 7256 }, 7257 "plugins": { 7258 "visualfilters": { 7259 "button": "è§è§è¿æ»¤å¨", 7260 "title": "è§è§è¿æ»¤å¨" 7261 }, 7262 "eng2p": { 7263 "button": "è±è¯ç¬¬äºäººç§°å¤æ°", 7264 "title": "è±è¯ç¬¬äºäººç§°å¤æ°" 7265 } 7266 }, 7267 "windows": { 7268 "bible": { 7269 "label": "å£ç»", 7270 "filter": "è¿æ»¤å¨...", 7271 "ot": "æ§çº¦", 7272 "nt": "æ°çº¦å£ç»", 7273 "dc": "ç»ä¹¦" 7274 }, 7275 "commentary": { 7276 "label": "解说" 7277 }, 7278 "map": { 7279 "label": "å°å¾", 7280 "placeholder": "æç´¢..." 7281 }, 7282 "search": { 7283 "label": "æç´¢", 7284 "placeholder": "æç´¢", 7285 "button": "æç´¢", 7286 "results": "ç»æ", 7287 "verses": "è¯" 7288 }, 7289 "media": { 7290 "label": "åªä½" 7291 }, 7292 "notes": { 7293 "label": "注æ" 7294 }, 7295 "audio": { 7296 "options": "é³é¢é项", 7297 "synctext": "åæ¥ææ¬ï¼æµè¯çï¼", 7298 "autoplay": "èªå¨ææ¾ä¸ä¸ä¸ª", 7299 "drama": "æå§", 7300 "nondrama": "éæå§" 7301 } 7302 }, 7303 "names": { 7304 "en": "Chinese - Simplified" 7305 } 7306 } 7307} 7308 7309 7310 7311/********* 7312* ../app/js/resources/zh-TW.js 7313**********/ 7314sofia.resources['zh-TW'] = { 7315 "translation": { 7316 "name": "䏿(ç¹é«)", 7317 "menu": { 7318 "search": { 7319 "placeholder": "æç´¢" 7320 }, 7321 "config": { 7322 "font": "åå½¢", 7323 "settings": "è¨ç½®", 7324 "tools": "å·¥å ·" 7325 }, 7326 "reset": "éç½®", 7327 "themes": { 7328 "default": "æ£å¸¸", 7329 "sepia": "çè³", 7330 "dark": "å¼±å " 7331 }, 7332 "labels": { 7333 "about": "大ç´", 7334 "addwindow": "æ·»å çªå£", 7335 "settings": "è¨ç½®", 7336 "feedback": "ä¿¡æ¯åé¥", 7337 "options": "é¸é " 7338 }, 7339 "feedback": { 7340 "name": "åå", 7341 "email": "é»åéµä»¶", 7342 "comments": "æ¨çæè¦", 7343 "feature": "åè½å»ºè°", 7344 "bug": "é¯èª¤å ±å", 7345 "other": "å ¶ä»", 7346 "send": "ç¼é", 7347 "thankyou": "æè¬æ¨çåé¥" 7348 } 7349 }, 7350 "plugins": { 7351 "visualfilters": { 7352 "button": "è¦è¦ºé濾å¨", 7353 "title": "è¦è¦ºé濾å¨", 7354 "newfilter": "æ°çéæ¿¾å¨", 7355 "strongsnumber": "å¼·ççï¼",
7356 "morphology": "å½¢æ å¸", 7357 "style": "é¢¨æ ¼" 7358 }, 7359 "eng2p": { 7360 "button": "è±èªç¬¬äºäººç¨±è¤æ¸", 7361 "title": "è±èªç¬¬äºäººç¨±è¤æ¸" 7362 }, 7363 "lemmapopup": { 7364 "findalloccurrences": "æ¥æ¾ææåºç¾ï¼ç´__count__ï¼" 7365 } 7366 }, 7367 "windows": { 7368 "bible": { 7369 "label": "èç¶", 7370 "filter": "éæ¿¾...", 7371 "ot": "èç´å ¨æ¸", 7372 "nt": "æ°ç´å ¨æ¸", 7373 "dc": "ç¶æ¸", 7374 "more": "æ´å¤", 7375 "less": "å°", 7376 "recentlyused": "æè¿ä½¿ç¨", 7377 "languages": "èªè¨", 7378 "countries": "åå®¶" 7379 }, 7380 "commentary": { 7381 "label": "è©è«" 7382 }, 7383 "map": { 7384 "label": "å°å", 7385 "placeholder": "æç´¢..." 7386 }, 7387 "search": { 7388 "label": "æç´¢", 7389 "placeholder": "æç´¢", 7390 "button": "æç´¢", 7391 "results": "çµæ", 7392 "verses": "ç¶æ", 7393 "options": "æç´¢é¸é " 7394 }, 7395 "media": { 7396 "label": "åªé«" 7397 }, 7398 "notes": { 7399 "label": "çè¨" 7400 }, 7401 "audio": { 7402 "options": "é³é »é¸é ", 7403 "synctext": "åæ¥ææ¬ï¼æ¸¬è©¦çï¼", 7404 "autoplay": "èªåææ¾ä¸ä¸é ", 7405 "drama": "æ²å", 7406 "nondrama": "éæ²å" 7407 }, 7408 "parallel": { 7409 "label": "ç¸ä¼¼ä¹è", 7410 "loading": "å è¼ä¸...", 7411 "showall": "é¡¯ç¤ºå ¨é¨", 7412 "hideall": "å ¨é¨é±è" 7413 }, 7414 "comparison": { 7415 "label": "å°ç §" 7416 } 7417 }, 7418 "names": { 7419 "en": "Chinese - Traditional" 7420 } 7421 } 7422} 7423 7424 7425 7426/********* 7427* ../app/js/common/languagedata.js 7428**********/ 7429window.data = window.data || {}; 7430 7431data.language = { 7432 7433 rtl: ['ar','arz','arc','dv','fa','ha','he','heb','khw','ks','ku','ps','ur','yi'], 7434 7435 isRTL: function(lang) { 7436 return data.language.rtl.indexOf(lang) > -1; 7437 }, 7438 7439 codes: { 7440 ara: 'Afar', 7441 abk: 'Abkhazian', 7442 ace: 'Achinese', 7443 ach: 'Acoli', 7444 ada: 'Adangme', 7445 ady: 'Adyghe; Adygei', 7446 afa: 'Afro-Asiatic languages', 7447 afh: 'Afrihili', 7448 afr: 'Afrikaans', 7449 ain: 'Ainu', 7450 aka: 'Akan', 7451 akk: 'Akkadian', 7452 alb: 'Albanian', 7453 ale: 'Aleut', 7454 alg: 'Algonquian languages', 7455 alt: 'Southern Altai', 7456 amh: 'Amharic', 7457 anp: 'Angika', 7458 apa: 'Apache languages', 7459 ara: 'Arabic', 7460 arg: 'Aragonese', 7461 arm: 'Armenian', 7462 arn: 'Mapudungun', 7463 arp: 'Arapaho', 7464 arw: 'Arawak', 7465 asm: 'Assamese', 7466 ast: 'Asturian', 7467 ath: 'Athapascan languages', 7468 aus: 'Australian languages', 7469 ava: 'Avaric', 7470 ave: 'Avestan', 7471 awa: 'Awadhi', 7472 aym: 'Aymara', 7473 aze: 'Azerbaijani', 7474 bad: 'Banda languages', 7475 bai: 'Bamileke languages', 7476 bak: 'Bashkir', 7477 bal: 'Baluchi', 7478 bam: 'Bambara', 7479 ban: 'Balinese', 7480 baq: 'Basque', 7481 bas: 'Basa', 7482 bat: 'Baltic languages', 7483 bej: 'Beja; Bedawiyet', 7484 bel: 'Belarusian', 7485 bem: 'Bemba', 7486 ben: 'Bengali', 7487 ber: 'Berber languages', 7488 bho: 'Bhojpuri', 7489 bih: 'Bihari languages', 7490 bik: 'Bikol', 7491 bin: 'Bini; Edo', 7492 bis: 'Bislama', 7493 bla: 'Siksika', 7494 bnt: 'Bantu languages', 7495 tib: 'Tibetan', 7496 bos: 'Bosnian', 7497 bra: 'Braj', 7498 bre: 'Breton', 7499 btk: 'Batak languages', 7500 bua: 'Buriat', 7501 bug: 'Buginese', 7502 bul: 'Bulgarian', 7503 bur: 'Burmese', 7504 byn: 'Blin', 7505 cad: 'Caddo', 7506 cai: 'Central American Indian languages', 7507 car: 'Galibi Carib', 7508 cat: 'Catalan; Valencian', 7509 cau: 'Caucasian languages', 7510 ceb: 'Cebuano', 7511 cel: 'Celtic languages', 7512 cze: 'Czech', 7513 cha: 'Chamorro', 7514 chb: 'Chibcha', 7515 che: 'Chechen', 7516 chg: 'Chagatai', 7517 chi: 'Chinese', 7518 chk: 'Chuukese', 7519 chm: 'Mari', 7520 chn: 'Chinook jargon', 7521 cho: 'Choctaw', 7522 chp: 'Chipewyan; Dene Suline', 7523 chr: 'Cherokee', 7524 chu: 'Old Slavonic', 7525 chv: 'Chuvash', 7526 chy: 'Cheyenne', 7527 cmc: 'Chamic languages', 7528 cop: 'Coptic', 7529 cor: 'Cornish', 7530 cos: 'Corsican', 7531 cre: 'Cree', 7532 crh: 'Crimean Tatar', 7533 crp: 'Creoles and pidgins', 7534 csb: 'Kashubian', 7535 cus: 'Cushitic languages', 7536 wel: 'Welsh', 7537 cze: 'Czech', 7538 dak: 'Dakota', 7539 dan: 'Danish', 7540 dar: 'Dargwa', 7541 day: 'Land Dayak languages', 7542 del: 'Delaware', 7543 den: 'Slave', 7544 ger: 'German', 7545 dgr: 'Dogrib', 7546 din: 'Dinka', 7547 div: 'Divehi', 7548 doi: 'Dogri', 7549 dra: 'Dravidian languages', 7550 dsb: 'Lower Sorbian', 7551 dua: 'Duala', 7552 dum: 'Dutch', 7553 dut: 'Dutch; Flemish', 7554 dyu: 'Dyula', 7555 dzo: 'Dzongkha', 7556 efi: 'Efik', 7557 eka: 'Ekajuk', 7558 gre: 'Greek', 7559 elx: 'Elamite', 7560 eng: 'English', 7561 engn: 'English Commentaries',
7562 epo: 'Esperanto', 7563 est: 'Estonian', 7564 baq: 'Basque', 7565 ewe: 'Ewe', 7566 ewo: 'Ewondo', 7567 fan: 'Fang', 7568 fao: 'Faroese', 7569 per: 'Persian', 7570 fat: 'Fanti', 7571 fij: 'Fijian', 7572 fil: 'Filipino; Pilipino', 7573 fin: 'Finnish', 7574 fiu: 'Finno-Ugrian languages', 7575 fon: 'Fon', 7576 fre: 'French', 7577 frr: 'Northern Frisian', 7578 frs: 'Eastern Frisian', 7579 fry: 'Western Frisian', 7580 ful: 'Fulah', 7581 fur: 'Friulian', 7582 gaa: 'Ga', 7583 gay: 'Gayo', 7584 gba: 'Gbaya', 7585 gem: 'Germanic languages', 7586 geo: 'Georgian', 7587 gez: 'Geez', 7588 gil: 'Gilbertese', 7589 gla: 'Gaelic', 7590 gle: 'Irish', 7591 glg: 'Galician', 7592 glv: 'Manx', 7593 gon: 'Gondi', 7594 gor: 'Gorontalo', 7595 got: 'Gothic', 7596 grb: 'Grebo', 7597 gre: 'Greek', 7598 grn: 'Guarani', 7599 gsw: 'Swiss German', 7600 guj: 'Gujarati', 7601 gwi: 'Gwich\'in', 7602 hai: 'Haida', 7603 hat: 'Haitian', 7604 hau: 'Hausa', 7605 haw: 'Hawaiian', 7606 heb: 'Hebrew', 7607 her: 'Herero', 7608 hil: 'Hiligaynon', 7609 him: 'Himachali languages; Western Pahari languages', 7610 hin: 'Hindi', 7611 hit: 'Hittite', 7612 hmn: 'Hmong; Mong', 7613 hmo: 'Hiri Motu', 7614 hrv: 'Croatian', 7615 hsb: 'Upper Sorbian', 7616 hun: 'Hungarian', 7617 hup: 'Hupa', 7618 arm: 'Armenian', 7619 iba: 'Iban', 7620 ibo: 'Igbo', 7621 ice: 'Icelandic', 7622 ido: 'Ido', 7623 iii: 'Sichuan Yi', 7624 ijo: 'Ijo languages', 7625 iku: 'Inuktitut', 7626 ile: 'Interlingue; Occidental', 7627 ilo: 'Iloko', 7628 inc: 'Indic languages', 7629 ind: 'Indonesian', 7630 ine: 'Indo-European languages', 7631 inh: 'Ingush', 7632 ipk: 'Inupiaq', 7633 ira: 'Iranian languages', 7634 iro: 'Iroquoian languages', 7635 ice: 'Icelandic', 7636 ita: 'Italian', 7637 jav: 'Javanese', 7638 jbo: 'Lojban', 7639 jpn: 'Japanese', 7640 jpr: 'Judeo-Persian', 7641 jrb: 'Judeo-Arabic', 7642 kaa: 'Kara-Kalpak', 7643 kab: 'Kabyle', 7644 kac: 'Kachin; Jingpho', 7645 kal: 'Kalaallisut; Greenlandic', 7646 kam: 'Kamba', 7647 kan: 'Kannada', 7648 kar: 'Karen languages', 7649 kas: 'Kashmiri', 7650 geo: 'Georgian', 7651 kau: 'Kanuri', 7652 kaw: 'Kawi', 7653 kaz: 'Kazakh', 7654 kbd: 'Kabardian', 7655 kha: 'Khasi', 7656 khi: 'Khoisan languages', 7657 khm: 'Central Khmer', 7658 kho: 'Khotanese; Sakan', 7659 kik: 'Kikuyu; Gikuyu', 7660 kin: 'Kinyarwanda', 7661 kir: 'Kirghiz; Kyrgyz', 7662 kmb: 'Kimbundu', 7663 kok: 'Konkani', 7664 kom: 'Komi', 7665 kon: 'Kongo', 7666 kor: 'Korean', 7667 kos: 'Kosraean', 7668 kpe: 'Kpelle', 7669 krc: 'Karachay-Balkar', 7670 krl: 'Karelian', 7671 kro: 'Kru languages', 7672 kru: 'Kurukh', 7673 kua: 'Kuanyama', 7674 kum: 'Kumyk', 7675 kur: 'Kurdish', 7676 kut: 'Kutenai', 7677 lad: 'Ladino', 7678 lah: 'Lahnda', 7679 lam: 'Lamba', 7680 lao: 'Lao', 7681 lat: 'Latin', 7682 lav: 'Latvian',
7683 lez: 'Lezghian', 7684 lim: 'Limburgan', 7685 lin: 'Lingala', 7686 lit: 'Lithuanian', 7687 lol: 'Mongo', 7688 loz: 'Lozi', 7689 ltz: 'Luxembourgish; Letzeburgesch', 7690 lua: 'Luba-Lulua', 7691 lub: 'Luba-Katanga', 7692 lug: 'Ganda', 7693 lui: 'Luiseno', 7694 lun: 'Lunda', 7695 luo: 'Luo (Kenya and Tanzania)', 7696 lus: 'Lushai', 7697 mac: 'Macedonian', 7698 mad: 'Madurese', 7699 mag: 'Magahi', 7700 mah: 'Marshallese', 7701 mai: 'Maithili', 7702 mak: 'Makasar', 7703 mal: 'Malayalam', 7704 man: 'Mandingo', 7705 mao: 'Maori', 7706 map: 'Austronesian languages', 7707 mar: 'Marathi', 7708 mas: 'Masai', 7709 may: 'Malay', 7710 mdf: 'Moksha', 7711 mdr: 'Mandar', 7712 men: 'Mende', 7713 mic: 'Mi\'kmaq; Micmac', 7714 min: 'Minangkabau', 7715 mac: 'Macedonian', 7716 mkh: 'Mon-Khmer languages', 7717 mlg: 'Malagasy', 7718 mlt: 'Maltese', 7719 mnc: 'Manchu', 7720 mni: 'Manipuri', 7721 mno: 'Manobo languages', 7722 moh: 'Mohawk', 7723 mon: 'Mongolian', 7724 mos: 'Mossi', 7725 mao: 'Maori', 7726 may: 'Malay', 7727 mul: 'Multiple languages', 7728 mun: 'Munda languages', 7729 mus: 'Creek', 7730 mwl: 'Mirandese', 7731 mwr: 'Marwari', 7732 bur: 'Burmese', 7733 myn: 'Mayan languages', 7734 myv: 'Erzya', 7735 nah: 'Nahuatl languages', 7736 nai: 'North American Indian languages', 7737 nap: 'Neapolitan', 7738 nau: 'Nauru', 7739 nav: 'Navajo; Navaho', 7740 nbl: 'Ndebele South', 7741 nde: 'Ndebele North', 7742 ndo: 'Ndonga', 7743 nep: 'Nepali', 7744 nia: 'Nias', 7745 nic: 'Niger-Kordofanian languages', 7746 niu: 'Niuean', 7747 dut: 'Dutch',
7748 nno: 'Norwegian Nynorsk', 7749 nob: 'Bokmål, Norwegian', 7750 nog: 'Nogai', 7751 nor: 'Norwegian', 7752 nqo: 'N\'Ko', 7753 nso: 'Pedi; Sepedi; Northern Sotho', 7754 nub: 'Nubian languages', 7755 nwc: 'Classical Newari; Old Newari; Classical Nepal Bhasa', 7756 nya: 'Chichewa; Chewa; Nyanja', 7757 nym: 'Nyamwezi', 7758 nyn: 'Nyankole', 7759 nyo: 'Nyoro', 7760 nzi: 'Nzima', 7761 oci: 'Occitan (post 1500)', 7762 oji: 'Ojibwa', 7763 ori: 'Oriya', 7764 orm: 'Oromo', 7765 osa: 'Osage', 7766 oss: 'Ossetian; Ossetic', 7767 ota: 'Turkish, Ottoman (1500-1928)', 7768 oto: 'Otomian languages', 7769 paa: 'Papuan languages', 7770 pag: 'Pangasinan', 7771 pal: 'Pahlavi', 7772 pam: 'Pampanga; Kapampangan', 7773 pan: 'Panjabi; Punjabi', 7774 pap: 'Papiamento', 7775 pau: 'Palauan', 7776 per: 'Persian', 7777 phi: 'Philippine languages', 7778 phn: 'Phoenician', 7779 pli: 'Pali', 7780 pol: 'Polish', 7781 pon: 'Pohnpeian', 7782 por: 'Portuguese', 7783 pra: 'Prakrit languages', 7784 pus: 'Pashto', 7785 que: 'Quechua', 7786 raj: 'Rajasthani', 7787 rap: 'Rapanui', 7788 rar: 'Rarotongan; Cook Islands Maori', 7789 roa: 'Romance languages', 7790 roh: 'Romansh', 7791 rom: 'Romany', 7792 rum: 'Romanian', 7793 run: 'Rundi', 7794 rus: 'Russian', 7795 sad: 'Sandawe', 7796 sag: 'Sango', 7797 sah: 'Yakut', 7798 sal: 'Salishan languages', 7799 sam: 'Samaritan Aramaic', 7800 san: 'Sanskrit', 7801 sas: 'Sasak', 7802 sat: 'Santali', 7803 scn: 'Sicilian', 7804 sco: 'Scots', 7805 sel: 'Selkup', 7806 sgn: 'Sign Languages', 7807 shn: 'Shan', 7808 sid: 'Sidamo', 7809 sin: 'Sinhala; Sinhalese', 7810 sio: 'Siouan languages', 7811 sit: 'Sino-Tibetan languages', 7812 sla: 'Slavic languages', 7813 slo: 'Slovak', 7814 sma: 'Southern Sami', 7815 sme: 'Northern Sami', 7816 smi: 'Sami languages', 7817 smj: 'Lule Sami', 7818 smn: 'Inari Sami', 7819 smo: 'Samoan', 7820 sms: 'Skolt Sami', 7821 sna: 'Shona', 7822 snd: 'Sindhi', 7823 snk: 'Soninke', 7824 sog: 'Sogdian', 7825 som: 'Somali', 7826 son: 'Songhai languages', 7827 sot: 'Sotho, Southern', 7828 spa: 'Spanish', 7829 alb: 'Albanian', 7830 srd: 'Sardinian', 7831 srn: 'Sranan Tongo', 7832 srp: 'Serbian', 7833 srr: 'Serer', 7834 ssa: 'Nilo-Saharan languages', 7835 ssw: 'Swati', 7836 suk: 'Sukuma', 7837 sun: 'Sundanese', 7838 sus: 'Susu', 7839 sux: 'Sumerian', 7840 swa: 'Swahili', 7841 swe: 'Swedish', 7842 syc: 'Classical Syriac', 7843 syr: 'Syriac', 7844 tah: 'Tahitian', 7845 tai: 'Tai languages', 7846 tam: 'Tamil', 7847 tat: 'Tatar', 7848 tel: 'Telugu', 7849 tem: 'Timne', 7850 ter: 'Tereno', 7851 tet: 'Tetum', 7852 tgk: 'Tajik', 7853 tgl: 'Tagalog', 7854 tha: 'Thai', 7855 tib: 'Tibetan', 7856 tig: 'Tigre', 7857 tir: 'Tigrinya', 7858 tiv: 'Tiv', 7859 tkl: 'Tokelau', 7860 tlh: 'Klingon', 7861 tli: 'Tlingit', 7862 tmh: 'Tamashek', 7863 tog: 'Tonga', 7864 ton: 'Tonga', 7865 tpi: 'Tok Pisin', 7866 tsi: 'Tsimshian', 7867 tsn: 'Tswana', 7868 tso: 'Tsonga', 7869 tuk: 'Turkmen', 7870 tum: 'Tumbuka', 7871 tup: 'Tupi languages', 7872 tur: 'Turkish', 7873 tut: 'Altaic languages', 7874 tvl: 'Tuvalu', 7875 twi: 'Twi', 7876 tyv: 'Tuvinian', 7877 udm: 'Udmurt', 7878 uga: 'Ugaritic', 7879 uig: 'Uighur; Uyghur', 7880 ukr: 'Ukrainian', 7881 umb: 'Umbundu', 7882 und: 'Undetermined', 7883 urd: 'Urdu', 7884 uzb: 'Uzbek', 7885 vai: 'Vai', 7886 ven: 'Venda', 7887 vie: 'Vietnamese', 7888 vol: 'Volapük', 7889 vot: 'Votic', 7890 wak: 'Wakashan languages', 7891 wal: 'Wolaitta; Wolaytta', 7892 war: 'Waray', 7893 was: 'Washo', 7894 wel: 'Welsh', 7895 wen: 'Sorbian languages', 7896 wln: 'Walloon', 7897 wol: 'Wolof', 7898 xal: 'Kalmyk; Oirat', 7899 xho: 'Xhosa', 7900 yao: 'Yao', 7901 yap: 'Yapese', 7902 yid: 'Yiddish', 7903 yor: 'Yoruba', 7904 ypk: 'Yupik languages', 7905 zap: 'Zapotec', 7906 zbl: 'Blissymbols; Blissymbolics; Bliss', 7907 zen: 'Zenaga', 7908 zgh: 'Standard Moroccan Tamazight', 7909 zha: 'Zhuang', 7910 chi: 'Chinese Simplified', 7911 zho: 'Chinese Traditional', 7912 znd: 'Zande languages', 7913 zul: 'Zulu', 7914 zun: 'Zuni' 7915 } 7916}; 7917 7918 7919 7920/********* 7921* ../app/js/common/countriesdata.js 7922**********/ 7923sofia.countries = [ 7924{"name":"Afghanistan","alpha-2":"AF","alpha-3":"AFG"}, 7925{"name":"à land Islands","alpha-2":"AX","alpha-3":"ALA"}, 7926{"name":"Albania","alpha-2":"AL","alpha-3":"ALB"}, 7927{"name":"Algeria","alpha-2":"DZ","alpha-3":"DZA"}, 7928{"name":"American Samoa","alpha-2":"AS","alpha-3":"ASM"}, 7929{"name":"Andorra","alpha-2":"AD","alpha-3":"AND"}, 7930{"name":"Angola","alpha-2":"AO","alpha-3":"AGO"}, 7931{"name":"Anguilla","alpha-2":"AI","alpha-3":"AIA"}, 7932{"name":"Antarctica","alpha-2":"AQ","alpha-3":"ATA"}, 7933{"name":"Antigua and Barbuda","alpha-2":"AG","alpha-3":"ATG"}, 7934{"name":"Argentina","alpha-2":"AR","alpha-3":"ARG"}, 7935{"name":"Armenia","alpha-2":"AM","alpha-3":"ARM"}, 7936{"name":"Aruba","alpha-2":"AW","alpha-3":"ABW"}, 7937{"name":"Australia","alpha-2":"AU","alpha-3":"AUS"}, 7938{"name":"Austria","alpha-2":"AT","alpha-3":"AUT"}, 7939{"name":"Azerbaijan","alpha-2":"AZ","alpha-3":"AZE"}, 7940{"name":"Bahamas","alpha-2":"BS","alpha-3":"BHS"}, 7941{"name":"Bahrain","alpha-2":"BH","alpha-3":"BHR"}, 7942{"name":"Bangladesh","alpha-2":"BD","alpha-3":"BGD"}, 7943{"name":"Barbados","alpha-2":"BB","alpha-3":"BRB"}, 7944{"name":"Belarus","alpha-2":"BY","alpha-3":"BLR"}, 7945{"name":"Belgium","alpha-2":"BE","alpha-3":"BEL"}, 7946{"name":"Belize","alpha-2":"BZ","alpha-3":"BLZ"}, 7947{"name":"Benin","alpha-2":"BJ","alpha-3":"BEN"}, 7948{"name":"Bermuda","alpha-2":"BM","alpha-3":"BMU"}, 7949{"name":"Bhutan","alpha-2":"BT","alpha-3":"BTN"}, 7950{"name":"Bolivia, Plurinational State of","alpha-2":"BO","alpha-3":"BOL"}, 7951{"name":"Bonaire, Sint Eustatius and Saba","alpha-2":"BQ","alpha-3":"BES"}, 7952{"name":"Bosnia and Herzegovina","alpha-2":"BA","alpha-3":"BIH"}, 7953{"name":"Botswana","alpha-2":"BW","alpha-3":"BWA"}, 7954{"name":"Bouvet Island","alpha-2":"BV","alpha-3":"BVT"}, 7955{"name":"Brazil","alpha-2":"BR","alpha-3":"BRA"}, 7956{"name":"British Indian Ocean Territory","alpha-2":"IO","alpha-3":"IOT"}, 7957{"name":"Brunei Darussalam","alpha-2":"BN","alpha-3":"BRN"}, 7958{"name":"Bulgaria","alpha-2":"BG","alpha-3":"BGR"}, 7959{"name":"Burkina Faso","alpha-2":"BF","alpha-3":"BFA"}, 7960{"name":"Burundi","alpha-2":"BI","alpha-3":"BDI"}, 7961{"name":"Cambodia","alpha-2":"KH","alpha-3":"KHM"}, 7962{"name":"Cameroon","alpha-2":"CM","alpha-3":"CMR"}, 7963{"name":"Canada","alpha-2":"CA","alpha-3":"CAN"}, 7964{"name":"Cape Verde","alpha-2":"CV","alpha-3":"CPV"}, 7965{"name":"Cayman Islands","alpha-2":"KY","alpha-3":"CYM"}, 7966{"name":"Central African Republic","alpha-2":"CF","alpha-3":"CAF"}, 7967{"name":"Chad","alpha-2":"TD","alpha-3":"TCD"}, 7968{"name":"Chile","alpha-2":"CL","alpha-3":"CHL"}, 7969{"name":"China","alpha-2":"CN","alpha-3":"CHN"}, 7970{"name":"Christmas Island","alpha-2":"CX","alpha-3":"CXR"}, 7971{"name":"Cocos (Keeling) Islands","alpha-2":"CC","alpha-3":"CCK"}, 7972{"name":"Colombia","alpha-2":"CO","alpha-3":"COL"}, 7973{"name":"Comoros","alpha-2":"KM","alpha-3":"COM"}, 7974{"name":"Congo","alpha-2":"CG","alpha-3":"COG"}, 7975{"name":"Congo, the Democratic Republic of the","alpha-2":"CD","alpha-3":"COD"}, 7976{"name":"Cook Islands","alpha-2":"CK","alpha-3":"COK"}, 7977{"name":"Costa Rica","alpha-2":"CR","alpha-3":"CRI"}, 7978{"name":"Côte d'Ivoire","alpha-2":"CI","alpha-3":"CIV"}, 7979{"name":"Croatia","alpha-2":"HR","alpha-3":"HRV"}, 7980{"name":"Cuba","alpha-2":"CU","alpha-3":"CUB"}, 7981{"name":"Curaçao","alpha-2":"CW","alpha-3":"CUW"}, 7982{"name":"Cyprus","alpha-2":"CY","alpha-3":"CYP"}, 7983{"name":"Czech Republic","alpha-2":"CZ","alpha-3":"CZE"}, 7984{"name":"Denmark","alpha-2":"DK","alpha-3":"DNK"}, 7985{"name":"Djibouti","alpha-2":"DJ","alpha-3":"DJI"}, 7986{"name":"Dominica","alpha-2":"DM","alpha-3":"DMA"}, 7987{"name":"Dominican Republic","alpha-2":"DO","alpha-3":"DOM"}, 7988{"name":"Ecuador","alpha-2":"EC","alpha-3":"ECU"}, 7989{"name":"Egypt","alpha-2":"EG","alpha-3":"EGY"}, 7990{"name":"El Salvador","alpha-2":"SV","alpha-3":"SLV"}, 7991{"name":"Equatorial Guinea","alpha-2":"GQ","alpha-3":"GNQ"}, 7992{"name":"Eritrea","alpha-2":"ER","alpha-3":"ERI"}, 7993{"name":"Estonia","alpha-2":"EE","alpha-3":"EST"}, 7994{"name":"Ethiopia","alpha-2":"ET","alpha-3":"ETH"}, 7995{"name":"Falkland Islands (Malvinas)","alpha-2":"FK","alpha-3":"FLK"}, 7996{"name":"Faroe Islands","alpha-2":"FO","alpha-3":"FRO"}, 7997{"name":"Fiji","alpha-2":"FJ","alpha-3":"FJI"}, 7998{"name":"Finland","alpha-2":"FI","alpha-3":"FIN"}, 7999{"name":"France","alpha-2":"FR","alpha-3":"FRA"}, 8000{"name":"French Guiana","alpha-2":"GF","alpha-3":"GUF"}, 8001{"name":"French Polynesia","alpha-2":"PF","alpha-3":"PYF"}, 8002{"name":"French Southern Territories","alpha-2":"TF","alpha-3":"ATF"}, 8003{"name":"Gabon","alpha-2":"GA","alpha-3":"GAB"}, 8004{"name":"Gambia","alpha-2":"GM","alpha-3":"GMB"}, 8005{"name":"Georgia","alpha-2":"GE","alpha-3":"GEO"}, 8006{"name":"Germany","alpha-2":"DE","alpha-3":"DEU"}, 8007{"name":"Ghana","alpha-2":"GH","alpha-3":"GHA"}, 8008{"name":"Gibraltar","alpha-2":"GI","alpha-3":"GIB"}, 8009{"name":"Greece","alpha-2":"GR","alpha-3":"GRC"}, 8010{"name":"Greenland","alpha-2":"GL","alpha-3":"GRL"}, 8011{"name":"Grenada","alpha-2":"GD","alpha-3":"GRD"}, 8012{"name":"Guadeloupe","alpha-2":"GP","alpha-3":"GLP"}, 8013{"name":"Guam","alpha-2":"GU","alpha-3":"GUM"}, 8014{"name":"Guatemala","alpha-2":"GT","alpha-3":"GTM"}, 8015{"name":"Guernsey","alpha-2":"GG","alpha-3":"GGY"}, 8016{"name":"Guinea","alpha-2":"GN","alpha-3":"GIN"}, 8017{"name":"Guinea-Bissau","alpha-2":"GW","alpha-3":"GNB"}, 8018{"name":"Guyana","alpha-2":"GY","alpha-3":"GUY"}, 8019{"name":"Haiti","alpha-2":"HT","alpha-3":"HTI"}, 8020{"name":"Heard Island and McDonald Islands","alpha-2":"HM","alpha-3":"HMD"}, 8021{"name":"Holy See (Vatican City State)","alpha-2":"VA","alpha-3":"VAT"}, 8022{"name":"Honduras","alpha-2":"HN","alpha-3":"HND"}, 8023{"name":"Hong Kong","alpha-2":"HK","alpha-3":"HKG"}, 8024{"name":"Hungary","alpha-2":"HU","alpha-3":"HUN"}, 8025{"name":"Iceland","alpha-2":"IS","alpha-3":"ISL"}, 8026{"name":"India","alpha-2":"IN","alpha-3":"IND"}, 8027{"name":"Indonesia","alpha-2":"ID","alpha-3":"IDN"},
8028{"name":"Iran, Islamic Republic of","alpha-2":"IR","alpha-3":"IRN"}, 8029{"name":"Iraq","alpha-2":"IQ","alpha-3":"IRQ"}, 8030{"name":"Ireland","alpha-2":"IE","alpha-3":"IRL"}, 8031{"name":"Isle of Man","alpha-2":"IM","alpha-3":"IMN"}, 8032{"name":"Israel","alpha-2":"IL","alpha-3":"ISR"}, 8033{"name":"Italy","alpha-2":"IT","alpha-3":"ITA"}, 8034{"name":"Jamaica","alpha-2":"JM","alpha-3":"JAM"}, 8035{"name":"Japan","alpha-2":"JP","alpha-3":"JPN"}, 8036{"name":"Jersey","alpha-2":"JE","alpha-3":"JEY"}, 8037{"name":"Jordan","alpha-2":"JO","alpha-3":"JOR"}, 8038{"name":"Kazakhstan","alpha-2":"KZ","alpha-3":"KAZ"}, 8039{"name":"Kenya","alpha-2":"KE","alpha-3":"KEN"}, 8040{"name":"Kiribati","alpha-2":"KI","alpha-3":"KIR"}, 8041{"name":"Korea, Democratic People's Republic of","alpha-2":"KP","alpha-3":"PRK"}, 8042{"name":"Korea, Republic of","alpha-2":"KR","alpha-3":"KOR"}, 8043{"name":"Kuwait","alpha-2":"KW","alpha-3":"KWT"}, 8044{"name":"Kyrgyzstan","alpha-2":"KG","alpha-3":"KGZ"}, 8045{"name":"Lao People's Democratic Republic","alpha-2":"LA","alpha-3":"LAO"}, 8046{"name":"Latvia","alpha-2":"LV","alpha-3":"LVA"}, 8047{"name":"Lebanon","alpha-2":"LB","alpha-3":"LBN"}, 8048{"name":"Lesotho","alpha-2":"LS","alpha-3":"LSO"}, 8049{"name":"Liberia","alpha-2":"LR","alpha-3":"LBR"}, 8050{"name":"Libya","alpha-2":"LY","alpha-3":"LBY"}, 8051{"name":"Liechtenstein","alpha-2":"LI","alpha-3":"LIE"}, 8052{"name":"Lithuania","alpha-2":"LT","alpha-3":"LTU"}, 8053{"name":"Luxembourg","alpha-2":"LU","alpha-3":"LUX"}, 8054{"name":"Macao","alpha-2":"MO","alpha-3":"MAC"}, 8055{"name":"Macedonia, the former Yugoslav Republic of","alpha-2":"MK","alpha-3":"MKD"}, 8056{"name":"Madagascar","alpha-2":"MG","alpha-3":"MDG"}, 8057{"name":"Malawi","alpha-2":"MW","alpha-3":"MWI"}, 8058{"name":"Malaysia","alpha-2":"MY","alpha-3":"MYS"}, 8059{"name":"Maldives","alpha-2":"MV","alpha-3":"MDV"}, 8060{"name":"Mali","alpha-2":"ML","alpha-3":"MLI"}, 8061{"name":"Malta","alpha-2":"MT","alpha-3":"MLT"}, 8062{"name":"Marshall Islands","alpha-2":"MH","alpha-3":"MHL"}, 8063{"name":"Martinique","alpha-2":"MQ","alpha-3":"MTQ"}, 8064{"name":"Mauritania","alpha-2":"MR","alpha-3":"MRT"}, 8065{"name":"Mauritius","alpha-2":"MU","alpha-3":"MUS"}, 8066{"name":"Mayotte","alpha-2":"YT","alpha-3":"MYT"}, 8067{"name":"Mexico","alpha-2":"MX","alpha-3":"MEX"}, 8068{"name":"Micronesia, Federated States of","alpha-2":"FM","alpha-3":"FSM"}, 8069{"name":"Moldova, Republic of","alpha-2":"MD","alpha-3":"MDA"}, 8070{"name":"Monaco","alpha-2":"MC","alpha-3":"MCO"}, 8071{"name":"Mongolia","alpha-2":"MN","alpha-3":"MNG"}, 8072{"name":"Montenegro","alpha-2":"ME","alpha-3":"MNE"}, 8073{"name":"Montserrat","alpha-2":"MS","alpha-3":"MSR"}, 8074{"name":"Morocco","alpha-2":"MA","alpha-3":"MAR"}, 8075{"name":"Mozambique","alpha-2":"MZ","alpha-3":"MOZ"}, 8076{"name":"Myanmar","alpha-2":"MM","alpha-3":"MMR"}, 8077{"name":"Namibia","alpha-2":"NA","alpha-3":"NAM"}, 8078{"name":"Nauru","alpha-2":"NR","alpha-3":"NRU"}, 8079{"name":"Nepal","alpha-2":"NP","alpha-3":"NPL"}, 8080{"name":"Netherlands","alpha-2":"NL","alpha-3":"NLD"}, 8081{"name":"New Caledonia","alpha-2":"NC","alpha-3":"NCL"}, 8082{"name":"New Zealand","alpha-2":"NZ","alpha-3":"NZL"}, 8083{"name":"Nicaragua","alpha-2":"NI","alpha-3":"NIC"}, 8084{"name":"Niger","alpha-2":"NE","alpha-3":"NER"}, 8085{"name":"Nigeria","alpha-2":"NG","alpha-3":"NGA"}, 8086{"name":"Niue","alpha-2":"NU","alpha-3":"NIU"}, 8087{"name":"Norfolk Island","alpha-2":"NF","alpha-3":"NFK"}, 8088{"name":"Northern Mariana Islands","alpha-2":"MP","alpha-3":"MNP"}, 8089{"name":"Norway","alpha-2":"NO","alpha-3":"NOR"}, 8090{"name":"Oman","alpha-2":"OM","alpha-3":"OMN"}, 8091{"name":"Pakistan","alpha-2":"PK","alpha-3":"PAK"}, 8092{"name":"Palau","alpha-2":"PW","alpha-3":"PLW"}, 8093{"name":"Palestine, State of","alpha-2":"PS","alpha-3":"PSE"}, 8094{"name":"Panama","alpha-2":"PA","alpha-3":"PAN"}, 8095{"name":"Papua New Guinea","alpha-2":"PG","alpha-3":"PNG"}, 8096{"name":"Paraguay","alpha-2":"PY","alpha-3":"PRY"}, 8097{"name":"Peru","alpha-2":"PE","alpha-3":"PER"}, 8098{"name":"Philippines","alpha-2":"PH","alpha-3":"PHL"}, 8099{"name":"Pitcairn","alpha-2":"PN","alpha-3":"PCN"}, 8100{"name":"Poland","alpha-2":"PL","alpha-3":"POL"}, 8101{"name":"Portugal","alpha-2":"PT","alpha-3":"PRT"}, 8102{"name":"Puerto Rico","alpha-2":"PR","alpha-3":"PRI"}, 8103{"name":"Qatar","alpha-2":"QA","alpha-3":"QAT"}, 8104{"name":"Réunion","alpha-2":"RE","alpha-3":"REU"}, 8105{"name":"Romania","alpha-2":"RO","alpha-3":"ROU"}, 8106{"name":"Russian Federation","alpha-2":"RU","alpha-3":"RUS"}, 8107{"name":"Rwanda","alpha-2":"RW","alpha-3":"RWA"}, 8108{"name":"Saint Barthélemy","alpha-2":"BL","alpha-3":"BLM"}, 8109{"name":"Saint Helena, Ascension and Tristan da Cunha","alpha-2":"SH","alpha-3":"SHN"}, 8110{"name":"Saint Kitts and Nevis","alpha-2":"KN","alpha-3":"KNA"}, 8111{"name":"Saint Lucia","alpha-2":"LC","alpha-3":"LCA"}, 8112{"name":"Saint Martin (French part)","alpha-2":"MF","alpha-3":"MAF"}, 8113{"name":"Saint Pierre and Miquelon","alpha-2":"PM","alpha-3":"SPM"}, 8114{"name":"Saint Vincent and the Grenadines","alpha-2":"VC","alpha-3":"VCT"}, 8115{"name":"Samoa","alpha-2":"WS","alpha-3":"WSM"}, 8116{"name":"San Marino","alpha-2":"SM","alpha-3":"SMR"}, 8117{"name":"Sao Tome and Principe","alpha-2":"ST","alpha-3":"STP"}, 8118{"name":"Saudi Arabia","alpha-2":"SA","alpha-3":"SAU"}, 8119{"name":"Senegal","alpha-2":"SN","alpha-3":"SEN"}, 8120{"name":"Serbia","alpha-2":"RS","alpha-3":"SRB"}, 8121{"name":"Seychelles","alpha-2":"SC","alpha-3":"SYC"}, 8122{"name":"Sierra Leone","alpha-2":"SL","alpha-3":"SLE"}, 8123{"name":"Singapore","alpha-2":"SG","alpha-3":"SGP"}, 8124{"name":"Sint Maarten (Dutch part)","alpha-2":"SX","alpha-3":"SXM"}, 8125{"name":"Slovakia","alpha-2":"SK","alpha-3":"SVK"}, 8126{"name":"Slovenia","alpha-2":"SI","alpha-3":"SVN"}, 8127{"name":"Solomon Islands","alpha-2":"SB","alpha-3":"SLB"}, 8128{"name":"Somalia","alpha-2":"SO","alpha-3":"SOM"}, 8129{"name":"South Africa","alpha-2":"ZA","alpha-3":"ZAF"}, 8130{"name":"South Georgia and the South Sandwich Islands","alpha-2":"GS","alpha-3":"SGS"}, 8131{"name":"South Sudan","alpha-2":"SS","alpha-3":"SSD"}, 8132{"name":"Spain","alpha-2":"ES","alpha-3":"ESP"}, 8133{"name":"Sri Lanka","alpha-2":"LK","alpha-3":"LKA"}, 8134{"name":"Sudan","alpha-2":"SD","alpha-3":"SDN"}, 8135{"name":"Suriname","alpha-2":"SR","alpha-3":"SUR"}, 8136{"name":"Svalbard and Jan Mayen","alpha-2":"SJ","alpha-3":"SJM"}, 8137{"name":"Swaziland","alpha-2":"SZ","alpha-3":"SWZ"}, 8138{"name":"Sweden","alpha-2":"SE","alpha-3":"SWE"}, 8139{"name":"Switzerland","alpha-2":"CH","alpha-3":"CHE"}, 8140{"name":"Syrian Arab Republic","alpha-2":"SY","alpha-3":"SYR"}, 8141{"name":"Taiwan, Province of China","alpha-2":"TW","alpha-3":"TWN"}, 8142{"name":"Tajikistan","alpha-2":"TJ","alpha-3":"TJK"}, 8143{"name":"Tanzania, United Republic of","alpha-2":"TZ","alpha-3":"TZA"}, 8144{"name":"Thailand","alpha-2":"TH","alpha-3":"THA"}, 8145{"name":"Timor-Leste","alpha-2":"TL","alpha-3":"TLS"}, 8146{"name":"Togo","alpha-2":"TG","alpha-3":"TGO"}, 8147{"name":"Tokelau","alpha-2":"TK","alpha-3":"TKL"}, 8148{"name":"Tonga","alpha-2":"TO","alpha-3":"TON"}, 8149{"name":"Trinidad and Tobago","alpha-2":"TT","alpha-3":"TTO"}, 8150{"name":"Tunisia","alpha-2":"TN","alpha-3":"TUN"}, 8151{"name":"Turkey","alpha-2":"TR","alpha-3":"TUR"}, 8152{"name":"Turkmenistan","alpha-2":"TM","alpha-3":"TKM"}, 8153{"name":"Turks and Caicos Islands","alpha-2":"TC","alpha-3":"TCA"}, 8154{"name":"Tuvalu","alpha-2":"TV","alpha-3":"TUV"}, 8155{"name":"Uganda","alpha-2":"UG","alpha-3":"UGA"}, 8156{"name":"Ukraine","alpha-2":"UA","alpha-3":"UKR"}, 8157{"name":"United Arab Emirates","alpha-2":"AE","alpha-3":"ARE"}, 8158{"name":"United Kingdom","alpha-2":"GB","alpha-3":"GBR"}, 8159{"name":"United States","alpha-2":"US","alpha-3":"USA"}, 8160{"name":"United States Minor Outlying Islands","alpha-2":"UM","alpha-3":"UMI"}, 8161{"name":"Uruguay","alpha-2":"UY","alpha-3":"URY"}, 8162{"name":"Uzbekistan","alpha-2":"UZ","alpha-3":"UZB"}, 8163{"name":"Vanuatu","alpha-2":"VU","alpha-3":"VUT"}, 8164{"name":"Venezuela, Bolivarian Republic of","alpha-2":"VE","alpha-3":"VEN"}, 8165{"name":"Viet Nam","alpha-2":"VN","alpha-3":"VNM"}, 8166{"name":"Virgin Islands, British","alpha-2":"VG","alpha-3":"VGB"}, 8167{"name":"Virgin Islands, U.S.","alpha-2":"VI","alpha-3":"VIR"}, 8168{"name":"Wallis and Futuna","alpha-2":"WF","alpha-3":"WLF"}, 8169{"name":"Western Sahara","alpha-2":"EH","alpha-3":"ESH"}, 8170{"name":"Yemen","alpha-2":"YE","alpha-3":"YEM"}, 8171{"name":"Zambia","alpha-2":"ZM","alpha-3":"ZMB"}, 8172{"name":"Zimbabwe","alpha-2":"ZW","alpha-3":"ZWE"} 8173]; 8174 8175 8176 8177/********* 8178* ../app/js/common/ajax.js 8179**********/ 8180sofia.ajax = function(params) { 8181 8182 var $params = $.extend(true, {}, params); 8183 8184 if ($params.url.indexOf('http') != 0) { 8185 8186 $params.url = 8187 // base URL 8188 sofia.config.baseContentUrl + 8189 // API settings 8190 (sofia.config.baseContentApiPath != '' ? sofia.config.baseContentApiPath + '?key=' + sofia.config.baseContentApiKey + '&action=' : '') + 8191 // then plain URL 8192 $params.url; 8193 8194 // add type overrides if possible 8195 if ($params.dataType == 'json') { 8196 $params.beforeSend = function(xhr){ 8197 if (xhr.overrideMimeType){ 8198 xhr.overrideMimeType('application/' + (sofia.config.baseContentApiPath != '' ? 'javascript' : 'json')); 8199 } 8200 } 8201 } 8202 8203 // change setting 8204 if (sofia.config.baseContentApiPath != '') { 8205 $params.dataType = 'jsonp'; 8206 } 8207 } 8208 8209 $params.success = function(data) { 8210 8211 if (params.success) { 8212 8213 // convert back to text 8214 if ((params.dataType == 'text' || params.dataType == 'html') && sofia.config.baseContentApiPath != '') { 8215 data = data.text; 8216 } 8217 8218 params.success(data); 8219 } 8220 }; 8221 8222 $params.error = function() { 8223 8224 if (params.error) { 8225 params.error.apply(null, arguments); 8226 } 8227 8228 }; 8229 8230 // run through jQuery 8231 $.ajax($params); 8232}; 8233 8234 8235 8236/********* 8237* ../app/js/common/appsettings.js 8238**********/ 8239/****************** 8240ReaderSettings 8241*******************/ 8242var AppSettings = (function() { 8243 var storage = {}; 8244 8245 function testlocalStorage() { 8246 try { 8247 window.localStorage.setItem('1', '2'); 8248 if (localStorage.getItem('1') !== '2') { 8249 return false; 8250 } 8251 window.localStorage.removeItem('1'); 8252 return localStorage.getItem('1') !== '2'; 8253 } catch(e) { 8254 return false; 8255 } 8256 } 8257 8258 if (testlocalStorage()) { 8259 storage = window.localStorage; 8260 } 8261 8262 // create me 8263 function getValue(key, defaultValue) { 8264 key = sofia.config.settingsPrefix + key; 8265 8266 // console.log('getValue', key, defaultValue); 8267 8268 var returnValue = {}, 8269 storedValue = null; 8270 8271 8272 // put all default values on the value object 8273 for (var objkey in defaultValue) { 8274 returnValue[objkey] = defaultValue[objkey]; 8275 } 8276 8277 //console.log('default', returnValue); 8278 8279 storedValue = storage[key]; 8280 8281 //console.log('storedValue', 'key:' + key, storedValue); 8282 8283 if (storedValue == null) { 8284 return returnValue; 8285 } else { 8286 try { 8287 storedValue = JSON.parse(storedValue); 8288 } catch (ex) { 8289 8290 } 8291 } 8292 8293 //console.log('storedValue', storedValue); 8294 8295 for (var objkey in storedValue) { 8296 returnValue[objkey] = storedValue[objkey]; 8297 } 8298 8299 //console.log('combined', returnValue); 8300 8301 return returnValue; 8302 } 8303 8304 function setValue(key, value) { 8305 8306 key = sofia.config.settingsPrefix + key; 8307
8308 // console.log('STORE', 'key:' + key, value); 8309 storage[key] = JSON.stringify(value); 8310 } 8311 8312 /* From QuirksMode */ 8313 function getCookieValue(name) { 8314 var nameEQ = name + "="; 8315 var ca = document.cookie.split(';'); 8316 for(var i = 0; i < ca.length; i++) { 8317 var c = ca[i]; 8318 while (c.charAt(0) == ' ') c = c.substring(1, c.length); 8319 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); 8320 } 8321 return null; 8322 } 8323 8324 return { 8325 getValue: getValue, 8326 setValue: setValue, 8327 getCookieValue: getCookieValue 8328 } 8329 8330})(); 8331 8332 8333 8334/********* 8335* ../app/js/common/eventlistener.js 8336**********/ 8337var EventEmitter = { 8338 events: [], 8339 8340 addEventListener: function (eventName, callback, bubble) { 8341 this.events[eventName] = this.events[eventName] || []; 8342 this.events[eventName].push(callback); 8343 }, 8344 removeEventListener: function (eventName, callback) { 8345 if (!eventName) { this.events = {}; return true; } 8346 var callbacks = this.events[eventName]; 8347 if (!callbacks) return true; 8348 if (!callback) { this.events[eventName] = []; return true; } 8349 for (i = 0; i < callbacks.length; i++) { 8350 if (callbacks[i] === callback) { 8351 this.events[eventName].splice(i, 1); 8352 return true; 8353 } 8354 } 8355 return false; 8356 }, 8357 dispatchEvent: function (eventName) { 8358 var i, 8359 args, 8360 callbacks = this.events[eventName]; 8361 8362 if (callbacks) { 8363 args = Array.prototype.slice.call(arguments, 1); 8364 for (i = 0; i < callbacks.length; i++) { 8365 callbacks[i].apply(null, args); 8366 } 8367 } 8368 }, 8369 8370 clearListeners: function() { 8371 this.events = []; 8372 }, 8373 8374 // jQuery like names 8375 on: function(eventName, callback, bubble) { 8376 this.addEventListener.call(this, eventName, callback, bubble); 8377 }, 8378 trigger: function(eventName, value) { 8379 this.dispatchEvent.call(this, eventName, value); 8380 } 8381}; 8382 8383 8384 8385/********* 8386* ../app/js/common/timer.js 8387**********/ 8388 8389var Timer = function(callback, seconds) { 8390 8391 var timeoutValue = null; 8392 function start() { 8393 if (timeoutValue == null) { 8394 clear(); 8395 8396 timeoutValue = setTimeout(function() { 8397 callback(); 8398 clear(); 8399 }, seconds); 8400 } 8401 } 8402 function clear() { 8403 if (timeoutValue != null) { 8404 clearTimeout(timeoutValue); 8405 timeoutValue = null; 8406 } 8407 } 8408 8409 return { 8410 start: start, 8411 clear: clear 8412 } 8413}; 8414 8415 8416 8417/********* 8418* ../app/js/common/stringutility.js 8419**********/ 8420var stringUtility = { 8421 8422 // get from URL 8423 parseQuerystring: function (querystring) { 8424 8425 querystring = querystring || window.location.search.substring(1); 8426 8427 // remove any preceding url and split 8428 querystring = querystring.substring(querystring.indexOf('?')+1).split('&'); 8429 var params = {}, pair, d = decodeURIComponent; 8430 // march and parse 8431 for (var i = querystring.length - 1; i >= 0; i--) { 8432 pair = querystring[i].split('='); 8433 params[d(pair[0])] = d(pair[1]); 8434 } 8435 8436 return params; 8437 } 8438}; 8439 8440 8441 8442/********* 8443* ../app/js/common/detection.js 8444**********/ 8445var Detection = (function() { 8446 var detection = {}; 8447 8448 detection.hasTouch = 'ontouchend' in document; 8449 8450 /* 8451 detection.hasFlash = (function() { 8452 if (navigator.plugins != null && navigator.plugins.length > 0){ 8453 return navigator.plugins["Shockwave Flash"] && true; 8454 } 8455 if (~navigator.userAgent.toLowerCase().indexOf("webtv")){ 8456 return true; 8457 } 8458 if ((~navigator.appVersion.indexOf("MSIE") || ~navigator.appVersion.indexOf("Trident")) && !~navigator.userAgent.indexOf("Opera")){ 8459 try { 8460 return new ActiveXObject("ShockwaveFlash.ShockwaveFlash") && true; 8461 } catch(e) {} 8462 } 8463 return false; 8464 })(); 8465 */ 8466 8467 8468 return detection; 8469})(); 8470 8471if (!Array.prototype.filter) { 8472 Array.prototype.filter = function(fun /*, thisp */) { 8473 "use strict"; 8474 8475 if (this === void 0 || this === null) 8476 throw new TypeError(); 8477 8478 var t = Object(this); 8479 var len = t.length >>> 0; 8480 if (typeof fun !== "function") 8481 throw new TypeError(); 8482 8483 var res = []; 8484 var thisp = arguments[1]; 8485 for (var i = 0; i < len; i++) { 8486 if (i in t) { 8487 var val = t[i]; // in case fun mutates this 8488 if (fun.call(thisp, val, i, t)) 8489 res.push(val); 8490 } 8491 } 8492 8493 return res; 8494 }; 8495} 8496 8497if (!Array.prototype.indexOf) { 8498 Array.prototype.indexOf = function(obj, start) { 8499 for (var i = (start || 0), j = this.length; i < j; i++) { 8500 if (this[i] === obj) { 8501 return i; 8502 } 8503 } 8504 return -1; 8505 }; 8506} 8507 8508if(typeof String.prototype.trim !== 'function') { 8509 String.prototype.trim = function() { 8510 return this.replace(/^\s+|\s+$/g, ''); 8511 }; 8512}; 8513 8514 8515 8516/********* 8517* ../app/js/common/navigation.js 8518**********/ 8519/* temporarily stores locations when settings are changed */ 8520var PlaceKeeper = (function() { 8521 8522 var currentWindow = null, 8523 currentData = null; 8524 8525 function storePlace() { 8526 currentData = getFirstLocation(); 8527 } 8528 8529 function getFirstLocation() { 8530 currentWindow = (sofia.app.windowManager) ? 8531 sofia.app.windowManager.getWindows().filter(function(w) { return w.className == 'BibleWindow'})[0] : 8532 null; 8533 8534 var data = (currentWindow != null) ? currentWindow.getData() : null; 8535 8536 return data; 8537 } 8538 8539 function restorePlace() { 8540 if (currentWindow != null) { 8541 // 8542 //firstWindow.trigger('message', {type:'globalmessage', messagetype: 'nav', }) 8543 8544 currentWindow.trigger('globalmessage', { 8545 type: 'globalmessage', 8546 target: currentWindow, 8547 data: { 8548 messagetype: 'nav', 8549 type: 'bible', 8550 locationInfo: currentData // firstWindow.getData() 8551 } 8552 }); 8553 } 8554 8555 } 8556 8557 return { 8558 storePlace: storePlace, 8559 restorePlace: restorePlace, 8560 getFirstLocation: getFirstLocation 8561 } 8562 8563})(); 8564 8565/* stores locations for back/foward buttons on browser */ 8566var TextNavigation = (function() { 8567 8568 var locations = [], 8569 locationIndex = -1; 8570 8571 $(window).on('popstate', handleBrowserNavigation); 8572 8573 function handleBrowserNavigation(e) { 8574 console.log('handleNavigation', e, e.state); 8575 8576 if (e.originalEvent && e.originalEvent.state != null && typeof e.originalEvent.state.locationid != 'undefined') { 8577 8578 var newlocationid = e.originalEvent.state.locationid, 8579 type = ''; 8580 8581 8582 if (locationIndex-1 > -1 && locations[locationIndex-1] == newlocationid) { 8583 locationIndex--; 8584 type = 'back'; 8585 } else if (locationIndex+1 < locations.length && locations[locationIndex+1] == newlocationid) { 8586 locationIndex++; 8587 type = 'forward'; 8588 } 8589 8590 setLocation(newlocationid); 8591 8592 textNavigation.trigger('locationchange', {type: type}); 8593 } 8594 8595 } 8596 8597 8598 // upon startup, store the first place 8599 function firstState(locationid) { 8600 console.log('firststate',locationid); 8601 8602 locations.push(locationid); 8603 locationIndex = 0; 8604 8605 window.history.replaceState({"locationid": locationid}, null, window.location.href); 8606 } 8607 8608 // each additional change 8609 function locationChange(locationid, type) { 8610 8611 // slice off anything after this one 8612 while (locationIndex > locations.length-1) { 8613 locations.pop(); 8614 } 8615 8616 locations.push(locationid); 8617 locationIndex++; 8618 8619 window.history.pushState({"locationid": locationid}, null, window.location.href); 8620 8621 8622 textNavigation.trigger('locationchange', {type: type}); 8623 } 8624 8625 8626 function setLocation(locationid) { 8627 // break off and be sure to add the trailing verse 1 (JN1_1) 8628 var fragmentid = locationid.indexOf('_') > -1 ? locationid : locationid + '_1', 8629 sectionid = fragmentid.split('_')[0]; 8630 8631 sofia.app.handleGlobalMessage({ 8632 type: 'globalmessage', 8633 target: this, 8634 data: { 8635 messagetype:'nav', 8636 type: 'bible', 8637 locationInfo: { 8638 fragmentid: fragmentid, 8639 sectionid: sectionid, 8640 offset: 0 8641 } 8642 } 8643 }); 8644 } 8645 8646 function back() { 8647 8648 window.history.go(-1); 8649 8650 /* 8651 if (locationIndex > 0) { 8652 locationIndex--; 8653 var locationid = locations[locationIndex]; 8654 setLocation(locationid); 8655 } 8656 */ 8657 } 8658 8659 function forward() { 8660 8661 window.history.go(1); 8662 8663 8664 /* 8665 if (locationIndex < locations.length-1) { 8666 // get this location 8667 locationIndex++; 8668 var locationid = locations[locationIndex]; 8669 setLocation(locationid); 8670 } 8671 */ 8672 } 8673 8674 function getLocations() { 8675 return locations; 8676 } 8677 8678 function getLocationIndex() { 8679 return locationIndex; 8680 } 8681 8682 8683 var textNavigation = { 8684 locationChange: locationChange, 8685 firstState: firstState, 8686 forward: forward, 8687 back: back, 8688 getLocationIndex: getLocationIndex, 8689 getLocations: getLocations 8690 } 8691 8692 textNavigation = $.extend(true, textNavigation, EventEmitter); 8693 8694 return textNavigation; 8695})(); 8696 8697 8698 8699/********* 8700* ../app/js/common/iso2iana.js 8701**********/ 8702var iso2iana = { 8703 // These are languages for which at least some browsers have hyphenation 8704 // dictionaries for listed under ISO-639-1 codes for which ISO-639-2 codes 8705 // are used in Bible modules and other data we process. 8706 data: { 8707 "afr": "af", 8708 "arz": "ar", 8709 "bul": "bg", 8710 "cat": "ca", 8711 "cym": "cy", 8712 "dan": "da", 8713 "dut": "nl", 8714 "eng": "en", 8715 "epo": "eo", 8716 "est": "et", 8717 "fin": "fi", 8718 "fra": "fr", 8719 "fre": "fr", 8720 "glg": "gl", 8721 "grc": "el", 8722 "grk": "el", 8723 "heb": "he", 8724 "hun": "hu", 8725 "ice": "is", 8726 "ina": "ia", 8727 "isl": "is", 8728 "ita": "it", 8729 "lat": "la", 8730 "lit": "lt", 8731 "mon": "mn", 8732 "nld": "nl", 8733 "nno": "nn", 8734 "nob": "nb", 8735 "por": "pt", 8736 "rus": "ru", 8737 "slv": "sl", 8738 "spa": "es", 8739 "swe": "sv", 8740 "tur": "tr", 8741 "ukr": "uk", 8742 "wel": "cy" 8743 }, 8744 8745 // Return the language code needed for browsers to properly process lang= 8746 // attributes. If we don't recognize a mapping, just return the original 8747 convert: function(iso) { 8748 var iana = (typeof this.data[iso] === 'undefined') ? iso : this.data[iso]; 8749 return iana; 8750 } 8751}; 8752 8753 8754 8755/********* 8756* ../app/js/common/clickoff.js 8757**********/ 8758var ClickOff = { 8759 8760 clickoffid: '', 8761 clickTargets:[], 8762 8763 getClickTargets: function() { 8764 return this.clickTargets; 8765 }, 8766 8767 setClickTargets: function(clickTargets) { 8768 this.clickTargets = clickTargets; 8769 this.clearOffClickTimer(); 8770 }, 8771 8772 docClick: function(e) { 8773 8774 var target = $(e.target), 8775 clickedOnThis = false; 8776 8777 // go through all nested clicked elements 8778 while (target != null && target.length > 0) { 8779 8780 for (var i=0, il=this.clickTargets.length; i<il; i++) { 8781 var clickTarget = this.clickTargets[i];
8782 8783 if (target[0] == clickTarget[0]) { 8784 clickedOnThis = true; 8785 break; 8786 } 8787 } 8788 8789 if (clickedOnThis) { 8790 break; 8791 } 8792 8793 target = target.parent(); 8794 } 8795 8796 console.log('clickoff', this.clickoffid, 'doc.click', clickedOnThis); 8797 8798 if (!clickedOnThis) { 8799 //e.preventDefault(); 8800 8801 //this.startOffClickTimer(); 8802 this.trigger('offclick'); 8803 8804 //$(document).off('click', this.docClick); 8805 this.unbindDocClick(); 8806 8807 //return false; 8808 } 8809 }, 8810 8811 offClickTimeout: null, 8812 8813 startOffClickTimer: function () { 8814 var t = this; 8815 8816 this.clearOffClickTimer(); 8817 this.offClickTimeout = setTimeout(function() { 8818 if (t.trigger) { 8819 t.trigger('offclick'); 8820 } 8821 }, 50); 8822 }, 8823 8824 clearOffClickTimer: function () { 8825 if (this.offClickTimeout != null) { 8826 clearTimeout(this.offClickTimeout); 8827 this.offClickTimeout = null; 8828 } 8829 }, 8830 8831 onshow: function() { 8832 console.log('clickoff', this.clickoffid, 'onshow'); 8833 8834 this.bindDocClick(); 8835 }, 8836 8837 onhide: function() { 8838 console.log('clickoff', this.clickoffid, 'onhide'); 8839 8840 this.clearOffClickTimer(); 8841 this.unbindDocClick(); 8842 }, 8843 8844 bindDocClick: function() { 8845 this.unbindDocClick(); 8846 8847 var proxy = this.proxy = $.proxy( this, 'docClick' ); 8848 8849 $(document).on('click.' + this.clickoffid, proxy); 8850 }, 8851 8852 unbindDocClick: function() { 8853 if (this.proxy) { 8854 $(document).off('click.' + this.clickoffid, this.proxy); 8855 } 8856 } 8857 8858}; 8859 8860 8861 8862/********* 8863* ../app/js/bible/bible.data.js 8864**********/ 8865/** 8866 * Bible namespace, Bible chapter/verse statistics, Book names and abbreviations 8867 * 8868 * @license MIT/GLPv2 8869 * @author John Dyer (http://j.hn/) 8870 */ 8871 8872if (typeof window.bible == 'undefined') 8873 window.bible = {}; 8874 8875/** 8876 * @description ID with chapter/verse statistics and English names 8877 */ 8878bible.BOOK_DATA = { 8879"FR":{"name":"Front matter","sortOrder":0,"shortCode":"FR","usfm":"FRT","osis":"Preface", 8880 "chapters":[null], 8881 "names":{"eng":["Front matter"]}}, 8882"IN":{"name":"Introduction","sortOrder":1,"shortCode":"IN","usfm":"INT","osis":"Intro", 8883 "chapters":[null], 8884 "names":{"eng":["Introduction"]}}, 8885"GN":{"name":"Genesis","sortOrder":2,"shortCode":"GN","usfm":"GEN","osis":"Gen","section":"penta", 8886 "chapters":[31,25,24,26,32,22,24,22,29,32,32,20,18,24,21,16,27,33,38,18,34,24,20,67,34,35,46,22,35,43,55,32,20,31,29,43,36,30,23,23,57,38,34,34,28,34,31,22,33,26], 8887 "names":{"eng":["Genesis","Ge","Gen"]}}, 8888"EX":{"name":"Exodus","sortOrder":3,"shortCode":"EX","usfm":"EXO","osis":"Exod","section":"penta", 8889 "chapters":[22,25,22,31,23,30,25,32,35,29,10,51,22,31,27,36,16,27,25,26,36,31,33,18,40,37,21,43,46,38,18,35,23,35,35,38,29,31,43,38], 8890 "names":{"eng":["Exodus","Ex","Exo"]}}, 8891"LV":{"name":"Leviticus","sortOrder":4,"shortCode":"LV","usfm":"LEV","osis":"Lev","section":"penta", 8892 "chapters":[17,16,17,35,19,30,38,36,24,20,47,8,59,57,33,34,16,30,37,27,24,33,44,23,55,46,34], 8893 "names":{"eng":["Leviticus","Le","Lev"]}}, 8894"NU":{"name":"Numbers","sortOrder":5,"shortCode":"NU","usfm":"NUM","osis":"Num","section":"penta", 8895 "chapters":[54,34,51,49,31,27,89,26,23,36,35,16,33,45,41,50,13,32,22,29,35,41,30,25,18,65,23,31,40,16,54,42,56,29,34,13], 8896 "names":{"eng":["Numbers","Nu","Num"]}}, 8897"DT":{"name":"Deuteronomy","sortOrder":6,"shortCode":"DT","usfm":"DEU","osis":"Deut","section":"penta", 8898 "chapters":[46,37,29,49,33,25,26,20,29,22,32,32,18,29,23,22,20,22,21,20,23,30,25,22,19,19,26,68,29,20,30,52,29,12], 8899 "names":{"eng":["Deuteronomy","Dt","Deut","Deu","De"]}}, 8900"JS":{"name":"Joshua","sortOrder":7,"shortCode":"JS","usfm":"JOS","osis":"Josh","section":"hist", 8901 "chapters":[18,24,17,24,15,27,26,35,27,43,23,24,33,15,63,10,18,28,51,9,45,34,16,33], 8902 "names":{"eng":["Joshua","Js","Jos","Jos","Josh"]}}, 8903"JG":{"name":"Judges","sortOrder":8,"shortCode":"JG","usfm":"JDG","osis":"Judg","section":"hist", 8904 "chapters":[36,23,31,24,31,40,25,35,57,18,40,15,25,20,20,31,13,31,30,48,25], 8905 "names":{"eng":["Judges","Jg","Jdg","Jdgs"]}}, 8906"RT":{"name":"Ruth","sortOrder":9,"shortCode":"RT","usfm":"RUT","osis":"Ruth","section":"hist", 8907 "chapters":[22,23,18,22], 8908 "names":{"eng":["Ruth","Ru","Rut"]}}, 8909"S1":{"name":"The First Book of Samuel","sortOrder":10,"shortCode":"S1","usfm":"1SA","osis":"1Sam","section":"hist", 8910 "chapters":[28,36,21,22,12,21,17,22,27,27,15,25,23,52,35,23,58,30,24,42,15,23,29,22,44,25,12,25,11,31,13], 8911 "names":{"eng":["1 Samuel","1S","1 Sam","1Sam","1 Sa","1Sa","I Samuel","I Sam","I Sa"]}}, 8912"S2":{"name":"The Second Book of Samuel","sortOrder":11,"shortCode":"S2","usfm":"2SA","osis":"2Sam","section":"hist", 8913 "chapters":[27,32,39,12,25,23,29,18,13,19,27,31,39,33,37,23,29,33,43,26,22,51,39,25], 8914 "names":{"eng":["2 Samuel","2S","2 Sam","2Sam","2 Sa","2Sa","II Samuel","II Sam","II Sa","IIS"]}}, 8915"K1":{"name":"The First Book of Kings","sortOrder":12,"shortCode":"K1","usfm":"1KI","osis":"1Kgs","section":"hist", 8916 "chapters":[53,46,28,34,18,38,51,66,28,29,43,33,34,31,34,34,24,46,21,43,29,53], 8917 "names":{"eng":["1 Kings","1K","1 Kin","1Kin","1 Ki","IK","1Ki","I Kings","I Kin","I Ki"]}}, 8918"K2":{"name":"The Second Book of Kings","sortOrder":13,"shortCode":"K2","usfm":"2KI","osis":"2Kgs","section":"hist", 8919 "chapters":[18,25,27,44,27,33,20,29,37,36,21,21,25,29,38,20,41,37,37,21,26,20,37,20,30], 8920 "names":{"eng":["2 Kings","2K","2 Kin","2Kin","2 Ki","IIK","2Ki","II Kings","II Kin","II Ki"]}}, 8921"R1":{"name":"The First Book of Chronicles","sortOrder":14,"shortCode":"R1","usfm":"1CH","osis":"1Chr","section":"hist", 8922 "chapters":[54,55,24,43,26,81,40,40,44,14,47,40,14,17,29,43,27,17,19,8,30,19,32,31,31,32,34,21,30], 8923 "names":{"eng":["1 Chronicles","1Ch","1 Chr","1Chr","1 Ch","ICh","I Chronicles","I Chr","I Ch"]}}, 8924"R2":{"name":"The Second Book of Chronicles","sortOrder":15,"shortCode":"R2","usfm":"2CH","osis":"2Chr","section":"hist", 8925 "chapters":[17,18,17,22,14,42,22,18,31,19,23,16,22,15,19,14,19,34,11,37,20,12,21,27,28,23,9,27,36,27,21,33,25,33,27,23], 8926 "names":{"eng":["2 Chronicles","2Ch","2 Chr","2 Chr","2Chr","2 Ch","IICh","II Chronicles","II Chr","II Ch"]}}, 8927"ER":{"name":"Ezra","sortOrder":16,"shortCode":"ER","usfm":"EZR","osis":"Ezra","section":"hist", 8928 "chapters":[11,70,13,24,17,22,28,36,15,44], 8929 "names":{"eng":["Ezra","Ezr"]}}, 8930"NH":{"name":"Nehemiah","sortOrder":17,"shortCode":"NH","usfm":"NEH","osis":"Neh","section":"hist", 8931 "chapters":[11,20,32,23,19,19,73,18,38,39,36,47,31],
8932 "names":{"eng":["Nehemiah","Ne","Neh","Neh","Ne"]}}, 8933"ET":{"name":"Esther","sortOrder":18,"shortCode":"ET","usfm":"EST","osis":"Esth","section":"hist", 8934 "chapters":[22,23,15,17,14,14,10,17,32,3], 8935 "names":{"eng":["Esther","Es","Est","Esth"]}}, 8936"JB":{"name":"Job","sortOrder":19,"shortCode":"JB","usfm":"JOB","osis":"Job","section":"poet", 8937 "chapters":[22,13,26,21,27,30,21,22,35,22,20,25,28,22,35,22,16,21,29,29,34,30,17,25,6,14,23,28,25,31,40,22,33,37,16,33,24,41,30,24,34,17], 8938 "names":{"eng":["Job","Jb","Job"]}}, 8939"PS":{"name":"Psalms","sortOrder":20,"shortCode":"PS","usfm":"PSA","osis":"Ps","section":"poet", 8940 "chapters":[6,12,8,8,12,10,17,9,20,18,7,8,6,7,5,11,15,50,14,9,13,31,6,10,22,12,14,9,11,12,24,11,22,22,28,12,40,22,13,17,13,11,5,26,17,11,9,14,20,23,19,9,6,7,23,13,11,11,17,12,8,12,11,10,13,20,7,35,36,5,24,20,28,23,10,12,20,72,13,19,16,8,18,12,13,17,7,18,52,17,16,15,5,23,11,13,12,9,9,5,8,28,22,35,45,48,43,13,31,7,10,10,9,8,18,19,2,29,176,7,8,9,4,8,5,6,5,6,8,8,3,18,3,3,21,26,9,8,24,13,10,7,12,15,21,10,20,14,9,6], 8941 "names":{"eng":["Psalm","Ps","Psa"]}}, 8942"PR":{"name":"Proverbs","sortOrder":21,"shortCode":"PR","usfm":"PRO","osis":"Prov","section":"poet", 8943 "chapters":[33,22,35,27,23,35,27,36,18,32,31,28,25,35,33,33,28,24,29,30,31,29,35,34,28,28,27,28,27,33,31], 8944 "names":{"eng":["Proverbs","Pr","Prov","Pro"]}}, 8945"EC":{"name":"Ecclesiastes","sortOrder":22,"shortCode":"EC","usfm":"ECC","osis":"Eccl","section":"poet", 8946 "chapters":[18,26,22,16,20,12,29,17,18,20,10,14], 8947 "names":{"eng":["Ecclesiastes","Ec","Ecc","Qohelet"]}}, 8948"SS":{"name":"Song of Solomon","sortOrder":23,"shortCode":"SS","usfm":"SNG","osis":"Song","section":"poet", 8949 "chapters":[17,17,11,16,16,13,13,14], 8950 "names":{"eng":["Song of Songs","So","Sos","Song of Solomon","SOS","SongOfSongs","SongofSolomon","Canticle of Canticles"]}}, 8951"IS":{"name":"Isaiah","sortOrder":24,"shortCode":"IS","usfm":"ISA","osis":"Isa","section":"majorp", 8952 "chapters":[31,22,26,6,30,13,25,22,21,34,16,6,22,32,9,14,14,7,25,6,17,25,18,23,12,21,13,29,24,33,9,20,24,17,10,22,38,22,8,31,29,25,28,28,25,13,15,22,26,11,23,15,12,17,13,12,21,14,21,22,11,12,19,12,25,24], 8953 "names":{"eng":["Isaiah","Is","Isa"]}}, 8954"JR":{"name":"Jeremiah","sortOrder":25,"shortCode":"JR","usfm":"JER","osis":"Jer","section":"majorp", 8955 "chapters":[19,37,25,31,31,30,34,22,26,25,23,17,27,22,21,21,27,23,15,18,14,30,40,10,38,24,22,17,32,24,40,44,26,22,19,32,21,28,18,16,18,22,13,30,5,28,7,47,39,46,64,34], 8956 "names":{"eng":["Jeremiah","Je","Jer"]}}, 8957"LM":{"name":"Lamentations","sortOrder":26,"shortCode":"LM","usfm":"LAM","osis":"Lam","section":"poet", 8958 "chapters":[22,22,66,22,22], 8959 "names":{"eng":["Lamentations","La","Lam","Lament"]}}, 8960"EK":{"name":"Ezekiel","sortOrder":27,"shortCode":"EK","usfm":"EZK","osis":"Ezek","section":"majorp", 8961 "chapters":[28,10,27,17,17,14,27,18,11,22,25,28,23,23,8,63,24,32,14,49,32,31,49,27,17,21,36,26,21,26,18,32,33,31,15,38,28,23,29,49,26,20,27,31,25,24,23,35], 8962 "names":{"eng":["Ezekiel","Ek","Ezek","Eze"]}}, 8963"DN":{"name":"Daniel","sortOrder":28,"shortCode":"DN","usfm":"DAN","osis":"Dan","section":"majorp", 8964 "chapters":[21,49,30,37,31,28,28,27,27,21,45,13], 8965 "names":{"eng":["Daniel","Da","Dan","Dl","Dnl"]}}, 8966"HS":{"name":"Hosea","sortOrder":29,"shortCode":"HS","usfm":"HOS","osis":"Hos","section":"minorp", 8967 "chapters":[11,23,5,19,15,11,16,14,17,15,12,14,16,9], 8968 "names":{"eng":["Hosea","Ho","Hos"]}}, 8969"JL":{"name":"Joel","sortOrder":30,"shortCode":"JL","usfm":"JOL","osis":"Joel","section":"minorp", 8970 "chapters":[20,32,21], 8971 "names":{"eng":["Joel","Jl","Joel","Joe"]}}, 8972"AM":{"name":"Amos","sortOrder":31,"shortCode":"AM","usfm":"AMO","osis":"Amos","section":"minorp", 8973 "chapters":[15,16,15,13,27,14,17,14,15], 8974 "names":{"eng":["Amos","Am","Amos","Amo"]}}, 8975"OB":{"name":"Obadiah","sortOrder":32,"shortCode":"OB","usfm":"OBA","osis":"Obad","section":"minorp", 8976 "chapters":[21], 8977 "names":{"eng":["Obadiah","Ob","Oba","Obd","Odbh"]}}, 8978"JH":{"name":"Jonah","sortOrder":33,"shortCode":"JH","usfm":"JON","osis":"Jonah","section":"minorp", 8979 "chapters":[17,10,10,11], 8980 "names":{"eng":["Jonah","Jh","Jon","Jnh"]}}, 8981"MC":{"name":"Micah","sortOrder":34,"shortCode":"MC","usfm":"MIC","osis":"Mic","section":"minorp", 8982 "chapters":[16,13,12,13,15,16,20], 8983 "names":{"eng":["Micah","Mi","Mic"]}}, 8984"NM":{"name":"Nahum","sortOrder":35,"shortCode":"NM","usfm":"NAM","osis":"Nah","section":"minorp", 8985 "chapters":[15,13,19],
8986 "names":{"eng":["Nahum","Na","Nah","Nah","Na"]}}, 8987"HK":{"name":"Habakkuk","sortOrder":36,"shortCode":"HK","usfm":"HAB","osis":"Hab","section":"minorp", 8988 "chapters":[17,20,19], 8989 "names":{"eng":["Habakkuk","Hb","Hab","Hk","Habk"]}}, 8990"ZP":{"name":"Zephaniah","sortOrder":37,"shortCode":"ZP","usfm":"ZEP","osis":"Zeph","section":"minorp", 8991 "chapters":[18,15,20], 8992 "names":{"eng":["Zephaniah","Zp","Zep","Zeph"]}}, 8993"HG":{"name":"Haggai","sortOrder":38,"shortCode":"HG","usfm":"HAG","osis":"Hag","section":"minorp", 8994 "chapters":[15,23], 8995 "names":{"eng":["Haggai","Ha","Hag","Hagg"]}}, 8996"ZC":{"name":"Zechariah","sortOrder":39,"shortCode":"ZC","usfm":"ZEC","osis":"Zech","section":"minorp", 8997 "chapters":[21,13,10,14,11,15,14,23,17,12,17,14,9,21], 8998 "names":{"eng":["Zechariah","Zc","Zech","Zec"]}}, 8999"ML":{"name":"Malachi","sortOrder":40,"shortCode":"ML","usfm":"MAL","osis":"Mal","section":"minorp", 9000 "chapters":[14,17,18,6], 9001 "names":{"eng":["Malachi","Ml","Mal","Mlc"]}}, 9002"TB":{"name":"Tobit","sortOrder":41,"shortCode":"TB","usfm":"TOB","osis":"Tob", 9003 "chapters":[22,14,17,21,22,17,18], 9004 "names":{"eng":["Tobit"]}}, 9005"JT":{"name":"Judith","sortOrder":42,"shortCode":"JT","usfm":"JDT","osis":"Jdt", 9006 "chapters":[16,28,10,15,24,21,32,36,14,23,23,20,20,19,13,25], 9007 "names":{"eng":["Judith"]}}, 9008"EG":{"name":"Esther (Greek)","sortOrder":43,"shortCode":"EG","usfm":"ESG","osis":"EsthGr", 9009 "chapters":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null], 9010 "names":{"eng":["Esther (Greek)"]}}, 9011"AE":{"name":"Additions to Esther","sortOrder":44,"shortCode":"AE","usfm":"ADE","osis":"AddEsth", 9012 "chapters":[], 9013 "names":{"eng":["Additions to Esther"]}}, 9014"WS":{"name":"Wisdom of Solomon","sortOrder":45,"shortCode":"WS","usfm":"WIS","osis":"Wis", 9015 "chapters":[], 9016 "names":{"eng":["Wisdom","Wisdom of Solomon"]}}, 9017"SR":{"name":"Sirach","sortOrder":46,"shortCode":"SR","usfm":"SIR","osis":"Sir", 9018 "chapters":[], 9019 "names":{"eng":["Sirach","Ecclesiasticus"]}}, 9020"BR":{"name":"Baruch","sortOrder":47,"shortCode":"BR","usfm":"BAR","osis":"Bar", 9021 "chapters":[], 9022 "names":{"eng":["Baruch"]}}, 9023"LJ":{"name":"Letter of Jeremiah","sortOrder":48,"shortCode":"LJ","usfm":"LJE","osis":"EpJer", 9024 "chapters":[], 9025 "names":{"eng":["Letter of Jeremiah"]}}, 9026"PA":{"name":"Song of the Three Children","sortOrder":49,"shortCode":"PA","usfm":"S3Y","osis":"PrAzar", 9027 "chapters":[], 9028 "names":{"eng":["Prayer of Azariah"]}}, 9029"SN":{"name":"Susanna","sortOrder":50,"shortCode":"SN","usfm":"SUS","osis":"Sus", 9030 "chapters":[], 9031 "names":{"eng":["Susanna"]}}, 9032"BL":{"name":"Bel and the Dragon","sortOrder":51,"shortCode":"BL","usfm":"BEL","osis":"Bel", 9033 "chapters":[], 9034 "names":{"eng":["Bel and the Dragon"]}}, 9035"M1":{"name":"1 Maccabees","sortOrder":52,"shortCode":"M1","usfm":"1MA","osis":"1Macc", 9036 "chapters":[], 9037 "names":{"eng":["1 Maccabees"]}}, 9038"M2":{"name":"2 Maccabees","sortOrder":53,"shortCode":"M2","usfm":"2MA","osis":"2Macc", 9039 "chapters":[], 9040 "names":{"eng":["2 Maccabees"]}}, 9041"E1":{"name":"1 Esdras","sortOrder":54,"shortCode":"E1","usfm":"1ES","osis":"1Esd", 9042 "chapters":[], 9043 "names":{"eng":["1 Esdras"]}}, 9044"PX":{"name":"Psalm 151","sortOrder":56,"shortCode":"PX","usfm":"PS2","osis":"AddPs", 9045 "chapters":[null], 9046 "names":{"eng":["Psalm 151"]}}, 9047"PN":{"name":"Prayer of Manasseh","sortOrder":56,"shortCode":"PN","usfm":"MAN","osis":"PrMan", 9048 "chapters":[null], 9049 "names":{"eng":["Prayer of Manasseh"]}}, 9050"M3":{"name":"3 Maccabees","sortOrder":57,"shortCode":"M3","usfm":"3MA","osis":"3Macc", 9051 "chapters":[], 9052 "names":{"eng":["3 Maccabees"]}}, 9053"E2":{"name":"2 Esdras","sortOrder":58,"shortCode":"E2","usfm":"2ES","osis":"2Esd", 9054 "chapters":[], 9055 "names":{"eng":["2 Esdras","5 Ezra"]}},
9056"M4":{"name":"4 Maccabees","sortOrder":59,"shortCode":"M4","usfm":"4MA","osis":"4Macc", 9057 "chapters":[], 9058 "names":{"eng":["4 Maccabees"]}}, 9059"OS":{"name":"Odes of Solomon","sortOrder":60,"shortCode":"OS","usfm":"ODS","osis":"OdesSol", 9060 "chapters":[], 9061 "names":{"eng":["Odes of Solomon"]}}, 9062"SP":{"name":"Psalms of Solomon","sortOrder":61,"shortCode":"SP","usfm":"PSS","osis":"PssSol", 9063 "chapters":[], 9064 "names":{"eng":["Psalms of Solomon"]}}, 9065"LL":{"name":"Epistle to the Laodiceans","sortOrder":62,"shortCode":"LL","usfm":"EPL","osis":"EpLao", 9066 "chapters":[], 9067 "names":{"eng":["Epistle to the Laodiceans"]}}, 9068"N1":{"name":"Ethiopic Apocalypse of Enoch","sortOrder":63,"shortCode":"N1","usfm":"1EN","osis":"1En", 9069 "chapters":[], 9070 "names":{"eng":["Ethiopic Apocalypse of Enoch"]}}, 9071"JE":{"name":"Jubilees","sortOrder":64,"shortCode":"JE","usfm":"JUB","osis":"Jub", 9072 "chapters":[], 9073 "names":{"eng":["Jubilees"]}}, 9074"AD":{"name":"Additions to Daniel","sortOrder":65,"shortCode":"AD","usfm":"DNT","osis":"AddDan", 9075 "chapters":[null,null,null,null,null,null,null,null,null,null,null,null,null,null], 9076 "names":{"eng":["Additions to Daniel"]}}, 9077"DG":{"name":"Daniel (Greek)","sortOrder":66,"shortCode":"DG","usfm":"DAG","osis":"DanGr", 9078 "chapters":[null,null,null,null,null,null,null,null,null,null,null,null], 9079 "names":{"eng":["Daniel (Greek)"]}}, 9080"MT":{"name":"Matthew","sortOrder":70,"shortCode":"MT","usfm":"MAT","osis":"Matt","section":"gospel", 9081 "chapters":[25,23,17,25,48,34,29,34,38,42,30,50,58,36,39,28,27,35,30,34,46,46,39,51,46,75,66,20], 9082 "names":{"eng":["Matthew","Mt","Matt","Mat"]}}, 9083"MK":{"name":"Mark","sortOrder":71,"shortCode":"MK","usfm":"MRK","osis":"Mark","section":"gospel", 9084 "chapters":[45,28,35,41,43,56,37,38,50,52,33,44,37,72,47,20], 9085 "names":{"eng":["Mark","Mk","Mar","Mrk"]}}, 9086"LK":{"name":"Luke","sortOrder":72,"shortCode":"LK","usfm":"LUK","osis":"Luke","section":"gospel", 9087 "chapters":[80,52,38,44,39,49,50,56,62,42,54,59,35,35,32,31,37,43,48,47,38,71,56,53], 9088 "names":{"eng":["Luke","Lk","Luk","Lu"]}}, 9089"JN":{"name":"John","sortOrder":73,"shortCode":"JN","usfm":"JHN","osis":"John","section":"gospel", 9090 "chapters":[51,25,36,54,47,71,53,59,41,42,57,50,38,31,27,33,26,40,42,31,25], 9091 "names":{"eng":["John","Jn","Joh","Jo"]}}, 9092"AC":{"name":"Acts","sortOrder":74,"shortCode":"AC","usfm":"ACT","osis":"Acts","section":"acts", 9093 "chapters":[26,47,26,37,42,15,60,40,43,48,30,25,52,28,41,40,34,28,41,38,40,30,35,27,27,32,44,31], 9094 "names":{"eng":["Acts","Ac","Act"]}}, 9095"RM":{"name":"Romans","sortOrder":75,"shortCode":"RM","usfm":"ROM","osis":"Rom","section":"paul", 9096 "chapters":[32,29,31,25,21,23,25,39,33,21,36,21,14,23,33,27], 9097 "names":{"eng":["Romans","Ro","Rom","Rmn","Rmns"]}}, 9098"C1":{"name":"Paul's First Letter to the Corinthians","sortOrder":76,"shortCode":"C1","usfm":"1CO","osis":"1Cor","section":"paul", 9099 "chapters":[31,16,23,21,13,20,40,13,27,33,34,31,13,40,58,24], 9100 "names":{"eng":["1 Corinthians","1Co","1 Cor","1Cor","ICo","1 Co","1Co","I Corinthians","I Cor","I Co"]}}, 9101"C2":{"name":"Paul's Second Lettor to the Corinthians","sortOrder":77,"shortCode":"C2","usfm":"2CO","osis":"2Cor","section":"paul", 9102 "chapters":[24,17,18,18,21,18,16,24,15,18,33,21,14], 9103 "names":{"eng":["2 Corinthians","2Co","2 Cor","2Cor","IICo","2 Co","2Co","II Corinthians","II Cor","II Co"]}}, 9104"GL":{"name":"Paul's Letter to the Galatians","sortOrder":78,"shortCode":"GL","usfm":"GAL","osis":"Gal","section":"paul", 9105 "chapters":[24,21,29,31,26,18], 9106 "names":{"eng":["Galatians","Ga","Gal","Gltns"]}}, 9107"EP":{"name":"Paul's Letter to the Ephesians","sortOrder":79,"shortCode":"EP","usfm":"EPH","osis":"Eph","section":"paul", 9108 "chapters":[23,22,21,32,33,24], 9109 "names":{"eng":["Ephesians","Ep","Eph","Ephn"]}}, 9110"PP":{"name":"Paul's Letter to the Philippians","sortOrder":80,"shortCode":"PP","usfm":"PHP","osis":"Phil","section":"paul", 9111 "chapters":[30,30,21,23], 9112 "names":{"eng":["Philippians","Pp","Phi","Phil","Phi"]}}, 9113"CL":{"name":"Paul's Letter to the Colossians","sortOrder":81,"shortCode":"CL","usfm":"COL","osis":"Col","section":"paul", 9114 "chapters":[29,23,25,18], 9115 "names":{"eng":["Colossians","Co","Col","Colo","Cln","Clns"]}}, 9116"H1":{"name":"Paul's First Letter to the Thessalonians","sortOrder":82,"shortCode":"H1","usfm":"1TH","osis":"1Thess","
9116section":"paul", 9117 "chapters":[10,20,13,18,28], 9118 "names":{"eng":["1 Thessalonians","1Th","1 Thess","1Thess","ITh","1 Thes","1Thes","1 The","1The","1 Th","1Th","I Thessalonians","I Thess","I The","I Th"]}}, 9119"H2":{"name":"Paul's Second Letter to the Thessalonians","sortOrder":83,"shortCode":"H2","usfm":"2TH","osis":"2Thess","section":"paul", 9120 "chapters":[12,17,18], 9121 "names":{"eng":["2 Thessalonians","2Th","2 Thess","2 Thess","2Thess","IITh","2 Thes","2Thes","2 The","2The","2 Th","2Th","II Thessalonians","II Thess","II The","II Th"]}}, 9122"T1":{"name":"Paul's First Letter to Timothy","sortOrder":84,"shortCode":"T1","usfm":"1TI","osis":"1Tim","section":"paul", 9123 "chapters":[20,15,16,16,25,21], 9124 "names":{"eng":["1 Timothy","1Ti","1 Tim","1Tim","1 Ti","ITi","1Ti","I Timothy","I Tim","I Ti"]}}, 9125"T2":{"name":"Paul's Second Letter to Timothy","sortOrder":85,"shortCode":"T2","usfm":"2TI","osis":"2Tim","section":"paul", 9126 "chapters":[18,26,17,22], 9127 "names":{"eng":["2 Timothy","2Ti","2 Tim","2 Tim","2Tim","2 Ti","IITi","2Ti","II Timothy","II Tim","II Ti"]}}, 9128"TT":{"name":"Paul's Letter to Titus","sortOrder":86,"shortCode":"TT","usfm":"TIT","osis":"Titus","section":"paul", 9129 "chapters":[16,15,15], 9130 "names":{"eng":["Titus","Ti","Tit","Tt","Ts"]}}, 9131"PM":{"name":"Paul's Letter to Philemon","sortOrder":87,"shortCode":"PM","usfm":"PHM","osis":"Phlm","section":"paul", 9132 "chapters":[25], 9133 "names":{"eng":["Philemon","Pm","Phile","Phile","Philm","Pm"]}}, 9134"HB":{"name":"The Letter to the Hebrews","sortOrder":88,"shortCode":"HB","usfm":"HEB","osis":"Heb","section":"general", 9135 "chapters":[14,18,19,16,14,20,28,13,28,39,40,29,25], 9136 "names":{"eng":["Hebrews","He","Heb","Hw"]}}, 9137"JM":{"name":"The Letter from James","sortOrder":89,"shortCode":"JM","usfm":"JAS","osis":"Jas","section":"general", 9138 "chapters":[27,26,18,17,20], 9139 "names":{"eng":["James","Jm","Jam","Jas","Ja"]}}, 9140"P1":{"name":"The First Letter from Peter","sortOrder":90,"shortCode":"P1","usfm":"1PE","osis":"1Pet","section":"general", 9141 "chapters":[25,25,22,19,14], 9142 "names":{"eng":["1 Peter","1P","1 Pet","1Pet","IPe","1P","I Peter","I Pet","I Pe"]}}, 9143"P2":{"name":"The Second Letter from Peter","sortOrder":91,"shortCode":"P2","usfm":"2PE","osis":"2Pet","section":"general", 9144 "chapters":[21,22,18], 9145 "names":{"eng":["2 Peter","2P","2 Pet","2Pet","2Pe","IIP","II Peter","II Pet","II Pe"]}}, 9146"J1":{"name":"John's First Letter","sortOrder":92,"shortCode":"J1","usfm":"1JN","osis":"1John","section":"general", 9147 "chapters":[10,29,24,21,21], 9148 "names":{"eng":["1 John","1J","1 Jn","1Jn","1 Jo","IJo","I John","I Jo","I Jn"]}}, 9149"J2":{"name":"John's Second Letter","sortOrder":93,"shortCode":"J2","usfm":"2JN","osis":"2John","section":"general", 9150 "chapters":[13], 9151 "names":{"eng":["2 John","2J","2 Jn","2Jn","2 Jo","IIJo","II John","II Jo","II Jn"]}}, 9152"J3":{"name":"John's Third Letter","sortOrder":94,"shortCode":"J3","usfm":"3JN","osis":"3John","section":"general", 9153 "chapters":[14], 9154 "names":{"eng":["3 John","3J","3 Jn","3 Jn","3Jn","3 Jo","IIIJo","III John","III Jo","III Jn"]}}, 9155"JD":{"name":"Jude's Letter","sortOrder":95,"shortCode":"JD","usfm":"JUD","osis":"Jude","section":"general", 9156 "chapters":[25], 9157 "names":{"eng":["Jude","Jude","Jude"]}}, 9158"RV":{"name":"The Revelation to John","sortOrder":96,"shortCode":"RV","usfm":"REV","osis":"Rev","section":"rev", 9159 "chapters":[20,29,22,11,14,17,17,13,21,11,19,17,18,20,8,21,18,24,21,15,27,20], 9160 "names":{"eng":["Revelation","Re","Rev","Rvltn"]}}, 9161"BK":{"name":"Back matter","sortOrder":97,"shortCode":"BK","usfm":"BAK","osis":"Back", 9162 "chapters":[null], 9163 "names":{"eng":["Back matter"]}}, 9164"OH":{"name":"Other","sortOrder":98,"shortCode":"OH","usfm":"OTH","osis":"Other", 9165 "chapters":[null], 9166 "names":{"eng":["Other"]}}, 9167"XA":{"name":"XXA","sortOrder":99,"shortCode":"XA","usfm":"XXA","osis":"XXA", 9168 "chapters":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null], 9169 "names":{"eng":["XXA"]}}, 9170"XB":{"name":"XXB","sortOrder":100,"shortCode":"XB","usfm":"XXB","osis":"XXB", 9171 "chapters":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null], 9172 "names":{"eng":["XXB"]}}, 9173"XC":{"name":"XXC","sortOrder":101,"shortCode":"XC","usfm":"XXC","osis":"XXC", 9174 "chapters":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null], 9175 "names":{"eng":["XXC"]}}, 9176"XD":{"name":"XXD","sortOrder":102,"shortCode":"XD","usfm":"XXD","osis":"XXD", 9177 "chapters":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null], 9178 "names":{"eng":["XXD"]}}, 9179"XE":{"name":"XXE","sortOrder":103,"shortCode":"XE","usfm":"XXE","osis":"XXE", 9180 "chapters":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null], 9181 "names":{"eng":["XXE"]}}, 9182"XF":{"name":"XXF","sortOrder":104,"shortCode":"XF","usfm":"XXF","osis":"XXF", 9183 "chapters":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null], 9184 "names":{"eng":["XXF"]}}, 9185"XG":{"name":"XXG","sortOrder":105,"shortCode":"XG","usfm":"XXG","osis":"XXG", 9186 "chapters":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null], 9187 "names":{"eng":["XXG"]}}, 9188"GS":{"name":"Glossary","sortOrder":106,"shortCode":"GS","usfm":"GLO","osis":"Glossary", 9189 "chapters":[null], 9190 "names":{"eng":["Glossary"]}}, 9191"CN":{"name":"Concordance","sortOrder":107,"shortCode":"CN","usfm":"CNC","osis":"Conc", 9192 "chapters":[null], 9193 "names":{"eng":["Concordance"]}}, 9194"TX":{"name":"Topical Index","sortOrder":108,"shortCode":"TX","usfm":"TDX","osis":"Topic", 9195 "chapters":[null], 9196 "names":{"eng":["Topical Index"]}}, 9197"NX":{"name":"Names Index","sortOrder":109,"shortCode":"NX","usfm":"NDX","osis":"Name",
9198 "chapters":[null], 9199 "names":{"eng":["Names Index"]}} 9200} 9201 9202bible.EXTRA_MATTER = ["FR","IN","BK","OH","XA","XB","XC","XD","XE","XF","XG","GS","CN","TX","NX"]; 9203 9204/** 9205 * @description Default order of Old Testament books 9206 */ 9207bible.OT_BOOKS_OSIS = ["Gen","Exod","Lev","Num","Deut","Josh","Judg","Ruth","1Sam","2Sam","1Kgs","2Kgs","1Chr","2Chr","Ezra","Neh","Esth","Job","Ps","Prov","Eccl","Song","Isa","Jer","Lam","Ezek","Dan","Hos","Joel","Amos","Obad","Jonah","Mic","Nah","Hab","Zeph","Hag","Zech","Mal"]; 9208 9209bible.OT_BOOKS_USFM = ["GEN","EXO","LEV","NUM","DEU","JOS","JDG","RUT","1SA","2SA","1KI","2KI","1CH","2CH","EZR","NEH","EST","JOB","PSA","PRO","ECC","SNG","ISA","JER","LAM","EZK","DAN","HOS","JOL","AMO","OBA","JON","MIC","NAM","HAB","ZEP","HAG","ZEC","MAL"]; 9210 9211bible.OT_BOOKS = 9212["GN","EX","LV","NU","DT","JS","JG","RT","S1","S2","K1","K2","R1","R2","ER","NH","ET","JB","PS","PR","EC","SS","IS","JR","LM","EK","DN","HS","JL","AM","OB","JH","MC","NM","HK","ZP","HG","ZC","ML"]; 9213 9214 9215/** 9216 * @description Default order of New Testament books 9217 */ 9218bible.NT_BOOKS_OSIS = ["Matt","Mark","Luke","John","Acts","Rom","1Cor","2Cor","Gal","Eph","Phil","Col","1Thess","2Thess","1Tim","2Tim","Titus","Phlm","Heb","Jas","1Pet","2Pet","1John","2John","3John","Jude","Rev"]; 9219 9220bible.NT_BOOKS_USFM = ["MAT","MRK","LUK","JHN","ACT","ROM","1CO","2CO","GAL","EPH","PHP","COL","1TH","2TH","1TI","2TI","TIT","PHM","HEB","JAS","1PE","2PE","1JN","2JN","3JN","JUD","REV"]; 9221 9222bible.NT_BOOKS = ["MT","MK","LK","JN","AC","RM","C1","C2","GL","EP","PP","CL","H1","H2","T1","T2","TT","PM","HB","JM","P1","P2","J1","J2","J3","JD","RV"]; 9223 9224/** 9225 * @description Default order of Apocryphal books 9226 */ 9227bible.AP_BOOKS_OSIS = ["Tob","Jdt","EsthGr","AddEsth","Wis","Sir","Bar","EpJer","PrAzar","Sus","Bel","1Macc","2Macc","3Macc","4Macc","1Esd","2Esd","Ps151"]; 9228 9229bible.AP_BOOKS_USFM = 9230["TOB","JDT","ESG","ADE","WIS","SIR","BAR","LJE","PA","SUS","BEL","1MA","2MA","1ES"] 9231 9232bible.AP_BOOKS = 9233["TB","JT","ED","AE","WS","SR","BR","LJ","S3Y","SN","BL","M1","M2","E1"] 9234 9235 9236/** 9237 * @description Default order of books 9238 */ 9239bible.DEFAULT_BIBLE = bible.OT_BOOKS.concat(bible.NT_BOOKS); 9240bible.DEFAULT_BIBLE_OSIS = bible.OT_BOOKS_OSIS.concat(bible.NT_BOOKS_OSIS); 9241bible.DEFAULT_BIBLE_USFM = bible.OT_BOOKS_USFM.concat(bible.NT_BOOKS_USFM); 9242 9243/** 9244 * @description Default order of Bible's with Apocryphal books 9245 */ 9246bible.APOCRYPHAL_BIBLE = bible.OT_BOOKS.concat(bible.AP_BOOKS, bible.NT_BOOKS); 9247bible.APOCRYPHAL_BIBLE_USFM = bible.OT_BOOKS_USFM.concat(bible.AP_BOOKS_USFM, bible.NT_BOOKS_USFM); 9248 9249 9250/** 9251 * @description Adds names in bulk for a language (each version contains their own list of book names) 9252 */ 9253bible.addNames = function(lang, bookList, namesData) { 9254 for (var i in bookList) { 9255 var dbsCode = bookList[i], 9256 bookInfo = bible.BOOK_DATA[ dbsCode ], 9257 names = namesData[i]; 9258 9259 if (typeof bookInfo != 'undefined') { 9260 9261 if (typeof names === 'string') { 9262 names = [names]; 9263 } 9264 9265 if (!bookInfo.names[lang]) { 9266 bookInfo.names[lang] = []; 9267 } 9268 9269 // bookInfo.names[lang] = names; 9270 bookInfo.names[lang].splice(bookInfo.names[lang].length-1, 0, names); 9271 9272 } 9273 } 9274} 9275 9276 9277/** 9278 * @description Used in selecting a chapter since not all Bibles use digits 0-9. 9279 */ 9280bible.numbers = { 9281 "default":["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70","71","72","73","74","75","76","77","78","79","80","81","82","83","84","85","86","87","88","89","90","91","92","93","94","95","96","97","98","99","100","101","102","103","104","105","106","107","108","109","110","111","112","113","114","115","116","117","118","119","120","121","122","123","124","125","126","127","128","129","130","131","132","133","134","135","136","137","138","139","140","141","142","143","144","145","146","147","148","149","150"] 9282} 9283 9284 9285 9286/********* 9287* ../app/js/bible/bible.reference.js 9288**********/ 9289/** 9290 * Bible reference parser 9291 * 9292 * @author John Dyer (http://j.hn/) 9293 */ 9294 9295 9296// Depends on bible.data.js 9297 9298bible.parseReference = function (textReference, language) { 9299 9300 var 9301 bookIndex = -1, 9302 chapter1 = -1, 9303 verse1 = -1, 9304 chapter2 = -1, 9305 verse2 = -1, 9306 input = new String(textReference), 9307 i, 9308 lang, 9309 bookid, 9310 matchingbookid = null, 9311 matchingLanguage = null, 9312 afterRange = false, 9313 afterSeparator = false, 9314 startedNumber = false, 9315 currentNumber = '', 9316 name, 9317 possibleMatch, 9318 shortCodeRegex = /^\w{2}\d{1,3}(_\d{1,3})?$/; 9319 9320 // is short code format (GN2 || GN2_1) 9321 //bible.shortCodeRegex.lastIndex = 0; 9322 if (shortCodeRegex.test(input)) { 9323 9324 var parts = input.split('_'), 9325 bookChapter = parts[0]; 9326 9327 9328 bookid = bookChapter.substring(0,2).toUpperCase(); 9329 chapter1 = parseInt(bookChapter.substring(2), 10); 9330 9331 if (parts.length > 1) { 9332 verse1 = parseInt(parts[1], 10); 9333 } 9334 9335 return bible.Reference(bookid, chapter1, verse1, chapter2, verse2, language); 9336 } 9337 9338 // check books for DBS, OSIS, USFM 9339 9340 9341 // go through all books and test all names 9342 for (bookid in bible.BOOK_DATA) { 9343 9344 // match id? 9345 possibleMatch = input.substring(0, Math.floor(bookid.length, input.length)).toLowerCase(); 9346 var nextIsSeparator = input.length > possibleMatch.length ? /(\d|\.|\s)/.test(input.substr(possibleMatch.length, 1)) : false; 9347 if (possibleMatch == bookid.toLowerCase() && nextIsSeparator) { 9348 matchingbookid = bookid; 9349 input = input.substring(bookid.length); 9350 //matchingLanguage = 'eng'; 9351 break; 9352 } 9353 9354 // if no direct match on OSIS id, then go through names in each language 9355 for (lang in bible.BOOK_DATA[bookid].names) { 9356 9357 // test each name starting with the full name, then short code, then abbreviation, then alternates 9358 for (var i=0, il=bible.BOOK_DATA[bookid].names[lang].length; i<il; i++) { 9359 9360 name = new String(bible.BOOK_DATA[bookid].names[lang][i]).toLowerCase(); 9361 possibleMatch = input.substring(0, Math.floor(name.length, input.length)).toLowerCase(); 9362 9363 if (possibleMatch == name) { 9364 matchingbookid = bookid; 9365 matchingLanguage = lang; 9366 input = input.substring(name.length); 9367 break; // out of names 9368 } 9369 } 9370 9371 if (matchingbookid != null) 9372 break; // out of languages 9373 9374 } 9375 if (matchingbookid != null) 9376 break; // out of books 9377 } 9378 9379 if (matchingbookid == null) 9380 return null; 9381 9382 // pull of _10_10 => 10_10 9383 if (input.substring(0,1) == '_') { 9384 input = input.substring(1) 9385 } 9386 9387 for (i = 0; i < input.length; i++) { 9388 var c = input.charAt(i); 9389 9390 if (c == ' ' || isNaN(c)) { 9391 if (!startedNumber) 9392 continue; 9393 9394 if (c == '-') { 9395 afterRange = true; 9396 afterSeparator = false; 9397 } else if (c == ':' || c == ',' || c == '.' || c == '_') { 9398 afterSeparator = true; 9399 } else { 9400 // ignore 9401 } 9402 9403 // reset 9404 currentNumber = ''; 9405 startedNumber = false; 9406 9407 } else { 9408 startedNumber = true; 9409 currentNumber += c; 9410 9411 if (afterSeparator) { 9412 if (afterRange) { 9413 verse2 = parseInt(currentNumber); 9414 } else { // 1:1 9415 verse1 = parseInt(currentNumber); 9416 } 9417 } else { 9418 if (afterRange) { 9419 chapter2 = parseInt(currentNumber); 9420 } else { // 1 9421 chapter1 = parseInt(currentNumber); 9422 } 9423 } 9424 } 9425 } 9426 9427 // reassign 1:1-2 9428 if (chapter1 > 0 && verse1 > 0 && chapter2 > 0 && verse2 <= 0) { 9429 verse2 = chapter2; 9430 chapter2 = chapter1; 9431 } 9432 // fix 1-2:5 9433 if (chapter1 > 0 && verse1 <= 0 && chapter2 > 0 && verse2 > 0) { 9434 verse1 = 1; 9435 } 9436 9437 // just book 9438 if (bookIndex > -1 && chapter1 <= 0 && verse1 <= 0 && chapter2 <= 0 && verse2 <= 0) { 9439 chapter1 = 1; 9440 //verse1 = 1; 9441 } 9442 9443 // validate max chapter 9444 if (chapter1 == -1) { 9445 chapter1 = 1; 9446 } else if (bible.BOOK_DATA[matchingbookid].chapters && bible.BOOK_DATA[matchingbookid].chapters.length > 0 && chapter1 > bible.BOOK_DATA[matchingbookid].chapters.length) { 9447 chapter1 = bible.BOOK_DATA[matchingbookid].chapters.length; 9448 if (verse1 > 0) 9449 verse1 = 1; 9450 } 9451 9452 // validate max verse 9453 /* 9454 if (verse1 == -1) { 9455 verse1 = 1; 9456 } else 9457 */ 9458 if (bible.BOOK_DATA[matchingbookid].chapters && bible.BOOK_DATA[matchingbookid].chapters.length > 0 && verse1 > bible.BOOK_DATA[matchingbookid].chapters[chapter1 - 1]) { 9459 verse1 = bible.BOOK_DATA[matchingbookid].chapters[chapter1 - 1]; 9460 } 9461 if (verse2 <= verse1) { 9462 chapter2 = -1; 9463 verse2 = -1; 9464 } 9465 9466 // finalize 9467 return bible.Reference(matchingbookid, chapter1, verse1, chapter2, verse2, language); 9468 9469} 9470 9471bible.Reference = function () { 9472 9473 var 9474 _bookid = -1, 9475 _chapter1 = -1, 9476 _verse1 = -1, 9477 _chapter2 = -1, 9478 _verse2 = -1, 9479 _language = 'eng'; 9480 9481 if (arguments.length == 1 && typeof arguments[0] == 'string') { // a string that needs to be parsed 9482 return bible.parseReference(arguments[0]); 9483 9484 } else if (arguments.length == 2 && typeof arguments[0] == 'string' && typeof arguments[1] == 'string') { // verse, lang 9485 return bible.parseReference(arguments[0], arguments[1]); 9486 9487 } else if (arguments.length >= 2 && typeof arguments[0] == 'string' && typeof arguments[1] == 'number'){ 9488 _bookid = arguments[0]; 9489 _chapter1 = arguments[1]; 9490 if (arguments.length >= 3) _verse1 = arguments[2]; 9491 if (arguments.length >= 4) _chapter2 = arguments[3]; 9492 if (arguments.length >= 5) _verse2 = arguments[4]; 9493 if (arguments.length >= 6) _language = arguments[5]; 9494 } else { 9495 return null; 9496 } 9497 9498 function padLeft(input, length, s) { 9499 while (input.length < length) 9500 input = s + input; 9501 return input; 9502 } 9503 9504 var refObject = { 9505 bookid: _bookid, 9506 chapter: _chapter1, 9507 verse: _verse1, 9508 chapter1: _chapter1, 9509 verse1: _verse1, 9510 chapter2: _chapter2, 9511 verse2: _verse2, 9512 language: _language, 9513 bookList: bible.DEFAULT_BIBLE, 9514 9515 isValid: function () { 9516 return (typeof _bookid != 'undefined' && _bookid != null && _chapter1 > 0); 9517 }, 9518 9519 chapterAndVerse: function (cvSeparator, vvSeparator, ccSeparator) { 9520 cvSeparator = cvSeparator || ':'; 9521 vvSeparator = vvSeparator || '-'; 9522 ccSeparator = ccSeparator || '-'; 9523 9524 if (this.chapter1 > 0 && this.verse1 <= 0 && this.chapter2 <= 0 && this.verse2 <= 0) // John 1 9525 return this.chapter1; 9526 else if (this.chapter1 > 0 && this.verse1 > 0 && this.chapter2 <= 0 && this.verse2 <= 0) // John 1:1 9527 return this.chapter1 + cvSeparator + this.verse1; 9528 else if (this.chapter1 > 0 && this.verse1 > 0 && this.chapter2 <= 0 && this.verse2 > 0) // John 1:1-5 9529 return this.chapter1 + cvSeparator + this.verse1 + vvSeparator + this.verse2; 9530 else if (this.chapter1 > 0 && this.verse1 <= 0 && this.chapter2 > 0 && this.verse2 <= 0) // John 1-2 9531 return this.chapter1 + ccSeparator + this.chapter2; 9532 else if (this.chapter1 > 0 && this.verse1 > 0 && this.chapter2 > 0 && this.verse2 > 0) // John 1:1-2:2 9533 return this.chapter1 + cvSeparator + this.verse1 + ccSeparator + ((this.chapter1 != this.chapter2) ? this.chapter2 + cvSeparator : '') + this.verse2; 9534 else 9535 return 'unknown'; 9536 }, 9537 9538 toFormat: function(format) { 9539 9540 function padLeft(nr, n, str){ 9541 return Array(n-String(nr).length+1).join(str||'0')+nr; 9542 } 9543 9544 9545 var t = this, 9546 output = format, 9547 bookInfo = bible.BOOK_DATA[this.bookid], 9548 flags = { 9549 // book number 9550 'I': function() { 9551 return bookInfo.sortOrder.toString(); 9552 }, 9553 'II': function() { 9554 return padLeft(bookInfo.sortOrder.toString(), 2); 9555 }, 9556 'III': function() { 9557 return padLeft(bookInfo.sortOrder.toString(), 3); 9558 }, 9559 // book USFM 9560 'UUU': function() { 9561 return bookInfo.usfm.toUpperCase(); 9562 }, 9563 'uuu': function() { 9564 return bookInfo.usfm.toLowerCase(); 9565 }, 9566 'Uuu': function() { 9567 return bookInfo.usfm.substring(0,1).toUpperCase() + bookInfo.usfm.substring(1).toLowerCase(); 9568 }, 9569 // DBS format 9570 'DD': function() { 9571 return bookInfo.shortCode.toUpperCase(); 9572 }, 9573 'dd': function() { 9574 return bookInfo.shortCode.toLowerCase(); 9575 }, 9576 9577 // Name 9578 'NNN': function() { 9579 var bookName = '', 9580 bookNames = bookInfo.names[t.language]; 9581 9582 if (typeof bookNames != 'undefined') { 9583 bookName = bookNames[0]; 9584 } else { 9585 bookName = bookInfo.names['eng'][0] 9586 } 9587 9588 return bookName; 9589 }, 9590 'N': function() { 9591 return bookInfo.usfm.substring(0,1).toUpperCase() + bookInfo.usfm.substring(1).toLowerCase(); 9592 }, 9593 9594 // chapter 9595 'C': function() { 9596 return t.chapter1.toString(); 9597 }, 9598 'CC': function() { 9599 return padLeft(t.chapter1.toString(), 2); 9600 }, 9601 'CCC': function() { 9602 return padLeft(t.chapter1.toString(), 3); 9603 }, 9604 // verse 9605 'V': function() { 9606 return t.verse1.toString(); 9607 }, 9608 'VV': function() { 9609 return padLeft(t.verse1.toString(), 2); 9610 }, 9611 'VVV': function() { 9612 return padLeft(t.verse1.toString(), 3); 9613 }, 9614 '##': function() { 9615 return t.chapterAndVerse(); 9616 } 9617 }; 9618 9619 // copy number/chapter/verse 9620 flags['i'] = flags['I']; 9621 flags['ii'] = flags['II']; 9622 flags['iii'] = flags['III']; 9623 flags['c'] = flags['C']; 9624 flags['cc'] = flags['CC']; 9625 flags['ccc'] = flags['CCC']; 9626 flags['v'] = flags['V']; 9627 flags['vv'] = flags['VV']; 9628 flags['vvv'] = flags['VVV']; 9629 9630 9631 // create and sort keys 9632 var keys = Object.keys(flags); 9633 keys = keys.sort(function(b, a) { 9634 if (a.length > b.length) { 9635 return 1; 9636 } else if (a.length < b.length) { 9637 return -1; 9638 } else { 9639 return 0; 9640 } 9641 }); 9642 9643 // do replacement 9644 for (var i in keys) { 9645 var key = keys[i]; 9646 output = output.replace(new RegExp(key, 'g'), flags[key]()); 9647 } 9648 9649 return output; 9650 }, 9651 9652 toString: function () {
9653 if (this.bookid == null) return "invalid"; 9654 9655 var bookName = '', 9656 bookNames = bible.BOOK_DATA[this.bookid].names[this.language]; 9657 9658 if (typeof bookNames != 'undefined') { 9659 bookName = bookNames[0]; 9660 } else { 9661 bookName = bible.BOOK_DATA[this.bookid].names['eng'][0] 9662 } 9663 9664 return bookName + ' ' + this.chapterAndVerse(); 9665 }, 9666 9667 toSection: function () { 9668 if (this.bookid == null) return "invalid"; 9669 9670 return this.bookid + '' + this.chapter1 + (this.verse1 > 0 ? '_' + this.verse1 : ''); 9671 }, 9672 9673/* 9674 toOsis: function () { 9675 if (this.bookid == null) return "invalid"; 9676 9677 return this.bookid + '.' + this.chapter1 + (this.verse1 > 0 ? '.' + this.verse1 : ''); 9678 }, 9679 9680 toOsisChapter: function () { 9681 if (this.bookid == null) return "invalid"; 9682 9683 return this.bookid + '.' + this.chapter1; 9684 }, 9685 9686 toOsisVerse: function () { 9687 if (this.bookid == null) return "invalid"; 9688 9689 return this.bookid + '.' + this.chapter1 + '.' + (this.verse1 > 0 ? this.verse1 : '0'); 9690 }, 9691*/ 9692 9693 prevChapter: function () { 9694 this.verse1 = 1; 9695 this.chapter2 = -1; 9696 this.verse2 = -1; 9697 if (this.chapter1 == 1 && this.bookList.indexOf(this.bookid) == 0) { 9698 return null; 9699 } else { 9700 if (this.chapter1 == 1) { 9701 // get the previous book 9702 this.bookid = this.bookList[this.bookList.indexOf(this.bookid)-1]; 9703 9704 // get the last chapter in this book 9705 this.chapter = this.chapter1 = bible.BOOK_DATA[this.bookid].chapters.length; 9706 } else { 9707 // just go back a chapter 9708 this.chapter = this.chapter1 = this.chapter1 - 1; 9709 } 9710 9711 } 9712 9713 // return the object () 9714 return this; 9715 }, 9716 9717 nextChapter: function () { 9718 this.verse1 = 1; 9719 this.chapter2 = -1; 9720 this.verse2 = -1; 9721 9722 // check for the last chapter in the last book 9723 if (this.bookList[this.bookid] == this.bookList.length-1 && bible.BOOK_DATA[this.bookid].chapters.length == this.chapter1) { 9724 return null; 9725 } else { 9726 9727 if (this.chapter1 < bible.BOOK_DATA[this.bookid].chapters.length) { 9728 // just go up one chapter 9729 this.chapter = this.chapter1 = this.chapter1+1; 9730 9731 } else if (this.bookList.indexOf(this.bookid) < this.bookList.length-1) { 9732 // go to the next book, first chapter 9733 this.bookid = this.bookList[this.bookList.indexOf(this.bookid)+1]; 9734 this.chapter = this.chapter1 = 1; 9735 } 9736 9737 } 9738 9739 return this; 9740 }, 9741 9742 isFirstChapter: function () { 9743 return (this.chapter1 == 1 && this.bookList.indexOf(this.bookid) == 0); 9744 }, 9745 9746 isLastChapter: function () { 9747 return (this.bookList[this.bookid] == this.bookList.length-1 && bible.BOOK_DATA[this.bookid].chapters.length == this.chapter1); 9748 } 9749 }; 9750 9751 return refObject; 9752}; 9753bible.utility = {}; 9754 9755 9756 9757/********* 9758* ../app/js/bible/morphology.js 9759**********/ 9760/* language tools */ 9761if (typeof(window.bible) == 'undefined') 9762 window.bible = {}; 9763 9764 9765bible.morphology = {}; 9766 9767// http://wiki.logos.com/Morphology_Codes 9768// 9769bible.morphology['robinson'] = { 9770 9771 format: function(morph) { 9772 9773 var firstDash = morph.indexOf('-'), 9774 partOfSpeechKey = (firstDash > -1) ? morph.substring(0, firstDash) : morph, 9775 partOfSpeech = this.partsOfSpeech[partOfSpeechKey.toUpperCase()], 9776 9777 parsingInfo = (firstDash > -1) ? morph.substring(firstDash+1) : '', 9778 formattedParsing = ''; 9779 9780 switch (partOfSpeechKey.toUpperCase()) { 9781 case 'T': 9782 case 'N': 9783 case 'R': 9784 case 'C': 9785 case 'D': 9786 case 'K': 9787 case 'I': 9788 case 'X': 9789 case 'Q': 9790 case 'F': 9791 case 'S': 9792 var c = this.nounCases[parsingInfo.substring(0, 1)]; 9793 var n = this.wordNumber[parsingInfo.substring(1, 2)]; 9794 var g = this.wordGender[parsingInfo.substring(2, 3)]; 9795 formattedParsing = c + ((n) ? ', ' + n + ((g) ? ', ' + g : '') : ''); 9796 break; 9797 9798 case 'P': 9799 9800 var firstLetter = parsingInfo.substr(0,1); 9801 if (firstLetter == '1' || firstLetter == '2') { 9802 9803 var p = this.wordPerson[parsingInfo.substring(0, 1)]; 9804 var c = this.nounCases[parsingInfo.substring(1, 2)]; 9805 var n = this.wordNumber[parsingInfo.substring(2, 3)]; 9806 formattedParsing = p + ((c) ? ', ' + c + ((n) ? ', ' + n : '') : ''); 9807 } else { 9808 var c = this.nounCases[parsingInfo.substring(0, 1)]; 9809 var n = this.wordNumber[parsingInfo.substring(1, 2)]; 9810 var g = this.wordGender[parsingInfo.substring(2, 3)]; 9811 formattedParsing = c + ((n) ? ', ' + n + ((g) ? ', ' + g : '') : ''); 9812 } 9813 9814 break; 9815 9816 case 'A': 9817 var c = this.nounCases[parsingInfo.substring(0, 1)]; 9818 var n = this.wordNumber[parsingInfo.substring(1, 2)]; 9819 formattedParsing = c + ', ' + n; 9820 break; 9821 9822 case 'PRT': 9823 9824 formattedParsing = this.particleTypes[parsingInfo]; 9825 break; 9826 9827 case 'V': 9828 var t = ''; 9829 var rem = '' 9830 if (parsingInfo.substring(0, 1) == '2') { 9831 t = this.verbTenses[parsingInfo.substring(0, 2)]; 9832 rem = parsingInfo.substring(2); 9833 } else { 9834 t = this.verbTenses[parsingInfo.substring(0, 1)]; 9835 rem = parsingInfo.substring(1); 9836 } 9837 var v = this.verbVoices[rem.substring(0, 1)]; 9838 var m = this.verbMoods[rem.substring(1, 2)]; 9839 9840 if (rem.length == 2) { 9841 formattedParsing = t + ', ' + v + ', ' + m; 9842 9843 } else if (rem.length == 5) { 9844 var p = this.wordPerson[rem.substring(3, 4)]; 9845 var n = this.wordNumber[rem.substring(4, 5)]; 9846 formattedParsing = t + ', ' + v + ', ' + m + ', ' + p + ', ' + n; 9847 9848 } else if (rem.length == 6) { 9849 var c = this.nounCases[rem.substring(3, 4)]; 9850 var n = this.wordNumber[rem.substring(4, 5)]; 9851 var g = this.wordGender[rem.substring(5, 6)]; 9852 9853 formattedParsing = t + ', ' + v + ', ' + m + ', ' + c + ', ' + n + ', ' + g; 9854 } 9855 break; 9856 9857 //m = this.verbMoods[parsingInfo.substring(2+offset,3+offset)]; 9858 default: 9859 formattedParsing = parsingInfo; 9860 9861 } 9862 9863 return (typeof partOfSpeech != 'undefined' ? partOfSpeech + (formattedParsing != '' ? ': ' : '') : '') + formattedParsing; 9864 9865 }, 9866 9867 partsOfSpeech: { 9868 N: 'noun', 9869 A: 'adjective', 9870 T: 'article', 9871 V: 'verb', 9872 P: 'personal pronoun', 9873 R: 'relative pronoun', 9874 C: 'reciprocal pronoun', 9875 D: 'demonstrative pronoun', 9876 K: 'correlative pronoun', 9877 I: 'interrogative pronoun', 9878 X: 'indefinite pronoun', 9879 Q: 'correlative or interrogative pronoun', 9880 F: 'reflexive pronoun', 9881 S: 'posessive pronoun', 9882 ADV: 'adverb', 9883 CONJ: 'conjunction', 9884 COND: 'cond', 9885 PRT: 'particle', 9886 PREP: 'preposition', 9887 INJ: 'interjection', 9888 ARAM: 'aramaic', 9889 HEB: 'hebrew' 9890 }, 9891 9892 getPartofSpeech: function(partOfSpeechKey) { 9893 var full = this.partsOfSpeech[partOfSpeechKey.toUpperCase()]; 9894 9895 return (full != null) ? full : '?'; 9896 }, 9897 9898 nounCases: { 9899 'N': 'nominative', 9900 'V': 'vocative', 9901 'G': 'genitive', 9902 'D': 'dative', 9903 'A': 'accusative', 9904 'P': 'proper name' 9905 }, 9906 9907 wordNumber: { 9908 'S': 'singular', 9909 'P': 'plural' 9910 }, 9911 9912 wordGender: { 9913 'M': 'masculine', 9914 'F': 'feminine', 9915 'N': 'neuter' 9916 }, 9917 9918 wordPerson: { 9919 '1': '1st', 9920 '2': '2nd', 9921 '3': '3rd' 9922 }, 9923 9924 verbTenses: { 9925 'P': 'present', 9926 'I': 'imperfect', 9927 'F': 'future', 9928 '2F': 'second future', 9929 'A': 'aorist',
9930 '2A': 'second aorist', 9931 'R': 'perfect', 9932 '2R': 'second perfect', 9933 'L': 'pluperfect', 9934 '2L': 'second pluperfect', 9935 'X': 'no tense stated' 9936 }, 9937 9938 verbVoices: { 9939 'A': 'active', 9940 'M': 'middle', 9941 'P': 'passive', 9942 'E': 'middle or passive', 9943 'D': 'middle deponent', 9944 'O': 'passive deponent', 9945 'N': 'middle or passive deponent', 9946 'Q': 'impersonal active', 9947 'X': 'no voice' 9948 }, 9949 9950 verbMoods: { 9951 'I': 'indicative', 9952 'S': 'subjunctive', 9953 'O': 'optative', 9954 'M': 'imperative', 9955 'N': 'infinitive', 9956 'P': 'participle', 9957 'R': 'imperative participle' 9958 }, 9959 9960 particleTypes: { 9961 'I': 'interogative', 9962 'N': 'negative' 9963 } 9964}; 9965bible.morphology['Greek'] = bible.morphology['robinson']; 9966 9967 9968 9969// http://openscriptures.github.io/morphhb/parsing/HebrewMorphologyCodes.html 9970bible.morphology['OSHB'] = { 9971 format: function(morph) { 9972 var languageKey = morph.substr(0,1), 9973 language = this.languages[languageKey], 9974 morphParts = morph.substr(1).split('/'), 9975 formattedOutput = ''; 9976 9977 for (var i=0, il=morphParts.length; i<il; i++) { 9978 var m = morphParts[i], 9979 partOfSpeechKey = m.substr(0,1), 9980 partOfSpeech = this.partsOfSpeech[partOfSpeechKey], 9981 morphCodes = m.length > 0 ? m.substr(1) : '', 9982 9983 partsList = [], 9984 details = []; 9985 9986 switch (partOfSpeechKey) { 9987 9988 case 'A': 9989 partsList = ['adjectiveTypes', 'gender', 'number', 'state']; 9990 9991 break; 9992 case 'C': 9993 case 'D': 9994 // nothing 9995 9996 break; 9997 case 'N': 9998 9999 partsList = ['nounTypes', 'gender', 'number', 'state']; 10000 10001 break; 10002 case 'P': 10003 10004 partsList = ['pronounTypes', 'person', 'gender', 'number']; 10005 10006 break; 10007 case 'R': 10008 partsList = ['prepositionTypes']; 10009 10010 break; 10011 case 'S': 10012 10013 partsList = ['suffixTypes', 'person', 'gender', 'number']; 10014 10015 break; 10016 case 'T': 10017 10018 partsList = ['particleTypes']; 10019 10020 break; 10021 case 'V': 10022 10023 partsList = [language.toLowerCase() + 'VerbStems', 'verbConjugationTypes', 'person', 'gender', 'number', 'state']; 10024 10025 break; 10026 10027 } 10028 10029 for (var j=0, jl=partsList.length; j<jl; j++) { 10030 if (morphCodes.length > j) { 10031 details.push( this[ partsList[j] ][ morphCodes.substr(j, 1) ] ); 10032 } 10033 } 10034 10035 10036 10037 formattedOutput += (i>0 ? '; ' : '') + partOfSpeech + (details.length > 0 ? ': ' + details.join(', ') : ''); 10038 } 10039 10040 return formattedOutput; 10041 }, 10042 10043 partsOfSpeech: { 10044 10045 'A':'adjective', 10046 'C':'conjunction', 10047 'D':'adverb', 10048 'N':'noun', 10049 'P':'pronoun', 10050 'R':'preposition', 10051 'S':'suffix', 10052 'T':'particle', 10053 'V':'verb' 10054 10055 }, 10056 10057 hebrewVerbStems: { 10058 10059 'q': 'qal', 10060 'N': 'niphal', 10061 'p': 'piel', 10062 'P': 'pual', 10063 'h': 'hiphil', 10064 'H': 'hophal', 10065 't': 'hithpael', 10066 'o': 'polel', 10067 'O': 'polal', 10068 'r': 'hithpolel', 10069 'm': 'poel', 10070 'M': 'poal', 10071 'k': 'palel', 10072 'K': 'pulal', 10073 10074 'Q': 'qal passive', 10075 'l': 'pilpel', 10076 'L': 'polpal', 10077 'f': 'hithpalpel', 10078 'D': 'nithpael', 10079 'j': 'pealal', 10080 'i': 'pilel', 10081 'u': 'hothpaal', 10082 'c': 'tiphil', 10083 'v': 'hishtaphel', 10084 'w': 'nithpalel', 10085 'y': 'nithpoel', 10086 'z': 'hithpoel' 10087 }, 10088 10089 aramaicVerbStems: { 10090 10091 'q': 'peal', 10092 'Q': 'peil', 10093 'u': 'hithpeel', 10094 'p': 'pael', 10095 'P': 'ithpaal', 10096 'M': 'hithpaal', 10097 'a': 'aphel', 10098 'h': 'haphel', 10099 's': 'saphel', 10100 'e': 'shaphel', 10101 'H': 'hophal', 10102 'i': 'ithpeel', 10103 't': 'hishtaphel', 10104 'v': 'ishtaphel', 10105 'w': 'hithaphel', 10106 'o': 'polel', 10107 'z': 'ithpoel', 10108 'r': 'hithpolel', 10109 'f': 'hithpalpel', 10110 'b': 'hephal', 10111 'c': 'tiphel', 10112 'm': 'poel', 10113 'l': 'palpel', 10114 'L': 'ithpalpel', 10115 'O': 'ithpolel', 10116 'G': 'ittaphal' 10117 10118 }, 10119 10120 verbConjugationTypes: { 10121 'p': 'perfect (qatal)', 10122 'q': 'sequential perfect (weqatal)', 10123 'i': 'imperfect (yiqtol)', 10124 'w': 'sequential imperfect (wayyiqtol)', 10125 'h': 'cohortative', 10126 'j': 'jussive', 10127 'v': 'imperative', 10128 'r': 'participle active', 10129 's': 'participle passive', 10130 'a': 'infinitive absolute', 10131 'c': 'infinitive construct' 10132 }, 10133 10134 adjectiveTypes: { 10135 'a': 'adjective', 10136 'c': 'cardinal number', 10137 'g': 'gentilic', 10138 'o': 'ordinal number' 10139 }, 10140 10141 nounTypes: { 10142 'c': 'common', 10143 'g': 'gentilic', 10144 'p': 'proper name' 10145 }, 10146 10147 pronounTypes: { 10148 'd': 'demonstrative', 10149 'f': 'indefinite',
10150 'i': 'interrogative', 10151 'p': 'personal', 10152 'r': 'relative' 10153 }, 10154 10155 prepositionTypes: { 10156 'd': 'definite article' 10157 }, 10158 10159 suffixTypes: { 10160 'd': 'directional he', 10161 'h': 'paragogic he', 10162 'n': 'paragogic nun', 10163 'p': 'pronominal' 10164 }, 10165 10166 particleTypes: { 10167 'a': 'affirmation', 10168 'd': 'definite article', 10169 'e': 'exhortation', 10170 'i': 'interrogative', 10171 'j': 'interjection', 10172 'm': 'demonstrative', 10173 'n': 'negative', 10174 'o': 'direct object marker', 10175 'r': 'relative' 10176 }, 10177 10178 person: { 10179 '1': 'first', 10180 '2': 'second', 10181 '3': 'third' 10182 }, 10183 10184 gender: { 10185 'b': 'both (noun)', 10186 'c': 'common (verb)', 10187 'f': 'feminine', 10188 'm': 'masculine' 10189 }, 10190 10191 number: { 10192 'd': 'dual', 10193 'p': 'plural', 10194 's': 'singular' 10195 }, 10196 10197 state: { 10198 'a': 'absolute', 10199 'c': 'construct', 10200 'd': 'determined' 10201 }, 10202 10203 languages: { 10204 'H': 'Hebrew', 10205 'A': 'Aramaic' 10206 } 10207}; 10208 10209bible.morphology['Hebrew'] = bible.morphology['OSHB']; 10210 10211 10212 10213/********* 10214* ../app/js/texts/textloader.js 10215**********/ 10216sofia.textproviders = {}; 10217 10218TextLoader = (function() { 10219 10220 var 10221 textInfoDataIsLoading = false, 10222 10223 textInfoLoadingCallbacks = [], 10224 10225 textInfoDataIsLoaded = false, 10226 10227 // simple data from manifests that includes name, lang 10228 textInfoData = [], 10229 10230 // full data with chapter arrays, etc. 10231 textData = {}, 10232 10233 cachedTexts = {}; 10234 10235 function loadSection(textInfo, sectionid, successCallback, errorCallback) { 10236 10237 // double check 10238 if (sectionid == 'null' || sectionid == null) { 10239 return; 10240 } 10241 10242 var textid = ''; 10243 10244 if (textInfo != null && typeof textInfo == 'string') { 10245 textid = textInfo; 10246 10247 getText(textid, function(textInfo) { 10248 loadSection(textInfo, sectionid, successCallback, errorCallback); 10249 }); 10250 return; 10251 10252 } else { 10253 textid = textInfo.id; 10254 10255 // sometimes the sections aren't yet known, so we'll check for them and hope for the best :) 10256 if (textInfo.sections && textInfo.sections.length > 0 && textInfo.sections.indexOf(sectionid) == -1) { 10257 sectionid = textInfo.sections[0]; 10258 } 10259 } 10260 10261 // send analytics for loading 10262 if (sofia.analytics && sofia.analytics.record) { 10263 sofia.analytics.record('load', textInfo.id, sectionid); 10264 } 10265 10266 // use stored text if present 10267 if (typeof cachedTexts[textid] == 'undefined') { 10268 cachedTexts[textid] = {}; 10269 } 10270 if (typeof cachedTexts[textid][sectionid] != 'undefined') { 10271 successCallback ( $(cachedTexts[textid][sectionid]) ); 10272 return; 10273 } 10274 10275 10276 // load from provider 10277 sofia.textproviders[textInfo.providerName].loadSection(textid, sectionid, function(html) { 10278 10279 // store 10280 cachedTexts[textid][sectionid] = html; 10281 10282 // send 10283 successCallback ( $(cachedTexts[textid][sectionid]) ); 10284 }); 10285 } 10286 10287 10288 function getTextid(input) { 10289 10290 var parts = input.split(':'), 10291 textid = (parts.length > 1) ? parts[1] : parts[0]; 10292 10293 return textid; 10294 10295 } 10296 10297 function getProviderName(input) { 10298 10299 // if not loaded, get it from provider 10300 var 10301 parts = input.split(':'), 10302 textid = parts.length > 1 ? parts[1] : parts[0], 10303 providerName = parts.length > 1 ? parts[0] : ''; 10304 10305 if (providerName == '') { 10306 var textInfo = textInfoData.filter(function(info) { 10307 return info.id == textid; 10308 })[0]; 10309 10310 if (textInfo && typeof textInfo.providerName != 'undefined') { 10311 providerName = textInfo.providerName; 10312 } else { 10313 providerName = 'local'; // ??? 10314 } 10315 } 10316 10317 return providerName; 10318 } 10319 10320 function getProviderId(input) { 10321 10322 // assume we have the full providerid 10323 if (input.indexOf(':') > -1) { 10324 return input; 10325 } else { 10326 // assume we only have the textid 10327 var textid = input, 10328 textInfo = textInfoData.filter(function(info) { 10329 return info.id == textid; 10330 })[0]; 10331 10332 return textInfo.providerid; 10333 } 10334 } 10335 10336 10337 function getText(textid, callback, errorCallback) { 10338 10339 // if already loaded, then send it right back 10340 var t = this, 10341 textinfo = textData[textid]; 10342 10343 if (typeof textinfo != 'undefined') { 10344 if (typeof callback != 'undefined') { 10345 callback(textinfo); 10346 } 10347 return textinfo; 10348 } 10349 10350 var providerName = getProviderName(textid), 10351 textid = getTextid(textid); 10352 10353 10354 sofia.textproviders[providerName].getTextInfo(textid, function(data) { 10355 10356 var initialInfo = textInfoData[textid]; 10357 data = $.extend({}, initialInfo, data, true); 10358 10359 10360 processText(data, providerName) 10361 //data.providerName = providerName; 10362 10363 10364 10365 10366 10367 // store 10368 textData[data.id] = data; 10369 10370 // set names 10371 if (data.divisionNames) { 10372 bible.addNames(data.lang, data.divisions, data.divisionNames); 10373 } 10374 10375 // send back 10376 callback(data); 10377 10378 }, errorCallback); 10379 10380 } 10381 10382 function loadTexts(callback) { 10383 if (textInfoDataIsLoaded) { 10384 callback(textInfoData); 10385 } else { 10386 loadTextsManifest(callback); 10387 } 10388 } 10389 10390 function loadTextsManifest(callback) { 10391 10392 if (callback) { 10393 textInfoLoadingCallbacks.push(callback); 10394 } 10395 10396 if (textInfoDataIsLoading) { 10397 return; 10398 } 10399 10400 textInfoDataIsLoading = true; 10401 10402 var providerKeys = Object.keys(sofia.textproviders), 10403 currentProviderIndex = 0; 10404 10405 function loadNextProvider() { 10406 if (currentProviderIndex < providerKeys.length) { 10407 10408 var providerName = providerKeys[currentProviderIndex] 10409 10410 sofia.textproviders[providerName].getTextManifest(function(data) { 10411 10412 if (data && data != null) { 10413 10414 // append to array from previous provider 10415 textInfoData = textInfoData.concat(data); 10416 } 10417 10418 currentProviderIndex++; 10419 loadNextProvider(); 10420 }); 10421 10422 10423 } else { 10424 textInfoDataIsLoading = false; 10425 textInfoDataIsLoaded = true; 10426 10427 10428 while (textInfoLoadingCallbacks.length > 0) { 10429 10430 var cb = textInfoLoadingCallbacks.pop(); 10431 cb(textInfoData); 10432 10433 } 10434 } 10435 } 10436 10437 loadNextProvider(); 10438 10439 } 10440 10441 function processTexts(text_array, providerName) { 10442 // add providerName, providerid, and id to each one 10443 for (var i=0, il=text_array.length; i<il; i++) { 10444 10445 var text = text_array[i]; 10446 10447 processText(text, providerName); 10448 } 10449 } 10450 10451 function processText(text, providerName) { 10452 // remove any provider info from the id 10453 if (text.id.split(':').length > 1) { 10454 text.id = text.id.split(':')[1]; 10455 } 10456 10457 text.providerName = providerName; 10458 text.providerid = providerName + ':' + text.id; 10459 10460 // TEMP for DBS 10461 if (typeof text.country != 'undefined' && typeof text.countries == 'undefined' && text.country != text.langName && text.country != text.langNameEnglish) { 10462 text.countries = []; 10463 10464 var possibleCountryMatches = sofia.countries.filter(function(c) { 10465 return c.name.indexOf(text.country) == 0;
10466 }); 10467 10468 if (possibleCountryMatches.length > 0) { 10469 text.countries.push( possibleCountryMatches[0]['alpha-2'] ); 10470 } 10471 } 10472 } 10473 10474 function startSearch(textid, divisions, searchTerms, onSearchLoad, onSearchIndexComplete, onSearchComplete) { 10475 10476 var providerName = getProviderName(textid); 10477 10478 sofia.textproviders[providerName].startSearch(textid, divisions, searchTerms, onSearchLoad, onSearchIndexComplete, onSearchComplete); 10479 10480 } 10481 10482 function getTextInfoData() { 10483 return textInfoData; 10484 } 10485 10486 // when the document is ready, start loading texts from providers 10487 $(function() { 10488 loadTextsManifest(); 10489 }); 10490 10491 var ext = { 10492 getText: getText, 10493 loadTexts: loadTexts, 10494 textData: textData, 10495 getTextInfoData: getTextInfoData, 10496 loadSection: loadSection, 10497 startSearch: startSearch, 10498 processTexts: processTexts, 10499 processText: processText 10500 } 10501 10502 return ext; 10503})(); 10504 10505 10506 10507/********* 10508* ../app/js/texts/textprovider-local.js 10509**********/ 10510 10511sofia.textproviders['local'] = (function() { 10512 10513 var providerName = 'local', 10514 fullName = '', 10515 textData = {}; 10516 10517 function getTextManifest(callback) { 10518 var textsUrl = 'content/texts/' + sofia.config.textsIndexPath; 10519 10520 sofia.ajax({ 10521 url: textsUrl, 10522 dataType: 'json', 10523 cache: false, 10524 success: function(data) { 10525 10526 var textInfoData = data.textInfoData; 10527 10528 //text_data = TextLoader.processTexts(textInfoData, providerName); 10529 10530 callback(textInfoData); 10531 }, 10532 error: function(jqXHR, textStatus, errorThrown) { 10533 //console.log('error loading texts.json', jqXHR, textStatus, errorThrown); 10534 //console.log(textStatus); 10535 10536 var modal = new MovableWindow(600,250, 'Texts Error'); 10537 //modal.size(500, 200).center(); 10538 10539 modal.body.css({background: '#000', color: '#fff' }).html( 10540 '<div style="padding: 20px;">' + 10541 '<p>Problem loading <code>' + sofia.config.baseContentUrl + textsUrl + '</code></p>' + 10542 '<p>Status: ' + textStatus + '</p>'+ 10543 '<p>Error: ' + errorThrown + '</p>'+ 10544 '</div>' 10545 ); 10546 modal.show().center(); 10547 10548 } 10549 }); 10550 } 10551 10552 function getTextInfo(textid, callback, errorCallback) { 10553 10554 if (typeof textData[textid] != 'undefined') { 10555 callback(textData[textid]); 10556 return; 10557 } 10558 10559 // load it! 10560 var infoUrl = 'content/texts/' + textid + '/info.json'; 10561 10562 sofia.ajax({ 10563 url: infoUrl, 10564 dataType: 'json', 10565 success: function(data) { 10566 textData[textid] = data; 10567 callback(data); 10568 }, 10569 error: function(error) { 10570 10571 console.log("ERROR TextInfoLoader.getText", infoUrl); 10572 10573 if (errorCallback) { 10574 errorCallback(error); 10575 } 10576 } 10577 }); 10578 } 10579 10580 function loadSection(textid, sectionid, callback, errorCallback) { 10581 10582 getTextInfo(textid, function(textInfo) { 10583 10584 var url = 'content/texts/' + textid + '/' + sectionid + '.html'; 10585 10586 sofia.ajax({ 10587 dataType: 'text', 10588 url: url, 10589 success: function(data) { 10590 10591 // text to treat this like JSON or text/html 10592 var text = data, 10593 // split at the closing head tag to prevent problems with loading head material 10594 main = $( text.indexOf('</head>') > -1 ? text.split('</head>')[1] : text ), 10595 content = main.filter('.section'), 10596 footnotes = main.filter('.footnotes'), 10597 notes = footnotes.find('.footnote'); 10598 10599 // move notes into place 10600 if (notes.length > 0) { 10601 notes.each(function() { 10602 var footnote = $(this), 10603 noteid = footnote.find('a').attr('href'), 10604 footnotetext = footnote.find('.text'), 10605 noteintext = content.find(noteid); 10606 10607 //console.log(noteid, noteintext); 10608 10609 noteintext.append(footnotetext); 10610 10611 }); 10612 } 10613 10614 content.attr('data-textid', textid); 10615 content.attr('data-lang3', textInfo.lang); 10616 10617 // FIX title after chapter number 10618 var c = content.find('.c'), 10619 afterc = c.next(); 10620 if (afterc.hasClass('s')) { 10621 c.before(afterc); 10622 } 10623 10624 // FIX verse numbers inside verse 10625 content.find('.v-num').each(function() { 10626 var vnum = $(this), 10627 v = vnum.closest('.v'); 10628 10629 if (v.length > 0) { 10630 v.before(vnum); 10631 } 10632 10633 });
10634 10635 var html = content.wrapAll('<div></div>').parent().html(); 10636 10637 callback(html); 10638 10639 }, error: function(jqXHR, textStatus, errorThrown) { 10640 if (errorCallback) { 10641 errorCallback(textid, sectionid); 10642 } 10643 10644 //console.log('error', textStatus, errorThrown, jqXHR ); 10645 } 10646 }); 10647 }); 10648 10649 } 10650 10651 function startSearch(textid, divisions, text, onSearchLoad, onSearchIndexComplete, onSearchComplete) { 10652 10653 var textSearch = new TextSearch(); 10654 10655 textSearch.on('load', onSearchLoad); 10656 textSearch.on('indexcomplete', onSearchIndexComplete); 10657 textSearch.on('complete', onSearchComplete); 10658 10659 textSearch.start(textid, divisions, text); 10660 10661 } 10662 10663 return { 10664 getTextManifest: getTextManifest, 10665 getTextInfo: getTextInfo, 10666 loadSection: loadSection, 10667 startSearch: startSearch, 10668 fullName: fullName 10669 } 10670 10671})(); 10672 10673/* 10674sofia.textproviders['example'] = (function() { 10675 10676 function getTextManifest (callback) { 10677 callback({}); 10678 } 10679 10680 function getTextInfo(textid, callback) { 10681 callback({}); 10682 } 10683 10684 function loadSection(textid, sectionid, callback) { 10685 callback(null); 10686 } 10687 10688 return { 10689 getTextManifest: getTextManifest, 10690 getTextInfo: getTextInfo, 10691 loadSection: loadSection 10692 } 10693 10694})(); 10695*/ 10696 10697 10698 10699/********* 10700* ../app/js/texts/textprovider-dbs.js 10701**********/ 10702sofia.config = $.extend(sofia.config, { 10703 dbsEnabled: true, 10704 dbsKey: '', 10705 dbsBase: 'https://api.dbp4.org/', 10706 dbsIncludeList: [], 10707 dbsExcludeList: [], 10708}); 10709 10710 10711 10712sofia.textproviders['dbs'] = (function() { 10713 10714 var text_data = [], 10715 text_data_is_loaded = false, 10716 text_data_is_loading = false, 10717 text_data_callbacks = [], 10718 10719 providerName = 'dbs', 10720 fullName = 'Digital Bible Society'; 10721 10722 function getTextManifest (callback) { 10723 10724 // check for offline use 10725 if (!sofia.config.enableOnlineSources || !sofia.config.dbsEnabled || sofia.config.dbsKey == '' ) { 10726 callback(null); 10727 return; 10728 } 10729 10730 // if loaded immediately callback 10731 if (text_data_is_loaded) { 10732 10733 callback(text_data); 10734 10735 } else { 10736 10737 // store callback 10738 text_data_callbacks.push(callback); 10739 10740 // don't continue 10741 if (text_data_is_loading) { 10742 return; 10743 } 10744 10745 text_data_is_loading = true; 10746 10747 $.ajax({
10748 url: sofia.config.dbsBase + 'bibles?v=4&key=' + sofia.config.dbsKey, 10749 /* 10750 dataType: 'jsonp', 10751 beforeSend: function(xhr){ 10752 if (xhr.overrideMimeType) { 10753 xhr.overrideMimeType('application/javascript'); 10754 } 10755 }, 10756 */ 10757 dataType: 'json', 10758 cache: false, 10759 success: function(data) { 10760 10761 if (data == null || data.data == null) { 10762 finish(); 10763 return; 10764 } 10765 10766 // convert to older DBS version 10767 /* 10768 { 10769 "id":"eng-CEV", 10770 "name":"Contemporary English Version (US Version)", 10771 "nameEnglish":"", 10772 "abbr":"CEV", 10773 "lang":"eng", 10774 "langName":"English", 10775 "langNameEnglish":"English", 10776 "dir":"ltr", 10777 "type":"bible", 10778 "absid":"eng-CEV", 10779 "absAudio":"NONE" 10780 }, 10781 */ 10782 text_data = []; 10783 for (var id in data.data) { 10784 10785 var dbs = data.data[id]; 10786 10787 // skip not in include list 10788 if (sofia.config.dbsIncludeList.length > 0 && sofia.config.dbsIncludeList.indexOf(dbs.abbr) == -1) { 10789 continue; 10790 } 10791 10792 // skip in exclude list 10793 if (sofia.config.dbsExcludeList.length > 0 && sofia.config.dbsExcludeList.indexOf(dbs.abbr) > -1) { 10794 continue; 10795 } 10796 10797 // keep this one! 10798 if (dbs.name != null) { 10799 10800 // find the plain text id 10801 var text_plain = dbs.filesets['dbp-prod'].filter(function(fileset) { return fileset.type == "text_plain";}); 10802 10803 var sofiabible = { 10804 type: 'bible', 10805 id: dbs.abbr, 10806 name: dbs.name, 10807 nameEnglish: dbs.vname, 10808 abbr: dbs.abbr.replace(/ENG/,''), 10809 lang: dbs.iso, 10810 langName: dbs.language, 10811 langNameEnglish: dbs.language, 10812 dbfilsPlainTextId: text_plain.length > 0 ? text_plain[0].id : '', 10813 dbs: dbs 10814 }; 10815 text_data.push(sofiabible); 10816 } 10817 } 10818 10819 TextLoader.processTexts(text_data, providerName); 10820 10821 finish(); 10822 }, 10823 error: function(jqXHR, textStatus, errorThrown) { 10824 10825 text_data = null; 10826 finish(); 10827 } 10828 }); 10829 } 10830 } 10831 10832 function finish() { 10833 text_data_is_loading = false; 10834 text_data_is_loaded = true; 10835 10836 while (text_data_callbacks.length > 0) { 10837 var cb = text_data_callbacks.pop(); 10838 cb(text_data); 10839 } 10840 } 10841 10842 function getProviderid(textid) { 10843 var parts = textid.split(':'), 10844 fullid = providerName + ':' + (parts.length > 1 ? parts[1] : parts[0]); 10845 10846 return fullid; 10847 } 10848 10849 function getTextInfo(textid, callback) { 10850 10851 if (!text_data_is_loaded) { 10852 10853 getTextManifest (function() { 10854 getTextInfo(textid, callback); 10855 }); 10856 return; 10857 } 10858 10859 var providerid = getProviderid(textid); 10860 10861 // get initial data 10862 var info = text_data.filter(function(text) { 10863 return text.providerid == providerid; 10864 })[0]; 10865 10866 if (typeof info.divisions == 'undefined' || info.divisions.length == 0) { 10867 10868 $.ajax({ 10869 url: sofia.config.dbsBase + 'bibles/' + info.id + '?v=4&key=' + sofia.config.dbsKey , 10870 dataType: 'json', 10871 /* 10872 dataType: 'jsonp', 10873 beforeSend: function(xhr){ 10874 if (xhr.overrideMimeType) { 10875 xhr.overrideMimeType('application/javascript'); 10876 } 10877 }, 10878 */ 10879 cache: false, 10880 success: function(data) { 10881 10882 10883 // transfer books and chapters 10884 /* 10885 divisions: ['GN','EX'] 10886 sections: ['GN1','GN2'] 10887 */ 10888 10889 info.divisions = []; 10890 info.sections = []; 10891 info.divisionNames = []; 10892 10893 // book list (divisions) 10894 for (var i=0, il=data.data.books.length; i<il; i++) { 10895 var bookinfo = data.data.books[i], 10896 usfmCode = bookinfo.book_id, 10897 bookIndex = bible.APOCRYPHAL_BIBLE_USFM.indexOf(usfmCode), 10898 dbsCode = bible.APOCRYPHAL_BIBLE[bookIndex]; 10899 10900 if (typeof dbsCode == 'undefined') { 10901 console.warn(bookinfo, usfmCode); 10902 } 10903 10904 info.divisions.push(dbsCode); 10905 info.divisionNames.push(bookinfo.name); 10906 10907 // chapter list (sections) 10908 for (var j=0, jl=bookinfo.chapters.length; j<jl; j++) { 10909 10910 info.sections.push(dbsCode + (j+1)); 10911 10912 } 10913 10914 } 10915 10916 10917 console.log('converted DBS' + info.id); 10918 console.log(info); 10919 10920 10921 10922 //$.extend(info, data); 10923 callback(info); 10924 10925 }, 10926 error: function(jqXHR, textStatus, errorThrown) { 10927 callback(null); 10928 } 10929 10930 }); 10931 10932 } else { 10933 10934 callback(info); 10935 } 10936 10937 } 10938 10939 10940 function getTextInfoSync(textid) { 10941 10942 var providerid = getProviderid(textid); 10943 10944 // get initial data 10945 var info = text_data.filter(function(text) { 10946 return text.providerid == providerid; 10947 })[0]; 10948 10949 return info; 10950 } 10951 10952
10953 function loadSection(textid, sectionid, callback) { 10954 10955 loadSectionText(textid, sectionid, callback); 10956 10957 } 10958 10959 10960 function loadSectionText(textid, sectionid, callback) { 10961 10962 var textinfo = getTextInfoSync(textid), 10963 10964 lang3 = textinfo.lang, 10965 lang = iso2iana.convert(lang3), 10966 dir = (textinfo.dir && (textinfo.dir == 'ltr' || textinfo.dir == 'rtl')) ? textinfo.dir : data.language.isRTL(lang) ? 'rtl' : 'ltr', 10967 10968 10969 bookid = dbsBookCode = sectionid.substring(0,2), 10970 usfm = bible.BOOK_DATA[dbsBookCode].usfm, 10971 chapterNum = sectionid.substring(2), 10972 10973 sectionIndex = textinfo.sections.indexOf(sectionid), 10974 previd = sectionIndex > 0 ? textinfo.sections[sectionIndex-1] : null, 10975 nextid = sectionIndex < textinfo.sections.length ? textinfo.sections[sectionIndex+1] : null; 10976 10977 10978 // TEXT VERSION 10979 10980 10981 $.ajax({ 10982 url: sofia.config.dbsBase + 'bibles/filesets/' + textinfo.dbsPlainTextId + '/' + usfm + '/' + chapterNum + '?v=4&key=' + sofia.config.dbsKey , 10983 dataType: 'json', 10984 /* 10985 dataType: 'jsonp', 10986 beforeSend: function(xhr){ 10987 if (xhr.overrideMimeType) { 10988 xhr.overrideMimeType('application/javascript'); 10989 } 10990 }, 10991 */ 10992 cache: false, 10993 success: function(data) { 10994 10995 // create HTML 10996 var html = []; 10997 10998 //<div class="section chapter AC AC1 eng_kjv eng" dir="ltr" lang="en" data-id="AC1" data-nextid="AC2" data-previd="JN21"> 10999 html.push('<div class="section chapter ' + textid + ' ' + bookid + ' ' + sectionid + ' ' + iso2iana.convert(lang) + ' " ' + 11000 ' data-textid="' + textid + '"' + 11001 ' data-id="' + sectionid + '"' + 11002 ' data-nextid="' + nextid + '"' + 11003 ' data-previd="' + previd + '"' + 11004 ' lang="' + lang + '"' + 11005 ' data-lang3="' + lang + '"' + 11006 ' dir="' + dir + '"' + 11007 '>'); 11008 11009 11010 if (chapterNum == '1') { 11011 html.push('<div class="mt">' + textinfo.divisionNames[textinfo.divisions.indexOf(bookid)] + '</div>'); 11012 } 11013 11014 html.push('<div class="c">' + chapterNum + '</div>'); 11015 11016 html.push('<div class="p">'); 11017 for (var i=0, il=data.data.length; i<il; i++ ) { 11018 var verse = data.data[i], 11019 text = verse.verse_text, 11020 vnum = verse.verse_start, 11021 vid = sectionid + '_' + vnum; 11022 11023 html.push(' <span class="v-num v-' + vnum + '">' + vnum + '</span><span class="v ' + vid + '" data-id="' + vid + '">
11023' + text + '</span>'); 11024 11025 } 11026 11027 html.push('</div>'); // p 11028 html.push('</div>'); // section 11029 11030 11031 11032 callback(html.join('')); 11033 11034 11035 }, 11036 error: function(jqXHR, textStatus, errorThrown) { 11037 callback(null); 11038 } 11039 11040 }); 11041 } 11042 11043 function startSearch(textid, divisions, text, onSearchLoad, onSearchIndexComplete, onSearchComplete) { 11044 11045 var e = { 11046 type:'complete', 11047 target: this, 11048 data: { 11049 results: [], 11050 searchIndexesData: [], // not needed for SearchWindow 11051 searchTermsRegExp: SearchTools.createSearchTerms(text, false), 11052 isLemmaSearch: false 11053 } 11054 }; 11055 11056 doSearch(textid, divisions, text, e, function() { 11057 11058 onSearchComplete(e); 11059 11060 }); 11061 } 11062 function doSearch(textid, divisions, text, e, callback) { 11063 11064 var textinfo = getTextInfoSync(textid); 11065 11066 $.ajax({ 11067 /* 11068 beforeSend: function(xhr){ 11069 if (xhr.overrideMimeType){ 11070 xhr.overrideMimeType("application/javascript"); 11071 } 11072 }, 11073 dataType: 'jsonp', 11074 */ 11075 dataType: 'json', 11076 11077 // One giant call seems faster, than doing all the books individually?
11078 url: sofia.config.dbsBase + 'text/search?v=4&key=' + sofia.config.dbsKey + '&fileset_id=' + textid + '&query=' + text.replace(/\s/gi, '+') + '&limit=2000', 11079 success: function(data) { 11080 11081 for (var i=0, il=data.data.length; i<il; i++) { 11082 var verse = data.data[i], 11083 dbsBookCode = bible.DEFAULT_BIBLE[ bible.DEFAULT_BIBLE_USFM.indexOf(verse.book_id) ], 11084 fragmentid = dbsBookCode + verse.chapter + '_' + verse.verse_start, 11085 hasMatch = e.data.searchTermsRegExp[0].test(verse.verse_text); 11086 11087 if (hasMatch && (divisions.length == 0 || divisions.indexOf(dbsBookCode) > -1)) { 11088 e.data.results.push({ 11089 fragmentid: fragmentid, 11090 html: highlightWords(verse.verse_text, e.data.searchTermsRegExp) 11091 }); 11092 } 11093 } 11094 11095 callback(data); 11096 } 11097 }); 11098 } 11099 11100 function highlightWords(text, searchTermsRegExp) { 11101 11102 var processedHtml = text; 11103 11104 for (var j=0, jl=searchTermsRegExp.length; j<jl; j++) { 11105 11106 searchTermsRegExp[j].lastIndex = 0; 11107 11108 // surround the word with a highlight 11109 processedHtml = processedHtml.replace(searchTermsRegExp[j], function(match) { 11110 return '<span class="highlight">' + match + '</span>'; 11111 }); 11112 } 11113 11114 return processedHtml; 11115 } 11116 11117 11118 11119 return { 11120 getTextManifest: getTextManifest, 11121 getTextInfo: getTextInfo, 11122 loadSection: loadSection, 11123 startSearch: startSearch, 11124 fullName: fullName 11125 } 11126 11127})(); 11128 11129 11130 11131/********* 11132* ../app/js/texts/search.js 11133**********/ 11134// truth AND Love 11135// 1. load indexes 11136// 2. find verses with both terms 11137// 3. reduce to chapters 11138// 4. load chapters 11139// 5. extract verses 11140 11141 11142// truth OR love 11143// 1. load indexes 11144// 2. merge lists of verses into canonical order 11145// 3. reduce to chapters 11146// 4. load chapters 11147// 5. extract verses 11148 11149 11150// "in truth" 11151// 1. load indexes 11152// 2. ... 11153// 3. reduce to chapters 11154// 4. load chapters 11155// 5. extract verses 11156 11157 11158// truth love 11159// 1. NO INDEX 11160// 2. load every chapter 11161// 3. use regexp to find verses with words 11162 11163var singleWordLanguages = ['cht','chs','chi','zho','cmn', 'jpn', 'kor']; 11164 11165TextSearch = function() { 11166 11167 var 11168 baseContentPath = sofia.config.baseContentUrl + 'content/' + 'texts/', 11169 isSearching = false, 11170 canceled = false, 11171 searchText = '', 11172 searchTextid = '', 11173 searchDivisions = [], 11174 //isAsciiRegExp = new RegExp('^[\040-\176]*$', 'gi'), 11175 isLemmaRegExp = /[GgHh]\d{1,6}/g, 11176 11177 isLemmaSearch = false, 11178 startTime = null, 11179 11180 searchTermsRegExp = [], 11181 searchIndexLoader = new SearchIndexLoader(), 11182 searchIndexesData = [], 11183 searchIndexesCurrentIndex = 0, 11184 11185 searchType = 'AND', 11186 11187 searchFinalResults = []; 11188 ; 11189 11190 searchIndexLoader.on('complete', indexesLoaded); 11191 11192 function start(textid, divisions, text) { 11193 11194 //console.log('TextSearch.start', text, textid); 11195 11196 if (isSearching) { 11197 //console.log('already started ... return'); 11198 return false; 11199 } 11200 isSearching = true; 11201 11202 // store variables 11203 searchText = text.trim(); 11204 searchTextid = textid; 11205 searchDivisions = divisions; 11206 textInfo = TextLoader.getText(searchTextid); 11207 11208 11209 // reset 11210 canceled = false; 11211 startTime = new Date(); 11212 searchFinalResults = []; 11213 searchTermsRegExp = []; 11214 searchIndexesData = [] 11215 searchIndexesCurrentIndex = 0; 11216 searchType = /\bOR\b/gi.test(text) ? 'OR' : 'AND'; 11217 11218 //createSearchTerms(); 11219 isLemmaSearch = isLemmaRegExp.test(searchText); 11220 searchTermsRegExp = SearchTools.createSearchTerms(text, isLemmaSearch); 11221 11222 if (sofia.config.serverSearchPath != '' && (window.location.protocol != 'file:' || sofia.config.baseContentUrl != '')) { 11223 11224 startServerSearch(textInfo, searchDivisions, searchText, isLemmaSearch); 11225 } else { 11226 // load indexes 11227 searchIndexLoader.loadIndexes(textInfo, searchDivisions, searchText, isLemmaSearch); 11228 } 11229 11230 return true; 11231 } 11232 11233 function startServerSearch(textInfo, searchDivisions, searchText, isLemmaSearch) { 11234 11235 $.ajax({ 11236 dataType: 'jsonp', 11237 url: sofia.config.baseContentUrl + sofia.config.serverSearchPath, 11238 data: { 11239 textid: textInfo.id, 11240 search: searchText.toLowerCase(), 11241 divisions: searchDivisions.join(','), 11242 date: (new Date()).toString() 11243 }, 11244 success: function(data) { 11245 11246 // create results 11247 if (data && data.results) { 11248 11249 // STEMs? 11250 if (typeof data.stem_words != 'undefinded' && data.stem_words.length > 0) { 11251 11252 searchType = 'OR'; 11253 // redo search terms 11254 searchTermsRegExp = []; 11255 11256 for (var i=0, il=data.stem_words.length; i<il; i++) { 11257 searchTermsRegExp.push( new XRegExp('\\b(' + data.stem_words[i] + ')\\b', 'gi') ); 11258 } 11259 11260 } 11261 11262 11263 11264 for (var i=0, il=data.results.length; i<il; i++) { 11265 var result = data.results[i], 11266 fragmentid = Object.keys(result)[0], 11267 html = result[fragmentid]; 11268 11269 11270 var result = findMatchesInVerse(html); 11271 11272 if (result.foundMatch) { 11273 searchFinalResults.push({fragmentid: fragmentid, html: result.html});
11274 } else { 11275 console.log('no match', html); 11276 } 11277 11278 // add to results 11279 //searchFinalResults.push({fragmentid: fragmentid, html: html}); 11280 } 11281 11282 11283 ext.trigger('complete', {type: 'complete', target:this, data: {results: searchFinalResults, searchIndexesData: searchIndexesData, searchTermsRegExp: searchTermsRegExp, isLemmaSearch: isLemmaSearch}}); 11284 11285 11286 } else { 11287 11288 ext.trigger('complete', {type: 'complete', target: this, data: {results: null, searchIndexesData: searchIndexesData, searchTermsRegExp: searchTermsRegExp, isLemmaSearch: isLemmaSearch}}); 11289 11290 11291 } 11292 11293 11294 11295 isSearching = false; 11296 }, 11297 error: function(a,b,c,d) { 11298 console.log('error:serverSearch', a,b,c,d); 11299 //reset() 11300 } 11301 }) 11302 } 11303 11304 // fires after indexer loader is done 11305 function indexesLoaded(e) { 11306 //console.log('searchIndexLoader:complete', e.data); 11307 11308 if (e.data.loadedIndexes.length == 0) { 11309 11310 // BRUTE FORCE? 11311 //console.log('BRUTE FORCE'); 11312 11313 // create "index" of all verses? 11314 searchIndexesData = []; 11315 for (var i=0, il = textInfo.sections.length; i<il; i++) { 11316 var sectionid = textInfo.sections[i], 11317 dbsBookCode = sectionid.substr(0,2), 11318 chapterNumber = parseInt(sectionid.substr(2), 10), 11319 sectionData = { 11320 sectionid: sectionid, 11321 fragmentids: [] 11322 }; 11323 11324 for (v=1; v<=bible.BOOK_DATA[dbsBookCode].chapters[chapterNumber-1]; v++ ) { 11325 sectionData.fragmentids.push(sectionid + '_' + v); 11326 } 11327 11328 11329 searchIndexesData.push(sectionData); 11330 } 11331 11332 loadNextSectionid(); 11333 11334 11335 11336 /* 11337 isSearching = false; 11338 ext.trigger('indexerror', {type: 'indexerror', target:this, data: {results: searchFinalResults}}); 11339 */ 11340 11341 } else { 11342 ext.trigger('indexcomplete', {type: 'indexcomplete', target:this, data: {searchIndexesData: e.data.loadedResults }}); 11343 11344 // begin loading 11345 11346 if (e.data.stemInfo && e.data.stemInfo.length > 0) { 11347 11348 //if (typeof data.stem_words != 'undefinded' ) { 11349 11350 searchType = 'OR'; 11351 // redo search terms 11352 searchTermsRegExp = []; 11353 11354 for (var si=0, sil=e.data.stemInfo.length; si <sil; si++) { 11355 var stemWords = e.data.stemInfo[si].words; 11356 11357 for (var i=0, il=stemWords.length; i<il; i++) { 11358 searchTermsRegExp.push( new XRegExp('\\b(' + stemWords[i] + ')\\b', 'gi') ); 11359 } 11360 11361 } 11362 //} 11363 } 11364 11365 11366 searchIndexesData = e.data.loadedResults; 11367 searchIndexesCurrentIndex = -1; 11368 11369 //console.log('start loading indexes', searchIndexesData.length, searchIndexesCurrentIndex); 11370 loadNextSectionid(); 11371 } 11372 11373 } 11374 11375 function loadNextSectionid() { 11376 searchIndexesCurrentIndex++; 11377 11378 ////console.log('loadNextSectionid', searchIndexesData.length, searchIndexesCurrentIndex); 11379 11380 if (searchIndexesCurrentIndex > searchIndexesData.length) { 11381 11382 //console.log('OVER'); 11383 11384 isSearching = false; 11385 11386 } else if (searchIndexesCurrentIndex == searchIndexesData.length) { 11387 // DONE! 11388 11389 //console.log('textSearch:complete'); 11390 11391 ext.trigger('complete', {type: 'complete', target:this, data: {results: searchFinalResults, searchIndexesData: searchIndexesData, searchTermsRegExp: searchTermsRegExp, isLemmaSearch: isLemmaSearch}}); 11392 11393 isSearching = false; 11394 11395 } else { 11396 var sectionData = searchIndexesData[searchIndexesCurrentIndex], 11397 sectionid = sectionData ? sectionData.sectionid : null, 11398 fragmentids = sectionData ? sectionData.fragmentids : null; 11399 ;// url = baseContentPath + textInfo.id + '/' + sectionid + '.json'; 11400 11401 if (!sectionData) { 11402 loadNextSectionid(); 11403 return; 11404 } 11405 11406 ext.trigger('load', {type: 'load', target:this, data: {sectionid: sectionid, index: searchIndexesCurrentIndex, total: searchIndexesData.length}}); 11407 11408 TextLoader.loadSection(textInfo, sectionid, function(content) { 11409 11410 for (var i=0, il=fragmentids.length; i<il; i++) { 11411 var 11412 fragmentid = fragmentids[i], 11413 fragmentNode = content.find('.' + fragmentid).clone(), 11414 11415 // assuming a single node 11416 //html = fragmentNode.html(); 11417 11418 html = ''; 11419 11420 // remove notes 11421 fragmentNode.find('.note, .cf, .v-num, .verse-num').remove(); 11422 11423 // concat verses split over multiple <span class="v"> nodes (paragraphs) 11424 fragmentNode.each(function(i,el) { 11425 html += $(el).html() + ' '; 11426 });
11427 11428 if (fragmentNode.length > 0) { 11429 11430 var result = findMatchesInVerse(html); 11431 11432 if (result.foundMatch) { 11433 searchFinalResults.push({fragmentid: fragmentid, html: result.html}); 11434 } 11435 } 11436 } 11437 11438 11439 // DEBUG!! 11440 //setTimeout(function() { 11441 loadNextSectionid(); 11442 //}, 10); 11443 11444 11445 }, function(error) { 11446 11447 //console.log('searchindex:error'); 11448 11449 loadNextSectionid(); 11450 }); 11451 11452 } 11453 } 11454 11455 11456 function findMatchesInVerse(html) { 11457 var processedHtml = html, 11458 foundMatch = false, 11459 regMatches = new Array(searchTermsRegExp.length); 11460 11461 for (var j=0, jl=searchTermsRegExp.length; j<jl; j++) { 11462 11463 searchTermsRegExp[j].lastIndex = 0; 11464 11465 if (isLemmaSearch) { 11466 11467 // add the 'highlight' class to the <l> node 11468 processedHtml = processedHtml.replace(searchTermsRegExp[j], function(match) { 11469 regMatches[j] = true; 11470 foundMatch = true; 11471 return match + ' class="highlight" '; 11472 }); 11473 11474 } else { 11475 11476 // surround the word with a highlight 11477 processedHtml = processedHtml.replace(searchTermsRegExp[j], function(match) { 11478 regMatches[j] = true; 11479 foundMatch = true; 11480 return '<span class="highlight">' + match + '</span>'; 11481 }); 11482 11483 } 11484 } 11485 11486 if (searchType == 'AND') { 11487 var foundAll = true; 11488 for (var j=0, jl=regMatches.length; j<jl; j++) { 11489 if (regMatches[j] !== true) { 11490 foundAll = false; 11491 break; 11492 } 11493 } 11494 foundMatch = foundAll; 11495 11496 } 11497 11498 return {html: processedHtml, foundMatch: foundMatch}; 11499 } 11500 11501 11502 11503 11504 var ext = { 11505 start: start, 11506 findMatchesInVerse: findMatchesInVerse 11507 }; 11508 ext = $.extend(true, ext, EventEmitter); 11509 11510 return ext; 11511}; 11512 11513SearchTools = { 11514 11515 isAsciiRegExp: new RegExp('^[\040-\176]*$', 'gi'), 11516 11517 isLemmaRegExp: /[GgHh]\d{1,6}/g, 11518 11519 createSearchTerms: function (searchText, isLemmaSearch) { 11520 var searchTermsRegExp = []; 11521 11522 if (isLemmaSearch) { 11523 11524 var strongNumbers = searchText.split(' '); 11525 11526 for (var i=0, il=strongNumbers.length; i<il; i++) { 11527 11528 var part = strongNumbers[i]; 11529 11530 searchTermsRegExp.push( new RegExp('s=("|\')(\\w\\d{1,4}[a-z]?\\s)?' + '(G|H)?' + part.substr(1) + '[a-z]?(\\s\\w\\d{1,4}[a-z]?)?("|\')', 'gi') ); 11531 11532 } 11533 11534 } else { 11535 11536 11537 11538 // check for quoted search "jesus christ" 11539 if (searchText.substring(0,1) == '"' && searchText.substring(searchText.length-1) == '"') { 11540 11541 var withoutQuotes = searchText.substring(1,searchText.length-1); 11542 withoutQuotes = withoutQuotes.replace(/\s/g,'(\\s?(<(.|\\n)*?>)?\\s?)?'); 11543 11544 searchTermsRegExp.push( new XRegExp('\\b(' + withoutQuotes + ')\\b', 'gi') ); 11545 11546 } else { 11547 11548 // ASCII characters have predictable word boundaries (space ' ' = \b) 11549 SearchTools.isAsciiRegExp.lastIndex = 0; 11550 11551 if (SearchTools.isAsciiRegExp.test( searchText )) { 11552 11553 // for non-quoted searches, use "AND" search 11554 var andSearchParts = searchText.split(/\s+AND\s+|\s+/gi); 11555 11556 // filter for duplicate words 11557 andSearchParts = $.unique(andSearchParts); 11558 11559 for (var i=0, il=andSearchParts.length; i<il; i++) { 11560 11561 var part = andSearchParts[i], 11562 partRegex = new XRegExp('\\b(' + part + ')\\b', 'gi'); 11563 11564 searchTermsRegExp.push( partRegex ); 11565 } 11566 11567 } else { 11568 11569 var words = SearchTools.splitWords(searchText); 11570 11571 for (var j=0, jl=words.length; j<jl; j++) { 11572 11573 searchTermsRegExp.push( new XRegExp(words[j], 'gi') ); 11574 11575 } 11576 } 11577 } 11578 } 11579 11580 return searchTermsRegExp; 11581 }, 11582 11583 splitWords: function(input) { 11584 11585 var 11586 removeRegChars = ['\\', '^', '$', '.', '|', '?', '*', '+', '(', ')', '[', ']', '{', '}']; 11587 otherRemoveChars = [ 11588 // roman 11589 ',',';', '!', '-', 'â', 'â', 'â', '~', ':', '"','/', "'s", 'âs', "'", 'â', 'â', 'â', 'â', '¿', '<', '>', '&', 11590 // chinese 11591 'ã', 'ï¼', 'ï¼', 'â', 'â', 'ï¼', 'ï¼', '~', 'ã', 'ã' 11592 ], 11593 punctuation = [].concat(removeRegChars).concat(otherRemoveChars), 11594 innerWordExceptions = ["'", 'â', '-'], 11595 words = [], 11596 word = ''; 11597 11598 function addWord() { 11599 if (word != '') { 11600 words.push(word); 11601 } 11602 word = ''; 11603 } 11604 11605 // formalize string 11606 input = new String(input); 11607 11608 // yes or no on apostrophes 11609 input = input.replace(/(['â]s)/gi, ''); 11610 11611 for (var i = 0, il = input.length; i<il; i++) { 11612 var letter = input.charAt(i),
11613 charCode = input.charCodeAt(i), 11614 isFirstChar = (i==0); 11615 isLastChar = (i==il-1), 11616 isPunctuation = punctuation.indexOf(letter) > -1, 11617 isWhitespace = letter == ' ', 11618 isLetter = !(isWhitespace || isPunctuation); 11619 11620 // if this is a letter 11621 if ( isLetter ) { 11622 11623 word += letter; 11624 11625 // If this is a Chinese/Japanese/Korean ideograph, it is a word by itself. No separator is needed. 11626 if (((charCode >= 0x4E00) && (charCode <= 0x9FFF)) || ((charCode >= 0x3400) && (charCode <= 0x4DFF)) || ((charCode >= 0x20000) && (charCode <= 0x2A6DF))) { 11627 11628 addWord(); // Technically, some ideographs combine to make a compound word, but concordance/search will work without that refinement, possibly with extra hits. 11629 } 11630 11631 } else if (!isFirstChar && !isLastChar && innerWordExceptions.indexOf(letter) > -1 && punctuation.indexOf(input[i-1]) == -1 && punctuation.indexOf(input[i+1]) == -1) { 11632 11633 word += letter; 11634 11635 } else { 11636 // it was punctuation! 11637 addWord(); 11638 } 11639 11640 ////console.log(letter, charCode); 11641 } 11642 11643 addWord(); 11644 11645 words = $.unique(words); 11646 11647 return words; 11648 }, 11649 11650 HASHSIZE: 20, 11651 11652 hashWord: function(word) { 11653 var hash = 0; 11654 for (i = 0; i < word.length; i++) { 11655 hash += word.charCodeAt(i); 11656 hash %= SearchTools.HASHSIZE; 11657 } 11658 return hash; 11659 } 11660}; 11661 11662SearchIndexLoader = function() { 11663 11664 var 11665 baseContentPath = sofia.config.baseContentUrl + 'content/' + 'texts/', 11666 textInfo = null, 11667 searchTerms = [], 11668 searchTermsIndex = -1, 11669 isLemmaSearch = false, 11670 isStemEnabled = true, 11671 stemmingData = {}, 11672 stemInfo = [], 11673 searchDivisions = [], 11674 // initial load: [{term:'light': occurrences: ['GN1_2', 'GN2_5']}, {term: 'love': ['JN3']} 11675 loadedIndexes = [], 11676 // final: [{sectionid:'GN1', fragmentids: ['GN1_2', 'GN1_3']}, {sectionid:'GN2', fragmentids: ['GN2_4']} } 11677 loadedResults = [], 11678 searchType = 'AND'; // OR 11679 11680 // START 11681 function loadIndexes(newTextInfo, divisions, searchText, isLemma) { 11682 11683 isLemmaSearch = isLemma; 11684 textInfo = newTextInfo; 11685 searchDivisions = divisions; 11686 11687 searchTerms = SearchTools.splitWords(searchText); 11688 11689 searchTermsIndex = -1; 11690 loadedIndexes = []; 11691 loadedResults = []; 11692 stemInfo = []; 11693 stemmingData = isLemmaSearch ? null : {}; 11694 11695 searchType = /\bOR\b/gi.test(searchText) ? 'OR' : 'AND'; 11696 11697 if (isStemEnabled && !isLemmaSearch) { 11698 loadStemmingData(); 11699 } else { 11700 loadNextIndex(); 11701 } 11702 } 11703 11704 function loadStemmingData() { 11705 11706 var stemUrl = baseContentPath + textInfo.id + '/index/stems.json'; 11707 11708 // attempt to load in index 11709 $.ajax({ 11710 beforeSend: function(xhr){ 11711 if (xhr.overrideMimeType){ 11712 xhr.overrideMimeType("application/json"); 11713 } 11714 }, 11715 11716 dataType: 'json', 11717 url: stemUrl, 11718 success: function(data) { 11719 console.log('Loaded Stems!', textInfo.id); 11720 11721 stemmingData = data; 11722 11723 loadNextIndex(); 11724 }, 11725 error: function() { 11726 // set it to null in order to check later 11727 stemmingData = null; 11728 11729 console.log('No stem data for', textInfo.id); 11730 loadNextIndex(); 11731 } 11732 }); 11733 } 11734 11735 11736 function loadNextIndex() { 11737 11738 // starts at -1, so this will make the first one 0 11739 searchTermsIndex++; 11740 11741 if (searchTermsIndex < searchTerms.length) { 11742 11743 loadSearchTermIndex( searchTerms[searchTermsIndex] ); 11744 11745 } else { 11746 11747 // if we've done all the indexes, then it's time to start combining them
11748 //combineIndexes(); 11749 processIndexes(); 11750 } 11751 11752 } 11753 11754 function loadSearchTermIndex(searchTerm) { 11755 11756 var indexUrl = '', 11757 key = '', 11758 hash = '', 11759 stem = ''; 11760 11761 if (isLemmaSearch) { 11762 key = searchTerm.toUpperCase(); 11763 var letter = key.substr(0,1), 11764 firstNumber = searchTerm.length >= 5 ? searchTerm.substr(1,1) : '0'; 11765 11766 indexUrl = baseContentPath + textInfo.id + '/indexlemma/_' + letter.toUpperCase() + firstNumber + '000' + '.json'; 11767 11768 } else { 11769 key = searchTerm.toLowerCase(); 11770 11771 if (isStemEnabled && stemmingData != null) { 11772 stem = stemmingData[key], 11773 hash = SearchTools.hashWord(stem); 11774 indexUrl = baseContentPath + textInfo.id + '/index/_stems_' + hash + '.json'; 11775 11776 } else { 11777 hash = SearchTools.hashWord(key); 11778 indexUrl = baseContentPath + textInfo.id + '/index/_' + hash + '.json'; 11779 11780 } 11781 } 11782 11783 if (searchTerm == 'undefined') { 11784 //console.log('STOP search. undefined term'); 11785 return; 11786 } 11787 11788 //console.log('Loading Index:' + searchTerm + ',' + searchTermEncoded); 11789 11790 // attempt to load in index 11791 $.ajax({ 11792 beforeSend: function(xhr){ 11793 if (xhr.overrideMimeType){ 11794 xhr.overrideMimeType("application/json"); 11795 } 11796 }, 11797 11798 dataType: 'json', 11799 url: indexUrl, 11800 success: function(data) { 11801 11802 var fragments = null; 11803 11804 //console.log(key, stem, data, data[stem]); 11805 11806 if (isStemEnabled && stemmingData != null) { 11807 fragments = data[stem].fragmentids; 11808 stemInfo.push({ 11809 word: key, 11810 stem: stem, 11811 words: data[stem].words 11812 }); 11813 } else { 11814 fragments = data[key]; 11815 } 11816 11817 loadedIndexes.push(fragments); 11818 11819 loadNextIndex(); 11820 }, 11821 error: function() { 11822 //console.log('no index for: ' + searchTerm); 11823 loadNextIndex(); 11824 } 11825 11826 }); 11827 11828 } 11829 11830 function processIndexes() { 11831 11832 // we'll combine everything into this ['GN1_1', 'GN1_2'] 11833 fragmentids = []; 11834 // then pair down to this [{sectionid:'GN1', fragmentids: ['GN1_1','GN1_2']}] 11835 loadedResults = []; 11836 11837 if (loadedIndexes.length > 0) { 11838 11839 if (searchType == 'OR') { 11840 // combine all the fragments 11841 for (var i=0, il=loadedIndexes.length; i<il; i++) { 11842 fragmentids = fragmentids.concat( loadedIndexes[i] ); // .occurrences ); 11843 ////console.log(loadedIndexes[i].term); 11844 ////console.log(loadedIndexes[i].occurrences); 11845 } 11846 11847 // sort! 11848 fragmentids.sort(function(a, b) { 11849 // split into parts 11850 function splitFragment(fragmentid) { 11851 var parts = fragmentid.split('_'), 11852 sectionid = parts[0], 11853 sectionIndex = textInfo.sections.indexOf(sectionid), 11854 fragmentNum = parseInt(parts[1], 10), 11855 value = { 11856 sectionid: sectionid, 11857 sectionIndex: sectionIndex, 11858 fragmentNum: fragmentNum 11859 }; 11860 11861 return value; 11862 } 11863 11864 var fraga = splitFragment(a), 11865 fragb = splitFragment(b); 11866 11867 11868 if (fraga.sectionIndex < fragb.sectionIndex || 11869 (fraga.sectionIndex == fragb.sectionIndex && fraga.fragmentNum < fragb.fragmentNum) ) 11870 return -1; 11871 if (fraga.sectionIndex > fragb.sectionIndex || 11872 (fraga.sectionIndex == fragb.sectionIndex && fraga.fragmentNum > fragb.fragmentNum) ) 11873 return 1; 11874 // a must be equal to b 11875 return 0; 11876 }); 11877 11878 11879 } else if (searchType == 'AND') { 11880 11881 // combine arrays to only include fragments (verses) where all indexes overlap (truth AND love) 11882 var totalIndexes = loadedIndexes.length; 11883 if (totalIndexes == 1) { 11884 fragmentids = loadedIndexes[0]; // .occurrences; 11885 } else if (totalIndexes > 1) { 11886 //fragmentids = loadedIndexes[0].occurrences.filter(function(val) { 11887 fragmentids = loadedIndexes[0].filter(function(val) { 11888 var inOtherArrays = true; 11889 for (var i=1; i<totalIndexes; i++) { 11890 //if (loadedIndexes[i].occurrences.indexOf(val) == -1) { 11891 if (loadedIndexes[i].indexOf(val) == -1) { 11892 inOtherArrays = false; 11893 break; 11894 } 11895 } 11896 11897 return inOtherArrays; 11898 }); 11899 } 11900 } 11901 11902 // reformat fragments into sectionids 11903 // ['JN1_1','JN1_2'] => [{sectionid: 'JN1', fragmentids: ['JN1_1','JN1_2']}] 11904 if (fragmentids) { 11905 for (var i=0, il=fragmentids.length; i<il; i++) { 11906 var fragmentid = fragmentids[i]; 11907 11908 if (fragmentid != '' && fragmentid != null) { 11909 11910 11911 var sectionid = fragmentid.split('_')[0], 11912 dbsBookCode = sectionid.substring(0,2); 11913 11914 if (searchDivisions.length == 0 || searchDivisions.indexOf(dbsBookCode) > -1) { 11915 11916 // see if we already created data for this section id 11917 var sectionidInfo = $.grep(loadedResults, function(val){ return val.sectionid == sectionid; }); 11918 11919 // create new data 11920 if (sectionidInfo.length == 0) { 11921 loadedResults.push({sectionid: sectionid, fragmentids: [fragmentid]}); 11922 } 11923 // add to this sectionid 11924 else { 11925 sectionidInfo[0].fragmentids.push(fragmentid); 11926 } 11927 } 11928 } 11929 } 11930 } 11931 } 11932 11933 // send up the chain 11934 ext.trigger('complete', {type:'complete', target: this, data: { 11935 loadedIndexes: loadedIndexes, 11936 loadedResults: loadedResults, 11937 fragmentids: fragmentids, 11938 stemInfo: stemInfo, 11939 } 11940 });
11941 } 11942 11943 var ext = { 11944 loadIndexes: loadIndexes 11945 }; 11946 ext = $.extend(true, ext, EventEmitter); 11947 11948 return ext; 11949}; 11950 11951 11952 11953/********* 11954* ../app/js/media/audiodatamanager.js 11955**********/ 11956 11957if (typeof sofia != 'undefined') { 11958 sofia.audioSources = []; 11959} 11960 11961var AudioDataManager = function() { 11962 11963 function getAudioInfo(textInfo, callback) { 11964 11965 var index = 0; 11966 function doNext() { 11967 11968 var audioSource = sofia.audioSources[index]; 11969 audioSource.getAudioInfo(textInfo, recieveData); 11970 11971 ////console.log('-AudioSource', index, textInfo.id); 11972 } 11973 function recieveData(audioInfo) { 11974 11975 ////console.log('-AudioSource.receiveData', textInfo.id); 11976 11977 // send data backup 11978 if (audioInfo != null) { 11979 ////console.log('found: ', data); 11980 11981 audioInfo.audioSourceIndex = index; 11982 11983 callback(audioInfo); 11984 } else { 11985 index++; 11986 11987 if (index < sofia.audioSources.length) { 11988 // try again 11989 doNext(); 11990 11991 } else { 11992 // didn't find anything 11993 callback(null); 11994 } 11995 11996 } 11997 11998 } 11999 12000 doNext(); 12001 } 12002 12003 function getFragmentAudio(textInfo, audioInfo, fragmentid, audioOption, callback) { 12004 12005 // pass to the correct source using the index number assigned when getAudioInfo was called 12006 sofia.audioSources[audioInfo.audioSourceIndex].getFragmentAudio(textInfo, audioInfo, fragmentid, audioOption, callback); 12007 12008 } 12009 12010 function getNextFragment(textInfo, audioInfo, fragmentid, callback) { 12011 sofia.audioSources[audioInfo.audioSourceIndex].getNextFragment(textInfo, audioInfo, fragmentid, callback); 12012 } 12013 12014 function getPrevFragment(textInfo, audioInfo, fragmentid, callback) { 12015 sofia.audioSources[audioInfo.audioSourceIndex].getPrevFragment(textInfo, audioInfo, fragmentid, callback); 12016 } 12017 12018 12019 var audio = { 12020 getAudioInfo: getAudioInfo, 12021 getFragmentAudio: getFragmentAudio, 12022 getNextFragment: getNextFragment, 12023 getPrevFragment: getPrevFragment 12024 }; 12025 return audio; 12026}; 12027 12028 12029var LocalAudio = (function() { 12030 12031 function getAudioInfo(textInfo, callback) { 12032 ////console.log('LocalAudio', textInfo.id); 12033 12034 var checkDirectory = textInfo.id; 12035 12036 12037 if (typeof textInfo.audioDirectory != 'undefined') { 12038 12039 // if empty it means, "Don't check anything" 12040 if (textInfo.audioDirectory == '') { 12041 callback(null); 12042 return; 12043 } else { 12044 12045 checkDirectory = textInfo.audioDirectory; 12046 } 12047 } 12048 12049 sofia.ajax({ 12050 dataType: 'json', 12051 url: 'content/audio/' + checkDirectory + '/info.json', 12052 success: function(audioInfo) { 12053 12054 if (typeof audioInfo == 'undefined' ) { 12055 callback(null); 12056 return; 12057 } 12058 12059 audioInfo.type = 'local'; 12060 audioInfo.directory = checkDirectory; 12061 12062 if (!audioInfo.title) { 12063 audioInfo.title = 'Local'; 12064 } 12065 12066 ////console.log('--LocalAudio success', textInfo.id); 12067 12068 callback(audioInfo); 12069 }, 12070 error: function(e) { 12071 12072 ////console.log('--LocalAudio error', textInfo.id); 12073 12074 callback(null); 12075 } 12076 }); 12077 } 12078 12079 function getFragmentAudio(textInfo, audioInfo, fragmentid, audioOption, callback) { 12080 12081 var fragmentData = findFragmentData(audioInfo, fragmentid); 12082 12083 if (fragmentData == null) { 12084 callback(null); 12085 return; 12086 } 12087 12088 // yeah, we found one! 12089 var audioData = { 12090 url: sofia.config.baseContentUrl + 'content/' + 'audio/' + audioInfo.directory + '/' + fragmentData.filename + '.' + fragmentData.exts[0], 12091 id: fragmentData.index, 12092 start: fragmentData.start, 12093 end: fragmentData.end 12094 } 12095 12096 callback(audioData); 12097 } 12098 12099 function findFragmentData(audioInfo, fragmentid) { 12100 // split through all the files 12101 var verseParts = fragmentid.split('_'), 12102 sectionid = verseParts[0], 12103 verseNumber = parseInt(verseParts[1], 10), 12104 fragmentIndex = 0, 12105 fragmentData = null; 12106 12107 // look through all the ranges 12108 /* 12109 { 12110 "start": "GN1_1", 12111 "end": "GN1_31", 12112 "filename": "GN1", 12113 "exts": [ 12114 "mp3" 12115 ] 12116 }, 12117 */ 12118 for (var i=0, il=audioInfo.fragments.length; i<il; i++) { 12119 var fragmentFileinfo = audioInfo.fragments[i], 12120 startFragmentParts = fragmentFileinfo.start.split('_'), 12121 startSectionid = startFragmentParts[0]; 12122 12123 // if matching chapter then check if verse 12124 if (sectionid == startSectionid) { 12125 var startVerseNumber = parseInt(startFragmentParts[1], 10), 12126 endFragmentParts = fragmentFileinfo.end.split('_'), 12127 endVerseNumber = parseInt(endFragmentParts[1], 10); 12128 12129 if (verseNumber >= startVerseNumber && verseNumber <= endVerseNumber) { 12130 //filename = fragmentFileinfo.filename + '.' + fragmentFileinfo.exts[0]; 12131 12132 fragmentIndex = i; 12133 fragmentData = fragmentFileinfo; 12134 12135 break; 12136 } 12137 } 12138 } 12139 12140 if (fragmentData != null) { 12141 fragmentData.index = fragmentIndex; 12142 } 12143 12144 return fragmentData; 12145 } 12146 12147 function getNextFragment(textInfo, audioInfo, fragmentid, callback) { 12148 var fragmentData = findFragmentData(audioInfo, fragmentid); 12149 12150 if (fragmentData == null) { 12151 callback(null); 12152 return; 12153 } 12154 12155 if (fragmentData.index < audioInfo.fragments.length-1) {
12156 var nextFragmentData = audioInfo.fragments[fragmentData.index+1]; 12157 12158 callback(nextFragmentData.start); 12159 } else { 12160 callback(null); 12161 } 12162 } 12163 12164 function getPrevFragment(textInfo, audioInfo, fragmentid, callback) { 12165 var fragmentData = findFragmentData(audioInfo, fragmentid); 12166 12167 if (fragmentData == null) { 12168 callback(null); 12169 return; 12170 } 12171 12172 if (fragmentData.index > 0) { 12173 var prevFragmentData = audioInfo.fragments[fragmentData.index-1]; 12174 12175 callback(prevFragmentData.start); 12176 } else { 12177 callback(null); 12178 } 12179 } 12180 12181 var audio = { 12182 getAudioInfo: getAudioInfo, 12183 getFragmentAudio: getFragmentAudio , 12184 getNextFragment: getNextFragment, 12185 getPrevFragment: getPrevFragment 12186 }; 12187 return audio; 12188})(); 12189 12190 12191 12192 12193var FaithComesByHearingAudio = (function() { 12194 12195 var fcbhLocation = null, 12196 fcbhList = null, 12197 fcbhIsLoaded = false, 12198 12199 currentTextInfo = null, 12200 currentCallback = null 12201 12202 12203 function init() { 12204 $(function(){ 12205 loadFcbHearingLocations(); 12206 }); 12207 } 12208 12209 function loadFcbHearingLocations() { 12210 $.ajax({ 12211 dataType: 'jsonp', 12212 url: sofia.protocol + '//dbt.io/audio/location?v=2&reply=jsonp&key=' + sofia.config.fcbhKey, 12213 success: function(data) { 12214 fcbhLocation = data; 12215 }, 12216 complete: function() { 12217 loadFcbHearingInfo(); 12218 } 12219 }); 12220 } 12221 12222 function loadFcbHearingInfo() { 12223 $.ajax({ 12224 dataType: 'jsonp', 12225 url: sofia.protocol + '//dbt.io/library/volume?v=2&reply=jsonp&media=audio&delivery=web&key=' + sofia.config.fcbhKey, 12226 success: function(data) { 12227 fcbhList = data; 12228 12229 ////console.log('FCBH', fcbhLocation, fcbhList); 12230 12231 }, 12232 complete: function() { 12233 fcbhIsLoaded = true; 12234 12235 attemptGetAudioInfo(); 12236 } 12237 }); 12238 } 12239 12240 12241 function getAudioInfo(textInfo, callback) { 12242 12243 // store 12244 currentTextInfo = textInfo; 12245 currentCallback = callback; 12246 12247 // attempt 12248 attemptGetAudioInfo(); 12249 } 12250 12251 function attemptGetAudioInfo() { 12252 12253 // check for FCBH info hardcoded in the textInfo object 12254 if (fcbhIsLoaded && currentTextInfo != null) { 12255 var fcbhKeys = ['fcbh_audio_nt','fcbh_audio_ot','fcbh_drama_nt','fcbh_drama_ot'], 12256 fcbhExists = false, 12257 audioData = {title: 'FCBH', type: 'fcbh'}; 12258 12259 for (var i=0, il=fcbhKeys.length; i<il; i++) { 12260 var key = fcbhKeys[i], 12261 keyData = currentTextInfo[ key ]; 12262 12263 audioData[key] = keyData; 12264 12265 if (keyData && keyData != '') { 12266 fcbhExists = true; 12267 } 12268 } 12269 12270 // favor drama 12271 if (audioData.fcbh_drama_nt != '' || audioData.fcbh_drama_ot != '') { 12272 audioData.title += ' (drama)'; 12273 } 12274 12275 if (fcbhExists) { 12276 currentCallback(audioData); 12277 } else { 12278 currentCallback(null); 12279 } 12280 12281 currentTextInfo = null; 12282 currentCallback = null; 12283 12284 return; 12285 } 12286 12287 12288 // TEMP: no longer attempt FCBH detection 12289 return; 12290 12291 12292 // if not in textInfo, then we need to look it up from the FCBH data 12293 if (fcbhIsLoaded && currentTextInfo != null && currentCallback != null) { 12294 12295 var audioData = { 12296 title: 'FCBH', 12297 fcbh_audio_nt: getFbchCollection(currentTextInfo, 'non-drama', 'nt'), 12298 fcbh_audio_ot: getFbchCollection(currentTextInfo, 'non-drama', 'ot'), 12299 fcbh_drama_nt: getFbchCollection(currentTextInfo, 'drama', 'nt'), 12300 fcbh_drama_ot: getFbchCollection(currentTextInfo, 'drama', 'ot') 12301 }; 12302 12303 // if we find one, send it! 12304 if (audioData.fcbh_audio_nt != null || 12305 audioData.fcbh_audio_ot != null || 12306 audioData.fcbh_drama_nt != null || 12307 audioData.fcbh_drama_ot != null) { 12308 12309 currentCallback(audioData); 12310 } else { 12311 12312 currentCallback(null); 12313 } 12314 12315 12316 12317 currentTextInfo = null; 12318 currentCallback = null; 12319 } 12320 } 12321 12322 function getFbchCollection(textInfo, mediaType, testament ) { 12323 12324 var 12325 // our attempt to find a fake dam_id (real: ENGESVN2DA and ENGESVO2DA) 12326 dam_id_start = textInfo.id.replace(/[-_]/gi, '').toUpperCase(), 12327 results = []; 12328 12329 12330 // match dam_id 12331 for (var i=0, il = fcbhList.length; i<il; i++) { 12332 var fbchCollection = fcbhList[i]; 12333 12334 if (fbchCollection.dam_id && fbchCollection.dam_id.indexOf(dam_id_start) > -1) { 12335 results.push(fbchCollection); 12336 } 12337 } 12338 12339 // if no dam_id match, then try language 12340 if (results.length == 0 && textInfo.lang != 'eng') { 12341 // dam_id 12342 for (var i=0, il = fcbhList.length; i<il; i++) { 12343 var fbchCollection = fcbhList[i]; 12344 12345 if (fbchCollection.language_iso && fbchCollection.language_iso == textInfo.lang) { 12346 results.push(fbchCollection); 12347 } 12348 } 12349 } 12350 12351 // if no dam_id or langauge match, try family code 12352 if (results.length == 0 && textInfo.lang != 'eng') { 12353 // dam_id 12354 for (var i=0, il = fcbhList.length; i<il; i++) { 12355 var fbchCollection = fcbhList[i]; 12356 12357 if (fbchCollection.language_family_iso && fbchCollection.language_family_iso == textInfo.lang) { 12358 results.push(fbchCollection); 12359 } 12360 } 12361 } 12362
12363 // filter by testament if needed 12364 if (testament != '') { 12365 var testamentResults = []; 12366 12367 for (var i=0, il = results.length; i<il; i++) { 12368 var fbchCollection = results[i]; 12369 12370 if (fbchCollection.collection_code && fbchCollection.collection_code.toLowerCase() == testament) { 12371 testamentResults.push(fbchCollection); 12372 } 12373 } 12374 12375 results = testamentResults; 12376 } 12377 12378 // filter by mediaType ('drama' or 'non-drama') if possible 12379 if (mediaType != '') { 12380 var mediaTypeResults = []; 12381 12382 for (var i=0, il = results.length; i<il; i++) { 12383 var fbchCollection = results[i]; 12384 12385 if (fbchCollection.media_type && fbchCollection.media_type.toLowerCase() == mediaType) { 12386 mediaTypeResults.push(fbchCollection); 12387 } 12388 } 12389 12390 if (mediaTypeResults.length > 0) { 12391 results = mediaTypeResults; 12392 } 12393 } 12394 12395 if (results.length > 0) { 12396 return results[0].dam_id; 12397 } else { 12398 return null; 12399 } 12400 } 12401 12402 function getFbchCollectionById(dam_id) { 12403 var collection = null; 12404 12405 if (fcbhList == null) { 12406 return null; 12407 } 12408 12409 for (var i=0, il = fcbhList.length; i<il; i++) { 12410 var fbchCollection = fcbhList[i]; 12411 12412 if (fbchCollection.dam_id && fbchCollection.dam_id == dam_id) { 12413 collection = fbchCollection; 12414 break; 12415 } 12416 } 12417 12418 return collection; 12419 } 12420 12421 function getFragmentAudio(textInfo, audioInfo, fragmentid, audioOption, callback) { 12422 if (audioInfo == null) { 12423 callback(null); 12424 return; 12425 } 12426 12427 // find the right dam_id for this audio fragment 12428 var 12429 fragmentparts = fragmentid.split('_'), 12430 sectionid = fragmentparts[0], 12431 dbsBookCode = sectionid.substr(0,2), 12432 chapterNum = sectionid.substr(2), 12433 bookInfo = bible.BOOK_DATA[dbsBookCode], 12434 testament = bible.OT_BOOKS.indexOf(dbsBookCode) > -1 ? 'ot' : 12435 bible.NT_BOOKS.indexOf(dbsBookCode) > -1 ? 'nt' : '', 12436 dramaKey = 'fcbh_drama_' + testament, 12437 audioKey = 'fcbh_audio_' + testament, 12438 dam_id = ''; 12439 12440 12441 // apocrypha! 12442 if (testament == '') { 12443 callback(null); 12444 return; 12445 } 12446 12447 12448 12449 // find correct damID 12450 if (audioOption == 'audio') { 12451 12452 if (audioInfo[audioKey] && audioInfo[audioKey] != '') { 12453 dam_id = audioInfo[audioKey]; 12454 } else if (audioInfo[dramaKey] && audioInfo[dramaKey] != '') { 12455 dam_id = audioInfo[dramaKey]; 12456 } 12457 12458 } else if (audioOption == 'drama') { 12459 12460 if (audioInfo[dramaKey] && audioInfo[dramaKey] != '') { 12461 dam_id = audioInfo[dramaKey]; 12462 } else if (audioInfo[audioKey] && audioInfo[audioKey] != '') { 12463 dam_id = audioInfo[audioKey]; 12464 } 12465 } 12466 12467 //console.log('audio preference', audioOption, dam_id); 12468 12469 var collectionInfo = getFbchCollectionById(dam_id); 12470 if (collectionInfo != null) { 12471 audioInfo.title = 'FCBH: ' + collectionInfo.version_code; // + '-' + (collectionInfo.version_name != '' ? collectionInfo.version_name : collectionInfo.volume_name); 12472 } 12473 12474 var 12475 url = sofia.protocol + '//dbt.io/audio/path?v=2&reply=jsonp&dam_id=' + dam_id + '&book_id=' + bookInfo.osis.toLowerCase() + '&chapter_id=' + chapterNum.toString() + '&key=' + sofia.config.fcbhKey; 12476 12477 $.ajax({ 12478 dataType: 'jsonp', 12479 url: url, 12480 success: function(pathData) { 12481 12482 if (fcbhLocation == null || fcbhLocation.length == 0) { 12483 callback(null); 12484 return; 12485 } 12486 12487 if (pathData.length == 0) { 12488 callback(null); 12489 return; 12490 } 12491 12492 var server = fcbhLocation[0], 12493 url = server.protocol + "://" + server.server + server.root_path + "/" + pathData[0].path, 12494 audioData = { 12495 id: sectionid, 12496 url: url, 12497 start: sectionid + '_1', 12498 end: '' 12499 }; 12500 12501 if (callback) { 12502 callback(audioData); 12503 } 12504 }, 12505 error: function(a,b,c,d) { 12506 12507 //console.log('FCBH location error', a,b,c,d); 12508 12509 if (callback) { 12510 callback(null); 12511 } 12512 } 12513 }); 12514 12515 } 12516 12517 function getNextFragment(textInfo, audioInfo, fragmentid, callback) { 12518 12519 // next chapter! 12520 var sectionid = fragmentid.split('_')[0], 12521 reference = new bible.Reference(sectionid), 12522 nextChapter = reference.nextChapter(); 12523 12524 callback( nextChapter.toSection() ); 12525 12526 } 12527 12528 function getPrevFragment(textInfo, audioInfo, fragmentid, callback) { 12529 12530 // prev chapter! 12531 var sectionid = fragmentid.split('_')[0], 12532 reference = new bible.Reference(sectionid), 12533 prevChapter = reference.prevChapter(); 12534 12535 callback( prevChapter.toSection() ); 12536 } 12537 12538 var audio = { 12539 getAudioInfo: getAudioInfo, 12540 getFragmentAudio: getFragmentAudio, 12541 getFbchCollection: getFbchCollection, 12542 getFragmentAudio: getFragmentAudio, 12543 fcbhIsLoaded: fcbhIsLoaded, 12544 getNextFragment: getNextFragment, 12545 getPrevFragment: getPrevFragment 12546 }; 12547 12548 init(); 12549 12550 return audio; 12551})(); 12552 12553 12554// FCBH First (March 2014) 12555sofia.initMethods.push(function() { 12556 if (typeof sofia != 'undefined') {
12557 if (sofia.config.enableOnlineSources) { 12558 12559 sofia.audioSources.push(FaithComesByHearingAudio); 12560 } 12561 } 12562 12563 if (typeof sofia != 'undefined') { 12564 sofia.audioSources.push(LocalAudio); 12565 } 12566}); 12567 12568 12569 12570/********* 12571* ../app/js/media/jesusfilmapi.js 12572**********/ 12573var JesusFilmMediaApi = (function() { 12574 12575 var 12576 isLoaded = false, 12577 isLoading = false, 12578 jfmLanguages = null, 12579 getPlayerCallbacks = []; 12580 12581 function load() { 12582 12583 if (isLoaded || isLoading) { 12584 return; 12585 } 12586 12587 isLoading = true; 12588 isLoaded = false; 12589 12590 $.ajax({ 12591 12592 url: 'content/media/jesusfilmmedia/jesusfilmlanguages.json', 12593 success: function(data) { 12594 12595 isLoading = true; 12596 isLoaded = true; 12597 12598 jfmLanguages = data; 12599 12600 findPlayers(); 12601 }, 12602 error: function(a,b,c,d) { 12603 console.log('error', a,b,c,d); 12604 } 12605 12606 }); 12607 } 12608 12609 // public method 12610 function getPlayer(lang, segmentNumber, callback) { 12611 12612 getPlayerCallbacks.push({lang: lang, segmentNumber: segmentNumber, callback:callback}); 12613 12614 if (!isLoaded) { 12615 if (!isLoading) { 12616 load(); 12617 } 12618 } else { 12619 findPlayers(); 12620 } 12621 } 12622 12623 function findPlayers() { 12624 12625 // wait until the languages are loaded 12626 if (jfmLanguages == null) { 12627 load(); 12628 return; 12629 } 12630 12631 // return all results 12632 while(getPlayerCallbacks.length > 0) { 12633 var callbackInfo = getPlayerCallbacks.pop(); 12634 12635 findPlayer(callbackInfo.lang, callbackInfo.segmentNumber, callbackInfo.callback); 12636 } 12637 } 12638 12639 function findPlayer(lang, segmentNumber, callback) { 12640 12641 12642 var languageId = jfmLanguages[lang], 12643 iframeUrl = ''; 12644 12645 if (typeof languageId == 'undefined' || languageId == null) { 12646 languageId = 529; 12647 lang = 'eng'; 12648 } 12649 12650 iframeUrl = sofia.protocol + '//api.arclight.org/videoPlayerUrl?refId=1_' + languageId + '-jf61' + segmentNumber + '-0-0&apiSessionId=' + sofia.config.jfmKey + '&playerStyle=vanilla'; 12651 12652 callback(iframeUrl, lang); 12653 } 12654 12655 var ext = { 12656 getPlayer: getPlayer 12657 12658 } 12659 12660 return ext; 12661})(); 12662 12663 12664 12665/********* 12666* ../app/js/media/medialibrary.js 12667**********/ 12668sofia.globals.mediaLargeImage = null; 12669 12670sofia.globals.mediaImageClick = function(e) { 12671 e.preventDefault(); 12672 12673 12674 if (sofia.globals.mediaLargeImage != null) { 12675 sofia.globals.mediaLargeImage.fadeOut(function() { 12676 $(this).remove(); 12677 }); 12678 } 12679 12680 var link = $(this), 12681 url = link.attr('href'), 12682 img = link.find('img'), 12683 imgWidth = img.width(), 12684 imgHeight = img.height(), 12685 imgOffset = img.offset(), 12686 win = $(window), 12687 winHeight = win.height(), 12688 winWidth = win.width(), 12689 12690 clonedImage = $('<img src="' + img.parent('a').attr('href') + '" />') 12691 12692 //clonedImage = img 12693 // .clone() 12694 .appendTo($('body')) 12695 .addClass('big-image') 12696 .css({top: imgOffset.top, left: imgOffset.left, width: imgWidth, height: imgHeight }), 12697 12698 newHeight = winHeight, 12699 newWidth = winHeight * imgWidth / imgHeight, 12700 newTop = 0, 12701 newLeft = winWidth/2 - newWidth/2; 12702 12703 // if the image is wider in ratio than the window 12704 if (newLeft < 0) { 12705 newWidth = winWidth; 12706 newHeight = winWidth * imgHeight / imgWidth; 12707 newLeft = 0; 12708 newTop = winHeight/2 - newHeight/2; 12709 } 12710 12711 clonedImage.on('load', function() { 12712 clonedImage.animate({ 12713 top: newTop, 12714 left: newLeft, 12715 width: newWidth, 12716 height: newHeight 12717 }); 12718 }); 12719 12720 sofia.globals.mediaLargeImage = clonedImage; 12721 12722 if (sofia.analytics) { 12723 sofia.analytics.record('imageclick', url); 12724 } 12725 12726 clonedImage.on('click', shrinkImage); 12727 12728 function shrinkImage() { 12729 clonedImage.animate( 12730 {top: imgOffset.top, left: imgOffset.left, width: imgWidth, height: imgHeight }, 12731 {complete: function() { clonedImage.remove();} 12732 }); 12733 12734 // remove this listener 12735 $('body').off('click', shrinkImage); 12736 } 12737 12738 // click anywhere to remove 12739 $('body').on('click', shrinkImage); 12740 12741 return false; 12742}; 12743 12744 12745// GLOBAL Video window 12746sofia.globals.videoWindow = null; 12747 12748$(function() { 12749 12750 // create window 12751 sofia.globals.videoWindow = new MovableWindow(640,360,'Video','video-player'); 12752 sofia.globals.videoWindow.center(); 12753 sofia.globals.videoWindow.hide(); 12754 12755 sofia.globals.videoWindow.container.find('.close-button').on('click', function() { 12756 12757 // attempt to hide video 12758 var video = sofia.globals.videoWindow.container.find('video'); 12759 if (video.length > 0) { 12760 video[0].pause(); 12761 } 12762 12763 sofia.globals.videoWindow.body.html(''); 12764 }); 12765 12766}); 12767 12768sofia.globals.mediaVideoClick = function(e) { 12769 e.preventDefault(); 12770 12771 var link = $(this), 12772 url = link.attr('href'), 12773 title = link.attr('title'); 12774 12775 sofia.globals.showVideo(url, title); 12776 12777 return false; 12778}; 12779 12780sofia.globals.mediaVideoJfmClick = function(e) { 12781 e.preventDefault(); 12782 12783 var link = $(this), 12784 //segmentNumber = link.attr('href'), 12785 segmentData = link.attr('data-filename'), 12786 title = link.attr('title'), 12787 12788 firstSection = $('.section:first'), 12789 12790 lang = firstSection.length > 0 ? firstSection.attr('data-lang3') : 'eng'; 12791 12792 12793 JesusFilmMediaApi.getPlayer(lang, segmentData, function(iframeUrl) { 12794 sofia.globals.showIframe(iframeUrl, title); 12795 }); 12796 12797 return false; 12798}; 12799 12800sofia.globals.showVideo = function(videoUrl, title) { 12801 12802 sofia.globals.videoWindow.body.html(''); 12803 12804 // add video
12805 var video = $('<video autoplay controls src="' + videoUrl + '" style="width:100%; height: auto;"></video>') 12806 .appendTo(sofia.globals.videoWindow.body); 12807 12808 // title? 12809 if (title) { 12810 sofia.globals.videoWindow.title.html(title); 12811 } 12812 12813 // show me! 12814 sofia.globals.videoWindow.show(); 12815} 12816 12817 12818sofia.globals.showIframe = function(iframeUrl, title) { 12819 12820 sofia.globals.videoWindow.body.html(''); 12821 12822 // add video 12823 var iframe = $('<iframe src="' + iframeUrl + '" allowfullscreen frameborder="0" width="' + sofia.globals.videoWindow.body.width() + '" height="' + sofia.globals.videoWindow.body.height() + '"></iframe>') 12824 .appendTo(sofia.globals.videoWindow.body); 12825 12826 // title? 12827 if (title) { 12828 sofia.globals.videoWindow.title.html(title); 12829 } 12830 12831 // show me! 12832 sofia.globals.videoWindow.show(); 12833} 12834 12835 12836 12837 12838 12839var MediaLibrary = (function(){ 12840 12841 12842 var 12843 currentMediaIndex = 0, 12844 mediaLibraries = null, 12845 dataIsLoaded = false; 12846 12847 function init() { 12848 12849 // load main info 12850 sofia.ajax({ 12851 dataType: 'json', 12852 url: 'content/media/media.json', 12853 success: function(data) { 12854 12855 mediaLibraries = data.media; 12856 12857 12858 }, complete: function() { 12859 loadNextMediaLibrary(); 12860 } 12861 }); 12862 12863 } 12864 12865 function loadNextMediaLibrary() { 12866 12867 //console.log('loadNextMediaLibrary', currentMediaIndex, mediaLibraries.length); 12868 12869 if (currentMediaIndex < mediaLibraries.length) { 12870 12871 var mediaLibrary = mediaLibraries[currentMediaIndex]; 12872 currentMediaIndex++; 12873 12874 sofia.ajax({ 12875 dataType: 'json', 12876 url: 'content/media/' + mediaLibrary.folder + '/info.json', 12877 success: function(data) { 12878 mediaLibrary.data = data; 12879 }, 12880 complete: function() { 12881 loadNextMediaLibrary(); 12882 } 12883 }); 12884 } else { 12885 12886 dataIsLoaded = true; 12887 sendMediaCallbacks(); 12888 } 12889 12890 } 12891 12892 12893 var getMediaListCallbacks = []; 12894 function getMediaLibraries(callback) { 12895 getMediaListCallbacks.push(callback); 12896 sendMediaCallbacks(); 12897 } 12898 function sendMediaCallbacks() { 12899 12900 if (!dataIsLoaded) { 12901 return; 12902 } 12903 12904 while (getMediaListCallbacks.length > 0) { 12905 var callback = getMediaListCallbacks.pop(); 12906 12907 callback(mediaLibraries); 12908 } 12909 } 12910 12911 var module = { 12912 getMediaLibraries: getMediaLibraries, 12913 init: init 12914 }; 12915 12916 12917 return module; 12918 12919})(); 12920 12921sofia.initMethods.push(MediaLibrary.init); 12922 12923 12924 12925/********* 12926* ../app/js/ui/textchooser.js 12927**********/ 12928sofia.config = $.extend(sofia.config, { 12929 12930 enableBibleSelectorTabs: false, 12931 bibleSelectorDefaultList: ['ENGNAS', 'ENGNIV'] 12932 12933}); 12934 12935 12936 12937/****************** 12938TextChooser 12939*******************/ 12940 12941var TextChooser = function() { 12942 // create me 12943 var 12944 // set by show() function 12945 container = null, 12946 text_type = null, 12947 target = null, 12948 12949 isFull = false, 12950 selectedTextInfo = null, 12951 textChooser = $('<div class="text-chooser nav-drop-list">' + 12952 '<span class="up-arrow"></span>' + 12953 '<span class="up-arrow-border"></span>' + 12954 '<div class="text-chooser-header">' + 12955 '<div class="text-chooser-selector">' + 12956 '<span class="text-chooser-default selected i18n" data-mode="default" data-i18n="[html]windows.bible.default"></span>' + 12957 '<span class="text-chooser-languages i18n" data-mode="languages" data-i18n="[html]windows.bible.languages"></span>' + 12958 '<span class="text-chooser-countries i18n" data-mode="countries" data-i18n="[html]windows.bible.countries"></span>' + 12959 '</div>' + 12960 '<input type="text" class="text-chooser-filter-text i18n" data-i18n="[placeholder]windows.bible.filter" />' + 12961 '<span class="close-button">Close</span>' + 12962 '</div>' + 12963 '<div class="text-chooser-main"></div>' + 12964 '</div>') 12965 .appendTo( $('body') ) 12966 .hide(), 12967 header = textChooser.find('.text-chooser-header'), 12968 main = textChooser.find('.text-chooser-main'), 12969 listselector = textChooser.find('.text-chooser-selector'), 12970 defaultSelector = textChooser.find('.text-chooser-default'), 12971 languagesSelector = textChooser.find('.text-chooser-languages'), 12972 countriesSelector = textChooser.find('.text-chooser-countries'), 12973 filter = textChooser.find('.text-chooser-filter-text'), 12974 title = textChooser.find('.text-chooser-title'), 12975 closeBtn = textChooser.find('.close-button').hide(), 12976 allTextsVisible = false, 12977 hasDefaultTexts = false, 12978 recentlyUsedKey = 'texts-recently-used', 12979 recentlyUsed = AppSettings.getValue(recentlyUsedKey, {"recent":[]} ), 12980 list_data = null; 12981 12982 //textChooser.find('.i18n').i18n(); 12983 12984 title.html("Texts"); 12985 12986 closeBtn.on('click', hide); 12987 12988 if (sofia.config.enableBibleSelectorTabs && sofia.config.bibleSelectorDefaultList && sofia.config.bibleSelectorDefaultList.length > 0) { 12989 12990 listselector.on('click', 'span', function() { 12991 $(this) 12992 .addClass('selected') 12993 .siblings() 12994 .removeClass('selected'); 12995 12996 filter 12997 .val(''); 12998 12999 if (!Detection.hasTouch) { 13000 filter.focus(); 13001 } 13002 13003 renderTexts(list_data); 13004 }); 13005 13006 13007 } else { 13008 listselector.hide(); 13009 } 13010 13011 13012 filter.on('keyup keypress', filterVersions);
13013 13014 filter.on('focus', function() { 13015 /* 13016 if (Detection.hasTouch) { 13017 filter.blur(); 13018 } 13019 */ 13020 }); 13021 13022 function filterVersions(e) { 13023 13024 // when the user presses return and there is only one version, attempt to go to that one 13025 if (e && e.which == 13) { 13026 var visibleRows = main.find('.text-chooser-row:visible, .text-chooser-row-divider:visible'); 13027 13028 if (visibleRows.length == 1) { 13029 13030 visibleRows.click(); 13031 13032 filter.val(''); 13033 return; 13034 } 13035 } 13036 13037 var text = filter.val().toLowerCase(); 13038 13039 13040 13041 if (text == '') { 13042 renderTexts(list_data); 13043 //updateRecentlyUsed(); 13044 } else { 13045 13046 // filter by type 13047 var arrayOfTexts = list_data; 13048 13049 arrayOfTexts = arrayOfTexts.filter(function(t) { 13050 13051 if (text_type == 'audio') { 13052 var hasAudio = t.hasAudio || 13053 typeof t.audioDirectory != 'undefined' || 13054 (typeof t.fcbh_audio_ot != 'undefined' || typeof t.fcbh_audio_nt != 'undefined' || 13055 typeof t.fcbh_drama_ot != 'undefined' || typeof t.fcbh_drama_nt != 'undefined'); 13056 13057 return hasAudio == true; 13058 } 13059 13060 var thisTextType = typeof t.type == 'undefined' ? 'bible' : t.type; 13061 return thisTextType == text_type; 13062 }); 13063 13064 var html = []; 13065 13066 for (var i=0, il=arrayOfTexts.length; i<il; i++) { 13067 var textInfo = arrayOfTexts[i], 13068 hasMatch = textInfo.name.toLowerCase().indexOf(text) > -1 || 13069 textInfo.abbr.toLowerCase().indexOf(text) > -1 || 13070 (textInfo.langNameEnglish && textInfo.langName.toLowerCase().indexOf(text) > -1) || 13071 (textInfo.langNameEnglish && textInfo.langNameEnglish.toLowerCase().indexOf(text) > -1); 13072 13073 13074 if (hasMatch) { 13075 html.push ( 13076 createTextRow(textInfo, false, '') 13077 ); 13078 } 13079 13080 } 13081 13082 13083 main.html('<table cellspacing="0">' + html.join('') + '</table>'); 13084 13085 } 13086 } 13087 13088 // handle when user clicks on a text 13089 textChooser.on('click', '.text-chooser-row', function() { 13090 var row = $(this), 13091 textid = row.attr('data-id'); 13092 13093 row.addClass('selected') 13094 .siblings() 13095 .removeClass('selected'); 13096 13097 13098 storeRecentlyUsed(textid); 13099 updateRecentlyUsed(); 13100 13101 hide(); 13102 13103 TextLoader.getText(textid, function(data) { 13104 13105 selectedTextInfo = data; 13106 13107 //console.log('chooser:change:click', selectedTextInfo); 13108 ext.trigger('change', {type:'change', target: this, data: {textInfo: selectedTextInfo, target: target} }); 13109 13110 }); 13111 13112 }); 13113 13114 13115 function storeRecentlyUsed(textInfo) { 13116 13117 if (text_type != 'bible') { 13118 return; 13119 } 13120 13121 var textid = (typeof textInfo == 'string') ? textInfo : textInfo.id; 13122 13123 // look for this version 13124 var existingVersions = recentlyUsed.recent.filter(function(t) { 13125 return t == textid; 13126 }); 13127 13128 if (existingVersions.length == 0) { 13129 13130 // store recent text 13131 recentlyUsed.recent.unshift(textid); 13132 13133 // limit to 5 13134 while (recentlyUsed.recent.length > 5 ) { 13135 recentlyUsed.recent.pop(); 13136 } 13137 } 13138 13139 //console.log('storeRecentlyUsed',recentlyUsed.recent.length); 13140 13141 // save 13142 AppSettings.setValue(recentlyUsedKey, recentlyUsed); 13143 } 13144 13145 function updateRecentlyUsed() { 13146 13147 if (list_data == null || text_type == null) { 13148 return; 13149 } 13150 13151 if (text_type != 'bible' || (getMode() != 'default' && getMode() != 'none')) { 13152 main.find('.text-chooser-recently-used').remove(); 13153 return; 13154 } 13155 13156 // RECENTly Used 13157 if (recentlyUsed.recent.length > 0) { 13158 13159 var isDefaultText = false; 13160 13161 // find if this should be a priority text shown at the beginning 13162 //if (sofia.config.topTexts && sofia.config.topTexts.length > 0) { 13163 // isDefaultText = true; 13164 //} 13165 13166 var recentlyUsedHtml = 13167 createHeaderRow( 13168 '', 13169 i18n.t('windows.bible.recentlyused'), 13170 '', 13171 '', 13172 'text-chooser-recently-used' + (isDefaultText ? ' is-default-text' : '') 13173 ); 13174 13175 for (var i=0, il=recentlyUsed.recent.length; i<il; i++) { 13176 var textid = recentlyUsed.recent[i], 13177 textInfo = list_data.filter(function(ti) { return ti.id == textid; })[0]; 13178 13179 if (textInfo) { 13180 recentlyUsedHtml += 13181 createTextRow(textInfo, isDefaultText, 'text-chooser-recently-used' ); 13182 } 13183 } 13184 13185 // remove existing 13186 main.find('.text-chooser-recently-used').remove(); 13187 13188 // add update recent stuff 13189 var recentRow = $(recentlyUsedHtml); 13190 main.find('table tbody').prepend(recentRow); 13191 } 13192 13193 } 13194 13195 13196 function checkIsDefaultText(id) { 13197 13198 var isDefaultText = false, 13199 parts = id.split(':'), 13200 textid = parts.length > 1 ? parts[1] : parts[0]; 13201
13202 // find if this should be a priority text shown at the beginning 13203 if (sofia.config.bibleSelectorDefaultList && sofia.config.bibleSelectorDefaultList.length > 0) { 13204 13205 for (var t=0, tl=sofia.config.bibleSelectorDefaultList.length; t<tl; t++) { 13206 if (textid == sofia.config.bibleSelectorDefaultList[t]) { 13207 isDefaultText = true; 13208 break; 13209 } 13210 } 13211 13212 } else { 13213 isDefaultText = false; 13214 } 13215 13216 13217 return isDefaultText; 13218 13219 } 13220 13221 function getMode() { 13222 if (sofia.config.enableBibleSelectorTabs) { 13223 var selectedMode = listselector.find('.selected'), 13224 mode = selectedMode.length > 0 ? selectedMode.data('mode') : 'none'; 13225 13226 return mode; 13227 } else { 13228 return 'none'; 13229 } 13230 } 13231 13232 function renderTexts(data) { 13233 13234 if (data == null || typeof data == 'undefined') { 13235 return; 13236 } 13237 13238 // render all the rows 13239 var html = [], 13240 arrayOfTexts = data, 13241 mode = getMode(); 13242 13243 if (mode == 'languages' || mode == 'default' || mode == 'none') { 13244 13245 // filter by type 13246 arrayOfTexts = arrayOfTexts.filter(function(t) { 13247 13248 if (text_type == 'audio') { 13249 var hasAudio = t.hasAudio || 13250 typeof t.audioDirectory != 'undefined' || 13251 (typeof t.fcbh_audio_ot != 'undefined' || typeof t.fcbh_audio_nt != 'undefined' || 13252 typeof t.fcbh_drama_ot != 'undefined' || typeof t.fcbh_drama_nt != 'undefined'); 13253 13254 return hasAudio === true; 13255 } else { 13256 if (t.hasText === false) { 13257 return false; 13258 } 13259 } 13260 13261 var thisTextType = typeof t.type == 'undefined' ? 'bible' : t.type; 13262 13263 return thisTextType == text_type; 13264 }); 13265 13266 // find languages 13267 var languages = []; 13268 for (var index in arrayOfTexts) { 13269 var text = arrayOfTexts[index]; 13270 13271 /* ORDER BY English Name */ 13272 var langKey = text.langNameEnglish; 13273 if (langKey == undefined || langKey == '') { 13274 langKey = text.langName; 13275 } 13276 13277 if (languages.indexOf(langKey) == -1) { 13278 languages.push( langKey ); 13279 } 13280 } 13281 13282 // PINNED 13283 var pinnedLanguages = []; 13284 if (sofia.config.pinnedLanguages && sofia.config.pinnedLanguages.length && sofia.config.pinnedLanguages.length > 0) { 13285 console.log('finding pins'); 13286 13287 for (var i=0, il = sofia.config.pinnedLanguages.length; i<il; i++) { 13288 var pinnedLanguage = sofia.config.pinnedLanguages[i]; 13289 13290 var pinnedIndex = languages.indexOf(pinnedLanguage); 13291 if (pinnedIndex > -1) { 13292 13293 if (mode == 'default' || mode == 'none') { 13294 // pull it out 13295 languages.splice(pinnedIndex, 1); 13296 } 13297 13298 // store for later 13299 pinnedLanguages.push(pinnedLanguage); 13300 } 13301 } 13302 } 13303 13304 // sort 13305 languages.sort(); 13306 13307 // put it back in 13308 if (pinnedLanguages.length > 0) { 13309 languages.splice.apply(languages, [0, 0].concat(pinnedLanguages)); 13310 } 13311 13312 13313 for (var index in languages) { 13314 13315 // get all the ones with this language 13316 var langName = languages[index], 13317 textsInLang = arrayOfTexts.filter(function(t) { return (t.langName == langName || t.langNameEnglish == langName) }), 13318 hasDefaultText = false, 13319 langHtml = []; 13320 13321 // sort the texts by name 13322 textsInLang = textsInLang.sort(function (a, b) { 13323 if (a.name == b.name) { 13324 return 0; 13325 } else if (a.name > b.name) { 13326 return 1; 13327 } else if (a.name < b.name) { 13328 return -1; 13329 } 13330 }); 13331 13332 // create HTML for the texts 13333 for (var textIndex in textsInLang) { 13334 var text = textsInLang[textIndex], 13335 isDefaultText = checkIsDefaultText(text.id); 13336 13337 if (text_type == 'bible' ) { 13338 if (mode == 'none' || mode == 'languages' || (isDefaultText && mode == 'default')) { 13339 langHtml.push( 13340 createTextRow( 13341 text, 13342 isDefaultText, 13343 mode == 'languages' ? 'collapsed' : '' 13344 ) 13345 ); 13346 } 13347 13348 if (!hasDefaultText && isDefaultText) { 13349 hasDefaultText = true; 13350 } 13351 } else if (text_type == 'deafbible' ) { 13352 langHtml.push( 13353 createImageRow( 13354 text, 13355 mode == 'languages' ? 'collapsed' : '' 13356 ) 13357 ); 13358 13359 } 13360 } 13361 13362 if (text_type == 'bible' && (mode == 'none' || mode == 'languages' || (hasDefaultText && mode == 'default')) ) { 13363 13364 var languageDisplayTitle = ''; 13365 13366 // vernacular first 13367 /* 13368 languageDisplayTitle = textsInLang[0].langName + 13369 ( textsInLang[0].langName != textsInLang[0].langNameEnglish && typeof textsInLang[0].langNameEnglish != 'undefined' ? ' (' + textsInLang[0].langNameEnglish + ')' : ''); 13370 */ 13371 13372 // english first 13373 var langName = textsInLang[0].langName, 13374 langNameEnglish = textsInLang[0].langNameEnglish; 13375 13376 if (langNameEnglish != '' && langNameEnglish != undefined) { 13377 languageDisplayTitle = langNameEnglish + (langName != langNameEnglish ? ' (' + langName + ')' : ''); 13378 } else { 13379 languageDisplayTitle = langName; 13380 } 13381 13382 html.push( 13383 createHeaderRow( 13384 '', 13385 languageDisplayTitle, 13386 '', 13387 '', 13388 mode == 'languages' ? 'collapsible-language collapsed' : '' 13389 ) 13390 ); 13391 } 13392 13393 html.push(langHtml.join(''));
13394 13395 } 13396 13397 13398 main.html('<table cellspacing="0" class="' + (mode == 'languages' ? 'collapsible' : '') + '">' + html.join('') + '</table>'); 13399 13400 13401 updateSelectedText(); 13402 13403 // do this after the 'selected' so it's not in the recently used 13404 updateRecentlyUsed(); 13405 13406 13407 } else if (mode == "countries") { 13408 13409 textChooser.removeClass('show-more'); 13410 13411 for (var i=0, il=sofia.countries.length; i<il; i++) { 13412 13413 var countryInfo = sofia.countries[i], 13414 textsInCountry = arrayOfTexts.filter(function(t) { 13415 return typeof t.countries != 'undefined' && t.countries.indexOf(countryInfo["alpha-2"]) > -1; 13416 }); 13417 13418 13419 if (textsInCountry.length > 0) { 13420 html.push( 13421 createHeaderRow(countryInfo["alpha-3"], 13422 countryInfo.name, 13423 '', 13424 '<img src="' + sofia.config.baseContentUrl + 'content/countries/' + countryInfo["alpha-2"].toLowerCase() + '.png" alt="' + countryInfo["alpha-2"] + '" />', 13425 'country collapsed') 13426 13427 ); 13428 13429 // order by languages? 13430 var languagesInCountry = []; 13431 13432 for (var index in textsInCountry) { 13433 var text = textsInCountry[index]; 13434 13435 /* ORDER BY English Name */ 13436 var langKey = text.langNameEnglish; 13437 if (langKey == undefined || langKey == '') { 13438 langKey = text.langName; 13439 } 13440 13441 if (languagesInCountry.indexOf(langKey) == -1) { 13442 languagesInCountry.push( langKey ); 13443 } 13444 } 13445 13446 languagesInCountry.sort(); 13447 13448 for (var index in languagesInCountry) { 13449 13450 // get all the ones with this language 13451 var langName = languagesInCountry[index], 13452 textsInLang = textsInCountry.filter(function(t) { return (t.langName == langName || t.langNameEnglish == langName) }), 13453 hasDefaultText = false, 13454 langHtml = []; 13455 13456 13457 // LANGUAGE 13458 var languageDisplayTitle = ''; 13459 13460 // english first 13461 var langName = textsInLang[0].langName, 13462 langNameEnglish = textsInLang[0].langNameEnglish; 13463 13464 if (langNameEnglish != '' && langNameEnglish != undefined) { 13465 languageDisplayTitle = langNameEnglish + (langName != langNameEnglish ? ' (' + langName + ')' : ''); 13466 } else { 13467 languageDisplayTitle = langName; 13468 } 13469 13470 html.push( 13471 createDividerRow( 13472 languageDisplayTitle, 13473 'collapsed' 13474 ) 13475 ); 13476 13477 13478 // sort the texts by name 13479 textsInLang = textsInLang.sort(function (a, b) { 13480 if (a.name == b.name) { 13481 return 0; 13482 } else if (a.name > b.name) { 13483 return 1; 13484 } else if (a.name < b.name) { 13485 return -1; 13486 } 13487 }); 13488 13489 13490 // create HTML for the texts 13491 for (var textIndex in textsInLang) { 13492 var text = textsInLang[textIndex]; 13493 13494 langHtml.push( 13495 createTextRow( 13496 text, 13497 false, 13498 'collapsed' 13499 ) 13500 ); 13501 13502 if (!hasDefaultText && isDefaultText) { 13503 hasDefaultText = true; 13504 } 13505 } 13506 13507 13508 13509 13510 13511 html.push(langHtml.join('')); 13512 13513 } 13514 13515 13516 13517 /* simple list in country */ 13518 /* 13519 for (var textIndex in textsInCountry) { 13520 var text = textsInCountry[textIndex]; 13521 13522 html.push( 13523 createTextRow(text, isDefaultText, 'collapsed') 13524 ); 13525 13526 } 13527 */ 13528 13529 } 13530 13531 } 13532 13533 main.html('<table cellspacing="0" class="collapsible">' + html.join('') + '</table>'); 13534 13535 } 13536 13537 } 13538 13539 main.on('click', '.collapsible .text-chooser-row-header', function() { 13540 13541 var header = $(this), 13542 children = header.nextUntil('.text-chooser-row-header'); 13543 13544 if (header.hasClass('collapsed')) { 13545 13546 header.removeClass('collapsed'); 13547 children.removeClass('collapsed'); 13548 13549 } else { 13550 13551 header.addClass('collapsed'); 13552 children.addClass('collapsed'); 13553 13554 } 13555 13556 13557 }); 13558 13559 function updateSelectedText() { 13560 13561 // find the selected text 13562 if (selectedTextInfo != null) { 13563 13564 textChooser 13565 .find('table .selected') 13566 .removeClass('selected'); 13567 13568 textChooser 13569 .find('[data-id="' + selectedTextInfo.id + '"]') 13570 .addClass('selected'); 13571 } 13572 13573 } 13574 13575 13576 function createTextRow(text, isDefaultText, className) { 13577 var hasAudio = text.hasAudio || 13578 typeof text.audioDirectory != 'undefined' || 13579 (typeof text.fcbh_audio_ot != 'undefined' || typeof text.fcbh_audio_nt != 'undefined' || 13580 typeof text.fcbh_drama_ot != 'undefined' || typeof text.fcbh_drama_nt != 'undefined'), 13581 hasLemma = text.hasLemma, 13582 13583 providerName = (typeof text.providerName != 'undefined' && text.providerName != 'local') ? text.providerName : '', 13584 providerFullName = sofia.textproviders[text.providerName] && sofia.textproviders[text.providerName].fullName ? sofia.textproviders[text.providerName].fullName : '', 13585 13586 colspan = 4 - (hasAudio ? 1 : 0) - (hasLemma ? 1 : 0) - (providerName != '' ? 1 : 0); 13587 13588 var html = '<tr class="text-chooser-row' + (isDefaultText ? ' is-default-text' : '') + (className != '' ? ' ' + className : '') + '" data-id="' + text.id + '" data-lang-name="' + text.langName + '" data-lang-name-english="' + text.langNameEnglish + '">' + 13589 '<td class="text-chooser-abbr">' + text.abbr + '</td>' + 13590 '<td class="text-chooser-name" ' + (colspan > 1 ? ' colspan="' + colspan + '"' : '') + '>
13590' + 13591 '<span>' + text.name + '</span>' + 13592 '</td>' + 13593 13594 (hasLemma === true ? '<td class="text-chooser-lemma"><span title="' + i18n.t('windows.bible.lemma') + '" data-i18n="[title]windows.bible.lemma"></span></td>' : '') + 13595 (hasAudio === true ? '<td class="text-chooser-audio"><span title="' + i18n.t('windows.bible.audio') + '" data-i18n="[title]windows.bible.audio"></span></td>' : '') + 13596 (providerName != '' ? '<td class="text-chooser-provider-' + providerName + '"><span title="' + providerFullName + '"></span></td>' : '') + 13597 '</tr>'; 13598 13599 return html; 13600 } 13601 13602 function createImageRow(text, className) { 13603 var 13604 providerName = (typeof text.providerName != 'undefined' && text.providerName != 'local') ? text.providerName : '', 13605 providerFullName = sofia.textproviders[text.providerName] && sofia.textproviders[text.providerName].fullName ? sofia.textproviders[text.providerName].fullName : '', 13606 imageUrl = 'content/texts/' + text.id + '/' + text.id + '.png'; 13607 13608 13609 var html = '<tr class="text-chooser-row' + (className != '' ? ' ' + className : '') + '" data-id="' + text.id + '" data-lang-name="' + text.langName + '" data-lang-name-english="' + text.langNameEnglish + '">' + 13610 '<td class="text-chooser-image">' + 13611 '<img src="' + imageUrl + '" />' + 13612 '</td>' + 13613 '<td class="text-chooser-name" >' + 13614 '<span>' + text.name + '</span>' + 13615 '</td>' + 13616 '</tr>'; 13617 13618 return html; 13619 } 13620 13621 13622 13623 function createHeaderRow(id, name, englishName, additionalHtml, className) { 13624 var html = '<tr class="text-chooser-row-header' + (className != '' ? ' ' + className : '') + '" data-id="' + id + '"><td colspan="5">' + 13625 '<span class="name">' + name + '</span>' + 13626 additionalHtml + 13627 '</td></tr>'; 13628 13629 13630 return html; 13631 } 13632 13633 function createDividerRow(name, className) { 13634 var html = '<tr class="text-chooser-row-divider ' + (className != '' ? ' ' + className : '') + '">' + 13635 //'<td> </td>' + 13636 //'<td colspan="4">' + 13637 '<td colspan="5">' + 13638 '<span class="name">' + name + '</span>' + 13639 '</td>' + 13640 '</tr>'; 13641 13642 13643 return html; 13644 } 13645 13646 function toggle() { 13647 13648 if (textChooser.is(':visible') ) { 13649 hide(); 13650 } else { 13651 show(); 13652 } 13653 13654 } 13655 13656 function setTarget(_container, _target, _text_type) { 13657 13658 var needsToRerender = _text_type != text_type;; 13659 13660 container = _container; 13661 target = _target; 13662 text_type = _text_type; 13663 13664 ext.setClickTargets([_target, textChooser]); 13665 13666 if (needsToRerender) { 13667 renderTexts(list_data); 13668 13669 if (text_type == 'bible' && sofia.config.enableBibleSelectorTabs && sofia.config.bibleSelectorDefaultList && sofia.config.bibleSelectorDefaultList.length > 0) { 13670 listselector 13671 .find('.text-chooser-default') 13672 .addClass('selected') 13673 .siblings() 13674 .removeClass('selected'); 13675 13676 listselector.show(); 13677 } else { 13678 13679 listselector 13680 .find('span') 13681 .removeClass('selected'); 13682 13683 listselector.hide(); 13684 } 13685 } 13686 13687 } 13688 13689 function getTarget() { 13690 return target; 13691 } 13692 13693 function show() { 13694 //$('.nav-drop-list').hide(); 13695 13696 //console.log('textchooser.show'); 13697 13698 size(); 13699 textChooser.show(); 13700 ext.onshow(); 13701 13702 13703 if (!list_data) { 13704 main.addClass('loading-indicator');//.html('Loading'); 13705 13706 TextLoader.loadTexts(function(data) { 13707 list_data = data; 13708
13709 // check for countries 13710 if (sofia.config.enableCountrySelector) { 13711 var hasCountries = list_data.filter(function(c) { return typeof c.countries != 'undefined' && c.countries.length > 0; }).length > 0; 13712 if (!hasCountries) { 13713 listselector.hide(); 13714 } 13715 } 13716 13717 //console.log('TextChoose.show(), load render'); 13718 main.removeClass('loading-indicator'); 13719 renderTexts(list_data); 13720 13721 }); 13722 } else { 13723 main.removeClass('loading-indicator'); 13724 } 13725 13726 size(); 13727 13728 if (filter.val() != '') { 13729 filter.val(''); 13730 filterVersions(); 13731 } 13732 13733 if (!Detection.hasTouch) { 13734 filter.focus(); 13735 } 13736 13737 } 13738 13739 function hide() { 13740 textChooser.hide(); 13741 ext.onhide(); 13742 } 13743 13744 function setTextInfo(text) { 13745 selectedTextInfo = text; 13746 13747 storeRecentlyUsed(selectedTextInfo); 13748 updateRecentlyUsed(); 13749 13750 updateSelectedText(); 13751 } 13752 13753 function getTextInfo() { 13754 return selectedTextInfo; 13755 } 13756 13757 function size(width,height) { 13758 13759 if (target == null || container == null) { 13760 return; 13761 } 13762 13763 //clearOffClickTimer(); 13764 13765 if (isFull) { 13766 13767 // cover the container area 13768 if (!(width && height)) { 13769 width = container.width(); 13770 height = container.height(); 13771 } 13772 13773 textChooser 13774 .width(width) 13775 .height(height) 13776 .css({top: container.offset().top,left: container.offset().left}); 13777 13778 main 13779 .width(width) 13780 .height(height - header.outerHeight()); 13781 13782 } else { 13783 13784 // reasonable size! 13785 var targetOffset = target.offset(), 13786 targetOuterHeight = target.outerHeight(), 13787 win = $(window), 13788 selectorWidth = textChooser.outerWidth(), 13789 13790 top = targetOffset.top + targetOuterHeight + 10, 13791 left = targetOffset.left, 13792 winHeight = win.height() - 40, 13793 winWidth = win.width(), 13794 maxHeight = winHeight - top; 13795 13796 if (winWidth < left + selectorWidth) { 13797 left = winWidth - selectorWidth; 13798 if (left < 0) { 13799 left = 0; 13800 } 13801 } 13802 13803 13804 textChooser 13805 .outerHeight(maxHeight) 13806 .css({top: top,left: left}); 13807 13808 main 13809 .outerHeight(maxHeight - header.outerHeight()); 13810 13811 13812 // UP ARROW 13813 var upArrowLeft = targetOffset.left - left + 20; 13814 13815 textChooser.find('.up-arrow, .up-arrow-border') 13816 .css({left: upArrowLeft}); 13817 13818 } 13819 13820 } 13821 13822 function isVisible() { 13823 return textChooser.is(':visible'); 13824 } 13825 13826 function node() { 13827 return textChooser; 13828 } 13829 13830 function close() { 13831 //textChooser.remove(); 13832 //ext.clearListeners(); 13833 } 13834 13835 var ext = { 13836 setTarget: setTarget, 13837 getTarget: getTarget, 13838 show: show, 13839 hide: hide, 13840 toggle: toggle, 13841 isVisible: isVisible, 13842 node: node, 13843 getTextInfo: getTextInfo, 13844 setTextInfo: setTextInfo, 13845 renderTexts: renderTexts, 13846 size: size, 13847 close: close 13848 }; 13849 ext = $.extend(true, ext, EventEmitter); 13850 ext = $.extend(true, ext, ClickOff); 13851 ext.clickoffid = 'version picker'; 13852 ext.on('offclick', function() { 13853 hide(); 13854 }); 13855 13856 return ext; 13857 13858}; 13859 13860sofia.initMethods.push(function() { 13861 sofia.globalTextChooser = new TextChooser(); 13862}); 13863 13864 13865 13866/********* 13867* ../app/js/ui/textnavigator.js 13868**********/ 13869/****************** 13870Text Navigator 13871*******************/ 13872 13873var TextNavigator = function() { 13874 // create me 13875 13876 var 13877 container = null, 13878 target = null, 13879 isFull = false, 13880 changer = $('<div class="text-navigator nav-drop-list">' + 13881 '<span class="up-arrow"></span>' + 13882 '<span class="up-arrow-border"></span>' + 13883 '<div class="text-navigator-header">' + 13884 '<span class="text-navigator-title"> </span>' + 13885 '<span class="text-navigator-back">Back</span>' + 13886 '<span class="text-navigator-close">Close</span>' + 13887 '</div>' + 13888 '<div class="text-navigator-divisions"></div>' + 13889 //'<div class="text-navigator-sections"></div>' + 13890 '</div>' 13891 ) 13892 //.css({height: $(window).height(), width: $(window).width()}) 13893 .appendTo( $('body') ) 13894 .hide(), 13895 header = changer.find('.text-navigator-header'), 13896 title = changer.find('.text-navigator-title'), 13897 back = changer.find('.text-navigator-back').hide(), 13898 closeBtn = changer.find('.text-navigator-close').hide(), 13899 fullname = $('<div class="text-navigator-fullname"></div>').appendTo( $('body') ).hide(), 13900 textInfo = null, 13901 13902 fullBookMode = false; 13903 13904 if (!Detection.hasTouch) { 13905 changer.on('mouseover', '.text-navigator-division', function() { 13906 13907 if (!fullBookMode) { 13908 13909 var node = $(this), 13910 name = node.attr('data-name'); 13911 13912 fullname 13913 .html(name) 13914 .css({ 13915 backgroundColor: node.css('backgroundColor'), 13916 top: (parseInt(node.offset().top, 10)-1) + 'px', 13917 left: node.offset().left, 13918 }) 13919 .show(); 13920 13921 fullname.lastNode = node; 13922 } 13923 13924 }) 13925 .on('mouseout', '.text-navigator-division', function() { 13926 fullname.hide(); 13927 }); 13928 } 13929 13930 // pointer-events: none for modern browsers; 13931 fullname.on('click', function() { 13932 if (fullname.lastNode) { 13933 fullname.lastNode.trigger('click'); 13934 fullname.hide(); 13935 } 13936 13937 }); 13938 13939 closeBtn.on('click', function() { 13940 hide(); 13941 }); 13942 13943 function hide() { 13944 changer.hide(); 13945 fullname.hide(); 13946 ext.onhide(); 13947 } 13948
13949 function toggle() { 13950 13951 if (changer.is(':visible')) { 13952 hide(); 13953 } else { 13954 show(); 13955 } 13956 13957 } 13958 13959 function show() { 13960 13961 //$('.nav-drop-list').hide(); 13962 13963 if (textInfo == null) { 13964 console.warn('chooser has not textInfo!'); 13965 return; 13966 } 13967 13968 title.html( textInfo.name ); 13969 13970 // reset width 13971 size(); 13972 changer.show(); 13973 ext.onshow(); 13974 size(); 13975 13976 // remove all selections 13977 changer.find('.selected').removeClass('selected'); 13978 changer.find('.text-navigator-divisions').scrollTop(0); 13979 13980 switch (textInfo.type.toLowerCase()) { 13981 case 'bible': 13982 case 'deafbible': 13983 case 'videobible': 13984 case 'commentary': 13985 13986 var textInputValue = target.val(), 13987 biblereference = new bible.Reference(textInputValue), 13988 fragmentid = (biblereference) ? biblereference.toSection() : null; 13989 13990 renderDivisions(); 13991 changer.find('.text-navigator-divisions').show().attr('dir', textInfo.dir).attr('lang', textInfo.lang); 13992 //changer.find('.text-navigator-sections').hide(); 13993 13994 if (fragmentid) { 13995 var parts = fragmentid.split('_'), 13996 sectionid = parts[0], 13997 divisionid = sectionid.substring(0,2), 13998 chapter = sectionid.substring(2); 13999 14000 var divisionNode = changer.find('.divisionid-' + divisionid).addClass('selected'); 14001 // scroll to it 14002 14003 if (divisionNode.length > 0) { 14004 var offset = divisionNode.position(); 14005 14006 changer.find('.text-navigator-divisions').scrollTop(offset.top-40); 14007 14008 renderSections(false); 14009 14010 divisionNode.find('.section-' + sectionid).addClass('selected'); 14011 } 14012 14013 14014 14015 } 14016 14017 break; 14018 case 'book': 14019 renderSections(); 14020 changer.find('.text-navigator-divisions').hide(); 14021 changer.find('.text-navigator-sections').show().attr('dir', textInfo.dir).attr('lang', textInfo.lang); 14022 break; 14023 14024 } 14025 } 14026 14027 function getBookSectionClass(bookid) { 14028 return bible.BOOK_DATA[bookid].section; 14029 } 14030 14031 // divisions = Bible books 14032 function renderDivisions() { 14033 //console.log('renderDivisions', textInfo); 14034 14035 // render books 14036 var html = []; 14037 14038 var hasPrintedOt = false, 14039 hasPrintedNt = false, 14040 hasPrintedAp = false; 14041 14042 fullBookMode = !(textInfo.divisionAbbreviations || textInfo.lang == 'eng') ; 14043 fullBookMode = true; 14044 14045 //console.log(fullBookMode, textInfo.divisionAbbreviations , textInfo.divabbr); 14046 14047 if (fullBookMode) { 14048 changer.find('.text-navigator-divisions').addClass('text-navigator-divisions-full'); 14049 } else { 14050 changer.find('.text-navigator-divisions').removeClass('text-navigator-divisions-full'); 14051 } 14052 14053 14054 for (var i=0, il= textInfo.divisions.length ; i<il; i++) { 14055 14056 var divisionid = textInfo.divisions[i], 14057 divisionName = (textInfo.divisionNames) ? 14058 textInfo.divisionNames[i] : null, 14059 divisionAbbr = (textInfo.divisionAbbreviations) ? 14060 textInfo.divisionAbbreviations[i] : null, 14061 displayName = fullBookMode ? divisionName : 14062 divisionAbbr != null ? 14063 divisionAbbr.replace(/\s/i,'').substring(0,3) : 14064 divisionName.replace(/\s/i,'').substring(0,3), 14065 book = bible.BOOK_DATA[divisionid]; 14066 14067 if (typeof book == 'undefined') { 14068 continue; 14069 } 14070 14071 if (bible.OT_BOOKS.indexOf(divisionid) > -1 && !hasPrintedOt) { 14072 html.push('<div class="text-navigator-division-header">' + i18n.t('windows.bible.ot') + '</div>'); 14073 hasPrintedOt = true; 14074 } 14075 if (bible.NT_BOOKS.indexOf(divisionid) > -1 && !hasPrintedNt) { 14076 html.push('<div class="text-navigator-division-header">' + i18n.t('windows.bible.nt') + '</div>'); 14077 hasPrintedNt = true; 14078 } 14079 /* 14080 if (bible.AP_BOOKS.indexOf(divisionid) > -1 && !hasPrintedAp) { 14081 html.push('<div class="text-navigator-division-header">' + i18n.t('windows.bible.dc') + '</div>'); 14082 hasPrintedAp = true; 14083 } 14084 */ 14085 14086 14087 /* 14088 var num_of_chapters = 0; 14089 for (var j=0, jl=textInfo.sections.length; j<jl; j++) { 14090 if (textInfo.sections[j].substring(0,2) == divisionid) { 14091 num_of_chapters++; 14092 } 14093 } 14094 html.push('<div class="text-navigator-division divisionid-' + divisionid + ' division-section-' + getBookSectionClass(divisionid) + '" data-id="' + divisionid + '" data-chapters="' + num_of_chapters + '" data-name="' + divisionName + '"><span>
14094' + displayName + '</span></div>'); 14095 */ 14096 14097 var chapters = textInfo.sections.filter(function(c) { 14098 return c.substring(0,2) == divisionid; 14099 }); 14100 14101 html.push('<div class="text-navigator-division divisionid-' + divisionid + ' division-section-' + getBookSectionClass(divisionid) + '" data-id="' + divisionid + '" data-chapters="' + chapters.join(',') + '" data-name="' + divisionName + '"><span>' + displayName + '</span></div>'); 14102 } // 14103 14104 changer.find('.text-navigator-divisions').html(html).show(); 14105 changer.find('.text-navigator-sections').remove(); 14106 14107 } 14108 14109 14110 // click a division (Bible book) 14111 changer.on('click', '.text-navigator-division', function() { 14112 14113 var divisionNode = $(this); 14114 14115 if (divisionNode.hasClass('selected')) { 14116 divisionNode.find('.text-navigator-sections').slideUp({complete: function() { 14117 divisionNode.removeClass('selected'); 14118 }}); 14119 14120 return; 14121 } 14122 14123 divisionNode 14124 .addClass('selected') 14125 .siblings() 14126 .removeClass('selected'); 14127 14128 fullname.hide(); 14129 14130 // hide previous book's chapter 14131 var divisions = changer.find('.text-navigator-divisions'), 14132 positionBefore = divisionNode.position(), 14133 scrollTopBefore = divisions.scrollTop(); 14134 14135 // close the existing set of sections 14136 changer.find('.text-navigator-sections').remove(); 14137 14138 var positionAfter = divisionNode.position(); 14139 14140 if (positionBefore.top > positionAfter.top) { 14141 var newScrollTop = scrollTopBefore - (positionBefore.top - positionAfter.top);// + header.outerHeight(true); 14142 14143 divisions.scrollTop(newScrollTop); 14144 } 14145 14146 renderSections(true); 14147 }); 14148 14149 14150 // sections = bible chapters
14151 function renderSections(animated) { 14152 14153 console.log('renderSections', textInfo.type); 14154 14155 var html = []; 14156 14157 switch (textInfo.type.toLowerCase()) { 14158 case 'bible': 14159 case 'deafbible': 14160 case 'videobible': 14161 14162 case 'commentary': 14163 // print out chapters 14164 var selected_division = changer.find('.text-navigator-division.selected'), 14165 isLast = selected_division.next().length == 0, 14166 divisionid = selected_division.attr('data-id'), 14167 divisionname = selected_division.attr('data-name'), 14168 //num_of_chapters = parseInt(selected_division.attr('data-chapters'), 10), 14169 chapters = selected_division.attr('data-chapters').split(','), 14170 numbers = typeof textInfo.numbers != 'undefined' ? textInfo.numbers : bible.numbers.default; 14171 14172 title.html( divisionname ); 14173 14174 console.log('chapters',chapters); 14175 14176 for (var chapter=0; chapter<chapters.length; chapter++) { 14177 var dbsChapterCode = chapters[chapter], 14178 chapterNumber = parseInt(dbsChapterCode.substring(2)); 14179 14180 html.push('<span class="text-navigator-section section-' + dbsChapterCode + '" data-id="' + dbsChapterCode + '">' + numbers[chapterNumber].toString() + '</span>'); 14181 } 14182 14183 var sectionNodes = $('<div class="text-navigator-sections" style="display:none;">' + html.join('') + '</div>'); 14184 14185 selected_division.find('span').after( sectionNodes ); 14186 14187 14188 if (animated === true && !isLast) { 14189 sectionNodes.slideDown(); 14190 } else { 14191 sectionNodes.show(); 14192 14193 if (isLast) { 14194 var divisions = changer.find('.text-navigator-divisions'); 14195 divisions.scrollTop( divisions.scrollTop() + 500 ); 14196 } 14197 } 14198 14199 break; 14200 case 'book': 14201 14202 //console.log('text', textInfo.sections); 14203 14204 // flat list of sections (i.e. pages) 14205 for (var i=0, il= textInfo.sections.length ; i<il; i++) { 14206 var sectionid = textInfo.sections[i]; 14207 html.push('<span class="text-navigator-section" data-id="' + sectionid + '">' + sectionid.replace('P','') + '</span>'); 14208 } 14209 14210 14211 break; 14212 } 14213 14214/* 14215 changer.find('.text-navigator-divisions').hide(); 14216 changer.find('.text-navigator-sections') 14217 .html(html) 14218 .attr('dir', textInfo.dir) 14219 .attr('lang', textInfo.lang) 14220 .show(); 14221*/ 14222 } 14223 14224 14225 14226 // click a chapter 14227 changer.on('click', '.text-navigator-section', function() { 14228 var sectionid = $(this) 14229 .addClass('selected') 14230 .attr('data-id'); 14231 14232 console.log('navigator selected', sectionid); 14233 ext.trigger('change', {type:'change', target: this, data: {sectionid: sectionid, target: target}}); 14234 14235 //navigation_changed_callback(sectionid); 14236 14237 changer.hide(); 14238 }); 14239 14240 function size(width,height) { 14241 14242 if (isFull) { 14243 14244 if (!(width && height)) { 14245 width = container.width(); 14246 height = container.height(); 14247 } 14248 14249 changer 14250 .width(width) 14251 .height(height) 14252 .css({top: container.offset().top,left: container.offset().left}); 14253 } else { 14254 14255 if (target == null) { 14256 return; 14257 } 14258 14259 var 14260 targetOffset = target.offset(), 14261 targetOuterHeight = target.outerHeight(), 14262 top = targetOffset.top + targetOuterHeight + 10, 14263 left = targetOffset.left, 14264 win = $(window), 14265 changerWidth = changer.outerWidth(), 14266 winHeight = win.height() - 40, 14267 winWidth = win.width(), 14268 maxHeight = winHeight - top; 14269 14270 if (winWidth < left + changerWidth) { 14271 left = winWidth - changerWidth; 14272 if (left < 0) { 14273 left = 0; 14274 } 14275 } 14276 14277 changer 14278 .outerHeight(maxHeight) 14279 .css({top: top, left: left}); 14280 14281 var upArrowLeft = targetOffset.left - left + 20; 14282 14283 changer.find('.up-arrow, .up-arrow-border') 14284 .css({left: upArrowLeft}); 14285 14286 changer.find('.text-navigator-divisions, .text-navigator-sections') 14287 .outerHeight(maxHeight - header.outerHeight()); 14288 14289 } 14290 } 14291 14292 function setTextInfo(value) { 14293 textInfo = value; 14294 14295 if (textInfo.title) { 14296 changer.find('.text-navigator-header').html( textInfo.title ); 14297 } 14298 14299 // set names 14300 if (textInfo.divisionNames) { 14301 bible.addNames(textInfo.lang, textInfo.divisions, textInfo.divisionNames); 14302 } 14303 } 14304 14305 function isVisible() { 14306 return changer.is(':visible'); 14307 } 14308 14309 function node() { 14310 return changer; 14311 } 14312 14313 function close() { 14314 //ext.clearListeners(); 14315 14316 //changer.remove(); 14317 } 14318 14319 function setTarget(_container, _target) { 14320 14321 container = _container; 14322 target = _target; 14323 14324 ext.setClickTargets([_target, changer]); 14325 } 14326 14327 function getTarget() { 14328 return target; 14329 } 14330
14331 // this is the return object! 14332 var ext = { 14333 setTarget: setTarget, 14334 getTarget: getTarget, 14335 show: show, 14336 toggle: toggle, 14337 hide: hide, 14338 isVisible: isVisible, 14339 node: node, 14340 setTextInfo: setTextInfo, 14341 size: size, 14342 close:close 14343 } 14344 14345 ext = $.extend(true, ext, EventEmitter); 14346 ext = $.extend(true, ext, ClickOff); 14347 ext.clickoffid = 'book/chapter picker'; 14348 ext.on('offclick', function() { 14349 hide(); 14350 }); 14351 14352 return ext; 14353 14354}; 14355 14356sofia.initMethods.push(function() { 14357 sofia.globalTextNavigator = new TextNavigator(); 14358}); 14359 14360 14361 14362/********* 14363* ../app/js/ui/infowindow.js 14364**********/ 14365var InfoWindow = function(id) { 14366 var container = $( '<div class="info-window"' + (id ? ' id="' + id + '"' : '') + '>'+ 14367 '<span class="close-button"></span>' + 14368 '<div class="info-body"></div>' + 14369 '</div>') 14370 .appendTo( $(document.body) ) 14371 .hide(), 14372 14373 body = container.find('.info-body'), 14374 close = container.find('.close-button'), 14375 win = $(window); 14376 14377 close 14378 .on('click', hide); 14379 14380 14381 function show() { 14382 container.show(); 14383 14384 ext.onshow(); 14385 ext.trigger('show'); 14386 14387 return ext; 14388 } 14389 function hide() { 14390 container.hide(); 14391 14392 ext.onhide(); 14393 14394 ext.trigger('hide'); 14395 14396 return ext; 14397 } 14398 14399 function center() { 14400 var 14401 infoWidth = container.outerWidth(), 14402 infoHeight = container.outerHeight(); 14403 14404 container.css({ 14405 top: win.height()/2 - infoHeight/2, 14406 left: win.width()/2 - infoWidth/2 14407 }); 14408 14409 return ext; 14410 } 14411 14412 function position(target) { 14413 var tOffset = target.offset(), 14414 tHeight = target.outerHeight(), 14415 left = tOffset.left - 20, 14416 top = tOffset.top + tHeight, 14417 infoWidth = container.outerWidth(), 14418 infoHeight = container.outerHeight(); 14419 14420 14421 // far left edge 14422 if (left < 0) { 14423 left = 0; 14424 // far right 14425 } else if (left + infoWidth > win.width()) { 14426 left = win.width() - infoWidth; 14427 } 14428 14429 if (top + infoHeight > win.height()) { 14430 top = tOffset.top - infoHeight; 14431 } 14432 14433 container.css({ 14434 top: top, 14435 left: left 14436 }); 14437 return ext; 14438 } 14439 14440 var ext = { 14441 show: show, 14442 hide: hide, 14443 container: container, 14444 body: body, 14445 position: position, 14446 center: center 14447 }; 14448 14449 ext = $.extend(true, ext, EventEmitter); 14450 ext = $.extend(true, ext, ClickOff); 14451 ext.setClickTargets([container]); 14452 ext.clickoffid = id; 14453 ext.on('offclick', function() { 14454 hide(); 14455 }); 14456 14457 14458 14459 14460 return ext; 14461 14462}; 14463 14464 14465 14466/********* 14467* ../app/js/ui/movablewindow.js 14468**********/ 14469var MovableWindow = function(width,height,titleText,id) { 14470 14471 width = width || 300; 14472 height = height || 200; 14473 titleText = titleText || ''; 14474 14475 var container = $( '<div class="movable-window" ' + (id ? ' id="' + id + '"' : '') + '>' + 14476 '<div class="movable-header">' + 14477 '<span class="movable-header-title">' + titleText + '</span>' + 14478 '<span class="close-button"></span>' + 14479 '</div>' + 14480 '<div class="movable-body"></div>' + 14481 '</div>') 14482 .appendTo( $(document.body) ) 14483 .hide(), 14484 header = container.find('.movable-header'), 14485 title = container.find('.movable-header-title'), 14486 body = container.find('.movable-body'), 14487 close = container.find('.close-button'), 14488 win = $(window), 14489 doc = $(document), 14490 14491 startWindowPosition = null, 14492 startMousePosition = null; 14493 14494 header.on('mousedown', function(e) { 14495 doc 14496 .on('mousemove', move) 14497 .on('mouseup', mouseup); 14498 14499 startWindowPosition = container.offset(); 14500 startMousePosition = {x:e.clientX, y:e.clientY}; 14501 }); 14502 14503 function mouseup() { 14504 doc 14505 .off('mousemove', move) 14506 .off('mouseup', mouseup); 14507 } 14508 14509 function move(e) { 14510 // handle move 14511 //console.log(e); 14512 14513 container.css({ 14514 top: startWindowPosition.top - (startMousePosition.y - e.clientY), 14515 left: startWindowPosition.left - (startMousePosition.x - e.clientX) 14516 }); 14517 } 14518 14519 close.on('click', hide); 14520 14521 14522 function size(width, height) { 14523 14524 body.width(width); 14525 body.height(height); 14526 14527 return ext; 14528 } 14529 size(width, height); 14530 center(); 14531 14532 function show() { 14533 container.show(); 14534 return ext; 14535 } 14536 function hide() { 14537 container.hide(); 14538 return ext; 14539 } 14540 function center() { 14541 var 14542 infoWidth = container.outerWidth(), 14543 infoHeight = container.outerHeight(), 14544 top = win.height()/2 - infoHeight/2, 14545 left = win.width()/2 - infoWidth/2; 14546 14547 if (top < 0) { 14548 top = 0; 14549 } 14550 if (left < 0) { 14551 left = 0; 14552 } 14553 14554 container.css({ 14555 top: top, 14556 left: left 14557 }); 14558 14559 return ext; 14560 } 14561 14562 var ext = { 14563 show: show, 14564 hide: hide, 14565 size: size, 14566 container: container, 14567 body: body, 14568 title: title, 14569 center: center, 14570 closeButton: close 14571 }; 14572 14573 return ext; 14574 14575}; 14576 14577 14578 14579/********* 14580* ../app/js/plugins/lemmainfo.js 14581**********/ 14582sofia.config = $.extend(sofia.config, { 14583 14584 enableLemmaInfoPlugin: true 14585 14586}); 14587 14588var LemmaInfoPlugin = function(app) { 14589 14590 if (!sofia.config.enableLemmaInfoPlugin) { 14591 return; 14592 } 14593 14594 var lemmaInfo = $('<div class="lemma-info"></div>
14594').appendTo( $(document.body) ).hide(); 14595 14596 if (!Detection.hasTouch) { 14597 14598 $('.windows-main').on('mouseover','.BibleWindow l', function(e) { 14599 14600 var l = $(this), 14601 morph = l.attr('m'), 14602 strongs = l.attr('s'), 14603 main = l.closest('.scroller-main'), 14604 mainOffset = main.offset(), 14605 section = l.closest('.section'), 14606 lang = section.attr('lang'), 14607 morphologyType = lang == 'heb' || lang == 'he' ? 'Hebrew' : lang == 'el' || lang == 'grc' || lang == 'gre' ? 'Greek' : '' 14608 morphInfo = (typeof morph == 'undefined' || morphologyType == '') ? '' : bible.morphology[morphologyType].format(morph); 14609 14610 if (morphInfo != null && morphInfo != '') { 14611 lemmaInfo.html( morphInfo ); 14612 14613 lemmaInfo.show(); 14614 14615 lemmaInfo.css({ 14616 left: mainOffset.left + 15 + 'px', 14617 top: (mainOffset.top + main.outerHeight() - lemmaInfo.outerHeight() - 10) + 'px' 14618 }); 14619 } 14620 14621 14622 }).on('mouseout','l', function(e) { 14623 lemmaInfo.hide(); 14624 }); 14625 } 14626}; 14627 14628sofia.plugins.push('LemmaInfoPlugin'); 14629 14630 14631 14632/********* 14633* ../app/js/plugins/lemmamatch.js 14634**********/ 14635sofia.config = $.extend(sofia.config, { 14636 14637 enableLemmaMatchPlugin: true 14638 14639}); 14640 14641var LemmaMatchPlugin = function(app) { 14642 14643 if (!sofia.config.enableLemmaMatchPlugin) { 14644 return; 14645 } 14646 14647 if (!Detection.hasTouch) { 14648 14649 $('.windows-main').on('mouseover','l', function(e) { 14650 14651 //console.log('over'); 14652 14653 var l = $(this), 14654 s = l.attr('s'); 14655 14656 if (!s) { 14657 return; 14658 } 14659 14660 var 14661 strongs = l.attr('s').replace('G','').replace('H',''), 14662 verse = l.closest('.verse, .v'), 14663 verseid = verse.attr('data-id'), 14664 bookid = (verseid) ? verseid.substring(0,2) : ''; 14665 langPrefix = (bookid != '' && bible.OT_BOOKS.indexOf(bookid) > -1) ? 'H' : 'G'; 14666 14667 14668 var strongParts = strongs.split(' '); 14669 14670 for (var i=0, il=strongParts.length; i<il; i++) { 14671 var strong = strongParts[i]; 14672 14673 14674 // ignore Greek article with second word 14675 if (strong == '3588' && il > 1) { 14676 continue; 14677 } 14678 14679 14680 if (verseid) { 14681 $('.' + verseid ).find('l[s*="' + strong + '"],l[s*="' + langPrefix + strong + '"]').addClass('lemma-highlight'); 14682 } else { 14683 $('l[s*="' + strong + '"],l[s*="' + langPrefix + strong + '"]').addClass('lemma-highlight'); 14684 14685 } 14686 14687 } 14688 14689 14690 14691 }).on('mouseout','l', function(e) { 14692 $('.lemma-highlight').removeClass('lemma-highlight'); 14693 }); 14694 } 14695}; 14696 14697sofia.plugins.push('LemmaMatchPlugin'); 14698 14699 14700 14701/********* 14702* ../app/js/plugins/lemmapopup.js 14703**********/ 14704sofia.config = $.extend(sofia.config, { 14705 14706 enableLemmaPopupPlugin: true 14707 14708}); 14709 14710 14711var LemmaPopupPlugin = function(app) { 14712 14713 if (!sofia.config.enableLemmaPopupPlugin) { 14714 return; 14715 } 14716 14717 var lemmaPopup = new InfoWindow('lemma-popup'), 14718 timer = new Timer(hidePopup, 500); 14719 14720 14721 function hidePopup() { 14722 lemmaPopup.hide(); 14723 } 14724 14725 lemmaPopup.on('hide', function() { 14726 $('.selected-lemma').removeClass('selected-lemma'); 14727 }); 14728 14729 lemmaPopup.container 14730 .on('click', '.lemma-findall', function(e) { 14731 var link = $(this), 14732 lemma = link.attr('data-lemma'), 14733 textid = link.attr('data-textid'); 14734 14735 console.log(lemma, textid); 14736 14737 sofia.app.windowManager.add('SearchWindow', {searchtext: lemma, textid: textid}); 14738 14739 lemmaPopup.hide(); 14740 }); 14741 14742 14743 $('.windows-main').on('click','.BibleWindow l', function(e) { 14744 14745 var l = $(this); 14746 14747 if (lemmaPopup.container.is(':visible')) { 14748 lemmaPopup.hide(); 14749 if (lemmaPopup.currentWord == this) { 14750 lemmaPopup.currentWord == null; 14751 l.removeClass('selected-lemma'); 14752 return; 14753 } 14754 } 14755 14756 lemmaPopup.currentWord = this; 14757 14758 $('.selected-lemma').removeClass('selected-lemma'); 14759 l.addClass('selected-lemma'); 14760 14761 var 14762 // get attribute 14763 morph = l.attr('m'), 14764 // split into array 14765 morphs = (morph && morph != null) ? morph.split(' ') : [], 14766 14767 // get attribute 14768 strong = l.attr('s'), 14769 // split into array 14770 strongs = (strong && strong != null) ? strong.replace(/H/gi,'').replace(/G/gi,'').split(' ') : [], 14771 14772 // normal elements stuff 14773 main = l.closest('.scroller-main'), 14774 verse = l.closest('.verse, .v') 14775 verse_code = verse.attr('data-id'), 14776 book_id = verse_code.substring(0,2), 14777 chapter = l.closest('.chapter'), 14778 sectionid = chapter.attr('data-id'), 14779 textid = chapter.attr('data-textid'), 14780 langPrefix = 'G', 14781 langCode = 'el', 14782 morphType = 'Greek', 14783 dir = 'ltr'; 14784 14785 // make strong's into ints 14786 for (var i=0; i<strongs.length; i++) { 14787 strongs[i] = parseInt(strongs[i], 10); 14788 } 14789 14790 14791 // check for language (greek or hebrew, noting LXX is greek) 14792 var sectionLang = l.closest('.section').attr('lang'); 14793 14794 if (sectionLang == 'el' || 14795 sectionLang == 'gr' || 14796 sectionLang == 'grc' || 14797 sectionLang == 'grk' || 14798 sectionLang == 'el' || 14799 bible.NT_BOOKS.indexOf(book_id) > -1 || 14800 bible.AP_BOOKS.indexOf(book_id) > -1 14801 ) { 14802 langPrefix = 'G'; 14803 langCode = 'el'; 14804 dir = 'ltr'; 14805 morphType = 'Greek'; 14806 14807 } else if ( 14808 sectionLang == 'he' || 14809 sectionLang == 'heb' || 14810 bible.OT_BOOKS.indexOf(book_id) > -1){ 14811 langPrefix = 'H'; 14812 langCode = 'he'; 14813 dir = 'rtl'; 14814 morphType = 'Hebrew'; 14815 14816 } 14817 14818 // remove G3588 and H853 (the) when there is more than two 14819 if (strongs.length > 0) { 14820 var articleIndex = -1; 14821 14822 for (var i=0; i<strongs.length; i++) { 14823 if ((strongs[i] == 3588 && langPrefix == 'G') || (strongs[i] == 853 && langPrefix == 'H')) { 14824 articleIndex = i; 14825 break; 14826 } 14827 } 14828 14829 if (articleIndex > -1) { 14830 strongs.splice(articleIndex, 1); 14831 14832 if (morphs.length > articleIndex) { 14833 morphs.splice(articleIndex, 1); 14834 } 14835 } 14836 } 14837 14838 //console.warn(strongs); 14839 14840 // record 14841 if (sofia.analytics) { 14842 sofia.analytics.record('lemma', langPrefix + strongs, l.html()); 14843 } 14844 14845 14846 // show popup 14847 lemmaPopup.setClickTargets([lemmaPopup.container, l]); 14848 lemmaPopup.show(); 14849 lemmaPopup.position(l); 14850 14851 lemmaPopup.body.html('Loading...'); 14852 14853 if (strongs.length > 0) { 14854 14855 lemmaPopup.body.html(''); 14856 lemmaPopup.body.addClass('loading-indicator'); 14857 14858 for (var i=0, il=strongs.length; i<il; i++) { 14859 14860 loadStrongsData(textid, strongs[i], i < morphs.length ? morphs[i] : '', morphType, langPrefix, langCode, l); 14861 14862 } 14863 } 14864 }); 14865 14866 14867 function loadStrongsData(textid, strongsNumber, morphKey, morphType, langPrefix, langCode, l) { 14868 sofia.ajax({ 14869 dataType: 'json', 14870 url: 'content/lexicons/strongs/entries/' + langPrefix + strongsNumber + '.json', 14871 success: function(data) { 14872 14873 var html = '<div class="lemma-word">' + 14874 '<span lang="' + iso2iana.convert(langCode) + '" dir="' + dir + '">' + data.lemma + '</span>' + 14875 ' ' + 14876 '<span class="lemma-strongs" dir="ltr"> (' + strongsNumber + ')</span>' + 14877 '</div>'; 14878 14879 14880 14881 if (morphKey != '') { 14882 html += '<span class="lemma-morphology">' + bible.morphology[morphType].format( morphKey ) + '</span>'; 14883 } 14884 14885 html += '<span class="lemma-findall" data-lemma="' + langPrefix + strongsNumber + '" data-textid="' + textid + '">' + 14886 i18n.t('plugins.lemmapopup.findalloccurrences', {count: data.frequency}) + 14887 '</span>'; 14888 14889 html += '<div class="lemma-outline">
14889' + data.outline + '</div>'; 14890 14891 lemmaPopup.body.removeClass('loading-indicator'); 14892 14893 lemmaPopup.body.append( html ); 14894 14895 lemmaPopup.position(l); 14896 14897 }, 14898 error: function() { 14899 lemmaPopup.body.html('Error loading ... ' + langPrefix + strongs[0] ); 14900 } 14901 14902 14903 }); 14904 } 14905 14906 14907}; 14908 14909sofia.plugins.push('LemmaPopupPlugin'); 14910 14911 14912 14913/********* 14914* ../app/js/plugins/versematch.js 14915**********/ 14916sofia.config = $.extend(sofia.config, { 14917 14918 enableVerseMatchPlugin: true 14919 14920}); 14921 14922var VerseMatchPlugin = function(app) { 14923 14924 if (!sofia.config.enableVerseMatchPlugin) { 14925 return; 14926 } 14927 14928 14929 if (!Detection.hasTouch) { 14930 14931 $('.windows-main').on('mouseover','.BibleWindow .verse, .BibleWindow .v', function(e) { 14932 14933 var verse = $(this), 14934 verseid = verse.attr('data-id'); 14935 14936 $('.BibleWindow .' + verseid).addClass('selected-verse'); 14937 14938 }).on('mouseout','.BibleWindow .verse, .BibleWindow .v', function(e) { 14939 var verse = $(this), 14940 verseid = verse.attr('data-id'); 14941 14942 $('.BibleWindow .' + verseid).removeClass('selected-verse'); 14943 }); 14944 } 14945}; 14946 14947sofia.plugins.push('VerseMatchPlugin'); 14948 14949 14950 14951/********* 14952* ../app/js/plugins/eng2p.js 14953**********/ 14954sofia.config = $.extend(sofia.config, { 14955 14956 enableEng2pPlugin: true, 14957 14958 // enables all possible English options (false = just color 'you') 14959 eng2pEnableAll: true, 14960 14961 eng2pDefaultSetting: 'none', 14962 14963 eng2pShowWindowAtStartup: false 14964}); 14965 14966 14967/** 14968 * Highlights words based on morphological data 14969 * 14970 * @author John Dyer (http://j.hn/) 14971 */ 14972var Eng2pPlugin = function(node) { 14973 14974 var languages = ['eng','en','en-us','en-gb']; 14975 14976 14977 if (!sofia.config.enableEng2pPlugin) { 14978 return; 14979 } 14980 14981 var 14982 engWindow = new MovableWindow(550,290), 14983 14984 configBlock = 14985 $('<div class="config-options" id="config-eng2p">' + 14986 //'<h3>English Second Person Plural</h3>' + 14987'<p class="i18n" data-i18n="[html]plugins.eng2p.description"></p>' + 14988'<table>' + 14989 '<tbody>' + 14990 '<tr>' + 14991 '<th>' + 14992 '<input type="radio" name="eng2p-option" id="eng2p-option-none" value="none" />' + 14993 '<label for="eng2p-option-none">None</label>' + 14994 '</th>' + 14995 '<td>You</td>' + 14996 '<td>Your</td>' + 14997 '<td>Yours</td>' + 14998 '<td>Yourselves</td>' + 14999 '</tr>' + 15000 '<tr>' + 15001 '<th>' + 15002 '<input type="radio" name="eng2p-option" id="eng2p-option-highlight" value="highlight" />' + 15003 '<label for="eng2p-option-highlight">Highlight</label>' + 15004 '</th>' + 15005 '<td><span class="eng2p-highlight-demo">You</span></td>' + 15006 '<td><span class="eng2p-highlight-demo">Your</span></td>' + 15007 '<td><span class="eng2p-highlight-demo">Yours</span></td>' + 15008 '<td><span class="eng2p-highlight-demo">Yourselves</span></td>' + 15009 '</tr>' + 15010 15011 (typeof sofia.config["eng2pEnableAll"] != 'undefined' && sofia.config["eng2pEnableAll"] == true ? 15012 '<tr>' + 15013 '<th>' + 15014 '<input type="radio" name="eng2p-option" id="eng2p-option-youall" value="youall" />' + 15015 '<label for="eng2p-option-youall">General US</label>' + 15016 '</th>' + 15017 '<td>You all</td>' + 15018 '<td>You all\'s</td>' + 15019 '<td>You all\'s</td>' + 15020 '<td>You allselves</td>' + 15021 '</tr>' + 15022 '<tr>' + 15023 '<th>' + 15024 '<input type="radio" name="eng2p-option" id="eng2p-option-yall" value="yall" />' + 15025 '<label for="eng2p-option-yall">Southern US</label>' + 15026 '</th>' + 15027 '<td>Y\'all</td>' + 15028 '<td>Y\'all\'s</td>' + 15029 '<td>Y\'all\'s</td>' + 15030 '<td>Y\'allselves</td>' + 15031 '</tr>' + 15032 '<tr>' + 15033 '<th>' + 15034 '<input type="radio" name="eng2p-option" id="eng2p-option-youguys" value="youguys" />' + 15035 '<label for="eng2p-option-youguys">Western US</label>' + 15036 '</th>' + 15037 '<td>You guys</td>' + 15038 '<td>Your guys\'s</td>' + 15039 '<td>Your guys\'s</td>' + 15040 '<td>Your guys selves</td>' + 15041 '</tr>' + 15042 '<tr>' + 15043 '<th>' + 15044 '<input type="radio" name="eng2p-option" id="eng2p-option-youseguys" value="youseguys" />' + 15045 '<label for="eng2p-option-youseguys">NYC/Chicago</label>' + 15046 '</th>' + 15047 '<td>Youse guys</td>' + 15048 '<td>Youse guys\'s</td>' + 15049 '<td>Youse guys\'s</td>' + 15050 '<td>Youse guys selves</td>' + 15051 '</tr>' + 15052 '<tr>' + 15053 '<th>' + 15054 '<input type="radio" name="eng2p-option" id="eng2p-option-yinz" value="yinz" />' + 15055 '<label for="eng2p-option-yinz">Pittsburgh</label>' + 15056 '</th>' + 15057 '<td>Yinz</td>' + 15058 '<td>Yinz\'s</td>' + 15059 '<td>Yinz\'s</td>' + 15060 '<td>Yinzselves</td>' + 15061 '</tr>' + 15062 '<tr>' + 15063 '<th>' + 15064 '<input type="radio" name="eng2p-option" id="eng2p-option-youlot" value="youlot" />' + 15065 '<label for="eng2p-option-youlot">United Kingdom</label>' + 15066 '</th>' + 15067 '<td>You lot</td>' + 15068 '<td>You lot\'s</td>' + 15069 '<td>You lot\'s</td>' + 15070 '<td>Yourlot\'s</td>' + 15071 '</tr>' + 15072 '<tr>' + 15073 '<th>' + 15074 '<input type="radio" name="eng2p-option" id="eng2p-option-ye" value="ye" />' + 15075 '<label for="eng2p-option-ye">Old English</label>' + 15076 '</th>' + 15077 '<td>Ye</td>' + 15078 '<td>Ye\'s</td>' + 15079 '<td>Ye\'s</td>' + 15080 '<td>Yeselves</td>' + 15081 '</tr>' + 15082 '<tr>' + 15083 '<th>' + 15084 '<input type="radio" name="eng2p-option" id="eng2p-option-youseguys" value="youse" />' + 15085 '<label for="eng2p-option-youseguys">Australia</label>' + 15086 '</th>' + 15087 '<td>Youse</td>' + 15088 '<td>Youse\'s</td>' + 15089 '<td>Youse\'s</td>' + 15090 '<td>Youseselves</td>' + 15091 '</tr>' 15092 :'') + 15093 '</tbody>' + 15094'</table>' + 15095 '</div>') 15096 .appendTo( engWindow.body ), 15097 button = $('<span class="config-button i18n" data-i18n="[html]plugins.eng2p.title" id="config-eng2p-button"></span>') 15098 .appendTo( $('#config-tools .config-body') ); 15099 15100 15101 //engWindow.title.html('English Second Person Plural'); 15102 engWindow.title 15103 .addClass('i18n') 15104 .attr('data-i18n','[html]plugins.eng2p.title') 15105 15106 button.on('click', function() { 15107 15108 engWindow.show().center(); 15109 15110 button.closest('.window-overlay').hide(); 15111 15112 }); 15113 15114 // SET DEFAULT 15115 15116 // setting from localStorage 15117 var eng2pSetting = AppSettings.getValue('docs-config-eng2p-setting', {'eng2p': sofia.config.eng2pDefaultSetting });
15118 15119 // override with querysring 15120 var queryString = window.location.search && window.location.search.length > 0 ? window.location.search.substring(1) : '', 15121 params = stringUtility.parseQuerystring( queryString ); 15122 15123 15124 // if it's in the querystring, try to use it 15125 if (typeof params['eng2p'] != 'undefined') { 15126 var tempEng2pSetting = params['eng2p']; 15127 15128 // see if there is a matching value 15129 if ($('#eng2p-option-' + tempEng2pSetting).length > 0) { 15130 eng2pSetting.eng2p = tempEng2pSetting; 15131 15132 } 15133 } 15134 15135 if (typeof params['eng2pshow'] != 'undefined' || sofia.config.eng2pShowWindowAtStartup === true) { 15136 engWindow.show(); 15137 engWindow.container.css({left: $(window).width() - engWindow.container.outerWidth(true) - 10}); 15138 } 15139 15140 // now set the value from either localStorage or querysring 15141 $('#eng2p-option-' + eng2pSetting.eng2p).prop('checked',true); 15142 getPluralValues(); 15143 15144 // create updates 15145 $('input[name="eng2p-option"]').on('click',function() { 15146 // update the setting value 15147 eng2pSetting = {eng2p: $(this).val() }; 15148 15149 // store value 15150 AppSettings.setValue('docs-config-eng2p-setting', eng2pSetting); 15151 15152 // values 15153 getPluralValues(); 15154 15155 // re-run 15156 $('div.chapter').each(function() { 15157 var chapter = $(this); 15158 15159 removePluralTransforms(chapter); 15160 checkChapter(chapter); 15161 }); 15162 }); 15163 15164 function checkChapter(chapter) { 15165 15166 var lang = chapter.attr('lang') != null ? chapter.attr('lang').toLowerCase() : ''; 15167 15168 if (languages.indexOf(lang) > -1 && eng2pSetting.eng2p != 'none') { 15169 //console.log('Eng2P', e.data.content.attr('data-id')); 15170 15171 //console.log('--transforming'); 15172 15173 runPluralTransforms(chapter); 15174 } 15175 15176 } 15177 15178 15179 function getPluralValues() { 15180 15181 var selectedRow = $('#eng2p-option-' + eng2pSetting.eng2p).closest('tr'); 15182 15183 bible.eng2p.youPluralSubject = selectedRow.find('td:eq(0)').html(); 15184 bible.eng2p.youPluralPossessiveDeterminer = selectedRow.find('td:eq(1)').html(); 15185 bible.eng2p.youPluralPossessivePronoun = selectedRow.find('td:eq(2)').html(); 15186 bible.eng2p.youPluralReflexive = selectedRow.find('td:eq(3)').html(); 15187 } 15188 15189 function removePluralTransforms(node) { 15190 bible.eng2p.removePluralTransforms(node); 15191 } 15192 15193 function runPluralTransforms(node) { 15194 node.find('.verse, .v').each(function(index, el) { 15195 var verse = $(this), 15196 verseid = verse.attr('data-id'); 15197 15198 15199 //if (bible.eng2p.doubles.indexOf(verseid) > -1) { 15200 //verse.addClass('eng2p-doubles'); 15201 if (bible.eng2p.pronouns.indexOf(verseid) > -1) { 15202 verse.addClass('eng2p-pronouns'); 15203 } 15204 if (bible.eng2p.verbs.indexOf(verseid) > -1) { 15205 verse.addClass('eng2p-verbs'); 15206 } 15207 15208 15209 // pronouns without also having a singular verb or pronoun 15210 if ((bible.eng2p.verbs.indexOf(verseid) > -1 || bible.eng2p.pronouns.indexOf(verseid) > -1) && bible.eng2p.doubles.indexOf(verseid) == -1) { 15211 var html = verse.html(); 15212 15213 if (eng2pSetting.eng2p == 'highlight') { 15214 html = bible.eng2p.highlightPlurals( html ); 15215 } else if (eng2pSetting.eng2p != 'none') { 15216 html = bible.eng2p.replacePlurals( html ); 15217 } 15218 15219 verse.html( html ); 15220 15221 }; 15222 }); 15223 } 15224 15225 // run transforms 15226 var ext = { 15227 sendMessage: function() {} 15228 }; 15229 ext = $.extend(true, ext, EventEmitter); 15230 15231 15232 ext.on('message', function(e) { 15233 15234 //console.log(e.data.messagetype, e.data.type, e.data.content.attr('lang').toLowerCase(), e.data.textid); 15235 15236 if (e.data.messagetype == 'textload' && e.data.type == 'bible') { 15237 15238 checkChapter(e.data.content); 15239 15240 } 15241 15242 }); 15243 15244 15245 return ext; 15246} 15247 15248sofia.plugins.push('Eng2pPlugin'); 15249 15250 15251bible.eng2p = { 15252 15253 youPluralRegExp: /\b([yY])(ou(r|rs|rselves)?|e)\b/g, 15254 15255 youPluralSubject: "Y'all", 15256 youPluralPossessiveDeterminer: "Y'all's", 15257 youPluralPossessivePronoun: "Y'all's", 15258 youPluralReflexive: "Y'allselves", 15259 15260 removePluralTransforms: function(node) { 15261 // remove the changed words 15262 node.find('.eng2p-corrected').remove(); 15263 15264 // remove the surrounding spans 15265 node.find('.eng2p-highlight').each(function() { 15266 var span = this; 15267 15268 this.parentNode.replaceChild(document.createTextNode(span.textContent), this); 15269 }); 15270 15271 // remove the surrounding spans 15272 node.find('.eng2p-original').each(function() { 15273 var span = this; 15274 15275 this.parentNode.replaceChild(document.createTextNode(span.textContent), this); 15276 }); 15277 }, 15278 15279 highlightPlurals: function(input) { 15280 var output = input.replace(bible.eng2p.youPluralRegExp, function(match, $1, $2, offset, originalString) { 15281 15282 // return the greatness 15283 return '<span class="eng2p-highlight">' + match + '</span>'; 15284 }); 15285 15286 return output; 15287 }, 15288 15289 replacePlurals: function(input) { 15290 15291 var output = input.replace(bible.eng2p.youPluralRegExp, function(match, $1, $2, offset, originalString) { 15292 var replacement = ''; 15293 15294 // you, your, yours checker 15295 switch (match.toLowerCase()) { 15296 case 'ye': 15297 case 'you': 15298 replacement = bible.eng2p.youPluralSubject; 15299 break; 15300 case 'your':
15301 replacement = bible.eng2p.youPluralPossessiveDeterminer; 15302 break; 15303 case 'yours': 15304 replacement = bible.eng2p.youPluralPossessivePronoun; 15305 break; 15306 case 'yourselves': 15307 replacement = bible.eng2p.youPluralReflexive; 15308 break; 15309 default: 15310 replacement = match; // 'UNKNOWN [' + match + ',' + $1 + ']'; 15311 break; 15312 15313 } 15314 15315 // You vs. you 15316 if ($1 === $1.toUpperCase()) { 15317 replacement = replacement.substring(0,1).toUpperCase() + replacement.substring(1); 15318 } else { 15319 replacement = replacement.substring(0,1).toLowerCase() + replacement.substring(1); 15320 } 15321 15322 // replace standard ' with â 15323 replacement = replacement.replace(/'/gi,'’'); 15324 15325 15326 // return the greatness 15327 return '<span class="eng2p-original">' + match + '</span><span class="eng2p-corrected">' + replacement + '</span>'; 15328 }); 15329 15330 return output; 15331 }, 15332 15333"pronouns":["GN1_29","GN3_5","GN9_2","GN9_3","GN9_5","GN9_7","GN9_9","GN9_10","GN9_11","GN9_12","GN9_15","GN17_10","GN17_11","GN17_12","GN17_13","GN18_4","GN18_5","GN19_2","GN19_8","GN22_5","GN23_4","GN23_8","GN23_9","GN24_49","GN26_27","GN27_45","GN29_4","GN31_5","GN31_6","GN31_7","GN31_9","GN31_29","GN32_19","GN34_8","GN34_9","GN34_10","GN34_11","GN34_15","GN34_16","GN35_2","GN37_7","GN40_7","GN41_55","GN42_9","GN42_14","GN42_15","GN42_16","GN42_19","GN42_20","GN42_22","GN42_33","GN42_34","GN42_38","GN43_3","GN43_5","GN43_6","GN43_7","GN43_11","GN43_12","GN43_13","GN43_14","GN43_23","GN43_27","GN43_29","GN44_10","GN44_17","GN44_19","GN44_23","GN44_27","GN45_4","GN45_5","GN45_7","GN45_8","GN45_12","GN45_17","GN45_18","GN45_19","GN45_20","GN46_33","GN47_3","GN47_16","GN47_23","GN47_24","GN48_21","GN49_1","GN49_2","GN50_4","GN50_20","GN50_21","GN50_24","GN50_25","EX1_16","EX3_13","EX3_14","EX3_15","EX3_16","EX3_17","EX3_19","EX3_20","EX3_22","EX4_15","EX5_4","EX5_10","EX5_11","EX5_13","EX5_14","EX5_17","EX5_18","EX5_19","EX5_21","EX6_6","EX6_7","EX6_8","EX7_4","EX7_9","EX8_25","EX8_28","EX9_8","EX9_28","EX10_5","EX10_8","EX10_10","EX10_11","EX10_16","EX10_17","EX10_24","EX11_1","EX11_9","EX12_2","EX12_5","EX12_6","EX12_11","EX12_13","EX12_14","EX12_15","EX12_16","EX12_17","EX12_19","EX12_20","EX12_21","EX12_22","EX12_23","EX12_25","EX12_26","EX12_31","EX12_32","EX12_49","EX13_3","EX13_4","EX13_19","EX14_13","EX14_14","EX16_4","EX16_6","EX16_7","EX16_8","EX16_9","EX16_12","EX16_15","EX16_16","EX16_23","EX16_29","EX16_32","EX16_33","EX18_10","EX19_4","EX19_6","EX19_12","EX20_20","EX20_22","EX20_23","EX22_24","EX23_9","EX23_13","EX23_21","EX23_25","EX23_31","EX24_8","EX24_14","EX26_33","EX29_42","EX30_8","EX30_10","EX30_15","EX30_16","EX30_31","EX30_32","EX30_36","EX30_37","EX31_13","EX31_14","EX32_2","EX32_13","EX32_29","EX32_30","EX33_5","EX35_2","EX35_3","EX35_5","EX35_10","LV1_2","LV3_17","LV6_18","LV7_26","LV7_32","LV8_33","LV8_34","LV9_4","LV9_6","LV10_4","LV10_6","LV10_7","LV10_9","LV10_17","LV11_4","LV11_5","LV11_6","LV11_7","LV11_8","LV11_10","LV11_11","LV11_12","LV11_20","LV11_23","LV11_26","LV11_27","LV11_28","LV11_29","LV11_31","LV11_35","LV11_38","LV11_39","LV11_43","LV11_44","LV11_45","LV14_34","LV16_29","LV16_30","LV16_31","LV16_34","LV17_11","LV17_12","LV18_2","LV18_3","LV18_4","LV18_24","LV18_26","LV18_27","LV18_28","LV18_30","LV19_2","LV19_3","LV19_4","LV19_5","LV19_6","LV19_9","LV19_10","LV19_23","LV19_25","LV19_27","LV19_28","LV19_31","LV19_33","LV19_34","LV19_36","LV20_7","LV20_8","LV20_14","LV20_22","LV20_23","LV20_24","LV20_25","LV20_26","LV21_8","LV22_3","LV22_19","LV22_20","LV22_24","LV22_25","LV22_29","LV22_32","LV22_33","LV23_3","LV23_7","LV23_10","LV23_11","LV23_12","LV23_14","LV23_15","LV23_17","LV23_21","LV23_22","LV23_24","LV23_27","LV23_28","LV23_31","LV23_32","LV23_36","LV23_38","LV23_39","LV23_40","LV23_41","LV23_43","LV24_3","LV24_22","LV25_2","LV25_6","LV25_9","LV25_10","LV25_11","LV25_12","LV25_17","LV25_21","LV25_23","LV25_24","LV25_38","LV25_44","LV25_45","LV25_46","LV25_55","LV26_1","LV26_4","LV26_5","LV26_6","LV26_7","LV26_8","LV26_9","LV26_11","LV26_12","LV26_13","LV26_15","LV26_16","LV26_17","LV26_18","LV26_19","LV26_20","LV26_21","LV26_22","LV26_24","LV26_25","LV26_26","LV26_28","LV26_29","LV26_30","LV26_31","LV26_32","LV26_33","LV26_34","LV26_35","LV26_36","LV26_37","LV26_38","LV26_39","NU1_4","NU1_5","NU9_8","NU9_10","NU9_14","NU10_8","NU10_9","NU10_10","NU10_29","NU11_18","NU11_20","NU12_4","NU12_6","NU14_9","NU14_25","NU14_28","NU14_29","NU14_30","NU14_31","NU14_32","NU14_33","NU14_34","NU14_41","NU14_42","NU14_43","NU15_2","NU15_3","NU15_14","NU15_15","NU15_16","NU15_18","NU15_19","NU15_20","NU15_21","NU15_23","NU15_29","NU15_39","NU15_40","NU15_41","NU16_3","NU16_6","NU16_7","NU16_9","NU16_41","NU17_4","NU17_5","NU18_1","NU18_3","NU18_4","NU18_6","NU18_7","NU18_23","NU18_26","NU18_27","NU18_28","NU18_29","NU18_30","NU18_31","NU18_32","NU20_10","NU22_8","NU22_13","NU22_19","NU25_18","NU28_11","NU28_19","NU28_22","NU28_25","NU28_26","NU28_30","NU28_31","NU29_1","NU29_5","NU29_7","NU29_8","NU29_12","NU29_35","NU29_39","NU31_3","NU31_18","NU31_19","NU31_24","NU32_
153336","NU32_8","NU32_14","NU32_21","NU32_22","NU32_23","NU32_24","NU32_29","NU32_30","NU33_51","NU33_52","NU33_53","NU33_54","NU33_55","NU33_56","NU34_2","NU34_3","NU34_4","NU34_6","NU34_7","NU34_9","NU34_10","NU34_12","NU34_17","NU35_10","NU35_11","NU35_12","NU35_13","NU35_29","NU35_33","NU35_34","DT1_6","DT1_7","DT1_8","DT1_9","DT1_10","DT1_11","DT1_12","DT1_13","DT1_15","DT1_16","DT1_17","DT1_18","DT1_20","DT1_22","DT1_23","DT1_26","DT1_27","DT1_29","DT1_30","DT1_31","DT1_32","DT1_33","DT1_34","DT1_35","DT1_37","DT1_39","DT1_40","DT1_42","DT1_43","DT1_44","DT1_45","DT2_3","DT2_4","DT2_5","DT2_13","DT3_18","DT3_19","DT3_20","DT3_21","DT3_22","DT3_26","DT4_1","DT4_2","DT4_3","DT4_4","DT4_5","DT4_6","DT4_8","DT4_12","DT4_13","DT4_14","DT4_15","DT4_16","DT4_20","DT4_21","DT4_22","DT4_23","DT4_26","DT4_27","DT4_34","DT5_1","DT5_4","DT5_5","DT5_22","DT5_23","DT5_28","DT5_30","DT5_32","DT5_33","DT6_1","DT6_14","DT6_16","DT6_17","DT6_20","DT7_4","DT7_7","DT7_8","DT8_1","DT8_19","DT8_20","DT9_7","DT9_8","DT9_9","DT9_10","DT9_16","DT9_17","DT9_18","DT9_19","DT9_21","DT9_23","DT9_24","DT9_25","DT10_4","DT10_15","DT10_16","DT10_17","DT11_2","DT11_4","DT11_5","DT11_7","DT11_8","DT11_9","DT11_11","DT11_13","DT11_14","DT11_16","DT11_17","DT11_18","DT11_19","DT11_21","DT11_22","DT11_23","DT11_24","DT11_25","DT11_26","DT11_27","DT11_28","DT11_31","DT11_32","DT12_1","DT12_2","DT12_4","DT12_5","DT12_6","DT12_7","DT12_10","DT12_11","DT12_12","DT12_32","DT13_3","DT13_4","DT13_5","DT13_7","DT14_1","DT14_7","DT14_8","DT14_10","DT14_19","DT17_16","DT20_2","DT20_3","DT20_4","DT20_18","DT23_4","DT24_8","DT24_9","DT25_17","DT27_1","DT27_4","DT27_12","DT28_14","DT28_63","DT29_2","DT29_4","DT29_5","DT29_6","DT29_10","DT29_11","DT29_14","DT29_16","DT29_18","DT29_22","DT30_18","DT30_19","DT31_5","DT31_12","DT31_13","DT31_19","DT31_26","DT31_27","DT31_28","DT31_29","DT32_17","DT32_38","DT32_46","DT32_47","JS1_3","JS1_4","JS1_11","JS1_13","JS1_14","JS1_15","JS2_9","JS2_10","JS2_11","JS2_12","JS2_14","JS2_16","JS2_21","JS3_3","JS3_4","JS3_5","JS3_8","JS3_9","JS3_10","JS3_11","JS3_12","JS4_2","JS4_3","JS4_5","JS4_6","JS4_21","JS4_22","JS4_23","JS4_24","JS5_9","JS6_5","JS6_10","JS6_16","JS6_18","JS7_12","JS7_13","JS7_14","JS8_2","JS8_4","JS8_7","JS8_8","JS9_8","JS9_11","JS9_12","JS9_22","JS9_23","JS9_24","JS10_19","JS10_24","JS10_25","JS15_4","JS18_3","JS18_4","JS18_6","JS18_7","JS18_8","JS20_2","JS20_3","JS22_2","JS22_3","JS22_4","JS22_5","JS22_8","JS22_16","JS22_18","JS22_19","JS22_24","JS22_25","JS22_27","JS22_28","JS23_3","JS23_4","JS23_5","JS23_7","JS23_8","JS23_9","JS23_10","JS23_11","JS23_12","JS23_13","JS23_14","JS23_15","JS23_16","JS24_2","JS24_3","JS24_5","JS24_6","JS24_7","JS24_8","JS24_9","JS24_10","JS24_11","JS24_12","JS24_13","JS24_14","JS24_15","JS24_19","JS24_20","JS24_22","JS24_23","JS24_27","JG2_1","JG2_2","JG2_3","JG3_28","JG6_8","JG6_9","JG6_10","JG6_31","JG7_7","JG7_15","JG7_18","JG8_2","JG8_3","JG8_7","JG8_19","JG8_23","JG8_24","JG9_2","JG9_7","JG9_15","JG9_17","JG9_18","JG9_19","JG10_12","JG10_13","JG10_14","JG11_7","JG11_9","JG12_2","JG12_4","JG14_12","JG14_13","JG15_7","JG15_12","JG18_6","JG18_8","JG18_9","JG18_10","JG18_18","JG18_25","JG19_9","JG19_24","JG19_30","JG20_7","JG20_12","JG21_21","JG21_22","RT1_8","RT1_9","RT1_11","RT1_13","RT2_4","RT4_9","RT4_10","S12_3","S12_23","S12_29","S14_9","S16_3","S16_4","S16_5","S16_6","S16_21","S17_3","S17_5","S18_11","S18_13","S18_14","S18_15","S18_16","S18_17","S18_18","S19_13","S110_2","S110_15","S110_18","S110_19","S111_2","S111_9","S111_10","S112_1","S112_2","S112_3","S112_5","S112_6","S112_7","S112_8","S112_11","S112_12","S112_13","S112_14","S112_15","S112_16","S112_17","S112_20","S112_22","S112_23","S112_24","S112_25","S114_9","S114_12","S114_40","S117_8","S117_9","S117_47","S118_23","S122_3","S122_7","S122_8","S123_21","S123_23","S125_19","S126_16","S129_10","S130_24","S130_26","S21_21","S21_24","S22_5","S22_6","S22_7","S23_17","S23_31","S24_11","S27_23","S210_5","S213_28","S215_10","S215_27","S215_28","S216_10","S216_20","S217_21","S218_2","S218_4","S219_10","S219_12","S219_22","S221_3","S221_4","K11_33","K18_61","K19_6","K111_2","K112_6","K112_9","K112_11","K112_14","K112_24","K112_28","K118_18","K118_21","K118_24","K118_25","K21_3","K21_6","K21_7","K22_18","K23_17","K23_18","K26_19","K27_12","K29_11","K29_15","K210_2","K210_3","K210_6","K210_9","K210_13","K210_23","K210_24","K211_5","K211_7","K212_7","K217_13","K217_36","K217_37","K2
1533317_38","K217_39","K218_27","K218_29","K218_30","K218_32","K219_6","K222_15","K222_18","K223_21","K225_24","R112_17","R113_2","R115_12","R115_13","R116_18","R116_19","R119_5","R122_18","R122_19","R128_8","R129_20","R27_19","R210_6","R210_9","R210_11","R210_14","R211_4","R212_5","R213_5","R213_8","R213_9","R213_11","R213_12","R215_2","R215_7","R218_14","R219_6","R219_7","R219_10","R219_11","R220_15","R220_17","R220_20","R223_4","R224_5","R224_20","R228_9","R228_10","R228_11","R228_13","R229_5","R229_8","R229_11","R229_31","R230_6","R230_7","R230_8","R230_9","R232_10","R232_11","R232_14","R232_15","R233_8","R234_23","R234_26","R235_3","R235_4","R235_5","R235_6","R236_23","ER1_3","ER4_2","ER4_3","ER8_28","ER9_11","ER9_12","ER10_10","ER10_11","NH1_8","NH1_9","NH2_17","NH2_19","NH2_20","NH4_14","NH5_7","NH5_8","NH5_9","NH5_11","NH6_3","NH8_9","NH8_10","NH9_5","NH13_17","NH13_18","NH13_21","NH13_25","NH13_27","ET8_8","JB6_22","JB6_25","JB6_27","JB6_28","JB12_2","JB12_3","JB13_2","JB13_4","JB13_5","JB13_9","JB13_10","JB13_11","JB13_12","JB13_17","JB16_2","JB16_4","JB16_5","JB17_10","JB18_3","JB19_21","JB19_29","JB21_2","JB21_27","JB21_34","JB27_5","JB27_11","JB27_12","JB32_6","JB32_11","JB32_12","JB32_14","JB42_8","PS4_4","PS11_1","PS22_26","PS24_7","PS24_9","PS31_24","PS34_11","PS48_13","PS58_2","PS58_9","PS62_3","PS62_8","PS69_32","PS75_5","PS76_11","PS78_1","PS82_6","PS95_8","PS95_9","PS105_11","PS115_14","PS115_15","PS118_26","PS127_2","PS129_8","PS134_2","PR1_23","PR1_26","PR1_27","PR4_2","PR8_4","SS2_7","SS3_5","SS5_8","SS8_4","IS1_7","IS1_11","IS1_12","IS1_14","IS1_15","IS1_16","IS1_18","IS2_22","IS3_14","IS3_15","IS5_5","IS5_8","IS7_13","IS7_14","IS8_13","IS8_19","IS10_3","IS21_10","IS22_14","IS23_7","IS23_14","IS27_12","IS28_18","IS28_19","IS28_22","IS29_10","IS29_11","IS29_16","IS30_3","IS30_12","IS30_13","IS30_15","IS30_16","IS30_18","IS30_20","IS30_29","IS31_7","IS32_20","IS33_4","IS33_11","IS35_4","IS36_12","IS36_14","IS36_15","IS36_17","IS36_18","IS37_6","IS40_1","IS40_9","IS40_21","IS40_26","IS41_21","IS41_23","IS41_24","IS41_26","IS42_9","IS42_17","IS42_23","IS43_10","IS43_12","IS43_14","IS43_15","IS44_8","IS46_1","IS48_6","IS48_14","IS50_1","IS50_10","IS50_11","IS51_2","IS51_6","IS51_12","IS52_12","IS55_2","IS55_3","IS55_8","IS55_9","IS55_12","IS57_3","IS57_4","IS58_3","IS58_4","IS59_2","IS59_3","IS61_5","IS61_6","IS61_7","IS62_6","IS65_7","IS65_11","IS65_12","IS65_13","IS65_14","IS65_15","IS66_5","IS66_13","IS66_14","IS66_20","IS66_22","JR2_5","JR2_7","JR2_9","JR2_29","JR2_30","JR2_31","JR3_12","JR3_14","JR3_15","JR3_18","JR3_22","JR4_3","JR4_4","JR4_10","JR5_14","JR5_15","JR5_18","JR5_19","JR5_25","JR6_16","JR6_17","JR6_20","JR7_3","JR7_4","JR7_5","JR7_6","JR7_7","JR7_8","JR7_11","JR7_13","JR7_14","JR7_15","JR7_21","JR7_22","JR7_23","JR7_25","JR8_17","JR9_20","JR10_1","JR11_4","JR11_5","JR11_7","JR12_13","JR13_16","JR13_18","JR13_20","JR13_23","JR14_13","JR14_14","JR15_14","JR16_9","JR16_11","JR16_12","JR16_13","JR17_1","JR17_21","JR17_22","JR18_6","JR18_11","JR21_4","JR21_5","JR21_8","JR21_9","JR21_14","JR23_2","JR23_16","JR23_17","JR23_33","JR23_38","JR23_39","JR23_40","JR25_3","JR25_4","JR25_5","JR25_6","JR25_7","JR25_27","JR25_29","JR25_34","JR26_4","JR26_5","JR26_11","JR26_13","JR26_14","JR26_15","JR27_4","JR27_9","JR27_10","JR27_12","JR27_14","JR27_15","JR27_16","JR29_6","JR29_7","JR29_8","JR29_9","JR29_10","JR29_11","JR29_12","JR29_13","JR29_14","JR29_16","JR29_20","JR29_21","JR29_27","JR29_31","JR30_22","JR32_36","JR32_43","JR33_10","JR34_13","JR34_14","JR34_15","JR34_16","JR34_17","JR34_21","JR35_6","JR35_7","JR35_14","JR35_15","JR35_18","JR36_19","JR37_7","JR37_9","JR37_10","JR37_19","JR38_5","JR40_3","JR40_9","JR40_10","JR42_4","JR42_9","JR42_10","JR42_11","JR42_12","JR42_13","JR42_15","JR42_16","JR42_18","JR42_19","JR42_20","JR42_21","JR44_2","JR44_3","JR44_4","JR44_7","JR44_8","JR44_9","JR44_10","JR44_11","JR44_21","JR44_22","JR44_23","JR44_25","JR44_29","JR48_6","JR49_30","JR50_12","JR51_24","JR51_46","JR51_50","LM1_12","EK5_7","EK5_16","EK5_17","EK6_3","EK6_4","EK6_5","EK6_6","EK6_7","EK6_8","EK6_9","EK9_5","EK11_5","EK11_6","EK11_7","EK11_8","EK11_9","EK11_10","EK11_11","EK11_12","EK11_17","EK11_19","EK12_11","EK12_22","EK12_25","EK13_8","EK13_11","EK13_12","EK13_15","EK13_18","EK13_19","EK13_20","EK13_21","EK13_23","EK14_6","EK14_22","EK14_23","EK16_45","EK16_55","EK18_2","EK18_3","EK18_25","EK18_29","EK18_30","EK18_31","EK20_3","EK20_5","EK20_7","EK20_18","
15333EK20_19","EK20_20","EK20_27","EK20_29","EK20_30","EK20_31","EK20_32","EK20_33","EK20_34","EK20_35","EK20_36","EK20_37","EK20_38","EK20_39","EK20_40","EK20_41","EK20_42","EK20_43","EK20_44","EK21_24","EK22_19","EK22_20","EK22_21","EK22_22","EK23_48","EK23_49","EK24_21","EK24_23","EK24_24","EK33_11","EK33_20","EK33_25","EK33_26","EK34_17","EK34_18","EK34_19","EK34_21","EK34_31","EK35_13","EK36_2","EK36_3","EK36_7","EK36_8","EK36_9","EK36_10","EK36_11","EK36_12","EK36_13","EK36_22","EK36_23","EK36_24","EK36_25","EK36_26","EK36_27","EK36_28","EK36_29","EK36_31","EK36_32","EK36_33","EK36_36","EK37_5","EK37_6","EK37_12","EK37_13","EK37_14","EK37_25","EK39_17","EK39_19","EK43_27","EK44_6","EK44_7","EK44_8","EK44_30","EK45_1","EK45_9","EK45_10","EK45_12","EK45_21","EK47_14","EK47_21","EK47_22","DN1_10","DN10_21","HS1_9","HS1_10","HS2_1","HS2_2","HS4_13","HS4_14","HS5_1","HS5_13","HS6_4","HS9_10","HS10_12","HS10_15","HS14_2","JL1_2","JL1_3","JL1_5","JL1_13","JL1_14","JL2_12","JL2_13","JL2_14","JL2_19","JL2_20","JL2_23","JL2_25","JL2_26","JL2_27","JL2_28","JL3_4","JL3_5","JL3_7","JL3_8","JL3_10","JL3_17","AM2_10","AM2_11","AM2_13","AM3_1","AM3_2","AM4_2","AM4_4","AM4_6","AM4_7","AM4_9","AM4_10","AM4_11","AM5_1","AM5_11","AM5_12","AM5_14","AM5_18","AM5_21","AM5_22","AM5_26","AM5_27","AM6_2","AM6_14","AM8_10","AM9_7","JH1_12","MC1_2","MC1_11","MC2_3","MC2_4","MC3_1","MC3_6","MC3_12","HK1_5","ZP2_2","ZP2_5","ZP2_12","ZP3_20","HG1_4","HG1_5","HG1_7","HG1_9","HG1_10","HG1_13","HG2_3","HG2_4","HG2_5","HG2_15","HG2_17","HG2_18","ZC1_2","ZC1_3","ZC1_4","ZC1_5","ZC1_6","ZC2_6","ZC2_8","ZC4_9","ZC6_15","ZC7_6","ZC7_10","ZC8_9","ZC8_13","ZC8_14","ZC8_16","ZC8_17","ZC8_23","ZC11_9","ZC11_12","ML1_2","ML1_5","ML1_6","ML1_7","ML1_9","ML1_10","ML1_12","ML1_13","ML2_1","ML2_2","ML2_3","ML2_4","ML2_8","ML2_9","ML2_13","ML2_15","ML2_16","ML2_17","ML3_1","ML3_5","ML3_6","ML3_7","ML3_8","ML3_9","ML3_10","ML3_11","ML3_12","ML3_13","ML4_2","ML4_3","ML4_5","MT3_7","MT3_9","MT3_11","MT4_19","MT5_11","MT5_12","MT5_13","MT5_14","MT5_16","MT5_18","MT5_20","MT5_22","MT5_28","MT5_32","MT5_34","MT5_37","MT5_39","MT5_44","MT5_45","MT5_46","MT5_47","MT5_48","MT6_1","MT6_2","MT6_5","MT6_8","MT6_9","MT6_14","MT6_15","MT6_16","MT6_19","MT6_20","MT6_25","MT6_26","MT6_27","MT6_29","MT6_30","MT6_32","MT6_33","MT7_2","MT7_6","MT7_7","MT7_9","MT7_11","MT7_12","MT7_15","MT7_23","MT8_10","MT8_11","MT9_4","MT9_11","MT9_29","MT10_9","MT10_13","MT10_14","MT10_15","MT10_16","MT10_17","MT10_19","MT10_20","MT10_23","MT10_27","MT10_29","MT10_30","MT10_31","MT10_40","MT10_42","MT11_9","MT11_11","MT11_17","MT11_21","MT11_22","MT11_24","MT11_28","MT11_29","MT12_6","MT12_11","MT12_27","MT12_28","MT12_31","MT12_36","MT13_11","MT13_16","MT13_17","MT13_18","MT14_16","MT15_3","MT15_5","MT15_6","MT15_7","MT15_16","MT16_11","MT16_15","MT16_28","MT17_12","MT17_17","MT17_20","MT17_24","MT18_3","MT18_10","MT18_12","MT18_13","MT18_14","MT18_18","MT18_19","MT18_35","MT19_8","MT19_9","MT19_23","MT19_24","MT19_28","MT20_4","MT20_7","MT20_26","MT20_27","MT20_32","MT21_2","MT21_3","MT21_13","MT21_21","MT21_24","MT21_27","MT21_28","MT21_31","MT21_32","MT21_43","MT22_31","MT22_42","MT23_3","MT23_8","MT23_9","MT23_10","MT23_11","MT23_13","MT23_15","MT23_16","MT23_23","MT23_25","MT23_27","MT23_28","MT23_29","MT23_31","MT23_32","MT23_34","MT23_35","MT23_36","MT23_38","MT23_39","MT24_2","MT24_4","MT24_9","MT24_20","MT24_23","MT24_25","MT24_26","MT24_33","MT24_34","MT24_42","MT24_44","MT24_47","MT25_8","MT25_9","MT25_12","MT25_34","MT25_40","MT25_45","MT26_13","MT26_15","MT26_21","MT26_29","MT26_31","MT26_32","MT26_64","MT26_66","MT27_17","MT27_21","MT27_24","MT28_5","MT28_7","MT28_14","MT28_20","MK1_8","MK1_17","MK2_8","MK2_18","MK3_28","MK4_11","MK4_24","MK5_19","MK6_11","MK6_31","MK6_37","MK7_6","MK7_9","MK7_11","MK7_13","MK7_18","MK8_12","MK8_17","MK8_29","MK9_1","MK9_13","MK9_19","MK9_41","MK9_50","MK10_3","MK10_5","MK10_15","MK10_29","MK10_36","MK10_43","MK10_44","MK11_2","MK11_3","MK11_17","MK11_23","MK11_24","MK11_25","MK11_29","MK11_33","MK12_43","MK13_5","MK13_9","MK13_11","MK13_21","MK13_23","MK13_29","MK13_30","MK13_36","MK13_37","MK14_7","MK14_9","MK14_13","MK14_15","MK14_18","MK14_25","MK14_28","MK14_49","MK14_64","MK15_9","MK16_7","LK2_10","LK2_11","LK2_12","LK3_7","LK3_8","LK3_13","LK3_14","LK3_16","LK4_21","LK4_24","LK4_25","LK5_4","LK5_22","LK5_33","LK6_9","LK6_20","LK6_22","LK6_23","LK6_24","LK6_25","LK6_26","LK6_27","LK6_28","LK6_30","LK6_31","LK6_32","LK6_33","LK6_34","LK6_35","LK6_36","LK6_38","LK6_47","LK7_9","LK7_26","LK7_28","LK7_32","LK8_10","LK8_25","LK9_5","LK9_13","LK9_20","LK9_27","LK9_41","LK9_44","LK9_48","LK9_50","LK10_3","LK10_6","LK10_8","LK10_9","LK10_10","LK10_11","LK10_12","LK10_13","LK10_14","LK10_16","LK10_19","LK10_20","LK10_24","LK11_5","LK11_8","LK11_9","LK11_11","LK11_13","LK11_19","LK11_20","LK11_39","LK11_41","LK11_42","LK11_43","LK11_44","LK11_46","LK11_47","LK11_48","LK11_51","LK11_52","LK12_1","LK12_4","LK12_5","LK12_7","LK12_8","LK12_11","LK12_12","LK12_14","LK12_22","LK12_24","LK12_25","
15333LK12_27","LK12_28","LK12_29","LK12_30","LK12_31","LK12_32","LK12_33","LK12_34","LK12_35","LK12_36","LK12_37","LK12_40","LK12_44","LK12_51","LK12_57","LK13_3","LK13_5","LK13_15","LK13_24","LK13_25","LK13_27","LK13_28","LK13_35","LK14_5","LK14_24","LK14_28","LK14_33","LK15_4","LK15_7","LK15_10","LK15_31","LK16_9","LK16_11","LK16_12","LK16_15","LK16_26","LK17_3","LK17_6","LK17_7","LK17_10","LK17_14","LK17_21","LK17_23","LK17_34","LK18_8","LK18_14","LK18_17","LK18_29","LK19_26","LK19_31","LK19_40","LK19_46","LK20_3","LK20_8","LK21_3","LK21_12","LK21_13","LK21_14","LK21_15","LK21_16","LK21_18","LK21_19","LK21_28","LK21_30","LK21_31","LK21_32","LK21_34","LK22_10","LK22_12","LK22_15","LK22_16","LK22_17","LK22_18","LK22_19","LK22_20","LK22_26","LK22_27","LK22_28","LK22_29","LK22_31","LK22_35","LK22_37","LK22_53","LK22_67","LK22_70","LK23_14","LK23_28","LK24_6","LK24_38","LK24_44","LK24_48","LK24_49","JN1_26","JN1_51","JN2_5","JN3_7","JN3_12","JN3_28","JN4_20","JN4_22","JN4_32","JN4_35","JN4_38","JN5_19","JN5_20","JN5_24","JN5_25","JN5_33","JN5_34","JN5_35","JN5_38","JN5_39","JN5_42","JN5_44","JN5_45","JN6_26","JN6_27","JN6_32","JN6_36","JN6_47","JN6_49","JN6_53","JN6_61","JN6_63","JN6_64","JN6_65","JN6_67","JN6_70","JN7_6","JN7_7","JN7_8","JN7_19","JN7_22","JN7_28","JN7_33","JN7_34","JN7_36","JN7_47","JN8_7","JN8_14","JN8_15","JN8_17","JN8_21","JN8_22","JN8_23","JN8_24","JN8_25","JN8_26","JN8_31","JN8_32","JN8_34","JN8_36","JN8_37","JN8_38","JN8_40","JN8_41","JN8_42","JN8_44","JN8_46","JN8_47","JN8_49","JN8_51","JN8_54","JN8_55","JN8_56","JN8_58","JN9_19","JN9_27","JN9_30","JN9_41","JN10_1","JN10_7","JN10_25","JN10_26","JN10_32","JN10_34","JN10_36","JN11_15","JN11_49","JN11_50","JN11_56","JN12_8","JN12_24","JN12_30","JN12_35","JN13_10","JN13_12","JN13_13","JN13_14","JN13_15","JN13_16","JN13_18","JN13_19","JN13_20","JN13_21","JN13_33","JN13_34","JN14_1","JN14_2","JN14_3","JN14_9","JN14_10","JN14_12","JN14_16","JN14_17","JN14_18","JN14_19","JN14_20","JN14_25","JN14_26","JN14_27","JN14_28","JN14_29","JN14_30","JN15_3","JN15_4","JN15_5","JN15_7","JN15_9","JN15_11","JN15_12","JN15_14","JN15_15","JN15_16","JN15_17","JN15_18","JN15_19","JN15_20","JN15_21","JN15_26","JN15_27","JN16_1","JN16_2","JN16_4","JN16_5","JN16_6","JN16_7","JN16_12","JN16_13","JN16_14","JN16_15","JN16_20","JN16_22","JN16_23","JN16_24","JN16_25","JN16_26","JN16_27","JN16_33","JN17_10","JN18_8","JN18_31","JN18_39","JN19_4","JN19_6","JN19_14","JN19_15","JN19_35","JN20_17","JN20_19","JN20_21","JN20_26","AC1_5","AC1_7","AC1_8","AC1_11","AC2_14","AC2_15","AC2_17","AC2_22","AC2_29","AC2_33","AC2_36","AC2_38","AC2_39","AC3_13","AC3_14","AC3_16","AC3_17","AC3_19","AC3_20","AC3_22","AC3_25","AC3_26","AC4_7","AC4_10","AC4_11","AC4_19","AC5_9","AC5_28","AC5_30","AC5_38","AC6_3","AC7_4","AC7_37","AC7_43","AC7_51","AC7_52","AC8_24","AC10_28","AC10_37","AC11_16","AC13_15","AC13_26","AC13_32","AC13_34","AC13_38","AC13_41","AC13_46","AC14_15","AC14_17","AC15_7","AC15_24","AC15_25","AC15_28","AC15_29","AC16_17","AC17_3","AC17_22","AC17_23","AC17_28","AC18_6","AC18_14","AC18_15","AC18_21","AC19_13","AC19_15","AC19_36","AC20_18","AC20_20","AC20_25","AC20_26","AC20_27","AC20_28","AC20_29","AC20_30","AC20_32","AC20_35","AC22_1","AC22_3","AC22_25","AC23_15","AC24_21","AC24_22","AC25_5","AC25_26","AC26_8","AC27_22","AC27_31","AC27_34","AC28_20","AC28_25","AC28_28","RM1_6","RM1_7","RM1_8","RM1_9","RM1_10","RM1_11","RM1_12","RM1_13","RM1_15","RM2_24","RM6_11","RM6_12","RM6_13","RM6_14","RM6_16","RM6_19","RM6_22","RM7_4","RM8_9","RM8_10","RM8_11","RM9_26","RM10_19","RM11_13","RM11_25","RM11_28","RM11_30","RM11_31","RM12_1","RM12_2","RM12_3","RM12_16","RM12_18","RM12_19","RM13_11","RM14_16","RM15_5","RM15_7","RM15_13","RM15_14","RM15_15","RM15_22","RM15_23","RM15_24","RM15_28","RM15_29","RM15_30","RM15_32","RM15_33","RM16_1","RM16_2","RM16_6","RM16_16","RM16_17","RM16_19","RM16_20","RM16_21","RM16_22","RM16_23","RM16_25","C11_3","C11_4","C11_6","C11_7","C11_8","C11_10","C11_11","C11_12","C11_13","C11_14","C11_26","C11_30","C12_1","C12_2","C12_3","C12_5","C13_1","C13_2","C13_3","C13_16","C13_17","C13_18","C13_21","C13_22","C13_23","C14_3","C14_6","C14_8","C14_10","C14_14","C14_15","C14_16","C14_17","C14_18","C14_19","C14_21","C15_1","C15_2","C15_4","C15_6","C15_9","C15_11","C15_12","C15_13","C16_1","C16_2","C16_5","C16_7","C16_8","C16_15","C16_19","C16_20","C17_5","C17_14","C17_28","C17_32","C17_35","C18_9","C19_1","C19_2","C19_11","C19_12","C110_1","C110_13","C110_15","C110_20","C110_27","C110_28","C111_2","C111_3","C111_13","C111_14","C111_18","C111_19","C111_20","C111_22","C111_23","C111_24","C111_30","C112_1","C112_3","C112_21","C112_27","C112_31","C114_5","C114_6","C114_9","C114_12","C114_18","C114_25","C114_36","C114_37","C115_1","C115_2","C115_3","C115_12","C115_14","C115_17","C115_31","C115_34","C115_51","C115_58","C116_1","C116_2","C116_3","C116_5","C116_6","C116_7","C116_10","C116_12","C116_14","C116_15","C116_16","C116_17","C116_18","C116_19","C116_20","C116_23","C116_24","C21_2","C21_6","C21_7","C21_8","C21_11","C21_12","C21_13","C21_14","C21_15","C21_16","C21_18","C21_19","C21_21","C21_23","C21_24","C22_1","C22_2","C22_3","C22_4","C22_5","C22_7","C22_8","C22_9","C22_10","C23_1","C23_2","C24_5","C24_12","C24_14","C24_15","C25_11","C25_12","C25_13","C26_1","C26_11","C26_12","C26_13","C26_17","C26_18","C27_4","C27_7","C27_8","C27_11","C27_12","C27_13","C27_14","C27_15","C27_16","C28_1","C28_6","C28_7","C28_8","C28_9","C28_10","C28_13","C28_14","C28_16","C28_17","C28_22","C28_23","C28_24","C29_1","C29_2","C29_3","C29_4","C29_5","C29_8","C29_10","C29_13","C29_14","C210_1","C210_6","C210_8","C210_9","C210_13","C210_14","C210_15","C210_16","C211_2","C211_3","C211_6","C211_7","C211_8","C211_9","C211_11","C211_20","C212_11","C212_12","C212_13","C212_14","C212_15","C212_16","C212_17","C212_18","C212_19","C212_20","C212_21","C213_
153331","C213_3","C213_4","C213_5","C213_7","C213_9","C213_11","C213_13","C213_14","GL1_3","GL1_6","GL1_7","GL1_8","GL1_9","GL1_11","GL1_20","GL2_5","GL3_1","GL3_2","GL3_5","GL3_28","GL3_29","GL4_11","GL4_12","GL4_13","GL4_14","GL4_15","GL4_16","GL4_17","GL4_18","GL4_19","GL4_20","GL4_28","GL5_2","GL5_7","GL5_8","GL5_10","GL5_12","GL5_13","GL5_21","GL6_1","GL6_11","GL6_12","GL6_13","GL6_18","EP1_2","EP1_13","EP1_15","EP1_16","EP1_17","EP1_18","EP2_1","EP2_8","EP2_11","EP2_13","EP2_17","EP2_22","EP3_1","EP3_2","EP3_13","EP3_16","EP3_17","EP4_1","EP4_4","EP4_17","EP4_20","EP4_22","EP4_23","EP4_26","EP4_29","EP4_31","EP4_32","EP5_3","EP5_6","EP5_19","EP5_33","EP6_1","EP6_4","EP6_5","EP6_9","EP6_11","EP6_14","EP6_21","EP6_22","PP1_2","PP1_3","PP1_4","PP1_5","PP1_6","PP1_7","PP1_8","PP1_9","PP1_10","PP1_12","PP1_19","PP1_24","PP1_25","PP1_26","PP1_27","PP1_28","PP1_29","PP2_3","PP2_4","PP2_5","PP2_12","PP2_13","PP2_17","PP2_18","PP2_19","PP2_20","PP2_25","PP2_26","PP2_30","PP3_1","PP3_15","PP3_18","PP4_5","PP4_6","PP4_7","PP4_9","PP4_15","PP4_17","PP4_18","PP4_19","PP4_21","PP4_22","PP4_23","CL1_2","CL1_3","CL1_4","CL1_5","CL1_6","CL1_8","CL1_9","CL1_12","CL1_21","CL1_22","CL1_24","CL1_25","CL1_27","CL2_1","CL2_4","CL2_5","CL2_8","CL2_13","CL2_16","CL2_18","CL3_3","CL3_4","CL3_7","CL3_8","CL3_13","CL3_15","CL3_16","CL3_21","CL4_1","CL4_6","CL4_7","CL4_8","CL4_9","CL4_10","CL4_12","CL4_13","CL4_14","CL4_16","CL4_18","H11_1","H11_2","H11_3","H11_4","H11_5","H11_6","H11_7","H11_8","H11_9","H12_1","H12_2","H12_6","H12_7","H12_8","H12_9","H12_10","H12_12","H12_13","H12_14","H12_17","H12_18","H12_19","H12_20","H13_2","H13_4","H13_5","H13_6","H13_7","H13_8","H13_9","H13_10","H13_11","H13_12","H13_13","H14_1","H14_2","H14_3","H14_4","H14_6","H14_8","H14_9","H14_10","H14_11","H14_13","H14_15","H15_1","H15_4","H15_5","H15_12","H15_13","H15_14","H15_18","H15_23","H15_24","H15_27","H15_28","H21_2","H21_3","H21_4","H21_5","H21_6","H21_7","H21_10","H21_11","H21_12","H22_1","H22_2","H22_3","H22_5","H22_13","H22_14","H22_17","H23_1","H23_3","H23_4","H23_5","H23_6","H23_7","H23_8","H23_9","H23_10","H23_11","H23_13","H23_16","H23_18","T16_21","T24_22","TT3_15","PM1","PM1","PM1","HB3_8","HB3_9","HB3_12","HB3_13","HB3_15","HB4_1","HB4_7","HB5_12","HB6_9","HB6_10","HB6_11","HB9_20","HB10_34","HB10_35","HB12_3","HB12_5","HB12_7","HB12_13","HB13_7","HB13_17","HB13_19","HB13_21","HB13_22","HB13_23","HB13_24","HB13_25","JM1_3","JM1_5","JM1_21","JM1_22","JM2_2","JM2_4","JM2_6","JM2_7","JM2_16","JM3_13","JM3_14","JM4_1","JM4_2","JM4_3","JM4_7","JM4_8","JM4_9","JM4_10","JM4_14","JM4_15","JM4_16","JM5_1","JM5_2","JM5_3","JM5_4","JM5_5","JM5_6","JM5_8","JM5_12","JM5_13","JM5_14","JM5_19","P11_2","P11_4","P11_7","P11_9","P11_10","P11_12","P11_13","P11_14","P11_15","P11_17","P11_18","P11_20","P11_21","P11_22","P11_25","P12_7","P12_9","P12_12","P12_21","P12_25","P13_2","P13_7","P13_13","P13_15","P13_16","P13_18","P13_21","P14_1","P14_4","P14_8","P14_12","P14_14","P14_15","P15_1","P15_2","P15_6","P15_7","P15_8","P15_9","P15_10","P15_12","P15_13","P15_14","P21_2","P21_5","P21_8","P21_10","P21_11","P21_12","P21_13","P21_15","P21_16","P21_19","P22_1","P22_3","P22_13","P23_1","P23_2","P23_8","P23_9","P23_11","P23_15","P23_17","J11_2","J11_3","J11_5","J12_1","J12_7","J12_8","J12_12","J12_13","J12_14","J12_20","J12_21","J12_24","J12_26","J12_27","J13_7","J13_13","J14_4","J15_13","J15_21","J21","J21","JD1","JD1","JD1","JD1","JD1","JD1","JD1","JD1","JD1","RV1_4","RV1_9","RV2_10","RV2_13","RV2_23","RV2_24","RV12_12","RV18_20","RV22_16"], 15334"verbs":["GN1_22","GN1_28","GN3_1","GN3_3","GN3_4","GN3_5","GN4_23","GN9_1","GN9_4","GN9_7","GN17_10","GN17_11","GN18_4","GN18_5","GN19_2","GN19_7","GN19_8","GN19_14","GN22_5","GN23_4","GN23_8","GN24_49","GN24_54","GN24_56","GN26_27","GN29_5","GN29_7","GN31_6","GN31_46","GN32_4","GN32_16","GN32_19","GN32_20","GN34_8","GN34_9","GN34_10","GN34_11","GN34_12","GN34_15","GN34_17","GN34_30","GN35_2","GN37_6","GN37_20","GN37_22","GN37_27","GN38_24","GN39_14","GN40_8","GN41_55","GN42_1","GN42_2","GN42_7","GN42_9","GN42_12","GN42_15","GN42_16","GN42_18","GN42_19","GN42_20","GN42_22","GN42_33","GN42_34","GN42_36","GN42_38","GN43_2","GN43_3","GN43_5","GN43_6","GN43_7","GN43_11","GN43_12","GN43_13","GN43_23","GN43_27","GN43_29","GN43_31","GN44_4","GN44_5","GN44_10","GN44_15","GN44_17","GN44_21","GN44_23","GN44_25","GN44_27","GN44_29","GN45_1","GN45_4","GN45_5","GN45_8","GN45_9","GN45_13","GN45_17","GN45_18","GN45_19","GN45_24","GN46_34","GN47_16","GN47_23","GN47_24","GN49_1","GN49_2","GN49_29","GN50_4","GN50_17","GN50_19","GN50_20","GN50_21","GN50_25","EX1_16","EX1_18","EX1_22","EX2_18","EX2_20","EX3_12","EX3_18","EX3_21","EX3_22","EX4_15","EX5_4","EX5_5","EX5_7","EX5_8","EX5_11","EX5_13","EX5_14","EX5_16","EX5_18","
15334EX5_19","EX5_21","EX6_7","EX6_26","EX7_9","EX8_8","EX8_25","EX8_28","EX9_8","EX9_28","EX9_30","EX10_2","EX10_8","EX10_10","EX10_11","EX10_17","EX10_24","EX11_7","EX12_3","EX12_4","EX12_5","EX12_9","EX12_10","EX12_11","EX12_14","EX12_15","EX12_17","EX12_18","EX12_20","EX12_21","EX12_22","EX12_24","EX12_25","EX12_27","EX12_31","EX12_32","EX12_46","EX13_3","EX13_19","EX14_2","EX14_13","EX14_14","EX15_21","EX16_3","EX16_6","EX16_7","EX16_9","EX16_12","EX16_16","EX16_23","EX16_25","EX16_26","EX16_28","EX16_29","EX17_2","EX19_4","EX19_5","EX19_6","EX19_12","EX19_15","EX20_20","EX20_22","EX20_23","EX22_21","EX22_22","EX22_25","EX22_31","EX23_9","EX23_13","EX23_25","EX24_1","EX24_14","EX25_2","EX25_3","EX25_9","EX25_19","EX30_9","EX30_32","EX30_37","EX31_13","EX31_14","EX32_2","EX32_24","EX32_27","EX32_29","EX32_30","EX34_13","EX35_3","EX35_5","EX35_30","LV1_2","LV2_11","LV2_12","LV3_17","LV7_23","LV7_24","LV7_26","LV7_32","LV8_31","LV8_32","LV8_33","LV8_35","LV9_3","LV9_6","LV10_4","LV10_6","LV10_7","LV10_9","LV10_12","LV10_13","LV10_14","LV10_17","LV10_18","LV11_2","LV11_3","LV11_4","LV11_8","LV11_9","LV11_11","LV11_13","LV11_21","LV11_22","LV11_24","LV11_33","LV11_42","LV11_43","LV11_44","LV11_45","LV14_34","LV15_2","LV15_31","LV16_29","LV16_30","LV16_31","LV17_14","LV18_3","LV18_4","LV18_5","LV18_6","LV18_24","LV18_26","LV18_30","LV19_2","LV19_3","LV19_4","LV19_5","LV19_11","LV19_12","LV19_15","LV19_19","LV19_23","LV19_25","LV19_26","LV19_27","LV19_28","LV19_30","LV19_31","LV19_33","LV19_34","LV19_35","LV19_37","LV20_7","LV20_8","LV20_15","LV20_22","LV20_23","LV20_24","LV20_25","LV20_26","LV22_20","LV22_22","LV22_24","LV22_25","LV22_28","LV22_29","LV22_30","LV22_31","LV22_32","LV23_2","LV23_3","LV23_4","LV23_6","LV23_7","LV23_8","LV23_10","LV23_12","LV23_14","LV23_15","LV23_16","LV23_17","LV23_18","LV23_19","LV23_21","LV23_25","LV23_27","LV23_28","LV23_31","LV23_32","LV23_35","LV23_36","LV23_37","LV23_38","LV23_39","LV23_40","LV23_41","LV23_42","LV25_2","LV25_9","LV25_10","LV25_11","LV25_12","LV25_13","LV25_14","LV25_17","LV25_18","LV25_19","LV25_20","LV25_22","LV25_24","LV25_44","LV25_45","LV25_46","LV26_1","LV26_2","LV26_3","LV26_5","LV26_6","LV26_7","LV26_10","LV26_12","LV26_14","LV26_15","LV26_16","LV26_17","LV26_18","LV26_21","LV26_23","LV26_25","LV26_26","LV26_27","LV26_29","LV26_38","NU1_2","NU1_3","NU4_18","NU4_19","NU4_27","NU4_32","NU5_3","NU6_23","NU9_3","NU9_8","NU10_5","NU10_6","NU10_7","NU10_9","NU10_10","NU11_18","NU11_19","NU11_20","NU12_4","NU12_6","NU12_8","NU13_2","NU13_17","NU13_18","NU13_20","NU14_9","NU14_25","NU14_28","NU14_29","NU14_30","NU14_31","NU14_34","NU14_42","NU14_43","NU15_2","NU15_3","NU15_12","NU15_14","NU15_19","NU15_20","NU15_21","NU15_22","NU15_39","NU15_40","NU16_3","NU16_6","NU16_7","NU16_8","NU16_10","NU16_11","NU16_16","NU16_17","NU16_21","NU16_24","NU16_26","NU16_28","NU16_30","NU16_38","NU16_41","NU16_45","NU18_1","NU18_5","NU18_7","NU18_26","NU18_28","NU18_29","NU18_31","NU18_32","NU19_3","NU20_4","NU20_5","NU20_8","NU20_10","NU20_12","NU20_24","NU21_5","NU21_17","NU21_27","NU22_8","NU22_13","NU22_19","NU25_5","NU25_17","NU26_2","NU27_8","NU27_9","NU27_10","NU27_11","NU27_14","NU28_2","NU28_3","NU28_11","NU28_18","NU28_19","NU28_20","NU28_23","NU28_24","NU28_25","NU28_26","NU28_27","NU28_31","NU29_1","NU29_2","NU29_7","NU29_8","NU29_12","NU29_13","NU29_35","NU29_36","NU29_39","NU31_3","NU31_4","NU31_15","NU31_17","NU31_18","NU31_19","NU31_20","NU31_23","NU31_24","NU31_29","NU32_6","NU32_7","NU32_14","NU32_15","NU32_20","NU32_22","NU32_23","NU32_24","NU32_29","NU33_52","NU33_53","NU33_54","NU33_55","NU34_7","NU34_8","NU34_10","NU34_13","NU34_18","NU35_2","NU35_4","NU35_5","NU35_6","NU35_7","NU35_8","NU35_11","NU35_13","NU35_14","NU35_31","NU35_32","NU35_33","DT1_7","DT1_8","DT1_13","DT1_14","DT1_16","DT1_17","DT1_18","DT1_19","DT1_20","DT1_22","DT1_26","DT1_27","DT1_29","DT1_31","DT1_33","DT1_39","DT1_40","DT1_41","DT1_42","DT1_43","DT1_45","DT1_46","DT2_3","DT2_4","DT2_5","DT2_6","DT2_13","DT2_24","DT3_18","DT3_20","DT3_22","DT4_1","DT4_2","DT4_6","DT4_11","DT4_15","DT4_16","DT4_22","DT4_23","DT4_25","DT4_26","DT4_27","DT4_28","DT4_29","DT5_1","DT5_5","DT5_23","DT5_24","DT5_30","DT5_32","DT5_33","DT6_3","DT6_14","DT6_16","DT6_17","DT7_5","DT7_12","DT7_25","DT8_1","DT8_19","DT8_20","DT9_7","DT9_8","DT9_16","DT9_18","DT9_21","DT9_22","DT9_23","DT9_24","DT10_16","DT10_19","DT11_2","DT11_8","DT11_9","DT11_10","DT11_13","DT11_16","DT11_17","DT11_18","DT11_19","DT11_22","DT11_23","DT11_25","DT11_27","DT11_28","DT11_31","DT11_32","DT12_1","DT12_2","DT12_3","DT12_4","DT12_5","DT12_6","DT12_7","DT12_8","DT12_9","DT12_10","DT12_11","DT12_12","DT12_16","DT12_32","DT13_4","DT13_13","DT14_1","DT14_4","DT14_6","DT14_7","DT14_8","DT14_9","DT14_10","DT14_11","DT14_12","DT14_20","DT14_21","DT17_16","DT18_15","DT19_19","DT20_3","DT20_18","DT22_24","DT24_8","DT27_2","DT27_4","DT28_62","DT28_63","DT28_68","DT29_2","DT29_6","DT29_7","DT29_9","DT29_16","DT29_17","DT30_18","DT31_5","DT31_6","DT31_14","DT31_19","DT31_26","DT31_27","DT31_28","DT31_29","DT32_1","DT32_3","DT32_6","DT32_7","DT32_39","DT32_43","DT32_46","DT32_47","DT32_51","JS1_11","JS1_14","JS1_15","JS2_1","JS2_5","JS2_10","JS2_12","JS2_13","JS2_14","JS2_16","JS3_3","JS3_4","JS3_5","JS3_6","JS3_8","JS3_9","JS3_10","JS3_12","JS4_2","JS4_3","JS4_5","JS4_7","JS4_17","JS4_22","JS4_24","JS6_3","JS6_4","JS6_6","JS6_7","JS6_10","JS6_16","JS6_18","JS6_22","JS7_2","JS7_12","JS7_13","JS7_14","JS8_2","JS8_4","JS8_7","JS8_8","JS9_6","JS9_8","JS9_11","JS9_22","JS10_4","JS10_18","JS10_19","JS10_22","JS10_24","JS10_25","JS18_4","JS18_6","JS18_8","JS20_2","JS22_2","JS22_3","JS22_4","JS22_5","JS22_8","JS22_16","JS22_18","JS22_19","JS22_28","JS22_31","JS23_3","JS23_4","JS23_5","JS23_6","JS23_7","JS23_8","JS23_11","JS23_12","JS23_13","JS23_14","JS23_16","JS24_6","JS24_7","JS24_8","JS24_11","JS24_13","JS24_14","JS24_15","JS24_19","JS24_20","JS24_22","JS24_23","JS24_27","JG2_2","JG3_28","JG5_2","JG5_3","JG5_9","JG5_10","JG5_23","JG6_10","JG6_31","JG7_15","JG7_17","JG7_18","JG7_24","JG8_5","JG8_15","JG8_18","JG8_19","JG8_24","JG9_2","JG9_7","JG9_15","JG9_16","JG9_18","JG9_19","JG9_28","JG9_48","JG10_12","JG10_13","JG10_14","JG11_7","JG11_26","JG12_2","JG12_3","JG14_2","JG14_12","JG14_13","JG14_15","JG14_18","JG15_7","JG15_10","JG15_12","JG16_18","JG16_25","JG18_2","JG18_6","JG18_9","JG18_10","JG18_14","JG18_24","JG19_5","JG19_9","JG19_23","JG19_24","JG19_30","JG20_3","JG20_7","JG20_13","JG20_23","JG20_28","JG21_10","JG21_11","JG21_20","JG21_21","JG21_22","RT1_8","RT1_9","RT1_11","RT1_12","RT1_13","RT1_20","RT1_21","RT2_15","RT2_16","RT4_2","S12_3","S12_23","S12_29","S14_9","S15_11","S16_2","S16_3","S16_5","S16_6","S16_7","S16_8","S16_9","S16_21","S17_3","S17_5","S18_17","S18_18","S18_22","S19_9","S19_13","S19_19","S110_14","S110_19","S110_24","S111_9","S111_10","S111_12","S111_14","S112_1","S112_3","S112_5","S112_7","S112_11","S112_12","S112_13","S112_14","S112_15","S112_16","S112_17","S112_20","S112_21","S112_24","S112_25","S113_9","S114_9","S114_10","S114_12","S114_17","S114_27","S114_29","S114_33","S114_34","S114_38","S114_40","S114_42","S115_3","S115_6","S115_32","S116_5","S116_17","S117_8","S117_9","S117_10","S117_25","S118_22","S118_25","S119_15","S121_14","S121_15","S122_7","S122_8","S122_13","S122_17","S123_21","S123_22","S123_23","S125_5","S125_6","S125_13","S125_19","S126_16","S127_10","S128_7","S129_10","S130_23","S21_20","S21_24","S22_5","S22_6","S22_7","S23_17","S23_18","S23_31","S23_38","S27_7","S210_5","S211_15","S211_20","S211_21","S213_9","S213_17","S213_28","S214_30","S215_10","S215_14","S215_28","S215_36","S216_11","S216_20","S217_16","S217_21","S218_12","S219_11","S219_12","S219_13","S2
1533419_22","S219_29","S220_16","S220_21","S221_3","S224_2","K11_28","K11_32","K11_33","K11_34","K11_35","K11_45","K13_24","K13_25","K13_26","K13_27","K19_6","K111_2","K112_5","K112_12","K112_24","K113_4","K113_13","K113_27","K113_31","K118_21","K118_24","K118_25","K118_27","K118_30","K118_33","K118_34","K118_40","K120_7","K120_9","K120_11","K120_12","K120_18","K120_28","K120_33","K121_9","K121_10","K122_3","K122_27","K122_28","K122_31","K21_2","K21_5","K21_6","K22_3","K22_5","K22_16","K22_17","K22_18","K22_20","K23_15","K23_17","K23_19","K24_41","K25_7","K26_2","K26_11","K26_13","K26_19","K26_32","K27_1","K27_4","K27_9","K27_14","K29_11","K29_27","K29_33","K29_34","K210_3","K210_6","K210_8","K210_10","K210_14","K210_19","K210_20","K210_23","K210_25","K211_5","K211_6","K211_8","K211_15","K212_7","K217_12","K217_13","K217_27","K217_35","K217_36","K217_37","K217_38","K217_39","K218_19","K218_22","K218_28","K218_31","K218_32","K218_36","K219_6","K219_10","K219_29","K220_7","K222_13","K222_15","K222_18","K223_18","K223_21","K225_24","R112_17","R115_12","R116_8","R116_9","R116_10","R116_11","R116_12","R116_15","R116_22","R116_23","R116_24","R116_28","R116_29","R116_30","R116_34","R116_35","R117_6","R119_5","R121_2","R122_19","R128_2","R128_8","R129_20","R27_19","R210_5","R210_12","R211_4","R212_5","R213_4","R213_9","R213_11","R213_12","R215_2","R215_7","R218_14","R218_18","R218_25","R218_26","R218_27","R218_30","R219_6","R219_7","R219_9","R219_10","R219_11","R220_15","R220_16","R220_17","R220_20","R220_21","R223_4","R223_7","R223_14","R224_5","R224_20","R228_9","R228_11","R228_13","R229_5","R229_11","R229_31","R230_6","R230_7","R230_8","R230_9","R232_7","R232_12","R232_13","R232_15","R234_21","R234_23","R234_26","R235_3","R235_4","R235_5","R235_6","R235_23","ER8_29","ER9_12","ER10_10","ER10_11","NH1_8","NH1_9","NH2_17","NH4_12","NH4_14","NH4_20","NH5_8","NH5_9","NH5_11","NH7_3","NH8_9","NH8_10","NH8_11","NH8_15","NH9_5","NH13_21","NH13_25","ET4_14","ET4_16","ET5_5","ET7_9","ET8_8","JB6_21","JB6_22","JB6_23","JB6_24","JB6_26","JB6_27","JB6_28","JB6_29","JB13_5","JB13_6","JB13_7","JB13_8","JB13_9","JB13_10","JB13_13","JB13_17","JB17_10","JB18_2","JB19_2","JB19_3","JB19_5","JB19_6","JB19_15","JB19_21","JB19_22","JB19_28","JB19_29","JB21_2","JB21_3","JB21_5","JB21_27","JB21_28","JB21_29","JB21_34","JB27_12","JB32_11","JB32_13","JB34_2","JB34_10","JB37_2","JB42_7","JB42_8","PS2_10","PS2_11","PS2_12","PS4_2","PS4_3","PS4_4","PS4_5","PS6_8","PS9_11","PS11_1","PS14_6","PS22_23","PS24_7","PS24_9","PS27_8","PS29_1","PS29_2","PS30_4","PS31_23","PS31_24","PS32_9","PS32_11","PS33_1","PS33_2","PS33_3","PS34_3","PS34_8","PS34_9","PS34_11","PS46_8","PS46_10","PS47_1","PS47_6","PS47_7","PS48_12","PS48_13","PS49_1","PS50_5","PS50_22","PS58_1","PS58_2","PS62_3","PS62_8","PS62_10","PS66_1","PS66_2","PS66_3","PS66_5","PS66_8","PS66_16","PS68_4","PS68_13","PS68_16","PS68_26","PS68_32","PS68_34","PS71_11","PS75_4","PS75_5","PS76_11","PS78_1","PS81_1","PS81_2","PS81_3","PS82_2","PS82_3","PS82_4","PS82_7","PS83_4","PS90_3","PS94_8","PS95_1","PS95_6","PS95_7","PS95_8","PS96_1","PS96_2","PS96_3","PS96_7","PS96_8","PS96_9","PS96_10","PS97_7","PS97_10","PS97_12","PS98_1","PS98_4","PS98_5","PS98_6","PS99_5","PS99_9","PS100_1","PS100_2","PS100_3","PS100_4","PS103_20","PS103_21","PS103_22","PS105_1","PS105_2","PS105_3","PS105_4","PS105_5","PS105_15","PS106_1","PS107_1","PS113_1","PS114_6","PS115_10","PS115_11","PS117_1","PS118_1","PS118_19","PS118_27","PS118_29","PS119_115","PS122_6","PS134_1","PS134_2","PS135_1","PS135_3","PS135_19","PS135_20","PS136_1","PS136_2","PS136_3","PS136_26","PS137_3","PS137_7","PS139_19","PS146_3","PS147_7","PS148_1","PS148_2","PS148_3","PS148_4","PS148_7","PS149_1","PS150_1","PS150_2","PS150_3","PS150_4","PS150_5","PR1_22","PR1_23","PR1_24","PR1_25","PR4_1","PR4_2","PR5_7","PR7_24","PR8_5","PR8_6","PR8_10","PR8_32","PR8_33","PR9_5","PR9_6","PR31_6","PR31_31","SS1_6","SS2_5","SS2_7","SS2_15","SS3_3","SS3_5","SS3_11","SS5_1","SS5_8","SS6_13","SS8_4","IS1_2","IS1_5","IS1_10","IS1_12","IS1_13","IS1_15","IS1_16","IS1_17","IS1_18","IS1_19","IS1_20","IS1_29","IS1_30","IS2_3","IS2_5","IS2_22","IS3_7","IS3_10","IS3_14","IS3_15","IS5_3","IS5_8","IS6_9","IS7_9","IS7_13","IS8_9","IS8_10","IS8_12","IS8_13","IS8_19","IS10_3","IS12_3","IS12_4","IS12_5","IS13_2","IS13_6","IS14_21","IS16_1","IS16_3","IS16_7","IS18_2","IS18_3","IS19_11","IS21_5","IS21_12","IS21_13","IS21_14","IS22_4","IS22_9","IS22_
1533410","IS22_11","IS22_14","IS23_1","IS23_2","IS23_6","IS23_13","IS23_14","IS24_15","IS26_2","IS26_4","IS26_19","IS27_2","IS27_12","IS28_12","IS28_14","IS28_15","IS28_18","IS28_22","IS28_23","IS29_1","IS29_9","IS30_10","IS30_11","IS30_12","IS30_15","IS30_16","IS30_17","IS30_21","IS30_22","IS31_6","IS32_9","IS32_10","IS32_11","IS33_11","IS33_13","IS34_1","IS34_16","IS35_3","IS35_4","IS36_4","IS36_7","IS36_13","IS36_16","IS36_21","IS37_6","IS37_10","IS37_30","IS40_1","IS40_2","IS40_3","IS40_18","IS40_21","IS40_25","IS40_26","IS41_1","IS41_21","IS41_22","IS41_23","IS42_10","IS42_18","IS43_10","IS43_18","IS43_19","IS44_8","IS44_23","IS45_8","IS45_11","IS45_17","IS45_19","IS45_20","IS45_21","IS45_22","IS46_3","IS46_5","IS46_8","IS46_9","IS46_12","IS48_1","IS48_6","IS48_14","IS48_16","IS48_20","IS49_1","IS49_9","IS49_13","IS50_1","IS50_11","IS51_1","IS51_2","IS51_4","IS51_6","IS51_7","IS52_2","IS52_3","IS52_9","IS52_11","IS52_12","IS55_1","IS55_2","IS55_3","IS55_6","IS55_12","IS56_1","IS56_9","IS56_12","IS57_3","IS57_4","IS57_14","IS58_3","IS58_4","IS58_6","IS61_6","IS62_7","IS62_10","IS62_11","IS65_12","IS65_13","IS65_14","IS65_15","IS65_18","IS66_1","IS66_5","IS66_10","IS66_11","IS66_12","IS66_13","IS66_14","JR2_4","JR2_7","JR2_10","JR2_12","JR2_19","JR2_29","JR2_31","JR3_13","JR3_14","JR3_16","JR3_19","JR3_20","JR3_22","JR4_3","JR4_4","JR4_5","JR4_6","JR4_8","JR4_16","JR5_1","JR5_10","JR5_19","JR5_20","JR5_21","JR5_22","JR5_31","JR6_1","JR6_4","JR6_5","JR6_6","JR6_16","JR6_17","JR6_18","JR7_2","JR7_3","JR7_4","JR7_5","JR7_6","JR7_9","JR7_10","JR7_12","JR7_13","JR7_21","JR7_23","JR8_8","JR8_14","JR9_4","JR9_17","JR9_20","JR10_1","JR10_2","JR10_5","JR11_2","JR11_4","JR11_6","JR11_7","JR11_13","JR12_9","JR12_13","JR13_15","JR13_16","JR13_17","JR13_18","JR13_23","JR14_13","JR16_12","JR16_13","JR17_4","JR17_20","JR17_21","JR17_22","JR17_24","JR17_27","JR18_11","JR18_13","JR18_18","JR19_3","JR20_6","JR20_10","JR20_13","JR21_3","JR21_11","JR21_12","JR22_3","JR22_4","JR22_5","JR22_10","JR22_26","JR22_30","JR23_2","JR23_16","JR23_20","JR23_35","JR23_36","JR23_38","JR25_3","JR25_4","JR25_5","JR25_6","JR25_7","JR25_8","JR25_27","JR25_28","JR25_29","JR25_34","JR26_4","JR26_5","JR26_11","JR26_12","JR26_13","JR26_14","JR26_15","JR27_4","JR27_9","JR27_10","JR27_12","JR27_13","JR27_14","JR27_15","JR27_16","JR27_17","JR29_5","JR29_6","JR29_7","JR29_8","JR29_12","JR29_13","JR29_15","JR29_19","JR29_20","JR29_28","JR30_6","JR30_22","JR30_24","JR31_6","JR31_7","JR31_10","JR31_34","JR32_5","JR33_11","JR33_20","JR34_14","JR34_15","JR34_16","JR34_17","JR35_5","JR35_6","JR35_7","JR35_11","JR35_13","JR35_14","JR35_15","JR35_18","JR37_7","JR37_9","JR37_10","JR37_18","JR40_3","JR40_9","JR40_10","JR41_6","JR42_9","JR42_10","JR42_11","JR42_15","JR42_16","JR42_18","JR42_19","JR42_20","JR42_21","JR42_22","JR43_2","JR44_2","JR44_4","JR44_9","JR44_21","JR44_22","JR44_23","JR44_24","JR44_25","JR44_26","JR44_29","JR46_3","JR46_4","JR46_9","JR46_14","JR48_2","JR48_6","JR48_9","JR48_14","JR48_17","JR48_20","JR48_26","JR48_28","JR49_3","JR49_5","JR49_8","JR49_11","JR49_14","JR49_20","JR49_28","JR49_30","JR49_31","JR50_2","JR50_5","JR50_8","JR50_14","JR50_15","JR50_16","JR50_26","JR50_27","JR50_29","JR50_45","JR51_3","JR51_6","JR51_8","JR51_9","JR51_10","JR51_11","JR51_12","JR51_27","JR51_28","JR51_45","JR51_46","JR51_50","LM1_12","LM1_18","LM4_15","EK5_7","EK6_3","EK6_6","EK6_7","EK6_13","EK7_4","EK7_9","EK9_1","EK9_5","EK9_6","EK9_7","EK11_5","EK11_6","EK11_7","EK11_8","EK11_10","EK11_11","EK11_12","EK11_15","EK11_17","EK12_20","EK13_2","EK13_5","EK13_7","EK13_8","EK13_9","EK13_11","EK13_12","EK13_14","EK13_18","EK13_19","EK13_21","EK13_23","EK14_6","EK14_8","EK14_22","EK14_23","EK15_7","EK17_12","EK17_21","EK18_19","EK18_25","EK18_30","EK18_31","EK18_32","EK20_7","EK20_18","EK20_19","EK20_20","EK20_34","EK20_38","EK20_39","EK20_41","EK20_42","EK20_43","EK20_44","EK21_24","EK22_21","EK22_22","EK23_49","EK24_21","EK24_22","EK24_23","EK24_24","EK25_3","EK25_5","EK30_2","EK32_20","EK33_10","EK33_11","EK33_20","EK33_25","EK33_26","EK33_30","EK34_3","EK34_4","EK34_7","EK34_9","EK34_18","EK34_21","EK35_9","EK35_13","EK36_1","EK36_3","EK36_4","
15334EK36_6","EK36_8","EK36_9","EK36_11","EK36_22","EK36_23","EK36_25","EK36_27","EK36_28","EK36_30","EK36_31","EK36_32","EK37_4","EK37_5","EK37_6","EK37_7","EK37_13","EK37_14","EK39_17","EK39_18","EK39_19","EK39_20","EK44_8","EK44_28","EK44_30","EK45_1","EK45_6","EK45_9","EK45_13","EK45_20","EK47_13","EK47_14","EK47_18","EK47_21","EK47_22","EK47_23","EK48_8","EK48_9","EK48_20","EK48_29","DN1_10","HS2_1","HS2_2","HS4_1","HS4_15","HS4_18","HS5_1","HS5_8","HS6_1","HS9_5","HS10_8","HS10_12","HS10_13","HS14_2","JL1_2","JL1_3","JL1_5","JL1_13","JL1_14","JL2_1","JL2_12","JL2_13","JL2_15","JL2_16","JL2_19","JL2_22","JL2_23","JL2_26","JL2_27","JL3_5","JL3_6","JL3_7","JL3_9","JL3_10","JL3_11","JL3_13","JL3_17","AM2_12","AM3_1","AM3_9","AM3_13","AM4_1","AM4_3","AM4_4","AM4_5","AM4_6","AM4_8","AM4_9","AM4_10","AM4_11","AM5_1","AM5_4","AM5_5","AM5_6","AM5_11","AM5_14","AM5_15","AM5_22","AM5_25","AM5_26","AM6_2","AM6_3","AM6_12","AM8_4","OB1","OB1","OB1","JH1_7","JH1_12","MC1_2","MC1_10","MC2_3","MC2_6","MC2_8","MC2_9","MC2_10","MC3_1","MC3_9","MC4_2","MC6_1","MC6_2","MC6_9","MC6_16","MC7_5","NM1_9","NM2_8","NM2_9","HK1_5","ZP1_11","ZP2_1","ZP2_3","ZP3_8","ZP3_14","HG1_5","HG1_6","HG1_7","HG1_8","HG1_9","HG2_4","HG2_5","HG2_15","HG2_18","ZC1_3","ZC1_4","ZC2_6","ZC2_9","ZC3_4","ZC3_10","ZC6_7","ZC6_15","ZC7_5","ZC7_6","ZC7_9","ZC7_10","ZC8_13","ZC8_15","ZC8_16","ZC8_17","ZC8_19","ZC9_12","ZC10_1","ZC11_2","ZC11_12","ZC14_5","ML1_2","ML1_5","ML1_6","ML1_7","ML1_8","ML1_9","ML1_10","ML1_13","ML2_2","ML2_4","ML2_8","ML2_13","ML2_14","ML2_15","ML2_16","ML2_17","ML3_6","ML3_7","ML3_8","ML3_10","ML3_12","ML3_13","ML3_14","ML3_18","ML4_2","ML4_3","ML4_4","MT2_8","MT3_2","MT3_3","MT3_8","MT3_9","MT4_17","MT5_11","MT5_12","MT5_13","MT5_14","MT5_17","MT5_20","MT5_21","MT5_27","MT5_33","MT5_38","MT5_43","MT5_44","MT5_45","MT5_46","MT5_47","MT5_48","MT6_1","MT6_5","MT6_7","MT6_8","MT6_9","MT6_14","MT6_15","MT6_16","MT6_19","MT6_20","MT6_24","MT6_25","MT6_26","MT6_28","MT6_31","MT6_32","MT6_33","MT6_34","MT7_1","MT7_2","MT7_6","MT7_7","MT7_11","MT7_12","MT7_13","MT7_15","MT7_16","MT7_20","MT7_23","MT8_26","MT8_32","MT9_4","MT9_6","MT9_13","MT9_24","MT9_28","MT9_30","MT9_38","MT10_5","MT10_6","MT10_7","MT10_8","MT10_9","MT10_11","MT10_12","MT10_14","MT10_16","MT10_17","MT10_18","MT10_19","MT10_20","MT10_22","MT10_23","MT10_26","MT10_27","MT10_28","MT10_31","MT10_34","MT11_4","MT11_7","MT11_8","MT11_9","MT11_14","MT11_17","MT11_29","MT12_3","MT12_5","MT12_7","MT12_33","MT12_34","MT13_14","MT13_17","MT13_18","MT13_29","MT13_30","MT13_51","MT14_16","MT14_18","MT14_27","MT15_3","MT15_5","MT15_6","MT15_10","MT15_14","MT15_16","MT15_17","MT15_34","MT16_2","MT16_3","MT16_6","MT16_8","MT16_9","MT16_10","MT16_11","MT16_15","MT17_5","MT17_7","MT17_9","MT17_17","MT17_20","MT18_3","MT18_10","MT18_18","MT18_35","MT19_4","MT19_14","MT19_28","MT20_4","MT20_6","MT20_7","MT20_22","MT20_23","MT20_25","MT20_32","MT21_2","MT21_3","MT21_5","MT21_13","MT21_16","MT21_21","MT21_22","MT21_24","MT21_25","MT21_32","MT21_33","MT21_42","MT22_4","MT22_9","MT22_13","MT22_18","MT22_19","MT22_21","MT22_29","MT22_31","MT23_3","MT23_8","MT23_9","MT23_10","MT23_13","MT23_15","MT23_23","MT23_25","MT23_27","MT23_28","MT23_29","MT23_30","MT23_31","MT23_32","MT23_33","MT23_34","MT23_35","MT23_37","MT23_39","MT24_2","MT24_4","MT24_6","MT24_9","MT24_15","MT24_20","MT24_23","MT24_26","MT24_32","MT24_33","MT24_42","MT24_43","MT24_44","MT25_6","MT25_8","MT25_9","MT25_13","MT25_28","MT25_30","MT25_34","MT25_35","MT25_36","MT25_40","MT25_41","MT25_42","MT25_43","MT25_45","MT26_2","MT26_10","MT26_11","MT26_15","MT26_18","MT26_26","MT26_27","MT26_31","MT26_36","MT26_38","MT26_40","MT26_41","MT26_45","MT26_46","MT26_48","MT26_55","MT26_64","MT26_65","MT27_17","MT27_21","MT27_24","MT27_65","MT28_5","MT28_6","MT28_7","MT28_9","MT28_10","MT28_13","MT28_19","MK1_3","MK1_15","MK2_8","MK2_10","MK2_25","MK4_3","MK4_13","MK4_24","MK4_40","MK5_39","MK6_9","MK6_10","MK6_11","MK6_31","MK6_37","MK6_38","MK6_50","MK7_8","MK7_9","MK7_11","MK7_12","MK7_13","MK7_14","MK7_18","MK8_5","MK8_15","MK8_17","MK8_18","MK8_19","MK8_20","MK8_21","MK8_29","MK9_7","MK9_16","MK9_19","MK9_33","MK9_39","MK9_41","MK9_50","MK10_14","MK10_36","MK10_38","MK10_39","MK10_42","MK10_49","MK11_2","MK11_3","MK11_5","MK11_17","MK11_22","MK11_24","MK11_25","MK11_29","MK11_30","MK11_31","MK12_10","MK12_15","MK12_17","MK12_24","MK12_26","MK12_27","MK12_38","MK13_5","MK13_7","MK13_9","MK13_11","MK13_13","MK13_14","MK13_18","MK13_21","MK13_23","MK13_28","MK13_29","MK13_33","MK13_35","MK13_37","MK14_6","MK14_7","MK14_13","MK14_14","MK14_15","MK14_22","MK14_27","MK14_32","MK14_34","MK14_38","MK14_41","MK14_42","MK14_44","MK14_48","MK14_49","MK14_62","MK14_64","MK14_71","MK15_9","MK15_12","MK15_36","MK16_6","MK16_7","MK16_15","LK2_10","LK2_12","LK2_49","LK3_4","LK3_8","LK3_13","LK3_14","LK4_23","LK5_4","LK5_22","LK5_24","LK5_30","LK5_34","LK6_2","LK6_3","LK6_21","LK6_22","LK6_23","LK6_24","LK6_25","LK6_27","LK6_28","LK6_31","LK6_32","LK6_33","LK6_34","LK6_35","LK6_36","LK6_37","LK6_38","LK6_46","LK7_22","LK7_24","LK7_25","LK7_26","LK7_32","LK7_33","LK7_34","LK8_18","LK8_52","LK9_3","LK9_4","LK9_5","LK9_13","LK9_14","LK9_20","LK9_35","LK9_44","LK9_50","LK10_2","LK10_3","LK10_4","LK10_5","LK10_7","LK10_8","LK10_9","LK10_10","LK10_11","LK10_20","LK10_23","LK10_24","LK11_2","LK11_9","LK11_13","LK11_18","LK11_39","LK11_41","LK11_42","LK11_43","LK11_44","LK11_46","LK11_47","LK11_48","LK11_52","LK12_1","LK12_3","LK12_4","LK12_5","LK12_7","LK12_11","LK12_15","LK12_22","LK12_24","LK12_26","LK12_27","LK12_29","LK12_30","LK12_31","LK12_33","LK12_39","LK12_40","LK12_51","LK12_54","LK12_55","LK12_56","LK12_57","LK13_2","LK13_3","LK13_4","LK13_5","LK13_14","LK13_24","LK13_25","LK13_26","LK13_27","LK13_28","LK13_32","LK13_34","LK13_35","LK14_17","LK15_6","LK15_9","LK15_22","LK15_23","LK16_9","LK16_11","LK16_12","LK16_13","LK16_15","LK17_3","LK17_6","LK17_10","LK17_14","LK17_22","LK17_23","LK17_32","LK18_6","LK18_16","LK19_13","LK19_24","LK19_27","LK19_30","LK19_31","LK19_33","LK19_46","LK20_3","LK20_5","
15334LK20_24","LK20_25","LK20_46","LK21_6","LK21_8","LK21_9","LK21_14","LK21_16","LK21_17","LK21_19","LK21_20","LK21_28","LK21_29","LK21_30","LK21_31","LK21_34","LK21_36","LK22_8","LK22_10","LK22_11","LK22_12","LK22_17","LK22_19","LK22_28","LK22_30","LK22_35","LK22_40","LK22_46","LK22_51","LK22_52","LK22_53","LK22_67","LK22_68","LK22_70","LK23_14","LK23_28","LK23_30","LK24_5","LK24_6","LK24_17","LK24_38","LK24_39","LK24_41","LK24_48","LK24_49","JN1_23","JN1_26","JN1_38","JN1_39","JN1_51","JN2_5","JN2_7","JN2_8","JN2_16","JN2_19","JN3_11","JN3_12","JN3_28","JN4_20","JN4_21","JN4_22","JN4_29","JN4_32","JN4_35","JN4_38","JN4_48","JN5_20","JN5_28","JN5_33","JN5_34","JN5_35","JN5_37","JN5_38","JN5_39","JN5_40","JN5_42","JN5_43","JN5_44","JN5_45","JN5_46","JN5_47","JN6_10","JN6_12","JN6_20","JN6_26","JN6_27","JN6_29","JN6_36","JN6_43","JN6_53","JN6_62","JN6_67","JN7_8","JN7_19","JN7_21","JN7_22","JN7_23","JN7_24","JN7_28","JN7_34","JN7_36","JN7_45","JN7_47","JN8_14","JN8_15","JN8_19","JN8_21","JN8_22","JN8_23","JN8_24","JN8_28","JN8_31","JN8_32","JN8_33","JN8_36","JN8_37","JN8_38","JN8_39","JN8_40","JN8_41","JN8_42","JN8_43","JN8_44","JN8_45","JN8_46","JN8_47","JN8_49","JN8_54","JN8_55","JN9_19","JN9_21","JN9_23","JN9_27","JN9_30","JN9_41","JN10_20","JN10_25","JN10_26","JN10_32","JN10_34","JN10_36","JN10_37","JN10_38","JN11_15","JN11_34","JN11_39","JN11_44","JN11_49","JN11_50","JN12_8","JN12_19","JN12_35","JN12_36","JN13_10","JN13_11","JN13_12","JN13_13","JN13_14","JN13_15","JN13_17","JN13_19","JN13_33","JN13_34","JN13_35","JN14_1","JN14_3","JN14_4","JN14_7","JN14_11","JN14_13","JN14_14","JN14_15","JN14_17","JN14_19","JN14_20","JN14_24","JN14_28","JN14_29","JN14_31","JN15_3","JN15_4","JN15_5","JN15_7","JN15_8","JN15_9","JN15_10","JN15_12","JN15_14","JN15_16","JN15_17","JN15_18","JN15_19","JN15_20","JN15_27","JN16_1","JN16_4","JN16_10","JN16_12","JN16_16","JN16_17","JN16_19","JN16_20","JN16_22","JN16_23","JN16_24","JN16_26","JN16_27","JN16_31","JN16_32","JN16_33","JN18_4","JN18_7","JN18_8","JN18_29","JN18_31","JN18_39","JN19_4","JN19_6","JN19_35","JN20_22","JN20_23","JN20_31","JN21_5","JN21_6","JN21_10","JN21_12","AC1_4","AC1_5","AC1_8","AC1_11","AC2_14","AC2_15","AC2_22","AC2_23","AC2_33","AC2_36","AC2_38","AC2_40","AC3_12","AC3_13","AC3_14","AC3_15","AC3_16","AC3_17","AC3_19","AC3_22","AC3_25","AC4_7","AC4_10","AC4_19","AC5_8","AC5_20","AC5_25","AC5_28","AC5_30","AC5_35","AC5_38","AC5_39","AC6_3","AC7_2","AC7_4","AC7_26","AC7_42","AC7_43","AC7_49","AC7_51","AC7_52","AC7_53","AC8_19","AC8_24","AC10_21","AC10_28","AC10_29","AC10_37","AC11_16","AC12_17","AC13_2","AC13_15","AC13_16","AC13_25","AC13_39","AC13_40","AC13_41","AC13_46","AC14_15","AC15_1","AC15_7","AC15_10","AC15_13","AC15_29","AC16_15","AC16_36","AC17_23","AC18_15","AC19_2","AC19_3","AC19_15","AC19_25","AC19_26","AC19_37","AC19_39","AC20_10","AC20_18","AC20_25","AC20_28","AC20_31","AC20_34","AC21_13","AC21_28","AC22_1","AC22_3","AC23_15","AC23_23","AC25_24","AC27_25","AC27_31","AC27_33","AC28_26","RM1_6","RM6_3","RM6_11","RM6_13","RM6_14","RM6_16","RM6_17","RM6_18","RM6_19","RM6_20","RM6_21","RM6_22","RM7_1","RM7_4","RM8_9","RM8_13","RM8_15","RM11_2","RM11_25","RM11_30","RM12_2","RM12_14","RM12_16","RM12_19","RM13_6","RM13_7","RM13_8","RM13_14","RM14_1","RM14_13","RM15_6","RM15_7","RM15_10","RM15_11","RM15_14","RM16_2","RM16_3","RM16_5","RM16_6","RM16_7","RM16_8","RM16_9","RM16_10","RM16_11","RM16_12","RM16_13","RM16_14","RM16_15","RM16_16","RM16_17","C11_5","C11_9","C11_10","C11_13","C11_15","C11_26","C11_30","C13_2","C13_3","C13_4","C13_5","C13_9","C13_16","C13_17","C14_5","C14_6","C14_8","C14_15","C14_16","C14_21","C15_2","C15_6","C15_7","C15_10","C15_12","C15_13","C16_2","C16_3","C16_4","C16_7","C16_8","C16_9","C16_11","C16_15","C16_16","C16_18","C16_19","C16_20","C17_1","C17_5","C17_23","C18_9","C18_12","C19_1","C19_2","C19_13","C19_24","C110_7","C110_10","C110_13","C110_14","C110_15","C110_18","C110_21","C110_25","C110_27","C110_28","C110_31","C110_32","C111_1","C111_2","C111_13","C111_17","C111_22","C111_24","C111_25","C111_26","C111_33","C111_34","C112_2","C112_27","C112_31","C114_1","C114_5","C114_9","C114_12","C114_20","C114_23","C114_26","C114_31","C114_39","C115_1","C115_2","C115_11","C115_17","C115_33","C115_34","C115_58","C116_1","C116_3","C116_6","C116_10","C116_11","C116_13","C116_15","C116_16","C116_18","C116_20","C21_7","C21_13","C21_14","C21_15","C21_24","C22_4","C22_9","C22_10","C23_2","C23_3","C25_12","C25_20","C26_12","C26_13","C26_14","C26_17","C26_18","C27_2","C27_3","C27_9","C27_11","C27_15","C28_7","C28_9","C28_10","C28_11","C29_3","C29_8","C210_7","C211_1","C211_4","C211_7","C211_16","C211_19","C211_20","C212_11","C212_13","C212_19","C212_20","C213_3","C213_5","C213_6","C213_7","C213_9","C213_11","C213_
1533412","GL1_6","GL1_9","GL1_13","GL3_2","GL3_3","GL3_4","GL3_7","GL3_26","GL3_27","GL3_28","GL3_29","GL4_6","GL4_8","GL4_9","GL4_10","GL4_12","GL4_13","GL4_14","GL4_15","GL4_17","GL4_21","GL4_28","GL5_1","GL5_2","GL5_4","GL5_7","GL5_10","GL5_13","GL5_15","GL5_16","GL5_17","GL5_18","GL6_1","GL6_2","GL6_7","GL6_11","EP1_13","EP2_2","EP2_5","EP2_8","EP2_11","EP2_12","EP2_13","EP2_19","EP2_22","EP3_2","EP3_4","EP3_18","EP3_19","EP4_1","EP4_4","EP4_20","EP4_21","EP4_25","EP4_26","EP4_27","EP4_30","EP4_32","EP5_1","EP5_2","EP5_5","EP5_7","EP5_8","EP5_11","EP5_15","EP5_17","EP5_18","EP5_25","EP6_1","EP6_4","EP6_5","EP6_9","EP6_10","EP6_11","EP6_13","EP6_14","EP6_16","EP6_17","EP6_21","EP6_22","PP1_10","PP1_27","PP1_30","PP2_2","PP2_5","PP2_12","PP2_14","PP2_15","PP2_18","PP2_22","PP2_26","PP2_28","PP2_29","PP3_1","PP3_2","PP3_15","PP3_17","PP4_1","PP4_4","PP4_6","PP4_8","PP4_9","PP4_10","PP4_14","PP4_15","PP4_16","PP4_21","CL1_4","CL1_5","CL1_6","CL1_7","CL1_9","CL1_22","CL1_23","CL2_6","CL2_7","CL2_8","CL2_10","CL2_11","CL2_12","CL2_20","CL3_1","CL3_2","CL3_3","CL3_4","CL3_5","CL3_7","CL3_8","CL3_9","CL3_12","CL3_15","CL3_17","CL3_18","CL3_19","CL3_20","CL3_21","CL3_22","CL3_23","CL3_24","CL4_1","CL4_2","CL4_5","CL4_8","CL4_10","CL4_12","CL4_15","CL4_16","CL4_17","CL4_18","H11_5","H11_6","H11_9","H12_1","H12_2","H12_5","H12_8","H12_9","H12_11","H12_13","H12_14","H12_20","H13_3","H13_4","H13_6","H13_8","H14_1","H14_2","H14_9","H14_10","H14_12","H14_13","H14_18","H15_1","H15_2","H15_4","H15_5","H15_11","H15_13","H15_14","H15_15","H15_16","H15_17","H15_18","H15_19","H15_20","H15_21","H15_22","H15_25","H15_26","H21_4","H21_5","H22_5","H22_6","H22_15","H23_1","H23_4","H23_7","H23_13","H23_14","H23_15","HB3_1","HB3_7","HB3_8","HB3_12","HB3_13","HB3_15","HB4_7","HB5_11","HB5_12","HB6_10","HB6_12","HB7_4","HB10_25","HB10_29","HB10_32","HB10_34","HB10_35","HB10_36","HB11_33","HB12_3","HB12_4","HB12_5","HB12_7","HB12_8","HB12_12","HB12_13","HB12_14","HB12_17","HB12_18","HB12_22","HB12_25","HB13_2","HB13_3","HB13_7","HB13_9","HB13_16","HB13_17","HB13_18","HB13_22","HB13_23","HB13_24","JM1_2","JM1_4","JM1_16","JM1_19","JM1_21","JM1_22","JM2_1","JM2_3","JM2_4","JM2_5","JM2_6","JM2_8","JM2_9","JM2_12","JM2_16","JM2_24","JM3_1","JM3_14","JM4_2","JM4_3","JM4_4","JM4_5","JM4_7","JM4_8","JM4_9","JM4_10","JM4_11","JM4_14","JM4_16","JM5_1","JM5_3","JM5_5","JM5_6","JM5_7","JM5_8","JM5_9","JM5_10","JM5_11","JM5_12","JM5_16","P11_6","P11_8","P11_13","P11_15","P11_16","P11_17","P11_18","P11_22","P12_2","P12_3","P12_5","P12_9","P12_13","P12_17","P12_20","P12_21","P12_24","P12_25","P13_6","P13_9","P13_13","P13_14","P13_15","P13_16","P14_1","P14_7","P14_12","P14_13","P14_14","P15_2","P15_4","P15_5","P15_6","P15_8","P15_9","P15_12","P15_14","P21_4","P21_5","P21_10","P21_19","P23_14","P23_15","P23_17","P23_18","J11_3","J12_1","J12_7","J12_13","J12_14","J12_15","J12_18","J12_20","J12_21","J12_24","J12_27","J12_28","J12_29","J13_1","J13_5","J13_11","J13_13","J13_15","J14_1","J14_2","J14_3","J14_4","J15_13","J15_21","J21","J21","J21","JD1","JD1","JD1","JD1","RV2_10","RV2_25","RV6_16","RV7_3","RV11_12","RV12_12","RV14_7","RV16_1","RV18_4","RV18_6","RV18_7","RV19_5","RV19_17","RV19_18"], 15335"doubles":["GN17_10","GN17_12","GN17_13","GN18_5","GN27_45","GN31_29","GN32_4","GN32_20","GN38_24","GN43_5","GN43_29","GN44_4","GN44_21","GN44_23","GN44_27","GN45_9","GN45_17","GN45_19","GN46_34","GN47_3","GN50_17","EX3_12","EX3_14","EX3_15","EX3_16","EX3_18","EX4_15","EX5_16","EX6_6","EX7_9","EX9_30","EX10_2","EX10_17","EX12_24","EX12_46","EX14_2","EX16_9","EX16_12","EX16_33","EX19_6","EX19_12","EX20_22","EX22_21","EX22_25","EX23_9","EX23_13","EX23_21","EX23_25","EX23_31","EX24_1","EX25_2","EX25_9","EX25_19","EX26_33","EX29_42","EX30_16","EX30_31","EX30_36","EX30_37","EX31_13","EX32_13","EX33_5","LV1_2","LV7_23","LV9_3","LV10_9","LV10_13","LV10_14","LV18_2","LV19_2","LV19_9","LV19_10","LV19_12","LV19_15","LV19_19","LV19_27","LV19_33","LV19_34","LV21_8","LV22_3","LV23_2","LV23_10","LV23_22","LV23_24","LV25_2","LV25_6","LV25_9","LV25_14","LV25_17","LV25_44","LV25_46","NU1_3","NU6_23","NU9_10","NU10_29","NU11_18","NU13_2","NU14_28","NU15_2","NU15_18","NU16_10","NU16_11","NU16_16","NU16_17","NU16_24","NU17_4","NU18_1","NU18_3","NU18_4","NU18_7","NU18_26","NU18_30","NU20_8","NU21_17","NU27_8","NU28_2","NU28_3","NU31_29","NU33_51","NU33_54","NU34_2","NU35_2","NU35_10","NU35_34","DT1_8","DT1_14","DT1_31","DT1_37","DT1_42","DT2_4","DT2_24","DT3_21","DT3_26","DT4_1","DT4_3","DT4_5","DT4_21","DT4_23","DT4_25","DT4_29","DT4_34","DT5_1","DT5_28","DT5_30","DT6_3","DT6_17","DT6_20","DT7_4","DT7_8","DT7_12","DT7_25","DT8_1","DT8_19","DT9_7","DT10_15","DT11_8","DT11_10","DT11_14","DT11_19","DT11_26","DT12_1","DT12_5","DT12_7","DT12_9","DT12_16","DT12_32","DT13_3","DT13_5","DT13_7","DT13_13","DT14_21","DT18_15","DT19_19","DT20_3","DT22_24","DT23_4","DT24_8","DT24_9","DT25_17","DT27_2","DT27_4","DT28_14","DT28_62","DT28_63","DT28_68","DT29_5","DT29_11","DT30_18","DT30_19","DT31_6","DT31_12","DT31_14","DT31_19","DT31_26","DT31_27","DT32_6","DT32_7","JS2_14","JS3_8","JS6_3","JS7_13","JS8_2","JS9_8","JS9_24","JS20_2","JS24_12","JS24_13","JG7_7","JG8_15","JG8_18","JG12_3","JG14_13","JG14_15","JG15_12","JG18_9","JG18_24","JG18_25","JG19_5","JG19_9","JG20_28","RT2_4","S12_29","S18_22","S19_19","S110_2","S110_15","S110_19","S114_12","S114_40","S115_3","S115_6","S118_22","S122_13","S125_6","S126_16","S129_10","S27_23","S211_20","S211_21","S213_17","S215_27","S216_10","S217_16","S218_12","S219_13","S2
1533519_29","S220_16","S220_21","S224_2","K13_26","K112_9","K112_28","K118_18","K120_9","K120_28","K120_33","K121_10","K122_27","K122_28","K21_3","K21_6","K22_3","K22_5","K22_16","K24_41","K29_11","K218_19","K218_27","K219_6","K219_10","K219_29","K222_18","R116_18","R116_35","R210_9","R218_27","R234_26","ER9_11","NH1_8","NH9_5","ET4_14","ET4_16","JB6_29","JB21_3","JB42_7","PS27_8","PS32_9","PS66_3","PS78_1","PS90_3","PS103_22","PS105_11","PS122_6","PS137_7","PS139_19","SS6_13","IS1_2","IS6_9","IS16_3","IS23_2","IS26_19","IS29_11","IS30_20","IS30_21","IS30_22","IS36_4","IS36_7","IS36_12","IS37_6","IS37_10","IS37_30","IS40_9","IS44_8","IS48_6","IS49_13","IS51_12","IS52_2","IS58_3","IS62_6","IS62_11","IS65_15","JR2_19","JR2_31","JR3_12","JR3_13","JR3_19","JR3_22","JR4_10","JR5_14","JR5_15","JR5_19","JR6_18","JR7_2","JR11_2","JR11_6","JR11_13","JR13_18","JR13_20","JR15_14","JR16_11","JR17_4","JR17_20","JR18_11","JR19_3","JR20_6","JR21_8","JR22_26","JR23_33","JR25_27","JR25_28","JR26_4","JR27_4","JR27_13","JR29_27","JR29_31","JR31_7","JR34_14","JR35_13","JR36_19","JR37_18","JR42_20","JR43_2","JR46_14","JR48_2","JR48_20","JR49_3","JR49_5","JR49_11","EK5_17","EK6_3","EK7_4","EK7_9","EK11_5","EK11_15","EK11_17","EK12_11","EK13_2","EK13_11","EK13_18","EK14_6","EK16_45","EK16_55","EK17_12","EK20_3","EK20_5","EK20_27","EK20_30","EK24_21","EK25_3","EK30_2","EK33_10","EK33_11","EK33_25","EK33_30","EK35_9","EK36_1","EK36_3","EK36_6","EK36_12","EK36_13","EK36_22","EK37_4","EK37_12","EK39_17","EK44_6","EK44_30","EK45_20","DN10_21","HS1_9","HS4_15","HS5_8","HS6_4","HS10_13","HS14_2","JL3_11","AM4_1","AM4_5","AM4_7","OB1","MC1_2","MC1_11","MC6_1","MC6_9","MC6_16","MC7_5","ZP2_5","ZP3_14","HG2_4","ZC1_3","ZC3_4","ZC4_9","ZC7_5","ZC9_12","ZC11_2","ZC14_5","ML1_2","ML1_6","ML1_7","ML1_8","ML2_14","ML2_15","ML3_8","ML3_13","MT5_21","MT5_27","MT5_33","MT5_39","MT5_43","MT6_2","MT6_9","MT8_32","MT9_6","MT10_16","MT11_8","MT11_21","MT11_24","MT15_5","MT17_5","MT17_20","MT21_5","MT21_16","MT21_21","MT21_28","MT22_4","MT23_34","MT23_37","MT23_38","MT24_23","MT24_25","MT24_26","MT25_6","MT26_18","MT26_45","MT26_46","MT26_64","MT26_65","MT28_7","MT28_9","MT28_20","MK4_3","MK5_19","MK7_11","MK8_29","MK10_49","MK11_23","MK13_21","MK14_41","MK14_42","MK16_6","LK2_10","LK4_23","LK5_4","LK5_24","LK6_23","LK6_30","LK7_25","LK7_34","LK9_41","LK10_3","LK10_13","LK10_19","LK11_2","LK11_5","LK11_41","LK12_32","LK13_25","LK13_26","LK13_32","LK13_34","LK13_35","LK15_31","LK16_12","LK17_3","LK17_6","LK17_7","LK17_21","LK17_23","LK22_10","LK22_11","LK22_31","LK22_67","LK22_70","LK23_14","LK24_49","JN1_38","JN3_7","JN3_11","JN4_21","JN4_35","JN7_6","JN8_17","JN8_19","JN8_25","JN8_33","JN10_36","JN11_34","JN12_19","JN14_9","JN14_10","JN15_20","JN16_5","JN16_32","JN19_4","JN19_6","JN19_14","JN19_15","JN20_17","JN21_12","AC3_25","AC5_8","AC5_9","AC5_25","AC5_28","AC10_21","AC13_25","AC13_46","AC20_25","AC25_26","AC28_26","C17_28","C112_21","C115_51","C27_11","C210_9","C212_14","GL1_20","GL5_2","GL6_1","CL4_17","T24_22","TT3_15","PM1","HB12_5","HB13_19","JM2_3","JM2_8","JM4_11","JM5_1","JM5_4","JM5_7","JM5_9","JM5_11","RV2_10","RV2_13","RV18_20"] 15336 15337}; 15338 15339 15340 15341/********* 15342* ../app/js/plugins/visualfilters.js 15343**********/ 15344/* 15345x click off dropdowns 15346x hebrew morphology 15347- multi-select morphology 15348- Strong's selector 15349- TYPE: [Any Text, Greek, Hebrew] 15350*/ 15351 15352sofia.config = $.extend(sofia.config, { 15353 15354 enableVisualFilters: true 15355 15356}); 15357 15358 15359 15360/** 15361 * Highlights words based on morphological data 15362 * 15363 * @author John Dyer (http://j.hn/) 15364 */ 15365 15366var VisualFilters = function(node) { 15367 15368 if (!sofia.config.enableVisualFilters) { 15369 return; 15370 } 15371 15372 15373 var 15374 settingsKey = 'docs-config-visualfilters', 15375 15376 filtersWindow = new MovableWindow(550,290).hide(), 15377 15378 defaultSettings = { 15379 transforms: [ 15380 15381 // underline Jesus in red 15382 { 15383 active: false, 15384 strongs: 'G2424', 15385 morphType: '', 15386 morph: '', 15387 styleLabel: 'Red Underline', 15388 style: 'border-bottom: solid 1px #ff3333' 15389 }, 15390 15391 // aorist verbs in blue 15392 { 15393 active: false, 15394 strongs: '', 15395 morphType: 'robinson', 15396 morph: 'V-A', 15397 styleLabel: 'Blue Text', 15398 style: 'color: #3333cc' 15399 }, 15400 15401 // proper names in gray 15402 { 15403 active: false, 15404 strongs: '', 15405 morphType: 'morphbh', 15406 morph: 'Np', 15407 styleLabel: 'Gray Text',
15408 style: 'color: #999999' 15409 } 15410 15411 ] 15412 }, 15413 visualSettings = AppSettings.getValue(settingsKey, defaultSettings), 15414 15415 visualNode = $('<div id="visualfilters-config">' + 15416 15417 '<input type="button" value="New Filter" />' + 15418 15419 '<table>' + 15420 '<thead>' + 15421 '<tr>' + 15422 '<th class="visualfilters-active"></th>' + 15423 '<th class="visualfilters-strongs i18n" data-i18n="[html]plugins.visualfilters.strongsnumber"></th>' + 15424 '<th class="visualfilters-morph i18n" data-i18n="[html]plugins.visualfilters.morphology">Morphology</th>' + 15425 '<th class="visualfilters-style i18n" data-i18n="[html]plugins.visualfilters.style">Style</th>' + 15426 '<th class="visualfilters-example"></th>' + 15427 '<th class="visualfilters-remove"></th>' + 15428 '</tr>' + 15429 '</thead>' + 15430 '<tbody>' + 15431 '</tbody>' + 15432 '</table>' + 15433 15434 '</div>') 15435 .appendTo( filtersWindow.body ), 15436 15437 stylesSelector = new StylesSelector(), 15438 15439 morphSelector = new MorphologySelector(), 15440 15441 tbody = visualNode.find('tbody'), 15442 15443 addRowButton = visualNode.find('input') 15444 .on('click', function() { 15445 var row = createRow(); 15446 tbody.append(row); 15447 }), 15448 15449 openVisualizationsButton = $('<span class="config-button i18n" data-i18n="[html]plugins.visualfilters.button" id="config-visualfilters-button"></span>') 15450 .appendTo( $('#config-tools .config-body') ); 15451 15452 //console.log('LOADED VIZ',visualSettings); 15453 15454 // Attach to Config pane 15455 15456 //filtersWindow.title.html('Visual Filters'); 15457 filtersWindow.title 15458 .addClass('i18n') 15459 .attr('data-i18n','[html]plugins.visualfilters.title') 15460 15461 openVisualizationsButton.on('click', function() { 15462 15463 filtersWindow.show().center(); 15464 15465 openVisualizationsButton.closest('.window-overlay').hide(); 15466 15467 }); 15468 15469 // ROWS 15470 tbody.on('click', '.visualfilters-remove', function() { 15471 15472 // remove row 15473 15474 $(this).closest("tr").remove(); 15475 15476 saveTransforms(); 15477 15478 VisualTransformer.resetTransforms(visualSettings); 15479 }); 15480 15481 tbody.on('change keyup', '.visualfilters-active input, .visualfilters-morph select, .visualfilters-strongs input, .visualfilters-morph input', function() { 15482 //updateExamples(); 15483 15484 saveTransforms(); 15485 15486 VisualTransformer.resetTransforms(visualSettings); 15487 }); 15488 15489 // STYLE 15490 var activeSpan = null; 15491 filtersWindow.body.on('click', '.visualfilters-style span', function(e) { 15492 e.preventDefault(); 15493 morphSelector.hide(); 15494 15495 if (stylesSelector.is(':visible') && activeSpan != null && activeSpan[0] == $(this)[0]) { 15496 stylesSelector.hide(); 15497 return; 15498 } 15499 15500 activeSpan = $(this); 15501 15502 stylesSelector 15503 .css({scrollTop: 0, top: activeSpan.offset().top + activeSpan.outerHeight(true), left: activeSpan.offset().left }) 15504 .show(); 15505 15506 return false; 15507 }); 15508 15509 stylesSelector.on('click', 'div', function() { 15510 15511 15512 var div = $(this); 15513 15514 stylesSelector.hide(); 15515 15516 if (activeSpan != null) { 15517 15518 var newStyle = div.find('span'), 15519 newStyleParams = newStyle.attr('data-style'), 15520 newStyleLabel = newStyle.html(); 15521 15522 console.log('new style', newStyleParams); 15523 15524 // add to data 15525 activeSpan 15526 .attr('data-style', newStyleParams) 15527 .html(newStyleLabel); 15528 15529 // actually style it 15530 activeSpan.attr('style',''); 15531 VisualTransformer.applyStyle(activeSpan, newStyleParams); 15532 15533 15534 saveTransforms(); 15535 15536 VisualTransformer.resetTransforms(visualSettings); 15537 } 15538 15539 return false; 15540 15541 }); 15542 15543 filtersWindow.container.find('.close-button').on('click', function() { 15544 stylesSelector.hide(); 15545 morphSelector.hide(); 15546 }); 15547 15548 15549 // morph 15550 filtersWindow.body.on('click', '.visualfilters-morph input', function(e) { 15551 e.preventDefault(); 15552 stylesSelector.hide(); 15553 15554 //morphSelector.attach( $(this) ); 15555 var input = $(this); 15556 15557 15558 if (morphSelector.is(':visible') && morphSelector.currentInput != null && morphSelector.currentInput[0] == input[0]) { 15559 morphSelector.hide(); 15560 return false; 15561 } 15562 15563 morphSelector.css({ 15564 top: input.offset().top + input.outerHeight(), 15565 left: input.offset().left 15566 }); 15567 15568 morphSelector.show(); 15569 15570 morphSelector.currentInput = input; 15571 morphSelector.setMorphology( input.siblings('select').val() ); 15572 morphSelector.updateMorphSelector( input.val() ); 15573 15574 15575 15576 return false; 15577 }); 15578 15579 morphSelector.on('update', function(value) { 15580 saveTransforms(); 15581 15582 VisualTransformer.resetTransforms(visualSettings); 15583 }); 15584 15585 // this will be cancelled if the user clicks in an input 15586 filtersWindow.body.on('click', function(e) { 15587 15588 if (morphSelector.is(':visible')) { 15589 morphSelector.hide(); 15590 } 15591 15592 if (stylesSelector.is(':visible')) { 15593 stylesSelector.hide(); 15594 } 15595 }); 15596 15597 15598 15599 // DRAW! 15600 15601 function drawTransforms() { 15602 tbody.empty(); 15603 15604 for (var i=0, il=visualSettings.transforms.length; i<il; i++) { 15605 var row = createRow(), 15606 transform = visualSettings.transforms[i]; 15607 15608 row.find('.visualfilters-active input').prop('checked', transform.active); 15609 row.find('.visualfilters-strongs input').val(transform.strongs); 15610 row.find('.visualfilters-morph input').val(transform.morph); 15611 row.find('.visualfilters-morph select').val(transform.morphType); 15612 row.find('.visualfilters-style span') 15613 .html(transform.styleLabel) 15614 .attr('data-style', transform.style); 15615 15616 VisualTransformer.applyStyle( row.find('.visualfilters-style span'), transform.style); 15617 15618 tbody.append(row); 15619 } 15620 } 15621 15622 15623 function createRow() { 15624 15625 return $( 15626 '<tr>' + 15627 '<td class="visualfilters-active">' + 15628 '<input type="checkbox" checked />' + 15629 '</td>' + 15630 '<td class="visualfilters-strongs">' + 15631 '<input type="text" placeholder="G2424, H234" />' + 15632 '</td>' + 15633 '<td class="visualfilters-morph">' + 15634 '<select>' + 15635 '<option value="morphhb">Hebrew</option>' + 15636 '<option value="robinson">Greek</option>' + 15637 '</select>' + 15638 '<input type="text" placeholder="V-A?" />' + 15639 '</td>' + 15640 '<td class="visualfilters-style">' + 15641 '<span>
15641Choose...</span>' + 15642 '</td>' + 15643 '<td class="visualfilters-remove">' + 15644 '<span class="close-button"></span>' + 15645 '</td>' + 15646 '</tr>'); 15647 } 15648 15649 function saveTransforms() { 15650 15651 // clear 15652 visualSettings.transforms = []; 15653 15654 tbody.find('tr').each(function() { 15655 var row = $(this), 15656 transform = {}; 15657 15658 transform.active = row.find('.visualfilters-active input').is(':checked'); 15659 transform.strongs = row.find('.visualfilters-strongs input').val(); 15660 transform.morph = row.find('.visualfilters-morph input').val(); 15661 transform.morphType = row.find('.visualfilters-morph select').val(); 15662 transform.style = row.find('.visualfilters-style span').attr('data-style'); 15663 transform.styleLabel = row.find('.visualfilters-style span').html(); 15664 15665 //console.log('saving', transform.style, row.find('.visualfilters-style span').prop('data-style')); 15666 15667 15668 if (transform.morph != '') { 15669 15670 if (transform.morphType == 'robinson') { 15671 transform.morphRegExp = new RegExp('^' + transform.morph.replace(/\?/gi,'.{1}'), 'gi'); 15672 } else if (transform.morphType == 'morphhb') { 15673 transform.morphRegExp = new RegExp( 15674 '(^H' + transform.morph.replace(/\?/gi,'.{1}') + ')|'+ 15675 '(/' + transform.morph.replace(/\?/gi,'.{1}') + ')', 'gi'); 15676 } 15677 } else { 15678 transform.morphRegExp = null; 15679 } 15680 15681 visualSettings.transforms.push(transform); 15682 }); 15683 15684 //console.log('saving', visualSettings); 15685 15686 // store for next load 15687 AppSettings.setValue(settingsKey, visualSettings); 15688 } 15689 15690 15691 15692 15693 // global object 15694 var ext = { 15695 sendMessage: function() {} 15696 }; 15697 ext = $.extend(true, ext, EventEmitter); 15698 15699 15700 ext.on('message', function(e) { 15701 if (e.data.messagetype == 'textload') { 15702 15703 var lang = e.data.content.attr('lang'), 15704 validLangs = ['heb','gre','grc', 'eng']; 15705 15706 if (validLangs.indexOf(lang) > -1) { 15707 15708 VisualTransformer.runTransforms(e.data.content, visualSettings); 15709 } 15710 } 15711 }); 15712 15713 // START UP 15714 drawTransforms(); 15715 saveTransforms(); 15716 15717 15718 return ext; 15719} 15720 15721sofia.plugins.push('VisualFilters'); 15722 15723 15724var VisualTransformer = (function() { 15725 15726 15727 function resetTransforms(visualSettings) { 15728 // remove all inline styles 15729 $('l').attr('style',''); 15730 15731 // re-run on existing chapters 15732 $('.section').each(function() { 15733 runTransforms( $(this), visualSettings); 15734 }); 15735 } 15736 15737 function runTransforms(sectionNode, visualSettings) { 15738 if (visualSettings.transforms.length === 0) 15739 return; 15740 15741 var sectionLang = sectionNode.attr('lang'); 15742 15743 sectionNode.find('l').each(function(index, node) { 15744 15745 var word = $(this); 15746 15747 for (var i=0, il=visualSettings.transforms.length; i<il; i++) { 15748 var transform = visualSettings.transforms[i], 15749 isMatch = false; 15750 15751 if (!transform.active) { 15752 continue; 15753 } 15754 15755 15756 15757 // strongs number 15758 if (transform.strongs != '') { 15759 15760 if (word.attr('s') == transform.strongs) { 15761 //applyStyle(word, transform.style); 15762 15763 isMatch = true; 15764 15765 } else { 15766 15767 continue; 15768 } 15769 } 15770 15771 // morphology 15772 if (transform.morph != '') { 15773 15774 // sectionLang == transform.morphType && 15775 if (transform.morphRegExp && transform.morphRegExp != null) { 15776 var wordMorphData = word.attr('m'); 15777 15778 if (wordMorphData != null && transform.morphRegExp.test(wordMorphData)) { 15779 //applyStyle(word, transform.style); 15780 15781 isMatch = true; 15782 } else { 15783 15784 continue; 15785 } 15786 } 15787 } 15788 15789 if (isMatch) { 15790 applyStyle(word, transform.style); 15791 } 15792 15793 15794 /* 15795 break; 15796 case 'frequency': 15797 15798 if (transform.data > 0) { 15799 var strongs = w.attr('data-lemma'); 15800 15801 if (strongs != null ) { 15802 15803 // run all possible strongs on this word or phrase 15804 strongs = strongs.split(' '); 15805 for (var j=0, jl=strongs.length; j<jl; j++) { 15806 var freq = (typeof strongsGreekFrequencies != 'undefined') ? strongsGreekFrequencies[ strongs[j] ] : 0; 15807 15808 if (freq > 0 && freq <= transform.data) { 15809 applyStyle(w, transform.style); 15810 } 15811 } 15812 } 15813 } 15814 15815 15816 break; 15817 */ 15818 15819 } 15820 15821 }); 15822 } 15823 15824 function applyStyle(node, css) { 15825 15826 if (typeof css == 'undefined' || css == null || css == '') { 15827 return; 15828 } 15829 15830 15831 var props = css.split(';'); 15832 15833 for (var i=0, il=props.length; i<il; i++) { 15834 15835 var parts = props[i].split(':'); 15836 15837 if (parts.length === 2) { 15838 node.css(parts[0], parts[1]); 15839 } 15840 } 15841 } 15842 15843 15844 return { 15845 resetTransforms: resetTransforms, 15846 runTransforms: runTransforms, 15847 applyStyle: applyStyle 15848 }; 15849 15850})(); 15851 15852 15853 15854 15855var MorphologySelector = function(parent) { 15856 // morph selector 15857 var currentMorphologyKey = 'robinson', 15858 15859 robinsonElements = { 15860 15861 "nounCase": { 15862 breakBefore: true, 15863 declension : 'Case', 15864 parts : [ 15865 { 15866 letter : 'N', 15867 type : 'Nominative' 15868 }, 15869 { 15870 letter : 'A', 15871 type : 'Accusative' 15872 }, 15873 { 15874 letter : 'D', 15875 type : 'Dative' 15876 }, 15877 { 15878 letter : 'G', 15879 type : 'Genative' 15880 }, 15881 { 15882 letter : 'V', 15883 type : 'Vocative' 15884 } 15885 ] 15886 }, 15887 15888 "number": { 15889 declension : 'Number', 15890 parts : [ 15891 { 15892 letter : 'P', 15893 type : 'Plural' 15894 }, 15895 { 15896 letter : 'S', 15897 type : 'Singular' 15898 } 15899 ] 15900 }, 15901 "gender":{ 15902 declension : 'Gender', 15903 parts : [ 15904 { 15905 letter : 'F', 15906 type : 'Feminine' 15907 }, 15908 { 15909 letter : 'M', 15910 type : 'Masculine' 15911 }, 15912 { 15913 letter : 'N', 15914 type : 'Nueter' 15915 } 15916 ] 15917 }, 15918 15919 15920 "verbTense": { 15921 breakBefore: true, 15922 declension : 'Tense', 15923 parts : [ 15924 { 15925 letter : 'A', 15926 type : 'Aorist' 15927 }, 15928 { 15929 letter : 'F', 15930 type : 'Future' 15931 }, 15932 { 15933 letter : 'I', 15934 type : 'Imperfect' 15935 }, 15936 { 15937 letter : 'R', 15938 type : 'Perfect' 15939 }, 15940 { 15941 letter : 'L', 15942 type : 'Pluperfect' 15943 }, 15944 { 15945 letter : 'P', 15946 type : 'Present' 15947 } 15948 ] 15949 }, 15950 "verbVoice": { 15951 declension : 'Voice', 15952 parts : [ 15953 { 15954 letter : 'A', 15955 type : 'Active' 15956 }, 15957 { 15958 letter : 'M', 15959 type : 'Middle' 15960 }, 15961 { 15962 letter : 'P', 15963 type : 'Passive' 15964 } 15965 ] 15966 }, 15967 "verbMood": { 15968 declension : 'Mood', 15969 parts : [ 15970 { 15971 letter : 'I', 15972 type : 'Indicative' 15973 }, 15974 { 15975 letter : 'S', 15976 type : 'Subjunctive' 15977 }, 15978 { 15979 letter : 'O', 15980 type : 'Optative' 15981 }, 15982 { 15983 letter : 'M', 15984 type : 'Imperative' 15985 }, 15986 { 15987 letter : 'N', 15988 type : 'Infinitive' 15989 }, 15990 { 15991 letter : 'P', 15992 type : 'Participle' 15993 } 15994 ] 15995 } 15996 , 15997 "person": { 15998 declension : 'Person', 15999 breakBefore: true, 16000 parts : [ 16001 { 16002 letter : '1', 16003 type : '1st Person' 16004 }, 16005 { 16006 letter : '2', 16007 type : '2nd Person' 16008 }, 16009 { 16010 letter : '3', 16011 type : '3rd Person' 16012 } 16013 ] 16014 } 16015 16016 }, 16017 16018 morphhbElements = { 16019 "nounTypes":{ 16020 declension : 'Type', 16021 parts : [ 16022 { 16023 letter : 'c', 16024 type : 'Common' 16025 }, 16026 { 16027 letter : 'g', 16028 type : 'Gentilic' 16029 }, 16030 { 16031 letter : 'p', 16032 type : 'Proper name' 16033 } 16034 ] 16035 }, 16036 16037 "person": { 16038 declension : 'Person', 16039 breakBefore: true, 16040 parts : [ 16041 { 16042 letter : '1', 16043 type : '1st Person' 16044 }, 16045 { 16046 letter : '2', 16047 type : '2nd Person' 16048 }, 16049 { 16050 letter : '3', 16051 type : '3rd Person' 16052 } 16053 ] 16054 }, 16055 "number": { 16056 declension : 'Number', 16057 parts : [ 16058 { 16059 letter : 'p', 16060 type : 'Plural' 16061 }, 16062 { 16063 letter : 's', 16064 type : 'Singular' 16065 } 16066 , 16067 { 16068 letter : 'd', 16069 type : 'Dual' 16070 } 16071 ] 16072 }, 16073 "state":{ 16074 declension : 'State', 16075 parts : [ 16076 { 16077 letter : 'a', 16078 type : 'Absolute' 16079 }, 16080 { 16081 letter : 'c', 16082 type : 'Construct' 16083 }, 16084 { 16085 letter : 'd', 16086 type : 'Determined' 16087 } 16088 ] 16089 }, 16090 "nounGender":{ 16091 declension : 'Gender', 16092 parts : [ 16093 { 16094 letter : 'f', 16095 type : 'Feminine' 16096 }, 16097 { 16098 letter : 'm', 16099 type : 'Masculine' 16100 }, 16101 { 16102 letter : 'b', 16103 type : 'Both' 16104 } 16105 ] 16106 }, 16107 "verbGender":{ 16108 declension : 'Gender', 16109 parts : [ 16110 { 16111 letter : 'f', 16112 type : 'Feminine' 16113 }, 16114 { 16115 letter : 'm', 16116 type : 'Masculine' 16117 }, 16118 { 16119 letter : 'c', 16120 type : 'Common' 16121 } 16122 ] 16123 }, 16124 16125 verbStem: { 16126 declension : 'Stem', 16127 parts : [ 16128 { 16129 letter: 'q', 16130 type: 'qal', 16131 }, 16132 { 16133 letter: 'N', 16134 type: 'niphal', 16135 }, 16136 { 16137 letter: 'p', 16138 type: 'piel', 16139 }, 16140 { 16141 letter: 'P', 16142 type: 'pual', 16143 }, 16144 { 16145 letter: 'h', 16146 type: 'hiphil', 16147 }, 16148 { 16149 letter: 'H', 16150 type: 'hophal', 16151 }, 16152 { 16153 letter: 't', 16154 type: 'hithpael', 16155 }, 16156 { 16157 letter: 'o', 16158 type: 'polel', 16159 }, 16160 { 16161 letter: 'O', 16162 type: 'polal', 16163 }, 16164 { 16165 letter: 'r', 16166 type: 'hithpolel', 16167 }, 16168 { 16169 letter: 'm', 16170 type: 'poel', 16171 }, 16172 { 16173 letter: 'M', 16174 type: 'poal', 16175 }, 16176 { 16177 letter: 'k', 16178 type: 'palel', 16179 }, 16180 { 16181 letter: 'K', 16182 type: 'pulal', 16183 16184 }, 16185 { 16186 letter: 'Q', 16187 type: 'qal passive', 16188 }, 16189 { 16190 letter: 'l', 16191 type: 'pilpel', 16192 }, 16193 { 16194 letter: 'L', 16195 type: 'polpal', 16196 }, 16197 { 16198 letter: 'f', 16199 type: 'hithpalpel', 16200 }, 16201 { 16202 letter: 'D', 16203 type: 'nithpael', 16204 }, 16205 { 16206 letter: 'j', 16207 type: 'pealal', 16208 }, 16209 { 16210 letter: 'i', 16211 type: 'pilel', 16212 }, 16213 { 16214 letter: 'u', 16215 type: 'hothpaal', 16216 }, 16217 { 16218 letter: 'c', 16219 type: 'tiphil', 16220 }, 16221 { 16222 letter: 'v', 16223 type: 'hishtaphel', 16224 }, 16225 { 16226 letter: 'w', 16227 type: 'nithpalel', 16228 }, 16229 { 16230 letter: 'y', 16231 type: 'nithpoel', 16232 }, 16233 { 16234 letter: 'z', 16235 type: 'hithpoel' 16236 } 16237 ] 16238 }, 16239 16240 verbType: { 16241 declension : 'Type', 16242 parts : [ 16243 { 16244 letter: 'p', 16245 type: 'perfect (qatal)', 16246 }, 16247 { 16248 letter: 'q', 16249 type: 'sequential perfect (weqatal)', 16250 }, 16251 { 16252 letter: 'i', 16253 type: 'imperfect (yiqtol)', 16254 }, 16255 { 16256 letter: 'w', 16257 type: 'sequential imperfect (wayyiqtol)', 16258 }, 16259 { 16260 letter: 'h', 16261 type: 'cohortative', 16262 }, 16263 { 16264 letter: 'j', 16265 type: 'jussive', 16266 }, 16267 { 16268 letter: 'v', 16269 type: 'imperative', 16270 }, 16271 { 16272 letter: 'r', 16273 type: 'participle active', 16274 }, 16275 { 16276 letter: 's', 16277 type: 'participle passive', 16278 }, 16279 { 16280 letter: 'a', 16281 type: 'infinitive absolute', 16282 }, 16283 { 16284 letter: 'c', 16285 type: 'infinitive construct' 16286 } 16287 ] 16288 }, 16289 16290 verbVoices: { 16291 declension : 'Voice', 16292 parts : [ 16293 { 16294 letter: 'A', 16295 type: 'active' 16296 }, 16297 { 16298 letter: 'M', 16299 type: 'middle' 16300 }, 16301 { 16302 letter: 'P', 16303 type: 'passive'
16304 }, 16305 { 16306 letter: 'E', 16307 type: 'middle or passive' 16308 }, 16309 { 16310 letter: 'D', 16311 type: 'middle deponent' 16312 }, 16313 { 16314 letter: 'O', 16315 type: 'passive deponent' 16316 }, 16317 { 16318 letter: 'N', 16319 type: 'middle or passive deponent' 16320 }, 16321 { 16322 letter: 'Q', 16323 type: 'impersonal active' 16324 }, 16325 { 16326 letter: 'X', 16327 type: 'no voice' 16328 } 16329 ] 16330 }, 16331 16332 verbMoods: { 16333 declension : 'Mood', 16334 parts : [ 16335 { 16336 letter: 'I', 16337 type: 'indicative' 16338 }, 16339 { 16340 letter: 'S', 16341 type: 'subjunctive' 16342 }, 16343 { 16344 letter: 'O', 16345 type: 'optative' 16346 }, 16347 { 16348 letter: 'M', 16349 type: 'imperative' 16350 }, 16351 { 16352 letter: 'N', 16353 type: 'infinitive' 16354 }, 16355 { 16356 letter: 'P', 16357 type: 'participle' 16358 }, 16359 { 16360 letter: 'R', 16361 type: 'imperative participle' 16362 } 16363 ] 16364 } 16365 }, 16366 16367 16368 morphologies = { 16369 "robinson": 16370 [ 16371 { 16372 letter : 'N', 16373 type : 'Noun', 16374 declensions : [ 16375 robinsonElements.nounCase, 16376 robinsonElements.number, 16377 robinsonElements.gender 16378 ] 16379 }, 16380 { 16381 letter : 'V', 16382 type : 'Verb', 16383 declensions : [ 16384 robinsonElements.verbTense, 16385 robinsonElements.verbVoice, 16386 robinsonElements.verbMood, 16387 robinsonElements.person, 16388 robinsonElements.number 16389 ] 16390 } 16391 ], 16392 "morphhb": [ 16393 { 16394 letter : 'N', 16395 type : 'Noun', 16396 declensions : [ 16397 morphhbElements.nounTypes, 16398 morphhbElements.nounGender, 16399 morphhbElements.number, 16400 morphhbElements.state 16401 ] 16402 }, 16403 { 16404 letter : 'V', 16405 type : 'Verb', 16406 declensions : [ 16407 morphhbElements.verbStem, 16408 morphhbElements.verbType, 16409 morphhbElements.person, 16410 morphhbElements.number, 16411 morphhbElements.verbGender, 16412 morphhbElements.state 16413 ] 16414 } 16415 ] 16416 }, 16417 currentMorphology = morphologies[currentMorphologyKey]; 16418 16419 16420 var 16421 morphSelector = $('<div class="morph-selector"><table>' + 16422 '<thead>' + 16423 '<tr><th>Part of Speech</th></tr>' + 16424 '</thead>' + 16425 '<tbody>' + 16426 '<tr></tr>' + 16427 '</tbody>' + 16428 '</table></div>') 16429 .appendTo( $(document.body) ) 16430 .hide(), 16431 16432 morphSelectorTimer = null, 16433 16434 startMorphSelectorTimer = function() { 16435 stopMorphSelectorTimer(); 16436 16437 morphSelectorTimer = setTimeout(function() { 16438 morphSelector.hide(); 16439 }, 1000); 16440 }, 16441 stopMorphSelectorTimer = function() { 16442 if (morphSelectorTimer != null) { 16443 clearTimeout(morphSelectorTimer); 16444 morphSelectorTimer = null; 16445 } 16446 }; 16447 /* 16448 morphSelector 16449 .on('mouseleave', function() { 16450 startMorphSelectorTimer(); 16451 }) 16452 .on('mouseover', function() { 16453 stopMorphSelectorTimer(); 16454 }); 16455 */ 16456 16457 16458 // find table parts 16459 var morphSelectorHeaderRow = morphSelector.find('thead tr'), 16460 morphSelectorMainRow = morphSelector.find('tbody tr'), 16461 morphSelectorPOS = $('<td class="morph-pos"></td>').appendTo(morphSelectorMainRow); 16462 16463 16464 16465 function setMorphology(value) { 16466 currentMorphologyKey = value; 16467 currentMorphology = morphologies[currentMorphologyKey]; 16468 16469 drawPartsOfSpeech(); 16470 } 16471 16472 function drawPartsOfSpeech() { 16473 16474 //console.log('drawing', currentMorphology); 16475 16476 morphSelectorPOS.empty(); 16477 16478 // add parts of speech 16479 for (var i=0, il=currentMorphology.length; i<il; i++) { 16480 morphSelectorPOS.append( 16481 $('<span data-value="' + currentMorphology[i].letter + '">' + currentMorphology[i].type + '</span>') 16482 ); 16483 } 16484 16485 drawSelectedPartOfSpeech(); 16486 16487 } 16488 // do first one 16489 drawPartsOfSpeech(); 16490 16491 function updateMorphSelector(value) { 16492 16493 if (value.length == 0) { 16494 morphSelector.find('span').removeClass('selected'); 16495 drawSelectedPartOfSpeech(); 16496 return; 16497 } 16498 16499 var firstChar = value.substring(0,1); 16500 16501 // select it 16502 var partOfSpeechSpan = morphSelectorPOS.find('span[data-value="' + firstChar + '"]'); 16503 16504 if (partOfSpeechSpan.length > 0) { 16505 partOfSpeechSpan.addClass('selected') 16506 .siblings() 16507 .removeClass('selected'); 16508 16509 drawSelectedPartOfSpeech(); 16510 16511 // do the rest of the characters one by one 16512 if (value.length > 1) { 16513 var remainder = value.substr(1); 16514 if (remainder.substr(0,1) == '-') { 16515 remainder = remainder.substr(1); 16516 } 16517 16518 for (var i=0, il=remainder.length; i<il; i++) { 16519 var letter = remainder[i]; 16520 16521 morphSelectorMainRow 16522 .find('td:nth-child(' + (i+2) + ')') 16523 .find('span[data-value="' + letter + '"]') 16524 .addClass('selected'); 16525 } 16526 } 16527 } 16528 } 16529 16530 function drawSelectedPartOfSpeech() { 16531 16532 // clear out headers 16533 morphSelectorHeaderRow.find('th').first() 16534 .siblings() 16535 .remove(); 16536 16537 // clear out the main declensions 16538 morphSelectorMainRow.find('td').first() 16539 .siblings() 16540 .remove(); 16541
16542 // find whatever is selected 16543 var selectedSpan = morphSelectorPOS.find('.selected'), 16544 selectedValue = selectedSpan.attr('data-value'); 16545 16546 if (selectedSpan.length == 0) { 16547 return; 16548 } 16549 16550 // find part of speech 16551 var partOfSpeech = null; 16552 for (var i=0, il=currentMorphology.length; i<il; i++) { 16553 if (currentMorphology[i].letter === selectedValue) { 16554 partOfSpeech = currentMorphology[i]; 16555 break; 16556 } 16557 } 16558 16559 // now make the new siblings 16560 for (var i=0, il=partOfSpeech.declensions.length; i<il; i++) { 16561 // create td 16562 //var td = $('<td />').appendTo( selectedSpan.closest('tr') ); 16563 var declension = partOfSpeech.declensions[i], 16564 td = $('<th>' + declension.declension + '</th>').appendTo( morphSelectorHeaderRow ); 16565 td = $('<td />').appendTo( selectedSpan.closest('tr') ); 16566 16567 for (var j=0, jl=declension.parts.length; j<jl; j++) { 16568 $('<span data-value="' + declension.parts[j].letter + '"' + (declension.breakBefore ? ' data-breakbefore="true"' : '') + '>' + declension.parts[j].type + '</span>') 16569 .appendTo(td); 16570 } 16571 } 16572 16573 morphSelector.height( morphSelector.find('table').height() ); 16574 } 16575 16576 // selecting a span 16577 morphSelector.on('click', 'span', function() { 16578 var selectedSpan = $(this), 16579 selectedValue = selectedSpan.attr('data-value'); 16580 16581 16582 // select or delect a class 16583 if (selectedSpan.hasClass('selected')) { 16584 16585 selectedSpan 16586 .removeClass('selected') 16587 16588 } else { 16589 16590 selectedSpan 16591 .addClass('selected') 16592 .siblings() 16593 .removeClass('selected'); 16594 16595 } 16596 16597 // redraw the parts if needed 16598 if (selectedSpan.closest('td').hasClass('morph-pos')) { 16599 drawSelectedPartOfSpeech(); 16600 } 16601 16602 16603 // push new value to input 16604 var selector = '', 16605 lastPartOfSpeechWithSelection = -1; 16606
16607 // first see what the last position with a selected value is 16608 // [Verb] [ ] [ ] [Subjunctive] == 3 16609 selectedSpan.closest('tr').find('td').each(function(index, node) { 16610 var td = $(this), 16611 selectedDeclension = td.find('span.selected'); 16612 16613 // if something is selected then mark it 16614 if (selectedDeclension.length > 0) { 16615 lastPartOfSpeechWithSelection = index; 16616 } 16617 }); 16618 16619 // construct the text input from the selected positions 16620 selectedSpan.closest('tr').find('td').each(function(index, node) { 16621 16622 var td = $(this), 16623 selectedDeclension = td.find('span.selected'), 16624 includeBreak = td.find('span').first().attr('data-breakbefore') == "true"; 16625 16626 // if nothing selected add ? but only if there is something after it 16627 if (selectedDeclension.length == 0) { 16628 if (index <= lastPartOfSpeechWithSelection) { 16629 selector += (includeBreak ? '-' : '') + '?'; 16630 } 16631 } else { 16632 selector += (includeBreak ? '-' : '') + selectedDeclension.attr('data-value'); 16633 } 16634 }); 16635 16636 16637 if (morphSelector.currentInput != null) { 16638 morphSelector.currentInput.val(selector); 16639 } 16640 16641 //saveTransforms(); 16642 //resetTransforms(); 16643 16644 morphSelector.trigger('update', selector); 16645 }); 16646 16647 morphSelector.updateMorphSelector = updateMorphSelector; 16648 morphSelector.setMorphology = setMorphology; 16649 morphSelector.currentInput = null; 16650 16651 return morphSelector; 16652}; 16653 16654 16655var StylesSelector = function() { 16656 var selector = $('<div id="visualfilters-styles"></div>') 16657 .appendTo( $(document.body) ) 16658 .hide(); 16659 16660 16661 // STYLES GUIDE 16662 // create styles 16663 var 16664 textColors = ['#999999', '#ff3333', '#33cc33', '#3333cc"', '#ffcc33', '#ff33ff', '#33ffff'], 16665 textColorNames = ['Gray', 'Red', 'Green', 'Blue', 'Orange', 'Magenta', 'Cyan'], 16666 backgroundColors = ['#ccccccc','#ff9999', '#99ff99', '#9999ff"', '#ffcc33', '#ffff33', '#ff99ff', '#99ffff'], 16667 backgroundColorNames = ['Gray', 'Red', 'Green', 'Blue', 'Orange', 'Yellow', 'Magenta', 'Cyan'], 16668 16669 styleCss = [], 16670 styleNames = [] 16671 ; 16672 16673 16674 // text 16675 for (var i=0, il=textColors.length; i<il; i++) { 16676 styleCss.push('color: ' + textColors[i] + ';'); 16677 styleNames.push(textColorNames[i] + ' Text'); 16678 } 16679 16680 // highlight 16681 for (var i=0, il=backgroundColors.length; i<il; i++) { 16682 styleCss.push('background-color: ' + backgroundColors[i] + ';'); 16683 styleNames.push(backgroundColorNames[i] + ' Highlight'); 16684 } 16685 16686 // underline 16687 for (var i=0, il=textColors.length; i<il; i++) { 16688 styleCss.push('border-bottom: solid 1px ' + textColors[i] + ';'); 16689 styleNames.push(textColorNames[i] + ' Underline'); 16690 } 16691 16692 // custom 16693 styleCss.push('color:red;text-shadow:yellow 0 0 2px;'); 16694 styleNames.push('Fire'); 16695 16696 styleCss.push('text-shadow:0 0 1px gray;'); 16697 styleNames.push('Shadow'); 16698 16699 styleCss.push('font-weight: bold;'); 16700 styleNames.push('Bold'); 16701 16702 styleCss.push('border: 2px dashed #333;'); 16703 styleNames.push('Dashed Box'); 16704 16705 styleCss.push('text-transform: uppercase;'); 16706 styleNames.push('Capitalize'); 16707 16708 styleCss.push('font-variant: small-caps;'); 16709 styleNames.push('Small Caps'); 16710 16711 16712 // draw styles 16713 for (var i=0, il=styleCss.length; i<il; i++) { 16714 selector.append( 16715 $('<div><span data-style="' + styleCss[i] + '" style="' + styleCss[i] + '">' + styleNames[i] + '</span></div>') 16716 ); 16717 } 16718 16719 return selector; 16720} 16721 16722 16723 16724/********* 16725* ../app/js/plugins/crossreferences.js 16726**********/ 16727 16728 16729var CrossReferencePopupPlugin = function(app) { 16730 16731 if (!sofia.config.enableNotesPopupPlugin) { 16732 return; 16733 } 16734 16735 var referencePopup = new InfoWindow('CrossReferencePopup'); 16736 16737 referencePopup.container.css({zIndex: 1000}); 16738 16739 function getFragmentidFromNode(node) { 16740 var possibleTexts = [node.attr('data-id'), node.attr('title'), node.html()], 16741 fragmentid = null; 16742 16743 for (var i=0, il=possibleTexts.length; i<il; i++) { 16744 var text = possibleTexts[i]; 16745 16746 if (typeof text != 'undefined' && text != null) { 16747 var bref = new bible.Reference(text.split(';')[0].trim()); 16748 if (typeof bref.toSection != 'undefined') { 16749 fragmentid = bref.toSection(); 16750 16751 break; 16752 } 16753 } 16754 16755 } 16756 16757 return fragmentid; 16758 } 16759 16760 sofia.globals.handleBibleRefClick = function(e) { 16761 var link = $(this), 16762 newfragmentid = getFragmentidFromNode(link); 16763 16764 // where are we? 16765 var currentLocationData = PlaceKeeper.getFirstLocation(); 16766 16767 // store the current one 16768 TextNavigation.locationChange(currentLocationData.fragmentid); 16769 16770 16771 if (newfragmentid != null && newfragmentid != '') { 16772 16773 TextNavigation.locationChange(newfragmentid); 16774 16775 ext.trigger('globalmessage', { 16776 type: 'globalmessage', 16777 target: this, 16778 data: { 16779 messagetype:'nav', 16780 type: 'bible', 16781 locationInfo: { 16782 fragmentid: newfragmentid, 16783 sectionid: newfragmentid.split('_')[0], 16784 offset: 0 16785 } 16786 } 16787 });
16788 } 16789 }; 16790 16791 16792 sofia.globals.handleBibleRefMouseover = function(e, textid) { 16793 var link = $(this), 16794 fragmentid = getFragmentidFromNode(link); 16795 16796 if (fragmentid != null) { 16797 16798 var sectionid = fragmentid.split('_')[0]; 16799 16800 if (typeof textid == 'undefined') { 16801 16802 if (link.closest('.section').hasClass('commentary')) { 16803 16804 textid = $('.BibleWindow:first .section:first').attr('data-textid'); 16805 16806 } else { 16807 textid = link.closest('.section').attr('data-textid'); 16808 } 16809 } 16810 16811 16812 console.log('hover', textid, sectionid, fragmentid); 16813 16814 TextLoader.getText(textid, function(textInfo) { 16815 16816 TextLoader.loadSection(textInfo, sectionid, function(contentNode) { 16817 16818 var verse = contentNode.find('.' + fragmentid), 16819 html = ''; 16820 16821 verse.find('.note').remove(); 16822 16823 verse.each(function() { 16824 html += $(this).html(); 16825 }); 16826 16827 16828 referencePopup.body.html( html ); 16829 referencePopup.show(); 16830 referencePopup.position(link); 16831 16832 }); 16833 }); 16834 16835 } 16836 } 16837 16838 sofia.globals.handleBibleRefMouseout = function(e) { 16839 16840 referencePopup.hide(); 16841 } 16842 16843 16844 $('.windows-main').on('click','.bibleref, .xt', sofia.globals.handleBibleRefClick); 16845 16846 if (!Detection.hasTouch) { 16847 $('.windows-main').on('mouseover','.bibleref, .xt', sofia.globals.handleBibleRefMouseover); 16848 $('.windows-main').on('mouseout','.bibleref, .xt', sofia.globals.handleBibleRefMouseout); 16849 } 16850 16851 16852 var ext = { 16853 getData: function() { 16854 return null; 16855 } 16856 }; 16857 ext = $.extend(true, ext, EventEmitter); 16858 16859 return ext; 16860}; 16861 16862sofia.plugins.push('CrossReferencePopupPlugin'); 16863 16864 16865 16866/********* 16867* ../app/js/plugins/notes.js 16868**********/ 16869sofia.config = $.extend(sofia.config, { 16870 16871 enableNotesPopupPlugin: true 16872 16873}); 16874 16875var NotesPopupPlugin = function(app) { 16876 16877 if (!sofia.config.enableNotesPopupPlugin) { 16878 return; 16879 } 16880 16881 var notesPopup = new InfoWindow('NotesPopup'); 16882 16883 notesPopup.body.on('click', '.bibleref, .xt', function(e) { 16884 16885 sofia.globals.handleBibleRefClick.call(this, e); 16886 16887 notesPopup.hide(); 16888 16889 }); 16890 16891 16892 if (!Detection.hasTouch) { 16893 notesPopup.body.on('mouseover', '.bibleref, .xt', function(e) { 16894 sofia.globals.handleBibleRefMouseover.call(this, e, $(notesPopup.currentWord).closest('.section').attr('data-textid') ); 16895 }); 16896 16897 notesPopup.body.on('mouseout', '.bibleref, .xt', function(e) { 16898 sofia.globals.handleBibleRefMouseout.call(this, e); 16899 }); 16900 } 16901 16902 $('.windows-main').on('click','.note .key, .cf .key', function(e) { 16903 16904 e.preventDefault(); 16905 16906 console.log('notes'); 16907 16908 var key = $(this); 16909 16910 16911 // hide if second click 16912 if (notesPopup.container.is(':visible') && notesPopup.currentWord == this) { 16913 notesPopup.hide(); 16914 notesPopup.currentWord == null; 16915 return; 16916 } 16917 notesPopup.currentWord = this; 16918 16919 // clone and attach content 16920 var content = key.parent().find('.text').clone(); 16921 16922 notesPopup.body 16923 .html('') 16924 .append(content); 16925 16926 // show popup 16927 notesPopup.show(); 16928 notesPopup.position(key); 16929 16930 return false; 16931 }); 16932 16933 16934 16935 var ext = { 16936 getData: function() { 16937 return null; 16938 } 16939 }; 16940 ext = $.extend(true, ext, EventEmitter); 16941 16942 return ext; 16943}; 16944 16945sofia.plugins.push('NotesPopupPlugin'); 16946 16947 16948 16949/********* 16950* ../app/js/plugins/googleanalytics.js 16951**********/ 16952sofia.config = $.extend(sofia.config, { 16953 // Google Analytics key 16954 gaKey: '', 16955 16956 // Google URL 16957 gaUrl: '' 16958}); 16959 16960 16961var GoogleAnalyticsPlugin = function(app) { 16962 16963 /*
vendor: 392 bytes, lines 16963-16980
16963 16964 (function(i,s,o,g,r,a,m){ 16965 16966 i['GoogleAnalyticsObject']=r; 16967 i[r]= i[r]|| function(){ 16968 (i[r].q = i[r].q || [] ).push(arguments) 16969 }, 16970 i[r].l=1*new Date(); 16971 16972 a=s.createElement(o), 16973 m=s.getElementsByTagName(o)[0]; 16974 16975 a.async=1; 16976 a.src=g; 16977 m.parentNode.insertBefore(a,m) 16978 })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); 16979 16980 ga('create', '
16980UA-3734687-19
vendor: 48 bytes, lines 16980-16982
16980', 'inscript.org'); 16981 ga('send', 'pageview'); 16982
16982*/ 16983 16984 if (sofia.config.enableOnlineSources && sofia.config.gaKey && sofia.config.gaKey != '') { 16985 loadGoogleAnalytics(); 16986 } 16987 16988 function loadGoogleAnalytics() { 16989 16990 window['GoogleAnalyticsObject'] = 'ga'; 16991 window['ga'] = window['ga'] || function() { 16992 (window['ga'].q = window['ga'].q || [] ).push(arguments) 16993 } 16994 window['ga'].l = 1*new Date(); 16995 16996 var script = document.createElement('script'); 16997 var lastScript = document.getElementsByTagName('script')[0]; 16998 16999 script.async = true; 17000 script.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'www.google-analytics.com/analytics.js'; 17001 lastScript.parentNode.insertBefore(script, lastScript); 17002 17003 ga('create', sofia.config.gaKey, (sofia.config.gaUrl) ? sofia.config.gaUrl : ''); 17004 ga('send', 'pageview'); 17005 } 17006 17007 17008 // EVENT MODEL 17009 17010 /* 17011 var ext = { 17012 sendMessage: function() {} 17013 }; 17014 ext = $.extend(true, ext, EventEmitter); 17015 17016 // listen for times when the user changes location 17017 ext.on('globalmessage', function(e) { 17018 17019 console.log('GOOGLE', e.data); 17020 17021 if (e.data.messagetype == 'usernav') { 17022 17023 17024 17025 if (ga) { 17026 //ga('send', 'event', 'usernav', 'click', 'nav buttons', 4); 17027 } 17028 17029 } 17030 17031 }); 17032 17033 return ext; 17034 */ 17035 17036 // DIRECT MODEL 17037 17038 var analytics = { 17039 17040 record: function(category, action, label, value) { 17041 17042 //console.log('GOOGLE', arguments); 17043 17044 if (sofia.config.enableOnlineSources && typeof ga != 'undefined') { 17045 ga('send', 'event', category, action, label, value); 17046 } 17047 17048 } 17049 17050 } 17051 17052 sofia.analytics = analytics; 17053 17054}; 17055 17056sofia.plugins.push('GoogleAnalyticsPlugin'); 17057 17058 17059 17060/********* 17061* ../app/js/plugins/mediaplugin.js 17062**********/ 17063sofia.config = $.extend(sofia.config, { 17064 17065 enableMediaLibraryPlugin: true 17066 17067}); 17068 17069var MediaLibraryPlugin = function(app) { 17070 17071 if (!sofia.config.enableMediaLibraryPlugin) { 17072 return; 17073 } 17074 17075 var mediaLibraries = null, 17076 mediaPopup = new InfoWindow('mediapopup'), 17077 contentToProcess = []; 17078 17079 //console.log('MediaLibraryPlugin startup', MediaLibrary.getMediaLibraries); 17080 17081 MediaLibrary.getMediaLibraries(function(data) { 17082 mediaLibraries = data; 17083 17084 setupMediaEvents(); 17085 17086 addMedia(); 17087 }); 17088 17089 function setupMediaEvents() { 17090 17091 // handle clicks 17092 $('.windows-main').on('click', '.mediathumb', function(e) { 17093 17094 // determine what kind of media this is 17095 var 17096 icon = $(this), 17097 mediaFolder = icon.attr('data-mediafolder'), 17098 verse = icon.closest('.verse, .v'), 17099 verseid = verse.attr('data-id'), 17100 reference = new bible.Reference(verseid).toString(), 17101 mediaLibrary = null, 17102 mediaForVerse = null; 17103 17104 // find library 17105 for (var i=0, il=mediaLibraries.length; i<il; i++ ) { 17106 var ml = mediaLibraries[i] 17107 17108 if (ml.folder == mediaFolder) { 17109 mediaLibrary = ml; 17110 break; 17111 } 17112 17113 } 17114 17115 console.log('media click', mediaLibrary); 17116 17117 17118 mediaForVerse = mediaLibrary.data[verseid]; 17119 17120 switch (mediaLibrary.type) { 17121 17122 case 'image': 17123 // clear it out! 17124 mediaPopup.body.html(''); 17125 17126 var html = ''; 17127 for (var i=0, il=mediaForVerse.length; i<il; i++ ) { 17128 var mediaInfo = mediaForVerse[i], 17129 fullUrl = sofia.config.baseContentUrl + 'content/' + 'media/' + mediaLibrary.folder + '/' + mediaInfo.filename + '.' + mediaInfo.exts[0], 17130 thumbUrl = fullUrl.replace('.jpg', '-thumb.jpg'); 17131 17132 html += '<li>' + 17133 '<a href="' + fullUrl + '" target="_blank">' + 17134 '<img src="' + thumbUrl + '" />' + 17135 '</a>' + 17136 '</li>'; 17137 } 17138 17139 mediaPopup.body.append('<strong>' + reference.toString() + '</strong>'); 17140 mediaPopup.body.append($('<ul class="inline-image-library-thumbs">' + html + '</ul>')); 17141 17142 17143 //mediaPopup.center().show(); 17144 mediaPopup.setClickTargets( [icon] ); 17145 mediaPopup.position( icon ).show(); 17146 17147 break; 17148 17149 case 'video': 17150 var mediaInfo = mediaForVerse[0], 17151 videoUrl = sofia.config.baseContentUrl + 'content/' + 'media/' + mediaLibrary.folder + '/' + mediaInfo.filename + '.' + mediaInfo.exts[0]; 17152 17153 sofia.globals.showVideo(videoUrl, mediaInfo.name); 17154 17155 break; 17156 17157 case 'jfm': 17158 17159 var lang = icon.closest('.section').attr('data-lang3'), 17160 mediaInfo = mediaForVerse[0], 17161 videoUrl = JesusFilmMediaApi.getPlayer(lang, mediaInfo.filename, function(iframeUrl, iframeLang) { 17162 17163 if (iframeUrl != null) { 17164 sofia.globals.showIframe(iframeUrl, mediaInfo.name + ' (' + iframeLang + (lang != iframeLang ? '/' + lang : '') + ')'); 17165 } 17166 17167 });
17168 17169 break; 17170 17171 } 17172 }); 17173 17174 } 17175 17176 // process chapters, add image icon to verses 17177 function addMedia() { 17178 17179 //console.log('addMedia',mediaLibraries); 17180 17181 if (mediaLibraries == null) { 17182 return; 17183 } 17184 17185 while (contentToProcess.length > 0) { 17186 var content = contentToProcess.pop(); 17187 17188 if (content.data('has-media') != undefined) { 17189 continue; 17190 } 17191 17192 // add images to verses 17193 content.find('.verse, .v').each(function() { 17194 var verse = $(this), 17195 verseid = verse.attr('data-id'); 17196 17197 // make sure we're just doing the first verse 17198 verse = verse.closest('.section').find('.' + verseid).first(); 17199 17200 if (verseid == 'LK1_1') { 17201 // //console.log('check'); 17202 } 17203 17204 if (!verse.hasClass('has-media')) { 17205 // check all libraries 17206 for (var i=0, il=mediaLibraries.length; i<il; i++) { 17207 var mediaLibrary = mediaLibraries[i], 17208 iconClassName = mediaLibrary.iconClassName, 17209 mediaForVerse = mediaLibrary.data ? mediaLibrary.data[verseid] : undefined; 17210 17211 // add media 17212 if (typeof mediaForVerse != 'undefined') { 17213 17214 // check if it's already been added 17215 //if (verse.closest('.chapter').find('.' + verseid).find('.' + iconClassName).length == 0) { 17216 17217 var icon = $('<span class="inline-icon ' + iconClassName + ' mediathumb" data-mediafolder="' + mediaLibrary.folder + '"></span>'), 17218 verseNumber = verse.find('.verse-num, v-num'); 17219 17220 if (verseNumber.length > 0) { 17221 verseNumber.after(icon); 17222 } else { 17223 icon.prependTo(verse); 17224 } 17225 //} 17226 } 17227 } // libraries loop 17228 17229 17230 verse.closest('.section').find('.' + verseid).addClass('has-media'); 17231 17232 } 17233 17234 }); // verse loop 17235 17236 17237 17238 content.data('has-media', true); 17239 17240 } // while 17241 17242 } 17243 17244 mediaPopup.body.on('click', '.inline-image-library-thumbs a', sofia.globals.mediaImageClick); 17245 mediaPopup.body.on('click', '.inline-video-library-thumbs a', sofia.globals.mediaVideoClick); 17246 17247 17248 var ext = {}; 17249 ext = $.extend(true, ext, EventEmitter); 17250 ext.on('message', function(e) { 17251 if (e.data.messagetype == 'textload' && e.data.type == 'bible') { 17252 //store 17253 contentToProcess.push(e.data.content); 17254 // run 17255 addMedia(); 17256 } 17257 }); 17258 17259 return ext; 17260} 17261 17262sofia.plugins.push('MediaLibraryPlugin'); 17263 17264 17265 17266/********* 17267* ../app/js/windows/textwindow.js 17268**********/ 17269 17270var TextWindow = function(id, parent, init_data, text_type) { 17271 17272 17273 // detect backflip 17274 var 17275 ua = window.navigator.userAgent.toLowerCase(), 17276 supports3d = !(ua.indexOf('unix') > -1 17277 || ua.indexOf('linux') > -1 17278 || ua.indexOf('opera') > -1 17279 //|| ua.indexOf('msie') > -1 17280 //|| ua.indexOf('trident') > -1 17281 //|| ua.indexOf('explorer') > -1 17282 ); 17283 17284 17285 var 17286 container = 17287 $('<div class="scroller-container' + (supports3d ? ' supports-3d' : '') + '">'+ 17288 '<div class="window-header scroller-header">'+ 17289 '<div class="scroller-header-inner">'+ 17290 //(Detection.hasTouch ? '<span class="header-input text-nav" ></span>' : '<input type="text" class="header-input text-nav" />') + 17291 '<input type="text" class="app-input text-nav" />' + 17292 '<div class="app-list text-list"></div>'+ 17293 '<span class="header-icon info-button"></span>'+ 17294 '<span class="header-icon audio-button"></span>'+ 17295 '</div>'+ 17296 '</div>'+ 17297 '<div class="scroller-flipper">' + 17298 '<div class="scroller-main">' + 17299 '<div class="scroller-text-wrapper reading-text"><div class="loading-indicator" style="height:' + parent.node.height() + 'px;"></div></div>' + 17300 '</div>'+ 17301 '<div class="scroller-info">Text info</div>' + 17302 '</div>' + 17303 '</div>').appendTo(parent.node), 17304 17305 // dom nodes 17306 flipper = container.find('.scroller-flipper'), 17307 header = container.find('.scroller-header'), 17308 main = container.find('.scroller-main'), 17309 info = container.find('.scroller-info'), 17310 infoBtn = container.find('.info-button'), 17311 wrapper = container.find('.scroller-text-wrapper'), 17312 navui = header.find('.text-nav'), 17313 textlistui = header.find('.text-list'), 17314 17315 // objects 17316 textChooser = sofia.globalTextChooser, 17317 textNavigator = sofia.globalTextNavigator, 17318 scroller = new Scroller(main), 17319 17320 audioui = container.find('.audio-button'), 17321 audioController = new AudioController(id, container, audioui, scroller), 17322 17323 // settings 17324 currentTextInfo = null, 17325 currentLocationInfo = null, 17326 hasFocus = false, 17327 isInitialized = false; 17328 17329 17330 /* 17331 if (Detection.hasTouch) { 17332 navui.prop('disabled',true); 17333 } 17334 */ 17335 17336 17337 17338 infoBtn.on('click', function() { 17339 17340 textChooser.hide(); 17341 textNavigator.hide(); 17342 17343 flipper.toggleClass('showinfo'); 17344 17345 if (flipper.hasClass('showinfo')) { 17346 17347 if (typeof currentTextInfo.aboutHtml != 'undefined') { 17348 17349 var aboutDoc = $(currentTextInfo.aboutHtml); 17350 17351 info.html(''); 17352 info.append( aboutDoc ); 17353 17354 } else { 17355 17356 sofia.ajax({ 17357 dataType: 'text', 17358 url: 'content/texts/' + currentTextInfo.id + '/about.html', 17359 success: function(htmlString) { 17360 17361 var breakTag = '<body', 17362 fixedHtml = htmlString.indexOf(breakTag) > -1 ? 17363 breakTag + htmlString.split(breakTag)[1] : 17364 '', 17365 aboutDoc = $(fixedHtml); 17366 17367 info.html(''); 17368 info.append( aboutDoc ); 17369 17370 // store for next time 17371 currentTextInfo.aboutHtml = fixedHtml; 17372 } 17373 }); 17374 } 17375 } 17376 }); 17377 17378 17379 textlistui 17380 .on('click', function(e) { 17381 17382 //console.log('clicked'); 17383 17384 if (flipper.hasClass('showinfo')) { 17385 flipper.removeClass('showinfo') 17386 } 17387 17388 // if this is selected, then toggle 17389 if (textChooser.getTarget() == textlistui) {
17390 textChooser.toggle(); 17391 } else { 17392 textChooser.setTarget(container, textlistui, text_type); 17393 textChooser.setTextInfo(currentTextInfo); 17394 textChooser.show(); 17395 } 17396 }); 17397 17398 navui 17399 .on('click', function(e) { 17400 17401 if (Detection.hasTouch) { 17402 this.blur(); 17403 } 17404 17405 if (flipper.hasClass('showinfo')) { 17406 flipper.removeClass('showinfo') 17407 } 17408 17409 if (textNavigator.getTarget() == navui) { 17410 textNavigator.toggle(); 17411 } else { 17412 textNavigator.setTarget(container, navui); 17413 textNavigator.setTextInfo(currentTextInfo); 17414 textNavigator.show(); 17415 } 17416 }) 17417 .on('keypress', function(e) { 17418 if (e.keyCode == 13) { 17419 var 17420 userinput = navui.val(), 17421 bibleref = new bible.Reference(userinput), 17422 fragmentid = (bibleref.toSection) ? bibleref.toSection() : '', 17423 sectionid = fragmentid.split('_')[0]; 17424 17425 if (sectionid != '') { 17426 17427 if (sofia.analytics) { 17428 sofia.analytics.record('usernav', 'input', sectionid + ':' + currentTextInfo.id); 17429 } 17430 17431 TextNavigation.locationChange(fragmentid); 17432 17433 scroller.load('text', sectionid, fragmentid); 17434 textNavigator.hide(); 17435 17436 navui.val(bibleref.toString()) 17437 navui[0].blur(); 17438 } 17439 } 17440 }); 17441 17442 textNavigator.on('change', function (e) { 17443 //console.log('scrollerapp:navigator:change', e); 17444 17445 if (e.data.target != navui) { 17446 return; 17447 } 17448 17449 if (sofia.analytics) { 17450 sofia.analytics.record('usernav', 'menu', e.data + ':' + currentTextInfo.id); 17451 } 17452 17453 TextNavigation.locationChange(e.data.sectionid); 17454 17455 // load new content 17456 scroller.load('text', e.data.sectionid); 17457 }); 17458 17459 textChooser.on('change', function (e) { 17460 17461 if (e.data.target != textlistui) { 17462 return; 17463 } 17464 17465 var newTextInfo = e.data.textInfo; 17466 17467 // ALWAYS UPDATE: for first load 17468 // update version name 17469 //textlistui.html( newTextInfo.abbr ); 17470 17471 setTextInfoUI(newTextInfo); 17472 17473 parent.tab.find('span').html( newTextInfo.abbr ); 17474 17475 // update the navigator with the latest header 17476 textNavigator.setTextInfo(newTextInfo); 17477 17478 audioController.setTextInfo(newTextInfo); 17479 17480 // if it has *changed* then we need to reload the text in the scroller 17481 if (currentTextInfo == null || newTextInfo.id != currentTextInfo.id) { 17482 17483 // store 17484 currentTextInfo = newTextInfo; 17485 17486 var oldLocationInfo = scroller.getLocationInfo(), 17487 nearestSectionId = oldLocationInfo != null ? oldLocationInfo.sectionid : currentTextInfo.sections[0]; 17488 17489 // load new text 17490 wrapper.html(''); 17491 scroller.setTextInfo(currentTextInfo); 17492 scroller.load( 'text', nearestSectionId); 17493 } 17494 17495 }); 17496 17497 scroller.on('scroll', update_textnav); 17498 scroller.on('locationchange', update_textnav); 17499 scroller.on('load', update_textnav); 17500 scroller.on('globalmessage', function(e) { 17501 if ((e.data.messagetype == 'nav' && hasFocus) || e.data.messagetype != 'nav') { 17502 17503 //console.log('sending global'); 17504 if (ext) { 17505 ext.trigger('globalmessage', {type: e.type, target: ext, data: e.data}); 17506 } 17507 } 17508 }); 17509 17510 // show the current position to the user 17511 function update_textnav(e) { 17512 17513 var newLocationInfo = scroller.getLocationInfo(); 17514 17515 // found a fragment 17516 if (newLocationInfo != null) { 17517 17518 currentLocationInfo = newLocationInfo; 17519 navui 17520 .html( currentLocationInfo.label ) 17521 .val( currentLocationInfo.label ); 17522 17523 ext.trigger('settingschange', {type: 'settingschange', target: this, data: getData() }); 17524 } 17525 17526 17527 } 17528 17529 // START UP 17530 17531 function init() { 17532 17533 // TEMP 17534 navui.html('Reference').val('Reference'); 17535 textlistui.html('Version'); 17536 17537 console.log('textsindow init', init_data, isInitialized, text_type); 17538 17539 if (init_data == null) { 17540 return; 17541 } 17542 17543 if (typeof init_data.textid == 'undefined' || init_data.textid == '') { 17544 init_data.textid = sofia.config.newBibleWindowVersion; 17545 } 17546 17547 // load the text specified by the init data 17548 TextLoader.getText(init_data.textid, 17549 17550 // success 17551 function(loadedTextInfo) { 17552 17553 // store this setting 17554 currentTextInfo = loadedTextInfo; 17555 17556 isInitialized = true; 17557 17558 startup(); 17559 }, 17560 17561 // error handler 17562 function() { 17563 17564 console.log('ERROR', init_data.textid, 'doesnt exist'); 17565 17566 // load all possible versions 17567 TextLoader.loadTexts(function(textInfoData) { 17568 17569 // find a text with the same language 17570 // REMOVED b/c the ids no longer use the lang_version syntax 17571 /* 17572 var newTextInfo = null, 17573 lang = init_data.textid.toString().split('-')[0].split('_')[0]; 17574 17575 for (var i=0, il=textInfoData.length; i<il; i++) { 17576 var textInfo = textInfoData[i]; 17577 17578 if (textInfo.type == text_type && (textInfo.lang == lang || textInfo.id.substring(0, lang.length) == lang)) { 17579 newTextInfo = textInfo; 17580 break; 17581 } 17582 } 17583 */ 17584 var textsWithType = textInfoData.filter(function(ti) { return ti.type == text_type; }); 17585 17586 if (textsWithType.length > 0) { 17587 newTextInfo = textsWithType[0]; 17588 } 17589 17590 // still nothing 17591 if (newTextInfo == null) { 17592 newTextInfo = textInfoData[0]; 17593 } 17594 17595 // let's try again with first one 17596 TextLoader.getText(newTextInfo.id, function(loadedTextInfo) { 17597 // store this setting 17598 currentTextInfo = loadedTextInfo; 17599 17600 isInitialized = true; 17601 17602 startup(); 17603 17604 });
17605 17606 }); 17607 17608 }); 17609 } 17610 17611 function setTextInfoUI(textinfo) { 17612 17613 17614 switch (textinfo.type ) { 17615 default: 17616 textlistui.removeClass('app-list-image'); 17617 textlistui.html( textinfo.abbr ); 17618 break; 17619 case 'deafbible': 17620 textlistui.addClass('app-list-image'); 17621 textlistui.html( '<img src="content/texts/' + textinfo.id + '/' + textinfo.id + '.png" />' ); 17622 break; 17623 17624 } 17625 17626 } 17627 17628 function startup() { 17629 17630 // send to objects 17631 textChooser.setTextInfo(currentTextInfo); 17632 setTextInfoUI(currentTextInfo); 17633 parent.tab.find('span').html( currentTextInfo.abbr ); 17634 textNavigator.setTextInfo(currentTextInfo); 17635 audioController.setTextInfo(currentTextInfo); 17636 17637 scroller.setTextInfo(currentTextInfo); 17638 17639 if (!init_data.sectionid && init_data.fragmentid) { 17640 init_data.sectionid = init_data.fragmentid.split('_')[0]; 17641 } 17642 17643 scroller.load('text', init_data.sectionid, init_data.fragmentid); 17644 17645 } 17646 17647 init(); 17648 17649 function size(width, height) { 17650 17651 //console.log('winsize',id,width,height); 17652 17653 container 17654 .outerWidth(width) 17655 .outerHeight(height); 17656 17657 flipper 17658 .outerWidth(width) 17659 .outerHeight( container.height() - header.outerHeight(true)); 17660 17661 main 17662 .outerWidth(width) 17663 .outerHeight( container.height() - header.outerHeight(true)); 17664 17665 info 17666 .outerWidth(width) 17667 .outerHeight( container.height() - header.outerHeight(true)); 17668 17669 textChooser.size(width, height); 17670 textNavigator.size(width, height); 17671 } 17672 17673 function getData() { 17674 // get data 17675 if (currentTextInfo == null) { 17676 currentTextInfo = textChooser.getTextInfo(); 17677 } 17678 if (currentLocationInfo == null) { 17679 currentLocationInfo = scroller.getLocationInfo(); 17680 } 17681 17682 // if not ready, then kill it 17683 if (currentTextInfo == null || currentLocationInfo == null) { 17684 return null; 17685 } 17686 17687 var data = { 17688 // textinfo 17689 textid: currentTextInfo.providerid, 17690 abbr: currentTextInfo.abbr, 17691 17692 // location info 17693 sectionid: currentLocationInfo.sectionid, 17694 fragmentid: currentLocationInfo.fragmentid, 17695 label: currentLocationInfo.label, 17696 labelTab: currentTextInfo.abbr, 17697 labelLong: currentLocationInfo.labelLong, 17698 hasFocus: hasFocus, 17699 params: { 17700 'win': text_type, 17701 'textid': currentTextInfo.providerid, 17702 'fragmentid': currentLocationInfo.fragmentid 17703 } 17704 }; 17705 17706 return data; 17707 } 17708 17709 17710 function close() { 17711 17712 textChooser.close(); 17713 textNavigator.close(); 17714 audioController.close(); 17715 17716 ext.clearListeners(); 17717 } 17718 17719 var ext = { 17720 size: size, 17721 getData: getData, 17722 close: close 17723 } 17724 ext = $.extend(true, ext, EventEmitter); 17725 17726 ext.on('focus', function() { 17727 hasFocus = true; 17728 }); 17729 ext.on('blur', function() { 17730 hasFocus = false; 17731 }); 17732 ext.on('message', function(e) { 17733 var data = e.data; 17734 17735 if (data.messagetype == 'nav' && (data.type == 'bible' || data.type == 'commentary'|| data.type == 'videobible' || data.type == 'deafbible') && data.locationInfo != null) { 17736 //console.log(id, data.locationInfo.fragmentid, data.locationInfo.offset) 17737 scroller.scrollTo( data.locationInfo.fragmentid, data.locationInfo.offset); 17738 } 17739 }); 17740 17741 return ext; 17742}; 17743 17744var BibleWindow = function(id, node, init_data) { 17745 return new TextWindow(id, node, init_data, 'bible'); 17746}; 17747 17748sofia.initMethods.push(function() { 17749 17750 sofia.windowTypes.push( { 17751 className:'BibleWindow', 17752 param: 'bible', 17753 paramKeys: { 17754 'textid': 't', 17755 'fragmentid':'v' 17756 }, 17757 init: { 17758 'textid':sofia.config.newBibleWindowVersion, 17759 'fragmentid':sofia.config.newBibleWindowVerse 17760 } 17761 }); 17762 17763}); 17764 17765var CommentaryWindow = function(id, node, init_data) { 17766 return new TextWindow(id, node, init_data, 'commentary'); 17767}; 17768 17769sofia.initMethods.push(function() { 17770 17771 if (typeof sofia.config.newCommentaryWindowTextId != 'undefined') { 17772 17773 sofia.windowTypes.push({ 17774 className:'CommentaryWindow', 17775 param: 'commentary', 17776 paramKeys: { 17777 'textid': 't', 17778 'fragmentid':'v' 17779 }, 17780 init: { 17781 'textid':sofia.config.newCommentaryWindowTextId, 17782 'fragmentid':'' 17783 } 17784 }); 17785 } 17786}); 17787 17788 17789 17790/********* 17791* ../app/js/windows/searchwindow.js 17792**********/ 17793 17794 17795// test/sample types 17796var SearchWindow = function(id, parent, init_data) { 17797
17798 var header = $('<div class="window-header search-header" >' + 17799 17800 '<input type="text" class="search-text app-input i18n" data-i18n="[placeholder]windows.search.placeholder" />' + 17801 '<div class="text-list app-list" style=""> </div>' + 17802 17803 '<div class="search-options-button header-icon" style=""></div>' + 17804 17805 '<input type="button" value="Search" data-i18n="[value]windows.search.button" class="search-button header-button i18n" />' + 17806 17807 //'<select class="search-list header-list" style="max-width: 100px; top: 22px; right: 5px; position: absolute;" ></select>' + 17808 '</div>').appendTo(parent.node), 17809 main = $('<div class="search-main"><div class="search-wrapper">' + 17810 '<div class="search-top">' + 17811 //'<h2></h2>' + 17812 '<div class="search-progress-bar">' + 17813 '<div class="search-progress-bar-inner"></div>' + 17814 '<span class="search-progress-bar-label"></span>' + 17815 '</div>' + 17816 '<div class="search-visual"></div>' + 17817 '<span class="search-visual-label"></span>' + 17818 '<div class="search-lemma-info"></div>' + 17819 '<div class="search-usage"></div>' + 17820 '</div>' + 17821 '<div class="search-results reading-text"></div>' + 17822 '</div></div>').appendTo(parent.node), 17823 footer = $('<div class="search-footer window-footer"></div>').appendTo(parent.node), 17824 17825 topLemmaInfo = main.find('.search-lemma-info').hide(), 17826 topVisual = main.find('.search-visual').hide(), 17827 topVisualLabel = main.find('.search-visual-label'), 17828 topUsage = main.find('.search-usage'), 17829 topBlock = main.find('.search-top'), 17830 topBlockTitle = topBlock.find('h2'), 17831 searchProgressBar = topBlock.find('.search-progress-bar').hide(), 17832 searchProgressBarInner = topBlock.find('.search-progress-bar-inner'), 17833 searchProgressBarLabel = topBlock.find('.search-progress-bar-label'), 17834 17835 resultsBlock = main.find('.search-results'), 17836 input = header.find('.search-text'), 17837 button = header.find('.search-button'), 17838 17839 textlistui = header.find('.text-list'), 17840 //textChooser = new TextChooser(parent.node, textlistui, 'bible'), 17841 textChooser = sofia.globalTextChooser, 17842 textChooserFocused = false, 17843 17844 searchOptionsButton = header.find('.search-options-button'), 17845 17846 divisionChooser = $('<div class="search-division-chooser">' + 17847 '<div class="search-division-header">' + i18n.t('windows.search.options') + '</div>' + 17848 '<div class="search-division-main"></div>' + 17849 '</div>').appendTo($('body')), 17850 17851 selectedTextInfo = null, 17852 17853 // used for redrawing divisions 17854 previousTextInfo = null, 17855 17856 currentResults = null, 17857 searchIndexesData = null, 17858 searchTermsRegExp = null, 17859 isLemmaSearch = false 17860 ; 17861 17862 header.find('.i18n').i18n(); 17863 17864 // EVENTS 17865 input.on('keypress', function(e) { 17866 if (e.which == 13) { 17867 doSearch(); 17868 17869 // record 17870 if (sofia.analytics) { 17871 sofia.analytics.record('search', input.val(), textChooser.getTextInfo().id ); 17872 } 17873 } 17874 }); 17875 17876 button.on('click', function() { 17877 17878 // record 17879 if (sofia.analytics) { 17880 sofia.analytics.record('search', input.val(), textChooser.getTextInfo().id ); 17881 } 17882 17883 doSearch() ; 17884 17885 }); 17886 17887 textChooser.on('change', function(e) { 17888 17889 if (e.data.target != textlistui) { 17890 return; 17891 } 17892 17893 setTextInfo(e.data.textInfo, false); 17894 17895 // reset UI 17896 clearResults(); 17897 }); 17898 17899 textlistui.on('click', function(e) { 17900 17901 if (textChooser.getTarget() == textlistui) { 17902 textChooser.toggle(); 17903 } else { 17904 textChooser.setTarget(parent.node, textlistui, 'bible'); 17905 textChooser.setTextInfo(selectedTextInfo); 17906 textChooser.show(); 17907 textChooserFocused = true; 17908 } 17909 }); 17910 17911 searchOptionsButton.on('click', function(e) { 17912 17913 if (divisionChooser.is(':visible')) { 17914 divisionChooser.hide(); 17915 17916 } else { 17917 divisionChooser.show(); 17918 17919 var uiPos = searchOptionsButton.offset(), 17920 top = uiPos.top + searchOptionsButton.outerHeight(true) + 10, 17921 left = uiPos.left, 17922 divWidth = divisionChooser.outerWidth(true), 17923 winWidth = $(window).width(); 17924 17925 if (left + divWidth > winWidth) { 17926 left = winWidth - divWidth - 10; 17927 } 17928 17929 divisionChooser.css({ 17930 top: top, 17931 left: left 17932 }); 17933 17934 } 17935 17936 // $(document).on('click', docClick); 17937 }); 17938 17939 function drawDivisions() { 17940 17941 // TODO: store the selected ones from this book to reselect on this one (unless it's a shorter one) 17942 17943 var otListHtml = '', 17944 ntListHtml = ''; 17945 17946 for (var i=0, il=selectedTextInfo.divisions.length; i<il; i++) { 17947 17948 var dbsBookCode = selectedTextInfo.divisions[i], 17949 bookName = selectedTextInfo.divisionNames[i], 17950 checkedStatus = ' checked', 17951 html = '<label class="division-name"><input type="checkbox" value="' + dbsBookCode + '"' + checkedStatus + ' />' + bookName + '</label>'; 17952 17953 if (bible.EXTRA_MATTER.indexOf(dbsBookCode) > -1 ) { 17954 continue; 17955 } 17956 17957 if (bible.NT_BOOKS.indexOf(dbsBookCode) > -1 ) { 17958 ntListHtml += html; 17959 } else { 17960 otListHtml += html; 17961 } 17962 } 17963 17964 var completeHtml = 17965 '<div class="division-list division-list-ot">' + 17966 '<label class="division-header">' + 17967 '<input type="checkbox" value="list-ot" checked />' + i18n.t('windows.bible.ot') + '</label>' + 17968 '</label>' + 17969 '<div class="division-list-items">' + 17970 otListHtml + 17971 '</div>' + 17972 '</div>' + 17973 '<div class="division-list division-list-nt">' + 17974 '<label class="division-header">' + 17975 '<input type="checkbox" value="list-nt" checked />' + i18n.t('windows.bible.nt') + '</label>' + 17976 '</label>' + 17977 '<div class="division-list-items">' + 17978 ntListHtml + 17979 '</div>' + 17980 '</div>'; 17981 17982 divisionChooser.attr('dir', selectedTextInfo.dir); 17983 divisionChooser.find('.search-division-main').html(completeHtml); 17984 17985 // TODO: check for items then hide 17986 var hasOtBooks = divisionChooser.find('.division-list-ot .division-list-items input').length > 0, 17987 hasNtBooks = divisionChooser.find('.division-list-nt .division-list-items input').length > 0;
17988 17989 if (!hasOtBooks) { 17990 divisionChooser.find('.division-list-ot').hide(); 17991 } 17992 if (!hasNtBooks) { 17993 divisionChooser.find('.division-list-nt').hide(); 17994 } 17995 } 17996 17997 function setDivisions(divisions) { 17998 17999 console.log('init divisions', divisions); 18000 if (typeof divisions == 'string') { 18001 divisions = divisions.split(','); 18002 } 18003 18004 18005 if (divisions && divisions.length > 0) { 18006 divisionChooser.find('.division-list input').prop('checked', false); 18007 18008 for (var i=0, il = divisions.length; i<il; i++) { 18009 divisionChooser 18010 .find('.division-list input[value="' + divisions[i] + '"]') 18011 .prop('checked',true); 18012 } 18013 } 18014 18015 // check headers 18016 checkDivisionHeader( divisionChooser.find('.division-list-ot') ); 18017 checkDivisionHeader( divisionChooser.find('.division-list-nt') ); 18018 18019 } 18020 18021 function checkDivisionHeader(divisionList) { 18022 var items = divisionList.find('.division-list-items input'), 18023 allChecked = true; 18024 18025 items.each(function(i,el) { 18026 18027 if (!$(el).is(':checked')) { 18028 allChecked = false; 18029 return false; 18030 } 18031 }); 18032 18033 divisionList.find('.division-header input').prop('checked', allChecked); 18034 } 18035 18036 divisionChooser.on('click', '.division-header input', function() { 18037 var checkbox = $(this), 18038 setChildrenTo = checkbox.is(':checked'); 18039 18040 checkbox.closest('.division-list').find('.division-list-items input').prop('checked', setChildrenTo);
18041 }); 18042 18043 divisionChooser.on('click', '.division-list-items input', function() { 18044 var checkbox = $(this); 18045 18046 checkDivisionHeader( checkbox.closest('.division-list') ); 18047 }); 18048 18049 resultsBlock.on('click', 'tr', function(e) { 18050 18051 var tr = $(this), 18052 fragmentid = tr.attr('data-fragmentid'); 18053 18054 18055 18056 // is there a bible window? 18057 var 18058 bibleWindows = sofia.app.windowManager.getWindows().filter(function(w) { return w.className == 'BibleWindow'}); 18059 18060console.log('search click', fragmentid, bibleWindows); 18061 18062 if (bibleWindows.length == 0) { 18063 18064 // open new window 18065 sofia.app.windowManager.add('BibleWindow', { 18066 textid: sofia.config.newBibleWindowVersion, 18067 fragmentid: fragmentid, 18068 sectionid: fragmentid.split('_')[0], 18069 }); 18070 18071 } else { 18072 18073 18074 ext.trigger('globalmessage', { 18075 type: 'globalmessage', 18076 target: this, 18077 data: { 18078 messagetype:'nav', 18079 type: 'bible', 18080 locationInfo: { 18081 fragmentid: fragmentid, 18082 sectionid: fragmentid.split('_')[0], 18083 offset: 0 18084 } 18085 } 18086 }); 18087 } 18088 18089 }); 18090 18091 function searchLoadHandler(e) { 18092 searchProgressBar.show(); 18093 18094 // give feedback! 18095 var reference = new bible.Reference(e.data.sectionid), 18096 label = e.data.sectionid, 18097 progress = (e.data.index+1) + ' / ' + e.data.total; 18098 18099 if (bible.BOOK_DATA['GN'].names[textInfo.lang]) { 18100 reference.lang = textInfo.lang; 18101 } 18102 label = reference.toString(); 18103 18104 18105 // show results in footer and bar 18106 footer.html('Loading: ' + progress + ' :: ' + label ); 18107 searchProgressBarInner.css({'width': ((e.data.index+1) / e.data.total * 100) + '%' }); 18108 searchProgressBarLabel.html(label); 18109 18110 // move label into place 18111 var progressWidth = searchProgressBarInner.outerWidth(true), 18112 labelWidth = searchProgressBarLabel.outerWidth(true); 18113 18114 if (labelWidth > progressWidth) { 18115 18116 searchProgressBarLabel 18117 .css({left: progressWidth + 'px', margin: '' }) 18118 .addClass('search-progress-bar-label-outside'); 18119 18120 18121 } else { 18122 searchProgressBarLabel 18123 .css({left: (progressWidth-labelWidth) + 'px', margin: '' }) 18124 .removeClass('search-progress-bar-label-outside'); 18125 } 18126 } 18127 18128 function searchIndexCompleteHandler(e) { 18129 var results = e.data.results; 18130 //console.log('searcher:indexcomplete', e.data); 18131 18132 18133 footer.html(i18n.t('windows.search.results') + e.data.searchIndexesData.length ); 18134 } 18135 18136 function searchCompleteHandler(e) { 18137 18138 // store results 18139 currentResults = e.data.results; 18140 searchIndexesData = e.data.searchIndexesData; 18141 searchTermsRegExp = e.data.searchTermsRegExp; 18142 isLemmaSearch = e.data.isLemmaSearch; 18143 18144 //console.log('searcher:complete'); // , e.data.results); 18145 18146 var results = e.data.results, 18147 html = //'<h2>Results: ' + results.length + '</h2>' + 18148 '<table>'; 18149 18150 searchProgressBarInner.css({'width': '100%' }); 18151 setFinalResultsCount(e.data.results ? e.data.results.length : 0); 18152 resultsBlock.removeClass('loading-indicator'); 18153 18154 if (results && results.length > 0) { 18155 18156 // create visual array 18157 var divisionCount = {}, 18158 bookList = null; 18159 18160 18161 if (isLemmaSearch) { 18162 var text = input.val(); 18163 18164 if (text.substr(0,1) == 'G') { 18165 bookList = bible.NT_BOOKS; 18166 } else if (text.substr(0,1) == 'H') { 18167 bookList = bible.OT_BOOKS; 18168 } 18169 } else { 18170 bookList = textInfo.divisions; 18171 } 18172 18173 for (var i=0, il=bookList.length; i<il; i++) { 18174 divisionCount[ bookList[i] ] = 0; 18175 } 18176 18177 18178 18179 for (var i=0, il=results.length; i<il; i++) { 18180 var result = results[i], 18181 label = '', 18182 fragmentid = result.fragmentid, 18183 dbsBookCode = fragmentid.substr(0,2); 18184 18185 divisionCount[dbsBookCode]++; 18186 18187 if (textInfo.type.toLowerCase() == 'bible') { 18188 var br = new bible.Reference(result.fragmentid); 18189 18190 ////console.log(br, br.toString(), bible.BOOK_DATA['GN'].names[textInfo.lang]); 18191 18192 if (bible.BOOK_DATA['GN'].names[textInfo.lang]) { 18193 br.lang = textInfo.lang; 18194 } 18195 label = br.toString(); 18196 } else { 18197 label = result.fragmentid; 18198 } 18199 18200 html += '<tr data-fragmentid="' + result.fragmentid + '" class="divisionid-' + result.fragmentid.substr(0,2) + '"><th>
18200' + label + '</th><td lang="' + iso2iana.convert(textInfo.lang) + '">' + result.html + '</td></tr>'; 18201 } 18202 html += '</table>'; 18203 18204 18205 resultsBlock 18206 .html( html ) 18207 .find('.v-num').remove(); 18208 18209 // render book list 18210 renderResultsVisual(divisionCount, bookList); 18211 18212 if (isLemmaSearch) { 18213 renderLemmaInfo(); 18214 renderUsage(); 18215 } 18216 18217 // TEMP 18218 var strongsUsage = {}; 18219 resultsBlock.find('.highlight').each(function() { 18220 var h = $(this), l = h.closest('l'); 18221 if (l.length > 0) { 18222 var strongs = l.attr('s').split(' ')[0]; 18223 if (typeof strongsUsage[strongs] != 'undefined') { 18224 strongsUsage[strongs].count++; 18225 } else { 18226 strongsUsage[strongs] = { count: 1 }; 18227 } 18228 } 18229 }); 18230 var strongsHtml = ''; 18231 var strongsKeys = Object.keys(strongsUsage); 18232 for (var i=0, il=strongsKeys.length; i<il; i++) { 18233 strongsHtml += '<tr><td>' + strongsKeys[i] + '</td><td>' + strongsUsage[strongsKeys[i]].count + '</td></tr>'; 18234 } 18235 topUsage 18236 .append( $('<table>' + strongsHtml + '</table>') ) 18237 .show(); 18238 18239 18240 18241 createHighlights(); 18242 18243 //createLemmaUsage(); 18244 } else { 18245 18246 resultsBlock.html( "No results" ); 18247 18248 } 18249 18250 ext.trigger('settingschange', {type: 'settingschange', target: this, data: null}); 18251 18252 } 18253 18254 function renderLemmaInfo() { 18255 var text = input.val(), 18256 strongs = text.split(' ')[0], 18257 strongsNumber = strongs.substr(1), 18258 strongLang = strongs.substr(0,1), 18259 langCode = (strongLang == 'H' ? 'he' : 'el'), 18260 dir = langCode == 'he' ? 'ltr' : 'rtl'; 18261 18262 sofia.ajax({ 18263 dataType: 'json', 18264 url: 'content/lexicons/strongs/entries/' + strongs + '.json', 18265 success: function(data) { 18266 18267 var html = '<div class="lemma-word">' + 18268 '<span lang="' + iso2iana.convert(langCode) + '" dir="' + dir + '">' + data.lemma + '</span>' + 18269 ' <span class="lemma-strongs" dir="ltr"> [strongs:' + strongsNumber + ']</span>' + 18270 '</div>'; 18271 18272 //html += '<div class="lemma-outline">' + data.outline + '</div>'; 18273 18274 topLemmaInfo 18275 .html(html) 18276 .show(); 18277 } 18278 }); 18279 18280 } 18281 18282 function renderUsage() { 18283 var usages = {}, 18284 usageArray = []; 18285 18286 resultsBlock.find('tr').each(function() { 18287 var tr = $(this), 18288 fragmentid = tr.attr('data-fragmentid'), 18289 highlightedPhrase = tr.find('.highlight')[0].textContent; 18290 18291 highlightedPhrase = highlightedPhrase.replace(/\b(with|or|and|if|a|the|in|a|by|of|for)\b/gi,'').trim(); 18292 18293 if (typeof usages[highlightedPhrase] == 'undefined') { 18294 usages[highlightedPhrase] = 0; 18295 } 18296 18297 usages[highlightedPhrase]++; 18298 }); 18299 18300 // turn into array 18301 for (var usage in usages) { 18302 usageArray.push({usage: usage, count: usages[usage]}); 18303 } 18304 18305 // sort 18306 usageArray.sort(function(a, b) { 18307 if (a.count < b.count) { 18308 return 1; 18309 } else if (a.count > b.count) { 18310 return -1; 18311 } else { 18312 return 0; 18313 } 18314 18315 }); 18316 18317 var html = ''; 18318 for (var i=0, il=usageArray.length; i<il; i++) { 18319 html += (i > 0 ? ', ' : '') + usageArray[i].usage + ' (' + usageArray[i].count + ')'; 18320 } 18321 18322 topUsage 18323 .html(html) 18324 .show(); 18325 } 18326 18327 function renderResultsVisual(divisionCount, bookList) { 18328 var totalWidth = topVisual.outerWidth(), 18329 totalBooks = bookList.length, 18330 width = 1/totalBooks*100, 18331 html = '', 18332 maxCount = 0, 18333 baseHeight = 2; 18334 maxHeight = 38; 18335 18336 for (var i=0, il=bookList.length; i<il; i++) { 18337 var count = divisionCount[ bookList[i] ]; 18338 if (count > maxCount) { 18339 maxCount = count; 18340 } 18341 } 18342 18343 18344 for (var i=0, il=bookList.length; i<il; i++) { 18345 var dbsBookCode = bookList[i], 18346 count = divisionCount[dbsBookCode], 18347 height = maxHeight * count / maxCount + baseHeight, 18348 top = maxHeight + baseHeight - height; 18349 18350 html += '<span class="search-result-book-bar ' + dbsBookCode + '" data-count="' + count + '" data-id="' + dbsBookCode + '" style="width:' + width + '%;"><span class="divisionid-' + dbsBookCode + '" style="height:' + height + 'px; margin-top: ' + top + 'px;"></span></span>'; 18351 } 18352 18353 topVisual.html(html).show(); 18354 } 18355 18356 topVisual.on('mouseover', '.search-result-book-bar', function() { 18357 18358 var bookBar = $(this), 18359 count = bookBar.attr('data-count'), 18360 dbsBookCode = bookBar.attr('data-id'), 18361 bookInfo = bible.BOOK_DATA[dbsBookCode], 18362 win = bookBar.closest('.window'), 18363 winPos = win.offset(), 18364 winWidth = win.outerWidth(true), 18365 bookBarPos = bookBar.offset(), 18366 top = bookBarPos.top + bookBar.height() + 10 - winPos.top, 18367 left = bookBarPos.left - winPos.left; 18368 18369 18370 topVisualLabel 18371 .html(bookInfo.names[textInfo.lang][0] + ': ' + count) 18372 .css({top: top, left: left}) 18373 .show(); 18374 18375 if (left + topVisualLabel.outerWidth() > winWidth) { 18376 left = winWidth - topVisualLabel.outerWidth() - 20; 18377 18378 topVisualLabel 18379 .css({left: left}); 18380 } 18381 18382 18383 }).on('mouseout', '.search-result-book-bar', function() { 18384 18385 topVisualLabel 18386 .hide(); 18387 }).on('click', '.search-result-book-bar', function() { 18388 18389 var bookBar = $(this), 18390 dbsBookCode = bookBar.attr('data-id'), 18391 bookInfo = bible.BOOK_DATA[dbsBookCode]; 18392
18393 // find first result? 18394 resultsBlock.find('tr').each(function() { 18395 var tr = $(this), 18396 fragmentid = tr.attr('data-fragmentid'); 18397 18398 if (fragmentid.indexOf(dbsBookCode) == 0) { 18399 18400 // scrollTo 18401 console.log('found', tr.offset()); 18402 18403 main.scrollTop( tr.offset().top - tr.outerHeight(true) - 50); 18404 18405 return false; 18406 } 18407 18408 }); 18409 18410 18411 }); 18412 18413 function setFinalResultsCount(count) { 18414 18415 footer.html(i18n.t('windows.search.results') + ': ' + count ); 18416 18417 // move to center 18418 searchProgressBarLabel.html(count + ' ' + i18n.t('windows.search.verses')); 18419 18420 var progressWidth = searchProgressBarInner.outerWidth(true), 18421 labelWidth = searchProgressBarLabel.outerWidth(true), 18422 labelLeft = progressWidth/2 - labelWidth; 18423 18424 searchProgressBarLabel 18425 //.css({left: labelLeft + 'px'}); 18426 .css({left: '50%', marginLeft: '-' + (labelWidth/2) + 'px'}); 18427 18428 } 18429 18430 function clearResults() { 18431 footer.html(''); 18432 topBlockTitle.html(''); 18433 resultsBlock.html(''); 18434 topVisual.html('').hide(); 18435 topLemmaInfo.html('').hide(); 18436 topUsage.html('').hide(); 18437 searchProgressBar.hide(); 18438 searchProgressBarLabel.html(''); 18439 searchProgressBarInner.width(0); 18440 } 18441 18442 // ACTIONS 18443 function doSearch() { 18444 18445 disable(); 18446 18447 textInfo = textChooser.getTextInfo(); 18448 18449 var text = input.val().trim(), 18450 //textid = list.val(), 18451 textid = textInfo.id, 18452 18453 divisions = getSelectedDivisions(), 18454 18455 allDivisions = divisionChooser.find('.division-list-items input'); 18456 18457 parent.tab.find('span').html(text); 18458 18459 // don't send the list if it's all books 18460 if (allDivisions.length == divisions.length) { 18461 divisions = []; 18462 } 18463 18464 clearResults(); 18465 18466 topBlockTitle.html('[' + text + '] in [' + textInfo.name + ']'); 18467 18468 removeHighlights(); 18469 18470 resultsBlock.addClass('loading-indicator'); 18471 18472 enable(); 18473 18474 TextLoader.startSearch(textid, divisions, text, searchLoadHandler, searchIndexCompleteHandler, searchCompleteHandler); 18475 } 18476 18477 function getSelectedDivisions() { 18478 18479 var divisions = [], 18480 selectedBooks = divisionChooser.find('.division-list-items input:checked'); 18481 18482 selectedBooks.each(function() { 18483 divisions.push( $(this).val() ); 18484 }); 18485 18486 return divisions; 18487 } 18488 18489 function disable() { 18490 input.prop('disabled', true); 18491 button.prop('disabled', true); 18492 } 18493 function enable() { 18494 input.prop('disabled', false); 18495 button.prop('disabled', false); 18496 } 18497 18498 18499 function size(width, height) { 18500 18501 header.outerWidth(width); 18502 footer.outerWidth(width); 18503 18504 main.outerWidth(width) 18505 .outerHeight(height - header.outerHeight() - footer.outerHeight()); 18506 } 18507 18508 function setTextInfo(textInfo, sendToChooser) { 18509 18510 // keep old one 18511 previousTextInfo = selectedTextInfo; 18512 18513 // store new one 18514 selectedTextInfo = textInfo; 18515 18516 textlistui.html(textInfo.abbr); 18517 18518 // draw books 18519 drawDivisions(); 18520 18521 if (sendToChooser) { 18522 textChooser.setTextInfo(textInfo); 18523 } 18524 } 18525 18526 // init 18527 function init() { 18528 18529 if (init_data.textid) { 18530 TextLoader.getText(init_data.textid, function(data) { 18531 18532 setTextInfo(data, true); 18533 18534 if (init_data.divisions) { 18535 setDivisions(init_data.divisions); 18536 } 18537 18538 if (init_data.searchtext && init_data.searchtext != '') { 18539 input.val(init_data.searchtext); 18540 18541 doSearch(); 18542 } 18543 18544 }); 18545 } else { 18546 console.log('SEARCH: no init textid'); 18547 18548 for (var index in TextLoader.textData) { 18549 18550 setTextInfo(TextLoader.textData[index], true); 18551 break; 18552 } 18553 } 18554 } 18555 init(); 18556 18557 function removeHighlights() { 18558 $('.BibleWindow .highlight').each(function(i, el) { 18559 18560 if (el.tagName.toLowerCase() == 'l') { 18561 // for Lemma tags, jsut remove hte hlight 18562 el.className = el.className.replace(/highlight/gi, ''); 18563 } else { 18564 // if it's just <span class="highlight">, replace it with text 18565 var textFragment = document.createTextNode(el.textContent); 18566 if (el && el.parentNode) { 18567 el.parentNode.insertBefore(textFragment, el); 18568 el.parentNode.removeChild(el); 18569 } 18570 } 18571 18572 }); 18573 } 18574 18575 function createHighlights() { 18576 18577 if (currentResults == null) { 18578 return; 18579 } 18580 18581 removeHighlights();
18582 18583 // try to highlight! 18584 for (var i=0, il=currentResults.length; i<il; i++) { 18585 var result = currentResults[i]; 18586 18587 18588 $('.' + result.fragmentid).each(function(i,el) { 18589 18590 18591 for (var j=0, jl=searchTermsRegExp.length; j<jl; j++) { 18592 18593 searchTermsRegExp[j].lastIndex = 0; 18594 18595 18596 18597 if (isLemmaSearch) { 18598 18599 // add the 'highlight' class to the <l> node 18600 el.innerHTML = el.innerHTML.replace(searchTermsRegExp[j], function(match) { 18601 return match + ' class="highlight" '; 18602 }); 18603 18604 } else { 18605 18606 // surround the word with a highlight 18607 el.innerHTML = el.innerHTML.replace(searchTermsRegExp[j], function(match) { 18608 return '<span class="highlight">' + match + '</span>'; 18609 }); 18610 18611 } 18612 } 18613 18614 }); 18615 18616 } 18617 18618 } 18619 18620 function createLemmaUsage() { 18621 var strongsUsage = {}; 18622 18623 $('.highlight').each(function() { 18624 var h = $(this), l = h.closest('l'); 18625 if (l.length > 0) { 18626 var strongs = l.attr('s').split(' ')[0]; 18627 if (typeof words[strongs] != 'undefined') { 18628 strongsUsage[strongs]++; 18629 } else { 18630 strongsUsage[strongs] = 1; 18631 } 18632 } 18633 }); 18634 console.log(words); 18635 18636 } 18637 18638 function close() { 18639 removeHighlights(); 18640 18641 divisionChooser.remove(); 18642 textChooser.close(); 18643 18644 ext.clearListeners(); 18645 ext = null; 18646 } 18647 18648 var ext = { 18649 size: size, 18650 getData: function() { 18651 18652 var divisions = divisionChooser.find('.division-list-ot .division-header input').is(':checked') && 18653 divisionChooser.find('.division-list-nt .division-header input').is(':checked') ? 18654 [] : getSelectedDivisions(); 18655 18656 return { 18657 searchtext: input.val().trim(), 18658 textid: (selectedTextInfo != null) ? selectedTextInfo.providerid : null, 18659 divisions: divisions, 18660 params: { 18661 'win': 'search', 18662 'textid': (selectedTextInfo != null) ? selectedTextInfo.providerid : null, 18663 'searchtext': input.val(), 18664 'divisions': divisions 18665 } 18666 } 18667 }, 18668 close: close 18669 }; 18670 ext = $.extend(true, ext, EventEmitter); 18671 18672 ext.on('message', function(e) { 18673 18674 if (e.data.messagetype == 'textload') { 18675 ////console.log('search:textload', e.data.sectionid); 18676 18677 createHighlights(e.data.content); 18678 18679 } 18680 }); 18681 18682 return ext; 18683 18684}; 18685 18686sofia.initMethods.push(function() { 18687 18688 if (sofia.config.enableOnlineSources) { 18689 18690 sofia.windowTypes.push( { 18691 className:'SearchWindow', 18692 param: 'search', 18693 paramKeys: { 18694 'textid': 't', 18695 'searchtext': 's', 18696 'divisions': 'd' 18697 }, 18698 init: { 18699 } 18700 }); 18701 } 18702}); 18703 18704 18705 18706/********* 18707* ../app/js/windows/mapswindow.js 18708**********/ 18709 18710var MapWindow = function(id, parent, data) { 18711 18712 //parentNode.css({position: 'relative'}); 18713 18714 var now = new Date(), 18715 //id = 'maps_' + now.getYear() + '_' + (now.getMonth()+1) + '_' + now.getDate() + '_' + now.getHours() + '_' + now.getMinutes() + '_' + now.getSeconds(), 18716 loadMapFunctionName = 'loadMap_' + id, 18717 //header = $('<div id="map-header" style="height: 40px; background: #ddd; padding: 9px;"><input type="text" style="width:100%;" /</div>').appendTo(parentNode), 18718 18719 18720 inputMarginLeft = 10, 18721 inputMarginRight = 45, 18722 inputMarginTop = 10, 18723 mapSearchInput = $('<input placeholder="" type="text" class="i18n" data-i18n="[placeholder]windows.map.placeholder" style="height: 32px; background: #fff; border: solid 0px #333; padding: 7px; position: absolute; top: ' + inputMarginTop + 'px; left: ' + inputMarginLeft + 'px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); z-index: 2; font-size: 14px;" />').appendTo(parent.node), 18724 18725 //mapSearchInput = header.find('input'), 18726
18727 mapContainer = $('<div class="window-maps" id="' + id + '"></div>').appendTo(parent.node), 18728 map = null, 18729 geocoder = null, 18730 infowindow = null, 18731 locationData = null, 18732 locationDataByVerse = null; 18733 18734 18735 // bind init method to window object 18736 window[loadMapFunctionName] = function() { 18737 var mapOptions = { 18738 zoom: 8, 18739 disableDefaultUI: true, 18740 center: new google.maps.LatLng(data.latitude, data.longitude), 18741 //mapTypeId: google.maps.MapTypeId.ROADMAP, 18742 18743 mapTypeId: google.maps.MapTypeId.HYBRID, 18744 mapTypeControl: false, 18745 mapTypeControlOptions: { 18746 style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR, 18747 position: google.maps.ControlPosition.BOTTOM_CENTER 18748 }, 18749 panControl: false, 18750 panControlOptions: { 18751 position: google.maps.ControlPosition.TOP_RIGHT 18752 }, 18753 zoomControl: true, 18754 zoomControlOptions: { 18755 style: google.maps.ZoomControlStyle.LARGE, 18756 position: google.maps.ControlPosition.LEFT_CENTER 18757 }, 18758 scaleControl: false, 18759 scaleControlOptions: { 18760 position: google.maps.ControlPosition.TOP_LEFT 18761 }, 18762 streetViewControl: false, 18763 streetViewControlOptions: { 18764 position: google.maps.ControlPosition.LEFT_TOP 18765 } 18766 }; 18767 18768 map = new google.maps.Map(document.getElementById(id), mapOptions); 18769 18770 google.maps.event.addListener(map, 'center_changed', function() { 18771 18772 var center = map.getCenter(); 18773 18774 ext.trigger('settingschange', { 18775 type:'settingschange', 18776 target: this, 18777 data: { 18778 latitude: center.lat(), 18779 longitude: center.lng(), 18780 label: 'Map: ' + center.lat().toFixed(3) +', ' + center.lng().toFixed(3) 18781 } 18782 }); 18783 18784 }); 18785 18786 18787 geocoder = new google.maps.Geocoder(); 18788 18789 infowindow = new google.maps.InfoWindow({ 18790 content: 'Empty for now' 18791 }); 18792 18793 18794 loadPins(); 18795 } 18796 18797 mapSearchInput.on('keypress', function(e) { 18798 if (map != null && e.which == 13) { 18799 18800 var search_value = mapSearchInput.val(); 18801 18802 18803 // find a marker! 18804 findMarkerByText(search_value); 18805 18806 } 18807 }); 18808 18809 function findMarkerByText(value) { 18810 for (var i=0, il=locationData.length; i<il; i++) { 18811 var location = locationData[i]; 18812 18813 //if (location.name.toLowerCase().indexOf(value.toLowerCase()) > -1) { 18814 if (location.name.toLowerCase() == value.toLowerCase()) { 18815 18816 openLocation(location); 18817 18818 break; 18819 } 18820 } 18821 } 18822 18823 mapContainer.on('click', '.verse, .v', function() { 18824 var link = $(this), 18825 sectionid = link.attr('data-sectionid'), 18826 fragmentid = link.attr('data-fragmentid'); 18827 18828 ext.trigger('globalmessage', { 18829 type: 'globalmessage', 18830 target: this, 18831 data: { 18832 messagetype: 'nav', 18833 type: 'bible', 18834 locationInfo: { 18835 sectionid: sectionid, 18836 fragmentid: fragmentid 18837 } 18838 } 18839 });
18840 18841 console.log('clicked', this); 18842 18843 }); 18844 18845 // dynamically load map 18846 if (typeof window.google == 'undefined' || 18847 typeof window.google.maps == 'undefined' || 18848 typeof window.google.maps.Map == 'undefined') { 18849 18850 var script = document.createElement('script'); 18851 script.type = 'text/javascript'; 18852 script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=' + loadMapFunctionName; 18853 18854 document.body.appendChild(script); 18855 } else { 18856 console.log('gogole is loaded'); 18857 18858 // load now! 18859 window[loadMapFunctionName](); 18860 } 18861 18862 18863 function loadPins() { 18864 18865 console.log('MAP: loading pins'); 18866 18867 sofia.ajax({ 18868 dataType: 'json', 18869 url: 'content/maps/maps.json', 18870 success: function(data) { 18871 18872 // store data 18873 locationData = data.names; 18874 18875 createPins(); 18876 //setTimeout(createPins, 50); 18877 } 18878 }); 18879 } 18880 18881 18882 function createPins() { 18883 18884 locationDataByVerse = {}; 18885 18886 var before = new Date(); 18887 for (var i=0, il=locationData.length; i<il; i++) { 18888 var location = locationData[i]; 18889 18890 // create pin 18891 (function(location) { 18892 18893 var marker = new google.maps.Marker({ 18894 position: new google.maps.LatLng(location.coordinates[1], location.coordinates[0]), 18895 map: map, 18896 title: location.name 18897 }); 18898 18899 location.marker = marker; 18900 18901 google.maps.event.addListener(marker, 'click', function() { 18902 18903 openLocation(location); 18904 18905 }); 18906 18907 18908 })(location); 18909 18910 // create back link 18911 for (var j=0, jl=location.verses.length; j<jl; j++) { 18912 18913 var verseid = location.verses[j], 18914 //bible_ref = new bible.Reference(verse), 18915 //verseid = bible_ref.bookid + bible_ref.chapter + '_' + bible_ref.verse, 18916 verseInfo = locationDataByVerse[verseid]; 18917 18918 if (typeof verseInfo == 'undefined') { 18919 verseInfo = []; 18920 locationDataByVerse[verseid] = verseInfo; 18921 } 18922 18923 verseInfo.push(location); 18924 } 18925 } 18926 var after = new Date(); 18927 18928 console.log('process time', after - before); 18929 18930 18931 // SO SLOW! 18932 18933 // create locationDataByVerse 18934 18935 /*var before = new Date(); 18936 18937 for (var i=0, il=locationData.length; i<il; i++) { 18938 var location = locationData[i]; 18939 18940 for (var j=0, jl=location.verses.length; j<jl; j++) { 18941 18942 var verseid = location.verses[j], 18943 //bible_ref = new bible.Reference(verse), 18944 //verseid = bible_ref.bookid + bible_ref.chapter + '_' + bible_ref.verse, 18945 verseInfo = locationDataByVerse[verseid]; 18946 18947 if (typeof verseInfo == 'undefined') { 18948 verseInfo = []; 18949 locationDataByVerse[verseid] = verseInfo; 18950 } 18951 18952 verseInfo.push(location); 18953 } 18954 } 18955 var after = new Date(); 18956 18957 console.log('process time', after - before); 18958 */ 18959 18960 //parentNode.html( JSON.stringify(locationDataByVerse) ); 18961 //return; 18962 18963 highlightStoredLocations(); 18964 } 18965 18966 function openLocation(location) { 18967 var verses_html = $.map(location.verses, function(a) { 18968 var bible_ref = new bible.Reference(a), 18969 sectionid = bible_ref.bookid + bible_ref.chapter, 18970 fragmentid = sectionid + '_' + bible_ref.verse1; 18971 18972 return '<span class="verse" style="text-decoration:underline; cursor: pointer" data-sectionid="' + sectionid + '" data-fragmentid="' + fragmentid + '">' + bible_ref.toString() + '</span>'; 18973 }); 18974 18975 infowindow.setContent( 18976 '<div style="width: 250px; height: 150px; overflow: auto;">' + 18977 '<h2>' + location.name + '</h2>' + 18978 '<p>' + 18979 verses_html.join('; ') + 18980 '</p>' + 18981 '</div>'); 18982 infowindow.open(map, location.marker); 18983 18984 map.setCenter(location.marker.getPosition()); 18985 } 18986 18987 function size(width, height) { 18988 18989 mapSearchInput.outerWidth(width - (inputMarginLeft+inputMarginRight)); 18990 18991 mapContainer 18992 .width(width) 18993 .height(height); // - header.outerHeight()); 18994 } 18995 18996 function getData() { 18997 18998 if (map == null) { 18999 return { 19000 params: { 19001 win: 'map' 19002 } 19003 }; 19004 } 19005 19006 var center = map.getCenter(), 19007 data = { 19008 latitude: center.lat(), 19009 longitude: center.lng(), 19010 params: { 19011 'win': 'map', 19012 'latitude': center.lat(), 19013 'longitude': center.lng() 19014 } 19015 }; 19016 19017 return data 19018 } 19019 19020 var contentToHighlight = []; 19021 function highlightStoredLocations() { 19022 //removeHighlights();
19023 19024 19025 if (contentToHighlight.length > 0) { 19026 for (var i=0, il=contentToHighlight.length; i<il; i++) { 19027 highlightLocations(contentToHighlight[i]); 19028 } 19029 contentToHighlight = []; 19030 } 19031 } 19032 19033 function highlightLocations(content) { 19034 19035 content.find('.verse, .v').each(function() { 19036 var verse = $(this), 19037 verseid = verse.attr('data-id'); 19038 verseLocations = locationDataByVerse[verseid], 19039 html = verse.html(); 19040 19041 //console.log(verseid, verseLocations); 19042 19043 if (typeof verseLocations != 'undefined') { 19044 19045 for (var i=0, il=verseLocations.length; i<il; i++) { 19046 var location = verseLocations[i], 19047 regexp = new RegExp('\\b' + location.name + '\\b', 'gi'), 19048 html = html.replace(regexp , '<span class="linked-location">' + location.name + '</span>'); 19049 19050 //location.marker.setIcon('http://maps.google.com/intl/en_us/mapfiles/ms/micons/purple-dot.png'); 19051 location.marker.setIcon('http://mt.google.com/vt/icon?color=ff135C13&name=icons/spotlight/spotlight-waypoint-a.png'); 19052 } 19053 } 19054 19055 verse.html( html ); 19056 19057 }); 19058 19059 19060 content.on('click', '.location', text_location_clicked); 19061 19062 19063 // highlight pins? 19064 19065 19066 } 19067 19068 function removeHighlights() { 19069 $('.BibleWindow .linked-location').each(function(i, el) { 19070 19071 if (el.tagName.toLowerCase() == 'l') { 19072 // for Lemma tags, just remove the class 19073 el.className = el.className.replace(/linked-location/gi, ''); 19074 } else { 19075 // if it's just <span class="linked-location">, replace it with text 19076 var textFragment = document.createTextNode(el.textContent); 19077 el.parentNode.insertBefore(textFragment, el); 19078 el.parentNode.removeChild(el); 19079 } 19080 19081 }); 19082 } 19083 19084 19085 function close() { 19086 console.log('closing maps'); 19087 19088 geocoder = null; 19089 map = null; 19090 infowindow = null; 19091 19092 ext.clearListeners(); 19093 19094 removeHighlights(); 19095 } 19096 19097 function text_location_clicked() { 19098 var element = $(this); 19099 19100 findMarkerByText( element.html() ); 19101 } 19102 19103 var ext = { 19104 size: size, 19105 getData: getData, 19106 sendMessage: function() {}, 19107 close: close 19108 }; 19109 ext = $.extend(true, ext, EventEmitter); 19110 19111 19112 ext.on('message', function(e) { 19113 if (e.data.messagetype == 'textload') { 19114 //console.log('maps got text load', e.data.sectionid); 19115 19116 // store until data is loaded 19117 if (locationDataByVerse == null) { 19118 contentToHighlight.push(e.data.content); 19119 return; 19120 } else { 19121 // are there ones left to do 19122 highlightStoredLocations(); 19123 19124 // do this one 19125 highlightLocations(e.data.content); 19126 } 19127 } 19128 }); 19129 19130 return ext; 19131}; 19132 19133//sofia.windowTypes.push('MapsWindow'); 19134 19135 19136 19137sofia.initMethods.push(function() { 19138 19139 if (sofia.config.enableOnlineSources) { 19140 19141 sofia.windowTypes.push( { 19142 className:'MapWindow', 19143 param: 'map', 19144 paramKeys: { 19145 'latitude': 'la', 19146 'longitude': 'ln', 19147 }, 19148 init: { 19149 'latitude': 31.7833, 19150 'longitude': 35.2167 19151 } 19152 }); 19153 } 19154}); 19155 19156 19157 19158/********* 19159* ../app/js/windows/mediawindow.js 19160**********/ 19161 19162var MediaWindow = function(id, parent, data) { 19163 19164 var mediaLibraries = null, 19165 contentToProcess = null, 19166 currentSectionId = ''; 19167 header = $('<div class="window-header"><span class="window-title i18n" data-i18n="[html]windows.media.label"></span></div>').appendTo(parent.node), 19168 main = $('<div class="window-main">' + 19169 '<div class="media-video"></div>' + 19170 '<div class="media-content"></div>' + 19171 '</div>').appendTo(parent.node), 19172 videoArea = main.find('media-video'), 19173 contentArea = main.find('media-content'); 19174 19175 header.find('.i18n').i18n(); 19176 19177 MediaLibrary.getMediaLibraries(function(data) { 19178 mediaLibraries = data; 19179 19180 processContent(); 19181 }); 19182 19183 function processContent() { 19184 if (mediaLibraries == null || contentToProcess == null) { 19185 return; 19186 } 19187 19188 var sectionid = contentToProcess.attr('data-id'); 19189 19190 if (currentSectionId == sectionid) { 19191 return; 19192 } 19193 19194 currentSectionId = sectionid; 19195 19196 19197 var bibleReference = new bible.Reference(sectionid); 19198 bibleReference.language = contentToProcess.attr('lang'); 19199 19200 19201 // remove all previous checks 19202 $('.checked-media').removeClass('checked-media'); 19203 19204 main.html(''); 19205 main.scrollTop(0); 19206 19207 19208 // vers images
19209 var node = $('<div class="media-library-verses">' + 19210 '<h2>' + bibleReference.toString() + '</h2>' + 19211 '<div class="media-library-thumbs"></div>' + 19212 '</div>').appendTo(main), 19213 gallery = node.find('.media-library-thumbs'), 19214 html = ''; 19215 19216 19217 // create HTML for <img>s 19218 contentToProcess.find('.verse, .v').each(function(i,el) { 19219 var verse = $(this), 19220 verseid = verse.attr('data-id'), 19221 reference = new bible.Reference(verseid); 19222 19223 verse = verse.closest('.chapter').find('.' + verseid + '').first(); 19224 19225 if (verse.hasClass('checked-media')) { 19226 return; 19227 } 19228 19229 html += renderVerse(verseid, reference, mediaLibraries); 19230 19231 verse.addClass('checked-media'); 19232 }); 19233 19234 //console.log(html); 19235 19236 gallery.html(html); 19237 19238 19239 var images = gallery.find('img'); 19240 19241 // show error message 19242 if (images.length == 0) { 19243 gallery.html('No media for this chapter'); 19244 return; 19245 } 19246 19247 // determine if all have loaded 19248 images.on('load', function() { 19249 var img = $(this); 19250 img.addClass('loaded'); 19251 19252 if (images.filter('.loaded').length == images.length) { 19253 resizeImages(gallery); 19254 } 19255 }); 19256 19257 } 19258 19259 19260 // handle resizing 19261 var timeoutVar = null; 19262 $(window).on('resize', function() { 19263 19264 if (timeoutVar != null) { 19265 clearTimeout(timeoutVar); 19266 } 19267 19268 timeoutVar = setTimeout(function() { 19269 startResize(); 19270 }, 100); 19271 }); 19272 19273 19274 main.on('click', '.media-library-thumbs a.mediatype-image', sofia.globals.mediaImageClick); 19275 main.on('click', '.media-library-thumbs a.mediatype-video', sofia.globals.mediaVideoClick); 19276 main.on('click', '.media-library-thumbs a.mediatype-jfm', sofia.globals.mediaVideoJfmClick); 19277 19278 19279 function renderVerse(verseid, reference, mediaLibraries) { 19280 19281 var html = ''; 19282 19283 for (var i=0, il=mediaLibraries.length; i<il; i++) { 19284 19285 var mediaLibrary = mediaLibraries[i], 19286 mediaForVerse = mediaLibrary.data ? mediaLibrary.data[verseid] : undefined; 19287 19288 // add media 19289 if (typeof mediaForVerse != 'undefined') { 19290 19291 for (var j=0, jl = mediaForVerse.length; j<jl; j++) { 19292 var mediaInfo = mediaForVerse[j],
19293 baseUrl = sofia.config.baseContentUrl + 'content/' + 'media/' + mediaLibrary.folder + '/', 19294 fullUrl = baseUrl + mediaInfo.filename + '.' + mediaInfo.exts[0], 19295 thumbUrl = baseUrl + mediaInfo.filename + '-thumb.jpg'; 19296 19297 html += '<a href="' + fullUrl + '" class="mediatype-' + mediaLibrary.type + '" target="_blank" ' + (mediaInfo.name ? 'title="' + mediaInfo.name + '"' : '') + ' data-filename="' + mediaInfo.filename + '">' + 19298 '<img src="' + thumbUrl + '" />' + 19299 //((mediaLibrary.type != 'image') ? '<span>' + mediaInfo.name + '</span>' : '') + 19300 ((mediaLibrary.type != 'image') ? '<b><i></i></b>' : '') + 19301 '<span>' + reference.toString() + '</span>' + 19302 '</a>'; 19303 } 19304 19305 } 19306 } 19307 19308 return html; 19309 19310 } 19311 19312 function startResize() { 19313 resizeImages( main.find('.media-library-thumbs')); 19314 } 19315 19316 19317 function resizeImages(gallery) { 19318 19319 // once loaded! 19320 var TARGET_ROW_HEIGHT = 80, 19321 TARGET_GUTTER_WIDTH = 4, 19322 container_width = gallery.width(), 19323 current_width = 0, 19324 currentRow = []; 19325 19326 gallery.find('img').each(function() { 19327 var img = $(this), 19328 a = img.closest('a'), 19329 width = img.data('original-width'); 19330 height = img.data('original-height'); 19331 19332 // store for resize 19333 if (width == null) { 19334 width = img.width(); 19335 img.data('original-width', width); 19336 } 19337 if (height == null) { 19338 height = img.height(); 19339 img.data('original-height', height); 19340 } 19341 19342 var 19343 height_ratio = TARGET_ROW_HEIGHT / height, // (height > TARGET_ROW_HEIGHT) ? TARGET_ROW_HEIGHT / height : 19344 new_width = Math.floor(height_ratio*width); 19345 19346 // will this push the last row 19347 //if (false && container_width < current_width + new_width ) { 19348 if (container_width < current_width + new_width ) { 19349 19350 // resize the previous ones 19351 var row_ratio = container_width / current_width, 19352 remainder = container_width - current_width, 19353 width_per_item = Math.ceil(remainder / currentRow.length); 19354 19355 19356 /* 19357 console.log('row', 19358 row_ratio, 19359 container_width, current_width, 19360 container_width - current_width); 19361 */ 19362 19363 for (var j=0, jl=currentRow.length; j<jl; j++) { 19364 var row_a = currentRow[j], 19365 row_img = row_a.find('img'), 19366 row_width = parseInt(row_a.width(), 10), 19367 row_height = parseInt(row_a.height(), 10), 19368 //new_row_width = Math.floor(row_width * row_ratio), 19369 new_row_width = row_width + width_per_item, 19370 new_row_height = Math.floor(row_height * row_ratio); 19371 19372 row_a.width(new_row_width); 19373 row_a.height(new_row_height); 19374 19375 row_img.width(new_row_width); 19376 row_img.height(new_row_height); 19377 19378 19379 if (j+1 == jl) { 19380 row_a.css('marginRight',0); 19381 } 19382 19383 remainder = remainder - width_per_item; 19384 19385 if (width_per_item > remainder) { 19386 width_per_item = remainder; 19387 } 19388 19389 } 19390 19391 19392 // start over 19393 currentRow = []; 19394 current_width = 0; 19395 } 19396 19397 19398 // restart 19399 //console.log(width, height, new_width); 19400 19401 a 19402 .width(new_width) 19403 .height(TARGET_ROW_HEIGHT) 19404 .css({ 19405 "marginRight": TARGET_GUTTER_WIDTH + 'px', 19406 "marginBottom": TARGET_GUTTER_WIDTH + 'px'}); 19407 19408 img 19409 .width(new_width) 19410 .height(TARGET_ROW_HEIGHT); 19411 19412 currentRow.push(a); 19413 19414 current_width += new_width + TARGET_GUTTER_WIDTH; 19415 19416 19417 19418 }); 19419 19420 } 19421 19422 19423 19424 function close() { 19425 19426 ext.clearListeners(); 19427 } 19428 19429 function size(width, height) { 19430 // do notheirng? 19431 main.outerHeight(height - header.outerHeight()) 19432 .outerWidth(width); 19433 19434 19435 startResize(); 19436 } 19437 19438 var ext = { 19439 size: size, 19440 getData: function() { 19441 return { 19442 19443 params: { 19444 'win': 'media' 19445 } 19446 19447 } 19448 }, 19449 close: close 19450 }; 19451 ext = $.extend(true, ext, EventEmitter); 19452 19453 ext.on('message', function(e) { 19454 if (e.data.messagetype == 'textload') { 19455 //processContent(e.data.content); 19456 } 19457 19458 if (e.data.messagetype == 'nav' && e.data.type == 'bible') { 19459 19460 var content = $('.section[data-id="' + e.data.locationInfo.sectionid + '"]').first(); 19461 19462 contentToProcess = content; 19463 19464 processContent(); 19465 } 19466 }); 19467 19468 setTimeout(function() { 19469 var firstWindowSettings = sofia.app.windowManager.getSettings()[0], 19470 firstWin = $('.window:first'), 19471 selectedChapter = (firstWindowSettings && firstWindowSettings.data) ? firstWin.find('.section[data-id="' + firstWindowSettings.data.sectionid + '"]').first() : null; 19472 19473 if (selectedChapter != null) { 19474 contentToProcess = selectedChapter; 19475 processContent(); 19476 } 19477 19478 }, 500); 19479 19480 // try to find the first one 19481 //var firstWindow = sofia.windowManager.windows[0]; 19482 19483 19484 19485 return ext; 19486 19487}; 19488//sofia.windowTypes.push('MediaWindow'); 19489 19490 19491 19492 19493 19494sofia.initMethods.push(function() { 19495 19496
19497 sofia.windowTypes.push( { 19498 className:'MediaWindow', 19499 param: 'media', 19500 paramKeys: {}, 19501 init: { 19502 'latitude': 31.7833, 19503 'longitude': 35.2167 19504 } 19505 }); 19506 19507}); 19508 19509 19510 19511/********* 19512* ../app/js/windows/parallelswindow.js 19513**********/ 19514 19515var ParallelsWindow = function(id, parent, init_data) { 19516 19517 var 19518 parallelsDataFolder = 'content/parallels/', 19519 19520 container = 19521 $('<div class="parallels-container">'+ 19522 '<div class="window-header parallels-header">'+ 19523 '<div class="scroller-header-inner">'+ 19524 '<div class="parallel-list">' + 19525 '<select class="header-list app-list"></select>' + 19526 '</div>' + 19527 '<div class="header-list app-list text-list"></div>'+ 19528 //'<div class="header-list parallels-list"></div>'+ 19529 '</div>'+ 19530 '</div>'+ 19531 '<div class="parallels-main">' + 19532 '</div>' + 19533 '</div>').appendTo(parent.node), 19534 19535 // dom nodes 19536 header = container.find('.parallels-header'), 19537 main = container.find('.parallels-main'), 19538 textlistui = header.find('.text-list'), 19539 19540 // objects 19541 textChooser = sofia.globalTextChooser, 19542 19543 parallelsList = container.find('.parallel-list select'), 19544 19545 // settings 19546 currentTextInfo = null, 19547 textsInitialized = false, 19548 parallelsData = null; 19549 19550 parallelsList.on('change', function() { 19551 19552 loadParallelData(); 19553 }); 19554 19555 textlistui.on('click', function(e) { 19556 19557 // if this is selected, then toggle 19558 if (textChooser.getTarget() == textlistui) { 19559 textChooser.toggle(); 19560 } else { 19561 textChooser.setTarget(container, textlistui, 'bible'); 19562 textChooser.setTextInfo(currentTextInfo); 19563 textChooser.show(); 19564 } 19565 19566 }); 19567 19568 19569 19570 textChooser.on('change', function (e) { 19571 19572 if (e.data.target != textlistui) { 19573 return; 19574 } 19575 19576 var newTextInfo = e.data.textInfo; 19577 19578 // ALWAYS UPDATE: for first load 19579 // update version name 19580 textlistui.html( newTextInfo.abbr ); 19581 19582 // if it has *changed* then we need to reload the text in the scroller 19583 if (currentTextInfo == null || newTextInfo.id != currentTextInfo.id) { 19584 19585 // store 19586 currentTextInfo = newTextInfo; 19587 19588 // load new text 19589 main.html(''); 19590 19591 loadParallelData(); 19592 } 19593 19594 }); 19595 19596 19597 // START UP 19598 19599 function init() { 19600 19601 // TEMP 19602 textlistui.html('Version'); 19603 19604 console.log('textsindow init',init_data, textsInitialized); 19605 19606 if (init_data == null) { 19607 return; 19608 } 19609 19610 sofia.ajax({ 19611 19612 dataType: 'json', 19613 url: 'content/parallels/parallels.json', 19614 success: function(data) { 19615 19616 parallelsData = data.parallels; 19617 19618 for (var i=0, il=parallelsData.length; i<il; i++) { 19619 parallelsList.append('<option data-id="' + parallelsData[i].id + '" value="' + parallelsData[i].filename + '">' + parallelsData[i].title + '</option>'); 19620 } 19621 19622 // 19623 console.log('parallel init', init_data.parallelid); 19624 19625 if (init_data.parallelid) { 19626 19627 parallelsList.find('option[data-id="' + init_data.parallelid + '"]').prop('selected', true); 19628 19629 } else { 19630 19631 parallelsList.find('option[data-id*="gospel"]:first').prop('selected', true); 19632 19633 console.log('first gospel', parallelsList.find('option[data-id~="gospel"]:first')); 19634 } 19635 19636 startup(); 19637 } 19638 }); 19639 19640 19641 // load the text specified by the init data 19642 TextLoader.getText(init_data.textid, 19643 19644 // success 19645 function(loadedTextInfo) { 19646 19647 // store this setting 19648 currentTextInfo = loadedTextInfo; 19649 19650 textsInitialized = true; 19651 19652 // send to objects 19653 textChooser.setTextInfo(currentTextInfo); 19654 textlistui.html(currentTextInfo.abbr); 19655 19656 startup(); 19657 }, 19658 19659 // error handler 19660 function() { 19661 19662 console.log('ERROR', init_data.textid, 'doesnt exist'); 19663 19664 // load all possible versions 19665 TextLoader.loadTexts(function(textInfoData) { 19666 19667 // find a text with the same language 19668 var newTextInfo = null, 19669 lang = init_data.textid.toString().split('-')[0].split('_')[0]; 19670 19671 for (var i=0, il=textInfoData.length; i<il; i++) { 19672 var textInfo = textInfoData[i]; 19673 19674 if (textInfo.type == text_type && (textInfo.lang == lang || textInfo.id.substring(0, lang.length) == lang)) { 19675 newTextInfo = textInfo; 19676 break; 19677 } 19678 } 19679 19680 // still nothing 19681 if (newTextInfo == null) { 19682 newTextInfo = textInfoData[0]; 19683 } 19684 19685 // let's try again with first one 19686 TextLoader.getText(newTextInfo.id, function(loadedTextInfo) { 19687 // store this setting 19688 currentTextInfo = loadedTextInfo; 19689 19690 textsInitialized = true; 19691 19692 // send to objects 19693 textChooser.setTextInfo(currentTextInfo); 19694 textlistui.html(currentTextInfo.abbr); 19695 19696 startup(); 19697 });
19698 19699 }); 19700 19701 }); 19702 } 19703 19704 function startup() { 19705 19706 19707 if (textsInitialized && parallelsData != null) { 19708 // load ! 19709 loadParallelData(); 19710 } 19711 } 19712 19713 init(); 19714 19715 19716 var currentParallelData = null, 19717 currentCells = null, 19718 currentCellIndex = -1; 19719 19720 function loadParallelData() { 19721 19722 // reset 19723 main.html( '' ); 19724 19725 currentParallelData = null; 19726 currentCells = null; 19727 currentCellIndex = -1; 19728 19729 console.log('parallels', parallelsList.val()); 19730 19731 sofia.ajax({ 19732 dataType: 'json', 19733 url: 'content/parallels/' + parallelsList.val(), 19734 success: function(data) { 19735 console.log('loaded parallel data', data); 19736 currentParallelData = data; 19737 createParallel(); 19738 }, 19739 error: function(e) { 19740 console.log('error laoding parallel data', e); 19741 } 19742 }); 19743 } 19744 19745 //var style = 'separatetitle'; 19746 var columnFormat = 'inlinetitle'; 19747 19748 function createParallel() { 19749 19750 var html = []; 19751 19752 // title 19753 html.push('<h1>' + currentParallelData.title + '</h1>'); 19754 19755 html.push('<p class="parallel-description">' + currentParallelData.description + '</p>'); 19756 19757 html.push('<div class="parallels-buttons">'); 19758 html.push('<span class="parallel-show-all">' + i18n.t('windows.parallel.showall') + '</span>'); 19759 html.push('<span class="parallel-hide-all">' + i18n.t('windows.parallel.hideall') + '</span>'); 19760 html.push('</div>'); 19761 19762 // opening 19763 html.push('<table dir="' + currentTextInfo.dir + '">'); 19764 19765 19766 19767 if (columnFormat == 'inlinetitle') { 19768 19769 var style = ' style="width: ' + (100/(currentParallelData.books.length+1)) + '%"'; 19770 19771 // main 19772 html.push('<tbody>'); 19773 for (var i=0, il=currentParallelData.parallels.length; i<il; i++) { 19774 var row = currentParallelData.parallels[i]; 19775 19776 if (typeof row.sectionTitle != 'undefined') { 19777 html.push('<tr><th class="section-title" colspan="' + (currentParallelData.books.length+1) + '">' + row.sectionTitle + '</th></tr>'); 19778 } else { 19779 19780 19781 // title row 19782 html.push('<tr class="parallel-entry-header">' + 19783 '<th class="parallel-title" ' + style + '>' + 19784 row.title + 19785 '</th>'); 19786 19787 19788 19789 // Passage titles 19790 for (var j=0, jl=row.passages.length; j<jl; j++) { 19791 var passage = row.passages[j]; 19792 19793 if (passage == null) { 19794 html.push('<td class="parallel-passage" ' + style + '>-</td>'); 19795 } else { 19796 var books = row.books ? row.books : currentParallelData.books; 19797 19798 //console.log(currentTextInfo.lang); 19799 html.push('<td class="parallel-passage" ' + style + ' lang="' + iso2iana.convert(currentTextInfo.lang) + '">' + 19800 bible.BOOK_DATA[books[j]].names[currentTextInfo.lang][0] + ' ' + passage + 19801 '</td>'); 19802 } 19803 } 19804 19805 html.push('</tr>'); 19806 19807 19808 // text 19809 html.push('<tr class="parallel-entry-text parallel-entry-text-collapsed">'); 19810 html.push('<th></th>'); 19811 19812 for (var j=0, jl=row.passages.length; j<jl; j++) { 19813 var passage = row.passages[j]; 19814 19815 if (passage == null) { 19816 html.push('<td></td>'); 19817 } else { 19818 19819 var books = row.books ? row.books : currentParallelData.books; 19820 19821 html.push('<td class="reading-text" data-bookid="' + books[j] + '" ' + 19822 'data-passage="' + passage + '" lang="' + iso2iana.convert(currentTextInfo.lang) + '">' + 19823 //i18n.t('windows.parallel.loading') + 19824 '</td>'); 19825 } 19826 } 19827 19828 html.push('</tr>'); 19829 19830 } 19831 } 19832 html.push('</tbody>'); 19833 } 19834 19835 if (columnFormat == 'separatetitle') { 19836 19837 // main 19838 html.push('<tbody>'); 19839 for (var i=0, il=currentParallelData.parallels.length; i<il; i++) { 19840 var row = currentParallelData.parallels[i]; 19841 19842 if (typeof row.sectionTitle != 'undefined') { 19843 html.push('<tr><th class="section-title" colspan="' + (currentParallelData.books.length).toString() + '">' + row.sectionTitle + '</th></tr>'); 19844 } else { 19845 19846 html.push('<tr><th class="parallel-title" colspan="' + (currentParallelData.books.length).toString() + '">' + row.title + '</th></tr>'); 19847 19848 19849 19850 // Passage titles 19851 html.push('<tr class="passage-row passage-notloaded">'); 19852 19853 for (var j=0, jl=row.passages.length; j<jl; j++) { 19854 var passage = row.passages[j];
19855 19856 if (passage == null) { 19857 html.push('<td class="parallel-passage">-</td>'); 19858 } else { 19859 //console.log(currentTextInfo.lang); 19860 html.push('<td class="parallel-passage" data-bookid="' + currentParallelData.books[j] + '" ' + 19861 'data-passage="' + passage + '">' + 19862 bible.BOOK_DATA[currentParallelData.books[j]].names[currentTextInfo.lang][0] + ' ' + passage + 19863 '</td>'); 19864 } 19865 } 19866 19867 html.push('</tr>'); 19868 19869 } 19870 } 19871 html.push('</tbody>'); 19872 } 19873 19874 19875 19876 19877 // close 19878 html.push('</table>'); 19879 19880 main.html( html.join('') ); 19881 19882 19883 // find all TDs 19884 //currentCells = main.find('td'); 19885 //currentCellIndex = 0; 19886 //loadNextPassage(); 19887 } 19888 19889 if (columnFormat == 'separatetitle') { 19890 main.on('click', '.passage-notloaded', function() { 19891 var row = $(this); 19892 19893 row.removeClass('passage-notloaded'); 19894 19895 var textRow = row.clone(); // .after(row); 19896 19897 textRow.find('td').each(function() { 19898 var cell = $(this).removeClass('parallel-passage'); 19899 19900 if (cell.attr('data-passage')) { 19901 cell.html(i18n.t('windows.parallel.loading')); 19902 } else { 19903 cell.html(''); 19904 } 19905 }); 19906 19907 row.after(textRow); 19908 19909 currentCells = textRow.find('td'); 19910 currentCellIndex = 0; 19911 19912 19913 loadNextPassage(); 19914 19915 }); 19916 } 19917 19918 if (columnFormat == 'inlinetitle') { 19919 main.on('click', '.parallel-entry-header', function() { 19920 var headerRow = $(this), 19921 textRow = headerRow.next('tr'); 19922 19923 if (textRow.hasClass('parallel-entry-text-collapsed')) { 19924 // load 19925 textRow.removeClass('parallel-entry-text-collapsed') 19926 19927 currentCells = textRow.find('td'); 19928 currentCellIndex = 0; 19929 19930 loadNextPassage(); 19931 19932 } else { 19933 19934 textRow.addClass('parallel-entry-text-collapsed') 19935 19936 } 19937 }); 19938 } 19939 19940 main.on('click', '.parallel-show-all', function() { 19941 19942 currentCells = $(); 19943 19944 // find any closed ones 19945 /* 19946 main.find('tr.parallel-entry-text-collapsed').each(function() { 19947 var tr = $(this).removeClass('parallel-entry-text-collapsed'); 19948 19949 19950 currentCells.add(tr.find('td')); 19951 19952 19953 19954 /* 19955 td.find('td').each(function() { 19956 processCell( $(this), null); 19957 }); 19958 19959 }); 19960 */ 19961 19962 19963 currentCells = main.find('tr.parallel-entry-text-collapsed td'); 19964 19965 console.log('total cells', currentCells.length); 19966 19967 currentCellIndex = 0; 19968 loadNextPassage(); 19969 19970 }); 19971 19972 main.on('click', '.parallel-hide-all', function() { 19973 19974 // find any closed ones 19975 main.find('tr.parallel-entry-text').each(function() { 19976 var tr = $(this).addClass('parallel-entry-text-collapsed'); 19977 }); 19978 19979 19980 }); 19981 19982 19983 19984 function loadNextPassage() { 19985 if (currentCellIndex < currentCells.length) { 19986 19987 // find passage and load 19988 var cell = currentCells.eq(currentCellIndex); 19989 19990 processCell(cell, function() { 19991 currentCellIndex++; 19992 loadNextPassage(); 19993 }); 19994 } 19995 } 19996 19997 function processCell(cell, callback) { 19998 19999 cell.closest('tr').removeClass('parallel-entry-text-collapsed'); 20000 20001 if (cell.hasClass('parallel-text-loaded')) { 20002 20003 if (callback) { 20004 callback(); 20005 } 20006 20007 return; 20008 } 20009 20010 var 20011 bookid = cell.attr('data-bookid'), 20012 passage = cell.attr('data-passage'); 20013 20014 if (bookid && passage) { 20015 20016 // load 20017 //console.log(bookid, passage); 20018 20019 var sectionid = bookid + passage.split(':')[0], 20020 verseParts = passage.split(':')[1], 20021 verseRanges = verseParts.split(','), 20022 fragmentids = []; 20023 20024 // go through each ##-##,##-## 20025 for (var i=0, il=verseRanges.length; i<il; i++) { 20026 var verseRange = verseRanges[i].split('-'); 20027 20028 if (verseRange.length == 1) { 20029 fragmentids.push(sectionid + '_' + verseRange[0].trim()); 20030 } else if (verseRange.length == 2) { 20031 var start = parseInt(verseRange[0], 10), 20032 end= parseInt(verseRange[1], 10); 20033 20034 20035 for (verse = start; verse<= end; verse++) { 20036 fragmentids.push(sectionid + '_' + verse); 20037 } 20038 } 20039 } 20040 20041 if (verseRanges.length > 1){ 20042 console.log(verseParts, fragmentids); 20043 } 20044 20045 TextLoader.loadSection( currentTextInfo, sectionid, function(content) { 20046 20047 // remove 20048 content.find('.cf,.note').remove(); 20049 20050 cell.html(''); 20051 //cell.append($('<span class="passage">' + bookid + ' ' + passage + '</span>')); 20052 20053 for (var i=0, il=fragmentids.length; i<il; i++) { 20054 var fragmentid = fragmentids[i], 20055 verseNode = content.find('.v[data-id="' + fragmentid + '"]'); 20056 20057 //console.log(verseNode.prev()); 20058 20059 if (verseNode.prev().hasClass('v-num')) {
20060 cell.append(verseNode.prev()); 20061 } 20062 20063 cell.append(verseNode); 20064 } 20065 20066 cell.addClass('parallel-text-loaded'); 20067 20068 20069 20070 if (callback) 20071 callback(); 20072 20073 }, function() { 20074 20075 if (callback) 20076 callback(); 20077 }); 20078 20079 20080 } else { 20081 20082 if (callback) 20083 callback(); 20084 } 20085 20086 20087 } 20088 20089 20090 function size(width, height) { 20091 20092 //console.log('newsize',width,height); 20093 20094 container 20095 .outerWidth(width) 20096 .outerHeight(height); 20097 20098 20099 main 20100 .outerWidth(width) 20101 .outerHeight( container.height() - header.outerHeight(true)); 20102 20103 20104 textChooser.size(width, height); 20105 } 20106 20107 20108 function getData() { 20109 20110 var data = { 20111 // textinfo 20112 textid: currentTextInfo.providerid, 20113 parallelid: parallelsList.find('option:selected').attr('data-id'), 20114 label: 'Parallel', 20115 labelLong: 'Parallel', 20116 params: { 20117 'win': 'parallel', 20118 'textid': currentTextInfo.providerid, 20119 'parallelid': parallelsList.find('option:selected').attr('data-id') 20120 } 20121 }; 20122 20123 return data; 20124 } 20125 20126 20127 function close() { 20128 20129 textChooser.close(); 20130 20131 ext.clearListeners(); 20132 } 20133 20134 var ext = { 20135 size: size, 20136 getData: getData, 20137 close: close 20138 } 20139 ext = $.extend(true, ext, EventEmitter); 20140 20141 return ext; 20142}; 20143 20144 20145sofia.initMethods.push(function() { 20146 20147 sofia.windowTypes.push( { 20148 className:'ParallelsWindow', 20149 param: 'parallel', 20150 paramKeys: { 20151 'textid': 't', 20152 'parallelid': 'p' 20153 }, 20154 init: { 20155 'textid': sofia.config.newBibleWindowVersion 20156 } 20157 }); 20158 20159}); 20160 20161 20162 20163/********* 20164* ../app/js/windows/scroller.js 20165**********/ 20166 20167var Scroller = function(node) { 20168 20169 var 20170 wrapper = node.find('.scroller-text-wrapper'), 20171 currentTextInfo = null, 20172 locationInfo = {}, 20173 ignoreScrollEvent = false, 20174 speedLastPos = null, 20175 speedDelta = 0; 20176 20177 speedIndicator = $('<div class="scroller-speed" style="z-index: 50; position: absolute; top: 0; left: 0; width: 50; background: black; padding: 5px;color:#fff"></div>') 20178 .appendTo(node.parent()) 20179 .hide(); 20180 20181 20182 var globalTimeout = null; 20183 20184 function startGlobalTimeout() { 20185 if (globalTimeout == null) { 20186 setTimeout(triggerGlobalEvent, 10); 20187 } 20188 } 20189 20190 function triggerGlobalEvent() { 20191 20192 ext.trigger('globalmessage', {type: 'globalmessage', target: this, data: {messagetype: 'nav', type: currentTextInfo.type.toLowerCase(), locationInfo: locationInfo}}); 20193 20194 clearTimeout(globalTimeout); 20195 globalTimeout = null; 20196 } 20197 20198 node.on('scroll', function() { 20199 20200 if (ignoreScrollEvent) { 20201 return; 20202 } 20203 //console.log('sendingscroll'); 20204 20205 update_location_info(); 20206 20207 ext.trigger('scroll', {type: 'scroll', target: this, data: {locationInfo: locationInfo}}); 20208 startGlobalTimeout(); 20209 20210 start_speed_test(); 20211 }); 20212 20213 20214 var speedInterval = null; 20215 20216 function start_speed_test() { 20217 if (speedInterval == null) { 20218 20219 speedInterval = setInterval(checkSpeed, 100); 20220 } 20221 } 20222 20223 function stop_speed_test() { 20224 if (speedInterval != null) { 20225 clearInterval(speedInterval); 20226 speedInterval = null; 20227 } 20228 20229 } 20230 20231 function checkSpeed() { 20232 20233 var speedNewPos = node.scrollTop(); 20234 if ( speedLastPos != null ){ // && newPos < maxScroll 20235 speedDelta = speedNewPos - speedLastPos; 20236 } 20237 speedLastPos = speedNewPos; 20238 20239 if (speedDelta === 0) { 20240 load_more(); 20241 stop_speed_test(); 20242 } 20243 20244/* 20245 if (speedDelta != null) { 20246 speedIndicator.html(speedDelta); 20247 } 20248*/ 20249 } 20250 20251 20252 20253 // find the top most visible node (verse, page, etc.) 20254 // pass it up as an event 20255 function update_location_info() { 20256 20257 // reset info 20258 var 20259 topOfContentArea = node.offset().top, 20260 sectionid = '', 20261 fragmentid = '', 20262 label = '', 20263 labelLong = '', 20264 fragmentSelector = currentTextInfo.fragmentSelector, 20265 newLocationInfo = null; 20266 20267 // magic for bibles or books 20268 if (typeof fragmentSelector == 'undefined' || fragmentSelector == '') { 20269 switch (currentTextInfo.type.toLowerCase()) { 20270 case 'videobible': 20271 case 'deafbible': 20272 case 'bible': 20273 case 'commentary': 20274 // find top 20275 fragmentSelector = '.verse, .v'; 20276 20277 break; 20278 case 'book': 20279 // find top 20280 fragmentSelector = '.page'; 20281 20282 break; 20283 } 20284 } 20285 20286 20287 var fragments = node.find( fragmentSelector ); 20288 20289 if (fragments.length == 1) { 20290 fragments = node.find('.section'); 20291 } 20292 20293 // look through all the markers and find the first one that is fully visible 20294 fragments.each(function(e) { 20295 var fragment = $(this), 20296 isFirstVisibleFragment = false; 20297 20298 // is the top of the fragment at the top of the scroll pane 20299 if (fragment.offset().top - topOfContentArea > -2) { 20300 isFirstVisibleFragment = true; 20301 20302 fragmentid = fragment.attr('data-id'); 20303 var totalFragments = fragment.parent().find('.' + fragmentid); 20304 20305 // multi-line verses 20306 if (totalFragments.length > 1) { 20307 20308 fragment = totalFragments.first(); 20309 20310 if (fragment.offset().top - topOfContentArea > -2) { 20311 isFirstVisibleFragment = true; 20312 } else { 20313 isFirstVisibleFragment = false; 20314 } 20315 } 20316 20317 } 20318 20319 if (isFirstVisibleFragment) { 20320 20321 // TODO: are there other fragments with this id 20322 20323 fragmentid = fragment.attr('data-id'); 20324 20325 20326 switch (currentTextInfo.type.toLowerCase()) { 20327 case 'videobible': 20328 case 'deafbible': 20329 case 'bible': 20330 case 'commentary': 20331 // find top 20332 var bibleref = new bible.Reference( fragmentid ); 20333 bibleref.language = currentTextInfo.lang; 20334 20335 label = bibleref.toString(); 20336 labelLong = label + ' (' + currentTextInfo.abbr + ')'; 20337 20338 break; 20339 case 'book': 20340 labelLong = label = currentTextInfo.name + ' ' + sectionid; 20341 20342 break; 20343 } 20344 20345 20346 // pass the marker data 20347 newLocationInfo = { 20348 // verse ID 20349 fragmentid: fragment.attr('data-id'), 20350 20351 sectionid: fragment.hasClass('section') ? fragment.attr('data-id') : fragment.closest('.section').attr('data-id'), 20352 20353 // extra positioning info 20354 offset: topOfContentArea - fragment.offset().top, 20355 20356 label: label, 20357 20358 labelLong: labelLong, 20359 20360 textid: currentTextInfo.id 20361 20362 }; 20363 return false; 20364 } 20365 20366 // means "keep looking" :) 20367 return true; 20368 }); 20369 20370 // found a fragment 20371 if (newLocationInfo != null && (locationInfo == null || newLocationInfo.fragmentid != locationInfo.fragmentid)) { 20372 ext.trigger('locationchange', {type:'locationchange', target: this, data: newLocationInfo}); 20373 } 20374 20375 //console.log('new location', newLocationInfo); 20376 20377 locationInfo = newLocationInfo; 20378 }; 20379 20380 20381 /* 20382 var load_more_timeout = null; 20383 function start_load_more_timeout() { 20384 20385 if (load_more_timeout === null) { 20386 20387 load_more_timeout = setTimeout(function() { 20388 load_more(); 20389 clearTimeout( load_more_timeout ); 20390 load_more_timeout = null; 20391 }, 100); 20392 20393 } 20394 } 20395 */ 20396 20397 function load_more() { 20398 20399 // measure top and bottom height 20400 var 20401 fragmentid = null; 20402 wrapper_height = wrapper.height(), 20403 node_height = node.height(), 20404 above_top = node_scrolltop = node.scrollTop(), 20405 sections = wrapper.find( '.section' ), 20406 sections_count = sections.length, 20407 total_height = 0, 20408 below_bottom = wrapper_height /*total_height*/ - node_height - node_scrolltop; 20409 20410 //console.log('load_more', speedDelta, below_bottom < node_height*2, above_top < node_height*2, above_top > node_height*15, sections_count > 4 && below_bottom > node_height*15) 20411 20412 20413 // only load if stopped 20414 if (speedDelta == 0) { 20415 20416 // add below 20417 if (below_bottom < node_height*2) { 20418 20419 fragmentid = sections 20420 .last() // the last chapter (bottom) 20421 .attr( 'data-nextid' ); 20422 20423 if (fragmentid != null && fragmentid != 'null' && sections.length < 50) { 20424 load('next', fragmentid); 20425 } 20426 20427 20428 } 20429 20430 // add above 20431 else if (above_top < node_height*2) { 20432 20433 fragmentid = sections 20434 .first() // the first chapter (top)
20435 .attr( 'data-previd' ); 20436 20437 //console.warn('load prev', fragmentid); 20438 20439 if (fragmentid != null && fragmentid != 'null' && sections.length < 50) { 20440 load('prev',fragmentid); 20441 } 20442 20443 } 20444 20445 20446 20447 // remove above 20448 else if (above_top > node_height*15) { 20449 //console.warn('remove above'); 20450 20451 if (wrapper.children().length >= 2) { 20452 20453 // we're removing the first section, so we need to find the first one and 20454 // measure where its first child should appear 20455 var first_node_of_second_section = wrapper.find('.section:eq(1)').children().first(), 20456 first_node_offset_before = first_node_of_second_section.offset().top; 20457 20458 wrapper.find('.section:first').remove(); 20459 20460 // now, remeasure where the first node appears and adjust the scrolltop 20461 var 20462 first_node_offset_after = first_node_of_second_section.offset().top, 20463 offset_difference = first_node_offset_after - first_node_offset_before; 20464 new_scrolltop = node.scrollTop(); 20465 updated_scrolltop = new_scrolltop - Math.abs(offset_difference); 20466 20467 node.scrollTop(updated_scrolltop); 20468 } 20469 } 20470 20471 20472 // remove below 20473 else if (sections_count > 4 && below_bottom > node_height*15) { 20474 //console.warn('remove below', below_bottom, node_height); 20475 20476 wrapper.find('.section:last').remove(); 20477 } 20478 } else { 20479 20480 20481 } 20482 20483 } 20484 20485 function load(loadType, sectionid, fragmentid) { 20486 20487 if (sectionid === 'null' || sectionid === null || sectionid == '') { 20488 return; 20489 } 20490 20491 // check if this exists 20492 //if ( wrapper.find('[data-id="' + sectionid + '"]').length > 0 ) { 20493 if ( wrapper.find('.' + sectionid).length > 0 ) { 20494 20495 if (fragmentid && fragmentid.trim() != '' && wrapper.find('.' + fragmentid).length > 0) { 20496 scrollTo(fragmentid); 20497 } 20498 return; 20499 } 20500 20501 if (loadType == 'text') { 20502 wrapper.html('<div class="loading-indicator" style="height:' + node.height() + 'px;"></div>'); 20503 node.scrollTop(0); 20504 } 20505 20506 //console.log(loadType, sectionid, fragmentid); 20507 20508 TextLoader.loadSection( currentTextInfo, sectionid, function(content) { 20509 20510 // check if this exists 20511 //if ( wrapper.find('[data-id="' + sectionid + '"]').length > 0 ) { 20512 if ( wrapper.find('.' + sectionid).length > 0 ) { 20513 20514 if (fragmentid && fragmentid.trim() != '' && wrapper.find('.' + fragmentid).length > 0) { 20515 scrollTo(fragmentid); 20516 } 20517 return; 20518 } 20519 20520 ignoreScrollEvent = true; 20521 20522 switch (loadType) { 20523 default: 20524 case 'text': 20525 20526 // clear out and reset 20527 wrapper.html(''); 20528 node.scrollTop(0); 20529 wrapper.append(content); 20530 20531 20532 20533 // TODO: scrollto fragmentid 20534 if (fragmentid) { 20535 scrollTo(fragmentid); 20536 } 20537 20538 locationInfo = null; 20539 20540 update_location_info(); 20541 //ext.trigger('scroll', {type: 'scroll', target: this, data: {locationInfo: locationInfo}}); 20542 20543 break; 20544 20545 case 'next': 20546 20547 wrapper.append(content); 20548 20549 20550 break; 20551 20552 case 'prev': 20553 20554 // add to top, measure, then reset scroll position 20555 /* 20556 var node_scrolltop_before = node.scrollTop(), 20557 first_item = node.find('.section').children().first(); 20558 20559 if (first_item.length > 0) { 20560 var first_item_offset_top_before = first_item.offset().top; 20561 20562 // add to top and measure 20563 wrapper.prepend(content); 20564 20565 var first_item_offset_top_after = first_item.offset().top, 20566 offest_difference = first_item_offset_top_after - first_item_offset_top_before, 20567 new_scrolltop = node_scrolltop_before + offest_difference; 20568 20569 node.scrollTop( Math.abs(new_scrolltop)); 20570 } 20571 */ 20572 20573 // add to bottom, then move up 20574 20575 var node_scrolltop_before = node.scrollTop(); 20576 var wrapper_height_before = wrapper.height(); 20577 wrapper.append(content); 20578 var wrapper_height_after = wrapper.height(); 20579 wrapper.prepend(content); 20580 20581 var height_difference = wrapper_height_after - wrapper_height_before, 20582 new_scrolltop = node_scrolltop_before + height_difference; 20583 20584 node.scrollTop( Math.abs(new_scrolltop )); 20585 20586 break; 20587 20588 } 20589 20590 ignoreScrollEvent = false; 20591 20592 // send load event up to Window/App 20593 //console.log('loaded', sectionid); 20594 ext.trigger('globalmessage', {type: 'globalmessage', 20595 target: this, 20596 data: { 20597 messagetype: 'textload', 20598 texttype: currentTextInfo.type.toLowerCase(), 20599 type: currentTextInfo.type.toLowerCase(), 20600 textid: currentTextInfo.id, 20601 abbr: currentTextInfo.abbr, 20602 sectionid: sectionid, 20603 fragmentid: fragmentid, 20604 content:content 20605 } 20606 });
20607 20608 load_more(); 20609 //start_load_more_timeout(); 20610 }); 20611 20612 } 20613 20614 function scrollTo(fragmentid, offset) { 20615 20616 if (typeof fragmentid == 'undefined') { 20617 return; 20618 } 20619 20620 20621 // find the fragment 20622 var fragment = wrapper.find('.' + fragmentid); 20623 20624 //console.log('scrollTo', fragmentid, fragment.length); 20625 20626 // if it exists, we'll move to it 20627 if (fragment.length > 0) { 20628 20629 var 20630 // calculate node position 20631 paneTop = node.offset().top, 20632 scrollTop = node.scrollTop(), 20633 nodeTop = fragment.offset().top, 20634 nodeTopAdjusted = nodeTop - paneTop + scrollTop; 20635 20636 // go to it 20637 ignoreScrollEvent = true; 20638 node.scrollTop(nodeTopAdjusted + (offset || 0)); 20639 ignoreScrollEvent = false; 20640 } 20641 // if it's not there, we'll see if we can load it for this book (or bible version) 20642 else { 20643 // need to load it! 20644 //console.log('need to load', fragmentid); 20645 20646 var sectionid = fragmentid.split('_')[0], 20647 hasSection = currentTextInfo != null ? currentTextInfo.sections.indexOf(sectionid) > -1 : false; 20648 20649 if (hasSection) { 20650 load('text', sectionid, fragmentid); 20651 } 20652 20653 } 20654 } 20655 20656 function size(width, height) { 20657 node 20658 .width(width) 20659 .height(height); 20660 } 20661 20662 20663 function getTextInfo() { 20664 return currentTextInfo; 20665 } 20666 20667 function setTextInfo(textinfo) { 20668 20669 if (typeof textinfo.stylesheet != 'undefined' ) { 20670 20671 var styleId = 'style-' + textinfo.id, 20672 styleLink = $('#' + styleId); 20673 20674 if (styleLink.length == 0) { 20675 styleLink = $('<link id="' + styleId + '" rel="stylesheet" href="' + sofia.config.baseContentUrl + 'content/texts/' + textinfo.id + '/' + textinfo.stylesheet + '" />') 20676 .appendTo($('head')); 20677 } 20678 } 20679 20680 20681 currentTextInfo = textinfo; 20682 } 20683 20684 function getLocationInfo() { 20685 return locationInfo; 20686 } 20687 20688 function setFocus(newHasFocus) { 20689 hasFocus = newHasFocus; 20690 } 20691 20692 20693 var ext = { 20694 load_more: load_more, 20695 load: load, 20696 size: size, 20697 getTextInfo: getTextInfo, 20698 setTextInfo: setTextInfo, 20699 getLocationInfo: getLocationInfo, 20700 scrollTo: scrollTo, 20701 setFocus: setFocus 20702 }; 20703 20704 ext = $.extend(true, ext, EventEmitter); 20705 20706 return ext; 20707 20708}; 20709 20710 20711 20712/********* 20713* ../app/js/windows/scrolleraudio.js 20714**********/ 20715 20716var AudioController = function(id, container, toggleButton, scroller) { 20717 20718 var block = $( 20719 '<div class="audio-controller">' + 20720 '<audio></audio>' + 20721 '<div class="audio-slider">' + 20722 '<div class="audio-slider-current"></div>' + 20723 '<div class="audio-slider-loaded"></div>' + 20724 '<span class="audio-slider-handle"></span>' + 20725 '</div>' + 20726 '<input type="button" class="audio-prev" value="Prev" />' + 20727 '<input type="button" class="audio-play" value="Play" />' + 20728 '<input type="button" class="audio-next" value="Next" />' + 20729 '<span class="audio-currenttime">00:00</span>' + 20730 '<span class="audio-duration">00:00</span>' + 20731 '<span class="audio-title"></span>' + 20732 '<span class="audio-subtitle"></span>' + 20733 '<input type="button" class="audio-options-button image-config-light" />' + 20734 '</div>' 20735 ).appendTo(container), 20736 optionsButton = block.find('.audio-options-button'), 20737 20738 options = $( 20739 '<div class="audio-options">' + 20740 '<span class="close-button"></span>' + 20741 '<strong class="i18n" data-i18n="[html]windows.audio.options"></strong>' + 20742 '<label><input type="checkbox" class="audio-scroll" checked /><span class="i18n" data-i18n="[html]windows.audio.synctext" /></label>' + 20743 '<label><input type="checkbox" class="audio-autoplay" checked /><span class="i18n" data-i18n="[html]windows.audio.autoplay" /></label>' + 20744 20745 '<div class="audio-dramatic-option">' + 20746 '<label><input type="radio" name="' + id + '-dramatic-option" class="audio-dramatic-audio" disabled /><span class="i18n" data-i18n="[html]windows.audio.nondrama" /></label>' + 20747 '<label><input type="radio" name="' + id + '-dramatic-option" class="audio-dramatic-drama" disabled /><span class="i18n" data-i18n="[html]windows.audio.drama" /></label>' + 20748 '</div>' + 20749 '</div>' 20750 ).appendTo(container), 20751 scrollCheckbox = options.find('.audio-scroll').prop('checked', true), 20752 autoplayCheckbox = options.find('.audio-autoplay').prop('checked', true), 20753 optionsCloseButton = options.find('.close-button'), 20754 20755 optionsDramaticBox = options.find('.audio-dramatic-option'), 20756 optionsDramaticDrama = options.find('.audio-dramatic-drama'), 20757 optionsDramaticAudio = options.find('.audio-dramatic-audio'), 20758 20759 audio = block.find('audio')[0], 20760 20761 playButton = block.find('.audio-play'), 20762 nextButton = block.find('.audio-next'), 20763 prevButton = block.find('.audio-prev'), 20764 currenttime = block.find('.audio-currenttime'), 20765 duration = block.find('.audio-duration'), 20766 title = block.find('.audio-title'), 20767 subtitle = block.find('.audio-subtitle'), 20768 20769 audioSlider = block.find('.audio-slider'), 20770 audioSliderCurrent = block.find('.audio-slider-current'), 20771 audioSliderHandle = block.find('.audio-slider-handle'), 20772 20773 isDraggingSliderHandle = false, 20774 20775 textInfo = null, 20776 audioInfo = null, 20777 locationInfo = null, 20778 sectionid = '', 20779 fragmentid = '', 20780 fragmentAudioData = null, 20781 loadAudioWhenPlayIsPressed = false, 20782 sectionHeight = 0, 20783 sectionNode = null, 20784 hasAudio = false, 20785 audioDataManager = new AudioDataManager(); 20786 20787 // START UP 20788 options.find('.i18n').i18n(); 20789 20790 if (toggleButton != null) { 20791 toggleButton.hide(); 20792 block.hide(); 20793 } 20794 options.hide(); 20795 20796 // OPTIONS 20797 optionsButton.on('click', function() { 20798 if (options.is(':visible')) { 20799 options.hide(); 20800 20801 $(document).off('click', doc_click); 20802 } else { 20803 options.show(); 20804 20805 setTimeout(function() { 20806 $(document).on('click', doc_click); 20807 }); 20808 } 20809 }); 20810 optionsCloseButton.on('click', function() { 20811 options.hide(); 20812 $(document).off('click', doc_click); 20813 }); 20814 20815 20816 // click off functionality 20817 function doc_click(e) { 20818 20819 var target = $(e.target), 20820 clickedOnOptions = false; 20821 20822 // go through all nested clicked elements 20823 while (target != null && target.length > 0) { 20824 20825 if (target[0] == options[0]) { 20826 clickedOnOptions = true; 20827 break; 20828 } 20829 20830 target = target.parent(); 20831 } 20832 20833 //return; 20834 if (!clickedOnOptions) { 20835 e.preventDefault(); 20836 20837 options.hide(); 20838 $(document).off('click', doc_click); 20839 20840 return false; 20841 } 20842 } 20843 20844 optionsDramaticAudio.on('change', updateDramatic); 20845 optionsDramaticDrama.on('change', updateDramatic); 20846 20847 function updateDramatic() { 20848 20849 var storedFragmentid = fragmentid; 20850 20851 // kill all existing values 20852 fragmentid = ''; 20853 sectionid = ''; 20854 fragmentAudioData = null; 20855 loadAudioWhenPlayIsPressed = false; 20856 20857 // stop audio 20858 if (!audio.paused && !audio.ended) { 20859 audio.pause(); 20860 audio.src = ''; 20861 } 20862 20863 $(audio).on('loadeddata', playWhenLoaded); 20864 loadAudio(storedFragmentid); 20865 } 20866 20867 // MAIN 20868 if (toggleButton != null) { 20869 toggleButton.on('click', function() { 20870 if (block.is(':visible')) { 20871 block.hide(); 20872 } else { 20873 block.show(); 20874 } 20875 }); 20876 } 20877 20878 playButton.on('click', function() { 20879 20880 if (audio.src == '' || audio.src == null) { 20881 20882 if (loadAudioWhenPlayIsPressed) { 20883 audio.src = fragmentAudioData.url; 20884 audio.load(); 20885 $(audio).on('loadeddata', playWhenLoaded); 20886 loadAudioWhenPlayIsPressed = false; 20887 } 20888 20889 return; 20890 } 20891 20892 20893 if (audio.paused || audio.ended) { 20894 audio.play(); 20895 } else { 20896 audio.pause(); 20897 } 20898 }); 20899 20900 prevButton.on('click', function() { 20901 20902 audioDataManager.getPrevFragment(textInfo, audioInfo, fragmentid, function(prevFragmentid) { 20903 20904 if (prevFragmentid == null) { 20905 return; 20906 } 20907 20908 if (scrollCheckbox.is(':checked')) { 20909 if (scroller != null && scroller.load) { 20910 scroller.load('text', prevFragmentid.split('_')[0], prevFragmentid); 20911 } 20912 } 20913 20914 if (fragmentAudioData == null || prevFragmentid != fragmentAudioData.fragmentid) { 20915 loadAudio(prevFragmentid); 20916 $(audio).on('loadeddata', playWhenLoaded); 20917 } 20918 }); 20919 20920 }); 20921 nextButton.on('click', function() { 20922 20923 audioDataManager.getNextFragment(textInfo, audioInfo, fragmentid, function(nextFragmentid) { 20924 20925 if (nextFragmentid == null) { 20926 return; 20927 } 20928 20929 if (scrollCheckbox.is(':checked')) { 20930 if (scroller != null && scroller.load) { 20931 scroller.load('text', nextFragmentid.split('_')[0], nextFragmentid); 20932 } 20933 } 20934 20935 if (fragmentAudioData == null || nextFragmentid != fragmentAudioData.fragmentid) { 20936 loadAudio(nextFragmentid); 20937 $(audio).on('loadeddata', playWhenLoaded); 20938 } 20939 }); 20940 20941 return; 20942 }); 20943 20944 // only for the smaller player below a scroller 20945 if (scroller != null) { 20946 20947 function updateLocation(e) { 20948 20949 var newLocationInfo = e.data; 20950 20951 // found a fragment 20952 if (newLocationInfo != null) { 20953 20954 locationInfo = newLocationInfo; 20955 20956 loadAudio(locationInfo.fragmentid); 20957 } 20958 } 20959 20960 scroller.on('locationchange', updateLocation); 20961 } 20962 20963 function loadAudio(newFragmentid) { 20964 20965 if (!hasAudio) { 20966 return; 20967 } 20968 20969 if (typeof newFragmentid == 'undefined') { 20970 return; 20971 } 20972 20973 if (fragmentid != newFragmentid) { 20974 20975 //// //console.log('AUDIO loading',newFragmentid,textInfo, hasAudio); 20976 20977 fragmentid = newFragmentid; 20978 20979 var newSectionid = fragmentid.split('_')[0], 20980 dbsBookCode = sectionid.substring(0,2), 20981 chaperNumber = sectionid.substring(2),
20982 loadNewData = audioInfo.pericopeBased || newSectionid != sectionid; 20983 20984 sectionid = newSectionid; 20985 20986 20987 // only do checks when we need to! 20988 if (loadNewData) { 20989 20990 var audioOption = optionsDramaticDrama.is(':checked') ? 'drama' : optionsDramaticAudio.is(':checked') ? 'audio' : ''; 20991 20992 audioDataManager.getFragmentAudio(textInfo, audioInfo, fragmentid, audioOption, function(newFragmentAudioData) { 20993 20994 // only update if this is new data 20995 if (fragmentAudioData == null || newFragmentAudioData == null || fragmentAudioData.id != newFragmentAudioData.id) { 20996 20997 20998 // if no data was received, then we need to hide everything 20999 if (!newFragmentAudioData || newFragmentAudioData.url == null) { 21000 audio.src = null; 21001 21002 title.html('[No audio]'); 21003 21004 if (toggleButton) { 21005 toggleButton.hide(); 21006 block.hide(); 21007 } 21008 21009 fragmentAudioData = newFragmentAudioData; 21010 return; 21011 } else { 21012 21013 // if we get a URL, then show the ear icon again 21014 if (toggleButton) toggleButton.show(); 21015 21016 // only when the previous data was null, do we reshow the control bar 21017 if (fragmentAudioData == null) { 21018 // let's not do it ever 21019 //block.show(); 21020 } 21021 21022 fragmentAudioData = newFragmentAudioData; 21023 } 21024 21025 // only load audio if player is visible 21026 if (block.is(':visible')) { 21027 //audio.currentTime = 0; 21028 audio.src = fragmentAudioData.url; 21029 audio.load(); 21030 } else { 21031 loadAudioWhenPlayIsPressed = true; 21032 } 21033 21034 21035 // store height info 21036 21037 // TODO: start and finish verse instead! 21038 sectionNode = container.find('.section[data-id="' + sectionid + '"]'); 21039 sectionHeight = sectionNode.height(); 21040 21041 // give feedback 21042 //title.html( new bible.Reference(sectionid).toString() + (audioInfo.title ? ' [' + audioInfo.title + ']' : '')); 21043 title.html( new bible.Reference(sectionid).toString() ); 21044 subtitle.html( audioInfo.title ); 21045 21046 } 21047 }); 21048 } 21049 } 21050 } 21051 21052 21053 function playWhenLoaded(){ 21054 //// //console.log('playWhenLoaded'); 21055 audio.play(); 21056 $(audio).off('loadeddata', playWhenLoaded); 21057 } 21058 21059 21060 $(audio) 21061 .on('play playing', function() { 21062 21063 var thisAudio = this; 21064 21065 playButton 21066 .attr('value','Pause') 21067 .addClass('playing'); 21068 21069 21070 // pause all other media on the page 21071 $('audio,video').each(function() { 21072 var audioOrVideoNode = this; 21073 21074 if (audioOrVideoNode != thisAudio && !audioOrVideoNode.paused && !audioOrVideoNode.ended) { 21075 audioOrVideoNode.pause(); 21076 } 21077 }); 21078 21079 }) 21080 .on('pause ended', function() { 21081 playButton 21082 .attr('value','Play') 21083 .removeClass('playing'); 21084 }) 21085 .on('loadstart', function() { 21086 playButton 21087 .attr('value','Play') 21088 .removeClass('playing'); 21089 21090 audioSliderHandle.css({left: '0%' }); 21091 currenttime.html( secondsToTimeCode(0) ); 21092 duration.html( secondsToTimeCode(0) ); 21093 }) 21094 .on('loadedmetadata', function() { 21095 duration.html( secondsToTimeCode(audio.duration) ); 21096 }) 21097 .on('ended', function() { 21098 if (autoplayCheckbox.is(':checked')) { 21099 21100 $(audio).on('loadeddata', playWhenLoaded); 21101 21102 nextButton.trigger('click'); 21103 //audio.play(); 21104 } 21105 }) 21106 .on('timeupdate', function() { 21107 currenttime.html( secondsToTimeCode(audio.currentTime) ); 21108 duration.html( secondsToTimeCode(audio.duration) ); 21109 21110 // slider 21111 audioSliderCurrent.css({width: (audio.currentTime / audio.duration * 100) + '%' }); 21112 if (!isDraggingSliderHandle) { 21113 audioSliderHandle.css({left: (audio.currentTime / audio.duration * 100) + '%' }); 21114 } 21115 21116 // don't auto scroll if not checked or if there was no connecting UI 21117 if (!scrollCheckbox.is(':checked') || toggleButton == null) { 21118 return; 21119 } 21120 21121 if (sectionNode.length == 0) { 21122 sectionNode = container.find('.section[data-id="' + sectionid + '"]'); 21123 } 21124 21125 sectionHeight = sectionNode.height(); 21126 21127 var 21128 // calculate percent to scroll 21129 chapter = parseInt(sectionid.substring(2), 10), 21130 skipSeconds = (chapter == 1) ? 10 : 8, 21131 fraction = (audio.currentTime-skipSeconds) / (audio.duration-skipSeconds); 21132 21133 // calculate node position 21134 pane = container.find('.scroller-main'), 21135 paneTop = pane.offset().top, 21136 scrollTop = pane.scrollTop(), 21137 nodeTop = sectionNode.offset().top, 21138 nodeTopAdjusted = nodeTop - paneTop + scrollTop, 21139 21140 offset = sectionHeight * fraction 21141 // adjust the offset by two lines or so 21142 // - 20 21143 - sectionNode.find('.v:first').height() 21144 - (sectionNode.find('.v:last').height()*fraction); 21145 21146 //// //console.log(fraction, sectionHeight, offset); 21147 21148 if (offset <= 0) { 21149 offset = 0; 21150 } 21151 21152 // go to it 21153 scroller.setFocus(true); 21154 pane.scrollTop(nodeTopAdjusted + offset); 21155 }); 21156 21157 audioSliderHandle.on('mousedown', function(e) { 21158 isDraggingSliderHandle = true; 21159 $(document).on('mousemove', documentMouseMove); 21160 $(document).on('mouseup', documentMouseUp); 21161 }); 21162 21163 21164 function documentMouseUp(e) { 21165 // //console.log('mouseup'); 21166 21167 isDraggingSliderHandle = false; 21168 $(document).off('mousemove', documentMouseMove); 21169 $(document).off('mouseup', documentMouseUp); 21170 } 21171 21172 function documentMouseMove(e) { 21173 21174 var 21175 width = audioSlider.width(), 21176 pos = audioSlider.offset(), 21177 clientX = e.clientX, 21178 offsetX = clientX - pos.left, 21179 percent = offsetX/width, 21180 newTime = percent * audio.duration; 21181 21182 audioSliderHandle.css({left: (percent*100) + '%'}); 21183 21184 audio.currentTime = newTime; 21185 } 21186 21187 21188 audioSlider.on('click', function(e) { 21189 21190 // //console.log('slider:click'); 21191 21192 var 21193 width = audioSlider.width(), 21194 offsetX = e.offsetX, 21195 percent = offsetX/width, 21196 newTime = percent * audio.duration; 21197 21198 audio.currentTime = newTime; 21199 21200 }); 21201 21202 function setTextInfo(newTextInfo) { 21203 if (textInfo == null || textInfo.id != newTextInfo.id) { 21204 21205 // reset 21206 title.html(''); 21207 subtitle.html(''); 21208 audioSliderCurrent.css({left: '0%' });
21209 audioSliderHandle.css({left: '0%' }); 21210 currenttime.html( secondsToTimeCode(0) ); 21211 duration.html( secondsToTimeCode(0) ); 21212 21213 textInfo = newTextInfo; 21214 21215 if (!audio.paused && !audio.ended) { 21216 try { 21217 audio.pause(); 21218 audio.src = null; 21219 } catch (e) { 21220 // boom! 21221 } 21222 } 21223 21224 if (textInfo.type == 'bible') { 21225 21226 audioDataManager.getAudioInfo(textInfo, function(newAudioInfo) { 21227 21228 if (newAudioInfo != null) { 21229 audioInfo = newAudioInfo; 21230 21231 console.log('AUDIO: YES', textInfo.id, textInfo.lang, audioInfo.type); 21232 21233 hasAudio = true; 21234 21235 sectionid = ''; 21236 fragmentAudioData = null; 21237 21238 if (audioInfo.type == 'local') { 21239 optionsDramaticBox.hide(); 21240 } else if (audioInfo.type == 'fcbh') { 21241 optionsDramaticBox.show(); 21242 21243 var hasNonDrama = 21244 (typeof audioInfo.fcbh_audio_nt != 'undefined' && audioInfo.fcbh_audio_nt != '') || 21245 (typeof audioInfo.fcbh_audio_ot != 'undefined' && audioInfo.fcbh_audio_ot != ''), 21246 hasDrama = 21247 (typeof audioInfo.fcbh_drama_nt != 'undefined' && audioInfo.fcbh_drama_nt != '') || 21248 (typeof audioInfo.fcbh_drama_ot != 'undefined' && audioInfo.fcbh_drama_ot != ''); 21249 21250 console.log(audioInfo, 'drama', hasDrama, 'audio', hasNonDrama); 21251 21252 // show hide 21253 if (hasNonDrama && hasDrama) { 21254 optionsDramaticAudio.prop('disabled', false); 21255 optionsDramaticDrama.prop('disabled', false); 21256 } else { 21257 optionsDramaticAudio.prop('disabled', true); 21258 optionsDramaticDrama.prop('disabled', true); 21259 } 21260 21261 if (hasNonDrama) { 21262 optionsDramaticAudio.prop('checked', true); 21263 optionsDramaticDrama.prop('checked', false); 21264 } else { 21265 optionsDramaticAudio.prop('checked', false); 21266 optionsDramaticDrama.prop('checked', true); 21267 } 21268 } 21269 21270 21271 if (fragmentid != '') { 21272 var newFragmentid = fragmentid; 21273 21274 fragmentid = ''; 21275 21276 // //console.log('AUDIO, new from old ', newFragmentid); 21277 loadAudio(newFragmentid); 21278 } else { 21279 21280 locationInfo = scroller.getLocationInfo(); 21281 21282 // //console.log('AUDIO, new from new ', locationInfo); 21283 if (locationInfo != null) { 21284 loadAudio(locationInfo.fragmentid); 21285 } 21286 } 21287 //} 21288 21289 // start load 21290 //block.show(); 21291 if (toggleButton) toggleButton.show(); 21292 21293 } else { 21294 hasAudio = false; 21295 21296 console.log('AUDIO: NO', textInfo.id, textInfo.lang, newAudioInfo); 21297 21298 21299 if (toggleButton) { 21300 toggleButton.hide(); 21301 block.hide(); 21302 } 21303 } 21304 }); 21305 } // if text is bible 21306 } // if this is a different text 21307 } 21308 21309 function secondsToTimeCode(time) { 21310 21311 var hours = Math.floor(time / 3600) % 24, 21312 minutes = Math.floor(time / 60) % 60, 21313 seconds = Math.floor(time % 60), 21314 result = 21315 (minutes < 10 ? '0' + minutes : minutes) + ':' 21316 + (seconds < 10 ? '0' + seconds : seconds); 21317 21318 return result; 21319 } 21320 21321 function size(width, height) { 21322 21323 block.outerWidth(width); 21324 } 21325 21326 //$(window).on('resize') 21327 21328 function close() { 21329 ext.clearListeners(); 21330 21331 block.remove(); 21332 options.remove(); 21333 21334 block = null; 21335 options = null; 21336 } 21337 21338 var ext = { 21339 setTextInfo: setTextInfo, 21340 size: size, 21341 close: close 21342 } 21343 ext = $.extend(true, ext, EventEmitter); 21344 21345 return ext; 21346} 21347 21348 21349 21350/********* 21351* ../app/js/windows/textcomparisonwindow.js 21352**********/ 21353 21354var TextComparisonWindow = function(id, parent, init_data) { 21355 21356 console.log('text comparisino', init_data ); 21357 21358 21359 var header = $('<div class="window-header" >' + 21360 '<input type="text" class="app-input comparison-fragmentid" value="" />' + 21361 '<input type="text" class="app-input comparison-texts" value="NASB, NET, WEB" />' + 21362 '<input type="button" value="GO" data-i18n="[value]windows.comparison.button" class="comparison-button header-button i18n" />' + 21363 '</div>').appendTo(parent.node), 21364 main = $('<div class="comparison-main">' + 21365 '</div>').appendTo(parent.node),
21366 footer = $('<div class="comparison-footer">' + 21367 '</div>').appendTo(parent.node) 21368 21369 inputFragment = header.find('.comparison-fragmentid'), 21370 inputTexts = header.find('.comparison-texts'), 21371 goButton = header.find('.comparison-button'); 21372 21373 21374 inputFragment.on('keydown', handleEnter); 21375 inputTexts.on('keydown', handleEnter); 21376 21377 goButton.on('click', function() { 21378 console.log('clicked'); 21379 doComparison(); 21380 }); 21381 21382 21383 function init() { 21384 21385 inputFragment.val( init_data.fragmentid ? init_data.fragmentid : init_data.params.fragmentid ? init_data.params.fragmentid : ''); 21386 inputTexts.val( init_data.textids ? init_data.textids : init_data.params.textids ? init_data.params.textids : '' ); 21387 21388 doComparison(); 21389 21390 } 21391 21392 function handleEnter(e) { 21393 if (e.which == 13) { 21394 doComparison(); 21395 } 21396 } 21397 21398 var sectionid = '', 21399 reference = '', 21400 textAbbrs = '', 21401 textids = [], 21402 textIndex = -1, 21403 textData = [], 21404 textInfoData = null, 21405 isComparing = false; 21406 21407 function doComparison() { 21408 21409 if (isComparing) { 21410 return; 21411 } 21412 21413 isComparing = true; 21414 21415 main.html(); 21416 main.addClass('loading-indicator'); 21417 21418 21419 reference = new bible.Reference(inputFragment.val()); 21420 21421 if (typeof reference.toSection == 'undefined') { 21422 return; 21423 } 21424 inputFragment.val( reference.toString() ); 21425 sectionid = reference.toSection().split('_')[0]; // 'JN1'; 21426 textAbbrs = inputTexts.val().split(','); 21427 textids = []; 21428 textIndex = -1; 21429 textData = []; 21430 21431 21432 TextLoader.loadTexts(function(tid) { 21433 textInfoData = tid; 21434 21435 21436 for (var i=0, il=textAbbrs.length; i<il; i++ ) { 21437 var abbr = textAbbrs[i].trim().toLowerCase(); 21438 21439 if (abbr != '') { 21440 21441 var possibleTexts = textInfoData.filter(function(t) { 21442 21443 return t.abbr.toLowerCase().indexOf(abbr) > -1; 21444 }); 21445 21446 if (possibleTexts.length > 0) { 21447 textids.push(possibleTexts[0].id); 21448 } 21449 } 21450 } 21451 21452 getNextText(); 21453 21454 }); 21455 21456 21457 21458 } 21459 21460 //var textids = ['eng-NASB1995', 'eng_net', 'eng_web']; // , 'esv']; 21461 21462 function getNextText() { 21463 21464 textIndex++; 21465 21466 if (textIndex < textids.length) { 21467 21468 var textid = textids[textIndex]; 21469 21470 TextLoader.getText(textid, function(textInfo) { 21471 21472 21473 TextLoader.loadSection( textInfo, sectionid, function(content) { 21474 //TextLoader.loadSection( textid, sectionid, function(content) { 21475 21476 textData.push({textInfo: textInfo, 21477 content: content}); 21478 21479 getNextText(); 21480 21481 }); 21482 }); 21483 21484 //} else if (textIndex == textids.length+1){ 21485 } else { 21486 21487 // complete! 21488 processData(); 21489 } 21490 21491 } 21492 21493 21494 21495 function processData() { 21496 21497 if (textData.length <= 1) { 21498 21499 main.removeClass('loading-indicator'); 21500 21501 return; 21502 21503 } 21504 21505 21506 var html = '<table class="comparison-table section">'; // using 'section' to get fonts and such 21507 21508 // heading 21509 html += '<thead>'; 21510 html += '<th></th>'; 21511 for (var i=0, il = textData.length; i<il; i++) { 21512 html += '<th>' + textData[i].textInfo.abbr + '</th>'; 21513 } 21514 html += '</thead>'; 21515 21516 html += '<tbody>'; 21517 21518 var startVerse = reference.verse1 > 0 ? reference.verse1 : 1, 21519 endVerse = reference.verse2 > 0 ? reference.verse2 : bible.BOOK_DATA[ reference.bookid ].chapters[reference.chapter]; 21520 21521 for (var verse=startVerse; verse<=endVerse; verse++) { 21522 21523 var verseid = sectionid + '_' + verse.toString(), 21524 baseText = getPlainText(textData[0].content, verseid); 21525 21526 html += '<tr>'; 21527 21528 html += '<th>' + verse + '</th>'; 21529 21530 html += '<td class="reading-text" style="width:' + (100/textData.length) + '%">' + baseText + '</td>'; 21531 21532 21533 // comparison 21534 for (var i=1, il = textData.length; i<il; i++) { 21535 var comparisonText = getPlainText(textData[i].content, verseid); 21536 21537 html += '<td class="reading-text" style="width:' + (100/textData.length) + '%">' + 21538 rejoinPunctuation( 21539 diffString( 21540 separatePunctuation(baseText) 21541 , 21542 separatePunctuation(comparisonText) 21543 ) 21544 ) + 21545 '</td>'; 21546 21547 } 21548 21549 21550 html += '<tr>'; 21551 } 21552 21553 21554 21555 html += '</tbody>'; 21556 21557 21558 html += '</table>'; 21559 21560 main.html(html); 21561 21562 main.removeClass('loading-indicator'); 21563 21564 // trigger settings change 21565 ext.trigger('settingschange', {type: 'settingschange', target: this, data: getData() }); 21566 21567 isComparing = false; 21568 } 21569 21570 function separatePunctuation(input) { 21571 21572 return input.replace(/([\.,;])/gi,' $1'); // ââââ 21573 } 21574 21575 function rejoinPunctuation(input) { 21576
21577 // fix HTML 21578 input = input.replace(/\s+<\/([^>]+)>/gi,'</$1> '); 21579 21580 // connect 21581 input = input.replace(/<\/ins>\s+<ins>/gi,' '); 21582 input = input.replace(/<\/del>\s+<del>/gi,' '); 21583 21584 // fix punctuation 21585 input = input.replace(/\s+([\.,;])/gi,'$1'); // ââ 21586 21587 return input; 21588 } 21589 21590 21591 function getPlainText(content, verseid) { 21592 21593 var verseNodes = content.find('.' + verseid), 21594 plainText = ''; 21595 21596 for (var i=0, il=verseNodes.length; i<il; i++) { 21597 21598 var verseNode = $(verseNodes[i]); 21599 21600 verseNode.find('.note, .cf, .v-num, .verse-num').remove(); 21601 21602 var text = verseNode.html(); 21603 21604 text = text.replace(/<[^>]+>/gi,''); 21605 21606 plainText += text + ' '; 21607 } 21608 21609 return plainText; 21610 21611 } 21612 21613 21614 21615 21616 21617 21618 function size(width, height) { 21619 main 21620 .width(width) 21621 .height(height - footer.outerHeight() - header.outerHeight() ); 21622 } 21623 21624 function getData() { 21625 21626 var 21627 data = { 21628 params: { 21629 win: 'comparison', 21630 textids: inputTexts.val(), 21631 fragmentid: inputFragment.val() 21632 } 21633 }; 21634 21635 return data; 21636 } 21637 21638 function close() { 21639 21640 ext.clearListeners(); 21641 } 21642 21643 var ext = { 21644 size: size, 21645 getData: getData, 21646 sendMessage: function() { 21647 // nothing for now 21648 }, 21649 close: close 21650 }; 21651 ext = $.extend(true, ext, EventEmitter); 21652 21653 21654 ext.on('message', function(e) { 21655 // nothing for now 21656 }); 21657 21658 init(); 21659 21660 return ext; 21661}; 21662 21663sofia.initMethods.push(function() { 21664 21665 sofia.windowTypes.push( { 21666 className:'TextComparisonWindow', 21667 param: 'comparison', 21668 paramKeys: { 21669 'textids': 't', 21670 'fragmentid': 'f' 21671 }, 21672 init: { 21673 textids: 'WEB, ASV', 21674 fragmentid: 'John 1:1-8' 21675 } 21676 }); 21677}); 21678 21679 21680 21681/********* 21682* ../app/js/windows/audiowindow.js 21683**********/ 21684sofia.config = $.extend(sofia.config, { 21685 enableAudioWindow: true, 21686 audioWindowDefaultBibleFragmentid: 'JN1_1', 21687 audioWindowDefaultBibleVersion: 'ENGESV' 21688}); 21689 21690 21691var AudioWindow = function(id, parent, init_data) { 21692 21693 console.log('audiowindow init', init_data); 21694 21695 var 21696 container = 21697 $('<div class="audio-window-container">'+ 21698 '<div class="window-header audio-window-header">'+ 21699 '<div class="audio-window-header-inner">'+ 21700 '<input type="text" class="app-input text-nav" />' + 21701 '<div class="app-list text-list"></div>'+ 21702 //'<span class="header-icon info-button"></span>'+ 21703 '</div>'+ 21704 '</div>'+ 21705 '<div class="audio-window-main">' + 21706 '</div>' + 21707 '<div class="audio-window-version-info">' + 21708 '</div>' + 21709 '</div>').appendTo(parent.node), 21710 21711 21712 header = container.find('.audio-window-header'), 21713 main = container.find('.audio-window-main'), 21714 navui = header.find('.text-nav'), 21715 textlistui = header.find('.text-list'), 21716 versioninfo = container.find('.audio-window-version-info'), 21717 21718 text_type = 'audio', 21719 currentTextInfo = null, 21720 currentLocationInfo = null, 21721 21722 // objects 21723 textChooser = sofia.globalTextChooser, 21724 textNavigator = sofia.globalTextNavigator, 21725 21726 scrollerMimic = {}, 21727 audioController = null; 21728 21729 // make something that functions like the text scroller so that the AudioController can understand it 21730 scrollerMimic = $.extend(true, scrollerMimic, EventEmitter); 21731 scrollerMimic.getLocationInfo = function() { 21732 return currentLocationInfo; 21733 }; 21734 21735 audioController = new AudioController(id, main, null, scrollerMimic); 21736 21737 // TEXT NAVIGATOR 21738 navui 21739 .on('click', function(e) { 21740 21741 if (Detection.hasTouch) { 21742 this.blur(); 21743 } 21744 21745 // if this is selected, then toggle 21746 if (textNavigator.getTarget() == navui) { 21747 textNavigator.toggle(); 21748 } else { 21749 textNavigator.setTarget(container, navui); 21750 textNavigator.setTextInfo(currentTextInfo); 21751 textNavigator.show(); 21752 } 21753 }) 21754 .on('keypress', function(e) { 21755 if (e.keyCode == 13) { 21756 var 21757 userinput = navui.val(), 21758 bibleref = new bible.Reference(userinput), 21759 fragmentid = (bibleref.toSection) ? bibleref.toSection() : '', 21760 sectionid = fragmentid.split('_')[0]; 21761 21762 if (sectionid != '') { 21763 changeLocation(fragmentid); 21764 } 21765 } 21766 }); 21767 21768 function changeLocation(inputLocation) { 21769 var bibleref = new bible.Reference(inputLocation), 21770 fragmentid = (bibleref.toSection) ? bibleref.toSection() : '', 21771 sectionid = fragmentid.split('_')[0], 21772 21773 newLocationInfo = { 21774 fragmentid: fragmentid, 21775 sectionid: sectionid 21776 }; 21777 21778 currentLocationInfo = newLocationInfo; 21779 21780 if (scrollerMimic.trigger) { 21781 scrollerMimic.trigger('locationchange', {type:'locationchange', target: this, data: newLocationInfo}); 21782 } 21783 if (ext && ext.trigger) { 21784 ext.trigger('settingschange', {type: 'settingschange', target: this, data: getData() }); 21785 } 21786 21787 textNavigator.hide(); 21788 21789 navui.val(bibleref.toString()); 21790 navui[0].blur(); 21791 } 21792 21793 textNavigator.on('change', function (e) { 21794 21795 if (e.data.target != navui) { 21796 return; 21797 } 21798 21799 changeLocation(e.data.sectionid); 21800 }); 21801 21802 // TEXT CHOOSER 21803 textlistui.on('click', function(e) { 21804 21805 // if this is selected, then toggle 21806 if (textChooser.getTarget() == textlistui) {
21807 textChooser.toggle(); 21808 } else { 21809 textChooser.setTarget(container, textlistui, text_type); 21810 textChooser.setTextInfo(currentTextInfo); 21811 textChooser.show(); 21812 } 21813 }); 21814 21815 textChooser.on('change', function(e) { 21816 if (e.data.target != textlistui) { 21817 return; 21818 } 21819 21820 var newTextInfo = e.data.textInfo; 21821 21822 updateText(newTextInfo); 21823 21824 }); 21825 21826 function updateText(newTextInfo) { 21827 21828 // ALWAYS UPDATE: for first load 21829 // update version name 21830 textlistui.html( newTextInfo.abbr ); 21831 21832 parent.tab.find('span').html( newTextInfo.abbr ); 21833 21834 // update the navigator with the latest header 21835 textNavigator.setTextInfo(newTextInfo); 21836 21837 audioController.setTextInfo(newTextInfo); 21838 21839 currentTextInfo = newTextInfo; 21840 21841 //versioninfo.html( currentTextInfo.aboutHtml ); 21842 } 21843 21844 21845 21846 function init() { 21847 21848 // TEMP 21849 navui.html('Reference').val('Reference'); 21850 textlistui.html('Version'); 21851 21852 if (init_data == null) { 21853 return; 21854 } 21855 21856 if (typeof init_data.fragmentid != 'undefined' && init_data.fragmentid != '') { 21857 //var bibleRef = new bible.Reference(init_data.fragmentid); 21858 //navui.val(bibleRef.toString()); 21859 changeLocation(init_data.fragmentid); 21860 } 21861 21862 if (typeof init_data.textid == 'undefined' || init_data.textid == '') { 21863 init_data.textid = sofia.config.newBibleWindowVersion; 21864 } 21865 21866 // load the text specified by the init data 21867 TextLoader.getText(init_data.textid, 21868 21869 // success 21870 function(loadedTextInfo) { 21871 21872 // store this setting 21873 currentTextInfo = loadedTextInfo; 21874 21875 updateText(loadedTextInfo); 21876 }, 21877 21878 // error handler 21879 function() { 21880// 21881// console.log('ERROR', init_data.textid, 'doesnt exist'); 21882// 21883// // load all possible versions 21884// TextLoader.loadTexts(function(textInfoData) { 21885// 21886// // find a text with the same language 21887// var newTextInfo = null, 21888// lang = init_data.textid.toString().split('-')[0].split('_')[0]; 21889// 21890// for (var i=0, il=textInfoData.length; i<il; i++) { 21891// var textInfo = textInfoData[i]; 21892// 21893// if (textInfo.type == text_type && (textInfo.lang == lang || textInfo.id.substring(0, lang.length) == lang)) { 21894// newTextInfo = textInfo; 21895// break; 21896// } 21897// } 21898// 21899// // still nothing 21900// if (newTextInfo == null) { 21901// newTextInfo = textInfoData[0]; 21902// } 21903// 21904// // let's try again with first one 21905// TextLoader.getText(newTextInfo.id, function(loadedTextInfo) { 21906// // store this setting 21907// currentTextInfo = loadedTextInfo; 21908// 21909// isInitialized = true; 21910// 21911// startup(); 21912// 21913// }); 21914// 21915// }); 21916 21917 }); 21918 } 21919 21920 21921 init(); 21922 21923 21924 21925 function size(width, height) { 21926 21927 container.outerWidth(width); 21928 21929 container 21930 .width(width) 21931 .height(height); // - header.outerHeight()); 21932 } 21933 21934 21935 function getData() { 21936 // get data 21937 if (currentTextInfo == null) { 21938 currentTextInfo = textChooser.getTextInfo(); 21939 } 21940 if (currentLocationInfo == null) { 21941 currentLocationInfo = scrollerMimic.getLocationInfo(); 21942 } 21943 21944 // if not ready, then kill it 21945 if (currentTextInfo == null || currentLocationInfo == null) { 21946 return null; 21947 } 21948 21949 var data = { 21950 // textinfo 21951 textid: currentTextInfo.providerid, 21952 abbr: currentTextInfo.abbr, 21953 21954 // location info 21955 sectionid: currentLocationInfo.sectionid, 21956 fragmentid: currentLocationInfo.fragmentid, 21957 label: currentLocationInfo.label, 21958 labelTab: currentTextInfo.abbr, 21959 labelLong: currentLocationInfo.labelLong, 21960 //hasFocus: hasFocus, 21961 params: { 21962 'win': 'audio', 21963 'textid': currentTextInfo.providerid, 21964 'fragmentid': currentLocationInfo.fragmentid 21965 } 21966 }; 21967 21968 return data; 21969 } 21970 21971// function getData() { 21972// 21973// var data = { 21974// params: { 21975// 'win': 'audio' 21976// } 21977// }; 21978// 21979// return data 21980// } 21981 21982 function close() { 21983 21984 ext.clearListeners(); 21985 21986 } 21987 21988 21989 var ext = { 21990 size: size, 21991 getData: getData, 21992 sendMessage: function() {}, 21993 close: close 21994 }; 21995 ext = $.extend(true, ext, EventEmitter); 21996 21997 21998 ext.on('message', function(e) { 21999 if (e.data.messagetype == 'textload') { 22000 } 22001 }); 22002 22003 return ext; 22004}; 22005 22006sofia.initMethods.push(function() { 22007 22008 if (sofia.config.enableAudioWindow) { 22009 22010 sofia.windowTypes.push( { 22011 className:'AudioWindow', 22012 param: 'audio', 22013 paramKeys: { 22014 'textid': 't', 22015 'fragmentid':'v' 22016 }, 22017 init: { 22018 'textid':sofia.config.audioWindowDefaultBibleVersion, 22019 'fragmentid':sofia.config.audioWindowDefaultBibleFragmentid 22020 } 22021 }); 22022 } 22023}); 22024 22025 22026 22027/********* 22028* ../app/js/windows/statisticswindow.js 22029**********/ 22030var StatisticsWindow = function(id, parent, data) { 22031 22032 var
22033 header = $('<div class="window-header"><span class="window-title i18n" data-i18n="[html]windows.stats.label"></span></div>').appendTo(parent.node), 22034 main = $('<div class="window-main">' + 22035 //'<div class="statistics-header"></div>' + 22036 '<div class="statistics-content loading-indicator"></div>' + 22037 '</div>').appendTo(parent.node), 22038 //statsHeaderNode = main.find('.statistics-header'), 22039 statsMainNode = main.find('.statistics-content'); 22040 22041 //header.find('.i18n').i18n(); 22042 22043 22044 var isReady = false, 22045 textid = '', 22046 sectionid = '', 22047 textInfo = null, 22048 isLoading = false, 22049 isLoaded = false, 22050 22051 word_stats = [], 22052 lemma_data = [], 22053 has_lemma = false; 22054 22055 22056 if (typeof WordCloud != 'undefined') { 22057 isReady = true; 22058 22059 } else { 22060 22061 $.ajax({ 22062 url: 'js/lib/wordcloud2.js', 22063 dataType: "script", 22064 success: function() { 22065 isReady = true; 22066 loadIntro(); 22067 } 22068 }); 22069 22070 } 22071 22072 22073 function startProcess(tid, sid) { 22074 22075 var tparts = tid.split(':'); 22076 22077 if (tparts.length > 1) { 22078 tid = tparts[tparts.length-1]; 22079 } 22080 22081 22082 if (tid == textid && sid == sectionid) { 22083 // do nothing, carry on! 22084 } else { 22085 22086 removeHighlights(); 22087 22088 // store the current ones 22089 sectionid = sid; 22090 textid = tid; 22091 textInfo = null; 22092 22093 word_stats = []; 22094 lemma_data = []; 22095 has_lemma = false; 22096 22097 isLoading = true; 22098 isLoaded = false; 22099 22100 main.scrollTop(0); 22101 statsMainNode.html(''); 22102 statsMainNode.addClass('loading-indicator'); 22103 22104 loadIntro(); 22105 } 22106 22107 } 22108 22109 function loadIntro() { 22110 22111 if (!isReady) { 22112 return; 22113 } 22114 22115 if (sectionid == '' || textid == '') { 22116 return; 22117 } 22118 22119 console.log('stats',textid,sectionid, 'intro start'); 22120 22121 //var intro = $('<div class="statistics-intro"><div>').appendTo(statsMainNode); 22122 22123 TextLoader.getText(textid, function(data) { 22124 22125 statsMainNode.removeClass('loading-indicator'); 22126 22127 textInfo = data; 22128 22129 var bibleReference = new bible.Reference(sectionid); 22130 bibleReference.lang = textInfo.lang; 22131 22132 if (bibleReference.toSection) { 22133 header.find('span').html( bibleReference.toString() + ' (' + textInfo.abbr + ')'); 22134 } 22135 22136 loadChapterInfo(); 22137 }); 22138 } 22139 22140 function loadChapterInfo() { 22141 22142 console.log('stats',textid,sectionid, 'loadChapterInfo start'); 22143 22144 var resultsNode = $('<div class="statistics-section statistics-frequent-words">' + 22145 '<h3>' + i18n.t('windows.stats.frequentwords') + '</h3>' + 22146 '<div class="statistics-wordcloud"></div>' + 22147 '<div class="statistics-results loading-indicator"></div>' + 22148 '<div>') 22149 .appendTo(statsMainNode), 22150 wordFrequenciesNode = resultsNode 22151 .find('.statistics-results'), 22152 wordCloudNode = resultsNode 22153 .find('.statistics-wordcloud'); 22154 22155 TextLoader.loadSection(textInfo, sectionid, function(content) { 22156 22157 // analyze verses 22158 var html = ''; 22159 22160 content.find('.verse, .v').each(function(i,el) { 22161 var verse = $(this), 22162 verseid = verse.attr('data-id') 22163 verse.find('.note').remove(); 22164 22165 //console.log('processing', verseid, words); 22166 22167 if (verse.find('l').length > 0) { 22168 22169 has_lemma = true; 22170 22171 verse.find('l').each(function() { 22172 var lemma = $(this), 22173 strongs_array = lemma.attr('s').split(' '); 22174 22175 strongs_array.forEach(function(strongs) { 22176 22177 // article 22178 if ( 22179 strongs == 'G2532' // καί 22180 || strongs == 'G3588' 22181 || strongs == 'G846' // αá½ÏÏÏ 22182 || strongs == 'G1722' // á¼Î½ (1722) 22183 || strongs == 'G1519' // Îµá¼°Ï (1519) 22184 || strongs == 'G1537' // á¼Î¾ (1537) 22185 || strongs == 'G1611'// δΠ(1161) 22186 ) { 22187 return; 22188 } 22189 22190 var 22191 word_infos = word_stats.filter(function(wi) { 22192 return wi.strongs == strongs; 22193 }), 22194 22195 word = lemma.html(); 22196 22197 if (word_infos.length > 0) { 22198 word_info = word_infos[0]; 22199 word_info.count++; 22200 if (word_info.words.indexOf(word) == -1) { 22201 word_info.words.push(word); 22202 } 22203 } else { 22204 word_stats.push({ 22205 strongs: strongs, 22206 word: lemma.html(), // + ' [' + strongs + ']', 22207 words: [word], 22208 count: 1 22209 }) 22210 } 22211 });
22212 22213 22214 }); 22215 22216 } else { 22217 var 22218 verse_html = verse.html(), 22219 verse_text = verse_html.replace(/<.*?>/gi,''); 22220 22221 if (textInfo.lang.indexOf('en') == 0) { 22222 verse_text = verse_text.replace(/[^(A-Za-z\s)]/gi,''); 22223 } 22224 22225 // english? 22226 words = verse_text.split(' '); 22227 22228 for (var i=0, il=words.length; i<il; i++) { 22229 var word = words[i];//.toLowerCase(); 22230 22231 if (word == '' || (exclusions[textInfo.lang] && exclusions[textInfo.lang].indexOf(word.toLowerCase()) > -1)) { 22232 continue; 22233 } 22234 22235 var 22236 word_infos = word_stats.filter(function(wi) { 22237 return wi.word == word || wi.word.toLowerCase() == word.toLowerCase(); 22238 }); 22239 22240 if (word_infos.length > 0) { 22241 word_infos[0].count++; 22242 } else { 22243 word_stats.push({ 22244 word: word, 22245 count: 1 22246 }) 22247 } 22248 } 22249 } 22250 }); 22251 22252 22253 // sort by most frequent 22254 22255 word_stats.sort(function(a, b) { 22256 if (a.count > b.count) { 22257 return -1; 22258 } else if (a.count < b.count) { 22259 return 1; 22260 } else { 22261 return 0; 22262 } 22263 }); 22264 var max = Math.max.apply(Math, word_stats.map(function(o) {return o.count;})), 22265 min = Math.min.apply(Math, word_stats.map(function(o) {return o.count;})), 22266 smallestSize = 9, 22267 biggestSize = 24; 22268 22269 // remove uncommon words 22270 var min_to_keep = 0, 22271 display_words = word_stats; 22272 22273 if (min_to_keep > 0) { 22274 display_words = word_stats.filter(function(w) { return w.count >= min_to_keep;} ); 22275 } 22276 22277 // create HTML 22278 var wordle_data = '', 22279 wordcloud_data = []; 22280 for (var i in display_words) { 22281 var word_info = display_words[i]; 22282 22283 //if (exclusions.en.indexOf(word_info.word.toLowerCase()) == -1) { 22284 22285 var size = smallestSize + 22286 ((biggestSize-smallestSize) * word_info.count / (max-min) ), 22287 displayWord = word_info.words ? word_info.words.join(', ') : word_info.word, 22288 wordleWord = word_info.word; 22289 22290 if (word_info.strongs) { 22291 displayWord = '<l s="' + word_info.strongs + '">' + displayWord + '</l>'; 22292 } 22293 22294 22295 html += '<span class="word" style="font-size:' + size + 'px" data-wordindex="' + i + '"><span dir="' + textInfo.dir + '">' + displayWord + '</span> <span dir="ltr">(' + word_info.count + ')</span></span>'; 22296 wordle_data += word_info.count + ' ' + displayWord + '\n'; 22297 wordcloud_data.push( [ wordleWord , word_info.count] ); 22298 //} 22299 } 22300 22301 console.log(word_stats); 22302 console.log(wordle_data); 22303 22304 // append 22305 wordFrequenciesNode 22306 .attr('dir',textInfo.dir) 22307 .html(html) 22308 .removeClass('loading-indicator'); 22309 22310 wordFrequenciesNode.find('.word') 22311 .on('mouseout', function() { removeHighlights(); }) 22312 .on('mouseover', function() { 22313 var el = this, 22314 index = parseInt($(el).attr('data-wordindex'), 10), 22315 word_info = word_stats[index]; 22316 22317 //console.log(el, index, word_info); 22318 createHighlights(word_info); 22319 22320 }) 22321 22322 var windowWidth = statsMainNode.width(); 22323 22324 wordCloudNode 22325 .width(windowWidth) 22326 .height(windowWidth*3/4); 22327 22328 WordCloud(wordCloudNode[0], { 22329 //gridSize: Math.round(16 * wordle.width() / 1024), 22330 //fontFamily: 'Times', 22331 minSize: 5, 22332 /* 22333 weightFactor: function (size) { 22334 return Math.pow(size, 2.3) * wordle.width() / 4000; 22335 }, 22336 */ 22337 22338 weightFactor: function (size) { 22339 22340 //console.log(size, b, this); 22341 22342 //return size * wordCloudNode.width() / 120; 22343 22344 var sizeMax = Math.min(wordCloudNode.width() / 7, 80), 22345 sizeMin = sizeMax * 0.1, 22346 newSize = sizeMin + (Math.abs(sizeMax-sizeMin) * (size-min) / (max-min) ); 22347 22348 // sizeMax = 10, sizeMin = 2 22349 // size = 5, min=1, max=10 22350 // 2 + (8 * 5 / 9) 22351 22352 22353 //console.log(min, max, size, sizeMin, sizeMax, newSize); 22354 return newSize; 22355 }, 22356 22357 list: wordcloud_data, 22358 //backgroundColor: '#efefef', 22359 hover: function(hover_word_info, word_position, mouse_event) { 22360 22361 removeHighlights(); 22362 22363 if (hover_word_info) { 22364 var word = hover_word_info[0]; 22365 22366 var word_info = word_stats.filter(function(a) { 22367 return a.word == word; 22368 })[0]; 22369 22370 createHighlights(word_info); 22371 } 22372 22373 }, 22374 color: function (word, size) { 22375 /* 22376 var rMax = 42, 22377 rMin = 138, 22378 rValue = Math.round( rMax - (Math.abs(rMax-rMin) * (size-min) / (max-min) ) ), 22379 gMax = 133, 22380 gMin = 182,
22381 gValue = Math.round( gMax - (Math.abs(gMax-gMin) * (size-min) / (max-min) ) ) 22382 22383 ; 22384 22385 //return '#ff0000'; 22386 22387 var bMax = 232, 22388 bMin = 230, 22389 bValue = Math.round( bMin + (Math.abs(bMax-bMin) * (size-min) / (max-min) ) ); 22390 */ 22391 22392 // search blue = 42, 133, 232 22393 // dark blue = 22, 71, 123 22394 22395 var rValue = Math.round( getRangeValue(42, 22, min, max, size) ), 22396 gValue = Math.round( getRangeValue(133, 71, min, max, size) ), 22397 bValue = Math.round( getRangeValue(232, 123, min, max, size) ); 22398 22399 console.log(word, min, max, size, rValue, gValue, bValue); 22400 22401 //console.log(word, weight, rValue, gValue, 'rgb(' + rValue + ',' + gValue + ',215)'); 22402 //return 'rgb(' + rValue + ',' + gValue + ',215)'; 22403 //return 'rgb(' + (weight * 10) + ',' + (weight * 10) + ',215)'; 22404 return 'rgb(' + rValue + ',' + gValue + ',' + bValue + ')'; 22405 }, 22406 click: function() { 22407 22408 } 22409 }); 22410 22411 if (has_lemma) { 22412 22413 loadLemmaInfo(); 22414 } 22415 22416 }); 22417 } 22418 22419 function getRangeValue(value1, value2, minValue, maxValue, value) { 22420 22421 if (value2 > value1) { 22422 return value1 + (Math.abs(value1-value2) * (value-minValue) / (maxValue-minValue) ); 22423 } else if (value1 > value2) { 22424 return value2 + (Math.abs(value1-value2) * (value-minValue) / (maxValue-minValue) ); 22425 } else { 22426 return value1; 22427 } 22428 22429 } 22430 22431 sofia.getRangeValue = getRangeValue; 22432 22433 var currentLemmaIndex = 0; 22434 function loadLemmaInfo() { 22435 22436 console.log('stats',textid,sectionid, 'lemma start'); 22437
22438 var lemmaNode = $('<div class="statistics-section statistics-rare-words">' + 22439 '<h3>Rare Words</h3>' + 22440 '<div class="statistics-results loading-indicator"></div>' + 22441 '<div>') 22442 .appendTo(statsMainNode) 22443 .find('.statistics-results'); 22444 22445 currentLemmaIndex = 0; 22446 22447 getNextLemma(function() { 22448 22449 lemma_data.sort(function(a, b) { 22450 if (a.word_info.count > b.word_info.count) { 22451 return -1; 22452 } else if (a.word_info.count < b.word_info.count) { 22453 return 1; 22454 } else { 22455 return 0; 22456 } 22457 }); 22458 22459 var rare_words = lemma_data.filter(function(lemma) { return lemma.frequency <= 5;}); 22460 22461 html = ''; 22462 for (var i=0, il=rare_words.length; i<il; i++) { 22463 var lemma = rare_words[i], 22464 lemmaLang = lemma.word_info.strongs.substr(0,1).toUpperCase() == 'G' ? 'gr' : 'he', 22465 dir = lemmaLang == 'gr' ? 'ltr' : 'rtl'; 22466 22467 html += '<tr class="rare"><td><l s="' + lemma.word_info.strongs + '" lang="' + lemmaLang + '" dir="' + dir + '">' + lemma.lemma + '</td><td>' + lemma.word_info.words.join(', ') + '</td><td>' + lemma.word_info.count + ' of ' + lemma.frequency + ' in ' + (lemmaLang == 'gr' ? 'NT' : 'OT') + '</td></tr>'; 22468 22469 } 22470 22471 lemmaNode 22472 .html( '<table>' + html + '</table>') 22473 .removeClass('loading-indicator'); 22474 22475 }); 22476 } 22477 22478 function getNextLemma(callback) { 22479 22480 if (currentLemmaIndex >= word_stats.length) { 22481 22482 callback(); 22483 22484 } else { 22485 22486 var word_info = word_stats[currentLemmaIndex]; 22487 22488 $.ajax({ 22489 dataType: 'json', 22490 url: 'content/lexicons/strongs/entries/' + word_info.strongs + '.json', 22491 success: function(data) { 22492 data.word_info = word_info; 22493 lemma_data.push(data); 22494 }, 22495 complete: function() { 22496 getNextLemma(callback); 22497 currentLemmaIndex++; 22498 } 22499 }); 22500 } 22501 } 22502 22503 22504 function removeHighlights() { 22505 $('.BibleWindow .highlight-stats').each(function(i, el) { 22506 22507 if (el.tagName.toLowerCase() == 'l') { 22508 // for Lemma tags, jsut remove hte hlight 22509 el.className = el.className.replace(/highlight/gi, ''); 22510 } else { 22511 // if it's just <span class="highlight">, replace it with text 22512 var textFragment = document.createTextNode(el.textContent); 22513 if (el && el.parentNode) { 22514 el.parentNode.insertBefore(textFragment, el); 22515 el.parentNode.removeChild(el); 22516 } 22517 } 22518 22519 }); 22520 } 22521 22522 function createHighlights(word_info) { 22523 22524 removeHighlights(); 22525 22526 22527 // do for all sections / chapters 22528 $('.' + sectionid).each(function(i,el) { 22529 22530 if (typeof word_info.strongs != 'undefined') { 22531 //var r = new RegExp('s=("|\')(\\w\\d{1,4}[a-z]?\\s)?' + '(G|H)?' + word_info.strongs.substr(1) + '[a-z]?(\\s\\w\\d{1,4}[a-z]?)?("|\')', 'gi'); 22532 22533 $(el).find('l[s*="' + word_info.strongs.substr(1) + '"],l[s*="' + word_info.strongs + '"]').addClass('highlight highlight-stats lemma-highlight'); 22534 22535 // add the 'highlight' class to the <l> node 22536 //el.innerHTML = el.innerHTML.replace(r, function(match) { 22537 // return match + ' class="highlight highlight-stats" '; 22538 //}); 22539 22540 } else { 22541 22542 //var r = new RegExp('\b' + word_info.word + '\b', 'gi'); 22543 var r = new XRegExp( '\\b' + word_info.word + '\\b', 'gi'); 22544 22545 // surround the word with a highlight 22546 el.innerHTML = el.innerHTML.replace(r, function(match) { 22547 return '<span class="highlight highlight-stats">' + match + '</span>'; 22548 }); 22549 22550 } 22551 22552 }); 22553 22554 22555 } 22556 22557 22558 function close() { 22559 22560 ext.clearListeners(); 22561 } 22562 22563 function size(width, height) { 22564 // do notheirng? 22565 main.outerHeight(height - header.outerHeight()) 22566 .outerWidth(width); 22567 } 22568 22569 var ext = { 22570 size: size, 22571 getData: function() { 22572 return { 22573 22574 params: { 22575 'win': 'stats' 22576 } 22577 22578 } 22579 }, 22580 close: close 22581 }; 22582 ext = $.extend(true, ext, EventEmitter); 22583 22584 ext.on('message', function(e) { 22585 if (e.data.messagetype == 'textload') { 22586 //processContent(e.data.content); 22587 } 22588 22589 if (e.data.messagetype == 'nav' && e.data.type == 'bible' && e.data.locationInfo) { 22590 22591 startProcess(e.data.locationInfo.textid, e.data.locationInfo.sectionid); 22592 } 22593 }); 22594 22595 setTimeout(function() { 22596 var firstWindowSettings = sofia.app.windowManager.getSettings()[0], 22597 firstWin = $('.window:first'), 22598 22599 selectedSectionid = firstWindowSettings.data.sectionid, 22600 selectedTextid = firstWindowSettings.data.textid; 22601 22602 startProcess(selectedTextid, selectedSectionid); 22603 22604 }, 1500); 22605 22606 return ext; 22607 22608}; 22609 22610 22611var exclusions = { 22612 "es": ["de"], 22613 22614 "chs": ["â","ï¼","ï¼","ã","ï¼","ï¼","ï¼","ï¼","ä¸","ï¼"], 22615 22616 "eng": [ 22617 // prepositions 22618"a", 22619"abaft", 22620"aboard", 22621"about", 22622"above", 22623"absent", 22624"across", 22625"afore", 22626"after", 22627"against", 22628"along", 22629"alongside", 22630"amid", 22631"amidst", 22632"among", 22633"amongst", 22634"an", 22635"anenst", 22636"apud", 22637"around", 22638"as", 22639"aside", 22640"astride", 22641"at", 22642"athwart", 22643"atop", 22644"barring", 22645"before", 22646"behind", 22647"below", 22648"beneath", 22649"beside", 22650"besides", 22651"between", 22652"beyond", 22653"but", 22654"by", 22655"circa", 22656"concerning", 22657"despite", 22658"down", 22659"during", 22660"except", 22661"excluding", 22662"failing", 22663"following", 22664"for", 22665"forenenst", 22666"from", 22667"given", 22668"in", 22669"including", 22670"inside", 22671"into", 22672"lest", 22673"like", 22674"minus", 22675"modulo", 22676"near", 22677"next", 22678"notwithstanding", 22679"of", 22680"off", 22681"on", 22682"onto", 22683"opposite", 22684"out", 22685"outside", 22686"over", 22687"pace", 22688"past", 22689"per", 22690"plus", 22691"pro", 22692"qua", 22693"regarding", 22694"round", 22695"sans", 22696"save", 22697"since", 22698"than", 22699"through", 22700"throughout", 22701"till", 22702"to", 22703"toward", 22704"towards", 22705"under", 22706"underneath", 22707"unlike", 22708"until", 22709"unto", 22710"up", 22711"upon", 22712"versus", 22713"via", 22714"with", 22715"within", 22716"without", 22717"worth", 22718 22719// articles 22720"a", 22721"the", 22722 22723// pronouns 22724"him", 22725"his", 22726"he", 22727"she", 22728"it", 22729"her", 22730"hers", 22731 22732// conjunctions 22733"and", 22734"but", 22735"yet", 22736 22737// other 22738"that", 22739 22740"was", 22741"were",
22742"be", 22743"being", 22744"been", 22745"had", 22746"its", 22747"i", 22748 22749 ] 22750}; 22751 22752sofia.initMethods.push(function() { 22753 22754 22755 sofia.windowTypes.push( { 22756 className:'StatisticsWindow', 22757 param: 'stats', 22758 paramKeys: {}, 22759 init: { 22760 } 22761 }); 22762 22763}); 22764 22765 22766 22767/********* 22768* ../app/js/windows/deafbiblewindow.js 22769**********/ 22770sofia.config = $.extend(sofia.config, { 22771 enableDeafBibleWindow: true, 22772 deafBibleWindowDefaultBibleFragmentid: 'JN1_1', 22773 deafBibleWindowDefaultBibleVersion: 'deaf_ASESLV' 22774}); 22775 22776var DeafBibleWindow = function(id, node, init_data) { 22777 22778 var window = new TextWindow(id, node, init_data, 'deafbible'); 22779 22780 node.node.on('click', '.deaf-video-header input', function() { 22781 var button = $(this), 22782 url = button.attr('data-src'), 22783 video = button.closest('.deaf-video').find('video'); 22784 22785 button 22786 .addClass('active') 22787 .siblings() 22788 .removeClass('active'); 22789 22790 console.log(url, video); 22791 22792 video.attr('src', url); 22793 }); 22794 22795 return window; 22796}; 22797 22798sofia.initMethods.push(function() { 22799 22800 if (sofia.config.enableDeafBibleWindow) { 22801 sofia.windowTypes.push({ 22802 className:'DeafBibleWindow', 22803 param: 'deafbible', 22804 paramKeys: { 22805 'textid': 't', 22806 'fragmentid':'v' 22807 }, 22808 init: { 22809 'textid': sofia.config.deafBibleWindowDefaultBibleVersion, 22810 'fragmentid': sofia.config.deafBibleWindowDefaultBibleFragmentid 22811 } 22812 }); 22813 } 22814}); 22815 22816 22817 22818/********* 22819* ../app/js/menu/mainmenu.js 22820**********/ 22821var MainMenu = function(node) { 22822 22823 // simply create all 'menuComponents' define below 22824 for (var i=0, il=sofia.menuComponents.length; i<il; i++) { 22825 var component = new window[sofia.menuComponents[i]](node); 22826 } 22827 22828 var ext = { 22829 22830 }; 22831 22832 ext = $.extend(true, ext, EventEmitter); 22833 return ext; 22834 22835}; 22836 22837 22838var MainMenuButton = function(node) { 22839 var 22840 body = $(document.body), 22841 win = $(window), 22842 22843 container = $('.windows-container'), 22844 mainMenuLogo = $('<div id="app-logo"></div>') 22845 .appendTo(node), 22846 support = $('<a href="https://patreon.com/johndyer?utm_medium=website&utm_source=biblewebapp.com&utm_campaign=mainlink" target="_blank" id="support">Support</a>') 22847 .appendTo(node), 22848 mainMenuButton = $('<div id="main-menu-button"></div>') 22849 .appendTo(node) 22850 .on('click', mainMenuClick), 22851 mainMenuDropDown = $('<div id="main-menu-dropdown">' + 22852 '<div class="main-menu-heading i18n" data-i18n="[html]menu.labels.addwindow">Add Window</div>' + 22853 '<div id="main-menu-windows-list" class="main-menu-list"></div>' + 22854 '<div class="main-menu-heading i18n" data-i18n="[html]menu.labels.options"></div>' + 22855 '<div id="main-menu-features" class="main-menu-list"></div>' + 22856 '</div>') 22857 .appendTo( body ) 22858 .hide(); 22859 22860 22861 function mainMenuClick(e) { 22862 22863 if (mainMenuDropDown.is(':visible')) { 22864 hide(); 22865 } else { 22866 show(); 22867 } 22868 } 22869 22870 function show() { 22871 mainMenuButton.addClass('active'); 22872 mainMenuDropDown.show(); 22873 ext.onshow(); 22874 } 22875 22876 function hide() { 22877 mainMenuButton.removeClass('active'); 22878 mainMenuDropDown.hide(); 22879 ext.onhide(); 22880 } 22881 22882 mainMenuDropDown.on('click', '.main-menu-item', function() { 22883 hide(); 22884 }); 22885 22886 22887 var ext = {}; 22888 ext = $.extend(true, ext, EventEmitter); 22889 ext = $.extend(true, ext, ClickOff); 22890 ext.clickoffid = 'version picker'; 22891 ext.on('offclick', function() { 22892 hide(); 22893 }); 22894 ext.setClickTargets([mainMenuButton, mainMenuDropDown]); 22895 22896 return ext; 22897 22898} 22899 22900 22901sofia.menuComponents.push('MainMenuButton'); 22902 22903 22904 22905/********* 22906* ../app/js/menu/topsearch.js 22907**********/ 22908 22909 22910 22911var MainSearchBox = function(node) { 22912 22913 var searchBox = $('<div id="main-search-box">' + 22914 //'<input type="search" placeholder="Search" id="main-search-input" />' + 22915 22916 //'<input type="search" placeholder="' + i18n.t('menu.search.placeholder') + '" id="main-search-input" />' + 22917 '<input type="search" class="i18n" data-i18n="[placeholder]menu.search.placeholder" id="main-search-input" />' + 22918 22919 '<input type="button" id="main-search-button" value="" />' + 22920 '</div>') 22921 .appendTo(node), 22922 22923 suggestions = $('<div id="main-search-suggestions"></div>') 22924 .appendTo(document.body) 22925 .hide(), 22926 22927 searchInput = searchBox.find('#main-search-input') 22928 .on('keydown', checkNavigation) 22929 .on('keyup', checkInput) 22930 //.on('focus', showSuggestions) 22931 .on('blur', hideSuggestions), 22932 22933 searchButton = searchBox.find('#main-search-button') 22934 .on('click', doSelectedAction), 22935 inputText = '', 22936 firstSearchWindow = null, 22937 firstBibleWindow = null; 22938 22939 22940 suggestions.on('click', '.suggestion-item', function() { 22941 var item = $(this); 22942 22943 // select the clicked one 22944 item 22945 .addClass('selected') 22946 .siblings() 22947 .removeClass('selected'); 22948 22949 // do it! 22950 doSelectedAction(); 22951 }); 22952 22953 22954 function getWindowInfo() { 22955 var
22956 appSettings = sofia.app.windowManager.getSettings(); 22957 22958 for (var i=0,il=appSettings.length; i<il; i++) { 22959 var settings = appSettings[i]; 22960 // first text 22961 if (settings.windowType == 'BibleWindow' && firstBibleWindow == null) { 22962 firstBibleWindow = settings; 22963 } 22964 22965 // first search 22966 if (settings.windowType == 'SearchWindow' && firstSearchWindow == null) { 22967 firstSearchWindow = settings; 22968 } 22969 } 22970 } 22971 22972 22973 function doSelectedAction() { 22974 22975 var actionName = suggestions.find('.selected').attr('data-name'), 22976 suggestedAction = suggestionActions.filter(function(item) { 22977 return item.name == actionName; 22978 })[0]; 22979 22980 suggestedAction.doAction( searchInput.val(), firstBibleWindow, firstSearchWindow ); 22981 22982 searchInput.val(''); 22983 suggestions.val(''); 22984 22985 hideSuggestions(); 22986 } 22987 22988 22989 function checkNavigation(e) { 22990 showSuggestions(); 22991 22992 var selectedSuggestion = suggestions.find('.selected'); 22993 22994 22995 if (e.which == 40) { // arrow down 22996 // go down 22997 22998 var nextSuggestion = selectedSuggestion.next(); 22999 23000 if (nextSuggestion.length == 0) { 23001 nextSuggestion = suggestions.children().first(); 23002 } 23003 23004 nextSuggestion 23005 .addClass('selected') 23006 .siblings() 23007 .removeClass('selected'); 23008 23009 } else if (e.which == 38) { // arrow up 23010 // go up 23011 23012 var prevSuggestion = selectedSuggestion.prev(); 23013 23014 if (prevSuggestion.length == 0) { 23015 prevSuggestion = suggestions.children().last(); 23016 } 23017 23018 prevSuggestion 23019 .addClass('selected') 23020 .siblings() 23021 .removeClass('selected'); 23022 23023 } else { 23024 //checkInput(e); 23025 } 23026 } 23027 23028 function checkInput(e) { 23029 23030 if (e.which == 13) { // ENTER 23031 23032 doSelectedAction(); 23033 23034 return; 23035 } else if (e.which == 38 || e.which == 40) { // UP or DOWN 23036 23037 return false; 23038 } 23039 23040 // try all actions 23041 var inputText = searchInput.val(), 23042 isFirst = true; 23043 23044 suggestions.html(''); 23045 23046 if (inputText == '') { 23047 hideSuggestions(); 23048 return; 23049 } else { 23050 showSuggestions(); 23051 } 23052 23053 for (var i=0,il=suggestionActions.length; i<il; i++) { 23054 var suggestionAction = suggestionActions[i], 23055 suggestionText = suggestionAction.checkInput(inputText, firstBibleWindow, firstSearchWindow); 23056 23057 23058 if (suggestionText != '') { 23059 var text = $('<span class="suggestion-item' + (isFirst ? ' selected' : '') + '" data-name="' + suggestionAction.name + '">' + suggestionText + '</span>') 23060 .appendTo(suggestions); 23061 23062 isFirst = false; 23063 } 23064 } 23065 } 23066 23067 function showSuggestions() { 23068 getWindowInfo(); 23069 23070 suggestions.show(); 23071 positionSuggetions(); 23072 } 23073 23074 function hideSuggestions() { 23075 suggestions.hide(); 23076 } 23077 23078 function positionSuggetions() { 23079 var inputPos = searchBox.offset(); 23080 23081 suggestions.css({ 23082 top: inputPos.top + searchBox.height(), 23083 left: inputPos.left 23084 }) 23085 23086 23087 } 23088 23089 23090 var suggestionActions = [ 23091 23092 23093 23094 { 23095 name: 'search', 23096 checkInput: function(inputText, firstBibleWindow, firstSearchWindow) { 23097 return i18n.t('menu.search.suggestsearch').replace('{0}', inputText).replace('{1}', (firstBibleWindow != null ? firstBibleWindow.data.abbr : '')); 23098 }, 23099 doAction: function(inputText, firstBibleWindow, firstSearchWindow) { 23100 23101 PlaceKeeper.storePlace(); 23102 23103 var textid = firstBibleWindow != null ? firstBibleWindow.data.textid : sofia.config.newBibleWindowVersion; 23104 sofia.app.windowManager.add('SearchWindow', {searchtext: inputText, textid: textid}); 23105 23106 PlaceKeeper.restorePlace(); 23107 23108 searchBox.val(''); 23109 } 23110 }, 23111 23112 { 23113 name: 'version', 23114 checkInput: function(inputText, firstBibleWindow, firstSearchWindow) { 23115 23116 var matchingVersions = TextLoader.getTextInfoData().filter(function(textInfo) { 23117 return textInfo.abbr.toLowerCase() == inputText.toLowerCase(); 23118 }); 23119 23120 if (matchingVersions.length > 0) { 23121 //return 'Open <strong>' + matchingVersions[0].abbr + '</strong> in new window'; 23122 return i18n.t('menu.search.suggestversion').replace('{0}', matchingVersions[0].abbr ); 23123 } 23124 23125 return ''; 23126 //return 'Open ' + inputText + ' in XXXXX'; 23127 }, 23128 doAction: function(inputText, firstBibleWindow, firstSearchWindow) { 23129 // 23130 23131 var matchingTexts = TextLoader.getTextInfoData().filter(function(textInfo) { 23132 return textInfo.abbr.toLowerCase() == inputText.toLowerCase(); 23133 }), 23134 selectedTextInfo = matchingTexts[0]; 23135 23136 sofia.app.windowManager.add('BibleWindow', {textid: selectedTextInfo.id, abbr: selectedTextInfo.abbr, fragmentid: firstBibleWindow ? firstBibleWindow.fragmentid : ''}); 23137 23138 } 23139 }, 23140 23141 { 23142 name: 'reference', 23143 checkInput: function(inputText, firstBibleWindow, firstSearchWindow) { 23144 23145 var reference = new bible.Reference(inputText); 23146 23147 if (typeof reference.isValid != 'undefined') { 23148 //return 'Go to <strong>' + reference.toString() + '</strong>' + (firstBibleWindow != null ? ' in ' + firstBibleWindow.data.abbr : ''); 23149 return i18n.t('menu.search.suggestreference').replace('{0}', reference.toString()).replace('{1}', (firstBibleWindow != null ? firstBibleWindow.data.abbr : '')); 23150 } 23151 23152 return ''; 23153 }, 23154 doAction: function(inputText, firstBibleWindow, firstSearchWindow) { 23155 23156 var reference = new bible.Reference(inputText), 23157 fragmentid = reference.toSection(); 23158 23159 if (firstBibleWindow == null) { 23160 sofia.app.windowManager.add('BibleWindow', {textid: sofia.config.newBibleWindowVersion, fragmentid: fragmentid}); 23161 23162 } else { 23163 23164 // HACK!! 23165 sofia.app.handleGlobalMessage( 23166 { 23167 data: { 23168 messagetype:'nav', 23169 type: 'bible', 23170 locationInfo: { 23171 fragmentid: fragmentid, 23172 sectionid: fragmentid.split('_')[0], 23173 offset: 0 23174 } 23175 } 23176 } 23177 ); 23178 23179 } 23180 23181 } 23182 } 23183 ]; 23184 23185 return searchBox; 23186 23187}; 23188sofia.menuComponents.push('MainSearchBox'); 23189 23190 23191 23192/********* 23193* ../app/js/menu/fullscreen.js 23194**********/ 23195var FullScreenButton = function(node) { 23196 23197 23198 if (!fullScreenApi.supportsFullScreen) { 23199 return null; 23200 } 23201 23202 $('html').addClass('supports-fullscreen'); 23203 23204 var 23205 el = document.body, 23206 //fullscreenButton = $('<div id="main-fullscreen-button" class="main-menu-item">Fullscreen</div>') 23207 // .appendTo( $('#main-menu-features') ) 23208 fullscreenButton = $('<div id="main-fullscreen-button"></div>') 23209 .appendTo( $('.windows-header') ) 23210 .on('click', toggleFullscreen), 23211 23212 isFullscreen = false; 23213 23214 function toggleFullscreen() { 23215 if (fullScreenApi.isFullScreen()) { 23216 exitFullscreen(); 23217 } else { 23218 enterFullscreen(); 23219 } 23220 } 23221 23222 function enterFullscreen() { 23223 fullScreenApi.requestFullScreen(el); 23224 } 23225 23226 function exitFullscreen() { 23227 fullScreenApi.cancelFullScreen(); 23228 23229 } 23230 23231 //console.log(fullScreenApi); 23232 23233 23234 return fullscreenButton; 23235};
23236sofia.menuComponents.push('FullScreenButton'); 23237 23238(function() { 23239 var 23240 fullScreenApi = { 23241 supportsFullScreen: false, 23242 isFullScreen: function() { return false; }, 23243 requestFullScreen: function() {}, 23244 cancelFullScreen: function() {}, 23245 fullScreenEventName: '', 23246 prefix: '' 23247 }, 23248 browserPrefixes = 'webkit moz o ms khtml'.split(' '); 23249 23250 // check for native support 23251 if (typeof document.cancelFullScreen != 'undefined') { 23252 fullScreenApi.supportsFullScreen = true; 23253 } else { 23254 // check for fullscreen support by vendor prefix 23255 for (var i = 0, il = browserPrefixes.length; i < il; i++ ) { 23256 fullScreenApi.prefix = browserPrefixes[i]; 23257 23258 if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] != 'undefined' ) { 23259 fullScreenApi.supportsFullScreen = true; 23260 23261 break; 23262 } 23263 } 23264 } 23265 23266 // update methods to do something useful 23267 if (fullScreenApi.supportsFullScreen) { 23268 fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange'; 23269 23270 fullScreenApi.isFullScreen = function() { 23271 switch (this.prefix) { 23272 case '': 23273 return document.fullScreen; 23274 case 'webkit': 23275 return document.webkitIsFullScreen; 23276 default: 23277 return document[this.prefix + 'FullScreen']; 23278 } 23279 } 23280 fullScreenApi.requestFullScreen = function(el) { 23281 if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { 23282 el.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); 23283 } 23284 else { 23285 return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen'](); 23286 } 23287 } 23288 fullScreenApi.cancelFullScreen = function(el) { 23289 return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen'](); 23290 } 23291 } 23292 23293 // jQuery plugin 23294 if (typeof jQuery != 'undefined') { 23295 jQuery.fn.requestFullScreen = function() { 23296 23297 return this.each(function() { 23298 if (fullScreenApi.supportsFullScreen) { 23299 fullScreenApi.requestFullScreen(this); 23300 } 23301 }); 23302 }; 23303 } 23304 23305 // export api 23306 window.fullScreenApi = fullScreenApi; 23307})(); 23308 23309 23310 23311/********* 23312* ../app/js/menu/addwindow.js 23313**********/ 23314sofia.config = $.extend(sofia.config, { 23315 windowTypesOrder: [] 23316}); 23317 23318 23319 23320var AddWindowButton = function(node) { 23321 23322 var buttonMenu = $('#main-menu-windows-list'); 23323 23324 23325 // create window buttons from window Types 23326 var windowTools = []; 23327 23328 if (sofia.config.windowTypesOrder.length > 0 ) { 23329 23330 23331 for (var i=0, il=sofia.config.windowTypesOrder.length; i<il; i++) { 23332 var windowTypeName = sofia.config.windowTypesOrder[i], 23333 winType = sofia.windowTypes.filter(function(wt) { return wt.className == windowTypeName}); 23334 23335 if (winType.length > 0) { 23336 winType = winType[0]; 23337 23338 windowTools.push({ 23339 type: winType.className, 23340 label: winType.param, 23341 data: winType.init 23342 }); 23343 } 23344 } 23345 23346 23347 } else { 23348 23349 // build default array 23350 23351 for (var i=0, il=sofia.windowTypes.length; i<il; i++) { 23352 var winType = sofia.windowTypes[i]; 23353 23354 windowTools.push({ 23355 type: winType.className, 23356 label: winType.param, 23357 data: winType.init 23358 }); 23359 } 23360 } 23361 23362 // add buttons 23363 for (var x in windowTools) { 23364 var tool = windowTools[x]; 23365 // ADD Button 23366 var addButton = $('<div class="main-menu-item window-add i18n" id="add-' + tool.type + '" data-i18n="[html]windows.' + tool.label + '.label"></div>') 23367 .appendTo(buttonMenu) 23368 .data('init', tool); 23369 } 23370 23371 buttonMenu.on('click', '.window-add', function(e) { 23372 //buttonMenu.hide(); 23373 23374 var label = $(this), 23375 settings = label.data('init'); 23376 23377 // when starting a bible or commentary window, try to match it up with the others 23378 if (settings.type == 'BibleWindow' || settings.type == 'CommentaryWindow') { 23379 23380 // get location from first window 23381 var 23382 firstBCWindow = (sofia.app.windowManager) ? 23383 sofia.app.windowManager.getWindows().filter(function(w) { return w.className == 'BibleWindow' || w.className == 'CommentaryWindow'})[0] : 23384 null, 23385 currentData = (firstBCWindow != null) ? firstBCWindow.getData() : null; 23386 23387 23388 // if no location, then use the defaults from config 23389 if (currentData != null) { 23390 console.log('first window', currentData); 23391 settings.data.fragmentid = currentData.fragmentid; 23392 settings.data.sectionid = currentData.sectionid; 23393 } else { 23394 var fragmentid = (typeof sofia.config.newWindowFragmentid != 'undefined') ? sofia.config.newWindowFragmentid : 'JN1_1', 23395 sectionid = fragmentid.split('_')[0]; 23396 23397 settings.data.fragmentid = fragmentid; 23398 settings.data.sectionid = sectionid; 23399 } 23400 23401 console.log('new window', settings); 23402 } 23403 23404 PlaceKeeper.storePlace(); 23405 sofia.app.windowManager.add(settings.type, settings.data); 23406 PlaceKeeper.restorePlace(); 23407 23408 if (sofia.analytics) { 23409 sofia.analytics.record('createwindow', settings.type); 23410 } 23411 23412 //windowManager.trigger('settingschange',{}); 23413 }); 23414 23415 return addButton; 23416 23417}; 23418sofia.menuComponents.push('AddWindowButton'); 23419 23420 23421 23422/********* 23423* ../app/js/menu/config.js 23424**********/ 23425 23426var ConfigButton = function(node) { 23427 var 23428 23429 container = $('.windows-container'), 23430 body = $(document.body), 23431 win = $(window), 23432 23433 configButton = $('<div class="main-menu-item image-config i18n" data-i18n="[html]menu.labels.settings"></div>') 23434 .appendTo( $('#main-menu-features') ) 23435 .on('click', buttonClick), 23436 23437 modalOverlay = $('<div class="modal-overlay"></div>') 23438 .appendTo( body ) 23439 .hide(), 23440 23441 configWindow = new MovableWindow(300, 380, i18n.t('menu.labels.settings'), 'config-window'); 23442 23443 configWindow.body.html( 23444 '<div id="main-config-box">' + 23445 '<div class="config-section" id="config-type">' + 23446 '<span class="config-header i18n" data-i18n="[html]menu.config.font"></span>' + 23447 '<div class="config-body"></div>' + 23448 '<div class="clear"></div>' + 23449 '</div>' + 23450 /* 23451 '<div class="config-section" id="config-toggles">' + 23452 '<span class="config-header i18n" data-i18n="[html]menu.config.settings"></span>' + 23453 '<div class="config-body"></div>' + 23454 '<div class="clear"></div>' + 23455 '</div>' + 23456 */ 23457 '<div class="config-section" id="config-tools">' + 23458 '<span class="config-header i18n" data-i18n="[html]menu.config.tools"></span>' + 23459 '<div class="config-body"></div>' + 23460 '<div class="clear"></div>' + 23461 '</div>' + 23462 '</div>' 23463 ); 23464 23465 function showConfig() { 23466 var winWidth = win.width(), 23467 winHeight = win.height(); 23468 23469 modalOverlay 23470 .width( winWidth ) 23471 .height( winHeight ) 23472 .show(); 23473 23474 configWindow 23475 .show() 23476 .center(); 23477 23478 container.addClass('blur'); 23479 23480 $('#main-menu-dropdown').hide(); 23481 $('#main-menu-button').removeClass('active'); 23482 } 23483 23484 function hideConfig() { 23485 configWindow.hide(); 23486 modalOverlay.hide(); 23487 container.removeClass('blur'); 23488 } 23489 23490 modalOverlay.on('click', function() { 23491 hideConfig(); 23492 }); 23493 23494 configWindow.closeButton.on('click', function() { 23495 hideConfig(); 23496 }); 23497 23498 function buttonClick(e) { 23499 23500 e.preventDefault(); 23501 23502 if (configWindow.container.is(':visible')) { 23503 23504 hideConfig(); 23505 23506 } else { 23507 23508 showConfig(); 23509 23510 } 23511 23512 return false; 23513 } 23514}; 23515sofia.menuComponents.push('ConfigButton'); 23516 23517 23518 23519/********* 23520* ../app/js/menu/about.js 23521**********/ 23522 23523var AboutScreen = function(node) { 23524 var 23525 container = $('.windows-container'), 23526 body = $(document.body), 23527 win = $(window), 23528 23529 aboutButton = $('<div class="main-menu-item about-logo i18n" data-i18n="[html]menu.labels.about">About</div>') 23530 .appendTo( $('#main-menu-features') ) 23531 .on('click', aboutClick), 23532 23533 modalOverlay = $('<div class="modal-overlay"></div>') 23534 .appendTo( body ) 23535 .hide(), 23536 isAboutLoaded = false, 23537 aboutWindow = new MovableWindow(500, 250, i18n.t('menu.labels.about')); 23538 23539 aboutWindow.body 23540 .css({padding: '20px'}); 23541 23542 aboutWindow.title.addClass('i18n').attr('data-i18n','[html]menu.labels.about'); 23543 23544 23545 function hideAbout() { 23546 aboutWindow.hide(); 23547 modalOverlay.hide(); 23548 container.removeClass('blur'); 23549 } 23550 23551 modalOverlay.on('click', function() { 23552 hideAbout(); 23553 }); 23554 23555 aboutWindow.closeButton.on('click', function() { 23556 hideAbout(); 23557 }); 23558 23559 function aboutClick() { 23560 if (aboutWindow.container.is(':visible')) { 23561 hideAbout(); 23562 } else { 23563 23564 $('#main-menu-dropdown').hide(); 23565 23566 var winWidth = win.width(), 23567 winHeight = win.height(); 23568 23569 23570 container.addClass('blur'); 23571 23572 aboutWindow 23573 .size(.8*winWidth, 0.7*winHeight) 23574 .show() 23575 .center(); 23576 23577 modalOverlay 23578 .width( winWidth ) 23579 .height( winHeight ) 23580 .show(); 23581 23582 23583 if (!isAboutLoaded) { 23584 23585 aboutWindow.body.addClass('loading-indicator'); 23586 23587 // assume a local file first 23588 $.ajax({
23589 url: sofia.config.aboutPagePath, 23590 dataType: 'html', 23591 success: function(data) { 23592 aboutWindow.body.removeClass('loading-indicator'); 23593 23594 isAboutLoaded = true; 23595 23596 showAbout(data, sofia.config.aboutPagePath); 23597 }, 23598 error: function() { 23599 23600 if (sofia.config.baseContentUrl != '') { 23601 23602 console.log("No local about.html"); 23603 23604 // this one will go through the CDN 23605 sofia.ajax({ 23606 url: sofia.config.aboutPagePath, 23607 dataType: 'text', 23608 success: function(data) { 23609 23610 console.log('Success: CDN about.html', data.indexOf('<html')); 23611 23612 aboutWindow.body.removeClass('loading-indicator'); 23613 23614 isAboutLoaded = true; 23615 23616 showAbout(data, sofia.config.baseContentUrl + sofia.config.aboutPagePath); 23617 }, 23618 error: function() { 23619 23620 // error 23621 console.log("Can't find a about.html"); 23622 23623 } 23624 }); 23625 } else { 23626 23627 console.log("No local about.html, no CDN to check"); 23628 23629 } 23630 } 23631 }); 23632 } 23633 } 23634 } 23635 23636 function showAbout(data, url) { 23637 23638 if (data.indexOf('<html') > -1) { 23639 23640 aboutWindow.body.html('<iframe style="border: 0;" src="' + url + '"></iframe>'); 23641 aboutWindow.body.css({padding: 2}); 23642 23643 var iframe = aboutWindow.body.find('iframe'); 23644 23645 iframe.css({ 23646 width: aboutWindow.body.width(), 23647 height: aboutWindow.body.height() - 5 23648 }); 23649 23650 } else { 23651 23652 aboutWindow.body.html(data); 23653 } 23654 } 23655 23656 return aboutButton; 23657}; 23658sofia.menuComponents.push('AboutScreen'); 23659 23660 23661 23662/********* 23663* ../app/js/menu/feedback.js 23664**********/ 23665sofia.config = $.extend(sofia.config, { 23666 23667 enableFeedback: false, 23668 23669 feedbackUrl: '' 23670 23671}); 23672 23673 23674 23675var Feedback = function(node) { 23676 23677 if (!sofia.config.enableFeedback|| sofia.config.feedbackUrl == '') { 23678 return; 23679 } 23680 23681 23682 var 23683 container = $('.windows-container'), 23684 body = $(document.body), 23685 win = $(window), 23686 23687 feedbackButton = $('<div class="main-menu-item feedback-logo i18n" data-i18n="[html]menu.labels.feedback">Feedback</div>') 23688 .appendTo( $('#main-menu-features') ) 23689 .on('click', clickFeedback), 23690 23691 modalOverlay = $('<div class="modal-overlay"></div>') 23692 .appendTo( body ) 23693 .hide(), 23694 23695 feedbackWindow = new MovableWindow(Math.min( win.width(), 500), 300, i18n.t('menu.labels.feedback') ), 23696 23697 name = $('<input type="text" id="feedback-from" class="app-input i18n" data-i18n="[placeholder]menu.feedback.name" />') 23698 .appendTo(feedbackWindow.body), 23699 23700 email = $('<input type="email" id="feedback-email" class="app-input i18n" data-i18n="[placeholder]menu.feedback.email" />') 23701 .appendTo(feedbackWindow.body), 23702 23703 subject = $('<select id="feedback-subject" class="app-list" >' + 23704 '<option class="i18n" data-i18n="[html]menu.feedback.feature"></option>' + 23705 '<option class="i18n" data-i18n="[html]menu.feedback.bug"></option>' + 23706 '<option class="i18n" data-i18n="[html]menu.feedback.other"></option>' + 23707 '</select>' 23708 ) 23709 .appendTo(feedbackWindow.body), 23710 23711 comments = $('<textarea id="feedback-comment" class="app-input i18n" data-i18n="[placeholder]menu.feedback.comments" />') 23712 .appendTo(feedbackWindow.body), 23713 23714 send = $('<input type="button" id="feedback-submit" class="app-button i18n" data-i18n="[value]menu.feedback.send" />') 23715 .appendTo(feedbackWindow.body), 23716 23717 message = $('<div class="feedback-message i18n" data-i18n="[placeholder]menu.feedback.thankyou"></div>') 23718 .appendTo(feedbackWindow.body) 23719 .hide() 23720 23721 ; 23722 23723 23724 feedbackWindow.body.addClass("feedback-body"); 23725 feedbackWindow.title.addClass('i18n').attr('data-i18n','[html]menu.labels.feedback'); 23726 23727 modalOverlay.on('click', function() { 23728 hideFeedback(); 23729 }); 23730 23731 feedbackWindow.closeButton.on('click', function() { 23732 hideFeedback(); 23733 }); 23734 23735 function hideFeedback() { 23736 feedbackWindow.hide(); 23737 modalOverlay.hide(); 23738 container.removeClass('blur'); 23739 } 23740 23741 function clickFeedback() { 23742 if (feedbackWindow.container.is(':visible')) { 23743 hideFeedback(); 23744 } else { 23745 feedbackWindow.show(); 23746 feedbackWindow.size( Math.min(500, win.width()-40), 300 ); 23747 feedbackWindow.center(); 23748 23749 message.hide(); 23750 feedbackWindow.body.find('input,textarea,select').show(); 23751 23752 $('#main-menu-dropdown').hide(); 23753 23754 var winWidth = win.width(), 23755 winHeight = win.height(); 23756 23757 container.addClass('blur'); 23758 modalOverlay 23759 .width( winWidth ) 23760 .height( winHeight ) 23761 .show(); 23762 } 23763 } 23764 23765 /// OPERATE FEEDBACK 23766 send.on('click', function() { 23767 23768 if (validateForm() ) { 23769 23770 $.ajax({
23771 url: sofia.config.baseContentUrl + sofia.config.feedbackUrl, 23772 dataType: (sofia.config.baseContentUrl.indexOf('http') > -1 ? 'jsonp' : 'json'), 23773 data: { 23774 name: name.val(), 23775 email: email.val(), 23776 subject: subject.val(), 23777 comments: comments.val() 23778 }, 23779 success: function() { 23780 23781 message.show(); 23782 feedbackWindow.body.find('input,textarea,select').hide(); 23783 23784 setTimeout(function() { 23785 hideFeedback(); 23786 }, 500); 23787 23788 }, 23789 error: function() { 23790 console.log('error', arguments); 23791 } 23792 23793 }) 23794 23795 } 23796 23797 23798 }); 23799 23800 function validateForm() { 23801 var valid = true; 23802 23803 if (name.val() === '') { 23804 name.addClass('invalid'); 23805 valid = false; 23806 } else { 23807 name.removeClass('invalid'); 23808 } 23809 23810 if (email.val() === '' || !email.val().match(/.+@.+\..+/gi)) { 23811 email.addClass('invalid'); 23812 valid = false; 23813 } else { 23814 email.removeClass('invalid'); 23815 } 23816 23817 23818 if (comments.val() === '') { 23819 comments.addClass('invalid'); 23820 valid = false; 23821 } else { 23822 comments.removeClass('invalid'); 23823 } 23824 23825 return valid; 23826 } 23827 23828 23829 23830 return feedbackButton; 23831}; 23832sofia.menuComponents.push('Feedback'); 23833 23834 23835 23836/********* 23837* ../app/js/menu/restore.js 23838**********/ 23839sofia.config = $.extend(sofia.config, { 23840 enableRestore: false 23841}); 23842 23843 23844var RestoreButton = function(node) { 23845 23846 if (!sofia.config.enableRestore) { 23847 return; 23848 } 23849 23850 var buttonMenu = $('#main-menu-windows-list'); 23851 23852 var restoreButton = $('<span class="window-reset i18n" data-i18n="[html]menu.reset">Reset</span>') 23853 .on('click', function() { 23854 23855 if (typeof sofia.config.windows != 'undefined') { 23856 23857 var querystring = []; 23858 23859 for (var i=0, il=sofia.config.windows.length; i<il; i++) { 23860 var win = sofia.config.windows[i]; 23861 23862 // type 23863 querystring.push('win'+ (i+1) + '=' + win.type); 23864 23865 // data 23866 var keys = Object.keys(win.data); 23867 for (var k in keys) { 23868 querystring.push(keys[k] + (i+1) + '=' + win.data[keys[k]]); 23869 } 23870 } 23871 23872 window.location.href = window.location.pathname + '?' + querystring.join('&'); 23873 23874 23875 23876 } else { 23877 window.location.reload(); 23878 } 23879 }) 23880 .appendTo(buttonMenu); 23881 23882 23883 return restoreButton; 23884 23885}; 23886sofia.menuComponents.push('RestoreButton'); 23887 23888 23889 23890/********* 23891* ../app/js/menu/navigation.js 23892**********/ 23893sofia.config = $.extend(sofia.config, { 23894 enableNavigationButtons: true 23895}); 23896 23897 23898 23899var NavigationButtons = function(node) { 23900 23901 if (!sofia.config.enableNavigationButtons) { 23902 return; 23903 } 23904 23905 $('html').addClass('supports-fullscreen'); 23906 23907 var 23908 forwardButton = $('<div id="main-forward-button" class="inactive"></div>') 23909 .appendTo( $('.windows-header') ) 23910 .on('click', forward), 23911 backButton = $('<div id="main-back-button" class="inactive"></div>') 23912 .appendTo( $('.windows-header') ) 23913 .on('click', back), 23914 23915 compactBackButton = $('<div id="compact-back-button"><span id="compact-back-button-label"></span></div>') 23916 .appendTo( $('body') ) 23917 .on('click', back), 23918 23919 isFullscreen = false; 23920 23921 function back() { 23922 TextNavigation.back(); 23923 //window.history.go(-1); 23924 } 23925 23926 function forward() { 23927 TextNavigation.forward(); 23928 //window.history.go(1); 23929 } 23930 23931 /* 23932 $(window).on('popstate', function(e) { 23933 console.log('fwrd/back pop', e); 23934 updateButtonStates(); 23935 }); 23936 */ 23937 23938 TextNavigation.on('locationchange', function(e) { 23939 console.log('fwrd/back locationchange', e); 23940 updateButtonStates(); 23941 }); 23942 23943 function updateButtonStates() { 23944 23945 var locations = TextNavigation.getLocations(), 23946 locationIndex = TextNavigation.getLocationIndex(); 23947 23948 // BACK 23949 if (locationIndex > 0) { 23950 backButton.removeClass('inactive'); 23951 23952 23953 // setup mobile/compact button 23954 var lastRef = new bible.Reference(locations[locations.length-2]); 23955 compactBackButton.find('#compact-back-button-label').html( lastRef.toString() ); 23956 23957 compactBackButton 23958 .addClass('active') 23959 .css({'display':''}); 23960 23961 23962 if ($('body').hasClass('compact-ui')) { 23963 23964 startCompactTimer(); 23965 23966 } 23967 23968 23969 } else { 23970 backButton.addClass('inactive'); 23971 23972 compactBackButton.removeClass('active'); 23973 } 23974 23975 // FORWARD 23976 if (locationIndex < locations.length-1) { 23977 forwardButton.removeClass('inactive'); 23978 } else { 23979 forwardButton.addClass('inactive'); 23980 } 23981 } 23982 23983 var compactTimer = null; 23984 function startCompactTimer() { 23985 clearCompactTimer(); 23986 23987 compactTimer = setTimeout(hideCompactTimer, 5000); 23988 } 23989 23990 function clearCompactTimer() { 23991 if (compactTimer != null) { 23992 clearTimeout(compactTimer); 23993 } 23994 } 23995 23996 function hideCompactTimer() { 23997 if (compactBackButton.is(':visible')) { 23998 compactBackButton.fadeOut(); 23999 } 24000 } 24001 24002 24003 24004 updateButtonStates(); 24005 24006 24007 return null; 24008}; 24009sofia.menuComponents.push('NavigationButtons'); 24010 24011 24012 24013/********* 24014* ../app/js/menu/config-fontsize.js 24015**********/ 24016sofia.config = $.extend(sofia.config, { 24017 24018 enableFontSizeSelector: true, 24019 24020 fontSizeMin: 14, 24021 fontSizeMax: 28, 24022 fontSizeStep: 2, 24023 fontSizeDefault: 18 24024 24025}); 24026 24027 24028var FontSizeSettings = function(node) { 24029 24030 // generate font sizes 24031 var styleCode = '';
24032 for (var size = sofia.config.fontSizeMin; size <= sofia.config.fontSizeMax; size += sofia.config.fontSizeStep) { 24033 styleCode += '.config-font-size-' + size.toString() + ' .reading-text { font-size: ' + size.toString() + 'px; }'; 24034 } 24035 $('<style>' + styleCode + '</style>').appendTo( $('head') ); 24036 24037 24038 if (!sofia.config.enableFontSizeSelector) { 24039 24040 setFontSize(sofia.config.fontSizeDefault); 24041 24042 return; 24043 } 24044 24045 var 24046 body = $('#config-type .config-body'), 24047 fontSizeKey = 'config-font-size', 24048 defaultFontSizeSetting = {"fontSize": sofia.config.fontSizeDefault}, 24049 fontSizeSetting = AppSettings.getValue(fontSizeKey, defaultFontSizeSetting); 24050 24051 $('<table id="font-size-table"><tr><td><span style="font-size:' + sofia.config.fontSizeMin + 'px">A</span><td style="width:100%"></td><td><span style="font-size:' + sofia.config.fontSizeMax + 'px">A</span></td></tr></table>') 24052 .appendTo(body); 24053 24054 // HTML5 range control (IE10+, FF35+) 24055 $('<input type="range" min="' + sofia.config.fontSizeMin + '" max="' + sofia.config.fontSizeMax + '" step="' + sofia.config.fontSizeStep + '" value="' + fontSizeSetting.fontSize + '" style="width: 100%;" />') 24056 .appendTo(body.find('td:eq(1)') ) 24057 .on('change input', function() { 24058 24059 console.log( $(this).val() ); 24060 setFontSize( $(this).val() ); 24061 24062 }); 24063 24064 setFontSize(fontSizeSetting.fontSize); 24065 24066 function setFontSize(newFontSize) { 24067 24068 24069 var body = $('body'); 24070 24071 PlaceKeeper.storePlace(); 24072 24073 // remove all others 24074 for (var size = sofia.config.fontSizeMin; size <= sofia.config.fontSizeMax; size += sofia.config.fontSizeStep) { 24075 24076 var className = 'config-font-size-' + size; 24077 24078 body.removeClass(className); 24079 } 24080 24081 body.addClass('config-font-size-' + newFontSize); 24082 24083 AppSettings.setValue(fontSizeKey, {fontSize: newFontSize}); 24084 24085 24086 if (sofia.analytics) { 24087 sofia.analytics.record('setting', 'fontsize', newFontSize); 24088 } 24089 24090 PlaceKeeper.restorePlace(); 24091 } 24092 24093}; 24094 24095sofia.menuComponents.push('FontSizeSettings'); 24096 24097 24098 24099/********* 24100* ../app/js/menu/config-fontfamily.js 24101**********/ 24102sofia.config = $.extend(sofia.config, { 24103 24104 enableFontFamilySelector: true, 24105 24106 fontFamilyStacks: { 24107 'Cambria': 'Cambria, Georgia, serif', 24108 'Palatino': 'Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif', 24109 //'Georgia': 'Georgia, Times, "Times New Roman", serif', 24110 'Libertine': 'Libertine', // Serif family but does not need fallback because we're loding it as a webfont if not present on system 24111 'Helvetica': '"Helvetica Neue", Helvetica, Arial, sans-serif', 24112 'Trebuchet': '"Trebuchet MS", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Tahoma, sans-serif' 24113 } 24114 24115}); 24116 24117 24118 24119var FontFamilySettings = function(node) { 24120 24121 var 24122 body = $('#config-type .config-body'), 24123 fontFamilyStackNames = Object.keys(sofia.config.fontFamilyStacks), // ['Cambria', 'Helvetica', 'Baskerville', 'Times', 'Libertine'], 24124 defaultFontSetting = {"fontName": fontFamilyStackNames[0]}, 24125 fontFamilyKey = 'config-font-family', 24126 fontFamilySetting = AppSettings.getValue(fontFamilyKey, defaultFontSetting), 24127 fontSettingHtml = '', 24128 fontFamilyStyle = ''; 24129 24130 // 24131 for(var i=0, il=fontFamilyStackNames.length; i<il; i++) { 24132 var fontStackName = fontFamilyStackNames[i], 24133 fontStackValue = sofia.config.fontFamilyStacks[fontStackName]; 24134 24135 fontSettingHtml += 24136 '<label id="config-font-family-' + fontStackName + '" class="config-font-family" title="' + fontStackName + '" >' + 24137 '<input type="radio" id="config-font-family-' + fontStackName + '-value" name="config-font-family" value="' + fontStackName + '" />' + 24138 'Aa' + 24139 '</label>'; 24140 24141 fontFamilyStyle += 24142 '#config-font-family-' + fontStackName + ', ' + 24143 '.config-font-family-' + fontStackName + ' .reading-text,' + 24144 '.config-font-family-' + fontStackName + ' #font-size-table {' + 24145 ' font-family: ' + fontStackValue + ';' + 24146 '}'; 24147 24148 } 24149 24150 $('<style>' + fontFamilyStyle + '</style>').appendTo( $('head') ); 24151 24152 if (!sofia.config.enableFontFamilySelector) { 24153 setFontFamily(defaultFontSetting.fontName); 24154 return; 24155 } 24156 24157 24158 $(fontSettingHtml).appendTo(body); 24159 24160 24161 function setFontFamily(newFontStackName) { 24162 24163 var body = $('body'); 24164 24165 PlaceKeeper.storePlace(); 24166 24167 // remove all others 24168 for(var i=0, il=fontFamilyStackNames.length; i<il; i++) { 24169 var fontStackName = fontFamilyStackNames[i], 24170 className = 'config-font-family-' + fontStackName; 24171 24172 body.removeClass(className); 24173 } 24174 24175 $(body).addClass('config-font-family-' + newFontStackName); 24176 24177 AppSettings.setValue(fontFamilyKey, {fontName: newFontStackName}); 24178 24179 if (sofia.analytics) { 24180 sofia.analytics.record('setting', 'fontfamily', newFontStackName); 24181 } 24182 24183 PlaceKeeper.restorePlace(); 24184 24185 } 24186 24187 // handle clicks 24188 body.on('change', 'input[name=config-font-family]', function() { 24189 var radio = $(this), 24190 newFontFamilyValue = radio.val(); 24191 24192 setFontFamily(newFontFamilyValue); 24193 }); 24194 24195 // set default 24196 body.find('#config-font-family-' + fontFamilySetting.fontName + '-value').trigger('click'); 24197}; 24198 24199sofia.menuComponents.push('FontFamilySettings'); 24200 24201 24202 24203/********* 24204* ../app/js/menu/config-theme.js 24205**********/ 24206sofia.config = $.extend(sofia.config, { 24207 24208 enableThemeSelector: true 24209 24210}); 24211 24212var ThemeSetting = function(node) { 24213 24214 if (!sofia.config.enableThemeSelector) { 24215 return; 24216 } 24217 24218 var 24219 body = $('#config-type .config-body'), 24220 themesBlock = $('<div id="config-themes"></div>') 24221 .appendTo(body), 24222 themeNames = ['default','sepia','dark'], 24223 defaultThemeSetting = {"themeName": themeNames[0]}, 24224 themeKey = 'config-theme', 24225 themeSetting = AppSettings.getValue(themeKey, defaultThemeSetting); 24226 24227 24228 for(var i=0, il=themeNames.length; i<il; i++) { 24229 var themeName = themeNames[i]; 24230 24231 $('<span id="config-theme-' + themeName + '" class="config-theme-toggle i18n" data-i18n="[html]menu.themes.' + themeName + '" data-themename="' + themeName + '">' + themeName + '</span>')
24232 .appendTo(themesBlock); 24233 } 24234 24235 // handle clicks 24236 themesBlock.on('click', '.config-theme-toggle', function() { 24237 var span = $(this), 24238 selectedTheme = span.attr('data-themename'), 24239 selectedThemeClass = 'theme-' + selectedTheme, 24240 $body = $(document.body); 24241 24242 // remove all themes 24243 for(var i=0, il=themeNames.length; i<il; i++) { 24244 var themeClassName = 'theme-' + themeNames[i]; 24245 $body.removeClass(themeClassName); 24246 } 24247 24248 $body.addClass(selectedThemeClass); 24249 24250 span 24251 .addClass('config-theme-toggle-selected') 24252 .siblings() 24253 .removeClass('config-theme-toggle-selected'); 24254 24255 24256 AppSettings.setValue(themeKey, {themeName: selectedTheme} ); 24257 }); 24258 24259 body.find('#config-theme-' + themeSetting.themeName + '').trigger('click'); 24260}; 24261 24262sofia.menuComponents.push('ThemeSetting'); 24263 24264 24265 24266/********* 24267* ../app/js/menu/config-language.js 24268**********/ 24269sofia.config = $.extend(sofia.config, { 24270 24271 enableLanguageSelector: true, 24272 24273 languageSelectorFallbackLang: 'en' 24274 24275}); 24276 24277 24278var LanguageSetting = function(node) { 24279 24280 if (!sofia.config.enableLanguageSelector) { 24281 return; 24282 } 24283 24284 var 24285 body = $('#config-tools .config-body'), 24286 list = $('<select id="config-language" class="app-list"></select>') 24287 .appendTo(body), 24288 langKeys = Object.keys(sofia.resources); 24289 24290 body.append($('<div class="clear"></div>')); 24291 24292 // make sure English isn't first! 24293 langKeys.sort(); 24294 24295 for(var i=0, il=langKeys.length; i<il; i++) { 24296 var langKey = langKeys[i], 24297 langName = sofia.resources[langKey].translation.name; 24298 24299 $('<option value="' + langKey + '">' + langName + '</option>') 24300 .appendTo(list); 24301 } 24302 24303 // handle clicks 24304 list.on('change', function() { 24305 var newLang = list.val(); 24306 24307 i18n.setLng(newLang); 24308 $('.i18n').i18n(); 24309 24310 localizeLanguages(); 24311 }); 24312 24313 function localizeLanguages() { 24314 var usersLanguage = i18n.lng(); 24315 24316 // go through options and add new info 24317 list.children('option').each(function() { 24318 var option = $(this), 24319 langValue = option.attr('value'); 24320 resourceData = sofia.resources[langValue].translation, 24321 name = resourceData.name, 24322 fallbackName = resourceData.names[sofia.config.languageSelectorFallbackLang], 24323 localizedName = resourceData.names[usersLanguage], 24324 fullname = name; 24325 24326 // use the localized name if possible 24327 if (localizedName != null && typeof localizedName != 'undefined' && localizedName != fullname) { 24328 fullname += ' (' + localizedName + ')'; 24329 24330 // fallback to english 24331 } else if (fallbackName != null && typeof fallbackName != 'undefined' && fullname != fallbackName) { 24332 fullname += ' (' + fallbackName + ')'; 24333 } 24334 24335 option.html(fullname); 24336 24337 }); 24338 } 24339 24340 list[0].localizeLanguages = localizeLanguages; 24341}; 24342 24343sofia.menuComponents.push('LanguageSetting'); 24344 24345 24346 24347/********* 24348* ../app/js/menu/config-toggles.js 24349**********/ 24350sofia.config = $.extend(sofia.config, { 24351 24352 enableSettingToggles: true, 24353 settingToggleNames: ['Chapters', 'Verses', 'Titles', 'Notes', 'Words of Christ', 'Media', 'Justify'], 24354 settingToggleDefaults: [true, true, true, true, true, true, false] 24355 24356}); 24357 24358 24359var ConfigToggles = function(node) { 24360 24361 var body = $('#config-type .config-body'), 24362 toggleNames = sofia.config.settingToggleNames, 24363 toggleDefaults = sofia.config.settingToggleDefaults, 24364 prefix = 'config-toggle-'; 24365 24366 if (!sofia.config.enableSettingToggles) { 24367 for(var i=0, il=toggleNames.length; i<il; i++) { 24368 var toggleName = toggleNames[i] 24369 setToggle(toggleName, toggleDefaults[i]); 24370 } 24371 24372 return; 24373 } 24374 24375 sofia.globals.createToggle = function(toggleName, defaultValue) { 24376 24377 var 24378 toggleId = toggleName.replace(/\s/gi, '').toLowerCase(), 24379 toggleDefaultSetting = {checked: defaultValue}, 24380 toggleSetting = AppSettings.getValue(toggleId, toggleDefaultSetting), 24381 toggle = $('<div id="config-toggle-' + toggleId + '" class="config-toggle">' + 24382 '<input id="config-toggle-' + toggleId + '-input" type="checkbox" value="' + toggleId + '" />' + 24383 '<label for="config-toggle-' + toggleId + '-input" title="' + toggleName + '">' + toggleName + '</label>' + 24384 '</div>') 24385 .appendTo(body); 24386 24387 toggle 24388 .find('input') 24389 .on('click', function() { 24390 var cb = $(this), 24391 checked = cb.is(':checked'), 24392 value = cb.val(); 24393 24394 setToggle(value, checked); 24395 24396 }); 24397 24398 setToggle(toggleId, toggleSetting.checked); 24399 24400 } 24401 24402 function setToggle(toggleId, checked) { 24403 24404 PlaceKeeper.storePlace(); 24405 24406 var toggle = $('#config-toggle-' + toggleId), 24407 body = $('body'), 24408 onClass = 'toggle-' + toggleId + '-on', 24409 offClass = 'toggle-' + toggleId + '-off'; 24410 24411 if (checked == true || checked == 'true') { 24412 toggle.addClass('toggle-on'); 24413 toggle.find('input').prop('checked', true); 24414 body 24415 .addClass(onClass) 24416 .removeClass(offClass); 24417 24418 } else { 24419 toggle.removeClass('toggle-on'); 24420 toggle.find('input').prop('checked', false); 24421 body 24422 .removeClass(onClass) 24423 .addClass(offClass); 24424 } 24425 24426 PlaceKeeper.restorePlace(); 24427 24428 24429 if (sofia.analytics) { 24430 sofia.analytics.record('setting', toggleId, checked); 24431 } 24432 24433 //console.log('setValue', toggleId, checked); 24434 AppSettings.setValue(toggleId, {checked: checked}); 24435 24436 } 24437 24438 24439 for(var i=0, il=toggleNames.length; i<il; i++) { 24440 var toggleName = toggleNames[i] 24441 sofia.globals.createToggle(toggleName, toggleDefaults[i]); 24442 } 24443 24444}; 24445 24446sofia.menuComponents.push('ConfigToggles'); 24447 24448 24449 24450/********* 24451* ../app/js/menu/config-url.js 24452**********/ 24453sofia.config = $.extend(sofia.config, { 24454 24455 enableUrlCopier: true 24456 24457}); 24458 24459 24460var ConfigUrl = function(node) { 24461 24462 if (!sofia.config.enableUrlCopier || location.protocol == 'file:') { 24463 return; 24464 } 24465 24466 var // body = $('#config-tools .config-body'), 24467 body = $('#main-menu-features'), 24468 urlBox = 24469 $('<div id="config-global-url">' + 24470 //'<span class="config-header">URL</span>' + 24471 '<span data-clipboard-action="copy" data-clipboard-target="#config-global-url-input"></span>' + 24472 '<input type="text" id="config-global-url-input" />' + 24473 //'<div ></div>' + 24474 '</div>'), 24475 linkButton = urlBox.find('span'), 24476 urlInput = urlBox.find('input'), 24477 urlDiv = urlBox.find('div'), 24478 clickables = [linkButton, urlInput, urlDiv]; 24479 24480 body.after(urlBox); 24481 24482 var urlTimer = new Timer(updateUrl, 500); 24483 24484 setTimeout(function() { 24485 sofia.app.windowManager.on('settingschange', function(e) { 24486 24487 // title to show active window's position 24488 urlTimer.start(); 24489 24490 }); 24491 24492 updateUrl(); 24493 }, 1000); 24494 24495 24496 var clipboard1 = new Clipboard(linkButton[0]); 24497 var clipboard2 = new Clipboard('#config-global-url-input'); 24498 24499 /* 24500 if (Detection.hasFlash) { 24501 ZeroClipboard.config( { moviePath: sofia.config.baseContentUrl + 'build/ZeroClipboard.swf' } ); 24502 for (var c in clickables) { 24503 var el = clickables[c]; 24504 24505 var client = new ZeroClipboard(el); 24506 client.on( 'dataRequested', function (client, args) { 24507 24508 updateUrl(); 24509 24510 client.setText( urlInput.val() ); 24511 24512 urlInput.focus(); 24513 urlInput.select(); 24514 24515 console.log('flash clipboard clicked'); 24516 }); 24517 } 24518 } 24519 */ 24520 24521 urlInput.on('click', function() { 24522 updateUrl(); 24523 24524 urlInput.focus(); 24525 urlInput.select(); 24526 24527 }); 24528 24529 linkButton.on('click', function() { 24530 updateUrl(); 24531 24532 urlInput.focus(); 24533 urlInput.select(); 24534 }); 24535 24536 24537 function updateUrl() { 24538 // get settings from all windows 24539 var windowSettings = sofia.app.windowManager.getSettings(), 24540 existingParams = stringUtility.parseQuerystring(); 24541 newParams = {}, 24542 mergedParams = {}, 24543 mergedArray = []; 24544 24545 for (var i=0, il=windowSettings.length; i<il; i++) { 24546 // get window settings 24547 var winSettings = windowSettings[i]; 24548 24549 if (winSettings.data == null || typeof winSettings.data.params == 'undefined') { 24550 continue; 24551 } 24552 24553 // get window type info 24554 var winTypeName = winSettings.data.params.win, 24555 winTypeInfo = sofia.windowTypes.filter(function(winType) { return winType.param == winTypeName; })[0]; 24556 24557 if (typeof winTypeInfo == 'undefined') { 24558 console.log('Cannot find', winTypeName, winTypeInfo, winSettings.data.params, winSettings.data.params.win);
24559 continue; 24560 } 24561 24562 // go through the params object 24563 // params: {'win': 'bible', 'fragmentid': 'JN1_1'} 24564 // shorten with paramKeys 24565 for (var paramName in winSettings.data.params) { 24566 var paramData = winSettings.data.params[paramName], 24567 paramShort = paramName == 'win' ? 'w' : winTypeInfo.paramKeys[paramName]; 24568 24569 newParams[ paramShort + (i+1) ] = paramData; 24570 //console.log(paramName, paramData); 24571 } 24572 24573 } 24574 24575 // keep all parameters that aren't windowed ones 24576 for (var param in existingParams) { 24577 // check if it's blank or if it's a previously existing parameter (say the third window, and now there is only 2) 24578 if (param != '' && param.indexOf('win') != 0 && param.indexOf('textid') != 0 && param.indexOf('searchtext') != 0 && param.indexOf('fragmentid') != 0 ) { 24579 mergedParams[ param ] = existingParams[param]; 24580 } 24581 } 24582 24583 24584 mergedParams = $.extend({}, mergedParams, newParams); 24585 24586 for (var param in mergedParams) { 24587 if (param != '' ) { 24588 mergedArray.push( param + '=' + encodeURIComponent( mergedParams[param] ) ); 24589 } 24590 } 24591 //mergedArray.reverse(); 24592 24593 url = location.protocol + '//' + location.host + location.pathname + '?' + mergedArray.join('&'); 24594 24595 urlInput.val(url); 24596 urlDiv.html(url); 24597 24598 //console.log('URL', url); 24599 } 24600 24601 24602 24603 //base.height('300px'); 24604}; 24605 24606sofia.menuComponents.push('ConfigUrl'); 24607 24608
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.