aladdin 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +22 -0
- data/README.md +43 -0
- data/assets/images/foundation/orbit/bullets.jpg +0 -0
- data/assets/images/foundation/orbit/left-arrow-small.png +0 -0
- data/assets/images/foundation/orbit/left-arrow.png +0 -0
- data/assets/images/foundation/orbit/loading.gif +0 -0
- data/assets/images/foundation/orbit/mask-black.png +0 -0
- data/assets/images/foundation/orbit/pause-black.png +0 -0
- data/assets/images/foundation/orbit/right-arrow-small.png +0 -0
- data/assets/images/foundation/orbit/right-arrow.png +0 -0
- data/assets/images/foundation/orbit/rotator-black.png +0 -0
- data/assets/images/foundation/orbit/timer-black.png +0 -0
- data/assets/javascripts/foundation/app.js +38 -0
- data/assets/javascripts/foundation/jquery.cookie.js +72 -0
- data/assets/javascripts/foundation/jquery.event.move.js +580 -0
- data/assets/javascripts/foundation/jquery.event.swipe.js +130 -0
- data/assets/javascripts/foundation/jquery.foundation.accordion.js +34 -0
- data/assets/javascripts/foundation/jquery.foundation.alerts.js +20 -0
- data/assets/javascripts/foundation/jquery.foundation.buttons.js +74 -0
- data/assets/javascripts/foundation/jquery.foundation.clearing.js +468 -0
- data/assets/javascripts/foundation/jquery.foundation.forms.js +486 -0
- data/assets/javascripts/foundation/jquery.foundation.joyride.js +639 -0
- data/assets/javascripts/foundation/jquery.foundation.magellan.js +85 -0
- data/assets/javascripts/foundation/jquery.foundation.mediaQueryToggle.js +27 -0
- data/assets/javascripts/foundation/jquery.foundation.navigation.js +55 -0
- data/assets/javascripts/foundation/jquery.foundation.orbit.js +897 -0
- data/assets/javascripts/foundation/jquery.foundation.reveal.js +794 -0
- data/assets/javascripts/foundation/jquery.foundation.tabs.js +43 -0
- data/assets/javascripts/foundation/jquery.foundation.tooltips.js +193 -0
- data/assets/javascripts/foundation/jquery.foundation.topbar.js +152 -0
- data/assets/javascripts/foundation/jquery.js +9440 -0
- data/assets/javascripts/foundation/jquery.offcanvas.js +50 -0
- data/assets/javascripts/foundation/jquery.placeholder.js +157 -0
- data/assets/javascripts/foundation/modernizr.foundation.js +4 -0
- data/bin/aladdin +20 -0
- data/lib/aladdin.rb +43 -0
- data/lib/aladdin/app.rb +89 -0
- data/lib/aladdin/render/markdown.rb +40 -0
- data/lib/aladdin/render/sanitize.rb +84 -0
- data/lib/aladdin/version.rb +4 -0
- data/views/haml/index.haml +43 -0
- data/views/haml/layout.haml +51 -0
- data/views/scss/_settings.scss +242 -0
- data/views/scss/app.scss +47 -0
- data/views/scss/github.scss +65 -0
- data/views/scss/pygment.scss +12 -0
- metadata +307 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
;(function (window, document, $) {
|
2
|
+
// Set the negative margin on the top menu for slide-menu pages
|
3
|
+
var $selector1 = $('#topMenu'),
|
4
|
+
events = 'click.fndtn';
|
5
|
+
if ($selector1.length > 0) $selector1.css("margin-top", $selector1.height() * -1);
|
6
|
+
|
7
|
+
// Watch for clicks to show the sidebar
|
8
|
+
var $selector2 = $('#sidebarButton');
|
9
|
+
if ($selector2.length > 0) {
|
10
|
+
$('#sidebarButton').on(events, function (e) {
|
11
|
+
e.preventDefault();
|
12
|
+
$('body').toggleClass('active');
|
13
|
+
});
|
14
|
+
}
|
15
|
+
|
16
|
+
// Watch for clicks to show the menu for slide-menu pages
|
17
|
+
var $selector3 = $('#menuButton');
|
18
|
+
if ($selector3.length > 0) {
|
19
|
+
$('#menuButton').on(events, function (e) {
|
20
|
+
e.preventDefault();
|
21
|
+
$('body').toggleClass('active-menu');
|
22
|
+
});
|
23
|
+
}
|
24
|
+
|
25
|
+
// // Adjust sidebars and sizes when resized
|
26
|
+
// $(window).resize(function() {
|
27
|
+
// // if (!navigator.userAgent.match(/Android/i)) $('body').removeClass('active');
|
28
|
+
// var $selector4 = $('#topMenu');
|
29
|
+
// if ($selector4.length > 0) $selector4.css("margin-top", $selector4.height() * -1);
|
30
|
+
// });
|
31
|
+
|
32
|
+
// Switch panels for the paneled nav on mobile
|
33
|
+
var $selector5 = $('#switchPanels');
|
34
|
+
if ($selector5.length > 0) {
|
35
|
+
$('#switchPanels dd').on(events, function (e) {
|
36
|
+
e.preventDefault();
|
37
|
+
var switchToPanel = $(this).children('a').attr('href'),
|
38
|
+
switchToIndex = $(switchToPanel).index();
|
39
|
+
$(this).toggleClass('active').siblings().removeClass('active');
|
40
|
+
$(switchToPanel).parent().css("left", (switchToIndex * (-100) + '%'));
|
41
|
+
});
|
42
|
+
}
|
43
|
+
|
44
|
+
$('#nav li a').on(events, function (e) {
|
45
|
+
e.preventDefault();
|
46
|
+
var href = $(this).attr('href'),
|
47
|
+
$target = $(href);
|
48
|
+
$('html, body').animate({scrollTop : $target.offset().top}, 300);
|
49
|
+
});
|
50
|
+
}(this, document, jQuery));
|
@@ -0,0 +1,157 @@
|
|
1
|
+
/*! http://mths.be/placeholder v2.0.7 by @mathias */
|
2
|
+
;(function(window, document, $) {
|
3
|
+
|
4
|
+
var isInputSupported = 'placeholder' in document.createElement('input'),
|
5
|
+
isTextareaSupported = 'placeholder' in document.createElement('textarea'),
|
6
|
+
prototype = $.fn,
|
7
|
+
valHooks = $.valHooks,
|
8
|
+
hooks,
|
9
|
+
placeholder;
|
10
|
+
|
11
|
+
if (isInputSupported && isTextareaSupported) {
|
12
|
+
|
13
|
+
placeholder = prototype.placeholder = function() {
|
14
|
+
return this;
|
15
|
+
};
|
16
|
+
|
17
|
+
placeholder.input = placeholder.textarea = true;
|
18
|
+
|
19
|
+
} else {
|
20
|
+
|
21
|
+
placeholder = prototype.placeholder = function() {
|
22
|
+
var $this = this;
|
23
|
+
$this
|
24
|
+
.filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]')
|
25
|
+
.not('.placeholder')
|
26
|
+
.bind({
|
27
|
+
'focus.placeholder': clearPlaceholder,
|
28
|
+
'blur.placeholder': setPlaceholder
|
29
|
+
})
|
30
|
+
.data('placeholder-enabled', true)
|
31
|
+
.trigger('blur.placeholder');
|
32
|
+
return $this;
|
33
|
+
};
|
34
|
+
|
35
|
+
placeholder.input = isInputSupported;
|
36
|
+
placeholder.textarea = isTextareaSupported;
|
37
|
+
|
38
|
+
hooks = {
|
39
|
+
'get': function(element) {
|
40
|
+
var $element = $(element);
|
41
|
+
return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value;
|
42
|
+
},
|
43
|
+
'set': function(element, value) {
|
44
|
+
var $element = $(element);
|
45
|
+
if (!$element.data('placeholder-enabled')) {
|
46
|
+
return element.value = value;
|
47
|
+
}
|
48
|
+
if (value == '') {
|
49
|
+
element.value = value;
|
50
|
+
// Issue #56: Setting the placeholder causes problems if the element continues to have focus.
|
51
|
+
if (element != document.activeElement) {
|
52
|
+
// We can't use `triggerHandler` here because of dummy text/password inputs :(
|
53
|
+
setPlaceholder.call(element);
|
54
|
+
}
|
55
|
+
} else if ($element.hasClass('placeholder')) {
|
56
|
+
clearPlaceholder.call(element, true, value) || (element.value = value);
|
57
|
+
} else {
|
58
|
+
element.value = value;
|
59
|
+
}
|
60
|
+
// `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363
|
61
|
+
return $element;
|
62
|
+
}
|
63
|
+
};
|
64
|
+
|
65
|
+
isInputSupported || (valHooks.input = hooks);
|
66
|
+
isTextareaSupported || (valHooks.textarea = hooks);
|
67
|
+
|
68
|
+
$(function() {
|
69
|
+
// Look for forms
|
70
|
+
$(document).delegate('form', 'submit.placeholder', function() {
|
71
|
+
// Clear the placeholder values so they don't get submitted
|
72
|
+
var $inputs = $('.placeholder', this).each(clearPlaceholder);
|
73
|
+
setTimeout(function() {
|
74
|
+
$inputs.each(setPlaceholder);
|
75
|
+
}, 10);
|
76
|
+
});
|
77
|
+
});
|
78
|
+
|
79
|
+
// Clear placeholder values upon page reload
|
80
|
+
$(window).bind('beforeunload.placeholder', function() {
|
81
|
+
$('.placeholder').each(function() {
|
82
|
+
this.value = '';
|
83
|
+
});
|
84
|
+
});
|
85
|
+
|
86
|
+
}
|
87
|
+
|
88
|
+
function args(elem) {
|
89
|
+
// Return an object of element attributes
|
90
|
+
var newAttrs = {},
|
91
|
+
rinlinejQuery = /^jQuery\d+$/;
|
92
|
+
$.each(elem.attributes, function(i, attr) {
|
93
|
+
if (attr.specified && !rinlinejQuery.test(attr.name)) {
|
94
|
+
newAttrs[attr.name] = attr.value;
|
95
|
+
}
|
96
|
+
});
|
97
|
+
return newAttrs;
|
98
|
+
}
|
99
|
+
|
100
|
+
function clearPlaceholder(event, value) {
|
101
|
+
var input = this,
|
102
|
+
$input = $(input);
|
103
|
+
if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) {
|
104
|
+
if ($input.data('placeholder-password')) {
|
105
|
+
$input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id'));
|
106
|
+
// If `clearPlaceholder` was called from `$.valHooks.input.set`
|
107
|
+
if (event === true) {
|
108
|
+
return $input[0].value = value;
|
109
|
+
}
|
110
|
+
$input.focus();
|
111
|
+
} else {
|
112
|
+
input.value = '';
|
113
|
+
$input.removeClass('placeholder');
|
114
|
+
input == document.activeElement && input.select();
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
function setPlaceholder() {
|
120
|
+
var $replacement,
|
121
|
+
input = this,
|
122
|
+
$input = $(input),
|
123
|
+
$origInput = $input,
|
124
|
+
id = this.id;
|
125
|
+
if (input.value == '') {
|
126
|
+
if (input.type == 'password') {
|
127
|
+
if (!$input.data('placeholder-textinput')) {
|
128
|
+
try {
|
129
|
+
$replacement = $input.clone().attr({ 'type': 'text' });
|
130
|
+
} catch(e) {
|
131
|
+
$replacement = $('<input>').attr($.extend(args(this), { 'type': 'text' }));
|
132
|
+
}
|
133
|
+
$replacement
|
134
|
+
.removeAttr('name')
|
135
|
+
.data({
|
136
|
+
'placeholder-password': true,
|
137
|
+
'placeholder-id': id
|
138
|
+
})
|
139
|
+
.bind('focus.placeholder', clearPlaceholder);
|
140
|
+
$input
|
141
|
+
.data({
|
142
|
+
'placeholder-textinput': $replacement,
|
143
|
+
'placeholder-id': id
|
144
|
+
})
|
145
|
+
.before($replacement);
|
146
|
+
}
|
147
|
+
$input = $input.removeAttr('id').hide().prev().attr('id', id).show();
|
148
|
+
// Note: `$input[0] != input` now!
|
149
|
+
}
|
150
|
+
$input.addClass('placeholder');
|
151
|
+
$input[0].value = $input.attr('placeholder');
|
152
|
+
} else {
|
153
|
+
$input.removeClass('placeholder');
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
}(this, document, jQuery));
|
@@ -0,0 +1,4 @@
|
|
1
|
+
/* Modernizr 2.6.2 (Custom Build) | MIT & BSD
|
2
|
+
* Build: http://modernizr.com/download/#-inlinesvg-svg-svgclippaths-touch-shiv-mq-cssclasses-teststyles-prefixes-ie8compat-load
|
3
|
+
*/
|
4
|
+
;window.Modernizr=function(a,b,c){function y(a){j.cssText=a}function z(a,b){return y(m.join(a+";")+(b||""))}function A(a,b){return typeof a===b}function B(a,b){return!!~(""+a).indexOf(b)}function C(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:A(f,"function")?f.bind(d||b):f}return!1}var d="2.6.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n={svg:"http://www.w3.org/2000/svg"},o={},p={},q={},r=[],s=r.slice,t,u=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["­",'<style id="s',h,'">',a,"</style>"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},v=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return u("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},w={}.hasOwnProperty,x;!A(w,"undefined")&&!A(w.call,"undefined")?x=function(a,b){return w.call(a,b)}:x=function(a,b){return b in a&&A(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=s.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(s.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(s.call(arguments)))};return e}),o.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:u(["@media (",m.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},o.svg=function(){return!!b.createElementNS&&!!b.createElementNS(n.svg,"svg").createSVGRect},o.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="<svg/>",(a.firstChild&&a.firstChild.namespaceURI)==n.svg},o.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(l.call(b.createElementNS(n.svg,"clipPath")))};for(var D in o)x(o,D)&&(t=D.toLowerCase(),e[t]=o[D](),r.push((e[t]?"":"no-")+t));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)x(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},y(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e<g;e++)d.createElement(f[e]);return d}function p(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return r.shivMethods?n(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+l().join().replace(/\w+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(r,b.frag)}function q(a){a||(a=b);var c=m(a);return r.shivCSS&&!f&&!c.hasCSS&&(c.hasCSS=!!k(a,"article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}mark{background:#FF0;color:#000}")),j||p(a,c),a}var c=a.html5||{},d=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,e=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,f,g="_html5shiv",h=0,i={},j;(function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=m,e.mq=v,e.testStyles=u,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+r.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,h){var i=b(a),j=i.autoCallback;i.url.split(".").pop().split("?").shift(),i.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]),i.instead?i.instead(a,e,f,g,h):(y[i.url]?i.noexec=!0:y[i.url]=1,f.load(i.url,i.forceCSS||!i.forceJS&&"css"==i.url.split(".").pop().split("?").shift()?"c":c,i.noexec,i.attrs,i.timeout),(d(e)||d(j))&&f.load(function(){k(),e&&e(i.origUrl,h,g),j&&j(i.origUrl,h,g),y[i.url]=2})))}function h(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var i,j,l=this.yepnope.loader;if(e(a))g(a,0,l,0);else if(w(a))for(i=0;i<a.length;i++)j=a[i],e(j)?g(j,0,l,0):w(j)?B(j):Object(j)===j&&h(j,l);else Object(a)===a&&h(a,l)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,null==b.readyState&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}}(this,document),Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0))},Modernizr.addTest("ie8compat",function(){return!window.addEventListener&&document.documentMode&&document.documentMode===7});
|
data/bin/aladdin
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
|
5
|
+
require 'aladdin'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
opt_parser = OptionParser.new do |opts|
|
9
|
+
|
10
|
+
opts.banner = 'Usage: aladdin /path/to/tutorial/directory'
|
11
|
+
|
12
|
+
opts.on_tail('--version', 'Display current gem version.') do
|
13
|
+
puts 'aladdin v' + Aladdin::VERSION
|
14
|
+
exit 0
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
opt_parser.parse!
|
19
|
+
|
20
|
+
Aladdin.launch from: ARGV[0]
|
data/lib/aladdin.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
require 'sinatra'
|
3
|
+
require 'zurb-foundation'
|
4
|
+
require 'albino'
|
5
|
+
require 'haml'
|
6
|
+
require 'redcarpet'
|
7
|
+
require 'sanitize'
|
8
|
+
|
9
|
+
require 'aladdin/render/sanitize'
|
10
|
+
require 'aladdin/render/markdown'
|
11
|
+
|
12
|
+
# Aladdin is for tutorial apps.
|
13
|
+
module Aladdin
|
14
|
+
|
15
|
+
# Launches the tutorial app using 'thin' as the default webserver.
|
16
|
+
# @option opts [String] from path to author's markdown documents;
|
17
|
+
# defaults to the current working directory
|
18
|
+
def self.launch(opts = {})
|
19
|
+
Aladdin::App.set :views, Aladdin::VIEWS.merge(markdown: opts[:from] || '.')
|
20
|
+
Aladdin::App.run!
|
21
|
+
end
|
22
|
+
|
23
|
+
# Converts a hash to struct.
|
24
|
+
def self.to_struct(hash)
|
25
|
+
Struct.new( *(k = hash.keys) ).new( *hash.values_at( *k ) )
|
26
|
+
end
|
27
|
+
private_class_method :to_struct
|
28
|
+
|
29
|
+
# Paths to different types of views.
|
30
|
+
VIEWS = {
|
31
|
+
haml: File.expand_path('../../views/haml', __FILE__),
|
32
|
+
scss: File.expand_path('../../views/scss', __FILE__),
|
33
|
+
default: File.expand_path('../../views', __FILE__)
|
34
|
+
}
|
35
|
+
|
36
|
+
# Paths to other parts of the library.
|
37
|
+
PATHS = to_struct(
|
38
|
+
assets: File.expand_path('../../assets', __FILE__),
|
39
|
+
).freeze
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
require 'aladdin/app'
|
data/lib/aladdin/app.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
module Aladdin
|
3
|
+
|
4
|
+
# Sinatra app that serves the tutorial. Should be able to use this in a
|
5
|
+
# config.ru file or as middleware. Authors should launch the app using the
|
6
|
+
# +bin/aladdin+ executable.
|
7
|
+
# Adapted from https://github.com/jerodsanto/sinatra-foundation-skeleton/
|
8
|
+
class App < Sinatra::Base
|
9
|
+
|
10
|
+
# Default markdown options.
|
11
|
+
MARKDOWN_OPTIONS = {
|
12
|
+
renderer: Aladdin::Render::HTML,
|
13
|
+
no_intra_emphasis: true,
|
14
|
+
tables: true,
|
15
|
+
fenced_code_blocks: true,
|
16
|
+
autolink: true,
|
17
|
+
strikethrough: true,
|
18
|
+
layout_engine: :haml
|
19
|
+
}
|
20
|
+
|
21
|
+
class << self
|
22
|
+
private
|
23
|
+
|
24
|
+
# Configures path to the views, with different paths for different file
|
25
|
+
# types.
|
26
|
+
# @return [void]
|
27
|
+
def configure_views
|
28
|
+
helpers do
|
29
|
+
def find_template(views, name, engine, &block)
|
30
|
+
_, dir = views.detect { |k,v| engine == Tilt[k] }
|
31
|
+
dir ||= views[:default]
|
32
|
+
super(dir, name, engine, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Configures path to static assets in the public folder.
|
38
|
+
# @return [void]
|
39
|
+
def configure_assets
|
40
|
+
set :public_folder, Aladdin::PATHS.assets
|
41
|
+
end
|
42
|
+
|
43
|
+
# Configures ZURB's compass to compile laddin's scss assets.
|
44
|
+
# @return [void]
|
45
|
+
def configure_compass
|
46
|
+
Compass.configuration do |config|
|
47
|
+
config.http_path = '/'
|
48
|
+
config.http_images_path = '/images'
|
49
|
+
end
|
50
|
+
set :scss, Compass.sass_engine_options
|
51
|
+
end
|
52
|
+
|
53
|
+
# Registers redcarpet2 and laddin's markdown renderer to be as close to
|
54
|
+
# the github-flavored markdown as possible.
|
55
|
+
# @return [void]
|
56
|
+
def configure_markdown
|
57
|
+
Tilt.register Tilt::RedcarpetTemplate::Redcarpet2, 'markdown', 'mkd', 'md'
|
58
|
+
set :markdown, MARKDOWN_OPTIONS
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
# Calls the given +block+ and invokes +pass+ on error.
|
64
|
+
# @param block block to call within wrapper
|
65
|
+
def render_or_pass(&block)
|
66
|
+
begin return block.call
|
67
|
+
rescue pass
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
configure_views
|
72
|
+
configure_markdown
|
73
|
+
|
74
|
+
configure :development, :test do
|
75
|
+
configure_assets
|
76
|
+
configure_compass
|
77
|
+
end
|
78
|
+
|
79
|
+
get '/stylesheets/*.css' do |path|
|
80
|
+
render_or_pass { scss path.to_sym }
|
81
|
+
end
|
82
|
+
|
83
|
+
get '/*' do |path|
|
84
|
+
render_or_pass { markdown path.to_sym }
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
module Aladdin
|
3
|
+
|
4
|
+
# laddin-render module for all of Laddin's rendering needs.
|
5
|
+
module Render
|
6
|
+
|
7
|
+
# HTML Renderer for Markdown.
|
8
|
+
# It creates pygmentized code blocks, supports hard-wraps, and only
|
9
|
+
# generates links for protocols which are considered safe.
|
10
|
+
# @see http://github.github.com/github-flavored-markdown/
|
11
|
+
class HTML < ::Redcarpet::Render::HTML
|
12
|
+
|
13
|
+
@sanitize = Aladdin::Sanitize.new
|
14
|
+
class << self; attr_reader :sanitize; end
|
15
|
+
|
16
|
+
# Creates a new HTML renderer.
|
17
|
+
# @param [Hash] options described in the RedCarpet documentation.
|
18
|
+
def initialize(options = {})
|
19
|
+
super options.merge(hard_wrap: true, safe_links_only: true)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Pygmentizes code blocks.
|
23
|
+
# @param [String] code code block contents
|
24
|
+
# @param [String] language name of language, for syntax highlighting
|
25
|
+
# @return [String] highlighted code
|
26
|
+
def block_code(code, language)
|
27
|
+
Albino.colorize code, language
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sanitizes the final document.
|
31
|
+
# @param [String] document html document
|
32
|
+
# @return [String] sanitized document
|
33
|
+
def postprocess(document)
|
34
|
+
HTML.sanitize.clean document
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
|
3
|
+
module Aladdin
|
4
|
+
|
5
|
+
# Encapsulate sanitization options.
|
6
|
+
# Adapted from
|
7
|
+
# https://github.com/github/gollum/blob/master/lib/gollum/sanitization.rb
|
8
|
+
class Sanitize < ::Sanitize
|
9
|
+
|
10
|
+
# white-listed elements
|
11
|
+
ELEMENTS = [
|
12
|
+
'a', 'abbr', 'acronym', 'address', 'area', 'b', 'big',
|
13
|
+
'blockquote', 'br', 'button', 'caption', 'center', 'cite',
|
14
|
+
'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir',
|
15
|
+
'div', 'dl', 'dt', 'em', 'fieldset', 'font', 'form', 'h1',
|
16
|
+
'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'input',
|
17
|
+
'ins', 'kbd', 'label', 'legend', 'li', 'map', 'menu',
|
18
|
+
'ol', 'optgroup', 'option', 'p', 'pre', 'q', 's', 'samp',
|
19
|
+
'select', 'small', 'span', 'strike', 'strong', 'sub',
|
20
|
+
'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th',
|
21
|
+
'thead', 'tr', 'tt', 'u', 'ul', 'var'
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
# white-listed attributes
|
25
|
+
ATTRIBUTES = {
|
26
|
+
'a' => ['href'],
|
27
|
+
'img' => ['src'],
|
28
|
+
:all => ['abbr', 'accept', 'accept-charset',
|
29
|
+
'accesskey', 'action', 'align', 'alt', 'axis',
|
30
|
+
'border', 'cellpadding', 'cellspacing', 'char',
|
31
|
+
'charoff', 'class', 'charset', 'checked', 'cite',
|
32
|
+
'clear', 'cols', 'colspan', 'color',
|
33
|
+
'compact', 'coords', 'datetime', 'dir',
|
34
|
+
'disabled', 'enctype', 'for', 'frame',
|
35
|
+
'headers', 'height', 'hreflang',
|
36
|
+
'hspace', 'id', 'ismap', 'label', 'lang',
|
37
|
+
'longdesc', 'maxlength', 'media', 'method',
|
38
|
+
'multiple', 'name', 'nohref', 'noshade',
|
39
|
+
'nowrap', 'prompt', 'readonly', 'rel', 'rev',
|
40
|
+
'rows', 'rowspan', 'rules', 'scope',
|
41
|
+
'selected', 'shape', 'size', 'span',
|
42
|
+
'start', 'summary', 'tabindex', 'target',
|
43
|
+
'title', 'type', 'usemap', 'valign', 'value',
|
44
|
+
'vspace', 'width']
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
# white-listed protocols
|
48
|
+
PROTOCOLS = {
|
49
|
+
'a' => {'href' => ['http', 'https', 'mailto', 'ftp', 'irc', 'apt', :relative]},
|
50
|
+
'img' => {'src' => ['http', 'https', :relative]}
|
51
|
+
}.freeze
|
52
|
+
|
53
|
+
# elements to remove (incl. contents)
|
54
|
+
REMOVE_CONTENTS = [
|
55
|
+
'script',
|
56
|
+
'style'
|
57
|
+
].freeze
|
58
|
+
|
59
|
+
# attributes to add to elements
|
60
|
+
ADD_ATTRIBUTES = {
|
61
|
+
'a' => {'rel' => 'nofollow'}
|
62
|
+
}
|
63
|
+
|
64
|
+
# Creates a new sanitizer with Aladdin's configuration.
|
65
|
+
def initialize
|
66
|
+
super config
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Returns a configuration hash.
|
72
|
+
def config
|
73
|
+
{ elements: ELEMENTS.dup,
|
74
|
+
attributes: ATTRIBUTES.dup,
|
75
|
+
protocols: PROTOCOLS.dup,
|
76
|
+
add_attributes: ADD_ATTRIBUTES.dup,
|
77
|
+
remove_contents: REMOVE_CONTENTS.dup,
|
78
|
+
allow_comments: false
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|