Added javascript asset management and improved Rakefiles and configuration for themes and plugins.

- Added Guard for file watching
- Configs can be automatically reloaded
- Static asset changes do not trigger Jekyll build
- CommonJS modular js support proved by stich-rb
- Javascript is concatenated and uglified
- Environment variables toggle uglify and fingerprinting
- New Jekyll plugin config_tag
- New Jekyll plugin javascript_assets_tag
- Added theme specific configurations
- Custome Jekyll Guard to the rescue
- Install, Generate, Watch, and Preview work with Guard now.
- Now configs are no longer tracked by Octopress, only theme defauts are.
- Console messages can be colorized.
- misc config reorganization and improvements
This commit is contained in:
Brandon Mathis 2013-02-10 02:47:53 -06:00
parent 4972fb6c62
commit 7bdab0e65c
83 changed files with 1725 additions and 591 deletions

View File

@ -0,0 +1,10 @@
---
# -------------------------------- #
# App.net Sidebar Plugin Configs #
# -------------------------------- #
adn:
user:
post-count: 4
show-replies: false
show-reposts: false

View File

@ -0,0 +1,4 @@
# Delicious link stream
delicious:
user:
count: 4

View File

@ -7,6 +7,3 @@
# This will be configured for you when you run config_deploy # This will be configured for you when you run config_deploy
deploy_branch: "gh-pages" deploy_branch: "gh-pages"
deploy_dir: "_deploy" deploy_dir: "_deploy"
# Hidden "dot" files that should be included with the deployed site (see task copydot)
copy_dot_files: []

View File

@ -0,0 +1,14 @@
---
# ------------------------ #
# RSync Config #
# deploy_default: rsync #
# ------------------------ #
# Be sure your public key is listed in your server's ~/.ssh/authorized_keys file
ssh_user: "user@domain.com"
ssh_port: "22"
document_root: "~/website.com/"
rsync_delete: false
rsync_args: "" # Any extra arguments to pass to rsync
rsync_checksum: true # Attempts to only upload changed files
deploy_dir: "_deploy"

View File

@ -0,0 +1,2 @@
# Facebook Like button on posts/pages
facebook_like: false

View File

@ -0,0 +1,24 @@
---
# --------------------------------------- #
# Github Sidebar Repos Listing Plugin #
# --------------------------------------- #
# Github repositories
github_repos:
user:
# Automatically select repositories based on:
count: 4
skip_forks: true # Don't show repos you've forked from others
sort_by_watchers: true # Show your top watched projects, set to false to sort by last update
# Instead of fetching automatically, specify a list of repositories to include
# Setting repos will ignore the automatic settings above
repos:
# Example:
# user: imathis
# repos:
# - octopress
# - hsl-color-picker

View File

@ -4,6 +4,10 @@
# --------------------------------- # # --------------------------------- #
# Hidden: No visible button, just add author information to search results # Hidden: No visible button, just add author information to search results
googleplus_user: googleplus:
googleplus_hidden: false user:
google_plus_image_size: 32 hidden: false
image_size: 32
# Google+ Sharing
plus_one: false

View File

@ -10,7 +10,7 @@ source: source # source file directory
destination: public # compiled site directory destination: public # compiled site directory
plugins: plugins plugins: plugins
code_dir: downloads/code code_dir: downloads/code
category_dir: blog/categories category_dir: categories
include: include:
- .htaccess - .htaccess
markdown: redcarpet markdown: redcarpet
@ -22,14 +22,15 @@ redcarpet:
- superscript - superscript
- smart - smart
pygments: false # Jekyll's default Python Pygments have been replaced by pygments.rb. pygments: false # Jekyll's default Python Pygments have been replaced by pygments.rb.
# Set to true to use Albino + Python Pygments include:
- .htaccess
env: production env: production # affects asset compilation
blog_index_dir: source # directory for your blog's index page (if you put your index in source/blog/index.html, set this to 'source/blog') post_index_dir: source # directory for your posts index page (if you put your index in source/blog/index.html, set this to 'source/blog')
stash_dir: _stash # directory to stash posts for speedy generation stash_dir: _stash # directory to stash posts for speedy generation
posts_dir: _posts # directory for blog files posts_dir: _posts # directory for blog files
themes_dir: .themes # directory for blog files
new_post_ext: markdown # default new post file extension when using the new_post task new_post_ext: markdown # default new post file extension when using the new_post task
new_page_ext: markdown # default new page file extension when using the new_page task new_page_ext: markdown # default new page file extension when using the new_page task
server_host: 0.0.0.0 # host ip address for preview server server_host: 0.0.0.0 # host ip address for preview server
server_port: 4000 # port for preview server eg. localhost:4000 server_port: 4000 # port for preview server eg. localhost:4000
timezone: local # default time and date used to local timezone. Vew supported timezones (under TZ column): http://en.wikipedia.org/wiki/List_of_tz_database_time_zones

View File

@ -0,0 +1,4 @@
# Pinboard link stream
pinboard:
user:
count: 4

View File

@ -0,0 +1,17 @@
---
# Asset configuration
# Javascript assets stored in assets/javascripts and assets/javascripts/lib
# Are wrapped with CommonJS functions, combined, uglified and fingerprinted
# Supported files: .js, .coffee, .mustache, .eco, .tmpl
require_js:
# Dependiences are added first as globals
dependencies:
- lib/modernizr.js
- lib/swfobject-dynamic.js
- lib/*.*
# files from the javascripts root path are wrapped with CommonJS functions
modules:
- '*.*'

View File

@ -3,6 +3,14 @@
# Classic Theme Configuration # # Classic Theme Configuration #
# ------------------------------- # # ------------------------------- #
theme:
name: classic
# path to theme stylesheets directory
stylesheets_dir: assets/stylesheets
# path to theme javascripts directory
javascripts_dir: assets/javascripts
url: http://yoursite.com url: http://yoursite.com
title: My Octopress Blog title: My Octopress Blog
subtitle: A blogging framework for hackers. subtitle: A blogging framework for hackers.
@ -32,13 +40,13 @@ page_sidebar: page_default.html
post_sidebar: post_default.html post_sidebar: post_default.html
paginate: 10 # Posts per page on the blog index paginate: 10 # Posts per page on the blog index
pagination_dir: blog # Directory base for pagination URLs eg. /blog/page/2/ pagination_dir: # Directory base for pagination URLs eg. /page/2/
recent_posts: 5 # Posts in the sidebar Recent Posts section recent_posts: 5 # Posts in the sidebar Recent Posts section
excerpt_link: "Read on →" # "Continue reading" link text at the bottom of excerpted articles excerpt_link: "Read on " # "Continue reading" link text at the bottom of excerpted articles
excerpt_in_feed: false # Truncate excerpted articles in the atom feed excerpt_in_feed: false # Truncate excerpted articles in the atom feed
permalink_label: "Permalink" permalink_label: "Permalink"
permalink_label_feed: "⚓ Permalink" permalink_label_feed: " Permalink"
linklog_marker: "→" linklog_marker: ""
linklog_marker_position: after linklog_marker_position: after
linklog_marker_position_feed: after linklog_marker_position_feed: after
standard_post_marker: standard_post_marker:

View File

@ -0,0 +1,19 @@
---
# -------------------------------- #
# Tweet Sidebar Plugin Configs #
# -------------------------------- #
twitter:
user:
timeline:
tweet-count: 4
show-replies: false
show-rts: false
follow-button:
show: true
follower-count: false
# Sharing button on posts
tweet-button: true

View File

@ -0,0 +1,68 @@
# App.net fetcher for Octopress (c) Brandon Mathis // MIT License
helpers = require('helpers')
Adn =
timeline: []
cookie: 'adn-feed'
classname: 'adn-feed'
template: ->
helpers.statusFeed @timeline, @classname
errorTemplate: ->
helpers.errorTemplate "Failed to load posts.", @classname
parseHtml: (post)->
text = helpers.trimDisplayUrls post.html
text = text.replace "@#{mention.name}", "<a href='https://alpha.app.net/#{mention.name}'>@#{mention.name}</a>" for mention in post.entities.mentions
text = text.replace "##{hashtag.name}", "<a href='https://alpha.app.net/hashtags/#{hashtag.name}'>##{hashtag.name}</a>" for hashtag in post.entities.hashtags
text
getPost: (post) ->
type = if post.repost_of then 'repost' else 'post'
post = if type is 'repost' then post.repost_of else post
{
type: type
url: post.canonical_url
date: post.created_at
author: { user: post.user.username, name: post.user.name, url: post.user.canonical_url } if type is 'repost'
text: @parseHtml post
}
format: (posts, user, options) ->
postList = []
for post in posts
postList.push @getPost(post) unless post.repost_of and !options.reposts
postList
init: (user, options, callback) ->
posts = $.cookie @cookie
if posts
@timeline = JSON.parse(posts)
if @timeline.length isnt options.count
$.removeCookie @cookie
@timeline = []
init user, options, callback
else
callback @template()
else
url = "https://alpha-api.app.net/stream/0/users/@#{user}/posts?"
url += "&max_id=#{options.max_id}" if options.max_id
url += "&include_directed_posts=0" unless options.replies
url += "&callback=?"
$.ajax
url: url
dataType: 'jsonp'
error: (err) => callback @errorTemplate
success: (response) =>
@timeline = @timeline.concat response.data
if @timeline.length < options.count
options.max_id = response.meta.max_id
init user, options, callback
else
@timeline = @format @timeline.slice(0, options.count), user, options
$.cookie @cookie, JSON.stringify @timeline, { path: '/' }
callback @template()
module.exports = Adn

View File

@ -0,0 +1,38 @@
# Delicious fetcher for Octopress (c) Brandon Mathis // MIT License
helpers = require('helpers')
Delicious =
cookie: 'delicious-feed'
classname: 'delicious-feed'
template: (data) ->
helpers.linkFeed data, @classname
errorTemplate: ->
helpers.errorTemplate "Failed to load bookmarks.", @classname
format: (feed) ->
for item in feed
{
url: item.u
date: item.dt
title: item.d
text: item.n
meta: "Tagged: " + ("<a title='view items #{item.a} tagged #{tag}' href='http://delicious.com/#{item.a}/#{tag}'>#{tag}</a>" for tag in item.t).join ' '
}
init: (user, options, callback) ->
feed = $.cookie @cookie
if feed
callback @template JSON.parse(feed)
else
$.ajax
url: "http://feeds.delicious.com/v2/json/#{user}?&count=#{options.count}&callback=?"
dataType: 'jsonp'
error: (err) => callback @errorTemplate
success: (data) =>
data = @format data
$.cookie @cookie, JSON.stringify data, { path: '/' }
callback @template data
module.exports = Delicious

View File

@ -0,0 +1,60 @@
# Github fetcher for Octopress (c) Brandon Mathis // MIT License
helpers = require('helpers')
GitHub =
cookie: 'github-feed'
classname: 'github-feed'
template: (data)->
helpers.linkFeed data, @classname
errorTemplate: ->
helpers.errorTemplate "Failed to load repo list.", @classname
addRepo: (repo)->
title: repo.name
url: repo.url
text: repo.description
meta: repo.meta
format: (repos, options) ->
repoList = []
if options.repos
filter = []
filter.push i.trim().toLowerCase() for i in options.repos.split ','
for repo in repos
unless repoList.length is options.count
if options.forks or options.watchers
repo.meta = ''
if options.watchers
repo.meta += "Watchers: #{repo.watchers}"
if options.forks
repo.meta += ' ,' if repo.meta.length > 0
repo.meta += "Forks: #{repo.forks}"
if filter and filter.indexOf repo.name.toLowerCase() > -1
# repo order should match list
repoList[filter.indexOf repo.name] = @addRepo repo, options
else if !filter
repoList.push @addRepo repo, options unless options.skipForks and repo.fork
repoList
init: (user, options, callback) ->
if options.count or options.repos
feed = $.cookie @cookie
if feed
callback @template JSON.parse(feed)
else
$.ajax
url: "https://api.github.com/users/#{user}/repos?callback=?"
dataType: 'jsonp'
error: (err) => callback @errorTemplate
success: (data) =>
data = @format(data.data, options)
$.cookie @cookie, JSON.stringify data, { path: '/' }
callback @template data
module.exports = GitHub

View File

@ -0,0 +1,60 @@
# Github fetcher for Octopress (c) Brandon Mathis // MIT License
helpers = require('helpers')
GitHub =
cookie: 'github-feed'
classname: 'github-feed'
template: (data)->
helpers.linkFeed data, @classname
errorTemplate: ->
helpers.errorTemplate "Failed to load repo list.", @classname
addRepo: (repo)->
title: repo.name
url: repo.url
text: repo.description
meta: repo.meta
format: (repos, options) ->
repoList = []
if options.repos
filter = []
filter.push i.trim().toLowerCase() for i in options.repos.split ','
for repo in repos
unless repoList.length is options.count and options.count > 1
if options.forks or options.watchers
repo.meta = ''
if options.watchers
repo.meta += "Watchers: #{repo.watchers}"
if options.forks
repo.meta += ' ,' if repo.meta.length > 0
repo.meta += "Forks: #{repo.forks}"
if filter and filter.indexOf repo.name.toLowerCase() > -1
# repo order should match list
repoList[filter.indexOf repo.name] = @addRepo repo, options
else if !filter
repoList.push @addRepo repo, options unless options.skipForks and repo.fork
repoList
init: (user, options, callback) ->
if options.count or options.repos
feed = $.cookie @cookie
if feed
callback @template JSON.parse(feed)
else
$.ajax
url: "https://api.github.com/users/#{user}/repos?callback=?"
dataType: 'jsonp'
error: (err) => callback @errorTemplate
success: (data) =>
data = @format(data.data, options)
$.cookie @cookie, JSON.stringify data, { path: '/' }
callback @template data
module.exports = GitHub

View File

@ -0,0 +1,96 @@
# Helper methods for Octopress (c) 2013 Brandon Mathis // MIT License
Helpers =
# Template helpers
linkFeed: (feed, classname)->
"<ul class='#{classname}'>" + (for item in feed
text = "<li class='feed-item'>"
text += "<p class='title'><a href='#{item.url}'>#{item.title.replace '<','&lt;'}</a></p>"
text += "<p class='text'>#{item.text.replace '<','&lt;'}</p>"
text += "<p class='meta'>#{item.meta}</p>" if item.meta
text += "</li>"
).join('') + "</ul>"
statusFeed: (feed, classname = '')->
"<ul class='#{classname}'>" + (for post in feed
text = "<li class='status-item'>"
text += "<p><a href='#{post.url}'>#{@timeago post.date}</a>#{post.text}</p>"
text += "</li>"
).join('') + "</ul>"
errorTemplate: (message, service = '') ->
"<p class='#{service} feed-error'>#{message}</p>"
htmlEscape: (str) ->
return String(str)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
# Url helpers
trimUrl: (url)->
parts = []
for part in url.split '/'
parts.push part if parts.concat(part).join('/').length < 35
parts = parts.join('/')
if parts.length < url.length then parts + '&hellip;' else parts
linkify: (text, marker, url) ->
text.replace new RegExp("(^|\\W|>)(#{marker})([^\\s<]+)", 'g'), "$1<a href='#{url}$3'>$2$3</a>"
trimDisplayUrls: (text)->
text.replace />https?:\/\/([^\s<]+)/gi, (match, p1)=>
">#{@trimUrl(p1)}"
# ----------#
# Utilities #
# ----------#
# Sort an array of objects on a given key
sortByKey: (list, key, order='asc') ->
list = list.sort (a,b)->
if a[key] > b[key] then 1 else if b[key] > a[key] then -1 else 0
if order is 'desc' then list.reverse() else list
# Timeago based on JavaScript Pretty Date Copyright (c) 2011 John Resig
timeago: (time)->
say =
just_now: "now"
minute_ago: "1m"
minutes_ago: "m"
hour_ago: "1h"
hours_ago: "h"
yesterday: "1d"
days_ago: "d"
last_week: "1w"
weeks_ago: "w"
time = time.replace(/-/g,"/").replace(/[TZ]/g," ")
secs = ((new Date().getTime() - new Date(time).getTime()) / 1000)
mins = Math.floor secs / 60
hours = Math.floor secs / 3600
days = Math.floor secs / 86400
weeks = Math.floor days / 7
return '#' if isNaN(secs) or days < 0
if days is 0
if secs < 60 then say.just_now
else if secs < 120 then say.minute_ago
else if secs < 3600 then mins + say.minutes_ago
else if secs < 7200 then say.hour_ago
else hours + say.hours_ago
else
if days is 1 then say.yesterday
else if days < 7 then days + say.days_ago
else if days is 7 then say.last_week
else weeks + say.weeks_ago
module.exports = Helpers

View File

@ -0,0 +1,18 @@
// iOS scaling bug fix
// By @mathias, @cheeaun and @jdalton
// Source url: https://gist.github.com/901295
var addEvent = 'addEventListener',
type = 'gesturestart',
qsa = 'querySelectorAll',
scales = [1, 1],
meta = qsa in doc ? doc[qsa]('meta[name=viewport]') : [];
function fix() {
meta.content = 'width=device-width,minimum-scale=' + scales[0] + ',maximum-scale=' + scales[1];
doc.removeEventListener(type, fix, true);
}
if ((meta = meta[meta.length - 1]) && addEvent in doc) {
fix();
scales = [0.25, 1.6];
doc[addEvent](type, fix, true);
}

View File

@ -0,0 +1,92 @@
/*!
* jQuery Cookie Plugin v1.3.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd && define.amd.jQuery) {
// AMD. Register as anonymous module.
define(['jquery'], factory);
} else {
// Browser globals.
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function raw(s) {
return s;
}
function decoded(s) {
return decodeURIComponent(s.replace(pluses, ' '));
}
function converted(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
return config.json ? JSON.parse(s) : s;
} catch(er) {}
}
var config = $.cookie = function (key, value, options) {
// write
if (value !== undefined) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setDate(t.getDate() + days);
}
value = config.json ? JSON.stringify(value) : String(value);
return (document.cookie = [
encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// read
var decode = config.raw ? raw : decoded;
var cookies = document.cookie.split('; ');
var result = key ? undefined : {};
for (var i = 0, l = cookies.length; i < l; i++) {
var parts = cookies[i].split('=');
var name = decode(parts.shift());
var cookie = decode(parts.join('='));
if (key && key === name) {
result = converted(cookie);
break;
}
if (!key) {
result[name] = converted(cookie);
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) !== undefined) {
$.cookie(key, '', $.extend(options, { expires: -1 }));
return true;
}
return false;
};
}));

View File

@ -0,0 +1,464 @@
/*! Modernizr 2.6.2 (Custom Build) | MIT & BSD
* Build: http://modernizr.com/download/#-video-shiv-cssclasses-addtest-testprop-testallprops-domprefixes-forms_placeholder-load
*/
;
window.Modernizr = (function( window, document, undefined ) {
var version = '2.6.2',
Modernizr = {},
enableClasses = true,
docElement = document.documentElement,
mod = 'modernizr',
modElem = document.createElement(mod),
mStyle = modElem.style,
inputElem = document.createElement('input') ,
smile = ':)',
toString = {}.toString, omPrefixes = 'Webkit Moz O ms',
cssomPrefixes = omPrefixes.split(' '),
domPrefixes = omPrefixes.toLowerCase().split(' '),
tests = {},
inputs = {},
attrs = {},
classes = [],
slice = classes.slice,
featureName,
_hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
hasOwnProp = function (object, property) {
return _hasOwnProperty.call(object, property);
};
}
else {
hasOwnProp = function (object, property) {
return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
};
}
if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) {
var target = this;
if (typeof target != "function") {
throw new TypeError();
}
var args = slice.call(arguments, 1),
bound = function () {
if (this instanceof bound) {
var F = function(){};
F.prototype = target.prototype;
var self = new F();
var result = target.apply(
self,
args.concat(slice.call(arguments))
);
if (Object(result) === result) {
return result;
}
return self;
} else {
return target.apply(
that,
args.concat(slice.call(arguments))
);
}
};
return bound;
};
}
function setCss( str ) {
mStyle.cssText = str;
}
function setCssAll( str1, str2 ) {
return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
}
function is( obj, type ) {
return typeof obj === type;
}
function contains( str, substr ) {
return !!~('' + str).indexOf(substr);
}
function testProps( props, prefixed ) {
for ( var i in props ) {
var prop = props[i];
if ( !contains(prop, "-") && mStyle[prop] !== undefined ) {
return prefixed == 'pfx' ? prop : true;
}
}
return false;
}
function testDOMProps( props, obj, elem ) {
for ( var i in props ) {
var item = obj[props[i]];
if ( item !== undefined) {
if (elem === false) return props[i];
if (is(item, 'function')){
return item.bind(elem || obj);
}
return item;
}
}
return false;
}
function testPropsAll( prop, prefixed, elem ) {
var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
if(is(prefixed, "string") || is(prefixed, "undefined")) {
return testProps(props, prefixed);
} else {
props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
return testDOMProps(props, prefixed, elem);
}
} tests['video'] = function() {
var elem = document.createElement('video'),
bool = false;
try {
if ( bool = !!elem.canPlayType ) {
bool = new Boolean(bool);
bool.ogg = elem.canPlayType('video/ogg; codecs="theora"') .replace(/^no$/,'');
bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,'');
bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,'');
}
} catch(e) { }
return bool;
};
function webforms() {
Modernizr['input'] = (function( props ) {
for ( var i = 0, len = props.length; i < len; i++ ) {
attrs[ props[i] ] = !!(props[i] in inputElem);
}
if (attrs.list){
attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);
}
return attrs;
})('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
Modernizr['inputtypes'] = (function(props) {
for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
inputElem.setAttribute('type', inputElemType = props[i]);
bool = inputElem.type !== 'text';
if ( bool ) {
inputElem.value = smile;
inputElem.style.cssText = 'position:absolute;visibility:hidden;';
if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {
docElement.appendChild(inputElem);
defaultView = document.defaultView;
bool = defaultView.getComputedStyle &&
defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
(inputElem.offsetHeight !== 0);
docElement.removeChild(inputElem);
} else if ( /^(search|tel)$/.test(inputElemType) ){
} else if ( /^(url|email)$/.test(inputElemType) ) {
bool = inputElem.checkValidity && inputElem.checkValidity() === false;
} else {
bool = inputElem.value != smile;
}
}
inputs[ props[i] ] = !!bool;
}
return inputs;
})('search tel url email datetime date month week time datetime-local number range color'.split(' '));
}
for ( var feature in tests ) {
if ( hasOwnProp(tests, feature) ) {
featureName = feature.toLowerCase();
Modernizr[featureName] = tests[feature]();
classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
}
}
Modernizr.input || webforms();
Modernizr.addTest = function ( feature, test ) {
if ( typeof feature == 'object' ) {
for ( var key in feature ) {
if ( hasOwnProp( feature, key ) ) {
Modernizr.addTest( key, feature[ key ] );
}
}
} else {
feature = feature.toLowerCase();
if ( Modernizr[feature] !== undefined ) {
return Modernizr;
}
test = typeof test == 'function' ? test() : test;
if (typeof enableClasses !== "undefined" && enableClasses) {
docElement.className += ' ' + (test ? '' : 'no-') + feature;
}
Modernizr[feature] = test;
}
return Modernizr;
};
setCss('');
modElem = inputElem = null;
;(function(window, document) {
var options = window.html5 || {};
var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
var saveClones = /^(?: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;
var supportsHtml5Styles;
var expando = '_html5shiv';
var expanID = 0;
var expandoData = {};
var supportsUnknownElements;
(function() {
try {
var a = document.createElement('a');
a.innerHTML = '<xyz></xyz>';
supportsHtml5Styles = ('hidden' in a);
supportsUnknownElements = a.childNodes.length == 1 || (function() {
(document.createElement)('a');
var frag = document.createDocumentFragment();
return (
typeof frag.cloneNode == 'undefined' ||
typeof frag.createDocumentFragment == 'undefined' ||
typeof frag.createElement == 'undefined'
);
}());
} catch(e) {
supportsHtml5Styles = true;
supportsUnknownElements = true;
}
}()); function addStyleSheet(ownerDocument, cssText) {
var p = ownerDocument.createElement('p'),
parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
p.innerHTML = 'x<style>' + cssText + '</style>';
return parent.insertBefore(p.lastChild, parent.firstChild);
}
function getElements() {
var elements = html5.elements;
return typeof elements == 'string' ? elements.split(' ') : elements;
}
function getExpandoData(ownerDocument) {
var data = expandoData[ownerDocument[expando]];
if (!data) {
data = {};
expanID++;
ownerDocument[expando] = expanID;
expandoData[expanID] = data;
}
return data;
}
function createElement(nodeName, ownerDocument, data){
if (!ownerDocument) {
ownerDocument = document;
}
if(supportsUnknownElements){
return ownerDocument.createElement(nodeName);
}
if (!data) {
data = getExpandoData(ownerDocument);
}
var node;
if (data.cache[nodeName]) {
node = data.cache[nodeName].cloneNode();
} else if (saveClones.test(nodeName)) {
node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
} else {
node = data.createElem(nodeName);
}
return node.canHaveChildren && !reSkip.test(nodeName) ? data.frag.appendChild(node) : node;
}
function createDocumentFragment(ownerDocument, data){
if (!ownerDocument) {
ownerDocument = document;
}
if(supportsUnknownElements){
return ownerDocument.createDocumentFragment();
}
data = data || getExpandoData(ownerDocument);
var clone = data.frag.cloneNode(),
i = 0,
elems = getElements(),
l = elems.length;
for(;i<l;i++){
clone.createElement(elems[i]);
}
return clone;
}
function shivMethods(ownerDocument, data) {
if (!data.cache) {
data.cache = {};
data.createElem = ownerDocument.createElement;
data.createFrag = ownerDocument.createDocumentFragment;
data.frag = data.createFrag();
}
ownerDocument.createElement = function(nodeName) {
if (!html5.shivMethods) {
return data.createElem(nodeName);
}
return createElement(nodeName, ownerDocument, data);
};
ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
'var n=f.cloneNode(),c=n.createElement;' +
'h.shivMethods&&(' +
getElements().join().replace(/\w+/g, function(nodeName) {
data.createElem(nodeName);
data.frag.createElement(nodeName);
return 'c("' + nodeName + '")';
}) +
');return n}'
)(html5, data.frag);
} function shivDocument(ownerDocument) {
if (!ownerDocument) {
ownerDocument = document;
}
var data = getExpandoData(ownerDocument);
if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
data.hasCSS = !!addStyleSheet(ownerDocument,
'article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}' +
'mark{background:#FF0;color:#000}'
);
}
if (!supportsUnknownElements) {
shivMethods(ownerDocument, data);
}
return ownerDocument;
} var html5 = {
'elements': options.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': (options.shivCSS !== false),
'supportsUnknownElements': supportsUnknownElements,
'shivMethods': (options.shivMethods !== false),
'type': 'default',
'shivDocument': shivDocument,
createElement: createElement,
createDocumentFragment: createDocumentFragment
}; window.html5 = html5;
shivDocument(document);
}(this, document));
Modernizr._version = version;
Modernizr._domPrefixes = domPrefixes;
Modernizr._cssomPrefixes = cssomPrefixes;
Modernizr.testProp = function(prop){
return testProps([prop]);
};
Modernizr.testAllProps = testPropsAll;
docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +
(enableClasses ? ' js ' + classes.join(' ') : '');
return Modernizr;
})(this, this.document);
/*yepnope1.5.4|WTFPL*/
(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));};
// testing for placeholder attribute in inputs and textareas
// re-using Modernizr.input if available
Modernizr.addTest('placeholder', function(){
return !!( 'placeholder' in ( Modernizr.input || document.createElement('input') ) &&
'placeholder' in ( Modernizr.textarea || document.createElement('textarea') )
);
});
;

View File

@ -0,0 +1,107 @@
var octopress = (function(){
return {
addMobileNav: function () {
var mainNav = $('ul.main-navigation, ul[role=main-navigation]').before('<fieldset class="mobile-nav">')
var mobileNav = $('fieldset.mobile-nav').append('<select>');
mobileNav.find('select').append('<option value="">Navigate&hellip;</option>');
var addOption = function() {
mobileNav.find('select').append('<option value="' + this.href + '">&raquo; ' + $(this).text() + '</option>');
}
mainNav.find('a').each(addOption);
$('ul.subscription a').each(addOption);
mobileNav.find('select').bind('change', function(event) {
if (event.target.value) { window.location.href = event.target.value; }
});
}
, addSidebarToggler: function () {
if(!$('body').hasClass('sidebar-footer')) {
$('#content').append('<span class="toggle-sidebar"></span>');
$('.toggle-sidebar').bind('click', function(e) {
e.preventDefault();
if ($('body').hasClass('collapse-sidebar')) {
$('body').removeClass('collapse-sidebar');
} else {
$('body').addClass('collapse-sidebar');
}
});
}
var sections = $('.sidebar section');
if (sections.length > 1) {
sections.each(function(index){
if ((sections.length >= 3) && index % 3 === 0) {
$(this).addClass("first");
}
var count = ((index +1) % 2) ? "odd" : "even";
$(this).addClass(count);
});
}
if (sections.length >= 3){ $('aside.sidebar').addClass('thirds'); }
}
, testFeature: function (features) {
getTestClasses = function (tests) {
classes = '';
if (typeof(tests.join) == 'function') {
for (var i=0; i < features.length; i++)
classes += getClass(features[i]) + ' ';
} else {
classes = getClass(tests);
}
return classes;
}
getClass = function (test) {
return ((Modernizr.testAllProps(test) ? test : "no-"+test).toLowerCase())
}
$('html').addClass(getTestClasses(features));
}
, flashVideoFallback: function (){
var flashplayerlocation = "/assets/jwplayer/player.swf",
flashplayerskin = "/assets/jwplayer/glow/glow.xml";
$('video').each(function(video){
video = $(video);
if (!Modernizr.video.h264 && swfobject.getFlashPlayerVersion() || window.location.hash.indexOf("flash-test") !== -1){
video.children('source[src$=mp4]').first().map(function(source){
var src = $(source).attr('src'),
id = 'video_'+Math.round(1 + Math.random()*(100000)),
width = video.attr('width'),
height = parseInt(video.attr('height'), 10) + 30;
video.after('<div class="flash-video"><div><div id='+id+'>');
swfobject.embedSWF(flashplayerlocation, id, width, height + 30, "9.0.0",
{ file : src, image : video.attr('poster'), skin : flashplayerskin } ,
{ movie : src, wmode : "opaque", allowfullscreen : "true" }
);
});
video.remove();
}
});
}
, wrapFlashVideos: function () {
$('object').each(function(object) {
object = $(object);
if ( $('param[name=movie]', object).length ) {
var wrapper = object.before('<div class="flash-video"><div>').previous();
$(wrapper).children().append(object);
}
});
$('iframe[src*=vimeo],iframe[src*=youtube]').each(function(iframe) {
iframe = $(iframe);
var wrapper = iframe.before('<div class="flash-video"><div>').previous();
$(wrapper).children().append(iframe);
});
}
}
})();
$(document).ready(function() {
octopress.wrapFlashVideos();
octopress.testFeature(['maskImage', 'transform']);
octopress.flashVideoFallback();
octopress.addCodeLineNumbers();
octopress.addMobileNav();
octopress.addSidebarToggler();
});

View File

@ -1,5 +1,4 @@
/*! SWFObject v2.2 modified by Brandon Mathis to contain only what is necessary to dynamically embed flash objects /*! SWFObject v2.2 modified by Brandon Mathis to contain only what is necessary to dynamically embed flash objects
* Uncompressed source in javascripts/libs/swfobject-dynamic.js
* <http://code.google.com/p/swfobject/> * <http://code.google.com/p/swfobject/>
released under the MIT License <http://www.opensource.org/licenses/mit-license.php> released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/ */

View File

@ -0,0 +1,42 @@
# Pinboard fetcher for Octopress (c) Brandon Mathis // MIT License
helpers = require('helpers')
Pinboard =
cookie: 'pinboard-feed'
classname: 'pinboard-feed'
template: (data) ->
helpers.linkFeed data, @classname
errorTemplate: ->
helpers.errorTemplate "Failed to load pins.", @classname
format: (feed) ->
for item in feed
{
url: item.u
date: item.dt
title: item.d
text: item.n
meta: "Tagged: " + ("<a title='view items #{item.a} tagged #{tag}' href='http://pinboard.in/u:#{item.a}/t:#{tag}'>#{tag}</a>" for tag in item.t).join ' '
}
init: (user, options, callback) ->
feed = $.cookie @cookie
if feed
callback @template JSON.parse(feed)
else
url = "http://feeds.pinboard.in/json/v1/u:#{user}"
url += "&count=#{options.count}"
$.ajax
url: url
dataType: 'jsonp'
jsonp: 'cb'
error: (err) => callback @errorTemplate
success: (data) =>
data = @format data
$.cookie @cookie, JSON.stringify data, { path: '/' }
callback @template data
module.exports = Pinboard

View File

@ -0,0 +1,12 @@
twitter = require('twitter')
adn = require('adn')
pinboard = require('pinboard')
delicious = require('delicious')
github = require('github')
Site =
init: ->
adn.init 'imathis', { count: 3 }, (posts)->
console.log posts
module.exports = Site

View File

@ -0,0 +1,74 @@
# Twitter fetcher for Octopress (c) Brandon Mathis // MIT License
helpers = require('helpers')
Twitter =
timeline: []
cookie: 'twitter-feed'
classname: 'twitter-feed'
template: ->
helpers.statusFeed @timeline, @classname
errorTemplate: ->
helpers.errorTemplate "Failed to load tweets.", @classname
parseHtml: (text, urls)->
# Use twitter entities to replace t.co shortened urls with expanded ones.
for url in urls
text = text.replace new RegExp(url.url, 'g'), "<a href='#{url.expanded_url}'>#{url.display_url}</a>" if url.expanded_url
# Trim up and link urls which aren't included in Twitters url entities
text = text.replace /([^'"])(https?:\/\/)([\w\-:;?&=+.%#\/]+)/gi, (p...) ->
"#{p[1]}<a href='#{p[2]+p[3]}'>#{helpers.trimUrl(p[3])}</a>"
# Link up user mentions and hashtags
text = helpers.linkify text, '@', 'http://twitter.com/'
text = helpers.linkify text, '#', 'http://search.twitter.com/search?q=%23'
getTweet: (tweet, user) ->
type = if tweet.retweeted_status then 'repost' else 'post'
user = if type is 'repost' then tweet.entities.user_mentions[0].screen_name else user
post = if type is 'repost' then tweet.retweeted_status else tweet
{
url: "https://twitter.com/#{user}/status/#{tweet.id_str}"
type: type
date: tweet.created_at
author: { user: user, url: "http://twitter.com/#{user}" } if type is 'repost'
text: @parseHtml tweet.text.replace(/\n/g, '<br>'), tweet.entities.urls
}
format: (tweets, user) ->
(@getTweet(tweet, user) for tweet in tweets)
init: (user, options, callback) ->
tweets = $.cookie('twitter-feed')
if tweets
@timeline = JSON.parse(tweets)
if @timeline.length isnt options.count
$.removeCookie('twitter-feed')
@timeline = []
@init user, options, callback
else
callback @template()
else
url = "http://api.twitter.com/1/statuses/user_timeline/#{user}.json?trim_user=true&include_entities=1"
url += "&exclude_replies=1" unless options.replies
url += "&include_rts=true" if options.include_rts
url += "&max_id=#{options.max_id}" if options.max_id
url += "&callback=?"
$.ajax
url: url
dataType: 'jsonp'
error: (err) => callback @errorTemplate
success: (data) =>
@timeline = @timeline.concat data
if @timeline.length < options.count
options.max_id = data.slice(-1)[0].id
@init user, options, callback
else
@timeline = @format @timeline.slice(0, options.count), user
$.cookie @cookie, JSON.stringify @timeline, { path: '/' }
callback @template()
module.exports = Twitter

View File

@ -21,7 +21,9 @@
{% capture canonical %}{{ site.url }}{% if site.permalink contains '.html' %}{{ page.url }}{% else %}{% if page.url contains site.category_dir %}/{% endif %}{{ page.url | remove:'index.html' }}{% endif %}{% endcapture %} {% capture canonical %}{{ site.url }}{% if site.permalink contains '.html' %}{{ page.url }}{% else %}{% if page.url contains site.category_dir %}/{% endif %}{{ page.url | remove:'index.html' }}{% endif %}{% endcapture %}
<link rel="canonical" href="{{ canonical }}"> <link rel="canonical" href="{{ canonical }}">
<link href="{{ root_url }}/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css"> <link href="{{ root_url }}/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<script src="{{ root_url }}/javascripts/octopress.min.js" type="text/javascript"></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="{{ root_url }}/javascripts/jquery.min.js"%3E%3C/script%3E'))</script>
<script src="{{ root_url }}{% javascript_assets_tag %}"></script>
<link href="{{ site.subscribe_rss }}" rel="alternate" title="{{site.title}}" type="application/atom+xml"> <link href="{{ site.subscribe_rss }}" rel="alternate" title="{{site.title}}" type="application/atom+xml">
{% include custom/head.html %} {% include custom/head.html %}
{% include google_analytics.html %} {% include google_analytics.html %}

View File

@ -1,9 +1,7 @@
{% if site.github_user %} {% if site.github_user %}
<section> <section>
<h1>On GitHub</h1> <h1>On GitHub</h1>
<ul id="gh_repos" data-user="{{site.github_user}}" data-count="{{site.github_repo_count}}" data-skip="{{site.github_skip_forks}}"> <div id="gh_repos" data-user="{{site.github_user}}" data-count="{{site.github_repo_count}}" data-skip="{{site.github_skip_forks}}" data-repos="{{site.github_repos | join:, }}"></div>
<li class="loading">Status updating...</li>
</ul>
{% if site.github_show_profile_link %} {% if site.github_show_profile_link %}
<a class="github-follow" href="https://github.com/{{site.github_user}}">Follow @{{site.github_user}}</a> <a class="github-follow" href="https://github.com/{{site.github_user}}">Follow @{{site.github_user}}</a>
{% endif %} {% endif %}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,305 +0,0 @@
var octopress = (function(){
return {
addMobileNav: function () {
var mainNav = $('ul.main-navigation, ul[role=main-navigation]').before('<fieldset class="mobile-nav">')
var mobileNav = $('fieldset.mobile-nav').append('<select>');
mobileNav.find('select').append('<option value="">Navigate&hellip;</option>');
var addOption = function() {
mobileNav.find('select').append('<option value="' + this.href + '">&raquo; ' + $(this).text() + '</option>');
}
mainNav.find('a').each(addOption);
$('ul.subscription a').each(addOption);
mobileNav.find('select').bind('change', function(event) {
if (event.target.value) { window.location.href = event.target.value; }
});
mobileNav.find('select').val('');
}
, addSidebarToggler: function () {
if(!$('body').hasClass('sidebar-footer')) {
$('#content').append('<span class="toggle-sidebar"></span>');
$('.toggle-sidebar').bind('click', function(e) {
e.preventDefault();
if ($('body').hasClass('collapse-sidebar')) {
$('body').removeClass('collapse-sidebar');
} else {
$('body').addClass('collapse-sidebar');
}
});
}
var sections = $('.sidebar section');
if (sections.length > 1) {
sections.each(function(index){
if ((sections.length >= 3) && index % 3 === 0) {
$(this).addClass("first");
}
var count = ((index +1) % 2) ? "odd" : "even";
$(this).addClass(count);
});
}
if (sections.length >= 3){ $('aside.sidebar').addClass('thirds'); }
}
, addCodeLineNumbers: function () {
if (navigator.appName === 'Microsoft Internet Explorer') { return; }
$('div.gist-highlight').each(function(index) {
var tableStart = '<table><tbody><tr><td class="gutter">',
lineNumbers = '<pre class="line-numbers">',
tableMiddle = '</pre></td><td class="code">',
tableEnd = '</td></tr></tbody></table>',
count = $('.line', this).length;
for (var i=1;i<=count; i++) {
lineNumbers += '<span class="line-number">'+i+'</span>\n';
}
var table = tableStart + lineNumbers + tableMiddle + '<pre>'+$('pre', this).html()+'</pre>' + tableEnd;
$(this).html(table);
});
}
, testFeature: function (features) {
getTestClasses = function (tests) {
classes = '';
if (typeof(tests.join) == 'function') {
for (var i=0; i < features.length; i++)
classes += getClass(features[i]) + ' ';
} else {
classes = getClass(tests);
}
return classes;
}
getClass = function (test) {
return ((Modernizr.testAllProps(test) ? test : "no-"+test).toLowerCase())
}
$('html').addClass(getTestClasses(features));
}
, flashVideoFallback: function (){
var flashplayerlocation = "/assets/jwplayer/player.swf",
flashplayerskin = "/assets/jwplayer/glow/glow.xml";
$('video').each(function(video){
video = $(video);
if (!Modernizr.video.h264 && swfobject.getFlashPlayerVersion() || window.location.hash.indexOf("flash-test") !== -1){
video.children('source[src$=mp4]').first().map(function(source){
var src = $(source).attr('src'),
id = 'video_'+Math.round(1 + Math.random()*(100000)),
width = video.attr('width'),
height = parseInt(video.attr('height'), 10) + 30;
video.after('<div class="flash-video"><div><div id='+id+'>');
swfobject.embedSWF(flashplayerlocation, id, width, height + 30, "9.0.0",
{ file : src, image : video.attr('poster'), skin : flashplayerskin } ,
{ movie : src, wmode : "opaque", allowfullscreen : "true" }
);
});
video.remove();
}
});
}
, wrapFlashVideos: function () {
$('object').each(function(object) {
object = $(object);
if ( $('param[name=movie]', object).length ) {
var wrapper = object.before('<div class="flash-video"><div>').previous();
$(wrapper).children().append(object);
}
});
$('iframe[src*=vimeo],iframe[src*=youtube]').each(function(iframe) {
iframe = $(iframe);
var wrapper = iframe.before('<div class="flash-video"><div>').previous();
$(wrapper).children().append(iframe);
});
}
/* Sky Slavin, Ludopoli. MIT license. * based on JavaScript Pretty Date * Copyright (c) 2008 John Resig (jquery.com) * Licensed under the MIT license. */
/* Updated considerably by Brandon Mathis */
, prettyDate: function (time) {
if (navigator.appName === 'Microsoft Internet Explorer') {
return "<span>&infin;</span>"; // because IE date parsing isn't fun.
}
var say = {
just_now: " now",
minute_ago: "1m",
minutes_ago: "m",
hour_ago: "1h",
hours_ago: "h",
yesterday: "1d",
days_ago: "d",
last_week: "1w",
weeks_ago: "w"
};
var current_date = new Date(),
current_date_time = current_date.getTime(),
current_date_full = current_date_time + (1 * 60000),
date = new Date(time),
diff = ((current_date_full - date.getTime()) / 1000),
day_diff = Math.floor(diff / 86400);
if (isNaN(day_diff) || day_diff < 0) { return "<span>&infin;</span>"; }
return day_diff === 0 && (
diff < 60 && say.just_now ||
diff < 120 && say.minute_ago ||
diff < 3600 && Math.floor(diff / 60) + say.minutes_ago ||
diff < 7200 && say.hour_ago ||
diff < 86400 && Math.floor(diff / 3600) + say.hours_ago) ||
day_diff === 1 && say.yesterday ||
day_diff < 7 && day_diff + say.days_ago ||
day_diff === 7 && say.last_week ||
day_diff > 7 && Math.ceil(day_diff / 7) + say.weeks_ago;
}
, renderDeliciousLinks: function (items) {
var output = "<ul>";
for (var i=0,l=items.length; i<l; i++) {
output += '<li><a href="' + items[i].u + '" title="Tags: ' + (items[i].t == "" ? "" : items[i].t.join(', ')) + '">' + items[i].d + '</a></li>';
}
output += "</ul>";
$('#delicious').html(output);
}
// Twitter fetcher for Octopress (c) Brandon Mathis // MIT License
, twitter: (function(){
function linkifyTweet(text, url) {
// Linkify urls, usernames, hashtags
text = text.replace(/(https?:\/\/)([\w\-:;?&=+.%#\/]+)/gi, '<a href="$1$2">$2</a>')
.replace(/(^|\W)@(\w+)/g, '$1<a href="https://twitter.com/$2">@$2</a>')
.replace(/(^|\W)#(\w+)/g, '$1<a href="https://search.twitter.com/search?q=%23$2">#$2</a>');
// Use twitter's api to replace t.co shortened urls with expanded ones.
for (var u in url) {
if(url[u].expanded_url != null){
var shortUrl = new RegExp(url[u].url, 'g');
text = text.replace(shortUrl, url[u].expanded_url);
var shortUrl = new RegExp(">"+(url[u].url.replace(/https?:\/\//, '')), 'g');
text = text.replace(shortUrl, ">"+url[u].display_url);
}
}
return text
}
function render(tweets, twitter_user) {
var timeline = document.getElementById('tweets'),
content = '';
for (var t in tweets) {
content += '<li>'+'<p>'+'<a href="https://twitter.com/'+twitter_user+'/status/'+tweets[t].id_str+'">'+octopress.prettyDate(tweets[t].created_at)+'</a>'+linkifyTweet(tweets[t].text.replace(/\n/g, '<br>'), tweets[t].entities.urls)+'</p>'+'</li>';
}
timeline.innerHTML = content;
}
return {
getFeed: function(target){
target = $(target);
if (target.length == 0) return;
var user = target.attr('data-user');
var count = parseInt(target.attr('data-count'), 10);
var replies = target.attr('data-replies') == 'true';
$.ajax({
url: "https://api.twitter.com/1/statuses/user_timeline/" + user + ".json?trim_user=true&count=" + (count + 20) + "&include_entities=1&exclude_replies=" + (replies ? "0" : "1") + "&callback=?"
, dataType: 'jsonp'
, error: function (err) { $('#tweets li.loading').addClass('error').text("Twitter's busted"); }
, success: function(data) { render(data.slice(0, count), user); }
});
}
}
})()
, github: (function(){
htmlEscape = function (str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
function render(target, data){
var i = 0, repos = '';
for(i = 0; i < data.length; i++) {
repos += '<li><a href="'+data[i].html_url+'">'+htmlEscape(data[i].name)+'</a><p>'+htmlEscape(data[i].description)+'</p></li>';
}
target.html(repos);
}
return {
showRepos: function(target){
target = $(target);
if (target.length == 0) return;
var user = target.attr('data-user')
var count = parseInt(target.attr('data-count'))
var skip_forks = target.attr('data-skip') == 'true';
$.ajax({
url: "https://api.github.com/users/"+user+"/repos?sort=pushed&callback=?"
, dataType: 'jsonp'
, error: function (err) { target.find('.loading').addClass('error').text("Error loading feed"); }
, success: function(data) {
var repos = [];
if (!data.data) { return; }
for (var i = 0; i < data.data.length; i++) {
if (skip_forks && data.data[i].fork) { continue; }
repos.push(data.data[i]);
}
if (count) { repos.splice(count); }
render(target, repos);
}
});
}
};
})()
}
})();
$(document).ready(function() {
octopress.wrapFlashVideos();
octopress.testFeature(['maskImage', 'transform']);
octopress.flashVideoFallback();
octopress.addCodeLineNumbers();
octopress.addMobileNav();
octopress.addSidebarToggler();
octopress.twitter.getFeed('#tweets')
octopress.github.showRepos('#gh_repos');
});
var htmlEncode = (function() {
var entities = {
'&' : '&amp;'
, '<' : '&lt;'
, '"' : '&quot;'
};
return function(value) {
return value.replace(/[&<"]/g, function(c) {
return entities[c];
});
};
})();
// iOS scaling bug fix
// Rewritten version
// By @mathias, @cheeaun and @jdalton
// Source url: https://gist.github.com/901295
(function(doc) {
var addEvent = 'addEventListener',
type = 'gesturestart',
qsa = 'querySelectorAll',
scales = [1, 1],
meta = qsa in doc ? doc[qsa]('meta[name=viewport]') : [];
function fix() {
meta.content = 'width=device-width,minimum-scale=' + scales[0] + ',maximum-scale=' + scales[1];
doc.removeEventListener(type, fix, true);
}
if ((meta = meta[meta.length - 1]) && addEvent in doc) {
fix();
scales = [0.25, 1.6];
doc[addEvent](type, fix, true);
}
}(document));

View File

@ -1,62 +0,0 @@
var pinboard = (function(){
function fetch(url) {
(function(){
var pinboardLinkroll = document.createElement('script');
pinboardLinkroll.type = 'text/javascript';
pinboardLinkroll.async = true;
pinboardLinkroll.src = url;
document.getElementsByTagName('head')[0].appendChild(pinboardLinkroll);
})();
}
function linkroll(element) {
var items;
this.set_items = function(i) {
this.items = i;
}
this.show_bmarks = function() {
var lines = [];
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i];
var str = this.format_item(item);
lines.push(str);
}
document.getElementById(element).innerHTML = lines.join("\n");
}
this.cook = function(v) {
return v.replace('<', '&lt;').replace('>', '&gt>');
}
this.format_item = function(it) {
var str = "<li class=\"pin-item\">";
if (!it.d) { return; }
str += "<p><a class=\"pin-title\" href=\"" + this.cook(it.u) + "\">" + this.cook(it.d) + "</a>";
if (it.n) {
str += "<span class=\"pin-description\">" + this.cook(it.n) + "</span>\n";
}
if (it.t.length > 0) {
for (var i = 0; i < it.t.length; i++) {
var tag = it.t[i];
str += " <a class=\"pin-tag\" href=\"//pinboard.in/u:"+ this.cook(it.a) + "/t:" + this.cook(tag) + "\">" + this.cook(tag).replace(/^\s+|\s+$/g, '') + "</a> ";
}
}
str += "</p></li>\n";
return str;
}
}
return {
getFeed: function(options) {
this.element = options.target;
fetch("https://feeds.pinboard.in/json/v1/u:"+options.user+"/?cb=pinboard.render\&count="+options.count);
},
render: function(data) {
var lr = new linkroll(this.element);
lr.set_items(data);
lr.show_bmarks();
}
}
})();

View File

@ -1,12 +0,0 @@
/*! SWFObject v2.2 modified by Brandon Mathis to contain only what is necessary to dynamically embed flash objects
* Uncompressed source in javascripts/lib/swfobject-dynamic.js
* <http://code.google.com/p/swfobject/>
released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
var swfobject=function(){function s(a,b,d){var q,k=n(d);if(g.wk&&g.wk<312)return q;if(k){if(typeof a.id==l)a.id=d;if(g.ie&&g.win){var e="",c;for(c in a)if(a[c]!=Object.prototype[c])c.toLowerCase()=="data"?b.movie=a[c]:c.toLowerCase()=="styleclass"?e+=' class="'+a[c]+'"':c.toLowerCase()!="classid"&&(e+=" "+c+'="'+a[c]+'"');c="";for(var f in b)b[f]!=Object.prototype[f]&&(c+='<param name="'+f+'" value="'+b[f]+'" />');k.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+e+">"+c+
"</object>";q=n(a.id)}else{f=i.createElement(o);f.setAttribute("type",m);for(var h in a)a[h]!=Object.prototype[h]&&(h.toLowerCase()=="styleclass"?f.setAttribute("class",a[h]):h.toLowerCase()!="classid"&&f.setAttribute(h,a[h]));for(e in b)b[e]!=Object.prototype[e]&&e.toLowerCase()!="movie"&&(a=f,c=e,h=b[e],d=i.createElement("param"),d.setAttribute("name",c),d.setAttribute("value",h),a.appendChild(d));k.parentNode.replaceChild(f,k);q=f}}return q}function n(a){var b=null;try{b=i.getElementById(a)}catch(d){}return b}
function t(a){var b=g.pv,a=a.split(".");a[0]=parseInt(a[0],10);a[1]=parseInt(a[1],10)||0;a[2]=parseInt(a[2],10)||0;return b[0]>a[0]||b[0]==a[0]&&b[1]>a[1]||b[0]==a[0]&&b[1]==a[1]&&b[2]>=a[2]?!0:!1}function u(a){return/[\\\"<>\.;]/.exec(a)!=null&&typeof encodeURIComponent!=l?encodeURIComponent(a):a}var l="undefined",o="object",m="application/x-shockwave-flash",v=window,i=document,j=navigator,g=function(){var a=typeof i.getElementById!=l&&typeof i.getElementsByTagName!=l&&typeof i.createElement!=l,
b=j.userAgent.toLowerCase(),d=j.platform.toLowerCase(),g=d?/win/.test(d):/win/.test(b),d=d?/mac/.test(d):/mac/.test(b),b=/webkit/.test(b)?parseFloat(b.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):!1,k=!+"\u000b1",e=[0,0,0],c=null;if(typeof j.plugins!=l&&typeof j.plugins["Shockwave Flash"]==o){if((c=j.plugins["Shockwave Flash"].description)&&!(typeof j.mimeTypes!=l&&j.mimeTypes[m]&&!j.mimeTypes[m].enabledPlugin))k=!1,c=c.replace(/^.*\s+(\S+\s+\S+$)/,"$1"),e[0]=parseInt(c.replace(/^(.*)\..*$/,"$1"),
10),e[1]=parseInt(c.replace(/^.*\.(.*)\s.*$/,"$1"),10),e[2]=/[a-zA-Z]/.test(c)?parseInt(c.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}else if(typeof v.ActiveXObject!=l)try{var f=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");if(f&&(c=f.GetVariable("$version")))k=!0,c=c.split(" ")[1].split(","),e=[parseInt(c[0],10),parseInt(c[1],10),parseInt(c[2],10)]}catch(h){}return{w3:a,pv:e,wk:b,ie:k,win:g,mac:d}}();return{embedSWF:function(a,b,d,i,k,e,c,f,h){var j={success:!1,id:b};if(g.w3&&!(g.wk&&g.wk<312)&&
a&&b&&d&&i&&k){d+="";i+="";var p={};if(f&&typeof f===o)for(var m in f)p[m]=f[m];p.data=a;p.width=d;p.height=i;a={};if(c&&typeof c===o)for(var n in c)a[n]=c[n];if(e&&typeof e===o)for(var r in e)typeof a.flashvars!=l?a.flashvars+="&"+r+"="+e[r]:a.flashvars=r+"="+e[r];if(t(k))b=s(p,a,b),j.success=!0,j.ref=b}h&&h(j)},ua:g,getFlashPlayerVersion:function(){return{major:g.pv[0],minor:g.pv[1],release:g.pv[2]}},hasFlashPlayerVersion:t,createSWF:function(a,b,d){if(g.w3)return s(a,b,d)},getQueryParamValue:function(a){var b=
i.location.search||i.location.hash;if(b){/\?/.test(b)&&(b=b.split("?")[1]);if(a==null)return u(b);for(var b=b.split("&"),d=0;d<b.length;d++)if(b[d].substring(0,b[d].indexOf("="))==a)return u(b[d].substring(b[d].indexOf("=")+1))}return""}}}();

File diff suppressed because one or more lines are too long

14
Gemfile
View File

@ -10,12 +10,20 @@ group :development do
gem 'haml', '~> 3.1.7' gem 'haml', '~> 3.1.7'
gem 'compass', '~> 0.12.2' gem 'compass', '~> 0.12.2'
gem 'rubypants', '~> 0.2.0' gem 'rubypants', '~> 0.2.0'
gem 'rb-fsevent', '~> 0.9.3'
gem 'stringex', '~> 1.4.0' gem 'stringex', '~> 1.4.0'
gem 'liquid', '~> 2.3.0' gem 'liquid', '~> 2.3.0'
gem 'tzinfo', '~> 0.3.35' gem 'tzinfo', '~> 0.3.35'
gem 'rake-minify' gem 'stitch-rb'
gem 'rdiscount', '~> 1.6.8' gem 'uglifier'
# Guard related
gem 'guard'
gem 'guard-shell'
gem 'guard-compass'
gem 'guard-coffeescript'
gem 'rb-inotify', :require => false
gem 'rb-fsevent', :require => false
gem 'rb-fchange', :require => false
end end
group :test do group :test do

View File

@ -5,13 +5,35 @@ GEM
chunky_png (1.2.6) chunky_png (1.2.6)
classifier (1.3.3) classifier (1.3.3)
fast-stemmer (>= 1.0.0) fast-stemmer (>= 1.0.0)
coderay (1.0.9)
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.4.0)
compass (0.12.2) compass (0.12.2)
chunky_png (~> 1.2) chunky_png (~> 1.2)
fssm (>= 0.2.7) fssm (>= 0.2.7)
sass (~> 3.1) sass (~> 3.1)
directory_watcher (1.4.1) directory_watcher (1.4.1)
execjs (1.4.0)
multi_json (~> 1.0)
fast-stemmer (1.0.1) fast-stemmer (1.0.1)
ffi (1.2.0)
fssm (0.2.9) fssm (0.2.9)
guard (1.6.2)
listen (>= 0.6.0)
lumberjack (>= 1.0.2)
pry (>= 0.9.10)
terminal-table (>= 1.4.3)
thor (>= 0.14.6)
guard-coffeescript (1.2.1)
coffee-script (>= 2.2.0)
guard (>= 1.1.0)
guard-compass (0.0.6)
compass (>= 0.10.5)
guard (>= 0.2.1)
guard-shell (0.5.1)
guard (>= 1.1.0)
haml (3.1.7) haml (3.1.7)
jekyll (0.12.0) jekyll (0.12.0)
classifier (~> 1.3) classifier (~> 1.3)
@ -20,25 +42,32 @@ GEM
liquid (~> 2.3) liquid (~> 2.3)
maruku (~> 0.5) maruku (~> 0.5)
pygments.rb (~> 0.3.2) pygments.rb (~> 0.3.2)
jsmin (1.0.1)
kramdown (0.13.8) kramdown (0.13.8)
liquid (2.3.0) liquid (2.3.0)
listen (0.7.3)
lumberjack (1.0.2)
maruku (0.6.1) maruku (0.6.1)
syntax (>= 1.0.0) syntax (>= 1.0.0)
method_source (0.8.1)
minitest (4.6.2) minitest (4.6.2)
multi_json (1.5.0)
posix-spawn (0.3.6) posix-spawn (0.3.6)
pygments.rb (0.3.4) pry (0.9.12)
coderay (~> 1.0.5)
method_source (~> 0.8)
slop (~> 3.4)
pygments.rb (0.3.7)
posix-spawn (~> 0.3.6) posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.1.0) yajl-ruby (~> 1.1.0)
rack (1.5.2) rack (1.5.2)
rack-protection (1.3.2) rack-protection (1.3.2)
rack rack
rake (10.0.3) rake (10.0.3)
rake-minify (0.4.0) rb-fchange (0.0.6)
jsmin (~> 1.0.1) ffi
rake (>= 0.8.7)
rb-fsevent (0.9.3) rb-fsevent (0.9.3)
rdiscount (1.6.8) rb-inotify (0.8.8)
ffi (>= 0.5.0)
redcarpet (2.2.2) redcarpet (2.2.2)
rubypants (0.2.0) rubypants (0.2.0)
sass (3.2.4) sass (3.2.4)
@ -46,10 +75,17 @@ GEM
rack (~> 1.3, >= 1.3.6) rack (~> 1.3, >= 1.3.6)
rack-protection (~> 1.2) rack-protection (~> 1.2)
tilt (~> 1.3, >= 1.3.3) tilt (~> 1.3, >= 1.3.3)
slop (3.4.3)
stitch-rb (0.0.8)
stringex (1.4.0) stringex (1.4.0)
syntax (1.0.0) syntax (1.0.0)
terminal-table (1.4.5)
thor (0.17.0)
tilt (1.3.3) tilt (1.3.3)
tzinfo (0.3.35) tzinfo (0.3.35)
uglifier (1.2.6)
execjs (>= 0.3.0)
multi_json (~> 1.3)
yajl-ruby (1.1.0) yajl-ruby (1.1.0)
PLATFORMS PLATFORMS
@ -58,6 +94,10 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
RedCloth (~> 4.2.9) RedCloth (~> 4.2.9)
compass (~> 0.12.2) compass (~> 0.12.2)
guard
guard-coffeescript
guard-compass
guard-shell
haml (~> 3.1.7) haml (~> 3.1.7)
jekyll (~> 0.12.0) jekyll (~> 0.12.0)
liquid (~> 2.3.0) liquid (~> 2.3.0)
@ -65,11 +105,13 @@ DEPENDENCIES
pygments.rb (~> 0.3.4) pygments.rb (~> 0.3.4)
rack (~> 1.5.0) rack (~> 1.5.0)
rake (~> 10.0.3) rake (~> 10.0.3)
rake-minify rb-fchange
rb-fsevent (~> 0.9.3) rb-fsevent
rdiscount (~> 1.6.8) rb-inotify
redcarpet (~> 2.2.2) redcarpet (~> 2.2.2)
rubypants (~> 0.2.0) rubypants (~> 0.2.0)
sinatra (~> 1.3.3) sinatra (~> 1.3.3)
stitch-rb
stringex (~> 1.4.0) stringex (~> 1.4.0)
tzinfo (~> 0.3.35) tzinfo (~> 0.3.35)
uglifier

38
Guardfile Normal file
View File

@ -0,0 +1,38 @@
$:.unshift File.expand_path("lib", File.dirname(__FILE__))
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
require 'octopress'
require 'guard/jekyll'
configurator = Octopress::Configuration.new
configuration = configurator.read_configuration
js_assets = Octopress::JSAssetsManager.new
stylesheets_dir = "assets/stylesheets"
javascripts_dir = "assets/javascripts"
guard :compass do
watch %r{^#{stylesheets_dir}/(.*)\.s[ac]ss$}
end
guard :jekyll do
# If a template file changes, trigger a Jekyll build
watch /^#{configuration[:source]}\/.+\.(md|markdown|textile|html|haml|slim|xml)/
end
guard :shell do
# If a non template file changes, copy it to destination
watch /^#{configuration[:source]}\/.+\.[^(md|markdown|textile|html|haml|slim|xml)]/ do |m|
if File.exists?(m.first)
file = File.basename(m.first)
path = m.first.sub /^#{configuration[:source]}/, "#{configuration[:destination]}"
FileUtils.mkdir_p path.sub /#{file}/,''
FileUtils.cp m.first, path
"Copied #{m.first} -> #{path}"
end
end
watch /^#{javascripts_dir}\/.+\.(js|coffee|mustache|eco|tmpl)/ do |change|
js_assets.compile
end
end

209
Rakefile
View File

@ -1,14 +1,14 @@
$:.unshift File.expand_path("lib", File.dirname(__FILE__)) # For use/testing when no gem is installed $:.unshift File.expand_path("lib", File.dirname(__FILE__)) # For use/testing when no gem is installed
require "rubygems" require 'rubygems'
require "bundler/setup" require 'bundler/setup'
require "stringex" require 'stringex'
require 'time' require 'time'
require 'tzinfo' require 'tzinfo'
require 'rake/minify'
require 'yaml' require 'yaml'
require 'octopress' require 'octopress'
require "rake/testtask" require 'rake/testtask'
require 'colors'
### Configuring Octopress: ### Configuring Octopress:
### Under _config/ you will find: ### Under _config/ you will find:
@ -24,18 +24,80 @@ full_stash_dir = "#{configuration[:source]}/#{configuration[:stash_dir]}"
desc "Initial setup for Octopress: copies the default theme into the path of Jekyll's generator. Rake install defaults to rake install[classic] to install a different theme run rake install[some_theme_name]" desc "Initial setup for Octopress: copies the default theme into the path of Jekyll's generator. Rake install defaults to rake install[classic] to install a different theme run rake install[some_theme_name]"
task :install, :theme do |t, args| task :install, :theme do |t, args|
if File.directory?(configuration[:source]) || File.directory?("sass") theme = args.theme || 'classic'
theme_configuration = configurator.read_theme_configuration(theme)
if File.directory?(theme_configuration[:source]) || File.directory?(theme_configuration[:theme][:javascripts_dir]) || File.directory?(theme_configuration[:theme][:stylesheets_dir])
abort("rake aborted!") if ask("A theme is already installed, proceeding will overwrite existing files. Are you sure?", ['y', 'n']) == 'n' abort("rake aborted!") if ask("A theme is already installed, proceeding will overwrite existing files. Are you sure?", ['y', 'n']) == 'n'
end end
# copy theme into working Jekyll directories # copy theme into working Jekyll directories
puts "## Installing "+theme+" theme"
Rake::Task["install_template"].invoke(theme)
Rake::Task["install_stylesheets"].invoke(theme)
Rake::Task["install_javascripts"].invoke(theme)
Rake::Task["install_configs"].invoke(theme)
mkdir_p 'site'
end
task :install_configs, :theme do |t, args|
theme = args.theme || 'classic' theme = args.theme || 'classic'
puts "## Copying "+theme+" theme into ./#{configuration[:source]} and ./sass" mkdir_p "_config"
mkdir_p configuration[:source] if File.directory? ".themes/#{theme}/_config"
cp_r "#{configuration[:themes_dir]}/#{theme}/source/.", configuration[:source] cp_r ".themes/#{theme}/_config/.", "_config/defaults", :remove_destination=>true
mkdir_p "sass" user_config_site = <<-EOF
cp_r "#{configuration[:themes_dir]}/#{theme}/sass/.", "sass" ---
mkdir_p "#{configuration[:source]}/#{configuration[:posts_dir]}" # --------------------------- #
mkdir_p configuration[:destination] # User Configuration #
# --------------------------- #
EOF
File.open('_config/site.yml', 'w') { |f| f.write user_config_site }
user_config_deploy = <<-EOF
---
# -------------------------- #
# Deployment Config #
# -------------------------- #
deploy_method: rsync
EOF
File.open('_config/deploy.yml', 'w') { |f| f.write user_config_deploy }
end
end
desc "Install stylesheets for a theme"
task :install_stylesheets, :theme do |t, args|
theme = args.theme || 'classic'
theme_configuration = configurator.read_theme_configuration(theme)
begin
stylesheets_dir = File.join(".themes/#{theme}", theme_configuration[:theme][:stylesheets_dir])
rescue
"The #{theme} theme must have a configuration file. This theme isn't compatable with Octopress 3.0 installation. You can probably still install it manually.".yellow
end
mkdir_p "assets/stylesheets"
if File.directory? stylesheets_dir
cp_r "#{stylesheets_dir}/.", "assets/stylesheets"
end
end
desc "Install javascript assets for a theme"
task :install_javascripts, :theme do |t, args|
theme = args.theme || 'classic'
theme_configuration = configurator.read_theme_configuration(theme)
begin
javascripts_dir = File.join(".themes/#{theme}", theme_configuration[:theme][:javascripts_dir])
rescue
"The #{theme} theme must have a configuration file. This theme isn't compatable with Octopress 3.0 installation. You can probably still install it manually.".yellow
end
mkdir_p "assets/javascripts"
if File.directory? javascripts_dir
cp_r "#{javascripts_dir}/.", "assets/javascripts"
end
end
task :install_template, :theme do |t, args|
theme = args.theme || 'classic'
mkdir_p "source/_posts"
cp_r ".themes/#{theme}/source/.", 'source'
end end
####################### #######################
@ -43,38 +105,17 @@ end
####################### #######################
desc "Generate jekyll site" desc "Generate jekyll site"
task :generate, :no_future do |t, args| task :generate do
future = args.no_future raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." if configuration[:source].nil? || !File.directory?(configuration[:source])
raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(configuration[:source])
configurator.write_configs_for_generation configurator.write_configs_for_generation
puts "## Generating Site with Jekyll" puts "## Generating Site with Jekyll"
system "compass compile --css-dir #{configuration[:source]}/stylesheets" system "compass compile --css-dir #{configuration[:source]}/stylesheets"
Rake::Task['minify_and_combine'].execute system "jekyll --no-server --no-auto #{'--no-future' if Octopress.env == 'production'}"
system "jekyll --no-server --no-auto #{'--no-future' if future.nil?}" unpublished = get_unpublished(Dir.glob("#{configuration[:source]}/#{configuration[:posts_dir]}/*.*"), {env: Octopress.env, message: "\nThese posts were not generated:"})
unpublished = get_unpublished(Dir.glob("#{configuration[:source]}/#{configuration[:posts_dir]}/*.*"), {no_future: future.nil?, message: "\nThese posts were not generated:"})
puts unpublished unless unpublished.empty? puts unpublished unless unpublished.empty?
configurator.remove_configs_for_generation configurator.remove_configs_for_generation
end end
Rake::Minify.new(:minify_and_combine) do
files = FileList.new("#{configuration[:source]}/javascripts/group/*.*")
output_file = "#{configuration[:source]}/javascripts/octopress.min.js"
puts "BEGIN Minifying #{output_file}"
group(output_file) do
files.each do |filename|
puts "Minifying- #{filename} into #{output_file}"
if filename.include? '.min.js'
add(filename, :minify => false)
else
add(filename)
end
end
end
puts "END Minifying #{output_file}"
end
# usage rake generate_only[my-post] # usage rake generate_only[my-post]
desc "Generate only the specified post (much faster)" desc "Generate only the specified post (much faster)"
task :generate_only, :filename do |t, args| task :generate_only, :filename do |t, args|
@ -90,42 +131,32 @@ task :generate_only, :filename do |t, args|
Rake::Task["integrate"].execute Rake::Task["integrate"].execute
end end
# - Check to see if site has been installed first rescue properly
desc "Watch the site and regenerate when it changes" desc "Watch the site and regenerate when it changes"
task :watch, :show_future do |t, args| task :watch do
future = args.show_future raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." if configuration[:source].nil? || !File.directory?(configuration[:source])
raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(configuration[:source])
configurator.write_configs_for_generation
puts "Starting to watch source with Jekyll and Compass." puts "Starting to watch source with Jekyll and Compass."
system "compass compile --css-dir #{configuration[:source]}/stylesheets" guardPid = Process.spawn("guard")
Rake::Task['minify_and_combine'].execute
jekyllPid = Process.spawn("jekyll --auto #{'--no-future' if future.nil?}")
compassPid = Process.spawn("compass watch")
trap("INT") { trap("INT") {
[jekyllPid, compassPid].each { |pid| Process.kill(9, pid) rescue Errno::ESRCH } Process.kill(9, guardPid) rescue Errno::ESRCH
configurator.remove_configs_for_generation
exit 0 exit 0
} }
[jekyllPid, compassPid].each { |pid| Process.wait(pid) } Process.wait guardPid
end end
desc "preview the site in a web browser." desc "preview the site in a web browser."
task :preview, :show_future do |t, args| task :preview do
future = args.show_future raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." if configuration[:source].nil? || !File.directory?(configuration[:source])
raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(configuration[:source]) guardPid = Process.spawn("guard")
configurator.write_configs_for_generation puts "Starting Rack, serving to http://#{configuration[:server_host]}:#{configuration[:server_port]}"
puts "Starting to watch source with Jekyll and Compass. Starting Rack, serving to http://#{configuration[:server_host]}:#{configuration[:server_port]}"
system "compass compile --css-dir #{configuration[:source]}/stylesheets"
jekyllPid = Process.spawn("jekyll --auto #{'--no-future' if future.nil?}")
compassPid = Process.spawn("compass watch")
rackupPid = Process.spawn("rackup --host #{configuration[:server_host]} --port #{configuration[:server_port]}") rackupPid = Process.spawn("rackup --host #{configuration[:server_host]} --port #{configuration[:server_port]}")
trap("INT") { trap("INT") {
[jekyllPid, compassPid, rackupPid].each { |pid| Process.kill(9, pid) rescue Errno::ESRCH } [guardPid, rackupPid].each { |pid| Process.kill(9, pid) rescue Errno::ESRCH }
configurator.remove_configs_for_generation
exit 0 exit 0
} }
[jekyllPid, compassPid, rackupPid].each { |pid| Process.wait(pid) } [guardPid, rackupPid].each { |pid| Process.wait(pid) }
end end
# usage rake new_post[my-new-post] or rake new_post['my new post'] or rake new_post (defaults to "new-post") # usage rake new_post[my-new-post] or rake new_post['my new post'] or rake new_post (defaults to "new-post")
@ -219,42 +250,60 @@ desc "Clean out caches: .pygments-cache, .gist-cache, .sass-cache, and Compass-g
task :clean do task :clean do
rm_rf [".pygments-cache", ".gist-cache"] rm_rf [".pygments-cache", ".gist-cache"]
system "compass clean" system "compass clean"
puts "## Cleaned Sass-generated files, and various caches ##" puts "## Cleaned Compass-generated files, and various caches ##"
end end
desc "Update theme source and style" desc "Update theme source and style"
task :update, :theme do |t, args| task :update, :theme do |t, args|
theme = args.theme || 'classic' theme = args.theme || 'classic'
Rake::Task[:update_source].invoke(theme) Rake::Task[:update_template].invoke(theme)
Rake::Task[:update_style].invoke(theme) Rake::Task[:update_stylesheets].invoke(theme)
Rake::Task[:update_javascripts].invoke(theme)
end end
desc "Move sass to sass.old, install sass theme updates, replace sass/custom with sass.old/custom" desc "Move stylesheets to stylesheets.old, install stylesheets theme updates, replace stylesheets/custom with stylesheets.old/custom"
task :update_style, :theme do |t, args| task :update_stylesheets, :theme do |t, args|
theme = args.theme || 'classic' theme = args.theme || 'classic'
if File.directory?("sass.old") if File.directory?("#{configuration[:assets]}.old/stylesheets")
puts "removed existing sass.old directory" rm_r "#{configuration[:assets]}.old/stylesheets", :secure=>true
rm_r "sass.old", :secure=>true puts "Removed existing assets.old/stylesheets directory"
end end
mv "sass", "sass.old" mkdir "#{configuration[:assets]}.old"
puts "## Moved styles into sass.old/" mv "#{configuration[:assets]}/stylesheets", "#{configuration[:assets]}.old/stylesheets"
cp_r "#{configuration[:themes_dir]}/"+theme+"/sass/", "sass" puts "Moved styles into #{configuration[:assets]}.old/stylesheets"
cp_r "sass.old/custom/.", "sass/custom" install_stylesheets(theme)
puts "## Updated Sass ##" cp_r "#{configuration[:assets]}.old/stylesheets/custom", "#{configuration[:assets]}/stylesheets/custom"
puts "## Updated Stylesheets ##"
rm_r ".sass-cache", :secure=>true if File.directory?(".sass-cache") rm_r ".sass-cache", :secure=>true if File.directory?(".sass-cache")
end end
desc "Move javascripts to #{configuration[:assets]}.old/javascripts, install javascripts theme updates."
task :update_javascripts, :theme do |t, args|
if File.directory?(".themes/#{theme}/javascripts")
theme = args.theme || 'classic'
if File.directory?("#{configuration[:assets]}.old/javascripts")
rm_r "#{configuration[:assets]}.old/javascripts", :secure=>true
puts "Removed existing assets.old/javascripts directory"
end
mkdir "#{configuration[:assets]}.old"
cp_r "#{configuration[:assets]}/javascripts/.", "#{configuration[:assets]}.old/javascripts"
puts "Copied styles into #{configuration[:assets]}.old/javascripts"
install_javascripts(theme)
puts "## Updated Javascripts ##"
end
end
desc "Move source to source.old, install source theme updates, replace source/_includes/navigation.html with source.old's navigation" desc "Move source to source.old, install source theme updates, replace source/_includes/navigation.html with source.old's navigation"
task :update_source, :theme do |t, args| task :update_template, :theme do |t, args|
theme = args.theme || 'classic' theme = args.theme || 'classic'
if File.directory?("#{configuration[:source]}.old") if File.directory?("#{configuration[:source]}.old")
puts "## Removed existing #{configuration[:source]}.old directory" puts "Removed existing #{configuration[:source]}.old directory"
rm_r "#{configuration[:source]}.old", :secure=>true rm_r "#{configuration[:source]}.old", :secure=>true
end end
mkdir "#{configuration[:source]}.old" mkdir "#{configuration[:source]}.old"
cp_r "#{configuration[:source]}/.", "#{configuration[:source]}.old" cp_r "#{configuration[:source]}/.", "#{configuration[:source]}.old"
puts "## Copied #{configuration[:source]} into #{configuration[:source]}.old/" puts "## Copied #{configuration[:source]} into #{configuration[:source]}.old/"
cp_r "#{configuration[:themes_dir]}/"+theme+"/source/.", configuration[:source], :remove_destination=>true cp_r ".themes/"+theme+"/source/.", configuration[:source], :remove_destination=>true
cp_r "#{configuration[:source]}.old/_includes/custom/.", "#{configuration[:source]}/_includes/custom/", :remove_destination=>true cp_r "#{configuration[:source]}.old/_includes/custom/.", "#{configuration[:source]}/_includes/custom/", :remove_destination=>true
mv "#{configuration[:source]}/index.html", "#{configuration[:blog_index_dir]}", :force=>true if configuration[:blog_index_dir] != configuration[:source] mv "#{configuration[:source]}/index.html", "#{configuration[:blog_index_dir]}", :force=>true if configuration[:blog_index_dir] != configuration[:source]
cp "#{configuration[:source]}.old/index.html", configuration[:source] if configuration[:blog_index_dir] != configuration[:source] && File.exists?("#{configuration[:source]}.old/index.html") cp "#{configuration[:source]}.old/index.html", configuration[:source] if configuration[:blog_index_dir] != configuration[:source] && File.exists?("#{configuration[:source]}.old/index.html")
@ -348,7 +397,7 @@ task :set_root_dir, :dir do |t, args|
dir = "/" + args.dir.sub(/(\/*)(.+)/, "\\2").sub(/\/$/, ''); dir = "/" + args.dir.sub(/(\/*)(.+)/, "\\2").sub(/\/$/, '');
end end
# update personal configuration # update personal configuration
site_configs = configurator.read_config('site.yml') site_configs = configurator.read_configuration('site.yml')
site_configs[:destination] = "public#{dir}" site_configs[:destination] = "public#{dir}"
site_configs[:subscribe_rss] = "#{dir}/atom.xml" site_configs[:subscribe_rss] = "#{dir}/atom.xml"
site_configs[:root] = "/#{dir.sub(/^\//, '')}" site_configs[:root] = "/#{dir.sub(/^\//, '')}"
@ -387,13 +436,13 @@ task :setup_github_pages, :repo do |t, args|
system "git remote rename origin octopress" system "git remote rename origin octopress"
if branch == 'master' if branch == 'master'
# If this is a user/organization pages repository, add the correct origin remote # If this is a user/organization pages repository, add the correct origin remote
# and checkout the source branch for committing changes to the blog source. # and checkout the source branch for committing changes to the website's source.
system "git remote add origin #{repo_url}" system "git remote add origin #{repo_url}"
puts "Added remote #{repo_url} as origin" puts "Added remote #{repo_url} as origin"
system "git config branch.master.remote origin" system "git config branch.master.remote origin"
puts "Set origin as default remote" puts "Set origin as default remote"
system "git branch -m master source" system "git branch -m master source"
puts "Master branch renamed to 'source' for committing your blog source files" puts "Master branch renamed to 'source' for committing your website's source files"
else else
unless !configuration[:destination].match("#{project}").nil? unless !configuration[:destination].match("#{project}").nil?
Rake::Task[:set_root_dir].invoke(project) Rake::Task[:set_root_dir].invoke(project)
@ -475,7 +524,7 @@ def get_unpublished(posts, options={})
file = File.read(post) file = File.read(post)
data = YAML.load file.match(/(^-{3}\n)(.+?)(\n-{3})/m)[2] data = YAML.load file.match(/(^-{3}\n)(.+?)(\n-{3})/m)[2]
if options[:no_future] if options[:env] == 'production'
future = Time.now < Time.parse(data['date'].to_s) ? "future date: #{data['date']}" : false future = Time.now < Time.parse(data['date'].to_s) ? "future date: #{data['date']}" : false
end end
draft = data['published'] == false ? 'published: false' : false draft = data['published'] == false ? 'published: false' : false

View File

@ -1,3 +0,0 @@
# Delicious link stream
delicious_user:
delicious_count: 3

View File

@ -1,16 +0,0 @@
---
# ------------------------ #
# RSync Config #
# deploy_default: rsync #
# ------------------------ #
# Be sure your public key is listed in your server's ~/.ssh/authorized_keys file
ssh_user: "user@domain.com"
ssh_port: "22"
document_root: "~/website.com/"
rsync_delete: false
rsync_args: "" # Any extra arguments to pass to rsync
deploy_dir: "_deploy"
# Hidden "dot" files that should be included with the deployed site (see task copydot)
copy_dot_files: []

View File

@ -1,10 +0,0 @@
---
# --------------------------------------- #
# Github Sidebar Repos Listing Plugin #
# --------------------------------------- #
# Github repositories
github_user:
github_repo_count: 0
github_show_profile_link: true
github_skip_forks: true

View File

@ -1,3 +0,0 @@
# Pinboard link stream
pinboard_user:
pinboard_count: 3

View File

@ -1,18 +0,0 @@
---
# --------------------------------- #
# Post/Page Share Configuration #
# --------------------------------- #
# Javascript social buttons often generate lots of http requests and may track your viewer's browsing history
# Show respect for privacy and bandwidth with simple links for Twitter, Facebook and Google Plus.
respectfully_social: true
# Facebook Like
facebook_like: false
# Google+
google_plus_one: false
google_plus_one_size: medium
# Twitter
twitter_tweet_button: true

View File

@ -1,10 +0,0 @@
---
# -------------------------------- #
# Tweet Sidebar Plugin Configs #
# -------------------------------- #
twitter_user:
twitter_tweet_count: 4
twitter_show_replies: false
twitter_follow_button: true
twitter_show_follower_count: false

View File

@ -1,6 +0,0 @@
---
# -------------------------- #
# User Deployment Config #
# -------------------------- #
deploy_default: rsync

View File

@ -1,5 +0,0 @@
---
# --------------------------- #
# User Site Configuration #
# --------------------------- #

View File

@ -6,17 +6,19 @@ config = Octopress::Configuration.new.read_configuration
project_path = File.dirname(__FILE__) project_path = File.dirname(__FILE__)
project_type = :stand_alone project_type = :stand_alone
compass_http_path = config[:destination].gsub('public', '') # Publishing paths
http_path = compass_http_path compass_http_path = config[:destination].gsub('public', '')
http_images_path = "#{compass_http_path}/images" http_path = compass_http_path
http_generated_images_path = "#{compass_http_path}/images" http_images_path = "#{http_path}/images"
http_fonts_path = "#{compass_http_path}/fonts" http_generated_images_path = "#{http_path}/images"
css_dir = "#{config[:destination]}/stylesheets" http_fonts_path = "#{http_path}/fonts"
css_dir = "#{config[:destination]}/stylesheets"
sass_dir = "sass" # Local development paths
images_dir = "#{config[:source]}/images" sass_dir = "assets/stylesheets"
fonts_dir = "#{config[:source]}/fonts" images_dir = "#{config[:source]}/images"
generated_images_dir = "#{config[:source]}/images" fonts_dir = "#{config[:source]}/fonts"
generated_images_dir = "#{config[:source]}/images"
line_comments = false line_comments = false
output_style = :compressed output_style = :compressed unless Octopress.env == 'development'

42
lib/colors.rb Normal file
View File

@ -0,0 +1,42 @@
class String
# colorization
def colorize(color_code)
"\e[#{color_code}m#{self}\e[0m"
end
def black
colorize(30)
end
def red
colorize(31)
end
def green
colorize(32)
end
def yellow
colorize(33)
end
def blue
colorize(34)
end
def magenta
colorize(35)
end
def cyan
colorize(36)
end
def white
colorize(37)
end
def bold
colorize(1)
end
end

35
lib/guard/jekyll.rb Normal file
View File

@ -0,0 +1,35 @@
require 'guard'
require 'guard/guard'
require 'guard/watcher'
$:.unshift File.expand_path("..", File.dirname(__FILE__))
require 'octopress'
module Guard
class Jekyll < Guard
VERSION = '0.0.1'
# Calls #run_all if the :all_on_start option is present.
def start
run_all if options[:all_on_start]
end
# Call #run_on_change for all files which match this guard.
def run_all
if Watcher.match_files(self, Dir.glob('{,**/}*{,.*}').uniq).size > 0
configurator = Octopress::Configuration.new
configurator.write_configs_for_generation
system "jekyll#{' --no-future' if Octopress.env == 'production'}"
configurator.remove_configs_for_generation
end
end
def run_on_changes(_)
configurator = Octopress::Configuration.new
configurator.write_configs_for_generation
system "jekyll#{' --no-future' if Octopress.env == 'production'}"
configurator.remove_configs_for_generation
end
end
end

View File

@ -1,12 +1,14 @@
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed $:.unshift File.expand_path(File.dirname(__FILE__)) # For use/testing when no gem is installed
require "octopress/core_ext" require "octopress/core_ext"
require "octopress/configuration" require "octopress/configuration"
require "octopress/js_asset_manager"
module Octopress module Octopress
# Static: Fetches the Octopress environment # Static: Fetches the Octopress environment
def self.env def self.env
ENV["OCTOPRESS_ENV"] || Configuration.read_config("defaults/jekyll.yml")[:env] configurator = Octopress::Configuration.new
ENV["OCTOPRESS_ENV"] || configurator.read_config("defaults/jekyll.yml")[:env]
end end
end end

View File

@ -13,6 +13,10 @@ module Octopress
File.absolute_path(File.join(self.config_directory, *subdirs)) File.absolute_path(File.join(self.config_directory, *subdirs))
end end
def theme_config_dir(theme, *subdirs)
File.absolute_path(File.join(File.dirname(__FILE__), '../', '../' '.themes', theme, '_config', *subdirs))
end
# Static: Reads the configuration of the specified file # Static: Reads the configuration of the specified file
# #
# path - the String path to the configuration file, relative to ./_config # path - the String path to the configuration file, relative to ./_config
@ -69,6 +73,20 @@ module Octopress
configs.to_symbol_keys configs.to_symbol_keys
end end
# Static: Reads all the theme's configuration files into one hash
#
# Returns a Hash of all the configuration files, with each key being a symbol
def read_theme_configuration(theme)
configs = {}
Dir.glob(self.theme_config_dir(theme, '**', '*.yml')) do |filename|
file_yaml = YAML.load(File.read(filename))
unless file_yaml.nil?
configs = file_yaml.deep_merge(configs)
end
end
configs.to_symbol_keys
end
# Static: Writes configuration files necessary for generation of the Jekyll site # Static: Writes configuration files necessary for generation of the Jekyll site
# #
# Returns a Hash of the items which were written to the Jekyll configuration file # Returns a Hash of the items which were written to the Jekyll configuration file

View File

@ -18,9 +18,23 @@ class Hash
target target
end end
def to_symbol_keys def to_symbol_keys
inject({}) { |memo,(k,v)| memo[k.to_sym] = v; memo } inject({}) do |memo,(k,v)|
if v.is_a? Hash
memo[k.to_sym] = v.to_symbol_keys
else
memo[k.to_sym] = v;
end
memo
end
end end
def to_string_keys def to_string_keys
inject({}) { |memo,(k,v)| memo[k.to_s] = v; memo } inject({}) do |memo,(k,v)|
if v.is_a? Hash
memo[k.to_s] = v.to_string_keys
else
memo[k.to_s] = v;
end
memo
end
end end
end end

View File

@ -0,0 +1,61 @@
$:.unshift File.expand_path(File.dirname(__FILE__)) # For use/testing when no gem is installed
require 'digest/md5'
require 'stitch-rb'
require 'uglifier'
require 'coffee-script'
module Octopress
class JSAssetsManager
attr_reader :config
def initialize
configurator = Octopress::Configuration.new
@configuration = configurator.read_configuration
@js_assets_path = File.expand_path("../../assets/javascripts", File.dirname(__FILE__))
# Read js dependencies from require_js.yml configuration
@dependencies = @configuration[:require_js][:dependencies].collect {|item| Dir.glob("#{@js_assets_path}/#{item}") }.flatten.uniq
@modules = @configuration[:require_js][:modules].collect {|item| Dir.glob("#{@js_assets_path}/#{item}") }.flatten.uniq
@template_path = File.expand_path("../../#{@configuration[:source]}", File.dirname(__FILE__))
@build_path = "/javascripts/build"
end
def get_fingerprint
Digest::MD5.hexdigest(@modules.concat(@dependencies).uniq.map! do |path|
"#{File.mtime(path).to_i}"
end.join)
end
def url
fingerprint = @fingerprint || get_fingerprint
Octopress.env == 'production' ? "#{@build_path}/all-#{fingerprint}.js" : "#{@build_path}/all.js"
end
def compile
@fingerprint = get_fingerprint
filename = url
file = "#{@template_path + filename}"
if File.exists?(file) and File.open(file) {|f| f.readline} =~ /#{@fingerprint}/
false
else
modules = @modules.delete_if { |f| @dependencies.include? f }
js = Stitch::Package.new(:dependencies => @dependencies, :paths => modules).compile
js = "/* Octopress fingerprint: #{@fingerprint} */\n" + js
js = Uglifier.new.compile js if Octopress.env == 'production'
write_path = "#{@template_path}/#{@build_path}"
(Dir["#{write_path}/*"]).each { |f| FileUtils.rm_rf(f) }
FileUtils.mkdir_p write_path
File.open(file, 'w') { |f| f.write js }
"Javascripts compiled to #{filename}."
end
end
end
end

29
plugins/config_tag.rb Normal file
View File

@ -0,0 +1,29 @@
$:.unshift File.expand_path("../lib", File.dirname(__FILE__)) # For use/testing when no gem is installed
require 'octopress'
require 'json'
class ConfigTag < Liquid::Tag
def initialize(tag_name, options, tokens)
super
config = Octopress::Configuration.read_configuration
options = options.split(',').map {|i| i.strip }
options.first.split('.').each { |k| config = config[k] } #reference objects with dot notation
@config = vars
@key = options.first.sub(/_/, '-').sub(/\./, '-')
@tag = (options.last || 'div')
end
def render(context)
tag = "<#{@tag} class='#{@key}'"
@config.each do |k,v|
unless v.nil?
v = v.join ',' if v.respond_to? 'join'
v = v.to_json if v.respond_to? 'keys'
tag += " data-#{k.sub'_','-'}='#{v}'"
end
end
tag += "></#{@tag}>"
end
end
Liquid::Template.register_tag('config_tag', ConfigTag)

16
plugins/js_assets.rb Normal file
View File

@ -0,0 +1,16 @@
$:.unshift File.expand_path("../lib", File.dirname(__FILE__)) # For use/testing when no gem is installed
require 'octopress'
class JavascriptAssets < Liquid::Tag
def initialize(tag_name, options, tokens)
super
end
def render(context)
js_assets = Octopress::JSAssetsManager.new
js_assets.compile
js_assets.url
end
end
Liquid::Template.register_tag('javascript_assets_tag', JavascriptAssets)

View File

@ -99,10 +99,7 @@ module OctopressLiquidFilters
# Removes trailing forward slash from a string for easily appending url segments # Removes trailing forward slash from a string for easily appending url segments
def strip_slash(input) def strip_slash(input)
if input =~ /(.+)\/$|^\/$/ input.sub(/\/\s*$/, '')
input = $1
end
input
end end
# Returns a url without the protocol (http://) # Returns a url without the protocol (http://)