├── .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 | [](https://travis-ci.org/MaxCDN/osscdn) [](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 |
31 |
32 |
33 |
34 |
35 |
40 |
41 |
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 |
13 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
33 |
34 |
35 |
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 |
--------------------------------------------------------------------------------