├── .gitignore ├── .gitattributes ├── .jshintrc ├── bower.json ├── package.json ├── GPL-3.0-LICENSE.txt ├── demo └── index.html ├── MIT-LICENSE.txt ├── Gruntfile.js ├── dist └── joindin.min.js ├── README.md └── src └── joindin.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": false, 5 | "curly": false, 6 | "eqeqeq": true, 7 | "eqnull": true, 8 | "immed": true, 9 | "latedef": true, 10 | "newcap": true, 11 | "noarg": true, 12 | "undef": true, 13 | "unused": true, 14 | "quotmark": "single", 15 | "strict": false, 16 | "trailing": true, 17 | "smarttabs": true 18 | } -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joindin-js", 3 | "version": "0.1.0", 4 | "main": "src/joindin.js", 5 | "author": { 6 | "name": "Aurelio De Rosa", 7 | "email": "a.derosa@audero.it", 8 | "web": "http://www.audero.it" 9 | }, 10 | "description": "An unofficial JavaScript library to embed Joind.in comments, talks, and more.", 11 | "keywords": [ 12 | "javascript", 13 | "library", 14 | "joind.in", 15 | "joind" 16 | ], 17 | "license": [ 18 | "MIT", 19 | "GPL-3.0" 20 | ], 21 | "ignore": [ 22 | "**/.*", 23 | "node_modules" 24 | ] 25 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joindin-js", 3 | "version": "0.1.0", 4 | "author": { 5 | "name": "Aurelio De Rosa", 6 | "email": "a.derosa@audero.it", 7 | "web": "http://www.audero.it" 8 | }, 9 | "description": "An unofficial JavaScript library to embed Joind.in comments, talks, and more", 10 | "dependencies": {}, 11 | "devDependencies": { 12 | "grunt": "^0.4.5", 13 | "grunt-contrib-jshint": "^0.9.2", 14 | "grunt-contrib-uglify": "^0.5.1", 15 | "jshint-stylish": "^0.4.0", 16 | "load-grunt-tasks": "^0.6.0" 17 | }, 18 | "engines": { 19 | "node": ">=0.10.16" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GPL-3.0-LICENSE.txt: -------------------------------------------------------------------------------- 1 | JoindIn.js is an unofficial JavaScript library to embed Joind.in 2 | comments, talks, and more. 3 | 4 | Copyright (C) 2014 Aurelio De Rosa 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JoindIn.js Demo by Aurelio De Rosa 6 | 13 | 14 | 15 |

JoindIn.js Demo

16 | 17 |

The elements below are loaded asynchronously using the JoindIn.js library.

18 | 19 |
20 |
21 | 22 |
23 |
24 | 25 |
26 | JoindIn.js has been created by 27 | Aurelio De Rosa 28 | (@AurelioDeRosa). 29 |
30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Aurelio De Rosa 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 'use strict'; 3 | 4 | require('load-grunt-tasks')(grunt); 5 | 6 | var config = { 7 | src: 'src', 8 | dist: 'dist' 9 | }; 10 | 11 | grunt.initConfig({ 12 | pkg: grunt.file.readJSON('package.json'), 13 | config: config, 14 | 15 | uglify: { 16 | options: { 17 | banner: '/*! joindin.js <%= pkg.version %> | Aurelio De Rosa (@AurelioDeRosa) | MIT/GPL-3.0 Licensed */\n' 18 | }, 19 | dist: { 20 | files: { 21 | '<%= config.dist %>/joindin.min.js': ['<%= config.src %>/joindin.js'] 22 | } 23 | } 24 | }, 25 | jshint: { 26 | files: ['Gruntfile.js', 'src/**/*.js'], 27 | options: { 28 | jshintrc: '.jshintrc', 29 | reporter: require('jshint-stylish') 30 | } 31 | }, 32 | watch: { 33 | files: ['<%= jshint.files %>'], 34 | tasks: ['jshint'] 35 | } 36 | }); 37 | 38 | grunt.registerTask('test', [ 39 | 'jshint' 40 | ]); 41 | 42 | grunt.registerTask('default', [ 43 | 'jshint', 44 | 'uglify' 45 | ]); 46 | }; -------------------------------------------------------------------------------- /dist/joindin.min.js: -------------------------------------------------------------------------------- 1 | /*! joindin.js 0.1.0 | Aurelio De Rosa (@AurelioDeRosa) | MIT/GPL-3.0 Licensed */ 2 | var JoindIn=function(a){"use strict";function b(b,c){b=b.comments[0];var d=h("blockquote");d.cite=b.talk_uri,d.setAttribute("itemprop","review"),d.setAttribute("itemscope",""),d.setAttribute("itemtype","http://schema.org/Review"),d.style.padding="10px",d.style.background="#fff",d.style.borderRadius="5px",d.style.border="1px solid #ddd";var e=h("div"),f=h("img");f.src=g.replace(/RATING/,b.rating),f.alt="Rate "+b.rating+" of 5",e.style.width="140px",e.style.float="left",e.appendChild(f);var i=h("div");i.style.width="auto",i.style.overflow="hidden";var j=h("p");j.setAttribute("itemprop","description"),j.textContent=b.comment,j.style.marginTop="0",i.appendChild(j);var k=h("footer"),l=h("cite");l.setAttribute("itemprop","author"),l.textContent=b.user_display_name;var m=h("time");m.setAttribute("datetime",b.created_date),m.setAttribute("itemprop","datePublished"),m.textContent=new Date(b.created_date).toLocaleString(),k.appendChild(a.createTextNode("— ")),k.appendChild(l),k.appendChild(a.createTextNode(" on ")),k.appendChild(m),i.appendChild(k);var n=h("div");n.style.clear="both",d.appendChild(e),d.appendChild(i),d.appendChild(n),c.appendChild(d)}function c(a,b){a=a.talks[0];var c=h("div");c.style.border="1px solid #d7dcdf",c.style.background="#f0f4f8",c.style.borderRadius="6px",c.style.padding="10px 15px",c.style.marginBottom="10px";var d=h("a");d.href=a.website_uri,d.style.color="#2368af";var e=h("h1");e.textContent=a.talk_title,e.style.marginTop="0",e.style.marginBottom="5px",d.appendChild(e);var f=h("p");f.textContent=a.talk_description;var i=h("span");i.textContent=a.speakers.map(function(a){return a.speaker_name}).join(" , ");var j=h("img");j.src=g.replace(/RATING/,a.average_rating),j.style.display="block",j.style.marginTop="10px",c.appendChild(d),c.appendChild(i),c.appendChild(j),c.appendChild(f),b.appendChild(c)}var d=".joindin-embed",e=[],f="http://api.joind.in/v2.1/",g="https://joind.in/inc/img/rating-RATING.gif",h=a.createElement.bind(a),i=a.querySelectorAll(d);return[].forEach.call(i,function(d,g){var i=d.getAttribute("data-type"),j=d.getAttribute("data-id");if("talk_comments"===i)e[g]=function(a){b(a,d)};else{if("talks"!==i)throw new Error("Data type not recognized");e[g]=function(a){c(a,d)}}var k=h("script");k.src=f+i+"/"+j+"?format=json&callback=JoindIn.callbacks["+g+"]",a.head.appendChild(k),a.head.removeChild(k)}),{callbacks:e}}(document); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JoindIn.js # 2 | 3 | [JoindIn.js](https://github.com/AurelioDeRosa/JoindIn.js) is an unofficial JavaScript library to embed Joind.in 4 | comments, talks, and more. The library is written in plain JavaScript, so has no dependencies. 5 | 6 | ## Compatibility ## 7 | 8 | JoindIn.js has been tested on the following browsers: Internet Explorer 9+, Chrome, Opera, Firefox, and Safari. 9 | 10 | ## Demo ## 11 | A live demo is available [here](http://htmlpreview.github.io/?https://github.com/AurelioDeRosa/JoindIn.js/blob/master/demo/index.html). 12 | 13 | ## Elements supported ## 14 | 15 | The library supports the following elements: 16 | 17 | * Talk (`data-type="talks"`) 18 | * Comment (`data-type="talk_comments"`) 19 | 20 | More elements will be integrated soon. 21 | 22 | ## Installation ## 23 | 24 | You can install JoindIn.js using [Bower](http://bower.io): 25 | 26 | ```shell 27 | bower install joindin-js 28 | ``` 29 | 30 | Then, include the JavaScript file in your web page as shown below: 31 | 32 | ```html 33 | 34 | ``` 35 | 36 | **Note**: the `async` attribute isn't necessary but can improve the performance of your website. 37 | 38 | If you don't have or don't want to use Bower, you can clone this repository running the command: 39 | 40 | ```shell 41 | git clone https://github.com/AurelioDeRosa/JoindIn.js.git 42 | ``` 43 | 44 | The last option is to manually download the library. 45 | 46 | ## Usage ## 47 | 48 | Once you have the JavaScript file in place, you have to create one or more DOM elements for every element you want to 49 | embed: talk, comment, or any other of the [element supported](#elements-supported). The DOM elements you create must 50 | use the class `joindin-embed` and define two `data-*` attributes: 51 | 52 | * `data-id`: the ID of the element you want to embed. To know how to retrieve the ID of an element read the 53 | section [How to retrieve the ID of the element to embed](#how-to-retrieve-the-id-of-the-element-to-embed); 54 | * `data-type`: [the type of the element](#elements-supported) to embed. 55 | 56 | ### Embedding a talk ### 57 | To embed my talk "[Modern front-end with the eyes of a PHP developer](https://joind.in/talk/view/10889)", 58 | you have to add the following element to your page: 59 | 60 | ```html 61 |
62 |
63 | ``` 64 | 65 | ### Embedding a comment on a talk ### 66 | 67 | To embed one specific comment published on the same talk, you have to add the following element instead: 68 | 69 | ```html 70 |
71 |
72 | ``` 73 | 74 | Note that in this case the ID refers to the comment, not the talk. 75 | 76 | ### How to retrieve the ID of the element to embed ## 77 | 78 | Retrieving the ID of the element you want to embed is the tricky part of using this library. 79 | 80 | For a talk the ID can be found in the URL of the website. For example, the URL of my talk 81 | "[Modern front-end with the eyes of a PHP developer](https://joind.in/talk/view/10889)" is 82 | [https://joind.in/talk/view/10889](https://joind.in/talk/view/10889), so its ID is 10889. 83 | 84 | To retrieve the ID of a comment you have to look at the source code of the page. It's written as part of the class 85 | name set to the element wrapping each comment. For example, you can find a class name like `comment-43964` where 86 | 43964 is the ID of the comment. 87 | 88 | ## Note ## 89 | 90 | JoindIn.js is in a very early stage, so it's not stable and [every contribution is welcomed](#contribute). 91 | 92 | ## Contribute ## 93 | 94 | I'd love the contribution of anyone who is keen to help. Report a bug, fix a issue, suggest a feature: 95 | any contribution to improve the project is highly appreciated. If you're uncertain whether an addition should be 96 | made, feel free to open up an issue so we can discuss it. 97 | 98 | ## License ## 99 | 100 | JoindIn.js is dual licensed under [MIT](http://www.opensource.org/licenses/MIT) and 101 | [GPL-3.0](http://opensource.org/licenses/GPL-3.0) 102 | 103 | ## Author ## 104 | 105 | [Aurelio De Rosa](http://www.audero.it) ([@AurelioDeRosa](https://twitter.com/AurelioDeRosa)) -------------------------------------------------------------------------------- /src/joindin.js: -------------------------------------------------------------------------------- 1 | /* jshint browser: true */ 2 | /* exported JoindIn */ 3 | var JoindIn = (function (document) { 4 | 'use strict'; 5 | 6 | // Defines the class the serves as a hook to select elements 7 | var joindInClass = '.joindin-embed'; 8 | // Contains the callbacks to execute, one for each JoindIn embedded element defined in the page 9 | var callbacks = []; 10 | // Defines the base URL to call the JoindIn API 11 | var urlAPI = 'http://api.joind.in/v2.1/'; 12 | // Defines the URL to show the rating image 13 | var ratingImageUrl = 'https://joind.in/inc/img/rating-RATING.gif'; 14 | 15 | // Stores the createElement function to improve the effect of the minification 16 | var createElement = document.createElement.bind(document); 17 | 18 | // Defines the function to create the element for a talk comment 19 | function createTalkComment(data, element) { 20 | data = data.comments[0]; 21 | 22 | var wrapper = createElement('blockquote'); 23 | wrapper.cite = data.talk_uri; 24 | wrapper.setAttribute('itemprop', 'review'); 25 | wrapper.setAttribute('itemscope', ''); 26 | wrapper.setAttribute('itemtype', 'http://schema.org/Review'); 27 | wrapper.style.padding = '10px'; 28 | wrapper.style.background = '#fff'; 29 | wrapper.style.borderRadius = '5px'; 30 | wrapper.style.border = '1px solid #ddd'; 31 | 32 | var ratingWrapper = createElement('div'); 33 | var rating = createElement('img'); 34 | rating.src = ratingImageUrl.replace(/RATING/, data.rating); 35 | rating.alt = 'Rate ' + data.rating + ' of 5'; 36 | ratingWrapper.style.width = '140px'; 37 | ratingWrapper.style.float = 'left'; 38 | ratingWrapper.appendChild(rating); 39 | 40 | var textWrapper = createElement('div'); 41 | textWrapper.style.width = 'auto'; 42 | textWrapper.style.overflow = 'hidden'; 43 | var text = createElement('p'); 44 | text.setAttribute('itemprop', 'description'); 45 | text.textContent = data.comment; 46 | text.style.marginTop = '0'; 47 | textWrapper.appendChild(text); 48 | 49 | var footer = createElement('footer'); 50 | 51 | var user = createElement('cite'); 52 | user.setAttribute('itemprop', 'author'); 53 | user.textContent = data.user_display_name; 54 | 55 | var date = createElement('time'); 56 | date.setAttribute('datetime', data.created_date); 57 | date.setAttribute('itemprop', 'datePublished'); 58 | date.textContent = new Date(data.created_date).toLocaleString(); 59 | 60 | footer.appendChild(document.createTextNode('— ')); 61 | footer.appendChild(user); 62 | footer.appendChild(document.createTextNode(' on ')); 63 | footer.appendChild(date); 64 | textWrapper.appendChild(footer); 65 | 66 | var clearer = createElement('div'); 67 | clearer.style.clear = 'both'; 68 | 69 | wrapper.appendChild(ratingWrapper); 70 | wrapper.appendChild(textWrapper); 71 | wrapper.appendChild(clearer); 72 | 73 | element.appendChild(wrapper); 74 | } 75 | 76 | // Defines the function to create the element for a talk 77 | function createTalk(data, element) { 78 | data = data.talks[0]; 79 | 80 | var wrapper = createElement('div'); 81 | wrapper.style.border = '1px solid #d7dcdf'; 82 | wrapper.style.background = '#f0f4f8'; 83 | wrapper.style.borderRadius = '6px'; 84 | wrapper.style.padding = '10px 15px'; 85 | wrapper.style.marginBottom = '10px'; 86 | 87 | var titleWrapper = createElement('a'); 88 | titleWrapper.href = data.website_uri; 89 | titleWrapper.style.color = '#2368af'; 90 | var title = createElement('h1'); 91 | title.textContent = data.talk_title; 92 | title.style.marginTop = '0'; 93 | title.style.marginBottom = '5px'; 94 | titleWrapper.appendChild(title); 95 | 96 | var description = createElement('p'); 97 | description.textContent = data.talk_description; 98 | 99 | var author = createElement('span'); 100 | author.textContent = data.speakers.map(function(element) { 101 | return element.speaker_name; 102 | }).join(' , '); 103 | 104 | var rating = createElement('img'); 105 | rating.src = ratingImageUrl.replace(/RATING/, data.average_rating); 106 | rating.style.display = 'block'; 107 | rating.style.marginTop = '10px'; 108 | 109 | wrapper.appendChild(titleWrapper); 110 | wrapper.appendChild(author); 111 | wrapper.appendChild(rating); 112 | wrapper.appendChild(description); 113 | 114 | element.appendChild(wrapper); 115 | } 116 | 117 | var elements = document.querySelectorAll(joindInClass); 118 | [].forEach.call(elements, function(element, index) { 119 | // Use getAttribute() instead of dataset to support IE9-10 120 | var type = element.getAttribute('data-type'); 121 | var id = element.getAttribute('data-id'); 122 | 123 | if (type === 'talk_comments') { 124 | callbacks[index] = function(data) { 125 | createTalkComment(data, element); 126 | }; 127 | } else if (type === 'talks') { 128 | callbacks[index] = function(data) { 129 | createTalk(data, element); 130 | }; 131 | } else { 132 | throw new Error('Data type not recognized'); 133 | } 134 | 135 | var script = createElement('script'); 136 | script.src = urlAPI + type + '/' + id + '?format=json&callback=JoindIn.callbacks[' + index + ']'; 137 | document.head.appendChild(script); 138 | document.head.removeChild(script); 139 | }); 140 | 141 | // Exposes the callbacks outside the IIFE 142 | return { 143 | callbacks: callbacks 144 | }; 145 | })(document); --------------------------------------------------------------------------------