WIP - Github Finder integration
need to replace jsonp and github stuff with gitter
28
GithubFinder/css/code_highlighter.css
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#code.Dark { background-color: #191919; color: white; }
|
||||
#code.Dark .comment { color: #5E5A5F; }
|
||||
#code.Dark .string { color: #93996D; }
|
||||
#code.Dark .keywords { color: #CAA76F;}
|
||||
#code.Dark .brackets { color: #F5F8F8; }
|
||||
#code.Dark .symbol { color: #C06F52; }
|
||||
|
||||
/* CSS */
|
||||
#code.Dark .properties { color: #C4AD7A}
|
||||
#code.Dark .selectors { color: #977042 }
|
||||
|
||||
|
||||
|
||||
|
||||
#code.Light { background-color: white; color: black; }
|
||||
#code.Light .comment { color: green; }
|
||||
#code.Light .string { color: teal; }
|
||||
#code.Light .keywords { color: navy; font-weight: bold; }
|
||||
#code.Light .brackets { color: navy; }
|
||||
#code.Light .symbol { font-weight: bold; }
|
||||
|
||||
/* CSS */
|
||||
#code.Light .properties { color: #C4AD7A}
|
||||
#code.Light .selectors { color: #977042 }
|
||||
|
||||
/* CPP */
|
||||
#code.Ligh .datatype { color: #006699; }
|
||||
#code.Dark .datatype { font-weight: bold;}
|
||||
BIN
GithubFinder/img/dir.gif
Normal file
|
After Width: | Height: | Size: 101 B |
BIN
GithubFinder/img/dir.png
Normal file
|
After Width: | Height: | Size: 485 B |
BIN
GithubFinder/img/folder_explore.png
Normal file
|
After Width: | Height: | Size: 679 B |
BIN
GithubFinder/img/la.gif
Normal file
|
After Width: | Height: | Size: 64 B |
BIN
GithubFinder/img/la.png
Normal file
|
After Width: | Height: | Size: 242 B |
BIN
GithubFinder/img/la_h.gif
Normal file
|
After Width: | Height: | Size: 53 B |
BIN
GithubFinder/img/la_h.png
Normal file
|
After Width: | Height: | Size: 154 B |
BIN
GithubFinder/img/txt.gif
Normal file
|
After Width: | Height: | Size: 94 B |
BIN
GithubFinder/img/txt.png
Normal file
|
After Width: | Height: | Size: 290 B |
360
GithubFinder/javascripts/f.js
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
/* to padd to get exactly 10,240 bytes */
|
||||
// ;"ALEXLE";
|
||||
window.F = Class.create({
|
||||
initialize: function(options){
|
||||
options = Object.extend( {
|
||||
user_id: 'samsonjs'
|
||||
,repository: SJS.projName
|
||||
,branch: 'master'
|
||||
}, options || {} );
|
||||
|
||||
this.ps = [];
|
||||
this.shas = {};
|
||||
|
||||
this.u = options.user_id;
|
||||
this.r = options.repository;
|
||||
this.b = options.branch;
|
||||
this.id = options.id;
|
||||
|
||||
this.render(this.id);
|
||||
|
||||
this.repo = null;
|
||||
|
||||
/* Prototype RC2 */
|
||||
// document.on('click','a[data-sha]', function( event, element ){
|
||||
// this.click( element.readAttribute('data-sha'), element );
|
||||
// element.blur();
|
||||
// }.bind(this) );
|
||||
|
||||
document.observe('click', function(e) {
|
||||
e = e.findElement();
|
||||
if( !e.readAttribute('data-sha') ) return;
|
||||
this.click( e.readAttribute('data-sha'), e );
|
||||
e.blur();
|
||||
}.bind(this));
|
||||
|
||||
var idc = $('in'),
|
||||
s = function() { idc.className = 'on' },
|
||||
h = function() { if( Ajax.activeRequestCount == 0 ) idc.className = 'off' };
|
||||
Ajax.Responders.register( {
|
||||
onException: function(r,x) { console.log(x);h() }
|
||||
,onComplete: h
|
||||
,onCreate: s
|
||||
});
|
||||
|
||||
/* init plugins */
|
||||
if( FP )
|
||||
for( var i = 0; i < FP.length; i++ )
|
||||
new FP[i](this);
|
||||
|
||||
/* extractURL: if user assigns user_id, repo, branch */
|
||||
this.xU();
|
||||
this.oR(); // open repo
|
||||
}
|
||||
|
||||
|
||||
,xU: function() {
|
||||
|
||||
}
|
||||
|
||||
,render: function(selector) {
|
||||
$(selector || document.body).insert(this.h());
|
||||
this.psW = $('ps_w');
|
||||
this.bW = $('b_w');
|
||||
}
|
||||
|
||||
,h: function() {
|
||||
return [
|
||||
'<div id=content>',
|
||||
'<div id=finder class=tbb>',
|
||||
'<div id=r_w>',
|
||||
'<div class=p>',
|
||||
'<table width=100%>',
|
||||
'<tr>',
|
||||
'<td align=left id=r></td>', // repo
|
||||
'<td align=center>Branch: <span id=brs_w></span></td>', // branches
|
||||
'<td align=center id=in>Loading...</td>',
|
||||
'<td align=right style=font-weight:bold><a href=https://github.com/sr3d/GithubFinder>Github Finder</a></td>',
|
||||
'</tr>',
|
||||
'</table>',
|
||||
'</div>', // .p
|
||||
'</div>', // #r_w
|
||||
|
||||
'<div id=b_w>', // browser wrapper
|
||||
'<div id=ps_w style="width:200px"></div>',
|
||||
'</div>',
|
||||
|
||||
// '<div class=clear></div>',
|
||||
'</div>', // #finder
|
||||
|
||||
'<div id=f_c_w style="display:none">', // file content wrapper
|
||||
'<div class=p>',
|
||||
'<div id=f_h class=big></div>',
|
||||
'</div>',
|
||||
|
||||
'<div id=f_c>', // file content
|
||||
'<div class=p>', // padding
|
||||
'<div id=f_w>', // file wrapper
|
||||
'<div id=f></div>', // file
|
||||
'</div>',
|
||||
'<div id=diffoutput></div>',
|
||||
'</div>', // padding
|
||||
'</div>',
|
||||
|
||||
'<div class=clear></div>',
|
||||
'</div>', // #f_c_w
|
||||
|
||||
'<div id=footer><b><a href=http://github.com/sr3d/GithubFinder>GithubFinder</a></b></div>',
|
||||
'</div>' // # content
|
||||
].join(' ');
|
||||
}
|
||||
|
||||
/* openRepo */
|
||||
,oR: function(repo) {
|
||||
this.reset()
|
||||
|
||||
var u,r,b;
|
||||
if( !repo ) {
|
||||
/* check URL params */
|
||||
var p = uP();
|
||||
if( p["user_id"] && p["repo"] ) {
|
||||
u = this.u = p["user_id"];
|
||||
r = this.r = p["repo"]
|
||||
b = this.b = p["branch"] || 'master';
|
||||
} else {
|
||||
// debugger
|
||||
/* if user just come from a github repo ... */
|
||||
var m = (new RegExp("^http://github.com/(.+)","i")).exec(document.referrer),
|
||||
path = m ? m[1].split('/') : [];
|
||||
|
||||
if( path[0] && path[1] ) {
|
||||
u = this.u = path[0];
|
||||
r = this.r = path[1];
|
||||
b = this.b = path[3] || 'master';
|
||||
} else { /* default to app settings */
|
||||
u = this.u;
|
||||
r = this.r;
|
||||
b = this.b;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* User hits the "Go" button: grabbing the user/repo */
|
||||
repo = repo.split('/');
|
||||
if( repo.length < 2 ) { alert('invalid repository!'); return; }
|
||||
|
||||
u = this.u = repo[0];
|
||||
r = this.r = repo[1];
|
||||
b = this.b = ($('brs') ? $F('brs') : b) || 'master';
|
||||
}
|
||||
|
||||
|
||||
$('r').innerHTML = u + '/' + r;
|
||||
|
||||
/* Load the master branch */
|
||||
GH.Commits.listBranch( u, r, b, {
|
||||
onData: function(cs) {
|
||||
// if(!cs.commits) { alert('repo not found'); return; }
|
||||
var tree_sha = cs.commits[0].tree;
|
||||
this.renderPanel(tree_sha);
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
/* Show branches info */
|
||||
GH.Repo.listBranches( u, r, {
|
||||
onData: function(bes) {
|
||||
this.bes = $H(bes);
|
||||
this.rBs();
|
||||
}.bind(this)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
,reset: function() {
|
||||
$('f_c_w').hide();
|
||||
this.cI = -1;
|
||||
this.pI = 0;
|
||||
|
||||
while(this.ps.length > 0)
|
||||
(this.ps.pop()).dispose();
|
||||
}
|
||||
|
||||
,browse: function() {
|
||||
this.oR( $('r').innerHTML );
|
||||
return false;
|
||||
}
|
||||
|
||||
/* render branches */
|
||||
,rBs: function() {
|
||||
var h = '<select id=brs>';
|
||||
this.bes.each(function(b) {
|
||||
h +=
|
||||
'<option ' + (this.b == b.key ? ' selected=""' : ' ' ) + '>' +
|
||||
b.key +
|
||||
'</option>';
|
||||
}.bind(this));
|
||||
// html.push('</select>');
|
||||
$('brs_w').innerHTML = h + '</select>';
|
||||
document.getElementById('brs').observe('change', function() {
|
||||
this.browse();
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
,renderPanel: function( sh, ix, it ) {
|
||||
ix = ix || 0;
|
||||
/* clear previously opened panels */
|
||||
for( var i = this.ps.length - 1; i > ix; i-- ) {
|
||||
(this.ps.pop()).dispose();
|
||||
}
|
||||
this.open( sh, it );
|
||||
}
|
||||
|
||||
,_resizePanelsWrapper: function() {
|
||||
var w = (this.ps.length * 201);
|
||||
this.psW.style.width = w + 'px';
|
||||
|
||||
/* scroll to the last panel */
|
||||
this.bW.scrollLeft = w;
|
||||
}
|
||||
|
||||
/* request the content of the tree and render the panel */
|
||||
,open: function( tree_sha, item ) {
|
||||
GH.Tree.show( this.u, this.r, this.b, tree_sha, {
|
||||
onData: function(tree) { // tree is already sorted
|
||||
/* add all items to cache */
|
||||
for( var i = 0, len = tree.length; i < len; i++ )
|
||||
this.shas[ tree[i].sha ] = tree[i];
|
||||
|
||||
var name = item ? item.name : '' ;
|
||||
// debugger
|
||||
var p = new P( this, { tree: tree, index: this.ps.length, name: name, tree_sha: tree_sha, item: item } );
|
||||
this.ps.push( p );
|
||||
|
||||
this._resizePanelsWrapper();
|
||||
|
||||
}.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @sha: the sha of the object
|
||||
* @e: the source element
|
||||
* @kb: is this trigged by the keyboard
|
||||
*/
|
||||
,click: function(sha, e, kb) {
|
||||
// console.log("kb" + kb);
|
||||
// debugger
|
||||
var it = this.shas[ sha ],
|
||||
ix = +(e.up('.panel')).readAttribute('data-index'),
|
||||
path = "";
|
||||
|
||||
|
||||
/* set selection cursor && focus the item */
|
||||
e.up('ul').select('li.cur').invoke('removeClassName','cur');
|
||||
var p = e.up('div.panel'),
|
||||
li = e.up('li').addClassName('cur'),
|
||||
posTop = li.positionedOffset().top + li.offsetHeight - p.offsetHeight;
|
||||
if( posTop > p.scrollTop) {
|
||||
//p.scrollTop = posTop ;
|
||||
}
|
||||
|
||||
|
||||
/* current index */
|
||||
this.cI = it.index;
|
||||
this.pI = ix; // current panel index;
|
||||
|
||||
/* remember the current selected item */
|
||||
this.ps[ ix ].cI = it.index;
|
||||
|
||||
|
||||
/* don't be trigger happy: ptm = preview timer */
|
||||
if(this._p) clearTimeout( this._p );
|
||||
|
||||
/* set a small delay here incase user switches really fast (e.g. keyboard navigation ) */
|
||||
this._p = setTimeout( function(){
|
||||
|
||||
if( it.type == 'tree' ) {
|
||||
this.renderPanel( it.sha, ix, it );
|
||||
// don't show file preview panel
|
||||
$('f_c_w').hide();
|
||||
} else {
|
||||
|
||||
$('f_c_w').show();
|
||||
if( /text/.test(it.mime_type) ) {
|
||||
$('in').className = 'on';
|
||||
GH.Blob.show( this.u, this.r, it.sha, { onSuccess: function(r) {
|
||||
this.previewTextFile(r.responseText, it);
|
||||
}.bind(this)} );
|
||||
}
|
||||
}
|
||||
|
||||
/* showPreview */
|
||||
var p = function() {
|
||||
$('diffoutput').hide();
|
||||
$('f_c_w').show();
|
||||
$('f_h').innerHTML = path;
|
||||
}
|
||||
|
||||
}.bind(this), (kb ? 350 : 10)); // time out
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
,previewTextFile: function( text, it ) {
|
||||
text = text.replace(/\r\n/, "\n").split(/\n/);
|
||||
|
||||
var ln = [],
|
||||
l = [],
|
||||
sloc = 0;
|
||||
for( var i = 0, len = text.length; i < len; i++ ) {
|
||||
ln.push( '<span>' + (i + 1) + "</span>\n");
|
||||
|
||||
l.push( text[i] ? text[i].replace(/&/g, '&').replace(/</g, '<') : "" );
|
||||
// count actual loc
|
||||
sloc += text[i] ? 1 : 0;
|
||||
}
|
||||
|
||||
if (typeof f.theme === 'undefined') f.theme = 'Light';
|
||||
|
||||
var html = [
|
||||
'<div class=meta>',
|
||||
'<span>' + it.mode + '</span>',
|
||||
'<span>' + text.length + ' lines (' + sloc +' sloc)</span>',
|
||||
'<span>' + it.size + ' bytes</span>',
|
||||
'<span style="float:right">Theme: <select id="theme">',
|
||||
'<option ' + (f.theme == 'Light' ? 'selected' : '' ) + '>Light</option>',
|
||||
'<option ' + (f.theme == 'Dark' ? 'selected' : '' ) + '>Dark</option>',
|
||||
'</select></span>',
|
||||
'</div>',
|
||||
|
||||
'<div id=f_c_s>', // file content scroll
|
||||
'<table cellspacing=0 cellpadding=0>',
|
||||
'<tr>',
|
||||
'<td valign=top>',
|
||||
'<pre class=ln>',
|
||||
ln.join(''),
|
||||
'</pre>',
|
||||
'</td>',
|
||||
|
||||
'<td width=100% valign=top>',
|
||||
'<pre id=code>',
|
||||
l.join("\n"),
|
||||
'</pre>',
|
||||
'</td>',
|
||||
'</tr>',
|
||||
'</div>'
|
||||
];
|
||||
|
||||
$('diffoutput').hide();
|
||||
$('in').className = 'off';
|
||||
$('f').update( html.join('') ).show();
|
||||
|
||||
/* HACK!! */
|
||||
$('theme').observe('change', function() {
|
||||
window.f.theme = $F('theme');
|
||||
$('code').removeClassName('Light').removeClassName('Dark').addClassName(window.f.theme);
|
||||
});
|
||||
}
|
||||
|
||||
,diff:function(){ alert('Diff is disabled.'); }
|
||||
});
|
||||
159
GithubFinder/javascripts/gh.js
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
window.GH = {
|
||||
hash: {}
|
||||
// ,proxy: 'http://alexle.net/experiments/githubfinder/proxy.php?url='
|
||||
,proxy: './proxy.php?url='
|
||||
// ,proxy: ''
|
||||
,api: 'http://github.com/api/v2/json'
|
||||
|
||||
/* set the proxy.php url and switch to the correct AR (AjaxRequest) */
|
||||
,setProxy: function(p) {
|
||||
this.proxy = p;
|
||||
// window.AR = p.indexOf('./') == 0 ? Ajax.Request : JSP;
|
||||
window.AR = JSP;
|
||||
}
|
||||
|
||||
,Commits: {
|
||||
_cache: []
|
||||
/* list all commits for a specific branch */
|
||||
,listBranch: function(u, r, b, o ) {
|
||||
var onData = o.onData,
|
||||
url = GH.api + '/commits/list/' + u + '/' + r + '/' + b;
|
||||
o.onSuccess = function(res) {
|
||||
onData( res.responseText );
|
||||
}
|
||||
new JSP( url, o );
|
||||
}
|
||||
|
||||
,list: function( u, r, b, path, o ) {
|
||||
var self = this,
|
||||
url = GH.api + '/commits/list/' + u + '/' + r + '/' + b + path,
|
||||
onData = o.onData;
|
||||
|
||||
o.onSuccess = function(res) {
|
||||
var cs = res.responseText.commits;
|
||||
// if(!cs) { alert('not found'); return;}
|
||||
/* cache the commits */
|
||||
self._cache[ url ] = cs;
|
||||
onData( cs );
|
||||
}
|
||||
|
||||
/* hit the cache first */
|
||||
if( this._cache[ url ] ) {
|
||||
onData( this._cache[ url ] );
|
||||
return;
|
||||
}
|
||||
|
||||
new JSP( url, o );
|
||||
}
|
||||
|
||||
,show: function( u, r, sha, o ) {
|
||||
var self = this,
|
||||
url = GH.api + '/commits/show/' + u + '/' + r + '/' + sha,
|
||||
onData = o.onData;
|
||||
|
||||
o.onSuccess = function(res) {
|
||||
var c = res.responseText.commit;
|
||||
/* cache */
|
||||
self._cache[ sha ] = c;
|
||||
onData( c );
|
||||
}
|
||||
|
||||
/* hit the cache first */
|
||||
if( this._cache[ sha ] ) {
|
||||
onData( this._cache[ sha ] );
|
||||
return;
|
||||
}
|
||||
|
||||
new JSP( url, o );
|
||||
}
|
||||
}
|
||||
|
||||
,Tree: {
|
||||
_cache: {}
|
||||
,show: function( u, r, b, tree_sha, o ) {
|
||||
var self = this,
|
||||
url = GH.api + '/tree/show/' + u +'/' + r +'/' + tree_sha,
|
||||
onData = o.onData;
|
||||
|
||||
o.onSuccess = function(res) {
|
||||
var tree = res.responseText.tree;
|
||||
// if(!tree) { alert('not found'); return;}
|
||||
tree = tree.sort(function(a,b){
|
||||
// blobs always lose to tree
|
||||
if( a.type == 'blob' && b.type == 'tree' )
|
||||
return 1;
|
||||
if( a.type == 'tree' && b.type == 'blob' )
|
||||
return -1;
|
||||
return a.name > b.name ? 1 : ( a.name < b.name ? - 1 : 0 );
|
||||
});
|
||||
|
||||
/* add the index to the item */
|
||||
for( var i = 0, len = tree.length; i < len; i++ ) {
|
||||
tree[i].index = i;
|
||||
}
|
||||
|
||||
/* cache the tree so that we don't have to re-request every time */
|
||||
self._cache[ tree_sha ] = tree;
|
||||
|
||||
onData(tree);
|
||||
}
|
||||
|
||||
|
||||
/* hit the cache first */
|
||||
if( this._cache[ tree_sha ] ) {
|
||||
onData( this._cache[ tree_sha ] );
|
||||
return;
|
||||
}
|
||||
|
||||
new JSP( url, o);
|
||||
}
|
||||
}
|
||||
|
||||
,Blob: {
|
||||
show: function( u, r, sha, o ) {
|
||||
var url = GH.api + '/blob/show/' + u + '/' + r + '/' + sha;
|
||||
new AR( GH.proxy + url, o );
|
||||
}
|
||||
|
||||
/**
|
||||
* u,r,b: user, repo, branch
|
||||
* fn: filename
|
||||
* o: the options, with callback
|
||||
*/
|
||||
,loadPage: function(u,r,b,fn, o) {
|
||||
var url = 'http://github.com/' + u + '/' + r + '/blob/' + b +'/' + fn;
|
||||
new AR( GH.proxy + url, o );
|
||||
}
|
||||
}
|
||||
|
||||
,Repo: {
|
||||
show: function( u, r, o ) {
|
||||
var url = GH.api + '/repos/show/' + u + '/' + r,
|
||||
onData = o.onData;
|
||||
|
||||
o.onSuccess = function(res) {
|
||||
onData(res.responseText.repository);
|
||||
}
|
||||
new JSP( url, o );
|
||||
}
|
||||
|
||||
,listBranches: function( u, r, o ) {
|
||||
var url = GH.api + '/repos/show/' + u + '/' + r + '/branches',
|
||||
onData = o.onData;
|
||||
o.onSuccess = function(res) {
|
||||
var branches = res.responseText.branches;
|
||||
onData(branches);
|
||||
}
|
||||
new JSP( url, o );
|
||||
}
|
||||
}
|
||||
|
||||
,Raw: {
|
||||
loadBlobAtCommit: function( u, r, commitId, path, options ) {
|
||||
//http://github.com/:user_id/:repo/raw/:commit_id/:path
|
||||
// http://github.com/mojombo/grit/raw/c0f0b4f7a62d2e563b48d0dc5cd9eb3c21e3b4c2/lib/grit.rb
|
||||
url = 'https://github.com/' + u + '/' + r + '/raw/' + commitId + path;
|
||||
new AR( GH.proxy + url, options );
|
||||
}
|
||||
}
|
||||
};
|
||||
97
GithubFinder/javascripts/keyboard.js
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
var Keyboard = Class.create( PluginBase, {
|
||||
initialize: function($super, f) {
|
||||
// $super(f);
|
||||
|
||||
document.observe('keydown', function(e) {
|
||||
if(e.findElement().tagName == 'INPUT') return; // user has focus in something, bail out.
|
||||
|
||||
// var k = e.which ? e.which : e.keyCode; // keycode
|
||||
var k = e.which || e.keyCode; // keycode
|
||||
|
||||
var cI = f.cI,
|
||||
pI = f.pI;
|
||||
|
||||
var p = f.ps[pI]; // panel
|
||||
var t = p.tree; // the panel's tree
|
||||
|
||||
|
||||
var d = function() {
|
||||
if( t[ ++cI ] ) {
|
||||
var item = t[cI];
|
||||
// debugger
|
||||
f.click( item.sha, $$('#p' + pI + ' a')[cI], true );
|
||||
} else {
|
||||
cI--;
|
||||
}
|
||||
};
|
||||
|
||||
var u = function() {
|
||||
if( t[ --cI ] ) {
|
||||
var item = t[cI];
|
||||
f.click( item.sha, $$('#p' + pI + ' a')[cI], true );
|
||||
} else {
|
||||
cI++;
|
||||
}
|
||||
}
|
||||
|
||||
var l = function() {
|
||||
if( f.ps[--pI] ) {
|
||||
// debugger
|
||||
t = f.ps[pI].tree;
|
||||
// get index of the previously selected item
|
||||
cI = f.ps[pI].cI;
|
||||
// var item = f.ps[pI];
|
||||
f.click( t[cI].sha, $$('#p' + pI + ' a')[cI], true );
|
||||
|
||||
} else {
|
||||
pI++; // undo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var r = function() {
|
||||
if( !t[cI] || t[cI].type != 'tree' ) return;
|
||||
|
||||
if( f.ps[++pI] ) {
|
||||
t = f.ps[pI].tree;
|
||||
cI = -1;
|
||||
d(); // down!
|
||||
|
||||
} else {
|
||||
pI--; // undo
|
||||
}
|
||||
}
|
||||
|
||||
// k == 40 ? d() : ( k == 39 ? r() : ( k == 38 ? u() : ( k == 37 ? l() : '';
|
||||
switch( k ) {
|
||||
case 40: // key down
|
||||
d();
|
||||
break;
|
||||
|
||||
case 38: // up
|
||||
u();
|
||||
break;
|
||||
|
||||
case 37: //left
|
||||
l();
|
||||
break
|
||||
|
||||
case 39: // right
|
||||
r();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// console.log("keypress");
|
||||
|
||||
if( k >= 37 && k <= 40)
|
||||
e.stop();
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* add the plugin to the plugins list */
|
||||
FP.push(Keyboard);
|
||||
41
GithubFinder/javascripts/p.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* Panel */
|
||||
window.P = Class.create({
|
||||
initialize: function(f, options) {
|
||||
this.f = f;
|
||||
this.tree = options.tree || [];
|
||||
this.index = options.index || 0 ;
|
||||
this.name = options.name;
|
||||
this.item = options.item;
|
||||
|
||||
this.r();
|
||||
}
|
||||
|
||||
,dispose: function() {
|
||||
$('p' + this.index ).remove();
|
||||
this.p = null;
|
||||
}
|
||||
|
||||
,r: function() {
|
||||
this.f.psW.insert({ bottom: this.h() });
|
||||
}
|
||||
|
||||
,h: function() {
|
||||
var it, css, recent, ix=this.index, t=this.tree,bH = this.f.bW.offsetHeight,
|
||||
h = '<ul class=files>';
|
||||
|
||||
for( var i = 0; i < t.length; i++ ) {
|
||||
it = t[i];
|
||||
|
||||
h += '<li class=' + it.type + '>' +
|
||||
'<span class="ico">' +
|
||||
'<a href="#" data-sha="' + it.sha + '" data-name="' + it.name + '" onclick="return false">' +
|
||||
it.name +
|
||||
'</a>' +
|
||||
'</span>'+
|
||||
'</li>';
|
||||
}
|
||||
h += '</ul>';
|
||||
return '<div id=p' + ix + ' data-index=' + ix +' class=panel style="height:' + bH +'px">' + h + '</div>';
|
||||
}
|
||||
|
||||
});
|
||||
8
GithubFinder/javascripts/plugins/base.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
;window.FP = []; // this array contains the list of all registered plugins
|
||||
|
||||
var PluginBase = Class.create( {
|
||||
initialize: function(o) {
|
||||
if( !this.mixin ) this.mixin = {};
|
||||
Object.extend( o, this.mixin );
|
||||
}
|
||||
} );
|
||||
74
GithubFinder/javascripts/plugins/code_highlighter.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* code highliter */
|
||||
var CH = Class.create( PluginBase, {
|
||||
initialize: function($super, f) {
|
||||
$super(f);
|
||||
|
||||
f.theme = 'Light';
|
||||
// f.theme = 'Dark';
|
||||
|
||||
var hlt = CodeHighlighter;
|
||||
|
||||
var getFiletype = function(filename,text) {
|
||||
var fileType,
|
||||
matchingRules = {
|
||||
'ruby': [ /\.rb$/i, /\.ru$/i, /\bRakefile\b/i, /\bGemfile\b/i, /\.gemspec\b/i, /\bconsole\b/i, /\.rake$/i ]
|
||||
,'css': [ /\.css/i ]
|
||||
,'html': [ /\.html?$/i, /\.aspx$/i, /\.php$/i, /\.erb$/i ]
|
||||
,'javascript': [ /\.js$/i ]
|
||||
,'python': [ /\.py$/i ]
|
||||
,'applescript': [ /\.applescript$/i ]
|
||||
,'yaml': [ /\.yml$/i ]
|
||||
,'cpp': [ /\.c$/i, /\.cpp$/i, /\.h$/i ]
|
||||
,'clojure': [ /\.clj$/i ]
|
||||
,'haskell': [ /\.hs$/i ]
|
||||
};
|
||||
|
||||
|
||||
$H(matchingRules).each(function(type) {
|
||||
for( var i = 0; i < type.value.length; i++ ) {
|
||||
if( type.value[i].match(filename) ) {
|
||||
fileType = type.key;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
// debugger
|
||||
|
||||
/* attempt to futher detect the fileType */
|
||||
if( !fileType ) {
|
||||
text = text.replace(/\r\n/, "\n").split(/\n/)[0];
|
||||
fileType = /ruby/i.test(text) ? 'ruby' :
|
||||
/python/i.test(text) ? 'python' :
|
||||
/php/i.test(text) ? 'php' :
|
||||
'';
|
||||
}
|
||||
|
||||
return fileType;
|
||||
}
|
||||
|
||||
var old = f.previewTextFile;
|
||||
f.previewTextFile = function( text, item ) {
|
||||
old(text,item);
|
||||
var codeEl = $('code');
|
||||
codeEl.className = f.theme; // clear previous syntax class
|
||||
codeEl.addClassName(getFiletype(item.name,text));
|
||||
|
||||
hlt.init();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* add the link to the stylesheet */
|
||||
// ,addStylesheet: function() {
|
||||
// // <link href="css/code_highlighter.css" media="all" rel="stylesheet" type="text/css" />
|
||||
// var css = document.createElement('link');
|
||||
// css.href = 'css/code_highlighter.css';
|
||||
// css.rel = 'stylesheet';
|
||||
// css.type = 'text/css';
|
||||
// document.body.appendChild(css);
|
||||
// }
|
||||
|
||||
});
|
||||
|
||||
FP.push(CH);
|
||||
58
GithubFinder/javascripts/util.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* make sure we have console */
|
||||
/*
|
||||
;if( typeof(console) == 'undefined' )
|
||||
console = { log: function(){} };
|
||||
*/
|
||||
/* placeholder */
|
||||
// var pH = function() {
|
||||
// if( 'placeholder' in (document.createElement('input') ) )
|
||||
// return;
|
||||
//
|
||||
// $$('input[placeholder]').each(function(i) {
|
||||
// var t = i.readAttribute('placeholder');
|
||||
// var f = function() {
|
||||
// i.value.strip() == t ? i.value = '' : '';
|
||||
// };
|
||||
// i.on('focus', f);
|
||||
//
|
||||
// var b = function() {
|
||||
// i.value.strip() == '' ? i.value = t : '';
|
||||
// };
|
||||
// i.on('blur', b);
|
||||
// b();
|
||||
// });
|
||||
// };
|
||||
|
||||
|
||||
|
||||
|
||||
/* Util stuff */
|
||||
|
||||
/* return a truncated sha */
|
||||
var s = function(h) { return h.substr(0,6); };
|
||||
|
||||
/* truncate text to a minimum length, similar to the way Finder does it */
|
||||
var t = function(s,l) {
|
||||
var sl = s.length,
|
||||
d = '...', // delimiter
|
||||
dl = d.length,
|
||||
r = (l - dl) / 2; // radius
|
||||
return s.length < l ? s : s.substr(0, r) + d + s.substr( sl - r, sl);
|
||||
}
|
||||
|
||||
/* parse URL Params as a hash with key are lowered case. (Doesn't handle duplicated key). */
|
||||
var uP = function() {
|
||||
var ps = [], pair, pairs,
|
||||
url = window.location.href.split('?');
|
||||
|
||||
if( url.length == 1 ) return ps;
|
||||
|
||||
url = url[1].split('#')[0];
|
||||
|
||||
pairs = url.split('&');
|
||||
for( var i = 0; i < pairs.length; i++ ) {
|
||||
pair = pairs[i].split('=');
|
||||
ps[ pair[0].toLowerCase() ] = pair[1];
|
||||
}
|
||||
return ps;
|
||||
};
|
||||
394
GithubFinder/javascripts/vendors/code_highlighter.js
vendored
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
/* Unobtrustive Code Highlighter By Dan Webb 11/2005
|
||||
Version: 0.4
|
||||
|
||||
Usage:
|
||||
Add a script tag for this script and any stylesets you need to use
|
||||
to the page in question, add correct class names to CODE elements,
|
||||
define CSS styles for elements. That's it!
|
||||
|
||||
Known to work on:
|
||||
IE 5.5+ PC
|
||||
Firefox/Mozilla PC/Mac
|
||||
Opera 7.23 + PC
|
||||
Safari 2
|
||||
|
||||
Known to degrade gracefully on:
|
||||
IE5.0 PC
|
||||
|
||||
Note: IE5.0 fails due to the use of lookahead in some stylesets. To avoid script errors
|
||||
in older browsers use expressions that use lookahead in string format when defining stylesets.
|
||||
|
||||
This script is inspired by star-light by entirely cunning Dean Edwards
|
||||
http://dean.edwards.name/star-light/.
|
||||
*/
|
||||
|
||||
// replace callback support for safari.
|
||||
if ("a".replace(/a/, function() {return "b"}) != "b") (function(){
|
||||
var default_replace = String.prototype.replace;
|
||||
String.prototype.replace = function(search,replace){
|
||||
// replace is not function
|
||||
if(typeof replace != "function"){
|
||||
return default_replace.apply(this,arguments)
|
||||
}
|
||||
var str = "" + this;
|
||||
var callback = replace;
|
||||
// search string is not RegExp
|
||||
if(!(search instanceof RegExp)){
|
||||
var idx = str.indexOf(search);
|
||||
return (
|
||||
idx == -1 ? str :
|
||||
default_replace.apply(str,[search,callback(search, idx, str)])
|
||||
)
|
||||
}
|
||||
var reg = search;
|
||||
var result = [];
|
||||
var lastidx = reg.lastIndex;
|
||||
var re;
|
||||
while((re = reg.exec(str)) != null){
|
||||
var idx = re.index;
|
||||
var args = re.concat(idx, str);
|
||||
result.push(
|
||||
str.slice(lastidx,idx),
|
||||
callback.apply(null,args).toString()
|
||||
);
|
||||
if(!reg.global){
|
||||
lastidx += RegExp.lastMatch.length;
|
||||
break
|
||||
}else{
|
||||
lastidx = reg.lastIndex;
|
||||
}
|
||||
}
|
||||
result.push(str.slice(lastidx));
|
||||
return result.join("")
|
||||
}
|
||||
})();
|
||||
|
||||
var CodeHighlighter = { styleSets : new Array };
|
||||
|
||||
CodeHighlighter.addStyle = function(name, rules) {
|
||||
// using push test to disallow older browsers from adding styleSets
|
||||
if ([].push) this.styleSets.push({
|
||||
name : name,
|
||||
rules : rules,
|
||||
ignoreCase : arguments[2] || false
|
||||
})
|
||||
|
||||
// function setEvent() {
|
||||
// // set highlighter to run on load (use LowPro if present)
|
||||
// if (typeof Event != 'undefined' && typeof Event.onReady == 'function')
|
||||
// return Event.onReady(CodeHighlighter.init.bind(CodeHighlighter));
|
||||
//
|
||||
// var old = window.onload;
|
||||
//
|
||||
// if (typeof window.onload != 'function') {
|
||||
// window.onload = function() { CodeHighlighter.init() };
|
||||
// } else {
|
||||
// window.onload = function() {
|
||||
// old();
|
||||
// CodeHighlighter.init();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // only set the event when the first style is added
|
||||
// if (this.styleSets.length==1) setEvent();
|
||||
}
|
||||
|
||||
CodeHighlighter.init = function() {
|
||||
if (!document.getElementsByTagName) return;
|
||||
if ("a".replace(/a/, function() {return "b"}) != "b") return; // throw out Safari versions that don't support replace function
|
||||
// throw out older browsers
|
||||
|
||||
// var codeEls = document.getElementsByTagName("CODE");
|
||||
|
||||
// HACK:
|
||||
var codeEls = [$('code')];
|
||||
|
||||
// collect array of all pre elements
|
||||
codeEls.filter = function(f) {
|
||||
var a = new Array;
|
||||
for (var i = 0; i < this.length; i++)
|
||||
if (f(this[i]))
|
||||
a[a.length] = this[i];
|
||||
return a;
|
||||
}
|
||||
|
||||
var rules = new Array;
|
||||
rules.toString = function() {
|
||||
// joins regexes into one big parallel regex
|
||||
var exps = new Array;
|
||||
for (var i = 0; i < this.length; i++) exps.push(this[i].exp);
|
||||
return exps.join("|");
|
||||
}
|
||||
|
||||
function addRule(className, rule) {
|
||||
// add a replace rule
|
||||
var exp = (typeof rule.exp != "string")?String(rule.exp).substr(1, String(rule.exp).length-2):rule.exp;
|
||||
// converts regex rules to strings and chops of the slashes
|
||||
rules.push({
|
||||
className : className,
|
||||
exp : "(" + exp + ")",
|
||||
length : (exp.match(/(^|[^\\])\([^?]/g) || "").length + 1, // number of subexps in rule
|
||||
replacement : rule.replacement || null
|
||||
});
|
||||
}
|
||||
|
||||
function parse(text, ignoreCase) {
|
||||
// main text parsing and replacement
|
||||
return text.replace(new RegExp(rules, (ignoreCase)?"gi":"g"), function() {
|
||||
var i = 0, j = 1, rule;
|
||||
while (rule = rules[i++]) {
|
||||
if (arguments[j]) {
|
||||
// if no custom replacement defined do the simple replacement
|
||||
if (!rule.replacement) return "<span class=\"" + rule.className + "\">" + arguments[0] + "</span>";
|
||||
else {
|
||||
// replace $0 with the className then do normal replaces
|
||||
var str = rule.replacement.replace("$0", rule.className);
|
||||
for (var k = 1; k <= rule.length - 1; k++) str = str.replace("$" + k, arguments[j + k]);
|
||||
return str;
|
||||
}
|
||||
} else j+= rule.length;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function highlightCode(styleSet) {
|
||||
// clear rules array
|
||||
var parsed, clsRx = new RegExp("(\\s|^)" + styleSet.name + "(\\s|$)");
|
||||
rules.length = 0;
|
||||
|
||||
// // get stylable elements by filtering out all code elements without the correct className
|
||||
var stylableEls = codeEls.filter(function(item) { return clsRx.test(item.className) });
|
||||
// var stylableEls = codeEls;
|
||||
|
||||
// add style rules to parser
|
||||
for (var className in styleSet.rules) addRule(className, styleSet.rules[className]);
|
||||
|
||||
|
||||
// replace for all elements
|
||||
for (var i = 0; i < stylableEls.length; i++) {
|
||||
// EVIL hack to fix IE whitespace badness if it's inside a <pre>
|
||||
if (/MSIE/.test(navigator.appVersion) && stylableEls[i].parentNode.nodeName == 'PRE') {
|
||||
stylableEls[i] = stylableEls[i].parentNode;
|
||||
|
||||
parsed = stylableEls[i].innerHTML.replace(/(<code[^>]*>)([^<]*)<\/code>/i, function() {
|
||||
return arguments[1] + parse(arguments[2], styleSet.ignoreCase) + "</code>"
|
||||
});
|
||||
parsed = parsed.replace(/\n( *)/g, function() {
|
||||
var spaces = "";
|
||||
for (var i = 0; i < arguments[1].length; i++) spaces+= " ";
|
||||
return "\n" + spaces;
|
||||
});
|
||||
parsed = parsed.replace(/\t/g, " ");
|
||||
parsed = parsed.replace(/\n(<\/\w+>)?/g, "<br />$1").replace(/<br \/>[\n\r\s]*<br \/>/g, "<p><br></p>");
|
||||
|
||||
} else parsed = parse(stylableEls[i].innerHTML, styleSet.ignoreCase);
|
||||
|
||||
stylableEls[i].innerHTML = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
// run highlighter on all stylesets
|
||||
for (var i=0; i < this.styleSets.length; i++) {
|
||||
highlightCode(this.styleSets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
CodeHighlighter.addStyle("css", {
|
||||
comment : {
|
||||
exp : /\/\*[^*]*\*+([^\/][^*]*\*+)*\//
|
||||
},
|
||||
keywords : {
|
||||
exp : /@\w[\w\s]*/
|
||||
},
|
||||
selectors : {
|
||||
exp : "([\\w-:\\[.#][^{};>]*)(?={)"
|
||||
},
|
||||
properties : {
|
||||
exp : "([\\w-]+)(?=\\s*:)"
|
||||
},
|
||||
units : {
|
||||
exp : /([0-9])(em|en|px|%|pt)\b/,
|
||||
replacement : "$1<span class=\"$0\">$2</span>"
|
||||
},
|
||||
urls : {
|
||||
exp : /url\([^\)]*\)/
|
||||
}
|
||||
});
|
||||
|
||||
CodeHighlighter.addStyle("ruby",{
|
||||
comment : {
|
||||
exp : /#[^\n]*/
|
||||
},
|
||||
brackets : {
|
||||
exp : /\(|\)/
|
||||
},
|
||||
string : {
|
||||
exp : /'[^'\\]*(\\.[^'\\]*)*'|"[^"\\]*(\\.[^"\\]*)*"|\%w\(.*\)|`[^`\\]*(\\.[^`\\]*)*`/
|
||||
},
|
||||
keywords : {
|
||||
exp : /\b(do|end|self|class|def|if|module|yield|then|else|for|until|unless|while|elsif|case|when|break|retry|redo|rescue|require|raise|extend)\b/
|
||||
},
|
||||
/* Added by Shelly Fisher (shelly@agileevolved.com) */
|
||||
symbol : {
|
||||
exp : /([^:])(:[A-Za-z0-9_!?]+)/
|
||||
}
|
||||
});
|
||||
|
||||
CodeHighlighter.addStyle("html", {
|
||||
comment : {
|
||||
exp: /<!\s*(--([^-]|[\r\n]|-[^-])*--\s*)>/
|
||||
},
|
||||
tag : {
|
||||
exp: /(<\/?)([a-zA-Z1-9]+\s?)/,
|
||||
replacement: "$1<span class=\"$0\">$2"
|
||||
},
|
||||
string : {
|
||||
exp : /'[^']*'|"[^"]*"/
|
||||
},
|
||||
attribute : {
|
||||
exp: /\b([a-zA-Z-:]+)(=)/,
|
||||
replacement: "<span class=\"$0\">$1$2"
|
||||
},
|
||||
doctype : {
|
||||
exp: /<!DOCTYPE([^&]|&[^g]|&g[^t])*>/
|
||||
}
|
||||
});
|
||||
|
||||
CodeHighlighter.addStyle("javascript",{
|
||||
comment : {
|
||||
exp : /(\/\/[^\n]*(\n|$))|(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)/
|
||||
},
|
||||
parameter: {
|
||||
exp: /\bfunction\s?\((.+)\)/
|
||||
// ,replacement: "<span class='parameter'>$1</span>"
|
||||
},
|
||||
|
||||
brackets : {
|
||||
exp : /\(|\)/
|
||||
},
|
||||
string : {
|
||||
exp : /'[^']*'|"[^"]*"/
|
||||
},
|
||||
keywords : {
|
||||
exp : /\b(arguments|break|case|continue|default|delete|do|else|false|for|function|if|in|instanceof|new|null|return|switch|this|true|typeof|var|void|while|with)\b/
|
||||
},
|
||||
global : {
|
||||
exp : /\b(toString|valueOf|window|element|prototype|constructor|document|escape|unescape|parseInt|parseFloat|setTimeout|clearTimeout|setInterval|clearInterval|NaN|isNaN|Infinity|String|Numeric|Array)\b/
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
CodeHighlighter.addStyle("yaml", {
|
||||
keyword : {
|
||||
exp : /\/\*[^*]*\*+([^\/][^*]*\*+)*\//
|
||||
},
|
||||
value : {
|
||||
exp : /@\w[\w\s]*/
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
CodeHighlighter.addStyle("python",{
|
||||
comment : {
|
||||
exp : /#[^\n]+/
|
||||
},
|
||||
brackets : {
|
||||
exp : /\(|\)/
|
||||
},
|
||||
string : {
|
||||
exp : /'[^'\\]*(\\.[^'\\]*)*'|"[^"\\]*(\\.[^"\\]*)*"|""".*"""/
|
||||
},
|
||||
keywords : {
|
||||
exp : /\b(and|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|yield|as|None)\b/
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
CodeHighlighter.addStyle("applescript",{
|
||||
comment : {
|
||||
exp : /--[^\n]+|#![^\n]+/
|
||||
},
|
||||
brackets : {
|
||||
exp : /\(|\)/
|
||||
},
|
||||
string : {
|
||||
exp : /"[^"\\]*(\\.[^"\\]*)*"/
|
||||
},
|
||||
keywords : {
|
||||
exp : /\b(about|above|after|against|and|apart[\s]+from|around|as|aside[\s]+from|at|back|before|beginning|behind|below|beneath|beside|between|but|by|considering|contain|contains|contains|continue|copy|div|does|eighth|else|end|equal|equals|error|every|exit|false|fifth|first|for|fourth|from|front|get|given|global|if|ignoring|in|instead[\s]+of|into|is|it|its|last|local|me|middle|mod|my|ninth|not|of|on|onto|or|out[\s]+of|over|prop|property|put|ref|reference|repeat|return|returning|script|second|set|seventh|since|sixth|some|tell|tenth|that|the|then|third|through|thru|timeout|times|to|transaction|true|try|until|where|while|whose|with|without)\b/
|
||||
},
|
||||
global : {
|
||||
exp : /\b(AppleScript('s)?|current[\s]+application|missing[\s]+value|false|pi|true|version)\b/
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
CodeHighlighter.addStyle("cpp",{
|
||||
comment : {
|
||||
exp : /(\/\/[^\n]*(\n|$))|(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)/
|
||||
},
|
||||
brackets : {
|
||||
exp : /\(|\)/
|
||||
},
|
||||
string : {
|
||||
exp : /"[^"\\]*(\\.[^"\\]*)*"/
|
||||
},
|
||||
keywords : {
|
||||
exp : /\b(break|case|catch|class|const|__finally|__exception|__try|const_cast|continue|private|public|protected|__declspec|default|delete|deprecated|dllexport|dllimport|do|dynamic_cast|else|enum|explicit|extern|if|for|friend|goto|inline|mutable|naked|namespace|new|noinline|noreturn|nothrow|register|reinterpret_cast|return|selectany|sizeof|static|static_cast|struct|switch|template|this|thread|throw|true|false|try|typedef|typeid|typename|union|using|uuid|virtual|void|volatile|whcar_t|while)\b/
|
||||
},
|
||||
|
||||
datatype: {
|
||||
exp : /\b(ATOM|BOOL|BOOLEAN|BYTE|CHAR|COLORREF|DWORD|DWORDLONG|DWORD_PTR|DWORD32|DWORD64|FLOAT|HACCEL|HALF_PTR|HANDLE|HBITMAP|HBRUSH|HCOLORSPACE|HCONV|HCONVLIST|HCURSOR|HDC|HDDEDATA|HDESK|HDROP|HDWP|HENHMETAFILE|HFILE|HFONT|HGDIOBJ|HGLOBAL|HHOOK|HICON|HINSTANCE|HKEY|HKL|HLOCAL|HMENU|HMETAFILE|HMODULE|HMONITOR|HPALETTE|HPEN|HRESULT|HRGN|HRSRC|HSZ|HWINSTA|HWND|INT|INT_PTR|INT32|INT64|LANGID|LCID|LCTYPE|LGRPID|LONG|LONGLONG|LONG_PTR|LONG32|LONG64|LPARAM|LPBOOL|LPBYTE|LPCOLORREF|LPCSTR|LPCTSTR|LPCVOID|LPCWSTR|LPDWORD|LPHANDLE|LPINT|LPLONG|LPSTR|LPTSTR|LPVOID|LPWORD|LPWSTR|LRESULT|PBOOL|PBOOLEAN|PBYTE|PCHAR|PCSTR|PCTSTR|PCWSTR|PDWORDLONG|PDWORD_PTR|PDWORD32|PDWORD64|PFLOAT|PHALF_PTR|PHANDLE|PHKEY|PINT|PINT_PTR|PINT32|PINT64|PLCID|PLONG|PLONGLONG|PLONG_PTR|PLONG32|PLONG64|POINTER_32|POINTER_64|PSHORT|PSIZE_T|PSSIZE_T|PSTR|PTBYTE|PTCHAR|PTSTR|PUCHAR|PUHALF_PTR|PUINT|PUINT_PTR|PUINT32|PUINT64|PULONG|PULONGLONG|PULONG_PTR|PULONG32|PULONG64|PUSHORT|PVOID|PWCHAR|PWORD|PWSTR|SC_HANDLE|SC_LOCK|SERVICE_STATUS_HANDLE|SHORT|SIZE_T|SSIZE_T|TBYTE|TCHAR|UCHAR|UHALF_PTR|UINT|UINT_PTR|UINT32|UINT64|ULONG|ULONGLONG|ULONG_PTR|ULONG32|ULONG64|USHORT|USN|VOID|WCHAR|WORD|WPARAM|WPARAM|WPARAM|char|bool|short|int|__int32|__int64|__int8|__int16|long|float|double|__wchar_t|clock_t|_complex|_dev_t|_diskfree_t|div_t|ldiv_t|_exception|_EXCEPTION_POINTERS|FILE|_finddata_t|_finddatai64_t|_wfinddata_t|_wfinddatai64_t|__finddata64_t|__wfinddata64_t|_FPIEEE_RECORD|fpos_t|_HEAPINFO|_HFILE|lconv|intptr_t|jmp_buf|mbstate_t|_off_t|_onexit_t|_PNH|ptrdiff_t|_purecall_handler|sig_atomic_t|size_t|_stat|__stat64|_stati64|terminate_function|time_t|__time64_t|_timeb|__timeb64|tm|uintptr_t|_utimbuf|va_list|wchar_t|wctrans_t|wctype_t|wint_t|signed)\b/
|
||||
},
|
||||
|
||||
preprocessor : {
|
||||
exp : /^ *#.*/gm
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// http://www.undermyhat.org/blog/wp-content/uploads/2009/09/shBrushClojure.js
|
||||
CodeHighlighter.addStyle("clojure",{
|
||||
comment : {
|
||||
exp : /;[^\]]+$/
|
||||
},
|
||||
string : {
|
||||
exp : /"[^"\\]*(\\.[^"\\]*)*"/m
|
||||
},
|
||||
functions : {
|
||||
exp : /\b(:arglists|:doc|:file|:line|:macro|:name|:ns|:private|:tag|:test|new|alias|alter|and|apply|assert|class|cond|conj|count|def|defmacro|defn|defstruct|deref|do|doall|dorun|doseq|dosync|eval|filter|finally|find|first|fn|gen-class|gensym|if|import|inc|keys|let|list|loop|map|ns|or|print|println|quote|rand|recur|reduce|ref|repeat|require|rest|send|seq|set|sort|str|struct|sync|take|test|throw|trampoline|try|type|use|var|vec|when|while)\b/gmi
|
||||
},
|
||||
|
||||
keyword : {
|
||||
exp : /\[|\]/g
|
||||
},
|
||||
|
||||
symbols : {
|
||||
exp : /'[a-z][A-Za-z0-9_]*/g
|
||||
},
|
||||
|
||||
keywords: {
|
||||
exp: /(:[a-z][A-Za-z0-9_]*)/g
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
CodeHighlighter.addStyle("haskell",{
|
||||
comment : {
|
||||
exp : /(\-\-.*$)|(\{\-[\s\S]*?\-\})/gm
|
||||
},
|
||||
keywords : {
|
||||
exp : /\b(as|case|of|class|data|datafamily|data instance|default|deriving|deriving instance|do|forall|foreign|hiding|if|then|else|import|infix|infixl|infixr|instance|let|in|mdo|module|newtype|proc|qualified|rec|type|type family|type instance|where)\b/
|
||||
},
|
||||
string : {
|
||||
exp : /'[^']*'|"[^"]*"/
|
||||
}
|
||||
});
|
||||
113
GithubFinder/javascripts/vendors/jsonp.js
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
// See an updated version of this in it's own git repo:
|
||||
// http://github.com/dandean/Ajax.JSONRequest
|
||||
|
||||
/* JSON-P implementation for Prototype.js somewhat by Dan Dean (http://www.dandean.com)
|
||||
*
|
||||
* *HEAVILY* based on Tobie Langel's version: http://gist.github.com/145466.
|
||||
* Might as well just call this an iteration.
|
||||
*
|
||||
* This version introduces:
|
||||
* - onCreate and onFailure callback options.
|
||||
* - option to not invoke request upon instantiation.
|
||||
*
|
||||
* Tested in Firefox 3/3.5, Safari 4
|
||||
*
|
||||
* Note: while I still think JSON-P is an inherantly flawed technique,
|
||||
* there are some valid use cases which this can provide for.
|
||||
*
|
||||
* See examples below for usage.
|
||||
*/
|
||||
window.JSP = Class.create(Ajax.Base, (function() {
|
||||
var id = 0, head = document.getElementsByTagName('head')[0];
|
||||
return {
|
||||
initialize: function($super, url, options) {
|
||||
$super(options);
|
||||
this.options.url = url;
|
||||
this.options.callbackParamName = this.options.callbackParamName || 'callback';
|
||||
this.options.timeout = this.options.timeout || 10000; // Default timeout: 10 seconds
|
||||
this.options.invokeImmediately = (!Object.isUndefined(this.options.invokeImmediately)) ? this.options.invokeImmediately : true ;
|
||||
this.responseJSON = {};
|
||||
if (this.options.invokeImmediately) {
|
||||
this.request();
|
||||
}
|
||||
|
||||
Ajax.Responders.dispatch('onCreate', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.JSONRequest#_cleanup() -> "undefined"
|
||||
*
|
||||
* Cleans up after the request
|
||||
**/
|
||||
_cleanup: function() {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
if (this.script && Object.isElement(this.script)) {
|
||||
this.script.remove();
|
||||
this.script = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.JSONRequest#request() -> "undefined"
|
||||
*
|
||||
* Invokes the JSON-P request lifecycle
|
||||
**/
|
||||
request: function() {
|
||||
// Define local vars
|
||||
var key = this.options.callbackParamName,
|
||||
name = '_prototypeJSONPCallback_' + (id++);
|
||||
|
||||
// Add callback as a parameter and build request URL
|
||||
this.options.parameters[key] = name;
|
||||
var url = this.options.url + ((this.options.url.include('?') ? '&' : '?') + Object.toQueryString(this.options.parameters));
|
||||
|
||||
// Define callback function
|
||||
window[name] = function(response) {
|
||||
this._cleanup(); // Garbage collection
|
||||
window[name] = undefined;
|
||||
|
||||
|
||||
if( typeof(response) == 'Object' )
|
||||
this.responseJSON = response;
|
||||
else
|
||||
this.responseText = response;
|
||||
|
||||
try {
|
||||
Ajax.Responders.dispatch('onComplete', this, response);
|
||||
|
||||
if (Object.isFunction(this.options.onComplete)) {
|
||||
this.options.onComplete.call(this, this);
|
||||
}
|
||||
|
||||
if (Object.isFunction(this.options.onSuccess)) {
|
||||
this.options.onSuccess.call(this,this);
|
||||
}
|
||||
} catch( ex ) {
|
||||
Ajax.Responders.dispatch('onException', this, ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
}.bind(this);
|
||||
|
||||
this.script = new Element('script', { type: 'text/javascript', src: url });
|
||||
|
||||
if (Object.isFunction(this.options.onCreate)) {
|
||||
this.options.onCreate.call(this, this);
|
||||
}
|
||||
|
||||
|
||||
head.appendChild(this.script);
|
||||
|
||||
this.timeout = setTimeout(function() {
|
||||
this._cleanup();
|
||||
window[name] = Prototype.emptyFunction;
|
||||
if (Object.isFunction(this.options.onFailure)) {
|
||||
this.options.onFailure.call(this, this);
|
||||
}
|
||||
}.bind(this), this.options.timeout);
|
||||
}
|
||||
};
|
||||
})());
|
||||
60
GithubFinder/proxy.php
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
/* http://www.itbsllc.com/zip/proxyscripts.html */
|
||||
// PHP Proxy
|
||||
// Responds to both HTTP GET and POST requests
|
||||
//
|
||||
// Author: Abdul Qabiz
|
||||
// March 31st, 2006
|
||||
//
|
||||
|
||||
// Get the url of to be proxied
|
||||
// Is it a POST or a GET?
|
||||
$url = ($_POST['url']) ? $_POST['url'] : $_GET['url'];
|
||||
$jsoncallback = ($_POST['callback']) ? $_POST['callback'] : $_GET['callback'];
|
||||
|
||||
$allowed_domain = 'http://github.com';
|
||||
// echo strpos( $url, $allowed_domain );
|
||||
if( strpos( $url, $allowed_domain ) === false )
|
||||
die();
|
||||
|
||||
$headers = ($_POST['headers']) ? $_POST['headers'] : $_GET['headers'];
|
||||
$mimeType =($_POST['mimeType']) ? $_POST['mimeType'] : $_GET['mimeType'];
|
||||
|
||||
//Start the Curl session
|
||||
$session = curl_init($url);
|
||||
|
||||
// If it's a POST, put the POST data in the body
|
||||
if ($_POST['url']) {
|
||||
$postvars = '';
|
||||
while ($element = current($_POST)) {
|
||||
$postvars .= key($_POST).'='.$element.'&';
|
||||
next($_POST);
|
||||
}
|
||||
curl_setopt ($session, CURLOPT_POST, true);
|
||||
curl_setopt ($session, CURLOPT_POSTFIELDS, $postvars);
|
||||
}
|
||||
|
||||
// Don't return HTTP headers. Do return the contents of the call
|
||||
curl_setopt($session, CURLOPT_HEADER, ($headers == "true") ? true : false);
|
||||
|
||||
curl_setopt($session, CURLOPT_FOLLOWLOCATION, true);
|
||||
//curl_setopt($ch, CURLOPT_TIMEOUT, 4);
|
||||
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
// Make the call
|
||||
$response = curl_exec($session);
|
||||
|
||||
if ($mimeType != "")
|
||||
{
|
||||
// The web service returns XML. Set the Content-Type appropriately
|
||||
header("Content-Type: ".$mimeType);
|
||||
}
|
||||
|
||||
if( $jsoncallback )
|
||||
echo $jsoncallback . "(" . json_encode($response) . ")";
|
||||
else
|
||||
echo $response;
|
||||
|
||||
curl_close($session);
|
||||
|
||||
?>
|
||||
70
GithubFinder/styles.css
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#gh-finder { font-size: 12px; font-family: Helvetica, Verdana, sans-serif; margin: 3em auto 1em; padding: 0; width: 92% }
|
||||
#gh-finder .clear { clear:both }
|
||||
#gh-finder a { color: #22a; text-decoration: none; border-bottom: none }
|
||||
#gh-finder a:active { outline: none }
|
||||
#gh-finder :focus { -moz-outline-style: none; outline: none;}
|
||||
#gh-finder .p { padding: 10px; }
|
||||
#gh-finder img { border: 0px}
|
||||
|
||||
#finder { width: 100%; overflow: hidden; height: 343px }
|
||||
|
||||
#r_w { width: 100%; background-color: #EAEAEA; border-bottom: 1px solid #CCC }
|
||||
.tbb { border-bottom: 3px solid #CCC;} /* thick bottom border */
|
||||
|
||||
|
||||
/* logo */
|
||||
.big { font-size: 14px; font-weight: bold;}
|
||||
#r_w { -moz-border-radius-topright: 7px; -webkit-border-top-right-radius: 7px; border-top-right-radius: 7px;
|
||||
-moz-border-radius-topleft: 7px; -webkit-border-top-left-radius: 7px; border-top-left-radius: 7px
|
||||
}
|
||||
#in { color: transparent; padding: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px }
|
||||
#in.on { background-color: #4F4A45; color: white }
|
||||
|
||||
#b_w { overflow: hidden; overflow-x: auto; height: 300px }
|
||||
.panel { float: left; width: 200px; border-right: 1px solid #CCC; overflow: hidden; overflow-y: auto;
|
||||
height: 300px }
|
||||
|
||||
.files { padding: 0; margin: 0; font-size: 1.3em }
|
||||
.files li { list-style:none outside none; border-bottom: 1px solid #CCC; background-color: #F8F8F8; }
|
||||
|
||||
.t { padding: 10px; border-bottom: 1px solid #CCC; font-size: 14px; font-weight: bold} /* title */
|
||||
|
||||
.tree { background: #F8F8F8 url('./img/la.gif') no-repeat 95% 50%; font-weight: bold; }
|
||||
.ico { background:url('./img/txt.gif') no-repeat 3px 50%; display: block;}
|
||||
.tree .ico { background-image: url('./img/dir.gif'); }
|
||||
.cur { background: #3D8CFF url('./img/la_h.gif') no-repeat 95% 50% !important;}
|
||||
#gh-finder .cur a { color: white;}
|
||||
.blob { background-image: none !important; }
|
||||
.files a { padding: 8px 8px 8px 23px; display:block; }
|
||||
|
||||
#f_c_w .p {padding: 10px}
|
||||
#f_c { overflow: hidden; }
|
||||
#f_c_s { overflow-y: auto; }
|
||||
#f { border: 1px solid #CCC; }
|
||||
|
||||
#commits .big { padding: 10px; border-bottom: 1px solid #CCC;}
|
||||
|
||||
/* commit entry */
|
||||
.ce { padding: 10px 0; border-bottom: 1px solid #CCC; }
|
||||
|
||||
pre, #code, .meta, .diff { font:12px Monaco,monospace; margin: 0; }
|
||||
|
||||
.meta, .diff thead th { background-color: #EEE; color:#333; overflow:hidden; padding:10px; border-bottom: 1px solid #CCC}
|
||||
.meta span { margin-left: 5px; padding-left: 9px;}
|
||||
.meta span:first-child { display: none }
|
||||
|
||||
.ln, .diff th { background-color:#ECECEC; border-right:1px solid #DDD; color:#AAA; text-align: right; padding: 5px 5px }
|
||||
#code { padding: 5px;}
|
||||
/*.hover { background-color: #FFFFC7; }*/
|
||||
|
||||
.insert { background-color:#9E9 }
|
||||
|
||||
#footer { padding: 10px }
|
||||
|
||||
|
||||
/* extension stuff */
|
||||
.resize { cursor: col-resize; float: left; background-color: #DCDCDC;border-right: 1px solid #CCC;}
|
||||
.hrz { cursor: row-resize !important; background-color: #DCDCDC; ;border-bottom: 1px solid #CCC;height: 3px; line-height: 3px; width: 99.9%}
|
||||
#bml_w { float: right; font-size: 11px; width: 300px;}
|
||||
.bml { border: 1px solid #CCC; padding: 1px; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; font-weight: bold;}
|
||||
.bml:hover { background-color: white;}
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
#browse { text-align: center
|
||||
; font-size: 1.4em
|
||||
; margin: 1.6em
|
||||
}
|
||||
|
||||
#info { text-align: center
|
||||
; margin: 1em auto
|
||||
; padding: 1em
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
var global = this
|
||||
global.SJS = {
|
||||
proj: function(name) {
|
||||
SJS.projName = name
|
||||
var data = createObjectStore(name)
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener('DOMContentLoaded', ready, false)
|
||||
|
|
|
|||
|
|
@ -37,38 +37,38 @@ a.img { border: none }
|
|||
; text-decoration: underline
|
||||
}
|
||||
|
||||
ul { text-align: center
|
||||
; margin: 0 auto
|
||||
; padding: 1em
|
||||
; border: solid 1px #ccc
|
||||
; width: 90%
|
||||
; max-width: 950px
|
||||
; background-color: #fff
|
||||
; -moz-border-radius: 20px
|
||||
; -webkit-border-radius: 20px
|
||||
; border-radius: 20px
|
||||
; behavior: url(assets/border-radius.htc)
|
||||
; behavior: url(../assets/border-radius.htc)
|
||||
}
|
||||
ul.nav { text-align: center
|
||||
; margin: 0 auto
|
||||
; padding: 1em
|
||||
; border: solid 1px #ccc
|
||||
; width: 90%
|
||||
; max-width: 950px
|
||||
; background-color: #fff
|
||||
; -moz-border-radius: 20px
|
||||
; -webkit-border-radius: 20px
|
||||
; border-radius: 20px
|
||||
; behavior: url(assets/border-radius.htc)
|
||||
; behavior: url(../assets/border-radius.htc)
|
||||
}
|
||||
|
||||
li { display: inline
|
||||
; font-size: 1.6em
|
||||
; margin: 0
|
||||
; padding: 0
|
||||
}
|
||||
ul.nav li { display: inline
|
||||
; font-size: 1.6em
|
||||
; margin: 0
|
||||
; padding: 0
|
||||
}
|
||||
|
||||
li:after { content: ' •' }
|
||||
li:last-child:after { content: '' }
|
||||
ul.nav li:after { content: ' •' }
|
||||
ul.nav li:last-child:after { content: '' }
|
||||
|
||||
|
||||
li a { padding: 5px
|
||||
; text-shadow: #999 5px 5px 5px
|
||||
; -webkit-transition: all 0.5s ease-out
|
||||
}
|
||||
li a:visited { color: #227 }
|
||||
ul.nav li a { padding: 5px
|
||||
; text-shadow: #999 5px 5px 5px
|
||||
; -webkit-transition: all 0.5s ease-out
|
||||
}
|
||||
ul.nav li a:visited { color: #227 }
|
||||
|
||||
li a:hover,
|
||||
li a:active { color: #000
|
||||
ul.nav li a:hover,
|
||||
ul.nav li a:active { color: #000
|
||||
; text-shadow: #aa7 5px 5px 5px
|
||||
; border-bottom: dashed 1px #000
|
||||
}
|
||||
|
|
@ -81,10 +81,10 @@ ul#projects li { display: block
|
|||
; line-height: 1.8em
|
||||
}
|
||||
|
||||
li.ie-bullet,
|
||||
ul.nav li.ie-bullet,
|
||||
ul#projects li.ie-bullet { display: none }
|
||||
|
||||
li.last:after,
|
||||
ul.nav li.last:after,
|
||||
ul#projects li:after { content: '' }
|
||||
|
||||
footer { text-align: center
|
||||
|
|
@ -124,17 +124,17 @@ td:nth-child(2) { padding: 0 10px }
|
|||
@media only screen and (orientation:portrait) and (device-height:1024px),
|
||||
only screen and (max-device-width:480px)
|
||||
{
|
||||
ul { padding: 0.5em
|
||||
; width: 70%
|
||||
; max-width: 400px
|
||||
}
|
||||
ul.nav ul { padding: 0.5em
|
||||
; width: 70%
|
||||
; max-width: 400px
|
||||
}
|
||||
|
||||
li { display: block
|
||||
; font-size: 1.5em
|
||||
; line-height: 1.6em
|
||||
}
|
||||
ul.nav li { display: block
|
||||
; font-size: 1.5em
|
||||
; line-height: 1.6em
|
||||
}
|
||||
|
||||
li:after { content: '' }
|
||||
ul.nav li:after { content: '' }
|
||||
}
|
||||
|
||||
/* iPhone */
|
||||
|
|
@ -147,7 +147,7 @@ td:nth-child(2) { padding: 0 10px }
|
|||
|
||||
#breadcrumbs { font-size: 0.8em }
|
||||
|
||||
li { font-size: 1.2em }
|
||||
ul.nav li { font-size: 1.2em }
|
||||
|
||||
td { font-size: 1em
|
||||
; line-height: 1.1em
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
<h1>samhuri.net</h1>
|
||||
</header>
|
||||
<nav>
|
||||
<ul>
|
||||
<ul class=nav>
|
||||
<li><a href=proj>projects</a></li>
|
||||
<li class=ie-bullet>•</li>
|
||||
<li><a href=blog>blog</a></li>
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@
|
|||
<header>
|
||||
<h1>projects</h1>
|
||||
</header>
|
||||
<ul id=projects>
|
||||
<ul class=nav id=projects>
|
||||
{{#names}}
|
||||
<li><a href={{.}}>{{.}}</a></li>
|
||||
<li class=ie-bullet>•</li>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
<meta name=viewport content=width=device-width>
|
||||
<title>{{name}} :: samhuri.net</title>
|
||||
<link rel=icon type=image/gif href=../../assets/s.gif>
|
||||
<link rel=stylesheet href=../../GithubFinder/styles.css>
|
||||
<link rel=stylesheet href=../../GithubFinder/css/code_highlighter.css>
|
||||
<link rel=stylesheet href=../../assets/proj-all.min.css>
|
||||
<script>
|
||||
var _gaq = _gaq || [];
|
||||
|
|
@ -22,7 +24,36 @@
|
|||
<script src=../../assets/storage-polyfill.min.js></script>
|
||||
<![endif]-->
|
||||
<script src=../../assets/proj-all.min.js></script>
|
||||
<script> SJS.proj('{{name}}') </script>
|
||||
<script src=http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js></script>
|
||||
<script src=../../GithubFinder/javascripts/util.js></script>
|
||||
<script src=../../GithubFinder/javascripts/vendors/jsonp.js></script>
|
||||
<script src=../../GithubFinder/javascripts/gh.js></script>
|
||||
<script src=../../GithubFinder/javascripts/p.js></script>
|
||||
<script src=../../GithubFinder/javascripts/f.js></script>
|
||||
<script src=../../GithubFinder/javascripts/plugins/base.js></script>
|
||||
<script src=../../GithubFinder/javascripts/keyboard.js></script>
|
||||
<script src=../../GithubFinder/javascripts/plugins/code_highlighter.js></script>
|
||||
<script src=../../GithubFinder/javascripts/vendors/code_highlighter.js></script>
|
||||
<script>
|
||||
(function() {
|
||||
SJS.proj('compiler')
|
||||
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener('DOMContentLoaded', ready, false)
|
||||
} else if (window.attachEvent) {
|
||||
window.attachEvent('onload', ready)
|
||||
}
|
||||
function ready() {
|
||||
var bl = document.getElementById('browse-link')
|
||||
bl.onclick = function() {
|
||||
document.getElementById('browse').style.display = 'none'
|
||||
GH.setProxy('http://samhuri.net/GithubFinder/proxy.php?url=')
|
||||
var f = new F({ user_id: 'samsonjs', repository: 'compiler', id: 'gh-finder' })
|
||||
return false
|
||||
}
|
||||
}
|
||||
}())
|
||||
</script>
|
||||
</head>
|
||||
<nav id=breadcrumbs><a href=../../>samhuri.net</a> → <a href=../>projects</a></nav>
|
||||
<a href=https://github.com/samsonjs/{{name}}><img id=forkme src=../../assets/forkme.png alt="Fork me on GitHub"></a>
|
||||
|
|
@ -50,6 +81,8 @@
|
|||
</div>
|
||||
<br class=clear>
|
||||
</div>
|
||||
<div id=browse><a id=browse-link href=#>browse project</a></div>
|
||||
<div id=gh-finder></div>
|
||||
<footer>
|
||||
<a href=https://twitter.com/_sjs>@_sjs</a>
|
||||
</footer>
|
||||
|
|
|
|||