├── .gitignore
├── .gitmodules
├── COMMANDS.md
├── README.md
├── logs
└── .gitignore
├── package.json
├── plugins
├── caniuse.js
├── duckduckgo.js
├── google.js
├── octo.js
├── weather.js
└── wolframalpha.js
├── protobot.js
├── sandboxed-ipc-repl.rkt
├── settings.json
├── srepl-1.0.0-SNAPSHOT-standalone.jar
├── strftime.js
└── vendor
├── strftime
└── strftime.js
└── unescape
└── unescape.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | nohup.out
3 | node_modules
4 | logs
5 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "vendor/WAT"]
2 | path = vendor/WAT
3 | url = git://github.com/gf3/WAT.git
4 |
--------------------------------------------------------------------------------
/COMMANDS.md:
--------------------------------------------------------------------------------
1 | # Protobot
2 |
3 | You can add `@ user` to most commands/triggers to direct output towards others. E.g.: `pastie @ TheN00b`
4 |
5 | ## Commands:
6 |
7 |
8 | - api
9 | - Lookup prototype documentation. Syntax:
Class.classMethod
, Class#instanceMethod
, Class
. E.g.: api function#bind
10 |
11 | - cred
12 | - Display GitHub nerd cred for a user. E.g.:
cred jeresig
13 |
14 | - ddg
15 | - Get an abstract of some term, from DuckDuckGo. E.g.:
ddg jQuery
16 |
17 | - eval
18 | - Evaluate some Javascript. E.g.:
eval var a = 1; a++
19 |
20 | - g
21 | - Return the first google result. E.g.:
g fermion address map
22 |
23 | - gh
24 | - Display GitHub user information. E.g.:
gh coldhead
25 |
26 | - mdc
27 | - Lookup Mozilla Developer Center documentation. E.g.:
mdc function apply
28 |
29 | - time
30 | - Get the time at someone's location. E.g.:
time divya
31 |
32 | - wa
33 | - Compute some shiz on Wolfram Alpha. E.g.:
wa Neptune
34 |
35 | - weather
36 | - Get weather data. E.g.:
weather gf3
or just weather
37 |
38 |
39 | ## Triggers:
40 |
41 | * ===
42 | * about
43 | * accessproperty
44 | * anyone
45 | * appendscript
46 | * asi
47 | * backtrace
48 | * bracketnotation
49 | * bubble
50 | * casesensitive
51 | * commands
52 | * debugger
53 | * delegation
54 | * dotnotation
55 | * DRY
56 | * ES3
57 | * ES5
58 | * eventintro
59 | * jic
60 | * jsis
61 | * false
62 | * FOUC
63 | * gl / glwtd
64 | * help
65 | * jic
66 | * minimal
67 | * ninja
68 | * noob
69 | * pastie
70 | * PHP
71 | * point
72 | * plugins
73 | * proto
74 | * protoquery
75 | * reinvent
76 | * SOP
77 | * spelling
78 | * testcase
79 | * TIAS
80 | * truthy
81 | * TYVM
82 | * validid
83 | * vamp
84 | * wat
85 | * WET
86 |
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Install
2 |
3 | Require node and npm.
4 |
5 | ``` zsh
6 | git clone --recursive git@github.com:gf3/protobot.git
7 | cd protobot
8 | npm install
9 | ```
10 |
11 | # Edit
12 |
13 | Edit `protobot.js` file and change the `options` at the top of the file.
14 |
15 |
--------------------------------------------------------------------------------
/logs/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gf3/protobot/c08e286972f08d16549c65da3d8e740178b003e0/logs/.gitignore
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | { "name": "protobot"
2 | , "version": "0.0.2"
3 | , "engines": ["node >= 0.6.0"]
4 | , "main": "protobot.js"
5 | , "dependencies":
6 | { "groupie": ">= 0"
7 | , "hiredis": ">= 0"
8 | , "redis": ">= 0"
9 | , "jerk": ">= 1.1.23"
10 | , "sandbox": ">= 0.8.2"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/plugins/caniuse.js:
--------------------------------------------------------------------------------
1 | var http = require( 'http' )
2 |
3 | exports.register = register
4 |
5 | function register( j ) {
6 | j.watch_for( /^([\/.,`?]?)caniuse ([^#@]+)(?:\s*#([1-9]))?(?:\s*@\s*([-\[\]|_\w]+))?$/, canIUse )
7 | }
8 |
9 | function canIUse ( message ) {
10 | var search = message.match_data[ 2 ].split( ' ' ).join( '+' )
11 |
12 | if( !search )
13 | return
14 |
15 | http
16 | .get( { host: 'api.html5please.com', path: '/' + search + '.json?noagent', port: 80 }, function ( res ) {
17 | var data = ''
18 | res
19 | .on( 'data', function ( c ) { data += c } )
20 | .on( 'end', function() {
21 |
22 | var j = JSON.parse( data )
23 |
24 | if ( j.supported != 'unknown' && j.features.length === 0 )
25 | return
26 |
27 | var f = j.features
28 | , r = j.results
29 | , a = j.agents
30 |
31 | , use = ''
32 | , agents = ''
33 | , links = ''
34 |
35 | use += Object.keys( f ).map( function( k ) {
36 | links += ' http://caniuse.com/#search=' + k
37 | return f[ k ]
38 | }).join( ', ' ).replace( /,([^,]*?)$/, ', and$1' )
39 |
40 | agents += Object.keys( r ).map( function( k ) {
41 | return j.agents[ k ].name + ' ' + r[ k ]
42 | }).join( ', ' ).replace( /,([^,]*?)$/, ', and$1' )
43 |
44 | if( agents.length )
45 | message.say( message.user + ': You can use ' + use + ' with ' + agents + '.' + links )
46 | else
47 | message.say( message.user + ': ' + use + ' is not fully supported anywhere.' )
48 | })
49 | })
50 | }
51 |
--------------------------------------------------------------------------------
/plugins/duckduckgo.js:
--------------------------------------------------------------------------------
1 | exports.register = register
2 |
3 | var exec = require( 'child_process' ).exec
4 |
5 | /* ---------------------------- DuckDuckGo ---------------------------- */
6 | function DuckDuckGo() {
7 | }
8 |
9 | DuckDuckGo.prototype.search = function( query, cloudback ) {
10 | exec("curl 'http://api.duckduckgo.com/?format=json&q=" + escape( query ) + "'", function ( err, stdout, stderr ) {
11 | var results = JSON.parse( stdout ) // What could possibly go wrong?
12 | cloudback.call( this, results )
13 | })
14 | }
15 |
16 | function register( j ) {
17 | var ddg = new DuckDuckGo()
18 |
19 | j.watch_for( /^([\/.,`?]?)ddg ([^#@]+)(?:\s*#([1-9]))?(?:\s*@\s*([-\[\]\{\}`|_\w]+))?$/, function( message ) {
20 | var user = message.match_data[4] || message.name
21 | , term = message.match_data[2]
22 | , num = +message.match_data[3]-1 || 0
23 |
24 | ddg.search( term, function( results ) {
25 | if ( results["AbstractText"] )
26 | message.say( user + ': ' + unescapeAll( results["AbstractText"] ) + ' - ' + results["AbstractURL"] )
27 | else if ( results["Definition"] )
28 | message.say( user + ': ' + results["Definition"] + ' - ' + results["DefinitionURL"] )
29 | else if ( results["Redirect"] ) // !bang syntax used
30 | message.say( user + ': ' + results["Redirect"] )
31 | else if ( results["Results"].length )
32 | message.say( user + ': ' + results["Results"][num]["Text"] + " - " + results["Results"][num]["FirstURL"] )
33 | else
34 | message.say( user + ": Sorry, no results for '" + term + "'" )
35 | })
36 | })
37 | }
38 |
--------------------------------------------------------------------------------
/plugins/google.js:
--------------------------------------------------------------------------------
1 | /* ------------------------------ Includes && Options ------------------------------ */
2 | var exec = require( 'child_process' ).exec
3 | , unescapeAll = require( '../vendor/unescape/unescape' )
4 |
5 | /* ------------------------------ Google ------------------------------ */
6 | function Google() {
7 | this.search = function( query, hollaback ) {
8 | exec("curl -e 'http://gf3.ca/' 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=" + escape( query ) + "'", function ( err, stdout, stderr ) {
9 | var results = JSON.parse( stdout )[ 'responseData' ][ 'results' ]
10 | hollaback.call( this, results )
11 | })
12 | }
13 | }
14 |
15 | function register( j ) {
16 | var google = new Google
17 |
18 | j.watch_for( /^([\/.,`?]?)g(?:[ogle]{0,5}) ([^#@]+)(?:\s*#([1-9]))?(?:\s*@\s*([-\[\]\{\}`|_\w]+))?$/, function( message ) {
19 | var user = message.match_data[4] || message.user
20 | , res = +message.match_data[3]-1 || 0
21 |
22 | // Return if botty is present
23 | if ( message.match_data[1] == '?' && message.source.clients.indexOf( 'bot-t' ) >= 0 )
24 | return
25 |
26 | google.search( message.match_data[2], function( results ) {
27 | if ( results.length )
28 | message.say( user + ': ' + unescapeAll( results[res].titleNoFormatting ) + ' - ' + results[res].unescapedUrl )
29 | else
30 | message.say( user + ": Sorry, no results for '" + message.match_data[2] + "'" )
31 | })
32 | })
33 |
34 | // MDN, formerly known as MDC
35 | j.watch_for( /^([\/.,`?]?)(?:mdc|mdn) ([^#@]+)(?:\s*#([1-9]))?(?:\s*@\s*([-\[\]|_\w]+))?$/, function( message ) {
36 | var user = message.match_data[4] || message.user
37 | , res = +message.match_data[2]-1 || 0
38 |
39 | // Return if botty is present
40 | if ( message.match_data[1] == '?' && message.source.clients.indexOf( 'bot-t' ) >= 0 )
41 | return
42 |
43 | google.search( message.match_data[2] + ' site:developer.mozilla.org', function( results ) {
44 | if ( results.length )
45 | message.say( user + ": " + results[res].titleNoFormatting + " - " + results[res].unescapedUrl )
46 | else
47 | message.say( user + ": Sorry, no results for '" + message.match_data[2] + "'" )
48 | })
49 | })
50 | }
51 |
52 | /* ------------------------------ Export ------------------------------ */
53 | exports.register = register
54 |
--------------------------------------------------------------------------------
/plugins/octo.js:
--------------------------------------------------------------------------------
1 | var Octo = module.exports = {}
2 | , http = require( 'http' )
3 | , https = require( 'https' )
4 | , cache = {}
5 |
6 | Octo.register = function( j ) {
7 | // GitHub User
8 | j.watch_for( /^([\/.,`?]?)gh\s+(\w+)\s*$/, function( message ) {
9 | // Return if botty is present
10 | if ( message.match_data[1] == '?' && message.source.clients.indexOf( 'bot-t' ) >= 0 )
11 | return
12 | var name = message.match_data[2]
13 | Octo.user( name, function( err, user ) {
14 | if ( err )
15 | message.say( 'Error: ' + err.message )
16 | else
17 | message.say( 'GitHub user: ' + user.login + (user.name ? ' (' + user.name + ') ' : 'NO NAME') + 'Repos: ' + user.public_repos + ' • Following: ' + user.following + ' • Followers: ' + user.followers )
18 | })
19 | })
20 | // Nerd Cred
21 | j.watch_for( /^([\/.,`?]?)cred\s+(\w+)\s*$/, function( message ) {
22 | // Return if botty is present
23 | if ( message.match_data[1] == '?' && message.source.clients.indexOf( 'bot-t' ) >= 0 )
24 | return
25 | var name = message.match_data[2]
26 | Octo.score( name, function( err, score ) {
27 | if ( err )
28 | message.say( 'Error: ' + err.message )
29 | else
30 | message.say( 'GitHub User: ' + name + ' • Score: ' + score )
31 | })
32 | })
33 | }
34 |
35 | // Get GitHub User
36 | Octo.user = function user ( username, hollaback ) {
37 | // Cached?
38 | if ( cache[ username ] ) {
39 | hollaback.call( null, undefined, cache[ username ] )
40 | return
41 | }
42 |
43 | api( '/users/' + username, function( err, data ) {
44 | if ( data && data.message && data.message == 'Not Found' )
45 | hollaback.call( null, data )
46 | else {
47 | cache[ username ] = data
48 | hollaback.call( null, err, data )
49 | }
50 | })
51 | }
52 |
53 | // Get Nerd Cred Score
54 | Octo.score = function score ( username, hollaback ) {
55 | Octo.user( username, function( err, user ) {
56 | if ( err ) {
57 | hollaback.call( null, err )
58 | return
59 | }
60 |
61 | // Get repos
62 | api( '/repos/show/' + username, true, function( err, data ) {
63 | if ( err ) {
64 | hollaback.call( null, err )
65 | return
66 | }
67 |
68 | // XXX Totally arbitrary
69 | var score = 0
70 |
71 | score += user.followers * 2
72 | score += user.public_gists * 3
73 | score += user.public_repos * 4
74 |
75 | data.repositories.forEach( function( r ) {
76 | score += r.watchers
77 | score += r.forks * 5
78 | })
79 |
80 | score = score * 0.10
81 | hollaback.call( null, undefined, ~~score )
82 | })
83 | })
84 | }
85 |
86 | // Make API Calls
87 | function api ( endpoint, v2, hollaback ) {
88 | var options = {}
89 | , method
90 |
91 | if ( hollaback == undefined ) {
92 | hollaback = v2
93 | v2 = false
94 | }
95 |
96 | if ( v2 ) {
97 | options.host = 'github.com'
98 | options.path = '/api/v2/json' + endpoint
99 | options.port = 80
100 | method = http
101 | }
102 | else {
103 | options.host = 'api.github.com'
104 | options.path = endpoint
105 | method = https
106 | }
107 |
108 | method.get( options, function( res ) {
109 | var data = ''
110 |
111 | res.on( 'data', function( chunk ) {
112 | data += chunk
113 | }).on( 'end', function() {
114 | hollaback.call( null, undefined, JSON.parse( data ) )
115 | })
116 |
117 | }).on( 'error', function( err ) {
118 | hollaback.call( null, err )
119 | })
120 | }
121 |
--------------------------------------------------------------------------------
/plugins/weather.js:
--------------------------------------------------------------------------------
1 | var get = require( 'http' ).get
2 | , pipe_url =
3 | { host: "pipes.fy3.b.yahoo.com"
4 | , port: 80
5 | , path: "/pipes/pipe.run?_id=5a36359b823b6cb19e67fc6739c6a02b&_render=json&location="
6 | }
7 |
8 | function extend ( dest, src ) {
9 | var prop
10 | for (prop in src) {
11 | dest[prop] = src[prop];
12 | }
13 | return dest
14 | }
15 |
16 | function toC ( F ) {
17 | return Math.round( ( parseInt( F, 10 ) - 32 ) * ( 5 / 9 ) )
18 | }
19 |
20 | function toF ( C ) {
21 | return Math.round( parseInt( C, 10 ) * ( 9 / 5 ) + 32 )
22 | }
23 |
24 | function getWeather ( location, justTime, holla ) {
25 | if ( !location )
26 | holla( "Please provide a location" )
27 |
28 | var opt = extend( {}, pipe_url )
29 | opt.path += encodeURIComponent( location )
30 |
31 | get( opt, function( resp ) {
32 | var w, out, body = ""
33 |
34 | resp.on( 'data', function ( chunk ) {
35 | body += chunk
36 | })
37 |
38 | resp.on( 'close', function ( chunk ) {
39 | holla( "Sorry, no results for: " + location )
40 | })
41 |
42 | resp.on( 'end', function () {
43 | try {
44 | w = JSON.parse( body )
45 |
46 | if ( justTime ) {
47 | out = w.value.items[0].channel.item['yweather:condition'].date
48 | }
49 | else {
50 | out = w.value.items[0].channel.item.title
51 | out += ": "
52 | out += toC( w.value.items[0].channel.item['yweather:condition'].temp )
53 | out += "°C / "
54 | out += w.value.items[0].channel.item['yweather:condition'].temp
55 | out += "°F "
56 | out += w.value.items[0].channel.item['yweather:condition'].text
57 | }
58 |
59 | holla( out )
60 | }
61 | catch ( e ) {
62 | holla( "Sorry, no results for: " + location )
63 | }
64 | })
65 | }).on( "error", function( err ) {
66 | holla( "Sorry, no results for: " + location )
67 | })
68 | }
69 |
70 | exports.register = function( j, dynamic_json ) {
71 | j.watch_for( /^([\/.,`?]?)(time|weather)(\s+[^@]+)?(?:\s*@\s*([-\[\]\{\}`|_\w]+))?/, function( message ) {
72 | var user = message.match_data[4] || message.user
73 | , location = message.match_data[3]
74 | , person
75 |
76 | if ( location )
77 | location = location.trim()
78 |
79 | // Return if botty is present
80 | if ( message.match_data[1] == '?' && message.source.clients.indexOf( 'bot-t' ) >= 0 )
81 | return
82 |
83 | // Try and find by person first
84 | person = dynamic_json.crew.filter( function( v, i, a ) { return v.irc == location } )
85 | if ( person.length )
86 | location = person[0].location
87 | else if ( !person.length )
88 | person = dynamic_json.crew.filter( function( v, i, a ) { return v.irc == user } )
89 |
90 | if ( location == undefined && person.length )
91 | location = person[0].location
92 |
93 | getWeather( location, message.match_data[2] == 'time', function( result ) {
94 | message.say( user + ": " + result )
95 | })
96 | })
97 | }
--------------------------------------------------------------------------------
/plugins/wolframalpha.js:
--------------------------------------------------------------------------------
1 | /* ------------------------------ Includes && Options ----------------- */
2 | var exec = require( 'child_process' ).exec
3 | , unescapeAll = require( '../vendor/unescape/unescape' )
4 |
5 | /* ------------------------------ WolframAlpha ------------------------ */
6 | function WolframAlpha() {
7 | this.search = function( query, hollaback ) {
8 | var result =
9 | { url: 'http://www.wolframalpha.com/input/?i=' + encodeURIComponent( query )
10 | }
11 |
12 | exec( "curl -e 'http://www.wolframalpha.com' '" + result.url + "'", function ( err, stdout, stderr ) {
13 | var solution = />Solution:<[\s\S]*?alt\s*=\s*\"([^\""]*)\"/
14 | , other = /stringified"\s*:\s*"([^"\r\n]*)/g
15 |
16 | if ( solution.test( stdout ) )
17 | result.data = stdout
18 | .match( solution )[1]
19 | .replace( /\\\//, '/' )
20 | else {
21 | match = stdout.match( other )
22 | if ( !match || !match[1] )
23 | result.data = null
24 | else
25 | result.data = match[1]
26 | .replace( /stringified"\s*:\s*"/g, '' )
27 | .replace( /\\n/g, ' ' )
28 | .replace( /\\\//, '/' )
29 | }
30 |
31 | hollaback.call( this, result )
32 | })
33 | }
34 | }
35 |
36 | function register( j ) {
37 | var wa = new WolframAlpha
38 |
39 | j.watch_for( /^([\/.,`?]?)wa ([^@]+)(?:\s*@\s*([-\[\]\{\}`|_\w]+))?/, function( message ) {
40 | var user = message.match_data[3] || message.user
41 |
42 | // Return if botty is present
43 | if ( message.match_data[1] == '?' && message.source.clients.indexOf( 'bot-t' ) >= 0 )
44 | return
45 |
46 | wa.search( message.match_data[2], function( result ) {
47 | message.say( user + ": " + ( result && result.data ? unescapeAll( result.data ) : "Sorry, no results for '" + message.match_data[2] + "'" ) )
48 | })
49 | })
50 | }
51 |
52 | /* ------------------------------ Export ------------------------------ */
53 | exports.register = register
54 |
--------------------------------------------------------------------------------
/protobot.js:
--------------------------------------------------------------------------------
1 | /* ------------------------------ Includes && Options ------------------------------ */
2 | require( './vendor/strftime/strftime' )
3 |
4 | var util = require( 'util' )
5 | , fs = require( 'fs' )
6 | , path = require( 'path' )
7 | , http = require( 'http' )
8 | , URL = require( 'url' )
9 | , exec = require( 'child_process' ).exec
10 | , spawn = require( 'child_process' ).spawn
11 | , redis = require( 'redis' )
12 | , groupie = require('groupie')
13 | , jerk = require( 'jerk' )
14 | , Sandbox = require( 'sandbox' )
15 | , unescapeAll = require( './vendor/unescape/unescape' )
16 | , settingsFile = path.join( __dirname, "settings.json" )
17 | , bot
18 | , rclient
19 | , sandbox
20 | , options
21 | , commands
22 | , dynamic_json
23 | , c
24 |
25 | options = JSON.parse( fs.readFileSync( process.argv[2] || settingsFile ) );
26 |
27 | // Dynamic JSON reloads
28 | dynamic_json = {}
29 | reloadJSON(
30 | { wat: 'vendor/WAT/wat.json'
31 | , crew: 'http://ot-crew.com/crew.json'
32 | })
33 |
34 | // Sandbox
35 | sandbox = new Sandbox()
36 |
37 | /* ------------------------------ Simple Commands ------------------------------ */
38 | commands =
39 | { about: "http://github.com/gf3/protobot"
40 | , accessproperty: "https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Operators/Member_Operators"
41 | , anyone: "Has anyone really been far even as decided to use even go want to do look more like?"
42 | , appendscript: "var script = document.createElement( 'script' ); script.src='...'; document.body.appendChild( script );"
43 | , asi: "Automatic Semi-colon Insertion. Read: http://inimino.org/~inimino/blog/javascript_semicolons"
44 | , backtrace: "THE CONSEQUENCES WILL NEVER BE THE SAME"
45 | , bracketnotation: "https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Operators/Member_Operators#Bracket_notation"
46 | , bubble: "http://www.quirksmode.org/js/events_order.html"
47 | , casesensitive: "The case-sensitivity of document language element names in selectors depends on the document language. For example, in HTML, element names are case-insensitive, but in XML they are case-sensitive."
48 | , cc: "CASE CLOASED >:|"
49 | , cheeseburger: "(|%|)"
50 | , commands: "http://github.com/gf3/protobot/blob/master/COMMANDS.md"
51 | , 'debugger': "Debugging JavaScript is easy with the right tools! Try the Web Inspector for Safari + Chrome http://webkit.org/blog/197/web-inspector-redesign/ or Firebug for Firefox http://getfirebug.com/ or Dragonfly for Opera http://bit.ly/rNzdz"
52 | , delegation: "Info: http://pxlz.org/tZ Code: http://pxlz.org/ua"
53 | , dotnotation: "https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Operators/Member_Operators#Dot_notation"
54 | , DRY: "Don't Repeat Yourself"
55 | , ES3: "ES3 is edition 3 of ECMA-262, the ECMAScript specification: http://www.ecma-international.org/publications/standards/Ecma-262-arch.htm now obsoleted by ES5"
56 | , ES5: "ES5 is edition 5 of ECMA-262, the ECMAScript ( aka JavaScript ) specification: http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf"
57 | , eventintro: "http://www.quirksmode.org/js/introevents.html"
58 | , evil: "eval is evil! Don't! Read: http://blogs.msdn.com/b/ericlippert/archive/2003/11/01/53329.aspx and http://blogs.msdn.com/b/ericlippert/archive/2003/11/04/53335.aspx"
59 | , flip: "(╯‵Д′)╯彡┻━┻"
60 | , help: "NO U!"
61 | , heyyy: "(☞゚∀゚)☞"
62 | , isjc: "☁ It's so just Cloud™ - http://itssojustcloud.com/ - http://groups.google.com/group/jquery-dev/browse_thread/thread/6a39be05b8477401#msg_f90341223bb9b68b ☁"
63 | , jic: "just in case™"
64 | , jsis: "javascript is javascript is javascript"
65 | , 'false': 'falsy values in js: null, undefined, NaN, false, zero ( the number 0 - "0" is true ), "" ( empty string )'
66 | , fouc: "http://paulirish.com/2009/avoiding-the-fouc-v3/"
67 | , minimal: "A minimal test case should contain precisely the HTML and JavaScript necessary to demonstrate the problem, no more and no less. If it is more than 32 lines, it is probably not a minimal test case."
68 | , mlmlm: "much like multi-level marketing"
69 | , mlu: "much like urself"
70 | , ninja: "http://ejohn.org/apps/learn"
71 | , noob: "http://www.marriedtothesea.com/022310/i-hate-thinking.gif"
72 | , os: "oh snao™"
73 | , osb: "oh snao™ bitch"
74 | , osisjc: "( oh snao™ is so just cloud™ )™"
75 | , pastie: "Paste links not code: http://pastie.org/ , http://jsbin.com/ , http://dpaste.de/ , http://gist.github.com/"
76 | , PHP: "You're asking a JavaScript question but you're showing us PHP instead of HTML and JavaScript. Maybe your PHP code results in well-formed JavaScript code, maybe it doesn't; we don't know. Please show us the HTML JavaScript that the browser sees."
77 | , pizza: "(>"
78 | , plugins: "Check out: http://scripteka.com and http://livepipe.net"
79 | , point: "If you have a question, please just ask it. Do not look for topic experts. Do not ask \"Can I ask a question?\", \"Can anyone help?\", or \"Does anybody use/know about foo?\". Don't make people work to find out what your question is."
80 | , protoquery: "STOP! Don't do it. Prototype and jQuery do the same things, you don't need both. It just adds twice the overhead and potential for conflicts. Pick one or the other."
81 | , proto: "http://dhtmlkitchen.com/learn/js/enumeration/prototype-chain.jsp"
82 | , reinvent: "We will not help you reinvent the wheel if we recommend using the many wheels already available. If you choose to make your own, you're on your own."
83 | , sop: "Requests must respect the Same Origin Policy ( http://en.wikipedia.org/wiki/Same_origin_policy ). Requesting cross-domain content in javascript is generally prohibited. Seeing OPTIONS requests? See https://developer.mozilla.org/en/HTTP_access_control"
84 | , spelling: "Spelling and capitalization are important in programming."
85 | , testcase: "see: minimal"
86 | , tias: "Try It And See"
87 | , truthy: "Truthy/Falsy Values & Comparison Operators: http://www.sitepoint.com/blogs/2009/07/01/javascript-truthy-falsy/ Truthy/Falsy Values & Boolean Operator Results: http://11heavens.com/falsy-and-truthy-in-javascript"
88 | , tyvm: "Thank you SO SO SO much!"
89 | , uaa: "ur an alligator"
90 | , validid: 'ID attributes must begin with a letter ( [A-Za-z] ) and may be followed by any number of letters, digits ( [0-9] ), hyphens ( "-" ), underscores ( "_" ), colons ( ":" ), and periods ( "." ). http://www.w3.org/TR/html401/types.html#h-6.2 - furthermore, IDs are unique, meaning only one element in the DOM can have a given ID at any time'
91 | , vamp: "http://slash7.com/pages/vampires"
92 | , wattt: "(″・ิ_・ิ)っ"
93 | , WET: "Write Everything Twice"
94 | , whyyy: "ლ(゚д゚ლ)"
95 | , zalgo: "H̹̙̦̮͉̩̗̗ͧ̇̏̊̾Eͨ͆͒̆ͮ̃͏̷̮̣̫̤̣ ̵̞̹̻̀̉̓ͬ͑͡ͅCͯ̂͐͏̨̛͔̦̟͈̻O̜͎͍͙͚̬̝̣̽ͮ͐͗̀ͤ̍̀͢M̴̡̲̭͍͇̼̟̯̦̉̒͠Ḛ̛̙̞̪̗ͥͤͩ̾͑̔͐ͅṮ̴̷̷̗̼͍̿̿̓̽͐H̙̙̔̄͜"
96 | , '( ?:gl|glwtd )': "http://goodluckwiththatdude.com/"
97 | , '===': "For any primitive values o and p, o === p if o and p have the same value and type. For any Objects o and p, o === p if mutating o will mutate p in the same way."
98 | }
99 |
100 | for ( c in commands )
101 | watchForSingle( c, commands[c] )
102 |
103 | // Redis
104 | rclient = redis.createClient( 9307, 'stingfish.redistogo.com' )
105 | rclient.auth( 'da834e6f78e4ea8c4c25ac20f0c8869a' )
106 | rclient.on( 'error', function ( err ) {
107 | console.log( 'Redis error: ' + err )
108 | })
109 | rclient.hgetall( 'triggers', function ( err, obj ) {
110 | var i
111 | console.log( err, obj )
112 | if ( ! err )
113 | for ( i in obj )
114 | watchForSingle( i, obj[i] )
115 | })
116 |
117 | /* ------------------------------ Protobot ------------------------------ */
118 | bot = jerk( function( j ) {
119 | // Wat?
120 | j.watch_for( /\b(w[au]t)\b/, function( message ) {
121 | switch ( String( message.source ) ) {
122 | case '#jquery-ot':
123 | case '#runlevel6':
124 | message.say( dynamic_json.wat[ Math.floor( Math.random() * dynamic_json.wat.length ) ] )
125 | break
126 | }
127 | })
128 |
129 | // Noobs
130 | j.watch_for( RegExp("^(?:" + options.nick + "\\W+)?(?:hi|hello|hey)(?:\\W+" + options.nick + ".*)?$", "i"), function( message ) {
131 | var r = [ 'oh hai!', 'why hello there', 'hey', 'hi', 'sup?', 'hola', 'yo!' ]
132 | setTimeout( function() {
133 | message.say( message.user + ': ' + r[ Math.floor( Math.random() * r.length ) ] )
134 | }, Math.round( Math.random() * 10000 ))
135 | })
136 |
137 | // Boom!
138 | j.watch_for( /^Boom!$/i, function( message ) {
139 | message.say( 'Did you are unimpressed? and now?' )
140 | })
141 |
142 | // NO NO U
143 | j.watch_for( /^((?:NO )+)U$/, function( message ) {
144 | message.say( message.user + ': ' + message.match_data[1] + 'NO U' )
145 | })
146 |
147 | // Y U
148 | j.watch_for( /\by u\b/i, function( message ) {
149 | message.say( "(屮'Д')屮" )
150 | })
151 |
152 | // Shrug
153 | j.watch_for( /\bshrugs\b/i, function( message ) {
154 | message.say( "¯\\_(ツ)_/¯" )
155 | })
156 |
157 | // Alligator
158 | j.watch_for( /\balligator\b/i, function( message ) {
159 | message.say( "---,==,'<" )
160 | })
161 |
162 | // Live reload
163 | j.watch_for( /^[\/.,`?]?reload (\w+)$/, function( message ) {
164 | liveReload( message )
165 | })
166 |
167 | // Redis
168 | j.watch_for( /^(?:david_mark|protobot|bot\-t)[,:]? ([-_.:|\/\\\w]+) is[,:]? (.+)$/, function( message ) {
169 | rclient.hmset( 'triggers', message.match_data[1], message.match_data[2], function( err ) {
170 | if ( err )
171 | message.say( message.user + ': Oops, there was an error: ' + err )
172 | else {
173 | message.say( message.user + ': kk' )
174 | watchForSingle( message.match_data[1], message.match_data[2] )
175 | }
176 | })
177 | })
178 |
179 | j.watch_for( /^(?:david_mark|protobot|bot\-t)[,:]? forget[,:]? (.+)$/, function( message ) {
180 | rclient.hdel( 'triggers', message.match_data[1], function( err ) {
181 | if ( err )
182 | message.say( message.user + ': Oops, there was an error: ' + err )
183 | else {
184 | message.say( message.user + ': kk' )
185 | bot.forget( new RegExp( "^[\\/.`?]?" + message.match_data[1] + "(?:\\s*@\\s*([-\\[\\]\\{\\}`|_\\w]+))?\\s*$", "i" ) )
186 | }
187 | })
188 | })
189 |
190 | // Finger
191 | j.watch_for( /^([\/.,`?]?)f(?:inger)?(\s+[-\[\]\{\}`|_\w]+)?\s*$/, function( message ) {
192 | // Return if botty is present
193 | if ( message.match_data[1] == '?' && message.source.clients.indexOf( 'bot-t' ) >= 0 )
194 | return
195 |
196 | var name = to( message, 2 )
197 | , user = dynamic_json.crew.filter( function( v, i, a ) { return v.irc == name } )
198 | if ( user.length )
199 | message.say( '-ot crew • ' + util.inspect( user[0] ).replace( /\n/g, '' ) )
200 | else
201 | message.say( 'Error: User not found.' )
202 | })
203 |
204 | // Sandbox
205 | j.watch_for( /^([\/.,`?]?)eval (?:(.+?)(?:\/\/\s*@\s*([-\[\]\{\}`|_\w]+))|(.+))/, function( message ){
206 | // Return if botty is present
207 | if ( message.match_data[1] == '?' && message.source.clients.indexOf( 'bot-t' ) >= 0 )
208 | return
209 |
210 | var js = message.match_data[2] || message.match_data[4]
211 | sandbox.run( js, function( output ) { var original_length
212 | output = output.result.replace( /\n/g, ' ' )
213 | if ( ( original_length = output.length ) > ( 1024 - message.user.length - 3 ) )
214 | output = output.slice( 0, 768 ) + ' (' + ( original_length - 768 ) + ' characters truncated)'
215 | message.say( to( message, 3 ) + ': ' + output )
216 | })
217 | })
218 |
219 | // Racket Sandbox
220 | j.watch_for( /^rkt[→>] (.*)/, function ( message ) {
221 | var stdout = ''
222 | , stderr = ''
223 | , child = spawn( 'racket', [ 'sandboxed-ipc-repl.rkt' ] )
224 | , stdoutput = function( data ) {
225 | if ( !!data )
226 | stdout += data
227 | }
228 | , stderrput = function( data ) {
229 | if ( !!data )
230 | stderr += data
231 | }
232 |
233 | child.stdout.on( 'data', stdoutput )
234 | child.stderr.on( 'data', stderrput )
235 | child.on( 'exit', function( code ) { var out
236 | if ( code )
237 | out = stderr.split( '\n' )[0].replace( 'UNKNOWN::0: read', 'Error' )
238 | else
239 | out = stdout
240 | message.say( message.user + ': ' + out )
241 | })
242 | child.stdin.write( message.match_data[1] )
243 | child.stdin.end()
244 | })
245 |
246 | // Clojure Sandbox
247 | j.watch_for( /^clj[→>] (.*)/, function ( message ) {
248 | var stdout = ''
249 | , stderr = ''
250 | , child = spawn( 'java', [ '-jar', 'srepl-1.0.0-SNAPSHOT-standalone.jar' ] )
251 | , stdoutput = function( data ) {
252 | if ( !!data )
253 | stdout += data
254 | }
255 | , stderrput = function( data ) {
256 | if ( !!data )
257 | stderr += data
258 | }
259 |
260 | child.stdout.on( 'data', stdoutput )
261 | child.stderr.on( 'data', stderrput )
262 | child.on( 'exit', function( code ) { var out
263 | if ( code )
264 | out = stderr
265 | else
266 | out = stdout
267 | message.say( message.user + ': ' + out )
268 | })
269 | child.stdin.write( message.match_data[1] )
270 | child.stdin.end()
271 | })
272 |
273 | // "it doesn't work"
274 | j.watch_for( /^(?:it )?doesn(?:')?t work(?:\s*@\s*([-\[\]\{\}`|_\w]+))?/, function( message ) {
275 | message.say( to( message, "doesn't work" ) + ": What do you mean it doesn't work? What happens when you try to run it? What's the output? What's the error message? Saying \"it doesn't work\" is pointless." )
276 | })
277 |
278 | // Prototype API
279 | j.watch_for( /^api ([$\w]+(?:[\.#]\w+)*)(?:\s+@\s*([-\[\]|_\w]+))?/, function( message ) {
280 | message.say( to( message, 2 ) + ": Sorry, the `api` command is temporarily disabled. Docs here: http://api.prototypejs.org/" )
281 | })
282 |
283 | // LOGS
284 | j.watch_for( /.*/, function ( message ) {
285 | var now = new Date()
286 | , location = path.join( options.logdir, message.source )
287 | , file = path.join( location, now.strftime( '%Y-%m-%d.log' ) )
288 |
289 | // Make the directory
290 | path.exists( location, function( exists ) {
291 | var log
292 | if ( ! exists )
293 | fs.mkdirSync( location, 0755 )
294 | log = fs.createWriteStream( file, { flags: 'a' })
295 | log.write( message + '\n' )
296 | log.end()
297 | })
298 | })
299 | }).connect( options )
300 |
301 | // Register plugins
302 | if ( options["plugins"] )
303 | options["plugins"].forEach( function( plugin ) {
304 | var plugReg = require( "./plugins/" + plugin ).register
305 | try {
306 | jerk( function( j ) {
307 | // Gross hack until global var is dead
308 | plugReg( j, dynamic_json )
309 | })
310 | } catch (e) {
311 | console.error( "Failed to register plugin %s: %s", plugin, e )
312 | }
313 | })
314 |
315 | /* ------------------------------ Functions ------------------------------ */
316 | function to ( message, def, idx ) {
317 | if ( idx === undefined && typeof def === 'number' )
318 | idx = def, def = null
319 | else
320 | idx = idx || 1
321 | return !!message.match_data[idx] ? message.match_data[idx].trim() : def || message.user
322 | }
323 |
324 | function watchForSingle ( trigger, msg ) {
325 | jerk( function( j ) {
326 | j.watch_for( new RegExp( "^([\\/.,`?])?" + trigger + "(?:\\s*@\\s*([-\\[\\]\\{\\}`|_\\w]+))?\\s*$", "i" ), function( message ) {
327 | // Return if botty is present
328 | if ( message.match_data[1] == '?' && message.source.clients.indexOf( 'bot-t' ) >= 0 )
329 | return
330 |
331 | message.say( to( message, 2 ) + ": " + msg )
332 | })
333 | })
334 | }
335 | function reloadJSON ( what, hollaback ) {
336 | hollaback = hollaback || function(){}
337 |
338 | Object.keys( what ).forEach( function( k ) {
339 | var url
340 | if ( what[k].slice( 0, 4 ) == 'http' ) {
341 | url = URL.parse( what[k] )
342 | http
343 | .get( { host: url.host, path: url.pathname + ( url.search || '' ), port: 80 }, function ( res ) {
344 | var data = ''
345 | res
346 | .on( 'data', function ( c ) { data += c } )
347 | .on( 'end', function(){
348 | var j = JSON.parse( data )
349 | hollaback.call( null, undefined, dynamic_json[k] = j )
350 | })
351 | })
352 | .on( 'error', hollaback )
353 | }
354 | else
355 | fs.readFile( what[k], function( er, data ) {
356 | if ( ! er )
357 | dynamic_json[k] = JSON.parse( data )
358 | if ( hollaback )
359 | hollaback.call( null, er, dynamic_json[k] )
360 | })
361 | })
362 | }
363 |
364 | function liveReload( message ) { var chain
365 | switch ( message.match_data[1] ) {
366 | case 'wat':
367 | chain =
368 | [ function( done ) { exec( 'git pull origin master', { cwd: path.join( __dirname, 'vendor', 'WAT' ) }, done ) }
369 | , function( done ) { reloadJSON( { wat: 'vendor/WAT/wat.json' }, done) }
370 | , function( done ) { message.say( message.user + ': Last WAT: ' + dynamic_json.wat[ dynamic_json.wat.length - 1 ] ); done() }
371 | ]
372 | break
373 | case 'crew':
374 | chain =
375 | [ function( done ) { reloadJSON( { crew: 'http://ot-crew.com/crew.json' }, done) }
376 | ]
377 | break
378 | case 'self': // Assumes it will be automagically restarted by forever/god/monit/whatever
379 | chain =
380 | [ function( done ) { exec( 'git pull origin master', { cwd: __dirname }, done ) }
381 | , function( done ) { exec( 'git submodule init', { cwd: __dirname }, done ) }
382 | , function( done ) { exec( 'git submodule update', { cwd: __dirname }, done ) }
383 | , function( done ) { exec( 'git pull origin master', { cwd: path.join( __dirname, 'vendor', 'WAT' ) }, done ) } // Always use latest WAT
384 | , function( done ) { process.exit( 0 ) }
385 | ]
386 | break
387 | }
388 |
389 | if ( chain )
390 | groupie.chain( chain, function ( er, results ) {
391 | if ( er ) {
392 | message.say( message.user + ': Sorry there was an error reloading "' + message.match_data[1] + '"' )
393 | message.say( message.user + ': ' + er.message )
394 | }
395 | else
396 | message.say( message.user + ': Successfully reloaded "' + message.match_data[1] + '"' )
397 | })
398 | }
399 |
400 |
--------------------------------------------------------------------------------
/sandboxed-ipc-repl.rkt:
--------------------------------------------------------------------------------
1 | #lang racket
2 | (require racket/sandbox
3 | racket/pretty)
4 | (require (planet "describe.rkt" ("williams" "describe.plt" 1 3)))
5 |
6 | (define e (make-evaluator 'racket/base '(require racket/list)
7 | '(require racket/vector)
8 | '(require racket/function)
9 | '(require racket/mpair)
10 | '(require racket/port)
11 | '(require net/url)
12 | '(require (planet "describe.rkt" ("williams" "describe.plt" 1 3)))))
13 |
14 | (e '(define (join lst str)
15 | (let jn ([l lst] [a ""])
16 | (cond [(null? (cdr l)) (string-append a (car l))]
17 | [else (jn (cdr l) (string-append a (car l) str))]))))
18 |
19 | (e '(define (.. start end)
20 | (let ([diff (- end start)])
21 | (build-list diff (λ (n) (+ n start))))))
22 |
23 | (pretty-write (e (read)))
24 |
--------------------------------------------------------------------------------
/settings.json:
--------------------------------------------------------------------------------
1 | { "server": "irc.freenode.net"
2 | , "nick": "david_mark"
3 | , "channels": [ "#RubyOnRails", "#runlevel6", "#inimino", "#prototype", "#jquery-ot", "#wadsup" ]
4 | , "user":
5 | { "username": "david_mark"
6 | , "hostname": "intertubes"
7 | , "servername": "tube001"
8 | , "realname": "Prototype Bot"
9 | }
10 | , "logdir": "logs"
11 | , "plugins": [ "caniuse"
12 | , "duckduckgo"
13 | , "google"
14 | , "octo"
15 | , "weather"
16 | , "wolframalpha"
17 | ]
18 | }
--------------------------------------------------------------------------------
/srepl-1.0.0-SNAPSHOT-standalone.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gf3/protobot/c08e286972f08d16549c65da3d8e740178b003e0/srepl-1.0.0-SNAPSHOT-standalone.jar
--------------------------------------------------------------------------------
/strftime.js:
--------------------------------------------------------------------------------
1 | // Written by Gianni Chiappetta - gianni[at]runlevel6[dot]org
2 | // Released under the WTFPL
3 |
4 | if ( typeof Date.prototype.strftime == 'undefined' ) {
5 | /**
6 | * Date#strftime(format) -> String
7 | * - format (String): Formats time according to the directives in the given format string. Any text not listed as a directive will be passed through to the output string.
8 | *
9 | * Ruby-style date formatting. Format matchers:
10 | *
11 | * %a - The abbreviated weekday name (``Sun'')
12 | * %A - The full weekday name (``Sunday'')
13 | * %b - The abbreviated month name (``Jan'')
14 | * %B - The full month name (``January'')
15 | * %c - The preferred local date and time representation
16 | * %d - Day of the month (01..31)
17 | * %e - Day of the month without leading zeroes (1..31)
18 | * %H - Hour of the day, 24-hour clock (00..23)
19 | * %I - Hour of the day, 12-hour clock (01..12)
20 | * %j - Day of the year (001..366)
21 | * %k - Hour of the day, 24-hour clock w/o leading zeroes (0..23)
22 | * %l - Hour of the day, 12-hour clock w/o leading zeroes (1..12)
23 | * %m - Month of the year (01..12)
24 | * %M - Minute of the hour (00..59)
25 | * %p - Meridian indicator (``AM'' or ``PM'')
26 | * %P - Meridian indicator (``am'' or ``pm'')
27 | * %S - Second of the minute (00..60)
28 | * %U - Week number of the current year,
29 | * starting with the first Sunday as the first
30 | * day of the first week (00..53)
31 | * %W - Week number of the current year,
32 | * starting with the first Monday as the first
33 | * day of the first week (00..53)
34 | * %w - Day of the week (Sunday is 0, 0..6)
35 | * %x - Preferred representation for the date alone, no time
36 | * %X - Preferred representation for the time alone, no date
37 | * %y - Year without a century (00..99)
38 | * %Y - Year with century
39 | * %Z - Time zone name
40 | * %z - Time zone expressed as a UTC offset (``-04:00'')
41 | * %% - Literal ``%'' character
42 | *
43 | * http://www.ruby-doc.org/core/classes/Time.html#M000298
44 | *
45 | **/
46 |
47 | Object.defineProperty( Date.prototype, 'strftime',
48 | { value: (function(){
49 | var cache = { start_of_year: new Date( 'Jan 1 ' + new Date()).getFullYear() }
50 | , regexp = /%([a-z]|%)/mig
51 | , day_in_ms = 1000 * 60 * 60 * 24
52 | , days = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ]
53 | , months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]
54 | , abbr_days = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
55 | , abbr_months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
56 | , formats =
57 | { 'a': weekday_name_abbr
58 | , 'A': weekday_name
59 | , 'b': month_name_abbr
60 | , 'B': month_name
61 | , 'c': default_local
62 | , 'd': day_padded
63 | , 'e': day
64 | , 'H': hour_24_padded
65 | , 'I': hour_padded
66 | , 'j': day_of_year
67 | , 'k': hour_24
68 | , 'l': hour
69 | , 'm': month
70 | , 'M': minute
71 | , 'p': meridian_upcase
72 | , 'P': meridian
73 | , 'S': second
74 | , 'U': week_number_from_sunday
75 | // , 'W': week_number_from_monday
76 | , 'w': day_of_week
77 | , 'x': default_local_date
78 | , 'X': default_local_time
79 | , 'y': year_abbr
80 | , 'Y': year
81 | // , 'Z': time_zone_name
82 | , 'z': time_zone_offset
83 | , '%': function() { return '%' }
84 | }
85 |
86 | // Strftime
87 | return strftime
88 |
89 | /* ------------------------------ Utility Functions ------------------------------ */
90 | // day
91 | function day( date ) {
92 | return date.getDate() + ''
93 | }
94 |
95 | // day_of_week
96 | function day_of_week( date ) {
97 | return date.getDay() + ''
98 | }
99 |
100 | // day_of_year
101 | function day_of_year( date ) {
102 | return ( ( ( date.getTime() - cache[ 'start_of_year' ].getTime() ) / day_in_ms + 1 ) + '' ).split( /\./ )[0]
103 | }
104 |
105 | // day_padded
106 | function day_padded( date ) {
107 | return ( '0' + day( date ) ).slice(-2)
108 | }
109 |
110 | // default_local
111 | function default_local( date ) {
112 | return date.toLocaleString()
113 | }
114 |
115 | // default_local_date
116 | function default_local_date( date ) {
117 | return date.toLocaleDateString()
118 | }
119 |
120 | // default_local_time
121 | function default_local_time( date ) {
122 | return date.toLocaleTimeString()
123 | }
124 |
125 | // hour
126 | function hour( date ) {
127 | var hour = date.getHours()
128 |
129 | if ( hour === 0 )
130 | hour = 12
131 | else if ( hour > 12 )
132 | hour -= 12
133 |
134 | return hour + ''
135 | }
136 |
137 | // hour_24
138 | function hour_24( date ) {
139 | return date.getHours()
140 | }
141 |
142 | // hour_24_padded
143 | function hour_24_padded( date ) {
144 | return ( '0' + hour_24( date ) ).slice(-2)
145 | }
146 |
147 | // hour_padded
148 | function hour_padded( date ) {
149 | return ( '0' + hour( date ) ).slice(-2)
150 | }
151 |
152 | // meridian
153 | function meridian( date ) {
154 | return date.getHours() >= 12 ? 'pm' : 'am'
155 | }
156 |
157 | // meridian_upcase
158 | function meridian_upcase( date ) {
159 | return meridian( date ).toUpperCase()
160 | }
161 |
162 | // minute
163 | function minute( date ) {
164 | return ( '0' + date.getMinutes() ).slice(-2)
165 | }
166 |
167 | // month
168 | function month( date ) {
169 | return ( '0' + ( date.getMonth() + 1 ) ).slice(-2)
170 | }
171 |
172 | // month_name
173 | function month_name( date ) {
174 | return months[ date.getMonth() ]
175 | }
176 |
177 | // month_name_abbr
178 | function month_name_abbr( date ) {
179 | return abbr_months[ date.getMonth() ]
180 | }
181 |
182 | // second
183 | function second( date ) {
184 | return ( '0' + date.getSeconds() ).slice(-2)
185 | }
186 |
187 | // time_zone_offset
188 | function time_zone_offset( date ) {
189 | var tz_offset = date.getTimezoneOffset()
190 | return ( tz_offset >= 0 ? '-' : '' ) + ( '0' + ( tz_offset / 60 ) ).slice(-2) + ':' + ( '0' + ( tz_offset % 60 ) ).slice(-2)
191 | }
192 |
193 | // week_number_from_sunday
194 | function week_number_from_sunday( date ) {
195 | return ( '0' + Math.round( parseInt( day_of_year( date ), 10 ) / 7 ) ).slice(-2)
196 | }
197 |
198 | // weekday_name
199 | function weekday_name( date ) {
200 | return days[ date.getDay() ]
201 | }
202 |
203 | // weekday_name_abbr
204 | function weekday_name_abbr( date ) {
205 | return abbr_days[ date.getDay() ]
206 | }
207 |
208 | // year
209 | function year( date ) {
210 | return date.getFullYear() + ''
211 | }
212 |
213 | // year_abbr
214 | function year_abbr( date ) {
215 | return year( date ).slice(-2)
216 | }
217 |
218 | /*------------------------------ Main ------------------------------*/
219 | function strftime( format ) {
220 | var match
221 | , output = format
222 | cache[ 'start_of_year' ] = new Date( 'Jan 1 ' + this.getFullYear() )
223 |
224 | while ( match = regexp.exec( format ) )
225 | if ( match[1] in formats )
226 | output = output.replace( new RegExp( match[0], 'mg' ), formats[ match[1] ](this) )
227 |
228 | return output
229 | }
230 | })()
231 | })
232 | }
233 |
234 |
--------------------------------------------------------------------------------
/vendor/strftime/strftime.js:
--------------------------------------------------------------------------------
1 | // Written by Gianni Chiappetta - gianni[at]runlevel6[dot]org
2 | // Released under the WTFPL
3 |
4 | if ( typeof Date.prototype.strftime == 'undefined' ) {
5 | /**
6 | * Date#strftime(format) -> String
7 | * - format (String): Formats time according to the directives in the given format string. Any text not listed as a directive will be passed through to the output string.
8 | *
9 | * Ruby-style date formatting. Format matchers:
10 | *
11 | * %a - The abbreviated weekday name (``Sun'')
12 | * %A - The full weekday name (``Sunday'')
13 | * %b - The abbreviated month name (``Jan'')
14 | * %B - The full month name (``January'')
15 | * %c - The preferred local date and time representation
16 | * %d - Day of the month (01..31)
17 | * %e - Day of the month without leading zeroes (1..31)
18 | * %H - Hour of the day, 24-hour clock (00..23)
19 | * %I - Hour of the day, 12-hour clock (01..12)
20 | * %j - Day of the year (001..366)
21 | * %k - Hour of the day, 24-hour clock w/o leading zeroes (0..23)
22 | * %l - Hour of the day, 12-hour clock w/o leading zeroes (1..12)
23 | * %m - Month of the year (01..12)
24 | * %M - Minute of the hour (00..59)
25 | * %p - Meridian indicator (``AM'' or ``PM'')
26 | * %P - Meridian indicator (``am'' or ``pm'')
27 | * %S - Second of the minute (00..60)
28 | * %U - Week number of the current year,
29 | * starting with the first Sunday as the first
30 | * day of the first week (00..53)
31 | * %W - Week number of the current year,
32 | * starting with the first Monday as the first
33 | * day of the first week (00..53)
34 | * %w - Day of the week (Sunday is 0, 0..6)
35 | * %x - Preferred representation for the date alone, no time
36 | * %X - Preferred representation for the time alone, no date
37 | * %y - Year without a century (00..99)
38 | * %Y - Year with century
39 | * %Z - Time zone name
40 | * %z - Time zone expressed as a UTC offset (``-04:00'')
41 | * %% - Literal ``%'' character
42 | *
43 | * http://www.ruby-doc.org/core/classes/Time.html#M000298
44 | *
45 | **/
46 |
47 | Object.defineProperty( Date.prototype, 'strftime',
48 | { value: (function(){
49 | var cache = { start_of_year: new Date( 'Jan 1 ' + new Date()).getFullYear() }
50 | , regexp = /%([a-z]|%)/mig
51 | , day_in_ms = 1000 * 60 * 60 * 24
52 | , days = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ]
53 | , months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]
54 | , abbr_days = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
55 | , abbr_months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
56 | , formats =
57 | { 'a': weekday_name_abbr
58 | , 'A': weekday_name
59 | , 'b': month_name_abbr
60 | , 'B': month_name
61 | , 'c': default_local
62 | , 'd': day_padded
63 | , 'e': day
64 | , 'H': hour_24_padded
65 | , 'I': hour_padded
66 | , 'j': day_of_year
67 | , 'k': hour_24
68 | , 'l': hour
69 | , 'm': month
70 | , 'M': minute
71 | , 'p': meridian_upcase
72 | , 'P': meridian
73 | , 'S': second
74 | , 'U': week_number_from_sunday
75 | // , 'W': week_number_from_monday
76 | , 'w': day_of_week
77 | , 'x': default_local_date
78 | , 'X': default_local_time
79 | , 'y': year_abbr
80 | , 'Y': year
81 | // , 'Z': time_zone_name
82 | , 'z': time_zone_offset
83 | , '%': function() { return '%' }
84 | }
85 |
86 | // Strftime
87 | return strftime
88 |
89 | /* ------------------------------ Utility Functions ------------------------------ */
90 | // day
91 | function day( date ) {
92 | return date.getDate() + ''
93 | }
94 |
95 | // day_of_week
96 | function day_of_week( date ) {
97 | return date.getDay() + ''
98 | }
99 |
100 | // day_of_year
101 | function day_of_year( date ) {
102 | return ( ( ( date.getTime() - cache[ 'start_of_year' ].getTime() ) / day_in_ms + 1 ) + '' ).split( /\./ )[0]
103 | }
104 |
105 | // day_padded
106 | function day_padded( date ) {
107 | return ( '0' + day( date ) ).slice(-2)
108 | }
109 |
110 | // default_local
111 | function default_local( date ) {
112 | return date.toLocaleString()
113 | }
114 |
115 | // default_local_date
116 | function default_local_date( date ) {
117 | return date.toLocaleDateString()
118 | }
119 |
120 | // default_local_time
121 | function default_local_time( date ) {
122 | return date.toLocaleTimeString()
123 | }
124 |
125 | // hour
126 | function hour( date ) {
127 | var hour = date.getHours()
128 |
129 | if ( hour === 0 )
130 | hour = 12
131 | else if ( hour > 12 )
132 | hour -= 12
133 |
134 | return hour + ''
135 | }
136 |
137 | // hour_24
138 | function hour_24( date ) {
139 | return date.getHours()
140 | }
141 |
142 | // hour_24_padded
143 | function hour_24_padded( date ) {
144 | return ( '0' + hour_24( date ) ).slice(-2)
145 | }
146 |
147 | // hour_padded
148 | function hour_padded( date ) {
149 | return ( '0' + hour( date ) ).slice(-2)
150 | }
151 |
152 | // meridian
153 | function meridian( date ) {
154 | return date.getHours() >= 12 ? 'pm' : 'am'
155 | }
156 |
157 | // meridian_upcase
158 | function meridian_upcase( date ) {
159 | return meridian( date ).toUpperCase()
160 | }
161 |
162 | // minute
163 | function minute( date ) {
164 | return ( '0' + date.getMinutes() ).slice(-2)
165 | }
166 |
167 | // month
168 | function month( date ) {
169 | return ( '0' + ( date.getMonth() + 1 ) ).slice(-2)
170 | }
171 |
172 | // month_name
173 | function month_name( date ) {
174 | return months[ date.getMonth() ]
175 | }
176 |
177 | // month_name_abbr
178 | function month_name_abbr( date ) {
179 | return abbr_months[ date.getMonth() ]
180 | }
181 |
182 | // second
183 | function second( date ) {
184 | return ( '0' + date.getSeconds() ).slice(-2)
185 | }
186 |
187 | // time_zone_offset
188 | function time_zone_offset( date ) {
189 | var tz_offset = date.getTimezoneOffset()
190 | return ( tz_offset >= 0 ? '-' : '' ) + ( '0' + ( tz_offset / 60 ) ).slice(-2) + ':' + ( '0' + ( tz_offset % 60 ) ).slice(-2)
191 | }
192 |
193 | // week_number_from_sunday
194 | function week_number_from_sunday( date ) {
195 | return ( '0' + Math.round( parseInt( day_of_year( date ), 10 ) / 7 ) ).slice(-2)
196 | }
197 |
198 | // weekday_name
199 | function weekday_name( date ) {
200 | return days[ date.getDay() ]
201 | }
202 |
203 | // weekday_name_abbr
204 | function weekday_name_abbr( date ) {
205 | return abbr_days[ date.getDay() ]
206 | }
207 |
208 | // year
209 | function year( date ) {
210 | return date.getFullYear() + ''
211 | }
212 |
213 | // year_abbr
214 | function year_abbr( date ) {
215 | return year( date ).slice(-2)
216 | }
217 |
218 | /*------------------------------ Main ------------------------------*/
219 | function strftime( format ) {
220 | var match
221 | , output = format
222 | cache[ 'start_of_year' ] = new Date( 'Jan 1 ' + this.getFullYear() )
223 |
224 | while ( match = regexp.exec( format ) )
225 | if ( match[1] in formats )
226 | output = output.replace( new RegExp( match[0], 'mg' ), formats[ match[1] ](this) )
227 |
228 | return output
229 | }
230 | })()
231 | })
232 | }
233 |
234 |
--------------------------------------------------------------------------------
/vendor/unescape/unescape.js:
--------------------------------------------------------------------------------
1 | var entities =
2 | { ' ': ' '
3 | , '¡': '¡'
4 | , '¢': '¢'
5 | , '£': '£'
6 | , '¤': '¤'
7 | , '¥': '¥'
8 | , '¦': '¦'
9 | , '§': '§'
10 | , '¨': '¨'
11 | , '©': '©'
12 | , 'ª': 'ª'
13 | , '«': '«'
14 | , '¬': '¬'
15 | , '': ''
16 | , '®': '®'
17 | , '¯': '¯'
18 | , '°': '°'
19 | , '±': '±'
20 | , '²': '²'
21 | , '³': '³'
22 | , '´': '´'
23 | , 'µ': 'µ'
24 | , '¶': '¶'
25 | , '·': '·'
26 | , '¸': '¸'
27 | , '¹': '¹'
28 | , 'º': 'º'
29 | , '»': '»'
30 | , '¼': '¼'
31 | , '½': '½'
32 | , '¾': '¾'
33 | , '¿': '¿'
34 | , 'À': 'À'
35 | , 'Á': 'Á'
36 | , 'Â': 'Â'
37 | , 'Ã': 'Ã'
38 | , 'Ä': 'Ä'
39 | , 'Å': 'Å'
40 | , 'Æ': 'Æ'
41 | , 'Ç': 'Ç'
42 | , 'È': 'È'
43 | , 'É': 'É'
44 | , 'Ê': 'Ê'
45 | , 'Ë': 'Ë'
46 | , 'Ì': 'Ì'
47 | , 'Í': 'Í'
48 | , 'Î': 'Î'
49 | , 'Ï': 'Ï'
50 | , 'Ð': 'Ð'
51 | , 'Ñ': 'Ñ'
52 | , 'Ò': 'Ò'
53 | , 'Ó': 'Ó'
54 | , 'Ô': 'Ô'
55 | , 'Õ': 'Õ'
56 | , 'Ö': 'Ö'
57 | , '×': '×'
58 | , 'Ø': 'Ø'
59 | , 'Ù': 'Ù'
60 | , 'Ú': 'Ú'
61 | , 'Û': 'Û'
62 | , 'Ü': 'Ü'
63 | , 'Ý': 'Ý'
64 | , 'Þ': 'Þ'
65 | , 'ß': 'ß'
66 | , 'à': 'à'
67 | , 'á': 'á'
68 | , 'â': 'â'
69 | , 'ã': 'ã'
70 | , 'ä': 'ä'
71 | , 'å': 'å'
72 | , 'æ': 'æ'
73 | , 'ç': 'ç'
74 | , 'è': 'è'
75 | , 'é': 'é'
76 | , 'ê': 'ê'
77 | , 'ë': 'ë'
78 | , 'ì': 'ì'
79 | , 'í': 'í'
80 | , 'î': 'î'
81 | , 'ï': 'ï'
82 | , 'ð': 'ð'
83 | , 'ñ': 'ñ'
84 | , 'ò': 'ò'
85 | , 'ó': 'ó'
86 | , 'ô': 'ô'
87 | , 'õ': 'õ'
88 | , 'ö': 'ö'
89 | , '÷': '÷'
90 | , 'ø': 'ø'
91 | , 'ù': 'ù'
92 | , 'ú': 'ú'
93 | , 'û': 'û'
94 | , 'ü': 'ü'
95 | , 'ý': 'ý'
96 | , 'þ': 'þ'
97 | , 'ÿ': 'ÿ'
98 | , 'ƒ': 'ƒ'
99 | , 'Α': 'Α'
100 | , 'Β': 'Β'
101 | , 'Γ': 'Γ'
102 | , 'Δ': 'Δ'
103 | , 'Ε': 'Ε'
104 | , 'Ζ': 'Ζ'
105 | , 'Η': 'Η'
106 | , 'Θ': 'Θ'
107 | , 'Ι': 'Ι'
108 | , 'Κ': 'Κ'
109 | , 'Λ': 'Λ'
110 | , 'Μ': 'Μ'
111 | , 'Ν': 'Ν'
112 | , 'Ξ': 'Ξ'
113 | , 'Ο': 'Ο'
114 | , 'Π': 'Π'
115 | , 'Ρ': 'Ρ'
116 | , 'Σ': 'Σ'
117 | , 'Τ': 'Τ'
118 | , 'Υ': 'Υ'
119 | , 'Φ': 'Φ'
120 | , 'Χ': 'Χ'
121 | , 'Ψ': 'Ψ'
122 | , 'Ω': 'Ω'
123 | , 'α': 'α'
124 | , 'β': 'β'
125 | , 'γ': 'γ'
126 | , 'δ': 'δ'
127 | , 'ε': 'ε'
128 | , 'ζ': 'ζ'
129 | , 'η': 'η'
130 | , 'θ': 'θ'
131 | , 'ι': 'ι'
132 | , 'κ': 'κ'
133 | , 'λ': 'λ'
134 | , 'μ': 'μ'
135 | , 'ν': 'ν'
136 | , 'ξ': 'ξ'
137 | , 'ο': 'ο'
138 | , 'π': 'π'
139 | , 'ρ': 'ρ'
140 | , 'ς': 'ς'
141 | , 'σ': 'σ'
142 | , 'τ': 'τ'
143 | , 'υ': 'υ'
144 | , 'φ': 'φ'
145 | , 'χ': 'χ'
146 | , 'ψ': 'ψ'
147 | , 'ω': 'ω'
148 | , 'ϑ': 'ϑ'
149 | , 'ϒ': 'ϒ'
150 | , 'ϖ': 'ϖ'
151 | , '•': '•'
152 | , '…': '…'
153 | , '′': '′'
154 | , '″': '″'
155 | , '‾': '‾'
156 | , '⁄': '⁄'
157 | , '℘': '℘'
158 | , 'ℑ': 'ℑ'
159 | , 'ℜ': 'ℜ'
160 | , '™': '™'
161 | , 'ℵ': 'ℵ'
162 | , '←': '←'
163 | , '↑': '↑'
164 | , '→': '→'
165 | , '↓': '↓'
166 | , '↔': '↔'
167 | , '↵': '↵'
168 | , '⇐': '⇐'
169 | , '⇑': '⇑'
170 | , '⇒': '⇒'
171 | , '⇓': '⇓'
172 | , '⇔': '⇔'
173 | , '∀': '∀'
174 | , '∂': '∂'
175 | , '∃': '∃'
176 | , '∅': '∅'
177 | , '∇': '∇'
178 | , '∈': '∈'
179 | , '∉': '∉'
180 | , '∋': '∋'
181 | , '∏': '∏'
182 | , '∑': '∑'
183 | , '−': '−'
184 | , '∗': '∗'
185 | , '√': '√'
186 | , '∝': '∝'
187 | , '∞': '∞'
188 | , '∠': '∠'
189 | , '∧': '∧'
190 | , '∨': '∨'
191 | , '∩': '∩'
192 | , '∪': '∪'
193 | , '∫': '∫'
194 | , '∴': '∴'
195 | , '∼': '∼'
196 | , '≅': '≅'
197 | , '≈': '≈'
198 | , '≠': '≠'
199 | , '≡': '≡'
200 | , '≤': '≤'
201 | , '≥': '≥'
202 | , '⊂': '⊂'
203 | , '⊃': '⊃'
204 | , '⊄': '⊄'
205 | , '⊆': '⊆'
206 | , '⊇': '⊇'
207 | , '⊕': '⊕'
208 | , '⊗': '⊗'
209 | , '⊥': '⊥'
210 | , '⋅': '⋅'
211 | , '⌈': '⌈'
212 | , '⌉': '⌉'
213 | , '⌊': '⌊'
214 | , '⌋': '⌋'
215 | , '〈': '〈'
216 | , '〉': '〉'
217 | , '◊': '◊'
218 | , '♠': '♠'
219 | , '♣': '♣'
220 | , '♥': '♥'
221 | , '♦': '♦'
222 | , '"': '"'
223 | , '&': '&'
224 | , '<': '<'
225 | , '>': '>'
226 | , 'Œ': 'Œ'
227 | , 'œ': 'œ'
228 | , 'Š': 'Š'
229 | , 'š': 'š'
230 | , 'Ÿ': 'Ÿ'
231 | , 'ˆ': 'ˆ'
232 | , '˜': '˜'
233 | , ' ': ' '
234 | , ' ': ' '
235 | , ' ': ' '
236 | , '': ''
237 | , '': ''
238 | , '': ''
239 | , '': ''
240 | , '–': '–'
241 | , '—': '—'
242 | , '‘': '‘'
243 | , '’': '’'
244 | , '‚': '‚'
245 | , '“': '“'
246 | , '”': '”'
247 | , '„': '„'
248 | , '†': '†'
249 | , '‡': '‡'
250 | , '‰': '‰'
251 | , '‹': '‹'
252 | , '›': '›'
253 | , '€': '€'
254 | }
255 |
256 | function unescapeEntity( input ) {
257 | if ( input.charAt(1) === '#' )
258 | return String.fromCharCode( parseInt( input.substr(2), 10 ) )
259 | else if ( entities.hasOwnProperty( input ) )
260 | return entities[ input ]
261 | }
262 |
263 | module.exports = function unescapeAll( input ) {
264 | var entityRe = /&(#?)(\d{1,5}|\w{1,8});/gm
265 | return input.replace( entityRe, unescapeEntity )
266 | }
267 |
--------------------------------------------------------------------------------