├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── app.js ├── config.js ├── models └── sectionscrape.js ├── package.json ├── scraper ├── css-cssinfos │ ├── index.js │ ├── page.js │ └── run.sh ├── css-mdn │ └── scrape.js ├── dom-mdn │ └── scrape.js ├── html-mdn │ └── scrape.js ├── jquery │ └── scrape.js ├── js-mdn │ └── scrape.js ├── package.json ├── php │ └── phpext.js ├── python │ └── scrape.js └── xslt-w3 │ └── scrape.js ├── static ├── coda │ ├── css │ │ ├── bootstrap-responsive.css │ │ ├── bootstrap-responsive.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.min.css │ │ └── custom.css │ ├── index.html │ ├── js │ │ └── views │ │ │ └── fullwindow.js │ └── templates │ │ └── toc.html ├── css │ ├── bootstrap-responsive.css │ ├── bootstrap-responsive.min.css │ ├── bootstrap.css │ ├── bootstrap.min.css │ └── custom.css ├── data │ ├── css-mdn.json │ ├── dom-mdn.json │ ├── html-mdn.json │ ├── jquery.json │ ├── js-mdn.json │ ├── php-ext.json │ ├── python.json │ └── xslt-w3.json ├── dochub.appcache ├── favicon.ico ├── images │ ├── jquery_logo.png │ ├── mdn_logo.png │ ├── php_logo.png │ └── python_logo.gif ├── index.html ├── js │ ├── app.build.js │ ├── app.js │ ├── collections │ │ ├── jqentries.js │ │ ├── mdndomobjs.js │ │ ├── mdnhtmlelements.js │ │ ├── mdnjsobjs.js │ │ ├── mozdevcssprops.js │ │ ├── pageelements.js │ │ ├── pagescrapes.js │ │ ├── phpexts.js │ │ ├── pythonpages.js │ │ └── xsltpages.js │ ├── libs │ │ ├── backbone │ │ │ ├── backbone-full.js │ │ │ ├── backbone-min.js │ │ │ └── backbone.js │ │ ├── bootstrap │ │ │ ├── bootstrap-collapse.js │ │ │ ├── bootstrap-dropdown.js │ │ │ ├── bootstrap-modal.js │ │ │ ├── bootstrap-scrollspy.js │ │ │ └── bootstrap-twipsy.js │ │ ├── jquery │ │ │ ├── jquery-full.js │ │ │ ├── jquery-min.js │ │ │ ├── jquery-serialize.js │ │ │ ├── jquery.js │ │ │ └── jquery.tablesorter.min.js │ │ ├── require │ │ │ ├── domReady-full.js │ │ │ ├── domReady-min.js │ │ │ ├── require-min.js │ │ │ └── require.js │ │ ├── spin │ │ │ ├── spin.full.js │ │ │ ├── spin.js │ │ │ └── spin.min.js │ │ └── underscore │ │ │ ├── underscore-full.js │ │ │ ├── underscore-min.js │ │ │ └── underscore.js │ ├── main.js │ ├── models │ │ ├── pageelement.js │ │ ├── pagescrape.js │ │ └── sectionscrape.js │ ├── order.js │ ├── router.js │ ├── text.js │ └── views │ │ ├── fullwindow.js │ │ ├── jquerysearchresults.js │ │ ├── languageview.js │ │ ├── mozdevcssprop.js │ │ ├── pagescrapedlanguageview.js │ │ ├── pagesearchheader.js │ │ ├── pagesearchresults.js │ │ ├── pagetocbar.js │ │ ├── searchheader.js │ │ ├── searchresults.js │ │ ├── tocbar.js │ │ └── topnav.js └── templates │ ├── mdnpage.html │ ├── page.html │ ├── sectionscrape.html │ ├── toc.html │ ├── tocpagescraperesult.html │ ├── tocresult.html │ └── topnav.html └── web.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *~ 3 | .DS_Store 4 | \#*\# -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node web.js 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Runnning locally 2 | 3 | Run the server: 4 | 5 | npm install # downloads dependencies for web server 6 | node web.js 7 | 8 | Open http://localhost:5000/ in your browser. 9 | 10 | # Scraper 11 | 12 | The `static/data/` directory contains our scrape of the sites we get content from. Right now we don't update this very often. If you want the most up-to-date content, you can run the scrapers: 13 | 14 | cd scraper 15 | npm install # downloads dependencies for scrapers 16 | (cd css-mdn; node scrape.js) 17 | (cd html-mdn; node scrape.js) 18 | (cd js-mdn; node scrape.js) 19 | (cd dom-mdn; node scrape.js) 20 | (cd jquery; node scrape.js) 21 | 22 | # Notes 23 | 24 | How to use the r.js optimizer: 25 | 26 | node node_modules/requirejs/bin/r.js -o static/js/app.build.js 27 | 28 | Make sure the node server is serving the correct static folder 29 | (whether you want /static or /static-build). 30 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'express', 3 | 'module', 4 | 'path', 5 | './config', 6 | 'fs' 7 | ], function (express, module, path, config, fs) { 8 | 9 | var app = null; 10 | 11 | console.log(global.process.env); 12 | 13 | // can't serve cache manifest w/ express static since it doesn't set the header 14 | // correctly. so we'll load the file once, keep it in memory, serve it up manually 15 | var filename = module.uri; 16 | var manifestFilename = 'dochub.appcache'; 17 | var manifest = null; 18 | fs.readFile(path.dirname(filename) + '/static/' + manifestFilename, function(err,buf) { 19 | if(err) 20 | throw(err); 21 | manifest = { 22 | headers:{ 23 | 'Content-Type' : 'text/cache-manifest', 24 | 'Content-Length': buf.length, 25 | // 'Cache-Control' : 'public, max-age=' + 60*60 // do we need this caching? 26 | }, 27 | body: buf 28 | }; 29 | }); 30 | 31 | return { 32 | initialize: function() { 33 | if ( app ) return; 34 | 35 | console.log('INITIALIZING APP'); 36 | app = express.createServer(); 37 | app.listen(config.app_port); 38 | 39 | app.configure(function() { 40 | app.use(express.logger({ format: ':method :url :status' })); 41 | app.use(function(req, res, next) { 42 | // overrides for coda 43 | var coda_overrides = { 44 | '/': '/coda/', 45 | '/css/bootstrap-responsive.css': '/coda/css/bootstrap-responsive.css', 46 | '/css/bootstrap-responsive.min.css': '/coda/css/bootstrap-responsive.min.css', 47 | '/css/bootstrap.css': '/coda/css/bootstrap.css', 48 | '/css/bootstrap.min.css': '/coda/css/bootstrap.min.css', 49 | '/js/views/fullwindow.js': '/coda/js/views/fullwindow.js', 50 | '/templates/toc.html': '/coda/templates/toc.html' 51 | }; 52 | if (coda_overrides[req.url] && /Coda/.test(req.headers['user-agent'])) { 53 | req.url = coda_overrides[req.url]; 54 | } 55 | next(); 56 | }); 57 | // preempt static to serve up cache manifest 58 | app.get("/" + manifestFilename, function(req, res){ 59 | res.writeHead(200,manifest.headers); 60 | res.end(manifest.body); 61 | }); 62 | var staticDir = path.dirname(filename) + '/static'; 63 | console.log('initializing static: ' + staticDir); 64 | app.use(express.static(staticDir)); 65 | app.use(express.bodyParser()); 66 | app.use(express.methodOverride()); 67 | console.log('dochub now running on port ' + config.app_port); 68 | }); 69 | }, 70 | 71 | }; 72 | }); 73 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | 3 | return { 4 | host_dev: 'localhost', 5 | host_prod: 'instacss.herokuapp.com', 6 | host_staging: 'instacss-staging.herokuapp.com', 7 | 8 | app_port: global.process.env.PORT || 5000, 9 | environment: global.process.env.NODE_ENV || 'development', 10 | 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /models/sectionscrape.js: -------------------------------------------------------------------------------- 1 | define(['mongoose'], function(mongoose) { 2 | 3 | var Schema = mongoose.Schema; 4 | var ObjectId = Schema.ObjectId; 5 | 6 | var SectionScrape = new Schema({ 7 | id : ObjectId, 8 | url : String, 9 | title : { type: String, required: true }, 10 | sectionNames : [String], // ordering important 11 | sectionHTMLs : [String] 12 | }); 13 | 14 | return mongoose.model('SectionScrape',SectionScrape); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dochub-backend", 3 | "version": "1.0.1", 4 | "dependencies": { 5 | "requirejs": "1.0.x" 6 | , "express": "2.5.x" 7 | , "underscore": "1.3.x" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scraper/css-cssinfos/index.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | 3 | requirejs.config({ 4 | // Pass the top-level main.js/index.js require 5 | // function to requirejs so that node modules 6 | // are loaded relative to the top-level JS file. 7 | nodeRequire: require 8 | }); 9 | 10 | requirejs([ 11 | 'step', 12 | 'request', 13 | 'cheerio' 14 | ], function(step, request, cheerio) { 15 | 16 | step( 17 | 18 | function getIndexPage() { 19 | request({uri:'http://css-infos.net/properties/webkit'}, this); 20 | }, 21 | 22 | function parseIndexHTML(error, response, body) { 23 | if (error && response.statusCode !== 200) { 24 | console.log('ERROR loading ' + url) 25 | throw(error); 26 | } 27 | var $ = cheerio.load(body); // use cheerio to generate a new jquery obj 28 | linksToScrape = {}; 29 | $('ul.webkit a').each(function(index) { 30 | console.log('"' + $(this).text().trim() + '" "http://css-infos.net' + $(this).attr('href') + '"'); 31 | }); 32 | } 33 | ); 34 | 35 | }); 36 | -------------------------------------------------------------------------------- /scraper/css-cssinfos/page.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | 3 | requirejs.config({ 4 | // Pass the top-level main.js/index.js require 5 | // function to requirejs so that node modules 6 | // are loaded relative to the top-level JS file. 7 | nodeRequire: require 8 | }); 9 | 10 | requirejs([ 11 | '../../config', 12 | 'step', 13 | 'request', 14 | 'cheerio', 15 | 'mongoose', 16 | '../../models/mozdevcssprop', 17 | 'underscore' 18 | ], function(config, step, request, cheerio, mongoose, MDNProp, _) { 19 | 20 | console.log('---------'); 21 | console.log('scraping:',process.argv) 22 | 23 | var propName = process.argv[2]; 24 | var url = process.argv[3]; 25 | 26 | if ( propName[0] == '"' && propName[propName.length-1] == '"' ) 27 | propName = propName.substring(1,propName.length-1); 28 | if ( url[0] == '"' && url[url.length-1] == '"' ) 29 | url = url.substring(1,url.length-1); 30 | 31 | // connect to teh mongo db 32 | console.log(config.mongo_uri); 33 | 34 | var mdnprop = new MDNProp(); 35 | 36 | step( 37 | function connectToDB() { 38 | mongoose.connect(config.mongo_uri, this); 39 | }, 40 | 41 | function searchForExistingDoc(err) { 42 | if (err) 43 | throw err; 44 | else 45 | console.log('connected to ' + config.mongo_uri); 46 | 47 | MDNProp.findOne({title:propName},this); 48 | }, 49 | 50 | function processFind(err, doc) { 51 | if (err) 52 | throw err; 53 | 54 | if ( !doc ) { 55 | console.log('no existing doc, creating one'); 56 | return null; 57 | } else { 58 | console.log('removing doc and creating a new one'); 59 | doc.remove(this); 60 | } 61 | }, 62 | 63 | function processRemove(err) { 64 | if (err) 65 | throw err; 66 | request({uri:url}, this); 67 | }, 68 | 69 | function scrapeHTML(err, response, body) { 70 | if (err) { 71 | console.log('ERROR loading ' + url) 72 | throw err; 73 | } 74 | 75 | if ( response.statusCode % 300 < 100 ) { 76 | console.log('ERROR: redirected, bad url'); 77 | throw ""; 78 | } 79 | 80 | var $ = cheerio.load(body); 81 | mdnprop['type'] = 'css'; 82 | mdnprop['title'] = propName; 83 | mdnprop['sectionNames'] = []; 84 | mdnprop['sectionHTMLs'] = []; 85 | 86 | $('article section').each(function(i,elem) { 87 | var $section = cheerio.load($(this).html()); 88 | $section('section').attr('style','padding-top:0px'); 89 | var sectionName = ""; 90 | for ( var j = 1; j <= 4; j++ ) { 91 | var headers = $section('h' + j); 92 | if ( headers.length > 0 ) { 93 | sectionName = headers.text(); 94 | // replace w/ h3 95 | headers.after('

' + headers.text() +'

'); 96 | headers.remove(); 97 | break; 98 | } 99 | } 100 | console.log('found section ' + sectionName); 101 | mdnprop['sectionNames'].push(sectionName); 102 | mdnprop['sectionHTMLs'].push($section.html()); 103 | }); 104 | 105 | mdnprop.save(this); 106 | }, 107 | 108 | function postDBSave(err) { 109 | if (err) { 110 | console.log('ERROR could not save to db'); 111 | throw err; 112 | } 113 | 114 | console.log(mdnprop); 115 | console.log('successfully saved to db'); 116 | mongoose.disconnect(); 117 | } 118 | ); 119 | return; 120 | }); 121 | -------------------------------------------------------------------------------- /scraper/css-cssinfos/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | node index.js > index.txt && 4 | while read line; do 5 | node page.js $line 6 | done < index.txt 7 | -------------------------------------------------------------------------------- /scraper/css-mdn/scrape.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | 3 | requirejs([ 4 | 'step', 5 | 'spider', 6 | 'underscore', 7 | 'cheerio', 8 | '../../models/sectionscrape', 9 | 'path', 10 | 'fs' 11 | ], function(step, spider, _, cheerio, SectionScrape, path, fs) { 12 | 13 | var results = []; 14 | 15 | var spidey = spider(); 16 | 17 | // use this to visit all links on a page 18 | var visitLinks = function($) { 19 | $('a').each(function() { 20 | var href = $(this).attr('href'); 21 | spidey.get(href); 22 | }); 23 | }; 24 | 25 | // file where we'll dump the json 26 | var filename = path.dirname(__filename) + '/../../static/data/css-mdn.json'; 27 | console.log('dumping to ' + filename); 28 | var file = fs.openSync(filename,'w'); 29 | 30 | // main index of mdn's css docs 31 | spidey.route('developer.mozilla.org', '/en/CSS_Reference', function ($) { 32 | visitLinks($); 33 | }); 34 | 35 | var blacklist = [ 36 | 'https://developer.mozilla.org/en/CSS/CSS_Reference' 37 | , 'https://developer.mozilla.org/en/CSS/CSS_Reference/Property_Template' 38 | ]; 39 | 40 | // some urls redirect to other pages w/o changing the url (for example: https://developer.mozilla.org/en/CSS/-moz-scrollbars-none) 41 | // so in addition to not visiting the same url twice, keep this list to prevent visiting the same title twice 42 | var titles = []; 43 | 44 | spidey.route('developer.mozilla.org', /\/en\/CSS\/*/, function ($, url) { 45 | if (_.include(blacklist,url)) return; 46 | visitLinks($); 47 | 48 | console.log('---------'); 49 | console.log('scraping:',url); 50 | 51 | var title = $('article .page-title h1').text().trim(); 52 | if ( title === '' || title === null ) { 53 | console.log('ERROR: could not get title, skipping'); 54 | return; 55 | } else if ( _.indexOf(titles,title) !== -1 ) { 56 | console.log('WARNING: already scraped something with this title, skipping'); 57 | return; 58 | } 59 | 60 | console.log('title:',title); 61 | 62 | var scrapeData = new SectionScrape(); 63 | scrapeData['title'] = title; 64 | scrapeData['url'] = url; 65 | scrapeData['sectionNames'] = []; 66 | scrapeData['sectionHTMLs'] = []; 67 | 68 | // get all section ids 69 | var ids = _.map($('[id^=section_]'), function(div) { return div.attribs.id } ); 70 | if ( ids.length === 0 ) { 71 | console.log('WARNING: no sections...'); 72 | return; 73 | } 74 | 75 | for ( var i = 0; i < ids.length; i++ ) { 76 | // load the section html as its own jquery object 77 | var $section = cheerio.load($('[id^=' + ids[i] + ']').html()); 78 | 79 | // strip scripts 80 | $section('script').remove(); 81 | var sectionName = ""; 82 | 83 | // TODO find relative hrefs and turn them into absolute hrefs 84 | 85 | // find the title of the section--mdn isn't very consistent with what size headers they use 86 | _.each([1,2,3,4],function(h) { 87 | var headers = $section('h' + h); 88 | if ( sectionName === "" && headers.length > 0 ) { 89 | sectionName = headers.text(); 90 | } 91 | }); 92 | 93 | scrapeData['sectionNames'].push(sectionName); 94 | scrapeData['sectionHTMLs'].push($section.html()); 95 | } 96 | 97 | results.push(scrapeData.toJSON()); 98 | titles.push(title); 99 | }); 100 | 101 | // start 'er up 102 | spidey.get('https://developer.mozilla.org/en/CSS_Reference').log('info'); 103 | 104 | process.on('exit', function () { 105 | fs.writeSync(file,JSON.stringify(results,null,'\t')); 106 | console.log('DONE'); 107 | }); 108 | return; 109 | }); 110 | -------------------------------------------------------------------------------- /scraper/dom-mdn/scrape.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | 3 | requirejs([ 4 | 'step', 5 | 'spider', 6 | 'underscore', 7 | 'cheerio', 8 | '../../models/sectionscrape', 9 | 'path', 10 | 'fs' 11 | ], function(step, spider, _, cheerio, SectionScrape, path, fs) { 12 | 13 | var results = []; 14 | 15 | var spidey = spider(); 16 | 17 | // use this to visit all links on a page 18 | var visitLinks = function($) { 19 | $('a').each(function() { 20 | var href = $(this).attr('href'); 21 | spidey.get(href); 22 | }); 23 | }; 24 | 25 | // file where we'll dump the json 26 | var filename = path.dirname(__filename) + '/../../static/data/dom-mdn.json'; 27 | console.log('dumping to ' + filename); 28 | var file = fs.openSync(filename,'w'); 29 | 30 | // main index of mdn's dom docs 31 | spidey.route('developer.mozilla.org', '/en/Gecko_DOM_Reference', function ($) { 32 | visitLinks($); 33 | }); 34 | 35 | var blacklist = [ 36 | // 'https://developer.mozilla.org/en/Gecko_DOM_Reference' 37 | ]; 38 | 39 | // some urls redirect to other pages w/o changing the url (for example: https://developer.mozilla.org/en/CSS/-moz-scrollbars-none) 40 | // so in addition to not visiting the same url twice, keep this list to prevent visiting the same title twice 41 | var titles = []; 42 | 43 | // Need to lowercase titles that match anything in here, b/c MDN doc is inconsistent. 44 | var titleRegexes = [ /^Document\./, /^Element\./ ]; 45 | 46 | spidey.route('developer.mozilla.org', /\/en\/DOM\/*/, function ($, url) { 47 | if ( _.include(blacklist,url) ) return; 48 | visitLinks($); 49 | 50 | console.log('---------'); 51 | console.log('scraping:',url); 52 | 53 | var title = $('article .page-title h1').text().trim(); 54 | if ( title === '' || title === null ) { 55 | console.log('ERROR: could not get title, skipping'); 56 | return; 57 | } else if ( _.include(titles,title) ) { 58 | console.log('WARNING: already scraped something with this title, skipping'); 59 | return; 60 | } 61 | 62 | var titleMatches = _.filter(titleRegexes, function(regex) { 63 | return regex.test(title); 64 | }); 65 | 66 | if (titleMatches.length > 0) { 67 | title = title[0].toLowerCase() + title.substr(1); 68 | } 69 | 70 | console.log('title:',title); 71 | 72 | var scrapeData = new SectionScrape(); 73 | scrapeData['title'] = title; 74 | scrapeData['url'] = url; 75 | scrapeData['sectionNames'] = []; 76 | scrapeData['sectionHTMLs'] = []; 77 | 78 | // get all section ids 79 | var ids = _.map($('[id^=section_]'), function(div) { return div.attribs.id } ); 80 | if ( ids.length === 0 ) { 81 | console.log('WARNING: no sections...'); 82 | return; 83 | } 84 | 85 | for ( var i = 0; i < ids.length; i++ ) { 86 | // load the section html as its own jquery object 87 | var $section = cheerio.load($('[id^=' + ids[i] + ']').html()); 88 | 89 | // strip scripts 90 | $section('script').remove(); 91 | var sectionName = ""; 92 | 93 | // TODO find relative hrefs and turn them into absolute hrefs 94 | 95 | // find the title of the section--mdn isn't very consistent with what size headers they use 96 | _.each([1,2,3,4],function(h) { 97 | var headers = $section('h' + h); 98 | if ( sectionName === "" && headers.length > 0 ) { 99 | sectionName = headers.text(); 100 | } 101 | }); 102 | 103 | scrapeData['sectionNames'].push(sectionName); 104 | scrapeData['sectionHTMLs'].push($section.html()); 105 | } 106 | 107 | results.push(scrapeData.toJSON()); 108 | titles.push(title); 109 | }); 110 | 111 | // start 'er up 112 | spidey.get('https://developer.mozilla.org/en/Gecko_DOM_Reference').log('info'); 113 | 114 | process.on('exit', function () { 115 | fs.writeSync(file,JSON.stringify(results,null,'\t')); 116 | console.log('DONE'); 117 | }); 118 | return; 119 | }); 120 | -------------------------------------------------------------------------------- /scraper/html-mdn/scrape.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | 3 | requirejs([ 4 | 'step', 5 | 'spider', 6 | 'underscore', 7 | 'cheerio', 8 | '../../models/sectionscrape', 9 | 'path', 10 | 'fs' 11 | ], function(step, spider, _, cheerio, SectionScrape, path, fs) { 12 | 13 | var results = []; 14 | 15 | var spidey = spider(); 16 | 17 | // use this to visit all links on a page 18 | var visitLinks = function($) { 19 | $('a').each(function() { 20 | var href = $(this).attr('href'); 21 | spidey.get(href); 22 | }); 23 | }; 24 | 25 | // file where we'll dump the json 26 | var filename = path.dirname(__filename) + '/../../static/data/html-mdn.json'; 27 | console.log('dumping to ' + filename); 28 | var file = fs.openSync(filename,'w'); 29 | 30 | // main index of mdn's html docs 31 | spidey.route('developer.mozilla.org', '/en/HTML/Element', function ($) { 32 | visitLinks($); 33 | }); 34 | 35 | var blacklist = [ 36 | // 'https://developer.mozilla.org/en/HTML/Element' 37 | ]; 38 | 39 | // some urls redirect to other pages w/o changing the url (for example: https://developer.mozilla.org/en/CSS/-moz-scrollbars-none) 40 | // so in addition to not visiting the same url twice, keep this list to prevent visiting the same title twice 41 | var titles = []; 42 | 43 | spidey.route('developer.mozilla.org', /\/en\/HTML\/Element\/*/, function ($, url) { 44 | if ( _.indexOf(blacklist,url) !== -1 ) return; 45 | visitLinks($); 46 | 47 | console.log('---------'); 48 | console.log('scraping:',url); 49 | 50 | var title = $('article .page-title h1').text().trim(); 51 | if ( title === '' || title === null ) { 52 | console.log('ERROR: could not get title, skipping'); 53 | return; 54 | } else if ( _.indexOf(titles,title) !== -1 ) { 55 | console.log('WARNING: already scraped something with this title, skipping'); 56 | return; 57 | } 58 | 59 | console.log('title:',title); 60 | 61 | var scrapeData = new SectionScrape(); 62 | scrapeData['title'] = title; 63 | scrapeData['url'] = url; 64 | scrapeData['sectionNames'] = []; 65 | scrapeData['sectionHTMLs'] = []; 66 | 67 | // get all section ids 68 | var ids = _.map($('[id^=section_]'), function(div) { return div.attribs.id } ); 69 | if ( ids.length === 0 ) { 70 | console.log('WARNING: no sections...'); 71 | return; 72 | } 73 | 74 | for ( var i = 0; i < ids.length; i++ ) { 75 | // load the section html as its own jquery object 76 | var $section = cheerio.load($('[id^=' + ids[i] + ']').html()); 77 | 78 | // strip scripts 79 | $section('script').remove(); 80 | var sectionName = ""; 81 | 82 | // TODO find relative hrefs and turn them into absolute hrefs 83 | 84 | // find the title of the section--mdn isn't very consistent with what size headers they use 85 | _.each([1,2,3,4],function(h) { 86 | var headers = $section('h' + h); 87 | if ( sectionName === "" && headers.length > 0 ) { 88 | sectionName = headers.text(); 89 | } 90 | }); 91 | 92 | scrapeData['sectionNames'].push(sectionName); 93 | scrapeData['sectionHTMLs'].push($section.html()); 94 | } 95 | 96 | results.push(scrapeData.toJSON()); 97 | titles.push(title); 98 | }); 99 | 100 | // start 'er up 101 | spidey.get('https://developer.mozilla.org/en/HTML/Element').log('info'); 102 | 103 | process.on('exit', function () { 104 | fs.writeSync(file,JSON.stringify(results,null,'\t')); 105 | console.log('DONE'); 106 | }); 107 | return; 108 | }); 109 | -------------------------------------------------------------------------------- /scraper/jquery/scrape.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | 3 | requirejs([ 4 | 'step', 5 | 'spider', 6 | 'underscore', 7 | 'cheerio', 8 | '../../models/sectionscrape', 9 | 'path', 10 | 'fs' 11 | ], function(step, spider, _, cheerio, SectionScrape, path, fs) { 12 | 13 | var results = []; 14 | 15 | var spidey = spider(); 16 | 17 | // file where we'll dump the json 18 | var filename = path.dirname(__filename) + '/../../static/data/jquery.json'; 19 | console.log('dumping to ' + filename); 20 | var file = fs.openSync(filename,'w'); 21 | 22 | // main index 23 | spidey.route('api.jquery.com', '/', function ($) { 24 | $('#content a.title-link').each(function() { 25 | spidey.get($(this).attr('href')); 26 | }); 27 | }); 28 | 29 | var blacklist = [ 30 | ]; 31 | 32 | // protect against different urls but same content (=> same content) 33 | var titles = []; 34 | 35 | // Change '.add()' to 'add' 36 | var jQueryFunctionNameRegex = /^\.?(\S+)\(\)$/; 37 | var defunctionfy = function(txt) { 38 | var result = txt.match(jQueryFunctionNameRegex); 39 | if (result === null) { 40 | return txt; 41 | } else { 42 | return result[1]; 43 | } 44 | } 45 | 46 | spidey.route('api.jquery.com', '\/*', function ($, url) { 47 | if ( _.indexOf(blacklist,url) !== -1 ) return; 48 | 49 | console.log('---------'); 50 | console.log('scraping:',url); 51 | 52 | var title = defunctionfy($('h1.jq-clearfix').text().trim()); 53 | if ( title === '' || title === null ) { 54 | console.log('ERROR: could not get title, skipping'); 55 | return; 56 | } else if ( _.indexOf(titles,title) !== -1 ) { 57 | console.log('WARNING: already scraped something with this title, skipping'); 58 | return; 59 | } 60 | 61 | console.log('title:',title); 62 | 63 | var scrapeData = new SectionScrape(); 64 | scrapeData['title'] = title; 65 | scrapeData['url'] = url; 66 | scrapeData['sectionNames'] = [title]; 67 | scrapeData['sectionHTMLs'] = 68 | _.map(_.toArray($('div.entry-content').children()).slice(1), 69 | function(obj) { 70 | return $(obj).html(); 71 | }); 72 | 73 | results.push(scrapeData.toJSON()); 74 | titles.push(title); 75 | }); 76 | 77 | // start 'er up 78 | spidey.get('http://api.jquery.com/').log('info'); 79 | 80 | process.on('exit', function () { 81 | fs.writeSync(file,JSON.stringify(results,null,'\t')); 82 | console.log('DONE'); 83 | }); 84 | 85 | return; 86 | }); 87 | -------------------------------------------------------------------------------- /scraper/js-mdn/scrape.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | 3 | requirejs([ 4 | 'spider', 5 | 'underscore', 6 | 'cheerio', 7 | '../../models/sectionscrape', 8 | 'path', 9 | 'fs' 10 | ], function(spider, _, cheerio, SectionScrape, path, fs) { 11 | 12 | var results = []; 13 | 14 | var spidey = spider(); 15 | 16 | // use this to visit all links on a page 17 | var visitLinks = function($) { 18 | $('a').each(function() { 19 | var href = $(this).attr('href'); 20 | spidey.get(href); 21 | }); 22 | }; 23 | 24 | // file where we'll dump the json 25 | var filename = path.dirname(__filename) + '/../../static/data/js-mdn.json'; 26 | console.log('dumping to ' + filename); 27 | var file = fs.openSync(filename,'w'); 28 | 29 | // main index of mdn's js docs 30 | spidey.route('developer.mozilla.org', '/en/JavaScript/Reference', function ($) { 31 | visitLinks($); 32 | }); 33 | 34 | var blacklist = [ 35 | // 'https://developer.mozilla.org/en/JavaScript/Reference' 36 | ]; 37 | 38 | // some urls redirect to other pages w/o changing the url (for example: https://developer.mozilla.org/en/CSS/-moz-scrollbars-none) 39 | // so in addition to not visiting the same url twice, keep this list to prevent visiting the same title twice 40 | var titles = []; 41 | 42 | spidey.route('developer.mozilla.org', /(\/en\/JavaScript_typed_arrays|\/en\/JavaScript\/Reference\/(Global_Objects|Statement|Operators))\/*/, function ($, url) { 43 | if ( _.include(blacklist, url) ) return; 44 | visitLinks($); 45 | 46 | console.log('---------'); 47 | console.log('scraping:',url); 48 | 49 | var title = $('article .page-title h1').text().trim(); 50 | if ( /Global_Objects/.test(url) && url.split('Global_Objects/').length > 1) 51 | title = url.split('Global_Objects/')[1].replace(/\//g, '.'); 52 | if ( title === '' || title === null ) { 53 | console.log('ERROR: could not get title, skipping'); 54 | return; 55 | } else if ( _.include(titles, title) ) { 56 | console.log('WARNING: already scraped something with this title, skipping'); 57 | return; 58 | } 59 | 60 | console.log('title:',title); 61 | 62 | var scrapeData = new SectionScrape(); 63 | scrapeData['title'] = title; 64 | scrapeData['url'] = url; 65 | scrapeData['sectionNames'] = []; 66 | scrapeData['sectionHTMLs'] = []; 67 | 68 | // get all section ids 69 | var ids = _.map($('[id^=section_]'), function(div) { return div.attribs.id } ); 70 | if ( ids.length === 0 ) { 71 | console.log('WARNING: no sections...'); 72 | return; 73 | } 74 | 75 | for ( var i = 0; i < ids.length; i++ ) { 76 | // load the section html as its own jquery object 77 | var $section = cheerio.load($('[id^=' + ids[i] + ']').html()); 78 | 79 | // strip scripts 80 | $section('script').remove(); 81 | var sectionName = ""; 82 | 83 | // TODO find relative hrefs and turn them into absolute hrefs 84 | 85 | // find the title of the section--mdn isn't very consistent with what size headers they use 86 | _.each([1,2,3,4],function(h) { 87 | var headers = $section('h' + h); 88 | if ( sectionName === "" && headers.length > 0 ) { 89 | sectionName = headers.text(); 90 | } 91 | }); 92 | 93 | scrapeData['sectionNames'].push(sectionName); 94 | scrapeData['sectionHTMLs'].push($section.html()); 95 | } 96 | 97 | results.push(scrapeData.toJSON()); 98 | titles.push(title); 99 | }); 100 | 101 | // start 'er up 102 | spidey.get('https://developer.mozilla.org/en/JavaScript/Reference').log('info'); 103 | 104 | process.on('exit', function () { 105 | fs.writeSync(file,JSON.stringify(results,null,'\t')); 106 | console.log('DONE'); 107 | }); 108 | return; 109 | }); 110 | -------------------------------------------------------------------------------- /scraper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "instacss-scraper", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "requirejs": ">=1.0.0", 6 | "mongoose": ">=2.3.9", 7 | "underscore": ">=1.2.1", 8 | "request": ">=2.2.5", 9 | "cheerio": ">=0.2.2", 10 | "step": ">=0.0.5", 11 | "commander": ">=0.4.1", 12 | "spider": "https://github.com/rgarcia/spider/tarball/master" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /scraper/php/phpext.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | 3 | requirejs([ 4 | 'spider', 5 | 'underscore', 6 | '../../models/sectionscrape', 7 | 'path', 8 | 'fs' 9 | ], function(spider, _, SectionScrape, path, fs) { 10 | 11 | // c.f. http://docs.jquery.com/Frequently_Asked_Questions#How_do_I_select_an_element_by_an_ID_that_has_characters_used_in_CSS_notation.3F 12 | function jq(myid) { 13 | return '#' + myid; 14 | } 15 | var extractJqueryId = function(url) { 16 | var remaining = url.split('manual/en/')[1]; 17 | return jq(remaining.replace('.php', '')); 18 | } 19 | 20 | var desiredExts = _.map([ 21 | "Apache", 22 | "Arrays", 23 | "Bzip2", 24 | "Calendar", 25 | "Classkit", 26 | "Class/Objects", 27 | "COM", 28 | "cURL", 29 | "Date/Time", 30 | "DBA", 31 | "DB++", 32 | "Direct IO", 33 | "Directories", 34 | "DOM", 35 | ".NET", 36 | "Eio", 37 | "Error Handling", 38 | "Program execution", 39 | "Filesystem", 40 | "FTP", 41 | "Function Handling", 42 | "Gearman", 43 | "GeoIP", 44 | "Hash", 45 | "HTTP", 46 | "ID3", 47 | "IIS", 48 | "IMAP", 49 | "Ingres", 50 | "Java", 51 | "JSON", 52 | "LDAP", 53 | "Libeventlibxml", 54 | "Mail", 55 | "Math", 56 | "Mailparse", 57 | "Memcache", 58 | "Memcached", 59 | "Mongo", 60 | "MySQL", 61 | "MySQLi", 62 | "OAuth", 63 | "OpenSSL", 64 | "Output Control", 65 | "PostgreSQL", 66 | "POSIX", 67 | "Readline", 68 | "SimpleXML", 69 | "SQLite", 70 | "SQLite3", 71 | "SQLSRV", 72 | "SSH2", 73 | "Streams", 74 | "Strings", 75 | "SWF", 76 | "Sybase", 77 | "ODBC", 78 | "URLs", 79 | "Variable handling", 80 | "WinCache", 81 | "XML-RPC", 82 | "XSL", 83 | "Yaml", 84 | "Zip", 85 | "Zlib", 86 | ], function(str) { 87 | return str.toLowerCase(); 88 | }); 89 | 90 | // File where we'll dump the json 91 | var filename = path.dirname(__filename) + '/../../static/data/php-ext.json'; 92 | console.log('[Dumping to ' + filename + '.]'); 93 | var file = fs.openSync(filename, 'w'); 94 | 95 | // The list of results we're going to put into the file 96 | var results = []; 97 | 98 | var crawlfn = function($) { 99 | return function() { 100 | var href = 'http://www.php.net/manual/en/' + $(this).attr('href'); 101 | jsSpider.get(href); 102 | }; 103 | }; 104 | 105 | // Config the crawler 106 | var jsSpider = spider(); 107 | jsSpider.route('www.php.net', '/manual/en/extensions.alphabetical.php', function ($, url) { 108 | console.log(url); 109 | 110 | var links = _.filter($(jq('extensions.alphabetical') + ' a'), function(link) { 111 | var txt = $(link).text().trim().toLowerCase(); 112 | return desiredExts.indexOf(txt) > -1; 113 | }); 114 | $(links).each(crawlfn($)); 115 | }).route('www.php.net', '/manual/en/book*', function ($, url) { 116 | console.log(url); 117 | var jqId = extractJqueryId(url); 118 | $(jqId + ' a').each(crawlfn($)); 119 | }).route('www.php.net', '/manual/en/ref*', function ($, url) { 120 | console.log(url); 121 | var jqId = extractJqueryId(url); 122 | $(jqId + ' a').each(crawlfn($)); 123 | }); 124 | 125 | var subroutes = [ 126 | 'function*', 127 | ]; 128 | for (var i = 0; i < subroutes.length; ++i) { 129 | // Need this closure to get the subroute 130 | (function() { 131 | var subroute = subroutes[i]; 132 | jsSpider.route('www.php.net', '/manual/en/' + subroute, function ($, url) { 133 | var title = $('h1.refname').text().trim(); 134 | if (title === '') return; 135 | 136 | // Extract everything after the subroute and lowercase first letter 137 | var remaining = url.split('manual/en/')[1]; 138 | console.log('[Scraping ' + url + ' :: ' + title + ']'); 139 | 140 | // Create new obj to save 141 | var scrapeData = new SectionScrape(); 142 | 143 | scrapeData['title'] = title; 144 | scrapeData['url'] = url; 145 | scrapeData['sectionNames'] = [ title ]; 146 | scrapeData['sectionHTMLs'] = [ $(extractJqueryId(url)).html() ]; 147 | 148 | results.push(scrapeData.toJSON()); 149 | 150 | // Continue crawling 151 | $('#pageText a').each(crawlfn($)); 152 | }); 153 | })(); 154 | } 155 | 156 | // Start the crawler 157 | jsSpider.get('http://www.php.net/manual/en/extensions.alphabetical.php').log('info'); 158 | 159 | // Write on exit 160 | process.on('exit', function() { 161 | fs.writeSync(file, JSON.stringify(results, null, '\t')); 162 | console.log('[Done.]'); 163 | }); 164 | 165 | return; 166 | }); 167 | 168 | -------------------------------------------------------------------------------- /scraper/python/scrape.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | 3 | requirejs([ 4 | 'spider', 5 | 'underscore', 6 | 'cheerio', 7 | 'path', 8 | 'fs' 9 | ], function(spider, _, cheerio, path, fs) { 10 | 11 | var scrapeData = []; 12 | var spidey = spider(); 13 | 14 | // file where we'll dump the json 15 | var filename = path.dirname(__filename) + '/../../static/data/python.json'; 16 | console.log('[Dumping to ' + filename + '.]'); 17 | var file = fs.openSync(filename, 'w'); 18 | 19 | var rootUrls = [ 20 | 'http://docs.python.org/reference/', 21 | 'http://docs.python.org/library/', 22 | ]; 23 | 24 | spidey.route('docs.python.org', '/reference/', function ($, url) { 25 | // TODO: 26 | console.log('Skipping ' + url); 27 | }); 28 | 29 | 30 | spidey.route('docs.python.org', '/library/', function ($, url) { 31 | console.log('---------'); 32 | console.log('scraping:', url); 33 | 34 | var regex = /^((\d+)\.(\d+))\.\s+.+$/; 35 | var linksToFollow = _.filter($('a.reference'), function(elt) { 36 | var result = regex.exec($(elt).text().trim()); 37 | if (result !== null) { 38 | // Only take certain sections 39 | var sectionNum = parseInt(result[2], 10); 40 | return (5 <= sectionNum) && (sectionNum <= 31); 41 | } 42 | return false; 43 | }); 44 | _.each(linksToFollow, function(elt) { 45 | spidey.get(url + $(elt).attr('href')); 46 | }); 47 | }); 48 | 49 | spidey.route('docs.python.org', '/library/*', function ($, url) { 50 | 51 | // Some links are just hashtags to pages we've already scraped. 52 | // Skip these. 53 | if (url.indexOf('#') !== -1) { 54 | return false; 55 | } 56 | 57 | // Change the html by taking all links that referred to by searchableItems 58 | // and prepending the title. This is to avoid collisions if more than one page is visible. 59 | var title = $($($('h1').children()[0]).children()[0]).text(); 60 | 61 | // Get the list of all elements from the page. 62 | // class declarations 63 | var searchableItems = _.map( 64 | _.filter($('dl.class'), function(elt) { 65 | var $firstChild = $($(elt).children()[0]); 66 | return $firstChild.attr('id'); 67 | }), function(elt) { 68 | var $firstChild = $($(elt).children()[0]); 69 | return { 70 | 'name' : $firstChild.attr('id'), 71 | 'domId' : $firstChild.attr('id') 72 | }; 73 | }); 74 | // class method declarations 75 | var methods = _.toArray($('dl.function')).concat(_.toArray($('dl.method'))); 76 | searchableItems = searchableItems.concat(searchableItems, 77 | _.map(_.filter(methods, function(elt) { 78 | var $firstChild = $($(elt).children()[0]); 79 | return $firstChild.attr('id'); 80 | }), function(elt) { 81 | var $firstChild = $($(elt).children()[0]); 82 | return { 83 | 'name' : $firstChild.attr('id'), 84 | 'domId' : $firstChild.attr('id') 85 | }; 86 | })); 87 | // class variable declarations 88 | searchableItems = searchableItems.concat(searchableItems, 89 | _.map(_.filter($('dl.class'), function(elt) { 90 | var $firstChild = $($(elt).children()[0]); 91 | return $firstChild.attr('id'); 92 | }), function(elt) { 93 | var $firstChild = $($(elt).children()[0]); 94 | return { 95 | 'name' : $firstChild.attr('id'), 96 | 'domId' : $firstChild.attr('id') 97 | }; 98 | })); 99 | 100 | console.log(url + ' :: ' + title); 101 | console.log(searchableItems.length); 102 | searchableItems = _.uniq(searchableItems.sort(function(a, b) { 103 | return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); 104 | }), true, function(item) { 105 | return item.name; 106 | }); 107 | console.log(searchableItems.length); 108 | for (var i = 0; i < searchableItems.length; ++i) { 109 | var domId = searchableItems[i].domId; 110 | var newId = title + '_' + domId; 111 | 112 | // Now go through and change: 113 | // (1) All internal links that ref this. 114 | $('a[href=#' + domId + ']').each(function(i, elt) { 115 | $(elt).attr('href', '#' + newId); 116 | }); 117 | // (2) Ihe id of the element being referenced. 118 | $('#' + domId).attr('id', newId); 119 | 120 | // Do this after we modify the element. 121 | searchableItems[i].domId = newId; 122 | } 123 | 124 | // If a link href starts with a hashtag, leave it. If it is a relative url, 125 | // then make it absolute. 126 | var baseUrl = url.substr(0, url.lastIndexOf('/') + 1); 127 | $('a').each(function(i, elt) { 128 | var $elt = $(elt); 129 | var href = $elt.attr('href'); 130 | if (href.charAt(0) != '#' && href.indexOf('://') === -1) { 131 | $elt.attr('href', baseUrl + href); 132 | } 133 | }); 134 | 135 | // Change img srcs from relative to absolute 136 | $('img').each(function(i, img) { 137 | var src = img.attribs.src; 138 | if (src.indexOf('http') === -1) { 139 | img.attribs.src = baseUrl + src; 140 | // console.log(img.attribs.src); 141 | } 142 | }); 143 | 144 | scrapeData.push({ 145 | url : url, 146 | title : title, 147 | html : _.map($('div.body').children(), function(obj) { 148 | return $(obj).html(); 149 | }).join(''), 150 | searchableItems : searchableItems 151 | }); 152 | }); 153 | 154 | // Start 'er up 155 | _.each(rootUrls, function(url) { 156 | spidey.get(url).log('info'); 157 | }); 158 | 159 | process.on('exit', function () { 160 | fs.writeSync(file, JSON.stringify(scrapeData, null, '\t')); 161 | console.log('DONE'); 162 | }); 163 | 164 | return; 165 | }); 166 | 167 | -------------------------------------------------------------------------------- /scraper/xslt-w3/scrape.js: -------------------------------------------------------------------------------- 1 | var requirejs = require('requirejs'); 2 | 3 | requirejs([ 4 | 'spider', 5 | 'underscore', 6 | 'cheerio', 7 | 'path', 8 | 'fs' 9 | ], function(spider, _, cheerio, path, fs) { 10 | 11 | var scrapeData = []; 12 | var spidey = spider(); 13 | 14 | // file where we'll dump the json 15 | var filename = path.dirname(__filename) + '/../../static/data/xslt-w3.json'; 16 | console.log('[Dumping to ' + filename + '.]'); 17 | var file = fs.openSync(filename, 'w'); 18 | 19 | var urls = [ 20 | 'http://www.w3.org/TR/xslt20/', 21 | 'http://www.w3.org/TR/xpath-functions/' 22 | ]; 23 | var urlToTitleMap = {}; 24 | urlToTitleMap[urls[0]] = 'XSLT'; 25 | urlToTitleMap[urls[1]] = 'XPath'; 26 | 27 | var getSearchableItems = function(url, $) { 28 | var filterSelector; 29 | var filterFunc; 30 | if (url === urls[0]) { 31 | filterSelector = 'a[href|="#element"]'; 32 | filterFunc = function(elt) { 33 | return $(elt).text().trim().indexOf('xsl:') === 0; 34 | }; 35 | } else if (url === urls[1]) { 36 | filterSelector = 'a[href|="#func"]'; 37 | filterFunc = function(elt) { 38 | var children = $(elt).children(); 39 | if (children.length === 0) { 40 | return false; 41 | } 42 | var txt = $(children[0]).text().trim(); 43 | return txt.indexOf(' ') === -1 && txt.indexOf('(') === -1 && 44 | (txt.indexOf('op:') === 0 || txt.indexOf('fn:') === 0); 45 | }; 46 | } else { 47 | throw "What the heck we scrapin'??: " + url; 48 | } 49 | 50 | var searchableItems = _.map( 51 | _.filter($(filterSelector), filterFunc), function(elt) { 52 | var $elt = $(elt); 53 | return { 54 | // 'name' : $elt.text().split(':')[1], 55 | 'name' : $elt.text(), 56 | 'domId' : $elt.attr('href').substr(1) 57 | }; 58 | }); 59 | 60 | console.log(searchableItems.length); 61 | searchableItems = _.uniq(searchableItems.sort(function(a, b) { 62 | return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); 63 | }), true, function(item) { 64 | return item.name; 65 | }); 66 | console.log(searchableItems.length); 67 | 68 | return searchableItems; 69 | } 70 | 71 | // W3's single-page xslt docs 72 | spidey.route('www.w3.org', '*', function ($, url) { 73 | 74 | console.log('---------'); 75 | console.log('scraping:', url); 76 | 77 | 78 | // Get the list of all elements (searchable items) from Appendix D 79 | var searchableItems = getSearchableItems(url, $); 80 | 81 | // Change the html by taking all links that referred to by searchableItems 82 | // and prepending the title (which is this particular case is 'XSLT') 83 | // This is to avoid collisions if more than one page is visible (although 84 | // this doesn't apply to XSLT b/c there's only one page, but it could to Python). 85 | var title = urlToTitleMap[url]; 86 | for (var i = 0; i < searchableItems.length; ++i) { 87 | var domId = searchableItems[i].domId; 88 | var newId = title + '_' + domId; 89 | 90 | var $elt = $('a[name=' + domId + ']'); 91 | $elt.attr('name', newId); 92 | $elt.attr('id', newId); 93 | 94 | // Now go through and change all internal links that href to this. 95 | $('a[href=#' + domId + ']').each(function(i, elt) { 96 | $(elt).attr('href', '#' + newId); 97 | }); 98 | 99 | // Do this after we modify the element. 100 | searchableItems[i].domId = newId; 101 | } 102 | 103 | // Each without an href attribute is used as an anchor on this page. 104 | // It has . Modify it so it becomes 105 | // . This is because the client 106 | // will select anchors by id, not by name. 107 | $('a').each(function(i, elt) { 108 | var $elt = $(elt); 109 | if ($elt.attr('name') && !$elt.attr('id')) { 110 | $elt.attr('id', $elt.attr('name')); 111 | } 112 | }); 113 | 114 | // Change img srcs from relative to absolute 115 | $('img').each(function(i, img) { 116 | var src = img.attribs.src; 117 | console.log(src); 118 | if (src.indexOf('http') === -1) { 119 | img.attribs.src = url + src; 120 | console.log(img.attribs.src); 121 | } 122 | }); 123 | 124 | scrapeData.push({ 125 | url : url, 126 | title : title, 127 | html : _.map($('body').children(), function(obj) { 128 | return $(obj).html(); 129 | }).join(''), 130 | searchableItems : searchableItems 131 | }); 132 | }); 133 | 134 | // Start 'er up 135 | _.each(urls, function(url) { 136 | spidey.get(url).log('info'); 137 | }); 138 | 139 | process.on('exit', function () { 140 | fs.writeSync(file, JSON.stringify(scrapeData, null, '\t')); 141 | console.log('DONE'); 142 | }); 143 | 144 | return; 145 | }); 146 | 147 | -------------------------------------------------------------------------------- /static/coda/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | DocHub | Instant Documentation Search 13 | 14 | 15 | 18 | 19 | 20 | 21 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 59 | 60 | 61 | 62 | 64 |
65 |
66 | 67 | 68 | 69 | 72 | 75 | 76 | 77 | 80 | 83 | 84 | 85 | 88 | 162 | 163 | 164 | 167 | 170 | 171 | 172 |
73 | CSS/HTML/JavaScript/DOM data is from the Mozilla Developer Network. 74 |
81 | jQuery data is from http://api.jquery.com. Version is 1.7. 82 |
89 | PHP data is from http://www.php.net/manual/en/extensions.alphabetical.php. 90 |

Currently supported PHP extensions:

91 |

92 |

    93 |
  • Apache
  • 94 |
  • Arrays
  • 95 |
  • Bzip2
  • 96 |
  • Calendar
  • 97 |
  • Classkit
  • 98 |
  • Class/Objects
  • 99 |
  • COM
  • 100 |
  • cURL
  • 101 |
  • Date/Time
  • 102 |
  • DBA
  • 103 |
  • DB++
  • 104 |
  • Direct IO
  • 105 |
  • Directories
  • 106 |
  • DOM
  • 107 |
  • .NET
  • 108 |
  • Eio
  • 109 |
  • Error Handling
  • 110 |
  • Program execution
  • 111 |
  • Filesystem
  • 112 |
  • FTP
  • 113 |
  • Function Handling
  • 114 |
  • Gearman
  • 115 |
  • GeoIP
  • 116 |
  • Hash
  • 117 |
  • HTTP
  • 118 |
  • ID3
  • 119 |
  • IIS
  • 120 |
  • IMAP
  • 121 |
  • Ingres
  • 122 |
  • Java
  • 123 |
  • JSON
  • 124 |
  • LDAP
  • 125 |
  • Libevent
  • 126 |
  • libxml
  • 127 |
  • Mail
  • 128 |
  • Math
  • 129 |
  • Mailparse
  • 130 |
  • Memcache
  • 131 |
  • Memcached
  • 132 |
  • Mongo
  • 133 |
  • MySQL
  • 134 |
  • MySQLi
  • 135 |
  • OAuth
  • 136 |
  • OpenSSL
  • 137 |
  • Output Control
  • 138 |
  • PostgreSQL
  • 139 |
  • POSIX
  • 140 |
  • Readline
  • 141 |
  • SimpleXML
  • 142 |
  • SQLite
  • 143 |
  • SQLite3
  • 144 |
  • SQLSRV
  • 145 |
  • SSH2
  • 146 |
  • Streams
  • 147 |
  • Strings
  • 148 |
  • SWF
  • 149 |
  • Sybase
  • 150 |
  • ODBC
  • 151 |
  • URLs
  • 152 |
  • Variable handling
  • 153 |
  • WinCache
  • 154 |
  • XML-RPC
  • 155 |
  • XSL
  • 156 |
  • Yaml
  • 157 |
  • Zip
  • 158 |
  • Zlib
  • 159 |
160 |

161 |
168 | Python data is from http://docs.python.org/library/. Version is 2.7. 169 |
173 |
174 |
175 |
176 | 183 |
184 |
185 |
186 |
187 | 188 | 189 | 190 | 203 | 204 | 206 | 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /static/coda/js/views/fullwindow.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone' 5 | ], function($, _, BackBone) { 6 | 7 | // handles events on the entire window 8 | 9 | var FullWindowView = BackBone.View.extend({ 10 | 11 | initialize: function() { 12 | _.bindAll(this, 'onResize'); 13 | $(window).resize(this.onResize); 14 | this.$tocWell = $('#toc'); 15 | this.$tocResultsDiv = $('#toc-results'); 16 | this.$searchResults = $('#search-results'); 17 | this.$container = $('#container'); 18 | this.onResize(); 19 | }, 20 | 21 | onResize: function() { 22 | // make sure the TOC div reaches the bottom of the screen 23 | var windowHeight = $(window).height(); 24 | /* this.$tocWell.height(windowHeight - 100); */ 25 | this.$tocWell.height(windowHeight - 20); 26 | 27 | // Adjust the -196 magic # to account for the heights of new objects 28 | // put in the tocbar. For example, if a new thing occupies +24px height 29 | // in the toc bar, make the magic number -(196 + 24) = -220. 30 | /* this.$tocResultsDiv.height(windowHeight - 125); */ 31 | this.$tocResultsDiv.height(windowHeight - 42); 32 | 33 | /* this.$searchResults.height(windowHeight - 80); */ 34 | this.$searchResults.height(windowHeight - 0); 35 | this.$container.height(Math.max($('#toc').height(), this.$searchResults.height())); 36 | }, 37 | }); 38 | 39 | return FullWindowView; 40 | }); 41 | -------------------------------------------------------------------------------- /static/coda/templates/toc.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
    5 |
6 |
7 |
8 | -------------------------------------------------------------------------------- /static/css/bootstrap-responsive.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.0.1 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | .clearfix { 11 | *zoom: 1; 12 | } 13 | .clearfix:before, .clearfix:after { 14 | display: table; 15 | content: ""; 16 | } 17 | .clearfix:after { 18 | clear: both; 19 | } 20 | .hidden { 21 | display: none; 22 | visibility: hidden; 23 | } 24 | @media (max-width: 480px) { 25 | .nav-collapse { 26 | -webkit-transform: translate3d(0, 0, 0); 27 | } 28 | .page-header h1 small { 29 | display: block; 30 | line-height: 18px; 31 | } 32 | input[class*="span"], 33 | select[class*="span"], 34 | textarea[class*="span"], 35 | .uneditable-input { 36 | display: block; 37 | width: 100%; 38 | min-height: 28px; 39 | /* Make inputs at least the height of their button counterpart */ 40 | 41 | /* Makes inputs behave like true block-level elements */ 42 | 43 | -webkit-box-sizing: border-box; 44 | /* Older Webkit */ 45 | 46 | -moz-box-sizing: border-box; 47 | /* Older FF */ 48 | 49 | -ms-box-sizing: border-box; 50 | /* IE8 */ 51 | 52 | box-sizing: border-box; 53 | /* CSS3 spec*/ 54 | 55 | } 56 | .input-prepend input[class*="span"], .input-append input[class*="span"] { 57 | width: auto; 58 | } 59 | input[type="checkbox"], input[type="radio"] { 60 | border: 1px solid #ccc; 61 | } 62 | .form-horizontal .control-group > label { 63 | float: none; 64 | width: auto; 65 | padding-top: 0; 66 | text-align: left; 67 | } 68 | .form-horizontal .controls { 69 | margin-left: 0; 70 | } 71 | .form-horizontal .control-list { 72 | padding-top: 0; 73 | } 74 | .form-horizontal .form-actions { 75 | padding-left: 10px; 76 | padding-right: 10px; 77 | } 78 | .modal { 79 | position: absolute; 80 | top: 10px; 81 | left: 10px; 82 | right: 10px; 83 | width: auto; 84 | margin: 0; 85 | } 86 | .modal.fade.in { 87 | top: auto; 88 | } 89 | .modal-header .close { 90 | padding: 10px; 91 | margin: -10px; 92 | } 93 | .carousel-caption { 94 | position: static; 95 | } 96 | } 97 | @media (max-width: 767px) { 98 | .container { 99 | width: auto; 100 | padding: 0 20px; 101 | } 102 | .row-fluid { 103 | width: 100%; 104 | } 105 | .row { 106 | margin-left: 0; 107 | } 108 | .row > [class*="span"], .row-fluid > [class*="span"] { 109 | float: none; 110 | display: block; 111 | width: auto; 112 | margin: 0; 113 | } 114 | } 115 | @media (min-width: 768px) and (max-width: 979px) { 116 | .row { 117 | margin-left: -20px; 118 | *zoom: 1; 119 | } 120 | .row:before, .row:after { 121 | display: table; 122 | content: ""; 123 | } 124 | .row:after { 125 | clear: both; 126 | } 127 | [class*="span"] { 128 | float: left; 129 | margin-left: 20px; 130 | } 131 | .span1 { 132 | width: 42px; 133 | } 134 | .span2 { 135 | width: 104px; 136 | } 137 | .span3 { 138 | width: 166px; 139 | } 140 | .span4 { 141 | width: 228px; 142 | } 143 | .span5 { 144 | width: 290px; 145 | } 146 | .span6 { 147 | width: 352px; 148 | } 149 | .span7 { 150 | width: 414px; 151 | } 152 | .span8 { 153 | width: 476px; 154 | } 155 | .span9 { 156 | width: 538px; 157 | } 158 | .span10 { 159 | width: 600px; 160 | } 161 | .span11 { 162 | width: 662px; 163 | } 164 | .span12, .container { 165 | width: 724px; 166 | } 167 | .offset1 { 168 | margin-left: 82px; 169 | } 170 | .offset2 { 171 | margin-left: 144px; 172 | } 173 | .offset3 { 174 | margin-left: 206px; 175 | } 176 | .offset4 { 177 | margin-left: 268px; 178 | } 179 | .offset5 { 180 | margin-left: 330px; 181 | } 182 | .offset6 { 183 | margin-left: 392px; 184 | } 185 | .offset7 { 186 | margin-left: 454px; 187 | } 188 | .offset8 { 189 | margin-left: 516px; 190 | } 191 | .offset9 { 192 | margin-left: 578px; 193 | } 194 | .offset10 { 195 | margin-left: 640px; 196 | } 197 | .offset11 { 198 | margin-left: 702px; 199 | } 200 | .row-fluid { 201 | width: 100%; 202 | *zoom: 1; 203 | } 204 | .row-fluid:before, .row-fluid:after { 205 | display: table; 206 | content: ""; 207 | } 208 | .row-fluid:after { 209 | clear: both; 210 | } 211 | .row-fluid > [class*="span"] { 212 | float: left; 213 | margin-left: 2.762430939%; 214 | } 215 | .row-fluid > [class*="span"]:first-child { 216 | margin-left: 0; 217 | } 218 | .row-fluid > .span1 { 219 | width: 5.801104972%; 220 | } 221 | .row-fluid > .span2 { 222 | width: 14.364640883%; 223 | } 224 | .row-fluid > .span3 { 225 | width: 22.928176794%; 226 | } 227 | .row-fluid > .span4 { 228 | width: 31.491712705%; 229 | } 230 | .row-fluid > .span5 { 231 | width: 40.055248616%; 232 | } 233 | .row-fluid > .span6 { 234 | width: 48.618784527%; 235 | } 236 | .row-fluid > .span7 { 237 | width: 57.182320438000005%; 238 | } 239 | .row-fluid > .span8 { 240 | width: 65.74585634900001%; 241 | } 242 | .row-fluid > .span9 { 243 | width: 74.30939226%; 244 | } 245 | .row-fluid > .span10 { 246 | width: 82.87292817100001%; 247 | } 248 | .row-fluid > .span11 { 249 | width: 91.436464082%; 250 | } 251 | .row-fluid > .span12 { 252 | width: 99.999999993%; 253 | } 254 | input.span1, textarea.span1, .uneditable-input.span1 { 255 | width: 32px; 256 | } 257 | input.span2, textarea.span2, .uneditable-input.span2 { 258 | width: 94px; 259 | } 260 | input.span3, textarea.span3, .uneditable-input.span3 { 261 | width: 156px; 262 | } 263 | input.span4, textarea.span4, .uneditable-input.span4 { 264 | width: 218px; 265 | } 266 | input.span5, textarea.span5, .uneditable-input.span5 { 267 | width: 280px; 268 | } 269 | input.span6, textarea.span6, .uneditable-input.span6 { 270 | width: 342px; 271 | } 272 | input.span7, textarea.span7, .uneditable-input.span7 { 273 | width: 404px; 274 | } 275 | input.span8, textarea.span8, .uneditable-input.span8 { 276 | width: 466px; 277 | } 278 | input.span9, textarea.span9, .uneditable-input.span9 { 279 | width: 528px; 280 | } 281 | input.span10, textarea.span10, .uneditable-input.span10 { 282 | width: 590px; 283 | } 284 | input.span11, textarea.span11, .uneditable-input.span11 { 285 | width: 652px; 286 | } 287 | input.span12, textarea.span12, .uneditable-input.span12 { 288 | width: 714px; 289 | } 290 | } 291 | @media (max-width: 979px) { 292 | body { 293 | padding-top: 0; 294 | } 295 | .navbar-fixed-top { 296 | position: static; 297 | margin-bottom: 18px; 298 | } 299 | .navbar-fixed-top .navbar-inner { 300 | padding: 5px; 301 | } 302 | .navbar .container { 303 | width: auto; 304 | padding: 0; 305 | } 306 | .navbar .brand { 307 | padding-left: 10px; 308 | padding-right: 10px; 309 | margin: 0 0 0 -5px; 310 | } 311 | .navbar .nav-collapse { 312 | clear: left; 313 | } 314 | .navbar .nav { 315 | float: none; 316 | margin: 0 0 9px; 317 | } 318 | .navbar .nav > li { 319 | float: none; 320 | } 321 | .navbar .nav > li > a { 322 | margin-bottom: 2px; 323 | } 324 | .navbar .nav > .divider-vertical { 325 | display: none; 326 | } 327 | .navbar .nav .nav-header { 328 | color: #999999; 329 | text-shadow: none; 330 | } 331 | .navbar .nav > li > a, .navbar .dropdown-menu a { 332 | padding: 6px 15px; 333 | font-weight: bold; 334 | color: #999999; 335 | -webkit-border-radius: 3px; 336 | -moz-border-radius: 3px; 337 | border-radius: 3px; 338 | } 339 | .navbar .dropdown-menu li + li a { 340 | margin-bottom: 2px; 341 | } 342 | .navbar .nav > li > a:hover, .navbar .dropdown-menu a:hover { 343 | background-color: #222222; 344 | } 345 | .navbar .dropdown-menu { 346 | position: static; 347 | top: auto; 348 | left: auto; 349 | float: none; 350 | display: block; 351 | max-width: none; 352 | margin: 0 15px; 353 | padding: 0; 354 | background-color: transparent; 355 | border: none; 356 | -webkit-border-radius: 0; 357 | -moz-border-radius: 0; 358 | border-radius: 0; 359 | -webkit-box-shadow: none; 360 | -moz-box-shadow: none; 361 | box-shadow: none; 362 | } 363 | .navbar .dropdown-menu:before, .navbar .dropdown-menu:after { 364 | display: none; 365 | } 366 | .navbar .dropdown-menu .divider { 367 | display: none; 368 | } 369 | .navbar-form, .navbar-search { 370 | float: none; 371 | padding: 9px 15px; 372 | margin: 9px 0; 373 | border-top: 1px solid #222222; 374 | border-bottom: 1px solid #222222; 375 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 376 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 377 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 378 | } 379 | .navbar .nav.pull-right { 380 | float: none; 381 | margin-left: 0; 382 | } 383 | .navbar-static .navbar-inner { 384 | padding-left: 10px; 385 | padding-right: 10px; 386 | } 387 | .btn-navbar { 388 | display: block; 389 | } 390 | .nav-collapse { 391 | overflow: hidden; 392 | height: 0; 393 | } 394 | } 395 | @media (min-width: 980px) { 396 | .nav-collapse.collapse { 397 | height: auto !important; 398 | } 399 | } 400 | @media (min-width: 1200px) { 401 | .row { 402 | margin-left: -30px; 403 | *zoom: 1; 404 | } 405 | .row:before, .row:after { 406 | display: table; 407 | content: ""; 408 | } 409 | .row:after { 410 | clear: both; 411 | } 412 | [class*="span"] { 413 | float: left; 414 | margin-left: 30px; 415 | } 416 | .span1 { 417 | width: 70px; 418 | } 419 | .span2 { 420 | width: 170px; 421 | } 422 | .span3 { 423 | width: 270px; 424 | } 425 | .span4 { 426 | width: 370px; 427 | } 428 | .span5 { 429 | width: 470px; 430 | } 431 | .span6 { 432 | width: 570px; 433 | } 434 | .span7 { 435 | width: 670px; 436 | } 437 | .span8 { 438 | width: 770px; 439 | } 440 | .span9 { 441 | width: 870px; 442 | } 443 | .span10 { 444 | width: 970px; 445 | } 446 | .span11 { 447 | width: 1070px; 448 | } 449 | .span12, .container { 450 | width: 1170px; 451 | } 452 | .offset1 { 453 | margin-left: 130px; 454 | } 455 | .offset2 { 456 | margin-left: 230px; 457 | } 458 | .offset3 { 459 | margin-left: 330px; 460 | } 461 | .offset4 { 462 | margin-left: 430px; 463 | } 464 | .offset5 { 465 | margin-left: 530px; 466 | } 467 | .offset6 { 468 | margin-left: 630px; 469 | } 470 | .offset7 { 471 | margin-left: 730px; 472 | } 473 | .offset8 { 474 | margin-left: 830px; 475 | } 476 | .offset9 { 477 | margin-left: 930px; 478 | } 479 | .offset10 { 480 | margin-left: 1030px; 481 | } 482 | .offset11 { 483 | margin-left: 1130px; 484 | } 485 | .row-fluid { 486 | width: 100%; 487 | *zoom: 1; 488 | } 489 | .row-fluid:before, .row-fluid:after { 490 | display: table; 491 | content: ""; 492 | } 493 | .row-fluid:after { 494 | clear: both; 495 | } 496 | .row-fluid > [class*="span"] { 497 | float: left; 498 | margin-left: 2.564102564%; 499 | } 500 | .row-fluid > [class*="span"]:first-child { 501 | margin-left: 0; 502 | } 503 | .row-fluid > .span1 { 504 | width: 5.982905983%; 505 | } 506 | .row-fluid > .span2 { 507 | width: 14.529914530000001%; 508 | } 509 | .row-fluid > .span3 { 510 | width: 23.076923077%; 511 | } 512 | .row-fluid > .span4 { 513 | width: 31.623931624%; 514 | } 515 | .row-fluid > .span5 { 516 | width: 40.170940171000005%; 517 | } 518 | .row-fluid > .span6 { 519 | width: 48.717948718%; 520 | } 521 | .row-fluid > .span7 { 522 | width: 57.264957265%; 523 | } 524 | .row-fluid > .span8 { 525 | width: 65.81196581200001%; 526 | } 527 | .row-fluid > .span9 { 528 | width: 74.358974359%; 529 | } 530 | .row-fluid > .span10 { 531 | width: 82.905982906%; 532 | } 533 | .row-fluid > .span11 { 534 | width: 91.45299145300001%; 535 | } 536 | .row-fluid > .span12 { 537 | width: 100%; 538 | } 539 | input.span1, textarea.span1, .uneditable-input.span1 { 540 | width: 60px; 541 | } 542 | input.span2, textarea.span2, .uneditable-input.span2 { 543 | width: 160px; 544 | } 545 | input.span3, textarea.span3, .uneditable-input.span3 { 546 | width: 260px; 547 | } 548 | input.span4, textarea.span4, .uneditable-input.span4 { 549 | width: 360px; 550 | } 551 | input.span5, textarea.span5, .uneditable-input.span5 { 552 | width: 460px; 553 | } 554 | input.span6, textarea.span6, .uneditable-input.span6 { 555 | width: 560px; 556 | } 557 | input.span7, textarea.span7, .uneditable-input.span7 { 558 | width: 660px; 559 | } 560 | input.span8, textarea.span8, .uneditable-input.span8 { 561 | width: 760px; 562 | } 563 | input.span9, textarea.span9, .uneditable-input.span9 { 564 | width: 860px; 565 | } 566 | input.span10, textarea.span10, .uneditable-input.span10 { 567 | width: 960px; 568 | } 569 | input.span11, textarea.span11, .uneditable-input.span11 { 570 | width: 1060px; 571 | } 572 | input.span12, textarea.span12, .uneditable-input.span12 { 573 | width: 1160px; 574 | } 575 | .thumbnails { 576 | margin-left: -30px; 577 | } 578 | .thumbnails > li { 579 | margin-left: 30px; 580 | } 581 | } 582 | -------------------------------------------------------------------------------- /static/css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | .clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";} 2 | .clearfix:after{clear:both;} 3 | .hidden{display:none;visibility:hidden;} 4 | @media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:767px){.container{width:auto;padding:0 20px;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;}}@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .span1{width:42px;} .span2{width:104px;} .span3{width:166px;} .span4{width:228px;} .span5{width:290px;} .span6{width:352px;} .span7{width:414px;} .span8{width:476px;} .span9{width:538px;} .span10{width:600px;} .span11{width:662px;} .span12,.container{width:724px;} .offset1{margin-left:82px;} .offset2{margin-left:144px;} .offset3{margin-left:206px;} .offset4{margin-left:268px;} .offset5{margin-left:330px;} .offset6{margin-left:392px;} .offset7{margin-left:454px;} .offset8{margin-left:516px;} .offset9{margin-left:578px;} .offset10{margin-left:640px;} .offset11{margin-left:702px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid>.span1{width:5.801104972%;} .row-fluid>.span2{width:14.364640883%;} .row-fluid>.span3{width:22.928176794%;} .row-fluid>.span4{width:31.491712705%;} .row-fluid>.span5{width:40.055248616%;} .row-fluid>.span6{width:48.618784527%;} .row-fluid>.span7{width:57.182320438000005%;} .row-fluid>.span8{width:65.74585634900001%;} .row-fluid>.span9{width:74.30939226%;} .row-fluid>.span10{width:82.87292817100001%;} .row-fluid>.span11{width:91.436464082%;} .row-fluid>.span12{width:99.999999993%;} input.span1,textarea.span1,.uneditable-input.span1{width:32px;} input.span2,textarea.span2,.uneditable-input.span2{width:94px;} input.span3,textarea.span3,.uneditable-input.span3{width:156px;} input.span4,textarea.span4,.uneditable-input.span4{width:218px;} input.span5,textarea.span5,.uneditable-input.span5{width:280px;} input.span6,textarea.span6,.uneditable-input.span6{width:342px;} input.span7,textarea.span7,.uneditable-input.span7{width:404px;} input.span8,textarea.span8,.uneditable-input.span8{width:466px;} input.span9,textarea.span9,.uneditable-input.span9{width:528px;} input.span10,textarea.span10,.uneditable-input.span10{width:590px;} input.span11,textarea.span11,.uneditable-input.span11{width:652px;} input.span12,textarea.span12,.uneditable-input.span12{width:714px;}}@media (max-width:979px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav .nav-header{color:#999999;text-shadow:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .span1{width:70px;} .span2{width:170px;} .span3{width:270px;} .span4{width:370px;} .span5{width:470px;} .span6{width:570px;} .span7{width:670px;} .span8{width:770px;} .span9{width:870px;} .span10{width:970px;} .span11{width:1070px;} .span12,.container{width:1170px;} .offset1{margin-left:130px;} .offset2{margin-left:230px;} .offset3{margin-left:330px;} .offset4{margin-left:430px;} .offset5{margin-left:530px;} .offset6{margin-left:630px;} .offset7{margin-left:730px;} .offset8{margin-left:830px;} .offset9{margin-left:930px;} .offset10{margin-left:1030px;} .offset11{margin-left:1130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid>.span1{width:5.982905983%;} .row-fluid>.span2{width:14.529914530000001%;} .row-fluid>.span3{width:23.076923077%;} .row-fluid>.span4{width:31.623931624%;} .row-fluid>.span5{width:40.170940171000005%;} .row-fluid>.span6{width:48.717948718%;} .row-fluid>.span7{width:57.264957265%;} .row-fluid>.span8{width:65.81196581200001%;} .row-fluid>.span9{width:74.358974359%;} .row-fluid>.span10{width:82.905982906%;} .row-fluid>.span11{width:91.45299145300001%;} .row-fluid>.span12{width:100%;} input.span1,textarea.span1,.uneditable-input.span1{width:60px;} input.span2,textarea.span2,.uneditable-input.span2{width:160px;} input.span3,textarea.span3,.uneditable-input.span3{width:260px;} input.span4,textarea.span4,.uneditable-input.span4{width:360px;} input.span5,textarea.span5,.uneditable-input.span5{width:460px;} input.span6,textarea.span6,.uneditable-input.span6{width:560px;} input.span7,textarea.span7,.uneditable-input.span7{width:660px;} input.span8,textarea.span8,.uneditable-input.span8{width:760px;} input.span9,textarea.span9,.uneditable-input.span9{width:860px;} input.span10,textarea.span10,.uneditable-input.span10{width:960px;} input.span11,textarea.span11,.uneditable-input.span11{width:1060px;} input.span12,textarea.span12,.uneditable-input.span12{width:1160px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}} 5 | -------------------------------------------------------------------------------- /static/dochub.appcache: -------------------------------------------------------------------------------- 1 | CACHE MANIFEST 2 | 3 | # Version 0.01 4 | 5 | # just copy/pasted a dump of server logs...might be a better way to do this programmatically 6 | CACHE: 7 | index.html 8 | js/libs/require/require.js 9 | css/custom.css 10 | css/docs.css 11 | css/bootstrap.css 12 | js/main.js 13 | images/php_logo.png 14 | images/jquery_logo.png 15 | images/mdn_logo.png 16 | images/python_logo.gif 17 | js/order.js 18 | js/app.js 19 | js/libs/jquery/jquery-min.js 20 | js/libs/bootstrap/bootstrap-dropdown.js 21 | js/libs/underscore/underscore-min.js 22 | js/libs/bootstrap/bootstrap-scrollspy.js 23 | js/libs/bootstrap/bootstrap-twipsy.js 24 | js/libs/jquery/jquery.js 25 | js/libs/backbone/backbone.js 26 | js/libs/underscore/underscore.js 27 | js/router.js 28 | js/views/fullwindow.js 29 | js/views/jquerysearchresults.js 30 | js/views/topnav.js 31 | js/libs/require/domReady-min.js 32 | js/libs/backbone/backbone-min.js 33 | js/views/languageview.js 34 | js/collections/mozdevcssprops.js 35 | js/collections/mdnhtmlelements.js 36 | js/collections/mdndomobjs.js 37 | js/collections/mdnjsobjs.js 38 | js/collections/jqentries.js 39 | js/collections/phpexts.js 40 | js/collections/xsltpages.js 41 | js/collections/pagescrapes.js 42 | js/collections/pageelements.js 43 | js/collections/pythonpages.js 44 | js/views/searchresults.js 45 | js/text.js 46 | js/views/mozdevcssprop.js 47 | js/views/tocbar.js 48 | js/models/sectionscrape.js 49 | js/models/pagescrape.js 50 | js/models/pageelement.js 51 | js/views/searchheader.js 52 | js/views/pagescrapedlanguageview.js 53 | js/views/pagesearchresults.js 54 | js/views/pagesearchheader.js 55 | js/views/pagetocbar.js 56 | templates/toc.html 57 | templates/mdnpage.html 58 | templates/sectionscrape.html 59 | templates/tocresult.html 60 | templates/topnav.html 61 | templates/page.html 62 | templates/tocpagescraperesult.html 63 | js/libs/spin/spin.js 64 | js/libs/spin/spin.min.js 65 | data/css-mdn.json 66 | data/dom-mdn.json 67 | data/html-mdn.json 68 | data/jquery.json 69 | data/js-mdn.json 70 | data/php-ext.json 71 | data/xslt-w3.json 72 | data/python.json 73 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgarcia/dochub/2cbe62ec86aef0cf41897523d46e3685cf4d5a36/static/favicon.ico -------------------------------------------------------------------------------- /static/images/jquery_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgarcia/dochub/2cbe62ec86aef0cf41897523d46e3685cf4d5a36/static/images/jquery_logo.png -------------------------------------------------------------------------------- /static/images/mdn_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgarcia/dochub/2cbe62ec86aef0cf41897523d46e3685cf4d5a36/static/images/mdn_logo.png -------------------------------------------------------------------------------- /static/images/php_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgarcia/dochub/2cbe62ec86aef0cf41897523d46e3685cf4d5a36/static/images/php_logo.png -------------------------------------------------------------------------------- /static/images/python_logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgarcia/dochub/2cbe62ec86aef0cf41897523d46e3685cf4d5a36/static/images/python_logo.gif -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | DocHub | Instant Documentation Search 13 | 14 | 15 | 18 | 19 | 20 | 21 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 53 | 54 | 55 | 56 | 58 |
59 |
60 | 61 | 62 | 63 | 66 | 69 | 70 | 71 | 74 | 77 | 78 | 79 | 82 | 156 | 157 | 158 | 161 | 164 | 165 | 166 |
67 | CSS/HTML/JavaScript/DOM data is from the Mozilla Developer Network. 68 |
75 | jQuery data is from http://api.jquery.com. Version is 1.7. 76 |
83 | PHP data is from http://www.php.net/manual/en/extensions.alphabetical.php. 84 |

Currently supported PHP extensions:

85 |

86 |

    87 |
  • Apache
  • 88 |
  • Arrays
  • 89 |
  • Bzip2
  • 90 |
  • Calendar
  • 91 |
  • Classkit
  • 92 |
  • Class/Objects
  • 93 |
  • COM
  • 94 |
  • cURL
  • 95 |
  • Date/Time
  • 96 |
  • DBA
  • 97 |
  • DB++
  • 98 |
  • Direct IO
  • 99 |
  • Directories
  • 100 |
  • DOM
  • 101 |
  • .NET
  • 102 |
  • Eio
  • 103 |
  • Error Handling
  • 104 |
  • Program execution
  • 105 |
  • Filesystem
  • 106 |
  • FTP
  • 107 |
  • Function Handling
  • 108 |
  • Gearman
  • 109 |
  • GeoIP
  • 110 |
  • Hash
  • 111 |
  • HTTP
  • 112 |
  • ID3
  • 113 |
  • IIS
  • 114 |
  • IMAP
  • 115 |
  • Ingres
  • 116 |
  • Java
  • 117 |
  • JSON
  • 118 |
  • LDAP
  • 119 |
  • Libevent
  • 120 |
  • libxml
  • 121 |
  • Mail
  • 122 |
  • Math
  • 123 |
  • Mailparse
  • 124 |
  • Memcache
  • 125 |
  • Memcached
  • 126 |
  • Mongo
  • 127 |
  • MySQL
  • 128 |
  • MySQLi
  • 129 |
  • OAuth
  • 130 |
  • OpenSSL
  • 131 |
  • Output Control
  • 132 |
  • PostgreSQL
  • 133 |
  • POSIX
  • 134 |
  • Readline
  • 135 |
  • SimpleXML
  • 136 |
  • SQLite
  • 137 |
  • SQLite3
  • 138 |
  • SQLSRV
  • 139 |
  • SSH2
  • 140 |
  • Streams
  • 141 |
  • Strings
  • 142 |
  • SWF
  • 143 |
  • Sybase
  • 144 |
  • ODBC
  • 145 |
  • URLs
  • 146 |
  • Variable handling
  • 147 |
  • WinCache
  • 148 |
  • XML-RPC
  • 149 |
  • XSL
  • 150 |
  • Yaml
  • 151 |
  • Zip
  • 152 |
  • Zlib
  • 153 |
154 |

155 |
162 | Python data is from http://docs.python.org/library/. Version is 2.7. 163 |
167 |
168 |
169 |
170 | 177 |
178 |
179 |
180 |
181 | 182 | 183 | 184 | 197 | 198 | 200 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /static/js/app.build.js: -------------------------------------------------------------------------------- 1 | ({ 2 | appDir: "../", 3 | baseUrl: "js", 4 | dir: "../../static-build", 5 | modules: [ 6 | { 7 | name: "main" 8 | } 9 | ], 10 | paths: { 11 | jQuery: 'libs/jquery/jquery', 12 | Underscore: 'libs/underscore/underscore', 13 | Backbone: 'libs/backbone/backbone', 14 | templates: '../templates' 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /static/js/app.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'router', 6 | ], function($, _, Backbone, Router) { 7 | var initialize = function() { 8 | // c.f. http://www.html5rocks.com/en/tutorials/appcache/beginner/ 9 | window.applicationCache.addEventListener('updateready', function(e) { 10 | // Browser downloaded a new app cache. 11 | // Swap it in and reload the page to get the new hotness. 12 | window.applicationCache.swapCache(); 13 | if (confirm('A new version of this DocHub is available. Load it?')) { 14 | window.location.reload(); 15 | } 16 | }, false); 17 | 18 | Router.initialize(); 19 | } 20 | 21 | return { 22 | initialize: initialize, 23 | }; 24 | }); 25 | -------------------------------------------------------------------------------- /static/js/collections/jqentries.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'models/sectionscrape' 6 | ], function($, _, Backbone, SectionScrape) { 7 | 8 | var jqEntryPattern = new RegExp("^(\\W*)(.+)$"); 9 | 10 | var JQEntry = Backbone.Collection.extend({ 11 | url: '/data/jquery.json', 12 | model: SectionScrape, 13 | 14 | comparator: function(model) { 15 | var title = model.get('title'); 16 | var results = jqEntryPattern.exec(title); 17 | var prefix = results[1]; 18 | var name = results[2]; 19 | 20 | if (prefix) { 21 | return '2' + title; 22 | } else if (name[0] === name[0].toLowerCase()) { 23 | return '0' + name; 24 | } else { 25 | return '1' + name; 26 | } 27 | }, 28 | }); 29 | 30 | return JQEntry; 31 | }); 32 | -------------------------------------------------------------------------------- /static/js/collections/mdndomobjs.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'models/sectionscrape' 6 | ], function($, _, Backbone, SectionScrape) { 7 | 8 | var MDNDomObj = Backbone.Collection.extend({ 9 | url: '/data/dom-mdn.json', 10 | model: SectionScrape, 11 | 12 | comparator: function(model) { 13 | var title = model.get('title'); 14 | if (title[0] === title[0].toLowerCase()) { 15 | return '0' + title; 16 | } else { 17 | return '1' + title; 18 | } 19 | }, 20 | }); 21 | 22 | return MDNDomObj; 23 | }); 24 | -------------------------------------------------------------------------------- /static/js/collections/mdnhtmlelements.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'models/sectionscrape' 6 | ], function($, _, Backbone, SectionScrape) { 7 | 8 | // Handle the following cases: 9 | // 10 | var htmlElementPattern = new RegExp("^(\\W*)(.+)$"); 11 | 12 | var MDNHtmlElemnts = Backbone.Collection.extend({ 13 | url: '/data/html-mdn.json', 14 | model: SectionScrape, 15 | 16 | comparator: function(model) { 17 | var title = model.get('title'); 18 | var results = htmlElementPattern.exec(title); 19 | var prefix = results[1]; 20 | var name = results[2].toLowerCase(); 21 | 22 | return prefix ? ('1' + title) : ('0' + name); 23 | }, 24 | 25 | }); 26 | 27 | return MDNHtmlElemnts; 28 | }); 29 | -------------------------------------------------------------------------------- /static/js/collections/mdnjsobjs.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'models/sectionscrape' 6 | ], function($, _, Backbone, SectionScrape) { 7 | 8 | var MDNJsObj = Backbone.Collection.extend({ 9 | url: '/data/js-mdn.json', 10 | model: SectionScrape, 11 | 12 | comparator: function(model) { 13 | return model.get('title'); 14 | }, 15 | 16 | }); 17 | 18 | return MDNJsObj; 19 | }); 20 | -------------------------------------------------------------------------------- /static/js/collections/mozdevcssprops.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'models/sectionscrape' 6 | ], function($, _, Backbone, SectionScrape) { 7 | 8 | // Handle the following cases: 9 | // -xyz 10 | // ::-xyz 11 | // ::xyz 12 | // :xyz 13 | // @xyz 14 | // 15 | var cssPropsPattern = new RegExp("^(\\W*)(.+)$"); 16 | 17 | var MozDevCSSProps = Backbone.Collection.extend({ 18 | url: '/data/css-mdn.json', 19 | model: SectionScrape, 20 | 21 | comparator: function(model) { 22 | var title = model.get('title'); 23 | var results = cssPropsPattern.exec(title); 24 | var prefix = results[1]; 25 | var name = results[2]; 26 | 27 | if (prefix) { 28 | return '2' + title; 29 | } else if (name[0] === name[0].toLowerCase()) { 30 | return '0' + name; 31 | } else { 32 | return '1' + name; 33 | } 34 | }, 35 | 36 | }); 37 | 38 | return MozDevCSSProps; 39 | }); 40 | -------------------------------------------------------------------------------- /static/js/collections/pageelements.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'models/pageelement' 6 | ], function($, _, Backbone, PageElement) { 7 | 8 | var PageElements = Backbone.Collection.extend({ 9 | model: PageElement, 10 | 11 | comparator: function(model) { 12 | return model.get('lowerCaseTitle'); 13 | } 14 | }); 15 | 16 | return PageElements; 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /static/js/collections/pagescrapes.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'models/pagescrape' 6 | ], function($, _, Backbone, PageScrape) { 7 | 8 | var PageScrapes = Backbone.Collection.extend({ 9 | model: PageScrape 10 | }); 11 | 12 | return PageScrapes; 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /static/js/collections/phpexts.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'models/sectionscrape' 6 | ], function($, _, Backbone, SectionScrape) { 7 | 8 | var PhpExt = Backbone.Collection.extend({ 9 | url: '/data/php-ext.json', 10 | model: SectionScrape, 11 | 12 | comparator: function(model) { 13 | return model.get('title').toLowerCase(); 14 | }, 15 | }); 16 | 17 | return PhpExt; 18 | }); 19 | -------------------------------------------------------------------------------- /static/js/collections/pythonpages.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'collections/pagescrapes' 6 | ], function($, _, Backbone, PageScrapes) { 7 | 8 | var PythonPages = PageScrapes.extend({ 9 | url: '/data/python.json' 10 | }); 11 | 12 | return PythonPages; 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /static/js/collections/xsltpages.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jQuery', 3 | 'Underscore', 4 | 'Backbone', 5 | 'collections/pagescrapes' 6 | ], function($, _, Backbone, PageScrapes) { 7 | 8 | var XsltElements = PageScrapes.extend({ 9 | url: '/data/xslt-w3.json' 10 | }); 11 | 12 | return XsltElements; 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /static/js/libs/backbone/backbone.js: -------------------------------------------------------------------------------- 1 | define(['order!libs/backbone/backbone-min'], function(){ 2 | _.noConflict(); 3 | $.noConflict(); 4 | return Backbone.noConflict(); 5 | }); 6 | -------------------------------------------------------------------------------- /static/js/libs/bootstrap/bootstrap-collapse.js: -------------------------------------------------------------------------------- 1 | /* ============================================================= 2 | * bootstrap-collapse.js v2.0.1 3 | * http://twitter.github.com/bootstrap/javascript.html#collapse 4 | * ============================================================= 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | !function( $ ){ 21 | 22 | "use strict" 23 | 24 | var Collapse = function ( element, options ) { 25 | this.$element = $(element) 26 | this.options = $.extend({}, $.fn.collapse.defaults, options) 27 | 28 | if (this.options["parent"]) { 29 | this.$parent = $(this.options["parent"]) 30 | } 31 | 32 | this.options.toggle && this.toggle() 33 | } 34 | 35 | Collapse.prototype = { 36 | 37 | constructor: Collapse 38 | 39 | , dimension: function () { 40 | var hasWidth = this.$element.hasClass('width') 41 | return hasWidth ? 'width' : 'height' 42 | } 43 | 44 | , show: function () { 45 | var dimension = this.dimension() 46 | , scroll = $.camelCase(['scroll', dimension].join('-')) 47 | , actives = this.$parent && this.$parent.find('.in') 48 | , hasData 49 | 50 | if (actives && actives.length) { 51 | hasData = actives.data('collapse') 52 | actives.collapse('hide') 53 | hasData || actives.data('collapse', null) 54 | } 55 | 56 | this.$element[dimension](0) 57 | this.transition('addClass', 'show', 'shown') 58 | this.$element[dimension](this.$element[0][scroll]) 59 | 60 | } 61 | 62 | , hide: function () { 63 | var dimension = this.dimension() 64 | this.reset(this.$element[dimension]()) 65 | this.transition('removeClass', 'hide', 'hidden') 66 | this.$element[dimension](0) 67 | } 68 | 69 | , reset: function ( size ) { 70 | var dimension = this.dimension() 71 | 72 | this.$element 73 | .removeClass('collapse') 74 | [dimension](size || 'auto') 75 | [0].offsetWidth 76 | 77 | this.$element.addClass('collapse') 78 | } 79 | 80 | , transition: function ( method, startEvent, completeEvent ) { 81 | var that = this 82 | , complete = function () { 83 | if (startEvent == 'show') that.reset() 84 | that.$element.trigger(completeEvent) 85 | } 86 | 87 | this.$element 88 | .trigger(startEvent) 89 | [method]('in') 90 | 91 | $.support.transition && this.$element.hasClass('collapse') ? 92 | this.$element.one($.support.transition.end, complete) : 93 | complete() 94 | } 95 | 96 | , toggle: function () { 97 | this[this.$element.hasClass('in') ? 'hide' : 'show']() 98 | } 99 | 100 | } 101 | 102 | /* COLLAPSIBLE PLUGIN DEFINITION 103 | * ============================== */ 104 | 105 | $.fn.collapse = function ( option ) { 106 | return this.each(function () { 107 | var $this = $(this) 108 | , data = $this.data('collapse') 109 | , options = typeof option == 'object' && option 110 | if (!data) $this.data('collapse', (data = new Collapse(this, options))) 111 | if (typeof option == 'string') data[option]() 112 | }) 113 | } 114 | 115 | $.fn.collapse.defaults = { 116 | toggle: true 117 | } 118 | 119 | $.fn.collapse.Constructor = Collapse 120 | 121 | 122 | /* COLLAPSIBLE DATA-API 123 | * ==================== */ 124 | 125 | $(function () { 126 | $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { 127 | var $this = $(this), href 128 | , target = $this.attr('data-target') 129 | || e.preventDefault() 130 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 131 | , option = $(target).data('collapse') ? 'toggle' : $this.data() 132 | $(target).collapse(option) 133 | }) 134 | }) 135 | 136 | }( window.jQuery ); -------------------------------------------------------------------------------- /static/js/libs/bootstrap/bootstrap-dropdown.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrap-dropdown.js v1.3.0 3 | * http://twitter.github.com/bootstrap/javascript.html#dropdown 4 | * ============================================================ 5 | * Copyright 2011 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | /* DROPDOWN PLUGIN DEFINITION 24 | * ========================== */ 25 | 26 | $.fn.dropdown = function ( selector ) { 27 | return this.each(function () { 28 | $(this).delegate(selector || d, 'click', function (e) { 29 | var li = $(this).parent('li') 30 | , isActive = li.hasClass('open') 31 | 32 | clearMenus() 33 | !isActive && li.toggleClass('open') 34 | return false 35 | }) 36 | }) 37 | } 38 | 39 | /* APPLY TO STANDARD DROPDOWN ELEMENTS 40 | * =================================== */ 41 | 42 | var d = 'a.menu, .dropdown-toggle' 43 | 44 | function clearMenus() { 45 | $(d).parent('li').removeClass('open') 46 | } 47 | 48 | $(function () { 49 | $('html').bind("click", clearMenus) 50 | $('body').dropdown( '[data-dropdown] a.menu, [data-dropdown] .dropdown-toggle' ) 51 | }) 52 | 53 | }( window.jQuery || window.ender ); 54 | -------------------------------------------------------------------------------- /static/js/libs/bootstrap/bootstrap-modal.js: -------------------------------------------------------------------------------- 1 | /* ========================================================= 2 | * bootstrap-modal.js v1.4.0 3 | * http://twitter.github.com/bootstrap/javascript.html#modal 4 | * ========================================================= 5 | * Copyright 2011 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================= */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | "use strict" 24 | 25 | /* CSS TRANSITION SUPPORT (https://gist.github.com/373874) 26 | * ======================================================= */ 27 | 28 | var transitionEnd 29 | 30 | $(document).ready(function () { 31 | 32 | $.support.transition = (function () { 33 | var thisBody = document.body || document.documentElement 34 | , thisStyle = thisBody.style 35 | , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined 36 | return support 37 | })() 38 | 39 | // set CSS transition event type 40 | if ( $.support.transition ) { 41 | transitionEnd = "TransitionEnd" 42 | if ( $.browser.webkit ) { 43 | transitionEnd = "webkitTransitionEnd" 44 | } else if ( $.browser.mozilla ) { 45 | transitionEnd = "transitionend" 46 | } else if ( $.browser.opera ) { 47 | transitionEnd = "oTransitionEnd" 48 | } 49 | } 50 | 51 | }) 52 | 53 | 54 | /* MODAL PUBLIC CLASS DEFINITION 55 | * ============================= */ 56 | 57 | var Modal = function ( content, options ) { 58 | this.settings = $.extend({}, $.fn.modal.defaults, options) 59 | this.$element = $(content) 60 | .delegate('.close', 'click.modal', $.proxy(this.hide, this)) 61 | 62 | if ( this.settings.show ) { 63 | this.show() 64 | } 65 | 66 | return this 67 | } 68 | 69 | Modal.prototype = { 70 | 71 | toggle: function () { 72 | return this[!this.isShown ? 'show' : 'hide']() 73 | } 74 | 75 | , show: function () { 76 | var that = this 77 | this.isShown = true 78 | this.$element.trigger('show') 79 | 80 | escape.call(this) 81 | backdrop.call(this, function () { 82 | var transition = $.support.transition && that.$element.hasClass('fade') 83 | 84 | that.$element 85 | .appendTo(document.body) 86 | .show() 87 | 88 | if (transition) { 89 | that.$element[0].offsetWidth // force reflow 90 | } 91 | 92 | that.$element.addClass('in') 93 | 94 | transition ? 95 | that.$element.one(transitionEnd, function () { that.$element.trigger('shown') }) : 96 | that.$element.trigger('shown') 97 | 98 | }) 99 | 100 | return this 101 | } 102 | 103 | , hide: function (e) { 104 | e && e.preventDefault() 105 | 106 | if ( !this.isShown ) { 107 | return this 108 | } 109 | 110 | var that = this 111 | this.isShown = false 112 | 113 | escape.call(this) 114 | 115 | this.$element 116 | .trigger('hide') 117 | .removeClass('in') 118 | 119 | $.support.transition && this.$element.hasClass('fade') ? 120 | hideWithTransition.call(this) : 121 | hideModal.call(this) 122 | 123 | return this 124 | } 125 | 126 | } 127 | 128 | 129 | /* MODAL PRIVATE METHODS 130 | * ===================== */ 131 | 132 | function hideWithTransition() { 133 | // firefox drops transitionEnd events :{o 134 | var that = this 135 | , timeout = setTimeout(function () { 136 | that.$element.unbind(transitionEnd) 137 | hideModal.call(that) 138 | }, 500) 139 | 140 | this.$element.one(transitionEnd, function () { 141 | clearTimeout(timeout) 142 | hideModal.call(that) 143 | }) 144 | } 145 | 146 | function hideModal (that) { 147 | this.$element 148 | .hide() 149 | .trigger('hidden') 150 | 151 | backdrop.call(this) 152 | } 153 | 154 | function backdrop ( callback ) { 155 | var that = this 156 | , animate = this.$element.hasClass('fade') ? 'fade' : '' 157 | if ( this.isShown && this.settings.backdrop ) { 158 | var doAnimate = $.support.transition && animate 159 | 160 | this.$backdrop = $('