├── .gitignore ├── images ├── icon.png ├── icon128.png ├── icon16.png └── icon48.png ├── fonts ├── FontAwesome.otf ├── fontawesome-webfont.eot ├── fontawesome-webfont.ttf ├── fontawesome-webfont.woff ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.ttf ├── glyphicons-halflings-regular.woff └── glyphicons-halflings-regular.svg ├── screenshots ├── V1.2 │ ├── 1.PNG │ ├── 2.png │ ├── 3.png │ └── 4.png ├── Chrome Settings Menu.PNG ├── Duolingo Notes Version.PNG ├── Set up shortcut key 1.png ├── Set up shortcut key 2.png ├── V1.0 │ ├── Add to Duolingo Notes.png │ └── Duolingo Notes Popup.png ├── Force Chrome Update Extensions.PNG ├── Right Duolingo Notes Action Button.PNG └── Duolingo Notes Version On Google Web Store.PNG ├── js ├── jqueryFunctions.js ├── app.js ├── contextScript.addToNotes.js ├── popupCtrl.js ├── html2csv.js ├── underscore-min.js ├── guessLanguage.js └── bootstrap.min.js ├── css ├── angular-csp.css ├── main.css ├── bootstrap-theme.min.css └── font-awesome.min.css ├── manifest.json ├── LICENSE ├── README.md └── popup.html /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/images/icon.png -------------------------------------------------------------------------------- /images/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/images/icon128.png -------------------------------------------------------------------------------- /images/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/images/icon16.png -------------------------------------------------------------------------------- /images/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/images/icon48.png -------------------------------------------------------------------------------- /fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /screenshots/V1.2/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/V1.2/1.PNG -------------------------------------------------------------------------------- /screenshots/V1.2/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/V1.2/2.png -------------------------------------------------------------------------------- /screenshots/V1.2/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/V1.2/3.png -------------------------------------------------------------------------------- /screenshots/V1.2/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/V1.2/4.png -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /screenshots/Chrome Settings Menu.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/Chrome Settings Menu.PNG -------------------------------------------------------------------------------- /screenshots/Duolingo Notes Version.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/Duolingo Notes Version.PNG -------------------------------------------------------------------------------- /screenshots/Set up shortcut key 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/Set up shortcut key 1.png -------------------------------------------------------------------------------- /screenshots/Set up shortcut key 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/Set up shortcut key 2.png -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /screenshots/V1.0/Add to Duolingo Notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/V1.0/Add to Duolingo Notes.png -------------------------------------------------------------------------------- /screenshots/V1.0/Duolingo Notes Popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/V1.0/Duolingo Notes Popup.png -------------------------------------------------------------------------------- /screenshots/Force Chrome Update Extensions.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/Force Chrome Update Extensions.PNG -------------------------------------------------------------------------------- /screenshots/Right Duolingo Notes Action Button.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/Right Duolingo Notes Action Button.PNG -------------------------------------------------------------------------------- /screenshots/Duolingo Notes Version On Google Web Store.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakeLin/DuolingoNotes/HEAD/screenshots/Duolingo Notes Version On Google Web Store.PNG -------------------------------------------------------------------------------- /js/jqueryFunctions.js: -------------------------------------------------------------------------------- 1 | // Using Jquery inside the controller is considered as bad practice in Angular JS 2 | // Use a separate file to place Jquery stuff. Do things quickly but they should be done in Controller. 3 | $(function() { 4 | $('#exportToCsv').click(function(){ 5 | $('#notesTable').table2CSV({header:['Question','Duolingo\'s answer','Your answer','Comments']}); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /css/angular-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | @charset "UTF-8"; 4 | 5 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], 6 | .ng-cloak, .x-ng-cloak, 7 | .ng-hide { 8 | display: none !important; 9 | } 10 | 11 | ng\:form { 12 | display: block; 13 | } 14 | 15 | /* The styles below ensure that the CSS transition will ALWAYS 16 | * animate and close. A nasty bug occurs with CSS transitions where 17 | * when the active class isn't set, or if the active class doesn't 18 | * contain any styles to transition to, then, if ngAnimate is used, 19 | * it will appear as if the webpage is broken due to the forever hanging 20 | * animations. The border-spacing (!ie) and zoom (ie) CSS properties are 21 | * used below since they trigger a transition without making the browser 22 | * animate anything and they're both highly underused CSS properties */ 23 | .ng-animate-start { border-spacing:1px 1px; -ms-zoom:1.0001; } 24 | .ng-animate-active { border-spacing:0px 0px; -ms-zoom:1; } 25 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | 4 | "name": "Duolingo Notes", 5 | "description": "Help you learn Duolingo faster and better", 6 | "version": "2.0", 7 | 8 | "browser_action": { 9 | "default_icon": "images/icon.png", 10 | "default_popup": "popup.html" 11 | }, 12 | "permissions": [ 13 | "https://*.duolingo.com/", 14 | "http://*.duolingo.com/", 15 | "activeTab", 16 | "storage", 17 | "unlimitedStorage", 18 | "notifications", 19 | "clipboardWrite", 20 | "contextMenus" 21 | ], 22 | "background": { 23 | "scripts": ["js/jquery-2.1.0.min.js", "js/app.js"] 24 | }, 25 | "commands": { 26 | "add-to-notes": { 27 | "suggested_key": { 28 | "default": "Alt+A" 29 | }, 30 | "description": "Add to Duolingo Notes" 31 | } 32 | }, 33 | "icons": { 34 | "16": "images/icon16.png", 35 | "48": "images/icon48.png", 36 | "128": "images/icon128.png" 37 | }, 38 | "web_accessible_resources": [ 39 | "images/icon.png" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jake Lin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jake Lin on 5/2/2014. 3 | */ 4 | 5 | function addToNotes(info, tab) { 6 | chrome.tabs.executeScript(null, { file: "js/jquery-2.1.0.min.js" }, function() { 7 | chrome.tabs.executeScript(null, { file: "js/contextScript.addToNotes.js" }, function (result) { 8 | // console.log(result); 9 | }); 10 | }); 11 | } 12 | 13 | var showForPages = ["https://www.duolingo.com/skill/*", "http://www.duolingo.com/skill/*","https://www.duolingo.com/practice/*", "http://www.duolingo.com/practice/*"]; 14 | var contexts = ["page","selection","link","editable","image","video","audio"]; 15 | 16 | chrome.contextMenus.create({"title": "Add to Duolingo Notes", 17 | "documentUrlPatterns":showForPages, 18 | "contexts":contexts, 19 | "onclick": addToNotes}); 20 | 21 | chrome.commands.onCommand.addListener(function(command) { 22 | console.log('Command:', command); 23 | if (command == "add-to-notes") { 24 | addToNotes(); 25 | } 26 | }); 27 | 28 | // Migrate all data from chrome.storage.sync to chrome.storage.local due to storage limit problem. 29 | // Can be removed in the future. 30 | console.log("Migration starts"); // TODO: will be removed 31 | chrome.storage.sync.get('notes', function (result){ 32 | var notes = result['notes']; 33 | console.log("notes"); 34 | console.log(notes); 35 | if (notes) { 36 | chrome.storage.local.set({'notes': notes}, function() {}); 37 | chrome.storage.sync.set({'notes': null}, function() {}); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jake Lin on 5/2/2014. 3 | */ 4 | 5 | /* Base styles (regardless of theme) */ 6 | .bs-callout { 7 | margin: 20px 0; 8 | padding: 15px 30px 15px 15px; 9 | border-left: 5px solid #eee; 10 | } 11 | .bs-callout h4 { 12 | margin-top: 0; 13 | } 14 | .bs-callout p:last-child { 15 | margin-bottom: 0; 16 | } 17 | .bs-callout code, 18 | .bs-callout .highlight { 19 | background-color: #fff; 20 | } 21 | 22 | /* Themes for different contexts */ 23 | .bs-callout-danger { 24 | background-color: #fcf2f2; 25 | border-color: #dFb5b4; 26 | } 27 | .bs-callout-warning { 28 | background-color: #fefbed; 29 | border-color: #f1e7bc; 30 | } 31 | .bs-callout-info { 32 | background-color: #f0f7fd; 33 | border-color: #d0e3f0; 34 | } 35 | 36 | .bs-callout-danger h4 { 37 | color: #B94A48; 38 | } 39 | 40 | .bs-callout-warning h4 { 41 | color: #C09853; 42 | } 43 | 44 | .bs-callout-info h4 { 45 | color: #3A87AD; 46 | } 47 | 48 | body { 49 | min-width: 780px; 50 | overflow-x: hidden; 51 | } 52 | 53 | .btn-margin { 54 | margin-right: 5px; 55 | } 56 | 57 | #search { 58 | margin: 15px 0; 59 | } 60 | 61 | .round-btn { 62 | border-radius:100%; 63 | } 64 | 65 | .word_image { 66 | width: 60px; 67 | height: 60px; 68 | margin: 0 5px 5px; 69 | } 70 | 71 | .word_image:hover { 72 | cursor: pointer; 73 | transform:matrix(3, 0, 0, 3, 60, 0); 74 | -ms-transform:matrix(3, 0, 0, 3, 60, 0); /* IE 9 */ 75 | -moz-transform:matrix(3, 0, 0, 3, 60, 0); /* Firefox */ 76 | -webkit-transform:matrix(3, 0, 0, 3, 60, 0); /* Safari and Chrome */ 77 | -o-transform:matrix(3, 0, 0, 3, 60, 0); /* Opera */ 78 | } 79 | 80 | .action-buttons { 81 | margin: 20px 20px 0; 82 | } 83 | 84 | .action-buttons .btn { 85 | margin: 0 15px; 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Duolingo Notes 2 | 3 | Duolingo Notes is a Google Chrome Extension can help the duolingo users save notes during learning. 4 | 5 | This Extension is free on [Google Web Store | Duolingo Notes](https://chrome.google.com/webstore/detail/duolingo-notes/fdhafjdcofficgjebiflfamofkoedieh). 6 | 7 | User Guide is availabe on [Duolingo | Duolingo Notes App - Help you learn Duolingo faster and better](https://www.duolingo.com/comment/2976444). 8 | 9 | ### Features 10 | * bookmark the question and answer during learning. 11 | * Review your bookmarked notes. 12 | * Copy the note. 13 | * Speak the note. 14 | * Add personal comments. 15 | * Search the note. 16 | * Delete the note. 17 | * Delete all notes. 18 | * Export notes to CSV. 19 | * Use shortcut key (Alt+A) to add note. 20 | 21 | ### Design Considerations 22 | * I use chrome.storage (chrome.storage.sync) instead of IndexedDB because I want to sync the notes across different Google Chrome browsers easily. Due to storage limit, I have changed to use chrome.storage.local to replace chrome.storage.sync. 23 | 24 | ### License 25 | The app is under MIT License. The duolingo trademark and the owl logo are owned by Duolingo. And the third party libraries are owned by original vendors. 26 | 27 | ### Release Notes 28 | * V2.0 29 | Add shortcut key (Alt+A) to add note. Thanks to [northernguy](https://www.duolingo.com/northernguy) for suggesting these features. 30 | 31 | * V1.9 32 | Add delete all notes button. Add export notes to CSV feature. Thanks to [northernguy](https://www.duolingo.com/northernguy) for suggesting these features. 33 | 34 | * V1.8 35 | Support image select question. 36 | 37 | * V1.7 38 | Improve User Experience, show bigger image when mouse hover over, add tooltip for buttons, resize the popup and display better for Windows users. 39 | 40 | * V1.6 41 | Use chrome.storage.local to replace chrome.storage.sync due to the storage limit of chrome.storage.sync (https://developer.chrome.com/extensions/storage#type-StorageArea). Thanks to [northernguy](https://www.duolingo.com/northernguy) for reporting that. 42 | 43 | * V1.5 44 | Added Support to Practice Lesson(practicing/strengthening a lesson). The user can add notes during Practice Lesson. Thanks to [jan williams](https://www.duolingo.com/willijanb) for reporting that. 45 | 46 | * V1.4 47 | Added tooltip for personal comments. Thanks to [Dessamator](https://www.duolingo.com/Dessamator) for suggesting this feature. 48 | 49 | * V1.3 50 | Added personal comments box. 51 | 52 | * V1.2 53 | Added speech button. 54 | -------------------------------------------------------------------------------- /js/contextScript.addToNotes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jake Lin on 4/30/2014. 3 | */ 4 | 5 | function guid() { 6 | function _p8(s) { 7 | var p = (Math.random().toString(16)+"000000000").substr(2,8); 8 | return s ? "-" + p.substr(0,4) + "-" + p.substr(4,4) : p ; 9 | } 10 | return _p8() + _p8(true) + _p8(true) + _p8(); 11 | } 12 | 13 | var type = undefined, question = undefined, userAnswer = undefined, duolingoAnswer = undefined, correct = undefined; 14 | 15 | console.log('add to notes'); 16 | 17 | if($('#app.listen')[0] !== undefined){ 18 | // Type what you hear 19 | type = 'listen'; 20 | question = '♪'; 21 | userAnswer = $('#graded-word-input').text(); 22 | } 23 | else if ($('#app.translate')[0] !== undefined){ 24 | // Text translation 25 | type = 'translate'; 26 | question = $('#session-element-container .text-to-translate .token').text(); 27 | userAnswer = $('#graded-text-input').text(); 28 | } 29 | else if ($('#app.name')[0] !== undefined){ 30 | // Type what you see (pictures) 31 | type = 'name'; 32 | question = []; 33 | $('.list-tilted-images li').each(function( index ) { 34 | var bg = $(this).css('background-image'); 35 | bg = bg.replace('url(','').replace(')',''); 36 | question.push(bg); 37 | // console.log( bg ); 38 | }); 39 | 40 | userAnswer = $('#graded-word-input').text(); 41 | } 42 | else if ($('#app.judge')[0] !== undefined){ 43 | // Multiple choice with text 44 | type = 'judge'; 45 | question = $('.judge-row .col-left').text(); 46 | userAnswer = $('.judge-row .col-right .white-label.active').text(); 47 | } 48 | else if ($('#app.select')[0] !== undefined){ 49 | // Multiple choice with text and pictures 50 | type = 'select'; 51 | question = $('.challenge-select .player').text(); 52 | userAnswer = $('.challenge-select .select-images li.selected .select-images-frame').css('background-image'); 53 | userAnswer = userAnswer.replace('url(','').replace(')',''); 54 | 55 | duolingoAnswer = $('.challenge-select .select-images li.correct .select-images-frame').css('background-image'); 56 | duolingoAnswer = duolingoAnswer.replace('url(','').replace(')',''); 57 | } 58 | else if ($('#app.reverse_speak')[0] !== undefined){ 59 | // Say something after translating it to the language you're learning 60 | type = 'reverse_speak'; 61 | question = $('#original-text').text(); 62 | duolingoAnswer = userAnswer = '♪'; 63 | } 64 | else if ($('#app.speak')[0] !== undefined){ 65 | // Read something in the language you're learning 66 | type = 'speak'; 67 | question = $('#original-text').text(); 68 | duolingoAnswer = userAnswer = '♪'; 69 | } 70 | else if ($('#app.form')[0] !== undefined){ 71 | // Fill in the blank using a drop down 72 | type = 'form'; 73 | } 74 | 75 | correct = ($('#grade .icon-wrong-big')[0] === undefined); 76 | if(!duolingoAnswer) { 77 | duolingoAnswer = $('#grade .lighter').text(); 78 | } 79 | 80 | if(correct === true && !duolingoAnswer){ 81 | duolingoAnswer = userAnswer; 82 | } 83 | 84 | console.log("type: " + type + ", question: " + question + ", userAnswer:" + userAnswer + ", duolingoAnswer:" + duolingoAnswer + ", correct:" + correct); 85 | if (question && userAnswer && duolingoAnswer) { 86 | chrome.storage.local.get('notes', function (result){ 87 | var notes = result['notes'] || {}; 88 | var id = guid(); 89 | notes[id] = {id: id, d:Date.now(), t: type, q:question, ua:userAnswer, da:duolingoAnswer, r: correct, c: ''}; 90 | console.log(notes[id]); 91 | chrome.storage.local.set({'notes': notes}, function() {}); 92 | }); 93 | } 94 | -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Duolingo Notes 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Duolingo Notes - Help you learn Duolingo faster and better

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 32 | 36 | 37 | 38 | 39 |
QuestionDuolingo's answerYour answerComments
25 |
26 |
{{ note.q }}
27 |
29 |
30 |
{{ note.da }}
31 |
33 |
34 |
{{ note.ua }}
35 |
40 |
41 | 42 | 43 |
44 |
45 |
If there is the first time to use the app. Please check out the User Guide on Duolingo. Version: {{versionNumber()}}
46 |
47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /js/popupCtrl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jake Lin on 5/2/2014. 3 | */ 4 | 5 | 'use strict'; 6 | var duolingoApp = angular.module('duolingoApp', []); 7 | 8 | duolingoApp.controller('PopupController', ['$scope', function($scope) { 9 | // Binding variables 10 | $scope.notes = []; 11 | 12 | /** Methods **/ 13 | // Check whether display 'Speak' button or not 14 | $scope.showSpeakButton = function (text) { 15 | if (typeof popup.showSpeakButton === 'undefined') { 16 | if ('speechSynthesis' in window) { 17 | popup.showSpeakButton = true; 18 | } else { 19 | popup.showSpeakButton = false; 20 | } 21 | } 22 | // console.log(popup.showSpeakButton); 23 | // Check the text, if equals to '♪' then hide the button 24 | if(typeof text !== 'undefined' && text === '♪') { 25 | return false; 26 | } 27 | 28 | return popup.showSpeakButton; 29 | }; 30 | 31 | // Check whether display the button. 32 | $scope.showButton = function (text) { 33 | // text is not null, undefined or empty or '♪' 34 | return (text && text !== '♪'); 35 | }; 36 | 37 | $scope.versionNumber = function () { 38 | var manifest = chrome.runtime.getManifest(); 39 | return manifest.version; 40 | }; 41 | 42 | $scope.clickCopy = function (text) { 43 | var copyDiv = document.createElement('div'); 44 | copyDiv.contentEditable = true; 45 | document.body.appendChild(copyDiv); 46 | copyDiv.innerHTML = text; 47 | copyDiv.unselectable = "off"; 48 | copyDiv.focus(); 49 | document.execCommand('SelectAll'); 50 | document.execCommand("Copy", false, null); 51 | document.body.removeChild(copyDiv); 52 | }; 53 | 54 | $scope.clickSpeak = function (text) { 55 | // Create a new instance of SpeechSynthesisUtterance. 56 | var msg = new SpeechSynthesisUtterance(); 57 | 58 | // Set the text. 59 | msg.text = text; 60 | 61 | guessLanguage.detect(text, function(language) { 62 | console.log('Detected language of provided text is [' + language + ']'); 63 | // Find the voice and set the utterance instance's voice attribute. 64 | msg.voice = speechSynthesis.getVoices().filter(function(voice) { return voice.name == popup.LANGUAGE_MAP[language]; })[0]; msg.voice = speechSynthesis.getVoices().filter(function(voice) { return voice.name == popup.LANGUAGE_MAP[language]; })[0]; 65 | // Queue this utterance. 66 | window.speechSynthesis.speak(msg); 67 | }); 68 | }; 69 | 70 | $scope.clickDelete = function (id) { 71 | chrome.storage.local.get('notes', function (result){ 72 | var notes = result['notes']; 73 | delete notes[id]; 74 | chrome.storage.local.set({'notes': notes}, function() { popup.loadNotes();}); 75 | }); 76 | }; 77 | 78 | $scope.clickDeleteAll = function () { 79 | chrome.storage.local.get('notes', function (result){ 80 | chrome.storage.local.set({'notes': null}, function() { popup.loadNotes();}); 81 | }); 82 | }; 83 | 84 | $scope.changeComment = function (id, comment) { 85 | chrome.storage.local.get('notes', function (result){ 86 | var notes = result['notes']; 87 | notes[id]['c'] = comment; 88 | chrome.storage.local.set({'notes': notes}, function() { }); 89 | }); 90 | }; 91 | 92 | // Private stuff 93 | var popup = {}; 94 | popup.LANGUAGE_MAP = { 95 | 'en' : 'Google US English', 96 | 'es' : 'Google Español', 97 | 'fr' : 'Google Français', 98 | 'ja' : 'Google 日本人', 99 | 'ko' : 'Google 한국의', 100 | 'zh' : 'Google 中国的', 101 | 'zh_TW' : 'Google 中国的', 102 | 'it' : 'Google Italiano', 103 | 'de' : 'Google Deutsch' 104 | }; 105 | 106 | popup.loadNotes = function () { 107 | chrome.storage.local.get('notes', function (result){ 108 | 109 | // Flaten the object to an array for bidding. 110 | var notes = []; 111 | for(var guid in result['notes']) { 112 | var note = result['notes'][guid]; 113 | 114 | // set the diplay color for the user answer 115 | if(note.r === true) { 116 | note.textColor = 'blue'; 117 | } 118 | else if (note.r === false) { 119 | note.textColor = 'red'; 120 | } 121 | notes.push(note); 122 | } 123 | 124 | // Sort by created datetime descendingly. 125 | $scope.notes = _.sortBy(notes, function(note){ 126 | return -(new Date(note.d)); 127 | }); 128 | 129 | $scope.$apply(); 130 | }); 131 | }; 132 | 133 | popup.init = function () { 134 | popup.loadNotes(); 135 | }; 136 | 137 | popup.init(); 138 | }]); 139 | -------------------------------------------------------------------------------- /js/html2csv.js: -------------------------------------------------------------------------------- 1 | /* FileSaver.js 2 | * A saveAs() FileSaver implementation. 3 | * 2014-05-27 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * License: X11/MIT 7 | * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md 8 | */ 9 | 10 | /*global self */ 11 | /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 12 | 13 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 14 | 15 | var saveAs = saveAs 16 | // IE 10+ (native saveAs) 17 | || (typeof navigator !== "undefined" && 18 | navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator)) 19 | // Everyone else 20 | || (function(view) { 21 | "use strict"; 22 | // IE <10 is explicitly unsupported 23 | if (typeof navigator !== "undefined" && 24 | /MSIE [1-9]\./.test(navigator.userAgent)) { 25 | return; 26 | } 27 | var 28 | doc = view.document 29 | // only get URL when necessary in case Blob.js hasn't overridden it yet 30 | , get_URL = function() { 31 | return view.URL || view.webkitURL || view; 32 | } 33 | , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 34 | , can_use_save_link = !view.externalHost && "download" in save_link 35 | , click = function(node) { 36 | var event = doc.createEvent("MouseEvents"); 37 | event.initMouseEvent( 38 | "click", true, false, view, 0, 0, 0, 0, 0 39 | , false, false, false, false, 0, null 40 | ); 41 | node.dispatchEvent(event); 42 | } 43 | , webkit_req_fs = view.webkitRequestFileSystem 44 | , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem 45 | , throw_outside = function(ex) { 46 | (view.setImmediate || view.setTimeout)(function() { 47 | throw ex; 48 | }, 0); 49 | } 50 | , force_saveable_type = "application/octet-stream" 51 | , fs_min_size = 0 52 | , deletion_queue = [] 53 | , process_deletion_queue = function() { 54 | var i = deletion_queue.length; 55 | while (i--) { 56 | var file = deletion_queue[i]; 57 | if (typeof file === "string") { // file is an object URL 58 | get_URL().revokeObjectURL(file); 59 | } else { // file is a File 60 | file.remove(); 61 | } 62 | } 63 | deletion_queue.length = 0; // clear queue 64 | } 65 | , dispatch = function(filesaver, event_types, event) { 66 | event_types = [].concat(event_types); 67 | var i = event_types.length; 68 | while (i--) { 69 | var listener = filesaver["on" + event_types[i]]; 70 | if (typeof listener === "function") { 71 | try { 72 | listener.call(filesaver, event || filesaver); 73 | } catch (ex) { 74 | throw_outside(ex); 75 | } 76 | } 77 | } 78 | } 79 | , FileSaver = function(blob, name) { 80 | // First try a.download, then web filesystem, then object URLs 81 | var 82 | filesaver = this 83 | , type = blob.type 84 | , blob_changed = false 85 | , object_url 86 | , target_view 87 | , get_object_url = function() { 88 | var object_url = get_URL().createObjectURL(blob); 89 | deletion_queue.push(object_url); 90 | return object_url; 91 | } 92 | , dispatch_all = function() { 93 | dispatch(filesaver, "writestart progress write writeend".split(" ")); 94 | } 95 | // on any filesys errors revert to saving with object URLs 96 | , fs_error = function() { 97 | // don't create more object URLs than needed 98 | if (blob_changed || !object_url) { 99 | object_url = get_object_url(blob); 100 | } 101 | if (target_view) { 102 | target_view.location.href = object_url; 103 | } else { 104 | window.open(object_url, "_blank"); 105 | } 106 | filesaver.readyState = filesaver.DONE; 107 | dispatch_all(); 108 | } 109 | , abortable = function(func) { 110 | return function() { 111 | if (filesaver.readyState !== filesaver.DONE) { 112 | return func.apply(this, arguments); 113 | } 114 | }; 115 | } 116 | , create_if_not_found = {create: true, exclusive: false} 117 | , slice 118 | ; 119 | filesaver.readyState = filesaver.INIT; 120 | if (!name) { 121 | name = "download"; 122 | } 123 | if (can_use_save_link) { 124 | object_url = get_object_url(blob); 125 | save_link.href = object_url; 126 | save_link.download = name; 127 | click(save_link); 128 | filesaver.readyState = filesaver.DONE; 129 | dispatch_all(); 130 | return; 131 | } 132 | // Object and web filesystem URLs have a problem saving in Google Chrome when 133 | // viewed in a tab, so I force save with application/octet-stream 134 | // http://code.google.com/p/chromium/issues/detail?id=91158 135 | if (view.chrome && type && type !== force_saveable_type) { 136 | slice = blob.slice || blob.webkitSlice; 137 | blob = slice.call(blob, 0, blob.size, force_saveable_type); 138 | blob_changed = true; 139 | } 140 | // Since I can't be sure that the guessed media type will trigger a download 141 | // in WebKit, I append .download to the filename. 142 | // https://bugs.webkit.org/show_bug.cgi?id=65440 143 | if (webkit_req_fs && name !== "download") { 144 | name += ".download"; 145 | } 146 | if (type === force_saveable_type || webkit_req_fs) { 147 | target_view = view; 148 | } 149 | if (!req_fs) { 150 | fs_error(); 151 | return; 152 | } 153 | fs_min_size += blob.size; 154 | req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { 155 | fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { 156 | var save = function() { 157 | dir.getFile(name, create_if_not_found, abortable(function(file) { 158 | file.createWriter(abortable(function(writer) { 159 | writer.onwriteend = function(event) { 160 | target_view.location.href = file.toURL(); 161 | deletion_queue.push(file); 162 | filesaver.readyState = filesaver.DONE; 163 | dispatch(filesaver, "writeend", event); 164 | }; 165 | writer.onerror = function() { 166 | var error = writer.error; 167 | if (error.code !== error.ABORT_ERR) { 168 | fs_error(); 169 | } 170 | }; 171 | "writestart progress write abort".split(" ").forEach(function(event) { 172 | writer["on" + event] = filesaver["on" + event]; 173 | }); 174 | writer.write(blob); 175 | filesaver.abort = function() { 176 | writer.abort(); 177 | filesaver.readyState = filesaver.DONE; 178 | }; 179 | filesaver.readyState = filesaver.WRITING; 180 | }), fs_error); 181 | }), fs_error); 182 | }; 183 | dir.getFile(name, {create: false}, abortable(function(file) { 184 | // delete file if it already exists 185 | file.remove(); 186 | save(); 187 | }), abortable(function(ex) { 188 | if (ex.code === ex.NOT_FOUND_ERR) { 189 | save(); 190 | } else { 191 | fs_error(); 192 | } 193 | })); 194 | }), fs_error); 195 | }), fs_error); 196 | } 197 | , FS_proto = FileSaver.prototype 198 | , saveAs = function(blob, name) { 199 | return new FileSaver(blob, name); 200 | } 201 | ; 202 | FS_proto.abort = function() { 203 | var filesaver = this; 204 | filesaver.readyState = filesaver.DONE; 205 | dispatch(filesaver, "abort"); 206 | }; 207 | FS_proto.readyState = FS_proto.INIT = 0; 208 | FS_proto.WRITING = 1; 209 | FS_proto.DONE = 2; 210 | 211 | FS_proto.error = 212 | FS_proto.onwritestart = 213 | FS_proto.onprogress = 214 | FS_proto.onwrite = 215 | FS_proto.onabort = 216 | FS_proto.onerror = 217 | FS_proto.onwriteend = 218 | null; 219 | 220 | view.addEventListener("unload", process_deletion_queue, false); 221 | saveAs.unload = function() { 222 | process_deletion_queue(); 223 | view.removeEventListener("unload", process_deletion_queue, false); 224 | }; 225 | return saveAs; 226 | }( 227 | typeof self !== "undefined" && self 228 | || typeof window !== "undefined" && window 229 | || this.content 230 | )); 231 | // `self` is undefined in Firefox for Android content script context 232 | // while `this` is nsIContentFrameMessageManager 233 | // with an attribute `content` that corresponds to the window 234 | 235 | if (typeof module !== "undefined" && module !== null) { 236 | module.exports = saveAs; 237 | } else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) { 238 | define([], function() { 239 | return saveAs; 240 | }); 241 | } 242 | 243 | jQuery.fn.table2CSV = function(options) { 244 | var options = jQuery.extend({ 245 | separator: ',', 246 | header: [], 247 | delivery: 'download' // popup, value, download 248 | }, 249 | options); 250 | 251 | var csvData = []; 252 | var headerArr = []; 253 | var el = this; 254 | 255 | //header 256 | var numCols = options.header.length; 257 | var tmpRow = []; // construct header avalible array 258 | 259 | if (numCols > 0) { 260 | for (var i = 0; i < numCols; i++) { 261 | tmpRow[tmpRow.length] = formatData(options.header[i]); 262 | } 263 | } else { 264 | $(el).filter(':visible').find('th').each(function() { 265 | if ($(this).css('display') != 'none') tmpRow[tmpRow.length] = formatData($(this).html()); 266 | }); 267 | } 268 | 269 | row2CSV(tmpRow); 270 | 271 | // actual data 272 | $(el).find('tr').each(function() { 273 | var tmpRow = []; 274 | $(this).filter(':visible').find('td').each(function() { 275 | if ($(this).css('display') != 'none') tmpRow[tmpRow.length] = formatData($(this).html()); 276 | }); 277 | row2CSV(tmpRow); 278 | }); 279 | if (options.delivery == 'popup') { 280 | var mydata = csvData.join('\n'); 281 | return popup(mydata); 282 | } 283 | else if (options.delivery == 'download') { 284 | var mydata = csvData.join('\n'); 285 | return download(mydata); 286 | } 287 | else { 288 | var mydata = csvData.join('\n'); 289 | return mydata; 290 | } 291 | 292 | function row2CSV(tmpRow) { 293 | var tmp = tmpRow.join('') // to remove any blank rows 294 | if (tmpRow.length > 0 && tmp != '') { 295 | var mystr = tmpRow.join(options.separator); 296 | csvData.push(mystr); 297 | } 298 | } 299 | function formatData(input) { 300 | // replace " with “ 301 | var regexp = new RegExp(/["]/g); 302 | var output = input.replace(regexp, "“"); 303 | //HTML 304 | var regexp = new RegExp(/\<[^\<]+\>/g); 305 | var output = output.replace(regexp, ""); 306 | output = output.trim(); 307 | if (output == "") return ''; 308 | return '"' + output + '"'; 309 | } 310 | function popup(data) { 311 | var generator = window.open('', 'csv', 'height=400,width=600'); 312 | generator.document.write('CSV'); 313 | generator.document.write(''); 314 | generator.document.write(''); 317 | generator.document.write(''); 318 | generator.document.close(); 319 | return true; 320 | } 321 | function download(data) { 322 | var blob = new Blob([data], { 323 | type: "text/plain;charset=utf-8;", 324 | }); 325 | saveAs(blob, "DuolingoNotes.csv"); 326 | } 327 | }; 328 | -------------------------------------------------------------------------------- /css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.1.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-color:#357ebd}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} -------------------------------------------------------------------------------- /js/underscore-min.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.6.0 2 | // http://underscorejs.org 3 | // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 | // Underscore may be freely distributed under the MIT license. 5 | (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?void(this._wrapped=n):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.6.0";var A=j.each=j.forEach=function(n,t,e){if(null==n)return n;if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return;return n};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var O="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},j.find=j.detect=function(n,t,r){var e;return k(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var k=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:k(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,j.property(t))},j.where=function(n,t){return j.filter(n,j.matches(t))},j.findWhere=function(n,t){return j.find(n,j.matches(t))},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);var e=-1/0,u=-1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;o>u&&(e=n,u=o)}),e},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);var e=1/0,u=1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;u>o&&(e=n,u=o)}),e},j.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=j.random(r++),e[r-1]=e[t],e[t]=n}),e},j.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=j.values(n)),n[j.random(n.length-1)]):j.shuffle(n).slice(0,Math.max(0,t))};var E=function(n){return null==n?j.identity:j.isFunction(n)?n:j.property(n)};j.sortBy=function(n,t,r){return t=E(t),j.pluck(j.map(n,function(n,e,u){return{value:n,index:e,criteria:t.call(r,n,e,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=E(r),A(t,function(i,a){var o=r.call(e,i,a,t);n(u,o,i)}),u}};j.groupBy=F(function(n,t,r){j.has(n,t)?n[t].push(r):n[t]=[r]}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=E(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])t?[]:o.call(n,0,t)},j.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},j.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},j.rest=j.tail=j.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},j.compact=function(n){return j.filter(n,j.identity)};var M=function(n,t,r){return t&&j.every(n,j.isArray)?c.apply(r,n):(A(n,function(n){j.isArray(n)||j.isArguments(n)?t?a.apply(r,n):M(n,t,r):r.push(n)}),r)};j.flatten=function(n,t){return M(n,t,[])},j.without=function(n){return j.difference(n,o.call(arguments,1))},j.partition=function(n,t){var r=[],e=[];return A(n,function(n){(t(n)?r:e).push(n)}),[r,e]},j.uniq=j.unique=function(n,t,r,e){j.isFunction(t)&&(e=r,r=t,t=!1);var u=r?j.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:j.contains(a,r))||(a.push(r),i.push(n[e]))}),i},j.union=function(){return j.uniq(j.flatten(arguments,!0))},j.intersection=function(n){var t=o.call(arguments,1);return j.filter(j.uniq(n),function(n){return j.every(t,function(t){return j.contains(t,n)})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===j&&(e[u]=arguments[r++]);for(;r=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u),e=u=null):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o,c=function(){var l=j.now()-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u),i=u=null))};return function(){i=this,u=arguments,a=j.now();var l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u),i=u=null),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return j.partial(t,n)},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=function(n){if(!j.isObject(n))return[];if(w)return w(n);var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o)&&"constructor"in n&&"constructor"in t)return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.constant=function(n){return function(){return n}},j.property=function(n){return function(t){return t[n]}},j.matches=function(n){return function(t){if(t===n)return!0;for(var r in n)if(n[r]!==t[r])return!1;return!0}},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},j.now=Date.now||function(){return(new Date).getTime()};var T={escape:{"&":"&","<":"<",">":">",'"':""","'":"'"}};T.unescape=j.invert(T.escape);var I={escape:new RegExp("["+j.keys(T.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(T.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(I[n],function(t){return T[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}}),"function"==typeof define&&define.amd&&define("underscore",[],function(){return j})}).call(this); 6 | //# sourceMappingURL=underscore-min.map -------------------------------------------------------------------------------- /css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.0.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857142858em;text-align:center}.fa-ul{padding-left:0;margin-left:2.142857142857143em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;top:.14285714285714285em;text-align:center}.fa-li.fa-lg{left:-1.8571428571428572em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1);-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);-o-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1);-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"} -------------------------------------------------------------------------------- /js/guessLanguage.js: -------------------------------------------------------------------------------- 1 | /* Guess the natural language of a text 2 | * Copyright (c) 2012, Rich Tibbett 3 | * http://github.com/richtr/guessLanguage.js/ 4 | * 5 | * Original Python package: 6 | * Copyright (c) 2008, Kent S Johnson 7 | * http://code.google.com/p/guess-language/ 8 | * 9 | * Original C++ version for KDE: 10 | * Copyright (c) 2006 Jacob R Rideout 11 | * http://websvn.kde.org/branches/work/sonnet-refactoring/common/nlp/guesslanguage.cpp?view=markup 12 | * 13 | * Original Language::Guess Perl module: 14 | * Copyright (c) 2004-2006 Maciej Ceglowski 15 | * http://web.archive.org/web/20090228163219/http://languid.cantbedone.org/ 16 | * 17 | * Note: Language::Guess is GPL-licensed. KDE developers received permission 18 | * from the author to distribute their port under LGPL: 19 | * http://lists.kde.org/?l=kde-sonnet&m=116910092228811&w=2 20 | * 21 | * This program is free software: you can redistribute it and/or modify it 22 | * under the terms of the GNU Lesser General Public License as published 23 | * by the Free Software Foundation, either version 3 of the License, 24 | * or (at your option) any later version. 25 | * 26 | * This program is distributed in the hope that it will be useful, 27 | * but WITHOUT ANY WARRANTY; without even the implied warranty 28 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 29 | * See the GNU Lesser General Public License for more details. 30 | * 31 | * You should have received a copy of the GNU Lesser General Public License 32 | * along with this program. If not, see . 33 | */ 34 | 35 | (function(global, undefined) { 36 | 37 | var guessLanguage = function() { 38 | 39 | var models = global._languageData || {}; 40 | 41 | if (typeof module === "object" && module.exports === global) { 42 | models = require('./_languageData') || {}; 43 | } 44 | 45 | var MAX_LENGTH = 4096; 46 | var MIN_LENGTH = 20; 47 | var MAX_GRAMS = 300; 48 | 49 | var NAME_MAP = { 50 | "ab": "Abkhazian", 51 | "af": "Afrikaans", 52 | "ar": "Arabic", 53 | "az": "Azeri", 54 | "be": "Belarusian", 55 | "bg": "Bulgarian", 56 | "bn": "Bengali", 57 | "bo": "Tibetan", 58 | "br": "Breton", 59 | "ca": "Catalan", 60 | "ceb": "Cebuano", 61 | "cs": "Czech", 62 | "cy": "Welsh", 63 | "da": "Danish", 64 | "de": "German", 65 | "el": "Greek", 66 | "en": "English", 67 | "eo": "Esperanto", 68 | "es": "Spanish", 69 | "et": "Estonian", 70 | "eu": "Basque", 71 | "fa": "Farsi", 72 | "fi": "Finnish", 73 | "fo": "Faroese", 74 | "fr": "French", 75 | "fy": "Frisian", 76 | "gd": "Scots Gaelic", 77 | "gl": "Galician", 78 | "gu": "Gujarati", 79 | "ha": "Hausa", 80 | "haw": "Hawaiian", 81 | "he": "Hebrew", 82 | "hi": "Hindi", 83 | "hr": "Croatian", 84 | "hu": "Hungarian", 85 | "hy": "Armenian", 86 | "id": "Indonesian", 87 | "is": "Icelandic", 88 | "it": "Italian", 89 | "ja": "Japanese", 90 | "ka": "Georgian", 91 | "kk": "Kazakh", 92 | "km": "Cambodian", 93 | "ko": "Korean", 94 | "ku": "Kurdish", 95 | "ky": "Kyrgyz", 96 | "la": "Latin", 97 | "lt": "Lithuanian", 98 | "lv": "Latvian", 99 | "mg": "Malagasy", 100 | "mk": "Macedonian", 101 | "ml": "Malayalam", 102 | "mn": "Mongolian", 103 | "mr": "Marathi", 104 | "ms": "Malay", 105 | "nd": "Ndebele", 106 | "ne": "Nepali", 107 | "nl": "Dutch", 108 | "nn": "Nynorsk", 109 | "no": "Norwegian", 110 | "nso": "Sepedi", 111 | "pa": "Punjabi", 112 | "pl": "Polish", 113 | "ps": "Pashto", 114 | "pt": "Portuguese", 115 | "pt_PT": "Portuguese (Portugal)", 116 | "pt_BR": "Portuguese (Brazil)", 117 | "ro": "Romanian", 118 | "ru": "Russian", 119 | "sa": "Sanskrit", 120 | "sh": "Serbo-Croatian", 121 | "sk": "Slovak", 122 | "sl": "Slovene", 123 | "so": "Somali", 124 | "sq": "Albanian", 125 | "sr": "Serbian", 126 | "sv": "Swedish", 127 | "sw": "Swahili", 128 | "ta": "Tamil", 129 | "te": "Telugu", 130 | "th": "Thai", 131 | "tl": "Tagalog", 132 | "tlh": "Klingon", 133 | "tn": "Setswana", 134 | "tr": "Turkish", 135 | "ts": "Tsonga", 136 | "tw": "Twi", 137 | "uk": "Ukrainian", 138 | "ur": "Urdu", 139 | "uz": "Uzbek", 140 | "ve": "Venda", 141 | "vi": "Vietnamese", 142 | "xh": "Xhosa", 143 | "zh": "Chinese", 144 | "zh_TW": "Traditional Chinese (Taiwan)", 145 | "zu": "Zulu", 146 | }; 147 | 148 | var IANA_MAP = { 149 | "ab": 12026, 150 | "af": 40, 151 | "ar": 26020, 152 | "az": 26030, 153 | "be": 11890, 154 | "bg": 26050, 155 | "bn": 26040, 156 | "bo": 26601, 157 | "br": 1361, 158 | "ca": 3, 159 | "ceb": 26060, 160 | "cs": 26080, 161 | "cy": 26560, 162 | "da": 26090, 163 | "de": 26160, 164 | "el": 26165, 165 | "en": 26110, 166 | "eo": 11933, 167 | "es": 26460, 168 | "et": 26120, 169 | "eu": 1232, 170 | "fa": 26130, 171 | "fi": 26140, 172 | "fo": 11817, 173 | "fr": 26150, 174 | "fy": 1353, 175 | "gd": 65555, 176 | "gl": 1252, 177 | "gu": 26599, 178 | "ha": 26170, 179 | "haw": 26180, 180 | "he": 26592, 181 | "hi": 26190, 182 | "hr": 26070, 183 | "hu": 26200, 184 | "hy": 26597, 185 | "id": 26220, 186 | "is": 26210, 187 | "it": 26230, 188 | "ja": 26235, 189 | "ka": 26600, 190 | "kk": 26240, 191 | "km": 1222, 192 | "ko": 26255, 193 | "ku": 11815, 194 | "ky": 26260, 195 | "la": 26280, 196 | "lt": 26300, 197 | "lv": 26290, 198 | "mg": 1362, 199 | "mk": 26310, 200 | "ml": 26598, 201 | "mn": 26320, 202 | "mr": 1201, 203 | "ms": 1147, 204 | "ne": 26330, 205 | "nl": 26100, 206 | "nn": 172, 207 | "no": 26340, 208 | "pa": 65550, 209 | "pl": 26380, 210 | "ps": 26350, 211 | "pt": 26390, 212 | "ro": 26400, 213 | "ru": 26410, 214 | "sa": 1500, 215 | "sh": 1399, 216 | "sk": 26430, 217 | "sl": 26440, 218 | "so": 26450, 219 | "sq": 26010, 220 | "sr": 26420, 221 | "sv": 26480, 222 | "sw": 26470, 223 | "ta": 26595, 224 | "te": 26596, 225 | "th": 26594, 226 | "tl": 26490, 227 | "tlh": 26250, 228 | "tn": 65578, 229 | "tr": 26500, 230 | "tw": 1499, 231 | "uk": 26520, 232 | "ur": 26530, 233 | "uz": 26540, 234 | "vi": 26550, 235 | "zh": 26065, 236 | "zh_TW": 22, 237 | }; 238 | 239 | var SINGLETONS = [ 240 | ["Armenian", "hy"], 241 | ["Hebrew", "he"], 242 | ["Bengali", "bn"], 243 | ["Gurmukhi", "pa"], 244 | ["Greek", "el"], 245 | ["Gujarati", "gu"], 246 | ["Oriya", "or"], 247 | ["Tamil", "ta"], 248 | ["Telugu", "te"], 249 | ["Kannada", "kn"], 250 | ["Malayalam", "ml"], 251 | ["Sinhala", "si"], 252 | ["Thai", "th"], 253 | ["Lao", "lo"], 254 | ["Tibetan", "bo"], 255 | ["Burmese", "my"], 256 | ["Georgian", "ka"], 257 | ["Mongolian", "mn"], 258 | ["Khmer", "km"] 259 | ]; 260 | 261 | var UNKNOWN = 'unknown'; 262 | 263 | var BASIC_LATIN = ["en", "ceb", "ha", "so", "tlh", "id", "haw", "la", "sw", "eu", "nr", "nso", "zu", "xh", "ss", "st", "tn", "ts"]; 264 | var EXTENDED_LATIN = ["cs", "af", "pl", "hr", "ro", "sk", "sl", "tr", "hu", "az", "et", "sq", "ca", "es", "fr", "de", "nl", "it", "da", "is", "no", "sv", "fi", "lv", "pt", "ve", "lt", "tl", "cy", "vi"]; 265 | var ALL_LATIN = BASIC_LATIN.concat(EXTENDED_LATIN); 266 | var CYRILLIC = ["ru", "uk", "kk", "uz", "mn", "sr", "mk", "bg", "ky"]; 267 | var ARABIC = ["ar", "fa", "ps", "ur"]; 268 | var DEVANAGARI = ["hi", "ne"]; 269 | var PT = ["pt_BR", "pt_PT"]; 270 | 271 | // Unicode char greedy regex block range matchers 272 | var unicodeBlockTests = { 273 | "Basic Latin": /[\u0000-\u007F]/g, 274 | "Latin-1 Supplement": /[\u0080-\u00FF]/g, 275 | "Latin Extended-A": /[\u0100-\u017F]/g, 276 | "Latin Extended-B": /[\u0180-\u024F]/g, 277 | "IPA Extensions": /[\u0250-\u02AF]/g, 278 | "Spacing Modifier Letters": /[\u02B0-\u02FF]/g, 279 | "Combining Diacritical Marks": /[\u0300-\u036F]/g, 280 | "Greek and Coptic": /[\u0370-\u03FF]/g, 281 | "Cyrillic": /[\u0400-\u04FF]/g, 282 | "Cyrillic Supplement": /[\u0500-\u052F]/g, 283 | "Armenian": /[\u0530-\u058F]/g, 284 | "Hebrew": /[\u0590-\u05FF]/g, 285 | "Arabic": /[\u0600-\u06FF]/g, 286 | "Syriac": /[\u0700-\u074F]/g, 287 | "Arabic Supplement": /[\u0750-\u077F]/g, 288 | "Thaana": /[\u0780-\u07BF]/g, 289 | "NKo": /[\u07C0-\u07FF]/g, 290 | "Devanagari": /[\u0900-\u097F]/g, 291 | "Bengali": /[\u0980-\u09FF]/g, 292 | "Gurmukhi": /[\u0A00-\u0A7F]/g, 293 | "Gujarati": /[\u0A80-\u0AFF]/g, 294 | "Oriya": /[\u0B00-\u0B7F]/g, 295 | "Tamil": /[\u0B80-\u0BFF]/g, 296 | "Telugu": /[\u0C00-\u0C7F]/g, 297 | "Kannada": /[\u0C80-\u0CFF]/g, 298 | "Malayalam": /[\u0D00-\u0D7F]/g, 299 | "Sinhala": /[\u0D80-\u0DFF]/g, 300 | "Thai": /[\u0E00-\u0E7F]/g, 301 | "Lao": /[\u0E80-\u0EFF]/g, 302 | "Tibetan": /[\u0F00-\u0FFF]/g, 303 | "Myanmar": /[\u1000-\u109F]/g, 304 | "Georgian": /[\u10A0-\u10FF]/g, 305 | "Hangul Jamo": /[\u1100-\u11FF]/g, 306 | "Ethiopic": /[\u1200-\u137F]/g, 307 | "Ethiopic Supplement": /[\u1380-\u139F]/g, 308 | "Cherokee": /[\u13A0-\u13FF]/g, 309 | "Unified Canadian Aboriginal Syllabics": /[\u1400-\u167F]/g, 310 | "Ogham": /[\u1680-\u169F]/g, 311 | "Runic": /[\u16A0-\u16FF]/g, 312 | "Tagalog": /[\u1700-\u171F]/g, 313 | "Hanunoo": /[\u1720-\u173F]/g, 314 | "Buhid": /[\u1740-\u175F]/g, 315 | "Tagbanwa": /[\u1760-\u177F]/g, 316 | "Khmer": /[\u1780-\u17FF]/g, 317 | "Mongolian": /[\u1800-\u18AF]/g, 318 | "Limbu": /[\u1900-\u194F]/g, 319 | "Tai Le": /[\u1950-\u197F]/g, 320 | "New Tai Lue": /[\u1980-\u19DF]/g, 321 | "Khmer Symbols": /[\u19E0-\u19FF]/g, 322 | "Buginese": /[\u1A00-\u1A1F]/g, 323 | "Balinese": /[\u1B00-\u1B7F]/g, 324 | "Phonetic Extensions": /[\u1D00-\u1D7F]/g, 325 | "Phonetic Extensions Supplement": /[\u1D80-\u1DBF]/g, 326 | "Combining Diacritical Marks Supplement": /[\u1DC0-\u1DFF]/g, 327 | "Latin Extended Additional": /[\u1E00-\u1EFF]/g, 328 | "Greek Extended": /[\u1F00-\u1FFF]/g, 329 | "General Punctuation": /[\u2000-\u206F]/g, 330 | "Superscripts and Subscripts": /[\u2070-\u209F]/g, 331 | "Currency Symbols": /[\u20A0-\u20CF]/g, 332 | "Combining Diacritical Marks for Symbols": /[\u20D0-\u20FF]/g, 333 | "Letterlike Symbols": /[\u2100-\u214F]/g, 334 | "Number Forms": /[\u2150-\u218F]/g, 335 | "Arrows": /[\u2190-\u21FF]/g, 336 | "Mathematical Operators": /[\u2200-\u22FF]/g, 337 | "Miscellaneous Technical": /[\u2300-\u23FF]/g, 338 | "Control Pictures": /[\u2400-\u243F]/g, 339 | "Optical Character Recognition": /[\u2440-\u245F]/g, 340 | "Enclosed Alphanumerics": /[\u2460-\u24FF]/g, 341 | "Box Drawing": /[\u2500-\u257F]/g, 342 | "Block Elements": /[\u2580-\u259F]/g, 343 | "Geometric Shapes": /[\u25A0-\u25FF]/g, 344 | "Miscellaneous Symbols": /[\u2600-\u26FF]/g, 345 | "Dingbats": /[\u2700-\u27BF]/g, 346 | "Miscellaneous Mathematical Symbols-A": /[\u27C0-\u27EF]/g, 347 | "Supplemental Arrows-A": /[\u27F0-\u27FF]/g, 348 | "Braille Patterns": /[\u2800-\u28FF]/g, 349 | "Supplemental Arrows-B": /[\u2900-\u297F]/g, 350 | "Miscellaneous Mathematical Symbols-B": /[\u2980-\u29FF]/g, 351 | "Supplemental Mathematical Operators": /[\u2A00-\u2AFF]/g, 352 | "Miscellaneous Symbols and Arrows": /[\u2B00-\u2BFF]/g, 353 | "Glagolitic": /[\u2C00-\u2C5F]/g, 354 | "Latin Extended-C": /[\u2C60-\u2C7F]/g, 355 | "Coptic": /[\u2C80-\u2CFF]/g, 356 | "Georgian Supplement": /[\u2D00-\u2D2F]/g, 357 | "Tifinagh": /[\u2D30-\u2D7F]/g, 358 | "Ethiopic Extended": /[\u2D80-\u2DDF]/g, 359 | "Supplemental Punctuation": /[\u2E00-\u2E7F]/g, 360 | "CJK Radicals Supplement": /[\u2E80-\u2EFF]/g, 361 | "KangXi Radicals": /[\u2F00-\u2FDF]/g, 362 | "Ideographic Description Characters": /[\u2FF0-\u2FFF]/g, 363 | "CJK Symbols and Punctuation": /[\u3000-\u303F]/g, 364 | "Hiragana": /[\u3040-\u309F]/g, 365 | "Katakana": /[\u30A0-\u30FF]/g, 366 | "Bopomofo": /[\u3100-\u312F]/g, 367 | "Hangul Compatibility Jamo": /[\u3130-\u318F]/g, 368 | "Kanbun": /[\u3190-\u319F]/g, 369 | "Bopomofo Extended": /[\u31A0-\u31BF]/g, 370 | "CJK Strokes": /[\u31C0-\u31EF]/g, 371 | "Katakana Phonetic Extensions": /[\u31F0-\u31FF]/g, 372 | "Enclosed CJK Letters and Months": /[\u3200-\u32FF]/g, 373 | "CJK Compatibility": /[\u3300-\u33FF]/g, 374 | "CJK Unified Ideographs Extension A": /[\u3400-\u4DBF]/g, 375 | "Yijing Hexagram Symbols": /[\u4DC0-\u4DFF]/g, 376 | "CJK Unified Ideographs": /[\u4E00-\u9FFF]/g, 377 | "Yi Syllables": /[\uA000-\uA48F]/g, 378 | "Yi Radicals": /[\uA490-\uA4CF]/g, 379 | "Modifier Tone Letters": /[\uA700-\uA71F]/g, 380 | "Latin Extended-D": /[\uA720-\uA7FF]/g, 381 | "Syloti Nagri": /[\uA800-\uA82F]/g, 382 | "Phags-pa": /[\uA840-\uA87F]/g, 383 | "Hangul Syllables": /[\uAC00-\uD7AF]/g, 384 | "High Surrogates": /[\uD800-\uDB7F]/g, 385 | "High Private Use Surrogates": /[\uDB80-\uDBFF]/g, 386 | "Low Surrogates": /[\uDC00-\uDFFF]/g, 387 | "Private Use Area": /[\uE000-\uF8FF]/g, 388 | "CJK Compatibility Ideographs": /[\uF900-\uFAFF]/g, 389 | "Alphabetic Presentation Forms": /[\uFB00-\uFB4F]/g, 390 | "Arabic Presentation Forms-A": /[\uFB50-\uFDFF]/g, 391 | "Variation Selectors": /[\uFE00-\uFE0F]/g, 392 | "Vertical Forms": /[\uFE10-\uFE1F]/g, 393 | "Combining Half Marks": /[\uFE20-\uFE2F]/g, 394 | "CJK Compatibility Forms": /[\uFE30-\uFE4F]/g, 395 | "Small Form Variants": /[\uFE50-\uFE6F]/g, 396 | "Arabic Presentation Forms-B": /[\uFE70-\uFEFF]/g, 397 | "Halfwidth and Fullwidth Forms": /[\uFF00-\uFFEF]/g, 398 | "Specials": /[\uFFF0-\uFFFF]/g/*, 399 | "Linear B Syllabary": /[\u10000-\u1007F]/g, 400 | "Linear B Ideograms": /[\u10080-\u100FF]/g, 401 | "Aegean Numbers": /[\u10100-\u1013F]/g, 402 | "Ancient Greek Numbers": /[\u10140-\u1018F]/g, 403 | "Old Italic": /[\u10300-\u1032F]/g, 404 | "Gothic": /[\u10330-\u1034F]/g, 405 | "Ugaritic": /[\u10380-\u1039F]/g, 406 | "Old Persian": /[\u103A0-\u103DF]/g, 407 | "Deseret": /[\u10400-\u1044F]/g, 408 | "Shavian": /[\u10450-\u1047F]/g, 409 | "Osmanya": /[\u10480-\u104AF]/g, 410 | "Cypriot Syllabary": /[\u10800-\u1083F]/g, 411 | "Phoenician": /[\u10900-\u1091F]/g, 412 | "Kharoshthi": /[\u10A00-\u10A5F]/g, 413 | "Cuneiform": /[\u12000-\u123FF]/g, 414 | "Cuneiform Numbers and Punctuation": /[\u12400-\u1247F]/g, 415 | "Byzantine Musical Symbols": /[\u1D000-\u1D0FF]/g, 416 | "Musical Symbols": /[\u1D100-\u1D1FF]/g, 417 | "Ancient Greek Musical Notation": /[\u1D200-\u1D24F]/g, 418 | "Tai Xuan Jing Symbols": /[\u1D300-\u1D35F]/g, 419 | "Counting Rod Numerals": /[\u1D360-\u1D37F]/g, 420 | "Mathematical Alphanumeric Symbols": /[\u1D400-\u1D7FF]/g, 421 | "CJK Unified Ideographs Extension B": /[\u20000-\u2A6DF]/g, 422 | "CJK Compatibility Ideographs Supplement": /[\u2F800-\u2FA1F]/g, 423 | "Tags": /[\uE0000-\uE007F]/g, 424 | "Variation Selectors Supplement": /[\uE0100-\uE01EF]/g, 425 | "Supplementary Private Use Area-A": /[\uF0000-\uFFFFF]/g, 426 | "Supplementary Private Use Area-B": /[\u100000-\u10FFFF]/g*/ 427 | }; 428 | 429 | function findRuns(text) { 430 | 431 | var relevant_runs = {}; 432 | 433 | for (var key in unicodeBlockTests) { 434 | 435 | // Count the number of characters in each character block. 436 | var charCount = text.match(unicodeBlockTests[key]); 437 | 438 | // return run types that used for 40% or more of the string 439 | // always return basic latin if found more than 15% 440 | // and extended additional latin if over 10% (for Vietnamese) 441 | var pct = (charCount ? charCount.length : 0) / text.length; 442 | 443 | relevant_runs[key] = pct; 444 | 445 | } 446 | 447 | return relevant_runs; 448 | } 449 | 450 | function identify(text, callback) { 451 | 452 | var scripts = findRuns(text); 453 | 454 | // Identify the language. 455 | if (scripts["Hangul Syllables"] + scripts["Hangul Jamo"] + scripts["Hangul Compatibility Jamo"] >= 0.4) { 456 | callback.apply(undefined, ["ko"]); 457 | return; 458 | } 459 | 460 | if (scripts["Greek and Coptic"] >= 0.4) { 461 | callback.apply(undefined, ["el"]); 462 | return; 463 | } 464 | 465 | if (scripts["Hiragana"] + scripts["Katakana"] + scripts["Katakana Phonetic Extensions"] >= 0.2) { 466 | callback.apply(undefined, ["ja"]); 467 | return; 468 | } 469 | 470 | if (scripts["CJK Unified Ideographs"] + scripts["Bopomofo"] + scripts["Bopomofo Extended"] + scripts["KangXi Radicals"] >= 0.4) { 471 | callback.apply(undefined, ["zh"]); 472 | return; 473 | } 474 | 475 | if (scripts["Cyrillic"] >= 0.4) { 476 | check(text, CYRILLIC, callback); 477 | return; 478 | } 479 | 480 | if (scripts["Arabic"] + scripts["Arabic Presentation Forms-A"] + scripts["Arabic Presentation Forms-B"] >= 0.4) { 481 | check(text, ARABIC, callback); 482 | return; 483 | } 484 | 485 | if (scripts["Devanagari"] >= 0.4) { 486 | check(text, DEVANAGARI, callback); 487 | return; 488 | } 489 | 490 | // Try languages with unique scripts 491 | for (var i = 0, l = SINGLETONS.length; i < l; i++) { 492 | if (scripts[SINGLETONS[i][0]] >= 0.4) { 493 | callback.apply(undefined, [SINGLETONS[i][1]]); 494 | return; 495 | } 496 | } 497 | 498 | // Extended Latin 499 | if (scripts["Latin-1 Supplement"] + scripts["Latin Extended-A"] + scripts["IPA Extensions"] >= 0.4) { 500 | check(text, EXTENDED_LATIN, function(latin_lang) { 501 | if (latin_lang == "pt") { 502 | check(text, PT, callback); 503 | } else { 504 | callback.apply(undefined, [latin_lang]); 505 | } 506 | }); 507 | return; 508 | } 509 | 510 | if (scripts["Basic Latin"] >= 0.15) { 511 | check(text, ALL_LATIN, callback); 512 | return; 513 | } 514 | 515 | callback.apply(undefined, [UNKNOWN]); 516 | // return; 517 | } 518 | 519 | function check(sample, langs, callback) { 520 | 521 | if (sample.length < MIN_LENGTH) { 522 | callback.apply(undefined, [UNKNOWN]); 523 | return; 524 | } 525 | 526 | var scores = {}; 527 | var model = createOrderedModel(sample) 528 | for (var i = 0, l = langs.length; i < l; i++) { 529 | 530 | var lkey = langs[i].toLowerCase(); 531 | 532 | var known_model = models[lkey] || null; 533 | 534 | if (!known_model) { 535 | continue; 536 | } 537 | 538 | scores[lkey] = distance(model, known_model); 539 | 540 | } 541 | 542 | var scoresArr = []; 543 | for (var index in scores) { 544 | scoresArr.push([index, scores[index]]); 545 | } 546 | 547 | if (scoresArr.length == 0) { 548 | callback.apply(undefined, [UNKNOWN]); 549 | return; 550 | } 551 | 552 | // we want the lowest score, less distance = greater chance of match 553 | var sortedScores = scoresArr.sort(function(objA, objB) { 554 | return objA[1] - objB[1]; // sort low-to-high 555 | }); 556 | 557 | // return the best match we've now calculated 558 | callback.apply(undefined, [sortedScores[0][0]]); 559 | //return; 560 | } 561 | 562 | function createOrderedModel(content) { 563 | // Create a list of trigrams in content sorted by frequency. 564 | var trigrams = {}, 565 | sortedTrigrams = []; 566 | var content = content.toLowerCase(); 567 | 568 | var contentArr = content.split(""); 569 | for (var i = 0, l = contentArr.length - 2; i < l; i++) { 570 | var trigramKey = contentArr[i] + contentArr[i + 1] + contentArr[i + 2] + ""; 571 | if (!trigrams[trigramKey]) { 572 | trigrams[trigramKey] = 1; 573 | } else { 574 | trigrams[trigramKey] += 1; 575 | } 576 | } 577 | 578 | // convert object to array 579 | for (var i in trigrams) { 580 | sortedTrigrams[sortedTrigrams.length] = [i, trigrams[i]]; 581 | } 582 | 583 | // sort array results 584 | return sortedTrigrams.sort(function(objA, objB) { 585 | return objB[1] - objA[1]; // sort high-to-low 586 | }); 587 | } 588 | 589 | function distance(model, known_model) { 590 | // Calculate the distance to the known model. 591 | var dist = 0; 592 | 593 | for (var i = 0, l = model.length; i < l; i++) { 594 | 595 | if (known_model[model[i][0]]) { 596 | 597 | dist += Math.abs(model[i][1] - known_model[model[i][0]]); 598 | 599 | } else { 600 | 601 | dist += MAX_GRAMS; 602 | 603 | } 604 | 605 | } 606 | 607 | return dist; 608 | } 609 | 610 | return { 611 | detect: function(text, callback) { 612 | // Return the ISO 639-2 language identifier, i.e. 'en'. 613 | 614 | if (!text) { 615 | callback.apply(undefined, [UNKNOWN]); 616 | return; 617 | } 618 | 619 | text = text.substr(0, MAX_LENGTH).replace(/[\u0021-\u0040]/g, ''); 620 | 621 | identify(text, callback); 622 | 623 | }, 624 | info: function(text, callback) { 625 | // Return language info tuple (id, code, name), i.e. ('en', 26110, 'English'). 626 | 627 | this.detect(text, function(language) { 628 | 629 | if (language === UNKNOWN) { 630 | callback.apply(undefined, [[ UNKNOWN, UNKNOWN, UNKNOWN ]]); 631 | return; 632 | } 633 | 634 | callback.apply(undefined, [ 635 | 636 | [ language, IANA_MAP[language], NAME_MAP[language] ] 637 | 638 | ]);; 639 | 640 | }); 641 | 642 | }, 643 | code: function(text, callback) { 644 | // Return the language IANA code, i.e. 26110. 645 | 646 | this.detect(text, function(language) { 647 | 648 | if (language === UNKNOWN) { 649 | callback.apply(undefined, [ -1 ]);; 650 | return; 651 | } 652 | 653 | callback.apply(undefined, [ 654 | 655 | IANA_MAP[language] 656 | 657 | ]); 658 | 659 | }); 660 | 661 | }, 662 | name: function(text, callback) { 663 | // Return the full language name, i.e. 'English'. 664 | 665 | this.detect(text, function(language) { 666 | 667 | if (language === UNKNOWN) { 668 | callback.apply(undefined, [ UNKNOWN ]);; 669 | return; 670 | } 671 | 672 | callback.apply(undefined, [ 673 | 674 | NAME_MAP[language] 675 | 676 | ]); 677 | 678 | }); 679 | 680 | } 681 | }; 682 | 683 | }; 684 | 685 | global.guessLanguage = (global.module || {}).exports = new guessLanguage(); 686 | 687 | })(this); 688 | -------------------------------------------------------------------------------- /js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.1.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});return this.$element.trigger(j),j.isDefaultPrevented()?void 0:(this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this)};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);!e&&f.toggle&&"show"==c&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(b){a(d).remove(),a(e).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(a(c).is("body")?window:c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);{var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})}},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(b.RESET).addClass("affix");var a=this.$window.scrollTop(),c=this.$element.offset();return this.pinnedOffset=c.top-a},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"top"==this.affixed&&(e.top+=d),"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(b.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:c-h-this.$element.height()}))}}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.svg: -------------------------------------------------------------------------------- 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 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | --------------------------------------------------------------------------------