Skip to content

Commit

Permalink
Page styling. Including word densities
Browse files Browse the repository at this point in the history
  • Loading branch information
gus committed Jan 25, 2010
1 parent e7d062d commit 3d296d9
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 9 deletions.
3 changes: 2 additions & 1 deletion app.rb
Expand Up @@ -21,7 +21,8 @@ class App < Sinatra::Base
page = Page.new("http://#{domain}/#{path}")
content_type 'application/json', :charset => 'utf-8'
{:title => page.title, :description => page.description, :keywords => page.keywords,
:single_word_density => page.single_word_density}.to_json
:single_word_density => page.single_word_density,
:double_word_density => page.double_word_density}.to_json
end
end # App
end
55 changes: 47 additions & 8 deletions public/javascripts/app/page.js
Expand Up @@ -3,15 +3,54 @@ Seota.Page = function(details) {
};

Seota.Page.prototype = $.extend({}, {
_labeled_tag: function(class, label, value) {
var template = "<div class='key_value {{ class }}'>" +
"<label>{{ label }}</label>" +
"<div class='value'>{{ value }}</div>" +
"</div>";
return $(Mustache.to_html(template, {"class" : class, "label" : label, "value" : value}));
},

_title_tag: function() {
return this._labeled_tag("title", "Title", this.details.title);
},

_description_tag: function() {
return this._labeled_tag("description", "Description", this.details.description);
},

_keywords_tag: function() {
return this._labeled_tag("keywords", "Keywords", this.details.keywords);
},

_word_density: function(class, label, collection) {
var words_div = $("<div class='grid_6'>").addClass(class);
words_div.append($("<label>").text(label));
for(word in collection) {
var density_div = $("<div class='density'>");
density_div.append($("<div class='grid_4 alpha'>").text(word));
density_div.append($("<div class='grid_2 omega'>").text(collection[word]));
words_div.append(density_div);
}
return words_div;
},

_single_word_density_tag: function() {
return this._word_density("single_word_density alpha", "Single Word Density",
this.details.single_word_density);
},

_double_word_density_tag: function() {
return this._word_density("double_word_density omega", "Double Word Density",
this.details.double_word_density);
},

render: function(container) {
container.empty();
container.append($("<div>").text(this.details.title));
// container.append($("<div>").text(this.details.description));
// container.append($("<div>").text(this.details.keywords));
var words_div = $("<div>");
for(word in this.details.single_word_density) {
words_div.append($("<div>").text(word + " => " + this.details.single_word_density[word]));
}
container.append(words_div);
container.append(this._title_tag());
container.append(this._description_tag());
container.append(this._keywords_tag());
container.append(this._single_word_density_tag());
container.append(this._double_word_density_tag());
}
});
261 changes: 261 additions & 0 deletions public/javascripts/mustache.js
@@ -0,0 +1,261 @@
/*
Shamless port of http://github.com/defunkt/mustache
by Jan Lehnardt <jan@apache.org>, Alexander Lang <alex@upstream-berlin.com>,
Sebastian Cohnen <sebastian.cohnen@googlemail.com>
Thanks @defunkt for the awesome code.
See http://github.com/defunkt/mustache for more info.
*/

var Mustache = function() {
var Renderer = function() {};

Renderer.prototype = {
otag: "{{",
ctag: "}}",
pragmas: {},

render: function(template, context, partials) {
// fail fast
if(template.indexOf(this.otag) == -1) {
return template;
}

template = this.render_pragmas(template);
var html = this.render_section(template, context, partials);
return this.render_tags(html, context, partials);
},

/*
Looks for %PRAGMAS
*/
render_pragmas: function(template) {
// no pragmas
if(template.indexOf(this.otag + "%") == -1) {
return template;
}

var that = this;
var regex = new RegExp(this.otag + "%(.+)" + this.ctag);
return template.replace(regex, function(match, pragma) {
that.pragmas[pragma] = true;
return "";
// ignore unknown pragmas silently
});
},

/*
Tries to find a partial in the global scope and render it
*/
render_partial: function(name, context, partials) {
if(typeof(context[name]) != "object") {
throw({message: "subcontext for '" + name + "' is not an object"});
}
if(!partials || !partials[name]) {
throw({message: "unknown_partial"});
}
return this.render(partials[name], context[name], partials);
},

/*
Renders boolean and enumerable sections
*/
render_section: function(template, context, partials) {
if(template.indexOf(this.otag + "#") == -1) {
return template;
}
var that = this;
// CSW - Added "+?" so it finds the tighest bound, not the widest
var regex = new RegExp(this.otag + "\\#(.+)" + this.ctag +
"\\s*([\\s\\S]+?)" + this.otag + "\\/\\1" + this.ctag + "\\s*", "mg");

// for each {{#foo}}{{/foo}} section do...
return template.replace(regex, function(match, name, content) {
var value = that.find(name, context);
if(that.is_array(value)) { // Enumerable, Let's loop!
return that.map(value, function(row) {
return that.render(content, that.merge(context,
that.create_context(row)), partials);
}).join('');
} else if(value) { // boolean section
return that.render(content, context, partials);
} else {
return "";
}
});
},

/*
Replace {{foo}} and friends with values from our view
*/
render_tags: function(template, context, partials) {
var lines = template.split("\n");

var new_regex = function() {
return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\/#]+?)\\1?" +
that.ctag + "+", "g");
};

// tit for tat
var that = this;

var regex = new_regex();
for (var i=0; i < lines.length; i++) {
lines[i] = lines[i].replace(regex, function (match,operator,name) {
switch(operator) {
case "!": // ignore comments
return match;
case "=": // set new delimiters, rebuild the replace regexp
that.set_delimiters(name);
regex = new_regex();
// redo the line in order to get tags with the new delimiters
// on the same line
i--;
return "";
case ">": // render partial
return that.render_partial(name, context, partials);
case "{": // the triple mustache is unescaped
return that.find(name, context);
return "";
default: // escape the value
return that.escape(that.find(name, context));
}
},this);
};
return lines.join("\n");
},

set_delimiters: function(delimiters) {
var dels = delimiters.split(" ");
this.otag = this.escape_regex(dels[0]);
this.ctag = this.escape_regex(dels[1]);
},

escape_regex: function(text) {
// thank you Simon Willison
if(!arguments.callee.sRE) {
var specials = [
'/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '\\'
];
arguments.callee.sRE = new RegExp(
'(\\' + specials.join('|\\') + ')', 'g'
);
}
return text.replace(arguments.callee.sRE, '\\$1');
},

/*
find `name` in current `context`. That is find me a value
from the view object
*/
find: function(name, context) {
name = this.trim(name);
if(typeof context[name] === "function") {
return context[name].apply(context);
}
if(context[name] !== undefined) {
return context[name];
}
// silently ignore unkown variables
return "";
},

// Utility methods

/*
Does away with nasty characters
*/
escape: function(s) {
return s.toString().replace(/[&"<>\\]/g, function(s) {
switch(s) {
case "&": return "&amp;";
case "\\": return "\\\\";;
case '"': return '\"';;
case "<": return "&lt;";
case ">": return "&gt;";
default: return s;
}
});
},

/*
Merges all properties of object `b` into object `a`.
`b.property` overwrites a.property`
*/
merge: function(a, b) {
var _new = {};
for(var name in a) {
if(a.hasOwnProperty(name)) {
_new[name] = a[name];
}
};
for(var name in b) {
if(b.hasOwnProperty(name)) {
_new[name] = b[name];
}
};
return _new;
},

// by @langalex, support for arrays of strings
create_context: function(_context) {
if(this.is_object(_context)) {
return _context;
} else if(this.pragmas["JSTACHE-ENABLE-STRING-ARRAYS"]) {
return {'.': _context};
}
},

is_object: function(a) {
return a && typeof a == 'object'
},

/*
Thanks Doug Crockford
JavaScript — The Good Parts lists an alternative that works better with
frames. Frames can suck it, we use the simple version.
*/
is_array: function(a) {
return (a &&
typeof a === 'object' &&
a.constructor === Array);
},

/*
Gets rid of leading and trailing whitespace
*/
trim: function(s) {
return s.replace(/^\s*|\s*$/g, '');
},

/*
Why, why, why? Because IE. Cry, cry cry.
*/
map: function(array, fn) {
if (typeof array.map == "function") {
return array.map(fn)
} else {
var r = [];
var l = array.length;
for(i=0;i<l;i++) {
r.push(fn(array[i]));
}
return r;
}
}
};

return({
name: "mustache.js",
version: "0.2",

/*
Turns a template and view into HTML
*/
to_html: function(template, view, partials) {
return new Renderer().render(template, view, partials);
}
});
}();
9 changes: 9 additions & 0 deletions public/stylesheets/site.css
@@ -0,0 +1,9 @@
#content #details .key_value {
padding-bottom: 1ex;
border-bottom: 1px dotted #cccccc;
}
#content #details label {
padding: 1ex 0;
display: block;
font-weight: bold;
}
10 changes: 10 additions & 0 deletions public/stylesheets/site.less
@@ -1,2 +1,12 @@
#header {
}

#content {
#details {
.key_value { padding-bottom: 1ex; border-bottom: 1px dotted #ccc; }
label { padding: 1ex 0; display: block; font-weight: bold; }
}
}

#footer {
}
2 changes: 2 additions & 0 deletions views/index.haml
Expand Up @@ -25,9 +25,11 @@
#footer.grid_16.alpha.omega
&copy;
= Time.now.year
\- Thumble Monks (Justin Knowlden)

%script{:src => "/javascripts/jquery-1.4.min.js"}
%script{:src => "/javascripts/sammy.js"}
%script{:src => "/javascripts/mustache.js"}
%script{:src => "/javascripts/app/seota.js"}
%script{:src => "/javascripts/app/sitemap.js"}
%script{:src => "/javascripts/app/page.js"}
Expand Down

0 comments on commit 3d296d9

Please sign in to comment.