Started implementing entry loading in client side.
- Bug fix in ts_entry:new/1. Msspelled ``atomic``. - Bug fix in ts_json:record_to_ejson/1. For ``ts_entry`` records, the Username and TimelineId elements were not being converted from atoms to list. - Added the entry template for loaded and created entry elements. - Added ICanHaz.js (which wraps mustache.js) and underscore.js. - Implemented a naive version of displayEntries() in ts.js. - Added debug alerts for error cases in ts.js. - Styling the new entry elements.
This commit is contained in:
parent
dfab257a12
commit
122a3bd1e3
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
start() ->
|
start() ->
|
||||||
ok = application:load(mnesia),
|
ok = application:load(mnesia),
|
||||||
ok = application:set_env(mnesia, dir, "/home/jdbernard/projects/timestamper/web-app/db/test"),
|
ok = application:set_env(mnesia, dir, "C:/Documents and Settings/jbernard/My Documents/projects/personal/timestamper/web-app/db/test"),
|
||||||
ok = mnesia:start(),
|
ok = mnesia:start(),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ create_table(TableOpts) ->
|
|||||||
{type, ordered_set}, {index, [timestamp]}]).
|
{type, ordered_set}, {index, [timestamp]}]).
|
||||||
|
|
||||||
new(ER = #ts_entry{}) ->
|
new(ER = #ts_entry{}) ->
|
||||||
{atmoic, NewRow} = mnesia:transaction(fun() ->
|
{atomic, NewRow} = mnesia:transaction(fun() ->
|
||||||
{Username, TimelineId, _} = ER#ts_entry.ref,
|
{Username, TimelineId, _} = ER#ts_entry.ref,
|
||||||
NextId = id_counter:next_counter(ts_entry_id),
|
NextId = id_counter:next_counter(ts_entry_id),
|
||||||
NewRow = ER#ts_entry{ref = {Username, TimelineId, NextId}},
|
NewRow = ER#ts_entry{ref = {Username, TimelineId, NextId}},
|
||||||
|
@ -32,8 +32,8 @@ record_to_ejson(Record=#ts_entry{}) ->
|
|||||||
|
|
||||||
% create the EJSON struct
|
% create the EJSON struct
|
||||||
{struct, [
|
{struct, [
|
||||||
{username, Username},
|
{username, atom_to_list(Username)},
|
||||||
{timeline_id, TimelineId},
|
{timeline_id, atom_to_list(TimelineId)},
|
||||||
{entry_id, EntryId},
|
{entry_id, EntryId},
|
||||||
{timestamp, encode_datetime(DateTime)},
|
{timestamp, encode_datetime(DateTime)},
|
||||||
{mark, Record#ts_entry.mark},
|
{mark, Record#ts_entry.mark},
|
||||||
|
@ -79,6 +79,17 @@ body {
|
|||||||
-webkit-border-bottom-left-radius: 0.5em;
|
-webkit-border-bottom-left-radius: 0.5em;
|
||||||
border-bottom-left-radius: 0.5em; }
|
border-bottom-left-radius: 0.5em; }
|
||||||
|
|
||||||
|
#more-entries a {
|
||||||
|
border: 1px solid #979681;
|
||||||
|
background: #f6f3ea;
|
||||||
|
display: block;
|
||||||
|
color: #626150;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: smaller;
|
||||||
|
float: left; }
|
||||||
|
#more-entries a:hover {
|
||||||
|
color: #b34c2b; }
|
||||||
|
|
||||||
.bar form {
|
.bar form {
|
||||||
border-top: solid 1px #979681;
|
border-top: solid 1px #979681;
|
||||||
padding: 0.5em 0 0.5em 2em;
|
padding: 0.5em 0 0.5em 2em;
|
||||||
@ -147,17 +158,44 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
float: left; }
|
float: left; }
|
||||||
|
|
||||||
#new-entry form {
|
#new-entry {
|
||||||
border: 0;
|
-moz-border-radius-bottomleft: 0.5em;
|
||||||
margin: 0;
|
-webkit-border-bottom-left-radius: 0.5em;
|
||||||
|
border-bottom-left-radius: 0.5em;
|
||||||
|
border-bottom-width: 0.2em; }
|
||||||
|
#new-entry form {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
#new-entry input {
|
||||||
|
color: #626150; }
|
||||||
|
#new-entry #add-notes {
|
||||||
|
display: none;
|
||||||
|
padding: 0.5em 0 0.5em 2em; }
|
||||||
|
#new-entry #new-entry-input {
|
||||||
|
margin-right: 1em; }
|
||||||
|
|
||||||
|
.entry-bar {
|
||||||
padding: 0; }
|
padding: 0; }
|
||||||
#new-entry input {
|
.entry-bar .id {
|
||||||
color: #626150; }
|
color: #b34c2b;
|
||||||
#new-entry #add-notes {
|
font-weight: bold;
|
||||||
display: none;
|
padding-left: 1.5em;
|
||||||
padding: 0.5em 0 0.5em 2em; }
|
display: inline-block;
|
||||||
#new-entry #new-entry-input {
|
margin: 0;
|
||||||
margin-right: 1em; }
|
background: #657a8b; }
|
||||||
|
.entry-bar .details {
|
||||||
|
color: #252d42;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #e6dec7;
|
||||||
|
border-color: #979681;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0.2em;
|
||||||
|
border-top-width: 0;
|
||||||
|
position: relative;
|
||||||
|
margin: 0; }
|
||||||
|
.entry-bar .details #entry-notes {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
#login-dialog {
|
#login-dialog {
|
||||||
font-size: small; }
|
font-size: small; }
|
||||||
|
@ -94,6 +94,20 @@ body {
|
|||||||
@include rounded2(bottom, left, 0.5em);
|
@include rounded2(bottom, left, 0.5em);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#more-entries a {
|
||||||
|
border: 1px solid $bbor;
|
||||||
|
background: lighten($bbg, 10%);
|
||||||
|
display: block;
|
||||||
|
color: $greyTxt;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: smaller;
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $obor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.bar form {
|
.bar form {
|
||||||
|
|
||||||
border-top: solid 1px $bbor;
|
border-top: solid 1px $bbor;
|
||||||
@ -191,6 +205,9 @@ body {
|
|||||||
|
|
||||||
#new-entry {
|
#new-entry {
|
||||||
|
|
||||||
|
@include rounded2(bottom, left, 0.5em);
|
||||||
|
border-bottom-width: $iBorWidth;
|
||||||
|
|
||||||
form {
|
form {
|
||||||
border: 0;
|
border: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -207,6 +224,37 @@ body {
|
|||||||
#new-entry-input { margin-right: 1em; }
|
#new-entry-input { margin-right: 1em; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.entry-bar {
|
||||||
|
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.id {
|
||||||
|
color: $obor;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-left: 1.5em;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
background: $ibg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
color: $txt;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: $bbg;
|
||||||
|
border-color: $bbor;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: $iBorWidth;
|
||||||
|
border-top-width: 0;
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
#entry-mark { }
|
||||||
|
#entry-notes {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#login-dialog {
|
#login-dialog {
|
||||||
font-size: small;
|
font-size: small;
|
||||||
|
|
||||||
|
401
www/js/ICanHaz.js
Normal file
401
www/js/ICanHaz.js
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
/*!
|
||||||
|
ICanHaz.js version 0.9 -- by @HenrikJoreteg
|
||||||
|
More info at: http://icanhazjs.com
|
||||||
|
*/
|
||||||
|
(function ($) {
|
||||||
|
/*!
|
||||||
|
mustache.js -- Logic-less templates in JavaScript
|
||||||
|
|
||||||
|
by @janl (MIT Licensed, https://github.com/janl/mustache.js/blob/master/LICENSE).
|
||||||
|
|
||||||
|
See http://mustache.github.com/ for more info.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Mustache = function() {
|
||||||
|
var Renderer = function() {};
|
||||||
|
|
||||||
|
Renderer.prototype = {
|
||||||
|
otag: "{{",
|
||||||
|
ctag: "}}",
|
||||||
|
pragmas: {},
|
||||||
|
buffer: [],
|
||||||
|
pragmas_implemented: {
|
||||||
|
"IMPLICIT-ITERATOR": true
|
||||||
|
},
|
||||||
|
context: {},
|
||||||
|
|
||||||
|
render: function(template, context, partials, in_recursion) {
|
||||||
|
// reset buffer & set context
|
||||||
|
if(!in_recursion) {
|
||||||
|
this.context = context;
|
||||||
|
this.buffer = []; // TODO: make this non-lazy
|
||||||
|
}
|
||||||
|
|
||||||
|
// fail fast
|
||||||
|
if(!this.includes("", template)) {
|
||||||
|
if(in_recursion) {
|
||||||
|
return template;
|
||||||
|
} else {
|
||||||
|
this.send(template);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template = this.render_pragmas(template);
|
||||||
|
var html = this.render_section(template, context, partials);
|
||||||
|
if(in_recursion) {
|
||||||
|
return this.render_tags(html, context, partials, in_recursion);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.render_tags(html, context, partials, in_recursion);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sends parsed lines
|
||||||
|
*/
|
||||||
|
send: function(line) {
|
||||||
|
if(line != "") {
|
||||||
|
this.buffer.push(line);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Looks for %PRAGMAS
|
||||||
|
*/
|
||||||
|
render_pragmas: function(template) {
|
||||||
|
// no pragmas
|
||||||
|
if(!this.includes("%", template)) {
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
|
||||||
|
this.ctag);
|
||||||
|
return template.replace(regex, function(match, pragma, options) {
|
||||||
|
if(!that.pragmas_implemented[pragma]) {
|
||||||
|
throw({message:
|
||||||
|
"This implementation of mustache doesn't understand the '" +
|
||||||
|
pragma + "' pragma"});
|
||||||
|
}
|
||||||
|
that.pragmas[pragma] = {};
|
||||||
|
if(options) {
|
||||||
|
var opts = options.split("=");
|
||||||
|
that.pragmas[pragma][opts[0]] = opts[1];
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
// ignore unknown pragmas silently
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Tries to find a partial in the curent scope and render it
|
||||||
|
*/
|
||||||
|
render_partial: function(name, context, partials) {
|
||||||
|
name = this.trim(name);
|
||||||
|
if(!partials || partials[name] === undefined) {
|
||||||
|
throw({message: "unknown_partial '" + name + "'"});
|
||||||
|
}
|
||||||
|
if(typeof(context[name]) != "object") {
|
||||||
|
return this.render(partials[name], context, partials, true);
|
||||||
|
}
|
||||||
|
return this.render(partials[name], context[name], partials, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Renders inverted (^) and normal (#) sections
|
||||||
|
*/
|
||||||
|
render_section: function(template, context, partials) {
|
||||||
|
if(!this.includes("#", template) && !this.includes("^", template)) {
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
// CSW - Added "+?" so it finds the tighest bound, not the widest
|
||||||
|
var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
|
||||||
|
"\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
|
||||||
|
"\\s*", "mg");
|
||||||
|
|
||||||
|
// for each {{#foo}}{{/foo}} section do...
|
||||||
|
return template.replace(regex, function(match, type, name, content) {
|
||||||
|
var value = that.find(name, context);
|
||||||
|
if(type == "^") { // inverted section
|
||||||
|
if(!value || that.is_array(value) && value.length === 0) {
|
||||||
|
// false or empty list, render it
|
||||||
|
return that.render(content, context, partials, true);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
} else if(type == "#") { // normal section
|
||||||
|
if(that.is_array(value)) { // Enumerable, Let's loop!
|
||||||
|
return that.map(value, function(row) {
|
||||||
|
return that.render(content, that.create_context(row),
|
||||||
|
partials, true);
|
||||||
|
}).join("");
|
||||||
|
} else if(that.is_object(value)) { // Object, Use it as subcontext!
|
||||||
|
return that.render(content, that.create_context(value),
|
||||||
|
partials, true);
|
||||||
|
} else if(typeof value === "function") {
|
||||||
|
// higher order section
|
||||||
|
return value.call(context, content, function(text) {
|
||||||
|
return that.render(text, context, partials, true);
|
||||||
|
});
|
||||||
|
} else if(value) { // boolean section
|
||||||
|
return that.render(content, context, partials, true);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Replace {{foo}} and friends with values from our view
|
||||||
|
*/
|
||||||
|
render_tags: function(template, context, partials, in_recursion) {
|
||||||
|
// tit for tat
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
var new_regex = function() {
|
||||||
|
return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
|
||||||
|
that.ctag + "+", "g");
|
||||||
|
};
|
||||||
|
|
||||||
|
var regex = new_regex();
|
||||||
|
var tag_replace_callback = function(match, operator, name) {
|
||||||
|
switch(operator) {
|
||||||
|
case "!": // ignore comments
|
||||||
|
return "";
|
||||||
|
case "=": // set new delimiters, rebuild the replace regexp
|
||||||
|
that.set_delimiters(name);
|
||||||
|
regex = new_regex();
|
||||||
|
return "";
|
||||||
|
case ">": // render partial
|
||||||
|
return that.render_partial(name, context, partials);
|
||||||
|
case "{": // the triple mustache is unescaped
|
||||||
|
return that.find(name, context);
|
||||||
|
default: // escape the value
|
||||||
|
return that.escape(that.find(name, context));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var lines = template.split("\n");
|
||||||
|
for(var i = 0; i < lines.length; i++) {
|
||||||
|
lines[i] = lines[i].replace(regex, tag_replace_callback, this);
|
||||||
|
if(!in_recursion) {
|
||||||
|
this.send(lines[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(in_recursion) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Checks whether a value is thruthy or false or 0
|
||||||
|
function is_kinda_truthy(bool) {
|
||||||
|
return bool === false || bool === 0 || bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value;
|
||||||
|
if(is_kinda_truthy(context[name])) {
|
||||||
|
value = context[name];
|
||||||
|
} else if(is_kinda_truthy(this.context[name])) {
|
||||||
|
value = this.context[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof value === "function") {
|
||||||
|
return value.apply(context);
|
||||||
|
}
|
||||||
|
if(value !== undefined) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
// silently ignore unkown variables
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
|
||||||
|
// Utility methods
|
||||||
|
|
||||||
|
/* includes tag */
|
||||||
|
includes: function(needle, haystack) {
|
||||||
|
return haystack.indexOf(this.otag + needle) != -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Does away with nasty characters
|
||||||
|
*/
|
||||||
|
escape: function(s) {
|
||||||
|
s = String(s === null ? "" : s);
|
||||||
|
return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
|
||||||
|
switch(s) {
|
||||||
|
case "&": return "&";
|
||||||
|
case "\\": return "\\\\";
|
||||||
|
case '"': return '\"';
|
||||||
|
case "<": return "<";
|
||||||
|
case ">": return ">";
|
||||||
|
default: return s;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// by @langalex, support for arrays of strings
|
||||||
|
create_context: function(_context) {
|
||||||
|
if(this.is_object(_context)) {
|
||||||
|
return _context;
|
||||||
|
} else {
|
||||||
|
var iterator = ".";
|
||||||
|
if(this.pragmas["IMPLICIT-ITERATOR"]) {
|
||||||
|
iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
|
||||||
|
}
|
||||||
|
var ctx = {};
|
||||||
|
ctx[iterator] = _context;
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
is_object: function(a) {
|
||||||
|
return a && typeof a == "object";
|
||||||
|
},
|
||||||
|
|
||||||
|
is_array: function(a) {
|
||||||
|
return Object.prototype.toString.call(a) === '[object 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(var i = 0; i < l; i++) {
|
||||||
|
r.push(fn(array[i]));
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return({
|
||||||
|
name: "mustache.js",
|
||||||
|
version: "0.3.0",
|
||||||
|
|
||||||
|
/*
|
||||||
|
Turns a template and view into HTML
|
||||||
|
*/
|
||||||
|
to_html: function(template, view, partials, send_fun) {
|
||||||
|
var renderer = new Renderer();
|
||||||
|
if(send_fun) {
|
||||||
|
renderer.send = send_fun;
|
||||||
|
}
|
||||||
|
renderer.render(template, view, partials);
|
||||||
|
if(!send_fun) {
|
||||||
|
return renderer.buffer.join("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}();/*!
|
||||||
|
ICanHaz.js -- by @HenrikJoreteg
|
||||||
|
*/
|
||||||
|
/*global jQuery */
|
||||||
|
function ICanHaz() {
|
||||||
|
var self = this;
|
||||||
|
self.VERSION = "0.9";
|
||||||
|
self.templates = {};
|
||||||
|
self.partials = {};
|
||||||
|
|
||||||
|
// public function for adding templates
|
||||||
|
// We're enforcing uniqueness to avoid accidental template overwrites.
|
||||||
|
// If you want a different template, it should have a different name.
|
||||||
|
self.addTemplate = function (name, templateString) {
|
||||||
|
if (self[name]) throw "Invalid name: " + name + ".";
|
||||||
|
if (self.templates[name]) throw "Template \" + name + \" exists";
|
||||||
|
|
||||||
|
self.templates[name] = templateString;
|
||||||
|
self[name] = function (data, raw) {
|
||||||
|
data = data || {};
|
||||||
|
var result = Mustache.to_html(self.templates[name], data, self.partials);
|
||||||
|
return raw ? result : $(result);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// public function for adding partials
|
||||||
|
self.addPartial = function (name, templateString) {
|
||||||
|
if (self.partials[name]) {
|
||||||
|
throw "Partial \" + name + \" exists";
|
||||||
|
} else {
|
||||||
|
self.partials[name] = templateString;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// grabs templates from the DOM and caches them.
|
||||||
|
// Loop through and add templates.
|
||||||
|
// Whitespace at beginning and end of all templates inside <script> tags will
|
||||||
|
// be trimmed. If you want whitespace around a partial, add it in the parent,
|
||||||
|
// not the partial. Or do it explicitly using <br/> or
|
||||||
|
self.grabTemplates = function () {
|
||||||
|
$('script[type="text/html"]').each(function (a, b) {
|
||||||
|
var script = $((typeof a === 'number') ? b : a), // Zepto doesn't bind this
|
||||||
|
text = (''.trim) ? script.html().trim() : $.trim(script.html());
|
||||||
|
|
||||||
|
self[script.hasClass('partial') ? 'addPartial' : 'addTemplate'](script.attr('id'), text);
|
||||||
|
script.remove();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// clears all retrieval functions and empties caches
|
||||||
|
self.clearAll = function () {
|
||||||
|
for (var key in self.templates) {
|
||||||
|
delete self[key];
|
||||||
|
}
|
||||||
|
self.templates = {};
|
||||||
|
self.partials = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
self.refresh = function () {
|
||||||
|
self.clearAll();
|
||||||
|
self.grabTemplates();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
window.ich = new ICanHaz();
|
||||||
|
|
||||||
|
// init itself on document ready
|
||||||
|
$(function () {
|
||||||
|
ich.grabTemplates();
|
||||||
|
});
|
||||||
|
})(window.jQuery || window.Zepto);
|
9
www/js/ICanHaz.min.js
vendored
Normal file
9
www/js/ICanHaz.min.js
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
(function(i){var n=function(){var f=function(){};f.prototype={otag:"{{",ctag:"}}",pragmas:{},buffer:[],pragmas_implemented:{"IMPLICIT-ITERATOR":true},context:{},render:function(a,b,c,d){if(!d){this.context=b;this.buffer=[]}if(!this.includes("",a))if(d)return a;else{this.send(a);return}a=this.render_pragmas(a);a=this.render_section(a,b,c);if(d)return this.render_tags(a,b,c,d);this.render_tags(a,b,c,d)},send:function(a){a!=""&&this.buffer.push(a)},render_pragmas:function(a){if(!this.includes("%",a))return a;
|
||||||
|
var b=this;return a.replace(RegExp(this.otag+"%([\\w-]+) ?([\\w]+=[\\w]+)?"+this.ctag),function(c,d,e){if(!b.pragmas_implemented[d])throw{message:"This implementation of mustache doesn't understand the '"+d+"' pragma"};b.pragmas[d]={};if(e){c=e.split("=");b.pragmas[d][c[0]]=c[1]}return""})},render_partial:function(a,b,c){a=this.trim(a);if(!c||c[a]===undefined)throw{message:"unknown_partial '"+a+"'"};if(typeof b[a]!="object")return this.render(c[a],b,c,true);return this.render(c[a],b[a],c,true)},render_section:function(a,
|
||||||
|
b,c){if(!this.includes("#",a)&&!this.includes("^",a))return a;var d=this;return a.replace(RegExp(this.otag+"(\\^|\\#)\\s*(.+)\\s*"+this.ctag+"\n*([\\s\\S]+?)"+this.otag+"\\/\\s*\\2\\s*"+this.ctag+"\\s*","mg"),function(e,j,k,h){e=d.find(k,b);if(j=="^")return!e||d.is_array(e)&&e.length===0?d.render(h,b,c,true):"";else if(j=="#")return d.is_array(e)?d.map(e,function(g){return d.render(h,d.create_context(g),c,true)}).join(""):d.is_object(e)?d.render(h,d.create_context(e),c,true):typeof e==="function"?
|
||||||
|
e.call(b,h,function(g){return d.render(g,b,c,true)}):e?d.render(h,b,c,true):""})},render_tags:function(a,b,c,d){var e=this,j=function(){return RegExp(e.otag+"(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?"+e.ctag+"+","g")},k=j(),h=function(o,m,l){switch(m){case "!":return"";case "=":e.set_delimiters(l);k=j();return"";case ">":return e.render_partial(l,b,c);case "{":return e.find(l,b);default:return e.escape(e.find(l,b))}};a=a.split("\n");for(var g=0;g<a.length;g++){a[g]=a[g].replace(k,h,this);d||this.send(a[g])}if(d)return a.join("\n")},
|
||||||
|
set_delimiters:function(a){a=a.split(" ");this.otag=this.escape_regex(a[0]);this.ctag=this.escape_regex(a[1])},escape_regex:function(a){if(!arguments.callee.sRE)arguments.callee.sRE=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\)","g");return a.replace(arguments.callee.sRE,"\\$1")},find:function(a,b){a=this.trim(a);var c;if(b[a]===false||b[a]===0||b[a])c=b[a];else if(this.context[a]===false||this.context[a]===0||this.context[a])c=this.context[a];if(typeof c==="function")return c.apply(b);
|
||||||
|
if(c!==undefined)return c;return""},includes:function(a,b){return b.indexOf(this.otag+a)!=-1},escape:function(a){a=String(a===null?"":a);return a.replace(/&(?!\w+;)|["<>\\]/g,function(b){switch(b){case "&":return"&";case "\\":return"\\\\";case '"':return'"';case "<":return"<";case ">":return">";default:return b}})},create_context:function(a){if(this.is_object(a))return a;else{var b=".";if(this.pragmas["IMPLICIT-ITERATOR"])b=this.pragmas["IMPLICIT-ITERATOR"].iterator;var c={};c[b]=a;return c}},
|
||||||
|
is_object:function(a){return a&&typeof a=="object"},is_array:function(a){return Object.prototype.toString.call(a)==="[object Array]"},trim:function(a){return a.replace(/^\s*|\s*$/g,"")},map:function(a,b){if(typeof a.map=="function")return a.map(b);else{for(var c=[],d=a.length,e=0;e<d;e++)c.push(b(a[e]));return c}}};return{name:"mustache.js",version:"0.3.0",to_html:function(a,b,c,d){var e=new f;if(d)e.send=d;e.render(a,b,c);if(!d)return e.buffer.join("\n")}}}();window.ich=new function(){var f=this;
|
||||||
|
f.VERSION="0.9";f.templates={};f.partials={};f.addTemplate=function(a,b){if(f[a])throw"Invalid name: "+a+".";if(f.templates[a])throw'Template " + name + " exists';f.templates[a]=b;f[a]=function(c,d){c=c||{};var e=n.to_html(f.templates[a],c,f.partials);return d?e:i(e)}};f.addPartial=function(a,b){if(f.partials[a])throw'Partial " + name + " exists';else f.partials[a]=b};f.grabTemplates=function(){i('script[type="text/html"]').each(function(a,b){var c=i(typeof a==="number"?b:a),d="".trim?c.html().trim():
|
||||||
|
i.trim(c.html());f[c.hasClass("partial")?"addPartial":"addTemplate"](c.attr("id"),d);c.remove()})};f.clearAll=function(){for(var a in f.templates)delete f[a];f.templates={};f.partials={}};f.refresh=function(){f.clearAll();f.grabTemplates()}};i(function(){ich.grabTemplates()})})(window.jQuery||window.Zepto);
|
18
www/js/ts.js
18
www/js/ts.js
@ -3,12 +3,12 @@ var timelines = [];
|
|||||||
var activeTimeline = [];
|
var activeTimeline = [];
|
||||||
var entries = [];
|
var entries = [];
|
||||||
|
|
||||||
var lastEntryBar;
|
var newEntryBar;
|
||||||
|
|
||||||
/* Setup after the document is ready for manipulation. */
|
/* Setup after the document is ready for manipulation. */
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
|
||||||
lastEntryBar = $("#last-entry");
|
newEntryBar = $("#new-entry");
|
||||||
|
|
||||||
// wire the login dialog using jQuery UI
|
// wire the login dialog using jQuery UI
|
||||||
$("#login-dialog").dialog({
|
$("#login-dialog").dialog({
|
||||||
@ -125,7 +125,7 @@ function loadEntries(user, timeline) {
|
|||||||
|
|
||||||
// call the API list_entries function via AJAX
|
// call the API list_entries function via AJAX
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/ts_api/entries/" + user.username + "/" + timeline.timeline_id,
|
url: "/ts_api/entries/" + user.username + "/" + timeline.timeline_id + "?order=desc",
|
||||||
type: "GET",
|
type: "GET",
|
||||||
|
|
||||||
success: function(data, textStatus, jqXHR) {
|
success: function(data, textStatus, jqXHR) {
|
||||||
@ -133,10 +133,22 @@ function loadEntries(user, timeline) {
|
|||||||
|
|
||||||
// push the entries onto the page
|
// push the entries onto the page
|
||||||
displayEntries(entries)
|
displayEntries(entries)
|
||||||
|
},
|
||||||
|
|
||||||
|
error: function(jqXHR, textStatus, error) {
|
||||||
|
alert(jqXHR.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Push the entries onto the top of the entry display. */
|
||||||
|
function displayEntries(entries) {
|
||||||
|
|
||||||
|
_.each(entries, function(entry) {
|
||||||
|
newEntryBar.after(ich.entry(entry));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/* Show/hide the editable user-info panel. */
|
/* Show/hide the editable user-info panel. */
|
||||||
function toggleUserInfo(event) {
|
function toggleUserInfo(event) {
|
||||||
$("#user-info").slideToggle("slow");
|
$("#user-info").slideToggle("slow");
|
||||||
|
25
www/js/underscore-min.js
vendored
Normal file
25
www/js/underscore-min.js
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Underscore.js 1.1.4
|
||||||
|
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
|
||||||
|
// Underscore is freely distributable under the MIT license.
|
||||||
|
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||||
|
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||||
|
// For all details and documentation:
|
||||||
|
// http://documentcloud.github.com/underscore
|
||||||
|
(function(){var q=this,C=q._,m={},j=Array.prototype,n=Object.prototype,i=j.slice,D=j.unshift,E=n.toString,o=n.hasOwnProperty,s=j.forEach,t=j.map,u=j.reduce,v=j.reduceRight,w=j.filter,x=j.every,y=j.some,p=j.indexOf,z=j.lastIndexOf;n=Array.isArray;var F=Object.keys,c=function(a){return new l(a)};if(typeof module!=="undefined"&&module.exports){module.exports=c;c._=c}else q._=c;c.VERSION="1.1.4";var k=c.each=c.forEach=function(a,b,d){if(a!=null)if(s&&a.forEach===s)a.forEach(b,d);else if(c.isNumber(a.length))for(var e=
|
||||||
|
0,f=a.length;e<f;e++){if(b.call(d,a[e],e,a)===m)break}else for(e in a)if(o.call(a,e))if(b.call(d,a[e],e,a)===m)break};c.map=function(a,b,d){var e=[];if(a==null)return e;if(t&&a.map===t)return a.map(b,d);k(a,function(f,g,h){e[e.length]=b.call(d,f,g,h)});return e};c.reduce=c.foldl=c.inject=function(a,b,d,e){var f=d!==void 0;if(a==null)a=[];if(u&&a.reduce===u){if(e)b=c.bind(b,e);return f?a.reduce(b,d):a.reduce(b)}k(a,function(g,h,G){if(!f&&h===0){d=g;f=true}else d=b.call(e,d,g,h,G)});if(!f)throw new TypeError("Reduce of empty array with no initial value");
|
||||||
|
return d};c.reduceRight=c.foldr=function(a,b,d,e){if(a==null)a=[];if(v&&a.reduceRight===v){if(e)b=c.bind(b,e);return d!==void 0?a.reduceRight(b,d):a.reduceRight(b)}a=(c.isArray(a)?a.slice():c.toArray(a)).reverse();return c.reduce(a,b,d,e)};c.find=c.detect=function(a,b,d){var e;A(a,function(f,g,h){if(b.call(d,f,g,h)){e=f;return true}});return e};c.filter=c.select=function(a,b,d){var e=[];if(a==null)return e;if(w&&a.filter===w)return a.filter(b,d);k(a,function(f,g,h){if(b.call(d,f,g,h))e[e.length]=
|
||||||
|
f});return e};c.reject=function(a,b,d){var e=[];if(a==null)return e;k(a,function(f,g,h){b.call(d,f,g,h)||(e[e.length]=f)});return e};c.every=c.all=function(a,b,d){b=b||c.identity;var e=true;if(a==null)return e;if(x&&a.every===x)return a.every(b,d);k(a,function(f,g,h){if(!(e=e&&b.call(d,f,g,h)))return m});return e};var A=c.some=c.any=function(a,b,d){b=b||c.identity;var e=false;if(a==null)return e;if(y&&a.some===y)return a.some(b,d);k(a,function(f,g,h){if(e=b.call(d,f,g,h))return m});return e};c.include=
|
||||||
|
c.contains=function(a,b){var d=false;if(a==null)return d;if(p&&a.indexOf===p)return a.indexOf(b)!=-1;A(a,function(e){if(d=e===b)return true});return d};c.invoke=function(a,b){var d=i.call(arguments,2);return c.map(a,function(e){return(b?e[b]:e).apply(e,d)})};c.pluck=function(a,b){return c.map(a,function(d){return d[b]})};c.max=function(a,b,d){if(!b&&c.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};k(a,function(f,g,h){g=b?b.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});
|
||||||
|
return e.value};c.min=function(a,b,d){if(!b&&c.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};k(a,function(f,g,h){g=b?b.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};c.sortBy=function(a,b,d){return c.pluck(c.map(a,function(e,f,g){return{value:e,criteria:b.call(d,e,f,g)}}).sort(function(e,f){var g=e.criteria,h=f.criteria;return g<h?-1:g>h?1:0}),"value")};c.sortedIndex=function(a,b,d){d=d||c.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(b)?
|
||||||
|
e=g+1:f=g}return e};c.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(c.isArray(a))return a;if(c.isArguments(a))return i.call(a);return c.values(a)};c.size=function(a){return c.toArray(a).length};c.first=c.head=function(a,b,d){return b&&!d?i.call(a,0,b):a[0]};c.rest=c.tail=function(a,b,d){return i.call(a,c.isUndefined(b)||d?1:b)};c.last=function(a){return a[a.length-1]};c.compact=function(a){return c.filter(a,function(b){return!!b})};c.flatten=function(a){return c.reduce(a,function(b,
|
||||||
|
d){if(c.isArray(d))return b.concat(c.flatten(d));b[b.length]=d;return b},[])};c.without=function(a){var b=i.call(arguments,1);return c.filter(a,function(d){return!c.include(b,d)})};c.uniq=c.unique=function(a,b){return c.reduce(a,function(d,e,f){if(0==f||(b===true?c.last(d)!=e:!c.include(d,e)))d[d.length]=e;return d},[])};c.intersect=function(a){var b=i.call(arguments,1);return c.filter(c.uniq(a),function(d){return c.every(b,function(e){return c.indexOf(e,d)>=0})})};c.zip=function(){for(var a=i.call(arguments),
|
||||||
|
b=c.max(c.pluck(a,"length")),d=Array(b),e=0;e<b;e++)d[e]=c.pluck(a,""+e);return d};c.indexOf=function(a,b,d){if(a==null)return-1;if(d){d=c.sortedIndex(a,b);return a[d]===b?d:-1}if(p&&a.indexOf===p)return a.indexOf(b);d=0;for(var e=a.length;d<e;d++)if(a[d]===b)return d;return-1};c.lastIndexOf=function(a,b){if(a==null)return-1;if(z&&a.lastIndexOf===z)return a.lastIndexOf(b);for(var d=a.length;d--;)if(a[d]===b)return d;return-1};c.range=function(a,b,d){var e=i.call(arguments),f=e.length<=1;a=f?0:e[0];
|
||||||
|
b=f?e[0]:e[1];d=e[2]||1;e=Math.max(Math.ceil((b-a)/d),0);f=0;for(var g=Array(e);f<e;){g[f++]=a;a+=d}return g};c.bind=function(a,b){var d=i.call(arguments,2);return function(){return a.apply(b||{},d.concat(i.call(arguments)))}};c.bindAll=function(a){var b=i.call(arguments,1);if(b.length==0)b=c.functions(a);k(b,function(d){a[d]=c.bind(a[d],a)});return a};c.memoize=function(a,b){var d={};b=b||c.identity;return function(){var e=b.apply(this,arguments);return e in d?d[e]:d[e]=a.apply(this,arguments)}};
|
||||||
|
c.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};c.defer=function(a){return c.delay.apply(c,[a,1].concat(i.call(arguments,1)))};var B=function(a,b,d){var e;return function(){var f=this,g=arguments,h=function(){e=null;a.apply(f,g)};d&&clearTimeout(e);if(d||!e)e=setTimeout(h,b)}};c.throttle=function(a,b){return B(a,b,false)};c.debounce=function(a,b){return B(a,b,true)};c.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments));return b.apply(this,
|
||||||
|
d)}};c.compose=function(){var a=i.call(arguments);return function(){for(var b=i.call(arguments),d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};c.keys=F||function(a){if(c.isArray(a))return c.range(0,a.length);var b=[],d;for(d in a)if(o.call(a,d))b[b.length]=d;return b};c.values=function(a){return c.map(a,c.identity)};c.functions=c.methods=function(a){return c.filter(c.keys(a),function(b){return c.isFunction(a[b])}).sort()};c.extend=function(a){k(i.call(arguments,1),function(b){for(var d in b)a[d]=
|
||||||
|
b[d]});return a};c.clone=function(a){return c.isArray(a)?a.slice():c.extend({},a)};c.tap=function(a,b){b(a);return a};c.isEqual=function(a,b){if(a===b)return true;var d=typeof a;if(d!=typeof b)return false;if(a==b)return true;if(!a&&b||a&&!b)return false;if(a._chain)a=a._wrapped;if(b._chain)b=b._wrapped;if(a.isEqual)return a.isEqual(b);if(c.isDate(a)&&c.isDate(b))return a.getTime()===b.getTime();if(c.isNaN(a)&&c.isNaN(b))return false;if(c.isRegExp(a)&&c.isRegExp(b))return a.source===b.source&&a.global===
|
||||||
|
b.global&&a.ignoreCase===b.ignoreCase&&a.multiline===b.multiline;if(d!=="object")return false;if(a.length&&a.length!==b.length)return false;d=c.keys(a);var e=c.keys(b);if(d.length!=e.length)return false;for(var f in a)if(!(f in b)||!c.isEqual(a[f],b[f]))return false;return true};c.isEmpty=function(a){if(c.isArray(a)||c.isString(a))return a.length===0;for(var b in a)if(o.call(a,b))return false;return true};c.isElement=function(a){return!!(a&&a.nodeType==1)};c.isArray=n||function(a){return E.call(a)===
|
||||||
|
"[object Array]"};c.isArguments=function(a){return!!(a&&o.call(a,"callee"))};c.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};c.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};c.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};c.isNaN=function(a){return a!==a};c.isBoolean=function(a){return a===true||a===false};c.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};c.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||
|
||||||
|
a.ignoreCase===false))};c.isNull=function(a){return a===null};c.isUndefined=function(a){return a===void 0};c.noConflict=function(){q._=C;return this};c.identity=function(a){return a};c.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};c.mixin=function(a){k(c.functions(a),function(b){H(b,c[b]=a[b])})};var I=0;c.uniqueId=function(a){var b=I++;return a?a+b:b};c.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g};c.template=function(a,b){var d=c.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+
|
||||||
|
a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,function(e,f){return"',"+f.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(e,f){return"');"+f.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return b?d(b):d};var l=function(a){this._wrapped=a};c.prototype=l.prototype;var r=function(a,b){return b?c(a).chain():a},H=function(a,b){l.prototype[a]=function(){var d=
|
||||||
|
i.call(arguments);D.call(d,this._wrapped);return r(b.apply(c,d),this._chain)}};c.mixin(c);k(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=j[a];l.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});k(["concat","join","slice"],function(a){var b=j[a];l.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});l.prototype.chain=function(){this._chain=true;return this};l.prototype.value=function(){return this._wrapped}})();
|
770
www/js/underscore.js
Normal file
770
www/js/underscore.js
Normal file
@ -0,0 +1,770 @@
|
|||||||
|
// Underscore.js 1.1.4
|
||||||
|
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
|
||||||
|
// Underscore is freely distributable under the MIT license.
|
||||||
|
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||||
|
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||||
|
// For all details and documentation:
|
||||||
|
// http://documentcloud.github.com/underscore
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
// Baseline setup
|
||||||
|
// --------------
|
||||||
|
|
||||||
|
// Establish the root object, `window` in the browser, or `global` on the server.
|
||||||
|
var root = this;
|
||||||
|
|
||||||
|
// Save the previous value of the `_` variable.
|
||||||
|
var previousUnderscore = root._;
|
||||||
|
|
||||||
|
// Establish the object that gets returned to break out of a loop iteration.
|
||||||
|
var breaker = {};
|
||||||
|
|
||||||
|
// Save bytes in the minified (but not gzipped) version:
|
||||||
|
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
|
||||||
|
|
||||||
|
// Create quick reference variables for speed access to core prototypes.
|
||||||
|
var slice = ArrayProto.slice,
|
||||||
|
unshift = ArrayProto.unshift,
|
||||||
|
toString = ObjProto.toString,
|
||||||
|
hasOwnProperty = ObjProto.hasOwnProperty;
|
||||||
|
|
||||||
|
// All **ECMAScript 5** native function implementations that we hope to use
|
||||||
|
// are declared here.
|
||||||
|
var
|
||||||
|
nativeForEach = ArrayProto.forEach,
|
||||||
|
nativeMap = ArrayProto.map,
|
||||||
|
nativeReduce = ArrayProto.reduce,
|
||||||
|
nativeReduceRight = ArrayProto.reduceRight,
|
||||||
|
nativeFilter = ArrayProto.filter,
|
||||||
|
nativeEvery = ArrayProto.every,
|
||||||
|
nativeSome = ArrayProto.some,
|
||||||
|
nativeIndexOf = ArrayProto.indexOf,
|
||||||
|
nativeLastIndexOf = ArrayProto.lastIndexOf,
|
||||||
|
nativeIsArray = Array.isArray,
|
||||||
|
nativeKeys = Object.keys;
|
||||||
|
|
||||||
|
// Create a safe reference to the Underscore object for use below.
|
||||||
|
var _ = function(obj) { return new wrapper(obj); };
|
||||||
|
|
||||||
|
// Export the Underscore object for **CommonJS**, with backwards-compatibility
|
||||||
|
// for the old `require()` API. If we're not in CommonJS, add `_` to the
|
||||||
|
// global object.
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = _;
|
||||||
|
_._ = _;
|
||||||
|
} else {
|
||||||
|
root._ = _;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current version.
|
||||||
|
_.VERSION = '1.1.4';
|
||||||
|
|
||||||
|
// Collection Functions
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
// The cornerstone, an `each` implementation, aka `forEach`.
|
||||||
|
// Handles objects implementing `forEach`, arrays, and raw objects.
|
||||||
|
// Delegates to **ECMAScript 5**'s native `forEach` if available.
|
||||||
|
var each = _.each = _.forEach = function(obj, iterator, context) {
|
||||||
|
var value;
|
||||||
|
if (obj == null) return;
|
||||||
|
if (nativeForEach && obj.forEach === nativeForEach) {
|
||||||
|
obj.forEach(iterator, context);
|
||||||
|
} else if (_.isNumber(obj.length)) {
|
||||||
|
for (var i = 0, l = obj.length; i < l; i++) {
|
||||||
|
if (iterator.call(context, obj[i], i, obj) === breaker) return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (var key in obj) {
|
||||||
|
if (hasOwnProperty.call(obj, key)) {
|
||||||
|
if (iterator.call(context, obj[key], key, obj) === breaker) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the results of applying the iterator to each element.
|
||||||
|
// Delegates to **ECMAScript 5**'s native `map` if available.
|
||||||
|
_.map = function(obj, iterator, context) {
|
||||||
|
var results = [];
|
||||||
|
if (obj == null) return results;
|
||||||
|
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
||||||
|
each(obj, function(value, index, list) {
|
||||||
|
results[results.length] = iterator.call(context, value, index, list);
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
||||||
|
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
||||||
|
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
||||||
|
var initial = memo !== void 0;
|
||||||
|
if (obj == null) obj = [];
|
||||||
|
if (nativeReduce && obj.reduce === nativeReduce) {
|
||||||
|
if (context) iterator = _.bind(iterator, context);
|
||||||
|
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
||||||
|
}
|
||||||
|
each(obj, function(value, index, list) {
|
||||||
|
if (!initial && index === 0) {
|
||||||
|
memo = value;
|
||||||
|
initial = true;
|
||||||
|
} else {
|
||||||
|
memo = iterator.call(context, memo, value, index, list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!initial) throw new TypeError("Reduce of empty array with no initial value");
|
||||||
|
return memo;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The right-associative version of reduce, also known as `foldr`.
|
||||||
|
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
||||||
|
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
||||||
|
if (obj == null) obj = [];
|
||||||
|
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
||||||
|
if (context) iterator = _.bind(iterator, context);
|
||||||
|
return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
||||||
|
}
|
||||||
|
var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
|
||||||
|
return _.reduce(reversed, iterator, memo, context);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the first value which passes a truth test. Aliased as `detect`.
|
||||||
|
_.find = _.detect = function(obj, iterator, context) {
|
||||||
|
var result;
|
||||||
|
any(obj, function(value, index, list) {
|
||||||
|
if (iterator.call(context, value, index, list)) {
|
||||||
|
result = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return all the elements that pass a truth test.
|
||||||
|
// Delegates to **ECMAScript 5**'s native `filter` if available.
|
||||||
|
// Aliased as `select`.
|
||||||
|
_.filter = _.select = function(obj, iterator, context) {
|
||||||
|
var results = [];
|
||||||
|
if (obj == null) return results;
|
||||||
|
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
|
||||||
|
each(obj, function(value, index, list) {
|
||||||
|
if (iterator.call(context, value, index, list)) results[results.length] = value;
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return all the elements for which a truth test fails.
|
||||||
|
_.reject = function(obj, iterator, context) {
|
||||||
|
var results = [];
|
||||||
|
if (obj == null) return results;
|
||||||
|
each(obj, function(value, index, list) {
|
||||||
|
if (!iterator.call(context, value, index, list)) results[results.length] = value;
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine whether all of the elements match a truth test.
|
||||||
|
// Delegates to **ECMAScript 5**'s native `every` if available.
|
||||||
|
// Aliased as `all`.
|
||||||
|
_.every = _.all = function(obj, iterator, context) {
|
||||||
|
iterator = iterator || _.identity;
|
||||||
|
var result = true;
|
||||||
|
if (obj == null) return result;
|
||||||
|
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
||||||
|
each(obj, function(value, index, list) {
|
||||||
|
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine if at least one element in the object matches a truth test.
|
||||||
|
// Delegates to **ECMAScript 5**'s native `some` if available.
|
||||||
|
// Aliased as `any`.
|
||||||
|
var any = _.some = _.any = function(obj, iterator, context) {
|
||||||
|
iterator = iterator || _.identity;
|
||||||
|
var result = false;
|
||||||
|
if (obj == null) return result;
|
||||||
|
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
||||||
|
each(obj, function(value, index, list) {
|
||||||
|
if (result = iterator.call(context, value, index, list)) return breaker;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine if a given value is included in the array or object using `===`.
|
||||||
|
// Aliased as `contains`.
|
||||||
|
_.include = _.contains = function(obj, target) {
|
||||||
|
var found = false;
|
||||||
|
if (obj == null) return found;
|
||||||
|
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
||||||
|
any(obj, function(value) {
|
||||||
|
if (found = value === target) return true;
|
||||||
|
});
|
||||||
|
return found;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Invoke a method (with arguments) on every item in a collection.
|
||||||
|
_.invoke = function(obj, method) {
|
||||||
|
var args = slice.call(arguments, 2);
|
||||||
|
return _.map(obj, function(value) {
|
||||||
|
return (method ? value[method] : value).apply(value, args);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convenience version of a common use case of `map`: fetching a property.
|
||||||
|
_.pluck = function(obj, key) {
|
||||||
|
return _.map(obj, function(value){ return value[key]; });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the maximum element or (element-based computation).
|
||||||
|
_.max = function(obj, iterator, context) {
|
||||||
|
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
|
||||||
|
var result = {computed : -Infinity};
|
||||||
|
each(obj, function(value, index, list) {
|
||||||
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||||
|
computed >= result.computed && (result = {value : value, computed : computed});
|
||||||
|
});
|
||||||
|
return result.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the minimum element (or element-based computation).
|
||||||
|
_.min = function(obj, iterator, context) {
|
||||||
|
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
|
||||||
|
var result = {computed : Infinity};
|
||||||
|
each(obj, function(value, index, list) {
|
||||||
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||||
|
computed < result.computed && (result = {value : value, computed : computed});
|
||||||
|
});
|
||||||
|
return result.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sort the object's values by a criterion produced by an iterator.
|
||||||
|
_.sortBy = function(obj, iterator, context) {
|
||||||
|
return _.pluck(_.map(obj, function(value, index, list) {
|
||||||
|
return {
|
||||||
|
value : value,
|
||||||
|
criteria : iterator.call(context, value, index, list)
|
||||||
|
};
|
||||||
|
}).sort(function(left, right) {
|
||||||
|
var a = left.criteria, b = right.criteria;
|
||||||
|
return a < b ? -1 : a > b ? 1 : 0;
|
||||||
|
}), 'value');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use a comparator function to figure out at what index an object should
|
||||||
|
// be inserted so as to maintain order. Uses binary search.
|
||||||
|
_.sortedIndex = function(array, obj, iterator) {
|
||||||
|
iterator = iterator || _.identity;
|
||||||
|
var low = 0, high = array.length;
|
||||||
|
while (low < high) {
|
||||||
|
var mid = (low + high) >> 1;
|
||||||
|
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
|
||||||
|
}
|
||||||
|
return low;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Safely convert anything iterable into a real, live array.
|
||||||
|
_.toArray = function(iterable) {
|
||||||
|
if (!iterable) return [];
|
||||||
|
if (iterable.toArray) return iterable.toArray();
|
||||||
|
if (_.isArray(iterable)) return iterable;
|
||||||
|
if (_.isArguments(iterable)) return slice.call(iterable);
|
||||||
|
return _.values(iterable);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the number of elements in an object.
|
||||||
|
_.size = function(obj) {
|
||||||
|
return _.toArray(obj).length;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Array Functions
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
// Get the first element of an array. Passing **n** will return the first N
|
||||||
|
// values in the array. Aliased as `head`. The **guard** check allows it to work
|
||||||
|
// with `_.map`.
|
||||||
|
_.first = _.head = function(array, n, guard) {
|
||||||
|
return n && !guard ? slice.call(array, 0, n) : array[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns everything but the first entry of the array. Aliased as `tail`.
|
||||||
|
// Especially useful on the arguments object. Passing an **index** will return
|
||||||
|
// the rest of the values in the array from that index onward. The **guard**
|
||||||
|
// check allows it to work with `_.map`.
|
||||||
|
_.rest = _.tail = function(array, index, guard) {
|
||||||
|
return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the last element of an array.
|
||||||
|
_.last = function(array) {
|
||||||
|
return array[array.length - 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Trim out all falsy values from an array.
|
||||||
|
_.compact = function(array) {
|
||||||
|
return _.filter(array, function(value){ return !!value; });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return a completely flattened version of an array.
|
||||||
|
_.flatten = function(array) {
|
||||||
|
return _.reduce(array, function(memo, value) {
|
||||||
|
if (_.isArray(value)) return memo.concat(_.flatten(value));
|
||||||
|
memo[memo.length] = value;
|
||||||
|
return memo;
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return a version of the array that does not contain the specified value(s).
|
||||||
|
_.without = function(array) {
|
||||||
|
var values = slice.call(arguments, 1);
|
||||||
|
return _.filter(array, function(value){ return !_.include(values, value); });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Produce a duplicate-free version of the array. If the array has already
|
||||||
|
// been sorted, you have the option of using a faster algorithm.
|
||||||
|
// Aliased as `unique`.
|
||||||
|
_.uniq = _.unique = function(array, isSorted) {
|
||||||
|
return _.reduce(array, function(memo, el, i) {
|
||||||
|
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el;
|
||||||
|
return memo;
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Produce an array that contains every item shared between all the
|
||||||
|
// passed-in arrays.
|
||||||
|
_.intersect = function(array) {
|
||||||
|
var rest = slice.call(arguments, 1);
|
||||||
|
return _.filter(_.uniq(array), function(item) {
|
||||||
|
return _.every(rest, function(other) {
|
||||||
|
return _.indexOf(other, item) >= 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Zip together multiple lists into a single array -- elements that share
|
||||||
|
// an index go together.
|
||||||
|
_.zip = function() {
|
||||||
|
var args = slice.call(arguments);
|
||||||
|
var length = _.max(_.pluck(args, 'length'));
|
||||||
|
var results = new Array(length);
|
||||||
|
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
|
||||||
|
// we need this function. Return the position of the first occurrence of an
|
||||||
|
// item in an array, or -1 if the item is not included in the array.
|
||||||
|
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
|
||||||
|
// If the array is large and already in sort order, pass `true`
|
||||||
|
// for **isSorted** to use binary search.
|
||||||
|
_.indexOf = function(array, item, isSorted) {
|
||||||
|
if (array == null) return -1;
|
||||||
|
if (isSorted) {
|
||||||
|
var i = _.sortedIndex(array, item);
|
||||||
|
return array[i] === item ? i : -1;
|
||||||
|
}
|
||||||
|
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
||||||
|
for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
||||||
|
_.lastIndexOf = function(array, item) {
|
||||||
|
if (array == null) return -1;
|
||||||
|
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
|
||||||
|
var i = array.length;
|
||||||
|
while (i--) if (array[i] === item) return i;
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate an integer Array containing an arithmetic progression. A port of
|
||||||
|
// the native Python `range()` function. See
|
||||||
|
// [the Python documentation](http://docs.python.org/library/functions.html#range).
|
||||||
|
_.range = function(start, stop, step) {
|
||||||
|
var args = slice.call(arguments),
|
||||||
|
solo = args.length <= 1,
|
||||||
|
start = solo ? 0 : args[0],
|
||||||
|
stop = solo ? args[0] : args[1],
|
||||||
|
step = args[2] || 1,
|
||||||
|
len = Math.max(Math.ceil((stop - start) / step), 0),
|
||||||
|
idx = 0,
|
||||||
|
range = new Array(len);
|
||||||
|
while (idx < len) {
|
||||||
|
range[idx++] = start;
|
||||||
|
start += step;
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function (ahem) Functions
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
// Create a function bound to a given object (assigning `this`, and arguments,
|
||||||
|
// optionally). Binding with arguments is also known as `curry`.
|
||||||
|
_.bind = function(func, obj) {
|
||||||
|
var args = slice.call(arguments, 2);
|
||||||
|
return function() {
|
||||||
|
return func.apply(obj || {}, args.concat(slice.call(arguments)));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bind all of an object's methods to that object. Useful for ensuring that
|
||||||
|
// all callbacks defined on an object belong to it.
|
||||||
|
_.bindAll = function(obj) {
|
||||||
|
var funcs = slice.call(arguments, 1);
|
||||||
|
if (funcs.length == 0) funcs = _.functions(obj);
|
||||||
|
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Memoize an expensive function by storing its results.
|
||||||
|
_.memoize = function(func, hasher) {
|
||||||
|
var memo = {};
|
||||||
|
hasher = hasher || _.identity;
|
||||||
|
return function() {
|
||||||
|
var key = hasher.apply(this, arguments);
|
||||||
|
return key in memo ? memo[key] : (memo[key] = func.apply(this, arguments));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delays a function for the given number of milliseconds, and then calls
|
||||||
|
// it with the arguments supplied.
|
||||||
|
_.delay = function(func, wait) {
|
||||||
|
var args = slice.call(arguments, 2);
|
||||||
|
return setTimeout(function(){ return func.apply(func, args); }, wait);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defers a function, scheduling it to run after the current call stack has
|
||||||
|
// cleared.
|
||||||
|
_.defer = function(func) {
|
||||||
|
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal function used to implement `_.throttle` and `_.debounce`.
|
||||||
|
var limit = function(func, wait, debounce) {
|
||||||
|
var timeout;
|
||||||
|
return function() {
|
||||||
|
var context = this, args = arguments;
|
||||||
|
var throttler = function() {
|
||||||
|
timeout = null;
|
||||||
|
func.apply(context, args);
|
||||||
|
};
|
||||||
|
if (debounce) clearTimeout(timeout);
|
||||||
|
if (debounce || !timeout) timeout = setTimeout(throttler, wait);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns a function, that, when invoked, will only be triggered at most once
|
||||||
|
// during a given window of time.
|
||||||
|
_.throttle = function(func, wait) {
|
||||||
|
return limit(func, wait, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns a function, that, as long as it continues to be invoked, will not
|
||||||
|
// be triggered. The function will be called after it stops being called for
|
||||||
|
// N milliseconds.
|
||||||
|
_.debounce = function(func, wait) {
|
||||||
|
return limit(func, wait, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the first function passed as an argument to the second,
|
||||||
|
// allowing you to adjust arguments, run code before and after, and
|
||||||
|
// conditionally execute the original function.
|
||||||
|
_.wrap = function(func, wrapper) {
|
||||||
|
return function() {
|
||||||
|
var args = [func].concat(slice.call(arguments));
|
||||||
|
return wrapper.apply(this, args);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns a function that is the composition of a list of functions, each
|
||||||
|
// consuming the return value of the function that follows.
|
||||||
|
_.compose = function() {
|
||||||
|
var funcs = slice.call(arguments);
|
||||||
|
return function() {
|
||||||
|
var args = slice.call(arguments);
|
||||||
|
for (var i=funcs.length-1; i >= 0; i--) {
|
||||||
|
args = [funcs[i].apply(this, args)];
|
||||||
|
}
|
||||||
|
return args[0];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Object Functions
|
||||||
|
// ----------------
|
||||||
|
|
||||||
|
// Retrieve the names of an object's properties.
|
||||||
|
// Delegates to **ECMAScript 5**'s native `Object.keys`
|
||||||
|
_.keys = nativeKeys || function(obj) {
|
||||||
|
if (_.isArray(obj)) return _.range(0, obj.length);
|
||||||
|
var keys = [];
|
||||||
|
for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
|
||||||
|
return keys;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retrieve the values of an object's properties.
|
||||||
|
_.values = function(obj) {
|
||||||
|
return _.map(obj, _.identity);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return a sorted list of the function names available on the object.
|
||||||
|
// Aliased as `methods`
|
||||||
|
_.functions = _.methods = function(obj) {
|
||||||
|
return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extend a given object with all the properties in passed-in object(s).
|
||||||
|
_.extend = function(obj) {
|
||||||
|
each(slice.call(arguments, 1), function(source) {
|
||||||
|
for (var prop in source) obj[prop] = source[prop];
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a (shallow-cloned) duplicate of an object.
|
||||||
|
_.clone = function(obj) {
|
||||||
|
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Invokes interceptor with the obj, and then returns obj.
|
||||||
|
// The primary purpose of this method is to "tap into" a method chain, in
|
||||||
|
// order to perform operations on intermediate results within the chain.
|
||||||
|
_.tap = function(obj, interceptor) {
|
||||||
|
interceptor(obj);
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Perform a deep comparison to check if two objects are equal.
|
||||||
|
_.isEqual = function(a, b) {
|
||||||
|
// Check object identity.
|
||||||
|
if (a === b) return true;
|
||||||
|
// Different types?
|
||||||
|
var atype = typeof(a), btype = typeof(b);
|
||||||
|
if (atype != btype) return false;
|
||||||
|
// Basic equality test (watch out for coercions).
|
||||||
|
if (a == b) return true;
|
||||||
|
// One is falsy and the other truthy.
|
||||||
|
if ((!a && b) || (a && !b)) return false;
|
||||||
|
// Unwrap any wrapped objects.
|
||||||
|
if (a._chain) a = a._wrapped;
|
||||||
|
if (b._chain) b = b._wrapped;
|
||||||
|
// One of them implements an isEqual()?
|
||||||
|
if (a.isEqual) return a.isEqual(b);
|
||||||
|
// Check dates' integer values.
|
||||||
|
if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
|
||||||
|
// Both are NaN?
|
||||||
|
if (_.isNaN(a) && _.isNaN(b)) return false;
|
||||||
|
// Compare regular expressions.
|
||||||
|
if (_.isRegExp(a) && _.isRegExp(b))
|
||||||
|
return a.source === b.source &&
|
||||||
|
a.global === b.global &&
|
||||||
|
a.ignoreCase === b.ignoreCase &&
|
||||||
|
a.multiline === b.multiline;
|
||||||
|
// If a is not an object by this point, we can't handle it.
|
||||||
|
if (atype !== 'object') return false;
|
||||||
|
// Check for different array lengths before comparing contents.
|
||||||
|
if (a.length && (a.length !== b.length)) return false;
|
||||||
|
// Nothing else worked, deep compare the contents.
|
||||||
|
var aKeys = _.keys(a), bKeys = _.keys(b);
|
||||||
|
// Different object sizes?
|
||||||
|
if (aKeys.length != bKeys.length) return false;
|
||||||
|
// Recursive comparison of contents.
|
||||||
|
for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given array or object empty?
|
||||||
|
_.isEmpty = function(obj) {
|
||||||
|
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
||||||
|
for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given value a DOM element?
|
||||||
|
_.isElement = function(obj) {
|
||||||
|
return !!(obj && obj.nodeType == 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given value an array?
|
||||||
|
// Delegates to ECMA5's native Array.isArray
|
||||||
|
_.isArray = nativeIsArray || function(obj) {
|
||||||
|
return toString.call(obj) === '[object Array]';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given variable an arguments object?
|
||||||
|
_.isArguments = function(obj) {
|
||||||
|
return !!(obj && hasOwnProperty.call(obj, 'callee'));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given value a function?
|
||||||
|
_.isFunction = function(obj) {
|
||||||
|
return !!(obj && obj.constructor && obj.call && obj.apply);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given value a string?
|
||||||
|
_.isString = function(obj) {
|
||||||
|
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given value a number?
|
||||||
|
_.isNumber = function(obj) {
|
||||||
|
return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
|
||||||
|
// that does not equal itself.
|
||||||
|
_.isNaN = function(obj) {
|
||||||
|
return obj !== obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given value a boolean?
|
||||||
|
_.isBoolean = function(obj) {
|
||||||
|
return obj === true || obj === false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given value a date?
|
||||||
|
_.isDate = function(obj) {
|
||||||
|
return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is the given value a regular expression?
|
||||||
|
_.isRegExp = function(obj) {
|
||||||
|
return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given value equal to null?
|
||||||
|
_.isNull = function(obj) {
|
||||||
|
return obj === null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is a given variable undefined?
|
||||||
|
_.isUndefined = function(obj) {
|
||||||
|
return obj === void 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility Functions
|
||||||
|
// -----------------
|
||||||
|
|
||||||
|
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
||||||
|
// previous owner. Returns a reference to the Underscore object.
|
||||||
|
_.noConflict = function() {
|
||||||
|
root._ = previousUnderscore;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keep the identity function around for default iterators.
|
||||||
|
_.identity = function(value) {
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run a function **n** times.
|
||||||
|
_.times = function (n, iterator, context) {
|
||||||
|
for (var i = 0; i < n; i++) iterator.call(context, i);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add your own custom functions to the Underscore object, ensuring that
|
||||||
|
// they're correctly added to the OOP wrapper as well.
|
||||||
|
_.mixin = function(obj) {
|
||||||
|
each(_.functions(obj), function(name){
|
||||||
|
addToWrapper(name, _[name] = obj[name]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate a unique integer id (unique within the entire client session).
|
||||||
|
// Useful for temporary DOM ids.
|
||||||
|
var idCounter = 0;
|
||||||
|
_.uniqueId = function(prefix) {
|
||||||
|
var id = idCounter++;
|
||||||
|
return prefix ? prefix + id : id;
|
||||||
|
};
|
||||||
|
|
||||||
|
// By default, Underscore uses ERB-style template delimiters, change the
|
||||||
|
// following template settings to use alternative delimiters.
|
||||||
|
_.templateSettings = {
|
||||||
|
evaluate : /<%([\s\S]+?)%>/g,
|
||||||
|
interpolate : /<%=([\s\S]+?)%>/g
|
||||||
|
};
|
||||||
|
|
||||||
|
// JavaScript micro-templating, similar to John Resig's implementation.
|
||||||
|
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
||||||
|
// and correctly escapes quotes within interpolated code.
|
||||||
|
_.template = function(str, data) {
|
||||||
|
var c = _.templateSettings;
|
||||||
|
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
|
||||||
|
'with(obj||{}){__p.push(\'' +
|
||||||
|
str.replace(/\\/g, '\\\\')
|
||||||
|
.replace(/'/g, "\\'")
|
||||||
|
.replace(c.interpolate, function(match, code) {
|
||||||
|
return "'," + code.replace(/\\'/g, "'") + ",'";
|
||||||
|
})
|
||||||
|
.replace(c.evaluate || null, function(match, code) {
|
||||||
|
return "');" + code.replace(/\\'/g, "'")
|
||||||
|
.replace(/[\r\n\t]/g, ' ') + "__p.push('";
|
||||||
|
})
|
||||||
|
.replace(/\r/g, '\\r')
|
||||||
|
.replace(/\n/g, '\\n')
|
||||||
|
.replace(/\t/g, '\\t')
|
||||||
|
+ "');}return __p.join('');";
|
||||||
|
var func = new Function('obj', tmpl);
|
||||||
|
return data ? func(data) : func;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The OOP Wrapper
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
// If Underscore is called as a function, it returns a wrapped object that
|
||||||
|
// can be used OO-style. This wrapper holds altered versions of all the
|
||||||
|
// underscore functions. Wrapped objects may be chained.
|
||||||
|
var wrapper = function(obj) { this._wrapped = obj; };
|
||||||
|
|
||||||
|
// Expose `wrapper.prototype` as `_.prototype`
|
||||||
|
_.prototype = wrapper.prototype;
|
||||||
|
|
||||||
|
// Helper function to continue chaining intermediate results.
|
||||||
|
var result = function(obj, chain) {
|
||||||
|
return chain ? _(obj).chain() : obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A method to easily add functions to the OOP wrapper.
|
||||||
|
var addToWrapper = function(name, func) {
|
||||||
|
wrapper.prototype[name] = function() {
|
||||||
|
var args = slice.call(arguments);
|
||||||
|
unshift.call(args, this._wrapped);
|
||||||
|
return result(func.apply(_, args), this._chain);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add all of the Underscore functions to the wrapper object.
|
||||||
|
_.mixin(_);
|
||||||
|
|
||||||
|
// Add all mutator Array functions to the wrapper.
|
||||||
|
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
||||||
|
var method = ArrayProto[name];
|
||||||
|
wrapper.prototype[name] = function() {
|
||||||
|
method.apply(this._wrapped, arguments);
|
||||||
|
return result(this._wrapped, this._chain);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add all accessor Array functions to the wrapper.
|
||||||
|
each(['concat', 'join', 'slice'], function(name) {
|
||||||
|
var method = ArrayProto[name];
|
||||||
|
wrapper.prototype[name] = function() {
|
||||||
|
return result(method.apply(this._wrapped, arguments), this._chain);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start chaining a wrapped Underscore object.
|
||||||
|
wrapper.prototype.chain = function() {
|
||||||
|
this._chain = true;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extracts the result from a wrapped and chained object.
|
||||||
|
wrapper.prototype.value = function() {
|
||||||
|
return this._wrapped;
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
@ -7,8 +7,20 @@
|
|||||||
<link rel="stylesheet" media="screen" href="/css/ts-screen.css" type="text/css"/>
|
<link rel="stylesheet" media="screen" href="/css/ts-screen.css" type="text/css"/>
|
||||||
<script type="text/javascript" src="/js/jquery-1.5.min.js"></script>
|
<script type="text/javascript" src="/js/jquery-1.5.min.js"></script>
|
||||||
<script type="text/javascript" src="/js/jquery-ui-1.8.10.custom.min.js"></script>
|
<script type="text/javascript" src="/js/jquery-ui-1.8.10.custom.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/underscore-min.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/ICanHaz.min.js"></script>
|
||||||
<script type="text/javascript" src="/js/ts.js"></script>
|
<script type="text/javascript" src="/js/ts.js"></script>
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||||
|
|
||||||
|
<script id="entry" type="text/html">
|
||||||
|
<div class="entry-bar" id="entry-{{entry_id}}">
|
||||||
|
<span class="id">{{entry_id}}.</span>
|
||||||
|
<span class="details">
|
||||||
|
<span class="entry-mark">{{mark}}</span>
|
||||||
|
<span class="entry-notes">{{notes}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -110,8 +122,8 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="last-entry" class="bar last-bar">
|
<div id="more-entries" class="last-bar">
|
||||||
<a href="#" onclick="loadMoreEntries();">Load More Entries</a>
|
<a href="#" onclick="loadMoreEntries();">load more entries</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="login-dialog" title="Login">
|
<div id="login-dialog" title="Login">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
ebin_dir = /home/jdbernard/projects/timestamper/web-app/ebin
|
ebin_dir = "C:/Documents and Settings/jbernard/My Documents/projects/personal/timestamper/web-app/ebin"
|
||||||
#include_dir = /home/jdbernard/projects/timestamper/web-app/src
|
#include_dir = /home/jdbernard/projects/timestamper/web-app/src
|
||||||
|
|
||||||
runmod = timestamper
|
runmod = timestamper
|
||||||
@ -6,6 +6,6 @@ runmod = timestamper
|
|||||||
<server timestamper-test>
|
<server timestamper-test>
|
||||||
port = 8000
|
port = 8000
|
||||||
listen = 127.0.0.1
|
listen = 127.0.0.1
|
||||||
docroot = /home/jdbernard/projects/timestamper/web-app/www
|
docroot = "C:/Documents and Settings/jbernard/My Documents/projects/personal/timestamper/web-app/www"
|
||||||
appmods = ts_api
|
appmods = ts_api
|
||||||
</server>
|
</server>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user