├── .gitignore ├── .jshintrc ├── .travis.yml ├── LICENSE ├── README.md └── frontend ├── .bowerrc ├── .editorconfig ├── .gitattributes ├── Gruntfile.js ├── README.md ├── app ├── favicon.ico ├── images │ └── osscdn-logo.png ├── index.html ├── robots.txt ├── scripts │ ├── app.js │ └── controllers │ │ └── main.js ├── styles │ └── main.css └── views │ └── main.html ├── bower.json ├── package.json └── serve.js /.gitignore: -------------------------------------------------------------------------------- 1 | frontend/.tmp 2 | frontend/app/bower_components 3 | frontend/app/data 4 | frontend/dist 5 | node_modules 6 | walker/config.js 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "browser": true, 4 | "camelcase": true, 5 | "curly": true, 6 | "eqeqeq": true, 7 | "esnext": true, 8 | "immed": true, 9 | "indent": 4, 10 | "latedef": false, 11 | "newcap": true, 12 | "noarg": true, 13 | "node": true, 14 | "quotmark": "single", 15 | "strict": true, 16 | "trailing": true, 17 | "undef": true, 18 | "unused": true, 19 | "globals": { 20 | "angular": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | install: 5 | - cd frontend 6 | - npm install -g bower grunt-cli 7 | - npm install 8 | - bower install 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 MaxCDN 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open Source Software CDN 2 | [![Build Status](https://travis-ci.org/MaxCDN/osscdn.png?branch=master)](https://travis-ci.org/MaxCDN/osscdn) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com/) 3 | 4 | OSSCDN is a free [CDN](http://en.wikipedia.org/wiki/Content_delivery_network) 5 | powered by [MaxCDN](http://www.maxcdn.com) that hosts all kinds of Open Source projects. 6 | 7 | This includes, but not limited to, JS libraries, jQuery plugins, Linux Mirrors, 8 | executable files and more. 9 | 10 | 11 | ## [Submit files](https://github.com/jsdelivr/jsdelivr#how-to-submit-or-update-projects) 12 | 13 | 14 | 15 | 16 | ## Development 17 | 18 | The instructions below show how to get project dependencies installed and the project running: 19 | 20 | 1. Make sure you have a recent version of [Node.js](http://nodejs.org/) installed. It should include NPM, a package control by default. We'll use that to fetch our dependencies. 21 | 2. In addition you are going to require [Bower](http://bower.io/). That will be used to deal with some of the frontend dependencies. Install it using npm like this `npm install bower -g`. After that you should have `bower` command available. 22 | 3. The frontend relies on [Grunt](http://gruntjs.com/) for various tasks. Install the terminal client using `npm install grunt-cli -g`. After this you should have `grunt` command available. 23 | 4. Execute `npm install` at frontend/ 24 | 5. Execute `bower install` at frontend/ 25 | 6. Execute `cd frontend` 26 | 7. Execute `grunt server`. This will run a development server and open the app in your browser. As you make changes to the project it will refresh the browser automatically. 27 | 28 | ### Production Server 29 | 30 | The frontend contains our Node.js based production server. Provided you have installed the project dependencies, simply execute `frontend/serve.js` to get it running. Note that it expects to find the data at `dist/` directory. The directory itself may be generated using `grunt build`. That will wipe the contents of the directory entirely. After this you should symlink your data so that `dist/data` directory points at it. 31 | 32 | 33 | ## License 34 | 35 | OSSCDN code is released under the [MIT License](/LICENSE). 36 | -------------------------------------------------------------------------------- /frontend/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /frontend/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /frontend/Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2013-10-03 using generator-angular 0.4.0 2 | 'use strict'; 3 | var LIVERELOAD_PORT = 35729; 4 | var lrSnippet = require('connect-livereload')({ port: LIVERELOAD_PORT }); 5 | var mountFolder = function (connect, dir) { 6 | return connect.static(require('path').resolve(dir)); 7 | }; 8 | 9 | module.exports = function (grunt) { 10 | require('load-grunt-tasks')(grunt); 11 | require('time-grunt')(grunt); 12 | 13 | // configurable paths 14 | var yeomanConfig = { 15 | app: 'app', 16 | dist: 'dist' 17 | }; 18 | 19 | try { 20 | yeomanConfig.app = require('./bower.json').appPath || yeomanConfig.app; 21 | } catch (e) {} 22 | 23 | grunt.initConfig({ 24 | yeoman: yeomanConfig, 25 | watch: { 26 | styles: { 27 | files: ['<%= yeoman.app %>/styles/{,*/}*.css'], 28 | tasks: [] 29 | }, 30 | livereload: { 31 | options: { 32 | livereload: LIVERELOAD_PORT 33 | }, 34 | files: [ 35 | '<%= yeoman.app %>/{,*/}*.html', 36 | 'app/styles/{,*/}*.css', 37 | '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js', 38 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' 39 | ] 40 | } 41 | }, 42 | connect: { 43 | options: { 44 | port: 9000, 45 | // Change this to '0.0.0.0' to access the server from outside. 46 | hostname: 'localhost' 47 | }, 48 | livereload: { 49 | options: { 50 | middleware: function (connect) { 51 | return [ 52 | lrSnippet, 53 | mountFolder(connect, '.tmp'), 54 | mountFolder(connect, yeomanConfig.app) 55 | ]; 56 | } 57 | } 58 | }, 59 | test: { 60 | options: { 61 | middleware: function (connect) { 62 | return [ 63 | mountFolder(connect, '.tmp'), 64 | mountFolder(connect, 'test') 65 | ]; 66 | } 67 | } 68 | }, 69 | dist: { 70 | options: { 71 | middleware: function (connect) { 72 | return [ 73 | mountFolder(connect, yeomanConfig.dist) 74 | ]; 75 | } 76 | } 77 | } 78 | }, 79 | open: { 80 | server: { 81 | url: 'http://localhost:<%= connect.options.port %>' 82 | } 83 | }, 84 | clean: { 85 | dist: { 86 | files: [{ 87 | dot: true, 88 | src: [ 89 | '.tmp', 90 | '<%= yeoman.dist %>/*', 91 | '!<%= yeoman.dist %>/.git*' 92 | ] 93 | }] 94 | }, 95 | server: '.tmp' 96 | }, 97 | jshint: { 98 | options: { 99 | jshintrc: '../.jshintrc' 100 | }, 101 | all: [ 102 | 'Gruntfile.js', 103 | 'serve.js', 104 | '<%= yeoman.app %>/scripts/{,*/}*.js' 105 | ] 106 | }, 107 | // not used since Uglify task does concat, 108 | // but still available if needed 109 | /*concat: { 110 | dist: {} 111 | },*/ 112 | rev: { 113 | dist: { 114 | files: { 115 | src: [ 116 | '<%= yeoman.dist %>/styles/{,*/}*.css', 117 | '<%= yeoman.dist %>/scripts/{,*/}*.js' 118 | //'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 119 | //'<%= yeoman.dist %>/styles/fonts/*' 120 | ] 121 | } 122 | } 123 | }, 124 | useminPrepare: { 125 | html: '<%= yeoman.app %>/index.html', 126 | options: { 127 | dest: '<%= yeoman.dist %>' 128 | } 129 | }, 130 | usemin: { 131 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], 132 | html: ['<%= yeoman.dist %>/{,*/}*.html'], 133 | options: { 134 | dirs: ['<%= yeoman.dist %>'] 135 | } 136 | }, 137 | uglify: { 138 | options: { 139 | preserveComments: false, 140 | report: 'min' 141 | }, 142 | scripts: { 143 | options: { 144 | mangle: false 145 | }, 146 | src: '<%= yeoman.app %>/scripts/**/*.js', 147 | dest: '<%= yeoman.dist %>/scripts/scripts.js' 148 | } 149 | }, 150 | cssmin: { 151 | options: { 152 | report: 'min' 153 | } 154 | }, 155 | htmlmin: { 156 | dist: { 157 | options: { 158 | collapseWhitespace: true, 159 | removeComments: true 160 | }, 161 | files: [{ 162 | expand: true, 163 | cwd: '<%= yeoman.dist %>', 164 | src: '**/*.html', 165 | dest: '<%= yeoman.dist %>' 166 | }] 167 | } 168 | }, 169 | // Put files not handled in other tasks here 170 | copy: { 171 | dist: { 172 | files: [{ 173 | expand: true, 174 | dot: true, 175 | cwd: '<%= yeoman.app %>', 176 | dest: '<%= yeoman.dist %>', 177 | src: [ 178 | 'index.html', 179 | 'views/*.html', 180 | 'data/*.json', 181 | '*.{ico,png,txt}', 182 | 'bower_components/**/*', 183 | 'images/*.{png,jpg}', 184 | 'images/{,*/}*.{gif,webp}' 185 | ] 186 | }, { 187 | expand: true, 188 | cwd: '.tmp/images', 189 | dest: '<%= yeoman.dist %>/images', 190 | src: [ 191 | 'generated/*' 192 | ] 193 | }] 194 | } 195 | }, 196 | concurrent: { 197 | server: [], 198 | test: [], 199 | dist: [] 200 | }, 201 | cdnify: { 202 | dist: { 203 | html: ['<%= yeoman.dist %>/*.html'] 204 | } 205 | }, 206 | ngmin: { 207 | dist: { 208 | files: [{ 209 | expand: true, 210 | cwd: '<%= yeoman.dist %>/scripts', 211 | src: '*.js', 212 | dest: '<%= yeoman.dist %>/scripts' 213 | }] 214 | } 215 | } 216 | }); 217 | 218 | grunt.registerTask('server', function (target) { 219 | if(target === 'dist') { 220 | return grunt.task.run(['build', 'open', 'connect:dist:keepalive']); 221 | } 222 | 223 | grunt.task.run([ 224 | 'clean:server', 225 | 'concurrent:server', 226 | 'connect:livereload', 227 | 'open', 228 | 'watch' 229 | ]); 230 | }); 231 | 232 | grunt.registerTask('test', [ 233 | 'jshint' 234 | ]); 235 | 236 | grunt.registerTask('build', [ 237 | 'clean:dist', 238 | 'useminPrepare', 239 | 'concurrent:dist', 240 | 'concat', 241 | 'copy:dist', 242 | 'cdnify', 243 | 'ngmin', 244 | 'cssmin', 245 | 'uglify', 246 | 'uglify:scripts', 247 | 'rev', 248 | 'usemin', 249 | 'htmlmin' 250 | ]); 251 | 252 | grunt.registerTask('default', [ 253 | 'test', 254 | 'build' 255 | ]); 256 | }; 257 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Frontend 2 | 3 | Based mostly on Angular. Uses Grunt, Bower and co. It comes with a standalone server based on Node.js (Express). 4 | 5 | In case you want to run it, copy `data.json` generated using walker to `/dist` and either run `./serve.js` or `grunt server`. The latter is very useful during development. Before running either you'll likely want to `grunt install` and `npm install` dependencies. 6 | -------------------------------------------------------------------------------- /frontend/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxCDN/osscdn/001c0143688d11c28d3a8026199dc56be87c3afa/frontend/app/favicon.ico -------------------------------------------------------------------------------- /frontend/app/images/osscdn-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxCDN/osscdn/001c0143688d11c28d3a8026199dc56be87c3afa/frontend/app/images/osscdn-logo.png -------------------------------------------------------------------------------- /frontend/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Open Source Software CDN by MaxCDN 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 |
27 |
28 | Fork me on GitHub 29 |
30 |
31 |
32 |
33 |
34 | 35 | 40 | 41 |
42 | 54 |
55 | 56 |
57 |
58 |
59 | 60 |
61 | 62 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 90 |
91 | 92 | 93 | -------------------------------------------------------------------------------- /frontend/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('osscdnApp', ['ngAnimate', 'ui.router', 'ngDropdowns', 'semverSort', 4 | 'ngClipboard', 'angular-flash.service', 'angular-flash.flash-alert-directive']) 5 | .config(function($stateProvider, $urlRouterProvider, flashProvider, ngClipProvider) { 6 | $urlRouterProvider.otherwise('/'); 7 | 8 | $stateProvider.state('libraries', { 9 | url: '/:name', 10 | templateUrl: 'views/main.html', 11 | controller: 'MainCtrl' 12 | }); 13 | 14 | ngClipProvider.setPath('//oss.maxcdn.com/zeroclipboard/1.3.3/ZeroClipboard.swf'); 15 | 16 | flashProvider.successClassnames.push('alert-success'); 17 | }); 18 | -------------------------------------------------------------------------------- /frontend/app/scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('osscdnApp').controller('MainCtrl', function($scope, $http, $state, $filter, flash) { 4 | $scope.search = {}; 5 | $scope.libraries = []; 6 | $scope.limit = 10; 7 | 8 | var root = 'http://api.jsdelivr.com/v1/jsdelivr/libraries'; 9 | 10 | $http.get(root + '?fields=name').then(function(res) { 11 | // attach random keys needed by random sorting 12 | $scope.libraries = res.data.map(function(lib) { 13 | lib.randomKey = parseInt(Math.random() * 1000, 10); 14 | 15 | return lib; 16 | }); 17 | 18 | if($state.params.name) { 19 | $scope.search.name = $state.params.name; 20 | $scope.limit = 1; 21 | } 22 | }); 23 | 24 | $scope.orderByName = function(library) { 25 | if($scope.search && $scope.search.name) { 26 | return library.name.length; 27 | } 28 | 29 | // use random order in case no search query exists 30 | return library.randomKey; 31 | }; 32 | 33 | $scope.getLibraries = function() { 34 | if($scope.libraries.filtered) { 35 | $scope.libraries.filtered.forEach(getLibrary); 36 | } 37 | else { 38 | console.warn('Missing library data!'); 39 | } 40 | }; 41 | 42 | $scope.hasEnoughItems = function(library) { 43 | if(!library.selectedVersion) { 44 | return; 45 | } 46 | 47 | // it would be better to calculate visibility status via CSS 48 | var version = library.selectedVersion.value; 49 | 50 | return library.cdn[version].length > 6; 51 | }; 52 | 53 | $scope.copy = function(demo, item, index) { 54 | console.log(demo, item, index); 55 | }; 56 | 57 | $scope.getCDNLink = function(name, version, file) { 58 | return '//oss.maxcdn.com/' + name + '/' + version + '/' + file; 59 | }; 60 | 61 | $scope.copied = function() { 62 | flash.success = 'Copied to clipboard'; 63 | }; 64 | 65 | function getLibrary(library) { 66 | if(library.description) { 67 | return; 68 | } 69 | 70 | $http.get(root + '/' + library.name + 71 | '?fields=author,name,description,homepage,assets').then(function(res) { 72 | var d = res.data[0]; 73 | var k; 74 | 75 | for(k in d) { 76 | library[k] = d[k]; 77 | } 78 | 79 | // XXX: convert assets to cdn format 80 | library.cdn = convertAssets(library.assets); 81 | delete library.assets; 82 | 83 | library.versions = $filter('semverSort')(Object.keys(library.cdn)).map(function(version) { 84 | return { 85 | text: semverize(version), 86 | value: version 87 | }; 88 | }).reverse(); 89 | 90 | library.selectedVersion = angular.copy(library.versions[0]); 91 | }); 92 | } 93 | 94 | function convertAssets(arr) { 95 | var ret = {}; 96 | 97 | if(!arr) { 98 | return ret; 99 | } 100 | 101 | arr.forEach(function(v) { 102 | ret[v.version] = v.files; 103 | }); 104 | 105 | return ret; 106 | } 107 | 108 | function semverize(str) { 109 | // x.y -> x.y.0 110 | var parts = str.split('.'); 111 | 112 | if(parts && parts.length === 2) { 113 | return str + '.0'; 114 | } 115 | 116 | return str; 117 | } 118 | }); 119 | 120 | angular.module('osscdnApp').filter('as', function($parse) { 121 | return function(value, path) { 122 | return $parse(path).assign(this, value); 123 | }; 124 | }); 125 | 126 | angular.module('osscdnApp').directive('repeatDone', function() { 127 | return { 128 | restrict: 'A', 129 | scope: { 130 | method: '&repeatDone' 131 | }, 132 | link: function($scope) { 133 | if($scope.$parent.$last) { 134 | $scope.method()(); 135 | } 136 | } 137 | }; 138 | }); 139 | -------------------------------------------------------------------------------- /frontend/app/styles/main.css: -------------------------------------------------------------------------------- 1 | /*csslint adjoining-classes: false*/ 2 | 3 | body { 4 | background-color: #f4f4f4; 5 | color: #5e5b64; 6 | font: 15px/1.3 "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 7 | margin: 40px auto 20px auto; 8 | } 9 | 10 | a:hover{ 11 | text-decoration: none; 12 | } 13 | 14 | ul { 15 | list-style-type: none; 16 | margin: 0 auto; 17 | padding: 0; 18 | } 19 | 20 | header { 21 | margin-bottom: 20px; 22 | } 23 | 24 | footer { 25 | padding-top: 20px; 26 | text-align: center; 27 | } 28 | 29 | input { 30 | margin-bottom: 20px; 31 | } 32 | 33 | /* Bootstrap overrides start */ 34 | .container { 35 | max-width: 940px; 36 | } 37 | 38 | nav > .list-inline > li { 39 | padding-left: 2px; 40 | padding-right: 2px; 41 | } 42 | /* Bootstrap overrides end */ 43 | 44 | .alert.fade.in { 45 | visibility: visible; 46 | } 47 | 48 | .alert { 49 | visibility: hidden; 50 | position: fixed; 51 | z-index: 1005; 52 | top: 1em; 53 | } 54 | 55 | .logo { 56 | margin-bottom: 10px; 57 | } 58 | 59 | .logo + div { 60 | z-index: 1002; 61 | } 62 | 63 | .extra { 64 | margin-top: 1em; 65 | } 66 | 67 | .wrap-dd-select { 68 | padding: 0.25em; 69 | } 70 | 71 | .dropdown { 72 | max-height: 20em; 73 | overflow: auto; 74 | z-index: 10; 75 | } 76 | 77 | .dropdown:before, 78 | .dropdown:after { 79 | left: 2em; 80 | right: inherit; 81 | } 82 | 83 | 84 | .library, 85 | .file { 86 | padding: 0.5em; 87 | } 88 | 89 | .library { 90 | background-color: white; 91 | border: 0.1em solid #e3e3e3; 92 | -webkit-border-radius: 0.2em 0.1em 0.1em 0.1em; 93 | -moz-border-radius: 0.2em 0.1em 0.1em 0.1em; 94 | border-radius: 0.2em 0.1em 0.1em 0.1em; 95 | margin-bottom: 1em; 96 | } 97 | 98 | .library .name { 99 | display: inline-block; 100 | vertical-align: middle; 101 | } 102 | 103 | .library .versionSelector { 104 | margin-left: 1em; 105 | } 106 | 107 | .library .description { 108 | margin: 1em 0; 109 | } 110 | 111 | .library .extras .author { 112 | font-weight: bold; 113 | margin-right: 0.5em; 114 | } 115 | 116 | .library .extras a { 117 | color: #333; 118 | display: inline-block; 119 | margin-left: 0.25em; 120 | margin-right: 0.25em; 121 | opacity: 0.8; 122 | vertical-align: middle; 123 | } 124 | 125 | .library .extras a:hover { 126 | opacity: 1; 127 | } 128 | 129 | .library .files { 130 | -webkit-transition: 0.25s linear all; 131 | -moz-transition: 0.25s linear all; 132 | -ms-transition: 0.25s linear all; 133 | -o-transition: 0.25s linear all; 134 | transition: 0.25s linear all; 135 | max-height: 15em; 136 | overflow: auto; 137 | } 138 | 139 | .library .files.full { 140 | max-height: 30em; 141 | } 142 | 143 | .library .file:nth-child(odd) { 144 | background-color: #f9f9f9; 145 | } 146 | 147 | .library .file:nth-of-type(1) { 148 | border-top: 0.1em solid #ddd; 149 | } 150 | 151 | .library .file { 152 | border-bottom: 0.1em solid #ddd; 153 | } 154 | 155 | .library .show { 156 | text-align: center; 157 | cursor: pointer; 158 | } 159 | 160 | .loader { 161 | text-align: center; 162 | } 163 | 164 | .loader i { 165 | vertical-align: middle; 166 | } 167 | 168 | /* animations - see http://www.nganimate.org/ */ 169 | .extra.ng-enter, 170 | .extra.ng-leave { 171 | -webkit-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all; 172 | -moz-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all; 173 | -ms-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all; 174 | -o-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all; 175 | transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all; 176 | position: relative; 177 | display: block; 178 | overflow: hidden; 179 | -ms-text-overflow: clip; 180 | -o-text-overflow: clip; 181 | text-overflow: clip; 182 | white-space: nowrap; 183 | } 184 | 185 | .extra.ng-leave-active, 186 | .extra.ng-enter { 187 | height: 0; 188 | opacity: 0; 189 | } 190 | 191 | .extra.ng-leave, 192 | .extra.ng-enter-active { 193 | height: 100%; 194 | opacity: 1; 195 | } 196 | 197 | .lbdiv { 198 | margin-bottom: 5px; 199 | } 200 | 201 | /* override base color */ 202 | .github-fork-ribbon { 203 | background-color: #007200; 204 | } 205 | 206 | @media (min-width: 992px) { 207 | nav > ul { 208 | text-align: right; 209 | } 210 | 211 | .library .extras { 212 | text-align: right; 213 | } 214 | 215 | .library .description { 216 | margin: 1em 0.5em; 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /frontend/app/views/main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Woohoo! 4 | {{flash.message}} 5 |
6 |
7 | 8 |
9 |
10 | 11 |
12 |
13 | 14 |
15 |

Loading...

16 |
17 | 18 |
19 |
20 |
21 |
22 |
23 |

24 | {{library.name}} 25 |

26 | 27 | 32 |
33 | 34 |
35 |
36 | {{library.author}} 37 | 38 | 39 | 40 | 41 | 42 | 43 |
44 |
45 |
46 | 47 |
48 |

{{library.description}}

49 |
50 |
    51 |
  • 52 |
      53 |
    • 54 | {{getCDNLink(library.name, version, file)}} 55 | 58 |
    • 59 |
    60 |
  • 61 |
62 | 63 |
64 |
Show less
65 |
Show more
66 |
67 | 68 |
69 |
70 |
71 | -------------------------------------------------------------------------------- /frontend/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "osscdn", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "angular": "~1.2.14", 6 | "angular-animate": "~1.2.14", 7 | "angular-dropdowns": "~0.1.0", 8 | "angular-flash": "~0.1.12", 9 | "angular-route": "~1.2.14", 10 | "angular-ui-router": "0.2.7", 11 | "es5-shim": "~2.3.0", 12 | "github-fork-ribbon-css": "~0.1.0", 13 | "ng-clip": "~0.1.2", 14 | "zeroclipboard": "1.3.2", 15 | "angular-semver-sort": "~0.2.0" 16 | }, 17 | "devDependencies": { 18 | "angular-mocks": "~1.2.14", 19 | "angular-scenario": "~1.2.14" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "osscdn", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "express": "3.4.8", 7 | "h5bp": "0.1.2" 8 | }, 9 | "devDependencies": { 10 | "connect-livereload": "0.3.2", 11 | "grunt": "0.4.2", 12 | "grunt-concurrent": "0.5.0", 13 | "grunt-contrib-clean": "0.5.0", 14 | "grunt-contrib-concat": "0.3.0", 15 | "grunt-contrib-connect": "0.7.1", 16 | "grunt-contrib-copy": "0.5.0", 17 | "grunt-contrib-cssmin": "0.9.0", 18 | "grunt-contrib-htmlmin": "0.2.0", 19 | "grunt-contrib-jshint": "0.9.2", 20 | "grunt-contrib-uglify": "0.4.0", 21 | "grunt-contrib-watch": "0.6.1", 22 | "grunt-google-cdn": "0.4.0", 23 | "grunt-ngmin": "0.0.3", 24 | "grunt-open": "0.2.3", 25 | "grunt-rev": "0.1.0", 26 | "grunt-usemin": "2.1.0", 27 | "load-grunt-tasks": "0.4.0", 28 | "time-grunt": "0.2.10" 29 | }, 30 | "engines": { 31 | "node": ">=0.10.0" 32 | }, 33 | "subdomain": "osscdn", 34 | "scripts": { 35 | "start": "node serve.js", 36 | "test": "grunt test" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /frontend/serve.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* jshint browser:false */ 3 | 4 | 'use strict'; 5 | 6 | var path = require('path'); 7 | var express = require('express'); 8 | var h5bp = require('h5bp'); 9 | 10 | var staticMw = express['static']; 11 | 12 | 13 | main(); 14 | 15 | function main() { 16 | var app = express(); 17 | 18 | var port = process.env.PORT || 8000; 19 | var halfDay = 43200000; 20 | var day = halfDay * 2; 21 | var week = day * 7; 22 | 23 | app.configure(function() { 24 | app.set('port', port); 25 | 26 | app.use(express.logger('dev')); 27 | app.use(h5bp({root: path.join(__dirname, 'dist')})); 28 | app.use(express.compress()); 29 | 30 | app.use(app.router); 31 | }); 32 | 33 | app.configure('development', function() { 34 | app.use(express.errorHandler()); 35 | }); 36 | 37 | app.use('/dist/scripts', staticMw({maxAge: week})); 38 | app.use('/dist/styles', staticMw({maxAge: week})); 39 | 40 | app.use(staticMw(path.join(__dirname, 'dist'), { 41 | maxAge: halfDay 42 | })); 43 | 44 | app.use(function(req, res) { 45 | res.sendfile(__dirname + '/dist/index.html'); 46 | }); 47 | 48 | process.on('exit', terminator); 49 | 50 | ['SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGBUS', 51 | 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGPIPE', 'SIGTERM' 52 | ].forEach(function(element) { 53 | process.on(element, function() { terminator(element); }); 54 | }); 55 | 56 | app.listen(port, function() { 57 | console.log('%s: Node (version: %s) %s started on %d ...', Date(Date.now() ), process.version, process.argv[1], port); 58 | }); 59 | } 60 | 61 | function terminator(sig) { 62 | if(typeof sig === 'string') { 63 | console.log('%s: Received %s - terminating Node server ...', 64 | Date(Date.now()), sig); 65 | 66 | process.exit(1); 67 | } 68 | 69 | console.log('%s: Node server stopped.', Date(Date.now()) ); 70 | } 71 | --------------------------------------------------------------------------------