├── .gitignore ├── .npmrc ├── Dockerfile ├── LICENSE ├── NOTICE ├── README.md ├── config.fuseki.js ├── config.js ├── config.sparql.js ├── data ├── public │ ├── css │ │ ├── bootstrap.css │ │ └── custom.css │ ├── img │ │ ├── synapta-logo.svg │ │ └── zazuko-logo.png │ ├── js │ │ ├── bootstrap.js │ │ ├── jquery.js │ │ ├── jsonld.js │ │ ├── react.js │ │ ├── render-ld.js │ │ ├── search.js │ │ └── sticky.js │ ├── rdf2h │ │ ├── matchers.ttl │ │ └── site-matchers.ttl │ ├── search │ │ └── index.html │ └── sparql │ │ └── index.html ├── scripts │ ├── fuseki-config.ttl │ └── start-fuseki ├── sparql │ └── search.sparql └── templates │ ├── 404.html │ └── graph.html ├── index.js ├── lib ├── abstract-store.js ├── file-store.js ├── graph-split.js ├── handler-middleware.js ├── ldp-module-handler.js ├── patch-headers-middleware.js ├── render-html-middleware.js ├── sparql-handler.js ├── sparql-proxy.js └── sparql-search.js ├── logo.svg ├── package.json └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | npm-debug.log 4 | /nbproject/private/ -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="" 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6.7-onbuild 2 | EXPOSE 8080 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Trifid-LD - Lightweight Linked Data Server and Proxy 2 | Copyright 2015 Zazuko GmbH 3 | Author: Thomas Bergwinkl, Adrian Gschwend 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | With the release of version 1.0 Trifid-LD was renamed to Trifid. Please switch your Trifid-LD installations to Trifid, you can find the latest release in its new repository at 2 | https://github.com/zazuko/trifid 3 | -------------------------------------------------------------------------------- /config.fuseki.js: -------------------------------------------------------------------------------- 1 | /* global rdf:false */ 2 | 3 | 'use strict'; 4 | 5 | var 6 | fs = require('fs'), 7 | path = require('path'); 8 | 9 | 10 | var buildQuery = function (iri) { 11 | return 'DESCRIBE <' + iri + '>'; 12 | }; 13 | 14 | var buildExistsQuery = function (iri) { 15 | return 'ASK { <' + iri + '> ?p ?o }'; 16 | }; 17 | 18 | var patchResponseHeaders = function (res, headers) { 19 | if (res.statusCode === 200) { 20 | // clean existings values 21 | var fieldList = [ 22 | 'Access-Control-Allow-Origin', 23 | 'Cache-Control', 24 | 'Fuseki-Request-ID', 25 | 'Server', 26 | 'Vary']; 27 | 28 | if (res._headers) { 29 | fieldList.forEach(function (field) { 30 | if (field in res._headers) { 31 | delete res._headers[field]; 32 | } 33 | 34 | if (field.toLowerCase() in res._headers) { 35 | delete res._headers[field.toLowerCase()]; 36 | } 37 | }); 38 | } 39 | 40 | // cors header 41 | headers['Access-Control-Allow-Origin'] = '*'; 42 | 43 | // cache header 44 | headers['Cache-Control'] = 'public, max-age=120'; 45 | 46 | // vary header 47 | headers['Vary'] = 'Accept'; 48 | } 49 | 50 | return headers; 51 | }; 52 | 53 | module.exports = { 54 | app: 'trifid-ld', 55 | logger: { 56 | level: 'debug' 57 | }, 58 | listener: { 59 | port: 8080 60 | }, 61 | expressSettings: { 62 | 'trust proxy': 'loopback', 63 | 'x-powered-by': null 64 | }, 65 | patchHeaders: { 66 | patchResponse: patchResponseHeaders 67 | }, 68 | sparqlProxy: { 69 | path: '/sparql', 70 | options: { 71 | /*authentication: { 72 | user: 'user', 73 | password: 'password' 74 | },*/ 75 | endpointUrl:'http://localhost:3030/tbbt/sparql' 76 | } 77 | }, 78 | sparqlSearch: { 79 | path: '/query', 80 | options: { 81 | endpointUrl:'http://localhost:3030/tbbt/sparql', 82 | resultsPerPage: 5, 83 | queryTemplate: fs.readFileSync(path.join(__dirname, 'data/sparql/search.sparql')).toString(), 84 | variables: { 85 | 'q': { 86 | variable: '%searchstring%', 87 | required: true 88 | } 89 | } 90 | } 91 | }, 92 | HandlerClass: require('./lib/sparql-handler'), 93 | handlerOptions: { 94 | endpointUrl: 'http://localhost:3030/tbbt/sparql', 95 | buildQuery: buildQuery, 96 | buildExistsQuery: buildExistsQuery 97 | } 98 | }; 99 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | /* global rdf:false */ 2 | 3 | 'use strict'; 4 | 5 | 6 | global.rdf = require('rdf-ext')(rdf); 7 | 8 | 9 | var 10 | fs = require('fs'), 11 | graphSplit = require('./lib/graph-split')(rdf), 12 | url = require('url'); 13 | 14 | 15 | var init = function () { 16 | var config = this; 17 | 18 | var importGraph = function (filename) { 19 | return new Promise(function (resolve) { 20 | rdf.parseTurtle(fs.readFileSync(filename).toString(), function (graph) { 21 | resolve(graph); 22 | }); 23 | }); 24 | }; 25 | 26 | return Promise.all([ 27 | importGraph('./node_modules/tbbt-ld/dist/tbbt.nt') 28 | ]).then(function (graphs) { 29 | var 30 | mergedGraph = rdf.createGraph(), 31 | searchNs = 'http://localhost:8080', 32 | replaceNs = url.format({ 33 | protocol: 'http:', 34 | hostname: config.hostname, 35 | port: config.port || '', 36 | pathname: config.path || '' 37 | }); 38 | 39 | graphs.forEach(function (graph) { 40 | // map namespace to listener config 41 | graph = rdf.utils.mapNamespaceGraph(graph, searchNs, replaceNs); 42 | 43 | mergedGraph.addAll(graph); 44 | }); 45 | 46 | config.handlerOptions.storeOptions = { 47 | graph: mergedGraph, 48 | split: rdf.utils.splitGraphByNamedNodeSubject 49 | }; 50 | 51 | return Promise.resolve(); 52 | }); 53 | }; 54 | 55 | var patchResponseHeaders = function (res, headers) { 56 | if (res.statusCode === 200) { 57 | // clean existings values 58 | var fieldList = [ 59 | 'Access-Control-Allow-Origin', 60 | 'Cache-Control', 61 | 'Fuseki-Request-ID', 62 | 'Server', 63 | 'Vary']; 64 | 65 | if (res._headers) { 66 | fieldList.forEach(function (field) { 67 | if (field in res._headers) { 68 | delete res._headers[field]; 69 | } 70 | 71 | if (field.toLowerCase() in res._headers) { 72 | delete res._headers[field.toLowerCase()]; 73 | } 74 | }); 75 | } 76 | 77 | // cors header 78 | headers['Access-Control-Allow-Origin'] = '*'; 79 | 80 | // cache header 81 | headers['Cache-Control'] = 'public, max-age=120'; 82 | 83 | // vary header 84 | headers['Vary'] = 'Accept'; 85 | } 86 | 87 | return headers; 88 | }; 89 | 90 | module.exports = { 91 | app: 'trifid-ld', 92 | logger: { 93 | level: 'debug' 94 | }, 95 | // public interface visible after any reverse proxies 96 | hostname: 'localhost', 97 | port: 8080, 98 | path: '', 99 | // listener 100 | listener: { 101 | hostname: '', 102 | port: 8080 103 | }, 104 | expressSettings: { 105 | 'trust proxy': 'loopback', 106 | 'x-powered-by': null 107 | }, 108 | patchHeaders: { 109 | patchResponse: patchResponseHeaders 110 | }, 111 | init: init, 112 | HandlerClass: require('./lib/ldp-module-handler'), 113 | handlerOptions: { 114 | rdf: rdf, 115 | StoreClass: graphSplit.SplitStore 116 | } 117 | }; 118 | -------------------------------------------------------------------------------- /config.sparql.js: -------------------------------------------------------------------------------- 1 | var baseConfig = require('./config.fuseki'); 2 | var defaultsDeep = require('lodash/defaultsDeep'); 3 | 4 | var config = { 5 | listener: { 6 | port: 8080 7 | }, 8 | handlerOptions: { 9 | endpointUrl: 'http://localhost:3030/tbbt/sparql' 10 | } 11 | }; 12 | 13 | module.exports = defaultsDeep(config, baseConfig); 14 | -------------------------------------------------------------------------------- /data/public/css/custom.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 1em; 3 | } 4 | #header { 5 | background-color: #53BBF4; 6 | color: #fff; 7 | width: 100%; 8 | font-size: 2em; 9 | padding: 1em 3em 1em 1em; 10 | } 11 | #footer { 12 | background-color: #4298B5; 13 | color: #fff; 14 | font-size: 0.9em; 15 | width: 100%; 16 | text-align: center; 17 | padding: 2em 3em 2em 1em; 18 | } 19 | hr.dashed { 20 | border-top: 1px dashed #8c8b8b; 21 | width: 5%; 22 | margin-right:100%; 23 | } 24 | hr.baffone { 25 | height: 30px; 26 | border-style: solid; 27 | border-color: #8c8b8b; 28 | border-width: 1px 0 0 0; 29 | border-radius: 20px; 30 | } 31 | hr.baffone:before { 32 | display: block; 33 | content: ""; 34 | height: 30px; 35 | margin-top: -31px; 36 | border-style: solid; 37 | border-color: #8c8b8b; 38 | border-width: 0 0 1px 0; 39 | border-radius: 20px; 40 | } 41 | .moving { 42 | padding-top: 100px; 43 | margin-top: -100px; 44 | } 45 | 46 | /* STICKY */ 47 | #sticky.stick { 48 | margin-top: 0 !important; 49 | position: fixed; 50 | top: 0; 51 | z-index: 10000; 52 | } 53 | 54 | 55 | /* CONTAINER */ 56 | .container { 57 | width: 100%; 58 | padding: 2% 2% 2% 2%; 59 | } 60 | 61 | .LOD { 62 | background-color: #28abe3; 63 | color: #FFF; 64 | font-size: 2em; 65 | width: 100%; 66 | padding: 1em 3em 1em 2em; 67 | } 68 | -------------------------------------------------------------------------------- /data/public/img/synapta-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 12 | 13 | 28 | 29 | -------------------------------------------------------------------------------- /data/public/img/zazuko-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zazukoians/trifid-ld/cd9f5d261a8beadd9195466ea11df0ed6c0d6bae/data/public/img/zazuko-logo.png -------------------------------------------------------------------------------- /data/public/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.2 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | if (typeof jQuery === 'undefined') { 8 | throw new Error('Bootstrap\'s JavaScript requires jQuery') 9 | } 10 | 11 | +function ($) { 12 | 'use strict'; 13 | var version = $.fn.jquery.split(' ')[0].split('.') 14 | if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) { 15 | throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher') 16 | } 17 | }(jQuery); 18 | 19 | /* ======================================================================== 20 | * Bootstrap: transition.js v3.3.2 21 | * http://getbootstrap.com/javascript/#transitions 22 | * ======================================================================== 23 | * Copyright 2011-2015 Twitter, Inc. 24 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 25 | * ======================================================================== */ 26 | 27 | 28 | +function ($) { 29 | 'use strict'; 30 | 31 | // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) 32 | // ============================================================ 33 | 34 | function transitionEnd() { 35 | var el = document.createElement('bootstrap') 36 | 37 | var transEndEventNames = { 38 | WebkitTransition : 'webkitTransitionEnd', 39 | MozTransition : 'transitionend', 40 | OTransition : 'oTransitionEnd otransitionend', 41 | transition : 'transitionend' 42 | } 43 | 44 | for (var name in transEndEventNames) { 45 | if (el.style[name] !== undefined) { 46 | return { end: transEndEventNames[name] } 47 | } 48 | } 49 | 50 | return false // explicit for ie8 ( ._.) 51 | } 52 | 53 | // http://blog.alexmaccaw.com/css-transitions 54 | $.fn.emulateTransitionEnd = function (duration) { 55 | var called = false 56 | var $el = this 57 | $(this).one('bsTransitionEnd', function () { called = true }) 58 | var callback = function () { if (!called) $($el).trigger($.support.transition.end) } 59 | setTimeout(callback, duration) 60 | return this 61 | } 62 | 63 | $(function () { 64 | $.support.transition = transitionEnd() 65 | 66 | if (!$.support.transition) return 67 | 68 | $.event.special.bsTransitionEnd = { 69 | bindType: $.support.transition.end, 70 | delegateType: $.support.transition.end, 71 | handle: function (e) { 72 | if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) 73 | } 74 | } 75 | }) 76 | 77 | }(jQuery); 78 | 79 | /* ======================================================================== 80 | * Bootstrap: alert.js v3.3.2 81 | * http://getbootstrap.com/javascript/#alerts 82 | * ======================================================================== 83 | * Copyright 2011-2015 Twitter, Inc. 84 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 85 | * ======================================================================== */ 86 | 87 | 88 | +function ($) { 89 | 'use strict'; 90 | 91 | // ALERT CLASS DEFINITION 92 | // ====================== 93 | 94 | var dismiss = '[data-dismiss="alert"]' 95 | var Alert = function (el) { 96 | $(el).on('click', dismiss, this.close) 97 | } 98 | 99 | Alert.VERSION = '3.3.2' 100 | 101 | Alert.TRANSITION_DURATION = 150 102 | 103 | Alert.prototype.close = function (e) { 104 | var $this = $(this) 105 | var selector = $this.attr('data-target') 106 | 107 | if (!selector) { 108 | selector = $this.attr('href') 109 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 110 | } 111 | 112 | var $parent = $(selector) 113 | 114 | if (e) e.preventDefault() 115 | 116 | if (!$parent.length) { 117 | $parent = $this.closest('.alert') 118 | } 119 | 120 | $parent.trigger(e = $.Event('close.bs.alert')) 121 | 122 | if (e.isDefaultPrevented()) return 123 | 124 | $parent.removeClass('in') 125 | 126 | function removeElement() { 127 | // detach from parent, fire event then clean up data 128 | $parent.detach().trigger('closed.bs.alert').remove() 129 | } 130 | 131 | $.support.transition && $parent.hasClass('fade') ? 132 | $parent 133 | .one('bsTransitionEnd', removeElement) 134 | .emulateTransitionEnd(Alert.TRANSITION_DURATION) : 135 | removeElement() 136 | } 137 | 138 | 139 | // ALERT PLUGIN DEFINITION 140 | // ======================= 141 | 142 | function Plugin(option) { 143 | return this.each(function () { 144 | var $this = $(this) 145 | var data = $this.data('bs.alert') 146 | 147 | if (!data) $this.data('bs.alert', (data = new Alert(this))) 148 | if (typeof option == 'string') data[option].call($this) 149 | }) 150 | } 151 | 152 | var old = $.fn.alert 153 | 154 | $.fn.alert = Plugin 155 | $.fn.alert.Constructor = Alert 156 | 157 | 158 | // ALERT NO CONFLICT 159 | // ================= 160 | 161 | $.fn.alert.noConflict = function () { 162 | $.fn.alert = old 163 | return this 164 | } 165 | 166 | 167 | // ALERT DATA-API 168 | // ============== 169 | 170 | $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) 171 | 172 | }(jQuery); 173 | 174 | /* ======================================================================== 175 | * Bootstrap: button.js v3.3.2 176 | * http://getbootstrap.com/javascript/#buttons 177 | * ======================================================================== 178 | * Copyright 2011-2015 Twitter, Inc. 179 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 180 | * ======================================================================== */ 181 | 182 | 183 | +function ($) { 184 | 'use strict'; 185 | 186 | // BUTTON PUBLIC CLASS DEFINITION 187 | // ============================== 188 | 189 | var Button = function (element, options) { 190 | this.$element = $(element) 191 | this.options = $.extend({}, Button.DEFAULTS, options) 192 | this.isLoading = false 193 | } 194 | 195 | Button.VERSION = '3.3.2' 196 | 197 | Button.DEFAULTS = { 198 | loadingText: 'loading...' 199 | } 200 | 201 | Button.prototype.setState = function (state) { 202 | var d = 'disabled' 203 | var $el = this.$element 204 | var val = $el.is('input') ? 'val' : 'html' 205 | var data = $el.data() 206 | 207 | state = state + 'Text' 208 | 209 | if (data.resetText == null) $el.data('resetText', $el[val]()) 210 | 211 | // push to event loop to allow forms to submit 212 | setTimeout($.proxy(function () { 213 | $el[val](data[state] == null ? this.options[state] : data[state]) 214 | 215 | if (state == 'loadingText') { 216 | this.isLoading = true 217 | $el.addClass(d).attr(d, d) 218 | } else if (this.isLoading) { 219 | this.isLoading = false 220 | $el.removeClass(d).removeAttr(d) 221 | } 222 | }, this), 0) 223 | } 224 | 225 | Button.prototype.toggle = function () { 226 | var changed = true 227 | var $parent = this.$element.closest('[data-toggle="buttons"]') 228 | 229 | if ($parent.length) { 230 | var $input = this.$element.find('input') 231 | if ($input.prop('type') == 'radio') { 232 | if ($input.prop('checked') && this.$element.hasClass('active')) changed = false 233 | else $parent.find('.active').removeClass('active') 234 | } 235 | if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') 236 | } else { 237 | this.$element.attr('aria-pressed', !this.$element.hasClass('active')) 238 | } 239 | 240 | if (changed) this.$element.toggleClass('active') 241 | } 242 | 243 | 244 | // BUTTON PLUGIN DEFINITION 245 | // ======================== 246 | 247 | function Plugin(option) { 248 | return this.each(function () { 249 | var $this = $(this) 250 | var data = $this.data('bs.button') 251 | var options = typeof option == 'object' && option 252 | 253 | if (!data) $this.data('bs.button', (data = new Button(this, options))) 254 | 255 | if (option == 'toggle') data.toggle() 256 | else if (option) data.setState(option) 257 | }) 258 | } 259 | 260 | var old = $.fn.button 261 | 262 | $.fn.button = Plugin 263 | $.fn.button.Constructor = Button 264 | 265 | 266 | // BUTTON NO CONFLICT 267 | // ================== 268 | 269 | $.fn.button.noConflict = function () { 270 | $.fn.button = old 271 | return this 272 | } 273 | 274 | 275 | // BUTTON DATA-API 276 | // =============== 277 | 278 | $(document) 279 | .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { 280 | var $btn = $(e.target) 281 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 282 | Plugin.call($btn, 'toggle') 283 | e.preventDefault() 284 | }) 285 | .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { 286 | $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) 287 | }) 288 | 289 | }(jQuery); 290 | 291 | /* ======================================================================== 292 | * Bootstrap: carousel.js v3.3.2 293 | * http://getbootstrap.com/javascript/#carousel 294 | * ======================================================================== 295 | * Copyright 2011-2015 Twitter, Inc. 296 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 297 | * ======================================================================== */ 298 | 299 | 300 | +function ($) { 301 | 'use strict'; 302 | 303 | // CAROUSEL CLASS DEFINITION 304 | // ========================= 305 | 306 | var Carousel = function (element, options) { 307 | this.$element = $(element) 308 | this.$indicators = this.$element.find('.carousel-indicators') 309 | this.options = options 310 | this.paused = 311 | this.sliding = 312 | this.interval = 313 | this.$active = 314 | this.$items = null 315 | 316 | this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) 317 | 318 | this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element 319 | .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) 320 | .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) 321 | } 322 | 323 | Carousel.VERSION = '3.3.2' 324 | 325 | Carousel.TRANSITION_DURATION = 600 326 | 327 | Carousel.DEFAULTS = { 328 | interval: 5000, 329 | pause: 'hover', 330 | wrap: true, 331 | keyboard: true 332 | } 333 | 334 | Carousel.prototype.keydown = function (e) { 335 | if (/input|textarea/i.test(e.target.tagName)) return 336 | switch (e.which) { 337 | case 37: this.prev(); break 338 | case 39: this.next(); break 339 | default: return 340 | } 341 | 342 | e.preventDefault() 343 | } 344 | 345 | Carousel.prototype.cycle = function (e) { 346 | e || (this.paused = false) 347 | 348 | this.interval && clearInterval(this.interval) 349 | 350 | this.options.interval 351 | && !this.paused 352 | && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) 353 | 354 | return this 355 | } 356 | 357 | Carousel.prototype.getItemIndex = function (item) { 358 | this.$items = item.parent().children('.item') 359 | return this.$items.index(item || this.$active) 360 | } 361 | 362 | Carousel.prototype.getItemForDirection = function (direction, active) { 363 | var activeIndex = this.getItemIndex(active) 364 | var willWrap = (direction == 'prev' && activeIndex === 0) 365 | || (direction == 'next' && activeIndex == (this.$items.length - 1)) 366 | if (willWrap && !this.options.wrap) return active 367 | var delta = direction == 'prev' ? -1 : 1 368 | var itemIndex = (activeIndex + delta) % this.$items.length 369 | return this.$items.eq(itemIndex) 370 | } 371 | 372 | Carousel.prototype.to = function (pos) { 373 | var that = this 374 | var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) 375 | 376 | if (pos > (this.$items.length - 1) || pos < 0) return 377 | 378 | if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" 379 | if (activeIndex == pos) return this.pause().cycle() 380 | 381 | return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) 382 | } 383 | 384 | Carousel.prototype.pause = function (e) { 385 | e || (this.paused = true) 386 | 387 | if (this.$element.find('.next, .prev').length && $.support.transition) { 388 | this.$element.trigger($.support.transition.end) 389 | this.cycle(true) 390 | } 391 | 392 | this.interval = clearInterval(this.interval) 393 | 394 | return this 395 | } 396 | 397 | Carousel.prototype.next = function () { 398 | if (this.sliding) return 399 | return this.slide('next') 400 | } 401 | 402 | Carousel.prototype.prev = function () { 403 | if (this.sliding) return 404 | return this.slide('prev') 405 | } 406 | 407 | Carousel.prototype.slide = function (type, next) { 408 | var $active = this.$element.find('.item.active') 409 | var $next = next || this.getItemForDirection(type, $active) 410 | var isCycling = this.interval 411 | var direction = type == 'next' ? 'left' : 'right' 412 | var that = this 413 | 414 | if ($next.hasClass('active')) return (this.sliding = false) 415 | 416 | var relatedTarget = $next[0] 417 | var slideEvent = $.Event('slide.bs.carousel', { 418 | relatedTarget: relatedTarget, 419 | direction: direction 420 | }) 421 | this.$element.trigger(slideEvent) 422 | if (slideEvent.isDefaultPrevented()) return 423 | 424 | this.sliding = true 425 | 426 | isCycling && this.pause() 427 | 428 | if (this.$indicators.length) { 429 | this.$indicators.find('.active').removeClass('active') 430 | var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) 431 | $nextIndicator && $nextIndicator.addClass('active') 432 | } 433 | 434 | var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" 435 | if ($.support.transition && this.$element.hasClass('slide')) { 436 | $next.addClass(type) 437 | $next[0].offsetWidth // force reflow 438 | $active.addClass(direction) 439 | $next.addClass(direction) 440 | $active 441 | .one('bsTransitionEnd', function () { 442 | $next.removeClass([type, direction].join(' ')).addClass('active') 443 | $active.removeClass(['active', direction].join(' ')) 444 | that.sliding = false 445 | setTimeout(function () { 446 | that.$element.trigger(slidEvent) 447 | }, 0) 448 | }) 449 | .emulateTransitionEnd(Carousel.TRANSITION_DURATION) 450 | } else { 451 | $active.removeClass('active') 452 | $next.addClass('active') 453 | this.sliding = false 454 | this.$element.trigger(slidEvent) 455 | } 456 | 457 | isCycling && this.cycle() 458 | 459 | return this 460 | } 461 | 462 | 463 | // CAROUSEL PLUGIN DEFINITION 464 | // ========================== 465 | 466 | function Plugin(option) { 467 | return this.each(function () { 468 | var $this = $(this) 469 | var data = $this.data('bs.carousel') 470 | var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) 471 | var action = typeof option == 'string' ? option : options.slide 472 | 473 | if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) 474 | if (typeof option == 'number') data.to(option) 475 | else if (action) data[action]() 476 | else if (options.interval) data.pause().cycle() 477 | }) 478 | } 479 | 480 | var old = $.fn.carousel 481 | 482 | $.fn.carousel = Plugin 483 | $.fn.carousel.Constructor = Carousel 484 | 485 | 486 | // CAROUSEL NO CONFLICT 487 | // ==================== 488 | 489 | $.fn.carousel.noConflict = function () { 490 | $.fn.carousel = old 491 | return this 492 | } 493 | 494 | 495 | // CAROUSEL DATA-API 496 | // ================= 497 | 498 | var clickHandler = function (e) { 499 | var href 500 | var $this = $(this) 501 | var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 502 | if (!$target.hasClass('carousel')) return 503 | var options = $.extend({}, $target.data(), $this.data()) 504 | var slideIndex = $this.attr('data-slide-to') 505 | if (slideIndex) options.interval = false 506 | 507 | Plugin.call($target, options) 508 | 509 | if (slideIndex) { 510 | $target.data('bs.carousel').to(slideIndex) 511 | } 512 | 513 | e.preventDefault() 514 | } 515 | 516 | $(document) 517 | .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) 518 | .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) 519 | 520 | $(window).on('load', function () { 521 | $('[data-ride="carousel"]').each(function () { 522 | var $carousel = $(this) 523 | Plugin.call($carousel, $carousel.data()) 524 | }) 525 | }) 526 | 527 | }(jQuery); 528 | 529 | /* ======================================================================== 530 | * Bootstrap: collapse.js v3.3.2 531 | * http://getbootstrap.com/javascript/#collapse 532 | * ======================================================================== 533 | * Copyright 2011-2015 Twitter, Inc. 534 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 535 | * ======================================================================== */ 536 | 537 | 538 | +function ($) { 539 | 'use strict'; 540 | 541 | // COLLAPSE PUBLIC CLASS DEFINITION 542 | // ================================ 543 | 544 | var Collapse = function (element, options) { 545 | this.$element = $(element) 546 | this.options = $.extend({}, Collapse.DEFAULTS, options) 547 | this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]') 548 | this.transitioning = null 549 | 550 | if (this.options.parent) { 551 | this.$parent = this.getParent() 552 | } else { 553 | this.addAriaAndCollapsedClass(this.$element, this.$trigger) 554 | } 555 | 556 | if (this.options.toggle) this.toggle() 557 | } 558 | 559 | Collapse.VERSION = '3.3.2' 560 | 561 | Collapse.TRANSITION_DURATION = 350 562 | 563 | Collapse.DEFAULTS = { 564 | toggle: true, 565 | trigger: '[data-toggle="collapse"]' 566 | } 567 | 568 | Collapse.prototype.dimension = function () { 569 | var hasWidth = this.$element.hasClass('width') 570 | return hasWidth ? 'width' : 'height' 571 | } 572 | 573 | Collapse.prototype.show = function () { 574 | if (this.transitioning || this.$element.hasClass('in')) return 575 | 576 | var activesData 577 | var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') 578 | 579 | if (actives && actives.length) { 580 | activesData = actives.data('bs.collapse') 581 | if (activesData && activesData.transitioning) return 582 | } 583 | 584 | var startEvent = $.Event('show.bs.collapse') 585 | this.$element.trigger(startEvent) 586 | if (startEvent.isDefaultPrevented()) return 587 | 588 | if (actives && actives.length) { 589 | Plugin.call(actives, 'hide') 590 | activesData || actives.data('bs.collapse', null) 591 | } 592 | 593 | var dimension = this.dimension() 594 | 595 | this.$element 596 | .removeClass('collapse') 597 | .addClass('collapsing')[dimension](0) 598 | .attr('aria-expanded', true) 599 | 600 | this.$trigger 601 | .removeClass('collapsed') 602 | .attr('aria-expanded', true) 603 | 604 | this.transitioning = 1 605 | 606 | var complete = function () { 607 | this.$element 608 | .removeClass('collapsing') 609 | .addClass('collapse in')[dimension]('') 610 | this.transitioning = 0 611 | this.$element 612 | .trigger('shown.bs.collapse') 613 | } 614 | 615 | if (!$.support.transition) return complete.call(this) 616 | 617 | var scrollSize = $.camelCase(['scroll', dimension].join('-')) 618 | 619 | this.$element 620 | .one('bsTransitionEnd', $.proxy(complete, this)) 621 | .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) 622 | } 623 | 624 | Collapse.prototype.hide = function () { 625 | if (this.transitioning || !this.$element.hasClass('in')) return 626 | 627 | var startEvent = $.Event('hide.bs.collapse') 628 | this.$element.trigger(startEvent) 629 | if (startEvent.isDefaultPrevented()) return 630 | 631 | var dimension = this.dimension() 632 | 633 | this.$element[dimension](this.$element[dimension]())[0].offsetHeight 634 | 635 | this.$element 636 | .addClass('collapsing') 637 | .removeClass('collapse in') 638 | .attr('aria-expanded', false) 639 | 640 | this.$trigger 641 | .addClass('collapsed') 642 | .attr('aria-expanded', false) 643 | 644 | this.transitioning = 1 645 | 646 | var complete = function () { 647 | this.transitioning = 0 648 | this.$element 649 | .removeClass('collapsing') 650 | .addClass('collapse') 651 | .trigger('hidden.bs.collapse') 652 | } 653 | 654 | if (!$.support.transition) return complete.call(this) 655 | 656 | this.$element 657 | [dimension](0) 658 | .one('bsTransitionEnd', $.proxy(complete, this)) 659 | .emulateTransitionEnd(Collapse.TRANSITION_DURATION) 660 | } 661 | 662 | Collapse.prototype.toggle = function () { 663 | this[this.$element.hasClass('in') ? 'hide' : 'show']() 664 | } 665 | 666 | Collapse.prototype.getParent = function () { 667 | return $(this.options.parent) 668 | .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') 669 | .each($.proxy(function (i, element) { 670 | var $element = $(element) 671 | this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) 672 | }, this)) 673 | .end() 674 | } 675 | 676 | Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { 677 | var isOpen = $element.hasClass('in') 678 | 679 | $element.attr('aria-expanded', isOpen) 680 | $trigger 681 | .toggleClass('collapsed', !isOpen) 682 | .attr('aria-expanded', isOpen) 683 | } 684 | 685 | function getTargetFromTrigger($trigger) { 686 | var href 687 | var target = $trigger.attr('data-target') 688 | || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 689 | 690 | return $(target) 691 | } 692 | 693 | 694 | // COLLAPSE PLUGIN DEFINITION 695 | // ========================== 696 | 697 | function Plugin(option) { 698 | return this.each(function () { 699 | var $this = $(this) 700 | var data = $this.data('bs.collapse') 701 | var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) 702 | 703 | if (!data && options.toggle && option == 'show') options.toggle = false 704 | if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) 705 | if (typeof option == 'string') data[option]() 706 | }) 707 | } 708 | 709 | var old = $.fn.collapse 710 | 711 | $.fn.collapse = Plugin 712 | $.fn.collapse.Constructor = Collapse 713 | 714 | 715 | // COLLAPSE NO CONFLICT 716 | // ==================== 717 | 718 | $.fn.collapse.noConflict = function () { 719 | $.fn.collapse = old 720 | return this 721 | } 722 | 723 | 724 | // COLLAPSE DATA-API 725 | // ================= 726 | 727 | $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { 728 | var $this = $(this) 729 | 730 | if (!$this.attr('data-target')) e.preventDefault() 731 | 732 | var $target = getTargetFromTrigger($this) 733 | var data = $target.data('bs.collapse') 734 | var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this }) 735 | 736 | Plugin.call($target, option) 737 | }) 738 | 739 | }(jQuery); 740 | 741 | /* ======================================================================== 742 | * Bootstrap: dropdown.js v3.3.2 743 | * http://getbootstrap.com/javascript/#dropdowns 744 | * ======================================================================== 745 | * Copyright 2011-2015 Twitter, Inc. 746 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 747 | * ======================================================================== */ 748 | 749 | 750 | +function ($) { 751 | 'use strict'; 752 | 753 | // DROPDOWN CLASS DEFINITION 754 | // ========================= 755 | 756 | var backdrop = '.dropdown-backdrop' 757 | var toggle = '[data-toggle="dropdown"]' 758 | var Dropdown = function (element) { 759 | $(element).on('click.bs.dropdown', this.toggle) 760 | } 761 | 762 | Dropdown.VERSION = '3.3.2' 763 | 764 | Dropdown.prototype.toggle = function (e) { 765 | var $this = $(this) 766 | 767 | if ($this.is('.disabled, :disabled')) return 768 | 769 | var $parent = getParent($this) 770 | var isActive = $parent.hasClass('open') 771 | 772 | clearMenus() 773 | 774 | if (!isActive) { 775 | if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { 776 | // if mobile we use a backdrop because click events don't delegate 777 | $('